Compare commits

..

4 commits

Author SHA1 Message Date
j
01f669b61d better way to calculate remove 2024-03-19 11:48:36 +01:00
j
a229194d48 actually cleanup, only remove renders in infinity range 2024-03-19 10:12:15 +01:00
j
3d5293e7de lower priority 2024-03-19 10:11:40 +01:00
j
6e4e8ca7a6 to infinity 2024-03-10 18:14:59 +00:00
4 changed files with 156 additions and 10 deletions

View file

@ -0,0 +1,15 @@
[Unit]
Description=render to infinity and beyond
After=pandora.service
[Service]
Type=simple
Restart=always
User=pandora
Group=pandora
Nice=19
WorkingDirectory=/srv/pandora/pandora
ExecStart=/srv/pandora/pandora/manage.py infinity
[Install]
WantedBy=multi-user.target

View file

@ -0,0 +1,19 @@
import json
import os
import subprocess
from django.core.management.base import BaseCommand
from django.conf import settings
from ...render import render_infinity
class Command(BaseCommand):
help = 'render infinity'
def add_arguments(self, parser):
parser.add_argument('--prefix', action='store', dest='prefix', default="/srv/t_for_time", help='prefix to build clips in')
parser.add_argument('--duration', action='store', dest='duration', default="3600", help='target duration of all fragments in seconds')
def handle(self, **options):
render_infinity(options)

View file

@ -1,8 +1,9 @@
#!/usr/bin/python3 #!/usr/bin/python3
import argparse import argparse
import collections
import json
import os import os
import socket import socket
import collections
import time import time
from threading import Thread from threading import Thread
from datetime import datetime from datetime import datetime
@ -57,6 +58,15 @@ class Sync(Thread):
if self.is_main: if self.is_main:
self.socket_enable_broadcast() self.socket_enable_broadcast()
if mpv.MPV_VERSION >= (2, 2):
self.mpv = mpv.MPV(
log_handler=mpv_log, input_default_bindings=True,
input_vo_keyboard=True,
sub_font_size=FONT_SIZE, sub_font=FONT,
sub_border_size=FONT_BORDER,
sub_margin_y=SUB_MARGIN,
)
else:
self.mpv = mpv.MPV( self.mpv = mpv.MPV(
log_handler=mpv_log, input_default_bindings=True, log_handler=mpv_log, input_default_bindings=True,
input_vo_keyboard=True, input_vo_keyboard=True,
@ -73,6 +83,7 @@ class Sync(Thread):
self.playlist_mtime = os.stat(self.playlist).st_mtime self.playlist_mtime = os.stat(self.playlist).st_mtime
self.mpv.loadlist(self.playlist) self.mpv.loadlist(self.playlist)
logger.error("loaded paylist: %s", self.playlist) logger.error("loaded paylist: %s", self.playlist)
logger.debug("current playlist: %s", json.dumps(self.mpv.playlist, indent=2))
self.deviations = collections.deque(maxlen=10) self.deviations = collections.deque(maxlen=10)
if not self.is_main: if not self.is_main:
self.mpv.pause = False self.mpv.pause = False
@ -135,8 +146,33 @@ class Sync(Thread):
playlist_mtime = os.stat(self.playlist).st_mtime playlist_mtime = os.stat(self.playlist).st_mtime
if self.playlist_mtime != playlist_mtime: if self.playlist_mtime != playlist_mtime:
self.playlist_mtime = playlist_mtime self.playlist_mtime = playlist_mtime
self.mpv.loadlist(self.playlist) #self.mpv.loadlist(self.playlist)
with open(self.playlist) as fd:
items = fd.read().strip().split('\n')
base = os.path.dirname(self.playlist)
items = [os.path.join(base, item) for item in items]
current_items = self.mpv.playlist_filenames
for filename in items:
if filename not in current_items:
self.mpv.playlist_append(filename)
logger.error("add: %s", filename)
remove = []
for filename in current_items:
if filename not in items:
remove.append(filename)
for filename in remove:
for idx, item in enumerate(self.mpv.playlist):
if item["filename"] == filename:
logger.error("remove: %s %s", idx, filename)
self.mpv.playlist_remove(idx)
break
for idx, filename in enumerate(items):
current_idx = self.mpv.playlist_filenames.index(filename)
if idx != current_idx:
logger.error("move item %s %s -> %s", filename, current_idx, idx)
self.mpv.playlist_move(current_idx, idx)
logger.error("reloaded paylist: %s", self.playlist) logger.error("reloaded paylist: %s", self.playlist)
logger.debug("current playlist: %s", json.dumps(self.mpv.playlist, indent=2))
def init_socket(self): def init_socket(self):
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0) sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, 0)
@ -261,7 +297,7 @@ def main():
prefix = os.path.expanduser('~/Videos/t_for_time') prefix = os.path.expanduser('~/Videos/t_for_time')
parser = argparse.ArgumentParser(description='t_for_time sync player') parser = argparse.ArgumentParser(description='t_for_time sync player')
parser.add_argument('--mode', help='ip of peer', default="peer") parser.add_argument('--mode', help='peer or main', default="peer")
parser.add_argument('--playlist', default='/srv/t_for_time/render/128/front.m3u', help="m3u") parser.add_argument('--playlist', default='/srv/t_for_time/render/128/front.m3u', help="m3u")
parser.add_argument('--prefix', help='video location', default=prefix) 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('--window', action='store_true', help='run in window', default=False)

