1 import os
, socket
, re
, sys
, ConfigParser
, config
, time
2 import urllib2
, cookielib
, thread
3 from xml
.dom
import minidom
4 from ConfigParser
import NoOptionError
5 from Cheetah
.Template
import Template
6 from plugin
import Plugin
7 from urllib
import unquote_plus
, quote
, unquote
8 from xml
.sax
.saxutils
import escape
9 from lrucache
import LRUCache
11 SCRIPTDIR
= os
.path
.dirname(__file__
)
15 p
= os
.path
.dirname(__file__
)
16 p
= p
.split(os
.path
.sep
)
19 p
= os
.path
.sep
.join(p
)
20 config_file_path
= os
.path
.join(p
, 'pyTivo.conf')
25 CONTENT_TYPE
= 'text/html'
27 def Reset(self
, handler
, query
):
29 handler
.server
.reset()
31 subcname
= query
['Container'][0]
32 cname
= subcname
.split('/')[0]
33 handler
.send_response(200)
35 t
= Template(file=os
.path
.join(SCRIPTDIR
,'templates', 'redirect.tmpl'))
37 t
.text
= '<h3>The pyTivo Server has been soft reset.</h3> <br>pyTivo has reloaded the pyTivo.conf file and all changed should now be in effect.'
38 handler
.wfile
.write(t
)
40 def Admin(self
, handler
, query
):
41 #Read config file new each time in case there was any outside edits
42 config
= ConfigParser
.ConfigParser()
43 config
.read(config_file_path
)
46 for section
in config
.sections():
47 if not(section
.startswith('_tivo_') or section
.startswith('Server')):
48 if not(config
.has_option(section
,'type')):
49 shares_data
.append((section
, dict(config
.items(section
, raw
=True))))
50 elif config
.get(section
,'type').lower() != 'admin':
51 shares_data
.append((section
, dict(config
.items(section
, raw
=True))))
53 subcname
= query
['Container'][0]
54 cname
= subcname
.split('/')[0]
55 handler
.send_response(200)
57 t
= Template(file=os
.path
.join(SCRIPTDIR
,'templates', 'settings.tmpl'))
59 t
.server_data
= dict(config
.items('Server', raw
=True))
60 t
.server_known
= ["port", "guid", "ffmpeg", "beacon", "hack83", "debug", \
61 "optres", "audio_br", "video_br", "max_video_br", "width",\
62 "height", "ffmpeg_prams", "bufsize", "precache"]
63 t
.shares_data
= shares_data
64 t
.shares_known
= ["type", "path", "auto_subshares"]
65 t
.tivos_data
= [ (section
, dict(config
.items(section
, raw
=True))) for section
in config
.sections() \
66 if section
.startswith('_tivo_')]
67 t
.tivos_known
= ["aspect169", "audio_br", "video_br", "width", "height", "ffmpeg_prams", "shares"]
68 handler
.wfile
.write(t
)
70 def UpdateSettings(self
, handler
, query
):
71 config
= ConfigParser
.ConfigParser()
72 config
.read(config_file_path
)
74 if key
.startswith('Server.'):
75 section
, option
= key
.split('.')
76 if option
== "new__setting":
77 new_setting
= query
[key
][0]
79 if option
== "new__value":
80 new_value
= query
[key
][0]
82 if query
[key
][0] == " ":
83 config
.remove_option(section
, option
)
85 config
.set(section
, option
, query
[key
][0])
86 if not(new_setting
== ' ' and new_value
== ' '):
87 config
.set('Server', new_setting
, new_value
)
89 sections
= query
['Section_Map'][0].split(']')
90 sections
.pop() #last item is junk
91 for section
in sections
:
92 ID
, name
= section
.split('|')
93 if query
[ID
][0] == "Delete_Me":
94 config
.remove_section(name
)
96 if query
[ID
][0] != name
:
97 config
.remove_section(name
)
98 config
.add_section(query
[ID
][0])
100 if key
.startswith(ID
+ '.'):
101 junk
, option
= key
.split('.')
102 if option
== "new__setting":
103 new_setting
= query
[key
][0]
105 if option
== "new__value":
106 new_value
= query
[key
][0]
108 if query
[key
][0] == " ":
109 config
.remove_option(query
[ID
][0], option
)
111 config
.set(query
[ID
][0], option
, query
[key
][0])
112 if not(new_setting
== ' ' and new_value
== ' '):
113 config
.set(query
[ID
][0], new_setting
, new_value
)
114 if query
['new_Section'][0] != " ":
115 config
.add_section(query
['new_Section'][0])
116 f
= open(config_file_path
, "w")
120 subcname
= query
['Container'][0]
121 cname
= subcname
.split('/')[0]
122 handler
.send_response(200)
123 handler
.end_headers()
124 t
= Template(file=os
.path
.join(SCRIPTDIR
,'templates', 'redirect.tmpl'))
126 t
.text
= '<h3>Your Settings have been saved.</h3> <br>You settings have been saved to the pyTivo.conf file. However you will need to do a <b>Soft Reset</b> before these changes will take effect.'
127 handler
.wfile
.write(t
)
129 def NPL(self
, handler
, query
):
130 folder
= '/NowPlaying'
131 if 'Folder' in query
:
132 folder
+= '/' + str(query
['Folder'][0])
133 theurl
= 'https://192.168.1.150/TiVoConnect?Command=QueryContainer&Container=' + folder
135 password
= '' #TiVo MAK
137 r
=urllib2
.Request(theurl
)
138 auth_handler
= urllib2
.HTTPDigestAuthHandler()
139 auth_handler
.add_password('TiVo DVR', '192.168.1.150', 'tivo', password
)
140 opener
= urllib2
.build_opener(auth_handler
)
141 urllib2
.install_opener(opener
)
144 handle
= urllib2
.urlopen(r
)
146 print "Possibly wrong Media Access Key, or IP address for your TiVo."
147 handler
.send_response(404)
148 handler
.end_headers()
150 thepage
= handle
.read()
152 xmldoc
= minidom
.parseString(thepage
)
153 items
= xmldoc
.getElementsByTagName('Item')
158 entry
['Title'] = item
.getElementsByTagName("Title")[0].firstChild
.data
159 entry
['ContentType'] = item
.getElementsByTagName("ContentType")[0].firstChild
.data
160 if (len(item
.getElementsByTagName("UniqueId")) >= 1):
161 entry
['UniqueId'] = item
.getElementsByTagName("UniqueId")[0].firstChild
.data
162 if entry
['ContentType'] == 'x-tivo-container/folder':
163 entry
['TotalItems'] = item
.getElementsByTagName("TotalItems")[0].firstChild
.data
164 entry
['LastChangeDate'] = item
.getElementsByTagName("LastChangeDate")[0].firstChild
.data
165 entry
['LastChangeDate'] = time
.strftime("%b %d, %Y", time
.localtime(int(entry
['LastChangeDate'], 16)))
167 link
= item
.getElementsByTagName("Links")[0]
168 if (len(link
.getElementsByTagName("CustomIcon")) >= 1):
169 entry
['Icon'] = link
.getElementsByTagName("CustomIcon")[0].getElementsByTagName("Url")[0].firstChild
.data
170 keys
= ['SourceSize', 'Duration', 'CaptureDate', 'EpisodeTitle', 'Description', 'SourceChannel', 'SourceStation']
173 entry
[key
] = item
.getElementsByTagName(key
)[0].firstChild
.data
176 entry
['SourceSize'] = "%.3f GB" % float(float(entry
['SourceSize'])/(1024*1024*1024))
177 entry
['Duration'] = str(int(entry
['Duration'])/(60*60*1000)).zfill(2) + ':' \
178 + str((int(entry
['Duration'])/60*1000)%60).zfill(2) + ':' \
179 + str((int(entry
['Duration'])/1000)%60).zfill(2)
180 entry
['CaptureDate'] = time
.strftime("%b %d, %Y", time
.localtime(int(entry
['CaptureDate'], 16)))
184 subcname
= query
['Container'][0]
185 cname
= subcname
.split('/')[0]
186 handler
.send_response(200)
187 handler
.end_headers()
188 t
= Template(file=os
.path
.join(SCRIPTDIR
,'templates', 'npl.tmpl'))
190 if folder
!= '/NowPlaying':
194 handler
.wfile
.write(t
)
196 def ToGo(self
, handler
, query
):
197 theurl
= "http://192.168.1.150/download/Survivor%20Micronesia%20--%20Fans%20vs.%20Favorites.TiVo?Container=%2FNowPlaying&id=385244"
198 password
= '' #TiVo MAK
199 tivoIP
= '192.168.1.150'
200 outfile
= "video.tivo"
202 status
[theurl
] = {'running':True, 'error':'', 'rate':''}
204 thread
.start_new_thread(get_tivo_file
, (theurl
, password
, tivoIP
, outfile
))
207 def get_tivo_file(url
, mak
, tivoIP
, outfile
):
209 cj
= cookielib
.LWPCookieJar()
211 r
=urllib2
.Request(url
)
212 auth_handler
= urllib2
.HTTPDigestAuthHandler()
213 auth_handler
.add_password('TiVo DVR', tivoIP
, 'tivo', mak
)
214 opener
= urllib2
.build_opener(urllib2
.HTTPCookieProcessor(cj
), auth_handler
)
215 urllib2
.install_opener(opener
)
218 handle
= urllib2
.urlopen(r
)
220 #If we get "Too many transfers error" try a second time. For some reason
221 #urllib2 does not properly close connections when a transfer is canceled.
224 handle
= urllib2
.urlopen(r
)
226 status
[url
]['running'] = False
227 status
[url
]['error'] = e
.code
230 status
[url
]['running'] = False
231 status
[url
]['error'] = e
.code
234 f
= open(outfile
, 'wb')
236 start_time
= time
.time()
237 output
= handle
.read(1024)
238 while status
[url
]['running'] and output
!= '':
241 if ((time
.time() - start_time
) >= 5):
242 status
[url
]['rate'] = int(kilobytes
/(time
.time() - start_time
))
244 start_time
= time
.time()
245 output
= handle
.read(1024)
246 status
[url
]['running'] = False