Commit 9766afe2 authored by Gleb Pinigin's avatar Gleb Pinigin

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.
parent 698bd790
......@@ -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
......
//
// 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
//
// 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
......@@ -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;
};
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment