pub mod album;
pub mod api;
pub mod artist;
pub mod lyric;
pub mod playlist;
#[cfg(feature = "qimei")]
pub mod qimei;
pub mod resolve;
pub mod search;
pub mod song;
use anyhow::Result;
use serde_json::Value;
use tokio::sync::Mutex;
use tracing::debug;
use crate::config::Config;
pub const TANG_CONCURRENCY: usize = 2;
pub struct Session {
pub client: reqwest::Client,
pub config: Config,
qimei36: Mutex<Option<String>>,
pub tang_permits: tokio::sync::Semaphore,
pub tang_exhausted: std::sync::atomic::AtomicBool,
}
impl Session {
pub fn new(client: reqwest::Client, config: Config) -> Self {
Self {
client,
config,
qimei36: Mutex::new(None),
tang_permits: tokio::sync::Semaphore::new(TANG_CONCURRENCY),
tang_exhausted: std::sync::atomic::AtomicBool::new(false),
}
}
pub async fn qimei36(&self) -> String {
let mut guard = self.qimei36.lock().await;
if let Some(q) = guard.as_ref() {
return q.clone();
}
let q = self.obtain_qimei().await;
*guard = Some(q.clone());
q
}
pub async fn refresh_qimei(&self) {
let q = self.obtain_qimei().await;
*self.qimei36.lock().await = Some(q);
}
#[cfg(feature = "qimei")]
async fn obtain_qimei(&self) -> String {
match qimei::obtain(&self.client).await {
Ok(q) => {
debug!(q36 = %q, "取得 QIMEI36");
q
}
Err(e) => {
debug!("qimei 请求失败,使用随机 QIMEI36: {e:#}");
api::random_qimei36()
}
}
}
#[cfg(not(feature = "qimei"))]
async fn obtain_qimei(&self) -> String {
debug!("未启用 qimei feature,使用随机 QIMEI36");
api::random_qimei36()
}
pub async fn call(
&self,
qimei36: &str,
ct: &str,
module: &str,
method: &str,
param: Value,
) -> Result<Value> {
api::call(&self.client, qimei36, ct, module, method, param).await
}
}
const QQ_HOSTS: &[&str] = &[
"y.qq.com",
"i.y.qq.com",
"m.y.qq.com",
"c.y.qq.com",
"c6.y.qq.com",
"music.qq.com",
];
pub fn is_qq_host(url: &str) -> bool {
crate::net::host_in(url, QQ_HOSTS)
}