FSCheckContentTypeRequest.m 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. * This file is part of the FreeStreamer project,
  3. * (C)Copyright 2011-2018 Matias Muhonen <mmu@iki.fi> 穆马帝
  4. * See the file ''LICENSE'' for using the code.
  5. *
  6. * https://github.com/muhku/FreeStreamer
  7. */
  8. #import "FSCheckContentTypeRequest.h"
  9. @interface FSCheckContentTypeRequest ()
  10. - (BOOL)guessContentTypeByUrl:(NSURLResponse *)response;
  11. @end
  12. @implementation FSCheckContentTypeRequest
  13. - (id)init
  14. {
  15. self = [super init];
  16. if (self) {
  17. _format = kFSFileFormatUnknown;
  18. _playlist = NO;
  19. _xml = NO;
  20. }
  21. return self;
  22. }
  23. - (void)start
  24. {
  25. if (_task) {
  26. return;
  27. }
  28. _format = kFSFileFormatUnknown;
  29. _playlist = NO;
  30. _contentType = @"";
  31. NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:_url
  32. cachePolicy:NSURLRequestReloadIgnoringCacheData
  33. timeoutInterval:10.0];
  34. [request setHTTPMethod:@"HEAD"];
  35. NSURLSession *session = [NSURLSession sessionWithConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]
  36. delegate:self
  37. delegateQueue:[NSOperationQueue mainQueue]];
  38. @synchronized (self) {
  39. _task = [session dataTaskWithRequest:request];
  40. }
  41. [_task resume];
  42. if (!_task) {
  43. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  44. NSLog(@"FSCheckContentTypeRequest: Unable to open connection for URL: %@", _url);
  45. #endif
  46. self.onFailure();
  47. return;
  48. }
  49. }
  50. - (void)cancel
  51. {
  52. if (!_task) {
  53. return;
  54. }
  55. @synchronized (self) {
  56. [_task cancel];
  57. _task = nil;
  58. }
  59. }
  60. /*
  61. * =======================================
  62. * Properties
  63. * =======================================
  64. */
  65. - (FSFileFormat)format
  66. {
  67. return _format;
  68. }
  69. - (NSString *)contentType
  70. {
  71. return _contentType;
  72. }
  73. - (BOOL)playlist
  74. {
  75. return _playlist;
  76. }
  77. - (BOOL)xml
  78. {
  79. return _xml;
  80. }
  81. /*
  82. * =======================================
  83. * NSURLSessionDelegate
  84. * =======================================
  85. */
  86. - (void)URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dataTask
  87. didReceiveResponse:(NSURLResponse *)response
  88. completionHandler:(void (^)(NSURLSessionResponseDisposition disposition))completionHandler {
  89. _contentType = response.MIMEType;
  90. _format = kFSFileFormatUnknown;
  91. _playlist = NO;
  92. NSInteger statusCode = ((NSHTTPURLResponse *)response).statusCode;
  93. if (statusCode >= 200 && statusCode <= 299) {
  94. // Only use the content type if the response indicated success (2xx)
  95. if ([_contentType isEqualToString:@"audio/mpeg"]) {
  96. _format = kFSFileFormatMP3;
  97. } else if ([_contentType isEqualToString:@"audio/x-wav"]) {
  98. _format = kFSFileFormatWAVE;
  99. } else if ([_contentType isEqualToString:@"audio/x-aifc"]) {
  100. _format = kFSFileFormatAIFC;
  101. } else if ([_contentType isEqualToString:@"audio/x-aiff"]) {
  102. _format = kFSFileFormatAIFF;
  103. } else if ([_contentType isEqualToString:@"audio/x-m4a"]) {
  104. _format = kFSFileFormatM4A;
  105. } else if ([_contentType isEqualToString:@"audio/mp4"]) {
  106. _format = kFSFileFormatMPEG4;
  107. } else if ([_contentType isEqualToString:@"audio/x-caf"]) {
  108. _format = kFSFileFormatCAF;
  109. } else if ([_contentType isEqualToString:@"audio/aac"] ||
  110. [_contentType isEqualToString:@"audio/aacp"]) {
  111. _format = kFSFileFormatAAC_ADTS;
  112. } else if ([_contentType isEqualToString:@"audio/x-mpegurl"] ||
  113. [_contentType isEqualToString:@"application/x-mpegurl"]) {
  114. _format = kFSFileFormatM3UPlaylist;
  115. _playlist = YES;
  116. } else if ([_contentType isEqualToString:@"audio/x-scpls"] ||
  117. [_contentType isEqualToString:@"application/pls+xml"]) {
  118. _format = kFSFileFormatPLSPlaylist;
  119. _playlist = YES;
  120. } else if ([_contentType isEqualToString:@"text/xml"] ||
  121. [_contentType isEqualToString:@"application/xml"]) {
  122. _format = kFSFileFormatXML;
  123. _xml = YES;
  124. } else {
  125. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  126. NSLog(@"FSCheckContentTypeRequest: Cannot resolve %@, guessing the content type by URL: %@", _contentType, _url);
  127. #endif
  128. [self guessContentTypeByUrl:response];
  129. }
  130. } else {
  131. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  132. NSLog(@"FSCheckContentTypeRequest: Invalid HTTP status code received %li, guessing the content type by URL: %@", (long)statusCode, _url);
  133. #endif
  134. [self guessContentTypeByUrl:response];
  135. }
  136. _task = nil;
  137. self.onCompletion();
  138. }
  139. - (void)URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task
  140. didCompleteWithError:(nullable NSError *)error {
  141. @synchronized (self) {
  142. _task = nil;
  143. _format = kFSFileFormatUnknown;
  144. _playlist = NO;
  145. }
  146. // Still, try if we could resolve the content type by the URL
  147. if ([self guessContentTypeByUrl:nil]) {
  148. self.onCompletion();
  149. } else {
  150. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  151. NSLog(@"FSCheckContentTypeRequest: Unable to determine content-type for the URL: %@, error %@", _url, [error localizedDescription]);
  152. #endif
  153. self.onFailure();
  154. }
  155. }
  156. /*
  157. * =======================================
  158. * Private
  159. * =======================================
  160. */
  161. - (BOOL)guessContentTypeByUrl:(NSURLResponse *)response
  162. {
  163. /* The server did not provide meaningful content type;
  164. last resort: check the file suffix, if there is one */
  165. NSString *absoluteUrl;
  166. if (response) {
  167. absoluteUrl = [response.URL absoluteString];
  168. } else {
  169. absoluteUrl = [_url absoluteString];
  170. }
  171. if ([absoluteUrl hasSuffix:@".mp3"]) {
  172. _format = kFSFileFormatMP3;
  173. } else if ([absoluteUrl hasSuffix:@".mp4"]) {
  174. _format = kFSFileFormatMPEG4;
  175. } else if ([absoluteUrl hasSuffix:@".m3u"]) {
  176. _format = kFSFileFormatM3UPlaylist;
  177. _playlist = YES;
  178. } else if ([absoluteUrl hasSuffix:@".pls"]) {
  179. _format = kFSFileFormatPLSPlaylist;
  180. _playlist = YES;
  181. } else if ([absoluteUrl hasSuffix:@".xml"]) {
  182. _format = kFSFileFormatXML;
  183. _xml = YES;
  184. } else {
  185. #if defined(DEBUG) || (TARGET_IPHONE_SIMULATOR)
  186. NSLog(@"FSCheckContentTypeRequest: Failed to determine content type from the URL: %@", _url);
  187. #endif
  188. /*
  189. * Failed to guess the content type based on the URL.
  190. */
  191. return NO;
  192. }
  193. /*
  194. * We have determined a content-type.
  195. */
  196. return YES;
  197. }
  198. @end