Logo: adds support for using media cover as logo source

This commit is contained in:
李通洲
2025-10-21 15:05:25 +08:00
parent 5d73cc1232
commit 338b552499
10 changed files with 75 additions and 52 deletions
+3 -3
View File
@@ -1,8 +1,8 @@
#include "media.h"
void ffDetectMediaImpl(FFMediaResult* media);
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover);
const FFMediaResult* ffDetectMedia(void)
const FFMediaResult* ffDetectMedia(bool saveCover)
{
static FFMediaResult result;
@@ -17,7 +17,7 @@ const FFMediaResult* ffDetectMedia(void)
ffStrbufInit(&result.url);
ffStrbufInit(&result.status);
ffStrbufInit(&result.cover);
ffDetectMediaImpl(&result);
ffDetectMediaImpl(&result, saveCover);
if(result.song.length == 0 && result.error.length == 0)
ffStrbufAppendS(&result.error, "No media found");
+1 -1
View File
@@ -16,4 +16,4 @@ typedef struct FFMediaResult
FFstrbuf cover;
} FFMediaResult;
const FFMediaResult* ffDetectMedia();
const FFMediaResult* ffDetectMedia(bool saveCover);
+22 -18
View File
@@ -13,7 +13,7 @@ extern void MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_queue_t queu
extern void MRMediaRemoteGetNowPlayingApplicationDisplayID(dispatch_queue_t queue, void (^callback)(_Nullable CFStringRef displayID)) __attribute__((weak_import));
extern void MRMediaRemoteGetNowPlayingApplicationDisplayName(int unknown, dispatch_queue_t queue, void (^callback)(_Nullable CFStringRef name)) __attribute__((weak_import));
static const char* getMediaByMediaRemote(FFMediaResult* result)
static const char* getMediaByMediaRemote(FFMediaResult* result, bool saveCover)
{
#define FF_TEST_FN_EXISTANCE(fn) if (!fn) return "MediaRemote function " #fn " is not available"
FF_TEST_FN_EXISTANCE(MRMediaRemoteGetNowPlayingInfo);
@@ -31,20 +31,24 @@ static const char* getMediaByMediaRemote(FFMediaResult* result)
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoTitle"), &result->song);
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtist"), &result->artist);
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoAlbum"), &result->album);
NSData* artworkData = (__bridge NSData*) CFDictionaryGetValue(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtworkData"));
if (artworkData)
if (saveCover)
{
CFStringRef mime = (CFStringRef) CFDictionaryGetValue(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtworkMIMEType"));
NSData* artworkData = (__bridge NSData*) CFDictionaryGetValue(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtworkData"));
if (artworkData)
{
CFStringRef mime = (CFStringRef) CFDictionaryGetValue(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtworkMIMEType"));
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
FF_CFTYPE_AUTO_RELEASE CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mime, NULL);
FF_CFTYPE_AUTO_RELEASE CFStringRef ext = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension);
FF_CFTYPE_AUTO_RELEASE CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mime, NULL);
FF_CFTYPE_AUTO_RELEASE CFStringRef ext = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension);
#pragma clang diagnostic pop
NSString *tmpDir = NSTemporaryDirectory();
NSString *uuid = [[NSUUID UUID] UUIDString];
NSString *path = [tmpDir stringByAppendingPathComponent:[NSString stringWithFormat:@"ff_%@.%@", uuid, ext ? (__bridge NSString *) ext : @"img"]];
if ([artworkData writeToFile:path atomically:NO])
ffStrbufSetS(&result->cover, path.UTF8String);
NSString *tmpDir = NSTemporaryDirectory();
NSString *uuid = [[NSUUID UUID] UUIDString];
NSString *path = [tmpDir stringByAppendingPathComponent:[NSString stringWithFormat:@"ff_%@.%@", uuid, ext ? (__bridge NSString *) ext : @"img"]];
if ([artworkData writeToFile:path atomically:NO])
ffStrbufSetS(&result->cover, path.UTF8String);
}
}
}
else
@@ -87,7 +91,7 @@ static const char* getMediaByMediaRemote(FFMediaResult* result)
}
__attribute__((visibility("default"), used))
int ffPrintMediaByMediaRemote(void)
int ffPrintMediaByMediaRemote(bool saveCover)
{
FFMediaResult media = {
.status = ffStrbufCreate(),
@@ -98,7 +102,7 @@ int ffPrintMediaByMediaRemote(void)
.player = ffStrbufCreate(),
.cover = ffStrbufCreate(),
};
if (getMediaByMediaRemote(&media) != NULL)
if (getMediaByMediaRemote(&media, saveCover) != NULL)
return 1;
ffStrbufPutTo(&media.status, stdout);
ffStrbufPutTo(&media.song, stdout);
@@ -117,10 +121,10 @@ int ffPrintMediaByMediaRemote(void)
return 0;
}
static const char* getMediaByAuthorizedProcess(FFMediaResult* result)
static const char* getMediaByAuthorizedProcess(FFMediaResult* result, bool saveCover)
{
// #1737
FF_STRBUF_AUTO_DESTROY script = ffStrbufCreateF("import ctypes;ctypes.CDLL('%s').ffPrintMediaByMediaRemote()", instance.state.platform.exePath.chars);
FF_STRBUF_AUTO_DESTROY script = ffStrbufCreateF("import ctypes;ctypes.CDLL('%s').ffPrintMediaByMediaRemote(%s)", instance.state.platform.exePath.chars, saveCover ? "True" : "False");
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
const char* error = ffProcessAppendStdOut(&buffer, (char* const[]) {
@@ -141,13 +145,13 @@ static const char* getMediaByAuthorizedProcess(FFMediaResult* result)
return NULL;
}
void ffDetectMediaImpl(FFMediaResult* media)
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover)
{
const char* error;
if (@available(macOS 15.4, *))
error = getMediaByAuthorizedProcess(media);
error = getMediaByAuthorizedProcess(media, saveCover);
else
error = getMediaByMediaRemote(media);
error = getMediaByMediaRemote(media, saveCover);
if (error)
ffStrbufAppendS(&media->error, error);
else if (media->player.length == 0 && media->playerId.length > 0)
+3 -1
View File
@@ -2,6 +2,7 @@
#include "fastfetch.h"
#include "detection/media/media.h"
#include "util/stringUtils.h"
#include "util/unused.h"
#include <string.h>
@@ -271,8 +272,9 @@ static const char* getMedia(FFMediaResult* result)
#endif
void ffDetectMediaImpl(FFMediaResult* media)
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover)
{
FF_UNUSED(saveCover); // We don't save the cover to a file for Mpris implementation
#ifdef FF_HAVE_DBUS
const char* error = getMedia(media);
ffStrbufAppendS(&media->error, error);
+4 -4
View File
@@ -3,7 +3,7 @@
#include "media.h"
#include "media_windows.dll.h"
static const char* getMedia(FFMediaResult* media)
static const char* getMedia(FFMediaResult* media, bool saveCover)
{
FF_LIBRARY_LOAD(libffwinrt, "dlopen libffwinrt" FF_LIBRARY_EXTENSION " failed", "libffwinrt" FF_LIBRARY_EXTENSION, 0)
FF_LIBRARY_LOAD_SYMBOL_MESSAGE(libffwinrt, ffWinrtDetectMedia)
@@ -11,7 +11,7 @@ static const char* getMedia(FFMediaResult* media)
FFWinrtMediaResult result = {};
const char* error = ffffWinrtDetectMedia(&result);
const char* error = ffffWinrtDetectMedia(&result, saveCover);
if (error)
{
ffStrbufSetStatic(&media->error, error);
@@ -37,8 +37,8 @@ static const char* getMedia(FFMediaResult* media)
return NULL;
}
void ffDetectMediaImpl(FFMediaResult* media)
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover)
{
const char* error = getMedia(media);
const char* error = getMedia(media, saveCover);
ffStrbufAppendS(&media->error, error);
}
+24 -21
View File
@@ -10,14 +10,12 @@ extern "C"
{
#include "media_windows.dll.h"
const char* ffWinrtDetectMedia(FFWinrtMediaResult* result)
const char* ffWinrtDetectMedia(FFWinrtMediaResult* result, bool saveCover)
{
// C++/WinRT requires Windows 8.1+ and C++ runtime (std::string, exceptions and other stuff)
// Make it a separate dll in order not to break Windows 7 support
using namespace winrt::Windows::Media::Control;
using namespace winrt::Windows::ApplicationModel;
using namespace winrt::Windows::Storage;
using namespace winrt::Windows::Storage::Streams;
try
{
@@ -66,33 +64,38 @@ const char* ffWinrtDetectMedia(FFWinrtMediaResult* result)
result->playerName[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
} catch (...) { }
if (auto thumbRef = mediaProps.Thumbnail())
if (saveCover)
{
try
using namespace winrt::Windows::Storage;
using namespace winrt::Windows::Storage::Streams;
if (auto thumbRef = mediaProps.Thumbnail())
{
if (auto stream = thumbRef.OpenReadAsync().get())
try
{
if (stream.Size() > 0)
if (auto stream = thumbRef.OpenReadAsync().get())
{
Buffer buffer(static_cast<uint32_t>(stream.Size()));
stream.ReadAsync(buffer, buffer.Capacity(), InputStreamOptions::None).get();
wchar_t tempPath[MAX_PATH];
if (GetTempPathW(MAX_PATH, tempPath) > 0)
if (stream.Size() > 0)
{
auto tempFolder = StorageFolder::GetFolderFromPathAsync(tempPath).get();
auto tempFile = tempFolder.CreateFileAsync(L"ff_thumb.img", CreationCollisionOption::GenerateUniqueName).get();
FileIO::WriteBufferAsync(tempFile, buffer).get();
Buffer buffer(static_cast<uint32_t>(stream.Size()));
stream.ReadAsync(buffer, buffer.Capacity(), InputStreamOptions::None).get();
::wcsncpy(result->cover, tempFile.Path().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
result->cover[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
wchar_t tempPath[MAX_PATH];
if (GetTempPathW(MAX_PATH, tempPath) > 0)
{
auto tempFolder = StorageFolder::GetFolderFromPathAsync(tempPath).get();
auto tempFile = tempFolder.CreateFileAsync(L"ff_thumb.img", CreationCollisionOption::GenerateUniqueName).get();
FileIO::WriteBufferAsync(tempFile, buffer).get();
::wcsncpy(result->cover, tempFile.Path().data(), FF_MEDIA_WIN_RESULT_BUFLEN);
result->cover[FF_MEDIA_WIN_RESULT_BUFLEN - 1] = L'\0';
}
}
}
}
}
catch (...)
{
// Ignore thumbnail errors
catch (...)
{
// Ignore thumbnail errors
}
}
}
+1 -1
View File
@@ -14,4 +14,4 @@ typedef struct FFWinrtMediaResult
} FFWinrtMediaResult;
__attribute__((__dllexport__))
const char* ffWinrtDetectMedia(FFWinrtMediaResult* result);
const char* ffWinrtDetectMedia(FFWinrtMediaResult* result, bool saveCover);
+10
View File
@@ -2,6 +2,7 @@
#include "common/io/io.h"
#include "common/printing.h"
#include "common/processing.h"
#include "detection/media/media.h"
#include "detection/os/os.h"
#include "detection/terminalshell/terminalshell.h"
#include "util/textModifier.h"
@@ -483,6 +484,15 @@ static bool updateLogoPath(void)
if (ffStrbufEqualS(&options->source, "-")) // stdin
return true;
if (ffStrbufIgnCaseEqualS(&options->source, "mediacover"))
{
const FFMediaResult* media = ffDetectMedia(true);
if (media->cover.length == 0)
return false;
ffStrbufSet(&options->source, &media->cover);
return true;
}
FF_STRBUF_AUTO_DESTROY fullPath = ffStrbufCreate();
if (ffPathExpandEnv(options->source.chars, &fullPath) && ffPathExists(fullPath.chars, FF_PATHTYPE_FILE))
{
+6 -2
View File
@@ -43,7 +43,7 @@ static bool artistInSongTitle(const FFstrbuf* song, const FFstrbuf* artist)
bool ffPrintMedia(FFMediaOptions* options)
{
const FFMediaResult* media = ffDetectMedia();
const FFMediaResult* media = ffDetectMedia(false);
if(media->error.length > 0)
{
@@ -128,7 +128,7 @@ void ffGenerateMediaJsonConfig(FFMediaOptions* options, yyjson_mut_doc* doc, yyj
bool ffGenerateMediaJsonResult(FF_MAYBE_UNUSED FFMediaOptions* options, yyjson_mut_doc* doc, yyjson_mut_val* module)
{
const FFMediaResult* media = ffDetectMedia();
const FFMediaResult* media = ffDetectMedia(false);
if(media->error.length > 0)
{
@@ -143,6 +143,10 @@ bool ffGenerateMediaJsonResult(FF_MAYBE_UNUSED FFMediaOptions* options, yyjson_m
yyjson_mut_obj_add_strbuf(doc, song, "artist", &media->artist);
yyjson_mut_obj_add_strbuf(doc, song, "album", &media->album);
yyjson_mut_obj_add_strbuf(doc, song, "status", &media->status);
if (media->cover.length > 0)
yyjson_mut_obj_add_strbuf(doc, song, "cover", &media->status);
else
yyjson_mut_obj_add_null(doc, song, "cover");
yyjson_mut_val* player = yyjson_mut_obj_add_obj(doc, obj, "player");
yyjson_mut_obj_add_strbuf(doc, player, "name", &media->player);
+1 -1
View File
@@ -10,7 +10,7 @@
bool ffPrintPlayer(FFPlayerOptions* options)
{
const FFMediaResult* media = ffDetectMedia();
const FFMediaResult* media = ffDetectMedia(false);
if(media->error.length > 0)
{