From 9ae71b99d5b56ced67afb6af73c7e1d4f48201a2 Mon Sep 17 00:00:00 2001 From: euzu Date: Fri, 19 Sep 2025 12:15:24 +0200 Subject: [PATCH] Fixed SeriesInfo episode data-type problem, Added accept_unsecure_ssl_certificate to config.yml --- README.md | 1 + .../src/api/model/active_provider_manager.rs | 38 ++++++------- backend/src/model/config/base.rs | 3 + backend/src/model/xtream.rs | 6 +- backend/src/processing/parser/xtream.rs | 55 ++++++++++++++++++- backend/src/utils/network/request.rs | 5 +- frontend/public/assets/i18n/en.json | 1 + .../app/components/config/main_config_view.rs | 4 ++ shared/src/model/config/base.rs | 8 +++ 9 files changed, 95 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 19a54fd09..ca9c7d0d8 100644 --- a/README.md +++ b/README.md @@ -90,6 +90,7 @@ Top level entries in the config files are: * `ipcheck` _optional_ * `config_hot_reload` _optional_, default false. * `sleep_timer_mins` _optional_, used for closing stream after the given minutes. +* `accept_unsecure_ssl_certificates` _optional_, default false. ### 1.1. `threads` If you are running on a cpu which has multiple cores, you can set for example `threads: 2` to run two threads. diff --git a/backend/src/api/model/active_provider_manager.rs b/backend/src/api/model/active_provider_manager.rs index 3cbd68063..9c7efdd0e 100644 --- a/backend/src/api/model/active_provider_manager.rs +++ b/backend/src/api/model/active_provider_manager.rs @@ -934,6 +934,9 @@ mod tests { } } + fn dummy_callback(_: &str, _: usize) {} + + // Test acquiring with an alias #[test] fn test_provider_with_alias() { @@ -943,10 +946,10 @@ mod tests { // Adding alias to the provider input.aliases = Some(vec![alias]); - let (change_tx, _) = tokio::sync::mpsc::channel::<(String, usize)>(1); + let change_callback: ProviderConnectionChangeCallback = Arc::new(dummy_callback); let dummy_get_connection = |_s: &str| -> Option<&ProviderConfigConnection> { None }; // Create MultiProviderLineup with the provider and alias - let lineup = MultiProviderLineup::new(&input, Some(dummy_get_connection), &change_tx); + let lineup = MultiProviderLineup::new(&input, Some(dummy_get_connection), &change_callback); let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async move { // Test that the alias provider is available @@ -968,9 +971,9 @@ mod tests { let alias = create_config_input_alias(2, "http://alias.com", 0, 2); // Adding alias with different priority input.aliases = Some(vec![alias]); - let (change_tx, _) = tokio::sync::mpsc::channel::<(String, usize)>(1); + let change_callback: ProviderConnectionChangeCallback = Arc::new(dummy_callback); let dummy_get_connection = |_s: &str| -> Option<&ProviderConfigConnection> { None }; - let lineup = MultiProviderLineup::new(&input, Some(dummy_get_connection), &change_tx); + let lineup = MultiProviderLineup::new(&input, Some(dummy_get_connection), &change_callback); // The alias has a higher priority, so the alias should be acquired first let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async move { @@ -990,9 +993,9 @@ mod tests { // Adding multiple aliases input.aliases = Some(vec![alias1, alias2]); - let (change_tx, _) = tokio::sync::mpsc::channel::<(String, usize)>(1); + let change_callback: ProviderConnectionChangeCallback = Arc::new(dummy_callback); let dummy_get_connection = |_s: &str| -> Option<&ProviderConfigConnection> { None }; - let lineup = MultiProviderLineup::new(&input, Some(dummy_get_connection), &change_tx); + let lineup = MultiProviderLineup::new(&input, Some(dummy_get_connection), &change_callback); let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async move { // The alias with priority 0 should be acquired first (higher priority) @@ -1021,9 +1024,9 @@ mod tests { // Adding alias input.aliases = Some(vec![alias1, alias2]); - let (change_tx, _) = tokio::sync::mpsc::channel::<(String, usize)>(1); + let change_callback: ProviderConnectionChangeCallback = Arc::new(dummy_callback); let dummy_get_connection = |_s: &str| -> Option<&ProviderConfigConnection> { None }; - let lineup = MultiProviderLineup::new(&input, Some(dummy_get_connection), &change_tx); + let lineup = MultiProviderLineup::new(&input, Some(dummy_get_connection), &change_callback); let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async move { // Acquire connection from alias2 @@ -1049,10 +1052,9 @@ mod tests { #[test] fn test_acquire_when_capacity_available() { let cfg = create_config_input(1, "provider5_1", 1, 2); - let on_connection_change = move |_name, _connections| {}; - let change_tx = Arc::new(on_connection_change); + let change_callback: ProviderConnectionChangeCallback = Arc::new(dummy_callback); let dummy_get_connection = |_s: &str| -> Option<&ProviderConfigConnection> { None }; - let lineup = SingleProviderLineup::new(&cfg, Some(dummy_get_connection), &change_tx); + let lineup = SingleProviderLineup::new(&cfg, Some(dummy_get_connection), &change_callback); let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async move { // First acquire attempt should succeed @@ -1071,11 +1073,10 @@ mod tests { #[test] fn test_release_connection() { let cfg = create_config_input(1, "provider7_1", 1, 2); - let on_connection_change = move |_name, _connections| {}; - let change_tx = Arc::new(on_connection_change); + let change_callback: ProviderConnectionChangeCallback = Arc::new(dummy_callback); let dummy_get_connection = |_s: &str| -> Option<&ProviderConfigConnection> { None }; - let lineup = SingleProviderLineup::new(&cfg, Some(dummy_get_connection), &change_tx); + let lineup = SingleProviderLineup::new(&cfg, Some(dummy_get_connection), &change_callback); let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async move { // Acquire two connections @@ -1103,9 +1104,9 @@ mod tests { cfg1.aliases = Some(vec![alias]); // Create MultiProviderLineup with the provider and alias - let (change_tx, _) = tokio::sync::mpsc::channel::<(String, usize)>(1); + let change_callback: ProviderConnectionChangeCallback = Arc::new(dummy_callback); let dummy_get_connection = |_s: &str| -> Option<&ProviderConfigConnection> { None }; - let lineup = MultiProviderLineup::new(&cfg1, Some(dummy_get_connection), &change_tx); + let lineup = MultiProviderLineup::new(&cfg1, Some(dummy_get_connection), &change_callback); let rt = tokio::runtime::Runtime::new().unwrap(); rt.block_on(async move { // Test acquiring the first provider @@ -1136,10 +1137,9 @@ mod tests { #[test] fn test_concurrent_acquire() { let cfg = create_config_input(1, "provider9_1", 1, 2); - let on_connection_change = move |_name, _connections| {}; - let change_tx = Arc::new(on_connection_change); + let change_callback: ProviderConnectionChangeCallback = Arc::new(dummy_callback); let dummy_get_connection = |_s: &str| -> Option<&ProviderConfigConnection> { None }; - let lineup = Arc::new(SingleProviderLineup::new(&cfg, Some(dummy_get_connection), &change_tx)); + let lineup = Arc::new(SingleProviderLineup::new(&cfg, Some(dummy_get_connection), &change_callback)); let available_count = Arc::new(AtomicU16::new(2)); let grace_period_count = Arc::new(AtomicU16::new(1)); diff --git a/backend/src/model/config/base.rs b/backend/src/model/config/base.rs index 53e49a31e..502a05dbd 100644 --- a/backend/src/model/config/base.rs +++ b/backend/src/model/config/base.rs @@ -40,6 +40,7 @@ fn create_directories(cfg: &Config, temp_path: &Path) { } } +#[allow(clippy::struct_excessive_bools)] #[derive(Debug, Clone, Default)] pub struct Config { pub threads: u8, @@ -57,6 +58,7 @@ pub struct Config { pub sleep_timer_mins: Option, pub update_on_boot: bool, pub config_hot_reload: bool, + pub accept_unsecure_ssl_certificates: bool, pub web_ui: Option, pub messaging: Option, pub reverse_proxy: Option, @@ -137,6 +139,7 @@ impl From<&ConfigDto> for Config { sleep_timer_mins: dto.sleep_timer_mins, update_on_boot: dto.update_on_boot, config_hot_reload: dto.config_hot_reload, + accept_unsecure_ssl_certificates: dto.accept_unsecure_ssl_certificates, web_ui: dto.web_ui.as_ref().map(Into::into), messaging: dto.messaging.as_ref().map(Into::into), reverse_proxy: dto.reverse_proxy.as_ref().map(Into::into), diff --git a/backend/src/model/xtream.rs b/backend/src/model/xtream.rs index d265dff1c..5dcf707ee 100644 --- a/backend/src/model/xtream.rs +++ b/backend/src/model/xtream.rs @@ -277,8 +277,8 @@ pub struct XtreamSeriesInfoEpisodeInfo { // Used for serde_json deserialization, can not be used with bincode #[derive(Debug, Clone, Serialize, Deserialize)] pub struct XtreamSeriesInfoEpisode { - #[serde(default, deserialize_with = "string_default_on_null")] - pub id: String, + #[serde(default, deserialize_with = "string_or_number_u32")] + pub id: u32, #[serde(default, deserialize_with = "string_or_number_u32")] pub episode_num: u32, #[serde(default, deserialize_with = "string_default_on_null")] @@ -299,7 +299,7 @@ pub struct XtreamSeriesInfoEpisode { impl XtreamSeriesInfoEpisode { pub fn get_id(&self) -> u32 { - self.id.parse::().unwrap_or(0) + self.id } } diff --git a/backend/src/processing/parser/xtream.rs b/backend/src/processing/parser/xtream.rs index 94876329e..b4d078596 100644 --- a/backend/src/processing/parser/xtream.rs +++ b/backend/src/processing/parser/xtream.rs @@ -49,8 +49,8 @@ pub fn parse_xtream_series_info(info: &Value, group_title: &str, series_name: &s (episode.clone(), PlaylistItem { header: PlaylistItemHeader { - id: episode.id.clone(), - uuid: generate_playlist_uuid(&input.name, &episode.id, PlaylistItemType::Series, &episode_url), + id: episode.id.to_string(), + uuid: generate_playlist_uuid(&input.name, &episode.id.to_string(), PlaylistItemType::Series, &episode_url), name: series_name.to_string(), logo: episode.info.as_ref().map_or_else(String::new, |info| info.movie_image.clone()), group: group_title.to_string(), @@ -189,3 +189,54 @@ pub fn parse_xtream(input: &ConfigInput, Err(err) => Err(err) } } + +#[cfg(test)] +mod tests { + use std::error::Error; + use std::fs; + use reqwest::blocking::{Client, get, ClientBuilder}; + use crate::model::XtreamSeriesInfo; + + #[test] + fn test_read_json_file_into_struct() { + let file_content = fs::read_to_string("series-info.json").expect("Unable to read file"); + let _info: XtreamSeriesInfo = serde_json::from_str(&file_content).expect("JSON was not well-formatted"); + + } + + fn fetch_image(url: &str) -> Result<(), reqwest::Error> { + let client = ClientBuilder::new().danger_accept_invalid_certs(true).build()?; + + let result = client.get(url) + .header(reqwest::header::USER_AGENT, "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36") + .send(); + if let Err(e) = result { + let mut err: &dyn Error = &e; + while let Some(src) = err.source() { + let _ = println!("\n\nCaused by: {}", src); + err = src; + } + return Err(e); + } + + if let Ok(response) = result { + if response.status().is_success() { + println!("Erfolgreich abgerufen!"); + } else { + println!("Fehler: {}", response.status()); + } + + } + + Ok(()) + } + + #[test] + fn test_image() { + + let url = "https://www.mxgp.com/sites/default/files/news/image/Screenshot%202025-02-27%20at%2011.41.53.png"; + if let Err(e) = fetch_image(url) { + println!("Fehler beim Abrufen: {}", e); + } + } +} \ No newline at end of file diff --git a/backend/src/utils/network/request.rs b/backend/src/utils/network/request.rs index 5e041053a..1344f8997 100644 --- a/backend/src/utils/network/request.rs +++ b/backend/src/utils/network/request.rs @@ -395,12 +395,13 @@ pub async fn get_input_json_content(client: Arc, input: &InputS pub fn create_client(cfg: &AppConfig) -> reqwest::ClientBuilder { + let config = cfg.config.load(); let mut client = reqwest::Client::builder() .redirect(reqwest::redirect::Policy::limited(10)) .pool_idle_timeout(Duration::from_secs(30)) - .pool_max_idle_per_host(10); + .pool_max_idle_per_host(10) + .danger_accept_invalid_certs(config.accept_unsecure_ssl_certificates); - let config = cfg.config.load(); if let Some(proxy_cfg) = config.proxy.as_ref() { match Url::parse(&proxy_cfg.url) { diff --git a/frontend/public/assets/i18n/en.json b/frontend/public/assets/i18n/en.json index 0dd01a66e..91edeec52 100644 --- a/frontend/public/assets/i18n/en.json +++ b/frontend/public/assets/i18n/en.json @@ -206,6 +206,7 @@ "SLEEP_TIMER_MINS": "Sleep Timer (mins)", "CONNECT_TIMEOUT_SECS": "Connect Timeout (secs)", "CONFIG_HOT_RELOAD": "Config Hot Reload", + "ACCEPT_UNSECURE_SSL_CERTIFICATES": "Accept Unsecure SSL Certificates", "CUSTOM_STREAM_RESPONSE_PATH": "Custom Stream Response dir", "REVERSE_PROXY": "Reverse Proxy", "WEB_SEARCH": "Web Search", diff --git a/frontend/src/app/components/config/main_config_view.rs b/frontend/src/app/components/config/main_config_view.rs index a728b4784..807c2558d 100644 --- a/frontend/src/app/components/config/main_config_view.rs +++ b/frontend/src/app/components/config/main_config_view.rs @@ -18,6 +18,7 @@ const LABEL_USER_CONFIG_DIR: &str = "LABEL.USER_CONFIG_DIR"; const LABEL_SLEEP_TIMER_MINS: &str = "LABEL.SLEEP_TIMER_MINS"; const LABEL_CONNECT_TIMEOUT_SECS: &str = "LABEL.CONNECT_TIMEOUT_SECS"; const LABEL_CUSTOM_STREAM_RESPONSE_PATH: &str = "LABEL.CUSTOM_STREAM_RESPONSE_PATH"; +const LABEL_ACCEPT_UNSECURE_SSL_CERTIFICATES: &str = "LABEL.ACCEPT_UNSECURE_SSL_CERTIFICATES"; generate_form_reducer!( state: MainConfigFormState { form: MainConfigDto }, @@ -26,6 +27,7 @@ generate_form_reducer!( UpdateOnBoot => update_on_boot: bool, ConfigHotReload => config_hot_reload: bool, UserAccessControl => user_access_control: bool, + AcceptUnsecureSslCertificates => accept_unsecure_ssl_certificates: bool, Threads => threads: u8, WorkingDir => working_dir: String, MappingPath => mapping_path: Option, @@ -75,6 +77,7 @@ pub fn MainConfigView() -> Html { { config_field_bool!(form_state.form, translate.t(LABEL_UPDATE_ON_BOOT), update_on_boot) } { config_field_bool!(form_state.form, translate.t(LABEL_CONFIG_HOT_RELOAD), config_hot_reload) } { config_field_bool!(form_state.form, translate.t(LABEL_USER_ACCESS_CONTROL), user_access_control) } + { config_field_bool!(form_state.form, translate.t(LABEL_ACCEPT_UNSECURE_SSL_CERTIFICATES), accept_unsecure_ssl_certificates) } { config_field!(form_state.form, translate.t(LABEL_THREADS), threads) } { config_field!(form_state.form, translate.t(LABEL_WORKING_DIR), working_dir) } { config_field_optional!(form_state.form, translate.t(LABEL_MAPPING_PATH), mapping_path) } @@ -92,6 +95,7 @@ pub fn MainConfigView() -> Html { { edit_field_bool!(form_state, translate.t(LABEL_UPDATE_ON_BOOT), update_on_boot, MainConfigFormAction::UpdateOnBoot) } { edit_field_bool!(form_state, translate.t(LABEL_CONFIG_HOT_RELOAD), config_hot_reload, MainConfigFormAction::ConfigHotReload) } { edit_field_bool!(form_state, translate.t(LABEL_USER_ACCESS_CONTROL), user_access_control, MainConfigFormAction::UserAccessControl) } + { edit_field_bool!(form_state, translate.t(LABEL_ACCEPT_UNSECURE_SSL_CERTIFICATES), user_access_control, MainConfigFormAction::AcceptUnsecureSslCertificates) } { edit_field_number_u8!(form_state, translate.t(LABEL_THREADS), threads, MainConfigFormAction::Threads) } { edit_field_text!(form_state, translate.t(LABEL_WORKING_DIR), working_dir, MainConfigFormAction::WorkingDir) } { edit_field_text_option!(form_state, translate.t(LABEL_MAPPING_PATH), mapping_path, MainConfigFormAction::MappingPath) } diff --git a/shared/src/model/config/base.rs b/shared/src/model/config/base.rs index 2c9787de1..42214be2a 100644 --- a/shared/src/model/config/base.rs +++ b/shared/src/model/config/base.rs @@ -37,6 +37,8 @@ pub struct ConfigDto { #[serde(default)] pub config_hot_reload: bool, #[serde(default)] + pub accept_unsecure_ssl_certificates: bool, + #[serde(default)] pub web_ui: Option, #[serde(default, skip_serializing_if = "Option::is_none")] pub messaging: Option, @@ -75,6 +77,8 @@ pub struct MainConfigDto { pub update_on_boot: bool, #[serde(default)] pub config_hot_reload: bool, + #[serde(default)] + pub accept_unsecure_ssl_certificates: bool, } impl Default for MainConfigDto { @@ -91,6 +95,7 @@ impl Default for MainConfigDto { sleep_timer_mins: None, update_on_boot: false, config_hot_reload: false, + accept_unsecure_ssl_certificates: false, } } } @@ -109,6 +114,7 @@ impl From<&ConfigDto> for MainConfigDto { sleep_timer_mins: config.sleep_timer_mins, update_on_boot: config.update_on_boot, config_hot_reload: config.config_hot_reload, + accept_unsecure_ssl_certificates: config.accept_unsecure_ssl_certificates, } } } @@ -242,5 +248,7 @@ impl ConfigDto { self.sleep_timer_mins = main_config.sleep_timer_mins; self.update_on_boot = main_config.update_on_boot; self.config_hot_reload = main_config.config_hot_reload; + self.accept_unsecure_ssl_certificates = main_config.accept_unsecure_ssl_certificates; + } } \ No newline at end of file