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

Add refresh album art option to album list context menu

This commit is contained in:
Daniel Barber 2020-02-03 22:30:53 -05:00
parent 567dc3bbe9
commit 03e7264a2e
Signed by: danbarber
GPG Key ID: 931D8112E0103DD8
12 changed files with 112 additions and 58 deletions

View File

@ -101,6 +101,7 @@
E4B3DF6523D66A4400728F6B /* QueueSongCoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B3DF6423D66A4400728F6B /* QueueSongCoverView.swift */; };
E4B5AE7E22F4C49600CCEC65 /* MPDServerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */; };
E4BB7F8F23E5E7BC00906E2F /* MPDAlbumArtImageDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4BB7F8E23E5E7BC00906E2F /* MPDAlbumArtImageDataProvider.swift */; };
E4BB7F9323E9150A00906E2F /* CoverArtService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4BB7F9223E9150A00906E2F /* CoverArtService.swift */; };
E4BBD2F323357C0700702C16 /* ArtistListState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4BBD2F223357C0700702C16 /* ArtistListState.swift */; };
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; };
E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* MPDIdle.swift */; };
@ -297,6 +298,7 @@
E4B3DF6423D66A4400728F6B /* QueueSongCoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueSongCoverView.swift; sourceTree = "<group>"; };
E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDServerDelegate.swift; sourceTree = "<group>"; };
E4BB7F8E23E5E7BC00906E2F /* MPDAlbumArtImageDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDAlbumArtImageDataProvider.swift; sourceTree = "<group>"; };
E4BB7F9223E9150A00906E2F /* CoverArtService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoverArtService.swift; sourceTree = "<group>"; };
E4BBD2F223357C0700702C16 /* ArtistListState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArtistListState.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>"; };
@ -638,6 +640,7 @@
isa = PBXGroup;
children = (
E439109722640213002982E9 /* SongNotifierService.swift */,
E4BB7F9223E9150A00906E2F /* CoverArtService.swift */,
);
path = Services;
sourceTree = "<group>";
@ -998,6 +1001,7 @@
E4F26F7923411B1500D45FF9 /* ArtistReducer.swift in Sources */,
E4B11B73226A6C770075461B /* TrackTimer.swift in Sources */,
E44051942278765A0090CD6F /* App.swift in Sources */,
E4BB7F9323E9150A00906E2F /* CoverArtService.swift in Sources */,
E4B11B79226D346B0075461B /* AlbumListReducer.swift in Sources */,
E47E2FE52220AA0700F747E6 /* FlexibleGridViewLayout.swift in Sources */,
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */,

View File

@ -93,6 +93,18 @@ class AlbumViewItem: NSCollectionViewItem {
}
}
func refreshAlbumArt() {
guard let album = album,
let mpdSong = album.mpdAlbum.firstSong
else { return }
let song = Song(mpdSong: mpdSong)
CoverArtService(song: song).refresh {
self.setAlbumCover(album)
}
}
@IBAction func showAlbumDetail(_ sender: NSButton) {
guard let album = album else { return }
@ -125,4 +137,8 @@ class AlbumViewItem: NSCollectionViewItem {
App.mpdClient.addAlbumToQueue(album: album.mpdAlbum, at: queueLength)
}
@IBAction func refreshAlbumArtMenuAction(_ sender: NSMenuItem) {
refreshAlbumArt()
}
}

View File

@ -35,7 +35,7 @@
</shadow>
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="defaultCoverArt" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" refusesFirstResponder="YES" state="on" transparent="YES" imageScaling="proportionallyUpOrDown" inset="2" id="t8A-Hz-L38">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="showAlbumDetail:" target="-2" id="A4Q-gb-B45"/>
@ -54,7 +54,7 @@
<textField identifier="albumTitle" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEh-NL-c2W">
<rect key="frame" x="8" y="28" width="142" height="17"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" title="Label" id="pDs-0t-e1j">
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@ -62,7 +62,7 @@
<textField identifier="albumArtist" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5Uu-j1-qyT">
<rect key="frame" x="8" y="10" width="142" height="16"/>
<textFieldCell key="cell" lineBreakMode="truncatingTail" title="Label" id="yZn-e9-zyP">
<font key="font" metaFont="label" size="13"/>
<font key="font" metaFont="system"/>
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
@ -98,6 +98,13 @@
<action selector="addAlbumToQueueMenuAction:" target="-2" id="6wW-oR-ykh"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="g9v-9R-xDB"/>
<menuItem title="Refresh album art" id="4Ld-db-wka">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="refreshAlbumArtMenuAction:" target="-2" id="wyO-Dn-k9r"/>
</connections>
</menuItem>
</items>
<point key="canvasLocation" x="191" y="38"/>
</menu>

