aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-01-30 18:34:09 +0100
committermetamuffin <metamuffin@disroot.org>2025-01-30 18:34:09 +0100
commit9d6411fd92e73c204425f8dd37dc3cf567f604e4 (patch)
treed61d3e0b6bcd803e6ccb6d01669d40a1454ec009
parentbfc5552a8eba07897c2ed626b49c085d97fdfa0d (diff)
downloadjellything-9d6411fd92e73c204425f8dd37dc3cf567f604e4.tar
jellything-9d6411fd92e73c204425f8dd37dc3cf567f604e4.tar.bz2
jellything-9d6411fd92e73c204425f8dd37dc3cf567f604e4.tar.zst
avoid transitive crate deps by re-export
-rw-r--r--Cargo.lock4
-rw-r--r--base/Cargo.toml1
-rw-r--r--base/src/lib.rs2
-rw-r--r--common/src/config.rs1
-rw-r--r--common/src/lib.rs4
-rw-r--r--import/Cargo.toml1
-rw-r--r--import/src/infojson.rs2
-rw-r--r--import/src/lib.rs44
-rw-r--r--import/src/matroska.rs2
-rw-r--r--import/src/tmdb.rs10
-rw-r--r--import/src/trakt.rs6
-rw-r--r--remuxer/Cargo.toml1
-rw-r--r--remuxer/src/extract.rs2
-rw-r--r--remuxer/src/fragment.rs2
-rw-r--r--remuxer/src/lib.rs3
-rw-r--r--remuxer/src/metadata.rs378
-rw-r--r--remuxer/src/remux.rs2
-rw-r--r--remuxer/src/seek_index.rs6
-rw-r--r--server/src/routes/external_compat.rs6
-rw-r--r--server/src/routes/ui/home.rs29
-rw-r--r--stream/Cargo.toml3
-rw-r--r--stream/src/fragment.rs13
-rw-r--r--stream/src/hls.rs10
-rw-r--r--stream/src/jhls.rs15
-rw-r--r--stream/src/lib.rs13
-rw-r--r--stream/src/webvtt.rs7
-rw-r--r--transcoder/Cargo.toml1
-rw-r--r--transcoder/src/fragment.rs6
-rw-r--r--transcoder/src/subtitles.rs2
29 files changed, 119 insertions, 457 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 55c12c8..863b2fe 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1756,7 +1756,6 @@ dependencies = [
"futures",
"jellybase",
"jellyclient",
- "jellycommon",
"log",
"rayon",
"regex",
@@ -1785,7 +1784,6 @@ dependencies = [
"anyhow",
"bincode",
"jellybase",
- "jellycommon",
"jellymatroska",
"log",
"serde",
@@ -1798,7 +1796,6 @@ version = "0.1.0"
dependencies = [
"anyhow",
"jellybase",
- "jellycommon",
"jellyremuxer",
"jellytranscoder",
"log",
@@ -1873,7 +1870,6 @@ dependencies = [
"image",
"imgref",
"jellybase",
- "jellycommon",
"jellyremuxer",
"libavif-image",
"log",
diff --git a/base/Cargo.toml b/base/Cargo.toml
index b9e47de..e897404 100644
--- a/base/Cargo.toml
+++ b/base/Cargo.toml
@@ -22,3 +22,4 @@ aes-gcm-siv = "0.11.1"
[features]
db_json = []
+rocket = ["jellycommon/rocket"]
diff --git a/base/src/lib.rs b/base/src/lib.rs
index cf28d85..cb5bae7 100644
--- a/base/src/lib.rs
+++ b/base/src/lib.rs
@@ -9,6 +9,8 @@ pub mod database;
pub mod federation;
pub mod permission;
+pub use jellycommon as common;
+
use jellycommon::config::{GlobalConfig, SecretsConfig};
use std::sync::{
atomic::{AtomicBool, Ordering},
diff --git a/common/src/config.rs b/common/src/config.rs
index 682fdd7..4ff1477 100644
--- a/common/src/config.rs
+++ b/common/src/config.rs
@@ -23,7 +23,6 @@ pub struct GlobalConfig {
#[serde(default = "default::secrets_path")] pub secrets_path: PathBuf,
#[serde(default = "default::transcoding_profiles")] pub transcoding_profiles: Vec<EncodingProfile>,
#[serde(default = "default::max_in_memory_cache_size")] pub max_in_memory_cache_size: usize,
- #[serde(default)] pub use_in_memory_import_storage: bool,
#[serde(default)] pub admin_username: Option<String>,
#[serde(default = "default::login_expire")] pub login_expire: i64,
#[serde(default)] pub default_permission_set: PermissionSet,
diff --git a/common/src/lib.rs b/common/src/lib.rs
index 503febd..5dadd1c 100644
--- a/common/src/lib.rs
+++ b/common/src/lib.rs
@@ -25,6 +25,7 @@ pub struct NodeID(pub [u8; 32]);
#[derive(Debug, Clone, Deserialize, Serialize, Default, Encode, Decode)]
pub struct Node {
pub slug: String,
+ #[serde(default)]
pub parents: Vec<NodeID>,
pub kind: Option<NodeKind>,
pub poster: Option<Asset>,
@@ -36,9 +37,12 @@ pub struct Node {
pub release_date: Option<i64>, // in unix millis
pub index: Option<usize>,
pub media: Option<MediaInfo>,
+ #[serde(default)]
pub ratings: BTreeMap<Rating, f64>,
pub federated: Option<String>,
+ #[serde(default)]
pub people: BTreeMap<PeopleGroup, Vec<Appearance>>,
+ #[serde(default)]
pub external_ids: BTreeMap<String, String>,
}
diff --git a/import/Cargo.toml b/import/Cargo.toml
index 988e626..645326d 100644
--- a/import/Cargo.toml
+++ b/import/Cargo.toml
@@ -4,7 +4,6 @@ version = "0.1.0"
edition = "2021"
[dependencies]
-jellycommon = { path = "../common" }
jellybase = { path = "../base" }
jellyclient = { path = "../client" }
diff --git a/import/src/infojson.rs b/import/src/infojson.rs
index 3e4667e..69c28ca 100644
--- a/import/src/infojson.rs
+++ b/import/src/infojson.rs
@@ -5,7 +5,7 @@
*/
use anyhow::Context;
use bincode::{Decode, Encode};
-use jellycommon::chrono::{format::Parsed, Utc};
+use jellybase::common::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 c841294..4203ba1 100644
--- a/import/src/lib.rs
+++ b/import/src/lib.rs
@@ -5,16 +5,20 @@
*/
use anyhow::{anyhow, Context, Result};
use infojson::YVideo;
-use jellybase::{assetfed::AssetInner, database::Database, CONF, SECRETS};
-use jellycommon::{
- Chapter, LocalTrack, MediaInfo, Node, NodeID, NodeKind, Rating, SourceTrack, SourceTrackKind,
- TrackSource,
+use jellybase::{
+ assetfed::AssetInner,
+ common::{
+ Chapter, LocalTrack, MediaInfo, Node, NodeID, NodeKind, Rating, SourceTrack,
+ SourceTrackKind, TrackSource,
+ },
+ database::Database,
+ CONF, SECRETS,
};
use matroska::matroska_metadata;
use rayon::iter::{ParallelDrainRange, ParallelIterator};
use std::{
collections::HashMap,
- fs::File,
+ fs::{read_to_string, File},
io::BufReader,
mem::swap,
path::{Path, PathBuf},
@@ -118,32 +122,37 @@ fn import_iter_inner(path: &Path, db: &Database, incremental: bool) -> Result<Ve
}
fn import_file(db: &Database, path: &Path) -> Result<()> {
- let parent_slug = path
- .parent()
- .ok_or(anyhow!("no parent"))?
- .file_name()
- .ok_or(anyhow!("parent no filename"))?
- .to_string_lossy();
+ let parent_slug = if path == CONF.media_path {
+ "library".to_string()
+ } else {
+ path.parent()
+ .ok_or(anyhow!("no parent"))?
+ .file_name()
+ .ok_or(anyhow!("parent no filename"))?
+ .to_string_lossy()
+ .to_string()
+ };
let parent = NodeID::from_slug(&parent_slug);
let filename = path.file_name().unwrap().to_string_lossy();
match filename.as_ref() {
- "poster.jpeg" | "poster.webp" => {
+ "poster.jpeg" | "poster.webp" | "poster.png" => {
db.update_node_init(parent, |node| {
node.slug = parent_slug.to_string();
node.poster = Some(AssetInner::Media(path.to_owned()).ser());
Ok(())
})?;
}
- "backdrop.jpeg" | "backdrop.webp" => {
+ "backdrop.jpeg" | "backdrop.webp" | "backdrop#.png" => {
db.update_node_init(parent, |node| {
node.slug = parent_slug.to_string();
node.backdrop = Some(AssetInner::Media(path.to_owned()).ser());
Ok(())
})?;
}
- "info.json" | "info.yaml" => {
- let data = serde_yaml::from_reader::<_, Node>(BufReader::new(File::open(path)?))?;
+ "node.json" | "node.yaml" => {
+ let raw = format!("slug: {parent_slug}\n{}", read_to_string(path)?);
+ let data = serde_yaml::from_str::<Node>(&raw)?;
db.update_node_init(parent, |node| {
node.slug = parent_slug.to_string();
fn merge_option<T>(a: &mut Option<T>, b: Option<T>) {
@@ -151,9 +160,14 @@ fn import_file(db: &Database, path: &Path) -> Result<()> {
*a = b;
}
}
+ merge_option(&mut node.kind, data.kind);
merge_option(&mut node.title, data.title);
merge_option(&mut node.tagline, data.tagline);
merge_option(&mut node.description, data.description);
+ merge_option(&mut node.index, data.index);
+ merge_option(&mut node.release_date, data.release_date);
+ node.external_ids.extend(data.external_ids);
+
Ok(())
})?;
}
diff --git a/import/src/matroska.rs b/import/src/matroska.rs
index 6a33420..f9a59ab 100644
--- a/import/src/matroska.rs
+++ b/import/src/matroska.rs
@@ -14,8 +14,8 @@ use ebml_struct::{
use jellybase::{
assetfed::AssetInner,
cache::{cache_file, cache_memory},
+ common::Asset,
};
-use jellycommon::Asset;
use log::info;
use std::{
fs::File,
diff --git a/import/src/tmdb.rs b/import/src/tmdb.rs
index 522d9d6..678ce61 100644
--- a/import/src/tmdb.rs
+++ b/import/src/tmdb.rs
@@ -5,10 +5,12 @@
*/
use anyhow::{anyhow, bail, Context};
use bincode::{Decode, Encode};
-use jellybase::cache::{async_cache_file, async_cache_memory, CachePath};
-use jellycommon::{
- chrono::{format::Parsed, Utc},
- TmdbKind,
+use jellybase::{
+ cache::{async_cache_file, async_cache_memory, CachePath},
+ common::{
+ chrono::{format::Parsed, Utc},
+ TmdbKind,
+ },
};
use log::info;
use reqwest::{
diff --git a/import/src/trakt.rs b/import/src/trakt.rs
index f37eb74..98532c5 100644
--- a/import/src/trakt.rs
+++ b/import/src/trakt.rs
@@ -4,8 +4,10 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use bincode::{Decode, Encode};
-use jellybase::cache::async_cache_memory;
-use jellycommon::{Appearance, ObjectIds, PeopleGroup, Person, TraktKind};
+use jellybase::{
+ cache::async_cache_memory,
+ common::{Appearance, ObjectIds, PeopleGroup, Person, TraktKind},
+};
use reqwest::{
header::{HeaderMap, HeaderName, HeaderValue},
Client, ClientBuilder,
diff --git a/remuxer/Cargo.toml b/remuxer/Cargo.toml
index 3386717..2313dcc 100644
--- a/remuxer/Cargo.toml
+++ b/remuxer/Cargo.toml
@@ -4,7 +4,6 @@ version = "0.1.0"
edition = "2021"
[dependencies]
-jellycommon = { path = "../common" }
jellymatroska = { path = "../matroska" }
jellybase = { path = "../base" }
diff --git a/remuxer/src/extract.rs b/remuxer/src/extract.rs
index 364d33c..12e4003 100644
--- a/remuxer/src/extract.rs
+++ b/remuxer/src/extract.rs
@@ -5,7 +5,7 @@
*/
use crate::seek_index::get_seek_index;
use anyhow::{anyhow, bail};
-use jellycommon::LocalTrack;
+use jellybase::common::LocalTrack;
use jellymatroska::{block::Block, read::EbmlReader, Master, MatroskaTag};
use log::debug;
use std::{fs::File, io::BufReader, path::PathBuf};
diff --git a/remuxer/src/fragment.rs b/remuxer/src/fragment.rs
index 3cd122e..d7adc41 100644
--- a/remuxer/src/fragment.rs
+++ b/remuxer/src/fragment.rs
@@ -9,7 +9,7 @@ use crate::{
segment_extractor::SegmentExtractIter,
};
use anyhow::{anyhow, Context, Result};
-use jellycommon::{LocalTrack, Node, SourceTrackKind};
+use jellybase::common::{LocalTrack, Node, SourceTrackKind};
use jellymatroska::{read::EbmlReader, write::EbmlWriter, Master, MatroskaTag};
use log::{debug, info};
use std::{
diff --git a/remuxer/src/lib.rs b/remuxer/src/lib.rs
index 7f5ad1c..3ea16e2 100644
--- a/remuxer/src/lib.rs
+++ b/remuxer/src/lib.rs
@@ -5,7 +5,6 @@
*/
pub mod extract;
pub mod fragment;
-pub mod metadata;
pub mod remux;
pub mod seek_index;
pub mod segment_extractor;
@@ -14,7 +13,7 @@ pub mod trim_writer;
pub use fragment::write_fragment_into;
pub use remux::remux_stream_into;
-use jellycommon::{SourceTrack, SourceTrackKind};
+use jellybase::common::{SourceTrack, SourceTrackKind};
use jellymatroska::{Master, MatroskaTag};
pub fn ebml_header(webm: bool) -> MatroskaTag {
diff --git a/remuxer/src/metadata.rs b/remuxer/src/metadata.rs
deleted file mode 100644
index 3e14821..0000000
--- a/remuxer/src/metadata.rs
+++ /dev/null
@@ -1,378 +0,0 @@
-/*
- 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>
-*/
-use anyhow::{anyhow, bail, Context, Result};
-use bincode::{Decode, Encode};
-use jellycommon::{Chapter, LocalTrack, SourceTrack, SourceTrackKind, TrackSource};
-use jellymatroska::{
- matroska::MatroskaTag,
- read::EbmlReader,
- unflatten::{Unflat, Unflatten},
-};
-use log::{debug, error, info, warn};
-use std::{path::PathBuf, time::Instant};
-
-#[derive(Default, Clone, Debug, Encode, Decode)]
-pub struct MatroskaMetadata {
- pub title: Option<String>,
- pub description: Option<String>,
- pub tagline: Option<String>,
- pub tracks: Vec<SourceTrack>,
- pub track_sources: Vec<LocalTrack>,
- pub cover: Option<(String, Vec<u8>)>,
- pub infojson: Option<String>,
- pub chapters: Vec<Chapter>,
- pub duration: f64,
-}
-
-pub fn import_metadata(input: &mut EbmlReader) -> Result<MatroskaMetadata> {
- while let Some(item) = input.next() {
- let item = match item {
- Ok((_, item)) => item,
- Err(e) => {
- if !matches!(e, jellymatroska::error::Error::Io(_)) {
- warn!("{e}");
- }
- break;
- }
- };
- match item {
- MatroskaTag::Ebml(_) => {
- let mut iter = Unflatten::new_with_end(input, item);
- while let Some(Ok(Unflat {
- children: _, item, ..
- })) = iter.n()
- {
- match item {
- MatroskaTag::DocType(t) => {
- if !matches!(t.as_str(), "matroska" | "webm") {
- error!("file is neither matroska nor webm but {:?}", t)
- }
- }
- _ => debug!("(re) tag ignored: {item:?}"),
- }
- }
- }
- MatroskaTag::Segment(_) => {
- info!("extracting metadata...");
- let mut children = Unflatten::new_with_end(input, item);
- let t = Instant::now();
- let r = import_read_segment(&mut children)?;
- info!("done in {:?}", t.elapsed());
- return Ok(r);
- }
- _ => debug!("(r) tag ignored: {item:?}"),
- }
- }
- Err(anyhow!("no segment found"))
-}
-
-fn import_read_segment(segment: &mut Unflatten) -> Result<MatroskaMetadata> {
- let (mut timestamp_scale, mut duration) = (None, None);
- let mut m = MatroskaMetadata::default();
-
- let (
- mut info_found,
- mut tags_found,
- mut attachments_found,
- mut tracks_found,
- mut found_chapters,
- ) = (false, false, false, false, false);
-
- while let Some(Ok(Unflat { children, item, .. })) = segment.n() {
- match item {
- MatroskaTag::SeekHead(_) => {}
- MatroskaTag::Info(_) => {
- info_found = true;
- let mut children = children.unwrap();
- while let Some(Ok(Unflat {
- children: _, item, ..
- })) = children.n()
- {
- match item {
- MatroskaTag::Title(t) => m.title = Some(t),
- MatroskaTag::TimestampScale(v) => timestamp_scale = Some(v),
- MatroskaTag::Duration(v) => duration = Some(v),
- _ => debug!("(rsi) tag ignored: {item:?}"),
- }
- }
- }
- MatroskaTag::Void(_) => {}
- MatroskaTag::Tags(_) => {
- tags_found = true;
- let mut children = children.unwrap();
- while let Some(Ok(Unflat { children, item, .. })) = children.n() {
- match item {
- MatroskaTag::Tag(_) => {
- let mut children = children.unwrap();
- while let Some(Ok(Unflat { children, item, .. })) = children.n() {
- match item {
- MatroskaTag::SimpleTag(_) => {
- let (mut key, mut value) = (None, None);
- let mut children = children.unwrap();
- while let Some(Ok(Unflat {
- children: _, item, ..
- })) = children.n()
- {
- match item {
- MatroskaTag::TagName(k) => key = Some(k),
- MatroskaTag::TagString(v) => value = Some(v),
- _ => debug!("(rstts) tag ignored: {item:?}"),
- }
- }
- match (key, value) {
- (Some(key), Some(value)) => match key.as_str() {
- "DESCRIPTION" => m.description = Some(value),
- "COMMENT" => m.tagline = Some(value),
- _ => debug!("simple tag ignored: {key:?}"),
- },
- (None, None) => (),
- _ => warn!("simple tag with only one of name/string"),
- }
- }
- _ => debug!("(rstt) tag ignored: {item:?}"),
- }
- }
- }
- MatroskaTag::Crc32(_) => {}
- _ => debug!("(rst) tag ignored: {item:?}"),
- }
- }
- }
- MatroskaTag::Attachments(_) => {
- attachments_found = true;
- let mut children = children.unwrap();
- while let Some(Ok(Unflat { children, item, .. })) = children.n() {
- match item {
- MatroskaTag::AttachedFile(_) => {
- let (mut name, mut data, mut mime) = Default::default();
- let mut children = children.unwrap();
- while let Some(Ok(Unflat {
- children: _, item, ..
- })) = children.n()
- {
- match item {
- MatroskaTag::FileName(n) => name = Some(n),
- MatroskaTag::FileData(d) => data = Some(d),
- MatroskaTag::FileMimeType(m) => mime = Some(m),
- _ => debug!("(rsaa) tag ignored: {item:?}"),
- }
- }
- let (name, data, mime) = (
- name.ok_or(anyhow!("attachment without name"))?,
- data.ok_or(anyhow!("attachment without data"))?,
- mime.ok_or(anyhow!("attachment without mime type"))?,
- );
- info!("attachment found: {name:?} type {mime:?}");
- match (name.as_str(), mime.as_str()) {
- ("info.json", "application/json") => {
- m.infojson =
- Some(String::from_utf8(data).context("info.json invalid")?)
- }
- (_, "image/jpeg" | "image/png" | "image/webp") => {
- m.cover = Some((mime, data))
- }
- _ => (),
- }
- }
- _ => debug!("(rsa) tag ignored: {item:?}"),
- }
- }
- }
- MatroskaTag::Cues(_) => {}
- MatroskaTag::Chapters(_) => {
- found_chapters = true;
- let mut children = children.unwrap();
- while let Some(Ok(Unflat { children, item, .. })) = children.n() {
- match item {
- MatroskaTag::EditionEntry(_) => {
- let mut children = children.unwrap();
- while let Some(Ok(Unflat { children, item, .. })) = children.n() {
- match item {
- MatroskaTag::EditionUID(_)
- | MatroskaTag::EditionFlagHidden(_)
- | MatroskaTag::EditionFlagDefault(_) => {}
- MatroskaTag::ChapterAtom(_) => {
- let mut children = children.unwrap();
- let mut chap = Chapter::default();
- while let Some(Ok(Unflat { children, item, .. })) =
- children.n()
- {
- match item {
- MatroskaTag::ChapterFlagEnabled(_)
- | MatroskaTag::ChapterFlagHidden(_)
- | MatroskaTag::ChapterUID(_) => (),
- MatroskaTag::ChapterTimeStart(t) => {
- chap.time_start = Some(t as f64 * 1e-9)
- }
- MatroskaTag::ChapterTimeEnd(t) => {
- chap.time_end = Some(t as f64 * 1e-9)
- }
- MatroskaTag::ChapterDisplay(_) => {
- let mut string = String::new();
- let mut lang = String::new();
- let mut children = children.unwrap();
- while let Some(Ok(Unflat { item, .. })) =
- children.n()
- {
- match item {
- MatroskaTag::ChapString(s) => {
- string = s
- }
- MatroskaTag::ChapLanguage(l) => {
- lang = l
- }
- _ => warn!(
- "(rscead) tag ignored: {item:?}"
- ),
- }
- }
- chap.labels.push((lang, string))
- }
- _ => warn!("(rscea) tag ignored: {item:?}"),
- }
- }
- m.chapters.push(chap);
- }
- _ => warn!("(rsce) tag ignored: {item:?}"),
- }
- }
- if !m.chapters.is_empty() {
- info!("{} chapters added", m.chapters.len());
- }
- }
- _ => warn!("(rsc) tag ignored: {item:?}"),
- }
- }
- }
- MatroskaTag::Tracks(_) => {
- tracks_found = true;
- let mut children = children.unwrap();
- while let Some(Ok(Unflat { children, item, .. })) = children.n() {
- match item {
- MatroskaTag::TrackEntry(_) => {
- let mut children = children.unwrap();
- let (
- mut index,
- mut language,
- mut codec,
- mut kind,
- mut sample_rate,
- mut channels,
- mut width,
- mut height,
- mut display_width,
- mut display_height,
- mut name,
- mut fps,
- mut bit_depth,
- mut codec_private,
- mut default_duration,
- mut display_unit,
- ) = (
- None, None, None, None, None, None, None, None, None, None, None,
- None, None, None, None, None,
- );
- while let Some(Ok(Unflat { children, item, .. })) = children.n() {
- match item {
- MatroskaTag::CodecID(b) => codec = Some(b),
- MatroskaTag::Language(v) => language = Some(v),
- MatroskaTag::TrackNumber(v) => index = Some(v),
- MatroskaTag::TrackType(v) => kind = Some(v),
- MatroskaTag::Name(v) => name = Some(v),
- MatroskaTag::CodecPrivate(v) => codec_private = Some(v),
- MatroskaTag::DefaultDuration(v) => default_duration = Some(v),
- MatroskaTag::Audio(_) => {
- let mut children = children.unwrap();
- while let Some(Ok(Unflat { item, .. })) = children.n() {
- match item {
- MatroskaTag::Channels(v) => {
- channels = Some(v as usize)
- }
- MatroskaTag::SamplingFrequency(v) => {
- sample_rate = Some(v)
- }
- MatroskaTag::BitDepth(v) => bit_depth = Some(v),
- _ => (),
- }
- }
- }
- MatroskaTag::Video(_) => {
- let mut children = children.unwrap();
- while let Some(Ok(Unflat { item, .. })) = children.n() {
- match item {
- MatroskaTag::PixelWidth(v) => width = Some(v),
- MatroskaTag::PixelHeight(v) => height = Some(v),
- MatroskaTag::DisplayWidth(v) => {
- display_width = Some(v)
- }
- MatroskaTag::DisplayHeight(v) => {
- display_height = Some(v)
- }
- MatroskaTag::DisplayUnit(v) => {
- display_unit = Some(v)
- }
- MatroskaTag::FrameRate(v) => fps = Some(v),
- _ => (),
- }
- }
- }
- _ => (),
- }
- }
- let track_index = index.unwrap();
- let kind = match kind.ok_or(anyhow!("track type required"))? {
- 1 => SourceTrackKind::Video {
- fps,
- width: width.unwrap(),
- height: height.unwrap(),
- display_width,
- display_height,
- display_unit,
- },
- 2 => SourceTrackKind::Audio {
- bit_depth: bit_depth.map(|x| x as usize),
- channels: channels.unwrap_or(1), // TODO
- sample_rate: sample_rate.unwrap_or(41_100.0), // TODO
- },
- 17 => SourceTrackKind::Subtitles,
- _ => bail!("invalid track type"),
- };
- m.tracks.push(SourceTrack {
- federated: vec![],
- default_duration,
- source: TrackSource::Local(LocalTrack {
- track: track_index as usize,
- path: PathBuf::new(),
- codec_private,
- }),
- name: name.unwrap_or_else(|| "unnamed".to_string()),
- codec: codec.unwrap(),
- language: language.unwrap_or_else(|| "none".to_string()),
- kind,
- });
- }
- MatroskaTag::Crc32(_) => {}
- _ => warn!("(rst) tag ignored: {item:?}"),
- }
- }
- }
- MatroskaTag::Cluster(_) => {}
-
- _ => warn!("(rs) tag ignored: {item:?}"),
- };
- if info_found && tracks_found && attachments_found && tags_found && found_chapters {
- debug!("we found all we need, stopping read early");
- break;
- }
- }
- segment.exit_dirty();
-
- if let Some(duration) = duration {
- m.duration = (duration * timestamp_scale.unwrap_or(1_000_000) as f64) / 1_000_000_000_f64;
- }
-
- Ok(m)
-}
diff --git a/remuxer/src/remux.rs b/remuxer/src/remux.rs
index ef49620..a2e0d8a 100644
--- a/remuxer/src/remux.rs
+++ b/remuxer/src/remux.rs
@@ -8,7 +8,7 @@ use crate::{
segment_extractor::SegmentExtractIter, trim_writer::TrimWriter,
};
use anyhow::{anyhow, Context};
-use jellycommon::{
+use jellybase::common::{
seek_index::{BlockIndex, SeekIndex},
LocalTrack, Node, SourceTrack,
};
diff --git a/remuxer/src/seek_index.rs b/remuxer/src/seek_index.rs
index 581ed9a..bd351d9 100644
--- a/remuxer/src/seek_index.rs
+++ b/remuxer/src/seek_index.rs
@@ -4,8 +4,10 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use anyhow::{Context, Result};
-use jellybase::cache::cache_memory;
-use jellycommon::seek_index::{BlockIndex, SeekIndex};
+use jellybase::{
+ cache::cache_memory,
+ common::seek_index::{BlockIndex, SeekIndex},
+};
use jellymatroska::{
block::Block,
read::EbmlReader,
diff --git a/server/src/routes/external_compat.rs b/server/src/routes/external_compat.rs
index 7babfa5..eda3537 100644
--- a/server/src/routes/external_compat.rs
+++ b/server/src/routes/external_compat.rs
@@ -5,6 +5,7 @@
*/
use super::ui::{account::session::Session, error::MyResult};
use crate::routes::ui::node::rocket_uri_macro_r_library_node;
+use crate::routes::ui::player::{rocket_uri_macro_r_player, PlayerConfig};
use anyhow::anyhow;
use jellybase::database::Database;
use rocket::{get, response::Redirect, State};
@@ -18,7 +19,10 @@ pub fn r_ext_youtube_watch(_session: Session, db: &State<Database>, v: &str) ->
Err(anyhow!("element not found"))?
};
let node = db.get_node(id)?.ok_or(anyhow!("node missing"))?;
- Ok(Redirect::to(rocket::uri!(r_library_node(&node.slug))))
+ Ok(Redirect::to(rocket::uri!(r_player(
+ &node.slug,
+ PlayerConfig::default()
+ ))))
}
#[get("/channel/<id>")]
diff --git a/server/src/routes/ui/home.rs b/server/src/routes/ui/home.rs
index ebed647..6c6fdbc 100644
--- a/server/src/routes/ui/home.rs
+++ b/server/src/routes/ui/home.rs
@@ -3,14 +3,19 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
-use super::{account::session::Session, layout::LayoutPage, node::NodeCard};
+use super::{
+ account::session::Session,
+ layout::LayoutPage,
+ node::{DatabaseNodeUserDataExt, NodeCard},
+};
use crate::{
database::Database,
routes::ui::{error::MyResult, layout::DynLayoutPage},
};
+use anyhow::Context;
use chrono::{Datelike, Utc};
use jellybase::CONF;
-use jellycommon::{user::WatchedState, Rating};
+use jellycommon::{user::WatchedState, NodeID, Rating};
use rocket::{get, State};
use tokio::fs::read_to_string;
@@ -21,14 +26,12 @@ pub fn r_home(sess: Session, db: &State<Database>) -> MyResult<DynLayoutPage> {
.flat_map(|i| Some(items[cheap_daily_random(i).checked_rem(items.len())?].clone()))
.collect::<Vec<_>>();
- // let toplevel = T_NODE
- // .get(db, "library")?
- // .context("root node missing")?
- // .into_iter()
- // .map(|n| db.get_node_with_userdata(&n, &sess))
- // .collect::<anyhow::Result<Vec<_>>>()?
- // .into_iter()
- // .collect::<Vec<_>>();
+ let toplevel = db
+ .get_node_children(NodeID::from_slug("library"))
+ .context("root node missing")?
+ .into_iter()
+ .map(|n| db.get_node_with_userdata(n, &sess))
+ .collect::<anyhow::Result<Vec<_>>>()?;
items.sort_by_key(|(n, _)| {
n.ratings
@@ -68,9 +71,9 @@ pub fn r_home(sess: Session, db: &State<Database>) -> MyResult<DynLayoutPage> {
title: "Home".to_string(),
content: markup::new! {
h2 { "Explore " @CONF.brand }
- // ul.children.hlist {@for (id, node, udata) in &toplevel {
- // li { @NodeCard { id, node, udata } }
- // }}
+ ul.children.hlist {@for (node, udata) in &toplevel {
+ li { @NodeCard { node, udata } }
+ }}
@if !continue_watching.is_empty() {
h2 { "Continue Watching" }
ul.children.hlist {@for (node, udata) in &continue_watching {
diff --git a/stream/Cargo.toml b/stream/Cargo.toml
index b66cfca..36979c9 100644
--- a/stream/Cargo.toml
+++ b/stream/Cargo.toml
@@ -4,8 +4,7 @@ version = "0.1.0"
edition = "2021"
[dependencies]
-jellycommon = { path = "../common", features = ["rocket"] }
-jellybase = { path = "../base" }
+jellybase = { path = "../base", features = ["rocket"] }
jellytranscoder = { path = "../transcoder" }
jellyremuxer = { path = "../remuxer" }
diff --git a/stream/src/fragment.rs b/stream/src/fragment.rs
index 2dbc716..e276d29 100644
--- a/stream/src/fragment.rs
+++ b/stream/src/fragment.rs
@@ -4,11 +4,14 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use anyhow::{anyhow, bail, Result};
-use jellybase::{permission::PermissionSetExt, CONF};
-use jellycommon::{
- stream::StreamSpec,
- user::{PermissionSet, UserPermission},
- LocalTrack, Node,
+use jellybase::{
+ common::{
+ stream::StreamSpec,
+ user::{PermissionSet, UserPermission},
+ LocalTrack, Node,
+ },
+ permission::PermissionSetExt,
+ CONF,
};
use jellytranscoder::fragment::transcode;
use log::warn;
diff --git a/stream/src/hls.rs b/stream/src/hls.rs
index c09b77f..dca1036 100644
--- a/stream/src/hls.rs
+++ b/stream/src/hls.rs
@@ -5,10 +5,12 @@
*/
use anyhow::{anyhow, Result};
-use jellybase::CONF;
-use jellycommon::{
- stream::{StreamFormat, StreamSpec},
- LocalTrack, Node, SourceTrackKind,
+use jellybase::{
+ common::{
+ stream::{StreamFormat, StreamSpec},
+ LocalTrack, Node, SourceTrackKind,
+ },
+ CONF,
};
use std::{fmt::Write, ops::Range, sync::Arc};
use tokio::{
diff --git a/stream/src/jhls.rs b/stream/src/jhls.rs
index 28d383f..79fc5fe 100644
--- a/stream/src/jhls.rs
+++ b/stream/src/jhls.rs
@@ -4,12 +4,15 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use anyhow::{anyhow, Result};
-use jellybase::{permission::PermissionSetExt, CONF};
-use jellycommon::{
- jhls::JhlsTrackIndex,
- stream::StreamSpec,
- user::{PermissionSet, UserPermission},
- LocalTrack, Node,
+use jellybase::{
+ common::{
+ jhls::JhlsTrackIndex,
+ stream::StreamSpec,
+ user::{PermissionSet, UserPermission},
+ LocalTrack, Node,
+ },
+ permission::PermissionSetExt,
+ CONF,
};
use std::sync::Arc;
use tokio::io::{AsyncWriteExt, DuplexStream};
diff --git a/stream/src/lib.rs b/stream/src/lib.rs
index 2055440..ca7578d 100644
--- a/stream/src/lib.rs
+++ b/stream/src/lib.rs
@@ -12,11 +12,14 @@ pub mod webvtt;
use anyhow::{anyhow, bail, Context, Result};
use fragment::fragment_stream;
use hls::{hls_master_stream, hls_variant_stream};
-use jellybase::{permission::PermissionSetExt, CONF};
-use jellycommon::{
- stream::{StreamFormat, StreamSpec},
- user::{PermissionSet, UserPermission},
- LocalTrack, Node, TrackSource,
+use jellybase::{
+ common::{
+ stream::{StreamFormat, StreamSpec},
+ user::{PermissionSet, UserPermission},
+ LocalTrack, Node, TrackSource,
+ },
+ permission::PermissionSetExt,
+ CONF,
};
use jhls::jhls_index;
use std::{io::SeekFrom, ops::Range, sync::Arc};
diff --git a/stream/src/webvtt.rs b/stream/src/webvtt.rs
index 02a4181..f78ac2f 100644
--- a/stream/src/webvtt.rs
+++ b/stream/src/webvtt.rs
@@ -4,8 +4,11 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use anyhow::{anyhow, Context, Result};
-use jellybase::{cache::async_cache_memory, CONF};
-use jellycommon::{stream::StreamSpec, LocalTrack, Node};
+use jellybase::{
+ cache::async_cache_memory,
+ common::{stream::StreamSpec, LocalTrack, Node},
+ CONF,
+};
use jellyremuxer::extract::extract_track;
use jellytranscoder::subtitles::{parse_subtitles, write_webvtt};
use std::sync::Arc;
diff --git a/transcoder/Cargo.toml b/transcoder/Cargo.toml
index f1e5062..d0ddf9c 100644
--- a/transcoder/Cargo.toml
+++ b/transcoder/Cargo.toml
@@ -4,7 +4,6 @@ version = "0.1.0"
edition = "2021"
[dependencies]
-jellycommon = { path = "../common" }
jellybase = { path = "../base" }
jellyremuxer = { path = "../remuxer" }
log = { workspace = true }
diff --git a/transcoder/src/fragment.rs b/transcoder/src/fragment.rs
index 4b72eaf..8822fa2 100644
--- a/transcoder/src/fragment.rs
+++ b/transcoder/src/fragment.rs
@@ -5,8 +5,10 @@
*/
use crate::LOCAL_VIDEO_TRANSCODING_TASKS;
-use jellybase::cache::{async_cache_file, CachePath};
-use jellycommon::jhls::EncodingProfile;
+use jellybase::{
+ cache::{async_cache_file, CachePath},
+ common::jhls::EncodingProfile,
+};
use log::{debug, info};
use std::process::Stdio;
use tokio::{
diff --git a/transcoder/src/subtitles.rs b/transcoder/src/subtitles.rs
index d7e7b29..77b423d 100644
--- a/transcoder/src/subtitles.rs
+++ b/transcoder/src/subtitles.rs
@@ -4,7 +4,7 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
use anyhow::{anyhow, bail, Context};
-use jellycommon::jhls::SubtitleCue;
+use jellybase::common::jhls::SubtitleCue;
use std::fmt::Write;
pub fn parse_subtitles(