# HG changeset patch # User Daniel O'Connor # Date 1250752696 -34200 # Node ID fa7ca67af8c98e38b60ff8069fe6f87fcfc7f68a Initial commit of a python program to fetch iview streams using flvstreamer. diff -r 000000000000 -r fa7ca67af8c9 iview.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iview.py Thu Aug 20 16:48:16 2009 +0930 @@ -0,0 +1,142 @@ +#!/usr/bin/env python + +import exceptions +import htmlentitydefs +import re +import sys +import time +import urllib2 +import xml.dom.minidom as minidom +from xml.parsers.expat import ExpatError + +confurl = 'http://www.abc.net.au/iview/iview_231_config.xml' + +pattern = re.compile("&(\w+?);") + +def descape_entity(m, defs=htmlentitydefs.entitydefs): + try: + return defs[m.group(1)] + except KeyError: + return m.group(0) # use as is +def descape(string): + return pattern.sub(descape_entity, string) + +def toBool(s): + if type(s) is bool: + return s + s = str(s).strip().lower() + return not s[0] in ['f','n','0'] + +class NoAsset(exceptions.Exception): + pass + +class Series(object): + def __init__(self, seriesElem): + self.id = seriesElem.getAttribute("id") + self.url = seriesElem.getAttribute("href") + + # Fetch titles and so on + #print "Fetching series URL " + self.url + xmlp = minidom.parse(urllib2.urlopen(self.url)) + self.title = self.gatherChildren(xmlp.getElementsByTagName("title")) + self.descr = self.gatherChildren(xmlp.getElementsByTagName("description")) + + # No asset means unpublished? + assets = xmlp.getElementsByTagName("abc:videoAsset") + if len(assets) == 0: + raise NoAsset + + self.asset = assets[0].childNodes[0].data.rstrip('.flv') + + rating = xmlp.getElementsByTagName("abc:rating")[0].childNodes + if len(rating) > 0: + self.rating = descape(rating[0].data) + else: + self.rating = "unrated" + + self.imgurl = xmlp.getElementsByTagName("image")[0].getElementsByTagName("url")[0].childNodes[0].data + self.expiry = time.strptime(xmlp.getElementsByTagName("abc:expireDate")[0].childNodes[0].data , + '%d/%m/%Y %H:%M:%S') + + def gatherChildren(self, elemList): + rtn = [] + for e in elemList: + if len(e.childNodes) > 0: + rtn.append(descape(e.childNodes[0].data.strip())) + return rtn + +class Channel(object): + def __init__(self, chanElem): + self.id = chanElem.getAttribute("id") + self.sort = chanElem.getAttribute("sort") + self.path = chanElem.getAttribute("path") + self.thumb = chanElem.getAttribute("thumb") + self.name = descape(chanElem.getElementsByTagName("name")[0].childNodes[0].data) + self.descr = descape(chanElem.getElementsByTagName("description")[0].childNodes[0].data) + self.series = [] + + def getSeries(self): + # This can take ages + print "Fetching series for channel " + self.name + # Series is nested for some reason + xmlp = minidom.parse(urllib2.urlopen(self.path)) + nl = xmlp.getElementsByTagName("series")[0] + for s in nl.getElementsByTagName("series"): + try: + self.series.append(Series(s)) + except ExpatError: + print "Unable to parse XML, skipping" + continue + except NoAsset: + print "No asset tag, skipping" + continue + +class IView(object): + def __init__(self): + # Fetch and parse config URL + #print "Fetching configuration URL" + xmlp = minidom.parse(urllib2.urlopen(confurl)) + self.params = {} + for param in xmlp.getElementsByTagName("param"): + self.params[param.getAttribute("name")] = param.getAttribute("value") + + # Get token & metered status from auth_path URL + #print "Fetching authorisation information" + self.getAuth() + + # Build channel list + #print "Fetching channel list" + xmlp = minidom.parse(urllib2.urlopen(self.params['base_url'] + '/' + self.params['xml_channels'])) + self.channels = [] + for chan in xmlp.getElementsByTagName("channel"): + self.channels.append(Channel(chan)) + + def getAuth(self): + xmlp = minidom.parse(urllib2.urlopen(self.params['auth_path'])) + self.token = xmlp.getElementsByTagName("token")[0].childNodes[0].data + self.metered = not toBool(xmlp.getElementsByTagName("free")[0].childNodes[0].data) + + server = xmlp.getElementsByTagName("server")[0].childNodes + if len(server) == 0: + self.rtmp = self.params['server_streaming'].rstrip('/ondemand') + '////' + self.params['media_path'] + self.tcurl = self.params['server_streaming'] + else: + self.rtmp = xmlp.getElementsByTagName("server")[0].childNodes[0].data + self.tcurl = None + + def genFetchCmd(self, series, outfile): + cmd = ['-m', '1200'] + if self.tcurl == None: + cmd += ['-r', self.rtmp + '?auth=' + self.token] + cmd += ['-y', series.asset] + else: + cmd += ['-r', self.rtmp + series.asset] + cmd += ['-t', self.tcurl + '?auth=' + self.token] + cmd += ['-o', outfile] + return cmd + +# Non-metered: +#/home/darius/projects/flvstreamer/flvstreamer_x86 +# -r rtmp://cp53909.edgefcs.net////flash/playback/_definst_/catch_up/compass_09_23_28 +# -t rtmp://cp53909.edgefcs.net/ondemand?auth=daEbFbibab6d4c0cwdjcwcya4dTb9cucucw-bkJnTd-8-klt_rFzqL&aifp=v001 +# -o ./compass_09_23_28.flv -m 1200 diff -r 000000000000 -r fa7ca67af8c9 iviewget.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/iviewget.py Thu Aug 20 16:48:16 2009 +0930 @@ -0,0 +1,103 @@ +#!/usr/bin/env python + +import iview +import subprocess +import sys +import time + +outpath = '.' +outext = 'flv16x9' +flvstreamerpath = '/home/darius/projects/flvstreamer/flvstreamer_x86' + +def listChannels(iview): + i = 1 + print "Id %-30s %-35s" % ('Name', 'Description') + for c in iview.channels: + print "%2d %-30s %-35s" % (i, c.name, c.descr) + i += 1 + +def listSeries(chan): + i = 1 + for s in chan.series: + if len(s.title) > 1: + title = s.title[1] + else: + title = s.title[0] + print "%3d %s" % (i, title) + i += 1 + +def getInt(f): + ans = f.readline() + try: + i = int(ans) + except ValueError: + return None + + return i + +def getSeries(iview, series, outfile = None): + cmd = [flvstreamerpath] + + if outfile == None: + outfile = outpath + '/' + series.asset.replace('/', '-') + '.' + outext + cmd += iview.genFetchCmd(series, outfile) + p = subprocess.Popen(cmd) + while True: + res = p.poll() + if res == None: + time.sleep(0.1) + else: + return res + +if __name__ == "__main__": + iview = iview.IView() + if iview.metered: + print "WARNING: iview is metered" + print + + slist = [] + while True: + chan = None + listChannels(iview) + while True: + print "Please select a channel number (0 to exit): ", + i = getInt(sys.stdin) + if i != None and i >= 0 and i <= len(iview.channels): + break + print "Invalid entry, try again" + + if i == 0: + break + + cidx = i - 1 + chan = iview.channels[cidx] + chan.getSeries() + + listSeries(chan) + while True: + print "Please select which series to download (space separated numbers):" + res = sys.stdin.readline() + for r in res.split(): + try: + i = int(r) + except ValueError: + i = None + + if i != None and i >= 0 and i <= len(chan.series): + slist += [chan.series[i - 1]] + + if len(slist) > 0 or i == 0: + break + print "Invalid entry, try again" + + if len(slist) == 0: + print "Nothing to download" + sys.exit(0) + + print "Downloading %d items" % (len(slist)) + for series in slist: + print "Fetching " + series.asset + res = getSeries(iview, series) + print "Result is " + str(res) + # Re-auth or the server won't love us + iview.getAuth()