mirror of
https://github.com/danbee/persephone
synced 2025-03-04 08:39:11 +00:00
WIP: Refactor MPDClient
This should make handling the queuing side work more reliably.
This commit is contained in:
parent
537a66d6aa
commit
fe748e2c61
@ -22,6 +22,14 @@
|
|||||||
E41B22C021FB6BBA00D544F6 /* libmpdclient.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (Required, ); }; };
|
E41B22C021FB6BBA00D544F6 /* libmpdclient.2.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (Required, ); }; };
|
||||||
E41B22C121FB6C3300D544F6 /* libmpdclient.2.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
E41B22C121FB6C3300D544F6 /* libmpdclient.2.dylib in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E41B22BF21FB6BBA00D544F6 /* libmpdclient.2.dylib */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||||
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41B22C521FB932700D544F6 /* MPDClient.swift */; };
|
E41B22C621FB932700D544F6 /* MPDClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41B22C521FB932700D544F6 /* MPDClient.swift */; };
|
||||||
|
E41E52FD223BF87300173814 /* MPDClient+Connection.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E52FC223BF87300173814 /* MPDClient+Connection.swift */; };
|
||||||
|
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */; };
|
||||||
|
E41E5301223BF99300173814 /* MPDClient+Queue.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5300223BF99300173814 /* MPDClient+Queue.swift */; };
|
||||||
|
E41E5303223BF9C300173814 /* MPDClient+Idle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5302223BF9C300173814 /* MPDClient+Idle.swift */; };
|
||||||
|
E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5304223BFB0700173814 /* MPDClient+Error.swift */; };
|
||||||
|
E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5306223C019100173814 /* MPDClient+Status.swift */; };
|
||||||
|
E41E5309223C020400173814 /* MPDClient+Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E5308223C020400173814 /* MPDClient+Command.swift */; };
|
||||||
|
E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41E530A223C033700173814 /* MPDClient+Album.swift */; };
|
||||||
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */; };
|
E41EA46C221636AF0068EF46 /* GeneralPrefsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */; };
|
||||||
E421ACA3221F73C4008B2449 /* MediaKeyTap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */; };
|
E421ACA3221F73C4008B2449 /* MediaKeyTap.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */; };
|
||||||
E421ACA4221F73C4008B2449 /* MediaKeyTap.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
E421ACA4221F73C4008B2449 /* MediaKeyTap.framework in Embed Libraries */ = {isa = PBXBuildFile; fileRef = E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
@ -55,6 +63,8 @@
|
|||||||
E4A83BEF2221F8CF0098FED6 /* AlbumArtPrefsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.swift */; };
|
E4A83BEF2221F8CF0098FED6 /* AlbumArtPrefsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.swift */; };
|
||||||
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */; };
|
E4A83BF12221FAA00098FED6 /* PreferencesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */; };
|
||||||
E4A83BF4222207D50098FED6 /* AlbumArtService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BF3222207D50098FED6 /* AlbumArtService.swift */; };
|
E4A83BF4222207D50098FED6 /* AlbumArtService.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4A83BF3222207D50098FED6 /* AlbumArtService.swift */; };
|
||||||
|
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */; };
|
||||||
|
E4C8B53E22349002009A20F3 /* Idle.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4C8B53D22349002009A20F3 /* Idle.swift */; };
|
||||||
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC8F2204EC7F0024217A /* Delegate.swift */; };
|
E4E8CC902204EC7F0024217A /* Delegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC8F2204EC7F0024217A /* Delegate.swift */; };
|
||||||
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC912204F4B80024217A /* QueueViewController.swift */; };
|
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC912204F4B80024217A /* QueueViewController.swift */; };
|
||||||
E4E8CC942206097F0024217A /* NotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC932206097F0024217A /* NotificationsController.swift */; };
|
E4E8CC942206097F0024217A /* NotificationsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = E4E8CC932206097F0024217A /* NotificationsController.swift */; };
|
||||||
@ -168,6 +178,14 @@
|
|||||||
E41B22E921FB966C00D544F6 /* capabilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = capabilities.h; sourceTree = "<group>"; };
|
E41B22E921FB966C00D544F6 /* capabilities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = capabilities.h; sourceTree = "<group>"; };
|
||||||
E41B22EA21FB966C00D544F6 /* queue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = queue.h; sourceTree = "<group>"; };
|
E41B22EA21FB966C00D544F6 /* queue.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = queue.h; sourceTree = "<group>"; };
|
||||||
E41B22EB21FB966C00D544F6 /* playlist.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = playlist.h; sourceTree = "<group>"; };
|
E41B22EB21FB966C00D544F6 /* playlist.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = playlist.h; sourceTree = "<group>"; };
|
||||||
|
E41E52FC223BF87300173814 /* MPDClient+Connection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Connection.swift"; sourceTree = "<group>"; };
|
||||||
|
E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Transport.swift"; sourceTree = "<group>"; };
|
||||||
|
E41E5300223BF99300173814 /* MPDClient+Queue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Queue.swift"; sourceTree = "<group>"; };
|
||||||
|
E41E5302223BF9C300173814 /* MPDClient+Idle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Idle.swift"; sourceTree = "<group>"; };
|
||||||
|
E41E5304223BFB0700173814 /* MPDClient+Error.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Error.swift"; sourceTree = "<group>"; };
|
||||||
|
E41E5306223C019100173814 /* MPDClient+Status.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Status.swift"; sourceTree = "<group>"; };
|
||||||
|
E41E5308223C020400173814 /* MPDClient+Command.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Command.swift"; sourceTree = "<group>"; };
|
||||||
|
E41E530A223C033700173814 /* MPDClient+Album.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MPDClient+Album.swift"; sourceTree = "<group>"; };
|
||||||
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPrefsViewController.swift; sourceTree = "<group>"; };
|
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GeneralPrefsViewController.swift; sourceTree = "<group>"; };
|
||||||
E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaKeyTap.framework; path = Carthage/Build/Mac/MediaKeyTap.framework; sourceTree = "<group>"; };
|
E421ACA1221F73B8008B2449 /* MediaKeyTap.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = MediaKeyTap.framework; path = Carthage/Build/Mac/MediaKeyTap.framework; sourceTree = "<group>"; };
|
||||||
E42A8F3922176D6400A13ED9 /* LICENSE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = "<group>"; };
|
E42A8F3922176D6400A13ED9 /* LICENSE.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.md; sourceTree = "<group>"; };
|
||||||
@ -199,6 +217,8 @@
|
|||||||
E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumArtPrefsController.swift; sourceTree = "<group>"; };
|
E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumArtPrefsController.swift; sourceTree = "<group>"; };
|
||||||
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = "<group>"; };
|
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesViewController.swift; sourceTree = "<group>"; };
|
||||||
E4A83BF3222207D50098FED6 /* AlbumArtService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumArtService.swift; sourceTree = "<group>"; };
|
E4A83BF3222207D50098FED6 /* AlbumArtService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlbumArtService.swift; sourceTree = "<group>"; };
|
||||||
|
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PreferencesWindowController.swift; sourceTree = "<group>"; };
|
||||||
|
E4C8B53D22349002009A20F3 /* Idle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Idle.swift; sourceTree = "<group>"; };
|
||||||
E4E8CC8F2204EC7F0024217A /* Delegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delegate.swift; sourceTree = "<group>"; };
|
E4E8CC8F2204EC7F0024217A /* Delegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Delegate.swift; sourceTree = "<group>"; };
|
||||||
E4E8CC912204F4B80024217A /* QueueViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueViewController.swift; sourceTree = "<group>"; };
|
E4E8CC912204F4B80024217A /* QueueViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QueueViewController.swift; sourceTree = "<group>"; };
|
||||||
E4E8CC932206097F0024217A /* NotificationsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsController.swift; sourceTree = "<group>"; };
|
E4E8CC932206097F0024217A /* NotificationsController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsController.swift; sourceTree = "<group>"; };
|
||||||
@ -322,6 +342,14 @@
|
|||||||
E408D3BC220E03D20006D9BE /* Extensions */ = {
|
E408D3BC220E03D20006D9BE /* Extensions */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
E41E52FC223BF87300173814 /* MPDClient+Connection.swift */,
|
||||||
|
E41E52FE223BF95E00173814 /* MPDClient+Transport.swift */,
|
||||||
|
E41E5300223BF99300173814 /* MPDClient+Queue.swift */,
|
||||||
|
E41E5302223BF9C300173814 /* MPDClient+Idle.swift */,
|
||||||
|
E41E5304223BFB0700173814 /* MPDClient+Error.swift */,
|
||||||
|
E41E5306223C019100173814 /* MPDClient+Status.swift */,
|
||||||
|
E41E5308223C020400173814 /* MPDClient+Command.swift */,
|
||||||
|
E41E530A223C033700173814 /* MPDClient+Album.swift */,
|
||||||
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */,
|
E408D3BD220E03EE0006D9BE /* RawRepresentable.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
@ -424,8 +452,8 @@
|
|||||||
E4A642DB220912FA00067D21 /* MPDClient */ = {
|
E4A642DB220912FA00067D21 /* MPDClient */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E408D3BC220E03D20006D9BE /* Extensions */,
|
|
||||||
E41B22C521FB932700D544F6 /* MPDClient.swift */,
|
E41B22C521FB932700D544F6 /* MPDClient.swift */,
|
||||||
|
E408D3BC220E03D20006D9BE /* Extensions */,
|
||||||
E4D1B595220BA27C0026F233 /* Protocols */,
|
E4D1B595220BA27C0026F233 /* Protocols */,
|
||||||
E4D1B594220BA2490026F233 /* Models */,
|
E4D1B594220BA2490026F233 /* Models */,
|
||||||
);
|
);
|
||||||
@ -446,6 +474,7 @@
|
|||||||
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */,
|
E41EA46B221636AF0068EF46 /* GeneralPrefsViewController.swift */,
|
||||||
E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.swift */,
|
E4A83BEE2221F8CF0098FED6 /* AlbumArtPrefsController.swift */,
|
||||||
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */,
|
E4A83BF02221FAA00098FED6 /* PreferencesViewController.swift */,
|
||||||
|
E4C8B53B22342005009A20F3 /* PreferencesWindowController.swift */,
|
||||||
);
|
);
|
||||||
path = Controllers;
|
path = Controllers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -465,6 +494,7 @@
|
|||||||
E4A642D922090CBE00067D21 /* Status.swift */,
|
E4A642D922090CBE00067D21 /* Status.swift */,
|
||||||
E4EB2378220F10B8008C70C0 /* Pair.swift */,
|
E4EB2378220F10B8008C70C0 /* Pair.swift */,
|
||||||
E4EB237A220F7CF1008C70C0 /* Album.swift */,
|
E4EB237A220F7CF1008C70C0 /* Album.swift */,
|
||||||
|
E4C8B53D22349002009A20F3 /* Idle.swift */,
|
||||||
);
|
);
|
||||||
path = Models;
|
path = Models;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -693,10 +723,15 @@
|
|||||||
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */,
|
E408D3C2220E134F0006D9BE /* AlbumViewController.swift in Sources */,
|
||||||
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */,
|
E40FE71B221B904300A4223F /* NSEvent.swift in Sources */,
|
||||||
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */,
|
E4928E0B2218D62A001D4BEA /* CGColor.swift in Sources */,
|
||||||
|
E4C8B53E22349002009A20F3 /* Idle.swift in Sources */,
|
||||||
E4F6B460221E119B00ACF42A /* QueueDataSource.swift in Sources */,
|
E4F6B460221E119B00ACF42A /* QueueDataSource.swift in Sources */,
|
||||||
|
E4C8B53C22342005009A20F3 /* PreferencesWindowController.swift in Sources */,
|
||||||
|
E41E5307223C019100173814 /* MPDClient+Status.swift in Sources */,
|
||||||
|
E41E530B223C033700173814 /* MPDClient+Album.swift in Sources */,
|
||||||
E4A642DA22090CBE00067D21 /* Status.swift in Sources */,
|
E4A642DA22090CBE00067D21 /* Status.swift in Sources */,
|
||||||
E47E2FD72220720300F747E6 /* AlbumItemView.swift in Sources */,
|
E47E2FD72220720300F747E6 /* AlbumItemView.swift in Sources */,
|
||||||
E450AD9522262DF10091BED3 /* AlbumArtQueue.swift in Sources */,
|
E450AD9522262DF10091BED3 /* AlbumArtQueue.swift in Sources */,
|
||||||
|
E41E52FD223BF87300173814 /* MPDClient+Connection.swift in Sources */,
|
||||||
E450AD7E222620A10091BED3 /* AlbumItem.swift in Sources */,
|
E450AD7E222620A10091BED3 /* AlbumItem.swift in Sources */,
|
||||||
E4E8CC942206097F0024217A /* NotificationsController.swift in Sources */,
|
E4E8CC942206097F0024217A /* NotificationsController.swift in Sources */,
|
||||||
E408D3B6220DD8970006D9BE /* Notification.swift in Sources */,
|
E408D3B6220DD8970006D9BE /* Notification.swift in Sources */,
|
||||||
@ -709,7 +744,9 @@
|
|||||||
E40F41F3221EDE27004B6CB8 /* Preferences.swift in Sources */,
|
E40F41F3221EDE27004B6CB8 /* Preferences.swift in Sources */,
|
||||||
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */,
|
E47E2FDD2220A6D100F747E6 /* Time.swift in Sources */,
|
||||||
E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */,
|
E407861C2110CE6E006887B1 /* AppDelegate.swift in Sources */,
|
||||||
|
E41E5309223C020400173814 /* MPDClient+Command.swift in Sources */,
|
||||||
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */,
|
E47E2FE52220AA0700F747E6 /* AlbumViewLayout.swift in Sources */,
|
||||||
|
E41E52FF223BF95E00173814 /* MPDClient+Transport.swift in Sources */,
|
||||||
E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */,
|
E47E2FD322205D2500F747E6 /* MainWindow.swift in Sources */,
|
||||||
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */,
|
E47E2FD122205C4600F747E6 /* MainSplitViewController.swift in Sources */,
|
||||||
E4E8CC9A22075D370024217A /* Song.swift in Sources */,
|
E4E8CC9A22075D370024217A /* Song.swift in Sources */,
|
||||||
@ -719,9 +756,12 @@
|
|||||||
E47E2FD5222071FD00F747E6 /* AlbumViewItem.swift in Sources */,
|
E47E2FD5222071FD00F747E6 /* AlbumViewItem.swift in Sources */,
|
||||||
E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */,
|
E408D3BE220E03EE0006D9BE /* RawRepresentable.swift in Sources */,
|
||||||
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */,
|
E4E8CC922204F4B80024217A /* QueueViewController.swift in Sources */,
|
||||||
|
E41E5301223BF99300173814 /* MPDClient+Queue.swift in Sources */,
|
||||||
E4EB237B220F7CF1008C70C0 /* Album.swift in Sources */,
|
E4EB237B220F7CF1008C70C0 /* Album.swift in Sources */,
|
||||||
E450AD9D2229B9050091BED3 /* String.swift in Sources */,
|
E450AD9D2229B9050091BED3 /* String.swift in Sources */,
|
||||||
|
E41E5303223BF9C300173814 /* MPDClient+Idle.swift in Sources */,
|
||||||
E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */,
|
E435E3E4221CD75D00184CFC /* NSImage.swift in Sources */,
|
||||||
|
E41E5305223BFB0700173814 /* MPDClient+Error.swift in Sources */,
|
||||||
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */,
|
E435E3E2221CD4E200184CFC /* NSFont.swift in Sources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
|||||||
@ -1,9 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<Workspace
|
<Workspace
|
||||||
version = "1.0">
|
version = "1.0">
|
||||||
<FileRef
|
|
||||||
location = "group:../README.md">
|
|
||||||
</FileRef>
|
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "self:">
|
location = "self:">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
|
|||||||
94
Persephone/MPDClient/Extensions/MPDClient+Album.swift
Normal file
94
Persephone/MPDClient/Extensions/MPDClient+Album.swift
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
//
|
||||||
|
// MPDAlbum.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2019/3/15.
|
||||||
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import mpdclient
|
||||||
|
|
||||||
|
extension MPDClient {
|
||||||
|
func fetchAllAlbums() {
|
||||||
|
queueCommand(command: .fetchAllAlbums)
|
||||||
|
}
|
||||||
|
|
||||||
|
func playAlbum(_ album: Album) {
|
||||||
|
queueCommand(command: .playAlbum, userData: ["album": album])
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAlbumURI(for album: Album) {
|
||||||
|
queueCommand(command: .getAlbumURI, userData: ["album": album])
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendPlayAlbum(_ album: Album) {
|
||||||
|
var songs: [Song] = []
|
||||||
|
|
||||||
|
mpd_run_clear(self.connection)
|
||||||
|
mpd_search_db_songs(self.connection, true)
|
||||||
|
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, album.title)
|
||||||
|
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM_ARTIST, album.artist)
|
||||||
|
mpd_search_commit(self.connection)
|
||||||
|
while let mpdSong = mpd_recv_song(self.connection) {
|
||||||
|
songs.append(Song(mpdSong))
|
||||||
|
}
|
||||||
|
for song in songs {
|
||||||
|
mpd_run_add(self.connection, song.uri)
|
||||||
|
}
|
||||||
|
mpd_run_play_pos(self.connection, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func allAlbums() {
|
||||||
|
var albums: [Album] = []
|
||||||
|
var artist: String = ""
|
||||||
|
|
||||||
|
mpd_search_db_tags(self.connection, MPD_TAG_ALBUM)
|
||||||
|
mpd_search_add_group_tag(self.connection, MPD_TAG_ALBUM_ARTIST)
|
||||||
|
mpd_search_commit(self.connection)
|
||||||
|
|
||||||
|
while let mpdPair = mpd_recv_pair(self.connection) {
|
||||||
|
let pair = Pair(mpdPair)
|
||||||
|
|
||||||
|
switch pair.name {
|
||||||
|
case "AlbumArtist":
|
||||||
|
artist = pair.value
|
||||||
|
case "Album":
|
||||||
|
albums.append(Album(title: pair.value, artist: artist))
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
mpd_return_pair(self.connection, pair.mpdPair)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.delegate?.didLoadAlbums(mpdClient: self, albums: albums)
|
||||||
|
}
|
||||||
|
|
||||||
|
func albumURI(for album: Album) -> String? {
|
||||||
|
var songURI: String?
|
||||||
|
|
||||||
|
guard isConnected else { return nil }
|
||||||
|
|
||||||
|
print("Getting URI")
|
||||||
|
mpd_search_db_songs(self.connection, true)
|
||||||
|
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, album.title)
|
||||||
|
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM_ARTIST, album.artist)
|
||||||
|
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_TRACK, "1")
|
||||||
|
|
||||||
|
mpd_search_commit(self.connection)
|
||||||
|
print("Performed search")
|
||||||
|
|
||||||
|
while let mpdSong = mpd_recv_song(self.connection) {
|
||||||
|
let song = Song(mpdSong)
|
||||||
|
print(song)
|
||||||
|
|
||||||
|
if songURI == nil {
|
||||||
|
songURI = song.uriString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
print("Got URI")
|
||||||
|
|
||||||
|
return songURI
|
||||||
|
}
|
||||||
|
}
|
||||||
45
Persephone/MPDClient/Extensions/MPDClient+Command.swift
Normal file
45
Persephone/MPDClient/Extensions/MPDClient+Command.swift
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
//
|
||||||
|
// CommandQueue.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2019/3/15.
|
||||||
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension MPDClient {
|
||||||
|
func sendCommand(
|
||||||
|
command: Command,
|
||||||
|
userData: Dictionary<String, Any> = [:]
|
||||||
|
) {
|
||||||
|
switch command {
|
||||||
|
|
||||||
|
// Transport commands
|
||||||
|
case .prevTrack:
|
||||||
|
sendPreviousTrack()
|
||||||
|
case .nextTrack:
|
||||||
|
sendNextTrack()
|
||||||
|
case .stop:
|
||||||
|
sendStop()
|
||||||
|
case .playPause:
|
||||||
|
sendPlay()
|
||||||
|
|
||||||
|
// Status commands
|
||||||
|
case .fetchStatus:
|
||||||
|
sendRunStatus()
|
||||||
|
case .fetchQueue:
|
||||||
|
sendFetchQueue()
|
||||||
|
|
||||||
|
// Album commands
|
||||||
|
case .fetchAllAlbums:
|
||||||
|
allAlbums()
|
||||||
|
case .playAlbum:
|
||||||
|
guard let album = userData["album"] as? Album else { return }
|
||||||
|
sendPlayAlbum(album)
|
||||||
|
case .getAlbumURI:
|
||||||
|
guard let album = userData["album"] as? Album else { return }
|
||||||
|
_ = getAlbumURI(for: album)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
Persephone/MPDClient/Extensions/MPDClient+Connection.swift
Normal file
50
Persephone/MPDClient/Extensions/MPDClient+Connection.swift
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
//
|
||||||
|
// Connection.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2019/3/15.
|
||||||
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import mpdclient
|
||||||
|
|
||||||
|
extension MPDClient {
|
||||||
|
func connect(host: String, port: Int) {
|
||||||
|
commandQueue.addOperation { [unowned self] in
|
||||||
|
guard let connection = mpd_connection_new(host, UInt32(port), 10000),
|
||||||
|
mpd_connection_get_error(connection) == MPD_ERROR_SUCCESS
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
self.isConnected = true
|
||||||
|
|
||||||
|
guard let status = mpd_run_status(connection)
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
self.connection = connection
|
||||||
|
self.status = Status(status)
|
||||||
|
|
||||||
|
self.fetchQueue()
|
||||||
|
self.fetchAllAlbums()
|
||||||
|
self.idle()
|
||||||
|
|
||||||
|
self.delegate?.didConnect(mpdClient: self)
|
||||||
|
self.delegate?.didUpdateState(mpdClient: self, state: self.status!.state)
|
||||||
|
self.delegate?.didUpdateTime(mpdClient: self, total: self.status!.totalTime, elapsedMs: self.status!.elapsedTimeMs)
|
||||||
|
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
|
||||||
|
self.delegate?.didUpdateQueuePos(mpdClient: self, song: self.status!.song)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func disconnect() {
|
||||||
|
guard isConnected else { return }
|
||||||
|
|
||||||
|
noIdle()
|
||||||
|
commandQueue.addOperation { [unowned self] in
|
||||||
|
self.delegate?.willDisconnect(mpdClient: self)
|
||||||
|
|
||||||
|
mpd_connection_free(self.connection)
|
||||||
|
self.isConnected = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
Persephone/MPDClient/Extensions/MPDClient+Error.swift
Normal file
24
Persephone/MPDClient/Extensions/MPDClient+Error.swift
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
//
|
||||||
|
// Error.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2019/3/15.
|
||||||
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import mpdclient
|
||||||
|
|
||||||
|
extension MPDClient {
|
||||||
|
func getLastErrorMessage() -> String? {
|
||||||
|
if mpd_connection_get_error(connection) == MPD_ERROR_SUCCESS {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if let errorMessage = mpd_connection_get_error_message(connection) {
|
||||||
|
return String(cString: errorMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
56
Persephone/MPDClient/Extensions/MPDClient+Idle.swift
Normal file
56
Persephone/MPDClient/Extensions/MPDClient+Idle.swift
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
//
|
||||||
|
// Idle.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2019/3/15.
|
||||||
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import mpdclient
|
||||||
|
|
||||||
|
extension MPDClient {
|
||||||
|
func noIdle() {
|
||||||
|
if isIdle {
|
||||||
|
mpd_send_noidle(connection)
|
||||||
|
isIdle = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func idle() {
|
||||||
|
let idleOperation = BlockOperation {
|
||||||
|
if !self.isIdle && self.commandsQueued == 0 {
|
||||||
|
mpd_send_idle(self.connection)
|
||||||
|
self.isIdle = true
|
||||||
|
|
||||||
|
let result = mpd_recv_idle(self.connection, true)
|
||||||
|
self.handleIdleResult(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
idleOperation.queuePriority = .veryLow
|
||||||
|
commandQueue.addOperation(idleOperation)
|
||||||
|
}
|
||||||
|
|
||||||
|
func handleIdleResult(_ result: mpd_idle) {
|
||||||
|
isIdle = false
|
||||||
|
|
||||||
|
let mpdIdle = Idle(rawValue: result.rawValue)
|
||||||
|
|
||||||
|
if mpdIdle.contains(.queue) {
|
||||||
|
self.fetchQueue()
|
||||||
|
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
|
||||||
|
}
|
||||||
|
if mpdIdle.contains(.player) {
|
||||||
|
self.fetchStatus()
|
||||||
|
|
||||||
|
if let status = self.status {
|
||||||
|
self.delegate?.didUpdateState(mpdClient: self, state: status.state)
|
||||||
|
self.delegate?.didUpdateTime(mpdClient: self, total: status.totalTime, elapsedMs: status.elapsedTimeMs)
|
||||||
|
self.delegate?.didUpdateQueuePos(mpdClient: self, song: status.song)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !mpdIdle.isEmpty {
|
||||||
|
self.idle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
38
Persephone/MPDClient/Extensions/MPDClient+Queue.swift
Normal file
38
Persephone/MPDClient/Extensions/MPDClient+Queue.swift
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
//
|
||||||
|
// Queue.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2019/3/15.
|
||||||
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import mpdclient
|
||||||
|
|
||||||
|
extension MPDClient {
|
||||||
|
func fetchQueue() {
|
||||||
|
sendCommand(command: .fetchQueue)
|
||||||
|
}
|
||||||
|
|
||||||
|
func playTrack(queuePos: Int) {
|
||||||
|
guard isConnected else { return }
|
||||||
|
|
||||||
|
noIdle()
|
||||||
|
let commandOperation = BlockOperation { [unowned self] in
|
||||||
|
mpd_run_play_pos(self.connection, UInt32(queuePos))
|
||||||
|
}
|
||||||
|
commandOperation.queuePriority = .veryHigh
|
||||||
|
commandQueue.addOperation(commandOperation)
|
||||||
|
idle()
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendFetchQueue() {
|
||||||
|
self.queue = []
|
||||||
|
mpd_send_list_queue_meta(connection)
|
||||||
|
|
||||||
|
while let mpdSong = mpd_recv_song(connection) {
|
||||||
|
let song = Song(mpdSong)
|
||||||
|
self.queue.append(song)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Persephone/MPDClient/Extensions/MPDClient+Status.swift
Normal file
21
Persephone/MPDClient/Extensions/MPDClient+Status.swift
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// Status.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2019/3/15.
|
||||||
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import mpdclient
|
||||||
|
|
||||||
|
extension MPDClient {
|
||||||
|
func fetchStatus() {
|
||||||
|
sendCommand(command: .fetchStatus)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendRunStatus() {
|
||||||
|
guard let status = mpd_run_status(connection) else { return }
|
||||||
|
self.status = Status(status)
|
||||||
|
}
|
||||||
|
}
|
||||||
65
Persephone/MPDClient/Extensions/MPDClient+Transport.swift
Normal file
65
Persephone/MPDClient/Extensions/MPDClient+Transport.swift
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
//
|
||||||
|
// Transport.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2019/3/15.
|
||||||
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import mpdclient
|
||||||
|
|
||||||
|
extension MPDClient {
|
||||||
|
func playPause() {
|
||||||
|
queueCommand(command: .playPause)
|
||||||
|
}
|
||||||
|
|
||||||
|
func stop() {
|
||||||
|
queueCommand(command: .stop)
|
||||||
|
}
|
||||||
|
|
||||||
|
func prevTrack() {
|
||||||
|
queueCommand(command: .prevTrack)
|
||||||
|
}
|
||||||
|
|
||||||
|
func nextTrack() {
|
||||||
|
queueCommand(command: .nextTrack)
|
||||||
|
}
|
||||||
|
|
||||||
|
func seekCurrentSong(timeInSeconds: Float) {
|
||||||
|
noIdle()
|
||||||
|
commandQueue.addOperation { [unowned self] in
|
||||||
|
mpd_run_seek_current(self.connection, timeInSeconds, false)
|
||||||
|
}
|
||||||
|
idle()
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendNextTrack() {
|
||||||
|
guard let state = status?.state,
|
||||||
|
state.isOneOf([.playing, .paused])
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
mpd_run_next(connection)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendPreviousTrack() {
|
||||||
|
guard let state = status?.state,
|
||||||
|
state.isOneOf([.playing, .paused])
|
||||||
|
else { return }
|
||||||
|
|
||||||
|
mpd_run_previous(connection)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendStop() {
|
||||||
|
mpd_run_stop(connection)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sendPlay() {
|
||||||
|
if status?.state == .stopped {
|
||||||
|
mpd_run_play(connection)
|
||||||
|
} else {
|
||||||
|
mpd_run_toggle_pause(connection)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -12,285 +12,41 @@ import mpdclient
|
|||||||
class MPDClient {
|
class MPDClient {
|
||||||
var delegate: MPDClientDelegate?
|
var delegate: MPDClientDelegate?
|
||||||
|
|
||||||
private var connection: OpaquePointer?
|
var connection: OpaquePointer?
|
||||||
private var isConnected: Bool = false
|
var isConnected: Bool = false
|
||||||
private var status: Status?
|
var isIdle: Bool = false
|
||||||
private var queue: [Song] = []
|
var status: Status?
|
||||||
|
var queue: [Song] = []
|
||||||
|
|
||||||
private let commandQueue = DispatchQueue(label: "commandQueue")
|
let commandQueue = OperationQueue()
|
||||||
|
var commandsQueued: UInt = 0
|
||||||
|
|
||||||
enum Command {
|
enum Command {
|
||||||
case prevTrack, nextTrack, playPause, stop,
|
case prevTrack, nextTrack, playPause, stop,
|
||||||
fetchStatus, fetchQueue, fetchAllAlbums
|
fetchStatus, fetchQueue, fetchAllAlbums,
|
||||||
}
|
playAlbum, getAlbumURI
|
||||||
|
|
||||||
struct Idle: OptionSet {
|
|
||||||
let rawValue: UInt32
|
|
||||||
|
|
||||||
static let database = Idle(rawValue: 0x1)
|
|
||||||
static let storedPlaylist = Idle(rawValue: 0x2)
|
|
||||||
static let queue = Idle(rawValue: 0x4)
|
|
||||||
static let player = Idle(rawValue: 0x8)
|
|
||||||
static let mixer = Idle(rawValue: 0x10)
|
|
||||||
static let output = Idle(rawValue: 0x20)
|
|
||||||
static let options = Idle(rawValue: 0x40)
|
|
||||||
static let update = Idle(rawValue: 0x80)
|
|
||||||
static let sticker = Idle(rawValue: 0x100)
|
|
||||||
static let subscription = Idle(rawValue: 0x200)
|
|
||||||
static let message = Idle(rawValue: 0x400)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
init(withDelegate delegate: MPDClientDelegate?) {
|
init(withDelegate delegate: MPDClientDelegate?) {
|
||||||
|
commandQueue.maxConcurrentOperationCount = 1
|
||||||
self.delegate = delegate
|
self.delegate = delegate
|
||||||
}
|
}
|
||||||
|
|
||||||
func connect(host: String, port: Int) {
|
func queueCommand(
|
||||||
commandQueue.async { [unowned self] in
|
command: Command,
|
||||||
guard let connection = mpd_connection_new(host, UInt32(port), 10000),
|
priority: BlockOperation.QueuePriority = .normal,
|
||||||
mpd_connection_get_error(connection) == MPD_ERROR_SUCCESS
|
userData: Dictionary<String, Any> = [:]
|
||||||
else { return }
|
) {
|
||||||
|
|
||||||
self.isConnected = true
|
|
||||||
|
|
||||||
guard let status = mpd_run_status(connection)
|
|
||||||
else { return }
|
|
||||||
|
|
||||||
self.connection = connection
|
|
||||||
self.status = Status(status)
|
|
||||||
|
|
||||||
self.fetchQueue()
|
|
||||||
self.fetchAllAlbums()
|
|
||||||
self.idle()
|
|
||||||
|
|
||||||
self.delegate?.didConnect(mpdClient: self)
|
|
||||||
self.delegate?.didUpdateState(mpdClient: self, state: self.status!.state)
|
|
||||||
self.delegate?.didUpdateTime(mpdClient: self, total: self.status!.totalTime, elapsedMs: self.status!.elapsedTimeMs)
|
|
||||||
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
|
|
||||||
self.delegate?.didUpdateQueuePos(mpdClient: self, song: self.status!.song)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func disconnect() {
|
|
||||||
guard isConnected else { return }
|
guard isConnected else { return }
|
||||||
|
|
||||||
noIdle()
|
noIdle()
|
||||||
commandQueue.async { [unowned self] in
|
let commandOperation = BlockOperation() { [unowned self] in
|
||||||
self.delegate?.willDisconnect(mpdClient: self)
|
self.commandsQueued -= 1
|
||||||
|
self.sendCommand(command: command, userData: userData)
|
||||||
mpd_connection_free(self.connection)
|
|
||||||
self.isConnected = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchStatus() {
|
|
||||||
sendCommand(command: .fetchStatus)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchQueue() {
|
|
||||||
sendCommand(command: .fetchQueue)
|
|
||||||
}
|
|
||||||
|
|
||||||
func fetchAllAlbums() {
|
|
||||||
sendCommand(command: .fetchAllAlbums)
|
|
||||||
}
|
|
||||||
|
|
||||||
func playPause() {
|
|
||||||
queueCommand(command: .playPause)
|
|
||||||
}
|
|
||||||
|
|
||||||
func stop() {
|
|
||||||
queueCommand(command: .stop)
|
|
||||||
}
|
|
||||||
|
|
||||||
func prevTrack() {
|
|
||||||
queueCommand(command: .prevTrack)
|
|
||||||
}
|
|
||||||
|
|
||||||
func nextTrack() {
|
|
||||||
queueCommand(command: .nextTrack)
|
|
||||||
}
|
|
||||||
|
|
||||||
func playTrack(queuePos: Int) {
|
|
||||||
guard isConnected else { return }
|
|
||||||
|
|
||||||
noIdle()
|
|
||||||
commandQueue.async { [unowned self] in
|
|
||||||
mpd_run_play_pos(self.connection, UInt32(queuePos))
|
|
||||||
}
|
}
|
||||||
|
commandOperation.queuePriority = priority
|
||||||
|
commandsQueued += 1
|
||||||
|
commandQueue.addOperation(commandOperation)
|
||||||
idle()
|
idle()
|
||||||
}
|
}
|
||||||
|
|
||||||
func seekCurrentSong(timeInSeconds: Float) {
|
|
||||||
noIdle()
|
|
||||||
commandQueue.async { [unowned self] in
|
|
||||||
mpd_run_seek_current(self.connection, timeInSeconds, false)
|
|
||||||
}
|
|
||||||
idle()
|
|
||||||
}
|
|
||||||
|
|
||||||
func playAlbum(_ album: Album) {
|
|
||||||
noIdle()
|
|
||||||
commandQueue.async { [unowned self] in
|
|
||||||
var songs: [Song] = []
|
|
||||||
|
|
||||||
mpd_run_clear(self.connection)
|
|
||||||
mpd_search_db_songs(self.connection, true)
|
|
||||||
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM, album.title)
|
|
||||||
mpd_search_add_tag_constraint(self.connection, MPD_OPERATOR_DEFAULT, MPD_TAG_ALBUM_ARTIST, album.artist)
|
|
||||||
mpd_search_commit(self.connection)
|
|
||||||
while let mpdSong = mpd_recv_song(self.connection) {
|
|
||||||
songs.append(Song(mpdSong))
|
|
||||||
}
|
|
||||||
for song in songs {
|
|
||||||
mpd_run_add(self.connection, song.uri)
|
|
||||||
}
|
|
||||||
mpd_run_play_pos(self.connection, 0)
|
|
||||||
}
|
|
||||||
idle()
|
|
||||||
}
|
|
||||||
|
|
||||||
func queueCommand(command: Command) {
|
|
||||||
guard isConnected else { return }
|
|
||||||
|
|
||||||
noIdle()
|
|
||||||
commandQueue.async { [unowned self] in
|
|
||||||
self.sendCommand(command: command)
|
|
||||||
}
|
|
||||||
idle()
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendCommand(command: Command) {
|
|
||||||
switch command {
|
|
||||||
|
|
||||||
// Transport commands
|
|
||||||
case .prevTrack:
|
|
||||||
sendPreviousTrack()
|
|
||||||
case .nextTrack:
|
|
||||||
sendNextTrack()
|
|
||||||
case .stop:
|
|
||||||
sendStop()
|
|
||||||
case .playPause:
|
|
||||||
sendPlay()
|
|
||||||
case .fetchStatus:
|
|
||||||
sendRunStatus()
|
|
||||||
case .fetchQueue:
|
|
||||||
sendFetchQueue()
|
|
||||||
case .fetchAllAlbums:
|
|
||||||
allAlbums()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendNextTrack() {
|
|
||||||
guard let state = status?.state,
|
|
||||||
state.isOneOf([.playing, .paused])
|
|
||||||
else { return }
|
|
||||||
|
|
||||||
mpd_run_next(connection)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendPreviousTrack() {
|
|
||||||
guard let state = status?.state,
|
|
||||||
state.isOneOf([.playing, .paused])
|
|
||||||
else { return }
|
|
||||||
|
|
||||||
mpd_run_previous(connection)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendStop() {
|
|
||||||
mpd_run_stop(connection)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendPlay() {
|
|
||||||
if status?.state == .stopped {
|
|
||||||
mpd_run_play(connection)
|
|
||||||
} else {
|
|
||||||
mpd_run_toggle_pause(connection)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendRunStatus() {
|
|
||||||
guard let status = mpd_run_status(connection) else { return }
|
|
||||||
self.status = Status(status)
|
|
||||||
}
|
|
||||||
|
|
||||||
func sendFetchQueue() {
|
|
||||||
self.queue = []
|
|
||||||
mpd_send_list_queue_meta(connection)
|
|
||||||
|
|
||||||
while let mpdSong = mpd_recv_song(connection) {
|
|
||||||
let song = Song(mpdSong)
|
|
||||||
self.queue.append(song)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func allAlbums() {
|
|
||||||
var albums: [Album] = []
|
|
||||||
var artist: String = ""
|
|
||||||
|
|
||||||
mpd_search_db_tags(connection, MPD_TAG_ALBUM)
|
|
||||||
mpd_search_add_group_tag(connection, MPD_TAG_ALBUM_ARTIST)
|
|
||||||
mpd_search_commit(connection)
|
|
||||||
while let mpdPair = mpd_recv_pair(connection) {
|
|
||||||
let pair = Pair(mpdPair)
|
|
||||||
|
|
||||||
switch pair.name {
|
|
||||||
case "AlbumArtist":
|
|
||||||
artist = pair.value
|
|
||||||
case "Album":
|
|
||||||
albums.append(Album(title: pair.value, artist: artist))
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
mpd_return_pair(connection, pair.mpdPair)
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate?.didLoadAlbums(mpdClient: self, albums: albums)
|
|
||||||
}
|
|
||||||
|
|
||||||
func noIdle() {
|
|
||||||
mpd_send_noidle(connection)
|
|
||||||
}
|
|
||||||
|
|
||||||
func idle() {
|
|
||||||
commandQueue.async { [unowned self] in
|
|
||||||
mpd_send_idle(self.connection)
|
|
||||||
|
|
||||||
let result = mpd_recv_idle(self.connection, true)
|
|
||||||
self.handleIdleResult(result)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func handleIdleResult(_ result: mpd_idle) {
|
|
||||||
let mpdIdle = Idle(rawValue: result.rawValue)
|
|
||||||
|
|
||||||
if mpdIdle.contains(.queue) {
|
|
||||||
self.fetchQueue()
|
|
||||||
self.delegate?.didUpdateQueue(mpdClient: self, queue: self.queue)
|
|
||||||
}
|
|
||||||
if mpdIdle.contains(.player) {
|
|
||||||
self.fetchStatus()
|
|
||||||
|
|
||||||
if let status = self.status {
|
|
||||||
self.delegate?.didUpdateState(mpdClient: self, state: status.state)
|
|
||||||
self.delegate?.didUpdateTime(mpdClient: self, total: status.totalTime, elapsedMs: status.elapsedTimeMs)
|
|
||||||
self.delegate?.didUpdateQueuePos(mpdClient: self, song: status.song)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !mpdIdle.isEmpty {
|
|
||||||
self.idle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getLastErrorMessage() -> String? {
|
|
||||||
if mpd_connection_get_error(connection) == MPD_ERROR_SUCCESS {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if let errorMessage = mpd_connection_get_error_message(connection) {
|
|
||||||
return String(cString: errorMessage)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -45,6 +45,10 @@ extension MPDClient {
|
|||||||
return mpd_song_get_uri(mpdSong)
|
return mpd_song_get_uri(mpdSong)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var uriString: String {
|
||||||
|
return String(cString: uri)
|
||||||
|
}
|
||||||
|
|
||||||
func getTag(_ tagType: TagType) -> String {
|
func getTag(_ tagType: TagType) -> String {
|
||||||
let mpdTagType = mpd_tag_type(rawValue: Int32(tagType.rawValue))
|
let mpdTagType = mpd_tag_type(rawValue: Int32(tagType.rawValue))
|
||||||
|
|
||||||
|
|||||||
@ -9,6 +9,10 @@
|
|||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
struct Preferences {
|
struct Preferences {
|
||||||
|
let mpdHostDefault = "127.0.0.1"
|
||||||
|
let mpdPortDefault = 6600
|
||||||
|
let mpdLibraryDirDefault = "~/Music"
|
||||||
|
|
||||||
let preferences = UserDefaults.standard
|
let preferences = UserDefaults.standard
|
||||||
|
|
||||||
var mpdHost: String? {
|
var mpdHost: String? {
|
||||||
@ -20,6 +24,10 @@ struct Preferences {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mpdHostOrDefault: String {
|
||||||
|
return mpdHost ?? mpdHostDefault
|
||||||
|
}
|
||||||
|
|
||||||
var mpdPort: Int? {
|
var mpdPort: Int? {
|
||||||
get {
|
get {
|
||||||
return preferences.value(forKey: "mpdPort") as? Int
|
return preferences.value(forKey: "mpdPort") as? Int
|
||||||
@ -33,12 +41,21 @@ struct Preferences {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var mpdHostOrDefault: String {
|
var mpdPortOrDefault: Int {
|
||||||
return mpdHost ?? "127.0.0.1"
|
return mpdPort ?? mpdPortDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
var mpdPortOrDefault: Int {
|
var mpdLibraryDir: String? {
|
||||||
return mpdPort ?? 6600
|
get {
|
||||||
|
return preferences.string(forKey: "mpdLibraryDir")
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
preferences.set(newValue, forKey: "mpdLibraryDir")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mpdLibraryDirOrDefault: String {
|
||||||
|
return mpdLibraryDir ?? mpdLibraryDirDefault
|
||||||
}
|
}
|
||||||
|
|
||||||
func addObserver(_ observer: NSObject, forKeyPath keyPath: String) {
|
func addObserver(_ observer: NSObject, forKeyPath keyPath: String) {
|
||||||
|
|||||||
@ -9,9 +9,29 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class AlbumArtPrefsController: NSViewController {
|
class AlbumArtPrefsController: NSViewController {
|
||||||
|
var preferences = Preferences()
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
super.viewDidLoad()
|
super.viewDidLoad()
|
||||||
|
|
||||||
|
if let mpdLibraryDir = preferences.mpdLibraryDir {
|
||||||
|
mpdLibraryDirField.stringValue = mpdLibraryDir
|
||||||
|
}
|
||||||
|
|
||||||
preferredContentSize = NSMakeSize(view.frame.size.width, view.frame.size.height)
|
preferredContentSize = NSMakeSize(view.frame.size.width, view.frame.size.height)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func viewDidAppear() {
|
||||||
|
super.viewDidAppear()
|
||||||
|
|
||||||
|
guard let title = title
|
||||||
|
else { return }
|
||||||
|
self.parent?.view.window?.title = title
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBAction func updateMpdLibraryDir(_ sender: NSTextField) {
|
||||||
|
preferences.mpdLibraryDir = sender.stringValue
|
||||||
|
}
|
||||||
|
|
||||||
|
@IBOutlet var mpdLibraryDirField: NSTextField!
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,6 +27,10 @@ class GeneralPrefsViewController: NSViewController {
|
|||||||
|
|
||||||
override func viewDidAppear() {
|
override func viewDidAppear() {
|
||||||
super.viewDidAppear()
|
super.viewDidAppear()
|
||||||
|
|
||||||
|
guard let title = title
|
||||||
|
else { return }
|
||||||
|
self.parent?.view.window?.title = title
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction func updateMpdHost(_ sender: NSTextField) {
|
@IBAction func updateMpdHost(_ sender: NSTextField) {
|
||||||
|
|||||||
@ -12,45 +12,48 @@ class PreferencesViewController: NSTabViewController {
|
|||||||
private lazy var tabViewSizes: [String : NSSize] = [:]
|
private lazy var tabViewSizes: [String : NSSize] = [:]
|
||||||
|
|
||||||
override func viewDidLoad() {
|
override func viewDidLoad() {
|
||||||
|
super.viewDidLoad()
|
||||||
|
|
||||||
if let viewController = self.tabViewItems.first?.viewController, let title = viewController.title {
|
if let viewController = self.tabViewItems.first?.viewController, let title = viewController.title {
|
||||||
tabViewSizes[title] = viewController.view.frame.size
|
tabViewSizes[title] = viewController.view.frame.size
|
||||||
}
|
}
|
||||||
|
|
||||||
super.viewDidLoad()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// override func transition(from fromViewController: NSViewController, to toViewController: NSViewController, options: NSViewController.TransitionOptions, completionHandler completion: (() -> Void)?) {
|
override func transition(from fromViewController: NSViewController, to toViewController: NSViewController, options: NSViewController.TransitionOptions, completionHandler completion: (() -> Void)?) {
|
||||||
// NSAnimationContext.runAnimationGroup({ context in
|
NSAnimationContext.runAnimationGroup({ context in
|
||||||
// context.duration = 0.5
|
context.duration = 0.25
|
||||||
// self.updateWindowFrameAnimated(viewController: toViewController)
|
|
||||||
// super.transition(
|
self.updateWindowFrameAnimated(viewController: toViewController)
|
||||||
// from: fromViewController,
|
|
||||||
// to: toViewController,
|
super.transition(
|
||||||
// options: [.crossfade, .allowUserInteraction],
|
from: fromViewController,
|
||||||
// completionHandler: completion
|
to: toViewController,
|
||||||
// )
|
options: [.crossfade, .allowUserInteraction],
|
||||||
// }, completionHandler: nil)
|
completionHandler: completion
|
||||||
// }
|
)
|
||||||
//
|
}, completionHandler: nil)
|
||||||
// func updateWindowFrameAnimated(viewController: NSViewController) {
|
}
|
||||||
// guard let title = viewController.title, let window = view.window
|
|
||||||
// else { return }
|
func updateWindowFrameAnimated(viewController: NSViewController) {
|
||||||
//
|
guard let title = viewController.title,
|
||||||
// let contentSize: NSSize
|
let window = view.window
|
||||||
//
|
else { return }
|
||||||
// if tabViewSizes.keys.contains(title) {
|
|
||||||
// contentSize = tabViewSizes[title]!
|
let contentSize: NSSize
|
||||||
// } else {
|
|
||||||
// contentSize = viewController.view.frame.size
|
if tabViewSizes.keys.contains(title) {
|
||||||
// tabViewSizes[title] = contentSize
|
contentSize = tabViewSizes[title]!
|
||||||
// }
|
} else {
|
||||||
//
|
contentSize = viewController.view.frame.size
|
||||||
// let newWindowSize = window.frameRect(forContentRect: NSRect(origin: NSPoint.zero, size: contentSize)).size
|
tabViewSizes[title] = contentSize
|
||||||
//
|
}
|
||||||
// var frame = window.frame
|
|
||||||
// frame.origin.y += frame.height
|
let newWindowSize = window.frameRect(forContentRect: NSRect(origin: NSPoint.zero, size: contentSize)).size
|
||||||
// frame.origin.y -= newWindowSize.height
|
|
||||||
// frame.size = newWindowSize
|
var frame = window.frame
|
||||||
// window.animator().setFrame(frame, display: false)
|
frame.origin.y += frame.height
|
||||||
// }
|
frame.origin.y -= newWindowSize.height
|
||||||
|
frame.size = newWindowSize
|
||||||
|
window.animator().setFrame(frame, display: false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// PreferencesWindowController.swift
|
||||||
|
// Persephone
|
||||||
|
//
|
||||||
|
// Created by Daniel Barber on 2019/3/09.
|
||||||
|
// Copyright © 2019 Dan Barber. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class PreferencesWindowController: NSWindowController, NSWindowDelegate {
|
||||||
|
override func windowDidLoad() {
|
||||||
|
super.windowDidLoad()
|
||||||
|
}
|
||||||
|
|
||||||
|
func windowShouldClose(_ sender: NSWindow) -> Bool {
|
||||||
|
self.window?.orderOut(sender)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -249,7 +249,7 @@
|
|||||||
<!--Window Controller-->
|
<!--Window Controller-->
|
||||||
<scene sceneID="Rpk-bo-5kf">
|
<scene sceneID="Rpk-bo-5kf">
|
||||||
<objects>
|
<objects>
|
||||||
<windowController id="xYu-7w-E5x" sceneMemberID="viewController">
|
<windowController id="xYu-7w-E5x" customClass="PreferencesWindowController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<window key="window" title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="3FN-my-6kU">
|
<window key="window" title="Preferences" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" id="3FN-my-6kU">
|
||||||
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
<windowStyleMask key="styleMask" titled="YES" closable="YES"/>
|
||||||
<rect key="contentRect" x="245" y="301" width="416" height="100"/>
|
<rect key="contentRect" x="245" y="301" width="416" height="100"/>
|
||||||
@ -293,54 +293,63 @@
|
|||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="916" y="236"/>
|
<point key="canvasLocation" x="916" y="236"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--Album Art Prefs Controller-->
|
<!--Album Art-->
|
||||||
<scene sceneID="pQx-0G-WVt">
|
<scene sceneID="pQx-0G-WVt">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="3C9-vU-zjZ" customClass="AlbumArtPrefsController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController title="Album Art" id="3C9-vU-zjZ" customClass="AlbumArtPrefsController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" id="PyK-v2-kus">
|
<view key="view" id="PyK-v2-kus">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="524" height="100"/>
|
<rect key="frame" x="0.0" y="0.0" width="524" height="100"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
<subviews>
|
<subviews>
|
||||||
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zZn-Rm-e1f">
|
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zZn-Rm-e1f">
|
||||||
<rect key="frame" x="52" y="63" 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">
|
<textFieldCell key="cell" lineBreakMode="clipping" title="Music Directory:" id="sPn-V6-CfK">
|
||||||
<font key="font" usesAppearanceFont="YES"/>
|
<font key="font" usesAppearanceFont="YES"/>
|
||||||
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
</textField>
|
</textField>
|
||||||
<textField verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="gDk-ca-eOa">
|
<textField verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gDk-ca-eOa">
|
||||||
<rect key="frame" x="162" y="58" width="288" height="22"/>
|
<rect key="frame" x="162" y="58" width="288" height="22"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="~/Music" drawsBackground="YES" id="7WZ-b7-GUs">
|
||||||
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" borderStyle="bezel" placeholderString="MPD music library path" drawsBackground="YES" id="7WZ-b7-GUs">
|
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
|
||||||
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
|
||||||
</textFieldCell>
|
</textFieldCell>
|
||||||
|
<connections>
|
||||||
|
<action selector="updateMpdLibraryDir:" target="3C9-vU-zjZ" id="3Ta-fH-5Zh"/>
|
||||||
|
</connections>
|
||||||
</textField>
|
</textField>
|
||||||
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="pRL-MG-1Be">
|
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="pRL-MG-1Be">
|
||||||
<rect key="frame" x="160" y="27" width="253" height="18"/>
|
<rect key="frame" x="160" y="27" width="265" height="18"/>
|
||||||
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
|
<buttonCell key="cell" type="check" title="Fetch missing artwork from MusicBrainz" bezelStyle="regularSquare" imagePosition="left" inset="2" id="LpD-Ew-HMd">
|
||||||
<buttonCell key="cell" type="check" title="Get missing artwork from MusicBrainz" bezelStyle="regularSquare" imagePosition="left" state="on" inset="2" id="LpD-Ew-HMd">
|
|
||||||
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
|
||||||
<font key="font" metaFont="system"/>
|
<font key="font" metaFont="system"/>
|
||||||
</buttonCell>
|
</buttonCell>
|
||||||
</button>
|
</button>
|
||||||
</subviews>
|
</subviews>
|
||||||
<constraints>
|
<constraints>
|
||||||
<constraint firstItem="zZn-Rm-e1f" firstAttribute="leading" secondItem="PyK-v2-kus" secondAttribute="leading" constant="54" id="GOH-Mx-w1M"/>
|
<constraint firstItem="zZn-Rm-e1f" firstAttribute="leading" secondItem="PyK-v2-kus" secondAttribute="leading" constant="55" id="F9T-mO-lMa"/>
|
||||||
<constraint firstItem="zZn-Rm-e1f" firstAttribute="top" secondItem="PyK-v2-kus" secondAttribute="top" constant="20" symbolic="YES" id="Ou1-BH-AgV"/>
|
<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>
|
</constraints>
|
||||||
</view>
|
</view>
|
||||||
|
<connections>
|
||||||
|
<outlet property="mpdLibraryDirField" destination="gDk-ca-eOa" id="myi-BQ-0NS"/>
|
||||||
|
</connections>
|
||||||
</viewController>
|
</viewController>
|
||||||
<customObject id="KzD-E3-lpA" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
<customObject id="KzD-E3-lpA" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
|
||||||
</objects>
|
</objects>
|
||||||
<point key="canvasLocation" x="1626" y="339"/>
|
<point key="canvasLocation" x="1626" y="339"/>
|
||||||
</scene>
|
</scene>
|
||||||
<!--General Prefs View Controller-->
|
<!--General-->
|
||||||
<scene sceneID="xTC-Y5-Agk">
|
<scene sceneID="xTC-Y5-Agk">
|
||||||
<objects>
|
<objects>
|
||||||
<viewController id="nYi-sw-ZNp" customClass="GeneralPrefsViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
<viewController title="General" id="nYi-sw-ZNp" customClass="GeneralPrefsViewController" customModule="Persephone" customModuleProvider="target" sceneMemberID="viewController">
|
||||||
<view key="view" id="Uwt-Lw-ILP">
|
<view key="view" id="Uwt-Lw-ILP">
|
||||||
<rect key="frame" x="0.0" y="0.0" width="420" height="100"/>
|
<rect key="frame" x="0.0" y="0.0" width="420" height="100"/>
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
<autoresizingMask key="autoresizingMask"/>
|
||||||
|
|||||||
@ -13,13 +13,21 @@ import PMKFoundation
|
|||||||
|
|
||||||
class AlbumArtService: NSObject {
|
class AlbumArtService: NSObject {
|
||||||
static var shared = AlbumArtService()
|
static var shared = AlbumArtService()
|
||||||
|
|
||||||
var session = URLSession(configuration: .default)
|
var session = URLSession(configuration: .default)
|
||||||
let albumArtQueue = DispatchQueue(label: "albumArtCacheQueue", attributes: .concurrent)
|
let cacheQueue = DispatchQueue(label: "albumArtCacheQueue", attributes: .concurrent)
|
||||||
|
let filesystemQueue = DispatchQueue(label: "albumArtFilesystemQueue", attributes: .concurrent)
|
||||||
|
|
||||||
func fetchAlbumArt(for album: AlbumItem, callback: @escaping (_ image: NSImage) -> Void) {
|
func fetchAlbumArt(for album: AlbumItem, callback: @escaping (_ image: NSImage) -> Void) {
|
||||||
albumArtQueue.async {
|
cacheQueue.async { [unowned self] in
|
||||||
|
//print("Trying cache")
|
||||||
if !self.getCachedArtwork(for: album, callback: callback) {
|
if !self.getCachedArtwork(for: album, callback: callback) {
|
||||||
self.getRemoteArtwork(for: album, callback: callback)
|
// self.filesystemQueue.async {
|
||||||
|
// _ = self.getArtworkFromFilesystem(for: album, callback: callback)
|
||||||
|
// }
|
||||||
|
// if !self.getArtworkFromFilesystem(for: album, callback: callback) {
|
||||||
|
// // self.getRemoteArtwork(for: album, callback: callback)
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,6 +53,13 @@ class AlbumArtService: NSObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getArtworkFromFilesystem(for album: AlbumItem, callback: @escaping (_ image: NSImage) -> Void) -> Bool {
|
||||||
|
print("No cache trying filesystem")
|
||||||
|
let uri = AppDelegate.mpdClient.getAlbumURI(for: album.album)
|
||||||
|
print(uri)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
func cacheArtwork(for album: AlbumItem, data: Data) {
|
func cacheArtwork(for album: AlbumItem, data: Data) {
|
||||||
guard let bundleIdentifier = Bundle.main.bundleIdentifier,
|
guard let bundleIdentifier = Bundle.main.bundleIdentifier,
|
||||||
let cacheDir = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
|
let cacheDir = try? FileManager.default.url(for: .cachesDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user