mirror of
https://github.com/danbee/persephone
synced 2025-03-04 08:39:11 +00:00
Compare commits
No commits in common. "caf7f990e07379d19e1d92802aa7764a910b6098" and "a2706f4e0adbe5932ebfe5021683d76dfbeeffa4" have entirely different histories.
caf7f990e0
...
a2706f4e0a
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,17 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 0.11.0-alpha - 2019-04-01
|
||||
|
||||
### Added
|
||||
|
||||
- Display the artwork of the currently playing track underneath the queue
|
||||
|
||||
### Changed
|
||||
|
||||
- Fix a bug that caused the album view to jump about occasionally
|
||||
- Fix a bug that caused the incorrect artwork to be displayed for albums that
|
||||
have the same name as another album
|
||||
|
||||
## 0.10.3a - 2019-03-25
|
||||
|
||||
### Changed
|
||||
|
||||
3
Cartfile
3
Cartfile
@ -1,4 +1,3 @@
|
||||
github "SwiftyJSON/SwiftyJSON" ~> 4.0
|
||||
github "PromiseKit/Foundation" ~> 3.0
|
||||
github "nhurden/MediaKeyTap"
|
||||
github "krzyzanowskim/CryptoSwift"
|
||||
github "nhurden/MediaKeyTap" "fix-tis-tsm-error"
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
github "PromiseKit/Foundation" "3.3.2"
|
||||
github "PromiseKit/Foundation" "3.3.1"
|
||||
github "SwiftyJSON/SwiftyJSON" "4.2.0"
|
||||
github "krzyzanowskim/CryptoSwift" "1.0.0"
|
||||
github "mxcl/PromiseKit" "6.8.4"
|
||||
github "nhurden/MediaKeyTap" "2.2.1"
|
||||
github "mxcl/PromiseKit" "6.8.3"
|
||||
github "nhurden/MediaKeyTap" "355d346c56243e6d56487fa46fcad945251e16ae"
|
||||
|
||||
@ -19,7 +19,6 @@
|
||||
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 */; };
|
||||
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 */; };
|
||||
@ -42,7 +41,7 @@
|
||||
E42A8F3C22176D6400A13ED9 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = E42A8F3A22176D6400A13ED9 /* README.md */; };
|
||||
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E1221CD4E200184CFC /* NSFont.swift */; };
|
||||
E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E435E3E3221CD75D00184CFC /* NSImage.swift */; };
|
||||
E450AD7E222620A10091BED3 /* Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD7D222620A10091BED3 /* Album.swift */; };
|
||||
E450AD7E222620A10091BED3 /* AlbumItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD7D222620A10091BED3 /* AlbumItem.swift */; };
|
||||
E450AD8622262AE60091BED3 /* SwiftyJSON.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E450AD8522262AE60091BED3 /* SwiftyJSON.framework */; };
|
||||
E450AD8822262AEC0091BED3 /* SwiftyJSON.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E450AD8522262AE60091BED3 /* SwiftyJSON.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
E450AD8F22262C620091BED3 /* PromiseKit.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD8E22262C620091BED3 /* PromiseKit.framework.dSYM */; };
|
||||
@ -51,17 +50,11 @@
|
||||
E450AD9322262C970091BED3 /* PromiseKit.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E450AD8C22262C590091BED3 /* PromiseKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
E450AD9522262DF10091BED3 /* AlbumArtQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD9422262DF10091BED3 /* AlbumArtQueue.swift */; };
|
||||
E450AD98222633920091BED3 /* Alamofire.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD96222633920091BED3 /* Alamofire.framework.dSYM */; };
|
||||
E450AD9D2229B9050091BED3 /* String.swift in Sources */ = {isa = PBXBuildFile; fileRef = E450AD9C2229B9050091BED3 /* String.swift */; };
|
||||
E450ADA12229E7C90091BED3 /* PMKFoundation.framework.dSYM in Resources */ = {isa = PBXBuildFile; fileRef = E450AD9F2229E7C90091BED3 /* PMKFoundation.framework.dSYM */; };
|
||||
E450ADA32229E7E00091BED3 /* PMKFoundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E450ADA02229E7C90091BED3 /* PMKFoundation.framework */; };
|
||||
E450ADA42229E7E00091BED3 /* PMKFoundation.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E450ADA02229E7C90091BED3 /* PMKFoundation.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45962C52241A78500FC1A1E /* MPDCommand.swift */; };
|
||||
E45E4FDA22515D87004B537F /* CHANGELOG.md in Resources */ = {isa = PBXBuildFile; fileRef = E45E4FD722515D87004B537F /* CHANGELOG.md */; };
|
||||
E45E4FDB22515D87004B537F /* Brewfile in Resources */ = {isa = PBXBuildFile; fileRef = E45E4FD822515D87004B537F /* Brewfile */; };
|
||||
E45E4FDC22515D87004B537F /* Cartfile in Resources */ = {isa = PBXBuildFile; fileRef = E45E4FD922515D87004B537F /* Cartfile */; };
|
||||
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, ); }; };
|
||||
E45962C62241A78500FC1A1E /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = E45962C52241A78500FC1A1E /* Command.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 */; };
|
||||
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */; };
|
||||
@ -71,20 +64,20 @@
|
||||
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FDC2220A6D100F747E6 /* Time.swift */; };
|
||||
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */; };
|
||||
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4928E0A2218D62A001D4BEA /* CGColor.swift */; };
|
||||
E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A642D922090CBE00067D21 /* MPDStatus.swift */; };
|
||||
E4A642DA22090CBE00067D21 /* Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A642D922090CBE00067D21 /* Status.swift */; };
|
||||
E4A83BEF2221F8CF0098FED6 /* AlbumArtPrefsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.swift */; };
|
||||
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */; };
|
||||
E4A83BF4222207D50098FED6 /* AlbumArtService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BF3222207D50098FED6 /* AlbumArtService.swift */; };
|
||||
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; };
|
||||
E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* MPDIdle.swift */; };
|
||||
E4C8B53E22349002009A20F3 /* Idle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* Idle.swift */; };
|
||||
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC8F2204EC7F0024217A /* Delegate.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 */; };
|
||||
E4EB2379220F10B8008C70C0 /* MPDPair.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EB2378220F10B8008C70C0 /* MPDPair.swift */; };
|
||||
E4EB237B220F7CF1008C70C0 /* MPDAlbum.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EB237A220F7CF1008C70C0 /* MPDAlbum.swift */; };
|
||||
E4E8CC9A22075D370024217A /* Song.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC9922075D370024217A /* Song.swift */; };
|
||||
E4EB2379220F10B8008C70C0 /* Pair.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EB2378220F10B8008C70C0 /* Pair.swift */; };
|
||||
E4EB237B220F7CF1008C70C0 /* Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4EB237A220F7CF1008C70C0 /* Album.swift */; };
|
||||
E4F6B460221E119B00ACF42A /* QueueDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */; };
|
||||
E4F6B463221E125900ACF42A /* QueueItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B462221E125900ACF42A /* QueueItem.swift */; };
|
||||
E4F6B463221E125900ACF42A /* SongItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B462221E125900ACF42A /* SongItem.swift */; };
|
||||
E4F6B467221E233200ACF42A /* AlbumDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4F6B466221E233200ACF42A /* AlbumDataSource.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
@ -115,7 +108,6 @@
|
||||
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 */,
|
||||
);
|
||||
@ -153,7 +145,6 @@
|
||||
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>"; };
|
||||
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>"; };
|
||||
E41B22C421FB715A00D544F6 /* module.modulemap */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.module-map"; path = module.modulemap; sourceTree = "<group>"; };
|
||||
E41B22C521FB932700D544F6 /* MPDClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDClient.swift; sourceTree = "<group>"; };
|
||||
@ -210,7 +201,7 @@
|
||||
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>"; };
|
||||
E435E3E3221CD75D00184CFC /* NSImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSImage.swift; sourceTree = "<group>"; };
|
||||
E450AD7D222620A10091BED3 /* Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Album.swift; sourceTree = "<group>"; };
|
||||
E450AD7D222620A10091BED3 /* AlbumItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumItem.swift; sourceTree = "<group>"; };
|
||||
E450AD8522262AE60091BED3 /* SwiftyJSON.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftyJSON.framework; path = Carthage/Build/Mac/SwiftyJSON.framework; sourceTree = "<group>"; };
|
||||
E450AD8C22262C590091BED3 /* PromiseKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PromiseKit.framework; path = Carthage/Build/Mac/PromiseKit.framework; sourceTree = "<group>"; };
|
||||
E450AD8E22262C620091BED3 /* PromiseKit.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = PromiseKit.framework.dSYM; path = Carthage/Build/Mac/PromiseKit.framework.dSYM; sourceTree = "<group>"; };
|
||||
@ -218,15 +209,11 @@
|
||||
E450AD9422262DF10091BED3 /* AlbumArtQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumArtQueue.swift; sourceTree = "<group>"; };
|
||||
E450AD96222633920091BED3 /* Alamofire.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = Alamofire.framework.dSYM; path = Carthage/Build/Mac/Alamofire.framework.dSYM; sourceTree = "<group>"; };
|
||||
E450AD97222633920091BED3 /* Alamofire.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Alamofire.framework; path = Carthage/Build/Mac/Alamofire.framework; sourceTree = "<group>"; };
|
||||
E450AD9C2229B9050091BED3 /* String.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = String.swift; sourceTree = "<group>"; };
|
||||
E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = PersephoneBridgingHeader.h; sourceTree = "<group>"; };
|
||||
E450AD9F2229E7C90091BED3 /* PMKFoundation.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = PMKFoundation.framework.dSYM; path = Carthage/Build/Mac/PMKFoundation.framework.dSYM; sourceTree = "<group>"; };
|
||||
E450ADA02229E7C90091BED3 /* PMKFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PMKFoundation.framework; path = Carthage/Build/Mac/PMKFoundation.framework; sourceTree = "<group>"; };
|
||||
E45962C52241A78500FC1A1E /* MPDCommand.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDCommand.swift; sourceTree = "<group>"; };
|
||||
E45E4FD722515D87004B537F /* CHANGELOG.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = CHANGELOG.md; sourceTree = SOURCE_ROOT; };
|
||||
E45E4FD822515D87004B537F /* Brewfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Brewfile; sourceTree = SOURCE_ROOT; };
|
||||
E45E4FD922515D87004B537F /* Cartfile */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Cartfile; sourceTree = SOURCE_ROOT; };
|
||||
E45E4FDD225168DA004B537F /* CryptoSwift.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = CryptoSwift.framework.dSYM; path = Carthage/Build/Mac/CryptoSwift.framework.dSYM; sourceTree = "<group>"; };
|
||||
E45E4FDE225168DA004B537F /* CryptoSwift.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CryptoSwift.framework; path = Carthage/Build/Mac/CryptoSwift.framework; sourceTree = "<group>"; };
|
||||
E45962C52241A78500FC1A1E /* Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
|
||||
E465049921E94DF500A70F4C /* WindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowController.swift; sourceTree = "<group>"; };
|
||||
E47E2FCB2220573500F747E6 /* MediaKeyTap.framework.dSYM */ = {isa = PBXFileReference; lastKnownFileType = wrapper.dsym; name = MediaKeyTap.framework.dSYM; path = Carthage/Build/Mac/MediaKeyTap.framework.dSYM; sourceTree = "<group>"; };
|
||||
E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSplitViewController.swift; sourceTree = "<group>"; };
|
||||
@ -236,20 +223,20 @@
|
||||
E47E2FDC2220A6D100F747E6 /* Time.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Time.swift; sourceTree = "<group>"; };
|
||||
E47E2FE42220AA0700F747E6 /* AlbumViewLayout.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AlbumViewLayout.swift; sourceTree = "<group>"; };
|
||||
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 /* Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Status.swift; sourceTree = "<group>"; };
|
||||
E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumArtPrefsController.swift; sourceTree = "<group>"; };
|
||||
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = "<group>"; };
|
||||
E4A83BF3222207D50098FED6 /* AlbumArtService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumArtService.swift; sourceTree = "<group>"; };
|
||||
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
||||
E4C8B53D22349002009A20F3 /* MPDIdle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MPDIdle.swift; sourceTree = "<group>"; };
|
||||
E4C8B53D22349002009A20F3 /* Idle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Idle.swift; sourceTree = "<group>"; };
|
||||
E4E8CC8F2204EC7F0024217A /* Delegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delegate.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
E4E8CC9922075D370024217A /* Song.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Song.swift; sourceTree = "<group>"; };
|
||||
E4EB2378220F10B8008C70C0 /* Pair.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Pair.swift; sourceTree = "<group>"; };
|
||||
E4EB237A220F7CF1008C70C0 /* Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Album.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 /* SongItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SongItem.swift; sourceTree = "<group>"; };
|
||||
E4F6B466221E233200ACF42A /* AlbumDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumDataSource.swift; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
@ -258,11 +245,9 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E45E4FE122516953004B537F /* CryptoSwift.framework in Frameworks */,
|
||||
E41B22C021FB6BBA00D544F6 /* libmpdclient.2.dylib in Frameworks */,
|
||||
E421ACA3221F73C4008B2449 /* MediaKeyTap.framework in Frameworks */,
|
||||
E450AD8622262AE60091BED3 /* SwiftyJSON.framework in Frameworks */,
|
||||
E45E4FE0225168DA004B537F /* CryptoSwift.framework in Frameworks */,
|
||||
E450ADA32229E7E00091BED3 /* PMKFoundation.framework in Frameworks */,
|
||||
E450AD9222262C970091BED3 /* PromiseKit.framework in Frameworks */,
|
||||
);
|
||||
@ -288,14 +273,11 @@
|
||||
E407860F2110CE6E006887B1 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E41B22BE21FB6B3300D544F6 /* Frameworks */,
|
||||
E407861A2110CE6E006887B1 /* Persephone */,
|
||||
E407862D2110CE70006887B1 /* PersephoneTests */,
|
||||
E40786382110CE70006887B1 /* PersephoneUITests */,
|
||||
E40786192110CE6E006887B1 /* Products */,
|
||||
E45E4FD822515D87004B537F /* Brewfile */,
|
||||
E45E4FD922515D87004B537F /* Cartfile */,
|
||||
E45E4FD722515D87004B537F /* CHANGELOG.md */,
|
||||
E41B22BE21FB6B3300D544F6 /* Frameworks */,
|
||||
E42A8F3922176D6400A13ED9 /* LICENSE.md */,
|
||||
E42A8F3A22176D6400A13ED9 /* README.md */,
|
||||
);
|
||||
@ -314,23 +296,23 @@
|
||||
E407861A2110CE6E006887B1 /* Persephone */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E407861B2110CE6E006887B1 /* AppDelegate.swift */,
|
||||
E407861F2110CE70006887B1 /* Assets.xcassets */,
|
||||
E4D1B597220BA3A20026F233 /* Controllers */,
|
||||
E4F6B45E221E117600ACF42A /* DataSources */,
|
||||
E408D3B7220DE8CC0006D9BE /* Extensions */,
|
||||
E41B22C721FB966C00D544F6 /* include */,
|
||||
E40786242110CE70006887B1 /* Info.plist */,
|
||||
E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */,
|
||||
E450AD8922262B420091BED3 /* Operations */,
|
||||
E4A83BF2222207BE0098FED6 /* Services */,
|
||||
E4A83BEC2221F5DD0098FED6 /* Preferences */,
|
||||
E47E2FE32220AA0700F747E6 /* Layouts */,
|
||||
E4F6B461221E124700ACF42A /* Models */,
|
||||
E4A642DB220912FA00067D21 /* MPDClient */,
|
||||
E450AD8922262B420091BED3 /* Operations */,
|
||||
E40786252110CE70006887B1 /* Persephone.entitlements */,
|
||||
E450AD9E2229B9BC0091BED3 /* PersephoneBridgingHeader.h */,
|
||||
E4A83BEC2221F5DD0098FED6 /* Preferences */,
|
||||
E4F6B45E221E117600ACF42A /* DataSources */,
|
||||
E407861F2110CE70006887B1 /* Assets.xcassets */,
|
||||
E408D3B7220DE8CC0006D9BE /* Extensions */,
|
||||
E4D1B598220BA3C90026F233 /* Resources */,
|
||||
E4A83BF2222207BE0098FED6 /* Services */,
|
||||
E4D1B597220BA3A20026F233 /* Controllers */,
|
||||
E408D3C3220E138B0006D9BE /* Views */,
|
||||
E41B22C721FB966C00D544F6 /* include */,
|
||||
E407861B2110CE6E006887B1 /* AppDelegate.swift */,
|
||||
E40786242110CE70006887B1 /* Info.plist */,
|
||||
E40786252110CE70006887B1 /* Persephone.entitlements */,
|
||||
E4A642DB220912FA00067D21 /* MPDClient */,
|
||||
);
|
||||
path = Persephone;
|
||||
sourceTree = "<group>";
|
||||
@ -358,10 +340,11 @@
|
||||
children = (
|
||||
E4928E0A2218D62A001D4BEA /* CGColor.swift */,
|
||||
E408D3B5220DD8970006D9BE /* Notification.swift */,
|
||||
E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */,
|
||||
E40FE71A221B904300A4223F /* NSEvent.swift */,
|
||||
E435E3E1221CD4E200184CFC /* NSFont.swift */,
|
||||
E435E3E3221CD75D00184CFC /* NSImage.swift */,
|
||||
E408D3B8220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift */,
|
||||
E450AD9C2229B9050091BED3 /* String.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@ -369,15 +352,15 @@
|
||||
E408D3BC220E03D20006D9BE /* Extensions */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E41E530A223C033700173814 /* MPDClient+Album.swift */,
|
||||
E41E5308223C020400173814 /* MPDClient+Command.swift */,
|
||||
E41E52FC223BF87300173814 /* MPDClient+Connection.swift */,
|
||||
E42410B52241B956005ED6DF /* MPDClient+Database.swift */,
|
||||
E41E5304223BFB0700173814 /* MPDClient+Error.swift */,
|
||||
E41E5302223BF9C300173814 /* MPDClient+Idle.swift */,
|
||||
E41E5300223BF99300173814 /* MPDClient+Queue.swift */,
|
||||
E41E5306223C019100173814 /* MPDClient+Status.swift */,
|
||||
E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */,
|
||||
E41E5300223BF99300173814 /* MPDClient+Queue.swift */,
|
||||
E41E5302223BF9C300173814 /* MPDClient+Idle.swift */,
|
||||
E41E5304223BFB0700173814 /* MPDClient+Error.swift */,
|
||||
E41E5306223C019100173814 /* MPDClient+Status.swift */,
|
||||
E41E5308223C020400173814 /* MPDClient+Command.swift */,
|
||||
E41E530A223C033700173814 /* MPDClient+Album.swift */,
|
||||
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
@ -395,8 +378,6 @@
|
||||
E41B22BE21FB6B3300D544F6 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E45E4FDE225168DA004B537F /* CryptoSwift.framework */,
|
||||
E45E4FDD225168DA004B537F /* CryptoSwift.framework.dSYM */,
|
||||
E450ADA02229E7C90091BED3 /* PMKFoundation.framework */,
|
||||
E450AD9F2229E7C90091BED3 /* PMKFoundation.framework.dSYM */,
|
||||
E450AD97222633920091BED3 /* Alamofire.framework */,
|
||||
@ -467,8 +448,8 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E41E530D223EF4CF00173814 /* AlbumArtService+Caching.swift */,
|
||||
E41E5311223EF74A00173814 /* AlbumArtService+Filesystem.swift */,
|
||||
E41E530F223EF6CE00173814 /* AlbumArtService+Remote.swift */,
|
||||
E41E5311223EF74A00173814 /* AlbumArtService+Filesystem.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@ -492,10 +473,10 @@
|
||||
E4A642DB220912FA00067D21 /* MPDClient */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E408D3BC220E03D20006D9BE /* Extensions */,
|
||||
E4D1B594220BA2490026F233 /* Models */,
|
||||
E41B22C521FB932700D544F6 /* MPDClient.swift */,
|
||||
E408D3BC220E03D20006D9BE /* Extensions */,
|
||||
E4D1B595220BA27C0026F233 /* Protocols */,
|
||||
E4D1B594220BA2490026F233 /* Models */,
|
||||
);
|
||||
path = MPDClient;
|
||||
sourceTree = "<group>";
|
||||
@ -511,8 +492,8 @@
|
||||
E4A83BED2221F5E60098FED6 /* Controllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.swift */,
|
||||
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */,
|
||||
E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.swift */,
|
||||
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */,
|
||||
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */,
|
||||
);
|
||||
@ -522,8 +503,8 @@
|
||||
E4A83BF2222207BE0098FED6 /* Services */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E4A83BF3222207D50098FED6 /* AlbumArtService.swift */,
|
||||
E41E530C223EF4BA00173814 /* Extensions */,
|
||||
E4A83BF3222207D50098FED6 /* AlbumArtService.swift */,
|
||||
);
|
||||
path = Services;
|
||||
sourceTree = "<group>";
|
||||
@ -531,12 +512,12 @@
|
||||
E4D1B594220BA2490026F233 /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E4EB237A220F7CF1008C70C0 /* MPDAlbum.swift */,
|
||||
E45962C52241A78500FC1A1E /* MPDCommand.swift */,
|
||||
E4C8B53D22349002009A20F3 /* MPDIdle.swift */,
|
||||
E4EB2378220F10B8008C70C0 /* MPDPair.swift */,
|
||||
E4E8CC9922075D370024217A /* MPDSong.swift */,
|
||||
E4A642D922090CBE00067D21 /* MPDStatus.swift */,
|
||||
E4E8CC9922075D370024217A /* Song.swift */,
|
||||
E4A642D922090CBE00067D21 /* Status.swift */,
|
||||
E4EB2378220F10B8008C70C0 /* Pair.swift */,
|
||||
E4EB237A220F7CF1008C70C0 /* Album.swift */,
|
||||
E4C8B53D22349002009A20F3 /* Idle.swift */,
|
||||
E45962C52241A78500FC1A1E /* Command.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
@ -552,12 +533,12 @@
|
||||
E4D1B597220BA3A20026F233 /* Controllers */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */,
|
||||
E47E2FD4222071FD00F747E6 /* AlbumViewItem.swift */,
|
||||
E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */,
|
||||
E408D3C1220E134F0006D9BE /* AlbumViewController.swift */,
|
||||
E4E8CC932206097F0024217A /* NotificationsController.swift */,
|
||||
E4E8CC912204F4B80024217A /* QueueViewController.swift */,
|
||||
E465049921E94DF500A70F4C /* WindowController.swift */,
|
||||
E47E2FD022205C4600F747E6 /* MainSplitViewController.swift */,
|
||||
);
|
||||
path = Controllers;
|
||||
sourceTree = "<group>";
|
||||
@ -574,8 +555,8 @@
|
||||
E4F6B45E221E117600ACF42A /* DataSources */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E4F6B466221E233200ACF42A /* AlbumDataSource.swift */,
|
||||
E4F6B45F221E119B00ACF42A /* QueueDataSource.swift */,
|
||||
E4F6B466221E233200ACF42A /* AlbumDataSource.swift */,
|
||||
);
|
||||
path = DataSources;
|
||||
sourceTree = "<group>";
|
||||
@ -583,11 +564,10 @@
|
||||
E4F6B461221E124700ACF42A /* Models */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
E450AD7D222620A10091BED3 /* Album.swift */,
|
||||
E40F41F2221EDE27004B6CB8 /* Preferences.swift */,
|
||||
E4F6B462221E125900ACF42A /* QueueItem.swift */,
|
||||
E419E2862249B96600216A8C /* Song.swift */,
|
||||
E47E2FDC2220A6D100F747E6 /* Time.swift */,
|
||||
E40F41F2221EDE27004B6CB8 /* Preferences.swift */,
|
||||
E4F6B462221E125900ACF42A /* SongItem.swift */,
|
||||
E450AD7D222620A10091BED3 /* AlbumItem.swift */,
|
||||
);
|
||||
path = Models;
|
||||
sourceTree = "<group>";
|
||||
@ -663,7 +643,7 @@
|
||||
TargetAttributes = {
|
||||
E40786172110CE6E006887B1 = {
|
||||
CreatedOnToolsVersion = 9.4.1;
|
||||
LastSwiftMigration = 1020;
|
||||
LastSwiftMigration = 1010;
|
||||
SystemCapabilities = {
|
||||
com.apple.Sandbox = {
|
||||
enabled = 0;
|
||||
@ -672,12 +652,12 @@
|
||||
};
|
||||
E40786292110CE70006887B1 = {
|
||||
CreatedOnToolsVersion = 9.4.1;
|
||||
LastSwiftMigration = 1020;
|
||||
LastSwiftMigration = 1010;
|
||||
TestTargetID = E40786172110CE6E006887B1;
|
||||
};
|
||||
E40786342110CE70006887B1 = {
|
||||
CreatedOnToolsVersion = 9.4.1;
|
||||
LastSwiftMigration = 1020;
|
||||
LastSwiftMigration = 1010;
|
||||
TestTargetID = E40786172110CE6E006887B1;
|
||||
};
|
||||
};
|
||||
@ -707,14 +687,10 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
E45E4FDB22515D87004B537F /* Brewfile in Resources */,
|
||||
E450AD9122262C780091BED3 /* SwiftyJSON.framework.dSYM in Resources */,
|
||||
E42A8F3B22176D6400A13ED9 /* LICENSE.md in Resources */,
|
||||
E45E4FDA22515D87004B537F /* CHANGELOG.md in Resources */,
|
||||
E45E4FDC22515D87004B537F /* Cartfile in Resources */,
|
||||
E450AD98222633920091BED3 /* Alamofire.framework.dSYM in Resources */,
|
||||
E40786202110CE70006887B1 /* Assets.xcassets in Resources */,
|
||||
E45E4FDF225168DA004B537F /* CryptoSwift.framework.dSYM in Resources */,
|
||||
E42A8F3C22176D6400A13ED9 /* README.md in Resources */,
|
||||
E450AD8F22262C620091BED3 /* PromiseKit.framework.dSYM in Resources */,
|
||||
E408D3CB220E341D0006D9BE /* AlbumViewItem.xib in Resources */,
|
||||
@ -770,37 +746,36 @@
|
||||
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */,
|
||||
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */,
|
||||
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */,
|
||||
E4C8B53E22349002009A20F3 /* MPDIdle.swift in Sources */,
|
||||
E4C8B53E22349002009A20F3 /* Idle.swift in Sources */,
|
||||
E4F6B460221E119B00ACF42A /* QueueDataSource.swift in Sources */,
|
||||
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */,
|
||||
E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */,
|
||||
E41E5310223EF6CE00173814 /* AlbumArtService+Remote.swift in Sources */,
|
||||
E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */,
|
||||
E42410B62241B956005ED6DF /* MPDClient+Database.swift in Sources */,
|
||||
E4A642DA22090CBE00067D21 /* MPDStatus.swift in Sources */,
|
||||
E4A642DA22090CBE00067D21 /* Status.swift in Sources */,
|
||||
E47E2FD72220720300F747E6 /* AlbumItemView.swift in Sources */,
|
||||
E450AD9522262DF10091BED3 /* AlbumArtQueue.swift in Sources */,
|
||||
E41E52FD223BF87300173814 /* MPDClient+Connection.swift in Sources */,
|
||||
E450AD7E222620A10091BED3 /* Album.swift in Sources */,
|
||||
E450AD7E222620A10091BED3 /* AlbumItem.swift in Sources */,
|
||||
E4E8CC942206097F0024217A /* NotificationsController.swift in Sources */,
|
||||
E408D3B6220DD8970006D9BE /* Notification.swift in Sources */,
|
||||
E45962C62241A78500FC1A1E /* MPDCommand.swift in Sources */,
|
||||
E45962C62241A78500FC1A1E /* Command.swift in Sources */,
|
||||
E408D3B9220DE98F0006D9BE /* NSUserInterfaceItemIdentifier.swift in Sources */,
|
||||
E4EB2379220F10B8008C70C0 /* MPDPair.swift in Sources */,
|
||||
E4F6B463221E125900ACF42A /* QueueItem.swift in Sources */,
|
||||
E4EB2379220F10B8008C70C0 /* Pair.swift in Sources */,
|
||||
E4F6B463221E125900ACF42A /* SongItem.swift in Sources */,
|
||||
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */,
|
||||
E465049A21E94DF500A70F4C /* WindowController.swift in Sources */,
|
||||
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */,
|
||||
E40F41F3221EDE27004B6CB8 /* Preferences.swift in Sources */,
|
||||
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */,
|
||||
E419E2872249B96600216A8C /* Song.swift in Sources */,
|
||||
E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */,
|
||||
E41E5309223C020400173814 /* MPDClient+Command.swift in Sources */,
|
||||
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */,
|
||||
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */,
|
||||
E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */,
|
||||
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */,
|
||||
E4E8CC9A22075D370024217A /* MPDSong.swift in Sources */,
|
||||
E4E8CC9A22075D370024217A /* Song.swift in Sources */,
|
||||
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */,
|
||||
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */,
|
||||
E4A83BF4222207D50098FED6 /* AlbumArtService.swift in Sources */,
|
||||
@ -810,7 +785,8 @@
|
||||
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */,
|
||||
E41E5312223EF74A00173814 /* AlbumArtService+Filesystem.swift in Sources */,
|
||||
E41E5301223BF99300173814 /* MPDClient+Queue.swift in Sources */,
|
||||
E4EB237B220F7CF1008C70C0 /* MPDAlbum.swift in Sources */,
|
||||
E4EB237B220F7CF1008C70C0 /* Album.swift in Sources */,
|
||||
E450AD9D2229B9050091BED3 /* String.swift in Sources */,
|
||||
E41E5303223BF9C300173814 /* MPDClient+Idle.swift in Sources */,
|
||||
E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */,
|
||||
E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */,
|
||||
@ -1001,7 +977,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.Persephone;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
SYSTEM_HEADER_SEARCH_PATHS = Persephone/include;
|
||||
USER_HEADER_SEARCH_PATHS = libmpdclient/output;
|
||||
};
|
||||
@ -1032,7 +1008,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.Persephone;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
SYSTEM_HEADER_SEARCH_PATHS = Persephone/include;
|
||||
USER_HEADER_SEARCH_PATHS = libmpdclient/output;
|
||||
};
|
||||
@ -1060,7 +1036,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.PersephoneTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
SYSTEM_HEADER_SEARCH_PATHS = Persephone/include;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Persephone.app/Contents/MacOS/Persephone";
|
||||
USER_HEADER_SEARCH_PATHS = libmpdclient/output;
|
||||
@ -1089,7 +1065,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.PersephoneTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
SYSTEM_HEADER_SEARCH_PATHS = Persephone/include;
|
||||
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Persephone.app/Contents/MacOS/Persephone";
|
||||
USER_HEADER_SEARCH_PATHS = libmpdclient/output;
|
||||
@ -1113,7 +1089,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.PersephoneUITests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
TEST_TARGET_NAME = Persephone;
|
||||
};
|
||||
name = Debug;
|
||||
@ -1135,7 +1111,7 @@
|
||||
PRODUCT_BUNDLE_IDENTIFIER = me.danbarber.PersephoneUITests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
SWIFT_VERSION = 5.0;
|
||||
SWIFT_VERSION = 4.2;
|
||||
TEST_TARGET_NAME = Persephone;
|
||||
};
|
||||
name = Release;
|
||||
|
||||
@ -30,7 +30,6 @@
|
||||
},
|
||||
"properties" : {
|
||||
"template-rendering-intent" : "original",
|
||||
"preserves-vector-representation" : true,
|
||||
"auto-scaling" : "auto"
|
||||
"preserves-vector-representation" : true
|
||||
}
|
||||
}
|
||||
@ -40,16 +40,11 @@ class AlbumViewController: NSViewController,
|
||||
albumCollectionView.dataSource = dataSource
|
||||
|
||||
preferences.addObserver(self, forKeyPath: "mpdLibraryDir")
|
||||
preferences.addObserver(self, forKeyPath: "fetchMissingArtworkFromInternet")
|
||||
}
|
||||
|
||||
override func viewWillLayout() {
|
||||
super.viewWillLayout()
|
||||
|
||||
if let layout = albumCollectionView.collectionViewLayout as? AlbumViewLayout {
|
||||
layout.saveScrollPosition()
|
||||
}
|
||||
|
||||
albumCollectionView.collectionViewLayout?.invalidateLayout()
|
||||
}
|
||||
|
||||
@ -71,19 +66,16 @@ class AlbumViewController: NSViewController,
|
||||
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]
|
||||
guard let albums = notification.userInfo?[Notification.albumsKey] as? [MPDClient.Album]
|
||||
else { return }
|
||||
|
||||
dataSource.albums = albums.map { Album(mpdAlbum: $0) }
|
||||
dataSource.albums = albums.map { AlbumItem(album: $0, coverArt: nil) }
|
||||
albumCollectionView.reloadData()
|
||||
}
|
||||
|
||||
|
||||
@ -10,7 +10,7 @@ import Cocoa
|
||||
|
||||
class AlbumViewItem: NSCollectionViewItem {
|
||||
var observer: NSKeyValueObservation?
|
||||
var album: Album?
|
||||
var album: AlbumItem?
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
@ -27,7 +27,7 @@ class AlbumViewItem: NSCollectionViewItem {
|
||||
}
|
||||
}
|
||||
|
||||
func setAlbum(_ album: Album) {
|
||||
func setAlbum(_ album: AlbumItem) {
|
||||
self.album = album
|
||||
albumTitle.stringValue = album.title
|
||||
albumArtist.stringValue = album.artist
|
||||
@ -53,7 +53,7 @@ class AlbumViewItem: NSCollectionViewItem {
|
||||
@IBAction func playAlbum(_ sender: Any) {
|
||||
guard let album = album else { return }
|
||||
|
||||
AppDelegate.mpdClient.playAlbum(album.mpdAlbum)
|
||||
AppDelegate.mpdClient.playAlbum(album.album)
|
||||
}
|
||||
|
||||
@IBOutlet var albumCoverView: NSImageView!
|
||||
|
||||
@ -19,7 +19,7 @@ class NotificationsController: MPDClientDelegate {
|
||||
sendNotification(name: Notification.willDisconnect)
|
||||
}
|
||||
|
||||
func didUpdateState(mpdClient: MPDClient, state: MPDClient.MPDStatus.State) {
|
||||
func didUpdateState(mpdClient: MPDClient, state: MPDClient.Status.State) {
|
||||
sendNotification(
|
||||
name: Notification.stateChanged,
|
||||
userInfo: [Notification.stateKey: state]
|
||||
@ -44,7 +44,7 @@ class NotificationsController: MPDClientDelegate {
|
||||
sendNotification(name: Notification.databaseUpdateFinished)
|
||||
}
|
||||
|
||||
func didUpdateQueue(mpdClient: MPDClient, queue: [MPDClient.MPDSong]) {
|
||||
func didUpdateQueue(mpdClient: MPDClient, queue: [MPDClient.Song]) {
|
||||
sendNotification(
|
||||
name: Notification.queueChanged,
|
||||
userInfo: [Notification.queueKey: queue]
|
||||
@ -58,7 +58,7 @@ class NotificationsController: MPDClientDelegate {
|
||||
)
|
||||
}
|
||||
|
||||
func didLoadAlbums(mpdClient: MPDClient, albums: [MPDClient.MPDAlbum]) {
|
||||
func didLoadAlbums(mpdClient: MPDClient, albums: [MPDClient.Album]) {
|
||||
sendNotification(
|
||||
name: Notification.loadedAlbums,
|
||||
userInfo: [Notification.albumsKey: albums]
|
||||
|
||||
@ -7,14 +7,12 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import PromiseKit
|
||||
|
||||
class QueueViewController: NSViewController,
|
||||
NSOutlineViewDelegate {
|
||||
var dataSource = QueueDataSource()
|
||||
|
||||
@IBOutlet var queueView: NSOutlineView!
|
||||
@IBOutlet var queueAlbumArtImage: NSImageView!
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
@ -43,14 +41,14 @@ class QueueViewController: NSViewController,
|
||||
}
|
||||
|
||||
@objc func stateChanged(_ notification: Notification) {
|
||||
guard let state = notification.userInfo?[Notification.stateKey] as? MPDClient.MPDStatus.State
|
||||
guard let state = notification.userInfo?[Notification.stateKey] as? MPDClient.Status.State
|
||||
else { return }
|
||||
|
||||
dataSource.setQueueIcon(state)
|
||||
}
|
||||
|
||||
@objc func queueChanged(_ notification: Notification) {
|
||||
guard let queue = notification.userInfo?[Notification.queueKey] as? [MPDClient.MPDSong]
|
||||
guard let queue = notification.userInfo?[Notification.queueKey] as? [MPDClient.Song]
|
||||
else { return }
|
||||
|
||||
dataSource.updateQueue(queue)
|
||||
@ -62,26 +60,8 @@ class QueueViewController: NSViewController,
|
||||
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
|
||||
}
|
||||
}
|
||||
.cauterize()
|
||||
} else {
|
||||
queueAlbumArtImage.image = NSImage.defaultCoverArt
|
||||
}
|
||||
}
|
||||
|
||||
func outlineView(
|
||||
@ -96,12 +76,12 @@ class QueueViewController: NSViewController,
|
||||
}
|
||||
|
||||
func outlineView(_ outlineView: NSOutlineView, viewFor tableColumn: NSTableColumn?, item: Any) -> NSView? {
|
||||
if let queueItem = item as? QueueItem {
|
||||
if let songItem = item as? SongItem {
|
||||
switch tableColumn?.identifier.rawValue {
|
||||
case "songTitleColumn":
|
||||
return cellForSongTitle(outlineView, with: queueItem)
|
||||
return cellForSongTitle(outlineView, with: songItem)
|
||||
case "songArtistColumn":
|
||||
return cellForSongArtist(outlineView, with: queueItem)
|
||||
return cellForSongArtist(outlineView, with: songItem)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
@ -112,14 +92,14 @@ class QueueViewController: NSViewController,
|
||||
}
|
||||
}
|
||||
|
||||
func cellForSongTitle(_ outlineView: NSOutlineView, with queueItem: QueueItem) -> NSView {
|
||||
func cellForSongTitle(_ outlineView: NSOutlineView, with songItem: SongItem) -> NSView {
|
||||
let cellView = outlineView.makeView(
|
||||
withIdentifier: .queueSongTitle,
|
||||
owner: self
|
||||
) as! NSTableCellView
|
||||
|
||||
cellView.textField?.stringValue = queueItem.song.title
|
||||
if queueItem.isPlaying {
|
||||
cellView.textField?.stringValue = songItem.song.getTag(.title)
|
||||
if songItem.isPlaying {
|
||||
cellView.textField?.font = .systemFontBold
|
||||
cellView.imageView?.image = dataSource.queueIcon
|
||||
} else {
|
||||
@ -130,14 +110,14 @@ func cellForSongTitle(_ outlineView: NSOutlineView, with queueItem: QueueItem) -
|
||||
return cellView
|
||||
}
|
||||
|
||||
func cellForSongArtist(_ outlineView: NSOutlineView, with queueItem: QueueItem) -> NSView {
|
||||
func cellForSongArtist(_ outlineView: NSOutlineView, with songItem: SongItem) -> NSView {
|
||||
let cellView = outlineView.makeView(
|
||||
withIdentifier: .queueSongArtist,
|
||||
owner: self
|
||||
) as! NSTableCellView
|
||||
|
||||
cellView.textField?.stringValue = queueItem.song.artist
|
||||
if queueItem.isPlaying {
|
||||
cellView.textField?.stringValue = songItem.song.getTag(.artist)
|
||||
if songItem.isPlaying {
|
||||
cellView.textField?.font = .systemFontBold
|
||||
} else {
|
||||
cellView.textField?.font = .systemFontRegular
|
||||
|
||||
@ -13,7 +13,7 @@ class WindowController: NSWindowController {
|
||||
case prevTrack, playPause, stop, nextTrack
|
||||
}
|
||||
|
||||
var state: MPDClient.MPDStatus.State?
|
||||
var state: MPDClient.Status.State?
|
||||
var totalTime: UInt?
|
||||
var elapsedTimeMs: UInt?
|
||||
var trackTimer: Timer?
|
||||
@ -64,7 +64,7 @@ class WindowController: NSWindowController {
|
||||
}
|
||||
|
||||
@objc func stateChanged(_ notification: Notification) {
|
||||
guard let state = notification.userInfo?[Notification.stateKey] as? MPDClient.MPDStatus.State
|
||||
guard let state = notification.userInfo?[Notification.stateKey] as? MPDClient.Status.State
|
||||
else { return }
|
||||
|
||||
self.state = state
|
||||
|
||||
@ -7,23 +7,14 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import PromiseKit
|
||||
|
||||
class AlbumDataSource: NSObject, NSCollectionViewDataSource {
|
||||
var albums: [Album] = []
|
||||
var albums: [AlbumItem] = []
|
||||
|
||||
func collectionView(_ collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int {
|
||||
return albums.count
|
||||
}
|
||||
|
||||
func resetCoverArt() {
|
||||
albums = albums.map {
|
||||
var album = $0
|
||||
album.coverArtFetched = false
|
||||
return album
|
||||
}
|
||||
}
|
||||
|
||||
func collectionView(_ collectionView: NSCollectionView, itemForRepresentedObjectAt indexPath: IndexPath) -> NSCollectionViewItem {
|
||||
let item = collectionView.makeItem(withIdentifier: .albumViewItem, for: indexPath)
|
||||
guard let albumViewItem = item as? AlbumViewItem else { return item }
|
||||
@ -31,22 +22,13 @@ class AlbumDataSource: NSObject, NSCollectionViewDataSource {
|
||||
albumViewItem.view.wantsLayer = true
|
||||
albumViewItem.setAlbum(albums[indexPath.item])
|
||||
|
||||
if albums[indexPath.item].coverArt == nil &&
|
||||
!albums[indexPath.item].coverArtFetched {
|
||||
if albums[indexPath.item].coverArt == nil {
|
||||
AlbumArtService(album: albums[indexPath.item]).fetchAlbumArt { image in
|
||||
self.albums[indexPath.item].coverArt = image
|
||||
|
||||
AppDelegate.mpdClient.getAlbumFirstSong(for: albums[indexPath.item].mpdAlbum) {
|
||||
guard let song = $0 else { return }
|
||||
|
||||
AlbumArtService(song: Song(mpdSong: song))
|
||||
.fetchAlbumArt()
|
||||
.done { image in
|
||||
self.albums[indexPath.item].coverArt = image
|
||||
self.albums[indexPath.item].coverArtFetched = true
|
||||
|
||||
DispatchQueue.main.async {
|
||||
collectionView.reloadItems(at: [indexPath])
|
||||
}
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
collectionView.reloadItems(at: [indexPath])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,21 +9,16 @@
|
||||
import Cocoa
|
||||
|
||||
class QueueDataSource: NSObject, NSOutlineViewDataSource {
|
||||
var queue: [QueueItem] = []
|
||||
var queue: [SongItem] = []
|
||||
var queuePos: Int = -1
|
||||
|
||||
var queueIcon: NSImage? = nil
|
||||
|
||||
func updateQueue(_ queue: [MPDClient.MPDSong]) {
|
||||
func updateQueue(_ queue: [MPDClient.Song]) {
|
||||
queuePos = -1
|
||||
|
||||
self.queue = queue.enumerated().map { index, mpdSong in
|
||||
let song = Song(mpdSong: mpdSong)
|
||||
return QueueItem(
|
||||
song: song,
|
||||
queuePos: index,
|
||||
isPlaying: index == queuePos
|
||||
)
|
||||
self.queue = queue.enumerated().map { index, song in
|
||||
SongItem(song: song, queuePos: index, isPlaying: index == queuePos)
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,7 +35,7 @@ class QueueDataSource: NSObject, NSOutlineViewDataSource {
|
||||
}
|
||||
}
|
||||
|
||||
func setQueueIcon(_ state: MPDClient.MPDStatus.State) {
|
||||
func setQueueIcon(_ state: MPDClient.Status.State) {
|
||||
switch state {
|
||||
case .playing:
|
||||
queueIcon = .playIcon
|
||||
|
||||
24
Persephone/Extensions/String.swift
Normal file
24
Persephone/Extensions/String.swift
Normal file
@ -0,0 +1,24 @@
|
||||
//
|
||||
// String.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Daniel Barber on 2019/3/01.
|
||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension String {
|
||||
func sha1() -> String {
|
||||
let data = self.data(using: String.Encoding.utf8)!
|
||||
var digest = [UInt8](repeating: 0, count:Int(CC_SHA1_DIGEST_LENGTH))
|
||||
|
||||
data.withUnsafeBytes {
|
||||
_ = CC_SHA1($0, CC_LONG(data.count), &digest)
|
||||
}
|
||||
|
||||
let hexBytes = digest.map { String(format: "%02hhx", $0) }
|
||||
|
||||
return hexBytes.joined()
|
||||
}
|
||||
}
|
||||
@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>0.11.1-alpha</string>
|
||||
<string>0.10.3a</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSApplicationCategoryType</key>
|
||||
|
||||
@ -36,6 +36,10 @@ class AlbumViewLayout: NSCollectionViewFlowLayout {
|
||||
var divider: CGFloat = 1
|
||||
var itemWidth: CGFloat = 0
|
||||
|
||||
if let scrollView = collectionView.enclosingScrollView {
|
||||
scrollPosition = scrollView.documentVisibleRect.minY / collectionView.bounds.height
|
||||
}
|
||||
|
||||
repeat {
|
||||
let totalPaddingWidth = sectionInset.left + sectionInset.right
|
||||
let totalGutterWidth = (divider - 1) * (minimumInteritemSpacing)
|
||||
@ -48,15 +52,6 @@ class AlbumViewLayout: NSCollectionViewFlowLayout {
|
||||
itemSize = NSSize(width: itemWidth, height: itemHeight)
|
||||
}
|
||||
|
||||
func saveScrollPosition() {
|
||||
guard let collectionView = collectionView
|
||||
else { return }
|
||||
|
||||
if let scrollView = collectionView.enclosingScrollView {
|
||||
scrollPosition = scrollView.documentVisibleRect.minY / collectionView.bounds.height
|
||||
}
|
||||
}
|
||||
|
||||
func setScrollPosition() {
|
||||
guard let collectionView = collectionView
|
||||
else { return }
|
||||
|
||||
@ -14,28 +14,28 @@ extension MPDClient {
|
||||
enqueueCommand(command: .fetchAllAlbums)
|
||||
}
|
||||
|
||||
func playAlbum(_ album: MPDAlbum) {
|
||||
func playAlbum(_ album: Album) {
|
||||
enqueueCommand(command: .playAlbum, userData: ["album": album])
|
||||
}
|
||||
|
||||
func getAlbumFirstSong(for album: MPDAlbum, callback: @escaping (MPDSong?) -> Void) {
|
||||
func getAlbumURI(for album: Album, callback: @escaping (String?) -> Void) {
|
||||
enqueueCommand(
|
||||
command: .getAlbumFirstSong,
|
||||
command: .getAlbumURI,
|
||||
priority: .low,
|
||||
userData: ["album": album, "callback": callback]
|
||||
)
|
||||
}
|
||||
|
||||
func sendPlayAlbum(_ album: MPDAlbum) {
|
||||
var songs: [MPDSong] = []
|
||||
func sendPlayAlbum(_ album: Album) {
|
||||
var songs: [Song] = []
|
||||
|
||||
mpd_run_clear(self.connection)
|
||||
mpd_search_db_songs(self.connection, true)
|
||||
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, album.title)
|
||||
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM_ARTIST, album.artist)
|
||||
mpd_search_commit(self.connection)
|
||||
while let song = mpd_recv_song(self.connection) {
|
||||
songs.append(MPDSong(song))
|
||||
while let mpdSong = mpd_recv_song(self.connection) {
|
||||
songs.append(Song(mpdSong))
|
||||
}
|
||||
for song in songs {
|
||||
mpd_run_add(self.connection, song.uri)
|
||||
@ -44,35 +44,35 @@ extension MPDClient {
|
||||
}
|
||||
|
||||
func allAlbums() {
|
||||
var albums: [MPDAlbum] = []
|
||||
var albums: [Album] = []
|
||||
var artist: String = ""
|
||||
|
||||
mpd_search_db_tags(self.connection, MPD_TAG_ALBUM)
|
||||
mpd_search_add_group_tag(self.connection, MPD_TAG_ALBUM_ARTIST)
|
||||
mpd_search_commit(self.connection)
|
||||
|
||||
while let pair = mpd_recv_pair(self.connection) {
|
||||
let pair = MPDPair(pair)
|
||||
while let mpdPair = mpd_recv_pair(self.connection) {
|
||||
let pair = Pair(mpdPair)
|
||||
|
||||
switch pair.name {
|
||||
case "AlbumArtist":
|
||||
artist = pair.value
|
||||
case "Album":
|
||||
albums.append(MPDAlbum(title: pair.value, artist: artist))
|
||||
albums.append(Album(title: pair.value, artist: artist))
|
||||
default:
|
||||
break
|
||||
}
|
||||
|
||||
mpd_return_pair(self.connection, pair.pair)
|
||||
mpd_return_pair(self.connection, pair.mpdPair)
|
||||
}
|
||||
|
||||
self.delegate?.didLoadAlbums(mpdClient: self, albums: albums)
|
||||
}
|
||||
|
||||
func albumFirstSong(for album: MPDAlbum, callback: (MPDSong?) -> Void) {
|
||||
guard isConnected else { return }
|
||||
func albumURI(for album: Album, callback: (String?) -> Void) {
|
||||
var songURI: String?
|
||||
|
||||
var firstSong: MPDSong?
|
||||
guard isConnected else { return }
|
||||
|
||||
mpd_search_db_songs(self.connection, true)
|
||||
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, album.title)
|
||||
@ -81,14 +81,19 @@ extension MPDClient {
|
||||
|
||||
mpd_search_commit(self.connection)
|
||||
|
||||
while let song = mpd_recv_song(self.connection) {
|
||||
let song = MPDSong(song)
|
||||
while let mpdSong = mpd_recv_song(self.connection) {
|
||||
let song = Song(mpdSong)
|
||||
|
||||
if firstSong == nil {
|
||||
firstSong = song
|
||||
if songURI == nil {
|
||||
songURI = song.uriString
|
||||
}
|
||||
}
|
||||
|
||||
callback(firstSong)
|
||||
callback(
|
||||
songURI?
|
||||
.split(separator: "/")
|
||||
.dropLast()
|
||||
.joined(separator: "/")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import Foundation
|
||||
|
||||
extension MPDClient {
|
||||
func sendCommand(
|
||||
command: MPDCommand,
|
||||
command: Command,
|
||||
userData: Dictionary<String, Any> = [:]
|
||||
) {
|
||||
switch command {
|
||||
@ -49,19 +49,18 @@ extension MPDClient {
|
||||
case .fetchAllAlbums:
|
||||
allAlbums()
|
||||
case .playAlbum:
|
||||
guard let album = userData["album"] as? MPDAlbum else { return }
|
||||
guard let album = userData["album"] as? Album else { return }
|
||||
sendPlayAlbum(album)
|
||||
case .getAlbumFirstSong:
|
||||
guard let album = userData["album"] as? MPDAlbum,
|
||||
let callback = userData["callback"] as? (MPDSong?) -> Void
|
||||
case .getAlbumURI:
|
||||
guard let album = userData["album"] as? Album,
|
||||
let callback = userData["callback"] as? (String?) -> Void
|
||||
else { return }
|
||||
|
||||
albumFirstSong(for: album, callback: callback)
|
||||
albumURI(for: album, callback: callback)
|
||||
}
|
||||
}
|
||||
|
||||
func enqueueCommand(
|
||||
command: MPDCommand,
|
||||
command: Command,
|
||||
priority: BlockOperation.QueuePriority = .normal,
|
||||
userData: Dictionary<String, Any> = [:]
|
||||
) {
|
||||
|
||||
@ -22,7 +22,7 @@ extension MPDClient {
|
||||
else { return }
|
||||
|
||||
self.connection = connection
|
||||
self.status = MPDStatus(status)
|
||||
self.status = Status(status)
|
||||
|
||||
self.fetchQueue()
|
||||
self.fetchAllAlbums()
|
||||
|
||||
@ -30,7 +30,7 @@ extension MPDClient {
|
||||
func handleIdleResult(_ result: mpd_idle) {
|
||||
isIdle = false
|
||||
|
||||
let mpdIdle = MPDIdle(rawValue: result.rawValue)
|
||||
let mpdIdle = Idle(rawValue: result.rawValue)
|
||||
|
||||
if mpdIdle.contains(.database) {
|
||||
self.fetchAllAlbums()
|
||||
|
||||
@ -27,7 +27,7 @@ extension MPDClient {
|
||||
mpd_send_list_queue_meta(connection)
|
||||
|
||||
while let mpdSong = mpd_recv_song(connection) {
|
||||
let song = MPDSong(mpdSong)
|
||||
let song = Song(mpdSong)
|
||||
self.queue.append(song)
|
||||
}
|
||||
}
|
||||
|
||||
@ -16,6 +16,6 @@ extension MPDClient {
|
||||
|
||||
func sendRunStatus() {
|
||||
guard let status = mpd_run_status(connection) else { return }
|
||||
self.status = MPDStatus(status)
|
||||
self.status = Status(status)
|
||||
}
|
||||
}
|
||||
|
||||
@ -15,8 +15,8 @@ class MPDClient {
|
||||
var connection: OpaquePointer?
|
||||
var isConnected: Bool = false
|
||||
var isIdle: Bool = false
|
||||
var status: MPDStatus?
|
||||
var queue: [MPDSong] = []
|
||||
var status: Status?
|
||||
var queue: [Song] = []
|
||||
|
||||
let commandQueue = OperationQueue()
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@
|
||||
import Foundation
|
||||
|
||||
extension MPDClient {
|
||||
struct MPDAlbum {
|
||||
struct Album {
|
||||
let title: String
|
||||
let artist: String
|
||||
}
|
||||
@ -9,7 +9,7 @@
|
||||
import Foundation
|
||||
|
||||
extension MPDClient {
|
||||
enum MPDCommand {
|
||||
enum Command {
|
||||
// Transport commands
|
||||
case prevTrack
|
||||
case nextTrack
|
||||
@ -30,6 +30,6 @@ extension MPDClient {
|
||||
// Album commands
|
||||
case fetchAllAlbums
|
||||
case playAlbum
|
||||
case getAlbumFirstSong
|
||||
case getAlbumURI
|
||||
}
|
||||
}
|
||||
27
Persephone/MPDClient/Models/Idle.swift
Normal file
27
Persephone/MPDClient/Models/Idle.swift
Normal file
@ -0,0 +1,27 @@
|
||||
//
|
||||
// Idle.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Daniel Barber on 2019/3/09.
|
||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension MPDClient {
|
||||
struct Idle: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
static let database = Idle(rawValue: 0x1)
|
||||
static let storedPlaylist = Idle(rawValue: 0x2)
|
||||
static let queue = Idle(rawValue: 0x4)
|
||||
static let player = Idle(rawValue: 0x8)
|
||||
static let mixer = Idle(rawValue: 0x10)
|
||||
static let output = Idle(rawValue: 0x20)
|
||||
static let options = Idle(rawValue: 0x40)
|
||||
static let update = Idle(rawValue: 0x80)
|
||||
static let sticker = Idle(rawValue: 0x100)
|
||||
static let subscription = Idle(rawValue: 0x200)
|
||||
static let message = Idle(rawValue: 0x400)
|
||||
}
|
||||
}
|
||||
@ -1,27 +0,0 @@
|
||||
//
|
||||
// Idle.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Daniel Barber on 2019/3/09.
|
||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
extension MPDClient {
|
||||
struct MPDIdle: OptionSet {
|
||||
let rawValue: UInt32
|
||||
|
||||
static let database = MPDIdle(rawValue: 0x1)
|
||||
static let storedPlaylist = MPDIdle(rawValue: 0x2)
|
||||
static let queue = MPDIdle(rawValue: 0x4)
|
||||
static let player = MPDIdle(rawValue: 0x8)
|
||||
static let mixer = MPDIdle(rawValue: 0x10)
|
||||
static let output = MPDIdle(rawValue: 0x20)
|
||||
static let options = MPDIdle(rawValue: 0x40)
|
||||
static let update = MPDIdle(rawValue: 0x80)
|
||||
static let sticker = MPDIdle(rawValue: 0x100)
|
||||
static let subscription = MPDIdle(rawValue: 0x200)
|
||||
static let message = MPDIdle(rawValue: 0x400)
|
||||
}
|
||||
}
|
||||
@ -1,28 +0,0 @@
|
||||
//
|
||||
// Pair.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Daniel Barber on 2019/2/09.
|
||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import mpdclient
|
||||
|
||||
extension MPDClient {
|
||||
class MPDPair {
|
||||
let pair: UnsafeMutablePointer<mpd_pair>
|
||||
|
||||
init(_ pair: UnsafeMutablePointer<mpd_pair>) {
|
||||
self.pair = pair
|
||||
}
|
||||
|
||||
var name: String {
|
||||
get { return String(cString: pair.pointee.name) }
|
||||
}
|
||||
|
||||
var value: String {
|
||||
get { return String(cString: pair.pointee.value) }
|
||||
}
|
||||
}
|
||||
}
|
||||
26
Persephone/MPDClient/Models/Pair.swift
Normal file
26
Persephone/MPDClient/Models/Pair.swift
Normal file
@ -0,0 +1,26 @@
|
||||
//
|
||||
// Pair.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Daniel Barber on 2019/2/09.
|
||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import mpdclient
|
||||
|
||||
class Pair {
|
||||
let mpdPair: UnsafeMutablePointer<mpd_pair>
|
||||
|
||||
init(_ mpdPair: UnsafeMutablePointer<mpd_pair>) {
|
||||
self.mpdPair = mpdPair
|
||||
}
|
||||
|
||||
var name: String {
|
||||
get { return String(cString: mpdPair.pointee.name) }
|
||||
}
|
||||
|
||||
var value: String {
|
||||
get { return String(cString: mpdPair.pointee.value) }
|
||||
}
|
||||
}
|
||||
@ -10,8 +10,8 @@ import Foundation
|
||||
import mpdclient
|
||||
|
||||
extension MPDClient {
|
||||
class MPDSong {
|
||||
let song: OpaquePointer
|
||||
class Song {
|
||||
let mpdSong: OpaquePointer
|
||||
|
||||
enum TagType: Int {
|
||||
case unknown = -1
|
||||
@ -33,41 +33,26 @@ extension MPDClient {
|
||||
case tagCount
|
||||
}
|
||||
|
||||
init(_ song: OpaquePointer) {
|
||||
self.song = song
|
||||
init(_ mpdSong: OpaquePointer) {
|
||||
self.mpdSong = mpdSong
|
||||
}
|
||||
|
||||
deinit {
|
||||
mpd_song_free(song)
|
||||
mpd_song_free(mpdSong)
|
||||
}
|
||||
|
||||
var uri: UnsafePointer<Int8> {
|
||||
return mpd_song_get_uri(song)
|
||||
return mpd_song_get_uri(mpdSong)
|
||||
}
|
||||
|
||||
var uriString: String {
|
||||
return String(cString: uri)
|
||||
}
|
||||
|
||||
var album: MPDAlbum {
|
||||
return MPDAlbum(
|
||||
title: getTag(.album),
|
||||
artist: artist
|
||||
)
|
||||
}
|
||||
|
||||
var artist: String {
|
||||
if getTag(.albumArtist) != "" {
|
||||
return getTag(.albumArtist)
|
||||
} else {
|
||||
return getTag(.artist)
|
||||
}
|
||||
}
|
||||
|
||||
func getTag(_ tagType: TagType) -> String {
|
||||
let mpdTagType = mpd_tag_type(rawValue: Int32(tagType.rawValue))
|
||||
|
||||
guard let tag = mpd_song_get_tag(song, mpdTagType, 0)
|
||||
guard let tag = mpd_song_get_tag(mpdSong, mpdTagType, 0)
|
||||
else { return "" }
|
||||
|
||||
return String(cString: tag)
|
||||
@ -10,8 +10,8 @@ import Foundation
|
||||
import mpdclient
|
||||
|
||||
extension MPDClient {
|
||||
class MPDStatus {
|
||||
private let status: OpaquePointer
|
||||
class Status {
|
||||
private let mpdStatus: OpaquePointer
|
||||
|
||||
enum State: UInt {
|
||||
case unknown = 0
|
||||
@ -20,38 +20,38 @@ extension MPDClient {
|
||||
case paused = 3
|
||||
}
|
||||
|
||||
init(_ status: OpaquePointer) {
|
||||
self.status = status
|
||||
init(_ mpdStatus: OpaquePointer) {
|
||||
self.mpdStatus = mpdStatus
|
||||
}
|
||||
|
||||
deinit {
|
||||
mpd_status_free(status)
|
||||
mpd_status_free(mpdStatus)
|
||||
}
|
||||
|
||||
var state: State {
|
||||
let mpdState = mpd_status_get_state(status)
|
||||
let mpdState = mpd_status_get_state(mpdStatus)
|
||||
|
||||
return State(rawValue: UInt(mpdState.rawValue))!
|
||||
}
|
||||
|
||||
var totalTime: UInt {
|
||||
let mpdTotalTime = mpd_status_get_total_time(status)
|
||||
let mpdTotalTime = mpd_status_get_total_time(mpdStatus)
|
||||
|
||||
return UInt(mpdTotalTime)
|
||||
}
|
||||
|
||||
var elapsedTimeMs: UInt {
|
||||
let mpdElapsedTimeMs = mpd_status_get_elapsed_ms(status)
|
||||
let mpdElapsedTimeMs = mpd_status_get_elapsed_ms(mpdStatus)
|
||||
|
||||
return UInt(mpdElapsedTimeMs)
|
||||
}
|
||||
|
||||
var song: Int {
|
||||
return Int(mpd_status_get_song_pos(status))
|
||||
return Int(mpd_status_get_song_pos(mpdStatus))
|
||||
}
|
||||
|
||||
var updating: Bool {
|
||||
let updating = mpd_status_get_update_id(status)
|
||||
let updating = mpd_status_get_update_id(mpdStatus)
|
||||
|
||||
return updating > 0
|
||||
}
|
||||
@ -12,14 +12,14 @@ protocol MPDClientDelegate {
|
||||
func didConnect(mpdClient: MPDClient)
|
||||
func willDisconnect(mpdClient: MPDClient)
|
||||
|
||||
func didUpdateState(mpdClient: MPDClient, state: MPDClient.MPDStatus.State)
|
||||
func didUpdateState(mpdClient: MPDClient, state: MPDClient.Status.State)
|
||||
func didUpdateTime(mpdClient: MPDClient, total: UInt, elapsedMs: UInt)
|
||||
|
||||
func willStartDatabaseUpdate(mpdClient: MPDClient)
|
||||
func didFinishDatabaseUpdate(mpdClient: MPDClient)
|
||||
|
||||
func didUpdateQueue(mpdClient: MPDClient, queue: [MPDClient.MPDSong])
|
||||
func didUpdateQueue(mpdClient: MPDClient, queue: [MPDClient.Song])
|
||||
func didUpdateQueuePos(mpdClient: MPDClient, song: Int)
|
||||
|
||||
func didLoadAlbums(mpdClient: MPDClient, albums: [MPDClient.MPDAlbum])
|
||||
func didLoadAlbums(mpdClient: MPDClient, albums: [MPDClient.Album])
|
||||
}
|
||||
|
||||
@ -7,23 +7,17 @@
|
||||
//
|
||||
|
||||
import Cocoa
|
||||
import CryptoSwift
|
||||
|
||||
struct Album {
|
||||
var mpdAlbum: MPDClient.MPDAlbum
|
||||
struct AlbumItem {
|
||||
var album: MPDClient.Album
|
||||
var coverArt: NSImage?
|
||||
var coverArtFetched: Bool = false
|
||||
|
||||
init(mpdAlbum: MPDClient.MPDAlbum) {
|
||||
self.mpdAlbum = mpdAlbum
|
||||
}
|
||||
|
||||
var title: String {
|
||||
return mpdAlbum.title
|
||||
return album.title
|
||||
}
|
||||
|
||||
var artist: String {
|
||||
return mpdAlbum.artist
|
||||
return album.artist
|
||||
}
|
||||
|
||||
var hash: String {
|
||||
@ -1,25 +0,0 @@
|
||||
//
|
||||
// SongItem.swift
|
||||
// Persephone
|
||||
//
|
||||
// Created by Daniel Barber on 2019/3/25.
|
||||
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct Song {
|
||||
var mpdSong: MPDClient.MPDSong
|
||||
|
||||
var title: String {
|
||||
return mpdSong.getTag(.title)
|
||||
}
|
||||
|
||||
var artist: String {
|
||||
return mpdSong.getTag(.artist)
|
||||
}
|
||||
|
||||
var album: Album {
|
||||
return Album(mpdAlbum: mpdSong.album)
|
||||
}
|
||||
}
|
||||
@ -8,8 +8,8 @@
|
||||
|
||||
import Foundation
|
||||
|
||||
struct QueueItem {
|
||||
var song: Song
|
||||
struct SongItem {
|
||||
var song: MPDClient.Song
|
||||
var queuePos: Int
|
||||
var isPlaying: Bool
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" initialViewController="B8D-0N-5wS">
|
||||
<dependencies>
|
||||
<deployment identifier="macosx"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14490.70"/>
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
|
||||
<capability name="System colors introduced in macOS 10.14" minToolsVersion="10.0"/>
|
||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
||||
</dependencies>
|
||||
@ -315,11 +315,11 @@
|
||||
<objects>
|
||||
<viewController title="Album Art" id="3C9-vU-zjZ" customClass="AlbumArtPrefsController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" id="PyK-v2-kus">
|
||||
<rect key="frame" x="0.0" y="0.0" width="524" height="168"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="524" height="100"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zZn-Rm-e1f">
|
||||
<rect key="frame" x="53" y="130" width="104" height="17"/>
|
||||
<rect key="frame" x="53" y="62" width="104" height="17"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" title="Music Directory:" id="sPn-V6-CfK">
|
||||
<font key="font" usesAppearanceFont="YES"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -327,7 +327,7 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gDk-ca-eOa">
|
||||
<rect key="frame" x="162" y="126" width="288" height="22"/>
|
||||
<rect key="frame" x="162" y="58" width="288" height="22"/>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="~/Music" drawsBackground="YES" id="7WZ-b7-GUs">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -338,7 +338,7 @@
|
||||
</connections>
|
||||
</textField>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pRL-MG-1Be">
|
||||
<rect key="frame" x="160" y="95" width="264" height="18"/>
|
||||
<rect key="frame" x="160" y="27" width="265" height="18"/>
|
||||
<buttonCell key="cell" type="check" title="Fetch missing artwork from MusicBrainz" bezelStyle="regularSquare" imagePosition="left" inset="2" id="LpD-Ew-HMd">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
@ -347,51 +347,15 @@
|
||||
<action selector="updateFetchMissingArtworkFromInternet:" target="3C9-vU-zjZ" id="I7x-9V-xJr"/>
|
||||
</connections>
|
||||
</button>
|
||||
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="z1g-nP-ksw">
|
||||
<rect key="frame" x="160" y="64" width="264" height="18"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="260" id="gK0-aW-CJy"/>
|
||||
</constraints>
|
||||
<buttonCell key="cell" type="check" title="Save fetched artwork to music directory" bezelStyle="regularSquare" imagePosition="left" enabled="NO" inset="2" id="ZeZ-O4-vjS">
|
||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||
<font key="font" metaFont="system"/>
|
||||
</buttonCell>
|
||||
</button>
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="xgS-Kg-8KR">
|
||||
<rect key="frame" x="162" y="26" width="144" height="22"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="144" id="DSX-th-Wn1"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" enabled="NO" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="folder.jpg" drawsBackground="YES" id="nKF-YI-xBL">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="SmH-w6-5QI">
|
||||
<rect key="frame" x="37" y="30" width="119" height="17"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" enabled="NO" alignment="right" title="Cover art filename:" id="b4u-u7-iWD">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="secondaryLabelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="pRL-MG-1Be" firstAttribute="centerX" secondItem="z1g-nP-ksw" secondAttribute="centerX" id="0Ev-Ia-XBO"/>
|
||||
<constraint firstItem="gDk-ca-eOa" firstAttribute="leading" secondItem="pRL-MG-1Be" secondAttribute="leading" id="1jd-wZ-a4Q"/>
|
||||
<constraint firstItem="pRL-MG-1Be" firstAttribute="leading" secondItem="z1g-nP-ksw" secondAttribute="leading" id="3tZ-Ub-RaT"/>
|
||||
<constraint firstItem="gDk-ca-eOa" firstAttribute="leading" secondItem="zZn-Rm-e1f" secondAttribute="trailing" constant="7" id="C0m-yx-gXh"/>
|
||||
<constraint firstItem="z1g-nP-ksw" firstAttribute="leading" secondItem="xgS-Kg-8KR" secondAttribute="leading" id="Dkv-ai-X2G"/>
|
||||
<constraint firstItem="xgS-Kg-8KR" firstAttribute="leading" secondItem="SmH-w6-5QI" secondAttribute="trailing" constant="8" symbolic="YES" id="KQk-nr-H8y"/>
|
||||
<constraint firstItem="zZn-Rm-e1f" firstAttribute="leading" secondItem="PyK-v2-kus" secondAttribute="leading" constant="55" id="OzK-MR-zuB"/>
|
||||
<constraint firstAttribute="bottom" secondItem="SmH-w6-5QI" secondAttribute="bottom" constant="30" id="aBY-Ny-jPe"/>
|
||||
<constraint firstItem="pRL-MG-1Be" firstAttribute="top" secondItem="gDk-ca-eOa" secondAttribute="bottom" constant="15" id="dKy-uC-r43"/>
|
||||
<constraint firstItem="xgS-Kg-8KR" firstAttribute="top" secondItem="z1g-nP-ksw" secondAttribute="bottom" constant="18" id="lfR-Im-bd4"/>
|
||||
<constraint firstAttribute="trailing" secondItem="gDk-ca-eOa" secondAttribute="trailing" constant="74" id="n8X-T2-tXA"/>
|
||||
<constraint firstAttribute="bottom" secondItem="xgS-Kg-8KR" secondAttribute="bottom" constant="26" id="oXZ-qo-HwX"/>
|
||||
<constraint firstItem="SmH-w6-5QI" firstAttribute="top" secondItem="zZn-Rm-e1f" secondAttribute="bottom" constant="83" id="qhC-mD-Bvw"/>
|
||||
<constraint firstItem="z1g-nP-ksw" firstAttribute="top" secondItem="pRL-MG-1Be" secondAttribute="bottom" constant="17" id="sTP-hk-zfU"/>
|
||||
<constraint firstItem="zZn-Rm-e1f" firstAttribute="leading" secondItem="PyK-v2-kus" secondAttribute="leading" constant="55" id="F9T-mO-lMa"/>
|
||||
<constraint firstItem="gDk-ca-eOa" firstAttribute="top" secondItem="PyK-v2-kus" secondAttribute="top" constant="20" symbolic="YES" id="NSz-Xf-KZS"/>
|
||||
<constraint firstAttribute="trailing" secondItem="gDk-ca-eOa" secondAttribute="trailing" constant="74" id="QMb-TP-IdQ"/>
|
||||
<constraint firstItem="pRL-MG-1Be" firstAttribute="top" secondItem="gDk-ca-eOa" secondAttribute="bottom" constant="15" id="bD6-hA-Wz5"/>
|
||||
<constraint firstItem="gDk-ca-eOa" firstAttribute="leading" secondItem="zZn-Rm-e1f" secondAttribute="trailing" constant="7" id="oZ5-45-Pe5"/>
|
||||
<constraint firstItem="gDk-ca-eOa" firstAttribute="leading" secondItem="pRL-MG-1Be" secondAttribute="leading" id="sBG-Yb-ii6"/>
|
||||
<constraint firstItem="zZn-Rm-e1f" firstAttribute="top" secondItem="PyK-v2-kus" secondAttribute="top" constant="21" id="wHW-jd-TaG"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
@ -401,7 +365,7 @@
|
||||
</viewController>
|
||||
<customObject id="KzD-E3-lpA" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1626" y="373"/>
|
||||
<point key="canvasLocation" x="1626" y="339"/>
|
||||
</scene>
|
||||
<!--General-->
|
||||
<scene sceneID="xTC-Y5-Agk">
|
||||
@ -425,7 +389,7 @@
|
||||
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="IbX-oV-soD">
|
||||
<rect key="frame" x="162" y="26" width="80" height="22"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="80" id="vW2-G6-2vi"/>
|
||||
<constraint firstAttribute="width" constant="80" id="WAb-PB-Z1Y"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="6600" drawsBackground="YES" id="i9j-nB-bqq">
|
||||
<numberFormatter key="formatter" formatterBehavior="custom10_4" numberStyle="decimal" usesGroupingSeparator="NO" minimumIntegerDigits="1" maximumIntegerDigits="2000000000" maximumFractionDigits="3" id="UiQ-gi-Hbp">
|
||||
@ -442,17 +406,17 @@
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="kvB-99-zwY">
|
||||
<rect key="frame" x="76" y="62" width="80" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="76" id="4VR-n5-bGr"/>
|
||||
</constraints>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Server Host:" id="AVi-g9-Irz">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="AU9-wN-kbU">
|
||||
<rect key="frame" x="78" y="30" width="76" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="72" id="Of6-Ls-knP"/>
|
||||
</constraints>
|
||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="AU9-wN-kbU">
|
||||
<rect key="frame" x="77" y="30" width="77" height="17"/>
|
||||
<textFieldCell key="cell" lineBreakMode="clipping" alignment="right" title="Server Port:" id="DgA-xT-2ir">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -461,15 +425,16 @@
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="kvB-99-zwY" firstAttribute="centerX" secondItem="AU9-wN-kbU" secondAttribute="centerX" id="6bm-yZ-rJu"/>
|
||||
<constraint firstItem="AU9-wN-kbU" firstAttribute="top" secondItem="kvB-99-zwY" secondAttribute="bottom" constant="15" id="6qh-zn-2xt"/>
|
||||
<constraint firstAttribute="trailing" secondItem="wPm-sJ-e9E" secondAttribute="trailing" constant="74" id="B0v-nc-2aA"/>
|
||||
<constraint firstItem="kvB-99-zwY" firstAttribute="leading" secondItem="Uwt-Lw-ILP" secondAttribute="leading" constant="78" id="Hvc-Wt-Uha"/>
|
||||
<constraint firstItem="wPm-sJ-e9E" firstAttribute="leading" secondItem="kvB-99-zwY" secondAttribute="trailing" constant="8" symbolic="YES" id="NAc-1j-JEH"/>
|
||||
<constraint firstAttribute="bottom" secondItem="IbX-oV-soD" secondAttribute="bottom" constant="26" id="QHJ-ID-D3L"/>
|
||||
<constraint firstAttribute="bottom" secondItem="AU9-wN-kbU" secondAttribute="bottom" constant="30" id="j5r-By-kiE"/>
|
||||
<constraint firstItem="wPm-sJ-e9E" firstAttribute="leading" secondItem="IbX-oV-soD" secondAttribute="leading" id="lz4-7S-QQb"/>
|
||||
<constraint firstItem="IbX-oV-soD" firstAttribute="top" secondItem="wPm-sJ-e9E" secondAttribute="bottom" constant="10" id="sZA-01-JAS"/>
|
||||
<constraint firstItem="wPm-sJ-e9E" firstAttribute="top" secondItem="Uwt-Lw-ILP" secondAttribute="top" constant="20" symbolic="YES" id="1u9-dh-XrA"/>
|
||||
<constraint firstItem="wPm-sJ-e9E" firstAttribute="leading" secondItem="kvB-99-zwY" secondAttribute="trailing" constant="8" symbolic="YES" id="655-3c-mJH"/>
|
||||
<constraint firstItem="AU9-wN-kbU" firstAttribute="top" secondItem="kvB-99-zwY" secondAttribute="bottom" constant="15" id="FPR-mZ-SUo"/>
|
||||
<constraint firstItem="IbX-oV-soD" firstAttribute="top" secondItem="wPm-sJ-e9E" secondAttribute="bottom" constant="10" symbolic="YES" id="G7a-VX-OLJ"/>
|
||||
<constraint firstItem="kvB-99-zwY" firstAttribute="top" secondItem="Uwt-Lw-ILP" secondAttribute="top" constant="21" id="ITg-Ag-wpl"/>
|
||||
<constraint firstItem="kvB-99-zwY" firstAttribute="leading" secondItem="Uwt-Lw-ILP" secondAttribute="leading" constant="78" id="Kw0-2i-oST"/>
|
||||
<constraint firstItem="IbX-oV-soD" firstAttribute="leading" secondItem="AU9-wN-kbU" secondAttribute="trailing" constant="10" id="Y6y-25-qRM"/>
|
||||
<constraint firstAttribute="trailing" secondItem="wPm-sJ-e9E" secondAttribute="trailing" constant="74" id="cX3-Sz-RXR"/>
|
||||
<constraint firstItem="kvB-99-zwY" firstAttribute="centerX" secondItem="AU9-wN-kbU" secondAttribute="centerX" id="fdg-fL-UzL"/>
|
||||
<constraint firstItem="wPm-sJ-e9E" firstAttribute="leading" secondItem="IbX-oV-soD" secondAttribute="leading" id="qBw-Ri-Z4l"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
@ -485,19 +450,18 @@
|
||||
<scene sceneID="QcX-dC-cTZ">
|
||||
<objects>
|
||||
<viewController id="KIP-rq-4dM" customClass="QueueViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<splitView key="view" wantsLayer="YES" dividerStyle="thin" id="84I-w3-Mxl">
|
||||
<rect key="frame" x="0.0" y="0.0" width="328" height="548"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<view key="view" id="2su-YT-hba">
|
||||
<rect key="frame" x="0.0" y="0.0" width="350" height="300"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" id="S3o-nF-NN7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="328" height="219"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<scrollView borderType="none" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="S3o-nF-NN7">
|
||||
<rect key="frame" x="0.0" y="0.0" width="350" height="300"/>
|
||||
<clipView key="contentView" drawsBackground="NO" id="WI8-Pw-03L">
|
||||
<rect key="frame" x="0.0" y="0.0" width="328" height="219"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="350" height="300"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<outlineView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="lastColumnOnly" selectionHighlightStyle="sourceList" multipleSelection="NO" autosaveColumns="NO" rowSizeStyle="automatic" viewBased="YES" indentationPerLevel="16" outlineTableColumn="0Co-uF-CCB" id="jEJ-jg-fll">
|
||||
<rect key="frame" x="0.0" y="0.0" width="328" height="219"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="350" height="300"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<size key="intercellSpacing" width="3" height="2"/>
|
||||
<color key="backgroundColor" name="_sourceListBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -520,11 +484,9 @@
|
||||
<rect key="frame" x="1" y="1" width="200" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" misplaced="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xgd-Cz-np3">
|
||||
<rect key="frame" x="0.0" y="1" width="329" height="14"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="443" id="mkA-ng-q8a"/>
|
||||
</constraints>
|
||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="xgd-Cz-np3">
|
||||
<rect key="frame" x="0.0" y="1" width="447" height="14"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="QUEUE" id="Mqf-uh-ibl">
|
||||
<font key="font" metaFont="smallSystemBold"/>
|
||||
<color key="textColor" name="controlAccentColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -532,10 +494,6 @@
|
||||
</textFieldCell>
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="xgd-Cz-np3" firstAttribute="leading" secondItem="GOd-cg-juD" secondAttribute="leading" constant="2" id="iOU-E2-K47"/>
|
||||
<constraint firstItem="xgd-Cz-np3" firstAttribute="centerY" secondItem="GOd-cg-juD" secondAttribute="centerY" id="uxd-zs-s33"/>
|
||||
</constraints>
|
||||
</tableCellView>
|
||||
<tableCellView identifier="songTitleCell" id="5rR-Gz-AcP">
|
||||
<rect key="frame" x="1" y="20" width="200" height="17"/>
|
||||
@ -544,7 +502,7 @@
|
||||
<imageView translatesAutoresizingMaskIntoConstraints="NO" id="o8i-cz-hIP">
|
||||
<rect key="frame" x="3" y="0.0" width="17" height="17"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" constant="17" id="v6R-Dd-a1y"/>
|
||||
<constraint firstAttribute="width" constant="17" id="UFf-Fg-9Qg"/>
|
||||
</constraints>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" imageScaling="proportionallyDown" id="ckK-gW-Vhx"/>
|
||||
</imageView>
|
||||
@ -558,12 +516,12 @@
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="o8i-cz-hIP" firstAttribute="bottom" secondItem="i0h-bn-auJ" secondAttribute="bottom" id="3PU-pE-aEs"/>
|
||||
<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="top" secondItem="i0h-bn-auJ" secondAttribute="top" id="Aju-r7-8gb"/>
|
||||
<constraint firstItem="o8i-cz-hIP" firstAttribute="centerY" secondItem="5rR-Gz-AcP" secondAttribute="centerY" id="POX-ni-OdT"/>
|
||||
<constraint firstItem="i0h-bn-auJ" firstAttribute="leading" secondItem="o8i-cz-hIP" secondAttribute="trailing" constant="7" id="QIl-PH-2Ox"/>
|
||||
<constraint firstAttribute="trailing" secondItem="i0h-bn-auJ" secondAttribute="trailing" constant="2" id="S2f-3u-olm"/>
|
||||
<constraint firstAttribute="trailing" secondItem="i0h-bn-auJ" secondAttribute="trailing" constant="2" id="0bp-mw-2mz"/>
|
||||
<constraint firstItem="i0h-bn-auJ" firstAttribute="leading" secondItem="o8i-cz-hIP" secondAttribute="trailing" constant="7" id="GUh-cd-QQx"/>
|
||||
<constraint firstItem="o8i-cz-hIP" firstAttribute="centerY" secondItem="5rR-Gz-AcP" secondAttribute="centerY" id="QT8-Zj-AGK"/>
|
||||
<constraint firstItem="o8i-cz-hIP" firstAttribute="leading" secondItem="5rR-Gz-AcP" secondAttribute="leading" constant="3" id="VCd-sT-dmS"/>
|
||||
<constraint firstItem="o8i-cz-hIP" firstAttribute="bottom" secondItem="i0h-bn-auJ" secondAttribute="bottom" id="ud8-g2-OYn"/>
|
||||
<constraint firstItem="o8i-cz-hIP" firstAttribute="top" secondItem="i0h-bn-auJ" secondAttribute="top" id="yN0-Pp-adE"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="imageView" destination="o8i-cz-hIP" id="4In-Lr-QcL"/>
|
||||
@ -572,7 +530,7 @@
|
||||
</tableCellView>
|
||||
</prototypeCellViews>
|
||||
</tableColumn>
|
||||
<tableColumn identifier="songArtistColumn" width="122" minWidth="64" maxWidth="1000" id="SPM-QP-DX8">
|
||||
<tableColumn identifier="songArtistColumn" width="144" minWidth="64" maxWidth="1000" id="SPM-QP-DX8">
|
||||
<tableHeaderCell key="headerCell" lineBreakMode="truncatingTail" borderStyle="border" alignment="left" title="Artist">
|
||||
<font key="font" metaFont="smallSystem"/>
|
||||
<color key="textColor" name="headerTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -586,11 +544,11 @@
|
||||
<tableColumnResizingMask key="resizingMask" resizeWithTable="YES"/>
|
||||
<prototypeCellViews>
|
||||
<tableCellView identifier="songArtistCell" id="JSk-Vc-Y7e">
|
||||
<rect key="frame" x="204" y="1" width="122" height="17"/>
|
||||
<rect key="frame" x="204" y="1" width="144" height="17"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" translatesAutoresizingMaskIntoConstraints="NO" id="tBe-Q9-3Rw">
|
||||
<rect key="frame" x="0.0" y="0.0" width="127" height="17"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="149" height="17"/>
|
||||
<textFieldCell key="cell" lineBreakMode="truncatingTail" sendsActionOnEndEditing="YES" title="Table View Cell" id="Ceb-ec-ydU">
|
||||
<font key="font" metaFont="system"/>
|
||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||
@ -599,9 +557,9 @@
|
||||
</textField>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstAttribute="trailing" secondItem="tBe-Q9-3Rw" secondAttribute="trailing" constant="-3" id="E6a-sY-dne"/>
|
||||
<constraint firstItem="tBe-Q9-3Rw" firstAttribute="leading" secondItem="JSk-Vc-Y7e" secondAttribute="leading" constant="2" id="Gm4-l1-WRz"/>
|
||||
<constraint firstItem="tBe-Q9-3Rw" firstAttribute="centerY" secondItem="JSk-Vc-Y7e" secondAttribute="centerY" id="KhJ-nn-rh5"/>
|
||||
<constraint firstItem="tBe-Q9-3Rw" firstAttribute="centerY" secondItem="JSk-Vc-Y7e" secondAttribute="centerY" id="Tkg-cb-Bg6"/>
|
||||
<constraint firstAttribute="trailing" secondItem="tBe-Q9-3Rw" secondAttribute="trailing" constant="-3" id="VhZ-ua-QQX"/>
|
||||
<constraint firstItem="tBe-Q9-3Rw" firstAttribute="leading" secondItem="JSk-Vc-Y7e" secondAttribute="leading" constant="2" id="cTy-tR-Grg"/>
|
||||
</constraints>
|
||||
<connections>
|
||||
<outlet property="textField" destination="tBe-Q9-3Rw" id="2e6-zi-tKj"/>
|
||||
@ -619,11 +577,11 @@
|
||||
<nil key="backgroundColor"/>
|
||||
</clipView>
|
||||
<constraints>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="200" id="Kqx-7n-cws"/>
|
||||
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="500" id="zuT-k9-w8d"/>
|
||||
<constraint firstAttribute="width" relation="lessThanOrEqual" constant="500" id="tgW-46-U0V"/>
|
||||
<constraint firstAttribute="width" relation="greaterThanOrEqual" constant="200" id="ynf-58-b0B"/>
|
||||
</constraints>
|
||||
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="7mx-v9-DSr">
|
||||
<rect key="frame" x="0.0" y="237" width="328" height="16"/>
|
||||
<rect key="frame" x="0.0" y="284" width="350" height="16"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="NO" id="p5z-C0-FUJ">
|
||||
@ -631,40 +589,21 @@
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</scroller>
|
||||
</scrollView>
|
||||
<customView id="iUb-eV-Qws">
|
||||
<rect key="frame" x="0.0" y="220" width="328" height="328"/>
|
||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
|
||||
<subviews>
|
||||
<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"/>
|
||||
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyUpOrDown" image="blankAlbum" id="IoN-3N-TCb"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
<constraints>
|
||||
<constraint firstItem="Dw3-M5-tWY" firstAttribute="leading" secondItem="iUb-eV-Qws" secondAttribute="leading" id="ERf-Kd-Q7L"/>
|
||||
<constraint firstAttribute="bottom" secondItem="Dw3-M5-tWY" secondAttribute="bottom" id="GGz-uh-bzq"/>
|
||||
<constraint firstAttribute="trailing" secondItem="Dw3-M5-tWY" secondAttribute="trailing" id="c91-AQ-BT2"/>
|
||||
<constraint firstAttribute="width" secondItem="iUb-eV-Qws" secondAttribute="height" multiplier="1:1" id="f4L-V4-5x2"/>
|
||||
<constraint firstItem="Dw3-M5-tWY" firstAttribute="top" secondItem="iUb-eV-Qws" secondAttribute="top" id="ue4-Gb-CaX"/>
|
||||
</constraints>
|
||||
<shadow key="shadow">
|
||||
<color key="color" white="0.0" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
</shadow>
|
||||
</customView>
|
||||
</subviews>
|
||||
<holdingPriorities>
|
||||
<real value="250"/>
|
||||
<real value="250"/>
|
||||
</holdingPriorities>
|
||||
</splitView>
|
||||
<constraints>
|
||||
<constraint firstItem="S3o-nF-NN7" firstAttribute="leading" secondItem="2su-YT-hba" secondAttribute="leading" id="B7i-Su-Rgr"/>
|
||||
<constraint firstItem="S3o-nF-NN7" firstAttribute="top" secondItem="2su-YT-hba" secondAttribute="top" id="EZN-ac-0xk"/>
|
||||
<constraint firstAttribute="bottom" secondItem="S3o-nF-NN7" secondAttribute="bottom" id="OTS-yj-4am"/>
|
||||
<constraint firstAttribute="trailing" secondItem="S3o-nF-NN7" secondAttribute="trailing" id="Yis-ZS-y2w"/>
|
||||
</constraints>
|
||||
</view>
|
||||
<connections>
|
||||
<outlet property="queueAlbumArtImage" destination="Dw3-M5-tWY" id="3hQ-Gu-XqM"/>
|
||||
<outlet property="queueView" destination="jEJ-jg-fll" id="cwo-E8-deo"/>
|
||||
</connections>
|
||||
</viewController>
|
||||
<customObject id="du4-e9-TfX" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="820" y="749"/>
|
||||
<point key="canvasLocation" x="834" y="679"/>
|
||||
</scene>
|
||||
<!--Album View Controller-->
|
||||
<scene sceneID="7Ua-Hj-zWt">
|
||||
@ -715,12 +654,11 @@
|
||||
</viewController>
|
||||
<customObject id="uex-Ws-5X4" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="531" y="1358"/>
|
||||
<point key="canvasLocation" x="744" y="1077"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="NSPreferencesGeneral" width="32" height="32"/>
|
||||
<image name="blankAlbum" width="128" height="128"/>
|
||||
<image name="coverArtPreferencesIcon" width="32" height="32"/>
|
||||
<image name="nextTrackButton" width="17" height="17"/>
|
||||
<image name="playButton" width="17" height="17"/>
|
||||
|
||||
@ -11,60 +11,43 @@ import PromiseKit
|
||||
|
||||
class AlbumArtService {
|
||||
var preferences = Preferences()
|
||||
let song: Song
|
||||
let album: Album
|
||||
|
||||
let album: AlbumItem
|
||||
|
||||
let cachedArtworkSize = 180
|
||||
let cachedArtworkQuality: CGFloat = 0.5
|
||||
|
||||
let bigArtworkSize = 600
|
||||
|
||||
var session = URLSession(configuration: .default)
|
||||
let artworkQueue = DispatchQueue(label: "albumArtQueue", qos: .utility)
|
||||
let cacheQueue = DispatchQueue(label: "albumArtCacheQueue")
|
||||
|
||||
init(song: Song) {
|
||||
self.song = song
|
||||
self.album = song.album
|
||||
init(album: AlbumItem) {
|
||||
self.album = album
|
||||
}
|
||||
|
||||
func fetchBigAlbumArt() -> Promise<NSImage?> {
|
||||
return firstly {
|
||||
self.getArtworkFromFilesystem()
|
||||
}.then { (image: NSImage?) -> Promise<NSImage?> in
|
||||
image.map(Promise.value) ?? self.getRemoteArtwork()
|
||||
}.recover { (_) -> Guarantee<NSImage?> in
|
||||
return .value(nil)
|
||||
func fetchAlbumArt(callback: @escaping (_ image: NSImage?) -> Void) {
|
||||
cacheQueue.async {
|
||||
firstly {
|
||||
self.getCachedArtwork()
|
||||
}.then { artwork -> Promise<NSImage?> in
|
||||
artwork.map(Promise.value) ?? self.cacheIfNecessary(self.getArtworkFromFilesystem())
|
||||
}.then { artwork -> Promise<NSImage?> in
|
||||
artwork.map(Promise.value) ?? self.cacheIfNecessary(self.getArtworkFromMusicBrainz().map(Optional.some))
|
||||
}.tap { result in
|
||||
switch result {
|
||||
case .fulfilled(nil), .rejected(MusicBrainzError.noArtworkAvailable):
|
||||
self.cacheArtwork(data: Data())
|
||||
default:
|
||||
break
|
||||
}
|
||||
}.recover { error in
|
||||
.value(nil)
|
||||
}.done(callback)
|
||||
}
|
||||
}
|
||||
|
||||
func fetchAlbumArt() -> Guarantee<NSImage?> {
|
||||
return firstly {
|
||||
self.getCachedArtwork()
|
||||
}.then { (artwork: NSImage?) -> Promise<NSImage?> in
|
||||
artwork.map(Promise.value) ?? self.getArtworkFromFilesystem()
|
||||
}.then { (artwork: NSImage?) -> Promise<NSImage?> in
|
||||
artwork.map(Promise.value) ?? self.getRemoteArtwork()
|
||||
}.compactMap(on: artworkQueue) {
|
||||
return self.sizeAndCacheImage($0).map(Optional.some)
|
||||
}.recover { _ in
|
||||
return .value(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func sizeAndCacheImage(_ image: NSImage?) -> NSImage? {
|
||||
switch image {
|
||||
case nil:
|
||||
self.cacheArtwork(data: Data())
|
||||
return image
|
||||
case let image:
|
||||
if self.isArtworkCached() {
|
||||
return image
|
||||
} else {
|
||||
let sizedImage = image?.toFitBox(
|
||||
size: NSSize(width: self.cachedArtworkSize, height: self.cachedArtworkSize)
|
||||
)
|
||||
self.cacheArtwork(data: sizedImage?.jpegData(compressionQuality: self.cachedArtworkQuality))
|
||||
return sizedImage
|
||||
func cacheIfNecessary(_ promise: Promise<NSImage?>) -> Promise<NSImage?> {
|
||||
return promise.get { image in
|
||||
if let data = image?.jpegData(compressionQuality: self.cachedArtworkQuality) {
|
||||
self.cacheArtwork(data: data)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -14,38 +14,22 @@ extension AlbumArtService {
|
||||
|
||||
func getCachedArtwork() -> Promise<NSImage?> {
|
||||
return Promise { seal in
|
||||
artworkQueue.async {
|
||||
if self.isArtworkCached() {
|
||||
let cacheFilePath = AlbumArtService.cacheDir.appendingPathComponent(self.album.hash).path
|
||||
let data = FileManager.default.contents(atPath: cacheFilePath)
|
||||
let image = NSImage(data: data ?? Data()) ?? NSImage.defaultCoverArt
|
||||
let cacheFilePath = AlbumArtService.cacheDir.appendingPathComponent(album.hash).path
|
||||
let data = FileManager.default.contents(atPath: cacheFilePath)
|
||||
let image = data.flatMap(NSImage.init(data:))
|
||||
|
||||
seal.fulfill(image)
|
||||
} else {
|
||||
seal.fulfill(nil)
|
||||
}
|
||||
}
|
||||
seal.fulfill(image)
|
||||
}
|
||||
}
|
||||
|
||||
func cacheArtwork(data: Data?) {
|
||||
artworkQueue.async {
|
||||
guard let bundleIdentifier = Bundle.main.bundleIdentifier,
|
||||
let cacheDir = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
|
||||
.appendingPathComponent(bundleIdentifier)
|
||||
else { return }
|
||||
guard let bundleIdentifier = Bundle.main.bundleIdentifier,
|
||||
let cacheDir = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
|
||||
.appendingPathComponent(bundleIdentifier)
|
||||
else { return }
|
||||
|
||||
let cacheFilePath = cacheDir.appendingPathComponent(self.album.hash).path
|
||||
let cacheFilePath = cacheDir.appendingPathComponent(album.hash).path
|
||||
|
||||
if !self.isArtworkCached() {
|
||||
FileManager.default.createFile(atPath: cacheFilePath, contents: data, attributes: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func isArtworkCached() -> Bool {
|
||||
let cacheFilePath = AlbumArtService.cacheDir.appendingPathComponent(album.hash).path
|
||||
|
||||
return FileManager.default.fileExists(atPath: cacheFilePath)
|
||||
FileManager.default.createFile(atPath: cacheFilePath, contents: data, attributes: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,61 +10,29 @@ import Cocoa
|
||||
import PromiseKit
|
||||
|
||||
extension AlbumArtService {
|
||||
var coverArtFilenames: [String] {
|
||||
return [
|
||||
func getArtworkFromFilesystem() -> Promise<NSImage?> {
|
||||
let coverArtFilenames = [
|
||||
"folder.jpg",
|
||||
"cover.jpg",
|
||||
"\(album.artist) - \(album.title).jpg"
|
||||
]
|
||||
|
||||
return getAlbumURI().map { albumURI in
|
||||
let musicDir = self.preferences.expandedMpdLibraryDir
|
||||
|
||||
return coverArtFilenames
|
||||
.lazy
|
||||
.map { "\(musicDir)/\(albumURI)/\($0)" }
|
||||
.compactMap(self.tryImage)
|
||||
.first
|
||||
}
|
||||
}
|
||||
|
||||
var musicDir: String {
|
||||
return self.preferences.expandedMpdLibraryDir
|
||||
}
|
||||
|
||||
func getArtworkFromFilesystem() -> Promise<NSImage?> {
|
||||
func getAlbumURI() -> Promise<String> {
|
||||
return Promise { seal in
|
||||
artworkQueue.async {
|
||||
guard let artworkPath = self.fileSystemArtworkFilePath()
|
||||
else { seal.fulfill(nil); return }
|
||||
|
||||
let image = self.tryImage(artworkPath)
|
||||
|
||||
seal.fulfill(image)
|
||||
}
|
||||
AppDelegate.mpdClient.getAlbumURI(for: album.album, callback: seal.fulfill)
|
||||
}
|
||||
}
|
||||
|
||||
func saveArtworkToFilesystem(data: Data?) {
|
||||
let artworkFileName = coverArtFilenames.first!
|
||||
|
||||
if self.fileSystemArtworkFilePath() == nil {
|
||||
FileManager.default.createFile(
|
||||
atPath: "\(self.musicDir)/\(self.songPath)/\(artworkFileName)",
|
||||
contents: data,
|
||||
attributes: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func fileSystemArtworkFilePath() -> String? {
|
||||
let musicDir = self.preferences.expandedMpdLibraryDir
|
||||
|
||||
return self.coverArtFilenames
|
||||
.lazy
|
||||
.map { "\(musicDir)/\(self.songPath)/\($0)" }
|
||||
.first {
|
||||
FileManager.default.fileExists(atPath: $0)
|
||||
}
|
||||
}
|
||||
|
||||
var songPath: String {
|
||||
return song
|
||||
.mpdSong
|
||||
.uriString
|
||||
.split(separator: "/")
|
||||
.dropLast()
|
||||
.joined(separator: "/")
|
||||
.compactMap { $0 }
|
||||
}
|
||||
|
||||
func tryImage(_ filePath: String) -> NSImage? {
|
||||
@ -72,6 +40,10 @@ extension AlbumArtService {
|
||||
let image = NSImage(data: data)
|
||||
else { return nil }
|
||||
|
||||
return image
|
||||
let imageThumb = image.toFitBox(
|
||||
size: NSSize(width: self.cachedArtworkSize, height: self.cachedArtworkSize)
|
||||
)
|
||||
|
||||
return imageThumb
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,24 +12,17 @@ import PromiseKit
|
||||
import PMKFoundation
|
||||
|
||||
extension AlbumArtService {
|
||||
enum RemoteArtworkError: Error {
|
||||
enum MusicBrainzError: Error {
|
||||
case noArtworkAvailable
|
||||
case notConfigured
|
||||
}
|
||||
|
||||
func getRemoteArtwork() -> Promise<NSImage?> {
|
||||
func getRemoteArtwork() -> Promise<NSImage> {
|
||||
return Promise { seal in
|
||||
if preferences.fetchMissingArtworkFromInternet {
|
||||
artworkQueue.async {
|
||||
let albumArtWorkItem = DispatchWorkItem {
|
||||
self.getArtworkFromMusicBrainz().map(Optional.some).pipe(to: seal.resolve)
|
||||
}
|
||||
|
||||
AlbumArtQueue.shared.addToQueue(workItem: albumArtWorkItem)
|
||||
}
|
||||
} else {
|
||||
throw RemoteArtworkError.notConfigured
|
||||
let albumArtWorkItem = DispatchWorkItem {
|
||||
self.getArtworkFromMusicBrainz().pipe(to: seal.resolve)
|
||||
}
|
||||
|
||||
AlbumArtQueue.shared.addToQueue(workItem: albumArtWorkItem)
|
||||
}
|
||||
}
|
||||
|
||||
@ -37,26 +30,25 @@ extension AlbumArtService {
|
||||
var search = URLComponents(string: "https://musicbrainz.org/ws/2/release/")!
|
||||
search.query = "query=artist:\(album.artist) AND release:\(album.title) AND country:US&limit=1&fmt=json"
|
||||
|
||||
return firstly {
|
||||
URLSession.shared.dataTask(.promise, with: search.url!).validate()
|
||||
}.compactMap {
|
||||
JSON($0.data)
|
||||
}.compactMap {
|
||||
$0["releases"][0]["id"].string
|
||||
}.compactMap {
|
||||
URLComponents(string: "https://coverartarchive.org/release/\($0)/front-500")?.url
|
||||
}.then { (url: URL?) -> Promise<(data: Data, response: URLResponse)> in
|
||||
return URLSession.shared.dataTask(.promise, with: url!).validate()
|
||||
}.compactMap {
|
||||
NSImage(data: $0.data)?.toFitBox(
|
||||
size: NSSize(width: self.cachedArtworkSize, height: self.cachedArtworkSize)
|
||||
)
|
||||
}.recover { error -> Promise<NSImage> in
|
||||
if case PMKHTTPError.badStatusCode(404, _, _) = error {
|
||||
throw RemoteArtworkError.noArtworkAvailable
|
||||
} else {
|
||||
throw error
|
||||
return URLSession.shared.dataTask(.promise, with: search.url!).validate()
|
||||
.compactMap {
|
||||
JSON($0.data)
|
||||
}.compactMap {
|
||||
$0["releases"][0]["id"].string
|
||||
}.compactMap {
|
||||
URLComponents(string: "https://coverartarchive.org/release/\($0)/front-500")?.url
|
||||
}.then { (url: URL?) -> Promise<(data: Data, response: URLResponse)> in
|
||||
return URLSession.shared.dataTask(.promise, with: url!).validate()
|
||||
}.compactMap {
|
||||
NSImage(data: $0.data)?.toFitBox(
|
||||
size: NSSize(width: self.cachedArtworkSize, height: self.cachedArtworkSize)
|
||||
)
|
||||
}.recover { error -> Promise<NSImage> in
|
||||
if case PMKHTTPError.badStatusCode(404, _, _) = error {
|
||||
throw MusicBrainzError.noArtworkAvailable
|
||||
} else {
|
||||
throw error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 795 KiB After Width: | Height: | Size: 784 KiB |
Loading…
Reference in New Issue
Block a user