Spaces:
Sleeping
Sleeping
Upload app.py with huggingface_hub
Browse files
app.py
CHANGED
|
@@ -6,7 +6,6 @@ see poetic/providers/ for the swappable provider seam (local ZeroGPU vs HF API).
|
|
| 6 |
|
| 7 |
import logging
|
| 8 |
import tempfile
|
| 9 |
-
from concurrent.futures import ThreadPoolExecutor
|
| 10 |
|
| 11 |
import gradio as gr
|
| 12 |
|
|
@@ -20,12 +19,10 @@ from poetic.recite import (
|
|
| 20 |
music_tags,
|
| 21 |
voice_or_default,
|
| 22 |
)
|
| 23 |
-
from poetic.storyboard import generate_scenes
|
| 24 |
from poetic.styles import DEFAULT_IMAGE_STYLE, apply_image_style
|
| 25 |
from poetic.textfree import ensure_text_free
|
| 26 |
from poetic.video import (
|
| 27 |
audio_duration,
|
| 28 |
-
concat_clips,
|
| 29 |
mix_recitation_over_music,
|
| 30 |
mux_song_into_video,
|
| 31 |
)
|
|
@@ -107,39 +104,28 @@ def _paint_scene(scene_brief: str, style_id: str):
|
|
| 107 |
return providers.get_painter().paint(brief)
|
| 108 |
|
| 109 |
|
| 110 |
-
def do_animate(brief_en: str, audio_path: str | None,
|
| 111 |
-
understanding: str = "", style_id: str = "",
|
| 112 |
progress: gr.Progress = gr.Progress()):
|
| 113 |
-
"""
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
|
|
|
|
|
|
| 119 |
try:
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
log.info("do_animate: %d keyframes\n%s", len(scenes), "\n---\n".join(scenes))
|
| 123 |
-
|
| 124 |
-
progress(0.2, desc="绘制关键画面…")
|
| 125 |
-
with ThreadPoolExecutor(max_workers=min(4, len(scenes))) as ex:
|
| 126 |
-
keyframes = list(ex.map(lambda s: _paint_scene(s, style_id), scenes))
|
| 127 |
-
|
| 128 |
-
# First-last-frame interpolation: each segment is a smooth generated
|
| 129 |
-
# video from one keyframe to the next, joined seamlessly → one flowing
|
| 130 |
-
# film (not crossfaded scenes).
|
| 131 |
-
clips = providers.get_animator().interpolate_keyframes(
|
| 132 |
-
keyframes, scenes,
|
| 133 |
progress_cb=lambda f, d: progress(f, desc=d),
|
| 134 |
)
|
| 135 |
-
progress(0.
|
| 136 |
-
video = concat_clips(clips)
|
| 137 |
except gr.Error:
|
| 138 |
raise
|
| 139 |
except Exception as exc: # noqa: BLE001 — cold-start/network → warm copy
|
| 140 |
-
raise gr.Error("正在
|
| 141 |
if audio_path:
|
| 142 |
-
progress(0.
|
| 143 |
video = mux_song_into_video(video, audio_path)
|
| 144 |
return video
|
| 145 |
|
|
@@ -152,7 +138,7 @@ def do_everything(poem: str, style_id: str, voice: str, music_vibe: str,
|
|
| 152 |
yield understanding, brief_en, card, painting, gr.update(), gr.update()
|
| 153 |
audio = do_recite(poem, voice, music_vibe)
|
| 154 |
yield understanding, brief_en, card, painting, audio, gr.update()
|
| 155 |
-
video = do_animate(brief_en, audio,
|
| 156 |
yield understanding, brief_en, card, painting, audio, video
|
| 157 |
|
| 158 |
|
|
@@ -454,7 +440,7 @@ with gr.Blocks(title="诗情画意 · Poetry to Art") as demo:
|
|
| 454 |
|
| 455 |
animate_btn.click(
|
| 456 |
do_animate,
|
| 457 |
-
inputs=[brief_state, song_audio,
|
| 458 |
outputs=video_out,
|
| 459 |
)
|
| 460 |
|
|
|
|
| 6 |
|
| 7 |
import logging
|
| 8 |
import tempfile
|
|
|
|
| 9 |
|
| 10 |
import gradio as gr
|
| 11 |
|
|
|
|
| 19 |
music_tags,
|
| 20 |
voice_or_default,
|
| 21 |
)
|
|
|
|
| 22 |
from poetic.styles import DEFAULT_IMAGE_STYLE, apply_image_style
|
| 23 |
from poetic.textfree import ensure_text_free
|
| 24 |
from poetic.video import (
|
| 25 |
audio_duration,
|
|
|
|
| 26 |
mix_recitation_over_music,
|
| 27 |
mux_song_into_video,
|
| 28 |
)
|
|
|
|
| 104 |
return providers.get_painter().paint(brief)
|
| 105 |
|
| 106 |
|
| 107 |
+
def do_animate(painting, brief_en: str, audio_path: str | None, style_id: str = "",
|
|
|
|
| 108 |
progress: gr.Progress = gr.Progress()):
|
| 109 |
+
"""Single coherent shot: animate the painting itself (slow camera move +
|
| 110 |
+
ambient motion) — one continuous living painting, no scene morphing/cuts.
|
| 111 |
+
Then lay the recitation + music underneath."""
|
| 112 |
+
if painting is None:
|
| 113 |
+
if not (brief_en and brief_en.strip()):
|
| 114 |
+
raise gr.Error("请先点「作画」,有了画才能让它动起来。")
|
| 115 |
+
progress(0.1, desc="先画一幅…")
|
| 116 |
+
painting = _paint_scene(brief_en, style_id)
|
| 117 |
try:
|
| 118 |
+
video = providers.get_animator().animate(
|
| 119 |
+
painting, brief_en or "",
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 120 |
progress_cb=lambda f, d: progress(f, desc=d),
|
| 121 |
)
|
| 122 |
+
progress(0.93, desc="收尾…")
|
|
|
|
| 123 |
except gr.Error:
|
| 124 |
raise
|
| 125 |
except Exception as exc: # noqa: BLE001 — cold-start/network → warm copy
|
| 126 |
+
raise gr.Error("正在让画动起来(首次最久,约一两分钟),请稍候再点一次「让画活起来」。") from exc
|
| 127 |
if audio_path:
|
| 128 |
+
progress(0.96, desc="把朗诵与音乐配进来…")
|
| 129 |
video = mux_song_into_video(video, audio_path)
|
| 130 |
return video
|
| 131 |
|
|
|
|
| 138 |
yield understanding, brief_en, card, painting, gr.update(), gr.update()
|
| 139 |
audio = do_recite(poem, voice, music_vibe)
|
| 140 |
yield understanding, brief_en, card, painting, audio, gr.update()
|
| 141 |
+
video = do_animate(painting, brief_en, audio, style_id)
|
| 142 |
yield understanding, brief_en, card, painting, audio, video
|
| 143 |
|
| 144 |
|
|
|
|
| 440 |
|
| 441 |
animate_btn.click(
|
| 442 |
do_animate,
|
| 443 |
+
inputs=[painting_state, brief_state, song_audio, style_radio],
|
| 444 |
outputs=video_out,
|
| 445 |
)
|
| 446 |
|