mirror of
https://github.com/danbee/persephone
synced 2025-03-04 08:39:11 +00:00
Compare commits
16 Commits
22b3ca2aff
...
5f7768df67
| Author | SHA1 | Date | |
|---|---|---|---|
| 5f7768df67 | |||
| db463252d4 | |||
| cbac5587b3 | |||
| dafe7009f2 | |||
| ea1793df51 | |||
| 92f894be94 | |||
| 2157677c32 | |||
| 9fef5a0fa2 | |||
| ad7994ce14 | |||
| c5bb5935e9 | |||
|
|
ab3bd561f2 | ||
|
|
53a9b38a9a | ||
|
|
1fd8e23f64 | ||
|
|
c1181fa283 | ||
|
|
32a4e38bd1 | ||
| c7ea8f34d0 |
@ -28,6 +28,8 @@ class AppDelegate: NSObject,
|
||||
@IBOutlet weak var playSelectedSongMenuItem: NSMenuItem!
|
||||
@IBOutlet weak var playSelectedSongNextMenuItem: NSMenuItem!
|
||||
@IBOutlet weak var addSelectedSongToQueueMenuItem: NSMenuItem!
|
||||
|
||||
@IBOutlet weak var savePlaylistMenuItem: NSMenuItem!
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
mediaKeyTap = MediaKeyTap(delegate: self)
|
||||
@ -35,10 +37,12 @@ class AppDelegate: NSObject,
|
||||
|
||||
App.store.subscribe(self) {
|
||||
$0.select {
|
||||
($0.serverState, $0.playerState, $0.uiState)
|
||||
($0.serverState, $0.playerState, $0.uiState, $0.playlistState)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(didConnect), name: .didConnect, object: nil)
|
||||
|
||||
_ = App.userNotificationsController
|
||||
|
||||
_ = App.mpdServerController
|
||||
@ -136,6 +140,27 @@ class AppDelegate: NSObject,
|
||||
connectMenuItem.isEnabled = !connected
|
||||
disconnectMenuItem.isEnabled = connected
|
||||
}
|
||||
|
||||
func setPlaylists(state: PlaylistState) {
|
||||
guard let playlistsMenuItem = NSApplication.shared.mainMenu?.item(withTitle: "Playlists")
|
||||
else { return }
|
||||
|
||||
playlistsMenuItem.submenu?.items.forEach { item in
|
||||
if item.tag == 1 {
|
||||
playlistsMenuItem.submenu?.removeItem(item)
|
||||
}
|
||||
}
|
||||
|
||||
state.playlists.forEach { playlist in
|
||||
let playlistMenuItem = NSMenuItem(
|
||||
title: playlist.path,
|
||||
action: #selector(loadPlaylist),
|
||||
keyEquivalent: ""
|
||||
)
|
||||
playlistMenuItem.tag = 1
|
||||
playlistsMenuItem.submenu?.addItem(playlistMenuItem)
|
||||
}
|
||||
}
|
||||
|
||||
func handle(mediaKey: MediaKey, event: KeyEvent) {
|
||||
switch mediaKey {
|
||||
@ -147,6 +172,17 @@ class AppDelegate: NSObject,
|
||||
App.mpdClient.prevTrack()
|
||||
}
|
||||
}
|
||||
|
||||
@objc func didConnect() {
|
||||
App.mpdClient.fetchPlaylists()
|
||||
}
|
||||
|
||||
@objc func loadPlaylist(_ sender: NSMenuItem) {
|
||||
let name = sender.title
|
||||
|
||||
App.mpdClient.clearQueue()
|
||||
App.mpdClient.loadQueueFromPlaylist(name: name)
|
||||
}
|
||||
|
||||
@IBAction func connectMenuAction(_ sender: NSMenuItem) {
|
||||
App.mpdServerController.connect()
|
||||
@ -221,7 +257,10 @@ class AppDelegate: NSObject,
|
||||
|
||||
extension AppDelegate: StoreSubscriber {
|
||||
typealias StoreSubscriberStateType = (
|
||||
serverState: ServerState, playerState: PlayerState, uiState: UIState
|
||||
serverState: ServerState,
|
||||
playerState: PlayerState,
|
||||
uiState: UIState,
|
||||
playlistState: PlaylistState
|
||||
)
|
||||
|
||||
func newState(state: StoreSubscriberStateType) {
|
||||
@ -230,5 +269,6 @@ extension AppDelegate: StoreSubscriber {
|
||||
setSongMenuItemsState(selectedSong: state.uiState.selectedSong)
|
||||
setControlsMenuItemsState(state: state.playerState)
|
||||
setConnectMenuItemsState(connected: state.serverState.connected)
|
||||
setPlaylists(state: state.playlistState)
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||
<dependencies>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16096"/>
|
||||
<capability name="System colors introduced in macOS 10.14" minToolsVersion="10.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@ -620,7 +621,7 @@
|
||||
<menuItem title="Show Sidebar" keyEquivalent="s" id="kIP-vf-haE">
|
||||
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
|
||||
<connections>
|
||||
<action selector="toggleSourceList:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
|
||||
<action selector="toggleSidebar:" target="Ady-hI-5gd" id="iwa-gc-5KM"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem title="Enter Full Screen" keyEquivalent="f" id="4J7-dP-txa">
|
||||
@ -772,14 +773,13 @@
|
||||
<subviews>
|
||||
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="sourceList" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" indentationPerLevel="16" outlineTableColumn="0Co-uF-CCB" id="jEJ-jg-fll">
|
||||
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
<color key="backgroundColor" name="_sourceListBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn identifier="songTitleColumn" width="222" minWidth="128" maxWidth="1000" id="0Co-uF-CCB">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Title">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
@ -809,14 +809,14 @@
|
||||
<rect key="frame" x="1" y="20" width="222" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView translatesAutoresizingMaskIntoConstraints="NO" id="o8i-cz-hIP">
|
||||
<imageView ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="o8i-cz-hIP">
|
||||
<rect key="frame" x="3" y="0.0" width="17" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="17" id="UFf-Fg-9Qg"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" imageScaling="proportionallyDown" image="playButton" id="ckK-gW-Vhx"/>
|
||||
</imageView>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="i0h-bn-auJ">
|
||||
<textField verticalHuggingPriority="750" ambiguous="YES" translatesAutoresizingMaskIntoConstraints="NO" id="i0h-bn-auJ">
|
||||
<rect key="frame" x="25" y="0.0" width="197" height="17"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="ei8-1e-ErK">
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -842,7 +842,6 @@
|
||||
</tableColumn>
|
||||
<tableColumn identifier="songArtistColumn" width="222" minWidth="128" maxWidth="1000" id="SPM-QP-DX8">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Artist">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</tableHeaderCell>
|
||||
|
||||
@ -0,0 +1,24 @@
|
||||
//
|
||||
// AlbumDetailSongTitleView.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Nemo157 on 20200619...
|
||||
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
|
||||
class AlbumDetailSongTitleView: NSTableCellView {
|
||||
@IBOutlet var songTitle: NSTextField!
|
||||
@IBOutlet var songArtist: NSTextField!
|
||||
|
||||
func setShowArtist(_ show: Bool) {
|
||||
songArtist.isHidden = !show
|
||||
}
|
||||
|
||||
func setSong(_ song: Song) {
|
||||
songTitle?.stringValue = song.title
|
||||
songArtist?.stringValue = song.artist
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ extension AlbumDetailView: NSTableViewDelegate {
|
||||
let cellView = tableView.makeView(
|
||||
withIdentifier: .discNumber,
|
||||
owner: self
|
||||
) as! NSTableCellView
|
||||
) as! NSTableCellView
|
||||
|
||||
cellView.textField?.stringValue = "Disc \(disc)"
|
||||
|
||||
@ -70,7 +70,7 @@ extension AlbumDetailView: NSTableViewDelegate {
|
||||
let cellView = tableView.makeView(
|
||||
withIdentifier: .trackNumber,
|
||||
owner: self
|
||||
) as! NSTableCellView
|
||||
) as! NSTableCellView
|
||||
|
||||
cellView.textField?.stringValue = "\(song.trackNumber)."
|
||||
|
||||
@ -81,9 +81,10 @@ extension AlbumDetailView: NSTableViewDelegate {
|
||||
let cellView = tableView.makeView(
|
||||
withIdentifier: .songTitle,
|
||||
owner: self
|
||||
) as! NSTableCellView
|
||||
) as! AlbumDetailSongTitleView
|
||||
|
||||
cellView.textField?.stringValue = song.title
|
||||
cellView.setShowArtist(dataSource.showSongArtist)
|
||||
cellView.setSong(song)
|
||||
|
||||
return cellView
|
||||
}
|
||||
@ -92,7 +93,7 @@ extension AlbumDetailView: NSTableViewDelegate {
|
||||
let cellView = tableView.makeView(
|
||||
withIdentifier: .songDuration,
|
||||
owner: self
|
||||
) as! NSTableCellView
|
||||
) as! NSTableCellView
|
||||
|
||||
cellView.textField?.font = .timerFont
|
||||
cellView.textField?.stringValue = song.duration.formattedTime
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="16097.2" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16097.2"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<objects>
|
||||
@ -17,12 +17,12 @@
|
||||
</customObject>
|
||||
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||
<customView wantsLayer="YES" id="Hz6-mo-xeY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="823" height="568"/>
|
||||
<customView wantsLayer="YES" misplaced="YES" id="Hz6-mo-xeY">
|
||||
<rect key="frame" x="0.0" y="0.0" width="823" height="567"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="m2v-pR-e9v">
|
||||
<rect key="frame" x="357" y="515" width="448" height="28"/>
|
||||
<rect key="frame" x="357" y="513" width="448" height="28"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="444" id="erC-QS-9hc"/>
|
||||
</constraints>
|
||||
@ -33,7 +33,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="4Jx-I5-Nkv">
|
||||
<rect key="frame" x="357" y="488" width="448" height="19"/>
|
||||
<rect key="frame" x="357" y="486" width="448" height="19"/>
|
||||
<textFieldCell key="cell" title="Metadata" id="ztJ-4E-qvI">
|
||||
<font key="font" metaFont="system" size="16"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -41,7 +41,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="FWd-vZ-5CT">
|
||||
<rect key="frame" x="31" y="243" width="300" height="300"/>
|
||||
<rect key="frame" x="31" y="241" width="300" height="300"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="300" id="69o-NQ-qL7"/>
|
||||
<constraint firstAttribute="width" constant="300" id="8XY-bQ-C7X"/>
|
||||
@ -53,22 +53,21 @@
|
||||
</shadow>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="defaultCoverArt" id="scE-kj-gex"/>
|
||||
</imageView>
|
||||
<scrollView horizontalCompressionResistancePriority="250" borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="BOb-Lr-10M">
|
||||
<rect key="frame" x="359" y="33" width="444" height="426"/>
|
||||
<scrollView horizontalCompressionResistancePriority="250" borderType="none" autohidesScrollers="YES" horizontalLineScroll="18" horizontalPageScroll="10" verticalLineScroll="18" verticalPageScroll="10" hasHorizontalScroller="NO" horizontalScrollElasticity="none" translatesAutoresizingMaskIntoConstraints="NO" id="BOb-Lr-10M">
|
||||
<rect key="frame" x="359" y="33" width="444" height="424"/>
|
||||
<clipView key="contentView" drawsBackground="NO" copiesOnScroll="NO" id="9QN-UB-b4l">
|
||||
<rect key="frame" x="0.0" y="0.0" width="444" height="426"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="444" height="424"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" id="ehr-qh-87Q" customClass="AlbumDetailSongListView" customModule="Persephone" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="444" height="426"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="none" columnSelection="YES" multipleSelection="NO" autosaveColumns="NO" rowHeight="16" rowSizeStyle="automatic" usesAutomaticRowHeights="YES" viewBased="YES" id="ehr-qh-87Q" customClass="AlbumDetailSongListView" customModule="Persephone" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="444" height="424"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
<color key="backgroundColor" red="0.11764705882352941" green="0.11764705882352941" blue="0.11764705882352941" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||
<tableColumns>
|
||||
<tableColumn identifier="trackNumberColumn" width="40" minWidth="40" maxWidth="40" id="cwb-jE-CEP">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Track No.">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
@ -79,12 +78,11 @@
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView identifier="trackNumberCell" id="bVN-zt-KW7">
|
||||
<rect key="frame" x="1" y="1" width="40" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<tableCellView identifier="trackNumberCell" translatesAutoresizingMaskIntoConstraints="NO" id="bVN-zt-KW7">
|
||||
<rect key="frame" x="1" y="1" width="40" height="16"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="6eU-Jx-HDR">
|
||||
<rect key="frame" x="0.0" y="1" width="40" height="16"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="40" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="left" title="1." id="Z5y-oS-Qm8">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -93,20 +91,19 @@
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="6eU-Jx-HDR" firstAttribute="centerY" secondItem="bVN-zt-KW7" secondAttribute="centerY" id="5cb-M0-OIZ"/>
|
||||
<constraint firstItem="6eU-Jx-HDR" firstAttribute="leading" secondItem="bVN-zt-KW7" secondAttribute="leading" constant="2" id="KXb-Ua-LaU"/>
|
||||
<constraint firstItem="6eU-Jx-HDR" firstAttribute="centerX" secondItem="bVN-zt-KW7" secondAttribute="centerX" id="MGU-H7-mAj"/>
|
||||
<constraint firstItem="6eU-Jx-HDR" firstAttribute="top" secondItem="bVN-zt-KW7" secondAttribute="top" id="b71-yc-bdr"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="textField" destination="6eU-Jx-HDR" id="DWy-vj-9Eq"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
<tableCellView identifier="discNumberCell" id="MHh-8c-iyL">
|
||||
<rect key="frame" x="1" y="20" width="40" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<tableCellView identifier="discNumberCell" translatesAutoresizingMaskIntoConstraints="NO" id="MHh-8c-iyL">
|
||||
<rect key="frame" x="1" y="19" width="40" height="16"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="nwx-zY-r5o">
|
||||
<rect key="frame" x="0.0" y="1" width="441" height="16"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="441" height="16"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="437" id="irN-AG-Pcj"/>
|
||||
</constraints>
|
||||
@ -118,8 +115,9 @@
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="nwx-zY-r5o" firstAttribute="centerY" secondItem="MHh-8c-iyL" secondAttribute="centerY" id="IOV-5t-KTN"/>
|
||||
<constraint firstItem="nwx-zY-r5o" firstAttribute="top" secondItem="MHh-8c-iyL" secondAttribute="top" id="KbM-w9-As3"/>
|
||||
<constraint firstItem="nwx-zY-r5o" firstAttribute="leading" secondItem="MHh-8c-iyL" secondAttribute="leading" constant="2" id="nw0-ya-Pwt"/>
|
||||
<constraint firstAttribute="bottom" secondItem="nwx-zY-r5o" secondAttribute="bottom" id="z6c-sM-3fK"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="textField" destination="nwx-zY-r5o" id="NFl-7o-wmK"/>
|
||||
@ -129,7 +127,6 @@
|
||||
</tableColumn>
|
||||
<tableColumn identifier="trackTitleColumn" width="353" minWidth="40" maxWidth="1000" id="7yp-QQ-EzC">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Song Title">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
@ -140,33 +137,60 @@
|
||||
</textFieldCell>
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES" userResizable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView identifier="songTitleCell" id="41U-5i-Oot">
|
||||
<rect key="frame" x="44" y="1" width="353" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<tableCellView identifier="songTitleCell" translatesAutoresizingMaskIntoConstraints="NO" id="41U-5i-Oot" customClass="AlbumDetailSongTitleView" customModule="Persephone" customModuleProvider="target">
|
||||
<rect key="frame" x="0.0" y="0.0" width="353" height="31"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="R8t-bV-9LI">
|
||||
<rect key="frame" x="0.0" y="1" width="353" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="left" title="My Song Title" id="Sdi-jJ-EOM">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<stackView distribution="fill" orientation="vertical" alignment="leading" spacing="0.0" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="FJy-NN-WaB">
|
||||
<rect key="frame" x="0.0" y="0.0" width="353" height="31"/>
|
||||
<subviews>
|
||||
<textField autoresizesSubviews="NO" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="R8t-bV-9LI">
|
||||
<rect key="frame" x="-2" y="15" width="349" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="left" title="My Song Title" id="Sdi-jJ-EOM">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField autoresizesSubviews="NO" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="tQ2-xw-nh6">
|
||||
<rect key="frame" x="-2" y="0.0" width="349" height="15"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="left" title="Song Artist" id="1XN-kG-CWb">
|
||||
<font key="font" metaFont="cellTitle"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="R8t-bV-9LI" secondAttribute="trailing" constant="8" id="2JM-VY-eK8"/>
|
||||
<constraint firstItem="R8t-bV-9LI" firstAttribute="leading" secondItem="FJy-NN-WaB" secondAttribute="leading" id="6DN-qh-z3m"/>
|
||||
<constraint firstAttribute="trailing" secondItem="tQ2-xw-nh6" secondAttribute="trailing" constant="8" id="ad7-zz-zbp"/>
|
||||
<constraint firstItem="tQ2-xw-nh6" firstAttribute="leading" secondItem="FJy-NN-WaB" secondAttribute="leading" id="n9L-fM-JWy"/>
|
||||
</constraints>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="R8t-bV-9LI" firstAttribute="centerX" secondItem="41U-5i-Oot" secondAttribute="centerX" id="0Hz-cI-Y32"/>
|
||||
<constraint firstItem="R8t-bV-9LI" firstAttribute="centerY" secondItem="41U-5i-Oot" secondAttribute="centerY" id="7iv-Qw-o7d"/>
|
||||
<constraint firstItem="R8t-bV-9LI" firstAttribute="leading" secondItem="41U-5i-Oot" secondAttribute="leading" constant="2" id="UzY-kH-q4q"/>
|
||||
<constraint firstAttribute="trailing" secondItem="FJy-NN-WaB" secondAttribute="trailing" id="Cs2-fX-Bfs"/>
|
||||
<constraint firstItem="FJy-NN-WaB" firstAttribute="top" secondItem="41U-5i-Oot" secondAttribute="top" id="N8E-Gq-gHp"/>
|
||||
<constraint firstItem="FJy-NN-WaB" firstAttribute="leading" secondItem="41U-5i-Oot" secondAttribute="leading" id="XZh-lV-rXN"/>
|
||||
<constraint firstAttribute="bottom" secondItem="FJy-NN-WaB" secondAttribute="bottom" id="aLa-by-ri5"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="textField" destination="R8t-bV-9LI" id="b79-el-ZAY"/>
|
||||
<outlet property="songArtist" destination="tQ2-xw-nh6" id="5pJ-77-key"/>
|
||||
<outlet property="songTitle" destination="R8t-bV-9LI" id="9rC-ff-9B9"/>
|
||||
</connections>
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="trackDurationColumn" width="42" minWidth="42" maxWidth="42" id="ha5-ff-2az">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Duration">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||
</tableHeaderCell>
|
||||
@ -220,7 +244,7 @@
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<button verticalHuggingPriority="750" imageHugsTitle="YES" translatesAutoresizingMaskIntoConstraints="NO" id="jMU-bv-TNF">
|
||||
<rect key="frame" x="31" y="184" width="119" height="35"/>
|
||||
<rect key="frame" x="31" y="182" width="119" height="35"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="33" id="2uQ-mC-4QY"/>
|
||||
<constraint firstAttribute="width" constant="119" id="h2n-ZB-Ufr"/>
|
||||
|
||||
@ -25,15 +25,24 @@ class AlbumTracksDataSource: NSObject, NSTableViewDataSource {
|
||||
}
|
||||
|
||||
var albumSongs: [AlbumSongItem] = []
|
||||
var showSongArtist: Bool = false
|
||||
|
||||
func setAlbumSongs(_ songs: [Song]) {
|
||||
var disc: String? = ""
|
||||
var albumArtist: String?
|
||||
|
||||
showSongArtist = false
|
||||
songs.forEach { song in
|
||||
if song.disc != disc && song.disc != "0" {
|
||||
disc = song.disc
|
||||
albumSongs.append(AlbumSongItem(disc: song.disc))
|
||||
}
|
||||
if albumArtist == nil {
|
||||
albumArtist = song.albumArtist
|
||||
}
|
||||
if song.artist != albumArtist {
|
||||
showSongArtist = true
|
||||
}
|
||||
|
||||
albumSongs.append(AlbumSongItem(song: song))
|
||||
}
|
||||
@ -50,6 +59,7 @@ class AlbumTracksDataSource: NSObject, NSTableViewDataSource {
|
||||
type: .albumSongItem(song.mpdSong.uriString),
|
||||
title: song.title,
|
||||
artist: song.artist,
|
||||
albumArtist: song.albumArtist,
|
||||
album: song.album.title,
|
||||
uri: song.mpdSong.uriString
|
||||
),
|
||||
@ -70,10 +80,11 @@ class AlbumTracksDataSource: NSObject, NSTableViewDataSource {
|
||||
) { draggingItem, index, stop in
|
||||
guard let item = draggingItem.item as? NSPasteboardItem,
|
||||
let draggedSong = item.draggedSong(forType: .songPasteboardType),
|
||||
case let (title, artist, album, uri) = (
|
||||
case let (title, artist, album, albumArtist, uri) = (
|
||||
draggedSong.title,
|
||||
draggedSong.artist,
|
||||
draggedSong.album,
|
||||
draggedSong.albumArtist,
|
||||
draggedSong.uri
|
||||
)
|
||||
else { return }
|
||||
@ -83,6 +94,7 @@ class AlbumTracksDataSource: NSObject, NSTableViewDataSource {
|
||||
let draggedSongView = DraggedSongView(
|
||||
title: title,
|
||||
artist: artist,
|
||||
albumArtist: albumArtist,
|
||||
album: album,
|
||||
uri: uri
|
||||
)
|
||||
|
||||
26
Mac/Components/Playlists/SavePlaylistViewController.swift
Normal file
26
Mac/Components/Playlists/SavePlaylistViewController.swift
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// SavePlaylistViewController.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Dan Barber on 2020-4-11.
|
||||
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import AppKit
|
||||
|
||||
class SavePlaylistViewController: NSViewController {
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
}
|
||||
|
||||
@IBAction func saveButtonAction(_ sender: Any) {
|
||||
App.mpdClient.saveQueueToPlaylist(name: playlistName.stringValue)
|
||||
self.dismiss(sender)
|
||||
}
|
||||
|
||||
@IBAction func cancelButtonAction(_ sender: Any) {
|
||||
self.dismiss(sender)
|
||||
}
|
||||
|
||||
@IBOutlet var playlistName: NSTextFieldCell!
|
||||
}
|
||||
@ -44,6 +44,7 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
|
||||
type: .queueItem(queueItem.queuePos),
|
||||
title: queueItem.song.title,
|
||||
artist: queueItem.song.artist,
|
||||
albumArtist: queueItem.song.albumArtist,
|
||||
album: queueItem.song.album.title,
|
||||
uri: queueItem.song.mpdSong.uriString
|
||||
),
|
||||
@ -129,10 +130,11 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
|
||||
guard let item = draggingItem.item as? NSPasteboardItem,
|
||||
let data = item.data(forType: .songPasteboardType),
|
||||
let draggedSong = try? PropertyListDecoder().decode(DraggedSong.self, from: data),
|
||||
case let (title, artist, album, uri) = (
|
||||
case let (title, artist, album, albumArtist, uri) = (
|
||||
draggedSong.title,
|
||||
draggedSong.artist,
|
||||
draggedSong.album,
|
||||
draggedSong.albumArtist,
|
||||
draggedSong.uri
|
||||
)
|
||||
else { return }
|
||||
@ -142,6 +144,7 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
|
||||
let draggedSongView = DraggedSongView(
|
||||
title: title,
|
||||
artist: artist,
|
||||
albumArtist: albumArtist,
|
||||
album: album,
|
||||
uri: uri
|
||||
)
|
||||
|
||||
@ -44,7 +44,9 @@ class QueueViewController: NSViewController {
|
||||
case NSEvent.keyCodeBS:
|
||||
let queuePos = queueView.selectedRow
|
||||
|
||||
App.mpdClient.removeSong(at: queuePos)
|
||||
if queuePos >= 0 {
|
||||
App.mpdClient.removeSong(at: queuePos)
|
||||
}
|
||||
default:
|
||||
nextResponder?.keyDown(with: event)
|
||||
}
|
||||
|
||||
@ -16,12 +16,14 @@ class DraggedSongView: NSViewController {
|
||||
|
||||
private let songTitle: String
|
||||
private let songArtist: String
|
||||
private let songAlbumArtist: String
|
||||
private let songAlbum: String
|
||||
private let songUri: String
|
||||
|
||||
init(title: String, artist: String, album: String, uri: String) {
|
||||
init(title: String, artist: String, albumArtist: String, album: String, uri: String) {
|
||||
songTitle = title
|
||||
songArtist = artist
|
||||
songAlbumArtist = albumArtist
|
||||
songAlbum = album
|
||||
songUri = uri
|
||||
|
||||
@ -60,7 +62,7 @@ class DraggedSongView: NSViewController {
|
||||
}
|
||||
|
||||
func setCoverArt() {
|
||||
let mpdAlbum = MPDClient.MPDAlbum(title: songAlbum, artist: songArtist)
|
||||
let mpdAlbum = MPDClient.MPDAlbum(title: songAlbum, artist: songAlbumArtist)
|
||||
|
||||
let provider = MPDAlbumArtImageDataProvider(
|
||||
songUri: songUri,
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="15705" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="16096" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15705"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="16096"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
@ -176,6 +176,20 @@
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Playlists" id="WzS-gW-tqH">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Playlists" autoenablesItems="NO" identifier="playlistsMenu" id="v7W-VT-YsS">
|
||||
<items>
|
||||
<menuItem title="Save Playlist…" id="lL7-Nu-DRz">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<segue destination="3oS-GD-9H1" kind="modal" id="eUS-mx-Dph"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="cFk-06-bnH"/>
|
||||
</items>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Controls" id="hB8-hX-gD5">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Controls" autoenablesItems="NO" id="61K-cL-XWL">
|
||||
@ -277,7 +291,7 @@
|
||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="-183" y="-242"/>
|
||||
<point key="canvasLocation" x="-275" y="-242"/>
|
||||
</scene>
|
||||
<!--Window Controller-->
|
||||
<scene sceneID="R2V-B0-nI4">
|
||||
@ -502,7 +516,7 @@
|
||||
</windowController>
|
||||
<customObject id="0sd-8B-etN" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="346" y="-242"/>
|
||||
<point key="canvasLocation" x="349" y="-546"/>
|
||||
</scene>
|
||||
<!--General-->
|
||||
<scene sceneID="5er-B6-hoB">
|
||||
@ -689,18 +703,18 @@
|
||||
<objects>
|
||||
<viewController id="KIP-rq-4dM" customClass="QueueViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<splitView key="view" dividerStyle="thin" id="84I-w3-Mxl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="528"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="526"/>
|
||||
<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="198"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="196"/>
|
||||
<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="198"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="196"/>
|
||||
<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="198"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="329" height="196"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<size key="intercellSpacing" width="3" height="0.0"/>
|
||||
<color key="backgroundColor" name="_sourceListBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -859,7 +873,7 @@
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<customView id="iUb-eV-Qws">
|
||||
<rect key="frame" x="0.0" y="199" width="329" height="329"/>
|
||||
<rect key="frame" x="0.0" y="197" width="329" height="329"/>
|
||||
<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">
|
||||
@ -904,7 +918,7 @@
|
||||
</items>
|
||||
</menu>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="824.5" y="745.5"/>
|
||||
<point key="canvasLocation" x="824.5" y="766"/>
|
||||
</scene>
|
||||
<!--Album View Controller-->
|
||||
<scene sceneID="7Ua-Hj-zWt">
|
||||
@ -957,6 +971,108 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1485" y="182"/>
|
||||
</scene>
|
||||
<!--Save Playlist-->
|
||||
<scene sceneID="Aaj-nS-I3Z">
|
||||
<objects>
|
||||
<viewController title="Save Playlist" id="3oS-GD-9H1" customClass="SavePlaylistViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" misplaced="YES" id="dgg-SY-65p">
|
||||
<rect key="frame" x="0.0" y="0.0" width="286" height="93"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<gridView xPlacement="leading" yPlacement="bottom" rowAlignment="none" rowSpacing="8" translatesAutoresizingMaskIntoConstraints="NO" id="dtK-8L-o11">
|
||||
<rect key="frame" x="0.0" y="0.0" width="286" height="95"/>
|
||||
<rows>
|
||||
<gridRow topPadding="20" id="ilS-gI-bD9"/>
|
||||
<gridRow bottomPadding="17" id="dEZ-bX-GdR"/>
|
||||
</rows>
|
||||
<columns>
|
||||
<gridColumn leadingPadding="20" id="NE8-vb-vlD"/>
|
||||
<gridColumn trailingPadding="20" id="ES5-gL-nIM"/>
|
||||
</columns>
|
||||
<gridCells>
|
||||
<gridCell row="ilS-gI-bD9" column="NE8-vb-vlD" xPlacement="trailing" yPlacement="center" id="r1d-Yn-TIF">
|
||||
<textField key="contentView" horizontalHuggingPriority="251" verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="LIQ-7r-gbX">
|
||||
<rect key="frame" x="18" y="57" width="44" height="16"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Name:" id="7UN-bH-wIn">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</gridCell>
|
||||
<gridCell row="ilS-gI-bD9" column="ES5-gL-nIM" yPlacement="center" id="oSk-cx-yku">
|
||||
<textField key="contentView" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="vfb-1b-g2p">
|
||||
<rect key="frame" x="66" y="54" width="200" height="21"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="200" id="UEE-eo-PUq"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" drawsBackground="YES" id="vhi-nK-mWd">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</gridCell>
|
||||
<gridCell row="dEZ-bX-GdR" column="NE8-vb-vlD" id="bwK-5c-kB7"/>
|
||||
<gridCell row="dEZ-bX-GdR" column="ES5-gL-nIM" xPlacement="trailing" id="iiE-dZ-nPw">
|
||||
<stackView key="contentView" distribution="fill" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="QAY-lb-fGF">
|
||||
<rect key="frame" x="118" y="17" width="148" height="21"/>
|
||||
<subviews>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gG3-Cp-l12">
|
||||
<rect key="frame" x="-6" y="-7" width="82" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Cancel" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="NW6-Xn-CWZ">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
<string key="keyEquivalent" base64-UTF8="YES">
|
||||
Gw
|
||||
</string>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="cancelButtonAction:" target="3oS-GD-9H1" id="7f1-6J-VDQ"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pUw-bS-at3" userLabel="Save">
|
||||
<rect key="frame" x="72" y="-7" width="82" height="32"/>
|
||||
<buttonCell key="cell" type="push" title="Save" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="HPK-qZ-pkt">
|
||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
<connections>
|
||||
<action selector="saveButtonAction:" target="3oS-GD-9H1" id="iRy-nu-VaK"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="pUw-bS-at3" firstAttribute="width" secondItem="gG3-Cp-l12" secondAttribute="width" id="OLN-VK-JsL"/>
|
||||
</constraints>
|
||||
<visibilityPriorities>
|
||||
<integer value="1000"/>
|
||||
<integer value="1000"/>
|
||||
</visibilityPriorities>
|
||||
<customSpacing>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
<real value="3.4028234663852886e+38"/>
|
||||
</customSpacing>
|
||||
</stackView>
|
||||
</gridCell>
|
||||
</gridCells>
|
||||
</gridView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="bottom" secondItem="dtK-8L-o11" secondAttribute="bottom" id="MuW-ZC-8WV"/>
|
||||
<constraint firstItem="dtK-8L-o11" firstAttribute="leading" secondItem="dgg-SY-65p" secondAttribute="leading" id="XHm-e2-NVy"/>
|
||||
<constraint firstItem="dtK-8L-o11" firstAttribute="top" secondItem="dgg-SY-65p" secondAttribute="top" id="hui-Vk-WXp"/>
|
||||
<constraint firstAttribute="trailing" secondItem="dtK-8L-o11" secondAttribute="trailing" id="oNB-WL-1Fm"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="playlistName" destination="vhi-nK-mWd" id="SF8-1x-eqS"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="2co-Sp-RKj" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="349" y="-261"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="NSPreferencesGeneral" width="32" height="32"/>
|
||||
|
||||
@ -31,6 +31,7 @@ class WindowController: NSWindowController {
|
||||
|
||||
@IBOutlet weak var searchQuery: NSSearchField!
|
||||
|
||||
@IBOutlet weak var savePlaylistMenuItem: NSMenuItem!
|
||||
override func windowDidLoad() {
|
||||
super.windowDidLoad()
|
||||
window?.titleVisibility = .hidden
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
38BAC36B249CB1A7004BAEA4 /* AlbumDetailSongTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 38BAC36A249CB1A6004BAEA4 /* AlbumDetailSongTitleView.swift */; };
|
||||
E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E407861B2110CE6E006887B1 /* AppDelegate.swift */; };
|
||||
E40786202110CE70006887B1 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E407861F2110CE70006887B1 /* Assets.xcassets */; };
|
||||
E40786232110CE70006887B1 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = E40786212110CE70006887B1 /* Main.storyboard */; };
|
||||
@ -35,6 +36,12 @@
|
||||
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 */; };
|
||||
E42B18E024538473000C8DFD /* MPDClient+Playlists.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42B18DF24538473000C8DFD /* MPDClient+Playlists.swift */; };
|
||||
E42B18E124538473000C8DFD /* MPDClient+Playlists.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42B18DF24538473000C8DFD /* MPDClient+Playlists.swift */; };
|
||||
E42B18E324539110000C8DFD /* PlaylistState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42B18E224539110000C8DFD /* PlaylistState.swift */; };
|
||||
E42B18E424539110000C8DFD /* PlaylistState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42B18E224539110000C8DFD /* PlaylistState.swift */; };
|
||||
E42B18E6245391F6000C8DFD /* PlaylistActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42B18E5245391F6000C8DFD /* PlaylistActions.swift */; };
|
||||
E42B18E7245391F6000C8DFD /* PlaylistActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42B18E5245391F6000C8DFD /* PlaylistActions.swift */; };
|
||||
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E1221CD4E200184CFC /* NSFont.swift */; };
|
||||
E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E3221CD75D00184CFC /* NSImage.swift */; };
|
||||
E439109822640213002982E9 /* SongNotifierService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E439109722640213002982E9 /* SongNotifierService.swift */; };
|
||||
@ -259,6 +266,7 @@
|
||||
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 */; };
|
||||
E4ABD9ED2442935700D7A8EF /* SavePlaylistViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4ABD9EB2442935700D7A8EF /* SavePlaylistViewController.swift */; };
|
||||
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 */; };
|
||||
@ -284,6 +292,9 @@
|
||||
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 */; };
|
||||
E4DFF2F82454E9A4001A89DD /* PlaylistReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DFF2F72454E9A4001A89DD /* PlaylistReducer.swift */; };
|
||||
E4DFF2FA2454ECDF001A89DD /* MPDPlaylist.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DFF2F92454ECDF001A89DD /* MPDPlaylist.swift */; };
|
||||
E4DFF2FC2454EED1001A89DD /* Playlist.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DFF2FB2454EED1001A89DD /* Playlist.swift */; };
|
||||
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 */; };
|
||||
@ -368,6 +379,7 @@
|
||||
/* End PBXCopyFilesBuildPhase section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
38BAC36A249CB1A6004BAEA4 /* AlbumDetailSongTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailSongTitleView.swift; sourceTree = "<group>"; };
|
||||
E40786182110CE6E006887B1 /* Persephone.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Persephone.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
E407861B2110CE6E006887B1 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
E407861F2110CE70006887B1 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
@ -442,6 +454,9 @@
|
||||
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>"; };
|
||||
E42B18DF24538473000C8DFD /* MPDClient+Playlists.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Playlists.swift"; sourceTree = "<group>"; };
|
||||
E42B18E224539110000C8DFD /* PlaylistState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistState.swift; sourceTree = "<group>"; };
|
||||
E42B18E5245391F6000C8DFD /* PlaylistActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistActions.swift; sourceTree = "<group>"; };
|
||||
E435E3E1221CD4E200184CFC /* NSFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSFont.swift; sourceTree = "<group>"; };
|
||||
E435E3E3221CD75D00184CFC /* NSImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSImage.swift; sourceTree = "<group>"; };
|
||||
E439109722640213002982E9 /* SongNotifierService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SongNotifierService.swift; sourceTree = "<group>"; };
|
||||
@ -596,6 +611,7 @@
|
||||
E4A642D922090CBE00067D21 /* MPDStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDStatus.swift; sourceTree = "<group>"; };
|
||||
E4A83BEE2221F8CF0098FED6 /* CoverArtPrefsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoverArtPrefsController.swift; sourceTree = "<group>"; };
|
||||
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = "<group>"; };
|
||||
E4ABD9EB2442935700D7A8EF /* SavePlaylistViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SavePlaylistViewController.swift; sourceTree = "<group>"; };
|
||||
E4B11B52226928F20075461B /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
||||
E4B11B60226A4BFF0075461B /* PlayerReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerReducer.swift; sourceTree = "<group>"; };
|
||||
E4B11B62226A4C510075461B /* AppReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReducer.swift; sourceTree = "<group>"; };
|
||||
@ -623,6 +639,9 @@
|
||||
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>"; };
|
||||
E4DFF2F72454E9A4001A89DD /* PlaylistReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlaylistReducer.swift; sourceTree = "<group>"; };
|
||||
E4DFF2F92454ECDF001A89DD /* MPDPlaylist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDPlaylist.swift; sourceTree = "<group>"; };
|
||||
E4DFF2FB2454EED1001A89DD /* Playlist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Playlist.swift; 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>"; };
|
||||
@ -787,6 +806,7 @@
|
||||
E41E5304223BFB0700173814 /* MPDClient+Error.swift */,
|
||||
E41E5302223BF9C300173814 /* MPDClient+Idle.swift */,
|
||||
E453825423FA347C007F6BFC /* MPDClient+Mixer.swift */,
|
||||
E42B18DF24538473000C8DFD /* MPDClient+Playlists.swift */,
|
||||
E41E5300223BF99300173814 /* MPDClient+Queue.swift */,
|
||||
E42A4D5022E2167E001C6CAD /* MPDClient+Songs.swift */,
|
||||
E41E5306223C019100173814 /* MPDClient+Status.swift */,
|
||||
@ -877,6 +897,7 @@
|
||||
E442CCC42347D5B900004E0C /* Components */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E4ABD9EA2442912100D7A8EF /* Playlists */,
|
||||
E453824D23F9F700007F6BFC /* VolumeControl */,
|
||||
E442CCCB2347D77A00004E0C /* Browser */,
|
||||
E4A83BEC2221F5DD0098FED6 /* Preferences */,
|
||||
@ -933,6 +954,7 @@
|
||||
E43B67A822909793007DCF55 /* AlbumDetailView.swift */,
|
||||
E4E7A6AC22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift */,
|
||||
E43B67AC229194CD007DCF55 /* AlbumTracksDataSource.swift */,
|
||||
38BAC36A249CB1A6004BAEA4 /* AlbumDetailSongTitleView.swift */,
|
||||
);
|
||||
path = "Album Detail";
|
||||
sourceTree = "<group>";
|
||||
@ -1197,6 +1219,14 @@
|
||||
path = Services;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E4ABD9EA2442912100D7A8EF /* Playlists */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E4ABD9EB2442935700D7A8EF /* SavePlaylistViewController.swift */,
|
||||
);
|
||||
path = Playlists;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
E4B11B5F226A4BED0075461B /* Reducers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@ -1207,6 +1237,7 @@
|
||||
E4B11B74226CC4D30075461B /* QueueReducer.swift */,
|
||||
E4F2EFF124076B5E00198159 /* ServerReducer.swift */,
|
||||
E440519D227BB0720090CD6F /* UIReducer.swift */,
|
||||
E4DFF2F72454E9A4001A89DD /* PlaylistReducer.swift */,
|
||||
);
|
||||
path = Reducers;
|
||||
sourceTree = "<group>";
|
||||
@ -1223,6 +1254,7 @@
|
||||
E4B11B67226A4FA00075461B /* QueueState.swift */,
|
||||
E4F2EFED24076A2700198159 /* ServerState.swift */,
|
||||
E440519F227BB0AB0090CD6F /* UIState.swift */,
|
||||
E42B18E224539110000C8DFD /* PlaylistState.swift */,
|
||||
);
|
||||
path = State;
|
||||
sourceTree = "<group>";
|
||||
@ -1236,6 +1268,7 @@
|
||||
E4B11BBF2275EE150075461B /* QueueActions.swift */,
|
||||
E4F2EFEF24076B0900198159 /* ServerActions.swift */,
|
||||
E440519B227BAF2E0090CD6F /* UIActions.swift */,
|
||||
E42B18E5245391F6000C8DFD /* PlaylistActions.swift */,
|
||||
);
|
||||
path = Actions;
|
||||
sourceTree = "<group>";
|
||||
@ -1259,6 +1292,7 @@
|
||||
E4E8CC9922075D370024217A /* MPDSong.swift */,
|
||||
E4A642D922090CBE00067D21 /* MPDStatus.swift */,
|
||||
E42A4D4E22E20D7D001C6CAD /* MPDTag.swift */,
|
||||
E4DFF2F92454ECDF001A89DD /* MPDPlaylist.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
@ -1292,6 +1326,7 @@
|
||||
E419E2862249B96600216A8C /* Song.swift */,
|
||||
E47E2FDC2220A6D100F747E6 /* Time.swift */,
|
||||
E4B11B72226A6C770075461B /* TrackTimer.swift */,
|
||||
E4DFF2FB2454EED1001A89DD /* Playlist.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
@ -1430,7 +1465,7 @@
|
||||
isa = PBXProject;
|
||||
attributes = {
|
||||
LastSwiftUpdateCheck = 1130;
|
||||
LastUpgradeCheck = 0940;
|
||||
LastUpgradeCheck = 1200;
|
||||
ORGANIZATIONNAME = "Dan Barber";
|
||||
TargetAttributes = {
|
||||
E40786172110CE6E006887B1 = {
|
||||
@ -1662,6 +1697,7 @@
|
||||
E48059EA2426D73600362CF3 /* coutput.c in Sources */,
|
||||
E4B11B61226A4C000075461B /* PlayerReducer.swift in Sources */,
|
||||
E4805A0E2426D73600362CF3 /* cmount.c in Sources */,
|
||||
E42B18E6245391F6000C8DFD /* PlaylistActions.swift in Sources */,
|
||||
E4FF71922276029000D4C412 /* PreferencesActions.swift in Sources */,
|
||||
E489E39922B85D0400CA8CBD /* NSPasteboard.swift in Sources */,
|
||||
E43AC1F122C68E6A001E483C /* NSPasteboardItem.swift in Sources */,
|
||||
@ -1671,6 +1707,8 @@
|
||||
E48059CC2426D73600362CF3 /* mixer.c in Sources */,
|
||||
E48059D22426D73600362CF3 /* partition.c in Sources */,
|
||||
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */,
|
||||
E4ABD9ED2442935700D7A8EF /* SavePlaylistViewController.swift in Sources */,
|
||||
E4DFF2F82454E9A4001A89DD /* PlaylistReducer.swift in Sources */,
|
||||
E48059E02426D73600362CF3 /* password.c in Sources */,
|
||||
E48059EC2426D73600362CF3 /* stats.c in Sources */,
|
||||
E419E2872249B96600216A8C /* Song.swift in Sources */,
|
||||
@ -1681,6 +1719,7 @@
|
||||
E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */,
|
||||
E48059D62426D73600362CF3 /* ierror.c in Sources */,
|
||||
E4FF718E2276010E00D4C412 /* PreferencesState.swift in Sources */,
|
||||
E42B18E324539110000C8DFD /* PlaylistState.swift in Sources */,
|
||||
E48059DE2426D73600362CF3 /* audio_format.c in Sources */,
|
||||
E439109822640213002982E9 /* SongNotifierService.swift in Sources */,
|
||||
E4B3DF6523D66A4400728F6B /* QueueSongCoverView.swift in Sources */,
|
||||
@ -1703,6 +1742,7 @@
|
||||
E4805A062426D73600362CF3 /* error.c in Sources */,
|
||||
E4805A122426D73600362CF3 /* rplaylist.c in Sources */,
|
||||
E4805A202426D73600362CF3 /* parser.c in Sources */,
|
||||
E42B18E024538473000C8DFD /* MPDClient+Playlists.swift in Sources */,
|
||||
E4B5AE7E22F4C49600CCEC65 /* MPDServerDelegate.swift in Sources */,
|
||||
E4805A2A2426D73600362CF3 /* message.c in Sources */,
|
||||
E4B11BB8227538FA0075461B /* CurrentCoverArtView.swift in Sources */,
|
||||
@ -1714,14 +1754,17 @@
|
||||
E480510F2423BD7100362CF3 /* MachTime.swift in Sources */,
|
||||
E47E2FD5222071FD00F747E6 /* AlbumViewItem.swift in Sources */,
|
||||
E4805A142426D73600362CF3 /* response.c in Sources */,
|
||||
38BAC36B249CB1A7004BAEA4 /* AlbumDetailSongTitleView.swift in Sources */,
|
||||
E4F2EFF024076B0900198159 /* ServerActions.swift in Sources */,
|
||||
E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */,
|
||||
E4DFF2FA2454ECDF001A89DD /* MPDPlaylist.swift in Sources */,
|
||||
E4B11B66226A4F830075461B /* PlayerState.swift in Sources */,
|
||||
E4B11BBE2275EDAA0075461B /* PlayerActions.swift in Sources */,
|
||||
E4FF71942276043A00D4C412 /* MPDServer.swift in Sources */,
|
||||
E4805A282426D73600362CF3 /* player.c in Sources */,
|
||||
E48059D02426D73600362CF3 /* send.c in Sources */,
|
||||
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */,
|
||||
E4DFF2FC2454EED1001A89DD /* Playlist.swift in Sources */,
|
||||
E440519C227BAF2E0090CD6F /* UIActions.swift in Sources */,
|
||||
E48059F22426D73600362CF3 /* capabilities.c in Sources */,
|
||||
E48059F82426D73600362CF3 /* rdirectory.c in Sources */,
|
||||
@ -1773,6 +1816,7 @@
|
||||
E4805A0F2426D73600362CF3 /* cmount.c in Sources */,
|
||||
E48059E32426D73600362CF3 /* mount.c in Sources */,
|
||||
E480511424255BAF00362CF3 /* DraggedSong.swift in Sources */,
|
||||
E42B18E124538473000C8DFD /* MPDClient+Playlists.swift in Sources */,
|
||||
E48059CB2426D73600362CF3 /* list.c in Sources */,
|
||||
E4805A212426D73600362CF3 /* parser.c in Sources */,
|
||||
E48059EB2426D73600362CF3 /* coutput.c in Sources */,
|
||||
@ -1799,6 +1843,7 @@
|
||||
E480513D24255E7200362CF3 /* PlayerState.swift in Sources */,
|
||||
E480511D24255BD200362CF3 /* MPDClientWrapper.c in Sources */,
|
||||
E480512924255BDB00362CF3 /* MPDClient+Status.swift in Sources */,
|
||||
E42B18E424539110000C8DFD /* PlaylistState.swift in Sources */,
|
||||
E48059FD2426D73600362CF3 /* neighbor.c in Sources */,
|
||||
E4805A252426D73600362CF3 /* iso8601.c in Sources */,
|
||||
E480512C24255BDF00362CF3 /* MPDCommand.swift in Sources */,
|
||||
@ -1856,6 +1901,7 @@
|
||||
E480512224255BDB00362CF3 /* MPDClient+Connection.swift in Sources */,
|
||||
E480511124255BA900362CF3 /* MachTime.swift in Sources */,
|
||||
E4805A112426D73600362CF3 /* tag.c in Sources */,
|
||||
E42B18E7245391F6000C8DFD /* PlaylistActions.swift in Sources */,
|
||||
E4805A192426D73600362CF3 /* status.c in Sources */,
|
||||
E48059EF2426D73600362CF3 /* socket.c in Sources */,
|
||||
E480514B24255E7D00362CF3 /* PreferencesActions.swift in Sources */,
|
||||
@ -1957,6 +2003,7 @@
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
@ -2018,6 +2065,7 @@
|
||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
||||
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
|
||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
||||
@ -2067,7 +2115,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Shared/libmpdclient/output",
|
||||
);
|
||||
MARKETING_VERSION = "0.17.1-alpha";
|
||||
MARKETING_VERSION = "0.17.3-alpha";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.Persephone;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@ -2100,7 +2148,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Shared/libmpdclient/output",
|
||||
);
|
||||
MARKETING_VERSION = "0.17.1-alpha";
|
||||
MARKETING_VERSION = "0.17.3-alpha";
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.Persephone;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1010"
|
||||
LastUpgradeVersion = "1200"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
|
||||
@ -1,47 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>SchemeUserState</key>
|
||||
<dict>
|
||||
<key>Persephone-iOS.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</dict>
|
||||
<key>Persephone.xcscheme</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>Persephone.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>0</integer>
|
||||
</dict>
|
||||
<key>libmpdclient.xcscheme_^#shared#^_</key>
|
||||
<dict>
|
||||
<key>orderHint</key>
|
||||
<integer>2</integer>
|
||||
</dict>
|
||||
</dict>
|
||||
<key>SuppressBuildableAutocreation</key>
|
||||
<dict>
|
||||
<key>E40786172110CE6E006887B1</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>E40786292110CE70006887B1</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>E40786342110CE70006887B1</key>
|
||||
<dict>
|
||||
<key>primary</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</dict>
|
||||
</dict>
|
||||
</plist>
|
||||
@ -69,4 +69,10 @@ class MPDServerDelegate: MPDClientDelegate {
|
||||
|
||||
func didLoadArtists(mpdClient: MPDClient, artists: [String]) {
|
||||
}
|
||||
|
||||
func didLoadPlaylists(mpdClient: MPDClient, playlists: [MPDClient.MPDPlaylist]) {
|
||||
DispatchQueue.main.async {
|
||||
App.store.dispatch(UpdatePlaylistsAction(playlists: playlists))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,9 +59,11 @@ extension MPDClient {
|
||||
|
||||
mpd_search_db_songs(self.connection, false)
|
||||
if filter != "" {
|
||||
let escapedFilter = filter.replacingOccurrences(of: "'", with: "\\'")
|
||||
|
||||
mpd_search_add_expression(
|
||||
self.connection,
|
||||
"(any contains '\(filter)')"
|
||||
"(any contains '\(escapedFilter)')"
|
||||
)
|
||||
}
|
||||
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_TRACK, "1")
|
||||
@ -73,7 +75,7 @@ extension MPDClient {
|
||||
|
||||
let mpdAlbum = MPDAlbum(
|
||||
title: mpdSong.album.title,
|
||||
artist: mpdSong.artist,
|
||||
artist: mpdSong.albumArtist,
|
||||
firstSong: mpdSong,
|
||||
date: mpdSong.date
|
||||
)
|
||||
|
||||
@ -142,6 +142,20 @@ extension MPDClient {
|
||||
offset: offset,
|
||||
callback: callback
|
||||
)
|
||||
|
||||
// Playlist commands
|
||||
case .fetchPlaylists:
|
||||
playlists()
|
||||
|
||||
case .saveQueueToPlaylist:
|
||||
guard let name = userData["name"] as? String else { return }
|
||||
|
||||
sendSaveQueueToPlaylist(name: name)
|
||||
|
||||
case .loadQueueFromPlaylist:
|
||||
guard let name = userData["name"] as? String else { return }
|
||||
|
||||
sendLoadQueueFromPlaylist(name: name)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -59,6 +59,9 @@ extension MPDClient {
|
||||
if mpdIdle.contains(.database) {
|
||||
self.fetchAllAlbums()
|
||||
}
|
||||
if mpdIdle.contains(.storedPlaylist) {
|
||||
self.fetchPlaylists()
|
||||
}
|
||||
if mpdIdle.contains(.queue) {
|
||||
self.fetchQueue()
|
||||
self.fetchStatus()
|
||||
|
||||
52
Shared/MPDClient/Extensions/MPDClient+Playlists.swift
Normal file
52
Shared/MPDClient/Extensions/MPDClient+Playlists.swift
Normal file
@ -0,0 +1,52 @@
|
||||
//
|
||||
// MPDClient+Playlists.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Dan Barber on 2020-4-24.
|
||||
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import mpdclient
|
||||
|
||||
extension MPDClient {
|
||||
func fetchPlaylists() {
|
||||
enqueueCommand(command: .fetchPlaylists)
|
||||
}
|
||||
|
||||
func saveQueueToPlaylist(name: String) {
|
||||
enqueueCommand(
|
||||
command: .saveQueueToPlaylist,
|
||||
userData: ["name": name]
|
||||
)
|
||||
}
|
||||
|
||||
func loadQueueFromPlaylist(name: String) {
|
||||
enqueueCommand(
|
||||
command: .loadQueueFromPlaylist,
|
||||
userData: ["name": name]
|
||||
)
|
||||
}
|
||||
|
||||
func playlists() {
|
||||
var playlists: [MPDPlaylist] = []
|
||||
|
||||
mpd_send_list_playlists(connection)
|
||||
|
||||
while let playlist = mpd_recv_playlist(connection) {
|
||||
let mpdPlaylist = MPDPlaylist(playlist)
|
||||
|
||||
playlists.append(mpdPlaylist)
|
||||
}
|
||||
|
||||
self.delegate?.didLoadPlaylists(mpdClient: self, playlists: playlists)
|
||||
}
|
||||
|
||||
func sendSaveQueueToPlaylist(name: String) {
|
||||
mpd_run_save(connection, name)
|
||||
}
|
||||
|
||||
func sendLoadQueueFromPlaylist(name: String) {
|
||||
mpd_run_load(connection, name)
|
||||
}
|
||||
}
|
||||
@ -53,5 +53,10 @@ extension MPDClient {
|
||||
|
||||
// Song commands
|
||||
case fetchAlbumArt
|
||||
|
||||
// Playlist commands
|
||||
case fetchPlaylists
|
||||
case saveQueueToPlaylist
|
||||
case loadQueueFromPlaylist
|
||||
}
|
||||
}
|
||||
|
||||
31
Shared/MPDClient/Models/MPDPlaylist.swift
Normal file
31
Shared/MPDClient/Models/MPDPlaylist.swift
Normal file
@ -0,0 +1,31 @@
|
||||
//
|
||||
// MPDPlaylist.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Dan Barber on 2020-4-25.
|
||||
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension MPDClient {
|
||||
class MPDPlaylist {
|
||||
let playlist: OpaquePointer
|
||||
|
||||
init(_ playlist: OpaquePointer) {
|
||||
self.playlist = playlist
|
||||
}
|
||||
|
||||
deinit {
|
||||
mpd_playlist_free(playlist)
|
||||
}
|
||||
|
||||
var path: UnsafePointer<Int8> {
|
||||
return mpd_playlist_get_path(playlist)
|
||||
}
|
||||
|
||||
var pathString: String {
|
||||
return String(cString: path)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -36,16 +36,21 @@ extension MPDClient {
|
||||
var album: MPDAlbum {
|
||||
return MPDAlbum(
|
||||
title: getTag(.album),
|
||||
artist: artist,
|
||||
artist: albumArtist,
|
||||
date: date
|
||||
)
|
||||
}
|
||||
|
||||
var artist: String {
|
||||
if getTag(.albumArtist) != "" {
|
||||
return getTag(.albumArtist)
|
||||
return getTag(.artist)
|
||||
}
|
||||
|
||||
var albumArtist: String {
|
||||
let albumArtist = getTag(.albumArtist);
|
||||
if albumArtist != "" {
|
||||
return albumArtist
|
||||
} else {
|
||||
return getTag(.artist)
|
||||
return artist
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -25,4 +25,6 @@ protocol MPDClientDelegate {
|
||||
func didLoadAlbums(mpdClient: MPDClient, albums: [MPDClient.MPDAlbum])
|
||||
|
||||
func didLoadArtists(mpdClient: MPDClient, artists: [String])
|
||||
|
||||
func didLoadPlaylists(mpdClient: MPDClient, playlists: [MPDClient.MPDPlaylist])
|
||||
}
|
||||
|
||||
@ -10,6 +10,7 @@ struct DraggedSong: Codable {
|
||||
var type: DraggedSongType
|
||||
var title: String
|
||||
var artist: String
|
||||
var albumArtist: String
|
||||
var album: String
|
||||
var uri: String
|
||||
}
|
||||
|
||||
25
Shared/Models/Playlist.swift
Normal file
25
Shared/Models/Playlist.swift
Normal file
@ -0,0 +1,25 @@
|
||||
//
|
||||
// Playlist.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Dan Barber on 2020-4-25.
|
||||
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
struct Playlist {
|
||||
var mpdPlaylist: MPDClient.MPDPlaylist
|
||||
|
||||
init(mpdPlaylist: MPDClient.MPDPlaylist) {
|
||||
self.mpdPlaylist = mpdPlaylist
|
||||
}
|
||||
|
||||
var path: String {
|
||||
return mpdPlaylist.pathString
|
||||
}
|
||||
}
|
||||
|
||||
extension Playlist: Equatable {
|
||||
static func == (lhs: Playlist, rhs: Playlist) -> Bool {
|
||||
return lhs.path == rhs.path
|
||||
}
|
||||
}
|
||||
@ -25,6 +25,10 @@ struct Song {
|
||||
return mpdSong.getTag(.artist)
|
||||
}
|
||||
|
||||
var albumArtist: String {
|
||||
return mpdSong.getTag(.albumArtist)
|
||||
}
|
||||
|
||||
var duration: Time {
|
||||
return Time(timeInSeconds: mpdSong.duration)
|
||||
}
|
||||
|
||||
13
Shared/State/Actions/PlaylistActions.swift
Normal file
13
Shared/State/Actions/PlaylistActions.swift
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// PlaylistActions.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Dan Barber on 2020-4-24.
|
||||
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import ReSwift
|
||||
|
||||
struct UpdatePlaylistsAction: Action {
|
||||
var playlists: [MPDClient.MPDPlaylist]
|
||||
}
|
||||
@ -15,4 +15,5 @@ struct AppState: StateType {
|
||||
var albumListState = AlbumListState()
|
||||
var preferencesState = PreferencesState()
|
||||
var uiState = UIState()
|
||||
var playlistState = PlaylistState()
|
||||
}
|
||||
|
||||
13
Shared/State/PlaylistState.swift
Normal file
13
Shared/State/PlaylistState.swift
Normal file
@ -0,0 +1,13 @@
|
||||
//
|
||||
// PlaylistState.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Dan Barber on 2020-4-24.
|
||||
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import ReSwift
|
||||
|
||||
struct PlaylistState: StateType, Equatable {
|
||||
var playlists: [Playlist] = []
|
||||
}
|
||||
@ -15,6 +15,7 @@ func appReducer(action: Action, state: AppState?) -> AppState {
|
||||
queueState: queueReducer(action: action, state: state?.queueState),
|
||||
albumListState: albumListReducer(action: action, state: state?.albumListState),
|
||||
preferencesState: preferencesReducer(action: action, state: state?.preferencesState),
|
||||
uiState: uiReducer(action: action, state: state?.uiState)
|
||||
uiState: uiReducer(action: action, state: state?.uiState),
|
||||
playlistState: playlistReducer(action: action, state: state?.playlistState)
|
||||
)
|
||||
}
|
||||
|
||||
@ -51,6 +51,12 @@ func playerReducer(action: Action, state: PlayerState?) -> PlayerState {
|
||||
case let action as UpdateElapsedTimeAction:
|
||||
state.elapsedTimeMs = action.elapsedTimeMs
|
||||
|
||||
if let elapsedTimeMs = state.elapsedTimeMs,
|
||||
let totalTime = state.totalTime,
|
||||
elapsedTimeMs / 1000 > totalTime {
|
||||
App.mpdClient.enqueueCommand(command: .fetchStatus)
|
||||
}
|
||||
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
24
Shared/State/Reducers/PlaylistReducer.swift
Normal file
24
Shared/State/Reducers/PlaylistReducer.swift
Normal file
@ -0,0 +1,24 @@
|
||||
//
|
||||
// PlaylistReducer.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Dan Barber on 2020-4-25.
|
||||
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import ReSwift
|
||||
|
||||
func playlistReducer(action: Action, state: PlaylistState?) -> PlaylistState {
|
||||
var state = state ?? PlaylistState()
|
||||
|
||||
switch action {
|
||||
case let action as UpdatePlaylistsAction:
|
||||
state.playlists = action.playlists.map { Playlist(mpdPlaylist: $0) }
|
||||
|
||||
default:
|
||||
break
|
||||
|
||||
}
|
||||
|
||||
return state
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user