From f7af09a35018684ba7d944a77fb67aa6642dae21 Mon Sep 17 00:00:00 2001 From: euzu Date: Mon, 11 Dec 2023 11:15:06 +0100 Subject: [PATCH] implemented xtream series to m3u playlist. --- CHANGELOG.md | 2 +- README.md | 7 +++--- src/download.rs | 25 ++++++++++++++++------ src/model/config.rs | 4 ++++ src/model/model_playlist.rs | 11 ++++++++++ src/processing/playlist_processor.rs | 32 +++++++++++++++++----------- 6 files changed, 58 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ebf5e5684..44e801184 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ # Changelog # v1.1.5(2023-12-??) * Changed api-proxy user default proxy type from `reverse` to `redirect` -* Added `xtream_resolve_series` option for `m3u` target +* Added `xtream_resolve_series` and `xtream_resolve_series_delay` option for `m3u` target # v1.1.4(2023-12-06) * Breaking change, `config.yml` split into `config.yml` and `source.yml` diff --git a/README.md b/README.md index ad8a02d95..4a44928c9 100644 --- a/README.md +++ b/README.md @@ -285,9 +285,10 @@ Each format has 2 properties - `xtream_skip_video_direct_source` if true the direct_source property from provider for movies is ignored `m3u` output has additional options -- `xtream_resolve_series` `This is option is currently ignored because of mass requests its highly possible that you are banned` -if you have xtream input and m3u output, the series are fetched and resolved. -This can cause a lot of requests to the provider. Be cautious when using this option. +- `xtream_resolve_series` is set to `true` and you have xtream input and m3u output, the series are fetched and resolved. +This can cause a lot of requests to the provider. Be cautious when using this option. +- `xtream_resolve_series_delay` to avoid a provider ban you can set the seconds between series_info_request's. Default is 2 seconds. +But be aware that the more series entries there are, the longer the process takes. ```yaml output: diff --git a/src/download.rs b/src/download.rs index 5b224f294..2c09dd7b7 100644 --- a/src/download.rs +++ b/src/download.rs @@ -41,11 +41,11 @@ pub(crate) async fn get_m3u_playlist(cfg: &Config, input: &ConfigInput, working_ } } - -pub(crate) async fn get_xtream_playlist_series<'a>(fpl: &mut FetchedPlaylist<'a>, errors: &mut Vec) { +pub(crate) async fn get_xtream_playlist_series<'a>(fpl: &mut FetchedPlaylist<'a>, errors: &mut Vec, resolve_delay: u16) -> Vec { let input = fpl.input; + let mut result: Vec = vec![]; for plg in &mut fpl.playlist { - let mut group_series: Option> = None; + let mut group_series: Vec = vec![]; for pli in &plg.channels { let (fetch_series, series_info_url) = { let mut header = pli.header.borrow_mut(); @@ -60,20 +60,31 @@ pub(crate) async fn get_xtream_playlist_series<'a>(fpl: &mut FetchedPlaylist<'a> Ok(series_content) => { match parse_xtream_series_info(&series_content, pli.header.borrow().group.as_str(), input) { Ok(series_info) => { - group_series = series_info; + if let Some(mut series) = series_info { + series.drain(..).for_each(|item| group_series.push(item)); + } } Err(err) => errors.push(err), } } Err(err) => errors.push(err) }; - sleep(std::time::Duration::new(2, 0)); // 2 seconds between + if resolve_delay > 0 { + sleep(std::time::Duration::new(resolve_delay as u64, 0)); // 2 seconds between + } } } - if let Some(mut series) = group_series { - series.drain(..).for_each(|spli| plg.channels.push(spli)); + if !group_series.is_empty() { + let group = PlaylistGroup { + id: plg.id, + title: plg.title.clone(), + channels: group_series, + xtream_cluster: XtreamCluster::Series, + }; + result.push(group); } } + result } diff --git a/src/model/config.rs b/src/model/config.rs index ffbfa1d21..cef0e61db 100644 --- a/src/model/config.rs +++ b/src/model/config.rs @@ -143,6 +143,8 @@ impl ConfigRename { } } +fn default_as_two() -> u16 { 2 } + #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] pub(crate) struct ConfigTargetOptions { #[serde(default = "default_as_false")] @@ -159,6 +161,8 @@ pub(crate) struct ConfigTargetOptions { pub xtream_skip_video_direct_source: bool, #[serde(default = "default_as_false")] pub xtream_resolve_series: bool, + #[serde(default = "default_as_two")] + pub xtream_resolve_series_delay: u16, } #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)] diff --git a/src/model/model_playlist.rs b/src/model/model_playlist.rs index c1f376440..8a766afd0 100644 --- a/src/model/model_playlist.rs +++ b/src/model/model_playlist.rs @@ -19,6 +19,17 @@ pub(crate) struct FetchedPlaylist<'a> { pub epg: Option, } +impl FetchedPlaylist<'_> { + pub(crate) fn update_playlist(&mut self, plg: &PlaylistGroup) { + for grp in self.playlist.iter_mut() { + if grp.id == plg.id { + plg.channels.iter().for_each(|item| grp.channels.push(item.clone())); + return; + } + } + } +} + #[derive(Debug, Clone, PartialEq)] pub(crate) enum XtreamCluster { Live = 1, diff --git a/src/processing/playlist_processor.rs b/src/processing/playlist_processor.rs index b0854fb59..a93698454 100644 --- a/src/processing/playlist_processor.rs +++ b/src/processing/playlist_processor.rs @@ -426,22 +426,30 @@ pub(crate) async fn process_playlist<'a>(playlists: &mut [FetchedPlaylist<'a>], new_fpl.playlist = v; } } - let resolve_series = + let (resolve_series, resolve_series_delay) = if let Some(options) = &target.options { - options.xtream_resolve_series && fpl.input.input_type == InputType::Xtream && target.has_output(&TargetType::M3u) + (options.xtream_resolve_series && fpl.input.input_type == InputType::Xtream && target.has_output(&TargetType::M3u), + options.xtream_resolve_series_delay) } else { - false + (false, 0) }; if resolve_series { - get_xtream_playlist_series(fpl, errors).await; - // the original list has now resolved the series info items. - // wee need to pipe them an put them into - // TODO - //for plg in fpl.playlist { - // 1. extract series playlistitems per group - // 2. run processing pipe - // 3. assign results to the new_fpl groups. - //} + let mut series_playlist = get_xtream_playlist_series(fpl, errors, resolve_series_delay).await; + // original content saved into original list + for plg in &series_playlist { + fpl.update_playlist(plg); + } + // run processing pipe over new items + for f in &pipe { + let r = f(&mut series_playlist, target); + if let Some(v) = r { + series_playlist = v; + } + } + // assign new items to the new playlist + for plg in &series_playlist { + new_fpl.update_playlist(plg); + } } // stats