From 11834b993463da1c24695938a416bf0667c96517 Mon Sep 17 00:00:00 2001 From: Stefan Naumann Date: Sun, 30 Apr 2017 12:48:53 +0200 Subject: [PATCH] a bit cleanup and live-mode --- .gitignore | 1 + cfg/config.json | 2 +- setup.sh | 6 + src/piplayer.py | 341 ++++++++++++++++-------------------------------- src/ui.py | 221 +++++++++++++++++++++++++++++++ 5 files changed, 339 insertions(+), 232 deletions(-) create mode 100644 .gitignore create mode 100644 src/ui.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..0d20b64 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*.pyc diff --git a/cfg/config.json b/cfg/config.json index 2aaef08..66a39fe 100644 --- a/cfg/config.json +++ b/cfg/config.json @@ -1 +1 @@ -{"WindowX": 3, "WindowY": 47, "WindowWidth": 641, "WindowHeight": 449} \ No newline at end of file +{"WindowX": 1, "WindowY": 460, "WindowWidth": 641, "WindowHeight": 449} \ No newline at end of file diff --git a/setup.sh b/setup.sh index 9b3f988..97c2693 100644 --- a/setup.sh +++ b/setup.sh @@ -64,7 +64,13 @@ prepare) rm -rf $pyFLTKFolder rm $pyFLTKFile fi + bash setup.sh livestreamer ;; +# install only the livestreamer python-skript +livestreamer) + sudo pip install livestreamer + ;; +# create documentation from the python doxygen comments doc) mkdir doc doxygen Doxyfile diff --git a/src/piplayer.py b/src/piplayer.py index 0b01e8c..08401ce 100644 --- a/src/piplayer.py +++ b/src/piplayer.py @@ -7,135 +7,80 @@ import time import genericThread import config import fileio - -playState = True; -durationState = 0; -positionState = 0; +import ui omx = genericThread.genericThread ( 1, "OMX", 1); state = genericThread.genericThread ( 2, "State", 2); +stateRun = True; +live=False; def startOMX ( argv ): global stateRun argv=argv[0]; - argv.insert ( 0, "omxplayer" ) - argv.append ( "--win"); - (x,y,x2,y2) = sizeCalc(); - argv.append( str(x)+","+str(y)+","+str(x2)+","+str(y2) ) - argv.append ( "-o"); - argv.append("local"); - argv.append("--aspect-mode"); - argv.append("letterbox"); - subprocess.run ( argv ); - stateRun = False; - -def callDBus ( param ): - try: - param.insert ( 0, config.dbusconfig ); - param.insert ( 0, "bash" ); - out = subprocess.check_output ( param ) - return out; - except subprocess.CalledProcessError: - return b""; - -def playCb(ptr): - callDBus ( ["pause"] ); - getState( None ); - global playState - global playBtn - if ( playState == True ): - playBtn.label ("@||"); - else : - playBtn.label("@>"); - -def resizeCb (): - (x,y,x2,y2) = sizeCalc() - status = callDBus ( ["status"] ); - times = 0; - while (status=="" and times < 5) : - status = callDBus ( ["status"] ); - time.sleep ( 0.5 ); - times = times + 1; + stream = argv.pop(); - if ( times < 5 ): - callDBus (["setvideopos", str(x), str(y), str(x2), str(y2)]); + i = 0; + output = "local" + while ( i < len(argv) ): + if ( argv[i] =="-o"): + if ( argv[i+1] == "local" or argv[i+1] == "hdmi" ): + output=argv[i+1]; + else: + output="local" + i = i + 1 + + (x,y,x2,y2) = ui.sizeCalc(); + argv = [ "omxplayer", "--win", str(x)+","+str(y)+","+str(x2)+","+str(y2), "-o", output, "--aspect-mode", "letterbox", stream ]; + subprocess.run ( argv ); + stateRun = False; -def sizeCalc ( ): - global window - x = window.x(); - y = window.y(); - w = window.w(); - h = window.h(); +def startOMXLive ( argv ): + global stateRun + quality=argv[1]; + argv=argv[0]; - #x=x+2; - #y=y+2; - x2 = x + w; - y2 = y + h - 75; + stream = argv.pop(); - return (x,y,x2,y2); - -def DecodeState ( param ): - global playState; - global durationState; - global positionState; - i=0 - while (i < len(param)) : - m = re.search ( "Duration: ([0-9]+)", param[i] ); - if ( m is None ): - m = re.search ( "Position: ([0-9]+)", param[i] ); - if ( m is None ): - m = re.search ( "Paused: (true|false)", param[i] ); - if ( m is None ) : - pass; - else : - playState = ( m.group(1) == "false"); + i = 0; + output = "local" + while ( i < len(argv) ): + if ( argv[i] =="-o"): + if ( argv[i+1] == "local" or argv[i+1] == "hdmi" ): + output=argv[i+1]; else: - positionState = int ( m.group(1) ); - else: - durationState = int ( m.group(1) ); - i=i+1 - -def timeOut ( time ) : - time = int(time / 1000000); # mikrosec -> sec - hours = int(time / 3600); - minutes = int((time / 60)) - hours*60; - sec = time - minutes*60 - hours*360; - - return ( str(hours).zfill(2) + ":" + str(minutes).zfill(2) + ":" +str(sec).zfill(2) ); - -def setPosition ( pos ): - callDBus ( ["setposition", str(pos)] ); - getState ( None ); - -def getState ( ptr ) : - status = callDBus ( ["status"] ); - status = status.decode("utf-8").split("\n") - DecodeState ( status ); + output="local" + i = i + 1 + + (x,y,x2,y2) = ui.sizeCalc(); + argv = [ "livestreamer", stream, quality, "-np", + "omxplayer --win "+str(x)+","+str(y)+","+str(x2)+","+str(y2)+" -o "+output+" --aspect-mode letterbox --live" ]; + print (argv); + subprocess.run ( argv ); + stateRun = False; - statusLabel.value ( timeOut ( positionState ) + " / " + timeOut ( durationState ) ); - if ( durationState == 0): - statusProgress.value(0); - else : - statusProgress.value( positionState / durationState ); - -stateRun = True; def thread_state ( args ) : - global stateRun; - while ( stateRun == True ): - getState( None ); - time.sleep (1); + global stateRun + global live + beginTime = int(time.time()); + if ( live == False ): + while ( stateRun == True ): + ui.getState( None ); + time.sleep (1); + else : + while ( stateRun == True ): + ui.statusLabel.value ( "Uptime: " + ui.timeOut ( (int(time.time()) - beginTime)*1000000 )) + time.sleep(1); + -def killAll ( ptr ): - global stateRun; - global cfg - global window +def killAll ( cfg ): + global stateRun stateRun = False; - callDBus ( ["stop"] ); - cfg["WindowX"] = window.x(); - cfg["WindowY"] = window.y(); - cfg["WindowWidth"] = window.w(); - cfg["WindowHeight"] = window.h(); + ui.callDBus ( ["stop"] ); + cfg["WindowX"] = ui.window.x(); + cfg["WindowY"] = ui.window.y(); + cfg["WindowWidth"] = ui.window.w(); + cfg["WindowHeight"] = ui.window.h(); fileio.saveConfig ( config.configfile, cfg ); @@ -145,125 +90,59 @@ def killAll ( ptr ): def FLTK_run () : global stateRun - global window - global statusProgress - x = window.x(); - y = window.y(); - w = window.w(); - h = window.h(); - while (stateRun == True and window.shown()): + x = ui.window.x(); + y = ui.window.y(); + w = ui.window.w(); + h = ui.window.h(); + while (stateRun == True and ui.window.shown()): Fl.check(); - if ( x != window.x() or y != window.y() or - w != window.w() or h != window.h() ): - resizeWindow (); - if (Fl.event_button1() and Fl.event_inside ( statusProgress.x(), statusProgress.y(), statusProgress.x() + statusProgress.w(), statusProgress.y() + statusProgress.h() ) ): - seekCb (); + if ( x != ui.window.x() or y != ui.window.y() or + w != ui.window.w() or h != ui.window.h() ): + ui.resizeWindow (); + if (Fl.event_button1() and + Fl.event_inside ( ui.statusProgress.x(), + ui.statusProgress.y(), + ui.statusProgress.x() + ui.statusProgress.w(), + ui.statusProgress.y() + ui.statusProgress.h() ) ): + ui.seekCb (); time.sleep(0.1); # I don't want to burn that much cpu-time - -def resizeWindow ( ): - global window; - global playBtn - global stateBtn - global killBtn - global statusLabel - global statusProgress - global volUp - global volDown - - h = window.h() - w = window.w() - - statusLabel.resize ( w-230, h-50, statusLabel.w(), statusLabel.h() ); - playBtn.position ( 20, h-60 ); - stateBtn.position ( 60, h-60); - killBtn.position ( 100, h-60 ); - - volUp.position ( w - 40, h-50 ); - volDown.position ( w - 60, h-50 ); - statusProgress.position ( 160, h-50 ); - statusProgress.size ( w - 410, 20 ); - - playBtn.redraw(); - stateBtn.redraw(); - killBtn.redraw(); - #statusLabel.redraw(); - statusProgress.redraw(); - window.redraw(); - - resizeCb (); - -def seekCb (): - global statusProgress; - x = Fl.event_x(); - x = x - statusProgress.x(); - dim = x / statusProgress.w(); - positionState = durationState * dim; - setPosition ( positionState ); - -def volUpCb ( ptr ): - callDBus ( ["volumeup"] ); - -def volDownCb ( ptr ): - callDBus ( ["volumedown"] ); - -if ( len ( sys.argv ) <= 1 ): - print ("Usage: " + sys.argv[0] + "[omxplayer-arguments] FILENAME"); - sys.exit(0); - -omx.setFn ( startOMX, [sys.argv[1:]] ); -state.setFn ( thread_state, [] ); - -cfg = fileio.getConfig ( config.configfile ); - -window = Fl_Window( cfg.get("WindowX", 100) , cfg.get("WindowY", 100), cfg.get("WindowWidth", 640), cfg.get("WindowHeight", 480)) -window.label(sys.argv[0]) -window.size_range ( 320, 240, 0, 0); - -# start omx-player -omx.start(); - -playBtn = Fl_Button(20, 420, 40, 40) -playBtn.labeltype(FL_SYMBOL_LABEL) -playBtn.label("@||") -playBtn.callback(playCb) -playBtn.box ( FL_THIN_UP_BOX ); - -stateBtn = Fl_Button ( 60, 420, 40, 40 ); -stateBtn.label("?") -stateBtn.callback(getState) -stateBtn.box ( FL_THIN_UP_BOX ); - -killBtn = Fl_Button ( 100, 420, 40, 40 ); -killBtn.label ("x"); -killBtn.callback ( killAll ); -killBtn.box ( FL_THIN_UP_BOX ); - -statusLabel = Fl_Output ( 410, 430, 150, 20 ); -statusLabel.label (""); -statusLabel.box ( FL_FLAT_BOX ); - -statusProgress = Fl_Progress ( 160, 430, 230, 20 ); -statusProgress.box( FL_FLAT_BOX ); -statusProgress.minimum(0); -statusProgress.maximum(1); -statusProgress.color ( 0xffffffff ); -statusProgress.selection_color( 0x272828ff ); - -volUp = Fl_Button ( 600, 430, 20,20 ); -volUp.label ( "+" ); -volUp.box ( FL_THIN_UP_BOX ); -volUp.callback ( volUpCb ); - -volDown = Fl_Button ( 580, 430, 20,20 ); -volDown.label ( "-" ); -volDown.box ( FL_THIN_UP_BOX ); -volDown.callback ( volDownCb ); - -resizeWindow(); - -window.end() -window.show(len(sys.argv), sys.argv) -state.start(); -FLTK_run (); -killAll( None ) +def main () : + global live + + if ( len ( sys.argv ) <= 1 ): + print ("Usage: " + sys.argv[0] + " [parameters] FILENAME\n\ +Options:\n\ + * --live - indicate that a livestream is played (i.e. Twitch), will be played using livestreamer"); + sys.exit(0); + + i = 0 + quality = "best" + live = False; + omx.setFn ( startOMX, [sys.argv[1:]] ); + while ( i < len (sys.argv) ): + if (sys.argv[i] == "--live"): + live = True + if (sys.argv[i] == "--quality" or sys.argv[i] =="-q"): + quality=sys.argv[1+i]; + i = i + 1; + + print (quality) + if ( live == True ): + omx.setFn ( startOMXLive, [sys.argv[1:], quality] ); + + state.setFn ( thread_state, [] ); + + cfg = fileio.getConfig ( config.configfile ); + + ui.init( cfg ); + + # start omx-player + omx.start(); + state.start(); + + FLTK_run (); + killAll( cfg ) + +if __name__ == "__main__" : + main() diff --git a/src/ui.py b/src/ui.py new file mode 100644 index 0000000..5de4f7b --- /dev/null +++ b/src/ui.py @@ -0,0 +1,221 @@ +import subprocess +import sys +import config +## TODO find a better way to get killAll-fn +import piplayer +import re +from fltk import * + +playState = True; +durationState = 0; +positionState = 0; + +dbusaddr = "org.mpris.MediaPlayer2.omxplayer" + +def setDBusAddr ( addr ) : + global dbusaddr + dbusaddr = addr + +def callDBus ( param ): + try: + param.insert ( 0, config.dbusconfig ); + param.insert ( 0, "bash" ); + out = subprocess.check_output ( param ) + return out; + except subprocess.CalledProcessError: + return b""; + +def playCb(ptr): + callDBus ( ["pause"] ); + getState( None ); + global playState + global playBtn + if ( playState == True ): + playBtn.label ("@||"); + else : + playBtn.label("@>"); + +def sizeCalc ( ): + global window + x = window.x(); + y = window.y(); + w = window.w(); + h = window.h(); + + #x=x+2; + #y=y+2; + x2 = x + w; + y2 = y + h - 75; + + return (x,y,x2,y2); + + +def DecodeState ( param ): + global playState; + global durationState; + global positionState; + i=0 + while (i < len(param)) : + m = re.search ( "Duration: ([0-9]+)", param[i] ); + if ( m is None ): + m = re.search ( "Position: ([0-9]+)", param[i] ); + if ( m is None ): + m = re.search ( "Paused: (true|false)", param[i] ); + if ( m is None ) : + pass; + else : + playState = ( m.group(1) == "false"); + else: + positionState = int ( m.group(1) ); + else: + durationState = int ( m.group(1) ); + i=i+1 + +def timeOut ( time ) : + time = int(time / 1000000); # mikrosec -> sec + hours = int(time / 3600); + minutes = int((time / 60)) - hours*60; + sec = time - minutes*60 - hours*360; + + return ( str(hours).zfill(2) + ":" + str(minutes).zfill(2) + ":" +str(sec).zfill(2) ); + +def setPosition ( pos ): + callDBus ( ["setposition", str(pos)] ); + getState ( None ); + +def getState ( ptr ) : + status = callDBus ( ["status"] ); + status = status.decode("utf-8").split("\n") + DecodeState ( status ); + + statusLabel.value ( timeOut ( positionState ) + " / " + timeOut ( durationState ) ); + if ( durationState == 0): + statusProgress.value(0); + else : + statusProgress.value( positionState / durationState ); + +def resizeWindow ( ): + global window; + global playBtn + global stateBtn + global killBtn + global statusLabel + global statusProgress + global volUp + global volDown + + h = window.h() + w = window.w() + + statusLabel.resize ( w-230, h-50, statusLabel.w(), statusLabel.h() ); + playBtn.position ( 20, h-60 ); + stateBtn.position ( 60, h-60); + killBtn.position ( 100, h-60 ); + + volUp.position ( w - 40, h-50 ); + volDown.position ( w - 60, h-50 ); + statusProgress.position ( 160, h-50 ); + statusProgress.size ( w - 410, 20 ); + + playBtn.redraw(); + stateBtn.redraw(); + killBtn.redraw(); + #statusLabel.redraw(); + statusProgress.redraw(); + window.redraw(); + + resizeOMX (); + +def resizeOMX (): + (x,y,x2,y2) = sizeCalc() + + status = callDBus ( ["status"] ); + times = 0; + while (status=="" and times < 5) : + status = callDBus ( ["status"] ); + time.sleep ( 0.5 ); + times = times + 1; + + if ( times < 5 ): + callDBus (["setvideopos", str(x), str(y), str(x2), str(y2)]); + +def seekCb (): + global statusProgress; + x = Fl.event_x(); + x = x - statusProgress.x(); + dim = x / statusProgress.w(); + positionState = durationState * dim; + setPosition ( positionState ); + +def volUpCb ( ptr ): + callDBus ( ["volumeup"] ); + +def volDownCb ( ptr ): + callDBus ( ["volumedown"] ); + + +window="" +playBtn="" +stateBtn="" +killBtn="" +statusLabel="" +statusProgress="" +volUp="" +volDown="" +def init ( cfg ): + global window + global playBtn + global stateBtn + global killBtn + global statusLabel + global statusProgress + global volUp + global volDown + global stateBtn + window = Fl_Window( cfg.get("WindowX", 100) , cfg.get("WindowY", 100), cfg.get("WindowWidth", 640), cfg.get("WindowHeight", 480)) + window.label(sys.argv[0]) + window.size_range ( 320, 240, 0, 0); + + playBtn = Fl_Button(20, 420, 40, 40) + playBtn.labeltype(FL_SYMBOL_LABEL) + playBtn.label("@||") + playBtn.callback(playCb) + playBtn.box ( FL_THIN_UP_BOX ); + + stateBtn = Fl_Button ( 60, 420, 40, 40 ); + stateBtn.label("?") + stateBtn.callback(getState) + stateBtn.box ( FL_THIN_UP_BOX ); + + killBtn = Fl_Button ( 100, 420, 40, 40 ); + killBtn.label ("x"); + ## TODO + killBtn.callback ( piplayer.killAll ); + killBtn.box ( FL_THIN_UP_BOX ); + + statusLabel = Fl_Output ( 410, 430, 150, 20 ); + statusLabel.label (""); + statusLabel.box ( FL_FLAT_BOX ); + + statusProgress = Fl_Progress ( 160, 430, 230, 20 ); + statusProgress.box( FL_FLAT_BOX ); + statusProgress.minimum(0); + statusProgress.maximum(1); + statusProgress.color ( 0xffffffff ); + statusProgress.selection_color( 0x272828ff ); + + volUp = Fl_Button ( 600, 430, 20,20 ); + volUp.label ( "+" ); + volUp.box ( FL_THIN_UP_BOX ); + volUp.callback ( volUpCb ); + + volDown = Fl_Button ( 580, 430, 20,20 ); + volDown.label ( "-" ); + volDown.box ( FL_THIN_UP_BOX ); + volDown.callback ( volDownCb ); + + resizeWindow(); + + window.end() + window.show(1, [sys.argv[0]]) +