From b14f5d3f194aa96d32fa4ced2e7cc5fc705ef22d Mon Sep 17 00:00:00 2001 From: metamuffin Date: Fri, 22 Dec 2023 16:01:58 +0100 Subject: rework import system pt. 6: tmdb import --- import/Cargo.toml | 2 +- import/src/lib.rs | 141 ++++++++++++++--------------------------------------- import/src/tmdb.rs | 47 ++++++++++++------ 3 files changed, 71 insertions(+), 119 deletions(-) (limited to 'import') diff --git a/import/Cargo.toml b/import/Cargo.toml index a54967c..91a7662 100644 --- a/import/Cargo.toml +++ b/import/Cargo.toml @@ -12,7 +12,7 @@ jellyremuxer = { path = "../remuxer" } log = { workspace = true } anyhow = "1.0.75" -reqwest = { version = "0.11.22", features = ["blocking", "json"] } +reqwest = { version = "0.11.22", features = ["json"] } serde = { version = "1.0.193", features = ["derive"] } serde_json = "1.0.108" diff --git a/import/src/lib.rs b/import/src/lib.rs index e69e203..3f14960 100644 --- a/import/src/lib.rs +++ b/import/src/lib.rs @@ -28,13 +28,16 @@ use std::{ cmp::Ordering, ffi::OsStr, fs::File, - io::{BufReader, Write}, + io::BufReader, os::unix::prelude::OsStrExt, path::{Path, PathBuf}, sync::{Arc, LazyLock}, }; +use tmdb::tmdb_image; use tokio::{io::AsyncWriteExt, sync::Semaphore, task::spawn_blocking}; +use crate::tmdb::TmdbKind; + static IMPORT_SEM: LazyLock = LazyLock::new(|| Semaphore::new(1)); pub async fn import(db: &Database, fed: &Federation) -> anyhow::Result<()> { @@ -183,8 +186,39 @@ async fn process_source( }; match s { ImportSource::Override(n) => insert_node(&id, n)?, - ImportSource::Tmdb { id } => { - todo!() + ImportSource::Tmdb { id: tid } => { + let key = CONF + .tmdb_api_key + .as_ref() + .ok_or(anyhow!("no tmdb api key"))?; + let details = tmdb::tmdb_details(TmdbKind::Movie, tid, key).await?; + + let mut node = Node::default(); + + if let Some(poster) = details.poster_path { + node.private.poster = Some( + async_cache_file( + &["tmdb-asset", "poster", &format!("{tid}")], + |mut f| async move { Ok(f.write_all(&tmdb_image(&poster).await?).await?) }, + ) + .await?, + ); + } + if let Some(backdrop) = details.backdrop_path { + node.private.backdrop = Some( + async_cache_file( + &["tmdb-asset", "backdrop", &format!("{tid}")], + |mut f| async move { Ok(f.write_all(&tmdb_image(&backdrop).await?).await?) }, + ) + .await?, + ); + } + + node.public.tagline = details.tagline; + node.public.title = details.title; + node.public.description = Some(details.overview); + + insert_node(&id, node)?; } ImportSource::Media { location, .. } => { // TODO use ignore options @@ -314,107 +348,6 @@ fn merge_node(x: Node, y: Node) -> Node { } } -// #[async_recursion] -// pub async fn import_path( -// path: PathBuf, -// db: &Database, -// fed: &Federation, -// mut node_path: Vec, -// ) -> anyhow::Result<(Vec, usize)> { -// if path.is_dir() { -// let mpath = path.join("directory.json"); -// let children_paths = path.read_dir()?.map(Result::unwrap).filter_map(|e| { -// if e.path().extension() == Some(&OsStr::from_bytes(b"jelly")) -// || e.metadata().unwrap().is_dir() -// { -// Some(e.path()) -// } else { -// None -// } -// }); -// let identifier = if mpath.exists() { -// path.file_name().unwrap().to_str().unwrap().to_string() -// } else { -// node_path -// .last() -// .cloned() -// .ok_or(anyhow!("non-root node requires parent"))? -// }; - -// node_path.push(identifier.clone()); -// let mut all: FuturesUnordered<_> = children_paths -// .into_iter() -// .map(|p| import_path(p.clone(), db, fed, node_path.clone()).map_err(|e| (p, e))) -// .collect(); -// node_path.pop(); // we will set the dirs path later and need it to not be included - -// let mut children_ids = Vec::new(); -// let mut errs = 0; -// while let Some(k) = all.next().await { -// match k { -// core::result::Result::Ok((els, errs2)) => { -// errs += errs2; -// children_ids.extend(els) -// } -// Err((p, e)) => { -// errs += 1; -// error!("import of {p:?} failed: {e:?}") -// } -// } -// } -// if mpath.exists() { -// let mut node: Node = -// serde_json::from_reader(File::open(mpath).context("metadata missing")?)?; - -// node.public.children = children_ids; -// node.public.path = node_path; -// node.public.id = Some(identifier.to_owned()); -// info!("adding {identifier}"); -// db.node.insert(&identifier, &node)?; -// Ok((vec![identifier], errs)) -// } else { -// Ok((children_ids, errs)) -// } -// } else if path.is_file() { -// info!("loading {path:?}"); -// let datafile = File::open(path.clone()).context("cant load metadata")?; -// let mut node: Node = serde_json::from_reader(datafile).context("invalid metadata")?; -// let identifier = node.private.id.clone().unwrap_or_else(|| { -// path.file_name() -// .unwrap() -// .to_str() -// .unwrap() -// .strip_suffix(".json") -// .unwrap() -// .to_string() -// }); - -// let idents = if let Some(io) = node.private.import.take() { -// let session = fed -// .get_session(&io.host) -// .await -// .context("creating session")?; - -// import_remote(io, db, &session, identifier.clone(), node_path) -// .await -// .context("federated import")? -// } else { -// debug!("adding {identifier}"); -// node.public.path = node_path; -// node.public.id = Some(identifier.to_owned()); -// let did_insert = db.node.insert(&identifier, &node)?.is_none(); -// if did_insert { -// vec![identifier] -// } else { -// vec![] -// } -// }; -// Ok((idents, 0)) -// } else { -// bail!("did somebody really put a fifo or socket in the library?!") -// } -// } - static SEM_REMOTE_IMPORT: LazyLock = LazyLock::new(|| Semaphore::new(16)); #[async_recursion] diff --git a/import/src/tmdb.rs b/import/src/tmdb.rs index c38d50e..3780524 100644 --- a/import/src/tmdb.rs +++ b/import/src/tmdb.rs @@ -72,28 +72,47 @@ pub struct TmdbProductionCompany { pub logo_path: Option, } -pub fn tmdb_search(kind: &str, query: &str, key: &str) -> anyhow::Result { +#[derive(Debug, Clone, Copy)] +pub enum TmdbKind { + Tv, + Movie, +} +impl TmdbKind { + pub fn as_str(&self) -> &'static str { + match self { + TmdbKind::Tv => "tv", + TmdbKind::Movie => "movie", + } + } +} + +pub async fn tmdb_search(kind: TmdbKind, query: &str, key: &str) -> anyhow::Result { info!("searching tmdb: {query:?}"); - Ok(reqwest::blocking::get(&format!( - "https://api.themoviedb.org/3/search/{kind}?query={}&api_key={key}", + Ok(reqwest::get(&format!( + "https://api.themoviedb.org/3/search/{}?query={}&api_key={key}", + kind.as_str(), query.replace(" ", "+") - ))? - .json::()?) + )) + .await? + .json::() + .await?) } -pub fn tmdb_details(kind: &str, id: u64, key: &str) -> anyhow::Result { +pub async fn tmdb_details(kind: TmdbKind, id: u64, key: &str) -> anyhow::Result { info!("fetching details: {id:?}"); - Ok(reqwest::blocking::get(&format!( - "https://api.themoviedb.org/3/{kind}/{id}?api_key={key}" - ))? - .json()?) + Ok(reqwest::get(&format!( + "https://api.themoviedb.org/3/{}/{id}?api_key={key}", + kind.as_str() + )) + .await? + .json() + .await?) } -pub fn tmdb_image(path: &str, out: &mut impl Write) -> anyhow::Result<()> { +pub async fn tmdb_image(path: &str) -> anyhow::Result> { info!("downloading image {path:?}"); - let mut res = reqwest::blocking::get(&format!("https://image.tmdb.org/t/p/original{path}"))?; - res.copy_to(out)?; - Ok(()) + let res = reqwest::get(&format!("https://image.tmdb.org/t/p/original{path}")).await?; + Ok(res.bytes().await?.to_vec()) } pub fn parse_release_date(d: &str) -> anyhow::Result> { -- cgit v1.2.3-70-g09d2