feat: 添加单曲信息获取和下载功能
- 新增 aria2 模块,实现下载功能- 新增 single 模块,实现单曲信息获取 - 新增 utils 模块,用于创建请求客户端
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
/target
|
||||||
|
/.idea
|
1959
Cargo.lock
generated
Normal file
1959
Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
11
Cargo.toml
Normal file
11
Cargo.toml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "netease-downloader"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
tokio = { version = "1.47.1", default-features = false, features = ["rt-multi-thread", "macros", "time"] }
|
||||||
|
reqwest = { version = "0.12.23", default-features = false, features = ["rustls-tls", "json"] }
|
||||||
|
serde = { version = "1.0.219", default-features = false, features = ["std", "derive"] }
|
||||||
|
serde_json = { version = "1.0.142", default-features = false, features = ["std"] }
|
||||||
|
aria2-ws = "0.5.1"
|
18
src/aria2.rs
Normal file
18
src/aria2.rs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
use crate::ErrorString;
|
||||||
|
use aria2_ws::{Client, TaskOptions};
|
||||||
|
|
||||||
|
pub async fn download(url: String, path: String) -> Result<(), ErrorString> {
|
||||||
|
let client = Client::connect("ws://127.0.0.1:16800/jsonrpc", Some("GenshinMinecraft"))
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("无法创建 Client: {e}"))?;
|
||||||
|
let options = TaskOptions {
|
||||||
|
out: Some(path),
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
client
|
||||||
|
.add_uri(vec![url], Some(options.clone()), None, None)
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("无法添加任务: {e}"))?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
12
src/main.rs
Normal file
12
src/main.rs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
mod aria2;
|
||||||
|
mod single;
|
||||||
|
pub mod utils;
|
||||||
|
|
||||||
|
type ErrorString = String;
|
||||||
|
|
||||||
|
#[tokio::main]
|
||||||
|
async fn main() {
|
||||||
|
let single_songs_info = single::single(448317056).await.unwrap();
|
||||||
|
println!("{:?}", single_songs_info);
|
||||||
|
aria2::download(single_songs_info.download_url, format!("{}.flac", single_songs_info.name)).await.unwrap();
|
||||||
|
}
|
53
src/single.rs
Normal file
53
src/single.rs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
use crate::ErrorString;
|
||||||
|
use crate::utils::create_reqwest_client;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use tokio::task::JoinHandle;
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct SingleSongsInfo {
|
||||||
|
pub id: u64,
|
||||||
|
pub name: String,
|
||||||
|
pub artist: String,
|
||||||
|
pub album: String,
|
||||||
|
pub pic_url: String,
|
||||||
|
pub download_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
struct DetailJson {
|
||||||
|
name: String,
|
||||||
|
ar_name: String,
|
||||||
|
al_name: String,
|
||||||
|
pic: String,
|
||||||
|
url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn single(id: u64) -> Result<SingleSongsInfo, ErrorString> {
|
||||||
|
let client = create_reqwest_client().await?;
|
||||||
|
|
||||||
|
let info_handle: JoinHandle<Result<DetailJson, ErrorString>> = tokio::spawn(async move {
|
||||||
|
let info_request_url =
|
||||||
|
format!("https://api.kxzjoker.cn/api/163_music?url={id}&level=jymaster&type=json");
|
||||||
|
client
|
||||||
|
.post(info_request_url)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("无法获取歌曲信息: {e}"))?
|
||||||
|
.json::<DetailJson>()
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("无法解析歌曲信息: {e}"))
|
||||||
|
});
|
||||||
|
|
||||||
|
let info_result = info_handle
|
||||||
|
.await
|
||||||
|
.map_err(|e| format!("歌曲信息获取失败: {e}"))??;
|
||||||
|
|
||||||
|
Ok(SingleSongsInfo {
|
||||||
|
id,
|
||||||
|
name: info_result.name,
|
||||||
|
artist: info_result.ar_name,
|
||||||
|
album: info_result.al_name,
|
||||||
|
pic_url: info_result.pic,
|
||||||
|
download_url: info_result.url,
|
||||||
|
})
|
||||||
|
}
|
16
src/utils.rs
Normal file
16
src/utils.rs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
use crate::ErrorString;
|
||||||
|
use reqwest::Client;
|
||||||
|
use std::time::Duration;
|
||||||
|
use tokio::sync::OnceCell;
|
||||||
|
|
||||||
|
static CLIENT: OnceCell<Client> = OnceCell::const_new();
|
||||||
|
pub async fn create_reqwest_client() -> Result<&'static Client, ErrorString> {
|
||||||
|
CLIENT
|
||||||
|
.get_or_try_init(|| async {
|
||||||
|
Client::builder()
|
||||||
|
.timeout(Duration::from_secs(5))
|
||||||
|
.user_agent("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/139.0.0.0 Safari/537.36")
|
||||||
|
.build()
|
||||||
|
})
|
||||||
|
.await.map_err(|e| format!("无法创建 Client: {e}"))
|
||||||
|
}
|
Reference in New Issue
Block a user