diff --git a/src/api/api_model.rs b/src/api/api_model.rs index 742963e13..a897dc87a 100644 --- a/src/api/api_model.rs +++ b/src/api/api_model.rs @@ -15,9 +15,9 @@ use crate::utils::default_utils::default_as_empty_str; pub(crate) struct FileDownload { /// uuid of the download for identification. pub uuid: String, - /// file_dir is the directory where the download should be placed. + /// `file_dir` is the directory where the download should be placed. pub file_dir: PathBuf, - /// file_path is the complete path including the filename. + /// `file_path` is the complete path including the filename. pub file_path: PathBuf, /// filename is the filename. pub filename: String, @@ -43,7 +43,7 @@ pub(crate) struct FileDownload { fn get_download_directory(download_cfg: &VideoDownloadConfig, filestem: &str) -> PathBuf { if download_cfg.organize_into_directories { let mut stem = filestem; - if let Some(re) = &download_cfg._re_episode_pattern { + if let Some(re) = &download_cfg.t_re_episode_pattern { if let Some(captures) = re.captures(stem) { if let Some(episode) = captures.name("episode") { if !episode.as_str().is_empty() { @@ -52,7 +52,7 @@ fn get_download_directory(download_cfg: &VideoDownloadConfig, filestem: &str) -> } } } - let re_ending = download_cfg._re_remove_filename_ending.as_ref().unwrap(); + let re_ending = download_cfg.t_re_remove_filename_ending.as_ref().unwrap(); let dir_name = re_ending.replace(stem, ""); let file_dir: PathBuf = [download_cfg.directory.as_ref().unwrap(), dir_name.as_ref()].iter().collect(); file_dir @@ -74,7 +74,7 @@ impl FileDownload { pub fn new(req_url: &str, req_filename: &str, download_cfg: &VideoDownloadConfig) -> Option { match reqwest::Url::parse(req_url) { Ok(url) => { - let filename_re = download_cfg._re_filename.as_ref().unwrap(); + let filename_re = download_cfg.t_re_filename.as_ref().unwrap(); let tmp_filename = filename_re.replace_all(&unidecode(req_filename) .replace(' ', "_"), "") .replace("__", "_") @@ -83,13 +83,13 @@ impl FileDownload { let file_stem = filename_path.file_stem().and_then(OsStr::to_str).unwrap_or("").trim_matches(FILENAME_TRIM_PATTERNS); let file_ext = filename_path.extension().and_then(OsStr::to_str).unwrap_or(""); - let mut filename = format!("{}.{}", file_stem, file_ext); + let mut filename = format!("{file_stem}.{file_ext}"); let file_dir = get_download_directory(download_cfg, file_stem); let mut file_path: PathBuf = file_dir.clone(); file_path.push(&filename); let mut x: usize = 1; while file_path.is_file() { - filename = format!("{}_{}.{}", file_stem, x, file_ext); + filename = format!("{file_stem}_{x}.{file_ext}"); file_path.clone_from(&file_dir); file_path.push(&filename); x += 1; diff --git a/src/api/api_utils.rs b/src/api/api_utils.rs index 803201140..0fb274be8 100644 --- a/src/api/api_utils.rs +++ b/src/api/api_utils.rs @@ -28,10 +28,10 @@ pub(crate) fn get_user_target_by_credentials<'a>(username: &str, password: &str, app_state.config.get_target_for_user(username, password) } else { let token = api_req.token.as_str().trim(); - if !token.is_empty() { - app_state.config.get_target_for_user_by_token(token) - } else { + if token.is_empty() { None + } else { + app_state.config.get_target_for_user_by_token(token) } } } @@ -43,7 +43,7 @@ pub(crate) fn get_user_target<'a>(api_req: &'a UserApiRequest, app_state: &'a we } pub(crate) fn get_user_server_info(cfg: &Config, user: &ProxyUserCredentials) -> ApiProxyServerInfo { - let server_info_list = cfg._api_proxy.read().unwrap().as_ref().unwrap().server.clone(); + let server_info_list = cfg.t_api_proxy.read().unwrap().as_ref().unwrap().server.clone(); let server_info_name = match &user.server { Some(server_name) => server_name.as_str(), None => "default" @@ -68,14 +68,14 @@ pub(crate) async fn stream_response(stream_url: &str, req: &HttpRequest, input: }); 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(), stream_url); } Err(err) => { - error!("Received failure from server {}: {}", stream_url, err) + error!("Received failure from server {}: {}", stream_url, err); } } } else { - error!("Url is malformed {}", &stream_url) + error!("Url is malformed {}", &stream_url); } HttpResponse::BadRequest().finish() } diff --git a/src/api/download_api.rs b/src/api/download_api.rs index 75781dbc0..c89103760 100644 --- a/src/api/download_api.rs +++ b/src/api/download_api.rs @@ -16,7 +16,7 @@ async fn download_file(active: Arc>>, client: &reqwe match client.get(file_download.url.clone()).send().await { Ok(response) => { match fs::create_dir_all(&file_download.file_dir) { - Ok(_) => { + Ok(()) => { if let Some(file_path_str) = file_download.file_path.to_str() { info!("Downloading {}", file_path_str); match File::create(&file_download.file_path) { @@ -26,29 +26,26 @@ async fn download_file(active: Arc>>, client: &reqwe loop { match stream.try_next().await { Ok(item) => { - match item { - Some(chunk) => { - match file.write_all(&chunk) { - Ok(_) => { - downloaded += chunk.len() as u64; - active.write().unwrap().as_mut().unwrap().size = downloaded; - } - Err(err) => return Err(format!("Error while writing to file: {} {}", file_path_str, err)) + if let Some(chunk) = item { + match file.write_all(&chunk) { + Ok(()) => { + downloaded += chunk.len() as u64; + active.write().unwrap().as_mut().unwrap().size = downloaded; } + Err(err) => return Err(format!("Error while writing to file: {file_path_str} {err}")) } - None => { - let megabytes = request_utils::bytes_to_megabytes(downloaded); - info!("Downloaded {}, filesize: {}MB", file_path_str, megabytes); - active.write().unwrap().as_mut().unwrap().size = downloaded; - return Ok(()); - } + } else { + let megabytes = request_utils::bytes_to_megabytes(downloaded); + info!("Downloaded {}, filesize: {}MB", file_path_str, megabytes); + active.write().unwrap().as_mut().unwrap().size = downloaded; + return Ok(()); } } - Err(err) => return Err(format!("Error while writing to file: {} {}", file_path_str, err)) + Err(err) => return Err(format!("Error while writing to file: {file_path_str} {err}")) } } } - Err(err) => Err(format!("Error while writing to file: {} {}", file_path_str, err)) + Err(err) => Err(format!("Error while writing to file: {file_path_str} {err}")) } } else { Err("Error file-download file-path unknown".to_string()) @@ -61,21 +58,19 @@ async fn download_file(active: Arc>>, client: &reqwe } } -fn run_download_queue(download_cfg: &VideoDownloadConfig, download_queue: Arc) -> Result<(), String> { - let next_download = { - download_queue.as_ref().queue.lock().unwrap().pop_front() - }; +fn run_download_queue(download_cfg: &VideoDownloadConfig, download_queue: &Arc) -> Result<(), String> { + let next_download = download_queue.as_ref().queue.lock().unwrap().pop_front(); if next_download.is_some() { { *download_queue.as_ref().active.write().unwrap() = next_download; } let headers = request_utils::get_request_headers(&download_cfg.headers, None); - let dq = Arc::clone(&download_queue); + let dq = Arc::clone(download_queue); match reqwest::Client::builder().default_headers(headers).build() { Ok(client) => { actix_rt::spawn(async move { loop { if dq.active.read().unwrap().deref().is_some() { match download_file(Arc::clone(&dq.active), &client).await { - Ok(_) => { + Ok(()) => { if let Some(fd) = &mut *dq.active.write().unwrap() { fd.finished = true; dq.finished.write().unwrap().push(fd.clone()); @@ -124,8 +119,8 @@ pub(crate) async fn queue_download_file( let response = HttpResponse::Ok().json(download_info!(file_download)); app_state.downloads.queue.lock().unwrap().push_back(file_download); if app_state.downloads.active.read().unwrap().is_none() { - match run_download_queue(download_cfg, Arc::clone(&app_state.downloads)) { - Ok(_) => {} + match run_download_queue(download_cfg, &app_state.downloads) { + Ok(()) => {} Err(err) => return HttpResponse::InternalServerError().json(json!({"error": err})), } } diff --git a/src/api/main_api.rs b/src/api/main_api.rs index 07e68c2bb..3d8c26188 100644 --- a/src/api/main_api.rs +++ b/src/api/main_api.rs @@ -54,7 +54,7 @@ pub(crate) async fn start_server(cfg: Arc, targets: Arc) if let Some(expression) = schedule { let cloned_data = shared_data.clone(); actix_rt::spawn(async move { - start_scheduler(&expression, cloned_data).await + start_scheduler(&expression, cloned_data).await; }); } @@ -100,6 +100,6 @@ pub(crate) async fn start_server(cfg: Arc, targets: Arc) srvcfg.configure(index_register(&web_dir_path)); } }) - }).bind(format!("{}:{}", host, port))?.run().await + }).bind(format!("{host}:{port}"))?.run().await } diff --git a/src/api/v1_api.rs b/src/api/v1_api.rs index 38a132097..b10dbd7fe 100644 --- a/src/api/v1_api.rs +++ b/src/api/v1_api.rs @@ -1,3 +1,4 @@ +use std::collections::HashMap; use std::sync::Arc; use actix_web::{HttpResponse, web}; @@ -17,7 +18,7 @@ use crate::utils::{config_reader, download}; fn _save_config_api_proxy(backup_dir: &str, api_proxy: &mut ApiProxyConfig, file_path: &str) -> Option { match config_reader::save_api_proxy(file_path, backup_dir, api_proxy) { - Ok(_) => {} + Ok(()) => {} Err(err) => { error!("Failed to save api_proxy.yml {}", err.to_string()); return Some(err); @@ -28,7 +29,7 @@ fn _save_config_api_proxy(backup_dir: &str, api_proxy: &mut ApiProxyConfig, file fn _save_config_main(file_path: &str, backup_dir: &str, cfg: &ConfigDto) -> Option { match config_reader::save_main_config(file_path, backup_dir, cfg) { - Ok(_) => {} + Ok(()) => {} Err(err) => { error!("Failed to save config.yml {}", err.to_string()); return Some(err); @@ -42,11 +43,11 @@ pub(crate) async fn save_config_api_proxy_user( app_state: web::Data, ) -> HttpResponse { let mut users = req.0; - users.iter_mut().flat_map(|t| &mut t.credentials).for_each(|c| c.trim()); - if let Some(api_proxy) = app_state.config._api_proxy.write().unwrap().as_mut() { + users.iter_mut().flat_map(|t| &mut t.credentials).for_each(super::super::model::api_proxy::ProxyUserCredentials::trim); + if let Some(api_proxy) = app_state.config.t_api_proxy.write().unwrap().as_mut() { let backup_dir = app_state.config.backup_dir.as_ref().unwrap().as_str(); api_proxy.user = users; - if let Some(err) = _save_config_api_proxy(backup_dir, api_proxy, app_state.config._api_proxy_file_path.as_str()) { + if let Some(err) = _save_config_api_proxy(backup_dir, api_proxy, app_state.config.t_api_proxy_file_path.as_str()) { return HttpResponse::InternalServerError().json(json!({"error": err.to_string()})); } api_proxy.user.iter_mut().flat_map(|t| &mut t.credentials).for_each(|c| c.prepare(true)); @@ -60,7 +61,7 @@ pub(crate) async fn save_config_main( ) -> HttpResponse { let cfg = req.0; if cfg.is_valid() { - let file_path = app_state.config._config_file_path.as_str(); + let file_path = app_state.config.t_config_file_path.as_str(); let backup_dir = app_state.config.backup_dir.as_ref().unwrap().as_str(); if let Some(err) = _save_config_main(file_path, backup_dir, &cfg) { return HttpResponse::InternalServerError().json(json!({"error": err.to_string()})); @@ -81,10 +82,10 @@ pub(crate) async fn save_config_api_proxy_config( return HttpResponse::BadRequest().json(json!({"error": "Invalid content"})); } } - if let Some(api_proxy) = app_state.config._api_proxy.write().unwrap().as_mut() { + if let Some(api_proxy) = app_state.config.t_api_proxy.write().unwrap().as_mut() { api_proxy.server = req_api_proxy; let backup_dir = app_state.config.backup_dir.as_ref().unwrap().as_str(); - if let Some(err) = _save_config_api_proxy(backup_dir, api_proxy, app_state.config._api_proxy_file_path.as_str()) { + if let Some(err) = _save_config_api_proxy(backup_dir, api_proxy, app_state.config.t_api_proxy_file_path.as_str()) { return HttpResponse::InternalServerError().json(json!({"error": err.to_string()})); } } @@ -113,7 +114,7 @@ pub(crate) async fn playlist_update( fn create_config_input_for_url(url: &str) -> ConfigInput { ConfigInput { id: 0, - headers: Default::default(), + headers: HashMap::default(), input_type: InputType::M3u, url: String::from(url), epg_url: None, @@ -142,7 +143,7 @@ async fn get_playlist(inp: Option<&ConfigInput>, cfg: &Config) -> HttpResponse { InputType::Xtream => download::get_xtream_playlist(input, &cfg.working_dir).await, }; if result.is_empty() { - let error_strings: Vec = errors.iter().map(|err| err.to_string()).collect(); + let error_strings: Vec = errors.iter().map(std::string::ToString::to_string).collect(); HttpResponse::BadRequest().json(json!({"error": error_strings.join(", ")})) } else { HttpResponse::Ok().json(result) @@ -156,7 +157,7 @@ pub(crate) async fn playlist( req: web::Json, app_state: web::Data, ) -> HttpResponse { - return if let Some(input_id) = &req.input_id { + return if let Some(input_id) = req.input_id { get_playlist(app_state.config.get_input_by_id(input_id), &app_state.config).await } else { let url = req.url.as_deref().unwrap_or(""); @@ -201,18 +202,18 @@ pub(crate) async fn config( let map_config = |config: &Config| ServerConfig { api: config.api.clone(), threads: config.threads, - working_dir: config.working_dir.to_owned(), - backup_dir: config.backup_dir.to_owned(), + working_dir: config.working_dir.clone(), + backup_dir: config.backup_dir.clone(), schedule: config.schedule.clone(), messaging: config.messaging.clone(), video: config.video.clone(), sources: config.sources.iter().map(map_source).collect(), - api_proxy: config_reader::read_api_proxy(app_state.config._api_proxy_file_path.as_str(), false), + api_proxy: config_reader::read_api_proxy(app_state.config.t_api_proxy_file_path.as_str(), false), }; - let mut result = match config_reader::read_config(app_state.config._config_path.as_str(), - app_state.config._config_file_path.as_str(), - app_state.config._sources_file_path.as_str()) { + let mut result = match config_reader::read_config(app_state.config.t_config_path.as_str(), + app_state.config.t_config_file_path.as_str(), + app_state.config.t_sources_file_path.as_str()) { Ok(mut cfg) => { let _ = cfg.prepare(true); map_config(&cfg) @@ -222,7 +223,7 @@ pub(crate) async fn config( // if we didn't read it from file then we should use it from app_state if result.api_proxy.is_none() { - result.api_proxy.clone_from(&app_state.config._api_proxy.read().unwrap()); + result.api_proxy.clone_from(&app_state.config.t_api_proxy.read().unwrap()); } HttpResponse::Ok().json(result) diff --git a/src/api/xmltv_api.rs b/src/api/xmltv_api.rs index a3ac53328..3981f595b 100644 --- a/src/api/xmltv_api.rs +++ b/src/api/xmltv_api.rs @@ -17,9 +17,8 @@ fn get_epg_path_for_target_of_type(target_name: &str, file_path: Option if let Some(epg_path) = file_path { if file_utils::path_exists(&epg_path) { return Some(epg_path); - } else { - info!("Cant find epg file for {target_name} target: {}", epg_path.to_str().unwrap_or("?")); } + info!("Cant find epg file for {target_name} target: {}", epg_path.to_str().unwrap_or("?")); } None } @@ -61,7 +60,7 @@ async fn xmltv_api( } fn get_xmltv_epg_url(input: &ConfigInput) -> Result { - let epg_url = input.epg_url.as_ref().map_or("".to_string(), |s| s.to_owned()); + let epg_url = input.epg_url.as_ref().map_or(String::new(), std::borrow::ToOwned::to_owned); if epg_url.is_empty() { if let Some(user_info) = input.get_user_info() { let url = user_info.base_url.as_str(); @@ -99,11 +98,11 @@ async fn get_xmltv_raw_epg(config: &Config, user: &ProxyUserCredentials, target_ } } } else { - debug!("Could not generate epg url for {target_name}") + debug!("Could not generate epg url for {target_name}"); } } } else { - debug!("No epg_url is provided for target {target_name}, multi input requires epg_url") + debug!("No epg_url is provided for target {target_name}, multi input requires epg_url"); } } None diff --git a/src/api/xtream_api.rs b/src/api/xtream_api.rs index a63eea881..a2b521f77 100644 --- a/src/api/xtream_api.rs +++ b/src/api/xtream_api.rs @@ -92,11 +92,11 @@ fn get_xtream_player_api_info_url(input: &ConfigInput, cluster: &XtreamCluster, XtreamCluster::Video => ("get_vod_info", "vod_id"), XtreamCluster::Series => ("get_series_info", "series_id"), }; - get_xtream_player_api_action_url(input, action).map(|action_url| format!("{}&{}={}", action_url, stream_id_field, stream_id)) + 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 { - let ctx_path = if context.is_empty() { "".to_string() } else { format!("{}/", context) }; + let ctx_path = if context.is_empty() { String::new() } else { format!("{context}/") }; if let Some(user_info) = input.get_user_info() { Some(format!("{}/{}{}/{}/{}", &user_info.base_url, @@ -130,11 +130,11 @@ fn get_user_info(user: &ProxyUserCredentials, cfg: &Config) -> XtreamAuthorizati status: "Active".to_string(), }, server_info: XtreamServerInfo { - url: server_info.host.to_owned(), - port: server_info.http_port.to_owned(), - https_port: server_info.https_port.to_owned(), + url: server_info.host.clone(), + port: server_info.http_port.clone(), + https_port: server_info.https_port.clone(), server_protocol: server_info.protocol.clone(), - rtmp_port: server_info.rtmp_port.to_owned(), + rtmp_port: server_info.rtmp_port.clone(), timezone: server_info.timezone.to_string(), timestamp_now: now.timestamp(), time_now: now.format("%Y-%m-%d %H:%M:%S").to_string(), @@ -167,9 +167,9 @@ async fn xtream_player_api_stream( Err(_) => return HttpResponse::BadRequest().finish() }; - if let Ok(pli) =xtream_get_item_for_stream_id(req_stream_id, &app_state.config, target, None) { + if let Ok(pli) = xtream_get_item_for_stream_id(req_stream_id, &app_state.config, target, None) { let input_id: u16 = pli.input_id; - if let Some(input) = app_state.config.get_input_by_id(&input_id) { + if let Some(input) = app_state.config.get_input_by_id(input_id) { let mut query_path = if stream_req.action_path.is_empty() { String::new() } else { format!("{}/", stream_req.action_path) }; query_path = format!("{query_path}{}{stream_ext}", pli.provider_id); if let Some(stream_url) = get_xtream_player_api_stream_url(input, stream_req.context.to_string().as_str(), query_path.as_str()) { @@ -242,7 +242,7 @@ async fn xtream_player_api_timeshift_stream( app_state: web::Data, ) -> HttpResponse { let (username, password, duration, start, stream_id) = path.into_inner(); - let action_path = format!("{}/{}/", duration, start); + let action_path = format!("{duration}/{start}/"); xtream_player_api_stream(&req, &api_req, &app_state, XtreamApiStreamRequest::from(XtreamApiStreamContext::Timeshift, &username, &password, &stream_id, &action_path)).await } @@ -255,7 +255,7 @@ fn get_xtream_vod_info(target: &ConfigTarget, pli: &XtreamPlaylistItem, content: movie_data.insert("category_id".to_string(), Value::Number(serde_json::value::Number::from(category_id))); let options = XtreamMappingOptions::from_target_options(target.options.as_ref()); if options.skip_video_direct_source { - movie_data.insert("direct_source".to_string(), Value::String("".to_string())); + movie_data.insert("direct_source".to_string(), Value::String(String::new())); } else { movie_data.insert("direct_source".to_string(), Value::String(pli.url.to_string())); } @@ -269,7 +269,7 @@ fn get_xtream_vod_info(target: &ConfigTarget, pli: &XtreamPlaylistItem, content: fn get_xtream_series_info(config: &Config, target: &ConfigTarget, pli: &XtreamPlaylistItem, content: &str) -> Result { if let Ok(mut doc) = serde_json::from_str::(content) { - let mut new_id_to_provider_id_mapping : Vec<(u32, u32)> = Vec::new(); + let mut new_id_to_provider_id_mapping: Vec<(u32, u32)> = Vec::new(); if let Some(mut new_id) = xtream_get_max_series_info_episode_id(config, target.name.as_str()) { if let Some(episodes) = doc.get_mut("episodes") { if let Some(episodes_map) = episodes.as_object_mut() { @@ -287,7 +287,7 @@ fn get_xtream_series_info(config: &Config, target: &ConfigTarget, pli: &XtreamPl } } if options.skip_series_direct_source { - episode.insert("direct_source".to_string(), Value::String("".to_string())); + episode.insert("direct_source".to_string(), Value::String(String::new())); } } } @@ -348,7 +348,7 @@ async fn xtream_get_stream_info_response(app_state: &AppState, user: &ProxyUserC if let Ok(pli) = xtream_get_item_for_stream_id(req_stream_id, &app_state.config, target, Some(cluster)) { let input_id = pli.input_id; - if let Some(input) = app_state.config.get_input_by_id(&input_id) { + if let Some(input) = app_state.config.get_input_by_id(input_id) { if let Some(info_url) = get_xtream_player_api_info_url(input, cluster, pli.provider_id) { // Redirect is only possible for live streams, vod and series info needs to be modified if user.proxy == ProxyType::Redirect && cluster == &XtreamCluster::Live { @@ -376,11 +376,11 @@ async fn xtream_get_short_epg(app_state: &AppState, user: &ProxyUserCredentials, if let Ok(pli) = xtream_get_item_for_stream_id(req_stream_id, &app_state.config, target, None) { let input_id: u16 = pli.input_id; - if let Some(input) = app_state.config.get_input_by_id(&input_id) { + if let Some(input) = app_state.config.get_input_by_id(input_id) { if let Some(action_url) = get_xtream_player_api_action_url(input, "get_short_epg") { let mut info_url = format!("{}&stream_id={}", action_url, pli.provider_id); if !(limit.is_empty() || limit.eq("0")) { - info_url = format!("{}&limit={}", info_url, limit); + info_url = format!("{info_url}&limit={limit}"); } if let Ok(url) = Url::parse(&info_url) { if user.proxy == ProxyType::Redirect { @@ -421,9 +421,8 @@ async fn xtream_player_api_handle_content_action(config: &Config, target_name: & let category_id = category_id.trim(); if !category_id.is_empty() { return Some(serve_query(&file_path, &HashMap::from([("category_id", category_id)]))); - } else { - return Some(serve_file(&file_path, req, mime::APPLICATION_JSON).await); } + return Some(serve_file(&file_path, req, mime::APPLICATION_JSON).await); } else if let Some(payload) = content { return Some(HttpResponse::Ok().body(payload)); } @@ -435,58 +434,57 @@ async fn xtream_player_api_handle_content_action(config: &Config, target_name: & async fn xtream_player_api( req: &HttpRequest, api_req: UserApiRequest, - _app_state: &web::Data, + app_state: &web::Data, ) -> HttpResponse { - match get_user_target(&api_req, _app_state) { + match get_user_target(&api_req, app_state) { Some((user, target)) => { let action = api_req.action.trim(); let target_name = &target.name; if target.has_output(&TargetType::Xtream) { if action.is_empty() { - return HttpResponse::Ok().json(get_user_info(&user, &_app_state.config)); + return HttpResponse::Ok().json(get_user_info(&user, &app_state.config)); } match action { "get_series_info" => { - xtream_get_stream_info_response(_app_state, &user, target, + xtream_get_stream_info_response(app_state, &user, target, api_req.series_id.trim(), &XtreamCluster::Series).await } "get_vod_info" => { - xtream_get_stream_info_response(_app_state, &user, target, + xtream_get_stream_info_response(app_state, &user, target, api_req.vod_id.trim(), &XtreamCluster::Video).await } "get_epg" | "get_short_epg" => { - xtream_get_short_epg(_app_state, &user, target, + xtream_get_short_epg(app_state, &user, target, api_req.stream_id.trim(), api_req.limit.trim()).await } _ => { let category_id = api_req.category_id.as_str().trim(); - match xtream_player_api_handle_content_action(&_app_state.config, target_name, action, category_id, req).await { - Some(response) => response, - _ => { - let cat_id = if category_id.is_empty() { 0 } else { category_id.parse::().unwrap_or(0) }; - match match action { - "get_live_streams" => xtream_repository::xtream_load_rewrite_playlist(&XtreamCluster::Live, &_app_state.config, target, cat_id), - "get_vod_streams" => xtream_repository::xtream_load_rewrite_playlist(&XtreamCluster::Video, &_app_state.config, target, cat_id), - "get_series" => xtream_repository::xtream_load_rewrite_playlist(&XtreamCluster::Series, &_app_state.config, target, cat_id), - _ => Err(Error::new(ErrorKind::Unsupported, format!("Cant find action: {}/{}", target_name, action))), - } { - Ok(payload) => HttpResponse::Ok().body(payload), - Err(err) => { - error!("Could not create response for xtream target action: {} {} {}", target_name, action, err); - HttpResponse::NoContent().finish() - } + if let Some(response) = xtream_player_api_handle_content_action(&app_state.config, target_name, action, category_id, req).await { + response + } else { + let cat_id = if category_id.is_empty() { 0 } else { category_id.parse::().unwrap_or(0) }; + match match action { + "get_live_streams" => xtream_repository::xtream_load_rewrite_playlist(&XtreamCluster::Live, &app_state.config, target, cat_id), + "get_vod_streams" => xtream_repository::xtream_load_rewrite_playlist(&XtreamCluster::Video, &app_state.config, target, cat_id), + "get_series" => xtream_repository::xtream_load_rewrite_playlist(&XtreamCluster::Series, &app_state.config, target, cat_id), + _ => Err(Error::new(ErrorKind::Unsupported, format!("Cant find action: {target_name}/{action}"))), + } { + Ok(payload) => HttpResponse::Ok().body(payload), + Err(err) => { + error!("Could not create response for xtream target action: {} {} {}", target_name, action, err); + HttpResponse::NoContent().finish() } } } } } } else { - HttpResponse::Ok().json(get_user_info(&user, &_app_state.config)) + HttpResponse::Ok().json(get_user_info(&user, &app_state.config)) } } _ => { diff --git a/src/auth/user.rs b/src/auth/user.rs index 0c802e63d..ae6875b09 100644 --- a/src/auth/user.rs +++ b/src/auth/user.rs @@ -9,9 +9,9 @@ pub(crate) struct UserCredential { impl UserCredential { pub(crate) fn zeroize(&mut self) { + let password_ptr = self.password.as_mut_ptr(); + let password_len = self.password.len(); unsafe { - let password_ptr = self.password.as_mut_ptr(); - let password_len = self.password.len(); ptr::write_bytes(password_ptr, 0, password_len); } } diff --git a/src/filter.rs b/src/filter.rs index 9e954c902..c87443393 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -1,3 +1,4 @@ +#![allow(clippy::empty_docs)] use std::cell::RefCell; use enum_iterator::all; use std::collections::{HashMap}; @@ -71,7 +72,6 @@ pub(crate) struct RegexWithCaptures { } #[derive(Parser)] -//#[grammar = "filter.pest"] #[grammar_inline = r#" WHITESPACE = _{ " " | "\t" } field = { ^"group" | ^"title" | ^"name" | ^"url" } @@ -168,7 +168,7 @@ impl std::fmt::Display for Filter { let flt = match op { UnaryOperator::Not => format!("NOT {expr}"), }; - write!(f, "{}", flt) + write!(f, "{flt}") } Filter::BinaryExpression(left, op, right) => { write!(f, "{left} {op} {right}") @@ -177,7 +177,7 @@ impl std::fmt::Display for Filter { } } -fn get_parser_item_field(expr: Pair) -> Result { +fn get_parser_item_field(expr: &Pair) -> Result { if expr.as_rule() == Rule::field { let field_text = expr.as_str(); for item in all::() { @@ -189,7 +189,7 @@ fn get_parser_item_field(expr: Pair) -> Result create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "unknown field: {}", expr.as_str()) } -fn get_parser_regexp(expr: Pair, templates: &Vec) -> Result { +fn get_parser_regexp(expr: &Pair, templates: &Vec) -> Result { if expr.as_rule() == Rule::regexp { let mut parsed_text = String::from(expr.as_str()); parsed_text.pop(); @@ -219,9 +219,9 @@ fn get_parser_regexp(expr: Pair, templates: &Vec) -> Resu fn get_parser_comparison(expr: Pair, templates: &Vec) -> Result { let mut expr_inner = expr.into_inner(); - match get_parser_item_field(expr_inner.next().unwrap()) { + match get_parser_item_field(&expr_inner.next().unwrap()) { Ok(field) => { - match get_parser_regexp(expr_inner.next().unwrap(), templates) { + match get_parser_regexp(&expr_inner.next().unwrap(), templates) { Ok(regexp) => Ok(Filter::Comparison(field, regexp)), Err(err) => Err(err), } @@ -277,27 +277,27 @@ fn get_parser_expression(expr: Pair, templates: &Vec, err uop = Some(UnaryOperator::Not); } Rule::bool_op => { - match get_parser_binary_op(pair.into_inner().next().unwrap()) { + match get_parser_binary_op(&pair.into_inner().next().unwrap()) { Ok(binop) => { bop = Some(binop); } Err(err) => { - errors.push(format!("{}", err)); + errors.push(format!("{err}")); } } } _ => { - errors.push(format!("did not expect rule: {:?}", pair)) + errors.push(format!("did not expect rule: {pair:?}")); } } } if stmts.is_empty() || stmts.len() > 1 { - errors.push(format!("did not expect multiple rule: {:?}", stmts)); + errors.push(format!("did not expect multiple rule: {stmts:?}")); } stmts.pop().unwrap() } -fn get_parser_binary_op(expr: Pair) -> Result { +fn get_parser_binary_op(expr: &Pair) -> Result { match expr.as_rule() { Rule::and => Ok(BinaryOperator::And), Rule::or => Ok(BinaryOperator::Or), @@ -334,7 +334,7 @@ pub(crate) fn get_filter(filter_text: &str, templates: Option<&Vec { - match get_parser_binary_op(expr.into_inner().next().unwrap()) { + match get_parser_binary_op(&expr.into_inner().next().unwrap()) { Ok(binop) => { op = Some(binop); } @@ -344,7 +344,7 @@ pub(crate) fn get_filter(filter_text: &str, templates: Option<&Vec { - errors.push(format!("unknown expression {:?}", expr)); + errors.push(format!("unknown expression {expr:?}")); } } } @@ -381,15 +381,14 @@ fn build_dependency_graph(templates: &Vec) -> GraphDependency { let mut node_names = HashMap::new(); let mut node_deps = HashMap::new(); - let mut add_node = |di_graph: &mut DiGraph<_, _>, node_name: &String| match node_ids.get(node_name) { - Some(idx) => *idx, - _ => { - let key = node_name.clone(); - let idx = di_graph.add_node(node_name.clone()); - node_names.insert(idx.index(), key.clone()); - node_ids.insert(key, idx); - idx - } + let mut add_node = |di_graph: &mut DiGraph<_, _>, node_name: &String| if let Some(idx) = node_ids.get(node_name) { + *idx + } else { + let key = node_name.clone(); + let index = di_graph.add_node(node_name.clone()); + node_names.insert(index.index(), key.clone()); + node_ids.insert(key, index); + index }; for template in templates { @@ -412,14 +411,14 @@ fn build_dependency_graph(templates: &Vec) -> GraphDependency { .map(|scc| scc.iter().map(|&i| node_names.get(&i.index()).unwrap().clone()).collect()) .collect(); for cyclic in &cycles { - error!("Cyclic template dependencies detected [{}]", cyclic.join(" <-> ")) + error!("Cyclic template dependencies detected [{}]", cyclic.join(" <-> ")); } (graph, node_names, node_deps, !cycles.is_empty()) } pub(crate) fn prepare_templates(templates: &Vec) -> Result, M3uFilterError> { - let mut result: Vec = templates.to_vec(); + let mut result: Vec = templates.clone(); let (graph, node_map, node_deps, cyclic) = build_dependency_graph(templates); if cyclic { return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Cyclic dependencies in templates detected!"); @@ -439,7 +438,7 @@ pub(crate) fn prepare_templates(templates: &Vec) -> Result, msg: & let bot = rustelebot::create_instance(&telegram.bot_token, chat_id); match rustelebot::send_message(&bot, msg, None) { - Ok(_) => debug!("Text message sent successfully to {}", chat_id), + Ok(()) => debug!("Text message sent successfully to {}", chat_id), Err(e) => error!("Text message wasn't sent to {} because of: {}", chat_id, e) } }; } if let Some(rest) = &messaging.rest { - let url = rest.url.to_owned(); + let url = rest.url.clone(); let data = msg.to_owned(); actix_rt::spawn(async move { let client = reqwest::Client::new(); diff --git a/src/model/api_proxy.rs b/src/model/api_proxy.rs index cb429059a..a22d52fed 100644 --- a/src/model/api_proxy.rs +++ b/src/model/api_proxy.rs @@ -62,7 +62,7 @@ impl ProxyUserCredentials { self.username = config_reader::resolve_env_var(&self.username); self.password = config_reader::resolve_env_var(&self.password); if let Some(tkn) = &self.token { - self.token = Some(config_reader::resolve_env_var(tkn)) + self.token = Some(config_reader::resolve_env_var(tkn)); } self.trim(); } @@ -209,7 +209,7 @@ impl ApiProxyConfig { } if let Some(token) = &user.token { if token.is_empty() { - user.token = None + user.token = None; } else if tokens.contains(token) { errors.push(format!("Non unique token found {}", &user.username)); } else { diff --git a/src/model/config.rs b/src/model/config.rs index a24d493e3..5d1a24b01 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -9,7 +9,7 @@ use std::str::FromStr; use std::sync::{Arc, RwLock}; use log::{debug, error, warn}; -use path_absolutize::*; +use path_absolutize::Absolutize; use crate::auth::user::UserCredential; use crate::filter::{Filter, get_filter, MockValueProcessor, PatternTemplate, prepare_templates, ValueProvider}; @@ -216,7 +216,7 @@ pub(crate) struct ConfigSort { impl ConfigSort { pub(crate) fn prepare(&mut self) -> Result<(), M3uFilterError> { if let Some(channels) = self.channels.as_mut() { - handle_m3u_filter_error_result_list!(M3uFilterErrorKind::Info, channels.iter_mut().map(|r| r.prepare())); + handle_m3u_filter_error_result_list!(M3uFilterErrorKind::Info, channels.iter_mut().map(ConfigSortChannel::prepare)); } Ok(()) } @@ -297,11 +297,11 @@ pub(crate) struct ConfigTarget { #[serde(skip_serializing_if = "Option::is_none")] pub watch: Option>, #[serde(skip_serializing, skip_deserializing)] - pub _watch_re: Option>, + pub t_watch_re: Option>, #[serde(skip_serializing, skip_deserializing)] - pub _filter: Option, + pub t_filter: Option, #[serde(skip_serializing, skip_deserializing)] - pub _mapping: Option>, + pub t_mapping: Option>, } @@ -335,7 +335,7 @@ impl ConfigTarget { } if let Some(fname) = &format.filename { if !fname.trim().is_empty() { - warn!("Filename for target output xtream is ignored: {}", self.name) + warn!("Filename for target output xtream is ignored: {}", self.name); } } } @@ -349,7 +349,7 @@ impl ConfigTarget { 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), + Ok(watch_re) => self.t_watch_re = Some(watch_re), Err(err) => { return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Invalid watch regular expression: {}", err); } @@ -359,9 +359,9 @@ impl ConfigTarget { match get_filter(&self.filter, templates) { Ok(fltr) => { debug!("Filter: {}", fltr); - self._filter = Some(fltr); + self.t_filter = Some(fltr); if let Some(renames) = self.rename.as_mut() { - handle_m3u_filter_error_result_list!(M3uFilterErrorKind::Info, renames.iter_mut().map(|r| r.prepare())); + handle_m3u_filter_error_result_list!(M3uFilterErrorKind::Info, renames.iter_mut().map(ConfigRename::prepare)); } if let Some(sort) = self.sort.as_mut() { handle_m3u_filter_error_result!(M3uFilterErrorKind::Info, sort.prepare()); @@ -374,15 +374,13 @@ impl ConfigTarget { pub(crate) fn filter(&self, provider: &ValueProvider) -> bool { let mut processor = MockValueProcessor {}; - return self._filter.as_ref().unwrap().filter(provider, &mut processor); + return self.t_filter.as_ref().unwrap().filter(provider, &mut processor); } pub(crate) fn get_m3u_filename(&self) -> Option { for format in &self.output { - match format.target { - TargetType::M3u => return format.filename.clone(), - TargetType::Strm => {} - TargetType::Xtream => {} + if let TargetType::M3u = format.target { + return format.filename.clone(); } } None @@ -405,6 +403,7 @@ pub(crate) struct ConfigSource { } impl ConfigSource { + #[allow(clippy::cast_possible_truncation)] pub(crate) fn prepare(&mut self, index: u16) -> Result { handle_m3u_filter_error_result_list!(M3uFilterErrorKind::Info, self.inputs.iter_mut().enumerate().map(|(idx, i)| i.prepare(index+(idx as u16)))); Ok(index + (self.inputs.len() as u16)) @@ -529,7 +528,7 @@ impl ConfigInput { match self.input_type { InputType::M3u => { if self.username.is_none() || self.password.is_none() { - debug!("for input type m3u: username and password are ignored") + debug!("for input type m3u: username and password are ignored"); } } InputType::Xtream => { @@ -550,7 +549,7 @@ impl ConfigInput { if self.input_type == InputType::Xtream { if self.username.is_some() || self.password.is_some() { return Some(InputUserInfo { - base_url: self.url.to_owned(), + base_url: self.url.clone(), username: self.username.as_ref().unwrap().to_owned(), password: self.password.as_ref().unwrap().to_owned(), }); @@ -621,11 +620,11 @@ pub(crate) struct VideoDownloadConfig { pub organize_into_directories: bool, pub episode_pattern: Option, #[serde(skip_serializing, skip_deserializing)] - pub _re_episode_pattern: Option, + pub t_re_episode_pattern: Option, #[serde(skip_serializing, skip_deserializing)] - pub _re_filename: Option, + pub t_re_filename: Option, #[serde(skip_serializing, skip_deserializing)] - pub _re_remove_filename_ending: Option, + pub t_re_remove_filename_ending: Option, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] @@ -655,12 +654,12 @@ impl VideoConfig { if re.is_err() { return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "cant parse regex: {}", episode_pattern); } - downl._re_episode_pattern = Some(re.unwrap()); + downl.t_re_episode_pattern = Some(re.unwrap()); } } - downl._re_filename = Some(regex::Regex::new(r"[^A-Za-z0-9_.-]").unwrap()); - downl._re_remove_filename_ending = Some(regex::Regex::new(r"[_.\s-]$").unwrap()); + downl.t_re_filename = Some(regex::Regex::new(r"[^A-Za-z0-9_.-]").unwrap()); + downl.t_re_remove_filename_ending = Some(regex::Regex::new(r"[_.\s-]$").unwrap()); } } Ok(()) @@ -708,7 +707,8 @@ pub(crate) struct WebAuthConfig { pub issuer: String, pub secret: String, pub userfile: Option, - pub _users: Option>, + #[serde(skip_serializing, skip_deserializing)] + pub t_users: Option>, } impl WebAuthConfig { @@ -724,7 +724,7 @@ impl WebAuthConfig { None => file_utils::get_default_user_file_path(config_path), Some(file) => file.to_owned() }; - self.userfile = Some(userfile_name.to_owned()); + self.userfile = Some(userfile_name.clone()); let mut userfile_path = PathBuf::from(&userfile_name); if !file_utils::path_exists(&userfile_path) { @@ -748,7 +748,7 @@ impl WebAuthConfig { } } - self._users = Some(users); + self.t_users = Some(users); } else { return create_m3u_filter_error_result!(M3uFilterErrorKind::Info, "Could not read userfile {:?}", &userfile_path); } @@ -756,7 +756,7 @@ impl WebAuthConfig { } pub fn get_user_password(&self, username: &str) -> Option<&str> { - if let Some(users) = &self._users { + if let Some(users) = &self.t_users { for credential in users { if credential.username.eq_ignore_ascii_case(username) { return Some(credential.password.as_str()); @@ -785,21 +785,21 @@ pub(crate) struct Config { pub web_auth: Option, pub messaging: Option, #[serde(skip_serializing, skip_deserializing)] - pub _api_proxy: Arc>>, + pub t_api_proxy: Arc>>, #[serde(skip_serializing, skip_deserializing)] - pub _config_path: String, + pub t_config_path: String, #[serde(skip_serializing, skip_deserializing)] - pub _config_file_path: String, + pub t_config_file_path: String, #[serde(skip_serializing, skip_deserializing)] - pub _sources_file_path: String, + pub t_sources_file_path: String, #[serde(skip_serializing, skip_deserializing)] - pub _api_proxy_file_path: String, + pub t_api_proxy_file_path: String, } impl Config { pub fn set_api_proxy(&mut self, api_proxy: Option) { - self._api_proxy = Arc::new(RwLock::new(api_proxy)); + self.t_api_proxy = Arc::new(RwLock::new(api_proxy)); } fn _get_target_for_user(&self, user_target: Option<(ProxyUserCredentials, String)>) -> Option<(ProxyUserCredentials, &ConfigTarget)> { @@ -828,7 +828,7 @@ impl Config { } pub fn get_target_for_user(&self, username: &str, password: &str) -> Option<(ProxyUserCredentials, &ConfigTarget)> { - match self._api_proxy.read().unwrap().as_ref() { + match self.t_api_proxy.read().unwrap().as_ref() { Some(api_proxy) => { self._get_target_for_user(api_proxy.get_target_name(username, password)) } @@ -837,7 +837,7 @@ impl Config { } pub fn get_target_for_user_by_token(&self, token: &str) -> Option<(ProxyUserCredentials, &ConfigTarget)> { - match self._api_proxy.read().unwrap().as_ref() { + match self.t_api_proxy.read().unwrap().as_ref() { Some(api_proxy) => { self._get_target_for_user(api_proxy.get_target_name_by_token(token)) } @@ -845,10 +845,10 @@ impl Config { } } - pub(crate) fn get_input_by_id(&self, input_id: &u16) -> Option<&ConfigInput> { + pub(crate) fn get_input_by_id(&self, input_id: u16) -> Option<&ConfigInput> { for source in &self.sources { for input in &source.inputs { - if input.id == *input_id { + if input.id == input_id { return Some(input); } } @@ -856,7 +856,7 @@ impl Config { None } - pub(crate) fn set_mappings(&mut self, mappings: Option) -> Result<(), M3uFilterError> { + pub(crate) fn set_mappings(&mut self, mappings: Option) { if let Some(mapping_list) = mappings { for source in &mut self.sources { for target in &mut source.targets { @@ -868,12 +868,11 @@ impl Config { target_mappings.push(mappings); } } - target._mapping = if !target_mappings.is_empty() { Some(target_mappings) } else { None }; + target.t_mapping = if target_mappings.is_empty() { None } else { Some(target_mappings) }; } } } } - Ok(()) } pub fn prepare(&mut self, resolve_var: bool) -> Result<(), M3uFilterError> { @@ -884,7 +883,7 @@ impl Config { let backupdir = PathBuf::from(self.backup_dir.as_ref().unwrap()); if !backupdir.exists() { match std::fs::create_dir(backupdir) { - Ok(_) => {} + Ok(()) => {} Err(err) => { error!("Could not create backup dir {} {}", self.backup_dir.as_ref().unwrap(), err) } } } @@ -936,7 +935,7 @@ impl Config { } Some(video) => { match video.prepare() { - Ok(_) => {} + Ok(()) => {} Err(err) => return Err(err) } } @@ -947,10 +946,10 @@ impl Config { } if let Some(web_auth) = &mut self.web_auth { - if !web_auth.enabled { - self.web_auth = None + if web_auth.enabled { + web_auth.prepare(&self.t_config_path, resolve_var)?; } else { - web_auth.prepare(&self._config_path, resolve_var)? + self.web_auth = None; } } diff --git a/src/model/mapping.rs b/src/model/mapping.rs index bf7272b70..a9fbd88de 100644 --- a/src/model/mapping.rs +++ b/src/model/mapping.rs @@ -8,7 +8,7 @@ use regex::Regex; use crate::{handle_m3u_filter_error_result, valid_property}; use crate::filter::{Filter, get_filter, PatternTemplate, prepare_templates, RegexWithCaptures, ValueProcessor}; use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind}; -use crate::model::config::{AFFIX_FIELDS, ItemField, MAPPER_ATTRIBUTE_FIELDS, }; +use crate::model::config::{AFFIX_FIELDS, ItemField, MAPPER_ATTRIBUTE_FIELDS}; use crate::model::playlist::{FieldAccessor, PlaylistItem}; use crate::utils::default_utils::{default_as_empty_map, default_as_empty_str, default_as_false}; @@ -38,37 +38,37 @@ pub(crate) struct Mapper { #[serde(default = "default_as_empty_map")] assignments: HashMap, #[serde(skip_serializing, skip_deserializing)] - pub(crate) _filter: Option, + pub(crate) t_filter: Option, #[serde(skip_serializing, skip_deserializing)] - pub(crate) _pattern: Option, + pub(crate) t_pattern: Option, #[serde(skip_serializing, skip_deserializing)] - pub _tags: Vec, + pub t_tags: Vec, #[serde(skip_serializing, skip_deserializing)] - pub _tagre: Option, + pub t_tagre: Option, #[serde(skip_serializing, skip_deserializing)] - pub _attre: Option, + pub t_attre: Option, } impl Mapper { pub fn prepare(&mut self, templates: Option<&Vec>, tags: Option<&Vec>) -> Result<(), M3uFilterError> { match get_filter(&self.pattern, templates) { Ok(pattern) => { - self._pattern = Some(pattern); + self.t_pattern = Some(pattern); match &self.filter { Some(flt) => { match get_filter(flt, templates) { - Ok(filter) => self._filter = Some(filter), + Ok(filter) => self.t_filter = Some(filter), Err(err) => return Err(err), } } - _ => self._filter = None + _ => self.t_filter = None } - self._tags = match tags { + self.t_tags = match tags { Some(list) => list.clone(), _ => vec![] }; - self._tagre = Some(Regex::new("").unwrap()); - self._attre = Some(Regex::new("<(.*?)>").unwrap()); + self.t_tagre = Some(Regex::new("").unwrap()); + self.t_attre = Some(Regex::new("<(.*?)>").unwrap()); Ok(()) } Err(err) => Err(err) @@ -95,14 +95,14 @@ impl MappingValueProcessor<'_> { fn apply_attributes(&mut self, captured_names: &HashMap<&str, &str>) { let mapper = self.mapper; - let attr_re = &mapper._attre.as_ref().unwrap(); + let attr_re = &mapper.t_attre.as_ref().unwrap(); let attributes = &mapper.attributes; for (key, value) in attributes { if valid_property!(key.as_str(), MAPPER_ATTRIBUTE_FIELDS) { if value.contains('<') { // possible replacement let replaced = attr_re.replace_all(value, |captures: ®ex::Captures| { let capture_name = &captures[1]; - captured_names.get(&capture_name).unwrap_or(&&captures[0]).to_string() + (*captured_names.get(&capture_name).unwrap_or(&&captures[0])).to_string() }); self.set_property(key, &replaced); } else { @@ -114,34 +114,33 @@ impl MappingValueProcessor<'_> { fn apply_tags(&mut self, value: &String, captures: &HashMap<&str, &str>) -> Option { let mut new_value = String::from(value); - let tag_captures = self.mapper._tagre.as_ref().unwrap().captures_iter(value) + let tag_captures = self.mapper.t_tagre.as_ref().unwrap().captures_iter(value) .filter(|caps| caps.len() > 1) .filter_map(|caps| caps.get(1)) .map(|caps| caps.as_str()) .collect::>(); for tag_capture in tag_captures { - for mapping_tag in &self.mapper._tags { + for mapping_tag in &self.mapper.t_tags { if mapping_tag.name.eq(tag_capture) { // we have the right tag, now get all captured values let mut captured_tag_values: Vec<&str> = Vec::new(); for cap in &mapping_tag.captures { - match captures.get(cap.as_str()) { - Some(cap_value) => captured_tag_values.push(cap_value), - _ => { - debug!("Cant find any tag match for {}", tag_capture); - return None; - } + if let Some(cap_value) = captures.get(cap.as_str()) { + captured_tag_values.push(cap_value); + } else { + debug!("Cant find any tag match for {}", tag_capture); + return None; } } if !captured_tag_values.is_empty() { let captured_text = captured_tag_values.join(&mapping_tag.concat); - let replacement = if !captured_text.trim().is_empty() { - // Now we have all our captured values, lets create the tag - format!("{}{}{}", &mapping_tag.prefix, captured_text, &mapping_tag.suffix) - } else { + let replacement = if captured_text.trim().is_empty() { // nothing found so replace tag with empty string - String::from("") + String::new() + } else { + // Now we have all our captured values, lets create the tag + format!("{}{captured_text}{}", &mapping_tag.prefix, &mapping_tag.suffix) }; new_value = new_value.replace(format!("", mapping_tag.name).as_str(), replacement.as_str()); } @@ -215,10 +214,10 @@ impl ValueProcessor for MappingValueProcessor<'_> { } ); } - let _ = &MappingValueProcessor::<'_>::apply_attributes(self, &captured_values); - let _ = &MappingValueProcessor::<'_>::apply_suffix(self, &captured_values); - let _ = &MappingValueProcessor::<'_>::apply_prefix(self, &captured_values); - let _ = &MappingValueProcessor::<'_>::apply_assignments(self); + let () = &MappingValueProcessor::<'_>::apply_attributes(self, &captured_values); + let () = &MappingValueProcessor::<'_>::apply_suffix(self, &captured_values); + let () = &MappingValueProcessor::<'_>::apply_prefix(self, &captured_values); + let () = &MappingValueProcessor::<'_>::apply_assignments(self); true } } diff --git a/src/model/playlist.rs b/src/model/playlist.rs index f65164977..4983280b4 100644 --- a/src/model/playlist.rs +++ b/src/model/playlist.rs @@ -22,7 +22,7 @@ pub(crate) struct FetchedPlaylist<'a> { impl FetchedPlaylist<'_> { pub(crate) fn update_playlist(&mut self, plg: &PlaylistGroup) { - for grp in self.playlist.iter_mut() { + for grp in &mut self.playlist { if grp.id == plg.id { plg.channels.iter().for_each(|item| grp.channels.push(item.clone())); return; diff --git a/src/model/stats.rs b/src/model/stats.rs index b752b4987..627b96e1b 100644 --- a/src/model/stats.rs +++ b/src/model/stats.rs @@ -27,6 +27,6 @@ impl Display for InputStats { let str = format!("{{\"name\": {}, \"type\": {}, \"errors\": {}, \"raw\": {}, \"processed\": {}}}", self.name, self.input_type, self.error_count, self.raw_stats, self.processed_stats); - write!(f, "{}", str) + write!(f, "{str}") } } \ No newline at end of file diff --git a/src/model/xmltv.rs b/src/model/xmltv.rs index d6ce91128..8c017599c 100644 --- a/src/model/xmltv.rs +++ b/src/model/xmltv.rs @@ -26,13 +26,13 @@ impl XmlTag { fn write_to(&self, writer: &mut Writer) -> Result<(), Error> { let mut elem = BytesStart::new(self.name.as_str()); if let Some(attribs) = self.attributes.as_ref() { - attribs.iter().for_each(|(k, v)| elem.push_attribute((k.as_str(), v.as_str()))) + attribs.iter().for_each(|(k, v)| elem.push_attribute((k.as_str(), v.as_str()))); } writer.write_event(Event::Start(elem))?; self.value.as_ref().map(|text| writer.write_event(Event::Text(BytesText::new(text.as_str())))); if let Some(children) = &self.children { for child in children { - child.write_to(writer)? + child.write_to(writer)?; } } writer.write_event(Event::End(BytesEnd::new(self.name.as_str()))) @@ -50,11 +50,11 @@ impl Epg { pub(crate) fn write_to(&self, writer: &mut Writer) -> Result<(), quick_xml::Error> { let mut elem = BytesStart::new("tv"); if let Some(attribs) = self.attributes.as_ref() { - attribs.iter().for_each(|(k, v)| elem.push_attribute((k.as_str(), v.as_str()))) + attribs.iter().for_each(|(k, v)| elem.push_attribute((k.as_str(), v.as_str()))); } writer.write_event(Event::Start(elem))?; for child in &self.children { - child.write_to(writer)? + child.write_to(writer)?; } writer.write_event(Event::End(BytesEnd::new("tv"))) } diff --git a/src/model/xtream.rs b/src/model/xtream.rs index e83c6c518..ba6522615 100644 --- a/src/model/xtream.rs +++ b/src/model/xtream.rs @@ -59,8 +59,8 @@ fn deserialize_number_from_string<'de, D, T: DeserializeOwned>( } } -fn value_to_string_array(value: &[Value]) -> Option> { - Some(value.iter().filter_map(value_to_string).collect()) +fn value_to_string_array(value: &[Value]) -> Vec { + value.iter().filter_map(value_to_string).collect() } fn value_to_string(v: &Value) -> Option { @@ -102,7 +102,7 @@ fn deserialize_as_string_array<'de, D>(deserializer: D) -> Result, { Value::deserialize(deserializer).map(|v| match v { - Value::Array(value) => value_to_string_array(&value), + Value::Array(value) => Some(value_to_string_array(&value)), _ => None, }) } @@ -220,7 +220,7 @@ macro_rules! add_i64_property_if_exists { impl XtreamStream { pub(crate) fn get_stream_id(&self) -> String { - self.stream_id.map_or_else(|| self.series_id.map_or_else(|| String::from(""), |seid| format!("{}", seid)), |sid| format!("{}", sid)) + self.stream_id.map_or_else(|| self.series_id.map_or_else(String::new, |seid| format!("{seid}")), |sid| format!("{sid}")) } pub(crate) fn get_additional_properties(&self) -> Option { @@ -378,7 +378,7 @@ fn append_release_date(document: &mut serde_json::Map) { document.get("releaseDate") } else { None - }.map_or_else(|| Value::Null, |v| v.clone()); + }.map_or_else(|| Value::Null, std::clone::Clone::clone); if !&has_release_date_1 { document.insert("release_date".to_string(), release_date.clone()); } @@ -428,12 +428,12 @@ pub(crate) fn xtream_playlistitem_to_document(pli: &XtreamPlaylistItem, options: XtreamCluster::Live => { document.insert("stream_id".to_string(), stream_id_value); if options.skip_live_direct_source { - document.insert("direct_source".to_string(), Value::String("".to_string())); + document.insert("direct_source".to_string(), Value::String(String::new())); } else { document.insert("direct_source".to_string(), Value::String(pli.url.as_ref().clone())); } document.insert("thumbnail".to_string(), Value::String(pli.logo_small.as_ref().clone())); - document.insert("custom_sid".to_string(), Value::String("".to_string())); + document.insert("custom_sid".to_string(), Value::String(String::new())); document.insert("epg_channel_id".to_string(), match &pli.epg_channel_id { None => Value::Null, Some(epg_id) => Value::String(epg_id.as_ref().clone()) @@ -442,11 +442,11 @@ pub(crate) fn xtream_playlistitem_to_document(pli: &XtreamPlaylistItem, options: XtreamCluster::Video => { document.insert("stream_id".to_string(), stream_id_value); if options.skip_video_direct_source { - document.insert("direct_source".to_string(), Value::String("".to_string())); + document.insert("direct_source".to_string(), Value::String(String::new())); } else { document.insert("direct_source".to_string(), Value::String(pli.url.as_ref().clone())); } - document.insert("custom_sid".to_string(), Value::String("".to_string())); + document.insert("custom_sid".to_string(), Value::String(String::new())); } XtreamCluster::Series => { document.insert("series_id".to_string(), stream_id_value); diff --git a/src/processing/affix_processor.rs b/src/processing/affix_processor.rs index 435968f5f..9745371ce 100644 --- a/src/processing/affix_processor.rs +++ b/src/processing/affix_processor.rs @@ -53,14 +53,14 @@ fn get_affix_processor(input: &ConfigInput) -> Option { } pub fn apply_affixes(fetched_playlists: &mut [FetchedPlaylist]) { - fetched_playlists.iter_mut().for_each(|fetched_playlist| { + for fetched_playlist in fetched_playlists.iter_mut() { let FetchedPlaylist { input, playlist, epg: _ } = fetched_playlist; if let Some(affix_processor) = get_affix_processor(input) { - playlist.iter_mut().for_each(|group| { + for group in playlist.iter_mut() { group.channels.iter_mut().for_each(|channel| { affix_processor(channel); }); - }); + } } - }); + } } diff --git a/src/processing/m3u_parser.rs b/src/processing/m3u_parser.rs index 3ce19cc72..b2e25e880 100644 --- a/src/processing/m3u_parser.rs +++ b/src/processing/m3u_parser.rs @@ -13,7 +13,7 @@ fn token_value(it: &mut std::str::Chars) -> String { return get_value(it); } } - String::from("") + String::new() } fn get_value(it: &mut std::str::Chars) -> String { @@ -34,11 +34,10 @@ fn token_till(it: &mut std::str::Chars, stop_char: char) -> Option { break; } else if ch.is_whitespace() && result.is_empty() { continue; - } else { - result.push(ch); } + result.push(ch); } - if !result.is_empty() { Some(result.iter().collect::()) } else { None } + if result.is_empty() { None } else { Some(result.iter().collect::()) } } fn skip_digit(it: &mut std::str::Chars) -> Option { @@ -54,7 +53,7 @@ fn skip_digit(it: &mut std::str::Chars) -> Option { } } -fn create_empty_playlistitem_header(input_id: u16, url: String) -> PlaylistItemHeader { +fn create_empty_playlistitem_header(input_id: u16, url: &str) -> PlaylistItemHeader { PlaylistItemHeader { id: default_as_empty_rc_str(), stream_id: default_as_empty_rc_str(), @@ -67,7 +66,7 @@ fn create_empty_playlistitem_header(input_id: u16, url: String) -> PlaylistItemH audio_track: default_as_empty_rc_str(), time_shift: default_as_empty_rc_str(), rec: default_as_empty_rc_str(), - url: Rc::new(url), + url: Rc::new(url.to_owned()), epg_channel_id: None, item_type: default_playlist_item_type(), xtream_cluster: default_stream_cluster(), @@ -89,8 +88,8 @@ macro_rules! process_header_fields { }; } -fn process_header(input: &ConfigInput, video_suffixes: &Vec<&str>, content: &str, url: String) -> PlaylistItemHeader { - let mut plih = create_empty_playlistitem_header(input.id, url.clone()); +fn process_header(input: &ConfigInput, video_suffixes: &Vec<&str>, content: &str, url: &str) -> PlaylistItemHeader { + let mut plih = create_empty_playlistitem_header(input.id, url); let mut it = content.chars(); let line_token = token_till(&mut it, ':'); if line_token == Some(String::from("#EXTINF")) { @@ -99,13 +98,13 @@ fn process_header(input: &ConfigInput, video_suffixes: &Vec<&str>, content: &str if c.is_none() { break; } - match c.unwrap() { - ',' => plih.title = Rc::new(get_value(&mut it)), - _ => { - let token = token_till(&mut it, '='); - if let Some(t) = token { - let value = token_value(&mut it); - process_header_fields!(plih, t.as_str(), + if let ',' = c.unwrap() { + plih.title = Rc::new(get_value(&mut it)); + } else { + let token = token_till(&mut it, '='); + if let Some(t) = token { + let value = token_value(&mut it); + process_header_fields!(plih, t.as_str(), (id, "tvg-id"), (group, "group-title"), (name, "tvg-name"), @@ -114,14 +113,13 @@ fn process_header(input: &ConfigInput, video_suffixes: &Vec<&str>, content: &str (logo, "tvg-logo"), (logo_small, "tvg-logo-small"), (time_shift, "timeshift"), - (rec, "tvg-rec"); value) - } + (rec, "tvg-rec"); value); } } c = it.next(); } if plih.id.is_empty() { - if let Some(chanid) = extract_id_from_url(url.as_str()) { + if let Some(chanid) = extract_id_from_url(url) { plih.id = Rc::new(chanid); } } @@ -151,7 +149,7 @@ fn process_header(input: &ConfigInput, video_suffixes: &Vec<&str>, content: &str fn extract_id_from_url(url: &str) -> Option { if let Some(filename) = url.split('/').last() { - return filename.rsplit('.').next().map(|stem| stem.to_string()); + return filename.rsplit('.').next().map(std::string::ToString::to_string); } None } @@ -160,7 +158,7 @@ pub(crate) fn consume_m3u(cfg: &Config, input: &ConfigIn let mut header: Option = None; let mut group: Option = None; - let video_suffixes = cfg.video.as_ref().unwrap().extensions.iter().map(|ext| ext.as_str()).collect(); + let video_suffixes = cfg.video.as_ref().unwrap().extensions.iter().map(std::string::String::as_str).collect(); for line in lines { if line.starts_with("#EXTINF") { header = Some(line); @@ -174,13 +172,13 @@ pub(crate) fn consume_m3u(cfg: &Config, input: &ConfigIn continue; } if let Some(header_value) = header { - let item = PlaylistItem { header: RefCell::new(process_header(input, &video_suffixes, &header_value, line)) }; + let item = PlaylistItem { header: RefCell::new(process_header(input, &video_suffixes, &header_value, &line)) }; let mut header = item.header.borrow_mut(); if header.group.is_empty() { if let Some(group_value) = group { header.group = Rc::new(group_value); } else { - let current_title = header.title.to_owned(); + let current_title = header.title.clone(); header.group = Rc::new(string_utils::get_title_group(current_title.as_str())); } } diff --git a/src/processing/playlist_processor.rs b/src/processing/playlist_processor.rs index bcb2b3db8..f39c6c9c4 100644 --- a/src/processing/playlist_processor.rs +++ b/src/processing/playlist_processor.rs @@ -26,17 +26,18 @@ use crate::processing::xmltv_parser::flatten_tvguide; use crate::processing::xtream_processor::playlist_resolve_series; use crate::repository::playlist_repository::persist_playlist; use crate::utils::download; -use crate::utils::default_utils:: default_as_default; +use crate::utils::default_utils::default_as_default; fn is_valid(pli: &PlaylistItem, target: &ConfigTarget) -> bool { let provider = ValueProvider { pli: RefCell::new(pli) }; target.filter(&provider) } +#[allow(clippy::unnecessary_wraps)] fn filter_playlist(playlist: &mut [PlaylistGroup], target: &ConfigTarget) -> Option> { debug!("Filtering {} groups", playlist.len()); let mut new_playlist = Vec::new(); - playlist.iter_mut().for_each(|pg| { + for pg in playlist.iter_mut() { let channels = pg.channels.iter() .filter(|&pli| is_valid(pli, target)).cloned().collect::>(); debug!("Filtered group {} has now {}/{} items", pg.title, channels.len(), pg.channels.len()); @@ -48,7 +49,7 @@ fn filter_playlist(playlist: &mut [PlaylistGroup], target: &ConfigTarget) -> Opt xtream_cluster: pg.xtream_cluster.clone(), }); } - }); + } Some(new_playlist) } @@ -81,15 +82,15 @@ fn sort_playlist(target: &ConfigTarget, new_playlist: &mut [PlaylistGroup]) { new_playlist.sort_by(|a, b| playlistgroup_comparator(a, b, group_sort, match_as_ascii)); } if let Some(channel_sorts) = &sort.channels { - channel_sorts.iter().for_each(|channel_sort| { + for channel_sort in channel_sorts { let regexp = channel_sort.re.as_ref().unwrap(); - new_playlist.iter_mut().for_each(|group| { + for group in new_playlist.iter_mut() { let group_title = if match_as_ascii { Rc::new(unidecode(&group.title)) } else { Rc::clone(&group.title) }; if regexp.is_match(group_title.as_str()) { group.channels.sort_by(|chan1, chan2| playlistitem_comparator(chan1, chan2, channel_sort, match_as_ascii)); } - }); - }); + } + } } } } @@ -158,14 +159,14 @@ fn map_channel(channel: PlaylistItem, mapping: &Mapping) -> PlaylistItem { let mut mock_processor = MockValueProcessor {}; for m in &mapping.mapper { let mut processor = MappingValueProcessor { pli: ref_chan.clone(), mapper: m }; - match &m._filter { + match &m.t_filter { Some(filter) => { if filter.filter(&provider, &mut mock_processor) { - apply_pattern!(&m._pattern, &provider, &mut processor); + apply_pattern!(&m.t_pattern, &provider, &mut processor); } } _ => { - apply_pattern!(&m._pattern, &provider, &mut processor); + apply_pattern!(&m.t_pattern, &provider, &mut processor); } }; } @@ -174,10 +175,10 @@ fn map_channel(channel: PlaylistItem, mapping: &Mapping) -> PlaylistItem { } fn map_playlist(playlist: &mut [PlaylistGroup], target: &ConfigTarget) -> Option> { - if target._mapping.is_some() { + if target.t_mapping.is_some() { let new_playlist: Vec = playlist.iter().map(|playlist_group| { let mut grp = playlist_group.clone(); - let mappings = target._mapping.as_ref().unwrap(); + let mappings = target.t_mapping.as_ref().unwrap(); mappings.iter().filter(|&mapping| !mapping.mapper.is_empty()).for_each(|mapping| grp.channels = grp.channels.drain(..).map(|chan| map_channel(chan, mapping)).collect()); grp @@ -191,17 +192,16 @@ fn map_playlist(playlist: &mut [PlaylistGroup], target: &ConfigTarget) -> Option for channel in &playlist_group.channels { let cluster = &channel.header.borrow().xtream_cluster; let title = &channel.header.borrow().group; - match new_groups.iter_mut().find(|x| *x.title == **title) { - Some(grp) => grp.channels.push(channel.clone()), - _ => { - grp_id += 1; - new_groups.push(PlaylistGroup { - id: grp_id, - title: Rc::clone(title), - channels: vec![channel.clone()], - xtream_cluster: cluster.clone(), - }) - } + if let Some(grp) = new_groups.iter_mut().find(|x| *x.title == **title) { + grp.channels.push(channel.clone()); + } else { + grp_id += 1; + new_groups.push(PlaylistGroup { + id: grp_id, + title: Rc::clone(title), + channels: vec![channel.clone()], + xtream_cluster: cluster.clone(), + }); } } } @@ -255,7 +255,7 @@ async fn process_source(cfg: Arc, source_idx: usize, user_targets: Arc

, source_idx: usize, user_targets: Arc

, source_idx: usize, user_targets: Arc

{} + Ok(()) => {} Err(mut err) => err.drain(..).for_each(|e| errors.push(e)) } } @@ -327,7 +327,7 @@ async fn process_sources(config: Arc, user_targets: Arc) .for_each(|stat| shared_stats.lock().unwrap().push(stat)); }; handles.push(thread::spawn(process)); - if handles.len() as u8 >= thread_num { + if handles.len() >= thread_num as usize { handles.drain(..).for_each(|handle| { let _ = handle.join(); }); } } else { @@ -414,38 +414,40 @@ async fn process_playlist<'a>(playlists: &mut [FetchedPlaylist<'a>], } }); - if !new_playlist.is_empty() { + if new_playlist.is_empty() { + info!("Playlist is empty: {}", &target.name); + Ok(()) + } else { sort_playlist(target, &mut new_playlist); - if target._watch_re.is_some() { + if target.t_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 watch_re = target._watch_re.as_ref().unwrap(); - new_playlist.iter().for_each(|pl| { + let watch_re = target.t_watch_re.as_ref().unwrap(); + for pl in &new_playlist { if watch_re.iter().any(|r| r.is_match(&pl.title)) { - process_group_watch(cfg, &target.name, pl) + process_group_watch(cfg, &target.name, pl); } - }); + } } } - persist_playlist(&mut new_playlist, flatten_tvguide(&new_epg), target, cfg) - } else { - info!("Playlist is empty: {}", &target.name); - Ok(()) + persist_playlist(&mut new_playlist, flatten_tvguide(&new_epg).as_ref(), target, cfg) } } pub(crate) async fn exec_processing(cfg: Arc, targets: Arc) { - let (stats, errors) = process_sources(cfg.to_owned(), targets.to_owned()).await; - let stats_msg = format!("{{\"stats\": {}}}", stats.iter().map(|stat| stat.to_string()).collect::>().join("\n")); + let (stats, errors) = process_sources(cfg.clone(), targets.clone()).await; + let stats_msg = format!("{{\"stats\": {}}}", stats.iter().map(std::string::ToString::to_string).collect::>().join("\n")); // print stats info!("{}", stats_msg); // send stats send_message(&MsgKind::Stats, &cfg.messaging, stats_msg.as_str()); // log errors - errors.iter().for_each(|err| error!("{}", err.message)); + for err in &errors { + error!("{}", err.message); + } // send errors if let Some(message) = get_errors_notify_message!(errors, 255) { let error_msg = format!("{{\"errors\": \"{}\"}}", message.as_str()); diff --git a/src/processing/playlist_watch.rs b/src/processing/playlist_watch.rs index 4a878a7fc..99e3c9afd 100644 --- a/src/processing/playlist_watch.rs +++ b/src/processing/playlist_watch.rs @@ -23,29 +23,26 @@ pub(crate) fn process_group_watch(cfg: &Config, target_name: &str, pl: &Playlist let save_path = path.clone(); let mut changed = false; if path.exists() { - match load_watch_tree(&path) { - Some(loaded_tree) => { - // Find elements in set2 but not in set1 - let added_difference: BTreeSet = new_tree.difference(&loaded_tree).cloned().collect(); - let removed_difference: BTreeSet = loaded_tree.difference(&new_tree).cloned().collect(); - if !added_difference.is_empty() || !removed_difference.is_empty() { - changed = true; - handle_watch_notification(cfg, added_difference, removed_difference, target_name, &pl.title); - } - } - None => { - error!("failed to load watch_file {}", &path.to_str().unwrap_or_default()); + if let Some(loaded_tree) = load_watch_tree(&path) { + // Find elements in set2 but not in set1 + let added_difference: BTreeSet = new_tree.difference(&loaded_tree).cloned().collect(); + let removed_difference: BTreeSet = loaded_tree.difference(&new_tree).cloned().collect(); + if !added_difference.is_empty() || !removed_difference.is_empty() { changed = true; + handle_watch_notification(cfg, &added_difference, &removed_difference, target_name, &pl.title); } + } else { + 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) { - Ok(_) => {} + match save_watch_tree(&save_path, &new_tree) { + Ok(()) => {} Err(err) => { - error!("failed to write watch_file {}: {}", &save_path.to_str().unwrap_or_default(), err) + error!("failed to write watch_file {}: {}", &save_path.to_str().unwrap_or_default(), err); } } } @@ -56,9 +53,9 @@ pub(crate) fn process_group_watch(cfg: &Config, target_name: &str, pl: &Playlist } } -fn handle_watch_notification(cfg: &Config, added: BTreeSet, removed: BTreeSet, target_name: &str, group_name: &str) { - let added_entries = added.iter().map(|name| name.to_string()).collect::>().join("\n\t"); - let removed_entries = removed.iter().map(|name| name.to_string()).collect::>().join("\n\t"); +fn handle_watch_notification(cfg: &Config, added: &BTreeSet, removed: &BTreeSet, target_name: &str, group_name: &str) { + let added_entries = added.iter().map(std::string::ToString::to_string).collect::>().join("\n\t"); + let removed_entries = removed.iter().map(std::string::ToString::to_string).collect::>().join("\n\t"); let mut message = vec![]; if !added_entries.is_empty() { @@ -89,7 +86,7 @@ fn load_watch_tree(path: &Path) -> Option> { } } -fn save_watch_tree(path: &Path, tree: BTreeSet) -> std::io::Result<()> { +fn save_watch_tree(path: &Path, tree: &BTreeSet) -> std::io::Result<()> { let encoded: Vec = bincode::serialize(&tree).unwrap(); std::fs::write(path, encoded) } diff --git a/src/processing/xmltv_parser.rs b/src/processing/xmltv_parser.rs index 8d930cb12..59a1bf11f 100644 --- a/src/processing/xmltv_parser.rs +++ b/src/processing/xmltv_parser.rs @@ -18,14 +18,14 @@ pub(crate) fn parse_tvguide(content: &str) -> Option { Ok(Event::Eof) => break, Ok(Event::Start(e)) => { let name = String::from_utf8_lossy(e.name().as_ref()).to_string(); - let attributes = e.attributes().filter_map(|a| a.ok()) + let attributes = e.attributes().filter_map(std::result::Result::ok) .filter_map(|a| { let key = String::from_utf8_lossy(a.key.as_ref()).to_string(); let value = String::from(a.unescape_value().unwrap().as_ref()).to_string(); - if !value.is_empty() { - Some((key, value)) - } else { + if value.is_empty() { None + } else { + Some((key, value)) } }).collect::>(); let tag = XmlTag { @@ -78,7 +78,7 @@ pub(crate) fn flatten_tvguide(tv_guides: &[Epg]) -> Option { children: vec![], }; let mut channel_ids: Vec<&String> = vec![]; - tv_guides.iter().for_each(|guide| { + for guide in tv_guides { if epg.attributes.is_none() { epg.attributes.clone_from(&guide.attributes); } @@ -87,7 +87,7 @@ pub(crate) fn flatten_tvguide(tv_guides: &[Epg]) -> Option { if let Some(chan_id) = c.get_attribute_value(EPG_ID) { if !channel_ids.contains(&chan_id) { channel_ids.push(chan_id); - epg.children.push(c.clone()) + epg.children.push(c.clone()); } } } @@ -101,7 +101,7 @@ pub(crate) fn flatten_tvguide(tv_guides: &[Epg]) -> Option { } } }); - }); + } Some(epg) } } \ No newline at end of file diff --git a/src/processing/xtream_parser.rs b/src/processing/xtream_parser.rs index abb1e3591..27c569c31 100644 --- a/src/processing/xtream_parser.rs +++ b/src/processing/xtream_parser.rs @@ -39,23 +39,23 @@ pub(crate) fn parse_xtream_series_info(info: &Value, group_title: &str, input: & let result: Vec = series_info.episodes.values().flatten().map(|episode| PlaylistItem { header: RefCell::new(PlaylistItemHeader { - id: Rc::new(episode.id.to_owned()), - stream_id: Rc::new(episode.id.to_owned()), - name: Rc::new(episode.title.to_owned()), - logo: Rc::new(episode.info.movie_image.to_owned()), + id: Rc::new(episode.id.clone()), + stream_id: Rc::new(episode.id.clone()), + name: Rc::new(episode.title.clone()), + logo: Rc::new(episode.info.movie_image.clone()), logo_small: default_as_empty_rc_str(), group: Rc::new(group_title.to_string()), - title: Rc::new(episode.title.to_owned()), + title: Rc::new(episode.title.clone()), parent_code: default_as_empty_rc_str(), audio_track: default_as_empty_rc_str(), time_shift: default_as_empty_rc_str(), rec: default_as_empty_rc_str(), url: if episode.direct_source.is_empty() { - let ext = episode.container_extension.to_owned(); + let ext = episode.container_extension.clone(); let stream_base_url = format!("{}/series/{}/{}/{}.{}", url, username, password, episode.id.as_str(), ext); Rc::new(stream_base_url) } else { - Rc::new(episode.direct_source.to_owned()) + Rc::new(episode.direct_source.clone()) }, epg_channel_id: None, item_type: PlaylistItemType::Series, diff --git a/src/repository/epg_repository.rs b/src/repository/epg_repository.rs index ed1b80f55..a98c0e696 100644 --- a/src/repository/epg_repository.rs +++ b/src/repository/epg_repository.rs @@ -13,19 +13,19 @@ use crate::repository::xtream_repository::{xtream_get_epg_file_path, xtream_get_ fn epg_write_file(target: &ConfigTarget, epg: &Epg, path: &Path) -> Result<(), M3uFilterError> { let mut writer = Writer::new(Cursor::new(vec![])); match epg.write_to(&mut writer) { - Ok(_) => { + Ok(()) => { let result = writer.into_inner().into_inner(); match File::create(path) { Ok(mut epg_file) => { match epg_file.write_all("".as_bytes()) { - Ok(_) => {} + Ok(()) => {} Err(err) => return Err(M3uFilterError::new( M3uFilterErrorKind::Notify, format!("failed to write epg: {} - {}", path.to_str().unwrap_or("?"), err))), } match epg_file.write_all(&result) { - Ok(_) => { + Ok(()) => { if log_enabled!(Level::Debug) { - debug!("Epg for target {} written to {}", target.name, path.to_str().unwrap_or("?")) + debug!("Epg for target {} written to {}", target.name, path.to_str().unwrap_or("?")); } } Err(err) => return Err(M3uFilterError::new( @@ -42,7 +42,7 @@ fn epg_write_file(target: &ConfigTarget, epg: &Epg, path: &Path) -> Result<(), M Ok(()) } -pub(crate) fn epg_write(target: &ConfigTarget, cfg: &Config, epg: &Option, output: &TargetOutput) -> Result<(), M3uFilterError> { +pub(crate) fn epg_write(target: &ConfigTarget, cfg: &Config, epg: Option<&Epg>, output: &TargetOutput) -> Result<(), M3uFilterError> { if let Some(epg_data) = epg { match &output.target { TargetType::M3u => { @@ -55,7 +55,7 @@ pub(crate) fn epg_write(target: &ConfigTarget, cfg: &Config, epg: &Option, if log_enabled!(Level::Debug) { debug!("writing m3u epg to {}", path.to_str().unwrap_or("?")); } - epg_write_file(target, epg_data, &path)? + epg_write_file(target, epg_data, &path)?; } } TargetType::Xtream => { @@ -65,7 +65,7 @@ pub(crate) fn epg_write(target: &ConfigTarget, cfg: &Config, epg: &Option, if log_enabled!(Level::Debug) { debug!("writing xtream epg to {}", epg_path.to_str().unwrap_or("?")); } - epg_write_file(target, epg_data, &epg_path)? + epg_write_file(target, epg_data, &epg_path)?; } None => return Err(M3uFilterError::new( M3uFilterErrorKind::Notify, diff --git a/src/repository/index_record.rs b/src/repository/index_record.rs index 832c28687..1f01e07f3 100644 --- a/src/repository/index_record.rs +++ b/src/repository/index_record.rs @@ -4,7 +4,7 @@ use std::path::Path; use crate::utils::file_utils; /** -We write the structs with bincode::encode to a file. +We write the structs with `bincode::encode` to a file. To access each entry we need a index file where we can find the Entries with offset and size of the encoded struct. This is used for the index file where first entry is the index @@ -19,7 +19,7 @@ pub(crate) struct IndexRecord { impl IndexRecord { pub(crate) fn from_file(file: &mut File, offset: u32) -> Result { - file.seek(SeekFrom::Start(offset as u64))?; + file.seek(SeekFrom::Start(u64::from(offset)))?; let mut left_bytes = [0u8; 4]; let mut right_bytes = [0u8; 4]; file.read_exact(&mut left_bytes)?; diff --git a/src/repository/indexed_document_reader.rs b/src/repository/indexed_document_reader.rs index d7e8f8687..df4f09537 100644 --- a/src/repository/indexed_document_reader.rs +++ b/src/repository/indexed_document_reader.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::fs::File; use std::io::{Error, ErrorKind, Read, Seek, SeekFrom}; use std::marker::PhantomData; @@ -11,8 +12,8 @@ pub(crate) struct IndexedDocumentReader { cursor: u32, size: u32, failed: bool, - _buffer: Vec, - _type: PhantomData, + t_buffer: Vec, + t_type: PhantomData, } impl IndexedDocumentReader { @@ -24,7 +25,7 @@ impl IndexedDocumentReader { Ok(index_file) => { let size = match index_file.metadata() { Ok(metadata) => { - metadata.len() as usize + usize::try_from(metadata.len()).map_err(|err| Error::new(ErrorKind::Other, err))? } Err(_e) => 0, }; @@ -32,10 +33,10 @@ impl IndexedDocumentReader { main_file, index_file, cursor: 0, - size: size as u32, + size: u32::try_from(size).map_err(|err| Error::new(ErrorKind::Other, err))?, failed: false, - _buffer: Vec::new(), - _type: Default::default(), + t_buffer: Vec::new(), + t_type: PhantomData, }) } Err(e) => Err(e) @@ -63,24 +64,24 @@ impl IndexedDocumentReader { self.cursor += IndexRecord::get_record_size(); match record { Ok(index_record) => { - let offset = index_record.left as u64; + let offset = u64::from(index_record.left); let buf_size = index_record.right as usize; - if self._buffer.len() < buf_size { - self._buffer.resize(buf_size, 0u8); + if self.t_buffer.len() < buf_size { + self.t_buffer.resize(buf_size, 0u8); } self.main_file.seek(SeekFrom::Start(offset))?; - self.main_file.read_exact(&mut self._buffer[0..buf_size])?; - return match bincode::deserialize::(&self._buffer[0..buf_size]) { + self.main_file.read_exact(&mut self.t_buffer[0..buf_size])?; + return match bincode::deserialize::(&self.t_buffer[0..buf_size]) { Ok(value) => Ok(Some(value)), Err(err) => { self.failed = true; - Err(Error::new(ErrorKind::Other, format!("Failed to deserialize document {}", err))) + Err(Error::new(ErrorKind::Other, format!("Failed to deserialize document {err}"))) } }; } Err(err) => { self.failed = true; - return Err(Error::new(ErrorKind::Other, format!("Failed to deserialize document {}", err))); + return Err(Error::new(ErrorKind::Other, format!("Failed to deserialize document {err}"))); } } } @@ -109,7 +110,7 @@ pub(crate) fn read_indexed_item(main_path: &Path, index_path: &Path, offset: let mut index_file = File::open(index_path)?; let mut main_file = File::open(main_path)?; let index_record = IndexRecord::from_file(&mut index_file, offset)?; - main_file.seek(SeekFrom::Start(index_record.left as u64))?; + main_file.seek(SeekFrom::Start(u64::from(index_record.left)))?; let mut buffer: Vec = vec![0; index_record.right as usize]; main_file.read_exact(&mut buffer)?; if let Ok(item) = bincode::deserialize::(&buffer[..]) { diff --git a/src/repository/indexed_document_writer.rs b/src/repository/indexed_document_writer.rs index 01c645832..377e9af6f 100644 --- a/src/repository/indexed_document_writer.rs +++ b/src/repository/indexed_document_writer.rs @@ -1,3 +1,4 @@ +use std::convert::TryFrom; use std::fs::File; use std::io::{Error, ErrorKind, Write}; use std::path::PathBuf; @@ -20,11 +21,11 @@ impl IndexedDocumentWriter { match create_file_tuple(&main_path, &index_path, append) { Ok((main_file, index_file)) => { let main_offset = match &main_file.metadata() { - Ok(meta) => meta.len() as u32, + Ok(meta) => u32::try_from(meta.len()).map_err(|err| Error::new(ErrorKind::Other, err))?, Err(_) => 0 }; let index_offset = match &index_file.metadata() { - Ok(meta) => meta.len() as u32, + Ok(meta) => u32::try_from(meta.len()).map_err(|err| Error::new(ErrorKind::Other, err))?, Err(_) => 0 }; Ok(Self { @@ -54,11 +55,11 @@ impl IndexedDocumentWriter { let current_main_index = self.main_offset; let current_index_index = self.index_offset; if let Ok(encoded) = bincode::serialize(doc) { - match file_utils::check_write(self.main_file.write_all(&encoded)) { - Ok(_) => { - let bytes_written = encoded.len() as u32; + match file_utils::check_write(&self.main_file.write_all(&encoded)) { + Ok(()) => { + let bytes_written = u32::try_from(encoded.len()).map_err(|err| Error::new(ErrorKind::Other, err))?; let combined_bytes = IndexRecord::to_bytes(self.main_offset, bytes_written); - if let Err(err) = file_utils::check_write(self.index_file.write_all(&combined_bytes)) { + if let Err(err) = file_utils::check_write(&self.index_file.write_all(&combined_bytes)) { return Err(Error::new(ErrorKind::Other, format!("failed to write document: {} - {}", self.index_path.to_str().unwrap(), err))); } self.main_offset += bytes_written; diff --git a/src/repository/kodi_repository.rs b/src/repository/kodi_repository.rs index c36c22584..9ff9f94f2 100644 --- a/src/repository/kodi_repository.rs +++ b/src/repository/kodi_repository.rs @@ -109,11 +109,11 @@ pub(crate) fn kodi_write_strm_playlist(target: &ConfigTarget, cfg: &Config, new_ }; kodi_file_name = kodi_style_rename(&kodi_file_name, &style); } - let file_path = dir_path.join(format!("{}.strm", kodi_file_name)); + let file_path = dir_path.join(format!("{kodi_file_name}.strm")); match File::create(&file_path) { Ok(mut strm_file) => { - match file_utils::check_write(strm_file.write_all(header.url.as_bytes())) { - Ok(_) => (), + match file_utils::check_write(&strm_file.write_all(header.url.as_bytes())) { + Ok(()) => (), Err(e) => return create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "failed to write strm playlist: {}", e), } } diff --git a/src/repository/m3u_repository.rs b/src/repository/m3u_repository.rs index a37a2bdd4..48e6e5004 100644 --- a/src/repository/m3u_repository.rs +++ b/src/repository/m3u_repository.rs @@ -24,8 +24,8 @@ macro_rules! cant_write_result { pub(crate) fn m3u_get_file_paths(cfg: &Config, filename: &Option) -> Option<(std::path::PathBuf, std::path::PathBuf)> { match file_utils::get_file_path(&cfg.working_dir, Some(std::path::PathBuf::from(&filename.as_ref().unwrap()))) { Some(m3u_path) => { - let extension = m3u_path.extension().map(|ext| format!("{}_", ext.to_str().unwrap_or(""))).unwrap_or("".to_owned()); - let index_path = m3u_path.with_extension(format!("{}idx", &extension)); + let extension = m3u_path.extension().map(|ext| format!("{}_", ext.to_str().unwrap_or(""))); + let index_path = m3u_path.with_extension(format!("{}idx", &extension.unwrap_or(String::new()))); Some((m3u_path, index_path)) } None => None @@ -51,7 +51,7 @@ pub(crate) fn m3u_write_playlist(target: &ConfigTarget, cfg: &Config, new_playli let m3u_playlist = new_playlist.iter() .flat_map(|pg| &pg.channels) .filter(|&pli| pli.header.borrow().item_type != PlaylistItemType::SeriesInfo) - .map(|pli| pli.to_m3u()).collect::>(); + .map(super::super::model::playlist::PlaylistItem::to_m3u).collect::>(); let mut stream_id: u32 = 1; for mut m3u in m3u_playlist { m3u.stream_id = Rc::new(stream_id.to_string()); @@ -82,7 +82,7 @@ pub(crate) fn m3u_load_rewrite_playlist(cfg: &Config, target: &ConfigTarget, use match user.proxy { ProxyType::Reverse => { let stream_id = Rc::clone(&m3u_pli.stream_id); - result.push(m3u_pli.to_m3u(target, Some(format!("{}/{}", url, stream_id).as_str()))); + result.push(m3u_pli.to_m3u(target, Some(format!("{url}/{stream_id}").as_str()))); } ProxyType::Redirect => { result.push(m3u_pli.to_m3u(target, None)); diff --git a/src/repository/playlist_repository.rs b/src/repository/playlist_repository.rs index a1a69598e..87c3bd28b 100644 --- a/src/repository/playlist_repository.rs +++ b/src/repository/playlist_repository.rs @@ -7,7 +7,7 @@ use crate::repository::kodi_repository::kodi_write_strm_playlist; use crate::repository::m3u_repository::m3u_write_playlist; use crate::repository::xtream_repository::xtream_write_playlist; -pub(crate) fn persist_playlist(playlist: &mut [PlaylistGroup], epg: Option, +pub(crate) fn persist_playlist(playlist: &mut [PlaylistGroup], epg: Option<&Epg>, target: &ConfigTarget, cfg: &Config) -> Result<(), Vec> { let mut errors = vec![]; for output in &target.output { @@ -16,10 +16,10 @@ pub(crate) fn persist_playlist(playlist: &mut [PlaylistGroup], epg: Option, TargetType::Strm => kodi_write_strm_playlist(target, cfg, playlist, &output.filename), TargetType::Xtream => xtream_write_playlist(target, cfg, playlist) } { - Ok(_) => { + Ok(()) => { if !playlist.is_empty() { - match epg_write(target, cfg, &epg, output) { - Ok(_) => {} + match epg_write(target, cfg, epg, output) { + Ok(()) => {} Err(err) => errors.push(err) } } diff --git a/src/repository/xtream_repository.rs b/src/repository/xtream_repository.rs index bdf6736cc..758e57a30 100644 --- a/src/repository/xtream_repository.rs +++ b/src/repository/xtream_repository.rs @@ -32,7 +32,7 @@ macro_rules! cant_write_result { } fn get_collection_path(path: &Path, collection: &str) -> PathBuf { - path.join(format!("{}.json", collection)) + path.join(format!("{collection}.json")) } pub(crate) fn xtream_get_stream_id_cluster_index_file_path(storage_path: &Path) -> PathBuf { @@ -66,8 +66,8 @@ fn ensure_xtream_storage_path(cfg: &Config, target_name: &str) -> Result Option<(PathBuf, PathBuf)> { if cluster == &XtreamCluster::Series { let xtream_path = storage_path.join("series_info.db"); - let extension = xtream_path.extension().map(|ext| format!("{}_", ext.to_str().unwrap_or(""))).unwrap_or("".to_owned()); - let index_path = xtream_path.with_extension(format!("{}idx", &extension)); + let extension = xtream_path.extension().map(|ext| format!("{}_", ext.to_str().unwrap_or(""))); + let index_path = xtream_path.with_extension(format!("{}idx", &extension.unwrap_or_default())); return Some((xtream_path, index_path)); } None @@ -134,7 +134,7 @@ fn write_playlists_to_file(storage_path: &Path, collections: Vec<(XtreamCluster, id_list.push((cluster.clone(), start, stream_id)); } match save_stream_id_cluster_mapping(storage_path, &mut id_list) { - Ok(_) => Ok(()), + Ok(()) => Ok(()), Err(err) => Err(M3uFilterError::new(M3uFilterErrorKind::Notify, format!("failed to write xtream playlist: {} - {}", storage_path.to_str().unwrap(), err))) } } @@ -191,8 +191,8 @@ pub(crate) fn xtream_get_file_paths(storage_path: &Path, cluster: &XtreamCluster XtreamCluster::Video => COL_VOD, XtreamCluster::Series => COL_SERIES })); - let extension = xtream_path.extension().map(|ext| format!("{}_", ext.to_str().unwrap_or(""))).unwrap_or("".to_owned()); - let index_path = xtream_path.with_extension(format!("{}idx", &extension)); + let extension = xtream_path.extension().map(|ext| format!("{}_", ext.to_str().unwrap_or(""))); + let index_path = xtream_path.with_extension(format!("{}idx", &extension.unwrap_or_default())); (xtream_path, index_path) } @@ -231,19 +231,19 @@ pub(crate) fn xtream_write_playlist(target: &ConfigTarget, cfg: &Config, playlis 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 { - if header.id.parse::().is_ok() { - 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 { - errors.push(format!("Channel does not have an id: {}", pli.header.borrow().title.as_str())); - None - } - } else { None }; + let col = if header.item_type == PlaylistItemType::Series { + None + } else if header.id.parse::().is_ok() { + 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 { + errors.push(format!("Channel does not have an id: {}", pli.header.borrow().title.as_str())); + None + }; drop(header); if let Some(pl) = col { pl.push(pli); @@ -270,7 +270,7 @@ pub(crate) fn xtream_write_playlist(target: &ConfigTarget, cfg: &Config, playlis (XtreamCluster::Series, &mut series_col)]) { Ok(()) => {} Err(err) => { - errors.push(format!("Persisting collection failed:{}", err)); + errors.push(format!("Persisting collection failed:{err}")); } } @@ -290,10 +290,10 @@ pub(crate) fn xtream_get_collection_path(cfg: &Config, target_name: &str, collec return Ok((Some(col_path), None)); } } - Err(Error::new(ErrorKind::Other, format!("Cant find collection: {}/{}", target_name, collection_name))) + Err(Error::new(ErrorKind::Other, format!("Cant find collection: {target_name}/{collection_name}"))) } -fn _xtream_get_item_for_stream_id(stream_id: u32, storage_path: &Path, xtream_cluster: Option<&XtreamCluster>, mapping: &[(XtreamCluster, u32, u32)]) -> Result { +fn _xtream_get_item_for_stream_id(stream_id: u32, storage_path: &Path, xtream_cluster: Option<&XtreamCluster>, mapping: &[(XtreamCluster, u32, u32)]) -> Result { if let Some((cluster, cluster_start, _end)) = match xtream_cluster { Some(clus) => mapping.iter().find(|(c, _, _)| c == clus), None => mapping.iter().find(|(_cluster, start, _end)| stream_id >= *start), @@ -303,7 +303,7 @@ fn _xtream_get_item_for_stream_id(stream_id: u32, storage_path: &Path, xtream_c return read_indexed_item::(&xtream_path, &idx_path, IndexRecord::get_index_offset(stream_id - cluster_start)); } } - Err(Error::new(ErrorKind::Other, format!("Failed to read xtream item for stream-id {}", stream_id))) + Err(Error::new(ErrorKind::Other, format!("Failed to read xtream item for stream-id {stream_id}"))) } pub(crate) fn xtream_get_item_for_stream_id(stream_id: u32, config: &Config, target: &ConfigTarget, xtream_cluster: Option<&XtreamCluster>) -> Result { @@ -313,20 +313,20 @@ pub(crate) fn xtream_get_item_for_stream_id(stream_id: u32, config: &Config, tar if max < stream_id { // episoden id's fangen bei (max cluster id + 1) an. // episode mapping har 3 u32 also 12 bytes - let index = stream_id - (max+1); + let index = stream_id - (max + 1); if let Ok((_episode_id, provider_id, series_id)) = xtream_read_episode_id_mapping(&storage_path, index) { if let Ok(mut pli) = _xtream_get_item_for_stream_id(series_id, &storage_path, xtream_cluster, &mapping) { pli.provider_id = provider_id; return Ok(pli); } } - return Err(Error::new(ErrorKind::Other, format!("Failed to read xtream item for stream-id {}", stream_id))); + return Err(Error::new(ErrorKind::Other, format!("Failed to read xtream item for stream-id {stream_id}"))); } } return _xtream_get_item_for_stream_id(stream_id, &storage_path, xtream_cluster, &mapping); } } - Err(Error::new(ErrorKind::Other, format!("Failed to read xtream item for stream-id {}", stream_id))) + Err(Error::new(ErrorKind::Other, format!("Failed to read xtream item for stream-id {stream_id}"))) } pub(crate) fn xtream_load_rewrite_playlist(cluster: &XtreamCluster, config: &Config, target: &ConfigTarget, category_id: u32) -> Result { @@ -389,21 +389,21 @@ fn xtream_write_episode_id_mapping(storage_path: &Path, series_id: u32, episode_ } { let stream_id_bytes: [u8; 4] = series_id.to_le_bytes(); let mut bytes: Vec = Vec::new(); - episode_id_mapping.iter().for_each(|(episode_id, provider_id)| { + for (episode_id, provider_id) in episode_id_mapping { bytes.extend_from_slice(&episode_id.to_le_bytes()); bytes.extend_from_slice(&provider_id.to_le_bytes()); bytes.extend_from_slice(&stream_id_bytes); - }); - return file_utils::check_write(file.write_all(&bytes[..])); + } + return file_utils::check_write(&file.write_all(&bytes[..])); } Err(Error::new(ErrorKind::Other, format!("Failed to open series info idmapping file inside {}", storage_path.to_str().unwrap()))) } fn xtream_read_episode_id_mapping(storage_path: &Path, index: u32) -> Result<(u32, u32, u32), Error> { - let offset = index * 12; + let offset = index * 12; let episode_file = xtream_get_series_episode_id_mapping_file_path(storage_path); if let Ok(mut file) = File::open(episode_file) { - file.seek(SeekFrom::Start(offset as u64))?; + file.seek(SeekFrom::Start(u64::from(offset)))?; let mut episode_id_bytes = [0u8; 4]; let mut provider_id_bytes = [0u8; 4]; let mut series_id_bytes = [0u8; 4]; @@ -415,7 +415,7 @@ fn xtream_read_episode_id_mapping(storage_path: &Path, index: u32) -> Result<(u3 let series_id = u32::from_le_bytes(series_id_bytes); Ok((episode_id, provider_id, series_id)) } else { - Err(Error::new(ErrorKind::Other, format!("Could not find episode mapping at offset {}", offset))) + Err(Error::new(ErrorKind::Other, format!("Could not find episode mapping at offset {offset}"))) } } @@ -431,7 +431,7 @@ pub(crate) fn xtream_write_series_info(config: &Config, target_name: &str, let series_id_index_mapping_path = xtream_get_series_id_series_info_mapping_file_path(&storage_path); IndexRecord::to_file(&series_id_index_mapping_path, series_id, index_offset, true)?; } - Err(_) => return Err(Error::new(ErrorKind::Other, format!("failed to write xtream series info for target {}", target_name))) + Err(_) => return Err(Error::new(ErrorKind::Other, format!("failed to write xtream series info for target {target_name}"))) } xtream_write_episode_id_mapping(&storage_path, series_id, episode_id_mapping) } @@ -453,7 +453,7 @@ pub(crate) fn xtream_load_series_info(config: &Config, target_name: &str, series let mut buffer = [0u8; 8]; loop { match file.read_exact(&mut buffer) { - Ok(_) => { + Ok(()) => { let stream_id = u32::from_le_bytes(buffer[..4].try_into().unwrap()); if stream_id == series_id { let index = u32::from_le_bytes(buffer[4..].try_into().unwrap()); @@ -468,5 +468,5 @@ pub(crate) fn xtream_load_series_info(config: &Config, target_name: &str, series } } } - Err(Error::new(ErrorKind::Other, format!("Failed to read series info for id {} for {}", series_id, target_name))) + Err(Error::new(ErrorKind::Other, format!("Failed to read series info for id {series_id} for {target_name}"))) } \ No newline at end of file diff --git a/src/test.rs b/src/test.rs index 9112915ff..9e6326c1b 100644 --- a/src/test.rs +++ b/src/test.rs @@ -7,7 +7,7 @@ mod tests { let flt1 = "(Group ~ \"A\" OR Group ~ \"B\") AND (Name ~ \"C\" OR Name ~ \"D\" OR Name ~ \"E\") OR (NOT (Title ~ \"F\") AND NOT Title ~ \"K\")"; match get_filter(flt1, None) { Ok(filter) => { - assert_eq!(format!("{}", filter), flt1); + assert_eq!(format!("{filter}"), flt1); }, Err(_e) => {} } diff --git a/src/utils/config_reader.rs b/src/utils/config_reader.rs index 0d0c2a61c..ff817a5ac 100644 --- a/src/utils/config_reader.rs +++ b/src/utils/config_reader.rs @@ -15,13 +15,13 @@ use crate::model::mapping::Mappings; use crate::utils::{file_utils, multi_file_reader}; pub(crate) fn read_mappings(args_mapping: Option, cfg: &mut Config) -> Result<(), M3uFilterError> { - let mappings_file: String = args_mapping.unwrap_or(file_utils::get_default_mappings_path(cfg._config_path.as_str())); + let mappings_file: String = args_mapping.unwrap_or(file_utils::get_default_mappings_path(cfg.t_config_path.as_str())); match read_mapping(mappings_file.as_str()) { Ok(mappings) => { info!("Mappings File: {}", &mappings_file); if mappings.is_none() { debug!("no mapping loaded"); } - handle_m3u_filter_error_result!(M3uFilterErrorKind::Info, cfg.set_mappings(mappings)); + cfg.set_mappings(mappings); Ok(()) } Err(err) => Err(err), @@ -29,8 +29,8 @@ pub(crate) fn read_mappings(args_mapping: Option, cfg: &mut Config) -> R } pub(crate) fn read_api_proxy_config(args_api_proxy_config: Option, cfg: &mut Config) { - let api_proxy_config_file: String = args_api_proxy_config.unwrap_or(file_utils::get_default_api_proxy_config_path(cfg._config_path.as_str())); - api_proxy_config_file.clone_into(&mut cfg._api_proxy_file_path); + let api_proxy_config_file: String = args_api_proxy_config.unwrap_or(file_utils::get_default_api_proxy_config_path(cfg.t_config_path.as_str())); + api_proxy_config_file.clone_into(&mut cfg.t_api_proxy_file_path); let api_proxy_config = read_api_proxy(api_proxy_config_file.as_str(), true); match api_proxy_config { None => { @@ -49,9 +49,9 @@ pub(crate) fn read_config(config_path: &str, config_file: &str, sources_file: &s Ok(file) => { match serde_yaml::from_reader::<_, Config>(file) { Ok(mut result) => { - result._config_path = config_path.to_string(); - result._config_file_path = config_file.to_string(); - result._sources_file_path = sources_file.to_string(); + result.t_config_path = config_path.to_string(); + result.t_config_file_path = config_file.to_string(); + result.t_sources_file_path = sources_file.to_string(); match result.prepare(true) { Err(err) => Err(err), _ => Ok(result), @@ -137,7 +137,7 @@ pub(crate) fn save_main_config(file_path: &str, backup_dir: &str, config: &Confi pub(crate) fn resolve_env_var(value: &str) -> String { if !value.trim().is_empty() { - let pattern = Regex::new(r#"\$\{env:(?P[a-zA-Z_][a-zA-Z0-9_]*)}"#).unwrap(); + let pattern = Regex::new(r"\$\{env:(?P[a-zA-Z_][a-zA-Z0-9_]*)}").unwrap(); if let Some(caps) = pattern.captures(value) { if let Some(var) = caps.name("var") { let var_name = var.as_str(); diff --git a/src/utils/download.rs b/src/utils/download.rs index dc8a1b99b..b0f6a5480 100644 --- a/src/utils/download.rs +++ b/src/utils/download.rs @@ -12,15 +12,12 @@ use crate::utils::{file_utils, request_utils}; fn prepare_file_path(persist: Option<&String>, working_dir: &String, action: &str) -> Option { let persist_file: Option = - match persist { - Some(persist_path) => file_utils::prepare_persist_path(persist_path.as_str(), action), - _ => None - }; + persist.map(|persist_path| file_utils::prepare_persist_path(persist_path.as_str(), action)); if persist_file.is_some() { let file_path = file_utils::get_file_path(working_dir, persist_file); debug!("persist to file: {:?}", match &file_path { Some(fp) => fp.display().to_string(), - _ => "".to_string() + _ => String::new() }); file_path } else { @@ -29,7 +26,7 @@ fn prepare_file_path(persist: Option<&String>, working_dir: &String, action: &st } pub(crate) async fn get_m3u_playlist(cfg: &Config, input: &ConfigInput, working_dir: &String) -> (Vec, Vec) { - let url = input.url.to_owned(); + let url = input.url.clone(); let persist_file_path = prepare_file_path(input.persist.as_ref(), working_dir, ""); match request_utils::get_input_text_content(input, working_dir, &url, persist_file_path).await { Ok(text) => { @@ -69,7 +66,7 @@ pub(crate) async fn get_xtream_playlist_series<'a>(fpl: &mut FetchedPlaylist<'a> Err(err) => errors.push(err) }; if resolve_delay > 0 { - sleep(std::time::Duration::new(resolve_delay as u64, 0)); // 2 seconds between + sleep(std::time::Duration::new(u64::from(resolve_delay), 0)); // 2 seconds between } } } @@ -86,17 +83,17 @@ pub(crate) async fn get_xtream_playlist_series<'a>(fpl: &mut FetchedPlaylist<'a> result } -fn get_skip_cluster(input: &&ConfigInput) -> Vec { +fn get_skip_cluster(input: &ConfigInput) -> Vec { let mut skip_cluster = vec![]; if let Some(input_options) = &input.options { if input_options.xtream_skip_live { - skip_cluster.push(XtreamCluster::Live) + skip_cluster.push(XtreamCluster::Live); } if input_options.xtream_skip_vod { - skip_cluster.push(XtreamCluster::Video) + skip_cluster.push(XtreamCluster::Video); } if input_options.xtream_skip_series { - skip_cluster.push(XtreamCluster::Series) + skip_cluster.push(XtreamCluster::Series); } } if skip_cluster.len() == 3 { @@ -116,15 +113,15 @@ pub(crate) async fn get_xtream_playlist(input: &ConfigInput, working_dir: &Strin let password = input.password.as_ref().map_or("", |v| v); let base_url = format!("{}/player_api.php?username={}&password={}", input.url, username, password); - let skip_cluster = get_skip_cluster(&input); + let skip_cluster = get_skip_cluster(input); let mut errors = vec![]; for (xtream_cluster, category, stream) in &ACTIONS { if !skip_cluster.contains(xtream_cluster) { - let category_url = format!("{}&action={}", base_url, category); - let stream_url = format!("{}&action={}", base_url, stream); - let category_file_path = prepare_file_path(input.persist.as_ref(), working_dir, format!("{}_", category).as_str()); - let stream_file_path = prepare_file_path(input.persist.as_ref(), working_dir, format!("{}_", stream).as_str()); + let category_url = format!("{base_url}&action={category}"); + let stream_url = format!("{base_url}&action={stream}"); + let category_file_path = prepare_file_path(input.persist.as_ref(), working_dir, format!("{category}_").as_str()); + let stream_file_path = prepare_file_path(input.persist.as_ref(), working_dir, format!("{stream}_").as_str()); match request_utils::get_input_json_content(input, category_url.as_str(), category_file_path).await { Ok(category_content) => { @@ -151,10 +148,10 @@ pub(crate) async fn get_xtream_playlist(input: &ConfigInput, working_dir: &Strin } playlist.sort_by(|a, b| a.title.partial_cmp(&b.title).unwrap_or(Ordering::Greater)); let mut counter = 1; - playlist.iter_mut().for_each(|plg| { + for plg in &mut playlist { plg.id = counter; counter += 1; - }); + } (playlist, errors) } diff --git a/src/utils/file_utils.rs b/src/utils/file_utils.rs index f5b44bcf4..21b7a84c1 100644 --- a/src/utils/file_utils.rs +++ b/src/utils/file_utils.rs @@ -4,7 +4,7 @@ use std::io::Write; use std::path::{Path, PathBuf}; use log::{debug, error}; -use path_absolutize::*; +use path_absolutize::Absolutize; #[macro_export] macro_rules! exit { @@ -20,8 +20,8 @@ pub(crate) fn get_exe_path() -> PathBuf { match current_exe { Ok(exe) => { match fs::read_link(&exe) { - Ok(f) => f.parent().map_or(default_path, |p| p.to_path_buf()), - Err(_) => return exe.parent().map_or(default_path, |p| p.to_path_buf()) + Ok(f) => f.parent().map_or(default_path, std::path::Path::to_path_buf), + Err(_) => return exe.parent().map_or(default_path, std::path::Path::to_path_buf) } } Err(_) => default_path @@ -110,12 +110,11 @@ pub(crate) fn get_working_path(wd: &String) -> String { Some(d) => d, None => current_dir.join(wd) }; - match rp.canonicalize() { - Ok(ap) => String::from(ap.to_str().unwrap_or("./")), - Err(_) => { - error!("Path not found {:?}", &rp); - String::from("./") - } + if let Ok(ap) = rp.canonicalize() { + String::from(ap.to_str().unwrap_or("./")) + } else { + error!("Path not found {:?}", &rp); + String::from("./") } } } @@ -130,7 +129,7 @@ pub(crate) fn persist_file(persist_file: Option, text: &String) { let filename = &path_buf.to_str().unwrap_or("?"); match File::create(&path_buf) { Ok(mut file) => match file.write_all(text.as_bytes()) { - Ok(_) => debug!("persisted: {}", filename), + Ok(()) => debug!("persisted: {}", filename), Err(e) => error!("failed to persist file {}, {}", filename, e) }, Err(e) => error!("failed to persist file {}, {}", filename, e) @@ -138,10 +137,10 @@ pub(crate) fn persist_file(persist_file: Option, text: &String) { } } -pub(crate) fn prepare_persist_path(file_name: &str, date_prefix: &str) -> Option { +pub(crate) fn prepare_persist_path(file_name: &str, date_prefix: &str) -> PathBuf { let now = chrono::Local::now(); let persist_filename = file_name.replace("{}", format!("{date_prefix}{}", now.format("%Y%m%d_%H%M%S").to_string().as_str()).as_str()); - Some(std::path::PathBuf::from(persist_filename)) + std::path::PathBuf::from(persist_filename) } pub(crate) fn get_file_path(wd: &String, path: Option) -> Option { @@ -181,9 +180,9 @@ pub(crate) fn path_exists(file_path: &Path) -> bool { false } -pub(crate) fn check_write(res: std::io::Result<()>) -> Result<(), std::io::Error> { +pub(crate) fn check_write(res: &std::io::Result<()>) -> Result<(), std::io::Error> { match res { - Ok(_) => Ok(()), + Ok(()) => Ok(()), Err(_) => Err(std::io::Error::new(std::io::ErrorKind::Other, "Unable to write file")), } } diff --git a/src/utils/json_utils.rs b/src/utils/json_utils.rs index 45f76c0e6..ddd3ae489 100644 --- a/src/utils/json_utils.rs +++ b/src/utils/json_utils.rs @@ -32,7 +32,13 @@ fn yield_next_obj( mut reader: R, at_start: &mut bool, ) -> io::Result> { - if !*at_start { + if *at_start { + match read_skipping_ws(&mut reader)? { + b',' => deserialize_single(reader).map(Some), + b']' => Ok(None), + _ => Err(invalid_data("`,` or `]` not found")), + } + } else { *at_start = true; if read_skipping_ws(&mut reader)? == b'[' { // read the next char to see if the array is empty @@ -45,12 +51,6 @@ fn yield_next_obj( } else { Err(invalid_data("`[` not found")) } - } else { - match read_skipping_ws(&mut reader)? { - b',' => deserialize_single(reader).map(Some), - b']' => Ok(None), - _ => Err(invalid_data("`,` or `]` not found")), - } } } @@ -98,7 +98,7 @@ pub(crate) fn json_write_documents_to_file(file: &Path, value: &T) -> Result< let mut writer = BufWriter::new(file); serde_json::to_writer(&mut writer, value)?; match writer.flush() { - Ok(_) => Ok(()), + Ok(()) => Ok(()), Err(e) => Err(e) } } diff --git a/src/utils/request_utils.rs b/src/utils/request_utils.rs index 69d19aa05..fc93afb8f 100644 --- a/src/utils/request_utils.rs +++ b/src/utils/request_utils.rs @@ -17,60 +17,59 @@ pub(crate) async fn get_input_text_content(input: &ConfigInput, working_dir: &St if log_enabled!(Level::Debug) { debug!("getting input text content working_dir: {}, url: {}", working_dir, url_str); } - match url_str.parse::() { - Ok(url) => match download_text_content(input, url, persist_filepath).await { + + if let Ok(url) = url_str.parse::() { + match download_text_content(input, url, persist_filepath).await { Ok(content) => Ok(content), Err(e) => { error!("cant download input url: {} => {}", url_str, e); create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Failed to download") } } - Err(_) => { - let result = match get_file_path(working_dir, Some(PathBuf::from(url_str))) { - Some(filepath) => { - if filepath.exists() { - if let Some(persist_file_value) = persist_filepath { - let to_file = &persist_file_value; - match fs::copy(&filepath, to_file) { - Ok(_) => {} - Err(e) => { - error!("cant persist to: {} => {}", to_file.to_str().unwrap_or("?"), e); - return create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Failed to persist: {} => {}", to_file.to_str().unwrap_or("?"), e); - } - } - }; - match open_file(&filepath) { - Ok(file) => { - let mut content = String::new(); - match std::io::BufReader::new(file).read_to_string(&mut content) { - Ok(_) => Some(content), - Err(err) => { - let file_str = &filepath.to_str().unwrap_or("?"); - error!("cant read file: {} {}", file_str, err); - return create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Cant open file : {} => {}", file_str, err); - } - } - } - Err(err) => { - let file_str = &filepath.to_str().unwrap_or("?"); - error!("cant read file: {} {}", file_str, err); - return create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Cant open file : {} => {}", file_str, err); + } else { + let result = match get_file_path(working_dir, Some(PathBuf::from(url_str))) { + Some(filepath) => { + if filepath.exists() { + if let Some(persist_file_value) = persist_filepath { + let to_file = &persist_file_value; + match fs::copy(&filepath, to_file) { + Ok(_) => {} + Err(e) => { + error!("cant persist to: {} => {}", to_file.to_str().unwrap_or("?"), e); + return create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Failed to persist: {} => {}", to_file.to_str().unwrap_or("?"), e); } } - } else { - None + }; + match open_file(&filepath) { + Ok(file) => { + let mut content = String::new(); + match std::io::BufReader::new(file).read_to_string(&mut content) { + Ok(_) => Some(content), + Err(err) => { + let file_str = &filepath.to_str().unwrap_or("?"); + error!("cant read file: {} {}", file_str, err); + return create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Cant open file : {} => {}", file_str, err); + } + } + } + Err(err) => { + let file_str = &filepath.to_str().unwrap_or("?"); + error!("cant read file: {} {}", file_str, err); + return create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "Cant open file : {} => {}", file_str, err); + } } - } - None => None - }; - match result { - Some(content) => Ok(content), - None => { - let msg = format!("cant read input url: {:?}", url_str); - error!("{}", msg); - create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "{}", msg) + } else { + None } } + None => None + }; + if let Some(content) = result { + Ok(content) + } else { + let msg = format!("cant read input url: {url_str:?}"); + error!("{}", msg); + create_m3u_filter_error_result!(M3uFilterErrorKind::Notify, "{}", msg) } } }