VLCHTTPConnection.m 24.3 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
/*****************************************************************************
 * VLCHTTPConnection.m
 * VLC for iOS
 *****************************************************************************
 * Copyright (c) 2013 VideoLAN. All rights reserved.
 * $Id$
 *
 * Authors: Felix Paul Kühne <fkuehne # videolan.org>
 *          Jean-Baptiste Kempf <jb # videolan.org>
 *
 * Refer to the COPYING file of the official project for license.
 *****************************************************************************/
13 14 15 16 17 18 19 20 21

#import "VLCAppDelegate.h"
#import "VLCHTTPConnection.h"
#import "HTTPConnection.h"
#import "MultipartFormDataParser.h"
#import "HTTPMessage.h"
#import "HTTPDataResponse.h"
#import "HTTPFileResponse.h"
#import "MultipartMessageHeaderField.h"
22
#import "VLCHTTPUploaderController.h"
23
#import "HTTPDynamicFileResponse.h"
24
#import "VLCThumbnailsCache.h"
25 26 27

@interface VLCHTTPConnection()
{
28 29 30
    MultipartFormDataParser *_parser;
    NSFileHandle *_storeFile;
    NSString *_filepath;
31 32
    UInt64 _contentLength;
    UInt64 _receivedContent;
33
}
34 35 36

@property (nonatomic) VLCHTTPUploaderController *uploadController;

37 38
@end

39

40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
@implementation VLCHTTPConnection

- (BOOL)supportsMethod:(NSString *)method atPath:(NSString *)path
{
    // Add support for POST
    if ([method isEqualToString:@"POST"]) {
        if ([path isEqualToString:@"/upload.json"])
            return YES;
    }

    return [super supportsMethod:method atPath:path];
}

- (BOOL)expectsRequestBodyFromMethod:(NSString *)method atPath:(NSString *)path
{
    // Inform HTTP server that we expect a body to accompany a POST request
    if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.json"]) {
        // here we need to make sure, boundary is set in header
        NSString* contentType = [request headerField:@"Content-Type"];
        NSUInteger paramsSeparator = [contentType rangeOfString:@";"].location;
        if (NSNotFound == paramsSeparator)
            return NO;

        if (paramsSeparator >= contentType.length - 1)
            return NO;

        NSString* type = [contentType substringToIndex:paramsSeparator];
        if (![type isEqualToString:@"multipart/form-data"]) {
            // we expect multipart/form-data content type
            return NO;
        }

        // enumerate all params in content-type, and find boundary there
        NSArray* params = [[contentType substringFromIndex:paramsSeparator + 1] componentsSeparatedByString:@";"];
        for (NSString* param in params) {
            paramsSeparator = [param rangeOfString:@"="].location;
            if ((NSNotFound == paramsSeparator) || paramsSeparator >= param.length - 1)
                continue;

            NSString* paramName = [param substringWithRange:NSMakeRange(1, paramsSeparator-1)];
            NSString* paramValue = [param substringFromIndex:paramsSeparator+1];

            if ([paramName isEqualToString: @"boundary"])
                // let's separate the boundary from content-type, to make it more handy to handle
                [request setHeaderField:@"boundary" value:paramValue];
        }
        // check if boundary specified
        if (nil == [request headerField:@"boundary"])
            return NO;

        return YES;
    }
    return [super expectsRequestBodyFromMethod:method atPath:path];
}

- (NSObject<HTTPResponse> *)httpResponseForMethod:(NSString *)method URI:(NSString *)path
{
    if ([method isEqualToString:@"POST"] && [path isEqualToString:@"/upload.json"]) {
        return [[HTTPDataResponse alloc] initWithData:[@"\"OK\"" dataUsingEncoding:NSUTF8StringEncoding]];
    }
100 101
    if ([path hasPrefix:@"/download/"]) {
        NSString *filePath = [[path stringByReplacingOccurrencesOfString:@"/download/" withString:@""]stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
102 103 104
        HTTPFileResponse *fileResponse = [[HTTPFileResponse alloc] initWithFilePath:filePath forConnection:self];
        fileResponse.contentType = @"application/octet-stream";
        return fileResponse;
105
    }
106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122
    if ([path hasPrefix:@"/thumbnail"]) {
        NSString *filePath = [[path stringByReplacingOccurrencesOfString:@"/thumbnail/" withString:@""]stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
        filePath = [filePath stringByReplacingOccurrencesOfString:@".png" withString:@""];

        NSManagedObjectContext *moc = [[MLMediaLibrary sharedMediaLibrary] managedObjectContext];
        NSPersistentStoreCoordinator *psc = [moc persistentStoreCoordinator];
        NSManagedObject *mo = [moc existingObjectWithID:[psc managedObjectIDForURIRepresentation:[NSURL URLWithString:filePath]] error:nil];

        NSData *theData;
        if ([mo isKindOfClass:[MLFile class]])
            theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForMediaFile:(MLFile *)mo]);
        else if ([mo isKindOfClass:[MLShow class]])
            theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForShow:(MLShow *)mo]);
        else if ([mo isKindOfClass:[MLLabel class]])
            theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForLabel:(MLLabel *)mo]);
        else if ([mo isKindOfClass:[MLAlbum class]])
            theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForMediaFile:[[(MLAlbum *)mo tracks].anyObject files].anyObject]);
