1
1
mirror of https://github.com/danbee/persephone synced 2025-03-04 08:39:11 +00:00

Current artwork and notifications

This commit is contained in:
Daniel Barber 2019-04-27 22:38:04 -04:00
parent 63afa3ffce
commit d2d4705e87
Signed by: danbarber
GPG Key ID: 931D8112E0103DD8
15 changed files with 162 additions and 58 deletions

View File

@ -100,6 +100,10 @@
E4B11BB02274F71A0075461B /* EnumEquatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BAF2274F71A0075461B /* EnumEquatable.swift */; }; E4B11BB02274F71A0075461B /* EnumEquatable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BAF2274F71A0075461B /* EnumEquatable.swift */; };
E4B11BB22274F9520075461B /* ResetAlbumListArt.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB12274F9520075461B /* ResetAlbumListArt.swift */; }; E4B11BB22274F9520075461B /* ResetAlbumListArt.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB12274F9520075461B /* ResetAlbumListArt.swift */; };
E4B11BB42275002D0075461B /* UpdateMPDDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB32275002D0075461B /* UpdateMPDDatabase.swift */; }; E4B11BB42275002D0075461B /* UpdateMPDDatabase.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB32275002D0075461B /* UpdateMPDDatabase.swift */; };
E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB52275374B0075461B /* UserNotificationsController.swift */; };
E4B11BB8227538FA0075461B /* CurrentArtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB7227538FA0075461B /* CurrentArtView.swift */; };
E4B11BBA22753BF10075461B /* UpdateCurrentSong.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB922753BF10075461B /* UpdateCurrentSong.swift */; };
E4B11BBC227541C40075461B /* UpdateCurrentArtwork.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BBB227541C40075461B /* UpdateCurrentArtwork.swift */; };
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; }; E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; };
E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* MPDIdle.swift */; }; E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* MPDIdle.swift */; };
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC8F2204EC7F0024217A /* Delegate.swift */; }; E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC8F2204EC7F0024217A /* Delegate.swift */; };
@ -291,6 +295,10 @@
E4B11BAF2274F71A0075461B /* EnumEquatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumEquatable.swift; sourceTree = "<group>"; }; E4B11BAF2274F71A0075461B /* EnumEquatable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EnumEquatable.swift; sourceTree = "<group>"; };
E4B11BB12274F9520075461B /* ResetAlbumListArt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetAlbumListArt.swift; sourceTree = "<group>"; }; E4B11BB12274F9520075461B /* ResetAlbumListArt.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResetAlbumListArt.swift; sourceTree = "<group>"; };
E4B11BB32275002D0075461B /* UpdateMPDDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateMPDDatabase.swift; sourceTree = "<group>"; }; E4B11BB32275002D0075461B /* UpdateMPDDatabase.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateMPDDatabase.swift; sourceTree = "<group>"; };
E4B11BB52275374B0075461B /* UserNotificationsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationsController.swift; sourceTree = "<group>"; };
E4B11BB7227538FA0075461B /* CurrentArtView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentArtView.swift; sourceTree = "<group>"; };
E4B11BB922753BF10075461B /* UpdateCurrentSong.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCurrentSong.swift; sourceTree = "<group>"; };
E4B11BBB227541C40075461B /* UpdateCurrentArtwork.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateCurrentArtwork.swift; sourceTree = "<group>"; };
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; }; E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
E4C8B53D22349002009A20F3 /* MPDIdle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDIdle.swift; sourceTree = "<group>"; }; E4C8B53D22349002009A20F3 /* MPDIdle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDIdle.swift; sourceTree = "<group>"; };
E4E8CC8F2204EC7F0024217A /* Delegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delegate.swift; sourceTree = "<group>"; }; E4E8CC8F2204EC7F0024217A /* Delegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delegate.swift; sourceTree = "<group>"; };
@ -444,6 +452,7 @@
children = ( children = (
E47E2FD62220720300F747E6 /* AlbumItemView.swift */, E47E2FD62220720300F747E6 /* AlbumItemView.swift */,
E47E2FD222205D2500F747E6 /* MainWindow.swift */, E47E2FD222205D2500F747E6 /* MainWindow.swift */,
E4B11BB7227538FA0075461B /* CurrentArtView.swift */,
); );
path = Views; path = Views;
sourceTree = "<group>"; sourceTree = "<group>";
@ -622,6 +631,8 @@
E4B11BAC2274F2E80075461B /* UpdateAlbumArt.swift */, E4B11BAC2274F2E80075461B /* UpdateAlbumArt.swift */,
E4B11BB12274F9520075461B /* ResetAlbumListArt.swift */, E4B11BB12274F9520075461B /* ResetAlbumListArt.swift */,
E4B11BB32275002D0075461B /* UpdateMPDDatabase.swift */, E4B11BB32275002D0075461B /* UpdateMPDDatabase.swift */,
E4B11BB922753BF10075461B /* UpdateCurrentSong.swift */,
E4B11BBB227541C40075461B /* UpdateCurrentArtwork.swift */,
); );
path = Actions; path = Actions;
sourceTree = "<group>"; sourceTree = "<group>";
@ -664,6 +675,7 @@
E4E8CC932206097F0024217A /* NotificationsController.swift */, E4E8CC932206097F0024217A /* NotificationsController.swift */,
E4E8CC912204F4B80024217A /* QueueViewController.swift */, E4E8CC912204F4B80024217A /* QueueViewController.swift */,
E465049921E94DF500A70F4C /* WindowController.swift */, E465049921E94DF500A70F4C /* WindowController.swift */,
E4B11BB52275374B0075461B /* UserNotificationsController.swift */,
); );
path = Controllers; path = Controllers;
sourceTree = "<group>"; sourceTree = "<group>";
@ -883,6 +895,7 @@
E4B11BA92274EDE30075461B /* Loading.swift in Sources */, E4B11BA92274EDE30075461B /* Loading.swift in Sources */,
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */, E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */,
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */, E40FE71B221B904300A4223F /* NSEvent.swift in Sources */,
E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */,
E4B11B7B226D34F80075461B /* UpdateAlbumListAction.swift in Sources */, E4B11B7B226D34F80075461B /* UpdateAlbumListAction.swift in Sources */,
E4B11B6D226A5B180075461B /* UpdateQueueAction.swift in Sources */, E4B11B6D226A5B180075461B /* UpdateQueueAction.swift in Sources */,
E4B11B68226A4FA00075461B /* QueueState.swift in Sources */, E4B11B68226A4FA00075461B /* QueueState.swift in Sources */,
@ -913,6 +926,7 @@
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */, E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */,
E4B11B61226A4C000075461B /* PlayerReducer.swift in Sources */, E4B11B61226A4C000075461B /* PlayerReducer.swift in Sources */,
E465049A21E94DF500A70F4C /* WindowController.swift in Sources */, E465049A21E94DF500A70F4C /* WindowController.swift in Sources */,
E4B11BBA22753BF10075461B /* UpdateCurrentSong.swift in Sources */,
E4B11B71226A64E60075461B /* UpdateElapsedTimeAction.swift in Sources */, E4B11B71226A64E60075461B /* UpdateElapsedTimeAction.swift in Sources */,
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */, E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */,
E40F41F3221EDE27004B6CB8 /* Preferences.swift in Sources */, E40F41F3221EDE27004B6CB8 /* Preferences.swift in Sources */,
@ -929,6 +943,7 @@
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */, E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */,
E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */, E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */,
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */, E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */,
E4B11BB8227538FA0075461B /* CurrentArtView.swift in Sources */,
E4E8CC9A22075D370024217A /* MPDSong.swift in Sources */, E4E8CC9A22075D370024217A /* MPDSong.swift in Sources */,
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */, E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */,
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */, E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */,
@ -948,6 +963,7 @@
E4B11B6A226A4FBC0075461B /* AlbumListState.swift in Sources */, E4B11B6A226A4FBC0075461B /* AlbumListState.swift in Sources */,
E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */, E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */,
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */, E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */,
E4B11BBC227541C40075461B /* UpdateCurrentArtwork.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };

View File

@ -0,0 +1,14 @@
//
// UpdateCurrentArtwork.swift
// Persephone
//
// Created by Daniel Barber on 2019/4/27.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Cocoa
import ReSwift
struct UpdateCurrentArtwork: Action {
var coverArt: NSImage?
}

View File

@ -0,0 +1,13 @@
//
// UpdateCurrentSong.swift
// Persephone
//
// Created by Daniel Barber on 2019/4/27.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import ReSwift
struct UpdateCurrentSong: Action {
var currentSong: Song
}

View File

@ -16,6 +16,7 @@ class AppDelegate: NSObject,
MediaKeyTapDelegate { MediaKeyTapDelegate {
var preferences = Preferences() var preferences = Preferences()
var mediaKeyTap: MediaKeyTap? var mediaKeyTap: MediaKeyTap?
var userNotificationsController: UserNotificationsController?
static let mpdClient = MPDClient( static let mpdClient = MPDClient(
withDelegate: NotificationsController() withDelegate: NotificationsController()
@ -35,10 +36,10 @@ class AppDelegate: NSObject,
mediaKeyTap?.start() mediaKeyTap?.start()
AppDelegate.store.subscribe(self) { AppDelegate.store.subscribe(self) {
(subscription: Subscription<AppState>) -> Subscription<PlayerState> in $0.select { $0.playerState }
subscription.select { state in state.playerState }
} }
userNotificationsController = UserNotificationsController()
} }
func applicationWillTerminate(_ aNotification: Notification) { func applicationWillTerminate(_ aNotification: Notification) {

View File

@ -24,9 +24,7 @@ class AlbumViewController: NSViewController,
super.viewDidLoad() super.viewDidLoad()
AppDelegate.store.subscribe(self) { AppDelegate.store.subscribe(self) {
(subscription: Subscription<AppState>) -> Subscription<AlbumListState> in $0.select { $0.albumListState }
subscription.select { state -> AlbumListState in state.albumListState }
} }
albumScrollView.postsBoundsChangedNotifications = true albumScrollView.postsBoundsChangedNotifications = true

View File

@ -34,7 +34,7 @@ class AlbumViewItem: NSCollectionViewItem {
switch album.coverArt { switch album.coverArt {
case .loaded(let coverArt): case .loaded(let coverArt):
albumCoverView.image = coverArt albumCoverView.image = coverArt ?? .defaultCoverArt
default: default:
albumCoverView.image = .defaultCoverArt albumCoverView.image = .defaultCoverArt
} }

View File

@ -20,9 +20,7 @@ class QueueViewController: NSViewController,
super.viewDidLoad() super.viewDidLoad()
AppDelegate.store.subscribe(self) { AppDelegate.store.subscribe(self) {
(subscription: Subscription<AppState>) -> Subscription<QueueState> in $0.select { $0.queueState }
subscription.select { state in state.queueState }
} }
queueView.dataSource = dataSource queueView.dataSource = dataSource
@ -52,38 +50,6 @@ class QueueViewController: NSViewController,
} }
} }
func notifyTrack(_ state: QueueState) {
guard let currentSong = state.currentSong,
let status = AppDelegate.mpdClient.status,
status.state == .playing
else { return }
SongNotifierService(song: currentSong, image: queueAlbumArtImage.image)
.deliver()
}
func updateAlbumArt(_ state: QueueState) {
if let playingQueueItem = state.queue.first(
where: { $0.isPlaying }
) {
let albumArtService = AlbumArtService(song: playingQueueItem.song)
albumArtService.fetchBigAlbumArt()
.done() {
if let image = $0 {
self.queueAlbumArtImage.image = image
} else {
self.queueAlbumArtImage.image = NSImage.defaultCoverArt
}
self.notifyTrack(state)
}
.cauterize()
} else {
queueAlbumArtImage.image = NSImage.defaultCoverArt
}
}
func outlineView( func outlineView(
_ outlineView: NSOutlineView, _ outlineView: NSOutlineView,
selectionIndexesForProposedSelection proposedSelectionIndexes: IndexSet selectionIndexesForProposedSelection proposedSelectionIndexes: IndexSet
@ -164,6 +130,5 @@ extension QueueViewController: StoreSubscriber {
func newState(state: StoreSubscriberStateType) { func newState(state: StoreSubscriberStateType) {
dataSource.setQueueIcon() dataSource.setQueueIcon()
queueView.reloadData() queueView.reloadData()
updateAlbumArt(state)
} }
} }

View File

@ -0,0 +1,42 @@
//
// UserNotificationsController.swift
// Persephone
//
// Created by Daniel Barber on 2019/4/27.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
import ReSwift
class UserNotificationsController {
init() {
AppDelegate.store.subscribe(self) {
$0.select { $0.playerState.currentSong }
}
}
func notifyTrack(_ state: Song?) {
guard let currentSong = state,
let status = AppDelegate.mpdClient.status,
status.state == .playing
else { return }
let albumArtService = AlbumArtService(song: currentSong)
albumArtService.fetchBigAlbumArt()
.done() {
SongNotifierService(song: currentSong, image: $0)
.deliver()
}
.cauterize()
}
}
extension UserNotificationsController: StoreSubscriber {
typealias StoreSubscriberStateType = Song?
func newState(state: Song?) {
notifyTrack(state)
}
}

View File

@ -22,9 +22,7 @@ class WindowController: NSWindowController {
window?.titleVisibility = .hidden window?.titleVisibility = .hidden
AppDelegate.store.subscribe(self) { AppDelegate.store.subscribe(self) {
(subscription: Subscription<AppState>) -> Subscription<PlayerState> in $0.select { $0.playerState }
subscription.select { state in state.playerState }
} }
trackProgress.font = .timerFont trackProgress.font = .timerFont

View File

@ -17,7 +17,7 @@
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>0.11.2-alpha</string> <string>0.12.0-pre-alpha</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>1</string> <string>1</string>
<key>LSApplicationCategoryType</key> <key>LSApplicationCategoryType</key>

View File

@ -25,6 +25,32 @@ func playerReducer(action: Action, state: PlayerState?) -> PlayerState {
AppDelegate.trackTimer.stop(elapsedTimeMs: state.elapsedTimeMs) AppDelegate.trackTimer.stop(elapsedTimeMs: state.elapsedTimeMs)
} }
case let action as UpdateCurrentSong:
state.currentSong = action.currentSong
if let currentSong = state.currentSong {
let albumArtService = AlbumArtService(song: currentSong)
albumArtService.fetchBigAlbumArt()
.done() { image in
DispatchQueue.main.async {
if let image = image {
AppDelegate.store.dispatch(UpdateCurrentArtwork(coverArt: image))
} else {
AppDelegate.store.dispatch(UpdateCurrentArtwork(coverArt: .defaultCoverArt))
}
}
}
.cauterize()
} else {
DispatchQueue.main.async {
AppDelegate.store.dispatch(UpdateCurrentArtwork(coverArt: .defaultCoverArt))
}
}
case let action as UpdateCurrentArtwork:
state.currentArtwork = action.coverArt
case let action as UpdateElapsedTimeAction: case let action as UpdateElapsedTimeAction:
state.elapsedTimeMs = action.elapsedTimeMs state.elapsedTimeMs = action.elapsedTimeMs
@ -40,13 +66,3 @@ func playerReducer(action: Action, state: PlayerState?) -> PlayerState {
return state return state
} }
func updateElapsedTime(_ timer: Timer) {
guard let userInfo = timer.userInfo as? Dictionary<String, Any>,
let elapsedTimeMs = userInfo["elapsedTimeMs"] as? UInt
else { return }
AppDelegate.store.dispatch(
UpdateElapsedTimeAction(elapsedTimeMs: elapsedTimeMs)
)
}

View File

@ -6,6 +6,7 @@
// Copyright © 2019 Dan Barber. All rights reserved. // Copyright © 2019 Dan Barber. All rights reserved.
// //
import Cocoa
import ReSwift import ReSwift
func queueReducer(action: Action, state: QueueState?) -> QueueState { func queueReducer(action: Action, state: QueueState?) -> QueueState {
@ -37,6 +38,12 @@ func queueReducer(action: Action, state: QueueState?) -> QueueState {
state.queue[newSongRowPos].isPlaying = true state.queue[newSongRowPos].isPlaying = true
} }
DispatchQueue.main.async {
AppDelegate.store.dispatch(
UpdateCurrentSong(currentSong: state.queue[newSongRowPos].song)
)
}
default: default:
break break
} }

View File

@ -635,7 +635,7 @@
<rect key="frame" x="0.0" y="220" width="328" height="328"/> <rect key="frame" x="0.0" y="220" width="328" height="328"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/> <autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews> <subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="Dw3-M5-tWY"> <imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="Dw3-M5-tWY" customClass="CurrentArtView" customModule="Persephone" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="328" height="328"/> <rect key="frame" x="0.0" y="0.0" width="328" height="328"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="blankAlbum" id="IoN-3N-TCb"/> <imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="blankAlbum" id="IoN-3N-TCb"/>
</imageView> </imageView>

View File

@ -11,6 +11,8 @@ import ReSwift
struct PlayerState: StateType { struct PlayerState: StateType {
var status: MPDClient.MPDStatus? var status: MPDClient.MPDStatus?
var currentSong: Song?
var currentArtwork: NSImage?
var state: MPDClient.MPDStatus.State? var state: MPDClient.MPDStatus.State?

View File

@ -0,0 +1,32 @@
//
// CurrentArtView.swift
// Persephone
//
// Created by Daniel Barber on 2019/4/27.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Cocoa
import ReSwift
class CurrentArtView: NSImageView {
required init?(coder: NSCoder) {
super.init(coder: coder)
AppDelegate.store.subscribe(self) {
$0.select { $0.playerState.currentArtwork }
}
}
}
extension CurrentArtView: StoreSubscriber {
typealias StoreSubscriberStateType = NSImage?
func newState(state: NSImage?) {
if let coverArt = state {
image = coverArt
} else {
image = .defaultCoverArt
}
}
}