#!/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()