11 Commits

Author SHA1 Message Date
da56bc3a81 Removed extra scene-detection step 2025-08-29 07:38:49 +02:00
1c371e80a8 don't add metadata when extracting audio 2025-08-28 12:59:39 +02:00
17afcb1579 adjusted the workers, "lp" and progress output 2025-08-28 12:02:06 +02:00
359ff6f04c level of parallelism 2025-08-28 11:52:48 +02:00
ba05642ad6 Double line print 2025-08-28 11:37:20 +02:00
6cfdb40e7f New changes to code 2025-08-28 11:34:21 +02:00
d325866162 Again some updates for Svt-Parameters 2025-08-28 11:25:31 +02:00
4b3f087104 CleaneUr Output ; Updates Svt-Params 2025-08-28 11:23:14 +02:00
665272ffc9 Updated script to include Svt-Av1-Essentials 2025-08-28 11:01:14 +02:00
8bc672ca6b added option for mono audio 2025-08-09 16:34:06 +02:00
5f108ade66 fix for negative crop with autocrop 2025-08-06 13:43:00 +02:00
3 changed files with 52 additions and 37 deletions

View File

@@ -85,7 +85,9 @@ def convert_audio_track(stream_index, channels, temp_dir, source_file, should_do
# Step 3: Encode to Opus with the correct bitrate
bitrate = "192k" # Fallback
if final_channels == 2:
if final_channels == 1:
bitrate = "64k"
elif final_channels == 2:
bitrate = "128k"
elif final_channels == 6:
bitrate = "256k"

View File

@@ -19,6 +19,21 @@ DIR_CONV_LOGS = Path("conv_logs") # Directory for conversion logs
REMUX_CODECS = {"aac", "opus"} # Using a set for efficient lookups
SVT_AV1_PARAMS = {
"speed": "slower", # "slower", "slow", "medium", "fast", "faster"
"quality": "medium", # "higher", "high", "medium", "low", "lower"
"film-grain": 6,
"color-primaries": 1,
"transfer-characteristics": 1,
"matrix-coefficients": 1,
"scd": 0, # Scene change detection OFF for Av1an use
"keyint": 0, # Keyframe interval, 0 disables automatic keyframes placement at a constant interval
"lp": 2, # Level of parallelism
"auto-tiling": 1, # Auto tiling ON
"tune": 1, # 0 = VQ, 1 = PSNR, 2 = SSIM
"progress": 2, # Detailed progress output
}
def check_tools():
for tool in REQUIRED_TOOLS:
if shutil.which(tool) is None:
@@ -40,7 +55,7 @@ def convert_audio_track(index, ch, lang, audio_temp_dir, source_file, should_dow
print(f" - Extracting Audio Track #{index} to FLAC...")
ffmpeg_args = [
"ffmpeg", "-v", "quiet", "-stats", "-y", "-i", str(source_file), "-map", f"0:{index}"
"ffmpeg", "-v", "quiet", "-stats", "-y", "-i", str(source_file), "-map", f"0:{index}", "-map_metadata", "-1"
]
if should_downmix and ch >= 6:
if ch == 6:
@@ -68,14 +83,16 @@ def convert_audio_track(index, ch, lang, audio_temp_dir, source_file, should_dow
else:
# Not downmixing (or source is already stereo or less).
# Base bitrate on the source channel count.
if ch == 2: # Stereo
if ch == 1: # Mono
bitrate = "64k"
elif ch == 2: # Stereo
bitrate = "128k"
elif ch == 6: # 5.1 Surround
bitrate = "256k"
elif ch == 8: # 7.1 Surround
bitrate = "384k"
else: # Mono or other layouts
bitrate = "96k" # A sensible default for mono.
else: # Other layouts
bitrate = "96k" # A sensible default for other/uncommon layouts.
print(f" - Encoding Audio Track #{index} to Opus at {bitrate}...")
run_cmd([
@@ -86,7 +103,6 @@ def convert_audio_track(index, ch, lang, audio_temp_dir, source_file, should_dow
def convert_video(source_file_base, source_file_full, is_vfr, target_cfr_fps_for_handbrake, autocrop_filter=None):
print(" --- Starting Video Processing ---")
# source_file_base is file_path.stem (e.g., "my.anime.episode.01")
scene_file = Path(f"{source_file_base}.txt")
vpy_file = Path(f"{source_file_base}.vpy")
ut_video_file = Path(f"{source_file_base}.ut.mkv")
encoded_video_file = Path(f"temp-{source_file_base}.mkv")
@@ -159,39 +175,19 @@ clip.set_output()
with vpy_file.open("w", encoding="utf-8") as f:
f.write(vpy_script_content)
if not scene_file.exists():
print(" - Performing scene detection with av1an...")
av1an_sc_args = [
"av1an", "-i", str(vpy_file), "-s", str(scene_file), "--sc-only", "--verbose"
]
run_cmd(av1an_sc_args)
else:
print(" - Found existing scene file, skipping detection.")
print(" - Starting AV1 encode with av1an (this will take a long time)...")
total_cores = os.cpu_count() or 4 # Fallback if cpu_count is None
workers = max(total_cores - 2, 1) # Ensure at least 1 worker
print(f" - Using {workers} workers for av1an (Total Cores: {total_cores}).")
workers = max(1, (total_cores // 2) - 1) # Half the cores minus one, with a minimum of 1 worker.
print(f" - Using {workers} workers for av1an (Total Cores: {total_cores}, Logic: (Cores/2)-1).")
svt_av1_params = {
"preset": 2,
"crf": 27,
"film-grain": 6,
"lp": 1,
"tune": 1,
"keyint": -1,
"color-primaries": 1,
"transfer-characteristics": 1,
"matrix-coefficients": 1,
}
# Create the parameter string for av1an's -v option, which expects a single string.
av1an_video_params_str = " ".join([f"--{key} {value}" for key, value in svt_av1_params.items()])
av1an_video_params_str = " ".join([f"--{key} {value}" for key, value in SVT_AV1_PARAMS.items()])
print(f" - Using SVT-AV1 parameters: {av1an_video_params_str}")
av1an_enc_args = [
"av1an", "-i", str(vpy_file), "-o", str(encoded_video_file), "-s", str(scene_file), "-n",
"av1an", "-i", str(vpy_file), "-o", str(encoded_video_file), "-n",
"-e", "svt-av1", "--resume", "--sc-pix-format", "yuv420p", "-c", "mkvmerge",
"--set-thread-affinity", "1", "--pix-format", "yuv420p10le", "--force",
"--set-thread-affinity", "2", "--pix-format", "yuv420p10le", "--force",
"-w", str(workers),
"-v", av1an_video_params_str
]
@@ -270,17 +266,21 @@ def _snap_to_known_ar_cropdetect(w, h, x, y, video_w, video_h, tolerance=0.03):
new_h = round(video_w / best_match['ratio'])
if new_h % 8 != 0:
new_h = new_h + (8 - (new_h % 8))
new_h = min(new_h, video_h)
new_y = round((video_h - new_h) / 2)
if new_y % 2 != 0:
new_y -= 1
new_y = max(0, new_y)
return f"crop={video_w}:{new_h}:0:{new_y}", best_match['name']
if abs(h - video_h) < 16:
new_w = round(video_h * best_match['ratio'])
if new_w % 8 != 0:
new_w = new_w + (8 - (new_w % 8))
new_w = min(new_w, video_w)
new_x = round((video_w - new_w) / 2)
if new_x % 2 != 0:
new_x -= 1
new_x = max(0, new_x)
return f"crop={new_w}:{video_h}:{new_x}:0", best_match['name']
return f"crop={w}:{h}:{x}:{y}", None
@@ -433,8 +433,17 @@ def detect_autocrop_filter(input_file, significant_crop_threshold=5.0, min_crop=
return None
return _analyze_video_cropdetect(input_file, duration, width, height, max(1, os.cpu_count() // 2), significant_crop_threshold, min_crop, debug)
def main(no_downmix=False, autocrop=False):
def main(no_downmix=False, autocrop=False, speed=None, quality=None, grain=None):
check_tools()
# Override default SVT-AV1 params if provided via command line
if speed:
SVT_AV1_PARAMS["speed"] = speed
if quality:
SVT_AV1_PARAMS["quality"] = quality
if grain is not None:
SVT_AV1_PARAMS["film-grain"] = grain
current_dir = Path(".")
files_to_process = sorted(
f for f in current_dir.glob("*.mkv")
@@ -638,7 +647,6 @@ def main(no_downmix=False, autocrop=False):
print("Cleaning up persistent video temporary files (after successful processing)...")
video_temp_files_on_success = [
current_dir / f"{file_path.stem}.txt",
current_dir / f"{file_path.stem}.vpy",
current_dir / f"{file_path.stem}.ut.mkv",
current_dir / f"temp-{file_path.stem}.mkv", # This is encoded_video_file
@@ -720,5 +728,8 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Batch-process MKV files with resumable video encoding, audio downmixing, per-file logging, and optional autocrop.")
parser.add_argument("--no-downmix", action="store_true", help="Preserve original audio channel layout.")
parser.add_argument("--autocrop", action="store_true", help="Automatically detect and crop black bars from video using cropdetect.")
parser.add_argument("--speed", type=str, help="Set the encoding speed. Possible values: slower, slow, medium, fast, faster.")
parser.add_argument("--quality", type=str, help="Set the encoding quality. Possible values: lowest, low, medium, high, higher.")
parser.add_argument("--grain", type=int, help="Set the film-grain value (number). Adjusts the film grain synthesis level.")
args = parser.parse_args()
main(no_downmix=args.no_downmix, autocrop=args.autocrop)
main(no_downmix=args.no_downmix, autocrop=args.autocrop, speed=args.speed, quality=args.quality, grain=args.grain)

View File

@@ -90,14 +90,16 @@ def convert_audio_track(index, ch, lang, audio_temp_dir, source_file, should_dow
else:
# Not downmixing (or source is already stereo or less).
# Base bitrate on the source channel count.
if ch == 2: # Stereo
if ch == 1: # Mono
bitrate = "64k"
elif ch == 2: # Stereo
bitrate = "128k"
elif ch == 6: # 5.1 Surround
bitrate = "256k"
elif ch == 8: # 7.1 Surround
bitrate = "384k"
else: # Mono or other layouts
bitrate = "96k" # A sensible default for mono.
else: # Other layouts
bitrate = "96k" # A sensible default for other/uncommon layouts.
print(f" - Encoding Audio Track #{index} to Opus at {bitrate}...")
run_cmd([