-
Notifications
You must be signed in to change notification settings - Fork 1
Search
ChouChiu edited this page Sep 23, 2026
·
3 revisions
搜索模块提供跨平台的歌曲检索与歌词获取能力,通过 search feature 控制(默认开启)。
如需纯离线构建,可在依赖中禁用该特性以避免引入 reqwest、tokio 等网络依赖:
[dependencies]
lyrics-helper = { version = "0.5.0", default-features = false }| 平台 | Searcher | 特点与要求 |
|---|---|---|
| 网易云音乐 | NeteaseSearcher |
先尝试 Web 接口,海外 IP 或被风控(-460)时自动改走 eapi 接口并持久记录该状态 |
| QQ 音乐 | QQMusicSearcher |
支持公共曲库搜索;歌词接口下发十六进制加密 QRC,自动解密 |
| 酷狗音乐 | KugouSearcher |
支持候选列表搜索;歌词接口下发 Base64 编码的 LRC 文本,自动解码 |
| 汽水音乐 | SodaMusicSearcher |
字节跳动旗下汽水音乐公共检索接口 |
| Musixmatch | MusixmatchSearcher |
国际歌词源;支持 Android(默认)和桌面版 API(可通过 ApiOptions 配置) |
| LRCLIB | LRCLIBSearcher |
开放歌词数据库,无凭据要求,提供逐行同步 LRC 与纯文本歌词 |
| AMLL TTML DB | AmllTtmlDbSearcher |
社区逐词 TTML 静态库;无远程搜索接口,下载元数据索引后在本地多字段匹配 |
| Apple Music | AppleMusicSearcher |
需要传入开发者 Bearer Token;仅支持曲目元数据搜索,不提供歌词 |
| Spotify | SpotifySearcher |
需要传入 Web API 的 Access Token(Bearer);仅支持曲目搜索 |
Note
网易云音乐、QQ 音乐等国内流媒体平台返回的搜索结果高度依赖网络出口 IP 的地区版权状态(例如海外 IP 通常只能检索到翻唱或无法播放的条目)。在测试或编写自动化逻辑时,避免对具体歌曲的检索结果顺序做严格断言。
使用 search_for_best_result() 检索并在内部计算打分,返回匹配度最高的一条结果:
use lyrics_helper::models::TrackMetadata;
use lyrics_helper::searchers::netease::NeteaseSearcher;
use lyrics_helper::searchers::search_for_best_result;
#[tokio::main]
async fn main() {
let mut track = TrackMetadata::new();
track.title = Some("晴天".to_string());
track.artist = Some("周杰伦".to_string());
track.album = Some("叶惠美".to_string());
track.duration_ms = Some(269000);
track.ensure_artists();
match search_for_best_result(&NeteaseSearcher, &track).await {
Ok(Some(result)) => {
println!("匹配歌曲: {} - {}", result.title, result.artist());
println!("匹配等级: {:?}", result.match_type);
println!("平台 ID: {}", result.id);
}
Ok(None) => println!("未找到匹配曲目"),
Err(err) => eprintln!("搜索请求失败: {err}"),
}
}错误约定:
-
Ok(None):请求成功,但没有匹配的歌曲。 -
Err(SearchError):请求失败(网络错误、平台限流、命中验证码或接口异常)。详见搜索类型与高级函数。
所有平台搜索器均实现 Searcher trait:
use lyrics_helper::models::TrackMetadata;
use lyrics_helper::searchers::searcher::Searcher;
use lyrics_helper::searchers::*;
#[tokio::main]
async fn main() {
let mut track = TrackMetadata::new();
track.title = Some("Bang Bang".to_string());
track.artist = Some("Jessie J, Ariana Grande, Nicki Minaj".to_string());
track.duration_ms = Some(199000);
track.ensure_artists();
let platforms: [(&str, &dyn Searcher); 4] = [
("网易云", &netease::NeteaseSearcher),
("QQ 音乐", &qq_music::QQMusicSearcher),
("酷狗", &kugou::KugouSearcher),
("LRCLIB", &lrclib::LRCLIBSearcher),
];
for (name, searcher) in platforms {
match search_for_best_result(searcher, &track).await {
Ok(Some(best)) => println!("{name}: {} ({:?})", best.title, best.match_type),
Ok(None) => println!("{name}: 无结果"),
Err(err) => eprintln!("{name}: 错误 - {err}"),
}
}
}通过搜索得到 SearchResult 后,使用各平台的 providers 客户端获取歌词文本:
use lyrics_helper::models::TrackMetadata;
use lyrics_helper::providers::web::netease;
use lyrics_helper::searchers::netease::NeteaseSearcher;
use lyrics_helper::searchers::search_for_best_result;
#[tokio::main]
async fn main() {
let mut track = TrackMetadata::new();
track.title = Some("晴天".to_string());
track.artist = Some("周杰伦".to_string());
track.ensure_artists();
if let Ok(Some(result)) = search_for_best_result(&NeteaseSearcher, &track).await {
let song_id: i64 = result.id.parse().unwrap_or(0);
match netease::api::get_lyrics(song_id).await {
Ok((Some(lrc), translation)) => {
println!("原文歌词:\n{lrc}");
if let Some(trans) = translation {
println!("翻译歌词:\n{trans}");
}
}
Ok((None, _)) => println!("该歌曲无可用歌词"),
Err(err) => eprintln!("获取歌词失败: {err}"),
}
}
}AMLL TTML DB 是一个由社区维护的逐词 TTML 歌词库。其 provider 位于 lyrics_helper::providers::web::amll_ttml_db::api:
| 函数 | 说明 |
|---|---|
search(keyword) |
在元数据索引中进行多关键字模糊匹配,返回匹配条目列表(从新到旧) |
get_raw_lyrics(file_name) |
根据文件名从仓库中拉取原始 TTML 文件文本 |
get_lyrics(platform, id, format) |
根据网易云/QQ 音乐/Apple Music/Spotify 的平台歌曲 ID 获取对应格式歌词(支持 TTML、LRC、YRC、QRC、Lyricify Syllable 等) |
set_base_url(url) |
切换下载镜像站点(如 BIKONOO_BASE_URL 或 GBCLSTUDIO_BASE_URL),用于改善国内网络访问 |
使用示例:
use lyrics_helper::providers::web::amll_ttml_db::api::{self, Format, Platform};
use lyrics_helper::{parse, LyricsRawTypes};
#[tokio::main]
async fn main() {
let entries = api::search("晴天 周杰伦").await.expect("搜索索引失败");
let Some(entry) = entries.first() else {
println!("未找到条目");
return;
};
println!("命中条目: {} - {}", entry.title(), entry.artists.join(", "));
// 1. 获取原始逐词 TTML
if let Ok(Some(ttml)) = api::get_raw_lyrics(&entry.raw_lyric_file).await {
if let Some(data) = parse(&ttml, LyricsRawTypes::Ttml) {
println!("TTML 解析成功,共 {} 行", data.lines.as_ref().map_or(0, Vec::len));
}
}
// 2. 若条目记录了对应网易云 ID,可直接取转换为 LRC 的文本
if let Some(ncm_id) = entry.ncm_music_ids.first() {
if let Ok(Some(lrc)) = api::get_lyrics(Platform::Netease, ncm_id, Format::Lrc).await {
println!("LRC 获取成功,长度 {} 字节", lrc.len());
}
}
}