source: trunk/SapphireTVShowDataMenu.m @ 132

Revision 132, 14.7 KB checked in by gbooker, 7 years ago (diff)

Use the prefix header to import a bunch of things, and thus remove them from idividual header files.

No code change, just complier stuff.

Line 
1//
2//  SapphireTVShowDataMenu.m
3//  Sapphire
4//
5//  Created by Graham Booker on 6/30/07.
6//  Copyright 2007 __www.nanopi.net__. All rights reserved.
7//
8
9#import "SapphireTVShowDataMenu.h"
10#import "SapphireMetaData.h"
11#import "NSString-Extensions.h"
12#import "SapphireShowChooser.h"
13
14#define TVRAGE_EPLIST_XPATH @"//*[@class='b']"
15#define TVRAGE_EP_INFO @".//*[@class='b2']/*"
16#define TVRAGE_EP_TEXT @".//*[@class='b2']/text()"
17#define TVRAGE_SCREEN_CAP_XPATH @"//img[contains(@src, 'screencap')]"
18#define TVRAGE_SEARCH_XPATH @"//*[@class='b1']/a"
19#define TVRAGE_UNKNOWN_XPATH @"//*[contains(text(), 'Unknown Page')]"
20 
21#define LINK_KEY @"Link"
22
23#define TRANSLATIONS_KEY                @"Translations"
24
25@interface SapphireTVShowDataMenuDownloadDelegate : NSObject
26{
27        NSString *destination;
28}
29- (id)initWithDest:(NSString *)dest;
30@end
31
32@implementation SapphireTVShowDataMenuDownloadDelegate
33- (id)initWithDest:(NSString *)dest
34{
35        self = [super init];
36        if(!self)
37                return nil;
38       
39        destination = [dest retain];
40       
41        return self;
42}
43
44- (void)dealloc
45{
46        [destination release];
47        [super dealloc];
48}
49
50- (void)download:(NSURLDownload *)download decideDestinationWithSuggestedFilename:(NSString *)filename
51{
52        [download setDestination:destination allowOverwrite:YES];
53}
54@end
55 
56@interface SapphireImporterDataMenu (private)
57- (void)setText:(NSString *)theText;
58- (void)setFileProgress:(NSString *)updateFileProgress;
59- (void)resetUIElements;
60- (void)importNextItem:(NSTimer *)timer;
61- (void)setCurrentFile:(NSString *)theCurrentFile;
62- (void)pause;
63- (void)resume;
64- (void)skipNextItem;
65@end
66
67@interface SapphireTVShowDataMenu (private)
68- (void)writeSettings;
69@end
70
71@implementation SapphireTVShowDataMenu
72
73- (id) initWithScene: (BRRenderScene *) scene metaData:(SapphireDirectoryMetaData *)metaData savedSetting:(NSString *)path
74{
75        self = [super initWithScene:scene metaData:metaData];
76        if(!self)
77                return nil;
78       
79        settingsPath = [path retain];
80        NSDictionary *settings = [NSDictionary dictionaryWithContentsOfFile:path];
81        showTranslations = [[settings objectForKey:TRANSLATIONS_KEY] mutableCopy];
82        if(showTranslations == nil)
83                showTranslations = [NSMutableDictionary new];
84        showInfo = [NSMutableDictionary new];
85       
86        regcomp(&letterMarking, "[\\. -]?S[0-9]+E[S0-9]+", REG_EXTENDED | REG_ICASE);
87        regcomp(&seasonByEpisode, "[\\. -]?[0-9]+x[S0-9]+", REG_EXTENDED | REG_ICASE);
88        regcomp(&seasonEpisodeTriple, "[\\. -][0-9]{1,3}[S0-9]{2}[\\. -]", REG_EXTENDED | REG_ICASE);   
89        return self;
90}
91
92- (void)dealloc
93{
94        [showTranslations release];
95        [showInfo release];
96        [settingsPath release];
97        regfree(&letterMarking);
98        regfree(&seasonByEpisode);
99        regfree(&seasonEpisodeTriple);
100        [super dealloc];
101}
102
103- (NSString *)getScreencapUrl:(NSString *)epUrl
104{
105        NSURL *url = [NSURL URLWithString:epUrl];
106        NSError *error = nil;
107        NSXMLDocument *document = [[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyHTML error:&error];
108       
109        NSXMLElement *html = [document rootElement];
110        NSArray *caps = [html objectsForXQuery:TVRAGE_SCREEN_CAP_XPATH error:&error];
111        if([caps count])
112                return [[(NSXMLElement *)[caps objectAtIndex:0] attributeForName:@"src"] stringValue];
113        return nil;
114}
115
116- (void)addEp:(NSString *)epTitle season:(int)season epNum:(int)ep summary:(NSString *)summary link:(NSString *)epLink  absEpNum:(int)epNumber airDate:(NSDate *)airDate toDict:(NSMutableDictionary *)dict
117{
118        NSNumber *epNum = [NSNumber numberWithInt:ep];
119        id key = epNum;
120        if(ep == 0)
121                key = [epTitle lowercaseString];
122       
123        NSNumber *seasonNum = [NSNumber numberWithInt:season];
124        NSMutableDictionary *epDict = [dict objectForKey:key];
125        if(epDict == nil)
126        {
127                epDict = [NSMutableDictionary new];
128                [dict setObject:epDict forKey:key];
129                [epDict release];
130        }
131        if(ep != 0)
132                [epDict setObject:epNum forKey:META_EPISODE_NUMBER_KEY];
133        if(season != 0)
134                [epDict setObject:seasonNum forKey:META_SEASON_NUMBER_KEY];
135        if(epTitle != nil)
136                [epDict setObject:epTitle forKey:META_TITLE_KEY];
137        if(epLink != nil)
138                [epDict setObject:epLink forKey:LINK_KEY];
139        if(summary != nil)
140                [epDict setObject:summary forKey:META_DESCRIPTION_KEY];
141        if(epNumber != nil)
142                [epDict setObject:[NSNumber numberWithInt:epNumber] forKey:META_ABSOLUTE_EP_NUMBER_KEY];
143        if(airDate != nil)
144                [epDict setObject:airDate forKey:META_SHOW_AIR_DATE];
145}
146
147- (NSMutableDictionary *)getMetaForSeries:(NSString *)seriesName inSeason:(int)season
148{
149        NSMutableDictionary *ret = [NSMutableDictionary dictionary];
150        NSCharacterSet *decimalSet = [NSCharacterSet decimalDigitCharacterSet];
151        NSCharacterSet *skipSet = [NSCharacterSet characterSetWithCharactersInString:@"- "];
152        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.tvrage.com%@/episode_guide/%d", seriesName, season]];
153        NSError *error = nil;
154        NSXMLDocument *document = [[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyHTML error:&error];
155       
156        NSXMLElement *html = [document rootElement];
157        NSArray *eps = [html objectsForXQuery:TVRAGE_EPLIST_XPATH error:&error];
158        NSEnumerator *epEnum = [eps objectEnumerator];
159        NSXMLNode *epNode = nil;
160        while((epNode = [epEnum nextObject]) != nil)
161        {
162                NSString *epTitle = nil;
163                NSString *link = nil;
164                int season = 0;
165                int ep = 0;
166                int epNumber = 0;
167                NSMutableString *summary = nil;
168                NSDate *airDate = nil;
169               
170                NSArray *epInfos = [epNode objectsForXQuery:TVRAGE_EP_INFO error:&error];
171                NSEnumerator *epInfoEnum = [epInfos objectEnumerator];
172                NSXMLNode *epInfo = nil;
173                while((epInfo = [epInfoEnum nextObject]) != nil)
174                {
175                        NSString *nodeName = [epInfo name];
176                        if(link == nil && [nodeName isEqualToString:@"a"])
177                        {
178                                link = [[(NSXMLElement *)epInfo attributeForName:@"href"] stringValue];
179                                link = [NSString stringWithFormat:@"http://www.tvrage.com%@", link];
180                                NSString *epInfoStr = [[epInfo childAtIndex:0] stringValue];
181                                if(epInfoStr != nil)
182                                {
183                                        NSScanner *scanner = [NSScanner scannerWithString:epInfoStr];
184                                        if([epInfoStr rangeOfString:@" - " options:0].location != NSNotFound)
185                                        {
186                                                [scanner scanInt:&epNumber];
187                                                [scanner scanUpToCharactersFromSet:decimalSet intoString:nil];
188                                                [scanner scanInt:&season];
189                                                [scanner scanUpToCharactersFromSet:decimalSet intoString:nil];
190                                                [scanner scanInt:&ep];
191                                                [scanner scanCharactersFromSet:skipSet intoString:nil];                                                 
192                                        }
193                                        epTitle = [epInfoStr substringFromIndex:[scanner scanLocation]];
194                                }
195                        }
196                        else if(summary == nil && [nodeName isEqualToString:@"font"])
197                        {
198                                NSArray *summarys = [epInfo objectsForXQuery:@"text()" error:&error];
199                                summary = [NSMutableString string];
200                                NSEnumerator *sumEnum = [summarys objectEnumerator];
201                                NSXMLNode *sum = nil;
202                                while((sum = [sumEnum nextObject]) != nil)
203                                        [summary appendFormat:@"\n%@", [sum stringValue]];
204                                if([summary length])
205                                        [summary deleteCharactersInRange:NSMakeRange(0,1)];
206                                else
207                                        summary = nil;
208                        }
209                }
210                epInfos = [epNode objectsForXQuery:TVRAGE_EP_TEXT error:&error];
211                epInfoEnum = [epInfos objectEnumerator];
212                epInfo = nil;
213                while((epInfo = [epInfoEnum nextObject]) != nil)
214                {
215                        NSString *nodeName = [epInfo stringValue];
216                        if ([nodeName hasPrefix:@" ("] && [nodeName hasSuffix:@") "])
217                        {
218                                NSString *subStr = [nodeName substringWithRange:NSMakeRange(2, [nodeName length] - 4)];
219                               
220                                airDate = [NSDate dateWithNaturalLanguageString:subStr];
221                                if([airDate timeIntervalSince1970] == 0)
222                                        airDate = nil;
223                                else
224                                        break;
225                        }
226                }
227                [self addEp:epTitle season:season epNum:ep summary:summary link:link absEpNum:epNumber airDate:airDate toDict:ret];
228        }
229        return ret;
230}
231
232- (NSArray *)searchResultsForSeries:(NSString *)searchStr
233{
234        NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:@"http://www.tvrage.com/search.php?search=%@&sonly=1", [searchStr URLEncode]]];
235        NSError *error = nil;
236        NSXMLDocument *document = [[NSXMLDocument alloc] initWithContentsOfURL:url options:NSXMLDocumentTidyHTML error:&error];
237       
238        NSXMLElement *root = [document rootElement];
239        NSArray *results = [root objectsForXQuery:TVRAGE_SEARCH_XPATH error:&error];
240        NSMutableArray *ret = [NSMutableArray arrayWithCapacity:[results count]];
241        if([results count])
242        {
243                NSEnumerator *resultEnum = [results objectEnumerator];
244                NSXMLElement *result = nil;
245                while((result = [resultEnum nextObject]) != nil)
246                {
247                        NSURL *resultURL = [NSURL URLWithString:[[result attributeForName:@"href"] stringValue]];
248                        if(resultURL == nil)
249                                continue;
250                        [ret addObject:[NSDictionary dictionaryWithObjectsAndKeys:
251                                [[result childAtIndex:0] stringValue], @"name",
252                                [resultURL path], @"link",
253                                nil]];
254                }
255                return ret;
256        }
257        return nil;
258}
259
260- (NSMutableDictionary *)getInfo:(NSString *)show forSeason:(int)season
261{
262        NSMutableDictionary *showDict = [showInfo objectForKey:show];
263        NSMutableDictionary *seasonDict = nil;
264        NSNumber *seasonNum = [NSNumber numberWithInt:season];
265        if(!showDict)
266        {
267                showDict = [NSMutableDictionary new];
268                [showInfo setObject:showDict forKey:show];
269                [showDict release];
270        }
271        else
272                seasonDict = [showDict objectForKey:seasonNum];
273        if(!seasonDict)
274        {
275                seasonDict = [self getMetaForSeries:show inSeason:season];
276                if(seasonDict != nil)
277                        [showDict setObject:seasonDict forKey:seasonNum];
278        }
279        return seasonDict;
280}
281
282- (NSMutableDictionary *)getInfo:(NSString *)show forSeason:(int)season episode:(int)ep
283{
284        NSNumber *epNum = [NSNumber numberWithInt:ep];
285        return [NSMutableDictionary dictionaryWithDictionary:[[self getInfo:show forSeason:season] objectForKey:epNum]];
286}
287
288- (NSMutableDictionary *)getInfo:(NSString *)show forSeason:(int)season episodeTitle:(NSString *)epTitle
289{
290        return [NSMutableDictionary dictionaryWithDictionary:[[self getInfo:show forSeason:season] objectForKey:[epTitle lowercaseString]]];
291}
292
293- (void)writeSettings
294{
295        NSDictionary *settings = [NSDictionary dictionaryWithObjectsAndKeys:
296                showTranslations, TRANSLATIONS_KEY,
297                nil];
298        [settings writeToFile:settingsPath atomically:YES];
299}
300
301- (void)getItems
302{
303        importItems = [[meta subFileMetas] mutableCopy];
304}
305
306- (BOOL)doImport
307{
308        SapphireFileMetaData *fileMeta = [importItems objectAtIndex:0];
309        if([fileMeta importedTimeFromSource:META_TVRAGE_IMPORT_KEY])
310                return NO;
311        NSString *path = [fileMeta path];
312//      NSArray *pathComponents = [path pathComponents];
313        NSString *fileName = [path lastPathComponent];
314       
315        int index = NSNotFound;
316        regmatch_t matches[3];
317        const char *theFileName = [fileName fileSystemRepresentation];
318        NSString *scanString = nil;
319        if(!regexec(&letterMarking, theFileName, 3, matches, 0))
320        {
321                index = matches[0].rm_so;
322                scanString = [fileName substringFromIndex:index];
323        }
324        else if(!regexec(&seasonByEpisode, theFileName, 3, matches, 0))
325        {
326                index = matches[0].rm_so;
327                scanString = [fileName substringFromIndex:index];
328        }
329        else if(!regexec(&seasonEpisodeTriple, theFileName, 3, matches, 0))
330        {
331                index = matches[0].rm_so + 1;
332                NSMutableString *tempStr = [fileName mutableCopy];
333                [tempStr deleteCharactersInRange:NSMakeRange(0, index)];
334                [tempStr insertString:@"x" atIndex:matches[0].rm_eo - index - 3];
335                scanString = [tempStr autorelease];
336        }
337       
338        if(index == NSNotFound)
339                return NO;
340       
341        NSString *searchStr = [fileName substringToIndex:index];
342        NSString *show = [showTranslations objectForKey:[searchStr lowercaseString]];
343        if(show == nil)
344        {
345                NSArray *shows = [self searchResultsForSeries:searchStr];
346                [self pause];
347                SapphireShowChooser *chooser = [[SapphireShowChooser alloc] initWithScene:[self scene]];
348                [chooser setShows:shows];
349                [chooser setListTitle:[@"Show? " stringByAppendingString:fileName]];
350                [chooser setSearchStr:searchStr];
351                [[self stack] pushController:chooser];
352                return NO;
353        }
354       
355        int season = 0;
356        int ep = 0;
357        NSScanner *scanner = [NSScanner scannerWithString:scanString];
358        NSCharacterSet *digits = [NSCharacterSet decimalDigitCharacterSet];
359        [scanner scanUpToCharactersFromSet:digits intoString:nil];
360        [scanner scanInt:&season];
361        NSString *skipped = nil;
362        [scanner scanUpToCharactersFromSet:digits intoString:&skipped];
363        [scanner scanInt:&ep];
364        if([skipped hasSuffix:@"S"])
365                ep = 0;
366        if(season == 0)
367                return NO;
368       
369        NSMutableDictionary *info = nil;
370        if(ep != 0)
371                info = [self getInfo:show forSeason:season episode:ep];
372        else
373        {
374                NSString *showTitle = nil;
375                [scanner scanUpToCharactersFromSet:[NSCharacterSet letterCharacterSet] intoString:nil];
376                if([scanner scanUpToString:@"." intoString:&showTitle])
377                        info = [self getInfo:show forSeason:season episodeTitle:showTitle];
378        }
379        if(!info)
380                return NO;
381       
382        NSString *showInfoUrl = [info objectForKey:LINK_KEY];
383        NSString *image = nil;
384        NSString *coverArtDir = [[path stringByDeletingLastPathComponent] stringByAppendingPathComponent:@"Cover Art"];
385        NSString *newPath = [coverArtDir stringByAppendingPathComponent:fileName];
386        NSString *imageDestination = [[newPath stringByDeletingPathExtension] stringByAppendingPathExtension:@"jpg"];
387        BOOL isDir = NO;
388        BOOL imageExists = [[NSFileManager defaultManager] fileExistsAtPath:imageDestination isDirectory:&isDir] && !isDir;
389        if(showInfoUrl && !imageExists)
390                image = [self getScreencapUrl:showInfoUrl];
391        if(image)
392        {
393                NSURL *imageURL = [NSURL URLWithString:image];
394                NSURLRequest *request = [NSURLRequest requestWithURL:imageURL];
395                [[NSFileManager defaultManager] createDirectoryAtPath:coverArtDir attributes:nil];
396                SapphireTVShowDataMenuDownloadDelegate *myDelegate = [[SapphireTVShowDataMenuDownloadDelegate alloc] initWithDest:imageDestination];
397                [[NSURLDownload alloc] initWithRequest:request delegate:myDelegate];
398                [myDelegate release];
399        }
400       
401        [info removeObjectForKey:LINK_KEY];
402        [fileMeta importInfo:info fromSource:META_TVRAGE_IMPORT_KEY withTime:[[NSDate date] timeIntervalSince1970]];
403       
404        return YES;
405}
406
407- (void)setCompletionText
408{
409        [self setText:@"All availble TV Show data has been imported"];
410}
411
412- (void)importNextItem:(NSTimer *)timer
413{
414        SapphireFileMetaData *fileMeta = [importItems objectAtIndex:0];
415        NSString * fileName=[[fileMeta path] lastPathComponent] ;
416        [self setCurrentFile:[NSString stringWithFormat:@"Current File: %@",fileName]];
417        [super importNextItem:timer];
418}
419
420- (void)resetUIElements
421{
422        [super resetUIElements];
423        [title setTitle: @"Populate TV Show Data"];
424        [self setText:@"This will attempt to fetch information about TV shows automatically.  This procedure may take quite some time and could ask you questions"];
425        [button setTitle: @"Import TV Show Data"];
426}
427
428- (void) wasExhumedByPoppingController: (BRLayerController *) controller
429{
430        [super wasExhumedByPoppingController:controller];
431        if(![controller isKindOfClass:[SapphireShowChooser class]])
432                return;
433       
434        SapphireShowChooser *chooser = (SapphireShowChooser *)controller;
435        int selection = [chooser selection];
436        if(selection == SHOW_CHOOSE_CANCEL)
437                [self skipNextItem];
438        else if(selection == SHOW_CHOOSE_NOT_SHOW)
439                [[importItems objectAtIndex:0] importInfo:[NSMutableDictionary dictionary] fromSource:META_TVRAGE_IMPORT_KEY withTime:[[NSDate date] timeIntervalSince1970]];
440        else
441        {
442                NSDictionary *show = [[chooser shows] objectAtIndex:selection];
443                [showTranslations setObject:[show objectForKey:@"link"] forKey:[[chooser searchStr] lowercaseString]];
444                [self writeSettings];
445        }
446        [self resume];
447}
448
449@end
Note: See TracBrowser for help on using the repository browser.