diff --git a/Mac/App.swift b/Mac/App.swift index bb3f6b6..aa14f0e 100644 --- a/Mac/App.swift +++ b/Mac/App.swift @@ -12,6 +12,8 @@ struct App { static let store = Store(reducer: appReducer, state: nil) static let trackTimer = TrackTimer() static let userNotificationsController = UserNotificationsController() + static let mediaInfoController = MediaInfoController() + static let playerStateInfoController = PlayerStateInfoController() static let mpdServerDelegate = MPDServerDelegate() static let mpdServerController = MPDServerController(delegate: mpdServerDelegate) static var mpdClient: MPDClient! diff --git a/Mac/AppDelegate.swift b/Mac/AppDelegate.swift index 94da4dc..d84c089 100644 --- a/Mac/AppDelegate.swift +++ b/Mac/AppDelegate.swift @@ -40,7 +40,9 @@ class AppDelegate: NSObject, } _ = App.userNotificationsController - + _ = App.mediaInfoController + _ = App.playerStateInfoController + _ = App.mpdServerController } diff --git a/Mac/Components/Shared/MediaInfoController.swift b/Mac/Components/Shared/MediaInfoController.swift new file mode 100644 index 0000000..b1c1fe2 --- /dev/null +++ b/Mac/Components/Shared/MediaInfoController.swift @@ -0,0 +1,76 @@ +// +// MediaInfoController.swift +// Persephone +// +// Created by Alan Harper on 18/11/20. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import Foundation +import ReSwift +import MediaPlayer +import Kingfisher + +class MediaInfoController { + init() { + App.store.subscribe(self) { + $0.select { $0.playerState.currentSong } + } + } + + func notifyTrack(_ song: Song) { + let provider = MPDAlbumArtImageDataProvider( + songUri: song.mpdSong.uriString, + cacheKey: song.album.hash + ) + + let infoCenter = MPNowPlayingInfoCenter.default() + + var nowPlayingInfo = [ + MPMediaItemPropertyTitle: song.title, + MPMediaItemPropertyArtist: song.artist, + MPMediaItemPropertyAlbumArtist: song.album.artist, + MPMediaItemPropertyAlbumTitle: song.album.title, + MPMediaItemPropertyPlaybackDuration: NSNumber(value: song.duration.timeInSeconds), + MPNowPlayingInfoPropertyMediaType: NSNumber(value: MPNowPlayingInfoMediaType.audio.rawValue), + MPNowPlayingInfoPropertyIsLiveStream: NSNumber(value: false), + ] as [String : Any] + + if let elapsedTime = App.store.state.playerState.elapsedTimeMs { + nowPlayingInfo[MPNowPlayingInfoPropertyElapsedPlaybackTime] = NSNumber(value: elapsedTime / 1000) + } + + if #available(OSX 10.13.2, *) { + _ = KingfisherManager.shared.retrieveImage( + with: .provider(provider), + options: [ + .processor(DownsamplingImageProcessor(size: .notificationCoverSize)), + .scaleFactor(2), + ] + ) { result in + switch result { + case .success(let value): + let artwork = MPMediaItemArtwork.init(boundsSize: CGSize(width: 100.0, height: 100.0)) { (size: CGSize) in value.image } + nowPlayingInfo[MPMediaItemPropertyArtwork] = artwork + infoCenter.nowPlayingInfo = nowPlayingInfo + case .failure: + infoCenter.nowPlayingInfo = nowPlayingInfo + break + } + } + } else { + infoCenter.nowPlayingInfo = nowPlayingInfo + } + } +} + +extension MediaInfoController: StoreSubscriber { + typealias StoreSubscriberStateType = Song? + + + func newState(state: StoreSubscriberStateType) { + guard let song = state else {return} + + notifyTrack(song) + } +} diff --git a/Mac/Components/Shared/PlayerStateInfoController.swift b/Mac/Components/Shared/PlayerStateInfoController.swift new file mode 100644 index 0000000..ff6104f --- /dev/null +++ b/Mac/Components/Shared/PlayerStateInfoController.swift @@ -0,0 +1,72 @@ +// +// MediaInfoController.swift +// Persephone +// +// Created by Alan Harper on 18/11/20. +// Copyright © 2020 Dan Barber. All rights reserved. +// + +import Foundation +import ReSwift +import MediaPlayer +import Kingfisher + +class PlayerStateInfoController { + init() { + App.store.subscribe(self) { + $0.select { $0.playerState.state } + } + let commandCenter = MPRemoteCommandCenter.shared() + commandCenter.playCommand.addTarget { _ in + App.mpdClient.playPause() + return .success + } + commandCenter.togglePlayPauseCommand.addTarget { _ in + App.mpdClient.playPause() + return .success + } + commandCenter.pauseCommand.addTarget { _ in + App.mpdClient.playPause() + return .success + } + + commandCenter.nextTrackCommand.addTarget { _ in + App.mpdClient.nextTrack() + return .success + } + + commandCenter.previousTrackCommand.addTarget { _ in + App.mpdClient.prevTrack() + return .success + } + } + + func notifyState(_ state: MPDClient.MPDStatus.State?) { + let infoCenter = MPNowPlayingInfoCenter.default() + + switch(state) { + case .unknown: + infoCenter.playbackState = .unknown + break; + case .paused: + infoCenter.playbackState = .paused + break; + case .stopped: + infoCenter.playbackState = .stopped + break; + case .playing: + infoCenter.playbackState = .playing + break; + case .none: + return + } + } +} + +extension PlayerStateInfoController: StoreSubscriber { + typealias StoreSubscriberStateType = MPDClient.MPDStatus.State? + + func newState(state: StoreSubscriberStateType) { + notifyState(state) + } +} diff --git a/Persephone.xcodeproj/project.pbxproj b/Persephone.xcodeproj/project.pbxproj index 1e430df..0f37df4 100644 --- a/Persephone.xcodeproj/project.pbxproj +++ b/Persephone.xcodeproj/project.pbxproj @@ -307,6 +307,8 @@ E4FF7190227601B400D4C412 /* PreferencesReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4FF718F227601B400D4C412 /* PreferencesReducer.swift */; }; E4FF71922276029000D4C412 /* PreferencesActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4FF71912276029000D4C412 /* PreferencesActions.swift */; }; E4FF71942276043A00D4C412 /* MPDServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4FF71932276043A00D4C412 /* MPDServer.swift */; }; + E72BF0CE2567F45E00419CB0 /* PlayerStateInfoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E72BF0CD2567F45E00419CB0 /* PlayerStateInfoController.swift */; }; + E7AF1B642565124D00057784 /* MediaInfoController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7AF1B632565124D00057784 /* MediaInfoController.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -650,6 +652,8 @@ E4FF718F227601B400D4C412 /* PreferencesReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesReducer.swift; sourceTree = ""; }; E4FF71912276029000D4C412 /* PreferencesActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesActions.swift; sourceTree = ""; }; E4FF71932276043A00D4C412 /* MPDServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDServer.swift; sourceTree = ""; }; + E72BF0CD2567F45E00419CB0 /* PlayerStateInfoController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerStateInfoController.swift; sourceTree = ""; }; + E7AF1B632565124D00057784 /* MediaInfoController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaInfoController.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -958,6 +962,8 @@ E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */, E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */, E4B11BB52275374B0075461B /* UserNotificationsController.swift */, + E7AF1B632565124D00057784 /* MediaInfoController.swift */, + E72BF0CD2567F45E00419CB0 /* PlayerStateInfoController.swift */, ); path = Shared; sourceTree = ""; @@ -1643,6 +1649,7 @@ E48059D82426D73600362CF3 /* database.c in Sources */, E43AC1F522C6A4F4001E483C /* DraggedAlbum.swift in Sources */, E40FE71B221B904300A4223F /* NSEvent.swift in Sources */, + E72BF0CE2567F45E00419CB0 /* PlayerStateInfoController.swift in Sources */, E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */, E43AC1F622C6AD0B001E483C /* AlbumViewController+NSCollectionViewDelegate.swift in Sources */, E4B11B68226A4FA00075461B /* QueueState.swift in Sources */, @@ -1674,6 +1681,7 @@ E4805A0A2426D73600362CF3 /* sync.c in Sources */, E4DA820623D6236200C1EE58 /* NSSize.swift in Sources */, E408D3B6220DD8970006D9BE /* Notification.swift in Sources */, + E7AF1B642565124D00057784 /* MediaInfoController.swift in Sources */, E48059E42426D73600362CF3 /* run.c in Sources */, E4805A1A2426D73600362CF3 /* cmessage.c in Sources */, E48059C42426D73600362CF3 /* replay_gain.c in Sources */,