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

Revision 821, 34.6 KB checked in by gbooker, 6 years ago (diff)

Correct importing of VIDEO_TS movies
Refs #281

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