init_logging() makes more sense in the config module.
[pyTivo/wgw.git] / config.py
blob82e044dbeef23e1306f98bb4b7d0b78f791d5f66
1 import ConfigParser, os, sys
2 import logging
3 import re
4 import random
5 import string
6 from ConfigParser import NoOptionError
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 not isHDtivo(tsn) and not get169Letterbox(tsn)
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 (section.startswith(('_tivo_', 'logger_', 'handler_',
81 'formatter_'))
82 or section in ('Server', 'loggers', 'handlers',
83 'formatters')
87 tsnsect = '_tivo_' + tsn
88 if config.has_section(tsnsect) and config.has_option(tsnsect, 'shares'):
89 # clean up leading and trailing spaces & make sure ref is valid
90 tsnshares = []
91 for x in config.get(tsnsect, 'shares').split(','):
92 y = x.strip()
93 if config.has_section(y):
94 tsnshares.append((y, dict(config.items(y))))
95 if tsnshares:
96 shares = tsnshares
98 return shares
100 def getDebug():
101 try:
102 return config.getboolean('Server', 'debug')
103 except NoOptionError, ValueError:
104 return False
106 def getOptres(tsn = None):
107 if tsn and config.has_section('_tivo_' + tsn):
108 try:
109 return config.getboolean('_tivo_' + tsn, 'optres')
110 except NoOptionError, ValueError:
111 pass
112 try:
113 return config.getboolean('Server', 'optres')
114 except NoOptionError, ValueError:
115 return False
117 def getPixelAR(ref):
118 if config.has_option('Server', 'par'):
119 try:
120 return (True, config.getfloat('Server', 'par'))[ref]
121 except NoOptionError, ValueError:
122 pass
123 return (False, 1.0)[ref]
125 def get(section, key):
126 return config.get(section, key)
128 def getFFmpegTemplate(tsn):
129 if tsn and config.has_section('_tivo_' + tsn):
130 try:
131 return config.get('_tivo_' + tsn, 'ffmpeg_tmpl', raw=True)
132 except NoOptionError:
133 pass
134 try:
135 return config.get('Server', 'ffmpeg_tmpl', raw=True)
136 except NoOptionError: #default
137 return '%(video_codec)s %(video_fps)s %(video_br)s %(max_video_br)s \
138 %(buff_size)s %(aspect_ratio)s -comment pyTivo.py %(audio_br)s \
139 %(audio_fr)s %(audio_ch)s %(audio_codec)s %(audio_lang)s \
140 %(ffmpeg_pram)s %(format)s'
142 def getFFmpegPrams(tsn):
143 if tsn and config.has_section('_tivo_' + tsn):
144 try:
145 return config.get('_tivo_' + tsn, 'ffmpeg_pram', raw=True)
146 except NoOptionError:
147 pass
148 try:
149 return config.get('Server', 'ffmpeg_pram', raw=True)
150 except NoOptionError:
151 return None
153 def isHDtivo(tsn): # tsn's of High Definition Tivo's
154 return tsn and tsn[:3] in ['648', '652', '658']
156 def getValidWidths():
157 return [1920, 1440, 1280, 720, 704, 544, 480, 352]
159 def getValidHeights():
160 return [1080, 720, 480] # Technically 240 is also supported
162 # Return the number in list that is nearest to x
163 # if two values are equidistant, return the larger
164 def nearest(x, list):
165 return reduce(lambda a, b: closest(x, a, b), list)
167 def closest(x, a, b):
168 if abs(x - a) < abs(x - b) or (abs(x - a) == abs(x - b) and a > b):
169 return a
170 else:
171 return b
173 def nearestTivoHeight(height):
174 return nearest(height, getValidHeights())
176 def nearestTivoWidth(width):
177 return nearest(width, getValidWidths())
179 def getTivoHeight(tsn):
180 if tsn and config.has_section('_tivo_' + tsn):
181 try:
182 height = config.getint('_tivo_' + tsn, 'height')
183 return nearestTivoHeight(height)
184 except NoOptionError:
185 pass
186 try:
187 height = config.getint('Server', 'height')
188 return nearestTivoHeight(height)
189 except NoOptionError: #defaults for S3/S2 TiVo
190 if isHDtivo(tsn):
191 return 720
192 else:
193 return 480
195 def getTivoWidth(tsn):
196 if tsn and config.has_section('_tivo_' + tsn):
197 try:
198 width = config.getint('_tivo_' + tsn, 'width')
199 return nearestTivoWidth(width)
200 except NoOptionError:
201 pass
202 try:
203 width = config.getint('Server', 'width')
204 return nearestTivoWidth(width)
205 except NoOptionError: #defaults for S3/S2 TiVo
206 if isHDtivo(tsn):
207 return 1280
208 else:
209 return 544
211 def _trunc64(i):
212 return max(int(strtod(i)) / 64000, 1) * 64
214 def getAudioBR(tsn = None):
215 #convert to non-zero multiple of 64 to ensure ffmpeg compatibility
216 #compare audio_br to max_audio_br and return lowest
217 if tsn and config.has_section('_tivo_' + tsn):
218 try:
219 audiobr = _trunc64(config.get('_tivo_' + tsn, 'audio_br'))
220 return str(min(audiobr, getMaxAudioBR(tsn))) + 'k'
221 except NoOptionError:
222 pass
223 try:
224 audiobr = _trunc64(config.get('Server', 'audio_br'))
225 return str(min(audiobr, getMaxAudioBR(tsn))) + 'k'
226 except NoOptionError:
227 return str(min(384, getMaxAudioBR(tsn))) + 'k'
229 def _k(i):
230 return str(int(strtod(i)) / 1000) + 'k'
232 def getVideoBR(tsn = None):
233 if tsn and config.has_section('_tivo_' + tsn):
234 try:
235 return _k(config.get('_tivo_' + tsn, 'video_br'))
236 except NoOptionError:
237 pass
238 try:
239 return _k(config.get('Server', 'video_br'))
240 except NoOptionError: #defaults for S3/S2 TiVo
241 if isHDtivo(tsn):
242 return '8192k'
243 else:
244 return '4096K'
246 def getMaxVideoBR():
247 try:
248 return _k(config.get('Server', 'max_video_br'))
249 except NoOptionError: #default to 30000k
250 return '30000k'
252 def getVideoPCT():
253 try:
254 return config.getfloat('Server', 'video_pct')
255 except NoOptionError:
256 return 70
258 def getBuffSize(tsn = None):
259 if tsn and config.has_section('_tivo_' + tsn):
260 if config.has_option('_tivo_' + tsn, 'bufsize'):
261 try:
262 return _k(config.get('_tivo_' + tsn, 'bufsize'))
263 except NoOptionError:
264 pass
265 if config.has_option('Server', 'bufsize'):
266 try:
267 return _k(config.get('Server', 'bufsize'))
268 except NoOptionError:
269 pass
270 if isHDtivo(tsn):
271 return '4096k'
272 else:
273 return '1024k'
275 def getMaxAudioBR(tsn = None):
276 #convert to non-zero multiple of 64 for ffmpeg compatibility
277 if tsn and config.has_section('_tivo_' + tsn):
278 try:
279 return _trunc64(config.get('_tivo_' + tsn, 'max_audio_br'))
280 except NoOptionError:
281 pass
282 try:
283 return _trunc64(config.get('Server', 'max_audio_br'))
284 except NoOptionError:
285 return int(448) #default to 448
287 def get_tsn(name, tsn=None):
288 if tsn and config.has_section('_tivo_' + tsn):
289 try:
290 return config.get('_tivo_' + tsn, name)
291 except NoOptionError:
292 pass
293 try:
294 return config.get('Server', name)
295 except NoOptionError:
296 return None
298 def getAudioCodec(tsn=None):
299 return get_tsn('audio_codec', tsn)
301 def getAudioCH(tsn=None):
302 return get_tsn('audio_ch', tsn)
304 def getAudioFR(tsn=None):
305 return get_tsn('audio_fr', tsn)
307 def getAudioLang(tsn=None):
308 return get_tsn('audio_lang', tsn)
310 def getCopyTS(tsn = None):
311 if tsn and config.has_section('_tivo_' + tsn):
312 if config.has_option('_tivo_' + tsn, 'copy_ts'):
313 try:
314 return config.get('_tivo_' + tsn, 'copy_ts')
315 except NoOptionError, ValueError:
316 pass
317 if config.has_option('Server', 'copy_ts'):
318 try:
319 return config.get('Server', 'copy_ts')
320 except NoOptionError, ValueError:
321 pass
322 return 'none'
324 def getVideoFPS(tsn=None):
325 return get_tsn('video_fps', tsn)
327 def getVideoCodec(tsn=None):
328 return get_tsn('video_codec', tsn)
330 def getFormat(tsn = None):
331 return get_tsn('format', tsn)
333 # Parse a bitrate using the SI/IEEE suffix values as if by ffmpeg
334 # For example, 2K==2000, 2Ki==2048, 2MB==16000000, 2MiB==16777216
335 # Algorithm: http://svn.mplayerhq.hu/ffmpeg/trunk/libavcodec/eval.c
336 def strtod(value):
337 prefixes = {'y': -24, 'z': -21, 'a': -18, 'f': -15, 'p': -12,
338 'n': -9, 'u': -6, 'm': -3, 'c': -2, 'd': -1,
339 'h': 2, 'k': 3, 'K': 3, 'M': 6, 'G': 9,
340 'T': 12, 'P': 15, 'E': 18, 'Z': 21, 'Y': 24}
341 p = re.compile(r'^(\d+)(?:([yzafpnumcdhkKMGTPEZY])(i)?)?([Bb])?$')
342 m = p.match(value)
343 if m is None:
344 raise SyntaxError('Invalid bit value syntax')
345 (coef, prefix, power, byte) = m.groups()
346 if prefix is None:
347 value = float(coef)
348 else:
349 exponent = float(prefixes[prefix])
350 if power == 'i':
351 # Use powers of 2
352 value = float(coef) * pow(2.0, exponent / 0.3)
353 else:
354 # Use powers of 10
355 value = float(coef) * pow(10.0, exponent)
356 if byte == 'B': # B == Byte, b == bit
357 value *= 8;
358 return value
360 def init_logging():
361 if (config.has_section('loggers') and
362 config.has_section('handlers') and
363 config.has_section('formatters')):
365 logging.config.fileConfig(config_files)
367 elif getDebug():
368 logging.basicConfig(level=logging.DEBUG)
369 else:
370 logging.basicConfig(level=logging.INFO)