Skip to content

Tracks API

Auto-generated reference for the track model: Tracks, Track, and the media-specific subclasses. See Service Architecture for how these fit together.

Attachment

Attachment(
    path=None,
    url=None,
    name=None,
    mime_type=None,
    description=None,
    session=None,
)

Make a new Attachment.

If providing a path, the file must already exist. If providing a URL, download() fetches the file during the download phase. You must give either path or url.

If name is not provided it will use the file name (without extension). If mime_type is not provided, it will try to guess it.

Parameters:

Name Type Description Default
path Union[Path, str, None]

Path to an existing file.

None
url Optional[str]

URL to download the attachment from.

None
name Optional[str]

Name of the attachment.

None
mime_type Optional[str]

MIME type of the attachment.

None
description Optional[str]

Description of the attachment.

None
session Optional[AnySession]

Optional requests HTTP session to download the file with.

None

id property

id

Compute an ID from the attachment data.

download

download(session=None, *, no_proxy_download=False)

Download a URL-backed attachment to the temp directory.

to_dict

to_dict()

Serialise a URL-backed attachment for export/import.

from_url classmethod

from_url(
    url,
    name=None,
    mime_type=None,
    description=None,
    session=None,
)

Make an attachment from a URL.

Parameters:

Name Type Description Default
url str

URL to download the attachment from.

required
name Optional[str]

Name of the attachment.

None
mime_type Optional[str]

MIME type of the attachment.

None
description Optional[str]

Description of the attachment.

None
session Optional[AnySession]

Optional requests HTTP session to download the file with.

None

Returns:

Name Type Description
Attachment 'Attachment'

A new attachment instance.

Audio

Audio(
    *args,
    codec=None,
    bitrate=None,
    channels=None,
    joc=None,
    descriptive=False,
    **kwargs,
)

Bases: Track

Make a new Audio track object.

Parameters:

Name Type Description Default
codec Optional[Codec]

An Audio.Codec enum representing the audio codec. If not specified, unshackle uses MediaInfo to get the codec after it downloads the track.

None
bitrate Optional[Union[str, int, float]]

A number or float representing the average bandwidth in bits/s. unshackle rounds float values up to the nearest integer.

None
channels Optional[Union[str, int, float]]

A number, float, or string representing the number of audio channels. Strings may represent numbers or floats. Expanded layouts like 7.1.1 is not supported. All numbers and strings will be cast to float.

None
joc Optional[int]

The number of Joint-Object-Coding Channels/Objects in the audio track.

None
descriptive Union[bool, int]

Mark this audio as being descriptive audio for the blind.

False

Note: If codec, bitrate, channels, or joc is not specified some checks may be skipped or assume a value. Specifying as much information as possible is highly recommended.

atmos property

atmos

Return True if the audio track contains Atmos.

parse_channels staticmethod

parse_channels(channels)

Converts a Channel string to a float representing the audio channel layout. E.g. "3" -> "3.0", "2.1" -> "2.1", ".1" -> "0.1".

This does not validate channel strings as genuine channel counts or valid layouts. It does not convert the value to assume a sub speaker channel layout, e.g. 5.1->6.0. It also cannot read expanded surround sound channel layout strings like 7.1.2.

to_music_container

to_music_container()

Remux the track into the container of its codec, as a standalone audio file.

Music titles never reach the muxer, so the downloaded file is the delivered file, and after decryption that is still a fragmented MP4. Its header states a length of zero, so a player stops after the first fragment. FLAC inside an MP4 is also not a FLAC stream. A rename cannot correct either fault. Returns True once the new file replaces the downloaded one; every failure raises.

get_track_name

get_track_name()

Return the base Track Name.

Chapter

Chapter(timestamp, name=None)

Make a new Chapter with a Timestamp and optional name.

