source: trunk/SapphireMetaData.m @ 90

Revision 90, 22.0 KB checked in by gbooker, 7 years ago (diff)

Better pruning of the meta data for display

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