123 124 125 126
        else if ([mo isKindOfClass:[MLAlbumTrack class]])
            theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForMediaFile:[(MLAlbumTrack *)mo files].anyObject]);
        else if ([mo isKindOfClass:[MLShowEpisode class]])
            theData = UIImagePNGRepresentation([VLCThumbnailsCache thumbnailForMediaFile:[(MLShowEpisode *)mo files].anyObject]);
127

128 129 130 131 132
        if (theData) {
            HTTPDataResponse *dataResponse = [[HTTPDataResponse alloc] initWithData:theData];
            dataResponse.contentType = @"image/png";
            return dataResponse;
        }
133
    }
134 135 136 137
    NSString *filePath = [self filePathForURI:path];
    NSString *documentRoot = [config documentRoot];
    NSString *relativePath = [filePath substringFromIndex:[documentRoot length]];

138
    if (([relativePath isEqualToString:@"/index.html"]) || ([relativePath isEqualToString:@"/libMediaVLC.xml"])) {
139 140 141
        NSMutableArray *allMedia = [[NSMutableArray alloc] init];

        /* add all albums */
142 143
        NSArray *allAlbums = [MLAlbum allAlbums];
        for (MLAlbum *album in allAlbums) {
144 145
            if (album.name.length > 0 && album.tracks.count > 1)
                [allMedia addObject:album];
146
        }
147 148

        /* add all shows */
149 150
        NSArray *allShows = [MLShow allShows];
        for (MLShow *show in allShows) {
151 152
            if (show.name.length > 0 && show.episodes.count > 1)
                [allMedia addObject:show];
153
        }
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176

        /* add all folders*/
        NSArray *allFolders = [MLLabel allLabels];
        for (MLLabel *folder in allFolders)
            [allMedia addObject:folder];

        /* add all remaining files */
        NSArray *allFiles = [MLFile allFiles];
        for (MLFile *file in allFiles) {
            if (file.labels.count > 0) continue;

            if (!file.isShowEpisode && !file.isAlbumTrack)
                [allMedia addObject:file];
            else if (file.isShowEpisode) {
                if (file.showEpisode.show.episodes.count < 2)
                    [allMedia addObject:file];
            } else if (file.isAlbumTrack) {
                if (file.albumTrack.album.tracks.count < 2)
                    [allMedia addObject:file];
            }
        }

        NSMutableArray *mediaInHtml = [[NSMutableArray alloc] initWithCapacity:allMedia.count];
177 178 179 180
        NSMutableArray *mediaInXml = [[NSMutableArray alloc] initWithCapacity:allMedia.count];
        self.uploadController = [[VLCHTTPUploaderController alloc] init];
        NSString *hostName = [self.uploadController hostname];
        NSString *pathLibrary =[NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES) objectAtIndex:0];
181
        NSString *duration;
182 183

        for (NSManagedObject *mo in allMedia) {
184
            if ([mo isKindOfClass:[MLFile class]]) {
185
                duration = [[VLCTime timeWithNumber:[(MLFile *)mo duration]] stringValue];
186 187
                [mediaInHtml addObject:[NSString stringWithFormat:
                                        @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
188
                                        <a href=\"download/%@\" class=\"inner\"> \
189 190 191
                                        <div class=\"down icon\"></div> \
                                        <div class=\"infos\"> \
                                        <span class=\"first-line\">%@</span> \
192
                                        <span class=\"second-line\">%@ - %0.2f MB</span> \
193 194 195 196 197
                                        </div> \
                                        </a> \
                                        </div>",
                                        mo.objectID.URIRepresentation,
                                        [[(MLFile *)mo url] stringByReplacingOccurrencesOfString:@"file://"withString:@""],
198 199
                                        [(MLFile *)mo title],
                                        duration, (float)([(MLFile *)mo fileSizeInBytes] / 1e6)]];
200 201 202
                [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@\" thumb=\"http://%@/download/%@/Thumbnails/File/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\"/>",
                                       [(MLFile *)mo title], hostName, pathLibrary, [[NSString stringWithFormat:@"%@", mo.objectID.URIRepresentation] lastPathComponent], duration, [(MLFile *)mo fileSizeInBytes],
                                       hostName, [[(MLFile *)mo url] stringByReplacingOccurrencesOfString:@"file://"withString:@""]]];
203
            }
