aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock174
-rw-r--r--Cargo.toml1
-rw-r--r--import/Cargo.toml1
-rw-r--r--import/fallback_generator/Cargo.toml10
-rw-r--r--import/fallback_generator/src/lib.rs103
-rw-r--r--import/src/lib.rs21
6 files changed, 305 insertions, 5 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 94bead7..dfd9174 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3,6 +3,22 @@
version = 4
[[package]]
+name = "ab_glyph"
+version = "0.2.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0"
+dependencies = [
+ "ab_glyph_rasterizer",
+ "owned_ttf_parser",
+]
+
+[[package]]
+name = "ab_glyph_rasterizer"
+version = "0.1.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046"
+
+[[package]]
name = "addr2line"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -155,9 +171,18 @@ dependencies = [
[[package]]
name = "anyhow"
-version = "1.0.95"
+version = "1.0.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04"
+checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
+
+[[package]]
+name = "approx"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
+dependencies = [
+ "num-traits",
+]
[[package]]
name = "arbitrary"
@@ -1567,9 +1592,9 @@ dependencies = [
[[package]]
name = "image"
-version = "0.25.5"
+version = "0.25.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b"
+checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a"
dependencies = [
"bytemuck",
"byteorder-lite",
@@ -1599,6 +1624,24 @@ dependencies = [
]
[[package]]
+name = "imageproc"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2393fb7808960751a52e8a154f67e7dd3f8a2ef9bd80d1553078a7b4e8ed3f0d"
+dependencies = [
+ "ab_glyph",
+ "approx",
+ "getrandom 0.2.15",
+ "image",
+ "itertools",
+ "nalgebra",
+ "num",
+ "rand 0.8.5",
+ "rand_distr",
+ "rayon",
+]
+
+[[package]]
name = "imgref"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1764,6 +1807,7 @@ dependencies = [
"futures",
"jellybase",
"jellyclient",
+ "jellyimport-fallback-generator",
"jellyremuxer",
"log",
"rayon",
@@ -1777,6 +1821,16 @@ dependencies = [
]
[[package]]
+name = "jellyimport-fallback-generator"
+version = "0.1.0"
+dependencies = [
+ "ab_glyph",
+ "anyhow",
+ "image",
+ "imageproc",
+]
+
+[[package]]
name = "jellymatroska"
version = "0.1.0"
dependencies = [
@@ -2093,6 +2147,16 @@ dependencies = [
]
[[package]]
+name = "matrixmultiply"
+version = "0.3.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9380b911e3e96d10c1f415da0876389aaf1b56759054eeb0de7df940c456ba1a"
+dependencies = [
+ "autocfg",
+ "rawpointer",
+]
+
+[[package]]
name = "maybe-rayon"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2192,6 +2256,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2195bf6aa996a481483b29d62a7663eed3fe39600c460e323f8ff41e90bdd89b"
[[package]]
+name = "nalgebra"
+version = "0.32.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7b5c17de023a86f59ed79891b2e5d5a94c705dbe904a5b5c9c952ea6221b03e4"
+dependencies = [
+ "approx",
+ "matrixmultiply",
+ "num-complex",
+ "num-rational",
+ "num-traits",
+ "simba",
+ "typenum",
+]
+
+[[package]]
name = "nasm-rs"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2233,6 +2312,20 @@ dependencies = [
]
[[package]]
+name = "num"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23"
+dependencies = [
+ "num-bigint",
+ "num-complex",
+ "num-integer",
+ "num-iter",
+ "num-rational",
+ "num-traits",
+]
+
+[[package]]
name = "num-bigint"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2243,6 +2336,15 @@ dependencies = [
]
[[package]]
+name = "num-complex"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495"
+dependencies = [
+ "num-traits",
+]
+
+[[package]]
name = "num-conv"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2269,6 +2371,17 @@ dependencies = [
]
[[package]]
+name = "num-iter"
+version = "0.1.45"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf"
+dependencies = [
+ "autocfg",
+ "num-integer",
+ "num-traits",
+]
+
+[[package]]
name = "num-rational"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2339,6 +2452,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39"
[[package]]
+name = "owned_ttf_parser"
+version = "0.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4"
+dependencies = [
+ "ttf-parser",
+]
+
+[[package]]
name = "ownedbytes"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2780,6 +2902,12 @@ dependencies = [
]
[[package]]
+name = "rawpointer"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
+
+[[package]]
name = "rayon"
version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3147,6 +3275,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ea1a2d0a644769cc99faa24c3ad26b379b786fe7c36fd3c546254801650e6dd"
[[package]]
+name = "safe_arch"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "96b02de82ddbe1b636e6170c21be622223aea188ef2e139be0a5b219ec215323"
+dependencies = [
+ "bytemuck",
+]
+
+[[package]]
name = "scoped-tls"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3277,6 +3414,19 @@ dependencies = [
]
[[package]]
+name = "simba"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "061507c94fc6ab4ba1c9a0305018408e312e17c041eb63bef8aa726fa33aceae"
+dependencies = [
+ "approx",
+ "num-complex",
+ "num-traits",
+ "paste",
+ "wide",
+]
+
+[[package]]
name = "simd-adler32"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -3902,6 +4052,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b"
[[package]]
+name = "ttf-parser"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
+
+[[package]]
name = "tungstenite"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -4218,6 +4374,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
[[package]]
+name = "wide"
+version = "0.7.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "41b5576b9a81633f3e8df296ce0063042a73507636cbe956c61133dd7034ab22"
+dependencies = [
+ "bytemuck",
+ "safe_arch",
+]
+
+[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
diff --git a/Cargo.toml b/Cargo.toml
index 1a7b7b4..e351db0 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -10,6 +10,7 @@ members = [
"transcoder",
"base",
"import",
+ "import/fallback_generator",
]
resolver = "2"
diff --git a/import/Cargo.toml b/import/Cargo.toml
index 37b5a77..ad9d529 100644
--- a/import/Cargo.toml
+++ b/import/Cargo.toml
@@ -7,6 +7,7 @@ edition = "2021"
jellybase = { path = "../base" }
jellyclient = { path = "../client" }
jellyremuxer = { path = "../remuxer" }
+jellyimport-fallback-generator = { path = "fallback_generator" }
rayon = "1.10.0"
crossbeam-channel = "0.5.14"
diff --git a/import/fallback_generator/Cargo.toml b/import/fallback_generator/Cargo.toml
new file mode 100644
index 0000000..13823ba
--- /dev/null
+++ b/import/fallback_generator/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "jellyimport-fallback-generator"
+version = "0.1.0"
+edition = "2024"
+
+[dependencies]
+ab_glyph = "0.2.29"
+imageproc = "0.25.0"
+image = { version = "0.25.6", default-features = false, features = ["qoi"] }
+anyhow = "1.0.98"
diff --git a/import/fallback_generator/src/lib.rs b/import/fallback_generator/src/lib.rs
new file mode 100644
index 0000000..efb6579
--- /dev/null
+++ b/import/fallback_generator/src/lib.rs
@@ -0,0 +1,103 @@
+#![feature(random, string_remove_matches)]
+use ab_glyph::{FontRef, PxScale};
+use anyhow::Result;
+use image::{DynamicImage, ImageBuffer, ImageEncoder, Rgba, codecs::qoi::QoiEncoder};
+use imageproc::drawing::{draw_text_mut, text_size};
+use std::{
+ hash::{Hash, Hasher},
+ io::Write,
+};
+
+pub fn generate_fallback(name: &str, output: &mut dyn Write) -> Result<()> {
+ let width = 1024;
+ let height = (width * 1000) / 707;
+
+ let mut image = ImageBuffer::<Rgba<f32>, Vec<f32>>::new(width, height);
+
+ let text = name;
+ let bg = random_accent(text, 0.4);
+ image.pixels_mut().for_each(|p| *p = bg);
+
+ let font = FontRef::try_from_slice(include_bytes!(
+ "/usr/share/fonts/cantarell/Cantarell-VF.otf"
+ ))
+ .unwrap();
+ let font_bold = FontRef::try_from_slice(include_bytes!(
+ "/usr/share/fonts/TTF/OpenSans-CondensedExtraBold.ttf"
+ ))
+ .unwrap();
+
+ let mut bgtext = text.to_string();
+ bgtext.remove_matches(&[',', ' ']);
+ let bgtext = bgtext.repeat(3);
+ let scale = PxScale { x: 1000., y: 1000. };
+ let (w, h) = text_size(scale, &font, &bgtext);
+ for i in -1..4 {
+ draw_text_mut(
+ &mut image,
+ random_accent(&text, 0.6),
+ width as i32 / 2 - w as i32 / 2 + i * h as i32,
+ i * h as i32 * 2 / 3,
+ scale,
+ &font_bold,
+ &bgtext,
+ );
+ }
+
+ image.enumerate_pixels_mut().for_each(|(_x, y, p)| {
+ let f = 1. - (y as f32 / height as f32) * 0.5;
+ p.0[0] *= f;
+ p.0[1] *= f;
+ p.0[2] *= f;
+ });
+
+ let scale = PxScale { x: 200., y: 200. };
+ let (w, _h) = text_size(scale, &font, text);
+ draw_text_mut(
+ &mut image,
+ Rgba([1., 1., 1., 1.]),
+ width as i32 / 2 - w as i32 / 2,
+ height as i32 * 3 / 4,
+ scale,
+ &font,
+ text,
+ );
+
+ let image = DynamicImage::from(image).to_rgb8();
+
+ QoiEncoder::new(output).write_image(
+ image.as_raw(),
+ image.width(),
+ image.height(),
+ image::ExtendedColorType::Rgb8,
+ )?;
+ Ok(())
+}
+
+struct XorshiftHasher(u64);
+impl Hasher for XorshiftHasher {
+ fn finish(&self) -> u64 {
+ self.0
+ }
+ fn write(&mut self, bytes: &[u8]) {
+ for b in bytes {
+ self.0 = self
+ .0
+ .wrapping_add((*b as u64).wrapping_shl(24) + (*b as u64).wrapping_shl(56));
+ self.0 ^= self.0.wrapping_shl(13);
+ self.0 ^= self.0.wrapping_shr(7);
+ self.0 ^= self.0.wrapping_shl(17);
+ }
+ }
+}
+
+fn random_accent(text: &str, y: f32) -> Rgba<f32> {
+ let mut hasher = XorshiftHasher(0);
+ text.hash(&mut hasher);
+ let h = hasher.finish();
+ let mut u = (h >> 32) as u32 as f32 / u32::MAX as f32;
+ let mut v = (h) as u32 as f32 / u32::MAX as f32;
+ u *= 0.2;
+ v *= 0.2;
+ Rgba([y - u * 0.5 - v * 0.5, y + v, y + u, 1.])
+}
diff --git a/import/src/lib.rs b/import/src/lib.rs
index b93dbec..0d72da2 100644
--- a/import/src/lib.rs
+++ b/import/src/lib.rs
@@ -9,6 +9,7 @@ use anyhow::{anyhow, bail, Context, Result};
use infojson::YVideo;
use jellybase::{
assetfed::AssetInner,
+ cache::cache_file,
common::{Chapter, MediaInfo, Node, NodeID, NodeKind, Rating, SourceTrack, SourceTrackKind},
database::Database,
CONF, SECRETS,
@@ -17,6 +18,7 @@ use jellyclient::{
Appearance, LocalTrack, ObjectIds, PeopleGroup, Person, TmdbKind, TrackSource, TraktKind,
Visibility,
};
+use jellyimport_fallback_generator::generate_fallback;
use jellyremuxer::metadata::checked_matroska_metadata;
use log::info;
use musicbrainz::MusicBrainz;
@@ -702,6 +704,9 @@ fn apply_musicbrainz_recording(
.insert("musicbrainz.artist".to_string(), a.artist.id.to_string());
}
+ // // TODO proper dedup
+ // node.people.clear();
+
for rel in &rec.relations {
use musicbrainz::reltypes::*;
let a = match rel.type_id.as_str() {
@@ -757,6 +762,20 @@ fn apply_musicbrainz_recording(
jobs.push(note.to_string());
}
jobs.extend(rel.attributes.clone());
+
+ let headshot = match image_1.or(image_2) {
+ Some(x) => x,
+ None => AssetInner::Cache(cache_file(
+ "person-headshot-fallback",
+ &artist.sort_name,
+ |mut file| {
+ generate_fallback(&artist.sort_name, &mut file)?;
+ Ok(())
+ },
+ )?)
+ .ser(),
+ };
+
node.people.entry(group).or_default().push(Appearance {
jobs,
characters: vec![],
@@ -766,7 +785,7 @@ fn apply_musicbrainz_recording(
} else {
rel.target_credit.clone()
},
- headshot: image_1.or(image_2),
+ headshot: Some(headshot),
ids: ObjectIds::default(),
},
});