Files
tuliprox/shared/src/model/config/base.rs
T
2025-12-26 14:09:44 +01:00

265 lines
9.0 KiB
Rust

use crate::error::{TuliproxError, TuliproxErrorKind};
use crate::model::{ConfigApiDto, HdHomeRunConfigDto, IpCheckConfigDto, LibraryConfigDto, LogConfigDto, MessagingConfigDto, ProxyConfigDto, ReverseProxyConfigDto, ScheduleConfigDto, VideoConfigDto, WebUiConfigDto, DEFAULT_VIDEO_EXTENSIONS};
use crate::utils::default_connect_timeout_secs;
pub const DEFAULT_USER_AGENT: &str = "VLC/3.0.16 LibVLC/3.0.16";
#[allow(clippy::struct_excessive_bools)]
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct ConfigDto {
#[serde(default)]
pub process_parallel: bool,
pub api: ConfigApiDto,
pub working_dir: String,
#[serde(default)]
pub backup_dir: Option<String>,
#[serde(default)]
pub user_config_dir: Option<String>,
#[serde(default)]
pub mapping_path: Option<String>,
#[serde(default)]
pub custom_stream_response_path: Option<String>,
#[serde(default)]
pub video: Option<VideoConfigDto>,
#[serde(default)]
pub schedules: Option<Vec<ScheduleConfigDto>>,
#[serde(default)]
pub log: Option<LogConfigDto>,
#[serde(default)]
pub user_access_control: bool,
#[serde(default = "default_connect_timeout_secs")]
pub connect_timeout_secs: u32,
#[serde(default)]
pub sleep_timer_mins: Option<u32>,
#[serde(default)]
pub update_on_boot: bool,
#[serde(default)]
pub config_hot_reload: bool,
#[serde(default)]
pub accept_insecure_ssl_certificates: bool,
#[serde(default)]
pub web_ui: Option<WebUiConfigDto>,
#[serde(default)]
pub messaging: Option<MessagingConfigDto>,
#[serde(default)]
pub reverse_proxy: Option<ReverseProxyConfigDto>,
#[serde(default)]
pub hdhomerun: Option<HdHomeRunConfigDto>,
#[serde(default)]
pub proxy: Option<ProxyConfigDto>,
#[serde(default)]
pub ipcheck: Option<IpCheckConfigDto>,
#[serde(default)]
pub library: Option<LibraryConfigDto>,
}
// This MainConfigDto is a copy of ConfigDto simple fields for form editing.
// It has no other purpose than editing and saving the simple config values
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, PartialEq)]
pub struct MainConfigDto {
#[serde(default)]
pub process_parallel: bool,
pub working_dir: String,
#[serde(default)]
pub backup_dir: Option<String>,
#[serde(default)]
pub user_config_dir: Option<String>,
#[serde(default)]
pub mapping_path: Option<String>,
#[serde(default)]
pub custom_stream_response_path: Option<String>,
#[serde(default)]
pub user_access_control: bool,
#[serde(default = "default_connect_timeout_secs")]
pub connect_timeout_secs: u32,
#[serde(default)]
pub sleep_timer_mins: Option<u32>,
#[serde(default)]
pub update_on_boot: bool,
#[serde(default)]
pub config_hot_reload: bool,
#[serde(default)]
pub accept_insecure_ssl_certificates: bool,
}
impl Default for MainConfigDto {
fn default() -> Self {
MainConfigDto {
process_parallel: false,
working_dir: String::new(),
backup_dir: None,
user_config_dir: None,
mapping_path: None,
custom_stream_response_path: None,
user_access_control: false,
connect_timeout_secs: default_connect_timeout_secs(),
sleep_timer_mins: None,
update_on_boot: false,
config_hot_reload: false,
accept_insecure_ssl_certificates: false,
}
}
}
impl From<&ConfigDto> for MainConfigDto {
fn from(config: &ConfigDto) -> Self {
Self {
process_parallel: config.process_parallel,
working_dir: config.working_dir.clone(),
backup_dir: config.backup_dir.clone(),
user_config_dir: config.user_config_dir.clone(),
mapping_path: config.mapping_path.clone(),
custom_stream_response_path: config.custom_stream_response_path.clone(),
user_access_control: config.user_access_control,
connect_timeout_secs: config.connect_timeout_secs,
sleep_timer_mins: config.sleep_timer_mins,
update_on_boot: config.update_on_boot,
config_hot_reload: config.config_hot_reload,
accept_insecure_ssl_certificates: config.accept_insecure_ssl_certificates,
}
}
}
// This SchedulesConfigDto is a copy of ConfigDto schedules fields for form editing.
// It has no other purpose than editing and saving the schedules
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default, PartialEq)]
pub struct SchedulesConfigDto {
#[serde(default)]
pub schedules: Option<Vec<ScheduleConfigDto>>,
}
impl SchedulesConfigDto {
pub fn is_empty(&self) -> bool {
self.schedules.is_none() || self.schedules.as_ref().unwrap().is_empty()
}
}
impl From<&ConfigDto> for SchedulesConfigDto {
fn from(config: &ConfigDto) -> Self {
Self {
schedules: config.schedules.clone(),
}
}
}
pub struct HdHomeRunDeviceOverview {
pub enabled: bool,
pub devices: Vec<String>,
}
impl ConfigDto {
pub fn prepare(&mut self, include_computed: bool) -> Result<(), TuliproxError> {
if let Some(mins) = self.sleep_timer_mins {
if mins == 0 {
return Err(TuliproxError::new(TuliproxErrorKind::Info, "`sleep_timer_mins` must be > 0 when specified".to_string()));
}
}
self.api.prepare();
self.prepare_web()?;
self.prepare_hdhomerun(include_computed)?;
self.prepare_video_config()?;
if let Some(reverse_proxy) = self.reverse_proxy.as_mut() {
reverse_proxy.prepare(&self.working_dir)?;
}
if let Some(proxy) = &mut self.proxy {
proxy.prepare()?;
}
if let Some(ipcheck) = self.ipcheck.as_mut() {
ipcheck.prepare()?;
}
Ok(())
}
fn prepare_web(&mut self) -> Result<(), TuliproxError> {
if let Some(web_ui_config) = self.web_ui.as_mut() {
web_ui_config.prepare()?;
}
Ok(())
}
fn prepare_hdhomerun(&mut self, include_computed: bool) -> Result<(), TuliproxError> {
if let Some(hdhomerun) = &mut self.hdhomerun {
if hdhomerun.enabled {
hdhomerun.prepare(self.api.port, include_computed)?;
}
}
Ok(())
}
fn prepare_video_config(&mut self) -> Result<(), TuliproxError> {
match &mut self.video {
None => {
self.video = Some(VideoConfigDto {
extensions: DEFAULT_VIDEO_EXTENSIONS.iter().map(ToString::to_string).collect(),
download: None,
web_search: None,
});
}
Some(video) => {
match video.prepare() {
Ok(()) => {}
Err(err) => return Err(err)
}
}
}
Ok(())
}
pub fn is_valid(&self) -> bool {
if self.api.host.is_empty() {
return false;
}
if let Some(video) = &self.video {
if let Some(download) = &video.download {
if let Some(episode_pattern) = &download.episode_pattern {
if !episode_pattern.is_empty() {
let re = regex::Regex::new(episode_pattern);
if re.is_err() {
return false;
}
}
}
}
}
true
}
pub fn get_hdhr_device_overview(&self) -> Option<HdHomeRunDeviceOverview> {
self.hdhomerun.as_ref().map(|hdhr|
HdHomeRunDeviceOverview {
enabled: hdhr.enabled,
devices: hdhr.devices.iter().map(|d| d.name.to_string()).collect::<Vec<String>>(),
})
}
pub fn update_from_main_config(&mut self, main_config: &MainConfigDto) {
self.process_parallel = main_config.process_parallel;
self.working_dir = main_config.working_dir.clone();
self.backup_dir = main_config.backup_dir.clone();
self.user_config_dir = main_config.user_config_dir.clone();
self.mapping_path = main_config.mapping_path.clone();
self.custom_stream_response_path = main_config.custom_stream_response_path.clone();
self.user_access_control = main_config.user_access_control;
self.connect_timeout_secs = main_config.connect_timeout_secs;
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_insecure_ssl_certificates = main_config.accept_insecure_ssl_certificates;
}
pub fn is_geoip_enabled(&self) -> bool {
self.reverse_proxy.as_ref().is_some_and(|r| r.geoip.as_ref().is_some_and(|g| g.enabled))
}
pub fn is_library_enabled(&self) -> bool {
self.library.as_ref().is_some_and(|l| l.enabled)
}
}