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

Compare commits

..

No commits in common. "0e71714260de4f2f8682cbac3e3d94f9199f9d84" and "17ed1cdecc36299d34def5234bfa11ea35313514" have entirely different histories.

11 changed files with 48 additions and 135 deletions

View File

@ -11,8 +11,6 @@ import Cocoa
class AlbumViewController: NSViewController, class AlbumViewController: NSViewController,
NSCollectionViewDelegate, NSCollectionViewDelegate,
NSCollectionViewDelegateFlowLayout { NSCollectionViewDelegateFlowLayout {
var preferences = Preferences()
let paddingWidth: CGFloat = 40 let paddingWidth: CGFloat = 40
let gutterWidth: CGFloat = 20 let gutterWidth: CGFloat = 20
@ -38,8 +36,6 @@ class AlbumViewController: NSViewController,
) )
albumCollectionView.dataSource = dataSource albumCollectionView.dataSource = dataSource
preferences.addObserver(self, forKeyPath: "mpdLibraryDir")
} }
override func viewWillLayout() { override func viewWillLayout() {
@ -57,20 +53,6 @@ class AlbumViewController: NSViewController,
layout.setScrollPosition() layout.setScrollPosition()
} }
override func observeValue(
forKeyPath keyPath: String?,
of object: Any?,
change: [NSKeyValueChangeKey : Any]?,
context: UnsafeMutableRawPointer?
) {
switch keyPath {
case "mpdLibraryDir":
albumCollectionView.reloadData()
default:
break
}
}
@objc func updateAlbums(_ notification: Notification) { @objc func updateAlbums(_ notification: Notification) {
guard let albums = notification.userInfo?[Notification.albumsKey] as? [MPDClient.Album] guard let albums = notification.userInfo?[Notification.albumsKey] as? [MPDClient.Album]
else { return } else { return }

View File

@ -36,7 +36,7 @@ class QueueViewController: NSViewController,
let newQueuePos = queueView.selectedRow - 1 let newQueuePos = queueView.selectedRow - 1
if newQueuePos >= 0 { if newQueuePos >= 0 {
AppDelegate.mpdClient.playTrack(at: newQueuePos) AppDelegate.mpdClient.playTrack(queuePos: newQueuePos)
} }
} }

View File

@ -13,24 +13,4 @@ extension NSImage {
static let pauseIcon = NSImage(named: "pauseButton") static let pauseIcon = NSImage(named: "pauseButton")
static let defaultCoverArt = NSImage(named: "blankAlbum") static let defaultCoverArt = NSImage(named: "blankAlbum")
func toFitBox(size: NSSize) -> NSImage {
let newImage = NSImage(size: size)
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

@ -18,12 +18,8 @@ extension MPDClient {
queueCommand(command: .playAlbum, userData: ["album": album]) queueCommand(command: .playAlbum, userData: ["album": album])
} }
func getAlbumURI(for album: Album, callback: @escaping (String?) -> Void) { func getAlbumURI(for album: Album) {
queueCommand( queueCommand(command: .getAlbumURI, userData: ["album": album])
command: .getAlbumURI,
priority: .low,
userData: ["album": album, "callback": callback]
)
} }
func sendPlayAlbum(_ album: Album) { func sendPlayAlbum(_ album: Album) {
@ -69,31 +65,30 @@ extension MPDClient {
self.delegate?.didLoadAlbums(mpdClient: self, albums: albums) self.delegate?.didLoadAlbums(mpdClient: self, albums: albums)
} }
func albumURI(for album: Album, callback: (String?) -> Void) { func albumURI(for album: Album) -> String? {
var songURI: String? var songURI: String?
guard isConnected else { return } guard isConnected else { return nil }
print("Getting URI")
mpd_search_db_songs(self.connection, true) mpd_search_db_songs(self.connection, true)
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, album.title) mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, album.title)
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM_ARTIST, album.artist) mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM_ARTIST, album.artist)
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_TRACK, "1") mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_TRACK, "1")
mpd_search_commit(self.connection) mpd_search_commit(self.connection)
print("Performed search")
while let mpdSong = mpd_recv_song(self.connection) { while let mpdSong = mpd_recv_song(self.connection) {
let song = Song(mpdSong) let song = Song(mpdSong)
print(song)
if songURI == nil { if songURI == nil {
songURI = song.uriString songURI = song.uriString
} }
} }
print("Got URI")
callback( return songURI
songURI?
.split(separator: "/")
.dropLast()
.joined(separator: "/")
)
} }
} }

