Files
matrix/Cargo.toml
Matrix Music Bot Dev 11b484d644 feat: 转码 + 投递(M4:transcoder + delivery/audio_upload)
新增模块:
  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)
2026-07-02 21:22:49 +08:00

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 里单独添加。