|
1 | 1 | #include "fastfetch.h" |
2 | 2 | #include "detection/media/media.h" |
3 | 3 | #include "common/processing.h" |
| 4 | +#include "util/apple/cf_helpers.h" |
4 | 5 |
|
5 | 6 | #import <Foundation/Foundation.h> |
| 7 | +#import <CoreFoundation/CoreFoundation.h> |
6 | 8 |
|
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)); |
10 | 14 |
|
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 |
15 | 21 |
|
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); |
19 | 24 |
|
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"; |
25 | 36 |
|
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; |
43 | 71 | } |
44 | 72 |
|
45 | 73 | __attribute__((visibility("default"), used)) |
@@ -101,5 +129,13 @@ void ffDetectMediaImpl(FFMediaResult* media) |
101 | 129 | error = getMediaByAuthorizedProcess(media); |
102 | 130 | else |
103 | 131 | 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 | + } |
105 | 141 | } |
0 commit comments