English | 日本語
bdav is a Linux CLI that takes MPEG2-TS recordings (.ts / .m2t / .m2ts)
— e.g. captured by a PT3 tuner card — and produces a BDAV file tree
(CLIPINF/, PLAYLIST/, info.bdav, STREAM/) that consumer Blu-ray
recorders / players recognize and play back.
- No wine, no third-party binaries. Native implementation of the open BD-RE Part 3 (BDAV) format.
- Automatic subtitle resolution from Syobocal (しょぼいカレンダー), the de-facto Japanese anime episode database, with 3 selectable title formats.
- Parses foltia ANIME LOCKER filenames
(
{TID}-{EpNo}-...) to derive title ID and episode number automatically. - Per-directory or per-file input.
- Two-stage flow (preview → confirm → build) for safety; one-shot mode available for CI / automation.
- foltia ANIME LOCKER integration can create chapters automatically from CM-cut metadata.
For verified broadcast configurations and audio layouts, see
docs/bdav-native-format.md.
License: MIT (LICENSE). See NOTICE.md for treatment of
third-party tools, standards documents, and trademarks.
Zero third-party dependencies (Python standard library only), so any of the
following works. The uv route is the easiest — you don't even need
Python 3.11+ pre-installed; uv will fetch what it needs.
# One-time install of uv itself
curl -LsSf https://astral.sh/uv/install.sh | sh
# Install bdav as a global CLI
uv tool install --from git+https://github.com/sorshi/bdav bdav
bdav --versionUpgrade with uv tool upgrade bdav, remove with uv tool uninstall bdav.
The system Python is never touched.
# Debian/Ubuntu: sudo apt install pipx
# RHEL family: sudo dnf install pipx (or pip install --user pipx)
# macOS: brew install pipx
pipx install git+https://github.com/sorshi/bdav
bdav --versiongit clone https://github.com/sorshi/bdav.git
cd bdav
python3 -m venv .venv
source .venv/bin/activate
pip install -e .
bdav --versionPlace the api directory from inside the foltia-api-server directory under
foltia ANIME LOCKER's /home/foltia/php/.
Do not contact the foltia ANIME LOCKER vendor about this integration.
Only proceed if you understand and accept that you are solely responsible for
any outcome regarding foltia ANIME LOCKER.
This project runs on glibc 2.17+ via uv and python-build-standalone,
so the supported range is very broad.
Development and playback verification were done on Ubuntu Server 24.04 LTS
(x64), with discs burned on Windows 11 using ImgBurn and playback confirmed
on a PS3.
| OS | Minimum version | Notes |
|---|---|---|
| Debian | 10 (buster, 2019) or later | glibc 2.28+. Debian 12 (bookworm) / 13 (trixie) recommended |
| Ubuntu | 18.04 LTS (bionic) or later | glibc 2.27+. 22.04 / 24.04 LTS recommended |
| RHEL / AlmaLinux / Rocky | 8+ (practical); 7 works for uv itself | RHEL 8: glibc 2.28; RHEL 9/10 recommended |
| macOS | 12 Monterey (Intel) / 11 Big Sur (Apple Silicon) or later | |
| Windows | WSL2 only (assumed) | Native Windows unverified |
Route (C) (pip + venv) requires system Python 3.11+; in that case:
- Debian: 12 (bookworm, Python 3.11) or later; 11 and earlier need backports or pyenv
- Ubuntu: 24.04 LTS (Python 3.12) recommended; 22.04 ships
python3.11in universe - RHEL / AlmaLinux: 9 (Python 3.11 in AppStream) or later; on 8 use
dnf module install python3.11 - macOS: Homebrew
python@3.11or later
bdav author \
--input-dir /mnt/foltia/7749 \
--output /work/disc1 \
--channel-name BS11 --channel-number 211This scans each input file, derives TID / episode number from the foltia-style filename, fetches the program title + subtitle from Syobocal, formats them, prints a preview, then writes the BDAV tree.
bdav author --input-dir /mnt/foltia/7749 --dry-run# 1) Generate plan JSON + preview
bdav plan \
--input-dir /mnt/foltia/7749 \
--plan /work/7749.plan.json
# 2) Optionally hand-edit /work/7749.plan.json
# (e.g. fill in subtitles for episodes Syobocal didn't have)
# 3) Build
bdav build \
--plan /work/7749.plan.json \
--output /work/disc1Plan JSON format is documented in docs/workflow.md.
Selected with --subtitle-format (default: default).
| ID | Example output | Use case |
|---|---|---|
default |
#1 はじまりの章 |
Episode number + subtitle (default) |
subtitle-only |
はじまりの章 |
Subtitle text only |
title-episode-subtitle |
アニメA #1 はじまりの章 |
Includes program title |
bdav author --input-dir /mnt/foltia/7749 --output /work/disc1 \
--subtitle-format title-episode-subtitleCombine with --episode-pad 2 to zero-pad to #01 (default 0 = no padding).
Details: docs/subtitle-formats.md.
# Directory: all .ts/.m2ts/.m2t/.mts inside
bdav author --input-dir /mnt/foltia/7749 --output /work/disc1
# Glob pattern: quote it so the shell doesn't expand it
bdav author --input-dir '/mnt/foltia/7749-*.m2t' --output /work/disc1--input-dir accepts a directory, a glob pattern (* ? [ are detected),
or a single file path. In all cases TID / episode number are extracted from
the foltia filename and items are sorted by episode (so #1, #2, ... #10, #11
come out in numeric order, not alphabetical).
bdav author --output /work/disc1 \
--input /rec/00001.m2ts --title "#1 はじまりの章" \
--channel-name "AT-X" --channel-number 333 \
--input /rec/00002.m2ts --title "#2 出会いの章" \
--channel-name "BS11" --channel-number 211--title / --channel-name / --channel-number pair positionally
with --input (the i-th value goes with the i-th input).
--title, if given, is used verbatim asformatted_title— Syobocal API lookup and--subtitle-formatreformatting are both skipped (so you don't get a doubled#Nprefix).--channel-name/--channel-numberare useful when burning a mixed disc with different recording stations per program. The BDAV spec stores the recording channel per-playlist (.rpls), so consumer BD recorders display the correct channel name per program on the disc menu.- If you give only one
--channel-name(or--channel-number), it becomes the disc-wide default and applies to all items. - Specifying only one side (name or number) lets the other be
auto-filled from the bundled channel definition (e.g.
--channel-name "AT-X"→ number 333 is filled in). - If you specify neither,
bdavreads the input TS's SDT/PAT and resolves the station from the(ONID, TSID, SID)triplet via channels.toml, so even files that don't follow foltia naming get correct station info. Disable with--no-ts-probe. Inspect withbdav probe FILE. Regional terrestrial differences and additional stations can be overridden in~/.config/bdav/channels.toml. Details:docs/channels.md.
bdav author --output /work/disc1 --list /work/titles.tsvTSV columns: path<TAB>title<TAB>rec_time<TAB>ch_name<TAB>ch_num
(columns after the first are optional).
| Input | Handling |
|---|---|
.m2ts (192-byte TS) |
Placed in STREAM/ as-is (aligned to 6144-byte boundary) |
.ts / .m2t (188-byte TS) |
Arrival times interpolated from PCR, then wrapped to 192-byte M2TS |
No ffmpeg / tsMuxeR re-multiplexing is performed. PSI / PID /
stream_type are preserved verbatim, so the original broadcast audio
configuration (stereo / 5.1 surround / dual-mono bilingual) is correctly
reflected in the BDAV metadata.
| Option | Description |
|---|---|
--input-dir PATH |
Directory / glob pattern ('/path/7784*.m2t') / single file (foltia naming recommended) |
--input PATH (repeatable) |
Per-file input |
--title TITLE (repeatable) |
Title paired positionally with --input |
--list FILE |
TSV / JSON list file |
--output DIR |
Output BDAV directory |
--subtitle-format ID |
default / subtitle-only / title-episode-subtitle |
--episode-pad N |
Zero-pad episode number (default 0) |
--channel-name NAME (repeatable) |
Station name (e.g. BS11). Same count as --input → per-item; 1 value → disc-wide default. Number auto-filled from map if omitted |
--channel-number N (repeatable) |
Station number (e.g. BS11=211, AT-X=333). Same rule; name auto-filled from map if omitted |
--channel-map FILE (repeatable) |
Additional TOML overrides for the channel map (docs/channels.md) |
--no-ts-probe |
Disable SDT/PAT auto-resolution from the input TS |
--chapter-interval SEC |
Periodic chapter interval in seconds (default 300, 0 disables) |
--chapters CSV (repeatable) |
Explicit per-clip chapter list (seconds, comma-separated; 1:23.4 form accepted). Pairs with --input |
--chapters-file FILE (repeatable) |
Same but read from a text file (one timestamp per line, # for comments) |
--no-marks |
Don't write PlayListMark |
--copy-mode {copy,move,link} |
How to place files into STREAM/ (default copy). move consumes the input file; use copy or link for originals you want to preserve |
--no-backup |
Skip the BACKUP/ mirror |
--no-api |
Don't call the Syobocal API |
--api-cache-dir DIR |
API response cache directory |
--overwrite |
Overwrite existing output |
--dry-run (author) |
Show preview and exit without building |
--yes, -y (build / author) |
Don't prompt even if items are flagged review=true |
--report PATH |
Write a build report JSON |
-q, --quiet |
Suppress all non-error output (also implies --yes). For shell scripts. |
-v, --verbose |
Print debug details (e.g. source paths, packet counts) |
--foltia-url URL |
foltia host URL (e.g. http://foltia.local; /api/v1/ is auto-appended when missing). Imports CM-cut chapters per file |
--foltia-user USER, --foltia-pass PASS |
Basic Auth credentials (env: FOLTIA_USER, FOLTIA_PASS) |
--foltia-fields LIST |
foltia values that should force-override local values: chapters,subtitle,channel,all. Default (empty): foltia acts as a fallback — fills only fields the local sources couldn't resolve (e.g. an unknown regional terrestrial station name) |
Run bdav <subcommand> --help for the full list.
bdav plan / bdav author also estimates total disc usage and reports
which Blu-ray media type would fit:
Total disc usage estimate: 22.47 GB across 11 clip(s)
[OK] BD-R SL Single layer (25 GB) 89.8% full (25.03 GB capacity)
[OK] BD-R DL Dual layer (50 GB) 44.9% full (50.05 GB capacity)
[OK] BD-R XL TL Triple layer (100 GB) 22.4% full (100.10 GB capacity)
[OK] BD-R XL QL Quad layer (128 GB) 17.6% full (128.00 GB capacity)
→ Recommended media: BD-R SL
.ts / .m2t inputs are sized as their post-wrap (* 192/188) byte count.
UDF filesystem overhead (~10 MB) and BACKUP/ mirror size (~tens of KB)
are not counted; they're negligible compared to the stream data.
If the total exceeds all listed media types, a warning suggests splitting
across multiple discs.
At default verbosity, bdav build (and the build phase of bdav author)
prints a per-clip progress line, useful because each clip takes seconds-to-minutes
to process:
Building 11 clip(s) -> /work/disc1/BDAV
[ 1/11] 00001 7784-1-20260414-2300-333.m2t (1521 MB)... 12.3s → BS11 #1 まさかの上京 (1448s, 4827 EP)
[ 2/11] 00002 7784-2-20260421-2300-333.m2t (1518 MB)... 12.1s → BS11 #2 ヘッジホッグ閉店 (1445s, 4820 EP)
...
BDAV written: /work/disc1/BDAV
-q suppresses everything for use in scripts; -v adds debug lines under each clip.
/work/disc1/BDAV/
├── STREAM/
│ ├── 00001.m2ts # input placed (copy/link, or explicit move) and aligned to 6144B
│ └── 00002.m2ts
├── CLIPINF/
│ ├── 00001.clpi
│ └── 00002.clpi
├── PLAYLIST/
│ ├── 00001.rpls
│ └── 00002.rpls
├── info.bdav
└── BACKUP/
├── CLIPINF/...
├── PLAYLIST/...
└── info.bdav
docs/workflow.md— Plan → build flow and plan JSON schemadocs/subtitle-formats.md— Behavior and fallbacks of the 3 title formatsdocs/foltia-integration.md— foltia filenames + Syobocal integrationdocs/channels.md— Channel definition file (editing / regional differences)docs/bdav-native-format.md— BDAV output specification (implementation basis)
Wraps 188-byte TS into 192-byte M2TS (a safe transform — no re-multiplexing).
bdav author will auto-wrap on the fly, so this is only needed if you want
to pre-convert.
python3 scripts/ts_to_m2ts_packetwrap.py \
--source-glob "/recordings/*.ts" --out-dir /work/m2tsField-by-field dumper for *.clpi / *.rpls / info.bdav. Pass two files
plus --diff for byte-level comparison.
PYTHONPATH=src python3 tools/bdav_dump.py /work/disc1/BDAV/CLIPINF/00001.clpiDiff two BDAV directories (byte/field counts for CLIPINF / PLAYLIST / info.bdav).
Generates a synthetic M2TS test corpus via ffmpeg (development use).
error: no inputs (use --input-dir / --input / --list)— No input specified.- Syobocal API is unresponsive — pass
--no-apito skip; on failure the tool falls back to filename-derived data and flagsreview=true. Edit the plan JSON by hand, thenbdav build. output not empty: ... (use --overwrite)— Add--overwrite.- Generated BDAV not recognized by a real player — If the input
.m2tswas re-multiplexed by tsMuxeR / ffmpeg, thepmt_pid/pcr_pid/stream_typemay have changed. Pre-convert withscripts/ts_to_m2ts_packetwrap.py(188 → 192 packet wrapping) instead.
- During development we referenced many software projects, including chotBDAV. Our sincere thanks.
- This project was developed by Sorshi(宗子) of DCC-JPL Japan.
- This project's source code is MIT-licensed (
LICENSE). - It is an independent implementation of BD-RE Part 3 (BDAV) and bundles no third-party binaries or code; nor does it depend on any at runtime.
- For the treatment of standards documents, trademarks, and third-party
tools, see
NOTICE.md.