The timestamp may be in the following formats: - "HH:MM:SS" string, e.g., 25:05:23. - "HH:MM:SS.mss" string, e.g., 25:05:23.120. - a timecode integer in milliseconds, e.g., 90323120 is 25:05:23.120. - a timecode float in seconds, e.g., 90323.12 is 25:05:23.120.

If you have a timecode integer in seconds, multiply it by 1000. If you have a timecode float in milliseconds (no decimal value), convert it to an integer.

id property

id

Compute an ID from the Chapter data.

named property

named

True if the Chapter has a name.

Chapters

Chapters(iterable=None)

Bases: SortedKeyList, ABC

id property

id

Compute an ID from the Chapter data.

loads classmethod

loads(data)

Load chapter data from a string.

load classmethod

load(path)

Load chapter data from a file.

dumps

dumps(fallback_name='')

Return chapter data in OGM-based Simple Chapter format. https://mkvtoolnix.download/doc/mkvmerge.html#mkvmerge.chapters.simple

Parameters:

Name Type Description Default
fallback_name str

Name used for Chapters without a Name set.

''

The fallback name can use the following variables in f-string style:

  • {i}: The Chapter number starting at 1. E.g., "Chapter {i}": "Chapter 1", "Intro", "Chapter 3".
  • {j}: A number starting at 1 that increments any time a Chapter has no name. E.g., "Chapter {j}": "Chapter 1", "Intro", "Chapter 2".

unshackle formats these with f-strings, so format directives work. For example, "Chapter {i:02}" will result in "Chapter 01".

dump

dump(path, *args, **kwargs)

Write chapter data in OGM-based Simple Chapter format to a file.

Parameters:

Name Type Description Default
path Union[Path, str]

The file path to write the Chapter data to, overwriting any existing data.

required

See Chapters.dumps for more parameter documentation.

Hybrid

Hybrid(videos, source, output_name=None)

log instance-attribute

log = logging.getLogger('hybrid')

Takes the Dolby Vision and HDR10(+) streams out of the VideoTracks. It will then attempt to inject the Dolby Vision metadata layer to the HDR10(+) stream. If no DV track is available but HDR10+ is present, it will convert HDR10+ to DV.

level_5

level_5(input_video)

Make Level 5 active area metadata through crop detection on the HDR10 track.

This resolves mismatches where DV has no black bars but HDR10 does (or vice versa) by telling the display the correct active area.

sanitize_l6 staticmethod

sanitize_l6(max_mdl, min_mdl, max_cll, max_fall)

Clamp static L6 values to a valid relationship.

MaxCLL must not exceed the mastering-display peak (some HDR10+ sources ship MaxCLL 10000 on a 1000-nit master), and MaxFALL must not exceed MaxCLL. A value of 0 means "unknown", and this method keeps it as-is.

level_6

level_6()

Edit RPU Level 6 values using the static L6 luminance data from the RPU.

extract_hdr10plus

extract_hdr10plus(video)

Extract HDR10+ metadata from the video track

probe_hdr_metadata

probe_hdr_metadata()

Extract mastering display and Content Light Level metadata from the HDR10 track with FFprobe.

Returns (max_mdl, min_mdl, max_cll, max_fall) in dovi_tool level6 units: - max_mdl: nits (integer) - min_mdl: 0.0001 nit units (integer) - max_cll / max_fall: nits (integer)

convert_hdr10plus_to_dv

convert_hdr10plus_to_dv()

Convert HDR10+ metadata to Dolby Vision RPU

Subtitle

Subtitle(
    *args,
    codec=None,
    cc=False,
    sdh=False,
    forced=False,
    **kwargs,
)

Bases: Track

Make a new Subtitle track object.

Parameters:

Name Type Description Default
codec Optional[Codec]

A Subtitle.Codec enum representing the subtitle format. If not specified, unshackle uses MediaInfo to get the format after it downloads the track.

None
cc bool

