fixes
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@ use crate::providers::{
|
||||
};
|
||||
use crate::status::*;
|
||||
use crate::util::cache::VideoCache;
|
||||
use crate::util::hoster_proxy::{proxy_name_for_url, rewrite_hoster_url};
|
||||
use crate::util::parse_abbreviated_number;
|
||||
use crate::util::requester::Requester;
|
||||
use crate::videos::{ServerOptions, VideoItem};
|
||||
@@ -97,6 +98,13 @@ struct DetailMetadata {
|
||||
rating: Option<f32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
struct PlayerEpisode {
|
||||
label: String,
|
||||
film_id: String,
|
||||
episode_id: String,
|
||||
}
|
||||
|
||||
impl SextbProvider {
|
||||
pub fn new() -> Self {
|
||||
let provider = Self {
|
||||
@@ -900,6 +908,79 @@ impl SextbProvider {
|
||||
Ok(metadata)
|
||||
}
|
||||
|
||||
fn parse_player_episodes(html: &str) -> Result<Vec<PlayerEpisode>> {
|
||||
let regex = Self::regex(
|
||||
r#"(?is)<button[^>]*class="[^"]*\bbtn-player\b[^"]*"[^>]*data-source="(?P<film>\d+)"[^>]*data-id="(?P<episode>\d+)"[^>]*>.*?</i>\s*(?P<label>[A-Z]{2,3})\s*</button>"#,
|
||||
)?;
|
||||
let mut episodes = regex
|
||||
.captures_iter(html)
|
||||
.filter_map(|captures| {
|
||||
let label = captures.name("label")?.as_str().trim().to_string();
|
||||
let film_id = captures.name("film")?.as_str().trim().to_string();
|
||||
let episode_id = captures.name("episode")?.as_str().trim().to_string();
|
||||
Some(PlayerEpisode {
|
||||
label,
|
||||
film_id,
|
||||
episode_id,
|
||||
})
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
episodes.sort_by_key(|episode| {
|
||||
if episode.label == "DD" {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
}
|
||||
});
|
||||
episodes.dedup();
|
||||
|
||||
Ok(episodes)
|
||||
}
|
||||
|
||||
fn parse_player_iframe_url(response: &str) -> Option<String> {
|
||||
let regex = Regex::new(r#"(?is)<iframe[^>]+src="(?P<url>https://[^"]+)""#).ok()?;
|
||||
|
||||
let player_html = serde_json::from_str::<serde_json::Value>(response)
|
||||
.ok()
|
||||
.and_then(|value| value.get("player").and_then(|player| player.as_str()).map(str::to_string));
|
||||
|
||||
player_html
|
||||
.as_deref()
|
||||
.and_then(|html| regex.captures(html))
|
||||
.or_else(|| regex.captures(response))
|
||||
.and_then(|captures| captures.name("url"))
|
||||
.map(|value| value.as_str().trim().to_string())
|
||||
}
|
||||
|
||||
async fn resolve_proxy_video_url(
|
||||
&self,
|
||||
seed_url: &str,
|
||||
options: &ServerOptions,
|
||||
) -> Option<String> {
|
||||
let mut requester = requester_or_default(options, CHANNEL_ID, "resolve_proxy_video_url");
|
||||
let html = requester.get(seed_url, Some(Version::HTTP_2)).await.ok()?;
|
||||
let episodes = Self::parse_player_episodes(&html).ok()?;
|
||||
|
||||
for episode in episodes {
|
||||
let ajax_url = format!(
|
||||
"{}/ajax/player?episode={}&filmId={}",
|
||||
self.url, episode.episode_id, episode.film_id
|
||||
);
|
||||
let Some(response) = requester.get(&ajax_url, Some(Version::HTTP_2)).await.ok() else {
|
||||
continue;
|
||||
};
|
||||
let Some(iframe_url) = Self::parse_player_iframe_url(&response) else {
|
||||
continue;
|
||||
};
|
||||
if proxy_name_for_url(&iframe_url).is_some() {
|
||||
return Some(rewrite_hoster_url(options, &iframe_url));
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
fn apply_detail_metadata(&self, seed: ListItemSeed, detail: DetailMetadata) -> VideoItem {
|
||||
let mut item = VideoItem::new(
|
||||
seed.id,
|
||||
@@ -979,7 +1060,7 @@ impl SextbProvider {
|
||||
)
|
||||
.await;
|
||||
|
||||
match detail_fetch {
|
||||
let mut item = match detail_fetch {
|
||||
Ok(Ok(markdown)) => match Self::parse_detail(&markdown) {
|
||||
Ok(detail) => self.apply_detail_metadata(seed.clone(), detail),
|
||||
Err(error) => {
|
||||
@@ -995,7 +1076,22 @@ impl SextbProvider {
|
||||
report_provider_error_background(CHANNEL_ID, "fetch_detail_timeout", &seed.url);
|
||||
self.apply_detail_metadata(seed, DetailMetadata::default())
|
||||
}
|
||||
};
|
||||
|
||||
match timeout(
|
||||
StdDuration::from_secs(10),
|
||||
self.resolve_proxy_video_url(&item.url, options),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(Some(proxy_url)) => item.url = proxy_url,
|
||||
Ok(None) => {}
|
||||
Err(_) => {
|
||||
report_provider_error_background(CHANNEL_ID, "resolve_proxy_video_url_timeout", &item.url);
|
||||
}
|
||||
}
|
||||
|
||||
item
|
||||
}
|
||||
|
||||
async fn fetch_items_for_url(
|
||||
@@ -1207,4 +1303,34 @@ mod tests {
|
||||
assert!(detail.release_date.is_some());
|
||||
assert!(detail.added_at.is_some());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_dd_player_buttons_from_detail_html() {
|
||||
let html = r#"
|
||||
<div class="episode-list">
|
||||
<button class="btn-player episode active" data-source="16900830" data-id="3708319"><i class="fa fa-play-circle"></i> TB</button>
|
||||
<button class="btn-player episode" data-source="16900830" data-id="3708330"><i class="fa fa-play-circle"></i> SW</button>
|
||||
<button class="btn-player episode" data-source="16900830" data-id="3708339"><i class="fa fa-play-circle"></i> DD</button>
|
||||
</div>
|
||||
"#;
|
||||
|
||||
let episodes = SextbProvider::parse_player_episodes(html).expect("episodes should parse");
|
||||
assert_eq!(
|
||||
episodes.first(),
|
||||
Some(&PlayerEpisode {
|
||||
label: "DD".to_string(),
|
||||
film_id: "16900830".to_string(),
|
||||
episode_id: "3708339".to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_iframe_url_from_ajax_player_response() {
|
||||
let response = r#"{"player":"<iframe src=\"https://trailerhg.xyz/e/ttdc7a6qpskt\" width=\"100%\" height=\"100%\" frameborder=\"0\" allowfullscreen=\"true\"></iframe>"}"#;
|
||||
assert_eq!(
|
||||
SextbProvider::parse_player_iframe_url(response).as_deref(),
|
||||
Some("https://trailerhg.xyz/e/ttdc7a6qpskt")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user