aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock7
-rw-r--r--common/src/config.rs11
-rw-r--r--import/Cargo.toml2
-rw-r--r--import/src/lib.rs10
-rw-r--r--import/src/trakt.rs131
5 files changed, 148 insertions, 13 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 8d9ff8d..806d9c8 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1374,6 +1374,7 @@ dependencies = [
"serde_json",
"serde_yaml",
"tokio",
+ "urlencoding",
]
[[package]]
@@ -3163,6 +3164,12 @@ dependencies = [
]
[[package]]
+name = "urlencoding"
+version = "2.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
+
+[[package]]
name = "utf8parse"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/common/src/config.rs b/common/src/config.rs
index 1c1439e..4e90cf0 100644
--- a/common/src/config.rs
+++ b/common/src/config.rs
@@ -5,7 +5,6 @@
*/
use crate::{jhls::EncodingProfile, user::PermissionSet};
-use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, path::PathBuf};
@@ -53,15 +52,7 @@ pub struct ApiSecrets {
pub imdb: Option<String>,
pub omdb: Option<String>,
pub fanart_tv: Option<String>,
- pub trakt: Option<TraktApiSecrets>,
-}
-#[derive(Serialize, Deserialize, Debug)]
-pub struct TraktApiSecrets {
- pub client_id: String,
- pub client_secret: String,
- pub expire: DateTime<Utc>,
- pub access_token: String,
- pub refresh_token: String,
+ pub trakt: Option<String>,
}
mod default {
diff --git a/import/Cargo.toml b/import/Cargo.toml
index 6f6c474..4096dc8 100644
--- a/import/Cargo.toml
+++ b/import/Cargo.toml
@@ -13,12 +13,12 @@ jellyremuxer = { path = "../remuxer" }
log = { workspace = true }
anyhow = "1.0.75"
reqwest = { version = "0.11.22", features = ["json"] }
+urlencoding = "2.1.3"
bincode = { version = "2.0.0-rc.3", features = ["derive"] }
serde = { version = "1.0.193", features = ["derive"] }
serde_json = "1.0.108"
serde_yaml = "0.9.27"
-# bincode = { version = "2.0.0-rc.3", features = ["serde"] }
async-recursion = "1.0.5"
futures = "0.3.29"
diff --git a/import/src/lib.rs b/import/src/lib.rs
index 1d8efd8..ab74ecb 100644
--- a/import/src/lib.rs
+++ b/import/src/lib.rs
@@ -6,13 +6,17 @@
#![feature(lazy_cell)]
pub mod infojson;
pub mod tmdb;
+pub mod trakt;
use crate::tmdb::TmdbKind;
use anyhow::{anyhow, bail, Context, Ok};
use async_recursion::async_recursion;
use futures::{executor::block_on, stream::FuturesUnordered, StreamExt};
use jellybase::{
- cache::{async_cache_file, cache_memory}, database::{DataAcid, ReadableTable, Ser, T_NODE, T_NODE_IMPORT}, federation::Federation, AssetLocationExt, CONF, SECRETS
+ cache::{async_cache_file, cache_memory},
+ database::{DataAcid, ReadableTable, Ser, T_NODE, T_NODE_IMPORT},
+ federation::Federation,
+ AssetLocationExt, CONF, SECRETS,
};
use jellyclient::Session;
use jellycommon::{
@@ -238,7 +242,9 @@ async fn process_source(
ImportSource::Override(n) => insert_node(&id, n)?,
ImportSource::Tmdb { id: tid } => {
info!("tmdb lookup {id}");
- let key = SECRETS.api.tmdb
+ let key = SECRETS
+ .api
+ .tmdb
.as_ref()
.ok_or(anyhow!("no tmdb api key"))?;
diff --git a/import/src/trakt.rs b/import/src/trakt.rs
new file mode 100644
index 0000000..e142eb6
--- /dev/null
+++ b/import/src/trakt.rs
@@ -0,0 +1,131 @@
+use reqwest::{
+ header::{HeaderMap, HeaderName, HeaderValue},
+ Client, ClientBuilder,
+};
+use serde::{Deserialize, Serialize};
+
+pub struct Trakt {
+ client: Client,
+}
+
+impl Trakt {
+ pub fn new(api_key: &str) -> Self {
+ let client = ClientBuilder::new()
+ .default_headers(HeaderMap::from_iter([
+ (
+ HeaderName::from_static("trakt-api-key"),
+ HeaderValue::from_str(api_key).unwrap(),
+ ),
+ (
+ HeaderName::from_static("trakt-api-version"),
+ HeaderValue::from_static("2"),
+ ),
+ (
+ HeaderName::from_static("content-type"),
+ HeaderValue::from_static("application/json"),
+ ),
+ ]))
+ .build()
+ .unwrap();
+ Self { client }
+ }
+
+ pub async fn search(
+ &self,
+ types: &[TraktKind],
+ query: &str,
+ extended: bool,
+ ) -> anyhow::Result<Vec<TraktSearchResult>> {
+ let res = self
+ .client
+ .get(format!(
+ "https://api.trakt.tv/search/{}?query={}{}",
+ types
+ .iter()
+ .map(|t| serde_json::to_string(t).unwrap())
+ .collect::<Vec<_>>()
+ .join(","),
+ urlencoding::encode(query),
+ optext(extended)
+ ))
+ .send()
+ .await?;
+ Ok(res.json().await?)
+ }
+}
+
+fn optext(extended: bool) -> &'static str {
+ if extended {
+ "&extended=full"
+ } else {
+ ""
+ }
+}
+
+#[derive(Debug, Serialize, Deserialize, Clone, Copy)]
+#[serde(rename_all = "snake_case")]
+pub enum TraktKind {
+ Movie,
+ Show,
+ Season,
+ Episode,
+ Person,
+ User,
+}
+
+impl TraktKind {
+ pub fn singular(self) -> &'static str {
+ match self {
+ TraktKind::Movie => "movie",
+ TraktKind::Show => "show",
+ TraktKind::Season => "season",
+ TraktKind::Episode => "episode",
+ TraktKind::Person => "person",
+ TraktKind::User => "user",
+ }
+ }
+ pub fn plural(self) -> &'static str {
+ match self {
+ TraktKind::Movie => "movies",
+ TraktKind::Show => "shows",
+ TraktKind::Season => "seasons",
+ TraktKind::Episode => "episodes",
+ TraktKind::Person => "people",
+ TraktKind::User => "user", // //! not used in API
+ }
+ }
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct TraktSearchResult {
+ r#type: TraktKind,
+ score: f64,
+ inner: TraktKindObject,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+#[serde(rename_all = "snake_case")]
+pub enum TraktKindObject {
+ Movie(TraktMediaObject),
+ Show(TraktMediaObject),
+ Season(TraktMediaObject),
+ Episode(TraktMediaObject),
+ Person(TraktMediaObject),
+ User(TraktMediaObject),
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct TraktMediaObject {
+ title: String,
+ year: Option<u32>,
+ ids: TraktMediaObjectIds,
+}
+
+#[derive(Debug, Serialize, Deserialize)]
+pub struct TraktMediaObjectIds {
+ trakt: u64,
+ slug: String,
+
+ imdb: Option<String>,
+ tmdb: Option<u64>,
+}