Skip to content

Commit 1da8367

Browse files
committed
Media (macOS): simplify now playing handling
Also improve performance
1 parent bdbc885 commit 1da8367

File tree

4 files changed

+65
-107
lines changed

4 files changed

+65
-107
lines changed

CMakeLists.txt

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -326,11 +326,6 @@ endif()
326326
fastfetch_encode_c_string("${DATATEXT_JSON_HELP}" DATATEXT_JSON_HELP)
327327
fastfetch_load_text(src/data/structure.txt DATATEXT_STRUCTURE)
328328

329-
if(APPLE)
330-
# See src/detection/media/media_apple.m
331-
fastfetch_load_text(src/data/nowPlaying.scptd DATATEXT_NOWPLAYING)
332-
endif()
333-
334329
configure_file(src/fastfetch_config.h.in fastfetch_config.h @ONLY)
335330
configure_file(src/fastfetch_datatext.h.in fastfetch_datatext.h @ONLY)
336331
if(APPLE)

src/data/nowPlaying.scptd

Lines changed: 0 additions & 24 deletions
This file was deleted.

src/detection/media/media_apple.m

Lines changed: 65 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,94 +1,85 @@
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);

src/fastfetch_datatext.h.in

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,3 @@
22

33
#define FASTFETCH_DATATEXT_JSON_HELP @DATATEXT_JSON_HELP@
44
#define FASTFETCH_DATATEXT_STRUCTURE @DATATEXT_STRUCTURE@
5-
6-
#ifdef __APPLE__
7-
#define FASTFETCH_DATATEXT_NOWPLAYING @DATATEXT_NOWPLAYING@
8-
#endif

0 commit comments

Comments
 (0)