Closed Caption. - Intended as if you could not hear the audio at all. - Can have Sound as well as Dialogue, but does not have to. - Original source would be from EIA-CC encoded video. Typically all upper-case characters. Indicators of it being CC without knowing original source: - Extracted with CCExtractor, or - >>> (or similar) at the start of some or all lines, or - All text is uppercase or at least the majority, or - Subtitles use the scrolling-text style (one line appears, oldest line then disappears). The fact that you downloaded it as a SRT or VTT or such does not mean it is not from an EIA-CC stream. And I would not take the streaming services (CC) as gospel either as they tend to get it wrong too.

False
sdh bool

Deaf or Hard-of-Hearing. Also known as HOH in the UK (EU?). - Intended as if you could not hear the audio at all. - MUST have Sound as well as Dialogue to count as SDH. - It has no "syntax" or "format", but it is not transmitted in archaic forms like EIA-CC streams. It is intended for transmission through SubRip (SRT), WebVTT (VTT), TTML, and the other modern subtitle formats. If you can see important audio/sound transcriptions and not only dialogue and it does not have the indicators of CC, then it is most likely SDH. If it does not have important audio/sounds transcriptions it might only be regular subtitling (you would not mark as CC or SDH). This would be the case for most translation subtitles. Like Anime for example.

False
forced bool

Typically used if there is important information at some point in time like watching a Dubbed title and an important Sign or Letter is shown or someone talking in a different language. The Matroska Spec recommends that the player plays Forced tracks if the player's current playback audio language matches a subtitle marked as "forced". However, that does not mean every player works like this but there is no other way to reliably work with Forced subtitles where multiple forced subtitles may be in the output file. Know what to expect with "forced" subtitles.

False

Note: If codec is not specified, unshackle can skip some checks or assume a value. Give as much information as possible.

Information on Subtitle Types

https://bit.ly/2Oe4fLC (3PlayMedia Blog on SUB vs CC vs SDH). However, I would not pay much attention to the claims about SDH needing to be in the original language. It is logically not true.

CC == Closed Captions. Source: Every site. SDH = Subtitles for the Deaf or Hard-of-Hearing. Source: Every site. HOH = Exact same as SDH. Is a term used in the UK. Source: https://bit.ly/2PGJatz (ICO UK)

More in-depth information, examples, and stuff to look for can be found in the Parameter explanation list above.

get_track_name

get_track_name()

Return the base Track Name.

strip_webvtt_cue_classes staticmethod

strip_webvtt_cue_classes(text)

Reduce a cue payload to the markup every target format shares: <i>, <b> and <u> without their class list. It removes class spans (<c.foo>), <v>, <lang>, <ruby>/<rt> and karaoke timestamps (<00:00:01.000>).

A class only selects a CSS ::cue rule that no player receives, so it never carries styling, but it does break rendering. FFmpeg's SubRip reader treats a tag name as a tag only while it matches [0-9a-zA-Z_/]. The dot fails that test, so FFmpeg prints <i.loud> as cue text and then emits the closing italic-off with nothing opened. FFmpeg's WebVTT reader matches i/b/u exactly and drops the rest, so there a class silently loses the styling instead. The tags this method removes outright are the ones both readers already discard, and that libass would draw as text if a conversion carried them into ASS.

strip_webvtt_timestamp_map staticmethod

strip_webvtt_timestamp_map(text)

Remove X-TIMESTAMP-MAP header lines (RFC 8216 §3.5, HLS only).

Cue times are already absolute, but SubtitleEdit reads the header and offsets every cue by it during conversion.

sanitize_webvtt_timestamps staticmethod

sanitize_webvtt_timestamps(text)

Fix invalid timestamps in WebVTT files, particularly negative timestamps.

Parameters:

Name Type Description Default
text str

The WebVTT text as string

required

Returns:

Type Description
str

Sanitized WebVTT text

has_webvtt_cue_identifiers staticmethod

has_webvtt_cue_identifiers(text)

Examine WebVTT text for cue identifiers that need removal.

