draggable

This commit is contained in:
Sam Lavigne 2023-08-20 12:01:50 -04:00
parent 64473bd201
commit 99b4143dd6
5 changed files with 76 additions and 39 deletions

20
package-lock.json generated
View File

@ -15,6 +15,8 @@
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.4.2",
"svelte": "^4.0.5",
"svelte-dnd-action": "^0.9.25",
"uuid": "^9.0.0",
"vite": "^4.4.5"
}
},
@ -819,6 +821,15 @@
"node": ">=16"
}
},
"node_modules/svelte-dnd-action": {
"version": "0.9.25",
"resolved": "https://registry.npmjs.org/svelte-dnd-action/-/svelte-dnd-action-0.9.25.tgz",
"integrity": "sha512-6cW+5b+xYn2w1KaYdzdMiWOn4wzFq9KHpWMyYwixN4M+94RFw9Sn8GiRI0EPFoT5r6/RVD3d3lZ/OjllZlpYbg==",
"dev": true,
"peerDependencies": {
"svelte": ">=3.23.0"
}
},
"node_modules/svelte-hmr": {
"version": "0.15.3",
"resolved": "https://registry.npmjs.org/svelte-hmr/-/svelte-hmr-0.15.3.tgz",
@ -831,6 +842,15 @@
"svelte": "^3.19.0 || ^4.0.0"
}
},
"node_modules/uuid": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
"integrity": "sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==",
"dev": true,
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/vite": {
"version": "4.4.9",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.4.9.tgz",

View File

