Skip to content

Commit

Permalink
move download buttons inside of list items
Browse files Browse the repository at this point in the history
  • Loading branch information
malerba118 committed Jun 17, 2024
2 parents 8531af2 + 26865b0 commit 7a56683
Show file tree
Hide file tree
Showing 6 changed files with 168 additions and 24 deletions.
75 changes: 51 additions & 24 deletions examples/sound-effects/video-to-sfx/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { observer } from "mobx-react";
import { cn } from "@/lib/utils";
import { autorun, reaction, when } from "mobx";

export const HoverOverlay = ({ className }: { className?: string }) => {
const HoverOverlay = ({ className }: { className?: string }) => {
return (
<div
className={cn(
Expand All @@ -25,6 +25,9 @@ export const HoverOverlay = ({ className }: { className?: string }) => {
};
import { convertVideoToSFX } from "@/lib/videoToSFX";
import { useMutation } from "@tanstack/react-query";
import { DownloadIcon } from "lucide-react";
import { Button } from "@/components/ui/button";
import { mergeAndDownload } from "@/lib/mergeAndDownload";

const LoadingIndicator = () => {
const { ref, replay } = useScramble({
Expand Down Expand Up @@ -273,16 +276,24 @@ const Home = observer(() => {
</motion.div>
<motion.div className="stack gap-4 px-6 w-full">
{orchestrator.sfxPlayers.map((player, index) => (
<SoundEffect
key={index}
index={index}
onPlay={() => {
orchestrator.play(index);
}}
onPause={() => orchestrator.stop()}
player={player}
active={orchestrator.activeIndex === index}
/>
<div className="flex items-center">
<SoundEffect
key={"sound-effect" + index}
index={index}
onPlay={() => orchestrator.play(index)}
onPause={() => orchestrator.stop()}
player={player}
active={orchestrator.activeIndex === index}
onDownload={() => {
const url = orchestrator.getAudioUrl(index);
if (!file || !url) {
window.alert("Error downloading");
return;
}
mergeAndDownload(file, url);
}}
/>
</div>
))}
</motion.div>
</>
Expand Down Expand Up @@ -331,12 +342,14 @@ const SoundEffect = observer(
onPlay,
onPause,
active,
onDownload,
}: {
index: number;
player: AudioPlayer;
onPlay: () => void;
onPause: () => void;
active: boolean;
onDownload: () => void;
}) => {
return (
<motion.button
Expand All @@ -346,7 +359,7 @@ const SoundEffect = observer(
scale: 1,
transition: { ...springs.xxxslow(), delay: index * 0.1 },
}}
className="group relative h-16 rounded-xl w-full focus-visible:ring-gray-800 focus-visible:ring-2 focus-visible:outline-none"
className="flex justify-between group relative h-16 rounded-xl w-full focus-visible:ring-gray-800 focus-visible:ring-2 focus-visible:outline-none"
onClick={() => {
if (player.playing) {
onPause?.();
Expand All @@ -366,19 +379,33 @@ const SoundEffect = observer(
}}
>
<HoverOverlay className={cn(active && "opacity-20 inset-0")} />
<div className="overlay inset-4">
<Waveform player={player} barBgColor="bg-gray-900/30" />
<Mask
className="overlay"
image={masks.linear({
direction: "to-right",
opacities: [1, 1, 0, 0],
positions: [0, player.progress - 0.001, player.progress, 1],
})}
>
<Waveform player={player} barBgColor="bg-gray-900/100" />
</Mask>
<div className="relative flex-1 h-full rounded-inherit hstack gap-1">
<div className="overlay inset-4">
<Waveform player={player} barBgColor="bg-gray-900/30" />
<Mask
className="overlay"
image={masks.linear({
direction: "to-right",
opacities: [1, 1, 0, 0],
positions: [0, player.progress - 0.001, player.progress, 1],
})}
>
<Waveform player={player} barBgColor="bg-gray-900/100" />
</Mask>
</div>
</div>
<Button
variant="outline"
size="icon"
onClick={e => {
e.stopPropagation();
onDownload?.();
}}
key={"download" + index}
className="self-center mr-3 rounded-full bg-transparent hover:bg-white/20 active:bg-white/20 border-gray-800/15"
>
<DownloadIcon size={16} />
</Button>
</motion.button>
);
}
Expand Down
5 changes: 5 additions & 0 deletions examples/sound-effects/video-to-sfx/app/state/orchestrator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,9 @@ export class Orchestrator {
stop() {
this.playing = false;
}

getAudioUrl(index: number) {
const player = this.sfxPlayers[index];
return player.data;
}
}
2 changes: 2 additions & 0 deletions examples/sound-effects/video-to-sfx/app/state/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,13 @@ export class AudioPlayer {
audio: HTMLAudioElement;
progress: number = 0;
playing: boolean;
data: string;

constructor(data: string) {
this.audioLoaded = false;
this.waveformLoaded = false;
this.playing = false;
this.data = data;
this._player = new Tone.Player(
data,
action(() => {
Expand Down
69 changes: 69 additions & 0 deletions examples/sound-effects/video-to-sfx/lib/mergeAndDownload.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
export async function mergeAndDownload(
videoFile: File | null,
audioData: string
) {
const { FFmpeg } = await import("@ffmpeg/ffmpeg");
const { fetchFile, toBlobURL } = await import("@ffmpeg/util");
const ffmpeg = new FFmpeg();

const load = async () => {
const baseURL = "https://unpkg.com/@ffmpeg/[email protected]/dist/umd";
ffmpeg.on("log", ({ message }) => {
console.log(message);
});
await ffmpeg.load({
coreURL: await toBlobURL(`${baseURL}/ffmpeg-core.js`, "text/javascript"),
wasmURL: await toBlobURL(
`${baseURL}/ffmpeg-core.wasm`,
"application/wasm"
),
});
};
const process = async () => {
console.log("transcoding");
if (!videoFile) {
throw new Error("No video file");
}

if (videoFile) {
await ffmpeg.writeFile(
"input.mp4",
await fetchFile(URL.createObjectURL(videoFile))
);
}

await ffmpeg.writeFile("audio.mpeg", await fetchFile(audioData));

await ffmpeg.exec(["-v", "error", "-i", "input.mp4", "-f", "null", "-"]);

await ffmpeg.exec(["-v", "error", "-i", "audio.mpeg", "-f", "null", "-"]);

await ffmpeg.exec([
"-v",
"verbose",
"-i",
"input.mp4",
"-i",
"audio.mpeg",
"-c:v",
"copy", // Copy the video codec
"-c:a",
"aac", // Transcode audio to AAC
"-strict",
"experimental",
"output.mp4",
]);
console.log("transcoding completed");
const data = await ffmpeg.readFile("output.mp4");
const final_url = URL.createObjectURL(
new Blob([data.buffer], { type: "video/mp4" })
);
const downloadLinkFinalVideo = document.createElement("a");
downloadLinkFinalVideo.href = final_url;
downloadLinkFinalVideo.download = "final_output.mp4";
downloadLinkFinalVideo.click();
};

await load();
await process();
}
38 changes: 38 additions & 0 deletions examples/sound-effects/video-to-sfx/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions examples/sound-effects/video-to-sfx/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
"lint": "next lint"
},
"dependencies": {
"@ffmpeg/ffmpeg": "^0.12.10",
"@ffmpeg/util": "^0.12.1",
"@hookform/resolvers": "^3.6.0",
"@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-alert-dialog": "^1.0.5",
Expand Down Expand Up @@ -46,6 +48,7 @@
"date-fns": "^3.6.0",
"embla-carousel-react": "^8.1.5",
"framer-motion": "^11.2.10",
"geist": "^1.3.0",
"input-otp": "^1.2.4",
"lodash": "^4.17.21",
"lucide-react": "^0.395.0",
Expand Down

0 comments on commit 7a56683

Please sign in to comment.