Skip to content

y5gfunc.source.source

source

Functions:

Name Description
wobbly_source

Loads a video from a wobbly .wob project file.

bestsource

Loads a video source using bestsource (bs.VideoSource).

load_source

Bestsource and Wobbly wrapper to load a video source.

load_dv_p7

Loads a Dolby Vision Profile 7 video file.

wobbly_source

wobbly_source(wob_project_path: Union[str, Path], timecodes_v2_path: Optional[Union[str, Path]] = None) -> VideoNode

Loads a video from a wobbly .wob project file.

Parameters:

Name Type Description Default

wob_project_path

Union[str, Path]

Path to the .wob project file.

required

timecodes_v2_path

Optional[Union[str, Path]]

Optional path to a V2 timecodes file.

None

Returns:

Type Description
VideoNode

A VapourSynth VideoNode representing the processed video clip.

Source code in y5gfunc/source/source.py
def wobbly_source(
    wob_project_path: Union[str, Path],
    timecodes_v2_path: Optional[Union[str, Path]] = None,
) -> vs.VideoNode:
    """
    Loads a video from a wobbly .wob project file.

    Args:
        wob_project_path: Path to the .wob project file.
        timecodes_v2_path: Optional path to a V2 timecodes file.

    Returns:
        A VapourSynth VideoNode representing the processed video clip.
    """
    clip = load_and_process(
        wob_project_path, timecodes_v2_path, timecode_version="v2"
    ).std.SetFieldBased(False)
    return clip

bestsource

bestsource(file_path: Union[Path, str], track: int = 0, timecodes_v2_path: Optional[Union[Path, str]] = None, variableformat: int = -1, rff: bool = False) -> VideoNode

Loads a video source using bestsource (bs.VideoSource).

Parameters:

Name Type Description Default

file_path

Union[Path, str]

Path to the video file.

required

track

int

Index of the video track to load.

0

timecodes_v2_path

Optional[Union[Path, str]]

Path to a V2 timecodes file.

None

variableformat

int

See bestsource documentation.

-1

rff

bool

See bestsource documentation.

False

Returns: A VapourSynth VideoNode loaded by bestsource.

Source code in y5gfunc/source/source.py
def bestsource(
    file_path: Union[Path, str],
    track: int = 0,
    timecodes_v2_path: Optional[Union[Path, str]] = None,
    variableformat: int = -1,
    rff: bool = False,
) -> vs.VideoNode:
    """
    Loads a video source using bestsource (bs.VideoSource).

    Args:
        file_path: Path to the video file.
        track: Index of the video track to load.
        timecodes_v2_path: Path to a V2 timecodes file.
        variableformat: See bestsource documentation.
        rff: See bestsource documentation.
    Returns:
        A VapourSynth VideoNode loaded by bestsource.
    """
    if timecodes_v2_path:
        return BestSource.source(
            file=str(file_path),
            track=track,
            variableformat=variableformat,
            timecodes=str(timecodes_v2_path),
            rff=rff,
        )
    else:
        return BestSource.source(
            file=str(file_path),
            track=track,
            variableformat=variableformat,
            rff=rff,
        )

load_source

load_source(file_path: Union[Path, str], track: int = 0, matrix: Optional[Matrix] = None, matrix_in: Optional[Matrix] = None, timecodes_v2_path: Optional[Union[Path, str]] = None) -> VideoNode

Bestsource and Wobbly wrapper to load a video source.

This function acts as a primary interface for loading video sources. It checks the file extension: - If it's a ".wob" file, it uses wobbly_source. - Otherwise, it uses bestsource, attempting to automatically detect if the source uses RFF (Repeat First Field) based on frame counts.

After loading, it applies a color matrix conversion.

Parameters:

Name Type Description Default

file_path

Union[Path, str]

Path to the video file or .wob project file.

required

track

int

Index of the video track to load. Ignored for .wob files.

0

matrix

Optional[Matrix]

Target color matrix.

None

matrix_in

Optional[Matrix]

Input color matrix.

None

timecodes_v2_path

Optional[Union[Path, str]]

Path to a V2 timecodes file.

None

Returns:

Type Description
VideoNode

A VapourSynth VideoNode representing the loaded and matrix-converted video clip.

Raises:

Type Description
FileNotFoundError

If the resolved file_path does not exist.

AssertionError

If a .wob file is specified but track is not 0.

Source code in y5gfunc/source/source.py
def load_source(
    file_path: Union[Path, str],
    track: int = 0,
    matrix: Optional[Matrix] = None,
    matrix_in: Optional[Matrix] = None,
    timecodes_v2_path: Optional[Union[Path, str]] = None,
) -> vs.VideoNode:
    """
    Bestsource and Wobbly wrapper to load a video source.

    This function acts as a primary interface for loading video sources.
    It checks the file extension:
        - If it's a ".wob" file, it uses `wobbly_source`.
        - Otherwise, it uses `bestsource`, attempting to automatically detect if the source uses RFF (Repeat First Field) based on frame counts.

    After loading, it applies a color matrix conversion.

    Args:
        file_path: Path to the video file or .wob project file.
        track: Index of the video track to load. Ignored for .wob files.
        matrix: Target color matrix.
        matrix_in: Input color matrix.
        timecodes_v2_path: Path to a V2 timecodes file.

    Returns:
        A VapourSynth VideoNode representing the loaded and matrix-converted video clip.

    Raises:
        FileNotFoundError: If the resolved `file_path` does not exist.
        AssertionError: If a `.wob` file is specified but `track` is not 0.
    """
    file_path = resolve_path(file_path)

    if not file_path.exists():
        raise FileNotFoundError(f"load_source: File {file_path} does not exist.")

    if file_path.suffix.lower() == ".wob":
        assert track == 0
        clip = wobbly_source(file_path, timecodes_v2_path)
    else:
        # modified from https://guides.vcb-s.com/basic-guide-10/#%E6%A3%80%E6%B5%8B%E6%98%AF%E5%90%A6%E4%B8%BA%E5%85%A8%E7%A8%8B-soft-pulldownpure-film
        a = bestsource(file_path, rff=False)
        b = bestsource(file_path, rff=True)
        rff = False if abs(b.num_frames * 0.8 - a.num_frames) < 1 else True

        clip = bestsource(file_path, track, timecodes_v2_path, rff=rff)

    if matrix is None:
        matrix = Matrix.from_res(clip)

    if matrix_in is None:
        matrix_in = Matrix.from_res(clip)

    primaries = Primaries.from_matrix(matrix)
    primaries_in = Primaries.from_matrix(matrix_in)
    transfer = Transfer.from_matrix(matrix).value_vs
    transfer_in = Transfer.from_matrix(matrix).value_vs

    return clip.resize2.Spline36(
        matrix=matrix,
        matrix_in=matrix_in,
        primaries=primaries,
        primaries_in=primaries_in,
        transfer=transfer,
        transfer_in=transfer_in,
    )

