wip resolve vod info

This commit is contained in:
euzu
2024-12-10 23:16:04 +01:00
parent fc36c3c209
commit 925f2b85fc
19 changed files with 507 additions and 197 deletions
+12 -11
View File
@@ -26,7 +26,7 @@ use crate::model::stats::{InputStats, PlaylistStats};
use crate::processing::affix_processor::apply_affixes;
use crate::processing::playlist_watch::process_group_watch;
use crate::processing::xmltv_parser::flatten_tvguide;
use crate::processing::xtream_processor::playlist_resolve_series;
use crate::processing::xtream_processor::{playlist_resolve_series, playlist_resolve_movies};
use crate::repository::playlist_repository::persist_playlist;
use crate::utils::default_utils::default_as_default;
use crate::utils::download;
@@ -465,29 +465,30 @@ async fn process_playlist(playlists: &mut [FetchedPlaylist<'_>],
let pipe = get_processing_pipe(target);
debug_if_enabled!("Processing order is {}", &target.processing_order);
let mut new_fetched_playlists: Vec<FetchedPlaylist> = vec![];
for fpl in playlists.iter_mut() {
let mut new_fpl = execute_pipe(target, &pipe, fpl);
playlist_resolve_series(target, errors, &pipe, fpl, &mut new_fpl).await;
let mut processed_fetched_playlists: Vec<FetchedPlaylist> = vec![];
for provider_fpl in playlists.iter_mut() {
let mut processed_fpl = execute_pipe(target, &pipe, provider_fpl);
playlist_resolve_series(target, errors, &pipe, provider_fpl, &mut processed_fpl).await;
playlist_resolve_movies(cfg, target, errors, &processed_fpl).await;
// stats
let input_stats = stats.get_mut(&new_fpl.input.id);
let input_stats = stats.get_mut(&processed_fpl.input.id);
if let Some(stat) = input_stats {
stat.processed_stats.group_count = new_fpl.playlistgroups.len();
stat.processed_stats.channel_count = new_fpl.playlistgroups.iter()
stat.processed_stats.group_count = processed_fpl.playlistgroups.len();
stat.processed_stats.channel_count = processed_fpl.playlistgroups.iter()
.map(|group| group.channels.len())
.sum();
}
new_fetched_playlists.push(new_fpl);
processed_fetched_playlists.push(processed_fpl);
}
apply_affixes(&mut new_fetched_playlists);
apply_affixes(&mut processed_fetched_playlists);
let mut new_playlist = vec![];
let mut new_epg = vec![];
// each fetched playlist can have its own epgl url.
// we need to process each input epg.
for mut fp in new_fetched_playlists {
for mut fp in processed_fetched_playlists {
// collect all epg_channel ids
let epg_channel_ids: HashSet<_> = fp.playlistgroups.iter().flat_map(|g| &g.channels)
.filter_map(|c| c.header.borrow().epg_channel_id.clone()).collect();
+1 -2
View File
@@ -15,8 +15,7 @@ pub fn process_group_watch(cfg: &Config, target_name: &str, pl: &PlaylistGroup)
new_tree.insert(title);
});
let file_name = format!("watch_{target_name}_{}", &pl.title);
let watch_filename = sanitize_filename(&format!("{file_name}.bin"));
let watch_filename = format!("{}/{}.bin", sanitize_filename(target_name), sanitize_filename(&pl.title));
match file_utils::get_file_path(&cfg.working_dir, Some(std::path::PathBuf::from(&watch_filename))) {
Some(path) => {
let save_path = path.as_path();
+10 -4
View File
@@ -9,6 +9,7 @@ use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
use crate::model::config::ConfigInput;
use crate::model::playlist::{PlaylistGroup, PlaylistItem, PlaylistItemHeader, PlaylistItemType, XtreamCluster};
use crate::model::xtream::{XtreamCategory, XtreamSeriesInfo, XtreamSeriesInfoEpisode, XtreamStream};
use crate::repository::storage::hash_string;
fn map_to_xtream_category(categories: &Value) -> Result<Vec<XtreamCategory>, M3uFilterError> {
match serde_json::from_value::<Vec<XtreamCategory>>(categories.to_owned()) {
@@ -45,15 +46,18 @@ pub fn parse_xtream_series_info(info: &Value, group_title: &str, input: &ConfigI
match serde_json::from_value::<XtreamSeriesInfo>(info.to_owned()) {
Ok(series_info) => {
let result: Vec<PlaylistItem> = series_info.episodes.values().flatten().map(|episode|
let result: Vec<PlaylistItem> = series_info.episodes.values().flatten().map(|episode| {
let episode_url = create_xtream_series_info_url(url, username, password, episode);
PlaylistItem {
header: RefCell::new(PlaylistItemHeader {
id: Rc::new(episode.id.to_string()),
uuid: Rc::new(hash_string(&episode_url)),
name: Rc::new(episode.title.clone()),
logo: Rc::new(episode.info.movie_image.clone()),
group: Rc::new(group_title.to_string()),
title: Rc::new(episode.title.clone()),
url: create_xtream_series_info_url(url, username, password, episode),
url: episode_url,
item_type: PlaylistItemType::Series,
xtream_cluster: XtreamCluster::Series,
additional_properties: episode.get_additional_properties(&series_info),
@@ -61,7 +65,7 @@ pub fn parse_xtream_series_info(info: &Value, group_title: &str, input: &ConfigI
input_id: input.id,
..Default::default()
})
}).collect();
}}).collect();
if result.is_empty() { Ok(None) } else { Ok(Some(result)) }
}
Err(err) => {
@@ -109,14 +113,16 @@ pub fn parse_xtream(input: &ConfigInput,
if let Some(group) = group_map.get(&stream.category_id) {
let mut grp = group.borrow_mut();
let category_name = &grp.category_name;
let stream_url = create_xtream_url(xtream_cluster, url, username, password, &stream);
let item = PlaylistItem {
header: RefCell::new(PlaylistItemHeader {
id: Rc::new(stream.get_stream_id().to_string()),
uuid: Rc::new(hash_string(&stream_url)),
name: Rc::clone(&stream.name),
logo: Rc::clone(&stream.stream_icon),
group: Rc::clone(category_name),
title: Rc::clone(&stream.name),
url: create_xtream_url(xtream_cluster, url, username, password, &stream),
url: stream_url,
epg_channel_id: stream.epg_channel_id.clone(),
item_type: PlaylistItemType::from(xtream_cluster),
xtream_cluster,
+72 -9
View File
@@ -1,25 +1,37 @@
use crate::m3u_filter_error::M3uFilterError;
use crate::model::config::{ConfigTarget, InputType, TargetType};
use crate::model::playlist::FetchedPlaylist;
use crate::m3u_filter_error::{M3uFilterError, M3uFilterErrorKind};
use crate::model::config::{Config, ConfigInput, ConfigTarget, InputType, TargetType};
use crate::model::playlist::{FetchedPlaylist, PlaylistEntry, PlaylistItem, PlaylistItemType, UUIDType, XtreamCluster};
use crate::processing::playlist_processor::ProcessingPipe;
use crate::repository::storage::get_input_storage_path;
use crate::repository::xtream_repository::xtream_get_info_file_paths;
use crate::repository::IndexedDocumentQuery;
use crate::utils::download;
use crate::utils::download::get_xtream_stream_info_content;
use std::collections::HashSet;
use std::rc::Rc;
pub async fn playlist_resolve_series(target: &ConfigTarget, errors: &mut Vec<M3uFilterError>,
pipe: &ProcessingPipe,
fpl: &mut FetchedPlaylist<'_>,
new_fpl: &mut FetchedPlaylist<'_>) {
provider_fpl: &mut FetchedPlaylist<'_>,
processed_fpl: &mut FetchedPlaylist<'_>) {
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 && provider_fpl.input.input_type == InputType::Xtream && target.has_output(&TargetType::M3u),
options.xtream_resolve_series_delay)
} else {
(false, 0)
};
if resolve_series {
let mut series_playlist = download::get_xtream_playlist_series(fpl, errors, resolve_series_delay).await;
// collect all series in the processed lists
let to_process_uuids: HashSet<Rc<UUIDType>> = processed_fpl.playlistgroups.iter()
.filter(|plg| plg.xtream_cluster == XtreamCluster::Series)
.flat_map(|plg| &plg.channels)
.filter(|pli| pli.header.borrow().item_type == PlaylistItemType::SeriesInfo)
.map(|pli| Rc::clone(&pli.header.borrow().uuid)).collect();
let mut series_playlist = download::get_xtream_playlist_series(provider_fpl, to_process_uuids, errors, resolve_series_delay).await;
// original content saved into original list
for plg in &series_playlist {
fpl.update_playlist(plg);
provider_fpl.update_playlist(plg);
}
// run processing pipe over new items
for f in pipe {
@@ -30,7 +42,58 @@ pub async fn playlist_resolve_series(target: &ConfigTarget, errors: &mut Vec<M3u
}
// assign new items to the new playlist
for plg in &series_playlist {
new_fpl.update_playlist(plg);
processed_fpl.update_playlist(plg);
}
}
}
async fn playlist_resolve_movies_process_playlist_item(pli: &PlaylistItem, input: &ConfigInput, errors: &mut Vec<M3uFilterError>, resolve_delay: u16) -> Option<String> {
let mut result = None;
let provider_id = pli.get_provider_id().unwrap_or(0);
if let Some(info_url) = download::get_xtream_player_api_info_url(input, XtreamCluster::Video, provider_id) {
result = match get_xtream_stream_info_content(&info_url, input).await {
Ok(content) => Some(content),
Err(err) => {
errors.push(M3uFilterError::new(M3uFilterErrorKind::Info, format!("{err}")));
None
}
};
}
if resolve_delay > 0 {
actix_web::rt::time::sleep(std::time::Duration::new(u64::from(resolve_delay), 0)).await;
}
result
}
pub async fn playlist_resolve_movies(cfg: &Config, target: &ConfigTarget, errors: &mut Vec<M3uFilterError>, fpl: &FetchedPlaylist<'_>) {
let (resolve_movies, resolve_delay) =
if let Some(options) = &target.options {
(options.xtream_resolve_movies && fpl.input.input_type == InputType::Xtream, options.xtream_resolve_movies_delay)
} else {
(false, 0)
};
if !resolve_movies {
return;
}
match get_input_storage_path(fpl.input, &cfg.working_dir).map(|storage_path| xtream_get_info_file_paths(&storage_path, XtreamCluster::Video)) {
Ok(Some((file_path, idx_path))) => {
match cfg.file_locks.write_lock(&file_path).await {
Ok(_file_lock) => {
match IndexedDocumentWriter::<UUIDType, String>::try_new(&idx_path) {
Ok(mut info_id_mapping) => {
for pli in fpl.playlistgroups.iter().flat_map(|plg| &plg.channels) {
if info_id_mapping.query(pli.header.borrow().get_uuid()).is_none() {
playlist_resolve_movies_process_playlist_item(pli, fpl.input, errors, resolve_delay).await;
}
}
}
Err(err) => errors.push(M3uFilterError::new(M3uFilterErrorKind::Info, format!("Could not load id mapping for input {err}"))),
}
}
Err(err) => errors.push(M3uFilterError::new(M3uFilterErrorKind::Info, format!("{err}"))),
}
}
Ok(None) => errors.push(M3uFilterError::new(M3uFilterErrorKind::Notify, format!("Could not create storage path for input {}", &fpl.input.name.as_ref().map_or("?", |v| v)))),
Err(err) => errors.push(M3uFilterError::new(M3uFilterErrorKind::Notify, format!("Could not create storage path for input {err}"))),
}
}