feat: group clips into subfolders (clip_001/, clip_002/, etc.)
Each batch export creates a subfolder named after the group (e.g. clip_001/) containing all sub-clips and their audio files. Keeps the top-level export folder clean. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -26,17 +26,15 @@ import mpv
|
|||||||
|
|
||||||
|
|
||||||
def build_export_path(folder: str, basename: str, counter: int, sub: int | None = None) -> str:
|
def build_export_path(folder: str, basename: str, counter: int, sub: int | None = None) -> str:
|
||||||
name = f"{basename}_{counter:03d}"
|
group = f"{basename}_{counter:03d}"
|
||||||
if sub is not None:
|
name = f"{group}_{sub}" if sub is not None else group
|
||||||
name += f"_{sub}"
|
return os.path.join(folder, group, name + ".mp4")
|
||||||
return os.path.join(folder, name + ".mp4")
|
|
||||||
|
|
||||||
|
|
||||||
def build_sequence_dir(folder: str, basename: str, counter: int, sub: int | None = None) -> str:
|
def build_sequence_dir(folder: str, basename: str, counter: int, sub: int | None = None) -> str:
|
||||||
name = f"{basename}_{counter:03d}"
|
group = f"{basename}_{counter:03d}"
|
||||||
if sub is not None:
|
name = f"{group}_{sub}" if sub is not None else group
|
||||||
name += f"_{sub}"
|
return os.path.join(folder, group, name)
|
||||||
return os.path.join(folder, name)
|
|
||||||
|
|
||||||
|
|
||||||
def format_time(seconds: float) -> str:
|
def format_time(seconds: float) -> str:
|
||||||
@@ -1799,6 +1797,9 @@ class MainWindow(QMainWindow):
|
|||||||
else:
|
else:
|
||||||
name = self._txt_name.text() or "clip"
|
name = self._txt_name.text() or "clip"
|
||||||
n_clips = self._spn_clips.value()
|
n_clips = self._spn_clips.value()
|
||||||
|
# Create the group subfolder
|
||||||
|
group_dir = os.path.join(folder, f"{name}_{self._export_counter:03d}")
|
||||||
|
os.makedirs(group_dir, exist_ok=True)
|
||||||
jobs = []
|
jobs = []
|
||||||
for sub in range(n_clips):
|
for sub in range(n_clips):
|
||||||
start = self._cursor + sub * spread
|
start = self._cursor + sub * spread
|
||||||
|
|||||||
+9
-9
@@ -4,21 +4,21 @@ from main import _normalize_filename, ProcessedDB
|
|||||||
|
|
||||||
|
|
||||||
def test_build_export_path_first():
|
def test_build_export_path_first():
|
||||||
assert build_export_path("/out", "clip", 1) == "/out/clip_001.mp4"
|
assert build_export_path("/out", "clip", 1) == "/out/clip_001/clip_001.mp4"
|
||||||
|
|
||||||
def test_build_export_path_counter():
|
def test_build_export_path_counter():
|
||||||
assert build_export_path("/out", "clip", 42) == "/out/clip_042.mp4"
|
assert build_export_path("/out", "clip", 42) == "/out/clip_042/clip_042.mp4"
|
||||||
|
|
||||||
def test_build_export_path_deep_counter():
|
def test_build_export_path_deep_counter():
|
||||||
assert build_export_path("/out", "shot", 999) == "/out/shot_999.mp4"
|
assert build_export_path("/out", "shot", 999) == "/out/shot_999/shot_999.mp4"
|
||||||
|
|
||||||
def test_build_export_path_sub():
|
def test_build_export_path_sub():
|
||||||
assert build_export_path("/out", "clip", 1, sub=0) == "/out/clip_001_0.mp4"
|
assert build_export_path("/out", "clip", 1, sub=0) == "/out/clip_001/clip_001_0.mp4"
|
||||||
assert build_export_path("/out", "clip", 1, sub=2) == "/out/clip_001_2.mp4"
|
assert build_export_path("/out", "clip", 1, sub=2) == "/out/clip_001/clip_001_2.mp4"
|
||||||
|
|
||||||
def test_build_sequence_dir_sub():
|
def test_build_sequence_dir_sub():
|
||||||
assert build_sequence_dir("/out", "clip", 1, sub=0) == "/out/clip_001_0"
|
assert build_sequence_dir("/out", "clip", 1, sub=0) == "/out/clip_001/clip_001_0"
|
||||||
assert build_sequence_dir("/out", "clip", 1, sub=1) == "/out/clip_001_1"
|
assert build_sequence_dir("/out", "clip", 1, sub=1) == "/out/clip_001/clip_001_1"
|
||||||
|
|
||||||
def test_format_time_seconds():
|
def test_format_time_seconds():
|
||||||
assert format_time(0.0) == "0:00.0"
|
assert format_time(0.0) == "0:00.0"
|
||||||
@@ -216,10 +216,10 @@ def test_audio_extract_timing():
|
|||||||
|
|
||||||
|
|
||||||
def test_build_sequence_dir_basic():
|
def test_build_sequence_dir_basic():
|
||||||
assert build_sequence_dir("/out", "clip", 1) == "/out/clip_001"
|
assert build_sequence_dir("/out", "clip", 1) == "/out/clip_001/clip_001"
|
||||||
|
|
||||||
def test_build_sequence_dir_counter():
|
def test_build_sequence_dir_counter():
|
||||||
assert build_sequence_dir("/out", "clip", 42) == "/out/clip_042"
|
assert build_sequence_dir("/out", "clip", 42) == "/out/clip_042/clip_042"
|
||||||
|
|
||||||
def test_ffmpeg_command_image_sequence():
|
def test_ffmpeg_command_image_sequence():
|
||||||
cmd = build_ffmpeg_command("/in/v.mp4", 0.0, "/out/seq_001", image_sequence=True)
|
cmd = build_ffmpeg_command("/in/v.mp4", 0.0, "/out/seq_001", image_sequence=True)
|
||||||
|
|||||||
Reference in New Issue
Block a user