pmvhaven backend fix
This commit is contained in:
79
src/api.rs
79
src/api.rs
@@ -10,7 +10,6 @@ use crate::providers::all::AllProvider;
|
||||
use crate::providers::hanime::HanimeProvider;
|
||||
use crate::providers::okporn::OkpornProvider;
|
||||
use crate::providers::perverzija::PerverzijaProvider;
|
||||
use crate::providers::pmvhaven::PmvhavenProvider;
|
||||
use crate::providers::pornhub::PornhubProvider;
|
||||
use crate::providers::redtube::RedtubeProvider;
|
||||
use crate::providers::rule34video::Rule34videoProvider;
|
||||
@@ -164,82 +163,6 @@ async fn status(req: HttpRequest) -> Result<impl web::Responder, web::Error> {
|
||||
nsfw: true,
|
||||
cacheDuration: Some(1800),
|
||||
});
|
||||
if clientversion >= ClientVersion::new(22, 101, "22e".to_string()) {
|
||||
// pmvhaven
|
||||
status.add_channel(Channel {
|
||||
id: "pmvhaven".to_string(),
|
||||
name: "Pmvhaven".to_string(),
|
||||
description: "Explore a curated collection of captivating PMV".to_string(),
|
||||
premium: false,
|
||||
favicon: "https://www.google.com/s2/favicons?sz=64&domain=pmvhaven.com".to_string(),
|
||||
status: "active".to_string(),
|
||||
categories: vec![],
|
||||
options: vec![
|
||||
ChannelOption {
|
||||
id: "category".to_string(),
|
||||
title: "Category".to_string(),
|
||||
description: "Category of PMV Video get".to_string(), //"Sort the videos by Date or Name.".to_string(),
|
||||
systemImage: "folder".to_string(),
|
||||
colorName: "yellow".to_string(),
|
||||
options: vec![
|
||||
FilterOption {
|
||||
id: "all".to_string(),
|
||||
title: "All".to_string(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "pmv".to_string(),
|
||||
title: "PMV".to_string(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "hmv".to_string(),
|
||||
title: "HMV".to_string(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "tiktok".to_string(),
|
||||
title: "Tiktok".to_string(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "koreanbj".to_string(),
|
||||
title: "KoreanBJ".to_string(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "hypno".to_string(),
|
||||
title: "Hypno".to_string(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "other".to_string(),
|
||||
title: "Other".to_string(),
|
||||
},
|
||||
],
|
||||
multiSelect: false,
|
||||
},
|
||||
ChannelOption {
|
||||
id: "sort".to_string(),
|
||||
title: "Filter".to_string(),
|
||||
description: "Filter PMV Videos".to_string(),
|
||||
systemImage: "list.number".to_string(),
|
||||
colorName: "blue".to_string(),
|
||||
options: vec![
|
||||
FilterOption {
|
||||
id: "Newest".to_string(),
|
||||
title: "Newest".to_string(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "Top Rated".to_string(),
|
||||
title: "Top Rated".to_string(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "Most Viewed".to_string(),
|
||||
title: "Most Viewed".to_string(),
|
||||
},
|
||||
],
|
||||
multiSelect: false,
|
||||
},
|
||||
],
|
||||
nsfw: true,
|
||||
cacheDuration: Some(1800),
|
||||
});
|
||||
}
|
||||
if clientversion >= ClientVersion::new(22, 97, "22a".to_string()) {
|
||||
// perverzija
|
||||
status.add_channel(Channel {
|
||||
@@ -1241,6 +1164,7 @@ async fn videos_post(
|
||||
stars: Some(stars),
|
||||
categories: Some(categories),
|
||||
duration: Some(duration),
|
||||
sort: Some(sort.clone())
|
||||
};
|
||||
let video_items = provider
|
||||
.get_videos(
|
||||
@@ -1308,7 +1232,6 @@ pub fn get_provider(channel: &str) -> Option<DynProvider> {
|
||||
"perverzija" => Some(Arc::new(PerverzijaProvider::new())),
|
||||
"hanime" => Some(Arc::new(HanimeProvider::new())),
|
||||
"pornhub" => Some(Arc::new(PornhubProvider::new())),
|
||||
"pmvhaven" => Some(Arc::new(PmvhavenProvider::new())),
|
||||
"rule34video" => Some(Arc::new(Rule34videoProvider::new())),
|
||||
"redtube" => Some(Arc::new(RedtubeProvider::new())),
|
||||
"okporn" => Some(Arc::new(OkpornProvider::new())),
|
||||
|
||||
@@ -50,6 +50,7 @@ pub static ALL_PROVIDERS: Lazy<HashMap<&'static str, DynProvider>> = Lazy::new(|
|
||||
m.insert("rule34gen", Arc::new(rule34gen::Rule34genProvider::new()) as DynProvider);
|
||||
m.insert("xxdbx", Arc::new(xxdbx::XxdbxProvider::new()) as DynProvider);
|
||||
m.insert("hqporner", Arc::new(hqporner::HqpornerProvider::new()) as DynProvider);
|
||||
m.insert("pmvhaven", Arc::new(pmvhaven::PmvhavenProvider::new()) as DynProvider);
|
||||
// add more here as you migrate them
|
||||
m
|
||||
});
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
use crate::DbPool;
|
||||
use crate::api::ClientVersion;
|
||||
use crate::providers::Provider;
|
||||
use crate::status::*;
|
||||
use crate::util::cache::VideoCache;
|
||||
use crate::util::time::parse_time_to_seconds;
|
||||
use crate::videos::{ServerOptions, VideoItem};
|
||||
use async_trait::async_trait;
|
||||
use cute::c;
|
||||
use error_chain::error_chain;
|
||||
// use percent_encoding::{AsciiSet, CONTROLS, utf8_percent_encode};
|
||||
use std::vec;
|
||||
use htmlentity::entity::{ICodedDataTrait, decode};
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::{vec};
|
||||
|
||||
error_chain! {
|
||||
foreign_links {
|
||||
@@ -15,294 +18,112 @@ error_chain! {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct PmvhavenRequest {
|
||||
all: bool, //true,
|
||||
pmv: bool, //false,
|
||||
hmv: bool, //false,
|
||||
hypno: bool, //false,
|
||||
tiktok: bool, //false,
|
||||
koreanbj: bool, //false,
|
||||
other: bool, // false,
|
||||
explicitContent: Option<bool>, //null,
|
||||
sameSexContent: Option<bool>, //null,
|
||||
transContent: Option<String>, //null
|
||||
seizureWarning: Option<bool>, //null,
|
||||
tags: Vec<String>, //[],
|
||||
music: Vec<String>, //[],
|
||||
stars: Vec<String>, //[],
|
||||
creators: Vec<String>, //[],
|
||||
range: Vec<u32>, //[0,40],
|
||||
activeTime: String, //"All time",
|
||||
activeQuality: String, //"Quality",
|
||||
aspectRatio: String, //"Aspect Ratio",
|
||||
activeView: String, //"Newest",
|
||||
index: u32, //2,
|
||||
showSubscriptionsOnly: bool, //false,
|
||||
query: String, //"no",
|
||||
profile: Option<String>, //null
|
||||
}
|
||||
|
||||
impl PmvhavenRequest {
|
||||
pub fn new(page: u32) -> Self {
|
||||
PmvhavenRequest {
|
||||
all: true,
|
||||
pmv: false,
|
||||
hmv: false,
|
||||
hypno: false,
|
||||
tiktok: false,
|
||||
koreanbj: false,
|
||||
other: false,
|
||||
explicitContent: None,
|
||||
sameSexContent: None,
|
||||
transContent: None,
|
||||
seizureWarning: None,
|
||||
tags: vec![],
|
||||
music: vec![],
|
||||
stars: vec![],
|
||||
creators: vec![],
|
||||
range: vec![0, 40],
|
||||
activeTime: "All time".to_string(),
|
||||
activeQuality: "Quality".to_string(),
|
||||
aspectRatio: "Aspect Ratio".to_string(),
|
||||
activeView: "Newest".to_string(),
|
||||
index: page,
|
||||
showSubscriptionsOnly: false,
|
||||
query: "no".to_string(),
|
||||
profile: None,
|
||||
}
|
||||
}
|
||||
fn hypno(&mut self) -> &mut Self {
|
||||
self.all = false;
|
||||
self.pmv = false;
|
||||
self.hmv = false;
|
||||
self.tiktok = false;
|
||||
self.koreanbj = false;
|
||||
self.other = false;
|
||||
self.hypno = true;
|
||||
self
|
||||
}
|
||||
fn pmv(&mut self) -> &mut Self {
|
||||
self.all = false;
|
||||
self.pmv = true;
|
||||
self.hmv = false;
|
||||
self.tiktok = false;
|
||||
self.koreanbj = false;
|
||||
self.other = false;
|
||||
self.hypno = false;
|
||||
self
|
||||
}
|
||||
fn hmv(&mut self) -> &mut Self {
|
||||
self.all = false;
|
||||
self.pmv = false;
|
||||
self.hmv = true;
|
||||
self.tiktok = false;
|
||||
self.koreanbj = false;
|
||||
self.other = false;
|
||||
self.hypno = false;
|
||||
self
|
||||
}
|
||||
fn tiktok(&mut self) -> &mut Self {
|
||||
self.all = false;
|
||||
self.pmv = false;
|
||||
self.hmv = false;
|
||||
self.tiktok = true;
|
||||
self.koreanbj = false;
|
||||
self.other = false;
|
||||
self.hypno = false;
|
||||
self
|
||||
}
|
||||
fn koreanbj(&mut self) -> &mut Self {
|
||||
self.all = false;
|
||||
self.pmv = false;
|
||||
self.hmv = false;
|
||||
self.tiktok = false;
|
||||
self.koreanbj = true;
|
||||
self.other = false;
|
||||
self.hypno = false;
|
||||
self
|
||||
}
|
||||
fn other(&mut self) -> &mut Self {
|
||||
self.all = false;
|
||||
self.pmv = false;
|
||||
self.hmv = false;
|
||||
self.tiktok = false;
|
||||
self.koreanbj = false;
|
||||
self.other = true;
|
||||
self.hypno = false;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct PmvhavenSearch {
|
||||
mode: String, //"DefaultMoreSearch",
|
||||
data: String, //"pmv",
|
||||
index: u32,
|
||||
}
|
||||
|
||||
impl PmvhavenSearch {
|
||||
fn new(search: String, page: u32) -> PmvhavenSearch {
|
||||
PmvhavenSearch {
|
||||
mode: "DefaultMoreSearch".to_string(),
|
||||
data: search,
|
||||
index: page,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct PmvhavenVideo {
|
||||
title: String, //JAV Addiction Therapy",
|
||||
_uploader: Option<String>, //itonlygetsworse",
|
||||
duration: f32, //259.093333,
|
||||
_width: Option<String>, //3840",
|
||||
_height: Option<String>, //2160",
|
||||
_ratio: Option<u32>, //50,
|
||||
thumbnails: Vec<Option<String>>, //[
|
||||
// "placeholder",
|
||||
// "https://storage.pmvhaven.com/686f24e96f7124f3dfbe90ab/thumbnail/JAV Addiction Therapy_686f24e96f7124f3dfbe90ab.png",
|
||||
// "https://storage.pmvhaven.com/686f24e96f7124f3dfbe90ab/thumbnail/webp320_686f24e96f7124f3dfbe90ab.webp"
|
||||
// ],
|
||||
views: u32, //1971,
|
||||
_url: Option<String>, //https://storage.pmvhaven.com/686f24e96f7124f3dfbe90ab/JAV Addiction Therapy_686f24e96f7124f3dfbe90ab.mp4",
|
||||
previewUrlCompressed: Option<String>, //https://storage.pmvhaven.com/686f24e96f7124f3dfbe90ab/videoPreview/comus_686f24e96f7124f3dfbe90ab.mp4",
|
||||
_seizureWarning: Option<bool>, //false,
|
||||
_isoDate: Option<String>, //2025-07-10T02:52:26.000Z",
|
||||
_gayContent: Option<bool>, //false,
|
||||
_transContent: Option<bool>, //false,
|
||||
creator: Option<String>, //itonlygetsworse",
|
||||
_id: String, //686f2aeade2062f93d72931f",
|
||||
_totalRaters: Option<u32>, //42,
|
||||
_rating: Option<u32>, //164
|
||||
}
|
||||
|
||||
impl PmvhavenVideo {
|
||||
fn to_videoitem(self) -> VideoItem {
|
||||
// let encoded_title = percent_encode_emojis(&self.title);
|
||||
let thumbnail = self.thumbnails[self.thumbnails.len() - 1]
|
||||
.clone()
|
||||
.unwrap_or("".to_string());
|
||||
// let video_id = thumbnail.split("_").collect::<Vec<&str>>().last().unwrap_or(&"").to_string().split('.').next().unwrap_or("").to_string();
|
||||
let mut item = VideoItem::new(
|
||||
self._id.clone(),
|
||||
self.title.clone(),
|
||||
format!(
|
||||
"https://pmvhaven.com/video/{}_{}",
|
||||
self.title.replace(" ", "-"),
|
||||
self._id
|
||||
),
|
||||
"pmvhaven".to_string(),
|
||||
thumbnail,
|
||||
self.duration as u32,
|
||||
)
|
||||
.views(self.views);
|
||||
item = match self.creator {
|
||||
Some(c) => item.uploader(c),
|
||||
_ => item,
|
||||
};
|
||||
item = match self.previewUrlCompressed {
|
||||
Some(u) => item.preview(u),
|
||||
_ => item,
|
||||
};
|
||||
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize)]
|
||||
struct PmvhavenResponse {
|
||||
data: Vec<PmvhavenVideo>,
|
||||
_count: Option<u32>,
|
||||
}
|
||||
|
||||
impl PmvhavenResponse {
|
||||
fn to_videoitems(self) -> Vec<VideoItem> {
|
||||
return c![video.to_videoitem(), for video in self.data];
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PmvhavenProvider {
|
||||
url: String,
|
||||
stars: Arc<RwLock<Vec<String>>>,
|
||||
categories: Arc<RwLock<Vec<String>>>,
|
||||
}
|
||||
impl PmvhavenProvider {
|
||||
pub fn new() -> Self {
|
||||
PmvhavenProvider {
|
||||
let provider = PmvhavenProvider {
|
||||
url: "https://pmvhaven.com".to_string(),
|
||||
stars: Arc::new(RwLock::new(vec![])),
|
||||
categories: Arc::new(RwLock::new(vec![])),
|
||||
};
|
||||
provider
|
||||
}
|
||||
|
||||
fn build_channel(&self, clientversion: ClientVersion) -> Channel {
|
||||
// if clientversion >= ClientVersion::new(22, 101, "22e".to_string()) {
|
||||
let _ = clientversion;
|
||||
Channel {
|
||||
id: "pmvhaven".to_string(),
|
||||
name: "PMVHaven".to_string(),
|
||||
description: "Best PMV Videos".to_string(),
|
||||
premium: false,
|
||||
favicon: "https://www.google.com/s2/favicons?sz=64&domain=pmvhaven.com".to_string(),
|
||||
status: "active".to_string(),
|
||||
categories: self.categories.read().unwrap().iter().map(|c| c.clone()).collect(),
|
||||
options: vec![
|
||||
ChannelOption {
|
||||
id: "sort".to_string(),
|
||||
title: "Sort".to_string(),
|
||||
description: "Sort the Videos".to_string(),
|
||||
systemImage: "list.number".to_string(),
|
||||
colorName: "blue".to_string(),
|
||||
options: vec![
|
||||
FilterOption {
|
||||
id: "relevance".into(),
|
||||
title: "Relevance".into(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "newest".into(),
|
||||
title: "Newest".into(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "oldest".into(),
|
||||
title: "Oldest".into(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "most viewed".into(),
|
||||
title: "Most Viewed".into(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "most liked".into(),
|
||||
title: "Most Liked".into(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "most disliked".into(),
|
||||
title: "Most Disliked".into(),
|
||||
},
|
||||
],
|
||||
multiSelect: false,
|
||||
},
|
||||
ChannelOption {
|
||||
id: "duration".to_string(),
|
||||
title: "Duration".to_string(),
|
||||
description: "Length of the Videos".to_string(),
|
||||
systemImage: "timer".to_string(),
|
||||
colorName: "green".to_string(),
|
||||
options: vec![
|
||||
FilterOption {
|
||||
id: "any".into(),
|
||||
title: "Any".into(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "<4 min".into(),
|
||||
title: "<4 min".into(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "4-20 min".into(),
|
||||
title: "4-20 min".into(),
|
||||
},
|
||||
FilterOption {
|
||||
id: "20-60 min".into(),
|
||||
title: "20-60 min".into(),
|
||||
},
|
||||
FilterOption {
|
||||
id: ">1 hour".into(),
|
||||
title: ">1 hour".into(),
|
||||
},
|
||||
],
|
||||
multiSelect: false,
|
||||
},
|
||||
],
|
||||
nsfw: true,
|
||||
cacheDuration: None,
|
||||
}
|
||||
}
|
||||
async fn get(
|
||||
&self,
|
||||
cache: VideoCache,
|
||||
page: u8,
|
||||
sort: String,
|
||||
options: ServerOptions,
|
||||
) -> Result<Vec<VideoItem>> {
|
||||
let category = options.category.unwrap_or("".to_string());
|
||||
let index = format!("pmvhaven:{}:{}", page, category);
|
||||
let url = format!("{}/api/getmorevideos", self.url);
|
||||
let mut request = PmvhavenRequest::new(page as u32);
|
||||
request.activeView = sort;
|
||||
request = match category.as_str() {
|
||||
"hypno" => {
|
||||
request.hypno();
|
||||
request
|
||||
}
|
||||
"pmv" => {
|
||||
request.pmv();
|
||||
request
|
||||
}
|
||||
"hmv" => {
|
||||
request.hmv();
|
||||
request
|
||||
}
|
||||
"tiktok" => {
|
||||
request.tiktok();
|
||||
request
|
||||
}
|
||||
"koreanbj" => {
|
||||
request.koreanbj();
|
||||
request
|
||||
}
|
||||
"other" => {
|
||||
request.other();
|
||||
request
|
||||
}
|
||||
_ => request,
|
||||
};
|
||||
|
||||
let old_items = match cache.get(&index) {
|
||||
Some((time, items)) => {
|
||||
if time.elapsed().unwrap_or_default().as_secs() < 60 * 5 {
|
||||
println!("Cache hit for URL: {}", url);
|
||||
return Ok(items.clone());
|
||||
} else {
|
||||
items.clone()
|
||||
}
|
||||
// Push one item with minimal lock time and dedup by id
|
||||
fn push_unique(target: &Arc<RwLock<Vec<String>>>, item: String) {
|
||||
if let Ok(mut vec) = target.write() {
|
||||
if !vec.iter().any(|x| x == &item) {
|
||||
println!("Added new item: {}", item.clone());
|
||||
vec.push(item);
|
||||
}
|
||||
None => {
|
||||
vec![]
|
||||
}
|
||||
};
|
||||
|
||||
let mut requester = options.requester.clone().unwrap();
|
||||
let response = requester.post(&url, &request, vec![("Content-Type".to_string(),"text/plain;charset=UTF-8".to_string())]).await.unwrap();
|
||||
let videos = match response.json::<PmvhavenResponse>().await {
|
||||
Ok(resp) => resp,
|
||||
Err(e) => {
|
||||
println!("Failed to parse PmvhavenResponse: {}", e);
|
||||
return Ok(old_items);
|
||||
}
|
||||
};
|
||||
let video_items: Vec<VideoItem> = videos.to_videoitems();
|
||||
if !video_items.is_empty() {
|
||||
cache.remove(&url);
|
||||
cache.insert(url.clone(), video_items.clone());
|
||||
} else {
|
||||
return Ok(old_items);
|
||||
}
|
||||
return Ok(video_items);
|
||||
}
|
||||
|
||||
async fn query(
|
||||
@@ -312,11 +133,34 @@ impl PmvhavenProvider {
|
||||
query: &str,
|
||||
options: ServerOptions,
|
||||
) -> Result<Vec<VideoItem>> {
|
||||
let index = format!("pmvhaven:{}:{}", query, page);
|
||||
let url = format!("{}/api/v2/search", self.url);
|
||||
let request = PmvhavenSearch::new(query.to_string(), page as u32);
|
||||
// Check our Video Cache. If the result is younger than 1 hour, we return it.
|
||||
let old_items = match cache.get(&index) {
|
||||
let search_string = query.trim().to_string();
|
||||
let sort_string = match options.sort.unwrap_or("".to_string()).as_str() {
|
||||
"newest" => "sort=-uploadDate",
|
||||
"oldest" => "sort=uploadDate",
|
||||
"most viewed" => "sort=-views",
|
||||
"most liked" => "sort=-likes",
|
||||
"most disliked" => "sort=-dislikes",
|
||||
_ => "",
|
||||
};
|
||||
let duration_string = match options.duration.unwrap_or("".to_string()).as_str(){
|
||||
"<4 min" => "durationMax=240",
|
||||
"4-20 min" => "durationMin=240&durationMax=1200",
|
||||
"20-60 min" => "durationMin=1200&durationMax=3600",
|
||||
">1 hour" => "durationMin=3600",
|
||||
_ => "",
|
||||
};
|
||||
let endpoint = if search_string.is_empty() {
|
||||
"api/videos"
|
||||
} else {
|
||||
"api/videos/search"
|
||||
};
|
||||
let mut video_url = format!("{}/{}?limit=100&page={}&{}&{}", self.url, endpoint, page, duration_string, sort_string);
|
||||
if let Some(star) = self.stars.read().unwrap().iter().find(|s| s.to_ascii_lowercase() == search_string.to_ascii_lowercase()) {
|
||||
video_url = format!("{}&stars={}", video_url, star);
|
||||
} else if let Some(category) = self.categories.read().unwrap().iter().find(|s| s.to_ascii_lowercase() == search_string.to_ascii_lowercase()) {
|
||||
video_url = format!("{}&tagMode=AND&tags={}", video_url, category);
|
||||
}
|
||||
let old_items = match cache.get(&video_url) {
|
||||
Some((time, items)) => {
|
||||
if time.elapsed().unwrap_or_default().as_secs() < 60 * 5 {
|
||||
return Ok(items.clone());
|
||||
@@ -331,22 +175,74 @@ impl PmvhavenProvider {
|
||||
};
|
||||
|
||||
let mut requester = options.requester.clone().unwrap();
|
||||
let response = requester.post(&url, &request, vec![("Content-Type".to_string(),"text/plain;charset=UTF-8".to_string())]).await.unwrap();
|
||||
let videos = match response.json::<PmvhavenResponse>().await {
|
||||
Ok(resp) => resp,
|
||||
Err(e) => {
|
||||
println!("Failed to parse PmvhavenResponse: {}", e);
|
||||
return Ok(old_items);
|
||||
}
|
||||
};
|
||||
let video_items: Vec<VideoItem> = videos.to_videoitems();
|
||||
|
||||
let text = requester.get(&video_url).await.unwrap();
|
||||
let json = serde_json::from_str::<serde_json::Value>(&text).unwrap_or(serde_json::Value::Null);
|
||||
let video_items: Vec<VideoItem> = self
|
||||
.get_video_items_from_json(json)
|
||||
.await;
|
||||
if !video_items.is_empty() {
|
||||
cache.remove(&url);
|
||||
cache.insert(url.clone(), video_items.clone());
|
||||
cache.remove(&video_url);
|
||||
cache.insert(video_url.clone(), video_items.clone());
|
||||
} else {
|
||||
return Ok(old_items);
|
||||
}
|
||||
return Ok(video_items);
|
||||
Ok(video_items)
|
||||
}
|
||||
|
||||
async fn get_video_items_from_json(
|
||||
&self,
|
||||
json: serde_json::Value,
|
||||
) -> Vec<VideoItem> {
|
||||
if json.is_null() {
|
||||
return vec![];
|
||||
}
|
||||
let mut items = vec![];
|
||||
let success = json["success"].as_bool().unwrap_or(false);
|
||||
if !success {
|
||||
return items;
|
||||
}
|
||||
let videos = json["data"].as_array().cloned().unwrap_or_default();
|
||||
if videos.is_empty() {
|
||||
return items;
|
||||
}
|
||||
for video in videos.clone() {
|
||||
let title = decode(video["title"].as_str().unwrap_or("").as_bytes()).to_string().unwrap_or("".to_string());
|
||||
let id = video["_id"].as_str().unwrap_or(title.clone().as_str()).to_string();
|
||||
let video_url = video["videoUrl"].as_str().unwrap_or("").to_string();
|
||||
let views = video["views"].as_u64().unwrap_or(0);
|
||||
let thumb = video["thumbnailUrl"].as_str().unwrap_or("").to_string();
|
||||
let duration_str = video["duration"].as_str().unwrap_or("0");
|
||||
let duration = parse_time_to_seconds(duration_str).unwrap_or(0);
|
||||
let preview = video["previewUrl"].as_str().unwrap_or("").to_string();
|
||||
let tags_array = video["tags"].as_array().cloned().unwrap_or_default();
|
||||
for tag in tags_array.clone() {
|
||||
let tag_str = decode(tag.as_str().unwrap_or("").as_bytes()).to_string().unwrap_or("".to_string());
|
||||
Self::push_unique(&self.categories, tag_str.clone());
|
||||
}
|
||||
let stars_array = video["starsTags"].as_array().cloned().unwrap_or_default();
|
||||
for tag in stars_array.clone() {
|
||||
let tag_str = decode(tag.as_str().unwrap_or("").as_bytes()).to_string().unwrap_or("".to_string());
|
||||
Self::push_unique(&self.stars, tag_str.clone());
|
||||
}
|
||||
|
||||
let tags = stars_array.iter().chain(tags_array.iter()).cloned().collect::<Vec<_>>();
|
||||
let video_item = VideoItem::new(
|
||||
id,
|
||||
title,
|
||||
video_url.to_string(),
|
||||
"pmvhaven".to_string(),
|
||||
thumb,
|
||||
duration as u32,
|
||||
)
|
||||
.views(views as u32)
|
||||
.preview(preview)
|
||||
.tags(tags.iter().map(|t| decode(t.as_str().unwrap_or("").as_bytes()).to_string().unwrap_or("".to_string())).collect());
|
||||
items.push(video_item);
|
||||
}
|
||||
|
||||
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,29 +252,16 @@ impl Provider for PmvhavenProvider {
|
||||
&self,
|
||||
cache: VideoCache,
|
||||
pool: DbPool,
|
||||
sort: String,
|
||||
_sort: String,
|
||||
query: Option<String>,
|
||||
page: String,
|
||||
per_page: String,
|
||||
options: ServerOptions,
|
||||
) -> Vec<VideoItem> {
|
||||
let _ = per_page;
|
||||
let _ = pool; // Ignored in this implementation
|
||||
let videos: std::result::Result<Vec<VideoItem>, Error> = match query {
|
||||
Some(q) => {
|
||||
self.query(cache, page.parse::<u8>().unwrap_or(1), &q, options)
|
||||
.await
|
||||
}
|
||||
None => {
|
||||
self.get(
|
||||
cache,
|
||||
page.parse::<u8>().unwrap_or(1),
|
||||
sort,
|
||||
options,
|
||||
)
|
||||
.await
|
||||
}
|
||||
};
|
||||
let _ = pool;
|
||||
let videos: std::result::Result<Vec<VideoItem>, Error> = self.query(cache, page.parse::<u8>().unwrap_or(1), query.unwrap_or("".to_string()).as_str(), options)
|
||||
.await;
|
||||
match videos {
|
||||
Ok(v) => v,
|
||||
Err(e) => {
|
||||
@@ -387,4 +270,7 @@ impl Provider for PmvhavenProvider {
|
||||
}
|
||||
}
|
||||
}
|
||||
fn get_channel(&self, clientversion: ClientVersion) -> crate::status::Channel {
|
||||
self.build_channel(clientversion)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,7 @@ pub struct ServerOptions {
|
||||
pub stars: Option<String>, //
|
||||
pub categories: Option<String>, //
|
||||
pub duration: Option<String>, //
|
||||
pub sort: Option<String>, //
|
||||
}
|
||||
|
||||
#[derive(serde::Serialize, Debug)]
|
||||
|
||||
Reference in New Issue
Block a user