MTV API Video Tutorial

Author: Joel Voss  

MTV

The MTV API is a standards compliant REST Atom OpenSearch API which makes it quite easy to use and develop for. If you've written for this type of API, you can copy most of the code for this and many other APIs including YouTube, OpenSocial, ProgrammableWeb's beta API, as well as many others listed here. The MTV API doesn't require an account or an apikey which means you can use my example outright and any software you write that uses the API will work no matter who uses it.

The URI's that you use to navigate the API are in the documentation. The video tutorial above shows you how to use them in a browser. Here's an example:
http://api.mtvnservices.com/1/artist/white_stripes.

The data that the MTV API contains is quite simple: artists, videos, and relationships. Each artist has a list of videos and each artist has a list of related artists. Artists also have photos, which means your mashup can easily show users a preview of what they're looking for. This ends up with all the data you really need for an excellent mashup.

When I write my mashups, I like to have a dead simple data model of how I'm going to use it, so here's the overview:

So there we go. It may seem a little strange if you haven't looked at data models before, but if you're confused, just look at the code.

#!/usr/bin/env python

"""
mtvapi1.py
by Javantea
Oct 27, 2008
Based on pwmap2.py

Maps artists/videos using MTV API

Each artist has a list of related artists and a list of videos. 
This is a perfect system to map.

This is the data model:
white_stripes -- white_stripes-videos
\-- white_stripes-related -- ranconteurs
        \-- beck
        \-- ...
"""

import urllib2

from os import path
from time import sleep
from xml.etree import ElementTree

# We use urlopen and file depending on protocol.
# http://api.mtvnservices.com/

def parseMTVUrl(state1, url='/1/artist/browse/w', level=1):
	p_new = state1.i
	pageqs = ''
	#if pageno != 1: pageqs = '&page=' + str(pageno)
	filename = 'data/' + url.replace('/', '_')[1:] + '.xml'
	#print '<!-- Grabbing', 'http://api.mtvnservices.com' + url, '-->'
	if not path.exists(filename):
		# Need an absolute url.
		try:
			endp = 'http://api.mtvnservices.com'
			data = urllib2.urlopen(endp \
			+ url + pageqs).read()
			file(filename, 'wb').write(data)
			sleep(1.0)
		except urllib2.URLError, e:
			print 'Warning: Could not grab artist data', \
			url, ':', e
			#skip over adding this friend's friends.
			return
		#end try
	#end if

	# Use XML
	# Thanks to Yahoo Developer for the idea:
	# http://developer.yahoo.com/python/python-xml.html
	tree1 = ElementTree.parse(filename)
	root = tree1.getroot()
	#print root[0].tag, root[0].text
	NS   = "http://www.w3.org/2005/Atom"
	MRSSNS = "http://search.yahoo.com/mrss/"
	this_artist = [i.text for i in root.findall('{%s}title' % (NS, ))]
	urls = [((i.get('rel') == 'self') and i.get('href')) \
	for i in root.findall('{%s}entry/{%s}link' % (NS, NS))]
	artists = [i.text for i in root.findall('{%s}entry/{%s}title' % (NS, NS))]
	self_urls = []
	for lurl in urls:
		if lurl:
			self_urls.append(lurl)
		#end if
	#next url
	name_pts = []

	#print 'u', url
	#print 't', this_artist

	artist_name = ''
	if ((len(this_artist) == 1) and this_artist[0] != None):
		if this_artist[0][-16:] == ' Related Artists':
			artist_name = this_artist[0][:-16]
		else:
			artist_name = this_artist[0]
		#end if
	else:
			print "Missing artist_name:", this_artist
	#end if

	if artist_name not in state1.names.keys():
		state1.p.append({'name':artist_name, 'color':'#99ccff', \
		'url':'http://api.mtvnservices.com' + url, 'conns':[]})
		state1.names[artist_name] = state1.i
		name_pts.append(state1.i)
		artist_i = state1.i
		state1.i += 1
	else:
		artist_i = state1.names[artist_name]
	#end if

	# They must be crazy putting null at the first of all.
	if artists[0] == None:
		artists.pop(0)
		self_urls.pop(0)
	#end if

	j = 0
	for artist in artists:
		# What are they thinking putting a <title /> in here?
		if artist == None:
			j += 1
			continue
		#end if
		if artist not in state1.names.keys():
			state1.p.append({'name':artist, 'color':'#99ccff', \
			'url':self_urls[j], 'conns':[]})
			state1.names[artist] = state1.i
			name_pts.append(state1.i)
			state1.p[artist_i]['conns'].append(state1.p[state1.i])
			state1.i += 1
		else:
			name_pts.append(state1.names[artist])
		#end if

		# There are two links per mashup, we want the first
		j += 1
	#next artist

	if level > 0:
		# Recursive Call... Each level gets smaller by one.
		for url in self_urls[:4]:
			parseMTVUrl(state1, url.replace('http://api.mtvnservices.com', \
			'') + 'related', level - 1)
		#next url
	#end if

#end def parseMTVUrl(state1, [url], [levels])

class up1:
	pass

def main():
	url = '/1/artist/white_stripes/related'
	from sys import argv
	if len(argv) > 1:
			url = argv[1]
	#end if
	state1 = up1()
	state1.p = []
	state1.i = 0
	state1.names = {}
	parseMTVUrl(state1, url, 1)
	for point in state1.p:
		# If they don't have any connections, they're a outlier.
		if len(point['conns']) == 0:
			continue
		print 'p', point['name']
		for conn in point['conns']:
			print '    c', conn['name']
		#next conn
	#next point
#end def main()

if __name__ == '__main__':
	main()
#end if

The main() function is called first. It calls parseMTVUrl(url, level). That grabs the data from the API and calls itself for each level of related artists we're looking for. In the end, main prints out the list of artists and relationships. Pretty easy, huh? The code uses elementtree, which is pretty standard, though it doesn't come with Python.

This work was quite easy and comes with my product/mashup, Small Wide World. Thanks to ProgrammableWeb for making this article possible.

Please leave a comment here to tell me what you think.

deliciousdel.icio.usdiggDiggstumbleuponStumbleUponredditreddit

Follow the PW team on Twitter

ProgrammableWeb
APIs, mashups and code. Because the world's your programmable oyster.

John Musser
Founder, ProgrammableWeb

Adam DuVander
Executive Editor, ProgrammableWeb. Author, Map Scripting 101. Lover, APIs.