source: branches/CoreData/SapphireFrappliance/MetaData/SapphireMObjects/SapphireFileMetaData.m @ 544

Revision 544, 17.0 KB checked in by gbooker, 7 years ago (diff)

Now builds without warnings or errors

Line 
1#import "SapphireFileMetaData.h"
2#import "SapphireXMLData.h"
3#import "SapphireMovie.h"
4#import "SapphireEpisode.h"
5#import "SapphireDirectoryMetaData.h"
6#import "SapphireVideoTsParser.h"
7#import "SapphireMetaDataSupport.h"
8#import "SapphireMediaPreview.h"
9
10#import <QTKit/QTKit.h>
11
12@implementation SapphireFileMetaData
13
14//File Specific Keys
15#define MODIFIED_KEY                            @"Modified"
16#define WATCHED_KEY                                     @"Watched"
17#define FAVORITE_KEY                            @"Favorite"
18#define RESUME_KEY                                      @"Resume Time"
19#define SIZE_KEY                                        @"Size"
20#define DURATION_KEY                            @"Duration"
21#define AUDIO_DESC_KEY                          @"Audio Description"
22#define SAMPLE_RATE_KEY                         @"Sample Rate"
23#define VIDEO_DESC_KEY                          @"Video Description"
24#define AUDIO_FORMAT_KEY                        @"Audio Format"
25#define SUBTITLES_KEY                           @"Subtitles"
26#define JOINED_FILE_KEY                         @"Joined File"
27
28static NSSet *displayedMetaData;
29static NSArray *displayedMetaDataOrder;
30
31+ (void)load
32{
33        displayedMetaDataOrder = [NSArray arrayWithObjects:
34                                                          //These are not shown in the list
35                                                          META_MOVIE_MPAA_RATING_KEY,
36                                                          META_RATING_KEY,
37                                                          META_DESCRIPTION_KEY,
38                                                          META_MOVIE_PLOT_KEY,
39                                                          META_COPYRIGHT_KEY,
40                                                          META_TITLE_KEY,
41                                                          META_MOVIE_TITLE_KEY,
42                                                          META_SHOW_AIR_DATE,
43                                                          META_MOVIE_WIRTERS_KEY,
44                                                          META_MOVIE_RELEASE_DATE_KEY,
45                                                          META_MOVIE_IMDB_250_KEY,
46                                                          META_MOVIE_IMDB_RATING_KEY,                                     
47                                                          //These are displayed as line items
48                                                          META_MOVIE_DIRECTOR_KEY,
49                                                          META_MOVIE_CAST_KEY,
50                                                          META_MOVIE_GENRES_KEY,
51                                                          META_EPISODE_AND_SEASON_KEY,
52                                                          META_SEASON_NUMBER_KEY,
53                                                          META_EPISODE_NUMBER_KEY,
54                                                          META_MOVIE_IMDB_STATS_KEY,
55                                                          SIZE_KEY,
56                                                          DURATION_KEY,
57                                                          VIDEO_DESC_KEY,
58                                                          AUDIO_DESC_KEY,
59                                                          SUBTITLES_KEY,
60                                                          nil];
61        displayedMetaData = [[NSSet alloc] initWithArray:displayedMetaDataOrder];       
62}
63
64+ (SapphireFileMetaData *)fileWithPath:(NSString *)path inContext:(NSManagedObjectContext *)moc
65{
66        SapphireMetaData *meta = [SapphireMetaData metaDataWithPath:path inContext:moc];
67        if([meta isKindOfClass:[SapphireFileMetaData class]])
68                return (SapphireFileMetaData *)meta;
69        return nil;
70}
71
72+ (SapphireFileMetaData *)internalCreateFileWithPath:(NSString *)path parent:(SapphireDirectoryMetaData *)parent inContext:(NSManagedObjectContext *)moc
73{
74        SapphireFileMetaData *ret = [NSEntityDescription insertNewObjectForEntityForName:SapphireFileMetaDataName inManagedObjectContext:moc];
75        ret.parent = parent;
76        ret.path = path;
77       
78        return ret;
79}
80
81+ (SapphireFileMetaData *)createFileWithPath:(NSString *)path inContext:(NSManagedObjectContext *)moc
82{
83        SapphireFileMetaData *ret = [SapphireFileMetaData fileWithPath:path inContext:moc];
84        if(ret != nil)
85                return ret;
86       
87        SapphireDirectoryMetaData *parent = [SapphireDirectoryMetaData createDirectoryWithPath:[path stringByDeletingLastPathComponent] inContext:moc];
88        ret = [SapphireFileMetaData internalCreateFileWithPath:path parent:parent inContext:moc];
89       
90        return ret;
91}
92
93+ (SapphireFileMetaData *)createFileWithPath:(NSString *)path parent:(SapphireDirectoryMetaData *)parent inContext:(NSManagedObjectContext *)moc
94{
95        SapphireFileMetaData *ret = [SapphireFileMetaData fileWithPath:path inContext:moc];
96        if(ret != nil)
97                return ret;
98       
99        return [SapphireFileMetaData internalCreateFileWithPath:path parent:parent inContext:moc];
100}
101
102- (NSDictionary *)overrideDict:(NSDictionary *)dict
103{
104        NSDictionary *xml = self.xmlData.overrideProperties;
105        if(xml == nil)
106                return dict;
107       
108        NSMutableDictionary *orig = nil;
109        if(self.xmlData.originalProperties == nil)
110                orig = [[NSMutableDictionary alloc] init];
111        else
112                orig = [self.xmlData.originalProperties mutableCopy];
113       
114        NSMutableDictionary *ret = [[dict mutableCopy] autorelease];
115       
116        NSSet *dictKeys = [NSSet setWithArray:[dict allKeys]];
117        NSSet *xmlKeys = [NSSet setWithArray:[xml allKeys]];
118        NSMutableSet *unionKeys = [dictKeys mutableCopy];
119        [unionKeys intersectSet:xmlKeys];
120        [unionKeys removeObject:MODIFIED_KEY];
121       
122        NSEnumerator *keyEnum = [unionKeys objectEnumerator];
123        NSString *key;
124        while((key = [keyEnum nextObject]) != nil)
125        {
126                [orig setObject:[dict objectForKey:key] forKey:key];
127                [ret setObject:[xml objectForKey:key] forKey:key];
128        }
129        [unionKeys release];
130        self.xmlData.originalProperties = orig;
131        [orig release];
132        return ret;
133}
134
135- (void)insertDictionary:(NSDictionary *)dict withDefer:(NSMutableDictionary *)defer
136{
137        self.audioDescription = [dict objectForKey:AUDIO_DESC_KEY];
138        self.audioFormatID = [dict objectForKey:AUDIO_FORMAT_KEY];
139        self.duration = [dict objectForKey:DURATION_KEY];
140        self.favoriteValue = [[dict objectForKey:FAVORITE_KEY] boolValue];
141        self.fileClass = [dict objectForKey:@"File Class"];
142        self.fileContainerType = [dict objectForKey:@"File Container Type"];
143        int importType = IMPORT_TYPE_FILE_MASK;
144        id value = [dict objectForKey:MODIFIED_KEY];
145        if(value != nil)
146                self.modified = [NSDate dateWithTimeIntervalSince1970:[value intValue]];
147        self.resumeTimeValue = [[dict objectForKey:RESUME_KEY] unsignedIntValue];
148        self.sampleRate = [dict objectForKey:SAMPLE_RATE_KEY];
149        self.size = [dict objectForKey:SIZE_KEY];
150        self.subtitlesDescription = [dict objectForKey:SUBTITLES_KEY];
151        self.videoDescription = [dict objectForKey:VIDEO_DESC_KEY];
152        self.watchedValue = [[dict objectForKey:WATCHED_KEY] boolValue];
153        value = [dict objectForKey:@"XML Source"];
154        if(value != nil)
155        {
156                NSDictionary *xmlDict = (NSDictionary *)value;
157                SapphireXMLData *xml = [NSEntityDescription insertNewObjectForEntityForName:SapphireXMLDataName inManagedObjectContext:[self managedObjectContext]];
158                [xml insertDictionary:xmlDict];
159                self.xmlData = xml;
160                importType |= IMPORT_TYPE_XML_MASK;
161        }
162        value = [dict objectForKey:@"TVRage Source"];
163        if(value != nil)
164        {
165                NSDictionary *tvdict = [self overrideDict:(NSDictionary *)value];
166                SapphireEpisode *ep = [SapphireEpisode episodeWithDictionary:tvdict inContext:[self managedObjectContext]];
167                self.tvEpisode = ep;
168                if(ep != nil)
169                        self.fileClassValue = FILE_CLASS_TV_SHOW;
170                importType |= IMPORT_TYPE_TVSHOW_MASK;
171        }
172        value = [dict objectForKey:@"IMDB Source"];
173        if(value != nil)
174        {
175                NSDictionary *movieDict = [self overrideDict:(NSDictionary *)value];
176                SapphireMovie *movie = [SapphireMovie movieWithDictionary:movieDict inContext:[self managedObjectContext]];
177                self.movie = movie;
178                if(movie != nil)
179                        self.fileClassValue = FILE_CLASS_MOVIE;
180                importType |= IMPORT_TYPE_MOVIE_MASK;
181        }
182        self.importTypeValue = importType;
183        NSString *joinPath = [dict objectForKey:JOINED_FILE_KEY];
184        if(joinPath != nil)
185        {
186                NSMutableDictionary *joinDict = [defer objectForKey:@"Join"];
187                NSMutableArray *joinList = [joinDict objectForKey:joinPath];
188                if(joinList == nil)
189                {
190                        joinList = [NSMutableArray array];
191                        [joinDict setObject:joinList forKey:joinPath];
192                }
193                [joinList addObject:self];
194        }
195}
196
197/*Custom TV Episode handler*/
198- (NSComparisonResult) episodeCompare:(SapphireFileMetaData *)other
199{
200        /*Sort by episode*/
201        SapphireEpisode *myEp = self.tvEpisode;
202        SapphireEpisode *theirEp = other.tvEpisode;
203       
204        if(myEp != nil)
205        {
206                if(theirEp != nil)
207                        return [myEp compare:theirEp];
208                else
209                        return NSOrderedAscending;
210        }
211        else if (theirEp != nil)
212                return NSOrderedDescending;
213
214        return NSOrderedSame;
215}
216
217- (BOOL) needsUpdating
218{
219        /*Check modified date*/
220        NSDictionary *props = [[NSFileManager defaultManager] fileAttributesAtPath:self.path traverseLink:YES];
221        int modTime = [[props objectForKey:NSFileModificationDate] timeIntervalSince1970];
222       
223        if(props == nil)
224        /*No file*/
225                return FALSE;
226       
227        /*Has it been modified since last import?*/
228        return modTime != [self.modified timeIntervalSince1970];
229}
230
231- (oneway void)addFileData:(bycopy NSDictionary *)fileMeta
232{
233        self.audioDescription = [fileMeta objectForKey:AUDIO_DESC_KEY];
234        self.audioFormatID = [fileMeta objectForKey:AUDIO_FORMAT_KEY];
235        self.duration = [fileMeta objectForKey:DURATION_KEY];
236        id value = [fileMeta objectForKey:MODIFIED_KEY];
237        if(value != nil)
238                self.modified = [NSDate dateWithTimeIntervalSince1970:[value intValue]];
239        self.sampleRate = [fileMeta objectForKey:SAMPLE_RATE_KEY];
240        self.size = [fileMeta objectForKey:SIZE_KEY];
241        self.subtitlesDescription = [fileMeta objectForKey:SUBTITLES_KEY];
242        self.videoDescription = [fileMeta objectForKey:VIDEO_DESC_KEY];
243}
244
245
246BOOL updateMetaData(id <SapphireFile> file)
247{
248        BOOL updated =FALSE;
249        if([file needsUpdating])
250        {
251                /*We did an update*/
252                updated=TRUE ;
253                NSMutableDictionary *fileMeta = [NSMutableDictionary dictionary];
254                NSString *path = [file path];
255               
256                NSDictionary *props = [[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:YES];
257                int modTime = [[props objectForKey:NSFileModificationDate] timeIntervalSince1970];
258                /*Set modified, size, and version*/
259                [fileMeta setObject:[NSNumber numberWithInt:modTime] forKey:MODIFIED_KEY];
260                [fileMeta setObject:[props objectForKey:NSFileSize] forKey:SIZE_KEY];
261               
262                if([file fileContainerTypeValue] == FILE_CONTAINER_TYPE_QT_MOVIE)
263                {
264                        /*Open the movie*/
265                        NSError *error = nil;
266                        QTMovie *movie = [QTMovie movieWithFile:path error:&error];
267                        QTTime duration = [movie duration];
268                        [fileMeta setObject:[NSNumber numberWithFloat:(float)duration.timeValue/(float)duration.timeScale] forKey:DURATION_KEY];
269                        NSArray *audioTracks = [movie tracksOfMediaType:@"soun"];
270                        NSNumber *audioSampleRate = nil;
271                        if([audioTracks count])
272                        {
273                                /*Get the audio track*/
274                                QTTrack *track = [audioTracks objectAtIndex:0];
275                                QTMedia *media = [track media];
276                                if(media != nil)
277                                {
278                                        /*Get the audio format*/
279                                        Media qtMedia = [media quickTimeMedia];
280                                        Handle sampleDesc = NewHandle(1);
281                                        GetMediaSampleDescription(qtMedia, 1, (SampleDescriptionHandle)sampleDesc);
282                                        AudioStreamBasicDescription asbd;
283                                        ByteCount       propSize = 0;
284                                        QTSoundDescriptionGetProperty((SoundDescriptionHandle)sampleDesc, kQTPropertyClass_SoundDescription, kQTSoundDescriptionPropertyID_AudioStreamBasicDescription, sizeof(asbd), &asbd, &propSize);
285                                       
286                                        if(propSize != 0)
287                                        {
288                                                /*Set the format and sample rate*/
289                                                NSNumber *format = [NSNumber numberWithUnsignedInt:asbd.mFormatID];
290                                                [fileMeta setObject:format forKey:AUDIO_FORMAT_KEY];
291                                                audioSampleRate = [NSNumber numberWithDouble:asbd.mSampleRate];
292                                        }
293                                       
294                                        CFStringRef userText = nil;
295                                        propSize = 0;
296                                        QTSoundDescriptionGetProperty((SoundDescriptionHandle)sampleDesc, kQTPropertyClass_SoundDescription, kQTSoundDescriptionPropertyID_UserReadableText, sizeof(userText), &userText, &propSize);
297                                        if(userText != nil)
298                                        {
299                                                /*Set the description*/
300                                                [fileMeta setObject:(NSString *)userText forKey:AUDIO_DESC_KEY];
301                                                CFRelease(userText);
302                                        }
303                                        DisposeHandle(sampleDesc);
304                                }
305                        }
306                        /*Set the sample rate*/
307                        if(audioSampleRate != nil)
308                                [fileMeta setObject:audioSampleRate forKey:SAMPLE_RATE_KEY];
309                        NSArray *videoTracks = [movie tracksOfMediaType:@"vide"];
310                        if([videoTracks count])
311                        {
312                                /*Get the video track*/
313                                QTTrack *track = [videoTracks objectAtIndex:0];
314                                QTMedia *media = [track media];
315                                if(media != nil)
316                                {
317                                        /*Get the video description*/
318                                        Media qtMedia = [media quickTimeMedia];
319                                        Handle sampleDesc = NewHandle(1);
320                                        GetMediaSampleDescription(qtMedia, 1, (SampleDescriptionHandle)sampleDesc);
321                                        CFStringRef userText = nil;
322                                        ByteCount propSize = 0;
323                                        ICMImageDescriptionGetProperty((ImageDescriptionHandle)sampleDesc, kQTPropertyClass_ImageDescription, kICMImageDescriptionPropertyID_SummaryString, sizeof(userText), &userText, &propSize);
324                                        DisposeHandle(sampleDesc);
325                                       
326                                        if(userText != nil)
327                                        {
328                                                /*Set the description*/
329                                                [fileMeta setObject:(NSString *)userText forKey:VIDEO_DESC_KEY];
330                                                CFRelease(userText);
331                                        }
332                                }
333                        }
334                } //QTMovie
335                else if([file fileContainerTypeValue] == FILE_CONTAINER_TYPE_VIDEO_TS)
336                {
337                        SapphireVideoTsParser *dvd = [[SapphireVideoTsParser alloc] initWithPath:path];
338                       
339                        [fileMeta setObject:[dvd videoFormatsString ] forKey:VIDEO_DESC_KEY];
340                        [fileMeta setObject:[dvd audioFormatsString ] forKey:AUDIO_DESC_KEY];
341                        [fileMeta setObject:[dvd subtitlesString    ] forKey:SUBTITLES_KEY ];
342                        [fileMeta setObject:[dvd mainFeatureDuration] forKey:DURATION_KEY  ];
343                        [fileMeta setObject:[dvd totalSize          ] forKey:SIZE_KEY      ];
344                       
345                        [dvd release];
346                } // VIDEO_TS
347                [file addFileData:fileMeta];
348        }
349        return updated ;
350}
351
352- (BOOL)updateMetaData
353{
354        return updateMetaData(self);
355}
356
357- (NSString *)sizeString
358{
359        /*Get size*/
360        float size = [self sizeValue];
361        if(size == 0)
362                return @"-";
363       
364        /*The letter for magnitude*/
365        char letter = ' ';
366        if(size >= 1024000)
367        {
368                if(size >= 1024*1024000)
369                {
370                        /*GB*/
371                        size /= 1024 * 1024 * 1024;
372                        letter = 'G';
373                }
374                else
375                {
376                        /*MB*/
377                        size /= 1024 * 1024;
378                        letter = 'M';
379                }
380        }
381        else if (size >= 1000)
382        {
383                /*KB*/
384                size /= 1024;
385                letter = 'K';
386        }
387        return [NSString stringWithFormat:@"%.1f%cB", size, letter];   
388}
389
390- (void)setToReimportFromMask:(int)mask
391{
392        int currentMask = self.importTypeValue;
393        self.importTypeValue = currentMask & ~mask;
394        if(mask & IMPORT_TYPE_MOVIE_MASK)
395                self.movie = nil;
396        if(mask & IMPORT_TYPE_TVSHOW_MASK)
397                self.tvEpisode = nil;
398}
399
400- (void)clearMetaData
401{
402        self.audioDescription = nil;
403        self.audioFormatID = nil;
404        self.duration = nil;
405        self.favorite = nil;
406        self.fileClass = nil;
407        self.fileContainerType = nil;
408        self.hasVideo = nil;
409        self.importType = nil;
410        self.modified = nil;
411        self.resumeTime = nil;
412        self.sampleRate = nil;
413        self.size = nil;
414        self.subtitlesDescription = nil;
415        self.videoDescription = nil;
416        self.watched = nil;
417        self.movie = nil;
418        self.tvEpisode = nil;
419        if(self.xmlData != nil)
420        {
421                [[self managedObjectContext] deleteObject:self.xmlData];
422        }
423}
424
425- (NSString *)coverArtPath
426{
427        /*Find cover art for the current file in the "Cover Art" dir */
428        NSString *subPath = [[self path] stringByDeletingPathExtension];
429        NSString *fileName = [subPath lastPathComponent];
430        NSString * myArtPath=nil;
431        if([self fileClassValue]==FILE_CLASS_TV_SHOW)
432                myArtPath=[[self tvEpisode] coverArtPath];
433        if([self fileClassValue]==FILE_CLASS_MOVIE)
434                myArtPath=[[self movie] coverArtPath];
435       
436        /* Check the Collection Art location */
437        NSString *ret=searchCoverArtExtForPath(myArtPath);
438       
439        if(ret != nil)
440                return ret;
441       
442        /* Try Legacy Folders with the file */
443        ret=searchCoverArtExtForPath([[[subPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"Cover Art"] stringByAppendingPathComponent:fileName]);
444       
445        if(ret != nil)
446                return ret;
447       
448        /*Find cover art for the current file in the current dir*/
449        ret = searchCoverArtExtForPath(subPath);
450       
451        if(ret != nil)
452                return ret;
453       
454       
455        return nil;
456}
457
458- (NSMutableDictionary *)getDisplayedMetaDataInOrder:(NSArray * *)order;
459{
460        NSString *name = [[self path] lastPathComponent];
461        /*Create duration string*/
462        int duration = [self durationValue];
463        int secs = duration % 60;
464        int mins = (duration /60) % 60;
465        int hours = duration / 3600;
466        NSString *durationStr = nil;
467        if(hours != 0)
468                durationStr = [NSString stringWithFormat:@"%d:%02d:%02d", hours, mins, secs];
469        else if (mins != 0)
470                durationStr = [NSString stringWithFormat:@"%d:%02d", mins, secs];
471        else
472                durationStr = [NSString stringWithFormat:@"%ds", secs];
473        /*Set the order*/
474        if(order != nil)
475                *order = displayedMetaDataOrder;
476       
477        NSMutableDictionary *ret = [[NSMutableDictionary alloc] init];
478       
479        SapphireMovie *movie = [self movie];
480        SapphireEpisode *ep = [self tvEpisode];
481        if(movie != nil)
482        {
483                [movie insertDisplayMetaData:ret];
484        }
485        else if (ep != nil)
486        {
487                [ep insertDisplayMetaData:ret];
488        }
489       
490        id value = [self videoDescription];
491        if(value != nil)
492                [ret setObject:value forKey:BRLocalizedString(@"Video", @"Video format in metadata display")];
493        value = [self audioDescription];
494        if(value != nil)
495                [ret setObject:value forKey:BRLocalizedString(@"Audio", @"Audio format in metadata display")];
496        value = [self subtitlesDescription];
497        if(value != nil)
498                [ret setObject:value forKey:SUBTITLES_KEY];
499        if([self durationValue])
500        {
501                if([self sizeValue])
502                        [ret setObject:[NSString stringWithFormat:@"%@ (%@)", durationStr, [self sizeString]] forKey:BRLocalizedString(DURATION_KEY, @"file duration in metadata display")];
503                else
504                        [ret setObject:durationStr forKey:BRLocalizedString(DURATION_KEY, @"file duration in metadata display")];
505        }
506        else
507                [ret setObject:[self sizeString] forKey:BRLocalizedString(SIZE_KEY, @"filesize in metadata display")];
508       
509        /*Set the title*/
510        if([ret objectForKey:META_TITLE_KEY] == nil)
511                [ret setObject:name forKey:META_TITLE_KEY];
512        return ret;
513}
514
515- (int)overriddenSeasonNumber
516{
517        return self.xmlData.searchSeasonValue;
518}
519
520- (int)overriddenEpisodeNumber
521{
522        return self.xmlData.searchEpisodeValue;
523}
524
525- (int)overriddenSecondEpisodeNumber
526{
527        return self.xmlData.searchSecondEpisodeValue;
528}
529
530- (FileContainerType)fileContainerTypeValue
531{
532        return super.fileContainerTypeValue;
533}
534
535- (ImportTypeMask)importTypeValue
536{
537        return super.importTypeValue;
538}
539
540- (long)importedTimeFromSource:(int)source
541{
542        if(source == IMPORT_TYPE_FILE_MASK)
543                return [self.modified timeIntervalSince1970];
544        else if(source == IMPORT_TYPE_XML_MASK)
545                return [self.xmlData.modified timeIntervalSince1970];
546        return 0;
547}
548
549- (oneway void)importInfo:(bycopy NSMutableDictionary *)newMeta fromSource:(int)source withTime:(long)modTime
550{
551        //XXX
552}
553
554- (FileClass)fileClassValue
555{
556        return super.fileClassValue;
557}
558
559- (void)setFileClassValue:(FileClass)fileClass
560{
561        super.fileClassValue = fileClass;
562}
563
564@end
Note: See TracBrowser for help on using the repository browser.