11#include " fastfetch.h"
22#include " detection/media/media.h"
3- #include " common/library.h"
43#include " common/processing.h"
5- #include " util/apple/cf_helpers.h"
6- #include " util/apple/osascript.h"
7- #include " fastfetch_datatext.h"
84
95#import < Foundation/Foundation.h>
10- #import < CoreFoundation/CoreFoundation.h>
116
12- extern void MRMediaRemoteGetNowPlayingInfo (dispatch_queue_t dispatcher, void (^callback)(_Nullable CFDictionaryRef info)) __attribute__((weak_import));
13- extern void MRMediaRemoteGetNowPlayingClient (dispatch_queue_t dispatcher, void (^callback)(_Nullable id clientObj)) __attribute__((weak_import));
14- extern CFStringRef MRNowPlayingClientGetBundleIdentifier (id clientObj) __attribute__((weak_import));
15- extern CFStringRef MRNowPlayingClientGetParentAppBundleIdentifier (id clientObj) __attribute__((weak_import));
16- void MRMediaRemoteGetNowPlayingApplicationIsPlaying (dispatch_queue_t queue, void (^callback)(BOOL playing)) __attribute__((weak_import));
7+ @interface MRContentItem : NSObject <NSCopying >
8+ @property (nonatomic , readonly , copy ) NSDictionary <NSString*, NSString*>* nowPlayingInfo;
9+ @end
1710
18- static const char * getMediaByMediaRemote (FFMediaResult* result)
19- {
20- #define FF_TEST_FN_EXISTANCE (fn ) if (!fn) return " MediaRemote function " #fn " is not available"
21- FF_TEST_FN_EXISTANCE (MRMediaRemoteGetNowPlayingInfo);
22- FF_TEST_FN_EXISTANCE (MRMediaRemoteGetNowPlayingClient);
23- FF_TEST_FN_EXISTANCE (MRNowPlayingClientGetBundleIdentifier);
24- FF_TEST_FN_EXISTANCE (MRNowPlayingClientGetParentAppBundleIdentifier);
25- FF_TEST_FN_EXISTANCE (MRMediaRemoteGetNowPlayingApplicationIsPlaying);
26- #undef FF_TEST_FN_EXISTANCE
11+ @interface MRClient : NSObject <NSCopying , NSSecureCoding >
12+ @property (nonatomic , copy ) NSString * bundleIdentifier;
13+ @property (nonatomic , copy ) NSString * displayName;
14+ @end
2715
28- dispatch_group_t group = dispatch_group_create ();
29- dispatch_queue_t queue = dispatch_get_global_queue (DISPATCH_QUEUE_PRIORITY_HIGH, 0 );
30-
31- dispatch_group_enter (group);
32- MRMediaRemoteGetNowPlayingApplicationIsPlaying (queue, ^(BOOL playing) {
33- ffStrbufSetStatic (&result->status , playing ? " Playing" : " Paused" );
34- dispatch_group_leave (group);
35- });
36-
37- dispatch_group_enter (group);
38- __block const char * error = NULL ;
39- MRMediaRemoteGetNowPlayingInfo (queue, ^(_Nullable CFDictionaryRef info) {
40- if (info != nil )
41- {
42- error = ffCfDictGetString (info, CFSTR (" kMRMediaRemoteNowPlayingInfoTitle" ), &result->song );
43- if (!error)
44- {
45- ffCfDictGetString (info, CFSTR (" kMRMediaRemoteNowPlayingInfoArtist" ), &result->artist );
46- ffCfDictGetString (info, CFSTR (" kMRMediaRemoteNowPlayingInfoAlbum" ), &result->album );
47- }
48- }
49- else
50- error = " MRMediaRemoteGetNowPlayingInfo() failed" ;
51-
52- dispatch_group_leave (group);
53- });
16+ @interface MRPlayerPath : NSObject <NSCopying , NSSecureCoding >
17+ @property (nonatomic , copy ) MRClient* client;
18+ @end
5419
55- dispatch_group_enter (group);
56- MRMediaRemoteGetNowPlayingClient (queue, ^(_Nullable id clientObj) {
57- if (clientObj != nil )
58- {
59- CFStringRef identifier = MRNowPlayingClientGetBundleIdentifier (clientObj);
60- if (identifier == nil )
61- identifier = MRNowPlayingClientGetParentAppBundleIdentifier (clientObj);
62- if (identifier != nil )
63- ffCfStrGetString (identifier, &result->playerId );
64- }
65- dispatch_group_leave (group);
66- });
20+ @interface MRNowPlayingRequest
21+ + (bool )localIsPlaying ;
22+ + (MRContentItem*)localNowPlayingItem ;
23+ + (MRPlayerPath*)localNowPlayingPlayerPath ;
24+ @end
6725
68- dispatch_group_wait (group, DISPATCH_TIME_FOREVER);
69- // Don't dispatch_release because we are using ARC
26+ static const char * getMediaByMediaRemote (FFMediaResult* result)
27+ {
28+ if (!NSClassFromString (@" MRNowPlayingRequest" ))
29+ return " MediaRemote framework is not available" ;
30+ if (!MRNowPlayingRequest.localNowPlayingItem )
31+ return " No media found" ;
32+ ffStrbufSetStatic (&result->status , MRNowPlayingRequest.localIsPlaying ? " Playing" : " Paused" );
7033
71- if (result->playerId .length > 0 )
72- {
73- char buf[128 ];
74- snprintf (buf, ARRAY_SIZE (buf), " name of app id \" %s \" " , result->playerId .chars );
75- ffOsascript (buf, &result->player );
76- }
34+ NSDictionary <NSString *, NSString *>* infoDict = MRNowPlayingRequest.localNowPlayingItem .nowPlayingInfo ;
35+ ffStrbufSetS (&result->song , infoDict[@" kMRMediaRemoteNowPlayingInfoTitle" ].UTF8String );
36+ ffStrbufSetS (&result->artist , infoDict[@" kMRMediaRemoteNowPlayingInfoArtist" ].UTF8String );
37+ ffStrbufSetS (&result->album , infoDict[@" kMRMediaRemoteNowPlayingInfoAlbum" ].UTF8String );
7738
78- if (result->song .length > 0 )
79- return NULL ;
39+ MRClient* bundleObj = MRNowPlayingRequest.localNowPlayingPlayerPath .client ;
40+ ffStrbufSetS (&result->playerId , bundleObj.bundleIdentifier .UTF8String );
41+ ffStrbufSetS (&result->player , bundleObj.displayName .UTF8String );
42+ return NULL ;
43+ }
8044
81- return error;
45+ __attribute__ ((visibility(" default" ), used))
46+ int ffPrintMediaByMediaRemote(void )
47+ {
48+ FFMediaResult media = {
49+ .status = ffStrbufCreate (),
50+ .song = ffStrbufCreate (),
51+ .artist = ffStrbufCreate (),
52+ .album = ffStrbufCreate (),
53+ .playerId = ffStrbufCreate (),
54+ .player = ffStrbufCreate (),
55+ };
56+ if (getMediaByMediaRemote (&media) != NULL )
57+ return 1 ;
58+ ffStrbufPutTo (&media.status , stdout);
59+ ffStrbufPutTo (&media.song , stdout);
60+ ffStrbufPutTo (&media.artist , stdout);
61+ ffStrbufPutTo (&media.album , stdout);
62+ ffStrbufPutTo (&media.playerId , stdout);
63+ ffStrbufPutTo (&media.player , stdout);
64+ ffStrbufDestroy (&media.status );
65+ ffStrbufDestroy (&media.song );
66+ ffStrbufDestroy (&media.artist );
67+ ffStrbufDestroy (&media.album );
68+ ffStrbufDestroy (&media.playerId );
69+ ffStrbufDestroy (&media.player );
70+ return 0 ;
8271}
8372
84- static const char * getMediaByOsascript (FFMediaResult* result)
73+ static const char * getMediaByAuthorizedProcess (FFMediaResult* result)
8574{
75+ // #1737
76+ FF_STRBUF_AUTO_DESTROY script = ffStrbufCreateF (" import ctypes;ctypes.CDLL('%s ').ffPrintMediaByMediaRemote()" , instance.state .platform .exePath .chars );
8677 FF_STRBUF_AUTO_DESTROY buffer = ffStrbufCreate ();
87- // The script must be executed by `/usr/bin/osascript`. `ffOsascript` doesn't work here
78+
8879 const char * error = ffProcessAppendStdOut (&buffer, (char * const []) {
89- " /usr/bin/osascript " ,
90- " -e " ,
91- FASTFETCH_DATATEXT_NOWPLAYING ,
80+ " /usr/bin/python3 " , // Must be signed by Apple. Homebrew python doesn't work
81+ " -c " ,
82+ script. chars ,
9283 nil
9384 });
9485 if (error) return error;
@@ -107,7 +98,7 @@ void ffDetectMediaImpl(FFMediaResult* media)
10798{
10899 const char * error;
109100 if (@available (macOS 15.4 , *))
110- error = getMediaByOsascript (media);
101+ error = getMediaByAuthorizedProcess (media);
111102 else
112103 error = getMediaByMediaRemote (media);
113104 if (error) ffStrbufAppendS (&media->error , error);
0 commit comments