204 205
            else if ([mo isKindOfClass:[MLShow class]]) {
                NSArray *episodes = [(MLShow *)mo sortedEpisodes];
206 207
                [mediaInHtml addObject:[NSString stringWithFormat:
                                        @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
208
                                        <a href=\"#\" class=\"inner folder\"> \
209 210 211
                                        <div class=\"open icon\"></div> \
                                        <div class=\"infos\"> \
                                        <span class=\"first-line\">%@</span> \
Felix Paul Kühne's avatar
Felix Paul Kühne committed
212
                                        <span class=\"second-line\">%lu items</span> \
213 214 215 216
                                        </div> \
                                        </a> \
                                        <div class=\"content\">",
                                        mo.objectID.URIRepresentation,
217
                                        [(MLShow *)mo name],
Felix Paul Kühne's avatar
Felix Paul Kühne committed
218
                                        (unsigned long)[episodes count]]];
219
                for (MLShowEpisode *showEp in episodes) {
220
                    duration = [[VLCTime timeWithNumber:[(MLFile *)[[showEp files] anyObject] duration]] stringValue];
221 222
                    [mediaInHtml addObject:[NSString stringWithFormat:
                                            @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
223
                                            <a href=\"download/%@\" class=\"inner\"> \
224 225 226
                                            <div class=\"down icon\"></div> \
                                            <div class=\"infos\"> \
                                            <span class=\"first-line\">S%@E%@ - %@</span> \
227
                                            <span class=\"second-line\">%@ - %0.2f MB</span> \
228 229 230 231 232 233 234
                                            </div> \
                                            </a> \
                                            </div>",
                                            showEp.objectID.URIRepresentation,
                                            [[(MLFile *)[[showEp files] anyObject] url] stringByReplacingOccurrencesOfString:@"file://"withString:@""],
                                            showEp.seasonNumber,
                                            showEp.episodeNumber,
235 236
                                            showEp.name,
                                            duration, (float)([(MLFile *)[[showEp files] anyObject] fileSizeInBytes] / 1e6)]];
237 238 239 240 241
                    [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@ - S%@E%@\" thumb=\"http://%@/download/%@/Thumbnails/File/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\"/>",
                                           [(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:@""]]];
242
                }
243
                [mediaInHtml addObject:@"</div></div>"];
244 245
            } else if ([mo isKindOfClass:[MLLabel class]]) {
                NSArray *folderItems = [(MLLabel *)mo sortedFolderItems];
246 247
                [mediaInHtml addObject:[NSString stringWithFormat:
                                        @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
248
                                        <a href=\"#\" class=\"inner folder\"> \
249 250 251
                                        <div class=\"open icon\"></div> \
                                        <div class=\"infos\"> \
                                        <span class=\"first-line\">%@</span> \
Felix Paul Kühne's avatar
Felix Paul Kühne committed
252
                                        <span class=\"second-line\">%lu items</span> \
253 254 255 256
                                        </div> \
                                        </a> \
                                        <div class=\"content\">",
                                        mo.objectID.URIRepresentation,
257
                                        [(MLLabel *)mo name],
Felix Paul Kühne's avatar
Felix Paul Kühne committed
258
                                        (unsigned long)[folderItems count]]];
259
                for (MLFile *file in folderItems) {
260
                    duration = [[VLCTime timeWithNumber:[file duration]] stringValue];
261 262
                    [mediaInHtml addObject:[NSString stringWithFormat:
                                            @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
263
                                            <a href=\"download/%@\" class=\"inner\"> \
264 265 266
                                            <div class=\"down icon\"></div> \
                                            <div class=\"infos\"> \
                                            <span class=\"first-line\">%@</span> \
267
                                            <span class=\"second-line\">%@ - %0.2f MB</span> \
268 269 270 271 272
                                            </div> \
                                            </a> \
                                            </div>",
                                            file.objectID.URIRepresentation,
                                            [[file url] stringByReplacingOccurrencesOfString:@"file://"withString:@""],
273 274
                                            file.title,
                                            duration, (float)([file fileSizeInBytes] / 1e6)]];
275 276 277
                    [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@\" thumb=\"http://%@/download/%@/Thumbnails/File/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\"/>",
                                           file.title, hostName, pathLibrary, [[NSString stringWithFormat:@"%@", file.objectID.URIRepresentation] lastPathComponent], duration, [file fileSizeInBytes],
                                           hostName, [[file url] stringByReplacingOccurrencesOfString:@"file://"withString:@""]]];
278
                }
