Compare commits
23 Commits
50321219b2
...
770a01c604
| Author | SHA1 | Date | |
|---|---|---|---|
| 770a01c604 | |||
| 91c82bbc6f | |||
| 123f9c1e4a | |||
| cd2e5efc95 | |||
| a663315013 | |||
| 6236884195 | |||
| cff3e7bfc6 | |||
| f22b5b022c | |||
| cacb0b0124 | |||
| f39c2a4f99 | |||
| abe2c293eb | |||
| abf1579789 | |||
| 2f59eaeecf | |||
| fa338ee790 | |||
| a7e7620f68 | |||
| eb5cee2a75 | |||
| f39f69b4dc | |||
| 8d24d9df19 | |||
| 7c99a9a712 | |||
| 7f8e209970 | |||
| c0f8badbb5 | |||
| 3b4b5f5b5c | |||
| ff3c7c4856 |
@ -53,9 +53,11 @@
|
|||||||
E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519F227BB0AB0090CD6F /* UIState.swift */; };
|
E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519F227BB0AB0090CD6F /* UIState.swift */; };
|
||||||
E442CCCD2347E73C00004E0C /* Artist.swift in Sources */ = {isa = PBXBuildFile; fileRef = E442CCCC2347E73C00004E0C /* Artist.swift */; };
|
E442CCCD2347E73C00004E0C /* Artist.swift in Sources */ = {isa = PBXBuildFile; fileRef = E442CCCC2347E73C00004E0C /* Artist.swift */; };
|
||||||
E450AD7E222620A10091BED3 /* Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD7D222620A10091BED3 /* Album.swift */; };
|
E450AD7E222620A10091BED3 /* Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD7D222620A10091BED3 /* Album.swift */; };
|
||||||
E450AD9522262DF10091BED3 /* CoverArtQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD9422262DF10091BED3 /* CoverArtQueue.swift */; };
|
|
||||||
E451E36B22BD214D008BE9B2 /* DraggedSongType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E451E36A22BD214D008BE9B2 /* DraggedSongType.swift */; };
|
E451E36B22BD214D008BE9B2 /* DraggedSongType.swift in Sources */ = {isa = PBXBuildFile; fileRef = E451E36A22BD214D008BE9B2 /* DraggedSongType.swift */; };
|
||||||
E451E36E22BD2501008BE9B2 /* DraggedSong.swift in Sources */ = {isa = PBXBuildFile; fileRef = E451E36C22BD23DB008BE9B2 /* DraggedSong.swift */; };
|
E451E36E22BD2501008BE9B2 /* DraggedSong.swift in Sources */ = {isa = PBXBuildFile; fileRef = E451E36C22BD23DB008BE9B2 /* DraggedSong.swift */; };
|
||||||
|
E453825223FA0186007F6BFC /* VolumeControlView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E453825023FA0186007F6BFC /* VolumeControlView.swift */; };
|
||||||
|
E453825323FA0186007F6BFC /* VolumeControlView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E453825123FA0186007F6BFC /* VolumeControlView.xib */; };
|
||||||
|
E453825523FA347C007F6BFC /* MPDClient+Mixer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E453825423FA347C007F6BFC /* MPDClient+Mixer.swift */; };
|
||||||
E45878382296173C00586A1C /* AlbumDetailSongRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45878372296173C00586A1C /* AlbumDetailSongRowView.swift */; };
|
E45878382296173C00586A1C /* AlbumDetailSongRowView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45878372296173C00586A1C /* AlbumDetailSongRowView.swift */; };
|
||||||
E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45962C52241A78500FC1A1E /* MPDCommand.swift */; };
|
E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45962C52241A78500FC1A1E /* MPDCommand.swift */; };
|
||||||
E45E4FDA22515D87004B537F /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = E45E4FD722515D87004B537F /* CHANGELOG.md */; };
|
E45E4FDA22515D87004B537F /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = E45E4FD722515D87004B537F /* CHANGELOG.md */; };
|
||||||
@ -81,6 +83,8 @@
|
|||||||
E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A642D922090CBE00067D21 /* MPDStatus.swift */; };
|
E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A642D922090CBE00067D21 /* MPDStatus.swift */; };
|
||||||
E4A83BEF2221F8CF0098FED6 /* CoverArtPrefsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BEE2221F8CF0098FED6 /* CoverArtPrefsController.swift */; };
|
E4A83BEF2221F8CF0098FED6 /* CoverArtPrefsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BEE2221F8CF0098FED6 /* CoverArtPrefsController.swift */; };
|
||||||
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */; };
|
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */; };
|
||||||
|
E4B079C723E5E0AD0044B6D3 /* libmpdclient.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; };
|
||||||
|
E4B079C823E5E0AD0044B6D3 /* libmpdclient.2.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||||
E4B11B53226928F20075461B /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B52226928F20075461B /* AppState.swift */; };
|
E4B11B53226928F20075461B /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B52226928F20075461B /* AppState.swift */; };
|
||||||
E4B11B61226A4C000075461B /* PlayerReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B60226A4BFF0075461B /* PlayerReducer.swift */; };
|
E4B11B61226A4C000075461B /* PlayerReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B60226A4BFF0075461B /* PlayerReducer.swift */; };
|
||||||
E4B11B63226A4C510075461B /* AppReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B62226A4C510075461B /* AppReducer.swift */; };
|
E4B11B63226A4C510075461B /* AppReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B62226A4C510075461B /* AppReducer.swift */; };
|
||||||
@ -90,8 +94,6 @@
|
|||||||
E4B11B73226A6C770075461B /* TrackTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B72226A6C770075461B /* TrackTimer.swift */; };
|
E4B11B73226A6C770075461B /* TrackTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B72226A6C770075461B /* TrackTimer.swift */; };
|
||||||
E4B11B75226CC4D30075461B /* QueueReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B74226CC4D30075461B /* QueueReducer.swift */; };
|
E4B11B75226CC4D30075461B /* QueueReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B74226CC4D30075461B /* QueueReducer.swift */; };
|
||||||
E4B11B79226D346B0075461B /* AlbumListReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B78226D346B0075461B /* AlbumListReducer.swift */; };
|
E4B11B79226D346B0075461B /* AlbumListReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B78226D346B0075461B /* AlbumListReducer.swift */; };
|
||||||
E4B11BA62274E44A0075461B /* libmpdclient.2.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
|
||||||
E4B11BA72274E4500075461B /* libmpdclient.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; };
|
|
||||||
E4B11BA92274EDE30075461B /* Loading.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BA82274EDE30075461B /* Loading.swift */; };
|
E4B11BA92274EDE30075461B /* Loading.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BA82274EDE30075461B /* Loading.swift */; };
|
||||||
E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB52275374B0075461B /* UserNotificationsController.swift */; };
|
E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB52275374B0075461B /* UserNotificationsController.swift */; };
|
||||||
E4B11BB8227538FA0075461B /* CurrentCoverArtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB7227538FA0075461B /* CurrentCoverArtView.swift */; };
|
E4B11BB8227538FA0075461B /* CurrentCoverArtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB7227538FA0075461B /* CurrentCoverArtView.swift */; };
|
||||||
@ -100,11 +102,14 @@
|
|||||||
E4B11BC22275EE410075461B /* AlbumListActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BC12275EE410075461B /* AlbumListActions.swift */; };
|
E4B11BC22275EE410075461B /* AlbumListActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BC12275EE410075461B /* AlbumListActions.swift */; };
|
||||||
E4B3DF6523D66A4400728F6B /* QueueSongCoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B3DF6423D66A4400728F6B /* QueueSongCoverView.swift */; };
|
E4B3DF6523D66A4400728F6B /* QueueSongCoverView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B3DF6423D66A4400728F6B /* QueueSongCoverView.swift */; };
|
||||||
E4B5AE7E22F4C49600CCEC65 /* MPDServerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */; };
|
E4B5AE7E22F4C49600CCEC65 /* MPDServerDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */; };
|
||||||
|
E4BB7F8F23E5E7BC00906E2F /* MPDAlbumArtImageDataProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4BB7F8E23E5E7BC00906E2F /* MPDAlbumArtImageDataProvider.swift */; };
|
||||||
|
E4BB7F9323E9150A00906E2F /* CoverArtService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4BB7F9223E9150A00906E2F /* CoverArtService.swift */; };
|
||||||
E4BBD2F323357C0700702C16 /* ArtistListState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4BBD2F223357C0700702C16 /* ArtistListState.swift */; };
|
E4BBD2F323357C0700702C16 /* ArtistListState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4BBD2F223357C0700702C16 /* ArtistListState.swift */; };
|
||||||
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; };
|
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; };
|
||||||
E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* MPDIdle.swift */; };
|
E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* MPDIdle.swift */; };
|
||||||
E4D3BFA622B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4D3BFA522B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift */; };
|
E4D3BFA622B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4D3BFA522B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift */; };
|
||||||
E4DA820623D6236200C1EE58 /* NSSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DA820523D6236200C1EE58 /* NSSize.swift */; };
|
E4DA820623D6236200C1EE58 /* NSSize.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4DA820523D6236200C1EE58 /* NSSize.swift */; };
|
||||||
|
E4DCCFAE23E4DB5D009A8113 /* MPDClientWrapper.c in Sources */ = {isa = PBXBuildFile; fileRef = E4DCCFAD23E4DB5D009A8113 /* MPDClientWrapper.c */; };
|
||||||
E4E7A6AD22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E7A6AC22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift */; };
|
E4E7A6AD22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E7A6AC22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.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 */; };
|
||||||
@ -151,15 +156,15 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
E4B11BA52274E43B0075461B /* Embed Frameworks */ = {
|
E4B079C923E5E0AD0044B6D3 /* Embed Libraries */ = {
|
||||||
isa = PBXCopyFilesBuildPhase;
|
isa = PBXCopyFilesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
dstPath = "";
|
dstPath = "";
|
||||||
dstSubfolderSpec = 10;
|
dstSubfolderSpec = 10;
|
||||||
files = (
|
files = (
|
||||||
E4B11BA62274E44A0075461B /* libmpdclient.2.dylib in Embed Frameworks */,
|
E4B079C823E5E0AD0044B6D3 /* libmpdclient.2.dylib in Embed Libraries */,
|
||||||
);
|
);
|
||||||
name = "Embed Frameworks";
|
name = "Embed Libraries";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
@ -254,10 +259,12 @@
|
|||||||
E440519F227BB0AB0090CD6F /* UIState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIState.swift; sourceTree = "<group>"; };
|
E440519F227BB0AB0090CD6F /* UIState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIState.swift; sourceTree = "<group>"; };
|
||||||
E442CCCC2347E73C00004E0C /* Artist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Artist.swift; sourceTree = "<group>"; };
|
E442CCCC2347E73C00004E0C /* Artist.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Artist.swift; sourceTree = "<group>"; };
|
||||||
E450AD7D222620A10091BED3 /* Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Album.swift; sourceTree = "<group>"; };
|
E450AD7D222620A10091BED3 /* Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Album.swift; sourceTree = "<group>"; };
|
||||||
E450AD9422262DF10091BED3 /* CoverArtQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoverArtQueue.swift; sourceTree = "<group>"; };
|
|
||||||
E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PersephoneBridgingHeader.h; sourceTree = "<group>"; };
|
E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PersephoneBridgingHeader.h; sourceTree = "<group>"; };
|
||||||
E451E36A22BD214D008BE9B2 /* DraggedSongType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggedSongType.swift; sourceTree = "<group>"; };
|
E451E36A22BD214D008BE9B2 /* DraggedSongType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggedSongType.swift; sourceTree = "<group>"; };
|
||||||
E451E36C22BD23DB008BE9B2 /* DraggedSong.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggedSong.swift; sourceTree = "<group>"; };
|
E451E36C22BD23DB008BE9B2 /* DraggedSong.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggedSong.swift; sourceTree = "<group>"; };
|
||||||
|
E453825023FA0186007F6BFC /* VolumeControlView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VolumeControlView.swift; sourceTree = "<group>"; };
|
||||||
|
E453825123FA0186007F6BFC /* VolumeControlView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = VolumeControlView.xib; sourceTree = "<group>"; };
|
||||||
|
E453825423FA347C007F6BFC /* MPDClient+Mixer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Mixer.swift"; sourceTree = "<group>"; };
|
||||||
E45878372296173C00586A1C /* AlbumDetailSongRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailSongRowView.swift; sourceTree = "<group>"; };
|
E45878372296173C00586A1C /* AlbumDetailSongRowView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailSongRowView.swift; sourceTree = "<group>"; };
|
||||||
E45962C52241A78500FC1A1E /* MPDCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDCommand.swift; sourceTree = "<group>"; };
|
E45962C52241A78500FC1A1E /* MPDCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDCommand.swift; sourceTree = "<group>"; };
|
||||||
E45E4FD722515D87004B537F /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = SOURCE_ROOT; };
|
E45E4FD722515D87004B537F /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = SOURCE_ROOT; };
|
||||||
@ -294,11 +301,16 @@
|
|||||||
E4B11BC12275EE410075461B /* AlbumListActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumListActions.swift; sourceTree = "<group>"; };
|
E4B11BC12275EE410075461B /* AlbumListActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumListActions.swift; sourceTree = "<group>"; };
|
||||||
E4B3DF6423D66A4400728F6B /* QueueSongCoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueSongCoverView.swift; sourceTree = "<group>"; };
|
E4B3DF6423D66A4400728F6B /* QueueSongCoverView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueSongCoverView.swift; sourceTree = "<group>"; };
|
||||||
E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDServerDelegate.swift; sourceTree = "<group>"; };
|
E4B5AE7D22F4C49600CCEC65 /* MPDServerDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDServerDelegate.swift; sourceTree = "<group>"; };
|
||||||
|
E4BB7F8E23E5E7BC00906E2F /* MPDAlbumArtImageDataProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDAlbumArtImageDataProvider.swift; sourceTree = "<group>"; };
|
||||||
|
E4BB7F9223E9150A00906E2F /* CoverArtService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoverArtService.swift; sourceTree = "<group>"; };
|
||||||
E4BBD2F223357C0700702C16 /* ArtistListState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArtistListState.swift; sourceTree = "<group>"; };
|
E4BBD2F223357C0700702C16 /* ArtistListState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ArtistListState.swift; sourceTree = "<group>"; };
|
||||||
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
||||||
E4C8B53D22349002009A20F3 /* MPDIdle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDIdle.swift; sourceTree = "<group>"; };
|
E4C8B53D22349002009A20F3 /* MPDIdle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDIdle.swift; sourceTree = "<group>"; };
|
||||||
E4D3BFA522B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QueueViewController+NSOutlineViewDelegate.swift"; sourceTree = "<group>"; };
|
E4D3BFA522B419C000C56F48 /* QueueViewController+NSOutlineViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "QueueViewController+NSOutlineViewDelegate.swift"; sourceTree = "<group>"; };
|
||||||
E4DA820523D6236200C1EE58 /* NSSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSSize.swift; sourceTree = "<group>"; };
|
E4DA820523D6236200C1EE58 /* NSSize.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSSize.swift; sourceTree = "<group>"; };
|
||||||
|
E4DCCFAB23E4DB5D009A8113 /* Persephone-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Persephone-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
|
E4DCCFAC23E4DB5D009A8113 /* MPDClientWrapper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = MPDClientWrapper.h; sourceTree = "<group>"; };
|
||||||
|
E4DCCFAD23E4DB5D009A8113 /* MPDClientWrapper.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = MPDClientWrapper.c; sourceTree = "<group>"; };
|
||||||
E4E7A6AC22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AlbumDetailView+NSTableViewDelegate.swift"; sourceTree = "<group>"; };
|
E4E7A6AC22AAAF98006D566C /* AlbumDetailView+NSTableViewDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AlbumDetailView+NSTableViewDelegate.swift"; sourceTree = "<group>"; };
|
||||||
E4E8CC8F2204EC7F0024217A /* Delegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delegate.swift; sourceTree = "<group>"; };
|
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>"; };
|
||||||
@ -322,12 +334,12 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
E4B11BA72274E4500075461B /* libmpdclient.2.dylib in Frameworks */,
|
|
||||||
E49A5485233E5ADC00EED353 /* Differ in Frameworks */,
|
E49A5485233E5ADC00EED353 /* Differ in Frameworks */,
|
||||||
E4677C48233E60E70041474F /* MediaKeyTap in Frameworks */,
|
E4677C48233E60E70041474F /* MediaKeyTap in Frameworks */,
|
||||||
E43BECA0238835DC00CAF1EB /* Kingfisher in Frameworks */,
|
E43BECA0238835DC00CAF1EB /* Kingfisher in Frameworks */,
|
||||||
E49A548E233E5B6000EED353 /* SwiftyJSON in Frameworks */,
|
E49A548E233E5B6000EED353 /* SwiftyJSON in Frameworks */,
|
||||||
E4E96D13233E630800AFD36F /* PMKFoundation in Frameworks */,
|
E4E96D13233E630800AFD36F /* PMKFoundation in Frameworks */,
|
||||||
|
E4B079C723E5E0AD0044B6D3 /* libmpdclient.2.dylib in Frameworks */,
|
||||||
E49A5482233E580800EED353 /* PromiseKit in Frameworks */,
|
E49A5482233E580800EED353 /* PromiseKit in Frameworks */,
|
||||||
E49A548B233E5B2D00EED353 /* CryptoSwift in Frameworks */,
|
E49A548B233E5B2D00EED353 /* CryptoSwift in Frameworks */,
|
||||||
E49A5488233E5B0000EED353 /* ReSwift in Frameworks */,
|
E49A5488233E5B0000EED353 /* ReSwift in Frameworks */,
|
||||||
@ -387,7 +399,6 @@
|
|||||||
E40786242110CE70006887B1 /* Info.plist */,
|
E40786242110CE70006887B1 /* Info.plist */,
|
||||||
E4F6B461221E124700ACF42A /* Models */,
|
E4F6B461221E124700ACF42A /* Models */,
|
||||||
E4A642DB220912FA00067D21 /* MPDClient */,
|
E4A642DB220912FA00067D21 /* MPDClient */,
|
||||||
E450AD8922262B420091BED3 /* Operations */,
|
|
||||||
E40786252110CE70006887B1 /* Persephone.entitlements */,
|
E40786252110CE70006887B1 /* Persephone.entitlements */,
|
||||||
E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */,
|
E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */,
|
||||||
E4A83BF2222207BE0098FED6 /* Services */,
|
E4A83BF2222207BE0098FED6 /* Services */,
|
||||||
@ -441,11 +452,15 @@
|
|||||||
E42410B52241B956005ED6DF /* MPDClient+Database.swift */,
|
E42410B52241B956005ED6DF /* MPDClient+Database.swift */,
|
||||||
E41E5304223BFB0700173814 /* MPDClient+Error.swift */,
|
E41E5304223BFB0700173814 /* MPDClient+Error.swift */,
|
||||||
E41E5302223BF9C300173814 /* MPDClient+Idle.swift */,
|
E41E5302223BF9C300173814 /* MPDClient+Idle.swift */,
|
||||||
|
E453825423FA347C007F6BFC /* MPDClient+Mixer.swift */,
|
||||||
E41E5300223BF99300173814 /* MPDClient+Queue.swift */,
|
E41E5300223BF99300173814 /* MPDClient+Queue.swift */,
|
||||||
E42A4D5022E2167E001C6CAD /* MPDClient+Songs.swift */,
|
E42A4D5022E2167E001C6CAD /* MPDClient+Songs.swift */,
|
||||||
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */,
|
|
||||||
E41E5306223C019100173814 /* MPDClient+Status.swift */,
|
E41E5306223C019100173814 /* MPDClient+Status.swift */,
|
||||||
E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */,
|
E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */,
|
||||||
|
E4DCCFAD23E4DB5D009A8113 /* MPDClientWrapper.c */,
|
||||||
|
E4DCCFAC23E4DB5D009A8113 /* MPDClientWrapper.h */,
|
||||||
|
E4DCCFAB23E4DB5D009A8113 /* Persephone-Bridging-Header.h */,
|
||||||
|
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -512,6 +527,7 @@
|
|||||||
E442CCC42347D5B900004E0C /* Components */ = {
|
E442CCC42347D5B900004E0C /* Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E453824D23F9F700007F6BFC /* VolumeControl */,
|
||||||
E442CCCB2347D77A00004E0C /* Browser */,
|
E442CCCB2347D77A00004E0C /* Browser */,
|
||||||
E4A83BEC2221F5DD0098FED6 /* Preferences */,
|
E4A83BEC2221F5DD0098FED6 /* Preferences */,
|
||||||
E442CCC62347D5E700004E0C /* Queue */,
|
E442CCC62347D5E700004E0C /* Queue */,
|
||||||
@ -574,6 +590,7 @@
|
|||||||
E442CCC92347D6FD00004E0C /* Shared */ = {
|
E442CCC92347D6FD00004E0C /* Shared */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E4BB7F8D23E5E7A300906E2F /* ImageDataProviders */,
|
||||||
E4E13C2C2350D8CB00092A6E /* Layouts */,
|
E4E13C2C2350D8CB00092A6E /* Layouts */,
|
||||||
E408D3B7220DE8CC0006D9BE /* Extensions */,
|
E408D3B7220DE8CC0006D9BE /* Extensions */,
|
||||||
E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */,
|
E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */,
|
||||||
@ -594,12 +611,13 @@
|
|||||||
path = Browser;
|
path = Browser;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
E450AD8922262B420091BED3 /* Operations */ = {
|
E453824D23F9F700007F6BFC /* VolumeControl */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E450AD9422262DF10091BED3 /* CoverArtQueue.swift */,
|
E453825023FA0186007F6BFC /* VolumeControlView.swift */,
|
||||||
|
E453825123FA0186007F6BFC /* VolumeControlView.xib */,
|
||||||
);
|
);
|
||||||
path = Operations;
|
path = VolumeControl;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
E4A642DB220912FA00067D21 /* MPDClient */ = {
|
E4A642DB220912FA00067D21 /* MPDClient */ = {
|
||||||
@ -628,6 +646,7 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E439109722640213002982E9 /* SongNotifierService.swift */,
|
E439109722640213002982E9 /* SongNotifierService.swift */,
|
||||||
|
E4BB7F9223E9150A00906E2F /* CoverArtService.swift */,
|
||||||
);
|
);
|
||||||
path = Services;
|
path = Services;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -675,6 +694,14 @@
|
|||||||
path = Actions;
|
path = Actions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
E4BB7F8D23E5E7A300906E2F /* ImageDataProviders */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
E4BB7F8E23E5E7BC00906E2F /* MPDAlbumArtImageDataProvider.swift */,
|
||||||
|
);
|
||||||
|
path = ImageDataProviders;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
E4D1B594220BA2490026F233 /* Models */ = {
|
E4D1B594220BA2490026F233 /* Models */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -735,7 +762,7 @@
|
|||||||
E40786162110CE6E006887B1 /* Resources */,
|
E40786162110CE6E006887B1 /* Resources */,
|
||||||
E42A98F122430936004D8180 /* ShellScript */,
|
E42A98F122430936004D8180 /* ShellScript */,
|
||||||
E421AC9B221F7319008B2449 /* CopyFiles */,
|
E421AC9B221F7319008B2449 /* CopyFiles */,
|
||||||
E4B11BA52274E43B0075461B /* Embed Frameworks */,
|
E4B079C923E5E0AD0044B6D3 /* Embed Libraries */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@ -804,7 +831,7 @@
|
|||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
E40786172110CE6E006887B1 = {
|
E40786172110CE6E006887B1 = {
|
||||||
CreatedOnToolsVersion = 9.4.1;
|
CreatedOnToolsVersion = 9.4.1;
|
||||||
LastSwiftMigration = 1020;
|
LastSwiftMigration = 1130;
|
||||||
SystemCapabilities = {
|
SystemCapabilities = {
|
||||||
com.apple.HardenedRuntime = {
|
com.apple.HardenedRuntime = {
|
||||||
enabled = 1;
|
enabled = 1;
|
||||||
@ -863,6 +890,7 @@
|
|||||||
files = (
|
files = (
|
||||||
E45E4FDB22515D87004B537F /* Brewfile in Resources */,
|
E45E4FDB22515D87004B537F /* Brewfile in Resources */,
|
||||||
E42A8F3B22176D6400A13ED9 /* LICENSE.md in Resources */,
|
E42A8F3B22176D6400A13ED9 /* LICENSE.md in Resources */,
|
||||||
|
E453825323FA0186007F6BFC /* VolumeControlView.xib in Resources */,
|
||||||
E45E4FDA22515D87004B537F /* CHANGELOG.md in Resources */,
|
E45E4FDA22515D87004B537F /* CHANGELOG.md in Resources */,
|
||||||
E43B67AB22909793007DCF55 /* AlbumDetailView.xib in Resources */,
|
E43B67AB22909793007DCF55 /* AlbumDetailView.xib in Resources */,
|
||||||
E489E3A522B9D31800CA8CBD /* DraggedSongView.xib in Resources */,
|
E489E3A522B9D31800CA8CBD /* DraggedSongView.xib in Resources */,
|
||||||
@ -933,13 +961,14 @@
|
|||||||
E4B11B63226A4C510075461B /* AppReducer.swift in Sources */,
|
E4B11B63226A4C510075461B /* AppReducer.swift in Sources */,
|
||||||
E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */,
|
E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */,
|
||||||
E442CCCD2347E73C00004E0C /* Artist.swift in Sources */,
|
E442CCCD2347E73C00004E0C /* Artist.swift in Sources */,
|
||||||
|
E4DCCFAE23E4DB5D009A8113 /* MPDClientWrapper.c in Sources */,
|
||||||
E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */,
|
E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */,
|
||||||
E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */,
|
E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */,
|
||||||
|
E4BB7F8F23E5E7BC00906E2F /* MPDAlbumArtImageDataProvider.swift in Sources */,
|
||||||
E4235640228623D2001216D6 /* QueueSongInfoView.swift in Sources */,
|
E4235640228623D2001216D6 /* QueueSongInfoView.swift in Sources */,
|
||||||
E451E36E22BD2501008BE9B2 /* DraggedSong.swift in Sources */,
|
E451E36E22BD2501008BE9B2 /* DraggedSong.swift in Sources */,
|
||||||
E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */,
|
E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */,
|
||||||
E4B11BC02275EE150075461B /* QueueActions.swift in Sources */,
|
E4B11BC02275EE150075461B /* QueueActions.swift in Sources */,
|
||||||
E450AD9522262DF10091BED3 /* CoverArtQueue.swift in Sources */,
|
|
||||||
E43B67AD229194CD007DCF55 /* AlbumTracksDataSource.swift in Sources */,
|
E43B67AD229194CD007DCF55 /* AlbumTracksDataSource.swift in Sources */,
|
||||||
E41E52FD223BF87300173814 /* MPDClient+Connection.swift in Sources */,
|
E41E52FD223BF87300173814 /* MPDClient+Connection.swift in Sources */,
|
||||||
E450AD7E222620A10091BED3 /* Album.swift in Sources */,
|
E450AD7E222620A10091BED3 /* Album.swift in Sources */,
|
||||||
@ -968,6 +997,7 @@
|
|||||||
E419E2872249B96600216A8C /* Song.swift in Sources */,
|
E419E2872249B96600216A8C /* Song.swift in Sources */,
|
||||||
E451E36B22BD214D008BE9B2 /* DraggedSongType.swift in Sources */,
|
E451E36B22BD214D008BE9B2 /* DraggedSongType.swift in Sources */,
|
||||||
E4BBD2F323357C0700702C16 /* ArtistListState.swift in Sources */,
|
E4BBD2F323357C0700702C16 /* ArtistListState.swift in Sources */,
|
||||||
|
E453825223FA0186007F6BFC /* VolumeControlView.swift in Sources */,
|
||||||
E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */,
|
E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */,
|
||||||
E4FF718E2276010E00D4C412 /* PreferencesState.swift in Sources */,
|
E4FF718E2276010E00D4C412 /* PreferencesState.swift in Sources */,
|
||||||
E439109822640213002982E9 /* SongNotifierService.swift in Sources */,
|
E439109822640213002982E9 /* SongNotifierService.swift in Sources */,
|
||||||
@ -978,6 +1008,7 @@
|
|||||||
E4F26F7923411B1500D45FF9 /* ArtistReducer.swift in Sources */,
|
E4F26F7923411B1500D45FF9 /* ArtistReducer.swift in Sources */,
|
||||||
E4B11B73226A6C770075461B /* TrackTimer.swift in Sources */,
|
E4B11B73226A6C770075461B /* TrackTimer.swift in Sources */,
|
||||||
E44051942278765A0090CD6F /* App.swift in Sources */,
|
E44051942278765A0090CD6F /* App.swift in Sources */,
|
||||||
|
E4BB7F9323E9150A00906E2F /* CoverArtService.swift in Sources */,
|
||||||
E4B11B79226D346B0075461B /* AlbumListReducer.swift in Sources */,
|
E4B11B79226D346B0075461B /* AlbumListReducer.swift in Sources */,
|
||||||
E47E2FE52220AA0700F747E6 /* FlexibleGridViewLayout.swift in Sources */,
|
E47E2FE52220AA0700F747E6 /* FlexibleGridViewLayout.swift in Sources */,
|
||||||
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */,
|
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */,
|
||||||
@ -985,6 +1016,7 @@
|
|||||||
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */,
|
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */,
|
||||||
E4B5AE7E22F4C49600CCEC65 /* MPDServerDelegate.swift in Sources */,
|
E4B5AE7E22F4C49600CCEC65 /* MPDServerDelegate.swift in Sources */,
|
||||||
E4B11BB8227538FA0075461B /* CurrentCoverArtView.swift in Sources */,
|
E4B11BB8227538FA0075461B /* CurrentCoverArtView.swift in Sources */,
|
||||||
|
E453825523FA347C007F6BFC /* MPDClient+Mixer.swift in Sources */,
|
||||||
E4E8CC9A22075D370024217A /* MPDSong.swift in Sources */,
|
E4E8CC9A22075D370024217A /* MPDSong.swift in Sources */,
|
||||||
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */,
|
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */,
|
||||||
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */,
|
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */,
|
||||||
@ -1173,6 +1205,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
@ -1188,10 +1221,12 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/libmpdclient/output",
|
"$(PROJECT_DIR)/libmpdclient/output",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "0.15.2-alpha";
|
MARKETING_VERSION = "0.16.0-alpha";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.Persephone;
|
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.Persephone;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Persephone/MPDClient/Extensions/Persephone-Bridging-Header.h";
|
||||||
|
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
SYSTEM_HEADER_SEARCH_PATHS = Persephone/include;
|
SYSTEM_HEADER_SEARCH_PATHS = Persephone/include;
|
||||||
USER_HEADER_SEARCH_PATHS = libmpdclient/output;
|
USER_HEADER_SEARCH_PATHS = libmpdclient/output;
|
||||||
@ -1202,6 +1237,7 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
@ -1217,10 +1253,11 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/libmpdclient/output",
|
"$(PROJECT_DIR)/libmpdclient/output",
|
||||||
);
|
);
|
||||||
MARKETING_VERSION = "0.15.2-alpha";
|
MARKETING_VERSION = "0.16.0-alpha";
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.Persephone;
|
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.Persephone;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
SWIFT_OBJC_BRIDGING_HEADER = "Persephone/MPDClient/Extensions/Persephone-Bridging-Header.h";
|
||||||
SWIFT_VERSION = 5.0;
|
SWIFT_VERSION = 5.0;
|
||||||
SYSTEM_HEADER_SEARCH_PATHS = Persephone/include;
|
SYSTEM_HEADER_SEARCH_PATHS = Persephone/include;
|
||||||
USER_HEADER_SEARCH_PATHS = libmpdclient/output;
|
USER_HEADER_SEARCH_PATHS = libmpdclient/output;
|
||||||
|
|||||||
@ -6,8 +6,8 @@
|
|||||||
"repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift",
|
"repositoryURL": "https://github.com/krzyzanowskim/CryptoSwift",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "3a2acbb32ab68215ee1596ee6004da8e90c3721b",
|
"revision": "a44caef0550c346e0ab9172f7c9a3852c1833599",
|
||||||
"version": "1.0.0"
|
"version": "1.3.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -15,8 +15,8 @@
|
|||||||
"repositoryURL": "https://github.com/tonyarnold/Differ",
|
"repositoryURL": "https://github.com/tonyarnold/Differ",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "e2cca36e7258dd8add88ae46b5ea56509b066e21",
|
"revision": "dd5d4bfb1c27012d4790e877b29847d2ab9d989b",
|
||||||
"version": "1.4.3"
|
"version": "1.4.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -24,8 +24,8 @@
|
|||||||
"repositoryURL": "https://github.com/PromiseKit/Foundation",
|
"repositoryURL": "https://github.com/PromiseKit/Foundation",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "ee06d95342a5007de2fffd898f4f35de026842ac",
|
"revision": "1a276e598dac59489ed904887e0740fa75e571e0",
|
||||||
"version": "3.3.3"
|
"version": "3.3.4"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -33,8 +33,8 @@
|
|||||||
"repositoryURL": "https://github.com/onevcat/Kingfisher.git",
|
"repositoryURL": "https://github.com/onevcat/Kingfisher.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "8ef6ca8b1b767ac2400762ed2f2bf75ddea3de5b",
|
"revision": "44bfa76787a0f07e4d5ae3304e9d4f631fd519bd",
|
||||||
"version": "5.10.1"
|
"version": "5.13.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -51,8 +51,8 @@
|
|||||||
"repositoryURL": "https://github.com/mxcl/PromiseKit",
|
"repositoryURL": "https://github.com/mxcl/PromiseKit",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "4d8d1287d2e50c53a9f8430ffe88925292838c57",
|
"revision": "f14f16cc2602afec1030e4f492100d6d43dca544",
|
||||||
"version": "6.11.0"
|
"version": "6.13.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@ -27,6 +27,15 @@
|
|||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||||
|
<MacroExpansion>
|
||||||
|
<BuildableReference
|
||||||
|
BuildableIdentifier = "primary"
|
||||||
|
BlueprintIdentifier = "E40786172110CE6E006887B1"
|
||||||
|
BuildableName = "Persephone.app"
|
||||||
|
BlueprintName = "Persephone"
|
||||||
|
ReferencedContainer = "container:Persephone.xcodeproj">
|
||||||
|
</BuildableReference>
|
||||||
|
</MacroExpansion>
|
||||||
<Testables>
|
<Testables>
|
||||||
<TestableReference
|
<TestableReference
|
||||||
skipped = "NO">
|
skipped = "NO">
|
||||||
@ -49,17 +58,6 @@
|
|||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</TestableReference>
|
</TestableReference>
|
||||||
</Testables>
|
</Testables>
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "E40786172110CE6E006887B1"
|
|
||||||
BuildableName = "Persephone.app"
|
|
||||||
BlueprintName = "Persephone"
|
|
||||||
ReferencedContainer = "container:Persephone.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</TestAction>
|
</TestAction>
|
||||||
<LaunchAction
|
<LaunchAction
|
||||||
buildConfiguration = "Debug"
|
buildConfiguration = "Debug"
|
||||||
@ -81,8 +79,6 @@
|
|||||||
ReferencedContainer = "container:Persephone.xcodeproj">
|
ReferencedContainer = "container:Persephone.xcodeproj">
|
||||||
</BuildableReference>
|
</BuildableReference>
|
||||||
</BuildableProductRunnable>
|
</BuildableProductRunnable>
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
</LaunchAction>
|
||||||
<ProfileAction
|
<ProfileAction
|
||||||
buildConfiguration = "Release"
|
buildConfiguration = "Release"
|
||||||
|
|||||||
25
Persephone/Assets.xcassets/speakerDisabled.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "speakerDisabled.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "speakerDisabled@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Persephone/Assets.xcassets/speakerDisabled.imageset/speakerDisabled.png
vendored
Normal file
|
After Width: | Height: | Size: 345 B |
BIN
Persephone/Assets.xcassets/speakerDisabled.imageset/speakerDisabled@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 566 B |
25
Persephone/Assets.xcassets/speakerHigh.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "speakerHigh.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "speakerHigh@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Persephone/Assets.xcassets/speakerHigh.imageset/speakerHigh.png
vendored
Normal file
|
After Width: | Height: | Size: 428 B |
BIN
Persephone/Assets.xcassets/speakerHigh.imageset/speakerHigh@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 800 B |
25
Persephone/Assets.xcassets/speakerLow.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "speakerLow.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "speakerLow@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Persephone/Assets.xcassets/speakerLow.imageset/speakerLow.png
vendored
Normal file
|
After Width: | Height: | Size: 306 B |
BIN
Persephone/Assets.xcassets/speakerLow.imageset/speakerLow@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 531 B |
25
Persephone/Assets.xcassets/speakerMid.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "speakerMid.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "speakerMid@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Persephone/Assets.xcassets/speakerMid.imageset/speakerMid.png
vendored
Normal file
|
After Width: | Height: | Size: 357 B |
BIN
Persephone/Assets.xcassets/speakerMid.imageset/speakerMid@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 648 B |
25
Persephone/Assets.xcassets/speakerOff.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "speakerOff.png",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"filename" : "speakerOff@2x.png",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"version" : 1,
|
||||||
|
"author" : "xcode"
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"template-rendering-intent" : "template"
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
Persephone/Assets.xcassets/speakerOff.imageset/speakerOff.png
vendored
Normal file
|
After Width: | Height: | Size: 256 B |
BIN
Persephone/Assets.xcassets/speakerOff.imageset/speakerOff@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 383 B |
@ -56,10 +56,14 @@ class AlbumViewItem: NSCollectionViewItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setAlbumCover(_ album: Album) {
|
func setAlbumCover(_ album: Album) {
|
||||||
guard let imagePath = album.coverArtFilePath else { return }
|
guard let song = album.mpdAlbum.firstSong
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
let provider = MPDAlbumArtImageDataProvider(
|
||||||
|
songUri: song.uriString,
|
||||||
|
cacheKey: album.hash
|
||||||
|
)
|
||||||
|
|
||||||
let imageURL = URL(fileURLWithPath: imagePath)
|
|
||||||
let provider = LocalFileImageDataProvider(fileURL: imageURL)
|
|
||||||
albumCoverView.kf.setImage(
|
albumCoverView.kf.setImage(
|
||||||
with: .provider(provider),
|
with: .provider(provider),
|
||||||
placeholder: NSImage.defaultCoverArt,
|
placeholder: NSImage.defaultCoverArt,
|
||||||
@ -67,7 +71,34 @@ class AlbumViewItem: NSCollectionViewItem {
|
|||||||
.processor(DownsamplingImageProcessor(size: .albumListCoverSize)),
|
.processor(DownsamplingImageProcessor(size: .albumListCoverSize)),
|
||||||
.scaleFactor(2),
|
.scaleFactor(2),
|
||||||
]
|
]
|
||||||
|
) { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let imageResult):
|
||||||
|
guard let imageData = imageResult.image.tiffRepresentation
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
let rawProvider = RawImageDataProvider(
|
||||||
|
data: imageData,
|
||||||
|
cacheKey: album.hash
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.cacheSmallCover(provider: rawProvider)
|
||||||
|
|
||||||
|
case .failure(_):
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cacheSmallCover(provider: ImageDataProvider) {
|
||||||
|
_ = KingfisherManager.shared.retrieveImage(
|
||||||
|
with: .provider(provider),
|
||||||
|
options: [
|
||||||
|
.memoryCacheExpiration(.never),
|
||||||
|
.processor(DownsamplingImageProcessor(size: .queueSongCoverSize)),
|
||||||
|
.scaleFactor(2),
|
||||||
|
]
|
||||||
|
) { result in }
|
||||||
}
|
}
|
||||||
|
|
||||||
func setAppearance(selected isSelected: Bool) {
|
func setAppearance(selected isSelected: Bool) {
|
||||||
@ -89,6 +120,18 @@ class AlbumViewItem: NSCollectionViewItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func refreshAlbumArt() {
|
||||||
|
guard let album = album,
|
||||||
|
let mpdSong = album.mpdAlbum.firstSong
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
let song = Song(mpdSong: mpdSong)
|
||||||
|
|
||||||
|
CoverArtService(song: song).refresh {
|
||||||
|
self.setAlbumCover(album)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@IBAction func showAlbumDetail(_ sender: NSButton) {
|
@IBAction func showAlbumDetail(_ sender: NSButton) {
|
||||||
guard let album = album else { return }
|
guard let album = album else { return }
|
||||||
|
|
||||||
@ -121,4 +164,8 @@ class AlbumViewItem: NSCollectionViewItem {
|
|||||||
|
|
||||||
App.mpdClient.addAlbumToQueue(album: album.mpdAlbum, at: queueLength)
|
App.mpdClient.addAlbumToQueue(album: album.mpdAlbum, at: queueLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func refreshAlbumArtMenuAction(_ sender: NSMenuItem) {
|
||||||
|
refreshAlbumArt()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
</shadow>
|
</shadow>
|
||||||
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="defaultCoverArt" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" refusesFirstResponder="YES" state="on" transparent="YES" imageScaling="proportionallyUpOrDown" inset="2" id="t8A-Hz-L38">
|
<buttonCell key="cell" type="square" bezelStyle="shadowlessSquare" image="defaultCoverArt" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" refusesFirstResponder="YES" state="on" transparent="YES" imageScaling="proportionallyUpOrDown" inset="2" id="t8A-Hz-L38">
|
||||||
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
<font key="font" metaFont="label" size="13"/>
|
<font key="font" metaFont="system"/>
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="showAlbumDetail:" target="-2" id="A4Q-gb-B45"/>
|
<action selector="showAlbumDetail:" target="-2" id="A4Q-gb-B45"/>
|
||||||
@ -54,7 +54,7 @@
|
|||||||
<textField identifier="albumTitle" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEh-NL-c2W">
|
<textField identifier="albumTitle" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="KEh-NL-c2W">
|
||||||
<rect key="frame" x="8" y="28" width="142" height="17"/>
|
<rect key="frame" x="8" y="28" width="142" height="17"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" title="Label" id="pDs-0t-e1j">
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" title="Label" id="pDs-0t-e1j">
|
||||||
<font key="font" metaFont="label" size="13"/>
|
<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>
|
||||||
@ -62,7 +62,7 @@
|
|||||||
<textField identifier="albumArtist" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5Uu-j1-qyT">
|
<textField identifier="albumArtist" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="5Uu-j1-qyT">
|
||||||
<rect key="frame" x="8" y="10" width="142" height="16"/>
|
<rect key="frame" x="8" y="10" width="142" height="16"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" title="Label" id="yZn-e9-zyP">
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" title="Label" id="yZn-e9-zyP">
|
||||||
<font key="font" metaFont="label" size="13"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
@ -98,6 +98,13 @@
|
|||||||
<action selector="addAlbumToQueueMenuAction:" target="-2" id="6wW-oR-ykh"/>
|
<action selector="addAlbumToQueueMenuAction:" target="-2" id="6wW-oR-ykh"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
|
<menuItem isSeparatorItem="YES" id="g9v-9R-xDB"/>
|
||||||
|
<menuItem title="Refresh album art" id="4Ld-db-wka">
|
||||||
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="refreshAlbumArtMenuAction:" target="-2" id="wyO-Dn-k9r"/>
|
||||||
|
</connections>
|
||||||
|
</menuItem>
|
||||||
</items>
|
</items>
|
||||||
<point key="canvasLocation" x="191" y="38"/>
|
<point key="canvasLocation" x="191" y="38"/>
|
||||||
</menu>
|
</menu>
|
||||||
|
|||||||
@ -116,27 +116,27 @@ class AlbumDetailView: NSViewController {
|
|||||||
App.mpdClient.getAlbumSongs(for: album.mpdAlbum) { [weak self] (mpdSongs: [MPDClient.MPDSong]) in
|
App.mpdClient.getAlbumSongs(for: album.mpdAlbum) { [weak self] (mpdSongs: [MPDClient.MPDSong]) in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
|
DispatchQueue.main.async {
|
||||||
self.dataSource.setAlbumSongs(
|
self.dataSource.setAlbumSongs(
|
||||||
mpdSongs.map { Song(mpdSong: $0) }
|
mpdSongs.map { Song(mpdSong: $0) }
|
||||||
)
|
)
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.albumTracksView.reloadData()
|
self.albumTracksView.reloadData()
|
||||||
}
|
|
||||||
|
|
||||||
guard let song = self.dataSource.albumSongs.first?.song ??
|
guard let mpdSong = album.mpdAlbum.firstSong
|
||||||
self.dataSource.albumSongs[1].song
|
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
self.getBigCoverArt(song: song, album: album)
|
self.getBigCoverArt(song: Song(mpdSong: mpdSong), album: album)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBigCoverArt(song: Song, album: Album) {
|
func getBigCoverArt(song: Song, album: Album) {
|
||||||
guard let imagePath = album.coverArtFilePath else { return }
|
let provider = MPDAlbumArtImageDataProvider(
|
||||||
|
songUri: song.mpdSong.uriString,
|
||||||
|
cacheKey: album.hash
|
||||||
|
)
|
||||||
|
|
||||||
let imageURL = URL(fileURLWithPath: imagePath)
|
|
||||||
let provider = LocalFileImageDataProvider(fileURL: imageURL)
|
|
||||||
albumCoverView.kf.setImage(
|
albumCoverView.kf.setImage(
|
||||||
with: .provider(provider),
|
with: .provider(provider),
|
||||||
placeholder: NSImage.defaultCoverArt,
|
placeholder: NSImage.defaultCoverArt,
|
||||||
@ -145,18 +145,6 @@ class AlbumDetailView: NSViewController {
|
|||||||
.scaleFactor(2),
|
.scaleFactor(2),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
||||||
cacheSmallCover(provider: provider)
|
|
||||||
}
|
|
||||||
|
|
||||||
func cacheSmallCover(provider: ImageDataProvider) {
|
|
||||||
_ = KingfisherManager.shared.retrieveImage(
|
|
||||||
with: .provider(provider),
|
|
||||||
options: [
|
|
||||||
.processor(DownsamplingImageProcessor(size: .queueSongCoverSize)),
|
|
||||||
.scaleFactor(2),
|
|
||||||
]
|
|
||||||
) { result in }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setAppearance() {
|
func setAppearance() {
|
||||||
|
|||||||
@ -50,7 +50,8 @@ class AlbumTracksDataSource: NSObject, NSTableViewDataSource {
|
|||||||
type: .albumSongItem(song.mpdSong.uriString),
|
type: .albumSongItem(song.mpdSong.uriString),
|
||||||
title: song.title,
|
title: song.title,
|
||||||
artist: song.artist,
|
artist: song.artist,
|
||||||
cover: song.album.coverArtFilePath
|
album: song.album.title,
|
||||||
|
uri: song.mpdSong.uriString
|
||||||
),
|
),
|
||||||
ofType: .songPasteboardType
|
ofType: .songPasteboardType
|
||||||
)
|
)
|
||||||
@ -69,10 +70,11 @@ class AlbumTracksDataSource: NSObject, NSTableViewDataSource {
|
|||||||
) { draggingItem, index, stop in
|
) { draggingItem, index, stop in
|
||||||
guard let item = draggingItem.item as? NSPasteboardItem,
|
guard let item = draggingItem.item as? NSPasteboardItem,
|
||||||
let draggedSong = item.draggedSong(forType: .songPasteboardType),
|
let draggedSong = item.draggedSong(forType: .songPasteboardType),
|
||||||
case let (title?, artist?, cover?) = (
|
case let (title, artist, album, uri) = (
|
||||||
draggedSong.title,
|
draggedSong.title,
|
||||||
draggedSong.artist,
|
draggedSong.artist,
|
||||||
draggedSong.cover
|
draggedSong.album,
|
||||||
|
draggedSong.uri
|
||||||
)
|
)
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
@ -81,7 +83,8 @@ class AlbumTracksDataSource: NSObject, NSTableViewDataSource {
|
|||||||
let draggedSongView = DraggedSongView(
|
let draggedSongView = DraggedSongView(
|
||||||
title: title,
|
title: title,
|
||||||
artist: artist,
|
artist: artist,
|
||||||
cover: cover
|
album: album,
|
||||||
|
uri: uri
|
||||||
)
|
)
|
||||||
|
|
||||||
component.contents = draggedSongView.view.image()
|
component.contents = draggedSongView.view.image()
|
||||||
|
|||||||
@ -7,15 +7,12 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import AppKit
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
class CoverArtPrefsController: NSViewController {
|
class CoverArtPrefsController: NSViewController {
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
if let mpdLibraryDir = App.store.state.preferencesState.mpdLibraryDir {
|
|
||||||
mpdLibraryDirField.stringValue = mpdLibraryDir
|
|
||||||
}
|
|
||||||
|
|
||||||
if App.store.state.preferencesState.fetchMissingArtworkFromInternet {
|
if App.store.state.preferencesState.fetchMissingArtworkFromInternet {
|
||||||
fetchMissingArtworkFromInternet.state = .on
|
fetchMissingArtworkFromInternet.state = .on
|
||||||
} else {
|
} else {
|
||||||
@ -33,12 +30,6 @@ class CoverArtPrefsController: NSViewController {
|
|||||||
self.parent?.view.window?.title = title
|
self.parent?.view.window?.title = title
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func updateMpdLibraryDir(_ sender: NSTextField) {
|
|
||||||
App.store.dispatch(UpdateMPDLibraryDir(mpdLibraryDir: sender.stringValue))
|
|
||||||
}
|
|
||||||
|
|
||||||
@IBOutlet var mpdLibraryDirField: NSTextField!
|
|
||||||
|
|
||||||
@IBAction func updateFetchMissingArtworkFromInternet(_ sender: NSButton) {
|
@IBAction func updateFetchMissingArtworkFromInternet(_ sender: NSButton) {
|
||||||
App.store.dispatch(
|
App.store.dispatch(
|
||||||
UpdateFetchMissingArtworkFromInternet(
|
UpdateFetchMissingArtworkFromInternet(
|
||||||
@ -47,5 +38,10 @@ class CoverArtPrefsController: NSViewController {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func clearAlbumArtCache(_ sender: NSButton) {
|
||||||
|
KingfisherManager.shared.cache.clearDiskCache()
|
||||||
|
KingfisherManager.shared.cache.clearMemoryCache()
|
||||||
|
}
|
||||||
|
|
||||||
@IBOutlet var fetchMissingArtworkFromInternet: NSButton!
|
@IBOutlet var fetchMissingArtworkFromInternet: NSButton!
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,13 +17,23 @@ class CurrentCoverArtView: NSImageView {
|
|||||||
App.store.subscribe(self) {
|
App.store.subscribe(self) {
|
||||||
$0.select { $0.playerState.currentSong }
|
$0.select { $0.playerState.currentSong }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(didReloadAlbumArt), name: .didReloadAlbumArt, object: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setAlbumImage(_ album: Album) {
|
@objc func didReloadAlbumArt() {
|
||||||
guard let imagePath = album.coverArtFilePath else { return }
|
guard let song = App.store.state.playerState.currentSong
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
setSongImage(song)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setSongImage(_ song: Song) {
|
||||||
|
let provider = MPDAlbumArtImageDataProvider(
|
||||||
|
songUri: song.mpdSong.uriString,
|
||||||
|
cacheKey: song.album.hash
|
||||||
|
)
|
||||||
|
|
||||||
let imageURL = URL(fileURLWithPath: imagePath)
|
|
||||||
let provider = LocalFileImageDataProvider(fileURL: imageURL)
|
|
||||||
kf.setImage(
|
kf.setImage(
|
||||||
with: .provider(provider),
|
with: .provider(provider),
|
||||||
placeholder: NSImage.defaultCoverArt,
|
placeholder: NSImage.defaultCoverArt,
|
||||||
@ -44,6 +54,6 @@ extension CurrentCoverArtView: StoreSubscriber {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
setAlbumImage(song.album)
|
setSongImage(song)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -44,7 +44,8 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
|
|||||||
type: .queueItem(queueItem.queuePos),
|
type: .queueItem(queueItem.queuePos),
|
||||||
title: queueItem.song.title,
|
title: queueItem.song.title,
|
||||||
artist: queueItem.song.artist,
|
artist: queueItem.song.artist,
|
||||||
cover: queueItem.song.album.coverArtFilePath
|
album: queueItem.song.album.title,
|
||||||
|
uri: queueItem.song.mpdSong.uriString
|
||||||
),
|
),
|
||||||
ofType: .songPasteboardType
|
ofType: .songPasteboardType
|
||||||
)
|
)
|
||||||
@ -128,10 +129,11 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
|
|||||||
guard let item = draggingItem.item as? NSPasteboardItem,
|
guard let item = draggingItem.item as? NSPasteboardItem,
|
||||||
let data = item.data(forType: .songPasteboardType),
|
let data = item.data(forType: .songPasteboardType),
|
||||||
let draggedSong = try? PropertyListDecoder().decode(DraggedSong.self, from: data),
|
let draggedSong = try? PropertyListDecoder().decode(DraggedSong.self, from: data),
|
||||||
case let (title?, artist?, cover?) = (
|
case let (title, artist, album, uri) = (
|
||||||
draggedSong.title,
|
draggedSong.title,
|
||||||
draggedSong.artist,
|
draggedSong.artist,
|
||||||
draggedSong.cover
|
draggedSong.album,
|
||||||
|
draggedSong.uri
|
||||||
)
|
)
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
@ -140,7 +142,8 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
|
|||||||
let draggedSongView = DraggedSongView(
|
let draggedSongView = DraggedSongView(
|
||||||
title: title,
|
title: title,
|
||||||
artist: artist,
|
artist: artist,
|
||||||
cover: cover
|
album: album,
|
||||||
|
uri: uri
|
||||||
)
|
)
|
||||||
|
|
||||||
let view = draggedSongView.view
|
let view = draggedSongView.view
|
||||||
|
|||||||
@ -60,13 +60,14 @@ class QueueSongCoverView: NSTableCellView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setSong(_ queueItem: QueueItem, queueIcon: NSImage?) {
|
func setSong(_ queueItem: QueueItem, queueIcon: NSImage?) {
|
||||||
guard let imagePath = queueItem.song.album.coverArtFilePath
|
let song = queueItem.song
|
||||||
else { return }
|
|
||||||
|
|
||||||
isPlaying = queueItem.isPlaying
|
isPlaying = queueItem.isPlaying
|
||||||
|
|
||||||
let imageURL = URL(fileURLWithPath: imagePath)
|
let provider = MPDAlbumArtImageDataProvider(
|
||||||
let provider = LocalFileImageDataProvider(fileURL: imageURL)
|
songUri: song.mpdSong.uriString,
|
||||||
|
cacheKey: song.album.hash
|
||||||
|
)
|
||||||
|
|
||||||
queueSongCover.kf.setImage(
|
queueSongCover.kf.setImage(
|
||||||
with: .provider(provider),
|
with: .provider(provider),
|
||||||
|
|||||||
@ -24,6 +24,7 @@ class QueueViewController: NSViewController {
|
|||||||
|
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(didConnect), name: .didConnect, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(didConnect), name: .didConnect, object: nil)
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(willDisconnect), name: .willDisconnect, object: nil)
|
NotificationCenter.default.addObserver(self, selector: #selector(willDisconnect), name: .willDisconnect, object: nil)
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(didReloadAlbumArt), name: .didReloadAlbumArt, object: nil)
|
||||||
|
|
||||||
queueView.dataSource = dataSource
|
queueView.dataSource = dataSource
|
||||||
queueView.columnAutoresizingStyle = .sequentialColumnAutoresizingStyle
|
queueView.columnAutoresizingStyle = .sequentialColumnAutoresizingStyle
|
||||||
@ -62,6 +63,10 @@ class QueueViewController: NSViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func didReloadAlbumArt() {
|
||||||
|
queueView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
@IBAction func playTrack(_ sender: Any) {
|
@IBAction func playTrack(_ sender: Any) {
|
||||||
let queuePos = queueView.selectedRow
|
let queuePos = queueView.selectedRow
|
||||||
|
|
||||||
|
|||||||
@ -16,12 +16,14 @@ class DraggedSongView: NSViewController {
|
|||||||
|
|
||||||
private let songTitle: String
|
private let songTitle: String
|
||||||
private let songArtist: String
|
private let songArtist: String
|
||||||
private let songCover: String?
|
private let songAlbum: String
|
||||||
|
private let songUri: String
|
||||||
|
|
||||||
init(title: String, artist: String, cover: String? = nil) {
|
init(title: String, artist: String, album: String, uri: String) {
|
||||||
songTitle = title
|
songTitle = title
|
||||||
songArtist = artist
|
songArtist = artist
|
||||||
songCover = cover
|
songAlbum = album
|
||||||
|
songUri = uri
|
||||||
|
|
||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
}
|
}
|
||||||
@ -58,10 +60,12 @@ class DraggedSongView: NSViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setCoverArt() {
|
func setCoverArt() {
|
||||||
guard let imagePath = songCover else { return }
|
let mpdAlbum = MPDClient.MPDAlbum(title: songAlbum, artist: songArtist)
|
||||||
|
|
||||||
let imageURL = URL(fileURLWithPath: imagePath)
|
let provider = MPDAlbumArtImageDataProvider(
|
||||||
let provider = LocalFileImageDataProvider(fileURL: imageURL)
|
songUri: songUri,
|
||||||
|
cacheKey: Album(mpdAlbum: mpdAlbum).hash
|
||||||
|
)
|
||||||
|
|
||||||
coverImage.kf.setImage(
|
coverImage.kf.setImage(
|
||||||
with: .provider(provider),
|
with: .provider(provider),
|
||||||
|
|||||||
@ -16,33 +16,9 @@ extension NSImage {
|
|||||||
|
|
||||||
static let defaultCoverArt = NSImage(named: "defaultCoverArt")
|
static let defaultCoverArt = NSImage(named: "defaultCoverArt")
|
||||||
|
|
||||||
func toFitBox(size: NSSize) -> NSImage {
|
static let speakerDisabled = NSImage(named: "speakerDisabled")
|
||||||
var newSize: NSSize = NSSize.zero
|
static let speakerOff = NSImage(named: "speakerOff")
|
||||||
let aspectRatio = self.size.width / self.size.height
|
static let speakerLow = NSImage(named: "speakerLow")
|
||||||
let boxAspectRatio = size.width / size.height
|
static let speakerMid = NSImage(named: "speakerMid")
|
||||||
|
static let speakerHigh = NSImage(named: "speakerHigh")
|
||||||
if aspectRatio > boxAspectRatio {
|
|
||||||
newSize = NSSize(width: size.width, height: size.width / aspectRatio)
|
|
||||||
} else {
|
|
||||||
newSize = NSSize(width: size.height * aspectRatio, height: size.height)
|
|
||||||
}
|
|
||||||
|
|
||||||
let newImage = NSImage(size: newSize)
|
|
||||||
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]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,4 +11,5 @@ import Foundation
|
|||||||
extension Notification.Name {
|
extension Notification.Name {
|
||||||
static let didConnect = Notification.Name("MPDClientDidConnect")
|
static let didConnect = Notification.Name("MPDClientDidConnect")
|
||||||
static let willDisconnect = Notification.Name("MPDClientWillDisconnect")
|
static let willDisconnect = Notification.Name("MPDClientWillDisconnect")
|
||||||
|
static let didReloadAlbumArt = Notification.Name("MPDDidReloadAlbumArt")
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// MPDAlbumArtImageDataProvider.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2/1/20.
|
||||||
|
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
|
public struct MPDAlbumArtImageDataProvider: ImageDataProvider {
|
||||||
|
let songUri: String
|
||||||
|
|
||||||
|
init(songUri: String, cacheKey: String) {
|
||||||
|
self.songUri = songUri
|
||||||
|
self.cacheKey = cacheKey
|
||||||
|
}
|
||||||
|
|
||||||
|
public var cacheKey: String
|
||||||
|
|
||||||
|
public func data(handler: @escaping (Result<Data, Error>) -> Void) {
|
||||||
|
App.mpdClient.fetchAlbumArt(songUri: songUri, imageData: nil) { imageData in
|
||||||
|
guard let imageData = imageData
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
handler(.success(imageData))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public var contentURL: String? {
|
||||||
|
return songUri
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -18,14 +18,16 @@ class UserNotificationsController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func notifyTrack(_ state: Song?) {
|
func notifyTrack(_ state: Song?) {
|
||||||
guard let currentSong = state,
|
guard let song = state,
|
||||||
let coverArtFilePath = currentSong.album.coverArtFilePath,
|
|
||||||
let status = App.mpdClient.status,
|
let status = App.mpdClient.status,
|
||||||
status.state == .playing
|
status.state == .playing
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
let imageURL = URL(fileURLWithPath: coverArtFilePath)
|
let provider = MPDAlbumArtImageDataProvider(
|
||||||
let provider = LocalFileImageDataProvider(fileURL: imageURL)
|
songUri: song.mpdSong.uriString,
|
||||||
|
cacheKey: song.album.hash
|
||||||
|
)
|
||||||
|
|
||||||
_ = KingfisherManager.shared.retrieveImage(
|
_ = KingfisherManager.shared.retrieveImage(
|
||||||
with: .provider(provider),
|
with: .provider(provider),
|
||||||
options: [
|
options: [
|
||||||
@ -35,7 +37,7 @@ class UserNotificationsController {
|
|||||||
) { result in
|
) { result in
|
||||||
switch result {
|
switch result {
|
||||||
case .success(let value):
|
case .success(let value):
|
||||||
SongNotifierService(song: currentSong, image: value.image)
|
SongNotifierService(song: song, image: value.image)
|
||||||
.deliver()
|
.deliver()
|
||||||
case .failure:
|
case .failure:
|
||||||
break
|
break
|
||||||
|
|||||||
45
Persephone/Components/VolumeControl/VolumeControlView.swift
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// VolumeControlView.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2/16/20.
|
||||||
|
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
import ReSwift
|
||||||
|
|
||||||
|
class VolumeControlView: NSViewController {
|
||||||
|
static let shared = VolumeControlView()
|
||||||
|
static let popover = NSPopover()
|
||||||
|
|
||||||
|
var currentVolume: Int = 0
|
||||||
|
|
||||||
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
App.store.subscribe(self) {
|
||||||
|
$0.select { $0.playerState }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func volumeSliderAction(_ sender: NSSlider) {
|
||||||
|
let newVolume = sender.integerValue
|
||||||
|
|
||||||
|
if newVolume != currentVolume {
|
||||||
|
App.mpdClient.setVolume(to: newVolume)
|
||||||
|
currentVolume = newVolume
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBOutlet var volumeSlider: NSSlider!
|
||||||
|
}
|
||||||
|
|
||||||
|
extension VolumeControlView: StoreSubscriber {
|
||||||
|
typealias StoreSubscriberStateType = PlayerState
|
||||||
|
|
||||||
|
func newState(state: StoreSubscriberStateType) {
|
||||||
|
volumeSlider.integerValue = state.volume
|
||||||
|
currentVolume = state.volume
|
||||||
|
}
|
||||||
|
}
|
||||||
33
Persephone/Components/VolumeControl/VolumeControlView.xib
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="15705" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
|
||||||
|
<dependencies>
|
||||||
|
<deployment identifier="macosx"/>
|
||||||
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15705"/>
|
||||||
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
|
</dependencies>
|
||||||
|
<objects>
|
||||||
|
<customObject id="-2" userLabel="File's Owner" customClass="VolumeControlView" customModule="Persephone" customModuleProvider="target">
|
||||||
|
<connections>
|
||||||
|
<outlet property="view" destination="Hz6-mo-xeY" id="0bl-1N-x8E"/>
|
||||||
|
<outlet property="volumeSlider" destination="E78-vZ-qV0" id="7qP-Fm-MDY"/>
|
||||||
|
</connections>
|
||||||
|
</customObject>
|
||||||
|
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
|
||||||
|
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
|
||||||
|
<customView id="Hz6-mo-xeY">
|
||||||
|
<rect key="frame" x="0.0" y="0.0" width="36" height="145"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<subviews>
|
||||||
|
<slider horizontalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="E78-vZ-qV0" userLabel="Volume Slider">
|
||||||
|
<rect key="frame" x="9" y="12" width="19" height="121"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<sliderCell key="cell" continuous="YES" alignment="left" maxValue="100" doubleValue="50" tickMarkPosition="right" sliderType="linear" id="22u-9w-IXT"/>
|
||||||
|
<connections>
|
||||||
|
<action selector="volumeSliderAction:" target="-2" id="1yh-vH-sgN"/>
|
||||||
|
</connections>
|
||||||
|
</slider>
|
||||||
|
</subviews>
|
||||||
|
<point key="canvasLocation" x="-83" y="90.5"/>
|
||||||
|
</customView>
|
||||||
|
</objects>
|
||||||
|
</document>
|
||||||
@ -208,7 +208,7 @@
|
|||||||
<rect key="frame" x="0.0" y="14" width="153" height="24"/>
|
<rect key="frame" x="0.0" y="14" width="153" height="24"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<segmentedCell key="cell" borderStyle="border" alignment="left" style="texturedRounded" trackingMode="momentary" id="EBk-sD-nG7">
|
<segmentedCell key="cell" borderStyle="border" alignment="left" style="texturedRounded" trackingMode="momentary" id="EBk-sD-nG7">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
<segments>
|
<segments>
|
||||||
<segment image="prevTrackButton" width="32" enabled="NO"/>
|
<segment image="prevTrackButton" width="32" enabled="NO"/>
|
||||||
<segment image="playButton" width="48" enabled="NO" tag="1"/>
|
<segment image="playButton" width="48" enabled="NO" tag="1"/>
|
||||||
@ -230,7 +230,7 @@
|
|||||||
<rect key="frame" x="16" y="14" width="55" height="17"/>
|
<rect key="frame" x="16" y="14" width="55" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" placeholderString="8:88:88" id="g0c-k5-wCA">
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" placeholderString="8:88:88" id="g0c-k5-wCA">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
<color key="textColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
@ -257,7 +257,7 @@
|
|||||||
<rect key="frame" x="16" y="14" width="60" height="17"/>
|
<rect key="frame" x="16" y="14" width="60" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="left" placeholderString="-8:88:88" id="XUa-pD-s5c">
|
<textFieldCell key="cell" lineBreakMode="clipping" alignment="left" placeholderString="-8:88:88" id="XUa-pD-s5c">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
<color key="textColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
@ -277,9 +277,9 @@
|
|||||||
<button key="view" verticalHuggingPriority="750" id="E8L-uK-XT0">
|
<button key="view" verticalHuggingPriority="750" id="E8L-uK-XT0">
|
||||||
<rect key="frame" x="2" y="14" width="42" height="24"/>
|
<rect key="frame" x="2" y="14" width="42" height="24"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="shuffleButton" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" borderStyle="border" inset="2" id="YNb-hd-ax8">
|
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="shuffleButton" imagePosition="overlaps" alignment="center" lineBreakMode="truncatingTail" borderStyle="border" inset="2" id="YNb-hd-ax8">
|
||||||
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
|
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="handleShuffleButton:" target="B8D-0N-5wS" id="THd-0g-fmb"/>
|
<action selector="handleShuffleButton:" target="B8D-0N-5wS" id="THd-0g-fmb"/>
|
||||||
@ -295,7 +295,7 @@
|
|||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="repeatButton" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" borderStyle="border" inset="2" id="1bu-vK-3Hb">
|
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="repeatButton" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" borderStyle="border" inset="2" id="1bu-vK-3Hb">
|
||||||
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
|
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="handleRepeatButton:" target="B8D-0N-5wS" id="EN2-u4-DNl"/>
|
<action selector="handleRepeatButton:" target="B8D-0N-5wS" id="EN2-u4-DNl"/>
|
||||||
@ -308,7 +308,7 @@
|
|||||||
<rect key="frame" x="0.0" y="14" width="96" height="22"/>
|
<rect key="frame" x="0.0" y="14" width="96" height="22"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" usesSingleLineMode="YES" bezelStyle="round" id="F3N-3P-tS3">
|
<searchFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" borderStyle="bezel" usesSingleLineMode="YES" bezelStyle="round" id="F3N-3P-tS3">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</searchFieldCell>
|
</searchFieldCell>
|
||||||
@ -317,6 +317,20 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</searchField>
|
</searchField>
|
||||||
</toolbarItem>
|
</toolbarItem>
|
||||||
|
<toolbarItem implicitItemIdentifier="01E8DA80-2BDE-49F9-B311-69876CF0AE8E" label="Volume" paletteLabel="Volume" image="speakerHigh" sizingBehavior="auto" id="cMg-Mj-j7q">
|
||||||
|
<nil key="toolTip"/>
|
||||||
|
<button key="view" verticalHuggingPriority="750" id="cfN-LI-Cab">
|
||||||
|
<rect key="frame" x="2" y="14" width="42" height="24"/>
|
||||||
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
|
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="speakerHigh" imagePosition="overlaps" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="bJh-X9-7q0">
|
||||||
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
|
<font key="font" metaFont="label" size="13"/>
|
||||||
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="showVolumeControl:" target="B8D-0N-5wS" id="UoW-fa-jBM"/>
|
||||||
|
</connections>
|
||||||
|
</button>
|
||||||
|
</toolbarItem>
|
||||||
</allowedToolbarItems>
|
</allowedToolbarItems>
|
||||||
<defaultToolbarItems>
|
<defaultToolbarItems>
|
||||||
<toolbarItem reference="p3r-ty-Pxf"/>
|
<toolbarItem reference="p3r-ty-Pxf"/>
|
||||||
@ -329,6 +343,8 @@
|
|||||||
<toolbarItem reference="s1h-EC-nvL"/>
|
<toolbarItem reference="s1h-EC-nvL"/>
|
||||||
<toolbarItem reference="5U7-UV-xn2"/>
|
<toolbarItem reference="5U7-UV-xn2"/>
|
||||||
<toolbarItem reference="9ol-aR-mzv"/>
|
<toolbarItem reference="9ol-aR-mzv"/>
|
||||||
|
<toolbarItem reference="cMg-Mj-j7q"/>
|
||||||
|
<toolbarItem reference="mhg-16-CNM"/>
|
||||||
<toolbarItem reference="FRe-rR-Ulo"/>
|
<toolbarItem reference="FRe-rR-Ulo"/>
|
||||||
</defaultToolbarItems>
|
</defaultToolbarItems>
|
||||||
</toolbar>
|
</toolbar>
|
||||||
@ -345,6 +361,7 @@
|
|||||||
<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"/>
|
||||||
<outlet property="transportControls" destination="EBk-sD-nG7" id="yOo-58-Fby"/>
|
<outlet property="transportControls" destination="EBk-sD-nG7" id="yOo-58-Fby"/>
|
||||||
|
<outlet property="volumeState" destination="cfN-LI-Cab" id="hrE-SY-J9E"/>
|
||||||
<segue destination="fnD-7K-pHK" kind="relationship" relationship="window.shadowedContentViewController" id="fQQ-kB-KVc"/>
|
<segue destination="fnD-7K-pHK" kind="relationship" relationship="window.shadowedContentViewController" id="fQQ-kB-KVc"/>
|
||||||
</connections>
|
</connections>
|
||||||
</windowController>
|
</windowController>
|
||||||
@ -409,7 +426,7 @@
|
|||||||
<tabView key="tabView" type="noTabsNoBorder" id="6dC-M0-oC5">
|
<tabView key="tabView" type="noTabsNoBorder" id="6dC-M0-oC5">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="418" height="300"/>
|
<rect key="frame" x="0.0" y="0.0" width="418" height="300"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="delegate" destination="zhe-qh-Mal" id="LUL-qN-JlP"/>
|
<outlet property="delegate" destination="zhe-qh-Mal" id="LUL-qN-JlP"/>
|
||||||
</connections>
|
</connections>
|
||||||
@ -429,93 +446,47 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<viewController title="Cover Art" id="3C9-vU-zjZ" userLabel="Cover Art" customClass="CoverArtPrefsController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController title="Cover Art" id="3C9-vU-zjZ" userLabel="Cover Art" customClass="CoverArtPrefsController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" id="PyK-v2-kus">
|
<view key="view" id="PyK-v2-kus">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="524" height="168"/>
|
<rect key="frame" x="0.0" y="0.0" width="420" height="103"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zZn-Rm-e1f">
|
|
||||||
<rect key="frame" x="53" y="129" width="104" height="16"/>
|
|
||||||
<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="125" width="288" height="21"/>
|
|
||||||
<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">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pRL-MG-1Be">
|
||||||
<rect key="frame" x="160" y="94" width="264" height="18"/>
|
<rect key="frame" x="82" y="67" width="264" height="18"/>
|
||||||
|
<constraints>
|
||||||
|
<constraint firstAttribute="width" constant="260" id="PJN-iZ-3RV"/>
|
||||||
|
</constraints>
|
||||||
<buttonCell key="cell" type="check" title="Fetch missing artwork from MusicBrainz" bezelStyle="regularSquare" imagePosition="left" enabled="NO" inset="2" id="LpD-Ew-HMd">
|
<buttonCell key="cell" type="check" title="Fetch missing artwork from MusicBrainz" bezelStyle="regularSquare" imagePosition="left" enabled="NO" inset="2" id="LpD-Ew-HMd">
|
||||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="updateFetchMissingArtworkFromInternet:" target="3C9-vU-zjZ" id="I7x-9V-xJr"/>
|
<action selector="updateFetchMissingArtworkFromInternet:" target="3C9-vU-zjZ" id="I7x-9V-xJr"/>
|
||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="z1g-nP-ksw">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mXh-kY-tMC">
|
||||||
<rect key="frame" x="160" y="63" width="264" height="18"/>
|
<rect key="frame" x="78" y="21" width="185" height="32"/>
|
||||||
<constraints>
|
<buttonCell key="cell" type="push" title="Clear album art cache..." bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="l81-SG-7mf">
|
||||||
<constraint firstAttribute="width" constant="260" id="gK0-aW-CJy"/>
|
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
|
||||||
</constraints>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
<buttonCell key="cell" type="check" title="Save fetched artwork to music directory" bezelStyle="regularSquare" imagePosition="left" enabled="NO" inset="2" id="ZeZ-O4-vjS">
|
|
||||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="clearAlbumArtCache:" target="3C9-vU-zjZ" id="tXg-rz-lvh"/>
|
||||||
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xgS-Kg-8KR">
|
|
||||||
<rect key="frame" x="162" y="26" width="144" height="21"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="width" constant="144" id="DSX-th-Wn1"/>
|
|
||||||
</constraints>
|
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" enabled="NO" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="folder.jpg" drawsBackground="YES" id="nKF-YI-xBL">
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="SmH-w6-5QI">
|
|
||||||
<rect key="frame" x="37" y="30" width="119" height="16"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" enabled="NO" alignment="right" title="Cover art filename:" id="b4u-u7-iWD">
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="pRL-MG-1Be" firstAttribute="centerX" secondItem="z1g-nP-ksw" secondAttribute="centerX" id="0Ev-Ia-XBO"/>
|
<constraint firstItem="pRL-MG-1Be" firstAttribute="leading" secondItem="PyK-v2-kus" secondAttribute="leading" constant="84" id="81s-YL-o8A"/>
|
||||||
<constraint firstItem="gDk-ca-eOa" firstAttribute="leading" secondItem="pRL-MG-1Be" secondAttribute="leading" id="1jd-wZ-a4Q"/>
|
<constraint firstAttribute="bottom" secondItem="pRL-MG-1Be" secondAttribute="bottom" constant="69" id="L7b-jE-keG"/>
|
||||||
<constraint firstItem="pRL-MG-1Be" firstAttribute="leading" secondItem="z1g-nP-ksw" secondAttribute="leading" id="3tZ-Ub-RaT"/>
|
<constraint firstItem="mXh-kY-tMC" firstAttribute="leading" secondItem="pRL-MG-1Be" secondAttribute="leading" id="b9X-hO-aYJ"/>
|
||||||
<constraint firstItem="gDk-ca-eOa" firstAttribute="leading" secondItem="zZn-Rm-e1f" secondAttribute="trailing" constant="7" id="C0m-yx-gXh"/>
|
<constraint firstAttribute="bottom" secondItem="mXh-kY-tMC" secondAttribute="bottom" constant="28" id="ras-nE-Oq8"/>
|
||||||
<constraint firstItem="z1g-nP-ksw" firstAttribute="leading" secondItem="xgS-Kg-8KR" secondAttribute="leading" id="Dkv-ai-X2G"/>
|
|
||||||
<constraint firstItem="xgS-Kg-8KR" firstAttribute="leading" secondItem="SmH-w6-5QI" secondAttribute="trailing" constant="8" symbolic="YES" id="KQk-nr-H8y"/>
|
|
||||||
<constraint firstItem="zZn-Rm-e1f" firstAttribute="leading" secondItem="PyK-v2-kus" secondAttribute="leading" constant="55" id="OzK-MR-zuB"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="SmH-w6-5QI" secondAttribute="bottom" constant="30" id="aBY-Ny-jPe"/>
|
|
||||||
<constraint firstItem="pRL-MG-1Be" firstAttribute="top" secondItem="gDk-ca-eOa" secondAttribute="bottom" constant="15" id="dKy-uC-r43"/>
|
|
||||||
<constraint firstItem="xgS-Kg-8KR" firstAttribute="top" secondItem="z1g-nP-ksw" secondAttribute="bottom" constant="18" id="lfR-Im-bd4"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="gDk-ca-eOa" secondAttribute="trailing" constant="74" id="n8X-T2-tXA"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="xgS-Kg-8KR" secondAttribute="bottom" constant="26" id="oXZ-qo-HwX"/>
|
|
||||||
<constraint firstItem="SmH-w6-5QI" firstAttribute="top" secondItem="zZn-Rm-e1f" secondAttribute="bottom" constant="83" id="qhC-mD-Bvw"/>
|
|
||||||
<constraint firstItem="z1g-nP-ksw" firstAttribute="top" secondItem="pRL-MG-1Be" secondAttribute="bottom" constant="17" id="sTP-hk-zfU"/>
|
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="fetchMissingArtworkFromInternet" destination="pRL-MG-1Be" id="Xcp-sb-iZm"/>
|
<outlet property="fetchMissingArtworkFromInternet" destination="pRL-MG-1Be" id="Xcp-sb-iZm"/>
|
||||||
<outlet property="mpdLibraryDirField" destination="gDk-ca-eOa" id="myi-BQ-0NS"/>
|
|
||||||
</connections>
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<customObject id="KzD-E3-lpA" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="KzD-E3-lpA" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="1459" y="-242"/>
|
<point key="canvasLocation" x="1407" y="-274.5"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--View Controller-->
|
<!--View Controller-->
|
||||||
<scene sceneID="VvW-vT-alQ">
|
<scene sceneID="VvW-vT-alQ">
|
||||||
@ -528,7 +499,7 @@
|
|||||||
<subviews>
|
<subviews>
|
||||||
<tabView type="noTabsNoBorder" initialItem="XgS-cX-SDH" translatesAutoresizingMaskIntoConstraints="NO" id="ARv-cj-xlz">
|
<tabView type="noTabsNoBorder" initialItem="XgS-cX-SDH" translatesAutoresizingMaskIntoConstraints="NO" id="ARv-cj-xlz">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="478" height="558"/>
|
<rect key="frame" x="0.0" y="0.0" width="478" height="558"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
<tabViewItems>
|
<tabViewItems>
|
||||||
<tabViewItem label="Albums" identifier="" id="XgS-cX-SDH">
|
<tabViewItem label="Albums" identifier="" id="XgS-cX-SDH">
|
||||||
<view key="view" id="hB7-hN-SbB">
|
<view key="view" id="hB7-hN-SbB">
|
||||||
@ -593,7 +564,7 @@
|
|||||||
<real key="minimum" value="0.0"/>
|
<real key="minimum" value="0.0"/>
|
||||||
<real key="maximum" value="65535"/>
|
<real key="maximum" value="65535"/>
|
||||||
</numberFormatter>
|
</numberFormatter>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
@ -604,7 +575,7 @@
|
|||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kvB-99-zwY">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kvB-99-zwY">
|
||||||
<rect key="frame" x="76" y="61" width="80" height="16"/>
|
<rect key="frame" x="76" y="61" width="80" height="16"/>
|
||||||
<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="label" size="13"/>
|
||||||
<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>
|
||||||
@ -615,7 +586,7 @@
|
|||||||
<constraint firstAttribute="width" constant="72" id="Of6-Ls-knP"/>
|
<constraint firstAttribute="width" constant="72" id="Of6-Ls-knP"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<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="label" size="13"/>
|
||||||
<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>
|
||||||
@ -647,18 +618,18 @@
|
|||||||
<objects>
|
<objects>
|
||||||
<viewController id="KIP-rq-4dM" customClass="QueueViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController id="KIP-rq-4dM" customClass="QueueViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<splitView key="view" misplaced="YES" dividerStyle="thin" id="84I-w3-Mxl">
|
<splitView key="view" misplaced="YES" dividerStyle="thin" id="84I-w3-Mxl">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="329" height="543"/>
|
<rect key="frame" x="0.0" y="0.0" width="329" height="498"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="42" horizontalPageScroll="10" verticalLineScroll="42" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="S3o-nF-NN7">
|
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="42" horizontalPageScroll="10" verticalLineScroll="42" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="S3o-nF-NN7">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="329" height="217"/>
|
<rect key="frame" x="0.0" y="0.0" width="329" height="198"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxY="YES"/>
|
||||||
<clipView key="contentView" drawsBackground="NO" id="WI8-Pw-03L">
|
<clipView key="contentView" drawsBackground="NO" id="WI8-Pw-03L">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="329" height="217"/>
|
<rect key="frame" x="0.0" y="0.0" width="329" height="198"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="sourceList" multipleSelection="NO" autosaveColumns="NO" rowHeight="42" rowSizeStyle="automatic" viewBased="YES" indentationMarkerFollowsCell="NO" outlineTableColumn="0Co-uF-CCB" id="jEJ-jg-fll">
|
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="sourceList" multipleSelection="NO" autosaveColumns="NO" rowHeight="42" rowSizeStyle="automatic" viewBased="YES" indentationMarkerFollowsCell="NO" outlineTableColumn="0Co-uF-CCB" id="jEJ-jg-fll">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="329" height="217"/>
|
<rect key="frame" x="0.0" y="0.0" width="329" height="198"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<size key="intercellSpacing" width="3" height="0.0"/>
|
<size key="intercellSpacing" width="3" height="0.0"/>
|
||||||
<color key="backgroundColor" name="_sourceListBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="_sourceListBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -666,12 +637,11 @@
|
|||||||
<tableColumns>
|
<tableColumns>
|
||||||
<tableColumn identifier="songCoverColumn" width="43" minWidth="42" maxWidth="1000" id="0Co-uF-CCB" userLabel="Position">
|
<tableColumn identifier="songCoverColumn" width="43" minWidth="42" maxWidth="1000" id="0Co-uF-CCB" userLabel="Position">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border">
|
||||||
<font key="font" metaFont="message" size="11"/>
|
|
||||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="headerColor" catalog="System" colorSpace="catalog"/>
|
||||||
</tableHeaderCell>
|
</tableHeaderCell>
|
||||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="99v-Rb-3kv">
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" title="Text Cell" id="99v-Rb-3kv">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
@ -712,12 +682,11 @@
|
|||||||
</tableColumn>
|
</tableColumn>
|
||||||
<tableColumn identifier="songInfoColumn" width="213" minWidth="10" maxWidth="3.4028234663852886e+38" id="HP0-ty-PFY" userLabel="Song Info">
|
<tableColumn identifier="songInfoColumn" width="213" minWidth="10" maxWidth="3.4028234663852886e+38" id="HP0-ty-PFY" userLabel="Song Info">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||||
<font key="font" metaFont="message" size="11"/>
|
|
||||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
</tableHeaderCell>
|
</tableHeaderCell>
|
||||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="zb2-QK-DhK">
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="zb2-QK-DhK">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
@ -738,7 +707,7 @@
|
|||||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="i0h-bn-auJ" userLabel="Song Title View">
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="i0h-bn-auJ" userLabel="Song Title View">
|
||||||
<rect key="frame" x="1" y="23" width="211" height="16"/>
|
<rect key="frame" x="1" y="23" width="211" height="16"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Song Title" id="ei8-1e-ErK">
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Song Title" id="ei8-1e-ErK">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
@ -761,12 +730,11 @@
|
|||||||
</tableColumn>
|
</tableColumn>
|
||||||
<tableColumn identifier="songDurationColumn" width="64" minWidth="64" maxWidth="64" id="8O6-ox-kx2" userLabel="Duration">
|
<tableColumn identifier="songDurationColumn" width="64" minWidth="64" maxWidth="64" id="8O6-ox-kx2" userLabel="Duration">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left">
|
||||||
<font key="font" metaFont="message" size="11"/>
|
|
||||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
<color key="backgroundColor" white="0.0" alpha="0.0" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||||
</tableHeaderCell>
|
</tableHeaderCell>
|
||||||
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="JOa-Mc-ceQ">
|
<textFieldCell key="dataCell" lineBreakMode="truncatingTail" selectable="YES" editable="YES" alignment="left" title="Text Cell" id="JOa-Mc-ceQ">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
@ -783,7 +751,7 @@
|
|||||||
<constraint firstAttribute="height" constant="17" id="grB-CG-1vJ"/>
|
<constraint firstAttribute="height" constant="17" id="grB-CG-1vJ"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="right" title="88:88" id="JnJ-sF-vCP">
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" alignment="right" title="88:88" id="JnJ-sF-vCP">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="label" size="13"/>
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
@ -824,7 +792,7 @@
|
|||||||
</scroller>
|
</scroller>
|
||||||
</scrollView>
|
</scrollView>
|
||||||
<customView misplaced="YES" id="iUb-eV-Qws">
|
<customView misplaced="YES" id="iUb-eV-Qws">
|
||||||
<rect key="frame" x="0.0" y="218" width="329" height="325"/>
|
<rect key="frame" x="0.0" y="199" width="329" height="299"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="Dw3-M5-tWY" customClass="CurrentCoverArtView" customModule="Persephone" customModuleProvider="target">
|
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="Dw3-M5-tWY" customClass="CurrentCoverArtView" customModule="Persephone" customModuleProvider="target">
|
||||||
@ -932,6 +900,7 @@
|
|||||||
<image name="prevTrackButton" width="17" height="17"/>
|
<image name="prevTrackButton" width="17" height="17"/>
|
||||||
<image name="repeatButton" width="17" height="17"/>
|
<image name="repeatButton" width="17" height="17"/>
|
||||||
<image name="shuffleButton" width="17" height="17"/>
|
<image name="shuffleButton" width="17" height="17"/>
|
||||||
|
<image name="speakerHigh" width="21" height="17"/>
|
||||||
<image name="stopButton" width="17" height="17"/>
|
<image name="stopButton" width="17" height="17"/>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@ -27,6 +27,8 @@ class WindowController: NSWindowController {
|
|||||||
@IBOutlet var shuffleState: NSButton!
|
@IBOutlet var shuffleState: NSButton!
|
||||||
@IBOutlet var repeatState: NSButton!
|
@IBOutlet var repeatState: NSButton!
|
||||||
|
|
||||||
|
@IBOutlet var volumeState: NSButton!
|
||||||
|
|
||||||
@IBOutlet weak var searchQuery: NSSearchField!
|
@IBOutlet weak var searchQuery: NSSearchField!
|
||||||
|
|
||||||
override func windowDidLoad() {
|
override func windowDidLoad() {
|
||||||
@ -121,6 +123,25 @@ class WindowController: NSWindowController {
|
|||||||
trackRemaining.stringValue = time.formattedTime
|
trackRemaining.stringValue = time.formattedTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setVolumeControlIcon(_ state: PlayerState) {
|
||||||
|
volumeState.isEnabled = state.volume != -1
|
||||||
|
|
||||||
|
switch state.volume {
|
||||||
|
case -1:
|
||||||
|
volumeState.image = .speakerDisabled
|
||||||
|
case 0..<5:
|
||||||
|
volumeState.image = .speakerOff
|
||||||
|
case 5..<40:
|
||||||
|
volumeState.image = .speakerLow
|
||||||
|
case 40..<70:
|
||||||
|
volumeState.image = .speakerMid
|
||||||
|
case 70...100:
|
||||||
|
volumeState.image = .speakerHigh
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc func willDisconnect() {
|
@objc func willDisconnect() {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
App.store.dispatch(SetSearchQuery(searchQuery: ""))
|
App.store.dispatch(SetSearchQuery(searchQuery: ""))
|
||||||
@ -176,6 +197,16 @@ class WindowController: NSWindowController {
|
|||||||
@IBAction func handleSearchQuery(_ sender: NSSearchField) {
|
@IBAction func handleSearchQuery(_ sender: NSSearchField) {
|
||||||
App.store.dispatch(SetSearchQuery(searchQuery: sender.stringValue))
|
App.store.dispatch(SetSearchQuery(searchQuery: sender.stringValue))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func showVolumeControl(_ sender: NSButton) {
|
||||||
|
VolumeControlView.popover.contentViewController = VolumeControlView.shared
|
||||||
|
VolumeControlView.popover.behavior = .transient
|
||||||
|
VolumeControlView.popover.show(
|
||||||
|
relativeTo: sender.bounds,
|
||||||
|
of: sender,
|
||||||
|
preferredEdge: .maxY
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension WindowController: NSWindowDelegate {
|
extension WindowController: NSWindowDelegate {
|
||||||
@ -201,6 +232,7 @@ extension WindowController: StoreSubscriber {
|
|||||||
self.setShuffleRepeatState(state.playerState)
|
self.setShuffleRepeatState(state.playerState)
|
||||||
self.setTrackProgressControls(state.playerState)
|
self.setTrackProgressControls(state.playerState)
|
||||||
self.setDatabaseUpdatingIndicator(state.uiState)
|
self.setDatabaseUpdatingIndicator(state.uiState)
|
||||||
|
self.setVolumeControlIcon(state.playerState)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -74,8 +74,8 @@ extension MPDClient {
|
|||||||
let mpdAlbum = MPDAlbum(
|
let mpdAlbum = MPDAlbum(
|
||||||
title: mpdSong.album.title,
|
title: mpdSong.album.title,
|
||||||
artist: mpdSong.artist,
|
artist: mpdSong.artist,
|
||||||
date: mpdSong.date,
|
firstSong: mpdSong,
|
||||||
path: mpdSong.path
|
date: mpdSong.date
|
||||||
)
|
)
|
||||||
if (mpdAlbum != albums.last) {
|
if (mpdAlbum != albums.last) {
|
||||||
albums.append(mpdAlbum)
|
albums.append(mpdAlbum)
|
||||||
|
|||||||
@ -47,6 +47,11 @@ extension MPDClient {
|
|||||||
else { return }
|
else { return }
|
||||||
sendRepeatState(repeatState: repeatState)
|
sendRepeatState(repeatState: repeatState)
|
||||||
|
|
||||||
|
case .setVolume:
|
||||||
|
guard let volume = userData["volume"] as? Int
|
||||||
|
else { return }
|
||||||
|
sendSetVolume(to: volume)
|
||||||
|
|
||||||
// Database commands
|
// Database commands
|
||||||
case .updateDatabase:
|
case .updateDatabase:
|
||||||
sendUpdateDatabase()
|
sendUpdateDatabase()
|
||||||
@ -119,6 +124,22 @@ extension MPDClient {
|
|||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
albumSongs(for: album, callback: callback)
|
albumSongs(for: album, callback: callback)
|
||||||
|
|
||||||
|
// Song commands
|
||||||
|
case .fetchAlbumArt:
|
||||||
|
guard let songUri = userData["songUri"] as? String,
|
||||||
|
let offset = userData["offset"] as? Int32,
|
||||||
|
let callback = userData["callback"] as? (Data?) -> Void
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
let imageData = userData["imageData"] as? Data? ?? nil
|
||||||
|
|
||||||
|
sendFetchAlbumArt(
|
||||||
|
forUri: songUri,
|
||||||
|
imageData: imageData,
|
||||||
|
offset: offset,
|
||||||
|
callback: callback
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -126,6 +147,7 @@ extension MPDClient {
|
|||||||
func enqueueCommand(
|
func enqueueCommand(
|
||||||
command: MPDCommand,
|
command: MPDCommand,
|
||||||
priority: BlockOperation.QueuePriority = .normal,
|
priority: BlockOperation.QueuePriority = .normal,
|
||||||
|
forceIdle: Bool = false,
|
||||||
userData: Dictionary<String, Any> = [:]
|
userData: Dictionary<String, Any> = [:]
|
||||||
) {
|
) {
|
||||||
guard isConnected else { return }
|
guard isConnected else { return }
|
||||||
@ -135,8 +157,9 @@ extension MPDClient {
|
|||||||
let commandOperation = BlockOperation() { [unowned self] in
|
let commandOperation = BlockOperation() { [unowned self] in
|
||||||
self.sendCommand(command: command, userData: userData)
|
self.sendCommand(command: command, userData: userData)
|
||||||
|
|
||||||
self.idle()
|
self.idle(forceIdle)
|
||||||
}
|
}
|
||||||
|
|
||||||
commandOperation.queuePriority = priority
|
commandOperation.queuePriority = priority
|
||||||
commandQueue.addOperation(commandOperation)
|
commandQueue.addOperation(commandOperation)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,27 +11,47 @@ import mpdclient
|
|||||||
|
|
||||||
extension MPDClient {
|
extension MPDClient {
|
||||||
func noIdle() {
|
func noIdle() {
|
||||||
|
do {
|
||||||
|
idleLock.lock()
|
||||||
|
defer { idleLock.unlock() }
|
||||||
if isIdle {
|
if isIdle {
|
||||||
mpd_send_noidle(connection)
|
mpd_send_noidle(connection)
|
||||||
isIdle = false
|
isIdle = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func idle() {
|
func idle(_ force: Bool = false) {
|
||||||
if !self.isIdle && self.commandQueue.operationCount == 1 {
|
let shouldIdle: Bool
|
||||||
mpd_send_idle(self.connection)
|
|
||||||
|
do {
|
||||||
|
idleLock.lock()
|
||||||
|
defer { idleLock.unlock() }
|
||||||
|
shouldIdle = (!isIdle && commandQueue.operationCount == 1) || force
|
||||||
|
if shouldIdle {
|
||||||
|
mpd_send_idle(connection)
|
||||||
self.isIdle = true
|
self.isIdle = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let result = mpd_recv_idle(self.connection, true)
|
if shouldIdle {
|
||||||
self.handleIdleResult(result)
|
let result = mpd_recv_idle(connection, true)
|
||||||
|
handleIdleResult(result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleIdleResult(_ result: mpd_idle) {
|
func handleIdleResult(_ result: mpd_idle) {
|
||||||
isIdle = false
|
|
||||||
|
|
||||||
let mpdIdle = MPDIdle(rawValue: result.rawValue)
|
let mpdIdle = MPDIdle(rawValue: result.rawValue)
|
||||||
|
let wasIdle: Bool
|
||||||
|
|
||||||
|
do {
|
||||||
|
idleLock.lock()
|
||||||
|
defer { idleLock.unlock() }
|
||||||
|
wasIdle = isIdle
|
||||||
|
isIdle = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if wasIdle {
|
||||||
if mpdIdle.contains(.database) {
|
if mpdIdle.contains(.database) {
|
||||||
self.fetchAllAlbums()
|
self.fetchAllAlbums()
|
||||||
}
|
}
|
||||||
@ -44,7 +64,9 @@ extension MPDClient {
|
|||||||
self.delegate?.didUpdateQueuePos(mpdClient: self, song: status.song)
|
self.delegate?.didUpdateQueuePos(mpdClient: self, song: status.song)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if mpdIdle.contains(.player) || mpdIdle.contains(.options) {
|
if mpdIdle.contains(.player) ||
|
||||||
|
mpdIdle.contains(.options) ||
|
||||||
|
mpdIdle.contains(.mixer) {
|
||||||
self.fetchStatus()
|
self.fetchStatus()
|
||||||
|
|
||||||
if let status = self.status {
|
if let status = self.status {
|
||||||
@ -66,3 +88,4 @@ extension MPDClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
24
Persephone/MPDClient/Extensions/MPDClient+Mixer.swift
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// MPDClient+Mixer.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2/16/20.
|
||||||
|
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import mpdclient
|
||||||
|
|
||||||
|
extension MPDClient {
|
||||||
|
func setVolume(to volume: Int) {
|
||||||
|
enqueueCommand(
|
||||||
|
command: .setVolume,
|
||||||
|
priority: .high,
|
||||||
|
userData: ["volume": volume]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendSetVolume(to volume: Int) {
|
||||||
|
mpd_run_set_volume(connection, UInt32(volume))
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -19,27 +19,51 @@ extension MPDClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func playTrack(at queuePos: Int) {
|
func playTrack(at queuePos: Int) {
|
||||||
enqueueCommand(command: .playTrack, userData: ["queuePos": queuePos])
|
enqueueCommand(
|
||||||
|
command: .playTrack,
|
||||||
|
forceIdle: true,
|
||||||
|
userData: ["queuePos": queuePos]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func appendSong(_ song: MPDSong) {
|
func appendSong(_ song: MPDSong) {
|
||||||
enqueueCommand(command: .appendSong, userData: ["song": song])
|
enqueueCommand(
|
||||||
|
command: .appendSong,
|
||||||
|
forceIdle: true,
|
||||||
|
userData: ["song": song]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeSong(at queuePos: Int) {
|
func removeSong(at queuePos: Int) {
|
||||||
enqueueCommand(command: .removeSong, userData: ["queuePos": queuePos])
|
enqueueCommand(
|
||||||
|
command: .removeSong,
|
||||||
|
forceIdle: true,
|
||||||
|
userData: ["queuePos": queuePos]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func moveSongInQueue(at queuePos: Int, to newQueuePos: Int) {
|
func moveSongInQueue(at queuePos: Int, to newQueuePos: Int) {
|
||||||
enqueueCommand(command: .moveSongInQueue, userData: ["oldQueuePos": queuePos, "newQueuePos": newQueuePos])
|
enqueueCommand(
|
||||||
|
command: .moveSongInQueue,
|
||||||
|
forceIdle: true,
|
||||||
|
userData: ["oldQueuePos": queuePos, "newQueuePos": newQueuePos]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addSongToQueue(songUri: String, at queuePos: Int) {
|
func addSongToQueue(songUri: String, at queuePos: Int) {
|
||||||
enqueueCommand(command: .addSongToQueue, userData: ["uri": songUri, "queuePos": queuePos])
|
enqueueCommand(
|
||||||
|
command: .addSongToQueue,
|
||||||
|
forceIdle: true,
|
||||||
|
userData: ["uri": songUri, "queuePos": queuePos]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func addAlbumToQueue(album: MPDAlbum, at queuePos: Int) {
|
func addAlbumToQueue(album: MPDAlbum, at queuePos: Int) {
|
||||||
enqueueCommand(command: .addAlbumToQueue, userData: ["album": album, "queuePos": queuePos])
|
enqueueCommand(
|
||||||
|
command: .addAlbumToQueue,
|
||||||
|
forceIdle: true,
|
||||||
|
userData: ["album": album, "queuePos": queuePos]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendPlayTrack(at queuePos: Int) {
|
func sendPlayTrack(at queuePos: Int) {
|
||||||
|
|||||||
@ -10,19 +10,80 @@ import Foundation
|
|||||||
import mpdclient
|
import mpdclient
|
||||||
|
|
||||||
extension MPDClient {
|
extension MPDClient {
|
||||||
|
func fetchAlbumArt(
|
||||||
|
songUri: String,
|
||||||
|
imageData: Data?,
|
||||||
|
offset: Int32 = 0,
|
||||||
|
callback: @escaping (Data?) -> Void
|
||||||
|
) {
|
||||||
|
enqueueCommand(
|
||||||
|
command: .fetchAlbumArt,
|
||||||
|
priority: .low,
|
||||||
|
userData: [
|
||||||
|
"songUri": songUri,
|
||||||
|
"callback": callback,
|
||||||
|
"imageData": imageData as Any,
|
||||||
|
"offset": offset,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func searchSongs(_ terms: [MPDClient.MPDTag: String]) -> [MPDSong] {
|
func searchSongs(_ terms: [MPDClient.MPDTag: String]) -> [MPDSong] {
|
||||||
var songs: [MPDSong] = []
|
var songs: [MPDSong] = []
|
||||||
|
|
||||||
mpd_search_db_songs(self.connection, true)
|
mpd_search_db_songs(connection, true)
|
||||||
for (tag, term) in terms {
|
for (tag, term) in terms {
|
||||||
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, tag.mpdTag(), term)
|
mpd_search_add_tag_constraint(connection, MPD_OPERATOR_DEFAULT, tag.mpdTag(), term)
|
||||||
}
|
}
|
||||||
mpd_search_commit(self.connection)
|
mpd_search_commit(connection)
|
||||||
|
|
||||||
while let song = mpd_recv_song(self.connection) {
|
while let song = mpd_recv_song(connection) {
|
||||||
songs.append(MPDSong(song))
|
songs.append(MPDSong(song))
|
||||||
}
|
}
|
||||||
|
|
||||||
return songs
|
return songs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func sendFetchAlbumArt(
|
||||||
|
forUri songUri: String,
|
||||||
|
imageData: Data?,
|
||||||
|
offset: Int32,
|
||||||
|
callback: @escaping (Data?) -> Void
|
||||||
|
) -> Void {
|
||||||
|
var size: Int?
|
||||||
|
|
||||||
|
mpd_send_albumart(connection, songUri, String(offset))
|
||||||
|
|
||||||
|
guard let sizePair = mpd_recv_pair(connection) else {
|
||||||
|
mpd_connection_clear_error(connection)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
size = Int(MPDPair(sizePair).value)
|
||||||
|
mpd_return_pair(connection, sizePair)
|
||||||
|
|
||||||
|
var data = imageData ?? Data(count: size!)
|
||||||
|
|
||||||
|
let binaryPair = MPDPair(mpd_recv_pair(connection))
|
||||||
|
let chunkSize = Int(binaryPair.value)!
|
||||||
|
mpd_return_pair(connection, binaryPair.pair)
|
||||||
|
|
||||||
|
_ = data[offset...].withUnsafeMutableBytes { (pointer) in
|
||||||
|
mpd_recv_binary(connection, pointer.baseAddress, chunkSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
guard mpd_response_finish(connection) else { return }
|
||||||
|
|
||||||
|
let newOffset = offset + Int32(chunkSize)
|
||||||
|
|
||||||
|
if newOffset < size! {
|
||||||
|
fetchAlbumArt(
|
||||||
|
songUri: songUri,
|
||||||
|
imageData: data,
|
||||||
|
offset: newOffset,
|
||||||
|
callback: callback
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
callback(data)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,24 +11,42 @@ import mpdclient
|
|||||||
|
|
||||||
extension MPDClient {
|
extension MPDClient {
|
||||||
func playPause() {
|
func playPause() {
|
||||||
enqueueCommand(command: .playPause)
|
enqueueCommand(
|
||||||
|
command: .playPause,
|
||||||
|
priority: .high,
|
||||||
|
forceIdle: true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func stop() {
|
func stop() {
|
||||||
enqueueCommand(command: .stop)
|
enqueueCommand(
|
||||||
|
command: .stop,
|
||||||
|
priority: .high,
|
||||||
|
forceIdle: true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func prevTrack() {
|
func prevTrack() {
|
||||||
enqueueCommand(command: .prevTrack)
|
enqueueCommand(
|
||||||
|
command: .prevTrack,
|
||||||
|
priority: .high,
|
||||||
|
forceIdle: true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func nextTrack() {
|
func nextTrack() {
|
||||||
enqueueCommand(command: .nextTrack)
|
enqueueCommand(
|
||||||
|
command: .nextTrack,
|
||||||
|
priority: .high,
|
||||||
|
forceIdle: true
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func seekCurrentSong(timeInSeconds: Float) {
|
func seekCurrentSong(timeInSeconds: Float) {
|
||||||
enqueueCommand(
|
enqueueCommand(
|
||||||
command: .seekCurrentSong,
|
command: .seekCurrentSong,
|
||||||
|
priority: .high,
|
||||||
|
forceIdle: true,
|
||||||
userData: ["timeInSeconds": timeInSeconds]
|
userData: ["timeInSeconds": timeInSeconds]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -36,6 +54,8 @@ extension MPDClient {
|
|||||||
func setShuffleState(shuffleState: Bool) {
|
func setShuffleState(shuffleState: Bool) {
|
||||||
enqueueCommand(
|
enqueueCommand(
|
||||||
command: .setShuffleState,
|
command: .setShuffleState,
|
||||||
|
priority: .high,
|
||||||
|
forceIdle: true,
|
||||||
userData: ["shuffleState": shuffleState]
|
userData: ["shuffleState": shuffleState]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -43,6 +63,8 @@ extension MPDClient {
|
|||||||
func setRepeatState(repeatState: Bool) {
|
func setRepeatState(repeatState: Bool) {
|
||||||
enqueueCommand(
|
enqueueCommand(
|
||||||
command: .setRepeatState,
|
command: .setRepeatState,
|
||||||
|
priority: .high,
|
||||||
|
forceIdle: true,
|
||||||
userData: ["repeatState": repeatState]
|
userData: ["repeatState": repeatState]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
15
Persephone/MPDClient/Extensions/MPDClientWrapper.c
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
//
|
||||||
|
// MPDClientWrapper.c
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 1/31/20.
|
||||||
|
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "MPDClientWrapper.h"
|
||||||
|
|
||||||
|
int
|
||||||
|
mpd_send_albumart(struct mpd_connection *connection, const char * uri, const char * offset)
|
||||||
|
{
|
||||||
|
return mpd_send_command(connection, "albumart", uri, offset, NULL);
|
||||||
|
}
|
||||||
12
Persephone/MPDClient/Extensions/MPDClientWrapper.h
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
//
|
||||||
|
// MPDClientWrapper.h
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 1/31/20.
|
||||||
|
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <mpd/client.h>
|
||||||
|
|
||||||
|
int
|
||||||
|
mpd_send_albumart(struct mpd_connection *connection, const char * uri, const char * offset);
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
//
|
||||||
|
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "MPDClientWrapper.h"
|
||||||
@ -22,6 +22,8 @@ class MPDClient {
|
|||||||
|
|
||||||
let commandQueue = OperationQueue()
|
let commandQueue = OperationQueue()
|
||||||
|
|
||||||
|
let idleLock = NSLock()
|
||||||
|
|
||||||
init(withDelegate delegate: MPDClientDelegate?) {
|
init(withDelegate delegate: MPDClientDelegate?) {
|
||||||
commandQueue.maxConcurrentOperationCount = 1
|
commandQueue.maxConcurrentOperationCount = 1
|
||||||
self.delegate = delegate
|
self.delegate = delegate
|
||||||
|
|||||||
@ -12,8 +12,8 @@ extension MPDClient {
|
|||||||
struct MPDAlbum: Equatable {
|
struct MPDAlbum: Equatable {
|
||||||
let title: String
|
let title: String
|
||||||
let artist: String
|
let artist: String
|
||||||
|
var firstSong: MPDSong?
|
||||||
var date: String?
|
var date: String?
|
||||||
var path: String?
|
|
||||||
|
|
||||||
static func == (lhs: MPDAlbum, rhs: MPDAlbum) -> Bool {
|
static func == (lhs: MPDAlbum, rhs: MPDAlbum) -> Bool {
|
||||||
return lhs.title == rhs.title &&
|
return lhs.title == rhs.title &&
|
||||||
|
|||||||
@ -23,6 +23,8 @@ extension MPDClient {
|
|||||||
case setShuffleState
|
case setShuffleState
|
||||||
case setRepeatState
|
case setRepeatState
|
||||||
|
|
||||||
|
case setVolume
|
||||||
|
|
||||||
// Database commands
|
// Database commands
|
||||||
case updateDatabase
|
case updateDatabase
|
||||||
|
|
||||||
@ -48,5 +50,8 @@ extension MPDClient {
|
|||||||
case playAlbum
|
case playAlbum
|
||||||
case getAlbumFirstSong
|
case getAlbumFirstSong
|
||||||
case getAlbumSongs
|
case getAlbumSongs
|
||||||
|
|
||||||
|
// Song commands
|
||||||
|
case fetchAlbumArt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -37,8 +37,7 @@ extension MPDClient {
|
|||||||
return MPDAlbum(
|
return MPDAlbum(
|
||||||
title: getTag(.album),
|
title: getTag(.album),
|
||||||
artist: artist,
|
artist: artist,
|
||||||
date: date,
|
date: date
|
||||||
path: path
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -54,11 +53,6 @@ extension MPDClient {
|
|||||||
return getTag(.date)
|
return getTag(.date)
|
||||||
}
|
}
|
||||||
|
|
||||||
var path: String {
|
|
||||||
return NSString(string: uriString)
|
|
||||||
.deletingLastPathComponent
|
|
||||||
}
|
|
||||||
|
|
||||||
func getTag(_ tagType: MPDTag) -> String {
|
func getTag(_ tagType: MPDTag) -> String {
|
||||||
guard let tag = mpd_song_get_tag(song, tagType.mpdTag(), 0)
|
guard let tag = mpd_song_get_tag(song, tagType.mpdTag(), 0)
|
||||||
else { return "" }
|
else { return "" }
|
||||||
|
|||||||
@ -58,6 +58,10 @@ extension MPDClient {
|
|||||||
return mpd_status_get_repeat(status)
|
return mpd_status_get_repeat(status)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var volume: Int {
|
||||||
|
return Int(mpd_status_get_volume(status))
|
||||||
|
}
|
||||||
|
|
||||||
var updating: Bool {
|
var updating: Bool {
|
||||||
let updating = mpd_status_get_update_id(status)
|
let updating = mpd_status_get_update_id(status)
|
||||||
|
|
||||||
|
|||||||
@ -32,29 +32,6 @@ struct Album {
|
|||||||
var hash: String {
|
var hash: String {
|
||||||
return "\(title) - \(artist)".sha1()
|
return "\(title) - \(artist)".sha1()
|
||||||
}
|
}
|
||||||
|
|
||||||
var coverArtFilenames: [String] {
|
|
||||||
return [
|
|
||||||
"cover.jpg",
|
|
||||||
"folder.jpg",
|
|
||||||
"\(artist) - \(title).jpg",
|
|
||||||
"cover.png",
|
|
||||||
"folder.png",
|
|
||||||
"\(artist) - \(title ).png",
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
var coverArtFilePath: String? {
|
|
||||||
let musicDir = App.store.state.preferencesState.expandedMpdLibraryDir
|
|
||||||
guard let albumPath = mpdAlbum.path else { return nil }
|
|
||||||
|
|
||||||
return coverArtFilenames
|
|
||||||
.lazy
|
|
||||||
.map { "\(musicDir)/\(albumPath)/\($0)" }
|
|
||||||
.first {
|
|
||||||
FileManager.default.fileExists(atPath: $0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Album: Equatable {
|
extension Album: Equatable {
|
||||||
|
|||||||
@ -8,7 +8,8 @@
|
|||||||
|
|
||||||
struct DraggedSong: Codable {
|
struct DraggedSong: Codable {
|
||||||
var type: DraggedSongType
|
var type: DraggedSongType
|
||||||
var title: String?
|
var title: String
|
||||||
var artist: String?
|
var artist: String
|
||||||
var cover: String?
|
var album: String
|
||||||
|
var uri: String
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,23 +0,0 @@
|
|||||||
//
|
|
||||||
// CoverArtQueue.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/2/26.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AppKit
|
|
||||||
|
|
||||||
class CoverArtQueue {
|
|
||||||
static let shared = CoverArtQueue()
|
|
||||||
|
|
||||||
let queue = DispatchQueue(label: "CoverArtQueue")
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
83
Persephone/Services/CoverArtService.swift
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
//
|
||||||
|
// CoverArtService.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2/3/20.
|
||||||
|
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import PromiseKit
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
|
struct CoverArtService {
|
||||||
|
let song: Song
|
||||||
|
let provider: MPDAlbumArtImageDataProvider
|
||||||
|
|
||||||
|
init(song: Song) {
|
||||||
|
self.song = song
|
||||||
|
|
||||||
|
provider = MPDAlbumArtImageDataProvider(
|
||||||
|
songUri: song.mpdSong.uriString,
|
||||||
|
cacheKey: song.album.hash
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshAlbumListArt() -> Promise<Any> {
|
||||||
|
return Promise<Any> { seal in
|
||||||
|
_ = KingfisherManager.shared.retrieveImage(
|
||||||
|
with: .provider(provider),
|
||||||
|
options: [
|
||||||
|
.forceRefresh,
|
||||||
|
.memoryCacheExpiration(.never),
|
||||||
|
.processor(DownsamplingImageProcessor(size: .albumListCoverSize)),
|
||||||
|
.scaleFactor(2),
|
||||||
|
]
|
||||||
|
) { result in
|
||||||
|
seal.fulfill(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshCurrentlyPlayingArt() -> Promise<Any> {
|
||||||
|
return Promise<Any> { seal in
|
||||||
|
_ = KingfisherManager.shared.retrieveImage(
|
||||||
|
with: .provider(provider),
|
||||||
|
options: [
|
||||||
|
.forceRefresh,
|
||||||
|
.memoryCacheExpiration(.never),
|
||||||
|
.processor(DownsamplingImageProcessor(size: .currentlyPlayingCoverSize)),
|
||||||
|
.scaleFactor(2),
|
||||||
|
.callbackQueue(.mainAsync)
|
||||||
|
]
|
||||||
|
) { result in
|
||||||
|
seal.fulfill(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func refreshQueueSongArt() -> Promise<Any> {
|
||||||
|
return Promise<Any> { seal in
|
||||||
|
_ = KingfisherManager.shared.retrieveImage(
|
||||||
|
with: .provider(provider),
|
||||||
|
options: [
|
||||||
|
.forceRefresh,
|
||||||
|
.memoryCacheExpiration(.never),
|
||||||
|
.processor(DownsamplingImageProcessor(size: .queueSongCoverSize)),
|
||||||
|
.scaleFactor(2),
|
||||||
|
]
|
||||||
|
) { result in
|
||||||
|
seal.fulfill(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func refresh(callback: @escaping () -> Void?) {
|
||||||
|
_ = firstly {
|
||||||
|
when(fulfilled: refreshAlbumListArt(), refreshQueueSongArt(), refreshCurrentlyPlayingArt())
|
||||||
|
}.done { _, _, _ in
|
||||||
|
NotificationCenter.default.post(name: .didReloadAlbumArt, object: nil)
|
||||||
|
callback()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -20,3 +20,7 @@ struct UpdateElapsedTimeAction: Action {
|
|||||||
struct UpdateStatusAction: Action {
|
struct UpdateStatusAction: Action {
|
||||||
var status: MPDClient.MPDStatus
|
var status: MPDClient.MPDStatus
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct UpdateVolumeAction: Action {
|
||||||
|
var volume: Int
|
||||||
|
}
|
||||||
|
|||||||
@ -16,10 +16,6 @@ struct UpdateServerPort: Action {
|
|||||||
var port: Int?
|
var port: Int?
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UpdateMPDLibraryDir: Action {
|
|
||||||
var mpdLibraryDir: String?
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UpdateFetchMissingArtworkFromInternet: Action {
|
struct UpdateFetchMissingArtworkFromInternet: Action {
|
||||||
var fetchMissingArtworkFromInternet: Bool
|
var fetchMissingArtworkFromInternet: Bool
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,16 +17,19 @@ struct PlayerState: StateType {
|
|||||||
var shuffleState: Bool = false
|
var shuffleState: Bool = false
|
||||||
var repeatState: Bool = false
|
var repeatState: Bool = false
|
||||||
|
|
||||||
|
var volume: Int = 0
|
||||||
|
|
||||||
var totalTime: UInt?
|
var totalTime: UInt?
|
||||||
var elapsedTimeMs: UInt?
|
var elapsedTimeMs: UInt?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension PlayerState: Equatable {
|
extension PlayerState: Equatable {
|
||||||
static func == (lhs: PlayerState, rhs: PlayerState) -> Bool {
|
static func == (lhs: PlayerState, rhs: PlayerState) -> Bool {
|
||||||
return (lhs.state == rhs.state) &&
|
return lhs.state == rhs.state &&
|
||||||
(lhs.totalTime == rhs.totalTime) &&
|
lhs.totalTime == rhs.totalTime &&
|
||||||
(lhs.elapsedTimeMs == rhs.elapsedTimeMs) &&
|
lhs.elapsedTimeMs == rhs.elapsedTimeMs &&
|
||||||
(lhs.shuffleState == rhs.shuffleState) &&
|
lhs.shuffleState == rhs.shuffleState &&
|
||||||
(lhs.repeatState == rhs.repeatState)
|
lhs.repeatState == rhs.repeatState &&
|
||||||
|
lhs.volume == rhs.volume
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,17 +13,6 @@ struct PreferencesState: StateType, Equatable {
|
|||||||
let preferences = UserDefaults.standard
|
let preferences = UserDefaults.standard
|
||||||
|
|
||||||
var mpdServer: MPDServer
|
var mpdServer: MPDServer
|
||||||
let mpdLibraryDirDefault = "~/Music"
|
|
||||||
|
|
||||||
var mpdLibraryDir: String?
|
|
||||||
|
|
||||||
var mpdLibraryDirOrDefault: String {
|
|
||||||
return mpdLibraryDir ?? mpdLibraryDirDefault
|
|
||||||
}
|
|
||||||
|
|
||||||
var expandedMpdLibraryDir: String {
|
|
||||||
return NSString(string: mpdLibraryDirOrDefault).expandingTildeInPath
|
|
||||||
}
|
|
||||||
|
|
||||||
var fetchMissingArtworkFromInternet: Bool
|
var fetchMissingArtworkFromInternet: Bool
|
||||||
|
|
||||||
@ -32,7 +21,6 @@ struct PreferencesState: StateType, Equatable {
|
|||||||
host: preferences.string(forKey: "mpdHost"),
|
host: preferences.string(forKey: "mpdHost"),
|
||||||
port: preferences.value(forKey: "mpdPort") as? Int
|
port: preferences.value(forKey: "mpdPort") as? Int
|
||||||
)
|
)
|
||||||
self.mpdLibraryDir = preferences.string(forKey: "mpdLibraryDir")
|
|
||||||
self.fetchMissingArtworkFromInternet = preferences.bool(
|
self.fetchMissingArtworkFromInternet = preferences.bool(
|
||||||
forKey: "fetchMissingArtworkFromInternet"
|
forKey: "fetchMissingArtworkFromInternet"
|
||||||
)
|
)
|
||||||
@ -45,7 +33,6 @@ struct PreferencesState: StateType, Equatable {
|
|||||||
} else {
|
} else {
|
||||||
preferences.removeObject(forKey: "mpdPort")
|
preferences.removeObject(forKey: "mpdPort")
|
||||||
}
|
}
|
||||||
preferences.set(mpdLibraryDir, forKey: "mpdLibraryDir")
|
|
||||||
preferences.set(fetchMissingArtworkFromInternet, forKey: "fetchMissingArtworkFromInternet")
|
preferences.set(fetchMissingArtworkFromInternet, forKey: "fetchMissingArtworkFromInternet")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -20,6 +20,7 @@ func playerReducer(action: Action, state: PlayerState?) -> PlayerState {
|
|||||||
state.elapsedTimeMs = action.status.elapsedTimeMs
|
state.elapsedTimeMs = action.status.elapsedTimeMs
|
||||||
state.shuffleState = action.status.shuffleState
|
state.shuffleState = action.status.shuffleState
|
||||||
state.repeatState = action.status.repeatState
|
state.repeatState = action.status.repeatState
|
||||||
|
state.volume = action.status.volume
|
||||||
|
|
||||||
if state.state == .playing {
|
if state.state == .playing {
|
||||||
App.trackTimer.start(elapsedTimeMs: state.elapsedTimeMs)
|
App.trackTimer.start(elapsedTimeMs: state.elapsedTimeMs)
|
||||||
|
|||||||
@ -20,9 +20,6 @@ func preferencesReducer(action: Action, state: PreferencesState?) -> Preferences
|
|||||||
case let action as UpdateServerPort:
|
case let action as UpdateServerPort:
|
||||||
state.mpdServer.port = action.port
|
state.mpdServer.port = action.port
|
||||||
|
|
||||||
case let action as UpdateMPDLibraryDir:
|
|
||||||
state.mpdLibraryDir = action.mpdLibraryDir
|
|
||||||
|
|
||||||
case is SavePreferences:
|
case is SavePreferences:
|
||||||
state.save()
|
state.save()
|
||||||
|
|
||||||
|
|||||||
BIN
Resources/export/speakerDisabled.pdf
Normal file
BIN
Resources/export/speakerDisabled.png
Normal file
|
After Width: | Height: | Size: 345 B |
BIN
Resources/export/speakerDisabled@2x.png
Normal file
|
After Width: | Height: | Size: 566 B |
BIN
Resources/export/speakerHigh.pdf
Normal file
BIN
Resources/export/speakerHigh.png
Normal file
|
After Width: | Height: | Size: 428 B |
BIN
Resources/export/speakerHigh@2x.png
Normal file
|
After Width: | Height: | Size: 800 B |
BIN
Resources/export/speakerLow.pdf
Normal file
BIN
Resources/export/speakerLow.png
Normal file
|
After Width: | Height: | Size: 306 B |
BIN
Resources/export/speakerLow@2x.png
Normal file
|
After Width: | Height: | Size: 531 B |
BIN
Resources/export/speakerMid.pdf
Normal file
BIN
Resources/export/speakerMid.png
Normal file
|
After Width: | Height: | Size: 357 B |
BIN
Resources/export/speakerMid@2x.png
Normal file
|
After Width: | Height: | Size: 648 B |
BIN
Resources/export/speakerOff.pdf
Normal file
BIN
Resources/export/speakerOff.png
Normal file
|
After Width: | Height: | Size: 256 B |
BIN
Resources/export/speakerOff@2x.png
Normal file
|
After Width: | Height: | Size: 383 B |