VLCHTTPConnection.m 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508
  1. /*****************************************************************************
  2. * VLCHTTPConnection.m
  3. * VLC for iOS
  4. *****************************************************************************
  5. * Copyright (c) 2013-2015 VideoLAN. All rights reserved.
  6. * $Id$
  7. *
  8. * Authors: Felix Paul Kühne <fkuehne # videolan.org>
  9. * Jean-Baptiste Kempf <jb # videolan.org>
  10. *
  11. * Refer to the COPYING file of the official project for license.
  12. *****************************************************************************/
  13. #import "VLCAppDelegate.h"
  14. #import "VLCHTTPConnection.h"
  15. #import "HTTPConnection.h"
  16. #import "MultipartFormDataParser.h"
  17. #import "HTTPMessage.h"
  18. #import "HTTPDataResponse.h"
  19. #import "HTTPFileResponse.h"
  20. #import "MultipartMessageHeaderField.h"
  21. #import "VLCHTTPUploaderController.h"
  22. #import "HTTPDynamicFileResponse.h"
  23. #import "VLCThumbnailsCache.h"
  24. #import "NSString+SupportedMedia.h"
  25. #import "UIDevice+VLC.h"
  26. @interface VLCHTTPConnection()
  27. {
  28. MultipartFormDataParser *_parser;
  29. NSFileHandle *_storeFile;
  30. NSString *_filepath;
  31. UInt64 _contentLength;
  32. UInt64 _receivedContent;
  33. }
  34. @property (nonatomic) VLCHTTPUploaderController *uploadController;
  35. @end
  36. @implementation VLCHTTPConnection
  37. - (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path
  38. {
  39. // Add support for POST
  40. if ([method isEqualToString:@"POST"]) {
  41. if ([path isEqualToString:@"/upload.json"])
  42. return YES;
  43. }
  44. return [super supportsMethod:method atPath:path];
  45. }
  46. - (BOOL)expectsRequestBodyFromMethod:(NSString *)method atPath:(NSString *)path
  47. {
  48. // Inform HTTP server that we expect a body to accompany a POST request
  49. if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.json"]) {
  50. // here we need to make sure, boundary is set in header
  51. NSString* contentType = [request headerField:@"Content-Type"];
  52. NSUInteger paramsSeparator = [contentType rangeOfString:@";"].location;
  53. if (NSNotFound == paramsSeparator)
  54. return NO;
  55. if (paramsSeparator >= contentType.length - 1)
  56. return NO;
  57. NSString* type = [contentType substringToIndex:paramsSeparator];
  58. if (![type isEqualToString:@"multipart/form-data"]) {
  59. // we expect multipart/form-data content type
  60. return NO;
  61. }
  62. // enumerate all params in content-type, and find boundary there
  63. NSArray* params = [[contentType substringFromIndex:paramsSeparator + 1] componentsSeparatedByString:@";"];
  64. for (NSString* param in params) {
  65. paramsSeparator = [param rangeOfString:@"="].location;
  66. if ((NSNotFound == paramsSeparator) || paramsSeparator >= param.length - 1)
  67. continue;
  68. NSString* paramName = [param substringWithRange:NSMakeRange(1, paramsSeparator-1)];
  69. NSString* paramValue = [param substringFromIndex:paramsSeparator+1];
  70. if ([paramName isEqualToString: @"boundary"])
  71. // let's separate the boundary from content-type, to make it more handy to handle
  72. [request setHeaderField:@"boundary" value:paramValue];
  73. }
  74. // check if boundary specified
  75. if (nil == [request headerField:@"boundary"])
  76. return NO;
  77. return YES;
  78. }
  79. return [super expectsRequestBodyFromMethod:method atPath:path];
  80. }
  81. - (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path
  82. {
  83. if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.json"]) {
  84. return [[HTTPDataResponse alloc] initWithData:[@"\"OK\"" dataUsingEncoding:NSUTF8StringEncoding]];
  85. }
  86. if ([path hasPrefix:@"/download/"]) {
  87. NSString *filePath = [[path stringByReplacingOccurrencesOfString:@"/download/" withString:@""]stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  88. HTTPFileResponse *fileResponse = [[HTTPFileResponse alloc] initWithFilePath:filePath forConnection:self];
  89. fileResponse.contentType = @"application/octet-stream";
  90. return fileResponse;
  91. }
  92. if ([path hasPrefix:@"/thumbnail"]) {
  93. NSString *filePath = [[path stringByReplacingOccurrencesOfString:@"/thumbnail/" withString:@""]stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
  94. filePath = [filePath stringByReplacingOccurrencesOfString:@".png" withString:@""];
  95. NSManagedObjectContext *moc = [[MLMediaLibrary sharedMediaLibrary] managedObjectContext];
  96. NSPersistentStoreCoordinator *psc = [moc persistentStoreCoordinator];
  97. NSManagedObject *mo = [moc existingObjectWithID:[psc managedObjectIDForURIRepresentation:[NSURL URLWithString:filePath]] error:nil];
  98. NSData *theData;
  99. if ([mo isKindOfClass:[MLFile class]])
  100. theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForMediaFile:(MLFile *)mo]);
  101. else if ([mo isKindOfClass:[MLShow class]])
  102. theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForShow:(MLShow *)mo]);
  103. else if ([mo isKindOfClass:[MLLabel class]])
  104. theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForLabel:(MLLabel *)mo]);
  105. else if ([mo isKindOfClass:[MLAlbum class]])
  106. theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForMediaFile:[[(MLAlbum *)mo tracks].anyObject files].anyObject]);
  107. else if ([mo isKindOfClass:[MLAlbumTrack class]])
  108. theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForMediaFile:[(MLAlbumTrack *)mo files].anyObject]);
  109. else if ([mo isKindOfClass:[MLShowEpisode class]])
  110. theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForMediaFile:[(MLShowEpisode *)mo files].anyObject]);
  111. if (theData) {
  112. HTTPDataResponse *dataResponse = [[HTTPDataResponse alloc] initWithData:theData];
  113. dataResponse.contentType = @"image/png";
  114. return dataResponse;
  115. }
  116. }
  117. NSString *filePath = [self filePathForURI:path];
  118. NSString *documentRoot = [config documentRoot];
  119. NSString *relativePath = [filePath substringFromIndex:[documentRoot length]];
  120. if (([relativePath isEqualToString:@"/index.html"]) || ([relativePath isEqualToString:@"/libMediaVLC.xml"])) {
  121. NSMutableArray *allMedia = [[NSMutableArray alloc] init];
  122. /* add all albums */
  123. NSArray *allAlbums = [MLAlbum allAlbums];
  124. for (MLAlbum *album in allAlbums) {
  125. if (album.name.length > 0 && album.tracks.count > 1)
  126. [allMedia addObject:album];
  127. }
  128. /* add all shows */
  129. NSArray *allShows = [MLShow allShows];
  130. for (MLShow *show in allShows) {
  131. if (show.name.length > 0 && show.episodes.count > 1)
  132. [allMedia addObject:show];
  133. }
  134. /* add all folders*/
  135. NSArray *allFolders = [MLLabel allLabels];
  136. for (MLLabel *folder in allFolders)
  137. [allMedia addObject:folder];
  138. /* add all remaining files */
  139. NSArray *allFiles = [MLFile allFiles];
  140. for (MLFile *file in allFiles) {
  141. if (file.labels.count > 0) continue;
  142. if (!file.isShowEpisode && !file.isAlbumTrack)
  143. [allMedia addObject:file];
  144. else if (file.isShowEpisode) {
  145. if (file.showEpisode.show.episodes.count < 2)
  146. [allMedia addObject:file];
  147. } else if (file.isAlbumTrack) {
  148. if (file.albumTrack.album.tracks.count < 2)
  149. [allMedia addObject:file];
  150. }
  151. }
  152. NSMutableArray *mediaInHtml = [[NSMutableArray alloc] initWithCapacity:allMedia.count];
  153. NSMutableArray *mediaInXml = [[NSMutableArray alloc] initWithCapacity:allMedia.count];
  154. self.uploadController = [[VLCHTTPUploaderController alloc] init];
  155. NSString *hostName = [self.uploadController hostname];
  156. NSString *pathLibrary = [NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
  157. NSString *duration;
  158. for (NSManagedObject *mo in allMedia) {
  159. if ([mo isKindOfClass:[MLFile class]]) {
  160. duration = [[VLCTime timeWithNumber:[(MLFile *)mo duration]] stringValue];
  161. [mediaInHtml addObject:[NSString stringWithFormat:
  162. @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
  163. <a href=\"download/%@\" class=\"inner\"> \
  164. <div class=\"down icon\"></div> \
  165. <div class=\"infos\"> \
  166. <span class=\"first-line\">%@</span> \
  167. <span class=\"second-line\">%@ - %0.2f MB</span> \
  168. </div> \
  169. </a> \
  170. </div>",
  171. mo.objectID.URIRepresentation,
  172. [[(MLFile *)mo url] stringByReplacingOccurrencesOfString:@"file://"withString:@""],
  173. [(MLFile *)mo title],
  174. duration, (float)([(MLFile *)mo fileSizeInBytes] / 1e6)]];
  175. if ([relativePath isEqualToString:@"/libMediaVLC.xml"]) {
  176. NSString *pathSub = [self _checkSubtitleFound:[(MLFile *)mo url]];
  177. if (![pathSub isEqualToString:@""])
  178. pathSub = [NSString stringWithFormat:@"http://%@/download/%@", hostName, pathSub];
  179. [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@\" thumb=\"http://%@/download/%@/Thumbnails/File/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\" pathSubtitle=\"%@\"/>", [(MLFile *)mo title], hostName, pathLibrary, [[NSString stringWithFormat:@"%@", mo.objectID.URIRepresentation] lastPathComponent], duration, [(MLFile *)mo fileSizeInBytes], hostName, [[(MLFile *)mo url] stringByReplacingOccurrencesOfString:@"file://"withString:@""], pathSub]];
  180. }
  181. }
  182. else if ([mo isKindOfClass:[MLShow class]]) {
  183. NSArray *episodes = [(MLShow *)mo sortedEpisodes];
  184. [mediaInHtml addObject:[NSString stringWithFormat:
  185. @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
  186. <a href=\"#\" class=\"inner folder\"> \
  187. <div class=\"open icon\"></div> \
  188. <div class=\"infos\"> \
  189. <span class=\"first-line\">%@</span> \
  190. <span class=\"second-line\">%lu items</span> \
  191. </div> \
  192. </a> \
  193. <div class=\"content\">",
  194. mo.objectID.URIRepresentation,
  195. [(MLShow *)mo name],
  196. (unsigned long)[episodes count]]];
  197. for (MLShowEpisode *showEp in episodes) {
  198. duration = [[VLCTime timeWithNumber:[(MLFile *)[[showEp files] anyObject] duration]] stringValue];
  199. [mediaInHtml addObject:[NSString stringWithFormat:
  200. @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
  201. <a href=\"download/%@\" class=\"inner\"> \
  202. <div class=\"down icon\"></div> \
  203. <div class=\"infos\"> \
  204. <span class=\"first-line\">S%@E%@ - %@</span> \
  205. <span class=\"second-line\">%@ - %0.2f MB</span> \
  206. </div> \
  207. </a> \
  208. </div>",
  209. showEp.objectID.URIRepresentation,
  210. [[(MLFile *)[[showEp files] anyObject] url] stringByReplacingOccurrencesOfString:@"file://"withString:@""],
  211. showEp.seasonNumber,
  212. showEp.episodeNumber,
  213. showEp.name,
  214. duration, (float)([(MLFile *)[[showEp files] anyObject] fileSizeInBytes] / 1e6)]];
  215. if ([relativePath isEqualToString:@"/libMediaVLC.xml"]) {
  216. NSString *pathSub = [self _checkSubtitleFound:[(MLFile *)[[showEp files] anyObject] url]];
  217. if (![pathSub isEqualToString:@""])
  218. pathSub = [NSString stringWithFormat:@"http://%@/download/%@", hostName, pathSub];
  219. [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@ - S%@E%@\" thumb=\"http://%@/download/%@/Thumbnails/File/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\" pathSubtitle=\"%@\"/>", [(MLShow *)mo name], showEp.seasonNumber, showEp.episodeNumber, hostName, pathLibrary, [[NSString stringWithFormat:@"%@", showEp.objectID.URIRepresentation] lastPathComponent], duration, [(MLFile *)[[showEp files] anyObject] fileSizeInBytes], hostName, [[(MLFile *)[[showEp files] anyObject] url] stringByReplacingOccurrencesOfString:@"file://"withString:@""], pathSub]];
  220. }
  221. }
  222. [mediaInHtml addObject:@"</div></div>"];
  223. } else if ([mo isKindOfClass:[MLLabel class]]) {
  224. NSArray *folderItems = [(MLLabel *)mo sortedFolderItems];
  225. [mediaInHtml addObject:[NSString stringWithFormat:
  226. @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
  227. <a href=\"#\" class=\"inner folder\"> \
  228. <div class=\"open icon\"></div> \
  229. <div class=\"infos\"> \
  230. <span class=\"first-line\">%@</span> \
  231. <span class=\"second-line\">%lu items</span> \
  232. </div> \
  233. </a> \
  234. <div class=\"content\">",
  235. mo.objectID.URIRepresentation,
  236. [(MLLabel *)mo name],
  237. (unsigned long)[folderItems count]]];
  238. for (MLFile *file in folderItems) {
  239. duration = [[VLCTime timeWithNumber:[file duration]] stringValue];
  240. [mediaInHtml addObject:[NSString stringWithFormat:
  241. @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
  242. <a href=\"download/%@\" class=\"inner\"> \
  243. <div class=\"down icon\"></div> \
  244. <div class=\"infos\"> \
  245. <span class=\"first-line\">%@</span> \
  246. <span class=\"second-line\">%@ - %0.2f MB</span> \
  247. </div> \
  248. </a> \
  249. </div>",
  250. file.objectID.URIRepresentation,
  251. [[file url] stringByReplacingOccurrencesOfString:@"file://"withString:@""],
  252. file.title,
  253. duration, (float)([file fileSizeInBytes] / 1e6)]];
  254. if ([relativePath isEqualToString:@"/libMediaVLC.xml"]) {
  255. NSString *pathSub = [self _checkSubtitleFound:[file url]];
  256. if (![pathSub isEqualToString:@""])
  257. pathSub = [NSString stringWithFormat:@"http://%@/download/%@", hostName, pathSub];
  258. [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@\" thumb=\"http://%@/download/%@/Thumbnails/File/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\" pathSubtitle=\"%@\"/>", file.title, hostName, pathLibrary, [[NSString stringWithFormat:@"%@", file.objectID.URIRepresentation] lastPathComponent], duration, [file fileSizeInBytes], hostName, [[file url] stringByReplacingOccurrencesOfString:@"file://"withString:@""], pathSub]];
  259. }
  260. }
  261. [mediaInHtml addObject:@"</div></div>"];
  262. } else if ([mo isKindOfClass:[MLAlbum class]]) {
  263. NSArray *albumTracks = [(MLAlbum *)mo sortedTracks];
  264. [mediaInHtml addObject:[NSString stringWithFormat:
  265. @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
  266. <a href=\"#\" class=\"inner folder\"> \
  267. <div class=\"open icon\"></div> \
  268. <div class=\"infos\"> \
  269. <span class=\"first-line\">%@</span> \
  270. <span class=\"second-line\">%lu items</span> \
  271. </div> \
  272. </a> \
  273. <div class=\"content\">",
  274. mo.objectID.URIRepresentation,
  275. [(MLAlbum *)mo name],
  276. (unsigned long)[albumTracks count]]];
  277. for (MLAlbumTrack *track in albumTracks) {
  278. duration = [[VLCTime timeWithNumber:[(MLFile *)[[track files] anyObject] duration]] stringValue];
  279. [mediaInHtml addObject:[NSString stringWithFormat:
  280. @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
  281. <a href=\"download/%@\" class=\"inner\"> \
  282. <div class=\"down icon\"></div> \
  283. <div class=\"infos\"> \
  284. <span class=\"first-line\">%@</span> \
  285. <span class=\"second-line\">%@ - %0.2f MB</span> \
  286. </div> \
  287. </a> \
  288. </div>",
  289. track.objectID.URIRepresentation,
  290. [[(MLFile *)[[track files] anyObject] url] stringByReplacingOccurrencesOfString:@"file://"withString:@""],
  291. track.title,
  292. duration, (float)([(MLFile *)[[track files] anyObject] fileSizeInBytes] / 1e6)]];
  293. if ([relativePath isEqualToString:@"/libMediaVLC.xml"])
  294. [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@\" thumb=\"http://%@/download/%@/Thumbnails/File/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\" pathSubtitle=\"\"/>", track.title, hostName, pathLibrary, [[NSString stringWithFormat:@"%@", track.objectID.URIRepresentation] lastPathComponent], duration, [(MLFile *)[[track files] anyObject] fileSizeInBytes], hostName, [[(MLFile *)[[track files] anyObject] url] stringByReplacingOccurrencesOfString:@"file://"withString:@""]]];
  295. }
  296. [mediaInHtml addObject:@"</div></div>"];
  297. }
  298. }
  299. NSString *deviceModel = [[UIDevice currentDevice] model];
  300. NSDictionary *replacementDict;
  301. if ([relativePath isEqualToString:@"/libMediaVLC.xml"]) {
  302. replacementDict = @{@"FILES" : [mediaInXml componentsJoinedByString:@" "],
  303. @"NB_FILE" : [NSString stringWithFormat:@"%li", (unsigned long)mediaInXml.count],
  304. @"LIB_TITLE" : [[UIDevice currentDevice] name]};
  305. } else
  306. replacementDict = @{@"FILES" : [mediaInHtml componentsJoinedByString:@" "],
  307. @"WEBINTF_TITLE" : NSLocalizedString(@"WEBINTF_TITLE", nil),
  308. @"WEBINTF_DROPFILES" : NSLocalizedString(@"WEBINTF_DROPFILES", nil),
  309. @"WEBINTF_DROPFILES_LONG" : [NSString stringWithFormat:NSLocalizedString(@"WEBINTF_DROPFILES_LONG", nil), deviceModel],
  310. @"WEBINTF_DOWNLOADFILES" : NSLocalizedString(@"WEBINTF_DOWNLOADFILES", nil),
  311. @"WEBINTF_DOWNLOADFILES_LONG" : [NSString stringWithFormat: NSLocalizedString(@"WEBINTF_DOWNLOADFILES_LONG", nil), deviceModel]};
  312. return [[HTTPDynamicFileResponse alloc] initWithFilePath:[self filePathForURI:path]
  313. forConnection:self
  314. separator:@"%%"
  315. replacementDictionary:replacementDict];
  316. } else if ([relativePath isEqualToString:@"/style.css"]) {
  317. NSDictionary *replacementDict = @{@"WEBINTF_TITLE" : NSLocalizedString(@"WEBINTF_TITLE", nil)};
  318. return [[HTTPDynamicFileResponse alloc] initWithFilePath:[self filePathForURI:path]
  319. forConnection:self
  320. separator:@"%%"
  321. replacementDictionary:replacementDict];
  322. }
  323. return [super httpResponseForMethod:method URI:path];
  324. }
  325. - (void)prepareForBodyWithSize:(UInt64)contentLength
  326. {
  327. // set up mime parser
  328. NSString* boundary = [request headerField:@"boundary"];
  329. _parser = [[MultipartFormDataParser alloc] initWithBoundary:boundary formEncoding:NSUTF8StringEncoding];
  330. _parser.delegate = self;
  331. APLog(@"expecting file of size %lli kB", contentLength / 1024);
  332. _contentLength = contentLength;
  333. }
  334. - (void)processBodyData:(NSData *)postDataChunk
  335. {
  336. /* append data to the parser. It will invoke callbacks to let us handle
  337. * parsed data. */
  338. [_parser appendData:postDataChunk];
  339. _receivedContent += postDataChunk.length;
  340. APLog(@"received %lli kB (%lli %%)", _receivedContent / 1024, ((_receivedContent * 100) / _contentLength));
  341. }
  342. //-----------------------------------------------------------------
  343. #pragma mark multipart form data parser delegate
  344. - (void)processStartOfPartWithHeader:(MultipartMessageHeader*) header
  345. {
  346. /* in this sample, we are not interested in parts, other then file parts.
  347. * check content disposition to find out filename */
  348. MultipartMessageHeaderField* disposition = (header.fields)[@"Content-Disposition"];
  349. NSString* filename = [(disposition.params)[@"filename"] lastPathComponent];
  350. if ((nil == filename) || [filename isEqualToString: @""]) {
  351. // it's either not a file part, or
  352. // an empty form sent. we won't handle it.
  353. return;
  354. }
  355. // create the path where to store the media temporarily
  356. NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
  357. NSString* uploadDirPath = [searchPaths[0] stringByAppendingPathComponent:@"Upload"];
  358. NSFileManager *fileManager = [NSFileManager defaultManager];
  359. BOOL isDir = YES;
  360. if (![fileManager fileExistsAtPath:uploadDirPath isDirectory:&isDir ])
  361. [fileManager createDirectoryAtPath:uploadDirPath withIntermediateDirectories:YES attributes:nil error:nil];
  362. _filepath = [uploadDirPath stringByAppendingPathComponent: filename];
  363. NSNumber *freeSpace = [[UIDevice currentDevice] freeDiskspace];
  364. if (_contentLength >= freeSpace.longLongValue) {
  365. /* avoid deadlock since we are on a background thread */
  366. [self performSelectorOnMainThread:@selector(notifyUserAboutEndOfFreeStorage:) withObject:filename waitUntilDone:NO];
  367. [self handleResourceNotFound];
  368. [self stop];
  369. return;
  370. }
  371. APLog(@"Saving file to %@", _filepath);
  372. if (![fileManager createDirectoryAtPath:uploadDirPath withIntermediateDirectories:true attributes:nil error:nil])
  373. APLog(@"Could not create directory at path: %@", _filepath);
  374. if (![fileManager createFileAtPath:_filepath contents:nil attributes:nil])
  375. APLog(@"Could not create file at path: %@", _filepath);
  376. _storeFile = [NSFileHandle fileHandleForWritingAtPath:_filepath];
  377. [(VLCAppDelegate*)[UIApplication sharedApplication].delegate networkActivityStarted];
  378. [(VLCAppDelegate*)[UIApplication sharedApplication].delegate disableIdleTimer];
  379. }
  380. - (void)notifyUserAboutEndOfFreeStorage:(NSString *)filename
  381. {
  382. UIAlertView *alert = [[UIAlertView alloc] initWithTitle:NSLocalizedString(@"DISK_FULL", nil)
  383. message:[NSString stringWithFormat:
  384. NSLocalizedString(@"DISK_FULL_FORMAT", nil),
  385. filename,
  386. [[UIDevice currentDevice] model]]
  387. delegate:self
  388. cancelButtonTitle:NSLocalizedString(@"BUTTON_OK", nil)
  389. otherButtonTitles:nil];
  390. [alert show];
  391. }
  392. - (void)processContent:(NSData*)data WithHeader:(MultipartMessageHeader*) header
  393. {
  394. // here we just write the output from parser to the file.
  395. if (_storeFile) {
  396. @try {
  397. [_storeFile writeData:data];
  398. }
  399. @catch (NSException *exception) {
  400. APLog(@"File to write further data because storage is full.");
  401. [_storeFile closeFile];
  402. _storeFile = nil;
  403. /* don't block */
  404. [self performSelector:@selector(stop) withObject:nil afterDelay:0.1];
  405. }
  406. }
  407. }
  408. - (void)processEndOfPartWithHeader:(MultipartMessageHeader*)header
  409. {
  410. // as the file part is over, we close the file.
  411. APLog(@"closing file");
  412. [_storeFile closeFile];
  413. _storeFile = nil;
  414. }
  415. - (BOOL)shouldDie
  416. {
  417. if (_filepath) {
  418. if (_filepath.length > 0)
  419. [[(VLCAppDelegate*)[UIApplication sharedApplication].delegate uploadController] moveFileFrom:_filepath];
  420. }
  421. return [super shouldDie];
  422. }
  423. #pragma mark subtitle
  424. - (NSMutableArray *)_listOfSubtitle
  425. {
  426. NSMutableArray *listOfSubtitle = [[NSMutableArray alloc] init];
  427. NSString *documentsDirectory = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0];
  428. NSArray *allfiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:documentsDirectory error:nil];
  429. NSString *filePath;
  430. for (int count = 0; count < allfiles.count; count++) {
  431. filePath = [[NSString stringWithFormat:@"%@/%@", documentsDirectory, allfiles[count]] stringByReplacingOccurrencesOfString:@"file://"withString:@""];
  432. if ([filePath isSupportedSubtitleFormat])
  433. [listOfSubtitle addObject:filePath];
  434. }
  435. return listOfSubtitle;
  436. }
  437. - (NSString *)_checkSubtitleFound:(NSString *)fileURL
  438. {
  439. NSString *subtitlePath = @"";
  440. NSString *fileName = [[fileURL lastPathComponent] stringByDeletingPathExtension];
  441. NSMutableArray *listOfSubtitle = [[NSMutableArray alloc] init];
  442. listOfSubtitle = [self _listOfSubtitle];
  443. NSString *fileSub;
  444. for (int count = 0; count < listOfSubtitle.count; count++) {
  445. fileSub = [NSString stringWithFormat:@"%@", listOfSubtitle[count]];
  446. if ([fileSub rangeOfString:fileName].location != NSNotFound)
  447. subtitlePath = [listOfSubtitle objectAtIndex:count];
  448. }
  449. return subtitlePath;
  450. }
  451. @end