feat: rewrite export to use per-keyframe crop modes

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-14 15:58:01 +02:00
parent 279aee14cb
commit 1004bd0a28
+26 -27
View File
@@ -2793,35 +2793,34 @@ class MainWindow(QMainWindow):
out = build_export_path(folder, name, self._export_counter, sub=sub) out = build_export_path(folder, name, self._export_counter, sub=sub)
jobs.append((start, out, base_ratio, base_center)) jobs.append((start, out, base_ratio, base_center))
# Apply crop keyframes: each sub-clip uses the latest keyframe # Apply crop keyframes (or fall back to base state).
# at or before its start time (keyframes set in lock mode).
if self._crop_keyframes:
for i, (s, o, r, c) in enumerate(jobs):
center = base_center
for kt, kc in self._crop_keyframes:
if kt <= s + 0.05:
center = kc
else:
break
jobs[i] = (s, o, r, center)
# Random crop: ~1 per 3 clips gets a random crop + random position.
# When both portrait and square are on, they share the quota.
rand_portrait = self._chk_rand_portrait.isChecked() rand_portrait = self._chk_rand_portrait.isChecked()
rand_square = self._chk_rand_square.isChecked() rand_square = self._chk_rand_square.isChecked()
if (rand_portrait or rand_square) and n_clips > 1: widened = apply_keyframes_to_jobs(
n_random = max(1, n_clips // 3) jobs, self._crop_keyframes,
indices = random.sample(range(n_clips), n_random) base_center=base_center, base_ratio=base_ratio,
# Build pool of ratios to assign base_rand_p=rand_portrait, base_rand_s=rand_square,
if rand_portrait and rand_square: )
ratios = ["9:16", "1:1"]
elif rand_portrait: # Random crop: eligible clips (per their keyframe flags) have
ratios = ["9:16"] # ~1 in 3 chance of getting a random ratio applied.
else: portrait_eligible = [i for i, w in enumerate(widened) if w[4]]
ratios = ["1:1"] square_eligible = [i for i, w in enumerate(widened) if w[5]]
for idx in indices: rand_indices: dict[int, list[str]] = {}
s, o, _, c = jobs[idx] if portrait_eligible and n_clips > 1:
jobs[idx] = (s, o, random.choice(ratios), c) n = max(1, len(portrait_eligible) // 3)
for i in random.sample(portrait_eligible, min(n, len(portrait_eligible))):
rand_indices.setdefault(i, []).append("9:16")
if square_eligible and n_clips > 1:
n = max(1, len(square_eligible) // 3)
for i in random.sample(square_eligible, min(n, len(square_eligible))):
rand_indices.setdefault(i, []).append("1:1")
jobs = []
for i, (s, o, ratio, center, _rp, _rs) in enumerate(widened):
if i in rand_indices:
ratio = random.choice(rand_indices[i])
jobs.append((s, o, ratio, center))
# Subject tracking: re-detect crop center per sub-clip. # Subject tracking: re-detect crop center per sub-clip.
if self._chk_track.isChecked() and any(j[2] for j in jobs): if self._chk_track.isChecked() and any(j[2] for j in jobs):