pull/315/merge
Mike Lang 2 years ago committed by GitHub
commit 5a591c84c6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -181,6 +181,7 @@ columns | type | role
`public` | `BOOLEAN NOT NULL DEFAULT TRUE` | edit input | Whether the uploaded video should be public or not, if the upload location supports that distinction. For example, on youtube, non-public videos are "unlisted". It also controls whether the video will be added to playlists, only public videos are added to playlists. `public` | `BOOLEAN NOT NULL DEFAULT TRUE` | edit input | Whether the uploaded video should be public or not, if the upload location supports that distinction. For example, on youtube, non-public videos are "unlisted". It also controls whether the video will be added to playlists, only public videos are added to playlists.
`video_ranges` | `{start TIMESTAMP, end TIMESTAMP}[]` | edit input | A non-zero number of start and end times, describing the ranges of video to cut. They will be cut back-to-back in the given order, with the transitions between as per `video_transitions`. If already set, used as the default range settings when editing. `video_ranges` | `{start TIMESTAMP, end TIMESTAMP}[]` | edit input | A non-zero number of start and end times, describing the ranges of video to cut. They will be cut back-to-back in the given order, with the transitions between as per `video_transitions`. If already set, used as the default range settings when editing.
`video_transitions` | `{type TEXT, duration INTERVAL}[]` | edit input | Defines how to transition between each range defined in `video_ranges`, and must be exactly the length of `video_ranges` minus 1. Each index in `video_transitions` defines the transition between the range with the same index in `video_ranges` and the next one. Transitions either specify a transition type as understood by `ffmpeg`'s `xfade` filter and a duration (amount of overlap), or can be NULL to indicate a hard cut. `video_transitions` | `{type TEXT, duration INTERVAL}[]` | edit input | Defines how to transition between each range defined in `video_ranges`, and must be exactly the length of `video_ranges` minus 1. Each index in `video_transitions` defines the transition between the range with the same index in `video_ranges` and the next one. Transitions either specify a transition type as understood by `ffmpeg`'s `xfade` filter and a duration (amount of overlap), or can be NULL to indicate a hard cut.
`video_crop` | `{x, y, w, h INTEGER}` | edit input | If given, defines how the video should be cropped when editing.
`video_title` | `TEXT` | edit input | The title of the video. If already set, used as the default title when editing instead of `description`. `video_title` | `TEXT` | edit input | The title of the video. If already set, used as the default title when editing instead of `description`.
`video_description` | `TEXT` | edit input | The description field of the video. If already set, used as the default description when editing instead of `description`. `video_description` | `TEXT` | edit input | The description field of the video. If already set, used as the default description when editing instead of `description`.
`video_tags` | `TEXT[]` | edit input | Custom tags to annotate the video with. If already set, used as the default when editing instead of `tags`. `video_tags` | `TEXT[]` | edit input | Custom tags to annotate the video with. If already set, used as the default when editing instead of `tags`.

@ -17,10 +17,12 @@ from psycogreen.gevent import patch_psycopg
COMPOSITE_TYPES = [ COMPOSITE_TYPES = [
"video_range", "video_range",
"video_transition", "video_transition",
"public.box",
] ]
COLUMN_CASTS = { COLUMN_CASTS = {
"video_ranges": "video_range[]", "video_ranges": "video_range[]",
"video_transitions": "video_transition[]", "video_transitions": "video_transition[]",
"video_crop": "public.box",
} }
def get_column_placeholder(column): def get_column_placeholder(column):

