Compare commits
No commits in common. "b81fde390a24ab424268c4d17d7dee0373614065" and "89b26c4b8aa6ebab8f58fb95ec3bc80a28a97ce0" have entirely different histories.
b81fde390a
...
89b26c4b8a
12
CHANGELOG.md
@ -1,17 +1,5 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
## 0.12.0-alpha - 2019-05-18
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- Now Playing information and transport controls in the dock menu
|
|
||||||
- Queue position numbers in the queue
|
|
||||||
- Shuffle/repeat mode buttons
|
|
||||||
|
|
||||||
### Changed
|
|
||||||
|
|
||||||
- BIG refactor using unidirectional dataflow
|
|
||||||
|
|
||||||
## 0.11.2-alpha - 2019-04-09
|
## 0.11.2-alpha - 2019-04-09
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|||||||
2
Cartfile
@ -2,5 +2,3 @@ github "SwiftyJSON/SwiftyJSON" ~> 4.0
|
|||||||
github "PromiseKit/Foundation" ~> 3.0
|
github "PromiseKit/Foundation" ~> 3.0
|
||||||
github "nhurden/MediaKeyTap"
|
github "nhurden/MediaKeyTap"
|
||||||
github "krzyzanowskim/CryptoSwift"
|
github "krzyzanowskim/CryptoSwift"
|
||||||
github "ReSwift/ReSwift" "mjarvis/swift-5.0"
|
|
||||||
github "tonyarnold/Differ"
|
|
||||||
|
|||||||
@ -1,7 +1,5 @@
|
|||||||
github "PromiseKit/Foundation" "3.3.2"
|
github "PromiseKit/Foundation" "3.3.2"
|
||||||
github "ReSwift/ReSwift" "bc5943ad9493fc7fbad5200f690be538db8e86fb"
|
github "SwiftyJSON/SwiftyJSON" "4.2.0"
|
||||||
github "SwiftyJSON/SwiftyJSON" "4.3.0"
|
|
||||||
github "krzyzanowskim/CryptoSwift" "1.0.0"
|
github "krzyzanowskim/CryptoSwift" "1.0.0"
|
||||||
github "mxcl/PromiseKit" "6.8.4"
|
github "mxcl/PromiseKit" "6.8.4"
|
||||||
github "nhurden/MediaKeyTap" "2.2.1"
|
github "nhurden/MediaKeyTap" "2.2.1"
|
||||||
github "tonyarnold/Differ" "1.4.2"
|
|
||||||
|
|||||||
@ -17,8 +17,11 @@
|
|||||||
E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */; };
|
E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */; };
|
||||||
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3C1220E134F0006D9BE /* AlbumViewController.swift */; };
|
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E408D3C1220E134F0006D9BE /* AlbumViewController.swift */; };
|
||||||
E408D3CB220E341D0006D9BE /* AlbumViewItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = E408D3C9220E341D0006D9BE /* AlbumViewItem.xib */; };
|
E408D3CB220E341D0006D9BE /* AlbumViewItem.xib in Resources */ = {isa = PBXBuildFile; fileRef = E408D3C9220E341D0006D9BE /* AlbumViewItem.xib */; };
|
||||||
|
E40F41F3221EDE27004B6CB8 /* Preferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40F41F2221EDE27004B6CB8 /* Preferences.swift */; };
|
||||||
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40FE71A221B904300A4223F /* NSEvent.swift */; };
|
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */ = {isa = PBXBuildFile; fileRef = E40FE71A221B904300A4223F /* NSEvent.swift */; };
|
||||||
E419E2872249B96600216A8C /* Song.swift in Sources */ = {isa = PBXBuildFile; fileRef = E419E2862249B96600216A8C /* Song.swift */; };
|
E419E2872249B96600216A8C /* Song.swift in Sources */ = {isa = PBXBuildFile; fileRef = E419E2862249B96600216A8C /* Song.swift */; };
|
||||||
|
E41B22C021FB6BBA00D544F6 /* libmpdclient.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (Required, ); }; };
|
||||||
|
E41B22C121FB6C3300D544F6 /* libmpdclient.2.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||||
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41B22C521FB932700D544F6 /* MPDClient.swift */; };
|
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41B22C521FB932700D544F6 /* MPDClient.swift */; };
|
||||||
E41E52FD223BF87300173814 /* MPDClient+Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E52FC223BF87300173814 /* MPDClient+Connection.swift */; };
|
E41E52FD223BF87300173814 /* MPDClient+Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E52FC223BF87300173814 /* MPDClient+Connection.swift */; };
|
||||||
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */; };
|
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */; };
|
||||||
@ -28,36 +31,38 @@
|
|||||||
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 */; };
|
E41E530E223EF4CF00173814 /* AlbumArtService+Caching.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E530D223EF4CF00173814 /* AlbumArtService+Caching.swift */; };
|
||||||
E41E5310223EF6CE00173814 /* CoverArtService+Remote.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E530F223EF6CE00173814 /* CoverArtService+Remote.swift */; };
|
E41E5310223EF6CE00173814 /* AlbumArtService+Remote.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E530F223EF6CE00173814 /* AlbumArtService+Remote.swift */; };
|
||||||
E41E5312223EF74A00173814 /* CoverArtService+Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5311223EF74A00173814 /* CoverArtService+Filesystem.swift */; };
|
E41E5312223EF74A00173814 /* AlbumArtService+Filesystem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5311223EF74A00173814 /* AlbumArtService+Filesystem.swift */; };
|
||||||
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */; };
|
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */; };
|
||||||
E4235640228623D2001216D6 /* QueueSongTitleView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E423563F228623D2001216D6 /* QueueSongTitleView.swift */; };
|
E421ACA3221F73C4008B2449 /* MediaKeyTap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */; };
|
||||||
|
E421ACA4221F73C4008B2449 /* MediaKeyTap.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
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 */; };
|
||||||
E42A8F3B22176D6400A13ED9 /* LICENSE.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3922176D6400A13ED9 /* LICENSE.md */; };
|
E42A8F3B22176D6400A13ED9 /* LICENSE.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3922176D6400A13ED9 /* LICENSE.md */; };
|
||||||
E42A8F3C22176D6400A13ED9 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3A22176D6400A13ED9 /* README.md */; };
|
E42A8F3C22176D6400A13ED9 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3A22176D6400A13ED9 /* README.md */; };
|
||||||
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E1221CD4E200184CFC /* NSFont.swift */; };
|
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E1221CD4E200184CFC /* NSFont.swift */; };
|
||||||
E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E3221CD75D00184CFC /* NSImage.swift */; };
|
E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E3221CD75D00184CFC /* NSImage.swift */; };
|
||||||
E439109822640213002982E9 /* SongNotifierService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E439109722640213002982E9 /* SongNotifierService.swift */; };
|
E439109822640213002982E9 /* SongNotifierService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E439109722640213002982E9 /* SongNotifierService.swift */; };
|
||||||
E4405192227644340090CD6F /* MPDServerController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4405191227644340090CD6F /* MPDServerController.swift */; };
|
|
||||||
E44051942278765A0090CD6F /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = E44051932278765A0090CD6F /* App.swift */; };
|
|
||||||
E4405196227879960090CD6F /* MPDActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4405195227879960090CD6F /* MPDActions.swift */; };
|
|
||||||
E440519822787CB40090CD6F /* MPDState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519722787CB40090CD6F /* MPDState.swift */; };
|
|
||||||
E440519A22787CF60090CD6F /* MPDReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519922787CF60090CD6F /* MPDReducer.swift */; };
|
|
||||||
E440519C227BAF2E0090CD6F /* UIActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519B227BAF2E0090CD6F /* UIActions.swift */; };
|
|
||||||
E440519E227BB0720090CD6F /* UIReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519D227BB0720090CD6F /* UIReducer.swift */; };
|
|
||||||
E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E440519F227BB0AB0090CD6F /* UIState.swift */; };
|
|
||||||
E450AD7E222620A10091BED3 /* Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD7D222620A10091BED3 /* Album.swift */; };
|
E450AD7E222620A10091BED3 /* Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD7D222620A10091BED3 /* Album.swift */; };
|
||||||
|
E450AD8622262AE60091BED3 /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E450AD8522262AE60091BED3 /* SwiftyJSON.framework */; };
|
||||||
|
E450AD8822262AEC0091BED3 /* SwiftyJSON.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E450AD8522262AE60091BED3 /* SwiftyJSON.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
E450AD8F22262C620091BED3 /* PromiseKit.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD8E22262C620091BED3 /* PromiseKit.framework.dSYM */; };
|
E450AD8F22262C620091BED3 /* PromiseKit.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD8E22262C620091BED3 /* PromiseKit.framework.dSYM */; };
|
||||||
E450AD9122262C780091BED3 /* SwiftyJSON.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD9022262C780091BED3 /* SwiftyJSON.framework.dSYM */; };
|
E450AD9122262C780091BED3 /* SwiftyJSON.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD9022262C780091BED3 /* SwiftyJSON.framework.dSYM */; };
|
||||||
E450AD9522262DF10091BED3 /* CoverArtQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD9422262DF10091BED3 /* CoverArtQueue.swift */; };
|
E450AD9222262C970091BED3 /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E450AD8C22262C590091BED3 /* PromiseKit.framework */; };
|
||||||
|
E450AD9322262C970091BED3 /* PromiseKit.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E450AD8C22262C590091BED3 /* PromiseKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
|
E450AD9522262DF10091BED3 /* AlbumArtQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD9422262DF10091BED3 /* AlbumArtQueue.swift */; };
|
||||||
E450AD98222633920091BED3 /* Alamofire.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD96222633920091BED3 /* Alamofire.framework.dSYM */; };
|
E450AD98222633920091BED3 /* Alamofire.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD96222633920091BED3 /* Alamofire.framework.dSYM */; };
|
||||||
E450ADA12229E7C90091BED3 /* PMKFoundation.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD9F2229E7C90091BED3 /* PMKFoundation.framework.dSYM */; };
|
E450ADA12229E7C90091BED3 /* PMKFoundation.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD9F2229E7C90091BED3 /* PMKFoundation.framework.dSYM */; };
|
||||||
|
E450ADA32229E7E00091BED3 /* PMKFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E450ADA02229E7C90091BED3 /* PMKFoundation.framework */; };
|
||||||
|
E450ADA42229E7E00091BED3 /* PMKFoundation.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E450ADA02229E7C90091BED3 /* PMKFoundation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45962C52241A78500FC1A1E /* MPDCommand.swift */; };
|
E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45962C52241A78500FC1A1E /* MPDCommand.swift */; };
|
||||||
E45E4FDA22515D87004B537F /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = E45E4FD722515D87004B537F /* CHANGELOG.md */; };
|
E45E4FDA22515D87004B537F /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = E45E4FD722515D87004B537F /* CHANGELOG.md */; };
|
||||||
E45E4FDB22515D87004B537F /* Brewfile in Resources */ = {isa = PBXBuildFile; fileRef = E45E4FD822515D87004B537F /* Brewfile */; };
|
E45E4FDB22515D87004B537F /* Brewfile in Resources */ = {isa = PBXBuildFile; fileRef = E45E4FD822515D87004B537F /* Brewfile */; };
|
||||||
E45E4FDC22515D87004B537F /* Cartfile in Resources */ = {isa = PBXBuildFile; fileRef = E45E4FD922515D87004B537F /* Cartfile */; };
|
E45E4FDC22515D87004B537F /* Cartfile in Resources */ = {isa = PBXBuildFile; fileRef = E45E4FD922515D87004B537F /* Cartfile */; };
|
||||||
E45E4FDF225168DA004B537F /* CryptoSwift.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E45E4FDD225168DA004B537F /* CryptoSwift.framework.dSYM */; };
|
E45E4FDF225168DA004B537F /* CryptoSwift.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E45E4FDD225168DA004B537F /* CryptoSwift.framework.dSYM */; };
|
||||||
|
E45E4FE0225168DA004B537F /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E45E4FDE225168DA004B537F /* CryptoSwift.framework */; };
|
||||||
|
E45E4FE122516953004B537F /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E45E4FDE225168DA004B537F /* CryptoSwift.framework */; };
|
||||||
|
E45E4FE222516953004B537F /* CryptoSwift.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E45E4FDE225168DA004B537F /* CryptoSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
E465049A21E94DF500A70F4C /* WindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E465049921E94DF500A70F4C /* WindowController.swift */; };
|
E465049A21E94DF500A70F4C /* WindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E465049921E94DF500A70F4C /* WindowController.swift */; };
|
||||||
E47E2FCC2220573500F747E6 /* MediaKeyTap.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E47E2FCB2220573500F747E6 /* MediaKeyTap.framework.dSYM */; };
|
E47E2FCC2220573500F747E6 /* MediaKeyTap.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E47E2FCB2220573500F747E6 /* MediaKeyTap.framework.dSYM */; };
|
||||||
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */; };
|
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */; };
|
||||||
@ -68,56 +73,20 @@
|
|||||||
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */; };
|
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */; };
|
||||||
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4928E0A2218D62A001D4BEA /* CGColor.swift */; };
|
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4928E0A2218D62A001D4BEA /* CGColor.swift */; };
|
||||||
E4A642DA22090CBE00067D21 /* 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 /* AlbumArtPrefsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.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 */; };
|
E4A83BF4222207D50098FED6 /* AlbumArtService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BF3222207D50098FED6 /* AlbumArtService.swift */; };
|
||||||
E4B11B53226928F20075461B /* AppState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B52226928F20075461B /* AppState.swift */; };
|
|
||||||
E4B11B5A2269296C0075461B /* ReSwift.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E4B11B582269296C0075461B /* ReSwift.framework.dSYM */; };
|
|
||||||
E4B11B61226A4C000075461B /* PlayerReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B60226A4BFF0075461B /* PlayerReducer.swift */; };
|
|
||||||
E4B11B63226A4C510075461B /* AppReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B62226A4C510075461B /* AppReducer.swift */; };
|
|
||||||
E4B11B66226A4F830075461B /* PlayerState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B65226A4F830075461B /* PlayerState.swift */; };
|
|
||||||
E4B11B68226A4FA00075461B /* QueueState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B67226A4FA00075461B /* QueueState.swift */; };
|
|
||||||
E4B11B6A226A4FBC0075461B /* AlbumListState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B69226A4FBC0075461B /* AlbumListState.swift */; };
|
|
||||||
E4B11B73226A6C770075461B /* TrackTimer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B72226A6C770075461B /* TrackTimer.swift */; };
|
|
||||||
E4B11B75226CC4D30075461B /* QueueReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B74226CC4D30075461B /* QueueReducer.swift */; };
|
|
||||||
E4B11B79226D346B0075461B /* AlbumListReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11B78226D346B0075461B /* AlbumListReducer.swift */; };
|
|
||||||
E4B11B7E2274E36D0075461B /* Differ.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E4B11B7C2274E36D0075461B /* Differ.framework.dSYM */; };
|
|
||||||
E4B11B962274E43B0075461B /* Differ.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4B11B7D2274E36D0075461B /* Differ.framework */; };
|
|
||||||
E4B11B972274E43B0075461B /* Differ.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E4B11B7D2274E36D0075461B /* Differ.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
|
||||||
E4B11B982274E43B0075461B /* ReSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E4B11B572269296C0075461B /* ReSwift.framework */; };
|
|
||||||
E4B11B992274E43B0075461B /* ReSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E4B11B572269296C0075461B /* ReSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
|
||||||
E4B11B9A2274E43B0075461B /* CryptoSwift.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E45E4FDE225168DA004B537F /* CryptoSwift.framework */; };
|
|
||||||
E4B11B9B2274E43B0075461B /* CryptoSwift.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E45E4FDE225168DA004B537F /* CryptoSwift.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
|
||||||
E4B11B9C2274E43B0075461B /* PMKFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E450ADA02229E7C90091BED3 /* PMKFoundation.framework */; };
|
|
||||||
E4B11B9D2274E43B0075461B /* PMKFoundation.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E450ADA02229E7C90091BED3 /* PMKFoundation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
|
||||||
E4B11B9E2274E43B0075461B /* PromiseKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E450AD8C22262C590091BED3 /* PromiseKit.framework */; };
|
|
||||||
E4B11B9F2274E43B0075461B /* PromiseKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E450AD8C22262C590091BED3 /* PromiseKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
|
||||||
E4B11BA02274E43B0075461B /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E450AD8522262AE60091BED3 /* SwiftyJSON.framework */; };
|
|
||||||
E4B11BA12274E43B0075461B /* SwiftyJSON.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E450AD8522262AE60091BED3 /* SwiftyJSON.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
|
||||||
E4B11BA22274E43B0075461B /* MediaKeyTap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */; };
|
|
||||||
E4B11BA32274E43B0075461B /* MediaKeyTap.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
|
||||||
E4B11BA62274E44A0075461B /* libmpdclient.2.dylib in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
|
||||||
E4B11BA72274E4500075461B /* libmpdclient.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; };
|
|
||||||
E4B11BA92274EDE30075461B /* Loading.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BA82274EDE30075461B /* Loading.swift */; };
|
|
||||||
E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB52275374B0075461B /* UserNotificationsController.swift */; };
|
|
||||||
E4B11BB8227538FA0075461B /* CurrentCoverArtView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BB7227538FA0075461B /* CurrentCoverArtView.swift */; };
|
|
||||||
E4B11BBE2275EDAA0075461B /* PlayerActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BBD2275EDAA0075461B /* PlayerActions.swift */; };
|
|
||||||
E4B11BC02275EE150075461B /* QueueActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BBF2275EE150075461B /* QueueActions.swift */; };
|
|
||||||
E4B11BC22275EE410075461B /* AlbumListActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4B11BC12275EE410075461B /* AlbumListActions.swift */; };
|
|
||||||
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; };
|
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; };
|
||||||
E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* MPDIdle.swift */; };
|
E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* MPDIdle.swift */; };
|
||||||
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC8F2204EC7F0024217A /* Delegate.swift */; };
|
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC8F2204EC7F0024217A /* Delegate.swift */; };
|
||||||
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC912204F4B80024217A /* QueueViewController.swift */; };
|
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC912204F4B80024217A /* QueueViewController.swift */; };
|
||||||
|
E4E8CC942206097F0024217A /* NotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC932206097F0024217A /* NotificationsController.swift */; };
|
||||||
E4E8CC9A22075D370024217A /* MPDSong.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC9922075D370024217A /* MPDSong.swift */; };
|
E4E8CC9A22075D370024217A /* MPDSong.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC9922075D370024217A /* MPDSong.swift */; };
|
||||||
E4EB2379220F10B8008C70C0 /* MPDPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EB2378220F10B8008C70C0 /* MPDPair.swift */; };
|
E4EB2379220F10B8008C70C0 /* MPDPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EB2378220F10B8008C70C0 /* MPDPair.swift */; };
|
||||||
E4EB237B220F7CF1008C70C0 /* MPDAlbum.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EB237A220F7CF1008C70C0 /* MPDAlbum.swift */; };
|
E4EB237B220F7CF1008C70C0 /* MPDAlbum.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EB237A220F7CF1008C70C0 /* MPDAlbum.swift */; };
|
||||||
E4F6B460221E119B00ACF42A /* QueueDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */; };
|
E4F6B460221E119B00ACF42A /* QueueDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */; };
|
||||||
E4F6B463221E125900ACF42A /* QueueItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B462221E125900ACF42A /* QueueItem.swift */; };
|
E4F6B463221E125900ACF42A /* QueueItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B462221E125900ACF42A /* QueueItem.swift */; };
|
||||||
E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B466221E233200ACF42A /* AlbumDataSource.swift */; };
|
E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B466221E233200ACF42A /* AlbumDataSource.swift */; };
|
||||||
E4FF718E2276010E00D4C412 /* PreferencesState.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4FF718D2276010E00D4C412 /* PreferencesState.swift */; };
|
|
||||||
E4FF7190227601B400D4C412 /* PreferencesReducer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4FF718F227601B400D4C412 /* PreferencesReducer.swift */; };
|
|
||||||
E4FF71922276029000D4C412 /* PreferencesActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4FF71912276029000D4C412 /* PreferencesActions.swift */; };
|
|
||||||
E4FF71942276043A00D4C412 /* MPDServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4FF71932276043A00D4C412 /* MPDServer.swift */; };
|
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@ -138,6 +107,22 @@
|
|||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
/* Begin PBXCopyFilesBuildPhase section */
|
||||||
|
E41B22C221FB6C3300D544F6 /* Embed Libraries */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = "";
|
||||||
|
dstSubfolderSpec = 10;
|
||||||
|
files = (
|
||||||
|
E41B22C121FB6C3300D544F6 /* libmpdclient.2.dylib in Embed Libraries */,
|
||||||
|
E450ADA42229E7E00091BED3 /* PMKFoundation.framework in Embed Libraries */,
|
||||||
|
E421ACA4221F73C4008B2449 /* MediaKeyTap.framework in Embed Libraries */,
|
||||||
|
E45E4FE222516953004B537F /* CryptoSwift.framework in Embed Libraries */,
|
||||||
|
E450AD8822262AEC0091BED3 /* SwiftyJSON.framework in Embed Libraries */,
|
||||||
|
E450AD9322262C970091BED3 /* PromiseKit.framework in Embed Libraries */,
|
||||||
|
);
|
||||||
|
name = "Embed Libraries";
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
E421AC9B221F7319008B2449 /* CopyFiles */ = {
|
E421AC9B221F7319008B2449 /* CopyFiles */ = {
|
||||||
isa = PBXCopyFilesBuildPhase;
|
isa = PBXCopyFilesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
@ -147,24 +132,6 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
E4B11BA52274E43B0075461B /* Embed Frameworks */ = {
|
|
||||||
isa = PBXCopyFilesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
dstPath = "";
|
|
||||||
dstSubfolderSpec = 10;
|
|
||||||
files = (
|
|
||||||
E4B11B9B2274E43B0075461B /* CryptoSwift.framework in Embed Frameworks */,
|
|
||||||
E4B11B972274E43B0075461B /* Differ.framework in Embed Frameworks */,
|
|
||||||
E4B11BA12274E43B0075461B /* SwiftyJSON.framework in Embed Frameworks */,
|
|
||||||
E4B11B9F2274E43B0075461B /* PromiseKit.framework in Embed Frameworks */,
|
|
||||||
E4B11B9D2274E43B0075461B /* PMKFoundation.framework in Embed Frameworks */,
|
|
||||||
E4B11BA62274E44A0075461B /* libmpdclient.2.dylib in Embed Frameworks */,
|
|
||||||
E4B11BA32274E43B0075461B /* MediaKeyTap.framework in Embed Frameworks */,
|
|
||||||
E4B11B992274E43B0075461B /* ReSwift.framework in Embed Frameworks */,
|
|
||||||
);
|
|
||||||
name = "Embed Frameworks";
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
@ -185,6 +152,7 @@
|
|||||||
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawRepresentable.swift; sourceTree = "<group>"; };
|
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RawRepresentable.swift; sourceTree = "<group>"; };
|
||||||
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumViewController.swift; sourceTree = "<group>"; };
|
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumViewController.swift; sourceTree = "<group>"; };
|
||||||
E408D3C9220E341D0006D9BE /* AlbumViewItem.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AlbumViewItem.xib; sourceTree = "<group>"; };
|
E408D3C9220E341D0006D9BE /* AlbumViewItem.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AlbumViewItem.xib; sourceTree = "<group>"; };
|
||||||
|
E40F41F2221EDE27004B6CB8 /* Preferences.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Preferences.swift; sourceTree = "<group>"; };
|
||||||
E40FE71A221B904300A4223F /* NSEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEvent.swift; sourceTree = "<group>"; };
|
E40FE71A221B904300A4223F /* NSEvent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSEvent.swift; sourceTree = "<group>"; };
|
||||||
E419E2862249B96600216A8C /* Song.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Song.swift; sourceTree = "<group>"; };
|
E419E2862249B96600216A8C /* Song.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Song.swift; sourceTree = "<group>"; };
|
||||||
E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libmpdclient.2.dylib; path = libmpdclient/output/libmpdclient.2.dylib; sourceTree = "<group>"; };
|
E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libmpdclient.2.dylib; path = libmpdclient/output/libmpdclient.2.dylib; sourceTree = "<group>"; };
|
||||||
@ -233,32 +201,23 @@
|
|||||||
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>"; };
|
E41E530D223EF4CF00173814 /* AlbumArtService+Caching.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AlbumArtService+Caching.swift"; sourceTree = "<group>"; };
|
||||||
E41E530F223EF6CE00173814 /* CoverArtService+Remote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoverArtService+Remote.swift"; sourceTree = "<group>"; };
|
E41E530F223EF6CE00173814 /* AlbumArtService+Remote.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AlbumArtService+Remote.swift"; sourceTree = "<group>"; };
|
||||||
E41E5311223EF74A00173814 /* CoverArtService+Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoverArtService+Filesystem.swift"; sourceTree = "<group>"; };
|
E41E5311223EF74A00173814 /* AlbumArtService+Filesystem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AlbumArtService+Filesystem.swift"; sourceTree = "<group>"; };
|
||||||
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPrefsViewController.swift; sourceTree = "<group>"; };
|
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPrefsViewController.swift; sourceTree = "<group>"; };
|
||||||
E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaKeyTap.framework; path = Carthage/Build/Mac/MediaKeyTap.framework; sourceTree = "<group>"; };
|
E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaKeyTap.framework; path = Carthage/Build/Mac/MediaKeyTap.framework; sourceTree = "<group>"; };
|
||||||
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>"; };
|
||||||
E42A8F3922176D6400A13ED9 /* LICENSE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = "<group>"; };
|
E42A8F3922176D6400A13ED9 /* LICENSE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = "<group>"; };
|
||||||
E42A8F3A22176D6400A13ED9 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
E42A8F3A22176D6400A13ED9 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||||
E435E3E1221CD4E200184CFC /* NSFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSFont.swift; sourceTree = "<group>"; };
|
E435E3E1221CD4E200184CFC /* NSFont.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSFont.swift; sourceTree = "<group>"; };
|
||||||
E435E3E3221CD75D00184CFC /* NSImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSImage.swift; sourceTree = "<group>"; };
|
E435E3E3221CD75D00184CFC /* NSImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSImage.swift; sourceTree = "<group>"; };
|
||||||
E439109722640213002982E9 /* SongNotifierService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SongNotifierService.swift; sourceTree = "<group>"; };
|
E439109722640213002982E9 /* SongNotifierService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SongNotifierService.swift; sourceTree = "<group>"; };
|
||||||
E4405191227644340090CD6F /* MPDServerController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDServerController.swift; sourceTree = "<group>"; };
|
|
||||||
E44051932278765A0090CD6F /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
|
|
||||||
E4405195227879960090CD6F /* MPDActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDActions.swift; sourceTree = "<group>"; };
|
|
||||||
E440519722787CB40090CD6F /* MPDState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDState.swift; sourceTree = "<group>"; };
|
|
||||||
E440519922787CF60090CD6F /* MPDReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDReducer.swift; sourceTree = "<group>"; };
|
|
||||||
E440519B227BAF2E0090CD6F /* UIActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActions.swift; sourceTree = "<group>"; };
|
|
||||||
E440519D227BB0720090CD6F /* UIReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIReducer.swift; sourceTree = "<group>"; };
|
|
||||||
E440519F227BB0AB0090CD6F /* UIState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIState.swift; sourceTree = "<group>"; };
|
|
||||||
E450AD7D222620A10091BED3 /* Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Album.swift; sourceTree = "<group>"; };
|
E450AD7D222620A10091BED3 /* Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Album.swift; sourceTree = "<group>"; };
|
||||||
E450AD8522262AE60091BED3 /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = Carthage/Build/Mac/SwiftyJSON.framework; sourceTree = "<group>"; };
|
E450AD8522262AE60091BED3 /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = Carthage/Build/Mac/SwiftyJSON.framework; sourceTree = "<group>"; };
|
||||||
E450AD8C22262C590091BED3 /* PromiseKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PromiseKit.framework; path = Carthage/Build/Mac/PromiseKit.framework; sourceTree = "<group>"; };
|
E450AD8C22262C590091BED3 /* PromiseKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PromiseKit.framework; path = Carthage/Build/Mac/PromiseKit.framework; sourceTree = "<group>"; };
|
||||||
E450AD8E22262C620091BED3 /* PromiseKit.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = PromiseKit.framework.dSYM; path = Carthage/Build/Mac/PromiseKit.framework.dSYM; sourceTree = "<group>"; };
|
E450AD8E22262C620091BED3 /* PromiseKit.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = PromiseKit.framework.dSYM; path = Carthage/Build/Mac/PromiseKit.framework.dSYM; sourceTree = "<group>"; };
|
||||||
E450AD9022262C780091BED3 /* SwiftyJSON.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = SwiftyJSON.framework.dSYM; path = Carthage/Build/Mac/SwiftyJSON.framework.dSYM; sourceTree = "<group>"; };
|
E450AD9022262C780091BED3 /* SwiftyJSON.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = SwiftyJSON.framework.dSYM; path = Carthage/Build/Mac/SwiftyJSON.framework.dSYM; sourceTree = "<group>"; };
|
||||||
E450AD9422262DF10091BED3 /* CoverArtQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoverArtQueue.swift; sourceTree = "<group>"; };
|
E450AD9422262DF10091BED3 /* AlbumArtQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumArtQueue.swift; sourceTree = "<group>"; };
|
||||||
E450AD96222633920091BED3 /* Alamofire.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = Alamofire.framework.dSYM; path = Carthage/Build/Mac/Alamofire.framework.dSYM; sourceTree = "<group>"; };
|
E450AD96222633920091BED3 /* Alamofire.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = Alamofire.framework.dSYM; path = Carthage/Build/Mac/Alamofire.framework.dSYM; sourceTree = "<group>"; };
|
||||||
E450AD97222633920091BED3 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/Mac/Alamofire.framework; sourceTree = "<group>"; };
|
E450AD97222633920091BED3 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/Mac/Alamofire.framework; sourceTree = "<group>"; };
|
||||||
E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PersephoneBridgingHeader.h; sourceTree = "<group>"; };
|
E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PersephoneBridgingHeader.h; sourceTree = "<group>"; };
|
||||||
@ -280,42 +239,20 @@
|
|||||||
E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumViewLayout.swift; sourceTree = "<group>"; };
|
E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumViewLayout.swift; sourceTree = "<group>"; };
|
||||||
E4928E0A2218D62A001D4BEA /* CGColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGColor.swift; sourceTree = "<group>"; };
|
E4928E0A2218D62A001D4BEA /* CGColor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CGColor.swift; sourceTree = "<group>"; };
|
||||||
E4A642D922090CBE00067D21 /* 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 /* AlbumArtPrefsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumArtPrefsController.swift; sourceTree = "<group>"; };
|
||||||
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = "<group>"; };
|
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>"; };
|
E4A83BF3222207D50098FED6 /* AlbumArtService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumArtService.swift; sourceTree = "<group>"; };
|
||||||
E4B11B52226928F20075461B /* AppState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppState.swift; sourceTree = "<group>"; };
|
|
||||||
E4B11B572269296C0075461B /* ReSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = ReSwift.framework; path = Carthage/Build/Mac/ReSwift.framework; sourceTree = "<group>"; };
|
|
||||||
E4B11B582269296C0075461B /* ReSwift.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = ReSwift.framework.dSYM; path = Carthage/Build/Mac/ReSwift.framework.dSYM; 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>"; };
|
|
||||||
E4B11B65226A4F830075461B /* PlayerState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerState.swift; sourceTree = "<group>"; };
|
|
||||||
E4B11B67226A4FA00075461B /* QueueState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueState.swift; sourceTree = "<group>"; };
|
|
||||||
E4B11B69226A4FBC0075461B /* AlbumListState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumListState.swift; sourceTree = "<group>"; };
|
|
||||||
E4B11B72226A6C770075461B /* TrackTimer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrackTimer.swift; sourceTree = "<group>"; };
|
|
||||||
E4B11B74226CC4D30075461B /* QueueReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueReducer.swift; sourceTree = "<group>"; };
|
|
||||||
E4B11B78226D346B0075461B /* AlbumListReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumListReducer.swift; sourceTree = "<group>"; };
|
|
||||||
E4B11B7C2274E36D0075461B /* Differ.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = Differ.framework.dSYM; path = Carthage/Build/Mac/Differ.framework.dSYM; sourceTree = "<group>"; };
|
|
||||||
E4B11B7D2274E36D0075461B /* Differ.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Differ.framework; path = Carthage/Build/Mac/Differ.framework; sourceTree = "<group>"; };
|
|
||||||
E4B11BA82274EDE30075461B /* Loading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Loading.swift; sourceTree = "<group>"; };
|
|
||||||
E4B11BB52275374B0075461B /* UserNotificationsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationsController.swift; sourceTree = "<group>"; };
|
|
||||||
E4B11BB7227538FA0075461B /* CurrentCoverArtView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CurrentCoverArtView.swift; sourceTree = "<group>"; };
|
|
||||||
E4B11BBD2275EDAA0075461B /* PlayerActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlayerActions.swift; sourceTree = "<group>"; };
|
|
||||||
E4B11BBF2275EE150075461B /* QueueActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueActions.swift; sourceTree = "<group>"; };
|
|
||||||
E4B11BC12275EE410075461B /* AlbumListActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumListActions.swift; sourceTree = "<group>"; };
|
|
||||||
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
||||||
E4C8B53D22349002009A20F3 /* MPDIdle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDIdle.swift; sourceTree = "<group>"; };
|
E4C8B53D22349002009A20F3 /* MPDIdle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDIdle.swift; sourceTree = "<group>"; };
|
||||||
E4E8CC8F2204EC7F0024217A /* Delegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delegate.swift; sourceTree = "<group>"; };
|
E4E8CC8F2204EC7F0024217A /* Delegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delegate.swift; sourceTree = "<group>"; };
|
||||||
E4E8CC912204F4B80024217A /* QueueViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueViewController.swift; sourceTree = "<group>"; };
|
E4E8CC912204F4B80024217A /* QueueViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueViewController.swift; sourceTree = "<group>"; };
|
||||||
|
E4E8CC932206097F0024217A /* NotificationsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsController.swift; sourceTree = "<group>"; };
|
||||||
E4E8CC9922075D370024217A /* MPDSong.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDSong.swift; sourceTree = "<group>"; };
|
E4E8CC9922075D370024217A /* MPDSong.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDSong.swift; sourceTree = "<group>"; };
|
||||||
E4EB2378220F10B8008C70C0 /* MPDPair.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDPair.swift; sourceTree = "<group>"; };
|
E4EB2378220F10B8008C70C0 /* MPDPair.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDPair.swift; sourceTree = "<group>"; };
|
||||||
E4EB237A220F7CF1008C70C0 /* MPDAlbum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDAlbum.swift; sourceTree = "<group>"; };
|
E4EB237A220F7CF1008C70C0 /* MPDAlbum.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDAlbum.swift; sourceTree = "<group>"; };
|
||||||
E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueDataSource.swift; sourceTree = "<group>"; };
|
E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueDataSource.swift; sourceTree = "<group>"; };
|
||||||
E4F6B462221E125900ACF42A /* QueueItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueItem.swift; sourceTree = "<group>"; };
|
E4F6B462221E125900ACF42A /* QueueItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueItem.swift; sourceTree = "<group>"; };
|
||||||
E4F6B466221E233200ACF42A /* AlbumDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDataSource.swift; sourceTree = "<group>"; };
|
E4F6B466221E233200ACF42A /* AlbumDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDataSource.swift; sourceTree = "<group>"; };
|
||||||
E4FF718D2276010E00D4C412 /* PreferencesState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesState.swift; sourceTree = "<group>"; };
|
|
||||||
E4FF718F227601B400D4C412 /* PreferencesReducer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesReducer.swift; sourceTree = "<group>"; };
|
|
||||||
E4FF71912276029000D4C412 /* PreferencesActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesActions.swift; sourceTree = "<group>"; };
|
|
||||||
E4FF71932276043A00D4C412 /* MPDServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDServer.swift; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@ -323,14 +260,13 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
E4B11BA72274E4500075461B /* libmpdclient.2.dylib in Frameworks */,
|
E45E4FE122516953004B537F /* CryptoSwift.framework in Frameworks */,
|
||||||
E4B11B9A2274E43B0075461B /* CryptoSwift.framework in Frameworks */,
|
E41B22C021FB6BBA00D544F6 /* libmpdclient.2.dylib in Frameworks */,
|
||||||
E4B11B962274E43B0075461B /* Differ.framework in Frameworks */,
|
E421ACA3221F73C4008B2449 /* MediaKeyTap.framework in Frameworks */,
|
||||||
E4B11BA02274E43B0075461B /* SwiftyJSON.framework in Frameworks */,
|
E450AD8622262AE60091BED3 /* SwiftyJSON.framework in Frameworks */,
|
||||||
E4B11B9E2274E43B0075461B /* PromiseKit.framework in Frameworks */,
|
E45E4FE0225168DA004B537F /* CryptoSwift.framework in Frameworks */,
|
||||||
E4B11B9C2274E43B0075461B /* PMKFoundation.framework in Frameworks */,
|
E450ADA32229E7E00091BED3 /* PMKFoundation.framework in Frameworks */,
|
||||||
E4B11BA22274E43B0075461B /* MediaKeyTap.framework in Frameworks */,
|
E450AD9222262C970091BED3 /* PromiseKit.framework in Frameworks */,
|
||||||
E4B11B982274E43B0075461B /* ReSwift.framework in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -381,7 +317,6 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E407861B2110CE6E006887B1 /* AppDelegate.swift */,
|
E407861B2110CE6E006887B1 /* AppDelegate.swift */,
|
||||||
E44051932278765A0090CD6F /* App.swift */,
|
|
||||||
E407861F2110CE70006887B1 /* Assets.xcassets */,
|
E407861F2110CE70006887B1 /* Assets.xcassets */,
|
||||||
E4D1B597220BA3A20026F233 /* Controllers */,
|
E4D1B597220BA3A20026F233 /* Controllers */,
|
||||||
E4F6B45E221E117600ACF42A /* DataSources */,
|
E4F6B45E221E117600ACF42A /* DataSources */,
|
||||||
@ -397,7 +332,6 @@
|
|||||||
E4A83BEC2221F5DD0098FED6 /* Preferences */,
|
E4A83BEC2221F5DD0098FED6 /* Preferences */,
|
||||||
E4D1B598220BA3C90026F233 /* Resources */,
|
E4D1B598220BA3C90026F233 /* Resources */,
|
||||||
E4A83BF2222207BE0098FED6 /* Services */,
|
E4A83BF2222207BE0098FED6 /* Services */,
|
||||||
E4B11B64226A4F460075461B /* State */,
|
|
||||||
E408D3C3220E138B0006D9BE /* Views */,
|
E408D3C3220E138B0006D9BE /* Views */,
|
||||||
);
|
);
|
||||||
path = Persephone;
|
path = Persephone;
|
||||||
@ -456,8 +390,6 @@
|
|||||||
children = (
|
children = (
|
||||||
E47E2FD62220720300F747E6 /* AlbumItemView.swift */,
|
E47E2FD62220720300F747E6 /* AlbumItemView.swift */,
|
||||||
E47E2FD222205D2500F747E6 /* MainWindow.swift */,
|
E47E2FD222205D2500F747E6 /* MainWindow.swift */,
|
||||||
E4B11BB7227538FA0075461B /* CurrentCoverArtView.swift */,
|
|
||||||
E423563F228623D2001216D6 /* QueueSongTitleView.swift */,
|
|
||||||
);
|
);
|
||||||
path = Views;
|
path = Views;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -465,10 +397,6 @@
|
|||||||
E41B22BE21FB6B3300D544F6 /* Frameworks */ = {
|
E41B22BE21FB6B3300D544F6 /* Frameworks */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E4B11B7D2274E36D0075461B /* Differ.framework */,
|
|
||||||
E4B11B7C2274E36D0075461B /* Differ.framework.dSYM */,
|
|
||||||
E4B11B572269296C0075461B /* ReSwift.framework */,
|
|
||||||
E4B11B582269296C0075461B /* ReSwift.framework.dSYM */,
|
|
||||||
E45E4FDE225168DA004B537F /* CryptoSwift.framework */,
|
E45E4FDE225168DA004B537F /* CryptoSwift.framework */,
|
||||||
E45E4FDD225168DA004B537F /* CryptoSwift.framework.dSYM */,
|
E45E4FDD225168DA004B537F /* CryptoSwift.framework.dSYM */,
|
||||||
E450ADA02229E7C90091BED3 /* PMKFoundation.framework */,
|
E450ADA02229E7C90091BED3 /* PMKFoundation.framework */,
|
||||||
@ -540,9 +468,9 @@
|
|||||||
E41E530C223EF4BA00173814 /* Extensions */ = {
|
E41E530C223EF4BA00173814 /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E41E530D223EF4CF00173814 /* CoverArtService+Caching.swift */,
|
E41E530D223EF4CF00173814 /* AlbumArtService+Caching.swift */,
|
||||||
E41E5311223EF74A00173814 /* CoverArtService+Filesystem.swift */,
|
E41E5311223EF74A00173814 /* AlbumArtService+Filesystem.swift */,
|
||||||
E41E530F223EF6CE00173814 /* CoverArtService+Remote.swift */,
|
E41E530F223EF6CE00173814 /* AlbumArtService+Remote.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -550,7 +478,7 @@
|
|||||||
E450AD8922262B420091BED3 /* Operations */ = {
|
E450AD8922262B420091BED3 /* Operations */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E450AD9422262DF10091BED3 /* CoverArtQueue.swift */,
|
E450AD9422262DF10091BED3 /* AlbumArtQueue.swift */,
|
||||||
);
|
);
|
||||||
path = Operations;
|
path = Operations;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -585,7 +513,7 @@
|
|||||||
E4A83BED2221F5E60098FED6 /* Controllers */ = {
|
E4A83BED2221F5E60098FED6 /* Controllers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E4A83BEE2221F8CF0098FED6 /* CoverArtPrefsController.swift */,
|
E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.swift */,
|
||||||
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */,
|
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */,
|
||||||
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */,
|
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */,
|
||||||
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */,
|
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */,
|
||||||
@ -596,56 +524,13 @@
|
|||||||
E4A83BF2222207BE0098FED6 /* Services */ = {
|
E4A83BF2222207BE0098FED6 /* Services */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E4A83BF3222207D50098FED6 /* CoverArtService.swift */,
|
E4A83BF3222207D50098FED6 /* AlbumArtService.swift */,
|
||||||
E439109722640213002982E9 /* SongNotifierService.swift */,
|
E439109722640213002982E9 /* SongNotifierService.swift */,
|
||||||
E41E530C223EF4BA00173814 /* Extensions */,
|
E41E530C223EF4BA00173814 /* Extensions */,
|
||||||
);
|
);
|
||||||
path = Services;
|
path = Services;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
E4B11B5F226A4BED0075461B /* Reducers */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
E4B11B60226A4BFF0075461B /* PlayerReducer.swift */,
|
|
||||||
E4B11B62226A4C510075461B /* AppReducer.swift */,
|
|
||||||
E4B11B74226CC4D30075461B /* QueueReducer.swift */,
|
|
||||||
E4B11B78226D346B0075461B /* AlbumListReducer.swift */,
|
|
||||||
E4FF718F227601B400D4C412 /* PreferencesReducer.swift */,
|
|
||||||
E440519922787CF60090CD6F /* MPDReducer.swift */,
|
|
||||||
E440519D227BB0720090CD6F /* UIReducer.swift */,
|
|
||||||
);
|
|
||||||
path = Reducers;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
E4B11B64226A4F460075461B /* State */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
E4B11B6B226A5AF50075461B /* Actions */,
|
|
||||||
E4B11B5F226A4BED0075461B /* Reducers */,
|
|
||||||
E4B11B52226928F20075461B /* AppState.swift */,
|
|
||||||
E440519F227BB0AB0090CD6F /* UIState.swift */,
|
|
||||||
E4B11B65226A4F830075461B /* PlayerState.swift */,
|
|
||||||
E4B11B67226A4FA00075461B /* QueueState.swift */,
|
|
||||||
E4B11B69226A4FBC0075461B /* AlbumListState.swift */,
|
|
||||||
E4FF718D2276010E00D4C412 /* PreferencesState.swift */,
|
|
||||||
E440519722787CB40090CD6F /* MPDState.swift */,
|
|
||||||
);
|
|
||||||
path = State;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
E4B11B6B226A5AF50075461B /* Actions */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
E4B11BC12275EE410075461B /* AlbumListActions.swift */,
|
|
||||||
E4B11BBD2275EDAA0075461B /* PlayerActions.swift */,
|
|
||||||
E4B11BBF2275EE150075461B /* QueueActions.swift */,
|
|
||||||
E4FF71912276029000D4C412 /* PreferencesActions.swift */,
|
|
||||||
E4405195227879960090CD6F /* MPDActions.swift */,
|
|
||||||
E440519B227BAF2E0090CD6F /* UIActions.swift */,
|
|
||||||
);
|
|
||||||
path = Actions;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
E4D1B594220BA2490026F233 /* Models */ = {
|
E4D1B594220BA2490026F233 /* Models */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
@ -673,9 +558,8 @@
|
|||||||
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */,
|
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */,
|
||||||
E47E2FD4222071FD00F747E6 /* AlbumViewItem.swift */,
|
E47E2FD4222071FD00F747E6 /* AlbumViewItem.swift */,
|
||||||
E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */,
|
E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */,
|
||||||
E4405191227644340090CD6F /* MPDServerController.swift */,
|
E4E8CC932206097F0024217A /* NotificationsController.swift */,
|
||||||
E4E8CC912204F4B80024217A /* QueueViewController.swift */,
|
E4E8CC912204F4B80024217A /* QueueViewController.swift */,
|
||||||
E4B11BB52275374B0075461B /* UserNotificationsController.swift */,
|
|
||||||
E465049921E94DF500A70F4C /* WindowController.swift */,
|
E465049921E94DF500A70F4C /* WindowController.swift */,
|
||||||
);
|
);
|
||||||
path = Controllers;
|
path = Controllers;
|
||||||
@ -703,12 +587,10 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E450AD7D222620A10091BED3 /* Album.swift */,
|
E450AD7D222620A10091BED3 /* Album.swift */,
|
||||||
E4B11BA82274EDE30075461B /* Loading.swift */,
|
E40F41F2221EDE27004B6CB8 /* Preferences.swift */,
|
||||||
E4FF71932276043A00D4C412 /* MPDServer.swift */,
|
|
||||||
E4F6B462221E125900ACF42A /* QueueItem.swift */,
|
E4F6B462221E125900ACF42A /* QueueItem.swift */,
|
||||||
E419E2862249B96600216A8C /* Song.swift */,
|
E419E2862249B96600216A8C /* Song.swift */,
|
||||||
E47E2FDC2220A6D100F747E6 /* Time.swift */,
|
E47E2FDC2220A6D100F747E6 /* Time.swift */,
|
||||||
E4B11B72226A6C770075461B /* TrackTimer.swift */,
|
|
||||||
);
|
);
|
||||||
path = Models;
|
path = Models;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -724,8 +606,8 @@
|
|||||||
E40786152110CE6E006887B1 /* Frameworks */,
|
E40786152110CE6E006887B1 /* Frameworks */,
|
||||||
E40786162110CE6E006887B1 /* Resources */,
|
E40786162110CE6E006887B1 /* Resources */,
|
||||||
E42A98F122430936004D8180 /* ShellScript */,
|
E42A98F122430936004D8180 /* ShellScript */,
|
||||||
|
E41B22C221FB6C3300D544F6 /* Embed Libraries */,
|
||||||
E421AC9B221F7319008B2449 /* CopyFiles */,
|
E421AC9B221F7319008B2449 /* CopyFiles */,
|
||||||
E4B11BA52274E43B0075461B /* Embed Frameworks */,
|
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
@ -842,8 +724,6 @@
|
|||||||
E42A8F3C22176D6400A13ED9 /* README.md in Resources */,
|
E42A8F3C22176D6400A13ED9 /* README.md in Resources */,
|
||||||
E450AD8F22262C620091BED3 /* PromiseKit.framework.dSYM in Resources */,
|
E450AD8F22262C620091BED3 /* PromiseKit.framework.dSYM in Resources */,
|
||||||
E408D3CB220E341D0006D9BE /* AlbumViewItem.xib in Resources */,
|
E408D3CB220E341D0006D9BE /* AlbumViewItem.xib in Resources */,
|
||||||
E4B11B5A2269296C0075461B /* ReSwift.framework.dSYM in Resources */,
|
|
||||||
E4B11B7E2274E36D0075461B /* Differ.framework.dSYM in Resources */,
|
|
||||||
E40786232110CE70006887B1 /* Main.storyboard in Resources */,
|
E40786232110CE70006887B1 /* Main.storyboard in Resources */,
|
||||||
E450ADA12229E7C90091BED3 /* PMKFoundation.framework.dSYM in Resources */,
|
E450ADA12229E7C90091BED3 /* PMKFoundation.framework.dSYM in Resources */,
|
||||||
E47E2FCC2220573500F747E6 /* MediaKeyTap.framework.dSYM in Resources */,
|
E47E2FCC2220573500F747E6 /* MediaKeyTap.framework.dSYM in Resources */,
|
||||||
@ -891,81 +771,55 @@
|
|||||||
isa = PBXSourcesBuildPhase;
|
isa = PBXSourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
E4A83BEF2221F8CF0098FED6 /* CoverArtPrefsController.swift in Sources */,
|
E4A83BEF2221F8CF0098FED6 /* AlbumArtPrefsController.swift in Sources */,
|
||||||
E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */,
|
E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */,
|
||||||
E4B11BA92274EDE30075461B /* Loading.swift in Sources */,
|
|
||||||
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */,
|
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */,
|
||||||
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */,
|
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */,
|
||||||
E4B11BB62275374B0075461B /* UserNotificationsController.swift in Sources */,
|
|
||||||
E4B11B68226A4FA00075461B /* QueueState.swift in Sources */,
|
|
||||||
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */,
|
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */,
|
||||||
E4405196227879960090CD6F /* MPDActions.swift in Sources */,
|
|
||||||
E4405192227644340090CD6F /* MPDServerController.swift in Sources */,
|
|
||||||
E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */,
|
E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */,
|
||||||
E4F6B460221E119B00ACF42A /* QueueDataSource.swift in Sources */,
|
E4F6B460221E119B00ACF42A /* QueueDataSource.swift in Sources */,
|
||||||
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */,
|
E4C8B53C22342005009A20F3 /* PreferencesWindowController.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 */,
|
E41E5310223EF6CE00173814 /* AlbumArtService+Remote.swift in Sources */,
|
||||||
E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */,
|
E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */,
|
||||||
E440519822787CB40090CD6F /* MPDState.swift in Sources */,
|
|
||||||
E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */,
|
E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */,
|
||||||
E4235640228623D2001216D6 /* QueueSongTitleView.swift in Sources */,
|
|
||||||
E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */,
|
E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */,
|
||||||
E4B11BC02275EE150075461B /* QueueActions.swift in Sources */,
|
|
||||||
E47E2FD72220720300F747E6 /* AlbumItemView.swift in Sources */,
|
E47E2FD72220720300F747E6 /* AlbumItemView.swift in Sources */,
|
||||||
E450AD9522262DF10091BED3 /* CoverArtQueue.swift in Sources */,
|
E450AD9522262DF10091BED3 /* AlbumArtQueue.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 */,
|
||||||
|
E4E8CC942206097F0024217A /* NotificationsController.swift in Sources */,
|
||||||
E408D3B6220DD8970006D9BE /* Notification.swift in Sources */,
|
E408D3B6220DD8970006D9BE /* Notification.swift in Sources */,
|
||||||
E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */,
|
E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */,
|
||||||
E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */,
|
E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */,
|
||||||
E4EB2379220F10B8008C70C0 /* MPDPair.swift in Sources */,
|
E4EB2379220F10B8008C70C0 /* MPDPair.swift in Sources */,
|
||||||
E440519E227BB0720090CD6F /* UIReducer.swift in Sources */,
|
|
||||||
E4FF7190227601B400D4C412 /* PreferencesReducer.swift in Sources */,
|
|
||||||
E4F6B463221E125900ACF42A /* QueueItem.swift in Sources */,
|
E4F6B463221E125900ACF42A /* QueueItem.swift in Sources */,
|
||||||
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */,
|
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */,
|
||||||
E4B11BC22275EE410075461B /* AlbumListActions.swift in Sources */,
|
|
||||||
E4B11B61226A4C000075461B /* PlayerReducer.swift in Sources */,
|
|
||||||
E4FF71922276029000D4C412 /* PreferencesActions.swift in Sources */,
|
|
||||||
E465049A21E94DF500A70F4C /* WindowController.swift in Sources */,
|
E465049A21E94DF500A70F4C /* WindowController.swift in Sources */,
|
||||||
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */,
|
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */,
|
||||||
|
E40F41F3221EDE27004B6CB8 /* Preferences.swift in Sources */,
|
||||||
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */,
|
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */,
|
||||||
E419E2872249B96600216A8C /* Song.swift in Sources */,
|
E419E2872249B96600216A8C /* Song.swift in Sources */,
|
||||||
E44051A0227BB0AB0090CD6F /* UIState.swift in Sources */,
|
|
||||||
E4FF718E2276010E00D4C412 /* PreferencesState.swift in Sources */,
|
|
||||||
E439109822640213002982E9 /* SongNotifierService.swift in Sources */,
|
E439109822640213002982E9 /* SongNotifierService.swift in Sources */,
|
||||||
E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */,
|
E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */,
|
||||||
E4B11B75226CC4D30075461B /* QueueReducer.swift in Sources */,
|
|
||||||
E41E5309223C020400173814 /* MPDClient+Command.swift in Sources */,
|
E41E5309223C020400173814 /* MPDClient+Command.swift in Sources */,
|
||||||
E4B11B73226A6C770075461B /* TrackTimer.swift in Sources */,
|
|
||||||
E44051942278765A0090CD6F /* App.swift in Sources */,
|
|
||||||
E4B11B79226D346B0075461B /* AlbumListReducer.swift in Sources */,
|
|
||||||
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */,
|
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */,
|
||||||
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */,
|
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */,
|
||||||
E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */,
|
E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */,
|
||||||
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */,
|
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */,
|
||||||
E4B11BB8227538FA0075461B /* CurrentCoverArtView.swift in Sources */,
|
|
||||||
E4E8CC9A22075D370024217A /* MPDSong.swift in Sources */,
|
E4E8CC9A22075D370024217A /* MPDSong.swift in Sources */,
|
||||||
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */,
|
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */,
|
||||||
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */,
|
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */,
|
||||||
E4A83BF4222207D50098FED6 /* CoverArtService.swift in Sources */,
|
E4A83BF4222207D50098FED6 /* AlbumArtService.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 */,
|
E41E530E223EF4CF00173814 /* AlbumArtService+Caching.swift in Sources */,
|
||||||
E4B11BBE2275EDAA0075461B /* PlayerActions.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 */,
|
E41E5312223EF74A00173814 /* AlbumArtService+Filesystem.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 */,
|
||||||
E4B11B53226928F20075461B /* AppState.swift in Sources */,
|
|
||||||
E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */,
|
E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */,
|
||||||
E440519A22787CF60090CD6F /* MPDReducer.swift in Sources */,
|
|
||||||
E4B11B6A226A4FBC0075461B /* AlbumListState.swift in Sources */,
|
|
||||||
E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */,
|
E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */,
|
||||||
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */,
|
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */,
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,18 +0,0 @@
|
|||||||
//
|
|
||||||
// App.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/30.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
struct App {
|
|
||||||
static let store = Store<AppState>(reducer: appReducer, state: nil)
|
|
||||||
static let trackTimer = TrackTimer()
|
|
||||||
static let userNotificationsController = UserNotificationsController()
|
|
||||||
static let mpdServerController = MPDServerController()
|
|
||||||
static let mpdClient = MPDClient(withDelegate: mpdServerController)
|
|
||||||
}
|
|
||||||
@ -6,134 +6,84 @@
|
|||||||
// Copyright © 2018 Dan Barber. All rights reserved.
|
// Copyright © 2018 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
import ReSwift
|
|
||||||
import MediaKeyTap
|
import MediaKeyTap
|
||||||
|
|
||||||
@NSApplicationMain
|
@NSApplicationMain
|
||||||
class AppDelegate: NSObject,
|
class AppDelegate: NSObject, NSApplicationDelegate, MediaKeyTapDelegate {
|
||||||
NSApplicationDelegate,
|
var preferences = Preferences()
|
||||||
MediaKeyTapDelegate {
|
|
||||||
var mediaKeyTap: MediaKeyTap?
|
var mediaKeyTap: MediaKeyTap?
|
||||||
|
|
||||||
|
static let mpdClient = MPDClient(
|
||||||
|
withDelegate: NotificationsController()
|
||||||
|
)
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
App.mpdServerController.connect()
|
connect()
|
||||||
instantiateUserNotificationsController()
|
|
||||||
|
preferences.addObserver(self, forKeyPath: "mpdHost")
|
||||||
|
preferences.addObserver(self, forKeyPath: "mpdPort")
|
||||||
|
|
||||||
mediaKeyTap = MediaKeyTap(delegate: self)
|
mediaKeyTap = MediaKeyTap(delegate: self)
|
||||||
mediaKeyTap?.start()
|
mediaKeyTap?.start()
|
||||||
|
|
||||||
App.store.subscribe(self) {
|
NotificationCenter.default.addObserver(
|
||||||
$0.select {
|
self,
|
||||||
$0.uiState
|
selector: #selector(enableUpdateDatabaseMenuItem),
|
||||||
}
|
name: Notification.databaseUpdateFinished,
|
||||||
}
|
object: AppDelegate.mpdClient
|
||||||
}
|
)
|
||||||
|
|
||||||
func instantiateUserNotificationsController() {
|
|
||||||
_ = App.userNotificationsController
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationWillTerminate(_ aNotification: Notification) {
|
func applicationWillTerminate(_ aNotification: Notification) {
|
||||||
App.mpdServerController.disconnect()
|
disconnect()
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationDockMenu(_ sender: NSApplication) -> NSMenu? {
|
override func observeValue(
|
||||||
let dockMenu = NSMenu()
|
forKeyPath keyPath: String?,
|
||||||
dockMenu.autoenablesItems = false
|
of object: Any?,
|
||||||
|
change: [NSKeyValueChangeKey : Any]?,
|
||||||
guard let state = App.store.state.playerState.state else { return nil }
|
context: UnsafeMutableRawPointer?
|
||||||
|
) {
|
||||||
if let currentSong = App.store.state.playerState.currentSong,
|
switch keyPath {
|
||||||
state.isOneOf([.playing, .paused]) {
|
case "mpdHost", "mpdPort":
|
||||||
|
disconnect()
|
||||||
let nowPlayingItem = NSMenuItem(title: "Now Playing", action: nil, keyEquivalent: "")
|
connect()
|
||||||
let songItem = NSMenuItem(title: currentSong.title, action: nil, keyEquivalent: "")
|
default:
|
||||||
let albumItem = NSMenuItem(
|
break
|
||||||
title: "\(currentSong.artist) — \(currentSong.album.title)",
|
|
||||||
action: nil,
|
|
||||||
keyEquivalent: ""
|
|
||||||
)
|
|
||||||
|
|
||||||
nowPlayingItem.isEnabled = false
|
|
||||||
songItem.indentationLevel = 1
|
|
||||||
songItem.isEnabled = false
|
|
||||||
albumItem.indentationLevel = 1
|
|
||||||
albumItem.isEnabled = false
|
|
||||||
|
|
||||||
dockMenu.addItem(nowPlayingItem)
|
|
||||||
dockMenu.addItem(songItem)
|
|
||||||
dockMenu.addItem(albumItem)
|
|
||||||
dockMenu.addItem(NSMenuItem.separator())
|
|
||||||
}
|
|
||||||
|
|
||||||
let playPauseMenuItem = NSMenuItem(
|
|
||||||
title: state == .playing ? "Pause" : "Play",
|
|
||||||
action: #selector(playPauseMenuAction),
|
|
||||||
keyEquivalent: ""
|
|
||||||
)
|
|
||||||
let stopMenuItem = NSMenuItem(title: "Stop", action: #selector(stopMenuAction), keyEquivalent: "")
|
|
||||||
let nextTrackMenuItem = NSMenuItem(title: "Next", action: #selector(nextTrackMenuAction), keyEquivalent: "")
|
|
||||||
let prevTrackMenuItem = NSMenuItem(title: "Previous", action: #selector(prevTrackMenuAction), keyEquivalent: "")
|
|
||||||
|
|
||||||
playPauseMenuItem.isEnabled = state.isOneOf([.playing, .paused, .stopped])
|
|
||||||
stopMenuItem.isEnabled = state.isOneOf([.playing, .paused])
|
|
||||||
nextTrackMenuItem.isEnabled = state.isOneOf([.playing, .paused])
|
|
||||||
prevTrackMenuItem.isEnabled = state.isOneOf([.playing, .paused])
|
|
||||||
|
|
||||||
dockMenu.addItem(playPauseMenuItem)
|
|
||||||
dockMenu.addItem(stopMenuItem)
|
|
||||||
dockMenu.addItem(nextTrackMenuItem)
|
|
||||||
dockMenu.addItem(prevTrackMenuItem)
|
|
||||||
|
|
||||||
return dockMenu
|
|
||||||
}
|
|
||||||
|
|
||||||
func setMainWindowStateMenuItem(state: MainWindowState) {
|
|
||||||
switch state {
|
|
||||||
case .open: mainWindowMenuItem.state = .on
|
|
||||||
case .closed: mainWindowMenuItem.state = .off
|
|
||||||
case .minimised: mainWindowMenuItem.state = .mixed
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func handle(mediaKey: MediaKey, event: KeyEvent) {
|
func handle(mediaKey: MediaKey, event: KeyEvent) {
|
||||||
switch mediaKey {
|
switch mediaKey {
|
||||||
case .playPause:
|
case .playPause:
|
||||||
App.store.dispatch(MPDPlayPauseAction())
|
AppDelegate.mpdClient.playPause()
|
||||||
case .next, .fastForward:
|
case .next, .fastForward:
|
||||||
App.store.dispatch(MPDNextTrackAction())
|
AppDelegate.mpdClient.nextTrack()
|
||||||
case .previous, .rewind:
|
case .previous, .rewind:
|
||||||
App.store.dispatch(MPDPrevTrackAction())
|
AppDelegate.mpdClient.prevTrack()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func connect() {
|
||||||
|
AppDelegate.mpdClient.connect(
|
||||||
|
host: preferences.mpdHostOrDefault,
|
||||||
|
port: preferences.mpdPortOrDefault
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func disconnect() {
|
||||||
|
AppDelegate.mpdClient.disconnect()
|
||||||
|
}
|
||||||
|
|
||||||
@IBAction func updateDatabase(_ sender: NSMenuItem) {
|
@IBAction func updateDatabase(_ sender: NSMenuItem) {
|
||||||
App.store.dispatch(MPDUpdateDatabaseAction())
|
sender.isEnabled = false
|
||||||
|
AppDelegate.mpdClient.updateDatabase()
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func playPauseMenuAction(_ sender: NSMenuItem) {
|
@objc func enableUpdateDatabaseMenuItem() {
|
||||||
App.store.dispatch(MPDPlayPauseAction())
|
updateDatabaseMenuItem?.isEnabled = true
|
||||||
}
|
|
||||||
@IBAction func stopMenuAction(_ sender: NSMenuItem) {
|
|
||||||
App.store.dispatch(MPDStopAction())
|
|
||||||
}
|
|
||||||
@IBAction func nextTrackMenuAction(_ sender: NSMenuItem) {
|
|
||||||
App.store.dispatch(MPDNextTrackAction())
|
|
||||||
}
|
|
||||||
@IBAction func prevTrackMenuAction(_ sender: NSMenuItem) {
|
|
||||||
App.store.dispatch(MPDPrevTrackAction())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBOutlet weak var mainWindowMenuItem: NSMenuItem!
|
|
||||||
@IBOutlet weak var updateDatabaseMenuItem: NSMenuItem!
|
@IBOutlet weak var updateDatabaseMenuItem: NSMenuItem!
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AppDelegate: StoreSubscriber {
|
|
||||||
typealias StoreSubscriberStateType = UIState
|
|
||||||
|
|
||||||
func newState(state: UIState) {
|
|
||||||
updateDatabaseMenuItem.isEnabled = !state.databaseUpdating
|
|
||||||
setMainWindowStateMenuItem(state: state.mainWindowState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "repeatButton.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "repeatButton@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
},
|
|
||||||
"properties" : {
|
|
||||||
"template-rendering-intent" : "template"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 299 B |
|
Before Width: | Height: | Size: 1.2 KiB |
@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"images" : [
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "shuffleButton.png",
|
|
||||||
"scale" : "1x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"filename" : "shuffleButton@2x.png",
|
|
||||||
"scale" : "2x"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"idiom" : "universal",
|
|
||||||
"scale" : "3x"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"info" : {
|
|
||||||
"version" : 1,
|
|
||||||
"author" : "xcode"
|
|
||||||
},
|
|
||||||
"properties" : {
|
|
||||||
"template-rendering-intent" : "template"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 353 B |
|
Before Width: | Height: | Size: 1.3 KiB |
@ -6,13 +6,13 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
import ReSwift
|
|
||||||
import Differ
|
|
||||||
|
|
||||||
class AlbumViewController: NSViewController,
|
class AlbumViewController: NSViewController,
|
||||||
NSCollectionViewDelegate,
|
NSCollectionViewDelegate,
|
||||||
NSCollectionViewDelegateFlowLayout {
|
NSCollectionViewDelegateFlowLayout {
|
||||||
|
var preferences = Preferences()
|
||||||
|
|
||||||
let paddingWidth: CGFloat = 40
|
let paddingWidth: CGFloat = 40
|
||||||
let gutterWidth: CGFloat = 20
|
let gutterWidth: CGFloat = 20
|
||||||
|
|
||||||
@ -21,17 +21,26 @@ class AlbumViewController: NSViewController,
|
|||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
App.store.subscribe(self) {
|
|
||||||
$0.select { $0.albumListState }
|
|
||||||
}
|
|
||||||
|
|
||||||
albumScrollView.postsBoundsChangedNotifications = true
|
albumScrollView.postsBoundsChangedNotifications = true
|
||||||
|
|
||||||
albumCollectionView.dataSource = dataSource
|
NotificationCenter.default.addObserver(
|
||||||
}
|
self,
|
||||||
|
selector: #selector(updateAlbums(_:)),
|
||||||
|
name: Notification.loadedAlbums,
|
||||||
|
object: AppDelegate.mpdClient
|
||||||
|
)
|
||||||
|
|
||||||
deinit {
|
NotificationCenter.default.addObserver(
|
||||||
App.store.unsubscribe(self)
|
self,
|
||||||
|
selector: #selector(clearAlbums(_:)),
|
||||||
|
name: Notification.willDisconnect,
|
||||||
|
object: AppDelegate.mpdClient
|
||||||
|
)
|
||||||
|
|
||||||
|
albumCollectionView.dataSource = dataSource
|
||||||
|
|
||||||
|
preferences.addObserver(self, forKeyPath: "mpdLibraryDir")
|
||||||
|
preferences.addObserver(self, forKeyPath: "fetchMissingArtworkFromInternet")
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewWillLayout() {
|
override func viewWillLayout() {
|
||||||
@ -53,21 +62,37 @@ class AlbumViewController: NSViewController,
|
|||||||
layout.setScrollPosition()
|
layout.setScrollPosition()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func observeValue(
|
||||||
|
forKeyPath keyPath: String?,
|
||||||
|
of object: Any?,
|
||||||
|
change: [NSKeyValueChangeKey : Any]?,
|
||||||
|
context: UnsafeMutableRawPointer?
|
||||||
|
) {
|
||||||
|
switch keyPath {
|
||||||
|
case "mpdLibraryDir":
|
||||||
|
albumCollectionView.reloadData()
|
||||||
|
case "fetchMissingArtworkFromInternet":
|
||||||
|
dataSource.resetCoverArt()
|
||||||
|
albumCollectionView.reloadData()
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func updateAlbums(_ notification: Notification) {
|
||||||
|
guard let albums = notification.userInfo?[Notification.albumsKey] as? [MPDClient.MPDAlbum]
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
dataSource.albums = albums.map { Album(mpdAlbum: $0) }
|
||||||
|
albumCollectionView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func clearAlbums(_ notification: Notification) {
|
||||||
|
dataSource.albums = []
|
||||||
|
|
||||||
|
albumCollectionView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
@IBOutlet var albumScrollView: NSScrollView!
|
@IBOutlet var albumScrollView: NSScrollView!
|
||||||
@IBOutlet var albumCollectionView: NSCollectionView!
|
@IBOutlet var albumCollectionView: NSCollectionView!
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AlbumViewController: StoreSubscriber {
|
|
||||||
typealias StoreSubscriberStateType = AlbumListState
|
|
||||||
|
|
||||||
func newState(state: StoreSubscriberStateType) {
|
|
||||||
let oldAlbums = dataSource.albums
|
|
||||||
|
|
||||||
dataSource.albums = state.albums
|
|
||||||
|
|
||||||
albumCollectionView.animateItemChanges(
|
|
||||||
oldData: oldAlbums,
|
|
||||||
newData: dataSource.albums
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
class AlbumViewItem: NSCollectionViewItem {
|
class AlbumViewItem: NSCollectionViewItem {
|
||||||
var observer: NSKeyValueObservation?
|
var observer: NSKeyValueObservation?
|
||||||
@ -31,11 +31,10 @@ 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
|
||||||
|
|
||||||
switch album.coverArt {
|
if let coverArt = album.coverArt {
|
||||||
case .loaded(let coverArt):
|
albumCoverView.image = coverArt
|
||||||
albumCoverView.image = coverArt ?? .defaultCoverArt
|
} else {
|
||||||
default:
|
|
||||||
albumCoverView.image = .defaultCoverArt
|
albumCoverView.image = .defaultCoverArt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,8 +52,8 @@ class AlbumViewItem: NSCollectionViewItem {
|
|||||||
|
|
||||||
@IBAction func playAlbum(_ sender: Any) {
|
@IBAction func playAlbum(_ sender: Any) {
|
||||||
guard let album = album else { return }
|
guard let album = album else { return }
|
||||||
|
|
||||||
App.store.dispatch(MPDPlayAlbum(album: album.mpdAlbum))
|
AppDelegate.mpdClient.playAlbum(album.mpdAlbum)
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBOutlet var albumCoverView: NSImageView!
|
@IBOutlet var albumCoverView: NSImageView!
|
||||||
|
|||||||
@ -1,85 +0,0 @@
|
|||||||
//
|
|
||||||
// MPDServerController.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/28.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
class MPDServerController {
|
|
||||||
init() {
|
|
||||||
App.store.subscribe(self) {
|
|
||||||
$0.select { $0.preferencesState.mpdServer }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func connect() {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(MPDConnectAction())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func disconnect() {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(MPDDisconnectAction())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension MPDServerController: StoreSubscriber {
|
|
||||||
typealias StoreSubscriberStateType = MPDServer
|
|
||||||
|
|
||||||
func newState(state: MPDServer) {
|
|
||||||
disconnect()
|
|
||||||
connect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension MPDServerController: MPDClientDelegate {
|
|
||||||
func didConnect(mpdClient: MPDClient) {}
|
|
||||||
|
|
||||||
func willDisconnect(mpdClient: MPDClient) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(UpdateAlbumListAction(albums: []))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func didUpdateStatus(mpdClient: MPDClient, status: MPDClient.MPDStatus) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(UpdateStatusAction(status: status))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func willStartDatabaseUpdate(mpdClient: MPDClient) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(DatabaseUpdateStartedAction())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func didFinishDatabaseUpdate(mpdClient: MPDClient) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(DatabaseUpdateFinishedAction())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func didUpdateQueue(mpdClient: MPDClient, queue: [MPDClient.MPDSong]) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(UpdateQueueAction(queue: queue))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func didUpdateQueuePos(mpdClient: MPDClient, song: Int) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(UpdateQueuePosAction(queuePos: song))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func didLoadAlbums(mpdClient: MPDClient, albums: [MPDClient.MPDAlbum]) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(UpdateAlbumListAction(albums: albums))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
class MainSplitViewController: NSSplitViewController {
|
class MainSplitViewController: NSSplitViewController {
|
||||||
override func keyDown(with event: NSEvent) {
|
override func keyDown(with event: NSEvent) {
|
||||||
|
|||||||
77
Persephone/Controllers/NotificationsController.swift
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
//
|
||||||
|
// MPDClientNotificationHandler.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2019/2/02.
|
||||||
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class NotificationsController: MPDClientDelegate {
|
||||||
|
let notificationQueue = DispatchQueue.main
|
||||||
|
|
||||||
|
func didConnect(mpdClient: MPDClient) {
|
||||||
|
sendNotification(name: Notification.didConnect)
|
||||||
|
}
|
||||||
|
|
||||||
|
func willDisconnect(mpdClient: MPDClient) {
|
||||||
|
sendNotification(name: Notification.willDisconnect)
|
||||||
|
}
|
||||||
|
|
||||||
|
func didUpdateState(mpdClient: MPDClient, state: MPDClient.MPDStatus.State) {
|
||||||
|
sendNotification(
|
||||||
|
name: Notification.stateChanged,
|
||||||
|
userInfo: [Notification.stateKey: state]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func didUpdateTime(mpdClient: MPDClient, total: UInt, elapsedMs: UInt) {
|
||||||
|
sendNotification(
|
||||||
|
name: Notification.timeChanged,
|
||||||
|
userInfo: [
|
||||||
|
Notification.totalTimeKey: total,
|
||||||
|
Notification.elapsedTimeMsKey: elapsedMs
|
||||||
|
]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func willStartDatabaseUpdate(mpdClient: MPDClient) {
|
||||||
|
sendNotification(name: Notification.databaseUpdateStarted)
|
||||||
|
}
|
||||||
|
|
||||||
|
func didFinishDatabaseUpdate(mpdClient: MPDClient) {
|
||||||
|
sendNotification(name: Notification.databaseUpdateFinished)
|
||||||
|
}
|
||||||
|
|
||||||
|
func didUpdateQueue(mpdClient: MPDClient, queue: [MPDClient.MPDSong]) {
|
||||||
|
sendNotification(
|
||||||
|
name: Notification.queueChanged,
|
||||||
|
userInfo: [Notification.queueKey: queue]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func didUpdateQueuePos(mpdClient: MPDClient, song: Int) {
|
||||||
|
sendNotification(
|
||||||
|
name: Notification.queuePosChanged,
|
||||||
|
userInfo: [Notification.queuePosKey: song]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func didLoadAlbums(mpdClient: MPDClient, albums: [MPDClient.MPDAlbum]) {
|
||||||
|
sendNotification(
|
||||||
|
name: Notification.loadedAlbums,
|
||||||
|
userInfo: [Notification.albumsKey: albums]
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func sendNotification(name: Notification.Name, userInfo: [AnyHashable : Any] = [:]) {
|
||||||
|
self.notificationQueue.async {
|
||||||
|
NotificationCenter.default.post(
|
||||||
|
name: name,
|
||||||
|
object: AppDelegate.mpdClient,
|
||||||
|
userInfo: userInfo
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -6,26 +6,24 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
import ReSwift
|
import PromiseKit
|
||||||
|
|
||||||
class QueueViewController: NSViewController,
|
class QueueViewController: NSViewController,
|
||||||
NSOutlineViewDelegate {
|
NSOutlineViewDelegate {
|
||||||
var dataSource = QueueDataSource()
|
var dataSource = QueueDataSource()
|
||||||
|
|
||||||
@IBOutlet var queueView: NSOutlineView!
|
@IBOutlet var queueView: NSOutlineView!
|
||||||
@IBOutlet var queueCoverArtImage: NSImageView!
|
@IBOutlet var queueAlbumArtImage: NSImageView!
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
App.store.subscribe(self) {
|
setupNotificationObservers()
|
||||||
$0.select { $0.queueState }
|
|
||||||
}
|
|
||||||
|
|
||||||
queueView.dataSource = dataSource
|
queueView.dataSource = dataSource
|
||||||
queueView.columnAutoresizingStyle = .sequentialColumnAutoresizingStyle
|
queueView.columnAutoresizingStyle = .sequentialColumnAutoresizingStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
override func keyDown(with event: NSEvent) {
|
override func keyDown(with event: NSEvent) {
|
||||||
switch event.keyCode {
|
switch event.keyCode {
|
||||||
@ -40,14 +38,68 @@ class QueueViewController: NSViewController,
|
|||||||
let newQueuePos = queueView.selectedRow - 1
|
let newQueuePos = queueView.selectedRow - 1
|
||||||
|
|
||||||
if newQueuePos >= 0 {
|
if newQueuePos >= 0 {
|
||||||
App.store.dispatch(MPDPlayTrack(queuePos: newQueuePos))
|
AppDelegate.mpdClient.playTrack(at: newQueuePos)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func stateChanged(_ notification: Notification) {
|
||||||
|
guard let state = notification.userInfo?[Notification.stateKey] as? MPDClient.MPDStatus.State
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
dataSource.setQueueIcon(state)
|
||||||
|
}
|
||||||
|
|
||||||
|
func notifyTrack() {
|
||||||
|
guard let currentSong = dataSource.currentSong,
|
||||||
|
let status = AppDelegate.mpdClient.status,
|
||||||
|
status.state == .playing
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
SongNotifierService(song: currentSong, image: queueAlbumArtImage.image)
|
||||||
|
.deliver()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func queueChanged(_ notification: Notification) {
|
||||||
|
guard let queue = notification.userInfo?[Notification.queueKey] as? [MPDClient.MPDSong]
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
dataSource.updateQueue(queue)
|
||||||
|
queueView.reloadData()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func queuePosChanged(_ notification: Notification) {
|
||||||
|
guard let queuePos = notification.userInfo?[Notification.queuePosKey] as? Int
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
dataSource.setQueuePos(queuePos)
|
||||||
|
queueView.reloadData()
|
||||||
|
updateAlbumArt()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateAlbumArt() {
|
||||||
|
if let playingQueueItem = dataSource.queue.first(where: { $0.isPlaying }) {
|
||||||
|
let albumArtService = AlbumArtService(song: playingQueueItem.song)
|
||||||
|
|
||||||
|
albumArtService.fetchBigAlbumArt()
|
||||||
|
.done() {
|
||||||
|
if let image = $0 {
|
||||||
|
self.queueAlbumArtImage.image = image
|
||||||
|
} else {
|
||||||
|
self.queueAlbumArtImage.image = NSImage.defaultCoverArt
|
||||||
|
}
|
||||||
|
|
||||||
|
self.notifyTrack()
|
||||||
|
}
|
||||||
|
.cauterize()
|
||||||
|
} else {
|
||||||
|
queueAlbumArtImage.image = NSImage.defaultCoverArt
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func outlineView(
|
func outlineView(
|
||||||
_ outlineView: NSOutlineView,
|
_ outlineView: NSOutlineView,
|
||||||
selectionIndexesForProposedSelection proposedSelectionIndexes: IndexSet
|
selectionIndexesForProposedSelection proposedSelectionIndexes: IndexSet
|
||||||
) -> IndexSet {
|
) -> IndexSet {
|
||||||
if proposedSelectionIndexes.contains(0) {
|
if proposedSelectionIndexes.contains(0) {
|
||||||
return IndexSet()
|
return IndexSet()
|
||||||
} else {
|
} else {
|
||||||
@ -72,13 +124,20 @@ class QueueViewController: NSViewController,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func cellForSongTitle(_ outlineView: NSOutlineView, with queueItem: QueueItem) -> NSView {
|
func cellForSongTitle(_ outlineView: NSOutlineView, with queueItem: QueueItem) -> NSView {
|
||||||
let cellView = outlineView.makeView(
|
let cellView = outlineView.makeView(
|
||||||
withIdentifier: .queueSongTitle,
|
withIdentifier: .queueSongTitle,
|
||||||
owner: self
|
owner: self
|
||||||
) as! QueueSongTitleView
|
) as! NSTableCellView
|
||||||
|
|
||||||
cellView.setQueueSong(queueItem, queueIcon: dataSource.queueIcon)
|
cellView.textField?.stringValue = queueItem.song.title
|
||||||
|
if queueItem.isPlaying {
|
||||||
|
cellView.textField?.font = .systemFontBold
|
||||||
|
cellView.imageView?.image = dataSource.queueIcon
|
||||||
|
} else {
|
||||||
|
cellView.textField?.font = .systemFontRegular
|
||||||
|
cellView.imageView?.image = nil
|
||||||
|
}
|
||||||
|
|
||||||
return cellView
|
return cellView
|
||||||
}
|
}
|
||||||
@ -109,14 +168,27 @@ class QueueViewController: NSViewController,
|
|||||||
|
|
||||||
return cellView
|
return cellView
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extension QueueViewController: StoreSubscriber {
|
func setupNotificationObservers() {
|
||||||
typealias StoreSubscriberStateType = QueueState
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(stateChanged(_:)),
|
||||||
|
name: Notification.stateChanged,
|
||||||
|
object: AppDelegate.mpdClient
|
||||||
|
)
|
||||||
|
|
||||||
func newState(state: StoreSubscriberStateType) {
|
NotificationCenter.default.addObserver(
|
||||||
dataSource.queue = state.queue
|
self,
|
||||||
dataSource.setQueueIcon(state)
|
selector: #selector(queueChanged(_:)),
|
||||||
queueView.reloadData()
|
name: Notification.queueChanged,
|
||||||
|
object: AppDelegate.mpdClient
|
||||||
|
)
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(queuePosChanged(_:)),
|
||||||
|
name: Notification.queuePosChanged,
|
||||||
|
object: AppDelegate.mpdClient
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,42 +0,0 @@
|
|||||||
//
|
|
||||||
// UserNotificationsController.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/27.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
class UserNotificationsController {
|
|
||||||
init() {
|
|
||||||
App.store.subscribe(self) {
|
|
||||||
$0.select { $0.playerState.currentSong }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func notifyTrack(_ state: Song?) {
|
|
||||||
guard let currentSong = state,
|
|
||||||
let status = App.mpdClient.status,
|
|
||||||
status.state == .playing
|
|
||||||
else { return }
|
|
||||||
|
|
||||||
let coverArtService = CoverArtService(song: currentSong)
|
|
||||||
|
|
||||||
coverArtService.fetchBigCoverArt()
|
|
||||||
.done() {
|
|
||||||
SongNotifierService(song: currentSong, image: $0)
|
|
||||||
.deliver()
|
|
||||||
}
|
|
||||||
.cauterize()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension UserNotificationsController: StoreSubscriber {
|
|
||||||
typealias StoreSubscriberStateType = Song?
|
|
||||||
|
|
||||||
func newState(state: Song?) {
|
|
||||||
notifyTrack(state)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,8 +6,7 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
class WindowController: NSWindowController {
|
class WindowController: NSWindowController {
|
||||||
enum TransportAction: Int {
|
enum TransportAction: Int {
|
||||||
@ -15,30 +14,41 @@ class WindowController: NSWindowController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var state: MPDClient.MPDStatus.State?
|
var state: MPDClient.MPDStatus.State?
|
||||||
|
var totalTime: UInt?
|
||||||
|
var elapsedTimeMs: UInt?
|
||||||
var trackTimer: Timer?
|
var trackTimer: Timer?
|
||||||
|
|
||||||
@IBOutlet var transportControls: NSSegmentedCell!
|
|
||||||
|
|
||||||
@IBOutlet var trackProgress: NSTextField!
|
|
||||||
@IBOutlet var trackProgressBar: NSSlider!
|
|
||||||
@IBOutlet var trackRemaining: NSTextField!
|
|
||||||
@IBOutlet var databaseUpdatingIndicator: NSProgressIndicator!
|
|
||||||
|
|
||||||
@IBOutlet var shuffleState: NSButton!
|
|
||||||
@IBOutlet var repeatState: NSButton!
|
|
||||||
|
|
||||||
override func windowDidLoad() {
|
override func windowDidLoad() {
|
||||||
super.windowDidLoad()
|
super.windowDidLoad()
|
||||||
window?.titleVisibility = .hidden
|
window?.titleVisibility = .hidden
|
||||||
window?.isExcludedFromWindowsMenu = true
|
|
||||||
|
|
||||||
App.store.subscribe(self) {
|
NotificationCenter.default.addObserver(
|
||||||
$0.select {
|
self,
|
||||||
($0.playerState, $0.uiState)
|
selector: #selector(stateChanged(_:)),
|
||||||
}
|
name: Notification.stateChanged,
|
||||||
}
|
object: AppDelegate.mpdClient
|
||||||
|
)
|
||||||
|
|
||||||
App.store.dispatch(MainWindowDidOpenAction())
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(timeChanged(_:)),
|
||||||
|
name: Notification.timeChanged,
|
||||||
|
object: AppDelegate.mpdClient
|
||||||
|
)
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(startDatabaseUpdatingIndicator),
|
||||||
|
name: Notification.databaseUpdateStarted,
|
||||||
|
object: AppDelegate.mpdClient
|
||||||
|
)
|
||||||
|
|
||||||
|
NotificationCenter.default.addObserver(
|
||||||
|
self,
|
||||||
|
selector: #selector(stopDatabaseUpdatingIndicator),
|
||||||
|
name: Notification.databaseUpdateFinished,
|
||||||
|
object: AppDelegate.mpdClient
|
||||||
|
)
|
||||||
|
|
||||||
trackProgress.font = .timerFont
|
trackProgress.font = .timerFont
|
||||||
trackRemaining.font = .timerFont
|
trackRemaining.font = .timerFont
|
||||||
@ -47,14 +57,23 @@ 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:
|
||||||
App.store.dispatch(MPDPlayPauseAction())
|
AppDelegate.mpdClient.playPause()
|
||||||
default:
|
default:
|
||||||
nextResponder?.keyDown(with: event)
|
nextResponder?.keyDown(with: event)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTransportControlState(_ state: PlayerState) {
|
@objc func stateChanged(_ notification: Notification) {
|
||||||
guard let state = state.state else { return }
|
guard let state = notification.userInfo?[Notification.stateKey] as? MPDClient.MPDStatus.State
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
self.state = state
|
||||||
|
|
||||||
|
setTransportControlState()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTransportControlState() {
|
||||||
|
guard let state = state else { return }
|
||||||
|
|
||||||
transportControls.setEnabled(state.isOneOf([.playing, .paused]), forSegment: 0)
|
transportControls.setEnabled(state.isOneOf([.playing, .paused]), forSegment: 0)
|
||||||
transportControls.setEnabled(state.isOneOf([.playing, .paused, .stopped]), forSegment: 1)
|
transportControls.setEnabled(state.isOneOf([.playing, .paused, .stopped]), forSegment: 1)
|
||||||
@ -68,34 +87,74 @@ class WindowController: NSWindowController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setShuffleRepeatState(_ state: PlayerState) {
|
@objc func timeChanged(_ notification: Notification) {
|
||||||
shuffleState.state = state.shuffleState ? .on : .off
|
guard let totalTime = notification.userInfo?[Notification.totalTimeKey] as? UInt,
|
||||||
repeatState.state = state.repeatState ? .on : .off
|
let elapsedTimeMs = notification.userInfo?[Notification.elapsedTimeMsKey] as? UInt
|
||||||
}
|
|
||||||
|
|
||||||
func setTrackProgressControls(_ playerState: PlayerState) {
|
|
||||||
guard let state = playerState.state,
|
|
||||||
let totalTime = playerState.totalTime,
|
|
||||||
let elapsedTimeMs = playerState.elapsedTimeMs
|
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
trackProgressBar.isEnabled = state.isOneOf([.playing, .paused])
|
self.totalTime = totalTime
|
||||||
|
self.elapsedTimeMs = elapsedTimeMs
|
||||||
|
|
||||||
|
setTrackProgressControls()
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTrackProgressControls() {
|
||||||
|
guard let totalTime = totalTime,
|
||||||
|
let elapsedTimeMs = elapsedTimeMs
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
trackProgressBar.isEnabled = [.playing, .paused].contains(state)
|
||||||
trackProgressBar.maxValue = Double(totalTime * 1000)
|
trackProgressBar.maxValue = Double(totalTime * 1000)
|
||||||
trackProgressBar.integerValue = Int(elapsedTimeMs)
|
|
||||||
|
|
||||||
setTimeElapsed(elapsedTimeMs)
|
if state == .playing {
|
||||||
setTimeRemaining(elapsedTimeMs, totalTime * 1000)
|
trackTimer?.invalidate()
|
||||||
}
|
|
||||||
|
|
||||||
func setDatabaseUpdatingIndicator(_ uiState: UIState) {
|
trackTimer = Timer.scheduledTimer(
|
||||||
if uiState.databaseUpdating {
|
timeInterval: 0.25,
|
||||||
databaseUpdatingIndicator.startAnimation(self)
|
target: self,
|
||||||
|
selector: #selector(updateProgress(_:)),
|
||||||
|
userInfo: [
|
||||||
|
"startTime": CACurrentMediaTime(),
|
||||||
|
"startElapsed": Double(elapsedTimeMs) / 1000
|
||||||
|
],
|
||||||
|
repeats: true
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
databaseUpdatingIndicator.stopAnimation(self)
|
trackTimer?.invalidate()
|
||||||
}
|
|
||||||
|
trackProgressBar.integerValue = Int(elapsedTimeMs)
|
||||||
|
setTimeElapsed()
|
||||||
|
setTimeRemaining()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTimeElapsed(_ elapsedTimeMs: UInt?) {
|
@objc func updateProgress(_ timer: Timer) {
|
||||||
|
let currentTime = CACurrentMediaTime()
|
||||||
|
|
||||||
|
guard let userInfo = timer.userInfo as? Dictionary<String, Any>,
|
||||||
|
let startTime = userInfo["startTime"] as? Double,
|
||||||
|
let startElapsed = userInfo["startElapsed"] as? Double
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
let timeDiff = currentTime - startTime
|
||||||
|
let newElapsedTimeMs = (startElapsed + timeDiff) * 1000
|
||||||
|
|
||||||
|
self.elapsedTimeMs = UInt(newElapsedTimeMs)
|
||||||
|
trackProgressBar.integerValue = Int(newElapsedTimeMs)
|
||||||
|
|
||||||
|
setTimeElapsed()
|
||||||
|
setTimeRemaining()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func startDatabaseUpdatingIndicator() {
|
||||||
|
databaseUpdatingIndicator.startAnimation(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc func stopDatabaseUpdatingIndicator() {
|
||||||
|
databaseUpdatingIndicator.stopAnimation(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setTimeElapsed() {
|
||||||
guard let elapsedTimeMs = elapsedTimeMs else { return }
|
guard let elapsedTimeMs = elapsedTimeMs else { return }
|
||||||
|
|
||||||
let time = Time(timeInSeconds: Int(elapsedTimeMs) / 1000)
|
let time = Time(timeInSeconds: Int(elapsedTimeMs) / 1000)
|
||||||
@ -103,34 +162,34 @@ class WindowController: NSWindowController {
|
|||||||
trackProgress.stringValue = time.formattedTime
|
trackProgress.stringValue = time.formattedTime
|
||||||
}
|
}
|
||||||
|
|
||||||
func setTimeRemaining(_ elapsedTimeMs: UInt?, _ totalTime: UInt?) {
|
func setTimeRemaining() {
|
||||||
guard let elapsedTimeMs = elapsedTimeMs,
|
guard let elapsedTimeMs = elapsedTimeMs,
|
||||||
let totalTime = totalTime
|
let totalTime = totalTime
|
||||||
else { return }
|
else { return }
|
||||||
|
|
||||||
let time = Time(
|
let time = Time(timeInSeconds: -(Int(totalTime) - Int(elapsedTimeMs) / 1000))
|
||||||
timeInSeconds: -(Int(totalTime) - Int(elapsedTimeMs)) / 1000
|
|
||||||
)
|
|
||||||
|
|
||||||
trackRemaining.stringValue = time.formattedTime
|
trackRemaining.stringValue = time.formattedTime
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 else {
|
||||||
else { return }
|
return
|
||||||
|
}
|
||||||
|
|
||||||
switch event.type {
|
switch event.type {
|
||||||
case .leftMouseDown:
|
case .leftMouseDown:
|
||||||
trackTimer?.invalidate()
|
trackTimer?.invalidate()
|
||||||
case .leftMouseDragged:
|
case .leftMouseDragged:
|
||||||
App.store.dispatch(
|
self.elapsedTimeMs = UInt(sender.integerValue)
|
||||||
UpdateElapsedTimeAction(elapsedTimeMs: UInt(sender.integerValue))
|
|
||||||
)
|
setTimeElapsed()
|
||||||
|
setTimeRemaining()
|
||||||
case .leftMouseUp:
|
case .leftMouseUp:
|
||||||
let seekTime = Float(sender.integerValue) / 1000
|
let seekTime = Float(sender.integerValue) / 1000
|
||||||
|
|
||||||
App.store.dispatch(MPDSeekCurrentSong(timeInSeconds: seekTime))
|
AppDelegate.mpdClient.seekCurrentSong(timeInSeconds: seekTime)
|
||||||
default:
|
default:
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -142,49 +201,20 @@ class WindowController: NSWindowController {
|
|||||||
|
|
||||||
switch transportAction {
|
switch transportAction {
|
||||||
case .prevTrack:
|
case .prevTrack:
|
||||||
App.store.dispatch(MPDPrevTrackAction())
|
AppDelegate.mpdClient.prevTrack()
|
||||||
case .playPause:
|
case .playPause:
|
||||||
App.store.dispatch(MPDPlayPauseAction())
|
AppDelegate.mpdClient.playPause()
|
||||||
case .stop:
|
case .stop:
|
||||||
App.store.dispatch(MPDStopAction())
|
AppDelegate.mpdClient.stop()
|
||||||
case .nextTrack:
|
case .nextTrack:
|
||||||
App.store.dispatch(MPDNextTrackAction())
|
AppDelegate.mpdClient.nextTrack()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func handleShuffleButton(_ sender: NSButton) {
|
@IBOutlet var transportControls: NSSegmentedCell!
|
||||||
App.store.dispatch(MPDSetShuffleAction(shuffleState: sender.state == .on))
|
|
||||||
}
|
@IBOutlet var trackProgress: NSTextField!
|
||||||
|
@IBOutlet var trackProgressBar: NSSlider!
|
||||||
@IBAction func handleRepeatButton(_ sender: NSButton) {
|
@IBOutlet var trackRemaining: NSTextField!
|
||||||
App.store.dispatch(MPDSetRepeatAction(repeatState: sender.state == .on))
|
@IBOutlet var databaseUpdatingIndicator: NSProgressIndicator!
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
extension WindowController: NSWindowDelegate {
|
|
||||||
func windowWillClose(_ notification: Notification) {
|
|
||||||
App.store.dispatch(MainWindowDidCloseAction())
|
|
||||||
}
|
|
||||||
|
|
||||||
func windowWillMiniaturize(_ notification: Notification) {
|
|
||||||
App.store.dispatch(MainWindowDidMinimizeAction())
|
|
||||||
}
|
|
||||||
|
|
||||||
func windowDidDeminiaturize(_ notification: Notification) {
|
|
||||||
App.store.dispatch(MainWindowDidOpenAction())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension WindowController: StoreSubscriber {
|
|
||||||
typealias StoreSubscriberStateType = (playerState: PlayerState, uiState: UIState)
|
|
||||||
|
|
||||||
func newState(state: (playerState: PlayerState, uiState: UIState)) {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.setTransportControlState(state.playerState)
|
|
||||||
self.setShuffleRepeatState(state.playerState)
|
|
||||||
self.setTrackProgressControls(state.playerState)
|
|
||||||
self.setDatabaseUpdatingIndicator(state.uiState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
import PromiseKit
|
import PromiseKit
|
||||||
|
|
||||||
class AlbumDataSource: NSObject, NSCollectionViewDataSource {
|
class AlbumDataSource: NSObject, NSCollectionViewDataSource {
|
||||||
@ -16,6 +16,14 @@ class AlbumDataSource: NSObject, NSCollectionViewDataSource {
|
|||||||
return albums.count
|
return albums.count
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func resetCoverArt() {
|
||||||
|
albums = albums.map {
|
||||||
|
var album = $0
|
||||||
|
album.coverArtFetched = false
|
||||||
|
return album
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
|
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
|
||||||
let item = collectionView.makeItem(withIdentifier: .albumViewItem, for: indexPath)
|
let item = collectionView.makeItem(withIdentifier: .albumViewItem, for: indexPath)
|
||||||
guard let albumViewItem = item as? AlbumViewItem else { return item }
|
guard let albumViewItem = item as? AlbumViewItem else { return item }
|
||||||
@ -23,23 +31,23 @@ 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 {
|
if albums[indexPath.item].coverArt == nil &&
|
||||||
case .notLoaded:
|
!albums[indexPath.item].coverArtFetched {
|
||||||
App.mpdClient.getAlbumFirstSong(for: albums[indexPath.item].mpdAlbum) { mpdSong in
|
|
||||||
guard let mpdSong = mpdSong else { return }
|
|
||||||
|
|
||||||
CoverArtService(song: Song(mpdSong: mpdSong))
|
AppDelegate.mpdClient.getAlbumFirstSong(for: albums[indexPath.item].mpdAlbum) {
|
||||||
.fetchCoverArt()
|
guard let song = $0 else { return }
|
||||||
|
|
||||||
|
AlbumArtService(song: Song(mpdSong: song))
|
||||||
|
.fetchAlbumArt()
|
||||||
.done { image in
|
.done { image in
|
||||||
|
self.albums[indexPath.item].coverArt = image
|
||||||
|
self.albums[indexPath.item].coverArtFetched = true
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
App.store.dispatch(
|
collectionView.reloadItems(at: [indexPath])
|
||||||
UpdateCoverArtAction(coverArt: image, albumIndex: indexPath.item)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return albumViewItem
|
return albumViewItem
|
||||||
|
|||||||
@ -6,17 +6,49 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
class QueueDataSource: NSObject, NSOutlineViewDataSource {
|
class QueueDataSource: NSObject, NSOutlineViewDataSource {
|
||||||
var queue: [QueueItem] = []
|
var queue: [QueueItem] = []
|
||||||
|
var queuePos: Int = -1
|
||||||
|
var currentSong: Song?
|
||||||
|
|
||||||
var queueIcon: NSImage? = nil
|
var queueIcon: NSImage? = nil
|
||||||
|
|
||||||
func setQueueIcon(_ state: QueueState) {
|
func updateQueue(_ queue: [MPDClient.MPDSong]) {
|
||||||
switch state.state {
|
queuePos = -1
|
||||||
case .playing?:
|
|
||||||
|
self.queue = queue.enumerated().map { index, mpdSong in
|
||||||
|
let song = Song(mpdSong: mpdSong)
|
||||||
|
return QueueItem(
|
||||||
|
song: song,
|
||||||
|
queuePos: index,
|
||||||
|
isPlaying: index == queuePos
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setQueuePos(_ queuePos: Int) {
|
||||||
|
let oldSongRowPos = self.queuePos
|
||||||
|
let newSongRowPos = queuePos
|
||||||
|
self.queuePos = queuePos
|
||||||
|
|
||||||
|
if oldSongRowPos >= 0 {
|
||||||
|
queue[oldSongRowPos].isPlaying = false
|
||||||
|
}
|
||||||
|
if newSongRowPos >= 0 {
|
||||||
|
queue[newSongRowPos].isPlaying = true
|
||||||
|
currentSong = queue[newSongRowPos].song
|
||||||
|
} else {
|
||||||
|
currentSong = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func setQueueIcon(_ state: MPDClient.MPDStatus.State) {
|
||||||
|
switch state {
|
||||||
|
case .playing:
|
||||||
queueIcon = .playIcon
|
queueIcon = .playIcon
|
||||||
case .paused?:
|
case .paused:
|
||||||
queueIcon = .pauseIcon
|
queueIcon = .pauseIcon
|
||||||
default:
|
default:
|
||||||
queueIcon = nil
|
queueIcon = nil
|
||||||
|
|||||||
@ -6,9 +6,9 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
extension CGColor {
|
extension CGColor {
|
||||||
static let albumBorderColorLight = NSColor.black.withAlphaComponent(0.15).cgColor
|
static let albumBorderColorLight = NSColor.black.withAlphaComponent(0.1).cgColor
|
||||||
static let albumBorderColorDark = NSColor.white.withAlphaComponent(0.15).cgColor
|
static let albumBorderColorDark = NSColor.white.withAlphaComponent(0.1).cgColor
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
extension NSEvent {
|
extension NSEvent {
|
||||||
static let keyCodeSpace: UInt16 = 49
|
static let keyCodeSpace: UInt16 = 49
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
extension NSFont {
|
extension NSFont {
|
||||||
static let systemFontRegular = systemFont(ofSize: 13, weight: .regular)
|
static let systemFontRegular = systemFont(ofSize: 13, weight: .regular)
|
||||||
|
|||||||
@ -6,13 +6,13 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
extension NSImage {
|
extension NSImage {
|
||||||
static let playIcon = NSImage(named: "playButton")
|
static let playIcon = NSImage(named: "playButton")
|
||||||
static let pauseIcon = NSImage(named: "pauseButton")
|
static let pauseIcon = NSImage(named: "pauseButton")
|
||||||
|
|
||||||
static let defaultCoverArt = NSImage(named: "defaultCoverArt")
|
static let defaultCoverArt = NSImage(named: "blankAlbum")
|
||||||
|
|
||||||
func toFitBox(size: NSSize) -> NSImage {
|
func toFitBox(size: NSSize) -> NSImage {
|
||||||
var newSize: NSSize = NSSize.zero
|
var newSize: NSSize = NSSize.zero
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
extension NSUserInterfaceItemIdentifier {
|
extension NSUserInterfaceItemIdentifier {
|
||||||
static let queueSongTitleColumn = NSUserInterfaceItemIdentifier("songTitleColumn")
|
static let queueSongTitleColumn = NSUserInterfaceItemIdentifier("songTitleColumn")
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>0.12.0-alpha</string>
|
<string>0.11.2-alpha</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
class AlbumViewLayout: NSCollectionViewFlowLayout {
|
class AlbumViewLayout: NSCollectionViewFlowLayout {
|
||||||
let maxItemWidth: CGFloat = 180
|
let maxItemWidth: CGFloat = 180
|
||||||
|
|||||||
@ -29,16 +29,6 @@ extension MPDClient {
|
|||||||
else { return }
|
else { return }
|
||||||
sendSeekCurrentSong(timeInSeconds: timeInSeconds)
|
sendSeekCurrentSong(timeInSeconds: timeInSeconds)
|
||||||
|
|
||||||
case .setShuffleState:
|
|
||||||
guard let shuffleState = userData["shuffleState"] as? Bool
|
|
||||||
else { return }
|
|
||||||
sendShuffleState(shuffleState: shuffleState)
|
|
||||||
|
|
||||||
case .setRepeatState:
|
|
||||||
guard let repeatState = userData["repeatState"] as? Bool
|
|
||||||
else { return }
|
|
||||||
sendRepeatState(repeatState: repeatState)
|
|
||||||
|
|
||||||
// Database commands
|
// Database commands
|
||||||
case .updateDatabase:
|
case .updateDatabase:
|
||||||
sendUpdateDatabase()
|
sendUpdateDatabase()
|
||||||
|
|||||||
@ -29,7 +29,8 @@ extension MPDClient {
|
|||||||
self.idle()
|
self.idle()
|
||||||
|
|
||||||
self.delegate?.didConnect(mpdClient: self)
|
self.delegate?.didConnect(mpdClient: self)
|
||||||
self.delegate?.didUpdateStatus(mpdClient: self, status: self.status!)
|
self.delegate?.didUpdateState(mpdClient: self, state: self.status!.state)
|
||||||
|
self.delegate?.didUpdateTime(mpdClient: self, total: self.status!.totalTime, elapsedMs: self.status!.elapsedTimeMs)
|
||||||
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
|
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
|
||||||
self.delegate?.didUpdateQueuePos(mpdClient: self, song: self.status!.song)
|
self.delegate?.didUpdateQueuePos(mpdClient: self, song: self.status!.song)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,12 +39,13 @@ extension MPDClient {
|
|||||||
self.fetchQueue()
|
self.fetchQueue()
|
||||||
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
|
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
|
||||||
}
|
}
|
||||||
if mpdIdle.contains(.player) || mpdIdle.contains(.options) {
|
if mpdIdle.contains(.player) {
|
||||||
self.fetchStatus()
|
self.fetchStatus()
|
||||||
|
|
||||||
if let status = self.status {
|
if let status = self.status {
|
||||||
self.delegate?.didUpdateStatus(mpdClient: self, status: status)
|
|
||||||
self.delegate?.didUpdateQueuePos(mpdClient: self, song: status.song)
|
self.delegate?.didUpdateQueuePos(mpdClient: self, song: status.song)
|
||||||
|
self.delegate?.didUpdateState(mpdClient: self, state: status.state)
|
||||||
|
self.delegate?.didUpdateTime(mpdClient: self, total: status.totalTime, elapsedMs: status.elapsedTimeMs)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if mpdIdle.contains(.update) {
|
if mpdIdle.contains(.update) {
|
||||||
|
|||||||
@ -33,20 +33,6 @@ extension MPDClient {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func setShuffleState(shuffleState: Bool) {
|
|
||||||
enqueueCommand(
|
|
||||||
command: .setShuffleState,
|
|
||||||
userData: ["shuffleState": shuffleState]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func setRepeatState(repeatState: Bool) {
|
|
||||||
enqueueCommand(
|
|
||||||
command: .setRepeatState,
|
|
||||||
userData: ["repeatState": repeatState]
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendNextTrack() {
|
func sendNextTrack() {
|
||||||
guard let state = status?.state,
|
guard let state = status?.state,
|
||||||
state.isOneOf([.playing, .paused])
|
state.isOneOf([.playing, .paused])
|
||||||
@ -78,12 +64,4 @@ extension MPDClient {
|
|||||||
func sendSeekCurrentSong(timeInSeconds: Float) {
|
func sendSeekCurrentSong(timeInSeconds: Float) {
|
||||||
mpd_run_seek_current(self.connection, timeInSeconds, false)
|
mpd_run_seek_current(self.connection, timeInSeconds, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendShuffleState(shuffleState: Bool) {
|
|
||||||
mpd_run_random(self.connection, shuffleState)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendRepeatState(repeatState: Bool) {
|
|
||||||
mpd_run_repeat(self.connection, repeatState)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
extension MPDClient {
|
extension MPDClient {
|
||||||
struct MPDAlbum: Equatable {
|
struct MPDAlbum {
|
||||||
let title: String
|
let title: String
|
||||||
let artist: String
|
let artist: String
|
||||||
}
|
}
|
||||||
|
|||||||
@ -17,9 +17,6 @@ extension MPDClient {
|
|||||||
case stop
|
case stop
|
||||||
case seekCurrentSong
|
case seekCurrentSong
|
||||||
|
|
||||||
case setShuffleState
|
|
||||||
case setRepeatState
|
|
||||||
|
|
||||||
// Database commands
|
// Database commands
|
||||||
case updateDatabase
|
case updateDatabase
|
||||||
|
|
||||||
|
|||||||
@ -50,14 +50,6 @@ extension MPDClient {
|
|||||||
return Int(mpd_status_get_song_pos(status))
|
return Int(mpd_status_get_song_pos(status))
|
||||||
}
|
}
|
||||||
|
|
||||||
var shuffleState: Bool {
|
|
||||||
return mpd_status_get_random(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
var repeatState: Bool {
|
|
||||||
return mpd_status_get_repeat(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
var updating: Bool {
|
var updating: Bool {
|
||||||
let updating = mpd_status_get_update_id(status)
|
let updating = mpd_status_get_update_id(status)
|
||||||
|
|
||||||
|
|||||||
@ -12,7 +12,8 @@ protocol MPDClientDelegate {
|
|||||||
func didConnect(mpdClient: MPDClient)
|
func didConnect(mpdClient: MPDClient)
|
||||||
func willDisconnect(mpdClient: MPDClient)
|
func willDisconnect(mpdClient: MPDClient)
|
||||||
|
|
||||||
func didUpdateStatus(mpdClient: MPDClient, status: MPDClient.MPDStatus)
|
func didUpdateState(mpdClient: MPDClient, state: MPDClient.MPDStatus.State)
|
||||||
|
func didUpdateTime(mpdClient: MPDClient, total: UInt, elapsedMs: UInt)
|
||||||
|
|
||||||
func willStartDatabaseUpdate(mpdClient: MPDClient)
|
func willStartDatabaseUpdate(mpdClient: MPDClient)
|
||||||
func didFinishDatabaseUpdate(mpdClient: MPDClient)
|
func didFinishDatabaseUpdate(mpdClient: MPDClient)
|
||||||
|
|||||||
@ -6,12 +6,13 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
import CryptoSwift
|
import CryptoSwift
|
||||||
|
|
||||||
struct Album {
|
struct Album {
|
||||||
var mpdAlbum: MPDClient.MPDAlbum
|
var mpdAlbum: MPDClient.MPDAlbum
|
||||||
var coverArt: Loading<NSImage?> = .notLoaded
|
var coverArt: NSImage?
|
||||||
|
var coverArtFetched: Bool = false
|
||||||
|
|
||||||
init(mpdAlbum: MPDClient.MPDAlbum) {
|
init(mpdAlbum: MPDClient.MPDAlbum) {
|
||||||
self.mpdAlbum = mpdAlbum
|
self.mpdAlbum = mpdAlbum
|
||||||
@ -29,10 +30,3 @@ struct Album {
|
|||||||
return "\(title) - \(artist)".sha1()
|
return "\(title) - \(artist)".sha1()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Album: Equatable {
|
|
||||||
static func == (lhs: Album, rhs: Album) -> Bool {
|
|
||||||
return (lhs.mpdAlbum == rhs.mpdAlbum) &&
|
|
||||||
(lhs.coverArt ~= rhs.coverArt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,28 +0,0 @@
|
|||||||
//
|
|
||||||
// Loading.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/27.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
enum Loading<T> {
|
|
||||||
case notLoaded
|
|
||||||
case loading
|
|
||||||
case loaded(T)
|
|
||||||
case error(Error)
|
|
||||||
|
|
||||||
static func ~= (lhs: Loading<T>, rhs: Loading<T>) -> Bool {
|
|
||||||
switch (lhs, rhs) {
|
|
||||||
case (_, .notLoaded),
|
|
||||||
(.loading, .loading),
|
|
||||||
(.loaded, .loaded),
|
|
||||||
(.error, .error):
|
|
||||||
return true
|
|
||||||
default:
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
//
|
|
||||||
// MPDServer.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/28.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
|
|
||||||
struct MPDServer {
|
|
||||||
let hostDefault = "127.0.0.1"
|
|
||||||
let portDefault = 6600
|
|
||||||
|
|
||||||
var host: String?
|
|
||||||
|
|
||||||
var hostOrDefault: String {
|
|
||||||
return host ?? hostDefault
|
|
||||||
}
|
|
||||||
|
|
||||||
var port: Int?
|
|
||||||
|
|
||||||
var portOrDefault: Int {
|
|
||||||
return port ?? portDefault
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension MPDServer: Equatable {
|
|
||||||
static func == (lhs: MPDServer, rhs: MPDServer) -> Bool {
|
|
||||||
return (lhs.host == rhs.host) &&
|
|
||||||
(lhs.port == rhs.port)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
77
Persephone/Models/Preferences.swift
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
//
|
||||||
|
// Preferences.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2019/2/15.
|
||||||
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
struct Preferences {
|
||||||
|
let mpdHostDefault = "127.0.0.1"
|
||||||
|
let mpdPortDefault = 6600
|
||||||
|
let mpdLibraryDirDefault = "~/Music"
|
||||||
|
|
||||||
|
let preferences = UserDefaults.standard
|
||||||
|
|
||||||
|
var mpdHost: String? {
|
||||||
|
get {
|
||||||
|
return preferences.string(forKey: "mpdHost")
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
preferences.set(newValue, forKey: "mpdHost")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mpdHostOrDefault: String {
|
||||||
|
return mpdHost ?? mpdHostDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
var mpdPort: Int? {
|
||||||
|
get {
|
||||||
|
return preferences.value(forKey: "mpdPort") as? Int
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
if (newValue.map { $0 > 0 } ?? false) {
|
||||||
|
preferences.set(newValue, forKey: "mpdPort")
|
||||||
|
} else {
|
||||||
|
preferences.removeObject(forKey: "mpdPort")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mpdPortOrDefault: Int {
|
||||||
|
return mpdPort ?? mpdPortDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
var mpdLibraryDir: String? {
|
||||||
|
get {
|
||||||
|
return preferences.string(forKey: "mpdLibraryDir")
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
preferences.set(newValue, forKey: "mpdLibraryDir")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mpdLibraryDirOrDefault: String {
|
||||||
|
return mpdLibraryDir ?? mpdLibraryDirDefault
|
||||||
|
}
|
||||||
|
|
||||||
|
var expandedMpdLibraryDir: String {
|
||||||
|
return NSString(string: mpdLibraryDirOrDefault).expandingTildeInPath
|
||||||
|
}
|
||||||
|
|
||||||
|
var fetchMissingArtworkFromInternet: Bool {
|
||||||
|
get {
|
||||||
|
return preferences.bool(forKey: "fetchMissingArtworkFromInternet")
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
preferences.set(newValue, forKey: "fetchMissingArtworkFromInternet")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func addObserver(_ observer: NSObject, forKeyPath keyPath: String) {
|
||||||
|
preferences.addObserver(observer, forKeyPath: keyPath, options: .new, context: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct QueueItem: Equatable {
|
struct QueueItem {
|
||||||
var song: Song
|
var song: Song
|
||||||
var queuePos: Int
|
var queuePos: Int
|
||||||
var isPlaying: Bool
|
var isPlaying: Bool
|
||||||
|
|||||||
@ -23,11 +23,3 @@ struct Song {
|
|||||||
return Album(mpdAlbum: mpdSong.album)
|
return Album(mpdAlbum: mpdSong.album)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension Song: Equatable {
|
|
||||||
static func == (lhs: Song, rhs: Song) -> Bool {
|
|
||||||
return (lhs.title == rhs.title) &&
|
|
||||||
(lhs.artist == rhs.artist) &&
|
|
||||||
(lhs.album == rhs.album)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@ -1,52 +0,0 @@
|
|||||||
//
|
|
||||||
// TrackTimer.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/19.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AppKit
|
|
||||||
|
|
||||||
class TrackTimer: NSObject {
|
|
||||||
var timer: Timer?
|
|
||||||
var startTime: CFTimeInterval = CACurrentMediaTime()
|
|
||||||
var startElapsed: Double = 0
|
|
||||||
|
|
||||||
func start(elapsedTimeMs: UInt?) {
|
|
||||||
guard let elapsedTimeMs = elapsedTimeMs else { return }
|
|
||||||
|
|
||||||
startTime = CACurrentMediaTime()
|
|
||||||
startElapsed = Double(elapsedTimeMs) / 1000
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.timer?.invalidate()
|
|
||||||
|
|
||||||
self.timer = Timer.scheduledTimer(
|
|
||||||
withTimeInterval: 0.25,
|
|
||||||
repeats: true
|
|
||||||
) { _ in
|
|
||||||
let currentTime = CACurrentMediaTime()
|
|
||||||
|
|
||||||
let timeDiff = currentTime - self.startTime
|
|
||||||
let newElapsedTimeMs = UInt((self.startElapsed + timeDiff) * 1000)
|
|
||||||
|
|
||||||
App.store.dispatch(
|
|
||||||
UpdateElapsedTimeAction(elapsedTimeMs: newElapsedTimeMs)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func stop(elapsedTimeMs: UInt?) {
|
|
||||||
guard let elapsedTimeMs = elapsedTimeMs else { return }
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
self.timer?.invalidate()
|
|
||||||
|
|
||||||
App.store.dispatch(
|
|
||||||
UpdateElapsedTimeAction(elapsedTimeMs: elapsedTimeMs)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,17 +1,17 @@
|
|||||||
//
|
//
|
||||||
// CoverArtQueue.swift
|
// AlbumArtOperationQueue.swift
|
||||||
// Persephone
|
// Persephone
|
||||||
//
|
//
|
||||||
// Created by Daniel Barber on 2019/2/26.
|
// Created by Daniel Barber on 2019/2/26.
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
class CoverArtQueue {
|
class AlbumArtQueue {
|
||||||
static let shared = CoverArtQueue()
|
static let shared = AlbumArtQueue()
|
||||||
|
|
||||||
let queue = DispatchQueue(label: "CoverArtQueue")
|
let queue = DispatchQueue(label: "AlbumArtQueue")
|
||||||
var lastDispatchedTime = DispatchTime(uptimeNanoseconds: 0) - 1
|
var lastDispatchedTime = DispatchTime(uptimeNanoseconds: 0) - 1
|
||||||
|
|
||||||
func addToQueue(workItem: DispatchWorkItem) {
|
func addToQueue(workItem: DispatchWorkItem) {
|
||||||
@ -1,22 +1,24 @@
|
|||||||
//
|
//
|
||||||
// CoverArtPrefsController.swift
|
// AlbumArtPrefsController.swift
|
||||||
// Persephone
|
// Persephone
|
||||||
//
|
//
|
||||||
// Created by Daniel Barber on 2019/2/23.
|
// Created by Daniel Barber on 2019/2/23.
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
|
class AlbumArtPrefsController: NSViewController {
|
||||||
|
var preferences = Preferences()
|
||||||
|
|
||||||
class CoverArtPrefsController: NSViewController {
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
if let mpdLibraryDir = App.store.state.preferencesState.mpdLibraryDir {
|
if let mpdLibraryDir = preferences.mpdLibraryDir {
|
||||||
mpdLibraryDirField.stringValue = mpdLibraryDir
|
mpdLibraryDirField.stringValue = mpdLibraryDir
|
||||||
}
|
}
|
||||||
|
|
||||||
if App.store.state.preferencesState.fetchMissingArtworkFromInternet {
|
if preferences.fetchMissingArtworkFromInternet {
|
||||||
fetchMissingArtworkFromInternet.state = .on
|
fetchMissingArtworkFromInternet.state = .on
|
||||||
} else {
|
} else {
|
||||||
fetchMissingArtworkFromInternet.state = .off
|
fetchMissingArtworkFromInternet.state = .off
|
||||||
@ -34,17 +36,17 @@ class CoverArtPrefsController: NSViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func updateMpdLibraryDir(_ sender: NSTextField) {
|
@IBAction func updateMpdLibraryDir(_ sender: NSTextField) {
|
||||||
App.store.dispatch(UpdateMPDLibraryDir(mpdLibraryDir: sender.stringValue))
|
preferences.mpdLibraryDir = sender.stringValue
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBOutlet var mpdLibraryDirField: NSTextField!
|
@IBOutlet var mpdLibraryDirField: NSTextField!
|
||||||
|
|
||||||
@IBAction func updateFetchMissingArtworkFromInternet(_ sender: NSButton) {
|
@IBAction func updateFetchMissingArtworkFromInternet(_ sender: NSButton) {
|
||||||
App.store.dispatch(
|
if sender.state == .on {
|
||||||
UpdateFetchMissingArtworkFromInternet(
|
preferences.fetchMissingArtworkFromInternet = true
|
||||||
fetchMissingArtworkFromInternet: sender.state == .on
|
} else {
|
||||||
)
|
preferences.fetchMissingArtworkFromInternet = false
|
||||||
)
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBOutlet var fetchMissingArtworkFromInternet: NSButton!
|
@IBOutlet var fetchMissingArtworkFromInternet: NSButton!
|
||||||
@ -6,19 +6,19 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
class GeneralPrefsViewController: NSViewController {
|
class GeneralPrefsViewController: NSViewController {
|
||||||
|
var preferences = Preferences()
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
if let mpdHost = App.store.state.preferencesState.mpdServer.host {
|
if let mpdHost = preferences.mpdHost {
|
||||||
mpdHostField.stringValue = mpdHost
|
mpdHostField.stringValue = mpdHost
|
||||||
}
|
}
|
||||||
|
|
||||||
if let mpdPort = App.store.state.preferencesState.mpdServer.port {
|
if let mpdPort = preferences.mpdPort {
|
||||||
print(mpdPort)
|
|
||||||
mpdPortField.stringValue = "\(mpdPort)"
|
mpdPortField.stringValue = "\(mpdPort)"
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -34,11 +34,11 @@ class GeneralPrefsViewController: NSViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func updateMpdHost(_ sender: NSTextField) {
|
@IBAction func updateMpdHost(_ sender: NSTextField) {
|
||||||
App.store.dispatch(UpdateServerHost(host: sender.stringValue))
|
preferences.mpdHost = sender.stringValue
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func updateMpdPort(_ sender: NSTextField) {
|
@IBAction func updateMpdPort(_ sender: NSTextField) {
|
||||||
App.store.dispatch(UpdateServerPort(port: sender.integerValue))
|
preferences.mpdPort = sender.integerValue
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBOutlet var mpdHostField: NSTextField!
|
@IBOutlet var mpdHostField: NSTextField!
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
class PreferencesViewController: NSTabViewController {
|
class PreferencesViewController: NSTabViewController {
|
||||||
private lazy var tabViewSizes: [String : NSSize] = [:]
|
private lazy var tabViewSizes: [String : NSSize] = [:]
|
||||||
|
|||||||
@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
class PreferencesWindowController: NSWindowController, NSWindowDelegate {
|
class PreferencesWindowController: NSWindowController, NSWindowDelegate {
|
||||||
override func windowDidLoad() {
|
override func windowDidLoad() {
|
||||||
@ -14,7 +14,6 @@ class PreferencesWindowController: NSWindowController, NSWindowDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func windowShouldClose(_ sender: NSWindow) -> Bool {
|
func windowShouldClose(_ sender: NSWindow) -> Bool {
|
||||||
App.store.dispatch(SavePreferences())
|
|
||||||
self.window?.orderOut(sender)
|
self.window?.orderOut(sender)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@ -39,7 +39,7 @@
|
|||||||
</textField>
|
</textField>
|
||||||
<imageView identifier="albumArtwork" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="Kfb-8f-ean">
|
<imageView identifier="albumArtwork" horizontalHuggingPriority="251" verticalHuggingPriority="251" horizontalCompressionResistancePriority="250" verticalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="Kfb-8f-ean">
|
||||||
<rect key="frame" x="0.0" y="39" width="128" height="128"/>
|
<rect key="frame" x="0.0" y="39" width="128" height="128"/>
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="defaultCoverArt" id="FsA-JX-BFh"/>
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="blankAlbum" id="FsA-JX-BFh"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
<button hidden="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="n8W-do-HyG">
|
<button hidden="YES" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="n8W-do-HyG">
|
||||||
<rect key="frame" x="43" y="81" width="42" height="43"/>
|
<rect key="frame" x="43" y="81" width="42" height="43"/>
|
||||||
@ -79,7 +79,7 @@
|
|||||||
<collectionViewItem id="Qgu-aI-55A" customClass="AlbumViewItem" customModule="Persephone" customModuleProvider="target"/>
|
<collectionViewItem id="Qgu-aI-55A" customClass="AlbumViewItem" customModule="Persephone" customModuleProvider="target"/>
|
||||||
</objects>
|
</objects>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="defaultCoverArt" width="128" height="128"/>
|
<image name="blankAlbum" width="128" height="128"/>
|
||||||
<image name="playButtonLarge" width="22" height="22"/>
|
<image name="playButtonLarge" width="22" height="22"/>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
<dependencies>
|
<dependencies>
|
||||||
<deployment identifier="macosx"/>
|
<deployment identifier="macosx"/>
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
|
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
|
||||||
|
<capability name="System colors introduced in macOS 10.14" minToolsVersion="10.0"/>
|
||||||
<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>
|
||||||
@ -26,7 +27,7 @@
|
|||||||
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW">
|
<menuItem title="Preferences…" keyEquivalent="," id="BOF-NM-1cW">
|
||||||
<connections>
|
<connections>
|
||||||
<action selector="preferencesItemSelected:" target="Voe-Tx-rLC" id="bxe-zW-cho"/>
|
<action selector="preferencesItemSelected:" target="Voe-Tx-rLC" id="bxe-zW-cho"/>
|
||||||
<segue destination="xYu-7w-E5x" kind="show" identifier="Preferences" id="OTW-56-v9E"/>
|
<segue destination="xYu-7w-E5x" kind="show" id="OTW-56-v9E"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
|
<menuItem isSeparatorItem="YES" id="wFC-TO-SCJ"/>
|
||||||
@ -90,18 +91,6 @@
|
|||||||
<action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/>
|
<action selector="performZoom:" target="Ady-hI-5gd" id="DIl-cC-cCs"/>
|
||||||
</connections>
|
</connections>
|
||||||
</menuItem>
|
</menuItem>
|
||||||
<menuItem title="Close" keyEquivalent="w" id="ahn-71-IzA">
|
|
||||||
<connections>
|
|
||||||
<action selector="performClose:" target="Ady-hI-5gd" id="ADY-rv-28Y"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem isSeparatorItem="YES" id="QhP-iz-rRa"/>
|
|
||||||
<menuItem title="Persephone" keyEquivalent="0" id="1Sq-L7-znT">
|
|
||||||
<attributedString key="attributedTitle"/>
|
|
||||||
<connections>
|
|
||||||
<segue destination="B8D-0N-5wS" kind="show" identifier="Main Window" id="D1q-xT-4O8"/>
|
|
||||||
</connections>
|
|
||||||
</menuItem>
|
|
||||||
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
|
<menuItem isSeparatorItem="YES" id="eu3-7i-yIM"/>
|
||||||
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
|
<menuItem title="Bring All to Front" id="LE2-aR-0XJ">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
@ -132,20 +121,19 @@
|
|||||||
</application>
|
</application>
|
||||||
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Persephone" customModuleProvider="target">
|
<customObject id="Voe-Tx-rLC" customClass="AppDelegate" customModule="Persephone" customModuleProvider="target">
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="mainWindowMenuItem" destination="1Sq-L7-znT" id="dC6-yY-6Ss"/>
|
|
||||||
<outlet property="updateDatabaseMenuItem" destination="EJg-93-1F6" id="gMf-SQ-lyI"/>
|
<outlet property="updateDatabaseMenuItem" destination="EJg-93-1F6" id="gMf-SQ-lyI"/>
|
||||||
</connections>
|
</connections>
|
||||||
</customObject>
|
</customObject>
|
||||||
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
<customObject id="YLy-65-1bz" customClass="NSFontManager"/>
|
||||||
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="Ady-hI-5gd" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="81" y="-332"/>
|
<point key="canvasLocation" x="115" y="-366"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Window Controller-->
|
<!--Window Controller-->
|
||||||
<scene sceneID="R2V-B0-nI4">
|
<scene sceneID="R2V-B0-nI4">
|
||||||
<objects>
|
<objects>
|
||||||
<windowController showSeguePresentationStyle="single" id="B8D-0N-5wS" customClass="WindowController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
<windowController id="B8D-0N-5wS" customClass="WindowController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<window key="window" title="Persephone" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" tabbingMode="disallowed" id="IQv-IB-iLA" customClass="MainWindow" customModule="Persephone" customModuleProvider="target">
|
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="IQv-IB-iLA" customClass="MainWindow" customModule="Persephone" customModuleProvider="target">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
|
||||||
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
|
||||||
<rect key="contentRect" x="207" y="570" width="960" height="560"/>
|
<rect key="contentRect" x="207" y="570" width="960" height="560"/>
|
||||||
@ -222,44 +210,9 @@
|
|||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||||
</progressIndicator>
|
</progressIndicator>
|
||||||
</toolbarItem>
|
</toolbarItem>
|
||||||
<toolbarItem implicitItemIdentifier="A07933B6-4770-4B28-856B-B98DB5C6EB55" label="" paletteLabel="Shuffle" image="shuffleButton" selectable="YES" id="gF4-mh-Nzd">
|
|
||||||
<nil key="toolTip"/>
|
|
||||||
<size key="minSize" width="42" height="24"/>
|
|
||||||
<size key="maxSize" width="42" height="24"/>
|
|
||||||
<button key="view" verticalHuggingPriority="750" id="E8L-uK-XT0">
|
|
||||||
<rect key="frame" x="2" y="14" width="42" height="24"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="shuffleButton" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" borderStyle="border" inset="2" id="YNb-hd-ax8">
|
|
||||||
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="handleShuffleButton:" target="B8D-0N-5wS" id="THd-0g-fmb"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
</toolbarItem>
|
|
||||||
<toolbarItem implicitItemIdentifier="4B55BD96-797D-4F30-B582-2D917823879F" label="" paletteLabel="Repeat" image="repeatButton" id="MwO-i4-shF">
|
|
||||||
<nil key="toolTip"/>
|
|
||||||
<size key="minSize" width="42" height="25"/>
|
|
||||||
<size key="maxSize" width="42" height="25"/>
|
|
||||||
<button key="view" verticalHuggingPriority="750" id="OqH-lV-sAg">
|
|
||||||
<rect key="frame" x="1" y="14" width="42" height="25"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
|
||||||
<buttonCell key="cell" type="roundTextured" bezelStyle="texturedRounded" image="repeatButton" imagePosition="only" alignment="center" lineBreakMode="truncatingTail" borderStyle="border" inset="2" id="1bu-vK-3Hb">
|
|
||||||
<behavior key="behavior" pushIn="YES" changeContents="YES" lightByContents="YES"/>
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
</buttonCell>
|
|
||||||
<connections>
|
|
||||||
<action selector="handleRepeatButton:" target="B8D-0N-5wS" id="EN2-u4-DNl"/>
|
|
||||||
</connections>
|
|
||||||
</button>
|
|
||||||
</toolbarItem>
|
|
||||||
</allowedToolbarItems>
|
</allowedToolbarItems>
|
||||||
<defaultToolbarItems>
|
<defaultToolbarItems>
|
||||||
<toolbarItem reference="p3r-ty-Pxf"/>
|
<toolbarItem reference="p3r-ty-Pxf"/>
|
||||||
<toolbarItem reference="mhg-16-CNM"/>
|
|
||||||
<toolbarItem reference="gF4-mh-Nzd"/>
|
|
||||||
<toolbarItem reference="MwO-i4-shF"/>
|
|
||||||
<toolbarItem reference="fw4-Lp-bWJ"/>
|
<toolbarItem reference="fw4-Lp-bWJ"/>
|
||||||
<toolbarItem reference="9ol-aR-mzv"/>
|
<toolbarItem reference="9ol-aR-mzv"/>
|
||||||
<toolbarItem reference="n52-8S-6kR"/>
|
<toolbarItem reference="n52-8S-6kR"/>
|
||||||
@ -274,8 +227,6 @@
|
|||||||
</window>
|
</window>
|
||||||
<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="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"/>
|
||||||
<outlet property="trackRemaining" destination="9WZ-ij-lrb" id="0pH-d7-wvD"/>
|
<outlet property="trackRemaining" destination="9WZ-ij-lrb" id="0pH-d7-wvD"/>
|
||||||
@ -315,8 +266,8 @@
|
|||||||
<!--Window Controller-->
|
<!--Window Controller-->
|
||||||
<scene sceneID="Rpk-bo-5kf">
|
<scene sceneID="Rpk-bo-5kf">
|
||||||
<objects>
|
<objects>
|
||||||
<windowController showSeguePresentationStyle="single" id="xYu-7w-E5x" customClass="PreferencesWindowController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
<windowController id="xYu-7w-E5x" customClass="PreferencesWindowController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<window key="window" title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" tabbingMode="disallowed" id="3FN-my-6kU">
|
<window key="window" title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="3FN-my-6kU">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||||
<rect key="contentRect" x="245" y="301" width="416" height="100"/>
|
<rect key="contentRect" x="245" y="301" width="416" height="100"/>
|
||||||
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
|
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1027"/>
|
||||||
@ -338,7 +289,7 @@
|
|||||||
<tabViewController title="General" selectedTabViewItemIndex="0" tabStyle="toolbar" id="zhe-qh-Mal" customClass="PreferencesViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
<tabViewController title="General" selectedTabViewItemIndex="0" tabStyle="toolbar" id="zhe-qh-Mal" customClass="PreferencesViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<tabViewItems>
|
<tabViewItems>
|
||||||
<tabViewItem label="General" identifier="generalPreferencesTab" image="NSPreferencesGeneral" id="kn0-fa-vM3"/>
|
<tabViewItem label="General" identifier="generalPreferencesTab" image="NSPreferencesGeneral" id="kn0-fa-vM3"/>
|
||||||
<tabViewItem label="Cover Art" identifier="coverArtPreferencesTab" image="coverArtPreferencesIcon" id="4Lj-dz-bOK"/>
|
<tabViewItem label="Album Art" identifier="albumArtPreferencesTab" image="coverArtPreferencesIcon" id="4Lj-dz-bOK"/>
|
||||||
</tabViewItems>
|
</tabViewItems>
|
||||||
<viewControllerTransitionOptions key="transitionOptions" allowUserInteraction="YES"/>
|
<viewControllerTransitionOptions key="transitionOptions" allowUserInteraction="YES"/>
|
||||||
<tabView key="tabView" type="noTabsNoBorder" id="6dC-M0-oC5">
|
<tabView key="tabView" type="noTabsNoBorder" id="6dC-M0-oC5">
|
||||||
@ -359,10 +310,10 @@
|
|||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="916" y="236"/>
|
<point key="canvasLocation" x="916" y="236"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Cover Art-->
|
<!--Album Art-->
|
||||||
<scene sceneID="pQx-0G-WVt">
|
<scene sceneID="pQx-0G-WVt">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController title="Cover Art" id="3C9-vU-zjZ" userLabel="Cover Art" customClass="CoverArtPrefsController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController title="Album Art" id="3C9-vU-zjZ" customClass="AlbumArtPrefsController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" id="PyK-v2-kus">
|
<view key="view" id="PyK-v2-kus">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="524" height="168"/>
|
<rect key="frame" x="0.0" y="0.0" width="524" height="168"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
@ -569,14 +520,14 @@
|
|||||||
<rect key="frame" x="1" y="1" width="200" height="17"/>
|
<rect key="frame" x="1" y="1" width="200" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xgd-Cz-np3">
|
<textField verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xgd-Cz-np3">
|
||||||
<rect key="frame" x="0.0" y="2" width="447" height="14"/>
|
<rect key="frame" x="0.0" y="1" width="329" height="14"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="443" id="mkA-ng-q8a"/>
|
<constraint firstAttribute="width" constant="443" id="mkA-ng-q8a"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="QUEUE" id="Mqf-uh-ibl">
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="QUEUE" id="Mqf-uh-ibl">
|
||||||
<font key="font" metaFont="smallSystemBold"/>
|
<font key="font" metaFont="smallSystemBold"/>
|
||||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlAccentColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
@ -586,27 +537,19 @@
|
|||||||
<constraint firstItem="xgd-Cz-np3" firstAttribute="centerY" secondItem="GOd-cg-juD" secondAttribute="centerY" id="uxd-zs-s33"/>
|
<constraint firstItem="xgd-Cz-np3" firstAttribute="centerY" secondItem="GOd-cg-juD" secondAttribute="centerY" id="uxd-zs-s33"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
</tableCellView>
|
</tableCellView>
|
||||||
<tableCellView identifier="songTitleCell" id="5rR-Gz-AcP" customClass="QueueSongTitleView" customModule="Persephone" customModuleProvider="target">
|
<tableCellView identifier="songTitleCell" id="5rR-Gz-AcP">
|
||||||
<rect key="frame" x="1" y="20" width="200" height="17"/>
|
<rect key="frame" x="1" y="20" width="200" height="17"/>
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<imageView identifier="queuePlayerState" translatesAutoresizingMaskIntoConstraints="NO" id="o8i-cz-hIP" userLabel="Player State View">
|
<imageView translatesAutoresizingMaskIntoConstraints="NO" id="o8i-cz-hIP">
|
||||||
<rect key="frame" x="3" y="-1" width="17" height="17"/>
|
<rect key="frame" x="3" y="0.0" width="17" height="17"/>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstAttribute="width" constant="17" id="00Z-Tq-MFp"/>
|
<constraint firstAttribute="width" constant="17" id="v6R-Dd-a1y"/>
|
||||||
</constraints>
|
</constraints>
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" imageScaling="proportionallyDown" image="playButton" id="ckK-gW-Vhx"/>
|
<imageCell key="cell" refusesFirstResponder="YES" imageScaling="proportionallyDown" id="ckK-gW-Vhx"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
<textField identifier="queuePosition" horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="mc4-Xr-oUl" userLabel="Queue Position View">
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="i0h-bn-auJ">
|
||||||
<rect key="frame" x="4" y="0.0" width="33" height="17"/>
|
<rect key="frame" x="25" y="0.0" width="175" height="17"/>
|
||||||
<textFieldCell key="cell" lineBreakMode="clipping" title="888." id="wpZ-1t-Do7">
|
|
||||||
<font key="font" metaFont="system"/>
|
|
||||||
<color key="textColor" name="tertiaryLabelColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
|
||||||
</textFieldCell>
|
|
||||||
</textField>
|
|
||||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="i0h-bn-auJ" userLabel="Song Title View">
|
|
||||||
<rect key="frame" x="40" y="0.0" width="160" height="17"/>
|
|
||||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="ei8-1e-ErK">
|
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="ei8-1e-ErK">
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
@ -615,19 +558,15 @@
|
|||||||
</textField>
|
</textField>
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="i0h-bn-auJ" firstAttribute="leading" secondItem="o8i-cz-hIP" secondAttribute="trailing" constant="22" id="68y-un-94T"/>
|
<constraint firstItem="o8i-cz-hIP" firstAttribute="bottom" secondItem="i0h-bn-auJ" secondAttribute="bottom" id="3PU-pE-aEs"/>
|
||||||
<constraint firstItem="mc4-Xr-oUl" firstAttribute="centerY" secondItem="5rR-Gz-AcP" secondAttribute="centerY" id="ELP-WE-eIm"/>
|
<constraint firstItem="o8i-cz-hIP" firstAttribute="leading" secondItem="5rR-Gz-AcP" secondAttribute="leading" constant="3" id="6gY-cP-87e"/>
|
||||||
<constraint firstItem="o8i-cz-hIP" firstAttribute="leading" secondItem="5rR-Gz-AcP" secondAttribute="leading" constant="3" id="SkK-PE-YOL"/>
|
<constraint firstItem="o8i-cz-hIP" firstAttribute="top" secondItem="i0h-bn-auJ" secondAttribute="top" id="Aju-r7-8gb"/>
|
||||||
<constraint firstAttribute="trailing" secondItem="i0h-bn-auJ" secondAttribute="trailing" constant="2" id="XQG-u9-wiY"/>
|
<constraint firstItem="o8i-cz-hIP" firstAttribute="centerY" secondItem="5rR-Gz-AcP" secondAttribute="centerY" id="POX-ni-OdT"/>
|
||||||
<constraint firstItem="mc4-Xr-oUl" firstAttribute="baseline" secondItem="i0h-bn-auJ" secondAttribute="baseline" id="YMz-iR-hUI"/>
|
<constraint firstItem="i0h-bn-auJ" firstAttribute="leading" secondItem="o8i-cz-hIP" secondAttribute="trailing" constant="7" id="QIl-PH-2Ox"/>
|
||||||
<constraint firstItem="mc4-Xr-oUl" firstAttribute="leading" secondItem="5rR-Gz-AcP" secondAttribute="leading" constant="6" id="s8m-Sb-8up"/>
|
<constraint firstAttribute="trailing" secondItem="i0h-bn-auJ" secondAttribute="trailing" constant="2" id="S2f-3u-olm"/>
|
||||||
<constraint firstItem="o8i-cz-hIP" firstAttribute="top" secondItem="5rR-Gz-AcP" secondAttribute="top" constant="1" id="srE-Dv-Ie5"/>
|
|
||||||
</constraints>
|
</constraints>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="imageView" destination="o8i-cz-hIP" id="4In-Lr-QcL"/>
|
<outlet property="imageView" destination="o8i-cz-hIP" id="4In-Lr-QcL"/>
|
||||||
<outlet property="queuePlayerStateImage" destination="o8i-cz-hIP" id="lUU-N4-jtd"/>
|
|
||||||
<outlet property="queuePosition" destination="mc4-Xr-oUl" id="aBD-Tg-cDt"/>
|
|
||||||
<outlet property="queueSongTitle" destination="i0h-bn-auJ" id="25v-lw-ypc"/>
|
|
||||||
<outlet property="textField" destination="i0h-bn-auJ" id="5YB-JL-2wJ"/>
|
<outlet property="textField" destination="i0h-bn-auJ" id="5YB-JL-2wJ"/>
|
||||||
</connections>
|
</connections>
|
||||||
</tableCellView>
|
</tableCellView>
|
||||||
@ -696,9 +635,9 @@
|
|||||||
<rect key="frame" x="0.0" y="220" width="328" height="328"/>
|
<rect key="frame" x="0.0" y="220" width="328" height="328"/>
|
||||||
<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">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="328" height="328"/>
|
<rect key="frame" x="0.0" y="0.0" width="328" height="328"/>
|
||||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="defaultCoverArt" id="IoN-3N-TCb"/>
|
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="blankAlbum" id="IoN-3N-TCb"/>
|
||||||
</imageView>
|
</imageView>
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
@ -719,7 +658,7 @@
|
|||||||
</holdingPriorities>
|
</holdingPriorities>
|
||||||
</splitView>
|
</splitView>
|
||||||
<connections>
|
<connections>
|
||||||
<outlet property="queueCoverArtImage" destination="Dw3-M5-tWY" id="3hQ-Gu-XqM"/>
|
<outlet property="queueAlbumArtImage" destination="Dw3-M5-tWY" id="3hQ-Gu-XqM"/>
|
||||||
<outlet property="queueView" destination="jEJ-jg-fll" id="cwo-E8-deo"/>
|
<outlet property="queueView" destination="jEJ-jg-fll" id="cwo-E8-deo"/>
|
||||||
</connections>
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
@ -781,13 +720,11 @@
|
|||||||
</scenes>
|
</scenes>
|
||||||
<resources>
|
<resources>
|
||||||
<image name="NSPreferencesGeneral" width="32" height="32"/>
|
<image name="NSPreferencesGeneral" width="32" height="32"/>
|
||||||
|
<image name="blankAlbum" width="128" height="128"/>
|
||||||
<image name="coverArtPreferencesIcon" width="32" height="32"/>
|
<image name="coverArtPreferencesIcon" width="32" height="32"/>
|
||||||
<image name="defaultCoverArt" width="128" height="128"/>
|
|
||||||
<image name="nextTrackButton" width="17" height="17"/>
|
<image name="nextTrackButton" width="17" height="17"/>
|
||||||
<image name="playButton" width="17" height="17"/>
|
<image name="playButton" width="17" height="17"/>
|
||||||
<image name="prevTrackButton" width="17" height="17"/>
|
<image name="prevTrackButton" width="17" height="17"/>
|
||||||
<image name="repeatButton" width="17" height="17"/>
|
|
||||||
<image name="shuffleButton" width="17" height="17"/>
|
|
||||||
<image name="stopButton" width="17" height="17"/>
|
<image name="stopButton" width="17" height="17"/>
|
||||||
</resources>
|
</resources>
|
||||||
</document>
|
</document>
|
||||||
|
|||||||
@ -1,15 +1,16 @@
|
|||||||
//
|
//
|
||||||
// CoverArtService.swift
|
// AlbumArtService.swift
|
||||||
// Persephone
|
// Persephone
|
||||||
//
|
//
|
||||||
// Created by Daniel Barber on 2019/2/23.
|
// Created by Daniel Barber on 2019/2/23.
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
import PromiseKit
|
import PromiseKit
|
||||||
|
|
||||||
class CoverArtService {
|
class AlbumArtService {
|
||||||
|
var preferences = Preferences()
|
||||||
let song: Song
|
let song: Song
|
||||||
let album: Album
|
let album: Album
|
||||||
|
|
||||||
@ -19,14 +20,14 @@ class CoverArtService {
|
|||||||
let bigArtworkSize = 600
|
let bigArtworkSize = 600
|
||||||
|
|
||||||
var session = URLSession(configuration: .default)
|
var session = URLSession(configuration: .default)
|
||||||
let coverArtQueue = DispatchQueue(label: "coverArtQueue", qos: .utility)
|
let artworkQueue = DispatchQueue(label: "albumArtQueue", qos: .utility)
|
||||||
|
|
||||||
init(song: Song) {
|
init(song: Song) {
|
||||||
self.song = song
|
self.song = song
|
||||||
self.album = song.album
|
self.album = song.album
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchBigCoverArt() -> Promise<NSImage?> {
|
func fetchBigAlbumArt() -> Promise<NSImage?> {
|
||||||
return firstly {
|
return firstly {
|
||||||
self.getArtworkFromFilesystem()
|
self.getArtworkFromFilesystem()
|
||||||
}.then { (image: NSImage?) -> Promise<NSImage?> in
|
}.then { (image: NSImage?) -> Promise<NSImage?> in
|
||||||
@ -36,14 +37,14 @@ class CoverArtService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func fetchCoverArt() -> Guarantee<NSImage?> {
|
func fetchAlbumArt() -> Guarantee<NSImage?> {
|
||||||
return firstly {
|
return firstly {
|
||||||
self.getCachedArtwork()
|
self.getCachedArtwork()
|
||||||
}.then { (artwork: NSImage?) -> Promise<NSImage?> in
|
}.then { (artwork: NSImage?) -> Promise<NSImage?> in
|
||||||
artwork.map(Promise.value) ?? self.getArtworkFromFilesystem()
|
artwork.map(Promise.value) ?? self.getArtworkFromFilesystem()
|
||||||
}.then { (artwork: NSImage?) -> Promise<NSImage?> in
|
}.then { (artwork: NSImage?) -> Promise<NSImage?> in
|
||||||
artwork.map(Promise.value) ?? self.getRemoteArtwork()
|
artwork.map(Promise.value) ?? self.getRemoteArtwork()
|
||||||
}.compactMap(on: coverArtQueue) {
|
}.compactMap(on: artworkQueue) {
|
||||||
return self.sizeAndCacheImage($0).map(Optional.some)
|
return self.sizeAndCacheImage($0).map(Optional.some)
|
||||||
}.recover { _ in
|
}.recover { _ in
|
||||||
return .value(nil)
|
return .value(nil)
|
||||||
@ -1,22 +1,22 @@
|
|||||||
//
|
//
|
||||||
// CoverArtService+Caching.swift
|
// AlbumArtService+Caching.swift
|
||||||
// Persephone
|
// Persephone
|
||||||
//
|
//
|
||||||
// Created by Daniel Barber on 2019/3/17.
|
// Created by Daniel Barber on 2019/3/17.
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
import PromiseKit
|
import PromiseKit
|
||||||
|
|
||||||
extension CoverArtService {
|
extension AlbumArtService {
|
||||||
static let cacheDir = try! FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent(Bundle.main.bundleIdentifier!)
|
static let cacheDir = try! FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent(Bundle.main.bundleIdentifier!)
|
||||||
|
|
||||||
func getCachedArtwork() -> Promise<NSImage?> {
|
func getCachedArtwork() -> Promise<NSImage?> {
|
||||||
return Promise { seal in
|
return Promise { seal in
|
||||||
coverArtQueue.async {
|
artworkQueue.async {
|
||||||
if self.isArtworkCached() {
|
if self.isArtworkCached() {
|
||||||
let cacheFilePath = CoverArtService.cacheDir.appendingPathComponent(self.album.hash).path
|
let cacheFilePath = AlbumArtService.cacheDir.appendingPathComponent(self.album.hash).path
|
||||||
let data = FileManager.default.contents(atPath: cacheFilePath)
|
let data = FileManager.default.contents(atPath: cacheFilePath)
|
||||||
let image = NSImage(data: data ?? Data()) ?? NSImage.defaultCoverArt
|
let image = NSImage(data: data ?? Data()) ?? NSImage.defaultCoverArt
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ extension CoverArtService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func cacheArtwork(data: Data?) {
|
func cacheArtwork(data: Data?) {
|
||||||
coverArtQueue.async {
|
artworkQueue.async {
|
||||||
guard let bundleIdentifier = Bundle.main.bundleIdentifier,
|
guard let bundleIdentifier = Bundle.main.bundleIdentifier,
|
||||||
let cacheDir = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
|
let cacheDir = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
|
||||||
.appendingPathComponent(bundleIdentifier)
|
.appendingPathComponent(bundleIdentifier)
|
||||||
@ -44,7 +44,7 @@ extension CoverArtService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func isArtworkCached() -> Bool {
|
func isArtworkCached() -> Bool {
|
||||||
let cacheFilePath = CoverArtService.cacheDir.appendingPathComponent(album.hash).path
|
let cacheFilePath = AlbumArtService.cacheDir.appendingPathComponent(album.hash).path
|
||||||
|
|
||||||
return FileManager.default.fileExists(atPath: cacheFilePath)
|
return FileManager.default.fileExists(atPath: cacheFilePath)
|
||||||
}
|
}
|
||||||
@ -1,15 +1,15 @@
|
|||||||
//
|
//
|
||||||
// CoverArtService+Filesystem.swift
|
// AlbumArtService+Filesystem.swift
|
||||||
// Persephone
|
// Persephone
|
||||||
//
|
//
|
||||||
// Created by Daniel Barber on 2019/3/17.
|
// Created by Daniel Barber on 2019/3/17.
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
import PromiseKit
|
import PromiseKit
|
||||||
|
|
||||||
extension CoverArtService {
|
extension AlbumArtService {
|
||||||
var coverArtFilenames: [String] {
|
var coverArtFilenames: [String] {
|
||||||
return [
|
return [
|
||||||
"folder.jpg",
|
"folder.jpg",
|
||||||
@ -19,12 +19,12 @@ extension CoverArtService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var musicDir: String {
|
var musicDir: String {
|
||||||
return App.store.state.preferencesState.expandedMpdLibraryDir
|
return self.preferences.expandedMpdLibraryDir
|
||||||
}
|
}
|
||||||
|
|
||||||
func getArtworkFromFilesystem() -> Promise<NSImage?> {
|
func getArtworkFromFilesystem() -> Promise<NSImage?> {
|
||||||
return Promise { seal in
|
return Promise { seal in
|
||||||
coverArtQueue.async {
|
artworkQueue.async {
|
||||||
guard let artworkPath = self.fileSystemArtworkFilePath()
|
guard let artworkPath = self.fileSystemArtworkFilePath()
|
||||||
else { seal.fulfill(nil); return }
|
else { seal.fulfill(nil); return }
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ extension CoverArtService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func fileSystemArtworkFilePath() -> String? {
|
func fileSystemArtworkFilePath() -> String? {
|
||||||
let musicDir = App.store.state.preferencesState.expandedMpdLibraryDir
|
let musicDir = self.preferences.expandedMpdLibraryDir
|
||||||
|
|
||||||
return self.coverArtFilenames
|
return self.coverArtFilenames
|
||||||
.lazy
|
.lazy
|
||||||
@ -1,17 +1,17 @@
|
|||||||
//
|
//
|
||||||
// CoverArtService+Remote.swift
|
// AlbumArtService+Remote.swift
|
||||||
// Persephone
|
// Persephone
|
||||||
//
|
//
|
||||||
// Created by Daniel Barber on 2019/3/17.
|
// Created by Daniel Barber on 2019/3/17.
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
import SwiftyJSON
|
import SwiftyJSON
|
||||||
import PromiseKit
|
import PromiseKit
|
||||||
import PMKFoundation
|
import PMKFoundation
|
||||||
|
|
||||||
extension CoverArtService {
|
extension AlbumArtService {
|
||||||
enum RemoteArtworkError: Error {
|
enum RemoteArtworkError: Error {
|
||||||
case noArtworkAvailable
|
case noArtworkAvailable
|
||||||
case notConfigured
|
case notConfigured
|
||||||
@ -19,13 +19,13 @@ extension CoverArtService {
|
|||||||
|
|
||||||
func getRemoteArtwork() -> Promise<NSImage?> {
|
func getRemoteArtwork() -> Promise<NSImage?> {
|
||||||
return Promise { seal in
|
return Promise { seal in
|
||||||
if App.store.state.preferencesState .fetchMissingArtworkFromInternet {
|
if preferences.fetchMissingArtworkFromInternet {
|
||||||
coverArtQueue.async {
|
artworkQueue.async {
|
||||||
let coverArtWorkItem = DispatchWorkItem {
|
let albumArtWorkItem = DispatchWorkItem {
|
||||||
self.getArtworkFromMusicBrainz().map(Optional.some).pipe(to: seal.resolve)
|
self.getArtworkFromMusicBrainz().map(Optional.some).pipe(to: seal.resolve)
|
||||||
}
|
}
|
||||||
|
|
||||||
CoverArtQueue.shared.addToQueue(workItem: coverArtWorkItem)
|
AlbumArtQueue.shared.addToQueue(workItem: albumArtWorkItem)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
throw RemoteArtworkError.notConfigured
|
throw RemoteArtworkError.notConfigured
|
||||||
@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
struct SongNotifierService {
|
struct SongNotifierService {
|
||||||
let song: Song
|
let song: Song
|
||||||
|
|||||||
@ -1,21 +0,0 @@
|
|||||||
//
|
|
||||||
// AlbumListActions.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/28.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AppKit
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
struct ResetAlbumListCoverArtAction: Action {}
|
|
||||||
|
|
||||||
struct UpdateCoverArtAction: Action {
|
|
||||||
var coverArt: NSImage?
|
|
||||||
var albumIndex: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UpdateAlbumListAction: Action {
|
|
||||||
var albums: [MPDClient.MPDAlbum]
|
|
||||||
}
|
|
||||||
@ -1,39 +0,0 @@
|
|||||||
//
|
|
||||||
// MPDActions.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/30.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
struct MPDConnectAction: Action {}
|
|
||||||
struct MPDDisconnectAction: Action {}
|
|
||||||
|
|
||||||
struct MPDPlayPauseAction: Action {}
|
|
||||||
struct MPDStopAction: Action {}
|
|
||||||
struct MPDNextTrackAction: Action {}
|
|
||||||
struct MPDPrevTrackAction: Action {}
|
|
||||||
|
|
||||||
struct MPDPlayTrack: Action {
|
|
||||||
let queuePos: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MPDPlayAlbum: Action {
|
|
||||||
let album: MPDClient.MPDAlbum
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MPDSeekCurrentSong: Action {
|
|
||||||
let timeInSeconds: Float
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MPDUpdateDatabaseAction: Action {}
|
|
||||||
|
|
||||||
struct MPDSetShuffleAction: Action {
|
|
||||||
let shuffleState: Bool
|
|
||||||
}
|
|
||||||
|
|
||||||
struct MPDSetRepeatAction: Action {
|
|
||||||
let repeatState: Bool
|
|
||||||
}
|
|
||||||
@ -1,26 +0,0 @@
|
|||||||
//
|
|
||||||
// PlayerActions.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/28.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AppKit
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
struct UpdateCurrentCoverArtAction: Action {
|
|
||||||
var coverArt: NSImage?
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UpdateCurrentSongAction: Action {
|
|
||||||
var currentSong: Song?
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UpdateElapsedTimeAction: Action {
|
|
||||||
var elapsedTimeMs: UInt = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UpdateStatusAction: Action {
|
|
||||||
var status: MPDClient.MPDStatus
|
|
||||||
}
|
|
||||||
@ -1,27 +0,0 @@
|
|||||||
//
|
|
||||||
// PreferencesActions.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/28.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
struct UpdateServerHost: Action {
|
|
||||||
var host: String?
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UpdateServerPort: Action {
|
|
||||||
var port: Int?
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UpdateMPDLibraryDir: Action {
|
|
||||||
var mpdLibraryDir: String?
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UpdateFetchMissingArtworkFromInternet: Action {
|
|
||||||
var fetchMissingArtworkFromInternet: Bool
|
|
||||||
}
|
|
||||||
|
|
||||||
struct SavePreferences: Action {}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
//
|
|
||||||
// QueueActions.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/28.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
struct UpdateQueueAction: Action {
|
|
||||||
var queue: [MPDClient.MPDSong]
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UpdateQueuePosAction: Action {
|
|
||||||
var queuePos: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UpdateQueuePlayerStateAction: Action {
|
|
||||||
var state: MPDClient.MPDStatus.State?
|
|
||||||
}
|
|
||||||
@ -1,19 +0,0 @@
|
|||||||
//
|
|
||||||
// UIActions.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/5/02.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
struct MainWindowDidOpenAction: Action {}
|
|
||||||
|
|
||||||
struct MainWindowDidCloseAction: Action {}
|
|
||||||
|
|
||||||
struct MainWindowDidMinimizeAction: Action {}
|
|
||||||
|
|
||||||
struct DatabaseUpdateStartedAction: Action {}
|
|
||||||
|
|
||||||
struct DatabaseUpdateFinishedAction: Action {}
|
|
||||||
@ -1,13 +0,0 @@
|
|||||||
//
|
|
||||||
// AlbumListState.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/19.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
struct AlbumListState: StateType, Equatable {
|
|
||||||
var albums: [Album] = []
|
|
||||||
}
|
|
||||||
@ -1,18 +0,0 @@
|
|||||||
//
|
|
||||||
// AppState.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/18.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
struct AppState: StateType {
|
|
||||||
var playerState = PlayerState()
|
|
||||||
var queueState = QueueState()
|
|
||||||
var albumListState = AlbumListState()
|
|
||||||
var preferencesState = PreferencesState()
|
|
||||||
var mpdState = MPDState()
|
|
||||||
var uiState = UIState()
|
|
||||||
}
|
|
||||||
@ -1,11 +0,0 @@
|
|||||||
//
|
|
||||||
// MPDState.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/30.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
struct MPDState: StateType {}
|
|
||||||
@ -1,33 +0,0 @@
|
|||||||
//
|
|
||||||
// PlayerState.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/19.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AppKit
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
struct PlayerState: StateType {
|
|
||||||
var status: MPDClient.MPDStatus?
|
|
||||||
var currentSong: Song?
|
|
||||||
var currentArtwork: NSImage?
|
|
||||||
|
|
||||||
var state: MPDClient.MPDStatus.State?
|
|
||||||
var shuffleState: Bool = false
|
|
||||||
var repeatState: Bool = false
|
|
||||||
|
|
||||||
var totalTime: UInt?
|
|
||||||
var elapsedTimeMs: UInt?
|
|
||||||
}
|
|
||||||
|
|
||||||
extension PlayerState: Equatable {
|
|
||||||
static func == (lhs: PlayerState, rhs: PlayerState) -> Bool {
|
|
||||||
return (lhs.state == rhs.state) &&
|
|
||||||
(lhs.totalTime == rhs.totalTime) &&
|
|
||||||
(lhs.elapsedTimeMs == rhs.elapsedTimeMs) &&
|
|
||||||
(lhs.shuffleState == rhs.shuffleState) &&
|
|
||||||
(lhs.repeatState == rhs.repeatState)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,51 +0,0 @@
|
|||||||
//
|
|
||||||
// PreferencesState.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/28.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
struct PreferencesState: StateType, Equatable {
|
|
||||||
let preferences = UserDefaults.standard
|
|
||||||
|
|
||||||
var mpdServer: MPDServer
|
|
||||||
let mpdLibraryDirDefault = "~/Music"
|
|
||||||
|
|
||||||
var mpdLibraryDir: String?
|
|
||||||
|
|
||||||
var mpdLibraryDirOrDefault: String {
|
|
||||||
return mpdLibraryDir ?? mpdLibraryDirDefault
|
|
||||||
}
|
|
||||||
|
|
||||||
var expandedMpdLibraryDir: String {
|
|
||||||
return NSString(string: mpdLibraryDirOrDefault).expandingTildeInPath
|
|
||||||
}
|
|
||||||
|
|
||||||
var fetchMissingArtworkFromInternet: Bool
|
|
||||||
|
|
||||||
init() {
|
|
||||||
self.mpdServer = MPDServer(
|
|
||||||
host: preferences.string(forKey: "mpdHost"),
|
|
||||||
port: preferences.value(forKey: "mpdPort") as? Int
|
|
||||||
)
|
|
||||||
self.mpdLibraryDir = preferences.string(forKey: "mpdLibraryDir")
|
|
||||||
self.fetchMissingArtworkFromInternet = preferences.bool(
|
|
||||||
forKey: "fetchMissingArtworkFromInternet"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func save() {
|
|
||||||
preferences.set(mpdServer.host, forKey: "mpdHost")
|
|
||||||
if (mpdServer.port.map { $0 > 0 } ?? false) {
|
|
||||||
preferences.set(mpdServer.port, forKey: "mpdPort")
|
|
||||||
} else {
|
|
||||||
preferences.removeObject(forKey: "mpdPort")
|
|
||||||
}
|
|
||||||
preferences.set(mpdLibraryDir, forKey: "mpdLibraryDir")
|
|
||||||
preferences.set(fetchMissingArtworkFromInternet, forKey: "fetchMissingArtworkFromInternet")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,16 +0,0 @@
|
|||||||
//
|
|
||||||
// QueueState.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/19.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
struct QueueState: StateType, Equatable {
|
|
||||||
var queue: [QueueItem] = []
|
|
||||||
var queuePos: Int = -1
|
|
||||||
|
|
||||||
var state: MPDClient.MPDStatus.State?
|
|
||||||
}
|
|
||||||
@ -1,41 +0,0 @@
|
|||||||
//
|
|
||||||
// AlbumListReducer.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/21.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
func albumListReducer(action: Action, state: AlbumListState?) -> AlbumListState {
|
|
||||||
var state = state ?? AlbumListState()
|
|
||||||
|
|
||||||
switch action {
|
|
||||||
case let action as UpdateAlbumListAction:
|
|
||||||
state.albums = action.albums.map { Album(mpdAlbum: $0) }
|
|
||||||
|
|
||||||
case let action as UpdateCoverArtAction:
|
|
||||||
state.albums[action.albumIndex].coverArt = .loaded(action.coverArt)
|
|
||||||
|
|
||||||
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:
|
|
||||||
break
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
@ -1,20 +0,0 @@
|
|||||||
//
|
|
||||||
// AppReducer.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/19.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
func appReducer(action: Action, state: AppState?) -> AppState {
|
|
||||||
return AppState(
|
|
||||||
playerState: playerReducer(action: action, state: state?.playerState),
|
|
||||||
queueState: queueReducer(action: action, state: state?.queueState),
|
|
||||||
albumListState: albumListReducer(action: action, state: state?.albumListState),
|
|
||||||
preferencesState: preferencesReducer(action: action, state: state?.preferencesState),
|
|
||||||
mpdState: mpdReducer(action: action, state: state?.mpdState),
|
|
||||||
uiState: uiReducer(action: action, state: state?.uiState)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@ -1,56 +0,0 @@
|
|||||||
//
|
|
||||||
// MPDReducer.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/30.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
func mpdReducer(action: Action, state: MPDState?) -> MPDState {
|
|
||||||
let state = state ?? MPDState()
|
|
||||||
|
|
||||||
switch action {
|
|
||||||
case is MPDConnectAction:
|
|
||||||
let mpdServer = App.store.state.preferencesState.mpdServer
|
|
||||||
App.mpdClient.connect(
|
|
||||||
host: mpdServer.hostOrDefault,
|
|
||||||
port: mpdServer.portOrDefault
|
|
||||||
)
|
|
||||||
case is MPDDisconnectAction:
|
|
||||||
App.mpdClient.disconnect()
|
|
||||||
|
|
||||||
case is MPDPlayPauseAction:
|
|
||||||
App.mpdClient.playPause()
|
|
||||||
case is MPDStopAction:
|
|
||||||
App.mpdClient.stop()
|
|
||||||
case is MPDNextTrackAction:
|
|
||||||
App.mpdClient.nextTrack()
|
|
||||||
case is MPDPrevTrackAction:
|
|
||||||
App.mpdClient.prevTrack()
|
|
||||||
|
|
||||||
case let action as MPDPlayTrack:
|
|
||||||
App.mpdClient.playTrack(at: action.queuePos)
|
|
||||||
|
|
||||||
case let action as MPDPlayAlbum:
|
|
||||||
App.mpdClient.playAlbum(action.album)
|
|
||||||
|
|
||||||
case let action as MPDSeekCurrentSong:
|
|
||||||
App.mpdClient.seekCurrentSong(timeInSeconds: action.timeInSeconds)
|
|
||||||
|
|
||||||
case let action as MPDSetShuffleAction:
|
|
||||||
App.mpdClient.setShuffleState(shuffleState: action.shuffleState)
|
|
||||||
|
|
||||||
case let action as MPDSetRepeatAction:
|
|
||||||
App.mpdClient.setRepeatState(repeatState: action.repeatState)
|
|
||||||
|
|
||||||
case is MPDUpdateDatabaseAction:
|
|
||||||
App.mpdClient.updateDatabase()
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
@ -1,70 +0,0 @@
|
|||||||
//
|
|
||||||
// PlayerReducer.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/19.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AppKit
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
func playerReducer(action: Action, state: PlayerState?) -> PlayerState {
|
|
||||||
var state = state ?? PlayerState()
|
|
||||||
|
|
||||||
switch action {
|
|
||||||
case let action as UpdateStatusAction:
|
|
||||||
state.status = action.status
|
|
||||||
state.state = action.status.state
|
|
||||||
state.totalTime = action.status.totalTime
|
|
||||||
state.elapsedTimeMs = action.status.elapsedTimeMs
|
|
||||||
state.shuffleState = action.status.shuffleState
|
|
||||||
state.repeatState = action.status.repeatState
|
|
||||||
|
|
||||||
if state.state == .playing {
|
|
||||||
App.trackTimer.start(elapsedTimeMs: state.elapsedTimeMs)
|
|
||||||
} else {
|
|
||||||
App.trackTimer.stop(elapsedTimeMs: state.elapsedTimeMs)
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(
|
|
||||||
UpdateQueuePlayerStateAction(state: state.state)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case let action as UpdateCurrentSongAction:
|
|
||||||
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:
|
|
||||||
state.elapsedTimeMs = action.elapsedTimeMs
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
@ -1,34 +0,0 @@
|
|||||||
//
|
|
||||||
// PreferencesReducer.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/28.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
func preferencesReducer(action: Action, state: PreferencesState?) -> PreferencesState {
|
|
||||||
|
|
||||||
var state = state ?? PreferencesState()
|
|
||||||
|
|
||||||
switch action {
|
|
||||||
case let action as UpdateServerHost:
|
|
||||||
state.mpdServer.host = action.host
|
|
||||||
|
|
||||||
case let action as UpdateServerPort:
|
|
||||||
state.mpdServer.port = action.port
|
|
||||||
|
|
||||||
case let action as UpdateMPDLibraryDir:
|
|
||||||
state.mpdLibraryDir = action.mpdLibraryDir
|
|
||||||
|
|
||||||
case is SavePreferences:
|
|
||||||
state.save()
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
@ -1,61 +0,0 @@
|
|||||||
//
|
|
||||||
// QueueReducer.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/21.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AppKit
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
func queueReducer(action: Action, state: QueueState?) -> QueueState {
|
|
||||||
var state = state ?? QueueState()
|
|
||||||
|
|
||||||
switch action {
|
|
||||||
case let action as UpdateQueueAction:
|
|
||||||
state.queuePos = -1
|
|
||||||
|
|
||||||
state.queue = action.queue.enumerated().map { index, mpdSong in
|
|
||||||
let song = Song(mpdSong: mpdSong)
|
|
||||||
|
|
||||||
return QueueItem(
|
|
||||||
song: song,
|
|
||||||
queuePos: index,
|
|
||||||
isPlaying: index == state.queuePos
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
case let action as UpdateQueuePosAction:
|
|
||||||
let oldSongRowPos = state.queuePos
|
|
||||||
let newSongRowPos = action.queuePos
|
|
||||||
state.queuePos = action.queuePos
|
|
||||||
|
|
||||||
if oldSongRowPos >= 0 {
|
|
||||||
state.queue[oldSongRowPos].isPlaying = false
|
|
||||||
}
|
|
||||||
if newSongRowPos >= 0 {
|
|
||||||
state.queue[newSongRowPos].isPlaying = true
|
|
||||||
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(
|
|
||||||
UpdateCurrentSongAction(currentSong: state.queue[newSongRowPos].song)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
App.store.dispatch(
|
|
||||||
UpdateCurrentSongAction(currentSong: nil)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
case let action as UpdateQueuePlayerStateAction:
|
|
||||||
state.state = action.state
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
@ -1,35 +0,0 @@
|
|||||||
//
|
|
||||||
// UIReducer.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/5/02.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
func uiReducer(action: Action, state: UIState?) -> UIState {
|
|
||||||
var state = state ?? UIState()
|
|
||||||
|
|
||||||
switch action {
|
|
||||||
case is MainWindowDidOpenAction:
|
|
||||||
state.mainWindowState = .open
|
|
||||||
|
|
||||||
case is MainWindowDidCloseAction:
|
|
||||||
state.mainWindowState = .closed
|
|
||||||
|
|
||||||
case is MainWindowDidMinimizeAction:
|
|
||||||
state.mainWindowState = .minimised
|
|
||||||
|
|
||||||
case is DatabaseUpdateStartedAction:
|
|
||||||
state.databaseUpdating = true
|
|
||||||
|
|
||||||
case is DatabaseUpdateFinishedAction:
|
|
||||||
state.databaseUpdating = false
|
|
||||||
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
return state
|
|
||||||
}
|
|
||||||
@ -1,21 +0,0 @@
|
|||||||
//
|
|
||||||
// UIState.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/5/02.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
enum MainWindowState {
|
|
||||||
case open
|
|
||||||
case closed
|
|
||||||
case minimised
|
|
||||||
}
|
|
||||||
|
|
||||||
struct UIState: StateType {
|
|
||||||
var mainWindowState: MainWindowState = .closed
|
|
||||||
|
|
||||||
var databaseUpdating: Bool = false
|
|
||||||
}
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
class AlbumItemView: NSView {
|
class AlbumItemView: NSView {
|
||||||
var trackingArea: NSTrackingArea?
|
var trackingArea: NSTrackingArea?
|
||||||
|
|||||||
@ -1,32 +0,0 @@
|
|||||||
//
|
|
||||||
// CurrentCoverArtView.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/4/27.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AppKit
|
|
||||||
import ReSwift
|
|
||||||
|
|
||||||
class CurrentCoverArtView: NSImageView {
|
|
||||||
required init?(coder: NSCoder) {
|
|
||||||
super.init(coder: coder)
|
|
||||||
|
|
||||||
App.store.subscribe(self) {
|
|
||||||
$0.select { $0.playerState.currentArtwork }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extension CurrentCoverArtView: StoreSubscriber {
|
|
||||||
typealias StoreSubscriberStateType = NSImage?
|
|
||||||
|
|
||||||
func newState(state: NSImage?) {
|
|
||||||
if let coverArt = state {
|
|
||||||
image = coverArt
|
|
||||||
} else {
|
|
||||||
image = .defaultCoverArt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -6,7 +6,7 @@
|
|||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
//
|
//
|
||||||
|
|
||||||
import AppKit
|
import Cocoa
|
||||||
|
|
||||||
class MainWindow: NSWindow {
|
class MainWindow: NSWindow {
|
||||||
override func keyDown(with event: NSEvent) {
|
override func keyDown(with event: NSEvent) {
|
||||||
|
|||||||
@ -1,30 +0,0 @@
|
|||||||
//
|
|
||||||
// QueueSongTitleView.swift
|
|
||||||
// Persephone
|
|
||||||
//
|
|
||||||
// Created by Daniel Barber on 2019/5/10.
|
|
||||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
import AppKit
|
|
||||||
|
|
||||||
class QueueSongTitleView: NSTableCellView {
|
|
||||||
@IBOutlet var queuePlayerStateImage: NSImageView!
|
|
||||||
@IBOutlet var queuePosition: NSTextField!
|
|
||||||
@IBOutlet var queueSongTitle: NSTextField!
|
|
||||||
|
|
||||||
func setQueueSong(_ queueItem: QueueItem, queueIcon: NSImage?) {
|
|
||||||
queuePosition?.font = .timerFont
|
|
||||||
queueSongTitle?.stringValue = queueItem.song.title
|
|
||||||
|
|
||||||
if queueItem.isPlaying && queueIcon != nil {
|
|
||||||
queueSongTitle?.font = .systemFontBold
|
|
||||||
queuePlayerStateImage?.image = queueIcon
|
|
||||||
queuePosition?.stringValue = ""
|
|
||||||
} else {
|
|
||||||
queueSongTitle?.font = .systemFontRegular
|
|
||||||
queuePlayerStateImage?.image = nil
|
|
||||||
queuePosition?.stringValue = "\(queueItem.queuePos + 1)."
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 299 B |
|
Before Width: | Height: | Size: 344 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 353 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 941 KiB After Width: | Height: | Size: 795 KiB |