Initial Commit
[Projects.git] / pytivo-youtube / source / plugins / youtube / .svn / text-base / youtube.py.svn-base
blob2f62bc4c5c2577f21d6309345f6a598c8070be9f
1 import os, sys, socket, re, urllib, zlib
2 from Cheetah.Template import Template
3 from plugin import Plugin, quote, unquote
4 from urlparse import urlparse
5 from xml.sax.saxutils import escape
6 from lrucache import LRUCache
7 from UserDict import DictMixin
8 from datetime import datetime, timedelta
9 import config
10 import time
11 import subprocess
12 import mind
13 import logging
14 import shutil
15 from xml.dom.minidom import parseString
16 import urllib
17 from Cheetah.Filters import Filter
19 SCRIPTDIR = os.path.dirname(__file__)
21 CLASS_NAME = 'Youtube'
23 default = "http://gdata.youtube.com/feeds/api/standardfeeds/top_rated"
25 # Preload the templates
26 tcname = os.path.join(SCRIPTDIR, 'templates', 'container.tmpl')
27 ttname = os.path.join(SCRIPTDIR, 'templates', 'TvBus.tmpl')
28 txname = os.path.join(SCRIPTDIR, 'templates', 'container.xsl')
29 CONTAINER_TEMPLATE = file(tcname, 'rb').read()
30 TVBUS_TEMPLATE = file(ttname, 'rb').read()
31 XSL_TEMPLATE = file(txname, 'rb').read()
33 # XXX BIG HACK
34 # subprocess is broken for me on windows so super hack
35 def patchSubprocess():
36     o = subprocess.Popen._make_inheritable
38     def _make_inheritable(self, handle):
39         if not handle: return subprocess.GetCurrentProcess()
40         return o(self, handle)
42     subprocess.Popen._make_inheritable = _make_inheritable
44 mswindows = (sys.platform == "win32")
45 if mswindows:
46     patchSubprocess()
48 def kill(pid):
49     if mswindows:
50         win32kill(pid)
51     else:
52         import signal
53         os.kill(pid, signal.SIGTERM)
55 def win32kill(pid):
56     import ctypes
57     handle = ctypes.windll.kernel32.OpenProcess(1, False, pid)
58     ctypes.windll.kernel32.TerminateProcess(handle, -1)
59     ctypes.windll.kernel32.CloseHandle(handle)
61 def qoramp(path):
62     if path.find("?") == -1:
63         return "?"
64     else:
65         return "&"
67 class Youtube(Plugin):
68     CONTENT_TYPE = 'x-container/tivo-videos'
70     videos_feed = "http://gdata.youtube.com/feeds/api/videos"
71     watch_video = "http://www.youtube.com/watch"
72     get_video = 'http://www.youtube.com/get_video'
73     standardfeeds = "http://gdata.youtube.com/feeds/api/standardfeeds/%s"
74     standardfeedsregion = "http://gdata.youtube.com/feeds/api/standardfeeds/%s/%s"
75     user_feed = "http://gdata.youtube.com/feeds/api/users/%s/%s"
76     playlist = "http://gdata.youtube.com/feeds/api/playlists/%s"
78     videos = {}
80     #def __init__(self):
81     #    try:
82     #        from multiprocessing import Process, Lock
83     #        p = Process(target=self.LoadVideos)
84     #        p.start()
85     #        p.join()
86     #    except(ImportError):
87     #        return self
88     #   return self
89     
90     def __init__(self):
91         #logging.debug('Starting Youtube Plugin Cache')
92         #self.QueryContainer()
93         return
95     #def LoadVideos(self):
96     #    while 1:
97     #        time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime())
98     #        for k,v in videos.iteritems():
100     def send_file(self, handler, container, name):
101         if handler.headers.getheader('Range') and \
102            handler.headers.getheader('Range') != 'bytes=0-':
103             handler.send_response(206)
104             handler.send_header('Connection', 'close')
105             handler.send_header('Content-Type', 'video/x-tivo-mpeg')
106             handler.send_header('Transfer-Encoding', 'chunked')
107             handler.end_headers()
108             handler.wfile.write("\x30\x0D\x0A")
109             return
111         def between(data, From, To):
112             start = data.find(From)
113             if start == -1:
114                 return ''
115             start += len(From)
116             end = data.find(To, start)
117             if end == -1:
118                 return ''
119             return data[start:end]
121         page = urllib.urlopen(self.watch_video + "?v=%s" % handler.path.split("/")[2]).read()
122         ticket = between(page, '"t": "', '"')
123         if not ticket:
124             handler.send_error(404)
125             return None
126         
127         url = self.get_video + "?video_id=%s&t=%s" % (handler.path.split("/")[2], ticket)
129         if container.has_key("fmt"):
130             url = url + "&fmt=%s" % (container.get("fmt"))
132         logging.debug('download url is %s' % url)
133         handler.send_response(200)
134         handler.end_headers()
136         settings = '-vcodec mpeg2video -r 29.97 -b 4096K -maxrate 17408k -bufsize 1024k -aspect 4:3 -s 544x480 -ab 192k -ar 48000 -acodec mp2 -ac 2 -f vob -'
137         if mswindows:
138             url = urllib.urlopen(url).geturl()
139             cmd = [config.get('Server', 'ffmpeg'), '-i', url] + settings.split(' ')
140             logging.debug('transcoding using ffmpeg command:')
141             logging.debug(' '.join(cmd))
142             ffmpeg = subprocess.Popen(cmd, bufsize=(512 * 1024), stdout=subprocess.PIPE)
143         else:
144             cmd = [config.get('Server', 'ffmpeg'), '-i', '-'] + settings.split(' ')
145             source = urllib.urlopen(url)
146             logging.debug('transcoding using ffmpeg command:')
147             logging.debug(' '.join(cmd))
148             ffmpeg = subprocess.Popen(cmd, bufsize=(512 * 1024), stdout=subprocess.PIPE, stdin=source)            
149         
150         try:
151             shutil.copyfileobj(ffmpeg.stdout, handler.wfile)
152         except:
153             kill(ffmpeg.pid)
155         source.close()
157     def __duration(self, full_path):
158         return transcode.video_info(full_path)['millisecs']
160     def __est_size(self, full_path, tsn = ''):
161         return int(full_path.duration) * 54672
163     def getVideo(self, entry):
164         video = {}
165         for contentlink in entry.getElementsByTagName("media:content"):
166             video['format'] = contentlink.attributes["yt:format"].value
167             video[video['format']] = contentlink.attributes["url"].value
168             video['type'] = contentlink.attributes["type"].value # always application/x-shockwave-flash ?
169             video['medium'] = contentlink.attributes["medium"].value
170             video['duration'] = contentlink.attributes["duration"].value
172         nodes = ['summary', 'id', 'published', 'title', 'updated', 'yt:aboutMe', 'yt:age', 'yt:books', 'yt:company', 'yt:countHint', 'yt:firstName', 'yt:gender', 'yt:hobbies', 'yt:hometown', 'yt:lastName', 'yt:location', 'yt:movies', 'yt:music', 'yt:noembed', 'yt:occupation', 'yt:playlistId', 'yt:playlistTitle', 'yt:position', 'yt:private', 'yt:queryString', 'yt:recorded', 'yt:relationship', 'yt:school', 'yt:uploaded', 'yt:username', 'yt:status', 'yt:videoid', 'media:category', 'media:description', 'media:keywords', 'media:rating', 'media:restriction', 'media:title', 'gml:pos', 'app:edited']
173         for nodename in nodes:
174             try:
175                 node = entry.getElementsByTagName(nodename)[0]
176                 if nodename.find(":")!=-1:
177                     nodename = nodename.split(":")[1]
178                 if node.nodeType == node.ELEMENT_NODE:
179                     video[nodename] = node.firstChild.data
180                 elif node.nodeType == node.TEXT_NODE:
181                     video[nodename] = node.data
182             except(IndexError, AttributeError):
183                 if nodename.find(":")!=-1:
184                     nodename = nodename.split(":")[1]
185                 video[nodename] = False
187         if entry.getElementsByTagName("yt:state"):
188             video['playable'] = False
189         else:
190             video['playable'] = True
192         if video['playlistId']:
193             video['id'] = video['playlistId']
194             video['group'] = True
195             video['title'] = self.getTitle(entry)
196             url = self.playlist % video['id']
197             feed = parseString(urllib.urlopen(url).read())
198             video['type'] = "playlist"
199             video['isdir'] = True
200             logging.debug('Getting playlist %s at %s' % (video['title'], url))
201             self.videos.update({video['id']: self.getVideos(feed)})
202             video['total_items'] = len(self.videos[video['id']])
203         elif video['videoid']:
204             video['id'] = video['videoid']
205             if entry.getElementsByTagName('gd:rating'):
206                 rating = entry.getElementsByTagName("gd:rating")[0]
207                 video['rating'] = rating.getAttribute('average')
208             else:
209                 video['rating'] = '3'
210             video['type'] = "video"
211             video['isdir'] = False
212             video['duration'] = entry.getElementsByTagName("yt:duration")[0].attributes["seconds"].value
213             video['vProgramGenre'] = video['keywords'].split(", ")
214             video['channelnumber'] = "0" # maybe have different channels for each youtube channel?
215             video['channelname'] = "YOUTUBE" # maybe have different names for each youtube channel?
216             video['showingBits'] = "1" # change this for different youtube ratings
217             video['displayMinorNumber'] = "111"
218             video['episodeTitle'] = video['title']
219             video['seriesTitle'] = video['title']
220             video['isEpisode'] = 'false'
221             video['vChoreographer'] = []
222             #author = entry.getElementsByTagName("author")[0]
223             #video['author'] = author.getElementsByTagName("name")[0].firstChild.data
224             #video['author.uri'] = author.getElementsByTagName("uri")[0].firstChild.data
225             video['author'] = entry.getElementsByTagName("media:credit")[0].firstChild.data
226             video['vActor'] = [video['author']]
227             video['vExecProducer'] = []
228             video['vGuestStar'] = []
229             video['vSeriesGenre'] = []
230             video['vHost'] = []
231             video['vWriter'] = []
232             video['vProducer'] = []
233             video['showType'] = ('SERIES', '5')
234             video['tvRating'] = "5"
235             if video['rating']:
236                 video['starRating'] = str(round(float(video['rating']) * 7 / 5)).split(".")[0]
237             else:
238                 video['starRating'] = '4'
239             video['vDirector'] = []
240             video['mpaaRating'] = "N8"
241             video['episodeNumber'] = '1'
242             video['seriesId'] = ""
243     
244             duration_delta = timedelta(milliseconds=int(video['duration'])*1000)
245     
246             try:
247                 from xml.utils import iso8601
248                 date = iso8601.parse(text(video['uploaded']))
249                 utcdate = datetime.utcfromtimestamp(date)
250                 video['time'] = date.isoformat()
251                 video['startTime'] = date.isoformat()
252                 video['stopTime'] = date+duration
253             except(ImportError):
254                 now = datetime.utcnow()
255                 video['time'] = now.isoformat()
256                 video['startTime'] = now.isoformat()
257                 video['stopTime'] = (now + duration_delta).isoformat()
258     
259             #video['originalAirDate'] = video['uploaded'].replace(".000Z", "")
260             video['movieYear'] = video['uploaded'].split("T")[0].split("-")[0]
261     
262             min = duration_delta.seconds / 60
263             sec = duration_delta.seconds % 60
264             hours = min / 60
265             min = min % 60
266             video['iso_duration'] = 'P' + str(duration_delta.days) + \
267                                        'DT' + str(hours) + 'H' + str(min) + \
268                                        'M' + str(sec) + 'S'
269             video['size'] = int(video['duration']) * 546720
270             video['milliseconds'] = int(video['duration']) * 1000
271         elif video['username']:
272             folder['group'] = True
273             url = entry.getElementsByTagName("content")[0].getAttribute('src')
274             feed = parseString(urllib.urlopen(url).read())
275             video['title'] = self.getTitle(feed)
276             video['id'] = video['username']
277             video['type'] = "subscription"
278             video['isdir'] = True
279             self.videos[video['id']] = self.getVideos(feed)
280             video['total_items'] = len(self.videos[video['id']])
281         elif entry.getElementsByTagName("media:player"):
282             video['id'] = entry.getElementsByTagName("media:player")[0].getAttribute('url').replace('http://www.youtube.com/watch?v=', '')
283             rating = entry.getElementsByTagName("gd:rating")[0]
284             video['rating'] = rating.getAttribute('average')
285             video['type'] = "video"
286             video['isdir'] = False
287             video['duration'] = entry.getElementsByTagName("yt:duration")[0].attributes["seconds"].value
288             video['vProgramGenre'] = video['keywords'].split(", ")
289             video['channelnumber'] = "0" # maybe have different channels for each youtube channel?
290             video['channelname'] = "YOUTUBE" # maybe have different names for each youtube channel?
291             video['showingBits'] = "1" # change this for different youtube ratings
292             video['displayMinorNumber'] = "111"
293             video['episodeTitle'] = video['title']
294             video['seriesTitle'] = video['title']
295             video['isEpisode'] = 'false'
296             video['vChoreographer'] = []
297             author = entry.getElementsByTagName("author")[0]
298             video['author'] = author.getElementsByTagName("name")[0].firstChild.data
299             #video['author.uri'] = author.getElementsByTagName("uri")[0].firstChild.data
300             video['vActor'] = [video['author']]
301             video['vExecProducer'] = []
302             video['vGuestStar'] = []
303             video['vSeriesGenre'] = []
304             video['vHost'] = []
305             video['vWriter'] = []
306             video['vProducer'] = []
307             video['showType'] = ('SERIES', '5')
308             video['tvRating'] = "5"
309             if video['rating']:
310                 video['starRating'] = str(round(float(video['rating']) * 7 / 5)).split(".")[0]
311             else:
312                 video['starRating'] = '4'
313             video['vDirector'] = []
314             video['mpaaRating'] = "N8"
315             video['episodeNumber'] = '1'
316             video['seriesId'] = ""
317     
318             duration_delta = timedelta(milliseconds=int(video['duration'])*1000)
319     
320             try:
321                 from xml.utils import iso8601
322                 date = iso8601.parse(text(video['uploaded']))
323                 utcdate = datetime.utcfromtimestamp(date)
324                 video['time'] = date.isoformat()
325                 video['startTime'] = date.isoformat()
326                 video['stopTime'] = date+duration
327             except(ImportError):
328                 now = datetime.utcnow()
329                 video['time'] = now.isoformat()
330                 video['startTime'] = now.isoformat()
331                 video['stopTime'] = (now + duration_delta).isoformat()
332     
333             #video['originalAirDate'] = video['uploaded'].replace(".000Z", "")
334             video['movieYear'] = video['updated'].split("T")[0].split("-")[0]
335     
336             min = duration_delta.seconds / 60
337             sec = duration_delta.seconds % 60
338             hours = min / 60
339             min = min % 60
340             video['iso_duration'] = 'P' + str(duration_delta.days) + \
341                                        'DT' + str(hours) + 'H' + str(min) + \
342                                        'M' + str(sec) + 'S'
343             video['size'] = int(video['duration']) * 546720
344             video['milliseconds'] = int(video['duration']) * 1000
345         else:
346             logging.debug('The entry named %s probably does not have proper pyTiVo Youtube Plugin support' % video['title'])
347             return False
349         return video
350    
351     def getVideos(self, feed):
352         videos = []
353         for entry in feed.getElementsByTagName("entry"):
354             video = self.getVideo(entry)
355             if video: videos.append(video)
356         return videos
358     def getTitle(self, feed):
359         return feed.getElementsByTagName("title")[0].firstChild.data
361     def item_count(self, query, files, last_start=0):
362         """Return only the desired portion of the list, as specified by 
363            ItemCount, AnchorItem and AnchorOffset. 'files' is either a 
364            list of objects with an 'id' attribute.
365         """
366         totalFiles = len(files)
367         index = 0
369         if totalFiles and query.has_key('ItemCount'):
370             count = int(query['ItemCount'][0])
372             if query.has_key('AnchorItem'):
373                 anchor = query['AnchorItem'][0]
374                 anchor = unquote(anchor)
375                 anchor = anchor.replace("/%s/" % query['Container'][0].split("/")[0], "", 1)
376                 anchor = anchor.replace("/TiVoConnect?Command=QueryContainer&Container=%s&id=" % query['Container'][0],'',1)
377                 anchor = anchor.replace("/TiVoConnect?Command=QueryContainer&Container=%s/" % query['Container'][0],'',1)
379                 filenames = [x['id'] for x in files]
381                 try:
382                     index = filenames.index(anchor)
383                 except ValueError:
384                     logging.debug('Anchor not found: %s' % anchor)
386                 if count > 0:
387                     index += 1
389                 if query.has_key('AnchorOffset'):
390                     index += int(query['AnchorOffset'][0])
392                 #foward count
393                 if count >= 0:
394                     files = files[index:index + count]
395                 #backwards count
396                 else:
397                     if index + count < 0:
398                         count = -index
399                     files = files[index + count:index]
400                     index += count
402             else:  # No AnchorItem
404                 if count >= 0:
405                     files = files[:count]
406                 else:
407                     index = count % len(files)
408                     files = files[count:]
410         return files, totalFiles, index
412     def QueryContainer(self, handler, query):
413         tsn = handler.headers.getheader('tsn', '')
414         subcname = query['Container'][0]
415         cname = subcname.split('/')[0]
416         container = handler.server.containers[cname]
418         try:
419             id = query['id'][0]
420         except(KeyError):
421             id = subcname
423         if subcname.find("/")!=-1:
424             id = id.split("/")[1]
425             try:
426                 videos = self.videos[id]
427             except(KeyError):
428                 path = self.playlist % id
429                 logging.debug('Getting: %s' % path)
430                 feed = parseString(urllib.urlopen(path).read())
431                 self.videos.update({id: self.getVideos(feed)})
432                 videos = self.videos[id]
433         else:
434             try:
435                 videos = self.videos[id]
436             except(KeyError):
437                 path = ""
438                 if container.has_key('user') and not container.has_key('path') and not container.has_key('playlist'):
439                     userfolders = ["uploads", "favorites", "playlists"]
440                     videos = []
441                     for folder in userfolders:
442                         video = {}
443                         path = "http://gdata.youtube.com/feeds/api/users/%s/%s" % (container.get('user'), folder)
444                         try:
445                             feed = parseString(urllib.urlopen(path).read())
446                         except:
447                             continue
448                         self.videos.update({"%s/%s" % (subcname,folder): self.getVideos(feed)})
449                         video['total_items'] = len(self.videos["%s/%s" % (subcname,folder)])
450                         video['group'] = False
451                         video['title'] = folder
452                         video['id'] = "%s/%s" % (subcname,folder)
453                         video['isdir'] = True
454                         videos.append(video)
455                 else:
456                     if container.has_key('path'):
457                         if container.get('path').startswith("http://gdata.youtube.com/feeds/"):
458                             path = container.get('path')
459                         elif container.has_key("author"):
460                             path = self.user_feed % (container.get('author'),container.get('path'))
461                         elif container.has_key("user"):
462                             path = self.user_feed % (container.get('user'),container.get('path'))
463                         else:
464                             path = container.get('path').replace(" ", "_")
465                             path = path.lower()
466                             if container.has_key("category"):
467                                 path = "%s_%s" % (path, container.get("category"))
468                             if container.has_key("region"):
469                                 self.standardfeedsregion % (container.get("region").upper(), path)
470                             else:
471                                 path = self.standardfeeds % path
472                     elif container.has_key('playlist'):
473                         path = self.playlist % container.get('playlist')
475                     if container.has_key('search'):
476                         path = path + "%sq=%s" % (qoramp(path), container.get('search'))
477                         if path.split("?")[0] == "":
478                             path = self.videos_feed + path
479                     if container.has_key('q'):
480                         path = path + "%sq=%s" % (qoramp(path), container.get('q'))
481                     if container.has_key('start-index'):
482                         path = path + "%sstart-index=%s" % (qoramp(path), container.get('start-index'))
483                     if container.has_key('max'):
484                         path = path + "%smax-results=%s" % (qoramp(path), container.get('max'))
485                     elif container.has_key('max-results'):
486                         path = path + "%smax-results=%s" % (qoramp(path), container.get('max-results'))
487                     else:
488                         path = path + "%smax-results=%s" % (qoramp(path), '50')
489                     if container.has_key('safeSearch'):
490                         path = path + "%ssafeSearch=%s" % (qoramp(path), container.get('safeSearch'))
491                     else:
492                         path = path + "%ssafeSearch=%s" % (qoramp(path), "strict")
493                     if container.has_key("time"):
494                         path = path + "%stime=%s" % (qoramp(path), container.get('time'))
495                     if container.has_key("uploader"):
496                         path = path + "%suploader=%s" % (qoramp(path), container.get('uploader'))
497                     if container.has_key("restriction"):
498                         path = path + "%srestriction=%s" % (qoramp(path), container.get('restriction'))
499                     if container.has_key("orderby"):
500                         path = path + "%sorderby=%s" % (qoramp(path), container.get('orderby'))
501                     if container.has_key("lr"):
502                         path = path + "%slr=%s" % (qoramp(path), container.get('lr'))            
503                     if container.has_key("location-radius"):
504                         path = path + "%slocation-radius=%s" % (qoramp(path), container.get('location-radius'))
505                     if container.has_key("location"):
506                         path = path + "%slocation=%s" % (qoramp(path), container.get('location'))
507                     if container.has_key("format"):
508                         path = path + "%sformat=%s" % (qoramp(path), container.get('format'))
509                     if container.has_key("client"):
510                         path = path + "%sclient=%s" % (qoramp(path), container.get('client'))
511                     if container.has_key("category"):
512                         path = path + "%scategory=%s" % (qoramp(path), container.get('category'))
513                     if container.has_key('v'):
514                         path = path + "%sv=%s" % (qoramp(path), container.get('v'))
515                     elif container.has_key('version'):
516                         path = path + "%sv=%s" % (qoramp(path), container.get('version'))
517                     else:
518                         path = path + "%sv=2" % (qoramp(path))
520                     logging.debug('Getting: %s' % path)
521                     feed = parseString(urllib.urlopen(path).read())
523                     self.videos.update({id: self.getVideos(feed)})
524                     videos = self.videos[id]
526             if container.has_key("refresh"):
527                 if container.get("refresh").lower() == "yes":
528                     refresh = {}
529                     refresh['total_items'] = 1
530                     refresh['group'] = False
531                     refresh['title'] = "Refresh"
532                     refresh['id'] = "refresh"
533                     refresh['isdir'] = True
534                     videos.append(refresh)
536         if container.has_key("group"):
537             folders = []
538             groups = []
539             for video in videos:
540                 if video[container.get("group")] in groups:
541                     self.videos["%s/%s" % (subcname,video[container.get("group")])].append(video)
542                     for folder in folders:
543                         if folder['id'] == "%s/%s" % (subcname,video[container.get("group")]):
544                             folder['total_items'] = folder['total_items'] + 1
545                 else:
546                     self.videos["%s/%s" % (subcname,video[container.get("group")])] = []
547                     self.videos["%s/%s" % (subcname,video[container.get("group")])].append(video)
548                     groups.append(video[container.get("group")])
549                     folder = {}
550                     folder['total_items'] = 1
551                     folder['group'] = False
552                     folder['title'] = video[container.get("group")]
553                     folder['id'] = "%s/%s" % (subcname,video[container.get("group")])
554                     folder['isdir'] = True
555                     folders.append(folder)
556             for folder in folders:
557                 if folder['total_items'] == 1:
558                     folder.update(self.videos[folder['id']][0])
559             videos, total, start = self.item_count(query, folders)
560         else:
561             videos, total, start = self.item_count(query, videos)
563         handler.send_response(200)
564         handler.end_headers()
565         t = Template(CONTAINER_TEMPLATE, filter=EncodeUnicode)
566         t.container = cname
567         t.name = subcname #self.getTitle(feed)
568         t.total = total
569         t.start = start
570         t.videos = videos
571         t.quote = quote
572         t.escape = escape
573         t.crc = zlib.crc32
574         t.guid = config.getGUID()
575         t.tivos = handler.tivos
576         t.tivo_names = handler.tivo_names
577         handler.wfile.write(t)
579     def TVBusQuery(self, handler, query):
580         tsn = handler.headers.getheader('tsn', '')
581         id = query['id'][0]
582         search = "http://gdata.youtube.com/feeds/api/videos?q=%s&max-results=%s&v=2"
584         try:
585             found=False
586             for video in self.videos[query['Container'][0]]:
587                 if video['id'] == id:
588                     file_info = video
589                     found=True
590                     break
591             if found:
592                 file_info = self.getVideo(parseString(urllib.urlopen(search % (id,"1")).read()))
593             else:
594                 file_info = self.getVideo(parseString(urllib.urlopen(search % (id,"1")).read()))
595         except(KeyError):
596             file_info = self.getVideo(parseString(urllib.urlopen(search % (id,"1")).read()))
598         handler.send_response(200)
599         handler.end_headers()
600         t = Template(TVBUS_TEMPLATE, filter=EncodeUnicode)
601         t.video = file_info
602         t.escape = escape
603         handler.wfile.write(t)
605     def XSL(self, handler, query):
606         handler.send_response(200)
607         handler.end_headers()
608         handler.wfile.write(XSL_TEMPLATE)
610     def Push(self, handler, query):
611         id = unquote(query['id'][0])
613         search = "http://gdata.youtube.com/feeds/api/videos?q=%s&max-results=%s&v=2"
615         tsn = query['tsn'][0]
616         for key in handler.tivo_names:
617             if handler.tivo_names[key] == tsn:
618                 tsn = key
619                 break
621         try:
622             found = False
623             for video in self.videos[query['Container'][0]]:
624                 if video['id'] == id:
625                     file_info = video
626                     found = True
627                     break
628             if not found:
629                 file_info = self.getVideo(parseString(urllib.urlopen(search % (id,"1")).read()))
630         except(KeyError):
631             file_info = self.getVideo(parseString(urllib.urlopen(search % (id,"1")).read()))
633         import socket
634         s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
635         s.connect(('tivo.com',123))
636         ip = s.getsockname()[0]
637         container = quote(query['Container'][0].split('/')[0])
638         port = config.getPort()
640         url = 'http://%s:%s/%s/%s' % (ip, port, container, quote(id))
642         try:
643             m = mind.getMind()
644             m.pushVideo(
645                 tsn = tsn,
646                 url = url,
647                 description = file_info['description'],
648                 duration = int(file_info['duration']) / 1000,
649                 size = file_info['size'],
650                 title = file_info['title'],
651                 subtitle = file_info['title'])
652         except Exception, e:
653             import traceback
654             handler.send_response(500)
655             handler.end_headers()
656             handler.wfile.write('%s\n\n%s' % (e, traceback.format_exc() ))
657             raise
659         referer = handler.headers.getheader('Referer')
660         handler.send_response(302)
661         handler.send_header('Location', referer)
662         handler.end_headers()
664 class EncodeUnicode(Filter):
665     def filter(self, val, **kw):
666         """Encode Unicode strings, by default in UTF-8"""
668         if kw.has_key('encoding'):
669             encoding = kw['encoding']
670         else:
671             encoding='utf8'
673         if type(val) == type(u''):
674             filtered = val.encode(encoding)
675         else:
676             filtered = str(val)
677         return filtered
679 class VideoDetails(DictMixin):
681     def __init__(self, d=None):
682         if d:
683             self.d = d
684         else:
685             self.d = {}
687     def __getitem__(self, key):
688         if key not in self.d:
689             self.d[key] = self.default(key)
690         return self.d[key]
692     def __contains__(self, key):
693         return True
695     def __setitem__(self, key, value):
696         self.d[key] = value
698     def __delitem__(self):
699         del self.d[key]
701     def keys(self):
702         return self.d.keys()
704     def __iter__(self):
705         return self.d.__iter__()
707     def iteritems(self):
708         return self.d.iteritems()
710     def default(self, key):
711         defaults = {
712             'showingBits' : '0',
713             'episodeNumber' : '0',
714             'displayMajorNumber' : '0',
715             'displayMinorNumber' : '0',
716             'isEpisode' : 'true',
717             'colorCode' : ('COLOR', '4'),
718             'showType' : ('SERIES', '5'),
719             'tvRating' : ('NR', '7')
720         }
721         if key in defaults:
722             return defaults[key]
723         elif key.startswith('v'):
724             return []
725         else:
726             return ''