@ -63,6 +63,7 @@ CUT_JOB_PARAMS = [
"public", "public",
"video_ranges", "video_ranges",
"video_transitions", "video_transitions",
"video_crop",
"video_title", "video_title",
"video_description", "video_description",
"video_tags", "video_tags",
@ -396,22 +397,40 @@ class Cutter(object):
nonlocal upload_finished nonlocal upload_finished
try: try:
cut_type = upload_backend.preferred_cut_type
has_complex_transitions = any(transition is not None for transition in job.video_transitions)
if upload_backend.encoding_settings is None: if upload_backend.encoding_settings is None:
# this forces the issue
cut_type = 'fast'
elif has_complex_transitions or job.video_crop is not None:
# required to be full cut
cut_type = 'full'
if cut_type == 'fast':
self.logger.debug("No encoding settings, using fast cut") self.logger.debug("No encoding settings, using fast cut")
if any(transition is not None for transition in job.video_transitions): if has_complex_transitions:
raise ValueError("Fast cuts do not support complex transitions") raise ValueError("Fast cuts do not support complex transitions")
if job.video_crop is not None:
raise ValueError("Fast cuts do not support cropping")
cut = fast_cut_segments(job.segment_ranges, job.video_ranges) cut = fast_cut_segments(job.segment_ranges, job.video_ranges)
else: else:
assert cut_type == 'full', cut_type
self.logger.debug("Using encoding settings for {} cut: {}".format( self.logger.debug("Using encoding settings for {} cut: {}".format(
"streamable" if upload_backend.encoding_streamable else "non-streamable", "streamable" if upload_backend.encoding_streamable else "non-streamable",
upload_backend.encoding_settings, upload_backend.encoding_settings,
)) ))
encode_args = upload_backend.encoding_settings
if job.video_crop:
x, y, w, h = job.video_crop
encode_args = [
"-vf", "crop={}:{}:{}:{}".format(w, h, x, y)
] + encode_args
if len(job.video_ranges) > 1: if len(job.video_ranges) > 1:
raise ValueError("Full cuts do not support multiple ranges") raise ValueError("Full cuts do not support multiple ranges")
range = job.video_ranges[0] range = job.video_ranges[0]
cut = full_cut_segments( cut = full_cut_segments(
job.segment_ranges[0], range.start, range.end, job.segment_ranges[0], range.start, range.end,
upload_backend.encoding_settings, stream=upload_backend.encoding_streamable, encode_args, stream=upload_backend.encoding_streamable,
) )
for chunk in cut: for chunk in cut:
@ -934,10 +953,8 @@ def main(
else: else:
raise ValueError("Unknown upload backend type: {!r}".format(backend_type)) raise ValueError("Unknown upload backend type: {!r}".format(backend_type))
backend = backend_type(credentials, **backend_config) backend = backend_type(credentials, **backend_config)
if cut_type == 'fast': backend.preferred_cut_type = cut_type
# mark for fast cut by clearing encoding settings if cut_type not in ('fast', 'full'):
backend.encoding_settings = None
elif cut_type != 'full':
raise ValueError("Unknown cut type: {!r}".format(cut_type)) raise ValueError("Unknown cut type: {!r}".format(cut_type))
upload_locations[location] = backend upload_locations[location] = backend
if backend.needs_transcode and not no_transcode_check: if backend.needs_transcode and not no_transcode_check:

@ -64,6 +64,13 @@ CREATE TYPE thumbnail_mode as ENUM (
'CUSTOM' 'CUSTOM'
); );
CREATE TYPE box as (
x INTEGER,
y INTEGER,
w INTEGER,
h INTEGER
)
CREATE TABLE events ( CREATE TABLE events (
id UUID PRIMARY KEY, id UUID PRIMARY KEY,
@ -88,6 +95,7 @@ CREATE TABLE events (
(video_ranges IS NULL AND video_transitions IS NULL) (video_ranges IS NULL AND video_transitions IS NULL)
OR CARDINALITY(video_ranges) = CARDINALITY(video_transitions) + 1 OR CARDINALITY(video_ranges) = CARDINALITY(video_transitions) + 1
), ),
video_crop public.box,
video_title TEXT CHECK (state IN ('UNEDITED', 'DONE') OR video_title IS NOT NULL), video_title TEXT CHECK (state IN ('UNEDITED', 'DONE') OR video_title IS NOT NULL),
video_description TEXT CHECK (state IN ('UNEDITED', 'DONE') OR video_description IS NOT NULL), video_description TEXT CHECK (state IN ('UNEDITED', 'DONE') OR video_description IS NOT NULL),
video_tags TEXT[] CHECK (state IN ('UNEDITED', 'DONE') OR video_tags IS NOT NULL), video_tags TEXT[] CHECK (state IN ('UNEDITED', 'DONE') OR video_tags IS NOT NULL),

@ -242,6 +242,7 @@ def update_row(ident, editor=None):
] ]
edit_columns = non_null_columns + [ edit_columns = non_null_columns + [
'allow_holes', 'uploader_whitelist', 'thumbnail_time', 'thumbnail_template', 'thumbnail_image' 'allow_holes', 'uploader_whitelist', 'thumbnail_time', 'thumbnail_template', 'thumbnail_image'
'video_crop'
] ]
sheet_columns = [ sheet_columns = [
'sheet_name', 'event_start', 'event_end', 'sheet_name', 'event_start', 'event_end',
@ -337,6 +338,8 @@ def update_row(ident, editor=None):
None if transition is None else tuple(transition) None if transition is None else tuple(transition)
for transition in new_row['video_transitions'] for transition in new_row['video_transitions']
] ]
if new_row.get('video_crop') is not None:
new_row['video_crop'] = tuple(new_row['video_crop'])
# Convert binary fields from base64 and do basic validation of contents # Convert binary fields from base64 and do basic validation of contents
if new_row.get('thumbnail_image') is not None: if new_row.get('thumbnail_image') is not None:

Loading…
Cancel
Save