source: trunk/SapphireMovieImporter.m @ 436

Revision 436, 31.7 KB checked in by gbooker, 7 years ago (diff)
  • Moved the compat classes to another project in a different directory
  • Include the headers in the frameworks
  • Refer to the headers in the frameworks in the main project
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/* - (BOOL) importMetaData:(id <SapphireFileMetaDataProtocol>)metaData */
646- (BOOL) importMetaData:(id <SapphireFileMetaDataProtocol>)metaData
647{
648        currentData = metaData;
649        /*Check to see if it is already imported*/
650        if([metaData importedTimeFromSource:META_IMDB_IMPORT_KEY])
651                return NO;
652        id controller = [[dataMenu stack] peekController];
653        /* Check to see if we are waiting on the user to select a show title */
654        if([controller isKindOfClass:[SapphireShowChooser class]])
655        {
656                /* Another chooser is on the screen - delay further processing */
657                return NO;
658        }
659        /*Get path*/
660        NSString *path = [metaData path];
661        if(![self isMovieCandidate:[path pathExtension]])
662                return NO;
663        /*Get fineName*/
664        NSString *fileName = [path lastPathComponent];
665        if([metaData fileClass]==FILE_CLASS_TV_SHOW) /* File is a TV Show - skip it */
666                return NO ;
667       
668        /*Get the movie title*/
669        NSString *movieDataLink = nil ;
670        /*Check to see if we know this movie*/
671        NSMutableDictionary *dict=[movieTranslations objectForKey:[[fileName lowercaseString] stringByDeletingPathExtension]];
672        if(dict == nil)
673        {
674                if(dataMenu == nil)
675                /*There is no data menu, background import. So we can't ask user, skip*/
676                        return NO;
677                /*Ask the user what movie this is*/
678                NSArray *movies = [self searchResultsForMovie:fileName];
679                /* No need to prompt the user for an empty set */
680                if(movies==nil)
681                {
682                        /* We tried to import but found nothing - mark this file to be skipped on future imports */
683                        [metaData importInfo:[NSMutableDictionary dictionary] fromSource:META_IMDB_IMPORT_KEY withTime:[[NSDate date] timeIntervalSince1970]];
684                        [metaData setFileClass:FILE_CLASS_OTHER];
685                        return YES;
686                }
687                /*Pause for the user's input*/
688                [dataMenu pause];
689                /*Bring up the prompt*/
690                SapphireMovieChooser *chooser = [[SapphireMovieChooser alloc] initWithScene:[dataMenu scene]];
691                [chooser setMovies:movies];
692                [chooser setFileName:fileName];         
693                [chooser setListTitle:BRLocalizedString(@"Select Movie Title", @"Prompt the user for title of movie")];
694                /*And display prompt*/
695                [[dataMenu stack] pushController:chooser];
696                [chooser release];
697                return NO ;
698                //Data will be ready for access on the next call
699        }
700
701        NSString * selectedPoster=nil ;
702        NSString * autoSelectPoster=nil ;
703        selectedPoster =        [dict objectForKey:SELECTED_POSTER_KEY] ;
704        autoSelectPoster=       [dict objectForKey:AUTO_SELECT_POSTER_KEY] ;
705        if(!selectedPoster)
706        {
707                if(dataMenu == nil)
708                /*There is no data menu, background import. So we can't ask user, skip*/
709                        return NO;
710                /* Posters will be downloaded, let the user choose one */
711                [SapphireFrontRowCompat renderScene:[dataMenu scene]];
712                NSArray *posters=[dict objectForKey:IMP_POSTERS_KEY];
713                if(![posters count])
714                {
715                        NSString *posterPath=nil ;
716                        /* Get the IMP Key with the IMDB Posters page */
717                        posterPath=[self getPosterPath:[dict objectForKey:IMDB_LINK_KEY]] ;
718                        if(posterPath!=nil)
719                        {
720                                [dict setObject:posterPath forKey:IMP_LINK_KEY];
721                                /*We got a posterPath, get the posterLinks */
722                                posters = [self getPosterLinks:posterPath];
723                                if(posters != nil)
724                                {
725                                        /* Add the poster links */
726                                        [dict setObject:posters forKey:IMP_POSTERS_KEY];
727                                        [self writeSettings];
728                                }
729                                /* Add another method via chooser incase IMDB doesn't have an IMP link */
730                        }
731                        else posters=nil ;
732                }
733                if([posters count])
734                {
735                        [dataMenu pause];
736                        posterChooser=[[SapphirePosterChooser alloc] initWithScene:[dataMenu scene]];
737                        if(![posterChooser okayToDisplay])
738                        {
739                                if(autoSelectPoster==nil)
740                                {
741                                        /* Auto Select the first poster */
742                                        autoSelectPoster=[posters objectAtIndex:0];
743                                        [dict setObject:autoSelectPoster forKey:AUTO_SELECT_POSTER_KEY];
744                                        [self writeSettings];
745                                }
746                                [posterChooser release];
747                        }
748                        else
749                        {
750                                [self downloadPosterCandidates:posters];
751                                [posterChooser setPosters:posters] ;
752                                [posterChooser setFileName:fileName];
753                                [posterChooser setListTitle:BRLocalizedString(@"Select Movie Poster", @"Prompt the user for poster selection")];
754                                [[dataMenu stack] pushController:posterChooser];
755                                [posterChooser release];
756                                return NO;
757                        }
758                        [dataMenu resume];
759                }
760        }
761        if(selectedPoster && [dict objectForKey:IMP_POSTERS_KEY])
762        {
763                /* Lets move the selected poster to the corresponding Cover Art Directory */
764                NSFileManager *fileAgent=[NSFileManager defaultManager];
765                NSString * poster=[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/Sapphire/Poster_Buffer"];
766                poster=[poster stringByAppendingPathComponent:[selectedPoster lastPathComponent]];
767                NSString * coverart=[[SapphireMetaData collectionArtPath]stringByAppendingPathComponent:@"@MOVIES"];
768                [fileAgent constructPath:coverart];
769                coverart=[coverart stringByAppendingPathComponent:[fileName stringByDeletingPathExtension]];
770                coverart=[coverart stringByAppendingPathExtension:[poster pathExtension]];
771                if([fileAgent fileExistsAtPath:poster])/* See if we need to clean up */
772                {
773                        if([fileAgent fileExistsAtPath:coverart])/* Remove old poster */
774                                [fileAgent removeFileAtPath:coverart handler:self];
775                        [fileAgent movePath:poster toPath:coverart handler:self] ;
776                        /* Lets clean up the Poster_Buffer */
777                        NSArray *oldPosters = [dict objectForKey:IMP_POSTERS_KEY];
778                        if([oldPosters count])
779                        {
780                                NSEnumerator *resultEnum = [oldPosters objectEnumerator];
781                                NSString *result = nil;
782                                while((result = [resultEnum nextObject]) != nil)
783                                {
784                                        BOOL isDir=NO ;
785                                        NSString *removeFile=[NSString stringWithFormat:@"%@/%@",[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/Sapphire/Poster_Buffer"],[result lastPathComponent]];
786                                        [fileAgent fileExistsAtPath:removeFile isDirectory:&isDir];
787                                        if(!isDir)[fileAgent removeFileAtPath:removeFile handler:self] ;
788                                }
789                        }
790                }
791                else if(![fileAgent fileExistsAtPath:coverart])
792                {
793                        /* We have seen this file before, but in a different location */
794                        /* - OR - the coverart has been deleted */
795                        NSArray * posterList=[NSArray arrayWithObject:selectedPoster];
796                        SapphireMovieDataMenuDownloadDelegate *myDelegate = [[SapphireMovieDataMenuDownloadDelegate alloc] initWithRequest:posterList withDestination:coverart delegate:self];
797                        [myDelegate downloadSingleMoviePoster] ;
798                        [myDelegate autorelease];
799                }
800//              return NO;
801        }
802        else if(autoSelectPoster)
803        {
804                /* The poster chooser wasn't loaded - ATV 1.0 */
805                NSFileManager *fileAgent=[NSFileManager defaultManager];
806                NSString * coverart=[[SapphireMetaData collectionArtPath]stringByAppendingPathComponent:@"@MOVIES"];
807                [fileAgent constructPath:coverart];
808                NSArray * posterList=[NSArray arrayWithObject:autoSelectPoster];
809                coverart=[coverart stringByAppendingPathComponent:[fileName stringByDeletingPathExtension]];
810                coverart=[coverart stringByAppendingPathExtension:[autoSelectPoster pathExtension]];
811                SapphireMovieDataMenuDownloadDelegate *myDelegate = [[SapphireMovieDataMenuDownloadDelegate alloc] initWithRequest:posterList withDestination:coverart delegate:self];
812                [myDelegate downloadSingleMoviePoster] ;
813                [myDelegate autorelease];       
814        }
815       
816        /*Import the info*/
817        /*IMDB Data */
818        NSMutableDictionary *infoIMDB = nil;
819        movieDataLink=[dict objectForKey:IMDB_LINK_KEY];
820        infoIMDB = [self getMetaForMovie:movieDataLink withPath:path];
821        if(!infoIMDB)
822                return NO;
823        [infoIMDB removeObjectForKey:IMDB_LINK_KEY];
824        [metaData importInfo:infoIMDB fromSource:META_IMDB_IMPORT_KEY withTime:[[NSDate date] timeIntervalSince1970]];
825        [metaData setFileClass:FILE_CLASS_MOVIE];
826        /*We imported something*/
827        return YES;
828}
829
830
831- (NSString *)completionText
832{
833        return BRLocalizedString(@"All availble Movie data has been imported", @"The Movie import is complete");
834}
835
836- (NSString *)initialText
837{
838        return BRLocalizedString(@"Fetch Movie Data", @"Title");
839}
840
841- (NSString *)informativeText
842{
843        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");
844}
845
846- (NSString *)buttonTitle
847{
848        return BRLocalizedString(@"Start Fetching Data", @"Button");
849}
850
851- (void) wasExhumedByPoppingController: (BRLayerController *) controller
852{
853        /*See if it was a movie chooser*/
854        if([controller isKindOfClass:[SapphireMovieChooser class]])
855        {
856                /*Get the user's selection*/
857                SapphireMovieChooser *chooser = (SapphireMovieChooser *)controller;
858                int selection = [chooser selection];
859                if(selection == MOVIE_CHOOSE_CANCEL)
860                {
861                        /*They aborted, skip*/
862                        [dataMenu skipNextItem];
863                }
864                else if(selection == MOVIE_CHOOSE_NOT_MOVIE)
865                {
866                        /*They said it is not a movie, so put in empty data so they are not asked again*/
867                        [currentData importInfo:[NSMutableDictionary dictionary] fromSource:META_IMDB_IMPORT_KEY withTime:[[NSDate date] timeIntervalSince1970]];
868                        if ([currentData fileClass] != FILE_CLASS_TV_SHOW)
869                                [currentData setFileClass:FILE_CLASS_UNKNOWN];
870                }
871                else
872                {
873                        /*They selected a movie title, save the translation and write it*/
874                        NSDictionary *movie = [[chooser movies] objectAtIndex:selection];
875                        NSString *filename = [[[chooser fileName] lowercaseString] stringByDeletingPathExtension];
876                        NSMutableDictionary * transDict = [movieTranslations objectForKey:filename];
877                        if(transDict == nil)
878                        {
879                                transDict=[NSMutableDictionary new] ;
880                                [movieTranslations setObject:transDict forKey:filename];
881                                [transDict release];
882                        }
883                        /* Add IMDB Key */
884                        [transDict setObject:[movie objectForKey:IMDB_LINK_KEY] forKey:IMDB_LINK_KEY];
885                }
886                [self writeSettings];
887                /*We can resume now*/
888                [dataMenu resume];
889        }
890        else if([controller isKindOfClass:[SapphirePosterChooser class]])
891        {
892                int selectedPoster = [posterChooser selectedPoster];
893                if(selectedPoster == POSTER_CHOOSE_CANCEL)
894                        /*They aborted, skip*/
895                        [dataMenu skipNextItem];
896                else
897                {
898                        NSString *selected = [[posterChooser posters] objectAtIndex:selectedPoster];
899                        NSString *filename = [[[posterChooser fileName] lowercaseString] stringByDeletingPathExtension];
900                        NSMutableDictionary * transDict = [movieTranslations objectForKey:filename];
901                        if(transDict == nil)
902                        {
903                                transDict=[NSMutableDictionary new] ;
904                                [movieTranslations setObject:transDict forKey:filename];
905                                [transDict release];
906                        }
907                        [transDict setObject:selected forKey:SELECTED_POSTER_KEY];
908                }
909                posterChooser = nil;
910                [self writeSettings];
911                /*We can resume now*/
912                [dataMenu resume];
913        }
914        else
915                return;
916}
917
918@end
Note: See TracBrowser for help on using the repository browser.