Compare commits

..

No commits in common. '55f48e18811109a64b0eea8285eef5a65a77a9b6' and '06931db26baa6c5f19b56609f48ce930842014c8' have entirely different histories.

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

@ -121,8 +121,6 @@ class Youtube(UploadBackend):
to make much difference. to make much difference.
mime_type: You must set this to the correct mime type for the encoded video. 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. 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 needs_transcode = True
@ -140,7 +138,7 @@ class Youtube(UploadBackend):
] ]
def __init__(self, credentials, category_id=23, language="en", use_yt_recommended_encoding=False, def __init__(self, credentials, category_id=23, language="en", use_yt_recommended_encoding=False,
mime_type='video/MP2T', unlisted_only=False): mime_type='video/MP2T'):
self.logger = logging.getLogger(type(self).__name__) self.logger = logging.getLogger(type(self).__name__)
self.client = GoogleAPIClient( self.client = GoogleAPIClient(
credentials['client_id'], credentials['client_id'],
@ -150,14 +148,11 @@ class Youtube(UploadBackend):
self.category_id = category_id self.category_id = category_id
self.language = language self.language = language
self.mime_type = mime_type self.mime_type = mime_type
self.unlisted_only = unlisted_only
if use_yt_recommended_encoding: if use_yt_recommended_encoding:
self.encoding_settings = self.recommended_settings self.encoding_settings = self.recommended_settings
self.encoding_streamable = False self.encoding_streamable = False
def upload_video(self, title, description, tags, public, data): 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 = { json = {
'snippet': { 'snippet': {
'title': title, 'title': title,

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

@ -126,20 +126,8 @@ class StreamLogEventsMiddleware(Middleware):
def get_rows(self): def get_rows(self):
all_rows = [] all_rows = []
entries_by_id = {} for row in self.client.get_entries()["event_log"]:
for entry in self.client.get_entries()["event_log"]: row = self.parse_row(row)
# 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 # Malformed rows can be skipped, represented as a None result
if row is not None: if row is not None:
all_rows.append(row) all_rows.append(row)
@ -162,19 +150,12 @@ class StreamLogEventsMiddleware(Middleware):
output["sheet_name"] = row["tab"]["name"] if row["tab"] else "unknown" output["sheet_name"] = row["tab"]["name"] if row["tab"] else "unknown"
# Implicit tags # Implicit tags
implicit_tags = [ output['tags'] += [
output['category'], output['category'],
output["sheet_name"], output["sheet_name"],
] ]
if output["poster_moment"]: if output["poster_moment"]:
implicit_tags.append('Poster Moment') output['tags'] += '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 return output

@ -12,12 +12,12 @@ import gevent
import gevent.backdoor import gevent.backdoor
from gevent.pywsgi import WSGIServer from gevent.pywsgi import WSGIServer
import prometheus_client import prometheus_client
import psycopg2
from psycopg2 import sql from psycopg2 import sql
import common import common
from common import database, dateutil from common import database, dateutil
from common.flask_stats import request_stats, after_request 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.oauth2.id_token
import google.auth.transport.requests import google.auth.transport.requests
@ -144,21 +144,6 @@ 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']) @app.route('/thrimshim/<ident>', methods=['GET'])
@request_stats @request_stats
def get_row(ident): def get_row(ident):

Loading…
Cancel
Save