uploaders
This commit is contained in:
237
src/api.rs
237
src/api.rs
@@ -1,7 +1,9 @@
|
||||
use crate::providers::{
|
||||
ALL_PROVIDERS, DynProvider, build_status_response, panic_payload_to_string,
|
||||
report_provider_error, resolve_provider_for_build, run_provider_guarded,
|
||||
run_uploader_provider_guarded,
|
||||
};
|
||||
use crate::uploaders::{UploaderProfile, UploadersRequest};
|
||||
use crate::util::cache::VideoCache;
|
||||
use crate::util::discord::send_discord_error_report;
|
||||
use crate::util::proxy::{Proxy, all_proxies_snapshot};
|
||||
@@ -141,10 +143,61 @@ pub fn config(cfg: &mut web::ServiceConfig) {
|
||||
// .route(web::get().to(videos_get))
|
||||
.route(web::post().to(videos_post)),
|
||||
)
|
||||
.service(web::resource("/uploaders").route(web::post().to(uploaders_post)))
|
||||
.service(web::resource("/test").route(web::get().to(test)))
|
||||
.service(web::resource("/proxies").route(web::get().to(proxies)));
|
||||
}
|
||||
|
||||
fn uploader_request_is_valid(request: &UploadersRequest) -> bool {
|
||||
request.uploaderId.is_some() || request.uploaderName.is_some()
|
||||
}
|
||||
|
||||
fn provider_hint_from_uploader_id(uploader_id: &str) -> Option<String> {
|
||||
let (channel, _) = uploader_id.split_once(':')?;
|
||||
Some(resolve_provider_for_build(channel).to_string())
|
||||
}
|
||||
|
||||
fn uploader_provider_ids() -> Vec<String> {
|
||||
let mut ids = ALL_PROVIDERS
|
||||
.iter()
|
||||
.filter_map(|(provider_id, _)| (*provider_id != "all").then(|| (*provider_id).to_string()))
|
||||
.collect::<Vec<_>>();
|
||||
ids.sort();
|
||||
ids
|
||||
}
|
||||
|
||||
fn uploader_match_sort_key(profile: &UploaderProfile) -> (u64, String, String) {
|
||||
(
|
||||
profile.videoCount,
|
||||
profile.channel.clone().unwrap_or_default(),
|
||||
profile.id.clone(),
|
||||
)
|
||||
}
|
||||
|
||||
async fn lookup_uploader_with_provider(
|
||||
provider_id: &str,
|
||||
provider: DynProvider,
|
||||
cache: VideoCache,
|
||||
pool: DbPool,
|
||||
request: &UploadersRequest,
|
||||
options: crate::videos::ServerOptions,
|
||||
) -> Result<Option<UploaderProfile>, String> {
|
||||
run_uploader_provider_guarded(
|
||||
provider_id,
|
||||
"uploaders_post.get_uploader",
|
||||
provider.get_uploader(
|
||||
cache,
|
||||
pool,
|
||||
request.uploaderId.clone(),
|
||||
request.uploaderName.clone(),
|
||||
request.query.clone(),
|
||||
request.profileContent,
|
||||
options,
|
||||
),
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
async fn status(req: HttpRequest) -> Result<impl web::Responder, web::Error> {
|
||||
#[cfg(feature = "debug")]
|
||||
let trace_id = crate::util::flow_debug::next_trace_id("status");
|
||||
@@ -491,6 +544,144 @@ async fn videos_post(
|
||||
Ok(web::HttpResponse::Ok().json(&videos))
|
||||
}
|
||||
|
||||
async fn uploaders_post(
|
||||
uploader_request: web::types::Json<UploadersRequest>,
|
||||
cache: web::types::State<VideoCache>,
|
||||
pool: web::types::State<DbPool>,
|
||||
requester: web::types::State<Requester>,
|
||||
req: HttpRequest,
|
||||
) -> Result<impl web::Responder, web::Error> {
|
||||
let trace_id = crate::util::flow_debug::next_trace_id("uploaders");
|
||||
let request = uploader_request.into_inner().normalized();
|
||||
if !uploader_request_is_valid(&request) {
|
||||
return Ok(web::HttpResponse::BadRequest().body(
|
||||
"At least one of uploaderId or uploaderName must be provided",
|
||||
));
|
||||
}
|
||||
|
||||
let public_url_base = format!(
|
||||
"{}://{}",
|
||||
req.connection_info().scheme(),
|
||||
req.connection_info().host()
|
||||
);
|
||||
let mut requester = requester.get_ref().clone();
|
||||
requester.set_debug_trace_id(Some(trace_id.clone()));
|
||||
let options = ServerOptions {
|
||||
featured: None,
|
||||
category: None,
|
||||
sites: None,
|
||||
filter: None,
|
||||
language: None,
|
||||
public_url_base: Some(public_url_base),
|
||||
requester: Some(requester),
|
||||
network: None,
|
||||
stars: None,
|
||||
categories: None,
|
||||
duration: None,
|
||||
sort: None,
|
||||
sexuality: None,
|
||||
};
|
||||
|
||||
crate::flow_debug!(
|
||||
"trace={} uploaders request uploader_id={:?} uploader_name={:?} profile_content={} query={:?}",
|
||||
trace_id,
|
||||
&request.uploaderId,
|
||||
&request.uploaderName,
|
||||
request.profileContent,
|
||||
&request.query
|
||||
);
|
||||
|
||||
if let Some(uploader_id) = request.uploaderId.as_deref() {
|
||||
if let Some(provider_id) = provider_hint_from_uploader_id(uploader_id) {
|
||||
let Some(provider) = get_provider(&provider_id) else {
|
||||
return Ok(web::HttpResponse::NotFound().finish());
|
||||
};
|
||||
let result = lookup_uploader_with_provider(
|
||||
&provider_id,
|
||||
provider,
|
||||
cache.get_ref().clone(),
|
||||
pool.get_ref().clone(),
|
||||
&request,
|
||||
options,
|
||||
)
|
||||
.await;
|
||||
return match result {
|
||||
Ok(Some(profile)) => Ok(web::HttpResponse::Ok().json(&profile)),
|
||||
Ok(None) => Ok(web::HttpResponse::NotFound().finish()),
|
||||
Err(_error) => {
|
||||
crate::flow_debug!(
|
||||
"trace={} uploaders targeted provider failed provider={} error={}",
|
||||
trace_id,
|
||||
&provider_id,
|
||||
&_error
|
||||
);
|
||||
Ok(web::HttpResponse::InternalServerError().finish())
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
let mut matches = Vec::new();
|
||||
let mut saw_error = false;
|
||||
let requested_name = request
|
||||
.uploaderName
|
||||
.as_ref()
|
||||
.map(|value| value.to_ascii_lowercase());
|
||||
|
||||
for provider_id in uploader_provider_ids() {
|
||||
let Some(provider) = get_provider(&provider_id) else {
|
||||
continue;
|
||||
};
|
||||
let result = lookup_uploader_with_provider(
|
||||
&provider_id,
|
||||
provider,
|
||||
cache.get_ref().clone(),
|
||||
pool.get_ref().clone(),
|
||||
&request,
|
||||
options.clone(),
|
||||
)
|
||||
.await;
|
||||
|
||||
match result {
|
||||
Ok(Some(profile)) => {
|
||||
if let Some(requested_name) = requested_name.as_deref() {
|
||||
if profile.name.to_ascii_lowercase() != requested_name {
|
||||
crate::flow_debug!(
|
||||
"trace={} uploaders ignoring non_exact_match provider={} requested={} returned={}",
|
||||
trace_id,
|
||||
&provider_id,
|
||||
requested_name,
|
||||
&profile.name
|
||||
);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
matches.push(profile);
|
||||
}
|
||||
Ok(None) => {}
|
||||
Err(_error) => {
|
||||
saw_error = true;
|
||||
crate::flow_debug!(
|
||||
"trace={} uploaders provider failed provider={} error={}",
|
||||
trace_id,
|
||||
&provider_id,
|
||||
&_error
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if matches.is_empty() {
|
||||
if saw_error {
|
||||
return Ok(web::HttpResponse::InternalServerError().finish());
|
||||
}
|
||||
return Ok(web::HttpResponse::NotFound().finish());
|
||||
}
|
||||
|
||||
matches.sort_by(|a, b| uploader_match_sort_key(b).cmp(&uploader_match_sort_key(a)));
|
||||
Ok(web::HttpResponse::Ok().json(&matches[0]))
|
||||
}
|
||||
|
||||
pub fn get_provider(channel: &str) -> Option<DynProvider> {
|
||||
let provider = ALL_PROVIDERS.get(channel).cloned();
|
||||
crate::flow_debug!(
|
||||
@@ -539,3 +730,49 @@ pub async fn proxies() -> Result<impl web::Responder, web::Error> {
|
||||
}
|
||||
Ok(web::HttpResponse::Ok().json(&by_protocol))
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn uploaders_request_requires_id_or_name() {
|
||||
let invalid = UploadersRequest::default();
|
||||
let valid = UploadersRequest {
|
||||
uploaderName: Some("Example".to_string()),
|
||||
..UploadersRequest::default()
|
||||
};
|
||||
|
||||
assert!(!uploader_request_is_valid(&invalid));
|
||||
assert!(uploader_request_is_valid(&valid));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uploader_provider_hint_uses_channel_prefix() {
|
||||
assert_eq!(
|
||||
provider_hint_from_uploader_id("hsex:xihongshiddd").as_deref(),
|
||||
Some("hsex")
|
||||
);
|
||||
assert_eq!(provider_hint_from_uploader_id("plain-id"), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn uploader_match_prefers_higher_video_count() {
|
||||
let a = UploaderProfile {
|
||||
id: "a".to_string(),
|
||||
name: "Example".to_string(),
|
||||
channel: Some("alpha".to_string()),
|
||||
videoCount: 3,
|
||||
..UploaderProfile::default()
|
||||
};
|
||||
let b = UploaderProfile {
|
||||
id: "b".to_string(),
|
||||
name: "Example".to_string(),
|
||||
channel: Some("beta".to_string()),
|
||||
videoCount: 9,
|
||||
..UploaderProfile::default()
|
||||
};
|
||||
|
||||
assert!(uploader_match_sort_key(&b) > uploader_match_sort_key(&a));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user