From d91e85ea4726274354e6d14c1a4b7d14ddfa3aaa Mon Sep 17 00:00:00 2001 From: Mike Lang Date: Sat, 12 Nov 2022 13:11:10 +1100 Subject: [PATCH] Add ability to crop videos Full cuts only. Uploads just the cropped area. This can be used to make youtube "shorts". --- DATABASE.md | 1 + cutter/cutter/main.py | 9 +++++++++ postgres/setup.sh | 8 ++++++++ thrimshim/thrimshim/main.py | 3 +++ 4 files changed, 21 insertions(+) diff --git a/DATABASE.md b/DATABASE.md index 7725448..287ccd8 100644 --- a/DATABASE.md +++ b/DATABASE.md @@ -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. `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_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_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`. diff --git a/cutter/cutter/main.py b/cutter/cutter/main.py index c9aaf67..9efd60a 100644 --- a/cutter/cutter/main.py +++ b/cutter/cutter/main.py @@ -63,6 +63,7 @@ CUT_JOB_PARAMS = [ "public", "video_ranges", "video_transitions", + "video_crop", "video_title", "video_description", "video_tags", @@ -400,12 +401,20 @@ class Cutter(object): self.logger.debug("No encoding settings, using fast cut") if any(transition is not None for transition in job.video_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) else: self.logger.debug("Using encoding settings for {} cut: {}".format( "streamable" if upload_backend.encoding_streamable else "non-streamable", 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: raise ValueError("Full cuts do not support multiple ranges") range = job.video_ranges[0] diff --git a/postgres/setup.sh b/postgres/setup.sh index 98483d4..6a1f709 100644 --- a/postgres/setup.sh +++ b/postgres/setup.sh @@ -64,6 +64,13 @@ CREATE TYPE thumbnail_mode as ENUM ( 'CUSTOM' ); +CREATE TYPE box as ( + x INTEGER, + y INTEGER, + w INTEGER, + h INTEGER +) + CREATE TABLE events ( id UUID PRIMARY KEY, @@ -88,6 +95,7 @@ CREATE TABLE events ( (video_ranges IS NULL AND video_transitions IS NULL) OR CARDINALITY(video_ranges) = CARDINALITY(video_transitions) + 1 ), + video_crop box, 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_tags TEXT[] CHECK (state IN ('UNEDITED', 'DONE') OR video_tags IS NOT NULL), diff --git a/thrimshim/thrimshim/main.py b/thrimshim/thrimshim/main.py index 32168ec..833e2ba 100644 --- a/thrimshim/thrimshim/main.py +++ b/thrimshim/thrimshim/main.py @@ -242,6 +242,7 @@ def update_row(ident, editor=None): ] edit_columns = non_null_columns + [ 'allow_holes', 'uploader_whitelist', 'thumbnail_time', 'thumbnail_template', 'thumbnail_image' + 'video_crop' ] sheet_columns = [ 'sheet_name', 'event_start', 'event_end', @@ -337,6 +338,8 @@ def update_row(ident, editor=None): None if transition is None else tuple(transition) 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 if new_row.get('thumbnail_image') is not None: