Просмотр исходного кода

Introduce media file discoverer

Extract media discovering functionality to separate class.
Fix of an error when not all files were shown in application if they were added simultaniously.
Fix of timer cancelation.
Gleb Pinigin 12 лет назад
Родитель
Сommit
9766afe264

+ 19 - 77
AspenProject/VLCAppDelegate.m

@@ -9,7 +9,7 @@
 //
 
 #import "VLCAppDelegate.h"
-#import "DirectoryWatcher.h"
+#import "VLCMediaFileDiscoverer.h"
 #import "NSString+SupportedMedia.h"
 #import "UIDevice+SpeedCategory.h"
 
@@ -19,13 +19,9 @@
 #import "PAPasscodeViewController.h"
 #import "UINavigationController+Theme.h"
 
-@interface VLCAppDelegate () <PAPasscodeViewControllerDelegate, DirectoryWatcherDelegate> {
+@interface VLCAppDelegate () <PAPasscodeViewControllerDelegate, VLCMediaFileDiscovererDelegate> {
     PAPasscodeViewController *_passcodeLockController;
     VLCDropboxTableViewController *_dropboxTableViewController;
-
-    DirectoryWatcher *_directoryWatcher;
-    NSTimer *_addMediaTimer;
-    NSMutableDictionary *_addedFiles;
 }
 
 @property (nonatomic) BOOL passcodeValidated;
@@ -62,7 +58,9 @@
     self.window.rootViewController = self.navigationController;
     [self.window makeKeyAndVisible];
 
-    _directoryWatcher = [DirectoryWatcher watchFolderWithPath:[self directoryPath] delegate:self];
+    VLCMediaFileDiscoverer *discoverer = [VLCMediaFileDiscoverer sharedInstance];
+    [discoverer addObserver:self];
+    [discoverer startDiscovering:[self directoryPath]];
 
     [self validatePasscode];
 
@@ -152,82 +150,26 @@
     return _dropboxTableViewController;
 }
 
