forked from 77media/video-flow
推进
This commit is contained in:
parent
b0e15c16c0
commit
f41f3b35e4
@ -209,7 +209,11 @@ export interface CreateMovieProjectV3Request {
|
||||
/**角色名 */
|
||||
role_name: string;
|
||||
/**角色描述 */
|
||||
role_description: string;
|
||||
role_description: {
|
||||
name: string;
|
||||
image_url: string;
|
||||
character_analysis: Record<string, any>;
|
||||
};
|
||||
/**照片URL */
|
||||
photo_url: string;
|
||||
/**声音URL */
|
||||
|
||||
@ -126,12 +126,11 @@ export const useTemplateStoryServiceHook = (): UseTemplateStoryService => {
|
||||
}
|
||||
try {
|
||||
|
||||
const character_briefs = {
|
||||
const character_brief = {
|
||||
name: selectedTemplate.storyRole[activeRoleIndex].role_name,
|
||||
image_url: imageUrl,
|
||||
character_analysis: JSON.parse(desc).character_analysis,
|
||||
};
|
||||
console.log("character_briefs", character_briefs);
|
||||
const updatedTemplate = {
|
||||
...selectedTemplate,
|
||||
storyRole: selectedTemplate.storyRole.map((role, index) =>
|
||||
@ -139,7 +138,7 @@ export const useTemplateStoryServiceHook = (): UseTemplateStoryService => {
|
||||
? {
|
||||
...role,
|
||||
photo_url: imageUrl,
|
||||
role_description: JSON.stringify(character_briefs),
|
||||
role_description: character_brief,
|
||||
}
|
||||
: role
|
||||
),
|
||||
|
||||
@ -146,8 +146,12 @@ export interface StoryTemplateEntity {
|
||||
storyRole: {
|
||||
/**角色名 */
|
||||
role_name: string;
|
||||
/**角色描述 */
|
||||
role_description: string;
|
||||
/**角色描述 对应后端是单个的character_brief */
|
||||
role_description: {
|
||||
name: string;
|
||||
image_url: string;
|
||||
character_analysis: Record<string, any>;
|
||||
};
|
||||
/**照片URL */
|
||||
photo_url: string;
|
||||
/**声音URL */
|
||||
|
||||
@ -72,6 +72,44 @@ const audioRecorderStyles = `
|
||||
background: #2563eb;
|
||||
transform: scale(1.1);
|
||||
}
|
||||
|
||||
/* 音量滑块样式 */
|
||||
.volume-slider {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.volume-slider::-webkit-slider-track {
|
||||
background: transparent;
|
||||
height: 16px;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.volume-slider::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
appearance: none;
|
||||
width: 0;
|
||||
height: 0;
|
||||
box-shadow: -100px 0 5px 100px #92ff77, -100px 0px 20px 100px #92ff77;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.volume-slider::-moz-range-track {
|
||||
background: transparent;
|
||||
height: 16px;
|
||||
border-radius: 8px;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.volume-slider::-moz-range-thumb {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
box-shadow: -100px 0 5px 100px #92ff77, -100px 0px 20px 100px #92ff77;
|
||||
}
|
||||
`;
|
||||
|
||||
interface AudioRecorderProps {
|
||||
@ -229,8 +267,11 @@ export function AudioRecorder({
|
||||
// 音量调节
|
||||
const handleVolumeChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newVolume = parseFloat(e.target.value);
|
||||
setVolume(newVolume);
|
||||
if (isMuted && newVolume > 0) {
|
||||
// 确保音量值在 0-1 范围内
|
||||
const clampedVolume = Math.max(0, Math.min(1, newVolume));
|
||||
console.log("newVolume", newVolume, "clampedVolume", clampedVolume);
|
||||
setVolume(clampedVolume);
|
||||
if (isMuted && clampedVolume > 0) {
|
||||
setIsMuted(false);
|
||||
}
|
||||
};
|
||||
@ -446,7 +487,7 @@ export function AudioRecorder({
|
||||
data-alt="audio-player-container"
|
||||
>
|
||||
{/* 大旋转的播放圆盘 */}
|
||||
<div className=" absolute z-1 right-6 top-[50%] translate-y-[-50%] scale-[1.4]">
|
||||
<div className=" absolute z-10 right-10 top-[45%] translate-y-[-50%] scale-[1.4]">
|
||||
<div className="relative">
|
||||
{/* 外层边框 - 唱片边缘 */}
|
||||
<div
|
||||
@ -460,7 +501,7 @@ export function AudioRecorder({
|
||||
|
||||
{/* 唱片主体 - 纯黑色唱片面 */}
|
||||
<div
|
||||
className={`absolute inset-2 w-24 h-24 rounded-full bg-black shadow-[inset_0_2px_8px_rgba(0,0,0,0.8)] ${
|
||||
className={`absolute inset-2 w-24 h-24 rounded-full bg-[0,0,0,.4] shadow-[inset_0_2px_8px_rgba(0,0,0,0.8)] ${
|
||||
isPlaying ? "animate-spin" : ""
|
||||
}`}
|
||||
style={{ animationDuration: "6s" }}
|
||||
@ -526,9 +567,19 @@ export function AudioRecorder({
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 播放控制 */}
|
||||
<button
|
||||
onClick={togglePlay}
|
||||
className="absolute z-10 right-[4.75rem] top-[45%] translate-y-[-50%] flex items-center justify-center w-10 h-10 bg-gradient-to-br from-[rgb(106,244,249)] to-[rgb(199,59,255)] text-white rounded-full shadow-[inset_0_1px_0_rgba(255,255,255,0.3),0_4px_12px_rgba(0,0,0,0.3)] hover:shadow-[inset_0_1px_0_rgba(255,255,255,0.4),0_6px_20px_rgba(106,244,249,0.4)] transition-all duration-300 hover:scale-105 active:scale-95"
|
||||
>
|
||||
{isPlaying ? (
|
||||
<Pause className="w-5 h-5 drop-shadow-sm" />
|
||||
) : (
|
||||
<Play className="w-5 h-5 drop-shadow-sm ml-0.5" />
|
||||
)}
|
||||
</button>
|
||||
{/* 头部 - 只显示操作按钮 */}
|
||||
<div className="relative z-10 flex justify-end gap-2 mb-3 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
|
||||
<div className="relative -right-3 z-10 flex justify-end gap-2 mb-3 opacity-0 group-hover:opacity-100 transition-opacity duration-200">
|
||||
<button
|
||||
onClick={handleDelete}
|
||||
className="text-white/60 hover:text-red-400 transition-colors"
|
||||
@ -547,62 +598,19 @@ export function AudioRecorder({
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
{/* 播放控制 */}
|
||||
<div className="relative z-10 flex items-center justify-center gap-4 mb-4 mt-12">
|
||||
<button
|
||||
onClick={togglePlay}
|
||||
className="flex items-center justify-center w-12 h-12 bg-gradient-to-br from-[rgb(106,244,249)] to-[rgb(199,59,255)] text-white rounded-xl shadow-[inset_0_1px_0_rgba(255,255,255,0.3),0_4px_12px_rgba(0,0,0,0.3)] hover:shadow-[inset_0_1px_0_rgba(255,255,255,0.4),0_6px_20px_rgba(106,244,249,0.4)] transition-all duration-300 hover:scale-105 active:scale-95"
|
||||
>
|
||||
{isPlaying ? (
|
||||
<Pause className="w-5 h-5 drop-shadow-sm" />
|
||||
) : (
|
||||
<Play className="w-5 h-5 drop-shadow-sm ml-0.5" />
|
||||
)}
|
||||
</button>
|
||||
{/* WaveSurfer 波形图区域 */}
|
||||
<div className="h-16 flex-1 bg-white/[0.05] rounded-lg overflow-hidden">
|
||||
<WaveformPlayer
|
||||
audioUrl={audioUrl}
|
||||
isPlaying={isPlaying}
|
||||
onPlayStateChange={setIsPlaying}
|
||||
volume={volume}
|
||||
isMuted={isMuted}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 音频设置 */}
|
||||
<div className="flex items-center justify-between px-2">
|
||||
<div className="flex items-center gap-6">
|
||||
{/* 音量控制 */}
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="w-24 h-3 bg-white/[0.08] rounded-full overflow-hidden shadow-inner border border-white/[0.1] relative">
|
||||
<div
|
||||
className="h-full bg-gradient-to-r from-[rgb(106,244,249)] to-[rgb(199,59,255)] rounded-full transition-all duration-300"
|
||||
style={{ width: `${volume * 100}%` }}
|
||||
/>
|
||||
{/* 可拖拽的音量滑块 */}
|
||||
<input
|
||||
type="range"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={volume}
|
||||
onChange={handleVolumeChange}
|
||||
className="absolute inset-0 w-full h-full opacity-0 cursor-pointer"
|
||||
/>
|
||||
{/* 视觉滑块指示器 */}
|
||||
<div
|
||||
className="absolute top-1/2 -translate-y-1/2 w-4 h-4 bg-white rounded-full shadow-lg border-2 border-[rgb(199,59,255)] pointer-events-none transition-all duration-200"
|
||||
style={{ left: `calc(${volume * 100}% - 8px)` }}
|
||||
/>
|
||||
</div>
|
||||
{/* 音频图标 */}
|
||||
<div className="text-white/70 hover:text-white/90 transition-colors">
|
||||
<Volume2 className="w-5 h-5" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="relative z-1 flex items-center justify-between px-2 mt-16 mb-2">
|
||||
<VolumeSlider volume={volume} onVolumeChange={handleVolumeChange} />
|
||||
</div>
|
||||
{/* WaveSurfer 波形图区域 */}
|
||||
<div className=" relative z-5 h-16 flex-1 bg-white/[0.05] rounded-lg overflow-hidden">
|
||||
<WaveformPlayer
|
||||
audioUrl={audioUrl}
|
||||
isPlaying={isPlaying}
|
||||
onPlayStateChange={setIsPlaying}
|
||||
volume={volume}
|
||||
isMuted={isMuted}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
@ -721,3 +729,130 @@ function WaveformPlayer({
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function VolumeSlider({
|
||||
volume,
|
||||
onVolumeChange,
|
||||
}: {
|
||||
volume: number;
|
||||
onVolumeChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
}) {
|
||||
const [isHovered, setIsHovered] = useState(false);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full">
|
||||
<style jsx>{`
|
||||
/* From Uiverse.io by javierBarroso */
|
||||
/* level settings 👇 */
|
||||
|
||||
.slider {
|
||||
/* slider */
|
||||
--slider-width: 60%;
|
||||
--slider-height: 12px;
|
||||
--slider-bg: rgba(82, 82, 82, 0.322);
|
||||
--slider-border-radius: 4px;
|
||||
/* level */
|
||||
--level-color: rgb(106,244,249);
|
||||
--level-transition-duration: 5s;
|
||||
/* icon */
|
||||
--icon-margin: 10px;
|
||||
--icon-color: var(--slider-bg);
|
||||
--icon-size: 20px;
|
||||
position: relative;
|
||||
left: -40px;
|
||||
}
|
||||
|
||||
.slider {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
display: -webkit-inline-box;
|
||||
display: -ms-inline-flexbox;
|
||||
display: inline-flex;
|
||||
-webkit-box-orient: horizontal;
|
||||
-webkit-box-direction: reverse;
|
||||
-ms-flex-direction: row-reverse;
|
||||
flex-direction: row-reverse;
|
||||
-webkit-box-align: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.slider .volume {
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-right: var(--icon-margin);
|
||||
color: var(--icon-color);
|
||||
width: var(--icon-size);
|
||||
height: auto;
|
||||
position: absolute;
|
||||
left: 12px;
|
||||
pointer-events: none;
|
||||
transition-duration: 0.5s;
|
||||
}
|
||||
|
||||
.slider .level {
|
||||
position: relative;
|
||||
left: -30px;
|
||||
top: -40px;
|
||||
-webkit-appearance: none;
|
||||
-moz-appearance: none;
|
||||
appearance: none;
|
||||
width: var(--slider-width);
|
||||
height: var(--slider-height);
|
||||
background: var(--slider-bg);
|
||||
overflow: hidden;
|
||||
border-radius: var(--slider-border-radius);
|
||||
-webkit-transition: height var(--level-transition-duration);
|
||||
-o-transition: height var(--level-transition-duration);
|
||||
transition: height var(--level-transition-duration);
|
||||
cursor: inherit;
|
||||
transform: rotate(270deg);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
.slider:hover .level {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.slider .level::-webkit-slider-thumb {
|
||||
-webkit-appearance: none;
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
-webkit-box-shadow: -200px 0 0 200px var(--level-color);
|
||||
box-shadow: -100px 0 5px 100px var(--level-color),
|
||||
-100px 0px 20px 100px var(--level-color);
|
||||
}
|
||||
.slider .level:hover ~ .volume {
|
||||
color: var(--level-color);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.slider .level::-moz-range-thumb {
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-radius: 0;
|
||||
border: none;
|
||||
box-shadow: -100px 0 5px 100px var(--level-color),
|
||||
-100px 0px 20px 100px var(--level-color);
|
||||
}
|
||||
`}</style>
|
||||
<label
|
||||
className="slider"
|
||||
onMouseEnter={() => setIsHovered(true)}
|
||||
onMouseLeave={() => setIsHovered(false)}
|
||||
>
|
||||
<input
|
||||
type="range"
|
||||
className="level"
|
||||
min="0"
|
||||
max="1"
|
||||
step="0.01"
|
||||
value={volume}
|
||||
onChange={onVolumeChange}
|
||||
/>
|
||||
<Volume2 className="text-[#777876]" />
|
||||
</label>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -365,7 +365,7 @@ const RenderTemplateStoryMode = ({
|
||||
<GlobalLoad show={isLoading} progress={0}>
|
||||
<div className="rounded-2xl min-h-min transition-all duration-700 ease-out">
|
||||
{/* 弹窗头部 */}
|
||||
<div className="flex gap-4 p-4 border-b border-white/[0.1]">
|
||||
<div className="flex gap-4 px-4 pb-2 border-b border-white/[0.1]">
|
||||
<h2 className="text-2xl font-bold text-white">
|
||||
Template Story Selection
|
||||
</h2>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user