diff --git a/backend/src/processing/processor/playlist.rs b/backend/src/processing/processor/playlist.rs index 7bbcf1508..64d490f42 100644 --- a/backend/src/processing/processor/playlist.rs +++ b/backend/src/processing/processor/playlist.rs @@ -267,6 +267,16 @@ async fn process_source(client: &reqwest::Client, app_config: Arc, so let mut target_stats = Vec::::new(); if let Some(source) = sources.get_source_at(source_idx) { let mut source_playlists = Vec::with_capacity(128); + + let broadcast_step = { + let event_manager = event_manager.clone(); + move |context: &str, msg: &str| { + if let Some(events) = &event_manager { + events.send_event(EventMessage::PlaylistUpdateProgress(context.to_owned(), msg.to_owned())); + } + } + }; + // Download the sources let mut source_downloaded = false; for input in &source.inputs { @@ -275,9 +285,12 @@ async fn process_source(client: &reqwest::Client, app_config: Arc, so let start_time = Instant::now(); // Download playlist for input let (playlistgroups, mut error_list) = { + broadcast_step("Playlist download", &format!("Downloading input '{}'", input.name)); let (downloaded_playlist, mut download_err) = playlist_download_from_input(client, &app_config, input).await; + broadcast_step("Playlist download", &format!("Persisting input '{}' playlist", input.name)); let (playlist, error) = persist_input_playlist(&app_config, input, downloaded_playlist).await; if let Some(err) = error { + broadcast_step("Playlist download", &format!("Failed to persist input '{}' playlist", input.name)); error!("Failed to persist input playlist {}", input.name); download_err.push(err); } @@ -286,6 +299,7 @@ async fn process_source(client: &reqwest::Client, app_config: Arc, so // Download epg for input let (tvguide, mut tvguide_errors) = if error_list.is_empty() { + broadcast_step("Playlist download", &format!("Downloading epg for input '{}'", input.name)); let working_dir = &app_config.config.load().working_dir; epg::get_xmltv(client, input, working_dir).await } else { @@ -298,6 +312,7 @@ async fn process_source(client: &reqwest::Client, app_config: Arc, so let channel_count = playlistgroups.iter().map(|group| group.channels.len()).sum(); let input_name = &input.name; if playlistgroups.is_empty() { + broadcast_step("Playlist download", &format!("Input '{}' playlist is empty", input.name)); info!("Source is empty {input_name}"); errors.push(notify_err!(format!("Source is empty {input_name}"))); } else { @@ -513,7 +528,7 @@ async fn process_playlist_for_target(app_config: &Arc, let mut step = StepMeasure::new(&target.name, broadcast_step); for provider_fpl in playlists.iter_mut() { let mut processed_fpl = execute_pipe(target, &pipe, provider_fpl, &mut duplicates); - playlist_resolve_series(app_config, client, target, errors, &pipe, provider_fpl, &mut processed_fpl).await; + playlist_resolve_series(app_config, client, target, errors, &pipe, provider_fpl, &mut processed_fpl, &step).await; playlist_resolve_vod(app_config, client, target, errors, &mut processed_fpl).await; // stats let input_stats = stats.get_mut(&processed_fpl.input.name); @@ -531,7 +546,6 @@ async fn process_playlist_for_target(app_config: &Arc, process_favourites(&mut new_playlist, target.favourites.as_deref()); - if new_playlist.is_empty() { step.stop(""); info!("Playlist is empty: {}", &target.name); diff --git a/backend/src/processing/processor/xtream_series.rs b/backend/src/processing/processor/xtream_series.rs index b77442d77..510725675 100644 --- a/backend/src/processing/processor/xtream_series.rs +++ b/backend/src/processing/processor/xtream_series.rs @@ -12,6 +12,7 @@ use shared::model::{InputType, PlaylistEntry, SeriesStreamProperties, StreamProp use shared::model::{PlaylistGroup, PlaylistItemType, XtreamCluster}; use std::sync::Arc; use std::time::Instant; +use crate::utils::StepMeasure; create_resolve_options_function_for_xtream_target!(series); @@ -101,12 +102,15 @@ pub async fn playlist_resolve_series(cfg: &Arc, pipe: &ProcessingPipe, provider_fpl: &mut FetchedPlaylist<'_>, processed_fpl: &mut FetchedPlaylist<'_>, + step: &StepMeasure, ) { let (resolve_series, resolve_delay) = get_resolve_series_options(target, processed_fpl); if !resolve_series { return; } - + step.broadcast("Playlist resolve series info", "Resolving series info"); let series_playlist = playlist_resolve_series_info(cfg, client, errors, processed_fpl, resolve_delay).await; if series_playlist.is_empty() { return; } + + step.broadcast("Playlist resolve series info", "Series info resolved, updating playlist"); // original content saved into original list for plg in &series_playlist { provider_fpl.update_playlist(plg); diff --git a/backend/src/processing/processor/xtream_vod.rs b/backend/src/processing/processor/xtream_vod.rs index 4a9bf2a91..b637c1a88 100644 --- a/backend/src/processing/processor/xtream_vod.rs +++ b/backend/src/processing/processor/xtream_vod.rs @@ -10,10 +10,14 @@ use shared::model::{InputType, PlaylistEntry, StreamProperties, VideoStreamPrope use shared::model::{PlaylistItemType, XtreamCluster}; use std::sync::Arc; use std::time::Instant; +use crate::utils::StepMeasure; create_resolve_options_function_for_xtream_target!(vod); -pub async fn playlist_resolve_vod(app_config: &Arc, client: &reqwest::Client, target: &ConfigTarget, errors: &mut Vec, fpl: &mut FetchedPlaylist<'_>) { +pub async fn playlist_resolve_vod(app_config: &Arc, client: &reqwest::Client, + target: &ConfigTarget, errors: &mut Vec, + fpl: &mut FetchedPlaylist<'_>, + step: &StepMeasure,) { let (resolve_movies, resolve_delay) = get_resolve_vod_options(target, fpl); if !resolve_movies { return; } diff --git a/backend/src/repository/bplustree.rs b/backend/src/repository/bplustree.rs index 42a2d222d..8d6a58c40 100644 --- a/backend/src/repository/bplustree.rs +++ b/backend/src/repository/bplustree.rs @@ -1815,8 +1815,8 @@ where self.leaf_values = values; self.leaf_idx = 0; } - Err(err) => { - error!("BPlusTreeDiskIterator Failed to read next entry: {err}"); + Err(_err) => { + // error!("BPlusTreeDiskIterator Failed to read next entry: {err}"); return None; } _ => return None, diff --git a/backend/src/utils/step_measure.rs b/backend/src/utils/step_measure.rs index 63d23ab3d..23b7008e6 100644 --- a/backend/src/utils/step_measure.rs +++ b/backend/src/utils/step_measure.rs @@ -17,7 +17,7 @@ fn format_duration(duration: Duration) -> String { } } -type StepMeasureCallback = Box; +pub type StepMeasureCallback = Box; pub struct StepMeasure { enabled: bool, @@ -41,11 +41,15 @@ impl StepMeasure { } } + pub fn broadcast(self, step: &str, msg: &str) { + (self.callback)(step, msg); + } + pub fn tick(&mut self, step: &str) { if self.enabled { let msg = format!("{}: processed {step} in {}", self.name, format_duration(self.step_start.elapsed())); debug!("{msg}"); - (self.callback)(&self.name, &msg); + self.broadcast(&self.name, &msg); self.step_start = Instant::now(); } } @@ -59,8 +63,8 @@ impl StepMeasure { let fmsg = format!("{}: finished in {}", self.name, format_duration( self.start.elapsed())); debug!("{msg}"); debug!("{fmsg}"); - (self.callback)(&self.name, &msg); - (self.callback)(&self.name, &fmsg); + self.broadcast(&self.name, &msg); + self.broadcast(&self.name, &fmsg); } self.enabled = false; } diff --git a/frontend/scss/app/components/playlist/_playlist_explorer.scss b/frontend/scss/app/components/playlist/_playlist_explorer.scss index 7051293d6..8b5a6d0af 100644 --- a/frontend/scss/app/components/playlist/_playlist_explorer.scss +++ b/frontend/scss/app/components/playlist/_playlist_explorer.scss @@ -17,6 +17,7 @@ &__header { display: flex; flex-flow: column; + &-toolbar { display: flex; flex-flow: row; @@ -27,6 +28,7 @@ .tp__icon-button { width: 2.5rem; height: 2.5rem; + .svg-icon { height: 100%; width: 100%; @@ -49,6 +51,7 @@ box-sizing: border-box; overflow: hidden; } + &__group, &__categories { display: flex; @@ -66,6 +69,7 @@ width: 100%; } } + &__item { display: flex; justify-content: flex-start; @@ -84,13 +88,26 @@ overflow: hidden; text-overflow: ellipsis; - img, .svg-icon { box-sizing: border-box; height: 100%; aspect-ratio: 1 / 1; } + &-label { + display: flex; + justify-content: flex-start; + align-items: center; + cursor: pointer; + box-sizing: border-box; + max-height: 3rem; + min-height: 2.5rem; + gap: var(--gap-default); + padding: 0 var(--padding-default); + overflow: hidden; + text-overflow: ellipsis; + } + &:hover { background-color: var(--text-button-hover-background-color); color: var(--text-button-hover-color); @@ -101,16 +118,19 @@ background-color: var(--tag-live-background-color); border: 1px solid var(--tag-live-border-color); } + &-video:before { content: "V"; background-color: var(--tag-video-background-color); border: 1px solid var(--tag-video-border-color); } + &-series:before { content: "S"; background-color: var(--tag-series-background-color); border: 1px solid var(--tag-series-border-color); } + &-live:before, &-video:before, &-series:before { @@ -125,6 +145,13 @@ &__channel { padding-left: var(--padding-micro); + + &-logo { + box-sizing: border-box; + max-height: 3rem; + min-height: 2.5rem; + aspect-ratio: 1 / 1; + } } } \ No newline at end of file diff --git a/frontend/src/app/components/playlist/playlist_explorer.rs b/frontend/src/app/components/playlist/playlist_explorer.rs index 6a9554495..9f2677710 100644 --- a/frontend/src/app/components/playlist/playlist_explorer.rs +++ b/frontend/src/app/components/playlist/playlist_explorer.rs @@ -288,7 +288,7 @@ pub fn PlaylistExplorer() -> Html { if logo.is_empty() { html! {} } else { - html! { () {