mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-20 06:42:20 +02:00
Fix/hls rewrite css fixes (#685)
* hls uri rewrite fixed * source editor canvas pan delete icon fix * css-fixes * added epg smart match form to input * staged input fix * Disable wasm compression
This commit is contained in:
@@ -498,11 +498,8 @@ async fn playlist_download_from_input(
|
||||
let mut save_status = false;
|
||||
if hybrid {
|
||||
if needs_m3u_download {
|
||||
input_cache::update_cluster_status(
|
||||
&mut status,
|
||||
"default",
|
||||
if m3u_error_count == 0 { ClusterState::Ok } else { ClusterState::Failed },
|
||||
);
|
||||
let m3u_state = if m3u_error_count == 0 { ClusterState::Ok } else { ClusterState::Failed };
|
||||
input_cache::update_cluster_status(&mut status, "default", m3u_state);
|
||||
save_status = true;
|
||||
}
|
||||
|
||||
@@ -528,6 +525,9 @@ async fn playlist_download_from_input(
|
||||
input_cache::update_cluster_status(&mut status, &cluster.to_string(), ClusterState::Failed);
|
||||
}
|
||||
save_status = !xtream_clusters_to_download.is_empty();
|
||||
} else {
|
||||
input_cache::update_cluster_status(&mut status, "default", ClusterState::Failed);
|
||||
save_status = true;
|
||||
}
|
||||
|
||||
if save_status {
|
||||
@@ -687,6 +687,30 @@ async fn download_input_epg(
|
||||
tvguide
|
||||
}
|
||||
|
||||
/// `invalidate_input_cache_status` performs a non-atomic file I/O sequence
|
||||
/// (`input_cache::load_input_status` + `input_cache::save_input_status`).
|
||||
/// Call this only while holding the per-input lock from
|
||||
/// `PlaylistProcessingContext::get_input_lock` (as done in `download_input`).
|
||||
async fn invalidate_input_cache_status(ctx: &PlaylistProcessingContext, input: &ConfigInput) {
|
||||
let storage_dir = { ctx.config.config.load().storage_dir.clone() };
|
||||
let storage_path = input_cache::resolve_input_storage_path(&storage_dir, &input.name).await;
|
||||
let mut status = input_cache::load_input_status(&storage_path);
|
||||
if !status.clusters.is_empty() {
|
||||
status.clusters.clear();
|
||||
input_cache::save_input_status(&storage_path, &status);
|
||||
}
|
||||
}
|
||||
|
||||
async fn load_cached_input_playlist(
|
||||
ctx: &PlaylistProcessingContext,
|
||||
input: &Arc<ConfigInput>,
|
||||
) -> (Box<dyn PlaylistSource>, Option<TuliproxError>) {
|
||||
match load_input_playlist(ctx, input, None).await {
|
||||
Ok(pl_source) => (pl_source, None),
|
||||
Err(err) => (MemoryPlaylistSource::default().boxed(), Some(err)),
|
||||
}
|
||||
}
|
||||
|
||||
async fn download_input(
|
||||
ctx: &PlaylistProcessingContext,
|
||||
input: &Arc<ConfigInput>,
|
||||
@@ -695,10 +719,10 @@ async fn download_input(
|
||||
let need_download = !ctx.is_input_downloaded(&input.name).await;
|
||||
// Keep this lock for the whole critical section (download + persist/load + mark processed)
|
||||
// so parallel sources sharing the same input cannot observe a half-written state.
|
||||
let _input_lock = if need_download { Some(ctx.get_input_lock(&input.name).await) } else { None };
|
||||
let mut input_lock = if need_download { Some(ctx.get_input_lock(&input.name).await) } else { None };
|
||||
let mut mark_as_processed = false;
|
||||
|
||||
let playlist_download_result = if need_download {
|
||||
let mut playlist_download_result = if need_download {
|
||||
// Check again after lock
|
||||
let already_processed = ctx.is_input_downloaded(&input.name).await;
|
||||
|
||||
@@ -718,7 +742,50 @@ async fn download_input(
|
||||
PlaylistDownloadResult::new(vec![], vec![], true, false)
|
||||
};
|
||||
|
||||
let (playlist, error) = if playlist_download_result.was_cached || playlist_download_result.persisted {
|
||||
let mut preloaded_playlist: Option<(Box<dyn PlaylistSource>, Option<TuliproxError>)> = None;
|
||||
if playlist_download_result.was_cached {
|
||||
let (cached_playlist, cached_error) = load_cached_input_playlist(ctx, input).await;
|
||||
// Defensive fallback: if cache metadata says "valid" but persisted data is unreadable,
|
||||
// retry once before forcing a refresh.
|
||||
let must_force_refresh = cached_error.is_some();
|
||||
if must_force_refresh {
|
||||
warn!(
|
||||
"Input '{}' cache hit produced unreadable playlist; retrying cached load once",
|
||||
input.name
|
||||
);
|
||||
let (retry_playlist, retry_error) = load_cached_input_playlist(ctx, input).await;
|
||||
if retry_error.is_none() {
|
||||
preloaded_playlist = Some((retry_playlist, None));
|
||||
} else {
|
||||
if input_lock.is_none() {
|
||||
input_lock = Some(ctx.get_input_lock(&input.name).await);
|
||||
}
|
||||
// Re-check immediately after locking to avoid duplicate refreshes when another worker
|
||||
// repaired the cache between our earlier retry and lock acquisition.
|
||||
let (locked_retry_playlist, locked_retry_error) = load_cached_input_playlist(ctx, input).await;
|
||||
if locked_retry_error.is_none() {
|
||||
warn!(
|
||||
"Input '{}' cache became readable after lock re-check; skipping refresh",
|
||||
input.name
|
||||
);
|
||||
preloaded_playlist = Some((locked_retry_playlist, None));
|
||||
} else {
|
||||
warn!(
|
||||
"Input '{}' cached playlist remained unreadable after retry and lock re-check; invalidating cache and forcing refresh",
|
||||
input.name
|
||||
);
|
||||
invalidate_input_cache_status(ctx, input).await;
|
||||
playlist_download_result = playlist_download_from_input(&ctx.client, &ctx.config, input).await;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
preloaded_playlist = Some((cached_playlist, None));
|
||||
}
|
||||
}
|
||||
|
||||
let (playlist, error) = if let Some(preloaded) = preloaded_playlist {
|
||||
preloaded
|
||||
} else if playlist_download_result.was_cached || playlist_download_result.persisted {
|
||||
match load_input_playlist(ctx, input, None).await {
|
||||
Ok(pl_source) => (pl_source, None),
|
||||
Err(e) => (MemoryPlaylistSource::default().boxed(), Some(e)),
|
||||
@@ -736,6 +803,9 @@ async fn download_input(
|
||||
ctx.mark_input_downloaded(input.name.clone()).await;
|
||||
}
|
||||
|
||||
// Explicitly release per-input lock after load/persist/mark steps are completed.
|
||||
drop(input_lock);
|
||||
|
||||
(playlist_download_result.download_err, playlist, error)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user