Skip to content

extension

do_audio(fileIn, track=0, trims=None, timesource=None, timescale=TimeScale.MKV, num_frames=0, extractor=FFMpeg.Extractor(), trimmer=AutoTrimmer(), encoder=AutoEncoder(), quiet=True, output=None)

One-liner to handle the whole audio processing

Parameters:

Name Type Description Default
fileIn PathLike | src_file | AudioNode

Input file or src_file/FileInfo or AudioNode

required
track int

Audio track number

0
trims Trim | list[Trim] | None

Frame ranges to trim and/or combine, e.g. (24, -24) or [(24, 500), (700, 900)] If your passed src_file has a trim it will use it. Any other trims passed here will overwrite it.

None
timesource TimeSourceT

The source of timestamps/timecodes. For details check the docstring on the type. Will be taken from input if it's a src_file and assume the usual 24 if not.

None
timescale TimeScaleT

Unit of time (in seconds) in terms of which frame timestamps are represented. For details check the docstring on the type.

MKV
num_frames int

Total number of frames, used for negative numbers in trims Will be taken from input if it's a src_file

0
extractor Extractor

Tool used to extract the audio (Will default to None if an AudioNode gets passed)

Extractor()
trimmer Trimmer | None

Tool used to trim the audio AutoTrimmer means it will choose ffmpeg for lossy and Sox for lossless

AutoTrimmer()
encoder Encoder | None

Tool used to encode the audio AutoEncoder means it won't reencode lossy and choose opus otherwise

AutoEncoder()
quiet bool

Whether the tool output should be visible

True
output PathLike | None

Custom output file or directory, extensions will be automatically added

None

Returns:

Type Description
AudioFile

AudioFile Object containing file path, delays and source

Source code in vsmuxtools/extension/audio.py
def do_audio(
    fileIn: PathLike | src_file | vs.AudioNode,
    track: int = 0,
    trims: Trim | list[Trim] | None = None,
    timesource: TimeSourceT = None,
    timescale: TimeScaleT = TimeScale.MKV,
    num_frames: int = 0,
    extractor: Extractor = FFMpeg.Extractor(),
    trimmer: Trimmer | None = AutoTrimmer(),
    encoder: Encoder | None = AutoEncoder(),
    quiet: bool = True,
    output: PathLike | None = None,
) -> AudioFile:
    """
    One-liner to handle the whole audio processing

    :param fileIn:          Input file or src_file/FileInfo or AudioNode
    :param track:           Audio track number
    :param trims:           Frame ranges to trim and/or combine, e.g. (24, -24) or [(24, 500), (700, 900)]
                            If your passed src_file has a trim it will use it. Any other trims passed here will overwrite it.
    :param timesource:      The source of timestamps/timecodes. For details check the docstring on the type.\n
                            Will be taken from input if it's a src_file and assume the usual 24 if not.

    :param timescale:       Unit of time (in seconds) in terms of which frame timestamps are represented.\n
                            For details check the docstring on the type.

    :param num_frames:      Total number of frames, used for negative numbers in trims
                            Will be taken from input if it's a src_file

    :param extractor:       Tool used to extract the audio (Will default to None if an AudioNode gets passed)
    :param trimmer:         Tool used to trim the audio
                            AutoTrimmer means it will choose ffmpeg for lossy and Sox for lossless

    :param encoder:         Tool used to encode the audio
                            AutoEncoder means it won't reencode lossy and choose opus otherwise

    :param quiet:           Whether the tool output should be visible
    :param output:          Custom output file or directory, extensions will be automatically added
    :return:                AudioFile Object containing file path, delays and source
    """
    if trims is not None:
        if isinstance(fileIn, src_file):
            danger("Other trims passed will overwrite whatever your src_file has!", do_audio, 1)
        if isinstance(fileIn, vs.AudioNode):
            danger("Trims won't be applied if you pass an Audionode. Just do them yourself before this lol.", do_audio, 1)
            trims = None
            trimmer = None

    if isinstance(fileIn, vs.AudioNode):
        extractor = None
        fileIn = export_audionode(fileIn)
    elif isinstance(fileIn, src_file):
        if not trims:
            trims = fileIn.trim

        clip = fileIn.src
        fileIn = fileIn.file
        if not num_frames:
            num_frames = clip.num_frames
        if timesource is None:
            timesource = Fraction(clip.fps_num, clip.fps_den)

    if timesource is None:
        timesource = Fraction(24000, 1001)

    return mt_audio(fileIn, track, trims, timesource, timescale, num_frames, extractor, trimmer, encoder, quiet, output)