View file

@ -1,5 +1,6 @@
#!/usr/bin/python3 #!/usr/bin/python3
from collections import defaultdict from collections import defaultdict
from glob import glob
import json import json
import os import os
import re import re
@ -307,6 +308,16 @@ def get_scene_duration(scene):
duration += clip['duration'] duration += clip['duration']
return duration return duration
def get_offset_duration(prefix):
duration = 0
for root, folders, files in os.walk(prefix):
for f in files:
if f == 'scene.json':
path = os.path.join(root, f)
scene = json.load(open(path))
duration += get_scene_duration(scene)
return duration
def render(root, scene, prefix=''): def render(root, scene, prefix=''):
fps = 24 fps = 24
files = [] files = []
@ -609,3 +620,68 @@ def update_subtitles(options):
srt = ox.srt.encode(subs) srt = ox.srt.encode(subs)
write_if_new(str(path), srt, 'b') write_if_new(str(path), srt, 'b')
def update_m3u(render_prefix, exclude=[]):
files = ox.sorted_strings(glob(render_prefix + "*/*/back.mp4"))
for ex in exclude:
files = [f for f in files if not f.startswith(ex + "/")]
back_m3u = "\n".join(files)
back_m3u = back_m3u.replace(render_prefix, "")
front_m3u = back_m3u.replace("back.mp4", "front.mp4")
back_m3u_f = render_prefix + "back.m3u"
front_m3u_f = render_prefix + "front.m3u"
with open(back_m3u_f + "_", "w") as fd:
fd.write(back_m3u)
with open(front_m3u_f + "_", "w") as fd:
fd.write(front_m3u)
shutil.move(front_m3u_f + "_", front_m3u_f)
cmd = ["scp", front_m3u_f, "front:" + front_m3u_f]
subprocess.check_call(cmd)
shutil.move(back_m3u_f + "_", back_m3u_f)
def render_infinity(options):
prefix = options['prefix']
duration = int(options['duration'])
state_f = os.path.join(prefix, "infinity.json")
if os.path.exists(state_f):
with open(state_f) as fd:
state = json.load(fd)
else:
state = {
"offset": 100,
"max-items": 30,
"no_video": False,
}
for key in ("prefix", "duration"):
state[key] = options[key]
while True:
render_prefix = state["prefix"] + "/render/"
current = [
f for f in os.listdir(render_prefix)
if f.isdigit() and os.path.isdir(render_prefix + f) and state["offset"] > int(f) >= 100
]
if len(current) > state["max-items"]:
current = ox.sorted_strings(current)
remove = current[:-state["max-items"]]
update_m3u(render_prefix, exclude=remove)
for folder in remove:
folder = render_prefix + folder
print("remove", folder)
shutil.rmtree(folder)
cmd = ["ssh", "front", "rm", "-rf", folder]
#print(cmd)
subprocess.check_call(cmd)
render_all(state)
path = "%s%s/" % (render_prefix, state["offset"])
cmd = ['rsync', '-a', path, "front:" + path]
subprocess.check_call(cmd)
update_m3u(render_prefix)
state["offset"] += 1
with open(state_f + "~", "w") as fd:
json.dump(state, fd, indent=2)
shutil.move(state_f + "~", state_f)