Files
llm-wiki/raw/articles/edcb-tools-2026.md
T
2026-06-30 22:22:00 +09:00

531 lines
16 KiB
Markdown

---
source_url: https://github.com/yutakobayashidev/edcb-tools
ingested: 2026-06-29
sha256: f9f2d326d774a922b3a96b590a9b49734bd847a60099d869a3fc3adf223164ed
discovered_from:
platform: discord
channel_id: '1028287639918497822'
channel_name: chat
message_id: '1521200608739332257'
author_id: '890908900520505354'
posted_at: 2026-06-29T17:08:07.664000000Z
message_excerpt: 'https://github.com/yutakobayashidev/edcb-tools'
score: 2
---
# edcb-tools
[![DeepWiki](badge)](https://deepwiki.com/yutakobayashidev/edcb-tools)
Rust client library, command line interface, and MCP server for EDCB/EpgTimer
CtrlCmd.
This crate currently provides a Tokio-based TCP client, binary codec, `edcb`
CLI, and `edcb-mcp` stdio MCP server for CtrlCmd APIs used by EDCB
integrations. The implementation is ported from `xtne6f/edcb.py`, with
KonomiTV's async usage used as a secondary reference.
## Distribution
Nix flake is the primary distribution surface. The default package builds both
first-class binaries:
- `edcb`
- `edcb-mcp`
Run the CLI directly from GitHub:
```sh
nix run github:yutakobayashidev/edcb-tools#edcb -- --host 127.0.0.1 services
```
Run the stdio MCP server directly from GitHub:
```sh
nix run github:yutakobayashidev/edcb-tools#edcb-mcp -- --host 127.0.0.1 --port 4510
```
Install both binaries into a Nix profile:
```sh
nix profile install github:yutakobayashidev/edcb-tools#edcb-tools
```
Use the package from another flake:
```nix
{
inputs.edcb-tools.url = "github:yutakobayashidev/edcb-tools";
outputs = { edcb-tools, ... }: {
# edcb-tools.packages.${system}.default
# edcb-tools.packages.${system}.edcb-tools
# edcb-tools.apps.${system}.edcb
# edcb-tools.apps.${system}.edcb-mcp
};
}
```
The Rust client library is intended to be consumed from this repository, not
published to crates.io:
```toml
[dependencies]
edcb-tools = { git = "https://github.com/yutakobayashidev/edcb-tools" }
```
## Supported in v1
- TCP transport
- `edcb` command line interface
- stdio MCP server surface
- EDCB primitive, string, vector, struct, and `SYSTEMTIME` codec
- Service, EPG, reserve, recorded-file, tuner, plugin, auto-add, manual-add,
and notify-status read APIs
- Program search, timetable retrieval, recorded item detail retrieval,
reservation detail retrieval, and event-based reservation
preview/create/update/delete with recording options
- Utility parsers for `ChSet5.txt`, `LogoData.ini`, logo directory indexes, and
program extended text
## Architecture
`EdcbClient` is a raw CtrlCmd client: its methods map closely to EDCB commands
and wire data structures. Application-level operations such as program search
and event-based reservation preview/create are exported from the crate root. The
CLI and MCP server call these operations instead of embedding CtrlCmd
orchestration directly. TCP transport is isolated behind an internal transport
boundary so additional transports can be added without rewriting command
encoding.
## To Do
- [ ] Unix domain socket transport
- [ ] Windows named pipe transport
- [ ] View app stream / SrvPipe stream helpers
- [ ] Recorded-file, auto-add, and manual-add mutation APIs
- [x] MCP server surface
- [ ] HTTP MCP transport
## Example
```rust
use std::time::Duration;
use edcb_tools::{ConnectionConfig, EdcbClient};
#[tokio::main]
async fn main() -> edcb_tools::Result<()> {
let client = EdcbClient::new(
ConnectionConfig::new("127.0.0.1", 4510).with_timeout(Duration::from_secs(5)),
);
let services = client.enum_service().await?;
for service in services {
println!("{}: {}", service.sid, service.service_name);
}
Ok(())
}
```
## Command Line Interface
Run the `edcb` CLI through the flake:
```sh
nix run .#edcb -- --host 127.0.0.1 --port 4510 services
```
During development, the same CLI can be run with Cargo:
```sh
cargo run --bin edcb -- --host 127.0.0.1 --port 4510 services
```
The same connection settings can be supplied through environment variables:
```sh
EDCB_HOST=127.0.0.1 EDCB_PORT=4510 EDCB_TIMEOUT_SECONDS=15 cargo run --bin edcb -- services
```
CLI options take precedence over environment variables. Defaults are
`127.0.0.1`, port `4510`, and a 15 second timeout.
Output is a stable line-based summary by default. Use `--json` for full
structured output.
Run `edcb --help`, `edcb help`, or `edcb help <command>` for clap-generated
usage, options, and examples from the current build.
Available commands:
- `services`
- `reserves`
- `recorded list`
- `recorded get <info-id>`
- `programs search [search options]`
- `programs timetable [timetable options]`
- `channels`
- `recording defaults`
- `recording presets`
- `reservation-conditions`
- `reservation-conditions get <condition-id>`
- `reservation-conditions create [search options] [recording options] --yes`
- `reservation-conditions update <condition-id> [search options] [recording options] --yes`
- `reservation-conditions delete <condition-id> --yes`
- `reserves get <reserve-id>`
- `reserves preview --event <onid:tsid:sid:eid> [recording options]`
- `reserves create --event <onid:tsid:sid:eid> [recording options] --yes`
- `reserves update <reserve-id> [recording options] --yes`
- `reserves delete <reserve-id> --yes`
- `tuner-reserves`
- `tuner-processes`
- `plugins <write|rec_name>`
- `notify-status`
`reserves preview` is a client-side preview that fetches the EDCB default
reservation settings and the target event, then builds the `ReserveData` that
would be sent. EDCB does not expose a reservation dry-run command. Use
`reserves create ... --yes` to send the actual add-reservation command. After
creation, the CLI fetches reservations again and returns the newly assigned
reservation ID when it can be resolved from the before/after difference.
`reserves update ... --yes` fetches the existing reservation, applies recording
option changes, sends the full updated reservation to EDCB, and returns the
updated reservation data.
`reserves delete ... --yes` first fetches the reservation by ID, then sends the
delete command and returns the deleted reservation data.
`programs search` prints event keys as `onid:tsid:sid:eid`, which can be passed
to `reserves preview` or `reserves create`.
Preview JSON has the same `ReserveData` shape that create/get return. The
previewed reservation has not been sent to EDCB yet. Abridged example:
```json
{
"reserve_id": 0,
"onid": 32736,
"tsid": 32736,
"sid": 1024,
"eid": 4208,
"start_time": "2026-06-29T22:00:00+09:00",
"duration_second": 1800,
"station_name": "Example Service",
"title": "Example Program",
"rec_setting": {
"rec_mode": 1,
"priority": 2
}
}
```
Useful reservation preview selectors:
```sh
edcb --json reserves preview --event 32736:32736:1024:4208 \
| jq '{event: "\(.onid):\(.tsid):\(.sid):\(.eid)", start_time, title, priority: .rec_setting.priority}'
```
Program search uses EDCB's `SearchKeyInfo`/`SearchPg` semantics. Date ranges are
recurring weekday/time-of-day ranges, not absolute datetimes. If no service is
specified, the CLI first fetches EDCB's service list and searches those services.
Use `programs timetable` when you want the program table for services/time
windows instead of keyword search.
`reservation-conditions` manages EDCB keyword auto reservations (`AutoAddData`)
with the same search options and recording options. EDCB does not return the
newly assigned AutoAdd ID from the add command, so create returns the condition
payload that was sent with `id` set to `0`; list or get conditions afterwards to
see assigned IDs.
Program search options:
- `--keyword <text>`
- `--exclude-keyword <text>`
- `--title-only`
- `--case-sensitive`
- `--regex`
- `--fuzzy`
- `--service <onid:tsid:sid>` (repeatable)
- `--genre <major:middle[:user_nibble]>` (repeatable)
- `--exclude-genre-ranges`
- `--date-range <start-dow:HH:MM-end-dow:HH:MM>` (repeatable, `0` is Sunday)
- `--exclude-date-ranges`
- `--duration-min <minutes>` and `--duration-max <minutes>`
- `--free-ca <all|free|paid>`
- `--search-enable` / `--search-disable`
- `--duplicate-title-check <none|same-channel|all-channels>`
- `--duplicate-title-check-days <days>`
Examples:
```sh
edcb programs search --keyword news --title-only
edcb programs search --keyword news --genre 0:1
edcb programs search --keyword news --date-range 1:19:00-1:23:00
edcb programs search --keyword news --duration-min 30 --duration-max 120 --free-ca free
edcb reservation-conditions create --keyword news --genre 0:1 --priority 4 --yes
edcb reservation-conditions update 77 --keyword news --duplicate-title-check same-channel --yes
```
Program timetable uses EDCB's `EnumPgInfoEx` semantics. It returns programs
grouped by service, nests short same-TS subchannels under their main channel,
and attaches reservation metadata when a matching reservation can be found.
JSON output includes `reservation_metadata_status`; if reservation lookup fails,
programs are still returned and the status contains the failure message.
Timetable options:
- `--service <onid:tsid:sid>` (repeatable)
- `--start-time <RFC3339 datetime>`
- `--end-time <RFC3339 datetime>`
- `--channel-type <gr|bs|cs|catv|sky|bs4k>`
Examples:
```sh
edcb programs timetable --channel-type gr
edcb programs timetable --service 32736:32736:1024 --start-time 2026-06-29T19:00:00+09:00 --end-time 2026-06-29T23:00:00+09:00
```
Timetable JSON nests program details under `channels[].programs[].event`.
Reservation metadata is optional per program; check
`reservation_metadata_status` before treating `reservation: null` as definitive.
Abridged example:
```json
{
"channels": [
{
"service": {
"onid": 32736,
"tsid": 32736,
"sid": 1024,
"service_name": "Example Service"
},
"programs": [
{
"event": {
"onid": 32736,
"tsid": 32736,
"sid": 1024,
"eid": 4208,
"start_time": "2026-06-29T22:00:00+09:00",
"short_info": {
"event_name": "Example Program"
}
},
"reservation": {
"id": 77,
"status": "Reserved",
"recording_availability": "Full"
}
}
],
"subchannels": null
}
],
"date_range": {
"earliest": "2026-06-29T19:00:00+09:00",
"latest": "2026-06-29T23:00:00+09:00"
},
"reservation_metadata_status": "Ok"
}
```
Useful timetable selectors:
```sh
edcb --json programs timetable --channel-type gr \
| jq -r '.channels[].programs[] | [.event.onid, .event.tsid, .event.sid, .event.eid, .event.start_time, .event.short_info.event_name, (.reservation != null)] | @tsv'
edcb --json programs timetable --channel-type gr \
| jq -r '.channels[].programs[] | select(.reservation == null) | "\(.event.onid):\(.event.tsid):\(.event.sid):\(.event.eid)\t\(.event.start_time)\t\(.event.short_info.event_name)"'
```
`channels` returns a DB-free KonomiTV-style channel snapshot built from
`ChSet5.txt` and `EnumService`. It includes `display_channel_id`, channel type,
service key, remocon ID, subchannel/radio flags, and watchability flags. Because
it is stateless, it does not preserve recorded-only historical channels, pinned
channels, jikkyo state, or viewer counts. Plain output is still one line per
channel; JSON output wraps the list in `channels` and includes
`epg_service_status` so callers can distinguish missing EPG metadata from an
empty channel list.
```sh
edcb channels
edcb --json channels
```
`recording defaults` decodes the EDCB default reservation settings returned by
`GetReserve2(0x7fffffff)`. `recording presets` reads `EpgTimerSrv.ini` through
`FileCopy2` and returns global defaults plus recording presets, including ID 0.
If EDCB returns an empty `EpgTimerSrv.ini`, use `recording defaults` for the
effective reservation default.
```sh
edcb recording defaults
edcb --json recording presets
```
Common recording options:
- `--priority <1-5>`
- `--enable` / `--disable`
- `--recording-mode <all|all-without-decoding|specified|specified-without-decoding|view>`
- `--start-margin <seconds>` and `--end-margin <seconds>`
- `--caption <default|enable|disable>` and `--data <default|enable|disable>`
- `--post-recording <default|nothing|standby|standby-and-reboot|suspend|suspend-and-reboot|shutdown>`
## MCP Server
Run the `edcb-mcp` stdio MCP server through the flake:
```sh
nix run .#edcb-mcp -- --host 127.0.0.1 --port 4510 --timeout-seconds 15
```
During development, the same server can be run with Cargo:
```sh
cargo run --bin edcb-mcp -- --host 127.0.0.1 --port 4510 --timeout-seconds 15
```
The same connection settings can be supplied through environment variables:
```sh
EDCB_HOST=127.0.0.1 EDCB_PORT=4510 EDCB_TIMEOUT_SECONDS=15 cargo run --bin edcb-mcp
```
CLI options take precedence over environment variables. Defaults are
`127.0.0.1`, port `4510`, and a 15 second timeout.
Run `edcb-mcp --help` for clap-generated server options from the current build.
Exposed MCP tools:
- `list_services`
- `list_reserves`
- `get_reservation`
- `list_recorded`
- `get_recorded_info`
- `list_channels`
- `get_recording_defaults`
- `get_recording_presets`
- `search_programs`
- `get_timetable`
- `list_reservation_conditions`
- `get_reservation_condition`
- `create_reservation_condition`
- `update_reservation_condition`
- `delete_reservation_condition`
- `preview_reservation`
- `create_reservation`
- `update_reservation`
- `delete_reservation`
- `list_tuner_reserves`
- `list_tuner_processes`
- `list_plugins`
- `get_notify_status`
`preview_reservation` does not mutate EDCB state. `create_reservation` creates
one reservation from an event key and the server's default reservation settings.
Both accept an optional `options` object using KonomiTV-style recording setting
names:
```json
{
"event": "32737:32737:1032:9285",
"options": {
"priority": 4,
"recording_start_margin": 60,
"recording_end_margin": 120
}
}
```
`update_reservation` accepts `reserve_id` and required `options`.
`delete_reservation` fetches the reservation before deleting it and returns the
deleted reservation data.
`search_programs` accepts KonomiTV-style search condition fields:
```json
{
"is_enabled": true,
"keyword": "news",
"exclude_keyword": "sports",
"is_title_only": true,
"is_case_sensitive": false,
"is_fuzzy_search_enabled": true,
"is_regex_search_enabled": false,
"service_ranges": [
{
"network_id": 32736,
"transport_stream_id": 32736,
"service_id": 1024
}
],
"genre_ranges": [
{
"major": 0,
"middle": 1,
"user_nibble": null
}
],
"is_exclude_genre_ranges": false,
"date_ranges": [
{
"start_day_of_week": 1,
"start_hour": 19,
"start_minute": 0,
"end_day_of_week": 1,
"end_hour": 23,
"end_minute": 0
}
],
"is_exclude_date_ranges": false,
"duration_range_min": 30,
"duration_range_max": 120,
"broadcast_type": "FreeOnly",
"duplicate_title_check_scope": "None",
"duplicate_title_check_period_days": 6
}
```
`create_reservation_condition` accepts a required `condition` object with the
same fields as `search_programs` and an optional `options` object with recording
settings. `update_reservation_condition` accepts `condition_id`, optional
`condition`, and optional `options`.
`get_timetable` accepts service/time/channel filters and returns channels with
programs, optional nested subchannels, and best-effort reservation metadata. The
response includes `reservation_metadata_status` so callers can distinguish "no
matching reservation" from "reservation lookup failed":
```json
{
"start_time": "2026-06-29T19:00:00+09:00",
"end_time": "2026-06-29T23:00:00+09:00",
"channel_type": "GR",
"services": [
{
"network_id": 32736,
"transport_stream_id": 32736,
"service_id": 1024
}
]
}
```
## Development
Use the Nix dev shell through direnv, then run:
```sh
nix fmt
nix build
nix run .#edcb -- --version
cargo test
cargo fmt --check
cargo clippy --all-targets -- -D warnings
```