diff --git a/Cargo.lock b/Cargo.lock index 901cdfc46..bea55808c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2581,6 +2581,7 @@ name = "shared" version = "0.1.0" dependencies = [ "bitflags 2.9.1", + "enum-iterator", "rand", "regex", "serde", diff --git a/backend/src/api/config_watch.rs b/backend/src/api/config_watch.rs index 403419910..fce37decd 100644 --- a/backend/src/api/config_watch.rs +++ b/backend/src/api/config_watch.rs @@ -1,5 +1,5 @@ use crate::api::model::app_state::AppState; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::{TuliproxError, TuliproxErrorKind}; use crate::utils; use crate::utils::is_directory; use log::{debug, error, info}; diff --git a/backend/src/api/endpoints/download_api.rs b/backend/src/api/endpoints/download_api.rs index 8561581b3..d58146e8e 100644 --- a/backend/src/api/endpoints/download_api.rs +++ b/backend/src/api/endpoints/download_api.rs @@ -13,7 +13,7 @@ use std::sync::Arc; use std::{fs}; use axum::response::IntoResponse; use shared::utils::bytes_to_megabytes; -use crate::tuliprox_error::to_io_error; +use shared::error::to_io_error; use crate::utils::request::create_client; async fn download_file(active: Arc>>, client: &reqwest::Client) -> Result<(), String> { diff --git a/backend/src/api/endpoints/v1_api.rs b/backend/src/api/endpoints/v1_api.rs index 6857f9364..f9c7e417b 100644 --- a/backend/src/api/endpoints/v1_api.rs +++ b/backend/src/api/endpoints/v1_api.rs @@ -6,7 +6,7 @@ use crate::api::model::config::{ServerConfig, ServerInputConfig, ServerSourceCon use crate::api::model::request::{PlaylistRequest, PlaylistRequestType}; use crate::auth::create_access_token; use crate::auth::validator_admin; -use crate::tuliprox_error::TuliproxError; +use shared::error::TuliproxError; use crate::model::StatusCheck; use crate::model::XtreamPlaylistItem; use crate::model::{Config, ConfigDto, ConfigInput, ConfigInputOptions, ConfigSource, ConfigTarget, InputType}; diff --git a/backend/src/api/endpoints/xtream_api.rs b/backend/src/api/endpoints/xtream_api.rs index 4a5f710c1..b1107e8c1 100644 --- a/backend/src/api/endpoints/xtream_api.rs +++ b/backend/src/api/endpoints/xtream_api.rs @@ -16,9 +16,9 @@ use crate::model::{ProxyType, ProxyUserCredentials, UserConnectionPermission}; use crate::repository::playlist_repository::get_target_id_mapping; use crate::repository::storage::{get_target_storage_path}; use crate::repository::{storage_const, user_repository, xtream_repository}; -use crate::tuliprox_error::create_tuliprox_error_result; -use crate::tuliprox_error::info_err; -use crate::tuliprox_error::{str_to_io_error, TuliproxError, TuliproxErrorKind}; +use shared::error::create_tuliprox_error_result; +use shared::error::info_err; +use shared::error::{str_to_io_error, TuliproxError, TuliproxErrorKind}; use crate::utils::{generate_playlist_uuid, hex_encode}; use crate::utils::get_u32_from_serde_value; use crate::utils::request::{extract_extension_from_url, sanitize_sensitive_info}; diff --git a/backend/src/api/model/config.rs b/backend/src/api/model/config.rs index bbb243701..673f5e81f 100644 --- a/backend/src/api/model/config.rs +++ b/backend/src/api/model/config.rs @@ -1,6 +1,6 @@ use serde::{Deserialize, Serialize}; - -use crate::model::{ApiProxyConfig, ConfigApi, InputType, LogConfig, MessagingConfig, ProcessingOrder, ReverseProxyConfig, ScheduleConfig, VideoConfig, ConfigRename, ConfigSort, ConfigTargetOptions, TargetOutput, WebUiConfig, ProxyConfig, IpCheckConfig}; +use shared::model::ProcessingOrder; +use crate::model::{ApiProxyConfig, ConfigApi, InputType, LogConfig, MessagingConfig, ReverseProxyConfig, ScheduleConfig, VideoConfig, ConfigRename, ConfigSort, ConfigTargetOptions, TargetOutput, WebUiConfig, ProxyConfig, IpCheckConfig}; #[derive(Deserialize, Serialize, Debug, Clone)] pub struct ServerInputConfig { diff --git a/backend/src/auth/authenticator.rs b/backend/src/auth/authenticator.rs index 64c9380c0..be3577c10 100644 --- a/backend/src/auth/authenticator.rs +++ b/backend/src/auth/authenticator.rs @@ -5,7 +5,7 @@ use crate::api::api_utils::get_username_from_auth_header; use crate::model::WebAuthConfig; use crate::api::model::app_state::AppState; use crate::auth::AuthBearer; -use crate::tuliprox_error::to_io_error; +use shared::error::to_io_error; const ROLE_ADMIN: &str = "ADMIN"; const ROLE_USER: &str = "USER"; diff --git a/backend/src/auth/password.rs b/backend/src/auth/password.rs index fea9a30be..6a80906ea 100644 --- a/backend/src/auth/password.rs +++ b/backend/src/auth/password.rs @@ -1,6 +1,6 @@ use rand::{Rng}; use rand::distr::Alphanumeric; -use crate::tuliprox_error::str_to_io_error; +use shared::error::str_to_io_error; fn generate_salt(length: usize) -> String { let salt: String = rand::rng() diff --git a/backend/src/foundation/filter.rs b/backend/src/foundation/filter.rs index 37383fe9a..e809faace 100644 --- a/backend/src/foundation/filter.rs +++ b/backend/src/foundation/filter.rs @@ -8,11 +8,11 @@ use pest::iterators::Pair; use pest::Parser; use std::cmp::Ordering; use std::collections::HashMap; -use shared::model::{FieldGetAccessor, FieldSetAccessor, PlaylistItemType}; -use crate::model::{PlaylistItem, ItemField}; +use shared::model::{FieldGetAccessor, FieldSetAccessor, ItemField, PlaylistItemType}; +use crate::model::{PlaylistItem}; use crate::tools::directed_graph::DirectedGraph; -use crate::tuliprox_error::{create_tuliprox_error_result, info_err}; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error_result, info_err}; +use shared::error::{TuliproxError, TuliproxErrorKind}; use shared::utils::CONSTANTS; pub fn get_field_value(pli: &PlaylistItem, field: ItemField) -> String { diff --git a/backend/src/foundation/mapper.rs b/backend/src/foundation/mapper.rs index ac536bb9f..2e23478ed 100644 --- a/backend/src/foundation/mapper.rs +++ b/backend/src/foundation/mapper.rs @@ -2,7 +2,7 @@ use crate::foundation::filter::{PatternTemplate, TemplateValue, ValueAccessor}; use crate::foundation::mapper::EvalResult::{AnyValue, Failure, Named, Number, Undefined, Value}; -use crate::tuliprox_error::{create_tuliprox_error_result, info_err, TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error_result, info_err, TuliproxError, TuliproxErrorKind}; use shared::utils::Capitalize; use log::{debug, trace}; use pest::iterators::{Pair, Pairs}; diff --git a/backend/src/messaging.rs b/backend/src/messaging.rs index 5b4fb8b5a..6a8e5ddb1 100644 --- a/backend/src/messaging.rs +++ b/backend/src/messaging.rs @@ -2,18 +2,7 @@ use std::sync::Arc; use crate::model::{MessagingConfig}; use log::{debug, error}; use reqwest::{header}; - -#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)] -pub enum MsgKind { - #[serde(rename = "info")] - Info, - #[serde(rename = "stats")] - Stats, - #[serde(rename = "error")] - Error, - #[serde(rename = "watch")] - Watch, -} +use shared::model::MsgKind; fn is_enabled(kind: MsgKind, cfg: &MessagingConfig) -> bool { cfg.notify_on.contains(&kind) diff --git a/backend/src/model/config.rs b/backend/src/model/config.rs index 750c80e03..2e48a793e 100644 --- a/backend/src/model/config.rs +++ b/backend/src/model/config.rs @@ -1,11 +1,8 @@ use arc_swap::{ArcSwapOption}; -use enum_iterator::Sequence; use std::collections::{HashSet}; -use std::fmt::Display; use std::fs::File; use std::io::Read; use std::path::{Path, PathBuf}; -use std::str::FromStr; use std::sync::Arc; use log::{debug, error, warn}; @@ -14,7 +11,7 @@ use rand::Rng; use crate::model::{ApiProxyConfig, ApiProxyServerInfo, Mappings, ProxyUserCredentials, SourcesConfig}; use crate::model::{ConfigInput, ConfigInputOptions, ConfigTarget, HdHomeRunConfig, IpCheckConfig, LogConfig, MessagingConfig, ProxyConfig, TargetOutput, VideoConfig, WebUiConfig}; -use crate::tuliprox_error::{create_tuliprox_error_result, TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error_result, TuliproxError, TuliproxErrorKind}; use shared::utils::{parse_to_kbps, default_connect_timeout_secs, default_grace_period_millis, default_grace_period_timeout_secs}; const STREAM_QUEUE_SIZE: usize = 1024; // mpsc channel holding messages. with 8192byte chunks and 2Mbit/s approx 8MB @@ -43,84 +40,6 @@ use crate::api::model::streams::transport_stream_buffer::TransportStreamBuffer; use crate::model::config_cache::CacheConfig; use crate::utils; -#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Sequence, Eq, PartialEq)] -pub enum ItemField { - #[serde(rename = "group")] - Group, - #[serde(rename = "name")] - Name, - #[serde(rename = "title")] - Title, - #[serde(rename = "url")] - Url, - #[serde(rename = "input")] - Input, - #[serde(rename = "type")] - Type, - #[serde(rename = "caption")] - Caption, -} - -impl ItemField { - const GROUP: &'static str = "Group"; - const NAME: &'static str = "Name"; - const TITLE: &'static str = "Title"; - const URL: &'static str = "Url"; - const INPUT: &'static str = "Input"; - const TYPE: &'static str = "Type"; - const CAPTION: &'static str = "Caption"; - - pub fn as_str(&self) -> &'static str { - match *self { - Self::Group => Self::GROUP, - Self::Name => Self::NAME, - Self::Title => Self::TITLE, - Self::Url => Self::URL, - Self::Input => Self::INPUT, - Self::Type => Self::TYPE, - Self::Caption => Self::CAPTION, - } - } -} - -impl Display for ItemField { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "{}", match *self { - Self::Group => Self::GROUP, - Self::Name => Self::NAME, - Self::Title => Self::TITLE, - Self::Url => Self::URL, - Self::Input => Self::INPUT, - Self::Type => Self::TYPE, - Self::Caption => Self::CAPTION, - }) - } -} - -impl FromStr for ItemField { - type Err = TuliproxError; - - fn from_str(s: &str) -> Result { - if s.eq_ignore_ascii_case(Self::GROUP) { - Ok(Self::Group) - } else if s.eq_ignore_ascii_case(Self::NAME) { - Ok(Self::Name) - } else if s.eq_ignore_ascii_case(Self::TITLE) { - Ok(Self::Title) - } else if s.eq_ignore_ascii_case(Self::CAPTION) { - Ok(Self::Caption) - } else if s.eq_ignore_ascii_case(Self::URL) { - Ok(Self::Url) - } else if s.eq_ignore_ascii_case(Self::INPUT) { - Ok(Self::Input) - } else if s.eq_ignore_ascii_case(Self::TYPE) { - Ok(Self::Type) - } else { - create_tuliprox_error_result!(TuliproxErrorKind::Info, "Unknown InputType: {}", s) - } - } -} - #[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)] pub enum FilterMode { diff --git a/backend/src/model/config_api_proxy.rs b/backend/src/model/config_api_proxy.rs index f25f31f73..29aeae7fc 100644 --- a/backend/src/model/config_api_proxy.rs +++ b/backend/src/model/config_api_proxy.rs @@ -1,5 +1,5 @@ use crate::api::model::app_state::AppState; -use crate::tuliprox_error::{create_tuliprox_error_result, info_err, TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error_result, info_err, TuliproxError, TuliproxErrorKind}; use crate::model::{Config}; use crate::repository::user_repository::{backup_api_user_db_file, get_api_user_db_path, load_api_user, merge_api_user}; use crate::utils::{save_api_proxy}; diff --git a/backend/src/model/config_cache.rs b/backend/src/model/config_cache.rs index 829e3f940..3ddf1195c 100644 --- a/backend/src/model/config_cache.rs +++ b/backend/src/model/config_cache.rs @@ -1,7 +1,7 @@ use std::path::PathBuf; use log::error; use path_clean::PathClean; -use crate::tuliprox_error::{info_err, TuliproxError, TuliproxErrorKind}; +use shared::error::{info_err, TuliproxError, TuliproxErrorKind}; use shared::utils::parse_size_base_2; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] diff --git a/backend/src/model/config_download.rs b/backend/src/model/config_download.rs index 6c7e39a9b..eb072a509 100644 --- a/backend/src/model/config_download.rs +++ b/backend/src/model/config_download.rs @@ -1,4 +1,4 @@ -use crate::tuliprox_error::{create_tuliprox_error_result, TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error_result, TuliproxError, TuliproxErrorKind}; use regex::Regex; use std::borrow::BorrowMut; use std::collections::HashMap; diff --git a/backend/src/model/config_epg.rs b/backend/src/model/config_epg.rs index e4a0677c7..74cbe3043 100644 --- a/backend/src/model/config_epg.rs +++ b/backend/src/model/config_epg.rs @@ -1,4 +1,4 @@ -use crate::tuliprox_error::{create_tuliprox_error_result, info_err, TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error_result, info_err, TuliproxError, TuliproxErrorKind}; use shared::utils::CONSTANTS; use log::warn; use regex::Regex; diff --git a/backend/src/model/config_hdhomerun.rs b/backend/src/model/config_hdhomerun.rs index ad820ac49..f74e65cf5 100644 --- a/backend/src/model/config_hdhomerun.rs +++ b/backend/src/model/config_hdhomerun.rs @@ -1,6 +1,6 @@ use std::collections::HashSet; use log::warn; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error_result}; +use shared::error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error_result}; fn default_friendly_name() -> String { String::from("TuliproxTV") } fn default_manufacturer() -> String { String::from("Silicondust") } fn default_model_name() -> String { String::from("HDTC-2US") } diff --git a/backend/src/model/config_input.rs b/backend/src/model/config_input.rs index 5291c66d1..0cc5db511 100644 --- a/backend/src/model/config_input.rs +++ b/backend/src/model/config_input.rs @@ -1,4 +1,4 @@ -use crate::tuliprox_error::{create_tuliprox_error_result, handle_tuliprox_error_result_list, info_err, TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error_result, handle_tuliprox_error_result_list, info_err, TuliproxError, TuliproxErrorKind}; use crate::model::{EpgConfig}; use shared::utils::default_as_true; use shared::utils::get_trimmed_string; diff --git a/backend/src/model/config_ip_check.rs b/backend/src/model/config_ip_check.rs index aa60a6a60..8701a1611 100644 --- a/backend/src/model/config_ip_check.rs +++ b/backend/src/model/config_ip_check.rs @@ -1,5 +1,5 @@ use regex::Regex; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::{TuliproxError, TuliproxErrorKind}; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] #[serde(deny_unknown_fields)] diff --git a/backend/src/model/config_messaging.rs b/backend/src/model/config_messaging.rs index 6f57c7c52..c9e76b99e 100644 --- a/backend/src/model/config_messaging.rs +++ b/backend/src/model/config_messaging.rs @@ -1,4 +1,4 @@ -use crate::messaging::MsgKind; +use shared::model::MsgKind; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] diff --git a/backend/src/model/config_proxy.rs b/backend/src/model/config_proxy.rs index 31dab8a01..3a9fc014e 100644 --- a/backend/src/model/config_proxy.rs +++ b/backend/src/model/config_proxy.rs @@ -1,4 +1,4 @@ -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::{TuliproxError, TuliproxErrorKind}; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] #[serde(deny_unknown_fields)] diff --git a/backend/src/model/config_rename.rs b/backend/src/model/config_rename.rs index b8f44621c..04df7e13e 100644 --- a/backend/src/model/config_rename.rs +++ b/backend/src/model/config_rename.rs @@ -1,6 +1,6 @@ +use shared::model::ItemField; use crate::foundation::filter::{apply_templates_to_pattern_single, PatternTemplate}; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error_result}; -use crate::model::ItemField; +use shared::error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error_result}; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] #[serde(deny_unknown_fields)] diff --git a/backend/src/model/config_sort.rs b/backend/src/model/config_sort.rs index 65ff486a4..0826aa4cf 100644 --- a/backend/src/model/config_sort.rs +++ b/backend/src/model/config_sort.rs @@ -1,7 +1,7 @@ use regex::Regex; use crate::foundation::filter::{apply_templates_to_pattern, apply_templates_to_pattern_single, PatternTemplate, TemplateValue}; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error, handle_tuliprox_error_result_list}; -use crate::model::{ItemField}; +use shared::error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error, handle_tuliprox_error_result_list}; +use shared::model::ItemField; #[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)] pub enum SortOrder { diff --git a/backend/src/model/config_source.rs b/backend/src/model/config_source.rs index e7ab5639b..d861eaa8e 100644 --- a/backend/src/model/config_source.rs +++ b/backend/src/model/config_source.rs @@ -1,6 +1,6 @@ use std::collections::{HashMap, HashSet}; use crate::foundation::filter::{prepare_templates, PatternTemplate}; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind, handle_tuliprox_error_result_list, create_tuliprox_error_result}; +use shared::error::{TuliproxError, TuliproxErrorKind, handle_tuliprox_error_result_list, create_tuliprox_error_result}; use crate::model::{ConfigInput, ProcessTargets}; use crate::model::config_target::ConfigTarget; use shared::utils::default_as_default; diff --git a/backend/src/model/config_target.rs b/backend/src/model/config_target.rs index 6a999d85e..c914e5795 100644 --- a/backend/src/model/config_target.rs +++ b/backend/src/model/config_target.rs @@ -2,40 +2,16 @@ use crate::foundation::filter::{get_filter, Filter, PatternTemplate, ValueProvid use crate::model::config_rename::ConfigRename; use crate::model::config_sort::ConfigSort; use crate::model::mapping::Mapping; -use crate::model::processing_order::ProcessingOrder; use crate::model::trakt::TraktConfig; -use crate::tuliprox_error::{create_tuliprox_error_result, handle_tuliprox_error_result_list, info_err, TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error_result, handle_tuliprox_error_result_list, info_err, TuliproxError, TuliproxErrorKind}; use shared::utils::{default_as_default, default_as_true, default_resolve_delay_secs}; use arc_swap::ArcSwapOption; use enum_iterator::Sequence; -use shared::model::ClusterFlags; +use shared::model::{ClusterFlags, ProcessingOrder, StrmExportStyle}; use shared::model::PlaylistItemType; use std::fmt::Display; use std::sync::Arc; -#[derive( - Debug, - Copy, - Clone, - serde::Serialize, - serde::Deserialize, - Sequence, - PartialEq, - Eq, - Hash, - Default -)] -pub enum ExportStyle { - #[serde(rename = "kodi")] - #[default] - Kodi, - #[serde(rename = "plex")] - Plex, - #[serde(rename = "emby")] - Emby, - #[serde(rename = "jellyfin")] - Jellyfin, -} #[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Eq, Hash)] pub enum TargetType { @@ -165,7 +141,7 @@ pub struct StrmTargetOutput { #[serde(default, skip_serializing_if = "Option::is_none")] pub username: Option, #[serde(default)] - pub style: ExportStyle, + pub style: StrmExportStyle, #[serde(default)] pub flat: bool, #[serde(default)] diff --git a/backend/src/model/config_web_auth.rs b/backend/src/model/config_web_auth.rs index a1a424035..7e4901d17 100644 --- a/backend/src/model/config_web_auth.rs +++ b/backend/src/model/config_web_auth.rs @@ -3,7 +3,7 @@ use std::fs::File; use std::io::BufRead; use std::path::PathBuf; use crate::auth::UserCredential; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error_result}; +use shared::error::{TuliproxError, TuliproxErrorKind, create_tuliprox_error_result}; use crate::utils; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] diff --git a/backend/src/model/config_webui.rs b/backend/src/model/config_webui.rs index 425e4a051..2f060168f 100644 --- a/backend/src/model/config_webui.rs +++ b/backend/src/model/config_webui.rs @@ -1,5 +1,5 @@ use shared::utils::default_as_true; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::{TuliproxError, TuliproxErrorKind}; use crate::model::WebAuthConfig; const RESERVED_PATHS: &[&str] = &[ diff --git a/backend/src/model/mapping.rs b/backend/src/model/mapping.rs index 3ee28d58d..01f74f131 100644 --- a/backend/src/model/mapping.rs +++ b/backend/src/model/mapping.rs @@ -7,8 +7,8 @@ use log::{trace}; use crate::foundation::filter::{apply_templates_to_pattern_single, get_filter, prepare_templates, Filter, PatternTemplate}; use crate::foundation::mapper::MapperScript; use crate::model::valid_property; -use crate::tuliprox_error::{create_tuliprox_error_result, info_err}; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error_result, info_err}; +use shared::error::{TuliproxError, TuliproxErrorKind}; pub const COUNTER_FIELDS: &[&str] = &["name", "title", "caption", "chno"]; diff --git a/backend/src/model/mod.rs b/backend/src/model/mod.rs index 1591fb34e..2c1f6d00c 100644 --- a/backend/src/model/mod.rs +++ b/backend/src/model/mod.rs @@ -13,7 +13,6 @@ mod config_hdhomerun; mod config_ip_check; mod config_sort; mod config_rename; -mod processing_order; mod config_target; mod config_source; mod config_proxy; @@ -42,7 +41,6 @@ pub use self::config_webui::*; pub use self::config_ip_check::*; pub use self::config_sort::*; pub use self::config_rename::*; -pub use self::processing_order::*; pub use self::config_target::*; pub use self::config_source::*; pub use self::config_proxy::*; diff --git a/backend/src/modules.rs b/backend/src/modules.rs index 279a067fd..250cec351 100644 --- a/backend/src/modules.rs +++ b/backend/src/modules.rs @@ -8,7 +8,6 @@ macro_rules! include_modules { extern crate pest_derive; pub mod api; pub mod auth; - pub mod tuliprox_error; pub mod messaging; pub mod model; pub mod processing; diff --git a/backend/src/processing/parser/xmltv.rs b/backend/src/processing/parser/xmltv.rs index c3b6ef5f3..88a34c0db 100644 --- a/backend/src/processing/parser/xmltv.rs +++ b/backend/src/processing/parser/xmltv.rs @@ -544,7 +544,7 @@ mod tests { assert_eq!("odisea.bg", normalize_channel_name("BG | ODISEA ᵁᴴᴰ ³⁸⁴⁰ᴾ", &epg_smart_cfg)); } - use crate::tuliprox_error::TuliproxError; + use shared::error::TuliproxError; use rphonetic::{Encoder, Metaphone}; #[test] diff --git a/backend/src/processing/parser/xtream.rs b/backend/src/processing/parser/xtream.rs index e1c22e9a3..983943c9d 100644 --- a/backend/src/processing/parser/xtream.rs +++ b/backend/src/processing/parser/xtream.rs @@ -1,4 +1,4 @@ -use crate::tuliprox_error::{create_tuliprox_error_result, TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error_result, TuliproxError, TuliproxErrorKind}; use crate::model::ConfigInput; use crate::model::{PlaylistGroup, PlaylistItem, PlaylistItemHeader}; use crate::model::{XtreamCategory, XtreamSeriesInfo, XtreamSeriesInfoEpisode, XtreamStream}; diff --git a/backend/src/processing/playlist_watch.rs b/backend/src/processing/playlist_watch.rs index f0420fff5..11f23e021 100644 --- a/backend/src/processing/playlist_watch.rs +++ b/backend/src/processing/playlist_watch.rs @@ -2,7 +2,8 @@ use std::collections::BTreeSet; use std::path::{Path}; use std::sync::Arc; use log::{error, info}; -use crate::messaging::{MsgKind, send_message}; +use shared::model::MsgKind; +use crate::messaging::{send_message}; use crate::model::Config; use crate::model::PlaylistGroup; use crate::utils; diff --git a/backend/src/processing/processor/playlist.rs b/backend/src/processing/processor/playlist.rs index 802614342..1c2a53794 100644 --- a/backend/src/processing/processor/playlist.rs +++ b/backend/src/processing/processor/playlist.rs @@ -10,17 +10,17 @@ use std::thread; use tokio::sync::Mutex; use crate::foundation::filter::{get_field_value, set_field_value, ValueProvider, ValueAccessor}; -use crate::messaging::{send_message, MsgKind}; -use crate::model::{ConfigTarget, InputType, ItemField, ProcessTargets, ProcessingOrder}; +use crate::messaging::{send_message}; +use crate::model::{ConfigTarget, InputType, ProcessTargets}; use crate::model::{CounterModifier, Mapping}; use crate::model::{FetchedPlaylist, PlaylistGroup, PlaylistItem}; -use shared::model::{FieldGetAccessor, FieldSetAccessor, PlaylistEntry, UUIDType, XtreamCluster}; +use shared::model::{FieldGetAccessor, FieldSetAccessor, ItemField, MsgKind, PlaylistEntry, ProcessingOrder, UUIDType, XtreamCluster}; use crate::model::{InputStats, PlaylistStats, SourceStats, TargetStats}; use crate::processing::playlist_watch::process_group_watch; use crate::processing::processor::xtream_series::playlist_resolve_series; use crate::processing::processor::trakt::process_trakt_categories_for_target; use crate::repository::playlist_repository::persist_playlist; -use crate::tuliprox_error::{get_errors_notify_message, notify_err, TuliproxError, TuliproxErrorKind}; +use shared::error::{get_errors_notify_message, notify_err, TuliproxError, TuliproxErrorKind}; use crate::utils::debug_if_enabled; use shared::utils::default_as_default; use deunicode::deunicode; diff --git a/backend/src/processing/processor/trakt.rs b/backend/src/processing/processor/trakt.rs index bfcf0170e..7a8cf665e 100644 --- a/backend/src/processing/processor/trakt.rs +++ b/backend/src/processing/processor/trakt.rs @@ -2,7 +2,7 @@ use std::borrow::Cow; use crate::model::{ConfigTarget, PlaylistGroup, PlaylistItem}; use shared::model::{FieldGetAccessor, FieldSetAccessor, XtreamCluster}; use crate::model::{TraktConfig, TraktContentType, TraktListConfig, TraktListItem, TraktMatchItem, TraktMatchResult}; -use crate::tuliprox_error::TuliproxError; +use shared::error::TuliproxError; use crate::utils::trakt::TraktClient; use crate::utils::trakt::extract_year_from_title; use crate::utils::trakt::normalize_title_for_matching; diff --git a/backend/src/processing/processor/xtream.rs b/backend/src/processing/processor/xtream.rs index a700a399e..adcd8cb22 100644 --- a/backend/src/processing/processor/xtream.rs +++ b/backend/src/processing/processor/xtream.rs @@ -1,5 +1,5 @@ -use crate::tuliprox_error::{info_err, notify_err}; -use crate::tuliprox_error::{str_to_io_error, TuliproxError, TuliproxErrorKind}; +use shared::error::{info_err, notify_err}; +use shared::error::{str_to_io_error, TuliproxError, TuliproxErrorKind}; use crate::model::{Config, ConfigInput}; use crate::model::{FetchedPlaylist, PlaylistItem}; use shared::model::{PlaylistEntry, PlaylistItemType, XtreamCluster}; diff --git a/backend/src/processing/processor/xtream_series.rs b/backend/src/processing/processor/xtream_series.rs index cc040229e..399851ac1 100644 --- a/backend/src/processing/processor/xtream_series.rs +++ b/backend/src/processing/processor/xtream_series.rs @@ -1,4 +1,4 @@ -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::{TuliproxError, TuliproxErrorKind}; use crate::model::{Config, ConfigTarget, InputType}; use crate::model::{FetchedPlaylist, PlaylistGroup, PlaylistItem}; use shared::model::{PlaylistItemType, XtreamCluster}; @@ -8,7 +8,7 @@ use crate::processing::processor::xtream::{create_resolve_episode_wal_files, cre use crate::repository::storage::get_input_storage_path; use crate::repository::xtream_repository::{write_series_info_to_wal_file, xtream_get_info_file_paths, xtream_update_input_info_file, xtream_update_input_series_episodes_record_from_wal_file, xtream_update_input_series_record_from_wal_file}; use crate::repository::IndexedDocumentReader; -use crate::tuliprox_error::{notify_err, info_err}; +use shared::error::{notify_err, info_err}; use crate::processing::processor::{handle_error, handle_error_and_return, create_resolve_options_function_for_xtream_target}; use std::collections::HashMap; use std::fs::File; diff --git a/backend/src/processing/processor/xtream_vod.rs b/backend/src/processing/processor/xtream_vod.rs index 7aa0d7ffb..a4ab90241 100644 --- a/backend/src/processing/processor/xtream_vod.rs +++ b/backend/src/processing/processor/xtream_vod.rs @@ -1,10 +1,10 @@ -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::{TuliproxError, TuliproxErrorKind}; use crate::model::{Config, ConfigTarget, InputType}; use crate::model::{FetchedPlaylist, PlaylistItem}; use shared::model::{PlaylistItemType, XtreamCluster}; use crate::processing::processor::xtream::{create_resolve_info_wal_files, playlist_resolve_download_playlist_item, read_processed_info_ids, should_update_info}; use crate::repository::xtream_repository::{write_vod_info_to_wal_file, xtream_update_input_info_file, xtream_update_input_vod_record_from_wal_file, InputVodInfoRecord}; -use crate::tuliprox_error::{notify_err}; +use shared::error::{notify_err}; use crate::processing::processor::{handle_error, handle_error_and_return, create_resolve_options_function_for_xtream_target}; use crate::utils::{get_u32_from_serde_value, get_u64_from_serde_value, get_string_from_serde_value}; use crate::repository::xtream_repository::xtream_get_input_info; diff --git a/backend/src/repository/bplustree.rs b/backend/src/repository/bplustree.rs index e52fed0d7..c48e523d4 100644 --- a/backend/src/repository/bplustree.rs +++ b/backend/src/repository/bplustree.rs @@ -3,7 +3,7 @@ use std::io::{self, BufReader, Read, Seek, SeekFrom, Write}; use std::marker::PhantomData; use std::mem::size_of; use std::path::Path; -use crate::tuliprox_error::{str_to_io_error, to_io_error}; +use shared::error::{str_to_io_error, to_io_error}; use log::error; use ruzstd::decoding::StreamingDecoder; use ruzstd::encoding::{compress_to_vec, CompressionLevel}; diff --git a/backend/src/repository/epg_repository.rs b/backend/src/repository/epg_repository.rs index 104543ef5..c3f86d561 100644 --- a/backend/src/repository/epg_repository.rs +++ b/backend/src/repository/epg_repository.rs @@ -1,4 +1,4 @@ -use crate::tuliprox_error::{notify_err, TuliproxError, TuliproxErrorKind}; +use shared::error::{notify_err, TuliproxError, TuliproxErrorKind}; use crate::model::{Config, ConfigTarget, TargetOutput}; use crate::model::Epg; use crate::repository::m3u_repository::m3u_get_epg_file_path; diff --git a/backend/src/repository/indexed_document.rs b/backend/src/repository/indexed_document.rs index 034f3059b..7d735345d 100644 --- a/backend/src/repository/indexed_document.rs +++ b/backend/src/repository/indexed_document.rs @@ -8,7 +8,7 @@ use crate::repository::bplustree::{BPlusTree, BPlusTreeQuery}; use log::error; use serde::{Deserialize, Serialize}; use tempfile::NamedTempFile; -use crate::tuliprox_error::{str_to_io_error, to_io_error}; +use shared::error::{str_to_io_error, to_io_error}; use crate::utils; use crate::utils::{bincode_deserialize, bincode_serialize}; diff --git a/backend/src/repository/m3u_playlist_iterator.rs b/backend/src/repository/m3u_playlist_iterator.rs index ff043bbca..aafff56ea 100644 --- a/backend/src/repository/m3u_playlist_iterator.rs +++ b/backend/src/repository/m3u_playlist_iterator.rs @@ -1,5 +1,5 @@ -use crate::tuliprox_error::info_err; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::info_err; +use shared::error::{TuliproxError, TuliproxErrorKind}; use crate::model::{ProxyType, ProxyUserCredentials}; use crate::model::{Config, ConfigTarget, ConfigTargetOptions, TargetType}; use crate::model::{M3uPlaylistItem}; diff --git a/backend/src/repository/m3u_repository.rs b/backend/src/repository/m3u_repository.rs index 4fb1919ad..12dbf2d9a 100644 --- a/backend/src/repository/m3u_repository.rs +++ b/backend/src/repository/m3u_repository.rs @@ -1,5 +1,5 @@ -use crate::tuliprox_error::{create_tuliprox_error,info_err}; -use crate::tuliprox_error::{str_to_io_error, TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error,info_err}; +use shared::error::{str_to_io_error, TuliproxError, TuliproxErrorKind}; use crate::model::ProxyUserCredentials; use crate::model::{Config, ConfigTarget, M3uTargetOutput}; use crate::model::{M3uPlaylistItem, PlaylistGroup, PlaylistItem}; diff --git a/backend/src/repository/playlist_repository.rs b/backend/src/repository/playlist_repository.rs index 76d854fd7..4fd46cb77 100644 --- a/backend/src/repository/playlist_repository.rs +++ b/backend/src/repository/playlist_repository.rs @@ -1,5 +1,5 @@ -use crate::tuliprox_error::info_err; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::info_err; +use shared::error::{TuliproxError, TuliproxErrorKind}; use crate::model::{Config, ConfigTarget, TargetOutput}; use crate::model::{PlaylistGroup}; use shared::model::{PlaylistItemType}; diff --git a/backend/src/repository/storage.rs b/backend/src/repository/storage.rs index e0ba51733..797615f28 100644 --- a/backend/src/repository/storage.rs +++ b/backend/src/repository/storage.rs @@ -1,7 +1,7 @@ use std::path::{Path, PathBuf}; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::{TuliproxError, TuliproxErrorKind}; use crate::model::{Config}; -use crate::tuliprox_error::{notify_err}; +use shared::error::{notify_err}; use crate::repository::storage_const; use crate::utils; diff --git a/backend/src/repository/strm_repository.rs b/backend/src/repository/strm_repository.rs index f40fcd394..fb0a31f74 100644 --- a/backend/src/repository/strm_repository.rs +++ b/backend/src/repository/strm_repository.rs @@ -1,9 +1,8 @@ -use crate::tuliprox_error::{create_tuliprox_error_result, info_err}; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error_result, info_err}; +use shared::error::{TuliproxError, TuliproxErrorKind}; use crate::model::{ApiProxyServerInfo, ProxyUserCredentials}; use crate::model::{Config, ConfigTarget, StrmTargetOutput}; -use crate::model::{PlaylistGroup, PlaylistItem, ExportStyle, -}; +use crate::model::{PlaylistGroup, PlaylistItem}; use crate::model::XtreamSeriesEpisode; use crate::repository::bplustree::BPlusTree; use crate::repository::storage::{ensure_target_storage_path, get_input_storage_path}; @@ -23,7 +22,7 @@ use std::path::{Path, PathBuf}; use std::sync::Arc; use tokio::fs::{create_dir_all, remove_dir, remove_file, File}; use tokio::io::{AsyncBufReadExt, AsyncReadExt, AsyncWriteExt, BufReader, BufWriter}; -use shared::model::{ClusterFlags, FieldGetAccessor, PlaylistItemType, UUIDType}; +use shared::model::{ClusterFlags, FieldGetAccessor, PlaylistItemType, StrmExportStyle, UUIDType}; use crate::utils; /// Sanitizes a string to be safe for use as a file or directory name by @@ -673,7 +672,7 @@ async fn style_based_rename( cfg: &Config, strm_item_info: &StrmItemInfo, input_tmdb_indexes: &mut InputTmdbIndexMap, - style: &ExportStyle, + style: &StrmExportStyle, underscore_whitespace: bool, flat: bool, ) -> (PathBuf, String) { @@ -694,10 +693,10 @@ async fn style_based_rename( // Dispatch the call to the responsible function based on the style. match style { - ExportStyle::Kodi => format_for_kodi(strm_item_info, tmdb_id, separator, flat), - ExportStyle::Plex => format_for_plex(strm_item_info, tmdb_id, separator, flat), - ExportStyle::Emby => format_for_emby(strm_item_info, tmdb_id, separator, flat), - ExportStyle::Jellyfin => format_for_jellyfin(strm_item_info, tmdb_id, separator, flat), + StrmExportStyle::Kodi => format_for_kodi(strm_item_info, tmdb_id, separator, flat), + StrmExportStyle::Plex => format_for_plex(strm_item_info, tmdb_id, separator, flat), + StrmExportStyle::Emby => format_for_emby(strm_item_info, tmdb_id, separator, flat), + StrmExportStyle::Jellyfin => format_for_jellyfin(strm_item_info, tmdb_id, separator, flat), } } @@ -706,7 +705,7 @@ async fn prepare_strm_files( new_playlist: &mut [PlaylistGroup], _root_path: &Path, underscore_whitespace: bool, - style: &ExportStyle, + style: &StrmExportStyle, flat: bool, ) -> Vec { let channel_count = new_playlist @@ -769,13 +768,13 @@ async fn prepare_strm_files( // A style-specific match is not strictly necessary here but is good practice for future flexibility. let new_filename = match style { // Plex, Emby, and Kodi all follow the `Filename - Suffix` pattern. - ExportStyle::Plex | ExportStyle::Emby | ExportStyle::Kodi => { + StrmExportStyle::Plex | StrmExportStyle::Emby | StrmExportStyle::Kodi => { format!("{base_filename}{version_separator}{version_label}") } // Jellyfin also follows this pattern, but explicitly shows an option for " - [Label]". // Using brackets makes the version distinct and is a clean implementation. - ExportStyle::Jellyfin => { + StrmExportStyle::Jellyfin => { format!("{base_filename}{version_separator}[{version_label}]") } }; diff --git a/backend/src/repository/xtream_playlist_iterator.rs b/backend/src/repository/xtream_playlist_iterator.rs index 1be633ec8..d42a71428 100644 --- a/backend/src/repository/xtream_playlist_iterator.rs +++ b/backend/src/repository/xtream_playlist_iterator.rs @@ -1,8 +1,8 @@ use std::collections::HashSet; use log::error; use shared::model::XtreamCluster; -use crate::tuliprox_error::info_err; -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::info_err; +use shared::error::{TuliproxError, TuliproxErrorKind}; use crate::model::{ProxyUserCredentials}; use crate::model::{Config, ConfigTarget, TargetType}; use crate::model::{XtreamPlaylistItem}; diff --git a/backend/src/repository/xtream_repository.rs b/backend/src/repository/xtream_repository.rs index 0d73169f9..4cb992da7 100644 --- a/backend/src/repository/xtream_repository.rs +++ b/backend/src/repository/xtream_repository.rs @@ -1,4 +1,4 @@ -use crate::tuliprox_error::{create_tuliprox_error, create_tuliprox_error_result, info_err, notify_err, str_to_io_error, to_io_error, TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error, create_tuliprox_error_result, info_err, notify_err, str_to_io_error, to_io_error, TuliproxError, TuliproxErrorKind}; use crate::model::{ProxyUserCredentials}; use crate::model::{Config, ConfigInput, ConfigTarget, XtreamTargetOutput}; use crate::model::{PlaylistGroup, PlaylistItem, XtreamPlaylistItem}; diff --git a/backend/src/utils/bincode_utils.rs b/backend/src/utils/bincode_utils.rs index f55d680f1..cb80df784 100644 --- a/backend/src/utils/bincode_utils.rs +++ b/backend/src/utils/bincode_utils.rs @@ -1,5 +1,5 @@ use std::io; -use crate::tuliprox_error::to_io_error; +use shared::error::to_io_error; #[inline] pub fn bincode_serialize(value: &T) -> io::Result> diff --git a/backend/src/utils/crypto_utils.rs b/backend/src/utils/crypto_utils.rs index 44f3c8807..8e3716aab 100644 --- a/backend/src/utils/crypto_utils.rs +++ b/backend/src/utils/crypto_utils.rs @@ -1,4 +1,4 @@ -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::{TuliproxError, TuliproxErrorKind}; use base64::{engine::general_purpose, Engine as _}; use openssl::symm::{Cipher, Crypter, Mode}; use rand::Rng; diff --git a/backend/src/utils/file/config_reader.rs b/backend/src/utils/file/config_reader.rs index 8e1cee9f8..d0091a96a 100644 --- a/backend/src/utils/file/config_reader.rs +++ b/backend/src/utils/file/config_reader.rs @@ -1,6 +1,6 @@ use crate::model::{ApiProxyConfig, SourcesConfig}; use crate::model::{Config, ConfigDto}; -use crate::tuliprox_error::{create_tuliprox_error, info_err, to_io_error, TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error, info_err, to_io_error, TuliproxError, TuliproxErrorKind}; use crate::utils::{open_file, EnvResolvingReader}; use crate::utils::{file_reader}; use crate::utils::sys_utils::exit; @@ -73,8 +73,6 @@ pub fn read_sources(sources_file: &str, resolve_env: bool, include_computed: boo } } - - pub fn read_config(config_path: &str, config_file: &str, sources_file: &str, api_proxy_file: &str, mappings_file: Option, include_computed: bool) -> Result { let resolve_env = true; diff --git a/backend/src/utils/file/csv_input_reader.rs b/backend/src/utils/file/csv_input_reader.rs index a4ec54dbc..b93f6f1e6 100644 --- a/backend/src/utils/file/csv_input_reader.rs +++ b/backend/src/utils/file/csv_input_reader.rs @@ -1,5 +1,5 @@ use crate::model::{ConfigInput, ConfigInputAlias, InputType}; -use crate::tuliprox_error::{str_to_io_error, to_io_error}; +use shared::error::{str_to_io_error, to_io_error}; use crate::utils::request::{get_credentials_from_url, get_local_file_content}; use crate::utils::EnvResolvingReader; use crate::utils::{file_reader, resolve_relative_path}; diff --git a/backend/src/utils/file/file_lock_manager.rs b/backend/src/utils/file/file_lock_manager.rs index c460dc015..a692488fa 100644 --- a/backend/src/utils/file/file_lock_manager.rs +++ b/backend/src/utils/file/file_lock_manager.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use std::{fmt, io}; use std::path::{Path, PathBuf}; use tokio::sync::{Mutex, RwLock, RwLockReadGuard, RwLockWriteGuard}; -use crate::tuliprox_error::str_to_io_error; +use shared::error::str_to_io_error; #[derive(Clone)] pub struct FileLockManager { diff --git a/backend/src/utils/file/file_utils.rs b/backend/src/utils/file/file_utils.rs index 0e9ac14fc..e32878831 100644 --- a/backend/src/utils/file/file_utils.rs +++ b/backend/src/utils/file/file_utils.rs @@ -5,7 +5,7 @@ use std::io::{BufReader, BufWriter, Read, Write}; use std::path::{Path, PathBuf}; use std::{env, fs}; -use crate::tuliprox_error::str_to_io_error; +use shared::error::str_to_io_error; use crate::utils::debug_if_enabled; use shared::utils::{API_PROXY_FILE, CONFIG_FILE, CONFIG_PATH, MAPPING_FILE, SOURCE_FILE, USER_FILE}; use log::{debug, error}; diff --git a/backend/src/utils/file/mapping_reader.rs b/backend/src/utils/file/mapping_reader.rs index 188140939..52d2feac2 100644 --- a/backend/src/utils/file/mapping_reader.rs +++ b/backend/src/utils/file/mapping_reader.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; use crate::foundation::filter::PatternTemplate; use crate::model::{Mapping, MappingDefinition, Mappings}; -use crate::tuliprox_error::{create_tuliprox_error_result, info_err, TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error_result, info_err, TuliproxError, TuliproxErrorKind}; use crate::utils::traverse_dir; use crate::utils::{config_file_reader, open_file}; use log::{warn}; diff --git a/backend/src/utils/network/epg.rs b/backend/src/utils/network/epg.rs index 8f7bc85a4..80f2705de 100644 --- a/backend/src/utils/network/epg.rs +++ b/backend/src/utils/network/epg.rs @@ -1,4 +1,4 @@ -use crate::tuliprox_error::TuliproxError; +use shared::error::TuliproxError; use crate::model::{Config, ConfigInput, PersistedEpgSource}; use crate::model::TVGuide; use crate::utils::{add_prefix_to_filename, cleanup_unlisted_files_with_suffix, prepare_file_path, short_hash}; diff --git a/backend/src/utils/network/ip_checker.rs b/backend/src/utils/network/ip_checker.rs index e688075c6..f0300afef 100644 --- a/backend/src/utils/network/ip_checker.rs +++ b/backend/src/utils/network/ip_checker.rs @@ -1,4 +1,4 @@ -use crate::tuliprox_error::{TuliproxError, TuliproxErrorKind}; +use shared::error::{TuliproxError, TuliproxErrorKind}; use crate::model::IpCheckConfig; use regex::Regex; use reqwest::Client; diff --git a/backend/src/utils/network/m3u.rs b/backend/src/utils/network/m3u.rs index 0bd447c6f..3778226ad 100644 --- a/backend/src/utils/network/m3u.rs +++ b/backend/src/utils/network/m3u.rs @@ -1,5 +1,5 @@ use std::sync::Arc; -use crate::tuliprox_error::TuliproxError; +use shared::error::TuliproxError; use crate::model::{Config, ConfigInput}; use crate::model::PlaylistGroup; use crate::processing::parser::m3u; diff --git a/backend/src/utils/network/request.rs b/backend/src/utils/network/request.rs index 73bab66ed..4317d8786 100644 --- a/backend/src/utils/network/request.rs +++ b/backend/src/utils/network/request.rs @@ -15,8 +15,8 @@ use reqwest::header::CONTENT_ENCODING; use reqwest::header::{HeaderMap, HeaderName, HeaderValue}; use url::Url; -use crate::tuliprox_error::create_tuliprox_error_result; -use crate::tuliprox_error::{str_to_io_error, TuliproxError, TuliproxErrorKind}; +use shared::error::create_tuliprox_error_result; +use shared::error::{str_to_io_error, TuliproxError, TuliproxErrorKind}; use crate::model::{format_elapsed_time, Config}; use crate::model::{ConfigInput, InputFetchMethod}; use crate::repository::storage::{get_input_storage_path}; diff --git a/backend/src/utils/network/xtream.rs b/backend/src/utils/network/xtream.rs index 4c7cacd59..63231b965 100644 --- a/backend/src/utils/network/xtream.rs +++ b/backend/src/utils/network/xtream.rs @@ -4,7 +4,7 @@ use crate::model::{PlaylistGroup, XtreamPlaylistItem}; use crate::processing::parser::xtream; use crate::repository::xtream_repository; use crate::repository::xtream_repository::{rewrite_xtream_series_info_content, rewrite_xtream_vod_info_content, xtream_get_input_info}; -use crate::tuliprox_error::{str_to_io_error, TuliproxError}; +use shared::error::{str_to_io_error, TuliproxError}; use crate::utils; use crate::utils::{get_string_from_serde_value, request}; use crate::utils::request::extract_extension_from_url; @@ -15,8 +15,8 @@ use std::io::Error; use std::str::FromStr; use std::sync::Arc; use std::time::{SystemTime, UNIX_EPOCH}; -use shared::model::{PlaylistEntry, XtreamCluster}; -use crate::messaging::{send_message, MsgKind}; +use shared::model::{MsgKind, PlaylistEntry, XtreamCluster}; +use crate::messaging::{send_message}; #[inline] pub fn get_xtream_stream_url_base(url: &str, username: &str, password: &str) -> String { diff --git a/backend/src/utils/trakt/client.rs b/backend/src/utils/trakt/client.rs index a82eeddc2..234cb6df0 100644 --- a/backend/src/utils/trakt/client.rs +++ b/backend/src/utils/trakt/client.rs @@ -1,9 +1,9 @@ use crate::model::{TraktApiConfig, TraktListConfig, TraktListItem}; -use crate::tuliprox_error::TuliproxError; +use shared::error::TuliproxError; use reqwest::header::{HeaderMap, HeaderValue}; use std::sync::Arc; use log::{debug, info}; -use crate::tuliprox_error::{info_err, TuliproxErrorKind}; +use shared::error::{info_err, TuliproxErrorKind}; use super::errors::{handle_trakt_api_error}; pub struct TraktClient { diff --git a/backend/src/utils/trakt/errors.rs b/backend/src/utils/trakt/errors.rs index d4d320bb5..872e83759 100644 --- a/backend/src/utils/trakt/errors.rs +++ b/backend/src/utils/trakt/errors.rs @@ -1,4 +1,4 @@ -use crate::tuliprox_error::{create_tuliprox_error_result, TuliproxError, TuliproxErrorKind}; +use shared::error::{create_tuliprox_error_result, TuliproxError, TuliproxErrorKind}; use reqwest::StatusCode; /// Handle Trakt API response status and convert to appropriate error diff --git a/shared/Cargo.toml b/shared/Cargo.toml index 2433de59e..82a0d2b0c 100644 --- a/shared/Cargo.toml +++ b/shared/Cargo.toml @@ -8,3 +8,4 @@ bitflags = "2.9.1" serde = { version = "1.0.219", features = ["derive"] } rand = "0.9.1" regex = "1.11.1" +enum-iterator = "2" diff --git a/shared/src/error/mod.rs b/shared/src/error/mod.rs new file mode 100644 index 000000000..5f7963a06 --- /dev/null +++ b/shared/src/error/mod.rs @@ -0,0 +1,3 @@ +mod tuliprox_error; + +pub use tuliprox_error::*; \ No newline at end of file diff --git a/backend/src/tuliprox_error.rs b/shared/src/error/tuliprox_error.rs similarity index 100% rename from backend/src/tuliprox_error.rs rename to shared/src/error/tuliprox_error.rs diff --git a/shared/src/lib.rs b/shared/src/lib.rs index 32c2eb054..71087e205 100644 --- a/shared/src/lib.rs +++ b/shared/src/lib.rs @@ -1,2 +1,3 @@ +pub mod error; pub mod model; pub mod utils; \ No newline at end of file diff --git a/shared/src/model/config/base.rs b/shared/src/model/config/base.rs new file mode 100644 index 000000000..390b1de9c --- /dev/null +++ b/shared/src/model/config/base.rs @@ -0,0 +1,56 @@ +use crate::model::{WebUiConfigDto, MessagingConfigDto, IpCheckConfigDto, HdHomeRunConfigDto, VideoConfigDto, ScheduleConfigDto, LogConfigDto, ReverseProxyConfigDto, ProxyConfigDto}; +use crate::utils::{default_connect_timeout_secs}; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct ConfigApiDto { + pub host: String, + pub port: u16, + #[serde(default)] + pub web_root: String, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct ConfigDto { + #[serde(default)] + pub threads: u8, + pub api: ConfigApiDto, + pub working_dir: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub backup_dir: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub user_config_dir: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub mapping_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub custom_stream_response_path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub video: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub schedules: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub log: Option, + #[serde(default)] + pub user_access_control: bool, + #[serde(default = "default_connect_timeout_secs")] + pub connect_timeout_secs: u32, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sleep_timer_mins: Option, + #[serde(default)] + pub update_on_boot: bool, + #[serde(default)] + pub config_hot_reload: bool, + #[serde(default)] + pub web_ui: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub messaging: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub reverse_proxy: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub hdhomerun: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub proxy: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub ipcheck: Option, +} diff --git a/shared/src/model/config/cache.rs b/shared/src/model/config/cache.rs new file mode 100644 index 000000000..29edb8aa5 --- /dev/null +++ b/shared/src/model/config/cache.rs @@ -0,0 +1,10 @@ +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct CacheConfigDto { + #[serde(default)] + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub size: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub dir: Option, +} \ No newline at end of file diff --git a/shared/src/model/config/epg.rs b/shared/src/model/config/epg.rs new file mode 100644 index 000000000..00d1a71d6 --- /dev/null +++ b/shared/src/model/config/epg.rs @@ -0,0 +1,49 @@ + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpgSourceDto { + pub url: String, + #[serde(default)] + pub priority: i16, + #[serde(default)] + pub logo_override: bool, +} + + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, Default)] +#[serde(rename_all = "lowercase")] +pub enum EpgNamePrefix { + #[default] + Ignore, + Suffix(String), + Prefix(String), +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpgSmartMatchConfigDto { + #[serde(default)] + pub enabled: bool, + pub normalize_regex: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub strip: Option>, + #[serde(default)] + pub name_prefix: EpgNamePrefix, + #[serde(default)] + pub name_prefix_separator: Option>, + #[serde(default)] + pub fuzzy_matching: bool, + #[serde(default)] + pub match_threshold: u16, + #[serde(default)] + pub best_match_threshold: u16, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct EpgConfigDto { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sources: Option>, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub smart_match: Option, +} diff --git a/shared/src/model/config/hdhomerun.rs b/shared/src/model/config/hdhomerun.rs new file mode 100644 index 000000000..c60ea5c41 --- /dev/null +++ b/shared/src/model/config/hdhomerun.rs @@ -0,0 +1,45 @@ + +fn default_friendly_name() -> String { String::from("TuliproxTV") } +fn default_manufacturer() -> String { String::from("Silicondust") } +fn default_model_name() -> String { String::from("HDTC-2US") } +fn default_firmware_name() -> String { String::from("hdhomeruntc_atsc") } +fn default_firmware_version() -> String { String::from("20170930") } +fn default_device_type() -> String { String::from("urn:schemas-upnp-org:device:MediaServer:1") } +fn default_device_udn() -> String { String::from("uuid:12345678-90ab-cdef-1234-567890abcdef::urn:dial-multicast:com.silicondust.hdhomerun") } + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct HdHomeRunDeviceConfigDto { + #[serde(default = "default_friendly_name")] + pub friendly_name: String, + #[serde(default = "default_manufacturer")] + pub manufacturer: String, + #[serde(default = "default_model_name")] + pub model_name: String, + #[serde(default = "default_model_name")] + pub model_number: String, + #[serde(default = "default_firmware_name")] + pub firmware_name: String, + #[serde(default = "default_firmware_version")] + pub firmware_version: String, + // pub device_auth: String, + #[serde(default = "default_device_type")] + pub device_type: String, + #[serde(default = "default_device_udn")] + pub device_udn: String, + pub name: String, + #[serde(default)] + pub port: u16, + #[serde(default)] + pub tuner_count: u8, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct HdHomeRunConfigDto { + #[serde(default)] + pub enabled: bool, + #[serde(default)] + pub auth: bool, + pub devices: Vec, +} diff --git a/shared/src/model/config/input.rs b/shared/src/model/config/input.rs new file mode 100644 index 000000000..839544f19 --- /dev/null +++ b/shared/src/model/config/input.rs @@ -0,0 +1,91 @@ +use std::collections::HashMap; +use enum_iterator::Sequence; +use crate::model::{EpgConfigDto}; +use crate::utils::{default_as_true}; + +#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Sequence, + PartialEq, Eq, Default)] +pub enum InputType { + #[serde(rename = "m3u")] + #[default] + M3u, + #[serde(rename = "xtream")] + Xtream, + #[serde(rename = "m3u_batch")] + M3uBatch, + #[serde(rename = "xtream_batch")] + XtreamBatch, +} + +#[allow(clippy::struct_excessive_bools)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct ConfigInputOptionsDto { + #[serde(default)] + pub xtream_skip_live: bool, + #[serde(default)] + pub xtream_skip_vod: bool, + #[serde(default)] + pub xtream_skip_series: bool, + #[serde(default = "default_as_true")] + pub xtream_live_stream_use_prefix: bool, + #[serde(default)] + pub xtream_live_stream_without_extension: bool, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct ConfigInputAliasDto { + #[serde(skip)] + pub id: u16, + pub name: String, + pub url: String, + pub username: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub password: Option, + #[serde(default)] + pub priority: i16, + #[serde(default)] + pub max_connections: u16, +} + +#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Sequence, + PartialEq, Eq, Default)] +pub enum InputFetchMethod { + #[default] + GET, + POST, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct ConfigInputDto { + #[serde(skip)] + pub id: u16, + pub name: String, + #[serde(default, rename = "type")] + pub input_type: InputType, + #[serde(default)] + pub headers: HashMap, + pub url: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub epg: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub username: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub password: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub persist: Option, + #[serde(default = "default_as_true")] + pub enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub options: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub aliases: Option>, + #[serde(default)] + pub priority: i16, + #[serde(default)] + pub max_connections: u16, + #[serde(default)] + pub method: InputFetchMethod, +} diff --git a/shared/src/model/config/ipcheck.rs b/shared/src/model/config/ipcheck.rs new file mode 100644 index 000000000..61456191c --- /dev/null +++ b/shared/src/model/config/ipcheck.rs @@ -0,0 +1,24 @@ +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct IpCheckConfigDto { + /// URL that may return both IPv4 and IPv6 in one response + #[serde(default, skip_serializing_if = "Option::is_none")] + pub url: Option, + + /// Dedicated URL to fetch only IPv4 + #[serde(default, skip_serializing_if = "Option::is_none")] + pub url_ipv4: Option, + + /// Dedicated URL to fetch only IPv6 + #[serde(default, skip_serializing_if = "Option::is_none")] + pub url_ipv6: Option, + + /// Optional regex pattern to extract IPv4 + #[serde(default, skip_serializing_if = "Option::is_none")] + pub pattern_ipv4: Option, + + /// Optional regex pattern to extract IPv6 + #[serde(default, skip_serializing_if = "Option::is_none")] + pub pattern_ipv6: Option, + +} \ No newline at end of file diff --git a/shared/src/model/config/log.rs b/shared/src/model/config/log.rs new file mode 100644 index 000000000..3a21d0777 --- /dev/null +++ b/shared/src/model/config/log.rs @@ -0,0 +1,12 @@ +use crate::utils::{default_as_true}; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct LogConfigDto { + #[serde(default = "default_as_true")] + pub sanitize_sensitive_info: bool, + #[serde(default)] + pub log_active_user: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub log_level: Option, +} diff --git a/shared/src/model/config/messaging.rs b/shared/src/model/config/messaging.rs new file mode 100644 index 000000000..92e649614 --- /dev/null +++ b/shared/src/model/config/messaging.rs @@ -0,0 +1,36 @@ +use crate::model::MsgKind; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct TelegramMessagingConfigDto { + pub bot_token: String, + pub chat_ids: Vec, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct RestMessagingConfigDto { + pub url: String, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct PushoverMessagingConfigDto { + pub url: Option, + pub token: String, + pub user: String, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct MessagingConfigDto { + #[serde(default)] + pub notify_on: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub telegram: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub rest: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub pushover: Option, + +} \ No newline at end of file diff --git a/shared/src/model/config/mod.rs b/shared/src/model/config/mod.rs new file mode 100644 index 000000000..c06f0ab70 --- /dev/null +++ b/shared/src/model/config/mod.rs @@ -0,0 +1,38 @@ +mod trakt; +mod base; +mod web; +mod messaging; +mod hdhomerun; +mod ipcheck; +mod source; +mod target; +mod sort; +mod video; +mod schedule; +mod log; +mod input; +mod stream; +mod epg; +mod reverse_proxy; +mod cache; +mod rate_limit; +mod proxy; + +pub use base::*; +pub use web::*; +pub use messaging::*; +pub use hdhomerun::*; +pub use ipcheck::*; +pub use source::*; +pub use sort::*; +pub use video::*; +pub use schedule::*; +pub use log::*; +pub use input::*; +pub use stream::*; +pub use epg::*; +pub use cache::*; +pub use rate_limit::*; +pub use reverse_proxy::*; +pub use proxy::*; +pub use trakt::*; diff --git a/shared/src/model/config/proxy.rs b/shared/src/model/config/proxy.rs new file mode 100644 index 000000000..8832eb777 --- /dev/null +++ b/shared/src/model/config/proxy.rs @@ -0,0 +1,8 @@ + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct ProxyConfigDto { + pub url: String, + pub username: Option, + pub password: Option, +} diff --git a/shared/src/model/config/rate_limit.rs b/shared/src/model/config/rate_limit.rs new file mode 100644 index 000000000..4df7ae8f5 --- /dev/null +++ b/shared/src/model/config/rate_limit.rs @@ -0,0 +1,7 @@ +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct RateLimitConfigDto { + pub enabled: bool, + pub period_millis: u64, + pub burst_size: u32, +} \ No newline at end of file diff --git a/shared/src/model/config/reverse_proxy.rs b/shared/src/model/config/reverse_proxy.rs new file mode 100644 index 000000000..03fe2a150 --- /dev/null +++ b/shared/src/model/config/reverse_proxy.rs @@ -0,0 +1,16 @@ +use crate::model::{CacheConfigDto, RateLimitConfigDto, StreamConfigDto}; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct ReverseProxyConfigDto { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub stream: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub cache: Option, + #[serde(default)] + pub resource_rewrite_disabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub rate_limit: Option, + #[serde(default)] + pub disable_referer_header: bool, +} diff --git a/shared/src/model/config/schedule.rs b/shared/src/model/config/schedule.rs new file mode 100644 index 000000000..3c592586a --- /dev/null +++ b/shared/src/model/config/schedule.rs @@ -0,0 +1,9 @@ + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct ScheduleConfigDto { + #[serde(default)] + pub schedule: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub targets: Option>, +} \ No newline at end of file diff --git a/shared/src/model/config/sort.rs b/shared/src/model/config/sort.rs new file mode 100644 index 000000000..d34291185 --- /dev/null +++ b/shared/src/model/config/sort.rs @@ -0,0 +1,40 @@ +use crate::model::ItemField; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ConfigSortGroupDto { + pub order: SortOrder, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sequence: Option>, +} + +#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize)] +pub enum SortOrder { + #[serde(rename = "asc")] + Asc, + #[serde(rename = "desc")] + Desc, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ConfigSortChannelDto { + // channel field + pub field: ItemField, + // match against group title + pub group_pattern: String, + pub order: SortOrder, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sequence: Option>, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct ConfigSortDto { + #[serde(default)] + pub match_as_ascii: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub groups: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub channels: Option>, +} diff --git a/shared/src/model/config/source.rs b/shared/src/model/config/source.rs new file mode 100644 index 000000000..2945ef1b4 --- /dev/null +++ b/shared/src/model/config/source.rs @@ -0,0 +1,41 @@ +use crate::model::{ConfigInputDto, ItemField}; +use crate::model::config::target::ConfigTargetDto; + + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(untagged)] +pub enum TemplateValue { + Single(String), + Multi(Vec), +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +pub struct PatternTemplateDto { + pub name: String, + pub value: TemplateValue, + #[serde(skip)] + pub placeholder: String, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ConfigRenameDto { + pub field: ItemField, + pub pattern: String, + pub new_name: String, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ConfigSourceDto { + pub inputs: Vec, + pub targets: Vec, +} + +#[derive(Default, Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct SourcesConfigDto { + #[serde(default, skip_serializing_if = "Option::is_none")] + pub templates: Option>, + pub sources: Vec, +} \ No newline at end of file diff --git a/shared/src/model/config/stream.rs b/shared/src/model/config/stream.rs new file mode 100644 index 000000000..41fce620a --- /dev/null +++ b/shared/src/model/config/stream.rs @@ -0,0 +1,29 @@ +use crate::utils::{default_grace_period_millis, default_grace_period_timeout_secs}; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct StreamBufferConfigDto { + #[serde(default)] + pub enabled: bool, + #[serde(default)] + pub size: usize, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct StreamConfigDto { + #[serde(default)] + pub retry: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub buffer: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub throttle: Option, + #[serde(default = "default_grace_period_millis")] + pub grace_period_millis: u64, + #[serde(default = "default_grace_period_timeout_secs")] + pub grace_period_timeout_secs: u64, + #[serde(default)] + pub forced_retry_interval_secs: u32, + #[serde(default, skip)] + pub throttle_kbps: u64, +} diff --git a/shared/src/model/config/target.rs b/shared/src/model/config/target.rs new file mode 100644 index 000000000..ae79cc9cb --- /dev/null +++ b/shared/src/model/config/target.rs @@ -0,0 +1,112 @@ +use crate::model::{ClusterFlags, ConfigSortDto, ProcessingOrder, StrmExportStyle, TargetType, TraktConfigDto}; +use crate::model::config::source::ConfigRenameDto; +use crate::utils::{default_as_true, default_resolve_delay_secs, default_as_default}; +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct ConfigTargetOptionsDto { + #[serde(default)] + pub ignore_logo: bool, + #[serde(default)] + pub share_live_streams: bool, + #[serde(default)] + pub remove_duplicates: bool, + #[serde(skip_serializing_if = "Option::is_none")] + pub force_redirect: Option, +} + +#[allow(clippy::struct_excessive_bools)] +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct XtreamTargetOutputDto { + #[serde(default = "default_as_true")] + pub skip_live_direct_source: bool, + #[serde(default = "default_as_true")] + pub skip_video_direct_source: bool, + #[serde(default = "default_as_true")] + pub skip_series_direct_source: bool, + #[serde(default)] + pub resolve_series: bool, + #[serde(default = "default_resolve_delay_secs")] + pub resolve_series_delay: u16, + #[serde(default)] + pub resolve_vod: bool, + #[serde(default = "default_resolve_delay_secs")] + pub resolve_vod_delay: u16, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub trakt: Option, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct M3uTargetOutputDto { + #[serde(skip_serializing_if = "Option::is_none")] + pub filename: Option, + #[serde(default)] + pub include_type_in_url: bool, + #[serde(default)] + pub mask_redirect_url: bool, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct StrmTargetOutputDto { + pub directory: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub username: Option, + #[serde(default)] + pub style: StrmExportStyle, + #[serde(default)] + pub flat: bool, + #[serde(default)] + pub underscore_whitespace: bool, + #[serde(default)] + pub cleanup: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub strm_props: Option>, +} +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct HdHomeRunTargetOutputDto { + pub device: String, + pub username: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub use_output: Option, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields, tag = "type", rename_all = "lowercase")] +pub enum TargetOutput { + Xtream(XtreamTargetOutputDto), + M3u(M3uTargetOutputDto), + Strm(StrmTargetOutputDto), + HdHomeRun(HdHomeRunTargetOutputDto), +} + + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct ConfigTargetDto { + #[serde(skip)] + pub id: u16, + #[serde(default = "default_as_true")] + pub enabled: bool, + #[serde(default = "default_as_default")] + pub name: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub options: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub sort: Option, + pub filter: String, + #[serde(default)] + pub output: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub rename: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub mapping: Option>, + #[serde(default)] + pub processing_order: ProcessingOrder, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub watch: Option>, +} + + diff --git a/shared/src/model/config/trakt.rs b/shared/src/model/config/trakt.rs new file mode 100644 index 000000000..92ec4d161 --- /dev/null +++ b/shared/src/model/config/trakt.rs @@ -0,0 +1,49 @@ +use serde::{Deserialize, Serialize}; + +fn default_fuzzy_threshold() -> u8 { + 80 +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +#[serde(rename_all = "lowercase")] +pub enum TraktContentType { + Vod, + Series, + Both, +} + +impl Default for TraktContentType { + fn default() -> Self { + Self::Both + } +} + +#[derive(Default, Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct TraktApiConfigDto { + #[serde(default)] + pub(crate) key: String, + #[serde(default)] + pub(crate) version: String, + #[serde(default)] + pub(crate) url: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct TraktListConfigDto { + pub user: String, + pub list_slug: String, + pub category_name: String, + pub content_type: TraktContentType, + #[serde(default = "default_fuzzy_threshold")] + pub fuzzy_match_threshold: u8, // Percentage (0-100) +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct TraktConfigDto { + #[serde(default)] + pub api: TraktApiConfigDto, + pub lists: Vec, +} diff --git a/shared/src/model/config/video.rs b/shared/src/model/config/video.rs new file mode 100644 index 000000000..d40afc9d1 --- /dev/null +++ b/shared/src/model/config/video.rs @@ -0,0 +1,25 @@ +use std::collections::HashMap; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct VideoDownloadConfigDto { + #[serde(default)] + pub headers: HashMap, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub directory: Option, + #[serde(default)] + pub organize_into_directories: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub episode_pattern: Option, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct VideoConfigDto { + #[serde(default)] + pub extensions: Vec, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub download: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub web_search: Option, +} diff --git a/shared/src/model/config/web.rs b/shared/src/model/config/web.rs new file mode 100644 index 000000000..20f40fc15 --- /dev/null +++ b/shared/src/model/config/web.rs @@ -0,0 +1,27 @@ +use crate::utils::{default_as_true}; + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] +#[serde(deny_unknown_fields)] +pub struct WebAuthConfigDto { + #[serde(default = "default_as_true")] + pub enabled: bool, + pub issuer: String, + pub secret: String, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub userfile: Option, +} + +#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] +pub struct WebUiConfigDto { + #[serde(default = "default_as_true")] + pub enabled: bool, + #[serde(default = "default_as_true")] + pub user_ui_enabled: bool, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub path: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub auth: Option, + #[serde(default, skip_serializing_if = "Option::is_none")] + pub player_server: Option, +} diff --git a/shared/src/model/item_field.rs b/shared/src/model/item_field.rs new file mode 100644 index 000000000..b03a44b0a --- /dev/null +++ b/shared/src/model/item_field.rs @@ -0,0 +1,83 @@ +use std::fmt::Display; +use std::str::FromStr; +use enum_iterator::Sequence; +use crate::create_tuliprox_error_result; +use crate::error::{TuliproxError, TuliproxErrorKind}; + +#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Sequence, Eq, PartialEq)] +pub enum ItemField { + #[serde(rename = "group")] + Group, + #[serde(rename = "name")] + Name, + #[serde(rename = "title")] + Title, + #[serde(rename = "url")] + Url, + #[serde(rename = "input")] + Input, + #[serde(rename = "type")] + Type, + #[serde(rename = "caption")] + Caption, +} + +impl ItemField { + const GROUP: &'static str = "Group"; + const NAME: &'static str = "Name"; + const TITLE: &'static str = "Title"; + const URL: &'static str = "Url"; + const INPUT: &'static str = "Input"; + const TYPE: &'static str = "Type"; + const CAPTION: &'static str = "Caption"; + + pub fn as_str(&self) -> &'static str { + match *self { + Self::Group => Self::GROUP, + Self::Name => Self::NAME, + Self::Title => Self::TITLE, + Self::Url => Self::URL, + Self::Input => Self::INPUT, + Self::Type => Self::TYPE, + Self::Caption => Self::CAPTION, + } + } +} + +impl Display for ItemField { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", match *self { + Self::Group => Self::GROUP, + Self::Name => Self::NAME, + Self::Title => Self::TITLE, + Self::Url => Self::URL, + Self::Input => Self::INPUT, + Self::Type => Self::TYPE, + Self::Caption => Self::CAPTION, + }) + } +} + +impl FromStr for ItemField { + type Err = TuliproxError; + + fn from_str(s: &str) -> Result { + if s.eq_ignore_ascii_case(Self::GROUP) { + Ok(Self::Group) + } else if s.eq_ignore_ascii_case(Self::NAME) { + Ok(Self::Name) + } else if s.eq_ignore_ascii_case(Self::TITLE) { + Ok(Self::Title) + } else if s.eq_ignore_ascii_case(Self::CAPTION) { + Ok(Self::Caption) + } else if s.eq_ignore_ascii_case(Self::URL) { + Ok(Self::Url) + } else if s.eq_ignore_ascii_case(Self::INPUT) { + Ok(Self::Input) + } else if s.eq_ignore_ascii_case(Self::TYPE) { + Ok(Self::Type) + } else { + create_tuliprox_error_result!(TuliproxErrorKind::Info, "Unknown InputType: {}", s) + } + } +} diff --git a/shared/src/model/messaging.rs b/shared/src/model/messaging.rs new file mode 100644 index 000000000..d6affab47 --- /dev/null +++ b/shared/src/model/messaging.rs @@ -0,0 +1,12 @@ + +#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq)] +pub enum MsgKind { + #[serde(rename = "info")] + Info, + #[serde(rename = "stats")] + Stats, + #[serde(rename = "error")] + Error, + #[serde(rename = "watch")] + Watch, +} \ No newline at end of file diff --git a/shared/src/model/mod.rs b/shared/src/model/mod.rs index b5b0b3e69..e1fba712c 100644 --- a/shared/src/model/mod.rs +++ b/shared/src/model/mod.rs @@ -1,5 +1,17 @@ mod cluster_flags; mod playlist; +mod messaging; +mod item_field; +mod target_type; +mod processing_order; +mod config; +mod strm_export_style; pub use self::cluster_flags::*; -pub use self::playlist::*; \ No newline at end of file +pub use self::playlist::*; +pub use self::messaging::*; +pub use self::item_field::*; +pub use self::target_type::*; +pub use self::processing_order::*; +pub use self::config::*; +pub use self::strm_export_style::*; \ No newline at end of file diff --git a/backend/src/model/processing_order.rs b/shared/src/model/processing_order.rs similarity index 100% rename from backend/src/model/processing_order.rs rename to shared/src/model/processing_order.rs diff --git a/shared/src/model/strm_export_style.rs b/shared/src/model/strm_export_style.rs new file mode 100644 index 000000000..30bcc530b --- /dev/null +++ b/shared/src/model/strm_export_style.rs @@ -0,0 +1,15 @@ +use enum_iterator::Sequence; + +#[derive( Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Sequence, + PartialEq, Eq, Hash, Default)] +pub enum StrmExportStyle { + #[serde(rename = "kodi")] + #[default] + Kodi, + #[serde(rename = "plex")] + Plex, + #[serde(rename = "emby")] + Emby, + #[serde(rename = "jellyfin")] + Jellyfin, +} \ No newline at end of file diff --git a/shared/src/model/target_type.rs b/shared/src/model/target_type.rs new file mode 100644 index 000000000..405d47ba2 --- /dev/null +++ b/shared/src/model/target_type.rs @@ -0,0 +1,32 @@ +use std::fmt::Display; +use enum_iterator::Sequence; + +#[derive(Debug, Copy, Clone, serde::Serialize, serde::Deserialize, Sequence, PartialEq, Eq, Hash)] +pub enum TargetType { + #[serde(rename = "m3u")] + M3u, + #[serde(rename = "xtream")] + Xtream, + #[serde(rename = "strm")] + Strm, + #[serde(rename = "hdhomerun")] + HdHomeRun, +} + +impl TargetType { + const M3U: &'static str = "M3u"; + const XTREAM: &'static str = "Xtream"; + const STRM: &'static str = "Strm"; + const HDHOMERUN: &'static str = "HdHomeRun"; +} + +impl Display for TargetType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{}", match *self { + Self::M3u => Self::M3U, + Self::Xtream => Self::XTREAM, + Self::Strm => Self::STRM, + Self::HdHomeRun => Self::HDHOMERUN, + }) + } +} \ No newline at end of file