Parameters:

Name Type Description Default
text str

The WebVTT text as string

required

Returns:

Type Description
bool

True if the text has cue identifiers, False otherwise

sanitize_webvtt_cue_identifiers staticmethod

sanitize_webvtt_cue_identifiers(text)

Remove WebVTT cue identifiers that can confuse subtitle parsers.

Some services use cue identifiers such as "Q0" and "Q1" that appear on their own line before the timing line. These can be incorrectly parsed as part of the previous cue's text by some parsers (like pysubs2).

Parameters:

Name Type Description Default
text str

The WebVTT text as string

required

Returns:

Type Description
str

Sanitized WebVTT text with cue identifiers removed

parse_vtt_time staticmethod

parse_vtt_time(t)

Parse WebVTT timestamp to milliseconds. Returns 0 for malformed input.

has_overlapping_webvtt_cues staticmethod

has_overlapping_webvtt_cues(text)

Examine WebVTT text for overlapping cues that need merging.

Detects cues with start times within 50ms of each other and the same end time, which indicates multi-line subtitles split into separate cues.

Parameters:

Name Type Description Default
text str

The WebVTT text as string

required

Returns:

Type Description
bool

True if the text has overlapping cues, False otherwise

merge_overlapping_webvtt_cues staticmethod

merge_overlapping_webvtt_cues(text)

Merge WebVTT cues that have overlapping/near-identical times but different line positions.

Some services use separate cues for each line of a multi-line subtitle, with slightly different start times (1ms apart) and different line: positions. This merges them into single cues with proper line ordering based on the line: position (lower percentage = higher on screen = first line).

Parameters:

Name Type Description Default
text str

The WebVTT text as string

required

Returns:

Type Description
str

WebVTT text with overlapping cues merged

sanitize_webvtt staticmethod

sanitize_webvtt(text)

More thorough sanitization of WebVTT files that corrects multiple potential issues.

This is lossy, so use it only as a fallback once normal parsing has failed. This method discards everything before the WEBVTT header line and reduces the header itself to a bare "WEBVTT" line. Negative timestamps become 00:00:00.000, and it pads timestamps that have no hours field to HH:MM:SS.mmm.

Parameters:

Name Type Description Default
text str

The WebVTT text as string

required

Returns:

Type Description
str

Sanitized WebVTT text

convert

convert(codec, *, forced=False)

Convert this Subtitle to another format.

Backend selection is data-driven (see tracks/subtitle_convert.py): unshackle uses the best available backend that can convert source->target, and falls back through the capability chain on failure. The conversion_method config key (auto | subby | pysubs2 | subtitleedit | pycaption) pins the backend, and preferred_conversion_method nudges it per-service. An explicit config value always wins.

forced marks an explicit user request (--sub-format). unshackle skips lossy downconverts of styled formats (SSA/ASS -> SRT) unless forced is True.

extract_fonts staticmethod

extract_fonts(text)

Font names referenced by an ASS/SSA subtitle.

