changeset 0:fa7ca67af8c9

Initial commit of a python program to fetch iview streams using flvstreamer.
author Daniel O'Connor <darius@dons.net.au>
date Thu, 20 Aug 2009 16:48:16 +0930
parents
children f914f08d69f5
files iview.py iviewget.py
diffstat 2 files changed, 245 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /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
--- /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()