View File

@ -24,22 +24,12 @@ extension MPDClient {
sendStop() sendStop()
case .playPause: case .playPause:
sendPlay() sendPlay()
case .seekCurrentSong:
guard let timeInSeconds = userData["timeInSeconds"] as? Float
else { return }
sendSeekCurrentSong(timeInSeconds: timeInSeconds)
// Status commands // Status commands
case .fetchStatus: case .fetchStatus:
sendRunStatus() sendRunStatus()
// Queue commands
case .fetchQueue: case .fetchQueue:
sendFetchQueue() sendFetchQueue()
case .playTrack:
guard let queuePos = userData["queuePos"] as? Int
else { return }
sendPlayTrack(at: queuePos)
// Album commands // Album commands
case .fetchAllAlbums: case .fetchAllAlbums:
@ -48,10 +38,8 @@ extension MPDClient {
guard let album = userData["album"] as? Album else { return } guard let album = userData["album"] as? Album else { return }
sendPlayAlbum(album) sendPlayAlbum(album)
case .getAlbumURI: case .getAlbumURI:
guard let album = userData["album"] as? Album, guard let album = userData["album"] as? Album else { return }
let callback = userData["callback"] as? (String?) -> Void _ = getAlbumURI(for: album)
else { return }
albumURI(for: album, callback: callback)
} }
} }
} }

View File

@ -18,13 +18,17 @@ extension MPDClient {
} }
func idle() { func idle() {
if !self.isIdle && self.commandsQueued == 0 { let idleOperation = BlockOperation {
mpd_send_idle(self.connection) if !self.isIdle && self.commandsQueued == 0 {
self.isIdle = true mpd_send_idle(self.connection)
self.isIdle = true
let result = mpd_recv_idle(self.connection, true) let result = mpd_recv_idle(self.connection, true)
self.handleIdleResult(result) self.handleIdleResult(result)
}
} }
idleOperation.queuePriority = .veryLow
commandQueue.addOperation(idleOperation)
} }
func handleIdleResult(_ result: mpd_idle) { func handleIdleResult(_ result: mpd_idle) {

View File

@ -14,12 +14,16 @@ extension MPDClient {
sendCommand(command: .fetchQueue) sendCommand(command: .fetchQueue)
} }
func playTrack(at queuePos: Int) { func playTrack(queuePos: Int) {
queueCommand(command: .playTrack, userData: ["queuePos": queuePos]) guard isConnected else { return }
}
func sendPlayTrack(at queuePos: Int) { noIdle()
mpd_run_play_pos(self.connection, UInt32(queuePos)) let commandOperation = BlockOperation { [unowned self] in
mpd_run_play_pos(self.connection, UInt32(queuePos))
}
commandOperation.queuePriority = .veryHigh
commandQueue.addOperation(commandOperation)
idle()
} }
func sendFetchQueue() { func sendFetchQueue() {

View File

@ -27,10 +27,11 @@ extension MPDClient {
} }
func seekCurrentSong(timeInSeconds: Float) { func seekCurrentSong(timeInSeconds: Float) {
queueCommand( noIdle()
command: .seekCurrentSong, commandQueue.addOperation { [unowned self] in
userData: ["timeInSeconds": timeInSeconds] mpd_run_seek_current(self.connection, timeInSeconds, false)
) }
idle()
} }
func sendNextTrack() { func sendNextTrack() {
@ -61,7 +62,4 @@ extension MPDClient {
} }
} }
func sendSeekCurrentSong(timeInSeconds: Float) {
mpd_run_seek_current(self.connection, timeInSeconds, false)
}
} }

View File

@ -22,8 +22,8 @@ class MPDClient {
var commandsQueued: UInt = 0 var commandsQueued: UInt = 0
enum Command { enum Command {
case prevTrack, nextTrack, playPause, stop, seekCurrentSong, case prevTrack, nextTrack, playPause, stop,
fetchStatus, fetchQueue, playTrack, fetchAllAlbums, fetchStatus, fetchQueue, fetchAllAlbums,
playAlbum, getAlbumURI playAlbum, getAlbumURI
} }
@ -40,15 +40,13 @@ class MPDClient {
guard isConnected else { return } guard isConnected else { return }
noIdle() noIdle()
let commandOperation = BlockOperation() { [unowned self] in let commandOperation = BlockOperation() { [unowned self] in
self.commandsQueued -= 1 self.commandsQueued -= 1
self.sendCommand(command: command, userData: userData) self.sendCommand(command: command, userData: userData)
self.idle()
} }
commandOperation.queuePriority = priority commandOperation.queuePriority = priority
commandsQueued += 1 commandsQueued += 1
commandQueue.addOperation(commandOperation) commandQueue.addOperation(commandOperation)
idle()
} }
} }