export_audionode(node, outfile=None)

Exports an audionode to a wav/w64 file.

Parameters:

Name Type Description Default
node AudioNode

Your audionode

required
outfile PathLike | None

Custom output path if any

None

Returns:

Type Description
Path

Returns path

Source code in vsmuxtools/extension/audio.py
def export_audionode(node: vs.AudioNode, outfile: PathLike | None = None) -> Path:
    """
    Exports an audionode to a wav/w64 file.

    :param node:            Your audionode
    :param outfile:         Custom output path if any

    :return:                Returns path
    """
    if not outfile:
        outfile = uniquify_path(Path(get_workdir(), "exported.wav"))

    outfile = ensure_path(outfile, export_audionode)
    with open(outfile, "wb") as bf:
        audio_async_render(node, bf)
    return outfile

Chapters

Bases: Chapters

Source code in vsmuxtools/extension/chapters.py
class Chapters(Ch):
    def __init__(
        self,
        chapter_source: src_file | PathLike | GlobSearch | Chapter | list[Chapter],
        timesource: TimeSourceT = None,
        timescale: TimeScaleT = TimeScale.MKV,
        _print: bool = True,
    ) -> None:
        """
        Convenience class for chapters

        :param chapter_source:      Input either src_file/FileInfo, txt with ogm chapters, xml or (a list of) self defined chapters.
        :param timesource:          The source of timestamps/timecodes. For details check the docstring on the type.\n
                                    Will be taken from input if it's a src_file and assume the usual 24 if not.

        :param timescale:           Unit of time (in seconds) in terms of which frame timestamps are represented.\n
                                    For details check the docstring on the type.

        :param _print:              Prints chapters after parsing and after trimming.
        """
        if isinstance(chapter_source, src_file):
            if isinstance(chapter_source.file, list):
                # I'll make a workaround for this soonish
                raise error("Cannot currently parse chapters when splicing multiple files.", self)
            clip_fps = Fraction(chapter_source.src.fps_num, chapter_source.src.fps_den)
            self.timestamps = resolve_timesource_and_scale(timesource if timesource else clip_fps, timescale, caller=self)
            self.chapters = parse_chapters_bdmv(chapter_source.file, clip_fps, chapter_source.src_cut.num_frames, _print)
            if self.chapters and chapter_source.trim:
                self.trim(chapter_source.trim[0], chapter_source.trim[1], chapter_source.src_cut.num_frames)
                if _print:
                    print("After trim:")
                    self.print()
        else:
            super().__init__(chapter_source, timesource or Fraction(24000, 1001), timescale, _print)

__init__(chapter_source, timesource=None, timescale=TimeScale.MKV, _print=True)

Convenience class for chapters

Parameters:

Name Type Description Default
chapter_source src_file | PathLike | GlobSearch | Chapter | list[Chapter]

Input either src_file/FileInfo, txt with ogm chapters, xml or (a list of) self defined chapters.

required
timesource TimeSourceT

The source of timestamps/timecodes. For details check the docstring on the type. Will be taken from input if it's a src_file and assume the usual 24 if not.

None
timescale TimeScaleT

Unit of time (in seconds) in terms of which frame timestamps are represented. For details check the docstring on the type.

MKV
_print bool

Prints chapters after parsing and after trimming.

