VLCMediaFileDiscoverer.m 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. //
  2. // VLCMediaFileDiscoverer.m
  3. // VLC for iOS
  4. //
  5. // Created by Gleb on 7/27/13.
  6. // Copyright (c) 2013 VideoLAN. All rights reserved.
  7. //
  8. // Refer to the COPYING file of the official project for license.
  9. //
  10. #import "VLCMediaFileDiscoverer.h"
  11. #import "DirectoryWatcher.h"
  12. #import "NSString+SupportedMedia.h"
  13. const float MediaTimerInterval = 2.f;
  14. @interface VLCMediaFileDiscoverer () <DirectoryWatcherDelegate> {
  15. NSMutableArray *_observers;
  16. DirectoryWatcher *_directoryWatcher;
  17. NSString *_directoryPath;
  18. NSArray *_directoryFiles;
  19. NSMutableDictionary *_addedFilesMapping;
  20. NSTimer *_addMediaTimer;
  21. }
  22. @end
  23. @implementation VLCMediaFileDiscoverer
  24. - (id)init
  25. {
  26. self = [super init];
  27. if (self) {
  28. _observers = [NSMutableArray array];
  29. _addedFilesMapping = [NSMutableDictionary dictionary];
  30. }
  31. return self;
  32. }
  33. + (instancetype)sharedInstance
  34. {
  35. static dispatch_once_t onceToken;
  36. static VLCMediaFileDiscoverer *instance;
  37. dispatch_once(&onceToken, ^{
  38. instance = [VLCMediaFileDiscoverer new];
  39. });
  40. return instance;
  41. }
  42. #pragma mark - observation
  43. - (void)addObserver:(id<VLCMediaFileDiscovererDelegate>)delegate
  44. {
  45. [_observers addObject:delegate];
  46. }
  47. - (void)removeObserver:(id<VLCMediaFileDiscovererDelegate>)delegate
  48. {
  49. [_observers removeObject:delegate];
  50. }
  51. - (void)notifyFileDeleted:(NSString *)fileName
  52. {
  53. if (![fileName isSupportedMediaFormat])
  54. return;
  55. for (id<VLCMediaFileDiscovererDelegate> delegate in _observers) {
  56. if ([delegate respondsToSelector:@selector(mediaFileDeleted:)]) {
  57. [delegate mediaFileDeleted:[self filePath:fileName]];
  58. }
  59. }
  60. }
  61. - (void)notifyFileAdded:(NSString *)fileName loading:(BOOL)isLoading
  62. {
  63. for (id<VLCMediaFileDiscovererDelegate> delegate in _observers) {
  64. if ([delegate respondsToSelector:@selector(mediaFileAdded:loading:)]) {
  65. [delegate mediaFileAdded:[self filePath:fileName] loading:isLoading];
  66. }
  67. }
  68. }
  69. - (void)notifySizeChanged:(NSString *)fileName size:(unsigned long long)size
  70. {
  71. for (id<VLCMediaFileDiscovererDelegate> delegate in _observers) {
  72. if ([delegate respondsToSelector:@selector(mediaFileChanged:size:)]) {
  73. [delegate mediaFileChanged:[self filePath:fileName] size:size];
  74. }
  75. }
  76. }
  77. #pragma mark - discovering
  78. - (void)startDiscovering:(NSString *)directoryPath
  79. {
  80. _directoryPath = directoryPath;
  81. _directoryFiles = [self directoryFiles];
  82. _directoryWatcher = [DirectoryWatcher watchFolderWithPath:directoryPath delegate:self];
  83. }
  84. - (void)stopDiscovering
  85. {
  86. [_directoryWatcher invalidate];
  87. _directoryWatcher.delegate = nil;
  88. [self invalidateTimer];
  89. }
  90. #pragma mark -
  91. - (NSArray *)directoryFiles
  92. {
  93. NSArray *foundFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:_directoryPath error:nil];
  94. return foundFiles;
  95. }
  96. - (NSString *)filePath:(NSString *)fileName
  97. {
  98. return [_directoryPath stringByAppendingPathComponent:fileName];
  99. }
  100. #pragma mark - directory watcher delegate
  101. - (void)directoryDidChange:(DirectoryWatcher *)folderWatcher
  102. {
  103. NSArray *foundFiles = [self directoryFiles];
  104. if (_directoryFiles.count > foundFiles.count) { // File was deleted
  105. NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"not (self in %@)", foundFiles];
  106. NSArray *deletedFiles = [_directoryFiles filteredArrayUsingPredicate:filterPredicate];
  107. for (NSString *fileName in deletedFiles) {
  108. [self notifyFileDeleted:fileName];
  109. }
  110. } else if (_directoryFiles.count < foundFiles.count) { // File was added
  111. NSPredicate *filterPredicate = [NSPredicate predicateWithFormat:@"not (self in %@)", _directoryFiles];
  112. NSArray *addedFiles = [foundFiles filteredArrayUsingPredicate:filterPredicate];
  113. for (NSString *fileName in addedFiles) {
  114. if ([fileName isSupportedMediaFormat]) {
  115. [_addedFilesMapping setObject:@(0) forKey:fileName];
  116. [self notifyFileAdded:fileName loading:YES];
  117. }
  118. }
  119. if (![_addMediaTimer isValid]) {
  120. _addMediaTimer = [NSTimer scheduledTimerWithTimeInterval:MediaTimerInterval
  121. target:self selector:@selector(addFileTimerFired)
  122. userInfo:nil repeats:YES];
  123. }
  124. }
  125. _directoryFiles = foundFiles;
  126. }
  127. #pragma mark - media timer
  128. - (void)addFileTimerFired
  129. {
  130. NSArray *allKeys = [_addedFilesMapping allKeys];
  131. NSFileManager *fileManager = [NSFileManager defaultManager];
  132. for (NSString *fileName in allKeys) {
  133. NSString *filePath = [self filePath:fileName];
  134. if (![fileManager fileExistsAtPath:filePath]) {
  135. [_addedFilesMapping removeObjectForKey:fileName];
  136. continue;
  137. }
  138. NSNumber *prevFetchedSize = [_addedFilesMapping objectForKey:fileName];
  139. NSDictionary *attribs = [fileManager attributesOfItemAtPath:filePath error:nil];
  140. NSNumber *updatedSize = [attribs objectForKey:NSFileSize];
  141. if (!updatedSize)
  142. continue;
  143. [self notifySizeChanged:fileName size:[updatedSize unsignedLongLongValue]];
  144. if ([prevFetchedSize compare:updatedSize] == NSOrderedSame) {
  145. [_addedFilesMapping removeObjectForKey:fileName];
  146. [self notifyFileAdded:fileName loading:NO];
  147. } else
  148. [_addedFilesMapping setObject:updatedSize forKey:fileName];
  149. }
  150. if (_addedFilesMapping.count == 0)
  151. [self invalidateTimer];
  152. }
  153. - (void)invalidateTimer
  154. {
  155. [_addMediaTimer invalidate];
  156. _addMediaTimer = nil;
  157. }
  158. @end