load_dv_p7

load_dv_p7(file_path: Union[Path, str], bl_index: int = 0, el_index: int = 1, rpu_file_path: Optional[Union[Path, str]] = None) -> VideoNode

Loads a Dolby Vision Profile 7 video file.

This function incorporates workarounds for an underlying FFmpeg issue that causes source filters (e.g., bestsource) to fail to extract the DolbyVisionRpu frame property from the last few frames of a video. For more details, see: https://github.com/vapoursynth/bestsource/issues/97

Workarounds: 1. Default method (rpu_file_path=None): It uses FFMS2 to extract the RPU data from the video file. While convenient, this method has a significant caveat: FFMS2 might return stale RPU data for the final frames. Specifically, it carries over the RPU from the last valid frame even after seeking. This behavior is documented in the discussion of the linked issue.

  1. External RPU file (rpu_file_path is provided): This method uses an externally supplied RPU file. This is the most reliable workaround as it completely bypasses reliance on FFmpeg for RPU extraction. RPU data is required to be extracted into a file first using dovi_tool and then provided to this function.

Parameters:

Name Type Description Default

file_path

Union[Path, str]

Path to the video file.

required

bl_index

int

Index of the Base Layer (BL) stream.

0

el_index

int

Index of the Enhancement Layer (EL) stream.

1

rpu_file_path

Optional[Union[Path, str]]

Optional path to the RPU file.

None

Returns:

Type Description
VideoNode

A VapourSynth VideoNode representing the Dolby Vision P7 video clip.

Source code in y5gfunc/source/source.py
def load_dv_p7(
    file_path: Union[Path, str],
    bl_index: int = 0,
    el_index: int = 1,
    rpu_file_path: Optional[Union[Path, str]] = None,
) -> vs.VideoNode:
    """
    Loads a Dolby Vision Profile 7 video file.

    This function incorporates workarounds for an underlying FFmpeg issue that causes source filters
    (e.g., bestsource) to fail to extract the `DolbyVisionRpu` frame property from the last few
    frames of a video. For more details, see: https://github.com/vapoursynth/bestsource/issues/97

    Workarounds:
    1.  Default method (rpu_file_path=None): It uses FFMS2 to extract the RPU data from the video file.
        While convenient, this method has a significant caveat: FFMS2 might return stale RPU data for the final frames.
        Specifically, it carries over the RPU from the last valid frame even after seeking. This behavior is documented
        in the discussion of the linked issue.

    2.  External RPU file (rpu_file_path is provided): This method uses an externally supplied RPU file.
        This is the most reliable workaround as it completely bypasses reliance on FFmpeg for RPU extraction.
        RPU data is required to be extracted into a file first using dovi_tool and then provided to this function.

    Args:
        file_path: Path to the video file.
        bl_index: Index of the Base Layer (BL) stream.
        el_index: Index of the Enhancement Layer (EL) stream.
        rpu_file_path: Optional path to the RPU file.

    Returns:
        A VapourSynth VideoNode representing the Dolby Vision P7 video clip.
    """
    file_path = resolve_path(file_path)

    bl = LSMAS.source(
        file_path,
        stream_index=bl_index,
        chroma_location=ChromaLocation.TOP_LEFT,
    ).resize2.Spline36(format=vs.YUV420P16)

    el = (
        LSMAS.source(file_path, stream_index=el_index)
        .resize2.Point(width=bl.width, height=bl.height, format=vs.YUV420P10)
        .std.PlaneStats()
    )

    if rpu_file_path:
        bl = write_rpu(rpu_file_path, bl)
        el = write_rpu(rpu_file_path, el)
    else:
        rpu = FFMS2.source(file_path, track=el_index)
        bl = bl.std.CopyFrameProps(rpu, "DolbyVisionRPU")
        el = el.std.CopyFrameProps(rpu, "DolbyVisionRPU")

    bl = tonemap(
        bl, src_csp=ColorSpace.DOLBY_VISION, dst_csp=ColorSpace.HDR10
    ).resize2.Spline36(format=vs.YUV420P16)

    hdr = core.vsnlq.MapNLQ(bl, el).std.SetFrameProps(
        _Matrix=Matrix.BT2020NCL,
        _Primaries=PRIMARIES_BT2020,
        _Transfer=Transfer.ST2084.value_vs,
    )

    hdr = core.akarin.PropExpr([hdr, el], lambda: {"_FEL": "y.PlaneStatsAverage 0 >"})
    return hdr