source: trunk/SapphireFrappliance/MetaDataImporting/SapphireScraper.m @ 1206

Revision 1206, 21.1 KB checked in by gbooker, 4 years ago (diff)

Switched a stringByAppendingString to a appendString since it's faster and this loop can consume a lot of CPU in some cases.

Line 
1/*
2 * SapphireScraper.m
3 * Sapphire
4 *
5 * Created by Graham Booker on Dec. 19, 2009.
6 * Copyright 2009 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 "SapphireScraper.h"
22#include "pcre.h"
23
24@interface SapphireScraper ()
25- (void)parseSettings;
26- (void)setBuffer:(int)index toString:(NSString *)str;
27- (void)clearBuffers;
28- (void)setStoredMatch:(int)index toString:(NSString *)str;
29- (void)clearStorchMatches;
30- (NSString *)parseFunction:(NSString *)function;
31@end
32
33@implementation SapphireScraper
34
35static NSMutableDictionary *scrapers = nil;
36static NSDictionary *scraperPaths = nil;
37
38+ (void)initialize
39{
40        if(!scrapers)
41                scrapers = [[NSMutableDictionary alloc] init];
42}
43
44+ (NSArray *)allScrapperNames
45{
46        NSBundle *selfBundle = [NSBundle bundleForClass:[self class]];
47        NSArray *paths = [selfBundle pathsForResourcesOfType:@"xml" inDirectory:@"scrapers"];
48       
49        NSMutableDictionary *scraperPathsDict = [[NSMutableDictionary alloc] init];
50        NSEnumerator *pathEnum = [paths objectEnumerator];
51        NSString *path;
52        while((path = [pathEnum nextObject]) != nil)
53        {
54                NSError *error;
55                NSURL *url = [NSURL fileURLWithPath:path];
56                NSXMLDocument *doc = [[NSXMLDocument alloc] initWithContentsOfURL:url options:0 error:&error];
57                if(!doc)
58                        continue;
59                NSXMLElement *root = [doc rootElement];
60               
61                NSString *name = [[root attributeForName:@"name"] stringValue];
62                NSString *type = [[root attributeForName:@"content"] stringValue];
63               
64                [scraperPathsDict setObject:[type stringByAppendingFormat:@"-%@", path] forKey:name];
65                [doc release];
66        }
67        [scraperPaths release];
68        scraperPaths = [scraperPathsDict copy];
69        [scraperPathsDict release];
70        return [scraperPaths allKeys];
71}
72
73+ (SapphireScraper *)scrapperWithName:(NSString *)filename
74{
75        if(!scraperPaths)
76                [SapphireScraper allScrapperNames];
77       
78        NSValue *value = [scrapers objectForKey:filename];
79        if(value == nil)
80        {
81                NSString *path = [scraperPaths objectForKey:filename];
82                if(path == nil)
83                        return nil;
84               
85                int index = [path rangeOfString:@"-"].location;
86                NSString *type = [path substringToIndex:index];
87                path = [path substringFromIndex:index+1];
88                NSError *error;
89                SapphireScraper *scraper;
90                if([type isEqualToString:@"tvshows"])
91                        scraper = [[SapphireTVShowScraper alloc] initWithPath:path error:&error];
92                else if([type isEqualToString:@"movies"])
93                        scraper = [[SapphireMovieScraper alloc] initWithPath:path error:&error];
94                else
95                        scraper = nil;
96               
97                if(!scraper)
98                        return nil;
99               
100                value = [NSValue valueWithNonretainedObject:scraper];
101                [scrapers setObject:value forKey:[scraper name]];
102                [scraper autorelease];
103        }
104        return [value nonretainedObjectValue];
105}
106
107- (id)initWithPath:(NSString *)path error:(NSError * *)error
108{
109        self = [super init];
110        if (self != nil) {
111                NSURL *url = [NSURL fileURLWithPath:path];
112                NSXMLDocument *doc = [[NSXMLDocument alloc] initWithContentsOfURL:url options:0 error:error];
113                root = [[doc rootElement] retain];
114                [doc release];
115                if(root == nil)
116                {
117                        [self autorelease];
118                        return nil;
119                }
120               
121                NSArray *includes;
122                while([(includes = [root elementsForName:@"include"]) count])
123                {
124                        NSXMLElement *include;
125                        NSEnumerator *includeEnum = [includes objectEnumerator];
126                        NSString *myDir = [path stringByDeletingLastPathComponent];
127                        while((include = [includeEnum nextObject]) != nil)
128                        {
129                                NSString *includePath = [myDir stringByAppendingPathComponent:[include stringValue]];
130                                NSXMLDocument *includeDoc = [[NSXMLDocument alloc] initWithContentsOfURL:[NSURL fileURLWithPath:includePath] options:0 error:nil];
131                                if(includeDoc)
132                                {
133                                        NSArray *children = [[includeDoc rootElement] children];
134                                        NSXMLElement *child;
135                                        NSEnumerator *childEnum = [children objectEnumerator];
136                                        while((child = [childEnum nextObject]) != nil)
137                                        {
138                                                [child detach];
139                                                [root addChild:child];
140                                        }
141                                }
142                                [include detach];
143                                [includeDoc release];
144                        }
145                }
146               
147                settings = [[NSMutableDictionary alloc] init];
148                [self parseSettings];
149        }
150        return self;
151}
152
153- (void) dealloc
154{
155        [scrapers removeObjectForKey:[self name]];
156        [root release];
157        [settings release];
158        [settingsXML release];
159        [self clearBuffers];
160        [self clearStorchMatches];
161        [super dealloc];
162}
163
164- (NSString *)name
165{
166        return [[root attributeForName:@"name"] stringValue];
167}
168
169- (NSString *)contentType
170{
171        return [[root attributeForName:@"content"] stringValue];
172}
173
174- (NSString *)thumbUrl
175{
176        return [[root attributeForName:@"content"] stringValue];
177}
178
179- (NSString *)serverEncoding
180{
181        return [[root attributeForName:@"thumb"] stringValue];
182}
183
184- (NSString *)settingsXML
185{
186        return settingsXML;
187}
188
189- (NSMutableDictionary *)settings
190{
191        return settings;
192}
193
194- (NSString *)searchResultsForURLContent:(NSString *)urlContent
195{
196        [self clearBuffers];
197        [self setBuffer:0 toString:urlContent];
198        return [self parseFunction:@"GetSearchResults"];
199}
200
201- (NSString *)searchResultsForNfoContent:(NSString *)nfoContent
202{
203        [self clearBuffers];
204        [self setBuffer:0 toString:nfoContent];
205        return [self parseFunction:@"NfoUrl"];
206}
207
208- (NSString *)functionResultWithArguments:(NSString *)function, ...
209{
210        va_list argList;
211        va_start(argList, function);
212        NSString *argument;
213        int index = 0;
214        while((argument = va_arg(argList, id)) != nil)
215        {
216                [self setBuffer:index toString:argument];
217                index++;
218        }
219        return [self parseFunction:function];
220}
221
222- (void)setBuffer:(int)index toString:(NSString *)str
223{
224        [scraperBuffers[index] release];
225        scraperBuffers[index] = [str retain];
226}
227
228- (void)clearBuffers
229{
230        int i;
231        for(i=0; i<SCRAPER_BUFFER_COUNT; i++)
232        {       
233                [scraperBuffers[i] release];
234                scraperBuffers[i] = nil;
235        }
236}
237
238- (void)setStoredMatch:(int)index toString:(NSString *)str
239{
240        [storedMatches[index] release];
241        storedMatches[index] = [str retain];
242}
243
244- (void)clearStorchMatches
245{
246        int i;
247        for(i=0; i<SCRAPER_MATCH_COUNT; i++)
248        {
249                [storedMatches[i] release];
250                storedMatches[i] = nil;
251        }
252}
253
254- (void)parseSetting:(NSXMLElement *)setting
255{
256        NSString *type = [[setting attributeForName:@"type"] stringValue];
257        if([type isEqualToString:@"sep"])
258                return;
259        NSString *settingID = [[setting attributeForName:@"id"] stringValue];
260        if(![settingID length] || ![type length])
261                return;
262       
263        NSString *defaultValue = [[setting attributeForName:@"default"] stringValue];
264        if(![defaultValue length])
265                defaultValue = @"";
266       
267        if([type isEqualToString:@"bool"])
268        {
269                if([defaultValue isEqualToString:@"true"])
270                        [settings setObject:[NSNumber numberWithBool:YES] forKey:settingID];
271                else
272                        [settings setObject:[NSNumber numberWithBool:NO] forKey:settingID];
273        }
274        else if([type isEqualToString:@"text"])
275                [settings setObject:defaultValue forKey:settingID];
276        else if([type isEqualToString:@"labelenum"])
277                [settings setObject:defaultValue forKey:settingID];
278}
279
280- (void)parseSettings
281{
282        settingsXML = [[self parseFunction:@"GetSettings"] retain];
283        if(![settingsXML length])
284                return;
285       
286        NSXMLDocument *doc = [[NSXMLDocument alloc] initWithXMLString:settingsXML options:0 error:nil];
287        NSXMLElement *rootSetting = [doc rootElement];
288        NSArray *settingDescs = [rootSetting elementsForName:@"setting"];
289       
290        int count = [settingDescs count], i;
291        for(i=0; i<count; i++)
292        {
293                [self parseSetting:[settingDescs objectAtIndex:i]];
294        }
295       
296        [doc release];
297}
298
299NSString *trimmedString(NSString *str)
300{
301        NSCharacterSet *whitespace = [NSCharacterSet characterSetWithCharactersInString:[NSString stringWithFormat:@"\n\r%C \t", 0x85]];
302       
303        int i, length = [str length];
304        for(i=0; i<length; i++)
305                if(![whitespace characterIsMember:[str characterAtIndex:i]])
306                        break;
307        int offset = i;
308        for(i=length-1; i>offset; i--)
309                if(![whitespace characterIsMember:[str characterAtIndex:i]])
310                        break;
311       
312        if(offset > i)
313                return @"";
314        return [str substringWithRange:NSMakeRange(offset, i+1-offset)];
315}
316
317NSString *cleanedString(NSString *str)
318{
319        NSMutableString *mutStr = [[NSMutableString alloc] init];
320        NSScanner *scanner = [NSScanner scannerWithString:str];
321        [scanner setCharactersToBeSkipped:[NSCharacterSet characterSetWithCharactersInString:@""]];
322        while(![scanner isAtEnd])
323        {
324                NSString *append = nil;
325                [scanner scanUpToString:@"<" intoString:&append];
326                if(append)
327                        [mutStr appendString:append];
328                NSString *tag = nil;
329                [scanner scanUpToString:@">" intoString:&tag];
330                if([tag hasPrefix:@"<br"] && ([tag length] == 3 || [tag characterAtIndex:3] == ' ' || [tag characterAtIndex:3] == '/'))
331                        [mutStr appendString:@"\n"];
332                [scanner scanString:@">" intoString:nil];
333        }
334        /*TV Rage doesn't understand that an & needs to be &amp; in the HTML, not just '&', so we have to work around yet another instance of their stupidity.  Decoding entities and then re-encoding them seems to be the safest way to do this*/
335        NSString *decoded = (NSString *)CFXMLCreateStringByUnescapingEntities(NULL, (CFStringRef)mutStr, NULL);
336        NSString *reencoded = (NSString *)CFXMLCreateStringByEscapingEntities(NULL, (CFStringRef)decoded, NULL);
337        [decoded release];
338        [mutStr release];
339        [reencoded autorelease];
340        return trimmedString(reencoded);
341}
342
343void bufferBooleanAttributeWithDefault(NSXMLElement *element, NSString *attributeName, BOOL defaultValue, BOOL *values)
344{
345        int i;
346        if(defaultValue)
347                for(i=0; i<SCRAPER_MATCH_COUNT; i++)
348                        values[i] = YES;
349        else
350                memset(values, 0, sizeof(BOOL)*SCRAPER_MATCH_COUNT);
351       
352        NSString *attr = [[element attributeForName:attributeName] stringValue];
353        if(attr)
354        {
355                NSArray *valueStrings = [attr componentsSeparatedByString:@","];
356                int count = [valueStrings count];
357                for(i=0; i<count; i++)
358                {
359                        int index = [[valueStrings objectAtIndex:i] intValue];
360                        if(index > 0 && index <= SCRAPER_MATCH_COUNT)
361                                values[index] = !defaultValue;
362                }
363        }
364}
365
366BOOL booleanAttributeWithDefault(NSXMLElement *element, NSString *attributeName, BOOL defaultValue)
367{
368        NSString *attr = [[element attributeForName:attributeName] stringValue];
369        if(attr)
370        {
371                NSString *checkValue;
372                if(defaultValue)
373                        checkValue = @"no";
374                else
375                        checkValue = @"yes";
376                if(![attr isEqualToString:checkValue])
377                        return defaultValue;
378                else
379                        return !defaultValue;
380        }
381        return defaultValue;
382}
383
384int integerAttributeWithDefault(NSXMLElement *element, NSString *attributeName, int defaultValue)
385{
386        NSString *attr = [[element attributeForName:attributeName] stringValue];
387        if(attr)
388        {
389                int ret = [attr intValue];
390                if(ret)
391                        return ret;
392        }
393        return defaultValue;
394}
395
396- (NSString *)substituteBuffersIntoInput:(NSString *)input
397{
398        NSMutableString *mutStr = [input mutableCopy];
399       
400        NSRange range;
401        while((range = [mutStr rangeOfString:@"$$"]).location != NSNotFound)
402        {
403                int index = [[mutStr substringFromIndex:range.location + 2] intValue];
404                NSString *replacement;
405                if(index > 0 && index <= SCRAPER_BUFFER_COUNT)
406                {
407                        if(index > 9)
408                                range.length += 2;
409                        else
410                                range.length ++;
411                       
412                        replacement = scraperBuffers[index - 1];
413                        if(replacement == nil)
414                                replacement = @"";
415                }
416                else
417                        replacement = @"";
418                [mutStr replaceCharactersInRange:range withString:replacement];
419        }
420        while((range = [mutStr rangeOfString:@"$INFO["]).location != NSNotFound)
421        {
422                int offset = range.location + 6;
423                NSRange endRange = [mutStr rangeOfString:@"]" options:0 range:NSMakeRange(offset, [mutStr length] - offset)];
424                NSString *replacement;
425                if(endRange.location != NSNotFound)
426                {
427                        range.length = endRange.location - range.location + 1;
428                        NSString *setting = [mutStr substringWithRange:NSMakeRange(offset, range.length - 7)];
429                        replacement = [settings objectForKey:setting];
430                        if(![replacement length])
431                                replacement = @"";
432                }
433                else
434                {
435                        replacement = @"";
436                }
437                [mutStr replaceCharactersInRange:range withString:replacement];
438        }
439        NSString *ret = [NSString stringWithString:mutStr];
440        [mutStr release];
441        return ret;
442}
443
444- (NSString *)replacementStrForOutput:(NSString *)output inputStr:(const char *)input matches:(int *)matches count:(int)matchCount
445{
446        NSMutableString *mutStr = [output mutableCopy];
447       
448        NSRange range = NSMakeRange(0, [mutStr length]);
449        while((range = [mutStr rangeOfString:@"\\" options:0 range:range]).location != NSNotFound)
450        {
451                BOOL storedMatch = ([mutStr characterAtIndex:range.location + 1] == '$');
452                int index = [[mutStr substringFromIndex:range.location + 1 + storedMatch] intValue];
453                NSString *replacement;
454                if(index > 0 && index < matchCount)
455                        range.length++;
456                range.length += storedMatch;
457               
458                int start = matches[index<<1];
459                int end = matches[(index<<1) + 1];
460                if(range.length > 1 && start != -1)
461                {
462                        replacement = [[[NSString alloc] initWithBytes:input+start length:end-start encoding:NSUTF8StringEncoding] autorelease];
463                        if(storedMatch)
464                                [self setStoredMatch:index toString:replacement];
465                        if(clean[index])
466                                replacement = cleanedString(replacement);
467                        else if(trim[index])
468                                replacement = trimmedString(replacement);
469                }
470                else if(range.length > 1 && storedMatch)
471                        replacement = storedMatches[index];
472                else
473                        replacement = @"";
474                [mutStr replaceCharactersInRange:range withString:replacement];
475                range.location += [replacement length];
476                range.length = [mutStr length] - range.location;
477        }
478       
479        NSString *ret = [NSString stringWithString:mutStr];
480        [mutStr release];
481        return ret;
482}
483
484- (void)parseExpression:(NSXMLElement *)element withInput:(NSString *)input intoDest:(int)dest andAppend:(BOOL)append
485{
486        NSString *output = [self substituteBuffersIntoInput:[[element attributeForName:@"output"] stringValue]];
487        NSArray *expressions = [element elementsForName:@"expression"];
488        NSString *expression = nil;
489        NSXMLElement *expressionElement = nil;
490        if([expressions count])
491        {
492                expressionElement = [expressions objectAtIndex:0];
493                expression = [[expressionElement childAtIndex:0] stringValue];
494        }
495        if(![expression length])
496                expression = @"(.*)";
497       
498        const char *errMsg = NULL;
499        int errOffset = 0;
500        pcre *reg = pcre_compile([expression UTF8String], PCRE_DOTALL, &errMsg, &errOffset, NULL);
501        if(!reg)
502                return;
503       
504        //AAA optional, compare;
505       
506        if(booleanAttributeWithDefault(expressionElement, @"clear", NO))
507                [self setBuffer:dest-1 toString:nil];
508       
509        BOOL repeat = booleanAttributeWithDefault(expressionElement, @"repeat", NO);
510       
511        bufferBooleanAttributeWithDefault(expressionElement, @"noclean", YES, clean);
512       
513        bufferBooleanAttributeWithDefault(expressionElement, @"trim", NO, trim);
514       
515        NSMutableString *result = [@"" mutableCopy];
516        int match[30];
517        int offset = 0;
518        const char *inputStr = [input UTF8String];
519        int inputLen = strlen(inputStr);
520        int matchCount = 0;
521        [self clearStorchMatches];
522        while((matchCount = pcre_exec(reg, NULL, inputStr, inputLen, offset, 0, match, 30)) >= 0)
523        {
524                BOOL addToResult = YES;
525                NSString *replacementString = [self replacementStrForOutput:output inputStr:inputStr matches:match count:matchCount];
526                int compare = integerAttributeWithDefault(expressionElement, @"compare", -1);
527                if(compare != -1)
528                {
529                        NSString *searchStr = nil;
530                        if(compare > 0 && compare <= 20)
531                                searchStr = scraperBuffers[compare -1];
532                        if([searchStr length] && [[replacementString lowercaseString] rangeOfString:searchStr].location == NSNotFound)
533                                addToResult = NO;
534                }
535                if(addToResult)
536                        [result appendString:replacementString];
537                if(!repeat)
538                        break;
539                offset = match[1];
540        }
541       
542        pcre_free(reg);
543       
544        NSString *final = result;
545        if(append)
546        {       
547                NSString *orig = scraperBuffers[dest - 1];
548                if(orig != nil)
549                        final = [orig stringByAppendingString:final];
550        }
551        if([final length])
552                [self setBuffer:dest-1 toString:final];
553        [result release];
554}
555
556- (BOOL)checkCondition:(NSString *)condition
557{
558        BOOL inverse = NO;
559        if([condition characterAtIndex:0] == '!')
560        {
561                inverse = YES;
562                condition = [condition substringFromIndex:1];
563        }
564       
565        id value = [settings objectForKey:condition];
566        BOOL ret = [value boolValue];
567        if(inverse)
568                ret = !ret;
569       
570        return ret;
571}
572
573- (int)parseElement:(NSXMLElement *)element
574{
575        NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
576        NSArray *regexChildren = nil;
577        NSString *value = nil;
578        NSString *conditional = nil;
579        regexChildren = [element elementsForName:@"RegExp"];
580        int count = [regexChildren count];
581        if(count)
582        {
583                int i;
584                for(i=0; i<count; i++)
585                        [self parseElement:[regexChildren objectAtIndex:i]];
586        }
587       
588        int result = 1;
589        value = [[element attributeForName:@"dest"] stringValue];
590        if(value != nil)
591                result = [value intValue];
592        BOOL append = NO;
593        if([value length] > 1 && [value characterAtIndex:1] == '+')
594                append = YES;
595       
596        conditional = [[element attributeForName:@"conditional"] stringValue];
597        if([conditional length] && ![self checkCondition:conditional])
598                return result;
599       
600        NSString *input = [[element attributeForName:@"input"] stringValue];
601        if(input)
602                input = [self substituteBuffersIntoInput:input];
603        else
604                input = scraperBuffers[0];
605       
606        [self parseExpression:element withInput:input intoDest:result andAppend:append];
607        [pool drain];
608       
609        return result;
610}
611
612- (NSString *)parseFunction:(NSString *)function
613{
614        NSArray *elements = [root elementsForName:function];
615        if(![elements count])
616                return nil;
617       
618        NSXMLElement *functionElement = [elements objectAtIndex:0];
619        elements = [functionElement elementsForName:@"RegExp"];
620        int count = [elements count], i;
621        for(i=0; i<count; i++)
622        {
623                [self parseElement:[elements objectAtIndex:i]];
624        }
625        int dest = integerAttributeWithDefault(functionElement, @"dest", 1);
626        NSString *ret = [[scraperBuffers[dest - 1] retain] autorelease];
627        if(booleanAttributeWithDefault(functionElement, @"clearbuffers", YES))
628                [self clearBuffers];
629       
630        return ret;     
631}
632
633@end
634
635@implementation SapphireMovieScraper
636
637- (id)initWithPath:(NSString *)path error:(NSError * *)error;
638{
639        self = [super initWithPath:path error:error];
640        if (self != nil) {
641                if(![[self contentType] isEqualToString:@"movies"])
642                {
643                        [self autorelease];
644                        return nil;
645                }
646        }
647        return self;
648}
649
650- (NSString *)searchURLForMovieName:(NSString *)movieName year:(NSString *)year
651{
652        [self clearBuffers];
653        [self setBuffer:0 toString:[movieName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
654        [self setBuffer:1 toString:[year stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
655        return [self parseFunction:@"CreateSearchUrl"];
656}
657
658- (NSString *)movieDetailsForURLContent:(NSString *)urlContent movieID:(NSString *)movieID atURL:(NSString *)url
659{
660        [self clearBuffers];
661        [self setBuffer:0 toString:urlContent];
662        [self setBuffer:1 toString:movieID];
663        [self setBuffer:2 toString:url];
664        return [self parseFunction:@"GetDetails"];
665}
666
667@end
668
669@implementation SapphireTVShowScraper
670
671- (id)initWithPath:(NSString *)path error:(NSError * *)error;
672{
673        self = [super initWithPath:path error:error];
674        if (self != nil) {
675                if(![[self contentType] isEqualToString:@"tvshows"])
676                {
677                        [self autorelease];
678                        return nil;
679                }
680        }
681        return self;
682}
683
684- (NSString *)searchURLForShowName:(NSString *)showName;
685{
686        [self clearBuffers];
687        [self setBuffer:0 toString:[showName stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
688        return [self parseFunction:@"CreateSearchUrl"];
689}
690
691- (NSString *)showDetailsForURLContent:(NSString *)urlContent showID:(NSString *)showID atURL:(NSString *)url;
692{
693        [self clearBuffers];
694        [self setBuffer:0 toString:urlContent];
695        [self setBuffer:1 toString:showID];
696        [self setBuffer:2 toString:url];
697        return [self parseFunction:@"GetDetails"];
698}
699
700- (NSString *)episodeListForURLContent:(NSString *)urlContent atURL:(NSString *)url;
701{
702        [self clearBuffers];
703        [self setBuffer:0 toString:urlContent];
704        [self setBuffer:1 toString:url];
705        return [self parseFunction:@"GetEpisodeList"];
706}
707
708- (NSString *)episodeDetailsForURLContent:(NSString *)urlContent episodeID:(NSString *)epID atURL:(NSString *)url;
709{
710        [self clearBuffers];
711        [self setBuffer:0 toString:urlContent];
712        [self setBuffer:1 toString:epID];
713        [self setBuffer:2 toString:url];
714        return [self parseFunction:@"GetEpisodeDetails"];
715}
716
717@end
718
719NSString *stringValueOfChild(NSXMLElement *element, NSString *childName)
720{
721        NSArray *children = [element elementsForName:childName];
722        if(![children count])
723                return nil;
724       
725        return [[children lastObject] stringValue];
726}
727
728NSNumber *intValueOfChild(NSXMLElement *element, NSString *childName)
729{
730        NSArray *children = [element elementsForName:childName];
731        if(![children count])
732                return nil;
733       
734        NSString *str = [[children lastObject] stringValue];
735        return [NSNumber numberWithInt:[str intValue]];
736}
737
738NSDate *dateValueOfChild(NSXMLElement *element, NSString *childName)
739{
740        NSArray *children = [element elementsForName:childName];
741        if(![children count])
742                return nil;
743       
744        NSString *str = [[children lastObject] stringValue];
745        return [NSDate dateWithNaturalLanguageString:str];
746}
747
748NSArray *arrayStringValueOfChild(NSXMLElement *element, NSString *childName)
749{
750        NSArray *children = [element elementsForName:childName];
751        if(![children count])
752                return nil;
753       
754        return [children valueForKey:@"stringValue"];
755}
756
757NSArray *arrayStringValueOfXPath(NSXMLElement *element, NSString *xpath)
758{
759        NSError *error = nil;
760        NSArray *children = [element objectsForXQuery:xpath error:&error];
761        if(![children count])
762                return nil;
763       
764        return [children valueForKey:@"stringValue"];
765}
Note: See TracBrowser for help on using the repository browser.