VLCDropboxController.m 9.16 KB
Newer Older
1 2 3 4
/*****************************************************************************
 * VLCDropboxController.m
 * VLC for iOS
 *****************************************************************************
5
 * Copyright (c) 2013-2015 VideoLAN. All rights reserved.
6 7 8 9 10 11 12
 * $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

#import "VLCDropboxController.h"
15
#import "NSString+SupportedMedia.h"
16 17 18 19 20 21 22
#import "VLCAppDelegate.h"

@interface VLCDropboxController ()
{
    DBRestClient *_restClient;
    NSArray *_currentFileList;

23 24 25
    NSMutableArray *_listOfDropboxFilesToDownload;
    BOOL _downloadInProgress;

26
    NSInteger _outstandingNetworkRequests;
27 28 29 30 31

    CGFloat _averageSpeed;
    CGFloat _fileSize;
    NSTimeInterval _startDL;
    NSTimeInterval _lastStatsUpdate;
32 33 34 35 36 37 38 39
}

@end

@implementation VLCDropboxController

#pragma mark - session handling

40 41 42 43 44 45
+ (instancetype)sharedInstance
{
    static VLCDropboxController *sharedInstance = nil;
    static dispatch_once_t pred;

    dispatch_once(&pred, ^{
46
        sharedInstance = [VLCDropboxController new];
47 48 49 50 51
    });

    return sharedInstance;
}

52 53
- (void)startSession
{
54
    [[DBSession sharedSession] isLinked];
55 56 57 58 59 60 61
}

- (void)logout
{
    [[DBSession sharedSession] unlinkAll];
}

62
- (BOOL)isAuthorized
63
{
64
    return [[DBSession sharedSession] isLinked];
65 66 67 68 69 70 71 72 73 74 75 76 77
}

- (DBRestClient *)restClient {
    if (!_restClient) {
        _restClient = [[DBRestClient alloc] initWithSession:[DBSession sharedSession]];
        _restClient.delegate = self;
    }
    return _restClient;
}

#pragma mark - file management
- (void)requestDirectoryListingAtPath:(NSString *)path
{
78
    if (self.isAuthorized)
79 80 81 82 83 84
        [[self restClient] loadMetadata:path];
}

- (void)downloadFileToDocumentFolder:(DBMetadata *)file
{
    if (!file.isDirectory) {
85 86 87
        if (!_listOfDropboxFilesToDownload)
            _listOfDropboxFilesToDownload = [[NSMutableArray alloc] init];
        [_listOfDropboxFilesToDownload addObject:file];
88

89 90
        if ([self.delegate respondsToSelector:@selector(numberOfFilesWaitingToBeDownloadedChanged)])
            [self.delegate numberOfFilesWaitingToBeDownloadedChanged];
91

92
        [self _triggerNextDownload];
93 94 95
    }
}

96 97 98 99 100 101
- (void)streamFile:(DBMetadata *)file
{
    if (!file.isDirectory)
        [[self restClient] loadStreamableURLForFile:file.path];
}

102 103 104 105 106 107 108 109 110 111 112 113 114 115 116
- (void)_triggerNextDownload
{
    if (_listOfDropboxFilesToDownload.count > 0 && !_downloadInProgress) {
        [self _reallyDownloadFileToDocumentFolder:_listOfDropboxFilesToDownload[0]];
        [_listOfDropboxFilesToDownload removeObjectAtIndex:0];

        if ([self.delegate respondsToSelector:@selector(numberOfFilesWaitingToBeDownloadedChanged)])
            [self.delegate numberOfFilesWaitingToBeDownloadedChanged];
    }
}

- (void)_reallyDownloadFileToDocumentFolder:(DBMetadata *)file
{
    NSArray *searchPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *filePath = [searchPaths[0] stringByAppendingFormat:@"/%@", file.filename];
117 118
    _startDL = [NSDate timeIntervalSinceReferenceDate];
    _fileSize = file.totalBytes;
119 120 121 122 123 124 125 126
    [[self restClient] loadFile:file.path intoPath:filePath];

    if ([self.delegate respondsToSelector:@selector(operationWithProgressInformationStarted)])
        [self.delegate operationWithProgressInformationStarted];

    _downloadInProgress = YES;
}

127 128 129
#pragma mark - restClient delegate
- (BOOL)_supportedFileExtension:(NSString *)filename
{
130
    if ([filename isSupportedMediaFormat] || [filename isSupportedAudioMediaFormat] || [filename isSupportedSubtitleFormat])
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
        return YES;

    return NO;
}

- (void)restClient:(DBRestClient *)client loadedMetadata:(DBMetadata *)metadata {
    NSMutableArray *listOfGoodFilesAndFolders = [[NSMutableArray alloc] init];

    if (metadata.isDirectory) {
        NSArray *contents = metadata.contents;
        NSUInteger metaDataCount = metadata.contents.count;
        for (NSUInteger x = 0; x < metaDataCount; x++) {
            DBMetadata *file = contents[x];
            if ([file isDirectory] || [self _supportedFileExtension:file.filename])
                [listOfGoodFilesAndFolders addObject:file];
        }
    }

    _currentFileList = [NSArray arrayWithArray:listOfGoodFilesAndFolders];

Felix Paul Kühne's avatar
Felix Paul Kühne committed
151
    APLog(@"found filtered metadata for %lu files", (unsigned long)_currentFileList.count);
152 153 154 155 156 157
    if ([self.delegate respondsToSelector:@selector(mediaListUpdated)])
        [self.delegate mediaListUpdated];
}

- (void)restClient:(DBRestClient *)client loadMetadataFailedWithError:(NSError *)error
{
158
    APLog(@"DBMetadata download failed with error %li", (long)error.code);
159
    [self _handleError:error];
160 161 162 163 164 165
}

- (void)restClient:(DBRestClient*)client loadedFile:(NSString*)localPath
{
    /* update library now that we got a file */
    VLCAppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
