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

Revision 834, 32.0 KB checked in by gbooker, 5 years ago (diff)

Don't do further processing on a deleted object (solves a CD issue).

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 <QTKit/QTKit.h>
19
20@implementation SapphireFileMetaData
21
22//ATV Extra Info;
23NSString *META_SHOW_BROADCASTER_KEY =           @"Broadcast Company";
24NSString *META_SHOW_PUBLISHED_DATE_KEY =        @"Published Date";
25NSString *META_SHOW_AQUIRED_DATE =                      @"Date Aquired";
26NSString *META_SHOW_RATING_KEY =                        @"Rating";
27NSString *META_SHOW_FAVORITE_RATING_KEY =       @"User Rating";
28NSString *META_COPYRIGHT_KEY =                          @"Copyright";
29
30//IMDB Type Info;
31NSString *META_MOVIE_TITLE_KEY =                                @"Title";
32NSString *META_MOVIE_CAST_KEY =                                 @"Cast";
33NSString *META_MOVIE_RELEASE_DATE_KEY =                 @"Release Date";
34NSString *META_MOVIE_DIRECTOR_KEY =                             @"Director";
35NSString *META_MOVIE_WIRTERS_KEY =                              @"Writers";
36NSString *META_MOVIE_GENRES_KEY =                               @"Genres";
37NSString *META_MOVIE_PLOT_KEY =                                 @"Plot";
38NSString *META_MOVIE_IMDB_RATING_KEY =                  @"IMDB Rating";
39NSString *META_MOVIE_IMDB_250_KEY =                             @"IMDB Top 250";
40NSString *META_MOVIE_MPAA_RATING_KEY =                  @"MPAA Rating";
41NSString *META_MOVIE_OSCAR_KEY =                                @"Oscars";
42NSString *META_MOVIE_IDENTIFIER_KEY =                   @"Movie ID";
43
44//TV Show Specific Keys;
45NSString *META_TITLE_KEY =                                      @"Title";
46NSString *META_SEASON_NUMBER_KEY =                      @"Season";
47NSString *META_EPISODE_NUMBER_KEY =                     @"Episode";
48NSString *META_SHOW_NAME_KEY =                          @"Show Name";
49NSString *META_DESCRIPTION_KEY =                        @"Show Description";
50NSString *META_SHOW_AIR_DATE =                          @"Air Date";
51NSString *META_RATING_KEY =                                     @"Rating";
52NSString *META_SUMMARY_KEY =                            @"Summary";
53NSString *META_ABSOLUTE_EP_NUMBER_KEY =         @"Episode Number";
54NSString *META_SHOW_IDENTIFIER_KEY =            @"Show ID";
55NSString *META_EPISODE_2_NUMBER_KEY =           @"Episode 2";
56NSString *META_ABSOLUTE_EP_2_NUMBER_KEY =       @"Episode Number 2";
57NSString *META_SEARCH_SEASON_NUMBER_KEY =       @"Search Season";
58NSString *META_SEARCH_EPISODE_NUMBER_KEY =      @"Search Episode";
59NSString *META_SEARCH_EPISODE_2_NUMBER_KEY =    @"Search Episode 2";
60NSString *META_SEARCH_IMDB_NUMBER_KEY =         @"Search IMDB Number";
61NSString *FILE_CLASS_KEY =                                      @"File Class";
62
63//File Specific Keys
64#define MODIFIED_KEY                            @"Modified"
65#define WATCHED_KEY                                     @"Watched"
66#define FAVORITE_KEY                            @"Favorite"
67#define RESUME_KEY                                      @"Resume Time"
68#define SIZE_KEY                                        @"Size"
69#define DURATION_KEY                            @"Duration"
70#define AUDIO_DESC_KEY                          @"Audio Description"
71#define SAMPLE_RATE_KEY                         @"Sample Rate"
72#define VIDEO_DESC_KEY                          @"Video Description"
73#define AUDIO_FORMAT_KEY                        @"Audio Format"
74#define SUBTITLES_KEY                           @"Subtitles"
75#define JOINED_FILE_KEY                         @"Joined File"
76
77static NSSet *displayedMetaData;
78static NSArray *displayedMetaDataOrder;
79static NSSet *secondaryFiles;
80
81+ (void)load
82{
83        displayedMetaDataOrder = [[NSArray alloc] initWithObjects:
84                                                          META_MOVIE_IMDB_250_KEY,
85                                                          META_MOVIE_IMDB_RATING_KEY,                                     
86                                                          META_MOVIE_DIRECTOR_KEY,
87                                                          META_MOVIE_CAST_KEY,
88                                                          META_MOVIE_GENRES_KEY,
89                                                          META_EPISODE_AND_SEASON_KEY,
90                                                          META_SEASON_NUMBER_KEY,
91                                                          META_EPISODE_NUMBER_KEY,
92                                                          META_MOVIE_IMDB_STATS_KEY,
93                                                          SIZE_KEY,
94                                                          DURATION_KEY,
95                                                          VIDEO_DESC_LABEL_KEY,
96                                                          AUDIO_DESC_LABEL_KEY,
97                                                          SUBTITLES_KEY,
98                                                          nil];
99        displayedMetaData = [[NSSet alloc] initWithArray:displayedMetaDataOrder];
100        secondaryFiles = [[NSSet alloc] initWithObjects:
101                                          @"xml",
102                                          @"srt",
103                                          @"sub",
104                                          @"idx",
105                                          @"ass",
106                                          @"ssa",
107                                          nil];
108}
109
110+ (SapphireFileMetaData *)fileWithPath:(NSString *)path inContext:(NSManagedObjectContext *)moc
111{
112        SapphireMetaData *meta = [SapphireMetaData metaDataWithPath:path inContext:moc];
113        if([meta isKindOfClass:[SapphireFileMetaData class]])
114                return (SapphireFileMetaData *)meta;
115        return nil;
116}
117
118+ (SapphireFileMetaData *)internalCreateFileWithPath:(NSString *)path parent:(SapphireDirectoryMetaData *)parent inContext:(NSManagedObjectContext *)moc
119{
120        SapphireFileMetaData *ret = [NSEntityDescription insertNewObjectForEntityForName:SapphireFileMetaDataName inManagedObjectContext:moc];
121        ret.parent = parent;
122        ret.path = path;
123       
124        return ret;
125}
126
127+ (SapphireFileMetaData *)createFileWithPath:(NSString *)path inContext:(NSManagedObjectContext *)moc
128{
129        SapphireFileMetaData *ret = [SapphireFileMetaData fileWithPath:path inContext:moc];
130        if(ret != nil)
131                return ret;
132       
133        SapphireDirectoryMetaData *parent = [SapphireDirectoryMetaData createDirectoryWithPath:[path stringByDeletingLastPathComponent] inContext:moc];
134        ret = [SapphireFileMetaData internalCreateFileWithPath:path parent:parent inContext:moc];
135       
136        return ret;
137}
138
139+ (SapphireFileMetaData *)createFileWithPath:(NSString *)path parent:(SapphireDirectoryMetaData *)parent inContext:(NSManagedObjectContext *)moc
140{
141        SapphireFileMetaData *ret = [SapphireFileMetaData fileWithPath:path inContext:moc];
142        if(ret != nil)
143                return ret;
144       
145        return [SapphireFileMetaData internalCreateFileWithPath:path parent:parent inContext:moc];
146}
147
148+ (NSDictionary *)upgradeV1FilesFromContext:(NSManagedObjectContext *)oldMoc toContext:(NSManagedObjectContext *)newMoc withMovies:(NSDictionary *)movieLookup directories:(NSDictionary *)dirLookup
149{
150        NSMutableDictionary *lookup = [NSMutableDictionary dictionary];
151        NSArray *files = doFetchRequest(SapphireFileMetaDataName, oldMoc, nil);
152        NSEnumerator *fileEnum = [files objectEnumerator];
153        NSManagedObject *oldFile;
154        while((oldFile = [fileEnum nextObject]) != nil)
155        {
156                SapphireFileMetaData *newFile = [NSEntityDescription insertNewObjectForEntityForName:SapphireFileMetaDataName inManagedObjectContext:newMoc];
157                NSString *path = [oldFile valueForKey:@"path"];
158                newFile.path = path;
159                newFile.parent = [dirLookup objectForKey:[oldFile valueForKeyPath:@"parent.path"]];
160                newFile.audioDescription = [oldFile valueForKey:@"audioDescription"];
161                newFile.audioFormatID = [oldFile valueForKey:@"audioFormatID"];
162                newFile.duration = [oldFile valueForKey:@"duration"];
163                newFile.favorite = [oldFile valueForKey:@"favorite"];
164                newFile.fileClass = [oldFile valueForKey:@"fileClass"];
165                newFile.fileContainerType = [oldFile valueForKey:@"fileContainerType"];
166                newFile.hasVideo = [oldFile valueForKey:@"hasVideo"];
167                newFile.importTypeValue = [[oldFile valueForKey:@"importType"] intValue] & ~IMPORT_TYPE_XML_MASK;
168                newFile.modified = [oldFile valueForKey:@"modified"];
169                newFile.resumeTime = [oldFile valueForKey:@"resumeTime"];
170                newFile.sampleRate = [oldFile valueForKey:@"sampleRate"];
171                newFile.size = [oldFile valueForKey:@"size"];
172                newFile.subtitlesDescription = [oldFile valueForKey:@"subtitlesDescription"];
173                newFile.videoDescription = [oldFile valueForKey:@"videoDescription"];
174                newFile.watched = [oldFile valueForKey:@"watched"];
175                NSNumber *oldMovieNumber = [oldFile valueForKeyPath:@"movie.imdbNumber"];
176                if(oldMovieNumber != nil)
177                        newFile.movie = [movieLookup objectForKey:oldMovieNumber];
178               
179                [lookup setObject:newFile forKey:path];
180        }
181        return lookup;
182}
183
184- (void)insertDictionary:(NSDictionary *)dict withDefer:(NSMutableDictionary *)defer
185{
186        self.audioDescription = [dict objectForKey:AUDIO_DESC_KEY];
187        self.audioFormatID = [dict objectForKey:AUDIO_FORMAT_KEY];
188        self.duration = [dict objectForKey:DURATION_KEY];
189        self.favoriteValue = [[dict objectForKey:FAVORITE_KEY] boolValue];
190        self.fileClass = [dict objectForKey:@"File Class"];
191        self.fileContainerType = [dict objectForKey:@"File Container Type"];
192        id value = [dict objectForKey:MODIFIED_KEY];
193        if(value != nil)
194                self.modified = [NSDate dateWithTimeIntervalSince1970:[value intValue]];
195        self.resumeTimeValue = [[dict objectForKey:RESUME_KEY] unsignedIntValue];
196        self.sampleRate = [dict objectForKey:SAMPLE_RATE_KEY];
197        self.size = [dict objectForKey:SIZE_KEY];
198        self.subtitlesDescription = [dict objectForKey:SUBTITLES_KEY];
199        self.videoDescription = [dict objectForKey:VIDEO_DESC_KEY];
200        self.watchedValue = [[dict objectForKey:WATCHED_KEY] boolValue];
201        self.hasVideoValue = self.videoDescription != nil;
202        value = [dict objectForKey:@"XML Source"];
203        if(value != nil)
204        {
205                NSDictionary *xmlDict = (NSDictionary *)value;
206                SapphireXMLData *xml = self.xmlData;
207                if(xml == nil)
208                {
209                        xml = [NSEntityDescription insertNewObjectForEntityForName:SapphireXMLDataName inManagedObjectContext:[self managedObjectContext]];
210                        self.xmlData = xml;
211                }
212                [xml insertDictionary:xmlDict];
213                xml.modified = [NSDate dateWithTimeIntervalSince1970:[[xmlDict objectForKey:MODIFIED_KEY] intValue]];
214                self.importTypeValue |= IMPORT_TYPE_XML_MASK;
215        }
216        value = [dict objectForKey:@"TVRage Source"];
217        if(value != nil)
218        {
219                SapphireEpisode *ep = [SapphireEpisode episodeWithDictionary:(NSDictionary *)value inContext:[self managedObjectContext]];
220                self.tvEpisode = ep;
221                if(ep != nil)
222                {
223                        self.fileClassValue = FILE_CLASS_TV_SHOW;
224                        NSString *epCoverPath = [[SapphireMetaDataSupport collectionArtPath] stringByAppendingPathComponent:[ep path]];
225                        NSString *oldBasePath = [[epCoverPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:[self.path lastPathComponent]];
226                        NSString *oldCoverPath = searchCoverArtExtForPath([oldBasePath stringByDeletingPathExtension]);
227                        if(oldCoverPath != nil)
228                        {
229                                NSString *newPath = [epCoverPath stringByAppendingPathExtension:[oldCoverPath pathExtension]];
230                                NSFileManager *fm = [NSFileManager defaultManager];
231                                [fm movePath:oldCoverPath toPath:newPath handler:nil];
232                        }
233                }
234                self.importTypeValue |= IMPORT_TYPE_TVSHOW_MASK;
235        }
236        value = [dict objectForKey:@"IMDB Source"];
237        if(value != nil)
238        {
239                SapphireMovie *movie = [SapphireMovie movieWithDictionary:(NSDictionary *)value inContext:[self managedObjectContext] lookup:defer];
240                self.movie = movie;
241                if(movie != nil)
242                {
243                        self.fileClassValue = FILE_CLASS_MOVIE;
244                        NSString *movieCoverPath = [movie coverArtPath];
245                        NSString *oldBasePath = [[[SapphireMetaDataSupport collectionArtPath] stringByAppendingPathComponent:@"@MOVIES"] stringByAppendingPathComponent:[self.path lastPathComponent]];
246                        NSString *oldCoverPath = searchCoverArtExtForPath([oldBasePath stringByDeletingPathExtension]);
247                        if(oldCoverPath != nil)
248                        {
249                                NSString *newPath = [movieCoverPath stringByAppendingPathExtension:[oldCoverPath pathExtension]];
250                                NSFileManager *fm = [NSFileManager defaultManager];
251                                [fm movePath:oldCoverPath toPath:newPath handler:nil];
252                        }
253                }
254                self.importTypeValue |= IMPORT_TYPE_MOVIE_MASK;
255        }
256        NSString *joinPath = [dict objectForKey:JOINED_FILE_KEY];
257        if(joinPath != nil)
258        {
259                NSMutableDictionary *joinDict = [defer objectForKey:@"Join"];
260                NSMutableArray *joinList = [joinDict objectForKey:joinPath];
261                if(joinList == nil)
262                {
263                        joinList = [NSMutableArray array];
264                        [joinDict setObject:joinList forKey:joinPath];
265                }
266                [joinList addObject:self];
267        }
268}
269
270/*Custom TV Episode handler*/
271- (NSComparisonResult) episodeCompare:(SapphireFileMetaData *)other
272{
273        /*Sort by episode*/
274        SapphireEpisode *myEp = self.tvEpisode;
275        SapphireEpisode *theirEp = nil;
276        if([other isKindOfClass:[SapphireFileSymLink class]])
277                theirEp = ((SapphireFileSymLink *)other).file.tvEpisode;
278        else
279                theirEp = other.tvEpisode;
280       
281        if(myEp != nil)
282        {
283                if(theirEp != nil)
284                        return [myEp compare:theirEp];
285                else
286                        return NSOrderedAscending;
287        }
288        else if (theirEp != nil)
289                return NSOrderedDescending;
290
291        return NSOrderedSame;
292}
293
294- (NSComparisonResult) movieCompare:(SapphireFileMetaData *)other
295{
296        SapphireMovie *myMovie = self.movie;
297        SapphireMovie *theirMovie = other.movie;
298       
299        if(myMovie != nil)
300                if(theirMovie != nil)
301                {
302                        NSComparisonResult ret = [myMovie titleCompare:theirMovie];
303                        if(ret == NSOrderedSame)
304                                ret = [myMovie releaseDateCompare:theirMovie];
305                        return ret;
306                }
307                else
308                        return NSOrderedAscending;
309        else if(theirMovie != nil)
310                return NSOrderedDescending;
311       
312        return NSOrderedSame;
313}
314
315- (BOOL) needsUpdating
316{
317        /*Check modified date*/
318        NSDictionary *props = [[NSFileManager defaultManager] fileAttributesAtPath:self.path traverseLink:YES];
319        int modTime = [[props objectForKey:NSFileModificationDate] timeIntervalSince1970];
320       
321        if(props == nil)
322        /*No file*/
323                return FALSE;
324       
325        /*Has it been modified since last import?*/
326        if(modTime != [self.modified timeIntervalSince1970])
327                return YES;
328       
329        if(self.hasVideo == nil)
330                return YES;
331       
332        return NO;
333}
334
335- (oneway void)addFileData:(bycopy NSDictionary *)fileMeta
336{
337        self.audioDescription = [fileMeta objectForKey:AUDIO_DESC_KEY];
338        self.audioFormatID = [fileMeta objectForKey:AUDIO_FORMAT_KEY];
339        self.duration = [fileMeta objectForKey:DURATION_KEY];
340        id value = [fileMeta objectForKey:MODIFIED_KEY];
341        if(value != nil)
342                self.modified = [NSDate dateWithTimeIntervalSince1970:[value intValue]];
343        self.sampleRate = [fileMeta objectForKey:SAMPLE_RATE_KEY];
344        self.size = [fileMeta objectForKey:SIZE_KEY];
345        self.subtitlesDescription = [fileMeta objectForKey:SUBTITLES_KEY];
346        NSString *videoDesc = [fileMeta objectForKey:VIDEO_DESC_KEY];
347        self.videoDescription = videoDesc;
348        if(videoDesc != nil)
349                self.hasVideoValue = YES;
350}
351
352
353BOOL updateMetaData(SapphireFileMetaData *file)
354{
355        BOOL updated =FALSE;
356        if([file needsUpdating])
357        {
358                /*We did an update*/
359                updated=TRUE ;
360                NSMutableDictionary *fileMeta = [NSMutableDictionary dictionary];
361                NSString *path = [file path];
362               
363                NSDictionary *props = [[NSFileManager defaultManager] fileAttributesAtPath:path traverseLink:YES];
364                int modTime = [[props objectForKey:NSFileModificationDate] timeIntervalSince1970];
365                /*Set modified, size, and version*/
366                [fileMeta setObject:[NSNumber numberWithInt:modTime] forKey:MODIFIED_KEY];
367                [fileMeta setObject:[props objectForKey:NSFileSize] forKey:SIZE_KEY];
368               
369                if([file fileContainerTypeValue] == FILE_CONTAINER_TYPE_QT_MOVIE)
370                {
371                        /*Open the movie*/
372                        NSError *error = nil;
373                        QTMovie *movie = [QTMovie movieWithFile:path error:&error];
374                        QTTime duration = [movie duration];
375                        [fileMeta setObject:[NSNumber numberWithFloat:(float)duration.timeValue/(float)duration.timeScale] forKey:DURATION_KEY];
376                        NSArray *audioTracks = [movie tracksOfMediaType:@"soun"];
377                        NSNumber *audioSampleRate = nil;
378                        if([audioTracks count])
379                        {
380                                /*Get the audio track*/
381                                QTTrack *track = [audioTracks objectAtIndex:0];
382                                QTMedia *media = [track media];
383                                if(media != nil)
384                                {
385                                        /*Get the audio format*/
386                                        Media qtMedia = [media quickTimeMedia];
387                                        Handle sampleDesc = NewHandle(1);
388                                        GetMediaSampleDescription(qtMedia, 1, (SampleDescriptionHandle)sampleDesc);
389                                        AudioStreamBasicDescription asbd;
390                                        ByteCount       propSize = 0;
391                                        QTSoundDescriptionGetProperty((SoundDescriptionHandle)sampleDesc, kQTPropertyClass_SoundDescription, kQTSoundDescriptionPropertyID_AudioStreamBasicDescription, sizeof(asbd), &asbd, &propSize);
392                                       
393                                        if(propSize != 0)
394                                        {
395                                                /*Set the format and sample rate*/
396                                                NSNumber *format = [NSNumber numberWithUnsignedInt:asbd.mFormatID];
397                                                [fileMeta setObject:format forKey:AUDIO_FORMAT_KEY];
398                                                audioSampleRate = [NSNumber numberWithDouble:asbd.mSampleRate];
399                                        }
400                                       
401                                        CFStringRef userText = nil;
402                                        propSize = 0;
403                                        QTSoundDescriptionGetProperty((SoundDescriptionHandle)sampleDesc, kQTPropertyClass_SoundDescription, kQTSoundDescriptionPropertyID_UserReadableText, sizeof(userText), &userText, &propSize);
404                                        if(userText != nil)
405                                        {
406                                                /*Set the description*/
407                                                [fileMeta setObject:(NSString *)userText forKey:AUDIO_DESC_KEY];
408                                                CFRelease(userText);
409                                        }
410                                        DisposeHandle(sampleDesc);
411                                }
412                        }
413                        /*Set the sample rate*/
414                        if(audioSampleRate != nil)
415                                [fileMeta setObject:audioSampleRate forKey:SAMPLE_RATE_KEY];
416                        NSArray *videoTracks = [movie tracksOfMediaType:@"vide"];
417                        if([videoTracks count])
418                        {
419                                /*Get the video track*/
420                                QTTrack *track = [videoTracks objectAtIndex:0];
421                                QTMedia *media = [track media];
422                                if(media != nil)
423                                {
424                                        /*Get the video description*/
425                                        Media qtMedia = [media quickTimeMedia];
426                                        Handle sampleDesc = NewHandle(1);
427                                        GetMediaSampleDescription(qtMedia, 1, (SampleDescriptionHandle)sampleDesc);
428                                        CFStringRef userText = nil;
429                                        ByteCount propSize = 0;
430                                        ICMImageDescriptionGetProperty((ImageDescriptionHandle)sampleDesc, kQTPropertyClass_ImageDescription, kICMImageDescriptionPropertyID_SummaryString, sizeof(userText), &userText, &propSize);
431                                        DisposeHandle(sampleDesc);
432                                       
433                                        if(userText != nil)
434                                        {
435                                                /*Set the description*/
436                                                [fileMeta setObject:(NSString *)userText forKey:VIDEO_DESC_KEY];
437                                                CFRelease(userText);
438                                        }
439                                }
440                        }
441                } //QTMovie
442                else if([file fileContainerTypeValue] == FILE_CONTAINER_TYPE_VIDEO_TS)
443                {
444                        SapphireVideoTsParser *dvd = [[SapphireVideoTsParser alloc] initWithPath:path];
445                       
446                        [fileMeta setObject:[dvd videoFormatsString ] forKey:VIDEO_DESC_KEY];
447                        [fileMeta setObject:[dvd audioFormatsString ] forKey:AUDIO_DESC_KEY];
448                        [fileMeta setObject:[dvd subtitlesString    ] forKey:SUBTITLES_KEY ];
449                        [fileMeta setObject:[dvd mainFeatureDuration] forKey:DURATION_KEY  ];
450                        [fileMeta setObject:[dvd totalSize          ] forKey:SIZE_KEY      ];
451                       
452                        [dvd release];
453                } // VIDEO_TS
454                [file addFileData:fileMeta];
455        }
456        return updated;
457}
458
459- (BOOL)updateMetaData
460{
461        return updateMetaData(self);
462}
463
464- (NSString *)sizeString
465{
466        /*Get size*/
467        float size = [self sizeValue];
468        if(size == 0)
469                return @"-";
470       
471        /*The letter for magnitude*/
472        char letter = ' ';
473        if(size >= 1024000)
474        {
475                if(size >= 1024*1024000)
476                {
477                        /*GB*/
478                        size /= 1024 * 1024 * 1024;
479                        letter = 'G';
480                }
481                else
482                {
483                        /*MB*/
484                        size /= 1024 * 1024;
485                        letter = 'M';
486                }
487        }
488        else if (size >= 1000)
489        {
490                /*KB*/
491                size /= 1024;
492                letter = 'K';
493        }
494        return [NSString stringWithFormat:@"%.1f%cB", size, letter];   
495}
496
497- (void)setToReimportFromMask:(NSNumber *)mask
498{
499        [self setToReimportFromMaskValue:[mask intValue]];
500}
501
502- (void)setToReimportFromMaskValue:(int)mask
503{
504        int currentMask = self.importTypeValue;
505        self.importTypeValue = currentMask & ~mask;
506        if(mask & IMPORT_TYPE_MOVIE_MASK)
507        {
508                SapphireMovie *movie = self.movie;
509                self.movie = nil;
510                if(movie != nil && [movie.filesSet count] == 0)
511                        [[self managedObjectContext] deleteObject:movie];
512        }
513        if(mask & IMPORT_TYPE_TVSHOW_MASK)
514        {
515                SapphireEpisode *ep = self.tvEpisode;
516                self.tvEpisode = nil;
517                if(ep != nil && [ep.filesSet count] == 0)
518                        [[self managedObjectContext] deleteObject:ep];
519        }
520}
521
522- (void)setToResetImportDecisions
523{
524        NSManagedObjectContext *moc = [self managedObjectContext];
525        NSString *lowerFileName = [[self.path lastPathComponent] lowercaseString];
526
527        SapphireEpisode *ep = self.tvEpisode;
528        if(ep != nil)
529        {
530                NSSet *translations = ep.tvShow.translationsSet;
531                SapphireTVTranslation *tran;
532                NSEnumerator *tranEnum = [translations objectEnumerator];
533                while((tran = [tranEnum nextObject]) != nil)
534                {
535                        if([lowerFileName hasPrefix:tran.name])
536                        {
537                                SapphireLog(SAPPHIRE_LOG_METADATA_STORE, SAPPHIRE_LOG_LEVEL_DETAIL, @"Deleting TV import translation for %@", tran.name);
538                                [moc deleteObject:tran];
539                        }
540                }
541        }
542        NSString *lookupName;
543        if([[SapphireSettings sharedSettings] dirLookup])
544                lookupName = [[[self.path stringByDeletingLastPathComponent] lastPathComponent] lowercaseString];
545        else
546                lookupName = lowerFileName;
547        SapphireMovieTranslation *movieTran = [SapphireMovieTranslation movieTranslationWithName:[lookupName stringByDeletingPathExtension] inContext:moc];
548        if(movieTran != nil)
549        {
550                SapphireLog(SAPPHIRE_LOG_METADATA_STORE, SAPPHIRE_LOG_LEVEL_DETAIL, @"Deleting Movie import translation for %@", movieTran.name);
551                [moc deleteObject:movieTran];
552        }
553       
554        [self setToReimportFromMaskValue:IMPORT_TYPE_ALL_MASK];
555}
556
557- (void)clearMetaData
558{
559        self.audioDescription = nil;
560        self.audioFormatID = nil;
561        self.duration = nil;
562        self.favoriteValue = 0;
563        self.fileClassValue = nil;
564        self.fileContainerType = nil;
565        self.hasVideo = nil;
566        self.importTypeValue = 0;
567        self.modified = nil;
568        self.resumeTime = nil;
569        self.sampleRate = nil;
570        self.size = nil;
571        self.subtitlesDescription = nil;
572        self.videoDescription = nil;
573        self.watchedValue = 0;
574        self.movie = nil;
575        self.tvEpisode = nil;
576        if(self.xmlData != nil)
577        {
578                [[self managedObjectContext] deleteObject:self.xmlData];
579        }
580}
581
582- (NSString *)coverArtPath
583{
584        /*Find cover art for the current file in the "Cover Art" dir */
585        NSString *subPath = [self path];
586        if([self fileContainerTypeValue] != FILE_CONTAINER_TYPE_VIDEO_TS)
587                subPath = [subPath stringByDeletingPathExtension];
588       
589        NSString *fileName = [subPath lastPathComponent];
590        NSString * myArtPath=nil;
591       
592        if([self fileClassValue]==FILE_CLASS_TV_SHOW)
593                myArtPath=[[self tvEpisode] coverArtPath];
594        if([self fileClassValue]==FILE_CLASS_MOVIE)
595                myArtPath=[[self movie] coverArtPath];
596       
597        /* Check the Collection Art location */
598        NSString *ret=searchCoverArtExtForPath(myArtPath);
599       
600        if(ret != nil)
601                return ret;
602       
603        /* Try Legacy Folders with the file */
604        ret=searchCoverArtExtForPath([[[subPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"Cover Art"] stringByAppendingPathComponent:fileName]);
605       
606        if(ret != nil)
607                return ret;
608       
609        /*Find cover art for the current file in the current dir*/
610        ret = searchCoverArtExtForPath(subPath);
611       
612        if(ret != nil)
613                return ret;
614       
615       
616        return nil;
617}
618
619- (NSString *)durationString
620{
621        /*Create duration string*/
622        int duration = [self durationValue];
623        int secs = duration % 60;
624        int mins = (duration /60) % 60;
625        int hours = duration / 3600;
626        NSString *durationStr = nil;
627        if(hours != 0)
628                durationStr = [NSString stringWithFormat:@"%d:%02d:%02d", hours, mins, secs];
629        else if (mins != 0)
630                durationStr = [NSString stringWithFormat:@"%d:%02d", mins, secs];
631        else
632                durationStr = [NSString stringWithFormat:@"%ds", secs];
633        return durationStr;
634}
635
636static BOOL moving = NO;
637static BOOL moveSuccess = NO;
638static NSString *movingFromPath = @"From";
639static NSString *movingToPath = @"To";
640
641- (void)threadedMove:(NSDictionary *)pathInfo
642{
643        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
644        moveSuccess = [[NSFileManager defaultManager] movePath:[pathInfo objectForKey:movingFromPath] toPath:[pathInfo objectForKey:movingToPath] handler:nil];
645        moving = NO;
646        [pool drain];
647}
648
649- (NSString *)moveToPath:(NSString *)newPath pathForMoveError:(NSString *)errorPath inDir:(SapphireDirectoryMetaData *)newParent
650{
651        NSString *oldPath = [self path];
652        NSFileManager *fm = [NSFileManager defaultManager];
653        if([fm fileExistsAtPath:newPath])
654                return [NSString stringWithFormat:BRLocalizedString(@"The name %@ is already taken", @"Name taken on a file/directory rename; parameter is name"), [newPath lastPathComponent]];
655        if(newParent != nil)
656        {
657                moving = YES;
658                [NSThread detachNewThreadSelector:@selector(threadedMove:) toTarget:self withObject:[NSDictionary dictionaryWithObjectsAndKeys:
659                                                                                                                                                                                         oldPath, movingFromPath,
660                                                                                                                                                                                         newPath, movingToPath,
661                                                                                                                                                                                         nil]];
662                while(moving)
663                        [[NSRunLoop currentRunLoop] runUntilDate:[[NSDate date] addTimeInterval:1]];           
664        }
665        else
666                moveSuccess = [fm movePath:oldPath toPath:newPath handler:nil];
667        if(!moveSuccess)
668                return [NSString stringWithFormat:BRLocalizedString(@"Could not move %@.  Is the filesystem read-only?", @"Unknown error renaming file/directory; parameter is name"), errorPath];
669        [self setPath:newPath];
670        if(newParent != nil)
671        {
672                SapphireDirectoryMetaData *oldParent = self.parent;
673                self.parent = newParent;
674                [oldParent clearPredicateCache];
675                [newParent clearPredicateCache];
676        }
677        [SapphireMetaDataSupport save:[self managedObjectContext]];
678        NSString *extLessPath = [oldPath stringByDeletingPathExtension];
679        NSEnumerator *secondaryExtEnum = [secondaryFiles objectEnumerator];
680        NSString *extension;
681       
682        while((extension = [secondaryExtEnum nextObject]) != nil)
683        {
684                NSString *secondaryPath = [extLessPath stringByAppendingPathExtension:extension];
685                if([fm fileExistsAtPath:secondaryPath])
686                {
687                        NSString *newSecondaryPath = [[newPath stringByDeletingPathExtension] stringByAppendingPathExtension:extension];
688                        if(newParent != nil)
689                        {
690                                moving = YES;
691                                [NSThread detachNewThreadSelector:@selector(threadedMove:) toTarget:self withObject:[NSDictionary dictionaryWithObjectsAndKeys:
692                                                                                                                                                                                                         secondaryPath, movingFromPath,
693                                                                                                                                                                                                         newSecondaryPath, movingToPath,
694                                                                                                                                                                                                         nil]];
695                                while(moving)
696                                        [[NSRunLoop currentRunLoop] runUntilDate:[[NSDate date] addTimeInterval:1]];           
697                        }
698                        else
699                                moveSuccess = [fm movePath:secondaryPath toPath:newSecondaryPath handler:nil];                 
700                        if(!moveSuccess)
701                                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];
702                }               
703        }
704        NSString *coverArtPath = searchCoverArtExtForPath(extLessPath);
705        if(coverArtPath != nil)
706        {
707                NSString *newCoverArtPath = [[newPath stringByDeletingPathExtension] stringByAppendingPathExtension:[coverArtPath pathExtension]];
708                if(newParent != nil)
709                {
710                        moving = YES;
711                        [NSThread detachNewThreadSelector:@selector(threadedMove:) toTarget:self withObject:[NSDictionary dictionaryWithObjectsAndKeys:
712                                                                                                                                                                                                 coverArtPath, movingFromPath,
713                                                                                                                                                                                                 newCoverArtPath, movingToPath,
714                                                                                                                                                                                                 nil]];
715                        while(moving)
716                                [[NSRunLoop currentRunLoop] runUntilDate:[[NSDate date] addTimeInterval:1]];           
717                }
718                else
719                        moveSuccess = [fm movePath:coverArtPath toPath:newCoverArtPath handler:nil];
720                if(!moveSuccess)
721                        return [NSString stringWithFormat:BRLocalizedString(@"Could not move cover art for %@.  Is the filesystem read-only?", @"Unknown error renaming file/directory; parameter is name"), errorPath];
722        }
723        return nil;
724}
725
726- (NSString *)moveToDir:(SapphireDirectoryMetaData *)dir
727{
728        NSString *destination = [dir path];
729        NSString *newPath = [destination stringByAppendingPathComponent:[[self path] lastPathComponent]];
730        return [self moveToPath:newPath pathForMoveError:[newPath lastPathComponent] inDir:dir];
731}
732
733- (NSString *)rename:(NSString *)newFilename
734{
735        int componentCount = [[newFilename pathComponents] count];
736        if(componentCount != 1)
737                return BRLocalizedString(@"A File name should not contain any '/' characters", @"");
738        NSString *oldPath = [self path];
739        newFilename = [newFilename stringByAppendingPathExtension:[oldPath pathExtension]];
740        NSString *newPath = [[oldPath stringByDeletingLastPathComponent] stringByAppendingPathComponent:newFilename];
741        if([oldPath isEqualToString:newPath])
742                return nil;
743        return [self moveToPath:newPath pathForMoveError:newFilename inDir:nil];
744}
745
746- (NSString *)prettyName
747{
748        if(self.tvEpisode != nil)
749        {
750                //TV Episode
751                SapphireEpisode *ep = self.tvEpisode;
752                NSString *tvShowName = ep.tvShow.name;
753                NSString *epName = [ep episodeTitle];
754                int season = ep.season.seasonNumberValue;
755                int firstEp = [ep episodeNumberValue];
756                int lastEp = [ep lastEpisodeNumberValue];
757               
758                NSString *SEString;
759                if(firstEp == 0)
760                        //Single Special Episode
761                        SEString = [NSString stringWithFormat:@"S%02dES1", season];
762                else if(lastEp == firstEp)
763                        //Single normal episode
764                        SEString = [NSString stringWithFormat:@"S%02dE%02d", season, firstEp];
765                else
766                        //Double episode
767                        SEString = [NSString stringWithFormat:@"S%02dE%02d-E%02d", season, firstEp, lastEp];
768               
769                return [NSString stringWithFormat:@"%@ %@ %@", tvShowName, SEString, epName];
770        }
771        else if(self.movie != nil)
772        {
773                //Movie
774                NSDate *releaseDate = [self.movie releaseDate];
775                NSCalendarDate *releaseCalDate = [NSCalendarDate dateWithTimeIntervalSinceReferenceDate:[releaseDate timeIntervalSinceReferenceDate]];
776                return [NSString stringWithFormat:@"%@ (%d)", [self.movie title], [releaseCalDate yearOfCommonEra]];
777        }
778        return nil;
779}
780
781- (NSString *)renameToPrettyName
782{
783        NSString *prettyName = [self prettyName];
784        if(prettyName == nil)
785        {
786                return BRLocalizedString(@"No pretty name to construct", @"");
787        }
788       
789        NSMutableString *mutStr = [prettyName mutableCopy];
790        [mutStr replaceOccurrencesOfString:@"/" withString:@"-" options:0 range:NSMakeRange(0, [mutStr length])];
791       
792        return [self rename:[mutStr autorelease]];
793}
794
795- (NSMutableDictionary *)getDisplayedMetaDataInOrder:(NSArray * *)order;
796{
797        NSString *name = [[self path] lastPathComponent];
798        NSString *durationStr = [self durationString];
799        /*Set the order*/
800        if(order != nil)
801                *order = displayedMetaDataOrder;
802       
803        NSMutableDictionary *ret = [[NSMutableDictionary alloc] init];
804       
805        SapphireMovie *movie = [self movie];
806        SapphireEpisode *ep = [self tvEpisode];
807        if(movie != nil)
808        {
809                [movie insertDisplayMetaData:ret];
810        }
811        else if (ep != nil)
812        {
813                [ep insertDisplayMetaData:ret];
814        }
815       
816        id value = [self videoDescription];
817        if(value != nil)
818                [ret setObject:value forKey:VIDEO_DESC_LABEL_KEY];
819        value = [self audioDescription];
820        if(value != nil)
821                [ret setObject:value forKey:AUDIO_DESC_LABEL_KEY];
822        value = [self subtitlesDescription];
823        if(value != nil)
824                [ret setObject:value forKey:SUBTITLES_KEY];
825        if([self durationValue])
826        {
827                if([self sizeValue])
828                        [ret setObject:[NSString stringWithFormat:@"%@ (%@)", durationStr, [self sizeString]] forKey:BRLocalizedString(DURATION_KEY, @"file duration in metadata display")];
829                else
830                        [ret setObject:durationStr forKey:BRLocalizedString(DURATION_KEY, @"file duration in metadata display")];
831        }
832        else
833                [ret setObject:[self sizeString] forKey:BRLocalizedString(SIZE_KEY, @"filesize in metadata display")];
834       
835        /*Set the title*/
836        if([ret objectForKey:META_TITLE_KEY] == nil)
837                [ret setObject:name forKey:META_TITLE_KEY];
838        return [ret autorelease];
839}
840
841- (NSString *)searchShowName
842{
843        return self.xmlData.searchShowName;
844}
845
846- (int)searchSeasonNumber
847{
848        NSNumber *value = self.xmlData.searchSeasonNumber;
849        if(value != nil)
850                return [value intValue];
851        return -1;
852}
853
854- (int)searchEpisodeNumber
855{
856        NSNumber *value = self.xmlData.searchEpisode;
857        if(value != nil)
858                return [value intValue];
859        return -1;
860}
861
862- (int)searchLastEpisodeNumber
863{
864        NSNumber *value = self.xmlData.searchLastEpisodeNumber;
865        if(value != nil)
866                return [value intValue];
867        return -1;
868}
869
870- (int)searchIMDBNumber
871{
872        NSNumber *value = self.xmlData.searchIMDBNumber;
873        if(value != nil)
874                return [value intValue];
875        return -1;
876}
877
878- (FileContainerType)fileContainerTypeValue
879{
880        return super.fileContainerTypeValue;
881}
882
883- (ImportTypeMask)importTypeValue
884{
885        return super.importTypeValue;
886}
887
888- (long)importedTimeFromSource:(int)source
889{
890        if(source == IMPORT_TYPE_FILE_MASK)
891                return [self.modified timeIntervalSince1970];
892        else if(source == IMPORT_TYPE_XML_MASK)
893                return [self.xmlData.modified timeIntervalSince1970];
894        return 0;
895}
896
897- (void)didImportType:(ImportTypeMask)type
898{
899        self.importTypeValue |= type;
900}
901
902- (void)setMovie:(SapphireMovie *)movie
903{
904        SapphireMovie *oldMovie = self.movie;
905        super.movie = movie;
906        if([self isDeleted])
907                return;
908        if(movie != nil)
909        {
910                [self setFileClassValue:FILE_CLASS_MOVIE];
911                self.importTypeValue |= IMPORT_TYPE_MOVIE_MASK;
912        }
913        if(movie != oldMovie)
914        {
915                self.xmlData.movie = movie;
916                [movie clearPredicateCache];
917                [oldMovie clearPredicateCache];
918                [[NSNotificationCenter defaultCenter] postNotificationName:FILE_DID_CHANGE_MOVIE_NOTIFICATION object:self];
919        }
920}
921
922- (void)setTvEpisode:(SapphireEpisode *)ep
923{
924        SapphireEpisode *oldEp = self.tvEpisode;
925        super.tvEpisode = ep;
926        if([self isDeleted])
927                return;
928        if(ep != nil)
929        {
930                [self setFileClassValue:FILE_CLASS_TV_SHOW];
931                self.importTypeValue |= IMPORT_TYPE_TVSHOW_MASK;
932                [ep clearPredicateCache];
933        }
934        if(ep != oldEp)
935                self.xmlData.episode = ep;
936        [oldEp clearPredicateCache];
937}
938
939- (void)setXmlData:(SapphireXMLData *)data
940{
941        super.xmlData = data;
942        if([self isDeleted])
943                return;
944        if(data != nil)
945        {
946                data.episode = self.tvEpisode;
947                data.movie = self.movie;
948        }
949}
950
951- (FileClass)fileClassValue
952{
953        FileClass xmlClass = self.xmlData.fileClassValue;
954        if(xmlClass != FILE_CLASS_UNKNOWN)
955                return xmlClass;
956        return super.fileClassValue;
957}
958
959- (void)setFileClassValue:(FileClass)fileClass
960{
961        FileClass xmlClass = self.xmlData.fileClassValue;
962        if(xmlClass != FILE_CLASS_UNKNOWN)
963                self.xmlData.fileClassValue = FILE_CLASS_UNKNOWN;
964        super.fileClassValue = fileClass;
965}
966
967- (void)setWatched:(NSNumber*)value_ {
968        NSNumber *oldValue = [self.watched retain];
969        super.watched = value_;
970        if(![oldValue isEqualToNumber:value_])
971        {
972                [self.parent clearPredicateCache];
973                [self.tvEpisode clearPredicateCache];
974                [self.movie clearPredicateCache];
975        }
976}
977
978- (void)setFavorite:(NSNumber*)value_ {
979        NSNumber *oldValue = [self.favorite retain];
980        super.favorite = value_;
981        if(![oldValue isEqualToNumber:value_])
982        {
983                [self.parent clearPredicateCache];
984                [self.tvEpisode clearPredicateCache];
985                [self.movie clearPredicateCache];
986        }
987}
988
989@end
Note: See TracBrowser for help on using the repository browser.