volume panel; copilot instructions
This commit is contained in:
@ -21,6 +21,10 @@ class SegmentModel(BaseModel):
|
||||
end: float
|
||||
|
||||
|
||||
class GainRangeModel(SegmentModel):
|
||||
gain_db: float
|
||||
|
||||
|
||||
class ExportWordModel(BaseModel):
|
||||
word: str
|
||||
start: float
|
||||
@ -33,6 +37,8 @@ class ExportRequest(BaseModel):
|
||||
output_path: str
|
||||
keep_segments: List[SegmentModel]
|
||||
mute_ranges: Optional[List[SegmentModel]] = None
|
||||
gain_ranges: Optional[List[GainRangeModel]] = None
|
||||
global_gain_db: float = 0.0
|
||||
mode: str = "fast"
|
||||
resolution: str = "1080p"
|
||||
format: str = "mp4"
|
||||
@ -42,6 +48,42 @@ class ExportRequest(BaseModel):
|
||||
deleted_indices: Optional[List[int]] = None
|
||||
|
||||
|
||||
def _map_ranges_to_output_timeline(
|
||||
ranges: List[dict],
|
||||
keep_segments: List[dict],
|
||||
) -> List[dict]:
|
||||
"""Map source-time ranges to output timeline after cuts are applied."""
|
||||
if not ranges or not keep_segments:
|
||||
return []
|
||||
|
||||
mapped: List[dict] = []
|
||||
output_cursor = 0.0
|
||||
for keep in keep_segments:
|
||||
keep_start = float(keep["start"])
|
||||
keep_end = float(keep["end"])
|
||||
keep_len = max(0.0, keep_end - keep_start)
|
||||
if keep_len <= 0:
|
||||
continue
|
||||
|
||||
for src_range in ranges:
|
||||
overlap_start = max(keep_start, float(src_range["start"]))
|
||||
overlap_end = min(keep_end, float(src_range["end"]))
|
||||
if overlap_end <= overlap_start:
|
||||
continue
|
||||
|
||||
mapped_range = {
|
||||
"start": output_cursor + (overlap_start - keep_start),
|
||||
"end": output_cursor + (overlap_end - keep_start),
|
||||
}
|
||||
if "gain_db" in src_range:
|
||||
mapped_range["gain_db"] = float(src_range["gain_db"])
|
||||
mapped.append(mapped_range)
|
||||
|
||||
output_cursor += keep_len
|
||||
|
||||
return mapped
|
||||
|
||||
|
||||
def _mux_audio(video_path: str, audio_path: str, output_path: str) -> str:
|
||||
"""Replace video's audio track with cleaned audio using FFmpeg."""
|
||||
import subprocess
|
||||
@ -66,15 +108,19 @@ async def export_video(req: ExportRequest):
|
||||
try:
|
||||
segments = [{"start": s.start, "end": s.end} for s in req.keep_segments]
|
||||
mute_segments = [{"start": s.start, "end": s.end} for s in req.mute_ranges] if req.mute_ranges else None
|
||||
gain_segments = [{"start": s.start, "end": s.end, "gain_db": s.gain_db} for s in req.gain_ranges] if req.gain_ranges else None
|
||||
|
||||
if not segments and not mute_segments:
|
||||
raise HTTPException(status_code=400, detail="No segments to export")
|
||||
|
||||
use_stream_copy = req.mode == "fast" and len(segments) == 1 and not mute_segments
|
||||
mapped_gain_segments = _map_ranges_to_output_timeline(gain_segments or [], segments)
|
||||
|
||||
has_gain = abs(float(req.global_gain_db)) > 1e-6 or bool(gain_segments)
|
||||
use_stream_copy = req.mode == "fast" and len(segments) == 1 and not mute_segments and not has_gain
|
||||
needs_reencode_for_subs = req.captions == "burn-in"
|
||||
|
||||
# Burn-in captions or mute ranges require re-encode
|
||||
if needs_reencode_for_subs or mute_segments:
|
||||
# Burn-in captions or audio filters require re-encode
|
||||
if needs_reencode_for_subs or mute_segments or has_gain:
|
||||
use_stream_copy = False
|
||||
|
||||
words_dicts = [w.model_dump() for w in req.words] if req.words else []
|
||||
@ -101,6 +147,8 @@ async def export_video(req: ExportRequest):
|
||||
resolution=req.resolution,
|
||||
format_hint=req.format,
|
||||
mute_ranges=mute_segments,
|
||||
gain_ranges=mapped_gain_segments,
|
||||
global_gain_db=req.global_gain_db,
|
||||
)
|
||||
else:
|
||||
output = export_reencode(
|
||||
@ -110,6 +158,8 @@ async def export_video(req: ExportRequest):
|
||||
resolution=req.resolution,
|
||||
format_hint=req.format,
|
||||
mute_ranges=mute_segments,
|
||||
gain_ranges=mapped_gain_segments,
|
||||
global_gain_db=req.global_gain_db,
|
||||
)
|
||||
finally:
|
||||
if ass_path and os.path.exists(ass_path):
|
||||
|
||||
Reference in New Issue
Block a user