diff --git a/backend/src/processing/parser/m3u.rs b/backend/src/processing/parser/m3u.rs index 33efaf6b3..60e234c7f 100644 --- a/backend/src/processing/parser/m3u.rs +++ b/backend/src/processing/parser/m3u.rs @@ -1,7 +1,7 @@ -use std::borrow::BorrowMut; use crate::model::{Config, ConfigInput}; -use shared::model::{PlaylistItemType, XtreamCluster, PlaylistGroup, PlaylistItem, PlaylistItemHeader}; -use shared::utils::{extract_id_from_url}; +use shared::model::{PlaylistGroup, PlaylistItem, PlaylistItemHeader, PlaylistItemType, XtreamCluster}; +use shared::utils::extract_id_from_url; +use std::borrow::BorrowMut; // other implementations like calculating text_distance on all titles took too much time @@ -110,9 +110,10 @@ macro_rules! process_header_fields { fn process_header(input_name: &str, video_suffixes: &[&str], content: &str, url: &str) -> PlaylistItemHeader { let mut plih = create_empty_playlistitem_header(input_name, url); let mut it = content.chars(); - let mut stack = String::with_capacity(64); + let mut stack = String::with_capacity(64); let line_token = token_till(&mut stack, &mut it, ':', false); if line_token.as_deref() == Some("#EXTINF") { + let mut provider_id = None::; let mut c = skip_digit(&mut it); loop { if c.is_none() { @@ -128,17 +129,24 @@ fn process_header(input_name: &str, video_suffixes: &[&str], content: &str, url: let token = token_till(&mut stack, &mut it, '=', true); if let Some(t) = token { let value = token_value(&mut stack, &mut it); - process_header_fields!(plih, t.to_lowercase().as_str(), - (id, "tvg-id"), - (group, "group-title"), - (name, "tvg-name"), - (chno, "tvg-chno"), - (parent_code, "parent-code"), - (audio_track, "audio-track"), - (logo, "tvg-logo"), - (logo_small, "tvg-logo-small"), - (time_shift, "timeshift"), - (rec, "tvg-rec"); value); + let token = t.to_lowercase(); + if token.as_str() == "xui-id" { + if !value.is_empty() { + provider_id = Some(value); + } + } else { + process_header_fields!(plih, token.as_str(), + (id, "tvg-id"), + (group, "group-title"), + (name, "tvg-name"), + (chno, "tvg-chno"), + (parent_code, "parent-code"), + (audio_track, "audio-track"), + (logo, "tvg-logo"), + (logo_small, "tvg-logo-small"), + (time_shift, "timeshift"), + (rec, "tvg-rec"); value); + } } } c = it.next(); @@ -146,11 +154,16 @@ fn process_header(input_name: &str, video_suffixes: &[&str], content: &str, url: if plih.id.is_empty() { plih.epg_channel_id = None; - if let Some(chanid) = extract_id_from_url(url) { + if let Some(pid) = provider_id { + plih.id = pid; + } else if let Some(chanid) = extract_id_from_url(url) { plih.id = chanid; } } else { plih.epg_channel_id = Some(plih.id.to_string()); + if let Some(pid) = provider_id { + plih.id = pid; + } } } @@ -260,7 +273,7 @@ mod test { let input: &str = "hello"; let video_suffixes = Vec::new(); let url = "http://hello.de/hello.ts"; - let line = r#"#EXTINF:-1 channel-id="abc-seven" tvg-id="abc-seven" tvg-logo="https://abc.nz/.images/seven.png" tvg-chno="7" group-title="Sydney" , Seven"#; + let line = r#"#EXTINF:-1 channel-id="abc-seven" tvg-id="abc-seven" tvg-logo="https://abc.nz/.images/seven.png" tvg-chno="7" group-title="Sydney" , Seven"#; let pli = process_header(input, &video_suffixes, line, url); assert_eq!(pli.title, "Seven"); @@ -275,7 +288,7 @@ mod test { let input: &str = "hello"; let video_suffixes = Vec::new(); let url = "http://hello.de/hello.ts"; - let line = r#"#EXTINF:-1 channel-id="abc-seven" tvg-id="abc-seven" tvg-logo="https://abc.nz/.images/seven.png" tvg-chno="7" group-title="Sydney", Seven"#; + let line = r#"#EXTINF:-1 channel-id="abc-seven" tvg-id="abc-seven" tvg-logo="https://abc.nz/.images/seven.png" tvg-chno="7" group-title="Sydney", Seven"#; let pli = process_header(input, &video_suffixes, line, url); assert_eq!(pli.title, "Seven"); diff --git a/shared/src/model/config/input.rs b/shared/src/model/config/input.rs index 109e7416f..08d0381ab 100644 --- a/shared/src/model/config/input.rs +++ b/shared/src/model/config/input.rs @@ -1,12 +1,12 @@ +use crate::error::{TuliproxError, TuliproxErrorKind}; +use crate::model::EpgConfigDto; +use crate::utils::{default_as_true, get_base_url_from_str, get_credentials_from_url_str, get_trimmed_string, sanitize_sensitive_info, trim_last_slash}; +use crate::{check_input_credentials, create_tuliprox_error_result, handle_tuliprox_error_result_list, info_err}; +use enum_iterator::Sequence; +use log::debug; use std::collections::{HashMap, HashSet}; use std::fmt::Display; use std::str::FromStr; -use enum_iterator::Sequence; -use crate::{check_input_credentials, create_tuliprox_error_result, handle_tuliprox_error_result_list, info_err}; -use crate::error::{TuliproxError, TuliproxErrorKind}; -use crate::model::{EpgConfigDto}; -use crate::utils::{default_as_true, get_base_url_from_str, get_credentials_from_url_str, get_trimmed_string, sanitize_sensitive_info, trim_last_slash}; -use log::debug; #[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Sequence, @@ -60,7 +60,17 @@ impl FromStr for InputType { } } -#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Eq, Default)] +#[derive( + Debug, + Copy, + Clone, + serde::Serialize, + serde::Deserialize, + Sequence, + PartialEq, + Eq, + Default +)] pub enum InputFetchMethod { #[default] GET, @@ -129,8 +139,8 @@ pub struct ConfigInputAliasDto { } impl ConfigInputAliasDto { - pub fn prepare(&mut self, index: u16, input_type: &InputType) -> Result<(), TuliproxError> { - self.id = index; + pub fn prepare(&mut self, index: u16, input_type: &InputType) -> Result { + self.id = index + 1; self.name = self.name.trim().to_string(); if self.name.is_empty() { return Err(info_err!("name for input is mandatory".to_string())); @@ -143,7 +153,7 @@ impl ConfigInputAliasDto { self.password = get_trimmed_string(&self.password); check_input_credentials!(self, input_type, true); - Ok(()) + Ok(self.id) } } @@ -183,7 +193,8 @@ pub struct ConfigInputDto { impl ConfigInputDto { #[allow(clippy::cast_possible_truncation)] pub fn prepare(&mut self, index: u16, include_computed: bool) -> Result { - self.id = index; + let mut current_index = index + 1; + self.id = current_index; self.check_url()?; self.name = self.name.trim().to_string(); @@ -232,9 +243,16 @@ impl ConfigInputDto { if let Some(aliases) = self.aliases.as_mut() { let input_type = &self.input_type; - handle_tuliprox_error_result_list!(TuliproxErrorKind::Info, aliases.iter_mut().enumerate().map(|(idx, i)| i.prepare(index+1+(idx as u16), input_type))); + handle_tuliprox_error_result_list!(TuliproxErrorKind::Info, aliases.iter_mut() + .map(|i| match i.prepare(current_index, input_type) { + Ok(new_idx) => { + current_index = new_idx; + Ok(()) + }, + Err(err) => Err(err) + })); } - Ok(index + self.aliases.as_ref().map_or(0, std::vec::Vec::len) as u16) + Ok(current_index) } fn check_url(&mut self) -> Result<(), TuliproxError> { @@ -244,6 +262,4 @@ impl ConfigInputDto { } Ok(()) } - - } \ No newline at end of file diff --git a/shared/src/model/config/sort.rs b/shared/src/model/config/sort.rs index 5713b23fd..30a152363 100644 --- a/shared/src/model/config/sort.rs +++ b/shared/src/model/config/sort.rs @@ -31,7 +31,7 @@ impl Display for SortOrder { SortOrder::Asc => "asc".to_string(), SortOrder::Desc => "desc".to_string(), }; - write!(f, "{}", str) + write!(f, "{str}") } } diff --git a/shared/src/model/config/source.rs b/shared/src/model/config/source.rs index c49370d28..fb80d1f80 100644 --- a/shared/src/model/config/source.rs +++ b/shared/src/model/config/source.rs @@ -16,9 +16,18 @@ pub struct ConfigSourceDto { impl ConfigSourceDto { #[allow(clippy::cast_possible_truncation)] pub fn prepare(&mut self, index: u16, include_computed: bool) -> Result { - handle_tuliprox_error_result_list!(TuliproxErrorKind::Info, self.inputs.iter_mut().enumerate() - .map(|(idx, i)| i.prepare(index+(idx as u16), include_computed))); - Ok(index + (self.inputs.len() as u16)) + let mut current_index = index; + handle_tuliprox_error_result_list!(TuliproxErrorKind::Info, self.inputs.iter_mut() + .map(|i| + match i.prepare(current_index, include_computed) { + Ok(new_idx) => { + current_index = new_idx; + Ok(()) + }, + Err(err) => Err(err) + } + )); + Ok(current_index) } } @@ -40,7 +49,7 @@ impl SourcesConfigDto { fn prepare_sources(&mut self, include_computed: bool) -> Result<(), TuliproxError> { // prepare sources and set id's - let mut source_index: u16 = 1; + let mut source_index: u16 = 0; let mut target_index: u16 = 1; for source in &mut self.sources { source_index = source.prepare(source_index, include_computed)?; diff --git a/shared/src/utils/string_utils.rs b/shared/src/utils/string_utils.rs index 051501913..1b64c3e21 100644 --- a/shared/src/utils/string_utils.rs +++ b/shared/src/utils/string_utils.rs @@ -76,10 +76,11 @@ pub fn trim_slash(s: &str) -> Cow { pub fn trim_last_slash(s: &str) -> Cow { if s.ends_with('/') { - Cow::Owned(s[..s.len() - 1].to_string()) - } else { - Cow::Borrowed(s) + if let Some(stripped) = s.strip_suffix('/') { + return Cow::Owned(stripped.to_string()) + } } + Cow::Borrowed(s) } #[cfg(test)] diff --git a/webui/src/app/components/card.rs b/webui/src/app/components/card.rs index b0ae43b7b..ec4a85ab5 100644 --- a/webui/src/app/components/card.rs +++ b/webui/src/app/components/card.rs @@ -10,7 +10,7 @@ pub struct CardProps { #[function_component] pub fn Card(props: &CardProps) -> Html { - let custom_class = use_state(|| String::new()); + let custom_class = use_state(String::new); let context = CardContext { custom_class: custom_class.clone(), }; diff --git a/webui/src/app/components/chip.rs b/webui/src/app/components/chip.rs index 2330241db..da756f94f 100644 --- a/webui/src/app/components/chip.rs +++ b/webui/src/app/components/chip.rs @@ -13,7 +13,7 @@ pub struct ChipProps { pub class: Option, #[prop_or(false)] pub removable: bool, - #[prop_or_else(|| Callback::noop())] + #[prop_or_else(Callback::noop)] pub on_remove: Callback, } diff --git a/webui/src/app/components/dashboard/ipinfo_action_card.rs b/webui/src/app/components/dashboard/ipinfo_action_card.rs index 85089f917..fdf98b235 100644 --- a/webui/src/app/components/dashboard/ipinfo_action_card.rs +++ b/webui/src/app/components/dashboard/ipinfo_action_card.rs @@ -57,11 +57,7 @@ pub fn IpinfoActionCard() -> Html { services_ctx.config.config_subscribe( &mut |cfg| { let exists = if let Some(app_cfg) = &cfg { - if app_cfg.config.ipcheck.is_some() { - true - } else { - false - } + app_cfg.config.ipcheck.is_some() } else { false }; diff --git a/webui/src/app/components/dashboard/status_card.rs b/webui/src/app/components/dashboard/status_card.rs index 469564f98..99d9032ae 100644 --- a/webui/src/app/components/dashboard/status_card.rs +++ b/webui/src/app/components/dashboard/status_card.rs @@ -22,7 +22,7 @@ pub fn StatusCard(props: &StatusCardProps) -> Html {
{props.data.clone()}
- diff --git a/webui/src/app/components/dashboard/user_action_card.rs b/webui/src/app/components/dashboard/user_action_card.rs index 38d9617eb..49240c3ab 100644 --- a/webui/src/app/components/dashboard/user_action_card.rs +++ b/webui/src/app/components/dashboard/user_action_card.rs @@ -10,7 +10,7 @@ use crate::hooks::use_service_context; pub fn UserActionCard() -> Html { let services = use_service_context(); let translate = use_translation(); - let username = use_state(|| String::new()); + let username = use_state(String::new); let handle_logout = { let services_ctx = services.clone(); @@ -32,7 +32,7 @@ pub fn UserActionCard() -> Html { html! { + subtitle={(*username).clone()}> } diff --git a/webui/src/app/components/mod.rs b/webui/src/app/components/mod.rs index 626e272f3..a61410c17 100644 --- a/webui/src/app/components/mod.rs +++ b/webui/src/app/components/mod.rs @@ -42,9 +42,9 @@ pub use playlist::playlist_view::*; pub use self::breadcrumbs::*; pub use self::playlist::*; pub use self::table::*; -pub use self::toggle_switch::*; +// pub use self::toggle_switch::*; pub use self::chip::*; pub use self::tag_list::*; -pub use self::popup_menu::*; +// pub use self::popup_menu::*; pub use self::confirm_dialog::*; pub use self::content_dialog::*; diff --git a/webui/src/app/components/playlist/assistant/mod.rs b/webui/src/app/components/playlist/assistant/mod.rs index 3a580a239..8b8b692d6 100644 --- a/webui/src/app/components/playlist/assistant/mod.rs +++ b/webui/src/app/components/playlist/assistant/mod.rs @@ -5,8 +5,8 @@ mod step_type; mod step_schedule; pub use self::playlist_assistant_context::*; -pub use self::playlist::*; +// pub use self::playlist::*; pub use self::step_name::*; pub use self::step_type::*; -pub use self::step_schedule::*; +// pub use self::step_schedule::*; diff --git a/webui/src/app/components/playlist/assistant/playlist.rs b/webui/src/app/components/playlist/assistant/playlist.rs index e608a3b08..b28f82cc8 100644 --- a/webui/src/app/components/playlist/assistant/playlist.rs +++ b/webui/src/app/components/playlist/assistant/playlist.rs @@ -41,7 +41,7 @@ impl FromStr for PlaylistAssistantStep { pub fn PlaylistAssistant() -> Html { let active_step = use_state(|| PlaylistAssistantStep::Name); - let custom_class = use_state(|| String::new()); + let custom_class = use_state(String::new); let context = PlaylistAssistantContext { custom_class: custom_class.clone(), }; diff --git a/webui/src/app/components/playlist/target/target_options.rs b/webui/src/app/components/playlist/target/target_options.rs index a6496ee79..dee24d359 100644 --- a/webui/src/app/components/playlist/target/target_options.rs +++ b/webui/src/app/components/playlist/target/target_options.rs @@ -69,8 +69,8 @@ pub fn TargetOptions(props: &TargetOptionsProps) -> Html { }); let has_options = tags.0; - let opts: Vec> = (&tags.1).clone(); - let redirect: Vec> = (&tags.2).clone(); + let opts: Vec> = (tags.1).clone(); + let redirect: Vec> = (tags.2).clone(); html! { Html { 3 => html! { }, 4 => html! { }, 5 => dto.sort.as_ref().map_or_else(|| html! {}, |_s| html! { }), - 6 => dto.t_filter.as_ref().map_or_else(|| html! {}, |f| html! { }}> }), - 7 => dto.rename.as_ref().map_or_else(|| html! {}, |_r| html! { }), + 6 => dto.t_filter.as_ref().map_or_else(|| html! {}, |f| html! { })}> }), + 7 => dto.rename.as_ref().map_or_else(|| html! {}, |_r| html! { }), 8 => html! { }, 9 => html! { }, 10 => html! { }, diff --git a/webui/src/app/components/popup_menu.rs b/webui/src/app/components/popup_menu.rs index 85c0ed042..e4946aa5e 100644 --- a/webui/src/app/components/popup_menu.rs +++ b/webui/src/app/components/popup_menu.rs @@ -44,10 +44,7 @@ pub fn PopupMenu(props: &PopupMenuProps) -> Html { top = rect.top() - 150.0; } - format!( - "position: fixed; top: {}px; left: {}px;", - top, left - ) + format!("position: fixed; top: {top}px; left: {left}px;") }) }; diff --git a/webui/src/app/components/reveal_content.rs b/webui/src/app/components/reveal_content.rs index e1dfbc879..2990a4250 100644 --- a/webui/src/app/components/reveal_content.rs +++ b/webui/src/app/components/reveal_content.rs @@ -1,4 +1,3 @@ -use wasm_bindgen::JsCast; use yew::platform::spawn_local; use yew::prelude::*; use crate::app::components::AppIcon; @@ -10,7 +9,7 @@ pub struct RevealContentProps { #[prop_or_default] pub icon: String, #[prop_or_default] - pub preview: Html, + pub preview: Option, pub children: Html, #[prop_or_default] pub actions: Option @@ -37,8 +36,16 @@ pub fn RevealContent(props: &RevealContentProps) -> Html { html! {
- {props.preview.clone()} - + { + match props.preview.as_ref() { + None => html! {}, + Some(preview) => html! { + {preview.clone()} + } + } + } + +
} } \ No newline at end of file diff --git a/webui/src/app/components/tag_list.rs b/webui/src/app/components/tag_list.rs index 7f215d47f..8a3f73dbe 100644 --- a/webui/src/app/components/tag_list.rs +++ b/webui/src/app/components/tag_list.rs @@ -14,8 +14,8 @@ pub struct TagListProps { pub tags: Vec>, #[prop_or_else(Callback::noop)] pub on_change: Callback>>, - #[prop_or(false)] - pub removable: bool, + // #[prop_or(false)] + // pub removable: bool, #[prop_or(false)] pub allow_add: bool, } @@ -25,7 +25,7 @@ pub fn tag_list(props: &TagListProps) -> Html { let TagListProps { tags, on_change, - removable, + // removable, allow_add, } = props.clone(); @@ -73,7 +73,7 @@ pub fn tag_list(props: &TagListProps) -> Html { html! {
{ for (*tag_state).iter().map(|tag| html! { - + }) } { diff --git a/webui/src/services/dialog_service.rs b/webui/src/services/dialog_service.rs index 60444107b..d06f502dc 100644 --- a/webui/src/services/dialog_service.rs +++ b/webui/src/services/dialog_service.rs @@ -46,20 +46,20 @@ impl Future for DialogFuture { } } } - +type DialogResultCallback = Box; #[derive(Clone)] pub struct ConfirmRequest { pub title: String, pub ok_caption: String, pub cancel_caption: String, - pub resolve: Rc>>>, + pub resolve: Rc>>, } #[derive(Clone)] pub struct ContentRequest { pub content: Html, pub actions: DialogActions, - pub resolve: Rc>>>, + pub resolve: Rc>>, } #[derive(Clone)] diff --git a/webui/src/services/playlist_service.rs b/webui/src/services/playlist_service.rs index ae0e24a0a..52b05c8d5 100644 --- a/webui/src/services/playlist_service.rs +++ b/webui/src/services/playlist_service.rs @@ -1,16 +1,18 @@ -use crate::services::{request_post}; -use std::rc::Rc; +use crate::services::request_post; use log::error; const TARGET_UPDATE_API_PATH: &str = "/api/v1/playlist/update"; -pub struct PlaylistService { +pub struct PlaylistService {} +impl Default for PlaylistService { + fn default() -> Self { + Self::new() + } } impl PlaylistService { pub fn new() -> Self { - Self { - } + Self {} } pub async fn update_targets(&self, targets: &[&str]) -> bool { diff --git a/webui/src/services/status_service.rs b/webui/src/services/status_service.rs index b11a1cd8a..e73af91aa 100644 --- a/webui/src/services/status_service.rs +++ b/webui/src/services/status_service.rs @@ -1,20 +1,23 @@ +use crate::services::request_get; use shared::model::StatusCheck; -use crate::services::{request_get}; const STATUS_PATH: &str = "/api/v1/status"; -pub struct StatusService { +pub struct StatusService {} + +impl Default for StatusService { + fn default() -> Self { + Self::new() + } } impl StatusService { pub fn new() -> Self { - Self { - } + Self {} } pub async fn get_server_status(&self) -> Result { request_get::(STATUS_PATH).await } - } \ No newline at end of file