aboutsummaryrefslogtreecommitdiff
path: root/import
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-02-19 18:06:21 +0100
committermetamuffin <metamuffin@disroot.org>2026-02-19 18:06:21 +0100
commit962309ddcb033e0032258d6badebb90415a34e3d (patch)
tree64a0bde3c6ddfefa334c71314d699b9a00fad0bf /import
parentc545bdbc10ae5a55f991e03260e6a74b92a75fda (diff)
downloadjellything-962309ddcb033e0032258d6badebb90415a34e3d.tar
jellything-962309ddcb033e0032258d6badebb90415a34e3d.tar.bz2
jellything-962309ddcb033e0032258d6badebb90415a34e3d.tar.zst
omdb import plugin
Diffstat (limited to 'import')
-rw-r--r--import/Cargo.toml2
-rw-r--r--import/src/lib.rs2
-rw-r--r--import/src/plugins/infojson.rs4
-rw-r--r--import/src/plugins/misc.rs2
-rw-r--r--import/src/plugins/mod.rs4
-rw-r--r--import/src/plugins/omdb.rs170
6 files changed, 179 insertions, 5 deletions
diff --git a/import/Cargo.toml b/import/Cargo.toml
index c62ef9e..ad5a39d 100644
--- a/import/Cargo.toml
+++ b/import/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "jellyimport"
-version = "0.1.0"
+version = "0.2.0"
edition = "2024"
[dependencies]
diff --git a/import/src/lib.rs b/import/src/lib.rs
index 441fd0a..9129a70 100644
--- a/import/src/lib.rs
+++ b/import/src/lib.rs
@@ -5,9 +5,9 @@
*/
#![feature(duration_constants)]
+pub mod helpers;
pub mod plugins;
pub mod reporting;
-pub mod helpers;
use crate::{
plugins::{ImportPlugin, PluginContext, infojson::is_info_json, init_plugins, misc::is_cover},
diff --git a/import/src/plugins/infojson.rs b/import/src/plugins/infojson.rs
index abfadc0..3928e7a 100644
--- a/import/src/plugins/infojson.rs
+++ b/import/src/plugins/infojson.rs
@@ -9,7 +9,7 @@ use chrono::{Utc, format::Parsed};
use jellycommon::*;
use jellydb::RowNum;
use jellyremuxer::matroska::{AttachedFile, Segment};
-use log::info;
+use log::debug;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, fs::File, io::BufReader, path::Path};
@@ -171,7 +171,7 @@ impl ImportPlugin for Infojson {
return Ok(());
}
- info!("import channel info.json at {path:?}");
+ debug!("import channel info.json at {path:?}");
let data = serde_json::from_reader::<_, YVideo>(BufReader::new(File::open(path)?))?;
let title = clean_uploader_name(&data.title);
diff --git a/import/src/plugins/misc.rs b/import/src/plugins/misc.rs
index e71106a..1699f6d 100644
--- a/import/src/plugins/misc.rs
+++ b/import/src/plugins/misc.rs
@@ -34,10 +34,10 @@ impl ImportPlugin for ImageFiles {
"backdrop.jpeg" | "backdrop.webp" | "backdrop.png" => PICT_BACKDROP,
_ => return Ok(()),
};
- info!("import {slot} at {path:?}");
let asset = ct.ic.cache.store(
format!("media/literal/{}-{slot}.image", HashKey(path)),
|| {
+ info!("import {slot} at {path:?}");
let mut data = Vec::new();
File::open(path)?.read_to_end(&mut data)?;
Ok(data)
diff --git a/import/src/plugins/mod.rs b/import/src/plugins/mod.rs
index a948c48..60bf09b 100644
--- a/import/src/plugins/mod.rs
+++ b/import/src/plugins/mod.rs
@@ -8,6 +8,7 @@ pub mod infojson;
pub mod media_info;
pub mod misc;
pub mod musicbrainz;
+pub mod omdb;
pub mod tags;
pub mod tmdb;
pub mod trakt;
@@ -74,6 +75,9 @@ pub fn init_plugins(secrets: &ApiSecrets) -> Vec<Box<dyn ImportPlugin>> {
if let Some(api_key) = &secrets.tmdb {
plugins.push(Box::new(tmdb::Tmdb::new(&api_key))); // deps: trakt
}
+ if let Some(api_key) = &secrets.omdb {
+ plugins.push(Box::new(omdb::Omdb::new(&api_key))); // deps: trakt
+ }
if let Some(api_key) = &secrets.acoustid {
plugins.push(Box::new(acoustid::AcoustID::new(&api_key)));
}
diff --git a/import/src/plugins/omdb.rs b/import/src/plugins/omdb.rs
new file mode 100644
index 0000000..bb58c6b
--- /dev/null
+++ b/import/src/plugins/omdb.rs
@@ -0,0 +1,170 @@
+/*
+ This file is part of jellything (https://codeberg.org/metamuffin/jellything)
+ which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
+ Copyright (C) 2026 metamuffin <metamuffin.org>
+*/
+
+use std::sync::Arc;
+
+use anyhow::{Context, Result, anyhow};
+use jellycache::Cache;
+use jellycommon::{
+ IDENT_IMDB, NO_IDENTIFIERS, NO_RATINGS, RTYP_IMDB, RTYP_METACRITIC, RTYP_ROTTEN_TOMATOES,
+};
+use jellydb::RowNum;
+use log::info;
+use reqwest::{
+ Client, ClientBuilder,
+ header::{HeaderMap, HeaderName, HeaderValue},
+};
+use serde::{Deserialize, Serialize};
+use tokio::runtime::Handle;
+
+use crate::{
+ USER_AGENT,
+ plugins::{ImportPlugin, PluginContext, PluginInfo},
+};
+
+pub struct Omdb {
+ client: Client,
+ key: String,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+#[serde(rename_all = "PascalCase")]
+struct OmdbMovie {
+ title: String,
+ year: String,
+ rated: String,
+ released: String,
+ runtime: String,
+ director: String,
+ writer: String,
+ actors: String,
+ plot: String,
+ language: String,
+ country: String,
+ awards: String,
+ poster: String,
+ ratings: Vec<OmdbRating>,
+ metascore: String,
+ #[serde(rename = "imdbRating")]
+ imdb_rating: String,
+ #[serde(rename = "imdbVotes")]
+ imdb_votes: String,
+ #[serde(rename = "imdbID")]
+ imdb_id: String,
+ r#type: String,
+ #[serde(rename = "DVD")]
+ dvd: Option<String>,
+ box_office: Option<String>,
+ production: Option<String>,
+ website: Option<String>,
+ response: String,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+#[serde(rename_all = "PascalCase")]
+struct OmdbRating {
+ source: String,
+ value: String,
+}
+
+impl Omdb {
+ pub fn new(api_key: &str) -> Self {
+ let client = ClientBuilder::new()
+ .default_headers(HeaderMap::from_iter([
+ (
+ HeaderName::from_static("accept"),
+ HeaderValue::from_static("application/json"),
+ ),
+ (
+ HeaderName::from_static("user-agent"),
+ HeaderValue::from_static(USER_AGENT),
+ ),
+ ]))
+ .build()
+ .unwrap();
+ Self {
+ client,
+ key: api_key.to_owned(),
+ }
+ }
+ fn lookup(&self, cache: &Cache, id: &str, rt: &Handle) -> Result<Arc<OmdbMovie>> {
+ cache
+ .cache_memory(&format!("ext/omdb/movie/{id}.json"), move || {
+ rt.block_on(async {
+ info!("lookup {id}");
+ let res = self
+ .client
+ .get(format!(
+ "http://www.omdbapi.com/?apikey={}&i={id}",
+ self.key
+ ))
+ .send()
+ .await?
+ .error_for_status()?;
+ Ok(res.json().await?)
+ })
+ })
+ .context("omdb lookup")
+ }
+}
+
+impl ImportPlugin for Omdb {
+ fn info(&self) -> PluginInfo {
+ PluginInfo {
+ name: "omdb",
+ handle_process: true,
+ ..Default::default()
+ }
+ }
+ fn process(&self, ct: &PluginContext, node: RowNum) -> Result<()> {
+ let data = ct.ic.get_node(node)?.unwrap();
+ let data = data.as_object();
+
+ let Some(id) = data.get(NO_IDENTIFIERS).unwrap_or_default().get(IDENT_IMDB) else {
+ return Ok(());
+ };
+
+ let entry = self.lookup(&ct.ic.cache, id, ct.rt)?;
+
+ let imdb = match entry.imdb_rating.as_str() {
+ "N/A" => None,
+ v => Some(v.parse::<f64>().context("parse imdb rating")?),
+ };
+ let metascore = match entry.metascore.as_str() {
+ "N/A" => None,
+ v => Some(v.parse::<f64>().context("parse metascore rating")?),
+ };
+ let rotten_tomatoes = entry
+ .ratings
+ .iter()
+ .find(|r| r.source == "Rotten Tomatoes")
+ .map(|r| {
+ r.value
+ .strip_suffix("%")
+ .ok_or(anyhow!("% missing"))?
+ .parse::<f64>()
+ .context("parse rotten tomatoes rating")
+ })
+ .transpose()?;
+
+ ct.ic.update_node(node, |mut node| {
+ for (typ, val) in [
+ (RTYP_METACRITIC, metascore),
+ (RTYP_IMDB, imdb),
+ (RTYP_ROTTEN_TOMATOES, rotten_tomatoes),
+ ] {
+ if let Some(x) = val {
+ node = node
+ .as_object()
+ .update(NO_RATINGS, |rts| rts.insert(typ, x));
+ }
+ }
+ node
+ })?;
+
+ Ok(())
+ }
+}