279
                [mediaInHtml addObject:@"</div></div>"];
280 281
            } else if ([mo isKindOfClass:[MLAlbum class]]) {
                NSArray *albumTracks = [(MLAlbum *)mo sortedTracks];
282 283
                [mediaInHtml addObject:[NSString stringWithFormat:
                                        @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
284
                                        <a href=\"#\" class=\"inner folder\"> \
285 286 287
                                        <div class=\"open icon\"></div> \
                                        <div class=\"infos\"> \
                                        <span class=\"first-line\">%@</span> \
Felix Paul Kühne's avatar
Felix Paul Kühne committed
288
                                        <span class=\"second-line\">%lu items</span> \
289 290 291 292
                                        </div> \
                                        </a> \
                                        <div class=\"content\">",
                                        mo.objectID.URIRepresentation,
293
                                        [(MLAlbum *)mo name],
Felix Paul Kühne's avatar
Felix Paul Kühne committed
294
                                        (unsigned long)[albumTracks count]]];
295
                for (MLAlbumTrack *track in albumTracks) {
296
                    duration = [[VLCTime timeWithNumber:[(MLFile *)[[track files] anyObject] duration]] stringValue];
297 298
                    [mediaInHtml addObject:[NSString stringWithFormat:
                                            @"<div style=\"background-image:url('thumbnail/%@.png')\"> \
299
                                            <a href=\"download/%@\" class=\"inner\"> \
300 301 302
                                            <div class=\"down icon\"></div> \
                                            <div class=\"infos\"> \
                                            <span class=\"first-line\">%@</span> \
303
                                            <span class=\"second-line\">%@ - %0.2f MB</span> \
304 305 306 307 308
                                            </div> \
                                            </a> \
                                            </div>",
                                            track.objectID.URIRepresentation,
                                            [[(MLFile *)[[track files] anyObject] url] stringByReplacingOccurrencesOfString:@"file://"withString:@""],
309 310
                                            track.title,
                                            duration, (float)([(MLFile *)[[track files] anyObject] fileSizeInBytes] / 1e6)]];
311 312 313
                    [mediaInXml addObject:[NSString stringWithFormat:@"<Media title=\"%@\" thumb=\"http://%@/download/%@/Thumbnails/File/%@.png\" duration=\"%@\" size=\"%li\" pathfile=\"http://%@/download/%@\"/>",
                                           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:@""]]];
314
                }
315
                [mediaInHtml addObject:@"</div></div>"];
316
            }
317
        }
318

319
        NSString *deviceModel = [[UIDevice currentDevice] model];
320 321 322 323 324 325 326 327 328 329 330 331 332
        NSDictionary *replacementDict;

        if ([relativePath isEqualToString:@"/libMediaVLC.xml"]) {
            replacementDict = @{@"FILES" : [mediaInXml componentsJoinedByString:@" "],
                                @"NB_FILE" : [NSString stringWithFormat:@"%li", (unsigned long)mediaInXml.count],
                                @"LIB_TITLE" : [[UIDevice currentDevice] name]};
        } else
            replacementDict = @{@"FILES" : [mediaInHtml componentsJoinedByString:@" "],
                                @"WEBINTF_TITLE" : NSLocalizedString(@"WEBINTF_TITLE", nil),
                                @"WEBINTF_DROPFILES" : NSLocalizedString(@"WEBINTF_DROPFILES", nil),
                                @"WEBINTF_DROPFILES_LONG" : [NSString stringWithFormat:NSLocalizedString(@"WEBINTF_DROPFILES_LONG", nil), deviceModel],
                                @"WEBINTF_DOWNLOADFILES" : NSLocalizedString(@"WEBINTF_DOWNLOADFILES", nil),
                                @"WEBINTF_DOWNLOADFILES_LONG" : [NSString stringWithFormat: NSLocalizedString(@"WEBINTF_DOWNLOADFILES_LONG", nil), deviceModel]};
