Mythremctl.py

From MythTV Official Wiki
Jump to: navigation, search

Important.png Note: The correct title of this article is mythremctl.py. It appears incorrectly here due to technical restrictions.


Author Raymond Wagner
Description A simple curses-based interface for manipulating mythfrontend using the control socket.
Supports Version24.png  Version241.png Version25.png  


Python-based remote control using the Frontend control socket to send keys and commands to mythfrontend.


PythonIcon.png mythremctl.py

#!/usr/bin/env python
from MythTV import MythDB, MythError, MythLog, Frontend

from datetime import datetime, timedelta
from curses   import wrapper, ascii
from time     import sleep
import sys, socket, curses, re, os

#note for ticket, on-screen-keyboard remotely does not have focus

MythLog._setlevel('none')
frontend = None

rplaytype = re.compile('Playback ([a-zA-Z]+)')
rrecorded = re.compile('Playback ([a-zA-Z]+) ([\d:]+) of ([\d:]+) ([-0-9\.]+x) (\d*) ([0-9-T:]+)')
rlivetv   = rrecorded
rvideo    = re.compile('Playback [a-zA-Z]+ ([\d:]+) ([-0-9\.]+x) .*/(.*) \d+ [\.\d]+')
rrname    = re.compile('\d+ [0-9-T:]+ (.*)')
rlname    = re.compile('\d+ [0-9-T: ]+ (.*)')

def align(side, window, y, string, flush=0):
    w = window.getmaxyx()[1]-1
    if len(string) > w:
        string = string[:w]
    if side == 0:
        x = 1
    elif side == 1:
        x = (w-len(string))/2+1
    elif side == 2:
        x = w-len(string)
    window.addstr(y,x,string)

def query_time(w, _dat=[0]):
    if _dat[0] == 0:
        ltime   = datetime.now()
        fetime  = frontend.getTime()
        _dat[0] = fetime - ltime
    time = datetime.now()+_dat[0]
    w.erase()
    w.border(curses.ACS_VLINE,    curses.ACS_VLINE,
             curses.ACS_HLINE,    curses.ACS_HLINE,
             curses.ACS_ULCORNER, curses.ACS_TTEE,
             curses.ACS_LTEE,     curses.ACS_RTEE)
    align(1,w,1,time.strftime('%H:%M:%S'))
    w.noutrefresh()

def query_load(w, _dat=[0]):
    if _dat[0] == 0:
        _dat[0] = datetime.now()
    now = datetime.now()
    if _dat[0] > now:
        return
    _dat[0] = now+timedelta(seconds=5)

    loads = frontend.getLoad()
    w.erase()
    w.border(curses.ACS_VLINE,    curses.ACS_VLINE,
             curses.ACS_HLINE,    curses.ACS_HLINE,
             curses.ACS_LTEE,     curses.ACS_RTEE,
             curses.ACS_LTEE,     curses.ACS_RTEE)
    align(0,w,2,'loads')
    align(2,w,1,' 1: {0:0.2f}'.format(loads[0]))
    align(2,w,2,' 5: {0:0.2f}'.format(loads[1]))
    align(2,w,3,'15: {0:0.2f}'.format(loads[2]))
    w.noutrefresh()

def query_loc(w, _dat=[0]):
    if _dat[0] == 0:
        _dat[0] = datetime.now()
    now = datetime.now()
    if _dat[0] > now:
        return
    _dat[0] = now+timedelta(seconds=5)

    loc = frontend.sendQuery('location')
    pb = rplaytype.match(loc)
    w.erase()
    w.border(curses.ACS_VLINE,    curses.ACS_VLINE,
             curses.ACS_HLINE,    curses.ACS_HLINE,
             curses.ACS_TTEE,     curses.ACS_URCORNER,
             curses.ACS_BTEE,     curses.ACS_LRCORNER)
    if pb:
        if pb.group(1) == 'Video':
            pb = rvideo.match(loc)
            align(0,w,1,'  Playback: %s' % pb.group(3))
            align(0,w,2,'     %s @ %s' % (pb.group(1),pb.group(2)))
        else:
            pb = rrecorded.match(loc)
            if pb is None:
                return
            if pb.group(1) == 'Recorded':
                show = frontend.sendQuery('recording %s %s' \
                                            % (pb.group(5),pb.group(6)))
                name = rrname.match(show).group(1)
                align(0,w,1,'  Playback: %s' % name)
                
            elif pb.group(1) == 'LiveTV':
                show = frontend.sendQuery('liveTV %s' % pb.group(5))
                name = rlname.match(show).group(1)
                align(0,w,1,'  LiveTV: %s - %s' % (pb.group(5),name))
            align(0,w,2,'      %s of %s @ %s' \
                                % (pb.group(2),pb.group(3),pb.group(4)))
    else:
        align(0,w,1,'  '+loc)
    w.noutrefresh()

