mirror of
https://github.com/euzu/tuliprox.git
synced 2026-10-04 06:52:26 +02:00
fetching tmdb data for series
This commit is contained in:
@@ -1,12 +1,12 @@
|
||||
use crate::library::{MediaMetadata, MetadataAsyncIter, MetadataCacheEntry};
|
||||
use crate::model::{AppConfig, ConfigInput, XtreamSeriesInfo, XtreamSeriesInfoEpisode, XtreamSeriesInfoEpisodeInfo, XtreamSeriesInfoInfo};
|
||||
use crate::model::{XtreamMovieData, XtreamMovieInfo, XtreamMovieInfoDetails};
|
||||
use crate::model::{AppConfig, ConfigInput};
|
||||
use serde_json::value::RawValue;
|
||||
use shared::error::TuliproxError;
|
||||
use shared::model::{PlaylistGroup, PlaylistItem, PlaylistItemHeader, PlaylistItemType, XtreamCluster};
|
||||
use shared::utils::string_to_uuid_type;
|
||||
use std::sync::Arc;
|
||||
use shared::utils::{generate_playlist_uuid, string_to_uuid_type};
|
||||
use std::path::Path;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub async fn get_library_playlist(_client: &reqwest::Client, app_config: &Arc<AppConfig>, input: &Arc<ConfigInput>) -> (Vec<PlaylistGroup>, Vec<TuliproxError>) {
|
||||
let config = &*app_config.config.load();
|
||||
@@ -31,12 +31,12 @@ pub async fn get_library_playlist(_client: &reqwest::Client, app_config: &Arc<Ap
|
||||
match entry.metadata {
|
||||
MediaMetadata::Movie(_) => {
|
||||
let pli = to_playlist_item(&entry, &input.name, &library_config.playlist.movie_category);
|
||||
group_movies.channels.push(pli);
|
||||
},
|
||||
group_movies.channels.extend(pli);
|
||||
}
|
||||
MediaMetadata::Series(_) => {
|
||||
let pli = to_playlist_item(&entry, &input.name, &library_config.playlist.series_category);
|
||||
group_series.channels.push(pli);
|
||||
},
|
||||
group_series.channels.extend(pli);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,47 +51,78 @@ pub async fn get_library_playlist(_client: &reqwest::Client, app_config: &Arc<Ap
|
||||
(groups, vec![])
|
||||
}
|
||||
|
||||
fn to_playlist_item(entry: &MetadataCacheEntry, input_name: &str, group_name: &str) -> PlaylistItem {
|
||||
fn to_playlist_item(entry: &MetadataCacheEntry, input_name: &str, group_name: &str) -> Vec<PlaylistItem> {
|
||||
let metadata = &entry.metadata;
|
||||
|
||||
let (xtream_cluster, item_type, additional_props): (XtreamCluster, PlaylistItemType, Option<Box<RawValue>>) = {
|
||||
match metadata {
|
||||
MediaMetadata::Movie(_) => {
|
||||
let add_props = metadata_cache_entry_to_xtream_movie_info(entry).and_then(|info| {
|
||||
let raw = serde_json::to_string(&info).ok()?;
|
||||
RawValue::from_string(raw).ok()
|
||||
});
|
||||
(XtreamCluster::Video, PlaylistItemType::LocalVideo, add_props)
|
||||
}
|
||||
MediaMetadata::Series(_s) => {
|
||||
(XtreamCluster::Series, PlaylistItemType::LocalSeriesInfo, None)
|
||||
}
|
||||
match metadata {
|
||||
MediaMetadata::Movie(_) => {
|
||||
let additional_properties = metadata_cache_entry_to_xtream_movie_info(entry).and_then(|info| {
|
||||
let raw = serde_json::to_string(&info).ok()?;
|
||||
RawValue::from_string(raw).ok()
|
||||
});
|
||||
vec![PlaylistItem {
|
||||
header: PlaylistItemHeader {
|
||||
uuid: string_to_uuid_type(&entry.uuid),
|
||||
name: metadata.title().to_string(),
|
||||
group: group_name.to_string(),
|
||||
title: metadata.title().to_string(),
|
||||
url: format!("file://{}", entry.file_path),
|
||||
xtream_cluster: XtreamCluster::Video,
|
||||
additional_properties,
|
||||
item_type: PlaylistItemType::LocalVideo,
|
||||
input_name: input_name.to_string(),
|
||||
..PlaylistItemHeader::default()
|
||||
}
|
||||
}]
|
||||
}
|
||||
};
|
||||
MediaMetadata::Series(_series) => {
|
||||
let mut items = vec![];
|
||||
if let Some(xtream_series_info) = metadata_cache_entry_to_xtream_series_info(entry) {
|
||||
let additional_properties = serde_json::to_string(&xtream_series_info).ok().and_then(|r| RawValue::from_string(r).ok());
|
||||
|
||||
PlaylistItem {
|
||||
header: PlaylistItemHeader {
|
||||
uuid: string_to_uuid_type(&entry.uuid),
|
||||
id: String::new(),
|
||||
virtual_id: 0,
|
||||
name: metadata.title().to_string(),
|
||||
chno: String::new(),
|
||||
logo: String::new(),
|
||||
logo_small: String::new(),
|
||||
group: group_name.to_string(),
|
||||
title: metadata.title().to_string(),
|
||||
parent_code: String::new(),
|
||||
audio_track: String::new(),
|
||||
time_shift: String::new(),
|
||||
rec: String::new(),
|
||||
url: format!("file://{}", entry.file_path),
|
||||
epg_channel_id: None,
|
||||
xtream_cluster,
|
||||
additional_properties: additional_props,
|
||||
item_type,
|
||||
category_id: 0,
|
||||
input_name: input_name.to_string(),
|
||||
},
|
||||
let series_info = PlaylistItem {
|
||||
header: PlaylistItemHeader {
|
||||
uuid: string_to_uuid_type(&entry.uuid),
|
||||
id: entry.uuid.clone(),
|
||||
name: metadata.title().to_string(),
|
||||
group: group_name.to_string(),
|
||||
title: metadata.title().to_string(),
|
||||
url: format!("file://{}", entry.file_path),
|
||||
xtream_cluster: XtreamCluster::Series,
|
||||
item_type: PlaylistItemType::LocalSeriesInfo,
|
||||
input_name: input_name.to_string(),
|
||||
additional_properties,
|
||||
..PlaylistItemHeader::default()
|
||||
}
|
||||
};
|
||||
|
||||
items.push(series_info);
|
||||
if let Some(episodes) = xtream_series_info.episodes.as_ref() {
|
||||
for episode in episodes {
|
||||
items.push(PlaylistItem {
|
||||
header: PlaylistItemHeader {
|
||||
id: episode.id.to_string(),
|
||||
// we use parent_code for local series to find the parent series info and straighten the virtual_ids
|
||||
parent_code: entry.uuid.clone(),
|
||||
uuid: generate_playlist_uuid(&input_name, &episode.id.to_string(), PlaylistItemType::LocalSeries, &episode.direct_source),
|
||||
name: episode.title.clone(),
|
||||
group: group_name.to_string(),
|
||||
title: episode.title.clone(),
|
||||
url: episode.direct_source.clone(),
|
||||
xtream_cluster: XtreamCluster::Series,
|
||||
item_type: PlaylistItemType::LocalSeries,
|
||||
category_id: 0,
|
||||
input_name: input_name.to_string(),
|
||||
additional_properties: episode.get_additional_properties(&xtream_series_info),
|
||||
..Default::default()
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
items
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,33 +143,24 @@ pub fn metadata_cache_entry_to_xtream_movie_info(
|
||||
|
||||
let info = XtreamMovieInfoDetails {
|
||||
kinopoisk_url: movie.tmdb_id.map(|id| format!("https://www.themoviedb.org/movie/{id}")),
|
||||
tmdb_id: movie.tmdb_id.map(|id|id.to_string()),
|
||||
|
||||
tmdb_id: movie.tmdb_id.map(|id| id.to_string()),
|
||||
name: Some(movie.title.clone()),
|
||||
o_name: movie.original_title.clone(),
|
||||
|
||||
cover_big: movie.poster.clone(),
|
||||
movie_image: movie.poster.clone(),
|
||||
|
||||
releasedate: movie.year.map(|y| format!("{y}-01-01")),
|
||||
episode_run_time: movie.runtime,
|
||||
|
||||
youtube_trailer: None,
|
||||
director: movie.directors.as_ref().map(|d| d.join(", ")),
|
||||
youtube_trailer: movie.videos.as_ref().and_then(|v| v.iter().find(|video| video.site.eq_ignore_ascii_case("youtube")).map(|video| video.key.clone())),
|
||||
actors: actor_names.clone(),
|
||||
cast: actor_names,
|
||||
genre: movie.genres.as_ref().map(|g| g.join(", ")),
|
||||
|
||||
description: movie.plot.clone(),
|
||||
plot: movie.plot.clone(),
|
||||
|
||||
age: None,
|
||||
mpaa_rating: movie.mpaa.clone(),
|
||||
|
||||
rating_count_kinopoisk: 0,
|
||||
|
||||
country: None,
|
||||
|
||||
backdrop_path: movie
|
||||
.fanart
|
||||
.as_ref()
|
||||
@@ -151,7 +173,6 @@ pub fn metadata_cache_entry_to_xtream_movie_info(
|
||||
.map(|p| vec![p.clone()])
|
||||
})
|
||||
.unwrap_or_default(),
|
||||
|
||||
duration_secs: movie.runtime.map(|r| (r * 60).to_string()),
|
||||
duration: movie.runtime.map(|r| {
|
||||
let h = r / 60;
|
||||
@@ -161,9 +182,7 @@ pub fn metadata_cache_entry_to_xtream_movie_info(
|
||||
|
||||
video: Vec::new(),
|
||||
audio: Vec::new(),
|
||||
|
||||
bitrate: 0,
|
||||
|
||||
rating: movie.rating.map(|r| format!("{r:.2}")),
|
||||
runtime: movie.runtime.map(|r| (r * 60).to_string()),
|
||||
status: Some("Released".to_string()),
|
||||
@@ -172,12 +191,9 @@ pub fn metadata_cache_entry_to_xtream_movie_info(
|
||||
let movie_data = XtreamMovieData {
|
||||
stream_id: 0,
|
||||
name: movie.title.clone(),
|
||||
|
||||
added: Some(entry.file_modified.to_string()),
|
||||
|
||||
category_id: 0,
|
||||
category_ids: vec![0],
|
||||
|
||||
container_extension,
|
||||
custom_sid: None,
|
||||
direct_source: String::new(),
|
||||
@@ -185,3 +201,109 @@ pub fn metadata_cache_entry_to_xtream_movie_info(
|
||||
|
||||
Some(XtreamMovieInfo { info, movie_data })
|
||||
}
|
||||
|
||||
pub fn metadata_cache_entry_to_xtream_series_info(
|
||||
entry: &MetadataCacheEntry,
|
||||
) -> Option<XtreamSeriesInfo> {
|
||||
let series = match &entry.metadata {
|
||||
MediaMetadata::Movie(_) => return None,
|
||||
MediaMetadata::Series(m) => m,
|
||||
};
|
||||
|
||||
let actor_names = series.actors.as_ref().map(|a| a.iter().map(|a| a.name.clone()).collect::<Vec<_>>().join(", ")).unwrap_or_default();
|
||||
let release_date = series.year.map(|y| format!("{y}-01-01")).unwrap_or_default();
|
||||
let youtube_trailer = series.videos.as_ref().and_then(|v| v.iter().find(|video| video.site.eq_ignore_ascii_case("youtube")).map(|video| video.key.clone())).unwrap_or_default();
|
||||
|
||||
let info = XtreamSeriesInfoInfo {
|
||||
name: series.title.clone(),
|
||||
cover: series.poster.clone().unwrap_or_default(),
|
||||
plot: series.plot.clone().unwrap_or_default(),
|
||||
cast: actor_names,
|
||||
director: series.directors.as_ref().map(|d| d.join(", ")).unwrap_or_default(),
|
||||
genre: series.genres.as_ref().map(|d| d.join(", ")).unwrap_or_default(),
|
||||
release_date: release_date.clone(),
|
||||
releaseDate: release_date.clone(),
|
||||
releasedate: release_date.clone(),
|
||||
last_modified: series.last_updated.to_string(),
|
||||
rating: series.rating.unwrap_or(0f64),
|
||||
rating_5based: 0.0,
|
||||
backdrop_path: series
|
||||
.fanart
|
||||
.as_ref()
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|f| vec![f.clone()])
|
||||
.or_else(|| {
|
||||
series.poster.as_ref()
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|p| vec![p.clone()])
|
||||
}),
|
||||
trailer: youtube_trailer.clone(),
|
||||
youtube_trailer,
|
||||
episode_run_time: String::from("0"),
|
||||
category_id: 0,
|
||||
tmdb_id: series.tmdb_id.clone(),
|
||||
tmdb: series.tmdb_id.clone(),
|
||||
year: series.year.clone(),
|
||||
};
|
||||
|
||||
// seasons are not delivered through xtream get_series_info.
|
||||
let seasons = Some(vec![]); /*series.seasons.as_ref()
|
||||
.map(|seasons| seasons.iter().map(|season| XtreamSeriesInfoSeason {
|
||||
air_date: season.air_date.clone().unwrap_or_default(),
|
||||
episode_count: season.episode_count,
|
||||
id: season.id,
|
||||
name: season.name.clone(),
|
||||
overview: season.overview.clone().unwrap_or_default(),
|
||||
season_number: season.season_number,
|
||||
vote_average: season.vote_average,
|
||||
cover: season.poster_path.as_ref().cloned().unwrap_or_default(),
|
||||
cover_big: season.poster_path.as_ref().cloned().unwrap_or_default(),
|
||||
}).collect());
|
||||
*/
|
||||
|
||||
let episodes = series.episodes.as_ref().map(|episodes| {
|
||||
episodes.iter().filter(|episode| !episode.file_path.is_empty()).map(|episode| {
|
||||
let container_extension = Path::new(&episode.file_path)
|
||||
.extension()
|
||||
.and_then(|s| s.to_str())
|
||||
.map(ToString::to_string)
|
||||
.unwrap_or_default();
|
||||
|
||||
let episode_release_date = episode.aired.as_ref().map(ToString::to_string).unwrap_or_default();
|
||||
|
||||
let tmdb_id = (episode.tmdb_id > 0).then_some(episode.tmdb_id);
|
||||
|
||||
XtreamSeriesInfoEpisode {
|
||||
id: episode.id,
|
||||
episode_num: episode.episode,
|
||||
season: episode.season,
|
||||
title: episode.title.clone(),
|
||||
container_extension,
|
||||
info: Some(XtreamSeriesInfoEpisodeInfo {
|
||||
id: tmdb_id,
|
||||
tmdb_id,
|
||||
tmdb: tmdb_id,
|
||||
season: episode.season,
|
||||
release_date: episode_release_date.clone(),
|
||||
releaseDate: episode_release_date.clone(),
|
||||
releasedate: episode_release_date,
|
||||
plot: episode.plot.clone().unwrap_or_default(),
|
||||
duration_secs: episode.runtime.map(|r| r * 60).unwrap_or(0),
|
||||
duration: episode.runtime
|
||||
.map(|r| format!("{:02}:{:02}:00", r / 60, r % 60))
|
||||
.unwrap_or_default(),
|
||||
movie_image: episode.thumb.clone().unwrap_or_default(),
|
||||
video: None,
|
||||
audio: None,
|
||||
bitrate: 0,
|
||||
rating: episode.rating.unwrap_or(0.0),
|
||||
}),
|
||||
custom_sid: String::new(),
|
||||
added: episode.file_modified.to_string(),
|
||||
direct_source: episode.file_path.clone(),
|
||||
}
|
||||
}).collect::<Vec<_>>()
|
||||
});
|
||||
|
||||
Some(XtreamSeriesInfo { seasons, info: Some(info), episodes })
|
||||
}
|
||||
@@ -334,7 +334,6 @@ async fn process_source(client: &reqwest::Client, app_config: Arc<AppConfig>, so
|
||||
let (mut playlistgroups, mut error_list) = playlist_download_from_input(client, &app_config, input).await;
|
||||
let (tvguide, mut tvguide_errors) = if error_list.is_empty() {
|
||||
let working_dir = &app_config.config.load().working_dir;
|
||||
|
||||
epg::get_xmltv(client, input, working_dir).await
|
||||
} else {
|
||||
(None, vec![])
|
||||
|
||||
Reference in New Issue
Block a user