333 334 335 336 337 338

        return [[HTTPDynamicFileResponse alloc] initWithFilePath:[self filePathForURI:path]
                                                   forConnection:self
                                                       separator:@"%%"
                                           replacementDictionary:replacementDict];
    } else if ([relativePath isEqualToString:@"/style.css"]) {
339
        NSDictionary *replacementDict = @{@"WEBINTF_TITLE" : NSLocalizedString(@"WEBINTF_TITLE", nil)};
340 341 342 343 344
        return [[HTTPDynamicFileResponse alloc] initWithFilePath:[self filePathForURI:path]
                                                   forConnection:self
                                                       separator:@"%%"
                                           replacementDictionary:replacementDict];
    }
345

346 347 348 349 350 351 352 353 354 355
    return [super httpResponseForMethod:method URI:path];
}

- (void)prepareForBodyWithSize:(UInt64)contentLength
{
    // set up mime parser
    NSString* boundary = [request headerField:@"boundary"];
    _parser = [[MultipartFormDataParser alloc] initWithBoundary:boundary formEncoding:NSUTF8StringEncoding];
    _parser.delegate = self;

356 357
    APLog(@"expecting file of size %lli kB", contentLength / 1024);
    _contentLength = contentLength;
358 359 360 361 362 363 364
}

- (void)processBodyData:(NSData *)postDataChunk
{
    /* append data to the parser. It will invoke callbacks to let us handle
     * parsed data. */
    [_parser appendData:postDataChunk];
365

366
    _receivedContent += postDataChunk.length;
367

368
    APLog(@"received %lli kB (%lli %%)", _receivedContent / 1024, ((_receivedContent * 100) / _contentLength));
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388
}

//-----------------------------------------------------------------
#pragma mark multipart form data parser delegate


- (void)processStartOfPartWithHeader:(MultipartMessageHeader*) header
{
    /* in this sample, we are not interested in parts, other then file parts.
     * check content disposition to find out filename */

    MultipartMessageHeaderField* disposition = (header.fields)[@"Content-Disposition"];
    NSString* filename = [(disposition.params)[@"filename"] lastPathComponent];

    if ((nil == filename) || [filename isEqualToString: @""]) {
        // it's either not a file part, or
        // an empty form sent. we won't handle it.
        return;
    }

389
    // create the path where to store the media temporarily
390
    NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
391
    NSString* uploadDirPath = [searchPaths[0] stringByAppendingPathComponent:@"Upload"];
392
    NSFileManager *fileManager = [NSFileManager defaultManager];
393 394

    BOOL isDir = YES;
395 396
    if (![fileManager fileExistsAtPath:uploadDirPath isDirectory:&isDir ]) {
        [fileManager createDirectoryAtPath:uploadDirPath withIntermediateDirectories:YES attributes:nil error:nil];
397 398
    }

399
    _filepath = [uploadDirPath stringByAppendingPathComponent: filename];
400

401
    APLog(@"Saving file to %@", _filepath);
402
    if (![fileManager createDirectoryAtPath:uploadDirPath withIntermediateDirectories:true attributes:nil error:nil])
403
        APLog(@"Could not create directory at path: %@", _filepath);
404

405
    if (![fileManager createFileAtPath:_filepath contents:nil attributes:nil])
406
        APLog(@"Could not create file at path: %@", _filepath);
407

408
    _storeFile = [NSFileHandle fileHandleForWritingAtPath:_filepath];
409
    [(VLCAppDelegate*)[UIApplication sharedApplication].delegate networkActivityStarted];
410
    [(VLCAppDelegate*)[UIApplication sharedApplication].delegate disableIdleTimer];
411 412 413 414 415
}

- (void)processContent:(NSData*)data WithHeader:(MultipartMessageHeader*) header
{
    // here we just write the output from parser to the file.
416 417 418 419 420 421 422 423 424 425 426 427 428
    if (_storeFile) {
        @try {
            [_storeFile writeData:data];
        }
        @catch (NSException *exception) {
            APLog(@"File to write further data because storage is full.");
            [_storeFile closeFile];
            _storeFile = nil;
            /* don't block */
            [self performSelector:@selector(stop) withObject:nil afterDelay:0.1];
        }
    }

429 430 431 432 433
}

- (void)processEndOfPartWithHeader:(MultipartMessageHeader*)header
{
    // as the file part is over, we close the file.
434
    APLog(@"closing file");
435 436 437 438
    [_storeFile closeFile];
    _storeFile = nil;
}

439
- (BOOL)shouldDie
440
{
441 442 443
    if (_filepath) {
        if (_filepath.length > 0)
            [[(VLCAppDelegate*)[UIApplication sharedApplication].delegate uploadController] moveFileFrom:_filepath];
444
    }
445
    return [super shouldDie];
446 447
}

448
@end