@ -11,6 +11,8 @@
"devDependencies": {
"@sveltejs/vite-plugin-svelte": "^2.4.2",
"svelte": "^4.0.5",
"svelte-dnd-action": "^0.9.25",
"uuid": "^9.0.0",
"vite": "^4.4.5"
},
"dependencies": {

View File

@ -7,10 +7,10 @@
import FilterPicker from "./FilterPicker.svelte";
import { FFmpeg } from "@ffmpeg/ffmpeg";
import { fetchFile, toBlobURL } from "@ffmpeg/util";
import {dndzone} from "svelte-dnd-action";
const baseURL = "https://unpkg.com/@ffmpeg/core@0.12.2/dist/esm";
// const baseURL = "";
// const videoURL = "https://ffmpegwasm.netlify.app/video/video-15s.avi";
const TIMEOUT = 40000;
const ffmpeg = new FFmpeg();
@ -38,29 +38,37 @@
}
async function transcode() {
// try {
message = "Start transcoding";
videoValue = null;
rendering = true;
for (let vid of $inputs) {
await ffmpeg.writeFile(vid, await fetchFile("/" + vid));
}
// const infile = await ffmpeg.readFile("example.mp4");
// videoValue = URL.createObjectURL(new Blob([infile.buffer], { type: "video/mp4" }));
// console.log("VIDEO", videoValue);
const clist = commandList();
console.log(clist);
// await ffmpeg.exec(["-hide_banner", "-i", "example.mp4", "-vf", "negate", "out.mp4", "-y"]);
await ffmpeg.exec(clist, TIMEOUT);
// await ffmpeg.exec(["-f", "lavfi", "-i", "color=size=1280x720:rate=25:color=red", "-t", "5", "out.mp4"])
message = "Complete transcoding";
const data = await ffmpeg.readFile("out.mp4");
rendering = false;
videoValue = URL.createObjectURL(new Blob([data.buffer], { type: "video/mp4" }));
rendering = false;
// console.log("VIDEO", videoValue);
// } catch (error) {
// console.log(error);
// }
}
async function loadFFmpeg() {
ffmpeg.on("log", ({ message: msg }) => {
console.log(msg);
log += msg + "\n";
logbox.scrollTop = logbox.scrollHeight;
// message = msg;
});
await ffmpeg.load({
coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, "text/javascript"),
wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, "application/wasm"),
// workerURL: await toBlobURL(`${baseURL}/ffmpeg-core.worker.js`, "text/javascript"),
});
console.log(ffmpeg);
ffmpegLoaded = true;
}
function updateCommand() {
@ -116,29 +124,20 @@
command.push("-pix_fmt");
command.push("yuv420p");
command.push("out.mp4");
return command;
}
function handleFilterSort(e) {
filters.set(e.detail.items);
}
inputs.subscribe(updateCommand);
output.subscribe(updateCommand);
filters.subscribe(updateCommand);
onMount(async () => {
ffmpeg.on("log", ({ message: msg }) => {
console.log(msg);
log += msg + "\n";
logbox.scrollTop = logbox.scrollHeight;
// message = msg;
});
await ffmpeg.load({
// coreURL: `${baseURL}/ffmpeg-core.js`,
// wasmURL: `${baseURL}/ffmpeg-core.wasm`,
// workerURL: `${baseURL}/ffmpeg-core.worker.js`,
coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, "text/javascript"),
wasmURL: await toBlobURL(`${baseURL}/ffmpeg-core.wasm`, "application/wasm"),
// workerURL: await toBlobURL(`${baseURL}/ffmpeg-core.worker.js`, "text/javascript"),
});
ffmpegLoaded = true;
loadFFmpeg();
});
</script>
@ -148,18 +147,16 @@
<p>
A tool to help you explore FFmpeg filters and options. To use: select one or more input videos
(there are currently two options), export and add some filters, and then hit "render" to
preview the output in browser.
Note: this is a work in progress, many things may still be broken! By <a href="https://lav.io"
>Sam Lavigne</a
>.
preview the output in browser. Note: this is a work in progress, many things may still be
broken! By <a href="https://lav.io">Sam Lavigne</a>.
</p>
</section>
<!-- {message} -->
<section class="command">
<textarea class="actual-command" bind:this={commandRef}>{command}</textarea>
<textarea readonly class="actual-command" bind:this={commandRef}>{command}</textarea>
<div>
<button on:click={copyCommand}>Copy</button>
<button on:click={render} disabled={!ffmpegLoaded}>
<button on:click={render} disabled={!ffmpegLoaded || rendering}>
{#if ffmpegLoaded}
{#if rendering}
Rendering...
@ -185,10 +182,13 @@
<section class="log">
<h3>FFmpeg Log</h3>
<textarea class="the-log" bind:this={logbox}>{log}</textarea>
<textarea readonly class="the-log" bind:this={logbox}>{log}</textarea>
</section>
<section class="preview">
{#if rendering}
<div class="rendering-video"><span>Rendering...</span></div>
{/if}
<video controls src={videoValue} />
</section>
@ -203,10 +203,10 @@
<div class="filter-picker">
<FilterPicker select={"video"} />
</div>
<div class="filters-holder">
{#each $filters as f, index}
<div class="filters-holder" use:dndzone={{items:$filters}} on:consider={handleFilterSort} on:finalize={handleFilterSort}>
{#each $filters as f (f.id)}
<div class="filter">
<Filter bind:filter={f} {index} />
<Filter bind:filter={f} />
</div>
{/each}
</div>
@ -247,6 +247,7 @@
.preview {
grid-area: prv;
position: relative;
}
.output {
@ -326,4 +327,16 @@
resize: none;
flex: 1;
}
.rendering-video {
position: absolute;
width: 100%;
height: 100%;
z-index: 2;
background-color: rgba(255, 255, 255, 0.8);
top: 0;
left: 0;
display: grid;
align-items: center;
justify-content: center;
}
</style>

View File

@ -1,5 +1,4 @@
<script>
import FILTERS from "./filters.json";
import { filters } from "./stores.js";
export let filter = {
@ -8,10 +7,10 @@
description: "",
};
export let index;
let show = false;
function remove() {
const index = $filters.findIndex((f) => f.id === filter.id);
$filters.splice(index, 1);
$filters = $filters;
}
@ -21,7 +20,9 @@
<div class="head">
<div class="name"><h3>{filter.name}<h3></div>
<div>
{#if filter.params}
<button on:click={() => show = !show}>{show ? "Hide" : "Show"} Options</button>
{/if}
<button on:click={remove}>X</button>
</div>
</div>

View File

@ -1,5 +1,6 @@
<script>
import uFuzzy from "@leeoniya/ufuzzy";
import { v4 as uuidv4 } from 'uuid';
import FILTERS from "./filters.json";
import { filters } from "./stores.js";
@ -27,7 +28,7 @@
}
function add(f) {
const newFilter = { ...f };
const newFilter = { ...f, filterId: f.id, id: uuidv4() };
if (f.params) {
newFilter.params = f.params.map((p) => {
p.value = null;