VLCDropboxController.m 11.5 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
#import "VLCPlaybackController.h"
Felix Paul Kühne's avatar
Felix Paul Kühne committed
17
#import "VLCActivityManager.h"
18
#if !TARGET_OS_TV
Felix Paul Kühne's avatar
Felix Paul Kühne committed
19
#import "VLCMediaFileDiscoverer.h"
20 21
#import <DropboxSDK/DBKeychain.h>
#else
22
#import <DropboxTVSDK/DBKeychain.h>
23
#endif
24
#import "VLCDropboxConstants.h"
25 26 27 28 29 30

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

31 32 33
    NSMutableArray *_listOfDropboxFilesToDownload;
    BOOL _downloadInProgress;

34
    NSInteger _outstandingNetworkRequests;
35 36 37 38 39

    CGFloat _averageSpeed;
    CGFloat _fileSize;
    NSTimeInterval _startDL;
    NSTimeInterval _lastStatsUpdate;
40 41

    UINavigationController *_lastKnownNavigationController;
42 43 44 45 46 47 48 49
}

@end

@implementation VLCDropboxController

#pragma mark - session handling

50 51 52 53 54 55
+ (instancetype)sharedInstance
{
    static VLCDropboxController *sharedInstance = nil;
    static dispatch_once_t pred;

    dispatch_once(&pred, ^{
56
        sharedInstance = [VLCDropboxController new];
57
        [sharedInstance shareCredentials];
58 59 60 61

        DBSession* dbSession = [[DBSession alloc] initWithAppKey:kVLCDropboxAppKey appSecret:kVLCDropboxPrivateKey root:kDBRootDropbox];
        [DBSession setSharedSession:dbSession];
        [DBRequest setNetworkRequestDelegate:sharedInstance];
62 63 64 65 66
    });

    return sharedInstance;
}

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90
- (void)shareCredentials
{
    /* share our credentials */
    NSDictionary *credentials = [DBKeychain credentials];
    if (credentials == nil)
        return;

    NSUbiquitousKeyValueStore *ubiquitousStore = [NSUbiquitousKeyValueStore defaultStore];
    [ubiquitousStore setDictionary:credentials forKey:kVLCStoreDropboxCredentials];
    [ubiquitousStore synchronize];
}

- (BOOL)restoreFromSharedCredentials
{
    NSUbiquitousKeyValueStore *ubiquitousStore = [NSUbiquitousKeyValueStore defaultStore];
    [ubiquitousStore synchronize];
    NSDictionary *credentials = [ubiquitousStore dictionaryForKey:kVLCStoreDropboxCredentials];
    if (!credentials)
        return NO;

    [DBKeychain setCredentials:credentials];
    return YES;
}

91 92
- (void)startSession
{
93
    [[DBSession sharedSession] isLinked];
94 95 96 97 98 99 100
}

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

101
- (BOOL)isAuthorized
102
{
103
    return [[DBSession sharedSession] isLinked];
104 105 106 107 108 109 110 111 112 113 114
}

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

#pragma mark - file management
115 116 117 118 119 120

- (BOOL)canPlayAll
{
    return NO;
}

121 122
- (void)requestDirectoryListingAtPath:(NSString *)path
{
123
    if (self.isAuthorized)
124 125 126 127 128 129
        [[self restClient] loadMetadata:path];
}

- (void)downloadFileToDocumentFolder:(DBMetadata *)file
{
    if (!file.isDirectory) {
130 131 132
        if (!_listOfDropboxFilesToDownload)
            _listOfDropboxFilesToDownload = [[NSMutableArray alloc] init];
        [_listOfDropboxFilesToDownload addObject:file];
133

134 135
        if ([self.delegate respondsToSelector:@selector(numberOfFilesWaitingToBeDownloadedChanged)])
            [self.delegate numberOfFilesWaitingToBeDownloadedChanged];
136

137
        [self _triggerNextDownload];
138 139 140
    }
}

141
- (void)streamFile:(DBMetadata *)file currentNavigationController:(UINavigationController *)navigationController
142
{
143 144
    if (!file.isDirectory) {
        _lastKnownNavigationController = navigationController;
145 146 147
        NSString *path = file.path;
        if (path != nil)
            [[self restClient] loadStreamableURLForFile:path];
148
    }
149 150
}

151 152 153 154 155 156 157 158 159 160 161 162 163 164 165
- (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];
166 167
    _startDL = [NSDate timeIntervalSinceReferenceDate];
    _fileSize = file.totalBytes;
168 169 170 171 172 173 174 175
    [[self restClient] loadFile:file.path intoPath:filePath];

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

    _downloadInProgress = YES;
}

176 177 178
#pragma mark - restClient delegate
- (BOOL)_supportedFileExtension:(NSString *)filename
{
179
    if ([filename isSupportedMediaFormat] || [filename isSupportedAudioMediaFormat] || [filename isSupportedSubtitleFormat])
180 181 182 183 184
        return YES;

    return NO;
}

