couple more vInfo corrections
[pyTivo/krkeegan.git] / config.py
blob96e90224cae546a75038035ef8379a6bc90e9a17
1 import ConfigParser, os, sys
2 import re
3 import random
4 import string
5 from ConfigParser import NoOptionError
7 BLACKLIST_169 = ('540', '649')
8 guid = ''.join([random.choice(string.letters) for i in range(10)])
10 config = ConfigParser.ConfigParser()
11 p = os.path.dirname(__file__)
13 config_files = [
14 '/etc/pyTivo.conf',
15 os.path.join(p, 'pyTivo.conf'),
17 config_exists = False
18 for config_file in config_files:
19 if os.path.exists(config_file):
20 config_exists = True
21 if not config_exists:
22 print 'ERROR: pyTivo.conf does not exist.\n' + \
23 'You must create this file before running pyTivo.'
24 sys.exit(1)
25 config.read(config_files)
27 def reset():
28 global config
29 del config
30 config = ConfigParser.ConfigParser()
31 config.read(config_files)
33 def getGUID():
34 if config.has_option('Server', 'GUID'):
35 return config.get('Server', 'GUID')
36 else:
37 return guid
39 def getTivoUsername():
40 return config.get('Server', 'tivo_username')
42 def getTivoPassword():
43 return config.get('Server', 'tivo_password')
45 def getBeaconAddresses():
46 if config.has_option('Server', 'beacon'):
47 beacon_ips = config.get('Server', 'beacon')
48 else:
49 beacon_ips = '255.255.255.255'
50 return beacon_ips
52 def getPort():
53 return config.get('Server', 'Port')
55 def get169Blacklist(tsn): # tivo does not pad 16:9 video
56 return tsn != '' and tsn[:3] in ('540')
58 def get169Letterbox(tsn): # tivo pads 16:9 video for 4:3 display
59 return tsn != '' and tsn[:3] in ('649')
61 def get169Setting(tsn):
62 if not tsn:
63 return True
65 if config.has_section('_tivo_' + tsn):
66 if config.has_option('_tivo_' + tsn, 'aspect169'):
67 try:
68 return config.getboolean('_tivo_' + tsn, 'aspect169')
69 except ValueError:
70 pass
72 if get169Blacklist(tsn) or get169Letterbox(tsn):
73 return False
75 return True
77 def getShares(tsn=''):
78 shares = [(section, dict(config.items(section)))
79 for section in config.sections()
80 if not (
81 section.startswith('_tivo_')
82 or section in ('Server', 'loggers', 'handlers', 'formatters')
83 or section.startswith('logger_')
84 or section.startswith('handler_')
85 or section.startswith('formatter_')
89 if config.has_section('_tivo_' + tsn):
90 if config.has_option('_tivo_' + tsn, 'shares'):
91 # clean up leading and trailing spaces & make sure ref is valid
92 tsnshares = []
93 for x in config.get('_tivo_' + tsn, 'shares').split(','):
94 y = x.lstrip().rstrip()
95 if config.has_section(y):
96 tsnshares += [(y, dict(config.items(y)))]
97 if tsnshares:
98 shares = tsnshares
100 for name, data in shares:
101 if not data.get('auto_subshares', 'False').lower() == 'true':
102 continue
104 base_path = data['path']
105 try:
106 for item in os.listdir(base_path):
107 item_path = os.path.join(base_path, item)
108 if not os.path.isdir(item_path) or item.startswith('.'):
109 continue
111 new_name = name + '/' + item
112 new_data = dict(data)
113 new_data['path'] = item_path
115 shares.append((new_name, new_data))
116 except:
117 pass
119 return shares
121 def getDebug(ref):
122 if config.has_option('Server', 'debug'):
123 try:
124 return str2tuple(config.get('Server', 'debug')+',,')[ref]
125 except NoOptionError:
126 pass
127 return str2tuple('False,,')[ref]
129 def getHack83():
130 try:
131 debug = config.get('Server', 'hack83')
132 if debug.lower() == 'true':
133 return True
134 else:
135 return False
136 except NoOptionError:
137 return False
139 def getOptres(tsn = None):
140 if tsn and config.has_section('_tivo_' + tsn):
141 try:
142 return config.getboolean('_tivo_' + tsn, 'optres')
143 except NoOptionError, ValueError:
144 pass
145 try:
146 return config.getboolean('Server', 'optres')
147 except NoOptionError, ValueError:
148 return False
150 def getPixelAR(ref):
151 if config.has_option('Server', 'par'):
152 try:
153 return (True, config.getfloat('Server', 'par'))[ref]
154 except NoOptionError, ValueError:
155 pass
156 return (False, 1.0)[ref]
158 def get(section, key):
159 return config.get(section, key)
161 def getFFmpegTemplate(tsn):
162 if tsn and config.has_section('_tivo_' + tsn):
163 try:
164 return config.get('_tivo_' + tsn, 'ffmpeg_tmpl', raw=True)
165 except NoOptionError:
166 pass
167 try:
168 return config.get('Server', 'ffmpeg_tmpl', raw=True)
169 except NoOptionError: #default
170 return '%(video_codec)s %(video_fps)s %(video_br)s %(max_video_br)s \
171 %(buff_size)s %(aspect_ratio)s -comment pyTivo.py %(audio_br)s \
172 %(audio_fr)s %(audio_ch)s %(audio_codec)s %(ffmpeg_pram)s %(format)s'
174 def getFFmpegPrams(tsn):
175 if tsn and config.has_section('_tivo_' + tsn):
176 try:
177 return config.get('_tivo_' + tsn, 'ffmpeg_pram', raw=True)
178 except NoOptionError:
179 pass
180 try:
181 return config.get('Server', 'ffmpeg_pram', raw=True)
182 except NoOptionError:
183 return None
185 def isHDtivo(tsn): # tsn's of High Definition Tivo's
186 return tsn != '' and tsn[:3] in ['648', '652']
188 def getValidWidths():
189 return [1920, 1440, 1280, 720, 704, 544, 480, 352]
191 def getValidHeights():
192 return [1080, 720, 480] # Technically 240 is also supported
194 # Return the number in list that is nearest to x
195 # if two values are equidistant, return the larger
196 def nearest(x, list):
197 return reduce(lambda a, b: closest(x, a, b), list)
199 def closest(x, a, b):
200 if abs(x - a) < abs(x - b) or (abs(x - a) == abs(x - b) and a > b):
201 return a
202 else:
203 return b
205 def nearestTivoHeight(height):
206 return nearest(height, getValidHeights())
208 def nearestTivoWidth(width):
209 return nearest(width, getValidWidths())
211 def getTivoHeight(tsn):
212 if tsn and config.has_section('_tivo_' + tsn):
213 try:
214 height = config.getint('_tivo_' + tsn, 'height')
215 return nearestTivoHeight(height)
216 except NoOptionError:
217 pass
218 try:
219 height = config.getint('Server', 'height')
220 return nearestTivoHeight(height)
221 except NoOptionError: #defaults for S3/S2 TiVo
222 if isHDtivo(tsn):
223 return 720
224 else:
225 return 480
227 def getTivoWidth(tsn):
228 if tsn and config.has_section('_tivo_' + tsn):
229 try:
230 width = config.getint('_tivo_' + tsn, 'width')
231 return nearestTivoWidth(width)
232 except NoOptionError:
233 pass
234 try:
235 width = config.getint('Server', 'width')
236 return nearestTivoWidth(width)
237 except NoOptionError: #defaults for S3/S2 TiVo
238 if isHDtivo(tsn):
239 return 1280
240 else:
241 return 544
243 def getAudioBR(tsn = None):
244 #convert to non-zero multiple of 64 to ensure ffmpeg compatibility
245 #compare audio_br to max_audio_br and return lowest
246 if tsn and config.has_section('_tivo_' + tsn):
247 try:
248 audiobr = int(max(int(strtod(config.get('_tivo_' + tsn, 'audio_br'))/1000), 64)/64)*64
249 return str(min(audiobr, getMaxAudioBR(tsn))) + 'k'
250 except NoOptionError:
251 pass
252 try:
253 audiobr = int(max(int(strtod(config.get('Server', 'audio_br'))/1000), 64)/64)*64
254 return str(min(audiobr, getMaxAudioBR(tsn))) + 'k'
255 except NoOptionError:
256 return str(min(384, getMaxAudioBR(tsn))) + 'k'
258 def getVideoBR(tsn = None):
259 if tsn and config.has_section('_tivo_' + tsn):
260 try:
261 return config.get('_tivo_' + tsn, 'video_br')
262 except NoOptionError:
263 pass
264 try:
265 return config.get('Server', 'video_br')
266 except NoOptionError: #defaults for S3/S2 TiVo
267 if isHDtivo(tsn):
268 return '8192k'
269 else:
270 return '4096K'
272 def getMaxVideoBR():
273 try:
274 return str(int(strtod(config.get('Server', 'max_video_br'))/1000)) + 'k'
275 except NoOptionError: #default to 30000k
276 return '30000k'
278 def getVideoPCT():
279 try:
280 return config.getfloat('Server', 'video_pct')
281 except NoOptionError:
282 return 70
284 def getBuffSize():
285 try:
286 return str(int(strtod(config.get('Server', 'bufsize'))))
287 except NoOptionError: #default 1024k
288 return '1024k'
290 def getMaxAudioBR(tsn = None):
291 #convert to non-zero multiple of 64 for ffmpeg compatibility
292 if tsn and config.has_section('_tivo_' + tsn):
293 try:
294 return int(int(strtod(config.get('_tivo_' + tsn, 'max_audio_br'))/1000)/64)*64
295 except NoOptionError:
296 pass
297 try:
298 return int(int(strtod(config.get('Server', 'max_audio_br'))/1000)/64)*64
299 except NoOptionError:
300 return int(448) #default to 448
302 def getAudioCodec(tsn = None):
303 if tsn and config.has_section('_tivo_' + tsn):
304 try:
305 return config.get('_tivo_' + tsn, 'audio_codec')
306 except NoOptionError:
307 pass
308 try:
309 return config.get('Server', 'audio_codec')
310 except NoOptionError:
311 return None
313 def getAudioCH(tsn = None):
314 if tsn and config.has_section('_tivo_' + tsn):
315 try:
316 return config.get('_tivo_' + tsn, 'audio_ch')
317 except NoOptionError:
318 pass
319 try:
320 return config.get('Server', 'audio_ch')
321 except NoOptionError:
322 return None
324 def getAudioFR(tsn = None):
325 if tsn and config.has_section('_tivo_' + tsn):
326 try:
327 return config.get('_tivo_' + tsn, 'audio_fr')
328 except NoOptionError:
329 pass
330 try:
331 return config.get('Server', 'audio_fr')
332 except NoOptionError:
333 return None
335 def getCopyTS(tsn = None):
336 if tsn and config.has_section('_tivo_' + tsn):
337 if config.has_option('_tivo_' + tsn, 'copy_ts'):
338 try:
339 return config.get('_tivo_' + tsn, 'copy_ts')
340 except NoOptionError, ValueError:
341 pass
342 if config.has_option('Server', 'copy_ts'):
343 try:
344 return config.get('Server', 'copy_ts')
345 except NoOptionError, ValueError:
346 pass
347 return 'none'
349 def getVideoFPS(tsn = None):
350 if tsn and config.has_section('_tivo_' + tsn):
351 try:
352 return config.get('_tivo_' + tsn, 'video_fps')
353 except NoOptionError:
354 pass
355 try:
356 return config.get('Server', 'video_fps')
357 except NoOptionError:
358 return None
360 def getVideoCodec(tsn = None):
361 if tsn and config.has_section('_tivo_' + tsn):
362 try:
363 return config.get('_tivo_' + tsn, 'video_codec')
364 except NoOptionError:
365 pass
366 try:
367 return config.get('Server', 'video_codec')
368 except NoOptionError:
369 return None
371 def getFormat(tsn = None):
372 if tsn and config.has_section('_tivo_' + tsn):
373 try:
374 return config.get('_tivo_' + tsn, 'force_format')
375 except NoOptionError:
376 pass
377 try:
378 return config.get('Server', 'force_format')
379 except NoOptionError:
380 return None
382 def str2tuple(s):
383 items = s.split(',')
384 L = [x.strip() for x in items]
385 return tuple(L)
387 # Parse a bitrate using the SI/IEEE suffix values as if by ffmpeg
388 # For example, 2K==2000, 2Ki==2048, 2MB==16000000, 2MiB==16777216
389 # Algorithm: http://svn.mplayerhq.hu/ffmpeg/trunk/libavcodec/eval.c
390 def strtod(value):
391 prefixes = {'y': -24, 'z': -21, 'a': -18, 'f': -15, 'p': -12,
392 'n': -9, 'u': -6, 'm': -3, 'c': -2, 'd': -1,
393 'h': 2, 'k': 3, 'K': 3, 'M': 6, 'G': 9,
394 'T': 12, 'P': 15, 'E': 18, 'Z': 21, 'Y': 24}
395 p = re.compile(r'^(\d+)(?:([yzafpnumcdhkKMGTPEZY])(i)?)?([Bb])?$')
396 m = p.match(value)
397 if m is None:
398 raise SyntaxError('Invalid bit value syntax')
399 (coef, prefix, power, byte) = m.groups()
400 if prefix is None:
401 value = float(coef)
402 else:
403 exponent = float(prefixes[prefix])
404 if power == 'i':
405 # Use powers of 2
406 value = float(coef) * pow(2.0, exponent / 0.3)
407 else:
408 # Use powers of 10
409 value = float(coef) * pow(10.0, exponent)
410 if byte == 'B': # B == Byte, b == bit
411 value *= 8;
412 return value