├── .gitignore ├── LICENSE ├── README.md ├── Screens ├── screen1.png ├── screen2.png ├── screen3.png └── screen3_thumb.png ├── SpotifyImporter.dmg └── SpotifyImporter ├── Podfile ├── Podfile.lock ├── Pods ├── AFNetworking │ ├── AFNetworking │ │ ├── AFHTTPRequestOperation.h │ │ ├── AFHTTPRequestOperation.m │ │ ├── AFHTTPRequestOperationManager.h │ │ ├── AFHTTPRequestOperationManager.m │ │ ├── AFHTTPSessionManager.h │ │ ├── AFHTTPSessionManager.m │ │ ├── AFNetworkReachabilityManager.h │ │ ├── AFNetworkReachabilityManager.m │ │ ├── AFNetworking.h │ │ ├── AFSecurityPolicy.h │ │ ├── AFSecurityPolicy.m │ │ ├── AFURLConnectionOperation.h │ │ ├── AFURLConnectionOperation.m │ │ ├── AFURLRequestSerialization.h │ │ ├── AFURLRequestSerialization.m │ │ ├── AFURLResponseSerialization.h │ │ ├── AFURLResponseSerialization.m │ │ ├── AFURLSessionManager.h │ │ └── AFURLSessionManager.m │ ├── LICENSE │ └── README.md ├── CHCSVParser │ ├── CHCSVParser │ │ └── CHCSVParser │ │ │ ├── CHCSVParser.h │ │ │ └── CHCSVParser.m │ ├── LICENSE.txt │ └── README.markdown ├── DJProgressHUD_OSX │ ├── DJProgressHUD │ │ ├── DJActivityIndicator.h │ │ ├── DJActivityIndicator.m │ │ ├── DJBezierPath.h │ │ ├── DJBezierPath.m │ │ ├── DJProgressHUD.h │ │ ├── DJProgressHUD.m │ │ ├── DJProgressIndicator.h │ │ └── DJProgressIndicator.m │ ├── LICENSE.txt │ └── README.md ├── Headers │ ├── Private │ │ ├── AFNetworking │ │ │ ├── AFHTTPRequestOperation.h │ │ │ ├── AFHTTPRequestOperationManager.h │ │ │ ├── AFHTTPSessionManager.h │ │ │ ├── AFNetworkReachabilityManager.h │ │ │ ├── AFNetworking.h │ │ │ ├── AFSecurityPolicy.h │ │ │ ├── AFURLConnectionOperation.h │ │ │ ├── AFURLRequestSerialization.h │ │ │ ├── AFURLResponseSerialization.h │ │ │ └── AFURLSessionManager.h │ │ ├── CHCSVParser │ │ │ └── CHCSVParser.h │ │ └── DJProgressHUD_OSX │ │ │ ├── DJActivityIndicator.h │ │ │ ├── DJBezierPath.h │ │ │ ├── DJProgressHUD.h │ │ │ └── DJProgressIndicator.h │ └── Public │ │ ├── AFNetworking │ │ ├── AFHTTPRequestOperation.h │ │ ├── AFHTTPRequestOperationManager.h │ │ ├── AFHTTPSessionManager.h │ │ ├── AFNetworkReachabilityManager.h │ │ ├── AFNetworking.h │ │ ├── AFSecurityPolicy.h │ │ ├── AFURLConnectionOperation.h │ │ ├── AFURLRequestSerialization.h │ │ ├── AFURLResponseSerialization.h │ │ └── AFURLSessionManager.h │ │ ├── CHCSVParser │ │ └── CHCSVParser.h │ │ └── DJProgressHUD_OSX │ │ ├── DJActivityIndicator.h │ │ ├── DJBezierPath.h │ │ ├── DJProgressHUD.h │ │ └── DJProgressIndicator.h ├── Manifest.lock ├── Pods.xcodeproj │ └── project.pbxproj └── Target Support Files │ ├── Pods-AFNetworking │ ├── Pods-AFNetworking-Private.xcconfig │ ├── Pods-AFNetworking-dummy.m │ ├── Pods-AFNetworking-prefix.pch │ └── Pods-AFNetworking.xcconfig │ ├── Pods-CHCSVParser │ ├── Pods-CHCSVParser-Private.xcconfig │ ├── Pods-CHCSVParser-dummy.m │ ├── Pods-CHCSVParser-prefix.pch │ └── Pods-CHCSVParser.xcconfig │ ├── Pods-DJProgressHUD_OSX │ ├── Pods-DJProgressHUD_OSX-Private.xcconfig │ ├── Pods-DJProgressHUD_OSX-dummy.m │ ├── Pods-DJProgressHUD_OSX-prefix.pch │ └── Pods-DJProgressHUD_OSX.xcconfig │ └── Pods │ ├── Pods-acknowledgements.markdown │ ├── Pods-acknowledgements.plist │ ├── Pods-dummy.m │ ├── Pods-environment.h │ ├── Pods-resources.sh │ ├── Pods.debug.xcconfig │ └── Pods.release.xcconfig ├── SpotifyImporter.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── SpotifyImporter.xcworkspace └── contents.xcworkspacedata ├── SpotifyImporter ├── Categories │ ├── NSArray+Filter.h │ ├── NSArray+Filter.m │ ├── NSData+HexString.h │ ├── NSData+HexString.m │ ├── NSString+Score.h │ └── NSString+Score.m ├── Models │ ├── SICSVImporter.h │ ├── SICSVImporter.m │ ├── SIDelayOperation.h │ ├── SIDelayOperation.m │ ├── SISong.h │ └── SISong.m ├── Supporting Files │ ├── Base.lproj │ │ └── Main.storyboard │ ├── Images.xcassets │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ └── instructions.imageset │ │ │ ├── Contents.json │ │ │ ├── bfBMD-1.png │ │ │ ├── bfBMD-2.png │ │ │ └── bfBMD.png │ ├── Info.plist │ ├── SIAppDelegate.h │ ├── SIAppDelegate.m │ └── main.m └── View Controllers │ ├── SIHelpGuidViewController.h │ ├── SIHelpGuidViewController.m │ ├── SIImporterViewController.h │ ├── SIImporterViewController.m │ ├── SIMainMenuViewController.h │ ├── SIMainMenuViewController.m │ ├── SISpotifyURIImporterViewController.h │ └── SISpotifyURIImporterViewController.m └── SpotifyImporterTests ├── SpotifyImporterTests.m └── Supporting Files └── Info.plist /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | #Pods/ 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Spotify Playlist Importer to Apple Music 2 | 3 | OS X which help you to import your playlist from Spotify to Apple Music. Inspired by [spotify2am](https://github.com/simonschellaert/spotify2am) am which didn't work for me :D 4 | 5 | ### [Downwload DMG](https://raw.github.com/m1entus/SpotifyImporter/master/SpotifyImporter.dmg) 6 | 7 | 8 | [![](https://raw.github.com/m1entus/SpotifyImporter/master/Screens/screen1.png)](https://raw.github.com/m1entus/SpotifyImporter/master/Screens/screen1.png) 9 | 10 | ## Why ? 11 | I created this project before [@maciej's Playlist Importer](https://lupin.rocks/entry/seamlessly-import-your-spotify-playlists-into-itunes) show up, i am iOS developer and i wanted to try OS X development. Disadvantage of @maciej's Playlist Importer is that you have to leave your Mac for a some time and it can't import your songs in background. This project disadvantage it that you have to sniff iTunes packet to get your account identifiers and cookie but it works in background! 12 | 13 | # Usage 14 | 15 | ## 1. Export from Spotify and import to SpotiyImporter 16 | 17 | 1. Export the Spotify URIs 18 | 19 | You can copy as many as you want, but remember - only single track URIs are supported. This is how it should look like: 20 | 21 | [![](https://raw.github.com/m1entus/SpotifyImporter/master/Screens/screen2.png)](https://raw.github.com/m1entus/SpotifyImporter/master/Screens/screen2.png) 22 | 23 | 2. Export the Spotify songs to an CSV File 24 | 25 | The first step is getting the songs you want to import into Apple Music into a CSV file. The simplest way to do this is using [Exportify](https://rawgit.com/watsonbox/exportify/master/exportify.html). 26 | If you want to export you whole Spotify library, simply create a new playlist called All and drag your whole library into it using the Spotify desktop app. You can then export the playlist All using Exportify. Save the resulting file as spotify.csv in the same directory as the directory you cloned this repo into. 27 | 28 | ## 2. Use an intercepting proxy to retrieve the Apple Music request headers 29 | 30 | We are going to retrieve cookie data from iTunes using Charles Proxy. 31 | 32 | 1. From the Menu Proxy go to SSL Proxy Settings 33 | 34 | 2. Check 'Enable SSL Proxying' 35 | 36 | 3. Click on add and insert '*itunes.apple.com' 37 | 38 | 4. In the same Menu check on 'Mac OS X Proxy' 39 | 40 | 5. Go to iTunes go to an Apple Music playlist but don't do nothing 41 | 42 | 6. Check you have enabled recording (please refer to image below) 43 | 44 | 7. When recording is enabled add the playlist to my Music 45 | 46 | [![](https://raw.github.com/m1entus/SpotifyImporter/master/Screens/screen3_thumb.png)](https://raw.github.com/m1entus/SpotifyImporter/master/Screens/screen3.png) 47 | 48 | Application simply compares the title and artist to find out if a Spotify and Apple Music song match, additionaly if some of this didn't match i am calculating matching score based on title of song. Some songs don't have the exact same title (extraneous spacing for example) in both services. 49 | -------------------------------------------------------------------------------- /Screens/screen1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1entus/SpotifyImporter/b8a1bd518594e20a9ccfff871d31b72ebaba5287/Screens/screen1.png -------------------------------------------------------------------------------- /Screens/screen2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1entus/SpotifyImporter/b8a1bd518594e20a9ccfff871d31b72ebaba5287/Screens/screen2.png -------------------------------------------------------------------------------- /Screens/screen3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1entus/SpotifyImporter/b8a1bd518594e20a9ccfff871d31b72ebaba5287/Screens/screen3.png -------------------------------------------------------------------------------- /Screens/screen3_thumb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1entus/SpotifyImporter/b8a1bd518594e20a9ccfff871d31b72ebaba5287/Screens/screen3_thumb.png -------------------------------------------------------------------------------- /SpotifyImporter.dmg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1entus/SpotifyImporter/b8a1bd518594e20a9ccfff871d31b72ebaba5287/SpotifyImporter.dmg -------------------------------------------------------------------------------- /SpotifyImporter/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, "10.10" 2 | 3 | pod 'AFNetworking' 4 | pod 'CHCSVParser' 5 | pod 'DJProgressHUD_OSX' 6 | 7 | inhibit_all_warnings! -------------------------------------------------------------------------------- /SpotifyImporter/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AFNetworking (2.5.4): 3 | - AFNetworking/NSURLConnection (= 2.5.4) 4 | - AFNetworking/NSURLSession (= 2.5.4) 5 | - AFNetworking/Reachability (= 2.5.4) 6 | - AFNetworking/Security (= 2.5.4) 7 | - AFNetworking/Serialization (= 2.5.4) 8 | - AFNetworking/UIKit (= 2.5.4) 9 | - AFNetworking/NSURLConnection (2.5.4): 10 | - AFNetworking/Reachability 11 | - AFNetworking/Security 12 | - AFNetworking/Serialization 13 | - AFNetworking/NSURLSession (2.5.4): 14 | - AFNetworking/Reachability 15 | - AFNetworking/Security 16 | - AFNetworking/Serialization 17 | - AFNetworking/Reachability (2.5.4) 18 | - AFNetworking/Security (2.5.4) 19 | - AFNetworking/Serialization (2.5.4) 20 | - CHCSVParser (2.1.0) 21 | - DJProgressHUD_OSX (0.0.1) 22 | 23 | DEPENDENCIES: 24 | - AFNetworking 25 | - CHCSVParser 26 | - DJProgressHUD_OSX 27 | 28 | SPEC CHECKSUMS: 29 | AFNetworking: 05edc0ac4c4c8cf57bcf4b84be5b0744b6d8e71e 30 | CHCSVParser: 3bcc7bdb72111a31222e95d57eacceefe4e537e8 31 | DJProgressHUD_OSX: a8de6d2da8750dbf671fcddc649a44a9cd2807c4 32 | 33 | COCOAPODS: 0.37.2 34 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.h: -------------------------------------------------------------------------------- 1 | // AFHTTPRequestOperation.h 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import 23 | #import "AFURLConnectionOperation.h" 24 | 25 | /** 26 | `AFHTTPRequestOperation` is a subclass of `AFURLConnectionOperation` for requests using the HTTP or HTTPS protocols. It encapsulates the concept of acceptable status codes and content types, which determine the success or failure of a request. 27 | */ 28 | @interface AFHTTPRequestOperation : AFURLConnectionOperation 29 | 30 | ///------------------------------------------------ 31 | /// @name Getting HTTP URL Connection Information 32 | ///------------------------------------------------ 33 | 34 | /** 35 | The last HTTP response received by the operation's connection. 36 | */ 37 | @property (readonly, nonatomic, strong) NSHTTPURLResponse *response; 38 | 39 | /** 40 | Responses sent from the server in data tasks created with `dataTaskWithRequest:success:failure:` and run using the `GET` / `POST` / et al. convenience methods are automatically validated and serialized by the response serializer. By default, this property is set to an AFHTTPResponse serializer, which uses the raw data as its response object. The serializer validates the status code to be in the `2XX` range, denoting success. If the response serializer generates an error in `-responseObjectForResponse:data:error:`, the `failure` callback of the session task or request operation will be executed; otherwise, the `success` callback will be executed. 41 | 42 | @warning `responseSerializer` must not be `nil`. Setting a response serializer will clear out any cached value 43 | */ 44 | @property (nonatomic, strong) AFHTTPResponseSerializer * responseSerializer; 45 | 46 | /** 47 | An object constructed by the `responseSerializer` from the response and response data. Returns `nil` unless the operation `isFinished`, has a `response`, and has `responseData` with non-zero content length. If an error occurs during serialization, `nil` will be returned, and the `error` property will be populated with the serialization error. 48 | */ 49 | @property (readonly, nonatomic, strong) id responseObject; 50 | 51 | ///----------------------------------------------------------- 52 | /// @name Setting Completion Block Success / Failure Callbacks 53 | ///----------------------------------------------------------- 54 | 55 | /** 56 | Sets the `completionBlock` property with a block that executes either the specified success or failure block, depending on the state of the request on completion. If `error` returns a value, which can be caused by an unacceptable status code or content type, then `failure` is executed. Otherwise, `success` is executed. 57 | 58 | This method should be overridden in subclasses in order to specify the response object passed into the success block. 59 | 60 | @param success The block to be executed on the completion of a successful request. This block has no return value and takes two arguments: the receiver operation and the object constructed from the response data of the request. 61 | @param failure The block to be executed on the completion of an unsuccessful request. This block has no return value and takes two arguments: the receiver operation and the error that occurred during the request. 62 | */ 63 | - (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 64 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure; 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperation.m: -------------------------------------------------------------------------------- 1 | // AFHTTPRequestOperation.m 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import "AFHTTPRequestOperation.h" 23 | 24 | static dispatch_queue_t http_request_operation_processing_queue() { 25 | static dispatch_queue_t af_http_request_operation_processing_queue; 26 | static dispatch_once_t onceToken; 27 | dispatch_once(&onceToken, ^{ 28 | af_http_request_operation_processing_queue = dispatch_queue_create("com.alamofire.networking.http-request.processing", DISPATCH_QUEUE_CONCURRENT); 29 | }); 30 | 31 | return af_http_request_operation_processing_queue; 32 | } 33 | 34 | static dispatch_group_t http_request_operation_completion_group() { 35 | static dispatch_group_t af_http_request_operation_completion_group; 36 | static dispatch_once_t onceToken; 37 | dispatch_once(&onceToken, ^{ 38 | af_http_request_operation_completion_group = dispatch_group_create(); 39 | }); 40 | 41 | return af_http_request_operation_completion_group; 42 | } 43 | 44 | #pragma mark - 45 | 46 | @interface AFURLConnectionOperation () 47 | @property (readwrite, nonatomic, strong) NSURLRequest *request; 48 | @property (readwrite, nonatomic, strong) NSURLResponse *response; 49 | @end 50 | 51 | @interface AFHTTPRequestOperation () 52 | @property (readwrite, nonatomic, strong) NSHTTPURLResponse *response; 53 | @property (readwrite, nonatomic, strong) id responseObject; 54 | @property (readwrite, nonatomic, strong) NSError *responseSerializationError; 55 | @property (readwrite, nonatomic, strong) NSRecursiveLock *lock; 56 | @end 57 | 58 | @implementation AFHTTPRequestOperation 59 | @dynamic response; 60 | @dynamic lock; 61 | 62 | - (instancetype)initWithRequest:(NSURLRequest *)urlRequest { 63 | self = [super initWithRequest:urlRequest]; 64 | if (!self) { 65 | return nil; 66 | } 67 | 68 | self.responseSerializer = [AFHTTPResponseSerializer serializer]; 69 | 70 | return self; 71 | } 72 | 73 | - (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { 74 | NSParameterAssert(responseSerializer); 75 | 76 | [self.lock lock]; 77 | _responseSerializer = responseSerializer; 78 | self.responseObject = nil; 79 | self.responseSerializationError = nil; 80 | [self.lock unlock]; 81 | } 82 | 83 | - (id)responseObject { 84 | [self.lock lock]; 85 | if (!_responseObject && [self isFinished] && !self.error) { 86 | NSError *error = nil; 87 | self.responseObject = [self.responseSerializer responseObjectForResponse:self.response data:self.responseData error:&error]; 88 | if (error) { 89 | self.responseSerializationError = error; 90 | } 91 | } 92 | [self.lock unlock]; 93 | 94 | return _responseObject; 95 | } 96 | 97 | - (NSError *)error { 98 | if (_responseSerializationError) { 99 | return _responseSerializationError; 100 | } else { 101 | return [super error]; 102 | } 103 | } 104 | 105 | #pragma mark - AFHTTPRequestOperation 106 | 107 | - (void)setCompletionBlockWithSuccess:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 108 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 109 | { 110 | // completionBlock is manually nilled out in AFURLConnectionOperation to break the retain cycle. 111 | #pragma clang diagnostic push 112 | #pragma clang diagnostic ignored "-Warc-retain-cycles" 113 | #pragma clang diagnostic ignored "-Wgnu" 114 | self.completionBlock = ^{ 115 | if (self.completionGroup) { 116 | dispatch_group_enter(self.completionGroup); 117 | } 118 | 119 | dispatch_async(http_request_operation_processing_queue(), ^{ 120 | if (self.error) { 121 | if (failure) { 122 | dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ 123 | failure(self, self.error); 124 | }); 125 | } 126 | } else { 127 | id responseObject = self.responseObject; 128 | if (self.error) { 129 | if (failure) { 130 | dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ 131 | failure(self, self.error); 132 | }); 133 | } 134 | } else { 135 | if (success) { 136 | dispatch_group_async(self.completionGroup ?: http_request_operation_completion_group(), self.completionQueue ?: dispatch_get_main_queue(), ^{ 137 | success(self, responseObject); 138 | }); 139 | } 140 | } 141 | } 142 | 143 | if (self.completionGroup) { 144 | dispatch_group_leave(self.completionGroup); 145 | } 146 | }); 147 | }; 148 | #pragma clang diagnostic pop 149 | } 150 | 151 | #pragma mark - AFURLRequestOperation 152 | 153 | - (void)pause { 154 | [super pause]; 155 | 156 | u_int64_t offset = 0; 157 | if ([self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey]) { 158 | offset = [(NSNumber *)[self.outputStream propertyForKey:NSStreamFileCurrentOffsetKey] unsignedLongLongValue]; 159 | } else { 160 | offset = [(NSData *)[self.outputStream propertyForKey:NSStreamDataWrittenToMemoryStreamKey] length]; 161 | } 162 | 163 | NSMutableURLRequest *mutableURLRequest = [self.request mutableCopy]; 164 | if ([self.response respondsToSelector:@selector(allHeaderFields)] && [[self.response allHeaderFields] valueForKey:@"ETag"]) { 165 | [mutableURLRequest setValue:[[self.response allHeaderFields] valueForKey:@"ETag"] forHTTPHeaderField:@"If-Range"]; 166 | } 167 | [mutableURLRequest setValue:[NSString stringWithFormat:@"bytes=%llu-", offset] forHTTPHeaderField:@"Range"]; 168 | self.request = mutableURLRequest; 169 | } 170 | 171 | #pragma mark - NSSecureCoding 172 | 173 | + (BOOL)supportsSecureCoding { 174 | return YES; 175 | } 176 | 177 | - (id)initWithCoder:(NSCoder *)decoder { 178 | self = [super initWithCoder:decoder]; 179 | if (!self) { 180 | return nil; 181 | } 182 | 183 | self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; 184 | 185 | return self; 186 | } 187 | 188 | - (void)encodeWithCoder:(NSCoder *)coder { 189 | [super encodeWithCoder:coder]; 190 | 191 | [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; 192 | } 193 | 194 | #pragma mark - NSCopying 195 | 196 | - (id)copyWithZone:(NSZone *)zone { 197 | AFHTTPRequestOperation *operation = [super copyWithZone:zone]; 198 | 199 | operation.responseSerializer = [self.responseSerializer copyWithZone:zone]; 200 | operation.completionQueue = self.completionQueue; 201 | operation.completionGroup = self.completionGroup; 202 | 203 | return operation; 204 | } 205 | 206 | @end 207 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/AFNetworking/AFNetworking/AFHTTPRequestOperationManager.m: -------------------------------------------------------------------------------- 1 | // AFHTTPRequestOperationManager.m 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import 23 | 24 | #import "AFHTTPRequestOperationManager.h" 25 | #import "AFHTTPRequestOperation.h" 26 | 27 | #import 28 | #import 29 | 30 | #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) 31 | #import 32 | #endif 33 | 34 | @interface AFHTTPRequestOperationManager () 35 | @property (readwrite, nonatomic, strong) NSURL *baseURL; 36 | @end 37 | 38 | @implementation AFHTTPRequestOperationManager 39 | 40 | + (instancetype)manager { 41 | return [[self alloc] initWithBaseURL:nil]; 42 | } 43 | 44 | - (instancetype)init { 45 | return [self initWithBaseURL:nil]; 46 | } 47 | 48 | - (instancetype)initWithBaseURL:(NSURL *)url { 49 | self = [super init]; 50 | if (!self) { 51 | return nil; 52 | } 53 | 54 | // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected 55 | if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) { 56 | url = [url URLByAppendingPathComponent:@""]; 57 | } 58 | 59 | self.baseURL = url; 60 | 61 | self.requestSerializer = [AFHTTPRequestSerializer serializer]; 62 | self.responseSerializer = [AFJSONResponseSerializer serializer]; 63 | 64 | self.securityPolicy = [AFSecurityPolicy defaultPolicy]; 65 | 66 | self.reachabilityManager = [AFNetworkReachabilityManager sharedManager]; 67 | 68 | self.operationQueue = [[NSOperationQueue alloc] init]; 69 | 70 | self.shouldUseCredentialStorage = YES; 71 | 72 | return self; 73 | } 74 | 75 | #pragma mark - 76 | 77 | #ifdef _SYSTEMCONFIGURATION_H 78 | #endif 79 | 80 | - (void)setRequestSerializer:(AFHTTPRequestSerializer *)requestSerializer { 81 | NSParameterAssert(requestSerializer); 82 | 83 | _requestSerializer = requestSerializer; 84 | } 85 | 86 | - (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { 87 | NSParameterAssert(responseSerializer); 88 | 89 | _responseSerializer = responseSerializer; 90 | } 91 | 92 | #pragma mark - 93 | 94 | - (AFHTTPRequestOperation *)HTTPRequestOperationWithHTTPMethod:(NSString *)method 95 | URLString:(NSString *)URLString 96 | parameters:(id)parameters 97 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 98 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 99 | { 100 | NSError *serializationError = nil; 101 | NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError]; 102 | if (serializationError) { 103 | if (failure) { 104 | #pragma clang diagnostic push 105 | #pragma clang diagnostic ignored "-Wgnu" 106 | dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ 107 | failure(nil, serializationError); 108 | }); 109 | #pragma clang diagnostic pop 110 | } 111 | 112 | return nil; 113 | } 114 | 115 | return [self HTTPRequestOperationWithRequest:request success:success failure:failure]; 116 | } 117 | 118 | - (AFHTTPRequestOperation *)HTTPRequestOperationWithRequest:(NSURLRequest *)request 119 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 120 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 121 | { 122 | AFHTTPRequestOperation *operation = [[AFHTTPRequestOperation alloc] initWithRequest:request]; 123 | operation.responseSerializer = self.responseSerializer; 124 | operation.shouldUseCredentialStorage = self.shouldUseCredentialStorage; 125 | operation.credential = self.credential; 126 | operation.securityPolicy = self.securityPolicy; 127 | 128 | [operation setCompletionBlockWithSuccess:success failure:failure]; 129 | operation.completionQueue = self.completionQueue; 130 | operation.completionGroup = self.completionGroup; 131 | 132 | return operation; 133 | } 134 | 135 | #pragma mark - 136 | 137 | - (AFHTTPRequestOperation *)GET:(NSString *)URLString 138 | parameters:(id)parameters 139 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 140 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 141 | { 142 | AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"GET" URLString:URLString parameters:parameters success:success failure:failure]; 143 | 144 | [self.operationQueue addOperation:operation]; 145 | 146 | return operation; 147 | } 148 | 149 | - (AFHTTPRequestOperation *)HEAD:(NSString *)URLString 150 | parameters:(id)parameters 151 | success:(void (^)(AFHTTPRequestOperation *operation))success 152 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 153 | { 154 | AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters success:^(AFHTTPRequestOperation *requestOperation, __unused id responseObject) { 155 | if (success) { 156 | success(requestOperation); 157 | } 158 | } failure:failure]; 159 | 160 | [self.operationQueue addOperation:operation]; 161 | 162 | return operation; 163 | } 164 | 165 | - (AFHTTPRequestOperation *)POST:(NSString *)URLString 166 | parameters:(id)parameters 167 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 168 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 169 | { 170 | AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"POST" URLString:URLString parameters:parameters success:success failure:failure]; 171 | 172 | [self.operationQueue addOperation:operation]; 173 | 174 | return operation; 175 | } 176 | 177 | - (AFHTTPRequestOperation *)POST:(NSString *)URLString 178 | parameters:(id)parameters 179 | constructingBodyWithBlock:(void (^)(id formData))block 180 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 181 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 182 | { 183 | NSError *serializationError = nil; 184 | NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError]; 185 | if (serializationError) { 186 | if (failure) { 187 | #pragma clang diagnostic push 188 | #pragma clang diagnostic ignored "-Wgnu" 189 | dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ 190 | failure(nil, serializationError); 191 | }); 192 | #pragma clang diagnostic pop 193 | } 194 | 195 | return nil; 196 | } 197 | 198 | AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithRequest:request success:success failure:failure]; 199 | 200 | [self.operationQueue addOperation:operation]; 201 | 202 | return operation; 203 | } 204 | 205 | - (AFHTTPRequestOperation *)PUT:(NSString *)URLString 206 | parameters:(id)parameters 207 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 208 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 209 | { 210 | AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters success:success failure:failure]; 211 | 212 | [self.operationQueue addOperation:operation]; 213 | 214 | return operation; 215 | } 216 | 217 | - (AFHTTPRequestOperation *)PATCH:(NSString *)URLString 218 | parameters:(id)parameters 219 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 220 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 221 | { 222 | AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters success:success failure:failure]; 223 | 224 | [self.operationQueue addOperation:operation]; 225 | 226 | return operation; 227 | } 228 | 229 | - (AFHTTPRequestOperation *)DELETE:(NSString *)URLString 230 | parameters:(id)parameters 231 | success:(void (^)(AFHTTPRequestOperation *operation, id responseObject))success 232 | failure:(void (^)(AFHTTPRequestOperation *operation, NSError *error))failure 233 | { 234 | AFHTTPRequestOperation *operation = [self HTTPRequestOperationWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters success:success failure:failure]; 235 | 236 | [self.operationQueue addOperation:operation]; 237 | 238 | return operation; 239 | } 240 | 241 | #pragma mark - NSObject 242 | 243 | - (NSString *)description { 244 | return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.operationQueue]; 245 | } 246 | 247 | #pragma mark - NSSecureCoding 248 | 249 | + (BOOL)supportsSecureCoding { 250 | return YES; 251 | } 252 | 253 | - (id)initWithCoder:(NSCoder *)decoder { 254 | NSURL *baseURL = [decoder decodeObjectForKey:NSStringFromSelector(@selector(baseURL))]; 255 | 256 | self = [self initWithBaseURL:baseURL]; 257 | if (!self) { 258 | return nil; 259 | } 260 | 261 | self.requestSerializer = [decoder decodeObjectOfClass:[AFHTTPRequestSerializer class] forKey:NSStringFromSelector(@selector(requestSerializer))]; 262 | self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; 263 | 264 | return self; 265 | } 266 | 267 | - (void)encodeWithCoder:(NSCoder *)coder { 268 | [coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))]; 269 | [coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))]; 270 | [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; 271 | } 272 | 273 | #pragma mark - NSCopying 274 | 275 | - (id)copyWithZone:(NSZone *)zone { 276 | AFHTTPRequestOperationManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL]; 277 | 278 | HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone]; 279 | HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone]; 280 | 281 | return HTTPClient; 282 | } 283 | 284 | @end 285 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/AFNetworking/AFNetworking/AFHTTPSessionManager.m: -------------------------------------------------------------------------------- 1 | // AFHTTPSessionManager.m 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import "AFHTTPSessionManager.h" 23 | 24 | #if (defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000) || (defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) 25 | 26 | #import "AFURLRequestSerialization.h" 27 | #import "AFURLResponseSerialization.h" 28 | 29 | #import 30 | #import 31 | 32 | #ifdef _SYSTEMCONFIGURATION_H 33 | #import 34 | #import 35 | #import 36 | #import 37 | #import 38 | #endif 39 | 40 | #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) 41 | #import 42 | #endif 43 | 44 | @interface AFHTTPSessionManager () 45 | @property (readwrite, nonatomic, strong) NSURL *baseURL; 46 | @end 47 | 48 | @implementation AFHTTPSessionManager 49 | @dynamic responseSerializer; 50 | 51 | + (instancetype)manager { 52 | return [[[self class] alloc] initWithBaseURL:nil]; 53 | } 54 | 55 | - (instancetype)init { 56 | return [self initWithBaseURL:nil]; 57 | } 58 | 59 | - (instancetype)initWithBaseURL:(NSURL *)url { 60 | return [self initWithBaseURL:url sessionConfiguration:nil]; 61 | } 62 | 63 | - (instancetype)initWithSessionConfiguration:(NSURLSessionConfiguration *)configuration { 64 | return [self initWithBaseURL:nil sessionConfiguration:configuration]; 65 | } 66 | 67 | - (instancetype)initWithBaseURL:(NSURL *)url 68 | sessionConfiguration:(NSURLSessionConfiguration *)configuration 69 | { 70 | self = [super initWithSessionConfiguration:configuration]; 71 | if (!self) { 72 | return nil; 73 | } 74 | 75 | // Ensure terminal slash for baseURL path, so that NSURL +URLWithString:relativeToURL: works as expected 76 | if ([[url path] length] > 0 && ![[url absoluteString] hasSuffix:@"/"]) { 77 | url = [url URLByAppendingPathComponent:@""]; 78 | } 79 | 80 | self.baseURL = url; 81 | 82 | self.requestSerializer = [AFHTTPRequestSerializer serializer]; 83 | self.responseSerializer = [AFJSONResponseSerializer serializer]; 84 | 85 | return self; 86 | } 87 | 88 | #pragma mark - 89 | 90 | #ifdef _SYSTEMCONFIGURATION_H 91 | #endif 92 | 93 | - (void)setRequestSerializer:(AFHTTPRequestSerializer *)requestSerializer { 94 | NSParameterAssert(requestSerializer); 95 | 96 | _requestSerializer = requestSerializer; 97 | } 98 | 99 | - (void)setResponseSerializer:(AFHTTPResponseSerializer *)responseSerializer { 100 | NSParameterAssert(responseSerializer); 101 | 102 | [super setResponseSerializer:responseSerializer]; 103 | } 104 | 105 | #pragma mark - 106 | 107 | - (NSURLSessionDataTask *)GET:(NSString *)URLString 108 | parameters:(id)parameters 109 | success:(void (^)(NSURLSessionDataTask *task, id responseObject))success 110 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure 111 | { 112 | NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"GET" URLString:URLString parameters:parameters success:success failure:failure]; 113 | 114 | [dataTask resume]; 115 | 116 | return dataTask; 117 | } 118 | 119 | - (NSURLSessionDataTask *)HEAD:(NSString *)URLString 120 | parameters:(id)parameters 121 | success:(void (^)(NSURLSessionDataTask *task))success 122 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure 123 | { 124 | NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"HEAD" URLString:URLString parameters:parameters success:^(NSURLSessionDataTask *task, __unused id responseObject) { 125 | if (success) { 126 | success(task); 127 | } 128 | } failure:failure]; 129 | 130 | [dataTask resume]; 131 | 132 | return dataTask; 133 | } 134 | 135 | - (NSURLSessionDataTask *)POST:(NSString *)URLString 136 | parameters:(id)parameters 137 | success:(void (^)(NSURLSessionDataTask *task, id responseObject))success 138 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure 139 | { 140 | NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"POST" URLString:URLString parameters:parameters success:success failure:failure]; 141 | 142 | [dataTask resume]; 143 | 144 | return dataTask; 145 | } 146 | 147 | - (NSURLSessionDataTask *)POST:(NSString *)URLString 148 | parameters:(id)parameters 149 | constructingBodyWithBlock:(void (^)(id formData))block 150 | success:(void (^)(NSURLSessionDataTask *task, id responseObject))success 151 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure 152 | { 153 | NSError *serializationError = nil; 154 | NSMutableURLRequest *request = [self.requestSerializer multipartFormRequestWithMethod:@"POST" URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters constructingBodyWithBlock:block error:&serializationError]; 155 | if (serializationError) { 156 | if (failure) { 157 | #pragma clang diagnostic push 158 | #pragma clang diagnostic ignored "-Wgnu" 159 | dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ 160 | failure(nil, serializationError); 161 | }); 162 | #pragma clang diagnostic pop 163 | } 164 | 165 | return nil; 166 | } 167 | 168 | __block NSURLSessionDataTask *task = [self uploadTaskWithStreamedRequest:request progress:nil completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { 169 | if (error) { 170 | if (failure) { 171 | failure(task, error); 172 | } 173 | } else { 174 | if (success) { 175 | success(task, responseObject); 176 | } 177 | } 178 | }]; 179 | 180 | [task resume]; 181 | 182 | return task; 183 | } 184 | 185 | - (NSURLSessionDataTask *)PUT:(NSString *)URLString 186 | parameters:(id)parameters 187 | success:(void (^)(NSURLSessionDataTask *task, id responseObject))success 188 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure 189 | { 190 | NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PUT" URLString:URLString parameters:parameters success:success failure:failure]; 191 | 192 | [dataTask resume]; 193 | 194 | return dataTask; 195 | } 196 | 197 | - (NSURLSessionDataTask *)PATCH:(NSString *)URLString 198 | parameters:(id)parameters 199 | success:(void (^)(NSURLSessionDataTask *task, id responseObject))success 200 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure 201 | { 202 | NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"PATCH" URLString:URLString parameters:parameters success:success failure:failure]; 203 | 204 | [dataTask resume]; 205 | 206 | return dataTask; 207 | } 208 | 209 | - (NSURLSessionDataTask *)DELETE:(NSString *)URLString 210 | parameters:(id)parameters 211 | success:(void (^)(NSURLSessionDataTask *task, id responseObject))success 212 | failure:(void (^)(NSURLSessionDataTask *task, NSError *error))failure 213 | { 214 | NSURLSessionDataTask *dataTask = [self dataTaskWithHTTPMethod:@"DELETE" URLString:URLString parameters:parameters success:success failure:failure]; 215 | 216 | [dataTask resume]; 217 | 218 | return dataTask; 219 | } 220 | 221 | - (NSURLSessionDataTask *)dataTaskWithHTTPMethod:(NSString *)method 222 | URLString:(NSString *)URLString 223 | parameters:(id)parameters 224 | success:(void (^)(NSURLSessionDataTask *, id))success 225 | failure:(void (^)(NSURLSessionDataTask *, NSError *))failure 226 | { 227 | NSError *serializationError = nil; 228 | NSMutableURLRequest *request = [self.requestSerializer requestWithMethod:method URLString:[[NSURL URLWithString:URLString relativeToURL:self.baseURL] absoluteString] parameters:parameters error:&serializationError]; 229 | if (serializationError) { 230 | if (failure) { 231 | #pragma clang diagnostic push 232 | #pragma clang diagnostic ignored "-Wgnu" 233 | dispatch_async(self.completionQueue ?: dispatch_get_main_queue(), ^{ 234 | failure(nil, serializationError); 235 | }); 236 | #pragma clang diagnostic pop 237 | } 238 | 239 | return nil; 240 | } 241 | 242 | __block NSURLSessionDataTask *dataTask = nil; 243 | dataTask = [self dataTaskWithRequest:request completionHandler:^(NSURLResponse * __unused response, id responseObject, NSError *error) { 244 | if (error) { 245 | if (failure) { 246 | failure(dataTask, error); 247 | } 248 | } else { 249 | if (success) { 250 | success(dataTask, responseObject); 251 | } 252 | } 253 | }]; 254 | 255 | return dataTask; 256 | } 257 | 258 | #pragma mark - NSObject 259 | 260 | - (NSString *)description { 261 | return [NSString stringWithFormat:@"<%@: %p, baseURL: %@, session: %@, operationQueue: %@>", NSStringFromClass([self class]), self, [self.baseURL absoluteString], self.session, self.operationQueue]; 262 | } 263 | 264 | #pragma mark - NSSecureCoding 265 | 266 | + (BOOL)supportsSecureCoding { 267 | return YES; 268 | } 269 | 270 | - (id)initWithCoder:(NSCoder *)decoder { 271 | NSURL *baseURL = [decoder decodeObjectOfClass:[NSURL class] forKey:NSStringFromSelector(@selector(baseURL))]; 272 | NSURLSessionConfiguration *configuration = [decoder decodeObjectOfClass:[NSURLSessionConfiguration class] forKey:@"sessionConfiguration"]; 273 | if (!configuration) { 274 | NSString *configurationIdentifier = [decoder decodeObjectOfClass:[NSString class] forKey:@"identifier"]; 275 | if (configurationIdentifier) { 276 | #if (defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED >= 80000) || (defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1100) 277 | configuration = [NSURLSessionConfiguration backgroundSessionConfigurationWithIdentifier:configurationIdentifier]; 278 | #else 279 | configuration = [NSURLSessionConfiguration backgroundSessionConfiguration:configurationIdentifier]; 280 | #endif 281 | } 282 | } 283 | 284 | self = [self initWithBaseURL:baseURL sessionConfiguration:configuration]; 285 | if (!self) { 286 | return nil; 287 | } 288 | 289 | self.requestSerializer = [decoder decodeObjectOfClass:[AFHTTPRequestSerializer class] forKey:NSStringFromSelector(@selector(requestSerializer))]; 290 | self.responseSerializer = [decoder decodeObjectOfClass:[AFHTTPResponseSerializer class] forKey:NSStringFromSelector(@selector(responseSerializer))]; 291 | 292 | return self; 293 | } 294 | 295 | - (void)encodeWithCoder:(NSCoder *)coder { 296 | [super encodeWithCoder:coder]; 297 | 298 | [coder encodeObject:self.baseURL forKey:NSStringFromSelector(@selector(baseURL))]; 299 | if ([self.session.configuration conformsToProtocol:@protocol(NSCoding)]) { 300 | [coder encodeObject:self.session.configuration forKey:@"sessionConfiguration"]; 301 | } else { 302 | [coder encodeObject:self.session.configuration.identifier forKey:@"identifier"]; 303 | } 304 | [coder encodeObject:self.requestSerializer forKey:NSStringFromSelector(@selector(requestSerializer))]; 305 | [coder encodeObject:self.responseSerializer forKey:NSStringFromSelector(@selector(responseSerializer))]; 306 | } 307 | 308 | #pragma mark - NSCopying 309 | 310 | - (id)copyWithZone:(NSZone *)zone { 311 | AFHTTPSessionManager *HTTPClient = [[[self class] allocWithZone:zone] initWithBaseURL:self.baseURL sessionConfiguration:self.session.configuration]; 312 | 313 | HTTPClient.requestSerializer = [self.requestSerializer copyWithZone:zone]; 314 | HTTPClient.responseSerializer = [self.responseSerializer copyWithZone:zone]; 315 | 316 | return HTTPClient; 317 | } 318 | 319 | @end 320 | 321 | #endif 322 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.h: -------------------------------------------------------------------------------- 1 | // AFNetworkReachabilityManager.h 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import 23 | #import 24 | 25 | #ifndef NS_DESIGNATED_INITIALIZER 26 | #if __has_attribute(objc_designated_initializer) 27 | #define NS_DESIGNATED_INITIALIZER __attribute__((objc_designated_initializer)) 28 | #else 29 | #define NS_DESIGNATED_INITIALIZER 30 | #endif 31 | #endif 32 | 33 | typedef NS_ENUM(NSInteger, AFNetworkReachabilityStatus) { 34 | AFNetworkReachabilityStatusUnknown = -1, 35 | AFNetworkReachabilityStatusNotReachable = 0, 36 | AFNetworkReachabilityStatusReachableViaWWAN = 1, 37 | AFNetworkReachabilityStatusReachableViaWiFi = 2, 38 | }; 39 | 40 | /** 41 | `AFNetworkReachabilityManager` monitors the reachability of domains, and addresses for both WWAN and WiFi network interfaces. 42 | 43 | Reachability can be used to determine background information about why a network operation failed, or to trigger a network operation retrying when a connection is established. It should not be used to prevent a user from initiating a network request, as it's possible that an initial request may be required to establish reachability. 44 | 45 | See Apple's Reachability Sample Code (https://developer.apple.com/library/ios/samplecode/reachability/) 46 | 47 | @warning Instances of `AFNetworkReachabilityManager` must be started with `-startMonitoring` before reachability status can be determined. 48 | */ 49 | @interface AFNetworkReachabilityManager : NSObject 50 | 51 | /** 52 | The current network reachability status. 53 | */ 54 | @property (readonly, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; 55 | 56 | /** 57 | Whether or not the network is currently reachable. 58 | */ 59 | @property (readonly, nonatomic, assign, getter = isReachable) BOOL reachable; 60 | 61 | /** 62 | Whether or not the network is currently reachable via WWAN. 63 | */ 64 | @property (readonly, nonatomic, assign, getter = isReachableViaWWAN) BOOL reachableViaWWAN; 65 | 66 | /** 67 | Whether or not the network is currently reachable via WiFi. 68 | */ 69 | @property (readonly, nonatomic, assign, getter = isReachableViaWiFi) BOOL reachableViaWiFi; 70 | 71 | ///--------------------- 72 | /// @name Initialization 73 | ///--------------------- 74 | 75 | /** 76 | Returns the shared network reachability manager. 77 | */ 78 | + (instancetype)sharedManager; 79 | 80 | /** 81 | Creates and returns a network reachability manager for the specified domain. 82 | 83 | @param domain The domain used to evaluate network reachability. 84 | 85 | @return An initialized network reachability manager, actively monitoring the specified domain. 86 | */ 87 | + (instancetype)managerForDomain:(NSString *)domain; 88 | 89 | /** 90 | Creates and returns a network reachability manager for the socket address. 91 | 92 | @param address The socket address (`sockaddr_in`) used to evaluate network reachability. 93 | 94 | @return An initialized network reachability manager, actively monitoring the specified socket address. 95 | */ 96 | + (instancetype)managerForAddress:(const void *)address; 97 | 98 | /** 99 | Initializes an instance of a network reachability manager from the specified reachability object. 100 | 101 | @param reachability The reachability object to monitor. 102 | 103 | @return An initialized network reachability manager, actively monitoring the specified reachability. 104 | */ 105 | - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability NS_DESIGNATED_INITIALIZER; 106 | 107 | ///-------------------------------------------------- 108 | /// @name Starting & Stopping Reachability Monitoring 109 | ///-------------------------------------------------- 110 | 111 | /** 112 | Starts monitoring for changes in network reachability status. 113 | */ 114 | - (void)startMonitoring; 115 | 116 | /** 117 | Stops monitoring for changes in network reachability status. 118 | */ 119 | - (void)stopMonitoring; 120 | 121 | ///------------------------------------------------- 122 | /// @name Getting Localized Reachability Description 123 | ///------------------------------------------------- 124 | 125 | /** 126 | Returns a localized string representation of the current network reachability status. 127 | */ 128 | - (NSString *)localizedNetworkReachabilityStatusString; 129 | 130 | ///--------------------------------------------------- 131 | /// @name Setting Network Reachability Change Callback 132 | ///--------------------------------------------------- 133 | 134 | /** 135 | Sets a callback to be executed when the network availability of the `baseURL` host changes. 136 | 137 | @param block A block object to be executed when the network availability of the `baseURL` host changes.. This block has no return value and takes a single argument which represents the various reachability states from the device to the `baseURL`. 138 | */ 139 | - (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block; 140 | 141 | @end 142 | 143 | ///---------------- 144 | /// @name Constants 145 | ///---------------- 146 | 147 | /** 148 | ## Network Reachability 149 | 150 | The following constants are provided by `AFNetworkReachabilityManager` as possible network reachability statuses. 151 | 152 | enum { 153 | AFNetworkReachabilityStatusUnknown, 154 | AFNetworkReachabilityStatusNotReachable, 155 | AFNetworkReachabilityStatusReachableViaWWAN, 156 | AFNetworkReachabilityStatusReachableViaWiFi, 157 | } 158 | 159 | `AFNetworkReachabilityStatusUnknown` 160 | The `baseURL` host reachability is not known. 161 | 162 | `AFNetworkReachabilityStatusNotReachable` 163 | The `baseURL` host cannot be reached. 164 | 165 | `AFNetworkReachabilityStatusReachableViaWWAN` 166 | The `baseURL` host can be reached via a cellular connection, such as EDGE or GPRS. 167 | 168 | `AFNetworkReachabilityStatusReachableViaWiFi` 169 | The `baseURL` host can be reached via a Wi-Fi connection. 170 | 171 | ### Keys for Notification UserInfo Dictionary 172 | 173 | Strings that are used as keys in a `userInfo` dictionary in a network reachability status change notification. 174 | 175 | `AFNetworkingReachabilityNotificationStatusItem` 176 | A key in the userInfo dictionary in a `AFNetworkingReachabilityDidChangeNotification` notification. 177 | The corresponding value is an `NSNumber` object representing the `AFNetworkReachabilityStatus` value for the current reachability status. 178 | */ 179 | 180 | ///-------------------- 181 | /// @name Notifications 182 | ///-------------------- 183 | 184 | /** 185 | Posted when network reachability changes. 186 | This notification assigns no notification object. The `userInfo` dictionary contains an `NSNumber` object under the `AFNetworkingReachabilityNotificationStatusItem` key, representing the `AFNetworkReachabilityStatus` value for the current network reachability. 187 | 188 | @warning In order for network reachability to be monitored, include the `SystemConfiguration` framework in the active target's "Link Binary With Library" build phase, and add `#import ` to the header prefix of the project (`Prefix.pch`). 189 | */ 190 | extern NSString * const AFNetworkingReachabilityDidChangeNotification; 191 | extern NSString * const AFNetworkingReachabilityNotificationStatusItem; 192 | 193 | ///-------------------- 194 | /// @name Functions 195 | ///-------------------- 196 | 197 | /** 198 | Returns a localized string representation of an `AFNetworkReachabilityStatus` value. 199 | */ 200 | extern NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status); 201 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/AFNetworking/AFNetworking/AFNetworkReachabilityManager.m: -------------------------------------------------------------------------------- 1 | // AFNetworkReachabilityManager.m 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import "AFNetworkReachabilityManager.h" 23 | 24 | #import 25 | #import 26 | #import 27 | #import 28 | #import 29 | 30 | NSString * const AFNetworkingReachabilityDidChangeNotification = @"com.alamofire.networking.reachability.change"; 31 | NSString * const AFNetworkingReachabilityNotificationStatusItem = @"AFNetworkingReachabilityNotificationStatusItem"; 32 | 33 | typedef void (^AFNetworkReachabilityStatusBlock)(AFNetworkReachabilityStatus status); 34 | 35 | typedef NS_ENUM(NSUInteger, AFNetworkReachabilityAssociation) { 36 | AFNetworkReachabilityForAddress = 1, 37 | AFNetworkReachabilityForAddressPair = 2, 38 | AFNetworkReachabilityForName = 3, 39 | }; 40 | 41 | NSString * AFStringFromNetworkReachabilityStatus(AFNetworkReachabilityStatus status) { 42 | switch (status) { 43 | case AFNetworkReachabilityStatusNotReachable: 44 | return NSLocalizedStringFromTable(@"Not Reachable", @"AFNetworking", nil); 45 | case AFNetworkReachabilityStatusReachableViaWWAN: 46 | return NSLocalizedStringFromTable(@"Reachable via WWAN", @"AFNetworking", nil); 47 | case AFNetworkReachabilityStatusReachableViaWiFi: 48 | return NSLocalizedStringFromTable(@"Reachable via WiFi", @"AFNetworking", nil); 49 | case AFNetworkReachabilityStatusUnknown: 50 | default: 51 | return NSLocalizedStringFromTable(@"Unknown", @"AFNetworking", nil); 52 | } 53 | } 54 | 55 | static AFNetworkReachabilityStatus AFNetworkReachabilityStatusForFlags(SCNetworkReachabilityFlags flags) { 56 | BOOL isReachable = ((flags & kSCNetworkReachabilityFlagsReachable) != 0); 57 | BOOL needsConnection = ((flags & kSCNetworkReachabilityFlagsConnectionRequired) != 0); 58 | BOOL canConnectionAutomatically = (((flags & kSCNetworkReachabilityFlagsConnectionOnDemand ) != 0) || ((flags & kSCNetworkReachabilityFlagsConnectionOnTraffic) != 0)); 59 | BOOL canConnectWithoutUserInteraction = (canConnectionAutomatically && (flags & kSCNetworkReachabilityFlagsInterventionRequired) == 0); 60 | BOOL isNetworkReachable = (isReachable && (!needsConnection || canConnectWithoutUserInteraction)); 61 | 62 | AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusUnknown; 63 | if (isNetworkReachable == NO) { 64 | status = AFNetworkReachabilityStatusNotReachable; 65 | } 66 | #if TARGET_OS_IPHONE 67 | else if ((flags & kSCNetworkReachabilityFlagsIsWWAN) != 0) { 68 | status = AFNetworkReachabilityStatusReachableViaWWAN; 69 | } 70 | #endif 71 | else { 72 | status = AFNetworkReachabilityStatusReachableViaWiFi; 73 | } 74 | 75 | return status; 76 | } 77 | 78 | static void AFNetworkReachabilityCallback(SCNetworkReachabilityRef __unused target, SCNetworkReachabilityFlags flags, void *info) { 79 | AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); 80 | AFNetworkReachabilityStatusBlock block = (__bridge AFNetworkReachabilityStatusBlock)info; 81 | if (block) { 82 | block(status); 83 | } 84 | 85 | 86 | dispatch_async(dispatch_get_main_queue(), ^{ 87 | NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 88 | NSDictionary *userInfo = @{ AFNetworkingReachabilityNotificationStatusItem: @(status) }; 89 | [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:userInfo]; 90 | }); 91 | 92 | } 93 | 94 | static const void * AFNetworkReachabilityRetainCallback(const void *info) { 95 | return Block_copy(info); 96 | } 97 | 98 | static void AFNetworkReachabilityReleaseCallback(const void *info) { 99 | if (info) { 100 | Block_release(info); 101 | } 102 | } 103 | 104 | @interface AFNetworkReachabilityManager () 105 | @property (readwrite, nonatomic, assign) SCNetworkReachabilityRef networkReachability; 106 | @property (readwrite, nonatomic, assign) AFNetworkReachabilityAssociation networkReachabilityAssociation; 107 | @property (readwrite, nonatomic, assign) AFNetworkReachabilityStatus networkReachabilityStatus; 108 | @property (readwrite, nonatomic, copy) AFNetworkReachabilityStatusBlock networkReachabilityStatusBlock; 109 | @end 110 | 111 | @implementation AFNetworkReachabilityManager 112 | 113 | + (instancetype)sharedManager { 114 | static AFNetworkReachabilityManager *_sharedManager = nil; 115 | static dispatch_once_t onceToken; 116 | dispatch_once(&onceToken, ^{ 117 | struct sockaddr_in address; 118 | bzero(&address, sizeof(address)); 119 | address.sin_len = sizeof(address); 120 | address.sin_family = AF_INET; 121 | 122 | _sharedManager = [self managerForAddress:&address]; 123 | }); 124 | 125 | return _sharedManager; 126 | } 127 | 128 | + (instancetype)managerForDomain:(NSString *)domain { 129 | SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithName(kCFAllocatorDefault, [domain UTF8String]); 130 | 131 | AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; 132 | manager.networkReachabilityAssociation = AFNetworkReachabilityForName; 133 | 134 | return manager; 135 | } 136 | 137 | + (instancetype)managerForAddress:(const void *)address { 138 | SCNetworkReachabilityRef reachability = SCNetworkReachabilityCreateWithAddress(kCFAllocatorDefault, (const struct sockaddr *)address); 139 | 140 | AFNetworkReachabilityManager *manager = [[self alloc] initWithReachability:reachability]; 141 | manager.networkReachabilityAssociation = AFNetworkReachabilityForAddress; 142 | 143 | return manager; 144 | } 145 | 146 | - (instancetype)initWithReachability:(SCNetworkReachabilityRef)reachability { 147 | self = [super init]; 148 | if (!self) { 149 | return nil; 150 | } 151 | 152 | self.networkReachability = reachability; 153 | self.networkReachabilityStatus = AFNetworkReachabilityStatusUnknown; 154 | 155 | return self; 156 | } 157 | 158 | - (void)dealloc { 159 | [self stopMonitoring]; 160 | 161 | if (_networkReachability) { 162 | CFRelease(_networkReachability); 163 | _networkReachability = NULL; 164 | } 165 | } 166 | 167 | #pragma mark - 168 | 169 | - (BOOL)isReachable { 170 | return [self isReachableViaWWAN] || [self isReachableViaWiFi]; 171 | } 172 | 173 | - (BOOL)isReachableViaWWAN { 174 | return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWWAN; 175 | } 176 | 177 | - (BOOL)isReachableViaWiFi { 178 | return self.networkReachabilityStatus == AFNetworkReachabilityStatusReachableViaWiFi; 179 | } 180 | 181 | #pragma mark - 182 | 183 | - (void)startMonitoring { 184 | [self stopMonitoring]; 185 | 186 | if (!self.networkReachability) { 187 | return; 188 | } 189 | 190 | __weak __typeof(self)weakSelf = self; 191 | AFNetworkReachabilityStatusBlock callback = ^(AFNetworkReachabilityStatus status) { 192 | __strong __typeof(weakSelf)strongSelf = weakSelf; 193 | 194 | strongSelf.networkReachabilityStatus = status; 195 | if (strongSelf.networkReachabilityStatusBlock) { 196 | strongSelf.networkReachabilityStatusBlock(status); 197 | } 198 | 199 | }; 200 | 201 | SCNetworkReachabilityContext context = {0, (__bridge void *)callback, AFNetworkReachabilityRetainCallback, AFNetworkReachabilityReleaseCallback, NULL}; 202 | SCNetworkReachabilitySetCallback(self.networkReachability, AFNetworkReachabilityCallback, &context); 203 | SCNetworkReachabilityScheduleWithRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); 204 | 205 | switch (self.networkReachabilityAssociation) { 206 | case AFNetworkReachabilityForName: 207 | break; 208 | case AFNetworkReachabilityForAddress: 209 | case AFNetworkReachabilityForAddressPair: 210 | default: { 211 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0),^{ 212 | SCNetworkReachabilityFlags flags; 213 | SCNetworkReachabilityGetFlags(self.networkReachability, &flags); 214 | AFNetworkReachabilityStatus status = AFNetworkReachabilityStatusForFlags(flags); 215 | dispatch_async(dispatch_get_main_queue(), ^{ 216 | callback(status); 217 | 218 | NSNotificationCenter *notificationCenter = [NSNotificationCenter defaultCenter]; 219 | [notificationCenter postNotificationName:AFNetworkingReachabilityDidChangeNotification object:nil userInfo:@{ AFNetworkingReachabilityNotificationStatusItem: @(status) }]; 220 | 221 | 222 | }); 223 | }); 224 | } 225 | break; 226 | } 227 | } 228 | 229 | - (void)stopMonitoring { 230 | if (!self.networkReachability) { 231 | return; 232 | } 233 | 234 | SCNetworkReachabilityUnscheduleFromRunLoop(self.networkReachability, CFRunLoopGetMain(), kCFRunLoopCommonModes); 235 | } 236 | 237 | #pragma mark - 238 | 239 | - (NSString *)localizedNetworkReachabilityStatusString { 240 | return AFStringFromNetworkReachabilityStatus(self.networkReachabilityStatus); 241 | } 242 | 243 | #pragma mark - 244 | 245 | - (void)setReachabilityStatusChangeBlock:(void (^)(AFNetworkReachabilityStatus status))block { 246 | self.networkReachabilityStatusBlock = block; 247 | } 248 | 249 | #pragma mark - NSKeyValueObserving 250 | 251 | + (NSSet *)keyPathsForValuesAffectingValueForKey:(NSString *)key { 252 | if ([key isEqualToString:@"reachable"] || [key isEqualToString:@"reachableViaWWAN"] || [key isEqualToString:@"reachableViaWiFi"]) { 253 | return [NSSet setWithObject:@"networkReachabilityStatus"]; 254 | } 255 | 256 | return [super keyPathsForValuesAffectingValueForKey:key]; 257 | } 258 | 259 | @end 260 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/AFNetworking/AFNetworking/AFNetworking.h: -------------------------------------------------------------------------------- 1 | // AFNetworking.h 2 | // 3 | // Copyright (c) 2013 AFNetworking (http://afnetworking.com/) 4 | // 5 | // Permission is hereby granted, free of charge, to any person obtaining a copy 6 | // of this software and associated documentation files (the "Software"), to deal 7 | // in the Software without restriction, including without limitation the rights 8 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | // copies of the Software, and to permit persons to whom the Software is 10 | // furnished to do so, subject to the following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included in 13 | // all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | // THE SOFTWARE. 22 | 23 | #import 24 | #import 25 | 26 | #ifndef _AFNETWORKING_ 27 | #define _AFNETWORKING_ 28 | 29 | #import "AFURLRequestSerialization.h" 30 | #import "AFURLResponseSerialization.h" 31 | #import "AFSecurityPolicy.h" 32 | #import "AFNetworkReachabilityManager.h" 33 | 34 | #import "AFURLConnectionOperation.h" 35 | #import "AFHTTPRequestOperation.h" 36 | #import "AFHTTPRequestOperationManager.h" 37 | 38 | #if ( ( defined(__MAC_OS_X_VERSION_MAX_ALLOWED) && __MAC_OS_X_VERSION_MAX_ALLOWED >= 1090) || \ 39 | ( defined(__IPHONE_OS_VERSION_MAX_ALLOWED) && __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 ) ) 40 | #import "AFURLSessionManager.h" 41 | #import "AFHTTPSessionManager.h" 42 | #endif 43 | 44 | #endif /* _AFNETWORKING_ */ 45 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.h: -------------------------------------------------------------------------------- 1 | // AFSecurityPolicy.h 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import 23 | #import 24 | 25 | typedef NS_ENUM(NSUInteger, AFSSLPinningMode) { 26 | AFSSLPinningModeNone, 27 | AFSSLPinningModePublicKey, 28 | AFSSLPinningModeCertificate, 29 | }; 30 | 31 | /** 32 | `AFSecurityPolicy` evaluates server trust against pinned X.509 certificates and public keys over secure connections. 33 | 34 | Adding pinned SSL certificates to your app helps prevent man-in-the-middle attacks and other vulnerabilities. Applications dealing with sensitive customer data or financial information are strongly encouraged to route all communication over an HTTPS connection with SSL pinning configured and enabled. 35 | */ 36 | @interface AFSecurityPolicy : NSObject 37 | 38 | /** 39 | The criteria by which server trust should be evaluated against the pinned SSL certificates. Defaults to `AFSSLPinningModeNone`. 40 | */ 41 | @property (readonly, nonatomic, assign) AFSSLPinningMode SSLPinningMode; 42 | 43 | /** 44 | Whether to evaluate an entire SSL certificate chain, or just the leaf certificate. Defaults to `YES`. 45 | */ 46 | @property (nonatomic, assign) BOOL validatesCertificateChain; 47 | 48 | /** 49 | The certificates used to evaluate server trust according to the SSL pinning mode. By default, this property is set to any (`.cer`) certificates included in the app bundle. 50 | */ 51 | @property (nonatomic, strong) NSArray *pinnedCertificates; 52 | 53 | /** 54 | Whether or not to trust servers with an invalid or expired SSL certificates. Defaults to `NO`. 55 | */ 56 | @property (nonatomic, assign) BOOL allowInvalidCertificates; 57 | 58 | /** 59 | Whether or not to validate the domain name in the certificate's CN field. Defaults to `YES`. 60 | */ 61 | @property (nonatomic, assign) BOOL validatesDomainName; 62 | 63 | ///----------------------------------------- 64 | /// @name Getting Specific Security Policies 65 | ///----------------------------------------- 66 | 67 | /** 68 | Returns the shared default security policy, which does not allow invalid certificates, validates domain name, and does not validate against pinned certificates or public keys. 69 | 70 | @return The default security policy. 71 | */ 72 | + (instancetype)defaultPolicy; 73 | 74 | ///--------------------- 75 | /// @name Initialization 76 | ///--------------------- 77 | 78 | /** 79 | Creates and returns a security policy with the specified pinning mode. 80 | 81 | @param pinningMode The SSL pinning mode. 82 | 83 | @return A new security policy. 84 | */ 85 | + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode; 86 | 87 | ///------------------------------ 88 | /// @name Evaluating Server Trust 89 | ///------------------------------ 90 | 91 | /** 92 | Whether or not the specified server trust should be accepted, based on the security policy. 93 | 94 | This method should be used when responding to an authentication challenge from a server. 95 | 96 | @param serverTrust The X.509 certificate trust of the server. 97 | 98 | @return Whether or not to trust the server. 99 | 100 | @warning This method has been deprecated in favor of `-evaluateServerTrust:forDomain:`. 101 | */ 102 | - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust DEPRECATED_ATTRIBUTE; 103 | 104 | /** 105 | Whether or not the specified server trust should be accepted, based on the security policy. 106 | 107 | This method should be used when responding to an authentication challenge from a server. 108 | 109 | @param serverTrust The X.509 certificate trust of the server. 110 | @param domain The domain of serverTrust. If `nil`, the domain will not be validated. 111 | 112 | @return Whether or not to trust the server. 113 | */ 114 | - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust 115 | forDomain:(NSString *)domain; 116 | 117 | @end 118 | 119 | ///---------------- 120 | /// @name Constants 121 | ///---------------- 122 | 123 | /** 124 | ## SSL Pinning Modes 125 | 126 | The following constants are provided by `AFSSLPinningMode` as possible SSL pinning modes. 127 | 128 | enum { 129 | AFSSLPinningModeNone, 130 | AFSSLPinningModePublicKey, 131 | AFSSLPinningModeCertificate, 132 | } 133 | 134 | `AFSSLPinningModeNone` 135 | Do not used pinned certificates to validate servers. 136 | 137 | `AFSSLPinningModePublicKey` 138 | Validate host certificates against public keys of pinned certificates. 139 | 140 | `AFSSLPinningModeCertificate` 141 | Validate host certificates against pinned certificates. 142 | */ 143 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/AFNetworking/AFNetworking/AFSecurityPolicy.m: -------------------------------------------------------------------------------- 1 | // AFSecurityPolicy.m 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import "AFSecurityPolicy.h" 23 | 24 | #import 25 | 26 | #if !defined(__IPHONE_OS_VERSION_MIN_REQUIRED) 27 | static NSData * AFSecKeyGetData(SecKeyRef key) { 28 | CFDataRef data = NULL; 29 | 30 | __Require_noErr_Quiet(SecItemExport(key, kSecFormatUnknown, kSecItemPemArmour, NULL, &data), _out); 31 | 32 | return (__bridge_transfer NSData *)data; 33 | 34 | _out: 35 | if (data) { 36 | CFRelease(data); 37 | } 38 | 39 | return nil; 40 | } 41 | #endif 42 | 43 | static BOOL AFSecKeyIsEqualToKey(SecKeyRef key1, SecKeyRef key2) { 44 | #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) 45 | return [(__bridge id)key1 isEqual:(__bridge id)key2]; 46 | #else 47 | return [AFSecKeyGetData(key1) isEqual:AFSecKeyGetData(key2)]; 48 | #endif 49 | } 50 | 51 | static id AFPublicKeyForCertificate(NSData *certificate) { 52 | id allowedPublicKey = nil; 53 | SecCertificateRef allowedCertificate; 54 | SecCertificateRef allowedCertificates[1]; 55 | CFArrayRef tempCertificates = nil; 56 | SecPolicyRef policy = nil; 57 | SecTrustRef allowedTrust = nil; 58 | SecTrustResultType result; 59 | 60 | allowedCertificate = SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificate); 61 | __Require_Quiet(allowedCertificate != NULL, _out); 62 | 63 | allowedCertificates[0] = allowedCertificate; 64 | tempCertificates = CFArrayCreate(NULL, (const void **)allowedCertificates, 1, NULL); 65 | 66 | policy = SecPolicyCreateBasicX509(); 67 | __Require_noErr_Quiet(SecTrustCreateWithCertificates(tempCertificates, policy, &allowedTrust), _out); 68 | __Require_noErr_Quiet(SecTrustEvaluate(allowedTrust, &result), _out); 69 | 70 | allowedPublicKey = (__bridge_transfer id)SecTrustCopyPublicKey(allowedTrust); 71 | 72 | _out: 73 | if (allowedTrust) { 74 | CFRelease(allowedTrust); 75 | } 76 | 77 | if (policy) { 78 | CFRelease(policy); 79 | } 80 | 81 | if (tempCertificates) { 82 | CFRelease(tempCertificates); 83 | } 84 | 85 | if (allowedCertificate) { 86 | CFRelease(allowedCertificate); 87 | } 88 | 89 | return allowedPublicKey; 90 | } 91 | 92 | static BOOL AFServerTrustIsValid(SecTrustRef serverTrust) { 93 | BOOL isValid = NO; 94 | SecTrustResultType result; 95 | __Require_noErr_Quiet(SecTrustEvaluate(serverTrust, &result), _out); 96 | 97 | isValid = (result == kSecTrustResultUnspecified || result == kSecTrustResultProceed); 98 | 99 | _out: 100 | return isValid; 101 | } 102 | 103 | static NSArray * AFCertificateTrustChainForServerTrust(SecTrustRef serverTrust) { 104 | CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); 105 | NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; 106 | 107 | for (CFIndex i = 0; i < certificateCount; i++) { 108 | SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); 109 | [trustChain addObject:(__bridge_transfer NSData *)SecCertificateCopyData(certificate)]; 110 | } 111 | 112 | return [NSArray arrayWithArray:trustChain]; 113 | } 114 | 115 | static NSArray * AFPublicKeyTrustChainForServerTrust(SecTrustRef serverTrust) { 116 | SecPolicyRef policy = SecPolicyCreateBasicX509(); 117 | CFIndex certificateCount = SecTrustGetCertificateCount(serverTrust); 118 | NSMutableArray *trustChain = [NSMutableArray arrayWithCapacity:(NSUInteger)certificateCount]; 119 | for (CFIndex i = 0; i < certificateCount; i++) { 120 | SecCertificateRef certificate = SecTrustGetCertificateAtIndex(serverTrust, i); 121 | 122 | SecCertificateRef someCertificates[] = {certificate}; 123 | CFArrayRef certificates = CFArrayCreate(NULL, (const void **)someCertificates, 1, NULL); 124 | 125 | SecTrustRef trust; 126 | __Require_noErr_Quiet(SecTrustCreateWithCertificates(certificates, policy, &trust), _out); 127 | 128 | SecTrustResultType result; 129 | __Require_noErr_Quiet(SecTrustEvaluate(trust, &result), _out); 130 | 131 | [trustChain addObject:(__bridge_transfer id)SecTrustCopyPublicKey(trust)]; 132 | 133 | _out: 134 | if (trust) { 135 | CFRelease(trust); 136 | } 137 | 138 | if (certificates) { 139 | CFRelease(certificates); 140 | } 141 | 142 | continue; 143 | } 144 | CFRelease(policy); 145 | 146 | return [NSArray arrayWithArray:trustChain]; 147 | } 148 | 149 | #pragma mark - 150 | 151 | @interface AFSecurityPolicy() 152 | @property (readwrite, nonatomic, assign) AFSSLPinningMode SSLPinningMode; 153 | @property (readwrite, nonatomic, strong) NSArray *pinnedPublicKeys; 154 | @end 155 | 156 | @implementation AFSecurityPolicy 157 | 158 | + (NSArray *)defaultPinnedCertificates { 159 | static NSArray *_defaultPinnedCertificates = nil; 160 | static dispatch_once_t onceToken; 161 | dispatch_once(&onceToken, ^{ 162 | NSBundle *bundle = [NSBundle bundleForClass:[self class]]; 163 | NSArray *paths = [bundle pathsForResourcesOfType:@"cer" inDirectory:@"."]; 164 | 165 | NSMutableArray *certificates = [NSMutableArray arrayWithCapacity:[paths count]]; 166 | for (NSString *path in paths) { 167 | NSData *certificateData = [NSData dataWithContentsOfFile:path]; 168 | [certificates addObject:certificateData]; 169 | } 170 | 171 | _defaultPinnedCertificates = [[NSArray alloc] initWithArray:certificates]; 172 | }); 173 | 174 | return _defaultPinnedCertificates; 175 | } 176 | 177 | + (instancetype)defaultPolicy { 178 | AFSecurityPolicy *securityPolicy = [[self alloc] init]; 179 | securityPolicy.SSLPinningMode = AFSSLPinningModeNone; 180 | 181 | return securityPolicy; 182 | } 183 | 184 | + (instancetype)policyWithPinningMode:(AFSSLPinningMode)pinningMode { 185 | AFSecurityPolicy *securityPolicy = [[self alloc] init]; 186 | securityPolicy.SSLPinningMode = pinningMode; 187 | 188 | [securityPolicy setPinnedCertificates:[self defaultPinnedCertificates]]; 189 | 190 | return securityPolicy; 191 | } 192 | 193 | - (id)init { 194 | self = [super init]; 195 | if (!self) { 196 | return nil; 197 | } 198 | 199 | self.validatesCertificateChain = YES; 200 | self.validatesDomainName = YES; 201 | 202 | return self; 203 | } 204 | 205 | - (void)setPinnedCertificates:(NSArray *)pinnedCertificates { 206 | _pinnedCertificates = pinnedCertificates; 207 | 208 | if (self.pinnedCertificates) { 209 | NSMutableArray *mutablePinnedPublicKeys = [NSMutableArray arrayWithCapacity:[self.pinnedCertificates count]]; 210 | for (NSData *certificate in self.pinnedCertificates) { 211 | id publicKey = AFPublicKeyForCertificate(certificate); 212 | if (!publicKey) { 213 | continue; 214 | } 215 | [mutablePinnedPublicKeys addObject:publicKey]; 216 | } 217 | self.pinnedPublicKeys = [NSArray arrayWithArray:mutablePinnedPublicKeys]; 218 | } else { 219 | self.pinnedPublicKeys = nil; 220 | } 221 | } 222 | 223 | #pragma mark - 224 | 225 | - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust { 226 | return [self evaluateServerTrust:serverTrust forDomain:nil]; 227 | } 228 | 229 | - (BOOL)evaluateServerTrust:(SecTrustRef)serverTrust 230 | forDomain:(NSString *)domain 231 | { 232 | NSMutableArray *policies = [NSMutableArray array]; 233 | if (self.validatesDomainName) { 234 | [policies addObject:(__bridge_transfer id)SecPolicyCreateSSL(true, (__bridge CFStringRef)domain)]; 235 | } else { 236 | [policies addObject:(__bridge_transfer id)SecPolicyCreateBasicX509()]; 237 | } 238 | 239 | SecTrustSetPolicies(serverTrust, (__bridge CFArrayRef)policies); 240 | 241 | if (self.SSLPinningMode == AFSSLPinningModeNone) { 242 | if (self.allowInvalidCertificates || AFServerTrustIsValid(serverTrust)){ 243 | return YES; 244 | } else { 245 | return NO; 246 | } 247 | } else if (!AFServerTrustIsValid(serverTrust) && !self.allowInvalidCertificates) { 248 | return NO; 249 | } 250 | 251 | NSArray *serverCertificates = AFCertificateTrustChainForServerTrust(serverTrust); 252 | switch (self.SSLPinningMode) { 253 | case AFSSLPinningModeNone: 254 | default: 255 | return NO; 256 | case AFSSLPinningModeCertificate: { 257 | NSMutableArray *pinnedCertificates = [NSMutableArray array]; 258 | for (NSData *certificateData in self.pinnedCertificates) { 259 | [pinnedCertificates addObject:(__bridge_transfer id)SecCertificateCreateWithData(NULL, (__bridge CFDataRef)certificateData)]; 260 | } 261 | SecTrustSetAnchorCertificates(serverTrust, (__bridge CFArrayRef)pinnedCertificates); 262 | 263 | if (!AFServerTrustIsValid(serverTrust)) { 264 | return NO; 265 | } 266 | 267 | if (!self.validatesCertificateChain) { 268 | return YES; 269 | } 270 | 271 | NSUInteger trustedCertificateCount = 0; 272 | for (NSData *trustChainCertificate in serverCertificates) { 273 | if ([self.pinnedCertificates containsObject:trustChainCertificate]) { 274 | trustedCertificateCount++; 275 | } 276 | } 277 | 278 | return trustedCertificateCount == [serverCertificates count]; 279 | } 280 | case AFSSLPinningModePublicKey: { 281 | NSUInteger trustedPublicKeyCount = 0; 282 | NSArray *publicKeys = AFPublicKeyTrustChainForServerTrust(serverTrust); 283 | if (!self.validatesCertificateChain && [publicKeys count] > 0) { 284 | publicKeys = @[[publicKeys firstObject]]; 285 | } 286 | 287 | for (id trustChainPublicKey in publicKeys) { 288 | for (id pinnedPublicKey in self.pinnedPublicKeys) { 289 | if (AFSecKeyIsEqualToKey((__bridge SecKeyRef)trustChainPublicKey, (__bridge SecKeyRef)pinnedPublicKey)) { 290 | trustedPublicKeyCount += 1; 291 | } 292 | } 293 | } 294 | 295 | return trustedPublicKeyCount > 0 && ((self.validatesCertificateChain && trustedPublicKeyCount == [serverCertificates count]) || (!self.validatesCertificateChain && trustedPublicKeyCount >= 1)); 296 | } 297 | } 298 | 299 | return NO; 300 | } 301 | 302 | #pragma mark - NSKeyValueObserving 303 | 304 | + (NSSet *)keyPathsForValuesAffectingPinnedPublicKeys { 305 | return [NSSet setWithObject:@"pinnedCertificates"]; 306 | } 307 | 308 | @end 309 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/AFNetworking/AFNetworking/AFURLResponseSerialization.h: -------------------------------------------------------------------------------- 1 | // AFURLResponseSerialization.h 2 | // Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a copy 5 | // of this software and associated documentation files (the "Software"), to deal 6 | // in the Software without restriction, including without limitation the rights 7 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | // copies of the Software, and to permit persons to whom the Software is 9 | // furnished to do so, subject to the following conditions: 10 | // 11 | // The above copyright notice and this permission notice shall be included in 12 | // all copies or substantial portions of the Software. 13 | // 14 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | // THE SOFTWARE. 21 | 22 | #import 23 | #import 24 | 25 | /** 26 | The `AFURLResponseSerialization` protocol is adopted by an object that decodes data into a more useful object representation, according to details in the server response. Response serializers may additionally perform validation on the incoming response and data. 27 | 28 | For example, a JSON response serializer may check for an acceptable status code (`2XX` range) and content type (`application/json`), decoding a valid JSON response into an object. 29 | */ 30 | @protocol AFURLResponseSerialization 31 | 32 | /** 33 | The response object decoded from the data associated with a specified response. 34 | 35 | @param response The response to be processed. 36 | @param data The response data to be decoded. 37 | @param error The error that occurred while attempting to decode the response data. 38 | 39 | @return The object decoded from the specified response data. 40 | */ 41 | - (id)responseObjectForResponse:(NSURLResponse *)response 42 | data:(NSData *)data 43 | error:(NSError *__autoreleasing *)error; 44 | 45 | @end 46 | 47 | #pragma mark - 48 | 49 | /** 50 | `AFHTTPResponseSerializer` conforms to the `AFURLRequestSerialization` & `AFURLResponseSerialization` protocols, offering a concrete base implementation of query string / URL form-encoded parameter serialization and default request headers, as well as response status code and content type validation. 51 | 52 | Any request or response serializer dealing with HTTP is encouraged to subclass `AFHTTPResponseSerializer` in order to ensure consistent default behavior. 53 | */ 54 | @interface AFHTTPResponseSerializer : NSObject 55 | 56 | - (instancetype) init; 57 | 58 | /** 59 | The string encoding used to serialize data received from the server, when no string encoding is specified by the response. `NSUTF8StringEncoding` by default. 60 | */ 61 | @property (nonatomic, assign) NSStringEncoding stringEncoding; 62 | 63 | /** 64 | Creates and returns a serializer with default configuration. 65 | */ 66 | + (instancetype)serializer; 67 | 68 | ///----------------------------------------- 69 | /// @name Configuring Response Serialization 70 | ///----------------------------------------- 71 | 72 | /** 73 | The acceptable HTTP status codes for responses. When non-`nil`, responses with status codes not contained by the set will result in an error during validation. 74 | 75 | See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 76 | */ 77 | @property (nonatomic, copy) NSIndexSet *acceptableStatusCodes; 78 | 79 | /** 80 | The acceptable MIME types for responses. When non-`nil`, responses with a `Content-Type` with MIME types that do not intersect with the set will result in an error during validation. 81 | */ 82 | @property (nonatomic, copy) NSSet *acceptableContentTypes; 83 | 84 | /** 85 | Validates the specified response and data. 86 | 87 | In its base implementation, this method checks for an acceptable status code and content type. Subclasses may wish to add other domain-specific checks. 88 | 89 | @param response The response to be validated. 90 | @param data The data associated with the response. 91 | @param error The error that occurred while attempting to validate the response. 92 | 93 | @return `YES` if the response is valid, otherwise `NO`. 94 | */ 95 | - (BOOL)validateResponse:(NSHTTPURLResponse *)response 96 | data:(NSData *)data 97 | error:(NSError *__autoreleasing *)error; 98 | 99 | @end 100 | 101 | #pragma mark - 102 | 103 | 104 | /** 105 | `AFJSONResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes JSON responses. 106 | 107 | By default, `AFJSONResponseSerializer` accepts the following MIME types, which includes the official standard, `application/json`, as well as other commonly-used types: 108 | 109 | - `application/json` 110 | - `text/json` 111 | - `text/javascript` 112 | */ 113 | @interface AFJSONResponseSerializer : AFHTTPResponseSerializer 114 | 115 | - (instancetype) init; 116 | 117 | /** 118 | Options for reading the response JSON data and creating the Foundation objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. 119 | */ 120 | @property (nonatomic, assign) NSJSONReadingOptions readingOptions; 121 | 122 | /** 123 | Whether to remove keys with `NSNull` values from response JSON. Defaults to `NO`. 124 | */ 125 | @property (nonatomic, assign) BOOL removesKeysWithNullValues; 126 | 127 | /** 128 | Creates and returns a JSON serializer with specified reading and writing options. 129 | 130 | @param readingOptions The specified JSON reading options. 131 | */ 132 | + (instancetype)serializerWithReadingOptions:(NSJSONReadingOptions)readingOptions; 133 | 134 | @end 135 | 136 | #pragma mark - 137 | 138 | /** 139 | `AFXMLParserResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLParser` objects. 140 | 141 | By default, `AFXMLParserResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: 142 | 143 | - `application/xml` 144 | - `text/xml` 145 | */ 146 | @interface AFXMLParserResponseSerializer : AFHTTPResponseSerializer 147 | 148 | @end 149 | 150 | #pragma mark - 151 | 152 | #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED 153 | 154 | /** 155 | `AFXMLDocumentResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. 156 | 157 | By default, `AFXMLDocumentResponseSerializer` accepts the following MIME types, which includes the official standard, `application/xml`, as well as other commonly-used types: 158 | 159 | - `application/xml` 160 | - `text/xml` 161 | */ 162 | @interface AFXMLDocumentResponseSerializer : AFHTTPResponseSerializer 163 | 164 | - (instancetype) init; 165 | 166 | /** 167 | Input and output options specifically intended for `NSXMLDocument` objects. For possible values, see the `NSJSONSerialization` documentation section "NSJSONReadingOptions". `0` by default. 168 | */ 169 | @property (nonatomic, assign) NSUInteger options; 170 | 171 | /** 172 | Creates and returns an XML document serializer with the specified options. 173 | 174 | @param mask The XML document options. 175 | */ 176 | + (instancetype)serializerWithXMLDocumentOptions:(NSUInteger)mask; 177 | 178 | @end 179 | 180 | #endif 181 | 182 | #pragma mark - 183 | 184 | /** 185 | `AFPropertyListResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes XML responses as an `NSXMLDocument` objects. 186 | 187 | By default, `AFPropertyListResponseSerializer` accepts the following MIME types: 188 | 189 | - `application/x-plist` 190 | */ 191 | @interface AFPropertyListResponseSerializer : AFHTTPResponseSerializer 192 | 193 | - (instancetype) init; 194 | 195 | /** 196 | The property list format. Possible values are described in "NSPropertyListFormat". 197 | */ 198 | @property (nonatomic, assign) NSPropertyListFormat format; 199 | 200 | /** 201 | The property list reading options. Possible values are described in "NSPropertyListMutabilityOptions." 202 | */ 203 | @property (nonatomic, assign) NSPropertyListReadOptions readOptions; 204 | 205 | /** 206 | Creates and returns a property list serializer with a specified format, read options, and write options. 207 | 208 | @param format The property list format. 209 | @param readOptions The property list reading options. 210 | */ 211 | + (instancetype)serializerWithFormat:(NSPropertyListFormat)format 212 | readOptions:(NSPropertyListReadOptions)readOptions; 213 | 214 | @end 215 | 216 | #pragma mark - 217 | 218 | /** 219 | `AFImageResponseSerializer` is a subclass of `AFHTTPResponseSerializer` that validates and decodes image responses. 220 | 221 | By default, `AFImageResponseSerializer` accepts the following MIME types, which correspond to the image formats supported by UIImage or NSImage: 222 | 223 | - `image/tiff` 224 | - `image/jpeg` 225 | - `image/gif` 226 | - `image/png` 227 | - `image/ico` 228 | - `image/x-icon` 229 | - `image/bmp` 230 | - `image/x-bmp` 231 | - `image/x-xbitmap` 232 | - `image/x-win-bitmap` 233 | */ 234 | @interface AFImageResponseSerializer : AFHTTPResponseSerializer 235 | 236 | #if defined(__IPHONE_OS_VERSION_MIN_REQUIRED) 237 | /** 238 | The scale factor used when interpreting the image data to construct `responseImage`. Specifying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the size property. This is set to the value of scale of the main screen by default, which automatically scales images for retina displays, for instance. 239 | */ 240 | @property (nonatomic, assign) CGFloat imageScale; 241 | 242 | /** 243 | Whether to automatically inflate response image data for compressed formats (such as PNG or JPEG). Enabling this can significantly improve drawing performance on iOS when used with `setCompletionBlockWithSuccess:failure:`, as it allows a bitmap representation to be constructed in the background rather than on the main thread. `YES` by default. 244 | */ 245 | @property (nonatomic, assign) BOOL automaticallyInflatesResponseImage; 246 | #endif 247 | 248 | @end 249 | 250 | #pragma mark - 251 | 252 | /** 253 | `AFCompoundSerializer` is a subclass of `AFHTTPResponseSerializer` that delegates the response serialization to the first `AFHTTPResponseSerializer` object that returns an object for `responseObjectForResponse:data:error:`, falling back on the default behavior of `AFHTTPResponseSerializer`. This is useful for supporting multiple potential types and structures of server responses with a single serializer. 254 | */ 255 | @interface AFCompoundResponseSerializer : AFHTTPResponseSerializer 256 | 257 | /** 258 | The component response serializers. 259 | */ 260 | @property (readonly, nonatomic, copy) NSArray *responseSerializers; 261 | 262 | /** 263 | Creates and returns a compound serializer comprised of the specified response serializers. 264 | 265 | @warning Each response serializer specified must be a subclass of `AFHTTPResponseSerializer`, and response to `-validateResponse:data:error:`. 266 | */ 267 | + (instancetype)compoundSerializerWithResponseSerializers:(NSArray *)responseSerializers; 268 | 269 | @end 270 | 271 | ///---------------- 272 | /// @name Constants 273 | ///---------------- 274 | 275 | /** 276 | ## Error Domains 277 | 278 | The following error domain is predefined. 279 | 280 | - `NSString * const AFURLResponseSerializationErrorDomain` 281 | 282 | ### Constants 283 | 284 | `AFURLResponseSerializationErrorDomain` 285 | AFURLResponseSerializer errors. Error codes for `AFURLResponseSerializationErrorDomain` correspond to codes in `NSURLErrorDomain`. 286 | */ 287 | extern NSString * const AFURLResponseSerializationErrorDomain; 288 | 289 | /** 290 | ## User info dictionary keys 291 | 292 | These keys may exist in the user info dictionary, in addition to those defined for NSError. 293 | 294 | - `NSString * const AFNetworkingOperationFailingURLResponseErrorKey` 295 | - `NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey` 296 | 297 | ### Constants 298 | 299 | `AFNetworkingOperationFailingURLResponseErrorKey` 300 | The corresponding value is an `NSURLResponse` containing the response of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. 301 | 302 | `AFNetworkingOperationFailingURLResponseDataErrorKey` 303 | The corresponding value is an `NSData` containing the original data of the operation associated with an error. This key is only present in the `AFURLResponseSerializationErrorDomain`. 304 | */ 305 | extern NSString * const AFNetworkingOperationFailingURLResponseErrorKey; 306 | 307 | extern NSString * const AFNetworkingOperationFailingURLResponseDataErrorKey; 308 | 309 | 310 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/AFNetworking/LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011–2015 Alamofire Software Foundation (http://alamofire.org/) 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/CHCSVParser/LICENSE.txt: -------------------------------------------------------------------------------- 1 | CHCSVParser 2 | 3 | Copyright (c) 2014 Dave DeLong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/CHCSVParser/README.markdown: -------------------------------------------------------------------------------- 1 | #CHCSVParser 2 | 3 | `CHCSVParser` is an Objective-C parser for CSV files. 4 | 5 | ##Supported Platforms 6 | 7 | - Mac OS X 10.7+ 8 | - iOS 6+ 9 | 10 | ##Usage 11 | 12 | In order to use `CHCSVParser`, you'll need to include the following two files in your project: 13 | 14 | - `CHCSVParser.h` 15 | - `CHCSVParser.m` 16 | 17 | `CHCSVParser` requires ARC. 18 | 19 | ###Parsing 20 | A `CHCSVParser` works very similarly to an `NSXMLParser`, in that it synchronously parses the data and invokes delegate callback methods to let you know that it has found a field, or has finished reading a line, or has encountered a syntax error. 21 | 22 | A `CHCSVParser` can be created in one of three ways: 23 | 24 | 1. With a URL to a file 25 | 2. With the contents of an `NSString` 26 | 3. With an `NSInputStream` 27 | 28 | `CHCSVParser` can be configured to parse other "character-separated" file formats, such as "TSV" (tab-separated). You can specify the delimiter of the parser during initialization. The delimiter can only be one character in length, and cannot be any newline character or `"`. Additionally, depending on which options you set on the parser, you may not use `#`, `\`, or `=` as the delimiter either 29 | 30 | By default, `CHCSVParser` will not sanitize the output of the fields; in other words, individual fields will be returned exactly as they are found in the CSV file. However, if you wish the fields to be cleaned (surrounding double quotes stripped, characters unescaped, etc), you can specify this by setting the `sanitizesFields` property to `YES`. 31 | 32 | `CHCSVParser` has other properties to alter the parsing behavior: 33 | 34 | - `recognizesBackslashesAsEscapes` allows you to parse delimited files where special characters (the delimiter, newlines, etc) are escaped using a backslash. When this option is enabled, you may not use a backslash as a delimiter. This option is disabled by default. 35 | 36 | - `recognizesComments` will skip parsing fields that being with an octothorpe (`#`). These fields are reported to the parser delegate as comments, and comments are terminated by an unescaped newline character. This option is disabled by default. 37 | 38 | - `recognizesLeadingEqualSign` allows quoted fields to begin with an `=`. Some programs use a leading equal sign to indicate that the contents of the field should be interpreted explicitly, and things like insignificant digits should not be removed. This option is disabled by default. 39 | 40 | ###Writing 41 | A `CHCSVWriter` has several methods for constructing CSV files: 42 | 43 | `-writeField:` accepts an object and writes its `-description` (after being properly escaped) out to the CSV file. It will also write field separator (`,`) if necessary. You may pass an empty string (`@""`) or `nil` to write an empty field. 44 | 45 | `-finishLine` is used to terminate the current CSV line. If you do not invoke `-finishLine`, then all of your CSV fields will be on a single line. 46 | 47 | `-writeLineOfFields:` accepts an array of objects, sends each one to `-writeField:`, and then invokes `-finishLine`. 48 | 49 | `-writeComment:` accepts a string and writes it out to the file as a CSV-style comment. 50 | 51 | If you wish to write CSV directly into an `NSString`, you should create an `NSOutputStream` for writing to memory and use that as the output stream of the `CHCSVWriter`. For an example of how to do this, see the `-[NSArray(CHCSVAdditions) CSVString]` method. 52 | 53 | Like `CHCSVParser`, `CHCSVWriter` can be customized with a delimiter other than `,` during initialization. 54 | 55 | ###Convenience Methods 56 | 57 | There are a couple of category methods on `NSArray` and `NSString` to simplify the common reading and writing of delimited files. 58 | 59 | In addition, the convenience APIs allow for additional parsing options beyond what is provided by `CHCSVParser`. When you specify the `CHCSVParserOptionUsesFirstLineAsKeys` option, parsing will return an array of `CHCSVOrderedDictionary` instances, instead of an array of arrays of strings. 60 | 61 | A `CHCSVOrderedDictionary` is an `NSDictionary` subclass that maintains a specific order to its key-value pairs, and allows you to look up keys and values by index. 62 | 63 | 64 | ##Data Encoding 65 | `CHCSVParser` relies on knowing the encoding of the content. It should work with pretty much any kind of file encoding, if you can provide what that encoding is. If you do not know the encoding of the file, then `CHCSVParser` can make a naïve guess. `CHCSVParser` will try to guess the encoding of the file from among these options: 66 | 67 | - MacOS Roman (`NSMacOSRomanStringEncoding`; the default/fallback encoding) 68 | - UTF-8 (`NSUTF8StringEncoding`) 69 | - UTF-16BE (`NSUTF16BigEndianStringEncoding`) 70 | - UTF-16LE (`NSUTF16LittleEndianStringEncoding`) 71 | - UTF-32BE (`NSUTF32BigEndianStringEncoding`) 72 | - UTF-32LE (`NSUTF32LittleEndianStringEncoding`) 73 | - ISO 2022-KR (`kCFStringEncodingISO_2022_KR`) 74 | 75 | ##Performance 76 | `CHCSVParser` is conscious of low-memory environments, such as the iPhone or iPad. It can safely parse very large CSV files, because it only loads portions of the file into memory at a single time. 77 | 78 | ##Credits & Contributors 79 | 80 | `CHCSVParser` was written by [Dave DeLong][1] and has accepted patches from [several other contributors](https://github.com/davedelong/CHCSVParser/graphs/contributors). 81 | 82 | `CHCSVParser` uses code to discover file encoding that was provided by [Rainer Brockerhoff][2]. 83 | 84 | [1]: http://davedelong.com 85 | [2]: http://brockerhoff.net 86 | 87 | ##License 88 | 89 | `CHCSVParser` is licensed under the MIT license, which is reproduced in its entirety here: 90 | 91 | 92 | >Copyright (c) 2014 Dave DeLong 93 | > 94 | >Permission is hereby granted, free of charge, to any person obtaining a copy 95 | >of this software and associated documentation files (the "Software"), to deal 96 | >in the Software without restriction, including without limitation the rights 97 | >to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 98 | >copies of the Software, and to permit persons to whom the Software is 99 | >furnished to do so, subject to the following conditions: 100 | > 101 | >The above copyright notice and this permission notice shall be included in 102 | >all copies or substantial portions of the Software. 103 | > 104 | >THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 105 | >IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 106 | >FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 107 | >AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 108 | >LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 109 | >OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 110 | >THE SOFTWARE. 111 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/DJProgressHUD_OSX/DJProgressHUD/DJActivityIndicator.h: -------------------------------------------------------------------------------- 1 | // 2 | // DJProgressIndicator.h 3 | // Playground 4 | // 5 | // Created by Daniel Jackson on 5/11/14. 6 | // Copyright (c) 2014 Daniel Jackson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DJActivityIndicator : NSView 12 | 13 | @property BOOL isAnimating; 14 | 15 | - (void)setColor:(NSColor *)value; 16 | - (void)setBackgroundColor:(NSColor *)value; 17 | 18 | - (void)stopAnimation:(id)sender; 19 | - (void)startAnimation:(id)sender; 20 | 21 | @end 22 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/DJProgressHUD_OSX/DJProgressHUD/DJActivityIndicator.m: -------------------------------------------------------------------------------- 1 | // 2 | // DJProgressIndicator.m 3 | // Playground 4 | // 5 | // Created by Daniel Jackson on 5/11/14. 6 | // Copyright (c) 2014 Daniel Jackson. All rights reserved. 7 | // 8 | 9 | #import "DJActivityIndicator.h" 10 | 11 | #define kAlphaWhenStopped 0.15 12 | #define kFadeMultiplier 0.85 13 | 14 | @interface DJActivityIndicator () 15 | { 16 | int position; 17 | NSMutableArray* finColors; 18 | 19 | BOOL isFadingOut; 20 | NSTimer* animationTimer; 21 | 22 | NSColor* foreColor; 23 | NSColor* backColor; 24 | } 25 | @end 26 | 27 | @implementation DJActivityIndicator 28 | 29 | - (id)initWithFrame:(NSRect)frame 30 | { 31 | self = [super initWithFrame:frame]; 32 | if (self) { 33 | position = 0; 34 | int numFins = 12; 35 | 36 | finColors = [[NSMutableArray alloc] initWithCapacity:numFins]; 37 | 38 | _isAnimating = NO; 39 | isFadingOut = NO; 40 | 41 | foreColor = [NSColor blackColor]; 42 | backColor = [NSColor clearColor]; 43 | 44 | for(int i=0; i= size.height) 59 | theMaxSize = size.height; 60 | else 61 | theMaxSize = size.width; 62 | 63 | [backColor set]; 64 | [NSBezierPath fillRect:[self bounds]]; 65 | 66 | CGContextRef currentContext = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort]; 67 | [NSGraphicsContext saveGraphicsState]; 68 | 69 | CGContextTranslateCTM(currentContext,[self bounds].size.width/2,[self bounds].size.height/2); 70 | 71 | NSBezierPath *path = [[NSBezierPath alloc] init]; 72 | CGFloat lineWidth = 0.0859375 * theMaxSize; 73 | CGFloat lineStart = 0.234375 * theMaxSize; 74 | CGFloat lineEnd = 0.421875 * theMaxSize; 75 | [path setLineWidth:lineWidth]; 76 | [path setLineCapStyle:NSRoundLineCapStyle]; 77 | [path moveToPoint:NSMakePoint(0,lineStart)]; 78 | [path lineToPoint:NSMakePoint(0,lineEnd)]; 79 | 80 | for (int i=0; i 0) { 136 | position--; 137 | } 138 | else { 139 | position = (int)finColors.count - 1; 140 | } 141 | 142 | CGFloat minAlpha = kAlphaWhenStopped; 143 | for (int i=0; i 0.01) { 155 | done = NO; 156 | break; 157 | } 158 | } 159 | if (done) { 160 | [self actuallyStopAnimation]; 161 | } 162 | } 163 | else { 164 | finColors[position] = foreColor; 165 | } 166 | 167 | [self setNeedsDisplay:YES]; 168 | 169 | 170 | } 171 | 172 | - (void)actuallyStartAnimation 173 | { 174 | [self actuallyStopAnimation]; 175 | 176 | _isAnimating = YES; 177 | isFadingOut = NO; 178 | 179 | position = 1; 180 | 181 | animationTimer = [NSTimer timerWithTimeInterval:(NSTimeInterval)0.05 182 | target:self 183 | selector:@selector(updateFrame:) 184 | userInfo:nil 185 | repeats:YES]; 186 | 187 | [[NSRunLoop currentRunLoop] addTimer:animationTimer forMode:NSRunLoopCommonModes]; 188 | [[NSRunLoop currentRunLoop] addTimer:animationTimer forMode:NSDefaultRunLoopMode]; 189 | [[NSRunLoop currentRunLoop] addTimer:animationTimer forMode:NSEventTrackingRunLoopMode]; 190 | } 191 | 192 | - (void)actuallyStopAnimation 193 | { 194 | _isAnimating = NO; 195 | isFadingOut = NO; 196 | 197 | if (animationTimer) { 198 | // we were using timer-based animation 199 | [animationTimer invalidate]; 200 | animationTimer = nil; 201 | } 202 | //[self setNeedsDisplay:YES]; 203 | } 204 | 205 | 206 | 207 | 208 | @end 209 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/DJProgressHUD_OSX/DJProgressHUD/DJBezierPath.h: -------------------------------------------------------------------------------- 1 | // 2 | // CPBezierPath.h 3 | // Cloud Play OSX 4 | // 5 | // Created by Daniel Jackson on 5/11/14. 6 | // Copyright (c) 2014 Daniel Jackson. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface DJBezierPath : NSBezierPath 12 | 13 | - (CGPathRef)quartzPath; 14 | 15 | @end 16 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/DJProgressHUD_OSX/DJProgressHUD/DJBezierPath.m: -------------------------------------------------------------------------------- 1 | // 2 | // CPBezierPath.m 3 | // Cloud Play OSX 4 | // 5 | // Created by Daniel Jackson on 5/11/14. 6 | // Copyright (c) 2014 Daniel Jackson. All rights reserved. 7 | // 8 | 9 | #import "DJBezierPath.h" 10 | 11 | @implementation DJBezierPath 12 | 13 | - (CGPathRef)quartzPath 14 | { 15 | int i, numElements; 16 | 17 | // Need to begin a path here. 18 | CGPathRef immutablePath = NULL; 19 | 20 | // Then draw the path elements. 21 | numElements = (int)[self elementCount]; 22 | if (numElements > 0) 23 | { 24 | CGMutablePathRef path = CGPathCreateMutable(); 25 | NSPoint points[3]; 26 | BOOL didClosePath = YES; 27 | 28 | for (i = 0; i < numElements; i++) 29 | { 30 | switch ([self elementAtIndex:i associatedPoints:points]) 31 | { 32 | case NSMoveToBezierPathElement: 33 | CGPathMoveToPoint(path, NULL, points[0].x, points[0].y); 34 | break; 35 | 36 | case NSLineToBezierPathElement: 37 | CGPathAddLineToPoint(path, NULL, points[0].x, points[0].y); 38 | didClosePath = NO; 39 | break; 40 | 41 | case NSCurveToBezierPathElement: 42 | CGPathAddCurveToPoint(path, NULL, points[0].x, points[0].y, 43 | points[1].x, points[1].y, 44 | points[2].x, points[2].y); 45 | didClosePath = NO; 46 | break; 47 | 48 | case NSClosePathBezierPathElement: 49 | CGPathCloseSubpath(path); 50 | didClosePath = YES; 51 | break; 52 | } 53 | } 54 | 55 | // Be sure the path is closed or Quartz may not do valid hit detection. 56 | if (!didClosePath) 57 | CGPathCloseSubpath(path); 58 | 59 | immutablePath = CGPathCreateCopy(path); 60 | CGPathRelease(path); 61 | } 62 | 63 | return immutablePath; 64 | } 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/DJProgressHUD_OSX/DJProgressHUD/DJProgressHUD.h: -------------------------------------------------------------------------------- 1 | // 2 | // CPProgressView.h 3 | // Cloud Play OSX 4 | // 5 | // Created by Daniel Jackson on 4/22/14. 6 | // Copyright (c) 2014 Daniel Jackson. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | /*#define popupWidth 120 13 | #define popupHeight 120 14 | 15 | #define popupAlpha 0.9 16 | 17 | #define xOffset 0 18 | #define yOffset 0 19 | 20 | #define indicatorSize 40*/ 21 | 22 | @class DJProgressIndicator; 23 | @class DJActivityIndicator; 24 | 25 | @interface DJProgressHUD : NSView 26 | 27 | + (void)setBackgroundAlpha:(CGFloat)bgAlph disableActions:(BOOL)disActions; 28 | 29 | + (void)showStatus:(NSString*)status FromView:(NSView*)view; 30 | + (void)showProgress:(CGFloat)progress withStatus:(NSString*)status FromView:(NSView*)view; 31 | 32 | + (void)dismiss; 33 | 34 | //+ (void)popActivity; 35 | @property (nonatomic) BOOL keepActivityCount; 36 | 37 | @property (nonatomic, readonly) BOOL animatingDismiss; 38 | @property (nonatomic, readonly) BOOL animatingShow; 39 | @property (nonatomic, readonly) BOOL displaying; 40 | @property (nonatomic, readonly) NSUInteger activityCount; 41 | 42 | /* 43 | -----Customization---- 44 | 45 | Make all changes to [CPProgressView instance] 46 | Prior to showing popup, call -(void)orientPopup 47 | */ 48 | 49 | #define pMaxWidth1 250 50 | #define pMaxHeight1 200 51 | 52 | 53 | 54 | //General Popup Values 55 | @property (nonatomic) CGVector pOffset; 56 | @property (nonatomic) CGFloat pAlpha; 57 | 58 | //Padding 59 | @property (nonatomic) CGFloat pPadding; 60 | 61 | @property (nonatomic) CGSize indicatorSize; 62 | @property (nonatomic) CGVector indicatorOffset; 63 | @property (nonatomic) CGSize labelSize; 64 | @property (nonatomic) CGVector labelOffset; 65 | 66 | @end 67 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/DJProgressHUD_OSX/DJProgressHUD/DJProgressHUD.m: -------------------------------------------------------------------------------- 1 | // 2 | // CPProgressView.m 3 | // Cloud Play OSX 4 | // 5 | // Created by Daniel Jackson on 4/22/14. 6 | // Copyright (c) 2014 Daniel Jackson. All rights reserved. 7 | // 8 | 9 | #import "DJProgressHUD.h" 10 | #import "DJActivityIndicator.h" 11 | #import "DJProgressIndicator.h" 12 | 13 | #define DEBUG_LOGS 1 14 | 15 | typedef void (^CompletionHander)(void); 16 | 17 | @interface DJProgressHUD () 18 | { 19 | NSView* parentView; 20 | 21 | CGSize pSize; //This is set automatically based on the content 22 | DJActivityIndicator* activityIndicator; 23 | DJProgressIndicator* progressIndicator; 24 | NSTextField* label; 25 | 26 | NSButton* backgroundMask; 27 | NSView* MainHUD; 28 | } 29 | 30 | @property CGFloat backgroundAlpha; 31 | @property BOOL actionsEnabled; 32 | 33 | @end 34 | 35 | @implementation DJProgressHUD 36 | 37 | #pragma mark - 38 | #pragma mark Class Methods 39 | 40 | + (void)showStatus:(NSString*)status FromView:(NSView*)view 41 | { 42 | [[self instance] showStatus:status FromView:view]; 43 | } 44 | 45 | + (void)setBackgroundAlpha:(CGFloat)bgAlph disableActions:(BOOL)disActions 46 | { 47 | DJProgressHUD* theHUD = [self instance]; 48 | theHUD.backgroundAlpha = bgAlph; 49 | 50 | [theHUD setBackground]; 51 | 52 | } 53 | 54 | + (void)showProgress:(CGFloat)progress withStatus:(NSString*)status FromView:(NSView*)view 55 | { 56 | [[self instance] showProgress:progress withStatus:status FromView:view]; 57 | } 58 | 59 | + (void)dismiss 60 | { 61 | if([[self instance] keepActivityCount]) { 62 | [[self instance] popActivity]; 63 | } 64 | else { 65 | [[self instance] hideViewAnimated]; 66 | } 67 | } 68 | 69 | + (void)popActivity 70 | { 71 | [[self instance] popActivity]; 72 | } 73 | 74 | #pragma mark - 75 | #pragma mark Master Methods 76 | 77 | - (void)showStatus:(NSString*)status FromView:(NSView*)view 78 | { 79 | parentView = view; 80 | 81 | label.stringValue = status; 82 | 83 | [activityIndicator setHidden:FALSE]; 84 | [progressIndicator setHidden:TRUE]; 85 | [activityIndicator startAnimation:nil]; 86 | 87 | if(![self displaying]) 88 | [self showViewAnimated]; 89 | else 90 | [self replaceViewQuick]; 91 | } 92 | 93 | - (void)addMask { 94 | 95 | [backgroundMask removeFromSuperview]; 96 | [backgroundMask setFrame:parentView.frame]; 97 | [backgroundMask setEnabled:!_actionsEnabled]; 98 | if(!backgroundMask.wantsLayer) { 99 | CALayer* layer = [CALayer layer]; 100 | [backgroundMask setLayer:layer]; 101 | } 102 | [backgroundMask.layer setOpacity:0.0]; 103 | [parentView addSubview:backgroundMask positioned:NSWindowAbove relativeTo:self]; 104 | } 105 | 106 | - (void)removeMask { 107 | 108 | [backgroundMask removeFromSuperview]; 109 | // [backgroundMask.animator.layer setOpacity:0.0]; 110 | } 111 | 112 | - (void)showProgress:(CGFloat)progress withStatus:(NSString*)status FromView:(NSView*)view 113 | { 114 | parentView = view; 115 | 116 | label.stringValue = status; 117 | 118 | [activityIndicator setHidden:TRUE]; 119 | [progressIndicator setHidden:FALSE]; 120 | [activityIndicator stopAnimation:nil]; 121 | [progressIndicator showProgress:progress]; 122 | 123 | if(![self displaying]) 124 | [self showViewAnimated]; 125 | else 126 | [self replaceViewQuick]; 127 | } 128 | 129 | #pragma mark - 130 | #pragma mark Instance Methods 131 | 132 | -(void)replaceViewQuick 133 | { 134 | [self beginShowView]; 135 | [MainHUD.layer setOpacity:_pAlpha]; 136 | } 137 | 138 | - (void)beginShowView 139 | { 140 | [self updateLayout]; 141 | 142 | if(!self.superview) [parentView addSubview:self]; 143 | [self.layer setFrame:parentView.frame]; 144 | [self addSubview:MainHUD]; 145 | NSRect size = [self getCenterWithinRect:parentView.frame scale:1.0]; 146 | [MainHUD.layer setFrame:size]; 147 | 148 | _displaying = true; 149 | _activityCount++; 150 | 151 | [self addMask]; 152 | 153 | [activityIndicator.layer setOpacity:1.0]; 154 | [progressIndicator.layer setOpacity:1.0]; 155 | [label.layer setOpacity:1.0]; 156 | } 157 | 158 | -(void)finishHideView 159 | { 160 | [MainHUD removeFromSuperview]; 161 | [self removeFromSuperview]; 162 | parentView = nil; 163 | _displaying = false; 164 | 165 | [self removeMask]; 166 | 167 | [activityIndicator stopAnimation:nil]; 168 | } 169 | 170 | - (void)showViewAnimated 171 | { 172 | if(![parentView wantsLayer]) 173 | { 174 | [parentView setWantsLayer:TRUE]; 175 | [parentView setLayer:[CALayer layer]]; 176 | } 177 | if(![MainHUD wantsLayer]) 178 | { 179 | [MainHUD setWantsLayer:TRUE]; 180 | [MainHUD setLayer:[CALayer layer]]; 181 | } 182 | 183 | [self beginShowView]; 184 | MainHUD.layer.opacity = 0.0; 185 | 186 | NSRect sizeBefore = [self getCenterWithinRect:parentView.frame scale:0.75]; 187 | [MainHUD.layer setFrame:sizeBefore]; 188 | 189 | _animatingShow = true; 190 | 191 | [CATransaction flush]; 192 | [CATransaction begin]; 193 | [CATransaction setValue:[NSNumber numberWithFloat:0.15f] 194 | forKey:kCATransactionAnimationDuration]; 195 | [CATransaction setCompletionBlock:^{ 196 | _animatingShow = false; 197 | }]; 198 | 199 | [MainHUD.layer setFrame:[self getCenterWithinRect:parentView.frame scale:1.0]]; 200 | [MainHUD.layer setOpacity:_pAlpha]; 201 | [CATransaction commit]; 202 | 203 | [self setNeedsDisplay:TRUE]; 204 | } 205 | 206 | - (void)hideViewAnimated 207 | { 208 | if(![parentView wantsLayer]) 209 | { 210 | [parentView setWantsLayer:TRUE]; 211 | [parentView setLayer:[CALayer layer]]; 212 | } 213 | if(![MainHUD wantsLayer]) 214 | { 215 | [MainHUD setWantsLayer:TRUE]; 216 | [MainHUD setLayer:[CALayer layer]]; 217 | } 218 | 219 | NSRect newSize = [self getCenterWithinRect:parentView.frame scale:0.75]; 220 | 221 | //The ring doesnt resize easily. Clear it. 222 | [progressIndicator clear]; 223 | 224 | _animatingDismiss = true; 225 | _animatingShow = true; 226 | 227 | [CATransaction flush]; 228 | [CATransaction begin]; 229 | [CATransaction setValue:[NSNumber numberWithFloat:0.15f] forKey:kCATransactionAnimationDuration]; 230 | [CATransaction setCompletionBlock:^{ 231 | _animatingShow = false; 232 | if(MainHUD.layer.opacity == 0.0) 233 | { 234 | [self finishHideView]; 235 | //[self updateLayout]; 236 | [MainHUD addSubview:progressIndicator]; 237 | } 238 | }]; 239 | [activityIndicator.layer setOpacity:0.0]; 240 | [label.layer setOpacity:0.0]; 241 | [MainHUD.layer setFrame:newSize]; 242 | [MainHUD.layer setOpacity:0.0]; 243 | [CATransaction commit]; 244 | 245 | [self setNeedsDisplay:TRUE]; 246 | } 247 | 248 | - (void)popActivity 249 | { 250 | _activityCount--; 251 | if(_activityCount == 0) 252 | [self hideViewAnimated]; 253 | } 254 | 255 | #pragma mark - 256 | #pragma mark Laying It Out 257 | 258 | - (void)updateLayout 259 | { 260 | [self setBackground]; 261 | 262 | CGSize maxContentSize = CGSizeMake(pMaxWidth1-(_pPadding*2), pMaxHeight1-(_pPadding*2)); 263 | CGSize minContentSize = CGSizeMake(_indicatorSize.width, _indicatorSize.height); 264 | 265 | CGFloat stringWidth = [label.stringValue sizeWithAttributes:@{ NSFontAttributeName : label.font }].width + 5; 266 | float stringHeight = [self heightForString:label.stringValue font:label.font width:maxContentSize.width] + 8; 267 | 268 | if(label.stringValue == nil || label.stringValue.length == 0) 269 | stringHeight = 0; 270 | 271 | stringWidth = (stringWidth > minContentSize.width) ? stringWidth : minContentSize.width; 272 | if(stringWidth > maxContentSize.width) 273 | stringWidth = maxContentSize.width; 274 | 275 | CGFloat maxStringHeight = maxContentSize.height-_indicatorSize.height-(_pPadding+(_pPadding/2)); 276 | stringHeight = (stringHeight > maxStringHeight) ? maxStringHeight : stringHeight; 277 | 278 | CGFloat popupWidth = stringWidth+(_pPadding*2); 279 | 280 | CGFloat lW = stringWidth; 281 | CGFloat lH = stringHeight; 282 | CGFloat lX = _pPadding; 283 | CGFloat lY = (stringHeight == 0) ? 0 : _pPadding; 284 | [label setFrame:NSMakeRect(lX, lY, lW, lH)]; 285 | 286 | CGFloat spaceBetween = (stringHeight != 0) ? _pPadding/3 : _pPadding; 287 | 288 | CGFloat iW = _indicatorSize.width; 289 | CGFloat iH = _indicatorSize.height; 290 | CGFloat iX = ((lW+(_pPadding*2))/2)-(iW/2); //center it 291 | CGFloat iY = lY+lH+(spaceBetween); 292 | NSRect indicatorRect = NSMakeRect(iX, iY, iW, iH); 293 | activityIndicator.frame = progressIndicator.frame = indicatorRect; 294 | 295 | CGFloat spaceOnTop = (stringHeight != 0) ? _pPadding/3 : 0; 296 | 297 | [progressIndicator setRingThickness:6]; 298 | [progressIndicator sizeToFit]; 299 | [activityIndicator setColor:[NSColor whiteColor]]; 300 | 301 | pSize.width = popupWidth; 302 | pSize.height = iY+iH+_pPadding+spaceOnTop;//+(_pPadding/2); 303 | 304 | [self setAutoresizesSubviews:YES]; 305 | [MainHUD setAutoresizesSubviews:YES]; 306 | 307 | [self setNeedsDisplay:TRUE]; 308 | [MainHUD setNeedsDisplay:TRUE]; 309 | } 310 | 311 | - (void)setBackground 312 | { 313 | CGColorRef bgcolor = CGColorCreateGenericRGB(0.05, 0.05, 0.05, _pAlpha); 314 | if(![MainHUD wantsLayer]) 315 | { 316 | CALayer* bgLayer = [CALayer layer]; 317 | [bgLayer setBackgroundColor:bgcolor]; 318 | [bgLayer setCornerRadius:15.0]; 319 | [MainHUD setWantsLayer:TRUE]; 320 | [MainHUD setLayer:bgLayer]; 321 | } 322 | else { 323 | [MainHUD.layer setBackgroundColor:bgcolor]; 324 | [MainHUD.layer setCornerRadius:15.0]; 325 | } 326 | 327 | if(![self layer]) { 328 | CALayer* bgLayer = [CALayer layer]; 329 | [self setLayer:bgLayer]; 330 | [self setWantsLayer:TRUE]; 331 | } 332 | 333 | [self.layer setBackgroundColor:CGColorCreateGenericRGB(0, 0, 0, _backgroundAlpha)]; 334 | 335 | [self setNeedsDisplay:TRUE]; 336 | } 337 | 338 | #pragma mark - 339 | #pragma mark Other 340 | 341 | -(CGFloat) heightForString:(NSString *)myString font:(NSFont*) myFont width:(CGFloat)myWidth 342 | { 343 | NSTextStorage *textStorage = [[NSTextStorage alloc] initWithString:myString]; 344 | NSTextContainer *textContainer = [[NSTextContainer alloc] initWithContainerSize:NSMakeSize(myWidth, FLT_MAX)]; 345 | ; 346 | NSLayoutManager *layoutManager = [[NSLayoutManager alloc] init]; 347 | [layoutManager addTextContainer:textContainer]; 348 | [textStorage addLayoutManager:layoutManager]; 349 | [textStorage addAttribute:NSFontAttributeName value:myFont 350 | range:NSMakeRange(0, [textStorage length])]; 351 | [textContainer setLineFragmentPadding:0.0]; 352 | 353 | (void) [layoutManager glyphRangeForTextContainer:textContainer]; 354 | return [layoutManager 355 | usedRectForTextContainer:textContainer].size.height; 356 | } 357 | 358 | - (NSRect)getCenterWithinRect:(NSRect)parentFrame scale:(CGFloat)scale 359 | { 360 | NSRect result; 361 | CGFloat newWidth = pSize.width*scale; 362 | CGFloat newHeight = pSize.height*scale; 363 | result.origin.x = parentFrame.size.width/2 - newWidth/2 + _pOffset.dx; 364 | result.origin.y = parentFrame.size.height/2 - newHeight/2 + _pOffset.dy; 365 | result.size.width = newWidth; 366 | result.size.height = newHeight; 367 | 368 | return result; 369 | } 370 | 371 | #pragma mark - 372 | 373 | - (void)initializePopup 374 | { 375 | self.autoresizingMask = NSViewMaxXMargin | NSViewMaxYMargin | NSViewMinXMargin | NSViewMinYMargin; 376 | MainHUD.autoresizingMask = NSViewMaxXMargin | NSViewMaxYMargin | NSViewMinXMargin | NSViewMinYMargin; 377 | 378 | MainHUD = [[NSView alloc] init]; 379 | activityIndicator = [[DJActivityIndicator alloc] init]; 380 | progressIndicator = [[DJProgressIndicator alloc] init]; 381 | backgroundMask = [[NSButton alloc] init]; 382 | label = [[NSTextField alloc] init]; 383 | 384 | [self addSubview:MainHUD]; 385 | [MainHUD addSubview:label]; 386 | [MainHUD addSubview:activityIndicator]; 387 | [MainHUD addSubview:progressIndicator]; 388 | 389 | //----DEFAULT VALUES---- 390 | 391 | _backgroundAlpha = 0.4; 392 | _actionsEnabled = FALSE; 393 | 394 | _pOffset = CGVectorMake(0, 0); 395 | _pAlpha = 0.9; 396 | _pPadding = 10; 397 | 398 | _indicatorSize = CGSizeMake(40, 40); 399 | _indicatorOffset = CGVectorMake(0, 0); 400 | 401 | _indicatorSize = CGSizeMake(40, 40); 402 | _indicatorOffset = CGVectorMake(0, 0); 403 | 404 | [label setBezeled:NO]; 405 | [label setDrawsBackground:NO]; 406 | [label setEditable:NO]; 407 | [label setSelectable:NO]; 408 | 409 | label.font = [NSFont boldSystemFontOfSize:12.0]; 410 | [label setTextColor:[NSColor colorWithCalibratedWhite:1.0 alpha:0.85]]; 411 | } 412 | 413 | + (DJProgressHUD *) instance 414 | { 415 | static dispatch_once_t once; 416 | static DJProgressHUD *sharedView; 417 | dispatch_once(&once, ^ { 418 | sharedView = [[self alloc] init]; 419 | [sharedView initializePopup]; 420 | }); 421 | 422 | return sharedView; 423 | } 424 | 425 | @end 426 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/DJProgressHUD_OSX/DJProgressHUD/DJProgressIndicator.h: -------------------------------------------------------------------------------- 1 | // 2 | // DJProgressIndicator.h 3 | // Playground 4 | // 5 | // Created by Daniel Jackson on 5/11/14. 6 | // Copyright (c) 2014 Daniel Jackson. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface DJProgressIndicator : NSView 13 | 14 | @property (nonatomic,readonly) CGFloat currentProgress; 15 | 16 | - (void)setBackgroundColor:(NSColor *)value; 17 | - (void)setRingColor:(NSColor *)value backgroundRingColor:(NSColor*)value2; 18 | - (void)setRingThickness:(CGFloat)thick; 19 | - (void)setRingRadius:(CGFloat)radius; 20 | 21 | - (void)showProgress:(float)progress; 22 | 23 | -(void)sizeToFit; 24 | 25 | -(void)clear; 26 | 27 | @property (nonatomic, readonly) CGFloat ringRadius; 28 | @property (nonatomic, readonly) CGFloat ringThickness; 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/DJProgressHUD_OSX/DJProgressHUD/DJProgressIndicator.m: -------------------------------------------------------------------------------- 1 | // 2 | // DJProgressIndicator.m 3 | // Playground 4 | // 5 | // Created by Daniel Jackson on 5/11/14. 6 | // Copyright (c) 2014 Daniel Jackson. All rights reserved. 7 | // 8 | 9 | #import "DJProgressIndicator.h" 10 | #import "DJBezierPath.h" 11 | 12 | @interface DJProgressIndicator () 13 | { 14 | NSColor* backRingColor; 15 | NSColor* ringColor; 16 | NSColor* backColor; 17 | } 18 | 19 | @property (nonatomic, strong) CAShapeLayer *backgroundRingLayer; 20 | @property (nonatomic, strong) CAShapeLayer *ringLayer; 21 | 22 | @end 23 | 24 | @implementation DJProgressIndicator 25 | 26 | - (id)initWithFrame:(NSRect)frame 27 | { 28 | self = [super initWithFrame:frame]; 29 | if (self) { 30 | _currentProgress = 0; 31 | 32 | _ringThickness = 6; 33 | _ringRadius = self.frame.size.width/2 - (_ringThickness); 34 | 35 | backRingColor = [NSColor darkGrayColor]; 36 | ringColor = [NSColor whiteColor]; 37 | backColor = [NSColor clearColor]; 38 | 39 | [self setAutoresizesSubviews:YES]; 40 | 41 | } 42 | return self; 43 | } 44 | 45 | - (void)drawRect:(NSRect)dirtyRect 46 | { 47 | [super drawRect:dirtyRect]; 48 | 49 | //NSLog(@"Drawing"); 50 | 51 | [self setBackground]; 52 | 53 | [self resetRings]; 54 | } 55 | 56 | - (void)setBackground 57 | { 58 | if(![self wantsLayer]) 59 | { 60 | CALayer* bgLayer = [CALayer layer]; 61 | [bgLayer setBackgroundColor:backColor.CGColor]; 62 | [self setWantsLayer:TRUE]; 63 | [self setLayer:bgLayer]; 64 | } 65 | else { 66 | [self.layer setBackgroundColor:backColor.CGColor]; 67 | } 68 | } 69 | 70 | - (void)resetRings 71 | { 72 | [_ringLayer removeFromSuperlayer]; 73 | _ringLayer = nil; 74 | [_backgroundRingLayer removeFromSuperlayer]; 75 | _backgroundRingLayer = nil; 76 | 77 | [self updateLayout]; 78 | self.ringLayer.strokeEnd = _currentProgress; 79 | } 80 | 81 | -(void)updateLayout 82 | { 83 | self.backgroundRingLayer.position = self.ringLayer.position = CGPointMake((CGRectGetWidth(self.bounds)/2), CGRectGetHeight(self.bounds)/2); 84 | } 85 | 86 | - (void)showProgress:(float)progress { 87 | 88 | NSLog(@"-----%f",progress); 89 | 90 | _currentProgress = progress; 91 | 92 | [self updateLayout]; 93 | 94 | if(progress >= 0) { 95 | self.ringLayer.strokeEnd = progress; 96 | } 97 | else { 98 | [self cancelRingLayerAnimation]; 99 | } 100 | 101 | //[self setNeedsDisplay:TRUE]; 102 | } 103 | 104 | - (CAShapeLayer *)ringLayer { 105 | if(!_ringLayer) { 106 | CGPoint center = CGPointMake(CGRectGetWidth(self.frame)/2, CGRectGetHeight(self.frame)/2); 107 | _ringLayer = [self createRingLayerWithCenter:center radius:_ringRadius lineWidth:_ringThickness color:ringColor]; 108 | [self.layer addSublayer:_ringLayer]; 109 | } 110 | return _ringLayer; 111 | } 112 | 113 | - (CAShapeLayer *)backgroundRingLayer { 114 | if(!_backgroundRingLayer) { 115 | CGPoint center = CGPointMake(CGRectGetWidth(self.frame)/2, CGRectGetHeight(self.frame)/2); 116 | _backgroundRingLayer = [self createRingLayerWithCenter:center radius:_ringRadius lineWidth:_ringThickness color:backRingColor]; 117 | _backgroundRingLayer.strokeEnd = 1; 118 | [self.layer addSublayer:_backgroundRingLayer]; 119 | } 120 | return _backgroundRingLayer; 121 | } 122 | 123 | - (void)cancelRingLayerAnimation { 124 | [CATransaction begin]; 125 | [CATransaction setDisableActions:YES]; 126 | [self.layer removeAllAnimations]; 127 | 128 | _ringLayer.strokeEnd = 0.0f; 129 | if (_ringLayer.superlayer) { 130 | [_ringLayer removeFromSuperlayer]; 131 | } 132 | _ringLayer = nil; 133 | 134 | if (_backgroundRingLayer.superlayer) { 135 | [_backgroundRingLayer removeFromSuperlayer]; 136 | } 137 | _backgroundRingLayer = nil; 138 | 139 | [CATransaction commit]; 140 | } 141 | 142 | -(void)clear 143 | { 144 | [self cancelRingLayerAnimation]; 145 | } 146 | 147 | - (CGPoint)pointOnCircleWithCenter:(CGPoint)center radius:(double)radius angleInDegrees:(double)angleInDegrees { 148 | float x = (float)(radius * cos(angleInDegrees * M_PI / 180)) + radius; 149 | float y = (float)(radius * sin(angleInDegrees * M_PI / 180)) + radius; 150 | return CGPointMake(x, y); 151 | } 152 | 153 | 154 | - (DJBezierPath *)createCirclePathWithCenter:(CGPoint)center radius:(CGFloat)radius sampleCount:(NSInteger)sampleCount { 155 | 156 | DJBezierPath *smoothedPath = [[DJBezierPath alloc] init]; 157 | CGPoint startPoint = [self pointOnCircleWithCenter:center radius:radius angleInDegrees:90]; 158 | 159 | [smoothedPath moveToPoint:startPoint]; 160 | 161 | CGFloat delta = 360.0f/sampleCount; 162 | CGFloat angleInDegrees = 90; 163 | for (NSInteger i=1; i 2 | @interface PodsDummy_Pods_AFNetworking : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_AFNetworking 5 | @end 6 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods-AFNetworking/Pods-AFNetworking-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Pods-environment.h" 6 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods-AFNetworking/Pods-AFNetworking.xcconfig: -------------------------------------------------------------------------------- 1 | PODS_AFNETWORKING_OTHER_LDFLAGS = -framework "CoreServices" -framework "Security" -framework "SystemConfiguration" -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods-CHCSVParser/Pods-CHCSVParser-Private.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods-CHCSVParser.xcconfig" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/CHCSVParser" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/CHCSVParser" "${PODS_ROOT}/Headers/Public/DJProgressHUD_OSX" 4 | OTHER_LDFLAGS = -ObjC 5 | PODS_ROOT = ${SRCROOT} 6 | SKIP_INSTALL = YES -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods-CHCSVParser/Pods-CHCSVParser-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_CHCSVParser : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_CHCSVParser 5 | @end 6 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods-CHCSVParser/Pods-CHCSVParser-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Pods-environment.h" 6 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods-CHCSVParser/Pods-CHCSVParser.xcconfig: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1entus/SpotifyImporter/b8a1bd518594e20a9ccfff871d31b72ebaba5287/SpotifyImporter/Pods/Target Support Files/Pods-CHCSVParser/Pods-CHCSVParser.xcconfig -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods-DJProgressHUD_OSX/Pods-DJProgressHUD_OSX-Private.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods-DJProgressHUD_OSX.xcconfig" 2 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 3 | HEADER_SEARCH_PATHS = "${PODS_ROOT}/Headers/Private" "${PODS_ROOT}/Headers/Private/DJProgressHUD_OSX" "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/CHCSVParser" "${PODS_ROOT}/Headers/Public/DJProgressHUD_OSX" 4 | OTHER_LDFLAGS = ${PODS_DJPROGRESSHUD_OSX_OTHER_LDFLAGS} -ObjC 5 | PODS_ROOT = ${SRCROOT} 6 | SKIP_INSTALL = YES -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods-DJProgressHUD_OSX/Pods-DJProgressHUD_OSX-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods_DJProgressHUD_OSX : NSObject 3 | @end 4 | @implementation PodsDummy_Pods_DJProgressHUD_OSX 5 | @end 6 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods-DJProgressHUD_OSX/Pods-DJProgressHUD_OSX-prefix.pch: -------------------------------------------------------------------------------- 1 | #ifdef __OBJC__ 2 | #import 3 | #endif 4 | 5 | #import "Pods-environment.h" 6 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods-DJProgressHUD_OSX/Pods-DJProgressHUD_OSX.xcconfig: -------------------------------------------------------------------------------- 1 | PODS_DJPROGRESSHUD_OSX_OTHER_LDFLAGS = -framework "QuartzCore" -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods/Pods-dummy.m: -------------------------------------------------------------------------------- 1 | #import 2 | @interface PodsDummy_Pods : NSObject 3 | @end 4 | @implementation PodsDummy_Pods 5 | @end 6 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods/Pods-environment.h: -------------------------------------------------------------------------------- 1 | 2 | // To check if a library is compiled with CocoaPods you 3 | // can use the `COCOAPODS` macro definition which is 4 | // defined in the xcconfigs so it is available in 5 | // headers also when they are imported in the client 6 | // project. 7 | 8 | 9 | // AFNetworking 10 | #define COCOAPODS_POD_AVAILABLE_AFNetworking 11 | #define COCOAPODS_VERSION_MAJOR_AFNetworking 2 12 | #define COCOAPODS_VERSION_MINOR_AFNetworking 5 13 | #define COCOAPODS_VERSION_PATCH_AFNetworking 4 14 | 15 | // AFNetworking/NSURLConnection 16 | #define COCOAPODS_POD_AVAILABLE_AFNetworking_NSURLConnection 17 | #define COCOAPODS_VERSION_MAJOR_AFNetworking_NSURLConnection 2 18 | #define COCOAPODS_VERSION_MINOR_AFNetworking_NSURLConnection 5 19 | #define COCOAPODS_VERSION_PATCH_AFNetworking_NSURLConnection 4 20 | 21 | // AFNetworking/NSURLSession 22 | #define COCOAPODS_POD_AVAILABLE_AFNetworking_NSURLSession 23 | #define COCOAPODS_VERSION_MAJOR_AFNetworking_NSURLSession 2 24 | #define COCOAPODS_VERSION_MINOR_AFNetworking_NSURLSession 5 25 | #define COCOAPODS_VERSION_PATCH_AFNetworking_NSURLSession 4 26 | 27 | // AFNetworking/Reachability 28 | #define COCOAPODS_POD_AVAILABLE_AFNetworking_Reachability 29 | #define COCOAPODS_VERSION_MAJOR_AFNetworking_Reachability 2 30 | #define COCOAPODS_VERSION_MINOR_AFNetworking_Reachability 5 31 | #define COCOAPODS_VERSION_PATCH_AFNetworking_Reachability 4 32 | 33 | // AFNetworking/Security 34 | #define COCOAPODS_POD_AVAILABLE_AFNetworking_Security 35 | #define COCOAPODS_VERSION_MAJOR_AFNetworking_Security 2 36 | #define COCOAPODS_VERSION_MINOR_AFNetworking_Security 5 37 | #define COCOAPODS_VERSION_PATCH_AFNetworking_Security 4 38 | 39 | // AFNetworking/Serialization 40 | #define COCOAPODS_POD_AVAILABLE_AFNetworking_Serialization 41 | #define COCOAPODS_VERSION_MAJOR_AFNetworking_Serialization 2 42 | #define COCOAPODS_VERSION_MINOR_AFNetworking_Serialization 5 43 | #define COCOAPODS_VERSION_PATCH_AFNetworking_Serialization 4 44 | 45 | // CHCSVParser 46 | #define COCOAPODS_POD_AVAILABLE_CHCSVParser 47 | #define COCOAPODS_VERSION_MAJOR_CHCSVParser 2 48 | #define COCOAPODS_VERSION_MINOR_CHCSVParser 1 49 | #define COCOAPODS_VERSION_PATCH_CHCSVParser 0 50 | 51 | // DJProgressHUD_OSX 52 | #define COCOAPODS_POD_AVAILABLE_DJProgressHUD_OSX 53 | #define COCOAPODS_VERSION_MAJOR_DJProgressHUD_OSX 0 54 | #define COCOAPODS_VERSION_MINOR_DJProgressHUD_OSX 0 55 | #define COCOAPODS_VERSION_PATCH_DJProgressHUD_OSX 1 56 | 57 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods/Pods-resources.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | 4 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 5 | 6 | RESOURCES_TO_COPY=${PODS_ROOT}/resources-to-copy-${TARGETNAME}.txt 7 | > "$RESOURCES_TO_COPY" 8 | 9 | XCASSET_FILES=() 10 | 11 | realpath() { 12 | DIRECTORY=$(cd "${1%/*}" && pwd) 13 | FILENAME="${1##*/}" 14 | echo "$DIRECTORY/$FILENAME" 15 | } 16 | 17 | install_resource() 18 | { 19 | case $1 in 20 | *.storyboard) 21 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 22 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .storyboard`.storyboardc" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 23 | ;; 24 | *.xib) 25 | echo "ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile ${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib ${PODS_ROOT}/$1 --sdk ${SDKROOT}" 26 | ibtool --reference-external-strings-file --errors --warnings --notices --output-format human-readable-text --compile "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename \"$1\" .xib`.nib" "${PODS_ROOT}/$1" --sdk "${SDKROOT}" 27 | ;; 28 | *.framework) 29 | echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 30 | mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 31 | echo "rsync -av ${PODS_ROOT}/$1 ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 32 | rsync -av "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" 33 | ;; 34 | *.xcdatamodel) 35 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1"`.mom\"" 36 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodel`.mom" 37 | ;; 38 | *.xcdatamodeld) 39 | echo "xcrun momc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd\"" 40 | xcrun momc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcdatamodeld`.momd" 41 | ;; 42 | *.xcmappingmodel) 43 | echo "xcrun mapc \"${PODS_ROOT}/$1\" \"${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm\"" 44 | xcrun mapc "${PODS_ROOT}/$1" "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/`basename "$1" .xcmappingmodel`.cdm" 45 | ;; 46 | *.xcassets) 47 | ABSOLUTE_XCASSET_FILE=$(realpath "${PODS_ROOT}/$1") 48 | XCASSET_FILES+=("$ABSOLUTE_XCASSET_FILE") 49 | ;; 50 | /*) 51 | echo "$1" 52 | echo "$1" >> "$RESOURCES_TO_COPY" 53 | ;; 54 | *) 55 | echo "${PODS_ROOT}/$1" 56 | echo "${PODS_ROOT}/$1" >> "$RESOURCES_TO_COPY" 57 | ;; 58 | esac 59 | } 60 | 61 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${CONFIGURATION_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 62 | if [[ "${ACTION}" == "install" ]]; then 63 | rsync -avr --copy-links --no-relative --exclude '*/.svn/*' --files-from="$RESOURCES_TO_COPY" / "${INSTALL_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 64 | fi 65 | rm -f "$RESOURCES_TO_COPY" 66 | 67 | if [[ -n "${WRAPPER_EXTENSION}" ]] && [ "`xcrun --find actool`" ] && [ -n "$XCASSET_FILES" ] 68 | then 69 | case "${TARGETED_DEVICE_FAMILY}" in 70 | 1,2) 71 | TARGET_DEVICE_ARGS="--target-device ipad --target-device iphone" 72 | ;; 73 | 1) 74 | TARGET_DEVICE_ARGS="--target-device iphone" 75 | ;; 76 | 2) 77 | TARGET_DEVICE_ARGS="--target-device ipad" 78 | ;; 79 | *) 80 | TARGET_DEVICE_ARGS="--target-device mac" 81 | ;; 82 | esac 83 | 84 | # Find all other xcassets (this unfortunately includes those of path pods and other targets). 85 | OTHER_XCASSETS=$(find "$PWD" -iname "*.xcassets" -type d) 86 | while read line; do 87 | if [[ $line != "`realpath $PODS_ROOT`*" ]]; then 88 | XCASSET_FILES+=("$line") 89 | fi 90 | done <<<"$OTHER_XCASSETS" 91 | 92 | printf "%s\0" "${XCASSET_FILES[@]}" | xargs -0 xcrun actool --output-format human-readable-text --notices --warnings --platform "${PLATFORM_NAME}" --minimum-deployment-target "${IPHONEOS_DEPLOYMENT_TARGET}" ${TARGET_DEVICE_ARGS} --compress-pngs --compile "${BUILT_PRODUCTS_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}" 93 | fi 94 | -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods/Pods.debug.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/CHCSVParser" "${PODS_ROOT}/Headers/Public/DJProgressHUD_OSX" 3 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/AFNetworking" -isystem "${PODS_ROOT}/Headers/Public/CHCSVParser" -isystem "${PODS_ROOT}/Headers/Public/DJProgressHUD_OSX" 4 | OTHER_LDFLAGS = $(inherited) -ObjC -l"Pods-AFNetworking" -l"Pods-CHCSVParser" -l"Pods-DJProgressHUD_OSX" -framework "CoreServices" -framework "QuartzCore" -framework "Security" -framework "SystemConfiguration" 5 | OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS) 6 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /SpotifyImporter/Pods/Target Support Files/Pods/Pods.release.xcconfig: -------------------------------------------------------------------------------- 1 | GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 2 | HEADER_SEARCH_PATHS = $(inherited) "${PODS_ROOT}/Headers/Public" "${PODS_ROOT}/Headers/Public/AFNetworking" "${PODS_ROOT}/Headers/Public/CHCSVParser" "${PODS_ROOT}/Headers/Public/DJProgressHUD_OSX" 3 | OTHER_CFLAGS = $(inherited) -isystem "${PODS_ROOT}/Headers/Public" -isystem "${PODS_ROOT}/Headers/Public/AFNetworking" -isystem "${PODS_ROOT}/Headers/Public/CHCSVParser" -isystem "${PODS_ROOT}/Headers/Public/DJProgressHUD_OSX" 4 | OTHER_LDFLAGS = $(inherited) -ObjC -l"Pods-AFNetworking" -l"Pods-CHCSVParser" -l"Pods-DJProgressHUD_OSX" -framework "CoreServices" -framework "QuartzCore" -framework "Security" -framework "SystemConfiguration" 5 | OTHER_LIBTOOLFLAGS = $(OTHER_LDFLAGS) 6 | PODS_ROOT = ${SRCROOT}/Pods -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Categories/NSArray+Filter.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Filter.h 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSArray (Filter) 12 | - (NSArray *)filteredArrayPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate; 13 | @end 14 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Categories/NSArray+Filter.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSArray+Filter.m 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import "NSArray+Filter.h" 10 | 11 | @implementation NSArray (Filter) 12 | - (NSArray *)filteredArrayPassingTest:(BOOL (^)(id obj, NSUInteger idx, BOOL *stop))predicate { 13 | return [self objectsAtIndexes:[self indexesOfObjectsPassingTest:predicate]]; 14 | } 15 | @end 16 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Categories/NSData+HexString.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+HexString.h 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface NSData (HexString) 12 | + (instancetype)dataWithHexString:(NSString *)hex; 13 | - (NSData *)reversedData; 14 | @end 15 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Categories/NSData+HexString.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSData+HexString.m 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import "NSData+HexString.h" 10 | 11 | @implementation NSData (HexString) 12 | 13 | + (instancetype)dataWithHexString:(NSString *)hex { 14 | char buf[3]; 15 | buf[2] = '\0'; 16 | NSAssert(0 == [hex length] % 2, @"Hex strings should have an even number of digits (%@)", hex); 17 | unsigned char *bytes = malloc([hex length]/2); 18 | unsigned char *bp = bytes; 19 | for (CFIndex i = 0; i < [hex length]; i += 2) { 20 | buf[0] = [hex characterAtIndex:i]; 21 | buf[1] = [hex characterAtIndex:i+1]; 22 | char *b2 = NULL; 23 | *bp++ = strtol(buf, &b2, 16); 24 | NSAssert(b2 == buf + 2, @"String should be all hex digits: %@ (bad digit around %ld)", hex, i); 25 | } 26 | 27 | return [NSData dataWithBytesNoCopy:bytes length:[hex length]/2 freeWhenDone:YES]; 28 | } 29 | 30 | - (NSData *)reversedData { 31 | NSData *myData = self; 32 | 33 | const char *bytes = [myData bytes]; 34 | 35 | NSUInteger datalength = [myData length]; 36 | 37 | char *reverseBytes = malloc(sizeof(char) * datalength); 38 | NSUInteger index = datalength - 1; 39 | 40 | for (int i = 0; i < datalength; i++) 41 | reverseBytes[index--] = bytes[i]; 42 | 43 | NSData *reversedData = [NSData dataWithBytesNoCopy:reverseBytes length: datalength freeWhenDone:YES]; 44 | 45 | return reversedData; 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Categories/NSString+Score.h: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Score.h 3 | // 4 | // Created by Nicholas Bruning on 5/12/11. 5 | // Copyright (c) 2011 Involved Pty Ltd. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | enum{ 11 | NSStringScoreOptionNone = 1 << 0, 12 | NSStringScoreOptionFavorSmallerWords = 1 << 1, 13 | NSStringScoreOptionReducedLongStringPenalty = 1 << 2 14 | }; 15 | 16 | typedef NSUInteger NSStringScoreOption; 17 | 18 | @interface NSString (Score) 19 | 20 | - (CGFloat) scoreAgainst:(NSString *)otherString; 21 | - (CGFloat) scoreAgainst:(NSString *)otherString fuzziness:(NSNumber *)fuzziness; 22 | - (CGFloat) scoreAgainst:(NSString *)otherString fuzziness:(NSNumber *)fuzziness options:(NSStringScoreOption)options; 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Categories/NSString+Score.m: -------------------------------------------------------------------------------- 1 | // 2 | // NSString+Score.m 3 | // 4 | // Created by Nicholas Bruning on 5/12/11. 5 | // Copyright (c) 2011 Involved Pty Ltd. All rights reserved. 6 | // 7 | 8 | //score reference: http://jsfiddle.net/JrLVD/ 9 | 10 | #import "NSString+Score.h" 11 | 12 | @implementation NSString (Score) 13 | 14 | - (CGFloat) scoreAgainst:(NSString *)otherString{ 15 | return [self scoreAgainst:otherString fuzziness:nil]; 16 | } 17 | 18 | - (CGFloat) scoreAgainst:(NSString *)otherString fuzziness:(NSNumber *)fuzziness{ 19 | return [self scoreAgainst:otherString fuzziness:fuzziness options:NSStringScoreOptionNone]; 20 | } 21 | 22 | - (CGFloat) scoreAgainst:(NSString *)anotherString fuzziness:(NSNumber *)fuzziness options:(NSStringScoreOption)options{ 23 | NSMutableCharacterSet *workingInvalidCharacterSet = [[NSCharacterSet lowercaseLetterCharacterSet] mutableCopy]; 24 | [workingInvalidCharacterSet formUnionWithCharacterSet:[NSCharacterSet uppercaseLetterCharacterSet]]; 25 | [workingInvalidCharacterSet addCharactersInString:@" "]; 26 | NSCharacterSet *invalidCharacterSet = [workingInvalidCharacterSet invertedSet]; 27 | 28 | NSString *string = [[[self decomposedStringWithCanonicalMapping] componentsSeparatedByCharactersInSet:invalidCharacterSet] componentsJoinedByString:@""]; 29 | NSString *otherString = [[[anotherString decomposedStringWithCanonicalMapping] componentsSeparatedByCharactersInSet:invalidCharacterSet] componentsJoinedByString:@""]; 30 | 31 | // If the string is equal to the abbreviation, perfect match. 32 | if([string isEqualToString:otherString]) return (CGFloat) 1.0f; 33 | 34 | //if it's not a perfect match and is empty return 0 35 | if([otherString length] == 0) return (CGFloat) 0.0f; 36 | 37 | CGFloat totalCharacterScore = 0; 38 | NSUInteger otherStringLength = [otherString length]; 39 | NSUInteger stringLength = [string length]; 40 | BOOL startOfStringBonus = NO; 41 | CGFloat otherStringScore; 42 | CGFloat fuzzies = 1; 43 | CGFloat finalScore; 44 | 45 | // Walk through abbreviation and add up scores. 46 | for(uint index = 0; index < otherStringLength; index++){ 47 | CGFloat characterScore = 0.1; 48 | NSInteger indexInString = NSNotFound; 49 | NSString *chr; 50 | NSRange rangeChrLowercase; 51 | NSRange rangeChrUppercase; 52 | 53 | chr = [otherString substringWithRange:NSMakeRange(index, 1)]; 54 | 55 | //make these next few lines leverage NSNotfound, methinks. 56 | rangeChrLowercase = [string rangeOfString:[chr lowercaseString]]; 57 | rangeChrUppercase = [string rangeOfString:[chr uppercaseString]]; 58 | 59 | if(rangeChrLowercase.location == NSNotFound && rangeChrUppercase.location == NSNotFound){ 60 | if(fuzziness){ 61 | fuzzies += 1 - [fuzziness floatValue]; 62 | } else { 63 | return 0; // this is an error! 64 | } 65 | 66 | } else if (rangeChrLowercase.location != NSNotFound && rangeChrUppercase.location != NSNotFound){ 67 | indexInString = MIN(rangeChrLowercase.location, rangeChrUppercase.location); 68 | 69 | } else if(rangeChrLowercase.location != NSNotFound || rangeChrUppercase.location != NSNotFound){ 70 | indexInString = rangeChrLowercase.location != NSNotFound ? rangeChrLowercase.location : rangeChrUppercase.location; 71 | 72 | } else { 73 | indexInString = MIN(rangeChrLowercase.location, rangeChrUppercase.location); 74 | 75 | } 76 | 77 | // Set base score for matching chr 78 | 79 | // Same case bonus. 80 | if(indexInString != NSNotFound && [[string substringWithRange:NSMakeRange(indexInString, 1)] isEqualToString:chr]){ 81 | characterScore += 0.1; 82 | } 83 | 84 | // Consecutive letter & start-of-string bonus 85 | if(indexInString == 0){ 86 | // Increase the score when matching first character of the remainder of the string 87 | characterScore += 0.6; 88 | if(index == 0){ 89 | // If match is the first character of the string 90 | // & the first character of abbreviation, add a 91 | // start-of-string match bonus. 92 | startOfStringBonus = YES; 93 | } 94 | } else if(indexInString != NSNotFound) { 95 | // Acronym Bonus 96 | // Weighing Logic: Typing the first character of an acronym is as if you 97 | // preceded it with two perfect character matches. 98 | if( [[string substringWithRange:NSMakeRange(indexInString - 1, 1)] isEqualToString:@" "] ){ 99 | characterScore += 0.8; 100 | } 101 | } 102 | 103 | // Left trim the already matched part of the string 104 | // (forces sequential matching). 105 | if(indexInString != NSNotFound){ 106 | string = [string substringFromIndex:indexInString + 1]; 107 | } 108 | 109 | totalCharacterScore += characterScore; 110 | } 111 | 112 | if(NSStringScoreOptionFavorSmallerWords == (options & NSStringScoreOptionFavorSmallerWords)){ 113 | // Weigh smaller words higher 114 | return totalCharacterScore / stringLength; 115 | } 116 | 117 | otherStringScore = totalCharacterScore / otherStringLength; 118 | 119 | if(NSStringScoreOptionReducedLongStringPenalty == (options & NSStringScoreOptionReducedLongStringPenalty)){ 120 | // Reduce the penalty for longer words 121 | CGFloat percentageOfMatchedString = otherStringLength / stringLength; 122 | CGFloat wordScore = otherStringScore * percentageOfMatchedString; 123 | finalScore = (wordScore + otherStringScore) / 2; 124 | 125 | } else { 126 | finalScore = ((otherStringScore * ((CGFloat)(otherStringLength) / (CGFloat)(stringLength))) + otherStringScore) / 2; 127 | } 128 | 129 | finalScore = finalScore / fuzzies; 130 | 131 | if(startOfStringBonus && finalScore + 0.15 < 1){ 132 | finalScore += 0.15; 133 | } 134 | 135 | return finalScore; 136 | } 137 | 138 | @end 139 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Models/SICSVImporter.h: -------------------------------------------------------------------------------- 1 | // 2 | // SICSVImporter.h 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void(^SICSVImporterCompletionHandler)(NSArray *songs, NSError *error); 12 | 13 | @interface SICSVImporter : NSObject 14 | @property (nonatomic, copy) SICSVImporterCompletionHandler completionHandler; 15 | 16 | - (void)importCSVWithContentsOfURL:(NSURL *)URL completionHandler:(SICSVImporterCompletionHandler)completionHandler; 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Models/SICSVImporter.m: -------------------------------------------------------------------------------- 1 | // 2 | // SICSVImporter.m 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import "SICSVImporter.h" 10 | #import 11 | #import "SISong.h" 12 | 13 | @interface SICSVImporter () 14 | @property (strong) NSMutableArray *lines; 15 | @property (strong) NSError *error; 16 | 17 | @property (strong) NSMutableArray *currentLine; 18 | @end 19 | 20 | @implementation SICSVImporter 21 | 22 | - (void)importCSVWithContentsOfURL:(NSURL *)URL completionHandler:(SICSVImporterCompletionHandler)completionHandler { 23 | 24 | self.completionHandler = completionHandler; 25 | CHCSVParser *parser = [[CHCSVParser alloc] initWithContentsOfDelimitedURL:URL delimiter:',']; 26 | parser.delegate = self; 27 | 28 | [parser parse]; 29 | } 30 | 31 | #pragma mark - 32 | 33 | - (void)parserDidBeginDocument:(CHCSVParser *)parser { 34 | self.lines = [[NSMutableArray alloc] init]; 35 | } 36 | 37 | - (void)parser:(CHCSVParser *)parser didBeginLine:(NSUInteger)recordNumber { 38 | self.currentLine = [[NSMutableArray alloc] init]; 39 | } 40 | 41 | - (void)parser:(CHCSVParser *)parser didEndLine:(NSUInteger)recordNumber { 42 | [self.lines addObject:self.currentLine]; 43 | self.currentLine = nil; 44 | } 45 | 46 | - (void)parser:(CHCSVParser *)parser didReadField:(NSString *)field atIndex:(NSInteger)fieldIndex { 47 | [self.currentLine addObject:field]; 48 | } 49 | 50 | - (void)parser:(CHCSVParser *)parser didFailWithError:(NSError *)error { 51 | 52 | self.error = error; 53 | 54 | if (error && error.code == CHCSVErrorCodeInvalidFormat) { 55 | self.error = [NSError errorWithDomain:error.domain code:error.code userInfo:@{ NSLocalizedDescriptionKey : [NSString stringWithFormat:@"Invalid CSV format, please verify %@ line of your CSV file.",@(self.lines.count)]}]; 56 | } 57 | if (self.completionHandler) { 58 | self.completionHandler(@[],self.error); 59 | } 60 | } 61 | 62 | - (void)parserDidEndDocument:(CHCSVParser *)parser { 63 | 64 | NSMutableArray *songs = [NSMutableArray arrayWithCapacity:self.lines.count]; 65 | 66 | if (self.lines.count) { 67 | // Remove column info line 68 | [self.lines removeObjectAtIndex:0]; 69 | 70 | for (NSArray *line in self.lines) { 71 | if (line.count >= 2) { 72 | [songs addObject:[SISong songWithTrackName:line[1] artistName:line[2]]]; 73 | } 74 | } 75 | } 76 | 77 | if (self.completionHandler) { 78 | self.completionHandler([songs copy],self.error); 79 | } 80 | } 81 | @end 82 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Models/SIDelayOperation.h: -------------------------------------------------------------------------------- 1 | // 2 | // SIDelayOperation.h 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SIDelayOperation : NSOperation 12 | @property (nonatomic, assign) NSTimeInterval delay; 13 | 14 | - (instancetype)initWithDelay:(NSTimeInterval)delay; 15 | @end 16 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Models/SIDelayOperation.m: -------------------------------------------------------------------------------- 1 | // 2 | // SIDelayOperation.m 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import "SIDelayOperation.h" 10 | 11 | @implementation SIDelayOperation 12 | 13 | - (instancetype)init { 14 | if (self = [super init]) { 15 | self.delay = 30; 16 | } 17 | return self; 18 | } 19 | 20 | - (instancetype)initWithDelay:(NSTimeInterval)delay { 21 | if (self = [self init]) { 22 | self.delay = delay; 23 | } 24 | return self; 25 | } 26 | 27 | - (void)main { 28 | [NSThread sleepForTimeInterval:self.delay]; 29 | } 30 | 31 | @end 32 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Models/SISong.h: -------------------------------------------------------------------------------- 1 | // 2 | // SISong.h 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SISong : NSObject 12 | @property (nonatomic, copy) NSString *trackName; 13 | @property (nonatomic, copy) NSString *artistName; 14 | @property (nonatomic, copy) NSString *itunesIdentifier; 15 | @property (nonatomic, strong) NSNumber *matchingScore; 16 | @property (nonatomic, assign) BOOL fetched; 17 | 18 | + (instancetype)songWithTrackName:(NSString *)trackName artistName:(NSString *)artistName; 19 | @end 20 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Models/SISong.m: -------------------------------------------------------------------------------- 1 | // 2 | // SISong.m 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import "SISong.h" 10 | 11 | @implementation SISong 12 | 13 | + (instancetype)songWithTrackName:(NSString *)trackName artistName:(NSString *)artistName { 14 | SISong *song = [[[self class] alloc] init]; 15 | song.trackName = [trackName stringByReplacingOccurrencesOfString:@"\"" withString:@""]; 16 | song.artistName = [artistName stringByReplacingOccurrencesOfString:@"\"" withString:@""]; 17 | return song; 18 | } 19 | @end 20 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Supporting Files/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Supporting Files/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Supporting Files/Images.xcassets/instructions.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "bfBMD.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "bfBMD-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "bfBMD-2.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Supporting Files/Images.xcassets/instructions.imageset/bfBMD-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1entus/SpotifyImporter/b8a1bd518594e20a9ccfff871d31b72ebaba5287/SpotifyImporter/SpotifyImporter/Supporting Files/Images.xcassets/instructions.imageset/bfBMD-1.png -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Supporting Files/Images.xcassets/instructions.imageset/bfBMD-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1entus/SpotifyImporter/b8a1bd518594e20a9ccfff871d31b72ebaba5287/SpotifyImporter/SpotifyImporter/Supporting Files/Images.xcassets/instructions.imageset/bfBMD-2.png -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Supporting Files/Images.xcassets/instructions.imageset/bfBMD.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m1entus/SpotifyImporter/b8a1bd518594e20a9ccfff871d31b72ebaba5287/SpotifyImporter/SpotifyImporter/Supporting Files/Images.xcassets/instructions.imageset/bfBMD.png -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | me.zaborowski.$(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 © 2015 Michal Zaborowski. All rights reserved. 29 | NSMainStoryboardFile 30 | Main 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Supporting Files/SIAppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SIAppDelegate : NSObject 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Supporting Files/SIAppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import "SIAppDelegate.h" 10 | #import "NSData+HexString.h" 11 | 12 | @interface SIAppDelegate () 13 | 14 | @end 15 | 16 | @implementation SIAppDelegate 17 | 18 | - (void)applicationDidFinishLaunching:(NSNotification *)aNotification { 19 | 20 | } 21 | 22 | - (void)applicationWillTerminate:(NSNotification *)aNotification { 23 | // Insert code here to tear down your application 24 | } 25 | 26 | @end 27 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/Supporting Files/main.m: -------------------------------------------------------------------------------- 1 | // 2 | // main.m 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | int main(int argc, const char * argv[]) { 12 | return NSApplicationMain(argc, argv); 13 | } 14 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/View Controllers/SIHelpGuidViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SIHelpGuidViewController.h 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 14/07/15. 6 | // Copyright © 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SIHelpGuidViewController : NSViewController 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/View Controllers/SIHelpGuidViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SIHelpGuidViewController.m 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 14/07/15. 6 | // Copyright © 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import "SIHelpGuidViewController.h" 10 | 11 | @interface SITextAttachmentCell : NSTextAttachmentCell 12 | @end 13 | 14 | @implementation SITextAttachmentCell 15 | 16 | - (NSRect)cellFrameForTextContainer:(nonnull NSTextContainer *)textContainer proposedLineFragment:(NSRect)lineFrag glyphPosition:(NSPoint)position characterIndex:(NSUInteger)charIndex { 17 | CGRect superRect = [super cellFrameForTextContainer:textContainer proposedLineFragment:lineFrag glyphPosition:position characterIndex:charIndex]; 18 | superRect.size = CGSizeMake(textContainer.textView.frame.size.width, superRect.size.height * textContainer.textView.frame.size.width/self.image.size.width); 19 | return superRect; 20 | } 21 | 22 | - (void)drawInteriorWithFrame:(NSRect)cellFrame inView:(nonnull NSView *)controlView { 23 | [self.image drawInRect:cellFrame]; 24 | } 25 | 26 | - (void)drawWithFrame:(NSRect)cellFrame inView:(NSView *)controlView { 27 | [self.image drawInRect:cellFrame]; 28 | } 29 | 30 | @end 31 | 32 | @interface SIHelpGuidViewController () 33 | @property (unsafe_unretained) IBOutlet NSTextView *textView; 34 | 35 | @end 36 | 37 | @implementation SIHelpGuidViewController 38 | 39 | 40 | - (void)viewDidLoad { 41 | [super viewDidLoad]; 42 | 43 | NSMutableAttributedString *attributedText = [[NSMutableAttributedString alloc] init]; 44 | 45 | [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@"We are going to retrieve cookie data from iTunes using Charles Proxy.\n\n"]]; 46 | 47 | [attributedText addAttribute:NSLinkAttributeName value:@"http://www.charlesproxy.com" range:[attributedText.string rangeOfString:@"Charles Proxy"]]; 48 | 49 | [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@"1. From the Menu Proxy go to SSL Proxy Settings\n\n"]]; 50 | [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@"2. Check 'Enable SSL Proxying'\n\n"]]; 51 | [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@"3. Click on add and insert '*itunes.apple.com'\n\n"]]; 52 | [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@"4. In the same Menu check on 'Mac OS X Proxy'\n\n"]]; 53 | [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@"5. Go to iTunes go to an Apple Music playlist but don't do nothing\n\n"]]; 54 | [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@"6. Check you have enabled recording (please refer to image below)\n\n"]]; 55 | [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@"7. When recording is enabled add the playlist to my Music\n\n"]]; 56 | [attributedText addAttributes:@{ NSFontAttributeName : [NSFont systemFontOfSize:16.0]} range:NSMakeRange(0, attributedText.length)]; 57 | 58 | NSTextAttachment *attachment = [[NSTextAttachment alloc] init]; 59 | 60 | NSTextAttachmentCell *attachmentCell =[[SITextAttachmentCell alloc] initImageCell:[NSImage imageNamed:@"instructions"]]; 61 | [attachment setAttachmentCell: attachmentCell ]; 62 | 63 | [attributedText appendAttributedString:[NSAttributedString attributedStringWithAttachment:attachment]]; 64 | 65 | [attributedText appendAttributedString:[[NSAttributedString alloc] initWithString:@"\n"]]; 66 | 67 | [self.textView.textStorage setAttributedString:attributedText]; 68 | } 69 | @end 70 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/View Controllers/SIImporterViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SIImporterViewController.h 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SIImporterViewController : NSViewController 12 | @property (nonatomic, strong) NSArray *songs; 13 | @end 14 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/View Controllers/SIImporterViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SIImporterViewController.m 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import "SIImporterViewController.h" 10 | #import "SISong.h" 11 | #import "NSArray+Filter.h" 12 | #import "SIDelayOperation.h" 13 | #import 14 | #import "NSData+HexString.h" 15 | #import 16 | 17 | NSString *const SIAppleRequestBodyString = @"61 6a 43 41 00 00 00 45 6d 73 74 63 00 00 00 04 55 94 17 a3 6d 6c 69 64 00 00 00 04 00 00 00 00 6d 75 73 72 00 00 00 04 00 00 00 81 6d 69 6b 64 00 00 00 01 02 6d 69 64 61 00 00 00 10 61 65 41 69 00 00 00 08 00 00 00 00 11 8c d9 2c 00"; 18 | 19 | NSString *const SIDsidUserDefaultsKey = @"SIDsidUserDefaultsKey"; 20 | NSString *const SIGsidUserDefaultsKey = @"SIGsidUserDefaultsKey"; 21 | NSString *const SICookieUserDefaultsKey = @"SICookieUserDefaultsKey"; 22 | 23 | @interface SIImporterViewController () 24 | @property (weak) IBOutlet NSTextField *cookieTextField; 25 | @property (weak) IBOutlet NSTextField *dsidTextField; 26 | @property (weak) IBOutlet NSTextField *guidTextField; 27 | @property (weak) IBOutlet NSTextField *importDelayTextField; 28 | @property (weak) IBOutlet NSTableView *tableView; 29 | @property (nonatomic, strong) NSOperationQueue *operationQueue; 30 | @end 31 | 32 | @implementation SIImporterViewController 33 | 34 | - (void)viewDidLoad { 35 | [super viewDidLoad]; 36 | self.operationQueue = [[NSOperationQueue alloc] init]; 37 | self.operationQueue.maxConcurrentOperationCount = 1; 38 | 39 | self.dsidTextField.stringValue = [[NSUserDefaults standardUserDefaults] objectForKey:SIDsidUserDefaultsKey] ?: @""; 40 | self.guidTextField.stringValue = [[NSUserDefaults standardUserDefaults] objectForKey:SIGsidUserDefaultsKey] ?: @""; 41 | self.cookieTextField.stringValue = [[NSUserDefaults standardUserDefaults] objectForKey:SICookieUserDefaultsKey] ?: @""; 42 | 43 | self.songs = [self.songs sortedArrayUsingDescriptors:@[[NSSortDescriptor sortDescriptorWithKey:@"matchingScore" ascending:NO selector:@selector(compare:)]]]; 44 | 45 | [self.tableView reloadData]; 46 | } 47 | 48 | 49 | - (IBAction)cancelButtonTapped:(id)sender { 50 | [self.view.window.sheetParent endSheet:self.view.window returnCode:NSModalResponseCancel]; 51 | } 52 | - (IBAction)importMatchedButtonTapped:(id)sender { 53 | NSArray *songsToImport = [self.songs filteredArrayPassingTest:^BOOL(SISong *obj, NSUInteger idx, BOOL *stop) { 54 | if (obj.matchingScore && obj.fetched && [obj.matchingScore floatValue] >= 0.8 && obj.itunesIdentifier) { 55 | return YES; 56 | } 57 | return NO; 58 | }]; 59 | [self importSongs:songsToImport]; 60 | } 61 | - (IBAction)importSelectedButtonTapped:(id)sender { 62 | 63 | NSArray *songsToImport = [[self.songs objectsAtIndexes:self.tableView.selectedRowIndexes] filteredArrayPassingTest:^BOOL(SISong *obj, NSUInteger idx, BOOL *stop) { 64 | return obj.itunesIdentifier != nil; 65 | }]; 66 | [self importSongs:songsToImport]; 67 | } 68 | - (IBAction)importAllButtonTapped:(id)sender { 69 | NSArray *songsToImport = [self.songs filteredArrayPassingTest:^BOOL(SISong *obj, NSUInteger idx, BOOL *stop) { 70 | if (obj.matchingScore && obj.fetched && [obj.matchingScore floatValue] > 0.0 && obj.itunesIdentifier) { 71 | return YES; 72 | } 73 | return NO; 74 | }]; 75 | [self importSongs:songsToImport]; 76 | } 77 | 78 | - (void)saveUserIdsToUserDefaults { 79 | [[NSUserDefaults standardUserDefaults] setObject:self.dsidTextField.stringValue forKey:SIDsidUserDefaultsKey]; 80 | [[NSUserDefaults standardUserDefaults] setObject:self.guidTextField.stringValue forKey:SIGsidUserDefaultsKey]; 81 | [[NSUserDefaults standardUserDefaults] setObject:self.cookieTextField.stringValue forKey:SICookieUserDefaultsKey]; 82 | [[NSUserDefaults standardUserDefaults] synchronize]; 83 | } 84 | 85 | - (void)importSongs:(NSArray *)songs { 86 | 87 | [self saveUserIdsToUserDefaults]; 88 | 89 | if (songs.count <= 0) { 90 | return; 91 | } 92 | 93 | NSMutableArray *songsToImport = [songs mutableCopy]; 94 | 95 | [DJProgressHUD showStatus:@"Checking authentication data..." FromView:self.view]; 96 | 97 | [self importFirstSong:[songsToImport firstObject] andVerifyAuthenticationData:^(BOOL authenticationSuccess) { 98 | [DJProgressHUD dismiss]; 99 | 100 | if (authenticationSuccess) { 101 | [songsToImport removeObjectAtIndex:0]; 102 | [self continueImportingSongs:songsToImport]; 103 | } 104 | }]; 105 | } 106 | 107 | - (void)showSuccessAlert { 108 | NSAlert *alert = [[NSAlert alloc] init]; 109 | [alert addButtonWithTitle:@"OK"]; 110 | [alert setMessageText:@"Success"]; 111 | [alert setInformativeText:@"Please restart iTunes and you will see your songs."]; 112 | [alert setAlertStyle:NSWarningAlertStyle]; 113 | [alert runModal]; 114 | } 115 | 116 | - (void)continueImportingSongs:(NSArray *)songs { 117 | [self.operationQueue cancelAllOperations]; 118 | 119 | if (songs.count <= 0) { 120 | [DJProgressHUD dismiss]; 121 | [self.view.window.sheetParent endSheet:self.view.window returnCode:NSModalResponseOK]; 122 | [self showSuccessAlert]; 123 | } 124 | 125 | AFHTTPRequestOperationManager *operationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://ld-3.itunes.apple.com"]]; 126 | operationManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/x-dmap-tagged",nil]; 127 | 128 | [DJProgressHUD showProgress:0.0 withStatus:@"Importing songs..." FromView:self.view]; 129 | __block NSInteger currentProgress = 0; 130 | NSInteger finalProgress = songs.count; 131 | 132 | for (SISong *song in songs) { 133 | 134 | __weak typeof(self) weakSelf = self; 135 | AFHTTPRequestOperation *requestOperation = [self requestOperationForManager:operationManager song:song]; 136 | [requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { 137 | currentProgress++; 138 | [DJProgressHUD showProgress:(CGFloat)currentProgress/(CGFloat)finalProgress withStatus:@"Importing songs..." FromView:weakSelf.view]; 139 | if (currentProgress >= finalProgress) { 140 | [DJProgressHUD dismiss]; 141 | [weakSelf.view.window.sheetParent endSheet:weakSelf.view.window returnCode:NSModalResponseOK]; 142 | [weakSelf showSuccessAlert]; 143 | } 144 | 145 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 146 | if (error) { 147 | [weakSelf.operationQueue cancelAllOperations]; 148 | } 149 | }]; 150 | 151 | NSInteger delay = self.importDelayTextField.integerValue; 152 | if (delay <= 1) { 153 | delay = 1; 154 | } 155 | SIDelayOperation *operation = [[SIDelayOperation alloc] initWithDelay:delay]; 156 | [self.operationQueue addOperations:@[requestOperation,operation] waitUntilFinished:NO]; 157 | } 158 | } 159 | 160 | - (void)importFirstSong:(SISong *)song andVerifyAuthenticationData:(void(^)(BOOL authenticationSuccess))completion { 161 | 162 | AFHTTPRequestOperationManager *operationManager = [[AFHTTPRequestOperationManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://ld-3.itunes.apple.com"]]; 163 | operationManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/x-dmap-tagged",nil]; 164 | 165 | AFHTTPRequestOperation *requestOperation = [self requestOperationForManager:operationManager song:song]; 166 | [requestOperation setCompletionBlockWithSuccess:^(AFHTTPRequestOperation *operation, id responseObject) { 167 | if (completion) { 168 | completion(YES); 169 | } 170 | } failure:^(AFHTTPRequestOperation *operation, NSError *error) { 171 | if (completion) { 172 | completion(NO); 173 | } 174 | if (operation.response.statusCode == 401) { 175 | NSAlert *alert = [[NSAlert alloc] init]; 176 | [alert addButtonWithTitle:@"OK"]; 177 | [alert setMessageText:@"Unable to authenticate user."]; 178 | [alert setInformativeText:@"Import operation aborted. Make sure you enter correct X-Dsid and X-Guid identifiers."]; 179 | [alert setAlertStyle:NSWarningAlertStyle]; 180 | [alert runModal]; 181 | } else { 182 | [[NSAlert alertWithError:error] runModal]; 183 | } 184 | }]; 185 | 186 | [requestOperation start]; 187 | } 188 | 189 | - (AFHTTPRequestOperation *)requestOperationForManager:(AFHTTPRequestOperationManager *)manager song:(SISong *)song { 190 | 191 | NSMutableURLRequest *request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:@"/WebObjects/MZDaap.woa/daap/databases/1/cloud-add" relativeToURL:manager.baseURL]]; 192 | [request setHTTPMethod:@"POST"]; 193 | 194 | NSDictionary *headers = @{ 195 | @"X-Apple-Store-Front" : @"143478-20,32", 196 | @"Client-iTunes-Sharing-Version" : @"3.12", 197 | @"Accept-Language" : @"pl-pl, pl;q=0.75, en-us;q=0.50, en;q=0.25", 198 | @"Client-Cloud-DAAP-Version" : @"1.0/iTunes-12.2.0.145", 199 | @"Accept-Encoding" : @"gzip", 200 | @"X-Apple-itre" : @"0", 201 | @"Client-DAAP-Version" : @"3.13", 202 | @"User-Agent" : @"iTunes/12.2 (Macintosh; OS X 10.10.4) AppleWebKit/0600.7.12", 203 | @"Connection" : @"keep-alive", 204 | @"Content-Type" : @"application/x-dmap-tagged", 205 | @"X-Dsid" : self.dsidTextField.stringValue, 206 | @"Cookie" : self.cookieTextField.stringValue, 207 | @"X-Guid" : self.guidTextField.stringValue, 208 | @"Content-Length" : @"77" 209 | }; 210 | 211 | 212 | unsigned int timeInterval = (unsigned int)([[NSDate date] timeIntervalSince1970]); 213 | unsigned int itunesIdentifier = (unsigned int)([song.itunesIdentifier integerValue]); 214 | 215 | NSMutableData *data = [[NSData dataWithHexString:[SIAppleRequestBodyString stringByReplacingOccurrencesOfString:@" " withString:@""]] mutableCopy]; 216 | 217 | NSData *timeIntervalData = [[NSData dataWithBytes:&timeInterval length:sizeof(timeInterval)] reversedData]; 218 | [data replaceBytesInRange:NSMakeRange(16, 4) withBytes:timeIntervalData.bytes]; 219 | 220 | NSData *itunesIdentifierData = [[NSData dataWithBytes:&itunesIdentifier length:sizeof(itunesIdentifier)] reversedData]; 221 | [data replaceBytesInRange:NSMakeRange(data.length-5, 5) withBytes:itunesIdentifierData.bytes]; 222 | 223 | [request setHTTPBody:data]; 224 | [request setAllHTTPHeaderFields:headers]; 225 | 226 | return [manager HTTPRequestOperationWithRequest:request success:nil failure:nil]; 227 | } 228 | 229 | - (NSData *)HTTPBodyForSong:(SISong *)song { 230 | return nil; 231 | } 232 | 233 | #pragma mark - 234 | 235 | - (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { 236 | return self.songs.count; 237 | } 238 | 239 | - (id)tableView:(NSTableView *)tableView objectValueForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { 240 | 241 | SISong *song = [self.songs objectAtIndex:row]; 242 | id value = [song valueForKey:tableColumn.identifier]; 243 | if ([tableColumn.identifier isEqualToString:@"matchingScore"]) { 244 | if (song.fetched) { 245 | if ([value floatValue] <= 0) { 246 | return @"Not Found"; 247 | } else { 248 | return value; 249 | } 250 | } else { 251 | return @"Couldn't fetched"; 252 | } 253 | } 254 | 255 | return value; 256 | } 257 | 258 | - (void)tableView:(NSTableView *)tableView willDisplayCell:(id)cell forTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row { 259 | SISong *song = [self.songs objectAtIndex:row]; 260 | if ([tableColumn.identifier isEqualToString:@"matchingScore"]) { 261 | if ([song.matchingScore doubleValue] >= 0.8) { 262 | [cell setTextColor:[NSColor greenColor]]; 263 | } else if ([song.matchingScore doubleValue] >= 0.4) { 264 | [cell setTextColor:[NSColor yellowColor]]; 265 | } else { 266 | [cell setTextColor:[NSColor redColor]]; 267 | } 268 | } 269 | } 270 | 271 | - (void)tableView:(NSTableView *)tableView sortDescriptorsDidChange:(NSArray *)oldDescriptors { 272 | NSArray *newDescriptors = [tableView sortDescriptors]; 273 | self.songs = [self.songs sortedArrayUsingDescriptors:newDescriptors]; 274 | [tableView reloadData]; 275 | } 276 | 277 | @end 278 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/View Controllers/SIMainMenuViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SIMainMenuViewController : NSViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/View Controllers/SIMainMenuViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import "SIMainMenuViewController.h" 10 | #import 11 | #import "SICSVImporter.h" 12 | #import 13 | #import "SISong.h" 14 | #import "NSString+Score.h" 15 | #import "SIImporterViewController.h" 16 | #import "SISpotifyURIImporterViewController.h" 17 | 18 | @interface SIMainMenuViewController () 19 | @property (nonatomic, strong) NSArray *songs; 20 | @end 21 | 22 | @implementation SIMainMenuViewController 23 | 24 | - (IBAction)importFromSpotifyURIsButtonTapped:(id)sender { 25 | [self performSegueWithIdentifier:@"importURI" sender:self]; 26 | } 27 | 28 | - (IBAction)importFromCSVButtonTapped:(id)sender { 29 | 30 | NSOpenPanel *openPanel = [NSOpenPanel openPanel]; 31 | [openPanel setCanChooseDirectories:NO]; 32 | [openPanel setAllowsMultipleSelection:NO]; 33 | [openPanel setMessage:@"Choose .csv file."]; 34 | [openPanel setAllowedFileTypes:@[@"csv",@"CSV"]]; 35 | 36 | __weak typeof(openPanel) weakOpenPanel = openPanel; 37 | [openPanel beginWithCompletionHandler:^(NSInteger result){ 38 | if (result == NSFileHandlingPanelOKButton) { 39 | [weakOpenPanel orderOut:weakOpenPanel]; 40 | NSURL *csvFileURL = [[weakOpenPanel URLs] objectAtIndex:0]; 41 | 42 | [DJProgressHUD showStatus:@"Parsing CSV file..." FromView:self.view]; 43 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{ 44 | SICSVImporter *parser = [[SICSVImporter alloc] init]; 45 | [parser importCSVWithContentsOfURL:csvFileURL completionHandler:^(NSArray *songs, NSError *error) { 46 | dispatch_async(dispatch_get_main_queue(), ^{ 47 | [DJProgressHUD dismiss]; 48 | [self findMusicUsingSongsArray:songs error:error]; 49 | }); 50 | }]; 51 | }); 52 | 53 | } 54 | }]; 55 | } 56 | - (IBAction)helpImportCSV:(id)sender { 57 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString: @"https://rawgit.com/watsonbox/exportify/master/exportify.html"]]; 58 | } 59 | 60 | - (IBAction)helpSpotifyURI:(id)sender { 61 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString: @"https://lupin.rocks/entry/seamlessly-import-your-spotify-playlists-into-itunes#uris"]]; 62 | } 63 | 64 | - (void)findMusicUsingSongsArray:(NSArray *)songs error:(NSError *)error { 65 | 66 | if (error) { 67 | NSAlert *alert = [NSAlert alertWithError:error]; 68 | [alert runModal]; 69 | } else { 70 | 71 | NSURLSessionConfiguration *sessionConfiguration = [NSURLSessionConfiguration defaultSessionConfiguration]; 72 | sessionConfiguration.HTTPMaximumConnectionsPerHost = 20; 73 | 74 | AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://itunes.apple.com"] sessionConfiguration:sessionConfiguration]; 75 | sessionManager.responseSerializer.acceptableContentTypes = [NSSet setWithObjects:@"text/html",@"application/json",nil]; 76 | [sessionManager.requestSerializer setValue:@"application/json; target=itml; charset=UTF-8" forHTTPHeaderField:@"Content-Type"]; 77 | [sessionManager.requestSerializer setValue:@"iTunes/12.2 (Macintosh; OS X 10.10.4) AppleWebKit/0600.7.12" forHTTPHeaderField:@"User-Agent"]; 78 | [sessionManager.requestSerializer setValue:@"https://apps.itunes.apple.com/files/desktop-music-app/" forHTTPHeaderField:@"Referer"]; 79 | [sessionManager.requestSerializer setValue:@"143478-20,32 t:music2" forHTTPHeaderField:@"X-Apple-Store-Front"]; 80 | [sessionManager.requestSerializer setValue:@"7200" forHTTPHeaderField:@"X-Apple-Tz"]; 81 | 82 | dispatch_group_t sessionGroup = dispatch_group_create(); 83 | 84 | // [DJProgressHUD showProgress:0.0 withStatus:@"Searching songs..." FromView:self.view]; 85 | 86 | __block NSInteger progress = 0; 87 | NSInteger numberOfSongs = songs.count; 88 | 89 | [songs enumerateObjectsUsingBlock:^(SISong *song, NSUInteger idx, BOOL *stop) { 90 | dispatch_group_enter(sessionGroup); 91 | 92 | [sessionManager GET:@"WebObjects/MZStore.woa/wa/search" parameters:@{@"clientApplication" : @"MusicPlayer", @"term" : song.trackName } success:^(NSURLSessionDataTask *task, id responseObject) { 93 | 94 | NSDictionary *resultsDictionary = responseObject[@"storePlatformData"][@"lockup"][@"results"]; 95 | NSArray *results = [resultsDictionary allValues]; 96 | 97 | [results enumerateObjectsUsingBlock:^(NSDictionary *resultObject, NSUInteger idx, BOOL *stop) { 98 | if ([resultObject[@"kind"] isEqualToString:@"song"]) { 99 | if (song.matchingScore && [song.matchingScore floatValue] >= 0.9) { 100 | *stop = YES; 101 | } 102 | 103 | NSString *resultObjectName = resultObject[@"name"]; 104 | NSString *resultObjectArtistName = resultObject[@"artistName"]; 105 | if ([resultObjectName.lowercaseString isEqualToString:song.trackName.lowercaseString] && [song.artistName.lowercaseString containsString:resultObjectArtistName.lowercaseString]) { 106 | song.itunesIdentifier = resultObject[@"id"]; 107 | song.matchingScore = @(1.0); 108 | 109 | } else if ([resultObjectName.lowercaseString isEqualToString:song.trackName.lowercaseString]) { 110 | song.itunesIdentifier = resultObject[@"id"]; 111 | song.matchingScore = @(0.9); 112 | 113 | } else if ([resultObjectName.lowercaseString scoreAgainst:song.trackName.lowercaseString] > 0.8) { 114 | song.itunesIdentifier = resultObject[@"id"]; 115 | song.matchingScore = @([resultObjectName.lowercaseString scoreAgainst:song.trackName.lowercaseString]); 116 | } else { 117 | CGFloat matchingScore = [resultObjectName.lowercaseString scoreAgainst:song.trackName.lowercaseString]; 118 | if (!song.matchingScore || matchingScore > [song.matchingScore floatValue]) { 119 | song.itunesIdentifier = resultObject[@"id"]; 120 | song.matchingScore = @(matchingScore); 121 | } 122 | } 123 | } 124 | }]; 125 | song.fetched = YES; 126 | progress++; 127 | [DJProgressHUD showProgress:(CGFloat)progress/(CGFloat)numberOfSongs withStatus:@"Searching songs..." FromView:self.view]; 128 | dispatch_group_leave(sessionGroup); 129 | 130 | } failure:^(NSURLSessionDataTask *task, NSError *error) { 131 | progress++; 132 | [DJProgressHUD showProgress:(CGFloat)progress/(CGFloat)numberOfSongs withStatus:@"Searching songs..." FromView:self.view]; 133 | dispatch_group_leave(sessionGroup); 134 | }]; 135 | }]; 136 | 137 | dispatch_group_notify(sessionGroup,dispatch_get_main_queue(),^{ 138 | self.songs = [songs copy]; 139 | [DJProgressHUD dismiss]; 140 | [self performSegueWithIdentifier:@"importCSV" sender:self]; 141 | }); 142 | } 143 | 144 | } 145 | 146 | - (void)prepareForSegue:(NSStoryboardSegue *)segue sender:(id)sender { 147 | if ([segue.identifier isEqualToString:@"importCSV"]) { 148 | SIImporterViewController *importerViewController = segue.destinationController; 149 | importerViewController.songs = self.songs; 150 | } else if ([segue.identifier isEqualToString:@"importURI"]) { 151 | SISpotifyURIImporterViewController *spotifyImporter = segue.destinationController; 152 | 153 | __weak typeof(self) weakSelf = self; 154 | spotifyImporter.compeltionHandler = ^(NSArray *songs, BOOL cancelled) { 155 | if (!cancelled) { 156 | [weakSelf findMusicUsingSongsArray:songs error:nil]; 157 | } 158 | }; 159 | } 160 | } 161 | 162 | @end 163 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/View Controllers/SISpotifyURIImporterViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // SISpotifyURIImporterViewController.h 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 13/07/15. 6 | // Copyright © 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | typedef void(^SISpotifyURIImporterViewControllerCompletionHandler)(NSArray *songs, BOOL canceled); 12 | 13 | @interface SISpotifyURIImporterViewController : NSViewController 14 | @property (nonatomic, copy) SISpotifyURIImporterViewControllerCompletionHandler compeltionHandler; 15 | @end 16 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporter/View Controllers/SISpotifyURIImporterViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // SISpotifyURIImporterViewController.m 3 | // SpotifyImporter 4 | // 5 | // Created by Michal Zaborowski on 13/07/15. 6 | // Copyright © 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import "SISpotifyURIImporterViewController.h" 10 | #import 11 | #import 12 | #import "SISong.h" 13 | 14 | @interface SISpotifyURIImporterViewController () 15 | @property (unsafe_unretained) IBOutlet NSTextView *textView; 16 | @end 17 | 18 | @implementation SISpotifyURIImporterViewController 19 | 20 | - (IBAction)helpButtonTapped:(id)sender { 21 | [[NSWorkspace sharedWorkspace] openURL:[NSURL URLWithString: @"https://lupin.rocks/entry/seamlessly-import-your-spotify-playlists-into-itunes#uris"]]; 22 | } 23 | - (IBAction)cancelButtonTapped:(id)sender { 24 | [self.view.window.sheetParent endSheet:self.view.window returnCode:NSModalResponseCancel]; 25 | if (self.compeltionHandler) { 26 | self.compeltionHandler(nil,YES); 27 | } 28 | 29 | } 30 | - (IBAction)continueButtonTapped:(id)sender { 31 | 32 | 33 | NSArray *lines = [self.textView.string componentsSeparatedByString:@"\n"]; 34 | NSMutableArray *splitedSongsURIs = [NSMutableArray array]; 35 | 36 | [lines enumerateObjectsUsingBlock:^(NSString *obj, NSUInteger idx, BOOL *stop) { 37 | NSString *URI = [obj stringByReplacingOccurrencesOfString:@"spotify:track:" withString:@""]; 38 | // Required. A comma-separated list of the Spotify IDs for the tracks. Maximum: 50 IDs. 39 | NSInteger groupIndex = idx / 50; 40 | NSMutableArray *groupedSongs = nil; 41 | if (splitedSongsURIs.count <= groupIndex) { 42 | groupedSongs = [NSMutableArray arrayWithCapacity:50]; 43 | splitedSongsURIs[groupIndex] = groupedSongs; 44 | } else { 45 | groupedSongs = splitedSongsURIs[groupIndex]; 46 | } 47 | [groupedSongs addObject:URI]; 48 | }]; 49 | 50 | AFHTTPSessionManager *sessionManager = [[AFHTTPSessionManager alloc] initWithBaseURL:[NSURL URLWithString:@"https://api.spotify.com"] sessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]]; 51 | 52 | dispatch_group_t sessionGroup = dispatch_group_create(); 53 | 54 | [DJProgressHUD showProgress:0.0 withStatus:@"Searching songs..." FromView:self.view]; 55 | 56 | __block NSError *networkError = nil; 57 | __block NSInteger progress = 0; 58 | NSInteger numberOfSongs = splitedSongsURIs.count; 59 | 60 | NSMutableArray *songs = [NSMutableArray array]; 61 | 62 | for (NSArray *songsURIs in splitedSongsURIs) { 63 | dispatch_group_enter(sessionGroup); 64 | 65 | [sessionManager GET:@"v1/tracks" parameters:@{ @"ids" : [songsURIs componentsJoinedByString:@","] } success:^(NSURLSessionDataTask *task, id responseObject) { 66 | 67 | NSArray *tracks = responseObject[@"tracks"]; 68 | [tracks enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) { 69 | NSDictionary *album = obj[@"album"]; 70 | NSArray *artists = obj[@"artists"]; 71 | NSMutableString *artistName = [[NSMutableString alloc] init]; 72 | [artists enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { 73 | if (idx <= 0) { 74 | [artistName appendString:obj[@"name"]]; 75 | } else { 76 | [artistName appendFormat:@" & %@",obj[@"name"]]; 77 | } 78 | }]; 79 | SISong *song = [SISong songWithTrackName:album[@"name"] artistName:[artistName copy]]; 80 | [songs addObject:song]; 81 | }]; 82 | 83 | 84 | progress++; 85 | [DJProgressHUD showProgress:(CGFloat)progress/(CGFloat)numberOfSongs withStatus:@"Fetching songs..." FromView:self.view]; 86 | 87 | dispatch_group_leave(sessionGroup); 88 | } failure:^(NSURLSessionDataTask *task, NSError *error) { 89 | progress++; 90 | [DJProgressHUD showProgress:(CGFloat)progress/(CGFloat)numberOfSongs withStatus:@"Fetching songs..." FromView:self.view]; 91 | 92 | networkError = error; 93 | dispatch_group_leave(sessionGroup); 94 | }]; 95 | } 96 | 97 | dispatch_group_notify(sessionGroup,dispatch_get_main_queue(),^{ 98 | 99 | [DJProgressHUD dismiss]; 100 | 101 | if (networkError) { 102 | [[NSAlert alertWithError:networkError] runModal]; 103 | } else { 104 | [self.view.window.sheetParent endSheet:self.view.window returnCode:NSModalResponseCancel]; 105 | if (self.compeltionHandler) { 106 | self.compeltionHandler([songs copy],NO); 107 | } 108 | } 109 | }); 110 | 111 | 112 | 113 | } 114 | 115 | @end 116 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporterTests/SpotifyImporterTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // SpotifyImporterTests.m 3 | // SpotifyImporterTests 4 | // 5 | // Created by Michal Zaborowski on 12.07.2015. 6 | // Copyright (c) 2015 Michal Zaborowski. All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | 12 | @interface SpotifyImporterTests : XCTestCase 13 | 14 | @end 15 | 16 | @implementation SpotifyImporterTests 17 | 18 | - (void)setUp { 19 | [super setUp]; 20 | // Put setup code here. This method is called before the invocation of each test method in the class. 21 | } 22 | 23 | - (void)tearDown { 24 | // Put teardown code here. This method is called after the invocation of each test method in the class. 25 | [super tearDown]; 26 | } 27 | 28 | - (void)testExample { 29 | // This is an example of a functional test case. 30 | XCTAssert(YES, @"Pass"); 31 | } 32 | 33 | - (void)testPerformanceExample { 34 | // This is an example of a performance test case. 35 | [self measureBlock:^{ 36 | // Put the code you want to measure the time of here. 37 | }]; 38 | } 39 | 40 | @end 41 | -------------------------------------------------------------------------------- /SpotifyImporter/SpotifyImporterTests/Supporting Files/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | me.zaborowski.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | --------------------------------------------------------------------------------