Initial Commit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
.idea
|
||||
Generated
+10
@@ -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
|
||||
Generated
+12
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PackageRequirementsSettings" />
|
||||
<component name="PyDocumentationSettings" />
|
||||
<component name="ReSTService" />
|
||||
<component name="TestRunnerService" />
|
||||
</module>
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
||||
Generated
+8
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/Music.iml" filepath="$PROJECT_DIR$/.idea/Music.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
||||
Executable
+151
@@ -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
|
||||
)
|
||||
Reference in New Issue
Block a user