185 186
- (void)restClient:(DBRestClient *)client loadedMetadata:(DBMetadata *)metadata
{
187
    _currentFileList = [NSArray arrayWithArray:metadata.contents];
188

Felix Paul Kühne's avatar
Felix Paul Kühne committed
189
    APLog(@"found filtered metadata for %lu files", (unsigned long)_currentFileList.count);
190 191 192 193 194 195
    if ([self.delegate respondsToSelector:@selector(mediaListUpdated)])
        [self.delegate mediaListUpdated];
}

- (void)restClient:(DBRestClient *)client loadMetadataFailedWithError:(NSError *)error
{
196
    APLog(@"DBMetadata download failed with error %li", (long)error.code);
197
    [self _handleError:error];
198 199 200 201
}

- (void)restClient:(DBRestClient*)client loadedFile:(NSString*)localPath
{
202
#if TARGET_OS_IOS
203
    /* update library now that we got a file */
Felix Paul Kühne's avatar
Felix Paul Kühne committed
204
    [[VLCMediaFileDiscoverer sharedInstance] performSelectorOnMainThread:@selector(updateMediaList) withObject:nil waitUntilDone:NO];
205 206 207

    if ([self.delegate respondsToSelector:@selector(operationWithProgressInformationStopped)])
        [self.delegate operationWithProgressInformationStopped];
208 209 210
    _downloadInProgress = NO;

    [self _triggerNextDownload];
211
#endif
212 213 214 215
}

- (void)restClient:(DBRestClient*)client loadFileFailedWithError:(NSError*)error
{
216
    APLog(@"DBFile download failed with error %li", (long)error.code);
217
    [self _handleError:error];
218 219
    if ([self.delegate respondsToSelector:@selector(operationWithProgressInformationStopped)])
        [self.delegate operationWithProgressInformationStopped];
220 221
    _downloadInProgress = NO;
    [self _triggerNextDownload];
222 223 224 225
}

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

231 232
    if ([self.delegate respondsToSelector:@selector(currentProgressInformation:)])
        [self.delegate currentProgressInformation:progress];
233 234
}

235 236
- (void)restClient:(DBRestClient*)restClient loadedStreamableURL:(NSURL*)url forFile:(NSString*)path
{
237 238
    VLCPlaybackController *vpc = [VLCPlaybackController sharedInstance];
    [vpc playURL:url successCallback:nil errorCallback:nil];
239 240 241 242 243 244 245 246
#if TARGET_OS_TV
    if (_lastKnownNavigationController) {
        VLCFullscreenMovieTVViewController *movieVC = [VLCFullscreenMovieTVViewController fullscreenMovieTVViewController];
        [_lastKnownNavigationController presentViewController:movieVC
                                                     animated:YES
                                                   completion:nil];
    }
#endif
247 248 249 250
}

- (void)restClient:(DBRestClient*)restClient loadStreamableURLFailedWithError:(NSError*)error
{
251
    APLog(@"loadStreamableURL failed with error %li", (long)error.code);
252
    [self _handleError:error];
253 254
}

255 256 257 258 259 260 261 262 263 264
#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
{
265
    _outstandingNetworkRequests++;
266
    if (_outstandingNetworkRequests == 1) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
267 268 269
        VLCActivityManager *activityManager = [VLCActivityManager defaultManager];
        [activityManager networkActivityStarted];
        [activityManager disableIdleTimer];
270
    }
271 272 273 274
}

- (void)networkRequestStopped
{
275
    _outstandingNetworkRequests--;
276
    if (_outstandingNetworkRequests == 0) {
Felix Paul Kühne's avatar
Felix Paul Kühne committed
277 278 279
        VLCActivityManager *activityManager = [VLCActivityManager defaultManager];
        [activityManager networkActivityStopped];
        [activityManager activateIdleTimer];
280
    }
281 282 283 284
}

#pragma mark - VLC internal communication and delegate

285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
- (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];
}

303 304 305 306 307
- (NSArray *)currentListFiles
{
    return _currentFileList;
}

308 309 310 311 312 313 314 315
- (NSInteger)numberOfFilesWaitingToBeDownloaded
{
    if (_listOfDropboxFilesToDownload)
        return _listOfDropboxFilesToDownload.count;

    return 0;
}

316 317 318
#pragma mark - user feedback
- (void)_handleError:(NSError *)error
{
319
#if TARGET_OS_IOS
320 321 322 323 324
    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];
325
    [alert show];
326 327 328 329 330 331 332 333 334 335 336 337
#else
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:[NSString stringWithFormat:NSLocalizedString(@"ERROR_NUMBER", nil), error.code]
                                                                   message:error.localizedDescription
                                                            preferredStyle:UIAlertControllerStyleAlert];

    UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString(@"BUTTON_CANCEL", nil)
                                                            style:UIAlertActionStyleDestructive
                                                          handler:^(UIAlertAction *action) {
                                                          }];

    [alert addAction:defaultAction];

338
    [[UIApplication sharedApplication].keyWindow.rootViewController presentViewController:alert animated:YES completion:nil];
339
#endif
340 341
}

342 343 344 345 346 347
- (void)reset
{
    [_restClient cancelAllRequests];
    _currentFileList = nil;
}

348
@end