playsync
This commit is contained in:
parent
1c0ea9c620
commit
3f20dbd968
2 changed files with 95 additions and 28 deletions
|
@ -5,6 +5,7 @@ import os
|
||||||
import random
|
import random
|
||||||
import socket
|
import socket
|
||||||
import string
|
import string
|
||||||
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from glob import glob
|
from glob import glob
|
||||||
|
@ -18,15 +19,12 @@ import mpv
|
||||||
import logging
|
import logging
|
||||||
logger = logging.getLogger('cdosea')
|
logger = logging.getLogger('cdosea')
|
||||||
|
|
||||||
DEFAULT_PORT=2680
|
DEFAULT_PORT = 2680
|
||||||
DEBUG=False
|
DEBUG = False
|
||||||
|
|
||||||
def update_playlist(playlist, prefix='video/'):
|
def update_playlist(playlist, prefix='video/', position=None):
|
||||||
playlist = 'play.m3u'
|
playlist = 'play.m3u'
|
||||||
|
|
||||||
today = datetime.date.today()
|
|
||||||
seconds_since_midnight = time.time() - time.mktime(today.timetuple())
|
|
||||||
|
|
||||||
files = []
|
files = []
|
||||||
videos = {}
|
videos = {}
|
||||||
for letter in string.ascii_uppercase:
|
for letter in string.ascii_uppercase:
|
||||||
|
@ -37,45 +35,76 @@ def update_playlist(playlist, prefix='video/'):
|
||||||
for letter in string.ascii_uppercase:
|
for letter in string.ascii_uppercase:
|
||||||
files.append(videos[letter][i])
|
files.append(videos[letter][i])
|
||||||
|
|
||||||
position = 0
|
if position is None:
|
||||||
while position < seconds_since_midnight:
|
today = datetime.date.today()
|
||||||
f = files.pop(0)
|
seconds_since_midnight = time.time() - time.mktime(today.timetuple())
|
||||||
try:
|
offset = 0
|
||||||
position += ox.avinfo(f)['duration']
|
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)
|
files.append(f)
|
||||||
except:
|
pos -= 1
|
||||||
pass
|
|
||||||
|
|
||||||
with open(playlist, 'w') as f:
|
with open(playlist, 'w') as f:
|
||||||
f.write('\n'.join(files))
|
f.write('\n'.join(files))
|
||||||
|
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)
|
||||||
|
|
||||||
class ThreadingUDPServer(ThreadingMixIn, UDPServer):
|
class ThreadingUDPServer(ThreadingMixIn, UDPServer):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
def q_binding(self, *args):
|
||||||
|
self.player.running = False
|
||||||
|
self.shutdown()
|
||||||
|
|
||||||
class Handler(BaseRequestHandler):
|
class Handler(BaseRequestHandler):
|
||||||
|
|
||||||
def handle(self):
|
def handle(self):
|
||||||
data = self.request[0].strip()
|
data = self.request[0].strip()
|
||||||
socket = self.request[1]
|
logger.debug("%s said: %s", self.client_address[0], data)
|
||||||
logger.debug("%s wrote: %s", self.client_address[0], data)
|
queue = False
|
||||||
if self.server.player.playing:
|
if data == b'POS':
|
||||||
response = b'PLAYING'
|
response = ('%d' % self.server.player.position).encode()
|
||||||
else:
|
else:
|
||||||
response = b'WAITING'
|
if self.server.player.playing:
|
||||||
|
response = b'PLAYING'
|
||||||
|
else:
|
||||||
|
response = b'WAITING'
|
||||||
|
queue = True
|
||||||
|
socket = self.request[1]
|
||||||
socket.sendto(response, self.client_address)
|
socket.sendto(response, self.client_address)
|
||||||
self.server.player.queue.put('')
|
if queue:
|
||||||
|
self.server.player.queue.put('')
|
||||||
|
|
||||||
class Player(Thread):
|
class Player(Thread):
|
||||||
first = True
|
first = True
|
||||||
running = True
|
running = True
|
||||||
playing = False
|
playing = False
|
||||||
current = ''
|
current = ''
|
||||||
|
playlist_items = 260
|
||||||
|
|
||||||
def __init__(self, peer, player, playlist):
|
def __init__(self, peer, player, playlist, lights=False):
|
||||||
self.peer = peer
|
self.peer = peer
|
||||||
self.playlist = playlist
|
self.playlist = playlist
|
||||||
self.player = player
|
self.player = player
|
||||||
|
self.lights = lights
|
||||||
self.queue = Queue()
|
self.queue = Queue()
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
self.daemon = True
|
self.daemon = True
|
||||||
|
@ -83,13 +112,14 @@ class Player(Thread):
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
logger.debug("update playlist")
|
logger.debug("update playlist")
|
||||||
update_playlist(self.playlist)
|
self.position, self.playlist_items = update_playlist(self.playlist, position=self.get_position())
|
||||||
logger.debug("load playlist")
|
logger.debug("load playlist")
|
||||||
self.player.loadlist(self.playlist)
|
self.player.loadlist(self.playlist)
|
||||||
self.player.pause = True
|
self.player.pause = True
|
||||||
while self.running:
|
while self.running:
|
||||||
logger.debug("play")
|
logger.debug("play")
|
||||||
self.play()
|
self.play()
|
||||||
|
self.position = (self.position + 1) % self.playlist_items
|
||||||
logger.debug("ping")
|
logger.debug("ping")
|
||||||
self.ping()
|
self.ping()
|
||||||
logger.debug("wait")
|
logger.debug("wait")
|
||||||
|
@ -98,6 +128,8 @@ class Player(Thread):
|
||||||
def play(self):
|
def play(self):
|
||||||
self.playing = True
|
self.playing = True
|
||||||
self.player.pause = False
|
self.player.pause = False
|
||||||
|
if self.lights:
|
||||||
|
trigger_lights(self.player.path.decode())
|
||||||
self.player.wait_for_playback()
|
self.player.wait_for_playback()
|
||||||
self.player.pause = True
|
self.player.pause = True
|
||||||
self.playing = False
|
self.playing = False
|
||||||
|
@ -115,6 +147,19 @@ class Player(Thread):
|
||||||
logger.debug("Sent: %s", data)
|
logger.debug("Sent: %s", data)
|
||||||
logger.debug("Received: %s", received)
|
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):
|
def my_log(loglevel, component, message):
|
||||||
logger.debug('[{}] {}: {}'.format(loglevel, component, message))
|
logger.debug('[{}] {}: {}'.format(loglevel, component, message))
|
||||||
|
@ -126,6 +171,7 @@ def main():
|
||||||
parser.add_argument('--playlist', help='play.m3u', default='play.m3u')
|
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('--window', action='store_true', help='run in window', default=False)
|
||||||
parser.add_argument('--debug', action='store_true', help='debug', 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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if ':' in args.peer:
|
if ':' in args.peer:
|
||||||
|
@ -138,14 +184,21 @@ def main():
|
||||||
if DEBUG:
|
if DEBUG:
|
||||||
log_format = '%(asctime)s:%(levelname)s:%(name)s:%(message)s'
|
log_format = '%(asctime)s:%(levelname)s:%(name)s:%(message)s'
|
||||||
logging.basicConfig(level=logging.DEBUG, format=log_format)
|
logging.basicConfig(level=logging.DEBUG, format=log_format)
|
||||||
base = os.path.dirname(os.path.abspath(__file__))
|
base = os.path.dirname(os.path.abspath(__file__))
|
||||||
os.chdir(base)
|
os.chdir(base)
|
||||||
|
if args.lights:
|
||||||
|
time.sleep(5)
|
||||||
|
|
||||||
player = mpv.MPV(log_handler=my_log, input_default_bindings=True, input_vo_keyboard=True)
|
player = mpv.MPV(
|
||||||
|
log_handler=my_log, input_default_bindings=True,
|
||||||
|
input_vo_keyboard=True, sub_text_font_size=28
|
||||||
|
)
|
||||||
player.fullscreen = not args.window
|
player.fullscreen = not args.window
|
||||||
player.loop = 'inf'
|
player.loop = 'inf'
|
||||||
|
|
||||||
server = ThreadingUDPServer(('0.0.0.0', args.port), Handler)
|
server = ThreadingUDPServer(('0.0.0.0', args.port), Handler)
|
||||||
server.player = Player(peer, player, args.playlist)
|
server.player = Player(peer, player, args.playlist, args.lights)
|
||||||
|
player.register_key_binding('q', server.q_binding)
|
||||||
|
|
||||||
logger.debug("listen on %s...", args.port)
|
logger.debug("listen on %s...", args.port)
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -11,11 +11,20 @@ import ox
|
||||||
|
|
||||||
socket.setdefaulttimeout(10)
|
socket.setdefaulttimeout(10)
|
||||||
|
|
||||||
base = 'https://cdosea.0x2620.org/static/render'
|
base = 'https://cdosea.0x2620.org/static/render/'
|
||||||
lock = '/tmp/update.lock'
|
lock = '/tmp/update.lock'
|
||||||
folder = os.path.abspath(os.path.dirname(__file__))
|
folder = os.path.abspath(os.path.dirname(__file__))
|
||||||
os.chdir(folder)
|
os.chdir(folder)
|
||||||
|
|
||||||
|
def get_subtitle(url, name):
|
||||||
|
try:
|
||||||
|
r = requests.get(url, stream=True)
|
||||||
|
with open('%s.tmp' % name, 'wb') as fd:
|
||||||
|
shutil.copyfileobj(r.raw, fd)
|
||||||
|
shutil.move('%s.tmp' % name, name)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
if os.path.exists(lock):
|
if os.path.exists(lock):
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
with open(lock, 'w') as f:
|
with open(lock, 'w') as f:
|
||||||
|
@ -24,7 +33,7 @@ with open(lock, 'w') as f:
|
||||||
for i in range(10):
|
for i in range(10):
|
||||||
for letter in string.ascii_lowercase:
|
for letter in string.ascii_lowercase:
|
||||||
name = '%s%02d.1080p.mp4' % (letter, i)
|
name = '%s%02d.1080p.mp4' % (letter, i)
|
||||||
url = '%s/%s' % (base, name)
|
url = '%s%s' % (base, name)
|
||||||
name = 'video/' + name
|
name = 'video/' + name
|
||||||
folder = os.path.dirname(name)
|
folder = os.path.dirname(name)
|
||||||
if not os.path.exists(folder):
|
if not os.path.exists(folder):
|
||||||
|
@ -36,6 +45,7 @@ for i in range(10):
|
||||||
else:
|
else:
|
||||||
get = True
|
get = True
|
||||||
if get:
|
if get:
|
||||||
|
print(url)
|
||||||
try:
|
try:
|
||||||
r = requests.get(url, stream=True)
|
r = requests.get(url, stream=True)
|
||||||
with open('%s.tmp' % name, 'wb') as fd:
|
with open('%s.tmp' % name, 'wb') as fd:
|
||||||
|
@ -45,6 +55,10 @@ for i in range(10):
|
||||||
mtime = time.mktime(datetime.datetime.strptime(r.headers.get("Last-Modified"), "%a, %d %b %Y %X GMT").timetuple())
|
mtime = time.mktime(datetime.datetime.strptime(r.headers.get("Last-Modified"), "%a, %d %b %Y %X GMT").timetuple())
|
||||||
os.utime(name, (mtime, mtime))
|
os.utime(name, (mtime, mtime))
|
||||||
ox.avinfo(name)['duration']
|
ox.avinfo(name)['duration']
|
||||||
|
get_subtitle(url.replace('.mp4', '.srt'), name.replace('.mp4', '.srt'))
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
os.unlink(lock)
|
||||||
|
sys.exit(-1)
|
||||||
except:
|
except:
|
||||||
print(url, 'failed')
|
print(url, 'failed')
|
||||||
os.unlink(lock)
|
os.unlink(lock)
|
||||||
|
|
Loading…
Reference in a new issue