Covers both sources that need attaching for correct rendering: - the Fontname column of every Style: line in [V4+ Styles]/[V4 Styles] (column located from the section's Format: line, not assumed by index), and - inline \fn font overrides inside Dialogue override blocks.

This method removes a leading @ (vertical-writing prefix), de-duplicates the names case-insensitively, and prefers a mixed-case spelling over an all-lowercase one.

sanitize_broken_webvtt staticmethod

sanitize_broken_webvtt(text)

Remove or fix corrupted WebVTT lines, particularly those with invalid timestamps.

Parameters:

Name Type Description Default
text str

The WebVTT text as string

required

Returns:

Type Description
str

Sanitized WebVTT text with corrupted lines removed

space_webvtt_headers staticmethod

space_webvtt_headers(data)

Space out the WEBVTT Headers from Captions.

Segmented VTT when merged may have the WEBVTT headers part of the next caption as they were not separated far enough from the previous caption and ended up as caption text rather than the header for the next segment.

merge_same_cues staticmethod

merge_same_cues(caption_set)

Merge captions with the same timecodes and text as one in-place.

filter_unwanted_cues staticmethod

filter_unwanted_cues(caption_set)

Filter out subtitle cues containing only   or whitespace.

merge_segmented_wvtt staticmethod

merge_segmented_wvtt(data, period_start=0.0)

Convert Segmented DASH WebVTT cues into a pycaption Caption List. Also returns an ISO 639-2 alpha-3 language tag if available.

Code ported originally by xhlove to Python from shaka-player. rlaphoenix has since improved it with pymp4 and pycaption functions.

strip_hearing_impaired

strip_hearing_impaired()

Remove the SDH captions.

The 'sdh_method' setting in config gives the SDH stripping method: - 'auto' (default): Tries subby first, then SubtitleEdit, then filter-subs - 'subby': Uses subby's SDHStripper - 'subtitleedit': Uses SubtitleEdit when available - 'filter-subs': Uses subtitle-filter library

reverse_rtl

reverse_rtl()

Reverse RTL (Right to Left) Start/End on Captions. Use this to fix the positioning of sentence-ending characters.

DownloadContext dataclass

DownloadContext(
    save_path,
    save_dir,
    progress,
    session=None,
    proxy=None,
    max_workers=None,
    adaptive_workers=False,
    download_processes=1,
    license_widevine=None,
    cdm=None,
)

Shared arguments passed to each manifest's download_track.

ensure_session

ensure_session()

Return the HTTP session, or a new Session if none was set.

Track

Track(
    url,
    language,
    is_original_lang=False,
    descriptor=URL,
    needs_repack=False,
    name=None,
    drm=None,
    edition=None,
    session=None,
    downloader=None,
    downloader_args=None,
    from_file=None,
    data=None,
    id_=None,
    extra=None,
)

data property writable

data

Arbitrary track data dictionary.

This uses a defaultdict with a dict as the factory for easier nested saving and safer exists-checks.

Reserved keys:

  • "hls" used by the HLS class.
  • playlist: m3u8.model.Playlist - The primary track information.
  • media: m3u8.model.Media - The audio/subtitle track information.
  • segment_durations: list[int] - A list of each segment's duration.
  • "dash" used by the DASH class.
  • manifest: lxml.ElementTree - DASH MPD manifest.
  • period: lxml.Element - The period of this track.
  • adaptation_set: lxml.Element - The adaptation set of this track.
  • representation: lxml.Element - The representation of this track.
  • timescale: int - The timescale of the track's segments.
  • segment_durations: list[int] - A list of each segment's duration.

You should not add, change, or remove any data within reserved keys. You may use their data, but note that these values can change or be removed at any point.

drm_preference property writable

drm_preference

DRM system this track must license with, one of the names in DRM_PREFERENCE_TYPES.

None (the default) lets the loaded CDM choose. Set it when the manifest advertises more than one DRM system but only one of them licenses this track.

prefers_playready

prefers_playready(cdm)

Whether to try PlayReady before Widevine. The track's preference wins over the loaded CDM.

download

download(
    session,
    prepare_drm,
    max_workers=None,
    progress=None,
    *,
    cdm=None,
    no_proxy_download=False,
    adaptive_workers=False,
    download_processes=1,
)

Download and optionally Decrypt this Track.

For a URL-descriptor Video or Audio track with no drm set, unshackle probes the DRM from the track's init data and stores it on the track, so a service need not declare drm itself.

move

move(target)

Move the Track's file from current location, to target location. This will overwrite anything at the target path.

Raises:

Type Description
TypeError

If the target argument is not the expected type.

ValueError

If track has no file to move, or the target does not exist.

OSError

If the file somehow failed to move.

Returns the new location of the track.

to_dict

to_dict()

Serialise the track for export/import (identity/URL/descriptor/language).

DRM is not serialised here. The export writer attaches the licensed DRM + keys. Subclasses add their own codec/quality fields.

base_kwargs_from_dict staticmethod

base_kwargs_from_dict(data)

Assemble the shared Track constructor kwargs from a to_dict() payload.

DRM is not reconstructed here: to_dict does not serialise it, and the import flow attaches the licensed DRM + content keys separately.

from_dict classmethod

from_dict(data)

Reconstruct the correct Track subclass from a to_dict() payload.

get_track_name

get_track_name()

Get the Track Name.

get_drm_for_cdm

get_drm_for_cdm(cdm)

Return the DRM matching the provided CDM, if available.

get_key_id

get_key_id(init_data=None, *args, **kwargs)

Probe the DRM encryption Key ID (KID) for this specific track.

It can find the Key ID by probing the track with FFprobe for enc_key_id data, as well as for mp4 tenc (Track Encryption) boxes.

It explicitly ignores PSSH information like the PSSH box, as the box is likely to contain multiple Key IDs that may or may not be for this specific track.

To retrieve the initialization segment, this method calls :meth:get_init_segment with the positional and keyword arguments. This method then uses the return value of get_init_segment to find the Key ID.

Returns:

Type Description
Optional[UUID]

The Key ID as a UUID object, or None if unshackle cannot find the Key ID.

load_drm_if_needed

load_drm_if_needed(service=None)

Load DRM information for this track if the parser deferred it.

Parameters:

Name Type Description Default
service Service | None

Service instance that can fetch track-specific DRM info

None

Returns:

Type Description
bool

True if the DRM loaded or is already present, False if the load failed

load_drm_from_playlist

load_drm_from_playlist()

Fallback method to load DRM by fetching this track's individual playlist.

get_init_segment

get_init_segment(
    maximum_size=20000,
    url=None,
    byte_range=None,
    session=None,
)

Get the Track's initial segment data.

HLS and DASH tracks must explicitly give a URL to the init segment or file. Give the byte-range for the init segment where possible.

If byte_range is not set, it will make a HEAD request and examine the size of the file. If it cannot find the size, it will download up to the first 20KB only, which should contain the entirety of the init segment. You may override this by changing the maximum_size.

The default maximum_size of 20000 (20KB) is a tried-and-tested value that seems to work well across the board.

Parameters:

Name Type Description Default
maximum_size int

Size to assume as the response body length if byte-range is not used, if unshackle cannot find the body size, or if the body size is larger than it. Use a value of 20000 (20KB) or higher.

20000
url Optional[str]

Explicit init map or file URL to probe from.

None
byte_range Optional[str]

Range of bytes to download from the explicit or implicit URL.

None
session Optional[Session]

HTTP session context, for example authorization and headers.

None

repackage

repackage(bsf_v=None)

Remux the track with FFmpeg -c copy.

A given bsf_v goes into the same pass as -bsf:v, which normalises video VUI colour metadata without a second full-file remux. Repackaging is mandatory. The bitstream filter is best-effort: if the combined pass fails, and it is not the AAC-retry case, unshackle tries it again once without bsf_v so the remux still succeeds. Returns True if unshackle applied the requested bsf_v (always False when bsf_v is None, because the caller requested nothing).

Tracks

Tracks(*args, manifest_url=None)

Video, Audio, Subtitle, Chapter, and Attachment Track Store. It provides convenience functions for listing, sorting, and selecting tracks.

exists

exists(by_id=None, by_url=None)

Examine whether a track already exists, by various methods.

add

add(tracks, warn_only=False)

Add a provided track to its appropriate array.

A track whose ID is already in the collection raises ValueError. With warn_only set, this method skips such a track and logs how many it skipped.

sort_videos

sort_videos(by_language=None, exact_match=False)

Sort video tracks by resolution then bitrate, and optionally language.

sort_audio

sort_audio(
    by_language=None, codec_priority=None, exact_match=False
)

Sort audio tracks by bitrate, codec priority, Atmos, descriptive, and optionally language.

sort_subtitles

sort_subtitles(
    by_language=None,
    type_priority=None,
    group_by=None,
    exact_match=False,
)

Sort subtitle tracks by various track attributes to a common P2P standard. You may optionally give a sequence of languages to prioritise to the top.

Section Order
  • by_language groups prioritized to top, and ascending alphabetically
  • then rest ascending alphabetically after the prioritized groups (Each section ascending alphabetically, but separated)
Type Order
  • Forced
  • Normal
  • Hard of Hearing (SDH/CC) (Least to most captions expected in the subtitle)

type_priority overrides the Type Order with an explicit ranking of "forced", "normal", and "sdh" (cc counts as sdh). Unlisted types fall to the end.

group_by sets the major sort order. "type" (default) keeps every forced track together, then every normal, then every SDH, each block ascending by language. "language" groups by language instead, so Finnish sits next to Finnish SDH, with the Type Order applied inside each language.

exact_match makes a by_language entry sort only its own tag. By default "en" also sorts "en-US" and "en-GB".

filter

filter(predicate)

Return a new Tracks with tracks filtered by predicate, preserving metadata.

merge_video_selections staticmethod

merge_video_selections(*groups)

Concatenate video selections, dropping duplicates (by track id, order-preserving).

A caller can choose a DV track as both the hybrid ingredient (lowest) and an explicit deliverable. Without dedup, unshackle would mux and download the same track twice.

partition_hybrid_videos staticmethod

partition_hybrid_videos(videos, non_hybrid_ranges)

Split videos into hybrid-ingredient candidates and the standalone-deliverable pool.

HDR10/HDR10+/DV tracks are hybrid ingredients. They only enter the standalone pool when the user explicitly requested their range alongside HYBRID, so for example -r HYBRID muxes only the hybrid while -r HYBRID,HDR10P also delivers HDR10+.

flag_hybrid_ingredients staticmethod

flag_hybrid_ingredients(
    hybrid_selected, non_hybrid_selected
)

Mark tracks selected only as hybrid ingredients so the standalone mux loop skips them.

A track that the caller also selected as an explicit deliverable (same track in both selections) stays unflagged, and the standalone mux loop muxes it alongside the hybrid.

mux

mux(
    title,
    delete=True,
    progress=None,
    audio_expected=True,
    title_language=None,
    skip_subtitles=False,
    output_path=None,
)

Multiplex all the Tracks into a Matroska Container file.

A failed mux does not raise. mkvmerge's exit code and error lines come back alongside the output path, and the caller must examine them.

Parameters:

Name Type Description Default
title str

Set the Matroska Container file title. Usually displayed in players instead of the filename if set.

required
delete bool

Delete all track files after multiplexing.

True
progress Optional[partial]

Update a rich progress bar with completed=.... This must be the progress object's update() func, pre-set with task id by functools.partial.

None
audio_expected bool

Whether the output must have audio. unshackle uses this to decide if it adds embedded audio metadata.

True
title_language Optional[Language]

The title's intended language. Used to select the best video track for audio metadata when multiple video tracks exist.

None
skip_subtitles bool

Skip muxing subtitle tracks into the container.

False
output_path Optional[Path]

Explicit destination for the muxed container. When None (default) unshackle derives the path from the first track, so callers muxing several track groups that share a video list must pass distinct paths to avoid clobbering each other.

None

Video

Video(
    *args,
    codec=None,
    range_=None,
    bitrate=None,
    width=None,
    height=None,
    fps=None,
    scan_type=None,
    closed_captions=None,
    dv_compatible_bitstream=False,
    **kwargs,
)

Bases: Track

Make a new Video track object.

Parameters:

Name Type Description Default
codec Optional[Codec]

A Video.Codec enum representing the video codec. If not specified, unshackle uses MediaInfo to get the codec after it downloads the track.

None
range_ Optional[Range]

A Video.Range enum representing the video colour range. Defaults to SDR if not specified.

None
bitrate Optional[Union[str, int, float]]

A number or float representing the average bandwidth in bytes/s. unshackle rounds float values up to the nearest integer.

None
width Optional[int]

The horizontal resolution of the video.

None
height Optional[int]

The vertical resolution of the video.

None
fps Optional[Union[str, int, float]]

A number, float, or string representing the frames/s of the video. Strings may represent numbers, floats, or a fraction (num/den). All strings will be cast to either a number or float.

None

Note: If codec, bitrate, width, height, or fps is not specified, unshackle can skip some checks or assume a value. Give as much information as possible.

Range

Bases: str, Enum

from_cicp staticmethod

from_cicp(primaries, transfer, matrix)

Convert CICP (Coding-Independent Code Points) values to Video Range.

ITU-T H.273 and ISO/IEC 23091-2 define CICP for signalling video colour properties independently of the compression codec. These values are used across AVC (H.264), HEVC (H.265), VVC, AV1, and other modern codecs.

The enum values (Primaries, Transfer, Matrix) match the official specifications: - ITU-T H.273: Coding-independent code points for video signal type identification - ISO/IEC 23091-2: Information technology - Coding-independent code points - Part 2: Video - H.264 Table E-3 (Colour Primaries) and Table E-4 (Transfer Characteristics) - H.265 Table E.3 and E.4 (identical to H.264)

Note: Value 0 = "Reserved" and Value 2 = "Unspecified" per specification. While both effectively mean "unknown" in practice, the distinction matters for spec compliance. Value 2 was added after user feedback (GitHub issue) and matches FFmpeg's AVColorPrimaries/AVColorTransferCharacteristic enums.

Sources: - https://www.itu.int/rec/T-REC-H.273 - https://www.itu.int/rec/T-REC-H.Sup19-202104-I - https://github.com/FFmpeg/FFmpeg/blob/master/libavutil/pixfmt.h

change_color_range

change_color_range(range_)

Change the Video's Colour Range to Limited (0) or Full (1).

vui_bsf

vui_bsf()

Return the -bsf:v value that rewrites SPS VUI colour metadata to match self.range.

Returns None when a rewrite is not necessary: SDR/DV/HYBRID ranges, non-AVC/HEVC codecs, a missing file, or when the bitstream already ships the correct colour metadata. Otherwise returns a {h264,hevc}_metadata=... string suitable for FFmpeg -bsf:v.

normalize_vui

normalize_vui()

Rewrite SPS VUI colour metadata to match self.range.

Some services ship HDR10/HLG bitstreams with stale BT.709 VUI, which makes downstream tools mis-classify the file. The manifest-derived range is the source of truth. Skips SDR, DV, and HYBRID. Returns True if unshackle rewrote the bitstream.

ccextractor

ccextractor(track_id, out_path, language, original=False)

Return a TextTrack object representing CC track extracted by CCExtractor.

extract_c608

extract_c608()

Extract Apple-Style c608 box (CEA-608) subtitle using CCExtractor.

This is not much more than a wrapper to the track.ccextractor function. All this does is examine if a c608 box exists, and only then does it call CCExtractor.

Even though there is a possibility of more than one c608 box, unshackle can extract only one. It is also very possible this needs to be done before any decryption as the decryption may destroy some of the metadata.

Need a test file with more than one c608 box, before unshackle can

extract more than one CEA-608 track.

remove_eia_cc

remove_eia_cc()

Remove EIA-CC data from Bitstream while keeping SEI data.

This works by removing all NAL Unit's with the Type of 6 from the bistream and then re-adding SEI data (effectively a new NAL Unit with only the SEI data). unshackle can do this only for bitstreams with x264 encoding information, because of the obscurity on the MDAT mp4 box structure. Therefore, we need to use hacky regex.