View File

@ -17,6 +17,15 @@ class CurrentCoverArtView: NSImageView {
App.store.subscribe(self) {
$0.select { $0.playerState.currentSong }
}
NotificationCenter.default.addObserver(self, selector: #selector(didReloadAlbumArt), name: .didReloadAlbumArt, object: nil)
}
@objc func didReloadAlbumArt() {
guard let song = App.store.state.playerState.currentSong
else { return }
setSongImage(song)
}
func setSongImage(_ song: Song) {

View File

@ -24,6 +24,7 @@ class QueueViewController: NSViewController {
NotificationCenter.default.addObserver(self, selector: #selector(didConnect), name: .didConnect, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(willDisconnect), name: .willDisconnect, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(didReloadAlbumArt), name: .didReloadAlbumArt, object: nil)
queueView.dataSource = dataSource
queueView.columnAutoresizingStyle = .sequentialColumnAutoresizingStyle
@ -62,6 +63,10 @@ class QueueViewController: NSViewController {
}
}
@objc func didReloadAlbumArt() {
queueView.reloadData()
}
@IBAction func playTrack(_ sender: Any) {
let queuePos = queueView.selectedRow

View File

@ -15,34 +15,4 @@ extension NSImage {
static let queuePauseIcon = NSImage(named: "queuePauseButton")
static let defaultCoverArt = NSImage(named: "defaultCoverArt")
func toFitBox(size: NSSize) -> NSImage {
var newSize: NSSize = NSSize.zero
let aspectRatio = self.size.width / self.size.height
let boxAspectRatio = size.width / size.height
if aspectRatio > boxAspectRatio {
newSize = NSSize(width: size.width, height: size.width / aspectRatio)
} else {
newSize = NSSize(width: size.height * aspectRatio, height: size.height)
}
let newImage = NSImage(size: newSize)
newImage.lockFocus()
self.draw(in: newImage.alignmentRect)
newImage.unlockFocus()
return newImage
}
func jpegData(compressionQuality: CGFloat) -> Data? {
guard let image = cgImage(forProposedRect: nil, context: nil, hints: nil)
else { return nil }
let bitmapImageRep = NSBitmapImageRep(cgImage: image)
return bitmapImageRep.representation(
using: .jpeg,
properties: [.compressionFactor: compressionQuality]
)
}
}

View File

@ -11,4 +11,5 @@ import Foundation
extension Notification.Name {
static let didConnect = Notification.Name("MPDClientDidConnect")
static let willDisconnect = Notification.Name("MPDClientWillDisconnect")
static let didReloadAlbumArt = Notification.Name("MPDDidReloadAlbumArt")
}

View File

@ -432,9 +432,11 @@
<rect key="frame" x="0.0" y="0.0" width="420" height="60"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pRL-MG-1Be">
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pRL-MG-1Be">
<rect key="frame" x="82" y="24" width="264" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<constraints>
<constraint firstAttribute="width" constant="260" id="PJN-iZ-3RV"/>
</constraints>
<buttonCell key="cell" type="check" title="Fetch missing artwork from MusicBrainz" bezelStyle="regularSquare" imagePosition="left" enabled="NO" inset="2" id="LpD-Ew-HMd">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
@ -444,6 +446,10 @@
</connections>
</button>
</subviews>
<constraints>
<constraint firstItem="pRL-MG-1Be" firstAttribute="leading" secondItem="PyK-v2-kus" secondAttribute="leading" constant="84" id="81s-YL-o8A"/>
<constraint firstAttribute="bottom" secondItem="pRL-MG-1Be" secondAttribute="bottom" constant="26" id="L7b-jE-keG"/>
</constraints>
</view>
<connections>
<outlet property="fetchMissingArtworkFromInternet" destination="pRL-MG-1Be" id="Xcp-sb-iZm"/>
@ -602,7 +608,7 @@
<tableColumns>
<tableColumn identifier="songCoverColumn" width="43" minWidth="42" maxWidth="1000" id="0Co-uF-CCB" userLabel="Position">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
<font key="font" metaFont="menu" size="11"/>
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
</tableHeaderCell>
@ -648,7 +654,7 @@
</tableColumn>
<tableColumn identifier="songInfoColumn" width="213" minWidth="10" maxWidth="3.4028234663852886e+38" id="HP0-ty-PFY" userLabel="Song Info">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<font key="font" metaFont="menu" size="11"/>
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tableHeaderCell>
@ -697,7 +703,7 @@
</tableColumn>
<tableColumn identifier="songDurationColumn" width="64" minWidth="64" maxWidth="64" id="8O6-ox-kx2" userLabel="Duration">
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
<font key="font" metaFont="menu" size="11"/>
<font key="font" metaFont="message" size="11"/>
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</tableHeaderCell>

View File

@ -0,0 +1,56 @@
//
// CoverArtService.swift
// Persephone
//
// Created by Daniel Barber on 2/3/20.
// Copyright © 2020 Dan Barber. All rights reserved.
//
import Foundation
import Kingfisher
struct CoverArtService {
let song: Song
func refresh(callback: @escaping () -> Void?) {
let provider = MPDAlbumArtImageDataProvider(
songUri: song.mpdSong.uriString,
cacheKey: song.album.hash
)
_ = KingfisherManager.shared.retrieveImage(
with: .provider(provider),
options: [
.forceRefresh,
.memoryCacheExpiration(.never),
.processor(DownsamplingImageProcessor(size: .albumListCoverSize)),
.scaleFactor(2),
]
) { _ in
callback()
}
_ = KingfisherManager.shared.retrieveImage(
with: .provider(provider),
options: [
.forceRefresh,
.memoryCacheExpiration(.never),
.processor(DownsamplingImageProcessor(size: .currentlyPlayingCoverSize)),
.scaleFactor(2),
.callbackQueue(.mainAsync)
]
) { _ in }
_ = KingfisherManager.shared.retrieveImage(
with: .provider(provider),
options: [
.forceRefresh,
.memoryCacheExpiration(.never),
.processor(DownsamplingImageProcessor(size: .queueSongCoverSize)),
.scaleFactor(2),
]
) { _ in
NotificationCenter.default.post(name: .didReloadAlbumArt, object: nil)
}
}
}

View File

@ -16,10 +16,6 @@ struct UpdateServerPort: Action {
var port: Int?
}
struct UpdateMPDLibraryDir: Action {
var mpdLibraryDir: String?
}
struct UpdateFetchMissingArtworkFromInternet: Action {
var fetchMissingArtworkFromInternet: Bool
}

View File

@ -13,17 +13,6 @@ struct PreferencesState: StateType, Equatable {
let preferences = UserDefaults.standard
var mpdServer: MPDServer
let mpdLibraryDirDefault = "~/Music"
var mpdLibraryDir: String?
var mpdLibraryDirOrDefault: String {
return mpdLibraryDir ?? mpdLibraryDirDefault
}
var expandedMpdLibraryDir: String {
return NSString(string: mpdLibraryDirOrDefault).expandingTildeInPath
}
var fetchMissingArtworkFromInternet: Bool
@ -32,7 +21,6 @@ struct PreferencesState: StateType, Equatable {
host: preferences.string(forKey: "mpdHost"),
port: preferences.value(forKey: "mpdPort") as? Int
)
self.mpdLibraryDir = preferences.string(forKey: "mpdLibraryDir")
self.fetchMissingArtworkFromInternet = preferences.bool(
forKey: "fetchMissingArtworkFromInternet"
)
@ -45,7 +33,6 @@ struct PreferencesState: StateType, Equatable {
} else {
preferences.removeObject(forKey: "mpdPort")
}
preferences.set(mpdLibraryDir, forKey: "mpdLibraryDir")
preferences.set(fetchMissingArtworkFromInternet, forKey: "fetchMissingArtworkFromInternet")
}
}

View File

@ -20,9 +20,6 @@ func preferencesReducer(action: Action, state: PreferencesState?) -> Preferences
case let action as UpdateServerPort:
state.mpdServer.port = action.port
case let action as UpdateMPDLibraryDir:
state.mpdLibraryDir = action.mpdLibraryDir
case is SavePreferences:
state.save()