archivebate: verified app clients get the proxy URL as item url and no formats

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MUYdCh2nCVsQYgJAT7gwSi
This commit is contained in:
Simon
2026-10-19 07:39:48 +00:00
parent 30271b9b7c
commit f900987434

View File

@@ -815,49 +815,19 @@ impl ArchivebateProvider {
item
}
/// For Hot Tub app clients: resolve every item's real media URL at the same
/// time (one task per item, no concurrency cap) and hand the app that URL
/// with the headers the CDN is played with, instead of the lazy
/// `/proxy/archivebate` redirect. Items that fail or time out keep the
/// proxied format, which re-resolves at playback time.
async fn resolve_direct_formats(
videos: Vec<VideoItem>,
options: &ServerOptions,
) -> Vec<VideoItem> {
let tasks: Vec<_> = videos
.iter()
.map(|video| {
let detail_url = video.url.clone();
let mut requester =
requester_or_default(options, CHANNEL_ID, "archivebate.resolve_direct");
tokio::spawn(async move {
timeout(
StdDuration::from_secs(25),
crate::proxies::archivebate::resolve_media(&mut requester, &detail_url),
)
.await
.ok()
.flatten()
})
})
.collect();
let resolved = futures::future::join_all(tasks).await;
/// For verified Hot Tub app clients: no `formats` at all. The item's `url`
/// is the lazy `/proxy/archivebate` redirect, which the app plays directly
/// (it resolves the media at playback time).
fn proxy_as_item_url(videos: Vec<VideoItem>, options: &ServerOptions) -> Vec<VideoItem> {
videos
.into_iter()
.zip(resolved)
.map(|(mut video, resolved)| match resolved.ok().flatten() {
Some(media) => {
video.formats = Some(vec![Self::video_format_with_headers(
VideoFormat::new(media.media_url, "source".to_string(), "mp4".to_string()),
vec![
("Referer".to_string(), media.embed_url),
("User-Agent".to_string(), FIREFOX_UA.to_string()),
],
)]);
video
.map(|mut video| {
let proxy_url = Self::proxied_video(options, &video.url);
if !proxy_url.is_empty() {
video.url = proxy_url;
}
None => Self::with_default_format(video, options),
video.formats = None;
video
})
.collect()
}
@@ -1261,7 +1231,7 @@ impl Provider for ArchivebateProvider {
.map(ClientVersion::is_verified_hottub)
.unwrap_or(false);
if is_app_client {
return Self::resolve_direct_formats(videos, &options).await;
return Self::proxy_as_item_url(videos, &options);
}
stream::iter(videos.into_iter().map(|video| {
let provider = self.clone();
@@ -1293,3 +1263,52 @@ impl Provider for ArchivebateProvider {
Some(self.build_channel(clientversion))
}
}
#[cfg(test)]
mod tests {
use super::*;
fn options() -> ServerOptions {
ServerOptions {
client_version: None,
featured: None,
category: None,
sites: None,
filter: None,
language: None,
public_url_base: Some("http://hottub.example".to_string()),
requester: None,
network: None,
stars: None,
categories: None,
duration: None,
sort: None,
sexuality: None,
}
}
#[test]
fn app_clients_get_the_proxy_url_and_no_formats() {
let mut item = VideoItem::new(
"archivebate:1".to_string(),
"t".to_string(),
"https://archivebate.com/watch/1".to_string(),
CHANNEL_ID.to_string(),
"https://cdn.example/t.jpg".to_string(),
10,
);
item.formats = Some(vec![VideoFormat::new(
"x".to_string(),
"source".to_string(),
"mp4".to_string(),
)]);
let out = ArchivebateProvider::proxy_as_item_url(vec![item], &options());
assert_eq!(out.len(), 1);
assert_eq!(
out[0].url,
"http://hottub.example/proxy/archivebate/archivebate.com/watch/1"
);
assert!(out[0].formats.is_none());
}
}