feat: 타임라인 에디터 및 비디오 스튜디오 컴포넌트 추가

- TimelineEditor, VideoStudio 컴포넌트 신규 추가
- 백엔드 transcriber, video_processor 서비스 개선
- 프론트엔드 HomePage 리팩토링 및 스타일 업데이트

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
kihong.kim
2026-01-06 21:21:58 +09:00
parent ad14c4ea8c
commit 5c57f33903
10 changed files with 3186 additions and 1040 deletions

View File

@@ -116,12 +116,14 @@ async def process_task(
outline_color=style.outline_color.lstrip("#"),
font_name=style.font_name,
position=style.position, # top, center, bottom
margin_v=style.margin_v,
outline_width=style.outline_width,
bold=style.bold,
shadow=style.shadow,
background_box=style.background_box,
background_opacity=style.background_opacity,
animation=style.animation,
max_chars_per_line=style.max_chars_per_line,
)
# Save subtitle file
@@ -284,12 +286,14 @@ async def continue_processing_task(
outline_color=style.outline_color.lstrip("#"),
font_name=style.font_name,
position=style.position,
margin_v=style.margin_v,
outline_width=style.outline_width,
bold=style.bold,
shadow=style.shadow,
background_box=style.background_box,
background_opacity=style.background_opacity,
animation=style.animation,
max_chars_per_line=style.max_chars_per_line,
)
job_dir = os.path.dirname(job.video_path)
@@ -511,12 +515,21 @@ async def trim_job_video(
video_ext = os.path.splitext(job.video_path)[1]
trimmed_path = os.path.join(video_dir, f"trimmed{video_ext}")
# Convert exclude_regions to list of dicts for the function
exclude_regions = None
if request.exclude_regions:
exclude_regions = [
{'start': region.start, 'end': region.end}
for region in request.exclude_regions
]
# Perform trim
success, message = await trim_video(
job.video_path,
trimmed_path,
request.start_time,
request.end_time,
exclude_regions,
)
if not success:
@@ -690,6 +703,7 @@ async def render_step_task(
intro_text: str | None = None,
intro_duration: float = 0.7,
intro_font_size: int = 100,
intro_position: str = "center",
):
"""Background task for final video rendering (subtitle composition + BGM + intro text)."""
job = job_store.get_job(job_id)
@@ -715,6 +729,7 @@ async def render_step_task(
outline_color=style.outline_color.lstrip("#"),
font_name=style.font_name,
position=style.position,
margin_v=style.margin_v,
outline_width=style.outline_width,
bold=style.bold,
shadow=style.shadow,
@@ -722,6 +737,7 @@ async def render_step_task(
background_opacity=style.background_opacity,
animation=style.animation,
time_offset=subtitle_offset,
max_chars_per_line=style.max_chars_per_line,
)
job_dir = os.path.dirname(job.video_path)
@@ -756,6 +772,7 @@ async def render_step_task(
intro_text=intro_text,
intro_duration=intro_duration,
intro_font_size=intro_font_size,
intro_position=intro_position,
)
if success:
@@ -846,6 +863,7 @@ async def render_video(
request.intro_text,
request.intro_duration,
request.intro_font_size,
request.intro_position,
)
return ProcessResponse(