chore(fase0): higiene do repositório + corrige gitignore que escondia fcpxml/models/
Fase 0 do roteiro de reestruturação (Engine/docs/10_MAPA_REESTRUTURACAO.md): move code/WHISPERX (2,6 GB de backups órfãos, sem uso ativo, sem .gitmodules) para ~/Archives/G-ART-WHISPERX-backup fora do workspace git; traz admin/ para o gate de lint de run_after_fix.sh; corrige fcpxml/writer/adjustment.py, que gerava um wrapper <adjustment> inexistente no DTD 1.13 (filtros agora vão direto no <clip>, na ordem exigida), com teste de regressão novo. Achado à parte: .gitignore tinha uma regra solta "models/" (pensada só para o cache do Whisper em code/models/) que também escondia do git todo o pacote fcpxml/models/ — nunca commitado, sem proteção nenhuma. Corrigida para /code/models/, ancorada na raiz. Docs atualizados no mesmo commit (02_MODULES, 09_MANUTENCAO, 10_MAPA_REESTRUTURACAO, 05_EXPERIENCIAS #34 e #36), conforme a regra do CLAUDE.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
0fdfe33613
commit
d13f643ebc
@@ -0,0 +1,93 @@
|
||||
"""Especificações de geração: rough cut, ritmo e montagem.
|
||||
|
||||
Extraído de models.py — ver fcpxml/models/__init__.py.
|
||||
"""
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from .enums import PacingCurve
|
||||
|
||||
|
||||
@dataclass
|
||||
class SegmentSpec:
|
||||
"""Specification for a segment in auto rough cut."""
|
||||
name: str
|
||||
keywords: List[str] = field(default_factory=list)
|
||||
duration_seconds: float = 0.0
|
||||
priority: str = "best" # favorites, longest, shortest, random, best
|
||||
|
||||
@dataclass
|
||||
class PacingConfig:
|
||||
"""Configuration for rough cut pacing."""
|
||||
pacing: str = "medium" # slow, medium, fast, dynamic
|
||||
min_clip_duration: float = 1.0
|
||||
max_clip_duration: float = 8.0
|
||||
avg_clip_duration: Optional[float] = None
|
||||
vary_pacing: bool = True
|
||||
|
||||
def get_duration_range(self) -> Tuple[float, float]:
|
||||
"""Get min/max based on pacing style."""
|
||||
ranges = {
|
||||
"slow": (5.0, 10.0),
|
||||
"medium": (2.0, 5.0),
|
||||
"fast": (0.5, 2.0),
|
||||
"dynamic": (1.0, 6.0),
|
||||
}
|
||||
return ranges.get(self.pacing, (2.0, 5.0))
|
||||
|
||||
@dataclass
|
||||
class RoughCutResult:
|
||||
"""Result of auto rough cut generation."""
|
||||
output_path: str
|
||||
clips_used: int
|
||||
clips_available: int
|
||||
target_duration: float
|
||||
actual_duration: float
|
||||
segments: int
|
||||
average_clip_duration: float
|
||||
|
||||
@dataclass
|
||||
class MontageConfig:
|
||||
"""Configuration for montage generation with pacing curves."""
|
||||
target_duration: float # Target duration in seconds
|
||||
pacing_curve: 'PacingCurve'
|
||||
start_duration: float = 2.0 # Clip duration at start
|
||||
end_duration: float = 0.5 # Clip duration at end
|
||||
min_duration: float = 0.2 # Minimum allowed clip duration
|
||||
max_duration: float = 5.0 # Maximum allowed clip duration
|
||||
|
||||
def get_duration_at_position(self, position: float) -> float:
|
||||
"""
|
||||
Calculate clip duration for a given position (0.0 to 1.0).
|
||||
|
||||
Args:
|
||||
position: Position in montage (0.0 = start, 1.0 = end)
|
||||
|
||||
Returns:
|
||||
Target duration in seconds for a clip at this position
|
||||
"""
|
||||
if self.pacing_curve == PacingCurve.CONSTANT:
|
||||
duration = (self.start_duration + self.end_duration) / 2
|
||||
|
||||
elif self.pacing_curve == PacingCurve.ACCELERATING:
|
||||
# Linear interpolation from start to end duration
|
||||
duration = self.start_duration + (self.end_duration - self.start_duration) * position
|
||||
|
||||
elif self.pacing_curve == PacingCurve.DECELERATING:
|
||||
# Reverse: start fast, end slow
|
||||
duration = self.end_duration + (self.start_duration - self.end_duration) * position
|
||||
|
||||
elif self.pacing_curve == PacingCurve.PYRAMID:
|
||||
# Slow → fast → slow (parabolic curve)
|
||||
if position < 0.5:
|
||||
# First half: slow to fast
|
||||
duration = self.start_duration + (self.end_duration - self.start_duration) * (position * 2)
|
||||
else:
|
||||
# Second half: fast to slow
|
||||
duration = self.end_duration + (self.start_duration - self.end_duration) * ((position - 0.5) * 2)
|
||||
else:
|
||||
duration = self.start_duration
|
||||
|
||||
# Clamp to min/max
|
||||
return max(self.min_duration, min(self.max_duration, duration))
|
||||
Reference in New Issue
Block a user