#!/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 mpv_log, update_playlist import logging logger = logging.getLogger('cdosea') DEFAULT_PORT = 2680 DEBUG = False 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, prefix, lights=False): self.peer = peer self.playlist = playlist self.prefix = prefix 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, self.prefix, 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 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[: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=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) 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) player = get_player(fullscreen=not args.window) server = ThreadingUDPServer(('0.0.0.0', args.port), Handler) server.player = Player(peer, player, args.playlist, args.prefix, 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()