pandora_cdosea/playout/playsync.py

214 lines
6.3 KiB
Python
Raw Normal View History

2017-05-15 17:03:50 +00:00
#!/usr/bin/python3
import argparse
import datetime
import os
import random
import socket
import string
2017-05-20 16:30:31 +00:00
import subprocess
2017-05-15 17:03:50 +00:00
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')
2017-05-20 16:30:31 +00:00
DEFAULT_PORT = 2680
DEBUG = False
2017-05-15 17:03:50 +00:00
2017-05-20 16:30:31 +00:00
def update_playlist(playlist, prefix='video/', position=None):
2017-05-15 17:03:50 +00:00
playlist = 'play.m3u'
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])
2017-05-20 16:30:31 +00:00
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)
2017-05-15 17:03:50 +00:00
files.append(f)
2017-05-20 16:30:31 +00:00
pos -= 1
2017-05-15 17:03:50 +00:00
with open(playlist, 'w') as f:
f.write('\n'.join(files))
2017-05-20 16:30:31 +00:00
f.write('\n')
return position, len(files)
def trigger_lights(path):
#number = int(path.split('/')[-1].split('.')[0][1:])
#cmd = ['/opt/LanBox-JSONRPC/fade.py', str(number)]
cmd = ['./lights.py', path]
subprocess.Popen(cmd)
2017-05-15 17:03:50 +00:00
class ThreadingUDPServer(ThreadingMixIn, UDPServer):
pass
2017-05-20 16:30:31 +00:00
def q_binding(self, *args):
self.player.running = False
self.shutdown()
2017-05-15 17:03:50 +00:00
class Handler(BaseRequestHandler):
def handle(self):
data = self.request[0].strip()
2017-05-20 16:30:31 +00:00
logger.debug("%s said: %s", self.client_address[0], data)
queue = False
if data == b'POS':
response = ('%d' % self.server.player.position).encode()
2017-05-15 17:03:50 +00:00
else:
2017-05-20 16:30:31 +00:00
if self.server.player.playing:
response = b'PLAYING'
else:
response = b'WAITING'
queue = True
socket = self.request[1]
2017-05-15 17:03:50 +00:00
socket.sendto(response, self.client_address)
2017-05-20 16:30:31 +00:00
if queue:
self.server.player.queue.put('')
2017-05-15 17:03:50 +00:00
class Player(Thread):
first = True
running = True
playing = False
current = ''
2017-05-20 16:30:31 +00:00
playlist_items = 260
2017-05-15 17:03:50 +00:00
2017-05-20 16:30:31 +00:00
def __init__(self, peer, player, playlist, lights=False):
2017-05-15 17:03:50 +00:00
self.peer = peer
self.playlist = playlist
self.player = player
2017-05-20 16:30:31 +00:00
self.lights = lights
2017-05-15 17:03:50 +00:00
self.queue = Queue()
Thread.__init__(self)
self.daemon = True
self.start()
def run(self):
logger.debug("update playlist")
2017-05-20 16:30:31 +00:00
self.position, self.playlist_items = update_playlist(self.playlist, position=self.get_position())
2017-05-15 17:03:50 +00:00
logger.debug("load playlist")
self.player.loadlist(self.playlist)
self.player.pause = True
while self.running:
logger.debug("play")
self.play()
2017-05-20 16:30:31 +00:00
self.position = (self.position + 1) % self.playlist_items
2017-05-15 17:03:50 +00:00
logger.debug("ping")
self.ping()
logger.debug("wait")
self.queue.get()
def play(self):
self.playing = True
self.player.pause = False
2017-05-20 16:30:31 +00:00
if self.lights:
trigger_lights(self.player.path.decode())
2017-05-15 17:03:50 +00:00
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)
2017-05-20 16:30:31 +00:00
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
2017-05-15 17:03:50 +00:00
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)
2017-05-20 16:30:31 +00:00
parser.add_argument('--lights', action='store_true', help='lights', default=False)
2017-05-15 17:03:50 +00:00
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)
2017-05-20 16:30:31 +00:00
base = os.path.dirname(os.path.abspath(__file__))
os.chdir(base)
if args.lights:
time.sleep(5)
2017-05-21 21:14:15 +00:00
# 42 max
2017-05-20 16:30:31 +00:00
player = mpv.MPV(
log_handler=my_log, input_default_bindings=True,
2017-05-21 11:56:38 +00:00
input_vo_keyboard=True, sub_text_font_size=28, sub_text_font='Menlo'
2017-05-20 16:30:31 +00:00
)
2017-05-15 17:03:50 +00:00
player.fullscreen = not args.window
player.loop = 'inf'
2017-05-20 16:30:31 +00:00
2017-05-15 17:03:50 +00:00
server = ThreadingUDPServer(('0.0.0.0', args.port), Handler)
2017-05-20 16:30:31 +00:00
server.player = Player(peer, player, args.playlist, args.lights)
player.register_key_binding('q', server.q_binding)
2017-05-15 17:03:50 +00:00
logger.debug("listen on %s...", args.port)
try:
server.serve_forever()
except KeyboardInterrupt:
pass
del player
if __name__ == "__main__":
main()