source: trunk/SapphireMetaData.m @ 119

Revision 119, 24.9 KB checked in by pmerrill, 7 years ago (diff)

syntax

Line 
1//
2//  SapphireMetaData.m
3//  Sapphire
4//
5//  Created by Graham Booker on 6/22/07.
6//  Copyright 2007 __www.nanopi.net__. All rights reserved.
7//
8
9#import "SapphireMetaData.h"
10#import <QTKit/QTKit.h>
11#include <sys/types.h>
12#include <sys/stat.h>
13#import "SapphireSettings.h"
14
15//Structure Specific Keys
16#define FILES_KEY                                       @"Files"
17#define DIRS_KEY                                        @"Dirs"
18#define META_VERSION_KEY                        @"Version"
19#define META_VERSION                            1
20
21//File Specific Keys
22#define MODIFIED_KEY                                    @"Modified"
23#define WATCHED_KEY                                     @"Watched"
24#define FAVORITE_KEY                                    @"Favorite"
25#define RESUME_KEY                                      @"Resume Time"
26#define SIZE_KEY                                                @"Size"
27#define DURATION_KEY                                    @"Duration"
28#define AUDIO_DESC_KEY                          @"Audio Description"
29#define SAMPLE_RATE_KEY                         @"Sample Rate"
30#define VIDEO_DESC_KEY                          @"Video Description"
31
32//TV Show Specific Keys
33#define EPISODE_NUMBER_KEY                      @"Episode"
34#define EPISODE_TITLE_KEY                               @"Title"
35#define SEASON_NUMBER_KEY                       @"Season"
36#define SHOW_NAME_KEY                           @"Show Name"
37#define SHOW_DESCRIPTION_KEY            @"Show Description"
38#define SHOW_AIR_DATE                           @"Air Date"
39
40//ATV Extra Info
41/*
42#define SHOW_BROADCASTER_KEY            @"Broadcast Company"
43#define SHOW_PUBLISHED_DATE_KEY @"Published Date"
44#define SHOW_AQUIRED_DATE                       @"Date Aquired"
45#define SHOW_RATING_KEY                         @"Rating"
46#define SHOW_FAVORITE_RATING            @"User Rating"
47*/
48//IMDB Type Info
49
50
51@implementation NSString (episodeSorting)
52
53- (NSComparisonResult) directoryNameCompare:(NSString *)other
54{
55        return [self compare:other options:NSCaseInsensitiveSearch | NSNumericSearch];
56}
57
58@end
59
60@implementation SapphireMetaData
61
62// Static set of file extensions to filter
63static NSSet *extensions = nil;
64
65+(void)load
66{
67        extensions = [[NSSet alloc] initWithObjects:@"avi", @"mov", @"mpg", @"mpeg", @"wmv",@"mkv", @"flv", @"divx", @"mp4", nil];
68}
69
70- (id)initWithDictionary:(NSDictionary *)dict parent:(SapphireMetaData *)myParent path:(NSString *)myPath
71{
72        self = [super init];
73        if(!self)
74                return nil;
75       
76        if(dict == nil)
77                metaData = [NSMutableDictionary new];
78        else
79                metaData = [dict mutableCopy];
80        path = [myPath retain];
81        parent = myParent;
82       
83        return self;
84}
85
86- (void)dealloc
87{
88        [metaData release];
89        [path release];
90        [super dealloc];
91}
92
93- (NSMutableDictionary *)dict
94{
95        return metaData;
96}
97
98- (NSString *)path
99{
100        return path;
101}
102
103- (void)setDelegate:(id <SapphireMetaDataDelegate>)newDelegate
104{
105        delegate = newDelegate;
106}
107
108- (void)writeMetaData
109{
110        [parent writeMetaData];
111}
112
113- (BOOL)isDirectory:(NSString *)fullPath
114{
115        BOOL isDir = NO;
116        return [[NSFileManager defaultManager] fileExistsAtPath:fullPath isDirectory:&isDir] && isDir;
117}
118
119- (NSMutableDictionary *)getDisplayedMetaData
120{
121        return nil;
122}
123
124@end
125
126@implementation SapphireMetaDataCollection
127
128- (id)initWithFile:(NSString *)dictionary path:(NSString *)myPath
129{
130        self = [super init];
131        if(!self)
132                return nil;
133       
134        dictionaryPath = [dictionary retain];
135        NSDictionary *metaDict = [NSDictionary dictionaryWithContentsOfFile:dictionary];
136        mainDirectory = [[SapphireDirectoryMetaData alloc] initWithDictionary:metaDict parent:self path:myPath];
137        metaData = [[mainDirectory dict] retain];
138        [metaData setObject:[NSNumber numberWithInt:META_VERSION] forKey:META_VERSION_KEY];
139       
140        return self;
141}
142
143- (void)dealloc
144{
145        [mainDirectory release];
146        [dictionaryPath release];
147        [super dealloc];
148}
149
150- (SapphireDirectoryMetaData *)rootDirectory
151{
152        return mainDirectory;
153}
154
155static void makeParentDir(NSFileManager *manager, NSString *dir)
156{
157        NSString *parent = [dir stringByDeletingLastPathComponent];
158       
159        BOOL isDir;
160        if(![manager fileExistsAtPath:parent isDirectory:&isDir])
161                makeParentDir(manager, parent);
162        else if(!isDir)
163                //Can't work with this
164                return;
165       
166        [manager createDirectoryAtPath:dir attributes:nil];
167}
168
169- (void)writeMetaData
170{
171        makeParentDir([NSFileManager defaultManager], [dictionaryPath stringByDeletingLastPathComponent]);
172        [[mainDirectory dict] writeToFile:dictionaryPath atomically:YES];
173}
174
175@end
176
177@interface SapphireDirectoryMetaData (private)
178- (void)reloadDirectoryContents;
179@end
180
181@implementation SapphireDirectoryMetaData
182
183- (id)initWithDictionary:(NSDictionary *)dict parent:(SapphireMetaData *)myParent path:(NSString *)myPath
184{
185        self = [super initWithDictionary:dict parent:myParent path:myPath];
186        if(!self)
187                return nil;
188       
189        metaFiles = [metaData objectForKey:FILES_KEY];
190        if(metaFiles == nil)
191                metaFiles = [NSMutableDictionary new];
192        else
193                metaFiles = [metaFiles mutableCopy];
194        [metaData setObject:metaFiles forKey:FILES_KEY];
195        [metaFiles release];
196
197        metaDirs = [metaData objectForKey:DIRS_KEY];
198        if(metaDirs == nil)
199                metaDirs = [NSMutableDictionary new];
200        else
201                metaDirs = [metaDirs mutableCopy];
202        [metaData setObject:metaDirs forKey:DIRS_KEY];
203        [metaDirs release];
204       
205        cachedMetaDirs = [NSMutableDictionary new];
206        cachedMetaFiles = [NSMutableDictionary new];
207       
208        return self;
209}
210
211- (void)dealloc
212{
213        [importTimer invalidate];
214        [importArray release];
215        [cachedMetaDirs release];
216        [cachedMetaFiles release];
217        [files release];
218        [directories release];
219        [super dealloc];
220}
221
222- (void)reloadDirectoryContents
223{
224        [files release];
225        [directories release];
226        files = [NSMutableArray new];
227        directories = [NSMutableArray new];
228        NSMutableArray *fileMetas = [NSMutableArray array];
229       
230        NSArray *names = [[NSFileManager defaultManager] directoryContentsAtPath:path];
231       
232        NSEnumerator *nameEnum = [names objectEnumerator];
233        NSString *name = nil;
234        // Display Menu Items
235        while((name = [nameEnum nextObject]) != nil)
236        {
237                if([name hasPrefix:@"."])
238                        continue;
239                if([name isEqualToString:@"Cover Art"])
240                        continue;
241                //Only accept if it is a directory or right extension
242                NSString *extension = [name pathExtension];
243                if([self isDirectory:[path stringByAppendingPathComponent:name]])
244                        [directories addObject:name];
245                else if([extensions containsObject:extension])
246                        [fileMetas addObject:[self metaDataForFile:name]];
247        }
248        [directories sortUsingSelector:@selector(directoryNameCompare:)];
249        [fileMetas sortUsingSelector:@selector(episodeCompare:)];
250        nameEnum = [fileMetas objectEnumerator];
251        SapphireFileMetaData *fileMeta = nil;
252        while((fileMeta = [nameEnum nextObject]) != nil)
253                [files addObject:[[fileMeta path] lastPathComponent]];
254        [self updateMetaData];
255        if([importArray count])
256                [self writeMetaData];
257        scannedDirectory = YES;
258}
259
260- (NSArray *)files
261{
262        return files;
263}
264
265- (NSArray *)directories
266{
267        return directories;
268}
269
270- (BOOL)hasPredicatedFiles:(SapphirePredicate *)predicate
271{
272        NSArray *filesToScan = files;
273        if(!scannedDirectory)
274                filesToScan = [metaFiles allKeys];
275        NSEnumerator *fileEnum = [filesToScan objectEnumerator];
276        NSString *file = nil;
277        while((file = [fileEnum nextObject]) != nil)
278        {
279                BOOL include = NO;
280                if([metaFiles objectForKey:file] != nil)
281                {
282                        SapphireFileMetaData *meta = [self metaDataForFile:file];
283                        include = [predicate accept:[meta path] meta:meta];
284                }
285                else
286                        include = [predicate accept:[path stringByAppendingPathComponent:file] meta:nil];
287                if(include)
288                        return YES;
289        }
290        return NO;
291}
292
293- (BOOL)hasXMLMetaData:(NSString *)file
294{
295//      NSString *extension = [file pathExtension] ;
296
297        return FALSE ;
298}
299
300- (BOOL)hasPredicatedDirectories:(SapphirePredicate *)predicate
301{
302        NSArray *directoriesToScan = directories;
303        if(!scannedDirectory)
304                directoriesToScan = [metaDirs allKeys];
305        NSEnumerator *directoryEnum = [directoriesToScan objectEnumerator];
306        NSString *directory = nil;
307        while((directory = [directoryEnum nextObject]) != nil)
308        {
309                SapphireDirectoryMetaData *meta = [self metaDataForDirectory:directory];
310                if(![[SapphireSettings sharedSettings] fastSwitching])
311                        [meta reloadDirectoryContents];
312               
313                if([meta hasPredicatedFiles:predicate] || [meta hasPredicatedDirectories:predicate])
314                        return YES;
315        }
316        return NO;
317}
318
319- (NSArray *)predicatedFiles:(SapphirePredicate *)predicate
320{
321        NSMutableArray *ret = [NSMutableArray array];
322        NSArray *filesToScan = files;
323        if(!scannedDirectory)
324                filesToScan = [metaFiles allKeys];
325        NSEnumerator *fileEnum = [filesToScan objectEnumerator];
326        NSString *file = nil;
327        while((file = [fileEnum nextObject]) != nil)
328        {
329                BOOL include = NO;
330                if([metaFiles objectForKey:file] != nil)
331                {
332                        SapphireFileMetaData *meta = [self metaDataForFile:file];
333                        include = [predicate accept:[meta path] meta:meta];
334                }
335                else
336                        include = [predicate accept:[path stringByAppendingPathComponent:file] meta:nil];
337                if(include)
338                        [ret addObject:file];
339        }
340        return ret;
341}
342- (NSArray *)predicatedDirectories:(SapphirePredicate *)predicate
343{
344        NSMutableArray *ret = [NSMutableArray array];
345        NSArray *directoriesToScan = directories;
346        if(!scannedDirectory)
347                directoriesToScan = [metaDirs allKeys];
348        NSEnumerator *directoryEnum = [directoriesToScan objectEnumerator];
349        NSString *directory = nil;
350        while((directory = [directoryEnum nextObject]) != nil)
351        {
352                SapphireDirectoryMetaData *meta = [self metaDataForDirectory:directory];
353                if(![[SapphireSettings sharedSettings] fastSwitching])
354                        [meta reloadDirectoryContents];
355
356                if([meta hasPredicatedFiles:predicate] || [meta hasPredicatedDirectories:predicate])
357                        [ret addObject:directory];
358        }
359        return ret;
360}
361
362- (SapphireFileMetaData *)metaDataForFile:(NSString *)file
363{
364        SapphireFileMetaData *ret = [cachedMetaFiles objectForKey:file];
365        if(ret == nil)
366        {
367                ret = [[SapphireFileMetaData alloc] initWithDictionary:[metaFiles objectForKey:file] parent:self path:[path stringByAppendingPathComponent:file]];
368                [metaFiles setObject:[ret dict] forKey:file];
369                [cachedMetaFiles setObject:ret forKey:file];
370                [ret autorelease];
371        }
372        return ret;
373}
374
375- (SapphireDirectoryMetaData *)metaDataForDirectory:(NSString *)file
376{
377        SapphireDirectoryMetaData *ret = [cachedMetaDirs objectForKey:file];
378        if(ret == nil)
379        {
380                ret = [[SapphireDirectoryMetaData alloc] initWithDictionary:[metaDirs objectForKey:file] parent:self path:[path stringByAppendingPathComponent:file]];
381                [metaDirs setObject:[ret dict] forKey:file];
382                [cachedMetaDirs setObject:ret forKey:file];
383                [ret autorelease];             
384        }
385        return ret;
386}
387
388- (BOOL)pruneMetaData
389{
390        BOOL ret = NO;
391        NSSet *existingSet = [NSSet setWithArray:files];
392        NSArray *metaArray = [metaFiles allKeys];
393        NSMutableSet *pruneSet = [NSMutableSet setWithArray:metaArray];
394       
395        [pruneSet minusSet:existingSet];
396        if([pruneSet anyObject] != nil)
397        {
398                NSEnumerator *pruneEnum = [pruneSet objectEnumerator];
399                NSString *pruneKey = nil;
400                while((pruneKey = [pruneEnum nextObject]) != nil)
401                        [metaFiles removeObjectForKey:pruneKey];
402                ret = YES;             
403        }
404       
405        existingSet = [NSSet setWithArray:directories];
406        metaArray = [metaDirs allKeys];
407        pruneSet = [NSMutableSet setWithArray:metaArray];
408       
409        [pruneSet minusSet:existingSet];
410        if([pruneSet anyObject] != nil)
411        {
412                NSEnumerator *pruneEnum = [pruneSet objectEnumerator];
413                NSString *pruneKey = nil;
414                while((pruneKey = [pruneEnum nextObject]) != nil)
415                        [metaDirs removeObjectForKey:pruneKey];
416                ret = YES;
417        }
418       
419        return ret;
420}
421
422- (BOOL)updateMetaData
423{
424        NSEnumerator *fileEnum = [files objectEnumerator];
425        NSString *fileName = nil;
426        importArray = [[NSMutableArray alloc] init];
427        while((fileName = [fileEnum nextObject]) != nil)
428        {
429                NSDictionary *fileMeta = [metaFiles objectForKey:fileName];
430                if(fileMeta == nil)
431                {
432                        [self metaDataForFile:fileName];
433                        [importArray addObject:fileName];
434                }
435                else
436                {
437                        NSString *filePath = [path stringByAppendingPathComponent:fileName];
438                        struct stat sb;
439                        memset(&sb, 0, sizeof(struct stat));
440                        stat([filePath fileSystemRepresentation], &sb);
441                        long modTime = sb.st_mtimespec.tv_sec;
442                        if([[fileMeta objectForKey:MODIFIED_KEY] intValue] != modTime || [[fileMeta objectForKey:META_VERSION_KEY] intValue] != META_VERSION)
443                                [importArray addObject:fileName];
444                }
445        }
446        return NO;
447}
448
449- (void)processFiles:(NSTimer *)timer
450{
451        NSString *file = [importArray objectAtIndex:0];
452       
453        [[self metaDataForFile:file] updateMetaData];
454       
455        [self writeMetaData];
456        [delegate updateCompleteForFile:file];
457       
458        [importArray removeObjectAtIndex:0];
459        [self resumeImport];
460}
461
462- (void)cancelImport
463{
464        [importTimer invalidate];
465        importTimer = nil;
466}
467
468- (void)resumeImport
469{
470        [importTimer invalidate];
471        if([importArray count])
472                importTimer = [NSTimer scheduledTimerWithTimeInterval:1.1 target:self selector:@selector(processFiles:) userInfo:nil repeats:NO];
473        else
474        {
475                importTimer = nil;
476                [importArray release];
477                importArray = nil;
478        }
479}
480
481- (void)resumeDelayedImport
482{
483        [importTimer invalidate];
484        if([importArray count])
485                importTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(resumeImport) userInfo:nil repeats:NO];
486        else
487                importTimer = nil;
488}
489
490- (SapphireMetaData *)metaDataForSubPath:(NSString *)subPath
491{
492        NSArray *components = [subPath pathComponents];
493        if(![components count])
494                return self;
495        NSString *file = [components objectAtIndex:0];
496       
497        if([self isDirectory:[path stringByAppendingPathComponent:file]])
498        {
499                NSMutableArray *newComp = [components mutableCopy];
500                [newComp removeObjectAtIndex:0];
501                [newComp autorelease];
502                SapphireDirectoryMetaData *nextLevel = [self metaDataForDirectory:file];
503                return [nextLevel metaDataForSubPath:[NSString pathWithComponents:newComp]];
504        }
505        else if([components count] > 1)
506                return nil;
507        return [self metaDataForFile:file];
508}
509
510- (NSArray *)subFileMetas
511{
512        NSMutableArray *ret = [[NSMutableArray alloc] init];
513        [self reloadDirectoryContents];
514        NSEnumerator *dirEnum = [directories objectEnumerator];
515        NSString *dir = nil;
516        while((dir = [dirEnum nextObject]) != nil)
517        {
518                SapphireDirectoryMetaData *dirMeta = [self metaDataForDirectory:dir];
519                if(dirMeta != nil)
520                        [ret addObjectsFromArray:[dirMeta subFileMetas]];
521        }
522        NSEnumerator *fileEnum = [files objectEnumerator];
523        NSString *file = nil;
524        while((file = [fileEnum nextObject]) != nil)
525        {
526                SapphireFileMetaData *fileMeta = [self metaDataForFile:file];
527                if(fileMeta != nil)
528                        [ret addObject:fileMeta];
529        }
530        return [ret autorelease];
531}
532
533- (void)scanForNewFiles
534{
535        [self reloadDirectoryContents];
536        NSEnumerator *dirEnum = [directories objectEnumerator];
537        NSString *dir = nil;
538        while((dir = [dirEnum nextObject]) != nil)
539        {
540                SapphireDirectoryMetaData *dirMeta = [self metaDataForDirectory:dir];
541                if(dirMeta != nil)
542                        [dirMeta scanForNewFiles];
543        }
544        NSEnumerator *fileEnum = [files objectEnumerator];
545        NSString *file = nil;
546        while((file = [fileEnum nextObject]) != nil)
547                [self metaDataForFile:file];
548}
549
550- (void)setupFiles:(NSArray * *)filesToScan andDirectories:(NSArray * *)directoriesToScan arraysForPredicate:(SapphirePredicate *)predicate
551{
552        if(predicate)
553        {
554                *filesToScan = [self predicatedFiles:predicate];
555                *directoriesToScan = [self predicatedDirectories:predicate];
556        }
557        else if(!scannedDirectory)
558        {
559                //Likely haven't scanned the directory yet, so use cached
560                *filesToScan = [metaFiles allKeys];
561                *directoriesToScan = [metaDirs allKeys];
562        }
563}
564
565- (BOOL)checkResult:(BOOL)result recursivelyOnFiles:(NSInvocation *)fileInv forPredicate:(SapphirePredicate *)predicate
566{
567        NSArray *filesToScan = files;
568        NSArray *directoriesToScan = directories;
569        [self setupFiles:&filesToScan andDirectories:&directoriesToScan arraysForPredicate:predicate];
570        NSEnumerator *fileEnum = [filesToScan objectEnumerator];
571        NSString *file = nil;
572        while((file = [fileEnum nextObject]) != nil)
573        {
574                [fileInv invokeWithTarget:[self metaDataForFile:file]];
575                BOOL result = NO;
576                [fileInv getReturnValue:&result];
577                if(result == result)
578                        return result;
579        }
580
581        NSEnumerator *dirEnum = [directoriesToScan objectEnumerator];
582        NSString *dir = nil;
583        while((dir = [dirEnum nextObject]) != nil)
584                if([[self metaDataForDirectory:dir] checkResult:result recursivelyOnFiles:fileInv forPredicate:predicate] == result)
585                        return result;
586       
587        return !result;
588}
589
590- (void)invokeRecursivelyOnFiles:(NSInvocation *)fileInv withPredicate:(SapphirePredicate *)predicate
591{
592        [self reloadDirectoryContents];
593        NSEnumerator *dirEnum = [directories objectEnumerator];
594        NSString *dir = nil;
595        while((dir = [dirEnum nextObject]) != nil)
596                [[self metaDataForDirectory:dir] invokeRecursivelyOnFiles:fileInv withPredicate:predicate];
597       
598        NSEnumerator *fileEnum = [files objectEnumerator];
599        NSString *file = nil;
600        while((file = [fileEnum nextObject]) != nil)
601        {
602                SapphireFileMetaData *fileMeta = [self metaDataForFile:file];
603                if(!predicate || [predicate accept:[fileMeta path] meta:fileMeta])
604                        [fileInv invokeWithTarget:fileMeta];
605        }
606}
607
608- (BOOL)watchedForPredicate:(SapphirePredicate *)predicate
609{
610        SEL select = @selector(watched);
611        NSInvocation *fileInv = [NSInvocation invocationWithMethodSignature:[[SapphireFileMetaData class] instanceMethodSignatureForSelector:select]];
612        [fileInv setSelector:select];
613        return [self checkResult:NO recursivelyOnFiles:fileInv forPredicate:predicate];
614}
615
616- (void)setWatched:(BOOL)watched predicate:(SapphirePredicate *)predicate
617{
618        SEL select = @selector(setWatched:);
619        NSInvocation *fileInv = [NSInvocation invocationWithMethodSignature:[[SapphireFileMetaData class] instanceMethodSignatureForSelector:select]];
620        [fileInv setSelector:select];
621        [fileInv setArgument:&watched atIndex:2];
622        [self invokeRecursivelyOnFiles:fileInv withPredicate:predicate];
623}
624
625- (BOOL)favoriteForPredicate:(SapphirePredicate *)predicate
626{
627        SEL select = @selector(favorite);
628        NSInvocation *fileInv = [NSInvocation invocationWithMethodSignature:[[SapphireFileMetaData class] instanceMethodSignatureForSelector:select]];
629        [fileInv setSelector:select];
630        return [self checkResult:YES recursivelyOnFiles:fileInv forPredicate:predicate];       
631}
632
633- (void)setFavorite:(BOOL)favorite predicate:(SapphirePredicate *)predicate
634{
635        SEL select = @selector(setFavorite:);
636        NSInvocation *fileInv = [NSInvocation invocationWithMethodSignature:[[SapphireFileMetaData class] instanceMethodSignatureForSelector:select]];
637        [fileInv setSelector:select];
638        [fileInv setArgument:&favorite atIndex:2];
639        [self invokeRecursivelyOnFiles:fileInv withPredicate:predicate];
640}
641
642- (void)setToImportFromTVForPredicate:(SapphirePredicate *)predicate
643{
644        SEL select = @selector(setToImportFromTV);
645        NSInvocation *fileInv = [NSInvocation invocationWithMethodSignature:[[SapphireFileMetaData class] instanceMethodSignatureForSelector:select]];
646        [fileInv setSelector:select];
647        [self invokeRecursivelyOnFiles:fileInv withPredicate:predicate];
648}
649
650- (NSMutableDictionary *)getDisplayedMetaData
651{
652        return [NSMutableDictionary dictionaryWithObjectsAndKeys:
653                [path lastPathComponent], META_TITLE_KEY,
654                nil];
655}
656
657@end
658
659@implementation SapphireFileMetaData : SapphireMetaData
660
661static NSDictionary *metaDataSubstitutions = nil;
662static NSSet *displayedMetaData = nil;
663
664+ (void) initialize
665{
666        metaDataSubstitutions = [[NSDictionary alloc] initWithObjectsAndKeys:
667                @"Video", VIDEO_DESC_KEY,
668                @"Audio", AUDIO_DESC_KEY,
669                nil];
670        displayedMetaData = [[NSSet alloc] initWithObjects:
671                VIDEO_DESC_KEY,
672                AUDIO_DESC_KEY,
673                META_RATING_KEY,
674                META_SUMMARY_KEY,
675                META_COPYRIGHT_KEY,
676                META_TITLE_KEY,
677                nil];
678               
679}
680
681- (BOOL) updateMetaData
682{
683        NSDictionary *props = [[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:YES];
684        int modTime = [[props objectForKey:NSFileModificationDate] timeIntervalSince1970];
685        BOOL updated =FALSE;
686       
687        if(props == nil)
688                //No file
689                return FALSE;
690       
691        if(modTime != [self modified] || [[metaData objectForKey:META_VERSION_KEY] intValue] != META_VERSION)
692        {
693                updated=TRUE ;
694                NSMutableDictionary *fileMeta = [NSMutableDictionary dictionary];
695               
696                [fileMeta setObject:[NSNumber numberWithInt:modTime] forKey:MODIFIED_KEY];
697                [fileMeta setObject:[props objectForKey:NSFileSize] forKey:SIZE_KEY];
698                [fileMeta setObject:[NSNumber numberWithInt:META_VERSION] forKey:META_VERSION_KEY];
699               
700                NSError *error = nil;
701                QTMovie *movie = [QTMovie movieWithFile:path error:&error];
702                QTTime duration = [movie duration];
703                [fileMeta setObject:[NSNumber numberWithFloat:(float)duration.timeValue/(float)duration.timeScale] forKey:DURATION_KEY];
704                NSArray *audioTracks = [movie tracksOfMediaType:@"soun"];
705                NSNumber *audioSampleRate = nil;
706                if([audioTracks count])
707                {
708                        QTTrack *track = [audioTracks objectAtIndex:0];
709                        QTMedia *media = [track media];
710                        audioSampleRate = [media attributeForKey:QTMediaTimeScaleAttribute];
711                        if(media != nil)
712                        {
713                                Media qtMedia = [media quickTimeMedia];
714                                Handle sampleDesc = NewHandle(1);
715                                GetMediaSampleDescription(qtMedia, 1, (SampleDescriptionHandle)sampleDesc);
716                                CFStringRef userText = nil;
717                                ByteCount       propSize = 0;
718                                QTSoundDescriptionGetProperty((SoundDescriptionHandle)sampleDesc, kQTPropertyClass_SoundDescription, kQTSoundDescriptionPropertyID_UserReadableText, sizeof(userText), &userText, &propSize);
719                                DisposeHandle(sampleDesc);
720                               
721                                if(userText != nil)
722                                {
723                                        [fileMeta setObject:(NSString *)userText forKey:AUDIO_DESC_KEY];
724                                        CFRelease(userText);
725                                }
726                        }
727                }
728                if(audioSampleRate != nil)
729                        [fileMeta setObject:audioSampleRate forKey:SAMPLE_RATE_KEY];
730                NSArray *videoTracks = [movie tracksOfMediaType:@"vide"];
731                if([videoTracks count])
732                {
733                        QTTrack *track = [videoTracks objectAtIndex:0];
734                        QTMedia *media = [track media];
735                        if(media != nil)
736                        {
737                                Media qtMedia = [media quickTimeMedia];
738                                Handle sampleDesc = NewHandle(1);
739                                GetMediaSampleDescription(qtMedia, 1, (SampleDescriptionHandle)sampleDesc);
740                                CFStringRef userText = nil;
741                                ByteCount       propSize = 0;
742                                ICMImageDescriptionGetProperty((ImageDescriptionHandle)sampleDesc, kQTPropertyClass_ImageDescription, kICMImageDescriptionPropertyID_SummaryString, sizeof(userText), &userText, &propSize);
743                                DisposeHandle(sampleDesc);
744                               
745                                if(userText != nil)
746                                {
747                                        [fileMeta setObject:(NSString *)userText forKey:VIDEO_DESC_KEY];
748                                        CFRelease(userText);
749                                }
750                        }
751                }
752                [metaData addEntriesFromDictionary:fileMeta];
753        }
754        return updated ;
755}
756
757- (void)importInfo:(NSDictionary *)newMeta
758{
759        [metaData addEntriesFromDictionary:newMeta];
760}
761
762- (int)modified
763{
764        return [[metaData objectForKey:MODIFIED_KEY] intValue];
765}
766
767- (BOOL)watched
768{
769        return [[metaData objectForKey:WATCHED_KEY] boolValue];
770}
771
772- (void)setWatched:(BOOL)watched
773{
774        [metaData setObject:[NSNumber numberWithBool:watched] forKey:WATCHED_KEY];
775}
776
777- (BOOL)favorite
778{
779        return [[metaData objectForKey:FAVORITE_KEY] boolValue];
780}
781
782- (void)setFavorite:(BOOL)favorite
783{
784        [metaData setObject:[NSNumber numberWithBool:favorite] forKey:FAVORITE_KEY];
785}
786
787- (BOOL)importedFromTV
788{
789        return [[metaData objectForKey:TVRAGE_IMPORT_KEY] boolValue];
790}
791
792- (void)setToImportFromTV
793{
794        [metaData removeObjectForKey:TVRAGE_IMPORT_KEY];
795}
796
797- (unsigned int)resumeTime
798{
799        return [[metaData objectForKey:RESUME_KEY] unsignedIntValue];
800}
801
802- (void)setResumeTime:(unsigned int)resumeTime
803{
804        [metaData setObject:[NSNumber numberWithUnsignedInt:resumeTime] forKey:RESUME_KEY];
805}
806
807- (long long)size
808{
809        return [[metaData objectForKey:SIZE_KEY] longLongValue];
810}
811
812- (float)duration
813{
814        return [[metaData objectForKey:DURATION_KEY] floatValue];
815}
816
817- (int)sampleRate
818{
819        return [[metaData objectForKey:SAMPLE_RATE_KEY] intValue];
820}
821
822- (int)episodeNumber
823{
824        return [[metaData objectForKey:EPISODE_NUMBER_KEY] intValue] ;
825}
826
827- (int)seasonNumber
828{
829        return [[metaData objectForKey:SEASON_NUMBER_KEY] intValue];
830}
831
832- (NSString *)episodeTitle
833{
834        NSString * title = [metaData objectForKey:EPISODE_TITLE_KEY] ;
835        if(title!=nil)return title ;
836        else
837        return nil ;
838}
839
840- (NSString *)sizeString
841{
842        float size = [self size];
843        if(size == 0)
844                return @"-";
845        char letter = ' ';
846        if(size >= 1024000)
847        {
848                if(size >= 1024*1024000)
849                {
850                        size /= 1024 * 1024 * 1024;
851                        letter = 'G';
852                }
853                else
854                {
855                        size /= 1024 * 1024;
856                        letter = 'M';
857                }
858        }
859        else if (size >= 1000)
860        {
861                size /= 1024;
862                letter = 'K';
863        }
864        return [NSString stringWithFormat:@"%.1f%cB", size, letter];   
865}
866
867- (NSMutableDictionary *)getDisplayedMetaData
868{
869        NSString *name = [path lastPathComponent];
870        int duration = [self duration];
871        int secs = duration % 60;
872        int mins = (duration /60) % 60;
873        int hours = duration / 3600;
874        NSString *durationStr = nil;
875        if(hours != 0)
876                durationStr = [NSString stringWithFormat:@"%d:%02d:%02d", hours, mins, secs];
877        else if (mins != 0)
878                durationStr = [NSString stringWithFormat:@"%d:%02d", mins, secs];
879        else
880                durationStr = [NSString stringWithFormat:@"%ds", secs];
881        NSMutableDictionary *ret = [metaData mutableCopy];
882        //Pretty this up now
883        NSMutableSet *currentKeys = [NSMutableSet setWithArray:[ret allKeys]];
884        [currentKeys minusSet:displayedMetaData];
885        [ret removeObjectsForKeys:[currentKeys allObjects]];
886       
887        NSEnumerator *subEnum = [metaDataSubstitutions keyEnumerator];
888        NSString *key = nil;
889        while((key = [subEnum nextObject]) != nil)
890        {
891                NSString *value = [ret objectForKey:key];
892                if(value != nil)
893                {
894                        [ret setObject:value forKey:[metaDataSubstitutions objectForKey:key]];
895                        [ret removeObjectForKey:key];
896                }
897        }
898        if([self duration])
899                [ret setObject:durationStr forKey:DURATION_KEY];
900        if([ret objectForKey:META_TITLE_KEY] == nil)
901                [ret setObject:name forKey:META_TITLE_KEY];\
902        if([self size])
903                [ret setObject:[self sizeString] forKey:SIZE_KEY];
904        return ret;
905}
906
907// Custom TV Episode handler
908- (NSComparisonResult) episodeCompare:(SapphireFileMetaData *)other
909{
910        int myNum = [self seasonNumber];
911        int theirNum = [other seasonNumber];
912        if(myNum == 0)
913                myNum = INT_MAX;
914        if(theirNum == 0)
915                theirNum = INT_MAX;
916        if(myNum > theirNum)
917                return NSOrderedDescending;
918        if(theirNum > myNum)
919                return NSOrderedAscending;
920       
921        myNum = [self episodeNumber];
922        theirNum = [other episodeNumber];
923        if(myNum == 0)
924                myNum = INT_MAX;
925        if(theirNum == 0)
926                theirNum = INT_MAX;
927        if(myNum > theirNum)
928                return NSOrderedDescending;
929        if(theirNum > myNum)
930                return NSOrderedAscending;
931        return [[path lastPathComponent] compare:[[other path] lastPathComponent] options:NSCaseInsensitiveSearch | NSNumericSearch];
932}
933
934@end
Note: See TracBrowser for help on using the repository browser.