source: branches/CoreData/SapphireFrappliance/MetaDataImporting/SapphireMovieImporter.m @ 557

Revision 557, 31.6 KB checked in by gbooker, 6 years ago (diff)

Numerous fixes. Importing now works; background importer is horribly broken (but can be fixed).

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