source: trunk/SapphireMetaData.m @ 118

Revision 118, 24.9 KB checked in by gbooker, 7 years ago (diff)

Sort shows by season and episode first

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