#!/usr/bin/python # -*- coding: UTF-8 -*- '''Mobile Video Remote version 0.2 (c) 2010 Jan ONDREJ (SAL) This program is licensed under GPLv2+. ''' import BaseHTTPServer import sys, os, time, re, signal from urllib import unquote as unquote VIDEODIR = "~/ln/video" PORT = 8000 MENU = {} HEADER='''\ Content-type: text/html\r \r Mobile remote ''' FOOTER = '''\ ''' TEMPLATE = '''\ HTTP/1.0 200 OK\r %s
%%s
%%s
%s''' % (HEADER, FOOTER) FILELIST_TEMPLATE = '''\ HTTP/1.0 200 OK\r %s
%%s

%%s

%s ''' % (HEADER, FOOTER) HTTP_OK = "HTTP/1.0 200 OK\r\n\r\n" def safepath(path): if '/..' in path: raise ValueError, "Unsafe path %s" % path if re.compile(r'^[\d\w\[\]():./ +-]+$', re.UNICODE ).search(path.decode('utf-8')): return path raise ValueError, "Unsafe character %s" % path class link(object): name = '' url = '' hidden = False TD = '''%s''' def __init__(self, name, url=''): self.name = name if url=='': self.url = name else: self.url = url def parent(self, p): if not self.url.startswith('/'): self.url = p.url + '/' + self.url def tag(self, width=50): return self.TD % (self.url, width, self.name) class cmd(link): name = '' format = "%s" TD = '''%s''' def __init__(self, name, cmd='echo NO CMD'): self.name = name self.cmd = cmd def parent(self, p): global MENU self.url = p.url + '/' + self.name MENU[self.url] = self # register self.purl = p.url def reformat(self, **args): return self.format % self.cmd % args def render(self, **args): try: cmd = self.reformat(**args) print "CMD:", cmd pid = os.fork() if pid==0: os.system(cmd) #for i in range(255): # try: # os.close(i) # print "CLOSED:", i # except: # pass # ignore errors, just close all fds print "EXIT" os._exit(0) except KeyError, e: print "KeyError:", e print "Location:", self.purl return 'HTTP/1.0 302 Redirect\r\nLocation: %s\r\n\r\n\n' % self.purl class hidden(cmd): hidden = True class setvar(hidden): def __init__(self, name, key, value): self.name = name self.key = key self.value = value def render(self, **args): try: global MENU MENU[self.key] = self.value % args pprint(MENU) except KeyError, e: print "KeyError:", e print "Location:", self.purl return 'HTTP/1.0 302 Redirect\r\nLocation: %s\r\n\r\n' % self.purl class menu(link): name = '' url = '' rows = [] columns = 2 def __init__(self): for row in self.rows: row.parent(self) def parent(self, p): if not self.url.startswith('/'): self.url = p.url + '/' + self.url global MENU MENU[self.url] = self # register def render(self, **args): rows = [] data = list(self.rows) # copy of rows for i in range(len(self.rows)/self.columns): rows.append('') for n in range(self.columns): col = data.pop(0) if not col.hidden: rows.append(col.tag(width=100/self.columns)) rows.append('') return TEMPLATE % (self.name, '\n'.join(rows).replace('\n', '')) class file_selector(menu): def __init__(self, name, path, target): self.name = name self.path = path self.target = target def parent(self, p): self.url = p.url+'/FILE%s' % self.target global MENU MENU[self.url] = self def render(self, **args): global MENU dir = [] d = args.get('d', '') # try httpfs links if d.startswith('http://') or d.startswith('https://'): import httpfs path = safepath(d) for i in httpfs.listdir(path): if i.endswith('/'): i = i[:-1] dir.append('%s
' % (self.url, path, i, i)) else: dir.append('%s
' % (self.target, path, i, i)) else: path = os.path.join(os.path.expanduser(self.path), d) path = safepath(path) for i in os.listdir(path): pathi = os.path.join(path, i) if os.path.isdir(pathi): dir.append('%s
' % (self.url, os.path.join(d, i), i)) elif os.path.islink(pathi): dir.append('%s
' % (self.url, os.readlink(pathi), i)) else: dir.append('%s
' % (self.target, pathi, i)) return FILELIST_TEMPLATE % (d, '\n'.join(dir)) class mp_cmd(cmd): fifo_fn = os.path.expanduser("~/.mplayer/fifo") #format = "echo %s > ~/.mplayer/fifo" def alrm(self, signum, frame): pass # ignore it, only interrup write call def render(self, **args): print "MPlayer fifo command:", self.cmd signal.signal(signal.SIGALRM, self.alrm) signal.alarm(1) try: open(self.fifo_fn, 'a').write(self.cmd+'\n') signal.alarm(0) except IOError: print "No mplayer is running?" signal.signal(signal.SIGALRM, signal.SIG_DFL) return HTTP_OK class mp_add(cmd): def __init__(self, name, args): self.name = name self.args = args def render(self, **args): global MENU MENU['args'] += self.args print 'ARGS:', MENU['args'] return HTTP_OK class mp_reset(cmd): def render(self, **args): global MENU MENU['args'] = [] class mp_extra_menu(menu): name = 'SET' url = '/mplayer/SET' target = '/mplayer/SET' rows = [ mp_add('ASP 4:3', ('-aspect', '4:3')), mp_add('ASP 16:10', ('-aspect', '16:10')), mp_add('SEEK 30', ('-ss', '00:30:00')), mp_add('SEEK 60', ('-ss', '01:00:00')), mp_add('broken', ('-ni','-nocache','-mc','0','-forceidx','-nobps','-vf-del','0')), mp_add('LAVF', ('-demuxer', 'lavf')), mp_reset('RESET'), link('BACK', '/mplayer') ] class mp_seek(menu): name = 'SEEK' url = '/mplayer/seek' rows = [ mp_cmd('REW 5s', 'seek -5'), mp_cmd('FF 5s', 'seek +5'), mp_cmd('REW 1', 'seek -60'), mp_cmd('FF 1', 'seek +60'), mp_cmd('REW 10', 'seek -600'), mp_cmd('FF 10', 'seek +600'), mp_cmd('REW 30', 'seek -1800'), mp_cmd('FF 30', 'seek +1800'), link('BACK', '/mplayer'), link('BACK', '/mplayer') ] class mp_run(hidden): def reformat(self, **args): r = super(mp_run, self).reformat(**args) if 'subtitle' in MENU: r = r.replace('&', '-sub "%s" &' % MENU['subtitle']) if MENU['args']: r = r.replace('&', ' '.join(MENU['args'])+' &') return r class mplayer(menu): name = "MPlayer" url = '/mplayer' rows = [ file_selector('FILE', VIDEODIR, 'PLAY'), file_selector('SUB', VIDEODIR, 'SUBF'), mp_cmd('STOP', 'stop'), mp_cmd('PAUSE', 'pause'), mp_cmd('- VOL', 'volume -1'), mp_cmd('VOL +', 'volume +1'), mp_cmd('REW 10s', 'seek -10'), mp_cmd('FF 10s', 'seek +10'), mp_cmd('REW 1', 'seek -60'), mp_cmd('FF 1', 'seek +60'), mp_cmd('FULLSCR', 'vo_fullscreen'), mp_seek(), mp_cmd('OSD', 'OSD'), mp_cmd('SUBSEL', 'sub_select'), mp_cmd('- SUB', 'sub_step -1'), mp_cmd('SUB +', 'sub_step +1'), mp_extra_menu(), mp_run('PLAY', 'mplayer -quiet "%(f)s" &'), setvar('SUBF', 'subtitle', '%(f)s'), ] class tv_cmd(cmd): format = "tvtime-command %s" class tv_ch(cmd): format = "tvtime-command CHANNEL_%s" class tv_channel(menu): name = "CHANNEL" url = "/tvtime/channel" columns = 3 rows = [ tv_ch(str(x), str(x)) for x in range(1,10) ]+[ link('BACK', '/tvtime'), tv_ch('0', '0'), tv_cmd('PREV', 'CHANNEL_PREV'), ] class tvtime(menu): name = "TVtime" url = '/tvtime' rows = [ cmd('START', 'tvtime &'), tv_cmd('EXIT', 'quit'), tv_cmd('FULLSCR', 'TOGGLE_FULLSCREEN'), tv_cmd('MUTE', 'TOGGLE_MUTE'), tv_cmd('- VOL', 'MIXER_DOWN 5'), tv_cmd('VOL +', 'MIXER_UP 5'), tv_cmd('<< CH', 'CHANNEL_DEC'), tv_cmd('CH >>', 'CHANNEL_INC'), tv_channel(), tv_cmd('MENU', 'ENTER'), tv_cmd('ASPECT', 'TOGGLE_ASPECT'), tv_cmd('MATTE', 'TOGGLE_MATTE'), ] class top(menu): name = "MAIN MENU" url = '/' columns = 1 rows = [ tvtime(), mplayer(), ] MENU['/'] = top() MENU['args'] = [] from pprint import pprint pprint(MENU) class Handler(BaseHTTPServer.BaseHTTPRequestHandler): def do_GET(self): if '?' in self.path: path, args = self.path.split('?', 1) args = dict([x.split('=', 1) for x in args.split('&')]) for key in args: args[key] = unquote(args[key]) else: path = self.path args = {} path = unquote(path) if path not in MENU: print "Not found:", self.path return print "URL:", self.path self.wfile.write(MENU[path].render(**args)) httpd = BaseHTTPServer.HTTPServer(('', PORT), Handler) while True: httpd.handle_request()