View File

@ -58,10 +58,6 @@ struct Preferences {
return mpdLibraryDir ?? mpdLibraryDirDefault return mpdLibraryDir ?? mpdLibraryDirDefault
} }
var expandedMpdLibraryDir: String {
return NSString(string: mpdLibraryDirOrDefault).expandingTildeInPath
}
func addObserver(_ observer: NSObject, forKeyPath keyPath: String) { func addObserver(_ observer: NSObject, forKeyPath keyPath: String) {
preferences.addObserver(observer, forKeyPath: keyPath, options: .new, context: nil) preferences.addObserver(observer, forKeyPath: keyPath, options: .new, context: nil)
} }

View File

@ -13,16 +13,18 @@ import PMKFoundation
class AlbumArtService: NSObject { class AlbumArtService: NSObject {
static var shared = AlbumArtService() static var shared = AlbumArtService()
var preferences = Preferences()
var session = URLSession(configuration: .default) var session = URLSession(configuration: .default)
let cacheQueue = DispatchQueue(label: "albumArtCacheQueue", attributes: .concurrent) let cacheQueue = DispatchQueue(label: "albumArtCacheQueue", attributes: .concurrent)
let filesystemQueue = DispatchQueue(label: "albumArtFilesystemQueue", attributes: .concurrent)
func fetchAlbumArt(for album: AlbumItem, callback: @escaping (_ image: NSImage) -> Void) { func fetchAlbumArt(for album: AlbumItem, callback: @escaping (_ image: NSImage) -> Void) {
cacheQueue.async { [unowned self] in cacheQueue.async { [unowned self] in
//print("Trying cache") //print("Trying cache")
if !self.getCachedArtwork(for: album, callback: callback) { if !self.getCachedArtwork(for: album, callback: callback) {
self.getArtworkFromFilesystem(for: album, callback: callback) // self.filesystemQueue.async {
// _ = self.getArtworkFromFilesystem(for: album, callback: callback)
// }
// if !self.getArtworkFromFilesystem(for: album, callback: callback) { // if !self.getArtworkFromFilesystem(for: album, callback: callback) {
// // self.getRemoteArtwork(for: album, callback: callback) // // self.getRemoteArtwork(for: album, callback: callback)
// } // }
@ -51,48 +53,14 @@ class AlbumArtService: NSObject {
} }
} }
func getArtworkFromFilesystem( func getArtworkFromFilesystem(for album: AlbumItem, callback: @escaping (_ image: NSImage) -> Void) -> Bool {
for album: AlbumItem, print("No cache trying filesystem")
callback: @escaping (_ image: NSImage) -> Void let uri = AppDelegate.mpdClient.getAlbumURI(for: album.album)
) { print(uri)
let coverArtFilenames = [ return false
"folder.jpg",
"cover.jpg",
"\(album.artist) - \(album.title).jpg"
]
AppDelegate.mpdClient.getAlbumURI(
for: album.album,
callback: { (_ albumURI: String?) in
guard let albumURI = albumURI
else { return }
let musicDir = self.preferences.expandedMpdLibraryDir
let fullAlbumURI = "\(musicDir)/\(albumURI)"
for coverArtFilename in coverArtFilenames {
let coverArtURI = "\(fullAlbumURI)/\(coverArtFilename)"
if FileManager.default.fileExists(atPath: coverArtURI),
let data = FileManager.default.contents(atPath: coverArtURI),
let image = NSImage(data: data) {
let imageThumb = image.toFitBox(
size: NSSize(width: 180, height: 180)
)
self.cacheArtwork(
for: album,
data: imageThumb.jpegData(compressionQuality: 0.5)
)
callback(imageThumb)
break
}
}
}
)
} }
func cacheArtwork(for album: AlbumItem, data: Data?) { func cacheArtwork(for album: AlbumItem, data: Data) {
guard let bundleIdentifier = Bundle.main.bundleIdentifier, guard let bundleIdentifier = Bundle.main.bundleIdentifier,
let cacheDir = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true) let cacheDir = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent(bundleIdentifier) .appendingPathComponent(bundleIdentifier)