11b484d644
新增模块:
src/transcoder.rs ── ffmpeg 子进程封装(MP3/AAC → stereo Opus 128k)
src/delivery/mod.rs ── DeliveryStrategy trait + DeliveryKind + DeliveryReceipt
src/delivery/audio_upload.rs ── Pattern A 实现(m.audio via Timeline::send)
src/delivery/url_link.rs ── Pattern B stub(v2 实现)
依赖新增:mime 0.3.17
transcoder.rs 关键设计:
- 输入已是 native Opus/WebM → 直接 copy(保留质量 + 节省 CPU)
- 其他格式 → ffmpeg 子进程转码
- ffmpeg 参数(与 plan 一致):
-c:a libopus -b:a 128k -ac 2 -ar 48000 -vbr on
-f webm -application audio -map_metadata 0
- 用 tokio::process::Command + tokio::time::timeout
- 提供 cache_path_for() 生成 <cache>/opus/<source>-<id>.webm
delivery/audio_upload.rs 关键设计:
- 读取本地 Opus 文件 → client.media().upload(...)
- 检查 max_upload_bytes(默认 50MB,留余量)
- 构造 AudioInfo + AudioMessageEventContent(用 MediaSource::Plain(mxc_uri))
- 经 Timeline::send 投递(SDK 内部 Megolm 加密)
- DeliveryReceipt.event_id 暂为 None(需要 timeline subscription 跟踪;M5 queue 实现)
遇到的 API 坑(已解决):
- AudioInfo 是 non_exhaustive 结构体 + 字段全 public,
需用 AudioInfo::new() + 字段赋值(不能用 struct expression)
- AudioInfo.duration 是 Option<std::time::Duration>(不是 UInt 秒数)
- MediaSource 在 matrix_sdk::ruma::events::room::MediaSource(非 message 子模块)
- AudioMessageEventContent::new(body, source) 只 2 参,info 用 .info(Some(Box::new(...))) 设置
- SendHandle 的 transaction_id 是私有的;v1 阶段用不到,Receipt.event_id 留 None
测试:cargo test → 21 passed + 4 ignored(需要 ffmpeg / yt-dlp + 网络)
新增测试:
- transcoder::cache_path_for_sanitizes
- transcoder::audio_format_decision
- transcoder::real_transcode_mp3_to_opus(ignored)
- transcoder::check_ffmpeg_works(ignored)
总测试:21 passed + 4 ignored
E2EE 不变量保持:
✅ 所有出站消息经 Timeline::send(delivery/audio_upload 强制)
✅ m.audio 消息体(MediaSource::Plain mxc_uri)经 Megolm 加密
后续 M5:!req 端到端(队列 + 下载 + 接入 event_loop)
33 lines
952 B
TOML
33 lines
952 B
TOML
[package]
|
|
name = "matrix-music-bot"
|
|
version = "0.1.0"
|
|
edition = "2024"
|
|
|
|
[dependencies]
|
|
aes-gcm = "0.11.0"
|
|
anyhow = "1.0.103"
|
|
argon2 = "0.5.3"
|
|
async-trait = "0.1.89"
|
|
base64 = "0.22.1"
|
|
clap = { version = "4.6.1", features = ["derive", "env"] }
|
|
futures-util = "0.3.32"
|
|
getrandom = { version = "0.3", features = ["std"] }
|
|
matrix-sdk = "0.18.0"
|
|
matrix-sdk-crypto = "0.18.0"
|
|
matrix-sdk-ui = "0.18.0"
|
|
mime = "0.3.17"
|
|
serde = { version = "1.0.228", features = ["derive"] }
|
|
serde_json = "1.0.150"
|
|
thiserror = "2.0.18"
|
|
tokio = { version = "1.52.3", features = ["full"] }
|
|
toml = "1.1.2"
|
|
tracing = "0.1.44"
|
|
tracing-subscriber = { version = "0.3.23", features = ["env-filter"] }
|
|
zeroize = "1.9.0"
|
|
|
|
# 推迟到对应 milestone 添加(避免 libsqlite3-sys 与 matrix-sdk-sqlite 冲突):
|
|
# - rusqlite → M5 (SqliteQueue)
|
|
# - reqwest → M3 (yt-dlp HTTP) / M5 (音频下载)
|
|
#
|
|
# 这些依赖会在对应 milestone 的 commit 里单独添加。
|