-#pragma mark - directory watcher delegate
-
-- (void)addFileTimerFired
-{
-    NSArray *allKeys = [_addedFiles allKeys];
-    NSFileManager *fileManager = [NSFileManager defaultManager];
-    MLMediaLibrary *sharedLibrary = [MLMediaLibrary sharedMediaLibrary];
-    for (NSString *fileURL in allKeys) {
-        if (![fileManager fileExistsAtPath:fileURL])
-            continue;
-
-        NSDictionary *attribs = [fileManager attributesOfItemAtPath:fileURL error:nil];
+#pragma mark - media discovering
 
-        NSNumber *prevFetchedSize = [_addedFiles objectForKey:fileURL];
-        NSNumber *updatedSize = [attribs objectForKey:NSFileSize];
-        if (!updatedSize)
-            continue;
+- (void)mediaFileAdded:(NSString *)fileName loading:(BOOL)isLoading {
+    if (!isLoading) {
+        MLMediaLibrary *sharedLibrary = [MLMediaLibrary sharedMediaLibrary];
+        [sharedLibrary addFilePaths:@[fileName]];
 
-        if ([prevFetchedSize compare:updatedSize] == NSOrderedSame) {
-            [_addedFiles removeObjectForKey:fileURL];
-            [sharedLibrary addFilePaths:@[fileURL]];
+        /* exclude media files from backup (QA1719) */
+        NSURL *excludeURL = [NSURL fileURLWithPath:fileName];
+        [excludeURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
 
-            /* exclude media files from backup (QA1719) */
-            NSURL *excludeURL = [NSURL fileURLWithPath:fileURL];
-            [excludeURL setResourceValue:@YES forKey:NSURLIsExcludedFromBackupKey error:nil];
-
-            // TODO Should we update media db after adding new files?
-            [sharedLibrary updateMediaDatabase];
-            [_playlistViewController updateViewContents];
-        } else
-            [_addedFiles setObject:updatedSize forKey:fileURL];
-    }
-
-    if (_addedFiles.count == 0) {
-        [_addMediaTimer invalidate];
-        _addMediaTimer = nil;
+        // TODO Should we update media db after adding new files?
+        [sharedLibrary updateMediaDatabase];
+        [_playlistViewController updateViewContents];
     }
 }
 
-- (void)directoryDidChange:(DirectoryWatcher *)folderWatcher
-{
-    NSArray *foundFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[self directoryPath] error:nil];
-    NSMutableArray *matchedFiles = [NSMutableArray arrayWithCapacity:foundFiles.count];
-    for (NSString *fileName in foundFiles) {
-        if ([fileName isSupportedMediaFormat])
-            [matchedFiles addObject:[[self directoryPath] stringByAppendingPathComponent:fileName]];
-    }
-
-    NSArray *mediaFiles = [MLFile allFiles];
-    if (mediaFiles.count > matchedFiles.count) { // File was deleted
-        [[MLMediaLibrary sharedMediaLibrary] updateMediaDatabase];
-        [_playlistViewController updateViewContents];
-
-    } else if (mediaFiles.count < matchedFiles.count) { // File was added
-        NSMutableArray *addedFiles = [NSMutableArray array];
-        for (NSString *fileName in matchedFiles) {
-            NSURL *fileURL = [NSURL fileURLWithPath:fileName];
-
-            BOOL found = NO;
-            for (MLFile *mediaFile in mediaFiles) {
-                if ([mediaFile.url isEqualToString:fileURL.absoluteString])
-                    found = YES;
-            }
-
-            if (!found)
-                [addedFiles addObject:fileName];
-        }
-
-        _addedFiles = [NSMutableDictionary dictionaryWithCapacity:[addedFiles count]];
-        for (NSString *fileURL in addedFiles)
-            [_addedFiles setObject:@(0) forKey:fileURL];
-
-        _addMediaTimer = [NSTimer scheduledTimerWithTimeInterval:2. target:self
-                                                        selector:@selector(addFileTimerFired)
-                                                        userInfo:nil repeats:YES];
-    }
+- (void)mediaFileDeleted:(NSString *)name {
+    [[MLMediaLibrary sharedMediaLibrary] updateMediaDatabase];
+    [_playlistViewController updateViewContents];
 }
 
 #pragma mark - media list methods

+ 30 - 0
AspenProject/VLCMediaFileDiscoverer.h

@@ -0,0 +1,30 @@
+//
+//  VLCMediaFileDiscoverer.h
+//  VLC for iOS
+//
+//  Created by Gleb on 7/27/13.
+//  Copyright (c) 2013 VideoLAN. All rights reserved.
+//
+
+#import <Foundation/Foundation.h>
+
+@protocol VLCMediaFileDiscovererDelegate <NSObject>
+
+@optional
+- (void)mediaFileAdded:(NSString *)fileName loading:(BOOL)isLoading;
+- (void)mediaFileChanged:(NSString *)fileName size:(unsigned long long)size;
+- (void)mediaFileDeleted:(NSString *)name;
+
+@end
+
+@interface VLCMediaFileDiscoverer : NSObject
+
+- (void)addObserver:(id<VLCMediaFileDiscovererDelegate>)delegate;
+- (void)removeObserver:(id<VLCMediaFileDiscovererDelegate>)delegate;
+
+- (void)startDiscovering:(NSString *)directoryPath;
+- (void)stopDiscovering;
+
++ (instancetype)sharedInstance;
+
+@end

+ 203 - 0
AspenProject/VLCMediaFileDiscoverer.m

@@ -0,0 +1,203 @@
+//
+//  VLCMediaFileDiscoverer.m
+//  VLC for iOS
+//
+//  Created by Gleb on 7/27/13.
+//  Copyright (c) 2013 VideoLAN. All rights reserved.
+//
+//  Refer to the COPYING file of the official project for license.
+//
+
+#import "VLCMediaFileDiscoverer.h"
+#import "DirectoryWatcher.h"
+#import "NSString+SupportedMedia.h"
+
+const float MediaTimerInterval = 2.f;
+
+@interface VLCMediaFileDiscoverer () <DirectoryWatcherDelegate> {
+    NSMutableArray *_observers;
+    DirectoryWatcher *_directoryWatcher;
+
+    NSString *_directoryPath;
+    NSArray *_directoryFiles;
+    NSMutableDictionary *_addedFilesMapping;
+    NSTimer *_addMediaTimer;
+}
+
+@end
+
+@implementation VLCMediaFileDiscoverer
+
+- (id)init
+{
+    self = [super init];
+    if (self) {
+        _observers = [NSMutableArray array];
+        _addedFilesMapping = [NSMutableDictionary dictionary];
+    }
+
+    return self;
+}
+
++ (instancetype)sharedInstance
+{
+    static dispatch_once_t onceToken;
+    static VLCMediaFileDiscoverer *instance;
+    dispatch_once(&onceToken, ^{
+        instance = [VLCMediaFileDiscoverer new];
+    });
+
+    return instance;
+}
+
+#pragma mark - observation
+
+- (void)addObserver:(id<VLCMediaFileDiscovererDelegate>)delegate
+{
+    [_observers addObject:delegate];
+}
+
+- (void)removeObserver:(id<VLCMediaFileDiscovererDelegate>)delegate
+{
+    [_observers removeObject:delegate];
+}
+
+- (void)notifyFileDeleted:(NSString *)fileName
+{
+    if (![fileName isSupportedMediaFormat])
+        return;
+
+    for (id<VLCMediaFileDiscovererDelegate> delegate in _observers) {
+        if ([delegate respondsToSelector:@selector(mediaFileDeleted:)]) {
+            [delegate mediaFileDeleted:[self filePath:fileName]];
+        }
+    }
+}
+
+- (void)notifyFileAdded:(NSString *)fileName loading:(BOOL)isLoading
+{
+    for (id<VLCMediaFileDiscovererDelegate> delegate in _observers) {
+        if ([delegate respondsToSelector:@selector(mediaFileAdded:loading:)]) {
+            [delegate mediaFileAdded:[self filePath:fileName] loading:isLoading];
+        }
+    }
+}
+
+- (void)notifySizeChanged:(NSString *)fileName size:(unsigned long long)size
+{
+    for (id<VLCMediaFileDiscovererDelegate> delegate in _observers) {
+        if ([delegate respondsToSelector:@selector(mediaFileChanged:size:)]) {
+            [delegate mediaFileChanged:[self filePath:fileName] size:size];
+        }
+    }
+}
+
+#pragma mark - discovering
+
+- (void)startDiscovering:(NSString *)directoryPath
+{
+    _directoryPath = directoryPath;
+     _directoryFiles = [self directoryFiles];
+
+    _directoryWatcher = [DirectoryWatcher watchFolderWithPath:directoryPath delegate:self];
+}
+
+- (void)stopDiscovering
+{
+    [_directoryWatcher invalidate];
+    _directoryWatcher.delegate = nil;
+
+    [self invalidateTimer];
+}
+
+#pragma mark -
+
+- (NSArray *)directoryFiles
+{
+    NSArray *foundFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:_directoryPath error:nil];
+
+    return foundFiles;
+}
+
+- (NSString *)filePath:(NSString *)fileName
+{
+    return [_directoryPath stringByAppendingPathComponent:fileName];
+}
+
+#pragma mark - directory watcher delegate
+
+- (void)directoryDidChange:(DirectoryWatcher *)folderWatcher
+{
+    NSArray *foundFiles = [self directoryFiles];
+
+    if (_directoryFiles.count > foundFiles.count) { // File was deleted
+        NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"not (self in %@)", foundFiles];
+        NSArray *deletedFiles = [_directoryFiles filteredArrayUsingPredicate:filterPredicate];
+
+        for (NSString *fileName in deletedFiles) {
+            [self notifyFileDeleted:fileName];
+        }
+
+    } else if (_directoryFiles.count < foundFiles.count) { // File was added
+        NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"not (self in %@)", _directoryFiles];
+        NSArray *addedFiles = [foundFiles filteredArrayUsingPredicate:filterPredicate];
+
+        for (NSString *fileName in addedFiles) {
+            if ([fileName isSupportedMediaFormat]) {
+                [_addedFilesMapping setObject:@(0) forKey:fileName];
+                [self notifyFileAdded:fileName loading:YES];
+            }
+        }
+
+        if (![_addMediaTimer isValid]) {
+            _addMediaTimer = [NSTimer scheduledTimerWithTimeInterval:MediaTimerInterval
+                                          target:self selector:@selector(addFileTimerFired)
+                                                            userInfo:nil repeats:YES];
+        }
+    }
+
+    _directoryFiles = foundFiles;
+}
+
+#pragma mark - media timer
+
+- (void)addFileTimerFired
+{
+    NSArray *allKeys = [_addedFilesMapping allKeys];
+    NSFileManager *fileManager = [NSFileManager defaultManager];
+
+    for (NSString *fileName in allKeys) {
+        NSString *filePath = [self filePath:fileName];
+        if (![fileManager fileExistsAtPath:filePath]) {
+            [_addedFilesMapping removeObjectForKey:fileName];
+            continue;
+        }
+
+        NSNumber *prevFetchedSize = [_addedFilesMapping objectForKey:fileName];
+
+        NSDictionary *attribs = [fileManager attributesOfItemAtPath:filePath error:nil];
+        NSNumber *updatedSize = [attribs objectForKey:NSFileSize];
+        if (!updatedSize)
+            continue;
+
+        [self notifySizeChanged:fileName size:[updatedSize unsignedLongLongValue]];
+
+        if ([prevFetchedSize compare:updatedSize] == NSOrderedSame) {
+            [_addedFilesMapping removeObjectForKey:fileName];
+            [self notifyFileAdded:fileName loading:NO];
+
+        } else
+            [_addedFilesMapping setObject:updatedSize forKey:fileName];
+    }
+
+    if (_addedFilesMapping.count == 0)
+        [self invalidateTimer];
+}
+
+- (void)invalidateTimer
+{
+    [_addMediaTimer invalidate];
+    _addMediaTimer = nil;
+}
+
+@end

