add sync/lights
This commit is contained in:
parent
e68500be28
commit
4c3a6a8c2d
3 changed files with 243 additions and 2 deletions
|
@ -2,7 +2,6 @@
|
|||
if [ "x$1" == "xupdate" ]; then
|
||||
shift
|
||||
exec python3 -m cdoseaplay.update $@
|
||||
|
||||
else
|
||||
shift
|
||||
gsettings set org.gnome.desktop.screensaver ubuntu-lock-on-suspend false
|
||||
|
@ -14,5 +13,9 @@ else
|
|||
gsettings set org.gnome.desktop.background primary-color '#888888'
|
||||
gsettings set org.gnome.desktop.background secondary-color '#888888'
|
||||
gsettings set org.gnome.desktop.background picture-options 'none'
|
||||
if [ "x$1" == "xsync" ]; then
|
||||
exec python3 -m cdoseaplay.sync $@
|
||||
else
|
||||
exec python3 -m cdoseaplay.play $@
|
||||
fi
|
||||
fi
|
||||
|
|
26
cdoseaplay/lights.py
Executable file
26
cdoseaplay/lights.py
Executable file
|
@ -0,0 +1,26 @@
|
|||
#!/usr/bin/python3
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
|
||||
import ox
|
||||
from pi import random
|
||||
|
||||
path = sys.argv[1]
|
||||
|
||||
n = int(os.path.getctime(path) - 1495280000)
|
||||
duration = ox.avinfo(path)['duration']
|
||||
|
||||
seq = random(n * 1000)
|
||||
pos = 0
|
||||
lights = []
|
||||
while pos < duration - 15:
|
||||
sleep = seq() + 15
|
||||
light = seq() + 1
|
||||
if pos + sleep > duration:
|
||||
break
|
||||
time.sleep(sleep)
|
||||
cmd = ['/opt/LanBox-JSONRPC/fade.py', str(light)]
|
||||
subprocess.Popen(cmd)
|
||||
pos += sleep
|
212
cdoseaplay/sync.py
Executable file
212
cdoseaplay/sync.py
Executable file
|
@ -0,0 +1,212 @@
|
|||
#!/usr/bin/python3
|
||||
import argparse
|
||||
import datetime
|
||||
import os
|
||||
import random
|
||||
import socket
|
||||
import string
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from glob import glob
|
||||
from queue import Queue
|
||||
from socketserver import UDPServer, ThreadingMixIn, BaseRequestHandler
|
||||
from threading import Thread
|
||||
|
||||
import ox
|
||||
import mpv
|
||||
|
||||
import logging
|
||||
logger = logging.getLogger('cdosea')
|
||||
|
||||
DEFAULT_PORT = 2680
|
||||
DEBUG = False
|
||||
|
||||
def update_playlist(playlist, prefix='video/', position=None):
|
||||
playlist = os.path.expanduser('~/Videos/cdosea.m3u')
|
||||
prefix = os.path.expanduser('~/Videos/CDOSEA')
|
||||
|
||||
files = []
|
||||
videos = {}
|
||||
for letter in string.ascii_uppercase:
|
||||
videos[letter] = glob('%s%s*.mp4' % (prefix, letter.lower()))
|
||||
random.shuffle(videos[letter])
|
||||
|
||||
for i in range(10):
|
||||
for letter in string.ascii_uppercase:
|
||||
files.append(videos[letter][i])
|
||||
|
||||
if position is None:
|
||||
today = datetime.date.today()
|
||||
seconds_since_midnight = time.time() - time.mktime(today.timetuple())
|
||||
offset = 0
|
||||
position = 0
|
||||
|
||||
while offset < seconds_since_midnight:
|
||||
f = files.pop(0)
|
||||
try:
|
||||
offset += ox.avinfo(f)['duration']
|
||||
files.append(f)
|
||||
except:
|
||||
pass
|
||||
position += 1
|
||||
else:
|
||||
pos = position
|
||||
while pos:
|
||||
f = files.pop(0)
|
||||
files.append(f)
|
||||
pos -= 1
|
||||
|
||||
with open(playlist, 'w') as f:
|
||||
f.write('\n'.join(files))
|
||||
f.write('\n')
|
||||
return position, len(files)
|
||||
|
||||
def trigger_lights(path):
|
||||
cmd = ['python3', '-m', 'cdoseaplay.lights', path]
|
||||
subprocess.Popen(cmd)
|
||||
|
||||
class ThreadingUDPServer(ThreadingMixIn, UDPServer):
|
||||
pass
|
||||
|
||||
def q_binding(self, *args):
|
||||
self.player.running = False
|
||||
self.shutdown()
|
||||
|
||||
class Handler(BaseRequestHandler):
|
||||
|
||||
def handle(self):
|
||||
data = self.request[0].strip()
|
||||
logger.debug("%s said: %s", self.client_address[0], data)
|
||||
queue = False
|
||||
if data == b'POS':
|
||||
response = ('%d' % self.server.player.position).encode()
|
||||
else:
|
||||
if self.server.player.playing:
|
||||
response = b'PLAYING'
|
||||
else:
|
||||
response = b'WAITING'
|
||||
queue = True
|
||||
socket = self.request[1]
|
||||
socket.sendto(response, self.client_address)
|
||||
if queue:
|
||||
self.server.player.queue.put('')
|
||||
|
||||
class Player(Thread):
|
||||
first = True
|
||||
running = True
|
||||
playing = False
|
||||
current = ''
|
||||
playlist_items = 260
|
||||
|
||||
def __init__(self, peer, player, playlist, lights=False):
|
||||
self.peer = peer
|
||||
self.playlist = playlist
|
||||
self.player = player
|
||||
self.lights = lights
|
||||
self.queue = Queue()
|
||||
Thread.__init__(self)
|
||||
self.daemon = True
|
||||
self.start()
|
||||
|
||||
def run(self):
|
||||
logger.debug("update playlist")
|
||||
self.position, self.playlist_items = update_playlist(self.playlist, position=self.get_position())
|
||||
logger.debug("load playlist")
|
||||
self.player.loadlist(self.playlist)
|
||||
self.player.pause = True
|
||||
while self.running:
|
||||
logger.debug("play")
|
||||
self.play()
|
||||
self.position = (self.position + 1) % self.playlist_items
|
||||
logger.debug("ping")
|
||||
self.ping()
|
||||
logger.debug("wait")
|
||||
self.queue.get()
|
||||
|
||||
def play(self):
|
||||
self.playing = True
|
||||
self.player.pause = False
|
||||
if self.lights:
|
||||
trigger_lights(self.player.path.decode())
|
||||
self.player.wait_for_playback()
|
||||
self.player.pause = True
|
||||
self.playing = False
|
||||
|
||||
def ping(self):
|
||||
data = 'NEXT'
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
logger.debug("ping: %s %s", self.peer, data)
|
||||
sock.sendto(bytes(data + "\n", "utf-8"), self.peer)
|
||||
received = str(sock.recv(1024), "utf-8")
|
||||
if received == 'WAITING' and not self.queue.qsize():
|
||||
if self.first:
|
||||
self.queue.put('')
|
||||
self.first = False
|
||||
logger.debug("Sent: %s", data)
|
||||
logger.debug("Received: %s", received)
|
||||
|
||||
def get_position(self):
|
||||
try:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
sock.settimeout(1)
|
||||
data = 'POS'
|
||||
sock.sendto(bytes(data + "\n", "utf-8"), self.peer)
|
||||
received = str(sock.recv(1024), "utf-8")
|
||||
pos = int(received)
|
||||
except socket.timeout:
|
||||
logger.debug('no response', exc_info=1)
|
||||
pos = None
|
||||
return pos
|
||||
|
||||
|
||||
def my_log(loglevel, component, message):
|
||||
logger.debug('[{}] {}: {}'.format(loglevel, component, message))
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(description='play 2 screens in sync')
|
||||
parser.add_argument('--peer', help='ip[:port] of peer', required=True)
|
||||
parser.add_argument('--port', type=int, help='local port', default=DEFAULT_PORT)
|
||||
parser.add_argument('--playlist', help='play.m3u', default='play.m3u')
|
||||
parser.add_argument('--window', action='store_true', help='run in window', default=False)
|
||||
parser.add_argument('--debug', action='store_true', help='debug', default=False)
|
||||
parser.add_argument('--lights', action='store_true', help='lights', default=False)
|
||||
args = parser.parse_args()
|
||||
|
||||
if ':' in args.peer:
|
||||
peer = args.peer.split(':')
|
||||
peer = (peer[0], int(peer[1]))
|
||||
else:
|
||||
peer = (args.peer, DEFAULT_PORT)
|
||||
|
||||
DEBUG = args.debug
|
||||
if DEBUG:
|
||||
log_format = '%(asctime)s:%(levelname)s:%(name)s:%(message)s'
|
||||
logging.basicConfig(level=logging.DEBUG, format=log_format)
|
||||
base = os.path.dirname(os.path.abspath(__file__))
|
||||
os.chdir(base)
|
||||
if args.lights:
|
||||
time.sleep(5)
|
||||
|
||||
# 42 max
|
||||
player = mpv.MPV(
|
||||
log_handler=my_log, input_default_bindings=True,
|
||||
input_vo_keyboard=True, sub_text_font_size=28, sub_text_font='Menlo'
|
||||
)
|
||||
player.fullscreen = not args.window
|
||||
player.loop = 'inf'
|
||||
|
||||
server = ThreadingUDPServer(('0.0.0.0', args.port), Handler)
|
||||
server.player = Player(peer, player, args.playlist, args.lights)
|
||||
player.register_key_binding('q', server.q_binding)
|
||||
|
||||
logger.debug("listen on %s...", args.port)
|
||||
try:
|
||||
server.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
del player
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in a new issue