def query_mem(w, _dat=[0]):
    if _dat[0] == 0:
        _dat[0] = datetime.now()
    now = datetime.now()
    if _dat[0] > now:
        return
    _dat[0] = now+timedelta(seconds=15)

    mem = frontend.getMemory()
    w.erase()
    w.border(curses.ACS_VLINE,    curses.ACS_VLINE,
             curses.ACS_HLINE,    curses.ACS_HLINE,
             curses.ACS_LTEE,     curses.ACS_RTEE,
             curses.ACS_LLCORNER, curses.ACS_BTEE)
    align(0,w,1,'phy:')
    align(0,w,2,'swp:')
    align(2,w,1,"%sM/%sM" % (mem['freemem'],mem['totalmem']))
    align(2,w,2,"%sM/%sM" % (mem['freeswap'],mem['totalswap']))
    w.noutrefresh()

def main(w):
    curses.halfdelay(10)
    frontend.connect()
    y,x = w.getmaxyx()

    mem = w.derwin(4,20,9,0)
    query_mem(mem)

    load = w.derwin(5,20,5,0)
    query_load(load)

    conn = w.derwin(4,20,2,0)
    align(2,conn,1,frontend.host)
    align(2,conn,2,"%s:%d" % frontend.socket.getpeername())
    conn.border(curses.ACS_VLINE,    curses.ACS_VLINE,
                curses.ACS_HLINE,    curses.ACS_HLINE,
                curses.ACS_LTEE,     curses.ACS_RTEE,
                curses.ACS_LTEE,     curses.ACS_RTEE)

    time = w.derwin(3,20,0,0)
    query_time(time)

    loc = w.derwin(13,x-20,0,19)
    loc.timeout(1)
    while True:
        a = None
        s = None
        try:
            query_time(time)
            query_load(load)
            query_loc(loc)
            query_mem(mem)
            align(1,w,0,' MythFrontend Remote Socket Interface ')
            curses.doupdate()

            a = w.getch()
            curses.flushinp()
            frontend.key[a]
        except KeyboardInterrupt:
            break
        except EOFError:
            break
        except MythError:
            print "Remote side closed connection..."
            break
        except ValueError:
            # assume an frontend stalled, opening a file
            pass
        except:
            raise

if __name__ == '__main__':
    if not os.environ.has_key('ESCDELAY'):
        os.environ['ESCDELAY'] = '25'
    
    if len(sys.argv) == 2:
        try:
            db = MythDB()
            frontend = db.getFrontend(sys.argv[1])
            wrapper(main)
        except socket.timeout:
            print "Could not connect to "+sys.argv[1]
            pass
        except TypeError:
            print sys.argv[1]+" does not exist"
            pass
        except KeyboardInterrupt:
            sys.exit()
        except:
            raise
    else:
        print "Please choose from the following available frontends:"
        frontends = None
        while frontend is None:
            if frontends is None:
                frontends = list(Frontend.fromUPNP())
                if len(frontends) == 0:
                    print "No frontends detected"
                    sys.exit()
            for i,f in enumerate(frontends):
                print "%d. %s" % (i+1, f)
            try:
                i = int(raw_input('> '))-1
                frontend = frontends[i]
                wrapper(main)
            except KeyboardInterrupt:
                sys.exit()
            except EOFError:
                sys.exit()
            except IndexError:
                print "This input requires a value between 1 and %d" % len(frontends)
            except:
                print "This input will only accept a number. Use Ctrl-C to exit"