+ 16 - 2
VLC for iOS.xcodeproj/project.pbxproj

@@ -250,6 +250,7 @@
 		A7CB0DB21716F72600050CF3 /* PlayingExternally~iphone.png in Resources */ = {isa = PBXBuildFile; fileRef = A7CB0DAE1716F72600050CF3 /* PlayingExternally~iphone.png */; };
 		A7CB0DB31716F72600050CF3 /* PlayingExternally~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = A7CB0DAF1716F72600050CF3 /* PlayingExternally~ipad.png */; };
 		A7CB0DB41716F72600050CF3 /* PlayingExternally@2x~ipad.png in Resources */ = {isa = PBXBuildFile; fileRef = A7CB0DB01716F72600050CF3 /* PlayingExternally@2x~ipad.png */; };
+		A7D03A4717A41AD30022C16F /* VLCMediaFileDiscoverer.m in Sources */ = {isa = PBXBuildFile; fileRef = A7D03A4617A41AD30022C16F /* VLCMediaFileDiscoverer.m */; };
 		A7DA16D1171083DF00D6FED9 /* VLCExternalDisplayController.m in Sources */ = {isa = PBXBuildFile; fileRef = A7DA16D0171083DF00D6FED9 /* VLCExternalDisplayController.m */; };
 		A7FF9F3E17428C1900999819 /* DeleteButton.png in Resources */ = {isa = PBXBuildFile; fileRef = A7FF9F3D17428C1900999819 /* DeleteButton.png */; };
 		A7FF9F4017428C3800999819 /* DeleteButton@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = A7FF9F3F17428C3800999819 /* DeleteButton@2x.png */; };
