Compare commits

...

11 Commits

Author SHA1 Message Date
Mike Lang 55f48e1881 cutter: Add unlisted-only safety flag to youtube upload backend 1 month ago
Mike Lang 12150a4005 thrimshim: Return list of available transitions
For use in thrimbletrimmer for a drop-down.
1 month ago
Mike Lang d4de1f94be Add descriptions to xfade transitions 1 month ago
Mike Lang 1dec53924f fix typo 1 month ago
Mike Lang 91511295c0 Fix poster moment tag 1 month ago
Mike Lang 25e5e933b5 Fix typo 1 month ago
Mike Lang 7d89569ead Code more defensively around out-of-order or missing parent ids 1 month ago
Mike Lang 760dbd1e07 sheetsync: Convey row parent info
by prefixing with a number of ^ characters and noting the parent id in the notes column.
1 month ago
Mike Lang 23960d947b streamlog: implicit tags must be first 1 month ago
Mike Lang 664f98150f sheets implicit tags fix 1 month ago
Mike Lang 62491d119f debugging 1 month ago

@ -25,87 +25,93 @@ from .fixts import FixTS
# These are the set of transition names from the ffmpeg xfade filter that we allow.
# This is mainly here to prevent someone putting in arbitrary strings and causing weird problems.
KNOWN_XFADE_TRANSITIONS = [
"fade",
"wipeleft",
"wiperight",
"wipeup",
"wipedown",
"slideleft",
"slideright",
"slideup",
"slidedown",
"circlecrop",
"rectcrop",
"distance",
"fadeblack",
"fadewhite",
"radial",
"smoothleft",
"smoothright",
"smoothup",
"smoothdown",
"circleopen",
"circleclose",
"vertopen",
"vertclose",
"horzopen",
"horzclose",
"dissolve",
"pixelize",
"diagtl",
"diagtr",
"diagbl",
"diagbr",
"hlslice",
"hrslice",
"vuslice",
"vdslice",
"hblur",
"fadegrays",
"wipetl",
"wipetr",
"wipebl",
"wipebr",
"squeezeh",
"squeezev",
"zoomin",
"fadefast",
"fadeslow",
"hlwind",
"hrwind",
"vuwind",
"vdwind",
"coverleft",
"coverright",
"coverup",
"coverdown",
"revealleft",
"revealright",
"revealup",
"revealdown",
]
# This is mainly here to prevent someone putting in arbitrary strings and causing weird problems,
# and to provide descriptions.
# See https://trac.ffmpeg.org/wiki/Xfade for examples.
KNOWN_XFADE_TRANSITIONS = {
"fade": "A simple cross-fade.",
"fadeblack": "A fade to black then to the new video.",
"fadewhite": "A fade to white then fade to the new video.",
"fadegrays": "The old video fades to grayscale then to the new video.",
"wipeleft": "A wipe from right to left.",
"wiperight": "A wipe from left to right.",
"wipeup": "A wipe from bottom to top.",
"wipedown": "A wipe from top to bottom.",
"slideleft": "The old video slides left as the new video comes in from the right.",
"slideright": "The old video slides right as the new video comes in from the left.",
"slideup": "The old video slides up as the new video comes in from the bottom.",
"slidedown": "The old video slides down as the new video comes in from the top.",
"circlecrop": "Circular black mask comes in from edges to center, then out again with new video.",
"rectcrop": "Rectangular black mask comes in from edges to center, then out again with new video.",
"distance": "???",
"radial": "Similar to clock wipe, but with a cross-fading line.",
"smoothleft": "Similar to wipe left, but with a cross-fading line.",
"smoothright": "Similar to wipe right, but with a cross-fading line.",
"smoothup": "Similar to wipe up, but with a cross-fading line.",
"smoothdown": "Similar to wipe down, but with a cross-fading line.",
"circleopen": "Circular wipe from outside in, with a cross-fading line.",
"circleclose": "Circular wipe from inside out, with a cross-fading line.",
"vertopen": "Wipe from center to either side, with a cross-fading line.",
"vertclose": "Wipe from either side to center, with a cross-fading line.",
"horzopen": "Wipe from center to top and bottom, with a cross-fading line.",
"horzclose": "Wipe from top and bottom to center, with a cross-fading line.",
"dissolve": "Similar to a fade, but each pixel changes instantly, more pixels change over time.",
"pixelize": "Pixelates the image, switches to new video, then unpixelates.",
# TODO the rest later
"diagtl": "",
"diagtr": "",
"diagbl": "",
"diagbr": "",
"hlslice": "",
"hrslice": "",
"vuslice": "",
"vdslice": "",
"hblur": "",
"wipetl": "",
"wipetr": "",
"wipebl": "",
"wipebr": "",
"squeezeh": "",
"squeezev": "",
"zoomin": "",
"hlwind": "",
"hrwind": "",
"vuwind": "",
"vdwind": "",
"coverleft": "",
"coverright": "",
"coverup": "",
"coverdown": "",
"revealleft": "",
"revealright": "",
"revealup": "",
"revealdown": "",
"fadefast": "???",
"fadeslow": "???",
}
# These are custom transitions implemented using xfade's custom transition support.
# It maps from name to the "expr" value to use.
# It maps from name to (description, expr).
# In these expressions:
# X and Y are pixel coordinates
# A and B are the old and new video's pixel values
# W and H are screen width and height
# P is a "progress" number from 0 to 1 that increases over the course of the wipe
CUSTOM_XFADE_TRANSITIONS = {
# A clock wipe is a 360 degree clockwise sweep around the center of the screen, starting at the top.
# It is intended to mimic an analog clock and insinuate a passing of time.
# It is implemented by calculating the angle of the point off a center line (using atan2())
# then using the new video if progress > that angle (normalized to 0-1).
"clockwipe": "if(lt((1-atan2(W/2-X,Y-H/2)/PI) / 2, P), A, B)",
# The classic star wipe is an expanding 5-pointed star from the center.
# It's mostly a meme.
# It is implemented by converting to polar coordinates (distance and angle off center),
# then comparing distance to a star formula derived from here: https://math.stackexchange.com/questions/4293250/how-to-write-a-polar-equation-for-a-five-pointed-star
# Made by SenseAmidstMadness.
"starwipe": "if(lt(sqrt(pow(X-W/2,2)+pow(Y-H/2,2))/sqrt(pow(W/2,2)+pow(H/2,2)),pow((1-P),2)*(0.75)*1/cos((2*asin(cos(5*(atan2(Y-H/2,X-W/2)+PI/2)))+PI*3)/(10))), B, A)",
"clockwipe": (
"A 360 degree clockwise sweep around the center of the screen, starting at the top.\n"
"Intended to mimic an analog clock and insinuate a passing of time.",
# Implemented by calculating the angle of the point off a center line (using atan2())
# then using the new video if progress > that angle (normalized to 0-1).
"if(lt((1-atan2(W/2-X,Y-H/2)/PI) / 2, P), A, B)",
),
"starwipe": (
"Wipe using an expanding 5-pointed star from the center. Mostly a meme.",
# Implemented by converting to polar coordinates (distance and angle off center),
# then comparing distance to a star formula derived from here: https://math.stackexchange.com/questions/4293250/how-to-write-a-polar-equation-for-a-five-pointed-star
# Made by SenseAmidstMadness.
"if(lt(sqrt(pow(X-W/2,2)+pow(Y-H/2,2))/sqrt(pow(W/2,2)+pow(H/2,2)),pow((1-P),2)*(0.75)*1/cos((2*asin(cos(5*(atan2(Y-H/2,X-W/2)+PI/2)))+PI*3)/(10))), B, A)",
),
}
@ -505,7 +511,8 @@ def ffmpeg_cut_transition(prev_segments, next_segments, video_type, duration, of
}
if video_type in CUSTOM_XFADE_TRANSITIONS:
xfade_kwargs["transition"] = "custom"
xfade_kwargs["expr"] = f"'{CUSTOM_XFADE_TRANSITIONS[video_type]}'" # wrap in '' for quoting
description, expr = CUSTOM_XFADE_TRANSITIONS[video_type]
xfade_kwargs["expr"] = f"'{expr}'" # wrap in '' for quoting
elif video_type in KNOWN_XFADE_TRANSITIONS:
xfade_kwargs["transition"] = video_type
else:

