diff --git a/restreamer/restreamer/main.py b/restreamer/restreamer/main.py
index 1e66e16..d437cab 100644
--- a/restreamer/restreamer/main.py
+++ b/restreamer/restreamer/main.py
@@ -314,6 +314,16 @@ def cut(channel, quality):
return "We have no content available within the requested time range.", 406
segment_ranges.append(segments)
+ crop = None
+ if crop in request.args:
+ if type not in ('mpegts', 'mp4'):
+ return "Crop can only be given for full cuts", 400
+ try:
+ x, y, width, height = [int(n) for n in request.args['crop'].split(',')]
+ except ValueError:
+ return "Bad crop value", 400
+ crop = x, y, width, height
+
type = request.args.get('type', 'fast')
if type == 'rough':
return Response(rough_cut_segments(segment_ranges, ranges), mimetype='video/MP2T')
@@ -327,6 +337,12 @@ def cut(channel, quality):
encoding_args = ['-c:v', 'libx264', '-preset', 'ultrafast', '-crf', '0', '-f', muxer]
if len(ranges) > 1:
return "full cut does not support multiple ranges at this time", 400
+ if crop is not None:
+ x, y, w, h = crop
+ encoding_args = [
+ "-vf",
+ "crop={}:{}:{}:{},scale=1080:1920".format(w, h, x, y),
+ ]
start, end = ranges[0]
return Response(full_cut_segments(segment_ranges[0], start, end, encoding_args, stream=stream), mimetype=mimetype)
else:
diff --git a/thrimbletrimmer/edit.html b/thrimbletrimmer/edit.html
index 508c0dc..19b3b78 100644
--- a/thrimbletrimmer/edit.html
+++ b/thrimbletrimmer/edit.html
@@ -283,6 +283,7 @@
+
Download Video
Download Current Frame as Image
diff --git a/thrimbletrimmer/scripts/edit.js b/thrimbletrimmer/scripts/edit.js
index 43fd2b9..5a1034b 100644
--- a/thrimbletrimmer/scripts/edit.js
+++ b/thrimbletrimmer/scripts/edit.js
@@ -304,6 +304,9 @@ window.addEventListener("DOMContentLoaded", async (event) => {
document.getElementById("download-type-select").addEventListener("change", () => {
updateDownloadLink();
});
+ document.getElementById("download-crop").addEventListener("change", () => {
+ updateDownloadLink();
+ });
document.getElementById("download-frame").addEventListener("click", (_event) => {
downloadFrame();
@@ -1078,8 +1081,11 @@ function handleLeavePageWhilePending(event) {
return event.returnValue;
}
-function generateDownloadURL(timeRanges, downloadType, allowHoles, quality) {
+function generateDownloadURL(timeRanges, downloadType, allowHoles, crop, quality) {
const queryParts = [`type=${downloadType}`, `allow_holes=${allowHoles}`];
+ if (crop != "" && downloadType === "mpegts") {
+ queryParts.push(`crop=${crop}`);
+ }
for (const range of timeRanges) {
let timeRangeString = "";
if (range.hasOwnProperty("start")) {
@@ -1099,6 +1105,7 @@ function generateDownloadURL(timeRanges, downloadType, allowHoles, quality) {
function updateDownloadLink() {
const downloadType = document.getElementById("download-type-select").value;
const allowHoles = document.getElementById("advanced-submission-option-allow-holes").checked;
+ const crop = document.getElementById("download-crop").value
const timeRanges = [];
for (const rangeContainer of document.getElementById("range-definitions").children) {
@@ -1120,6 +1127,7 @@ function updateDownloadLink() {
timeRanges,
downloadType,
allowHoles,
+ crop,
videoInfo.video_quality
);
document.getElementById("download-link").href = downloadURL;