source: branches/CoreData/SapphireFrappliance/MetaData/SapphireMObjects/SapphireDirectoryMetaData.m @ 570

Revision 570, 16.3 KB checked in by gbooker, 7 years ago (diff)

Upgrading metadata isn't done when finder starts anymore

Line 
1#import "SapphireDirectoryMetaData.h"
2#import "SapphireFileMetaData.h"
3#import "SapphireDirectorySymLink.h"
4#import "SapphireFileSymLink.h"
5#import "SapphireMetaDataSupport.h"
6#import "NSFileManager-Extensions.h"
7#import "CoreDataSupportFunctions.h"
8#import "SapphireMetaDataScanner.h"
9#import "SapphireImportHelper.h"
10#import "NSString-Extensions.h"
11#import "SapphireMetaDataUpgrading.h"
12
13@implementation NSString (episodeSorting)
14
15/*!
16 * @brief Comparison for directory names
17 *
18 * @param other The other string to compare
19 * @return The comparison result of the compare
20 */
21- (NSComparisonResult) directoryNameCompare:(NSString *)other
22{
23        NSString *myShortenedName=nil ;
24        NSString *otherShortenedName=nil ;
25        /* Make sure we get titles leading with "A" & "The" where the belong */
26        if([[self lowercaseString] hasPrefix:@"a "] && [self length]>2)
27                myShortenedName=[self substringFromIndex:2];
28        else if([[self lowercaseString] hasPrefix:@"the "] && [self length]>4)
29                myShortenedName=[self substringFromIndex:4];
30        if([[other lowercaseString] hasPrefix:@"a "]&& [other length]>2)
31                otherShortenedName=[other substringFromIndex:2];
32        else if([[other lowercaseString] hasPrefix:@"the "] && [other length]>4)
33                otherShortenedName=[other substringFromIndex:4];
34       
35        if(myShortenedName==nil)
36                myShortenedName=self;
37        if(otherShortenedName==nil)
38                otherShortenedName=other;
39       
40        return [myShortenedName compare:otherShortenedName options:NSCaseInsensitiveSearch | NSNumericSearch];
41}
42@end
43
44@implementation SapphireDirectoryMetaData
45
46+ (SapphireDirectoryMetaData *)directoryWithPath:(NSString *)path inContext:(NSManagedObjectContext *)moc
47{
48        SapphireMetaData *meta = [SapphireMetaData metaDataWithPath:path inContext:moc];
49        if([meta isKindOfClass:[SapphireDirectoryMetaData class]])
50                return (SapphireDirectoryMetaData *)meta;
51        return nil;
52}
53
54+ (SapphireDirectoryMetaData *)internalCreateDirectoryWithPath:(NSString *)path parent:(SapphireDirectoryMetaData *)parent inContext:(NSManagedObjectContext *)moc
55{
56        SapphireDirectoryMetaData *ret = [NSEntityDescription insertNewObjectForEntityForName:SapphireDirectoryMetaDataName inManagedObjectContext:moc];
57        ret.parent = parent;
58        ret.path = path;
59       
60        return ret;
61}
62
63+ (SapphireDirectoryMetaData *)createDirectoryWithPath:(NSString *)path inContext:(NSManagedObjectContext *)moc
64{
65        SapphireDirectoryMetaData *ret = [SapphireDirectoryMetaData directoryWithPath:path inContext:moc];
66        if(ret != nil)
67                return ret;
68       
69        SapphireDirectoryMetaData *parent = [SapphireDirectoryMetaData createDirectoryWithPath:[path stringByDeletingLastPathComponent] inContext:moc];
70        ret = [SapphireDirectoryMetaData internalCreateDirectoryWithPath:path parent:parent inContext:moc];
71       
72        return ret;
73}
74
75+ (SapphireDirectoryMetaData *)createDirectoryWithPath:(NSString *)path parent:(SapphireDirectoryMetaData *)parent inContext:(NSManagedObjectContext *)moc
76{
77        SapphireDirectoryMetaData *ret = [SapphireDirectoryMetaData directoryWithPath:path inContext:moc];
78        if(ret != nil)
79                return ret;
80
81        return [SapphireDirectoryMetaData internalCreateDirectoryWithPath:path parent:parent inContext:moc];
82}
83
84- (void) dealloc
85{
86        [importArray release];
87        Basic_Directory_Function_Deallocs
88        [super dealloc];
89}
90
91
92- (void)insertDictionary:(NSDictionary *)dict withDefer:(NSMutableDictionary *)defer andDisplay:(SapphireMetaDataUpgrading *)display
93{
94        NSManagedObjectContext *moc = [self managedObjectContext];
95        NSDictionary *dirs = [dict objectForKey:@"Dirs"];
96        NSEnumerator *dirEnum = [dirs keyEnumerator];
97        NSString *dir;
98        while((dir = [dirEnum nextObject]) != nil)
99        {
100                NSString *path = [self.path stringByAppendingPathComponent:dir];
101                SapphireDirectoryMetaData *newDir = [SapphireDirectoryMetaData createDirectoryWithPath:path parent:self inContext:moc];
102                [newDir insertDictionary:[dirs objectForKey:dir] withDefer:defer andDisplay:display];
103                if([newDir.metaDirsSet count] == 0 && [newDir.metaFilesSet count] == 0)
104                {
105                        newDir.parent = nil;
106                        [moc deleteObject:newDir];
107                }
108        }
109        NSDictionary *files = [dict objectForKey:@"Files"];
110        NSEnumerator *fileEnum = [files keyEnumerator];
111        NSString *file;
112        while((file = [fileEnum nextObject]) != nil)
113        {
114                NSString *path = [self.path stringByAppendingPathComponent:file];
115//              [display setCurrentFile:[NSString stringByCroppingDirectoryPath:path toLength:3]];
116                SapphireFileMetaData *newFile = [SapphireFileMetaData createFileWithPath:path parent:self inContext:moc];
117                [newFile insertDictionary:[files objectForKey:file] withDefer:defer];
118        }
119}
120
121- (BOOL)checkPredicate:(NSComparisonPredicate *)pred duplicateSet:(NSMutableSet *)dups
122{
123        NSManagedObjectContext *moc = [self managedObjectContext];
124        NSPredicate *pathPredicate = [NSPredicate predicateWithFormat:@"path BEGINSWITH %@", self.path];
125        NSPredicate *predicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:pathPredicate, pred, filterPredicate, nil]];
126        if(entityExists(SapphireFileMetaDataName, moc, predicate))
127                return YES;
128       
129        NSPredicate *subPred = [NSPredicate predicateWithFormat:[@"file." stringByAppendingString:[pred predicateFormat]]];
130        NSPredicate *subFilter = nil;
131        if(filterPredicate != nil)
132                subFilter = [NSPredicate predicateWithFormat:[@"file." stringByAppendingString:[filterPredicate predicateFormat]]];
133        predicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:pathPredicate, subPred, subFilter, nil]];
134        if(entityExists(SapphireFileSymLinkName, moc, predicate))
135                return YES;
136       
137        NSArray *array = doFetchRequest(SapphireDirectorySymLinkName, moc, pathPredicate);
138        if([array count])
139        {
140                SapphireDirectorySymLink *sym = nil;
141                NSEnumerator *symEnum = [array objectEnumerator];
142                while((sym = [symEnum nextObject]) != nil)
143                {
144                        SapphireDirectoryMetaData *dir = sym.directory;
145                        NSString *finalPath = dir.path;
146                        if([dups containsObject:finalPath])
147                                continue;
148                        [dups addObject:finalPath];
149                        [dir setFilterPredicate:filterPredicate];
150                        if([dir checkPredicate:pred duplicateSet:dups])
151                                return YES;
152                }
153        }
154        return NO;
155}
156
157NSComparisonResult fileAndLinkEpisodeCompare(id file1, id file2)
158{
159        /*Resolve link and try to sort by episodes*/
160        SapphireFileMetaData *first;
161        if([file1 isKindOfClass:[SapphireFileMetaData class]])
162                first = (SapphireFileMetaData *)file1;
163        else
164                first = ((SapphireFileSymLink *)file1).file;
165
166        SapphireFileMetaData *second;
167        if([file2 isKindOfClass:[SapphireFileMetaData class]])
168                second = (SapphireFileMetaData *)file2;
169        else
170                second = ((SapphireFileSymLink *)file2).file;
171       
172        NSComparisonResult result = [first episodeCompare:second];
173        if(result != NSOrderedSame)
174                return result;
175       
176        /*Finally sort by path*/
177        return [[[file1 valueForKey:@"path"] lastPathComponent] compare:[[file2 valueForKey:@"path"] lastPathComponent] options:NSCaseInsensitiveSearch | NSNumericSearch];
178}
179
180- (NSArray *)files
181{
182        NSArray *files = [self.metaFilesSet allObjects];
183        files = [files arrayByAddingObjectsFromArray:[self.linkedFilesSet allObjects]];
184        if(filterPredicate != nil)
185                files = [files filteredArrayUsingPredicate:filterPredicate];
186        files = [files sortedArrayUsingSelector:@selector(episodeCompare:)];
187        NSArray *fileNames = [files valueForKeyPath:@"path.lastPathComponent"];
188        return fileNames;
189}
190
191- (NSArray *)directories
192{
193        NSArray *dirs = [self.metaDirsSet allObjects];
194        dirs = [dirs arrayByAddingObjectsFromArray:[self.linkedDirsSet allObjects]];
195        if(filterPredicate != nil)
196                dirs = [dirs filteredArrayUsingPredicate:filterPredicate];
197        NSArray *dirNames = [dirs valueForKeyPath:@"path.lastPathComponent"];
198        return [dirNames sortedArrayUsingSelector:@selector(directoryNameCompare:)];
199}
200
201- (id <SapphireDirectory>)metaDataForDirectory:(NSString *)directory
202{
203        NSString *path = [self.path stringByAppendingPathComponent:directory];
204        NSManagedObjectContext *moc = [self managedObjectContext];
205       
206        SapphireDirectoryMetaData *ret = [SapphireDirectoryMetaData directoryWithPath:path inContext:moc];
207        if(ret == nil)
208                ret = [SapphireDirectorySymLink directoryLinkWithPath:path inContext:moc].directory;
209       
210        return ret;
211}
212
213- (SapphireFileMetaData *)metaDataForFile:(NSString *)file
214{
215        NSString *path = [self.path stringByAppendingPathComponent:file];
216        NSManagedObjectContext *moc = [self managedObjectContext];
217       
218        SapphireFileMetaData *ret = [SapphireFileMetaData fileWithPath:path inContext:moc];
219        if(ret == nil)
220                ret = [SapphireFileSymLink fileLinkWithPath:path inContext:moc].file;
221       
222        return ret;
223}
224
225- (void)reloadDirectoryContents
226{
227        NSManagedObjectContext *moc = [self managedObjectContext];
228        [moc refreshObject:self mergeChanges:YES];
229       
230        [importArray release];
231        importArray = [[NSMutableArray alloc] init];
232
233        NSMutableSet *files = [[self.metaFilesSet valueForKeyPath:@"path.lastPathComponent"] mutableCopy];
234        NSMutableSet *dirs = [[self.metaDirsSet valueForKeyPath:@"path.lastPathComponent"] mutableCopy];
235        NSMutableSet *linkedFiles = [[self.linkedFilesSet valueForKeyPath:@"path.lastPathComponent"] mutableCopy];
236        NSMutableSet *linkedDirs = [[self.linkedDirsSet valueForKeyPath:@"path.lastPathComponent"] mutableCopy];
237       
238        NSFileManager *fm = [NSFileManager defaultManager];
239       
240        NSArray *names = [fm directoryContentsAtPath:self.path];
241        NSEnumerator *nameEnum = [names objectEnumerator];
242        NSString *name;
243        while((name = [nameEnum nextObject]) != nil)
244        {
245                NSString *filePath = [self.path stringByAppendingPathComponent:name];
246                if(![fm acceptFilePath:filePath])
247                        continue;
248
249                NSDictionary *attributes = [fm fileAttributesAtPath:filePath traverseLink:NO];
250                if([[attributes fileType] isEqualToString:NSFileTypeSymbolicLink])
251                {
252                        /*Sym links are fun*/
253                        NSString *resolvedPath = [filePath stringByResolvingSymlinksInPath];
254                        if(![fm acceptFilePath:resolvedPath])
255                                continue;
256                       
257                        if([fm isDirectory:resolvedPath])
258                        {
259                                if([dirs containsObject:name])
260                                {
261                                        //Dir moved, but original data is still here
262                                        SapphireDirectoryMetaData *resolved = [SapphireDirectoryMetaData directoryWithPath:resolvedPath inContext:moc];
263                                        if(resolved != nil)
264                                                [moc deleteObject:resolved];
265
266                                        resolved = (SapphireDirectoryMetaData *)[self metaDataForDirectory:name];
267                                        resolved.parent = [SapphireDirectoryMetaData createDirectoryWithPath:[resolvedPath stringByDeletingLastPathComponent] inContext:moc];
268                                        resolved.path = resolvedPath;
269                                        [dirs removeObject:name];
270                                }
271                                [SapphireDirectorySymLink createDirectoryLinkWithPath:filePath toPath:resolvedPath inContext:moc];
272                                [linkedDirs removeObject:name];
273                        }
274                        else
275                        {
276                                if([files containsObject:name])
277                                {
278                                        //File moved, but original data is still here
279                                        SapphireFileMetaData *resolved = [SapphireFileMetaData fileWithPath:resolvedPath inContext:moc];
280                                        if(resolved != nil)
281                                                [moc deleteObject:resolved];
282
283                                        resolved = [self metaDataForFile:name];
284                                        resolved.parent = [SapphireDirectoryMetaData createDirectoryWithPath:[resolvedPath stringByDeletingLastPathComponent] inContext:moc];
285                                        resolved.path = resolvedPath;
286                                        [files removeObject:name];
287                                }
288                                [SapphireFileSymLink createFileLinkWithPath:filePath toPath:resolvedPath inContext:moc];
289                                [linkedFiles removeObject:name];
290                        }
291                }
292                else if([fm isDirectory:filePath])
293                {
294                        [SapphireDirectoryMetaData createDirectoryWithPath:filePath parent:self inContext:moc];
295                        [dirs removeObject:name];
296                }
297                else
298                {
299                        SapphireFileMetaData *file = [SapphireFileMetaData fileWithPath:filePath inContext:moc];
300                        if(file == nil)
301                        {
302                                file = [SapphireFileMetaData createFileWithPath:filePath parent:self inContext:moc];
303                                [importArray addObject:file];
304                        }
305                        else if([file needsUpdating])
306                                [importArray addObject:file];
307                        [files removeObject:name];
308                }
309        }
310       
311        nameEnum = [files objectEnumerator];
312        while((name = [nameEnum nextObject]) != nil)
313        {
314                SapphireFileMetaData *file = [self metaDataForFile:name];
315                [moc deleteObject:file];
316        }
317       
318        nameEnum = [dirs objectEnumerator];
319        while((name = [nameEnum nextObject]) != nil)
320        {
321                SapphireDirectoryMetaData *dir = (SapphireDirectoryMetaData *)[self metaDataForDirectory:name];
322                [moc deleteObject:dir];
323        }
324       
325        nameEnum = [linkedFiles objectEnumerator];
326        while((name = [nameEnum nextObject]) != nil)
327        {
328                SapphireFileSymLink *link = [SapphireFileSymLink fileLinkWithPath:[self.path stringByAppendingPathComponent:name] inContext:moc];
329                [moc deleteObject:link];
330        }
331       
332        nameEnum = [linkedDirs objectEnumerator];
333        while((name = [nameEnum nextObject]) != nil)
334        {
335                SapphireDirectorySymLink *link = [SapphireDirectorySymLink directoryLinkWithPath:[self.path stringByAppendingPathComponent:name] inContext:moc];
336                [moc deleteObject:link];
337        }
338        [SapphireMetaDataSupport save:moc];
339        [delegate directoryContentsChanged];
340}
341
342- (NSString *)coverArtPathUpToParents:(int)parents
343{
344        NSString *ret = searchCoverArtExtForPath([[self path] stringByAppendingPathComponent:@"Cover Art/cover"]);
345        if(ret != nil)
346                return ret;
347       
348        ret = searchCoverArtExtForPath([[self path] stringByAppendingPathComponent:@"cover"]);
349        if(ret != nil)
350                return ret;
351       
352        if(parents != 0)
353                return [[self parent] coverArtPathUpToParents:parents-1];
354        return nil;
355}
356
357- (NSString *)coverArtPath
358{
359        return [self coverArtPathUpToParents:2];
360}
361
362/*Function to invoke a command on all files in a subtree*/
363- (void)invokeRecursivelyOnFiles:(NSInvocation *)fileInv
364{
365        NSManagedObjectContext *moc = [self managedObjectContext];
366        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"path BEGINSWITH %@", self.path];
367        if(filterPredicate != nil)
368                predicate = [NSCompoundPredicate andPredicateWithSubpredicates:[NSArray arrayWithObjects:predicate, filterPredicate, nil]];
369        NSArray *array = doFetchRequest(SapphireFileMetaDataName, moc, predicate);
370
371        if([array count])
372        {
373                SapphireFileMetaData *file;
374                NSEnumerator *fileEnum = [array objectEnumerator];
375                while((file = [fileEnum nextObject]) != nil)
376                {
377                        [fileInv invokeWithTarget:file];
378                }
379        }
380}
381
382- (BOOL)checkPredicate:(NSComparisonPredicate *)pred
383{
384        NSMutableSet *dupSet = [[NSMutableSet alloc] init];
385        BOOL ret = [self checkPredicate:pred duplicateSet:dupSet];
386        [dupSet release];
387        return ret;
388}
389
390- (void)getSubFileMetasWithDelegate:(id <SapphireMetaDataScannerDelegate>)subDelegate skipDirectories:(NSMutableSet *)skip
391{
392        /*Scan dir and create scanner*/
393        [self reloadDirectoryContents];
394        SapphireMetaDataScanner *scanner = [[SapphireMetaDataScanner alloc] initWithDirectoryMetaData:self delegate:subDelegate];
395        /*Add ourselves to not rescan*/
396        [skip addObject:self.path];
397        [scanner setSkipDirectories:skip];
398        /*We want results*/
399        [scanner setGivesResults:YES];
400        [scanner release];
401}
402
403- (void)scanForNewFilesWithDelegate:(id <SapphireMetaDataScannerDelegate>)subDelegate skipDirectories:(NSMutableSet *)skip
404{
405        /*Scan dir and create scanner*/
406        [self reloadDirectoryContents];
407        SapphireMetaDataScanner *scanner = [[SapphireMetaDataScanner alloc] initWithDirectoryMetaData:self delegate:subDelegate];
408        /*Add ourselves to not rescan*/
409        [skip addObject:[self path]];
410        [scanner setSkipDirectories:skip];
411        /*We don't want results*/
412        [scanner setGivesResults:NO];
413        [scanner release];
414}
415
416- (void)processNextFile
417{
418        if(![importArray count])
419        {
420                importing &= ~2;
421                return;
422        }
423        SapphireFileMetaData *file = [importArray objectAtIndex:0];
424       
425        /*Get the file and update it*/
426        importing |= 2;
427        [[SapphireImportHelper sharedHelperForContext:[self managedObjectContext]] importAllData:file inform:self];
428}
429
430- (oneway void)informComplete:(BOOL)updated
431{
432        /*Tell delegate we updated*/
433        [SapphireMetaDataSupport save:[self managedObjectContext]];
434        SapphireFileMetaData *file = [importArray objectAtIndex:0];
435        [delegate updateCompleteForFile:[[file path] lastPathComponent]];
436       
437        /*Remove from list and redo timer*/
438        [importArray removeObjectAtIndex:0];
439        if(importing & 1)
440                [self processNextFile];
441        else
442                importing = 0;
443}
444
445- (void)cancelImport
446{
447        importing &= ~1;
448}
449
450- (void)resumeImport
451{
452        importing |= 1;
453        if(!(importing & 2))
454                [self processNextFile];
455}
456
457- (void)refreshAllObjects
458{
459        NSManagedObjectContext *moc = [self managedObjectContext];
460        NSEnumerator *objEnum;
461        NSManagedObject *obj;
462       
463        objEnum = [self.metaFilesSet objectEnumerator];
464        while((obj = [objEnum nextObject]) != nil)
465                [moc refreshObject:obj mergeChanges:YES];
466       
467        objEnum = [self.metaDirsSet objectEnumerator];
468        while((obj = [objEnum nextObject]) != nil)
469                [moc refreshObject:obj mergeChanges:YES];
470       
471        objEnum = [self.linkedFilesSet objectEnumerator];
472        while((obj = [objEnum nextObject]) != nil)
473                [moc refreshObject:obj mergeChanges:YES];
474       
475        objEnum = [self.linkedDirsSet objectEnumerator];
476        while((obj = [objEnum nextObject]) != nil)
477                [moc refreshObject:obj mergeChanges:YES];
478       
479        [moc refreshObject:self mergeChanges:YES];
480}
481
482#define RECURSIVE_FUNCTIONS_ALREADY_DEFINED
483#include "SapphireBasicDirectoryFunctions.h"
484
485@end
Note: See TracBrowser for help on using the repository browser.