True
Source code in vsmuxtools/extension/chapters.py
def __init__(
    self,
    chapter_source: src_file | PathLike | GlobSearch | Chapter | list[Chapter],
    timesource: TimeSourceT = None,
    timescale: TimeScaleT = TimeScale.MKV,
    _print: bool = True,
) -> None:
    """
    Convenience class for chapters

    :param chapter_source:      Input either src_file/FileInfo, txt with ogm chapters, xml or (a list of) self defined chapters.
    :param timesource:          The source of timestamps/timecodes. For details check the docstring on the type.\n
                                Will be taken from input if it's a src_file and assume the usual 24 if not.

    :param timescale:           Unit of time (in seconds) in terms of which frame timestamps are represented.\n
                                For details check the docstring on the type.

    :param _print:              Prints chapters after parsing and after trimming.
    """
    if isinstance(chapter_source, src_file):
        if isinstance(chapter_source.file, list):
            # I'll make a workaround for this soonish
            raise error("Cannot currently parse chapters when splicing multiple files.", self)
        clip_fps = Fraction(chapter_source.src.fps_num, chapter_source.src.fps_den)
        self.timestamps = resolve_timesource_and_scale(timesource if timesource else clip_fps, timescale, caller=self)
        self.chapters = parse_chapters_bdmv(chapter_source.file, clip_fps, chapter_source.src_cut.num_frames, _print)
        if self.chapters and chapter_source.trim:
            self.trim(chapter_source.trim[0], chapter_source.trim[1], chapter_source.src_cut.num_frames)
            if _print:
                print("After trim:")
                self.print()
    else:
        super().__init__(chapter_source, timesource or Fraction(24000, 1001), timescale, _print)

SubFile dataclass

Bases: SubFile

Utility class representing a subtitle file with various functions to run on.

Parameters:

Name Type Description Default
file PathLike | list[PathLike] | GlobSearch

Can be a string, Path object or GlobSearch. If the GlobSearch returns multiple results or if a list was passed it will merge them.

required
container_delay int

Set a container delay used in the muxing process later.

0
source PathLike | None

The file this sub originates from, will be set by the constructor.

None
encoding

Encoding used for reading and writing the subtitle files.

required
Source code in vsmuxtools/extension/sub.py
@dataclass
class SubFile(MTSubFile):
    """
    Utility class representing a subtitle file with various functions to run on.

    :param file:            Can be a string, Path object or GlobSearch.
                            If the GlobSearch returns multiple results or if a list was passed it will merge them.

    :param container_delay: Set a container delay used in the muxing process later.
    :param source:          The file this sub originates from, will be set by the constructor.
    :param encoding:        Encoding used for reading and writing the subtitle files.
    """

    def truncate_by_video(
        self: SubFileSelf,
        source: PathLike | VideoTrack | MkvTrack | VideoFile | vs.VideoNode,
        timesource: TimeSourceT = None,
        timescale: TimeScaleT = TimeScale.MKV,
    ) -> SubFileSelf:
        """
        Removes lines that start after the video ends and trims lines that extend past it.

        :param source:          Can be any video file or a VideoNode
        :param timesource:      The source of timestamps/timecodes. For details check the docstring on the type.
        :param timescale:       Unit of time (in seconds) in terms of which frame timestamps are represented.\n
                                For details check the docstring on the type.
        """

        if isinstance(source, vs.VideoNode):
            frames = source.num_frames
            if not timesource:
                timesource = Fraction(source.fps_num, source.fps_den)
            resolved_ts = resolve_timesource_and_scale(timesource, timescale, allow_warn=False, caller=self)
        else:
            if isinstance(source, VideoTrack) or isinstance(source, MkvTrack) or isinstance(source, VideoFile):
                file = ensure_path_exists(source.file, self)
            else:
                file = ensure_path_exists(source, self)

            assert get_absolute_track(file, 0, TrackType.VIDEO, self)
            assert get_executable("ffprobe")

            meta = get_timemeta_from_video(file, caller=self)
            frames = len(meta.pts)
            if not timesource:
                timesource = meta
                timescale = meta.timescale
            resolved_ts = resolve_timesource_and_scale(timesource, timescale, allow_warn=False, caller=self)

        cutoff = timedelta(milliseconds=resolved_ts.frame_to_time(frames + 1, TimeType.START, 2, True) * 10)

        def filter_lines(lines: LINES):
            removed = 0
            trimmed = 0
            new_list = list[_Line]()
            for line in lines:
                if line.start > cutoff:
                    removed += 1
                    continue
                if line.end > cutoff:
                    line.end = timedelta(milliseconds=resolved_ts.frame_to_time(frames, TimeType.END, 2, True) * 10)
                    trimmed += 1
                new_list.append(line)

            if removed or trimmed:
                if removed:
                    debug(f"Removed {removed} line{'s' if removed != 1 else ''} that started past the video", self)
                if trimmed:
                    debug(f"Trimmed {trimmed} line{'s' if trimmed != 1 else ''} that extended past the video", self)

            return new_list

        return self.manipulate_lines(filter_lines)

