8 from urllib
import quote
, unquote
9 from xml
.dom
import minidom
11 from Cheetah
.Template
import Template
14 from metadata
import tag_data
, from_container
15 from plugin
import EncodeUnicode
, Plugin
17 SCRIPTDIR
= os
.path
.dirname(__file__
)
21 # Some error/status message templates
23 MISSING
= """<h3>Missing Data.</h3> <br>
24 You must set both "tivo_mak" and "togo_path" before using this
26 The <a href="%s">ToGo</a> page
27 will reload in 10 seconds."""
29 TRANS_INIT
= """<h3>Transfer Initiated.</h3> <br>
30 Your selected transfer has been initiated.<br>
31 The <a href="%s">ToGo</a> page
32 will reload in 3 seconds."""
34 TRANS_STOP
= """<h3>Transfer Stopped.</h3> <br>
35 Your transfer has been stopped.<br>
36 The <a href="%s">ToGo</a> page
37 will reload in 3 seconds."""
39 UNABLE
= """<h3>Unable to Connect to TiVo.</h3> <br>
40 pyTivo was unable to connect to the TiVo at %s</br>
41 This most likely caused by an incorrect Media Access Key. Please return
42 to the ToGo page and double check your Media Access Key.<br>
43 The <a href="%s">ToGo</a> page will
44 reload in 20 seconds."""
46 # Preload the templates
47 trname
= os
.path
.join(SCRIPTDIR
, 'templates', 'redirect.tmpl')
48 tnname
= os
.path
.join(SCRIPTDIR
, 'templates', 'npl.tmpl')
49 REDIRECT_TEMPLATE
= file(trname
, 'rb').read()
50 NPL_TEMPLATE
= file(tnname
, 'rb').read()
52 status
= {} # Global variable to control download threads
53 tivo_cache
= {} # Cache of TiVo NPL
56 CONTENT_TYPE
= 'text/html'
58 def NPL(self
, handler
, query
):
59 shows_per_page
= 50 # Change this to alter the number of shows returned
60 cname
= query
['Container'][0].split('/')[0]
62 tivo_mak
= config
.get_server('tivo_mak')
63 togo_path
= config
.get_server('togo_path')
64 for name
, data
in config
.getShares():
66 togo_path
= data
.get('path')
69 tivoIP
= query
['TiVo'][0]
70 theurl
= ('https://' + tivoIP
+
71 '/TiVoConnect?Command=QueryContainer&ItemCount=' +
72 str(shows_per_page
) + '&Container=/NowPlaying')
74 folder
+= query
['Folder'][0]
75 theurl
+= '/' + folder
76 if 'AnchorItem' in query
:
77 theurl
+= '&AnchorItem=' + quote(query
['AnchorItem'][0])
78 if 'AnchorOffset' in query
:
79 theurl
+= '&AnchorOffset=' + query
['AnchorOffset'][0]
81 r
= urllib2
.Request(theurl
)
82 auth_handler
= urllib2
.HTTPDigestAuthHandler()
83 auth_handler
.add_password('TiVo DVR', tivoIP
, 'tivo', tivo_mak
)
84 opener
= urllib2
.build_opener(auth_handler
)
85 urllib2
.install_opener(opener
)
87 if (theurl
not in tivo_cache
or
88 (time
.time() - tivo_cache
[theurl
]['thepage_time']) >= 60):
89 # if page is not cached or old then retreive it
91 page
= urllib2
.urlopen(r
)
93 t
= Template(REDIRECT_TEMPLATE
)
95 t
.url
= '/TiVoConnect?Command=NPL&Container=' + quote(cname
)
96 t
.text
= UNABLE
% t
.url
97 handler
.send_response(200)
98 handler
.send_header('Content-Type', 'text/html')
100 handler
.wfile
.write(t
)
102 tivo_cache
[theurl
] = {'thepage': minidom
.parse(page
),
103 'thepage_time': time
.time()}
106 xmldoc
= tivo_cache
[theurl
]['thepage']
107 items
= xmldoc
.getElementsByTagName('Item')
108 TotalItems
= tag_data(xmldoc
, 'Details/TotalItems')
109 ItemStart
= tag_data(xmldoc
, 'ItemStart')
110 ItemCount
= tag_data(xmldoc
, 'ItemCount')
111 FirstAnchor
= tag_data(items
[0], 'Links/Content/Url')
116 entry
['ContentType'] = tag_data(item
, 'ContentType')
117 for tag
in ('CopyProtected', 'UniqueId'):
118 value
= tag_data(item
, tag
)
121 if entry
['ContentType'] == 'x-tivo-container/folder':
122 entry
['Title'] = tag_data(item
, 'Title')
123 entry
['TotalItems'] = tag_data(item
, 'TotalItems')
124 lc
= int(tag_data(item
, 'LastChangeDate'), 16)
125 entry
['LastChangeDate'] = time
.strftime('%b %d, %Y',
128 entry
.update(from_container(item
))
129 keys
= {'Icon': 'Links/CustomIcon/Url',
130 'Url': 'Links/Content/Url',
131 'SourceSize': 'Details/SourceSize',
132 'Duration': 'Details/Duration',
133 'CaptureDate': 'Details/CaptureDate'}
135 value
= tag_data(item
, keys
[key
])
139 entry
['SourceSize'] = ( '%.3f GB' %
140 (float(entry
['SourceSize']) / (1024 ** 3)) )
142 dur
= int(entry
['Duration']) / 1000
143 entry
['Duration'] = ( '%02d:%02d:%02d' %
144 (dur
/ 3600, (dur
% 3600) / 60, dur
% 60) )
146 entry
['CaptureDate'] = time
.strftime('%b %d, %Y',
147 time
.localtime(int(entry
['CaptureDate'], 16)))
158 cname
= query
['Container'][0].split('/')[0]
159 t
= Template(NPL_TEMPLATE
, filter=EncodeUnicode
)
163 t
.tivo_mak
= tivo_mak
164 t
.togo_path
= togo_path
165 t
.tivos
= config
.tivos
166 t
.tivo_names
= config
.tivo_names
171 t
.TotalItems
= int(TotalItems
)
172 t
.ItemStart
= int(ItemStart
)
173 t
.ItemCount
= int(ItemCount
)
174 t
.FirstAnchor
= quote(FirstAnchor
)
175 t
.shows_per_page
= shows_per_page
176 t
.my_url
= handler
.path
177 handler
.send_response(200)
178 handler
.send_header('Content-Type', 'text/html')
179 handler
.end_headers()
180 handler
.wfile
.write(t
)
182 def get_tivo_file(self
, url
, mak
, togo_path
):
184 cj
= cookielib
.LWPCookieJar()
186 parse_url
= urlparse
.urlparse(url
)
188 name
= unquote(parse_url
[2])[10:].split('.')
189 name
.insert(-1," - " + unquote(parse_url
[4]).split("id=")[1] + ".")
190 outfile
= os
.path
.join(togo_path
, "".join(name
))
192 r
= urllib2
.Request(url
)
193 auth_handler
= urllib2
.HTTPDigestAuthHandler()
194 auth_handler
.add_password('TiVo DVR', parse_url
[1], 'tivo', mak
)
195 opener
= urllib2
.build_opener(urllib2
.HTTPCookieProcessor(cj
),
197 urllib2
.install_opener(opener
)
200 handle
= urllib2
.urlopen(r
)
202 status
[url
]['running'] = False
203 status
[url
]['error'] = e
.code
206 f
= open(outfile
, 'wb')
208 start_time
= time
.time()
210 while status
[url
]['running']:
211 output
= handle
.read(1024000)
214 length
+= len(output
)
217 elapsed
= now
- start_time
219 status
[url
]['rate'] = int(length
/ elapsed
) / 1024
220 status
[url
]['size'] += length
223 if status
[url
]['running']:
224 status
[url
]['finished'] = True
225 except Exception, msg
:
226 logging
.getLogger('pyTivo.togo').info(msg
)
227 status
[url
]['running'] = False
231 def ToGo(self
, handler
, query
):
232 cname
= query
['Container'][0].split('/')[0]
233 tivoIP
= query
['TiVo'][0]
234 tivo_mak
= config
.get_server('tivo_mak')
235 togo_path
= config
.get_server('togo_path')
236 for name
, data
in config
.getShares():
237 if togo_path
== name
:
238 togo_path
= data
.get('path')
239 t
= Template(REDIRECT_TEMPLATE
)
240 t
.url
= query
['Redirect'][0]
242 if tivo_mak
and togo_path
:
243 theurl
= query
['Url'][0]
244 status
[theurl
] = {'running': True, 'error': '', 'rate': '',
245 'size': 0, 'finished': False}
246 thread
.start_new_thread(ToGo
.get_tivo_file
,
247 (self
, theurl
, tivo_mak
, togo_path
))
249 t
.text
= TRANS_INIT
% params
252 t
.text
= MISSING
% params
253 handler
.send_response(200)
254 handler
.send_header('Content-Type', 'text/html')
255 handler
.end_headers()
256 handler
.wfile
.write(t
)
258 def ToGoStop(self
, handler
, query
):
259 theurl
= query
['Url'][0]
260 status
[theurl
]['running'] = False
262 cname
= query
['Container'][0].split('/')[0]
263 tivoIP
= query
['TiVo'][0]
264 t
= Template(REDIRECT_TEMPLATE
)
266 t
.url
= query
['Redirect'][0]
267 t
.text
= TRANS_STOP
% (t
.url
)
268 handler
.send_response(200)
269 handler
.send_header('Content-Type', 'text/html')
270 handler
.end_headers()
271 handler
.wfile
.write(t
)