/* 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 */ #![feature(array_try_map)] pub mod api; pub mod config; pub mod helpers; pub mod r#impl; pub mod jhls; pub mod routes; pub mod stream; pub mod user; pub use chrono; use bincode::{Decode, Encode}; use serde::{Deserialize, Serialize}; use std::{ collections::{BTreeMap, BTreeSet}, path::PathBuf, }; #[macro_export] macro_rules! url_enum { ($(#[$a:meta])* enum $i:ident { $($(#[$va:meta])* $vi:ident = $vk:literal),*, }) => { $(#[$a])* pub enum $i { $($(#[$va])* $vi),* } impl $i { pub const ALL: &'static [$i] = &[$($i::$vi),*]; pub fn to_str(&self) -> &'static str { match self { $(Self::$vi => $vk),* } } pub fn from_str_opt(s: &str) -> Option { match s { $($vk => Some(Self::$vi) ),*, _ => None } } } impl std::fmt::Display for $i { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { f.write_str(self.to_str()) } } impl std::str::FromStr for $i { type Err = (); fn from_str(s: &str) -> Result { Self::from_str_opt(s).ok_or(()) } } }; } #[derive(Clone, Copy, Debug, Encode, Decode, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct NodeID(pub [u8; 32]); pub enum NodeIDOrSlug { ID(NodeID), Slug(String), } #[derive(Debug, Clone, Deserialize, Serialize, Default, Encode, Decode)] pub struct Node { #[serde(default)] pub slug: String, #[serde(default)] pub parents: BTreeSet, pub kind: NodeKind, pub poster: Option, pub backdrop: Option, pub title: Option, pub subtitle: Option, pub tagline: Option, pub description: Option, pub release_date: Option, // in unix millis pub index: Option, pub media: Option, #[serde(default)] pub ratings: BTreeMap, pub federated: Option, #[serde(default)] pub tags: BTreeSet, #[serde(default)] pub people: BTreeMap>, #[serde(default)] pub external_ids: BTreeMap, #[serde(default)] pub visibility: Visibility, #[serde(default)] pub storage_size: u64, } #[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Encode, Decode)] pub struct Asset(pub String); #[derive(Debug, Clone, Deserialize, Serialize, Default, Encode, Decode)] pub struct Appearance { #[serde(default)] pub jobs: Vec, #[serde(default)] pub characters: Vec, pub person: Person, } #[derive(Debug, Clone, Deserialize, Serialize, Default, Encode, Decode)] pub struct Person { pub name: String, pub headshot: Option, pub ids: ObjectIds, } #[derive(Debug, Clone, Deserialize, Serialize, Default, Encode, Decode)] pub struct ObjectIds { pub trakt: Option, pub slug: Option, pub imdb: Option, pub tmdb: Option, pub omdb: Option, pub tvdb: Option, } url_enum!( #[derive( Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, )] #[serde(rename_all = "snake_case")] enum PeopleGroup { Cast = "cast", Writing = "writing", Directing = "directing", Art = "art", Sound = "sound", Camera = "camera", Lighting = "lighting", Crew = "crew", Editing = "editing", Production = "production", Vfx = "vfx", CostumeMakeup = "costume_makeup", CreatedBy = "created_by", // https://musicbrainz.org/relationships/artist-recording // modelling after this, but its too many categories Performance = "performance", Instrument = "instrument", Vocal = "vocal", Arranger = "arranger", Producer = "producer", Engineer = "engineer", } ); #[derive( Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Encode, Decode, Default, )] #[serde(rename_all = "snake_case")] pub enum Visibility { Hidden, Reduced, #[default] Visible, } url_enum!( #[derive( Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Default, Encode, Decode, PartialOrd, Ord, )] #[serde(rename_all = "snake_case")] enum NodeKind { #[default] Unknown = "unknown", Movie = "movie", Video = "video", Music = "music", ShortFormVideo = "short_form_video", Collection = "collection", Channel = "channel", Show = "show", Series = "series", Season = "season", Episode = "episode", } ); #[derive(Debug, Clone, Deserialize, Serialize, Encode, Decode)] #[serde(rename_all = "snake_case")] pub enum TrackSource { Local(Asset), Remote(usize), } pub type TrackID = usize; #[derive(Debug, Clone, Deserialize, Serialize, Hash, Encode, Decode, PartialEq, Eq)] pub struct LocalTrack { pub path: PathBuf, pub track: TrackID, } #[derive(Debug, Clone, Deserialize, Serialize, Encode, Decode)] pub struct MediaInfo { pub duration: f64, // in seconds pub tracks: Vec, #[serde(default)] pub chapters: Vec, } #[derive(Debug, Clone, Deserialize, Serialize, Default, Encode, Decode)] pub struct Chapter { pub time_start: Option, pub time_end: Option, pub labels: Vec<(String, String)>, } #[derive(Debug, Clone, Deserialize, Serialize, Encode, Decode)] pub struct SourceTrack { pub source: TrackSource, pub kind: SourceTrackKind, pub name: String, pub codec: String, pub language: String, pub default_duration: Option, pub seek_pre_roll: u64, pub codec_delay: u64, pub flag_lacing: u64, #[serde(default)] pub federated: Vec, } url_enum!( #[derive( Debug, Clone, Deserialize, Serialize, PartialEq, Eq, PartialOrd, Ord, Hash, Encode, Decode, )] #[serde(rename_all = "snake_case")] enum Rating { Imdb = "imdb", Tmdb = "tmdb", RottenTomatoes = "rotten_tomatoes", Metacritic = "metacritic", YoutubeViews = "youtube_views", YoutubeLikes = "youtube_likes", YoutubeFollowers = "youtube_followers", Trakt = "trakt", } ); #[derive(Debug, Clone, PartialEq, Deserialize, Serialize, Encode, Decode)] #[serde(rename_all = "snake_case")] pub enum SourceTrackKind { Video { width: u64, height: u64, display_width: Option, display_height: Option, display_unit: u64, fps: Option, }, Audio { channels: usize, sample_rate: f64, bit_depth: Option, }, Subtitle, } #[derive(Debug, Serialize, Deserialize, Clone, Copy, Hash, PartialEq, Encode, Decode)] #[serde(rename_all = "snake_case")] pub enum TraktKind { Movie, Show, Season, Episode, Person, User, } #[derive(Debug, Clone, Copy, Hash, Serialize, Deserialize)] pub enum TmdbKind { Tv, Movie, }