source: trunk/SapphireMetaData.m @ 106

Last change on this file since 106 was 106, checked in by gbooker, 10 years ago

These two sets of function were identical except for the selector they used on the file's meta data. Combined into less code.

File size: 23.8 KB
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// Custom TV Episode handler
53- (NSComparisonResult) episodeCompare:(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       
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                        [files addObject:name];
246        }
247        [directories sortUsingSelector:@selector(episodeCompare:)];
248        [files sortUsingSelector:@selector(episodeCompare:)];
249        [self updateMetaData];
250        if([importArray count])
251                [self writeMetaData];
252        scannedDirectory = YES;
253}
254
255- (NSArray *)files
256{
257        return files;
258}
259
260- (NSArray *)directories
261{
262        return directories;
263}
264
265- (BOOL)hasPredicatedFiles:(SapphirePredicate *)predicate
266{
267        NSArray *filesToScan = files;
268        if(!scannedDirectory)
269                filesToScan = [metaFiles allKeys];
270        NSEnumerator *fileEnum = [filesToScan objectEnumerator];
271        NSString *file = nil;
272        while((file = [fileEnum nextObject]) != nil)
273        {
274                BOOL include = NO;
275                if([metaFiles objectForKey:file] != nil)
276                {
277                        SapphireFileMetaData *meta = [self metaDataForFile:file];
278                        include = [predicate accept:[meta path] meta:meta];
279                }
280                else
281                        include = [predicate accept:[path stringByAppendingPathComponent:file] meta:nil];
282                if(include)
283                        return YES;
284        }
285        return NO;
286}
287
288- (BOOL)hasXMLMetaData:(NSString *)file
289{
290//      NSString *extension = [file pathExtension] ;
291
292        return FALSE ;
293}
294
295- (BOOL)hasPredicatedDirectories:(SapphirePredicate *)predicate
296{
297        NSArray *directoriesToScan = directories;
298        if(!scannedDirectory)
299                directoriesToScan = [metaDirs allKeys];
300        NSEnumerator *directoryEnum = [directoriesToScan objectEnumerator];
301        NSString *directory = nil;
302        while((directory = [directoryEnum nextObject]) != nil)
303        {
304                SapphireDirectoryMetaData *meta = [self metaDataForDirectory:directory];
305                if(![[SapphireSettings sharedSettings] fastSwitching])
306                        [meta reloadDirectoryContents];
307               
308                if([meta hasPredicatedFiles:predicate] || [meta hasPredicatedDirectories:predicate])
309                        return YES;
310        }
311        return NO;
312}
313
314- (NSArray *)predicatedFiles:(SapphirePredicate *)predicate
315{
316        NSMutableArray *ret = [NSMutableArray array];
317        NSArray *filesToScan = files;
318        if(!scannedDirectory)
319                filesToScan = [metaFiles allKeys];
320        NSEnumerator *fileEnum = [filesToScan objectEnumerator];
321        NSString *file = nil;
322        while((file = [fileEnum nextObject]) != nil)
323        {
324                BOOL include = NO;
325                if([metaFiles objectForKey:file] != nil)
326                {
327                        SapphireFileMetaData *meta = [self metaDataForFile:file];
328                        include = [predicate accept:[meta path] meta:meta];
329                }
330                else
331                        include = [predicate accept:[path stringByAppendingPathComponent:file] meta:nil];
332                if(include)
333                        [ret addObject:file];
334        }
335        return ret;
336}
337- (NSArray *)predicatedDirectories:(SapphirePredicate *)predicate
338{
339        NSMutableArray *ret = [NSMutableArray array];
340        NSArray *directoriesToScan = directories;
341        if(!scannedDirectory)
342                directoriesToScan = [metaDirs allKeys];
343        NSEnumerator *directoryEnum = [directoriesToScan objectEnumerator];
344        NSString *directory = nil;
345        while((directory = [directoryEnum nextObject]) != nil)
346        {
347                SapphireDirectoryMetaData *meta = [self metaDataForDirectory:directory];
348                if(![[SapphireSettings sharedSettings] fastSwitching])
349                        [meta reloadDirectoryContents];
350
351                if([meta hasPredicatedFiles:predicate] || [meta hasPredicatedDirectories:predicate])
352                        [ret addObject:directory];
353        }
354        return ret;
355}
356
357- (SapphireFileMetaData *)metaDataForFile:(NSString *)file
358{
359        SapphireFileMetaData *ret = [cachedMetaFiles objectForKey:file];
360        if(ret == nil)
361        {
362                ret = [[SapphireFileMetaData alloc] initWithDictionary:[metaFiles objectForKey:file] parent:self path:[path stringByAppendingPathComponent:file]];
363                [metaFiles setObject:[ret dict] forKey:file];
364                [cachedMetaFiles setObject:ret forKey:file];
365                [ret autorelease];
366        }
367        return ret;
368}
369
370- (SapphireDirectoryMetaData *)metaDataForDirectory:(NSString *)file
371{
372        SapphireDirectoryMetaData *ret = [cachedMetaDirs objectForKey:file];
373        if(ret == nil)
374        {
375                ret = [[SapphireDirectoryMetaData alloc] initWithDictionary:[metaDirs objectForKey:file] parent:self path:[path stringByAppendingPathComponent:file]];
376                [metaDirs setObject:[ret dict] forKey:file];
377                [cachedMetaDirs setObject:ret forKey:file];
378                [ret autorelease];             
379        }
380        return ret;
381}
382
383- (BOOL)pruneMetaData
384{
385        BOOL ret = NO;
386        NSSet *existingSet = [NSSet setWithArray:files];
387        NSArray *metaArray = [metaFiles allKeys];
388        NSMutableSet *pruneSet = [NSMutableSet setWithArray:metaArray];
389       
390        [pruneSet minusSet:existingSet];
391        if([pruneSet anyObject] != nil)
392        {
393                NSEnumerator *pruneEnum = [pruneSet objectEnumerator];
394                NSString *pruneKey = nil;
395                while((pruneKey = [pruneEnum nextObject]) != nil)
396                        [metaFiles removeObjectForKey:pruneKey];
397                ret = YES;             
398        }
399       
400        existingSet = [NSSet setWithArray:directories];
401        metaArray = [metaDirs allKeys];
402        pruneSet = [NSMutableSet setWithArray:metaArray];
403       
404        [pruneSet minusSet:existingSet];
405        if([pruneSet anyObject] != nil)
406        {
407                NSEnumerator *pruneEnum = [pruneSet objectEnumerator];
408                NSString *pruneKey = nil;
409                while((pruneKey = [pruneEnum nextObject]) != nil)
410                        [metaDirs removeObjectForKey:pruneKey];
411                ret = YES;
412        }
413       
414        return ret;
415}
416
417- (BOOL)updateMetaData
418{
419        NSEnumerator *fileEnum = [files objectEnumerator];
420        NSString *fileName = nil;
421        importArray = [[NSMutableArray alloc] init];
422        while((fileName = [fileEnum nextObject]) != nil)
423        {
424                NSDictionary *fileMeta = [metaFiles objectForKey:fileName];
425                if(fileMeta == nil)
426                {
427                        [self metaDataForFile:fileName];
428                        [importArray addObject:fileName];
429                }
430                else
431                {
432                        NSString *filePath = [path stringByAppendingPathComponent:fileName];
433                        struct stat sb;
434                        memset(&sb, 0, sizeof(struct stat));
435                        stat([filePath fileSystemRepresentation], &sb);
436                        long modTime = sb.st_mtimespec.tv_sec;
437                        if([[fileMeta objectForKey:MODIFIED_KEY] intValue] != modTime || [[fileMeta objectForKey:META_VERSION_KEY] intValue] != META_VERSION)
438                                [importArray addObject:fileName];
439                }
440        }
441        return NO;
442}
443
444- (void)processFiles:(NSTimer *)timer
445{
446        NSString *file = [importArray objectAtIndex:0];
447       
448        [[self metaDataForFile:file] updateMetaData];
449       
450        [self writeMetaData];
451        [delegate updateCompleteForFile:file];
452       
453        [importArray removeObjectAtIndex:0];
454        [self resumeImport];
455}
456
457- (void)cancelImport
458{
459        [importTimer invalidate];
460        importTimer = nil;
461}
462
463- (void)resumeImport
464{
465        [importTimer invalidate];
466        if([importArray count])
467                importTimer = [NSTimer scheduledTimerWithTimeInterval:1.1 target:self selector:@selector(processFiles:) userInfo:nil repeats:NO];
468        else
469        {
470                importTimer = nil;
471                [importArray release];
472                importArray = nil;
473        }
474}
475
476- (void)resumeDelayedImport
477{
478        [importTimer invalidate];
479        if([importArray count])
480                importTimer = [NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(resumeImport) userInfo:nil repeats:NO];
481        else
482                importTimer = nil;
483}
484
485- (SapphireMetaData *)metaDataForSubPath:(NSString *)subPath
486{
487        NSArray *components = [subPath pathComponents];
488        if(![components count])
489                return self;
490        NSString *file = [components objectAtIndex:0];
491       
492        if([self isDirectory:[path stringByAppendingPathComponent:file]])
493        {
494                NSMutableArray *newComp = [components mutableCopy];
495                [newComp removeObjectAtIndex:0];
496                [newComp autorelease];
497                SapphireDirectoryMetaData *nextLevel = [self metaDataForDirectory:file];
498                return [nextLevel metaDataForSubPath:[NSString pathWithComponents:newComp]];
499        }
500        else if([components count] > 1)
501                return nil;
502        return [self metaDataForFile:file];
503}
504
505- (NSArray *)subFileMetas
506{
507        NSMutableArray *ret = [[NSMutableArray alloc] init];
508        [self reloadDirectoryContents];
509        NSEnumerator *dirEnum = [directories objectEnumerator];
510        NSString *dir = nil;
511        while((dir = [dirEnum nextObject]) != nil)
512        {
513                SapphireDirectoryMetaData *dirMeta = [self metaDataForDirectory:dir];
514                if(dirMeta != nil)
515                        [ret addObjectsFromArray:[dirMeta subFileMetas]];
516        }
517        NSEnumerator *fileEnum = [files objectEnumerator];
518        NSString *file = nil;
519        while((file = [fileEnum nextObject]) != nil)
520        {
521                SapphireFileMetaData *fileMeta = [self metaDataForFile:file];
522                if(fileMeta != nil)
523                        [ret addObject:fileMeta];
524        }
525        return [ret autorelease];
526}
527
528- (void)scanForNewFiles
529{
530        [self reloadDirectoryContents];
531        NSEnumerator *dirEnum = [directories objectEnumerator];
532        NSString *dir = nil;
533        while((dir = [dirEnum nextObject]) != nil)
534        {
535                SapphireDirectoryMetaData *dirMeta = [self metaDataForDirectory:dir];
536                if(dirMeta != nil)
537                        [dirMeta scanForNewFiles];
538        }
539        NSEnumerator *fileEnum = [files objectEnumerator];
540        NSString *file = nil;
541        while((file = [fileEnum nextObject]) != nil)
542                [self metaDataForFile:file];
543}
544
545- (void)setupFiles:(NSArray * *)filesToScan andDirectories:(NSArray * *)directoriesToScan arraysForPredicate:(SapphirePredicate *)predicate
546{
547        if(predicate)
548        {
549                *filesToScan = [self predicatedFiles:predicate];
550                *directoriesToScan = [self predicatedDirectories:predicate];
551        }
552        else if(!scannedDirectory)
553        {
554                //Likely haven't scanned the directory yet, so use cached
555                *filesToScan = [metaFiles allKeys];
556                *directoriesToScan = [metaDirs allKeys];
557        }
558}
559
560- (BOOL)checkResult:(BOOL)result recursivelyOnFiles:(NSInvocation *)fileInv forPredicate:(SapphirePredicate *)predicate
561{
562        NSArray *filesToScan = files;
563        NSArray *directoriesToScan = directories;
564        [self setupFiles:&filesToScan andDirectories:&directoriesToScan arraysForPredicate:predicate];
565        NSEnumerator *fileEnum = [filesToScan objectEnumerator];
566        NSString *file = nil;
567        while((file = [fileEnum nextObject]) != nil)
568        {
569                [fileInv invokeWithTarget:[self metaDataForFile:file]];
570                BOOL result = NO;
571                [fileInv getReturnValue:&result];
572                if(result == result)
573                        return result;
574        }
575
576        NSEnumerator *dirEnum = [directoriesToScan objectEnumerator];
577        NSString *dir = nil;
578        while((dir = [dirEnum nextObject]) != nil)
579                if([[self metaDataForDirectory:dir] checkResult:result recursivelyOnFiles:fileInv forPredicate:predicate] == result)
580                        return result;
581       
582        return !result;
583}
584
585- (void)invokeRecursivelyOnFiles:(NSInvocation *)fileInv withPredicate:(SapphirePredicate *)predicate
586{
587        [self reloadDirectoryContents];
588        NSEnumerator *dirEnum = [directories objectEnumerator];
589        NSString *dir = nil;
590        while((dir = [dirEnum nextObject]) != nil)
591                [[self metaDataForDirectory:dir] invokeRecursivelyOnFiles:fileInv withPredicate:predicate];
592       
593        NSEnumerator *fileEnum = [files objectEnumerator];
594        NSString *file = nil;
595        while((file = [fileEnum nextObject]) != nil)
596        {
597                SapphireFileMetaData *fileMeta = [self metaDataForFile:file];
598                if(!predicate || [predicate accept:[fileMeta path] meta:fileMeta])
599                        [fileInv invokeWithTarget:fileMeta];
600        }
601}
602
603- (BOOL)watchedForPredicate:(SapphirePredicate *)predicate
604{
605        SEL select = @selector(watched);
606        NSInvocation *fileInv = [NSInvocation invocationWithMethodSignature:[[SapphireFileMetaData class] instanceMethodSignatureForSelector:select]];
607        [fileInv setSelector:select];
608        return [self checkResult:NO recursivelyOnFiles:fileInv forPredicate:predicate];
609}
610
611- (void)setWatched:(BOOL)watched predicate:(SapphirePredicate *)predicate
612{
613        SEL select = @selector(setWatched:);
614        NSInvocation *fileInv = [NSInvocation invocationWithMethodSignature:[[SapphireFileMetaData class] instanceMethodSignatureForSelector:select]];
615        [fileInv setSelector:select];
616        [fileInv setArgument:&watched atIndex:2];
617        [self invokeRecursivelyOnFiles:fileInv withPredicate:predicate];
618}
619
620- (BOOL)favoriteForPredicate:(SapphirePredicate *)predicate
621{
622        SEL select = @selector(favorite);
623        NSInvocation *fileInv = [NSInvocation invocationWithMethodSignature:[[SapphireFileMetaData class] instanceMethodSignatureForSelector:select]];
624        [fileInv setSelector:select];
625        return [self checkResult:YES recursivelyOnFiles:fileInv forPredicate:predicate];       
626}
627
628- (void)setFavorite:(BOOL)favorite predicate:(SapphirePredicate *)predicate
629{
630        SEL select = @selector(setFavorite:);
631        NSInvocation *fileInv = [NSInvocation invocationWithMethodSignature:[[SapphireFileMetaData class] instanceMethodSignatureForSelector:select]];
632        [fileInv setSelector:select];
633        [fileInv setArgument:&favorite atIndex:2];
634        [self invokeRecursivelyOnFiles:fileInv withPredicate:predicate];
635}
636
637- (void)setToImportFromTVForPredicate:(SapphirePredicate *)predicate
638{
639        SEL select = @selector(setToImportFromTV);
640        NSInvocation *fileInv = [NSInvocation invocationWithMethodSignature:[[SapphireFileMetaData class] instanceMethodSignatureForSelector:select]];
641        [fileInv setSelector:select];
642        [self invokeRecursivelyOnFiles:fileInv withPredicate:predicate];
643}
644
645- (NSMutableDictionary *)getDisplayedMetaData
646{
647        return [NSMutableDictionary dictionaryWithObjectsAndKeys:
648                [path lastPathComponent], META_TITLE_KEY,
649                nil];
650}
651
652@end
653
654@implementation SapphireFileMetaData : SapphireMetaData
655
656static NSDictionary *metaDataSubstitutions = nil;
657static NSSet *displayedMetaData = nil;
658
659+ (void) initialize
660{
661        metaDataSubstitutions = [[NSDictionary alloc] initWithObjectsAndKeys:
662                @"Video", VIDEO_DESC_KEY,
663                @"Audio", AUDIO_DESC_KEY,
664                nil];
665        displayedMetaData = [[NSSet alloc] initWithObjects:
666                VIDEO_DESC_KEY,
667                AUDIO_DESC_KEY,
668                META_RATING_KEY,
669                META_SUMMARY_KEY,
670                META_COPYRIGHT_KEY,
671                META_TITLE_KEY,
672                nil];
673               
674}
675
676- (BOOL) updateMetaData
677{
678        NSDictionary *props = [[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:YES];
679        int modTime = [[props objectForKey:NSFileModificationDate] timeIntervalSince1970];
680        BOOL updated =FALSE;
681       
682        if(props == nil)
683                //No file
684                return FALSE;
685       
686        if(modTime != [self modified] || [[metaData objectForKey:META_VERSION_KEY] intValue] != META_VERSION)
687        {
688                updated=TRUE ;
689                NSMutableDictionary *fileMeta = [NSMutableDictionary dictionary];
690               
691                [fileMeta setObject:[NSNumber numberWithInt:modTime] forKey:MODIFIED_KEY];
692                [fileMeta setObject:[props objectForKey:NSFileSize] forKey:SIZE_KEY];
693                [fileMeta setObject:[NSNumber numberWithInt:META_VERSION] forKey:META_VERSION_KEY];
694               
695                NSError *error = nil;
696                QTMovie *movie = [QTMovie movieWithFile:path error:&error];
697                QTTime duration = [movie duration];
698                [fileMeta setObject:[NSNumber numberWithFloat:(float)duration.timeValue/(float)duration.timeScale] forKey:DURATION_KEY];
699                NSArray *audioTracks = [movie tracksOfMediaType:@"soun"];
700                NSNumber *audioSampleRate = nil;
701                if([audioTracks count])
702                {
703                        QTTrack *track = [audioTracks objectAtIndex:0];
704                        QTMedia *media = [track media];
705                        audioSampleRate = [media attributeForKey:QTMediaTimeScaleAttribute];
706                        if(media != nil)
707                        {
708                                Media qtMedia = [media quickTimeMedia];
709                                Handle sampleDesc = NewHandle(1);
710                                GetMediaSampleDescription(qtMedia, 1, (SampleDescriptionHandle)sampleDesc);
711                                CFStringRef userText = nil;
712                                ByteCount       propSize = 0;
713                                QTSoundDescriptionGetProperty((SoundDescriptionHandle)sampleDesc, kQTPropertyClass_SoundDescription, kQTSoundDescriptionPropertyID_UserReadableText, sizeof(userText), &userText, &propSize);
714                                DisposeHandle(sampleDesc);
715                               
716                                if(userText != nil)
717                                {
718                                        [fileMeta setObject:(NSString *)userText forKey:AUDIO_DESC_KEY];
719                                        CFRelease(userText);
720                                }
721                        }
722                }
723                if(audioSampleRate != nil)
724                        [fileMeta setObject:audioSampleRate forKey:SAMPLE_RATE_KEY];
725                NSArray *videoTracks = [movie tracksOfMediaType:@"vide"];
726                if([videoTracks count])
727                {
728                        QTTrack *track = [videoTracks objectAtIndex:0];
729                        QTMedia *media = [track media];
730                        if(media != nil)
731                        {
732                                Media qtMedia = [media quickTimeMedia];
733                                Handle sampleDesc = NewHandle(1);
734                                GetMediaSampleDescription(qtMedia, 1, (SampleDescriptionHandle)sampleDesc);
735                                CFStringRef userText = nil;
736                                ByteCount       propSize = 0;
737                                ICMImageDescriptionGetProperty((ImageDescriptionHandle)sampleDesc, kQTPropertyClass_ImageDescription, kICMImageDescriptionPropertyID_SummaryString, sizeof(userText), &userText, &propSize);
738                                DisposeHandle(sampleDesc);
739                               
740                                if(userText != nil)
741                                {
742                                        [fileMeta setObject:(NSString *)userText forKey:VIDEO_DESC_KEY];
743                                        CFRelease(userText);
744                                }
745                        }
746                }
747                [metaData addEntriesFromDictionary:fileMeta];
748        }
749        return updated ;
750}
751
752- (void)importInfo:(NSDictionary *)newMeta
753{
754        [metaData addEntriesFromDictionary:newMeta];
755}
756
757- (int)modified
758{
759        return [[metaData objectForKey:MODIFIED_KEY] intValue];
760}
761
762- (BOOL)watched
763{
764        return [[metaData objectForKey:WATCHED_KEY] boolValue];
765}
766
767- (void)setWatched:(BOOL)watched
768{
769        [metaData setObject:[NSNumber numberWithBool:watched] forKey:WATCHED_KEY];
770}
771
772- (BOOL)favorite
773{
774        return [[metaData objectForKey:FAVORITE_KEY] boolValue];
775}
776
777- (void)setFavorite:(BOOL)favorite
778{
779        [metaData setObject:[NSNumber numberWithBool:favorite] forKey:FAVORITE_KEY];
780}
781
782- (BOOL)importedFromTV
783{
784        return [[metaData objectForKey:TVRAGE_IMPORT_KEY] boolValue];
785}
786
787- (void)setToImportFromTV
788{
789        [metaData removeObjectForKey:TVRAGE_IMPORT_KEY];
790}
791
792- (unsigned int)resumeTime
793{
794        return [[metaData objectForKey:RESUME_KEY] unsignedIntValue];
795}
796
797- (void)setResumeTime:(unsigned int)resumeTime
798{
799        [metaData setObject:[NSNumber numberWithUnsignedInt:resumeTime] forKey:RESUME_KEY];
800}
801
802- (long long)size
803{
804        return [[metaData objectForKey:SIZE_KEY] longLongValue];
805}
806
807- (float)duration
808{
809        return [[metaData objectForKey:DURATION_KEY] floatValue];
810}
811
812- (int)sampleRate
813{
814        return [[metaData objectForKey:SAMPLE_RATE_KEY] intValue];
815}
816
817- (int)episodeNumber
818{
819        return [[metaData objectForKey:EPISODE_NUMBER_KEY] intValue] ;
820}
821- (NSString *)episodeTitle
822{
823        NSString * title = [metaData objectForKey:EPISODE_TITLE_KEY] ;
824        if(title!=nil)return title ;
825        else
826        return nil ;
827}
828
829- (NSString *)sizeString
830{
831        float size = [self size];
832        if(size == 0)
833                return @"-";
834        char letter = ' ';
835        if(size >= 1024000)
836        {
837                if(size >= 1024*1024000)
838                {
839                        size /= 1024 * 1024 * 1024;
840                        letter = 'G';
841                }
842                else
843                {
844                        size /= 1024 * 1024;
845                        letter = 'M';
846                }
847        }
848        else if (size >= 1000)
849        {
850                size /= 1024;
851                letter = 'K';
852        }
853        return [NSString stringWithFormat:@"%.1f%cB", size, letter];   
854}
855
856- (NSMutableDictionary *)getDisplayedMetaData
857{
858        NSString *name = [path lastPathComponent];
859        int duration = [self duration];
860        int secs = duration % 60;
861        int mins = (duration /60) % 60;
862        int hours = duration / 3600;
863        NSString *durationStr = nil;
864        if(hours != 0)
865                durationStr = [NSString stringWithFormat:@"%d:%02d:%02d", hours, mins, secs];
866        else if (mins != 0)
867                durationStr = [NSString stringWithFormat:@"%d:%02d", mins, secs];
868        else
869                durationStr = [NSString stringWithFormat:@"%ds", secs];
870        NSMutableDictionary *ret = [metaData mutableCopy];
871        //Pretty this up now
872        NSMutableSet *currentKeys = [NSMutableSet setWithArray:[ret allKeys]];
873        [currentKeys minusSet:displayedMetaData];
874        [ret removeObjectsForKeys:[currentKeys allObjects]];
875       
876        NSEnumerator *subEnum = [metaDataSubstitutions keyEnumerator];
877        NSString *key = nil;
878        while((key = [subEnum nextObject]) != nil)
879        {
880                NSString *value = [ret objectForKey:key];
881                if(value != nil)
882                {
883                        [ret setObject:value forKey:[metaDataSubstitutions objectForKey:key]];
884                        [ret removeObjectForKey:key];
885                }
886        }
887        if([self duration])
888                [ret setObject:durationStr forKey:DURATION_KEY];
889        if([ret objectForKey:META_TITLE_KEY] == nil)
890                [ret setObject:name forKey:META_TITLE_KEY];\
891        if([self size])
892                [ret setObject:[self sizeString] forKey:SIZE_KEY];
893        return ret;
894}
895
896@end
Note: See TracBrowser for help on using the repository browser.