source: branches/V3Schema/SapphireFrappliance/MetaData/SapphireMObjects/SapphireFileMetaData.m @ 1230

Revision 1230, 39.7 KB checked in by gbooker, 4 years ago (diff)

Check both translations; both with and without the year in them, when importing or resetting import.

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