Skip to content

Commit ad0ddfd

Browse files
committed
Media (macOS): support old macOS version
1 parent 1da8367 commit ad0ddfd

File tree

1 file changed

+69
-33
lines changed

1 file changed

+69
-33
lines changed

src/detection/media/media_apple.m

Lines changed: 69 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,73 @@
11
#include "fastfetch.h"
22
#include "detection/media/media.h"
33
#include "common/processing.h"
4+
#include "util/apple/cf_helpers.h"
45

56
#import <Foundation/Foundation.h>
7+
#import <CoreFoundation/CoreFoundation.h>
68

7-
@interface MRContentItem : NSObject <NSCopying>
8-
@property (nonatomic, readonly, copy) NSDictionary<NSString*, NSString*>* nowPlayingInfo;
9-
@end
9+
// https://github.com/andrewwiik/iOS-Blocks/blob/master/Widgets/Music/MediaRemote.h
10+
extern void MRMediaRemoteGetNowPlayingInfo(dispatch_queue_t dispatcher, void(^callback)(_Nullable CFDictionaryRef info)) __attribute__((weak_import));
11+
extern void MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_queue_t queue, void (^callback)(BOOL playing)) __attribute__((weak_import));
12+
extern void MRMediaRemoteGetNowPlayingApplicationDisplayID(dispatch_queue_t queue, void (^callback)(_Nullable CFStringRef displayID)) __attribute__((weak_import));
13+
extern void MRMediaRemoteGetNowPlayingApplicationDisplayName(int unknown, dispatch_queue_t queue, void (^callback)(_Nullable CFStringRef name)) __attribute__((weak_import));
1014

11-
@interface MRClient : NSObject <NSCopying, NSSecureCoding>
12-
@property (nonatomic, copy) NSString* bundleIdentifier;
13-
@property (nonatomic, copy) NSString* displayName;
14-
@end
15+
static const char* getMediaByMediaRemote(FFMediaResult* result)
16+
{
17+
#define FF_TEST_FN_EXISTANCE(fn) if (!fn) return "MediaRemote function " #fn " is not available"
18+
FF_TEST_FN_EXISTANCE(MRMediaRemoteGetNowPlayingInfo);
19+
FF_TEST_FN_EXISTANCE(MRMediaRemoteGetNowPlayingApplicationIsPlaying);
20+
#undef FF_TEST_FN_EXISTANCE
1521

16-
@interface MRPlayerPath : NSObject <NSCopying, NSSecureCoding>
17-
@property (nonatomic, copy) MRClient* client;
18-
@end
22+
dispatch_group_t group = dispatch_group_create();
23+
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
1924

20-
@interface MRNowPlayingRequest
21-
+ (bool)localIsPlaying;
22-
+ (MRContentItem*)localNowPlayingItem;
23-
+ (MRPlayerPath*)localNowPlayingPlayerPath;
24-
@end
25+
dispatch_group_enter(group);
26+
__block const char* error = NULL;
27+
MRMediaRemoteGetNowPlayingInfo(queue, ^(_Nullable CFDictionaryRef info) {
28+
if(info != nil)
29+
{
30+
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoTitle"), &result->song);
31+
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoArtist"), &result->artist);
32+
ffCfDictGetString(info, CFSTR("kMRMediaRemoteNowPlayingInfoAlbum"), &result->album);
33+
}
34+
else
35+
error = "MRMediaRemoteGetNowPlayingInfo() failed";
2536

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");
33-
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);
38-
39-
MRClient* bundleObj = MRNowPlayingRequest.localNowPlayingPlayerPath.client;
40-
ffStrbufSetS(&result->playerId, bundleObj.bundleIdentifier.UTF8String);
41-
ffStrbufSetS(&result->player, bundleObj.displayName.UTF8String);
42-
return NULL;
37+
dispatch_group_leave(group);
38+
});
39+
40+
dispatch_group_enter(group);
41+
MRMediaRemoteGetNowPlayingApplicationIsPlaying(queue, ^(BOOL playing) {
42+
ffStrbufSetStatic(&result->status, playing ? "Playing" : "Paused");
43+
dispatch_group_leave(group);
44+
});
45+
46+
if (MRMediaRemoteGetNowPlayingApplicationDisplayID)
47+
{
48+
dispatch_group_enter(group);
49+
MRMediaRemoteGetNowPlayingApplicationDisplayID(queue, ^(_Nullable CFStringRef displayID) {
50+
ffCfStrGetString(displayID, &result->playerId);
51+
dispatch_group_leave(group);
52+
});
53+
}
54+
55+
if (MRMediaRemoteGetNowPlayingApplicationDisplayName)
56+
{
57+
dispatch_group_enter(group);
58+
MRMediaRemoteGetNowPlayingApplicationDisplayName(0, queue, ^(_Nullable CFStringRef name) {
59+
ffCfStrGetString(name, &result->player);
60+
dispatch_group_leave(group);
61+
});
62+
}
63+
64+
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
65+
// Don't dispatch_release because we are using ARC
66+
67+
if(result->song.length > 0)
68+
return NULL;
69+
70+
return error;
4371
}
4472

4573
__attribute__((visibility("default"), used))
@@ -101,5 +129,13 @@ void ffDetectMediaImpl(FFMediaResult* media)
101129
error = getMediaByAuthorizedProcess(media);
102130
else
103131
error = getMediaByMediaRemote(media);
104-
if (error) ffStrbufAppendS(&media->error, error);
132+
if (error)
133+
ffStrbufAppendS(&media->error, error);
134+
else if (media->player.length == 0 && media->playerId.length > 0)
135+
{
136+
ffStrbufSet(&media->player, &media->playerId);
137+
if (ffStrbufStartsWithIgnCaseS(&media->player, "com."))
138+
ffStrbufSubstrAfter(&media->player, strlen("com.") - 1);
139+
ffStrbufReplaceAllC(&media->player, '.', ' ');
140+
}
105141
}

0 commit comments

Comments
 (0)