use std::sync::atomic::{AtomicBool, Ordering};
use async_trait::async_trait;
use super::Searchers;
use super::search_result::SearchResult;
use super::searcher::Searcher;
use crate::error::SearchError;
use crate::providers::web::netease::api;
use crate::providers::web::netease::response::SearchResponse;
static USE_NEW_SEARCH_FIRST: AtomicBool = AtomicBool::new(false);
const RISK_CONTROL_CODE: i64 = -460;
pub struct NeteaseSearcher;
#[async_trait]
impl Searcher for NeteaseSearcher {
fn name(&self) -> &str {
"Netease"
}
fn display_name(&self) -> &str {
"Netease Cloud Music"
}
fn searcher_type(&self) -> Searchers {
Searchers::Netease
}
async fn search_for_results_str(
&self,
search_string: &str,
) -> Result<Vec<SearchResult>, SearchError> {
let response = search_response(search_string).await?;
let Some(songs) = response.result.and_then(|result| result.songs) else {
return Ok(Vec::new());
};
let search_results: Vec<SearchResult> = songs
.into_iter()
.map(|song| {
SearchResult::new(
Searchers::Netease,
song.name,
song.artists.into_iter().map(|artist| artist.name).collect(),
song.album.name,
Some(song.duration as i32),
song.id.to_string(),
)
})
.collect();
Ok(search_results)
}
}
async fn search_response(keyword: &str) -> Result<SearchResponse, SearchError> {
let prefer_new = USE_NEW_SEARCH_FIRST.load(Ordering::Relaxed);
let first_error = match search_once(keyword, prefer_new).await {
Ok(response) => return Ok(response),
Err(error) => error,
};
USE_NEW_SEARCH_FIRST.store(!prefer_new, Ordering::Relaxed);
match search_once(keyword, !prefer_new).await {
Ok(response) => Ok(response),
Err(_) => {
USE_NEW_SEARCH_FIRST.store(prefer_new, Ordering::Relaxed);
Err(first_error)
}
}
}
async fn search_once(keyword: &str, use_new: bool) -> Result<SearchResponse, SearchError> {
if use_new {
api::search_new(keyword).await
} else {
search_web(keyword).await
}
}
async fn search_web(keyword: &str) -> Result<SearchResponse, SearchError> {
let response = api::search(keyword).await?;
if response.code == Some(RISK_CONTROL_CODE) {
return Err(SearchError::Api(format!(
"网易云 web 搜索接口返回 {RISK_CONTROL_CODE}(网络环境被风控)"
)));
}
Ok(response)
}