status upgrade

This commit is contained in:
Simon
2026-03-18 12:13:28 +00:00
parent ce781e2099
commit ce1afd9873
44 changed files with 508 additions and 3 deletions

View File

@@ -9,7 +9,7 @@ use std::sync::Arc;
use crate::{
DbPool,
api::ClientVersion,
status::Channel,
status::{Channel, ChannelGroup, ChannelView, Status, StatusResponse},
util::{cache::VideoCache, discord::send_discord_error_report, requester::Requester},
videos::{ServerOptions, VideoItem},
};
@@ -62,6 +62,12 @@ pub mod xxdbx;
// convenient alias
pub type DynProvider = Arc<dyn Provider>;
#[derive(Clone, Copy)]
pub struct ProviderChannelMetadata {
pub group_id: &'static str,
pub tags: &'static [&'static str],
}
pub static ALL_PROVIDERS: Lazy<HashMap<&'static str, DynProvider>> = Lazy::new(|| {
let mut m = HashMap::default();
m.insert("all", Arc::new(all::AllProvider::new()) as DynProvider);
@@ -326,6 +332,166 @@ pub fn build_proxy_url(options: &ServerOptions, proxy: &str, target: &str) -> St
}
}
fn channel_metadata_for(id: &str) -> Option<ProviderChannelMetadata> {
match id {
"all" | "hottub" => Some(all::CHANNEL_METADATA),
"pornhub" => Some(pornhub::CHANNEL_METADATA),
"spankbang" => Some(spankbang::CHANNEL_METADATA),
"rule34video" => Some(rule34video::CHANNEL_METADATA),
"redtube" => Some(redtube::CHANNEL_METADATA),
"okporn" => Some(okporn::CHANNEL_METADATA),
"pornhat" => Some(pornhat::CHANNEL_METADATA),
"perfectgirls" => Some(perfectgirls::CHANNEL_METADATA),
"okxxx" => Some(okxxx::CHANNEL_METADATA),
"homoxxx" => Some(homoxxx::CHANNEL_METADATA),
"missav" => Some(missav::CHANNEL_METADATA),
"xxthots" => Some(xxthots::CHANNEL_METADATA),
"sxyprn" => Some(sxyprn::CHANNEL_METADATA),
"porn00" => Some(porn00::CHANNEL_METADATA),
"youjizz" => Some(youjizz::CHANNEL_METADATA),
"paradisehill" => Some(paradisehill::CHANNEL_METADATA),
"porn4fans" => Some(porn4fans::CHANNEL_METADATA),
"porndish" => Some(porndish::CHANNEL_METADATA),
"shooshtime" => Some(shooshtime::CHANNEL_METADATA),
"pornzog" => Some(pornzog::CHANNEL_METADATA),
"omgxxx" => Some(omgxxx::CHANNEL_METADATA),
"beeg" => Some(beeg::CHANNEL_METADATA),
"tnaflix" => Some(tnaflix::CHANNEL_METADATA),
"tokyomotion" => Some(tokyomotion::CHANNEL_METADATA),
"viralxxxporn" => Some(viralxxxporn::CHANNEL_METADATA),
"rule34gen" => Some(rule34gen::CHANNEL_METADATA),
"xxdbx" => Some(xxdbx::CHANNEL_METADATA),
"xfree" => Some(xfree::CHANNEL_METADATA),
"hqporner" => Some(hqporner::CHANNEL_METADATA),
"pmvhaven" => Some(pmvhaven::CHANNEL_METADATA),
"noodlemagazine" => Some(noodlemagazine::CHANNEL_METADATA),
"pimpbunny" => Some(pimpbunny::CHANNEL_METADATA),
"javtiful" => Some(javtiful::CHANNEL_METADATA),
"hypnotube" => Some(hypnotube::CHANNEL_METADATA),
"freepornvideosxxx" => Some(freepornvideosxxx::CHANNEL_METADATA),
"heavyfetish" => Some(heavyfetish::CHANNEL_METADATA),
"hsex" => Some(hsex::CHANNEL_METADATA),
"hentaihaven" => Some(hentaihaven::CHANNEL_METADATA),
"hanime" => Some(hanime::CHANNEL_METADATA),
"perverzija" => Some(perverzija::CHANNEL_METADATA),
"chaturbate" => Some(chaturbate::CHANNEL_METADATA),
_ => None,
}
}
fn channel_group_title(group_id: &str) -> &'static str {
match group_id {
"meta-search" => "Meta Search",
"mainstream-tube" => "Mainstream Tube",
"studio-network" => "Studio & Network",
"amateur-homemade" => "Amateur & Homemade",
"creator-leaks" => "Creator & Leaks",
"asian-jav" => "Asian & JAV",
"fetish-kink" => "Fetish & Kink",
"hentai-animation" => "Hentai & Animation",
"gay-male" => "Gay & Male",
"live-cams" => "Live Cams",
"pmv-compilation" => "PMV & Compilation",
_ => "Other",
}
}
fn channel_group_order(group_id: &str) -> usize {
match group_id {
"meta-search" => 0,
"mainstream-tube" => 1,
"studio-network" => 2,
"amateur-homemade" => 3,
"creator-leaks" => 4,
"asian-jav" => 5,
"fetish-kink" => 6,
"hentai-animation" => 7,
"gay-male" => 8,
"live-cams" => 9,
"pmv-compilation" => 10,
_ => 99,
}
}
pub fn decorate_channel(channel: Channel) -> ChannelView {
let metadata = channel_metadata_for(&channel.id);
ChannelView {
id: channel.id,
name: channel.name,
description: channel.description,
premium: channel.premium,
favicon: channel.favicon,
status: channel.status,
categories: channel.categories,
options: channel.options,
nsfw: channel.nsfw,
group: metadata.map(|value| value.group_id.to_string()),
tags: metadata.map(|value| {
value
.tags
.iter()
.take(3)
.map(|tag| (*tag).to_string())
.collect()
}),
cacheDuration: channel.cacheDuration,
}
}
pub fn build_channel_groups(channels: &[ChannelView]) -> Vec<ChannelGroup> {
let mut groups = Vec::new();
let mut group_ids = channels
.iter()
.filter_map(|channel| channel.group.clone())
.collect::<Vec<_>>();
group_ids.sort_by_key(|group_id| (channel_group_order(group_id), group_id.clone()));
group_ids.dedup();
for group_id in group_ids {
let mut channel_ids = channels
.iter()
.filter(|channel| channel.group.as_deref() == Some(group_id.as_str()))
.map(|channel| channel.id.clone())
.collect::<Vec<_>>();
channel_ids.sort();
groups.push(ChannelGroup {
id: group_id.clone(),
title: channel_group_title(&group_id).to_string(),
channels: channel_ids,
});
}
groups
}
pub fn build_status_response(status: Status) -> StatusResponse {
let channels = status
.channels
.into_iter()
.map(decorate_channel)
.collect::<Vec<_>>();
let channelGroups = build_channel_groups(&channels);
StatusResponse {
id: status.id,
name: status.name,
subtitle: status.subtitle,
description: status.description,
iconUrl: status.iconUrl,
color: status.color,
status: status.status,
notices: status.notices,
channels,
channelGroups,
subscription: status.subscription,
nsfw: status.nsfw,
categories: status.categories,
options: status.options,
filtersFooter: status.filtersFooter,
}
}
#[async_trait]
pub trait Provider: Send + Sync {
async fn get_videos(
@@ -359,3 +525,51 @@ pub trait Provider: Send + Sync {
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::status::ChannelOption;
fn base_channel(id: &str) -> Channel {
Channel {
id: id.to_string(),
name: id.to_string(),
description: String::new(),
premium: false,
favicon: String::new(),
status: "active".to_string(),
categories: vec![],
options: Vec::<ChannelOption>::new(),
nsfw: true,
cacheDuration: None,
}
}
#[test]
fn decorates_channel_with_group_and_tags() {
let channel = decorate_channel(base_channel("hsex"));
assert_eq!(channel.group.as_deref(), Some("amateur-homemade"));
assert_eq!(
channel.tags.as_deref(),
Some(&[
"amateur".to_string(),
"chinese".to_string(),
"homemade".to_string(),
][..])
);
}
#[test]
fn builds_group_index() {
let channels = vec![
decorate_channel(base_channel("all")),
decorate_channel(base_channel("hsex")),
decorate_channel(base_channel("missav")),
];
let groups = build_channel_groups(&channels);
assert_eq!(groups[0].id, "meta-search");
assert_eq!(groups[1].id, "amateur-homemade");
assert_eq!(groups[2].id, "asian-jav");
}
}