-pyTivo
[pyTivo.git] / plugins / video / video.py
blob0212157ddef8595d3ac63d4929104154edc33d67
1 import transcode, os, socket, re
2 from Cheetah.Template import Template
3 from plugin import Plugin
4 from urllib import unquote_plus, quote, unquote
5 from urlparse import urlparse
6 from xml.sax.saxutils import escape
7 from lrucache import LRUCache
8 import Config
10 SCRIPTDIR = os.path.dirname(__file__)
13 class video(Plugin):
15 content_type = 'x-container/tivo-videos'
17 # Used for 8.3's broken requests
18 request_history = {}
20 def SendFile(self, handler, container, name):
22 #No longer a 'cheep' hack :p
23 if handler.headers.getheader('Range') and not handler.headers.getheader('Range') == 'bytes=0-':
24 handler.send_response(206)
25 handler.send_header('Connection', 'close')
26 handler.send_header('Content-Type', 'video/x-tivo-mpeg')
27 handler.send_header('Transfer-Encoding', 'chunked')
28 handler.send_header('Server', 'TiVo Server/1.4.257.475')
29 handler.end_headers()
30 handler.wfile.write("\x30\x0D\x0A")
31 return
33 tsn = handler.headers.getheader('tsn', '')
35 o = urlparse("http://fake.host" + handler.path)
36 path = unquote_plus(o[2])
37 handler.send_response(200)
38 handler.end_headers()
39 transcode.output_video(container['path'] + path[len(name)+1:], handler.wfile, tsn)
41 def RequestHack(self, tsn, subcname):
42 print 'Requesting', subcname
43 # Not a tivo act like a normal http server
44 if not tsn:
45 return subcname
47 # Have not seen before save this request
48 if tsn not in self.request_history:
49 self.request_history[tsn] = (subcname, '')
50 return subcname
52 #Asking for the root this is always correct
53 if len(subcname.split('/')) == 1:
54 return subcname
56 # Always replay the last request
57 # so when the tivo gives us the correct request give the incorect responce
59 self.request_history[tsn] = (subcname, self.request_history[tsn][0])
60 return self.request_history[tsn][1]
65 def QueryContainer(self, handler, query):
67 tsn = handler.headers.getheader('tsn', '')
68 subcname = query['Container'][0]
70 subcname = self.RequestHack(tsn, subcname)
71 query['Container'][0] = subcname
73 print 'Serveing', subcname
75 cname = subcname.split('/')[0]
77 if not handler.server.containers.has_key(cname) or not self.get_local_path(handler, query):
78 handler.send_response(404)
79 handler.end_headers()
80 return
82 path = self.get_local_path(handler, query)
83 def isdir(file):
84 return os.path.isdir(os.path.join(path, file))
86 def duration(file):
87 full_path = os.path.join(path, file)
88 return transcode.video_info(full_path)[4]
90 def est_size(file):
91 full_path = os.path.join(path, file)
92 #Size is estimated by taking audio and video bit rate adding 2%
94 if transcode.tivo_compatable(full_path): # Is TiVo compatible mpeg2
95 return int(os.stat(full_path).st_size)
96 else: # Must be re-encoded
97 audioBPS = strtod(Config.getAudioBR())
98 videoBPS = strtod(Config.getVideoBR())
99 bitrate = audioBPS + videoBPS
100 return int((duration(file)/1000)*(bitrate * 1.02 / 8))
102 def VideoFileFilter(file):
103 full_path = os.path.join(path, file)
105 if os.path.isdir(full_path):
106 return True
107 return transcode.suported_format(full_path)
109 handler.send_response(200)
110 handler.end_headers()
111 t = Template(file=os.path.join(SCRIPTDIR,'templates', 'container.tmpl'))
112 t.name = subcname
113 t.files, t.total, t.start = self.get_files(handler, query, VideoFileFilter)
114 t.duration = duration
115 t.est_size = est_size
116 t.isdir = isdir
117 t.quote = quote
118 t.escape = escape
119 handler.wfile.write(t)
122 # Parse a bitrate using the SI/IEEE suffix values as if by ffmpeg
123 # For example, 2K==2000, 2Ki==2048, 2MB==16000000, 2MiB==16777216
124 # Algorithm: http://svn.mplayerhq.hu/ffmpeg/trunk/libavcodec/eval.c
125 def strtod(value):
126 prefixes = {"y":-24,"z":-21,"a":-18,"f":-15,"p":-12,"n":-9,"u":-6,"m":-3,"c":-2,"d":-1,"h":2,"k":3,"K":3,"M":6,"G":9,"T":12,"P":15,"E":18,"Z":21,"Y":24}
127 p = re.compile(r'^(\d+)(?:([yzafpnumcdhkKMGTPEZY])(i)?)?([Bb])?$')
128 m = p.match(value)
129 if m is None:
130 raise SyntaxError('Invalid bit value syntax')
131 (coef, prefix, power, byte) = m.groups()
132 if prefix is None:
133 value = float(coef)
134 else:
135 exponent = float(prefixes[prefix])
136 if power == "i":
137 # Use powers of 2
138 value = float(coef) * pow(2.0, exponent/0.3)
139 else:
140 # Use powers of 10
141 value = float(coef) * pow(10.0, exponent)
142 if byte == "B": # B==Byte, b=bit
143 value *= 8;
144 return value