VLCHTTPConnection.m 28 KB

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