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

Revision 1267, 38.8 KB checked in by gbooker, 5 years ago (diff)

Since the helper only imports file data now, it doesn't need to know anything about managed objects. It no longer works with a moc, which caused (relatively) rare issues.

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