diff --git a/frontend/src/model/server-config.ts b/frontend/src/model/server-config.ts index 3788051fa..486eedc0e 100644 --- a/frontend/src/model/server-config.ts +++ b/frontend/src/model/server-config.ts @@ -32,6 +32,11 @@ export interface InputConfig { persist: string, name: string, enabled: boolean + options: { + xtream_skip_live: boolean, + xtream_skip_vod: boolean, + xtream_skip_series: boolean, + }, } export interface TargetConfig { diff --git a/src/model/config.rs b/src/model/config.rs index 5e24ae7a4..9e9697a85 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -285,6 +285,7 @@ impl ConfigRename { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] pub struct ConfigTargetOptions { #[serde(default)] pub ignore_logo: bool, @@ -514,6 +515,7 @@ impl FromStr for InputType { } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize, Default)] +#[serde(deny_unknown_fields)] pub struct ConfigInputOptions { #[serde(default)] pub xtream_skip_live: bool, diff --git a/src/model/xtream.rs b/src/model/xtream.rs index 5db8790ac..26be76011 100644 --- a/src/model/xtream.rs +++ b/src/model/xtream.rs @@ -378,8 +378,10 @@ pub struct XtreamMappingOptions { impl XtreamMappingOptions { pub fn from_target_options(options: Option<&ConfigTargetOptions>) -> Self { let (skip_live_direct_source, skip_video_direct_source, skip_series_direct_source) = options - .map_or((false, false, false), |o| (o.xtream_skip_live_direct_source, - o.xtream_skip_video_direct_source, o.xtream_skip_series_direct_source)); + .map_or((false, false, false), |o| ( + o.xtream_skip_live_direct_source, + o.xtream_skip_video_direct_source, + o.xtream_skip_series_direct_source)); Self { skip_live_direct_source, skip_video_direct_source, diff --git a/src/processing/playlist_processor.rs b/src/processing/playlist_processor.rs index fb66930e7..3f7a2d398 100644 --- a/src/processing/playlist_processor.rs +++ b/src/processing/playlist_processor.rs @@ -303,7 +303,6 @@ async fn process_source(cfg: Arc, source_idx: usize, user_targets: Arc

download::get_m3u_playlist(&cfg, input, &cfg.working_dir).await, InputType::Xtream => download::get_xtream_playlist(input, &cfg.working_dir).await, }; - // @TODO optmization dont hold tv_guide in memory, persist raw and later use sax parser to extract. let (tvguide, mut tvguide_errors) = if error_list.is_empty() { download::get_xmltv(&cfg, input, &cfg.working_dir).await } else { diff --git a/src/processing/xtream_processor.rs b/src/processing/xtream_processor.rs index 0e0d280be..c2e31dfd0 100644 --- a/src/processing/xtream_processor.rs +++ b/src/processing/xtream_processor.rs @@ -7,7 +7,7 @@ use crate::{info_err, notify_err}; use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashMap; -use std::fs::{File, OpenOptions}; +use std::fs::{File}; use std::io::{BufWriter, Error, ErrorKind, Write}; use std::path::PathBuf; @@ -35,6 +35,7 @@ macro_rules! handle_error_and_return { } use crate::repository::bplustree::BPlusTree; use crate::repository::xtream_repository::xtream_get_record_file_path; +use crate::utils::file_utils::append_or_crate_file; #[macro_export] macro_rules! create_resolve_options_function_for_xtream_target { @@ -102,7 +103,7 @@ pub(in crate::processing) fn create_resolve_episode_wal_files(cfg: &Config, inpu match get_input_storage_path(input, &cfg.working_dir) { Ok(storage_path) => { let info_path = storage_path.join(format!("series_episode_record.{FILE_SUFFIX_WAL}")); - let info_file = OpenOptions::new().append(true).open(&info_path).ok()?; + let info_file = append_or_crate_file(&info_path).ok()?; Some((info_file, info_path)) } Err(_) => None @@ -119,8 +120,8 @@ pub(in crate::processing) fn create_resolve_info_wal_files(cfg: &Config, input: } { let content_path = storage_path.join(format!("{file_prefix}_content.{FILE_SUFFIX_WAL}")); let info_path = storage_path.join(format!("{file_prefix}_record.{FILE_SUFFIX_WAL}")); - let content_file = OpenOptions::new().append(true).open(&content_path).ok()?; - let info_file = OpenOptions::new().append(true).open(&info_path).ok()?; + let content_file = append_or_crate_file(&content_path).ok()?; + let info_file = append_or_crate_file(&info_path).ok()?; return Some((content_file, info_file, content_path, info_path)); } None @@ -148,12 +149,12 @@ pub(in crate::processing) fn has_different_ts(ts: u64, pli: &PlaylistItem, field }) } -pub(in crate::processing) fn should_update_info(pli: &PlaylistItem, processed_provider_ids: &HashMap, field: &str) -> bool { +pub(in crate::processing) fn should_update_info(pli: &PlaylistItem, processed_provider_ids: &HashMap, field: &str) -> (bool, u32, u64) { if let Some(provider_id) = pli.header.borrow_mut().get_provider_id() { let timestamp = processed_provider_ids.get(&provider_id); - timestamp.is_none() || has_different_ts(*timestamp.unwrap(), pli, field) + (timestamp.is_none() || has_different_ts(*timestamp.unwrap(), pli, field), provider_id, *timestamp.unwrap_or(&0)) } else { - false + (false, 0, 0) } } diff --git a/src/processing/xtream_processor_series.rs b/src/processing/xtream_processor_series.rs index d410c2d6d..937b04fc9 100644 --- a/src/processing/xtream_processor_series.rs +++ b/src/processing/xtream_processor_series.rs @@ -3,17 +3,16 @@ use crate::model::config::{Config, ConfigTarget, InputType}; use crate::model::playlist::{FetchedPlaylist, PlaylistGroup, PlaylistItem, PlaylistItemType, XtreamCluster}; use crate::processing::playlist_processor::ProcessingPipe; use crate::processing::xtream_parser::parse_xtream_series_info; -use crate::processing::xtream_processor::{create_resolve_episode_wal_files, create_resolve_info_wal_files, get_u32_from_serde_value, get_u64_from_serde_value, playlist_resolve_process_playlist_item, read_processed_info_ids, should_update_info, write_info_content_to_wal_file}; +use crate::processing::xtream_processor::{create_resolve_episode_wal_files, create_resolve_info_wal_files, get_u32_from_serde_value, playlist_resolve_process_playlist_item, read_processed_info_ids, should_update_info, write_info_content_to_wal_file}; use crate::repository::storage::get_input_storage_path; use crate::repository::xtream_repository::{xtream_get_info_file_paths, xtream_update_input_info_file, xtream_update_input_series_episodes_record_from_wal_file, xtream_update_input_series_record_from_wal_file}; use crate::repository::IndexedDocumentReader; use crate::{create_resolve_options_function_for_xtream_target, handle_error, handle_error_and_return, info_err, notify_err}; -use serde_json::{Map, Value}; +use serde_json::{Value}; use std::collections::HashMap; use std::fs::File; use std::io::{BufWriter, Write}; -const TAG_SERIES_INFO_SERIES_ID: &str = "series_id"; const TAG_SERIES_INFO_LAST_MODIFIED: &str = "last_modified"; create_resolve_options_function_for_xtream_target!(series); @@ -22,13 +21,6 @@ async fn read_processed_series_info_ids(cfg: &Config, errors: &mut Vec Option<(u32, u64)> { - let doc = serde_json::from_str::>(content).ok()?; - let provider_id = get_u32_from_serde_value(doc.get(TAG_SERIES_INFO_SERIES_ID)?)?; - let last_modified = get_u64_from_serde_value(doc.get(TAG_SERIES_INFO_LAST_MODIFIED)?).unwrap_or(0); - Some((provider_id, last_modified)) -} - fn write_series_info_record_to_wal_file( writer: &mut BufWriter<&File>, provider_id: u32, @@ -49,8 +41,8 @@ fn write_series_episode_record_to_wal_file( Ok(()) } -fn should_update_series_info(pli: &PlaylistItem, processed_provider_ids: &HashMap) -> bool { - should_update_info(pli, processed_provider_ids, "last_modified") +fn should_update_series_info(pli: &PlaylistItem, processed_provider_ids: &HashMap) -> (bool, u32, u64) { + should_update_info(pli, processed_provider_ids, TAG_SERIES_INFO_LAST_MODIFIED) } async fn playlist_resolve_series_info(cfg: &Config, errors: &mut Vec, @@ -58,7 +50,7 @@ async fn playlist_resolve_series_info(cfg: &Config, errors: &mut Vec::new(&info_path, &idx_path) else { return result; }; - let Some((mut wal_file, wal_path)) = create_resolve_episode_wal_files(cfg, input) else { + let Some((wal_file, wal_path)) = create_resolve_episode_wal_files(cfg, input) else { errors.push(notify_err!("Could not create wal file for series episodes record".to_string())); return result; }; @@ -178,7 +170,8 @@ async fn process_series_info( handle_error!(wal_writer.flush(), |err| errors.push(notify_err!(format!("Failed to resolve series episodes, could not write to wal file {err}")))); drop(wal_writer); - handle_error!(xtream_update_input_series_episodes_record_from_wal_file(cfg, input, &mut wal_file, &wal_path).await, + drop(wal_file); + handle_error!(xtream_update_input_series_episodes_record_from_wal_file(cfg, input, &wal_path).await, |err| errors.push(err)); result } diff --git a/src/processing/xtream_processor_vod.rs b/src/processing/xtream_processor_vod.rs index c68b64b0d..f1440c7a9 100644 --- a/src/processing/xtream_processor_vod.rs +++ b/src/processing/xtream_processor_vod.rs @@ -56,7 +56,7 @@ fn write_vod_info_record_to_wal_file( Ok(()) } -fn should_update_vod_info(pli: &PlaylistItem, processed_provider_ids: &HashMap) -> bool { +fn should_update_vod_info(pli: &PlaylistItem, processed_provider_ids: &HashMap) -> (bool, u32, u64) { should_update_info(pli, processed_provider_ids, TAG_VOD_INFO_ADDED) } @@ -67,7 +67,7 @@ pub async fn playlist_resolve_vod(cfg: &Config, target: &ConfigTarget, errors: & // we cant write to the indexed-document directly because of the write lock and time-consuming operation. // All readers would be waiting for the lock and the app would be unresponsive. // We collect the content into a wal file and write it once we collected everything. - let Some((mut wal_content_file, mut wal_record_file, wal_content_path, wal_record_path)) = create_resolve_info_wal_files(cfg, fpl.input, XtreamCluster::Video) + let Some((wal_content_file, wal_record_file, wal_content_path, wal_record_path)) = create_resolve_info_wal_files(cfg, fpl.input, XtreamCluster::Video) else { return; }; let mut processed_info_ids = read_processed_vod_info_ids(cfg, errors, fpl).await; @@ -78,7 +78,7 @@ pub async fn playlist_resolve_vod(cfg: &Config, target: &ConfigTarget, errors: & for pli in fpl.playlistgroups.iter() .flat_map(|plg| &plg.channels) .filter(|&pli| pli.header.borrow().xtream_cluster == XtreamCluster::Video) { - let should_update = should_update_vod_info(pli, &processed_info_ids); + let (should_update, _provider_id, _ts) = should_update_vod_info(pli, &processed_info_ids); if should_update { if let Some(content) = playlist_resolve_process_playlist_item(pli, fpl.input, errors, resolve_delay, XtreamCluster::Video).await { if let Some((provider_id, info_record)) = extract_info_record_from_vod_info(&content) { @@ -96,13 +96,15 @@ pub async fn playlist_resolve_vod(cfg: &Config, target: &ConfigTarget, errors: & if content_updated { handle_error!(content_writer.flush(), |err| errors.push(notify_err!(format!("Failed to resolve vod, could not write to wal file {err}")))); - drop(content_writer); handle_error!(record_writer.flush(), |err| errors.push(notify_err!(format!("Failed to resolve vod tmdb, could not write to wal file {err}")))); + drop(content_writer); drop(record_writer); - handle_error!(xtream_update_input_info_file(cfg, fpl.input, &mut wal_content_file, &wal_content_path, XtreamCluster::Video).await, + drop(wal_content_file); + drop(wal_record_file); + handle_error!(xtream_update_input_info_file(cfg, fpl.input, &wal_content_path, XtreamCluster::Video).await, |err| errors.push(err)); - handle_error!(xtream_update_input_vod_record_from_wal_file(cfg, fpl.input, &mut wal_record_file, &wal_record_path).await, + handle_error!(xtream_update_input_vod_record_from_wal_file(cfg, fpl.input, &wal_record_path).await, |err| errors.push(err)); } } diff --git a/src/repository/bplustree.rs b/src/repository/bplustree.rs index 2dc950a90..fd05c0c9e 100644 --- a/src/repository/bplustree.rs +++ b/src/repository/bplustree.rs @@ -1,5 +1,5 @@ use std::array::TryFromSliceError; -use std::fs::{File, OpenOptions}; +use std::fs::{File}; use std::io::{self, BufReader, BufWriter, Error, ErrorKind, Read, Seek, SeekFrom, Write}; use std::marker::PhantomData; use std::mem::size_of; @@ -8,6 +8,7 @@ use std::path::Path; use flate2::Compression; use log::error; use serde::{Deserialize, Serialize}; +use crate::utils::file_utils::{create_new_file_for_write, open_read_write_file}; const BINCODE_OVERHEAD: usize = 4; const BLOCK_SIZE: usize = 4096; @@ -402,7 +403,7 @@ where pub fn store(&mut self, filepath: &Path) -> io::Result { if self.dirty { - let mut file = BufWriter::new(OpenOptions::new().write(true).create(true).truncate(true).open(filepath)?); + let mut file = BufWriter::new(create_new_file_for_write(filepath)?); let mut buffer = vec![0u8; BLOCK_SIZE]; let result = self.root.serialize_to_block(&mut file, &mut buffer, 0u64); file.flush()?; @@ -538,10 +539,7 @@ where if !filepath.exists() { return Err(io::Error::new(io::ErrorKind::NotFound, format!("File not found {}", filepath.to_str().unwrap_or("?")))); } - let file = is_file_valid(OpenOptions::new() - .write(true) - .read(true) - .open(filepath)?)?; + let file = is_file_valid(open_read_write_file(filepath)?)?; Ok(Self { file, _marker_k: PhantomData, diff --git a/src/repository/indexed_document.rs b/src/repository/indexed_document.rs index 0a9f7ee52..9bcacc40a 100644 --- a/src/repository/indexed_document.rs +++ b/src/repository/indexed_document.rs @@ -1,5 +1,5 @@ use std::fmt::Debug; -use std::fs::{File, OpenOptions}; +use std::fs::{File}; use std::io::{BufReader, Error, ErrorKind, Read, Seek, SeekFrom, Write}; use std::marker::PhantomData; use std::path::{Path, PathBuf}; @@ -8,6 +8,7 @@ use crate::repository::bplustree::{BPlusTree, BPlusTreeQuery}; use crate::utils::file_utils; use log::error; use serde::{Deserialize, Serialize}; +use crate::utils::file_utils::{create_new_file_for_read_write, create_new_file_for_write, open_read_write_file, open_readonly_file}; const BLOCK_SIZE: usize = 4096; const LEN_SIZE: usize = 4; @@ -90,18 +91,9 @@ where fn new_with_mode(main_path: PathBuf, index_path: PathBuf, append: bool) -> Result { let append_mode = append && main_path.exists(); let mut main_file = if append_mode { - OpenOptions::new() - .read(true) - .write(true) - .truncate(false) - .open(&main_path) + open_read_write_file(&main_path) } else { - OpenOptions::new() - .read(true) - .write(true) - .create(true) - .truncate(true) - .open(&main_path) + create_new_file_for_read_write(&main_path) }?; // Retrieve file size and convert to `u32` for `main_offset`, if possible @@ -253,11 +245,7 @@ where { pub fn new(main_path: &Path, index_path: &Path) -> Result { if main_path.exists() && index_path.exists() { - let main_file = OpenOptions::new() - .read(true) - .write(false) - .truncate(false) - .open(main_path)?; + let main_file = open_readonly_file(main_path)?; let index_tree = IndexedDocumentIndex::::load(index_path)?; Ok(Self { @@ -437,10 +425,7 @@ where if main_path.exists() && index_path.exists() { // Attempt to open the main file in the specified mode (append or not) - let main_file = OpenOptions::new() - .read(true) // Open in append mode - .write(true) // Open in append mode - .open(&main_path)?; + let main_file = open_read_write_file(&main_path)?; // Retrieve file size and convert to `u32` for `main_file`, if possible let size = main_file @@ -474,11 +459,7 @@ where let gc_main_path = file_utils::append_extension(&self.main_path, ".gc"); let gc_index_path = file_utils::append_extension(&self.index_path, ".gc"); { - let mut gc_file = OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .open(&gc_main_path)?; + let mut gc_file = create_new_file_for_write(&gc_main_path)?; let mut key_offset = Vec::<(K, OffsetPointer)>::new(); self.index_tree.traverse(|keys, values| { diff --git a/src/repository/kodi_repository.rs b/src/repository/kodi_repository.rs index 63da9d882..fcb5545e3 100644 --- a/src/repository/kodi_repository.rs +++ b/src/repository/kodi_repository.rs @@ -109,7 +109,6 @@ async fn get_tmdb_id(cfg: &Config, provider_id: Option, input_id: u16, std::collections::hash_map::Entry::Vacant(entry) => { if let Some(input) = cfg.get_input_by_id(input_id) { if let Ok(Some(tmdb_path)) = get_input_storage_path(input, &cfg.working_dir) - .map(|storage_path| xtream_get_record_file_path(&storage_path, item_type)) { if let Ok(file_lock) = cfg.file_locks.read_lock(&tmdb_path).await { if let Ok(tree) = BPlusTree::::load(&tmdb_path) { diff --git a/src/repository/xtream_repository.rs b/src/repository/xtream_repository.rs index 13cceec98..eaf717546 100644 --- a/src/repository/xtream_repository.rs +++ b/src/repository/xtream_repository.rs @@ -2,7 +2,6 @@ use std::collections::HashMap; use std::fs; use std::fs::File; use std::io::{BufReader, Error, ErrorKind, Read}; -use std::io::{Seek, SeekFrom}; use std::path::{Path, PathBuf}; use log::error; @@ -19,6 +18,7 @@ use crate::repository::target_id_mapping::{TargetIdMapping, VirtualIdRecord}; use crate::repository::xtream_playlist_iterator::XtreamPlaylistIterator; use crate::utils::json_utils::{json_iter_array, json_write_documents_to_file}; use crate::{create_m3u_filter_error, create_m3u_filter_error_result, notify_err, info_err}; +use crate::utils::file_utils::open_readonly_file; pub static COL_CAT_LIVE: &str = "cat_live"; pub static COL_CAT_SERIES: &str = "cat_series"; @@ -671,7 +671,6 @@ pub async fn xtream_get_input_info( pub async fn xtream_update_input_info_file( cfg: &Config, input: &ConfigInput, - wal_file: &mut File, wal_path: &Path, cluster: XtreamCluster, ) -> Result<(), M3uFilterError> { @@ -679,8 +678,7 @@ pub async fn xtream_update_input_info_file( Ok(Some((info_path, idx_path))) => { match cfg.file_locks.write_lock(&info_path).await { Ok(_file_lock) => { - wal_file.seek(SeekFrom::Start(0)).map_err(|err| notify_err!(format!("Could not read {cluster} info {err}")))?; - let mut reader = BufReader::new(wal_file); + let mut reader = BufReader::new(open_readonly_file(wal_path).map_err(|err| notify_err!(format!("Could not read {cluster} info {err}")))?); match IndexedDocumentWriter::::new_append(info_path, idx_path) { Ok(mut writer) => { let mut provider_id_bytes = [0u8; 4]; @@ -719,7 +717,6 @@ pub async fn xtream_update_input_info_file( pub async fn xtream_update_input_vod_record_from_wal_file( cfg: &Config, input: &ConfigInput, - wal_file: &mut File, wal_path: &Path, ) -> Result<(), M3uFilterError> { let record_path = get_input_storage_path(input, &cfg.working_dir).map(|storage_path| xtream_get_record_file_path(&storage_path, PlaylistItemType::Video)) @@ -728,8 +725,7 @@ pub async fn xtream_update_input_vod_record_from_wal_file( match cfg.file_locks.write_lock(&record_path).await { Ok(_file_lock) => { - wal_file.seek(SeekFrom::Start(0)).map_err(|err| notify_err!(format!("Could not read vod wal info {err}")))?; - let mut reader = BufReader::new(wal_file); + let mut reader = BufReader::new(open_readonly_file(wal_path).map_err(|err| notify_err!(format!("Could not read vod wal info {err}")))?); let mut provider_id_bytes = [0u8; 4]; let mut tmdb_id_bytes = [0u8; 4]; let mut ts_bytes = [0u8; 8]; @@ -763,7 +759,6 @@ pub async fn xtream_update_input_vod_record_from_wal_file( pub async fn xtream_update_input_series_record_from_wal_file( cfg: &Config, input: &ConfigInput, - wal_file: &mut File, wal_path: &Path, ) -> Result<(), M3uFilterError> { let record_path = get_input_storage_path(input, &cfg.working_dir).map(|storage_path| xtream_get_record_file_path(&storage_path, PlaylistItemType::SeriesInfo)) @@ -771,8 +766,7 @@ pub async fn xtream_update_input_series_record_from_wal_file( .and_then(|opt| opt.ok_or_else(|| notify_err!(format!("Error accessing storage path for input: {}", input.name.clone().unwrap_or_else(|| input.id.to_string())))))?; match cfg.file_locks.write_lock(&record_path).await { Ok(_file_lock) => { - wal_file.seek(SeekFrom::Start(0)).map_err(|err| notify_err!(format!("Could not read series wal info {err}")))?; - let mut reader = BufReader::new(wal_file); + let mut reader = BufReader::new(open_readonly_file(wal_path).map_err(|err| notify_err!(format!("Could not read series wal info {err}")))?); let mut provider_id_bytes = [0u8; 4]; let mut ts_bytes = [0u8; 8]; let mut tree_record_index: BPlusTree = BPlusTree::load(&record_path).unwrap_or_else(|_| BPlusTree::new()); @@ -802,7 +796,6 @@ pub async fn xtream_update_input_series_record_from_wal_file( pub async fn xtream_update_input_series_episodes_record_from_wal_file( cfg: &Config, input: &ConfigInput, - wal_file: &mut File, wal_path: &Path, ) -> Result<(), M3uFilterError> { let record_path = get_input_storage_path(input, &cfg.working_dir).map(|storage_path| xtream_get_record_file_path(&storage_path, PlaylistItemType::SeriesEpisode)) @@ -810,8 +803,7 @@ pub async fn xtream_update_input_series_episodes_record_from_wal_file( .and_then(|opt| opt.ok_or_else(|| notify_err!(format!("Error accessing storage path for input: {}", input.name.clone().unwrap_or_else(|| input.id.to_string())))))?; match cfg.file_locks.write_lock(&record_path).await { Ok(_file_lock) => { - wal_file.seek(SeekFrom::Start(0)).map_err(|err| notify_err!(format!("Could not read series episode wal info {err}")))?; - let mut reader = BufReader::new(wal_file); + let mut reader = BufReader::new(open_readonly_file(wal_path).map_err(|err| notify_err!(format!("Could not read series episode wal info {err}")))?); let mut provider_id_bytes = [0u8; 4]; let mut tmdb_id_bytes = [0u8; 4]; let mut tree_record_index: BPlusTree = BPlusTree::load(&record_path).unwrap_or_else(|_| BPlusTree::new()); diff --git a/src/utils/compressed_file_reader.rs b/src/utils/compressed_file_reader.rs index 9437bfd6d..662620d96 100644 --- a/src/utils/compressed_file_reader.rs +++ b/src/utils/compressed_file_reader.rs @@ -1,9 +1,8 @@ -use std::fs::OpenOptions; use std::io::{BufRead, BufReader, Read, Seek, SeekFrom}; use std::path::Path; use flate2::bufread::{GzDecoder, ZlibDecoder}; use crate::utils::compression_utils::{is_deflate, is_gzip}; - +use crate::utils::file_utils::open_readonly_file; pub struct CompressedFileReader { reader: BufReader>, @@ -11,9 +10,7 @@ pub struct CompressedFileReader { impl CompressedFileReader { pub fn new(path: &Path) -> std::io::Result { - let file = OpenOptions::new() - .read(true) - .open(path)?; + let file = open_readonly_file(path)?; let mut buffered_file = BufReader::new(file); let mut header = [0u8; 2]; diff --git a/src/utils/file_utils.rs b/src/utils/file_utils.rs index ce5c79ce3..f1486b0f6 100644 --- a/src/utils/file_utils.rs +++ b/src/utils/file_utils.rs @@ -1,5 +1,5 @@ use std::fs; -use std::fs::File; +use std::fs::{File, OpenOptions}; use std::io::Write; use std::path::{Path, PathBuf}; @@ -175,4 +175,29 @@ pub fn sanitize_filename(file_name: &str) -> String { .chars() .map(|c| if c.is_alphanumeric() || c == '_' || c == '-' { c } else { '_' }) .collect() +} + +#[inline] +pub fn append_or_crate_file(path: &Path) -> std::io::Result { + OpenOptions::new().create(true).append(true).open(path) +} + +#[inline] +pub fn create_new_file_for_write(path:&Path) -> std::io::Result { + OpenOptions::new().write(true).create(true).truncate(true).open(path) +} + +#[inline] +pub fn create_new_file_for_read_write(path:&Path) -> std::io::Result { + OpenOptions::new().read(true).write(true).create(true).truncate(true).open(path) +} + +#[inline] +pub fn open_read_write_file(path:&Path) -> std::io::Result { + OpenOptions::new().read(true).write(true).create(false).truncate(false).open(path) +} + +#[inline] +pub fn open_readonly_file(path:&Path) -> std::io::Result { + OpenOptions::new().read(true).write(false).truncate(false).create(false).open(path) } \ No newline at end of file diff --git a/test/rest-api.http b/test/rest-api.http index 2cf980767..15b80627a 100644 --- a/test/rest-api.http +++ b/test/rest-api.http @@ -49,9 +49,15 @@ GET http://{{local}}/player_api.php?{{xt_credentials}}&action=get_vod_streams&ca GET http://{{local}}/player_api.php?{{xt_credentials}}&action=get_vod_info&vod_id=8051 ### xtream series_categories -GET http://{{local}}/player_api.php?{{xt_credentials}}&action=get_series_categories +GET http://{{silver}}/player_api.php?{{xt_credentials}}&action=get_series_categories ### xtream series +GET http://{{silver}}/player_api.php?{{xt_credentials}}&action=get_series + +### xtream series for category +GET http://{{local}}/player_api.php?{{xt_credentials}}&action=get_series&category_id=56 + +### xtream series for category GET http://{{local}}/player_api.php?{{xt_credentials}}&action=get_series&category_id=56 ### xtream series info