diff --git a/.gitignore b/.gitignore index bd8b8bc..5475bbd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build/* .DS_Store +.cache/* src/rsrc.hpp PlayerLink.exe \ No newline at end of file diff --git a/src/MediaRemote.hpp b/src/MediaRemote.hpp index 3d5486a..cfda3bb 100644 --- a/src/MediaRemote.hpp +++ b/src/MediaRemote.hpp @@ -1,25 +1,17 @@ -// -// MediaRemote.h -// MusicRPC -// -// Created by Alexandra Aurora Göttlicher -// - +#ifdef __APPLE__ #import -FOUNDATION_EXPORT CFStringRef _Nullable kMRMediaRemoteNowPlayingInfoDidChangeNotification; - FOUNDATION_EXPORT CFStringRef _Nullable kMRMediaRemoteNowPlayingInfoTitle; FOUNDATION_EXPORT CFStringRef _Nullable kMRMediaRemoteNowPlayingInfoAlbum; FOUNDATION_EXPORT CFStringRef _Nullable kMRMediaRemoteNowPlayingInfoArtist; FOUNDATION_EXPORT CFStringRef _Nullable kMRMediaRemoteNowPlayingInfoDuration; FOUNDATION_EXPORT CFStringRef _Nullable kMRMediaRemoteNowPlayingInfoElapsedTime; +FOUNDATION_EXPORT CFStringRef _Nullable kMRMediaRemoteNowPlayingInfoArtworkData; +FOUNDATION_EXPORT CFStringRef _Nullable kMRMediaRemoteNowPlayingInfoPlaybackRate; typedef void (^ MRMediaRemoteGetNowPlayingInfoCompletion)(CFDictionaryRef _Nullable information); typedef void (^ MRMediaRemoteGetNowPlayingApplicationPIDCompletion)(int PID); -typedef void (^ MRMediaRemoteGetNowPlayingApplicationIsPlayingCompletion)(Boolean isPlaying); -FOUNDATION_EXPORT void MRMediaRemoteRegisterForNowPlayingNotifications(dispatch_queue_t _Nullable queue); FOUNDATION_EXPORT void MRMediaRemoteGetNowPlayingApplicationPID(dispatch_queue_t _Nullable queue, MRMediaRemoteGetNowPlayingApplicationPIDCompletion _Nullable completion); FOUNDATION_EXPORT void MRMediaRemoteGetNowPlayingInfo(dispatch_queue_t _Nullable queue, MRMediaRemoteGetNowPlayingInfoCompletion _Nullable completion); -FOUNDATION_EXPORT void MRMediaRemoteGetNowPlayingApplicationIsPlaying(dispatch_queue_t _Nullable queue, MRMediaRemoteGetNowPlayingApplicationIsPlayingCompletion _Nullable completion); +#endif \ No newline at end of file diff --git a/src/backends/darwin.mm b/src/backends/darwin.mm index e9fb827..f9fa0f3 100644 --- a/src/backends/darwin.mm +++ b/src/backends/darwin.mm @@ -1,32 +1,105 @@ #ifdef __APPLE__ -#include #include +#include #include +#include +#include +#include + #include "../MediaRemote.hpp" #include "../backend.hpp" +#define LAUNCH_AGENT_TEMPLATE \ + R"( + + + + Label + {} + ProgramArguments + + {} + + RunAtLoad + + AbandonProcessGroup + + +)" + std::shared_ptr backend::getMediaInformation() { - std::string appName = ""; - dispatch_semaphore_t semaphore = dispatch_semaphore_create(0); - MRMediaRemoteGetNowPlayingApplicationPID(dispatch_get_main_queue(), ^(pid_t pid) { - if (pid > 0) { - NSRunningApplication *app = [NSRunningApplication runningApplicationWithProcessIdentifier:pid]; - if (app) - appName = app.bundleIdentifier - } - dispatch_semaphore_signal(semaphore); - }); - MRMediaRemoteGetNowPlayingInfo(dispatch_get_main_queue(), ^(CFDictionaryRef result) { - if (result) { - NSDictionary *playingInfo = (__bridge NSDictionary *)(result); - NSLog(@"Now Playing Info: %@", playingInfo); + __block NSString *appName = nil; + __block NSDictionary *playingInfo = nil; + + dispatch_group_t group = dispatch_group_create(); + + dispatch_group_enter(group); + MRMediaRemoteGetNowPlayingApplicationPID(dispatch_get_main_queue(), ^(pid_t pid) { + if (pid > 0) { + NSRunningApplication *app = [NSRunningApplication runningApplicationWithProcessIdentifier:pid]; + if (app) + appName = [[app.bundleIdentifier copy] retain]; } - dispatch_semaphore_signal(semaphore); + dispatch_group_leave(group); }); - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); - dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER); - dispatch_release(semaphore); - return nullptr; + + dispatch_group_enter(group); + MRMediaRemoteGetNowPlayingInfo(dispatch_get_main_queue(), ^(CFDictionaryRef result) { + if (result) + playingInfo = [[(__bridge NSDictionary *)result copy] retain]; + dispatch_group_leave(group); + }); + + dispatch_group_wait(group, DISPATCH_TIME_FOREVER); + dispatch_release(group); + if (appName == nil || playingInfo == nil) + return nullptr; + + bool paused = [playingInfo[(__bridge NSString *)kMRMediaRemoteNowPlayingInfoPlaybackRate] intValue] == 0; + + NSString *title = playingInfo[(__bridge NSString *)kMRMediaRemoteNowPlayingInfoTitle]; + std::string songTitle = title ? [title UTF8String] : ""; + + NSString *album = playingInfo[(__bridge NSString *)kMRMediaRemoteNowPlayingInfoAlbum]; + std::string songAlbum = album ? [album UTF8String] : ""; + + NSString *artist = playingInfo[(__bridge NSString *)kMRMediaRemoteNowPlayingInfoArtist]; + std::string songArtist = artist ? [artist UTF8String] : ""; + + NSData *artworkData = playingInfo[(__bridge NSString *)kMRMediaRemoteNowPlayingInfoArtworkData]; + + std::string thumbnailData; + if (artworkData) + thumbnailData = std::string((const char *)[artworkData bytes], [artworkData length]); + + NSNumber *elapsedTimeNumber = playingInfo[(__bridge NSString *)kMRMediaRemoteNowPlayingInfoElapsedTime]; + + NSNumber *durationNumber = playingInfo[(__bridge NSString *)kMRMediaRemoteNowPlayingInfoDuration]; + + int64_t elapsedTimeMs = elapsedTimeNumber ? static_cast([elapsedTimeNumber doubleValue] * 1000) : 0; + + int64_t durationMs = durationNumber ? static_cast([durationNumber doubleValue] * 1000) : 0; + + std::string appNameString = appName.UTF8String; + + [appName release]; + [playingInfo release]; + return std::make_shared(paused, songTitle, songArtist, songAlbum, appNameString, thumbnailData, + durationMs, elapsedTimeMs); } -bool backend::toggleAutostart(bool enabled) { return false; } +bool backend::toggleAutostart(bool enabled) { + std::filesystem::path launchAgentPath = std::getenv("HOME"); + launchAgentPath = launchAgentPath / "Library" / "LaunchAgents" / "PlayerLink.plist"; + if (!enabled && std::filesystem::exists(launchAgentPath)) { + std::filesystem::remove(launchAgentPath); + return true; + } + NSString *binaryPath = [[[NSProcessInfo processInfo] arguments][0] stringByStandardizingPath]; + std::string formattedPlist = std::format(LAUNCH_AGENT_TEMPLATE, "PlayerLink", binaryPath.UTF8String); + std::ofstream o(launchAgentPath); + o.write(formattedPlist.c_str(), formattedPlist.size()); + o.close(); + return true; +} +#undef LAUNCH_AGENT_TEMPLATE #endif \ No newline at end of file