source: trunk/SapphireMovieImporter.m @ 225

Revision 225, 11.6 KB checked in by pmerrill, 7 years ago (diff)
  • Movie Importer will now filter out (skip) any file extention that isn't a member of videoExtentions.
Line 
1//
2//  SapphireMovieImporter.m
3//  Sapphire
4//
5//  Created by Patrick Merrill on 9/10/07.
6//  Copyright 2007 www.nanopi.net. All rights reserved.
7//
8
9#import "SapphireMovieImporter.h"
10#import "SapphireMetaData.h"
11#import "NSString-Extensions.h"
12#import "SapphireMovieChooser.h"
13
14 /* IMDB XPATHS */
15#define IMDB_SEARCH_XPATH @"//td[starts-with(a/@href,'/title')]"
16#define IMDB_RESULT_LINK_XPATH @"a/@href"
17#define IMDB_RESULT_NAME_XPATH @"normalize-space(string())"
18 
19 
20#define TRANSLATIONS_KEY                @"Translations"
21#define LINK_KEY                                @"Link"
22
23/*Delegate class to download cover art*/
24@interface SapphireMovieDataMenuDownloadDelegate : NSObject
25{
26        NSString *destination;
27}
28- (id)initWithDest:(NSString *)dest;
29@end
30
31@implementation SapphireMovieDataMenuDownloadDelegate
32/*!
33* @brief Initialize a cover art downloader
34 *
35 * @param dest The path to save the file
36 */
37- (id)initWithDest:(NSString *)dest
38{
39        self = [super init];
40        if(!self)
41                return nil;
42       
43        destination = [dest retain];
44       
45        return self;
46}
47
48- (void)dealloc
49{
50        [destination release];
51        [super dealloc];
52}
53
54/*!
55* @brief Delegate Method which prompts for location to save file.  Override and set new
56 * destination
57 *
58 * @param download The downloader
59 * @param filename The suggested filename
60 */
61- (void)download:(NSURLDownload *)download decideDestinationWithSuggestedFilename:(NSString *)filename
62{
63        [download setDestination:destination allowOverwrite:YES];
64}
65@end
66
67@interface SapphireMovieImporter (private)
68- (void)writeSettings;
69@end
70
71@implementation SapphireMovieImporter
72
73- (id) initWithSavedSetting:(NSString *)path
74{
75        self = [super init];
76        if(!self)
77                return nil;
78       
79        /*Get the settings*/
80        settingsPath = [path retain];
81        NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:path];
82        /*Get or create the show translation dict*/
83        movieTranslations = [[settings objectForKey:TRANSLATIONS_KEY] mutableCopy];
84        if(movieTranslations == nil)
85                movieTranslations = [NSMutableDictionary new];
86        /*Cached show info*/
87        movieInfo = [NSMutableDictionary new];
88       
89        return self;
90}
91
92- (void)dealloc
93{
94        [dataMenu release];
95        [movieTranslations release];
96        [movieInfo release];
97        [settingsPath release];
98        [super dealloc];
99}
100
101/*!
102* @brief Sets the importer's data menu
103 *
104 * @param theDataMenu The importer's menu
105 */
106- (void)setImporterDataMenu:(SapphireImporterDataMenu *)theDataMenu
107{
108        [dataMenu release];
109        dataMenu = [theDataMenu retain];
110}
111
112/*!
113* @brief Fetch information for a movie
114 *
115 * @param movieName The IMDB name (part of the show's URL)
116 * @return A cached dictionary of the movie info
117 */
118- (NSMutableDictionary *)getMetaForMovie:(NSString *)movieName
119{
120        NSError *error = nil;
121        NSMutableDictionary *ret = [NSMutableDictionary dictionary];
122        /*Get the movie html*/
123        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.IMDB.com%@",movieName]];
124        NSXMLDocument *document = [[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyHTML error:&error];
125
126        NSString *movieTitle= [[document objectsForXQuery:@"//div[@id='tn15title']/h1/replace(string(), '\n', '')" error:&error] objectAtIndex:0];
127        /* Dump XML document to disk (Dev Only) */
128        //NSString *documentPath =[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/Sapphire/XML"];
129        //[[document XMLDataWithOptions:NSXMLNodePrettyPrint] writeToFile:[NSString stringWithFormat:@"/%@/%@_title_result.xml",documentPath,movieTitle] atomically:YES] ;
130               
131
132       
133        [ret setObject:movieTitle forKey:META_MOVIE_TITLE];
134        return ret;
135}
136
137/*!
138* @brief Fetches the screencap URL from a show's info URL
139 *
140 * @param epUrl The show's info URL
141 * @return The screencap URL if it exists, nil otherwise
142 */
143- (NSString *)getScreencapUrl:(NSString *)epUrl
144{
145        /*Get the HTML document*/
146//      NSURL *url = [NSURL URLWithString:epUrl];
147//      NSError *error = nil;
148//      NSXMLDocument *document = [[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyHTML error:&error];
149       
150        /*Search for a screencap*/
151//      NSXMLElement *html = [document rootElement];
152//      NSArray *caps = [html objectsForXQuery:TVRAGE_SCREEN_CAP_XPATH error:&error];
153//      if([caps count])
154                /*Found one; return it*/
155//              return [[(NSXMLElement *)[caps objectAtIndex:0] attributeForName:@"src"] stringValue];
156        /*None found*/
157        return nil;
158}
159
160/*!
161* @brief Searches for a movie based on the filename
162 *
163 * @param searchStr Part of the filename to use in the show search
164 * @return An array of possible results
165 */
166- (NSArray *)searchResultsForMovie:(NSString *)searchStr
167{
168        /* prep the search string */
169        searchStr = [searchStr stringByDeletingPathExtension];
170        searchStr = [searchStr stringByReplacingAllOccurancesOf:@"_" withString:@" "];
171        searchStr = [searchStr stringByReplacingAllOccurancesOf:@"." withString:@" "];
172        searchStr = [searchStr stringByReplacingAllOccurancesOf:@"-" withString:@" "];
173        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.imdb.com/find?s=all&q=%@", [searchStr URLEncode]]];
174        NSError *error = nil;
175        NSXMLDocument *document = [[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyHTML error:&error];
176        /* Dump XML document to disk (Dev Only) */
177        //NSString *documentPath =[NSHomeDirectory() stringByAppendingPathComponent:@"Library/Application Support/Sapphire/XML"];
178        //[[document XMLDataWithOptions:NSXMLNodePrettyPrint] writeToFile:[NSString stringWithFormat:@"%@/%@_search_result.xml",documentPath,searchStr] atomically:YES] ;
179       
180        NSXMLElement *root = [document rootElement];
181       
182        NSString *resultTitle=[[[root objectsForXQuery:@"//title" error:&error]objectAtIndex:0] stringValue];
183       
184        if([resultTitle isEqualToString:@"IMDb Search"])/*Make sure we didn't get back a unique result */
185        {
186                /*Get the results list*/
187                NSArray *results = [root objectsForXQuery:IMDB_SEARCH_XPATH error:&error];
188
189                //Need to clean out (VG) entries <Video Game>
190                NSMutableArray *ret = [NSMutableArray arrayWithCapacity:[results count]];
191                if([results count])
192                {
193                        /*Get each result*/
194                        NSEnumerator *resultEnum = [results objectEnumerator];
195                        NSXMLElement *result = nil;
196                        while((result = [resultEnum nextObject]) != nil)
197                        {
198                                /*Add the result to the list*/
199
200                                NSURL *resultURL = [NSURL URLWithString:[[[result objectsForXQuery:IMDB_RESULT_LINK_XPATH error:&error] objectAtIndex:0] stringValue]] ;
201                       
202                                if(resultURL == nil)
203                                        continue;
204                                {
205                                        [ret addObject:[NSDictionary dictionaryWithObjectsAndKeys:
206                                        [[result objectsForXQuery:IMDB_RESULT_NAME_XPATH error:&error] objectAtIndex:0], @"name",
207                                        [resultURL path], @"link",
208                                        nil]];
209                                }
210                        }
211                        return ret;
212                }
213        }
214        else /* IMDB directly linked to a unique movie title */
215        {
216                NSMutableArray *ret = [NSMutableArray arrayWithCapacity:1];
217                [ret addObject:[NSDictionary dictionaryWithObjectsAndKeys:
218                resultTitle, @"name",
219                [[url relativeString] stringByReplacingAllOccurancesOf:@"http://www.imdb.com" withString:@""], @"link",
220                nil]];
221                return ret ;
222        }
223        /*No results found*/
224        return nil;
225}
226
227
228/*!
229* @brief Write our setings out
230 */
231- (void)writeSettings
232{
233        NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
234                movieTranslations, TRANSLATIONS_KEY,
235                nil];
236        [settings writeToFile:settingsPath atomically:YES];
237}
238
239/*!
240* @brief verify file extention of a file
241 *
242 * @param filePAth The file's path
243 * @return YES if candidate, NO otherwise
244 */
245- (BOOL)isMovieCandidate:(NSString*)fileExt
246{
247        if([[SapphireMetaData videoExtensions] member:fileExt])
248                return YES;
249        else return NO ;
250}
251
252/*!
253* @brief Import a single File
254 *
255 * @param metaData The file to import
256 * @return YES if imported, NO otherwise
257 */
258- (BOOL) importMetaData:(SapphireFileMetaData *)metaData
259{
260        currentData = metaData;
261        /*Check to see if it is already imported*/
262        if([metaData importedTimeFromSource:META_IMDB_IMPORT_KEY])
263                return NO;
264        /*Get path*/
265        NSString *path = [metaData path];
266        if(![self isMovieCandidate:[path pathExtension]])
267                return NO;
268        /*Get fineName*/
269        NSString *fileName = [path lastPathComponent];
270       
271        if([metaData fileClass]==FILE_CLASS_TV_SHOW) /* File is a TV Show - skip it */
272                return NO ;
273       
274        /*Get the movie title*/
275        NSString *searchStr = fileName;
276        /*Check to see if we know this movie*/
277        NSString *movie = [movieTranslations objectForKey:[searchStr lowercaseString]];
278        if(movie == nil)
279        {
280                /*Ask the user what movie this is*/
281                NSArray *movies = [self searchResultsForMovie:searchStr];
282                /*Pause for the user's input*/
283                [dataMenu pause];
284                /*Bring up the prompt*/
285                SapphireMovieChooser *chooser = [[SapphireMovieChooser alloc] initWithScene:[dataMenu scene]];
286                [chooser setMovies:movies];
287                [chooser setFileName:fileName];         
288                [chooser setListTitle:BRLocalizedString(@"Select Movie Title", @"Prompt the user for title of movie")];
289                [chooser setSearchStr:searchStr];
290                /*And display prompt*/
291                [[dataMenu stack] pushController:chooser];
292                [chooser release];
293                return NO ;
294                //Data will be ready for access on the next exe
295        }
296       
297        /*Get The Release Date*/
298
299
300       
301       
302
303        NSMutableDictionary *info = nil;
304        info = [self getMetaForMovie:movie];
305        if(!info)
306                return NO;
307       
308       
309        /*Import the info*/
310        [info removeObjectForKey:LINK_KEY];
311        [metaData importInfo:info fromSource:META_IMDB_IMPORT_KEY withTime:[[NSDate date] timeIntervalSince1970]];
312        [metaData setFileClass:FILE_CLASS_MOVIE];
313       
314        /*We imported something*/
315        return YES;
316}
317
318
319/*!
320* @brief The completion text to display
321 *
322 * @return The completion text to display
323 */
324- (NSString *)completionText
325{
326        return BRLocalizedString(@"All availble Movie data has been imported", @"The Movie import is complete");
327}
328
329/*!
330* @brief The initial text to display
331 *
332 * @return The initial text to display
333 */
334- (NSString *)initialText
335{
336        return BRLocalizedString(@"Movie Meta Data", @"Title");
337}
338
339/*!
340* @brief The informative text to display
341 *
342 * @return The informative text to display
343 */
344- (NSString *)informativeText
345{
346        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");
347}
348
349/*!
350* @brief The button title
351 *
352 * @return The button title
353 */
354- (NSString *)buttonTitle
355{
356        return BRLocalizedString(@"Start Fetching Data", @"Button");
357}
358
359/*!
360* @brief The data menu was exhumed
361 *
362 * @param controller The Controller which was on top
363 */
364- (void) wasExhumedByPoppingController: (BRLayerController *) controller
365{
366        /*See if it was a movie chooser*/
367        if(![controller isKindOfClass:[SapphireMovieChooser class]])
368                return;
369       
370        /*Get the user's selection*/
371        SapphireMovieChooser *chooser = (SapphireMovieChooser *)controller;
372        int selection = [chooser selection];
373        if(selection == MOVIE_CHOOSE_CANCEL)
374                /*They aborted, skip*/
375                [dataMenu skipNextItem];
376        else if(selection == MOVIE_CHOOSE_NOT_MOVIE)
377        {
378                /*They said it is not a movie, so put in empty data so they are not asked again*/
379                [currentData importInfo:[NSMutableDictionary dictionary] fromSource:META_IMDB_IMPORT_KEY withTime:[[NSDate date] timeIntervalSince1970]];
380                if ([currentData fileClass] != FILE_CLASS_TV_SHOW)
381                        [currentData setFileClass:FILE_CLASS_UNKNOWN];
382        }
383        else if(selection==MOVIE_CHOOSE_OTHER)
384        {
385                [currentData importInfo:[NSMutableDictionary dictionary] fromSource:META_IMDB_IMPORT_KEY withTime:[[NSDate date] timeIntervalSince1970]];
386                [currentData setFileClass:FILE_CLASS_OTHER] ;
387        }
388        else if(selection==MOVIE_CHOOSE_TV_SHOW)
389        {
390                [currentData importInfo:[NSMutableDictionary dictionary] fromSource:META_IMDB_IMPORT_KEY withTime:[[NSDate date] timeIntervalSince1970]];
391                [currentData setFileClass:FILE_CLASS_TV_SHOW] ;
392        }
393        else
394        {
395                /*They selected a movie title, save the translation and write it*/
396                NSDictionary *movie = [[chooser movies] objectAtIndex:selection];
397                [movieTranslations setObject:[movie objectForKey:@"link"] forKey:[[chooser searchStr] lowercaseString]];
398                [self writeSettings];
399        }
400        /*We can resume now*/
401        [dataMenu resume];
402}
403
404@end
Note: See TracBrowser for help on using the repository browser.