Skip to content

Instantly share code, notes, and snippets.

@bluec0re
Last active August 29, 2015 14:12
Show Gist options
  • Save bluec0re/e1c13be6c60015f29aff to your computer and use it in GitHub Desktop.
Save bluec0re/e1c13be6c60015f29aff to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# encoding: utf-8
# Licensed under GPLv3
# http://www.gnu.org/licenses/gpl-3.0.txt
# requires
# - wmctrl
# - vlc
# - irssi
# - terminal emulator with '-e' switch
# - bwm-ng (optional for bandwith display)
import argparse
from subprocess import Popen, check_output, check_call
import os
import time
import shutil
from collections import namedtuple
#TERM = os.environ.get('TERM', '/usr/bin/terminator')
TERM = '/usr/bin/terminator'
NICK = os.getlogin()
CACHING_MS = 1000
HALL = [
{
"slides": "rtmp://rtmp.stream.c3voc.de:1935/stream/s1_native_slides",
"audio": "http://audio.stream.c3voc.de:8000/s1_native.opus",
# "video": "rtmp://rtmp.stream.c3voc.de:1935/stream/s1_native_sd",
# "video": "http://hls.stream.c3voc.de/hls/s1_native.m3u8",
"video": "http://webm.stream.c3voc.de:8000/s1_native_sd.webm",
"irc": {
"host": "irc.hackint.eu",
"port": "6667",
"nick": NICK,
"channel": "31C3-hall-1"
}
},
{
"slides": "rtmp://rtmp.stream.c3voc.de:1935/stream/s2_native_slides",
"audio": "http://audio.stream.c3voc.de:8000/s2_native.opus",
# "video": "rtmp://rtmp.stream.c3voc.de:1935/stream/s2_native_sd",
# "video": "http://hls.stream.c3voc.de/hls/s2_native.m3u8",
"video": "http://webm.stream.c3voc.de:8000/s2_native_sd.webm",
"irc": {
"host": "irc.hackint.eu",
"port": "6667",
"nick": NICK,
"channel": "31C3-hall-2"
}
},
{
"slides": "rtmp://rtmp.stream.c3voc.de:1935/stream/s3_native_slides",
"audio": "http://audio.stream.c3voc.de:8000/s3_native.opus",
# "video": "rtmp://rtmp.stream.c3voc.de:1935/stream/s3_native_sd",
# "video": "http://hls.stream.c3voc.de/hls/s3_native.m3u8",
"video": "http://webm.stream.c3voc.de:8000/s3_native_sd.webm",
"irc": {
"host": "irc.hackint.eu",
"port": "6667",
"nick": NICK,
"channel": "31C3-hall-3"
}
},
{
"slides": "rtmp://rtmp.stream.c3voc.de:1935/stream/s4_native_slides",
"audio": "http://audio.stream.c3voc.de:8000/s4_native.opus",
# "video": "rtmp://rtmp.stream.c3voc.de:1935/stream/s4_native_sd",
# "video": "http://hls.stream.c3voc.de/hls/s4_native.m3u8",
"video": "http://webm.stream.c3voc.de:8000/s4_native_sd.webm",
"irc": {
"host": "irc.hackint.eu",
"port": "6667",
"nick": NICK,
"channel": "31C3-hall-4"
}
}
]
class Window:
def __init__(self, id, pid, x, y, width, height):
self.id = id
self.pid = pid
self.x = x
self.y = y
self.width = width
self.height = height
def move(self, x, y):
window = ['-i', '-r', hex(self.id)]
check_call(['wmctrl'] + window + ['-b', 'remove,maximized_vert,maximized_horz'])
check_call(['wmctrl'] + window + ['-e', '0,{0},{1},-1,-1'.format(max(x,0), max(y,0))])
check_call(['wmctrl'] + window + ['-b', 'remove,hidden,shaded'])
self.x, self.y = x, y
def resize(self, width, height):
window = ['-i', '-r', hex(self.id)]
check_call(['wmctrl'] + window + ['-b', 'remove,maximized_vert,maximized_horz'])
check_call(['wmctrl'] + window + ['-e', '0,-1,-1,{0},{1}'.format(width, height)])
check_call(['wmctrl'] + window + ['-b', 'remove,hidden,shaded'])
self.width, self.height = width, height
def rearrange(self, x, y, width, height):
window = ['-i', '-r', hex(self.id)]
check_call(['wmctrl'] + window + ['-b', 'remove,maximized_vert,maximized_horz'])
check_call(['wmctrl'] + window + ['-e', '0,-1,-1,{0},{1}'.format(width, height)])
check_call(['wmctrl'] + window + ['-e', '0,{0},{1},-1,-1'.format(max(x,0), max(y,0))])
check_call(['wmctrl'] + window + ['-b', 'remove,hidden,shaded'])
self.x, self.y = x, y
self.width, self.height = width, height
def __repr__(self):
return "Window({0.id:x}, {0.pid}, {0.x}, {0.y}, {0.width}, {0.height})".format(self)
class Tiler:
def __init__(self):
self.window_list = {}
desktops = check_output(["wmctrl", "-d"]).decode().split("\n")
self.desk_list = [line.split()[0] for line in desktops if line]
current = list(filter(lambda x: x and x.split()[1] == '*', desktops))[0].split()
self.desktop = current[0]
self.x, self.y = tuple(map(int, current[7].split(",")))
self.width, self.height = tuple(map(int, current[8].split("x")))
def get_win_list(self):
windows = check_output(["wmctrl", "-lpG"]).decode().split("\n")
win_list = {}
for desk in self.desk_list:
win_list[desk] = list(map(lambda x: Window(int(x.split()[0], 16),
int(x.split()[2]),
int(x.split()[3]),
int(x.split()[4]),
int(x.split()[5]),
int(x.split()[6])),
filter(lambda x: x and x.split()[1] == desk, windows)))
return win_list
def arrange(self, masterpid=None, ignore=[]):
winlist = self.get_win_list()[self.desktop]
print("Windows: " + str(winlist))
master_window_factor = 0.65
wintitle = 21
winborder = 1
if masterpid is not None:
master = list(filter(lambda w: w.pid == masterpid, winlist))[0]
winlist = list(filter(lambda w: w.pid != masterpid, winlist))
else:
master = winlist[0]
winlist = winlist[1:]
winlist = list(filter(lambda w: w.pid not in ignore, winlist))
layout = [
(master, (self.x, self.y, int(self.width * master_window_factor), self.height - wintitle - winborder))
]
x = self.x + layout[0][1][2] + 2 * winborder
width = int(self.width*(1-master_window_factor) - 2*winborder)
height = int(self.height/len(winlist) - wintitle - winborder)
for i, win in enumerate(winlist):
y = self.y + int(self.height/len(winlist)) * i
layout.append((win, (x, y, width, height)))
for win, l in layout:
win.rearrange(*l)
class Hall:
def __init__(self, saal):
self.settings = HALL[saal-1]
self.irc = self.slides = self.video = self.audio = self.bw = None
def start_bw(self):
self.bw = Popen([TERM, "-e", "bwm-ng"])
def start_irc(self):
try:
self.clean_irc()
except:
pass
path = '.irssi_{host}_{channel}'.format(**self.settings['irc'])
shutil.copytree(os.path.expanduser('~/.irssi'), path)
with open(path + '/startup', 'w') as fp:
fp.write("NETWORK ADD 31C3\nSERVER ADD -auto -network 31C3 {host} -port {port}\nCHANNEL ADD -auto #{channel} 31C3\nSET window_auto_change ON\nQUIT\n".format(**self.settings['irc']))
irc = Popen(['irssi', '--home', path])
irc.wait()
os.unlink(path + '/startup')
self.irc = Popen([TERM, '-e', 'irssi "--home={0}" -n "{nick}"'.format(path, **self.settings['irc'])])
def clean_irc(self):
path = '.irssi_{host}_{channel}'.format(**self.settings['irc'])
shutil.rmtree(path)
def start_audio(self):
self.audio = Popen(['vlc', '--network-caching', str(CACHING_MS), '--qt-start-minimized', self.settings['audio']])
def start_slides(self):
self.slides = Popen(['vlc', '--network-caching', str(CACHING_MS), '--no-qt-video-autoresize', self.settings['slides']])
def start_video(self, seperate_audio=True):
if seperate_audio:
self.video = Popen(['vlc', '--network-caching', str(CACHING_MS), '--no-audio', '--no-qt-video-autoresize', self.settings['video']])
else:
self.video = Popen(['vlc', '--network-caching', str(CACHING_MS), '--no-qt-video-autoresize', self.settings['video']])
def start(self, seperate_audio=True, video=True, slides=True, irc=True):
if seperate_audio:
self.start_audio()
if video:
self.start_video(seperate_audio)
if slides:
self.start_slides()
if irc:
self.start_irc()
def terminate(self):
if self.audio:
self.audio.terminate()
self.audio.wait()
if self.slides:
self.slides.terminate()
self.slides.wait()
if self.video:
self.video.terminate()
self.video.wait()
if self.irc:
self.irc.terminate()
self.irc.wait()
self.clean_irc()
if self.bw:
self.bw.terminate()
def wait(self):
if self.audio:
self.audio.wait()
if self.video:
self.video.wait()
if self.irc:
self.irc.wait()
self.clean_irc()
if self.slides:
self.slides.wait()
def getppid(pid):
with open("/proc/{0}/stat".format(pid)) as fp:
return int(fp.read().split()[3])
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument("HALL", choices=range(1, len(HALL)+1), type=int)
parser.add_argument("-bw", help="show bandwith", action="store_true")
parser.add_argument("--no-audio", help="disable seperate audio streaming", action="store_false", dest='audio')
parser.add_argument("--no-video", help="disable video stream", action="store_false", dest='video')
parser.add_argument("--no-slides", help="disable slide stream", action="store_false", dest='slides')
parser.add_argument("--no-irc", help="disable slide stream", action="store_false", dest='irc')
args = parser.parse_args()
s = Hall(args.HALL)
try:
if args.bw:
s.start_bw()
s.start(seperate_audio=args.audio, video=args.video, slides=args.slides, irc=args.irc)
time.sleep(2)
tiler = Tiler()
ignore = [os.getppid()]
while True:
pid = ignore[-1]
ppid = getppid(pid)
if ppid == 0:
break
ignore.append(ppid)
master = s.video or s.slides or s.irc
tiler.arrange(master.pid, ignore)
s.wait()
except Exception as e:
s.terminate()
if not isinstance(e, KeyboardInterrupt):
raise
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment