source: branches/CoreData/SapphireFrappliance/MetaData/Support/SapphireMetaDataSupport.m @ 780

Revision 780, 17.3 KB checked in by gbooker, 6 years ago (diff)

Some extra logging

Line 
1/*
2 * SapphireMetaDataSupport.m
3 * Sapphire
4 *
5 * Created by Graham Booker on Apr. 16, 2008.
6 * Copyright 2008 Sapphire Development Team and/or www.nanopi.net
7 * All rights reserved.
8 *
9 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU
10 * General Public License as published by the Free Software Foundation; either version 3 of the License,
11 * or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
14 * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General
15 * Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License along with this program; if not,
18 * write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19 */
20
21#import "SapphireMetaDataSupport.h"
22#import "SapphireDirectoryMetaData.h"
23#import "SapphireFileMetaData.h"
24#import "SapphireJoinedFile.h"
25#import "SapphireCollectionDirectory.h"
26#import "SapphireMovie.h"
27#import "SapphireCast.h"
28#import "SapphireMovieTranslation.h"
29#import "SapphireMoviePoster.h"
30#import "SapphireTVTranslation.h"
31#import "SapphireTVShow.h"
32#import "SapphireMetaDataUpgrading.h"
33#import "SapphireDirector.h"
34#import "SapphireGenre.h"
35#import "SapphireDirectorySymLink.h"
36#import "SapphireFileSymLink.h"
37#import "SapphireEpisode.h"
38#import "SapphireXMLData.h"
39#import "SapphireApplianceController.h"
40#import "SapphireSeason.h"
41#import "CoreDataSupportFunctions.h"
42
43#define META_VERSION_KEY                        @"Version"
44
45/* Movie Translations */
46#define MOVIE_TRAN_VERSION_KEY                                  @"Version"
47#define MOVIE_TRAN_CURRENT_VERSION                              2
48/* Translation Keys */
49#define MOVIE_TRAN_TRANSLATIONS_KEY                             @"Translations"
50#define MOVIE_TRAN_IMDB_LINK_KEY                                @"IMDB Link"
51#define MOVIE_TRAN_IMP_LINK_KEY                                 @"IMP Link"
52#define MOVIE_TRAN_IMP_POSTERS_KEY                              @"IMP Posters"
53#define MOVIE_TRAN_SELECTED_POSTER_KEY                  @"Selected Poster"
54#define MOVIE_TRAN_AUTO_SELECT_POSTER_KEY               @"Default Poster"
55
56static NSSet *coverArtExtentions = nil;
57
58NSString *searchCoverArtExtForPath(NSString *path)
59{
60        NSFileManager *fm = [NSFileManager defaultManager];
61        NSString *directory = [path stringByDeletingLastPathComponent];
62        NSArray *files = [fm directoryContentsAtPath:directory];
63        NSString *lastComp = [path lastPathComponent];
64        /*Search all files*/
65        NSEnumerator *fileEnum = [files objectEnumerator];
66        NSString *file = nil;
67        while((file = [fileEnum nextObject]) != nil)
68        {
69                NSString *ext = [file pathExtension];
70                if([ext length] &&
71                   [coverArtExtentions containsObject:ext] &&
72                   [lastComp caseInsensitiveCompare:[file stringByDeletingPathExtension]] == NSOrderedSame)
73                        return [directory stringByAppendingPathComponent:file];
74        }
75        /*Didn't find one*/
76        return nil;
77}
78
79@implementation SapphireMetaDataSupport
80
81+ (void)load
82{
83        coverArtExtentions = [[NSSet alloc] initWithObjects:
84                                                  @"jpg",
85                                                  @"jpeg",
86                                                  @"tif",
87                                                  @"tiff",
88                                                  @"png",
89                                                  @"gif",
90                                                  nil];
91}
92
93+ (SapphireMetaDataSupport *)sharedInstance
94{
95        static SapphireMetaDataSupport *shared = nil;
96       
97        if(shared == nil)
98                shared = [[SapphireMetaDataSupport alloc] init];
99       
100        return shared;
101}
102
103- (void) dealloc
104{
105        [writeTimer invalidate];
106        writeTimer = nil;
107        [super dealloc];
108}
109
110+ (void)pruneMetaData:(NSManagedObjectContext *)moc
111{
112        NSPredicate *movieFilePred = [NSPredicate predicateWithFormat:@"movie != nil"];
113        NSArray *movieFiles = doFetchRequest(SapphireFileMetaDataName, moc, movieFilePred);
114        NSSet *movieIds = [NSSet setWithArray:[movieFiles valueForKeyPath:@"movie.objectID"]];
115       
116        NSPredicate *movieNoFile = [NSPredicate predicateWithFormat:@"NOT SELF IN %@", movieIds];
117        NSArray *emptyMovies = doFetchRequest(SapphireMovieName, moc, movieNoFile);
118        SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DETAIL, @"Pruning Movies %@", [emptyMovies valueForKeyPath:@"title"]);
119        NSEnumerator *objEnum = [emptyMovies objectEnumerator];
120        NSManagedObject *obj;
121        while((obj = [objEnum nextObject]) != nil)
122                [moc deleteObject:obj];
123       
124        NSArray *allMovies = doFetchRequest(SapphireMovieName, moc, nil);
125       
126        NSDictionary *pruneKeys = [NSDictionary dictionaryWithObjectsAndKeys:
127                                                           SapphireCastName, @"cast",
128                                                           SapphireGenreName, @"genres",
129                                                           SapphireDirectorName, @"directors",
130                                                           nil];
131        NSEnumerator *keyEnum = [pruneKeys keyEnumerator];
132        NSString *key;
133        while((key = [keyEnum nextObject]) != nil)
134        {
135                NSString *objName = [pruneKeys objectForKey:key];
136                NSArray *itemSet = [allMovies valueForKeyPath:[NSString stringWithFormat:@"@distinctUnionOfSets.%@.objectID", key]];
137                NSArray *emptyItems = doFetchRequest(objName, moc, [NSPredicate predicateWithFormat:@"NOT SELF IN %@", itemSet]);
138                SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DETAIL, @"Pruning %@ %@", key, [emptyItems valueForKeyPath:@"name"]);
139                objEnum = [emptyItems objectEnumerator];
140                while((obj = [objEnum nextObject]) != nil)
141                        [moc deleteObject:obj];
142        }
143       
144        NSPredicate *epFilePred = [NSPredicate predicateWithFormat:@"tvEpisode != nil"];
145        NSArray *epFiles = doFetchRequest(SapphireFileMetaDataName, moc, epFilePred);
146        NSSet *epIds = [NSSet setWithArray:[epFiles valueForKeyPath:@"tvEpisode.objectID"]];
147       
148        NSPredicate *epNoFile = [NSPredicate predicateWithFormat:@"NOT SELF IN %@", epIds];
149        NSArray *emptyEpisodes = doFetchRequest(SapphireEpisodeName, moc, epNoFile);
150        SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DETAIL, @"Pruning Episodes %@", [emptyEpisodes valueForKeyPath:@"episodeTitle"]);
151        objEnum = [emptyEpisodes objectEnumerator];
152        while((obj = [objEnum nextObject]) != nil)
153                [moc deleteObject:obj];
154       
155        NSArray *allEps = doFetchRequest(SapphireEpisodeName, moc, nil);
156       
157        NSSet *seasonIds = [allEps valueForKeyPath:@"@distinctUnionOfObjects.season.objectID"];
158        NSPredicate *noEpisodes = [NSPredicate predicateWithFormat:@"NOT SELF IN %@", seasonIds];
159        NSArray *emptySeasons = doFetchRequest(SapphireSeasonName, moc, noEpisodes);
160        SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DETAIL, @"Pruning Seasons %@", [emptySeasons valueForKeyPath:@"path"]);
161        objEnum = [emptySeasons objectEnumerator];
162        while((obj = [objEnum nextObject]) != nil)
163                [moc deleteObject:obj];
164       
165        NSSet *showIds = [allEps valueForKeyPath:@"@distinctUnionOfObjects.tvShow.objectID"];
166        noEpisodes = [NSPredicate predicateWithFormat:@"NOT SELF IN %@", showIds];
167        NSArray *emptyShows = doFetchRequest(SapphireTVShowName, moc, noEpisodes);
168        SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DETAIL, @"Pruning Shows %@", [emptyShows valueForKeyPath:@"name"]);
169        objEnum = [emptyShows objectEnumerator];
170        while((obj = [objEnum nextObject]) != nil)
171                [moc deleteObject:obj];
172}
173
174- (void)realWriteMetaData:(NSTimer *)timer
175{
176        NSManagedObjectContext *context = nil;
177        if([timer isKindOfClass:[NSManagedObjectContext class]])
178                context = (NSManagedObjectContext *)timer;
179        else
180                context = [timer userInfo];
181       
182        if(writeTimer != nil)
183                SapphireLog(SAPPHIRE_LOG_METADATA_STORE, SAPPHIRE_LOG_LEVEL_DETAIL, @"Rescheduled write");
184        [writeTimer invalidate];
185        writeTimer = nil;
186        NSError *error = nil;
187        locked = NO;
188        BOOL success = NO;
189        @try {
190                success = [context save:&error];
191        }
192        @catch (NSException * e) {
193                SapphireLog(SAPPHIRE_LOG_METADATA_STORE, SAPPHIRE_LOG_LEVEL_DETAIL, @"Could not save due to exception \"%@\" with reason \"%@\"", [e name], [e reason]);
194        }
195        if(error != nil)
196        {
197                SapphireLog(SAPPHIRE_LOG_METADATA_STORE, SAPPHIRE_LOG_LEVEL_DETAIL, @"Save error \"%@\"", error);
198                NSException *underlying = [[error userInfo] objectForKey:@"NSUnderlyingException"];
199                SapphireLog(SAPPHIRE_LOG_METADATA_STORE, SAPPHIRE_LOG_LEVEL_DEBUG, @"Underlying is %@ %@ %@ %@", underlying, [underlying name], [underlying reason], [underlying userInfo]);
200                if([[underlying reason] isEqualToString:@"database is locked"])
201                {
202                        SapphireLog(SAPPHIRE_LOG_METADATA_STORE, SAPPHIRE_LOG_LEVEL_DEBUG, @"Detected locked");
203                        locked = YES;
204                }
205        }
206        if(success == NO)
207        {
208                SapphireLog(SAPPHIRE_LOG_METADATA_STORE, SAPPHIRE_LOG_LEVEL_DEBUG, @"Inserted objects is %@", [context insertedObjects]);
209                SapphireLog(SAPPHIRE_LOG_METADATA_STORE, SAPPHIRE_LOG_LEVEL_DEBUG, @"Updated objects is %@", [context updatedObjects]);
210                SapphireLog(SAPPHIRE_LOG_METADATA_STORE, SAPPHIRE_LOG_LEVEL_DEBUG, @"Deleted objects is %@", [context deletedObjects]);
211                interval *= 2;
212                [writeTimer invalidate];
213                SapphireLog(SAPPHIRE_LOG_METADATA_STORE, SAPPHIRE_LOG_LEVEL_DETAIL, @"Rescheduling write to occurr in %f seconds", interval);
214               
215                @try {
216                        NSSet *objSet = [context updatedObjects];
217                        NSEnumerator *objEnum = [objSet objectEnumerator];
218                        NSManagedObject *obj;
219                        while((obj = [objEnum nextObject]) != nil)
220                                [context refreshObject:obj mergeChanges:YES];
221                        objSet = [context deletedObjects];
222                        objEnum = [objSet objectEnumerator];
223                        while((obj = [objEnum nextObject]) != nil)
224                                [context refreshObject:obj mergeChanges:YES];                   
225                }
226                @catch (NSException * e) {
227                        SapphireLog(SAPPHIRE_LOG_METADATA_STORE, SAPPHIRE_LOG_LEVEL_DETAIL, @"Could not fix save due to exception \"%@\" with reason \"%@\"", [e name], [e reason]);
228                }
229               
230                writeTimer = [NSTimer scheduledTimerWithTimeInterval:interval target:self selector:@selector(realWriteMetaData:) userInfo:context repeats:NO];
231        }
232        else
233                SapphireLog(SAPPHIRE_LOG_METADATA_STORE, SAPPHIRE_LOG_LEVEL_DETAIL, @"Save successful");
234}
235
236- (BOOL)save:(NSManagedObjectContext *)context;
237{
238        if(writeTimer == nil)
239        {
240                interval = 1;
241                [self performSelectorOnMainThread:@selector(realWriteMetaData:) withObject:context waitUntilDone:YES];
242                return (writeTimer == nil);
243        }
244        else
245                return YES;
246}
247
248+ (BOOL)save:(NSManagedObjectContext *)context
249{
250        if(context == nil)
251                return NO;
252       
253        return [[SapphireMetaDataSupport sharedInstance] save:context];
254}
255
256- (BOOL)wasLocked
257{
258        return locked;
259}
260
261+ (BOOL)wasLocked
262{
263        return [[SapphireMetaDataSupport sharedInstance] wasLocked];
264}
265
266+ (void)importV1Store:(NSManagedObjectContext *)v1Context intoContext:(NSManagedObjectContext *)context withDisplay:(SapphireMetaDataUpgrading *)display
267{
268        [display setCurrentFile:@"Upgrading Cast"];
269        NSDictionary *castLookup = [SapphireCast upgradeV1CastFromContext:v1Context toContext:context];
270        [display setCurrentFile:@"Upgrading Directors"];
271        NSDictionary *directorLookup = [SapphireDirector upgradeV1DirectorsFromContext:v1Context toContext:context];
272        [display setCurrentFile:@"Upgrading Genres"];
273        NSDictionary *genreLookup = [SapphireGenre upgradeV1GenresFromContext:v1Context toContext:context];
274        [display setCurrentFile:@"Upgrading Movies"];
275        NSDictionary *movieLookup = [SapphireMovie upgradeV1MoviesFromContext:v1Context toContext:context withCast:castLookup directors:directorLookup genres:genreLookup];
276        [display setCurrentFile:@"Upgrading Shows"];
277        [SapphireTVShow upgradeV1ShowsFromContext:v1Context toContext:context];
278        [display setCurrentFile:@"Upgrading Directories"];
279        NSDictionary *dirLookup = [SapphireDirectoryMetaData upgradeV1DirectoriesFromContext:v1Context toContext:context];
280        [display setCurrentFile:@"Upgrading Files"];
281        NSDictionary *fileLookup = [SapphireFileMetaData upgradeV1FilesFromContext:v1Context toContext:context withMovies:movieLookup directories:dirLookup];
282        [display setCurrentFile:@"Upgrading SymLinks"];
283        [SapphireDirectorySymLink upgradeV1DirLinksFromContext:v1Context toContext:context directories:dirLookup];
284        [SapphireFileSymLink upgradeV1FileLinksFromContext:v1Context toContext:context directories:dirLookup file:fileLookup];
285        [display setCurrentFile:@"Upgrading Joined Files"];
286        [SapphireJoinedFile upgradeV1JoinedFileFromContext:v1Context toContext:context file:fileLookup];
287        [display setCurrentFile:@"Upgrading Episodes"];
288        [SapphireEpisode upgradeV1EpisodesFromContext:v1Context toContext:context file:fileLookup];
289        [display setCurrentFile:@"Upgrading XML"];
290        [SapphireXMLData upgradeV1XMLFromContext:v1Context toContext:context file:fileLookup];
291}
292
293+ (void)importPlist:(NSString *)configDir intoContext:(NSManagedObjectContext *)context withDisplay:(SapphireMetaDataUpgrading *)display
294{
295        NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:[configDir stringByAppendingPathComponent:@"metaData.plist"]];
296        NSMutableDictionary *defer = [NSMutableDictionary dictionaryWithObjectsAndKeys:
297                                                                  [NSMutableDictionary dictionary], @"Join",
298                                                                  [NSMutableDictionary dictionary], @"Cast",
299                                                                  [NSMutableDictionary dictionary], @"Directors",
300                                                                  nil];
301        int version = [[dict objectForKey:META_VERSION_KEY] intValue];
302        SapphireDirectoryMetaData *newDir = nil;
303        if(version > 2)
304        {
305                NSDictionary *slash = [dict objectForKey:@"/"];
306                newDir = [SapphireDirectoryMetaData createDirectoryWithPath:@"/" parent:nil inContext:context];
307                [newDir insertDictionary:slash withDefer:defer andDisplay:display];
308        }
309        else
310        {
311                newDir = [SapphireDirectoryMetaData createDirectoryWithPath:[NSHomeDirectory() stringByAppendingPathComponent:@"Movies"] parent:nil inContext:context];
312                [newDir insertDictionary:dict withDefer:defer andDisplay:display];
313        }
314        [display setCurrentFile:BRLocalizedString(@"Upgrading Joined Files", @"")];
315        NSDictionary *joinDict = [defer objectForKey:@"Join"];
316        if(joinDict != nil)
317        {
318                NSEnumerator *joinEunm = [joinDict keyEnumerator];
319                NSString *joinedPath;
320                while((joinedPath = [joinEunm nextObject]) != nil)
321                {
322                        SapphireJoinedFile *joinedFile = [SapphireJoinedFile joinedFileForPath:joinedPath inContext:context];
323                        NSArray *joinArray = [joinDict objectForKey:joinedPath];
324                        NSEnumerator *joinedEnum = [joinArray objectEnumerator];
325                        SapphireFileMetaData *joinFile;
326                        while((joinFile = [joinedEnum nextObject]) != nil)
327                                joinFile.joinedToFile = joinedFile;
328                }                       
329        }
330       
331        [display setCurrentFile:BRLocalizedString(@"Upgrading Collection Prefs", @"")];
332        NSDictionary *options = [dict objectForKey:@"Options"];
333        NSMutableSet *collections = [NSMutableSet set];
334        NSArray *custom = [options objectForKey:@"Directories"];
335        if([custom count])
336                [collections unionSet:[NSSet setWithArray:custom]];
337        NSDictionary *hidden = [options objectForKey:@"Hide"];
338        NSArray *keyArray = [hidden allKeys];
339        if([keyArray count])
340                [collections unionSet:[NSSet setWithArray:keyArray]];
341        NSDictionary *skipped = [options objectForKey:@"Skip"];
342        keyArray = [skipped allKeys];
343        if([keyArray count])
344                [collections unionSet:[NSSet setWithArray:keyArray]];
345       
346        NSEnumerator *collectionEnum = [collections objectEnumerator];
347        NSString *collectionPath;
348        while((collectionPath = [collectionEnum nextObject]) != nil)
349        {
350                [SapphireCollectionDirectory collectionAtPath:collectionPath
351                                                                                                mount:NO
352                                                                                                 skip:[[skipped objectForKey:collectionPath] boolValue]
353                                                                                           hidden:[[hidden objectForKey:collectionPath] boolValue]
354                                                                                           manual:[custom containsObject:collectionPath]
355                                                                                        inContext:context];
356        }
357        //Set the mount values for all
358        [SapphireCollectionDirectory availableCollectionDirectoriesInContext:context];
359       
360        [display setCurrentFile:BRLocalizedString(@"Upgrading Movie Translations", @"")];
361        NSDictionary *movieTranslations = [NSDictionary dictionaryWithContentsOfFile:[configDir stringByAppendingPathComponent:@"movieData.plist"]];
362        NSDictionary *translations = [movieTranslations objectForKey:MOVIE_TRAN_TRANSLATIONS_KEY];
363        NSEnumerator *movieEnum = [translations keyEnumerator];
364        NSString *movie = nil;
365        while((movie = [movieEnum nextObject]) != nil)
366        {
367                NSDictionary *movieDict = [translations objectForKey:movie];
368                SapphireMovieTranslation *trans = [SapphireMovieTranslation createMovieTranslationWithName:movie inContext:context];
369                trans.IMPLink = [movieDict objectForKey:MOVIE_TRAN_IMP_LINK_KEY];
370                NSString *IMDBLink = [movieDict objectForKey:MOVIE_TRAN_IMDB_LINK_KEY];
371                trans.IMDBLink = IMDBLink;
372               
373                int imdbNumber = [SapphireMovie imdbNumberFromString:IMDBLink];
374                if(imdbNumber != 0)
375                {
376                        SapphireMovie *thisMovie = [SapphireMovie movieWithIMDB:imdbNumber inContext:context];
377                        trans.movie = thisMovie;
378                }
379               
380                NSArray *posters = [movieDict objectForKey:MOVIE_TRAN_IMP_POSTERS_KEY];
381                NSSet *dupCheck = [NSSet setWithArray:posters];
382                posters = [dupCheck allObjects];
383               
384                NSString *selectedPoster = [movieDict objectForKey:MOVIE_TRAN_SELECTED_POSTER_KEY];
385                int i, count = [posters count];
386                for(i=0; i<count; i++)
387                {
388                        NSString *posterUrl = [posters objectAtIndex:i];
389                        if([posterUrl isEqualToString:selectedPoster])
390                                trans.selectedPosterIndexValue = i;
391                       
392                        [SapphireMoviePoster createPosterWithLink:posterUrl index:i translation:trans inContext:context];
393                }
394        }
395       
396        [display setCurrentFile:BRLocalizedString(@"Upgrading TV Translations", @"")];
397        NSDictionary *tvTranslations = [NSDictionary dictionaryWithContentsOfFile:[configDir stringByAppendingPathComponent:@"tvdata.plist"]];
398        translations = [tvTranslations objectForKey:@"Translations"];
399        NSEnumerator *tvEnum = [translations keyEnumerator];
400        NSString *tvShow = nil;
401        while((tvShow = [tvEnum nextObject]) != nil)
402        {
403                NSString *showPath = [translations objectForKey:tvShow];
404                SapphireTVTranslation *trans = [SapphireTVTranslation createTVTranslationForName:tvShow withPath:showPath inContext:context];
405                SapphireTVShow *show = [SapphireTVShow showWithPath:showPath inContext:context];
406                trans.tvShow = show;
407        }
408       
409        NSError *error = nil;
410        NSManagedObject *obj;
411        NSEnumerator *objEnum = [[context registeredObjects] objectEnumerator];
412        while((obj = [objEnum nextObject]) != nil)
413        {
414                if(![obj validateForUpdate:&error])
415                        SapphireLog(SAPPHIRE_LOG_METADATA_STORE, SAPPHIRE_LOG_LEVEL_INFO, @"%@", error);
416        }
417}
418
419+ (NSString *)collectionArtPath
420{
421        static NSString *path = nil;
422        if(path == nil)
423                path = [[applicationSupportDir() stringByAppendingPathComponent:@"Collection Art"] retain];
424        return path;
425}
426
427@end
Note: See TracBrowser for help on using the repository browser.