@ -121,6 +121,8 @@ class Youtube(UploadBackend):
to make much difference.
mime_type: You must set this to the correct mime type for the encoded video.
Default is video/MP2T, suitable for fast cuts or -f mpegts.
unlisted_only: If set to true, rejects any videos which are set to be public.
Intended as a safety feature when testing.
"""
needs_transcode = True
@ -138,7 +140,7 @@ class Youtube(UploadBackend):
]
def __init__(self, credentials, category_id=23, language="en", use_yt_recommended_encoding=False,
mime_type='video/MP2T'):
mime_type='video/MP2T', unlisted_only=False):
self.logger = logging.getLogger(type(self).__name__)
self.client = GoogleAPIClient(
credentials['client_id'],
@ -148,11 +150,14 @@ class Youtube(UploadBackend):
self.category_id = category_id
self.language = language
self.mime_type = mime_type
self.unlisted_only = unlisted_only
if use_yt_recommended_encoding:
self.encoding_settings = self.recommended_settings
self.encoding_streamable = False
def upload_video(self, title, description, tags, public, data):
if public and self.unlisted_only:
raise UploadError("Public video but unlisted-only was requested")
json = {
'snippet': {
'title': title,

@ -361,7 +361,7 @@ class SheetsEventsMiddleware(SheetsMiddleware):
# Undo the implicitly added tags
if key == "tags":
value = value[2:]
if row["poster_moment"]:
if row.get("poster_moment"):
value = value[1:]
return super().write_value(row, key, value)

@ -126,8 +126,20 @@ class StreamLogEventsMiddleware(Middleware):
def get_rows(self):
all_rows = []
for row in self.client.get_entries()["event_log"]:
row = self.parse_row(row)
entries_by_id = {}
for entry in self.client.get_entries()["event_log"]:
# Keep track of how many levels of sub-entry each entry is
entries_by_id[entry["id"]] = entry
parent = entry["parent"]
if parent is None:
entry["depth"] = 0
elif parent in entries_by_id:
entry["depth"] = entries_by_id[parent]["depth"] + 1
else:
logging.warning(f"Entry {entry['id']} has unknown or out-of-order parent {parent}")
entry["depth"] = 0
row = self.parse_row(entry)
# Malformed rows can be skipped, represented as a None result
if row is not None:
all_rows.append(row)
@ -150,12 +162,19 @@ class StreamLogEventsMiddleware(Middleware):
output["sheet_name"] = row["tab"]["name"] if row["tab"] else "unknown"
# Implicit tags
output['tags'] += [
implicit_tags = [
output['category'],
output["sheet_name"],
]
if output["poster_moment"]:
output['tags'] += 'Poster Moment'
implicit_tags.append('Poster Moment')
output['tags'] = implicit_tags + output['tags']
# Convey parent info
if row["depth"] > 0:
output["description"] = "^" * row["depth"] + " " + output["description"]
pre_note = f"Part of event {row['parent']}"
output["notes"] = f"{pre_note}. {output['notes']}" if output["notes"] else pre_note
return output

@ -12,12 +12,12 @@ import gevent
import gevent.backdoor
from gevent.pywsgi import WSGIServer
import prometheus_client
import psycopg2
from psycopg2 import sql
import common
from common import database, dateutil
from common.flask_stats import request_stats, after_request
from common.segments import KNOWN_XFADE_TRANSITIONS, CUSTOM_XFADE_TRANSITIONS
import google.oauth2.id_token
import google.auth.transport.requests
@ -144,6 +144,21 @@ def get_defaults():
})
@app.route('/thrimshim/transitions')
@request_stats
def get_transitions():
"""Get info on available transitions. Returns a list of {name, description}."""
items = [
(name, description) for name, description in KNOWN_XFADE_TRANSITIONS.items()
] + [
(name, description) for name, (description, expr) in CUSTOM_XFADE_TRANSITIONS.items()
]
return [
{"name": name, "description": description}
for name, description in items
]
@app.route('/thrimshim/<ident>', methods=['GET'])
@request_stats
def get_row(ident):

Loading…
Cancel
Save