Skip to content

Commit

Permalink
Improve video trimming and error handling (#6566)
Browse files Browse the repository at this point in the history
* amend trimming logic and return original file when error occurs

* add interactive story test

* add changeset

* add changeset

---------

Co-authored-by: gradio-pr-bot <gradio-pr-bot@users.noreply.github.com>
  • Loading branch information
hannahblair and gradio-pr-bot committed Dec 4, 2023
1 parent b639e04 commit d548202
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 31 deletions.
6 changes: 6 additions & 0 deletions .changeset/purple-deer-double.md
@@ -0,0 +1,6 @@
---
"@gradio/video": patch
"gradio": patch
---

fix:Improve video trimming and error handling
62 changes: 43 additions & 19 deletions js/video/Video.stories.svelte
Expand Up @@ -3,30 +3,15 @@
import Video from "./Index.svelte";
import { format } from "svelte-i18n";
import { get } from "svelte/store";
import { userEvent, within } from "@storybook/testing-library";
import { waitFor } from "@testing-library/dom";
</script>

<Meta
title="Components/Video"
component={Video}
argTypes={{
video: {
control: "text",
description:
"A path or URL for the default value that Video component is going to take. Can also be a tuple consisting of (video filepath, subtitle filepath). If a subtitle file is provided, it should be of type .srt or .vtt. Or can be callable, in which case the function will be called whenever the app loads to set the initial value of the component.",
name: "value"
},
autoplay: {
control: [true, false],
description: "Whether to autoplay the video on load",
name: "autoplay",
value: true
}
}}
/>
<Meta title="Components/Video" component={Video} />

<div>
<Template let:args>
<Video {...args} i18n={get(format)} />
<Video i18n={get(format)} {...args} />
</Template>
</div>

Expand Down Expand Up @@ -72,3 +57,42 @@
value: null
}}
/>

<Story
name="Trim video"
args={{
value: {
video: {
path: "https://gradio-static-files.s3.us-west-2.amazonaws.com/world.mp4",
url: "https://gradio-static-files.s3.us-west-2.amazonaws.com/world.mp4",
orig_name: "world.mp4"
}
},
label: "world video",
show_label: true,
interactive: "true",
sources: ["upload"],
width: 400
}}
play={async ({ canvasElement }) => {
const canvas = within(canvasElement);

const trimButton = canvas.getByLabelText("Trim video to selection");

userEvent.click(trimButton);

await new Promise((r) => setTimeout(r, 4000));

const rightHandle = canvas.getByLabelText(
"end drag handle for trimming video"
);

userEvent.click(rightHandle);

for (let i = 0; i < 4; i++) {
await userEvent.keyboard("[ArrowRight]");
}

userEvent.click(canvas.getByText("Trim"));
}}
/>
5 changes: 5 additions & 0 deletions js/video/shared/VideoControls.svelte
Expand Up @@ -28,6 +28,11 @@
const minutes = Math.floor(seconds / 60);
const secondsRemainder = Math.round(seconds) % 60;
const paddedSeconds = `0${secondsRemainder}`.slice(-2);
if (Number.isNaN(minutes) || Number.isNaN(secondsRemainder)) {
return "00:00";
}
return `${minutes}:${paddedSeconds}`;
};
Expand Down
2 changes: 2 additions & 0 deletions js/video/shared/VideoTimeline.svelte
Expand Up @@ -156,6 +156,7 @@
{:else}
<div id="timeline" class="thumbnail-wrapper">
<button
aria-label="start drag handle for trimming video"
class="handle left"
on:mousedown={() => startDragging("left")}
on:blur={stopDragging}
Expand All @@ -176,6 +177,7 @@
<img src={thumbnail} alt={`frame-${i}`} draggable="false" />
{/each}
<button
aria-label="end drag handle for trimming video"
class="handle right"
on:mousedown={() => startDragging("right")}
on:blur={stopDragging}
Expand Down
27 changes: 15 additions & 12 deletions js/video/shared/utils.ts
Expand Up @@ -66,15 +66,19 @@ export async function trimVideo(
endTime: number,
videoElement: HTMLVideoElement
): Promise<any> {
const videoUrl = videoElement.src;
const mimeType = lookup(videoElement.src) || "video/mp4";
const blobUrl = await toBlobURL(videoUrl, mimeType);
const response = await fetch(blobUrl);
const vidBlob = await response.blob();
const type = getVideoExtensionFromMimeType(mimeType) || "mp4";
const inputName = `input.${type}`;
const outputName = `output.${type}`;

try {
const videoUrl = videoElement.src;
const mimeType = lookup(videoElement.src) || "video/mp4";
const blobUrl = await toBlobURL(videoUrl, mimeType);
const response = await fetch(blobUrl);
const vidBlob = await response.blob();
const type = getVideoExtensionFromMimeType(mimeType) || "mp4";
const inputName = `input.${type}`;
const outputName = `output.${type}`;
if (startTime === 0 && endTime === 0) {
return vidBlob;
}

await ffmpeg.writeFile(
inputName,
Expand All @@ -84,10 +88,8 @@ export async function trimVideo(
let command = [
"-i",
inputName,
"-ss",
startTime.toString(),
"-to",
endTime.toString(),
...(startTime !== 0 ? ["-ss", startTime.toString()] : []),
...(endTime !== 0 ? ["-to", endTime.toString()] : []),
"-c:a",
"copy",
outputName
Expand All @@ -102,6 +104,7 @@ export async function trimVideo(
return outputBlob;
} catch (error) {
console.error("Error initializing FFmpeg:", error);
return vidBlob;
}
}

Expand Down

0 comments on commit d548202

Please sign in to comment.