rewrite resource urls for webui (#629)

* rewrite resource urls for webui
* fix for dbviewer
* added metadata update new field
This commit is contained in:
euzu
2026-03-05 00:04:53 +01:00
committed by GitHub
parent 342b399ec8
commit 36dcd0ff12
48 changed files with 1230 additions and 674 deletions
+11
View File
@@ -14,7 +14,9 @@ pub use self::xtream::*;
pub use self::xtream_vod::*;
pub use self::xtream_series::*;
pub use self::stream_probe::*;
use crate::api::model::ProviderHandle;
use shared::create_bitset;
use tokio_util::sync::CancellationToken;
create_bitset!(u8, ResolveOptionsFlags, Resolve, TmdbMissing, Probe, Background);
@@ -47,6 +49,15 @@ pub(crate) const FOREGROUND_BATCH_SIZE: usize = 200;
pub(crate) const FOREGROUND_RETRY_BATCH_MAX_SIZE: usize = FOREGROUND_BATCH_SIZE * 4;
pub(crate) const FOREGROUND_MIN_RETRY_DELAY_SECS: u64 = 1;
pub(crate) fn select_cancel_token<'a>(
acquired_handle: Option<&'a ProviderHandle>,
active_handle: Option<&'a ProviderHandle>,
) -> Option<&'a CancellationToken> {
acquired_handle
.and_then(|h| h.cancel_token.as_ref())
.or_else(|| active_handle.and_then(|h| h.cancel_token.as_ref()))
}
//
// fn get_resolve_<cluster>_options(target: &ConfigTarget, fpl: &FetchedPlaylist) -> bool
@@ -1,6 +1,7 @@
use crate::api::model::ActiveProviderManager;
use crate::model::ConfigInput;
use crate::model::{AppConfig};
use crate::processing::processor::select_cancel_token;
use crate::repository::{get_input_m3u_playlist_file_path, get_input_storage_path, get_input_local_library_playlist_file_path, xtream_get_file_path, BPlusTreeUpdate};
use crate::utils::{debug_if_enabled, ffmpeg};
use crate::utils::ffmpeg::{ProbeFailureKind, ProbeUrlOutcome};
@@ -115,14 +116,17 @@ pub async fn update_generic_stream_metadata(
debug_if_enabled!("Probing Generic Stream '{unique_id}'");
let probe_data = ffmpeg::probe_url(
let cancel_token = select_cancel_token(acquired_handle.as_ref(), active_handle);
let probe_data = ffmpeg::probe_url_with_cancel(
&probe_url,
user_agent.as_deref(),
analyze_duration,
probe_size,
ffprobe_timeout,
config.proxy.as_ref(),
).await;
cancel_token,
)
.await;
if let Some(handle) = acquired_handle {
active_provider.release_handle(&handle).await;
@@ -138,6 +142,10 @@ pub async fn update_generic_stream_metadata(
warn!("Probe failed or timed out for generic stream: {unique_id}");
return Ok(GenericProbeOutcome::ProbeFailed);
}
ProbeUrlOutcome::Failed(ProbeFailureKind::Cancelled) => {
warn!("Probe cancelled for generic stream: {unique_id}");
return Ok(GenericProbeOutcome::ProbeFailed);
}
};
// Hold the async file lock while the blocking DB update runs in a blocking thread.
@@ -164,6 +164,10 @@ pub async fn update_live_stream_metadata(
warn!("Probe failed for Live Stream ID {} (Input: {})", display_id, input.name);
// We still persist the updated last_probed_timestamp so we don't retry immediately
}
ProbeUrlOutcome::Failed(ProbeFailureKind::Cancelled) => {
warn!("Probe cancelled for Live Stream ID {} (Input: {})", display_id, input.name);
// We still persist the updated last_probed_timestamp so we don't retry immediately
}
}
// 4. Persist
@@ -8,8 +8,8 @@ use crate::processing::parser::xtream::create_xtream_series_episode_url;
use crate::processing::parser::xtream::parse_xtream_series_info;
use crate::processing::processor::playlist::{PlaylistProcessingContext, ProcessingPipe};
use crate::processing::processor::{
create_resolve_options_function_for_xtream_target, process_foreground_retry_once, ResolveOptions,
ResolveOptionsFlags, FOREGROUND_BATCH_SIZE as BATCH_SIZE, FOREGROUND_MIN_RETRY_DELAY_SECS,
create_resolve_options_function_for_xtream_target, process_foreground_retry_once, select_cancel_token,
ResolveOptions, ResolveOptionsFlags, FOREGROUND_BATCH_SIZE as BATCH_SIZE, FOREGROUND_MIN_RETRY_DELAY_SECS,
FOREGROUND_RETRY_BATCH_MAX_SIZE as RETRY_BATCH_MAX_SIZE,
};
use crate::ptt::ptt_parse_title;
@@ -30,6 +30,7 @@ use shared::model::{
};
use shared::model::{PlaylistGroup, PlaylistItemType, XtreamCluster};
use std::collections::{HashMap, HashSet};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
@@ -524,6 +525,7 @@ async fn update_series_info_immediate(
reasons.contains(ResolveReason::Probe),
probe_settings,
db_query,
None,
)
.await
}
@@ -620,6 +622,7 @@ pub async fn update_series_metadata(
do_probe: bool,
probe_settings: SeriesProbeSettings,
db_query: Option<Arc<Mutex<BPlusTreeQuery<u32, XtreamPlaylistItem>>>>,
tmdb_and_date_present_out: Option<&AtomicBool>,
) -> Result<Option<SeriesStreamProperties>, TuliproxError> {
let working_dir = &app_config.config.load().working_dir;
let storage_path = get_input_storage_path(&input.name, working_dir)
@@ -818,6 +821,13 @@ pub async fn update_series_metadata(
}
}
// Report whether TMDB and release date are present (regardless of whether we updated them this call).
if let Some(out) = tmdb_and_date_present_out {
let has_tmdb = properties.tmdb.is_some();
let has_date = properties.release_date.is_some();
out.store(has_tmdb && has_date, Ordering::Relaxed);
}
// 3. Probe Episodes (if enabled)
if do_probe && app_config.is_ffprobe_enabled().await {
if let Some(details) = properties.details.as_mut() {
@@ -863,13 +873,15 @@ pub async fn update_series_metadata(
ep.title, ep.season, ep.episode_num, missing_reason
);
match crate::utils::ffmpeg::probe_url(
let cancel_token = select_cancel_token(temp_handle.as_ref(), active_handle);
match crate::utils::ffmpeg::probe_url_with_cancel(
&episode_url,
user_agent.as_deref(),
probe_settings.analyze_duration_micros,
probe_settings.probe_size_bytes,
probe_settings.timeout_secs,
config.proxy.as_ref(),
cancel_token,
)
.await
{
@@ -892,6 +904,11 @@ pub async fn update_series_metadata(
probe_failure = Some(ProbeFailureKind::Other);
}
}
ProbeUrlOutcome::Failed(ProbeFailureKind::Cancelled) => {
if probe_failure.is_none() {
probe_failure = Some(ProbeFailureKind::Cancelled);
}
}
}
if let Some(h) = temp_handle {
@@ -923,6 +940,7 @@ pub async fn update_series_metadata(
shared::error::info_err!("Probe failed with 404 Not Found for Series {display_id}")
}
ProbeFailureKind::Other => shared::error::info_err!("Probe failed for Series {display_id}"),
ProbeFailureKind::Cancelled => shared::error::info_err!("Probe cancelled for Series {display_id}"),
};
return Err(err);
}
+25 -3
View File
@@ -7,8 +7,8 @@ use crate::model::{AppConfig, ConfigTarget};
use crate::model::{ConfigInput, ConfigInputFlags};
use crate::processing::processor::playlist::PlaylistProcessingContext;
use crate::processing::processor::{
create_resolve_options_function_for_xtream_target, process_foreground_retry_once, ResolveOptions,
ResolveOptionsFlags, FOREGROUND_BATCH_SIZE as BATCH_SIZE, FOREGROUND_MIN_RETRY_DELAY_SECS,
create_resolve_options_function_for_xtream_target, process_foreground_retry_once, select_cancel_token,
ResolveOptions, ResolveOptionsFlags, FOREGROUND_BATCH_SIZE as BATCH_SIZE, FOREGROUND_MIN_RETRY_DELAY_SECS,
FOREGROUND_RETRY_BATCH_MAX_SIZE as RETRY_BATCH_MAX_SIZE,
};
use crate::ptt::ptt_parse_title;
@@ -27,6 +27,7 @@ use shared::model::{
VideoStreamProperties, XtreamCluster, XtreamPlaylistItem, XtreamVideoInfo,
};
use std::collections::{HashMap, HashSet};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::Arc;
use std::time::{Duration, Instant};
@@ -479,6 +480,7 @@ async fn update_vod_info_immediate(
resolve_tmdb,
reasons.contains(ResolveReason::Probe),
db_query,
None,
)
.await
}
@@ -505,6 +507,7 @@ pub async fn update_vod_metadata(
resolve_tmdb: bool,
do_probe: bool,
db_query: Option<Arc<Mutex<BPlusTreeQuery<u32, XtreamPlaylistItem>>>>,
tmdb_resolved_out: Option<&AtomicBool>,
) -> Result<Option<VideoStreamProperties>, TuliproxError> {
let working_dir = &app_config.config.load().working_dir;
let storage_path = get_input_storage_path(&input.name, working_dir)
@@ -747,6 +750,17 @@ pub async fn update_vod_metadata(
}
}
// Report whether TMDB data is present (regardless of whether we updated it this call).
if let Some(out) = tmdb_resolved_out {
let has_tmdb = properties.tmdb.is_some();
let has_date = properties
.details
.as_ref()
.and_then(|d| d.release_date.as_ref())
.is_some();
out.store(has_tmdb && has_date, Ordering::Relaxed);
}
// 3. Probe (if enabled globally in config)
let ffprobe_enabled = app_config.is_ffprobe_enabled().await;
if do_probe && ffprobe_enabled {
@@ -793,13 +807,15 @@ pub async fn update_vod_metadata(
if active_handle.is_some() || temp_handle.is_some() {
debug_if_enabled!("Probing VOD '{}' (ID: {})", display_title, display_id);
match crate::utils::ffmpeg::probe_url(
let cancel_token = select_cancel_token(temp_handle.as_ref(), active_handle);
match crate::utils::ffmpeg::probe_url_with_cancel(
&stream_url,
user_agent.as_deref(),
analyze_duration,
probe_size,
ffprobe_timeout,
config.proxy.as_ref(),
cancel_token,
)
.await
{
@@ -823,6 +839,11 @@ pub async fn update_vod_metadata(
probe_failure = Some(ProbeFailureKind::Other);
}
}
ProbeUrlOutcome::Failed(ProbeFailureKind::Cancelled) => {
if probe_failure.is_none() {
probe_failure = Some(ProbeFailureKind::Cancelled);
}
}
}
if let Some(h) = temp_handle {
active_provider.release_handle(&h).await;
@@ -841,6 +862,7 @@ pub async fn update_vod_metadata(
shared::error::info_err!("Probe failed with 404 Not Found for VOD {display_id}")
}
ProbeFailureKind::Other => shared::error::info_err!("Probe failed for VOD {display_id}"),
ProbeFailureKind::Cancelled => shared::error::info_err!("Probe cancelled for VOD {display_id}"),
};
return Err(err);
}