@@ -682,6 +683,8 @@
 		A7CB0DAE1716F72600050CF3 /* PlayingExternally~iphone.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "PlayingExternally~iphone.png"; sourceTree = "<group>"; };
 		A7CB0DAF1716F72600050CF3 /* PlayingExternally~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "PlayingExternally~ipad.png"; sourceTree = "<group>"; };
 		A7CB0DB01716F72600050CF3 /* PlayingExternally@2x~ipad.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "PlayingExternally@2x~ipad.png"; sourceTree = "<group>"; };
+		A7D03A4517A41AD30022C16F /* VLCMediaFileDiscoverer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCMediaFileDiscoverer.h; sourceTree = "<group>"; };
+		A7D03A4617A41AD30022C16F /* VLCMediaFileDiscoverer.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCMediaFileDiscoverer.m; sourceTree = "<group>"; };
 		A7DA16CF171083DF00D6FED9 /* VLCExternalDisplayController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = VLCExternalDisplayController.h; sourceTree = "<group>"; };
 		A7DA16D0171083DF00D6FED9 /* VLCExternalDisplayController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = VLCExternalDisplayController.m; sourceTree = "<group>"; };
 		A7FF9F3D17428C1900999819 /* DeleteButton.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = DeleteButton.png; sourceTree = "<group>"; };
@@ -1246,11 +1249,10 @@
 		7D94FCE416DE7D1000F2623B /* AspenProject */ = {
 			isa = PBXGroup;
 			children = (
-				A7C30257175A3C7A00AD4388 /* DirectoryWatcher.h */,
-				A7C30258175A3C7A00AD4388 /* DirectoryWatcher.m */,
 				7D6B08BB174A72A900A05173 /* VLCConstants.h */,
 				7D94FCED16DE7D1000F2623B /* VLCAppDelegate.h */,
 				7D94FCEE16DE7D1000F2623B /* VLCAppDelegate.m */,
+				A7D03A4817A4249F0022C16F /* MediaDiscovering */,
 				7D2339AB176DE70E008D223C /* Menu */,
 				7D5F7ABA175265CB006CCCFA /* HTTP Connectivity */,
 				7D5F7AB9175265B2006CCCFA /* Playback */,
@@ -1398,6 +1400,17 @@
 			name = Extensions;
 			sourceTree = "<group>";
 		};
+		A7D03A4817A4249F0022C16F /* MediaDiscovering */ = {
+			isa = PBXGroup;
+			children = (
+				A7C30257175A3C7A00AD4388 /* DirectoryWatcher.h */,
+				A7C30258175A3C7A00AD4388 /* DirectoryWatcher.m */,
+				A7D03A4517A41AD30022C16F /* VLCMediaFileDiscoverer.h */,
+				A7D03A4617A41AD30022C16F /* VLCMediaFileDiscoverer.m */,
+			);
+			name = MediaDiscovering;
+			sourceTree = "<group>";
+		};
 		CC1BBC441704936500A20CBF /* External VLC Libraries */ = {
 			isa = PBXGroup;
 			children = (
@@ -1723,6 +1736,7 @@
 				A7990067176E9CF3009E8267 /* VLCMenuButton.m in Sources */,
 				7DDABBB71775A3C7003D8937 /* UIDevice+SpeedCategory.m in Sources */,
 				7DD2A3A7179BFAFE003EB537 /* VLCBugreporter.m in Sources */,
+				A7D03A4717A41AD30022C16F /* VLCMediaFileDiscoverer.m in Sources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};