mirror of
https://github.com/fastfetch-cli/fastfetch.git
synced 2026-09-12 10:22:12 +02:00
Media (macOS): adds length & position detection support
This commit is contained in:
+8
-2
@@ -1,7 +1,6 @@
|
|||||||
# find . -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.hpp" \) -print0 | xargs -0 clang-format -i
|
# find . -type f \( -name "*.c" -o -name "*.h" -o -name "*.cpp" -o -name "*.hpp" -o -name "*.m" -o -name "*.mm" \) -print0 | xargs -0 clang-format -i
|
||||||
|
|
||||||
BasedOnStyle: LLVM
|
BasedOnStyle: LLVM
|
||||||
Language: Cpp
|
|
||||||
|
|
||||||
UseTab: Never
|
UseTab: Never
|
||||||
IndentWidth: 4
|
IndentWidth: 4
|
||||||
@@ -81,3 +80,10 @@ AttributeMacros:
|
|||||||
|
|
||||||
MaxEmptyLinesToKeep: 1
|
MaxEmptyLinesToKeep: 1
|
||||||
KeepEmptyLinesAtTheStartOfBlocks: false
|
KeepEmptyLinesAtTheStartOfBlocks: false
|
||||||
|
|
||||||
|
---
|
||||||
|
Language: ObjC
|
||||||
|
ObjCBlockIndentWidth: 4
|
||||||
|
ObjCSpaceAfterProperty: true
|
||||||
|
ObjCSpaceBeforeProtocolList: true
|
||||||
|
ObjCBreakBeforeNestedBlockParam: true
|
||||||
|
|||||||
@@ -34,6 +34,29 @@ const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result) {
|
|||||||
return "TypeID is neither 'CFNumber' nor 'CFData'";
|
return "TypeID is neither 'CFNumber' nor 'CFData'";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* ffCfNumGetDouble(CFTypeRef cf, double* result) {
|
||||||
|
if (CFGetTypeID(cf) == CFNumberGetTypeID()) {
|
||||||
|
if (!CFNumberGetValue((CFNumberRef) cf, kCFNumberDoubleType, result) &&
|
||||||
|
!CFNumberGetValue((CFNumberRef) cf, kCFNumberFloatType, result)) {
|
||||||
|
return "Number type is not Double or Float";
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return "TypeID is neither 'CFNumber'";
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* ffCfDateGetEpoch(CFTypeRef cf, uint64_t* result) {
|
||||||
|
if (CFGetTypeID(cf) != CFDateGetTypeID()) {
|
||||||
|
return "TypeID is not 'CFDate'";
|
||||||
|
}
|
||||||
|
|
||||||
|
CFAbsoluteTime absTime = CFDateGetAbsoluteTime((CFDateRef) cf);
|
||||||
|
// Convert from seconds to milliseconds and add the difference between 1970 and 2001 in milliseconds
|
||||||
|
*result = (uint64_t) ((absTime + 978307200 /*kCFAbsoluteTimeIntervalSince1970*/) * 1000);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result) {
|
const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result) {
|
||||||
ffStrbufClear(result);
|
ffStrbufClear(result);
|
||||||
if (!cf) {
|
if (!cf) {
|
||||||
@@ -149,6 +172,15 @@ const char* ffCfDictGetInt64(CFDictionaryRef dict, CFStringRef key, int64_t* res
|
|||||||
return ffCfNumGetInt64(cf, result);
|
return ffCfNumGetInt64(cf, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* ffCfDictGetDouble(CFDictionaryRef dict, CFStringRef key, double* result) {
|
||||||
|
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||||
|
if (cf == NULL) {
|
||||||
|
return "CFDictionaryGetValue() failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ffCfNumGetDouble(cf, result);
|
||||||
|
}
|
||||||
|
|
||||||
const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length) {
|
const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length) {
|
||||||
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||||
if (cf == NULL) {
|
if (cf == NULL) {
|
||||||
@@ -182,3 +214,12 @@ const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryR
|
|||||||
*result = cf;
|
*result = cf;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char* ffCfDictGetDateAsEpoch(CFDictionaryRef dict, CFStringRef key, uint64_t* result) {
|
||||||
|
CFTypeRef cf = (CFTypeRef) CFDictionaryGetValue(dict, key);
|
||||||
|
if (cf == NULL) {
|
||||||
|
return "CFDictionaryGetValue() failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
return ffCfDateGetEpoch(cf, result);
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,14 +8,18 @@
|
|||||||
const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result);
|
const char* ffCfStrGetString(CFTypeRef cf, FFstrbuf* result);
|
||||||
const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result);
|
const char* ffCfNumGetInt(CFTypeRef cf, int32_t* result);
|
||||||
const char* ffCfNumGetInt64(CFTypeRef cf, int64_t* result);
|
const char* ffCfNumGetInt64(CFTypeRef cf, int64_t* result);
|
||||||
|
const char* ffCfNumGetDouble(CFTypeRef cf, double* result);
|
||||||
|
const char* ffCfDateGetEpoch(CFTypeRef cf, uint64_t* result);
|
||||||
const char* ffCfDataGetDataAsString(CFTypeRef cf, FFstrbuf* result);
|
const char* ffCfDataGetDataAsString(CFTypeRef cf, FFstrbuf* result);
|
||||||
const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result);
|
const char* ffCfDictGetString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result);
|
||||||
const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result);
|
const char* ffCfDictGetBool(CFDictionaryRef dict, CFStringRef key, bool* result);
|
||||||
const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result);
|
const char* ffCfDictGetInt(CFDictionaryRef dict, CFStringRef key, int* result);
|
||||||
const char* ffCfDictGetInt64(CFDictionaryRef dict, CFStringRef key, int64_t* result);
|
const char* ffCfDictGetInt64(CFDictionaryRef dict, CFStringRef key, int64_t* result);
|
||||||
|
const char* ffCfDictGetDouble(CFDictionaryRef dict, CFStringRef key, double* result);
|
||||||
const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length);
|
const char* ffCfDictGetData(CFDictionaryRef dict, CFStringRef key, uint32_t offset, uint32_t size, uint8_t* result, uint32_t* length);
|
||||||
const char* ffCfDictGetDataAsString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result);
|
const char* ffCfDictGetDataAsString(CFDictionaryRef dict, CFStringRef key, FFstrbuf* result);
|
||||||
const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result);
|
const char* ffCfDictGetDict(CFDictionaryRef dict, CFStringRef key, CFDictionaryRef* result);
|
||||||
|
const char* ffCfDictGetDateAsEpoch(CFDictionaryRef dict, CFStringRef key, uint64_t* result);
|
||||||
|
|
||||||
static inline CFNumberRef ffCfCreateInt(int value) {
|
static inline CFNumberRef ffCfCreateInt(int value) {
|
||||||
return CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value);
|
return CFNumberCreate(kCFAllocatorDefault, kCFNumberIntType, &value);
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "fastfetch.h"
|
#include "fastfetch.h"
|
||||||
#include "common/processing.h"
|
#include "common/processing.h"
|
||||||
#include "common/apple/cf_helpers.h"
|
#include "common/apple/cf_helpers.h"
|
||||||
|
#include "common/time.h"
|
||||||
#include "detection/media/media.h"
|
#include "detection/media/media.h"
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
#import <Foundation/Foundation.h>
|
||||||
@@ -8,17 +9,36 @@
|
|||||||
#import <CoreServices/CoreServices.h>
|
#import <CoreServices/CoreServices.h>
|
||||||
|
|
||||||
// https://github.com/andrewwiik/iOS-Blocks/blob/master/Widgets/Music/MediaRemote.h
|
// https://github.com/andrewwiik/iOS-Blocks/blob/master/Widgets/Music/MediaRemote.h
|
||||||
extern void MRMediaRemoteGetNowPlayingInfo(dispatch_queue_t dispatcher, void(^callback)(_Nullable CFDictionaryRef info)) FF_A_WEAK_IMPORT;
|
extern void MRMediaRemoteGetNowPlayingInfo(dispatch_queue_t dispatcher, void (^callback)(_Nullable CFDictionaryRef info)) FF_A_WEAK_IMPORT;
|
||||||
extern void MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_queue_t queue, void (^callback)(BOOL playing)) FF_A_WEAK_IMPORT;
|
extern void MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_queue_t queue, void (^callback)(BOOL playing)) FF_A_WEAK_IMPORT;
|
||||||
extern void MRMediaRemoteGetNowPlayingApplicationDisplayID(dispatch_queue_t queue, void (^callback)(_Nullable CFStringRef displayID)) FF_A_WEAK_IMPORT;
|
extern void MRMediaRemoteGetNowPlayingApplicationDisplayID(dispatch_queue_t queue, void (^callback)(_Nullable CFStringRef displayID)) FF_A_WEAK_IMPORT;
|
||||||
extern void MRMediaRemoteGetNowPlayingApplicationDisplayName(int unknown, dispatch_queue_t queue, void (^callback)(_Nullable CFStringRef name)) FF_A_WEAK_IMPORT;
|
extern void MRMediaRemoteGetNowPlayingApplicationDisplayName(int unknown, dispatch_queue_t queue, void (^callback)(_Nullable CFStringRef name)) FF_A_WEAK_IMPORT;
|
||||||
|
|
||||||
static const char* getMediaByMediaRemote(FFMediaResult* result, bool saveCover)
|
static uint32_t getTrueElapsedTime(CFDictionaryRef info) {
|
||||||
{
|
double elapsedTime;
|
||||||
#define FF_TEST_FN_EXISTANCE(fn) if (!fn) return "MediaRemote function " #fn " is not available"
|
if (ffCfDictGetDouble(info, CFSTR("kMRMediaRemoteNowPlayingInfoElapsedTime"), &elapsedTime) != NULL) {
|
||||||
FF_TEST_FN_EXISTANCE(MRMediaRemoteGetNowPlayingInfo);
|
return 0;
|
||||||
FF_TEST_FN_EXISTANCE(MRMediaRemoteGetNowPlayingApplicationIsPlaying);
|
}
|
||||||
#undef FF_TEST_FN_EXISTANCE
|
|
||||||
|
elapsedTime *= 1000;
|
||||||
|
|
||||||
|
double playbackRate;
|
||||||
|
uint64_t timestampEpoch;
|
||||||
|
if (ffCfDictGetDouble(info, CFSTR("kMRMediaRemoteNowPlayingInfoPlaybackRate"), &playbackRate) == NULL &&
|
||||||
|
ffCfDictGetDateAsEpoch(info, CFSTR("kMRMediaRemoteNowPlayingInfoTimestamp"), ×tampEpoch) == NULL) {
|
||||||
|
uint64_t timeDiff = ffTimeGetNow() - timestampEpoch;
|
||||||
|
elapsedTime += (double) timeDiff * playbackRate;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (uint32_t) elapsedTime;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* getMediaByMediaRemote(FFMediaResult* result, bool saveCover) {
|
||||||
|
#define FF_TEST_FN_EXISTENCE(fn) \
|
||||||
|
if (!fn) return "MediaRemote function " #fn " is not available"
|
||||||
|
FF_TEST_FN_EXISTENCE(MRMediaRemoteGetNowPlayingInfo);
|
||||||
|
FF_TEST_FN_EXISTENCE(MRMediaRemoteGetNowPlayingApplicationIsPlaying);
|
||||||
|
#undef FF_TEST_FN_EXISTENCE
|
||||||
|
|
||||||
dispatch_group_t group = dispatch_group_create();
|
dispatch_group_t group = dispatch_group_create();
|
||||||
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
|
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
|
||||||
@@ -26,32 +46,33 @@ static const char* getMediaByMediaRemote(FFMediaResult* result, bool saveCover)
|
|||||||
dispatch_group_enter(group);
|
dispatch_group_enter(group);
|
||||||
__block const char* error = NULL;
|
__block const char* error = NULL;
|
||||||
MRMediaRemoteGetNowPlayingInfo(queue, ^(_Nullable CFDictionaryRef info) {
|
MRMediaRemoteGetNowPlayingInfo(queue, ^(_Nullable CFDictionaryRef info) {
|
||||||
if(info != nil)
|
if (info != nil) {
|
||||||
{
|
|
||||||
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoTitle"), &result->song);
|
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoTitle"), &result->song);
|
||||||
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtist"), &result->artist);
|
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtist"), &result->artist);
|
||||||
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoAlbum"), &result->album);
|
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoAlbum"), &result->album);
|
||||||
|
double value;
|
||||||
|
if (ffCfDictGetDouble(info, CFSTR("kMRMediaRemoteNowPlayingInfoDuration"), &value) == NULL) {
|
||||||
|
result->length = (uint32_t) (value * 1000);
|
||||||
|
result->position = getTrueElapsedTime(info);
|
||||||
|
}
|
||||||
|
|
||||||
if (saveCover)
|
if (saveCover) {
|
||||||
{
|
|
||||||
NSData* artworkData = (__bridge NSData*) CFDictionaryGetValue(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtworkData"));
|
NSData* artworkData = (__bridge NSData*) CFDictionaryGetValue(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtworkData"));
|
||||||
if (artworkData)
|
if (artworkData) {
|
||||||
{
|
|
||||||
CFStringRef mime = (CFStringRef) CFDictionaryGetValue(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtworkMIMEType"));
|
CFStringRef mime = (CFStringRef) CFDictionaryGetValue(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtworkMIMEType"));
|
||||||
#pragma clang diagnostic push
|
#pragma clang diagnostic push
|
||||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||||
FF_CFTYPE_AUTO_RELEASE CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mime, NULL);
|
FF_CFTYPE_AUTO_RELEASE CFStringRef uti = UTTypeCreatePreferredIdentifierForTag(kUTTagClassMIMEType, mime, NULL);
|
||||||
FF_CFTYPE_AUTO_RELEASE CFStringRef ext = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension);
|
FF_CFTYPE_AUTO_RELEASE CFStringRef ext = UTTypeCopyPreferredTagWithClass(uti, kUTTagClassFilenameExtension);
|
||||||
#pragma clang diagnostic pop
|
#pragma clang diagnostic pop
|
||||||
NSString *tmpDir = NSTemporaryDirectory();
|
NSString* tmpDir = NSTemporaryDirectory();
|
||||||
NSString *uuid = NSUUID.UUID.UUIDString;
|
NSString* uuid = NSUUID.UUID.UUIDString;
|
||||||
NSString *path = [tmpDir stringByAppendingPathComponent:[NSString stringWithFormat:@"ff_%@.%@", uuid, ext ? (__bridge NSString *) ext : @"img"]];
|
NSString* path = [tmpDir stringByAppendingPathComponent:[NSString stringWithFormat:@"ff_%@.%@", uuid, ext ? (__bridge NSString*) ext : @"img"]];
|
||||||
if ([artworkData writeToFile:path atomically:NO])
|
if ([artworkData writeToFile:path atomically:NO])
|
||||||
ffStrbufSetS(&result->cover, path.UTF8String);
|
ffStrbufSetS(&result->cover, path.UTF8String);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else
|
||||||
else
|
|
||||||
error = "MRMediaRemoteGetNowPlayingInfo() failed";
|
error = "MRMediaRemoteGetNowPlayingInfo() failed";
|
||||||
|
|
||||||
dispatch_group_leave(group);
|
dispatch_group_leave(group);
|
||||||
@@ -63,8 +84,7 @@ static const char* getMediaByMediaRemote(FFMediaResult* result, bool saveCover)
|
|||||||
dispatch_group_leave(group);
|
dispatch_group_leave(group);
|
||||||
});
|
});
|
||||||
|
|
||||||
if (MRMediaRemoteGetNowPlayingApplicationDisplayID)
|
if (MRMediaRemoteGetNowPlayingApplicationDisplayID) {
|
||||||
{
|
|
||||||
dispatch_group_enter(group);
|
dispatch_group_enter(group);
|
||||||
MRMediaRemoteGetNowPlayingApplicationDisplayID(queue, ^(_Nullable CFStringRef displayID) {
|
MRMediaRemoteGetNowPlayingApplicationDisplayID(queue, ^(_Nullable CFStringRef displayID) {
|
||||||
ffCfStrGetString(displayID, &result->playerId);
|
ffCfStrGetString(displayID, &result->playerId);
|
||||||
@@ -72,8 +92,7 @@ static const char* getMediaByMediaRemote(FFMediaResult* result, bool saveCover)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MRMediaRemoteGetNowPlayingApplicationDisplayName)
|
if (MRMediaRemoteGetNowPlayingApplicationDisplayName) {
|
||||||
{
|
|
||||||
dispatch_group_enter(group);
|
dispatch_group_enter(group);
|
||||||
MRMediaRemoteGetNowPlayingApplicationDisplayName(0, queue, ^(_Nullable CFStringRef name) {
|
MRMediaRemoteGetNowPlayingApplicationDisplayName(0, queue, ^(_Nullable CFStringRef name) {
|
||||||
ffCfStrGetString(name, &result->player);
|
ffCfStrGetString(name, &result->player);
|
||||||
@@ -84,15 +103,14 @@ static const char* getMediaByMediaRemote(FFMediaResult* result, bool saveCover)
|
|||||||
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
|
||||||
// Don't dispatch_release because we are using ARC
|
// Don't dispatch_release because we are using ARC
|
||||||
|
|
||||||
if(result->song.length > 0)
|
if (result->song.length > 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
__attribute__((visibility("default"), used))
|
__attribute__((visibility("default"), used)) int ffPrintMediaByMediaRemote(bool saveCover) {
|
||||||
int ffPrintMediaByMediaRemote(bool saveCover)
|
|
||||||
{
|
|
||||||
FFMediaResult media = {
|
FFMediaResult media = {
|
||||||
.status = ffStrbufCreate(),
|
.status = ffStrbufCreate(),
|
||||||
.song = ffStrbufCreate(),
|
.song = ffStrbufCreate(),
|
||||||
@@ -102,15 +120,28 @@ int ffPrintMediaByMediaRemote(bool saveCover)
|
|||||||
.player = ffStrbufCreate(),
|
.player = ffStrbufCreate(),
|
||||||
.cover = ffStrbufCreate(),
|
.cover = ffStrbufCreate(),
|
||||||
};
|
};
|
||||||
if (getMediaByMediaRemote(&media, saveCover) != NULL)
|
if (getMediaByMediaRemote(&media, saveCover) != NULL) {
|
||||||
return 1;
|
return 1;
|
||||||
ffStrbufPutTo(&media.status, stdout);
|
}
|
||||||
ffStrbufPutTo(&media.song, stdout);
|
ffStrbufAppendC(&media.status, '\n');
|
||||||
ffStrbufPutTo(&media.artist, stdout);
|
ffStrbufAppend(&media.status, &media.song);
|
||||||
ffStrbufPutTo(&media.album, stdout);
|
ffStrbufAppendC(&media.status, '\n');
|
||||||
ffStrbufPutTo(&media.playerId, stdout);
|
ffStrbufAppend(&media.status, &media.artist);
|
||||||
ffStrbufPutTo(&media.player, stdout);
|
ffStrbufAppendC(&media.status, '\n');
|
||||||
ffStrbufWriteTo(&media.cover, stdout);
|
ffStrbufAppend(&media.status, &media.album);
|
||||||
|
ffStrbufAppendC(&media.status, '\n');
|
||||||
|
ffStrbufAppend(&media.status, &media.playerId);
|
||||||
|
ffStrbufAppendC(&media.status, '\n');
|
||||||
|
ffStrbufAppend(&media.status, &media.player);
|
||||||
|
ffStrbufAppendC(&media.status, '\n');
|
||||||
|
if (saveCover) {
|
||||||
|
ffStrbufAppend(&media.status, &media.cover);
|
||||||
|
}
|
||||||
|
ffStrbufAppendC(&media.status, '\n');
|
||||||
|
ffStrbufAppendUInt(&media.status, media.position);
|
||||||
|
ffStrbufAppendC(&media.status, '\n');
|
||||||
|
ffStrbufAppendUInt(&media.status, media.length);
|
||||||
|
write(STDOUT_FILENO, media.status.chars, media.status.length);
|
||||||
ffStrbufDestroy(&media.status);
|
ffStrbufDestroy(&media.status);
|
||||||
ffStrbufDestroy(&media.song);
|
ffStrbufDestroy(&media.song);
|
||||||
ffStrbufDestroy(&media.artist);
|
ffStrbufDestroy(&media.artist);
|
||||||
@@ -121,46 +152,55 @@ int ffPrintMediaByMediaRemote(bool saveCover)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char* getMediaByAuthorizedProcess(FFMediaResult* result, bool saveCover)
|
static const char* getMediaByAuthorizedProcess(FFMediaResult* result, bool saveCover) {
|
||||||
{
|
|
||||||
// #1737
|
// #1737
|
||||||
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 script = ffStrbufCreateF("import ctypes;ctypes.CDLL('%s').ffPrintMediaByMediaRemote(%s)", instance.state.platform.exePath.chars, saveCover ? "True" : "False");
|
||||||
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
|
FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate();
|
||||||
|
|
||||||
const char* error = ffProcessAppendStdOut(&buffer, (char* const[]) {
|
const char* error = ffProcessAppendStdOut(
|
||||||
"/usr/bin/python3", // Must be signed by Apple. Homebrew python doesn't work
|
&buffer,
|
||||||
"-c",
|
(char* const[]) { "/usr/bin/python3", // Must be signed by Apple. Homebrew python doesn't work
|
||||||
script.chars,
|
"-c",
|
||||||
nil
|
script.chars,
|
||||||
});
|
nil });
|
||||||
if (error) return error;
|
if (error) {
|
||||||
if (buffer.length == 0) return "No media found";
|
return error;
|
||||||
|
}
|
||||||
|
if (buffer.length == 0) {
|
||||||
|
return "No media found";
|
||||||
|
}
|
||||||
|
|
||||||
// status\ntitle\nartist\nalbum\nbundleName\nappName
|
// status\ntitle\nartist\nalbum\nbundleName\nappName\ncoverPath\nelapsedTimeMS\ndurationMS
|
||||||
FFstrbuf* const varList[] = { &result->status, &result->song, &result->artist, &result->album, &result->playerId, &result->player, &result->cover };
|
FFstrbuf* const strList[] = { &result->status, &result->song, &result->artist, &result->album, &result->playerId, &result->player, &result->cover };
|
||||||
char* line = NULL;
|
char* line = NULL;
|
||||||
size_t len = 0;
|
size_t len = 0;
|
||||||
for (uint32_t i = 0; i < ARRAY_SIZE(varList) && ffStrbufGetline(&line, &len, &buffer); ++i)
|
for (uint32_t i = 0; i < ARRAY_SIZE(strList) && ffStrbufGetline(&line, &len, &buffer); ++i) {
|
||||||
ffStrbufSetS(varList[i], line);
|
ffStrbufSetS(strList[i], line);
|
||||||
|
}
|
||||||
|
uint32_t* const numList[] = { &result->position, &result->length };
|
||||||
|
for (uint32_t i = 0; i < ARRAY_SIZE(numList) && ffStrbufGetline(&line, &len, &buffer); ++i) {
|
||||||
|
*numList[i] = (uint32_t) strtoul(line, NULL, 10);
|
||||||
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover)
|
void ffDetectMediaImpl(FFMediaResult* media, bool saveCover) {
|
||||||
{
|
|
||||||
const char* error;
|
const char* error;
|
||||||
if (@available(macOS 15.4, *))
|
if (@available(macOS 15.4, *)) {
|
||||||
error = getMediaByAuthorizedProcess(media, saveCover);
|
error = getMediaByAuthorizedProcess(media, saveCover);
|
||||||
else
|
} else {
|
||||||
error = getMediaByMediaRemote(media, saveCover);
|
error = getMediaByMediaRemote(media, saveCover);
|
||||||
if (error)
|
}
|
||||||
|
if (error) {
|
||||||
ffStrbufAppendS(&media->error, error);
|
ffStrbufAppendS(&media->error, error);
|
||||||
else if (media->player.length == 0 && media->playerId.length > 0)
|
} else if (media->player.length == 0 && media->playerId.length > 0) {
|
||||||
{
|
|
||||||
ffStrbufSet(&media->player, &media->playerId);
|
ffStrbufSet(&media->player, &media->playerId);
|
||||||
if (ffStrbufStartsWithIgnCaseS(&media->player, "com."))
|
if (ffStrbufStartsWithIgnCaseS(&media->player, "com.")) {
|
||||||
ffStrbufSubstrAfter(&media->player, strlen("com.") - 1);
|
ffStrbufSubstrAfter(&media->player, strlen("com.") - 1);
|
||||||
|
}
|
||||||
ffStrbufReplaceAllC(&media->player, '.', ' ');
|
ffStrbufReplaceAllC(&media->player, '.', ' ');
|
||||||
if (media->cover.length > 0)
|
if (media->cover.length > 0) {
|
||||||
media->removeCoverAfterUse = true;
|
media->removeCoverAfterUse = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user