source: trunk/SapphireFrappliance/SapphireMovieImporter.m @ 461

Revision 461, 31.8 KB checked in by gbooker, 7 years ago (diff)

Organization: Moved all of the frapp classes and files into a sub directory.

Line 
1/*
2 * SapphireMovieImporter.m
3 * Sapphire
4 *
5 * Created by Patrick Merrill on Sep. 10, 2007.
6 * Copyright 2007 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 "SapphireMovieImporter.h"
22#import "SapphireMetaData.h"
23#import "NSString-Extensions.h"
24#import "NSFileManager-Extensions.h"
25#import "SapphireMovieChooser.h"
26#import "SapphirePosterChooser.h"
27#import <SapphireCompatClasses/SapphireFrontRowCompat.h>
28#import "SapphireShowChooser.h"
29
30#define VERSION_KEY                                     @"Version"
31#define CURRENT_VERSION                         2
32/* Translation Keys */
33#define TRANSLATIONS_KEY                        @"Translations"
34#define IMDB_LINK_KEY                           @"IMDB Link"
35#define IMP_LINK_KEY                            @"IMP Link"
36#define IMP_POSTERS_KEY                         @"IMP Posters"
37#define SELECTED_POSTER_KEY                     @"Selected Poster"
38#define AUTO_SELECT_POSTER_KEY          @"Default Poster"
39 /* IMDB XPATHS */
40#define IMDB_SEARCH_XPATH                               @"//td[starts-with(a/@href,'/title')]"
41#define IMDB_UNIQUE_SEARCH_XPATH                @"//a[@class='tn15more inline']/@href"
42#define IMDB_RESULT_LINK_XPATH                  @"a/@href"
43#define IMDB_POSTER_LINK_XPATH                  @"//ul/li/a/@href"
44#define IMDB_RESULT_NAME_XPATH                  @"normalize-space(string())"
45#define IMDB_RESULT_TITLE_YEAR_XPATH    @"//div[@id='tn15title']/h1/replace(string(), '\n', '')"
46#define IMDB_RESULT_INFO_XPATH  @"//div[@class='info']"
47#define IMDB_RESTULT_CAST_NAMES_XPATH   @"//div[@class='info']/table/tr/td/a"
48/* IMP XPATHS */
49#define IMP_POSTER_CANDIDATES_XPATH             @"//img/@src"
50#define IMP_LINK_REDIRECT_XPATH                         @"//head/meta/@content/string()"
51
52
53
54
55/*Delegate class to download cover art*/
56@interface SapphireMovieDataMenuDownloadDelegate : NSObject
57{
58        NSString *destination;
59        NSArray *requestList ;
60        NSMutableArray *delegates ;
61        long downloadsLeft ;
62        id delegate;
63}
64- (id)initWithRequest:(NSArray*)reqList withDestination:(NSString *)dest delegate:(id)aDelegate;
65- (void) downloadDidFinish: (NSURLDownload *) download;
66- (void)downloadMoviePosters ;
67-(void)downloadSingleMoviePoster;
68@end
69
70@interface NSObject (MovieDataDownloadDelegateDelegate)
71- (void)downloadCompleted:(NSURLDownload *)download atIndex:(int)index;
72@end
73
74@implementation SapphireMovieDataMenuDownloadDelegate
75/*!
76* @brief Initialize a cover art downloader
77 *
78 * @param reqList The list of url requests to try
79 * @param dest The path to save the file
80 */
81- (id)initWithRequest:(NSArray*)reqList withDestination:(NSString *)dest delegate:(id)aDelegate;
82{
83        self = [super init];
84        if(!self)
85                return nil;
86        delegates = [NSMutableArray new];
87        destination = [dest retain];
88        requestList = [reqList retain];
89        downloadsLeft=[requestList count];
90        delegate = aDelegate;
91        return self;   
92}
93
94- (void)dealloc
95{
96        [destination release];
97        [requestList release];
98        [delegates release];
99        [super dealloc];
100}
101
102/*!
103 * @brief Fire the delegate to start downloading the posters
104 *
105 */
106-(void)downloadMoviePosters
107{
108        NSEnumerator *reqEnum = [requestList objectEnumerator] ;
109        NSString *req = nil ;
110        while((req = [reqEnum nextObject]) !=nil)
111        {
112                NSURL *posterURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.IMPAwards.com%@",req]];
113                NSString *fullDestination = [NSString stringWithFormat:@"%@/%@", destination, [req lastPathComponent]];
114                NSURLRequest *request = [NSURLRequest requestWithURL:posterURL];
115                NSURLDownload *currentDownload = [[NSURLDownload alloc] initWithRequest:request delegate:self] ;
116                [currentDownload setDestination:fullDestination allowOverwrite:YES];
117                [delegates addObject:currentDownload];
118                [currentDownload release];
119        }
120}
121
122/*!
123 * @brief Fire the delegate to start downloading a single poster
124 *
125 */
126-(void)downloadSingleMoviePoster
127{
128        NSURL *posterURL = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.IMPAwards.com%@",[requestList objectAtIndex:0]]];
129        NSString *fullDestination = destination;
130        NSURLRequest *request = [NSURLRequest requestWithURL:posterURL];
131        NSURLDownload *currentDownload = [[NSURLDownload alloc] initWithRequest:request delegate:self] ;
132        [currentDownload setDestination:fullDestination allowOverwrite:YES];
133        [delegates addObject:currentDownload];
134        [currentDownload release];
135}
136
137- (void) downloadDidFinish: (NSURLDownload *) download
138{
139        downloadsLeft--;
140        if([delegate respondsToSelector:@selector(downloadCompleted:atIndex:)])
141                [delegate downloadCompleted:download atIndex:[delegates indexOfObject:download]];
142}
143
144@end
145
146@interface SapphireMovieImporter (private)
147- (void)writeSettings;
148@end
149
150@implementation SapphireMovieImporter
151
152- (int)upgradeFromVersion1
153{
154        NSEnumerator *keyEnum = [[movieTranslations allKeys] objectEnumerator];
155        NSString *key = nil;
156        while((key = [keyEnum nextObject]) != nil)
157        {
158                NSString *newKey = [key stringByDeletingPathExtension];
159                if(![newKey isEqualToString:key])
160                {
161                        [movieTranslations setObject:[movieTranslations objectForKey:key] forKey:newKey];
162                        [movieTranslations removeObjectForKey:key];
163                }
164        }
165        return 2;
166}
167
168- (id) initWithSavedSetting:(NSString *)path
169{
170        self = [super init];
171        if(!self)
172                return nil;
173       
174        /*Get the settings*/
175        settingsPath = [path retain];
176        NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:path];
177        /*Get or create the show translation dict*/
178        movieTranslations = [[settings objectForKey:TRANSLATIONS_KEY] mutableCopy];
179        if(movieTranslations == nil)
180                movieTranslations = [NSMutableDictionary new];
181       
182        int version = [[settings objectForKey:VERSION_KEY] intValue];
183        if(version < CURRENT_VERSION)
184        {
185                if(version < 2)
186                        version = [self upgradeFromVersion1];
187                [self writeSettings];
188        }
189       
190        return self;
191}
192
193- (void)dealloc
194{
195        [movieTranslations release];
196        [settingsPath release];
197        [super dealloc];
198}
199
200- (void)setImporterDataMenu:(SapphireImporterDataMenu *)theDataMenu
201{
202        dataMenu = theDataMenu;
203}
204
205/*!
206* @brief Gets IMPAwards.com Poster page link
207 *
208 * @param candidateIMDBLink The functions IMDB Posters Path
209 */
210- (NSString *)getPosterPath:(NSString *)candidateIMDBLink
211{
212        NSError *error = nil ;
213        NSURL * url=[NSURL URLWithString:[NSString stringWithFormat:@"http://www.imdb.com%@/posters",candidateIMDBLink]] ;
214        NSXMLDocument *document = [[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyHTML error:&error];
215        NSXMLElement *root = [document rootElement];
216
217        /*Get the results list*/
218        NSArray *results = [root objectsForXQuery:IMDB_POSTER_LINK_XPATH error:&error];
219        if([results count])
220        {
221                /*Get each result*/
222                NSEnumerator *resultEnum = [results objectEnumerator];
223                NSXMLElement *result = nil;
224                while((result = [resultEnum nextObject]) != nil)
225                {
226                        /*Add the result to the list*/                 
227                        NSString *resultURL =[[result stringValue] lowercaseString];
228                        if(resultURL == nil)
229                                continue;
230                        else if([resultURL hasPrefix:@"http://www.impawards.com"])/* See if the link is to IMP */
231                        {
232                                NSString * foundPosterLink =[resultURL stringByReplacingAllOccurancesOf:@"http://www.impawards.com" withString:@""];
233                                return foundPosterLink;
234                        }
235                }               
236        }
237        return nil;
238}
239
240/*!
241* @brief Compile IMPAwards.com Poster link list
242 *
243 * @param posterPageLink The Movie's IMP Poster link extention
244 * @return An array of canidate poster images
245 */
246- (NSArray *)getPosterLinks:(NSString *)posterPageLink
247{
248        NSError *error = nil ;
249        NSURL * url=[NSURL URLWithString:[NSString stringWithFormat:@"http://www.IMPAwards.com%@",posterPageLink]] ;
250        NSXMLDocument *document = [[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyHTML error:&error];
251        NSXMLElement *root = [document rootElement];
252        NSMutableArray * candidatePosterLinks=[NSMutableArray arrayWithObjects:nil] ;
253        NSString * yearPathComponent=[posterPageLink stringByDeletingLastPathComponent];
254       
255        /*Get the results list*/
256        NSArray *results = [root objectsForXQuery:IMP_POSTER_CANDIDATES_XPATH error:&error];
257        if([results count]<1)
258        {
259                /* IMDB had the wrong release year link, see if IMP Tried to redirect*/
260                NSArray *newPosterPageLinkArray = [root objectsForXQuery:IMP_LINK_REDIRECT_XPATH error:&error];
261                if([newPosterPageLinkArray count])
262                {
263                        NSString * newPosterPageLink=[newPosterPageLinkArray objectAtIndex:0] ;
264                        NSScanner *trimmer=[NSScanner scannerWithString:newPosterPageLink];
265                        [trimmer scanUpToString:@"URL=.." intoString:&yearPathComponent];
266                        newPosterPageLink=[newPosterPageLink substringFromIndex:[yearPathComponent length]+6];
267                        yearPathComponent=[newPosterPageLink stringByDeletingLastPathComponent];
268                        url=[NSURL URLWithString:[NSString stringWithFormat:@"http://www.IMPAwards.com%@",newPosterPageLink]] ;
269                        document = [[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyHTML error:&error];
270                        root = [document rootElement];
271                        results = [root objectsForXQuery:IMP_POSTER_CANDIDATES_XPATH error:&error];                     
272                }
273        }
274
275        if([results count])
276        {
277                /*Get each result*/
278                NSEnumerator *resultEnum = [results objectEnumerator];
279                NSXMLElement *result = nil;
280                while((result = [resultEnum nextObject]) != nil)
281                {
282                        /*Add the result to the list*/                 
283                        NSString *resultURL =[[result stringValue] lowercaseString];
284                        if(resultURL == nil)
285                                continue;
286                        if([resultURL hasPrefix:@"posters/"]) /* get the displayed poster link */
287                        {
288                                NSString * subPath=[resultURL substringFromIndex:7];
289                                subPath=[NSString stringWithFormat:[NSString stringWithFormat:@"%@/posters%@",yearPathComponent,subPath]];
290                                [candidatePosterLinks addObject:subPath];
291                        }
292                        else if([resultURL hasPrefix:@"thumbs/"]) /* get the displayed poster link */
293                        {
294                                NSString * subPath=[resultURL substringFromIndex:11];
295                                subPath=[NSString stringWithFormat:[NSString stringWithFormat:@"%@/posters/%@",yearPathComponent,subPath]];
296                                [candidatePosterLinks addObject:subPath];
297                        }
298                }
299        }
300        return [[candidatePosterLinks copy] autorelease];
301}
302
303- (void)downloadPosterCandidates:(NSArray *)posterCandidates
304{
305        /* download all posters to the scratch folder */
306        NSString *posterBuffer = [NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/Sapphire/Poster_Buffer"];
307        [[NSFileManager defaultManager] constructPath:posterBuffer];
308        SapphireMovieDataMenuDownloadDelegate *myDelegate = [[SapphireMovieDataMenuDownloadDelegate alloc] initWithRequest:posterCandidates withDestination:posterBuffer delegate:self];
309        [myDelegate downloadMoviePosters] ;
310        [myDelegate autorelease];
311}
312
313/*!
314 * @brief A download completed
315 *
316 * @param download The download which completed
317 * @param index The index of this poster
318 */
319- (void)downloadCompleted:(NSURLDownload *)download atIndex:(int)index;
320{
321        id controller = [[dataMenu stack] peekController];
322        if([controller isKindOfClass:[SapphirePosterChooser class]])
323                [posterChooser reloadPoster:index];
324}
325
326/*!
327* @brief Fetch information for a movie
328 *
329 * @param movieTitleLink The IMDB link extention (part of the show's URL)
330 * @param moviePath The movie file's location
331 * @return A cached dictionary of the movie info
332 */
333- (NSMutableDictionary *)getMetaForMovie:(NSString *)movieTitleLink withPath:(NSString*)moviePath
334{
335        NSError *error = nil;
336        NSMutableDictionary *ret = [NSMutableDictionary dictionary];
337       
338        /* Gather IMDB Data */
339        /*Get the movie html*/
340        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.IMDB.com%@",movieTitleLink]];
341        NSXMLDocument *document = [[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyHTML error:&error];
342       
343        /* Get the movie title */
344        NSString *movieTitle= [[document objectsForXQuery:IMDB_RESULT_TITLE_YEAR_XPATH error:&error] objectAtIndex:0];
345        NSScanner *metaTrimmer=[NSScanner scannerWithString:movieTitle];
346        [metaTrimmer scanUpToString:@"(" intoString:&movieTitle];
347        movieTitle=[movieTitle substringToIndex:[movieTitle length]-1];
348       
349        /* Get the User Rating (IMDB) */
350        NSArray *ratingCandidates=[document objectsForXQuery:@"//b/string()" error:&error];
351        NSString *usrRating=[[document objectsForXQuery:@"//b/string()" error:&error] objectAtIndex:[ratingCandidates indexOfObject:@"User Rating:"]+1];
352        metaTrimmer=[NSScanner scannerWithString:usrRating];
353        [metaTrimmer scanUpToString:@"/" intoString:&usrRating];
354       
355        /* Check for IMDB top 250 */
356        NSNumber * top250=nil ;
357        NSArray *top250Candidate=[document objectsForXQuery:@"//div[@class='left']/a/string()" error:&error];
358
359        if([top250Candidate count])
360        {
361                NSString *top250Str=[top250Candidate objectAtIndex:0];
362                if([top250Str hasPrefix:@"Top 250:"])
363                        top250=[NSNumber numberWithInt:[[top250Str substringFromIndex:10] intValue]];
364        }
365               
366        /* Get the release date */
367        NSArray *rawData=[document objectsForXQuery:IMDB_RESULT_INFO_XPATH error:&error];
368        NSDate * releaseDate=nil ;
369        NSString * plot=nil;
370        NSString * mpaaRating=nil;
371        NSNumber * oscarsWon=nil ;
372        NSArray * directors=nil;
373        NSArray * writers=nil;
374        NSArray * genres=nil;
375        if([rawData count])
376        {
377                NSEnumerator *resultEnum = [rawData objectEnumerator];
378                NSXMLElement *result = nil;
379                while((result = [resultEnum nextObject]) != nil)
380                {
381                        NSString *dataCandidate=[result stringValue];
382
383                        if([dataCandidate length])
384                        {
385                                NSString * dataType=nil;
386                                NSScanner * trimmer=[NSScanner scannerWithString:dataCandidate];
387                                [trimmer scanUpToString:@"\n" intoString:&dataType];
388                                if([dataType hasPrefix:@"Release"])
389                                {
390                                        [trimmer scanUpToString:@"(" intoString:&dataCandidate];
391                                        releaseDate=[NSDate dateWithNaturalLanguageString:dataCandidate];
392
393                                }
394                                else if([dataType hasPrefix:@"Writers"])
395                                {
396                                        NSString *writersStr = [[trimmer string] substringFromIndex:[trimmer scanLocation]+1];
397                                        NSMutableArray *mutWrit = [[writersStr componentsSeparatedByString:@"\n"] mutableCopy];
398                                        [mutWrit removeObject:@""];
399                                        writers = [[mutWrit copy] autorelease];
400                                        [mutWrit release];
401                                }
402                                else if([dataType hasPrefix:@"Director"])
403                                {
404                                        NSString *directorsStr = [[trimmer string] substringFromIndex:[trimmer scanLocation]+1];
405                                        NSMutableArray *mutDirs = [[directorsStr componentsSeparatedByString:@"\n"] mutableCopy];
406                                        [mutDirs removeObject:@""];
407                                        directors = [[mutDirs copy] autorelease];
408                                        [mutDirs release];
409                                }
410                                else if([dataType hasPrefix:@"Awards"])
411                                {
412                                        NSString *awardsStr = [[trimmer string] substringFromIndex:[trimmer scanLocation]+1];
413                                        trimmer=[NSScanner scannerWithString:awardsStr];
414                                        [trimmer scanUpToString:@" Oscars." intoString:&awardsStr];
415                                        if([awardsStr length]<[[trimmer string] length])
416                                        {
417                                                awardsStr=[awardsStr substringFromIndex:3];
418                                                oscarsWon=[NSNumber numberWithInt:[awardsStr intValue]];
419                                        }
420                                        else if([awardsStr hasPrefix:@"Won Oscar"])
421                                                oscarsWon=[[NSNumber alloc] initWithInt:1];
422                                       
423                                }
424                                else if([dataType hasPrefix:@"MPAA"])
425                                {
426                                        NSString *mpaaStr = [[trimmer string] substringFromIndex:[trimmer scanLocation]+1];
427                                        if([mpaaStr hasPrefix:@"Rated"])
428                                        {
429                                                trimmer=[NSScanner scannerWithString:[mpaaStr substringFromIndex:6]] ;
430                                                [trimmer scanUpToString:@" " intoString:&mpaaRating];
431                                        }
432                                }
433                                else if([dataType hasPrefix:@"Genre"])
434                                {
435
436                                        NSMutableArray *myGenres=[NSMutableArray array];
437                                        while(![trimmer isAtEnd])
438                                        {
439                                                NSString *aGenre=nil;
440                                                [trimmer scanUpToString:@"/" intoString:&aGenre];
441                                                if(aGenre)
442                                                {
443                                                        if([aGenre isEqualToString:@"/"])
444                                                                continue ;
445                                                        else if([aGenre hasSuffix:@"more\n"])
446                                                                aGenre=[aGenre substringToIndex:[aGenre length]-6];
447                                                        else if([aGenre hasSuffix:@" "])
448                                                                aGenre=[aGenre substringToIndex:[aGenre length]-1];
449                                                        [myGenres addObject:aGenre];
450                                                }
451                                                else
452                                                {
453                                                        [trimmer scanUpToString:@" " intoString:&aGenre];
454                                                }
455                                        }
456                                        genres = [[myGenres copy] autorelease];
457                                }
458                                else if([dataType hasPrefix:@"Plot Outline"])
459                                {
460                                        [trimmer scanUpToString:@"more\n" intoString:&plot];
461                                }
462                                else
463                                        continue ;
464                        }
465                        else
466                                continue ;
467                }
468
469               
470        }
471       
472        /* Get the cast list */
473        NSArray *rawCast=[document objectsForXQuery:IMDB_RESTULT_CAST_NAMES_XPATH error:&error];
474        NSArray *completeCast=nil ;
475        if([rawCast count])
476        {
477                NSMutableArray *results=nil;
478                NSEnumerator *resultEnum = [rawCast objectEnumerator];
479                NSXMLElement *result = nil;
480                while((result = [resultEnum nextObject]) != nil)
481                {
482                        NSString *castName=nil;
483                        castName=[result stringValue];
484                        if([castName length])
485                        {
486                                NSString * castURL=[[[result attributeForName:@"href"]stringValue]lowercaseString];
487                                if([castURL hasPrefix:@"/name/"])
488                                {
489                                        if(!results)
490                                                results=[NSMutableArray arrayWithObject:castName];
491                                        else
492                                                [results addObject:castName];
493                                }
494                                else continue ;
495                        }
496                        else
497                        continue ;
498                }
499                completeCast=[[results copy] autorelease] ;
500        }
501       
502       
503        /* populate metadata to return */
504        [ret setObject:movieTitleLink forKey:META_MOVIE_IDENTIFIER_KEY];
505        if(oscarsWon)
506                [ret setObject:oscarsWon forKey:META_MOVIE_OSCAR_KEY];
507        else
508                [ret setObject:[NSNumber numberWithInt:0] forKey:META_MOVIE_OSCAR_KEY];
509        if(top250)
510                [ret setObject:top250 forKey:META_MOVIE_IMDB_250_KEY];
511        if([usrRating length]>0)
512                [ret setObject:[NSNumber numberWithFloat:[usrRating floatValue]] forKey:META_MOVIE_IMDB_RATING_KEY];
513        if(mpaaRating)
514                [ret setObject:mpaaRating forKey:META_MOVIE_MPAA_RATING_KEY];
515        else
516                [ret setObject:@"N/A" forKey:META_MOVIE_MPAA_RATING_KEY];
517        if(directors)
518                [ret setObject:directors forKey:META_MOVIE_DIRECTOR_KEY];
519        if(plot)
520                [ret setObject:plot forKey:META_MOVIE_PLOT_KEY];
521        if(releaseDate)
522                [ret setObject:releaseDate forKey:META_MOVIE_RELEASE_DATE_KEY];
523        if(genres)
524                [ret setObject:genres forKey:META_MOVIE_GENRES_KEY];
525        if(completeCast)
526                [ret setObject:completeCast forKey:META_MOVIE_CAST_KEY];
527        if(movieTitle)
528                [ret setObject:movieTitle forKey:META_MOVIE_TITLE_KEY];
529        return ret;
530}
531
532
533
534/*!
535* @brief Searches for a movie based on the filename
536 *
537 * @param searchStr Part of the filename to use in the show search
538 * @return An array of possible results
539 */
540- (NSArray *)searchResultsForMovie:(NSString *)searchStr
541{
542        /* prep the search string */
543        searchStr = [searchStr stringByDeletingPathExtension];
544        searchStr = [searchStr stringByReplacingAllOccurancesOf:@"_" withString:@" "];
545        searchStr = [searchStr stringByReplacingAllOccurancesOf:@"." withString:@" "];
546        searchStr = [searchStr stringByReplacingAllOccurancesOf:@"-" withString:@" "];
547        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.imdb.com/find?s=all&q=%@", [searchStr URLEncode]]];
548        NSError * error = nil;
549        BOOL uniqueResult=NO ;
550        NSArray * results = nil;
551        NSMutableArray *ret=nil;
552        NSXMLDocument *document = [[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyHTML error:&error];
553        NSXMLElement *root = [document rootElement];   
554        NSString *resultTitle=[[[root objectsForXQuery:@"//title" error:&error]objectAtIndex:0] stringValue];
555       
556        if([resultTitle isEqualToString:@"IMDb Search"])/*Make sure we didn't get back a unique result */
557        {
558                results = [root objectsForXQuery:IMDB_SEARCH_XPATH error:&error];
559                ret = [NSMutableArray arrayWithCapacity:[results count]];
560        }
561        else /* IMDB directly linked to a unique movie title */
562        {
563                uniqueResult=YES ;
564                ret = [NSMutableArray arrayWithCapacity:1];
565                results = [root objectsForXQuery:IMDB_UNIQUE_SEARCH_XPATH error:&error];               
566        }       
567               
568        if([results count])
569        {
570                /*Get each result*/
571                NSEnumerator *resultEnum = [results objectEnumerator];
572                NSXMLElement *result = nil;
573                while((result = [resultEnum nextObject]) != nil)
574                {
575                        if(uniqueResult)/*Check for a unique title link*/
576                        {
577                                NSURL *resultURL = [NSURL URLWithString:[[[result objectsForXQuery:IMDB_UNIQUE_SEARCH_XPATH error:&error] objectAtIndex:0] stringValue]] ;
578                                if(resultURL == nil)
579                                        continue;
580                                NSString *URLSubPath =[resultURL path] ;
581                                if([URLSubPath hasSuffix:@"/releaseinfo"])
582                                {
583                                        URLSubPath=[URLSubPath stringByReplacingAllOccurancesOf:@"/releaseinfo" withString:@""];
584                                        [ret addObject:[NSDictionary dictionaryWithObjectsAndKeys:
585                                                resultTitle, @"name",
586                                                URLSubPath, IMDB_LINK_KEY,
587                                                nil]];
588                                        return ret ;
589                                }
590                        }
591                        else
592                        {
593                                /*Add the result to the list*/
594                                NSURL *resultURL = [NSURL URLWithString:[[[result objectsForXQuery:IMDB_RESULT_LINK_XPATH error:&error] objectAtIndex:0] stringValue]] ;
595                                NSString * resultTitleValue=[result stringValue];
596                                /* Deal with AKA titles */
597                                if([resultTitleValue hasPrefix:@"\n"])
598                                {
599                                        resultTitleValue=[resultTitleValue substringFromIndex:3];
600                                        resultTitle=[resultTitle stringByReplacingAllOccurancesOf:@"\n" withString:@" "];
601                                }
602                                /* Skip image links */
603                                else if(resultURL == nil || [resultTitleValue characterAtIndex:0] == 160)
604                                        continue;
605                                /*Skip Video Game titles (VG) */
606                                if([resultTitleValue rangeOfString:@"(VG)"].location != NSNotFound)
607                                        continue ;
608                                [ret addObject:[NSDictionary dictionaryWithObjectsAndKeys:
609                                        [[result objectsForXQuery:IMDB_RESULT_NAME_XPATH error:&error] objectAtIndex:0], @"name",
610                                        [resultURL path], IMDB_LINK_KEY,
611                                        nil]];
612                        }
613                }
614                if(!uniqueResult && [ret count]>0)return ret;
615        }
616        return nil ;
617}
618
619
620/*!
621* @brief Write our setings out
622 */
623- (void)writeSettings
624{
625        NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
626                [NSNumber numberWithInt:CURRENT_VERSION], VERSION_KEY,
627                movieTranslations, TRANSLATIONS_KEY,
628                nil];
629        [settings writeToFile:settingsPath atomically:YES];
630}
631
632/*!
633* @brief verify file extention of a file
634 *
635 * @param filePAth The file's path
636 * @return YES if candidate, NO otherwise
637 */
638- (BOOL)isMovieCandidate:(NSString*)fileExt
639{
640        if([[SapphireMetaData videoExtensions] member:fileExt])
641                return YES;
642        else return NO ;
643}
644
645- (ImportState) importMetaData:(id <SapphireFileMetaDataProtocol>)metaData
646{
647        currentData = metaData;
648        /*Check to see if it is already imported*/
649        if([metaData importedTimeFromSource:META_IMDB_IMPORT_KEY])
650                return IMPORT_STATE_NOT_UPDATED;
651        id controller = [[dataMenu stack] peekController];
652        /* Check to see if we are waiting on the user to select a show title */
653        if([controller isKindOfClass:[SapphireShowChooser class]])
654        {
655                /* Another chooser is on the screen - delay further processing */
656                return IMPORT_STATE_NOT_UPDATED;
657        }
658        /*Get path*/
659        NSString *path = [metaData path];
660        if(![self isMovieCandidate:[path pathExtension]])
661                return IMPORT_STATE_NOT_UPDATED;
662        /*Get fineName*/
663        NSString *fileName = [path lastPathComponent];
664        if([metaData fileClass]==FILE_CLASS_TV_SHOW) /* File is a TV Show - skip it */
665                return IMPORT_STATE_NOT_UPDATED;
666       
667        /*Get the movie title*/
668        NSString *movieDataLink = nil ;
669        /*Check to see if we know this movie*/
670        NSMutableDictionary *dict=[movieTranslations objectForKey:[[fileName lowercaseString] stringByDeletingPathExtension]];
671        if(dict == nil)
672        {
673                if(dataMenu == nil)
674                /*There is no data menu, background import. So we can't ask user, skip*/
675                        return IMPORT_STATE_NOT_UPDATED;
676                /*Ask the user what movie this is*/
677                NSArray *movies = [self searchResultsForMovie:fileName];
678                /* No need to prompt the user for an empty set */
679                if(movies==nil)
680                {
681                        /* We tried to import but found nothing - mark this file to be skipped on future imports */
682                        [metaData importInfo:[NSMutableDictionary dictionary] fromSource:META_IMDB_IMPORT_KEY withTime:[[NSDate date] timeIntervalSince1970]];
683                        [metaData setFileClass:FILE_CLASS_OTHER];
684                        return IMPORT_STATE_UPDATED;
685                }
686                /*Bring up the prompt*/
687                SapphireMovieChooser *chooser = [[SapphireMovieChooser alloc] initWithScene:[dataMenu scene]];
688                [chooser setMovies:movies];
689                [chooser setFileName:fileName];         
690                [chooser setListTitle:BRLocalizedString(@"Select Movie Title", @"Prompt the user for title of movie")];
691                /*And display prompt*/
692                [[dataMenu stack] pushController:chooser];
693                [chooser release];
694                return IMPORT_STATE_NEEDS_SUSPEND;
695                //Data will be ready for access on the next call
696        }
697
698        NSString * selectedPoster=nil ;
699        NSString * autoSelectPoster=nil ;
700        selectedPoster =        [dict objectForKey:SELECTED_POSTER_KEY] ;
701        autoSelectPoster=       [dict objectForKey:AUTO_SELECT_POSTER_KEY] ;
702        if(!selectedPoster)
703        {
704                if(dataMenu == nil)
705                /*There is no data menu, background import. So we can't ask user, skip*/
706                        return IMPORT_STATE_NOT_UPDATED;
707                /* Posters will be downloaded, let the user choose one */
708                [SapphireFrontRowCompat renderScene:[dataMenu scene]];
709                NSArray *posters=[dict objectForKey:IMP_POSTERS_KEY];
710                if(![posters count])
711                {
712                        NSString *posterPath=nil ;
713                        /* Get the IMP Key with the IMDB Posters page */
714                        posterPath=[self getPosterPath:[dict objectForKey:IMDB_LINK_KEY]] ;
715                        if(posterPath!=nil)
716                        {
717                                [dict setObject:posterPath forKey:IMP_LINK_KEY];
718                                /*We got a posterPath, get the posterLinks */
719                                posters = [self getPosterLinks:posterPath];
720                                if(posters != nil)
721                                {
722                                        /* Add the poster links */
723                                        [dict setObject:posters forKey:IMP_POSTERS_KEY];
724                                        [self writeSettings];
725                                }
726                                /* Add another method via chooser incase IMDB doesn't have an IMP link */
727                        }
728                        else posters=nil ;
729                }
730                if([posters count])
731                {
732                        posterChooser=[[SapphirePosterChooser alloc] initWithScene:[dataMenu scene]];
733                        if(![posterChooser okayToDisplay])
734                        {
735                                if(autoSelectPoster==nil)
736                                {
737                                        /* Auto Select the first poster */
738                                        autoSelectPoster=[posters objectAtIndex:0];
739                                        [dict setObject:autoSelectPoster forKey:AUTO_SELECT_POSTER_KEY];
740                                        [self writeSettings];
741                                }
742                                [posterChooser release];
743                        }
744                        else
745                        {
746                                [self downloadPosterCandidates:posters];
747                                [posterChooser setPosters:posters] ;
748                                [posterChooser setFileName:fileName];
749                                [posterChooser setListTitle:BRLocalizedString(@"Select Movie Poster", @"Prompt the user for poster selection")];
750                                [[dataMenu stack] pushController:posterChooser];
751                                [posterChooser release];
752                                return IMPORT_STATE_NEEDS_SUSPEND;
753                        }
754                        [dataMenu resume];
755                }
756        }
757        if(selectedPoster && [dict objectForKey:IMP_POSTERS_KEY])
758        {
759                /* Lets move the selected poster to the corresponding Cover Art Directory */
760                NSFileManager *fileAgent=[NSFileManager defaultManager];
761                NSString * poster=[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/Sapphire/Poster_Buffer"];
762                poster=[poster stringByAppendingPathComponent:[selectedPoster lastPathComponent]];
763                NSString * coverart=[[SapphireMetaData collectionArtPath]stringByAppendingPathComponent:@"@MOVIES"];
764                [fileAgent constructPath:coverart];
765                coverart=[coverart stringByAppendingPathComponent:[fileName stringByDeletingPathExtension]];
766                coverart=[coverart stringByAppendingPathExtension:[poster pathExtension]];
767                if([fileAgent fileExistsAtPath:poster])/* See if we need to clean up */
768                {
769                        if([fileAgent fileExistsAtPath:coverart])/* Remove old poster */
770                                [fileAgent removeFileAtPath:coverart handler:self];
771                        [fileAgent movePath:poster toPath:coverart handler:self] ;
772                        /* Lets clean up the Poster_Buffer */
773                        NSArray *oldPosters = [dict objectForKey:IMP_POSTERS_KEY];
774                        if([oldPosters count])
775                        {
776                                NSEnumerator *resultEnum = [oldPosters objectEnumerator];
777                                NSString *result = nil;
778                                while((result = [resultEnum nextObject]) != nil)
779                                {
780                                        BOOL isDir=NO ;
781                                        NSString *removeFile=[NSString stringWithFormat:@"%@/%@",[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/Sapphire/Poster_Buffer"],[result lastPathComponent]];
782                                        [fileAgent fileExistsAtPath:removeFile isDirectory:&isDir];
783                                        if(!isDir)[fileAgent removeFileAtPath:removeFile handler:self] ;
784                                }
785                        }
786                }
787                else if(![fileAgent fileExistsAtPath:coverart])
788                {
789                        /* We have seen this file before, but in a different location */
790                        /* - OR - the coverart has been deleted */
791                        NSArray * posterList=[NSArray arrayWithObject:selectedPoster];
792                        SapphireMovieDataMenuDownloadDelegate *myDelegate = [[SapphireMovieDataMenuDownloadDelegate alloc] initWithRequest:posterList withDestination:coverart delegate:self];
793                        [myDelegate downloadSingleMoviePoster] ;
794                        [myDelegate autorelease];
795                }
796        }
797        else if(autoSelectPoster)
798        {
799                /* The poster chooser wasn't loaded - ATV 1.0 */
800                NSFileManager *fileAgent=[NSFileManager defaultManager];
801                NSString * coverart=[[SapphireMetaData collectionArtPath]stringByAppendingPathComponent:@"@MOVIES"];
802                [fileAgent constructPath:coverart];
803                NSArray * posterList=[NSArray arrayWithObject:autoSelectPoster];
804                coverart=[coverart stringByAppendingPathComponent:[fileName stringByDeletingPathExtension]];
805                coverart=[coverart stringByAppendingPathExtension:[autoSelectPoster pathExtension]];
806                SapphireMovieDataMenuDownloadDelegate *myDelegate = [[SapphireMovieDataMenuDownloadDelegate alloc] initWithRequest:posterList withDestination:coverart delegate:self];
807                [myDelegate downloadSingleMoviePoster] ;
808                [myDelegate autorelease];       
809        }
810       
811        /*Import the info*/
812        /*IMDB Data */
813        NSMutableDictionary *infoIMDB = nil;
814        movieDataLink=[dict objectForKey:IMDB_LINK_KEY];
815        infoIMDB = [self getMetaForMovie:movieDataLink withPath:path];
816        if(!infoIMDB)
817                return IMPORT_STATE_NOT_UPDATED;
818        [infoIMDB removeObjectForKey:IMDB_LINK_KEY];
819        [metaData importInfo:infoIMDB fromSource:META_IMDB_IMPORT_KEY withTime:[[NSDate date] timeIntervalSince1970]];
820        [metaData setFileClass:FILE_CLASS_MOVIE];
821        /*We imported something*/
822        return IMPORT_STATE_UPDATED;
823}
824
825
826- (NSString *)completionText
827{
828        return BRLocalizedString(@"All availble Movie data has been imported", @"The Movie import is complete");
829}
830
831- (NSString *)initialText
832{
833        return BRLocalizedString(@"Fetch Movie Data", @"Title");
834}
835
836- (NSString *)informativeText
837{
838        return BRLocalizedString(@"This tool will attempt to fetch information about your Movie files from the Internet (IMDB/IMPAwards).  This procedure may take quite some time and could ask you questions.  You may cancel at any time.", @"Description of the movie import");
839}
840
841- (NSString *)buttonTitle
842{
843        return BRLocalizedString(@"Start Fetching Data", @"Button");
844}
845
846- (void) wasExhumedByPoppingController: (BRLayerController *) controller
847{
848        /*See if it was a movie chooser*/
849        if([controller isKindOfClass:[SapphireMovieChooser class]])
850        {
851                /*Get the user's selection*/
852                SapphireMovieChooser *chooser = (SapphireMovieChooser *)controller;
853                int selection = [chooser selection];
854                if(selection == MOVIE_CHOOSE_CANCEL)
855                {
856                        /*They aborted, skip*/
857                        [dataMenu skipNextItem];
858                }
859                else if(selection == MOVIE_CHOOSE_NOT_MOVIE)
860                {
861                        /*They said it is not a movie, so put in empty data so they are not asked again*/
862                        [currentData importInfo:[NSMutableDictionary dictionary] fromSource:META_IMDB_IMPORT_KEY withTime:[[NSDate date] timeIntervalSince1970]];
863                        if ([currentData fileClass] != FILE_CLASS_TV_SHOW)
864                                [currentData setFileClass:FILE_CLASS_UNKNOWN];
865                }
866                else
867                {
868                        /*They selected a movie title, save the translation and write it*/
869                        NSDictionary *movie = [[chooser movies] objectAtIndex:selection];
870                        NSString *filename = [[[chooser fileName] lowercaseString] stringByDeletingPathExtension];
871                        NSMutableDictionary * transDict = [movieTranslations objectForKey:filename];
872                        if(transDict == nil)
873                        {
874                                transDict=[NSMutableDictionary new] ;
875                                [movieTranslations setObject:transDict forKey:filename];
876                                [transDict release];
877                        }
878                        /* Add IMDB Key */
879                        [transDict setObject:[movie objectForKey:IMDB_LINK_KEY] forKey:IMDB_LINK_KEY];
880                }
881                [self writeSettings];
882                /*We can resume now*/
883                [dataMenu resume];
884        }
885        else if([controller isKindOfClass:[SapphirePosterChooser class]])
886        {
887                int selectedPoster = [posterChooser selectedPoster];
888                if(selectedPoster == POSTER_CHOOSE_CANCEL)
889                        /*They aborted, skip*/
890                        [dataMenu skipNextItem];
891                else
892                {
893                        NSString *selected = [[posterChooser posters] objectAtIndex:selectedPoster];
894                        NSString *filename = [[[posterChooser fileName] lowercaseString] stringByDeletingPathExtension];
895                        NSMutableDictionary * transDict = [movieTranslations objectForKey:filename];
896                        if(transDict == nil)
897                        {
898                                transDict=[NSMutableDictionary new] ;
899                                [movieTranslations setObject:transDict forKey:filename];
900                                [transDict release];
901                        }
902                        [transDict setObject:selected forKey:SELECTED_POSTER_KEY];
903                }
904                posterChooser = nil;
905                [self writeSettings];
906                /*We can resume now*/
907                [dataMenu resume];
908        }
909        else
910                return;
911}
912
913@end
Note: See TracBrowser for help on using the repository browser.