diff --git a/public/examples/cut-bluer-grayscale.json b/public/examples/cut-bluer-grayscale.json new file mode 100644 index 0000000..f2f9808 --- /dev/null +++ b/public/examples/cut-bluer-grayscale.json @@ -0,0 +1,763 @@ +{ + "nodes": [ + { + "id": "9406a455-7e77-49bb-9cd8-381b54d47d31", + "type": "ffmpeg", + "data": { + "name": "punch.mp4", + "url": "/punch.mp4", + "ext": "mp4", + "outputs": [ + "v", + "a" + ], + "inputs": [], + "nodeType": "input", + "enabled": true + }, + "nodeType": "input", + "position": { + "x": 10, + "y": -20 + }, + "positionAbsolute": { + "x": 10, + "y": -20 + }, + "width": 108, + "height": 53, + "dragging": false + }, + { + "id": "eb32733d-28ad-4a38-9db7-50a98db5f309", + "type": "ffmpeg", + "data": { + "name": "out.mp4", + "ext": "mp4", + "inputs": [ + "v", + "a" + ], + "outputs": [], + "nodeType": "output", + "enabled": true + }, + "nodeType": "output", + "position": { + "x": 630, + "y": -20 + }, + "positionAbsolute": { + "x": 630, + "y": -20 + }, + "width": 89, + "height": 53, + "selected": false, + "dragging": false + }, + { + "id": "642846ba-60ad-45d3-bd53-e4adb24dacc7", + "type": "ffmpeg", + "data": { + "id": 391, + "meta": "...", + "name": "trim", + "type": "V->V", + "description": "Pick one continuous section from the input, drop the rest.", + "inputs": [ + "v" + ], + "outputs": [ + "v" + ], + "params": [ + { + "name": "start", + "type": "duration", + "desc": "Timestamp of the first frame that should be passed (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "0" + }, + { + "name": "starti", + "type": "duration", + "desc": "Timestamp of the first frame that should be passed (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "INT64_MAX" + }, + { + "name": "end", + "type": "duration", + "desc": "Timestamp of the first frame that should be dropped again (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "2" + }, + { + "name": "endi", + "type": "duration", + "desc": "Timestamp of the first frame that should be dropped again (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "INT64_MAX" + }, + { + "name": "start_pts", + "type": "int64", + "desc": "Timestamp of the first frame that should be passed (from I64_MIN to I64_MAX) (default I64_MIN)", + "min": -2000, + "max": 2000, + "default": -2000, + "value": -2000 + }, + { + "name": "end_pts", + "type": "int64", + "desc": "Timestamp of the first frame that should be dropped again (from I64_MIN to I64_MAX) (default I64_MIN)", + "min": -2000, + "max": 2000, + "default": -2000, + "value": -2000 + }, + { + "name": "duration", + "type": "duration", + "desc": "Maximum duration of the output (default 0)", + "min": null, + "max": null, + "default": "0", + "value": "0" + }, + { + "name": "durationi", + "type": "duration", + "desc": "Maximum duration of the output (default 0)", + "min": null, + "max": null, + "default": "0", + "value": "0" + }, + { + "name": "start_frame", + "type": "int64", + "desc": "Number of the first frame that should be passed to the output (from -1 to I64_MAX) (default -1)", + "min": -1, + "max": 2000, + "default": -1, + "value": -1 + }, + { + "name": "end_frame", + "type": "int64", + "desc": "Number of the first frame that should be dropped again (from 0 to I64_MAX) (default I64_MAX)", + "min": 0, + "max": 2000, + "default": 2000, + "value": 2000 + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": 170, + "y": -80 + }, + "positionAbsolute": { + "x": 170, + "y": -80 + }, + "width": 60, + "height": 50, + "dragging": false + }, + { + "id": "107e9b3e-6055-4fed-b3af-42d08621cb5a", + "type": "ffmpeg", + "data": { + "id": 391, + "meta": "...", + "name": "trim", + "type": "V->V", + "description": "Pick one continuous section from the input, drop the rest.", + "inputs": [ + "v" + ], + "outputs": [ + "v" + ], + "params": [ + { + "name": "start", + "type": "duration", + "desc": "Timestamp of the first frame that should be passed (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "2" + }, + { + "name": "starti", + "type": "duration", + "desc": "Timestamp of the first frame that should be passed (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "INT64_MAX" + }, + { + "name": "end", + "type": "duration", + "desc": "Timestamp of the first frame that should be dropped again (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "3" + }, + { + "name": "endi", + "type": "duration", + "desc": "Timestamp of the first frame that should be dropped again (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "INT64_MAX" + }, + { + "name": "start_pts", + "type": "int64", + "desc": "Timestamp of the first frame that should be passed (from I64_MIN to I64_MAX) (default I64_MIN)", + "min": -2000, + "max": 2000, + "default": -2000, + "value": -2000 + }, + { + "name": "end_pts", + "type": "int64", + "desc": "Timestamp of the first frame that should be dropped again (from I64_MIN to I64_MAX) (default I64_MIN)", + "min": -2000, + "max": 2000, + "default": -2000, + "value": -2000 + }, + { + "name": "duration", + "type": "duration", + "desc": "Maximum duration of the output (default 0)", + "min": null, + "max": null, + "default": "0", + "value": "0" + }, + { + "name": "durationi", + "type": "duration", + "desc": "Maximum duration of the output (default 0)", + "min": null, + "max": null, + "default": "0", + "value": "0" + }, + { + "name": "start_frame", + "type": "int64", + "desc": "Number of the first frame that should be passed to the output (from -1 to I64_MAX) (default -1)", + "min": -1, + "max": 2000, + "default": -1, + "value": -1 + }, + { + "name": "end_frame", + "type": "int64", + "desc": "Number of the first frame that should be dropped again (from 0 to I64_MAX) (default I64_MAX)", + "min": 0, + "max": 2000, + "default": 2000, + "value": 2000 + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": 170, + "y": 20 + }, + "positionAbsolute": { + "x": 170, + "y": 20 + }, + "width": 60, + "height": 50, + "dragging": false + }, + { + "id": "4119dd47-22f1-4bb7-ad01-326b8f6dc443", + "type": "ffmpeg", + "data": { + "id": 236, + "meta": "TSC", + "name": "gblur", + "type": "V->V", + "description": "Apply Gaussian Blur filter.", + "inputs": [ + "v" + ], + "outputs": [ + "v" + ], + "params": [ + { + "name": "sigma", + "type": "float", + "desc": "set sigma (from 0 to 1024) (default 0.5)", + "min": 0, + "max": 1024, + "default": 0.5, + "value": "5" + }, + { + "name": "steps", + "type": "int", + "desc": "set number of steps (from 1 to 6) (default 1)", + "min": 1, + "max": 6, + "default": 1, + "value": 1 + }, + { + "name": "planes", + "type": "int", + "desc": "set planes to filter (from 0 to 15) (default 15)", + "min": 0, + "max": 15, + "default": 15, + "value": 15 + }, + { + "name": "sigmaV", + "type": "float", + "desc": "set vertical sigma (from -1 to 1024) (default -1)", + "min": -1, + "max": 1024, + "default": -1, + "value": -1 + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": 270, + "y": -80 + }, + "positionAbsolute": { + "x": 270, + "y": -80 + }, + "width": 63, + "height": 50, + "dragging": false + }, + { + "id": "08e3f362-a488-4789-b491-edfa4fae13e7", + "type": "ffmpeg", + "data": { + "id": 351, + "meta": "...", + "name": "setpts", + "type": "V->V", + "description": "Set PTS for the output video frame.", + "inputs": [ + "v" + ], + "outputs": [ + "v" + ], + "params": [ + { + "name": "expr", + "type": "string", + "desc": "Expression determining the frame timestamp (default \"PTS\")", + "min": null, + "max": null, + "default": "PTS", + "value": "PTS-STARTPTS" + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": 370, + "y": -80 + }, + "positionAbsolute": { + "x": 370, + "y": -80 + }, + "width": 65, + "height": 50, + "dragging": false + }, + { + "id": "76b73f65-b7a3-460b-81b3-61605d736b59", + "type": "ffmpeg", + "data": { + "id": 351, + "meta": "...", + "name": "setpts", + "type": "V->V", + "description": "Set PTS for the output video frame.", + "inputs": [ + "v" + ], + "outputs": [ + "v" + ], + "params": [ + { + "name": "expr", + "type": "string", + "desc": "Expression determining the frame timestamp (default \"PTS\")", + "min": null, + "max": null, + "default": "PTS", + "value": "PTS-STARTPTS" + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": 370, + "y": 20 + }, + "positionAbsolute": { + "x": 370, + "y": 20 + }, + "width": 65, + "height": 50, + "dragging": false + }, + { + "id": "b78b0603-65df-4b0b-9831-647abf0c356c", + "type": "ffmpeg", + "data": { + "id": 252, + "meta": "T.C", + "name": "hue", + "type": "V->V", + "description": "Adjust the hue and saturation of the input video.", + "inputs": [ + "v" + ], + "outputs": [ + "v" + ], + "params": [ + { + "name": "h", + "type": "string", + "desc": "set the hue angle degrees expression", + "min": null, + "max": null, + "default": null, + "value": null + }, + { + "name": "s", + "type": "string", + "desc": "set the saturation expression (default \"1\")", + "min": null, + "max": null, + "default": "1", + "value": "0" + }, + { + "name": "H", + "type": "string", + "desc": "set the hue angle radians expression", + "min": null, + "max": null, + "default": null, + "value": null + }, + { + "name": "b", + "type": "string", + "desc": "set the brightness expression (default \"0\")", + "min": null, + "max": null, + "default": "0", + "value": "0" + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": 270, + "y": 20 + }, + "positionAbsolute": { + "x": 270, + "y": 20 + }, + "width": 60, + "height": 50, + "dragging": false + }, + { + "id": "9ceffaea-783e-4d7f-9893-4c080bcb2e24", + "type": "ffmpeg", + "data": { + "id": 445, + "meta": "..C", + "name": "concat", + "type": "N->N", + "description": "Concatenate audio and video streams.", + "inputs": [ + "v", + "v" + ], + "outputs": [ + "v" + ], + "params": [ + { + "name": "n", + "type": "int", + "desc": "specify the number of segments (from 1 to INT_MAX) (default 2)", + "min": 1, + "max": 2000, + "default": 2, + "value": 2 + }, + { + "name": "v", + "type": "int", + "desc": "specify the number of video streams (from 0 to INT_MAX) (default 1)", + "min": 0, + "max": 2000, + "default": 1, + "value": 1 + }, + { + "name": "a", + "type": "int", + "desc": "specify the number of audio streams (from 0 to INT_MAX) (default 0)", + "min": 0, + "max": 2000, + "default": 0, + "value": 0 + }, + { + "name": "unsafe", + "type": "boolean", + "desc": "enable unsafe mode (default false)", + "min": null, + "max": null, + "default": "false", + "value": "false" + } + ], + "isCustom": true, + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": 490, + "y": -80 + }, + "positionAbsolute": { + "x": 490, + "y": -80 + }, + "width": 71, + "height": 50, + "dragging": false + } + ], + "edges": [ + { + "source": "9406a455-7e77-49bb-9cd8-381b54d47d31", + "sourceHandle": "v_0", + "target": "a6796a9d-7fb6-494c-9545-bde5d4628eea", + "targetHandle": "v_0", + "id": "xyflow__edge-9406a455-7e77-49bb-9cd8-381b54d47d31v_0-a6796a9d-7fb6-494c-9545-bde5d4628eeav_0" + }, + { + "source": "a0798490-6c5b-4a14-82a3-a5e7da6d223b", + "sourceHandle": "v_0", + "target": "1623b321-0d2b-42a1-99af-bad37a146b07", + "targetHandle": "v_1", + "id": "xyflow__edge-a0798490-6c5b-4a14-82a3-a5e7da6d223bv_0-1623b321-0d2b-42a1-99af-bad37a146b07v_1" + }, + { + "source": "a6796a9d-7fb6-494c-9545-bde5d4628eea", + "sourceHandle": "v_0", + "target": "1623b321-0d2b-42a1-99af-bad37a146b07", + "targetHandle": "v_0", + "id": "xyflow__edge-a6796a9d-7fb6-494c-9545-bde5d4628eeav_0-1623b321-0d2b-42a1-99af-bad37a146b07v_0" + }, + { + "source": "1623b321-0d2b-42a1-99af-bad37a146b07", + "sourceHandle": "v_0", + "target": "eb32733d-28ad-4a38-9db7-50a98db5f309", + "targetHandle": "v_0", + "id": "xyflow__edge-1623b321-0d2b-42a1-99af-bad37a146b07v_0-eb32733d-28ad-4a38-9db7-50a98db5f309v_0" + }, + { + "source": "9406a455-7e77-49bb-9cd8-381b54d47d31", + "sourceHandle": "v_0", + "target": "66adb05a-5bf8-48f8-ac0c-0c8434191665", + "targetHandle": "v_0", + "id": "xyflow__edge-9406a455-7e77-49bb-9cd8-381b54d47d31v_0-66adb05a-5bf8-48f8-ac0c-0c8434191665v_0" + }, + { + "source": "66adb05a-5bf8-48f8-ac0c-0c8434191665", + "sourceHandle": "v_0", + "target": "eb32733d-28ad-4a38-9db7-50a98db5f309", + "targetHandle": "v_0", + "id": "xyflow__edge-66adb05a-5bf8-48f8-ac0c-0c8434191665v_0-eb32733d-28ad-4a38-9db7-50a98db5f309v_0" + }, + { + "source": "9406a455-7e77-49bb-9cd8-381b54d47d31", + "sourceHandle": "v_0", + "target": "8dfda4cc-5985-4258-b1a0-d66bec0328d8", + "targetHandle": "v_0", + "id": "xyflow__edge-9406a455-7e77-49bb-9cd8-381b54d47d31v_0-8dfda4cc-5985-4258-b1a0-d66bec0328d8v_0" + }, + { + "source": "8dfda4cc-5985-4258-b1a0-d66bec0328d8", + "sourceHandle": "v_0", + "target": "b2bd1fb9-b7ad-4c3b-9800-29748a8d8c73", + "targetHandle": "v_0", + "id": "xyflow__edge-8dfda4cc-5985-4258-b1a0-d66bec0328d8v_0-b2bd1fb9-b7ad-4c3b-9800-29748a8d8c73v_0" + }, + { + "source": "b2bd1fb9-b7ad-4c3b-9800-29748a8d8c73", + "sourceHandle": "v_0", + "target": "eb32733d-28ad-4a38-9db7-50a98db5f309", + "targetHandle": "v_0", + "id": "xyflow__edge-b2bd1fb9-b7ad-4c3b-9800-29748a8d8c73v_0-eb32733d-28ad-4a38-9db7-50a98db5f309v_0" + }, + { + "source": "9406a455-7e77-49bb-9cd8-381b54d47d31", + "sourceHandle": "a_1", + "target": "78efe031-cddd-477f-b975-708746d870e8", + "targetHandle": "a_0", + "id": "xyflow__edge-9406a455-7e77-49bb-9cd8-381b54d47d31a_1-78efe031-cddd-477f-b975-708746d870e8a_0" + }, + { + "source": "78efe031-cddd-477f-b975-708746d870e8", + "sourceHandle": "a_0", + "target": "eb32733d-28ad-4a38-9db7-50a98db5f309", + "targetHandle": "a_1", + "id": "xyflow__edge-78efe031-cddd-477f-b975-708746d870e8a_0-eb32733d-28ad-4a38-9db7-50a98db5f309a_1" + }, + { + "source": "9406a455-7e77-49bb-9cd8-381b54d47d31", + "sourceHandle": "v_0", + "target": "75b30196-a451-4eb0-85b1-3389ad4a711e", + "targetHandle": "v_0", + "id": "xyflow__edge-9406a455-7e77-49bb-9cd8-381b54d47d31v_0-75b30196-a451-4eb0-85b1-3389ad4a711ev_0" + }, + { + "source": "75b30196-a451-4eb0-85b1-3389ad4a711e", + "sourceHandle": "v_0", + "target": "eb32733d-28ad-4a38-9db7-50a98db5f309", + "targetHandle": "v_0", + "id": "xyflow__edge-75b30196-a451-4eb0-85b1-3389ad4a711ev_0-eb32733d-28ad-4a38-9db7-50a98db5f309v_0" + }, + { + "source": "9406a455-7e77-49bb-9cd8-381b54d47d31", + "sourceHandle": "v_0", + "target": "642846ba-60ad-45d3-bd53-e4adb24dacc7", + "targetHandle": "v_0", + "id": "xyflow__edge-9406a455-7e77-49bb-9cd8-381b54d47d31v_0-642846ba-60ad-45d3-bd53-e4adb24dacc7v_0" + }, + { + "source": "9406a455-7e77-49bb-9cd8-381b54d47d31", + "sourceHandle": "v_0", + "target": "107e9b3e-6055-4fed-b3af-42d08621cb5a", + "targetHandle": "v_0", + "id": "xyflow__edge-9406a455-7e77-49bb-9cd8-381b54d47d31v_0-107e9b3e-6055-4fed-b3af-42d08621cb5av_0" + }, + { + "source": "642846ba-60ad-45d3-bd53-e4adb24dacc7", + "sourceHandle": "v_0", + "target": "4119dd47-22f1-4bb7-ad01-326b8f6dc443", + "targetHandle": "v_0", + "id": "xyflow__edge-642846ba-60ad-45d3-bd53-e4adb24dacc7v_0-4119dd47-22f1-4bb7-ad01-326b8f6dc443v_0" + }, + { + "source": "4119dd47-22f1-4bb7-ad01-326b8f6dc443", + "sourceHandle": "v_0", + "target": "08e3f362-a488-4789-b491-edfa4fae13e7", + "targetHandle": "v_0", + "id": "xyflow__edge-4119dd47-22f1-4bb7-ad01-326b8f6dc443v_0-08e3f362-a488-4789-b491-edfa4fae13e7v_0" + }, + { + "source": "107e9b3e-6055-4fed-b3af-42d08621cb5a", + "sourceHandle": "v_0", + "target": "b78b0603-65df-4b0b-9831-647abf0c356c", + "targetHandle": "v_0", + "id": "xyflow__edge-107e9b3e-6055-4fed-b3af-42d08621cb5av_0-b78b0603-65df-4b0b-9831-647abf0c356cv_0" + }, + { + "source": "b78b0603-65df-4b0b-9831-647abf0c356c", + "sourceHandle": "v_0", + "target": "76b73f65-b7a3-460b-81b3-61605d736b59", + "targetHandle": "v_0", + "id": "xyflow__edge-b78b0603-65df-4b0b-9831-647abf0c356cv_0-76b73f65-b7a3-460b-81b3-61605d736b59v_0" + }, + { + "source": "08e3f362-a488-4789-b491-edfa4fae13e7", + "sourceHandle": "v_0", + "target": "9ceffaea-783e-4d7f-9893-4c080bcb2e24", + "targetHandle": "v_0", + "id": "xyflow__edge-08e3f362-a488-4789-b491-edfa4fae13e7v_0-9ceffaea-783e-4d7f-9893-4c080bcb2e24v_0" + }, + { + "source": "76b73f65-b7a3-460b-81b3-61605d736b59", + "sourceHandle": "v_0", + "target": "9ceffaea-783e-4d7f-9893-4c080bcb2e24", + "targetHandle": "v_1", + "id": "xyflow__edge-76b73f65-b7a3-460b-81b3-61605d736b59v_0-9ceffaea-783e-4d7f-9893-4c080bcb2e24v_1" + }, + { + "source": "9ceffaea-783e-4d7f-9893-4c080bcb2e24", + "sourceHandle": "v_0", + "target": "eb32733d-28ad-4a38-9db7-50a98db5f309", + "targetHandle": "v_0", + "id": "xyflow__edge-9ceffaea-783e-4d7f-9893-4c080bcb2e24v_0-eb32733d-28ad-4a38-9db7-50a98db5f309v_0" + }, + { + "source": "9406a455-7e77-49bb-9cd8-381b54d47d31", + "sourceHandle": "a_1", + "target": "eb32733d-28ad-4a38-9db7-50a98db5f309", + "targetHandle": "a_1", + "id": "xyflow__edge-9406a455-7e77-49bb-9cd8-381b54d47d31a_1-eb32733d-28ad-4a38-9db7-50a98db5f309a_1" + } + ], + "command": "ffmpeg -i punch.mp4 -filter_complex \"[0:v]trim=start=0:end=2[1];[0:v]trim=start=2:end=3[4];[1]gblur=sigma=5,setpts=expr=PTS-STARTPTS[3];[4]hue=s=0,setpts=expr=PTS-STARTPTS[6];[3][6]concat[out_1]\" -map \"[out_1]\" -map 0:a out.mp4" +} \ No newline at end of file diff --git a/public/examples/trim-concat.json b/public/examples/trim-concat.json new file mode 100644 index 0000000..78ebe65 --- /dev/null +++ b/public/examples/trim-concat.json @@ -0,0 +1,937 @@ +{ + "nodes": [ + { + "id": "7d004403-7325-49fe-a1b1-2ccfd02e10eb", + "type": "ffmpeg", + "data": { + "name": "shoe.mp4", + "url": "/punch.mp4", + "ext": "mp4", + "outputs": [ + "v", + "a" + ], + "inputs": [], + "nodeType": "input", + "enabled": true + }, + "nodeType": "input", + "position": { + "x": -190, + "y": -40 + }, + "positionAbsolute": { + "x": -190, + "y": -40 + }, + "width": 108, + "height": 53, + "dragging": false, + "selected": false + }, + { + "id": "10ebcbf9-cb96-4cff-ad69-63b5cab9a7f1", + "type": "ffmpeg", + "data": { + "name": "out.mp4", + "ext": "mp4", + "inputs": [ + "v", + "a" + ], + "outputs": [], + "nodeType": "output", + "enabled": true + }, + "nodeType": "output", + "position": { + "x": 320, + "y": -60 + }, + "positionAbsolute": { + "x": 320, + "y": -60 + }, + "width": 89, + "height": 53, + "dragging": false + }, + { + "id": "ca9bfea8-4e6e-4d37-86e8-2278f478311b", + "type": "ffmpeg", + "data": { + "id": 391, + "meta": "...", + "name": "trim", + "type": "V->V", + "description": "Pick one continuous section from the input, drop the rest.", + "inputs": [ + "v" + ], + "outputs": [ + "v" + ], + "params": [ + { + "name": "start", + "type": "duration", + "desc": "Timestamp of the first frame that should be passed (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "3" + }, + { + "name": "starti", + "type": "duration", + "desc": "Timestamp of the first frame that should be passed (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "INT64_MAX" + }, + { + "name": "end", + "type": "duration", + "desc": "Timestamp of the first frame that should be dropped again (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "4" + }, + { + "name": "endi", + "type": "duration", + "desc": "Timestamp of the first frame that should be dropped again (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "INT64_MAX" + }, + { + "name": "start_pts", + "type": "int64", + "desc": "Timestamp of the first frame that should be passed (from I64_MIN to I64_MAX) (default I64_MIN)", + "min": -2000, + "max": 2000, + "default": -2000, + "value": -2000 + }, + { + "name": "end_pts", + "type": "int64", + "desc": "Timestamp of the first frame that should be dropped again (from I64_MIN to I64_MAX) (default I64_MIN)", + "min": -2000, + "max": 2000, + "default": -2000, + "value": -2000 + }, + { + "name": "duration", + "type": "duration", + "desc": "Maximum duration of the output (default 0)", + "min": null, + "max": null, + "default": "0", + "value": "0" + }, + { + "name": "durationi", + "type": "duration", + "desc": "Maximum duration of the output (default 0)", + "min": null, + "max": null, + "default": "0", + "value": "0" + }, + { + "name": "start_frame", + "type": "int64", + "desc": "Number of the first frame that should be passed to the output (from -1 to I64_MAX) (default -1)", + "min": -1, + "max": 2000, + "default": -1, + "value": -1 + }, + { + "name": "end_frame", + "type": "int64", + "desc": "Number of the first frame that should be dropped again (from 0 to I64_MAX) (default I64_MAX)", + "min": 0, + "max": 2000, + "default": 2000, + "value": 2000 + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": -40, + "y": -70 + }, + "positionAbsolute": { + "x": -40, + "y": -70 + }, + "width": 60, + "height": 50, + "dragging": false + }, + { + "id": "36fdb883-b486-4d6d-a967-6c5e75fea2d4", + "type": "ffmpeg", + "data": { + "id": 391, + "meta": "...", + "name": "trim", + "type": "V->V", + "description": "Pick one continuous section from the input, drop the rest.", + "inputs": [ + "v" + ], + "outputs": [ + "v" + ], + "params": [ + { + "name": "start", + "type": "duration", + "desc": "Timestamp of the first frame that should be passed (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "1" + }, + { + "name": "starti", + "type": "duration", + "desc": "Timestamp of the first frame that should be passed (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "INT64_MAX" + }, + { + "name": "end", + "type": "duration", + "desc": "Timestamp of the first frame that should be dropped again (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "2" + }, + { + "name": "endi", + "type": "duration", + "desc": "Timestamp of the first frame that should be dropped again (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "INT64_MAX" + }, + { + "name": "start_pts", + "type": "int64", + "desc": "Timestamp of the first frame that should be passed (from I64_MIN to I64_MAX) (default I64_MIN)", + "min": -2000, + "max": 2000, + "default": -2000, + "value": -2000 + }, + { + "name": "end_pts", + "type": "int64", + "desc": "Timestamp of the first frame that should be dropped again (from I64_MIN to I64_MAX) (default I64_MIN)", + "min": -2000, + "max": 2000, + "default": -2000, + "value": -2000 + }, + { + "name": "duration", + "type": "duration", + "desc": "Maximum duration of the output (default 0)", + "min": null, + "max": null, + "default": "0", + "value": "0" + }, + { + "name": "durationi", + "type": "duration", + "desc": "Maximum duration of the output (default 0)", + "min": null, + "max": null, + "default": "0", + "value": "0" + }, + { + "name": "start_frame", + "type": "int64", + "desc": "Number of the first frame that should be passed to the output (from -1 to I64_MAX) (default -1)", + "min": -1, + "max": 2000, + "default": -1, + "value": -1 + }, + { + "name": "end_frame", + "type": "int64", + "desc": "Number of the first frame that should be dropped again (from 0 to I64_MAX) (default I64_MAX)", + "min": 0, + "max": 2000, + "default": 2000, + "value": 2000 + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": -40, + "y": -130 + }, + "positionAbsolute": { + "x": -40, + "y": -130 + }, + "width": 60, + "height": 50, + "dragging": false + }, + { + "id": "83608257-ca86-4e2b-bd15-8130dd57408f", + "type": "ffmpeg", + "data": { + "id": 76, + "meta": "...", + "name": "atrim", + "type": "A->A", + "description": "Pick one continuous section from the input, drop the rest.", + "inputs": [ + "a" + ], + "outputs": [ + "a" + ], + "params": [ + { + "name": "start", + "type": "duration", + "desc": "Timestamp of the first frame that should be passed (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "3" + }, + { + "name": "starti", + "type": "duration", + "desc": "Timestamp of the first frame that should be passed (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "INT64_MAX" + }, + { + "name": "end", + "type": "duration", + "desc": "Timestamp of the first frame that should be dropped again (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "4" + }, + { + "name": "endi", + "type": "duration", + "desc": "Timestamp of the first frame that should be dropped again (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "INT64_MAX" + }, + { + "name": "start_pts", + "type": "int64", + "desc": "Timestamp of the first frame that should be passed (from I64_MIN to I64_MAX) (default I64_MIN)", + "min": -2000, + "max": 2000, + "default": -2000, + "value": -2000 + }, + { + "name": "end_pts", + "type": "int64", + "desc": "Timestamp of the first frame that should be dropped again (from I64_MIN to I64_MAX) (default I64_MIN)", + "min": -2000, + "max": 2000, + "default": -2000, + "value": -2000 + }, + { + "name": "duration", + "type": "duration", + "desc": "Maximum duration of the output (default 0)", + "min": null, + "max": null, + "default": "0", + "value": "0" + }, + { + "name": "durationi", + "type": "duration", + "desc": "Maximum duration of the output (default 0)", + "min": null, + "max": null, + "default": "0", + "value": "0" + }, + { + "name": "start_sample", + "type": "int64", + "desc": "Number of the first audio sample that should be passed to the output (from -1 to I64_MAX) (default -1)", + "min": -1, + "max": 2000, + "default": -1, + "value": -1 + }, + { + "name": "end_sample", + "type": "int64", + "desc": "Number of the first audio sample that should be dropped again (from 0 to I64_MAX) (default I64_MAX)", + "min": 0, + "max": 2000, + "default": 2000, + "value": 2000 + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": -40, + "y": 50 + }, + "positionAbsolute": { + "x": -40, + "y": 50 + }, + "width": 63, + "height": 50, + "dragging": false + }, + { + "id": "2524a9ea-cc56-40f7-a815-c63f42cb27ac", + "type": "ffmpeg", + "data": { + "id": 59, + "meta": "...", + "name": "asetpts", + "type": "A->A", + "description": "Set PTS for the output audio frame.", + "inputs": [ + "a" + ], + "outputs": [ + "a" + ], + "params": [ + { + "name": "expr", + "type": "string", + "desc": "Expression determining the frame timestamp (default \"PTS\")", + "min": null, + "max": null, + "default": "PTS", + "value": "PTS-STARTPTS" + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": 60, + "y": 50 + }, + "positionAbsolute": { + "x": 60, + "y": 50 + }, + "width": 73, + "height": 50, + "dragging": false + }, + { + "id": "297207f9-d013-4382-802c-6358227487ff", + "type": "ffmpeg", + "data": { + "id": 59, + "meta": "...", + "name": "asetpts", + "type": "A->A", + "description": "Set PTS for the output audio frame.", + "inputs": [ + "a" + ], + "outputs": [ + "a" + ], + "params": [ + { + "name": "expr", + "type": "string", + "desc": "Expression determining the frame timestamp (default \"PTS\")", + "min": null, + "max": null, + "default": "PTS", + "value": "PTS-STARTPTS" + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": 60, + "y": -10 + }, + "positionAbsolute": { + "x": 60, + "y": -10 + }, + "width": 73, + "height": 50, + "dragging": false + }, + { + "id": "bbcf717d-709e-4195-8721-0beff2d3c724", + "type": "ffmpeg", + "data": { + "id": 351, + "meta": "...", + "name": "setpts", + "type": "V->V", + "description": "Set PTS for the output video frame.", + "inputs": [ + "v" + ], + "outputs": [ + "v" + ], + "params": [ + { + "name": "expr", + "type": "string", + "desc": "Expression determining the frame timestamp (default \"PTS\")", + "min": null, + "max": null, + "default": "PTS", + "value": "PTS-STARTPTS" + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": 60, + "y": -130 + }, + "positionAbsolute": { + "x": 60, + "y": -130 + }, + "width": 65, + "height": 50, + "dragging": false + }, + { + "id": "070a7983-c3aa-47af-af40-b7cc1d1beb7e", + "type": "ffmpeg", + "data": { + "id": 351, + "meta": "...", + "name": "setpts", + "type": "V->V", + "description": "Set PTS for the output video frame.", + "inputs": [ + "v" + ], + "outputs": [ + "v" + ], + "params": [ + { + "name": "expr", + "type": "string", + "desc": "Expression determining the frame timestamp (default \"PTS\")", + "min": null, + "max": null, + "default": "PTS", + "value": "PTS-STARTPTS" + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": 60, + "y": -70 + }, + "positionAbsolute": { + "x": 60, + "y": -70 + }, + "width": 65, + "height": 50, + "dragging": false + }, + { + "id": "8b5a5796-e55e-40c1-a6d1-c02810f36e9d", + "type": "ffmpeg", + "data": { + "id": 76, + "meta": "...", + "name": "atrim", + "type": "A->A", + "description": "Pick one continuous section from the input, drop the rest.", + "inputs": [ + "a" + ], + "outputs": [ + "a" + ], + "params": [ + { + "name": "start", + "type": "duration", + "desc": "Timestamp of the first frame that should be passed (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "1" + }, + { + "name": "starti", + "type": "duration", + "desc": "Timestamp of the first frame that should be passed (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "INT64_MAX" + }, + { + "name": "end", + "type": "duration", + "desc": "Timestamp of the first frame that should be dropped again (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "2" + }, + { + "name": "endi", + "type": "duration", + "desc": "Timestamp of the first frame that should be dropped again (default INT64_MAX)", + "min": null, + "max": null, + "default": "INT64_MAX", + "value": "INT64_MAX" + }, + { + "name": "start_pts", + "type": "int64", + "desc": "Timestamp of the first frame that should be passed (from I64_MIN to I64_MAX) (default I64_MIN)", + "min": -2000, + "max": 2000, + "default": -2000, + "value": -2000 + }, + { + "name": "end_pts", + "type": "int64", + "desc": "Timestamp of the first frame that should be dropped again (from I64_MIN to I64_MAX) (default I64_MIN)", + "min": -2000, + "max": 2000, + "default": -2000, + "value": -2000 + }, + { + "name": "duration", + "type": "duration", + "desc": "Maximum duration of the output (default 0)", + "min": null, + "max": null, + "default": "0", + "value": "0" + }, + { + "name": "durationi", + "type": "duration", + "desc": "Maximum duration of the output (default 0)", + "min": null, + "max": null, + "default": "0", + "value": "0" + }, + { + "name": "start_sample", + "type": "int64", + "desc": "Number of the first audio sample that should be passed to the output (from -1 to I64_MAX) (default -1)", + "min": -1, + "max": 2000, + "default": -1, + "value": -1 + }, + { + "name": "end_sample", + "type": "int64", + "desc": "Number of the first audio sample that should be dropped again (from 0 to I64_MAX) (default I64_MAX)", + "min": 0, + "max": 2000, + "default": 2000, + "value": 2000 + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": -40, + "y": -10 + }, + "positionAbsolute": { + "x": -40, + "y": -10 + }, + "width": 63, + "height": 50, + "dragging": false + }, + { + "id": "6b442773-04c1-4f44-befa-f83a497141af", + "type": "ffmpeg", + "data": { + "id": 445, + "meta": "..C", + "name": "concat", + "type": "N->N", + "description": "Concatenate audio and video streams.", + "inputs": [ + "v", + "a", + "v", + "a" + ], + "isCustom": true, + "outputs": [ + "v", + "a" + ], + "params": [ + { + "name": "n", + "type": "int", + "desc": "specify the number of segments (from 1 to INT_MAX) (default 2)", + "min": 1, + "max": 2000, + "default": 2, + "value": 2 + }, + { + "name": "v", + "type": "int", + "desc": "specify the number of video streams (from 0 to INT_MAX) (default 1)", + "min": 0, + "max": 2000, + "default": 1, + "value": 1 + }, + { + "name": "a", + "type": "int", + "desc": "specify the number of audio streams (from 0 to INT_MAX) (default 0)", + "min": 0, + "max": 2000, + "default": 0, + "value": "1" + }, + { + "name": "unsafe", + "type": "boolean", + "desc": "enable unsafe mode (default false)", + "min": null, + "max": null, + "default": "false", + "value": "false" + } + ], + "nodeType": "filter", + "enabled": true + }, + "nodeType": "filter", + "position": { + "x": 200, + "y": -60 + }, + "positionAbsolute": { + "x": 200, + "y": -60 + }, + "width": 71, + "height": 50, + "dragging": false + } + ], + "edges": [ + { + "source": "7d004403-7325-49fe-a1b1-2ccfd02e10eb", + "sourceHandle": "v_0", + "target": "36fdb883-b486-4d6d-a967-6c5e75fea2d4", + "targetHandle": "v_0", + "id": "xyflow__edge-7d004403-7325-49fe-a1b1-2ccfd02e10ebv_0-36fdb883-b486-4d6d-a967-6c5e75fea2d4v_0" + }, + { + "source": "7d004403-7325-49fe-a1b1-2ccfd02e10eb", + "sourceHandle": "v_0", + "target": "ca9bfea8-4e6e-4d37-86e8-2278f478311b", + "targetHandle": "v_0", + "id": "xyflow__edge-7d004403-7325-49fe-a1b1-2ccfd02e10ebv_0-ca9bfea8-4e6e-4d37-86e8-2278f478311bv_0" + }, + { + "source": "7d004403-7325-49fe-a1b1-2ccfd02e10eb", + "sourceHandle": "a_1", + "target": "8b5a5796-e55e-40c1-a6d1-c02810f36e9d", + "targetHandle": "a_0", + "id": "xyflow__edge-7d004403-7325-49fe-a1b1-2ccfd02e10eba_1-8b5a5796-e55e-40c1-a6d1-c02810f36e9da_0" + }, + { + "source": "7d004403-7325-49fe-a1b1-2ccfd02e10eb", + "sourceHandle": "a_1", + "target": "83608257-ca86-4e2b-bd15-8130dd57408f", + "targetHandle": "a_0", + "id": "xyflow__edge-7d004403-7325-49fe-a1b1-2ccfd02e10eba_1-83608257-ca86-4e2b-bd15-8130dd57408fa_0" + }, + { + "source": "36fdb883-b486-4d6d-a967-6c5e75fea2d4", + "sourceHandle": "v_0", + "target": "bbcf717d-709e-4195-8721-0beff2d3c724", + "targetHandle": "v_0", + "id": "xyflow__edge-36fdb883-b486-4d6d-a967-6c5e75fea2d4v_0-bbcf717d-709e-4195-8721-0beff2d3c724v_0" + }, + { + "source": "ca9bfea8-4e6e-4d37-86e8-2278f478311b", + "sourceHandle": "v_0", + "target": "070a7983-c3aa-47af-af40-b7cc1d1beb7e", + "targetHandle": "v_0", + "id": "xyflow__edge-ca9bfea8-4e6e-4d37-86e8-2278f478311bv_0-070a7983-c3aa-47af-af40-b7cc1d1beb7ev_0" + }, + { + "source": "8b5a5796-e55e-40c1-a6d1-c02810f36e9d", + "sourceHandle": "a_0", + "target": "297207f9-d013-4382-802c-6358227487ff", + "targetHandle": "a_0", + "id": "xyflow__edge-8b5a5796-e55e-40c1-a6d1-c02810f36e9da_0-297207f9-d013-4382-802c-6358227487ffa_0" + }, + { + "source": "83608257-ca86-4e2b-bd15-8130dd57408f", + "sourceHandle": "a_0", + "target": "2524a9ea-cc56-40f7-a815-c63f42cb27ac", + "targetHandle": "a_0", + "id": "xyflow__edge-83608257-ca86-4e2b-bd15-8130dd57408fa_0-2524a9ea-cc56-40f7-a815-c63f42cb27aca_0" + }, + { + "source": "bbcf717d-709e-4195-8721-0beff2d3c724", + "sourceHandle": "v_0", + "target": "6bcb655e-e9c0-427a-9eed-0c6ea4f52b2b", + "targetHandle": "v_0", + "id": "xyflow__edge-bbcf717d-709e-4195-8721-0beff2d3c724v_0-6bcb655e-e9c0-427a-9eed-0c6ea4f52b2bv_0" + }, + { + "source": "297207f9-d013-4382-802c-6358227487ff", + "sourceHandle": "a_0", + "target": "6bcb655e-e9c0-427a-9eed-0c6ea4f52b2b", + "targetHandle": "a_1", + "id": "xyflow__edge-297207f9-d013-4382-802c-6358227487ffa_0-6bcb655e-e9c0-427a-9eed-0c6ea4f52b2ba_1" + }, + { + "source": "070a7983-c3aa-47af-af40-b7cc1d1beb7e", + "sourceHandle": "v_0", + "target": "6bcb655e-e9c0-427a-9eed-0c6ea4f52b2b", + "targetHandle": "v_2", + "id": "xyflow__edge-070a7983-c3aa-47af-af40-b7cc1d1beb7ev_0-6bcb655e-e9c0-427a-9eed-0c6ea4f52b2bv_2" + }, + { + "source": "2524a9ea-cc56-40f7-a815-c63f42cb27ac", + "sourceHandle": "a_0", + "target": "6bcb655e-e9c0-427a-9eed-0c6ea4f52b2b", + "targetHandle": "a_3", + "id": "xyflow__edge-2524a9ea-cc56-40f7-a815-c63f42cb27aca_0-6bcb655e-e9c0-427a-9eed-0c6ea4f52b2ba_3" + }, + { + "source": "6bcb655e-e9c0-427a-9eed-0c6ea4f52b2b", + "sourceHandle": "v_0", + "target": "10ebcbf9-cb96-4cff-ad69-63b5cab9a7f1", + "targetHandle": "v_0", + "id": "xyflow__edge-6bcb655e-e9c0-427a-9eed-0c6ea4f52b2bv_0-10ebcbf9-cb96-4cff-ad69-63b5cab9a7f1v_0" + }, + { + "source": "6bcb655e-e9c0-427a-9eed-0c6ea4f52b2b", + "sourceHandle": "a_1", + "target": "10ebcbf9-cb96-4cff-ad69-63b5cab9a7f1", + "targetHandle": "a_1", + "id": "xyflow__edge-6bcb655e-e9c0-427a-9eed-0c6ea4f52b2ba_1-10ebcbf9-cb96-4cff-ad69-63b5cab9a7f1a_1" + }, + { + "source": "bbcf717d-709e-4195-8721-0beff2d3c724", + "sourceHandle": "v_0", + "target": "6b442773-04c1-4f44-befa-f83a497141af", + "targetHandle": "v_0", + "id": "xyflow__edge-bbcf717d-709e-4195-8721-0beff2d3c724v_0-6b442773-04c1-4f44-befa-f83a497141afv_0" + }, + { + "source": "297207f9-d013-4382-802c-6358227487ff", + "sourceHandle": "a_0", + "target": "6b442773-04c1-4f44-befa-f83a497141af", + "targetHandle": "a_1", + "id": "xyflow__edge-297207f9-d013-4382-802c-6358227487ffa_0-6b442773-04c1-4f44-befa-f83a497141afa_1" + }, + { + "source": "070a7983-c3aa-47af-af40-b7cc1d1beb7e", + "sourceHandle": "v_0", + "target": "6b442773-04c1-4f44-befa-f83a497141af", + "targetHandle": "v_2", + "id": "xyflow__edge-070a7983-c3aa-47af-af40-b7cc1d1beb7ev_0-6b442773-04c1-4f44-befa-f83a497141afv_2" + }, + { + "source": "2524a9ea-cc56-40f7-a815-c63f42cb27ac", + "sourceHandle": "a_0", + "target": "6b442773-04c1-4f44-befa-f83a497141af", + "targetHandle": "a_3", + "id": "xyflow__edge-2524a9ea-cc56-40f7-a815-c63f42cb27aca_0-6b442773-04c1-4f44-befa-f83a497141afa_3" + }, + { + "source": "6b442773-04c1-4f44-befa-f83a497141af", + "sourceHandle": "v_0", + "target": "10ebcbf9-cb96-4cff-ad69-63b5cab9a7f1", + "targetHandle": "v_0", + "id": "xyflow__edge-6b442773-04c1-4f44-befa-f83a497141afv_0-10ebcbf9-cb96-4cff-ad69-63b5cab9a7f1v_0" + }, + { + "source": "6b442773-04c1-4f44-befa-f83a497141af", + "sourceHandle": "a_1", + "target": "10ebcbf9-cb96-4cff-ad69-63b5cab9a7f1", + "targetHandle": "a_1", + "id": "xyflow__edge-6b442773-04c1-4f44-befa-f83a497141afa_1-10ebcbf9-cb96-4cff-ad69-63b5cab9a7f1a_1" + } + ], + "command": "ffmpeg -i shoe.mp4 -filter_complex \"[0:v]trim=start=3:end=4[3];[0:v]trim=start=1:end=2[1];[0:a]atrim=start=3:end=4[7];[0:a]atrim=start=1:end=2[5];[1]setpts=expr=PTS-STARTPTS[2];[3]setpts=expr=PTS-STARTPTS[4];[5]asetpts=expr=PTS-STARTPTS[6];[7]asetpts=expr=PTS-STARTPTS[8];[2][6][4][8]concat=a=1[out_370][out_869]\" -map [out_370] -map [out_869] out.mp4" +} \ No newline at end of file diff --git a/src/App.svelte b/src/App.svelte index 81fd17f..1f5b5dd 100644 --- a/src/App.svelte +++ b/src/App.svelte @@ -31,6 +31,8 @@ { name: "Slow Down Smoothly", url: "/examples/smooth_slow.json" }, { name: "Video Grid", url: "/examples/grid.json" }, { name: "Mirror", url: "/examples/mirror.json" }, + { name: "Cut Parts From Video And Audio", url: "/examples/trim-concat.json" }, + { name: 'Cut Parts Apply Blur And Grayscale', url: '/examples/cut-bluer-grayscale.json' }, ]; let videoValue = "/" + $inputs[0].name; @@ -121,7 +123,7 @@ ); if (outname.endsWith("mp4")) { setTimeout(() => { - vidPlayerRef.seekToNextFrame(); + vidPlayerRef?.seekToNextFrame?.(); }, 100); } } catch (e) { diff --git a/src/DynamicFilterModal.svelte b/src/DynamicFilterModal.svelte new file mode 100644 index 0000000..d5e2196 --- /dev/null +++ b/src/DynamicFilterModal.svelte @@ -0,0 +1,171 @@ + + +{#if showModal} + { + // force update the ui + modalInputs = [ + ...selectedFilter.inputs.filter((i) => i === "v" || i === "a"), + ...modalInputs, + ]; + modalOutputs = [ + ...selectedFilter.outputs.filter((i) => i === "v" || i === "a"), + ...modalOutputs, + ]; + + selectedFilter.inputs = modalInputs; + selectedFilter.outputs = modalOutputs; + selectedFilter.isCustom = true; // for handling the ordering of the inputs in the previewCommand in store.js + + add(selectedFilter); + selectedFilter = null; + showModal = false; + }} + > +

+ {selectedFilter.name} + (Either the input or output can have dynamic types or a variable number + of streams, and the order in which they are entered is important.) +

{selectedFilter.description}

+

+ +
+
+ +
+ +
    + {#if selectedFilter.inputs.length === 0} +
  • None
  • + {:else} + {#each selectedFilter.inputs as input} + {#if input === "v"} +
  • Video
  • + {:else if input === "a"} +
  • Audio
  • + {:else} + + {/if} + {/each} + {/if} + + {#each modalInputs as customInput, i} +
  • + +
  • + {/each} +
+
+ +
+ +
+ +
+
    + {#if selectedFilter.outputs.length === 0} +
  • None
  • + {:else} + {#each selectedFilter.outputs as output} + {#if output === "v"} +
  • Video
  • + {:else if output === "a"} +
  • Audio
  • + {:else} + + {/if} + {/each} + {/if} + + {#each modalOutputs as customOutput, i} +
  • + +
  • + {/each} +
+
+
+
+{/if} + + diff --git a/src/FilterPicker.svelte b/src/FilterPicker.svelte index 96b3aa3..f0301e2 100644 --- a/src/FilterPicker.svelte +++ b/src/FilterPicker.svelte @@ -2,21 +2,29 @@ import uFuzzy from "@leeoniya/ufuzzy"; import FILTERS from "./filters.json"; import { addNode } from "./stores.js"; + import DynamicFilterModal from "./DynamicFilterModal.svelte"; export let select = "video"; $: selectedFilters = selectFilters(select); $: allfilters = [...selectedFilters]; let q = ""; + let selectedFilter = null; + let showModal = false; + const uf = new uFuzzy(); function selectFilters(sel) { if (sel == "video") { - // return FILTERS.filter((f) => f.type.startsWith("V") || f.type.endsWith("V")); - return FILTERS.filter((f) => f.type[0] === "V" || f.type === "N->V"); + // add support to N->N inputs + return FILTERS.filter( + (f) => f.type[0] === "V" || f.type === "N->V" || f.type === "N->N" + ); } else if (sel == "audio") { - // return FILTERS.filter((f) => f.type.startsWith("A") || f.type.endsWith("A")); - return FILTERS.filter((f) => f.type[0] === "A" || f.type === "N->A"); + // add support to N->N inputs + return FILTERS.filter( + (f) => f.type[0] === "A" || f.type === "N->A" || f.type === "N->N" + ); } else { return [...FILTERS]; } @@ -62,14 +70,41 @@
{#each allfilters as f} -
add(f)}> -
{f.name} {f.type.replace("->", "⇒")}
+ + +
{ + // if input or output is N (dynamic) show modal + if ( + f.type.startsWith("N") || + f.type.endsWith("N") || + f.type === "N" + ) { + // deep copy current filter data so when overwriting it, it doesn't affect the original + selectedFilter = {...f, inputs: [...f.inputs], outputs: [...f.outputs]}; + showModal = true; + } else { + add(f); + } + }} + > +
+ {f.name} {f.type.replace("->", "⇒")} +
{f.description}
{/each}
+{#if showModal} + +{/if} + diff --git a/src/stores.js b/src/stores.js index 33dbf90..5b4484e 100644 --- a/src/stores.js +++ b/src/stores.js @@ -42,6 +42,7 @@ export const previewCommand = derived([edges, nodes], ([$edges, $nodes]) => { let finalCommand = []; let filtergraph = []; let labelIndex = 1; + let outs = 0; const edgeIds = {}; const inputIdMap = {}; @@ -70,7 +71,9 @@ export const previewCommand = derived([edges, nodes], ([$edges, $nodes]) => { label = labelIndex; labelIndex++; } else if (inNode.nodeType === "filter" && outNode.nodeType === "output") { - label = "out_" + type; + // this was breaking when it was IDing by the type, as work around i made it current-outs-count based ID and it works + label = 'out_' + outs; + outs++ } else if (inNode.nodeType === "input" && outNode.nodeType === "output") { label = "FILTERLESS_" + inputIdMap[inNode.id] + ":" + type; } else { @@ -97,7 +100,14 @@ export const previewCommand = derived([edges, nodes], ([$edges, $nodes]) => { let cmd = { weight: 0, in: [], out: [], cmd: "" }; const outs = $edges.filter((e) => e.source == n.id); - const ins = $edges.filter((e) => e.target == n.id); + let ins = $edges.filter((e) => e.target == n.id) + + // respect the user define input order (not just when the edges were created) this fixes a lot of issues whit complex filter like concat (v a v a) -> (v a) + if (n?.data?.isCustom) { + ins = ins.sort((a, b) => { + return Number(a.targetHandle.split('_')[1]) - Number(b.targetHandle.split('_')[1]) + }) + } if (outs.length == 0 && ins.length == 0) continue; @@ -155,13 +165,11 @@ export const previewCommand = derived([edges, nodes], ([$edges, $nodes]) => { finalCommand.push("-filter_complex", fg); - if (fg.includes("[out_a]")) { - finalCommand.push("-map", '"[out_a]"'); - } + const getMappers = fg.match(/\[out_\d+\]/g) || [] // get all the out_0 out_1 etc - if (fg.includes("[out_v]")) { - finalCommand.push("-map", '"[out_v]"'); - } + for (let m of getMappers) { + finalCommand.push('-map', `"${m}"`) + } for (let m of mediaMaps) { finalCommand.push("-map", m); diff --git a/tests/unit-tests/commandbuilder.js b/tests/unit-tests/commandbuilder.js index c1dae96..1f80f0d 100644 --- a/tests/unit-tests/commandbuilder.js +++ b/tests/unit-tests/commandbuilder.js @@ -76,7 +76,7 @@ describe("Command builder", () => { resetNodes(); addNode(makeFilter("filter", "V->V"), "filter"); expect(get(previewCommand).join(" ")).toBe( - `ffmpeg -i punch.mp4 -filter_complex "[0:v]filter[out_v]" -map "[out_v]" -map 0:a out.mp4` + `ffmpeg -i punch.mp4 -filter_complex "[0:v]filter[out_0]" -map "[out_0]" -map 0:a out.mp4` ); }); @@ -84,7 +84,7 @@ describe("Command builder", () => { resetNodes(); addNode(makeFilter("filter", "a->a"), "filter"); expect(get(previewCommand).join(" ")).toBe( - `ffmpeg -i punch.mp4 -filter_complex "[0:a]filter[out_a]" -map "[out_a]" -map 0:v out.mp4` + `ffmpeg -i punch.mp4 -filter_complex "[0:a]filter[out_0]" -map "[out_0]" -map 0:v out.mp4` ); }); @@ -93,7 +93,7 @@ describe("Command builder", () => { addNode(makeFilter("afilter", "a->a"), "filter"); addNode(makeFilter("vfilter", "v->v"), "filter"); expect(get(previewCommand).join(" ")).toBe( - `ffmpeg -i punch.mp4 -filter_complex "[0:a]afilter[out_a];[0:v]vfilter[out_v]" -map "[out_a]" -map "[out_v]" out.mp4` + `ffmpeg -i punch.mp4 -filter_complex "[0:a]afilter[out_1];[0:v]vfilter[out_0]" -map "[out_1]" -map "[out_0]" out.mp4` ); }); @@ -103,7 +103,7 @@ describe("Command builder", () => { addNode(makeFilter("vfilter2", "v->v"), "filter"); addNode(makeFilter("vfilter3", "v->v"), "filter"); expect(get(previewCommand).join(" ")).toBe( - `ffmpeg -i punch.mp4 -filter_complex "[0:v]vfilter,vfilter2,vfilter3[out_v]" -map "[out_v]" -map 0:a out.mp4` + `ffmpeg -i punch.mp4 -filter_complex "[0:v]vfilter,vfilter2,vfilter3[out_0]" -map "[out_0]" -map 0:a out.mp4` ); }); @@ -113,7 +113,7 @@ describe("Command builder", () => { addNode(makeFilter("vfilter", "v->v"), "filter"); addNode(makeFilter("vfilter2", "v->v"), "filter"); expect(get(previewCommand).join(" ")).toBe( - `ffmpeg -i punch.mp4 -filter_complex "[0:v]vfilter[1];[0:a]afilter[out_a];[1]vfilter2[out_v]" -map "[out_a]" -map "[out_v]" out.mp4` + `ffmpeg -i punch.mp4 -filter_complex "[0:v]vfilter[1];[0:a]afilter[out_1];[1]vfilter2[out_0]" -map "[out_1]" -map "[out_0]" out.mp4` ); }); @@ -121,31 +121,31 @@ describe("Command builder", () => { const examples = [ [ "crop_trim.json", - `ffmpeg -i punch.mp4 -filter_complex "[0:v]crop=out_w=iw/2,trim=start=1.7:duration=0.5[out_v]" -map "[out_v]" out.gif`, + `ffmpeg -i punch.mp4 -filter_complex "[0:v]crop=out_w=iw/2,trim=start=1.7:duration=0.5[out_0]" -map "[out_0]" out.gif`, ], [ "grid.json", - `ffmpeg -i punch.mp4 -i shoe.mp4 -i shoe.mp4 -i punch.mp4 -filter_complex "[1:v][0:v][3:v][2:v]xstack=inputs=4:grid=2x2:shortest=true[out_v]" -map "[out_v]" out.mp4`, + `ffmpeg -i punch.mp4 -i shoe.mp4 -i shoe.mp4 -i punch.mp4 -filter_complex "[1:v][0:v][3:v][2:v]xstack=inputs=4:grid=2x2:shortest=true[out_3]" -map "[out_3]" out.mp4`, ], [ "scale_overlay.json", - `ffmpeg -i punch.mp4 -i shoe.mp4 -filter_complex "[0:v]scale=w=120:h=120:force_original_aspect_ratio=increase[1];[1:v][1]overlay=x=290:y=50[out_v]" -map "[out_v]" out.mp4`, + `ffmpeg -i punch.mp4 -i shoe.mp4 -filter_complex "[0:v]scale=w=120:h=120:force_original_aspect_ratio=increase[1];[1:v][1]overlay=x=290:y=50[out_1]" -map "[out_1]" out.mp4`, ], [ "smooth_slow.json", - `ffmpeg -i punch.mp4 -filter_complex "[0:v]setpts=expr=2*PTS,minterpolate=fps=66[out_v];[0:a]asetpts=expr=2*PTS[out_a]" -map "[out_a]" -map "[out_v]" out.mp4`, + `ffmpeg -i punch.mp4 -filter_complex "[0:v]setpts=expr=2*PTS,minterpolate=fps=66[out_0];[0:a]asetpts=expr=2*PTS[out_1]" -map "[out_0]" -map "[out_1]" out.mp4`, ], [ "speedup.json", - `ffmpeg -i punch.mp4 -filter_complex "[0:v]setpts=expr=0.5*PTS[out_v]" -map "[out_v]" out.mp4`, + `ffmpeg -i punch.mp4 -filter_complex "[0:v]setpts=expr=0.5*PTS[out_0]" -map "[out_0]" out.mp4`, ], [ "text.json", - `ffmpeg -i punch.mp4 -filter_complex "[0:v]drawtext=fontfile=comic.ttf:text=LOL:fontcolor=red:bordercolor=white:boxborderw=3:fontsize=100:x=300:y=150:borderw=5[out_v]" -map "[out_v]" -map 0:a out.mp4`, + `ffmpeg -i punch.mp4 -filter_complex "[0:v]drawtext=fontfile=comic.ttf:text=LOL:fontcolor=red:bordercolor=white:boxborderw=3:fontsize=100:x=300:y=150:borderw=5[out_0]" -map "[out_0]" -map 0:a out.mp4`, ], [ "xfade.json", - `ffmpeg -i punch.mp4 -i shoe.mp4 -filter_complex "[0:v][1:v]xfade=transition=radial:duration=3[out_v]" -map "[out_v]" -map 0:a out.mp4`, + `ffmpeg -i punch.mp4 -i shoe.mp4 -filter_complex "[0:v][1:v]xfade=transition=radial:duration=3[out_1]" -map "[out_1]" -map 0:a out.mp4`, ], ];