166
    [appDelegate performSelectorOnMainThread:@selector(updateMediaList) withObject:nil waitUntilDone:NO];
167 168 169

    if ([self.delegate respondsToSelector:@selector(operationWithProgressInformationStopped)])
        [self.delegate operationWithProgressInformationStopped];
170 171 172
    _downloadInProgress = NO;

    [self _triggerNextDownload];
173 174 175 176
}

- (void)restClient:(DBRestClient*)client loadFileFailedWithError:(NSError*)error
{
177
    APLog(@"DBFile download failed with error %li", (long)error.code);
178
    [self _handleError:error];
179 180
    if ([self.delegate respondsToSelector:@selector(operationWithProgressInformationStopped)])
        [self.delegate operationWithProgressInformationStopped];
181 182
    _downloadInProgress = NO;
    [self _triggerNextDownload];
183 184 185 186
}

- (void)restClient:(DBRestClient*)client loadProgress:(CGFloat)progress forFile:(NSString*)destPath
{
187
    if ((_lastStatsUpdate > 0 && ([NSDate timeIntervalSinceReferenceDate] - _lastStatsUpdate > .5)) || _lastStatsUpdate <= 0) {
Carola Nitz's avatar
Carola Nitz committed
188
        [self calculateRemainingTime:progress * _fileSize expectedDownloadSize:_fileSize];
189 190 191
        _lastStatsUpdate = [NSDate timeIntervalSinceReferenceDate];
    }

192 193
    if ([self.delegate respondsToSelector:@selector(currentProgressInformation:)])
        [self.delegate currentProgressInformation:progress];
194 195
}

196 197 198
- (void)restClient:(DBRestClient*)restClient loadedStreamableURL:(NSURL*)url forFile:(NSString*)path
{
    VLCAppDelegate *appDelegate = (VLCAppDelegate *)[UIApplication sharedApplication].delegate;
Felix Paul Kühne's avatar
Felix Paul Kühne committed
199
    [appDelegate openMovieFromURL:url];
200 201 202 203
}

- (void)restClient:(DBRestClient*)restClient loadStreamableURLFailedWithError:(NSError*)error
{
204
    APLog(@"loadStreamableURL failed with error %li", (long)error.code);
205
    [self _handleError:error];
206 207
}

208 209 210 211 212 213 214 215 216 217
#pragma mark - DBSession delegate

- (void)sessionDidReceiveAuthorizationFailure:(DBSession *)session userId:(NSString *)userId
{
    APLog(@"DBSession received authorization failure with user ID %@", userId);
}

#pragma mark - DBNetworkRequest delegate
- (void)networkRequestStarted
{
218
    _outstandingNetworkRequests++;
219
    if (_outstandingNetworkRequests == 1) {
220
        [(VLCAppDelegate*)[UIApplication sharedApplication].delegate networkActivityStarted];
221
        [(VLCAppDelegate*)[UIApplication sharedApplication].delegate disableIdleTimer];
222
    }
223 224 225 226
}

- (void)networkRequestStopped
{
227
    _outstandingNetworkRequests--;
228
    if (_outstandingNetworkRequests == 0) {
229
        [(VLCAppDelegate*)[UIApplication sharedApplication].delegate networkActivityStopped];
230
        [(VLCAppDelegate*)[UIApplication sharedApplication].delegate activateIdleTimer];
231
    }
232 233 234 235
}

#pragma mark - VLC internal communication and delegate

236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253
- (void)calculateRemainingTime:(CGFloat)receivedDataSize expectedDownloadSize:(CGFloat)expectedDownloadSize
{
    CGFloat lastSpeed = receivedDataSize / ([NSDate timeIntervalSinceReferenceDate] - _startDL);
    CGFloat smoothingFactor = 0.005;
    _averageSpeed = isnan(_averageSpeed) ? lastSpeed : smoothingFactor * lastSpeed + (1 - smoothingFactor) * _averageSpeed;

    CGFloat RemainingInSeconds = (expectedDownloadSize - receivedDataSize)/_averageSpeed;

    NSDate *date = [NSDate dateWithTimeIntervalSince1970:RemainingInSeconds];
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateFormat:@"HH:mm:ss"];
    [formatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]];

    NSString  *remaingTime = [formatter stringFromDate:date];
    if ([self.delegate respondsToSelector:@selector(updateRemainingTime:)])
        [self.delegate updateRemainingTime:remaingTime];
}

254 255 256 257 258
- (NSArray *)currentListFiles
{
    return _currentFileList;
}

259 260 261 262 263 264 265 266
- (NSInteger)numberOfFilesWaitingToBeDownloaded
{
    if (_listOfDropboxFilesToDownload)
        return _listOfDropboxFilesToDownload.count;

    return 0;
}

267 268 269
#pragma mark - user feedback
- (void)_handleError:(NSError *)error
{
270 271 272 273 274
    VLCAlertView *alert = [[VLCAlertView alloc] initWithTitle:[NSString stringWithFormat:NSLocalizedString(@"ERROR_NUMBER", nil), error.code]
                                                      message:error.localizedDescription
                                                     delegate:self
                                            cancelButtonTitle:NSLocalizedString(@"BUTTON_CANCEL", nil)
                                            otherButtonTitles:nil];
275 276 277
    [alert show];
}

278
@end