source: trunk/SapphireFrappliance/SapphireMovieImporter.m @ 513

Revision 513, 32.6 KB checked in by gbooker, 6 years ago (diff)

Fixed importer so it doesn't try to download a single poster twice, making it fail in both attempts

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