mirror of
https://github.com/danbee/persephone
synced 2025-03-04 08:39:11 +00:00
Compare commits
31 Commits
9f8f3a6ecf
...
6458b7402b
| Author | SHA1 | Date | |
|---|---|---|---|
| 6458b7402b | |||
| ae075914b3 | |||
| 27425b76d6 | |||
| 2932ab9e33 | |||
| f878daad2c | |||
| 555a99d5be | |||
| b8698d2830 | |||
| 8f3e93db66 | |||
| 3405df578b | |||
| 0e996cbb4c | |||
| 26646ea88e | |||
| 7b1728b521 | |||
| de5d3e94fb | |||
| b581f297c9 | |||
| d075a06c41 | |||
| 9f948df141 | |||
| 04d4c77f8d | |||
| 1dae55c2b8 | |||
| 1d5d87c75b | |||
| 91fa2f62dc | |||
| 80597cdd56 | |||
| e2149a2f3e | |||
| c201ebaab6 | |||
| bed09eb888 | |||
| b712a8d00d | |||
| 4322a25b8b | |||
| d407f1e5f9 | |||
| 01428d8126 | |||
| 42d274058f | |||
| 4af3e7aead | |||
| 87d4f33b09 |
@ -29,9 +29,6 @@
|
|||||||
E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5306223C019100173814 /* MPDClient+Status.swift */; };
|
E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5306223C019100173814 /* MPDClient+Status.swift */; };
|
||||||
E41E5309223C020400173814 /* MPDClient+Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5308223C020400173814 /* MPDClient+Command.swift */; };
|
E41E5309223C020400173814 /* MPDClient+Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5308223C020400173814 /* MPDClient+Command.swift */; };
|
||||||
E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E530A223C033700173814 /* MPDClient+Album.swift */; };
|
E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E530A223C033700173814 /* MPDClient+Album.swift */; };
|
||||||
E41E530E223EF4CF00173814 /* CoverArtService+Caching.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E530D223EF4CF00173814 /* CoverArtService+Caching.swift */; };
|
|
||||||
E41E5310223EF6CE00173814 /* CoverArtService+Remote.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E530F223EF6CE00173814 /* CoverArtService+Remote.swift */; };
|
|
||||||
E41E5312223EF74A00173814 /* CoverArtService+Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5311223EF74A00173814 /* CoverArtService+Filesystem.swift */; };
|
|
||||||
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */; };
|
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */; };
|
||||||
E4235640228623D2001216D6 /* QueueSongTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E423563F228623D2001216D6 /* QueueSongTitleView.swift */; };
|
E4235640228623D2001216D6 /* QueueSongTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E423563F228623D2001216D6 /* QueueSongTitleView.swift */; };
|
||||||
E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42410B52241B956005ED6DF /* MPDClient+Database.swift */; };
|
E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */ = {isa = PBXBuildFile; fileRef = E42410B52241B956005ED6DF /* MPDClient+Database.swift */; };
|
||||||
@ -49,6 +46,7 @@
|
|||||||
E43B67AA22909793007DCF55 /* AlbumDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43B67A822909793007DCF55 /* AlbumDetailView.swift */; };
|
E43B67AA22909793007DCF55 /* AlbumDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43B67A822909793007DCF55 /* AlbumDetailView.swift */; };
|
||||||
E43B67AB22909793007DCF55 /* AlbumDetailView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E43B67A922909793007DCF55 /* AlbumDetailView.xib */; };
|
E43B67AB22909793007DCF55 /* AlbumDetailView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E43B67A922909793007DCF55 /* AlbumDetailView.xib */; };
|
||||||
E43B67AD229194CD007DCF55 /* AlbumTracksDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43B67AC229194CD007DCF55 /* AlbumTracksDataSource.swift */; };
|
E43B67AD229194CD007DCF55 /* AlbumTracksDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E43B67AC229194CD007DCF55 /* AlbumTracksDataSource.swift */; };
|
||||||
|
E43BECA0238835DC00CAF1EB /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = E43BEC9F238835DC00CAF1EB /* Kingfisher */; };
|
||||||
E4405192227644340090CD6F /* MPDServerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4405191227644340090CD6F /* MPDServerController.swift */; };
|
E4405192227644340090CD6F /* MPDServerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4405191227644340090CD6F /* MPDServerController.swift */; };
|
||||||
E44051942278765A0090CD6F /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = E44051932278765A0090CD6F /* App.swift */; };
|
E44051942278765A0090CD6F /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = E44051932278765A0090CD6F /* App.swift */; };
|
||||||
E440519C227BAF2E0090CD6F /* UIActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519B227BAF2E0090CD6F /* UIActions.swift */; };
|
E440519C227BAF2E0090CD6F /* UIActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519B227BAF2E0090CD6F /* UIActions.swift */; };
|
||||||
@ -74,7 +72,6 @@
|
|||||||
E489E39D22B9CF0000CA8CBD /* NSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E39C22B9CF0000CA8CBD /* NSView.swift */; };
|
E489E39D22B9CF0000CA8CBD /* NSView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E39C22B9CF0000CA8CBD /* NSView.swift */; };
|
||||||
E489E3A422B9D31800CA8CBD /* DraggedSongView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */; };
|
E489E3A422B9D31800CA8CBD /* DraggedSongView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */; };
|
||||||
E489E3A522B9D31800CA8CBD /* DraggedSongView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */; };
|
E489E3A522B9D31800CA8CBD /* DraggedSongView.xib in Resources */ = {isa = PBXBuildFile; fileRef = E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */; };
|
||||||
E48E92D7235113DF00A5E1BB /* Metadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = E48E92D6235113DF00A5E1BB /* Metadata.swift */; };
|
|
||||||
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4928E0A2218D62A001D4BEA /* CGColor.swift */; };
|
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4928E0A2218D62A001D4BEA /* CGColor.swift */; };
|
||||||
E49A5482233E580800EED353 /* PromiseKit in Frameworks */ = {isa = PBXBuildFile; productRef = E49A5481233E580800EED353 /* PromiseKit */; };
|
E49A5482233E580800EED353 /* PromiseKit in Frameworks */ = {isa = PBXBuildFile; productRef = E49A5481233E580800EED353 /* PromiseKit */; };
|
||||||
E49A5485233E5ADC00EED353 /* Differ in Frameworks */ = {isa = PBXBuildFile; productRef = E49A5484233E5ADC00EED353 /* Differ */; };
|
E49A5485233E5ADC00EED353 /* Differ in Frameworks */ = {isa = PBXBuildFile; productRef = E49A5484233E5ADC00EED353 /* Differ */; };
|
||||||
@ -85,7 +82,6 @@
|
|||||||
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 */; };
|
||||||
E4A83BF4222207D50098FED6 /* CoverArtService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BF3222207D50098FED6 /* CoverArtService.swift */; };
|
|
||||||
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 */; };
|
||||||
@ -108,6 +104,7 @@
|
|||||||
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 */; };
|
||||||
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 */; };
|
||||||
@ -234,9 +231,6 @@
|
|||||||
E41E5306223C019100173814 /* MPDClient+Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Status.swift"; sourceTree = "<group>"; };
|
E41E5306223C019100173814 /* MPDClient+Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Status.swift"; sourceTree = "<group>"; };
|
||||||
E41E5308223C020400173814 /* MPDClient+Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Command.swift"; sourceTree = "<group>"; };
|
E41E5308223C020400173814 /* MPDClient+Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Command.swift"; sourceTree = "<group>"; };
|
||||||
E41E530A223C033700173814 /* MPDClient+Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Album.swift"; sourceTree = "<group>"; };
|
E41E530A223C033700173814 /* MPDClient+Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Album.swift"; sourceTree = "<group>"; };
|
||||||
E41E530D223EF4CF00173814 /* CoverArtService+Caching.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoverArtService+Caching.swift"; sourceTree = "<group>"; };
|
|
||||||
E41E530F223EF6CE00173814 /* CoverArtService+Remote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoverArtService+Remote.swift"; sourceTree = "<group>"; };
|
|
||||||
E41E5311223EF74A00173814 /* CoverArtService+Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoverArtService+Filesystem.swift"; sourceTree = "<group>"; };
|
|
||||||
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPrefsViewController.swift; sourceTree = "<group>"; };
|
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPrefsViewController.swift; sourceTree = "<group>"; };
|
||||||
E423563F228623D2001216D6 /* QueueSongTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueSongTitleView.swift; sourceTree = "<group>"; };
|
E423563F228623D2001216D6 /* QueueSongTitleView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueSongTitleView.swift; sourceTree = "<group>"; };
|
||||||
E42410B52241B956005ED6DF /* MPDClient+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Database.swift"; sourceTree = "<group>"; };
|
E42410B52241B956005ED6DF /* MPDClient+Database.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Database.swift"; sourceTree = "<group>"; };
|
||||||
@ -279,13 +273,11 @@
|
|||||||
E489E39C22B9CF0000CA8CBD /* NSView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSView.swift; sourceTree = "<group>"; };
|
E489E39C22B9CF0000CA8CBD /* NSView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSView.swift; sourceTree = "<group>"; };
|
||||||
E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggedSongView.swift; sourceTree = "<group>"; };
|
E489E3A222B9D31800CA8CBD /* DraggedSongView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DraggedSongView.swift; sourceTree = "<group>"; };
|
||||||
E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DraggedSongView.xib; sourceTree = "<group>"; };
|
E489E3A322B9D31800CA8CBD /* DraggedSongView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = DraggedSongView.xib; sourceTree = "<group>"; };
|
||||||
E48E92D6235113DF00A5E1BB /* Metadata.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Metadata.swift; sourceTree = "<group>"; };
|
|
||||||
E4928E0A2218D62A001D4BEA /* CGColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGColor.swift; sourceTree = "<group>"; };
|
E4928E0A2218D62A001D4BEA /* CGColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGColor.swift; sourceTree = "<group>"; };
|
||||||
E4A3A6A022A457B600EA2C40 /* AlbumDetailSongListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailSongListView.swift; sourceTree = "<group>"; };
|
E4A3A6A022A457B600EA2C40 /* AlbumDetailSongListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDetailSongListView.swift; sourceTree = "<group>"; };
|
||||||
E4A642D922090CBE00067D21 /* MPDStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDStatus.swift; sourceTree = "<group>"; };
|
E4A642D922090CBE00067D21 /* MPDStatus.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDStatus.swift; sourceTree = "<group>"; };
|
||||||
E4A83BEE2221F8CF0098FED6 /* CoverArtPrefsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoverArtPrefsController.swift; sourceTree = "<group>"; };
|
E4A83BEE2221F8CF0098FED6 /* CoverArtPrefsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoverArtPrefsController.swift; sourceTree = "<group>"; };
|
||||||
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = "<group>"; };
|
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = "<group>"; };
|
||||||
E4A83BF3222207D50098FED6 /* CoverArtService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoverArtService.swift; sourceTree = "<group>"; };
|
|
||||||
E4B11B52226928F20075461B /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
E4B11B52226928F20075461B /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
||||||
E4B11B60226A4BFF0075461B /* PlayerReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerReducer.swift; sourceTree = "<group>"; };
|
E4B11B60226A4BFF0075461B /* PlayerReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerReducer.swift; sourceTree = "<group>"; };
|
||||||
E4B11B62226A4C510075461B /* AppReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReducer.swift; sourceTree = "<group>"; };
|
E4B11B62226A4C510075461B /* AppReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppReducer.swift; sourceTree = "<group>"; };
|
||||||
@ -306,6 +298,7 @@
|
|||||||
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>"; };
|
||||||
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>"; };
|
||||||
@ -332,6 +325,7 @@
|
|||||||
E4B11BA72274E4500075461B /* libmpdclient.2.dylib in Frameworks */,
|
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 */,
|
||||||
E49A548E233E5B6000EED353 /* SwiftyJSON in Frameworks */,
|
E49A548E233E5B6000EED353 /* SwiftyJSON in Frameworks */,
|
||||||
E4E96D13233E630800AFD36F /* PMKFoundation in Frameworks */,
|
E4E96D13233E630800AFD36F /* PMKFoundation in Frameworks */,
|
||||||
E49A5482233E580800EED353 /* PromiseKit in Frameworks */,
|
E49A5482233E580800EED353 /* PromiseKit in Frameworks */,
|
||||||
@ -432,6 +426,7 @@
|
|||||||
E489E39822B85D0400CA8CBD /* NSPasteboard.swift */,
|
E489E39822B85D0400CA8CBD /* NSPasteboard.swift */,
|
||||||
E489E39C22B9CF0000CA8CBD /* NSView.swift */,
|
E489E39C22B9CF0000CA8CBD /* NSView.swift */,
|
||||||
E43AC1F022C68E6A001E483C /* NSPasteboardItem.swift */,
|
E43AC1F022C68E6A001E483C /* NSPasteboardItem.swift */,
|
||||||
|
E4DA820523D6236200C1EE58 /* NSSize.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -448,9 +443,9 @@
|
|||||||
E41E5302223BF9C300173814 /* MPDClient+Idle.swift */,
|
E41E5302223BF9C300173814 /* MPDClient+Idle.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 */,
|
||||||
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */,
|
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -514,16 +509,6 @@
|
|||||||
path = mpd;
|
path = mpd;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
E41E530C223EF4BA00173814 /* Extensions */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
E41E530D223EF4CF00173814 /* CoverArtService+Caching.swift */,
|
|
||||||
E41E5311223EF74A00173814 /* CoverArtService+Filesystem.swift */,
|
|
||||||
E41E530F223EF6CE00173814 /* CoverArtService+Remote.swift */,
|
|
||||||
);
|
|
||||||
path = Extensions;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
E442CCC42347D5B900004E0C /* Components */ = {
|
E442CCC42347D5B900004E0C /* Components */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -642,8 +627,6 @@
|
|||||||
E4A83BF2222207BE0098FED6 /* Services */ = {
|
E4A83BF2222207BE0098FED6 /* Services */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E41E530C223EF4BA00173814 /* Extensions */,
|
|
||||||
E4A83BF3222207D50098FED6 /* CoverArtService.swift */,
|
|
||||||
E439109722640213002982E9 /* SongNotifierService.swift */,
|
E439109722640213002982E9 /* SongNotifierService.swift */,
|
||||||
);
|
);
|
||||||
path = Services;
|
path = Services;
|
||||||
@ -736,7 +719,6 @@
|
|||||||
E419E2862249B96600216A8C /* Song.swift */,
|
E419E2862249B96600216A8C /* Song.swift */,
|
||||||
E47E2FDC2220A6D100F747E6 /* Time.swift */,
|
E47E2FDC2220A6D100F747E6 /* Time.swift */,
|
||||||
E4B11B72226A6C770075461B /* TrackTimer.swift */,
|
E4B11B72226A6C770075461B /* TrackTimer.swift */,
|
||||||
E48E92D6235113DF00A5E1BB /* Metadata.swift */,
|
|
||||||
);
|
);
|
||||||
path = Models;
|
path = Models;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -768,6 +750,7 @@
|
|||||||
E49A548D233E5B6000EED353 /* SwiftyJSON */,
|
E49A548D233E5B6000EED353 /* SwiftyJSON */,
|
||||||
E4677C47233E60E70041474F /* MediaKeyTap */,
|
E4677C47233E60E70041474F /* MediaKeyTap */,
|
||||||
E4E96D12233E630800AFD36F /* PMKFoundation */,
|
E4E96D12233E630800AFD36F /* PMKFoundation */,
|
||||||
|
E43BEC9F238835DC00CAF1EB /* Kingfisher */,
|
||||||
);
|
);
|
||||||
productName = Persephone;
|
productName = Persephone;
|
||||||
productReference = E40786182110CE6E006887B1 /* Persephone.app */;
|
productReference = E40786182110CE6E006887B1 /* Persephone.app */;
|
||||||
@ -860,6 +843,7 @@
|
|||||||
E49A548C233E5B6000EED353 /* XCRemoteSwiftPackageReference "SwiftyJSON" */,
|
E49A548C233E5B6000EED353 /* XCRemoteSwiftPackageReference "SwiftyJSON" */,
|
||||||
E4A9BEF9233E5F9000457785 /* XCRemoteSwiftPackageReference "MediaKeyTap" */,
|
E4A9BEF9233E5F9000457785 /* XCRemoteSwiftPackageReference "MediaKeyTap" */,
|
||||||
E4E96D11233E630800AFD36F /* XCRemoteSwiftPackageReference "Foundation" */,
|
E4E96D11233E630800AFD36F /* XCRemoteSwiftPackageReference "Foundation" */,
|
||||||
|
E43BEC9E238835DC00CAF1EB /* XCRemoteSwiftPackageReference "Kingfisher" */,
|
||||||
);
|
);
|
||||||
productRefGroup = E40786192110CE6E006887B1 /* Products */;
|
productRefGroup = E40786192110CE6E006887B1 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@ -948,9 +932,7 @@
|
|||||||
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */,
|
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */,
|
||||||
E4B11B63226A4C510075461B /* AppReducer.swift in Sources */,
|
E4B11B63226A4C510075461B /* AppReducer.swift in Sources */,
|
||||||
E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */,
|
E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */,
|
||||||
E41E5310223EF6CE00173814 /* CoverArtService+Remote.swift in Sources */,
|
|
||||||
E442CCCD2347E73C00004E0C /* Artist.swift in Sources */,
|
E442CCCD2347E73C00004E0C /* Artist.swift in Sources */,
|
||||||
E48E92D7235113DF00A5E1BB /* Metadata.swift 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 */,
|
||||||
E4235640228623D2001216D6 /* QueueSongTitleView.swift in Sources */,
|
E4235640228623D2001216D6 /* QueueSongTitleView.swift in Sources */,
|
||||||
@ -961,6 +943,7 @@
|
|||||||
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 */,
|
||||||
|
E4DA820623D6236200C1EE58 /* NSSize.swift in Sources */,
|
||||||
E408D3B6220DD8970006D9BE /* Notification.swift in Sources */,
|
E408D3B6220DD8970006D9BE /* Notification.swift in Sources */,
|
||||||
E43AC1F822C7065A001E483C /* AlbumCoverButton.swift in Sources */,
|
E43AC1F822C7065A001E483C /* AlbumCoverButton.swift in Sources */,
|
||||||
E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */,
|
E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */,
|
||||||
@ -1005,18 +988,15 @@
|
|||||||
E4120D6C22AD8139004CB1F8 /* QueueView.swift in Sources */,
|
E4120D6C22AD8139004CB1F8 /* QueueView.swift in Sources */,
|
||||||
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */,
|
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */,
|
||||||
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */,
|
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */,
|
||||||
E4A83BF4222207D50098FED6 /* CoverArtService.swift in Sources */,
|
|
||||||
E47E2FD5222071FD00F747E6 /* AlbumViewItem.swift in Sources */,
|
E47E2FD5222071FD00F747E6 /* AlbumViewItem.swift in Sources */,
|
||||||
E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */,
|
E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */,
|
||||||
E4B11B66226A4F830075461B /* PlayerState.swift in Sources */,
|
E4B11B66226A4F830075461B /* PlayerState.swift in Sources */,
|
||||||
E4B11BBE2275EDAA0075461B /* PlayerActions.swift in Sources */,
|
E4B11BBE2275EDAA0075461B /* PlayerActions.swift in Sources */,
|
||||||
E4F26F7723411AE300D45FF9 /* ArtistListActions.swift in Sources */,
|
E4F26F7723411AE300D45FF9 /* ArtistListActions.swift in Sources */,
|
||||||
E4FF71942276043A00D4C412 /* MPDServer.swift in Sources */,
|
E4FF71942276043A00D4C412 /* MPDServer.swift in Sources */,
|
||||||
E41E530E223EF4CF00173814 /* CoverArtService+Caching.swift in Sources */,
|
|
||||||
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */,
|
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */,
|
||||||
E440519C227BAF2E0090CD6F /* UIActions.swift in Sources */,
|
E440519C227BAF2E0090CD6F /* UIActions.swift in Sources */,
|
||||||
E45878382296173C00586A1C /* AlbumDetailSongRowView.swift in Sources */,
|
E45878382296173C00586A1C /* AlbumDetailSongRowView.swift in Sources */,
|
||||||
E41E5312223EF74A00173814 /* CoverArtService+Filesystem.swift in Sources */,
|
|
||||||
E41E5301223BF99300173814 /* MPDClient+Queue.swift in Sources */,
|
E41E5301223BF99300173814 /* MPDClient+Queue.swift in Sources */,
|
||||||
E4EB237B220F7CF1008C70C0 /* MPDAlbum.swift in Sources */,
|
E4EB237B220F7CF1008C70C0 /* MPDAlbum.swift in Sources */,
|
||||||
E41E5303223BF9C300173814 /* MPDClient+Idle.swift in Sources */,
|
E41E5303223BF9C300173814 /* MPDClient+Idle.swift in Sources */,
|
||||||
@ -1193,10 +1173,10 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = BDEE7ZBFZ3;
|
DEVELOPMENT_TEAM = 8E7TQ638ZD;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||||
INFOPLIST_FILE = Persephone/Info.plist;
|
INFOPLIST_FILE = Persephone/Info.plist;
|
||||||
@ -1208,6 +1188,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/libmpdclient/output",
|
"$(PROJECT_DIR)/libmpdclient/output",
|
||||||
);
|
);
|
||||||
|
MARKETING_VERSION = "0.14.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 = "";
|
||||||
@ -1221,10 +1202,10 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = BDEE7ZBFZ3;
|
DEVELOPMENT_TEAM = 8E7TQ638ZD;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
FRAMEWORK_SEARCH_PATHS = "$(inherited)";
|
||||||
INFOPLIST_FILE = Persephone/Info.plist;
|
INFOPLIST_FILE = Persephone/Info.plist;
|
||||||
@ -1236,6 +1217,7 @@
|
|||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
"$(PROJECT_DIR)/libmpdclient/output",
|
"$(PROJECT_DIR)/libmpdclient/output",
|
||||||
);
|
);
|
||||||
|
MARKETING_VERSION = "0.14.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 = "";
|
||||||
@ -1389,6 +1371,14 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
|
E43BEC9E238835DC00CAF1EB /* XCRemoteSwiftPackageReference "Kingfisher" */ = {
|
||||||
|
isa = XCRemoteSwiftPackageReference;
|
||||||
|
repositoryURL = "https://github.com/onevcat/Kingfisher.git";
|
||||||
|
requirement = {
|
||||||
|
kind = upToNextMajorVersion;
|
||||||
|
minimumVersion = 5.10.1;
|
||||||
|
};
|
||||||
|
};
|
||||||
E49A5480233E580800EED353 /* XCRemoteSwiftPackageReference "PromiseKit" */ = {
|
E49A5480233E580800EED353 /* XCRemoteSwiftPackageReference "PromiseKit" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/mxcl/PromiseKit";
|
repositoryURL = "https://github.com/mxcl/PromiseKit";
|
||||||
@ -1448,6 +1438,11 @@
|
|||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
|
E43BEC9F238835DC00CAF1EB /* Kingfisher */ = {
|
||||||
|
isa = XCSwiftPackageProductDependency;
|
||||||
|
package = E43BEC9E238835DC00CAF1EB /* XCRemoteSwiftPackageReference "Kingfisher" */;
|
||||||
|
productName = Kingfisher;
|
||||||
|
};
|
||||||
E4677C47233E60E70041474F /* MediaKeyTap */ = {
|
E4677C47233E60E70041474F /* MediaKeyTap */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = E4A9BEF9233E5F9000457785 /* XCRemoteSwiftPackageReference "MediaKeyTap" */;
|
package = E4A9BEF9233E5F9000457785 /* XCRemoteSwiftPackageReference "MediaKeyTap" */;
|
||||||
|
|||||||
@ -28,6 +28,15 @@
|
|||||||
"version": "3.3.3"
|
"version": "3.3.3"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"package": "Kingfisher",
|
||||||
|
"repositoryURL": "https://github.com/onevcat/Kingfisher.git",
|
||||||
|
"state": {
|
||||||
|
"branch": null,
|
||||||
|
"revision": "8ef6ca8b1b767ac2400762ed2f2bf75ddea3de5b",
|
||||||
|
"version": "5.10.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"package": "MediaKeyTap",
|
"package": "MediaKeyTap",
|
||||||
"repositoryURL": "https://github.com/danbee/MediaKeyTap",
|
"repositoryURL": "https://github.com/danbee/MediaKeyTap",
|
||||||
|
|||||||
@ -23,9 +23,6 @@ class AppDelegate: NSObject,
|
|||||||
@IBOutlet weak var addSelectedSongToQueueMenuItem: NSMenuItem!
|
@IBOutlet weak var addSelectedSongToQueueMenuItem: NSMenuItem!
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
connectToMPDServer()
|
|
||||||
instantiateControllers()
|
|
||||||
|
|
||||||
mediaKeyTap = MediaKeyTap(delegate: self)
|
mediaKeyTap = MediaKeyTap(delegate: self)
|
||||||
mediaKeyTap?.start()
|
mediaKeyTap?.start()
|
||||||
|
|
||||||
@ -34,23 +31,10 @@ class AppDelegate: NSObject,
|
|||||||
$0.uiState
|
$0.uiState
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func connectToMPDServer() {
|
|
||||||
let mpdServer = App.store.state.preferencesState.mpdServer
|
|
||||||
|
|
||||||
App.mpdClient = MPDClient(
|
|
||||||
host: mpdServer.hostOrDefault,
|
|
||||||
port: mpdServer.portOrDefault,
|
|
||||||
withDelegate: App.mpdServerDelegate
|
|
||||||
)
|
|
||||||
|
|
||||||
App.mpdClient.connect()
|
|
||||||
}
|
|
||||||
|
|
||||||
func instantiateControllers() {
|
|
||||||
_ = App.mpdServerController
|
|
||||||
_ = App.userNotificationsController
|
_ = App.userNotificationsController
|
||||||
|
|
||||||
|
App.mpdServerController.connect()
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillTerminate(_ aNotification: Notification) {
|
func applicationWillTerminate(_ aNotification: Notification) {
|
||||||
|
|||||||
@ -23,34 +23,6 @@ class AlbumDataSource: NSObject, NSCollectionViewDataSource {
|
|||||||
albumViewItem.view.wantsLayer = true
|
albumViewItem.view.wantsLayer = true
|
||||||
albumViewItem.setAlbum(albums[indexPath.item])
|
albumViewItem.setAlbum(albums[indexPath.item])
|
||||||
|
|
||||||
switch albums[indexPath.item].coverArt {
|
|
||||||
case .notLoaded:
|
|
||||||
App.mpdClient.getAlbumFirstSong(for: albums[indexPath.item].mpdAlbum) { mpdSong in
|
|
||||||
guard let mpdSong = mpdSong else { return }
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(
|
|
||||||
UpdateAlbumMetaData(
|
|
||||||
metadata: Metadata(date: mpdSong.date),
|
|
||||||
albumIndex: indexPath.item
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
CoverArtService(song: Song(mpdSong: mpdSong))
|
|
||||||
.fetchCoverArt()
|
|
||||||
.done { image in
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(
|
|
||||||
UpdateCoverArtAction(coverArt: image, albumIndex: indexPath.item)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return albumViewItem
|
return albumViewItem
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import AppKit
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
class AlbumViewItem: NSCollectionViewItem {
|
class AlbumViewItem: NSCollectionViewItem {
|
||||||
var observer: NSKeyValueObservation?
|
var observer: NSKeyValueObservation?
|
||||||
@ -43,6 +44,7 @@ class AlbumViewItem: NSCollectionViewItem {
|
|||||||
override func prepareForReuse() {
|
override func prepareForReuse() {
|
||||||
super.prepareForReuse()
|
super.prepareForReuse()
|
||||||
|
|
||||||
|
albumCoverView.image = .defaultCoverArt
|
||||||
AlbumDetailView.popover.close()
|
AlbumDetailView.popover.close()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -50,13 +52,22 @@ class AlbumViewItem: NSCollectionViewItem {
|
|||||||
self.album = album
|
self.album = album
|
||||||
albumTitle.stringValue = album.title
|
albumTitle.stringValue = album.title
|
||||||
albumArtist.stringValue = album.artist
|
albumArtist.stringValue = album.artist
|
||||||
|
setAlbumCover(album)
|
||||||
switch album.coverArt {
|
|
||||||
case .loaded(let coverArt):
|
|
||||||
albumCoverView.image = coverArt ?? .defaultCoverArt
|
|
||||||
default:
|
|
||||||
albumCoverView.image = .defaultCoverArt
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setAlbumCover(_ album: Album) {
|
||||||
|
guard let imagePath = album.coverArtFilePath else { return }
|
||||||
|
|
||||||
|
let imageURL = URL(fileURLWithPath: imagePath)
|
||||||
|
let provider = LocalFileImageDataProvider(fileURL: imageURL)
|
||||||
|
albumCoverView.kf.setImage(
|
||||||
|
with: .provider(provider),
|
||||||
|
placeholder: NSImage.defaultCoverArt,
|
||||||
|
options: [
|
||||||
|
.processor(DownsamplingImageProcessor(size: .albumListCoverSize)),
|
||||||
|
.scaleFactor(2),
|
||||||
|
]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setAppearance(selected isSelected: Bool) {
|
func setAppearance(selected isSelected: Bool) {
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import AppKit
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
class AlbumDetailView: NSViewController {
|
class AlbumDetailView: NSViewController {
|
||||||
var observer: NSKeyValueObservation?
|
var observer: NSKeyValueObservation?
|
||||||
@ -49,18 +50,11 @@ class AlbumDetailView: NSViewController {
|
|||||||
|
|
||||||
getAlbumSongs(for: album)
|
getAlbumSongs(for: album)
|
||||||
|
|
||||||
let date = album.metadata?.date ?? ""
|
let date = album.mpdAlbum.date ?? ""
|
||||||
|
|
||||||
albumTitle.stringValue = album.title
|
albumTitle.stringValue = album.title
|
||||||
albumMetadata.stringValue = "\(album.artist) · \(date)"
|
albumMetadata.stringValue = "\(album.artist) · \(date)"
|
||||||
|
|
||||||
switch album.coverArt {
|
|
||||||
case .loaded(let coverArt):
|
|
||||||
albumCoverView.image = coverArt ?? .defaultCoverArt
|
|
||||||
default:
|
|
||||||
albumCoverView.image = .defaultCoverArt
|
|
||||||
}
|
|
||||||
|
|
||||||
super.viewWillAppear()
|
super.viewWillAppear()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +80,7 @@ class AlbumDetailView: NSViewController {
|
|||||||
|
|
||||||
let queueLength = App.store.state.queueState.queue.count
|
let queueLength = App.store.state.queueState.queue.count
|
||||||
App.mpdClient.appendSong(song.mpdSong)
|
App.mpdClient.appendSong(song.mpdSong)
|
||||||
|
App.mpdClient.fetchQueue()
|
||||||
App.mpdClient.playTrack(at: queueLength)
|
App.mpdClient.playTrack(at: queueLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,6 +90,7 @@ class AlbumDetailView: NSViewController {
|
|||||||
|
|
||||||
let queueLength = App.store.state.queueState.queue.count
|
let queueLength = App.store.state.queueState.queue.count
|
||||||
App.mpdClient.appendSong(song.mpdSong)
|
App.mpdClient.appendSong(song.mpdSong)
|
||||||
|
App.mpdClient.fetchQueue()
|
||||||
App.mpdClient.playTrack(at: queueLength)
|
App.mpdClient.playTrack(at: queueLength)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,20 +128,23 @@ class AlbumDetailView: NSViewController {
|
|||||||
self.dataSource.albumSongs[1].song
|
self.dataSource.albumSongs[1].song
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
self.getBigCoverArt(song: song)
|
self.getBigCoverArt(song: song, album: album)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getBigCoverArt(song: Song) {
|
func getBigCoverArt(song: Song, album: Album) {
|
||||||
let coverArtService = CoverArtService(song: song)
|
guard let imagePath = album.coverArtFilePath else { return }
|
||||||
|
|
||||||
coverArtService.fetchBigCoverArt()
|
let imageURL = URL(fileURLWithPath: imagePath)
|
||||||
.done(on: DispatchQueue.main) { [weak self] image in
|
let provider = LocalFileImageDataProvider(fileURL: imageURL)
|
||||||
if let image = image {
|
albumCoverView.kf.setImage(
|
||||||
self?.albumCoverView.image = image
|
with: .provider(provider),
|
||||||
}
|
placeholder: NSImage.defaultCoverArt,
|
||||||
}
|
options: [
|
||||||
.cauterize()
|
.processor(DownsamplingImageProcessor(size: .albumDetailCoverSize)),
|
||||||
|
.scaleFactor(2),
|
||||||
|
]
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setAppearance() {
|
func setAppearance() {
|
||||||
|
|||||||
@ -8,25 +8,42 @@
|
|||||||
|
|
||||||
import AppKit
|
import AppKit
|
||||||
import ReSwift
|
import ReSwift
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
class CurrentCoverArtView: NSImageView {
|
class CurrentCoverArtView: NSImageView {
|
||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
super.init(coder: coder)
|
super.init(coder: coder)
|
||||||
|
|
||||||
App.store.subscribe(self) {
|
App.store.subscribe(self) {
|
||||||
$0.select { $0.playerState.currentArtwork }
|
$0.select { $0.playerState.currentSong }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setAlbumImage(_ album: Album) {
|
||||||
|
guard let imagePath = album.coverArtFilePath else { return }
|
||||||
|
|
||||||
|
let imageURL = URL(fileURLWithPath: imagePath)
|
||||||
|
let provider = LocalFileImageDataProvider(fileURL: imageURL)
|
||||||
|
kf.setImage(
|
||||||
|
with: .provider(provider),
|
||||||
|
placeholder: NSImage.defaultCoverArt,
|
||||||
|
options: [
|
||||||
|
.processor(DownsamplingImageProcessor(size: .currentlyPlayingCoverSize)),
|
||||||
|
.scaleFactor(2),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension CurrentCoverArtView: StoreSubscriber {
|
extension CurrentCoverArtView: StoreSubscriber {
|
||||||
typealias StoreSubscriberStateType = NSImage?
|
typealias StoreSubscriberStateType = Song?
|
||||||
|
|
||||||
func newState(state: NSImage?) {
|
func newState(state: Song?) {
|
||||||
if let coverArt = state {
|
guard let song = state else {
|
||||||
image = coverArt
|
|
||||||
} else {
|
|
||||||
image = .defaultCoverArt
|
image = .defaultCoverArt
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setAlbumImage(song.album)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
16
Persephone/Components/Shared/Extensions/NSSize.swift
Normal file
16
Persephone/Components/Shared/Extensions/NSSize.swift
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//
|
||||||
|
// NSSize.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 1/20/20.
|
||||||
|
// Copyright © 2020 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
extension NSSize {
|
||||||
|
static let albumListCoverSize = NSSize(width: 180, height: 180)
|
||||||
|
static let albumDetailCoverSize = NSSize(width: 500, height: 500)
|
||||||
|
static let currentlyPlayingCoverSize = albumDetailCoverSize
|
||||||
|
static let notificationCoverSize = albumListCoverSize
|
||||||
|
}
|
||||||
@ -11,9 +11,24 @@ import ReSwift
|
|||||||
|
|
||||||
class MPDServerController {
|
class MPDServerController {
|
||||||
init() {
|
init() {
|
||||||
// App.store.subscribe(self) {
|
App.mpdClient = MPDClient(withDelegate: App.mpdServerDelegate)
|
||||||
// $0.select { $0.preferencesState.mpdServer }
|
|
||||||
// }
|
App.store.subscribe(self) {
|
||||||
|
$0.select { $0.preferencesState.mpdServer }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func connect() {
|
||||||
|
let mpdServer = App.store.state.preferencesState.mpdServer
|
||||||
|
|
||||||
|
App.mpdClient.connect(
|
||||||
|
host: mpdServer.hostOrDefault,
|
||||||
|
port: mpdServer.portOrDefault
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func disconnect() {
|
||||||
|
App.mpdClient.disconnect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -21,7 +36,9 @@ extension MPDServerController: StoreSubscriber {
|
|||||||
typealias StoreSubscriberStateType = MPDServer
|
typealias StoreSubscriberStateType = MPDServer
|
||||||
|
|
||||||
func newState(state: MPDServer) {
|
func newState(state: MPDServer) {
|
||||||
App.mpdClient.disconnect()
|
guard App.mpdClient != nil else { return }
|
||||||
App.mpdClient.connect()
|
|
||||||
|
disconnect()
|
||||||
|
connect()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import ReSwift
|
import ReSwift
|
||||||
|
import Kingfisher
|
||||||
|
|
||||||
class UserNotificationsController {
|
class UserNotificationsController {
|
||||||
init() {
|
init() {
|
||||||
@ -18,18 +19,28 @@ class UserNotificationsController {
|
|||||||
|
|
||||||
func notifyTrack(_ state: Song?) {
|
func notifyTrack(_ state: Song?) {
|
||||||
guard let currentSong = state,
|
guard let currentSong = 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 coverArtService = CoverArtService(song: currentSong)
|
let imageURL = URL(fileURLWithPath: coverArtFilePath)
|
||||||
|
let provider = LocalFileImageDataProvider(fileURL: imageURL)
|
||||||
coverArtService.fetchBigCoverArt()
|
_ = KingfisherManager.shared.retrieveImage(
|
||||||
.done() {
|
with: .provider(provider),
|
||||||
SongNotifierService(song: currentSong, image: $0)
|
options: [
|
||||||
|
.processor(DownsamplingImageProcessor(size: .notificationCoverSize)),
|
||||||
|
.scaleFactor(2),
|
||||||
|
]
|
||||||
|
) { result in
|
||||||
|
switch result {
|
||||||
|
case .success(let value):
|
||||||
|
SongNotifierService(song: currentSong, image: value.image)
|
||||||
.deliver()
|
.deliver()
|
||||||
|
case .failure:
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.cauterize()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14868" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="15702" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14868"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="15702"/>
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<scenes>
|
<scenes>
|
||||||
@ -302,6 +302,21 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</button>
|
</button>
|
||||||
</toolbarItem>
|
</toolbarItem>
|
||||||
|
<toolbarItem implicitItemIdentifier="7C15D391-03F5-42C7-AC6D-6E17549C698E" label="Search" paletteLabel="Search" sizingBehavior="auto" id="FRe-rR-Ulo">
|
||||||
|
<nil key="toolTip"/>
|
||||||
|
<searchField key="view" wantsLayer="YES" verticalHuggingPriority="750" textCompletion="NO" id="xfU-Xe-eno">
|
||||||
|
<rect key="frame" x="0.0" y="14" width="96" height="22"/>
|
||||||
|
<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">
|
||||||
|
<font key="font" metaFont="system"/>
|
||||||
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
|
</searchFieldCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="handleSearchQuery:" target="B8D-0N-5wS" id="BPh-1O-bUU"/>
|
||||||
|
</connections>
|
||||||
|
</searchField>
|
||||||
|
</toolbarItem>
|
||||||
</allowedToolbarItems>
|
</allowedToolbarItems>
|
||||||
<defaultToolbarItems>
|
<defaultToolbarItems>
|
||||||
<toolbarItem reference="p3r-ty-Pxf"/>
|
<toolbarItem reference="p3r-ty-Pxf"/>
|
||||||
@ -314,6 +329,7 @@
|
|||||||
<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="FRe-rR-Ulo"/>
|
||||||
</defaultToolbarItems>
|
</defaultToolbarItems>
|
||||||
</toolbar>
|
</toolbar>
|
||||||
<connections>
|
<connections>
|
||||||
@ -323,6 +339,7 @@
|
|||||||
<connections>
|
<connections>
|
||||||
<outlet property="databaseUpdatingIndicator" destination="LpV-iM-o6t" id="y0T-eR-ygY"/>
|
<outlet property="databaseUpdatingIndicator" destination="LpV-iM-o6t" id="y0T-eR-ygY"/>
|
||||||
<outlet property="repeatState" destination="OqH-lV-sAg" id="DPC-Ff-Srr"/>
|
<outlet property="repeatState" destination="OqH-lV-sAg" id="DPC-Ff-Srr"/>
|
||||||
|
<outlet property="searchQuery" destination="xfU-Xe-eno" id="d8u-sH-13V"/>
|
||||||
<outlet property="shuffleState" destination="E8L-uK-XT0" id="dCF-hm-dBs"/>
|
<outlet property="shuffleState" destination="E8L-uK-XT0" id="dCF-hm-dBs"/>
|
||||||
<outlet property="trackProgress" destination="kx6-xm-TAN" id="XDv-Th-Agj"/>
|
<outlet property="trackProgress" destination="kx6-xm-TAN" id="XDv-Th-Agj"/>
|
||||||
<outlet property="trackProgressBar" destination="KMy-xf-rmN" id="a67-JU-cyQ"/>
|
<outlet property="trackProgressBar" destination="KMy-xf-rmN" id="a67-JU-cyQ"/>
|
||||||
@ -392,7 +409,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="message"/>
|
<font key="font" metaFont="system"/>
|
||||||
<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>
|
||||||
@ -436,7 +453,7 @@
|
|||||||
</textField>
|
</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="160" y="94" width="264" height="18"/>
|
||||||
<buttonCell key="cell" type="check" title="Fetch missing artwork from MusicBrainz" bezelStyle="regularSquare" imagePosition="left" 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="system"/>
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
@ -543,9 +560,6 @@
|
|||||||
<constraint firstItem="ARv-cj-xlz" firstAttribute="leading" secondItem="BRY-0R-F3u" secondAttribute="leading" id="w2Z-xv-Fwz"/>
|
<constraint firstItem="ARv-cj-xlz" firstAttribute="leading" secondItem="BRY-0R-F3u" secondAttribute="leading" id="w2Z-xv-Fwz"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
<connections>
|
|
||||||
<outlet property="browseTabView" destination="ARv-cj-xlz" id="h93-fi-yY7"/>
|
|
||||||
</connections>
|
|
||||||
</viewController>
|
</viewController>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="1436" y="238"/>
|
<point key="canvasLocation" x="1436" y="238"/>
|
||||||
@ -633,26 +647,26 @@
|
|||||||
<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" dividerStyle="thin" id="84I-w3-Mxl">
|
<splitView key="view" dividerStyle="thin" id="84I-w3-Mxl">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="328" height="548"/>
|
<rect key="frame" x="0.0" y="0.0" width="328" height="547"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="17" horizontalPageScroll="10" verticalLineScroll="17" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="S3o-nF-NN7">
|
<scrollView misplaced="YES" borderType="none" autohidesScrollers="YES" horizontalLineScroll="17" horizontalPageScroll="10" verticalLineScroll="17" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="S3o-nF-NN7">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="328" height="219"/>
|
<rect key="frame" x="0.0" y="0.0" width="328" height="219"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" 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="328" height="219"/>
|
<rect key="frame" x="0.0" y="0.0" width="328" height="219"/>
|
||||||
<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" rowSizeStyle="automatic" viewBased="YES" indentationPerLevel="14" outlineTableColumn="0Co-uF-CCB" id="jEJ-jg-fll" customClass="QueueView" customModule="Persephone" customModuleProvider="target">
|
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="sourceList" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" indentationPerLevel="14" outlineTableColumn="0Co-uF-CCB" id="jEJ-jg-fll" customClass="QueueView" customModule="Persephone" customModuleProvider="target">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="328" height="219"/>
|
<rect key="frame" x="0.0" y="0.0" width="328" height="219"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<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"/>
|
||||||
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
|
||||||
<tableColumns>
|
<tableColumns>
|
||||||
<tableColumn identifier="songTitleColumn" width="200" minWidth="128" maxWidth="1000" id="0Co-uF-CCB">
|
<tableColumn identifier="songTitleColumn" width="200" minWidth="128" maxWidth="1000" id="0Co-uF-CCB">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Title">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" title="Title">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<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>
|
||||||
@ -733,7 +747,7 @@
|
|||||||
</tableColumn>
|
</tableColumn>
|
||||||
<tableColumn identifier="songArtistColumn" width="122" minWidth="64" maxWidth="1000" id="SPM-QP-DX8">
|
<tableColumn identifier="songArtistColumn" width="122" minWidth="64" maxWidth="1000" id="SPM-QP-DX8">
|
||||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Artist">
|
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Artist">
|
||||||
<font key="font" metaFont="smallSystem"/>
|
<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>
|
||||||
@ -791,8 +805,8 @@
|
|||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
</scroller>
|
</scroller>
|
||||||
</scrollView>
|
</scrollView>
|
||||||
<customView id="iUb-eV-Qws">
|
<customView misplaced="YES" id="iUb-eV-Qws">
|
||||||
<rect key="frame" x="0.0" y="220" width="328" height="328"/>
|
<rect key="frame" x="0.0" y="220" width="328" height="327"/>
|
||||||
<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">
|
||||||
|
|||||||
@ -27,7 +27,7 @@ class WindowController: NSWindowController {
|
|||||||
@IBOutlet var shuffleState: NSButton!
|
@IBOutlet var shuffleState: NSButton!
|
||||||
@IBOutlet var repeatState: NSButton!
|
@IBOutlet var repeatState: NSButton!
|
||||||
|
|
||||||
@IBOutlet var browseViewControls: NSSegmentedControl!
|
@IBOutlet weak var searchQuery: NSSearchField!
|
||||||
|
|
||||||
override func windowDidLoad() {
|
override func windowDidLoad() {
|
||||||
super.windowDidLoad()
|
super.windowDidLoad()
|
||||||
@ -42,6 +42,8 @@ class WindowController: NSWindowController {
|
|||||||
|
|
||||||
App.store.dispatch(MainWindowDidOpenAction())
|
App.store.dispatch(MainWindowDidOpenAction())
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(self, selector: #selector(willDisconnect), name: .willDisconnect, object: nil)
|
||||||
|
|
||||||
trackProgress.font = .timerFont
|
trackProgress.font = .timerFont
|
||||||
trackRemaining.font = .timerFont
|
trackRemaining.font = .timerFont
|
||||||
}
|
}
|
||||||
@ -49,7 +51,9 @@ class WindowController: NSWindowController {
|
|||||||
override func keyDown(with event: NSEvent) {
|
override func keyDown(with event: NSEvent) {
|
||||||
switch event.keyCode {
|
switch event.keyCode {
|
||||||
case NSEvent.keyCodeSpace:
|
case NSEvent.keyCodeSpace:
|
||||||
|
if !event.isARepeat {
|
||||||
App.mpdClient.playPause()
|
App.mpdClient.playPause()
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
nextResponder?.keyDown(with: event)
|
nextResponder?.keyDown(with: event)
|
||||||
}
|
}
|
||||||
@ -117,6 +121,13 @@ class WindowController: NSWindowController {
|
|||||||
trackRemaining.stringValue = time.formattedTime
|
trackRemaining.stringValue = time.formattedTime
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@objc func willDisconnect() {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
App.store.dispatch(SetSearchQuery(searchQuery: ""))
|
||||||
|
self.searchQuery.stringValue = ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Refactor this using a gesture recognizer
|
// TODO: Refactor this using a gesture recognizer
|
||||||
@IBAction func changeTrackProgress(_ sender: NSSlider) {
|
@IBAction func changeTrackProgress(_ sender: NSSlider) {
|
||||||
guard let event = NSApplication.shared.currentEvent
|
guard let event = NSApplication.shared.currentEvent
|
||||||
@ -161,6 +172,10 @@ class WindowController: NSWindowController {
|
|||||||
@IBAction func handleRepeatButton(_ sender: NSButton) {
|
@IBAction func handleRepeatButton(_ sender: NSButton) {
|
||||||
App.mpdClient.setRepeatState(repeatState: sender.state == .on)
|
App.mpdClient.setRepeatState(repeatState: sender.state == .on)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@IBAction func handleSearchQuery(_ sender: NSSearchField) {
|
||||||
|
App.store.dispatch(SetSearchQuery(searchQuery: sender.stringValue))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension WindowController: NSWindowDelegate {
|
extension WindowController: NSWindowDelegate {
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.13.0-alpha</string>
|
<string>$(MARKETING_VERSION)</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
|
|||||||
@ -11,7 +11,11 @@ import mpdclient
|
|||||||
|
|
||||||
extension MPDClient {
|
extension MPDClient {
|
||||||
func fetchAllAlbums() {
|
func fetchAllAlbums() {
|
||||||
enqueueCommand(command: .fetchAllAlbums)
|
enqueueCommand(command: .fetchAlbums, userData: ["filter": ""])
|
||||||
|
}
|
||||||
|
|
||||||
|
func fetchAlbums(filter: String) {
|
||||||
|
enqueueCommand(command: .fetchAlbums, userData: ["filter": filter])
|
||||||
}
|
}
|
||||||
|
|
||||||
func playAlbum(_ album: MPDAlbum) {
|
func playAlbum(_ album: MPDAlbum) {
|
||||||
@ -41,6 +45,7 @@ extension MPDClient {
|
|||||||
priority: .normal,
|
priority: .normal,
|
||||||
userData: ["songs": songs]
|
userData: ["songs": songs]
|
||||||
)
|
)
|
||||||
|
self.enqueueCommand(command: .fetchQueue)
|
||||||
self.enqueueCommand(
|
self.enqueueCommand(
|
||||||
command: .playTrack,
|
command: .playTrack,
|
||||||
priority: .normal,
|
priority: .normal,
|
||||||
@ -49,27 +54,32 @@ extension MPDClient {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func allAlbums() {
|
func albums(filter: String) {
|
||||||
var albums: [MPDAlbum] = []
|
var albums: [MPDAlbum] = []
|
||||||
var artist: String = ""
|
|
||||||
|
|
||||||
mpd_search_db_tags(self.connection, MPD_TAG_ALBUM)
|
mpd_search_db_songs(self.connection, false)
|
||||||
mpd_search_add_group_tag(self.connection, MPD_TAG_ALBUM_ARTIST)
|
if filter != "" {
|
||||||
|
mpd_search_add_expression(
|
||||||
|
self.connection,
|
||||||
|
"(any contains '\(filter)')"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_TRACK, "1")
|
||||||
|
|
||||||
mpd_search_commit(self.connection)
|
mpd_search_commit(self.connection)
|
||||||
|
|
||||||
while let pair = mpd_recv_pair(self.connection) {
|
while let song = mpd_recv_song(self.connection) {
|
||||||
let pair = MPDPair(pair)
|
let mpdSong = MPDSong(song)
|
||||||
|
|
||||||
switch pair.name {
|
let mpdAlbum = MPDAlbum(
|
||||||
case "AlbumArtist":
|
title: mpdSong.album.title,
|
||||||
artist = pair.value
|
artist: mpdSong.artist,
|
||||||
case "Album":
|
date: mpdSong.date,
|
||||||
albums.append(MPDAlbum(title: pair.value, artist: artist))
|
path: mpdSong.path
|
||||||
default:
|
)
|
||||||
break
|
if (mpdAlbum != albums.last) {
|
||||||
|
albums.append(mpdAlbum)
|
||||||
}
|
}
|
||||||
|
|
||||||
mpd_return_pair(self.connection, pair.pair)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
self.delegate?.didLoadAlbums(mpdClient: self, albums: albums)
|
self.delegate?.didLoadAlbums(mpdClient: self, albums: albums)
|
||||||
@ -101,7 +111,7 @@ extension MPDClient {
|
|||||||
func albumSongs(for album: MPDAlbum, callback: ([MPDSong]) -> Void) {
|
func albumSongs(for album: MPDAlbum, callback: ([MPDSong]) -> Void) {
|
||||||
guard isConnected else { return }
|
guard isConnected else { return }
|
||||||
|
|
||||||
let songs = searchSongs([MPDTag.album: album.title, MPDTag.artist: album.artist])
|
let songs = searchSongs([MPDTag.album: album.title, MPDTag.albumArtist: album.artist])
|
||||||
|
|
||||||
callback(songs)
|
callback(songs)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,14 @@ extension MPDClient {
|
|||||||
) {
|
) {
|
||||||
switch command {
|
switch command {
|
||||||
|
|
||||||
|
case .connect:
|
||||||
|
guard let host = userData["host"] as? String,
|
||||||
|
let port = userData["port"] as? Int
|
||||||
|
else { return }
|
||||||
|
createConnection(host: host, port: port)
|
||||||
|
case .disconnect:
|
||||||
|
freeConnection()
|
||||||
|
|
||||||
// Transport commands
|
// Transport commands
|
||||||
case .prevTrack:
|
case .prevTrack:
|
||||||
sendPreviousTrack()
|
sendPreviousTrack()
|
||||||
@ -92,8 +100,9 @@ extension MPDClient {
|
|||||||
allArtists()
|
allArtists()
|
||||||
|
|
||||||
// Album commands
|
// Album commands
|
||||||
case .fetchAllAlbums:
|
case .fetchAlbums:
|
||||||
allAlbums()
|
guard let filter = userData["filter"] as? String else { return }
|
||||||
|
albums(filter: filter)
|
||||||
case .playAlbum:
|
case .playAlbum:
|
||||||
guard let album = userData["album"] as? MPDAlbum else { return }
|
guard let album = userData["album"] as? MPDAlbum else { return }
|
||||||
sendPlayAlbum(album)
|
sendPlayAlbum(album)
|
||||||
|
|||||||
@ -10,8 +10,7 @@ import Foundation
|
|||||||
import mpdclient
|
import mpdclient
|
||||||
|
|
||||||
extension MPDClient {
|
extension MPDClient {
|
||||||
func makeConnectionOperation(host: String, port: Int) -> BlockOperation {
|
func createConnection(host: String, port: Int) {
|
||||||
BlockOperation { [unowned self] in
|
|
||||||
guard let connection = mpd_connection_new(host, UInt32(port), 10000),
|
guard let connection = mpd_connection_new(host, UInt32(port), 10000),
|
||||||
mpd_connection_get_error(connection) == MPD_ERROR_SUCCESS
|
mpd_connection_get_error(connection) == MPD_ERROR_SUCCESS
|
||||||
else { return }
|
else { return }
|
||||||
@ -24,26 +23,31 @@ extension MPDClient {
|
|||||||
self.connection = connection
|
self.connection = connection
|
||||||
self.status = MPDStatus(status)
|
self.status = MPDStatus(status)
|
||||||
|
|
||||||
self.delegate?.didConnect(mpdClient: self)
|
delegate?.didConnect(mpdClient: self)
|
||||||
self.delegate?.didUpdateStatus(mpdClient: self, status: self.status!)
|
delegate?.didUpdateStatus(mpdClient: self, status: self.status!)
|
||||||
|
|
||||||
self.idle()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func connect() {
|
func freeConnection() {
|
||||||
commandQueue.addOperation(connectionOperation)
|
|
||||||
}
|
|
||||||
|
|
||||||
func disconnect() {
|
|
||||||
guard isConnected else { return }
|
guard isConnected else { return }
|
||||||
|
|
||||||
noIdle()
|
|
||||||
commandQueue.addOperation { [unowned self] in
|
|
||||||
self.delegate?.willDisconnect(mpdClient: self)
|
self.delegate?.willDisconnect(mpdClient: self)
|
||||||
|
|
||||||
mpd_connection_free(self.connection)
|
mpd_connection_free(self.connection)
|
||||||
self.isConnected = false
|
self.isConnected = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func connect(host: String, port: Int) {
|
||||||
|
let commandOperation = BlockOperation() { [unowned self] in
|
||||||
|
self.sendCommand(command: .connect, userData: ["host": host, "port": port])
|
||||||
|
|
||||||
|
if self.isConnected {
|
||||||
|
self.idle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
commandQueue.addOperation(commandOperation)
|
||||||
|
}
|
||||||
|
|
||||||
|
func disconnect() {
|
||||||
|
enqueueCommand(command: .disconnect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,9 +22,8 @@ class MPDClient {
|
|||||||
|
|
||||||
let commandQueue = OperationQueue()
|
let commandQueue = OperationQueue()
|
||||||
|
|
||||||
init(host: String, port: Int, withDelegate delegate: MPDClientDelegate?) {
|
init(withDelegate delegate: MPDClientDelegate?) {
|
||||||
commandQueue.maxConcurrentOperationCount = 1
|
commandQueue.maxConcurrentOperationCount = 1
|
||||||
self.delegate = delegate
|
self.delegate = delegate
|
||||||
self.connectionOperation = makeConnectionOperation(host: host, port: port)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,5 +12,13 @@ extension MPDClient {
|
|||||||
struct MPDAlbum: Equatable {
|
struct MPDAlbum: Equatable {
|
||||||
let title: String
|
let title: String
|
||||||
let artist: String
|
let artist: String
|
||||||
|
var date: String?
|
||||||
|
var path: String?
|
||||||
|
|
||||||
|
static func == (lhs: MPDAlbum, rhs: MPDAlbum) -> Bool {
|
||||||
|
return lhs.title == rhs.title &&
|
||||||
|
lhs.artist == rhs.artist &&
|
||||||
|
lhs.date == rhs.date
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,6 +10,9 @@ import Foundation
|
|||||||
|
|
||||||
extension MPDClient {
|
extension MPDClient {
|
||||||
enum MPDCommand {
|
enum MPDCommand {
|
||||||
|
case connect
|
||||||
|
case disconnect
|
||||||
|
|
||||||
// Transport commands
|
// Transport commands
|
||||||
case prevTrack
|
case prevTrack
|
||||||
case nextTrack
|
case nextTrack
|
||||||
@ -41,7 +44,7 @@ extension MPDClient {
|
|||||||
case fetchAllArtists
|
case fetchAllArtists
|
||||||
|
|
||||||
// Album commands
|
// Album commands
|
||||||
case fetchAllAlbums
|
case fetchAlbums
|
||||||
case playAlbum
|
case playAlbum
|
||||||
case getAlbumFirstSong
|
case getAlbumFirstSong
|
||||||
case getAlbumSongs
|
case getAlbumSongs
|
||||||
|
|||||||
@ -36,7 +36,9 @@ extension MPDClient {
|
|||||||
var album: MPDAlbum {
|
var album: MPDAlbum {
|
||||||
return MPDAlbum(
|
return MPDAlbum(
|
||||||
title: getTag(.album),
|
title: getTag(.album),
|
||||||
artist: artist
|
artist: artist,
|
||||||
|
date: date,
|
||||||
|
path: path
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,6 +54,11 @@ 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 "" }
|
||||||
|
|||||||
@ -11,8 +11,6 @@ import CryptoSwift
|
|||||||
|
|
||||||
struct Album {
|
struct Album {
|
||||||
var mpdAlbum: MPDClient.MPDAlbum
|
var mpdAlbum: MPDClient.MPDAlbum
|
||||||
var coverArt: Loading<NSImage?> = .notLoaded
|
|
||||||
var metadata: Metadata?
|
|
||||||
|
|
||||||
init(mpdAlbum: MPDClient.MPDAlbum) {
|
init(mpdAlbum: MPDClient.MPDAlbum) {
|
||||||
self.mpdAlbum = mpdAlbum
|
self.mpdAlbum = mpdAlbum
|
||||||
@ -26,14 +24,41 @@ struct Album {
|
|||||||
return mpdAlbum.artist
|
return mpdAlbum.artist
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var date: String {
|
||||||
|
guard let date = mpdAlbum.date else { return "" }
|
||||||
|
return date
|
||||||
|
}
|
||||||
|
|
||||||
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 {
|
||||||
static func == (lhs: Album, rhs: Album) -> Bool {
|
static func == (lhs: Album, rhs: Album) -> Bool {
|
||||||
return (lhs.mpdAlbum == rhs.mpdAlbum) &&
|
return lhs.mpdAlbum == rhs.mpdAlbum
|
||||||
(lhs.coverArt ~= rhs.coverArt)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -19,6 +19,6 @@ struct Artist {
|
|||||||
|
|
||||||
extension Artist: Equatable {
|
extension Artist: Equatable {
|
||||||
static func == (lhs: Artist, rhs: Artist) -> Bool {
|
static func == (lhs: Artist, rhs: Artist) -> Bool {
|
||||||
return (lhs.name == rhs.name)
|
return lhs.name == rhs.name
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ struct MPDServer {
|
|||||||
|
|
||||||
extension MPDServer: Equatable {
|
extension MPDServer: Equatable {
|
||||||
static func == (lhs: MPDServer, rhs: MPDServer) -> Bool {
|
static func == (lhs: MPDServer, rhs: MPDServer) -> Bool {
|
||||||
return (lhs.host == rhs.host) &&
|
return lhs.host == rhs.host &&
|
||||||
(lhs.port == rhs.port)
|
lhs.port == rhs.port
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,13 +0,0 @@
|
|||||||
//
|
|
||||||
// Metadata.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/10/11.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
struct Metadata {
|
|
||||||
var date: String?
|
|
||||||
}
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
//
|
|
||||||
// CoverArtService.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/2/23.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AppKit
|
|
||||||
import PromiseKit
|
|
||||||
|
|
||||||
class CoverArtService {
|
|
||||||
let song: Song
|
|
||||||
let album: Album
|
|
||||||
|
|
||||||
let cachedArtworkSize = 180
|
|
||||||
let cachedArtworkQuality: CGFloat = 0.5
|
|
||||||
|
|
||||||
let bigArtworkSize = 600
|
|
||||||
|
|
||||||
var session = URLSession(configuration: .default)
|
|
||||||
let coverArtQueue = DispatchQueue(label: "coverArtQueue", qos: .utility)
|
|
||||||
|
|
||||||
init(song: Song) {
|
|
||||||
self.song = song
|
|
||||||
self.album = song.album
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchBigCoverArt() -> Promise<NSImage?> {
|
|
||||||
return firstly {
|
|
||||||
self.getArtworkFromFilesystem()
|
|
||||||
}.then { (image: NSImage?) -> Promise<NSImage?> in
|
|
||||||
image.map(Promise.value) ?? self.getRemoteArtwork()
|
|
||||||
}.recover { (_) -> Guarantee<NSImage?> in
|
|
||||||
return .value(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchCoverArt() -> Guarantee<NSImage?> {
|
|
||||||
return firstly {
|
|
||||||
self.getCachedArtwork()
|
|
||||||
}.then { (artwork: NSImage?) -> Promise<NSImage?> in
|
|
||||||
artwork.map(Promise.value) ?? self.getArtworkFromFilesystem()
|
|
||||||
}.then { (artwork: NSImage?) -> Promise<NSImage?> in
|
|
||||||
artwork.map(Promise.value) ?? self.getRemoteArtwork()
|
|
||||||
}.compactMap(on: coverArtQueue) {
|
|
||||||
return self.sizeAndCacheImage($0).map(Optional.some)
|
|
||||||
}.recover { _ in
|
|
||||||
return .value(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sizeAndCacheImage(_ image: NSImage?) -> NSImage? {
|
|
||||||
switch image {
|
|
||||||
case nil:
|
|
||||||
self.cacheArtwork(data: Data())
|
|
||||||
return image
|
|
||||||
case let image:
|
|
||||||
if self.isArtworkCached() {
|
|
||||||
return image
|
|
||||||
} else {
|
|
||||||
let sizedImage = image?.toFitBox(
|
|
||||||
size: NSSize(width: self.cachedArtworkSize, height: self.cachedArtworkSize)
|
|
||||||
)
|
|
||||||
self.cacheArtwork(data: sizedImage?.jpegData(compressionQuality: self.cachedArtworkQuality))
|
|
||||||
return sizedImage
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
//
|
|
||||||
// CoverArtService+Caching.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/17.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AppKit
|
|
||||||
import PromiseKit
|
|
||||||
|
|
||||||
extension CoverArtService {
|
|
||||||
static let cacheDir = try! FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent(Bundle.main.bundleIdentifier!)
|
|
||||||
|
|
||||||
func getCachedArtwork() -> Promise<NSImage?> {
|
|
||||||
return Promise { seal in
|
|
||||||
coverArtQueue.async {
|
|
||||||
if self.isArtworkCached() {
|
|
||||||
let cacheFilePath = CoverArtService.cacheDir.appendingPathComponent(self.album.hash).path
|
|
||||||
let data = FileManager.default.contents(atPath: cacheFilePath)
|
|
||||||
let image = NSImage(data: data ?? Data()) ?? NSImage.defaultCoverArt
|
|
||||||
|
|
||||||
seal.fulfill(image)
|
|
||||||
} else {
|
|
||||||
seal.fulfill(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func cacheArtwork(data: Data?) {
|
|
||||||
coverArtQueue.async {
|
|
||||||
guard let bundleIdentifier = Bundle.main.bundleIdentifier,
|
|
||||||
let cacheDir = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
|
|
||||||
.appendingPathComponent(bundleIdentifier)
|
|
||||||
else { return }
|
|
||||||
|
|
||||||
let cacheFilePath = cacheDir.appendingPathComponent(self.album.hash).path
|
|
||||||
|
|
||||||
if !self.isArtworkCached() {
|
|
||||||
FileManager.default.createFile(atPath: cacheFilePath, contents: data, attributes: nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func isArtworkCached() -> Bool {
|
|
||||||
let cacheFilePath = CoverArtService.cacheDir.appendingPathComponent(album.hash).path
|
|
||||||
|
|
||||||
return FileManager.default.fileExists(atPath: cacheFilePath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,77 +0,0 @@
|
|||||||
//
|
|
||||||
// CoverArtService+Filesystem.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/17.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AppKit
|
|
||||||
import PromiseKit
|
|
||||||
|
|
||||||
extension CoverArtService {
|
|
||||||
var coverArtFilenames: [String] {
|
|
||||||
return [
|
|
||||||
"folder.jpg",
|
|
||||||
"cover.jpg",
|
|
||||||
"\(album.artist) - \(album.title).jpg"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
var musicDir: String {
|
|
||||||
return App.store.state.preferencesState.expandedMpdLibraryDir
|
|
||||||
}
|
|
||||||
|
|
||||||
func getArtworkFromFilesystem() -> Promise<NSImage?> {
|
|
||||||
return Promise { seal in
|
|
||||||
coverArtQueue.async {
|
|
||||||
guard let artworkPath = self.fileSystemArtworkFilePath()
|
|
||||||
else { seal.fulfill(nil); return }
|
|
||||||
|
|
||||||
let image = self.tryImage(artworkPath)
|
|
||||||
|
|
||||||
seal.fulfill(image)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func saveArtworkToFilesystem(data: Data?) {
|
|
||||||
let artworkFileName = coverArtFilenames.first!
|
|
||||||
|
|
||||||
if self.fileSystemArtworkFilePath() == nil {
|
|
||||||
FileManager.default.createFile(
|
|
||||||
atPath: "\(self.musicDir)/\(self.songPath)/\(artworkFileName)",
|
|
||||||
contents: data,
|
|
||||||
attributes: nil
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fileSystemArtworkFilePath() -> String? {
|
|
||||||
let musicDir = App.store.state.preferencesState.expandedMpdLibraryDir
|
|
||||||
|
|
||||||
return self.coverArtFilenames
|
|
||||||
.lazy
|
|
||||||
.map { "\(musicDir)/\(self.songPath)/\($0)" }
|
|
||||||
.first {
|
|
||||||
FileManager.default.fileExists(atPath: $0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var songPath: String {
|
|
||||||
return song
|
|
||||||
.mpdSong
|
|
||||||
.uriString
|
|
||||||
.split(separator: "/")
|
|
||||||
.dropLast()
|
|
||||||
.joined(separator: "/")
|
|
||||||
}
|
|
||||||
|
|
||||||
func tryImage(_ filePath: String) -> NSImage? {
|
|
||||||
guard let data = FileManager.default.contents(atPath: filePath),
|
|
||||||
let image = NSImage(data: data)
|
|
||||||
else { return nil }
|
|
||||||
|
|
||||||
return image
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,62 +0,0 @@
|
|||||||
//
|
|
||||||
// CoverArtService+Remote.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/3/17.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AppKit
|
|
||||||
import SwiftyJSON
|
|
||||||
import PromiseKit
|
|
||||||
import PMKFoundation
|
|
||||||
|
|
||||||
extension CoverArtService {
|
|
||||||
enum RemoteArtworkError: Error {
|
|
||||||
case noArtworkAvailable
|
|
||||||
case notConfigured
|
|
||||||
}
|
|
||||||
|
|
||||||
func getRemoteArtwork() -> Promise<NSImage?> {
|
|
||||||
return Promise { seal in
|
|
||||||
if App.store.state.preferencesState .fetchMissingArtworkFromInternet {
|
|
||||||
coverArtQueue.async {
|
|
||||||
let coverArtWorkItem = DispatchWorkItem {
|
|
||||||
self.getArtworkFromMusicBrainz().map(Optional.some).pipe(to: seal.resolve)
|
|
||||||
}
|
|
||||||
|
|
||||||
CoverArtQueue.shared.addToQueue(workItem: coverArtWorkItem)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
throw RemoteArtworkError.notConfigured
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getArtworkFromMusicBrainz() -> Promise<NSImage> {
|
|
||||||
var search = URLComponents(string: "https://musicbrainz.org/ws/2/release/")!
|
|
||||||
search.query = "query=artist:\(album.artist) AND release:\(album.title) AND country:US&limit=1&fmt=json"
|
|
||||||
|
|
||||||
return firstly {
|
|
||||||
URLSession.shared.dataTask(.promise, with: search.url!).validate()
|
|
||||||
}.compactMap {
|
|
||||||
JSON($0.data)
|
|
||||||
}.compactMap {
|
|
||||||
$0["releases"][0]["id"].string
|
|
||||||
}.compactMap {
|
|
||||||
URLComponents(string: "https://coverartarchive.org/release/\($0)/front-500")?.url
|
|
||||||
}.then { (url: URL?) -> Promise<(data: Data, response: URLResponse)> in
|
|
||||||
return URLSession.shared.dataTask(.promise, with: url!).validate()
|
|
||||||
}.compactMap {
|
|
||||||
NSImage(data: $0.data)?.toFitBox(
|
|
||||||
size: NSSize(width: self.cachedArtworkSize, height: self.cachedArtworkSize)
|
|
||||||
)
|
|
||||||
}.recover { error -> Promise<NSImage> in
|
|
||||||
if case PMKHTTPError.badStatusCode(404, _, _) = error {
|
|
||||||
throw RemoteArtworkError.noArtworkAvailable
|
|
||||||
} else {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -9,18 +9,6 @@
|
|||||||
import AppKit
|
import AppKit
|
||||||
import ReSwift
|
import ReSwift
|
||||||
|
|
||||||
struct ResetAlbumListCoverArtAction: Action {}
|
|
||||||
|
|
||||||
struct UpdateCoverArtAction: Action {
|
|
||||||
var coverArt: NSImage?
|
|
||||||
var albumIndex: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UpdateAlbumMetaData: Action {
|
|
||||||
var metadata: Metadata
|
|
||||||
var albumIndex: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UpdateAlbumListAction: Action {
|
struct UpdateAlbumListAction: Action {
|
||||||
var albums: [MPDClient.MPDAlbum]
|
var albums: [MPDClient.MPDAlbum]
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,10 +9,6 @@
|
|||||||
import AppKit
|
import AppKit
|
||||||
import ReSwift
|
import ReSwift
|
||||||
|
|
||||||
struct UpdateCurrentCoverArtAction: Action {
|
|
||||||
var coverArt: NSImage?
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UpdateCurrentSongAction: Action {
|
struct UpdateCurrentSongAction: Action {
|
||||||
var currentSong: Song?
|
var currentSong: Song?
|
||||||
}
|
}
|
||||||
|
|||||||
@ -25,3 +25,7 @@ struct SetSelectedQueueItem: Action {
|
|||||||
struct SetSelectedSong: Action {
|
struct SetSelectedSong: Action {
|
||||||
let selectedSong: Song?
|
let selectedSong: Song?
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct SetSearchQuery: Action {
|
||||||
|
let searchQuery: String
|
||||||
|
}
|
||||||
|
|||||||
@ -12,7 +12,6 @@ import ReSwift
|
|||||||
struct PlayerState: StateType {
|
struct PlayerState: StateType {
|
||||||
var status: MPDClient.MPDStatus?
|
var status: MPDClient.MPDStatus?
|
||||||
var currentSong: Song?
|
var currentSong: Song?
|
||||||
var currentArtwork: NSImage?
|
|
||||||
|
|
||||||
var state: MPDClient.MPDStatus.State?
|
var state: MPDClient.MPDStatus.State?
|
||||||
var shuffleState: Bool = false
|
var shuffleState: Bool = false
|
||||||
|
|||||||
@ -15,26 +15,6 @@ func albumListReducer(action: Action, state: AlbumListState?) -> AlbumListState
|
|||||||
case let action as UpdateAlbumListAction:
|
case let action as UpdateAlbumListAction:
|
||||||
state.albums = action.albums.map { Album(mpdAlbum: $0) }
|
state.albums = action.albums.map { Album(mpdAlbum: $0) }
|
||||||
|
|
||||||
case let action as UpdateCoverArtAction:
|
|
||||||
state.albums[action.albumIndex].coverArt = .loaded(action.coverArt)
|
|
||||||
|
|
||||||
case let action as UpdateAlbumMetaData:
|
|
||||||
state.albums[action.albumIndex].metadata = action.metadata
|
|
||||||
|
|
||||||
case is ResetAlbumListCoverArtAction:
|
|
||||||
state.albums = state.albums.map {
|
|
||||||
var album = $0
|
|
||||||
switch album.coverArt {
|
|
||||||
case .loaded(let coverArt):
|
|
||||||
if coverArt == nil {
|
|
||||||
album.coverArt = .notLoaded
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
album.coverArt = .notLoaded
|
|
||||||
}
|
|
||||||
return album
|
|
||||||
}
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
|
|
||||||
|
|||||||
@ -39,29 +39,6 @@ func playerReducer(action: Action, state: PlayerState?) -> PlayerState {
|
|||||||
case let action as UpdateCurrentSongAction:
|
case let action as UpdateCurrentSongAction:
|
||||||
state.currentSong = action.currentSong
|
state.currentSong = action.currentSong
|
||||||
|
|
||||||
if let currentSong = state.currentSong {
|
|
||||||
let coverArtService = CoverArtService(song: currentSong)
|
|
||||||
|
|
||||||
coverArtService.fetchBigCoverArt()
|
|
||||||
.done() { image in
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
if let image = image {
|
|
||||||
App.store.dispatch(UpdateCurrentCoverArtAction(coverArt: image))
|
|
||||||
} else {
|
|
||||||
App.store.dispatch(UpdateCurrentCoverArtAction(coverArt: .defaultCoverArt))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.cauterize()
|
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(UpdateCurrentCoverArtAction(coverArt: .defaultCoverArt))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case let action as UpdateCurrentCoverArtAction:
|
|
||||||
state.currentArtwork = action.coverArt
|
|
||||||
|
|
||||||
case let action as UpdateElapsedTimeAction:
|
case let action as UpdateElapsedTimeAction:
|
||||||
state.elapsedTimeMs = action.elapsedTimeMs
|
state.elapsedTimeMs = action.elapsedTimeMs
|
||||||
|
|
||||||
|
|||||||
@ -36,7 +36,7 @@ func queueReducer(action: Action, state: QueueState?) -> QueueState {
|
|||||||
if oldSongRowPos >= 0 {
|
if oldSongRowPos >= 0 {
|
||||||
state.queue[oldSongRowPos].isPlaying = false
|
state.queue[oldSongRowPos].isPlaying = false
|
||||||
}
|
}
|
||||||
if newSongRowPos >= 0 {
|
if newSongRowPos >= 0 && state.queue.count > newSongRowPos {
|
||||||
state.queue[newSongRowPos].isPlaying = true
|
state.queue[newSongRowPos].isPlaying = true
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
|
import AppKit
|
||||||
import ReSwift
|
import ReSwift
|
||||||
|
|
||||||
func uiReducer(action: Action, state: UIState?) -> UIState {
|
func uiReducer(action: Action, state: UIState?) -> UIState {
|
||||||
@ -33,6 +34,12 @@ func uiReducer(action: Action, state: UIState?) -> UIState {
|
|||||||
case let action as SetSelectedQueueItem:
|
case let action as SetSelectedQueueItem:
|
||||||
state.selectedQueueItem = action.selectedQueueItem
|
state.selectedQueueItem = action.selectedQueueItem
|
||||||
|
|
||||||
|
case let action as SetSearchQuery:
|
||||||
|
state.searchQuery = action.searchQuery
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
App.mpdClient.fetchAlbums(filter: state.searchQuery)
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|||||||
@ -22,4 +22,6 @@ struct UIState: StateType {
|
|||||||
var selectedSong: Song?
|
var selectedSong: Song?
|
||||||
|
|
||||||
var selectedQueueItem: QueueItem?
|
var selectedQueueItem: QueueItem?
|
||||||
|
|
||||||
|
var searchQuery: String = ""
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user