cdosea-play/cdoseaplay/sync.py

178 lines
5.6 KiB
Python
Executable file

#!/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
from .utils import update_playlist, get_player, trigger_lights, get_path
from . import config
import logging
logger = logging.getLogger('cdosea')
DEFAULT_PORT = 2680
DEBUG = False
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]
logger.debug("respond to %s: %s", self.client_address[0], response)
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, prefix, server):
self.peer = peer
self.playlist = playlist
self.prefix = prefix
self.player = player
self.server = server
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, self.prefix, position=self.get_position())
logger.debug("load playlist")
self.player.loadlist(self.playlist)
try:
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()
except:
logger.error("playback failed")
self.running = False
self.server.shutdown()
def play(self):
self.playing = True
self.player.pause = False
if config.lights and self.player.path:
trigger_lights(get_path(self.player.path))
self.player.wait_for_playback()
self.player.pause = True
self.playing = False
def ping(self):
data = 'NEXT'
if ':' in self.peer[0]:
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
else:
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:
if ':' in self.peer[0]:
sock = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
else:
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 main():
playlist = os.path.expanduser('~/Videos/cdosea.m3u')
prefix = os.path.expanduser('~/Videos/CDOSEA')
parser = argparse.ArgumentParser(description='play 2 screens in sync')
parser.add_argument('--peer', help='ip of peer', required=True)
parser.add_argument('--peer-port', help='port of peer', default=DEFAULT_PORT)
parser.add_argument('--port', type=int, help='local port', default=DEFAULT_PORT)
parser.add_argument('--playlist', help='play.m3u', default=playlist)
parser.add_argument('--prefix', help='video location', default=prefix)
parser.add_argument('--window', action='store_true', help='run in window', default=False)
parser.add_argument('--debug', action='store_true', help='debug', default=False)
args = parser.parse_args()
peer = (args.peer, args.peer_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)
player = get_player(fullscreen=not args.window)
if ':' in args.peer:
listen = '::'
ThreadingUDPServer.address_family = socket.AF_INET6
else:
listen = '0.0.0.0'
server = ThreadingUDPServer((listen, args.port), Handler)
server.allow_reuse_address = True
server.player = Player(peer, player, args.playlist, args.prefix, server)
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()