review: allow manual start selection

* return start video
* use ubuntu instead of alpine because ffmpeg in alpine lacks drawtext support
* only cache if inputs are exactly the same, preventing accidents (prev scheme too weak)
* fix a bug where end range is wrong if start_range doesn't start at 0
	conceptually, this was a more serious bug as time_offset was wrong, but luckily the bug applied
	equally to both racers so there was no net effect
condor-scripts
Mike Lang 3 years ago
parent d37cca52c9
commit 345cb53f4e

@ -1,7 +1,6 @@
FROM alpine:3.7
FROM ubuntu:18.04
# dependencies needed for compiling c extensions
# also busybox-extras for telnet for easier use of backdoor
RUN apk --update add py2-pip gcc python-dev musl-dev file make busybox-extras
RUN chmod 1777 /tmp && apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --yes python-pip python-dev file gcc make ffmpeg
# Install gevent so that we don't need to re-install it when common changes
RUN pip install gevent
@ -11,7 +10,6 @@ COPY common /tmp/common
RUN pip install /tmp/common && rm -r /tmp/common
# Install actual application
RUN apk add ffmpeg
COPY restreamer /tmp/restreamer
RUN pip install /tmp/restreamer && rm -r /tmp/restreamer

@ -365,20 +365,32 @@ def review_race(match_id, race_number):
race start time to look for the start signal. Default 0,5.
finish_range: As start_range, but how long to make the final review video before/after
the nominal duration. Default -5,10.
racer1_start, racer2_start: Explicit start times, as float.
"""
if app.condor_db is None:
return "Reviews are disabled", 501
start_range = map(float, request.args.get('start_range', '0,5').split(','))
finish_range = map(float, request.args.get('finish_range', '-5,10').split(','))
racer1_start = float(request.args['racer1_start']) if 'racer1_start' in request.args else None
racer2_start = float(request.args['racer2_start']) if 'racer2_start' in request.args else None
try:
review_path = review(match_id, race_number, app.static_folder, app.condor_db, start_range, finish_range)
review_path = review(
match_id, race_number, app.static_folder, app.condor_db, start_range, finish_range,
racer1_start, racer2_start,
)
except RaceNotFound as e:
return str(e), 404
except NoSegments:
logging.warning("Failed review due to no segments", exc_info=True)
return "Video content is missing - cannot review automatically", 400
except CantFindStart as e:
return str(e), 400
return (
"{}\n"
"Please check start video and adjust start_range or set racer{}_start: {}\n"
"Note timestamps in that video are only valid for the current start_range.\n"
).format(
e, e.racer_number, os.path.join(app.static_url_path, os.path.relpath(e.path, app.static_folder))
), 400
relative_path = os.path.relpath(review_path, app.static_folder)
review_url = os.path.join(app.static_url_path, relative_path)

@ -3,6 +3,7 @@ import datetime
import logging
import os
import subprocess
from hashlib import sha256
from urlparse import urlparse
from uuid import uuid4
@ -21,14 +22,16 @@ class RaceNotFound(Exception):
class CantFindStart(Exception):
def __init__(self, racer, found):
def __init__(self, racer, racer_number, found, path):
self.racer = racer
self.racer_number = racer_number
self.found = found
self.path = path
def __str__(self):
if self.found > 0:
return "Found multiple ({}) possible start points for {}".format(self.found, self.racer)
return "Found multiple ({}) possible start points for racer {} ({})".format(self.found, self.racer_number, self.racer)
else:
return "Failed to find start point for {}".format(self.racer)
return "Failed to find start point for racer {} ({})".format(self.racer_number, self.racer)
def ts(dt):
@ -77,7 +80,10 @@ def conn_from_url(url):
)
def review(match_id, race_number, base_dir, db_url, start_range=(0, 5), finish_range=(-5, 10)):
def review(
match_id, race_number, base_dir, db_url, start_range=(0, 5), finish_range=(-5, 10),
racer1_start=None, racer2_start=None,
):
logger = logging.getLogger("review").getChild("{}-{}".format(match_id, race_number))
conn = conn_from_url(db_url)
@ -111,11 +117,15 @@ def review(match_id, race_number, base_dir, db_url, start_range=(0, 5), finish_r
(racer1, racer2, start, duration), = data
end = start + datetime.timedelta(seconds=duration/100.)
# cache hash encapsulates all input args
cache_hash = sha256(str((match_id, race_number, start_range, finish_range, racer1_start, racer2_start)))
cache_str = cache_hash.digest().encode('base64')[:12]
output_name = "{}-{}-{}-{}".format(match_id, racer1, racer2, race_number)
output_dir = os.path.join(base_dir, "reviews", output_name)
if not os.path.exists(output_dir):
os.makedirs(output_dir)
result_name = "review_{}_{}.mp4".format(*finish_range)
result_name = "review_{}.mp4".format(cache_str)
result_path = os.path.join(output_dir, result_name)
if os.path.exists(result_path):
logger.info("Result already exists for {}, reusing".format(result_path))
@ -123,10 +133,12 @@ def review(match_id, race_number, base_dir, db_url, start_range=(0, 5), finish_r
finish_paths = []
for racer_number, racer in enumerate((racer1, racer2)):
for racer_index, (racer, time_offset) in enumerate(((racer1, racer1_start), (racer2, racer2_start))):
nonce = str(uuid4())
start_path = os.path.join(output_dir, "start-{}-{}.mp4".format(racer_number, nonce))
racer_number = racer_index + 1
if time_offset is None:
start_path = os.path.join(output_dir, "start-{}-{}.mp4".format(racer_number, nonce))
logger.info("Cutting start for racer {} ({})".format(racer_number, racer))
start_start, start_end = add_range(start, start_range)
cut_to_file(logger, start_path, base_dir, racer, start_start, start_end)
@ -153,9 +165,10 @@ def review(match_id, race_number, base_dir, db_url, start_range=(0, 5), finish_r
time_offset = float(black_end.split(':')[1])
else:
found = len(lines)
logger.warning("Unable to detect start (expected 1 black interval, but found {})".format(found))
raise CantFindStart(racer, found)
time_offset = datetime.timedelta(seconds=time_offset)
logger.warning("Unable to detect start (expected 1 black interval, but found {}), re-cutting with timestamps".format(found))
cut_to_file(logger, start_path, base_dir, racer, start_start, start_end, frame_counter=True)
raise CantFindStart(racer, racer_number, found, start_path)
time_offset = datetime.timedelta(seconds=time_offset - start_range[0])
# start each racer's finish video at TIME_OFFSET later, so they are the same
# time since their actual start.

Loading…
Cancel
Save