mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-31 11:12:36 +01:00
* perf: binary-search EPG programme lookup Replaces the linear position() scan with a binary search via partition_point for EPG programmes. Reduces complexity to O(log p). * perf: cache interned PlaylistItemType label Avoids repetitive hash-map lookups during sorting and filtering. The five fixed labels are now interned once via OnceLock and returned as a cheap Arc clone. * perf: avoid per-event String allocation in web-UI XMLTV parser Uses borrowed Cow types instead of allocating a new String for every element name. Tracks the active text state using a small TextTag enum. * perf: throttle per-chunk download control poll Throttles the control_signal lock polling to a 200ms interval on multi-GB downloads to prevent CPU thrashing. Immediate actions (pause/cancel) remain guaranteed via control_notify. * perf: pre-size filtered channel buffers in apply_filter_to_playlist Pre-allocates the filtered channel vector based on the source group length. Eliminates step-by-step reallocations on large playlists. * fix: surface playlist cache load failures instead of silent empty fallback Replaces silent error swallowing with real error logging (distinguishing missing files from corruption) while preserving the empty fallback behavior. * perf: remove redundant first drain loop in get_remote_content_as_file Removes a dead, redundant loop. The stream body is now consumed only once, allowing the idle timeout to protect the transfer correctly. * perf: back off instead of busy-spinning on empty client-stream chunks Introduces a short Sleep backoff (Poll::Pending) when a provider sends continuous empty keep-alive chunks. Prevents the CPU from spinning hot. * perf: make library orphan cleanup linear instead of O(n^2) Reduces the complexity of cleaning up orphaned entries from O(n^2) to linear O(n). Loads the metadata library only once and pre-computes partitions. * perf: indexed batched merge for series expansion instead of O(n^2) scans Replaces expensive O(n^2) scans during series expansion with index-based batch processing. Builds temporary lookup indexes and merges groups linearly. * perf: pre-size group flatten buffers to reduce reallocation churn Pre-allocates target merge vectors and category indexes to eliminate repeated memory reallocations when flattening large target playlists. * perf: stream custom provider playlist endpoint instead of materializing it Switches from full in-memory materialization to lazy conversion and streaming of JSON/binary responses. Massively reduces the memory footprint. * refactor: centralize InputType batch/family checks behind enum helpers Moves scattered matches! checks for M3u/Xtream types into centralized enum helpers (is_batch(), etc.) to prevent logic drift. * refactor: centralize XMLTV tag/attribute literals into named constants Replaces hardcoded inline strings in the XMLTV parser with named constants (EPG_TAG_TITLE, etc.) as a single source of truth. * refactor: derive GeoIpUpdateError Display/Error via thiserror Replaces manual Display and Error implementations with the thiserror macro, allowing new error variants to self-document their formatting. * refactor: attach per-cluster facts to XtreamCluster instead of scattered matches Centralizes redundant API actions and collection name mappings directly onto the XtreamCluster struct instead of using scattered match blocks. * refactor: centralize PlaylistItemType video/series classification Introduces is_video() / is_series() on the enum to unify duplicate matches! checks across filters, probes, and tracking modules. * feat: add per-stage summary logs to VOD/series resolve queueing Adds a concise debug summary per processing stage (total counters and elapsed time) to make hot-path diagnostics easier without raising the log level. * refactor: generate ItemField get/set from a single direct-field list Uses a callback macro to synchronously generate both getter and setter blocks for directly-bound fields (Name, Url, etc.) from a single master list. * refactor: derive input-type routing from a single capability descriptor Bundles InputType behaviors (backends, connection rules) into a single capabilities() descriptor to prevent bugs when adding new variants. * refactor: derive output-format routing from a single capability descriptor Centralizes output format features (filter, EPG, and cache support) into a single TargetCapabilities descriptor instead of spreading logic across no-op match arms.
87 lines
3.1 KiB
Rust
87 lines
3.1 KiB
Rust
use crate::{
|
|
api::model::AppState,
|
|
model::InputSource,
|
|
repository::get_geoip_path,
|
|
utils::{request::download_text_content, GeoIp},
|
|
};
|
|
use log::{error, info};
|
|
use shared::{
|
|
model::{default_geoip_url, InputFetchMethod},
|
|
utils::Internable,
|
|
};
|
|
use std::{collections::HashMap, io::Cursor, sync::Arc};
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
pub(crate) enum GeoIpUpdateError {
|
|
#[error("GeoIp update is disabled")]
|
|
Disabled,
|
|
#[error("Failed to download geoip db: {0}")]
|
|
DownloadFailed(String),
|
|
#[error("Failed to process geoip db: {0}")]
|
|
ProcessFailed(String),
|
|
#[error("Unknown GeoIp processing error")]
|
|
UnknownProcessing,
|
|
}
|
|
|
|
pub(crate) async fn update_geoip_db(app_state: &Arc<AppState>) -> Result<(), GeoIpUpdateError> {
|
|
let config = app_state.app_config.config.load();
|
|
if let Some(geoip) = config.reverse_proxy.as_ref().and_then(|r| r.geoip.as_ref()) {
|
|
if geoip.enabled {
|
|
let geoip_db_path = &*get_geoip_path(&config.storage_dir);
|
|
let _file_lock = app_state.app_config.file_locks.write_lock(geoip_db_path).await;
|
|
|
|
let url = if geoip.url.trim().is_empty() { default_geoip_url() } else { geoip.url.clone() };
|
|
let input_source = InputSource {
|
|
name: "GeoIP".intern(),
|
|
url,
|
|
provider: None,
|
|
username: None,
|
|
password: None,
|
|
method: InputFetchMethod::GET,
|
|
headers: HashMap::default(),
|
|
};
|
|
return match download_text_content(
|
|
&app_state.app_config,
|
|
&app_state.http_client.load(),
|
|
&input_source,
|
|
None,
|
|
None,
|
|
false,
|
|
)
|
|
.await
|
|
{
|
|
Ok((content, _)) => {
|
|
let reader = Cursor::new(content);
|
|
let mut geoip = GeoIp::new();
|
|
let result = {
|
|
match geoip.import_ipv4_from_csv(reader, geoip_db_path) {
|
|
Ok(size) => (Some(size), None),
|
|
Err(err) => (None, Some(err)),
|
|
}
|
|
};
|
|
|
|
return match result {
|
|
(Some(_), None) => {
|
|
info!("GeoIp db updated");
|
|
app_state.geoip.store(Some(Arc::new(geoip)));
|
|
Ok(())
|
|
}
|
|
(None, Some(err)) => {
|
|
let error = GeoIpUpdateError::ProcessFailed(err.to_string());
|
|
error!("{error}");
|
|
Err(error)
|
|
}
|
|
_ => Err(GeoIpUpdateError::UnknownProcessing),
|
|
};
|
|
}
|
|
Err(err) => {
|
|
let error = GeoIpUpdateError::DownloadFailed(err.to_string());
|
|
error!("{error}");
|
|
Err(error)
|
|
}
|
|
};
|
|
}
|
|
}
|
|
Err(GeoIpUpdateError::Disabled)
|
|
}
|