diff --git a/bin/build_github_docker_beta.sh b/bin/build_github_docker_beta.sh index ce8a51101..bc27bcab5 100755 --- a/bin/build_github_docker_beta.sh +++ b/bin/build_github_docker_beta.sh @@ -60,7 +60,7 @@ docker tag ghcr.io/euzu/${BETA_IMAGE_NAME}:"${VERSION}" ghcr.io/euzu/${BETA_IMAG echo "Logging into GitHub Container Registry..." docker login ghcr.io -u euzu -p "${GHCR_IO_TOKEN}" -# Push alipne +# Push alpine docker push ghcr.io/euzu/${BETA_IMAGE_NAME}:"${VERSION}" docker push ghcr.io/euzu/${BETA_IMAGE_NAME}:latest @@ -70,4 +70,4 @@ rm -rf "${DOCKER_DIR}/web" rm -f "${DOCKER_DIR}/tuliprox" rm -rf "${DOCKER_DIR}/resources" -echo "Docker images ghcr.io/euzu/${BETA_IMAGE_NAME}${VERSION} have been successfully built, tagged, and pushed." +echo "Docker images ghcr.io/euzu/${BETA_IMAGE_NAME} with version ${VERSION} have been successfully built, tagged, and pushed." diff --git a/src/api/endpoints/m3u_api.rs b/src/api/endpoints/m3u_api.rs index 2a39fcc61..becb6fb61 100644 --- a/src/api/endpoints/m3u_api.rs +++ b/src/api/endpoints/m3u_api.rs @@ -172,9 +172,9 @@ async fn m3u_api_resource( Some(url) => { if user.proxy.is_redirect(m3u_item.item_type) || target.is_force_redirect(m3u_item.item_type) { debug!("Redirecting stream request to {}", sanitize_sensitive_info(&url)); - redirect(url.as_str()).into_response() + redirect(&url).into_response() } else { - resource_response(&app_state, url.as_str(), &req_headers, None).await.into_response() + resource_response(&app_state, &url, &req_headers, None).await.into_response() } } } diff --git a/src/api/endpoints/v1_api.rs b/src/api/endpoints/v1_api.rs index 61081c6ca..180cb8338 100644 --- a/src/api/endpoints/v1_api.rs +++ b/src/api/endpoints/v1_api.rs @@ -293,7 +293,7 @@ async fn config( app_state.config.t_config_file_path.as_str(), app_state.config.t_sources_file_path.as_str(), app_state.config.t_api_proxy_file_path.as_str(), - app_state.config.t_mapping_file_path.as_str(), + Some(app_state.config.t_mapping_file_path.to_string()), false) { Ok(mut cfg) => { let _ = cfg.prepare(false); diff --git a/src/api/endpoints/xtream_api.rs b/src/api/endpoints/xtream_api.rs index a9ba60cd5..000690bbe 100644 --- a/src/api/endpoints/xtream_api.rs +++ b/src/api/endpoints/xtream_api.rs @@ -1,5 +1,6 @@ // https://github.com/tellytv/go.xtream-codes/blob/master/structs.go +use std::borrow::Cow; use crate::api::api_utils; use crate::api::api_utils::{get_user_target, get_user_target_by_credentials, is_seek_request, redirect_response, resource_response, force_provider_stream_response, separate_number_and_remainder, serve_file, stream_response, RedirectParams, read_session_token}; use crate::api::api_utils::{redirect, try_option_bad_request, try_result_bad_request}; @@ -336,18 +337,18 @@ fn get_doc_id_and_field_name(input: &str) -> Option<(u32, &str)> { None } -fn get_doc_resource_field_value(field: &str, doc: Option<&Value>) -> Option { +fn get_doc_resource_field_value<'a>(field: &'a str, doc: Option<&'a Value>) -> Option> { if let Some(Value::Object(info_data)) = doc { if field.starts_with(crate::model::XC_PROP_BACKDROP_PATH) { return get_backdrop_path_value(field, info_data.get(crate::model::XC_PROP_BACKDROP_PATH)); } else if let Some(Value::String(url)) = info_data.get(field) { - return Some(url.to_string()); + return Some(Cow::Borrowed(url)); } } None } -fn xtream_get_info_resource_url(config: &Config, pli: &XtreamPlaylistItem, target: &ConfigTarget, resource: &str) -> Result, serde_json::Error> { +fn xtream_get_info_resource_url<'a>(config: &'a Config, pli: &'a XtreamPlaylistItem, target: &'a ConfigTarget, resource: &'a str) -> Result>, serde_json::Error> { let info_content = match pli.xtream_cluster { XtreamCluster::Video => { xtream_repository::xtream_load_vod_info(config, target.name.as_str(), pli.get_virtual_id()) @@ -380,7 +381,7 @@ fn xtream_get_info_resource_url(config: &Config, pli: &XtreamPlaylistItem, targe }; if let Some(value) = get_doc_resource_field_value(field, info_doc) { - return Ok(Some(value)); + return Ok(Some(Cow::Owned(value.into_owned()))); } } Ok(None) @@ -422,7 +423,7 @@ fn get_season_info_doc(doc: &Vec, season_id: u32) -> Option<&Value> { } -fn xtream_get_season_resource_url(config: &Config, pli: &XtreamPlaylistItem, target: &ConfigTarget, resource: &str) -> Result, serde_json::Error> { +fn xtream_get_season_resource_url<'a>(config: &'a Config, pli: &'a XtreamPlaylistItem, target: &'a ConfigTarget, resource: &'a str) -> Result>, serde_json::Error> { let info_content = match pli.xtream_cluster { XtreamCluster::Series => { xtream_repository::xtream_load_series_info(config, target.name.as_str(), pli.get_virtual_id()) @@ -441,7 +442,7 @@ fn xtream_get_season_resource_url(config: &Config, pli: &XtreamPlaylistItem, tar if let Some(Value::Array(seasons)) = seasons_doc { if let Some(value) = get_doc_resource_field_value(field, get_season_info_doc(seasons, season_id)) { - return Ok(Some(value)); + return Ok(Some(Cow::Owned(value.into_owned()))); } } } @@ -481,10 +482,10 @@ async fn xtream_player_api_resource( Some(url) => { if user.proxy.is_redirect(pli.item_type) || target.is_force_redirect(pli.item_type) { trace_if_enabled!("Redirecting resource request to {}", sanitize_sensitive_info(&url)); - redirect(url.as_str()).into_response() + redirect(&url).into_response() } else { trace_if_enabled!("Resource request to {}", sanitize_sensitive_info(&url)); - resource_response(app_state, url.as_str(), req_headers, None).await.into_response() + resource_response(app_state, &url, req_headers, None).await.into_response() } } } diff --git a/src/foundation/filter.rs b/src/foundation/filter.rs index 647bc1f89..908b5beef 100644 --- a/src/foundation/filter.rs +++ b/src/foundation/filter.rs @@ -1,5 +1,6 @@ #![allow(clippy::empty_docs)] +use std::borrow::Cow; use enum_iterator::all; use indexmap::IndexSet; use log::{debug, error, log_enabled, trace, Level}; @@ -8,7 +9,7 @@ use pest::Parser; use std::cmp::Ordering; use std::collections::HashMap; -use crate::model::{ItemField}; +use crate::model::{FieldGetAccessor, FieldSetAccessor, ItemField}; use crate::model::{PlaylistItem, PlaylistItemType}; use crate::tools::directed_graph::DirectedGraph; use crate::tuliprox_error::{create_tuliprox_error_result, info_err}; @@ -51,8 +52,8 @@ pub struct ValueProvider<'a> { } impl ValueProvider<'_> { - pub fn get(&self, field: &ItemField) -> String { - get_field_value(self.pli, field) + pub fn get(&self, field: &str) -> Option> { + self.pli.header.get_field(field) } } @@ -61,12 +62,12 @@ pub struct ValueAccessor<'a> { } impl ValueAccessor<'_> { - pub fn get(&self, field: &ItemField) -> String { - get_field_value(self.pli, field) + pub fn get(&self, field: &str) -> Option> { + self.pli.header.get_field(field) } - pub fn set(&mut self, field: &ItemField, value: &str) { - if set_field_value(self.pli, field, value.to_string()) { + pub fn set(&mut self, field: &str, value: &str) { + if self.pli.header.set_field(field, value) { trace!("Property {field} set to {value}"); } else { error!("Can't set unknown field {field} set to {value}"); @@ -167,44 +168,57 @@ pub enum Filter { BinaryExpression(Box, BinaryOperator, Box), } +fn get_caption<'a>(provider: &'a ValueProvider<'a>, rewc: &'a RegexWithCaptures) -> (bool, Cow<'a, str>) { + if let Some(value) = provider.get("title") { + if rewc.re.is_match(&value) { + return (true, value); + } + } + + if let Some(value) = provider.get("title") { + if rewc.re.is_match(&value) { + return (true, value); + } + } + (false, Cow::Borrowed("")) +} + impl Filter { pub fn filter(&self, provider: &ValueProvider) -> bool { match self { Self::FieldComparison(field, rewc) => { let (is_match, value) = if field == &ItemField::Caption { - let value = provider.get(&ItemField::Title); - if rewc.re.is_match(value.as_str()) { - (true, value) - } else { - let value = provider.get(&ItemField::Name); - (rewc.re.is_match(value.as_str()), value) - } + get_caption(provider, rewc) + } else if let Some(value) = provider.get(field.as_str()) { + (rewc.re.is_match(&value), value) } else { - let value = provider.get(field); - (rewc.re.is_match(value.as_str()), value) + (false, Cow::Borrowed("")) }; if log_enabled!(Level::Trace) { if is_match { - debug!("Match found: {:?} {} => {}={}", &rewc, &rewc.restr, &field, &value); + debug!("Match found: {rewc:?} {} => {field}={value}", &rewc.restr); } else { - debug!("Match failed: {self}: {:?} {} => {}={}", &rewc, &rewc.restr, &field, &value); + debug!("Match failed: {self}: {rewc:?} {} => {field}={value}", &rewc.restr); } } is_match } Self::TypeComparison(field, item_type) => { - let value = provider.get(field); - get_filter_item_type(value.as_str()).is_some_and(|pli_type| { - let is_match = pli_type.eq(item_type); - if log_enabled!(Level::Trace) { - if is_match { - debug!("Match found: {:?} {}", &field, value); - } else { - debug!("Match failed: {self}: {:?} {}", &field, &value); + if let Some(value) = provider.get(field.as_str()) { + get_filter_item_type(&value).is_some_and(|pli_type| { + let is_match = pli_type.eq(item_type); + if log_enabled!(Level::Trace) { + if is_match { + debug!("Match found: {field:?} {value}"); + } else { + debug!("Match failed: {self}: {field:?} {value}"); + } } - } - is_match - }) + is_match + }) + } else { + false + } } Self::Group(expr) => expr.filter(provider), Self::UnaryExpression(op, expr) => match op { diff --git a/src/foundation/mapper.rs b/src/foundation/mapper.rs index 0fb11934a..173bf6acd 100644 --- a/src/foundation/mapper.rs +++ b/src/foundation/mapper.rs @@ -154,7 +154,7 @@ impl Statement { AssignmentTarget::Field(name) => { match val { Value(content) => { - setter.set(name, content.as_str()); + setter.set(name.as_str(), content.as_str()); } Named(pairs) => { let mut result = String::with_capacity(128); @@ -166,7 +166,7 @@ impl Statement { result.push_str(", "); } } - setter.set(name, &result); + setter.set(name.as_str(), &result); } Undefined | AnyValue => {} Failure(err) => { @@ -610,29 +610,31 @@ impl Expression { } Expression::StringLiteral(s) => Value(s.clone()), Expression::RegexExpr { field, pattern: _pattern, re_pattern } => { - let val = accessor.get(field); - let mut values = vec![]; - for caps in re_pattern.captures_iter(&val) { - // Positional groups - for i in 1..caps.len() { - if let Some(m) = caps.get(i) { - values.push((i.to_string(), m.as_str().to_string())); + if let Some(val) = accessor.get(field.as_str()) { + let mut values = vec![]; + for caps in re_pattern.captures_iter(&val) { + // Positional groups + for i in 1..caps.len() { + if let Some(m) = caps.get(i) { + values.push((i.to_string(), m.as_str().to_string())); + } } - } - // named groups - for name in re_pattern.capture_names().flatten() { - if let Some(m) = caps.name(name) { - values.push((name.to_string(), m.as_str().to_string())); + // named groups + for name in re_pattern.capture_names().flatten() { + if let Some(m) = caps.name(name) { + values.push((name.to_string(), m.as_str().to_string())); + } } } + if values.is_empty() { + return Undefined; + } else if values.len() == 1 { + return Value(values[0].1.to_string()); + } + return Named(values); } - if values.is_empty() { - return Undefined; - } else if values.len() == 1 { - return Value(values[0].1.to_string()); - } - Named(values) + Undefined } Expression::FunctionCall { name, args } => { let mut evaluated_args: Vec = args.iter().map(|a| a.eval(ctx, accessor)).collect(); diff --git a/src/main.rs b/src/main.rs index b8a32dfd6..7228fa570 100644 --- a/src/main.rs +++ b/src/main.rs @@ -94,7 +94,7 @@ fn main() { let config_path: String = utils::resolve_directory_path(&resolve_env_var(&args.config_path.unwrap_or_else(utils::get_default_config_path))); let config_file: String = resolve_env_var(&args.config_file.unwrap_or_else(|| utils::get_default_config_file_path(&config_path))); let api_proxy_file = resolve_env_var(&args.api_proxy.unwrap_or_else(|| utils::get_default_api_proxy_config_path(config_path.as_str()))); - let mappings_file = resolve_env_var(&args.mapping_file.unwrap_or_else(|| utils::get_default_mappings_path(config_path.as_str()))); + let mappings_file = args.mapping_file.as_ref(); init_logger(args.log_level.as_ref(), config_file.as_str()); @@ -110,7 +110,7 @@ fn main() { let sources_file: String = args.source_file.unwrap_or_else(|| utils::get_default_sources_file_path(&config_path)); let cfg = utils::read_config(config_path.as_str(), config_file.as_str(), sources_file.as_str(), api_proxy_file.as_str(), - mappings_file.as_str(), true).unwrap_or_else(|err| exit!("{}", err)); + mappings_file.cloned(), true).unwrap_or_else(|err| exit!("{}", err)); set_sanitize_sensitive_info(cfg.log.as_ref().is_none_or(|l| l.sanitize_sensitive_info)); @@ -127,7 +127,7 @@ fn main() { info!("Config file: {:?}", &cfg.t_config_file_path); info!("Source file: {:?}", &cfg.t_sources_file_path); info!("Api Proxy File: {:?}", cfg.t_api_proxy_file_path); - match utils::read_mappings(mappings_file.as_str(), true) { + match utils::read_mappings(&cfg.t_mapping_file_path, true) { Ok(Some(mappings)) => { info!("Mapping file: {:?}", cfg.t_mapping_file_path); cfg.set_mappings(&mappings); diff --git a/src/model/config.rs b/src/model/config.rs index 92b1e18c3..74ffc8c52 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -65,6 +65,18 @@ impl ItemField { 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 { diff --git a/src/model/playlist.rs b/src/model/playlist.rs index ddd772e24..ddaae1d8a 100644 --- a/src/model/playlist.rs +++ b/src/model/playlist.rs @@ -1,16 +1,17 @@ -use std::cmp::PartialEq; -use std::fmt::{Display, Formatter}; -use std::str::FromStr; -use crate::model::ProxyUserCredentials; -use crate::model::{ConfigInput, ConfigTargetOptions}; -use crate::model::TVGuide; -use crate::model::xtream_const; use crate::model::xtream::{xtream_playlistitem_to_document, XtreamMappingOptions}; +use crate::model::xtream_const; +use crate::model::ProxyUserCredentials; +use crate::model::TVGuide; +use crate::model::{ConfigInput, ConfigTargetOptions}; +use crate::utils::request::extract_extension_from_url; +use crate::utils::{generate_playlist_uuid, get_provider_id}; use crate::utils::{get_string_from_serde_value, get_u64_from_serde_value}; use serde::{Deserialize, Serialize}; use serde_json::{Map, Value}; -use crate::utils::{generate_playlist_uuid, get_provider_id}; -use crate::utils::request::extract_extension_from_url; +use std::borrow::Cow; +use std::cmp::PartialEq; +use std::fmt::{Display, Formatter}; +use std::str::FromStr; // https://de.wikipedia.org/wiki/M3U // https://siptv.eu/howto/playlist.html @@ -140,7 +141,7 @@ impl PlaylistItemType { impl Display for PlaylistItemType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", match self { - Self::Live | Self::LiveHls | Self::LiveDash |Self::LiveUnknown => Self::LIVE, + Self::Live | Self::LiveHls | Self::LiveDash | Self::LiveUnknown => Self::LIVE, Self::Video => Self::VIDEO, Self::Series => Self::SERIES, Self::SeriesInfo => Self::SERIES_INFO, @@ -150,7 +151,7 @@ impl Display for PlaylistItemType { } pub trait FieldGetAccessor { - fn get_field(&self, field: &str) -> Option; + fn get_field(&self, field: &str) -> Option>; } pub trait FieldSetAccessor { fn set_field(&mut self, field: &str, value: &str) -> bool; @@ -254,21 +255,23 @@ macro_rules! to_m3u_resource_non_empty_fields { macro_rules! generate_field_accessor_impl_for_playlist_item_header { ($($prop:ident),*;) => { impl FieldGetAccessor for PlaylistItemHeader { - fn get_field(&self, field: &str) -> Option { - match field { + fn get_field(&self, field: &str) -> Option> { + let field = field.to_lowercase(); + match field.as_str() { $( - stringify!($prop) => Some(self.$prop.clone()), + stringify!($prop) => Some(Cow::Borrowed(&self.$prop)), )* - "caption" => Some(if self.title.is_empty() { self.name.clone() } else { self.title.clone() }), - "epg_channel_id" | "epg_id" => self.epg_channel_id.clone(), + "caption" => Some(if self.title.is_empty() { Cow::Borrowed(&self.name) } else { Cow::Borrowed(&self.title) }), + "epg_channel_id" | "epg_id" => self.epg_channel_id.as_ref().map(|s| Cow::Borrowed(s.as_str())), _ => None, } } } impl FieldSetAccessor for PlaylistItemHeader { fn set_field(&mut self, field: &str, value: &str) -> bool { + let field = field.to_lowercase(); let val = String::from(value); - match field { + match field.as_str() { $( stringify!($prop) => { self.$prop = val; @@ -327,7 +330,7 @@ impl M3uPlaylistItem { self.name, self.group); if !ignore_logo { - if rewrite_urls && self.t_resource_url.is_some(){ + if rewrite_urls && self.t_resource_url.is_some() { to_m3u_resource_non_empty_fields!(self, self.t_resource_url.as_ref().unwrap(), line, (logo, "tvg-logo"), (logo_small, "tvg-logo-small");); } else { to_m3u_non_empty_fields!(self, line, (logo, "tvg-logo"), (logo_small, "tvg-logo-small");); @@ -372,18 +375,19 @@ impl PlaylistEntry for M3uPlaylistItem { fn get_item_type(&self) -> PlaylistItemType { self.item_type } - } macro_rules! generate_field_accessor_impl_for_m3u_playlist_item { ($($prop:ident),*;) => { impl FieldGetAccessor for M3uPlaylistItem { - fn get_field(&self, field: &str) -> Option { - match field { + fn get_field(&self, field: &str) -> Option> { + let field = field.to_lowercase(); + match field.as_str() { $( - stringify!($prop) => Some(self.$prop.clone()), + stringify!($prop) => Some(Cow::Borrowed(&self.$prop)), )* - "epg_channel_id" | "epg_id" => self.epg_channel_id.clone(), + "caption" => Some(if self.title.is_empty() { Cow::Borrowed(&self.name) } else { Cow::Borrowed(&self.title) }), + "epg_channel_id" | "epg_id" => self.epg_channel_id.as_ref().map(|s| Cow::Borrowed(s.as_str())), _ => None, } } @@ -422,7 +426,7 @@ impl XtreamPlaylistItem { pub fn get_additional_property(&self, field: &str) -> Option { if let Some(json) = self.additional_properties.as_ref() { if let Ok(Value::Object(props)) = serde_json::from_str(json) { - return props.get(field).cloned(); + return props.get(field).cloned(); } } None @@ -457,22 +461,22 @@ impl PlaylistEntry for XtreamPlaylistItem { } } -pub fn get_backdrop_path_value(field: &str, value: Option<&Value>) -> Option { +pub fn get_backdrop_path_value<'a>(field: &'a str, value: Option<&'a Value>) -> Option> { match value { - Some(Value::String(url)) => Some(url.clone()), + Some(Value::String(url)) => Some(Cow::Borrowed(url)), Some(Value::Array(values)) => { match values.as_slice() { - [Value::String(single)] => Some(single.to_string()), + [Value::String(single)] => Some(Cow::Borrowed(single)), multiple if !multiple.is_empty() => { if let Some(index) = field.rfind('_') { if let Ok(bd_index) = field[index + 1..].parse::() { if let Some(Value::String(selected)) = multiple.get(bd_index) { - return Some(selected.to_string()); + return Some(Cow::Borrowed(selected)); } } } if let Value::String(url) = &multiple[0] { - Some(url.to_string()) + Some(Cow::Borrowed(url)) } else { None } @@ -487,21 +491,23 @@ pub fn get_backdrop_path_value(field: &str, value: Option<&Value>) -> Option { impl FieldGetAccessor for XtreamPlaylistItem { - fn get_field(&self, field: &str) -> Option { - match field { + fn get_field(&self, field: &str) -> Option> { + let field = field.to_lowercase(); + match field.as_str() { $( - stringify!($prop) => Some(self.$prop.clone()), + stringify!($prop) => Some(Cow::Borrowed(&self.$prop)), )* - "epg_channel_id" | "epg_id" => self.epg_channel_id.clone(), + "caption" => Some(if self.title.is_empty() { Cow::Borrowed(&self.name) } else { Cow::Borrowed(&self.title) }), + "epg_channel_id" | "epg_id" => self.epg_channel_id.as_ref().map(|s| Cow::Borrowed(s.as_str())), _ => { if field.starts_with(xtream_const::XC_PROP_BACKDROP_PATH) || field == xtream_const::XC_PROP_COVER { let props = self.additional_properties.as_ref().and_then(|add_props| serde_json::from_str::>(add_props).ok()); return match props { Some(doc) => { return if field == xtream_const::XC_PROP_COVER { - doc.get(field).and_then(|value| value.as_str().map(|s| s.to_string())) + doc.get(&field).and_then(|value| value.as_str()).map(|s| Cow::::Owned(s.to_owned())) } else { - get_backdrop_path_value(field, doc.get(xtream_const::XC_PROP_BACKDROP_PATH)) + get_backdrop_path_value(&field, doc.get(xtream_const::XC_PROP_BACKDROP_PATH)).map(|s| Cow::::Owned(s.to_string())) } } _=> None, @@ -562,7 +568,7 @@ impl PlaylistItem { let mut result = match header.additional_properties.as_ref() { None => Map::new(), Some(props) => { - if let Value::Object(map) = props { + if let Value::Object(map) = props { map.clone() } else { Map::new() @@ -570,7 +576,7 @@ impl PlaylistItem { } }; result.insert("container_extension".to_string(), Value::String(ext.to_string())); - additional_properties = serde_json::to_string(&Value::Object(result)).ok(); + additional_properties = serde_json::to_string(&Value::Object(result)).ok(); } } } @@ -600,7 +606,7 @@ impl PlaylistItem { item_type: header.item_type, category_id: header.category_id, input_name: header.input_name.to_string(), - channel_no: header.chno.parse::().unwrap_or(0) + channel_no: header.chno.parse::().unwrap_or(0), } } } @@ -635,7 +641,6 @@ impl PlaylistEntry for PlaylistItem { fn get_item_type(&self) -> PlaylistItemType { self.header.item_type } - } #[derive(Debug, Clone, Serialize, Deserialize)] @@ -656,11 +661,10 @@ impl PlaylistGroup { } #[inline] - pub fn filter_count(&self, filter: F) -> usize + pub fn filter_count(&self, filter: F) -> usize where F: Fn(&PlaylistItem) -> bool, { self.channels.iter().filter(|&c| filter(c)).count() } - } diff --git a/src/processing/processor/affix.rs b/src/processing/processor/affix.rs index e31d7a8fa..54d6b986e 100644 --- a/src/processing/processor/affix.rs +++ b/src/processing/processor/affix.rs @@ -8,9 +8,9 @@ fn create_affix_processor(affix: &InputAffix, is_prefix: bool) -> AffixProcessor Box::new(move |channel: &mut PlaylistItem| { let header = &mut channel.header; let value = header.get_field(affix.field.as_str()).map_or_else(|| String::from(&affix.value), |field_value| if is_prefix { - format!("{}{}", &affix.value, field_value.as_str()) + format!("{}{field_value}", &affix.value, ) } else { - format!("{}{}", field_value.as_str(), &affix.value) + format!("{field_value}{}", , &affix.value) }); debug_if_enabled!("Applying input {}: {}={}", if is_prefix {"prefix"} else {"suffix"}, &affix.field, &value); header.set_field(&affix.field, value.as_str()); diff --git a/src/processing/processor/mod.rs b/src/processing/processor/mod.rs index 37f41c85b..5f136855c 100644 --- a/src/processing/processor/mod.rs +++ b/src/processing/processor/mod.rs @@ -1,6 +1,6 @@ pub mod playlist; mod xtream; -mod affix; +// mod affix; mod xtream_vod; mod xtream_series; pub mod epg; diff --git a/src/processing/processor/playlist.rs b/src/processing/processor/playlist.rs index d584ebeea..0ea288654 100644 --- a/src/processing/processor/playlist.rs +++ b/src/processing/processor/playlist.rs @@ -16,7 +16,6 @@ use crate::model::{CounterModifier, Mapping}; use crate::model::{FetchedPlaylist, FieldGetAccessor, FieldSetAccessor, PlaylistEntry, PlaylistGroup, PlaylistItem, UUIDType, XtreamCluster}; use crate::model::{InputStats, PlaylistStats, SourceStats, TargetStats}; use crate::processing::playlist_watch::process_group_watch; -use crate::processing::processor::affix::apply_affixes; use crate::processing::processor::xtream_series::playlist_resolve_series; use crate::processing::processor::xtream_vod::playlist_resolve_vod; use crate::repository::playlist_repository::persist_playlist; @@ -462,9 +461,6 @@ async fn process_playlist_for_target(client: Arc, processed_fetched_playlists.push(processed_fpl); } - step.tick("Processed affixes"); - apply_affixes(&mut processed_fetched_playlists); - step.tick("Processed epg"); let (new_epg, new_playlist) = process_epg(&mut processed_fetched_playlists); diff --git a/src/utils/file/config_reader.rs b/src/utils/file/config_reader.rs index 49c5d64e3..507da3d99 100644 --- a/src/utils/file/config_reader.rs +++ b/src/utils/file/config_reader.rs @@ -13,6 +13,7 @@ use std::fs::File; use std::io::{self, BufReader, Read}; use std::path::{PathBuf}; use std::sync::Arc; +use crate::utils; enum EitherReader { Left(L), @@ -52,7 +53,7 @@ pub fn read_api_proxy_config(cfg: &Config) -> Result<(), TuliproxError> { } } -pub fn read_config(config_path: &str, config_file: &str, sources_file: &str, api_proxy_file: &str, mappings_file: &str, include_computed: bool) -> Result { +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 files = vec![std::path::PathBuf::from(config_file), std::path::PathBuf::from(sources_file)]; match MultiFileReader::new(&files) { Ok(reader) => { @@ -62,10 +63,11 @@ pub fn read_config(config_path: &str, config_file: &str, sources_file: &str, api result.t_config_file_path = config_file.to_string(); result.t_sources_file_path = sources_file.to_string(); result.t_api_proxy_file_path = api_proxy_file.to_string(); - result.t_mapping_file_path = mappings_file.to_string(); - match result.prepare(include_computed) { - Err(err) => Err(err), - _ => Ok(result), + if let Err(err) = result.prepare(include_computed) { Err(err) } else { + if result.t_mapping_file_path.is_empty() { + result.t_mapping_file_path = resolve_env_var(&mappings_file.unwrap_or_else(|| utils::get_default_mappings_path(config_path))); + } + Ok(result) } } Err(e) => {