├── .gitignore ├── .gitmodules ├── Audio Players ├── SMKAVPlayerItem.h ├── SMKAVPlayerItem.m ├── SMKAVQueuePlayer.h ├── SMKAVQueuePlayer.m ├── SMKMPMusicPlayer.h ├── SMKMPMusicPlayer.m ├── SMKSFBAudioPlayer.h ├── SMKSFBAudioPlayer.mm ├── SMKSpotifyPlayer.h └── SMKSpotifyPlayer.m ├── Categories ├── NSBundle+SMKAdditions.h ├── NSBundle+SMKAdditions.m ├── NSError+SMKAdditions.h ├── NSError+SMKAdditions.m ├── NSManagedObjectContext+SMKAdditions.h ├── NSManagedObjectContext+SMKAdditions.m ├── NSMutableArray+SMKAdditions.h ├── NSMutableArray+SMKAdditions.m ├── NSObject+AssociatedObjects.h ├── NSObject+AssociatedObjects.m ├── NSString+SMKAdditions.h ├── NSString+SMKAdditions.m ├── NSURL+SMKAdditions.h └── NSURL+SMKAdditions.m ├── Common Headers ├── SMKConvenience.h ├── SMKErrorCodes.h ├── SMKErrorCodes.m ├── SMKGlobalConstants.h ├── SMKGlobalConstants.m └── SMKPlatformImports.h ├── Content Sources ├── 8tracks │ ├── Data Model │ │ ├── SMK8TracksMix.h │ │ ├── SMK8TracksMix.m │ │ ├── SMK8TracksObject.h │ │ ├── SMK8TracksObject.m │ │ ├── SMK8TracksUser.h │ │ └── SMK8TracksUser.m │ ├── SMK8TracksContentSource.h │ └── SMK8TracksContentSource.m ├── MPMediaLibrary │ ├── Data Model │ │ ├── SMKMPMediaAlbum.h │ │ ├── SMKMPMediaAlbum.m │ │ ├── SMKMPMediaArtist.h │ │ ├── SMKMPMediaArtist.m │ │ ├── SMKMPMediaPlaylist.h │ │ ├── SMKMPMediaPlaylist.m │ │ ├── SMKMPMediaPredicate.h │ │ ├── SMKMPMediaPredicate.m │ │ ├── SMKMPMediaTrack.h │ │ └── SMKMPMediaTrack.m │ ├── SMKMPMediaContentSource.h │ ├── SMKMPMediaContentSource.m │ ├── SMKMPMediaHelpers.h │ └── SMKMPMediaHelpers.m ├── Shared │ ├── SMKArtworkCache.h │ ├── SMKArtworkCache.m │ ├── SMKManagedObjectContext.h │ ├── SMKManagedObjectContext.m │ ├── SMKPlatformNativeImage+SMKAdditions.h │ └── SMKPlatformNativeImage+SMKAdditions.m ├── Spotify │ ├── Categories │ │ ├── NSObject+SMKSpotifyAdditions.h │ │ └── NSObject+SMKSpotifyAdditions.m │ ├── Data Model │ │ ├── SPAlbum+SMKAlbum.h │ │ ├── SPAlbum+SMKAlbum.m │ │ ├── SPArtist+SMKArtist.h │ │ ├── SPArtist+SMKArtist.m │ │ ├── SPPlaylist+SMKPlaylist.h │ │ ├── SPPlaylist+SMKPlaylist.m │ │ ├── SPToplist+SMKPlaylist.h │ │ ├── SPToplist+SMKPlaylist.m │ │ ├── SPTrack+SMKTrack.h │ │ ├── SPTrack+SMKTrack.m │ │ ├── SPUser+SMKUser.h │ │ └── SPUser+SMKUser.m │ ├── SMKSpotifyConstants.h │ ├── SMKSpotifyConstants.m │ ├── SMKSpotifyContentSource.h │ ├── SMKSpotifyContentSource.m │ ├── SMKSpotifyHelpers.h │ └── SMKSpotifyHelpers.m └── iTunes │ ├── Categories │ ├── NSManagedObjectContext+SMKiTunesAdditions.h │ └── NSManagedObjectContext+SMKiTunesAdditions.m │ ├── Data Model │ ├── SMKiTunesDataModel.xcdatamodeld │ │ ├── .xccurrentversion │ │ └── SMKiTunesDataModel.xcdatamodel │ │ │ └── contents │ └── mogenerator │ │ ├── SMKITunesAlbum.h │ │ ├── SMKITunesAlbum.m │ │ ├── SMKiTunesArtist.h │ │ ├── SMKiTunesArtist.m │ │ ├── SMKiTunesKeyword.h │ │ ├── SMKiTunesKeyword.m │ │ ├── SMKiTunesObject.h │ │ ├── SMKiTunesObject.m │ │ ├── SMKiTunesPlaylist.h │ │ ├── SMKiTunesPlaylist.m │ │ ├── SMKiTunesTrack.h │ │ ├── SMKiTunesTrack.m │ │ ├── _SMKITunesAlbum.h │ │ ├── _SMKITunesAlbum.m │ │ ├── _SMKiTunesArtist.h │ │ ├── _SMKiTunesArtist.m │ │ ├── _SMKiTunesKeyword.h │ │ ├── _SMKiTunesKeyword.m │ │ ├── _SMKiTunesObject.h │ │ ├── _SMKiTunesObject.m │ │ ├── _SMKiTunesPlaylist.h │ │ ├── _SMKiTunesPlaylist.m │ │ ├── _SMKiTunesTrack.h │ │ └── _SMKiTunesTrack.m │ ├── SMKiTunesConstants.h │ ├── SMKiTunesConstants.m │ ├── SMKiTunesContentSource.h │ ├── SMKiTunesContentSource.m │ ├── SMKiTunesSyncOperation.h │ └── SMKiTunesSyncOperation.m ├── Example Apps ├── 8tracksExample │ ├── 8tracksExample-Info.plist │ ├── 8tracksExample-Prefix.pch │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── en.lproj │ │ ├── Credits.rtf │ │ ├── InfoPlist.strings │ │ └── MainMenu.xib │ └── main.m ├── MPMediaLibraryExample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── DetailViewController.h │ ├── DetailViewController.m │ ├── MPMediaLibraryExample-Info.plist │ ├── MPMediaLibraryExample-Prefix.pch │ ├── MasterViewController.h │ ├── MasterViewController.m │ ├── en.lproj │ │ ├── InfoPlist.strings │ │ ├── MainStoryboard_iPad.storyboard │ │ └── MainStoryboard_iPhone.storyboard │ └── main.m ├── SpotifyExampleMac │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── SpotifyExampleMac-Info.plist │ ├── SpotifyExampleMac-Prefix.pch │ ├── en.lproj │ │ ├── Credits.rtf │ │ ├── InfoPlist.strings │ │ └── MainMenu.xib │ └── main.m └── iTunesExample │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── User Interface │ ├── InsetTextField.h │ └── InsetTextField.m │ ├── en.lproj │ ├── Credits.rtf │ ├── InfoPlist.strings │ └── MainMenu.xib │ ├── iTunesExample-Info.plist │ ├── iTunesExample-Prefix.pch │ └── main.m ├── Master API ├── SMKQueueController.h └── SMKQueueController.m ├── Other Services ├── SMKLastFMClient+SMKTrack.h ├── SMKLastFMClient+SMKTrack.m ├── SMKLastFMClient.h └── SMKLastFMClient.m ├── Protocols ├── SMKAlbum.h ├── SMKArtist.h ├── SMKArtworkObject.h ├── SMKContentObject.h ├── SMKContentSource.h ├── SMKHierarchicalLoading.h ├── SMKPlayer.h ├── SMKPlaylist.h ├── SMKTrack.h ├── SMKUser.h └── SMKWebObject.h ├── README.md ├── SNRMusicKit.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcuserdata │ │ └── indragie.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── indragie.xcuserdatad │ └── xcschemes │ ├── SNRMusicKit.xcscheme │ ├── SNRMusicKitMac.xcscheme │ ├── SNRMusicKitiOS.xcscheme │ └── xcschememanagement.plist ├── SNRMusicKitMac ├── SNRMusicKitMac-Info.plist ├── SNRMusicKitMac-Prefix.pch ├── SNRMusicKitMac.h ├── SNRMusicKitMac.m └── en.lproj │ └── InfoPlist.strings └── SNRMusicKitiOS ├── SNRMusicKitiOS-Prefix.pch └── SNRMusicKitiOS.h /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.swp 3 | *~.nib 4 | build/ 5 | *.pbxuser 6 | *.perspective 7 | *.perspectivev3 8 | *.mode1v3 9 | *.mode2v3 10 | xcuserdata -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "External/SFBAudioEngine"] 2 | path = External/SFBAudioEngine 3 | url = git://github.com/sbooth/SFBAudioEngine.git 4 | [submodule "External/AFNetworking"] 5 | path = External/AFNetworking 6 | url = git://github.com/AFNetworking/AFNetworking.git 7 | [submodule "External/cocoalibspotify"] 8 | path = External/cocoalibspotify 9 | url = git://github.com/indragiek/cocoalibspotify.git 10 | [submodule "External/SSKeychain"] 11 | path = External/SSKeychain 12 | url = git://github.com/samsoffes/sskeychain.git 13 | -------------------------------------------------------------------------------- /Audio Players/SMKAVPlayerItem.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKAVPlayerItem.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKTrack.h" 11 | 12 | /** 13 | Subclass of AVPlayerItem that supports attaching an SMKTrack to it 14 | */ 15 | @interface SMKAVPlayerItem : AVPlayerItem 16 | @property (nonatomic, strong) id SMK_track; 17 | @end 18 | -------------------------------------------------------------------------------- /Audio Players/SMKAVPlayerItem.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKAVPlayerItem.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKAVPlayerItem.h" 10 | 11 | @implementation SMKAVPlayerItem 12 | @end 13 | -------------------------------------------------------------------------------- /Audio Players/SMKAVQueuePlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKAVQueuePlayer.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SMKPlayer.h" 12 | 13 | /** 14 | SMKAVQeuuePlayer is a wrapper for AVQueuePlayer 15 | 16 | It supports all the formats that OS X itself supports, including MP3, MPEG-4, 17 | AIFF, WAVE, and AAC as well as HTTP streaming of audio. 18 | */ 19 | @interface SMKAVQueuePlayer : NSObject 20 | 21 | #pragma mark - SMKPlayer API 22 | @property (nonatomic, copy) void (^finishedTrackBlock)(id player, id track, NSError *error); 23 | @property (nonatomic, strong, readonly) AVQueuePlayer *audioPlayer; 24 | 25 | @property (nonatomic, assign) NSTimeInterval seekTimeInterval; 26 | @property (nonatomic, assign, readonly) NSTimeInterval playbackTime; 27 | @property (nonatomic, assign, readonly) BOOL playing; 28 | @property (nonatomic, strong, readonly) id currentTrack; 29 | 30 | #if !TARGET_OS_IPHONE 31 | @property (nonatomic, assign) float volume; 32 | #endif 33 | 34 | - (void)pause; 35 | - (void)play; 36 | 37 | // Preloading (SMKPlayer @optional) 38 | @property (nonatomic, strong, readonly) id preloadedTrack; 39 | - (void)preloadTrack:(id)track completionHandler:(void(^)(NSError *error))handler; 40 | - (id)preloadedTrack; 41 | - (void)skipToPreloadedTrack; 42 | @end 43 | -------------------------------------------------------------------------------- /Audio Players/SMKMPMusicPlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMusicPlayer.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-28. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SMKPlayer.h" 12 | 13 | /** 14 | SMKMPMusicPlayer is a wrapper for MPMusicPlayerController. It's implemented because 15 | AVQueuePlayer doesn't support iTunes Match streaming/downloading. 16 | 17 | SMKMPMediaTrack will automatically use this player when it's a track on the cloud 18 | */ 19 | @interface SMKMPMusicPlayer : NSObject 20 | 21 | @property (nonatomic, copy) void (^finishedTrackBlock)(id player, id track, NSError *error); 22 | @property (nonatomic, strong, readonly) MPMusicPlayerController *audioPlayer; 23 | 24 | @property (nonatomic, assign) NSTimeInterval seekTimeInterval; 25 | @property (nonatomic, assign, readonly) NSTimeInterval playbackTime; 26 | @property (nonatomic, assign, readonly) BOOL playing; 27 | @property (nonatomic, strong, readonly) id currentTrack; 28 | 29 | - (void)pause; 30 | - (void)play; 31 | @end 32 | -------------------------------------------------------------------------------- /Audio Players/SMKMPMusicPlayer.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMusicPlayer.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-28. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKMPMusicPlayer.h" 10 | #import "SMKMPMediaTrack.h" 11 | 12 | #import "SMKErrorCodes.h" 13 | #import "NSError+SMKAdditions.h" 14 | 15 | @interface SMKMPMusicPlayer () 16 | @property (nonatomic, strong, readwrite) MPMusicPlayerController *audioPlayer; 17 | @property (nonatomic, strong, readwrite) MPMusicPlayerController *preloadAudioPlayer; 18 | @property (nonatomic, assign, readwrite) BOOL playing; 19 | @property (nonatomic, strong) id oldCurrentTrack; 20 | @property (nonatomic, strong) id preloadedTrack; 21 | @end 22 | 23 | @implementation SMKMPMusicPlayer 24 | - (id)init 25 | { 26 | if ((self = [super init])) { 27 | self.audioPlayer = [MPMusicPlayerController iPodMusicPlayer]; 28 | NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; 29 | [nc addObserver:self selector:@selector(_playbackStateDidChange:) name:MPMusicPlayerControllerPlaybackStateDidChangeNotification object:self.audioPlayer]; 30 | [nc addObserver:self selector:@selector(_nowPlayingItemDidChange:) name:MPMusicPlayerControllerNowPlayingItemDidChangeNotification object:self.audioPlayer]; 31 | [self.audioPlayer beginGeneratingPlaybackNotifications]; 32 | self.audioPlayer.repeatMode = MPMusicRepeatModeNone; 33 | self.audioPlayer.shuffleMode = MPMusicShuffleModeOff; 34 | self.seekTimeInterval = SMKPlayerDefaultSeekTimeInterval; 35 | } 36 | return self; 37 | } 38 | 39 | - (void)dealloc 40 | { 41 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 42 | } 43 | 44 | #pragma mark - SMKPlayer 45 | 46 | + (NSSet *)supportedContentSourceClasses 47 | { 48 | return [NSSet setWithObject:NSClassFromString(@"SMKMPMediaContentSource")]; 49 | } 50 | 51 | + (BOOL)supportsPreloading 52 | { 53 | return NO; 54 | } 55 | 56 | - (BOOL)supportsSeeking 57 | { 58 | return YES; 59 | } 60 | 61 | - (id)currentTrack 62 | { 63 | return self.oldCurrentTrack; 64 | } 65 | 66 | - (void)playTrack:(id)track completionHandler:(void(^)(NSError *error))handler 67 | { 68 | if ([track isKindOfClass:[SMKMPMediaTrack class]]) { 69 | SMKMPMediaTrack *mediaTrack = (SMKMPMediaTrack *)track; 70 | MPMediaItem *mediaItem = mediaTrack.representedObject; 71 | MPMediaItemCollection *collection = [MPMediaItemCollection collectionWithItems:@[mediaItem]]; 72 | [self.audioPlayer setQueueWithItemCollection:collection]; 73 | [self.audioPlayer play]; 74 | if (handler) { handler(nil); } 75 | } else if (handler) { 76 | NSError *error = [NSError SMK_errorWithCode:SMKPlayerErrorFailedToEnqueueTrack description:@"MPMusicPlayerController failed to play the track because the object was of the wrong type. Expected SMKMPMediaTrack"]; 77 | handler(error); 78 | } 79 | } 80 | 81 | - (void)pause 82 | { 83 | [self.audioPlayer pause]; 84 | } 85 | 86 | - (void)play 87 | { 88 | [self.audioPlayer play]; 89 | } 90 | 91 | - (NSTimeInterval)playbackTime 92 | { 93 | return self.audioPlayer.currentPlaybackTime; 94 | } 95 | 96 | + (NSSet *)keyPathsForValuesAffectingPlaybackTime 97 | { 98 | return [NSSet setWithObject:@"audioPlayer.currentPlaybackTime"]; 99 | } 100 | 101 | - (void)seekToPlaybackTime:(NSTimeInterval)time 102 | { 103 | self.audioPlayer.currentPlaybackTime = time; 104 | } 105 | 106 | - (void)seekBackward 107 | { 108 | self.audioPlayer.currentPlaybackTime = self.audioPlayer.currentPlaybackTime - self.seekTimeInterval; 109 | } 110 | 111 | - (void)seekForward 112 | { 113 | self.audioPlayer.currentPlaybackTime = self.audioPlayer.currentPlaybackTime + self.seekTimeInterval; 114 | } 115 | 116 | #pragma mark - Notifications 117 | 118 | - (void)_playbackStateDidChange:(NSNotification *)notification 119 | { 120 | self.playing = self.audioPlayer.playbackState == MPMusicPlaybackStatePlaying; 121 | } 122 | 123 | - (void)_nowPlayingItemDidChange:(NSNotification *)notification 124 | { 125 | if (!self.audioPlayer.nowPlayingItem && self.finishedTrackBlock) { 126 | self.finishedTrackBlock(self, self.oldCurrentTrack, nil); 127 | self.oldCurrentTrack = nil; 128 | } 129 | } 130 | @end 131 | -------------------------------------------------------------------------------- /Audio Players/SMKSFBAudioPlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKSFBAudioPlayer.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKPlayer.h" 11 | 12 | 13 | /** 14 | SMKSFBAudioPlayer is a wrapper class around SFBAudioEngine that provides support for a multitude of audio formats. 15 | 16 | Supported formats include: MP3, MPEG-4, AIFF, WAV, OGG, FLAC, Musepack, WavPack, 17 | Monkey's Audio, MOD, S3M, XM, IT, True Audio 18 | */ 19 | @interface SMKSFBAudioPlayer : NSObject 20 | 21 | #pragma mark - SMKPlayer API 22 | 23 | @property (nonatomic, copy) void (^finishedTrackBlock)(id player, id track, NSError *error); 24 | @property (nonatomic, assign) float volume; 25 | @property (nonatomic, assign) NSTimeInterval seekTimeInterval; 26 | @property (nonatomic, assign, readonly) NSTimeInterval playbackTime; 27 | @property (nonatomic, assign, readonly) BOOL playing; 28 | @property (nonatomic, strong, readonly) id currentTrack; 29 | 30 | // Preloading (SMKPlayer @optional) 31 | @property (nonatomic, strong, readonly) id preloadedTrack; 32 | - (void)preloadTrack:(id)track completionHandler:(void(^)(NSError *error))handler; 33 | - (id)preloadedTrack; 34 | - (void)skipToPreloadedTrack; 35 | 36 | #pragma mark - Player Specific API 37 | 38 | @property (nonatomic, assign) float preGain; 39 | 40 | /** 41 | @param value The EQ band value in dB 42 | @param band The band number from 0-9 (10 band equalizer) 43 | */ 44 | - (void)setEQValue:(float)value forEQBand:(int)band; 45 | 46 | /** 47 | Set this to YES to load the entire file in memory before playing instead of using a buffer 48 | */ 49 | @property (nonatomic, assign) BOOL loadFilesInMemory; 50 | @end 51 | -------------------------------------------------------------------------------- /Audio Players/SMKSpotifyPlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKSpotifyPlayer.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-25. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #if TARGET_OS_IPHONE 10 | #import "CocoaLibSpotify.h" 11 | #else 12 | #import 13 | #endif 14 | 15 | #import "SMKPlayer.h" 16 | 17 | @interface SMKSpotifyPlayer : NSObject 18 | 19 | #pragma mark - SMKPlayer 20 | @property (nonatomic, copy) void (^finishedTrackBlock)(id player, id track, NSError *error); 21 | @property (nonatomic, strong, readonly) SPPlaybackManager *audioPlayer; 22 | 23 | @property (nonatomic, assign) NSTimeInterval seekTimeInterval; 24 | @property (nonatomic, assign, readonly) NSTimeInterval playbackTime; 25 | @property (nonatomic, assign, readonly) BOOL playing; 26 | @property (nonatomic, assign) float volume; 27 | @property (nonatomic, strong, readonly) id currentTrack; 28 | 29 | - (id)initWithPlaybackSession:(SPSession *)aSession; 30 | 31 | // Preloading (SMKPlayer @optional) 32 | @property (nonatomic, strong, readonly) id preloadedTrack; 33 | - (void)preloadTrack:(id)track completionHandler:(void(^)(NSError *error))handler; 34 | - (id)preloadedTrack; 35 | - (void)skipToPreloadedTrack; 36 | @end 37 | -------------------------------------------------------------------------------- /Categories/NSBundle+SMKAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSBundle+SMKAdditions.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-22. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "TargetConditionals.h" 11 | 12 | @interface NSBundle (SMKAdditions) 13 | #if !TARGET_OS_IPHONE 14 | /** 15 | Returns the bundle for the framework itself 16 | */ 17 | + (NSBundle *)SMK_frameworkBundle; 18 | #endif 19 | /** 20 | Returns the name of the bundle (CFBundleName) 21 | */ 22 | - (NSString *)SMK_bundleName; 23 | @end 24 | -------------------------------------------------------------------------------- /Categories/NSBundle+SMKAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSBundle+SMKAdditions.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-22. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "NSBundle+SMKAdditions.h" 10 | #import "SNRMusicKitMac.h" 11 | 12 | @implementation NSBundle (SMKAdditions) 13 | #if !TARGET_OS_IPHONE 14 | + (NSBundle *)SMK_frameworkBundle 15 | { 16 | return [NSBundle bundleForClass:[SNRMusicKitMac class]]; 17 | } 18 | #endif 19 | 20 | - (NSString *)SMK_bundleName 21 | { 22 | return [[self infoDictionary] valueForKey:@"CFBundleName"]; 23 | } 24 | @end 25 | -------------------------------------------------------------------------------- /Categories/NSError+SMKAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSError+SMKAdditions.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSError (SMKAdditions) 12 | /** 13 | @param code The error code 14 | @param description The human readable error description 15 | @return The NSError object created with those parameters 16 | */ 17 | + (NSError *)SMK_errorWithCode:(NSInteger)code description:(NSString *)description; 18 | @end 19 | -------------------------------------------------------------------------------- /Categories/NSError+SMKAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSError+SMKAdditions.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "NSError+SMKAdditions.h" 10 | 11 | static NSString* const SMKDefaultErrorDomain = @"SMKErrorDomain"; 12 | 13 | @implementation NSError (SMKAdditions) 14 | + (NSError *)SMK_errorWithCode:(NSInteger)code description:(NSString *)description 15 | { 16 | return [NSError errorWithDomain:SMKDefaultErrorDomain code:code userInfo:@{description : NSLocalizedDescriptionKey}]; 17 | } 18 | @end 19 | -------------------------------------------------------------------------------- /Categories/NSMutableArray+SMKAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+SMKAdditions.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-25. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSMutableArray (SMKAdditions) 12 | /** 13 | @param sortDescriptors Array of sort descriptors to sort the array with 14 | @param predicate NSPredicate to filter the array 15 | */ 16 | - (void)SMK_processWithSortDescriptors:(NSArray *)sortDescriptors predicate:(NSPredicate*)predicate; 17 | @end 18 | -------------------------------------------------------------------------------- /Categories/NSMutableArray+SMKAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSMutableArray+SMKAdditions.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-25. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "NSMutableArray+SMKAdditions.h" 10 | 11 | @implementation NSMutableArray (SMKAdditions) 12 | - (void)SMK_processWithSortDescriptors:(NSArray *)sortDescriptors predicate:(NSPredicate*)predicate 13 | { 14 | if (predicate) 15 | [self filterUsingPredicate:predicate]; 16 | if ([sortDescriptors count]) 17 | [self sortUsingDescriptors:sortDescriptors]; 18 | } 19 | @end 20 | -------------------------------------------------------------------------------- /Categories/NSObject+AssociatedObjects.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+AssociatedObjects.h 3 | // 4 | // Created by Andy Matuschak on 8/27/09. 5 | // Public domain because I love you. 6 | // 7 | 8 | #import 9 | 10 | @interface NSObject (AMAssociatedObjects) 11 | - (void)associateValue:(id)value withKey:(void *)key; // Strong reference 12 | - (void)weaklyAssociateValue:(id)value withKey:(void *)key; 13 | - (id)associatedValueForKey:(void *)key; 14 | @end 15 | -------------------------------------------------------------------------------- /Categories/NSObject+AssociatedObjects.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+AssociatedObjects.m 3 | // 4 | // Created by Andy Matuschak on 8/27/09. 5 | // Public domain because I love you. 6 | // 7 | 8 | #import "NSObject+AssociatedObjects.h" 9 | #import 10 | 11 | @implementation NSObject (AMAssociatedObjects) 12 | 13 | - (void)associateValue:(id)value withKey:(void *)key 14 | { 15 | objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_RETAIN); 16 | } 17 | 18 | - (void)weaklyAssociateValue:(id)value withKey:(void *)key 19 | { 20 | objc_setAssociatedObject(self, key, value, OBJC_ASSOCIATION_ASSIGN); 21 | } 22 | 23 | - (id)associatedValueForKey:(void *)key 24 | { 25 | return objc_getAssociatedObject(self, key); 26 | } 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /Categories/NSString+SMKAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+SMKAdditions.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-22. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSString (SMKAdditions) 12 | /** 13 | @return A normalized string by stripping out case and diacritic characters 14 | */ 15 | - (NSString *)SMK_normalizedString; 16 | 17 | /** 18 | @return The string's MD5 hash 19 | */ 20 | - (NSString *)SMK_MD5Hash; 21 | @end 22 | -------------------------------------------------------------------------------- /Categories/NSString+SMKAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+SMKAdditions.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-22. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "NSString+SMKAdditions.h" 10 | #import 11 | 12 | @implementation NSString (SMKAdditions) 13 | - (NSString *)SMK_normalizedString 14 | { 15 | NSMutableString *result = [NSMutableString stringWithString:self]; 16 | CFStringNormalize((__bridge CFMutableStringRef)result, kCFStringNormalizationFormD); 17 | CFStringFold((__bridge CFMutableStringRef)result, kCFCompareCaseInsensitive | kCFCompareDiacriticInsensitive | kCFCompareWidthInsensitive, NULL); 18 | return result; 19 | } 20 | 21 | - (NSString *)SMK_MD5Hash 22 | { 23 | const char *ptr = [self UTF8String]; 24 | unsigned char md5Buffer[CC_MD5_DIGEST_LENGTH]; 25 | CC_MD5(ptr, (int)strlen(ptr), md5Buffer); 26 | NSMutableString *output = [NSMutableString stringWithCapacity:CC_MD5_DIGEST_LENGTH * 2]; 27 | for(int i = 0; i < CC_MD5_DIGEST_LENGTH; i++) 28 | [output appendFormat:@"%02x",md5Buffer[i]]; 29 | return output; 30 | } 31 | @end 32 | -------------------------------------------------------------------------------- /Categories/NSURL+SMKAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSURL+SMKAdditions.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-22. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "TargetConditionals.h" 11 | 12 | @interface NSURL (SMKAdditions) 13 | #if !TARGET_OS_IPHONE 14 | /** 15 | @return The URL to the main bundle's Application Support folder 16 | */ 17 | + (NSURL *)SMK_applicationSupportFolder; 18 | #endif 19 | @end 20 | -------------------------------------------------------------------------------- /Categories/NSURL+SMKAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSURL+SMKAdditions.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-22. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "NSURL+SMKAdditions.h" 10 | #import "NSBundle+SMKAdditions.h" 11 | 12 | @implementation NSURL (SMKAdditions) 13 | #if !TARGET_OS_IPHONE 14 | + (NSURL *)SMK_applicationSupportFolder 15 | { 16 | NSFileManager *fileManager = [NSFileManager defaultManager]; 17 | NSURL *appSupportURL = [[fileManager URLsForDirectory:NSApplicationSupportDirectory inDomains:NSUserDomainMask] lastObject]; 18 | return [appSupportURL URLByAppendingPathComponent:[[NSBundle mainBundle] SMK_bundleName]]; 19 | } 20 | #endif 21 | @end 22 | -------------------------------------------------------------------------------- /Common Headers/SMKConvenience.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMacros.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-22. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKPlatformImports.h" 10 | 11 | static inline BOOL SMKObjectIsValid(id object) 12 | { 13 | return (object != nil) 14 | && (object != [NSNull null]) 15 | && (![object respondsToSelector:@selector(length)] 16 | || [(NSData *)object length] != 0) 17 | && (![object respondsToSelector:@selector(count)] 18 | || [(NSArray *)object count] != 0); 19 | } 20 | static inline void SMKGenericErrorLog(NSString *errorText, NSError *error) 21 | { 22 | if (![errorText length]) 23 | errorText = @"Error"; 24 | NSLog(@"%@: %@, %@", errorText, error, [error userInfo]); 25 | } 26 | 27 | static inline NSData* SMKImageJPEGRepresentation(SMKPlatformNativeImage *image, CGFloat compressionQuality) 28 | { 29 | #if TARGET_OS_IPHONE 30 | return UIImageJPEGRepresentation(image, compressionQuality); 31 | #else 32 | NSDictionary *options = [NSDictionary dictionaryWithObject:[NSNumber numberWithFloat:compressionQuality] forKey:NSImageCompressionFactor]; 33 | return [NSBitmapImageRep representationOfImageRepsInArray:[image representations] usingType: NSJPEGFileType properties:options]; 34 | #endif 35 | } -------------------------------------------------------------------------------- /Common Headers/SMKErrorCodes.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKErrorCodes.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | extern NSInteger const SMKPlayerErrorFailedToCreateInputSource; 10 | extern NSInteger const SMKPlayerErrorFailedToCreateDecoder; 11 | extern NSInteger const SMKPlayerErrorFailedToEnqueueTrack; 12 | extern NSInteger const SMKPlayerErrorItemAlreadyExists; 13 | 14 | extern NSInteger const SMKCoreDataErrorDataStoreNotAFolder; 15 | extern NSInteger const SMKCoreDataErrorFailedToInitializeStore; -------------------------------------------------------------------------------- /Common Headers/SMKErrorCodes.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKErrorCodes.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKErrorCodes.h" 10 | 11 | NSInteger const SMKPlayerErrorFailedToCreateInputSource = 0; 12 | NSInteger const SMKPlayerErrorFailedToCreateDecoder = 1; 13 | NSInteger const SMKPlayerErrorItemAlreadyExists = 2; 14 | NSInteger const SMKPlayerErrorFailedToEnqueueTrack = 3; 15 | 16 | NSInteger const SMKCoreDataErrorDataStoreNotAFolder = 4; 17 | NSInteger const SMKCoreDataErrorFailedToInitializeStore = 5; -------------------------------------------------------------------------------- /Common Headers/SMKGlobalConstants.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKGlobalConstants.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-25. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | extern NSTimeInterval const SMKPlayerDefaultSeekTimeInterval; -------------------------------------------------------------------------------- /Common Headers/SMKGlobalConstants.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKGlobalConstants.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-25. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKGlobalConstants.h" 10 | 11 | NSTimeInterval const SMKPlayerDefaultSeekTimeInterval = 3.0; -------------------------------------------------------------------------------- /Common Headers/SMKPlatformImports.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKPlatformImports.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #if TARGET_OS_IPHONE 10 | #define SMKPlatformNativeImage UIImage 11 | #else 12 | #define SMKPlatformNativeImage NSImage 13 | #endif -------------------------------------------------------------------------------- /Content Sources/8tracks/Data Model/SMK8TracksMix.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMK8TracksMix.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMK8TracksObject.h" 11 | #import "SMKPlaylist.h" 12 | #import "SMKUser.h" 13 | 14 | @interface SMK8TracksMix : SMK8TracksObject 15 | #pragma mark - SMKPlaylist 16 | @property (nonatomic, strong, readonly) id user; 17 | @property (nonatomic, strong, readonly) NSString *extendedDescription; 18 | @end 19 | -------------------------------------------------------------------------------- /Content Sources/8tracks/Data Model/SMK8TracksMix.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMK8TracksMix.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMK8TracksMix.h" 10 | 11 | @implementation SMK8TracksMix 12 | 13 | #pragma mark - SMKPlaylist 14 | 15 | - (NSArray *)tracksWithSortDescriptors:(NSArray *)sortDescriptors 16 | predicate:(NSPredicate *)predicate 17 | batchSize:(NSUInteger)batchSize 18 | fetchLimit:(NSUInteger)fetchLimit 19 | withError:(NSError **)error 20 | { 21 | 22 | } 23 | 24 | - (void)fetchTracksWithSortDescriptors:(NSArray *)sortDescriptors 25 | predicate:(NSPredicate *)predicate 26 | batchSize:(NSUInteger)batchSize 27 | fetchlimit:(NSUInteger)fetchLimit 28 | completionHandler:(void(^)(NSArray *tracks, NSError *error))handler 29 | { 30 | 31 | } 32 | 33 | - (BOOL)isEditable 34 | { 35 | return NO; 36 | } 37 | @end 38 | -------------------------------------------------------------------------------- /Content Sources/8tracks/Data Model/SMK8TracksObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMK8TracksObject.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKContentSource.h" 11 | 12 | @interface SMK8TracksObject : NSObject 13 | @property (nonatomic, weak) id contentSource; 14 | @end 15 | -------------------------------------------------------------------------------- /Content Sources/8tracks/Data Model/SMK8TracksObject.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMK8TracksObject.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMK8TracksObject.h" 10 | 11 | @implementation SMK8TracksObject 12 | @end 13 | -------------------------------------------------------------------------------- /Content Sources/8tracks/Data Model/SMK8TracksUser.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMK8TracksUser.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKUser.h" 11 | #import "SMK8TracksObject.h" 12 | 13 | enum { 14 | SMK8TracksUserAvatarSize56 = 0, 15 | SMK8TracksUserAvatarSize72 = 1, 16 | SMK8TracksUserAvatarSize100 = 2, 17 | SMK8TracksUserAvatarSize200 = 3, 18 | SMK8TracksUserAvatarSize250 = 4 19 | }; 20 | typedef NSUInteger SMK8TracksUserAvatarSize; 21 | 22 | @interface SMK8TracksUser : SMK8TracksObject 23 | 24 | #pragma mark - SMKUser 25 | 26 | @property (nonatomic, strong, readonly) NSString *username; 27 | @property (nonatomic, strong, readonly) NSString *name; 28 | @property (nonatomic, strong, readonly) NSURL *webURL; 29 | @property (nonatomic, strong, readonly) NSURL *smallArtworkURL; 30 | @property (nonatomic, strong, readonly) NSURL *largeArtworkURL; 31 | @property (nonatomic, strong, readonly) NSString *uniqueIdentifier; 32 | 33 | #pragma mark - 8Tracks 34 | 35 | /** 36 | Returns the URL for the user's avatar in the specified size 37 | @param size The size of the avatar to return 38 | @return The URL to the avatar image 39 | */ 40 | - (NSURL *)avatarURLForSize:(SMK8TracksUserAvatarSize)size; 41 | 42 | /** 43 | The user's biography, in HTML format 44 | */ 45 | @property (nonatomic, strong, readonly) NSString *biography; 46 | 47 | /** 48 | The number of users this user follows. 49 | */ 50 | @property (nonatomic, strong, readonly) NSNumber *followingCount; 51 | 52 | /** 53 | The number of followers this user has. 54 | */ 55 | @property (nonatomic, strong, readonly) NSNumber *followerCount; 56 | 57 | /** 58 | The user's 8tracks user ID. 59 | */ 60 | @property (nonatomic, strong, readonly) NSNumber *userID; 61 | 62 | /** 63 | Boolean value indicating whether to hide NSFW content 64 | */ 65 | @property (nonatomic, strong, readonly) NSNumber *hideNSFW; 66 | 67 | /** 68 | The user's location 69 | */ 70 | @property (nonatomic, strong, readonly) NSString *location; 71 | 72 | /** 73 | Create an SMK8TracksUser object with the specified dictionary 74 | @param dictionary Dictionary to create object from 75 | @param contentSource The content source the user belongs to 76 | @return The SMK8TracksUser object 77 | */ 78 | + (instancetype)userWithDictionary:(NSDictionary *)dictionary 79 | contentSource:(id)contentSource; 80 | - (id)initWithDictionary:(NSDictionary *)dictionary 81 | contentSource:(id)contentSource; 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /Content Sources/8tracks/Data Model/SMK8TracksUser.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMK8TracksUser.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMK8TracksUser.h" 10 | 11 | static NSString* const SMK8TracksUserWebBaseURL = @"http://8tracks.com"; 12 | 13 | @implementation SMK8TracksUser { 14 | NSDictionary *_avatarURLs; 15 | } 16 | 17 | #pragma mark - Initialization 18 | 19 | + (instancetype)userWithDictionary:(NSDictionary *)dictionary contentSource:(id)contentSource 20 | { 21 | return [[SMK8TracksUser alloc] initWithDictionary:dictionary contentSource:contentSource]; 22 | } 23 | 24 | - (id)initWithDictionary:(NSDictionary *)dictionary contentSource:(id)contentSource 25 | { 26 | if ((self = [super init])) { 27 | self.contentSource = contentSource; 28 | _avatarURLs = dictionary[@"avatar_urls"]; 29 | _biography = dictionary[@"bio_html"]; 30 | _followingCount = dictionary[@"followed_by_current_user"]; 31 | _followerCount = dictionary[@"follows_count"]; 32 | _userID = dictionary[@"id"]; 33 | _hideNSFW = dictionary[@"hide_nsfw"]; 34 | _location = dictionary[@"location"]; 35 | _username = dictionary[@"username"]; 36 | _name = dictionary[@"username"]; 37 | NSString *URLString = [SMK8TracksUserWebBaseURL stringByAppendingString:dictionary[@"path"]]; 38 | _webURL = [NSURL URLWithString:URLString]; 39 | _smallArtworkURL = [self avatarURLForSize:SMK8TracksUserAvatarSize72]; 40 | _largeArtworkURL = [self avatarURLForSize:SMK8TracksUserAvatarSize250]; 41 | _uniqueIdentifier = [_userID stringValue]; 42 | } 43 | return self; 44 | } 45 | 46 | #pragma mark - SMKUser 47 | 48 | + (NSSet *)supportedSortKeys 49 | { 50 | return [NSSet setWithObjects:@"followingCount", @"followerCount", @"userID", @"username", @"name", @"location", @"hideNSFW", nil]; 51 | } 52 | 53 | #pragma mark - 8Tracks 54 | 55 | - (NSURL *)avatarURLForSize:(SMK8TracksUserAvatarSize)size 56 | { 57 | NSString *URLString = nil; 58 | switch (size) { 59 | case SMK8TracksUserAvatarSize56: 60 | URLString = _avatarURLs[@"sq56"]; 61 | break; 62 | case SMK8TracksUserAvatarSize72: 63 | URLString = _avatarURLs[@"sq72"]; 64 | break; 65 | case SMK8TracksUserAvatarSize100: 66 | URLString = _avatarURLs[@"sq100"]; 67 | break; 68 | case SMK8TracksUserAvatarSize200: 69 | URLString = _avatarURLs[@"max200"]; 70 | break; 71 | case SMK8TracksUserAvatarSize250: 72 | URLString = _avatarURLs[@"max250w"]; 73 | break; 74 | default: 75 | URLString = _avatarURLs[@"sq100"]; 76 | break; 77 | } 78 | return [NSURL URLWithString:URLString]; 79 | } 80 | @end 81 | -------------------------------------------------------------------------------- /Content Sources/8tracks/SMK8TracksContentSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMK8TracksContentSource.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "AFHTTPClient.h" 10 | #import "SMKContentSource.h" 11 | #import "SMK8TracksUser.h" 12 | 13 | @interface SMK8TracksContentSource : AFHTTPClient 14 | 15 | #pragma mark - Authentication 16 | 17 | /** 18 | This method needs to be called before making any calls to the API. 19 | @param key The API key to use for making calls to the 8tracks API 20 | */ 21 | - (void)setAPIKey:(NSString *)key; 22 | 23 | /** 24 | Authenticate with the specified credentials. 25 | @param username The username 26 | @param password The password 27 | @param sucess Completion handler called upon success 28 | @param failure Completion handler called upon failure 29 | */ 30 | - (void)authenticateWithUsername:(NSString *)username 31 | password:(NSString *)password 32 | success:(void(^)(SMK8TracksUser *user))success 33 | failure:(void(^)(NSError *error))failure; 34 | 35 | /** 36 | Create a user account with the specified information. 37 | @param username The username to create the account with 38 | @param password The account password 39 | @param email User's email address 40 | @discussion After this method is called, if account creation is successful 41 | then the user will be automatically authenticated 42 | */ 43 | - (void)createAccountWithUsername:(NSString *)username 44 | password:(NSString *)password 45 | email:(NSString *)email 46 | success:(void(^)(SMK8TracksUser *user))success 47 | failure:(void(^)(NSError *error))failure; 48 | 49 | 50 | #pragma mark - Mixes 51 | 52 | - (void)getLatestTenMixesWithSuccess:(void(^)(SMK8TracksUser *user))success 53 | failure:(void(^)(NSError *error))failure; 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Content Sources/8tracks/SMK8TracksContentSource.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMK8TracksContentSource.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMK8TracksContentSource.h" 10 | #import "AFJSONRequestOperation.h" 11 | 12 | static NSString* const SMK8TracksHTTClientAPIKeyHeader = @"X-Api-Key"; 13 | static NSString* const SMK8TracksHTTClientUserTokenHeader = @"X-User-Token"; 14 | static NSString* const SMK8TracksHTTClientBaseURL = @"https://8tracks.com/"; 15 | 16 | @implementation SMK8TracksContentSource 17 | 18 | - (id)init 19 | { 20 | if ((self = [super initWithBaseURL:[NSURL URLWithString:SMK8TracksHTTClientBaseURL]])) { 21 | [self setParameterEncoding:AFJSONParameterEncoding]; 22 | [self registerHTTPOperationClass:[AFJSONRequestOperation class]]; 23 | [self setDefaultHeader:@"Accept" value:@"application/json"]; 24 | } 25 | return self; 26 | } 27 | 28 | // Use the JSON format for everything 29 | - (NSMutableURLRequest *)requestWithMethod:(NSString *)method path:(NSString *)path parameters:(NSDictionary *)parameters 30 | { 31 | NSMutableDictionary *params = [NSMutableDictionary dictionaryWithDictionary:parameters]; 32 | [params setValue:@"json" forKey:@"format"]; 33 | return [super requestWithMethod:method path:path parameters:params]; 34 | } 35 | 36 | #pragma mark - Authentication 37 | 38 | - (void)setAPIKey:(NSString *)key 39 | { 40 | [self setDefaultHeader:SMK8TracksHTTClientAPIKeyHeader value:key]; 41 | } 42 | 43 | - (void)authenticateWithUsername:(NSString *)username 44 | password:(NSString *)password 45 | success:(void(^)(SMK8TracksUser *user))success 46 | failure:(void(^)(NSError *error))failure 47 | { 48 | [self postPath:@"sessions" parameters:@{@"login" : username, @"password" : password} success:^(AFHTTPRequestOperation *operation, id responseObject) { 49 | if ([responseObject[@"logged_in"] boolValue]) { 50 | [self setDefaultHeader:SMK8TracksHTTClientUserTokenHeader value:responseObject[@"user_token"]]; 51 | SMK8TracksUser *user = [SMK8TracksUser userWithDictionary:responseObject[@"current_user"] contentSource:self]; 52 | if (success) success(user); 53 | } else { 54 | if (failure) failure(nil); 55 | } 56 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 57 | if (failure) failure(error); 58 | }]; 59 | } 60 | 61 | - (void)createAccountWithUsername:(NSString *)username 62 | password:(NSString *)password 63 | email:(NSString *)email 64 | success:(void(^)(SMK8TracksUser *user))success 65 | failure:(void(^)(NSError *error))failure 66 | { 67 | [self postPath:@"users" parameters:@{@"user[login]" : username, @"user[password]" : password, @"user[email]" : email, @"user[agree_to_terms]" : @1} success:^(AFHTTPRequestOperation *operation, id responseObject) { 68 | if ([responseObject[@"logged_in"] boolValue]) { 69 | [self setDefaultHeader:SMK8TracksHTTClientUserTokenHeader value:responseObject[@"user_token"]]; 70 | SMK8TracksUser *user = [SMK8TracksUser userWithDictionary:responseObject[@"current_user"] contentSource:self]; 71 | if (success) success(user); 72 | } else { 73 | if (failure) failure(nil); 74 | } 75 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 76 | if (failure) failure(error); 77 | }]; 78 | } 79 | @end 80 | -------------------------------------------------------------------------------- /Content Sources/MPMediaLibrary/Data Model/SMKMPMediaAlbum.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMediaAlbum.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SMKAlbum.h" 12 | #import "SMKArtworkObject.h" 13 | 14 | @interface SMKMPMediaAlbum : NSObject 15 | @property (nonatomic, strong) MPMediaItemCollection *representedObject; 16 | @property (nonatomic, assign, readonly) id contentSource; 17 | 18 | // SMKAlbum @optional 19 | @property (nonatomic, assign, readonly) NSUInteger releaseYear; 20 | @property (nonatomic, assign, readonly) NSUInteger rating; 21 | @property (nonatomic, assign, readonly) NSTimeInterval duration; 22 | @end 23 | -------------------------------------------------------------------------------- /Content Sources/MPMediaLibrary/Data Model/SMKMPMediaArtist.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMediaArtist.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SMKArtist.h" 12 | 13 | @interface SMKMPMediaArtist : NSObject 14 | @property (nonatomic, strong, readonly) MPMediaItemCollection *representedObject; 15 | @property (nonatomic, assign, readonly) id contentSource; 16 | @end 17 | -------------------------------------------------------------------------------- /Content Sources/MPMediaLibrary/Data Model/SMKMPMediaArtist.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMediaArtist.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKMPMediaArtist.h" 10 | #import "SMKMPMediaContentSource.h" 11 | #import "SMKMPMediaHelpers.h" 12 | 13 | @interface SMKMPMediaAlbum (SMKInternal) 14 | - (id)initWithRepresentedObject:(MPMediaItemCollection *)object contentSource:(id)contentSource; 15 | @end 16 | 17 | @implementation SMKMPMediaArtist 18 | 19 | - (id)initWithRepresentedObject:(MPMediaItemCollection *)object contentSource:(id)contentSource 20 | { 21 | if ((self = [super init])) { 22 | _representedObject = object; 23 | _contentSource = contentSource; 24 | } 25 | return self; 26 | } 27 | 28 | #pragma mark - SMKContentObject 29 | 30 | - (NSString *)uniqueIdentifier 31 | { 32 | return [self.representedObject valueForProperty:MPMediaItemPropertyPersistentID]; 33 | } 34 | 35 | - (NSString *)name 36 | { 37 | NSString *albumArtist = [self.representedObject.representativeItem valueForProperty:MPMediaItemPropertyAlbumArtist]; 38 | NSString *artist = [self.representedObject.representativeItem valueForProperty:MPMediaItemPropertyArtist]; 39 | return albumArtist ?: artist; 40 | } 41 | 42 | + (NSSet *)supportedSortKeys 43 | { 44 | return [NSSet setWithObjects:@"name", nil]; 45 | } 46 | 47 | #pragma mark - SMKArtist 48 | 49 | - (void)fetchAlbumsWithSortDescriptors:(NSArray *)sortDescriptors 50 | predicate:(id)predicate 51 | completionHandler:(void(^)(NSArray *albums, NSError *error))handler 52 | { 53 | __weak SMKMPMediaArtist *weakSelf = self; 54 | dispatch_async([(SMKMPMediaContentSource*)self.contentSource queryQueue], ^{ 55 | SMKMPMediaArtist *strongSelf = weakSelf; 56 | MPMediaQuery *albumsQuery = [MPMediaQuery albumsQuery]; 57 | MPMediaItem *item = strongSelf.representedObject.representativeItem; 58 | MPMediaPropertyPredicate *artistPredicate = [SMKMPMediaHelpers predicateForArtistNameOfItem:item]; 59 | if (artistPredicate) { 60 | NSMutableSet *predicates = [NSMutableSet setWithObject:artistPredicate]; 61 | if (predicate) 62 | [predicates addObjectsFromArray:[[(SMKMPMediaPredicate *)predicate predicates] allObjects]]; 63 | albumsQuery.filterPredicates = predicates; 64 | NSArray *collections = albumsQuery.collections; 65 | NSMutableArray *albums = [NSMutableArray arrayWithCapacity:[collections count]]; 66 | [collections enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 67 | SMKMPMediaAlbum *album = [[SMKMPMediaAlbum alloc] initWithRepresentedObject:obj contentSource:strongSelf.contentSource]; 68 | [albums addObject:album]; 69 | }]; 70 | if ([sortDescriptors count]) 71 | [albums sortUsingDescriptors:sortDescriptors]; 72 | dispatch_async(dispatch_get_main_queue(), ^{ 73 | if (handler) handler(albums, nil); 74 | }); 75 | } else { 76 | dispatch_async(dispatch_get_main_queue(), ^{ 77 | if (handler) handler(nil, nil); 78 | }); 79 | } 80 | }); 81 | } 82 | @end 83 | -------------------------------------------------------------------------------- /Content Sources/MPMediaLibrary/Data Model/SMKMPMediaPlaylist.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMediaPlaylist.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SMKPlaylist.h" 12 | 13 | @interface SMKMPMediaPlaylist : NSObject 14 | @property (nonatomic, strong) MPMediaItemCollection *representedObject; 15 | @property (nonatomic, assign, readonly) id contentSource; 16 | @end 17 | -------------------------------------------------------------------------------- /Content Sources/MPMediaLibrary/Data Model/SMKMPMediaPlaylist.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMediaPlaylist.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKMPMediaPlaylist.h" 10 | #import "SMKMPMediaContentSource.h" 11 | #import "SMKMPMediaTrack.h" 12 | 13 | @interface SMKMPMediaTrack (SMKInternal) 14 | - (id)initWithRepresentedObject:(MPMediaItem*)object contentSource:(id)contentSource; 15 | @end 16 | 17 | @implementation SMKMPMediaPlaylist 18 | 19 | - (id)initWithRepresentedObject:(MPMediaItemCollection*)object contentSource:(id)contentSource 20 | { 21 | if ((self = [super init])) { 22 | _representedObject = object; 23 | _contentSource = contentSource; 24 | } 25 | return self; 26 | } 27 | 28 | #pragma mark - SMKContentObject 29 | 30 | - (NSString *)uniqueIdentifier 31 | { 32 | return [[self.representedObject valueForProperty:MPMediaPlaylistPropertyPersistentID] stringValue]; 33 | } 34 | 35 | - (NSString *)name 36 | { 37 | return [self.representedObject valueForProperty:MPMediaPlaylistPropertyName]; 38 | } 39 | 40 | + (NSSet *)supportedSortKeys 41 | { 42 | return [NSSet setWithObjects:@"name", nil]; 43 | } 44 | 45 | #pragma mark - SMKPlaylist 46 | 47 | - (void)fetchTracksWithCompletionHandler:(void(^)(NSArray *tracks, NSError *error))handler 48 | { 49 | __weak SMKMPMediaPlaylist *weakSelf = self; 50 | dispatch_async([(SMKMPMediaContentSource*)self.contentSource queryQueue], ^{ 51 | SMKMPMediaPlaylist *strongSelf = weakSelf; 52 | NSArray *items = [strongSelf.representedObject items]; 53 | NSMutableArray *tracks = [NSMutableArray arrayWithCapacity:[items count]]; 54 | [items enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 55 | SMKMPMediaTrack *track = [[SMKMPMediaTrack alloc] initWithRepresentedObject:obj contentSource:strongSelf.contentSource]; 56 | [tracks addObject:track]; 57 | }]; 58 | dispatch_async(dispatch_get_main_queue(), ^{ 59 | if (handler) handler(tracks, nil); 60 | }); 61 | }); 62 | } 63 | 64 | - (BOOL)isEditable 65 | { 66 | return NO; 67 | } 68 | @end 69 | -------------------------------------------------------------------------------- /Content Sources/MPMediaLibrary/Data Model/SMKMPMediaPredicate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMediaPredicate.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | /** 12 | This is a wrapper class that encapsulates a set of MPMediaPropertyPredicates 13 | to allow them to be passed as a single predicate object in order to conform 14 | to the SNRMusicKit protocols. 15 | */ 16 | @interface SMKMPMediaPredicate : NSObject 17 | @property (nonatomic, strong, readonly) NSSet *predicates; 18 | /** 19 | Creates a new SMKMPMediaPredicate object with the specified subpredicates. 20 | @param predicates Set of MPMediaPropertyPredicate objects 21 | */ 22 | - (id)initWithMediaPropertyPredicates:(NSSet *)predicates; 23 | + (instancetype)predicateWithMediaPropertyPredicates:(NSSet *)predicates; 24 | @end 25 | -------------------------------------------------------------------------------- /Content Sources/MPMediaLibrary/Data Model/SMKMPMediaPredicate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMediaPredicate.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKMPMediaPredicate.h" 10 | 11 | @implementation SMKMPMediaPredicate 12 | 13 | - (id)initWithMediaPropertyPredicates:(NSSet *)predicates 14 | { 15 | if ((self = [super init])) { 16 | _predicates = predicates; 17 | } 18 | return self; 19 | } 20 | 21 | + (instancetype)predicateWithMediaPropertyPredicates:(NSSet *)predicates 22 | { 23 | return [[self alloc] initWithMediaPropertyPredicates:predicates]; 24 | } 25 | @end 26 | -------------------------------------------------------------------------------- /Content Sources/MPMediaLibrary/Data Model/SMKMPMediaTrack.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMediaTrack.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import "SMKTrack.h" 12 | 13 | @interface SMKMPMediaTrack : NSObject 14 | @property (nonatomic, strong) MPMediaItem *representedObject; 15 | @property (nonatomic, assign, readonly) id contentSource; 16 | 17 | // SMKTrack @optional 18 | @property (nonatomic, strong, readonly) NSString *composer; 19 | @property (nonatomic, assign, readonly) NSUInteger trackNumber; 20 | @property (nonatomic, assign, readonly) NSUInteger discNumber; 21 | @property (nonatomic, assign, readonly) NSUInteger playCount; 22 | @property (nonatomic, strong, readonly) NSString *lyrics; 23 | @property (nonatomic, assign, readonly) NSUInteger rating; 24 | @property (nonatomic, strong, readonly) NSDate *lastPlayedDate; 25 | @property (nonatomic, strong, readonly) NSURL *playbackURL; 26 | @end 27 | -------------------------------------------------------------------------------- /Content Sources/MPMediaLibrary/Data Model/SMKMPMediaTrack.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMediaTrack.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKMPMediaTrack.h" 10 | #import "SMKMPMediaAlbum.h" 11 | #import "SMKMPMediaHelpers.h" 12 | #import "SMKAVQueuePlayer.h" 13 | #import "SMKMPMusicPlayer.h" 14 | 15 | @interface SMKMPMediaAlbum (SMKInternal) 16 | - (id)initWithRepresentedObject:(MPMediaItem*)object contentSource:(id)contentSource; 17 | @end 18 | 19 | @implementation SMKMPMediaTrack 20 | 21 | - (id)initWithRepresentedObject:(MPMediaItem*)object contentSource:(id)contentSource 22 | { 23 | if ((self = [super init])) { 24 | _representedObject = object; 25 | _contentSource = contentSource; 26 | } 27 | return self; 28 | } 29 | 30 | #pragma mark - SMKContentObject 31 | 32 | - (NSString *)uniqueIdentifier 33 | { 34 | return [self.representedObject valueForProperty:MPMediaItemPropertyPersistentID]; 35 | } 36 | 37 | - (NSString *)name 38 | { 39 | return [self.representedObject valueForProperty:MPMediaItemPropertyTitle]; 40 | } 41 | 42 | + (NSSet *)supportedSortKeys 43 | { 44 | return [NSSet setWithObjects:@"name", @"artistName", @"albumArtistName", @"duration", @"composer", @"trackNumber", @"discNumber", @"playCount", @"lyrics", @"genre", @"rating", @"lastPlayedDate", nil]; 45 | } 46 | 47 | - (Class)playerClass 48 | { 49 | return (self.playbackURL != nil) ? [SMKAVQueuePlayer class] : [SMKMPMusicPlayer class]; 50 | } 51 | 52 | #pragma mark - SMKTrack 53 | 54 | - (id)album 55 | { 56 | NSNumber *albumPersistentID = [self.representedObject valueForProperty:MPMediaItemPropertyAlbumPersistentID]; 57 | MPMediaQuery *albumQuery = [MPMediaQuery albumsQuery]; 58 | MPMediaPropertyPredicate *albumPredicate = [MPMediaPropertyPredicate predicateWithValue:albumPersistentID forProperty:MPMediaItemPropertyAlbumPersistentID]; 59 | albumQuery.filterPredicates = [NSSet setWithObject:albumPredicate]; 60 | NSArray *collections = albumQuery.collections; 61 | if ([collections count]) { 62 | return [[SMKMPMediaAlbum alloc] initWithRepresentedObject:[collections objectAtIndex:0] contentSource:self.contentSource]; 63 | } 64 | return nil; 65 | } 66 | 67 | - (id)artist 68 | { 69 | return [self.album artist]; 70 | } 71 | 72 | - (NSString *)artistName 73 | { 74 | return [self.representedObject valueForProperty:MPMediaItemPropertyArtist]; 75 | } 76 | 77 | - (NSString *)albumArtistName 78 | { 79 | return [self.representedObject valueForProperty:MPMediaItemPropertyAlbumArtist]; 80 | } 81 | 82 | - (NSTimeInterval)duration 83 | { 84 | return [[self.representedObject valueForProperty:MPMediaItemPropertyPlaybackDuration] doubleValue]; 85 | } 86 | 87 | - (NSString *)composer 88 | { 89 | return [self.representedObject valueForProperty:MPMediaItemPropertyComposer]; 90 | } 91 | 92 | - (NSUInteger)trackNumber 93 | { 94 | return [[self.representedObject valueForProperty:MPMediaItemPropertyAlbumTrackNumber] unsignedIntegerValue]; 95 | } 96 | 97 | - (NSUInteger)discNumber 98 | { 99 | return [[self.representedObject valueForProperty:MPMediaItemPropertyDiscNumber] unsignedIntegerValue]; 100 | } 101 | 102 | - (NSUInteger)playCount 103 | { 104 | return [[self.representedObject valueForProperty:MPMediaItemPropertyPlayCount] unsignedIntegerValue]; 105 | } 106 | 107 | - (NSString *)lyrics 108 | { 109 | return [self.representedObject valueForProperty:MPMediaItemPropertyLyrics]; 110 | } 111 | 112 | - (NSString *)genre 113 | { 114 | return [self.representedObject valueForProperty:MPMediaItemPropertyGenre]; 115 | } 116 | 117 | - (NSUInteger)rating 118 | { 119 | return [[self.representedObject valueForProperty:MPMediaItemPropertyRating] unsignedIntegerValue]; 120 | } 121 | 122 | - (NSDate *)lastPlayedDate 123 | { 124 | return [self.representedObject valueForProperty:MPMediaItemPropertyLastPlayedDate]; 125 | } 126 | 127 | - (NSURL *)playbackURL 128 | { 129 | return [self.representedObject valueForProperty:MPMediaItemPropertyAssetURL]; 130 | } 131 | @end 132 | -------------------------------------------------------------------------------- /Content Sources/MPMediaLibrary/SMKMPMediaContentSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMediaContentSource.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKContentSource.h" 11 | #import "SMKMPMediaPredicate.h" 12 | #import "SMKMPMediaPlaylist.h" 13 | #import "SMKMPMediaTrack.h" 14 | #import "SMKMPMediaAlbum.h" 15 | #import "SMKMPMediaArtist.h" 16 | 17 | @interface SMKMPMediaContentSource : NSObject 18 | @property (nonatomic, readonly) dispatch_queue_t queryQueue; 19 | @end 20 | -------------------------------------------------------------------------------- /Content Sources/MPMediaLibrary/SMKMPMediaContentSource.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMediaContentSource.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKMPMediaContentSource.h" 10 | #import 11 | 12 | @interface SMKMPMediaPlaylist (SMKInternal) 13 | - (id)initWithRepresentedObject:(MPMediaItemCollection*)object contentSource:(id)contentSource; 14 | @end 15 | 16 | @implementation SMKMPMediaContentSource 17 | 18 | - (id)init 19 | { 20 | if ((self = [super init])) { 21 | _queryQueue = dispatch_queue_create("com.indragie.SNRMusicKit.MPMediaQueryQueue", DISPATCH_QUEUE_CONCURRENT); 22 | } 23 | return self; 24 | } 25 | 26 | - (void)dealloc 27 | { 28 | dispatch_release(_queryQueue); 29 | } 30 | 31 | - (void)fetchPlaylistsWithSortDescriptors:(NSArray *)sortDescriptors 32 | predicate:(SMKMPMediaPredicate *)predicate 33 | completionHandler:(void(^)(NSArray *playlists, NSError *error))handler 34 | { 35 | __weak SMKMPMediaContentSource *weakSelf = self; 36 | dispatch_async(_queryQueue, ^{ 37 | SMKMPMediaContentSource *strongSelf = weakSelf; 38 | MPMediaQuery *playlistsQuery = [MPMediaQuery playlistsQuery]; 39 | if (predicate) playlistsQuery.filterPredicates = predicate.predicates; 40 | NSArray *collections = playlistsQuery.collections; 41 | NSMutableArray *playlists = [NSMutableArray arrayWithCapacity:[collections count]]; 42 | [collections enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 43 | SMKMPMediaPlaylist *playlist = [[SMKMPMediaPlaylist alloc] initWithRepresentedObject:obj contentSource:strongSelf]; 44 | [playlists addObject:playlist]; 45 | }]; 46 | if ([sortDescriptors count]) 47 | [playlists sortUsingDescriptors:sortDescriptors]; 48 | dispatch_async(dispatch_get_main_queue(), ^{ 49 | if (handler) handler(playlists, nil); 50 | }); 51 | }); 52 | } 53 | 54 | + (Class)predicateClass { return [SMKMPMediaPredicate class]; } 55 | @end 56 | -------------------------------------------------------------------------------- /Content Sources/MPMediaLibrary/SMKMPMediaHelpers.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMediaHelpers.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface SMKMPMediaHelpers : NSObject 13 | /** 14 | Creates a property predicate using either the album artist name (or the artist name) 15 | If neither value is available, returns nil. 16 | @param item The item to create a predicate from 17 | @return Predicate for matching the artist name 18 | */ 19 | + (MPMediaPropertyPredicate *)predicateForArtistNameOfItem:(MPMediaItem *)item; 20 | @end 21 | -------------------------------------------------------------------------------- /Content Sources/MPMediaLibrary/SMKMPMediaHelpers.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKMPMediaHelpers.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKMPMediaHelpers.h" 10 | 11 | @implementation SMKMPMediaHelpers 12 | + (MPMediaPropertyPredicate *)predicateForArtistNameOfItem:(MPMediaItem *)item 13 | { 14 | NSNumber *albumArtist = [item valueForProperty:MPMediaItemPropertyAlbumArtistPersistentID]; 15 | NSString *artist = [item valueForProperty:MPMediaItemPropertyArtistPersistentID]; 16 | if (albumArtist) { 17 | return [MPMediaPropertyPredicate predicateWithValue:albumArtist forProperty:MPMediaItemPropertyAlbumArtistPersistentID]; 18 | } else { 19 | return [MPMediaPropertyPredicate predicateWithValue:artist forProperty:MPMediaItemPropertyArtistPersistentID]; 20 | } 21 | } 22 | @end 23 | -------------------------------------------------------------------------------- /Content Sources/Shared/SMKArtworkCache.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKArtworkCache.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-25. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKPlatformImports.h" 11 | #import "SMKPlatformNativeImage+SMKAdditions.h" 12 | 13 | // 14 | // Cross platform artwork cache that handles on disk and in memory caching 15 | // This class is largely inspired by JMImageCache by Jake Marsh (who is incredibly awesome) 16 | // 17 | // 18 | 19 | @interface SMKArtworkCache : NSCache 20 | /** 21 | @return The shared instance of SMKArtworkCache 22 | */ 23 | + (instancetype)sharedCache; 24 | 25 | /** 26 | Compression quality of the image, 1.0 being best quality and 0.0 being lowest. 27 | Default value is 1.0. 28 | */ 29 | @property (nonatomic, assign) CGFloat JPEGCompressionQuality; 30 | 31 | /** 32 | Maximum size of the on disk cache, in MB 33 | Default maximum size is 200MB. 34 | */ 35 | @property (nonatomic, assign) NSUInteger maximumDiskCacheSize; 36 | /** 37 | Asynchronously fetches a cached image and calls the completion handler 38 | @param key The cached image key 39 | @param handler Hander block called with the fetched image 40 | */ 41 | - (void)fetchImageForKey:(NSString *)key completionHandler:(void (^)(SMKPlatformNativeImage *image))handler; 42 | 43 | /** 44 | Saves an image in the cache with the specified key 45 | @param key The key to save the cached image under 46 | @param targetSize The target size of the image to scale to. Pass CGSizeZero to keep the image at the original size. Do not apply scale factors to this size, as the scaling process will account for that. 47 | @param method The resizing method to use. Values are defined in SMKPlatformNativeImage+SMKAdditions.h 48 | @param image The image to save 49 | */ 50 | - (void)setCachedImage:(SMKPlatformNativeImage *)image 51 | forKey:(NSString *)key 52 | targetSize:(CGSize)targetSize 53 | resizingMethod:(SMKImageResizingMethod)method; 54 | 55 | /** 56 | Removes the image with the specified key from the cache 57 | @param key The key of the image to remove from the cache 58 | */ 59 | - (void)removeCachedImageForKey:(NSString *)key; 60 | 61 | /** 62 | Removes every image in the cache (including on disk) 63 | */ 64 | - (void)removeAllCachedImages; 65 | @end 66 | -------------------------------------------------------------------------------- /Content Sources/Shared/SMKManagedObjectContext.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKManagedObjectContext.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKContentSource.h" 11 | 12 | @interface SMKManagedObjectContext : NSManagedObjectContext 13 | @property (nonatomic, strong) id SMK_contentSource; 14 | @property (nonatomic, assign) NSUInteger defaultFetchBatchSize; 15 | @end 16 | -------------------------------------------------------------------------------- /Content Sources/Shared/SMKManagedObjectContext.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKManagedObjectContext.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKManagedObjectContext.h" 10 | 11 | @implementation SMKManagedObjectContext 12 | @synthesize defaultFetchBatchSize = _defaultFetchBatchSize; 13 | 14 | - (id)initWithConcurrencyType:(NSManagedObjectContextConcurrencyType)ct 15 | { 16 | if ((self = [super initWithConcurrencyType:ct])) { 17 | self.defaultFetchBatchSize = 20; 18 | } 19 | return self; 20 | } 21 | @end 22 | -------------------------------------------------------------------------------- /Content Sources/Shared/SMKPlatformNativeImage+SMKAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKPlatformNativeImage+SMKAdditions.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-26. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKPlatformImports.h" 10 | 11 | // Based on ImageCrop source code by Matt Gemmell 12 | // 13 | 14 | typedef enum { 15 | SMKImageResizeCrop, 16 | SMKImageResizeCropStart, 17 | SMKImageResizeCropEnd, 18 | SMKImageResizeScale 19 | } SMKImageResizingMethod; 20 | 21 | @interface SMKPlatformNativeImage (SMKAdditions) 22 | - (SMKPlatformNativeImage *)imageScaledToFitSize:(CGSize)size; 23 | - (SMKPlatformNativeImage *)imageCroppedToFitSize:(CGSize)size; 24 | - (SMKPlatformNativeImage *)imageToFitSize:(CGSize)size method:(SMKImageResizingMethod)resizeMethod; 25 | @end 26 | -------------------------------------------------------------------------------- /Content Sources/Spotify/Categories/NSObject+SMKSpotifyAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+SMKSpotifyAdditions.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKSpotifyConstants.h" 11 | 12 | @interface NSObject (SMKSpotifyAdditions) 13 | /** 14 | Asynchronously waits for the object to finish loading and calls the completion block. 15 | This method just calls -SMK_spotifyWaitAsyncThen:group: passing NULL as the group 16 | @param then The block to call when loading is finished. 17 | */ 18 | - (void)SMK_spotifyWaitAsyncThen:(void(^)())then; 19 | 20 | /** 21 | Enters the dispatch group, waits for the object to load, leaves the group, and calls the handler 22 | @param group The dispatch_group to enter and leave (optional, pass in NULL if you aren't using a group) 23 | @param then The block to call when loading is finished 24 | */ 25 | - (void)SMK_spotifyWaitAsyncThen:(void(^)())then group:(dispatch_group_t)group; 26 | @end 27 | -------------------------------------------------------------------------------- /Content Sources/Spotify/Categories/NSObject+SMKSpotifyAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSObject+SMKSpotifyAdditions.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "NSObject+SMKSpotifyAdditions.h" 10 | 11 | #if TARGET_OS_IPHONE 12 | #import "CocoaLibSpotify.h" 13 | #else 14 | #import 15 | #endif 16 | 17 | @implementation NSObject (SMKSpotifyAdditions) 18 | 19 | - (void)SMK_spotifyWaitAsyncThen:(void(^)())then 20 | { 21 | [self SMK_spotifyWaitAsyncThen:then group:NULL]; 22 | } 23 | 24 | - (void)SMK_spotifyWaitAsyncThen:(void(^)())then group:(dispatch_group_t)group 25 | { 26 | if ([self conformsToProtocol:@protocol(SPAsyncLoading)]) { 27 | if (group) dispatch_group_enter(group); 28 | [SPAsyncLoading waitUntilLoaded:self timeout:SMKSpotifyDefaultLoadingTimeout then:^(NSArray *loadedItems, NSArray *notLoadedItems) { 29 | if (group) dispatch_group_leave(group); 30 | if (then) then(); 31 | }]; 32 | } else if (then) { 33 | then(); 34 | } 35 | } 36 | @end 37 | -------------------------------------------------------------------------------- /Content Sources/Spotify/Data Model/SPAlbum+SMKAlbum.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPAlbum+SMKAlbum.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #if TARGET_OS_IPHONE 10 | #import "CocoaLibSpotify.h" 11 | #else 12 | #import 13 | #endif 14 | 15 | #import "SMKAlbum.h" 16 | #import "SMKWebObject.h" 17 | #import "SMKArtworkObject.h" 18 | #import "SMKHierarchicalLoading.h" 19 | 20 | @interface SPAlbum (SMKAlbum) 21 | - (SPAlbumBrowse *)SMK_associatedAlbumBrowse; 22 | @end 23 | 24 | @interface SPAlbumBrowse (SMKAlbum) 25 | @end -------------------------------------------------------------------------------- /Content Sources/Spotify/Data Model/SPAlbum+SMKAlbum.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPAlbum+SMKAlbum.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SPAlbum+SMKAlbum.h" 10 | #import "NSObject+SMKSpotifyAdditions.h" 11 | #import "NSObject+AssociatedObjects.h" 12 | #import "NSMutableArray+SMKAdditions.h" 13 | 14 | #import "SMKSpotifyConstants.h" 15 | #import "SMKSpotifyContentSource.h" 16 | #import "SMKSpotifyHelpers.h" 17 | 18 | static void* const SMKSPAlbumBrowseKey = @"SMK_SPAlbumBrowse"; 19 | 20 | @implementation SPAlbum (SMKAlbum) 21 | 22 | #pragma mark - SMKAlbum 23 | 24 | - (void)fetchTracksWithSortDescriptors:(NSArray *)sortDescriptors 25 | predicate:(NSPredicate *)predicate 26 | completionHandler:(void(^)(NSArray *tracks, NSError *error))handler 27 | { 28 | SPAlbumBrowse *browse = [self SMK_associatedAlbumBrowse]; 29 | dispatch_queue_t queue = [(SMKSpotifyContentSource *)self.session spotifyLocalQueue]; 30 | [SMKSpotifyHelpers loadItemsAynchronously:@[browse] 31 | sortDescriptors:sortDescriptors 32 | predicate:predicate 33 | sortingQueue:queue 34 | completionHandler:handler]; 35 | } 36 | 37 | - (NSUInteger)releaseYear 38 | { 39 | return self.year; 40 | } 41 | 42 | #pragma mark - SMKContentObject 43 | 44 | - (NSString *)uniqueIdentifier 45 | { 46 | return [self.spotifyURL absoluteString]; 47 | } 48 | 49 | + (NSSet *)supportedSortKeys 50 | { 51 | return [NSSet setWithObjects:@"releaseYear", @"name", @"artist", nil]; 52 | } 53 | 54 | - (id)contentSource 55 | { 56 | return (id)self.session; 57 | } 58 | 59 | #pragma mark - SMKWebObject 60 | 61 | - (NSURL *)webURL 62 | { 63 | return self.spotifyURL; 64 | } 65 | 66 | #pragma mark - SMKArtworkObject 67 | 68 | - (void)fetchArtworkWithSize:(SMKArtworkSize)size completionHandler:(void(^)(SMKPlatformNativeImage *image, NSError *error))handler 69 | { 70 | SPImage *image = [self _imageForSize:size]; 71 | [image SMK_spotifyWaitAsyncThen:^{ 72 | if (handler) { handler(image.image, nil); } 73 | }]; 74 | } 75 | 76 | #pragma mark - SMKHierarchicalLoading 77 | 78 | - (void)loadHierarchy:(dispatch_group_t)group array:(NSMutableArray *)array 79 | { 80 | dispatch_group_enter(group); 81 | __weak SPAlbum *weakSelf = self; 82 | [self SMK_spotifyWaitAsyncThen:^{ 83 | SPAlbum *strongSelf = weakSelf; 84 | SPAlbumBrowse *browse = [strongSelf SMK_associatedAlbumBrowse]; 85 | [browse loadHierarchy:group array:array]; 86 | dispatch_group_leave(group); 87 | }]; 88 | } 89 | 90 | #pragma mark - Private 91 | 92 | - (SPImage *)_imageForSize:(SMKArtworkSize)size 93 | { 94 | switch (size) { 95 | case SMKArtworkSizeSmallest: 96 | return self.smallestAvailableCover; 97 | case SMKArtworkSizeSmall: 98 | return self.smallCover; 99 | case SMKArtworkSizeLarge: 100 | return self.largeCover; 101 | case SMKArtworkSizeLargest: 102 | return self.largestAvailableCover; 103 | default: 104 | return nil; 105 | break; 106 | } 107 | } 108 | 109 | - (SPAlbumBrowse *)SMK_associatedAlbumBrowse 110 | { 111 | __block SPAlbumBrowse *browse = [self associatedValueForKey:SMKSPAlbumBrowseKey]; 112 | if (!browse) { 113 | dispatch_sync([SPSession libSpotifyQueue], ^{ 114 | browse = [SPAlbumBrowse browseAlbum:self inSession:self.session]; 115 | }); 116 | [self associateValue:browse withKey:SMKSPAlbumBrowseKey]; 117 | } 118 | return browse; 119 | } 120 | @end 121 | 122 | @implementation SPAlbumBrowse (SMKAlbum) 123 | 124 | #pragma mark - SMKHierarchicalLoading 125 | 126 | - (void)loadHierarchy:(dispatch_group_t)group array:(NSMutableArray *)array 127 | { 128 | __weak SPAlbumBrowse *weakSelf = self; 129 | dispatch_group_enter(group); 130 | [self SMK_spotifyWaitAsyncThen:^{ 131 | SPAlbumBrowse *strongSelf = weakSelf; 132 | [strongSelf.tracks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 133 | [obj loadHierarchy:group array:array]; 134 | }]; 135 | dispatch_group_leave(group); 136 | }]; 137 | } 138 | @end 139 | -------------------------------------------------------------------------------- /Content Sources/Spotify/Data Model/SPArtist+SMKArtist.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPArtist+SMKArtist.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #if TARGET_OS_IPHONE 10 | #import "CocoaLibSpotify.h" 11 | #else 12 | #import 13 | #endif 14 | 15 | #import "SMKArtist.h" 16 | #import "SMKWebObject.h" 17 | #import "SMKArtworkObject.h" 18 | #import "SMKHierarchicalLoading.h" 19 | 20 | @interface SPArtist (SMKArtist) 21 | - (SPArtistBrowse *)SMK_associatedArtistBrowse; 22 | @end 23 | 24 | @interface SPArtistBrowse (SMKArtist) 25 | @end -------------------------------------------------------------------------------- /Content Sources/Spotify/Data Model/SPArtist+SMKArtist.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPArtist+SMKArtist.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SPArtist+SMKArtist.h" 10 | #import "NSObject+AssociatedObjects.h" 11 | #import "NSObject+SMKSpotifyAdditions.h" 12 | #import "NSMutableArray+SMKAdditions.h" 13 | 14 | #import "SMKSpotifyContentSource.h" 15 | #import "SMKSpotifyHelpers.h" 16 | 17 | static void* const SMKSPArtistBrowseKey = @"SMK_SPArtistBrowse"; 18 | 19 | @interface SPArtist (SMKInternal) 20 | @property (nonatomic, readwrite, strong) SPSession *session; 21 | @end 22 | 23 | @implementation SPArtist (SMKArtist) 24 | 25 | #pragma mark - SMKArtist 26 | 27 | - (void)fetchAlbumsWithSortDescriptors:(NSArray *)sortDescriptors 28 | predicate:(NSPredicate *)predicate 29 | completionHandler:(void(^)(NSArray *albums, NSError *error))handler 30 | { 31 | SPArtistBrowse *browse = [self SMK_associatedArtistBrowse]; 32 | dispatch_queue_t queue = [(SMKSpotifyContentSource *)self.session spotifyLocalQueue]; 33 | [SMKSpotifyHelpers loadItemsAynchronously:@[browse] 34 | sortDescriptors:sortDescriptors 35 | predicate:predicate 36 | sortingQueue:queue 37 | completionHandler:handler]; 38 | } 39 | 40 | #pragma mark - SMKContentObject 41 | 42 | - (NSString *)uniqueIdentifier 43 | { 44 | return [self.spotifyURL absoluteString]; 45 | } 46 | 47 | + (NSSet *)supportedSortKeys 48 | { 49 | return [NSSet setWithObjects:@"name", nil]; 50 | } 51 | 52 | - (id)contentSource 53 | { 54 | return (id)self.session; 55 | } 56 | 57 | #pragma mark - SMKWebObject 58 | 59 | - (NSURL *)webURL 60 | { 61 | return self.spotifyURL; 62 | } 63 | 64 | #pragma mark - SMKHierarchicalLoading 65 | 66 | - (void)loadHierarchy:(dispatch_group_t)group array:(NSMutableArray *)array 67 | { 68 | dispatch_group_enter(group); 69 | __weak SPArtist *weakSelf = self; 70 | [self SMK_spotifyWaitAsyncThen:^{ 71 | SPArtist *strongSelf = weakSelf; 72 | SPArtistBrowse *browse = [strongSelf SMK_associatedArtistBrowse]; 73 | [browse loadHierarchy:group array:array]; 74 | dispatch_group_leave(group); 75 | }]; 76 | } 77 | 78 | #pragma mark - SMKArtworkObject 79 | 80 | - (void)fetchArtworkWithSize:(SMKArtworkSize)size 81 | completionHandler:(void(^)(SMKPlatformNativeImage *image, NSError *error))handler 82 | { 83 | SPArtistBrowse *browse = [self SMK_associatedArtistBrowse]; 84 | [browse SMK_spotifyWaitAsyncThen:^{ 85 | SPImage *image = browse.firstPortrait; 86 | [image SMK_spotifyWaitAsyncThen:^{ 87 | if (handler) handler(image.image, nil); 88 | }]; 89 | }]; 90 | } 91 | 92 | - (SPArtistBrowse *)SMK_associatedArtistBrowse 93 | { 94 | __block SPArtistBrowse *browse = [self associatedValueForKey:SMKSPArtistBrowseKey]; 95 | if (!browse) { 96 | dispatch_sync([SPSession libSpotifyQueue], ^{ 97 | browse = [SPArtistBrowse browseArtist:self inSession:self.session type:SP_ARTISTBROWSE_NO_TRACKS]; 98 | }); 99 | [self associateValue:browse withKey:SMKSPArtistBrowseKey]; 100 | } 101 | return browse; 102 | } 103 | @end 104 | 105 | @implementation SPArtistBrowse (SMKArtist) 106 | 107 | #pragma mark - SMKHierarchicalLoading 108 | 109 | - (void)loadHierarchy:(dispatch_group_t)group array:(NSMutableArray *)array 110 | { 111 | __weak SPArtistBrowse *weakSelf = self; 112 | dispatch_group_enter(group); 113 | [self SMK_spotifyWaitAsyncThen:^{ 114 | SPArtistBrowse *strongSelf = weakSelf; 115 | [strongSelf.albums enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 116 | [obj loadHierarchy:group array:array]; 117 | }]; 118 | dispatch_group_leave(group); 119 | }]; 120 | } 121 | @end 122 | -------------------------------------------------------------------------------- /Content Sources/Spotify/Data Model/SPPlaylist+SMKPlaylist.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPPlaylist+SMKPlaylist.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #if TARGET_OS_IPHONE 10 | #import "CocoaLibSpotify.h" 11 | #else 12 | #import 13 | #endif 14 | 15 | #import "SMKPlaylist.h" 16 | #import "SMKWebObject.h" 17 | #import "SMKArtworkObject.h" 18 | #import "SMKHierarchicalLoading.h" 19 | 20 | @interface SPPlaylist (SMKPlaylist) 21 | 22 | #pragma mark - SMKPlaylist 23 | 24 | - (id)user; 25 | - (NSString *)extendedDescription; 26 | - (void)moveTracks:(NSArray*)tracks toIndex:(NSUInteger)index completionHandler:(void(^)(NSError *error))handler; 27 | @end 28 | 29 | @interface SPPlaylistItem (SMKPlaylist) 30 | @end 31 | 32 | @interface SPPlaylistContainer (SMKPlaylist) 33 | @end -------------------------------------------------------------------------------- /Content Sources/Spotify/Data Model/SPPlaylist+SMKPlaylist.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPPlaylist+SMKPlaylist.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SPPlaylist+SMKPlaylist.h" 10 | #import "SPUser+SMKUser.h" 11 | #import "SPAlbum+SMKAlbum.h" 12 | #import "SPArtist+SMKArtist.h" 13 | #import "NSObject+SMKSpotifyAdditions.h" 14 | #import "NSMutableArray+SMKAdditions.h" 15 | #import "SMKSpotifyHelpers.h" 16 | 17 | #import "SMKSpotifyConstants.h" 18 | #import "SMKSpotifyContentSource.h" 19 | 20 | 21 | @interface SPPlaylist (SMKInternal) 22 | @property (nonatomic, readwrite, assign) __unsafe_unretained SPSession *session; 23 | @end 24 | 25 | @implementation SPPlaylist (SMKPlaylist) 26 | 27 | #pragma mark - SMKPlaylist 28 | 29 | - (void)fetchTracksWithCompletionHandler:(void(^)(NSArray *tracks, NSError *error))handler 30 | { 31 | dispatch_queue_t queue = [(SMKSpotifyContentSource *)self.session spotifyLocalQueue]; 32 | [SMKSpotifyHelpers loadItemsAynchronously:self.items 33 | sortDescriptors:nil 34 | predicate:nil 35 | sortingQueue:queue 36 | completionHandler:handler]; 37 | } 38 | 39 | - (BOOL)isEditable 40 | { 41 | return YES; 42 | } 43 | 44 | - (id)user 45 | { 46 | return self.owner; 47 | } 48 | 49 | - (NSString *)extendedDescription 50 | { 51 | return self.playlistDescription; 52 | } 53 | 54 | - (void)moveTracks:(NSArray*)tracks 55 | toIndex:(NSUInteger)index 56 | completionHandler:(void(^)(NSError *error))handler 57 | { 58 | NSMutableIndexSet *indexSet = [NSMutableIndexSet indexSet]; 59 | __weak SPPlaylist *weakSelf = self; 60 | [tracks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 61 | SPPlaylist *strongSelf = weakSelf; 62 | NSUInteger itemsIndex = [strongSelf.items indexOfObject:obj]; 63 | if (itemsIndex != NSNotFound) 64 | [indexSet addIndex:itemsIndex]; 65 | }]; 66 | [self moveTracksAtIndexes:indexSet toIndex:index completionHandler:handler]; 67 | } 68 | 69 | #pragma mark - SMKContentObject 70 | 71 | - (NSString *)uniqueIdentifier 72 | { 73 | return [self.spotifyURL absoluteString]; 74 | } 75 | 76 | + (NSSet *)supportedSortKeys 77 | { 78 | return [NSSet setWithObjects:@"name", @"extendedDescription", @"items", nil]; 79 | } 80 | 81 | - (id)contentSource 82 | { 83 | return (id)self.session; 84 | } 85 | 86 | #pragma mark - SMKArtworkObject 87 | 88 | - (void)fetchArtworkWithSize:(SMKArtworkSize)size 89 | completionHandler:(void(^)(SMKPlatformNativeImage *image, NSError *error))handler 90 | { 91 | __weak SPPlaylist *weakSelf = self; 92 | [self.image SMK_spotifyWaitAsyncThen:^{ 93 | SPPlaylist *strongSelf = weakSelf; 94 | if (handler) handler(strongSelf.image.image, nil); 95 | }]; 96 | } 97 | 98 | #pragma mark - SMKWebObject 99 | 100 | - (NSURL *)webURL 101 | { 102 | return self.spotifyURL; 103 | } 104 | 105 | #pragma mark - SMKHierarchicalLoading 106 | 107 | - (void)loadHierarchy:(dispatch_group_t)group array:(NSMutableArray *)array 108 | { 109 | __weak SPPlaylist *weakSelf = self; 110 | dispatch_group_enter(group); 111 | [self SMK_spotifyWaitAsyncThen:^{ 112 | SPPlaylist *strongSelf = weakSelf; 113 | [strongSelf.items enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 114 | if ([obj conformsToProtocol:@protocol(SMKHierarchicalLoading)]) 115 | [obj loadHierarchy:group array:array]; 116 | }]; 117 | dispatch_group_leave(group); 118 | }]; 119 | } 120 | @end 121 | 122 | @implementation SPPlaylistItem (SMKPlaylist) 123 | 124 | #pragma mark - SMKHierarchicalLoading 125 | 126 | - (void)loadHierarchy:(dispatch_group_t)group array:(NSMutableArray *)array 127 | { 128 | if ([self.item conformsToProtocol:@protocol(SMKHierarchicalLoading)]) 129 | [(id)self.item loadHierarchy:group array:array]; 130 | } 131 | @end 132 | 133 | @implementation SPPlaylistContainer (SMKPlaylist) 134 | 135 | #pragma mark - SMKHierarchicalLoading 136 | 137 | - (void)loadHierarchy:(dispatch_group_t)group array:(NSMutableArray *)array 138 | { 139 | __weak SPPlaylistContainer *weakSelf = self; 140 | dispatch_group_enter(group); 141 | [self SMK_spotifyWaitAsyncThen:^{ 142 | SPPlaylistContainer *strongSelf = weakSelf; 143 | [strongSelf.flattenedPlaylists enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 144 | [obj loadHierarchy:group array:array]; 145 | }]; 146 | dispatch_group_leave(group); 147 | }]; 148 | } 149 | 150 | @end -------------------------------------------------------------------------------- /Content Sources/Spotify/Data Model/SPToplist+SMKPlaylist.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPToplist+SMKPlaylist.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-26. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #if TARGET_OS_IPHONE 10 | #import "CocoaLibSpotify.h" 11 | #else 12 | #import 13 | #endif 14 | 15 | #import "SMKPlaylist.h" 16 | #import "SMKHierarchicalLoading.h" 17 | 18 | @interface SPToplist (SMKPlaylist) 19 | @property (nonatomic, copy) NSString *name; 20 | @end 21 | -------------------------------------------------------------------------------- /Content Sources/Spotify/Data Model/SPToplist+SMKPlaylist.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPToplist+SMKPlaylist.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-26. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SPToplist+SMKPlaylist.h" 10 | #import "NSObject+SMKSpotifyAdditions.h" 11 | #import "NSObject+AssociatedObjects.h" 12 | #import "SMKSpotifyHelpers.h" 13 | #import "SMKSpotifyContentSource.h" 14 | 15 | static void* const SMKSPToplistNameKey = "SMK_SPToplistName"; 16 | 17 | @implementation SPToplist (SMKPlaylist) 18 | 19 | #pragma mark - SMKPlaylist 20 | 21 | - (void)fetchTracksWithCompletionHandler:(void(^)(NSArray *tracks, NSError *error))handler 22 | { 23 | dispatch_queue_t queue = [(SMKSpotifyContentSource *)self.session spotifyLocalQueue]; 24 | [SMKSpotifyHelpers loadItemsAynchronously:self.tracks 25 | sortDescriptors:nil 26 | predicate:nil 27 | sortingQueue:queue 28 | completionHandler:handler]; 29 | } 30 | 31 | - (BOOL)isEditable 32 | { 33 | return NO; 34 | } 35 | 36 | #pragma mark - SMKContentObject 37 | 38 | - (NSString *)name 39 | { 40 | return [self associatedValueForKey:SMKSPToplistNameKey]; 41 | } 42 | 43 | - (void)setName:(NSString *)name 44 | { 45 | [self associateValue:[name copy] withKey:SMKSPToplistNameKey]; 46 | } 47 | 48 | - (NSString *)uniqueIdentifier 49 | { 50 | // TODO: Figure out some sort of unique identifier since spotify URL isn't available 51 | return nil; 52 | } 53 | 54 | + (NSSet *)supportedSortKeys 55 | { 56 | return [NSSet setWithObjects:@"name", @"extendedDescription", @"tracks", @"artists", @"albums", nil]; 57 | } 58 | 59 | - (id)contentSource 60 | { 61 | return (id)self.session; 62 | } 63 | 64 | #pragma mark - SMKHierarchicalLoading 65 | 66 | - (void)loadHierarchy:(dispatch_group_t)group array:(NSMutableArray *)array 67 | { 68 | __weak SPToplist *weakSelf = self; 69 | dispatch_group_enter(group); 70 | [self SMK_spotifyWaitAsyncThen:^{ 71 | SPToplist *strongSelf = weakSelf; 72 | [strongSelf.tracks enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 73 | [obj loadHierarchy:group array:array]; 74 | }]; 75 | dispatch_group_leave(group); 76 | }]; 77 | } 78 | @end 79 | -------------------------------------------------------------------------------- /Content Sources/Spotify/Data Model/SPTrack+SMKTrack.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPTrack+SMKTrack.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #if TARGET_OS_IPHONE 10 | #import "CocoaLibSpotify.h" 11 | #else 12 | #import 13 | #endif 14 | 15 | #import "SMKTrack.h" 16 | #import "SMKWebObject.h" 17 | #import "SMKHierarchicalLoading.h" 18 | 19 | @interface SPTrack (SMKTrack) 20 | @end 21 | -------------------------------------------------------------------------------- /Content Sources/Spotify/Data Model/SPTrack+SMKTrack.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPTrack+SMKTrack.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SPTrack+SMKTrack.h" 10 | #import "NSObject+SMKSpotifyAdditions.h" 11 | #import "SPAlbum+SMKAlbum.h" 12 | #import "SPArtist+SMKArtist.h" 13 | 14 | @interface SPTrack (SMKInternal) 15 | @property (nonatomic, readwrite, assign) __unsafe_unretained SPSession *session; 16 | @end 17 | 18 | @implementation SPTrack (SMKTrack) 19 | 20 | - (id)artist 21 | { 22 | return self.album.artist; 23 | } 24 | 25 | - (NSString *)artistName 26 | { 27 | return self.album.artist.name; 28 | } 29 | 30 | - (NSString *)albumArtistName 31 | { 32 | return [self artistName]; 33 | } 34 | 35 | - (NSString *)uniqueIdentifier 36 | { 37 | return [self.spotifyURL absoluteString]; 38 | } 39 | 40 | - (id)contentSource 41 | { 42 | return (id)self.session; 43 | } 44 | 45 | + (NSSet *)supportedSortKeys 46 | { 47 | return [NSSet setWithObjects:@"artist", @"artistName", @"albumArtistName", @"duration", @"trackNumber", @"discNumber", @"name", @"popularity", nil]; 48 | } 49 | 50 | - (Class)playerClass 51 | { 52 | return NSClassFromString(@"SMKSpotifyPlayer"); 53 | } 54 | 55 | #pragma mark - SMKWebObject 56 | 57 | - (NSURL *)webURL 58 | { 59 | return self.spotifyURL; 60 | } 61 | 62 | #pragma mark - SMKHierarchicalLoading 63 | 64 | - (void)loadHierarchy:(dispatch_group_t)group array:(NSMutableArray *)array 65 | { 66 | dispatch_group_enter(group); 67 | [self SMK_spotifyWaitAsyncThen:^{ 68 | [array addObject:self]; 69 | dispatch_group_leave(group); 70 | }]; 71 | } 72 | @end 73 | -------------------------------------------------------------------------------- /Content Sources/Spotify/Data Model/SPUser+SMKUser.h: -------------------------------------------------------------------------------- 1 | // 2 | // SPUser+SMKUser.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #if TARGET_OS_IPHONE 10 | #import "CocoaLibSpotify.h" 11 | #else 12 | #import 13 | #endif 14 | 15 | #import "SMKUser.h" 16 | #import "SMKWebObject.h" 17 | 18 | @interface SPUser (SMKUser) 19 | @end 20 | -------------------------------------------------------------------------------- /Content Sources/Spotify/Data Model/SPUser+SMKUser.m: -------------------------------------------------------------------------------- 1 | // 2 | // SPUser+SMKUser.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SPUser+SMKUser.h" 10 | #import "NSObject+SMKSpotifyAdditions.h" 11 | 12 | @interface SPUser (SMKInternal) 13 | @property (nonatomic, readwrite, assign) __unsafe_unretained SPSession *session; 14 | @end 15 | 16 | @implementation SPUser (SMKUser) 17 | 18 | #pragma mark - SMKUser 19 | 20 | - (NSString *)username 21 | { 22 | return self.canonicalName; 23 | } 24 | 25 | #pragma mark - SMKContentObject 26 | 27 | - (NSString *)name 28 | { 29 | return self.displayName; 30 | } 31 | 32 | + (NSSet *)supportedSortKeys 33 | { 34 | return [NSSet setWithObjects:@"username", @"name", nil]; 35 | } 36 | 37 | - (NSString *)uniqueIdentifier 38 | { 39 | return [self.spotifyURL absoluteString]; 40 | } 41 | 42 | - (id)contentSource 43 | { 44 | return (id)self.session; 45 | } 46 | 47 | #pragma mark - SMKWebObject 48 | 49 | - (NSURL *)webURL 50 | { 51 | return self.spotifyURL; 52 | } 53 | @end 54 | -------------------------------------------------------------------------------- /Content Sources/Spotify/SMKSpotifyConstants.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKSpotifyConstants.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSTimeInterval const SMKSpotifyDefaultLoadingTimeout; -------------------------------------------------------------------------------- /Content Sources/Spotify/SMKSpotifyConstants.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKSpotifyConstants.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKSpotifyConstants.h" 10 | 11 | NSTimeInterval const SMKSpotifyDefaultLoadingTimeout = 20.0; -------------------------------------------------------------------------------- /Content Sources/Spotify/SMKSpotifyContentSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKSpotifyContentSource.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #if TARGET_OS_IPHONE 10 | #import "CocoaLibSpotify.h" 11 | #else 12 | #import 13 | #endif 14 | 15 | #import "SMKContentSource.h" 16 | #import "SPUser+SMKUser.h" 17 | #import "SPPlaylist+SMKPlaylist.h" 18 | #import "SPTrack+SMKTrack.h" 19 | #import "SPAlbum+SMKAlbum.h" 20 | #import "SPArtist+SMKArtist.h" 21 | #import "SMKSpotifyPlayer.h" 22 | 23 | @interface SMKSpotifyContentSource : SPSession 24 | /** This queue is used to sort and filter content before it's returned */ 25 | @property (nonatomic, readonly) dispatch_queue_t spotifyLocalQueue; 26 | @end 27 | -------------------------------------------------------------------------------- /Content Sources/Spotify/SMKSpotifyContentSource.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKSpotifyContentSource.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-24. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKSpotifyContentSource.h" 10 | #import "SMKSpotifyConstants.h" 11 | #import "SMKSpotifyPlayer.h" 12 | 13 | #import "NSObject+SMKSpotifyAdditions.h" 14 | #import "NSMutableArray+SMKAdditions.h" 15 | #import "SPToplist+SMKPlaylist.h" 16 | 17 | @implementation SMKSpotifyContentSource { 18 | dispatch_queue_t _localQueue; 19 | } 20 | #pragma mark - SMKContentSource 21 | 22 | - (NSString *)name { return @"Spotify"; } 23 | 24 | + (Class)predicateClass { return [NSPredicate class]; } 25 | 26 | - (void)fetchPlaylistsWithSortDescriptors:(NSArray *)sortDescriptors 27 | predicate:(NSPredicate *)predicate 28 | completionHandler:(void(^)(NSArray *playlists, NSError *error))handler 29 | { 30 | __weak SMKSpotifyContentSource *weakSelf = self; 31 | [self SMK_spotifyWaitAsyncThen:^{ 32 | SMKSpotifyContentSource *strongSelf = weakSelf; 33 | dispatch_async(strongSelf.spotifyLocalQueue, ^{ 34 | __block SPToplist *globalToplist = nil; 35 | __block SPToplist *userToplist = nil; 36 | dispatch_sync([SPSession libSpotifyQueue], ^{ 37 | globalToplist = [SPToplist globalToplistInSession:strongSelf]; 38 | userToplist = [SPToplist toplistForCurrentUserInSession:strongSelf]; 39 | }); 40 | dispatch_group_t group = dispatch_group_create(); 41 | dispatch_group_enter(group); 42 | [SPAsyncLoading waitUntilLoaded:@[strongSelf.starredPlaylist, strongSelf.inboxPlaylist, strongSelf.userPlaylists, globalToplist, userToplist] timeout:SMKSpotifyDefaultLoadingTimeout then:^(NSArray *loadedItems, NSArray *notLoadedItems) { 43 | [strongSelf.userPlaylists.playlists enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 44 | [obj SMK_spotifyWaitAsyncThen:nil group:group]; 45 | }]; 46 | dispatch_group_leave(group); 47 | }]; 48 | dispatch_group_notify(group, strongSelf.spotifyLocalQueue, ^{ 49 | strongSelf.inboxPlaylist.name = @"Inbox"; 50 | strongSelf.starredPlaylist.name = @"Starred"; 51 | globalToplist.name = @"Global Toplist"; 52 | userToplist.name = @"My Toplist"; 53 | NSMutableArray *playlists = [NSMutableArray arrayWithObjects:strongSelf.inboxPlaylist, strongSelf.starredPlaylist, globalToplist, userToplist, nil]; 54 | [playlists addObjectsFromArray:[strongSelf.userPlaylists flattenedPlaylists]]; 55 | [playlists SMK_processWithSortDescriptors:sortDescriptors 56 | predicate:predicate]; 57 | dispatch_async(dispatch_get_main_queue(), ^{ 58 | if (handler) 59 | handler(playlists, nil); 60 | }); 61 | }); 62 | dispatch_release(group); 63 | }); 64 | }]; 65 | } 66 | 67 | - (void)dealloc 68 | { 69 | if (_localQueue) 70 | dispatch_release(_localQueue); 71 | } 72 | 73 | #pragma mark - Accessors 74 | 75 | - (dispatch_queue_t)spotifyLocalQueue 76 | { 77 | if (!_localQueue) { 78 | _localQueue = dispatch_queue_create("com.indragie.SNRMusicKit.spotifyLocalQueue", DISPATCH_QUEUE_SERIAL); 79 | } 80 | return _localQueue; 81 | } 82 | @end 83 | -------------------------------------------------------------------------------- /Content Sources/Spotify/SMKSpotifyHelpers.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKSpotifyHelpers.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-25. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SMKSpotifyHelpers : NSObject 12 | + (void)loadItems:(NSArray *)items group:(dispatch_group_t)group array:(NSMutableArray *)array; 13 | + (void)loadItemsAynchronously:(NSArray *)items 14 | sortDescriptors:(NSArray *)sortDescriptors 15 | predicate:(NSPredicate *)predicate 16 | sortingQueue:(dispatch_queue_t)queue 17 | completionHandler:(void(^)(NSArray *objects, NSError *error))handler; 18 | @end 19 | -------------------------------------------------------------------------------- /Content Sources/Spotify/SMKSpotifyHelpers.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKSpotifyHelpers.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-25. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKSpotifyHelpers.h" 10 | #import "NSObject+SMKSpotifyAdditions.h" 11 | #import "NSMutableArray+SMKAdditions.h" 12 | #import "SMKHierarchicalLoading.h" 13 | #import 14 | 15 | @implementation SMKSpotifyHelpers 16 | + (void)loadItems:(NSArray *)items group:(dispatch_group_t)group array:(NSMutableArray *)array 17 | { 18 | [items enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 19 | if ([obj conformsToProtocol:@protocol(SMKHierarchicalLoading)]) 20 | [obj loadHierarchy:group array:array]; 21 | }]; 22 | } 23 | 24 | + (void)loadItemsAynchronously:(NSArray *)items 25 | sortDescriptors:(NSArray *)sortDescriptors 26 | predicate:(NSPredicate *)predicate 27 | sortingQueue:(dispatch_queue_t)queue 28 | completionHandler:(void(^)(NSArray *objects, NSError *error))handler 29 | { 30 | NSMutableArray *hierarchy = [NSMutableArray array]; 31 | dispatch_group_t group = dispatch_group_create(); 32 | [self loadItems:items group:group array:hierarchy]; 33 | dispatch_group_notify(group, queue, ^{ 34 | [hierarchy SMK_processWithSortDescriptors:sortDescriptors predicate:predicate]; 35 | dispatch_async(dispatch_get_main_queue(), ^{ 36 | if (handler) handler(hierarchy, nil); 37 | }); 38 | }); 39 | dispatch_release(group); 40 | } 41 | @end 42 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Categories/NSManagedObjectContext+SMKiTunesAdditions.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSManagedObjectContext+SMKiTunesAdditions.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-22. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKiTunesArtist.h" 11 | #import "SMKiTunesAlbum.h" 12 | #import "SMKiTunesTrack.h" 13 | #import "SMKiTunesPlaylist.h" 14 | 15 | @interface NSManagedObjectContext (SMKiTunesAdditions) 16 | /** 17 | @param name The artist name 18 | @param create Whether to create the artist if it doesn't exist 19 | @return The SMKiTunesArtist object 20 | */ 21 | - (SMKiTunesArtist *)SMK_iTunesArtistWithName:(NSString *)name create:(BOOL)create; 22 | 23 | /** 24 | @param name The album name 25 | @param artist The artist of the album 26 | @param create Whether to create the album if it doesn't exist 27 | @return The SMKiTunesAlbum object 28 | */ 29 | - (SMKiTunesAlbum *)SMK_iTunesAlbumWithName:(NSString *)name byArtist:(SMKiTunesArtist *)artist create:(BOOL)create; 30 | 31 | /** 32 | @param identifier The iTunes Persistent ID of the track 33 | @return The SMKiTunesTrack object 34 | */ 35 | - (SMKiTunesTrack *)SMK_iTunesTrackWithIdentifier:(NSString *)identifier; 36 | 37 | /** 38 | @param identifier THe iTunes Persistent ID of the playlist 39 | @return The SNRiTunesPlaylist object 40 | */ 41 | - (SMKiTunesPlaylist *)SMK_iTunesPlaylistWithIdentifier:(NSString *)identifier; 42 | 43 | /** 44 | @return The shared compilations artist 45 | */ 46 | - (SMKiTunesArtist *)SMK_iTunesCompilationsArtist; 47 | @end 48 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Categories/NSManagedObjectContext+SMKiTunesAdditions.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSManagedObjectContext+SMKiTunesAdditions.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-22. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "NSManagedObjectContext+SMKiTunesAdditions.h" 10 | #import "NSManagedObjectContext+SMKAdditions.h" 11 | #import "SMKiTunesConstants.h" 12 | 13 | @implementation NSManagedObjectContext (SMKiTunesAdditions) 14 | - (SMKiTunesArtist *)SMK_iTunesArtistWithName:(NSString *)name create:(BOOL)create 15 | { 16 | if (!SMKObjectIsValid(name)) 17 | name = SMKiTunesUntitledArtistName; 18 | NSString *normalizedName = [SMKiTunesArtist sortingNameForName:name]; 19 | NSError *error = nil; 20 | NSArray *fetchedObjects = [self SMK_legacyFetchWithEntityName:SMKiTunesEntityNameArtist sortDescriptors:nil predicate:[NSPredicate predicateWithFormat:@"normalizedName == %@", normalizedName] batchSize:0 fetchLimit:1 error:&error]; 21 | if (error) 22 | SMKGenericErrorLog([NSString stringWithFormat:@"Error fetching iTunes artist with name %@", name], error); 23 | if (![fetchedObjects count]) { 24 | if (create) { 25 | SMKiTunesArtist *artist = [self SMK_createObjectOfEntityName:SMKiTunesEntityNameArtist]; 26 | [artist setName:name]; 27 | return artist; 28 | } 29 | return nil; 30 | } 31 | return [fetchedObjects objectAtIndex:0]; 32 | } 33 | 34 | - (SMKiTunesAlbum *)SMK_iTunesAlbumWithName:(NSString *)name byArtist:(SMKiTunesArtist *)artist create:(BOOL)create 35 | { 36 | if (!SMKObjectIsValid(name)) 37 | name = SMKiTunesUntitledAlbumName; 38 | NSString *normalizedName = [SMKiTunesAlbum sortingNameForName:name]; 39 | NSError *error = nil; 40 | NSArray *fetchedObjects = [self SMK_legacyFetchWithEntityName:SMKiTunesEntityNameAlbum sortDescriptors:nil predicate:[NSPredicate predicateWithFormat:@"(normalizedName == %@) AND (artist == %@)", normalizedName, artist] batchSize:0 fetchLimit:1 error:&error]; 41 | if (error) 42 | SMKGenericErrorLog([NSString stringWithFormat:@"Error fetching iTunes album with name %@", name], error); 43 | if (![fetchedObjects count]) { 44 | if (create) { 45 | SMKiTunesAlbum *album = [self SMK_createObjectOfEntityName:SMKiTunesEntityNameAlbum]; 46 | [album setName:name]; 47 | return album; 48 | } 49 | return nil; 50 | } 51 | return [fetchedObjects objectAtIndex:0]; 52 | } 53 | 54 | - (SMKiTunesTrack *)SMK_iTunesTrackWithIdentifier:(NSString *)identifier 55 | { 56 | NSError *error = nil; 57 | NSArray *fetchedObjects = [self SMK_legacyFetchWithEntityName:SMKiTunesEntityNameTrack sortDescriptors:nil predicate:[NSPredicate predicateWithFormat:@"identifier == %@", identifier] batchSize:0 fetchLimit:1 error:&error]; 58 | if (error) 59 | SMKGenericErrorLog([NSString stringWithFormat:@"Error fetching iTunes track with identifier %@", identifier], error); 60 | return ([fetchedObjects count] != 0) ? [fetchedObjects objectAtIndex:0] : nil; 61 | } 62 | 63 | - (SMKiTunesPlaylist *)SMK_iTunesPlaylistWithIdentifier:(NSString *)identifier 64 | { 65 | NSError *error = nil; 66 | NSArray *fetchedObjects = [self SMK_legacyFetchWithEntityName:SMKiTunesEntityNamePlaylist sortDescriptors:nil predicate:[NSPredicate predicateWithFormat:@"identifier == %@", identifier] batchSize:0 fetchLimit:1 error:&error]; 67 | if (error) 68 | SMKGenericErrorLog([NSString stringWithFormat:@"Error fetching iTunes playlist with identifier %@", identifier], error); 69 | return ([fetchedObjects count] != 0) ? [fetchedObjects objectAtIndex:0] : nil; 70 | } 71 | 72 | - (SMKiTunesArtist *)SMK_iTunesCompilationsArtist 73 | { 74 | return [self SMK_iTunesArtistWithName:SMKiTunesCompilationsArtistName create:YES]; 75 | } 76 | @end 77 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/SMKiTunesDataModel.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | SMKiTunesDataModel.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/SMKITunesAlbum.h: -------------------------------------------------------------------------------- 1 | #import "_SMKiTunesAlbum.h" 2 | #import "SMKAlbum.h" 3 | 4 | @interface SMKiTunesAlbum : _SMKiTunesAlbum {} 5 | @property (nonatomic, assign, readonly) NSTimeInterval duration; 6 | @property (nonatomic, assign, readonly) BOOL isCompilation; 7 | @property (nonatomic, assign, readonly) NSUInteger releaseYear; 8 | @property (nonatomic, assign, readonly) NSUInteger rating; 9 | @end 10 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/SMKITunesAlbum.m: -------------------------------------------------------------------------------- 1 | #import "SMKiTunesAlbum.h" 2 | #import "NSManagedObjectContext+SMKAdditions.h" 3 | #import "SMKiTunesConstants.h" 4 | #import "SMKiTunesTrack.h" 5 | #import "SMKManagedObjectContext.h" 6 | 7 | @implementation SMKiTunesAlbum 8 | 9 | - (void)fetchTracksWithSortDescriptors:(NSArray *)sortDescriptors 10 | predicate:(NSPredicate *)predicate 11 | completionHandler:(void(^)(NSArray *tracks, NSError *error))handler 12 | { 13 | SMKManagedObjectContext *context = (SMKManagedObjectContext *)[self managedObjectContext]; 14 | NSPredicate *finalPredicate = [self _compoundTrackPredicateWithPredicate:predicate]; 15 | [context SMK_asyncFetchWithEntityName:SMKiTunesEntityNameTrack 16 | sortDescriptors:sortDescriptors 17 | predicate:finalPredicate 18 | batchSize:context.defaultFetchBatchSize 19 | fetchLimit:0 20 | completionHandler:handler]; 21 | } 22 | 23 | + (NSSet *)supportedSortKeys 24 | { 25 | return [NSSet setWithObjects:@"name", @"releaseYear", @"artist", @"isCompilation", @"rating", nil]; 26 | } 27 | 28 | - (NSTimeInterval)duration 29 | { 30 | return [[self valueForKey:@"tracks.@sum.duration"] floatValue]; 31 | } 32 | 33 | - (BOOL)isCompilation 34 | { 35 | return [self cd_isCompilationValue]; 36 | } 37 | 38 | - (NSUInteger)releaseYear 39 | { 40 | return [self cd_releaseYearValue]; 41 | } 42 | 43 | - (NSUInteger)rating 44 | { 45 | return [self cd_ratingValue]; 46 | } 47 | 48 | #pragma mark - Private 49 | 50 | - (NSPredicate *)_compoundTrackPredicateWithPredicate:(NSPredicate *)predicate 51 | { 52 | NSPredicate *basePredicate = [NSPredicate predicateWithFormat:@"album == %@", self]; 53 | if (predicate) 54 | basePredicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[basePredicate, predicate]]; 55 | return basePredicate; 56 | } 57 | @end 58 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/SMKiTunesArtist.h: -------------------------------------------------------------------------------- 1 | #import "_SMKiTunesArtist.h" 2 | #import "SMKArtist.h" 3 | 4 | @interface SMKiTunesArtist : _SMKiTunesArtist {} 5 | @end 6 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/SMKiTunesArtist.m: -------------------------------------------------------------------------------- 1 | #import "SMKiTunesArtist.h" 2 | #import "SMKiTunesConstants.h" 3 | #import "NSManagedObjectContext+SMKAdditions.h" 4 | #import "SMKManagedObjectContext.h" 5 | #import "NSString+SMKAdditions.h" 6 | 7 | @implementation SMKiTunesArtist 8 | 9 | - (void)fetchAlbumsWithSortDescriptors:(NSArray *)sortDescriptors 10 | predicate:(NSPredicate *)predicate 11 | completionHandler:(void(^)(NSArray *albums, NSError *error))handler 12 | { 13 | NSPredicate *finalPredicate = [self _compoundAlbumPredicateWithPredicate:predicate]; 14 | SMKManagedObjectContext *context = (SMKManagedObjectContext *)[self managedObjectContext]; 15 | [context SMK_asyncFetchWithEntityName:SMKiTunesEntityNameArtist 16 | sortDescriptors:sortDescriptors 17 | predicate:finalPredicate 18 | batchSize:context.defaultFetchBatchSize 19 | fetchLimit:0 20 | completionHandler:handler]; 21 | } 22 | 23 | + (NSString *)sortingNameForName:(NSString *)name 24 | { 25 | NSArray *filterTerms = [NSArray arrayWithObjects:@"the ", @"a ", @"an ", nil]; 26 | NSMutableString *sortString = [NSMutableString stringWithString:name]; 27 | for (NSString *term in filterTerms) { 28 | NSRange range = [sortString rangeOfString:term options:NSCaseInsensitiveSearch]; 29 | if ((range.location == 0) && (range.length > 0)) { 30 | [sortString replaceCharactersInRange:range withString:@""]; 31 | break; 32 | } 33 | } 34 | return [sortString SMK_normalizedString]; 35 | } 36 | 37 | #pragma mark - Private 38 | 39 | - (NSPredicate *)_compoundAlbumPredicateWithPredicate:(NSPredicate *)predicate 40 | { 41 | NSPredicate *basePredicate = [NSPredicate predicateWithFormat:@"artist == %@", self]; 42 | if (predicate) 43 | basePredicate = [NSCompoundPredicate andPredicateWithSubpredicates:@[basePredicate, predicate]]; 44 | return basePredicate; 45 | } 46 | @end 47 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/SMKiTunesKeyword.h: -------------------------------------------------------------------------------- 1 | #import "_SMKiTunesKeyword.h" 2 | 3 | @interface SMKiTunesKeyword : _SMKiTunesKeyword {} 4 | // Custom logic goes here. 5 | @end 6 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/SMKiTunesKeyword.m: -------------------------------------------------------------------------------- 1 | #import "SMKiTunesKeyword.h" 2 | 3 | @implementation SMKiTunesKeyword 4 | 5 | // Custom logic goes here. 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/SMKiTunesObject.h: -------------------------------------------------------------------------------- 1 | #import "_SMKiTunesObject.h" 2 | #import "SMKContentSource.h" 3 | 4 | @interface SMKiTunesObject : _SMKiTunesObject {} 5 | - (NSString *)uniqueIdentifier; 6 | - (id)contentSource; 7 | + (NSSet *)supportedSortKeys; 8 | 9 | + (NSString *)sortingNameForName:(NSString *)name; 10 | @end 11 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/SMKiTunesObject.m: -------------------------------------------------------------------------------- 1 | #import "SMKiTunesObject.h" 2 | #import "NSManagedObjectContext+SMKAdditions.h" 3 | #import "NSString+SMKAdditions.h" 4 | #import "SMKManagedObjectContext.h" 5 | 6 | @implementation SMKiTunesObject 7 | 8 | - (NSString *)uniqueIdentifier 9 | { 10 | return [[[self objectID] URIRepresentation] absoluteString]; 11 | } 12 | 13 | - (id)contentSource 14 | { 15 | return [(SMKManagedObjectContext *)[self managedObjectContext] SMK_contentSource]; 16 | } 17 | 18 | + (NSSet *)supportedSortKeys 19 | { 20 | return [NSSet setWithObjects:@"name", nil]; 21 | } 22 | 23 | + (NSString *)sortingNameForName:(NSString *)name 24 | { 25 | return [name SMK_normalizedString]; 26 | } 27 | 28 | #pragma mark - Accessors 29 | 30 | - (void)setName:(NSString *)name 31 | { 32 | [self willChangeValueForKey:@"name"]; 33 | [self setPrimitiveName:name]; 34 | [self didChangeValueForKey:@"name"]; 35 | [self setNormalizedName:[[self class] sortingNameForName:name]]; 36 | } 37 | @end 38 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/SMKiTunesPlaylist.h: -------------------------------------------------------------------------------- 1 | #import "_SMKiTunesPlaylist.h" 2 | #import "SMKPlaylist.h" 3 | 4 | @interface SMKiTunesPlaylist : _SMKiTunesPlaylist {} 5 | @end 6 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/SMKiTunesPlaylist.m: -------------------------------------------------------------------------------- 1 | #import "SMKiTunesPlaylist.h" 2 | #import "NSManagedObjectContext+SMKAdditions.h" 3 | #import "SMKiTunesConstants.h" 4 | #import "SMKManagedObjectContext.h" 5 | 6 | @implementation SMKiTunesPlaylist 7 | 8 | - (void)fetchTracksWithCompletionHandler:(void(^)(NSArray *tracks, NSError *error))handler 9 | { 10 | SMKManagedObjectContext *context = (SMKManagedObjectContext *)[self managedObjectContext]; 11 | [context SMK_asyncFetchWithEntityName:SMKiTunesEntityNameTrack 12 | sortDescriptors:nil 13 | predicate:[NSPredicate predicateWithFormat:@"%@ in playlists", self] 14 | batchSize:context.defaultFetchBatchSize 15 | fetchLimit:0 16 | completionHandler:handler]; 17 | } 18 | 19 | - (BOOL)isEditable { return NO; } 20 | 21 | - (NSString *)uniqueIdentifier 22 | { 23 | return self.identifier; 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/SMKiTunesTrack.h: -------------------------------------------------------------------------------- 1 | #import "_SMKiTunesTrack.h" 2 | #import "SMKTrack.h" 3 | 4 | @interface SMKiTunesTrack : _SMKiTunesTrack {} 5 | // SMKTrack @optional 6 | @property (nonatomic, assign, readonly) NSUInteger trackNumber; 7 | @property (nonatomic, assign, readonly) NSUInteger discNumber; 8 | @property (nonatomic, assign, readonly) NSUInteger rating; 9 | @property (nonatomic, assign, readonly) BOOL isExplicit; 10 | @property (nonatomic, assign, readonly) BOOL isClean; 11 | @property (nonatomic, strong, readonly) NSString *genre; 12 | @property (nonatomic, strong, readonly) NSURL *playbackURL; 13 | @end 14 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/SMKiTunesTrack.m: -------------------------------------------------------------------------------- 1 | #import "SMKiTunesTrack.h" 2 | #import "SMKiTunesAlbum.h" 3 | 4 | @implementation SMKiTunesTrack 5 | 6 | - (id)artist 7 | { 8 | return [self.album artist]; 9 | } 10 | 11 | - (NSString *)uniqueIdentifier 12 | { 13 | return self.identifier; 14 | } 15 | 16 | - (NSTimeInterval)duration 17 | { 18 | return [self cd_durationValue]; 19 | } 20 | 21 | - (NSUInteger)trackNumber 22 | { 23 | return [self cd_trackNumberValue]; 24 | } 25 | 26 | - (NSUInteger)discNumber 27 | { 28 | return [self cd_discNumberValue]; 29 | } 30 | 31 | - (NSUInteger)rating 32 | { 33 | return [self cd_ratingValue]; 34 | } 35 | 36 | - (BOOL)isExplicit 37 | { 38 | return [self cd_isExplicitValue]; 39 | } 40 | 41 | - (BOOL)isClean 42 | { 43 | return [self cd_isCleanValue]; 44 | } 45 | 46 | - (NSURL *)playbackURL 47 | { 48 | if (!self.bookmark) { return nil; } 49 | NSError *error = nil; 50 | NSURL *URL = [NSURL URLByResolvingBookmarkData:self.bookmark options:NSURLBookmarkResolutionWithoutUI relativeToURL:nil bookmarkDataIsStale:NULL error:&error]; 51 | if (error) { 52 | NSLog(@"Error retrieving bookmark data for %@: %@ %@", self, error, [error userInfo]); 53 | } 54 | return URL; 55 | } 56 | 57 | + (NSSet *)supportedSortKeys 58 | { 59 | return [NSSet setWithObjects:@"album", @"duration", @"composer", @"trackNumber", @"discNumber", @"isExplicit", @"isClean", @"genre", @"rating", nil]; 60 | } 61 | 62 | - (Class)playerClass 63 | { 64 | return NSClassFromString(@"SMKAVQueuePlayer"); 65 | } 66 | @end 67 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/_SMKITunesAlbum.h: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This file is machine-generated and constantly overwritten. 2 | // Make changes to SMKiTunesAlbum.h instead. 3 | 4 | #import 5 | #import "SMKiTunesObject.h" 6 | 7 | extern const struct SMKiTunesAlbumAttributes { 8 | __unsafe_unretained NSString *cd_isCompilation; 9 | __unsafe_unretained NSString *cd_rating; 10 | __unsafe_unretained NSString *cd_releaseYear; 11 | } SMKiTunesAlbumAttributes; 12 | 13 | extern const struct SMKiTunesAlbumRelationships { 14 | __unsafe_unretained NSString *artist; 15 | __unsafe_unretained NSString *tracks; 16 | } SMKiTunesAlbumRelationships; 17 | 18 | extern const struct SMKiTunesAlbumFetchedProperties { 19 | } SMKiTunesAlbumFetchedProperties; 20 | 21 | @class SMKiTunesArtist; 22 | @class SMKiTunesTrack; 23 | 24 | 25 | 26 | 27 | 28 | @interface SMKiTunesAlbumID : NSManagedObjectID {} 29 | @end 30 | 31 | @interface _SMKiTunesAlbum : SMKiTunesObject {} 32 | + (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_; 33 | + (NSString*)entityName; 34 | + (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_; 35 | - (SMKiTunesAlbumID*)objectID; 36 | 37 | 38 | 39 | 40 | @property (nonatomic, strong) NSNumber* cd_isCompilation; 41 | 42 | 43 | @property BOOL cd_isCompilationValue; 44 | - (BOOL)cd_isCompilationValue; 45 | - (void)setCd_isCompilationValue:(BOOL)value_; 46 | 47 | //- (BOOL)validateCd_isCompilation:(id*)value_ error:(NSError**)error_; 48 | 49 | 50 | 51 | 52 | @property (nonatomic, strong) NSNumber* cd_rating; 53 | 54 | 55 | @property int32_t cd_ratingValue; 56 | - (int32_t)cd_ratingValue; 57 | - (void)setCd_ratingValue:(int32_t)value_; 58 | 59 | //- (BOOL)validateCd_rating:(id*)value_ error:(NSError**)error_; 60 | 61 | 62 | 63 | 64 | @property (nonatomic, strong) NSNumber* cd_releaseYear; 65 | 66 | 67 | @property int32_t cd_releaseYearValue; 68 | - (int32_t)cd_releaseYearValue; 69 | - (void)setCd_releaseYearValue:(int32_t)value_; 70 | 71 | //- (BOOL)validateCd_releaseYear:(id*)value_ error:(NSError**)error_; 72 | 73 | 74 | 75 | 76 | 77 | @property (nonatomic, strong) SMKiTunesArtist* artist; 78 | 79 | //- (BOOL)validateArtist:(id*)value_ error:(NSError**)error_; 80 | 81 | 82 | 83 | 84 | @property (nonatomic, strong) NSSet* tracks; 85 | 86 | - (NSMutableSet*)tracksSet; 87 | 88 | 89 | 90 | 91 | 92 | @end 93 | 94 | @interface _SMKiTunesAlbum (CoreDataGeneratedAccessors) 95 | 96 | - (void)addTracks:(NSSet*)value_; 97 | - (void)removeTracks:(NSSet*)value_; 98 | - (void)addTracksObject:(SMKiTunesTrack*)value_; 99 | - (void)removeTracksObject:(SMKiTunesTrack*)value_; 100 | 101 | @end 102 | 103 | @interface _SMKiTunesAlbum (CoreDataGeneratedPrimitiveAccessors) 104 | 105 | 106 | - (NSNumber*)primitiveCd_isCompilation; 107 | - (void)setPrimitiveCd_isCompilation:(NSNumber*)value; 108 | 109 | - (BOOL)primitiveCd_isCompilationValue; 110 | - (void)setPrimitiveCd_isCompilationValue:(BOOL)value_; 111 | 112 | 113 | 114 | 115 | - (NSNumber*)primitiveCd_rating; 116 | - (void)setPrimitiveCd_rating:(NSNumber*)value; 117 | 118 | - (int32_t)primitiveCd_ratingValue; 119 | - (void)setPrimitiveCd_ratingValue:(int32_t)value_; 120 | 121 | 122 | 123 | 124 | - (NSNumber*)primitiveCd_releaseYear; 125 | - (void)setPrimitiveCd_releaseYear:(NSNumber*)value; 126 | 127 | - (int32_t)primitiveCd_releaseYearValue; 128 | - (void)setPrimitiveCd_releaseYearValue:(int32_t)value_; 129 | 130 | 131 | 132 | 133 | 134 | - (SMKiTunesArtist*)primitiveArtist; 135 | - (void)setPrimitiveArtist:(SMKiTunesArtist*)value; 136 | 137 | 138 | 139 | - (NSMutableSet*)primitiveTracks; 140 | - (void)setPrimitiveTracks:(NSMutableSet*)value; 141 | 142 | 143 | @end 144 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/_SMKITunesAlbum.m: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This file is machine-generated and constantly overwritten. 2 | // Make changes to SMKiTunesAlbum.m instead. 3 | 4 | #import "_SMKiTunesAlbum.h" 5 | 6 | const struct SMKiTunesAlbumAttributes SMKiTunesAlbumAttributes = { 7 | .cd_isCompilation = @"cd_isCompilation", 8 | .cd_rating = @"cd_rating", 9 | .cd_releaseYear = @"cd_releaseYear", 10 | }; 11 | 12 | const struct SMKiTunesAlbumRelationships SMKiTunesAlbumRelationships = { 13 | .artist = @"artist", 14 | .tracks = @"tracks", 15 | }; 16 | 17 | const struct SMKiTunesAlbumFetchedProperties SMKiTunesAlbumFetchedProperties = { 18 | }; 19 | 20 | @implementation SMKiTunesAlbumID 21 | @end 22 | 23 | @implementation _SMKiTunesAlbum 24 | 25 | + (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_ { 26 | NSParameterAssert(moc_); 27 | return [NSEntityDescription insertNewObjectForEntityForName:@"SMKiTunesAlbum" inManagedObjectContext:moc_]; 28 | } 29 | 30 | + (NSString*)entityName { 31 | return @"SMKiTunesAlbum"; 32 | } 33 | 34 | + (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_ { 35 | NSParameterAssert(moc_); 36 | return [NSEntityDescription entityForName:@"SMKiTunesAlbum" inManagedObjectContext:moc_]; 37 | } 38 | 39 | - (SMKiTunesAlbumID*)objectID { 40 | return (SMKiTunesAlbumID*)[super objectID]; 41 | } 42 | 43 | + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { 44 | NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; 45 | 46 | if ([key isEqualToString:@"cd_isCompilationValue"]) { 47 | NSSet *affectingKey = [NSSet setWithObject:@"cd_isCompilation"]; 48 | keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKey]; 49 | } 50 | if ([key isEqualToString:@"cd_ratingValue"]) { 51 | NSSet *affectingKey = [NSSet setWithObject:@"cd_rating"]; 52 | keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKey]; 53 | } 54 | if ([key isEqualToString:@"cd_releaseYearValue"]) { 55 | NSSet *affectingKey = [NSSet setWithObject:@"cd_releaseYear"]; 56 | keyPaths = [keyPaths setByAddingObjectsFromSet:affectingKey]; 57 | } 58 | 59 | return keyPaths; 60 | } 61 | 62 | 63 | 64 | 65 | @dynamic cd_isCompilation; 66 | 67 | 68 | 69 | - (BOOL)cd_isCompilationValue { 70 | NSNumber *result = [self cd_isCompilation]; 71 | return [result boolValue]; 72 | } 73 | 74 | - (void)setCd_isCompilationValue:(BOOL)value_ { 75 | [self setCd_isCompilation:[NSNumber numberWithBool:value_]]; 76 | } 77 | 78 | - (BOOL)primitiveCd_isCompilationValue { 79 | NSNumber *result = [self primitiveCd_isCompilation]; 80 | return [result boolValue]; 81 | } 82 | 83 | - (void)setPrimitiveCd_isCompilationValue:(BOOL)value_ { 84 | [self setPrimitiveCd_isCompilation:[NSNumber numberWithBool:value_]]; 85 | } 86 | 87 | 88 | 89 | 90 | 91 | @dynamic cd_rating; 92 | 93 | 94 | 95 | - (int32_t)cd_ratingValue { 96 | NSNumber *result = [self cd_rating]; 97 | return [result intValue]; 98 | } 99 | 100 | - (void)setCd_ratingValue:(int32_t)value_ { 101 | [self setCd_rating:[NSNumber numberWithInt:value_]]; 102 | } 103 | 104 | - (int32_t)primitiveCd_ratingValue { 105 | NSNumber *result = [self primitiveCd_rating]; 106 | return [result intValue]; 107 | } 108 | 109 | - (void)setPrimitiveCd_ratingValue:(int32_t)value_ { 110 | [self setPrimitiveCd_rating:[NSNumber numberWithInt:value_]]; 111 | } 112 | 113 | 114 | 115 | 116 | 117 | @dynamic cd_releaseYear; 118 | 119 | 120 | 121 | - (int32_t)cd_releaseYearValue { 122 | NSNumber *result = [self cd_releaseYear]; 123 | return [result intValue]; 124 | } 125 | 126 | - (void)setCd_releaseYearValue:(int32_t)value_ { 127 | [self setCd_releaseYear:[NSNumber numberWithInt:value_]]; 128 | } 129 | 130 | - (int32_t)primitiveCd_releaseYearValue { 131 | NSNumber *result = [self primitiveCd_releaseYear]; 132 | return [result intValue]; 133 | } 134 | 135 | - (void)setPrimitiveCd_releaseYearValue:(int32_t)value_ { 136 | [self setPrimitiveCd_releaseYear:[NSNumber numberWithInt:value_]]; 137 | } 138 | 139 | 140 | 141 | 142 | 143 | @dynamic artist; 144 | 145 | 146 | 147 | @dynamic tracks; 148 | 149 | 150 | - (NSMutableSet*)tracksSet { 151 | [self willAccessValueForKey:@"tracks"]; 152 | 153 | NSMutableSet *result = (NSMutableSet*)[self mutableSetValueForKey:@"tracks"]; 154 | 155 | [self didAccessValueForKey:@"tracks"]; 156 | return result; 157 | } 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | @end 166 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/_SMKiTunesArtist.h: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This file is machine-generated and constantly overwritten. 2 | // Make changes to SMKiTunesArtist.h instead. 3 | 4 | #import 5 | #import "SMKiTunesObject.h" 6 | 7 | extern const struct SMKiTunesArtistAttributes { 8 | } SMKiTunesArtistAttributes; 9 | 10 | extern const struct SMKiTunesArtistRelationships { 11 | __unsafe_unretained NSString *albums; 12 | } SMKiTunesArtistRelationships; 13 | 14 | extern const struct SMKiTunesArtistFetchedProperties { 15 | } SMKiTunesArtistFetchedProperties; 16 | 17 | @class SMKiTunesAlbum; 18 | 19 | 20 | @interface SMKiTunesArtistID : NSManagedObjectID {} 21 | @end 22 | 23 | @interface _SMKiTunesArtist : SMKiTunesObject {} 24 | + (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_; 25 | + (NSString*)entityName; 26 | + (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_; 27 | - (SMKiTunesArtistID*)objectID; 28 | 29 | 30 | 31 | 32 | 33 | @property (nonatomic, strong) NSSet* albums; 34 | 35 | - (NSMutableSet*)albumsSet; 36 | 37 | 38 | 39 | 40 | 41 | @end 42 | 43 | @interface _SMKiTunesArtist (CoreDataGeneratedAccessors) 44 | 45 | - (void)addAlbums:(NSSet*)value_; 46 | - (void)removeAlbums:(NSSet*)value_; 47 | - (void)addAlbumsObject:(SMKiTunesAlbum*)value_; 48 | - (void)removeAlbumsObject:(SMKiTunesAlbum*)value_; 49 | 50 | @end 51 | 52 | @interface _SMKiTunesArtist (CoreDataGeneratedPrimitiveAccessors) 53 | 54 | 55 | 56 | - (NSMutableSet*)primitiveAlbums; 57 | - (void)setPrimitiveAlbums:(NSMutableSet*)value; 58 | 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/_SMKiTunesArtist.m: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This file is machine-generated and constantly overwritten. 2 | // Make changes to SMKiTunesArtist.m instead. 3 | 4 | #import "_SMKiTunesArtist.h" 5 | 6 | const struct SMKiTunesArtistAttributes SMKiTunesArtistAttributes = { 7 | }; 8 | 9 | const struct SMKiTunesArtistRelationships SMKiTunesArtistRelationships = { 10 | .albums = @"albums", 11 | }; 12 | 13 | const struct SMKiTunesArtistFetchedProperties SMKiTunesArtistFetchedProperties = { 14 | }; 15 | 16 | @implementation SMKiTunesArtistID 17 | @end 18 | 19 | @implementation _SMKiTunesArtist 20 | 21 | + (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_ { 22 | NSParameterAssert(moc_); 23 | return [NSEntityDescription insertNewObjectForEntityForName:@"SMKiTunesArtist" inManagedObjectContext:moc_]; 24 | } 25 | 26 | + (NSString*)entityName { 27 | return @"SMKiTunesArtist"; 28 | } 29 | 30 | + (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_ { 31 | NSParameterAssert(moc_); 32 | return [NSEntityDescription entityForName:@"SMKiTunesArtist" inManagedObjectContext:moc_]; 33 | } 34 | 35 | - (SMKiTunesArtistID*)objectID { 36 | return (SMKiTunesArtistID*)[super objectID]; 37 | } 38 | 39 | + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { 40 | NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; 41 | 42 | 43 | return keyPaths; 44 | } 45 | 46 | 47 | 48 | 49 | @dynamic albums; 50 | 51 | 52 | - (NSMutableSet*)albumsSet { 53 | [self willAccessValueForKey:@"albums"]; 54 | 55 | NSMutableSet *result = (NSMutableSet*)[self mutableSetValueForKey:@"albums"]; 56 | 57 | [self didAccessValueForKey:@"albums"]; 58 | return result; 59 | } 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/_SMKiTunesKeyword.h: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This file is machine-generated and constantly overwritten. 2 | // Make changes to SMKiTunesKeyword.h instead. 3 | 4 | #import 5 | 6 | 7 | extern const struct SMKiTunesKeywordAttributes { 8 | __unsafe_unretained NSString *name; 9 | } SMKiTunesKeywordAttributes; 10 | 11 | extern const struct SMKiTunesKeywordRelationships { 12 | __unsafe_unretained NSString *objects; 13 | } SMKiTunesKeywordRelationships; 14 | 15 | extern const struct SMKiTunesKeywordFetchedProperties { 16 | } SMKiTunesKeywordFetchedProperties; 17 | 18 | @class SMKiTunesObject; 19 | 20 | 21 | 22 | @interface SMKiTunesKeywordID : NSManagedObjectID {} 23 | @end 24 | 25 | @interface _SMKiTunesKeyword : NSManagedObject {} 26 | + (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_; 27 | + (NSString*)entityName; 28 | + (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_; 29 | - (SMKiTunesKeywordID*)objectID; 30 | 31 | 32 | 33 | 34 | @property (nonatomic, strong) NSString* name; 35 | 36 | 37 | //- (BOOL)validateName:(id*)value_ error:(NSError**)error_; 38 | 39 | 40 | 41 | 42 | 43 | @property (nonatomic, strong) NSSet* objects; 44 | 45 | - (NSMutableSet*)objectsSet; 46 | 47 | 48 | 49 | 50 | 51 | @end 52 | 53 | @interface _SMKiTunesKeyword (CoreDataGeneratedAccessors) 54 | 55 | - (void)addObjects:(NSSet*)value_; 56 | - (void)removeObjects:(NSSet*)value_; 57 | - (void)addObjectsObject:(SMKiTunesObject*)value_; 58 | - (void)removeObjectsObject:(SMKiTunesObject*)value_; 59 | 60 | @end 61 | 62 | @interface _SMKiTunesKeyword (CoreDataGeneratedPrimitiveAccessors) 63 | 64 | 65 | - (NSString*)primitiveName; 66 | - (void)setPrimitiveName:(NSString*)value; 67 | 68 | 69 | 70 | 71 | 72 | - (NSMutableSet*)primitiveObjects; 73 | - (void)setPrimitiveObjects:(NSMutableSet*)value; 74 | 75 | 76 | @end 77 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/_SMKiTunesKeyword.m: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This file is machine-generated and constantly overwritten. 2 | // Make changes to SMKiTunesKeyword.m instead. 3 | 4 | #import "_SMKiTunesKeyword.h" 5 | 6 | const struct SMKiTunesKeywordAttributes SMKiTunesKeywordAttributes = { 7 | .name = @"name", 8 | }; 9 | 10 | const struct SMKiTunesKeywordRelationships SMKiTunesKeywordRelationships = { 11 | .objects = @"objects", 12 | }; 13 | 14 | const struct SMKiTunesKeywordFetchedProperties SMKiTunesKeywordFetchedProperties = { 15 | }; 16 | 17 | @implementation SMKiTunesKeywordID 18 | @end 19 | 20 | @implementation _SMKiTunesKeyword 21 | 22 | + (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_ { 23 | NSParameterAssert(moc_); 24 | return [NSEntityDescription insertNewObjectForEntityForName:@"SMKiTunesKeyword" inManagedObjectContext:moc_]; 25 | } 26 | 27 | + (NSString*)entityName { 28 | return @"SMKiTunesKeyword"; 29 | } 30 | 31 | + (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_ { 32 | NSParameterAssert(moc_); 33 | return [NSEntityDescription entityForName:@"SMKiTunesKeyword" inManagedObjectContext:moc_]; 34 | } 35 | 36 | - (SMKiTunesKeywordID*)objectID { 37 | return (SMKiTunesKeywordID*)[super objectID]; 38 | } 39 | 40 | + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { 41 | NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; 42 | 43 | 44 | return keyPaths; 45 | } 46 | 47 | 48 | 49 | 50 | @dynamic name; 51 | 52 | 53 | 54 | 55 | 56 | 57 | @dynamic objects; 58 | 59 | 60 | - (NSMutableSet*)objectsSet { 61 | [self willAccessValueForKey:@"objects"]; 62 | 63 | NSMutableSet *result = (NSMutableSet*)[self mutableSetValueForKey:@"objects"]; 64 | 65 | [self didAccessValueForKey:@"objects"]; 66 | return result; 67 | } 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | @end 76 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/_SMKiTunesObject.h: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This file is machine-generated and constantly overwritten. 2 | // Make changes to SMKiTunesObject.h instead. 3 | 4 | #import 5 | 6 | 7 | extern const struct SMKiTunesObjectAttributes { 8 | __unsafe_unretained NSString *identifier; 9 | __unsafe_unretained NSString *name; 10 | __unsafe_unretained NSString *normalizedName; 11 | } SMKiTunesObjectAttributes; 12 | 13 | extern const struct SMKiTunesObjectRelationships { 14 | __unsafe_unretained NSString *keywords; 15 | } SMKiTunesObjectRelationships; 16 | 17 | extern const struct SMKiTunesObjectFetchedProperties { 18 | } SMKiTunesObjectFetchedProperties; 19 | 20 | @class SMKiTunesKeyword; 21 | 22 | 23 | 24 | 25 | 26 | @interface SMKiTunesObjectID : NSManagedObjectID {} 27 | @end 28 | 29 | @interface _SMKiTunesObject : NSManagedObject {} 30 | + (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_; 31 | + (NSString*)entityName; 32 | + (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_; 33 | - (SMKiTunesObjectID*)objectID; 34 | 35 | 36 | 37 | 38 | @property (nonatomic, strong) NSString* identifier; 39 | 40 | 41 | //- (BOOL)validateIdentifier:(id*)value_ error:(NSError**)error_; 42 | 43 | 44 | 45 | 46 | @property (nonatomic, strong) NSString* name; 47 | 48 | 49 | //- (BOOL)validateName:(id*)value_ error:(NSError**)error_; 50 | 51 | 52 | 53 | 54 | @property (nonatomic, strong) NSString* normalizedName; 55 | 56 | 57 | //- (BOOL)validateNormalizedName:(id*)value_ error:(NSError**)error_; 58 | 59 | 60 | 61 | 62 | 63 | @property (nonatomic, strong) NSSet* keywords; 64 | 65 | - (NSMutableSet*)keywordsSet; 66 | 67 | 68 | 69 | 70 | 71 | @end 72 | 73 | @interface _SMKiTunesObject (CoreDataGeneratedAccessors) 74 | 75 | - (void)addKeywords:(NSSet*)value_; 76 | - (void)removeKeywords:(NSSet*)value_; 77 | - (void)addKeywordsObject:(SMKiTunesKeyword*)value_; 78 | - (void)removeKeywordsObject:(SMKiTunesKeyword*)value_; 79 | 80 | @end 81 | 82 | @interface _SMKiTunesObject (CoreDataGeneratedPrimitiveAccessors) 83 | 84 | 85 | - (NSString*)primitiveIdentifier; 86 | - (void)setPrimitiveIdentifier:(NSString*)value; 87 | 88 | 89 | 90 | 91 | - (NSString*)primitiveName; 92 | - (void)setPrimitiveName:(NSString*)value; 93 | 94 | 95 | 96 | 97 | - (NSString*)primitiveNormalizedName; 98 | - (void)setPrimitiveNormalizedName:(NSString*)value; 99 | 100 | 101 | 102 | 103 | 104 | - (NSMutableSet*)primitiveKeywords; 105 | - (void)setPrimitiveKeywords:(NSMutableSet*)value; 106 | 107 | 108 | @end 109 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/_SMKiTunesObject.m: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This file is machine-generated and constantly overwritten. 2 | // Make changes to SMKiTunesObject.m instead. 3 | 4 | #import "_SMKiTunesObject.h" 5 | 6 | const struct SMKiTunesObjectAttributes SMKiTunesObjectAttributes = { 7 | .identifier = @"identifier", 8 | .name = @"name", 9 | .normalizedName = @"normalizedName", 10 | }; 11 | 12 | const struct SMKiTunesObjectRelationships SMKiTunesObjectRelationships = { 13 | .keywords = @"keywords", 14 | }; 15 | 16 | const struct SMKiTunesObjectFetchedProperties SMKiTunesObjectFetchedProperties = { 17 | }; 18 | 19 | @implementation SMKiTunesObjectID 20 | @end 21 | 22 | @implementation _SMKiTunesObject 23 | 24 | + (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_ { 25 | NSParameterAssert(moc_); 26 | return [NSEntityDescription insertNewObjectForEntityForName:@"SMKiTunesObject" inManagedObjectContext:moc_]; 27 | } 28 | 29 | + (NSString*)entityName { 30 | return @"SMKiTunesObject"; 31 | } 32 | 33 | + (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_ { 34 | NSParameterAssert(moc_); 35 | return [NSEntityDescription entityForName:@"SMKiTunesObject" inManagedObjectContext:moc_]; 36 | } 37 | 38 | - (SMKiTunesObjectID*)objectID { 39 | return (SMKiTunesObjectID*)[super objectID]; 40 | } 41 | 42 | + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { 43 | NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; 44 | 45 | 46 | return keyPaths; 47 | } 48 | 49 | 50 | 51 | 52 | @dynamic identifier; 53 | 54 | 55 | 56 | 57 | 58 | 59 | @dynamic name; 60 | 61 | 62 | 63 | 64 | 65 | 66 | @dynamic normalizedName; 67 | 68 | 69 | 70 | 71 | 72 | 73 | @dynamic keywords; 74 | 75 | 76 | - (NSMutableSet*)keywordsSet { 77 | [self willAccessValueForKey:@"keywords"]; 78 | 79 | NSMutableSet *result = (NSMutableSet*)[self mutableSetValueForKey:@"keywords"]; 80 | 81 | [self didAccessValueForKey:@"keywords"]; 82 | return result; 83 | } 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | @end 92 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/_SMKiTunesPlaylist.h: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This file is machine-generated and constantly overwritten. 2 | // Make changes to SMKiTunesPlaylist.h instead. 3 | 4 | #import 5 | #import "SMKiTunesObject.h" 6 | 7 | extern const struct SMKiTunesPlaylistAttributes { 8 | } SMKiTunesPlaylistAttributes; 9 | 10 | extern const struct SMKiTunesPlaylistRelationships { 11 | __unsafe_unretained NSString *tracks; 12 | } SMKiTunesPlaylistRelationships; 13 | 14 | extern const struct SMKiTunesPlaylistFetchedProperties { 15 | } SMKiTunesPlaylistFetchedProperties; 16 | 17 | @class SMKiTunesTrack; 18 | 19 | 20 | @interface SMKiTunesPlaylistID : NSManagedObjectID {} 21 | @end 22 | 23 | @interface _SMKiTunesPlaylist : SMKiTunesObject {} 24 | + (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_; 25 | + (NSString*)entityName; 26 | + (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_; 27 | - (SMKiTunesPlaylistID*)objectID; 28 | 29 | 30 | 31 | 32 | 33 | @property (nonatomic, strong) NSOrderedSet* tracks; 34 | 35 | - (NSMutableOrderedSet*)tracksSet; 36 | 37 | 38 | 39 | 40 | 41 | @end 42 | 43 | @interface _SMKiTunesPlaylist (CoreDataGeneratedAccessors) 44 | 45 | - (void)addTracks:(NSOrderedSet*)value_; 46 | - (void)removeTracks:(NSOrderedSet*)value_; 47 | - (void)addTracksObject:(SMKiTunesTrack*)value_; 48 | - (void)removeTracksObject:(SMKiTunesTrack*)value_; 49 | 50 | @end 51 | 52 | @interface _SMKiTunesPlaylist (CoreDataGeneratedPrimitiveAccessors) 53 | 54 | 55 | 56 | - (NSMutableOrderedSet*)primitiveTracks; 57 | - (void)setPrimitiveTracks:(NSMutableOrderedSet*)value; 58 | 59 | 60 | @end 61 | -------------------------------------------------------------------------------- /Content Sources/iTunes/Data Model/mogenerator/_SMKiTunesPlaylist.m: -------------------------------------------------------------------------------- 1 | // DO NOT EDIT. This file is machine-generated and constantly overwritten. 2 | // Make changes to SMKiTunesPlaylist.m instead. 3 | 4 | #import "_SMKiTunesPlaylist.h" 5 | 6 | const struct SMKiTunesPlaylistAttributes SMKiTunesPlaylistAttributes = { 7 | }; 8 | 9 | const struct SMKiTunesPlaylistRelationships SMKiTunesPlaylistRelationships = { 10 | .tracks = @"tracks", 11 | }; 12 | 13 | const struct SMKiTunesPlaylistFetchedProperties SMKiTunesPlaylistFetchedProperties = { 14 | }; 15 | 16 | @implementation SMKiTunesPlaylistID 17 | @end 18 | 19 | @implementation _SMKiTunesPlaylist 20 | 21 | + (id)insertInManagedObjectContext:(NSManagedObjectContext*)moc_ { 22 | NSParameterAssert(moc_); 23 | return [NSEntityDescription insertNewObjectForEntityForName:@"SMKiTunesPlaylist" inManagedObjectContext:moc_]; 24 | } 25 | 26 | + (NSString*)entityName { 27 | return @"SMKiTunesPlaylist"; 28 | } 29 | 30 | + (NSEntityDescription*)entityInManagedObjectContext:(NSManagedObjectContext*)moc_ { 31 | NSParameterAssert(moc_); 32 | return [NSEntityDescription entityForName:@"SMKiTunesPlaylist" inManagedObjectContext:moc_]; 33 | } 34 | 35 | - (SMKiTunesPlaylistID*)objectID { 36 | return (SMKiTunesPlaylistID*)[super objectID]; 37 | } 38 | 39 | + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { 40 | NSSet *keyPaths = [super keyPathsForValuesAffectingValueForKey:key]; 41 | 42 | 43 | return keyPaths; 44 | } 45 | 46 | 47 | 48 | 49 | @dynamic tracks; 50 | 51 | 52 | - (NSMutableOrderedSet*)tracksSet { 53 | [self willAccessValueForKey:@"tracks"]; 54 | 55 | NSMutableOrderedSet *result = (NSMutableOrderedSet*)[self mutableOrderedSetValueForKey:@"tracks"]; 56 | 57 | [self didAccessValueForKey:@"tracks"]; 58 | return result; 59 | } 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | @end 68 | -------------------------------------------------------------------------------- /Content Sources/iTunes/SMKiTunesConstants.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKiTunesConstants.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-22. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | extern NSString* const SMKiTunesEntityNameArtist; 10 | extern NSString* const SMKiTunesEntityNameAlbum; 11 | extern NSString* const SMKiTunesEntityNameTrack; 12 | extern NSString* const SMKiTunesEntityNamePlaylist; 13 | extern NSString* const SMKiTunesEntityNameTag; 14 | 15 | extern NSString* const SMKiTunesUntitledArtistName; 16 | extern NSString* const SMKiTunesUntitledTrackName; 17 | extern NSString* const SMKiTunesUntitledAlbumName; 18 | extern NSString* const SMKiTunesUntitledPlaylistName; 19 | extern NSString* const SMKiTunesCompilationsArtistName; -------------------------------------------------------------------------------- /Content Sources/iTunes/SMKiTunesConstants.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKiTunesConstants.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-22. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKiTunesConstants.h" 10 | 11 | NSString* const SMKiTunesEntityNameArtist = @"SMKiTunesArtist"; 12 | NSString* const SMKiTunesEntityNameAlbum = @"SMKiTunesAlbum"; 13 | NSString* const SMKiTunesEntityNameTrack = @"SMKiTunesTrack"; 14 | NSString* const SMKiTunesEntityNamePlaylist = @"SMKiTunesPlaylist"; 15 | NSString* const SMKiTunesEntityNameTag = @"SMKiTunesTag"; 16 | 17 | NSString* const SMKiTunesUntitledArtistName = @"Untitled Artist"; 18 | NSString* const SMKiTunesUntitledTrackName = @"Untitled Track"; 19 | NSString* const SMKiTunesUntitledAlbumName = @"Untitled Album"; 20 | NSString* const SMKiTunesUntitledPlaylistName = @"Untitled Playlist"; 21 | NSString* const SMKiTunesCompilationsArtistName = @"Various Artists"; -------------------------------------------------------------------------------- /Content Sources/iTunes/SMKiTunesContentSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKiTunesContentSource.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKManagedObjectContext.h" 11 | #import "SMKContentSource.h" 12 | #import "SMKiTunesConstants.h" 13 | #import "SMKiTunesPlaylist.h" 14 | #import "SMKiTunesTrack.h" 15 | #import "SMKiTunesArtist.h" 16 | #import "SMKiTunesAlbum.h" 17 | #import "SMKiTunesObject.h" 18 | 19 | @interface SMKiTunesContentSource : NSObject 20 | 21 | #pragma mark - Content Source Specific API 22 | 23 | /** 24 | Access to Core Data backing store 25 | */ 26 | @property (nonatomic, strong) SMKManagedObjectContext *mainQueueObjectContext; 27 | @property (nonatomic, strong) SMKManagedObjectContext *backgroundQueueObjectContext; 28 | @property (nonatomic, strong) NSPersistentStoreCoordinator *persistentStoreCoordinator; 29 | @property (nonatomic, strong) NSManagedObjectModel *managedObjectModel; 30 | 31 | /** 32 | Set to NO if you don't want to sync playlists 33 | */ 34 | @property (nonatomic, assign) BOOL syncPlaylists; 35 | 36 | /** 37 | Starts a sync with iTunes. This method is called automatically in SMKiTunesContentSource's -init. 38 | @discussion This method starts an asynchronous operation and will return immediately 39 | */ 40 | - (void)sync; 41 | 42 | /** 43 | Deletes the persistent store used to cache the iTunes Library info. 44 | @discussion You must manually call -sync again before accessing anything 45 | */ 46 | - (void)deleteStore; 47 | 48 | #pragma mark - Fetching 49 | 50 | /** 51 | Executes the specified fetch request asynchronously 52 | @param request The fetch request to execute 53 | @parame handler Completion handler to be called upon execution of the request 54 | */ 55 | - (void)executeFetchRequest:(NSFetchRequest *)request 56 | completionHandler:(void(^)(NSArray *playlists, NSError *error))handler; 57 | @end 58 | -------------------------------------------------------------------------------- /Content Sources/iTunes/SMKiTunesSyncOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKiTunesSyncOperation.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-22. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class SMKiTunesContentSource; 12 | @interface SMKiTunesSyncOperation : NSOperation 13 | @property (nonatomic, weak) SMKiTunesContentSource *contentSource; 14 | @property (nonatomic, assign) BOOL syncPlaylists; 15 | 16 | @property (nonatomic, copy) void (^completionBlock)(SMKiTunesSyncOperation *operation, NSUInteger importedCount); 17 | @property (nonatomic, copy) void (^progressBlock)(SMKiTunesSyncOperation *operation, NSUInteger trackNumber, NSUInteger totalTracks, NSError *error); 18 | @end -------------------------------------------------------------------------------- /Example Apps/8tracksExample/8tracksExample-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.indragie.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSHumanReadableCopyright 28 | Copyright © 2012 Indragie Karunaratne. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /Example Apps/8tracksExample/8tracksExample-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the '8tracksExample' target in the '8tracksExample' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /Example Apps/8tracksExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // 8tracksExample 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : NSObject 12 | 13 | @property (assign) IBOutlet NSWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example Apps/8tracksExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // 8tracksExample 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import 11 | 12 | @implementation AppDelegate { 13 | SMK8TracksContentSource *_source; 14 | } 15 | 16 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 17 | { 18 | _source = [SMK8TracksContentSource new]; 19 | [_source setAPIKey:@"API_KEY"]; 20 | [_source authenticateWithUsername:@"USERNAME" password:@"PASSWORD" success:^(SMK8TracksUser *user) { 21 | [_source getPath:@"mixes/1002" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) { 22 | NSLog(@"%@", responseObject); 23 | } failure:nil]; 24 | } failure:nil]; 25 | } 26 | 27 | @end 28 | -------------------------------------------------------------------------------- /Example Apps/8tracksExample/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} 2 | {\colortbl;\red255\green255\blue255;} 3 | \paperw9840\paperh8400 4 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural 5 | 6 | \f0\b\fs24 \cf0 Engineering: 7 | \b0 \ 8 | Some people\ 9 | \ 10 | 11 | \b Human Interface Design: 12 | \b0 \ 13 | Some other people\ 14 | \ 15 | 16 | \b Testing: 17 | \b0 \ 18 | Hopefully not nobody\ 19 | \ 20 | 21 | \b Documentation: 22 | \b0 \ 23 | Whoever\ 24 | \ 25 | 26 | \b With special thanks to: 27 | \b0 \ 28 | Mom\ 29 | } 30 | -------------------------------------------------------------------------------- /Example Apps/8tracksExample/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example Apps/8tracksExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // 8tracksExample 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **)argv); 14 | } 15 | -------------------------------------------------------------------------------- /Example Apps/MPMediaLibraryExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // MPMediaLibraryExample 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow *window; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /Example Apps/MPMediaLibraryExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // MPMediaLibraryExample 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @implementation AppDelegate 12 | 13 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 14 | { 15 | // Override point for customization after application launch. 16 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { 17 | UISplitViewController *splitViewController = (UISplitViewController *)self.window.rootViewController; 18 | UINavigationController *navigationController = [splitViewController.viewControllers lastObject]; 19 | splitViewController.delegate = (id)navigationController.topViewController; 20 | } 21 | return YES; 22 | } 23 | 24 | - (void)applicationWillResignActive:(UIApplication *)application 25 | { 26 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state. 27 | // Use this method to pause ongoing tasks, disable timers, and throttle down OpenGL ES frame rates. Games should use this method to pause the game. 28 | } 29 | 30 | - (void)applicationDidEnterBackground:(UIApplication *)application 31 | { 32 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later. 33 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 34 | } 35 | 36 | - (void)applicationWillEnterForeground:(UIApplication *)application 37 | { 38 | // Called as part of the transition from the background to the inactive state; here you can undo many of the changes made on entering the background. 39 | } 40 | 41 | - (void)applicationDidBecomeActive:(UIApplication *)application 42 | { 43 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface. 44 | } 45 | 46 | - (void)applicationWillTerminate:(UIApplication *)application 47 | { 48 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 49 | } 50 | 51 | @end 52 | -------------------------------------------------------------------------------- /Example Apps/MPMediaLibraryExample/DetailViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.h 3 | // MPMediaLibraryExample 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DetailViewController : UITableViewController 12 | 13 | @property (strong, nonatomic) id detailItem; 14 | 15 | @property (weak, nonatomic) IBOutlet UILabel *detailDescriptionLabel; 16 | - (IBAction)seekForward:(id)sender; 17 | @end 18 | -------------------------------------------------------------------------------- /Example Apps/MPMediaLibraryExample/DetailViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // DetailViewController.m 3 | // MPMediaLibraryExample 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "DetailViewController.h" 10 | #import "SNRMusicKitiOS.h" 11 | #import "SMKMPMediaContentSource.h" 12 | #import "SMKMPMusicPlayer.h" 13 | #import "SMKQueueController.h" 14 | 15 | @interface DetailViewController () 16 | @property (strong, nonatomic) UIPopoverController *masterPopoverController; 17 | @property (nonatomic, strong) NSArray *tracks; 18 | - (void)configureView; 19 | @end 20 | 21 | @implementation DetailViewController { 22 | SMKQueueController *_controller; 23 | } 24 | 25 | #pragma mark - Managing the detail item 26 | 27 | - (void)setDetailItem:(id)newDetailItem 28 | { 29 | if (_detailItem != newDetailItem) { 30 | _detailItem = newDetailItem; 31 | [self configureView]; 32 | } 33 | if (self.masterPopoverController != nil) { 34 | [self.masterPopoverController dismissPopoverAnimated:YES]; 35 | } 36 | } 37 | 38 | - (void)configureView 39 | { 40 | __weak DetailViewController *weakSelf = self; 41 | [self.detailItem fetchTracksWithCompletionHandler:^(NSArray *tracks, NSError *error) { 42 | DetailViewController *strongSelf = weakSelf; 43 | strongSelf.tracks = tracks; 44 | [self.tableView reloadData]; 45 | }]; 46 | } 47 | 48 | - (void)viewDidLoad 49 | { 50 | [super viewDidLoad]; 51 | [self configureView]; 52 | } 53 | 54 | #pragma mark - Table view 55 | 56 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 57 | { 58 | return 1; 59 | } 60 | 61 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 62 | { 63 | return self.tracks.count; 64 | } 65 | 66 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 67 | { 68 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"DetailCell" forIndexPath:indexPath]; 69 | SMKMPMediaTrack *object = self.tracks[indexPath.row]; 70 | cell.textLabel.text = [object name]; 71 | cell.detailTextLabel.text = [object artistName]; 72 | return cell; 73 | } 74 | 75 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 76 | { 77 | NSArray *array = [self.tracks subarrayWithRange:NSMakeRange(indexPath.row, [self.tracks count] - indexPath.row)]; 78 | NSLog(@"%@", array); 79 | _controller = [SMKQueueController queueControllerWithTracks:array]; 80 | [_controller play:nil]; 81 | } 82 | 83 | #pragma mark - Split view 84 | 85 | - (void)splitViewController:(UISplitViewController *)splitController willHideViewController:(UIViewController *)viewController withBarButtonItem:(UIBarButtonItem *)barButtonItem forPopoverController:(UIPopoverController *)popoverController 86 | { 87 | barButtonItem.title = NSLocalizedString(@"Master", @"Master"); 88 | [self.navigationItem setLeftBarButtonItem:barButtonItem animated:YES]; 89 | self.masterPopoverController = popoverController; 90 | } 91 | 92 | - (void)splitViewController:(UISplitViewController *)splitController willShowViewController:(UIViewController *)viewController invalidatingBarButtonItem:(UIBarButtonItem *)barButtonItem 93 | { 94 | // Called when the view is shown again in the split view, invalidating the button and popover controller. 95 | [self.navigationItem setLeftBarButtonItem:nil animated:YES]; 96 | self.masterPopoverController = nil; 97 | } 98 | 99 | - (IBAction)seekForward:(id)sender 100 | { 101 | [_controller seekForward:nil]; 102 | } 103 | 104 | @end 105 | -------------------------------------------------------------------------------- /Example Apps/MPMediaLibraryExample/MPMediaLibraryExample-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ${PRODUCT_NAME} 9 | CFBundleExecutable 10 | ${EXECUTABLE_NAME} 11 | CFBundleIdentifier 12 | com.indragie.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1.0 25 | LSRequiresIPhoneOS 26 | 27 | UIMainStoryboardFile 28 | MainStoryboard_iPhone 29 | UIMainStoryboardFile~ipad 30 | MainStoryboard_iPad 31 | UIRequiredDeviceCapabilities 32 | 33 | armv7 34 | 35 | UIStatusBarTintParameters 36 | 37 | UINavigationBar 38 | 39 | Style 40 | UIBarStyleDefault 41 | Translucent 42 | 43 | 44 | 45 | UISupportedInterfaceOrientations 46 | 47 | UIInterfaceOrientationPortrait 48 | UIInterfaceOrientationLandscapeLeft 49 | UIInterfaceOrientationLandscapeRight 50 | 51 | UIBackgroundModes 52 | 53 | audio 54 | 55 | UISupportedInterfaceOrientations~ipad 56 | 57 | UIInterfaceOrientationPortrait 58 | UIInterfaceOrientationPortraitUpsideDown 59 | UIInterfaceOrientationLandscapeLeft 60 | UIInterfaceOrientationLandscapeRight 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /Example Apps/MPMediaLibraryExample/MPMediaLibraryExample-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'MPMediaLibraryExample' target in the 'MPMediaLibraryExample' project 3 | // 4 | 5 | #import 6 | 7 | #ifndef __IPHONE_5_0 8 | #warning "This project uses features only available in iOS SDK 5.0 and later." 9 | #endif 10 | 11 | #ifdef __OBJC__ 12 | #import 13 | #import 14 | #endif 15 | -------------------------------------------------------------------------------- /Example Apps/MPMediaLibraryExample/MasterViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // MasterViewController.h 3 | // MPMediaLibraryExample 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @class DetailViewController; 12 | 13 | @interface MasterViewController : UITableViewController 14 | 15 | @property (strong, nonatomic) DetailViewController *detailViewController; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /Example Apps/MPMediaLibraryExample/MasterViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // MasterViewController.m 3 | // MPMediaLibraryExample 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "MasterViewController.h" 10 | 11 | #import "DetailViewController.h" 12 | #import "SNRMusicKitiOS.h" 13 | 14 | @interface MasterViewController () 15 | @property (nonatomic, strong) NSArray *playlists; 16 | @end 17 | 18 | @implementation MasterViewController { 19 | SMKMPMediaContentSource *_contentSource; 20 | } 21 | 22 | - (void)awakeFromNib 23 | { 24 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { 25 | self.clearsSelectionOnViewWillAppear = NO; 26 | self.contentSizeForViewInPopover = CGSizeMake(320.0, 600.0); 27 | } 28 | [super awakeFromNib]; 29 | } 30 | 31 | - (void)viewDidLoad 32 | { 33 | [super viewDidLoad]; 34 | _contentSource = [SMKMPMediaContentSource new]; 35 | __weak MasterViewController *weakSelf = self; 36 | [_contentSource fetchPlaylistsWithSortDescriptors:nil predicate:nil completionHandler:^(NSArray *playlists, NSError *error) { 37 | MasterViewController *strongSelf = weakSelf; 38 | strongSelf.playlists = playlists; 39 | [strongSelf.tableView reloadData]; 40 | }]; 41 | self.detailViewController = (DetailViewController *)[[self.splitViewController.viewControllers lastObject] topViewController]; 42 | } 43 | 44 | 45 | #pragma mark - Table View 46 | 47 | - (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView 48 | { 49 | return 1; 50 | } 51 | 52 | - (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section 53 | { 54 | return self.playlists.count; 55 | } 56 | 57 | - (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath 58 | { 59 | UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"Cell" forIndexPath:indexPath]; 60 | id object = self.playlists[indexPath.row]; 61 | cell.textLabel.text = [object name]; 62 | return cell; 63 | } 64 | 65 | - (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath 66 | { 67 | if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) { 68 | id object = self.playlists[indexPath.row]; 69 | self.detailViewController.detailItem = object; 70 | } 71 | } 72 | 73 | - (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender 74 | { 75 | if ([[segue identifier] isEqualToString:@"showDetail"]) { 76 | NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow]; 77 | id object = self.playlists[indexPath.row]; 78 | self.detailViewController.detailItem = object; 79 | } 80 | } 81 | 82 | @end 83 | -------------------------------------------------------------------------------- /Example Apps/MPMediaLibraryExample/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example Apps/MPMediaLibraryExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // MPMediaLibraryExample 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-27. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | #import "AppDelegate.h" 12 | 13 | int main(int argc, char *argv[]) 14 | { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Example Apps/SpotifyExampleMac/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // SpotifyExampleMac 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-25. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface AppDelegate : NSObject 13 | 14 | @property (assign) IBOutlet NSWindow *window; 15 | 16 | @property (nonatomic, weak) IBOutlet NSTableView *playlistsTableView; 17 | @property (nonatomic, weak) IBOutlet NSTableView *tracksTableView; 18 | 19 | @property (nonatomic, strong) NSArray *playlists; 20 | @property (nonatomic, strong) NSArray *tracks; 21 | @property (nonatomic, strong, readonly) SMKSpotifyPlayer *audioPlayer; 22 | @end 23 | -------------------------------------------------------------------------------- /Example Apps/SpotifyExampleMac/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // SpotifyExampleMac 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-25. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | @property (nonatomic, strong) SMKSpotifyPlayer *audioPlayer; 13 | @end 14 | 15 | @implementation AppDelegate { 16 | SMKSpotifyContentSource *_source; 17 | } 18 | 19 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 20 | { 21 | NSString *credentialsFolder = [[NSHomeDirectory() stringByAppendingPathComponent:@"Desktop"] stringByAppendingPathComponent:@"credentials"]; 22 | NSString *username = [NSString stringWithContentsOfFile:[credentialsFolder stringByAppendingPathComponent:@"spotify_username.txt"] encoding:NSUTF8StringEncoding error:nil]; 23 | NSString *password = [NSString stringWithContentsOfFile:[credentialsFolder stringByAppendingPathComponent:@"spotify_password.txt"] encoding:NSUTF8StringEncoding error:nil]; 24 | NSData *key = [NSData dataWithContentsOfFile:[credentialsFolder stringByAppendingPathComponent:@"spotify_appkey.key"]]; 25 | _source = [[SMKSpotifyContentSource alloc] initWithApplicationKey:key userAgent:@"com.indragie.SNRMusicKit" loadingPolicy:SPAsyncLoadingImmediate error:nil]; 26 | [_source attemptLoginWithUserName:username password:password]; 27 | [_source setDelegate:self]; 28 | [_source setUsingVolumeNormalization:YES]; 29 | [_source fetchPlaylistsWithSortDescriptors:nil predicate:nil completionHandler:^(NSArray *playlists, NSError *error) { 30 | self.playlists = playlists; 31 | }]; 32 | self.audioPlayer = [[SMKSpotifyPlayer alloc] initWithPlaybackSession:_source]; 33 | self.audioPlayer.volume = 0.5f; 34 | [self.audioPlayer setFinishedTrackBlock:^(id player, id track, NSError *error) { 35 | NSLog(@"Player %@ finished track %@ with error %@", player, track, error); 36 | }]; 37 | [self.tracksTableView setTarget:self]; 38 | [self.tracksTableView setDoubleAction:@selector(playTrack:)]; 39 | } 40 | 41 | #pragma mark - NSTableViewDelegate 42 | 43 | - (void)tableViewSelectionDidChange:(NSNotification *)notification 44 | { 45 | NSTableView *tableView = [notification object]; 46 | if (tableView == self.playlistsTableView) { 47 | NSInteger selectedRow = [tableView selectedRow]; 48 | if (selectedRow != -1) { 49 | id playlist = [self.playlists objectAtIndex:selectedRow]; 50 | [playlist fetchTracksWithCompletionHandler:^(NSArray *tracks, NSError *error) { 51 | self.tracks = tracks; 52 | }]; 53 | } 54 | } 55 | } 56 | 57 | - (void)playTrack:(id)sender 58 | { 59 | NSInteger selectedRow = [sender selectedRow]; 60 | if (selectedRow != -1) { 61 | id track = [self.tracks objectAtIndex:selectedRow]; 62 | [self.audioPlayer playTrack:track completionHandler:^(NSError *error) { 63 | if (error) 64 | NSLog(@"Error playing track %@: %@, %@", track, error, [error userInfo]); 65 | }]; 66 | } 67 | } 68 | 69 | #pragma mark - SPSessionDelegate 70 | 71 | -(void)sessionDidLoginSuccessfully:(SPSession *)aSession 72 | { 73 | NSLog(@"Successful login."); 74 | } 75 | 76 | - (void)session:(SPSession *)aSession didFailToLoginWithError:(NSError *)error 77 | { 78 | NSLog(@"Failed login: %@ %@", error, [error userInfo]); 79 | } 80 | @end 81 | -------------------------------------------------------------------------------- /Example Apps/SpotifyExampleMac/SpotifyExampleMac-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.indragie.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSHumanReadableCopyright 28 | Copyright © 2012 Indragie Karunaratne. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /Example Apps/SpotifyExampleMac/SpotifyExampleMac-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'SpotifyExampleMac' target in the 'SpotifyExampleMac' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /Example Apps/SpotifyExampleMac/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} 2 | {\colortbl;\red255\green255\blue255;} 3 | \paperw9840\paperh8400 4 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural 5 | 6 | \f0\b\fs24 \cf0 Engineering: 7 | \b0 \ 8 | Some people\ 9 | \ 10 | 11 | \b Human Interface Design: 12 | \b0 \ 13 | Some other people\ 14 | \ 15 | 16 | \b Testing: 17 | \b0 \ 18 | Hopefully not nobody\ 19 | \ 20 | 21 | \b Documentation: 22 | \b0 \ 23 | Whoever\ 24 | \ 25 | 26 | \b With special thanks to: 27 | \b0 \ 28 | Mom\ 29 | } 30 | -------------------------------------------------------------------------------- /Example Apps/SpotifyExampleMac/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example Apps/SpotifyExampleMac/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SpotifyExampleMac 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-25. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **)argv); 14 | } 15 | -------------------------------------------------------------------------------- /Example Apps/iTunesExample/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // iTunesExample 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface AppDelegate : NSObject 13 | 14 | @property (assign) IBOutlet NSWindow *window; 15 | @property (nonatomic, weak) IBOutlet NSTableView *playlistsTableView; 16 | @property (nonatomic, weak) IBOutlet NSTableView *tracksTableView; 17 | 18 | @property (nonatomic, strong) NSArray *playlists; 19 | @property (nonatomic, strong) NSArray *tracks; 20 | @property (nonatomic, strong, readonly) SMKAVQueuePlayer *audioPlayer; 21 | @end 22 | -------------------------------------------------------------------------------- /Example Apps/iTunesExample/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // iTunesExample 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | #import 11 | 12 | @interface AppDelegate () 13 | @property (nonatomic, strong) SMKAVQueuePlayer *audioPlayer; 14 | @end 15 | 16 | @implementation AppDelegate { 17 | SMKiTunesContentSource *_contentSource; 18 | } 19 | 20 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification 21 | { 22 | _contentSource = [SMKiTunesContentSource new]; 23 | self.audioPlayer = [SMKAVQueuePlayer new]; 24 | self.audioPlayer.volume = 0.5f; 25 | [self.audioPlayer setFinishedTrackBlock:^(id player, id track, NSError *error) { 26 | NSLog(@"Player %@ finished track %@ with error %@", player, track, error); 27 | }]; 28 | [self.tracksTableView setTarget:self]; 29 | [self.tracksTableView setDoubleAction:@selector(playTrack:)]; 30 | NSArray *sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]; 31 | [_contentSource fetchPlaylistsWithSortDescriptors:sortDescriptors batchSize:20 fetchLimit:0 predicate:nil completionHandler:^(NSArray *playlists, NSError *error) { 32 | self.playlists = playlists; 33 | }]; 34 | } 35 | 36 | - (void)tableViewSelectionDidChange:(NSNotification *)notification 37 | { 38 | NSTableView *tableView = [notification object]; 39 | if (tableView == self.playlistsTableView) { 40 | NSInteger selectedRow = [tableView selectedRow]; 41 | if (selectedRow != -1) { 42 | id playlist = [self.playlists objectAtIndex:selectedRow]; 43 | NSArray *sortDescriptors = @[[NSSortDescriptor sortDescriptorWithKey:@"name" ascending:YES]]; 44 | [playlist fetchTracksWithSortDescriptors:sortDescriptors predicate:nil batchSize:20 fetchlimit:0 completionHandler:^(NSArray *tracks, NSError *error) { 45 | self.tracks = tracks; 46 | }]; 47 | } 48 | } 49 | } 50 | 51 | - (void)playTrack:(id)sender 52 | { 53 | NSInteger selectedRow = [sender selectedRow]; 54 | if (selectedRow != -1) { 55 | id track = [self.tracks objectAtIndex:selectedRow]; 56 | [self.audioPlayer playTrack:track completionHandler:^(NSError *error) { 57 | if (error) 58 | NSLog(@"Error playing track %@: %@, %@", track, error, [error userInfo]); 59 | }]; 60 | } 61 | } 62 | @end 63 | -------------------------------------------------------------------------------- /Example Apps/iTunesExample/User Interface/InsetTextField.h: -------------------------------------------------------------------------------- 1 | // 2 | // InsetTextField.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface InsetTextField : NSTextField 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /Example Apps/iTunesExample/User Interface/InsetTextField.m: -------------------------------------------------------------------------------- 1 | // 2 | // InsetTextField.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "InsetTextField.h" 10 | 11 | @implementation InsetTextField 12 | 13 | - (id)initWithCoder:(NSCoder *)aDecoder 14 | { 15 | if ((self = [super initWithCoder:aDecoder])) { 16 | [[self cell] setBackgroundStyle:NSBackgroundStyleRaised]; 17 | } 18 | return self; 19 | } 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /Example Apps/iTunesExample/en.lproj/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} 2 | {\colortbl;\red255\green255\blue255;} 3 | \paperw9840\paperh8400 4 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural 5 | 6 | \f0\b\fs24 \cf0 Engineering: 7 | \b0 \ 8 | Some people\ 9 | \ 10 | 11 | \b Human Interface Design: 12 | \b0 \ 13 | Some other people\ 14 | \ 15 | 16 | \b Testing: 17 | \b0 \ 18 | Hopefully not nobody\ 19 | \ 20 | 21 | \b Documentation: 22 | \b0 \ 23 | Whoever\ 24 | \ 25 | 26 | \b With special thanks to: 27 | \b0 \ 28 | Mom\ 29 | } 30 | -------------------------------------------------------------------------------- /Example Apps/iTunesExample/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /Example Apps/iTunesExample/iTunesExample-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.indragie.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | ${MACOSX_DEPLOYMENT_TARGET} 27 | NSHumanReadableCopyright 28 | Copyright © 2012 Indragie Karunaratne. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /Example Apps/iTunesExample/iTunesExample-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'iTunesExample' target in the 'iTunesExample' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #endif 8 | -------------------------------------------------------------------------------- /Example Apps/iTunesExample/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // iTunesExample 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-23. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, char *argv[]) 12 | { 13 | return NSApplicationMain(argc, (const char **)argv); 14 | } 15 | -------------------------------------------------------------------------------- /Master API/SMKQueueController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKQueueController.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-29. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKTrack.h" 11 | #import "SMKPlayer.h" 12 | 13 | enum { 14 | SMKQueueControllerRepeatModeNone = 0, 15 | SMKQueueControllerRepeatModeAll = 1, 16 | SMKQueueControllerRepeatModeOne = 2 17 | }; 18 | typedef NSUInteger SMKQueueControllerRepeatMode; 19 | 20 | @interface SMKQueueController : NSObject 21 | 22 | #pragma mark - Queueing 23 | 24 | @property (nonatomic, retain, readonly) NSArray *tracks; 25 | - (id)initWithTracks:(NSArray *)tracks; 26 | + (instancetype)queueControllerWithTracks:(NSArray *)tracks; 27 | 28 | - (void)insertTrack:(id)newTrack afterTrack:(id)track; 29 | - (void)removeAllTracks; 30 | - (void)removeTrack:(id)track; 31 | 32 | #pragma mark - Player 33 | 34 | @property (nonatomic, strong, readonly) id currentPlayer; 35 | @property (nonatomic, strong, readonly) id currentTrack; 36 | @property (nonatomic, assign, readonly) NSUInteger indexOfCurrentTrack; 37 | @property (nonatomic, assign, readonly) BOOL playing; 38 | @property (nonatomic, assign) BOOL shuffle; 39 | @property (nonatomic, assign) SMKQueueControllerRepeatMode repeatMode; 40 | #if !TARGET_OS_IPHONE 41 | @property (nonatomic, assign) float volume; 42 | #endif 43 | 44 | - (IBAction)play:(id)sender; 45 | - (IBAction)pause:(id)sender; 46 | - (IBAction)playPause:(id)sender; 47 | - (IBAction)next:(id)sender; 48 | - (IBAction)previous:(id)sender; 49 | - (IBAction)seekForward:(id)sender; 50 | - (IBAction)seekBackward:(id)sender; 51 | 52 | @property (nonatomic, assign, readonly) NSTimeInterval playbackTime; 53 | - (void)seekToPlaybackTime:(NSTimeInterval)playbackTime; 54 | @end 55 | -------------------------------------------------------------------------------- /Other Services/SMKLastFMClient+SMKTrack.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKLastFMClient+SMKTrack.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-26. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKLastFMClient.h" 10 | #import "SMKTrack.h" 11 | 12 | @interface SMKLastFMClient (SMKTrack) 13 | /** 14 | Scrobbles the specified track with Last.fm. 15 | @param track The track to scrobble 16 | @param handler Completion handler to be called when operation is complete 17 | */ 18 | - (void)scrobbleTrack:(id)track completionHandler:(void (^)(NSDictionary *scrobbles, NSError *error))handler; 19 | 20 | /** 21 | Update's the users now playing status on Last.fm with the specified track 22 | @param track The track to set as now playing 23 | @param handler Completion handler to be called when operation is complete 24 | */ 25 | - (void)updateNowPlayingWithTrack:(id)track completionHandler:(void (^)(NSDictionary *response, NSError *error))handler; 26 | 27 | /** 28 | Loves the specified track on Last.fm 29 | @param track The track to love 30 | @param handler Completion handler to be called when operation is complete 31 | */ 32 | - (void)loveTrack:(id)track completionHandler:(void (^)(NSDictionary *response, NSError *error))handler; 33 | @end 34 | -------------------------------------------------------------------------------- /Other Services/SMKLastFMClient+SMKTrack.m: -------------------------------------------------------------------------------- 1 | // 2 | // SMKLastFMClient+SMKTrack.m 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-26. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SMKLastFMClient+SMKTrack.h" 10 | 11 | @implementation SMKLastFMClient (SMKTrack) 12 | - (void)scrobbleTrack:(id)track completionHandler:(void (^)(NSDictionary *scrobbles, NSError *error))handler 13 | { 14 | NSUInteger trackNumber = [track respondsToSelector:@selector(trackNumber)] ? [track trackNumber] : 0; 15 | NSUInteger timeStamp = [[NSDate date] timeIntervalSince1970]; 16 | [self scrobbleTrackWithName:[track name] 17 | album:[[track album] name] 18 | artist:[track artistName] 19 | albumArtist:[track albumArtistName] 20 | trackNumber:trackNumber 21 | duration:[track duration] 22 | timestamp:timeStamp 23 | completionHandler:handler]; 24 | } 25 | 26 | - (void)updateNowPlayingWithTrack:(id)track completionHandler:(void (^)(NSDictionary *response, NSError *error))handler 27 | { 28 | NSUInteger trackNumber = [track respondsToSelector:@selector(trackNumber)] ? [track trackNumber] : 0; 29 | [self updateNowPlayingTrackWithName:[track name] 30 | album:[[track album] name] 31 | artist:[track artistName] 32 | albumArtist:[track albumArtistName] 33 | trackNumber:trackNumber 34 | duration:[track duration] 35 | completionHandler:handler]; 36 | } 37 | 38 | - (void)loveTrack:(id)track completionHandler:(void (^)(NSDictionary *response, NSError *error))handler 39 | { 40 | [self loveTrackWithName:[track name] artist:[track artistName] completionHandler:handler]; 41 | } 42 | @end 43 | -------------------------------------------------------------------------------- /Protocols/SMKAlbum.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKAlbum.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKContentSource.h" 11 | #import "SMKArtist.h" 12 | 13 | #import "SMKContentObject.h" 14 | 15 | @protocol SMKAlbum 16 | @required 17 | /** 18 | @return The artist of the album. 19 | */ 20 | - (id)artist; 21 | 22 | /** 23 | This method will fetch the tracks asynchronously and call the completion handler when finished. 24 | @param sortDescriptors Array of NSSortDescriptor objects used to sort the content 25 | @param predicate A predicate to filter the results with. Use SMKContentSource +predicateClass to find out which 26 | class the content source expects its predicate to use. 27 | @discussion This method is asynchronous and will return immediately. 28 | */ 29 | - (void)fetchTracksWithSortDescriptors:(NSArray *)sortDescriptors 30 | predicate:(id)predicate 31 | completionHandler:(void(^)(NSArray *tracks, NSError *error))handler; 32 | 33 | @optional 34 | /** 35 | @return The release year of the album 36 | */ 37 | - (NSUInteger)releaseYear; 38 | 39 | /** 40 | @return The rating of the album 41 | */ 42 | - (NSUInteger)rating; 43 | 44 | /** 45 | @return The duration of the album in seconds. 46 | */ 47 | - (NSTimeInterval)duration; 48 | 49 | /** 50 | @return Whether the album is a compilation. 51 | */ 52 | - (BOOL)isCompilation; 53 | @end 54 | -------------------------------------------------------------------------------- /Protocols/SMKArtist.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKArtist.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKContentObject.h" 11 | 12 | @protocol SMKArtist 13 | @required 14 | 15 | /** 16 | This method will fetch the albums asynchronously and call the completion handler when finished. 17 | @param sortDescriptors Array of NSSortDescriptor objects used to sort the content 18 | @param predicate A predicate to filter the results with 19 | @discussion This method is asynchronous and will return immediately. 20 | */ 21 | - (void)fetchAlbumsWithSortDescriptors:(NSArray *)sortDescriptors 22 | predicate:(id)predicate 23 | completionHandler:(void(^)(NSArray *albums, NSError *error))handler; 24 | @end 25 | -------------------------------------------------------------------------------- /Protocols/SMKArtworkObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKArtworkObject.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKPlatformImports.h" 11 | 12 | enum { 13 | SMKArtworkSizeSmallest = 0, 14 | SMKArtworkSizeSmall = 1, 15 | SMKArtworkSizeLarge = 2, 16 | SMKArtworkSizeLargest = 3 17 | }; 18 | typedef NSUInteger SMKArtworkSize; 19 | 20 | @protocol SMKArtworkObject 21 | @required 22 | /** 23 | Fetches artwork asynchronously. 24 | @param size The artwork size. If artwork of the given size is not available, it will return the closest possible match. 25 | @param handler Completion block to be called when the image is fetched 26 | @return An artwork image 27 | @discussion This method is asynchronous and will return immediately 28 | */ 29 | - (void)fetchArtworkWithSize:(SMKArtworkSize)size completionHandler:(void(^)(SMKPlatformNativeImage *image, NSError *error))handler; 30 | 31 | @optional 32 | /** 33 | Fetches artwork asynchronously. 34 | @param targetSize The size of the image. If an image of this size is not available, the closest possible match will be returned. 35 | @param handler Completion block to be called when the image is fetched 36 | @return An artwork image 37 | @discussion This method is asynchronous and will return immediately 38 | */ 39 | - (void)fetchArtworkWithTargetSize:(CGSize)size completionHandler:(void(^)(SMKPlatformNativeImage *image, NSError *error))handler; 40 | @end 41 | -------------------------------------------------------------------------------- /Protocols/SMKContentObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKContentObject.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol SMKContentSource; 12 | @protocol SMKContentObject 13 | @required 14 | /** 15 | @return The name of the object. 16 | */ 17 | - (NSString *)name; 18 | 19 | /** 20 | @return A unique identifier string for the object; 21 | */ 22 | - (NSString *)uniqueIdentifier; 23 | 24 | /** 25 | @return The SMKContentSource object that this object belongs to 26 | */ 27 | - (id)contentSource; 28 | 29 | /** 30 | @return A set of keys that are supported for sorting 31 | */ 32 | + (NSSet *)supportedSortKeys; 33 | @end 34 | -------------------------------------------------------------------------------- /Protocols/SMKContentSource.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKContentSource.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol SMKContentSource 12 | @required 13 | /** 14 | This method will fetch the playlists asynchronously and call the completion handler when finished. 15 | @param sortDescriptors Array of NSSortDescriptor objects used to sort the content 16 | @param predicate A predicate to filter the results with. Use SMKContentSource +predicateClass to find out which 17 | class the content source expects its predicate to use. 18 | @discussion This method is asynchronous and will return immediately. 19 | */ 20 | - (void)fetchPlaylistsWithSortDescriptors:(NSArray *)sortDescriptors 21 | predicate:(id)predicate 22 | completionHandler:(void(^)(NSArray *playlists, NSError *error))handler; 23 | 24 | /** 25 | @return The class of the predicate used to sort objects from this content source 26 | */ 27 | + (Class)predicateClass; 28 | @end 29 | -------------------------------------------------------------------------------- /Protocols/SMKHierarchicalLoading.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKHierarchicalLoading.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-25. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol SMKHierarchicalLoading 12 | @required 13 | /** 14 | This method should enter the passed group, asynchronously load itself, then call this 15 | method on all of its children to trigger a "chain reaction" to load all the items. 16 | Once it has called the method on its children, it can safely leave the group. 17 | Objects at the very end of the hierarchy should add themselves to the passed array. 18 | @param group The group to enter and leave 19 | @param array Array for objects at the end of the hierarchy to add themselves to 20 | */ 21 | - (void)loadHierarchy:(dispatch_group_t)group array:(NSMutableArray *)array; 22 | @end 23 | -------------------------------------------------------------------------------- /Protocols/SMKPlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKPlayer.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKTrack.h" 11 | 12 | @protocol SMKPlayerDelegate; 13 | @protocol SMKPlayer 14 | @required 15 | /** 16 | @return An set of class names containing the names of content sources that this player can play. 17 | */ 18 | + (NSSet *)supportedContentSourceClasses; 19 | 20 | /** 21 | @return Whether the player supports preloading tracks. 22 | */ 23 | + (BOOL)supportsPreloading; 24 | 25 | /** 26 | @return Whether the player supports seeking. 27 | */ 28 | - (BOOL)supportsSeeking; 29 | 30 | /** 31 | @return The currently playing track. 32 | */ 33 | - (id)currentTrack; 34 | 35 | /** 36 | Play the specified track. 37 | @param track The track to play. 38 | @param handler Completion handler called with an NSError if playing failed and nil if successful. 39 | */ 40 | - (void)playTrack:(id)track completionHandler:(void(^)(NSError *error))handler; 41 | 42 | /** 43 | Pause playback. 44 | */ 45 | - (void)pause; 46 | 47 | /** 48 | Resume playback. 49 | */ 50 | - (void)play; 51 | 52 | /** 53 | @return Whether the player is playing. 54 | @discussion This is KVO observable. 55 | */ 56 | - (BOOL)playing; 57 | 58 | /** 59 | @return The current playback time. 60 | @discussion This is KVO observable. 61 | */ 62 | - (NSTimeInterval)playbackTime; 63 | 64 | /** 65 | The seek time interval for the -seekBackward and -seekForward methods. 66 | */ 67 | @property (nonatomic, assign) NSTimeInterval seekTimeInterval; 68 | 69 | /** 70 | Seek to the specified time if the player supports seeking. 71 | */ 72 | - (void)seekToPlaybackTime:(NSTimeInterval)time; 73 | 74 | /** 75 | Seek backward (-seekTimeInterval) seconds 76 | */ 77 | - (void)seekBackward; 78 | 79 | /** 80 | Seek forward (-seekTimeInterval) seconds 81 | */ 82 | - (void)seekForward; 83 | 84 | @optional 85 | 86 | /** 87 | @return The player volume from 0.0 to 1.0 88 | */ 89 | @property (nonatomic, assign) float volume; 90 | 91 | /** 92 | Preload the specified track (if the player supports preloading) 93 | @param handler Completion handler called with an NSError if preloading failed and nil if successful. 94 | */ 95 | - (void)preloadTrack:(id)track completionHandler:(void(^)(NSError *error))handler; 96 | 97 | /** 98 | @return The currently preloaded track if there is one 99 | */ 100 | - (id)preloadedTrack; 101 | 102 | /** 103 | Skips to the track that the player has preloaded. 104 | */ 105 | - (void)skipToPreloadedTrack; 106 | 107 | /** 108 | A block called when the current track finishes playing 109 | */ 110 | @property (nonatomic, copy) void (^finishedTrackBlock)(id player, id track, NSError *error); 111 | 112 | /** 113 | Returns the underlying player object that does all the real work. 114 | Useful if you want to do customization beyond SMKPlayer's API 115 | */ 116 | - (id)audioPlayer; 117 | @end -------------------------------------------------------------------------------- /Protocols/SMKPlaylist.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKPlaylist.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKTrack.h" 11 | #import "SMKContentSource.h" 12 | #import "SMKUser.h" 13 | 14 | #import "SMKContentObject.h" 15 | 16 | @protocol SMKPlaylist 17 | @required 18 | 19 | /** 20 | This method will fetch the tracks asynchronously and call the completion handler when finished. 21 | @param handler Handler to call when tracks have been fetched 22 | */ 23 | - (void)fetchTracksWithCompletionHandler:(void(^)(NSArray *tracks, NSError *error))handler; 24 | 25 | /** 26 | @return Whether the playlist is editable 27 | */ 28 | - (BOOL)isEditable; 29 | 30 | @optional 31 | 32 | /** 33 | @return The user this playlist belongs to 34 | */ 35 | - (id)user; 36 | 37 | /** 38 | @return An optional extended description for the playlist. 39 | */ 40 | - (NSString *)extendedDescription; 41 | 42 | /* These are methods for editing that will only be called if -isEditable returns YES. 43 | All indexes are relative to the state of the playlist before any changes */ 44 | 45 | /** 46 | @param indexes The original indexes of the items to move. 47 | @param index The index to move the items to. 48 | @param handler A handler block to be called when the operation completes 49 | */ 50 | - (void)moveTracksAtIndexes:(NSIndexSet*)indexes toIndex:(NSUInteger)index completionHandler:(void(^)(NSError *error))handler; 51 | 52 | /** 53 | @param tracks The tracks to move 54 | @param index The index to move the tracks to. 55 | @param handler A handler block to be called when the operation completes 56 | */ 57 | - (void)moveTracks:(NSArray*)tracks toIndex:(NSUInteger)index completionHandler:(void(^)(NSError *error))handler; 58 | 59 | /** 60 | @param track The tracks to add to the playlist. 61 | @param index The index to add the tracks to. 62 | @param handler A handler block to be called when the operation completes 63 | */ 64 | - (void)addTracks:(NSArray*)tracks atIndex:(NSUInteger)index completionHandler:(void(^)(NSError *error))handler; 65 | 66 | /** 67 | @param indexes The indexes of the tracks to remove 68 | @param handler A handler block to be called when the operation completes 69 | */ 70 | - (void)removeTracksAtIndexes:(NSIndexSet *)indexes completionHandler:(void(^)(NSError *error))handler; 71 | 72 | @end 73 | -------------------------------------------------------------------------------- /Protocols/SMKTrack.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKTrack.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKAlbum.h" 11 | #import "SMKArtist.h" 12 | #import "SMKContentSource.h" 13 | 14 | #import "SMKContentObject.h" 15 | 16 | @protocol SMKTrack 17 | @required 18 | /** 19 | @return The parent artist of the track. 20 | */ 21 | - (id)artist; 22 | 23 | /** 24 | @return The parent album of the track. 25 | */ 26 | - (id)album; 27 | 28 | /** 29 | @return The name of the artist 30 | */ 31 | - (NSString *)artistName; 32 | 33 | /** 34 | @return The name of the album artist 35 | */ 36 | - (NSString *)albumArtistName; 37 | 38 | /** 39 | @return The duration of the track in seconds. 40 | */ 41 | - (NSTimeInterval)duration; 42 | 43 | /** 44 | @return The player class used to play this track 45 | */ 46 | - (Class)playerClass; 47 | 48 | @optional 49 | 50 | /** 51 | @return The URL to play the track from 52 | @discussion This URL could be local or remote (for streaming services). If the track cannot be played via a URL, this method should return nil. 53 | */ 54 | - (NSURL *)playbackURL; 55 | 56 | /** 57 | @return The composer of the track. 58 | */ 59 | - (NSString *)composer; 60 | 61 | /** 62 | @return The track's track number. 63 | */ 64 | - (NSUInteger)trackNumber; 65 | 66 | /** 67 | @return The track's disc number. 68 | */ 69 | - (NSUInteger)discNumber; 70 | 71 | /** 72 | @return The number of times the track has been played. 73 | */ 74 | - (NSUInteger)playCount; 75 | 76 | /** 77 | @return The lyrics for the song. 78 | */ 79 | - (NSString *)lyrics; 80 | 81 | /** 82 | @return The genre of the song. 83 | */ 84 | - (NSString *)genre; 85 | 86 | /** 87 | @return The rating of the song 88 | */ 89 | - (NSUInteger)rating; 90 | 91 | /** 92 | @return The last play date of the song. 93 | */ 94 | - (NSDate *)lastPlayedDate; 95 | 96 | /** 97 | @return The popularity index for the track. 98 | @discussion This value can have different scales depending on the content source, 99 | but in general, a higher popularity index means the track is more popular. If this 100 | information is not available, this method will return 0. 101 | */ 102 | - (NSUInteger)popularity; 103 | 104 | /** 105 | @return Whether the track contains explicit content. 106 | */ 107 | - (BOOL)isExplicit; 108 | 109 | /** 110 | @return Whether the track is free of explicit content. 111 | */ 112 | - (BOOL)isClean; 113 | @end 114 | -------------------------------------------------------------------------------- /Protocols/SMKUser.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKUser.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKContentSource.h" 11 | #import "SMKContentObject.h" 12 | 13 | @protocol SMKUser 14 | @optional 15 | /** 16 | @return The username of the user 17 | */ 18 | - (NSString *)username; 19 | @end 20 | -------------------------------------------------------------------------------- /Protocols/SMKWebObject.h: -------------------------------------------------------------------------------- 1 | // 2 | // SMKWebObject.h 3 | // SNRMusicKit 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @protocol SMKWebObject 12 | @required 13 | /** 14 | @return The URL to the web page for this content object. 15 | */ 16 | - (NSURL *)webURL; 17 | @end 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## SNRMusicKit 2 | 3 | SNRMusicKit is an open source project that will become the backbone for [Sonora](http://getsonora.com) and other music applications that we may build for the Mac and iOS platforms. This framework will consist of the following components that will create an all-in-one solution for building content rich music applications: 4 | 5 | * **Content Sources**: Services and applications that will provide content. 6 | * **Players**: Different audio players sharing a common interface to handle a wide variety of content formats. 7 | * **Other Services**: Objective-C interfaces to other commonly used services like Last.fm. 8 | 9 | All local databases will be **Core Data** SQL databases, in addition to other persistent caching of artwork, etc. All networking code will be built on top of [AFNetworking](https://github.com/AFNetworking/AFNetworking), specifically subclasses of **AFHTTPClient**. 10 | 11 | The code and API will be as modern as possible, making use of blocks, GCD, and new Objective-C syntax. The current goal is to target **OS X 10.7+ and iOS 5.0+** but that may change. 12 | 13 | ### Contributing 14 | 15 | Once the project is under way, I'd love contributions via pull requests. If you intend on making large additions or changes to the code, you may want to consult with me before beginning to ensure that we both agree that it is suitable for this library. 16 | 17 | ### Compiling 18 | 19 | * Clone the git repository, and download the submodules using the following commands: 20 | 21 | ``` 22 | cd 23 | git submodule init 24 | git submodule update 25 | ``` 26 | 27 | #### Additional Instructions for Mac Framework only 28 | * **SFBAudioEngine** requires some additional frameworks and libraries to compile that are not included in the repository. Download [this](https://github.com/downloads/sbooth/SFBAudioEngine/Frameworks.tar.bz2) archive, extract it, and put the Frameworks folder inside the SFBAudioEngine folder. 29 | * **[mogenerator](https://github.com/rentzsch/mogenerator)** needs to be installed to successfully compile. 30 | 31 | ### Progress 32 | 33 | #### Content Sources 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 |
NameiOSMacImplemented
iTunes
MPMediaLibrary
Spotify
49 | 50 | #### Players 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 |
NameiOSMacImplemented
AVQueuePlayer
MPMusicPlayerController
SFBAudioEngine
Spotify SDK
69 | 70 | ### Roadmap 71 | 72 | #### Short term 73 | 74 | * ~~iOS MPMediaLibrary content source implementation~~ 75 | * Queue controller for playing a queue of songs 76 | * ~~Cross platform artwork cache~~ 77 | * Search protocols & implementation for MPMediaLibrary and Spotify 78 | * ~~Last.fm engine for scrobbling (basically a polished version of SNRLastFMEngine built on AFNetworking)~~ 79 | 80 | #### Long term 81 | 82 | * 8tracks content source implementation 83 | 84 | ### License 85 | 86 | This library is licensed under the [BSD License](http://opensource.org/licenses/bsd-license.php). All third party libraries used in the project must be licensed under BSD or a similarly unrestrictive license (ie. MIT is acceptable, GPL or any of its variants are not). By making contributions to this project, you are **licensing your own additions under the BSD license as well**. Any contributions licensed under anything else will not be accepted into the master repository (but feel free to keep them in your own fork). -------------------------------------------------------------------------------- /SNRMusicKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SNRMusicKit.xcodeproj/project.xcworkspace/xcuserdata/indragie.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/indragiek/SNRMusicKit/2eb02ff6281bc623f7147c14b69344135cbadeff/SNRMusicKit.xcodeproj/project.xcworkspace/xcuserdata/indragie.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /SNRMusicKit.xcodeproj/xcuserdata/indragie.xcuserdatad/xcschemes/SNRMusicKit.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 51 | 52 | 58 | 59 | 60 | 61 | 62 | 63 | 69 | 70 | 76 | 77 | 78 | 79 | 81 | 82 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /SNRMusicKit.xcodeproj/xcuserdata/indragie.xcuserdatad/xcschemes/SNRMusicKitMac.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /SNRMusicKit.xcodeproj/xcuserdata/indragie.xcuserdatad/xcschemes/SNRMusicKitiOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 42 | 43 | 44 | 45 | 51 | 52 | 54 | 55 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /SNRMusicKit.xcodeproj/xcuserdata/indragie.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | 8tracksExample.xcscheme 8 | 9 | orderHint 10 | 12 11 | 12 | MPMediaLibraryExample.xcscheme 13 | 14 | orderHint 15 | 14 16 | 17 | SNRMusicKit.xcscheme 18 | 19 | orderHint 20 | 0 21 | 22 | SNRMusicKitMac.xcscheme 23 | 24 | orderHint 25 | 1 26 | 27 | SNRMusicKitiOS.xcscheme 28 | 29 | orderHint 30 | 2 31 | 32 | SpotifyExampleMac.xcscheme 33 | 34 | orderHint 35 | 13 36 | 37 | iTunesExample.xcscheme 38 | 39 | orderHint 40 | 11 41 | 42 | 43 | SuppressBuildableAutocreation 44 | 45 | 03157B2E15EBE3AC0048DB65 46 | 47 | primary 48 | 49 | 50 | 03576BB915E9695E00316930 51 | 52 | primary 53 | 54 | 55 | 036991ED15E3EAE1003BCCBE 56 | 57 | primary 58 | 59 | 60 | 0369921615E3EB0B003BCCBE 61 | 62 | primary 63 | 64 | 65 | 0369922915E3EB27003BCCBE 66 | 67 | primary 68 | 69 | 70 | 0369923815E3EB44003BCCBE 71 | 72 | primary 73 | 74 | 75 | 03927DFF15E69ED700E6483E 76 | 77 | primary 78 | 79 | 80 | 03FBED3A15E70FA400A67DEE 81 | 82 | primary 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /SNRMusicKitMac/SNRMusicKitMac-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | English 7 | CFBundleExecutable 8 | ${EXECUTABLE_NAME} 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | com.indragie.${PRODUCT_NAME:rfc1034identifier} 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | ${PRODUCT_NAME} 17 | CFBundlePackageType 18 | FMWK 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSHumanReadableCopyright 26 | Copyright © 2012 Indragie Karunaratne. All rights reserved. 27 | NSPrincipalClass 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /SNRMusicKitMac/SNRMusicKitMac-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'SNRMusicKitMac' target in the 'SNRMusicKitMac' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import "SMKErrorCodes.h" 8 | #import "SMKConvenience.h" 9 | #import "SMKPlatformImports.h" 10 | #import "SMKGlobalConstants.h" 11 | #endif 12 | -------------------------------------------------------------------------------- /SNRMusicKitMac/SNRMusicKitMac.h: -------------------------------------------------------------------------------- 1 | // 2 | // SNRMusicKitMac.h 3 | // SNRMusicKitMac 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SNRMusicKitMac : NSObject 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SNRMusicKitMac/SNRMusicKitMac.m: -------------------------------------------------------------------------------- 1 | // 2 | // SNRMusicKitMac.m 3 | // SNRMusicKitMac 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import "SNRMusicKitMac.h" 10 | 11 | @implementation SNRMusicKitMac 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SNRMusicKitMac/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* Localized versions of Info.plist keys */ 2 | 3 | -------------------------------------------------------------------------------- /SNRMusicKitiOS/SNRMusicKitiOS-Prefix.pch: -------------------------------------------------------------------------------- 1 | // 2 | // Prefix header for all source files of the 'SNRMusicKitiOS' target in the 'SNRMusicKitiOS' project 3 | // 4 | 5 | #ifdef __OBJC__ 6 | #import 7 | #import 8 | #import "SMKErrorCodes.h" 9 | #import "SMKConvenience.h" 10 | #import "SMKPlatformImports.h" 11 | #import "SMKGlobalConstants.h" 12 | #endif 13 | -------------------------------------------------------------------------------- /SNRMusicKitiOS/SNRMusicKitiOS.h: -------------------------------------------------------------------------------- 1 | // 2 | // SNRMusicKitiOS.h 3 | // SNRMusicKitiOS 4 | // 5 | // Created by Indragie Karunaratne on 2012-08-21. 6 | // Copyright (c) 2012 Indragie Karunaratne. All rights reserved. 7 | // 8 | 9 | #import 10 | #import "SMKMPMediaContentSource.h" 11 | #import "SMKAVQueuePlayer.h" --------------------------------------------------------------------------------