mirror of
https://github.com/storytold/artcraft.git
synced 2026-10-20 17:02:53 -04:00
allow playing audio ref in seedance prompt input (#1151)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
@@ -12,8 +13,10 @@ import { downloadFileFromUrl } from "@storyteller/api";
|
||||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||||
import {
|
||||
faImages,
|
||||
faPlay,
|
||||
faPlus,
|
||||
faSpinnerThird,
|
||||
faStop,
|
||||
faTrashAlt,
|
||||
faXmark,
|
||||
} from "@fortawesome/pro-solid-svg-icons";
|
||||
@@ -73,6 +76,84 @@ interface ImagePromptRowProps {
|
||||
uploadAudio?: UploadImageFn;
|
||||
}
|
||||
|
||||
const AudioRefTile = ({
|
||||
audio,
|
||||
index,
|
||||
onRemove,
|
||||
}: {
|
||||
audio: RefAudio;
|
||||
index: number;
|
||||
onRemove: (id: string) => void;
|
||||
}) => {
|
||||
const audioRef = useRef<HTMLAudioElement | null>(null);
|
||||
const [isPlaying, setIsPlaying] = useState(false);
|
||||
|
||||
const handleTogglePlay = useCallback(
|
||||
(e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
if (isPlaying) {
|
||||
if (audioRef.current) {
|
||||
audioRef.current.pause();
|
||||
audioRef.current.currentTime = 0;
|
||||
}
|
||||
setIsPlaying(false);
|
||||
} else {
|
||||
const el = new Audio(audio.url);
|
||||
el.volume = 0.2;
|
||||
audioRef.current = el;
|
||||
el.onended = () => setIsPlaying(false);
|
||||
el.play();
|
||||
setIsPlaying(true);
|
||||
}
|
||||
},
|
||||
[isPlaying, audio.url],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
if (audioRef.current) {
|
||||
audioRef.current.pause();
|
||||
audioRef.current = null;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="glass relative aspect-square overflow-hidden rounded-lg w-14 border-2 border-white/30 hover:border-white/80 transition-all group cursor-pointer flex items-center justify-center">
|
||||
<button
|
||||
onClick={handleTogglePlay}
|
||||
className="flex items-center justify-center w-full h-full"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={isPlaying ? faStop : faPlay}
|
||||
className={twMerge(
|
||||
"h-5 w-5 transition-colors",
|
||||
isPlaying
|
||||
? "text-red-400"
|
||||
: "text-base-fg/60 group-hover:text-base-fg",
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
<div className="absolute bottom-0 left-0 right-0 flex items-center justify-center bg-black/70 py-0.5 text-[10px] font-bold text-white pointer-events-none">
|
||||
#{index + 1} · {audio.duration}s
|
||||
</div>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (audioRef.current) {
|
||||
audioRef.current.pause();
|
||||
audioRef.current = null;
|
||||
}
|
||||
onRemove(audio.id);
|
||||
}}
|
||||
className="opacity-0 group-hover:opacity-100 absolute right-[2px] top-[2px] flex h-5 w-5 items-center justify-center rounded-full bg-black/50 hover:bg-red/70 text-white backdrop-blur-md transition-colors hover:bg-black cursor-pointer"
|
||||
>
|
||||
<FontAwesomeIcon icon={faXmark} className="h-2.5 w-2.5" />
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export const ImagePromptRow = ({
|
||||
visible,
|
||||
className,
|
||||
@@ -1065,30 +1146,12 @@ export const ImagePromptRow = ({
|
||||
</div>
|
||||
<div className="flex gap-2 items-center">
|
||||
{referenceAudios.map((audio, index) => (
|
||||
<div
|
||||
<AudioRefTile
|
||||
key={audio.id}
|
||||
className="glass relative aspect-square overflow-hidden rounded-lg w-14 border-2 border-white/30 hover:border-white/80 transition-all group cursor-pointer flex items-center justify-center"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faMusic}
|
||||
className="h-5 w-5 text-base-fg/60"
|
||||
/>
|
||||
<div className="absolute bottom-0 left-0 right-0 flex items-center justify-center bg-black/70 py-0.5 text-[10px] font-bold text-white">
|
||||
#{index + 1} · {audio.duration}s
|
||||
</div>
|
||||
<button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
handleRemoveAudio(audio.id);
|
||||
}}
|
||||
className="opacity-0 group-hover:opacity-100 absolute right-[2px] top-[2px] flex h-5 w-5 items-center justify-center rounded-full bg-black/50 hover:bg-red/70 text-white backdrop-blur-md transition-colors hover:bg-black cursor-pointer"
|
||||
>
|
||||
<FontAwesomeIcon
|
||||
icon={faXmark}
|
||||
className="h-2.5 w-2.5"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
audio={audio}
|
||||
index={index}
|
||||
onRemove={handleRemoveAudio}
|
||||
/>
|
||||
))}
|
||||
{uploadingAudio && (
|
||||
<div className="glass relative aspect-square overflow-hidden rounded-lg w-14 border-2 border-white/30 flex items-center justify-center">
|
||||
|
||||
Reference in New Issue
Block a user