Mercurial > ~darius > hgwebdir.cgi > py-iview
comparison iview.py @ 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 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:fa7ca67af8c9 |
---|---|
1 #!/usr/bin/env python | |
2 | |
3 import exceptions | |
4 import htmlentitydefs | |
5 import re | |
6 import sys | |
7 import time | |
8 import urllib2 | |
9 import xml.dom.minidom as minidom | |
10 from xml.parsers.expat import ExpatError | |
11 | |
12 confurl = 'http://www.abc.net.au/iview/iview_231_config.xml' | |
13 | |
14 pattern = re.compile("&(\w+?);") | |
15 | |
16 def descape_entity(m, defs=htmlentitydefs.entitydefs): | |
17 try: | |
18 return defs[m.group(1)] | |
19 except KeyError: | |
20 return m.group(0) # use as is | |
21 def descape(string): | |
22 return pattern.sub(descape_entity, string) | |
23 | |
24 def toBool(s): | |
25 if type(s) is bool: | |
26 return s | |
27 s = str(s).strip().lower() | |
28 return not s[0] in ['f','n','0'] | |
29 | |
30 class NoAsset(exceptions.Exception): | |
31 pass | |
32 | |
33 class Series(object): | |
34 def __init__(self, seriesElem): | |
35 self.id = seriesElem.getAttribute("id") | |
36 self.url = seriesElem.getAttribute("href") | |
37 | |
38 # Fetch titles and so on | |
39 #print "Fetching series URL " + self.url | |
40 xmlp = minidom.parse(urllib2.urlopen(self.url)) | |
41 self.title = self.gatherChildren(xmlp.getElementsByTagName("title")) | |
42 self.descr = self.gatherChildren(xmlp.getElementsByTagName("description")) | |
43 | |
44 # No asset means unpublished? | |
45 assets = xmlp.getElementsByTagName("abc:videoAsset") | |
46 if len(assets) == 0: | |
47 raise NoAsset | |
48 | |
49 self.asset = assets[0].childNodes[0].data.rstrip('.flv') | |
50 | |
51 rating = xmlp.getElementsByTagName("abc:rating")[0].childNodes | |
52 if len(rating) > 0: | |
53 self.rating = descape(rating[0].data) | |
54 else: | |
55 self.rating = "unrated" | |
56 | |
57 self.imgurl = xmlp.getElementsByTagName("image")[0].getElementsByTagName("url")[0].childNodes[0].data | |
58 self.expiry = time.strptime(xmlp.getElementsByTagName("abc:expireDate")[0].childNodes[0].data , | |
59 '%d/%m/%Y %H:%M:%S') | |
60 | |
61 def gatherChildren(self, elemList): | |
62 rtn = [] | |
63 for e in elemList: | |
64 if len(e.childNodes) > 0: | |
65 rtn.append(descape(e.childNodes[0].data.strip())) | |
66 return rtn | |
67 | |
68 class Channel(object): | |
69 def __init__(self, chanElem): | |
70 self.id = chanElem.getAttribute("id") | |
71 self.sort = chanElem.getAttribute("sort") | |
72 self.path = chanElem.getAttribute("path") | |
73 self.thumb = chanElem.getAttribute("thumb") | |
74 self.name = descape(chanElem.getElementsByTagName("name")[0].childNodes[0].data) | |
75 self.descr = descape(chanElem.getElementsByTagName("description")[0].childNodes[0].data) | |
76 self.series = [] | |
77 | |
78 def getSeries(self): | |
79 # This can take ages | |
80 print "Fetching series for channel " + self.name | |
81 # Series is nested for some reason | |
82 xmlp = minidom.parse(urllib2.urlopen(self.path)) | |
83 nl = xmlp.getElementsByTagName("series")[0] | |
84 for s in nl.getElementsByTagName("series"): | |
85 try: | |
86 self.series.append(Series(s)) | |
87 except ExpatError: | |
88 print "Unable to parse XML, skipping" | |
89 continue | |
90 except NoAsset: | |
91 print "No asset tag, skipping" | |
92 continue | |
93 | |
94 class IView(object): | |
95 def __init__(self): | |
96 # Fetch and parse config URL | |
97 #print "Fetching configuration URL" | |
98 xmlp = minidom.parse(urllib2.urlopen(confurl)) | |
99 self.params = {} | |
100 for param in xmlp.getElementsByTagName("param"): | |
101 self.params[param.getAttribute("name")] = param.getAttribute("value") | |
102 | |
103 # Get token & metered status from auth_path URL | |
104 #print "Fetching authorisation information" | |
105 self.getAuth() | |
106 | |
107 # Build channel list | |
108 #print "Fetching channel list" | |
109 xmlp = minidom.parse(urllib2.urlopen(self.params['base_url'] + '/' + self.params['xml_channels'])) | |
110 self.channels = [] | |
111 for chan in xmlp.getElementsByTagName("channel"): | |
112 self.channels.append(Channel(chan)) | |
113 | |
114 def getAuth(self): | |
115 xmlp = minidom.parse(urllib2.urlopen(self.params['auth_path'])) | |
116 self.token = xmlp.getElementsByTagName("token")[0].childNodes[0].data | |
117 self.metered = not toBool(xmlp.getElementsByTagName("free")[0].childNodes[0].data) | |
118 | |
119 server = xmlp.getElementsByTagName("server")[0].childNodes | |
120 if len(server) == 0: | |
121 self.rtmp = self.params['server_streaming'].rstrip('/ondemand') + '////' + self.params['media_path'] | |
122 self.tcurl = self.params['server_streaming'] | |
123 else: | |
124 self.rtmp = xmlp.getElementsByTagName("server")[0].childNodes[0].data | |
125 self.tcurl = None | |
126 | |
127 def genFetchCmd(self, series, outfile): | |
128 cmd = ['-m', '1200'] | |
129 if self.tcurl == None: | |
130 cmd += ['-r', self.rtmp + '?auth=' + self.token] | |
131 cmd += ['-y', series.asset] | |
132 else: | |
133 cmd += ['-r', self.rtmp + series.asset] | |
134 cmd += ['-t', self.tcurl + '?auth=' + self.token] | |
135 cmd += ['-o', outfile] | |
136 return cmd | |
137 | |
138 # Non-metered: | |
139 #/home/darius/projects/flvstreamer/flvstreamer_x86 | |
140 # -r rtmp://cp53909.edgefcs.net////flash/playback/_definst_/catch_up/compass_09_23_28 | |
141 # -t rtmp://cp53909.edgefcs.net/ondemand?auth=daEbFbibab6d4c0cwdjcwcya4dTb9cucucw-bkJnTd-8-klt_rFzqL&aifp=v001 | |
142 # -o ./compass_09_23_28.flv -m 1200 |