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

Can drag an album onto the queue

This commit is contained in:
Daniel Barber 2019-07-19 12:02:37 -04:00
parent 23a1a2e8cf
commit 53b759b723
Signed by: danbarber
GPG Key ID: 931D8112E0103DD8
11 changed files with 145 additions and 60 deletions

View File

@ -35,6 +35,8 @@
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */; };
E4235640228623D2001216D6 /* QueueSongTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E423563F228623D2001216D6 /* QueueSongTitleView.swift */; };
E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42410B52241B956005ED6DF /* MPDClient+Database.swift */; };
E42A4D4F22E20D7D001C6CAD /* MPDTag.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42A4D4E22E20D7D001C6CAD /* MPDTag.swift */; };
E42A4D5122E2167E001C6CAD /* MPDClient+Songs.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42A4D5022E2167E001C6CAD /* MPDClient+Songs.swift */; };
E42A8F3B22176D6400A13ED9 /* LICENSE.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3922176D6400A13ED9 /* LICENSE.md */; };
E42A8F3C22176D6400A13ED9 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3A22176D6400A13ED9 /* README.md */; };
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E1221CD4E200184CFC /* NSFont.swift */; };
@ -233,6 +235,8 @@
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPrefsViewController.swift; sourceTree = "<group>"; };
E423563F228623D2001216D6 /* QueueSongTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueSongTitleView.swift; sourceTree = "<group>"; };
E42410B52241B956005ED6DF /* MPDClient+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Database.swift"; sourceTree = "<group>"; };
E42A4D4E22E20D7D001C6CAD /* MPDTag.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDTag.swift; sourceTree = "<group>"; };
E42A4D5022E2167E001C6CAD /* MPDClient+Songs.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Songs.swift"; sourceTree = "<group>"; };
E42A8F3922176D6400A13ED9 /* LICENSE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = "<group>"; };
E42A8F3A22176D6400A13ED9 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
E435E3E1221CD4E200184CFC /* NSFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSFont.swift; sourceTree = "<group>"; };
@ -438,6 +442,7 @@
E41E5304223BFB0700173814 /* MPDClient+Error.swift */,
E41E5302223BF9C300173814 /* MPDClient+Idle.swift */,
E41E5300223BF99300173814 /* MPDClient+Queue.swift */,
E42A4D5022E2167E001C6CAD /* MPDClient+Songs.swift */,
E41E5306223C019100173814 /* MPDClient+Status.swift */,
E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */,
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */,
@ -635,6 +640,7 @@
E4EB2378220F10B8008C70C0 /* MPDPair.swift */,
E4E8CC9922075D370024217A /* MPDSong.swift */,
E4A642D922090CBE00067D21 /* MPDStatus.swift */,
E42A4D4E22E20D7D001C6CAD /* MPDTag.swift */,
);
path = Models;
sourceTree = "<group>";
@ -897,6 +903,7 @@
E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */,
E4B11BA92274EDE30075461B /* Loading.swift in Sources */,
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */,
E42A4D4F22E20D7D001C6CAD /* MPDTag.swift in Sources */,
E43AC1F522C6A4F4001E483C /* DraggedAlbum.swift in Sources */,
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */,
E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */,
@ -984,6 +991,7 @@
E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */,
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */,
E489E39D22B9CF0000CA8CBD /* NSView.swift in Sources */,
E42A4D5122E2167E001C6CAD /* MPDClient+Songs.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@ -11,7 +11,7 @@ import AppKit
extension AlbumViewController: NSCollectionViewDelegate {
func registerForDragAndDrop(_ collectionView: NSCollectionView) {
collectionView.registerForDraggedTypes([.albumPasteboardType])
collectionView.setDraggingSourceOperationMask(.every, forLocal: true)
collectionView.setDraggingSourceOperationMask(.copy, forLocal: true)
}
func collectionView(_ collectionView: NSCollectionView, pasteboardWriterForItemAt index: Int) -> NSPasteboardWriting? {

View File

@ -24,7 +24,7 @@ class QueueViewController: NSViewController {
queueView.dataSource = dataSource
queueView.columnAutoresizingStyle = .sequentialColumnAutoresizingStyle
queueView.registerForDraggedTypes([.songPasteboardType])
queueView.registerForDraggedTypes([.songPasteboardType, .albumPasteboardType])
queueView.draggingDestinationFeedbackStyle = .regular
}

View File

@ -57,47 +57,68 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
var newQueuePos = index - 1
guard newQueuePos >= 0,
let draggingTypes = info.draggingPasteboard.types,
draggingTypes.contains(.songPasteboardType),
let pasteboardItem = info.draggingPasteboard.pasteboardItems?.first,
let draggedSong = pasteboardItem.draggedSong(forType: .songPasteboardType)
let draggingTypes = info.draggingPasteboard.types
else { return [] }
switch draggedSong.type {
case let .queueItem(queuePos):
if newQueuePos > queuePos { newQueuePos -= 1 }
guard queuePos != newQueuePos
if draggingTypes.contains(.songPasteboardType) {
guard let pasteboardItem = info.draggingPasteboard.pasteboardItems?.first,
let draggedSong = pasteboardItem.draggedSong(forType: .songPasteboardType)
else { return [] }
return .move
case .albumSongItem:
switch draggedSong.type {
case let .queueItem(queuePos):
if newQueuePos > queuePos { newQueuePos -= 1 }
guard queuePos != newQueuePos
else { return [] }
return .move
case .albumSongItem:
return .copy
}
} else if draggingTypes.contains(.albumPasteboardType) {
return .copy
}
return []
}
func outlineView(_ outlineView: NSOutlineView, acceptDrop info: NSDraggingInfo, item: Any?, childIndex index: Int) -> Bool {
var newQueuePos = index - 1
guard let draggingTypes = info.draggingPasteboard.types,
draggingTypes.contains(.songPasteboardType),
let data = info.draggingPasteboard.data(forType: .songPasteboardType),
let draggedSong = try? PropertyListDecoder().decode(DraggedSong.self, from: data)
guard let draggingTypes = info.draggingPasteboard.types
else { return false }
switch draggedSong.type {
case let .queueItem(queuePos):
if newQueuePos > queuePos { newQueuePos -= 1 }
guard queuePos != newQueuePos
if draggingTypes.contains(.songPasteboardType) {
guard let data = info.draggingPasteboard.data(forType: .songPasteboardType),
let draggedSong = try? PropertyListDecoder().decode(DraggedSong.self, from: data)
else { return false }
App.mpdClient.moveSongInQueue(at: queuePos, to: newQueuePos)
return true
case let .albumSongItem(uri):
App.mpdClient.addSongToQueue(songUri: uri, at: newQueuePos)
switch draggedSong.type {
case let .queueItem(queuePos):
if newQueuePos > queuePos { newQueuePos -= 1 }
guard queuePos != newQueuePos
else { return false }
App.mpdClient.moveSongInQueue(at: queuePos, to: newQueuePos)
return true
case let .albumSongItem(uri):
App.mpdClient.addSongToQueue(songUri: uri, at: newQueuePos)
return true
}
} else if draggingTypes.contains(.albumPasteboardType) {
guard let data = info.draggingPasteboard.data(forType: .albumPasteboardType),
let draggedAlbum = try? PropertyListDecoder().decode(DraggedAlbum.self, from: data)
else { return false }
let mpdAlbum = MPDClient.MPDAlbum(title: draggedAlbum.title, artist: draggedAlbum.artist)
App.mpdClient.addAlbumToQueue(album: mpdAlbum, at: newQueuePos)
return true
}
return false
}
func outlineView(_ outlineView: NSOutlineView, draggingSession session: NSDraggingSession, willBeginAt screenPoint: NSPoint, forItems draggedItems: [Any]) {

View File

@ -101,16 +101,7 @@ extension MPDClient {
func albumSongs(for album: MPDAlbum, callback: ([MPDSong]) -> Void) {
guard isConnected else { return }
var songs: [MPDSong] = []
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_ARTIST, album.artist)
mpd_search_commit(self.connection)
while let song = mpd_recv_song(self.connection) {
songs.append(MPDSong(song))
}
let songs = searchSongs([MPDTag.album: album.title, MPDTag.artist: album.artist])
callback(songs)
}

View File

@ -81,6 +81,12 @@ extension MPDClient {
else { return }
sendAddSongToQueue(uri: songUri, at: queuePos)
case .addAlbumToQueue:
guard let album = userData["album"] as? MPDAlbum,
let queuePos = userData["queuePos"] as? Int
else { return }
sendAddAlbumToQueue(album: album, at: queuePos)
// Album commands
case .fetchAllAlbums:
allAlbums()

View File

@ -38,6 +38,10 @@ extension MPDClient {
enqueueCommand(command: .addSongToQueue, userData: ["uri": songUri, "queuePos": queuePos])
}
func addAlbumToQueue(album: MPDAlbum, at queuePos: Int) {
enqueueCommand(command: .addAlbumToQueue, userData: ["album": album, "queuePos": queuePos])
}
func sendPlayTrack(at queuePos: Int) {
mpd_run_play_pos(self.connection, UInt32(queuePos))
}
@ -80,4 +84,16 @@ extension MPDClient {
func sendAddSongToQueue(uri: String, at queuePos: Int) {
mpd_run_add_id_to(self.connection, uri, UInt32(queuePos))
}
func sendAddAlbumToQueue(album: MPDAlbum, at queuePos: Int) {
let songs = searchSongs([MPDTag.album: album.title, MPDTag.artist: album.artist])
var insertPos = UInt32(queuePos)
for song in songs {
mpd_run_add_id_to(self.connection, song.uri, insertPos)
insertPos += 1
}
}
}

View File

@ -0,0 +1,28 @@
//
// MPDClient+Songs.swift
// Persephone
//
// Created by Daniel Barber on 2019/7/19.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
import mpdclient
extension MPDClient {
func searchSongs(_ terms: [MPDClient.MPDTag: String]) -> [MPDSong] {
var songs: [MPDSong] = []
mpd_search_db_songs(self.connection, true)
for (tag, term) in terms {
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, tag.mpdTag(), term)
}
mpd_search_commit(self.connection)
while let song = mpd_recv_song(self.connection) {
songs.append(MPDSong(song))
}
return songs
}
}

View File

@ -35,6 +35,7 @@ extension MPDClient {
case removeSong
case moveSongInQueue
case addSongToQueue
case addAlbumToQueue
// Album commands
case fetchAllAlbums

View File

@ -13,26 +13,6 @@ extension MPDClient {
class MPDSong {
let song: OpaquePointer
enum TagType: Int {
case unknown = -1
case artist, album, albumArtist, title, track, name,
genre, date, composer, performer, comment, disc
case musicBrainzArtistId
case musicBrainzAlbumId
case musicBrainzAlbumArtistId
case musicBrainzTrackId
case musicBrainzReleaseTrackId
case originalDate
case artistSort
case albumArtistSort
case albumSort
case tagCount
}
init(_ song: OpaquePointer) {
self.song = song
}
@ -68,10 +48,8 @@ extension MPDClient {
}
}
func getTag(_ tagType: TagType) -> String {
let mpdTagType = mpd_tag_type(rawValue: Int32(tagType.rawValue))
guard let tag = mpd_song_get_tag(song, mpdTagType, 0)
func getTag(_ tagType: MPDTag) -> String {
guard let tag = mpd_song_get_tag(song, tagType.mpdTag(), 0)
else { return "" }
return String(cString: tag)

View File

@ -0,0 +1,36 @@
//
// MPDTag.swift
// Persephone
//
// Created by Daniel Barber on 2019/7/19.
// Copyright © 2019 Dan Barber. All rights reserved.
//
import Foundation
import mpdclient
extension MPDClient {
enum MPDTag: Int {
case unknown = -1
case artist, album, albumArtist, title, track, name,
genre, date, composer, performer, comment, disc
case musicBrainzArtistId
case musicBrainzAlbumId
case musicBrainzAlbumArtistId
case musicBrainzTrackId
case musicBrainzReleaseTrackId
case originalDate
case artistSort
case albumArtistSort
case albumSort
case tagCount
func mpdTag() -> mpd_tag_type {
return mpd_tag_type(rawValue: Int32(self.rawValue))
}
}
}