From 14bb4ac95f5080634f85891fea247adfdc29f410 Mon Sep 17 00:00:00 2001 From: w33b Date: Thu, 19 Mar 2026 18:16:12 +0100 Subject: [PATCH] Add Linux/Windows support --- utils/encode_downloads.py | 12 ++++---- utils/encode_stream.py | 27 +++++++++-------- utils/interpolate.py | 47 ++++++++++++++++++++++-------- utils/upcale.py | 61 +++++++++++++++++++++++++++------------ 4 files changed, 95 insertions(+), 52 deletions(-) diff --git a/utils/encode_downloads.py b/utils/encode_downloads.py index 20a2655..c8e2e08 100644 --- a/utils/encode_downloads.py +++ b/utils/encode_downloads.py @@ -21,7 +21,7 @@ def _encode_video( hentai_title: str, input_aspect: str = "16:9" ): - print(f'Encoding {preset['h']}p AV1') + print(f"Encoding {preset['h']}p AV1") cmd = [ "ffmpeg", @@ -33,12 +33,12 @@ def _encode_video( "-map", "1:t?", # Attachments from source video (optional) "-map", "1:d?", # Other Data from source video (optional) "-disposition:v:0", "default", # Mark video as default in mkv container - "-metadata", f'Title=\"{hentai_title} [hstream.moe]\"', + "-metadata", f"Title={hentai_title} [hstream.moe]", "-c:v", "libsvtav1", "-crf", preset['crf'], "-preset", "4", - "-pix_fmt", "yuv420p10le", # 10bit - "-vf", f"scale=\'min({preset['w']},iw)\':-2,setsar=1:1", + "-pix_fmt", "yuv420p10le", + "-vf", f"scale=min({preset['w']}\\,iw):-2,setsar=1:1", "-aspect", input_aspect, "-c:a", "libopus", "-b:a", "160k", @@ -46,10 +46,8 @@ def _encode_video( output_video ] - print(cmd) - try: - subprocess.run(cmd, shell=True, check=True) + subprocess.run(cmd, check=True) except subprocess.CalledProcessError as e: print(f"\nffmpeg failed with error code {e.returncode}", file=sys.stderr) sys.exit(e.returncode) diff --git a/utils/encode_stream.py b/utils/encode_stream.py index e0651d9..201353c 100644 --- a/utils/encode_stream.py +++ b/utils/encode_stream.py @@ -69,12 +69,11 @@ def _encode_720p_fallback( ] try: - subprocess.run(cmd, shell=True, check=True) + subprocess.run(cmd, check=True) except subprocess.CalledProcessError as e: print(f"\nffmpeg failed with error code {e.returncode}", file=sys.stderr) sys.exit(e.returncode) - def _change_chunk_extension( preset: dict[str, str], cdn_folder: str, @@ -141,36 +140,36 @@ def _encode( "-map", "1:a:0", # Audio from Source "-c:v", preset['encoder'], "-preset", preset['preset'], - "-crf", preset['crf'], + "-crf", str(preset['crf']), "-pix_fmt", "yuv420p", # 8bit to increase decode performance "-vf", f"scale={preset['w']}:{preset['h']},setsar=1:1", "-aspect", aspect_ratio, ] if preset["encoder"] == "libx264": - cmd += ["-x264-params", f"keyint=24:min-keyint=24:scenecut=0"] + cmd += ["-x264-params", "keyint=24:min-keyint=24:scenecut=0"] cmd += ["-c:a", "aac", "-b:a", "160k"] elif preset["encoder"] == "libsvtav1": - cmd += ["-svtav1-params", f"keyint={keyframe_interval}s,fast-decode=1,tune=0"] + cmd += ["-svtav1-params", f"keyint={keyframe_interval}s:fast-decode=1:tune=0"] cmd += ["-c:a", "aac", "-b:a", "160k"] - + + output_path = os.path.join(cdn_folder, preset['out_folder'], 'manifest.mpd') + cmd += [ "-ac", "2", "-sn", # No subtitles "-map_metadata", "-1", # Get rid of metadata which might be incorrect "-use_template", "1", # Don't list every segment url, use template instead "-use_timeline", "1", # Make sure segment timing is always correct - "-init_seg_name", "chunks/init-stream$RepresentationID$.webm", # Init segment - "-media_seg_name", "chunks/chunk-stream$RepresentationID$-$Number%05d$.webm", # Media segments - "-seg_duration", str(segment_duration), # DASH segment duration - "-f", "dash", - os.path.join(cdn_folder, preset['out_folder'], 'manifest.mpd') + "-init_seg_name", "chunks/init-stream$RepresentationID$.webm", + "-media_seg_name", "chunks/chunk-stream$RepresentationID$-$Number%05d$.webm", + "-seg_duration", str(segment_duration), + "-f", "dash", + output_path ] - print(cmd) - try: - subprocess.run(cmd, shell=True, check=True) + subprocess.run(cmd, check=True) except subprocess.CalledProcessError as e: print(f"\nffmpeg failed with error code {e.returncode}", file=sys.stderr) sys.exit(e.returncode) diff --git a/utils/interpolate.py b/utils/interpolate.py index d746280..9ddbd5c 100644 --- a/utils/interpolate.py +++ b/utils/interpolate.py @@ -40,20 +40,43 @@ def _interpolate( vapoursynth_file: str, interpolate_output: str, ): - cmd = [ - "vspipe", - "-c", "y4m", - vapoursynth_file, - "-", "|", - "ffmpeg", "-v", "quiet", "-stats", - "-i", "-", - "-c:v", "hevc_nvenc", - "-qp", "5", - interpolate_output - ] + print('Started Interpolation') try: - subprocess.run(cmd, shell=True, check=True) + # First process (vspipe) + vspipe = subprocess.Popen( + [ + "vspipe", + "-c", "y4m", + vapoursynth_file, + "-" + ], + stdout=subprocess.PIPE + ) + + # Second process (ffmpeg), reading from vspipe + ffmpeg = subprocess.Popen( + [ + "ffmpeg", + "-v", "quiet", "-stats", + "-f", "yuv4mpegpipe", + "-i", "-", + "-c:v", "hevc_nvenc", + "-qp", "5", + interpolate_output + ], + stdin=vspipe.stdout + ) + + # Important: allow vspipe to receive SIGPIPE if ffmpeg exits + vspipe.stdout.close() + + ffmpeg.wait() + vspipe.wait() + + if ffmpeg.returncode != 0: + raise subprocess.CalledProcessError(ffmpeg.returncode, "ffmpeg") + except subprocess.CalledProcessError as e: print(f"\nffmpeg failed with error code {e.returncode}", file=sys.stderr) sys.exit(e.returncode) diff --git a/utils/upcale.py b/utils/upcale.py index ffe449c..6573acb 100644 --- a/utils/upcale.py +++ b/utils/upcale.py @@ -20,25 +20,29 @@ def _re_encode( :type input_aspect: str """ + fps = get_framerate(source_video) + + vf_filter = f"fps={fps},scale=-1:min({MAX_INPUT_WIDTH}\\,ih)" + cmd = [ "ffmpeg", "-v", "quiet", "-stats", "-i", source_video, "-c:v", "ffv1", "-level", "3", - "-vf", f"fps={get_framerate(source_video)},scale=-1:\'min({MAX_INPUT_WIDTH},ih)\'", + "-vf", vf_filter, "-aspect", input_aspect, "-pix_fmt", "yuv420p", "-color_primaries", "1", "-color_trc", "1", "-colorspace", "1", - "-an", + "-an", "-sn", "-map_metadata", "-1", temp_out_video ] try: - subprocess.run(cmd, shell=True, check=True) + subprocess.run(cmd, check=True) except subprocess.CalledProcessError as e: print(f"\nffmpeg failed with error code {e.returncode} at _re_encode()", file=sys.stderr) sys.exit(e.returncode) @@ -51,23 +55,42 @@ def _upscale( vapoursynth_script = os.path.join('utils', 'vs-realesrgan.vpy') - cmd = [ - "vspipe", - "-c", "y4m", - vapoursynth_script, - "-", # Video output to pipe - "|", # Pipe - "ffmpeg", "-v", "quiet", "-stats", - "-f", "yuv4mpegpipe", - "-i", "-", # Pipe Video Input - "-c:v", "hevc_nvenc", - "-qp", "5", - "-aspect", input_aspect, - upscale_output - ] - try: - subprocess.run(cmd, shell=True, check=True) + # First process (vspipe) + vspipe = subprocess.Popen( + [ + "vspipe", + "-c", "y4m", + vapoursynth_script, + "-" + ], + stdout=subprocess.PIPE + ) + + # Second process (ffmpeg), reading from vspipe + ffmpeg = subprocess.Popen( + [ + "ffmpeg", + "-v", "quiet", "-stats", + "-f", "yuv4mpegpipe", + "-i", "-", + "-c:v", "hevc_nvenc", + "-qp", "5", + "-aspect", input_aspect, + upscale_output + ], + stdin=vspipe.stdout + ) + + # Important: allow vspipe to receive SIGPIPE if ffmpeg exits + vspipe.stdout.close() + + ffmpeg.wait() + vspipe.wait() + + if ffmpeg.returncode != 0: + raise subprocess.CalledProcessError(ffmpeg.returncode, "ffmpeg") + except subprocess.CalledProcessError as e: print(f"\nffmpeg failed with error code {e.returncode}", file=sys.stderr) sys.exit(e.returncode)