Compare commits
No commits in common. "c406737ec124794f753feb85b89ea6fd607c2654" and "bd00dcf57643914a377d86913d0c92148dd8ffd2" have entirely different histories.
c406737ec1
...
bd00dcf576
27
CHANGELOG.md
@ -1,27 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
## 0.10.2a - 2019-03-23
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Will fetch album art from the filesystem or MusicBrainz
|
|
||||||
- Menu option to update the database
|
|
||||||
- Album art preferences icon
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- Refactored command queue to be more reliable
|
|
||||||
- Resizing the windows no longer makes the album view jump around
|
|
||||||
- Fixed a crash when another client clears the queue
|
|
||||||
|
|
||||||
## 0.8.0a - 2019-02-23
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Connects to MPD
|
|
||||||
- Lists all albums
|
|
||||||
- Will play an album
|
|
||||||
- Queue is working
|
|
||||||
- Transport controls are working
|
|
||||||
- Seek bar is working
|
|
||||||
- Media keys work
|
|
||||||
2
Cartfile
@ -1,3 +1 @@
|
|||||||
github "SwiftyJSON/SwiftyJSON" ~> 4.0
|
|
||||||
github "PromiseKit/Foundation" ~> 3.0
|
|
||||||
github "nhurden/MediaKeyTap" "fix-tis-tsm-error"
|
github "nhurden/MediaKeyTap" "fix-tis-tsm-error"
|
||||||
|
|||||||
@ -1,4 +1 @@
|
|||||||
github "PromiseKit/Foundation" "3.3.1"
|
|
||||||
github "SwiftyJSON/SwiftyJSON" "4.2.0"
|
|
||||||
github "mxcl/PromiseKit" "6.8.3"
|
|
||||||
github "nhurden/MediaKeyTap" "355d346c56243e6d56487fa46fcad945251e16ae"
|
github "nhurden/MediaKeyTap" "355d346c56243e6d56487fa46fcad945251e16ae"
|
||||||
|
|||||||
@ -16,60 +16,29 @@
|
|||||||
E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */; };
|
E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */; };
|
||||||
E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */; };
|
E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */; };
|
||||||
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3C1220E134F0006D9BE /* AlbumViewController.swift */; };
|
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3C1220E134F0006D9BE /* AlbumViewController.swift */; };
|
||||||
E408D3CB220E341D0006D9BE /* AlbumViewItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = E408D3C9220E341D0006D9BE /* AlbumViewItem.xib */; };
|
E408D3CB220E341D0006D9BE /* AlbumItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = E408D3C9220E341D0006D9BE /* AlbumItem.xib */; };
|
||||||
E40F41F3221EDE27004B6CB8 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40F41F2221EDE27004B6CB8 /* Preferences.swift */; };
|
E40F41F3221EDE27004B6CB8 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40F41F2221EDE27004B6CB8 /* Preferences.swift */; };
|
||||||
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40FE71A221B904300A4223F /* NSEvent.swift */; };
|
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40FE71A221B904300A4223F /* NSEvent.swift */; };
|
||||||
E41B22C021FB6BBA00D544F6 /* libmpdclient.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (Required, ); }; };
|
E41B22C021FB6BBA00D544F6 /* libmpdclient.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (Required, ); }; };
|
||||||
E41B22C121FB6C3300D544F6 /* libmpdclient.2.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
E41B22C121FB6C3300D544F6 /* libmpdclient.2.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||||
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41B22C521FB932700D544F6 /* MPDClient.swift */; };
|
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41B22C521FB932700D544F6 /* MPDClient.swift */; };
|
||||||
E41E52FD223BF87300173814 /* MPDClient+Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E52FC223BF87300173814 /* MPDClient+Connection.swift */; };
|
E41EA46C221636AF0068EF46 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EA46B221636AF0068EF46 /* PreferencesViewController.swift */; };
|
||||||
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */; };
|
|
||||||
E41E5301223BF99300173814 /* MPDClient+Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5300223BF99300173814 /* MPDClient+Queue.swift */; };
|
|
||||||
E41E5303223BF9C300173814 /* MPDClient+Idle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5302223BF9C300173814 /* MPDClient+Idle.swift */; };
|
|
||||||
E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5304223BFB0700173814 /* MPDClient+Error.swift */; };
|
|
||||||
E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5306223C019100173814 /* MPDClient+Status.swift */; };
|
|
||||||
E41E5309223C020400173814 /* MPDClient+Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5308223C020400173814 /* MPDClient+Command.swift */; };
|
|
||||||
E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E530A223C033700173814 /* MPDClient+Album.swift */; };
|
|
||||||
E41E530E223EF4CF00173814 /* AlbumArtService+Caching.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E530D223EF4CF00173814 /* AlbumArtService+Caching.swift */; };
|
|
||||||
E41E5310223EF6CE00173814 /* AlbumArtService+Remote.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E530F223EF6CE00173814 /* AlbumArtService+Remote.swift */; };
|
|
||||||
E41E5312223EF74A00173814 /* AlbumArtService+Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5311223EF74A00173814 /* AlbumArtService+Filesystem.swift */; };
|
|
||||||
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */; };
|
|
||||||
E421ACA3221F73C4008B2449 /* MediaKeyTap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */; };
|
E421ACA3221F73C4008B2449 /* MediaKeyTap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */; };
|
||||||
E421ACA4221F73C4008B2449 /* MediaKeyTap.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
E421ACA4221F73C4008B2449 /* MediaKeyTap.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42410B52241B956005ED6DF /* MPDClient+Database.swift */; };
|
|
||||||
E42A8F3B22176D6400A13ED9 /* LICENSE.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3922176D6400A13ED9 /* LICENSE.md */; };
|
E42A8F3B22176D6400A13ED9 /* LICENSE.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3922176D6400A13ED9 /* LICENSE.md */; };
|
||||||
E42A8F3C22176D6400A13ED9 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3A22176D6400A13ED9 /* README.md */; };
|
E42A8F3C22176D6400A13ED9 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3A22176D6400A13ED9 /* README.md */; };
|
||||||
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E1221CD4E200184CFC /* NSFont.swift */; };
|
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E1221CD4E200184CFC /* NSFont.swift */; };
|
||||||
E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E3221CD75D00184CFC /* NSImage.swift */; };
|
E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E3221CD75D00184CFC /* NSImage.swift */; };
|
||||||
E450AD7E222620A10091BED3 /* AlbumItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD7D222620A10091BED3 /* AlbumItem.swift */; };
|
|
||||||
E450AD8622262AE60091BED3 /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E450AD8522262AE60091BED3 /* SwiftyJSON.framework */; };
|
|
||||||
E450AD8822262AEC0091BED3 /* SwiftyJSON.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E450AD8522262AE60091BED3 /* SwiftyJSON.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
|
||||||
E450AD8F22262C620091BED3 /* PromiseKit.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD8E22262C620091BED3 /* PromiseKit.framework.dSYM */; };
|
|
||||||
E450AD9122262C780091BED3 /* SwiftyJSON.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD9022262C780091BED3 /* SwiftyJSON.framework.dSYM */; };
|
|
||||||
E450AD9222262C970091BED3 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E450AD8C22262C590091BED3 /* PromiseKit.framework */; };
|
|
||||||
E450AD9322262C970091BED3 /* PromiseKit.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E450AD8C22262C590091BED3 /* PromiseKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
|
||||||
E450AD9522262DF10091BED3 /* AlbumArtQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD9422262DF10091BED3 /* AlbumArtQueue.swift */; };
|
|
||||||
E450AD98222633920091BED3 /* Alamofire.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD96222633920091BED3 /* Alamofire.framework.dSYM */; };
|
|
||||||
E450AD9D2229B9050091BED3 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD9C2229B9050091BED3 /* String.swift */; };
|
|
||||||
E450ADA12229E7C90091BED3 /* PMKFoundation.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD9F2229E7C90091BED3 /* PMKFoundation.framework.dSYM */; };
|
|
||||||
E450ADA32229E7E00091BED3 /* PMKFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E450ADA02229E7C90091BED3 /* PMKFoundation.framework */; };
|
|
||||||
E450ADA42229E7E00091BED3 /* PMKFoundation.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E450ADA02229E7C90091BED3 /* PMKFoundation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
|
||||||
E45962C62241A78500FC1A1E /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45962C52241A78500FC1A1E /* Command.swift */; };
|
|
||||||
E465049A21E94DF500A70F4C /* WindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E465049921E94DF500A70F4C /* WindowController.swift */; };
|
E465049A21E94DF500A70F4C /* WindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E465049921E94DF500A70F4C /* WindowController.swift */; };
|
||||||
E47E2FCC2220573500F747E6 /* MediaKeyTap.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E47E2FCB2220573500F747E6 /* MediaKeyTap.framework.dSYM */; };
|
E47E2FCC2220573500F747E6 /* MediaKeyTap.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E47E2FCB2220573500F747E6 /* MediaKeyTap.framework.dSYM */; };
|
||||||
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */; };
|
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */; };
|
||||||
E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD222205D2500F747E6 /* MainWindow.swift */; };
|
E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD222205D2500F747E6 /* MainWindow.swift */; };
|
||||||
E47E2FD5222071FD00F747E6 /* AlbumViewItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD4222071FD00F747E6 /* AlbumViewItem.swift */; };
|
E47E2FD5222071FD00F747E6 /* AlbumItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD4222071FD00F747E6 /* AlbumItem.swift */; };
|
||||||
E47E2FD72220720300F747E6 /* AlbumItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD62220720300F747E6 /* AlbumItemView.swift */; };
|
E47E2FD72220720300F747E6 /* AlbumItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD62220720300F747E6 /* AlbumItemView.swift */; };
|
||||||
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FDC2220A6D100F747E6 /* Time.swift */; };
|
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FDC2220A6D100F747E6 /* Time.swift */; };
|
||||||
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */; };
|
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */; };
|
||||||
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4928E0A2218D62A001D4BEA /* CGColor.swift */; };
|
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4928E0A2218D62A001D4BEA /* CGColor.swift */; };
|
||||||
E4A642DA22090CBE00067D21 /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A642D922090CBE00067D21 /* Status.swift */; };
|
E4A642DA22090CBE00067D21 /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A642D922090CBE00067D21 /* Status.swift */; };
|
||||||
E4A83BEF2221F8CF0098FED6 /* AlbumArtPrefsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.swift */; };
|
|
||||||
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */; };
|
|
||||||
E4A83BF4222207D50098FED6 /* AlbumArtService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BF3222207D50098FED6 /* AlbumArtService.swift */; };
|
|
||||||
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; };
|
|
||||||
E4C8B53E22349002009A20F3 /* Idle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* Idle.swift */; };
|
|
||||||
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC8F2204EC7F0024217A /* Delegate.swift */; };
|
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC8F2204EC7F0024217A /* Delegate.swift */; };
|
||||||
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC912204F4B80024217A /* QueueViewController.swift */; };
|
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC912204F4B80024217A /* QueueViewController.swift */; };
|
||||||
E4E8CC942206097F0024217A /* NotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC932206097F0024217A /* NotificationsController.swift */; };
|
E4E8CC942206097F0024217A /* NotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC932206097F0024217A /* NotificationsController.swift */; };
|
||||||
@ -106,10 +75,7 @@
|
|||||||
dstSubfolderSpec = 10;
|
dstSubfolderSpec = 10;
|
||||||
files = (
|
files = (
|
||||||
E41B22C121FB6C3300D544F6 /* libmpdclient.2.dylib in Embed Libraries */,
|
E41B22C121FB6C3300D544F6 /* libmpdclient.2.dylib in Embed Libraries */,
|
||||||
E450ADA42229E7E00091BED3 /* PMKFoundation.framework in Embed Libraries */,
|
|
||||||
E421ACA4221F73C4008B2449 /* MediaKeyTap.framework in Embed Libraries */,
|
E421ACA4221F73C4008B2449 /* MediaKeyTap.framework in Embed Libraries */,
|
||||||
E450AD8822262AEC0091BED3 /* SwiftyJSON.framework in Embed Libraries */,
|
|
||||||
E450AD9322262C970091BED3 /* PromiseKit.framework in Embed Libraries */,
|
|
||||||
);
|
);
|
||||||
name = "Embed Libraries";
|
name = "Embed Libraries";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -142,7 +108,7 @@
|
|||||||
E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSUserInterfaceItemIdentifier.swift; sourceTree = "<group>"; };
|
E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSUserInterfaceItemIdentifier.swift; sourceTree = "<group>"; };
|
||||||
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawRepresentable.swift; sourceTree = "<group>"; };
|
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawRepresentable.swift; sourceTree = "<group>"; };
|
||||||
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumViewController.swift; sourceTree = "<group>"; };
|
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumViewController.swift; sourceTree = "<group>"; };
|
||||||
E408D3C9220E341D0006D9BE /* AlbumViewItem.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AlbumViewItem.xib; sourceTree = "<group>"; };
|
E408D3C9220E341D0006D9BE /* AlbumItem.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AlbumItem.xib; sourceTree = "<group>"; };
|
||||||
E40F41F2221EDE27004B6CB8 /* Preferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
E40F41F2221EDE27004B6CB8 /* Preferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
||||||
E40FE71A221B904300A4223F /* NSEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEvent.swift; sourceTree = "<group>"; };
|
E40FE71A221B904300A4223F /* NSEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEvent.swift; sourceTree = "<group>"; };
|
||||||
E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libmpdclient.2.dylib; path = libmpdclient/output/libmpdclient.2.dylib; sourceTree = "<group>"; };
|
E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libmpdclient.2.dylib; path = libmpdclient/output/libmpdclient.2.dylib; sourceTree = "<group>"; };
|
||||||
@ -183,52 +149,22 @@
|
|||||||
E41B22E921FB966C00D544F6 /* capabilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = capabilities.h; sourceTree = "<group>"; };
|
E41B22E921FB966C00D544F6 /* capabilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = capabilities.h; sourceTree = "<group>"; };
|
||||||
E41B22EA21FB966C00D544F6 /* queue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = queue.h; sourceTree = "<group>"; };
|
E41B22EA21FB966C00D544F6 /* queue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = queue.h; sourceTree = "<group>"; };
|
||||||
E41B22EB21FB966C00D544F6 /* playlist.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = playlist.h; sourceTree = "<group>"; };
|
E41B22EB21FB966C00D544F6 /* playlist.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = playlist.h; sourceTree = "<group>"; };
|
||||||
E41E52FC223BF87300173814 /* MPDClient+Connection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Connection.swift"; sourceTree = "<group>"; };
|
E41EA46B221636AF0068EF46 /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = "<group>"; };
|
||||||
E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Transport.swift"; sourceTree = "<group>"; };
|
|
||||||
E41E5300223BF99300173814 /* MPDClient+Queue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Queue.swift"; sourceTree = "<group>"; };
|
|
||||||
E41E5302223BF9C300173814 /* MPDClient+Idle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Idle.swift"; sourceTree = "<group>"; };
|
|
||||||
E41E5304223BFB0700173814 /* MPDClient+Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Error.swift"; sourceTree = "<group>"; };
|
|
||||||
E41E5306223C019100173814 /* MPDClient+Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Status.swift"; sourceTree = "<group>"; };
|
|
||||||
E41E5308223C020400173814 /* MPDClient+Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Command.swift"; sourceTree = "<group>"; };
|
|
||||||
E41E530A223C033700173814 /* MPDClient+Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Album.swift"; sourceTree = "<group>"; };
|
|
||||||
E41E530D223EF4CF00173814 /* AlbumArtService+Caching.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AlbumArtService+Caching.swift"; sourceTree = "<group>"; };
|
|
||||||
E41E530F223EF6CE00173814 /* AlbumArtService+Remote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AlbumArtService+Remote.swift"; sourceTree = "<group>"; };
|
|
||||||
E41E5311223EF74A00173814 /* AlbumArtService+Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AlbumArtService+Filesystem.swift"; sourceTree = "<group>"; };
|
|
||||||
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPrefsViewController.swift; sourceTree = "<group>"; };
|
|
||||||
E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaKeyTap.framework; path = Carthage/Build/Mac/MediaKeyTap.framework; sourceTree = "<group>"; };
|
E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaKeyTap.framework; path = Carthage/Build/Mac/MediaKeyTap.framework; sourceTree = "<group>"; };
|
||||||
E42410B52241B956005ED6DF /* MPDClient+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Database.swift"; sourceTree = "<group>"; };
|
|
||||||
E42A8F3922176D6400A13ED9 /* LICENSE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; 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>"; };
|
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>"; };
|
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>"; };
|
E435E3E3221CD75D00184CFC /* NSImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSImage.swift; sourceTree = "<group>"; };
|
||||||
E450AD7D222620A10091BED3 /* AlbumItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumItem.swift; sourceTree = "<group>"; };
|
|
||||||
E450AD8522262AE60091BED3 /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = Carthage/Build/Mac/SwiftyJSON.framework; sourceTree = "<group>"; };
|
|
||||||
E450AD8C22262C590091BED3 /* PromiseKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PromiseKit.framework; path = Carthage/Build/Mac/PromiseKit.framework; sourceTree = "<group>"; };
|
|
||||||
E450AD8E22262C620091BED3 /* PromiseKit.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = PromiseKit.framework.dSYM; path = Carthage/Build/Mac/PromiseKit.framework.dSYM; sourceTree = "<group>"; };
|
|
||||||
E450AD9022262C780091BED3 /* SwiftyJSON.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = SwiftyJSON.framework.dSYM; path = Carthage/Build/Mac/SwiftyJSON.framework.dSYM; sourceTree = "<group>"; };
|
|
||||||
E450AD9422262DF10091BED3 /* AlbumArtQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumArtQueue.swift; sourceTree = "<group>"; };
|
|
||||||
E450AD96222633920091BED3 /* Alamofire.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = Alamofire.framework.dSYM; path = Carthage/Build/Mac/Alamofire.framework.dSYM; sourceTree = "<group>"; };
|
|
||||||
E450AD97222633920091BED3 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/Mac/Alamofire.framework; sourceTree = "<group>"; };
|
|
||||||
E450AD9C2229B9050091BED3 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
|
||||||
E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PersephoneBridgingHeader.h; sourceTree = "<group>"; };
|
|
||||||
E450AD9F2229E7C90091BED3 /* PMKFoundation.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = PMKFoundation.framework.dSYM; path = Carthage/Build/Mac/PMKFoundation.framework.dSYM; sourceTree = "<group>"; };
|
|
||||||
E450ADA02229E7C90091BED3 /* PMKFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PMKFoundation.framework; path = Carthage/Build/Mac/PMKFoundation.framework; sourceTree = "<group>"; };
|
|
||||||
E45962C52241A78500FC1A1E /* Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
|
|
||||||
E465049921E94DF500A70F4C /* WindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowController.swift; sourceTree = "<group>"; };
|
E465049921E94DF500A70F4C /* WindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowController.swift; sourceTree = "<group>"; };
|
||||||
E47E2FCB2220573500F747E6 /* MediaKeyTap.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = MediaKeyTap.framework.dSYM; path = Carthage/Build/Mac/MediaKeyTap.framework.dSYM; sourceTree = "<group>"; };
|
E47E2FCB2220573500F747E6 /* MediaKeyTap.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = MediaKeyTap.framework.dSYM; path = Carthage/Build/Mac/MediaKeyTap.framework.dSYM; sourceTree = "<group>"; };
|
||||||
E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSplitViewController.swift; sourceTree = "<group>"; };
|
E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSplitViewController.swift; sourceTree = "<group>"; };
|
||||||
E47E2FD222205D2500F747E6 /* MainWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = "<group>"; };
|
E47E2FD222205D2500F747E6 /* MainWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainWindow.swift; sourceTree = "<group>"; };
|
||||||
E47E2FD4222071FD00F747E6 /* AlbumViewItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumViewItem.swift; sourceTree = "<group>"; };
|
E47E2FD4222071FD00F747E6 /* AlbumItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumItem.swift; sourceTree = "<group>"; };
|
||||||
E47E2FD62220720300F747E6 /* AlbumItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumItemView.swift; sourceTree = "<group>"; };
|
E47E2FD62220720300F747E6 /* AlbumItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumItemView.swift; sourceTree = "<group>"; };
|
||||||
E47E2FDC2220A6D100F747E6 /* Time.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Time.swift; sourceTree = "<group>"; };
|
E47E2FDC2220A6D100F747E6 /* Time.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Time.swift; sourceTree = "<group>"; };
|
||||||
E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumViewLayout.swift; sourceTree = "<group>"; };
|
E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumViewLayout.swift; sourceTree = "<group>"; };
|
||||||
E4928E0A2218D62A001D4BEA /* CGColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGColor.swift; sourceTree = "<group>"; };
|
E4928E0A2218D62A001D4BEA /* CGColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGColor.swift; sourceTree = "<group>"; };
|
||||||
E4A642D922090CBE00067D21 /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = "<group>"; };
|
E4A642D922090CBE00067D21 /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = "<group>"; };
|
||||||
E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumArtPrefsController.swift; sourceTree = "<group>"; };
|
|
||||||
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = "<group>"; };
|
|
||||||
E4A83BF3222207D50098FED6 /* AlbumArtService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumArtService.swift; sourceTree = "<group>"; };
|
|
||||||
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
|
||||||
E4C8B53D22349002009A20F3 /* Idle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Idle.swift; sourceTree = "<group>"; };
|
|
||||||
E4E8CC8F2204EC7F0024217A /* Delegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delegate.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>"; };
|
E4E8CC912204F4B80024217A /* QueueViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueViewController.swift; sourceTree = "<group>"; };
|
||||||
E4E8CC932206097F0024217A /* NotificationsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsController.swift; sourceTree = "<group>"; };
|
E4E8CC932206097F0024217A /* NotificationsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsController.swift; sourceTree = "<group>"; };
|
||||||
@ -247,9 +183,6 @@
|
|||||||
files = (
|
files = (
|
||||||
E41B22C021FB6BBA00D544F6 /* libmpdclient.2.dylib in Frameworks */,
|
E41B22C021FB6BBA00D544F6 /* libmpdclient.2.dylib in Frameworks */,
|
||||||
E421ACA3221F73C4008B2449 /* MediaKeyTap.framework in Frameworks */,
|
E421ACA3221F73C4008B2449 /* MediaKeyTap.framework in Frameworks */,
|
||||||
E450AD8622262AE60091BED3 /* SwiftyJSON.framework in Frameworks */,
|
|
||||||
E450ADA32229E7E00091BED3 /* PMKFoundation.framework in Frameworks */,
|
|
||||||
E450AD9222262C970091BED3 /* PromiseKit.framework in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -296,10 +229,6 @@
|
|||||||
E407861A2110CE6E006887B1 /* Persephone */ = {
|
E407861A2110CE6E006887B1 /* Persephone */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */,
|
|
||||||
E450AD8922262B420091BED3 /* Operations */,
|
|
||||||
E4A83BF2222207BE0098FED6 /* Services */,
|
|
||||||
E4A83BEC2221F5DD0098FED6 /* Preferences */,
|
|
||||||
E47E2FE32220AA0700F747E6 /* Layouts */,
|
E47E2FE32220AA0700F747E6 /* Layouts */,
|
||||||
E4F6B461221E124700ACF42A /* Models */,
|
E4F6B461221E124700ACF42A /* Models */,
|
||||||
E4F6B45E221E117600ACF42A /* DataSources */,
|
E4F6B45E221E117600ACF42A /* DataSources */,
|
||||||
@ -344,7 +273,6 @@
|
|||||||
E40FE71A221B904300A4223F /* NSEvent.swift */,
|
E40FE71A221B904300A4223F /* NSEvent.swift */,
|
||||||
E435E3E1221CD4E200184CFC /* NSFont.swift */,
|
E435E3E1221CD4E200184CFC /* NSFont.swift */,
|
||||||
E435E3E3221CD75D00184CFC /* NSImage.swift */,
|
E435E3E3221CD75D00184CFC /* NSImage.swift */,
|
||||||
E450AD9C2229B9050091BED3 /* String.swift */,
|
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -352,15 +280,6 @@
|
|||||||
E408D3BC220E03D20006D9BE /* Extensions */ = {
|
E408D3BC220E03D20006D9BE /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E41E52FC223BF87300173814 /* MPDClient+Connection.swift */,
|
|
||||||
E42410B52241B956005ED6DF /* MPDClient+Database.swift */,
|
|
||||||
E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */,
|
|
||||||
E41E5300223BF99300173814 /* MPDClient+Queue.swift */,
|
|
||||||
E41E5302223BF9C300173814 /* MPDClient+Idle.swift */,
|
|
||||||
E41E5304223BFB0700173814 /* MPDClient+Error.swift */,
|
|
||||||
E41E5306223C019100173814 /* MPDClient+Status.swift */,
|
|
||||||
E41E5308223C020400173814 /* MPDClient+Command.swift */,
|
|
||||||
E41E530A223C033700173814 /* MPDClient+Album.swift */,
|
|
||||||
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */,
|
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
@ -378,16 +297,8 @@
|
|||||||
E41B22BE21FB6B3300D544F6 /* Frameworks */ = {
|
E41B22BE21FB6B3300D544F6 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E450ADA02229E7C90091BED3 /* PMKFoundation.framework */,
|
|
||||||
E450AD9F2229E7C90091BED3 /* PMKFoundation.framework.dSYM */,
|
|
||||||
E450AD97222633920091BED3 /* Alamofire.framework */,
|
|
||||||
E450AD96222633920091BED3 /* Alamofire.framework.dSYM */,
|
|
||||||
E450AD8C22262C590091BED3 /* PromiseKit.framework */,
|
|
||||||
E450AD8E22262C620091BED3 /* PromiseKit.framework.dSYM */,
|
|
||||||
E450AD8522262AE60091BED3 /* SwiftyJSON.framework */,
|
|
||||||
E450AD9022262C780091BED3 /* SwiftyJSON.framework.dSYM */,
|
|
||||||
E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */,
|
|
||||||
E47E2FCB2220573500F747E6 /* MediaKeyTap.framework.dSYM */,
|
E47E2FCB2220573500F747E6 /* MediaKeyTap.framework.dSYM */,
|
||||||
|
E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */,
|
||||||
E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */,
|
E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */,
|
||||||
);
|
);
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
@ -444,24 +355,6 @@
|
|||||||
path = mpd;
|
path = mpd;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
E41E530C223EF4BA00173814 /* Extensions */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
E41E530D223EF4CF00173814 /* AlbumArtService+Caching.swift */,
|
|
||||||
E41E530F223EF6CE00173814 /* AlbumArtService+Remote.swift */,
|
|
||||||
E41E5311223EF74A00173814 /* AlbumArtService+Filesystem.swift */,
|
|
||||||
);
|
|
||||||
path = Extensions;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
E450AD8922262B420091BED3 /* Operations */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
E450AD9422262DF10091BED3 /* AlbumArtQueue.swift */,
|
|
||||||
);
|
|
||||||
path = Operations;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
E47E2FE32220AA0700F747E6 /* Layouts */ = {
|
E47E2FE32220AA0700F747E6 /* Layouts */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -473,42 +366,14 @@
|
|||||||
E4A642DB220912FA00067D21 /* MPDClient */ = {
|
E4A642DB220912FA00067D21 /* MPDClient */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E41B22C521FB932700D544F6 /* MPDClient.swift */,
|
|
||||||
E408D3BC220E03D20006D9BE /* Extensions */,
|
E408D3BC220E03D20006D9BE /* Extensions */,
|
||||||
|
E41B22C521FB932700D544F6 /* MPDClient.swift */,
|
||||||
E4D1B595220BA27C0026F233 /* Protocols */,
|
E4D1B595220BA27C0026F233 /* Protocols */,
|
||||||
E4D1B594220BA2490026F233 /* Models */,
|
E4D1B594220BA2490026F233 /* Models */,
|
||||||
);
|
);
|
||||||
path = MPDClient;
|
path = MPDClient;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
E4A83BEC2221F5DD0098FED6 /* Preferences */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
E4A83BED2221F5E60098FED6 /* Controllers */,
|
|
||||||
);
|
|
||||||
path = Preferences;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
E4A83BED2221F5E60098FED6 /* Controllers */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */,
|
|
||||||
E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.swift */,
|
|
||||||
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */,
|
|
||||||
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */,
|
|
||||||
);
|
|
||||||
path = Controllers;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
E4A83BF2222207BE0098FED6 /* Services */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
E41E530C223EF4BA00173814 /* Extensions */,
|
|
||||||
E4A83BF3222207D50098FED6 /* AlbumArtService.swift */,
|
|
||||||
);
|
|
||||||
path = Services;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
E4D1B594220BA2490026F233 /* Models */ = {
|
E4D1B594220BA2490026F233 /* Models */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -516,8 +381,6 @@
|
|||||||
E4A642D922090CBE00067D21 /* Status.swift */,
|
E4A642D922090CBE00067D21 /* Status.swift */,
|
||||||
E4EB2378220F10B8008C70C0 /* Pair.swift */,
|
E4EB2378220F10B8008C70C0 /* Pair.swift */,
|
||||||
E4EB237A220F7CF1008C70C0 /* Album.swift */,
|
E4EB237A220F7CF1008C70C0 /* Album.swift */,
|
||||||
E4C8B53D22349002009A20F3 /* Idle.swift */,
|
|
||||||
E45962C52241A78500FC1A1E /* Command.swift */,
|
|
||||||
);
|
);
|
||||||
path = Models;
|
path = Models;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -533,8 +396,9 @@
|
|||||||
E4D1B597220BA3A20026F233 /* Controllers */ = {
|
E4D1B597220BA3A20026F233 /* Controllers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E47E2FD4222071FD00F747E6 /* AlbumViewItem.swift */,
|
E47E2FD4222071FD00F747E6 /* AlbumItem.swift */,
|
||||||
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */,
|
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */,
|
||||||
|
E41EA46B221636AF0068EF46 /* PreferencesViewController.swift */,
|
||||||
E4E8CC932206097F0024217A /* NotificationsController.swift */,
|
E4E8CC932206097F0024217A /* NotificationsController.swift */,
|
||||||
E4E8CC912204F4B80024217A /* QueueViewController.swift */,
|
E4E8CC912204F4B80024217A /* QueueViewController.swift */,
|
||||||
E465049921E94DF500A70F4C /* WindowController.swift */,
|
E465049921E94DF500A70F4C /* WindowController.swift */,
|
||||||
@ -547,7 +411,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E40786212110CE70006887B1 /* Main.storyboard */,
|
E40786212110CE70006887B1 /* Main.storyboard */,
|
||||||
E408D3C9220E341D0006D9BE /* AlbumViewItem.xib */,
|
E408D3C9220E341D0006D9BE /* AlbumItem.xib */,
|
||||||
);
|
);
|
||||||
path = Resources;
|
path = Resources;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -567,7 +431,6 @@
|
|||||||
E47E2FDC2220A6D100F747E6 /* Time.swift */,
|
E47E2FDC2220A6D100F747E6 /* Time.swift */,
|
||||||
E40F41F2221EDE27004B6CB8 /* Preferences.swift */,
|
E40F41F2221EDE27004B6CB8 /* Preferences.swift */,
|
||||||
E4F6B462221E125900ACF42A /* SongItem.swift */,
|
E4F6B462221E125900ACF42A /* SongItem.swift */,
|
||||||
E450AD7D222620A10091BED3 /* AlbumItem.swift */,
|
|
||||||
);
|
);
|
||||||
path = Models;
|
path = Models;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -687,15 +550,11 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
E450AD9122262C780091BED3 /* SwiftyJSON.framework.dSYM in Resources */,
|
|
||||||
E42A8F3B22176D6400A13ED9 /* LICENSE.md in Resources */,
|
E42A8F3B22176D6400A13ED9 /* LICENSE.md in Resources */,
|
||||||
E450AD98222633920091BED3 /* Alamofire.framework.dSYM in Resources */,
|
|
||||||
E40786202110CE70006887B1 /* Assets.xcassets in Resources */,
|
E40786202110CE70006887B1 /* Assets.xcassets in Resources */,
|
||||||
E42A8F3C22176D6400A13ED9 /* README.md in Resources */,
|
E42A8F3C22176D6400A13ED9 /* README.md in Resources */,
|
||||||
E450AD8F22262C620091BED3 /* PromiseKit.framework.dSYM in Resources */,
|
E408D3CB220E341D0006D9BE /* AlbumItem.xib in Resources */,
|
||||||
E408D3CB220E341D0006D9BE /* AlbumViewItem.xib in Resources */,
|
|
||||||
E40786232110CE70006887B1 /* Main.storyboard in Resources */,
|
E40786232110CE70006887B1 /* Main.storyboard in Resources */,
|
||||||
E450ADA12229E7C90091BED3 /* PMKFoundation.framework.dSYM in Resources */,
|
|
||||||
E47E2FCC2220573500F747E6 /* MediaKeyTap.framework.dSYM in Resources */,
|
E47E2FCC2220573500F747E6 /* MediaKeyTap.framework.dSYM in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -741,55 +600,34 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
E4A83BEF2221F8CF0098FED6 /* AlbumArtPrefsController.swift in Sources */,
|
|
||||||
E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */,
|
E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */,
|
||||||
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */,
|
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */,
|
||||||
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */,
|
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */,
|
||||||
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */,
|
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */,
|
||||||
E4C8B53E22349002009A20F3 /* Idle.swift in Sources */,
|
|
||||||
E4F6B460221E119B00ACF42A /* QueueDataSource.swift in Sources */,
|
E4F6B460221E119B00ACF42A /* QueueDataSource.swift in Sources */,
|
||||||
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */,
|
|
||||||
E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */,
|
|
||||||
E41E5310223EF6CE00173814 /* AlbumArtService+Remote.swift in Sources */,
|
|
||||||
E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */,
|
|
||||||
E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */,
|
|
||||||
E4A642DA22090CBE00067D21 /* Status.swift in Sources */,
|
E4A642DA22090CBE00067D21 /* Status.swift in Sources */,
|
||||||
E47E2FD72220720300F747E6 /* AlbumItemView.swift in Sources */,
|
E47E2FD72220720300F747E6 /* AlbumItemView.swift in Sources */,
|
||||||
E450AD9522262DF10091BED3 /* AlbumArtQueue.swift in Sources */,
|
|
||||||
E41E52FD223BF87300173814 /* MPDClient+Connection.swift in Sources */,
|
|
||||||
E450AD7E222620A10091BED3 /* AlbumItem.swift in Sources */,
|
|
||||||
E4E8CC942206097F0024217A /* NotificationsController.swift in Sources */,
|
E4E8CC942206097F0024217A /* NotificationsController.swift in Sources */,
|
||||||
E408D3B6220DD8970006D9BE /* Notification.swift in Sources */,
|
E408D3B6220DD8970006D9BE /* Notification.swift in Sources */,
|
||||||
E45962C62241A78500FC1A1E /* Command.swift in Sources */,
|
|
||||||
E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */,
|
E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */,
|
||||||
E4EB2379220F10B8008C70C0 /* Pair.swift in Sources */,
|
E4EB2379220F10B8008C70C0 /* Pair.swift in Sources */,
|
||||||
E4F6B463221E125900ACF42A /* SongItem.swift in Sources */,
|
E4F6B463221E125900ACF42A /* SongItem.swift in Sources */,
|
||||||
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */,
|
|
||||||
E465049A21E94DF500A70F4C /* WindowController.swift in Sources */,
|
E465049A21E94DF500A70F4C /* WindowController.swift in Sources */,
|
||||||
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */,
|
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */,
|
||||||
E40F41F3221EDE27004B6CB8 /* Preferences.swift in Sources */,
|
E40F41F3221EDE27004B6CB8 /* Preferences.swift in Sources */,
|
||||||
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */,
|
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */,
|
||||||
E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */,
|
E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */,
|
||||||
E41E5309223C020400173814 /* MPDClient+Command.swift in Sources */,
|
|
||||||
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */,
|
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */,
|
||||||
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */,
|
|
||||||
E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */,
|
E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */,
|
||||||
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */,
|
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */,
|
||||||
E4E8CC9A22075D370024217A /* Song.swift in Sources */,
|
E4E8CC9A22075D370024217A /* Song.swift in Sources */,
|
||||||
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */,
|
E41EA46C221636AF0068EF46 /* PreferencesViewController.swift in Sources */,
|
||||||
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */,
|
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */,
|
||||||
E4A83BF4222207D50098FED6 /* AlbumArtService.swift in Sources */,
|
E47E2FD5222071FD00F747E6 /* AlbumItem.swift in Sources */,
|
||||||
E47E2FD5222071FD00F747E6 /* AlbumViewItem.swift in Sources */,
|
|
||||||
E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */,
|
E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */,
|
||||||
E41E530E223EF4CF00173814 /* AlbumArtService+Caching.swift in Sources */,
|
|
||||||
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */,
|
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */,
|
||||||
E41E5312223EF74A00173814 /* AlbumArtService+Filesystem.swift in Sources */,
|
|
||||||
E41E5301223BF99300173814 /* MPDClient+Queue.swift in Sources */,
|
|
||||||
E4EB237B220F7CF1008C70C0 /* Album.swift in Sources */,
|
E4EB237B220F7CF1008C70C0 /* Album.swift in Sources */,
|
||||||
E450AD9D2229B9050091BED3 /* String.swift in Sources */,
|
|
||||||
E41E5303223BF9C300173814 /* MPDClient+Idle.swift in Sources */,
|
|
||||||
E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */,
|
E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */,
|
||||||
E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */,
|
|
||||||
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */,
|
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -893,7 +731,6 @@
|
|||||||
ONLY_ACTIVE_ARCH = YES;
|
ONLY_ACTIVE_ARCH = YES;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = Persephone/PersephoneBridgingHeader.h;
|
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
};
|
};
|
||||||
name = Debug;
|
name = Debug;
|
||||||
@ -947,7 +784,6 @@
|
|||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
MTL_ENABLE_DEBUG_INFO = NO;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
SWIFT_COMPILATION_MODE = wholemodule;
|
SWIFT_COMPILATION_MODE = wholemodule;
|
||||||
SWIFT_OBJC_BRIDGING_HEADER = Persephone/PersephoneBridgingHeader.h;
|
|
||||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
|
|||||||
@ -1,6 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Workspace
|
<Workspace
|
||||||
version = "1.0">
|
version = "1.0">
|
||||||
|
<FileRef
|
||||||
|
location = "group:../README.md">
|
||||||
|
</FileRef>
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "self:">
|
location = "self:">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
|||||||
@ -17,7 +17,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate {
|
|||||||
static let mpdClient = MPDClient(
|
static let mpdClient = MPDClient(
|
||||||
withDelegate: NotificationsController()
|
withDelegate: NotificationsController()
|
||||||
)
|
)
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
connect()
|
connect()
|
||||||
|
|
||||||
@ -26,13 +26,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate {
|
|||||||
|
|
||||||
mediaKeyTap = MediaKeyTap(delegate: self)
|
mediaKeyTap = MediaKeyTap(delegate: self)
|
||||||
mediaKeyTap?.start()
|
mediaKeyTap?.start()
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
self,
|
|
||||||
selector: #selector(enableUpdateDatabaseMenuItem),
|
|
||||||
name: Notification.databaseUpdateFinished,
|
|
||||||
object: AppDelegate.mpdClient
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillTerminate(_ aNotification: Notification) {
|
func applicationWillTerminate(_ aNotification: Notification) {
|
||||||
@ -75,15 +68,4 @@ class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate {
|
|||||||
func disconnect() {
|
func disconnect() {
|
||||||
AppDelegate.mpdClient.disconnect()
|
AppDelegate.mpdClient.disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func updateDatabase(_ sender: NSMenuItem) {
|
|
||||||
sender.isEnabled = false
|
|
||||||
AppDelegate.mpdClient.updateDatabase()
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func enableUpdateDatabaseMenuItem() {
|
|
||||||
updateDatabaseMenuItem?.isEnabled = true
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBOutlet weak var updateDatabaseMenuItem: NSMenuItem!
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -32,4 +32,4 @@
|
|||||||
"template-rendering-intent" : "original",
|
"template-rendering-intent" : "original",
|
||||||
"preserves-vector-representation" : true
|
"preserves-vector-representation" : true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "coverArtPreferencesIcon.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "coverArtPreferencesIcon@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
@ -1,5 +1,5 @@
|
|||||||
//
|
//
|
||||||
// AlbumViewItem.swift
|
// AlbumItem.swift
|
||||||
// Persephone
|
// Persephone
|
||||||
//
|
//
|
||||||
// Created by Daniel Barber on 2019/2/08.
|
// Created by Daniel Barber on 2019/2/08.
|
||||||
@ -8,9 +8,9 @@
|
|||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class AlbumViewItem: NSCollectionViewItem {
|
class AlbumItem: NSCollectionViewItem {
|
||||||
var observer: NSKeyValueObservation?
|
var observer: NSKeyValueObservation?
|
||||||
var album: AlbumItem?
|
var album: MPDClient.Album?
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
@ -27,16 +27,10 @@ class AlbumViewItem: NSCollectionViewItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setAlbum(_ album: AlbumItem) {
|
func setAlbum(_ album: MPDClient.Album) {
|
||||||
self.album = album
|
self.album = album
|
||||||
albumTitle.stringValue = album.title
|
albumTitle.stringValue = album.title
|
||||||
albumArtist.stringValue = album.artist
|
albumArtist.stringValue = album.artist
|
||||||
|
|
||||||
if let coverArt = album.coverArt {
|
|
||||||
albumCoverView.image = coverArt
|
|
||||||
} else {
|
|
||||||
albumCoverView.image = .defaultCoverArt
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setAppearance() {
|
func setAppearance() {
|
||||||
@ -53,7 +47,7 @@ class AlbumViewItem: NSCollectionViewItem {
|
|||||||
@IBAction func playAlbum(_ sender: Any) {
|
@IBAction func playAlbum(_ sender: Any) {
|
||||||
guard let album = album else { return }
|
guard let album = album else { return }
|
||||||
|
|
||||||
AppDelegate.mpdClient.playAlbum(album.album)
|
AppDelegate.mpdClient.playAlbum(album)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBOutlet var albumCoverView: NSImageView!
|
@IBOutlet var albumCoverView: NSImageView!
|
||||||
@ -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,25 +53,11 @@ 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 }
|
||||||
|
|
||||||
dataSource.albums = albums.map { AlbumItem(album: $0, coverArt: nil) }
|
dataSource.albums = albums
|
||||||
albumCollectionView.reloadData()
|
albumCollectionView.reloadData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,14 +36,6 @@ class NotificationsController: MPDClientDelegate {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func willStartDatabaseUpdate(mpdClient: MPDClient) {
|
|
||||||
sendNotification(name: Notification.databaseUpdateStarted)
|
|
||||||
}
|
|
||||||
|
|
||||||
func didFinishDatabaseUpdate(mpdClient: MPDClient) {
|
|
||||||
sendNotification(name: Notification.databaseUpdateFinished)
|
|
||||||
}
|
|
||||||
|
|
||||||
func didUpdateQueue(mpdClient: MPDClient, queue: [MPDClient.Song]) {
|
func didUpdateQueue(mpdClient: MPDClient, queue: [MPDClient.Song]) {
|
||||||
sendNotification(
|
sendNotification(
|
||||||
name: Notification.queueChanged,
|
name: Notification.queueChanged,
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class GeneralPrefsViewController: NSViewController {
|
class PreferencesViewController: NSViewController {
|
||||||
var preferences = Preferences()
|
var preferences = Preferences()
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
@ -21,16 +21,6 @@ class GeneralPrefsViewController: NSViewController {
|
|||||||
if let mpdPort = preferences.mpdPort {
|
if let mpdPort = preferences.mpdPort {
|
||||||
mpdPortField.stringValue = "\(mpdPort)"
|
mpdPortField.stringValue = "\(mpdPort)"
|
||||||
}
|
}
|
||||||
|
|
||||||
preferredContentSize = NSMakeSize(view.frame.size.width, view.frame.size.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidAppear() {
|
|
||||||
super.viewDidAppear()
|
|
||||||
|
|
||||||
guard let title = title
|
|
||||||
else { return }
|
|
||||||
self.parent?.view.window?.title = title
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func updateMpdHost(_ sender: NSTextField) {
|
@IBAction func updateMpdHost(_ sender: NSTextField) {
|
||||||
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -36,20 +36,6 @@ class WindowController: NSWindowController {
|
|||||||
object: AppDelegate.mpdClient
|
object: AppDelegate.mpdClient
|
||||||
)
|
)
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
self,
|
|
||||||
selector: #selector(startDatabaseUpdatingIndicator),
|
|
||||||
name: Notification.databaseUpdateStarted,
|
|
||||||
object: AppDelegate.mpdClient
|
|
||||||
)
|
|
||||||
|
|
||||||
NotificationCenter.default.addObserver(
|
|
||||||
self,
|
|
||||||
selector: #selector(stopDatabaseUpdatingIndicator),
|
|
||||||
name: Notification.databaseUpdateFinished,
|
|
||||||
object: AppDelegate.mpdClient
|
|
||||||
)
|
|
||||||
|
|
||||||
trackProgress.font = .timerFont
|
trackProgress.font = .timerFont
|
||||||
trackRemaining.font = .timerFont
|
trackRemaining.font = .timerFont
|
||||||
}
|
}
|
||||||
@ -146,14 +132,6 @@ class WindowController: NSWindowController {
|
|||||||
setTimeRemaining()
|
setTimeRemaining()
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func startDatabaseUpdatingIndicator() {
|
|
||||||
databaseUpdatingIndicator.startAnimation(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func stopDatabaseUpdatingIndicator() {
|
|
||||||
databaseUpdatingIndicator.stopAnimation(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setTimeElapsed() {
|
func setTimeElapsed() {
|
||||||
guard let elapsedTimeMs = elapsedTimeMs else { return }
|
guard let elapsedTimeMs = elapsedTimeMs else { return }
|
||||||
|
|
||||||
@ -216,5 +194,4 @@ class WindowController: NSWindowController {
|
|||||||
@IBOutlet var trackProgress: NSTextField!
|
@IBOutlet var trackProgress: NSTextField!
|
||||||
@IBOutlet var trackProgressBar: NSSlider!
|
@IBOutlet var trackProgressBar: NSSlider!
|
||||||
@IBOutlet var trackRemaining: NSTextField!
|
@IBOutlet var trackRemaining: NSTextField!
|
||||||
@IBOutlet var databaseUpdatingIndicator: NSProgressIndicator!
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,29 +9,19 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class AlbumDataSource: NSObject, NSCollectionViewDataSource {
|
class AlbumDataSource: NSObject, NSCollectionViewDataSource {
|
||||||
var albums: [AlbumItem] = []
|
var albums: [MPDClient.Album] = []
|
||||||
|
|
||||||
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
|
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||||
return albums.count
|
return albums.count
|
||||||
}
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
|
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
|
||||||
let item = collectionView.makeItem(withIdentifier: .albumViewItem, for: indexPath)
|
let item = collectionView.makeItem(withIdentifier: .albumItem, for: indexPath)
|
||||||
guard let albumViewItem = item as? AlbumViewItem else { return item }
|
guard let albumItem = item as? AlbumItem else { return item }
|
||||||
|
|
||||||
albumViewItem.view.wantsLayer = true
|
albumItem.view.wantsLayer = true
|
||||||
albumViewItem.setAlbum(albums[indexPath.item])
|
albumItem.setAlbum(albums[indexPath.item])
|
||||||
|
|
||||||
if albums[indexPath.item].coverArt == nil {
|
return albumItem
|
||||||
AlbumArtService(album: albums[indexPath.item]).fetchAlbumArt { image in
|
|
||||||
self.albums[indexPath.item].coverArt = image
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
collectionView.reloadItems(at: [indexPath])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return albumViewItem
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,6 +14,9 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
|
|||||||
|
|
||||||
var queueIcon: NSImage? = nil
|
var queueIcon: NSImage? = nil
|
||||||
|
|
||||||
|
let playIcon = NSImage(named: "playButton")
|
||||||
|
let pauseIcon = NSImage(named: "pauseButton")
|
||||||
|
|
||||||
func updateQueue(_ queue: [MPDClient.Song]) {
|
func updateQueue(_ queue: [MPDClient.Song]) {
|
||||||
queuePos = -1
|
queuePos = -1
|
||||||
|
|
||||||
@ -38,9 +41,9 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
|
|||||||
func setQueueIcon(_ state: MPDClient.Status.State) {
|
func setQueueIcon(_ state: MPDClient.Status.State) {
|
||||||
switch state {
|
switch state {
|
||||||
case .playing:
|
case .playing:
|
||||||
queueIcon = .playIcon
|
queueIcon = playIcon
|
||||||
case .paused:
|
case .paused:
|
||||||
queueIcon = .pauseIcon
|
queueIcon = pauseIcon
|
||||||
default:
|
default:
|
||||||
queueIcon = nil
|
queueIcon = nil
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,26 +11,4 @@ import Cocoa
|
|||||||
extension NSImage {
|
extension NSImage {
|
||||||
static let playIcon = NSImage(named: "playButton")
|
static let playIcon = NSImage(named: "playButton")
|
||||||
static let pauseIcon = NSImage(named: "pauseButton")
|
static let pauseIcon = NSImage(named: "pauseButton")
|
||||||
|
|
||||||
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]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,5 +16,5 @@ extension NSUserInterfaceItemIdentifier {
|
|||||||
static let queueSongArtist = NSUserInterfaceItemIdentifier("songArtistCell")
|
static let queueSongArtist = NSUserInterfaceItemIdentifier("songArtistCell")
|
||||||
static let queueSongTitle = NSUserInterfaceItemIdentifier("songTitleCell")
|
static let queueSongTitle = NSUserInterfaceItemIdentifier("songTitleCell")
|
||||||
|
|
||||||
static let albumViewItem = NSUserInterfaceItemIdentifier("AlbumViewItem")
|
static let albumItem = NSUserInterfaceItemIdentifier("AlbumItem")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -14,8 +14,6 @@ extension Notification {
|
|||||||
|
|
||||||
static let stateChanged = Name("MPDClientStateChanged")
|
static let stateChanged = Name("MPDClientStateChanged")
|
||||||
static let timeChanged = Name("MPDClientTimeChanged")
|
static let timeChanged = Name("MPDClientTimeChanged")
|
||||||
static let databaseUpdateStarted = Name("MPDClientDatabaseUpdateStarted")
|
|
||||||
static let databaseUpdateFinished = Name("MPDClientDatabaseUpdateFinished")
|
|
||||||
static let queueChanged = Name("MPDClientQueueChanged")
|
static let queueChanged = Name("MPDClientQueueChanged")
|
||||||
static let queuePosChanged = Name("MPDClientQueuePosChanged")
|
static let queuePosChanged = Name("MPDClientQueuePosChanged")
|
||||||
static let loadedAlbums = Name("MPDClientLoadedAlbums")
|
static let loadedAlbums = Name("MPDClientLoadedAlbums")
|
||||||
|
|||||||
@ -1,24 +0,0 @@
|
|||||||
//
|
|
||||||
// String.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/01.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension String {
|
|
||||||
func sha1() -> String {
|
|
||||||
let data = self.data(using: String.Encoding.utf8)!
|
|
||||||
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
|
|
||||||
|
|
||||||
data.withUnsafeBytes {
|
|
||||||
_ = CC_SHA1($0, CC_LONG(data.count), &digest)
|
|
||||||
}
|
|
||||||
|
|
||||||
let hexBytes = digest.map { String(format: "%02hhx", $0) }
|
|
||||||
|
|
||||||
return hexBytes.joined()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -17,7 +17,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.10.2a</string>
|
<string>0.8.0</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
|
|||||||
@ -1,99 +0,0 @@
|
|||||||
//
|
|
||||||
// MPDAlbum.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/15.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import mpdclient
|
|
||||||
|
|
||||||
extension MPDClient {
|
|
||||||
func fetchAllAlbums() {
|
|
||||||
enqueueCommand(command: .fetchAllAlbums)
|
|
||||||
}
|
|
||||||
|
|
||||||
func playAlbum(_ album: Album) {
|
|
||||||
enqueueCommand(command: .playAlbum, userData: ["album": album])
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAlbumURI(for album: Album, callback: @escaping (String?) -> Void) {
|
|
||||||
enqueueCommand(
|
|
||||||
command: .getAlbumURI,
|
|
||||||
priority: .low,
|
|
||||||
userData: ["album": album, "callback": callback]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendPlayAlbum(_ album: Album) {
|
|
||||||
var songs: [Song] = []
|
|
||||||
|
|
||||||
mpd_run_clear(self.connection)
|
|
||||||
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 mpdSong = mpd_recv_song(self.connection) {
|
|
||||||
songs.append(Song(mpdSong))
|
|
||||||
}
|
|
||||||
for song in songs {
|
|
||||||
mpd_run_add(self.connection, song.uri)
|
|
||||||
}
|
|
||||||
mpd_run_play_pos(self.connection, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func allAlbums() {
|
|
||||||
var albums: [Album] = []
|
|
||||||
var artist: String = ""
|
|
||||||
|
|
||||||
mpd_search_db_tags(self.connection, MPD_TAG_ALBUM)
|
|
||||||
mpd_search_add_group_tag(self.connection, MPD_TAG_ALBUM_ARTIST)
|
|
||||||
mpd_search_commit(self.connection)
|
|
||||||
|
|
||||||
while let mpdPair = mpd_recv_pair(self.connection) {
|
|
||||||
let pair = Pair(mpdPair)
|
|
||||||
|
|
||||||
switch pair.name {
|
|
||||||
case "AlbumArtist":
|
|
||||||
artist = pair.value
|
|
||||||
case "Album":
|
|
||||||
albums.append(Album(title: pair.value, artist: artist))
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
mpd_return_pair(self.connection, pair.mpdPair)
|
|
||||||
}
|
|
||||||
|
|
||||||
self.delegate?.didLoadAlbums(mpdClient: self, albums: albums)
|
|
||||||
}
|
|
||||||
|
|
||||||
func albumURI(for album: Album, callback: (String?) -> Void) {
|
|
||||||
var songURI: String?
|
|
||||||
|
|
||||||
guard isConnected else { return }
|
|
||||||
|
|
||||||
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_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_TRACK, "1")
|
|
||||||
|
|
||||||
mpd_search_commit(self.connection)
|
|
||||||
|
|
||||||
while let mpdSong = mpd_recv_song(self.connection) {
|
|
||||||
let song = Song(mpdSong)
|
|
||||||
|
|
||||||
if songURI == nil {
|
|
||||||
songURI = song.uriString
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
callback(
|
|
||||||
songURI?
|
|
||||||
.split(separator: "/")
|
|
||||||
.dropLast()
|
|
||||||
.joined(separator: "/")
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,79 +0,0 @@
|
|||||||
//
|
|
||||||
// CommandQueue.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/15.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension MPDClient {
|
|
||||||
func sendCommand(
|
|
||||||
command: Command,
|
|
||||||
userData: Dictionary<String, Any> = [:]
|
|
||||||
) {
|
|
||||||
switch command {
|
|
||||||
|
|
||||||
// Transport commands
|
|
||||||
case .prevTrack:
|
|
||||||
sendPreviousTrack()
|
|
||||||
case .nextTrack:
|
|
||||||
sendNextTrack()
|
|
||||||
case .stop:
|
|
||||||
sendStop()
|
|
||||||
case .playPause:
|
|
||||||
sendPlay()
|
|
||||||
case .seekCurrentSong:
|
|
||||||
guard let timeInSeconds = userData["timeInSeconds"] as? Float
|
|
||||||
else { return }
|
|
||||||
sendSeekCurrentSong(timeInSeconds: timeInSeconds)
|
|
||||||
|
|
||||||
// Database commands
|
|
||||||
case .updateDatabase:
|
|
||||||
sendUpdateDatabase()
|
|
||||||
|
|
||||||
// Status commands
|
|
||||||
case .fetchStatus:
|
|
||||||
sendRunStatus()
|
|
||||||
|
|
||||||
// Queue commands
|
|
||||||
case .fetchQueue:
|
|
||||||
sendFetchQueue()
|
|
||||||
case .playTrack:
|
|
||||||
guard let queuePos = userData["queuePos"] as? Int
|
|
||||||
else { return }
|
|
||||||
sendPlayTrack(at: queuePos)
|
|
||||||
|
|
||||||
// Album commands
|
|
||||||
case .fetchAllAlbums:
|
|
||||||
allAlbums()
|
|
||||||
case .playAlbum:
|
|
||||||
guard let album = userData["album"] as? Album else { return }
|
|
||||||
sendPlayAlbum(album)
|
|
||||||
case .getAlbumURI:
|
|
||||||
guard let album = userData["album"] as? Album,
|
|
||||||
let callback = userData["callback"] as? (String?) -> Void
|
|
||||||
else { return }
|
|
||||||
albumURI(for: album, callback: callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func enqueueCommand(
|
|
||||||
command: Command,
|
|
||||||
priority: BlockOperation.QueuePriority = .normal,
|
|
||||||
userData: Dictionary<String, Any> = [:]
|
|
||||||
) {
|
|
||||||
guard isConnected else { return }
|
|
||||||
|
|
||||||
noIdle()
|
|
||||||
|
|
||||||
let commandOperation = BlockOperation() { [unowned self] in
|
|
||||||
self.sendCommand(command: command, userData: userData)
|
|
||||||
|
|
||||||
self.idle()
|
|
||||||
}
|
|
||||||
commandOperation.queuePriority = priority
|
|
||||||
commandQueue.addOperation(commandOperation)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,50 +0,0 @@
|
|||||||
//
|
|
||||||
// Connection.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/15.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import mpdclient
|
|
||||||
|
|
||||||
extension MPDClient {
|
|
||||||
func connect(host: String, port: Int) {
|
|
||||||
commandQueue.addOperation { [unowned self] in
|
|
||||||
guard let connection = mpd_connection_new(host, UInt32(port), 10000),
|
|
||||||
mpd_connection_get_error(connection) == MPD_ERROR_SUCCESS
|
|
||||||
else { return }
|
|
||||||
|
|
||||||
self.isConnected = true
|
|
||||||
|
|
||||||
guard let status = mpd_run_status(connection)
|
|
||||||
else { return }
|
|
||||||
|
|
||||||
self.connection = connection
|
|
||||||
self.status = Status(status)
|
|
||||||
|
|
||||||
self.fetchQueue()
|
|
||||||
self.fetchAllAlbums()
|
|
||||||
self.idle()
|
|
||||||
|
|
||||||
self.delegate?.didConnect(mpdClient: self)
|
|
||||||
self.delegate?.didUpdateState(mpdClient: self, state: self.status!.state)
|
|
||||||
self.delegate?.didUpdateTime(mpdClient: self, total: self.status!.totalTime, elapsedMs: self.status!.elapsedTimeMs)
|
|
||||||
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
|
|
||||||
self.delegate?.didUpdateQueuePos(mpdClient: self, song: self.status!.song)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func disconnect() {
|
|
||||||
guard isConnected else { return }
|
|
||||||
|
|
||||||
noIdle()
|
|
||||||
commandQueue.addOperation { [unowned self] in
|
|
||||||
self.delegate?.willDisconnect(mpdClient: self)
|
|
||||||
|
|
||||||
mpd_connection_free(self.connection)
|
|
||||||
self.isConnected = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
//
|
|
||||||
// MPDClient+Database.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/19.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import mpdclient
|
|
||||||
|
|
||||||
extension MPDClient {
|
|
||||||
func updateDatabase() {
|
|
||||||
enqueueCommand(command: .updateDatabase)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendUpdateDatabase() {
|
|
||||||
mpd_run_update(connection, nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,24 +0,0 @@
|
|||||||
//
|
|
||||||
// Error.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/15.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import mpdclient
|
|
||||||
|
|
||||||
extension MPDClient {
|
|
||||||
func getLastErrorMessage() -> String? {
|
|
||||||
if mpd_connection_get_error(connection) == MPD_ERROR_SUCCESS {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if let errorMessage = mpd_connection_get_error_message(connection) {
|
|
||||||
return String(cString: errorMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,64 +0,0 @@
|
|||||||
//
|
|
||||||
// Idle.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/15.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import mpdclient
|
|
||||||
|
|
||||||
extension MPDClient {
|
|
||||||
func noIdle() {
|
|
||||||
if isIdle {
|
|
||||||
mpd_send_noidle(connection)
|
|
||||||
isIdle = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func idle() {
|
|
||||||
if !self.isIdle && self.commandQueue.operationCount == 1 {
|
|
||||||
mpd_send_idle(self.connection)
|
|
||||||
self.isIdle = true
|
|
||||||
|
|
||||||
let result = mpd_recv_idle(self.connection, true)
|
|
||||||
self.handleIdleResult(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleIdleResult(_ result: mpd_idle) {
|
|
||||||
isIdle = false
|
|
||||||
|
|
||||||
let mpdIdle = Idle(rawValue: result.rawValue)
|
|
||||||
|
|
||||||
if mpdIdle.contains(.database) {
|
|
||||||
self.fetchAllAlbums()
|
|
||||||
}
|
|
||||||
if mpdIdle.contains(.queue) {
|
|
||||||
self.fetchQueue()
|
|
||||||
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
|
|
||||||
}
|
|
||||||
if mpdIdle.contains(.player) {
|
|
||||||
self.fetchStatus()
|
|
||||||
|
|
||||||
if let status = self.status {
|
|
||||||
self.delegate?.didUpdateState(mpdClient: self, state: status.state)
|
|
||||||
self.delegate?.didUpdateTime(mpdClient: self, total: status.totalTime, elapsedMs: status.elapsedTimeMs)
|
|
||||||
self.delegate?.didUpdateQueuePos(mpdClient: self, song: status.song)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if mpdIdle.contains(.update) {
|
|
||||||
self.fetchStatus()
|
|
||||||
|
|
||||||
if self.status?.updating ?? false {
|
|
||||||
self.delegate?.willStartDatabaseUpdate(mpdClient: self)
|
|
||||||
} else {
|
|
||||||
self.delegate?.didFinishDatabaseUpdate(mpdClient: self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !mpdIdle.isEmpty {
|
|
||||||
self.idle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
//
|
|
||||||
// Queue.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/15.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import mpdclient
|
|
||||||
|
|
||||||
extension MPDClient {
|
|
||||||
func fetchQueue() {
|
|
||||||
sendCommand(command: .fetchQueue)
|
|
||||||
}
|
|
||||||
|
|
||||||
func playTrack(at queuePos: Int) {
|
|
||||||
enqueueCommand(command: .playTrack, userData: ["queuePos": queuePos])
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendPlayTrack(at queuePos: Int) {
|
|
||||||
mpd_run_play_pos(self.connection, UInt32(queuePos))
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendFetchQueue() {
|
|
||||||
self.queue = []
|
|
||||||
mpd_send_list_queue_meta(connection)
|
|
||||||
|
|
||||||
while let mpdSong = mpd_recv_song(connection) {
|
|
||||||
let song = Song(mpdSong)
|
|
||||||
self.queue.append(song)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
//
|
|
||||||
// Status.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/15.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import mpdclient
|
|
||||||
|
|
||||||
extension MPDClient {
|
|
||||||
func fetchStatus() {
|
|
||||||
sendCommand(command: .fetchStatus)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendRunStatus() {
|
|
||||||
guard let status = mpd_run_status(connection) else { return }
|
|
||||||
self.status = Status(status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
//
|
|
||||||
// Transport.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/15.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import mpdclient
|
|
||||||
|
|
||||||
extension MPDClient {
|
|
||||||
func playPause() {
|
|
||||||
enqueueCommand(command: .playPause)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stop() {
|
|
||||||
enqueueCommand(command: .stop)
|
|
||||||
}
|
|
||||||
|
|
||||||
func prevTrack() {
|
|
||||||
enqueueCommand(command: .prevTrack)
|
|
||||||
}
|
|
||||||
|
|
||||||
func nextTrack() {
|
|
||||||
enqueueCommand(command: .nextTrack)
|
|
||||||
}
|
|
||||||
|
|
||||||
func seekCurrentSong(timeInSeconds: Float) {
|
|
||||||
enqueueCommand(
|
|
||||||
command: .seekCurrentSong,
|
|
||||||
userData: ["timeInSeconds": timeInSeconds]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendNextTrack() {
|
|
||||||
guard let state = status?.state,
|
|
||||||
state.isOneOf([.playing, .paused])
|
|
||||||
else { return }
|
|
||||||
|
|
||||||
mpd_run_next(connection)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendPreviousTrack() {
|
|
||||||
guard let state = status?.state,
|
|
||||||
state.isOneOf([.playing, .paused])
|
|
||||||
else { return }
|
|
||||||
|
|
||||||
mpd_run_previous(connection)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendStop() {
|
|
||||||
mpd_run_stop(connection)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendPlay() {
|
|
||||||
if status?.state == .stopped {
|
|
||||||
mpd_run_play(connection)
|
|
||||||
} else {
|
|
||||||
mpd_run_toggle_pause(connection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendSeekCurrentSong(timeInSeconds: Float) {
|
|
||||||
mpd_run_seek_current(self.connection, timeInSeconds, false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -12,16 +12,285 @@ import mpdclient
|
|||||||
class MPDClient {
|
class MPDClient {
|
||||||
var delegate: MPDClientDelegate?
|
var delegate: MPDClientDelegate?
|
||||||
|
|
||||||
var connection: OpaquePointer?
|
private var connection: OpaquePointer?
|
||||||
var isConnected: Bool = false
|
private var isConnected: Bool = false
|
||||||
var isIdle: Bool = false
|
private var status: Status?
|
||||||
var status: Status?
|
private var queue: [Song] = []
|
||||||
var queue: [Song] = []
|
|
||||||
|
|
||||||
let commandQueue = OperationQueue()
|
private let commandQueue = DispatchQueue(label: "commandQueue")
|
||||||
|
|
||||||
|
enum Command {
|
||||||
|
case prevTrack, nextTrack, playPause, stop,
|
||||||
|
fetchStatus, fetchQueue, fetchAllAlbums
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Idle: OptionSet {
|
||||||
|
let rawValue: UInt32
|
||||||
|
|
||||||
|
static let database = Idle(rawValue: 0x1)
|
||||||
|
static let storedPlaylist = Idle(rawValue: 0x2)
|
||||||
|
static let queue = Idle(rawValue: 0x4)
|
||||||
|
static let player = Idle(rawValue: 0x8)
|
||||||
|
static let mixer = Idle(rawValue: 0x10)
|
||||||
|
static let output = Idle(rawValue: 0x20)
|
||||||
|
static let options = Idle(rawValue: 0x40)
|
||||||
|
static let update = Idle(rawValue: 0x80)
|
||||||
|
static let sticker = Idle(rawValue: 0x100)
|
||||||
|
static let subscription = Idle(rawValue: 0x200)
|
||||||
|
static let message = Idle(rawValue: 0x400)
|
||||||
|
}
|
||||||
|
|
||||||
init(withDelegate delegate: MPDClientDelegate?) {
|
init(withDelegate delegate: MPDClientDelegate?) {
|
||||||
commandQueue.maxConcurrentOperationCount = 1
|
|
||||||
self.delegate = delegate
|
self.delegate = delegate
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func connect(host: String, port: Int) {
|
||||||
|
commandQueue.async { [unowned self] in
|
||||||
|
guard let connection = mpd_connection_new(host, UInt32(port), 10000),
|
||||||
|
mpd_connection_get_error(connection) == MPD_ERROR_SUCCESS
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
self.isConnected = true
|
||||||
|
|
||||||
|
guard let status = mpd_run_status(connection)
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
self.connection = connection
|
||||||
|
self.status = Status(status)
|
||||||
|
|
||||||
|
self.fetchQueue()
|
||||||
|
self.fetchAllAlbums()
|
||||||
|
self.idle()
|
||||||
|
|
||||||
|
self.delegate?.didConnect(mpdClient: self)
|
||||||
|
self.delegate?.didUpdateState(mpdClient: self, state: self.status!.state)
|
||||||
|
self.delegate?.didUpdateTime(mpdClient: self, total: self.status!.totalTime, elapsedMs: self.status!.elapsedTimeMs)
|
||||||
|
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
|
||||||
|
self.delegate?.didUpdateQueuePos(mpdClient: self, song: self.status!.song)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func disconnect() {
|
||||||
|
guard isConnected else { return }
|
||||||
|
|
||||||
|
noIdle()
|
||||||
|
commandQueue.async { [unowned self] in
|
||||||
|
self.delegate?.willDisconnect(mpdClient: self)
|
||||||
|
|
||||||
|
mpd_connection_free(self.connection)
|
||||||
|
self.isConnected = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchStatus() {
|
||||||
|
sendCommand(command: .fetchStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchQueue() {
|
||||||
|
sendCommand(command: .fetchQueue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchAllAlbums() {
|
||||||
|
sendCommand(command: .fetchAllAlbums)
|
||||||
|
}
|
||||||
|
|
||||||
|
func playPause() {
|
||||||
|
queueCommand(command: .playPause)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stop() {
|
||||||
|
queueCommand(command: .stop)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prevTrack() {
|
||||||
|
queueCommand(command: .prevTrack)
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextTrack() {
|
||||||
|
queueCommand(command: .nextTrack)
|
||||||
|
}
|
||||||
|
|
||||||
|
func playTrack(queuePos: Int) {
|
||||||
|
guard isConnected else { return }
|
||||||
|
|
||||||
|
noIdle()
|
||||||
|
commandQueue.async { [unowned self] in
|
||||||
|
mpd_run_play_pos(self.connection, UInt32(queuePos))
|
||||||
|
}
|
||||||
|
idle()
|
||||||
|
}
|
||||||
|
|
||||||
|
func seekCurrentSong(timeInSeconds: Float) {
|
||||||
|
noIdle()
|
||||||
|
commandQueue.async { [unowned self] in
|
||||||
|
mpd_run_seek_current(self.connection, timeInSeconds, false)
|
||||||
|
}
|
||||||
|
idle()
|
||||||
|
}
|
||||||
|
|
||||||
|
func playAlbum(_ album: Album) {
|
||||||
|
noIdle()
|
||||||
|
commandQueue.async { [unowned self] in
|
||||||
|
var songs: [Song] = []
|
||||||
|
|
||||||
|
mpd_run_clear(self.connection)
|
||||||
|
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 mpdSong = mpd_recv_song(self.connection) {
|
||||||
|
songs.append(Song(mpdSong))
|
||||||
|
}
|
||||||
|
for song in songs {
|
||||||
|
mpd_run_add(self.connection, song.uri)
|
||||||
|
}
|
||||||
|
mpd_run_play_pos(self.connection, 0)
|
||||||
|
}
|
||||||
|
idle()
|
||||||
|
}
|
||||||
|
|
||||||
|
func queueCommand(command: Command) {
|
||||||
|
guard isConnected else { return }
|
||||||
|
|
||||||
|
noIdle()
|
||||||
|
commandQueue.async { [unowned self] in
|
||||||
|
self.sendCommand(command: command)
|
||||||
|
}
|
||||||
|
idle()
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendCommand(command: Command) {
|
||||||
|
switch command {
|
||||||
|
|
||||||
|
// Transport commands
|
||||||
|
case .prevTrack:
|
||||||
|
sendPreviousTrack()
|
||||||
|
case .nextTrack:
|
||||||
|
sendNextTrack()
|
||||||
|
case .stop:
|
||||||
|
sendStop()
|
||||||
|
case .playPause:
|
||||||
|
sendPlay()
|
||||||
|
case .fetchStatus:
|
||||||
|
sendRunStatus()
|
||||||
|
case .fetchQueue:
|
||||||
|
sendFetchQueue()
|
||||||
|
case .fetchAllAlbums:
|
||||||
|
allAlbums()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendNextTrack() {
|
||||||
|
guard let state = status?.state,
|
||||||
|
state.isOneOf([.playing, .paused])
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
mpd_run_next(connection)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendPreviousTrack() {
|
||||||
|
guard let state = status?.state,
|
||||||
|
state.isOneOf([.playing, .paused])
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
mpd_run_previous(connection)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendStop() {
|
||||||
|
mpd_run_stop(connection)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendPlay() {
|
||||||
|
if status?.state == .stopped {
|
||||||
|
mpd_run_play(connection)
|
||||||
|
} else {
|
||||||
|
mpd_run_toggle_pause(connection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendRunStatus() {
|
||||||
|
guard let status = mpd_run_status(connection) else { return }
|
||||||
|
self.status = Status(status)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendFetchQueue() {
|
||||||
|
self.queue = []
|
||||||
|
mpd_send_list_queue_meta(connection)
|
||||||
|
|
||||||
|
while let mpdSong = mpd_recv_song(connection) {
|
||||||
|
let song = Song(mpdSong)
|
||||||
|
self.queue.append(song)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func allAlbums() {
|
||||||
|
var albums: [Album] = []
|
||||||
|
var artist: String = ""
|
||||||
|
|
||||||
|
mpd_search_db_tags(connection, MPD_TAG_ALBUM)
|
||||||
|
mpd_search_add_group_tag(connection, MPD_TAG_ALBUM_ARTIST)
|
||||||
|
mpd_search_commit(connection)
|
||||||
|
while let mpdPair = mpd_recv_pair(connection) {
|
||||||
|
let pair = Pair(mpdPair)
|
||||||
|
|
||||||
|
switch pair.name {
|
||||||
|
case "AlbumArtist":
|
||||||
|
artist = pair.value
|
||||||
|
case "Album":
|
||||||
|
albums.append(Album(title: pair.value, artist: artist))
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
mpd_return_pair(connection, pair.mpdPair)
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate?.didLoadAlbums(mpdClient: self, albums: albums)
|
||||||
|
}
|
||||||
|
|
||||||
|
func noIdle() {
|
||||||
|
mpd_send_noidle(connection)
|
||||||
|
}
|
||||||
|
|
||||||
|
func idle() {
|
||||||
|
commandQueue.async { [unowned self] in
|
||||||
|
mpd_send_idle(self.connection)
|
||||||
|
|
||||||
|
let result = mpd_recv_idle(self.connection, true)
|
||||||
|
self.handleIdleResult(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleIdleResult(_ result: mpd_idle) {
|
||||||
|
let mpdIdle = Idle(rawValue: result.rawValue)
|
||||||
|
|
||||||
|
if mpdIdle.contains(.queue) {
|
||||||
|
self.fetchQueue()
|
||||||
|
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
|
||||||
|
}
|
||||||
|
if mpdIdle.contains(.player) {
|
||||||
|
self.fetchStatus()
|
||||||
|
|
||||||
|
if let status = self.status {
|
||||||
|
self.delegate?.didUpdateState(mpdClient: self, state: status.state)
|
||||||
|
self.delegate?.didUpdateTime(mpdClient: self, total: status.totalTime, elapsedMs: status.elapsedTimeMs)
|
||||||
|
self.delegate?.didUpdateQueuePos(mpdClient: self, song: status.song)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !mpdIdle.isEmpty {
|
||||||
|
self.idle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLastErrorMessage() -> String? {
|
||||||
|
if mpd_connection_get_error(connection) == MPD_ERROR_SUCCESS {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let errorMessage = mpd_connection_get_error_message(connection) {
|
||||||
|
return String(cString: errorMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,35 +0,0 @@
|
|||||||
//
|
|
||||||
// Command.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/19.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension MPDClient {
|
|
||||||
enum Command {
|
|
||||||
// Transport commands
|
|
||||||
case prevTrack
|
|
||||||
case nextTrack
|
|
||||||
case playPause
|
|
||||||
case stop
|
|
||||||
case seekCurrentSong
|
|
||||||
|
|
||||||
// Database commands
|
|
||||||
case updateDatabase
|
|
||||||
|
|
||||||
// Status commands
|
|
||||||
case fetchStatus
|
|
||||||
|
|
||||||
// Queue commands
|
|
||||||
case fetchQueue
|
|
||||||
case playTrack
|
|
||||||
|
|
||||||
// Album commands
|
|
||||||
case fetchAllAlbums
|
|
||||||
case playAlbum
|
|
||||||
case getAlbumURI
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
//
|
|
||||||
// Idle.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/09.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
extension MPDClient {
|
|
||||||
struct Idle: OptionSet {
|
|
||||||
let rawValue: UInt32
|
|
||||||
|
|
||||||
static let database = Idle(rawValue: 0x1)
|
|
||||||
static let storedPlaylist = Idle(rawValue: 0x2)
|
|
||||||
static let queue = Idle(rawValue: 0x4)
|
|
||||||
static let player = Idle(rawValue: 0x8)
|
|
||||||
static let mixer = Idle(rawValue: 0x10)
|
|
||||||
static let output = Idle(rawValue: 0x20)
|
|
||||||
static let options = Idle(rawValue: 0x40)
|
|
||||||
static let update = Idle(rawValue: 0x80)
|
|
||||||
static let sticker = Idle(rawValue: 0x100)
|
|
||||||
static let subscription = Idle(rawValue: 0x200)
|
|
||||||
static let message = Idle(rawValue: 0x400)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -45,10 +45,6 @@ extension MPDClient {
|
|||||||
return mpd_song_get_uri(mpdSong)
|
return mpd_song_get_uri(mpdSong)
|
||||||
}
|
}
|
||||||
|
|
||||||
var uriString: String {
|
|
||||||
return String(cString: uri)
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTag(_ tagType: TagType) -> String {
|
func getTag(_ tagType: TagType) -> String {
|
||||||
let mpdTagType = mpd_tag_type(rawValue: Int32(tagType.rawValue))
|
let mpdTagType = mpd_tag_type(rawValue: Int32(tagType.rawValue))
|
||||||
|
|
||||||
|
|||||||
@ -49,11 +49,5 @@ extension MPDClient {
|
|||||||
var song: Int {
|
var song: Int {
|
||||||
return Int(mpd_status_get_song_pos(mpdStatus))
|
return Int(mpd_status_get_song_pos(mpdStatus))
|
||||||
}
|
}
|
||||||
|
|
||||||
var updating: Bool {
|
|
||||||
let updating = mpd_status_get_update_id(mpdStatus)
|
|
||||||
|
|
||||||
return updating > 0
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,9 +15,6 @@ protocol MPDClientDelegate {
|
|||||||
func didUpdateState(mpdClient: MPDClient, state: MPDClient.Status.State)
|
func didUpdateState(mpdClient: MPDClient, state: MPDClient.Status.State)
|
||||||
func didUpdateTime(mpdClient: MPDClient, total: UInt, elapsedMs: UInt)
|
func didUpdateTime(mpdClient: MPDClient, total: UInt, elapsedMs: UInt)
|
||||||
|
|
||||||
func willStartDatabaseUpdate(mpdClient: MPDClient)
|
|
||||||
func didFinishDatabaseUpdate(mpdClient: MPDClient)
|
|
||||||
|
|
||||||
func didUpdateQueue(mpdClient: MPDClient, queue: [MPDClient.Song])
|
func didUpdateQueue(mpdClient: MPDClient, queue: [MPDClient.Song])
|
||||||
func didUpdateQueuePos(mpdClient: MPDClient, song: Int)
|
func didUpdateQueuePos(mpdClient: MPDClient, song: Int)
|
||||||
|
|
||||||
|
|||||||
@ -1,26 +0,0 @@
|
|||||||
//
|
|
||||||
// AlbumItem.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/2/26.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Cocoa
|
|
||||||
|
|
||||||
struct AlbumItem {
|
|
||||||
var album: MPDClient.Album
|
|
||||||
var coverArt: NSImage?
|
|
||||||
|
|
||||||
var title: String {
|
|
||||||
return album.title
|
|
||||||
}
|
|
||||||
|
|
||||||
var artist: String {
|
|
||||||
return album.artist
|
|
||||||
}
|
|
||||||
|
|
||||||
var hash: String {
|
|
||||||
return "\(title) - \(artist)".sha1()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,10 +9,6 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct Preferences {
|
struct Preferences {
|
||||||
let mpdHostDefault = "127.0.0.1"
|
|
||||||
let mpdPortDefault = 6600
|
|
||||||
let mpdLibraryDirDefault = "~/Music"
|
|
||||||
|
|
||||||
let preferences = UserDefaults.standard
|
let preferences = UserDefaults.standard
|
||||||
|
|
||||||
var mpdHost: String? {
|
var mpdHost: String? {
|
||||||
@ -24,10 +20,6 @@ struct Preferences {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var mpdHostOrDefault: String {
|
|
||||||
return mpdHost ?? mpdHostDefault
|
|
||||||
}
|
|
||||||
|
|
||||||
var mpdPort: Int? {
|
var mpdPort: Int? {
|
||||||
get {
|
get {
|
||||||
return preferences.value(forKey: "mpdPort") as? Int
|
return preferences.value(forKey: "mpdPort") as? Int
|
||||||
@ -41,34 +33,12 @@ struct Preferences {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mpdHostOrDefault: String {
|
||||||
|
return mpdHost ?? "127.0.0.1"
|
||||||
|
}
|
||||||
|
|
||||||
var mpdPortOrDefault: Int {
|
var mpdPortOrDefault: Int {
|
||||||
return mpdPort ?? mpdPortDefault
|
return mpdPort ?? 6600
|
||||||
}
|
|
||||||
|
|
||||||
var mpdLibraryDir: String? {
|
|
||||||
get {
|
|
||||||
return preferences.string(forKey: "mpdLibraryDir")
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
preferences.set(newValue, forKey: "mpdLibraryDir")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var mpdLibraryDirOrDefault: String {
|
|
||||||
return mpdLibraryDir ?? mpdLibraryDirDefault
|
|
||||||
}
|
|
||||||
|
|
||||||
var expandedMpdLibraryDir: String {
|
|
||||||
return NSString(string: mpdLibraryDirOrDefault).expandingTildeInPath
|
|
||||||
}
|
|
||||||
|
|
||||||
var fetchMissingArtworkFromInternet: Bool {
|
|
||||||
get {
|
|
||||||
return preferences.bool(forKey: "fetchMissingArtworkFromInternet")
|
|
||||||
}
|
|
||||||
set {
|
|
||||||
preferences.set(newValue, forKey: "fetchMissingArtworkFromInternet")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func addObserver(_ observer: NSObject, forKeyPath keyPath: String) {
|
func addObserver(_ observer: NSObject, forKeyPath keyPath: String) {
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
//
|
|
||||||
// AlbumArtOperationQueue.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/2/26.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Cocoa
|
|
||||||
|
|
||||||
class AlbumArtQueue {
|
|
||||||
static let shared = AlbumArtQueue()
|
|
||||||
|
|
||||||
let queue = DispatchQueue(label: "AlbumArtQueue")
|
|
||||||
var lastDispatchedTime = DispatchTime(uptimeNanoseconds: 0) - 1
|
|
||||||
|
|
||||||
func addToQueue(workItem: DispatchWorkItem) {
|
|
||||||
let dispatchTime = max(lastDispatchedTime + 1, DispatchTime(uptimeNanoseconds: 0))
|
|
||||||
lastDispatchedTime = dispatchTime
|
|
||||||
|
|
||||||
queue.asyncAfter(deadline: dispatchTime, execute: workItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,15 +0,0 @@
|
|||||||
//
|
|
||||||
// PersephoneBridgingHeader.h
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/01.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef PersephoneBridgingHeader_h
|
|
||||||
#define PersephoneBridgingHeader_h
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* PersephoneBridgingHeader_h */
|
|
||||||
|
|
||||||
#import <CommonCrypto/CommonCrypto.h>
|
|
||||||
@ -1,53 +0,0 @@
|
|||||||
//
|
|
||||||
// AlbumArtPrefsController.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/2/23.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Cocoa
|
|
||||||
|
|
||||||
class AlbumArtPrefsController: NSViewController {
|
|
||||||
var preferences = Preferences()
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
if let mpdLibraryDir = preferences.mpdLibraryDir {
|
|
||||||
mpdLibraryDirField.stringValue = mpdLibraryDir
|
|
||||||
}
|
|
||||||
|
|
||||||
if preferences.fetchMissingArtworkFromInternet {
|
|
||||||
fetchMissingArtworkFromInternet.state = .on
|
|
||||||
} else {
|
|
||||||
fetchMissingArtworkFromInternet.state = .off
|
|
||||||
}
|
|
||||||
|
|
||||||
preferredContentSize = NSMakeSize(view.frame.size.width, view.frame.size.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
override func viewDidAppear() {
|
|
||||||
super.viewDidAppear()
|
|
||||||
|
|
||||||
guard let title = title
|
|
||||||
else { return }
|
|
||||||
self.parent?.view.window?.title = title
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBAction func updateMpdLibraryDir(_ sender: NSTextField) {
|
|
||||||
preferences.mpdLibraryDir = sender.stringValue
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBOutlet var mpdLibraryDirField: NSTextField!
|
|
||||||
|
|
||||||
@IBAction func updateFetchMissingArtworkFromInternet(_ sender: NSButton) {
|
|
||||||
if sender.state == .on {
|
|
||||||
preferences.fetchMissingArtworkFromInternet = true
|
|
||||||
} else {
|
|
||||||
preferences.fetchMissingArtworkFromInternet = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBOutlet var fetchMissingArtworkFromInternet: NSButton!
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
//
|
|
||||||
// PreferencesController.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/2/23.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Cocoa
|
|
||||||
|
|
||||||
class PreferencesViewController: NSTabViewController {
|
|
||||||
private lazy var tabViewSizes: [String : NSSize] = [:]
|
|
||||||
|
|
||||||
override func viewDidLoad() {
|
|
||||||
super.viewDidLoad()
|
|
||||||
|
|
||||||
if let viewController = self.tabViewItems.first?.viewController, let title = viewController.title {
|
|
||||||
tabViewSizes[title] = viewController.view.frame.size
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override func transition(from fromViewController: NSViewController, to toViewController: NSViewController, options: NSViewController.TransitionOptions, completionHandler completion: (() -> Void)?) {
|
|
||||||
NSAnimationContext.runAnimationGroup({ context in
|
|
||||||
context.duration = 0.25
|
|
||||||
|
|
||||||
self.updateWindowFrameAnimated(viewController: toViewController)
|
|
||||||
|
|
||||||
super.transition(
|
|
||||||
from: fromViewController,
|
|
||||||
to: toViewController,
|
|
||||||
options: [.crossfade, .allowUserInteraction],
|
|
||||||
completionHandler: completion
|
|
||||||
)
|
|
||||||
}, completionHandler: nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func updateWindowFrameAnimated(viewController: NSViewController) {
|
|
||||||
guard let title = viewController.title,
|
|
||||||
let window = view.window
|
|
||||||
else { return }
|
|
||||||
|
|
||||||
let contentSize: NSSize
|
|
||||||
|
|
||||||
if tabViewSizes.keys.contains(title) {
|
|
||||||
contentSize = tabViewSizes[title]!
|
|
||||||
} else {
|
|
||||||
contentSize = viewController.view.frame.size
|
|
||||||
tabViewSizes[title] = contentSize
|
|
||||||
}
|
|
||||||
|
|
||||||
let newWindowSize = window.frameRect(forContentRect: NSRect(origin: NSPoint.zero, size: contentSize)).size
|
|
||||||
|
|
||||||
var frame = window.frame
|
|
||||||
frame.origin.y += frame.height
|
|
||||||
frame.origin.y -= newWindowSize.height
|
|
||||||
frame.size = newWindowSize
|
|
||||||
window.animator().setFrame(frame, display: false)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
//
|
|
||||||
// PreferencesWindowController.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/09.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Cocoa
|
|
||||||
|
|
||||||
class PreferencesWindowController: NSWindowController, NSWindowDelegate {
|
|
||||||
override func windowDidLoad() {
|
|
||||||
super.windowDidLoad()
|
|
||||||
}
|
|
||||||
|
|
||||||
func windowShouldClose(_ sender: NSWindow) -> Bool {
|
|
||||||
self.window?.orderOut(sender)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<objects>
|
<objects>
|
||||||
<customObject id="-2" userLabel="File's Owner" customClass="AlbumViewItem" customModule="Persephone" customModuleProvider="target">
|
<customObject id="-2" userLabel="File's Owner" customClass="AlbumItem" customModule="Persephone" customModuleProvider="target">
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="albumArtist" destination="5Uu-j1-qyT" id="2Et-tX-InT"/>
|
<outlet property="albumArtist" destination="5Uu-j1-qyT" id="2Et-tX-InT"/>
|
||||||
<outlet property="albumCoverView" destination="Kfb-8f-ean" id="CXx-gB-gz8"/>
|
<outlet property="albumCoverView" destination="Kfb-8f-ean" id="CXx-gB-gz8"/>
|
||||||
@ -76,7 +76,7 @@
|
|||||||
</connections>
|
</connections>
|
||||||
<point key="canvasLocation" x="-22" y="125.5"/>
|
<point key="canvasLocation" x="-22" y="125.5"/>
|
||||||
</customView>
|
</customView>
|
||||||
<collectionViewItem id="Qgu-aI-55A" customClass="AlbumViewItem" customModule="Persephone" customModuleProvider="target"/>
|
<collectionViewItem id="Qgu-aI-55A" customClass="AlbumItem" customModule="Persephone" customModuleProvider="target"/>
|
||||||
</objects>
|
</objects>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="blankAlbum" width="128" height="128"/>
|
<image name="blankAlbum" width="128" height="128"/>
|
||||||
@ -62,20 +62,6 @@
|
|||||||
</items>
|
</items>
|
||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Database" id="usv-UH-vkC">
|
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
|
||||||
<menu key="submenu" title="Database" autoenablesItems="NO" id="rFP-zL-1X4">
|
|
||||||
<items>
|
|
||||||
<menuItem title="Update" keyEquivalent="u" id="EJg-93-1F6">
|
|
||||||
<attributedString key="attributedTitle"/>
|
|
||||||
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="updateDatabase:" target="Voe-Tx-rLC" id="FO1-Ge-TUL"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
</items>
|
|
||||||
</menu>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem title="Window" id="aUF-d1-5bR">
|
<menuItem title="Window" id="aUF-d1-5bR">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
|
<menu key="submenu" title="Window" systemMenu="window" id="Td7-aD-5lo">
|
||||||
@ -119,11 +105,7 @@
|
|||||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
|
<outlet property="delegate" destination="Voe-Tx-rLC" id="PrD-fu-P6m"/>
|
||||||
</connections>
|
</connections>
|
||||||
</application>
|
</application>
|
||||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Persephone" customModuleProvider="target">
|
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Persephone" customModuleProvider="target"/>
|
||||||
<connections>
|
|
||||||
<outlet property="updateDatabaseMenuItem" destination="EJg-93-1F6" id="gMf-SQ-lyI"/>
|
|
||||||
</connections>
|
|
||||||
</customObject>
|
|
||||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||||
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
@ -203,17 +185,9 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
</toolbarItem>
|
</toolbarItem>
|
||||||
<toolbarItem implicitItemIdentifier="6B3CE282-B6C1-48BF-8962-1A02892D8DF8" label="" paletteLabel="" tag="-1" sizingBehavior="auto" id="fw4-Lp-bWJ">
|
|
||||||
<nil key="toolTip"/>
|
|
||||||
<progressIndicator key="view" identifier="databaseUpdatingIndicator" wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" displayedWhenStopped="NO" bezeled="NO" indeterminate="YES" controlSize="small" style="spinning" id="LpV-iM-o6t">
|
|
||||||
<rect key="frame" x="0.0" y="14" width="16" height="16"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
</progressIndicator>
|
|
||||||
</toolbarItem>
|
|
||||||
</allowedToolbarItems>
|
</allowedToolbarItems>
|
||||||
<defaultToolbarItems>
|
<defaultToolbarItems>
|
||||||
<toolbarItem reference="p3r-ty-Pxf"/>
|
<toolbarItem reference="p3r-ty-Pxf"/>
|
||||||
<toolbarItem reference="fw4-Lp-bWJ"/>
|
|
||||||
<toolbarItem reference="9ol-aR-mzv"/>
|
<toolbarItem reference="9ol-aR-mzv"/>
|
||||||
<toolbarItem reference="n52-8S-6kR"/>
|
<toolbarItem reference="n52-8S-6kR"/>
|
||||||
<toolbarItem reference="s1h-EC-nvL"/>
|
<toolbarItem reference="s1h-EC-nvL"/>
|
||||||
@ -226,7 +200,6 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</window>
|
</window>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="databaseUpdatingIndicator" destination="LpV-iM-o6t" id="y0T-eR-ygY"/>
|
|
||||||
<outlet property="trackProgress" destination="kx6-xm-TAN" id="XDv-Th-Agj"/>
|
<outlet property="trackProgress" destination="kx6-xm-TAN" id="XDv-Th-Agj"/>
|
||||||
<outlet property="trackProgressBar" destination="KMy-xf-rmN" id="a67-JU-cyQ"/>
|
<outlet property="trackProgressBar" destination="KMy-xf-rmN" id="a67-JU-cyQ"/>
|
||||||
<outlet property="trackRemaining" destination="9WZ-ij-lrb" id="0pH-d7-wvD"/>
|
<outlet property="trackRemaining" destination="9WZ-ij-lrb" id="0pH-d7-wvD"/>
|
||||||
@ -266,7 +239,7 @@
|
|||||||
<!--Window Controller-->
|
<!--Window Controller-->
|
||||||
<scene sceneID="Rpk-bo-5kf">
|
<scene sceneID="Rpk-bo-5kf">
|
||||||
<objects>
|
<objects>
|
||||||
<windowController id="xYu-7w-E5x" customClass="PreferencesWindowController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
<windowController id="xYu-7w-E5x" sceneMemberID="viewController">
|
||||||
<window key="window" title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="3FN-my-6kU">
|
<window key="window" title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="3FN-my-6kU">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||||
<rect key="contentRect" x="245" y="301" width="416" height="100"/>
|
<rect key="contentRect" x="245" y="301" width="416" height="100"/>
|
||||||
@ -276,107 +249,24 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</window>
|
</window>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="zhe-qh-Mal" kind="relationship" relationship="window.shadowedContentViewController" id="iWi-v3-HxM"/>
|
<segue destination="nYi-sw-ZNp" kind="relationship" relationship="window.shadowedContentViewController" id="607-3F-gJf"/>
|
||||||
</connections>
|
</connections>
|
||||||
</windowController>
|
</windowController>
|
||||||
<customObject id="0sd-8B-etN" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="0sd-8B-etN" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="915" y="-155"/>
|
<point key="canvasLocation" x="915" y="-89"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--General-->
|
<!--Preferences View Controller-->
|
||||||
<scene sceneID="5er-B6-hoB">
|
|
||||||
<objects>
|
|
||||||
<tabViewController title="General" selectedTabViewItemIndex="0" tabStyle="toolbar" id="zhe-qh-Mal" customClass="PreferencesViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
|
||||||
<tabViewItems>
|
|
||||||
<tabViewItem label="General" identifier="generalPreferencesTab" image="NSPreferencesGeneral" id="kn0-fa-vM3"/>
|
|
||||||
<tabViewItem label="Album Art" identifier="albumArtPreferencesTab" image="coverArtPreferencesIcon" id="4Lj-dz-bOK"/>
|
|
||||||
</tabViewItems>
|
|
||||||
<viewControllerTransitionOptions key="transitionOptions" allowUserInteraction="YES"/>
|
|
||||||
<tabView key="tabView" type="noTabsNoBorder" id="6dC-M0-oC5">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="418" height="300"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<font key="font" metaFont="message"/>
|
|
||||||
<connections>
|
|
||||||
<outlet property="delegate" destination="zhe-qh-Mal" id="LUL-qN-JlP"/>
|
|
||||||
</connections>
|
|
||||||
</tabView>
|
|
||||||
<connections>
|
|
||||||
<outlet property="tabView" destination="6dC-M0-oC5" id="jFQ-3f-s5E"/>
|
|
||||||
<segue destination="nYi-sw-ZNp" kind="relationship" relationship="tabItems" id="Jr4-ql-vhk"/>
|
|
||||||
<segue destination="3C9-vU-zjZ" kind="relationship" relationship="tabItems" id="zOQ-NT-Tof"/>
|
|
||||||
</connections>
|
|
||||||
</tabViewController>
|
|
||||||
<customObject id="XtF-QO-9W0" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="916" y="236"/>
|
|
||||||
</scene>
|
|
||||||
<!--Album Art-->
|
|
||||||
<scene sceneID="pQx-0G-WVt">
|
|
||||||
<objects>
|
|
||||||
<viewController title="Album Art" id="3C9-vU-zjZ" customClass="AlbumArtPrefsController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
|
||||||
<view key="view" id="PyK-v2-kus">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="524" height="100"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zZn-Rm-e1f">
|
|
||||||
<rect key="frame" x="53" y="62" width="104" height="17"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Music Directory:" id="sPn-V6-CfK">
|
|
||||||
<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>
|
|
||||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gDk-ca-eOa">
|
|
||||||
<rect key="frame" x="162" y="58" width="288" height="22"/>
|
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="~/Music" drawsBackground="YES" id="7WZ-b7-GUs">
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="updateMpdLibraryDir:" target="3C9-vU-zjZ" id="3Ta-fH-5Zh"/>
|
|
||||||
</connections>
|
|
||||||
</textField>
|
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pRL-MG-1Be">
|
|
||||||
<rect key="frame" x="160" y="27" width="265" height="18"/>
|
|
||||||
<buttonCell key="cell" type="check" title="Fetch missing artwork from MusicBrainz" bezelStyle="regularSquare" imagePosition="left" inset="2" id="LpD-Ew-HMd">
|
|
||||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="updateFetchMissingArtworkFromInternet:" target="3C9-vU-zjZ" id="I7x-9V-xJr"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="zZn-Rm-e1f" firstAttribute="leading" secondItem="PyK-v2-kus" secondAttribute="leading" constant="55" id="F9T-mO-lMa"/>
|
|
||||||
<constraint firstItem="gDk-ca-eOa" firstAttribute="top" secondItem="PyK-v2-kus" secondAttribute="top" constant="20" symbolic="YES" id="NSz-Xf-KZS"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="gDk-ca-eOa" secondAttribute="trailing" constant="74" id="QMb-TP-IdQ"/>
|
|
||||||
<constraint firstItem="pRL-MG-1Be" firstAttribute="top" secondItem="gDk-ca-eOa" secondAttribute="bottom" constant="15" id="bD6-hA-Wz5"/>
|
|
||||||
<constraint firstItem="gDk-ca-eOa" firstAttribute="leading" secondItem="zZn-Rm-e1f" secondAttribute="trailing" constant="7" id="oZ5-45-Pe5"/>
|
|
||||||
<constraint firstItem="gDk-ca-eOa" firstAttribute="leading" secondItem="pRL-MG-1Be" secondAttribute="leading" id="sBG-Yb-ii6"/>
|
|
||||||
<constraint firstItem="zZn-Rm-e1f" firstAttribute="top" secondItem="PyK-v2-kus" secondAttribute="top" constant="21" id="wHW-jd-TaG"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
<connections>
|
|
||||||
<outlet property="fetchMissingArtworkFromInternet" destination="pRL-MG-1Be" id="Xcp-sb-iZm"/>
|
|
||||||
<outlet property="mpdLibraryDirField" destination="gDk-ca-eOa" id="myi-BQ-0NS"/>
|
|
||||||
</connections>
|
|
||||||
</viewController>
|
|
||||||
<customObject id="KzD-E3-lpA" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="1626" y="339"/>
|
|
||||||
</scene>
|
|
||||||
<!--General-->
|
|
||||||
<scene sceneID="xTC-Y5-Agk">
|
<scene sceneID="xTC-Y5-Agk">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController title="General" id="nYi-sw-ZNp" customClass="GeneralPrefsViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController id="nYi-sw-ZNp" customClass="PreferencesViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" id="Uwt-Lw-ILP">
|
<view key="view" id="Uwt-Lw-ILP">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="420" height="100"/>
|
<rect key="frame" x="0.0" y="0.0" width="420" height="100"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="wPm-sJ-e9E">
|
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wPm-sJ-e9E">
|
||||||
<rect key="frame" x="162" y="58" width="184" height="22"/>
|
<rect key="frame" x="162" y="58" width="184" height="22"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" alignment="left" placeholderString="127.0.0.1" drawsBackground="YES" id="MSX-mn-2ma">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" alignment="left" placeholderString="127.0.0.1" drawsBackground="YES" id="MSX-mn-2ma">
|
||||||
<font key="font" usesAppearanceFont="YES"/>
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -386,11 +276,9 @@
|
|||||||
<action selector="updateMpdHost:" target="nYi-sw-ZNp" id="Y7x-N9-6ag"/>
|
<action selector="updateMpdHost:" target="nYi-sw-ZNp" id="Y7x-N9-6ag"/>
|
||||||
</connections>
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="IbX-oV-soD">
|
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="IbX-oV-soD">
|
||||||
<rect key="frame" x="162" y="26" width="80" height="22"/>
|
<rect key="frame" x="162" y="26" width="80" height="22"/>
|
||||||
<constraints>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<constraint firstAttribute="width" constant="80" id="WAb-PB-Z1Y"/>
|
|
||||||
</constraints>
|
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="6600" drawsBackground="YES" id="i9j-nB-bqq">
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="6600" drawsBackground="YES" id="i9j-nB-bqq">
|
||||||
<numberFormatter key="formatter" formatterBehavior="custom10_4" numberStyle="decimal" usesGroupingSeparator="NO" minimumIntegerDigits="1" maximumIntegerDigits="2000000000" maximumFractionDigits="3" id="UiQ-gi-Hbp">
|
<numberFormatter key="formatter" formatterBehavior="custom10_4" numberStyle="decimal" usesGroupingSeparator="NO" minimumIntegerDigits="1" maximumIntegerDigits="2000000000" maximumFractionDigits="3" id="UiQ-gi-Hbp">
|
||||||
<real key="minimum" value="0.0"/>
|
<real key="minimum" value="0.0"/>
|
||||||
@ -404,19 +292,18 @@
|
|||||||
<action selector="updateMpdPort:" target="nYi-sw-ZNp" id="406-EC-aO2"/>
|
<action selector="updateMpdPort:" target="nYi-sw-ZNp" id="406-EC-aO2"/>
|
||||||
</connections>
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kvB-99-zwY">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="kvB-99-zwY">
|
||||||
<rect key="frame" x="76" y="62" width="80" height="17"/>
|
<rect key="frame" x="76" y="62" width="80" height="17"/>
|
||||||
<constraints>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<constraint firstAttribute="width" constant="76" id="4VR-n5-bGr"/>
|
|
||||||
</constraints>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Server Host:" id="AVi-g9-Irz">
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Server Host:" id="AVi-g9-Irz">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AU9-wN-kbU">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AU9-wN-kbU">
|
||||||
<rect key="frame" x="77" y="30" width="77" height="17"/>
|
<rect key="frame" x="77" y="30" width="77" height="17"/>
|
||||||
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Server Port:" id="DgA-xT-2ir">
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Server Port:" id="DgA-xT-2ir">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -424,18 +311,6 @@
|
|||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="wPm-sJ-e9E" firstAttribute="top" secondItem="Uwt-Lw-ILP" secondAttribute="top" constant="20" symbolic="YES" id="1u9-dh-XrA"/>
|
|
||||||
<constraint firstItem="wPm-sJ-e9E" firstAttribute="leading" secondItem="kvB-99-zwY" secondAttribute="trailing" constant="8" symbolic="YES" id="655-3c-mJH"/>
|
|
||||||
<constraint firstItem="AU9-wN-kbU" firstAttribute="top" secondItem="kvB-99-zwY" secondAttribute="bottom" constant="15" id="FPR-mZ-SUo"/>
|
|
||||||
<constraint firstItem="IbX-oV-soD" firstAttribute="top" secondItem="wPm-sJ-e9E" secondAttribute="bottom" constant="10" symbolic="YES" id="G7a-VX-OLJ"/>
|
|
||||||
<constraint firstItem="kvB-99-zwY" firstAttribute="top" secondItem="Uwt-Lw-ILP" secondAttribute="top" constant="21" id="ITg-Ag-wpl"/>
|
|
||||||
<constraint firstItem="kvB-99-zwY" firstAttribute="leading" secondItem="Uwt-Lw-ILP" secondAttribute="leading" constant="78" id="Kw0-2i-oST"/>
|
|
||||||
<constraint firstItem="IbX-oV-soD" firstAttribute="leading" secondItem="AU9-wN-kbU" secondAttribute="trailing" constant="10" id="Y6y-25-qRM"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="wPm-sJ-e9E" secondAttribute="trailing" constant="74" id="cX3-Sz-RXR"/>
|
|
||||||
<constraint firstItem="kvB-99-zwY" firstAttribute="centerX" secondItem="AU9-wN-kbU" secondAttribute="centerX" id="fdg-fL-UzL"/>
|
|
||||||
<constraint firstItem="wPm-sJ-e9E" firstAttribute="leading" secondItem="IbX-oV-soD" secondAttribute="leading" id="qBw-Ri-Z4l"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="mpdHostField" destination="wPm-sJ-e9E" id="PR7-oL-tVQ"/>
|
<outlet property="mpdHostField" destination="wPm-sJ-e9E" id="PR7-oL-tVQ"/>
|
||||||
@ -444,7 +319,7 @@
|
|||||||
</viewController>
|
</viewController>
|
||||||
<customObject id="lzf-yO-5pP" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="lzf-yO-5pP" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="1574" y="69"/>
|
<point key="canvasLocation" x="917" y="217"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Queue View Controller-->
|
<!--Queue View Controller-->
|
||||||
<scene sceneID="QcX-dC-cTZ">
|
<scene sceneID="QcX-dC-cTZ">
|
||||||
@ -603,7 +478,7 @@
|
|||||||
</viewController>
|
</viewController>
|
||||||
<customObject id="du4-e9-TfX" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="du4-e9-TfX" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="834" y="679"/>
|
<point key="canvasLocation" x="848" y="635"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Album View Controller-->
|
<!--Album View Controller-->
|
||||||
<scene sceneID="7Ua-Hj-zWt">
|
<scene sceneID="7Ua-Hj-zWt">
|
||||||
@ -619,7 +494,7 @@
|
|||||||
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
|
<rect key="frame" x="0.0" y="0.0" width="450" height="300"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<collectionView identifier="albumCollectionView" id="lfq-AB-epE">
|
<collectionView id="lfq-AB-epE">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="450" height="158"/>
|
<rect key="frame" x="0.0" y="0.0" width="450" height="158"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES"/>
|
||||||
<collectionViewLayout key="collectionViewLayout" id="YE8-sD-l5P" customClass="AlbumViewLayout" customModule="Persephone" customModuleProvider="target"/>
|
<collectionViewLayout key="collectionViewLayout" id="YE8-sD-l5P" customClass="AlbumViewLayout" customModule="Persephone" customModuleProvider="target"/>
|
||||||
@ -658,8 +533,6 @@
|
|||||||
</scene>
|
</scene>
|
||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="NSPreferencesGeneral" width="32" height="32"/>
|
|
||||||
<image name="coverArtPreferencesIcon" width="32" height="32"/>
|
|
||||||
<image name="nextTrackButton" width="17" height="17"/>
|
<image name="nextTrackButton" width="17" height="17"/>
|
||||||
<image name="playButton" width="17" height="17"/>
|
<image name="playButton" width="17" height="17"/>
|
||||||
<image name="prevTrackButton" width="17" height="17"/>
|
<image name="prevTrackButton" width="17" height="17"/>
|
||||||
|
|||||||
@ -1,54 +0,0 @@
|
|||||||
//
|
|
||||||
// AlbumArtService.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/2/23.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Cocoa
|
|
||||||
import PromiseKit
|
|
||||||
|
|
||||||
class AlbumArtService {
|
|
||||||
var preferences = Preferences()
|
|
||||||
let album: AlbumItem
|
|
||||||
|
|
||||||
let cachedArtworkSize = 180
|
|
||||||
let cachedArtworkQuality: CGFloat = 0.5
|
|
||||||
|
|
||||||
var session = URLSession(configuration: .default)
|
|
||||||
let cacheQueue = DispatchQueue(label: "albumArtCacheQueue")
|
|
||||||
|
|
||||||
init(album: AlbumItem) {
|
|
||||||
self.album = album
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchAlbumArt(callback: @escaping (_ image: NSImage?) -> Void) {
|
|
||||||
cacheQueue.async {
|
|
||||||
firstly {
|
|
||||||
self.getCachedArtwork()
|
|
||||||
}.then { artwork -> Promise<NSImage?> in
|
|
||||||
artwork.map(Promise.value) ?? self.cacheIfNecessary(self.getArtworkFromFilesystem())
|
|
||||||
}.then { artwork -> Promise<NSImage?> in
|
|
||||||
artwork.map(Promise.value) ?? self.cacheIfNecessary(self.getArtworkFromMusicBrainz().map(Optional.some))
|
|
||||||
}.tap { result in
|
|
||||||
switch result {
|
|
||||||
case .fulfilled(nil), .rejected(MusicBrainzError.noArtworkAvailable):
|
|
||||||
self.cacheArtwork(data: Data())
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}.recover { error in
|
|
||||||
.value(nil)
|
|
||||||
}.done(callback)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cacheIfNecessary(_ promise: Promise<NSImage?>) -> Promise<NSImage?> {
|
|
||||||
return promise.get { image in
|
|
||||||
if let data = image?.jpegData(compressionQuality: self.cachedArtworkQuality) {
|
|
||||||
self.cacheArtwork(data: data)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
//
|
|
||||||
// AlbumArtService+Caching.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/17.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Cocoa
|
|
||||||
import PromiseKit
|
|
||||||
|
|
||||||
extension AlbumArtService {
|
|
||||||
static let cacheDir = try! FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent(Bundle.main.bundleIdentifier!)
|
|
||||||
|
|
||||||
func getCachedArtwork() -> Promise<NSImage?> {
|
|
||||||
return Promise { seal in
|
|
||||||
let cacheFilePath = AlbumArtService.cacheDir.appendingPathComponent(album.hash).path
|
|
||||||
let data = FileManager.default.contents(atPath: cacheFilePath)
|
|
||||||
let image = data.flatMap(NSImage.init(data:))
|
|
||||||
|
|
||||||
seal.fulfill(image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cacheArtwork(data: Data?) {
|
|
||||||
guard let bundleIdentifier = Bundle.main.bundleIdentifier,
|
|
||||||
let cacheDir = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
|
|
||||||
.appendingPathComponent(bundleIdentifier)
|
|
||||||
else { return }
|
|
||||||
|
|
||||||
let cacheFilePath = cacheDir.appendingPathComponent(album.hash).path
|
|
||||||
|
|
||||||
FileManager.default.createFile(atPath: cacheFilePath, contents: data, attributes: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,49 +0,0 @@
|
|||||||
//
|
|
||||||
// AlbumArtService+Filesystem.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/17.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Cocoa
|
|
||||||
import PromiseKit
|
|
||||||
|
|
||||||
extension AlbumArtService {
|
|
||||||
func getArtworkFromFilesystem() -> Promise<NSImage?> {
|
|
||||||
let coverArtFilenames = [
|
|
||||||
"folder.jpg",
|
|
||||||
"cover.jpg",
|
|
||||||
"\(album.artist) - \(album.title).jpg"
|
|
||||||
]
|
|
||||||
|
|
||||||
return getAlbumURI().map { albumURI in
|
|
||||||
let musicDir = self.preferences.expandedMpdLibraryDir
|
|
||||||
|
|
||||||
return coverArtFilenames
|
|
||||||
.lazy
|
|
||||||
.map { "\(musicDir)/\($0)" }
|
|
||||||
.compactMap(self.tryImage)
|
|
||||||
.first
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getAlbumURI() -> Promise<String> {
|
|
||||||
return Promise { seal in
|
|
||||||
AppDelegate.mpdClient.getAlbumURI(for: album.album, callback: seal.fulfill)
|
|
||||||
}
|
|
||||||
.compactMap { $0 }
|
|
||||||
}
|
|
||||||
|
|
||||||
func tryImage(_ filePath: String) -> NSImage? {
|
|
||||||
guard let data = FileManager.default.contents(atPath: filePath),
|
|
||||||
let image = NSImage(data: data)
|
|
||||||
else { return nil }
|
|
||||||
|
|
||||||
let imageThumb = image.toFitBox(
|
|
||||||
size: NSSize(width: self.cachedArtworkSize, height: self.cachedArtworkSize)
|
|
||||||
)
|
|
||||||
|
|
||||||
return imageThumb
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,54 +0,0 @@
|
|||||||
//
|
|
||||||
// AlbumArtService+Remote.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/17.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Cocoa
|
|
||||||
import SwiftyJSON
|
|
||||||
import PromiseKit
|
|
||||||
import PMKFoundation
|
|
||||||
|
|
||||||
extension AlbumArtService {
|
|
||||||
enum MusicBrainzError: Error {
|
|
||||||
case noArtworkAvailable
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRemoteArtwork() -> Promise<NSImage> {
|
|
||||||
return Promise { seal in
|
|
||||||
let albumArtWorkItem = DispatchWorkItem {
|
|
||||||
self.getArtworkFromMusicBrainz().pipe(to: seal.resolve)
|
|
||||||
}
|
|
||||||
|
|
||||||
AlbumArtQueue.shared.addToQueue(workItem: albumArtWorkItem)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getArtworkFromMusicBrainz() -> Promise<NSImage> {
|
|
||||||
var search = URLComponents(string: "https://musicbrainz.org/ws/2/release/")!
|
|
||||||
search.query = "query=artist:\(album.artist) AND release:\(album.title) AND country:US&limit=1&fmt=json"
|
|
||||||
|
|
||||||
return URLSession.shared.dataTask(.promise, with: search.url!).validate()
|
|
||||||
.compactMap {
|
|
||||||
JSON($0.data)
|
|
||||||
}.compactMap {
|
|
||||||
$0["releases"][0]["id"].string
|
|
||||||
}.compactMap {
|
|
||||||
URLComponents(string: "https://coverartarchive.org/release/\($0)/front-500")?.url
|
|
||||||
}.then { (url: URL?) -> Promise<(data: Data, response: URLResponse)> in
|
|
||||||
return URLSession.shared.dataTask(.promise, with: url!).validate()
|
|
||||||
}.compactMap {
|
|
||||||
NSImage(data: $0.data)?.toFitBox(
|
|
||||||
size: NSSize(width: self.cachedArtworkSize, height: self.cachedArtworkSize)
|
|
||||||
)
|
|
||||||
}.recover { error -> Promise<NSImage> in
|
|
||||||
if case PMKHTTPError.badStatusCode(404, _, _) = error {
|
|
||||||
throw MusicBrainzError.noArtworkAvailable
|
|
||||||
} else {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 982 B |
|
Before Width: | Height: | Size: 7.1 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 976 B |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 4.0 KiB |
|
Before Width: | Height: | Size: 784 KiB After Width: | Height: | Size: 173 KiB |
10
bin/release
@ -1,10 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
set -o pipefail && \
|
|
||||||
|
|
||||||
xcodebuild \
|
|
||||||
-project Persephone.xcodeproj \
|
|
||||||
-scheme Persephone \
|
|
||||||
-destination platform\=macOS build \
|
|
||||||
-configuration Release \
|
|
||||||
| xcpretty
|
|
||||||