Advanced example script

This is an advanced example of how to use Yukine to encode and mux a video. We will create a Source object, and then use Encode and mux to encode and mux the video. In this example, we will use one Source object for multiple encodes. We will use MatchCondition and MatchSettings to select specific audio and subtitle tracks from the source file based off the track properties and we will also use pre-defined Vapoursynth filters.

Advanced script
import signal
import argparse
from pathlib import Path

from muxtools import Chapters

from yukine import (
    Encode,
    Globby,
    Source,
    Processors,
    ImportMethod,
    mux,
    Encoder,
    AudioEncoder,
    has_chapters,
    EncodingMethod,
    SetAudioTrack,
    SetSubtitleTrack,
    MatchCondition,
    MatchSettings,
    SetFilter,
)


def main():
    # Setup argparse to not have to edit the script to change the input folder.
    args = argparse.ArgumentParser()
    args.add_argument("--input", type=Path, default=Path("input"))
    args.add_argument("--temp", type=Path, default=Path("temp"))
    args.add_argument("--debug", action="store_true")
    args = args.parse_args()

    iter = 0

    for f in sorted(args.input.iterdir(), key=lambda x: x.name):
        if f.suffix == ".mkv":
            # Increment the episode number, and format it as a two digit number.
            iter += 1
            ep_num: str = f"{iter:02d}"
            # type hinting
            input_file: Path = f

            vid1 = Source(
                video_input=input_file,
                importer=ImportMethod.FFMS2,
                av1an_settings="--workers 4 -x 240",
                encoder_settings="--preset 2 --crf 30",
                video_encoder=Encoder.svt_av1,
                encoding_method=EncodingMethod.av1an,
                audio=[
                    SetAudioTrack(
                        file=input_file,
                        encoder=AudioEncoder.OPUS,
                        default=True, # (1)!
                        title="Japanese OPUS 2.0",
                        # (2)!
                        match_settings=MatchSettings(strict=True),
                        # (3)!
                        match=[
                            MatchCondition(key="language", value="jpn"), # (4)!
                            MatchCondition(key="title", value="FLAC 2.0"),
                        ],
                    ),
                    SetAudioTrack(
                        file=input_file,
                        encoder=AudioEncoder.OPUS,
                        default=True,
                        title="English OPUS 5.1",
                        match_settings=MatchSettings(strict=True),
                        match=[
                            MatchCondition(key="language", value="eng"),
                            MatchCondition(key="title", value="TrueHD 5.1"),
                        ],
                    ),
                    # (5)!
                    SetAudioTrack(
                        file=input_file,
                        encoder=AudioEncoder.OPUS,
                        default=False,
                        forced=False,
                        match_settings=MatchSettings(strict=True),
                        match=[
                            MatchCondition(key="language", value="jpn"),
                            MatchCondition(key="title", value="Commentary", regex=True),
                        ],
                    ),
                ],
                subs=[
                    SetSubtitleTrack(
                        file=input_file,
                    ),
                    SetSubtitleTrack(
                        file=Globby("*.fre.ass", dir=Path(f"wise_wolf_eraisubs/{ep_num}")), # (6)!
                        default=True,
                        forced=False,
                        lang="fr",
                        title="Français (France)",
                    ),
                ],
                # (7)!
                process_methods=[
                    SetFilter(Processors.denoise(strength=0.3)),
                    SetFilter(Processors.deband(threshold=16)),
                ],
            )

            # Even though only one `Source` object is defined,
            # we can keep this here for later use if we want
            # to define more `Source` objects.
            # This is useful if we want to compare encode settings.

            vids: list[Source] = [vid1]

            for vid in vids:
                encoder = Encode(vid)

                # Tell ChunkedEncoder to stop if we're using it and not av1an.
                def force_exit(sig, frame):
                    print("Exiting on user script.")
                    if encoder.chunked_encoder is not None:
                        encoder.chunked_encoder.stop()
                    exit(0)

                signal.signal(signal.SIGINT, force_exit)

                encoder.run()
                mux(vid)


if __name__ == "__main__":
    main()
  1. Setting Track Properties

    You can set properties for the tracks. Here we set the default and title properties for the tracks. We can also set the forced property for the tracks along with the lang and delay properties.

  2. Strict matching

    We use the match and match_settings options to match tracks based on their properties. strict=True means that the track must match all the conditions in the match list. Without it, only one condition must match.

  3. Match conditions

    We can setup conditions to match tracks based on their properties. Here we use the other_language and title properties to match the tracks. We can also use the regex=True option to treat the value as a regex pattern.

  4. Keys and where to find them.

    The keys that the match conditions use are the ones that ffprobe and mkvmerge uses. You’ll likely need to make a small script that uses muxtools.ParsedFile to get the correct properties of the tracks.

  5. One of these things just doesn’t belong here.

    When a track is not matched, it is not added to the muxed video. This can be used for dealing with occasional tracks that are not in every file, like commentary tracks.

  6. Globby

    Globby is a helper function that returns the first Path that match a glob pattern. It is used here to get a subtitle file from a folder matching a glob pattern.

  7. Processors

    We can use Processors to apply select filters to the video. Here we use Processors.denoise and Processors.deband to denoise and deband the video. There are only a select few filters available, but they can make life easier if they are the ones you need. Providing a custom Vapoursynth script is also possible via vpy_script arg in Source