VLCHTTPConnection.m 28 KB

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