source: trunk/SapphireFrappliance/MetaData/SapphireMObjects/SapphireFileMetaData.m @ 1319

Revision 1319, 40.5 KB checked in by gbooker, 4 years ago (diff)

Added QT Metadata importer and changed another enum to better value names.

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#import "SapphireFileSymLink.h"
10#import "SapphireSettings.h"
11#import "CoreDataSupportFunctions.h"
12
13#import "SapphireTVShow.h"
14#import "SapphireSeason.h"
15#import "SapphireTVTranslation.h"
16#import "SapphireMovieTranslation.h"
17#import "SapphireJoinedFile.h"
18
19#import "NSArray-Extensions.h"
20#import "NSString-Extensions.h"
21#import "NSFileManager-Extensions.h"
22
23#import <QTKit/QTKit.h>
24
25@implementation SapphireFileMetaData
26
27//ATV Extra Info
28NSString *META_SHOW_BROADCASTER_KEY =           @"Broadcast Company";
29NSString *META_SHOW_AQUIRED_DATE =                      @"Date Aquired";
30NSString *META_SHOW_RATING_KEY =                        @"Rating";
31NSString *META_SHOW_FAVORITE_RATING_KEY =       @"User Rating";
32NSString *META_COPYRIGHT_KEY =                          @"Copyright";
33
34//General Keys
35NSString *META_TITLE_KEY =                                      @"Title";
36NSString *META_DESCRIPTION_KEY =                        @"Show Description";
37NSString *META_SUMMARY_KEY =                            @"Summary";
38NSString *META_RATING_KEY =                                     @"Rating";
39NSString *FILE_CLASS_KEY =                                      @"File Class";
40
41//IMDB Type Info
42NSString *META_MOVIE_TITLE_KEY =                                @"Title";
43NSString *META_MOVIE_CAST_KEY =                                 @"Cast";
44NSString *META_MOVIE_RELEASE_DATE_KEY =                 @"Release Date";
45NSString *META_MOVIE_DIRECTOR_KEY =                             @"Director";
46NSString *META_MOVIE_WIRTERS_KEY =                              @"Writers";
47NSString *META_MOVIE_GENRES_KEY =                               @"Genres";
48NSString *META_MOVIE_PLOT_KEY =                                 @"Plot";
49NSString *META_MOVIE_IMDB_RATING_KEY =                  @"IMDB Rating";
50NSString *META_MOVIE_IMDB_250_KEY =                             @"IMDB Top 250";
51NSString *META_MOVIE_MPAA_RATING_KEY =                  @"MPAA Rating";
52NSString *META_MOVIE_OSCAR_KEY =                                @"Oscars";
53NSString *META_MOVIE_IDENTIFIER_KEY =                   @"Movie ID";
54NSString *META_SEARCH_IMDB_NUMBER_KEY =                 @"Search IMDB Number";
55NSString *META_MOVIE_SORT_TITLE_KEY =                   @"Movie Sort Title";
56
57//TV Show Specific Keys
58NSString *META_SEASON_NUMBER_KEY =                      @"Season";
59NSString *META_EPISODE_NUMBER_KEY =                     @"Episode";
60NSString *META_SHOW_NAME_KEY =                          @"Show Name";
61NSString *META_SHOW_AIR_DATE =                          @"Air Date";
62NSString *META_ABSOLUTE_EP_NUMBER_KEY =         @"Episode Number";
63NSString *META_SHOW_IDENTIFIER_KEY =            @"Show ID";
64NSString *META_EPISODE_2_NUMBER_KEY =           @"Episode 2";
65NSString *META_ABSOLUTE_EP_2_NUMBER_KEY =       @"Episode Number 2";
66NSString *META_SEARCH_SEASON_NUMBER_KEY =       @"Search Season";
67NSString *META_SEARCH_EPISODE_NUMBER_KEY =      @"Search Episode";
68NSString *META_SEARCH_EPISODE_2_NUMBER_KEY =    @"Search Episode 2";
69
70//File Specific Keys
71NSString *META_FILE_MODIFIED_KEY =                              @"Modified";
72NSString *META_FILE_WATCHED_KEY =                               @"Watched";
73NSString *META_FILE_FAVORITE_KEY =                              @"Favorite";
74NSString *META_FILE_RESUME_KEY =                                @"Resume Time";
75NSString *META_FILE_SIZE_KEY =                                  @"Size";
76NSString *META_FILE_DURATION_KEY =                              @"Duration";
77NSString *META_FILE_AUDIO_DESC_KEY =                    @"Audio Description";
78NSString *META_FILE_SAMPLE_RATE_KEY =                   @"Sample Rate";
79NSString *META_FILE_VIDEO_DESC_KEY =                    @"Video Description";
80NSString *META_FILE_AUDIO_FORMAT_KEY =                  @"Audio Format";
81NSString *META_FILE_SUBTITLES_KEY =                     @"Subtitles";
82NSString *META_FILE_JOINED_FILE_KEY =                   @"Joined File";
83
84static NSSet *displayedMetaData;
85static NSArray *displayedMetaDataOrder;
86static NSSet *secondaryFiles;
87
88+ (void)load
89{
90        displayedMetaDataOrder = [[NSArray alloc] initWithObjects:
91                                                          META_MOVIE_IMDB_250_KEY,
92                                                          META_MOVIE_IMDB_RATING_KEY,                                     
93                                                          META_MOVIE_DIRECTOR_KEY,
94                                                          META_MOVIE_CAST_KEY,
95                                                          META_MOVIE_GENRES_KEY,
96                                                          META_EPISODE_AND_SEASON_KEY,
97                                                          META_SEASON_NUMBER_KEY,
98                                                          META_EPISODE_NUMBER_KEY,
99                                                          META_MOVIE_IMDB_STATS_KEY,
100                                                          META_FILE_SIZE_KEY,
101                                                          META_FILE_DURATION_KEY,
102                                                          VIDEO_DESC_LABEL_KEY,
103                                                          VIDEO2_DESC_LABEL_KEY,
104                                                          AUDIO_DESC_LABEL_KEY,
105                                                          AUDIO2_DESC_LABEL_KEY,
106                                                          META_FILE_SUBTITLES_KEY,
107                                                          nil];
108        displayedMetaData = [[NSSet alloc] initWithArray:displayedMetaDataOrder];
109        secondaryFiles = [[NSSet alloc] initWithObjects:
110                                          @"xml",
111                                          @"srt",
112                                          @"sub",
113                                          @"idx",
114                                          @"ass",
115                                          @"ssa",
116                                          nil];
117}
118
119+ (SapphireFileMetaData *)fileWithPath:(NSString *)path inContext:(NSManagedObjectContext *)moc
120{
121        NSPredicate *predicate = [NSPredicate predicateWithFormat:@"path == %@", path];
122        return (SapphireFileMetaData *)doSingleFetchRequest(SapphireFileMetaDataName, moc, predicate);
123}
124
125+ (SapphireFileMetaData *)internalCreateFileWithPath:(NSString *)path parent:(SapphireDirectoryMetaData *)parent inContext:(NSManagedObjectContext *)moc
126{
127        SapphireFileMetaData *ret = [NSEntityDescription insertNewObjectForEntityForName:SapphireFileMetaDataName inManagedObjectContext:moc];
128        ret.parent = parent;
129        ret.path = path;
130        ret.added = [NSDate date];
131       
132        return ret;
133}
134
135+ (SapphireFileMetaData *)createFileWithPath:(NSString *)path inContext:(NSManagedObjectContext *)moc
136{
137        SapphireFileMetaData *ret = [SapphireFileMetaData fileWithPath:path inContext:moc];
138        if(ret != nil)
139                return ret;
140       
141        SapphireDirectoryMetaData *parent = [SapphireDirectoryMetaData createDirectoryWithPath:[path stringByDeletingLastPathComponent] inContext:moc];
142        ret = [SapphireFileMetaData internalCreateFileWithPath:path parent:parent inContext:moc];
143       
144        return ret;
145}
146
147+ (SapphireFileMetaData *)createFileWithPath:(NSString *)path parent:(SapphireDirectoryMetaData *)parent inContext:(NSManagedObjectContext *)moc
148{
149        SapphireFileMetaData *ret = [SapphireFileMetaData fileWithPath:path inContext:moc];
150        if(ret != nil)
151                return ret;
152       
153        return [SapphireFileMetaData internalCreateFileWithPath:path parent:parent inContext:moc];
154}
155
156+ (NSDictionary *)upgradeFilesVersion:(int)version fromContext:(NSManagedObjectContext *)oldMoc toContext:(NSManagedObjectContext *)newMoc withMovies:(NSDictionary *)movieLookup directories:(NSDictionary *)dirLookup
157{
158        NSMutableDictionary *lookup = [NSMutableDictionary dictionary];
159        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
160        NSArray *files = doFetchRequest(SapphireFileMetaDataName, oldMoc, nil);
161        NSEnumerator *fileEnum = [files objectEnumerator];
162        NSManagedObject *oldFile;
163        while((oldFile = [fileEnum nextObject]) != nil)
164        {
165                SapphireFileMetaData *newFile = [NSEntityDescription insertNewObjectForEntityForName:SapphireFileMetaDataName inManagedObjectContext:newMoc];
166                NSString *path = [oldFile valueForKey:@"path"];
167                newFile.path = path;
168                newFile.parent = [dirLookup objectForKey:[path stringByDeletingLastPathComponent]];
169                newFile.audioDescription = [oldFile valueForKey:@"audioDescription"];
170                newFile.audioFormatID = [oldFile valueForKey:@"audioFormatID"];
171                newFile.duration = [oldFile valueForKey:@"duration"];
172                newFile.favorite = [oldFile valueForKey:@"favorite"];
173                newFile.fileClass = [oldFile valueForKey:@"fileClass"];
174                newFile.fileContainerType = [oldFile valueForKey:@"fileContainerType"];
175                newFile.hasVideo = [oldFile valueForKey:@"hasVideo"];
176                newFile.importTypeValue = [[oldFile valueForKey:@"importType"] intValue] & ~ImportTypeMaskXML;
177                newFile.modified = [oldFile valueForKey:@"modified"];
178                newFile.added = [oldFile valueForKey:@"modified"];
179                newFile.resumeTime = [oldFile valueForKey:@"resumeTime"];
180                newFile.sampleRate = [oldFile valueForKey:@"sampleRate"];
181                newFile.size = [oldFile valueForKey:@"size"];
182                newFile.subtitlesDescription = [oldFile valueForKey:@"subtitlesDescription"];
183                newFile.videoDescription = [oldFile valueForKey:@"videoDescription"];
184                newFile.watched = [oldFile valueForKey:@"watched"];
185                NSManagedObject *oldMovie = [oldFile valueForKey:@"movie"];
186                if(oldMovie != nil)
187                {
188                        NSNumber *oldMovieNumber = [oldMovie valueForKey:@"imdbNumber"];
189                        SapphireMovie *movie = nil;
190                        if(oldMovieNumber != nil)
191                                movie = [movieLookup objectForKey:oldMovieNumber];
192                        if(movie == nil)
193                        {
194                                NSString *oldMovieName = [oldMovie valueForKey:@"title"];
195                                if(oldMovieName != nil)
196                                        movie = [movieLookup objectForKey:oldMovieName];
197                        }
198                        newFile.movie = movie;
199                }
200               
201                [lookup setObject:newFile forKey:path];
202        }
203        [pool drain];
204        return lookup;
205}
206
207- (void)insertDictionary:(NSDictionary *)dict withDefer:(NSMutableDictionary *)defer
208{
209        self.audioDescription = [dict objectForKey:META_FILE_AUDIO_DESC_KEY];
210        self.audioFormatID = [dict objectForKey:META_FILE_AUDIO_FORMAT_KEY];
211        self.duration = [dict objectForKey:META_FILE_DURATION_KEY];
212        self.favoriteValue = [[dict objectForKey:META_FILE_FAVORITE_KEY] boolValue];
213        self.fileClass = [dict objectForKey:@"File Class"];
214        self.fileContainerType = [dict objectForKey:@"File Container Type"];
215        id value = [dict objectForKey:META_FILE_MODIFIED_KEY];
216        if(value != nil)
217                self.modified = [NSDate dateWithTimeIntervalSince1970:[value intValue]];
218        self.resumeTimeValue = [[dict objectForKey:META_FILE_RESUME_KEY] unsignedIntValue];
219        self.sampleRate = [dict objectForKey:META_FILE_SAMPLE_RATE_KEY];
220        self.size = [dict objectForKey:META_FILE_SIZE_KEY];
221        self.subtitlesDescription = [dict objectForKey:META_FILE_SUBTITLES_KEY];
222        self.videoDescription = [dict objectForKey:META_FILE_VIDEO_DESC_KEY];
223        self.watchedValue = [[dict objectForKey:META_FILE_WATCHED_KEY] boolValue];
224        self.hasVideoValue = self.videoDescription != nil;
225        value = [dict objectForKey:@"XML Source"];
226        if(value != nil)
227        {
228                NSDictionary *xmlDict = (NSDictionary *)value;
229                SapphireXMLData *xml = self.xmlData;
230                if(xml == nil)
231                {
232                        xml = [NSEntityDescription insertNewObjectForEntityForName:SapphireXMLDataName inManagedObjectContext:[self managedObjectContext]];
233                        self.xmlData = xml;
234                }
235                [xml insertDictionary:xmlDict];
236                xml.modified = [NSDate dateWithTimeIntervalSince1970:[[xmlDict objectForKey:META_FILE_MODIFIED_KEY] intValue]];
237                self.importTypeValue |= ImportTypeMaskXML;
238        }
239        value = [dict objectForKey:@"TVRage Source"];
240        if(value != nil)
241        {
242                SapphireEpisode *ep = [SapphireEpisode episodeWithDictionaries:[NSArray arrayWithObject:value] inContext:[self managedObjectContext]];
243                self.tvEpisode = ep;
244                if(ep != nil)
245                {
246                        self.fileClassValue = FILE_CLASS_TV_SHOW;
247                        NSString *epCoverPath = [[SapphireMetaDataSupport collectionArtPath] stringByAppendingPathComponent:[ep path]];
248                        NSString *oldBasePath = [[epCoverPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[self fileName]];
249                        NSString *oldCoverPath = searchCoverArtExtForPath(oldBasePath);
250                        if(oldCoverPath != nil)
251                        {
252                                NSString *newPath = [epCoverPath stringByAppendingPathExtension:[oldCoverPath pathExtension]];
253                                NSFileManager *fm = [NSFileManager defaultManager];
254                                [fm movePath:oldCoverPath toPath:newPath handler:nil];
255                        }
256                        [(NSMutableDictionary *)[defer objectForKey:@"TV Shows"] setObject:ep.tvShow forKey:[value objectForKey:META_SHOW_IDENTIFIER_KEY]];
257                }
258                self.importTypeValue |= ImportTypeMaskTVShow;
259        }
260        value = [dict objectForKey:@"IMDB Source"];
261        if(value != nil)
262        {
263                SapphireMovie *movie = [SapphireMovie movieWithDictionary:(NSDictionary *)value inContext:[self managedObjectContext] lookup:defer];
264                self.movie = movie;
265                if(movie != nil)
266                {
267                        self.fileClassValue = FILE_CLASS_MOVIE;
268                        NSString *movieCoverPath = [movie coverArtPath];
269                        NSString *oldBasePath = [[[SapphireMetaDataSupport collectionArtPath] stringByAppendingPathComponent:@"@MOVIES"] stringByAppendingPathComponent:[self fileName]];
270                        NSString *oldCoverPath = searchCoverArtExtForPath(oldBasePath);
271                        if(oldCoverPath != nil)
272                        {
273                                NSString *newPath = [movieCoverPath stringByAppendingPathExtension:[oldCoverPath pathExtension]];
274                                NSFileManager *fm = [NSFileManager defaultManager];
275                                [fm movePath:oldCoverPath toPath:newPath handler:nil];
276                        }
277                }
278                self.importTypeValue |= ImportTypeMaskMovie;
279        }
280        NSString *joinPath = [dict objectForKey:META_FILE_JOINED_FILE_KEY];
281        if(joinPath != nil)
282        {
283                NSMutableDictionary *joinDict = [defer objectForKey:@"Join"];
284                NSMutableArray *joinList = [joinDict objectForKey:joinPath];
285                if(joinList == nil)
286                {
287                        joinList = [NSMutableArray array];
288                        [joinDict setObject:joinList forKey:joinPath];
289                }
290                [joinList addObject:self];
291        }
292}
293
294/*Custom TV Episode handler*/
295- (NSComparisonResult) episodeCompare:(SapphireFileMetaData *)other
296{
297        /*Sort by episode*/
298        SapphireEpisode *myEp = self.tvEpisode;
299        SapphireEpisode *theirEp = nil;
300        if([other isKindOfClass:[SapphireFileSymLink class]])
301                theirEp = ((SapphireFileSymLink *)other).file.tvEpisode;
302        else
303                theirEp = other.tvEpisode;
304       
305        if(myEp != nil)
306        {
307                if(theirEp != nil)
308                        return [myEp compare:theirEp];
309                else
310                        return NSOrderedAscending;
311        }
312        else if (theirEp != nil)
313                return NSOrderedDescending;
314
315        return NSOrderedSame;
316}
317
318- (NSComparisonResult) movieCompare:(SapphireFileMetaData *)other
319{
320        SapphireMovie *myMovie = self.movie;
321        SapphireMovie *theirMovie = other.movie;
322       
323        if(myMovie != nil)
324                if(theirMovie != nil)
325                {
326                        NSComparisonResult ret = [myMovie titleCompare:theirMovie];
327                        if(ret == NSOrderedSame)
328                                ret = [myMovie releaseDateCompare:theirMovie];
329                        return ret;
330                }
331                else
332                        return NSOrderedAscending;
333        else if(theirMovie != nil)
334                return NSOrderedDescending;
335       
336        return NSOrderedSame;
337}
338
339- (BOOL) needsUpdating
340{
341        /*Check modified date*/
342        NSString *path = nil;
343        NSFileManager *fm = [NSFileManager defaultManager];
344        if([self fileContainerTypeValue] == FILE_CONTAINER_TYPE_VIDEO_TS)
345        {
346                NSString *vtsPath = [self.path stringByAppendingPathComponent:@"VIDEO_TS"];
347                NSEnumerator *fileEnum = [[fm directoryContentsAtPath:vtsPath] objectEnumerator];
348                NSString *file;
349                while((file = [fileEnum nextObject]) != nil)
350                {
351                        NSString *lowerFile = [file lowercaseString];
352                        if([lowerFile hasSuffix:@".ifo"] && ![[lowerFile lastPathComponent] isEqualToString:@"video_ts.ifo"])
353                        {
354                                path = [vtsPath stringByAppendingPathComponent:file];
355                                break;
356                        }
357                }
358        }
359        else
360                path = self.path;
361        NSDictionary *props = [fm fileAttributesAtPath:path traverseLink:YES];
362        int modTime = [[props objectForKey:NSFileModificationDate] timeIntervalSince1970];
363       
364        if(props == nil)
365        /*No file*/
366                return FALSE;
367       
368        /*Has it been modified since last import?*/
369        if(modTime != [self.modified timeIntervalSince1970])
370                return YES;
371       
372        if(self.hasVideo == nil)
373                return YES;
374       
375        return NO;
376}
377
378- (BOOL)needsImporting
379{
380        NSFileManager *fm = [NSFileManager defaultManager];
381       
382        if(self.joinedToFile != nil)
383                return NO;
384
385        if([self needsUpdating])
386                return YES;
387       
388        //Check XML
389        NSString *xmlFilePath=[[self extensionlessPath] stringByAppendingPathExtension:@"xml"];
390        SapphireXMLData *xml = self.xmlData;
391        NSDictionary *xmlProps = [fm fileAttributesAtPath:xmlFilePath traverseLink:YES];
392       
393        if(xmlProps == nil && xml != nil)
394                //XML file is gone, but we still reference it
395                return YES;
396
397        int modTime = 0;
398        if(xmlProps)
399                modTime = [[xmlProps objectForKey:NSFileModificationDate] timeIntervalSince1970];
400        if(modTime != [self importedTimeFromSource:ImportTypeMaskXML])
401                //XML modification time does not match our last import
402                return YES;
403       
404        //Match imports, but exclude xml and file b/c they are tracked through other means
405        int match = ImportTypeMaskAll & ~ImportTypeMaskFile & ~ImportTypeMaskXML;
406        switch (self.fileClassValue) {
407                case FILE_CLASS_TV_SHOW:
408                        match &= ~ImportTypeMaskMovie;
409                        break;
410                case FILE_CLASS_MOVIE:
411                        match &= ~ImportTypeMaskTVShow;
412                        break;
413                default:
414                        break;
415        }
416       
417        int completed = self.importTypeValue & match;
418        BOOL ret = (match != completed);
419        return ret;
420}
421
422- (oneway void)addFileData:(bycopy NSDictionary *)fileMeta
423{
424        self.audioDescription = [fileMeta objectForKey:META_FILE_AUDIO_DESC_KEY];
425        self.audioFormatID = [fileMeta objectForKey:META_FILE_AUDIO_FORMAT_KEY];
426        self.duration = [fileMeta objectForKey:META_FILE_DURATION_KEY];
427        id value = [fileMeta objectForKey:META_FILE_MODIFIED_KEY];
428        if(value != nil)
429                self.modified = [NSDate dateWithTimeIntervalSince1970:[value intValue]];
430        self.sampleRate = [fileMeta objectForKey:META_FILE_SAMPLE_RATE_KEY];
431        self.size = [fileMeta objectForKey:META_FILE_SIZE_KEY];
432        self.subtitlesDescription = [fileMeta objectForKey:META_FILE_SUBTITLES_KEY];
433        NSString *videoDesc = [fileMeta objectForKey:META_FILE_VIDEO_DESC_KEY];
434        self.videoDescription = videoDesc;
435        if(videoDesc != nil)
436                self.hasVideoValue = YES;
437}
438
439
440NSDictionary *fileMetaData(NSString *path, FileContainerType type)
441{
442        NSMutableDictionary *fileMeta = [NSMutableDictionary dictionary];
443        NSString *modifiedPath = path;
444        NSFileManager *fm = [NSFileManager defaultManager];
445       
446        if(type == FILE_CONTAINER_TYPE_VIDEO_TS)
447        {
448                NSString *vtsPath = [path stringByAppendingPathComponent:@"VIDEO_TS"];
449                NSEnumerator *fileEnum = [[fm directoryContentsAtPath:vtsPath] objectEnumerator];
450                NSString *file;
451                while((file = [fileEnum nextObject]) != nil)
452                {
453                        NSString *lowerFile = [file lowercaseString];
454                        if([lowerFile hasSuffix:@".ifo"] && ![[lowerFile lastPathComponent] isEqualToString:@"video_ts.ifo"])
455                        {
456                                modifiedPath = [vtsPath stringByAppendingPathComponent:file];
457                                break;
458                        }
459                }
460        }
461        NSDictionary *props = [fm fileAttributesAtPath:modifiedPath traverseLink:YES];
462        int modTime = [[props objectForKey:NSFileModificationDate] timeIntervalSince1970];
463        /*Set modified, size, and version*/
464        [fileMeta setObject:[NSNumber numberWithInt:modTime] forKey:META_FILE_MODIFIED_KEY];
465        [fileMeta setObject:[props objectForKey:NSFileSize] forKey:META_FILE_SIZE_KEY];
466       
467        if(type == FILE_CONTAINER_TYPE_QT_MOVIE)
468        {
469                /*Open the movie*/
470                NSError *error = nil;
471                QTMovie *movie = [QTMovie movieWithFile:path error:&error];
472                QTTime duration = [movie duration];
473                if(movie == nil)
474                        duration.timeValue = 0;
475                [fileMeta setObject:[NSNumber numberWithFloat:(float)duration.timeValue/(float)duration.timeScale] forKey:META_FILE_DURATION_KEY];
476                NSArray *audioTracks = [movie tracksOfMediaType:@"soun"];
477                NSNumber *audioSampleRate = nil;
478                int trackCount = [audioTracks count];
479                int i;
480                BOOL foundAC3 = NO;
481                for(i=0; i<trackCount; i++)
482                {
483                        /*Get the audio track*/
484                        QTTrack *track = [audioTracks objectAtIndex:i];
485                        QTMedia *media = [track media];
486                        if(media != nil)
487                        {
488                                /*Get the audio format*/
489                                Media qtMedia = [media quickTimeMedia];
490                                Handle sampleDesc = NewHandle(1);
491                                GetMediaSampleDescription(qtMedia, 1, (SampleDescriptionHandle)sampleDesc);
492                                AudioStreamBasicDescription asbd;
493                                ByteCount       propSize = 0;
494                                QTSoundDescriptionGetProperty((SoundDescriptionHandle)sampleDesc, kQTPropertyClass_SoundDescription, kQTSoundDescriptionPropertyID_AudioStreamBasicDescription, sizeof(asbd), &asbd, &propSize);
495                               
496                                if(propSize != 0 && !foundAC3)
497                                {
498                                        /*Set the format and sample rate*/
499                                        NSNumber *format = [NSNumber numberWithUnsignedInt:asbd.mFormatID];
500                                        [fileMeta setObject:format forKey:META_FILE_AUDIO_FORMAT_KEY];
501                                        audioSampleRate = [NSNumber numberWithDouble:asbd.mSampleRate];
502                                }
503                               
504                                CFStringRef userText = nil;
505                                propSize = 0;
506                                QTSoundDescriptionGetProperty((SoundDescriptionHandle)sampleDesc, kQTPropertyClass_SoundDescription, kQTSoundDescriptionPropertyID_UserReadableText, sizeof(userText), &userText, &propSize);
507                                if(userText != nil)
508                                {
509                                        if([(NSString *)userText hasPrefix:@"AC3"])
510                                                foundAC3 = YES;
511                                        /*Set the description*/
512                                        NSString *prevDesc = [fileMeta objectForKey:META_FILE_AUDIO_DESC_KEY];
513                                        NSString *newDesc;
514                                        if(prevDesc != nil)
515                                                newDesc = [prevDesc stringByAppendingFormat:@"\n%@", userText];
516                                        else
517                                                newDesc = (NSString *)userText;
518                                        [fileMeta setObject:newDesc forKey:META_FILE_AUDIO_DESC_KEY];
519                                        CFRelease(userText);
520                                }
521                                DisposeHandle(sampleDesc);
522                        }
523                }
524                /*Set the sample rate*/
525                if(audioSampleRate != nil)
526                        [fileMeta setObject:audioSampleRate forKey:META_FILE_SAMPLE_RATE_KEY];
527                NSArray *videoTracks = [movie tracksOfMediaType:@"vide"];
528                trackCount = [videoTracks count];
529                for(i=0; i<trackCount; i++)
530                {
531                        /*Get the video track*/
532                        QTTrack *track = [videoTracks objectAtIndex:i];
533                        QTMedia *media = [track media];
534                        if(media != nil)
535                        {
536                                /*Get the video description*/
537                                Media qtMedia = [media quickTimeMedia];
538                                Handle sampleDesc = NewHandle(1);
539                                GetMediaSampleDescription(qtMedia, 1, (SampleDescriptionHandle)sampleDesc);
540                                CFStringRef userText = nil;
541                                ByteCount propSize = 0;
542                                ICMImageDescriptionGetProperty((ImageDescriptionHandle)sampleDesc, kQTPropertyClass_ImageDescription, kICMImageDescriptionPropertyID_SummaryString, sizeof(userText), &userText, &propSize);
543                                DisposeHandle(sampleDesc);
544                               
545                                if(userText != nil)
546                                {
547                                        /*Set the description*/
548                                        NSString *prevDesc = [fileMeta objectForKey:META_FILE_VIDEO_DESC_KEY];
549                                        NSString *newDesc;
550                                        if(prevDesc != nil)
551                                                newDesc = [prevDesc stringByAppendingFormat:@"\n%@", userText];
552                                        else
553                                                newDesc = (NSString *)userText;
554                                        [fileMeta setObject:newDesc forKey:META_FILE_VIDEO_DESC_KEY];
555                                        CFRelease(userText);
556                                }
557                        }
558                }
559        } //QTMovie
560        else if(type == FILE_CONTAINER_TYPE_VIDEO_TS)
561        {
562                SapphireVideoTsParser *dvd = [[SapphireVideoTsParser alloc] initWithPath:path];
563                id description = [dvd videoFormatsString];
564                if(description)
565                        [fileMeta setObject:description forKey:META_FILE_VIDEO_DESC_KEY];
566                description = [dvd audioFormatsString];
567                if(description)
568                        [fileMeta setObject:description forKey:META_FILE_AUDIO_DESC_KEY];
569                description = [dvd mainFeatureDuration];
570                if(description)
571                        [fileMeta setObject:description forKey:META_FILE_DURATION_KEY];
572                description = [dvd totalSize];
573                if(description)
574                        [fileMeta setObject:description forKey:META_FILE_SIZE_KEY];
575                description = [dvd subtitlesString];
576                if(description)
577                        [fileMeta setObject:description forKey:META_FILE_SUBTITLES_KEY ];
578               
579                [dvd release];
580        } // VIDEO_TS
581        return fileMeta;
582}
583
584- (BOOL)updateMetaData
585{
586        if([self needsUpdating])
587        {
588                NSDictionary *fileMeta = fileMetaData([self path], [self fileContainerTypeValue]);
589                [self addFileData:fileMeta];
590                return YES;
591        }
592        return NO;
593}
594
595NSString *sizeStringForSize(float size)
596{
597        if(size == 0)
598                return @"-";
599       
600        /*The letter for magnitude*/
601        char letter = ' ';
602        if(size >= 1024000)
603        {
604                if(size >= 1024*1024000)
605                {
606                        /*GB*/
607                        size /= 1024 * 1024 * 1024;
608                        letter = 'G';
609                }
610                else
611                {
612                        /*MB*/
613                        size /= 1024 * 1024;
614                        letter = 'M';
615                }
616        }
617        else if (size >= 1000)
618        {
619                /*KB*/
620                size /= 1024;
621                letter = 'K';
622        }
623        return [NSString stringWithFormat:@"%.1f%cB", size, letter];   
624}
625
626- (NSString *)sizeString
627{
628        /*Get size*/
629        float size = [self sizeValue];
630        NSString *sizeStr = sizeStringForSize(size);
631        SapphireJoinedFile *joined = self.joinedFile;
632        if(joined != nil)
633        {
634                NSEnumerator *joinedEnum = [joined.joinedFilesSet objectEnumerator];
635                SapphireFileMetaData *file;
636                while((file = [joinedEnum nextObject]) != nil)
637                {
638                        size += file.sizeValue;
639                }
640                if(size != 0)
641                        sizeStr = [sizeStr stringByAppendingFormat:@"/%@", sizeStringForSize(size)];
642        }
643        return sizeStr;
644}
645
646- (void)setToReimportFromMask:(NSNumber *)mask
647{
648        [self setToReimportFromMaskValue:[mask intValue]];
649}
650
651- (void)setToReimportFromMaskValue:(int)mask
652{
653        int currentMask = self.importTypeValue;
654        self.importTypeValue = currentMask & ~mask;
655        if(mask & ImportTypeMaskMovie)
656        {
657                SapphireMovie *movie = self.movie;
658                self.movie = nil;
659                if(movie != nil && [movie.filesSet count] == 0)
660                        [[self managedObjectContext] deleteObject:movie];
661        }
662        if(mask & ImportTypeMaskTVShow)
663        {
664                SapphireEpisode *ep = self.tvEpisode;
665                self.tvEpisode = nil;
666                if(ep != nil && [ep.filesSet count] == 0)
667                        [[self managedObjectContext] deleteObject:ep];
668        }
669}
670
671- (void)setToResetImportDecisions
672{
673        NSManagedObjectContext *moc = [self managedObjectContext];
674        NSString *lowerFileName = [[self fileName] lowercaseString];
675
676        SapphireEpisode *ep = self.tvEpisode;
677        if(ep != nil)
678        {
679                NSSet *translations = ep.tvShow.translationsSet;
680                SapphireTVTranslation *tran;
681                NSEnumerator *tranEnum = [translations objectEnumerator];
682                while((tran = [tranEnum nextObject]) != nil)
683                {
684                        if([lowerFileName hasPrefix:tran.name])
685                        {
686                                SapphireLog(SapphireLogTypeMetadataStore, SapphireLogLevelDetail, @"Deleting TV import translation for %@", tran.name);
687                                [moc deleteObject:tran];
688                        }
689                }
690        }
691        NSString *lookupName;
692        if([[SapphireSettings sharedSettings] dirLookup])
693                lookupName = [[[self.path stringByDeletingLastPathComponent] lastPathComponent] lowercaseString];
694        else
695                lookupName = lowerFileName;
696        SapphireMovieTranslation *movieTran = [SapphireMovieTranslation movieTranslationWithName:lookupName inContext:moc];
697        if(movieTran != nil)
698        {
699                SapphireLog(SapphireLogTypeMetadataStore, SapphireLogLevelDetail, @"Deleting Movie import translation for %@", movieTran.name);
700                [moc deleteObject:movieTran];
701        }
702        NSScanner *titleYearScanner = [NSScanner scannerWithString:lookupName];
703        NSString *normalTitle = nil;
704        int year = 0;
705        BOOL success = YES;
706        success &= [titleYearScanner scanUpToString:@"(" intoString:&normalTitle];
707        success &= [titleYearScanner scanString:@"(" intoString:nil];
708        success &= [titleYearScanner scanInt:&year];
709        success &= [titleYearScanner scanString:@")" intoString:nil];
710       
711        if(success)
712        {
713                if([normalTitle hasSuffix:@" "])
714                        normalTitle = [normalTitle substringToIndex:[normalTitle length]-1];
715                movieTran = [SapphireMovieTranslation movieTranslationWithName:normalTitle inContext:moc];
716                if(movieTran != nil)
717                {
718                        SapphireLog(SapphireLogTypeMetadataStore, SapphireLogLevelDetail, @"Deleting Movie import translation for %@", movieTran.name);
719                        [moc deleteObject:movieTran];
720                }
721        }       
722       
723        [self setToReimportFromMaskValue:ImportTypeMaskAll];
724}
725
726- (void)clearMetaData
727{
728        self.audioDescription = nil;
729        self.audioFormatID = nil;
730        self.duration = nil;
731        self.favoriteValue = 0;
732        self.fileClassValue = nil;
733        self.fileContainerType = nil;
734        self.hasVideo = nil;
735        self.importTypeValue = 0;
736        self.modified = nil;
737        self.resumeTime = nil;
738        self.sampleRate = nil;
739        self.size = nil;
740        self.subtitlesDescription = nil;
741        self.videoDescription = nil;
742        self.watchedValue = 0;
743        self.movie = nil;
744        self.tvEpisode = nil;
745        if(self.xmlData != nil)
746        {
747                [[self managedObjectContext] deleteObject:self.xmlData];
748        }
749}
750
751- (NSString *)coverArtPath
752{
753        /*Find cover art for the current file in the "Cover Art" dir */
754        NSString *subPath = [self extensionlessPath];
755       
756        NSString *fileName = [subPath lastPathComponent];
757        NSString * myArtPath=nil;
758       
759        if([self fileClassValue]==FILE_CLASS_TV_SHOW)
760                myArtPath=[[self tvEpisode] coverArtPath];
761        if([self fileClassValue]==FILE_CLASS_MOVIE)
762                myArtPath=[[self movie] coverArtPath];
763       
764        /* Check the Collection Art location */
765        NSString *ret=searchCoverArtExtForPath(myArtPath);
766       
767        if(ret != nil)
768                return ret;
769       
770        /* Try Legacy Folders with the file */
771        ret=searchCoverArtExtForPath([[[subPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"Cover Art"] stringByAppendingPathComponent:fileName]);
772       
773        if(ret != nil)
774                return ret;
775       
776        /*Find cover art for the current file in the current dir*/
777        ret = searchCoverArtExtForPath(subPath);
778       
779        if(ret != nil)
780                return ret;
781       
782       
783        return nil;
784}
785
786- (NSString *)durationString
787{
788        /*Create duration string*/
789        return [NSString colonSeparatedTimeStringForSeconds:[self durationValue]];
790}
791
792static BOOL moving = NO;
793static BOOL moveSuccess = NO;
794static NSString *movingFromPath = @"From";
795static NSString *movingToPath = @"To";
796
797- (void)threadedMove:(NSDictionary *)pathInfo
798{
799        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
800        moveSuccess = [[NSFileManager defaultManager] movePath:[pathInfo objectForKey:movingFromPath] toPath:[pathInfo objectForKey:movingToPath] handler:nil];
801        moving = NO;
802        [pool drain];
803}
804
805- (NSString *)moveToPath:(NSString *)newPath pathForMoveError:(NSString *)errorPath inDir:(SapphireDirectoryMetaData *)newParent
806{
807        NSString *oldPath = [[[self path] retain] autorelease];
808        NSString *extLessPath = [self extensionlessPath];
809        NSFileManager *fm = [NSFileManager defaultManager];
810        if([fm fileExistsAtPath:newPath])
811                return [NSString stringWithFormat:BRLocalizedString(@"The name %@ is already taken", @"Name taken on a file/directory rename; parameter is name"), [newPath lastPathComponent]];
812        if(newParent != nil)
813        {
814                moving = YES;
815                [NSThread detachNewThreadSelector:@selector(threadedMove:) toTarget:self withObject:[NSDictionary dictionaryWithObjectsAndKeys:
816                                                                                                                                                                                         oldPath, movingFromPath,
817                                                                                                                                                                                         newPath, movingToPath,
818                                                                                                                                                                                         nil]];
819                while(moving)
820                        [[NSRunLoop currentRunLoop] runUntilDate:[[NSDate date] addTimeInterval:1]];           
821        }
822        else
823                moveSuccess = [fm movePath:oldPath toPath:newPath handler:nil];
824        NSLog(@"Move %d", moveSuccess);
825        if(!moveSuccess)
826                return [NSString stringWithFormat:BRLocalizedString(@"Could not move %@.  Is the filesystem read-only?", @"Unknown error renaming file/directory; parameter is name"), errorPath];
827        [self setPath:newPath];
828        NSLog(@"path set to %@", newPath);
829        if(newParent != nil)
830        {
831                SapphireDirectoryMetaData *oldParent = self.parent;
832                self.parent = newParent;
833                [oldParent clearPredicateCache];
834                [newParent clearPredicateCache];
835        }
836        NSLog(@"new parent set");
837        [SapphireMetaDataSupport save:[self managedObjectContext]];
838        NSLog(@"Save done");
839        NSEnumerator *secondaryExtEnum = [secondaryFiles objectEnumerator];
840        NSString *extension;
841        NSString *newExtlessPath = newPath;
842        if(self.fileContainerTypeValue != FILE_CONTAINER_TYPE_VIDEO_TS)
843                newExtlessPath = [newExtlessPath stringByDeletingPathExtension];
844       
845        while((extension = [secondaryExtEnum nextObject]) != nil)
846        {
847                NSString *secondaryPath = [extLessPath stringByAppendingPathExtension:extension];
848                if([fm fileExistsAtPath:secondaryPath])
849                {
850                        NSString *newSecondaryPath = [newExtlessPath stringByAppendingPathExtension:extension];
851                        if(newParent != nil)
852                        {
853                                moving = YES;
854                                [NSThread detachNewThreadSelector:@selector(threadedMove:) toTarget:self withObject:[NSDictionary dictionaryWithObjectsAndKeys:
855                                                                                                                                                                                                         secondaryPath, movingFromPath,
856                                                                                                                                                                                                         newSecondaryPath, movingToPath,
857                                                                                                                                                                                                         nil]];
858                                while(moving)
859                                        [[NSRunLoop currentRunLoop] runUntilDate:[[NSDate date] addTimeInterval:1]];           
860                        }
861                        else
862                                moveSuccess = [fm movePath:secondaryPath toPath:newSecondaryPath handler:nil];                 
863                        if(!moveSuccess)
864                                return [NSString stringWithFormat:BRLocalizedString(@"Could not move %@ file for %@.  Is the filesystem read-only?", @"Unknown error renaming file/directory; parameter is extension, name"), extension, errorPath];
865                }
866        }
867        NSLog(@"Secondary files done");
868        NSString *coverArtPath = searchCoverArtExtForPath(extLessPath);
869        if(coverArtPath != nil)
870        {
871                NSString *newCoverArtPath = [newExtlessPath stringByAppendingPathExtension:[coverArtPath pathExtension]];
872                if(newParent != nil)
873                {
874                        moving = YES;
875                        [NSThread detachNewThreadSelector:@selector(threadedMove:) toTarget:self withObject:[NSDictionary dictionaryWithObjectsAndKeys:
876                                                                                                                                                                                                 coverArtPath, movingFromPath,
877                                                                                                                                                                                                 newCoverArtPath, movingToPath,
878                                                                                                                                                                                                 nil]];
879                        while(moving)
880                                [[NSRunLoop currentRunLoop] runUntilDate:[[NSDate date] addTimeInterval:1]];           
881                }
882                else
883                        moveSuccess = [fm movePath:coverArtPath toPath:newCoverArtPath handler:nil];
884                if(!moveSuccess)
885                        return [NSString stringWithFormat:BRLocalizedString(@"Could not move cover art for %@.  Is the filesystem read-only?", @"Unknown error renaming file/directory; parameter is name"), errorPath];
886        }
887        NSLog(@"Covert art done");
888        return nil;
889}
890
891- (NSString *)moveToDir:(SapphireDirectoryMetaData *)dir
892{
893        NSString *destination = [dir path];
894        NSString *newPath = [destination stringByAppendingPathComponent:[[self path] lastPathComponent]];
895        return [self moveToPath:newPath pathForMoveError:[newPath lastPathComponent] inDir:dir];
896}
897
898- (NSString *)rename:(NSString *)newFilename
899{
900        int componentCount = [[newFilename pathComponents] count];
901        if(componentCount != 1)
902                return BRLocalizedString(@"A File name should not contain any '/' characters", @"Error indicating that filenames cannot contain / characters");
903        NSString *oldPath = [self path];
904        if(self.fileContainerTypeValue != FILE_CONTAINER_TYPE_VIDEO_TS)
905                newFilename = [newFilename stringByAppendingPathExtension:[oldPath pathExtension]];
906        NSString *newPath = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newFilename];
907        if([oldPath isEqualToString:newPath])
908                return nil;
909        NSLog(@"Going to move path %@ to %@ with error %@", [self path], newPath, newFilename);
910        return [self moveToPath:newPath pathForMoveError:newFilename inDir:nil];
911}
912
913- (NSString *)prettyName
914{
915        if(self.tvEpisode != nil)
916        {
917                //TV Episode
918                SapphireEpisode *ep = self.tvEpisode;
919                NSString *tvShowName = ep.tvShow.name;
920                NSString *epName = [ep episodeTitle];
921                int season = ep.season.seasonNumberValue;
922                int firstEp = [ep episodeNumberValue];
923                int lastEp = [ep lastEpisodeNumberValue];
924               
925                NSString *SEString;
926                if(firstEp == 0)
927                        //Single Special Episode
928                        SEString = [NSString stringWithFormat:@"S%02dES1", season];
929                else if(lastEp == firstEp)
930                        //Single normal episode
931                        SEString = [NSString stringWithFormat:@"S%02dE%02d", season, firstEp];
932                else
933                        //Double episode
934                        SEString = [NSString stringWithFormat:@"S%02dE%02d-E%02d", season, firstEp, lastEp];
935               
936                return [NSString stringWithFormat:@"%@ %@ %@", tvShowName, SEString, epName];
937        }
938        else if(self.movie != nil)
939        {
940                //Movie
941                NSDate *releaseDate = [self.movie releaseDate];
942                NSCalendarDate *releaseCalDate = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate:[releaseDate timeIntervalSinceReferenceDate]];
943                return [NSString stringWithFormat:@"%@ (%d)", [self.movie title], [releaseCalDate yearOfCommonEra]];
944        }
945        return nil;
946}
947
948- (NSString *)renameToPrettyName
949{
950        NSString *prettyName = [self prettyName];
951        if(prettyName == nil)
952        {
953                return BRLocalizedString(@"No pretty name to construct", @"Error indicating there is no pretty name for this file");
954        }
955       
956        NSMutableString *mutStr = [prettyName mutableCopy];
957        [mutStr replaceOccurrencesOfString:@"/" withString:@"-" options:0 range:NSMakeRange(0, [mutStr length])];
958        NSLog(@"Going to rename %@ to %@", [self path], mutStr);
959       
960        return [self rename:[mutStr autorelease]];
961}
962
963- (NSString *)autoSortPath
964{
965        SapphireEpisode *episode = self.tvEpisode;
966        return [episode.season autoSortPath];
967}
968
969- (NSString *)moveToAutoSortName
970{
971        NSString *error = nil;
972        if(![[self fileName] isEqualToString:[self prettyName]])
973                error = [self renameToPrettyName];
974       
975        if(error != nil)
976                return error;
977       
978        NSString *autoSortSeasonPath = [self autoSortPath];
979        if(autoSortSeasonPath == nil)
980                return nil;
981       
982        NSString *autoSortShowPath = [autoSortSeasonPath stringByDeletingLastPathComponent];
983       
984        NSFileManager *fm = [NSFileManager defaultManager];
985        if(![fm isDirectory:autoSortShowPath])
986                return [NSString stringWithFormat:BRLocalizedString(@"Auto sort path %@ is not a directory", @"Error indicating auto sort path isn't a directory"), autoSortShowPath];
987       
988        if(![fm constructPath:autoSortSeasonPath])
989                return [NSString stringWithFormat:BRLocalizedString(@"Cannot create directory at path %@", @"Error indicating auto sort path can't be created"), autoSortSeasonPath];
990       
991        SapphireDirectoryMetaData *dirMeta = [SapphireDirectoryMetaData createDirectoryWithPath:autoSortSeasonPath inContext:[self managedObjectContext]];
992        return [self moveToDir:dirMeta];
993}
994
995- (NSString *)fileName
996{
997        NSString *ret = [self.path lastPathComponent];
998        if(self.fileContainerTypeValue != FILE_CONTAINER_TYPE_VIDEO_TS)
999                ret = [ret stringByDeletingPathExtension];
1000       
1001        return ret;
1002}
1003
1004- (NSString *)extensionlessPath
1005{
1006        NSString *ret = self.path;
1007        if(self.fileContainerTypeValue != FILE_CONTAINER_TYPE_VIDEO_TS)
1008                ret = [ret stringByDeletingPathExtension];
1009       
1010        return ret;
1011}
1012
1013- (NSMutableDictionary *)getDisplayedMetaDataInOrder:(NSArray * *)order;
1014{
1015        NSString *name = [[self path] lastPathComponent];
1016        NSString *durationStr = [self durationString];
1017        /*Set the order*/
1018        if(order != nil)
1019                *order = displayedMetaDataOrder;
1020       
1021        NSMutableDictionary *ret = [[NSMutableDictionary alloc] init];
1022       
1023        SapphireMovie *movie = [self movie];
1024        SapphireEpisode *ep = [self tvEpisode];
1025        if(movie != nil)
1026        {
1027                [movie insertDisplayMetaData:ret];
1028        }
1029        else if (ep != nil)
1030        {
1031                [ep insertDisplayMetaData:ret];
1032        }
1033       
1034        id value = [self videoDescription];
1035        if(value != nil)
1036        {
1037                NSString *valueString = (NSString *)value;
1038                NSMutableArray *valueArray = [[valueString componentsSeparatedByString:@"\n"] mutableCopy];
1039                [valueArray uniqueObjects];
1040                int count = [valueArray count];
1041                if(count > 1)
1042                {
1043                        NSString *first = nil;
1044                        int i;
1045                        for(i=0; i<count; i++)
1046                        {
1047                                NSString *trackName = [valueArray objectAtIndex:i];
1048                                if(![trackName hasPrefix:@"VobSub"])
1049                                {
1050                                        first = [[trackName retain] autorelease];
1051                                        [valueArray removeObjectAtIndex:i];
1052                                        break;
1053                                }
1054                        }
1055                        if(first == nil)
1056                        {
1057                                first = [[[valueArray objectAtIndex:0] retain] autorelease];
1058                                [valueArray removeObjectAtIndex:0];
1059                        }
1060                        [ret setObject:first forKey:VIDEO_DESC_LABEL_KEY];
1061                        [ret setObject:[valueArray componentsJoinedByString:@"\n"] forKey:VIDEO2_DESC_LABEL_KEY];
1062                }
1063                else
1064                        [ret setObject:value forKey:VIDEO_DESC_LABEL_KEY];
1065                [valueArray release];
1066        }
1067        value = [self audioDescription];
1068        if(value != nil)
1069        {
1070                NSString *valueString = (NSString *)value;
1071                NSMutableArray *valueArray = [[valueString componentsSeparatedByString:@"\n"] mutableCopy];
1072                [valueArray uniqueObjects];
1073                if([valueArray count] > 1)
1074                {
1075                        [ret setObject:[valueArray objectAtIndex:0] forKey:AUDIO_DESC_LABEL_KEY];
1076                        [valueArray removeObjectAtIndex:0];
1077                        [ret setObject:[valueArray componentsJoinedByString:@"\n"] forKey:AUDIO2_DESC_LABEL_KEY];
1078                }
1079                else
1080                        [ret setObject:value forKey:AUDIO_DESC_LABEL_KEY];
1081                [valueArray release];
1082        }
1083        value = [self subtitlesDescription];
1084        if(value != nil)
1085                [ret setObject:value forKey:META_FILE_SUBTITLES_KEY];
1086        if([self durationValue])
1087        {
1088                if([self sizeValue])
1089                {
1090                        int resumeTime = [self resumeTimeValue];
1091                        NSString *fullDurationString = [NSString stringWithFormat:@"%@ (%@)", durationStr, [self sizeString]];
1092                        if(resumeTime != 0)
1093                                fullDurationString = [fullDurationString stringByAppendingFormat:BRLocalizedString(@" %@ Remaining", @"Time left to display in preview pane next to file runtime"), [NSString colonSeparatedTimeStringForSeconds:[self durationValue] - resumeTime]];
1094                        [ret setObject:fullDurationString forKey:META_FILE_DURATION_KEY];
1095                }
1096                else
1097                        [ret setObject:durationStr forKey:META_FILE_DURATION_KEY];
1098        }
1099        else
1100                [ret setObject:[self sizeString] forKey:META_FILE_SIZE_KEY];
1101       
1102        /*Set the title*/
1103        if([ret objectForKey:META_TITLE_KEY] == nil)
1104                [ret setObject:name forKey:META_TITLE_KEY];
1105        return [ret autorelease];
1106}
1107
1108- (NSString *)searchShowName
1109{
1110        return self.xmlData.searchShowName;
1111}
1112
1113- (int)searchSeasonNumber
1114{
1115        NSNumber *value = self.xmlData.searchSeasonNumber;
1116        if(value != nil)
1117                return [value intValue];
1118        return -1;
1119}
1120
1121- (int)searchEpisodeNumber
1122{
1123        NSNumber *value = self.xmlData.searchEpisode;
1124        if(value != nil)
1125                return [value intValue];
1126        return -1;
1127}
1128
1129- (int)searchLastEpisodeNumber
1130{
1131        NSNumber *value = self.xmlData.searchLastEpisodeNumber;
1132        if(value != nil)
1133                return [value intValue];
1134        return -1;
1135}
1136
1137- (int)searchIMDBNumber
1138{
1139        NSNumber *value = self.xmlData.searchIMDBNumber;
1140        if(value != nil)
1141                return [value intValue];
1142        return -1;
1143}
1144
1145- (FileContainerType)fileContainerTypeValue
1146{
1147        return super.fileContainerTypeValue;
1148}
1149
1150- (ImportTypeMask)importTypeValue
1151{
1152        return super.importTypeValue;
1153}
1154
1155- (long)importedTimeFromSource:(int)source
1156{
1157        if(source == ImportTypeMaskFile)
1158                return [self.modified timeIntervalSince1970];
1159        else if(source == ImportTypeMaskXML)
1160                return [self.xmlData.modified timeIntervalSince1970];
1161        return 0;
1162}
1163
1164- (void)didImportType:(ImportTypeMask)type
1165{
1166        self.importTypeValue |= type;
1167}
1168
1169- (void)setMovie:(SapphireMovie *)movie
1170{
1171        SapphireMovie *oldMovie = self.movie;
1172        super.movie = movie;
1173        if([self isDeleted])
1174                return;
1175        if(movie != nil)
1176        {
1177                [self setFileClassValue:FILE_CLASS_MOVIE];
1178                self.importTypeValue |= ImportTypeMaskMovie;
1179        }
1180        if(movie != oldMovie)
1181                self.xmlData.movie = movie;
1182}
1183
1184- (void)setTvEpisode:(SapphireEpisode *)ep
1185{
1186        SapphireEpisode *oldEp = self.tvEpisode;
1187        super.tvEpisode = ep;
1188        if([self isDeleted])
1189                return;
1190        if(ep != nil)
1191        {
1192                [self setFileClassValue:FILE_CLASS_TV_SHOW];
1193                self.importTypeValue |= ImportTypeMaskTVShow;
1194        }
1195        if(ep != oldEp)
1196                self.xmlData.episode = ep;
1197}
1198
1199- (void)setXmlData:(SapphireXMLData *)data
1200{
1201        super.xmlData = data;
1202        if([self isDeleted])
1203                return;
1204        if(data != nil)
1205        {
1206                data.episode = self.tvEpisode;
1207                data.movie = self.movie;
1208        }
1209}
1210
1211- (FileClass)fileClassValue
1212{
1213        FileClass xmlClass = self.xmlData.fileClassValue;
1214        if(xmlClass != FILE_CLASS_UNKNOWN)
1215                return xmlClass;
1216        return super.fileClassValue;
1217}
1218
1219- (void)setFileClassValue:(FileClass)fileClass
1220{
1221        FileClass xmlClass = self.xmlData.fileClassValue;
1222        if(xmlClass != FILE_CLASS_UNKNOWN)
1223                self.xmlData.fileClassValue = FILE_CLASS_UNKNOWN;
1224        super.fileClassValue = fileClass;
1225}
1226
1227- (void)setWatched:(NSNumber*)value_ {
1228        NSNumber *oldValue = [self.watched retain];
1229        super.watched = value_;
1230        if(![oldValue isEqualToNumber:value_])
1231        {
1232                self.resumeTime = nil;
1233                [self.parent clearPredicateCache];
1234                [self.tvEpisode clearPredicateCache];
1235                [self.movie clearPredicateCache];
1236        }
1237}
1238
1239- (void)setFavorite:(NSNumber*)value_ {
1240        NSNumber *oldValue = [self.favorite retain];
1241        super.favorite = value_;
1242        if(![oldValue isEqualToNumber:value_])
1243        {
1244                [self.parent clearPredicateCache];
1245                [self.tvEpisode clearPredicateCache];
1246                [self.movie clearPredicateCache];
1247        }
1248}
1249
1250@end
Note: See TracBrowser for help on using the repository browser.