카드 배치를 무음 제거 뒤로 옮겨 3초 약속을 지킨다
카드 시간을 무음 제거 **전** 타임라인에서 만들고 _remap_caps 로 압축하고 있었다. 자막은 그게 맞다(발화 시각을 따라가야 하니까). 하지만 카드는 다르다: - cards_fixed=True 는 "카드는 정확히 3초"라는 절대 길이 약속이라 압축하면 깨진다. 컷 한가운데가 무음이면 3.0초짜리가 2.0초로 눌린다. - fixed=False 에서도 3초 하한이 사라져 있었다. 기존 _load_comment_cards 는 n = max(1, int(dur // 3)) 으로 카드당 3초를 보장하는데 _cards_by_cut 엔 그게 없었고, quota 는 **원본** 컷 길이로 계산되므로 무음이 절반인 24초 컷은 8장 → 압축 12초 → 장당 1.5초가 됐다. 고친 방식: - _remap_placements() 추가 — 카드는 '시간'이 아니라 '컷 구간 자체'를 옮긴다. - _cards_by_cut 호출을 무음 제거 블록 뒤로 옮기고 압축된 구간·timeline_dur 로 계산. 무음 제거가 꺼져 있으면 card_places = placements 그대로다. - _cards_by_cut 에 컷당 장수 상한 min(len, max(1, floor(컷길이/min_sec))) 추가. 초과분은 버린다 — _load_comment_cards 와 같은 규칙. - 이제 필요 없어진 cut_cards 의 _remap_caps 호출 제거. 기존 탭(process_bg_template)과 _load_comment_cards 는 손대지 않았다. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
4e479b50ce
commit
499223a125
@ -96,6 +96,13 @@ def _cards_by_cut(paths, card_cuts, placements, dur: float, *,
|
|||||||
card_cuts[i] = i번째 카드가 속한 컷 인덱스. 컷 안에서는 균등 분할
|
card_cuts[i] = i번째 카드가 속한 컷 인덱스. 컷 안에서는 균등 분할
|
||||||
(fixed=True 면 min_sec 고정, 컷 뒷부분은 비움).
|
(fixed=True 면 min_sec 고정, 컷 뒷부분은 비움).
|
||||||
|
|
||||||
|
⚠ placements/dur 은 **최종(무음 제거 반영) 타임라인** 기준이어야 한다.
|
||||||
|
압축 전 시간으로 계산해 놓고 나중에 재매핑하면 fixed=True 의 "정확히 3초" 약속이
|
||||||
|
깨지고(3초짜리가 2초로 눌린다) 3초 하한도 사라진다.
|
||||||
|
|
||||||
|
컷당 장수 상한 = max(1, floor(컷길이/min_sec)) — `_load_comment_cards` 와 같은 규칙.
|
||||||
|
초과분은 버린다(카드가 1초씩 번쩍이느니 몇 장 빼는 게 낫다).
|
||||||
|
|
||||||
왜 컷 단위인가: 전체 균등 배치(`_load_comment_cards`)는 컷 경계를 몰라서
|
왜 컷 단위인가: 전체 균등 배치(`_load_comment_cards`)는 컷 경계를 몰라서
|
||||||
3번 컷 얘기하는 댓글이 7번 컷 위에 뜬다. 컷 안에서 계산하면 한 컷이 덜 차도
|
3번 컷 얘기하는 댓글이 7번 컷 위에 뜬다. 컷 안에서 계산하면 한 컷이 덜 차도
|
||||||
**다음 컷 카드가 앞으로 밀리지 않는다.**
|
**다음 컷 카드가 앞으로 밀리지 않는다.**
|
||||||
@ -113,7 +120,9 @@ def _cards_by_cut(paths, card_cuts, placements, dur: float, *,
|
|||||||
p1 = min(p1, dur)
|
p1 = min(p1, dur)
|
||||||
if p1 <= p0:
|
if p1 <= p0:
|
||||||
continue # 영상 실제 길이 밖 컷은 버린다
|
continue # 영상 실제 길이 밖 컷은 버린다
|
||||||
step = min_sec if fixed else (p1 - p0) / len(idxs)
|
n = min(len(idxs), max(1, int((p1 - p0) // min_sec))) # 카드당 min_sec 하한
|
||||||
|
idxs = idxs[:n]
|
||||||
|
step = min_sec if fixed else (p1 - p0) / n
|
||||||
for k, i in enumerate(idxs):
|
for k, i in enumerate(idxs):
|
||||||
s = p0 + k * step
|
s = p0 + k * step
|
||||||
if s >= p1:
|
if s >= p1:
|
||||||
@ -144,6 +153,20 @@ def _remap_caps(caps, keep_sorted):
|
|||||||
return out
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _remap_placements(placements, keep_sorted):
|
||||||
|
"""컷 구간 [(p0,p1)] 을 무음 제거 타임라인으로 재매핑.
|
||||||
|
|
||||||
|
⚠ 자막(`_remap_caps`)과 목적이 다르다. 자막은 '그 말이 나오는 시각'을 따라가면 되지만
|
||||||
|
카드는 **컷 구간 자체**를 옮겨야 한다. 카드 시간을 압축 전에 만들어 두고 나중에
|
||||||
|
자막처럼 재매핑하면 `cards_fixed`(정확히 3초)가 2초로 눌리고, 3초 하한도 사라진다.
|
||||||
|
구간을 먼저 옮기고 그 안에서 나누면 둘 다 지켜진다.
|
||||||
|
|
||||||
|
통째로 무음이라 사라진 컷은 (x, x) 빈 구간이 되고 `_cards_by_cut` 이 건너뛴다.
|
||||||
|
"""
|
||||||
|
return [(_elapsed_kept(keep_sorted, p0), _elapsed_kept(keep_sorted, p1))
|
||||||
|
for p0, p1 in placements]
|
||||||
|
|
||||||
|
|
||||||
def _safe_name(name: str) -> str:
|
def _safe_name(name: str) -> str:
|
||||||
s = "".join(c for c in name if c.isalnum() or c in (" ", "_", "-", ".")).strip()
|
s = "".join(c for c in name if c.isalnum() or c in (" ", "_", "-", ".")).strip()
|
||||||
return s[:60] or "video"
|
return s[:60] or "video"
|
||||||
@ -417,11 +440,7 @@ async def process_paste(
|
|||||||
eff_caps = [(p0, min(p1, dur), ef) for (p0, p1), (_, _, _, ef) in zip(placements, cuts)
|
eff_caps = [(p0, min(p1, dur), ef) for (p0, p1), (_, _, _, ef) in zip(placements, cuts)
|
||||||
if ef and p0 < dur]
|
if ef and p0 < dur]
|
||||||
|
|
||||||
# 댓글 카드(자동 탭): '몇 번 컷 소속'만 받아 여기서 시간을 만든다.
|
card_places = placements # 카드 배치 기준 구간(무음 제거 시 압축본으로 교체)
|
||||||
# 서버가 시간을 확정하면 무음 제거 때 자막만 당겨지고 카드는 혼자 어긋난다.
|
|
||||||
cut_cards = _cards_by_cut(_card_paths(comments_dir), card_cuts or [],
|
|
||||||
placements, dur, fixed=cards_fixed)
|
|
||||||
|
|
||||||
video_clips = [(0.0, dur)] # 병합본 = 한 덩어리(재컷 없음)
|
video_clips = [(0.0, dur)] # 병합본 = 한 덩어리(재컷 없음)
|
||||||
timeline_dur = dur
|
timeline_dur = dur
|
||||||
|
|
||||||
@ -436,7 +455,8 @@ async def process_paste(
|
|||||||
keep = sorted(keep)
|
keep = sorted(keep)
|
||||||
bottom_caps = _remap_caps(bottom_caps, keep)
|
bottom_caps = _remap_caps(bottom_caps, keep)
|
||||||
eff_caps = _remap_caps(eff_caps, keep)
|
eff_caps = _remap_caps(eff_caps, keep)
|
||||||
cut_cards = _remap_caps(cut_cards, keep)
|
# 카드는 '시간'이 아니라 '컷 구간'을 옮긴다 — 아래에서 이 구간 안에 나눠 넣는다.
|
||||||
|
card_places = _remap_placements(placements, keep)
|
||||||
video_clips = keep
|
video_clips = keep
|
||||||
timeline_dur = sum(e - s for s, e in keep)
|
timeline_dur = sum(e - s for s, e in keep)
|
||||||
yield {"type": "log",
|
yield {"type": "log",
|
||||||
@ -490,7 +510,13 @@ async def process_paste(
|
|||||||
video_clips = split_clips_at_scenes(video_clips, scenes)
|
video_clips = split_clips_at_scenes(video_clips, scenes)
|
||||||
yield {"type": "log", "msg": f"장면전환 {len(scenes)}곳 → 세그먼트 {len(video_clips)}개"}
|
yield {"type": "log", "msg": f"장면전환 {len(scenes)}곳 → 세그먼트 {len(video_clips)}개"}
|
||||||
|
|
||||||
# 댓글 카드: 컷 소속이 지정됐으면 그 컷 구간 안(위에서 계산), 아니면 폴더 균등 배치
|
# 댓글 카드(자동 탭): '몇 번 컷 소속'만 받아 여기서 시간을 만든다.
|
||||||
|
# 서버가 시간을 확정하면 무음 제거 때 자막만 당겨지고 카드는 혼자 어긋난다.
|
||||||
|
# ⚠ 무음 제거 **뒤**에 계산한다 — 압축 전 시간으로 만들어 재매핑하면
|
||||||
|
# cards_fixed(정확히 3초)가 눌리고 카드당 3초 하한이 사라진다.
|
||||||
|
cut_cards = _cards_by_cut(_card_paths(comments_dir), card_cuts or [],
|
||||||
|
card_places, timeline_dur, fixed=cards_fixed)
|
||||||
|
# 컷 소속이 지정됐으면 그 컷 구간 안, 아니면 폴더 전체 균등 배치
|
||||||
cards = cut_cards or _load_comment_cards(comments_dir, timeline_dur, fixed=cards_fixed)
|
cards = cut_cards or _load_comment_cards(comments_dir, timeline_dur, fixed=cards_fixed)
|
||||||
if cards:
|
if cards:
|
||||||
yield {"type": "log",
|
yield {"type": "log",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user