source: trunk/SapphireFrappliance/MetaDataImporting/SapphireTVShowImporter.m @ 931

Revision 931, 24.6 KB checked in by gbooker, 5 years ago (diff)

Made detection of no summary more robust and corrected for changes in TVRage at the same time.

Line 
1/*
2 * SapphireTVShowImporter.m
3 * Sapphire
4 *
5 * Created by Graham Booker on Jun. 30, 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 <SapphireCompatClasses/SapphireFrontRowCompat.h>
22#import "SapphireTVShowImporter.h"
23#import "SapphireFileMetaData.h"
24#import "NSString-Extensions.h"
25#import "NSFileManager-Extensions.h"
26#import "SapphireShowChooser.h"
27#import "SapphireMovieChooser.h"
28#import "SapphireMetaDataSupport.h"
29#import "SapphireTVTranslation.h"
30#import "SapphireEpisode.h"
31#import "SapphireSettings.h"
32#import "NSImage-Extensions.h"
33
34
35/* TVRage XPATHS  */
36#define TVRAGE_SHOWNAME_XPATH @".//font[@size=2][@color=\"white\"]/b/text()"
37#define TVRAGE_EPLIST_XPATH @"//*[@class='b']"
38#define TVRAGE_EP_INFO @".//*[@class='b2']/*"
39#define TVRAGE_EP_TEXT @".//*[@class='b2']/text()"
40#define TVRAGE_SCREEN_CAP_XPATH @".//img[contains(@src, 'screencap')]"
41#define TVRAGE_SEARCH_XPATH @"//*[@class='b1']/a"
42#define TVRAGE_UNKNOWN_XPATH @"//*[contains(text(), 'Unknown Page')]"
43
44#define TRANSLATIONS_KEY                @"Translations"
45#define LINK_KEY                                @"Link"
46#define IMG_URL                                 @"imgURL"
47
48/*Delegate class to download cover art*/
49@interface SapphireTVShowDataMenuDownloadDelegate : NSObject
50{
51        NSString *destination;
52}
53- (id)initWithDest:(NSString *)dest;
54@end
55
56@implementation SapphireTVShowDataMenuDownloadDelegate
57/*!
58 * @brief Initialize a cover art downloader
59 *
60 * @param dest The path to save the file
61 */
62- (id)initWithDest:(NSString *)dest
63{
64        self = [super init];
65        if(!self)
66                return nil;
67       
68        destination = [dest retain];
69       
70        return self;
71}
72
73- (void)dealloc
74{
75        [destination release];
76        [super dealloc];
77}
78
79/*!
80 * @brief Delegate Method which prompts for location to save file.  Override and set new
81 * destination
82 *
83 * @param download The downloader
84 * @param filename The suggested filename
85 */
86- (void)download:(NSURLDownload *)download decideDestinationWithSuggestedFilename:(NSString *)filename
87{
88        [download setDestination:destination allowOverwrite:YES];
89}
90@end
91 
92@interface SapphireTVShowImporter (private)
93- (void)writeSettings;
94@end
95
96@implementation SapphireTVShowImporter
97
98- (id) initWithContext:(NSManagedObjectContext *)context
99{
100        self = [super init];
101        if(!self)
102                return nil;
103       
104        /*Get the settings*/
105        moc = [context retain];
106        /*Cached show info*/
107        showInfo = [NSMutableDictionary new];
108       
109        /*Initialize the regexes*/
110        regcomp(&letterMarking, "[\\. -]?S[0-9]+E[S0-9]+(-E?[0-9]+)?", REG_EXTENDED | REG_ICASE);
111        regcomp(&seasonByEpisode, "[\\. -]?[0-9]+x[S0-9]+(-[0-9]+)?", REG_EXTENDED | REG_ICASE);
112        regcomp(&seasonEpisodeTriple, "[\\. -][0-9]{1,3}[S0-9]{2}[\\. -]", REG_EXTENDED | REG_ICASE);   
113        return self;
114}
115
116- (void)dealloc
117{
118        [moc release];
119        [showInfo release];
120        regfree(&letterMarking);
121        regfree(&seasonByEpisode);
122        regfree(&seasonEpisodeTriple);
123        [chooser release];
124        [super dealloc];
125}
126
127- (void)setImporterDataMenu:(SapphireImporterDataMenu *)theDataMenu
128{
129        dataMenu = theDataMenu;
130}
131
132/*!
133 * @brief Add an episode's info into our cache dict
134 *
135 * @param showName The TV Show's name
136 * @param epTitle The episode's title
137 * @param season The episode's season
138 * @param ep The episodes's episode number within the season
139 * @param summary The episodes's summary
140 * @param eplink The episode's info URL
141 * @param epNumber The absolute episode number
142 * @param airDate The episode's air date
143 * @param imgURL The episode's screenshot URL
144 * @param dict The cache dictionary
145 */
146- (void)addEp:(NSString *)showName title:(NSString *)epTitle season:(int)season epNum:(int)ep summary:(NSString *)summary link:(NSString *)epLink absEpNum:(int)epNumber airDate:(NSDate *)airDate showID:(NSString *)showID imgURL:(NSString *)imgURL toDict:(NSMutableDictionary *)dict
147{
148        /*Set the key by which to store this.  Either by season/ep or season/title*/
149        SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DEBUG, @"Adding ep to show %@, title %@, season %d, num %d, sum %@ link %@, absNum %d, date %@, id %@, img %@", showName, epTitle, season, ep, summary, epLink, epNumber, airDate, showID, imgURL);
150        NSNumber *epNum = [NSNumber numberWithInt:ep];
151        id key = epNum;
152        if(ep == 0)
153                key = [epTitle lowercaseString];
154       
155        /*Get the ep dict*/
156        NSNumber *seasonNum = [NSNumber numberWithInt:season];
157        NSMutableDictionary *epDict = [dict objectForKey:key];
158        if(epDict == nil)
159        {
160                epDict = [NSMutableDictionary new];
161                [dict setObject:epDict forKey:key];
162                [epDict release];
163        }
164        /*Add info*/
165        if(showName)
166        {
167                [epDict setObject:showName forKey:META_SHOW_NAME_KEY] ;
168        }
169        if(ep != 0)
170                [epDict setObject:epNum forKey:META_EPISODE_NUMBER_KEY];
171        if(season != 0)
172        {
173                [epDict setObject:seasonNum forKey:META_SEASON_NUMBER_KEY];
174        }
175        if(epTitle != nil)
176                [epDict setObject:epTitle forKey:META_TITLE_KEY];
177        if(epLink != nil)
178                [epDict setObject:epLink forKey:LINK_KEY];
179        if(showID != nil)
180                [epDict setObject:showID forKey:META_SHOW_IDENTIFIER_KEY];
181        if(summary != nil)
182                [epDict setObject:summary forKey:META_DESCRIPTION_KEY];
183        if(epNumber != nil)
184                [epDict setObject:[NSNumber numberWithInt:epNumber] forKey:META_ABSOLUTE_EP_NUMBER_KEY];
185        if(airDate != nil)
186                [epDict setObject:airDate forKey:META_SHOW_AIR_DATE];
187        if(imgURL != nil)
188                [epDict setObject:imgURL forKey:IMG_URL];
189}
190
191/*!
192 * @brief Fetch information about a season of a show
193 *
194 * @param seriesName The tvrage series name (part of the show's URL)
195 * @param season The season to fech
196 * @return A cached dictionary of the season's episodes
197 */
198- (NSMutableDictionary *)getMetaForSeries:(NSString *)seriesName inSeason:(int)season
199{
200        NSMutableDictionary *ret = [NSMutableDictionary dictionary];
201        NSCharacterSet *decimalSet = [NSCharacterSet decimalDigitCharacterSet];
202        /*Get the season's html*/
203        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.tvrage.com%@/episode_guide/%d", seriesName, season]];
204        NSError *error = nil;
205        NSXMLDocument *document = [[[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyHTML error:&error] autorelease];
206        /* Dump XML document to disk (Dev Only) */
207/*      NSString *documentPath =[applicationSupportDir() stringByAppendingPathComponent:@"XML"];
208        [[document XMLDataWithOptions:NSXMLNodePrettyPrint] writeToFile:[NSString stringWithFormat:@"/%@%@.xml",documentPath,seriesName] atomically:YES] ;*/
209        /*Get the episode list*/
210        if(error != nil && ![[error domain] isEqualToString:@"NSXMLParserErrorDomain"])
211        {
212                /*Error fetching data; return nil*/
213                return nil;
214        }
215        NSString *showName= nil;
216       
217       
218        NSXMLElement *html = [document rootElement];
219       
220        NSArray *titleArray=[html objectsForXQuery:TVRAGE_SHOWNAME_XPATH error:&error];
221        if([titleArray count])
222        {
223                showName=[[titleArray objectAtIndex:0] stringValue];
224                int length = [showName length];
225                if([showName characterAtIndex:length - 1] == '\n')
226                        showName = [showName substringToIndex:length - 1];
227/*              int index = [showName rangeOfString:@"Season" options:0].location;
228                if(index != NSNotFound)
229                        showName = [showName substringToIndex:index - 1];*/
230        }
231
232        NSArray *eps = [html objectsForXQuery:TVRAGE_EPLIST_XPATH error:&error];
233        NSEnumerator *epEnum = [eps objectEnumerator];
234        NSXMLNode *epNode = nil;
235        while((epNode = [epEnum nextObject]) != nil)
236        {
237                /*Parse the episode's info*/
238                NSString *epTitle = nil;
239                NSString *link = nil;
240                int seasonNum = 0;
241                int ep = 0;
242                int epNumber = 0;
243                NSString *summary = nil;
244                NSDate *airDate = nil;
245                NSString *imageURL = nil;
246               
247                /*Get the info pieces*/
248                NSArray *epInfos = [epNode objectsForXQuery:TVRAGE_EP_INFO error:&error];
249                NSEnumerator *epInfoEnum = [epInfos objectEnumerator];
250                NSXMLNode *epInfo = nil;
251                while((epInfo = [epInfoEnum nextObject]) != nil)
252                {
253                        NSString *nodeName = [epInfo name];
254                        NSArray *summaryObjects = [epInfo objectsForXQuery:@".//font" error:&error];
255                        if([summaryObjects count] && ![nodeName isEqualToString:@"font"])
256                        {
257                                /*Sometimes, the summary is inside formatting, strip*/
258                                epInfo = [summaryObjects objectAtIndex:0];
259                                nodeName = [epInfo name];
260                        }
261                        if(link == nil && [nodeName isEqualToString:@"a"])
262                        {
263                                /*Get the URL*/
264                                link = [[(NSXMLElement *)epInfo attributeForName:@"href"] stringValue];
265                                link = [NSString stringWithFormat:@"http://www.tvrage.com%@", link];
266                                /*Parse the name*/
267                                NSString *epInfoStr = [[epInfo childAtIndex:0] stringValue];
268                                if(epInfoStr != nil)
269                                {
270                                        /*Get the season number and ep numbers*/
271                                        NSScanner *scanner = [NSScanner scannerWithString:epInfoStr];
272                                        NSRange range = [epInfoStr rangeOfString:@" - " options:0];
273                                        if(range.location != NSNotFound)
274                                        {
275                                                [scanner scanInt:&epNumber];
276                                                [scanner scanUpToCharactersFromSet:decimalSet intoString:nil];
277                                                [scanner scanInt:&seasonNum];
278                                                [scanner scanUpToCharactersFromSet:decimalSet intoString:nil];
279                                                [scanner scanInt:&ep];
280                                                [scanner setScanLocation:range.length + range.location];
281                                                if(seasonNum == 0)
282                                                        seasonNum = season;
283                                        }
284                                        else
285                                                seasonNum = season;
286                                        epTitle = [epInfoStr substringFromIndex:[scanner scanLocation]];
287                                }
288                        }
289                        else if(summary == nil && [nodeName isEqualToString:@"font"])
290                        {
291                                /*Get the summary*/
292                                NSArray *summaries = [epInfo objectsForXQuery:@"string()" error:&error];
293                                summary = [summaries componentsJoinedByString:@"\n"];
294                                NSMutableString *mutSummary = [summary mutableCopy];
295                                [mutSummary replaceOccurrencesOfString:@"\n\n" withString:@"\n" options:0 range:NSMakeRange(0, [summary length])];
296                                summary = [NSString stringWithString:mutSummary];
297                                [mutSummary release];
298                               
299                                if([summary hasSuffix:@"No Summary (Add Here)"])
300                                        summary = nil;
301                                if(![summary length])
302                                        summary = nil;
303                        }
304                        else if(imageURL == nil)
305                        {
306                                NSArray *images = [epInfo objectsForXQuery:TVRAGE_SCREEN_CAP_XPATH error:&error];
307                                if([images count])
308                                {
309                                        imageURL = [[(NSXMLElement *)[images objectAtIndex:0] attributeForName:@"src"] stringValue];
310                                }
311                        }
312                }
313                epInfos = [epNode objectsForXQuery:TVRAGE_EP_TEXT error:&error];
314                epInfoEnum = [epInfos objectEnumerator];
315                epInfo = nil;
316                while((epInfo = [epInfoEnum nextObject]) != nil)
317                {
318                        /*Get the air date*/
319                        NSString *nodeName = [epInfo stringValue];
320                        if ([nodeName hasPrefix:@" ("] && [nodeName hasSuffix:@") "])
321                        {
322                                NSString *subStr = [nodeName substringWithRange:NSMakeRange(2, [nodeName length] - 4)];
323                               
324                                airDate = [NSDate dateWithNaturalLanguageString:subStr];
325                                if([airDate timeIntervalSince1970] == 0)
326                                        airDate = nil;
327                                else
328                                        break;
329                        }
330                }
331                /*Add to cache*/
332                [self addEp:showName title:epTitle season:seasonNum epNum:ep summary:summary link:link absEpNum:epNumber airDate:airDate showID:seriesName imgURL:imageURL toDict:ret];
333        }
334        return ret;
335}
336
337/*!
338 * @brief Searches for a show based on the filename
339 *
340 * @param searchStr Part of the filename to use in the show search
341 * @return An array of possible results
342 */
343- (NSArray *)searchResultsForSeries:(NSString *)searchStr
344{
345        /*Load the search info*/
346        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.tvrage.com/search.php?search=%@&sonly=1", [searchStr URLEncode]]];
347        NSError *error = nil;
348        NSXMLDocument *document = [[[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyHTML error:&error] autorelease];
349        SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DEBUG, @"Document is %@", document);
350        if(error != nil && ![[error domain] isEqualToString:@"NSXMLParserErrorDomain"])
351        {
352                /*Error fetching data*/
353                SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DEBUG, @"Got Error %@, %@", [error domain], [error userInfo]);
354                return nil;
355        }
356       
357        /*Get the results list*/
358        NSXMLElement *root = [document rootElement];
359        NSArray *results = [root objectsForXQuery:TVRAGE_SEARCH_XPATH error:&error];
360        SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DEBUG, @"Got raw search results %@", results);
361        NSMutableArray *ret = [NSMutableArray arrayWithCapacity:[results count]];
362        if([results count])
363        {
364                /*Get each result*/
365                NSEnumerator *resultEnum = [results objectEnumerator];
366                NSXMLElement *result = nil;
367                while((result = [resultEnum nextObject]) != nil)
368                {
369                        /*Add the result to the list*/
370                        NSURL *resultURL = [NSURL URLWithString:[[result attributeForName:@"href"] stringValue]];
371                        if(resultURL == nil)
372                                continue;
373                        [ret addObject:[NSDictionary dictionaryWithObjectsAndKeys:
374                                [[result childAtIndex:0] stringValue], @"name",
375                                [resultURL path], LINK_KEY,
376                                nil]];
377                }
378                return ret;
379        }
380        /*No results found*/
381        return [NSArray array];
382}
383
384/*!
385 * @brief Fetch cached information about a show's season, and if none, fetch it
386 *
387 * @param show The tvrage series name
388 * @param season The season to fetch
389 * @return A dictionary with info about the season
390 */
391- (NSMutableDictionary *)getInfo:(NSString *)show forSeason:(int)season
392{
393        /*Get the show's info*/
394        NSMutableDictionary *showDict = [showInfo objectForKey:show];
395        NSMutableDictionary *seasonDict = nil;
396        NSNumber *seasonNum = [NSNumber numberWithInt:season];
397        if(!showDict)
398        {
399                showDict = [NSMutableDictionary new];
400                [showInfo setObject:showDict forKey:show];
401                [showDict release];
402        }
403        else
404                /*Get the season's info*/
405                seasonDict = [showDict objectForKey:seasonNum];
406        if(!seasonDict)
407        {
408                /*Not in cache, so fetch it*/
409                seasonDict = [self getMetaForSeries:show inSeason:season];
410                if(seasonDict != nil)
411                        /*Put the result in cache*/
412                        [showDict setObject:seasonDict forKey:seasonNum];
413        }
414        /*Return the info*/
415        return seasonDict;
416}
417
418/*!
419 * @brief Get info about a show's episode from a key
420 *
421 * @param show The tvrage show name
422 * @param season The episode's season
423 * @param key The lookup key to use
424 * @return nil if network failure, empty dictionary if info doesn't exist, otherwise the episode's info
425 */
426- (NSMutableDictionary *)getInfo:(NSString *)show forSeason:(int)season key:(id)key
427{
428        NSMutableDictionary *seasonDict = [self getInfo:show forSeason:season];
429        if(seasonDict == nil)
430                return nil;
431        NSDictionary *ret = [seasonDict objectForKey:key];
432        if(ret != nil)
433                return [NSMutableDictionary dictionaryWithDictionary:ret];
434        return [NSDictionary dictionary];
435}
436
437/*!
438 * @brief Get info about a show's episode
439 *
440 * @param show The tvrage show name
441 * @param season The episode's season
442 * @param ep The episode's episode number
443 * @return nil if network failure, empty dictionary if info doesn't exist, otherwise the episode's info
444 */
445- (NSMutableDictionary *)getInfo:(NSString *)show forSeason:(int)season episode:(int)ep
446{
447        NSNumber *epNum = [NSNumber numberWithInt:ep];
448        return [self getInfo:show forSeason:season key:epNum];
449}
450
451/*!
452 * @brief Get info about a show's episode which doesn't have an episode number (specials)
453 *
454 * @param show The tvrage show name
455 * @param season The episode's season
456 * @param epTitle The episode's title
457 * @return nil if network failure, empty dictionary if info doesn't exist, otherwise the episode's info
458 */
459- (NSMutableDictionary *)getInfo:(NSString *)show forSeason:(int)season episodeTitle:(NSString *)epTitle
460{
461        return [self getInfo:show forSeason:season key:[epTitle lowercaseString]];
462}
463
464/*!
465 * @brief Write our setings out
466 */
467- (void)writeSettings
468{
469        NSError *error = nil;
470        [moc save:&error];
471}
472
473- (ImportState)importMetaData:(SapphireFileMetaData *)metaData path:(NSString *)path
474{
475        currentData = metaData;
476        /*Check to see if it is already imported*/
477        if([metaData importTypeValue] & IMPORT_TYPE_TVSHOW_MASK)
478                return IMPORT_STATE_NOT_UPDATED;
479        id controller = [[dataMenu stack] peekController];
480        /* Check to see if we are waiting on the user to select a movie title */
481        if(controller != nil && ![controller isKindOfClass:[SapphireImporterDataMenu class]])
482        {
483                /* Another chooser is on the screen - delay further processing */
484                return IMPORT_STATE_NOT_UPDATED;
485        }
486//      NSArray *pathComponents = [path pathComponents];
487        NSString *fileName = [path lastPathComponent];
488       
489        /*Check regexes to see if this is a tv show*/
490        int index = NSNotFound;
491        int secondEp = -1;
492        regmatch_t matches[3];
493        const char *theFileName = [fileName fileSystemRepresentation];
494        NSString *scanString = nil;
495        if(!regexec(&letterMarking, theFileName, 3, matches, 0))
496        {
497                index = matches[0].rm_so;
498                scanString = [fileName substringFromIndex:index];
499                secondEp = matches[1].rm_so;
500        }
501        else if(!regexec(&seasonByEpisode, theFileName, 3, matches, 0))
502        {
503                index = matches[0].rm_so;
504                scanString = [fileName substringFromIndex:index];
505                secondEp = matches[1].rm_so;
506        }
507        else if(!regexec(&seasonEpisodeTriple, theFileName, 3, matches, 0))
508        {
509                index = matches[0].rm_so;
510                /*Insert an artificial season/ep seperator so things are easier later*/
511                NSMutableString *tempStr = [fileName mutableCopy];
512//              if(index > [tempStr length] || index <= 0 )return NO;
513                [tempStr deleteCharactersInRange:NSMakeRange(0, index+1)];
514                [tempStr insertString:@"x" atIndex:matches[0].rm_eo - index - 4];
515                scanString = [tempStr autorelease];
516        }
517       
518        /*See if we found a match*/
519        if(index == NSNotFound)
520                return IMPORT_STATE_NOT_UPDATED;
521       
522        /*Get the show title*/
523        NSString *searchStr = [fileName substringToIndex:index];
524        NSString *searchShowName = [metaData searchShowName];
525        if(searchShowName != nil)
526                searchStr = searchShowName;
527        SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DEBUG, @"%@ matched regex; checking show name %@", fileName, searchStr);
528        /*Check to see if we know this title*/
529        SapphireTVTranslation *tran = [SapphireTVTranslation tvTranslationForName:[searchStr lowercaseString] inContext:moc];
530        if([tran showPath] == nil)
531        {
532                SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DEBUG, @"Conducting search");
533                if(dataMenu == nil)
534                        /*There is no data menu, background import. So we can't ask user, skip*/
535                        return IMPORT_STATE_NOT_UPDATED;
536                /*Ask the user what show this is*/
537                NSArray *shows = [self searchResultsForSeries:searchStr];
538                SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DEBUG, @"Found shows %@", shows);
539                if(shows == nil)
540                        return IMPORT_STATE_NOT_UPDATED;
541               
542                if([[SapphireSettings sharedSettings] autoSelection])
543                {
544                        NSString *path = [[shows objectAtIndex:0] objectForKey:LINK_KEY];
545                        if(tran == nil)
546                                tran = [SapphireTVTranslation createTVTranslationForName:[searchStr lowercaseString] withPath:path inContext:moc];
547                        else
548                                [tran setShowPath:path];
549                }
550                else
551                {
552                        /*Bring up the prompt*/
553                        chooser = [[SapphireShowChooser alloc] initWithScene:[dataMenu scene]];
554                        [chooser setShows:shows];
555                        [chooser setFileName:fileName];
556                        [chooser setListTitle:BRLocalizedString(@"Select Show Title", @"Prompt the user for showname with a file")];
557                        [chooser setSearchStr:searchStr];
558                        /*And display prompt*/
559                        [[dataMenu stack] pushController:chooser];
560                        return IMPORT_STATE_NEEDS_SUSPEND;
561                }
562        }
563       
564        int season = 0;
565        int ep = 0;
566        /*Get the season*/
567        NSScanner *scanner = [NSScanner scannerWithString:scanString];
568        NSCharacterSet *digits = [NSCharacterSet decimalDigitCharacterSet];
569        [scanner scanUpToCharactersFromSet:digits intoString:nil];
570        [scanner scanInt:&season];
571        /*Get the episode number*/
572        NSString *skipped = nil;
573        [scanner scanUpToCharactersFromSet:digits intoString:&skipped];
574        [scanner scanInt:&ep];
575        /*Was there an S before the episode number?*/
576        if([skipped hasSuffix:@"S"] || [skipped hasSuffix:@"s"])
577                ep = 0;
578       
579        int overriddenSeason = [metaData searchSeasonNumber];
580        if(overriddenSeason != -1)
581                season = overriddenSeason;
582
583        int overriddenEpisode = [metaData searchEpisodeNumber];
584        if(overriddenEpisode != -1)
585                ep = overriddenEpisode;
586
587        /*No season, no info*/
588        if(season == 0)
589                return IMPORT_STATE_NOT_UPDATED;
590       
591        int otherEp = 0;
592        if(secondEp != -1)
593        {
594                [scanner setScanLocation:secondEp - index];
595                [scanner scanUpToCharactersFromSet:digits intoString:nil];
596                [scanner scanInt:&otherEp];
597        }
598       
599        overriddenEpisode = [metaData searchLastEpisodeNumber];
600        if(overriddenEpisode != -1)
601                otherEp = overriddenEpisode;
602
603        /*Get the episode's info*/
604        NSMutableDictionary *info = nil, *info2 = nil;
605        if(ep != 0)
606        {
607                SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DEBUG, @"Importing TV show %@ %d %d %d", [tran showPath], season, ep, otherEp);
608                /*Match on s/e*/
609                info = [self getInfo:[tran showPath] forSeason:season episode:ep];
610                if(otherEp != 0)
611                        info2 = [self getInfo:[tran showPath] forSeason:season episode:otherEp];
612        }
613        else
614        {
615                /*Match on show title*/
616                NSString *showTitle = nil;
617                [scanner scanUpToCharactersFromSet:[NSCharacterSet alphanumericCharacterSet] intoString:nil];
618                if([scanner scanUpToString:@"." intoString:&showTitle])
619                        info = [self getInfo:[tran showPath] forSeason:season episodeTitle:showTitle];
620                SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DEBUG, @"Searching for title \"%@\"", showTitle);
621        }
622        /*No info, well, no info, so we didn't import*/
623        if(info == nil)
624                return IMPORT_STATE_NOT_UPDATED;
625        if(![info count])
626        {
627                /*Our search was successful, but there's no tv episode data, so it doesn't try to import again.*/
628                [metaData didImportType:IMPORT_TYPE_TVSHOW_MASK];
629                return IMPORT_STATE_NOT_UPDATED;
630        }
631       
632        SapphireLog(SAPPHIRE_LOG_IMPORT, SAPPHIRE_LOG_LEVEL_DETAIL, @"Import info is %@", info);
633               
634        /* Lets process the cover art directory structure */
635        NSString * previewArtPath = [NSFileManager previewArtPathForTV:[info objectForKey:META_SHOW_NAME_KEY] season:[[info objectForKey:META_SEASON_NUMBER_KEY] intValue]];
636                                               
637        [[NSFileManager defaultManager] constructPath:previewArtPath];
638        /*Check for screen cap locally and on server*/
639        NSString *imgURL = [info objectForKey:IMG_URL];
640        NSString *newPath = nil;
641        if(ep != 0)
642                newPath = [previewArtPath stringByAppendingFormat:@"/Episode %d", ep];
643        else
644                newPath = [previewArtPath stringByAppendingPathComponent:[info objectForKey:META_TITLE_KEY]];
645        NSString *imageDestination = [newPath stringByAppendingPathExtension:@"jpg"];
646        BOOL isDir = NO;
647        BOOL imageExists = [[NSFileManager defaultManager] fileExistsAtPath:imageDestination isDirectory:&isDir] && !isDir;
648        if(imgURL && !imageExists)
649        {
650                /*Download the screen cap*/
651                NSURL *imageURL = [NSURL URLWithString:imgURL];
652                NSURLRequest *request = [NSURLRequest requestWithURL:imageURL];
653                SapphireTVShowDataMenuDownloadDelegate *myDelegate = [[SapphireTVShowDataMenuDownloadDelegate alloc] initWithDest:imageDestination];
654                [[[NSURLDownload alloc] initWithRequest:request delegate:myDelegate] autorelease];
655                [myDelegate release];
656        }
657        else if(!imageExists)
658        {
659                //QTMovie is broken on ATV, don't fetch images there
660                if ([SapphireFrontRowCompat usingLeopard] && [metaData fileContainerTypeValue] == FILE_CONTAINER_TYPE_QT_MOVIE)
661                {
662                        // NSImage-Extensions
663                        [[NSImage imageFromMovie:path] writeToFile:imageDestination atomically:YES];
664                }
665        }
666       
667        /*Import the info*/
668        [info removeObjectForKey:LINK_KEY];
669        NSMutableArray *dictionaries = [NSMutableArray arrayWithObjects:info, info2, nil];
670        SapphireEpisode *episode = [SapphireEpisode episodeWithDictionaries:dictionaries inContext:moc];
671        [metaData setTvEpisode:episode];
672        [tran setTvShow:[episode tvShow]];
673       
674        /*We imported something*/
675        return IMPORT_STATE_UPDATED;
676}
677
678- (NSString *)completionText
679{
680        return BRLocalizedString(@"All available TV Show data has been imported", @"The TV Show import complete");
681}
682
683- (NSString *)initialText
684{
685        return BRLocalizedString(@"Fetch TV Show Data", @"Title");
686}
687
688- (NSString *)informativeText
689{
690        return BRLocalizedString(@"This tool will attempt to fetch information about your TV shows files from the Internet (TVRage).  This procedure may take quite some time and could ask you questions.  You may cancel at any time.", @"Description of the movie import");
691}
692
693- (NSString *)buttonTitle
694{
695        return BRLocalizedString(@"Start Fetching Data", @"Button");
696}
697
698- (void)wasExhumed
699{
700        /*See if it was a show chooser*/
701        if(chooser == nil)
702                return;
703       
704        /*Get the user's selection*/
705        int selection = [chooser selection];
706        if(selection == SHOW_CHOOSE_CANCEL)
707                /*They aborted, skip*/
708                [dataMenu skipNextItem];
709        else if(selection == SHOW_CHOOSE_NOT_SHOW)
710        {
711                /*They said it is not a show, so put in empty data so they are not asked again*/
712                [currentData didImportType:IMPORT_TYPE_TVSHOW_MASK];
713                if ([currentData fileClassValue] == FILE_CLASS_TV_SHOW)
714                        [currentData setFileClassValue:FILE_CLASS_UNKNOWN];
715        }
716        else
717        {
718                /*They selected a show, save the translation and write it*/
719                NSDictionary *show = [[chooser shows] objectAtIndex:selection];
720                [SapphireTVTranslation createTVTranslationForName:[[chooser searchStr] lowercaseString] withPath:[show objectForKey:LINK_KEY] inContext:moc];
721                [self writeSettings];
722        }
723        [chooser release];
724        chooser = nil;
725        /*We can resume now*/
726        [dataMenu resume];
727}
728
729@end
Note: See TracBrowser for help on using the repository browser.