From a665a31ff316540f945be36c88095d20fa2e0bc9 Mon Sep 17 00:00:00 2001 From: Mark Gregory Date: Mon, 19 Oct 2026 15:29:27 +0100 Subject: [PATCH] Initial Commit --- .gitignore | 1 + .idea/.gitignore | 10 ++ .idea/Music.iml | 12 ++ .../inspectionProfiles/profiles_settings.xml | 6 + .idea/modules.xml | 8 + audio_snippets.py | 151 ++++++++++++++++++ run.sh | 3 + 7 files changed, 191 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/Music.iml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/modules.xml create mode 100755 audio_snippets.py create mode 100755 run.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..30cf57e --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,10 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Ignored default folder with query files +/queries/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/Music.iml b/.idea/Music.iml new file mode 100644 index 0000000..5194d14 --- /dev/null +++ b/.idea/Music.iml @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..d31c1f5 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/audio_snippets.py b/audio_snippets.py new file mode 100755 index 0000000..d183b3f --- /dev/null +++ b/audio_snippets.py @@ -0,0 +1,151 @@ +#!/usr/bin/env python3 + +import argparse +import re +import subprocess +import tempfile +from pathlib import Path + +EXTENSIONS = { + ".mp3", ".m4a", ".wav", ".flac", + ".aac", ".ogg", ".aiff", ".aif" +} + + +def run(command): + subprocess.run(command, check=True) + + +def create_snippets( + source, + output, + snippet_seconds=8, + silence_seconds=4 +): + source = Path(source).expanduser().resolve() + output = Path(output).expanduser().resolve() + + if not source.is_dir(): + raise SystemExit(f"Folder not found: {source}") + + if snippet_seconds <= 0 or silence_seconds < 0: + raise SystemExit("Invalid snippet or silence duration") + + tracks = sorted( + ( + p for p in source.iterdir() + if p.is_file() + and p.suffix.lower() in EXTENSIONS + and p != output + ), + key=lambda p: [ + int(part) if part.isdigit() else part.lower() + for part in re.split(r"(\d+)", p.name) + ] + ) + + if not tracks: + raise SystemExit("No supported audio files found") + + print(f"Found {len(tracks)} tracks") + + output.parent.mkdir(parents=True, exist_ok=True) + + with tempfile.TemporaryDirectory() as temp: + temp = Path(temp) + segments = [] + + for index, track in enumerate(tracks, start=1): + print(f"[{index}/{len(tracks)}] {track.name}") + + snippet = temp / f"snippet_{index:04d}.wav" + + run([ + "ffmpeg", + "-hide_banner", + "-loglevel", "error", + "-y", + "-i", str(track), + "-t", str(snippet_seconds), + "-vn", + "-ac", "2", + "-ar", "44100", + "-c:a", "pcm_s16le", + str(snippet) + ]) + + segments.append(snippet) + + if index < len(tracks) and silence_seconds > 0: + silence = temp / f"silence_{index:04d}.wav" + + run([ + "ffmpeg", + "-hide_banner", + "-loglevel", "error", + "-y", + "-f", "lavfi", + "-i", + "anullsrc=r=44100:cl=stereo", + "-t", str(silence_seconds), + "-c:a", "pcm_s16le", + str(silence) + ]) + + segments.append(silence) + + playlist = temp / "playlist.txt" + + playlist.write_text( + "".join( + f"file '{str(segment)}'\n" + for segment in segments + ) + ) + + print("Combining snippets...") + + run([ + "ffmpeg", + "-hide_banner", + "-loglevel", "error", + "-y", + "-f", "concat", + "-safe", "0", + "-i", str(playlist), + "-c:a", "libmp3lame", + "-b:a", "192k", + str(output) + ]) + + print() + print("Complete!") + print(f"Output: {output}") + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + + parser.add_argument("source") + parser.add_argument("output") + + parser.add_argument( + "--snippet", + type=float, + default=5 + ) + + parser.add_argument( + "--silence", + type=float, + default=5 + ) + + args = parser.parse_args() + + create_snippets( + args.source, + args.output, + args.snippet, + args.silence + ) diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..09df75c --- /dev/null +++ b/run.sh @@ -0,0 +1,3 @@ +python3 "/Volumes/Developer/Applications/Music/audio_snippets.py" \ + "/Users/markgregory/Music/Sunnify/Christmas Songs - Mark-Gregory" \ + "$HOME/Downloads/christmas_comp.mp3" \ No newline at end of file