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 */; };
E4B11BB22274F9520075461B /* ResetAlbumListArt.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB12274F9520075461B /* ResetAlbumListArt.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 */; };
E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* MPDIdle.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>"; };
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>"; };
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>"; };
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>"; };
@ -444,6 +452,7 @@
children = (
E47E2FD62220720300F747E6 /* AlbumItemView.swift */,
E47E2FD222205D2500F747E6 /* MainWindow.swift */,
E4B11BB7227538FA0075461B /* CurrentArtView.swift */,
);
path = Views;
sourceTree = "<group>";
@ -622,6 +631,8 @@
E4B11BAC2274F2E80075461B /* UpdateAlbumArt.swift */,
E4B11BB12274F9520075461B /* ResetAlbumListArt.swift */,
E4B11BB32275002D0075461B /* UpdateMPDDatabase.swift */,
E4B11BB922753BF10075461B /* UpdateCurrentSong.swift */,
E4B11BBB227541C40075461B /* UpdateCurrentArtwork.swift */,
);
path = Actions;
sourceTree = "<group>";
@ -664,6 +675,7 @@
E4E8CC932206097F0024217A /* NotificationsController.swift */,
E4E8CC912204F4B80024217A /* QueueViewController.swift */,
E465049921E94DF500A70F4C /* WindowController.swift */,
E4B11BB52275374B0075461B /* UserNotificationsController.swift */,
);
path = Controllers;
sourceTree = "<group>";
@ -883,6 +895,7 @@
E4B11BA92274EDE30075461B /* Loading.swift in Sources */,
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */,
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */,
E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */,
E4B11B7B226D34F80075461B /* UpdateAlbumListAction.swift in Sources */,
E4B11B6D226A5B180075461B /* UpdateQueueAction.swift in Sources */,
E4B11B68226A4FA00075461B /* QueueState.swift in Sources */,
@ -913,6 +926,7 @@
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */,
E4B11B61226A4C000075461B /* PlayerReducer.swift in Sources */,
E465049A21E94DF500A70F4C /* WindowController.swift in Sources */,
E4B11BBA22753BF10075461B /* UpdateCurrentSong.swift in Sources */,
E4B11B71226A64E60075461B /* UpdateElapsedTimeAction.swift in Sources */,
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */,
E40F41F3221EDE27004B6CB8 /* Preferences.swift in Sources */,
@ -929,6 +943,7 @@
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */,
E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */,
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */,
E4B11BB8227538FA0075461B /* CurrentArtView.swift in Sources */,
E4E8CC9A22075D370024217A /* MPDSong.swift in Sources */,
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */,
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */,
@ -948,6 +963,7 @@
E4B11B6A226A4FBC0075461B /* AlbumListState.swift in Sources */,
E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */,
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */,
E4B11BBC227541C40075461B /* UpdateCurrentArtwork.swift in Sources */,
);
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 {
var preferences = Preferences()
var mediaKeyTap: MediaKeyTap?
var userNotificationsController: UserNotificationsController?
static let mpdClient = MPDClient(
withDelegate: NotificationsController()
@ -35,10 +36,10 @@ class AppDelegate: NSObject,
mediaKeyTap?.start()
AppDelegate.store.subscribe(self) {
(subscription: Subscription<AppState>) -> Subscription<PlayerState> in
subscription.select { state in state.playerState }
$0.select { $0.playerState }
}
userNotificationsController = UserNotificationsController()
}
func applicationWillTerminate(_ aNotification: Notification) {

View File

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

View File

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

View File

@ -20,9 +20,7 @@ class QueueViewController: NSViewController,
super.viewDidLoad()
AppDelegate.store.subscribe(self) {
(subscription: Subscription<AppState>) -> Subscription<QueueState> in
subscription.select { state in state.queueState }
$0.select { $0.queueState }
}
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(
_ outlineView: NSOutlineView,
selectionIndexesForProposedSelection proposedSelectionIndexes: IndexSet
@ -164,6 +130,5 @@ extension QueueViewController: StoreSubscriber {
func newState(state: StoreSubscriberStateType) {
dataSource.setQueueIcon()
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
AppDelegate.store.subscribe(self) {
(subscription: Subscription<AppState>) -> Subscription<PlayerState> in
subscription.select { state in state.playerState }
$0.select { $0.playerState }
}
trackProgress.font = .timerFont

View File

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

View File

@ -25,6 +25,32 @@ func playerReducer(action: Action, state: PlayerState?) -> PlayerState {
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:
state.elapsedTimeMs = action.elapsedTimeMs
@ -40,13 +66,3 @@ func playerReducer(action: Action, state: PlayerState?) -> PlayerState {
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.
//
import Cocoa
import ReSwift
func queueReducer(action: Action, state: QueueState?) -> QueueState {
@ -37,6 +38,12 @@ func queueReducer(action: Action, state: QueueState?) -> QueueState {
state.queue[newSongRowPos].isPlaying = true
}
DispatchQueue.main.async {
AppDelegate.store.dispatch(
UpdateCurrentSong(currentSong: state.queue[newSongRowPos].song)
)
}
default:
break
}

View File

@ -635,7 +635,7 @@
<rect key="frame" x="0.0" y="220" width="328" height="328"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<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"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="blankAlbum" id="IoN-3N-TCb"/>
</imageView>

View File

@ -11,6 +11,8 @@ import ReSwift
struct PlayerState: StateType {
var status: MPDClient.MPDStatus?
var currentSong: Song?
var currentArtwork: NSImage?
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
}
}
}