Tag Archives: NSXMLParser

Using the TVDB API

The other day when I was doing some more work on the Media Sorter I decided to implement the way I will get the program to find out the episode name for the current file.

After looking for ages on the internet I found out that there wasn’t any help anywhere on how to interpret the TVDB API XML files in objective-c. I found a framework but I wanted to try and make it myself so I could see how it works and suit it to my needs.

After a lot more searching and figuring I found out that it should be possible to do it all using NSXMLParser. This was a bit awkward to figure out too. It wasn’t straightforward to see how to use it, but anyways, I worked it out.

So here is a bit of code I put together which does a basic search of TVDB shows when you give it a show name to search for. It returns an array of the show information held in a new object I created (TVDBShow).

Here is the .m file for TVDBShow:

#import <Foundation/Foundation.h>

@interface TVDBShow : NSObject
@property (retain) NSString *seriesID;
@property (retain) NSString *language;
@property (retain) NSString *seriesName;
@property (retain) NSString *overview;

// Instance Methods
- (void)setShowWithSeriesID:(NSString*)newSeriesID Language:(NSString*)newLanguage SeriesName:(NSString*)newSeries Overview:(NSString*)newOverview;

// Class Methods
+ (TVDBShow*)showWithSeriesID:(NSString*)newSeriesID Language:(NSString*)newLanguage SeriesName:(NSString*)newSeries Overview:(NSString*)newOverview;

@end

@implementation TVDBShow
@synthesize seriesID, language, seriesName, overview;

//-----------------------------------------------------------
// Instance Methods
//-----------------------------------------------------------
- (void)setShowWithSeriesID:(NSString*)newSeriesID Language:(NSString*)newLanguage SeriesName:(NSString*)newSeries Overview:(NSString*)newOverview {
	[self setSeriesID:newSeriesID];
	[self setLanguage:newLanguage];
	[self setSeriesName:newSeries];
	[self setLanguage:newLanguage];
}

//-----------------------------------------------------------
// Class Methods
//-----------------------------------------------------------
+ (TVDBShow*)showWithSeriesID:(NSString*)newSeriesID Language:(NSString*)newLanguage SeriesName:(NSString*)newSeries Overview:(NSString*)newOverview {
    TVDBShow *returnableShow = [[TVDBShow alloc] init];
    [returnableShow setShowWithSeriesID:newSeriesID Language:newLanguage SeriesName:newSeries Overview:newOverview];
    return [returnableShow autorelease];
}

@end

And then the .m file for TVDBApi:

#import <Foundation/Foundation.h>
#import "TVDBShow.h"

typedef enum searchType {
    seriesSearch = 0,
    episodeSearch
} searchType;

@interface TVDBApi : NSObject {
    // API Objects
    NSString *APIKey;
    NSString *mirrorPath;
    NSString *getSeriesPath;
    NSUInteger currentSearchType;

    // Show Objects
    TVDBShow *foundShow; // Initalise and release when needed instead of in init.
    NSMutableString *collectedData;
    NSMutableArray *foundShowArray;
}

// Search Methods
- (void)searchForTVDBShowsWithName:(NSString*)showName;

// NSXML Parser Methods
- (void)parser:(NSXMLParser*)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict;
- (void)parser:(NSXMLParser*)parser foundCharacters:(NSString *)string;
- (void)parser:(NSXMLParser*)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName;

// Getters
- (NSArray*)getFoundShowArray;

@end

@implementation TVDBApi

- (id)init {
    self = [super init];
    if (self) {
        // Initialisations
		APIKey = [[NSString alloc] initWithString:@"APIKEY"];
		mirrorPath = [[NSString alloc] initWithString:@"http://www.thetvdb.com/api/"];
		getSeriesPath = [[NSString alloc] initWithString:@"GetSeries.php?seriesname="];
		currentSearchType = 0;
		foundShowArray = [[NSMutableArray alloc] init];
    }
    return self;
}
- (void)dealloc {
    [APIKey release];
	[mirrorPath release];
	[getSeriesPath release];
	[foundShowArray release];
    [super dealloc];
}

//-----------------------------------------------------------
// Search Methods
//-----------------------------------------------------------
- (void)searchForTVDBShowsWithName:(NSString*)showName {
	// Set up the url to search for the show 'showName'.
	showName = [showName stringByAddingPercentEscapesUsingEncoding:NSASCIIStringEncoding];
	NSURL *showSearchURL = [[NSURL alloc] initWithString:
							[NSString stringWithFormat:@"%@%@%@", mirrorPath, getSeriesPath, showName]];

	// Set up the search type and show array.
	currentSearchType = (searchType)seriesSearch;
	[foundShowArray removeAllObjects];

	// Create a XML parser to search through the returned results for us.
	NSXMLParser *XMLParser = [[NSXMLParser alloc] initWithContentsOfURL:showSearchURL];
	[XMLParser setDelegate:self];
	[XMLParser parse];

	// Release all the local objects.
	[showSearchURL release];
	[XMLParser release];
}

//-----------------------------------------------------------
// NSXML Parser Methods
//-----------------------------------------------------------
- (void)parser:(NSXMLParser*)parser didStartElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName attributes:(NSDictionary *)attributeDict {
	// Run different searches for different search types.
	if (currentSearchType == (searchType)seriesSearch) {
		// If we are doing a Series search then we want to create a new TVDBShow object to work with for each show we encounter in the search.
		if ([elementName isEqualToString:@"seriesid"]) {
			foundShow = [[TVDBShow alloc] init];
		}
	} else if (currentSearchType == (searchType)episodeSearch) {
		// If we are doing an Episode search then......
		NSLog(@"Implement Episode Search");
	}
}
- (void)parser:(NSXMLParser*)parser foundCharacters:(NSString *)string {
	// This will be the same for all search types as it just collects the data we want.
	if (!collectedData) {
		collectedData = [[NSMutableString alloc] init];
	}
	[collectedData setString:string];
}//Done
- (void)parser:(NSXMLParser*)parser didEndElement:(NSString *)elementName namespaceURI:(NSString *)namespaceURI qualifiedName:(NSString *)qName {
	// Also run different searches for different search types.
	if (currentSearchType == (searchType)seriesSearch) {
		// So were doing a Series search. We must pick out all the information we want and put it into the relevant field of foundShow.
		if		  ([elementName isEqualToString:@"seriesid"]) {
			[foundShow setSeriesID:collectedData];
		} else if ([elementName isEqualToString:@"language"]) {
			[foundShow setLanguage:collectedData];
		} else if ([elementName isEqualToString:@"SeriesName"]) {
			[foundShow setSeriesName:collectedData];
		} else if ([elementName isEqualToString:@"Overview"]) {
			[foundShow setOverview:collectedData];
		} else if ([elementName isEqualToString:@"id"]) {
			// This is the final element in this shows XML tree and because were not interested in this we can use it to close off the assignment of this shows details and add it to the foundShowArray.
			[foundShowArray addObject:foundShow];
			[foundShow release];
		}
	} else if (currentSearchType == (searchType)episodeSearch) {
		NSLog(@"Implement Episode Search");
	}
}

//-----------------------------------------------------------
// Getters
//-----------------------------------------------------------
- (NSArray*)getFoundShowArray {
	return foundShowArray;
}

@end

If anyone sees anything wrong with this code or how it could be improved please let me know. I’m still relatively new to object oriented programming. I should also be releasing some proper files for download or something sometime soon 🙂

Tagged , , ,