draggable
This commit is contained in:
parent
64473bd201
commit
99b4143dd6
|
@ -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",
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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,31 +38,39 @@
|
|||
}
|
||||
|
||||
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() {
|
||||
const cInputs = $inputs.map((i) => `-i ${i}`).join(" ");
|
||||
|
||||
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
<button on:click={() => show = !show}>{show ? "Hide" : "Show"} Options</button>
|
||||
{#if filter.params}
|
||||
<button on:click={() => show = !show}>{show ? "Hide" : "Show"} Options</button>
|
||||
{/if}
|
||||
<button on:click={remove}>X</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue