[enh] adds a command line runner for clitools
This commit is contained in:
parent
adca2e4502
commit
f9e0db4499
1
clitools/.gitignore
vendored
Normal file
1
clitools/.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
playlists
|
11
clitools/_run.sh
Executable file
11
clitools/_run.sh
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
killexit(){
|
||||||
|
pkill -9 -s $$
|
||||||
|
}
|
||||||
|
|
||||||
|
trap killexit SIGTERM SIGINT SIGKILL SIGSTOP
|
||||||
|
|
||||||
|
bash -c "$@"
|
||||||
|
|
||||||
|
killbill(){ pkill -9 -s $(awk '{print $6}' /proc/$1/stat) }
|
116
clitools/runner.py
Executable file
116
clitools/runner.py
Executable file
@ -0,0 +1,116 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import tty,termios
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import runner_lib as runner
|
||||||
|
|
||||||
|
|
||||||
|
bindings={
|
||||||
|
"Show playlist" : "l",
|
||||||
|
"Launch [0-x] cmd" : "0-x",
|
||||||
|
"Previous command" : "p",
|
||||||
|
"Next command" : "o",
|
||||||
|
"New command" : "a",
|
||||||
|
"Edit command" : "e",
|
||||||
|
"Delete command" : "d",
|
||||||
|
"Load playlist" : "L",
|
||||||
|
"Save playlist" : "S",
|
||||||
|
"Save as new" : "A",
|
||||||
|
"New playlist" : "N",
|
||||||
|
"Command help" : "H",
|
||||||
|
"Kill process Id" : "K",
|
||||||
|
"Edit Laser Id" : "i",
|
||||||
|
"Edit Laser Scene" : "s",
|
||||||
|
"Information" : "I",
|
||||||
|
"Help" : "h",
|
||||||
|
"Quit" : "q",
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
## Init user contact
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Main Loop
|
||||||
|
runner.action_info()
|
||||||
|
runner.action_help()
|
||||||
|
print("\n\nLoad a playlist? [Y/n]: ")
|
||||||
|
if "y" == runner.inkey() :
|
||||||
|
runner.action_loadPlaylist()
|
||||||
|
|
||||||
|
while True:
|
||||||
|
# Fuck zombies
|
||||||
|
runner._killBill()
|
||||||
|
runner._ok("> Next Action?")
|
||||||
|
k = runner.inkey()
|
||||||
|
|
||||||
|
if bindings["Next command"] == k:
|
||||||
|
runner.action_changeCommand( 1 )
|
||||||
|
runner.action_runCommand()
|
||||||
|
elif bindings["Previous command"] == k:
|
||||||
|
runner.action_changeCommand( -1 )
|
||||||
|
runner.action_runCommand()
|
||||||
|
elif re.match( r'^\d+$',k):
|
||||||
|
runner.action_match(k)
|
||||||
|
runner.action_runCommand()
|
||||||
|
elif bindings["New command"] == k:
|
||||||
|
runner.action_newCommand()
|
||||||
|
continue
|
||||||
|
elif bindings["Show playlist"] == k:
|
||||||
|
runner.action_listAll()
|
||||||
|
continue
|
||||||
|
elif bindings["Delete command"] == k:
|
||||||
|
runner.action_deleteCommand()
|
||||||
|
continue
|
||||||
|
elif bindings["Edit command"] == k:
|
||||||
|
runner.action_listAll()
|
||||||
|
runner.action_edit()
|
||||||
|
continue
|
||||||
|
elif bindings["Load playlist"] == k:
|
||||||
|
if runner.action_loadPlaylist():
|
||||||
|
runner.action_listAll()
|
||||||
|
continue
|
||||||
|
elif bindings["Save playlist"] == k:
|
||||||
|
runner.action_savePlaylist()
|
||||||
|
continue
|
||||||
|
elif bindings["Save as new"] == k:
|
||||||
|
runner.action_savePlaylist()
|
||||||
|
continue
|
||||||
|
elif bindings["New playlist"] == k:
|
||||||
|
runner.action_newPlaylist()
|
||||||
|
continue
|
||||||
|
elif bindings["Command help"] == k:
|
||||||
|
runner.action_commandHelp()
|
||||||
|
continue
|
||||||
|
elif bindings["Edit Laser Id"] == k:
|
||||||
|
runner.action_laserId()
|
||||||
|
continue
|
||||||
|
elif bindings["Edit Laser Scene"] == k:
|
||||||
|
runner.action_laserScene()
|
||||||
|
continue
|
||||||
|
elif bindings["Kill process Id"] == k:
|
||||||
|
runner.action_killPid()
|
||||||
|
continue
|
||||||
|
elif bindings["Help"] == k:
|
||||||
|
runner.action_help()
|
||||||
|
continue
|
||||||
|
elif bindings["Information"] == k:
|
||||||
|
runner.action_info()
|
||||||
|
continue
|
||||||
|
elif bindings["Quit"] == k:
|
||||||
|
runner.action_quit()
|
||||||
|
else:
|
||||||
|
runner._err("Unexpected key:{}".format(k))
|
||||||
|
continue
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
374
clitools/runner_lib.py
Normal file
374
clitools/runner_lib.py
Normal file
@ -0,0 +1,374 @@
|
|||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import tty,termios
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import redis
|
||||||
|
|
||||||
|
class bcolors:
|
||||||
|
HL = '\033[31m'
|
||||||
|
OKBLUE = '\033[94m'
|
||||||
|
OKGREEN = '\033[92m'
|
||||||
|
ENDC = '\033[0m'
|
||||||
|
BOLD = '\033[1m'
|
||||||
|
UNDERLINE = '\033[4m'
|
||||||
|
|
||||||
|
class _Getch:
|
||||||
|
def __call__(self):
|
||||||
|
fd = sys.stdin.fileno()
|
||||||
|
old_settings = termios.tcgetattr(fd)
|
||||||
|
try:
|
||||||
|
tty.setraw(sys.stdin.fileno())
|
||||||
|
ch = sys.stdin.read(1)
|
||||||
|
finally:
|
||||||
|
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
|
||||||
|
return ch
|
||||||
|
inkey = _Getch()
|
||||||
|
|
||||||
|
def intkey():
|
||||||
|
try:
|
||||||
|
i = int( inkey() )
|
||||||
|
return(i)
|
||||||
|
except ValueError:
|
||||||
|
print("Error.")
|
||||||
|
|
||||||
|
current_id=0
|
||||||
|
current_cmd=""
|
||||||
|
process = None
|
||||||
|
current_filename = ""
|
||||||
|
currentPlayList = []
|
||||||
|
playlistsDir = Path("./playlists")
|
||||||
|
if not playlistsDir.is_dir() : playlistsDir.mkdir()
|
||||||
|
|
||||||
|
def ask(q):
|
||||||
|
print(q)
|
||||||
|
return inkey()
|
||||||
|
|
||||||
|
def _ok(msg):
|
||||||
|
print( bcolors.BOLD+bcolors.OKBLUE+ msg + bcolors.ENDC)
|
||||||
|
|
||||||
|
def _err(msg):
|
||||||
|
print( bcolors.HL + msg + bcolors.ENDC)
|
||||||
|
|
||||||
|
def _kill(process):
|
||||||
|
if process :
|
||||||
|
try:
|
||||||
|
pid = os.getpgid(process.pid)
|
||||||
|
os.killpg(pid, signal.SIGTERM)
|
||||||
|
os.killpg(pid, signal.SIGKILL)
|
||||||
|
os.kill(pid, signal.SIGTERM)
|
||||||
|
os.kill(pid, signal.SIGKILL)
|
||||||
|
process.terminate()
|
||||||
|
process.kill()
|
||||||
|
except Exception as e:
|
||||||
|
print("woops:{}".format(e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _killBill():
|
||||||
|
subprocess.run("ps --ppid 1 -fo pid,sess,ppid,cmd | grep 'toRedis.py' | while read pid sid other; do pkill -9 -s $sid; done", shell=True,executable='/bin/bash')
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def action_info():
|
||||||
|
print("""
|
||||||
|
Welcome to LJ playlist manager
|
||||||
|
|
||||||
|
Currently running on
|
||||||
|
|
||||||
|
IP : {}
|
||||||
|
Port : {}
|
||||||
|
Key : {}
|
||||||
|
Scene : {}
|
||||||
|
Laser : {}
|
||||||
|
""".format(
|
||||||
|
environ["REDIS_IP"],
|
||||||
|
environ["REDIS_PORT"],
|
||||||
|
environ["REDIS_KEY"],
|
||||||
|
environ["REDIS_SCENE"],
|
||||||
|
environ["REDIS_LASER"]
|
||||||
|
))
|
||||||
|
|
||||||
|
|
||||||
|
def action_changeCommand( inc ):
|
||||||
|
global currentPlayList
|
||||||
|
global current_id
|
||||||
|
if 0 == len(currentPlayList):
|
||||||
|
_err("Empty playlist")
|
||||||
|
return False
|
||||||
|
current_id = (current_id + 1) % len(currentPlayList)
|
||||||
|
return True
|
||||||
|
|
||||||
|
def action_match( k ):
|
||||||
|
if int(k) > (len(currentPlayList) - 1):
|
||||||
|
print( bcolors.HL + "This key does not exist" + bcolors.ENDC )
|
||||||
|
return False
|
||||||
|
else :
|
||||||
|
current_id = int(k)
|
||||||
|
|
||||||
|
def action_runCommand():
|
||||||
|
global currentPlayList
|
||||||
|
global current_id
|
||||||
|
global process
|
||||||
|
|
||||||
|
# Get new command
|
||||||
|
try:
|
||||||
|
current_cmd = currentPlayList[current_id]
|
||||||
|
except IndexError as e:
|
||||||
|
_err("woops:{}".format(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("\n[!]New command:'{}'\n".format(current_cmd))
|
||||||
|
|
||||||
|
# Start subprocess
|
||||||
|
try :
|
||||||
|
_kill(process)
|
||||||
|
process = subprocess.Popen("./_run.sh '"+current_cmd+" | exports/toRedis.py -i $REDIS_IP -k $REDIS_KEY'", shell=True, executable='/bin/bash', stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, env=environ, preexec_fn=os.setsid)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("woops:{}".format(e))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def action_newCommand():
|
||||||
|
global currentPlayList
|
||||||
|
print("Enter new command or e(x)it.")
|
||||||
|
k = input()
|
||||||
|
# Exit early
|
||||||
|
if "x" == k:
|
||||||
|
return(False)
|
||||||
|
currentPlayList.append(k)
|
||||||
|
print(bcolors.OKBLUE + "Command added" + bcolors.ENDC)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def action_deleteCommand():
|
||||||
|
global currentPlayList
|
||||||
|
print("Select sequence to delete or e(x)it.")
|
||||||
|
action_listAll()
|
||||||
|
key = int(input())
|
||||||
|
# Exit early
|
||||||
|
if "x" == k:
|
||||||
|
return(False)
|
||||||
|
del currentPlayList[key]
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def action_listAll():
|
||||||
|
global currentPlayList, current_cmd
|
||||||
|
print("\n--------------------------------------")
|
||||||
|
for i,seq in enumerate(currentPlayList):
|
||||||
|
pre=""
|
||||||
|
suf=""
|
||||||
|
if current_cmd == seq :
|
||||||
|
pre = bcolors.HL
|
||||||
|
suf = bcolors.ENDC
|
||||||
|
print( pre + "{}\t{}".format(i,seq) + suf )
|
||||||
|
print("--------------------------------------\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def action_help():
|
||||||
|
global bindings
|
||||||
|
print("\nKey\tAction\n--------------------------------------")
|
||||||
|
for i in bindings:
|
||||||
|
print(" {}\t{}".format(bindings[i],i))
|
||||||
|
print("--------------------------------------\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def action_edit():
|
||||||
|
print("Enter the command number to edit, or 'x' to abort.")
|
||||||
|
k = intkey()
|
||||||
|
if 'x' == k:
|
||||||
|
return
|
||||||
|
print("Enter the next value, or 'x' to abort.")
|
||||||
|
value = input()
|
||||||
|
if 'x' == value:
|
||||||
|
return
|
||||||
|
currentPlayList[k] = value
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def action_loadPlaylist():
|
||||||
|
|
||||||
|
global playlistsDir
|
||||||
|
global currentPlayList
|
||||||
|
global current_playlist_name
|
||||||
|
# list files
|
||||||
|
i=0
|
||||||
|
file_list = [x for x in playlistsDir.glob("*json")]
|
||||||
|
if 0 == len(file_list ):
|
||||||
|
print( bcolors.HL + "Error. No file in path '{}'\n".format(playlistsDir.name))
|
||||||
|
return False
|
||||||
|
|
||||||
|
print("\n Id\tName")
|
||||||
|
for k,name in enumerate(file_list) :
|
||||||
|
print(" {}\t{}".format(k,name),flush=True)
|
||||||
|
|
||||||
|
# ask file
|
||||||
|
print("\nChoose a file or e(x)it:")
|
||||||
|
k = intkey()
|
||||||
|
if '' == k:
|
||||||
|
print("Invalid choice: '{}'".format(k))
|
||||||
|
return
|
||||||
|
|
||||||
|
# Exit early
|
||||||
|
if "x" == k: return(False)
|
||||||
|
|
||||||
|
# todo : helper for detecting invalid keys
|
||||||
|
try:
|
||||||
|
if k > (len(file_list) - 1):
|
||||||
|
print( bcolors.HL + "This key '{}' does not exist".format(k) + bcolors.ENDC )
|
||||||
|
return False
|
||||||
|
except TypeError:
|
||||||
|
print( bcolors.HL + "This key '{}' is not valid".format(k) + bcolors.ENDC )
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Load file
|
||||||
|
playlistFile = Path("./playlists/"+file_list[k].name)
|
||||||
|
currentPlayList = json.loads(playlistFile.read_text())
|
||||||
|
current_playlist_name = file_list[k].name
|
||||||
|
current_id = 0
|
||||||
|
print( bcolors.OKBLUE + "Playlist loaded: {}\n".format(current_playlist_name)+ bcolors.ENDC)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def action_newPlaylist():
|
||||||
|
global playlistsDir
|
||||||
|
global currentPlayList
|
||||||
|
# ask for name
|
||||||
|
print("Enter new playlist name (without.json) or e(x)it question?")
|
||||||
|
k = input()
|
||||||
|
|
||||||
|
# Exit early
|
||||||
|
if "x" == k:
|
||||||
|
return(False)
|
||||||
|
|
||||||
|
# save file
|
||||||
|
currentPlayList = []
|
||||||
|
_savePlaylist( k+".json" )
|
||||||
|
currentPlayList = []
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _savePlaylist( playlistname ):
|
||||||
|
global currentPlayList
|
||||||
|
filepath = Path("playlists/{}".format(playlistname))
|
||||||
|
with filepath.open("w", encoding ="utf-8") as f:
|
||||||
|
f.write(json.dumps(currentPlayList, indent=4, sort_keys=True))
|
||||||
|
return(True)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def action_savePlaylist( name=False ):
|
||||||
|
global current_playlist_name
|
||||||
|
playlist_name = name if name else current_playlist_name
|
||||||
|
if not playlist_name :
|
||||||
|
_err("No name found.")
|
||||||
|
return False
|
||||||
|
try:
|
||||||
|
_savePlaylist(playlist_name)
|
||||||
|
print( bcolors.OKBLUE + "\nSaved as '{}'.\n".format(playlist_name) + bcolors.ENDC)
|
||||||
|
except Exception as e:
|
||||||
|
print("woops:{}".format(e))
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def action_commandHelp():
|
||||||
|
global playlistsDir
|
||||||
|
|
||||||
|
# iterate through files
|
||||||
|
file_list=[]
|
||||||
|
for folder in ["generators","filters","exports"]:
|
||||||
|
p = Path("./"+folder)
|
||||||
|
for plFile in Path("./"+folder).iterdir() :
|
||||||
|
if re.match("^.*py$",plFile.name):
|
||||||
|
file_list.append(os.path.join(folder,plFile.name))
|
||||||
|
print("\n Id\tFile")
|
||||||
|
for k,filename in enumerate(file_list):
|
||||||
|
print(" {}\t{}".format(k,filename))
|
||||||
|
print("\nChoose a file:")
|
||||||
|
k = int(input())
|
||||||
|
print("\n-----------------------------------------------\n")
|
||||||
|
subprocess.run("python3 "+file_list[k]+" -h", shell=True, executable='/bin/bash')
|
||||||
|
print("\n-----------------------------------------------\n")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def _setKey( laser=0, scene=0 ):
|
||||||
|
global environ
|
||||||
|
laser = laser if laser else environ["REDIS_LASER"]
|
||||||
|
scene = scene if scene else environ["REDIS_SCENE"]
|
||||||
|
new_key = "/pl/{}/{}".format(scene,laser)
|
||||||
|
environ["REDIS_KEY"] = new_key
|
||||||
|
print("Sending new key '{}'".format(new_key))
|
||||||
|
|
||||||
|
|
||||||
|
def action_laserId():
|
||||||
|
k = int(ask("Enter the LJ Laser id [0-3]"))
|
||||||
|
_setKey( laser = k )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def action_laserScene():
|
||||||
|
k = int(ask("Enter the LJ Scene id [0-3]"))
|
||||||
|
_setKey( scene = k )
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def action_killPid():
|
||||||
|
print("Enter pid to kill")
|
||||||
|
kill_pid = input()
|
||||||
|
subprocess.run("pkill -9 -s $(awk '{print $6}' /proc/$kill_pid/stat)", shell=True,executable='/bin/bash', env={"kill_pid":kill_pid})
|
||||||
|
|
||||||
|
|
||||||
|
def action_quit():
|
||||||
|
print("Quit [Y/n]?")
|
||||||
|
quit = inkey()
|
||||||
|
if quit != "n":
|
||||||
|
_kill(process)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
environ = {
|
||||||
|
# "REDIS_IP" : "127.0.0.1",
|
||||||
|
"REDIS_IP" : "192.168.2.44",
|
||||||
|
"REDIS_PORT" : "6379",
|
||||||
|
"REDIS_KEY" : "/pl/0/0",
|
||||||
|
"REDIS_SCENE" : "0",
|
||||||
|
"REDIS_LASER" : "0"
|
||||||
|
}
|
12
clitools/runner_midi.py
Normal file
12
clitools/runner_midi.py
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import signal
|
||||||
|
import subprocess
|
||||||
|
import time
|
||||||
|
import tty,termios
|
||||||
|
import re
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
import runner_lib as runner
|
Loading…
Reference in New Issue
Block a user