aboutsummaryrefslogtreecommitdiff
path: root/import
diff options
context:
space:
mode:
Diffstat (limited to 'import')
-rw-r--r--import/Cargo.toml4
-rw-r--r--import/asset_token/Cargo.toml20
-rw-r--r--import/asset_token/src/lib.rs105
-rw-r--r--import/src/infojson.rs2
-rw-r--r--import/src/lib.rs16
-rw-r--r--import/src/tmdb.rs4
-rw-r--r--import/src/trakt.rs2
7 files changed, 140 insertions, 13 deletions
diff --git a/import/Cargo.toml b/import/Cargo.toml
index 112df40..d0b16b4 100644
--- a/import/Cargo.toml
+++ b/import/Cargo.toml
@@ -4,10 +4,12 @@ version = "0.1.0"
edition = "2021"
[dependencies]
-jellybase = { path = "../base" }
jellyremuxer = { path = "../remuxer" }
jellycache = { path = "../cache" }
+jellycommon = { path = "../common" }
+jellydb = { path = "../database" }
jellyimport-fallback-generator = { path = "fallback_generator" }
+jellyimport-asset-token = { path = "asset_token" }
rayon = "1.10.0"
crossbeam-channel = "0.5.14"
diff --git a/import/asset_token/Cargo.toml b/import/asset_token/Cargo.toml
new file mode 100644
index 0000000..95615ce
--- /dev/null
+++ b/import/asset_token/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "jellyimport-asset-token"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+jellycommon = { path = "../../common" }
+jellycache = { path = "../../cache" }
+serde = { version = "1.0.217", features = ["derive"] }
+serde_yaml = "0.9.34"
+log = { workspace = true }
+sha2 = "0.10.8"
+base64 = "0.22.1"
+tokio = { workspace = true }
+anyhow = "1.0.95"
+bincode = "2.0.0-rc.3"
+rand = "0.9.0"
+serde_json = "1.0.138"
+aes-gcm-siv = "0.11.1"
+humansize = "2.1.3"
diff --git a/import/asset_token/src/lib.rs b/import/asset_token/src/lib.rs
new file mode 100644
index 0000000..87ea261
--- /dev/null
+++ b/import/asset_token/src/lib.rs
@@ -0,0 +1,105 @@
+/*
+ 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) 2025 metamuffin <metamuffin.org>
+*/
+pub mod assetfed;
+
+use aes_gcm_siv::{
+ aead::{generic_array::GenericArray, Aead},
+ Aes256GcmSiv, KeyInit,
+};
+use anyhow::{anyhow, bail, Context};
+use base64::Engine;
+use bincode::{Decode, Encode};
+use jellycache::CachePath;
+pub use jellycommon as common;
+use jellycommon::{Asset, LocalTrack};
+use log::warn;
+use serde::{Deserialize, Serialize};
+use std::sync::Mutex;
+use std::{path::PathBuf, sync::LazyLock};
+
+#[rustfmt::skip]
+#[derive(Debug, Deserialize, Serialize, Default)]
+pub struct Config {
+ asset_key: Option<String>,
+}
+
+pub static CONF_PRELOAD: Mutex<Option<Config>> = Mutex::new(None);
+static CONF: LazyLock<Config> = LazyLock::new(|| {
+ CONF_PRELOAD
+ .lock()
+ .unwrap()
+ .take()
+ .expect("cache config not preloaded. logic error")
+});
+
+const VERSION: u32 = 3;
+
+static ASSET_KEY: LazyLock<Aes256GcmSiv> = LazyLock::new(|| {
+ if let Some(sk) = &CONF.asset_key {
+ let r = base64::engine::general_purpose::STANDARD
+ .decode(sk)
+ .expect("key invalid; should be valid base64");
+ aes_gcm_siv::Aes256GcmSiv::new_from_slice(&r)
+ .expect("key has the wrong length; should be 32 bytes")
+ } else {
+ warn!("session_key not configured; generating a random one.");
+ aes_gcm_siv::Aes256GcmSiv::new_from_slice(&[(); 32].map(|_| rand::random())).unwrap()
+ }
+});
+
+#[derive(Debug, Encode, Decode, Serialize)]
+pub enum AssetInner {
+ Federated { host: String, asset: Vec<u8> },
+ Cache(CachePath),
+ Assets(PathBuf),
+ Media(PathBuf),
+ LocalTrack(LocalTrack),
+}
+
+impl AssetInner {
+ pub fn ser(&self) -> Asset {
+ let mut plaintext = Vec::new();
+ plaintext.extend(u32::to_le_bytes(VERSION));
+ plaintext.extend(bincode::encode_to_vec(self, bincode::config::standard()).unwrap());
+
+ while plaintext.len() % 16 == 0 {
+ plaintext.push(0);
+ }
+
+ let nonce = [(); 12].map(|_| rand::random());
+ let mut ciphertext = ASSET_KEY
+ .encrypt(&GenericArray::from(nonce), plaintext.as_slice())
+ .unwrap();
+ ciphertext.extend(nonce);
+
+ Asset(base64::engine::general_purpose::URL_SAFE.encode(&ciphertext))
+ }
+ pub fn deser(s: &str) -> anyhow::Result<Self> {
+ let ciphertext = base64::engine::general_purpose::URL_SAFE.decode(s)?;
+ let (ciphertext, nonce) = ciphertext.split_at(ciphertext.len() - 12);
+ let plaintext = ASSET_KEY
+ .decrypt(nonce.into(), ciphertext)
+ .map_err(|_| anyhow!("asset token decrypt failed"))?;
+
+ let version = u32::from_le_bytes(plaintext[0..4].try_into().unwrap());
+ if version != VERSION {
+ bail!("asset token version mismatch");
+ }
+
+ let (data, _): (AssetInner, _) =
+ bincode::decode_from_slice(&plaintext[4..], bincode::config::standard())
+ .context("asset token has invalid format")?;
+ Ok(data)
+ }
+
+ /// Returns `true` if the asset inner is [`Federated`].
+ ///
+ /// [`Federated`]: AssetInner::Federated
+ #[must_use]
+ pub fn is_federated(&self) -> bool {
+ matches!(self, Self::Federated { .. })
+ }
+}
diff --git a/import/src/infojson.rs b/import/src/infojson.rs
index 1efbae9..e0ebc43 100644
--- a/import/src/infojson.rs
+++ b/import/src/infojson.rs
@@ -5,7 +5,7 @@
*/
use anyhow::Context;
use bincode::{Decode, Encode};
-use jellybase::common::chrono::{format::Parsed, Utc};
+use jellycommon::chrono::{format::Parsed, Utc};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
diff --git a/import/src/lib.rs b/import/src/lib.rs
index 784b717..426a96a 100644
--- a/import/src/lib.rs
+++ b/import/src/lib.rs
@@ -14,18 +14,18 @@ pub mod vgmdb;
pub mod wikidata;
pub mod wikimedia_commons;
+use jellydb::Database;
+pub use jellyimport_asset_token as asset_token;
+use jellyimport_asset_token::AssetInner;
+
use acoustid::{acoustid_fingerprint, AcoustID};
use anyhow::{anyhow, bail, Context, Result};
use infojson::YVideo;
-use jellybase::{
- assetfed::AssetInner,
- common::{
- Appearance, Chapter, LocalTrack, MediaInfo, Node, NodeID, NodeKind, ObjectIds, PeopleGroup,
- Person, Rating, SourceTrack, SourceTrackKind, TmdbKind, TrackSource, TraktKind, Visibility,
- },
- database::Database,
-};
use jellycache::cache_file;
+use jellycommon::{
+ Appearance, Chapter, LocalTrack, MediaInfo, Node, NodeID, NodeKind, ObjectIds, PeopleGroup,
+ Person, Rating, SourceTrack, SourceTrackKind, TmdbKind, TrackSource, TraktKind, Visibility,
+};
use jellyimport_fallback_generator::generate_fallback;
use jellyremuxer::metadata::checked_matroska_metadata;
use log::info;
diff --git a/import/src/tmdb.rs b/import/src/tmdb.rs
index dff0e95..ceb1650 100644
--- a/import/src/tmdb.rs
+++ b/import/src/tmdb.rs
@@ -6,11 +6,11 @@
use crate::USER_AGENT;
use anyhow::{anyhow, bail, Context};
use bincode::{Decode, Encode};
-use jellybase::common::{
+use jellycache::{async_cache_file, async_cache_memory, CachePath};
+use jellycommon::{
chrono::{format::Parsed, Utc},
TmdbKind,
};
-use jellycache::{async_cache_file, async_cache_memory, CachePath};
use log::info;
use reqwest::{
header::{HeaderMap, HeaderName, HeaderValue},
diff --git a/import/src/trakt.rs b/import/src/trakt.rs
index 434a3a0..f25fa9e 100644
--- a/import/src/trakt.rs
+++ b/import/src/trakt.rs
@@ -6,8 +6,8 @@
use crate::USER_AGENT;
use anyhow::Context;
use bincode::{Decode, Encode};
-use jellybase::common::{Appearance, ObjectIds, PeopleGroup, Person, TraktKind};
use jellycache::async_cache_memory;
+use jellycommon::{Appearance, ObjectIds, PeopleGroup, Person, TraktKind};
use log::info;
use reqwest::{
header::{HeaderMap, HeaderName, HeaderValue},