truncate_by_video(source, timesource=None, timescale=TimeScale.MKV)

Removes lines that start after the video ends and trims lines that extend past it.

Parameters:

Name Type Description Default
source PathLike | VideoTrack | MkvTrack | VideoFile | VideoNode

Can be any video file or a VideoNode

required
timesource TimeSourceT

The source of timestamps/timecodes. For details check the docstring on the type.

None
timescale TimeScaleT

Unit of time (in seconds) in terms of which frame timestamps are represented. For details check the docstring on the type.

MKV
Source code in vsmuxtools/extension/sub.py
def truncate_by_video(
    self: SubFileSelf,
    source: PathLike | VideoTrack | MkvTrack | VideoFile | vs.VideoNode,
    timesource: TimeSourceT = None,
    timescale: TimeScaleT = TimeScale.MKV,
) -> SubFileSelf:
    """
    Removes lines that start after the video ends and trims lines that extend past it.

    :param source:          Can be any video file or a VideoNode
    :param timesource:      The source of timestamps/timecodes. For details check the docstring on the type.
    :param timescale:       Unit of time (in seconds) in terms of which frame timestamps are represented.\n
                            For details check the docstring on the type.
    """

    if isinstance(source, vs.VideoNode):
        frames = source.num_frames
        if not timesource:
            timesource = Fraction(source.fps_num, source.fps_den)
        resolved_ts = resolve_timesource_and_scale(timesource, timescale, allow_warn=False, caller=self)
    else:
        if isinstance(source, VideoTrack) or isinstance(source, MkvTrack) or isinstance(source, VideoFile):
            file = ensure_path_exists(source.file, self)
        else:
            file = ensure_path_exists(source, self)

        assert get_absolute_track(file, 0, TrackType.VIDEO, self)
        assert get_executable("ffprobe")

        meta = get_timemeta_from_video(file, caller=self)
        frames = len(meta.pts)
        if not timesource:
            timesource = meta
            timescale = meta.timescale
        resolved_ts = resolve_timesource_and_scale(timesource, timescale, allow_warn=False, caller=self)

    cutoff = timedelta(milliseconds=resolved_ts.frame_to_time(frames + 1, TimeType.START, 2, True) * 10)

    def filter_lines(lines: LINES):
        removed = 0
        trimmed = 0
        new_list = list[_Line]()
        for line in lines:
            if line.start > cutoff:
                removed += 1
                continue
            if line.end > cutoff:
                line.end = timedelta(milliseconds=resolved_ts.frame_to_time(frames, TimeType.END, 2, True) * 10)
                trimmed += 1
            new_list.append(line)

        if removed or trimmed:
            if removed:
                debug(f"Removed {removed} line{'s' if removed != 1 else ''} that started past the video", self)
            if trimmed:
                debug(f"Trimmed {trimmed} line{'s' if trimmed != 1 else ''} that extended past the video", self)

        return new_list

    return self.manipulate_lines(filter_lines)