CLI tool to analyze Blu-ray BDMV/ folders, detect episodic playlists, explain disc structure, and remux one MKV per episode.
Anime Blu-ray discs hide episodes behind complex playlist structures — shared OP/ED segments, "Play All" concatenations, duplicate variants with different streams, and menus. bdpl parses the raw disc metadata to figure out which playlists are actual episodes and in what order.
Requires Python 3.10+.
pip install -e .For remuxing, you also need MKVToolNix (mkvmerge on PATH).
Point bdpl at a BDMV/ directory from a disc backup:
# Detect episodes and write structured JSON
bdpl scan /path/to/BDMV -o disc.json
# See what bdpl found and why
bdpl explain /path/to/BDMV
# Generate debug playlists for previewing in a media player
bdpl playlist /path/to/BDMV --out ./Playlists
# Extract menu-driven digital archive stills as images
bdpl archive /path/to/BDMV --out ./DigitalArchive
# Remux episodes to MKV with chapters and named tracks
bdpl remux /path/to/BDMV --out ./Episodes============================================================
Disc Summary
============================================================
Path: /mnt/bluray/BDMV
Playlists: 11
Clips: 15
------------------------------------------------------------
Playlists
------------------------------------------------------------
Name Duration Items Class
---- -------- ----- -----
00000.mpls 00:46 2 extra
00001.mpls 00:01 1 bumper
00002.mpls 01:21:06 4 play_all
00003.mpls 01:06 1 creditless_op
00004.mpls 01:43 1 creditless_ed
...
------------------------------------------------------------
Episodes
------------------------------------------------------------
Ep 1 26:15 conf=0.80 clips=[00007]
Ep 2 27:16 conf=0.80 clips=[00008]
Ep 3 27:22 conf=0.80 clips=[00009]
------------------------------------------------------------
Special Features (9)
------------------------------------------------------------
1. 00003.mpls 01:06 creditless_op
2. 00004.mpls 01:43 creditless_ed
3. 00005.mpls 02:00 creditless_ed
...
6. 00008.mpls ch.0 01:22 creditless_ed
7. 00008.mpls ch.1 00:16 creditless_ed
8. 00009.mpls 00:16 extra
9. 00010.mpls 00:17 extra
------------------------------------------------------------
Warnings
------------------------------------------------------------
[PLAY_ALL_ONLY] Episodes were inferred by decomposing Play All playlist
------------------------------------------------------------
Disc Hints
------------------------------------------------------------
index.bdmv: 9 title(s)
MovieObject.bdmv: 11 object(s), 11 with playlist refs
Title 0 -> 00002.mpls
Title 1 -> 00003.mpls
...
IG menu: 59 button action(s), chapter marks=[0, 1, 6, 11]
Detect episode playlists and emit a structured JSON mapping.
bdpl scan /path/to/BDMV -o disc.json # Write to file
bdpl scan /path/to/BDMV --stdout --pretty # Print to terminal
bdpl scan /path/to/BDMV --stdout --compact # Machine-readableOutput includes:
- Full playlist inventory with durations, streams, and chapters
- Episode candidates with confidence scores
- Episode scene segments (menu-scene boundaries when available)
- Special features (creditless OP/ED, extras, previews) detected from IG menus
- Playlist classifications (episode, play_all, bumper, creditless_op, etc.)
- Warnings for ambiguous or low-confidence results
Human-readable breakdown of the disc structure and analysis reasoning.
bdpl explain /path/to/BDMV
bdpl explain /path/to/BDMV --playlist 00002.mpls # Detail for one playlistGenerate .m3u debug playlists for previewing episodes in a media player.
bdpl playlist /path/to/BDMV --out ./PlaylistsRemux episodes to MKV with chapters and named tracks. Requires mkvmerge (MKVToolNix).
bdpl remux /path/to/BDMV --out ./Episodes
bdpl remux /path/to/BDMV --pattern "My Show (2024) - S01E{ep:02d}.mkv"
bdpl remux /path/to/BDMV --dry-run
# Also remux special features (creditless OP/ED, extras, previews)
bdpl remux /path/to/BDMV --specials
bdpl remux /path/to/BDMV --specials --specials-pattern "My Show - S00E{idx:02d} - {category}.mkv"Default filenames use Plex/Jellyfin-compatible SxxExx format with the disc folder name
(e.g., UCG_0080_D1 - S01E01.mkv, UCG_0080_D1 - S00E01 - creditless_op.mkv).
Pattern variables: {name} (disc folder), {ep} (episode #), {idx} (special #), {category} (special type).
Extract still images for digital archive playlists (menu/gallery-style content).
bdpl archive /path/to/BDMV --out ./DigitalArchive
bdpl archive /path/to/BDMV --format png
bdpl archive /path/to/BDMV --dry-runThe command detects playlists classified as digital_archive and captures one
frame per archive item via ffmpeg, naming outputs as
{playlist}-{index}-{clip_id}.{ext}.
bdpl reads the raw BDMV binary structures — no external tools needed for analysis:
- Parse
PLAYLIST/*.mplsfiles to extract play items (clip references with in/out timestamps), chapters, and stream tables - Parse
CLIPINF/*.clpifiles for stream metadata (codecs, languages) - Parse disc hints from
index.bdmv(title→playlist mapping),MovieObject.bdmv(navigation commands), and IG menu streams (button→chapter mappings) - Analyze the playlist graph:
- Compute segment signatures and deduplicate near-identical playlists
- Cluster playlists by duration to find episode-length candidates
- Detect "Play All" playlists (concatenations of other playlists)
- Label shared segments as OP, ED, BODY, PREVIEW, LEGAL
- Infer episode order using multiple strategies (see below)
- Boost confidence when navigation hints and IG menu data confirm episode boundaries
- Detect special features from IG menu
JumpTitlebuttons pointing to non-episode playlists (creditless OP/ED, extras, previews) - Extract scenes for each episode from IG/title hints and chapter anchors (fallback for metadata-only fixtures)
- Export results as JSON, text reports, M3U playlists, or MKV remux (including specials)
- Individual episode playlists: Each episode has its own MPLS with a unique "body" segment, plus shared OP/ED. Episodes are ordered by body clip ID.
- Play All decomposition: Some discs (common in anime) only have a single "Play All" playlist. bdpl decomposes it — each long play item (>10 min) becomes an episode.
- Chapter-based splitting: When a disc has a single long m2ts with multiple chapters but no separate playlists, bdpl splits into episodes using chapter boundaries and target duration heuristics.
When disc hints show a single main title plus a separate digital archive title, bdpl keeps the main title as one episode instead of forcing chapter-based splits.
Each detected episode gets a confidence score (0–1) based on how it was identified:
| Source | Base | Possible boosts |
|---|---|---|
| Individual playlists | 0.9 | +0.1 title hint |
| Play All decomposition | 0.7 | +0.1 title hint |
| Chapter splitting | 0.6 | +0.1 title hint, +0.1 IG chapter marks |
The scan output uses schema version bdpl.disc.v1:
{
"schema_version": "bdpl.disc.v1",
"disc": { "path": "...", "generated_at": "2026-02-08T..." },
"playlists": [
{
"mpls": "00002.mpls",
"duration_ms": 4866528.3,
"play_items": [
{
"clip_id": "00007",
"m2ts": "00007.m2ts",
"duration_ms": 1575073.5,
"label": "BODY",
"streams": [
{ "pid": 4113, "codec": "H.264/AVC", "lang": "" },
{ "pid": 4352, "codec": "LPCM", "lang": "jpn" },
{ "pid": 4608, "codec": "PGS", "lang": "jpn" },
{ "pid": 4609, "codec": "PGS", "lang": "eng" }
]
}
],
"chapters": [{ "mark_id": 0, "mark_type": 1, "timestamp": 188955000 }]
}
],
"episodes": [
{
"episode": 1,
"playlist": "00002.mpls",
"duration_ms": 1575073.5,
"confidence": 0.70,
"scenes": [
{
"key": ["SCENE", "00002.mpls", 1],
"clip_id": "00007",
"in_ms": 0.0,
"out_ms": 393768.1,
"duration_ms": 393768.1,
"label": "SCENE"
}
]
}
],
"warnings": [{ "code": "PLAY_ALL_ONLY", "message": "..." }],
"analysis": { "classifications": { "00002.mpls": "play_all" } }
}pip install -e ".[dev]"
pytest tests/ -vbdpl/
bdpl/
cli.py # Typer CLI
model.py # Dataclasses (Playlist, Episode, etc.)
bdmv/
reader.py # Big-endian binary reader
mpls.py # MPLS playlist parser
clpi.py # CLPI clip info parser
index_bdmv.py # index.bdmv title mapping parser
movieobject_bdmv.py # MovieObject.bdmv navigation command parser
ig_stream.py # [Experimental] IG menu stream parser
analyze/
__init__.py # scan_disc() pipeline
signatures.py # Deduplication
clustering.py # Duration clustering
segment_graph.py # Segment reuse & Play All detection
classify.py # Segment & playlist labeling
ordering.py # Episode ordering (individual, Play All, chapter split)
explain.py # Human-readable reports
export/
json_out.py # JSON output
text_report.py # Text reports
m3u.py # M3U playlists
mkv_chapters.py # MKV remux with chapters (via mkvmerge)
tests/
fixtures/ # Bundled BDMV metadata for portable tests
pyproject.toml
- You have a decrypted BDMV backup (bdpl does not handle copy protection)
- The disc follows the BD-ROM spec (magic
MPLS/HDMV, big-endian, 45 kHz timestamps) - Episode detection is heuristic — results include confidence scores and warnings when uncertain
MIT
See CONTRIBUTING.md for development setup, validation steps, and fixture safety rules.