From 4e7f4676a470060a680e4171f007f6cbd3a5cd3b Mon Sep 17 00:00:00 2001 From: euzu Date: Wed, 17 Jan 2024 13:50:49 +0100 Subject: [PATCH] Fixed "watch" file creation problem, fixed immediately response after ui update request --- CHANGELOG.md | 5 +++++ TODO.md | 4 +--- src/api/v1_api.rs | 2 +- src/model/config.rs | 29 +++++++++++++++++++--------- src/processing/playlist_processor.rs | 11 +++++------ src/processing/playlist_watch.rs | 3 +++ 6 files changed, 35 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0cd76b09c..8c7a7df02 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,9 @@ # Changelog +# v1.1.6(2024-xx-xx) +* watch filter are now regular expressions +* Fixed watch file not created problem +* UI responds immediately to update request + # v1.1.5(2024-01-11) * Changed api-proxy user default proxy type from `reverse` to `redirect` * Added `xtream_resolve_series` and `xtream_resolve_series_delay` option for `m3u` target diff --git a/TODO.md b/TODO.md index e636008b8..9f7fa2ba0 100644 --- a/TODO.md +++ b/TODO.md @@ -3,8 +3,7 @@ - xtream api, only looks for first input configuration. But if you have multiple xtream sources merged, - each entry should have a pointer to his input to get the right provider url. -- + each entry should have a pointer to his input to get the right provider url. - double download from web-ui possible @@ -13,7 +12,6 @@ - download from web-ui is not cancellable - resume download if available - display file size from header if available -- on web-ui page refresh download list should be fetched ## Open topics - xmltv api delivers original content if no epg_url is provided for input for reverse or makes redirect. diff --git a/src/api/v1_api.rs b/src/api/v1_api.rs index 8b672711f..694976a69 100644 --- a/src/api/v1_api.rs +++ b/src/api/v1_api.rs @@ -93,7 +93,7 @@ pub(crate) async fn playlist_update( let process_targets = validate_targets(&user_targets, &_app_state.config.sources); match process_targets { Ok(valid_targets) => { - exec_processing(Arc::clone(&_app_state.config), Arc::new(valid_targets)).await; + actix_rt::spawn(exec_processing(Arc::clone(&_app_state.config), Arc::new(valid_targets))); HttpResponse::Ok().finish() } Err(err) => { diff --git a/src/model/config.rs b/src/model/config.rs index 46ca2806f..b2676c251 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -197,6 +197,8 @@ pub(crate) struct ConfigTarget { #[serde(skip_serializing_if = "Option::is_none")] pub watch: Option>, #[serde(skip_serializing, skip_deserializing)] + pub _watch_re: Option>, + #[serde(skip_serializing, skip_deserializing)] pub _filter: Option, #[serde(skip_serializing, skip_deserializing)] pub _mapping: Option>, @@ -244,6 +246,16 @@ impl ConfigTarget { return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Multiple output formats with same type : {}", self.name); } + if let Some(watch) = &self.watch { + let regexps: Result, _> = watch.iter().map(|s| regex::Regex::new(s)).collect(); + match regexps { + Ok(watch_re) => self._watch_re = Some(watch_re), + Err(err) => { + return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Invalid watch regular expression: {}", err); + } + } + } + match get_filter(&self.filter, templates) { Ok(fltr) => { debug!("Filter: {}", fltr); @@ -297,12 +309,12 @@ impl ConfigSource { Ok(index + (self.inputs.len() as u16)) } - pub(crate)fn get_input_for_target(&self, target_name: &str, input_type: &InputType) -> Option<&ConfigInput> { + pub(crate) fn get_input_for_target(&self, target_name: &str, input_type: &InputType) -> Option<&ConfigInput> { for target in &self.targets { if target.name.eq(target_name) { for input in &self.inputs { if input.input_type.eq(input_type) { - return Some(input) + return Some(input); } } } @@ -481,7 +493,7 @@ pub(crate) struct VideoConfig { #[serde(skip_serializing_if = "Option::is_none")] pub download: Option, #[serde(skip_serializing_if = "Option::is_none")] - pub web_search: Option + pub web_search: Option, } impl VideoConfig { @@ -526,7 +538,6 @@ pub(crate) struct ConfigDto { } impl ConfigDto { - pub fn is_valid(&self) -> bool { if self.api.host.is_empty() { return false; @@ -535,7 +546,7 @@ impl ConfigDto { 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() { + if !episode_pattern.is_empty() { let re = regex::Regex::new(episode_pattern); if re.is_err() { return false; @@ -591,7 +602,7 @@ impl Config { pub(crate) fn get_input_for_target(&self, target_name: &str, input_type: &InputType) -> Option<&ConfigInput> { for source in &self.sources { - if let Some(cfg) = source.get_input_for_target(target_name, input_type) { return Some(cfg) } + if let Some(cfg) = source.get_input_for_target(target_name, input_type) { return Some(cfg); } } None } @@ -651,10 +662,10 @@ impl Config { self.backup_dir = Some(PathBuf::from(&self.working_dir).join(".backup").into_os_string().to_string_lossy().to_string()); } let backupdir = PathBuf::from(self.backup_dir.as_ref().unwrap()); - if ! backupdir.exists() { + if !backupdir.exists() { match std::fs::create_dir(backupdir) { Ok(_) => {} - Err(err) => { error!("Could not create backup dir {} {}", self.backup_dir.as_ref().unwrap(), err)} + Err(err) => { error!("Could not create backup dir {} {}", self.backup_dir.as_ref().unwrap(), err) } } } self.api.prepare(); @@ -709,7 +720,7 @@ impl Config { Ok(_) => {} Err(err) => return Err(err) } - }, + } }; Ok(()) } diff --git a/src/processing/playlist_processor.rs b/src/processing/playlist_processor.rs index 256391707..6f0373f8f 100644 --- a/src/processing/playlist_processor.rs +++ b/src/processing/playlist_processor.rs @@ -119,8 +119,7 @@ fn sort_playlist(target: &ConfigTarget, new_playlist: &mut [PlaylistGroup]) { let regexp = channel_sort.re.as_ref().unwrap(); new_playlist.iter_mut().for_each(|group| { let group_title = if *match_as_ascii { Rc::new(unidecode(&group.title)) } else { Rc::clone(&group.title) }; - let is_match = regexp.is_match(group_title.as_str()); - if is_match { + if regexp.is_match(group_title.as_str()) { group.channels.sort_by(|a, b| { let raw_value_a = get_field_value(a, &channel_sort.field); let raw_value_b = get_field_value(b, &channel_sort.field); @@ -489,14 +488,14 @@ pub(crate) async fn process_playlist<'a>(playlists: &mut [FetchedPlaylist<'a>], if !new_playlist.is_empty() { sort_playlist(target, &mut new_playlist); - if target.watch.is_some() { + if target._watch_re.is_some() { if default_as_default().eq_ignore_ascii_case(&target.name) { error!("cant watch a target with no unique name"); } else { - let titles = target.watch.as_ref().unwrap(); + let watch_re = target._watch_re.as_ref().unwrap(); new_playlist.iter().for_each(|pl| { - if titles.contains(&pl.title) { - process_group_watch(cfg, &target.name, pl) + if watch_re.iter().any(|r| r.is_match(&pl.title)) { + process_group_watch(cfg, &target.name, pl) } }); } diff --git a/src/processing/playlist_watch.rs b/src/processing/playlist_watch.rs index 78b4883ef..bf4d0890f 100644 --- a/src/processing/playlist_watch.rs +++ b/src/processing/playlist_watch.rs @@ -35,8 +35,11 @@ pub(crate) fn process_group_watch(cfg: &Config, target_name: &str, pl: &Playlist } None => { error!("failed to load watch_file {}", &path.to_str().unwrap_or_default()); + changed = true; } } + } else { + changed = true; } if changed { match save_watch_tree(&save_path, new_tree) {