Update some scripts, add script for backing up TL2 savegames and using ncmpcpp to play back and manage a mpd-httpd stream
parent
bb560c9e93
commit
aa3326e000
5 changed files with 232 additions and 1 deletions
@ -0,0 +1,23 @@ |
||||
#!/bin/zsh |
||||
# Back-up Torchlight II savegames |
||||
|
||||
# Path to steam library |
||||
STEAM_APPS=/media/Data/SteamLibrary/SteamApps |
||||
# Steam User ID to save, use * to backup all saves |
||||
STEAM_USER=* |
||||
# Path to create backups at |
||||
BACKUP_DIR=~/.backup/TL2 |
||||
|
||||
# TL2 paths |
||||
TL2_FOLDER="common/Torchlight II" |
||||
SAV_FOLDER="my games/runic games/torchlight 2" |
||||
|
||||
source $DOTFILES/lib/libzsh-utils.zsh |
||||
cd "$STEAM_APPS/$TL2_FOLDER/$SAV_FOLDER" |
||||
|
||||
filename="tl2saves_`date +%Y-%m-%d_%H-%M`.tar.xz" |
||||
msg "Backing up Torchlight II Saves as $filename (in $BACKUP_DIR)" |
||||
[ -e "$BACKUP_DIR" ] || mkdir -p "$BACKUP_DIR" |
||||
color 36 tar -cJvf "$BACKUP_DIR/$filename" {,mod}save/${^~STEAM_USER} |
||||
msg "Done!" |
||||
|
@ -0,0 +1,170 @@ |
||||
#!/usr/bin/python |
||||
# Use ncmpcpp to play and control a mpd-httpd stream while still being able to control the local volume |
||||
# (c) 2015 Taeyeon Mori |
||||
# requires Python 3.4 or higher |
||||
|
||||
import os |
||||
import sys |
||||
import pty |
||||
import tty |
||||
import stat |
||||
import array |
||||
import fcntl |
||||
import signal |
||||
import asyncio |
||||
import termios |
||||
import argparse |
||||
import contextlib |
||||
import subprocess |
||||
import concurrent.futures |
||||
|
||||
URL_TEMPLATE = "http://{host}:{http_port}/" |
||||
|
||||
|
||||
# ASYNCIO HACKS ================================================================================== |
||||
# asyncio is following a great rationale, but at this point in time, there are still some things |
||||
# missing from it to make it truly useful. Here comes one of 'em: |
||||
@asyncio.coroutine |
||||
def async_stdio(loop=None): |
||||
# get streams for the standard I/O (stdin and stdout) |
||||
if not os.path.sameopenfile(0, 1): |
||||
raise RuntimeError("The async_stdio hack only works when both STDIN and STDOUT point to the same TTY/PTS") |
||||
|
||||
if loop is None: |
||||
loop = asyncio.get_event_loop() |
||||
|
||||
reader = asyncio.StreamReader() |
||||
reader_protocol = asyncio.StreamReaderProtocol(reader) |
||||
|
||||
writer_transport, writer_protocol = yield from loop.connect_write_pipe(asyncio.streams.FlowControlMixin, os.fdopen(0, 'wb')) |
||||
writer = asyncio.StreamWriter(writer_transport, writer_protocol, None, loop) |
||||
|
||||
yield from loop.connect_read_pipe(lambda: reader_protocol, sys.stdin) |
||||
|
||||
return reader, writer |
||||
|
||||
|
||||
@asyncio.coroutine |
||||
def async_pty(pty, loop=None): |
||||
# same as above, just with a pty descriptor instead of STD* |
||||
if loop is None: |
||||
loop = asyncio.get_event_loop() |
||||
|
||||
reader = asyncio.StreamReader() |
||||
reader_protocol = asyncio.StreamReaderProtocol(reader) |
||||
|
||||
writer_transport, writer_protocol = yield from loop.connect_write_pipe(asyncio.streams.FlowControlMixin, os.fdopen(pty, 'wb')) |
||||
writer = asyncio.StreamWriter(writer_transport, writer_protocol, None, loop) |
||||
|
||||
yield from loop.connect_read_pipe(lambda: reader_protocol, os.fdopen(pty)) |
||||
|
||||
return reader, writer |
||||
# END ASYNCIO HACKS ============================================================================== |
||||
|
||||
|
||||
def parse_args(argv): |
||||
parser = argparse.ArgumentParser(prog=argv[0], add_help=False) |
||||
parser.add_argument("--help", action="store_true") |
||||
parser.add_argument("-h", "--host", default=os.environ.get("MPD_HOST", "localhost")) |
||||
parser.add_argument("-p", "--port", default=os.environ.get("MPD_PORT", "6600")) |
||||
parser.add_argument("-P", "--http-port", default="8000") |
||||
parser.add_argument("-E", "--http-output", default="1") |
||||
args = parser.parse_args(argv[1:]) |
||||
if args.help: |
||||
parser.print_help() |
||||
sys.exit(0) |
||||
return args |
||||
|
||||
|
||||
@asyncio.coroutine |
||||
def main_coro(args, loop): |
||||
# Enable stream |
||||
subprocess.check_call(["mpc", "-h", args.host, "-p", args.port, "enable", args.http_output]) |
||||
|
||||
# Manage stdio |
||||
std_reader, std_writer = yield from async_stdio() |
||||
|
||||
# Manage vlc |
||||
vlc_proc = yield from asyncio.create_subprocess_exec("vlc", "--repeat", URL_TEMPLATE.format(**vars(args)), "-I", "rc", stdin=subprocess.PIPE, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) |
||||
vlc_writer = vlc_proc.stdin |
||||
|
||||
# Manage pty |
||||
ptm, pts = pty.openpty() |
||||
loop.add_signal_handler(signal.SIGWINCH, propagate_winsize, ptm) |
||||
propagate_winsize(ptm) |
||||
|
||||
# Manage ncmpc |
||||
ptm_reader, ptm_writer = yield from async_pty(ptm) |
||||
pty_proc = yield from asyncio.create_subprocess_exec("ncmpcpp", "-h", args.host, "-p", args.port, stdin=pts, stdout=pts, stderr=pts, start_new_session=True, preexec_fn=reopen_tty) |
||||
|
||||
# Magic |
||||
input_task = asyncio.async(process_input(std_reader, ptm_writer, vlc_writer)) |
||||
output_task = asyncio.async(process_output(ptm_reader, std_writer)) |
||||
yield from asyncio.wait([input_task, output_task, pty_proc.wait()], return_when=concurrent.futures.FIRST_COMPLETED) |
||||
|
||||
# Cleanup |
||||
if pty_proc.returncode is None: |
||||
pty_proc.terminate() |
||||
vlc_proc.terminate() |
||||
|
||||
yield from asyncio.wait([input_task, output_task], timeout=1) |
||||
|
||||
loop.remove_signal_handler(signal.SIGWINCH) |
||||
os.close(ptm) |
||||
|
||||
|
||||
def propagate_winsize(fd): |
||||
# Notify pty of window size |
||||
buf = array.array('h', [0, 0, 0, 0]) |
||||
fcntl.ioctl(1, termios.TIOCGWINSZ, buf, True) |
||||
fcntl.ioctl(fd, termios.TIOCSWINSZ, buf) |
||||
|
||||
|
||||
def reopen_tty(): |
||||
# reopen tty to make it the controlling tty (see stdlib pty) |
||||
open(os.ttyname(1), "wb").close() |
||||
|
||||
|
||||
@asyncio.coroutine |
||||
def process_input(reader, writer, vlc_writer): |
||||
reader_fd = reader._transport.get_extra_info("pipe").fileno() |
||||
if True: # placeholder for eventual context manager for termio reset |
||||
tty.setraw(reader_fd) |
||||
while True: |
||||
data = yield from reader.read(512) |
||||
if not data: |
||||
break |
||||
elif data == b'+': |
||||
vlc_writer.write(b"volup 2\n") |
||||
yield from vlc_writer.drain() |
||||
elif data == b'-': |
||||
vlc_writer.write(b"voldown 2\n") |
||||
yield from vlc_writer.drain() |
||||
#elif data == b'\x03': # CTRL-C, RAW mode |
||||
# raise KeyboardInterrupt() |
||||
else: |
||||
writer.write(data) |
||||
yield from writer.drain() |
||||
|
||||
|
||||
@asyncio.coroutine |
||||
def process_output(reader, writer): |
||||
while True: |
||||
data = yield from reader.read(256) |
||||
if data: |
||||
writer.write(data) |
||||
yield from writer.drain() |
||||
else: |
||||
break |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
args = parse_args(sys.argv) |
||||
loop = asyncio.get_event_loop() |
||||
|
||||
mode = termios.tcgetattr(0) |
||||
try: |
||||
sys.exit(loop.run_until_complete(main_coro(args, loop))) |
||||
finally: |
||||
termios.tcsetattr(0, termios.TCSADRAIN, mode) |
||||
|
@ -0,0 +1,37 @@ |
||||
#!/bin/sh |
||||
# connect to the mpd httpd and open ncmpcpp |
||||
# Requirements: vlc, mpc, ncmpcpp |
||||
# Use MPD_HOST, MPD_PORT, MPD_HTTP_SINK, MPD_HTTP_PORT environment variables instead of arguments |
||||
|
||||
: ${MPD_HOST:=127.0.0.1} ${MPD_PORT:=6600} |
||||
: ${MPD_HTTP_SINK:=1} ${MPD_HTTP_PORT:=8080} |
||||
|
||||
export MPD_HOST |
||||
export MPD_PORT |
||||
|
||||
# Find programs |
||||
mpc=`which mpc 2>/dev/null` |
||||
vlc=`which vlc 2>/dev/null` |
||||
client=`which ncmpcpp 2>/dev/null || which ncmpc 2>/dev/null` |
||||
|
||||
if [ -z "$mpc" ]; then |
||||
echo "ERROR: mpc (MusicPlayerClient) not found on the system" |
||||
elif [ -z "$vlc" ]; then |
||||
echo "ERROR: VLC is required for local stream playback" |
||||
elif [ -z "$client" ]; then |
||||
echo "ERROR: No MPD client installed (ncmpcpp or ncmpc)" |
||||
else |
||||
# Enable Stream |
||||
mpc enable $MPC_HTTP_SINK |
||||
|
||||
# Play Stream (have vlc on fd:3) |
||||
vlc --repeat http://$MPD_HOST:$MPD_HTTP_PORT/mpd.ogg -I dummy 2>/dev/null & |
||||
vlc_pid=$! |
||||
|
||||
# Launch client in pty |
||||
"$client" -h $MPD_HOST -p $MPD_PORT |
||||
|
||||
# Stop stream playback |
||||
kill $vlc_pid |
||||
fi |
||||
|
Loading…
Reference in new issue