From 52e5a5471126cedbdd8ee695632fe7140cf50ff0 Mon Sep 17 00:00:00 2001 From: euzu Date: Sun, 10 Nov 2024 14:17:24 +0100 Subject: [PATCH] - hls support (m3u8 url, ignores proxy type, always redirect) - masking sensitive information in log - set "actix_web::middleware::logger" to level `error` --- CHANGELOG.md | 3 ++ src/api/api_utils.rs | 9 ++-- src/api/m3u_api.rs | 5 ++- src/api/v1_api.rs | 3 +- src/api/xtream_api.rs | 22 ++++++--- src/main.rs | 1 + src/model/playlist.rs | 59 +++++++++++++------------ src/model/xtream.rs | 18 ++++++++ src/processing/xtream_parser.rs | 4 +- src/repository/m3u_playlist_iterator.rs | 18 +++++--- src/repository/playlist_repository.rs | 21 ++++----- src/repository/xtream_repository.rs | 37 ++++++++++------ src/utils/request_utils.rs | 12 ++--- 13 files changed, 132 insertions(+), 80 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bf2a370a6..151247861 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,9 @@ # Changelog # 2.0.8 (2024-11-xx) - The configured directories `data`, `backup` and `video-download` are created when configured and do not exist. +- set "actix_web::middleware::logger" to level `error` +- masking sensitive information in log +- hls support (m3u8 url, ignores proxy type, always redirect) # 2.0.7 (2024-11-05) - EPG is now first downloaded to disk instead of directly into memory, then processed using a SAX parser (slower but reduces memory usage from up to 2GB). diff --git a/src/api/api_utils.rs b/src/api/api_utils.rs index e83e24938..e6e35c878 100644 --- a/src/api/api_utils.rs +++ b/src/api/api_utils.rs @@ -8,6 +8,7 @@ use crate::api::api_model::{AppState, UserApiRequest}; use crate::model::api_proxy::{ApiProxyServerInfo, ProxyUserCredentials}; use crate::model::config::{Config, ConfigTarget, ConfigInput}; use crate::utils::request_utils; +use crate::utils::request_utils::mask_sensitive_info; pub async fn serve_file(file_path: &Path, req: &HttpRequest, mime_type: mime::Mime) -> HttpResponse { if file_path.exists() { @@ -50,7 +51,7 @@ pub fn get_user_server_info(cfg: &Config, user: &ProxyUserCredentials) -> ApiPro pub async fn stream_response(stream_url: &str, req: &HttpRequest, input: Option<&ConfigInput>) -> HttpResponse { let req_headers: HashMap<&str, &[u8]> = req.headers().iter().map(|(k, v)| (k.as_str(), v.as_bytes())).collect(); - debug!("Try to open stream {}", stream_url); + debug!("Try to open stream {}", mask_sensitive_info(stream_url)); if let Ok(url) = Url::parse(stream_url) { let client = request_utils::get_client_request(input, &url, Some(&req_headers)); match client.send().await { @@ -62,14 +63,14 @@ pub async fn stream_response(stream_url: &str, req: &HttpRequest, input: Option< }); return response_builder.body(actix_web::body::BodyStream::new(response.bytes_stream())); } - debug!("Failed to open stream got status {} for {}", response.status(), stream_url); + debug!("Failed to open stream got status {} for {}", response.status(), mask_sensitive_info(stream_url)); } Err(err) => { - error!("Received failure from server {}: {}", stream_url, err); + error!("Received failure from server {}: {}", mask_sensitive_info(stream_url), err); } } } else { - error!("Url is malformed {}", &stream_url); + error!("Url is malformed {}", mask_sensitive_info(stream_url)); } HttpResponse::BadRequest().finish() } diff --git a/src/api/m3u_api.rs b/src/api/m3u_api.rs index b1247bff1..6ae7457f5 100644 --- a/src/api/m3u_api.rs +++ b/src/api/m3u_api.rs @@ -8,6 +8,7 @@ use crate::api::api_model::{AppState, UserApiRequest}; use crate::model::config::TargetType; use crate::repository::m3u_repository::{m3u_get_file_paths, m3u_get_item_for_stream_id, m3u_load_rewrite_playlist}; use crate::repository::storage::get_target_storage_path; +use crate::utils::request_utils::mask_sensitive_info; async fn m3u_api( api_req: web::Query, @@ -26,7 +27,7 @@ async fn m3u_api( .streaming(content_stream) } Err(err) => { - error!("{err}"); + error!("{}", mask_sensitive_info(err.to_string().as_str())); HttpResponse::NoContent().finish() } } @@ -53,7 +54,7 @@ async fn m3u_api_stream( return stream_response(m3u_item.url.as_str(), &req, None).await; } Err(err) => { - error!("Failed to get m3u url: {}", err); + error!("Failed to get m3u url: {}", mask_sensitive_info(err.to_string().as_str())); } } } diff --git a/src/api/v1_api.rs b/src/api/v1_api.rs index e3af0dc0a..49aef61d6 100644 --- a/src/api/v1_api.rs +++ b/src/api/v1_api.rs @@ -14,6 +14,7 @@ use crate::model::api_proxy::{ApiProxyConfig, ApiProxyServerInfo, ProxyUserCrede use crate::model::config::{Config, ConfigDto, ConfigInput, ConfigInputOptions, ConfigSource, ConfigTarget, InputType, validate_targets}; use crate::processing::playlist_processor; use crate::utils::{config_reader, download}; +use crate::utils::request_utils::mask_sensitive_info; fn _save_config_api_proxy(backup_dir: &str, api_proxy: &ApiProxyConfig, file_path: &str) -> Option { match config_reader::save_api_proxy(file_path, backup_dir, api_proxy) { @@ -104,7 +105,7 @@ async fn playlist_update( HttpResponse::Ok().finish() } Err(err) => { - error!("Failed playlist update {}", err.to_string()); + error!("Failed playlist update {}", mask_sensitive_info(err.to_string().as_str())); HttpResponse::BadRequest().json(json!({"error": err.to_string()})) } } diff --git a/src/api/xtream_api.rs b/src/api/xtream_api.rs index 1acc1fc2e..ec01a0082 100644 --- a/src/api/xtream_api.rs +++ b/src/api/xtream_api.rs @@ -25,6 +25,7 @@ use crate::repository::storage::{get_target_storage_path, hash_string}; use crate::repository::target_id_mapping::TargetIdMapping; use crate::repository::xtream_repository; use crate::utils::{json_utils, request_utils}; +use crate::utils::request_utils::mask_sensitive_info; const ACTION_GET_SERIES_INFO: &str = "get_series_info"; const ACTION_GET_VOD_INFO: &str = "get_vod_info"; @@ -160,7 +161,7 @@ fn get_xtream_player_api_info_url(input: &ConfigInput, cluster: XtreamCluster, s get_xtream_player_api_action_url(input, action).map(|action_url| format!("{action_url}&{stream_id_field}={stream_id}")) } -fn get_xtream_player_api_stream_url(input: &ConfigInput, context: &str, action_path: &str) -> Option { +fn get_xtream_player_api_stream_url(input: &ConfigInput, context: &str, action_path: &str, fallback_url: &str) -> Option { let ctx_path = if context.is_empty() { String::new() } else { format!("{context}/") }; if let Some(user_info) = input.get_user_info() { Some(format!("{}/{}{}/{}/{}", @@ -170,6 +171,8 @@ fn get_xtream_player_api_stream_url(input: &ConfigInput, context: &str, action_p &user_info.password, action_path )) + } else if !fallback_url.is_empty() { + Some(String::from(fallback_url)) } else { None } @@ -205,13 +208,22 @@ async fn xtream_player_api_stream( let pli = try_result_bad_request!(xtream_repository::xtream_get_item_for_stream_id(virtual_id, &app_state.config, target, None), true, format!("Failed to read xtream item for stream id {}", virtual_id)); let input = try_option_bad_request!(app_state.config.get_input_by_id(pli.input_id), true, format!("Cant find input for target {target_name}, context {}, stream_id {virtual_id}", stream_req.context)); + match pli.item_type { + PlaylistItemType::LiveUnknown | PlaylistItemType::LiveHls => { + let stream_url = pli.url.to_string(); + debug!("Redirecting stream request to {stream_url}"); + return HttpResponse::Found().insert_header(("Location", stream_url)).finish(); + } + _ => {} + } + let query_path = if stream_req.action_path.is_empty() { format!("{}{stream_ext}", pli.provider_id) } else { format!("{}/{}{stream_ext}", stream_req.action_path, pli.provider_id) }; - let stream_url = try_option_bad_request!(get_xtream_player_api_stream_url(input, stream_req.context.to_string().as_str(), &query_path), true, format!("Cant find stream url for target {target_name}, context {}, stream_id {virtual_id}", stream_req.context)); + let stream_url = try_option_bad_request!(get_xtream_player_api_stream_url(input, stream_req.context.to_string().as_str(), &query_path, pli.url.as_str()), true, format!("Cant find stream url for target {target_name}, context {}, stream_id {virtual_id}", stream_req.context)); if user.proxy == ProxyType::Redirect { debug!("Redirecting stream request to {stream_url}"); @@ -329,7 +341,7 @@ async fn xtream_get_stream_info(config: &Config, input: &ConfigInput, target: &C }; } - Err(Error::new(std::io::ErrorKind::Other, format!("Cant find stream with id: {}/{}/{}", + Err(Error::new(ErrorKind::Other, format!("Cant find stream with id: {}/{}/{}", target.name.replace(' ', "_").as_str(), &cluster, pli.virtual_id))) } @@ -384,7 +396,7 @@ async fn xtream_get_short_epg(app_state: &AppState, user: &ProxyUserCredentials, return match request_utils::download_text_content(input, info_url.as_str(), None).await { Ok(content) => HttpResponse::Ok().content_type(mime::APPLICATION_JSON).body(content), Err(err) => { - error!("Failed to download epg {}", err.to_string()); + error!("Failed to download epg {}", mask_sensitive_info(err.to_string().as_str())); HttpResponse::NoContent().finish() } }; @@ -401,7 +413,7 @@ async fn xtream_player_api_handle_content_action(config: &Config, target_name: & ACTION_GET_LIVE_CATEGORIES => xtream_repository::xtream_get_collection_path(config, target_name, xtream_repository::COL_CAT_LIVE), ACTION_GET_VOD_CATEGORIES => xtream_repository::xtream_get_collection_path(config, target_name, xtream_repository::COL_CAT_VOD), ACTION_GET_SERIES_CATEGORIES => xtream_repository::xtream_get_collection_path(config, target_name, xtream_repository::COL_CAT_SERIES), - _ => Err(std::io::Error::new(ErrorKind::Other, "")) + _ => Err(Error::new(ErrorKind::Other, "")) } { if let Some(file_path) = path { let category_id = category_id.trim(); diff --git a/src/main.rs b/src/main.rs index b8b39e304..c02a3a6d0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -184,6 +184,7 @@ fn init_logger(log_level: &str) { // Set the log level based on the parsed value log_builder.filter_level(get_log_level(log_level)); } + log_builder.filter_module("actix_web::middleware::logger", LevelFilter::Error); log_builder.init(); info!("Log Level {}", get_log_level(log_level)); } diff --git a/src/model/playlist.rs b/src/model/playlist.rs index 8e511adbe..ae748554e 100644 --- a/src/model/playlist.rs +++ b/src/model/playlist.rs @@ -6,7 +6,7 @@ use std::rc::Rc; use serde::{Deserialize, Serialize}; use serde_json::Value; -use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind}; +use crate::m3u_filter_error::M3uFilterError; use crate::model::config::{ConfigInput, ConfigTargetOptions}; use crate::model::xmltv::TVGuide; use crate::model::xtream::{xtream_playlistitem_to_document, XtreamMappingOptions}; @@ -81,6 +81,8 @@ pub enum PlaylistItemType { SeriesInfo = 4, // xtream series info fetched for series description SeriesEpisode = 5, // from SeriesInfo parsed episodes Catchup = 6, + LiveUnknown = 7, // No Provider id + LiveHls = 8, // m3u8 entry } impl From for PlaylistItemType { @@ -105,7 +107,7 @@ impl PlaylistItemType { impl Display for PlaylistItemType { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "{}", match self { - Self::Live => Self::LIVE, + Self::Live | Self::LiveHls | Self::LiveUnknown => Self::LIVE, Self::Video => Self::VIDEO, Self::Series => Self::SERIES, Self::SeriesInfo => Self::SERIES_INFO, @@ -237,6 +239,7 @@ pub struct M3uPlaylistItem { pub url: Rc, pub epg_channel_id: Option>, pub input_id: u16, + pub item_type: PlaylistItemType, } impl M3uPlaylistItem { @@ -313,37 +316,37 @@ impl PlaylistItem { url: Rc::clone(&header.url), epg_channel_id: header.epg_channel_id.clone(), input_id: header.input_id, + item_type: header.item_type, } } pub fn to_xtream(&self) -> Result { let header = self.header.borrow(); - match header.id.parse::() { - Ok(provider_id) => { - Ok(XtreamPlaylistItem { - virtual_id: header.virtual_id, - provider_id, - name: Rc::clone(&header.name), - logo: Rc::clone(&header.logo), - logo_small: Rc::clone(&header.logo_small), - group: Rc::clone(&header.group), - title: Rc::clone(&header.title), - parent_code: Rc::clone(&header.parent_code), - rec: Rc::clone(&header.rec), - url: Rc::clone(&header.url), - epg_channel_id: header.epg_channel_id.clone(), - xtream_cluster: header.xtream_cluster, - additional_properties: header.additional_properties.as_ref().and_then(|props| serde_json::to_string(props).ok()), - item_type: header.item_type, - series_fetched: header.series_fetched, - category_id: header.category_id, - input_id: header.input_id, - }) - } - Err(_) => { - Err(M3uFilterError::new(M3uFilterErrorKind::Info, format!("cant parse provider stream id: {}", header.id))) - } - } + let provider_id = header.id.parse::().unwrap_or_default(); + Ok(XtreamPlaylistItem { + virtual_id: header.virtual_id, + provider_id, + name: Rc::clone(&header.name), + logo: Rc::clone(&header.logo), + logo_small: Rc::clone(&header.logo_small), + group: Rc::clone(&header.group), + title: Rc::clone(&header.title), + parent_code: Rc::clone(&header.parent_code), + rec: Rc::clone(&header.rec), + url: Rc::clone(&header.url), + epg_channel_id: header.epg_channel_id.clone(), + xtream_cluster: header.xtream_cluster, + additional_properties: header.additional_properties.as_ref().and_then(|props| serde_json::to_string(props).ok()), + item_type: header.item_type, + series_fetched: header.series_fetched, + category_id: header.category_id, + input_id: header.input_id, + }) + // } + // Err(_) => { + // Err(M3uFilterError::new(M3uFilterErrorKind::Info, format!("cant parse provider stream id: {}", header.id))) + // } + // } } } diff --git a/src/model/xtream.rs b/src/model/xtream.rs index cb534edff..b2ce01232 100644 --- a/src/model/xtream.rs +++ b/src/model/xtream.rs @@ -213,6 +213,20 @@ macro_rules! add_i64_property_if_exists { } } +macro_rules! add_to_doc_str_property_if_not_exists { + ($document:expr, $prop_name:expr, $prop_value:expr) => { + match $document.get($prop_name) { + None => { + $document.insert(String::from($prop_name), $prop_value); + } + Some(value) => { if Value::is_null(value) { + $document.insert(String::from($prop_name), $prop_value); + }} + } + } +} + + impl XtreamStream { pub fn get_stream_id(&self) -> u32 { self.stream_id.unwrap_or_else(|| self.series_id.unwrap_or(0)) @@ -468,9 +482,13 @@ pub fn xtream_playlistitem_to_document(pli: &XtreamPlaylistItem, options: &Xtrea match pli.xtream_cluster { XtreamCluster::Live => { append_mandatory_fields(&mut document, LIVE_STREAM_FIELDS); + add_to_doc_str_property_if_not_exists!(document, "stream_type", Value::String(String::from("live"))); + add_to_doc_str_property_if_not_exists!(document, "added", Value::String(chrono::Utc::now().timestamp().to_string())); } XtreamCluster::Video => { append_mandatory_fields(&mut document, VIDEO_STREAM_FIELDS); + add_to_doc_str_property_if_not_exists!(document, "stream_type", Value::String(String::from("movie"))); + add_to_doc_str_property_if_not_exists!(document, "added", Value::String(chrono::Utc::now().timestamp().to_string())); } XtreamCluster::Series => { append_prepared_series_properties(props.as_ref(), &mut document); diff --git a/src/processing/xtream_parser.rs b/src/processing/xtream_parser.rs index 49ea8dc73..26f797089 100644 --- a/src/processing/xtream_parser.rs +++ b/src/processing/xtream_parser.rs @@ -98,7 +98,7 @@ pub fn parse_xtream(input: &ConfigInput, let username = input.username.as_ref().map_or("", |v| v); let password = input.password.as_ref().map_or("", |v| v); - return match map_to_xtream_streams(xtream_cluster, streams) { + match map_to_xtream_streams(xtream_cluster, streams) { Ok(xtream_streams) => { let group_map: HashMap::, RefCell> = xtream_categories.into_iter().map(|category| @@ -142,7 +142,7 @@ pub fn parse_xtream(input: &ConfigInput, }).collect())) } Err(err) => Err(err) - }; + } } Err(err) => Err(err) } diff --git a/src/repository/m3u_playlist_iterator.rs b/src/repository/m3u_playlist_iterator.rs index dfb057dc0..41c73d162 100644 --- a/src/repository/m3u_playlist_iterator.rs +++ b/src/repository/m3u_playlist_iterator.rs @@ -2,7 +2,7 @@ use crate::api::api_utils::get_user_server_info; use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind}; use crate::model::api_proxy::{ProxyType, ProxyUserCredentials}; use crate::model::config::{Config, ConfigTarget, ConfigTargetOptions}; -use crate::model::playlist::M3uPlaylistItem; +use crate::model::playlist::{M3uPlaylistItem, PlaylistItemType}; use crate::repository::indexed_document::IndexedDocumentReader; use crate::repository::m3u_repository::m3u_get_file_paths; use crate::repository::storage::ensure_target_storage_path; @@ -60,13 +60,19 @@ impl Iterator for M3uPlaylistIterator { } self.reader.next().map(|m3u_pli| { - match self.proxy_type { - ProxyType::Reverse => { - m3u_pli.to_m3u(&self.target_options, Some(format!("{}/{}", &self.base_url, m3u_pli.virtual_id).as_str())) - } - ProxyType::Redirect => { + // TODO hls and unknown reverse proxy + match m3u_pli.item_type { + PlaylistItemType::LiveUnknown | PlaylistItemType::LiveHls => { m3u_pli.to_m3u(&self.target_options, None) } + _ => match self.proxy_type { + ProxyType::Reverse => { + m3u_pli.to_m3u(&self.target_options, Some(format!("{}/{}", &self.base_url, m3u_pli.virtual_id).as_str())) + } + ProxyType::Redirect => { + m3u_pli.to_m3u(&self.target_options, None) + } + } } }) } diff --git a/src/repository/playlist_repository.rs b/src/repository/playlist_repository.rs index f545b6e42..371df87fc 100644 --- a/src/repository/playlist_repository.rs +++ b/src/repository/playlist_repository.rs @@ -1,6 +1,7 @@ use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind}; use crate::model::config::{Config, ConfigTarget, TargetType}; -use crate::model::playlist::PlaylistGroup; +use crate::model::playlist::PlaylistItemType::LiveUnknown; +use crate::model::playlist::{PlaylistGroup, PlaylistItemType}; use crate::model::xmltv::Epg; use crate::repository::epg_repository::epg_write; use crate::repository::kodi_repository::kodi_write_strm_playlist; @@ -10,7 +11,7 @@ use crate::repository::target_id_mapping::TargetIdMapping; use crate::repository::xtream_repository::xtream_write_playlist; pub fn persist_playlist(playlist: &mut [PlaylistGroup], epg: Option<&Epg>, - target: &ConfigTarget, cfg: &Config) -> Result<(), Vec> { + target: &ConfigTarget, cfg: &Config) -> Result<(), Vec> { let mut errors = vec![]; let target_path = match ensure_target_storage_path(cfg, &target.name) { Ok(path) => path, @@ -32,16 +33,13 @@ pub fn persist_playlist(playlist: &mut [PlaylistGroup], epg: Option<&Epg>, for group in playlist.iter_mut() { for channel in &group.channels { let mut header = channel.header.borrow_mut(); - if let Some(provider_id) = header.get_provider_id() { - let uuid = header.get_uuid(); - let item_type = header.item_type; - header.virtual_id = target_id_mapping.insert_entry(**uuid, provider_id, item_type, 0); - } else { - errors.push(M3uFilterError::new( - M3uFilterErrorKind::Info, - format!("Playlist item has no provider id: {}", &header.title), - )); + let provider_id = header.get_provider_id().unwrap_or_default(); + if provider_id == 0 { + header.item_type = if header.url.ends_with(".m3u8") { PlaylistItemType::LiveHls } else { LiveUnknown }; } + let uuid = header.get_uuid(); + let item_type = header.item_type; + header.virtual_id = target_id_mapping.insert_entry(**uuid, provider_id, item_type, 0); } } @@ -71,5 +69,4 @@ pub fn persist_playlist(playlist: &mut [PlaylistGroup], epg: Option<&Epg>, } if errors.is_empty() { Ok(()) } else { Err(errors) } - } diff --git a/src/repository/xtream_repository.rs b/src/repository/xtream_repository.rs index ec07d9e0e..f278389c0 100644 --- a/src/repository/xtream_repository.rs +++ b/src/repository/xtream_repository.rs @@ -201,20 +201,29 @@ pub fn xtream_write_playlist(target: &ConfigTarget, cfg: &Config, playlist: &mut for pli in plg.channels.drain(..) { let mut header = pli.header.borrow_mut(); - // we skip resolved series, because this is only necessary when writing m3u files - let col = if header.item_type == PlaylistItemType::Series { - None - } else if header.get_provider_id().is_some() { - header.category_id = *cat_id; - Some(match header.xtream_cluster { - XtreamCluster::Live => &mut live_col, - XtreamCluster::Series => &mut series_col, - XtreamCluster::Video => &mut vod_col, - }) - } else { - let title = header.title.as_str(); - errors.push(format!("Channel does not have an id: {title}")); - None + let col = match header.item_type { + PlaylistItemType::Series => { + // we skip resolved series, because this is only necessary when writing m3u files + None + }, + PlaylistItemType::LiveUnknown | PlaylistItemType::LiveHls => { + header.category_id = *cat_id; + Some(&mut live_col) + }, + _ => { + if header.get_provider_id().is_some() { + header.category_id = *cat_id; + Some(match header.xtream_cluster { + XtreamCluster::Live => &mut live_col, + XtreamCluster::Series => &mut series_col, + XtreamCluster::Video => &mut vod_col, + }) + } else { + let title = header.title.as_str(); + errors.push(format!("Channel does not have an id: {title}")); + None + } + } }; drop(header); if let Some(pl) = col { diff --git a/src/utils/request_utils.rs b/src/utils/request_utils.rs index 3719f8480..9c249a35b 100644 --- a/src/utils/request_utils.rs +++ b/src/utils/request_utils.rs @@ -28,7 +28,7 @@ pub const fn bytes_to_megabytes(bytes: u64) -> u64 { pub async fn get_input_text_content_as_file(input: &ConfigInput, working_dir: &str, url_str: &str, persist_filepath: Option) -> Result { if log_enabled!(Level::Debug) { - debug!("getting input text content working_dir: {}, url: {}", working_dir, url_str); + debug!("getting input text content working_dir: {}, url: {}", working_dir, mask_sensitive_info(url_str)); } if url_str.parse::().is_ok() { @@ -67,7 +67,7 @@ pub async fn get_input_text_content_as_file(input: &ConfigInput, working_dir: &s }; result.map_or_else(|| { - let msg = format!("cant read input url: {url_str:?}"); + let msg = format!("cant read input url: {}", mask_sensitive_info(url_str)); error!("{}", msg); create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "{}", msg) }, Ok) @@ -77,7 +77,7 @@ pub async fn get_input_text_content_as_file(input: &ConfigInput, working_dir: &s pub async fn get_input_text_content(input: &ConfigInput, working_dir: &String, url_str: &str, persist_filepath: Option) -> Result { if log_enabled!(Level::Debug) { - debug!("getting input text content working_dir: {}, url: {}", working_dir, url_str); + debug!("getting input text content working_dir: {}, url: {}", working_dir, mask_sensitive_info(url_str)); } if url_str.parse::().is_ok() { @@ -116,7 +116,7 @@ pub async fn get_input_text_content(input: &ConfigInput, working_dir: &String, u None => None }; result.map_or_else(|| { - let msg = format!("cant read input url: {url_str:?}"); + let msg = format!("cant read input url: {}", mask_sensitive_info(url_str)); error!("{}", msg); create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "{}", msg) }, Ok) @@ -295,7 +295,7 @@ pub async fn download_text_content_as_file(input: &ConfigInput, url_str: &str, w } } } else { - Err(std::io::Error::new(ErrorKind::Unsupported, format!("Malformed URL {url_str}"))) + Err(std::io::Error::new(ErrorKind::Unsupported, format!("Malformed URL {}", mask_sensitive_info(url_str)))) } } @@ -317,7 +317,7 @@ pub async fn download_text_content(input: &ConfigInput, url_str: &str, persist_f Err(err) => Err(err) } } else { - Err(std::io::Error::new(ErrorKind::Other, format!("Malformed URL {url_str}"))) + Err(std::io::Error::new(ErrorKind::Other, format!("Malformed URL {}", mask_sensitive_info(url_str)))) } }