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

Fetching most album art via MPD connection now

TODO: Dragged song view is broken
This commit is contained in:
Daniel Barber 2020-02-02 14:48:38 -05:00
parent fe61f5558b
commit 2045f37f5e
Signed by: danbarber
GPG Key ID: 931D8112E0103DD8
22 changed files with 233 additions and 98 deletions

View File

@ -81,6 +81,8 @@
E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A642D922090CBE00067D21 /* MPDStatus.swift */; };
E4A83BEF2221F8CF0098FED6 /* CoverArtPrefsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BEE2221F8CF0098FED6 /* CoverArtPrefsController.swift */; };
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */; };
E4B079C723E5E0AD0044B6D3 /* libmpdclient.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; };
E4B079C823E5E0AD0044B6D3 /* libmpdclient.2.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
E4B11B53226928F20075461B /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B52226928F20075461B /* AppState.swift */; };
E4B11B61226A4C000075461B /* PlayerReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B60226A4BFF0075461B /* PlayerReducer.swift */; };
E4B11B63226A4C510075461B /* AppReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B62226A4C510075461B /* AppReducer.swift */; };
@ -90,8 +92,6 @@
E4B11B73226A6C770075461B /* TrackTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B72226A6C770075461B /* TrackTimer.swift */; };
E4B11B75226CC4D30075461B /* QueueReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B74226CC4D30075461B /* QueueReducer.swift */; };
E4B11B79226D346B0075461B /* AlbumListReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B78226D346B0075461B /* AlbumListReducer.swift */; };
E4B11BA62274E44A0075461B /* libmpdclient.2.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
E4B11BA72274E4500075461B /* libmpdclient.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; };
E4B11BA92274EDE30075461B /* Loading.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BA82274EDE30075461B /* Loading.swift */; };
E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB52275374B0075461B /* UserNotificationsController.swift */; };
E4B11BB8227538FA0075461B /* CurrentCoverArtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB7227538FA0075461B /* CurrentCoverArtView.swift */; };
@ -100,11 +100,13 @@
E4B11BC22275EE410075461B /* AlbumListActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BC12275EE410075461B /* AlbumListActions.swift */; };
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 */; };
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 */; };
E4D3BFA622B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4D3BFA522B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift */; };
E4DA820623D6236200C1EE58 /* NSSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DA820523D6236200C1EE58 /* NSSize.swift */; };
E4DCCFAE23E4DB5D009A8113 /* MPDClientWrapper.c in Sources */ = {isa = PBXBuildFile; fileRef = E4DCCFAD23E4DB5D009A8113 /* MPDClientWrapper.c */; };
E4E7A6AD22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E7A6AC22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift */; };
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC8F2204EC7F0024217A /* Delegate.swift */; };
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC912204F4B80024217A /* QueueViewController.swift */; };
@ -151,15 +153,15 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
E4B11BA52274E43B0075461B /* Embed Frameworks */ = {
E4B079C923E5E0AD0044B6D3 /* Embed Libraries */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
E4B11BA62274E44A0075461B /* libmpdclient.2.dylib in Embed Frameworks */,
E4B079C823E5E0AD0044B6D3 /* libmpdclient.2.dylib in Embed Libraries */,
);
name = "Embed Frameworks";
name = "Embed Libraries";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
@ -294,11 +296,15 @@
E4B11BC12275EE410075461B /* AlbumListActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumListActions.swift; sourceTree = "<group>"; };
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>"; };
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>"; };
E4D3BFA522B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QueueViewController+NSOutlineViewDelegate.swift"; sourceTree = "<group>"; };
E4DA820523D6236200C1EE58 /* NSSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSSize.swift; sourceTree = "<group>"; };
E4DCCFAB23E4DB5D009A8113 /* Persephone-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Persephone-Bridging-Header.h"; sourceTree = "<group>"; };
E4DCCFAC23E4DB5D009A8113 /* MPDClientWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPDClientWrapper.h; sourceTree = "<group>"; };
E4DCCFAD23E4DB5D009A8113 /* MPDClientWrapper.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = MPDClientWrapper.c; sourceTree = "<group>"; };
E4E7A6AC22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AlbumDetailView+NSTableViewDelegate.swift"; sourceTree = "<group>"; };
E4E8CC8F2204EC7F0024217A /* Delegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delegate.swift; sourceTree = "<group>"; };
E4E8CC912204F4B80024217A /* QueueViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueViewController.swift; sourceTree = "<group>"; };
@ -322,12 +328,12 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E4B11BA72274E4500075461B /* libmpdclient.2.dylib in Frameworks */,
E49A5485233E5ADC00EED353 /* Differ in Frameworks */,
E4677C48233E60E70041474F /* MediaKeyTap in Frameworks */,
E43BECA0238835DC00CAF1EB /* Kingfisher in Frameworks */,
E49A548E233E5B6000EED353 /* SwiftyJSON in Frameworks */,
E4E96D13233E630800AFD36F /* PMKFoundation in Frameworks */,
E4B079C723E5E0AD0044B6D3 /* libmpdclient.2.dylib in Frameworks */,
E49A5482233E580800EED353 /* PromiseKit in Frameworks */,
E49A548B233E5B2D00EED353 /* CryptoSwift in Frameworks */,
E49A5488233E5B0000EED353 /* ReSwift in Frameworks */,
@ -446,6 +452,9 @@
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */,
E41E5306223C019100173814 /* MPDClient+Status.swift */,
E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */,
E4DCCFAC23E4DB5D009A8113 /* MPDClientWrapper.h */,
E4DCCFAD23E4DB5D009A8113 /* MPDClientWrapper.c */,
E4DCCFAB23E4DB5D009A8113 /* Persephone-Bridging-Header.h */,
);
path = Extensions;
sourceTree = "<group>";
@ -574,6 +583,7 @@
E442CCC92347D6FD00004E0C /* Shared */ = {
isa = PBXGroup;
children = (
E4BB7F8D23E5E7A300906E2F /* ImageDataProviders */,
E4E13C2C2350D8CB00092A6E /* Layouts */,
E408D3B7220DE8CC0006D9BE /* Extensions */,
E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */,
@ -675,6 +685,14 @@
path = Actions;
sourceTree = "<group>";
};
E4BB7F8D23E5E7A300906E2F /* ImageDataProviders */ = {
isa = PBXGroup;
children = (
E4BB7F8E23E5E7BC00906E2F /* MPDAlbumArtImageDataProvider.swift */,
);
path = ImageDataProviders;
sourceTree = "<group>";
};
E4D1B594220BA2490026F233 /* Models */ = {
isa = PBXGroup;
children = (
@ -735,7 +753,7 @@
E40786162110CE6E006887B1 /* Resources */,
E42A98F122430936004D8180 /* ShellScript */,
E421AC9B221F7319008B2449 /* CopyFiles */,
E4B11BA52274E43B0075461B /* Embed Frameworks */,
E4B079C923E5E0AD0044B6D3 /* Embed Libraries */,
);
buildRules = (
);
@ -804,7 +822,7 @@
TargetAttributes = {
E40786172110CE6E006887B1 = {
CreatedOnToolsVersion = 9.4.1;
LastSwiftMigration = 1020;
LastSwiftMigration = 1130;
SystemCapabilities = {
com.apple.HardenedRuntime = {
enabled = 1;
@ -933,8 +951,10 @@
E4B11B63226A4C510075461B /* AppReducer.swift in Sources */,
E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */,
E442CCCD2347E73C00004E0C /* Artist.swift in Sources */,
E4DCCFAE23E4DB5D009A8113 /* MPDClientWrapper.c in Sources */,
E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */,
E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */,
E4BB7F8F23E5E7BC00906E2F /* MPDAlbumArtImageDataProvider.swift in Sources */,
E4235640228623D2001216D6 /* QueueSongInfoView.swift in Sources */,
E451E36E22BD2501008BE9B2 /* DraggedSong.swift in Sources */,
E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */,
@ -1173,6 +1193,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
@ -1192,6 +1213,8 @@
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.Persephone;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Persephone/MPDClient/Extensions/Persephone-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
SYSTEM_HEADER_SEARCH_PATHS = Persephone/include;
USER_HEADER_SEARCH_PATHS = libmpdclient/output;
@ -1202,6 +1225,7 @@
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
@ -1221,6 +1245,7 @@
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.Persephone;
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
SWIFT_OBJC_BRIDGING_HEADER = "Persephone/MPDClient/Extensions/Persephone-Bridging-Header.h";
SWIFT_VERSION = 5.0;
SYSTEM_HEADER_SEARCH_PATHS = Persephone/include;
USER_HEADER_SEARCH_PATHS = libmpdclient/output;

View File

@ -27,6 +27,15 @@
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E40786172110CE6E006887B1"
BuildableName = "Persephone.app"
BlueprintName = "Persephone"
ReferencedContainer = "container:Persephone.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO">
@ -49,17 +58,6 @@
</BuildableReference>
</TestableReference>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "E40786172110CE6E006887B1"
BuildableName = "Persephone.app"
BlueprintName = "Persephone"
ReferencedContainer = "container:Persephone.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
@ -81,8 +79,6 @@
ReferencedContainer = "container:Persephone.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"

View File

@ -56,10 +56,14 @@ class AlbumViewItem: NSCollectionViewItem {
}
func setAlbumCover(_ album: Album) {
guard let imagePath = album.coverArtFilePath else { return }
guard let song = album.mpdAlbum.firstSong
else { return }
let provider = MPDAlbumArtImageDataProvider(
song: song,
cacheKey: album.hash
)
let imageURL = URL(fileURLWithPath: imagePath)
let provider = LocalFileImageDataProvider(fileURL: imageURL)
albumCoverView.kf.setImage(
with: .provider(provider),
placeholder: NSImage.defaultCoverArt,

View File

@ -133,10 +133,11 @@ class AlbumDetailView: NSViewController {
}
func getBigCoverArt(song: Song, album: Album) {
guard let imagePath = album.coverArtFilePath else { return }
let provider = MPDAlbumArtImageDataProvider(
song: song.mpdSong,
cacheKey: album.hash
)
let imageURL = URL(fileURLWithPath: imagePath)
let provider = LocalFileImageDataProvider(fileURL: imageURL)
albumCoverView.kf.setImage(
with: .provider(provider),
placeholder: NSImage.defaultCoverArt,
@ -146,10 +147,15 @@ class AlbumDetailView: NSViewController {
]
)
cacheSmallCover(provider: provider)
cacheSmallCover(song: song, album: album)
}
func cacheSmallCover(provider: ImageDataProvider) {
func cacheSmallCover(song: Song, album: Album) {
let provider = MPDAlbumArtImageDataProvider(
song: song.mpdSong,
cacheKey: album.hash
)
_ = KingfisherManager.shared.retrieveImage(
with: .provider(provider),
options: [

View File

@ -49,8 +49,7 @@ class AlbumTracksDataSource: NSObject, NSTableViewDataSource {
draggedSong: DraggedSong(
type: .albumSongItem(song.mpdSong.uriString),
title: song.title,
artist: song.artist,
cover: song.album.coverArtFilePath
artist: song.artist
),
ofType: .songPasteboardType
)
@ -80,8 +79,7 @@ class AlbumTracksDataSource: NSObject, NSTableViewDataSource {
let component = NSDraggingImageComponent(key: NSDraggingItem.ImageComponentKey.icon)
let draggedSongView = DraggedSongView(
title: title,
artist: artist,
cover: cover
artist: artist
)
component.contents = draggedSongView.view.image()

View File

@ -19,11 +19,12 @@ class CurrentCoverArtView: NSImageView {
}
}
func setAlbumImage(_ album: Album) {
guard let imagePath = album.coverArtFilePath else { return }
func setSongImage(_ song: Song) {
let provider = MPDAlbumArtImageDataProvider(
song: song.mpdSong,
cacheKey: song.album.hash
)
let imageURL = URL(fileURLWithPath: imagePath)
let provider = LocalFileImageDataProvider(fileURL: imageURL)
kf.setImage(
with: .provider(provider),
placeholder: NSImage.defaultCoverArt,
@ -44,6 +45,6 @@ extension CurrentCoverArtView: StoreSubscriber {
return
}
setAlbumImage(song.album)
setSongImage(song)
}
}

View File

@ -43,8 +43,7 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
draggedSong: DraggedSong(
type: .queueItem(queueItem.queuePos),
title: queueItem.song.title,
artist: queueItem.song.artist,
cover: queueItem.song.album.coverArtFilePath
artist: queueItem.song.artist
),
ofType: .songPasteboardType
)
@ -139,8 +138,7 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
let component = NSDraggingImageComponent(key: NSDraggingItem.ImageComponentKey.icon)
let draggedSongView = DraggedSongView(
title: title,
artist: artist,
cover: cover
artist: artist
)
let view = draggedSongView.view

View File

@ -60,14 +60,15 @@ class QueueSongCoverView: NSTableCellView {
}
func setSong(_ queueItem: QueueItem, queueIcon: NSImage?) {
guard let imagePath = queueItem.song.album.coverArtFilePath
else { return }
let song = queueItem.song
isPlaying = queueItem.isPlaying
let imageURL = URL(fileURLWithPath: imagePath)
let provider = LocalFileImageDataProvider(fileURL: imageURL)
let provider = MPDAlbumArtImageDataProvider(
song: song.mpdSong,
cacheKey: song.album.hash
)
queueSongCover.kf.setImage(
with: .provider(provider),
placeholder: NSImage.defaultCoverArt,

View File

@ -16,12 +16,11 @@ class DraggedSongView: NSViewController {
private let songTitle: String
private let songArtist: String
private let songCover: String?
private var songCoverImage: NSImage?
init(title: String, artist: String, cover: String? = nil) {
init(title: String, artist: String) {
songTitle = title
songArtist = artist
songCover = cover
super.init(nibName: nil, bundle: nil)
}
@ -58,18 +57,25 @@ class DraggedSongView: NSViewController {
}
func setCoverArt() {
guard let imagePath = songCover else { return }
let imageURL = URL(fileURLWithPath: imagePath)
let provider = LocalFileImageDataProvider(fileURL: imageURL)
let mpdAlbum = MPDClient.MPDAlbum(
title: titleLabel.stringValue,
artist: artistLabel.stringValue
)
coverImage.kf.setImage(
with: .provider(provider),
placeholder: NSImage.defaultCoverArt,
KingfisherManager.shared.cache.retrieveImage(
forKey: Album(mpdAlbum: mpdAlbum).hash,
options: [
.processor(DownsamplingImageProcessor(size: .queueSongCoverSize)),
.scaleFactor(2),
]
)
],
callbackQueue: .untouch
) { result in
switch result {
case .success(let cacheResult):
self.coverImage.image = cacheResult.image
case .failure(_):
return
}
}
}
}

View File

@ -0,0 +1,34 @@
//
// MPDAlbumArtImageDataProvider.swift
// Persephone
//
// Created by Daniel Barber on 2/1/20.
// Copyright © 2020 Dan Barber. All rights reserved.
//
import Foundation
import Kingfisher
public struct MPDAlbumArtImageDataProvider: ImageDataProvider {
let song: MPDClient.MPDSong
init(song: MPDClient.MPDSong, cacheKey: String? = nil) {
self.song = song
self.cacheKey = cacheKey ?? song.uriString
}
public var cacheKey: String
public func data(handler: @escaping (Result<Data, Error>) -> Void) {
App.mpdClient.fetchAlbumArt(song: song) { imageData in
guard let imageData = imageData
else { return }
handler(.success(imageData))
}
}
public var contentURL: String? {
return song.uriString
}
}

View File

@ -18,14 +18,16 @@ class UserNotificationsController {
}
func notifyTrack(_ state: Song?) {
guard let currentSong = state,
let coverArtFilePath = currentSong.album.coverArtFilePath,
guard let song = state,
let status = App.mpdClient.status,
status.state == .playing
else { return }
let provider = MPDAlbumArtImageDataProvider(
song: song.mpdSong,
cacheKey: song.album.hash
)
let imageURL = URL(fileURLWithPath: coverArtFilePath)
let provider = LocalFileImageDataProvider(fileURL: imageURL)
_ = KingfisherManager.shared.retrieveImage(
with: .provider(provider),
options: [
@ -35,7 +37,7 @@ class UserNotificationsController {
) { result in
switch result {
case .success(let value):
SongNotifierService(song: currentSong, image: value.image)
SongNotifierService(song: song, image: value.image)
.deliver()
case .failure:
break

View File

@ -647,18 +647,18 @@
<objects>
<viewController id="KIP-rq-4dM" customClass="QueueViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
<splitView key="view" misplaced="YES" dividerStyle="thin" id="84I-w3-Mxl">
<rect key="frame" x="0.0" y="0.0" width="329" height="543"/>
<rect key="frame" x="0.0" y="0.0" width="329" height="498"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<subviews>
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="42" horizontalPageScroll="10" verticalLineScroll="42" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="S3o-nF-NN7">
<rect key="frame" x="0.0" y="0.0" width="329" height="217"/>
<rect key="frame" x="0.0" y="0.0" width="329" height="198"/>
<autoresizingMask key="autoresizingMask" flexibleMaxY="YES"/>
<clipView key="contentView" drawsBackground="NO" id="WI8-Pw-03L">
<rect key="frame" x="0.0" y="0.0" width="329" height="217"/>
<rect key="frame" x="0.0" y="0.0" width="329" height="198"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="sourceList" multipleSelection="NO" autosaveColumns="NO" rowHeight="42" rowSizeStyle="automatic" viewBased="YES" indentationMarkerFollowsCell="NO" outlineTableColumn="0Co-uF-CCB" id="jEJ-jg-fll">
<rect key="frame" x="0.0" y="0.0" width="329" height="217"/>
<rect key="frame" x="0.0" y="0.0" width="329" height="198"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="0.0"/>
<color key="backgroundColor" name="_sourceListBackgroundColor" catalog="System" colorSpace="catalog"/>
@ -824,11 +824,11 @@
</scroller>
</scrollView>
<customView misplaced="YES" id="iUb-eV-Qws">
<rect key="frame" x="0.0" y="218" width="329" height="325"/>
<rect key="frame" x="0.0" y="199" width="329" height="299"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="Dw3-M5-tWY" customClass="CurrentCoverArtView" customModule="Persephone" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="329" height="329"/>
<rect key="frame" x="0.0" y="46" width="329" height="329"/>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="defaultCoverArt" id="IoN-3N-TCb"/>
</imageView>
</subviews>

View File

@ -74,8 +74,8 @@ extension MPDClient {
let mpdAlbum = MPDAlbum(
title: mpdSong.album.title,
artist: mpdSong.artist,
date: mpdSong.date,
path: mpdSong.path
firstSong: mpdSong,
date: mpdSong.date
)
if (mpdAlbum != albums.last) {
albums.append(mpdAlbum)

View File

@ -119,6 +119,14 @@ extension MPDClient {
else { return }
albumSongs(for: album, callback: callback)
// Song commands
case .fetchAlbumArt:
guard let song = userData["song"] as? MPDSong,
let callback = userData["callback"] as? (Data?) -> Void
else { return }
sendFetchAlbumArt(for: song, callback: callback)
}
}

View File

@ -10,6 +10,13 @@ import Foundation
import mpdclient
extension MPDClient {
func fetchAlbumArt(song: MPDSong, callback: @escaping (Data?) -> Void) {
enqueueCommand(
command: .fetchAlbumArt,
userData: ["song": song, "callback": callback]
)
}
func searchSongs(_ terms: [MPDClient.MPDTag: String]) -> [MPDSong] {
var songs: [MPDSong] = []
@ -25,4 +32,42 @@ extension MPDClient {
return songs
}
func sendFetchAlbumArt(for song: MPDSong, callback: @escaping (Data?) -> Void) -> Void {
var imageData: Data?
var currentOffset: Int32 = 0
var size: Int?
while size == nil || currentOffset < size! {
mpd_send_albumart(self.connection, song.uri, String(currentOffset))
guard let sizePair = mpd_recv_pair(self.connection) else {
mpd_connection_clear_error(self.connection)
return
}
size = Int(MPDPair(sizePair).value)
mpd_return_pair(self.connection, sizePair)
if imageData == nil {
imageData = Data(count: size!)
}
let binaryPair = MPDPair(mpd_recv_pair(self.connection))
let chunkSize = Int(binaryPair.value)!
mpd_return_pair(self.connection, binaryPair.pair)
imageData![currentOffset...].withUnsafeMutableBytes { (pointer) in
mpd_recv_binary(self.connection, pointer, chunkSize)
}
guard mpd_response_finish(self.connection) else { return }
currentOffset += Int32(chunkSize)
}
DispatchQueue.main.async {
callback(imageData)
}
}
}

View File

@ -0,0 +1,15 @@
//
// MPDClientWrapper.c
// Persephone
//
// Created by Daniel Barber on 1/31/20.
// Copyright © 2020 Dan Barber. All rights reserved.
//
#include "MPDClientWrapper.h"
int
mpd_send_albumart(struct mpd_connection *connection, const char * uri, const char * offset)
{
return mpd_send_command(connection, "albumart", uri, offset, NULL);
}

View File

@ -0,0 +1,12 @@
//
// MPDClientWrapper.h
// Persephone
//
// Created by Daniel Barber on 1/31/20.
// Copyright © 2020 Dan Barber. All rights reserved.
//
#include <mpd/client.h>
int
mpd_send_albumart(struct mpd_connection *connection, const char * uri, const char * offset);

View File

@ -0,0 +1,5 @@
//
// Use this file to import your target's public headers that you would like to expose to Swift.
//
#import "MPDClientWrapper.h"

View File

@ -12,8 +12,8 @@ extension MPDClient {
struct MPDAlbum: Equatable {
let title: String
let artist: String
var firstSong: MPDSong?
var date: String?
var path: String?
static func == (lhs: MPDAlbum, rhs: MPDAlbum) -> Bool {
return lhs.title == rhs.title &&

View File

@ -48,5 +48,8 @@ extension MPDClient {
case playAlbum
case getAlbumFirstSong
case getAlbumSongs
// Song commands
case fetchAlbumArt
}
}

View File

@ -37,8 +37,7 @@ extension MPDClient {
return MPDAlbum(
title: getTag(.album),
artist: artist,
date: date,
path: path
date: date
)
}

View File

@ -32,29 +32,6 @@ struct Album {
var hash: String {
return "\(title) - \(artist)".sha1()
}
var coverArtFilenames: [String] {
return [
"cover.jpg",
"folder.jpg",
"\(artist) - \(title).jpg",
"cover.png",
"folder.png",
"\(artist) - \(title ).png",
]
}
var coverArtFilePath: String? {
let musicDir = App.store.state.preferencesState.expandedMpdLibraryDir
guard let albumPath = mpdAlbum.path else { return nil }
return coverArtFilenames
.lazy
.map { "\(musicDir)/\(albumPath)/\($0)" }
.first {
FileManager.default.fileExists(atPath: $0)
}
}
}
extension Album: Equatable {