aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2026-01-23 17:41:45 +0100
committermetamuffin <metamuffin@disroot.org>2026-01-23 17:41:45 +0100
commit774f64c0789529884dd7a5232f190e347ad29532 (patch)
tree6eb85388837c993a054fba5ca59fdd329f5b5840
parent3671a4e07565c86f8071fb2309f463aeaf684ba3 (diff)
downloadjellything-774f64c0789529884dd7a5232f190e347ad29532.tar
jellything-774f64c0789529884dd7a5232f190e347ad29532.tar.bz2
jellything-774f64c0789529884dd7a5232f190e347ad29532.tar.zst
move locale code to own crate
-rw-r--r--Cargo.lock13
-rw-r--r--common/src/api.rs18
-rw-r--r--common/src/node.rs9
-rw-r--r--import/src/plugins/media_info.rs16
-rw-r--r--server/src/api.rs50
-rw-r--r--server/src/logger.rs (renamed from logic/src/admin/log.rs)20
-rw-r--r--server/src/main.rs6
-rw-r--r--server/src/routes.rs3
-rw-r--r--ui/Cargo.toml5
-rw-r--r--ui/locale/Cargo.toml7
-rw-r--r--ui/locale/src/lib.rs73
-rw-r--r--ui/src/components/mod.rs10
-rw-r--r--ui/src/components/node_page.rs3
-rw-r--r--ui/src/components/props.rs2
-rw-r--r--ui/src/components/stats.rs2
-rw-r--r--ui/src/format.rs19
-rw-r--r--ui/src/lib.rs13
-rw-r--r--ui/src/locale.rs52
-rw-r--r--ui/src/scaffold.rs6
19 files changed, 171 insertions, 156 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 246f3a6..d02588e 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2008,6 +2008,9 @@ dependencies = [
"chrono",
"humansize",
"jellycommon",
+ "jellyui-client-scripts",
+ "jellyui-client-style",
+ "jellyui-locale",
"markup",
"serde",
"serde_json",
@@ -2029,6 +2032,10 @@ dependencies = [
]
[[package]]
+name = "jellyui-locale"
+version = "0.1.0"
+
+[[package]]
name = "jiff"
version = "0.2.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -2251,8 +2258,7 @@ dependencies = [
[[package]]
name = "markup"
version = "0.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "74a887ad620fe1022257343ac77fcdd3720e92888e1b2e66e1b7a4707f453898"
+source = "git+https://github.com/metamuffin/markup.rs?rev=2ee9aee#2ee9aeeb7654ede4dbdd9c9bc7f57cab888ef12f"
dependencies = [
"markup-proc-macro",
]
@@ -2260,8 +2266,7 @@ dependencies = [
[[package]]
name = "markup-proc-macro"
version = "0.15.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ab6ee21fd1855134cacf2f41afdf45f1bc456c7d7f6165d763b4647062dd2be"
+source = "git+https://github.com/metamuffin/markup.rs?rev=2ee9aee#2ee9aeeb7654ede4dbdd9c9bc7f57cab888ef12f"
dependencies = [
"proc-macro2",
"quote",
diff --git a/common/src/api.rs b/common/src/api.rs
index ee26020..9040852 100644
--- a/common/src/api.rs
+++ b/common/src/api.rs
@@ -84,24 +84,6 @@ enums! {
// pub users: Vec<User>,
// }
-// pub struct LogLine {
-// pub time: DateTime<Utc>,
-// pub module: Option<&'static str>,
-// pub level: LogLevel,
-// pub message: String,
-// }
-
-// url_enum!(
-// #[derive(Serialize, Deserialize, Clone, Copy, PartialEq)]
-// enum LogLevel {
-// Trace = "trace",
-// Debug = "debug",
-// Info = "info",
-// Warn = "warn",
-// Error = "error",
-// }
-// );
-
// #[derive(Default, Serialize, Deserialize)]
// pub struct StatsBin {
// pub runtime: f64,
diff --git a/common/src/node.rs b/common/src/node.rs
index 3163dc5..60544e2 100644
--- a/common/src/node.rs
+++ b/common/src/node.rs
@@ -38,7 +38,7 @@ fields! {
TR_SOURCE: Object = 17 "source";
TR_NAME: &str = 18 "name";
TR_CODEC: &str = 19 "codec";
- TR_LANGUAGE: Tag = 20 "language";
+ TR_LANGUAGE: &str = 20 "language"; // BCP 47
TR_RATE: f64 = 23 "rate";
TR_BIT_DEPTH: u32 = 25 "bit_depth";
TR_CHANNELS: u32 = 28 "channels";
@@ -52,12 +52,6 @@ fields! {
CH_END: f64 = 30 "end";
CH_NAME: &str = 31 "name";
- LANG_UND: &str = 40 "und";
- LANG_NATIVE: &str = 41 "native";
- LANG_ENG: &str = 42 "eng";
- LANG_DEU: &str = 43 "deu";
- LANG_JPN: &str = 44 "jpn";
-
PICT_COVER: &str = 45 "cover";
PICT_BACKDROP: &str = 46 "backdrop";
@@ -95,7 +89,6 @@ fields! {
}
pub type Kind = Tag;
-pub type Language = Tag;
enums! {
VISI_HIDDEN = 76 "hidden";
diff --git a/import/src/plugins/media_info.rs b/import/src/plugins/media_info.rs
index 1730897..b750070 100644
--- a/import/src/plugins/media_info.rs
+++ b/import/src/plugins/media_info.rs
@@ -7,23 +7,13 @@
use crate::plugins::{ImportContext, ImportPlugin, PluginInfo};
use anyhow::Result;
use jellycommon::{
- jellyobject::{Object, ObjectBuffer, Tag},
+ jellyobject::{Object, ObjectBuffer},
*,
};
use jellydb::table::RowNum;
use jellyremuxer::matroska::{Segment, TrackType};
use std::path::Path;
-fn lang_str_to_tag(s: &str) -> Tag {
- use jellycommon::*;
- match s {
- "eng" => LANG_ENG.0,
- "deu" => LANG_DEU.0,
- "jpn" => LANG_JPN.0,
- _ => LANG_UND.0,
- }
-}
-
pub struct MediaInfo;
impl ImportPlugin for MediaInfo {
fn info(&self) -> PluginInfo {
@@ -44,9 +34,7 @@ impl ImportPlugin for MediaInfo {
tracks.entries.iter().map(|t| {
let mut track = ObjectBuffer::empty();
track = track.as_object().insert(TR_CODEC, &t.codec_id);
- track = track
- .as_object()
- .insert(TR_LANGUAGE, lang_str_to_tag(&t.language));
+ track = track.as_object().insert(TR_LANGUAGE, t.language.as_str());
if let Some(name) = &t.name {
track = track.as_object().insert(TR_NAME, name);
}
diff --git a/server/src/api.rs b/server/src/api.rs
index 00a8f1d..fe68b1a 100644
--- a/server/src/api.rs
+++ b/server/src/api.rs
@@ -4,13 +4,9 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
use super::ui::error::MyResult;
-use crate::helper::{accept::AcceptJson, language::AcceptLanguage, A};
-use jellycommon::{user::CreateSessionParams, NodeID};
-use jellylogic::{login::login_logic, node::get_nodes_modified_since, session::Session};
-use jellyui::locale::get_translation_table;
-use rocket::{get, post, response::Redirect, serde::json::Json, Either};
-use serde_json::{json, Value};
-use std::collections::HashMap;
+use crate::helper::A;
+use rocket::{get, post, response::Redirect, serde::json::Json};
+use serde_json::{Value, json};
#[get("/api")]
pub fn r_api_root() -> Redirect {
@@ -22,26 +18,26 @@ pub fn r_version() -> &'static str {
env!("CARGO_PKG_VERSION")
}
-#[get("/translations")]
-pub fn r_translations(
- lang: AcceptLanguage,
- aj: AcceptJson,
-) -> Either<Json<&'static HashMap<&'static str, &'static str>>, String> {
- let AcceptLanguage(lang) = lang;
- let table = get_translation_table(&lang);
- if *aj {
- Either::Left(Json(table))
- } else {
- let mut s = String::new();
- for (k, v) in table {
- s += k;
- s += "=";
- s += v;
- s += "\n";
- }
- Either::Right(s)
- }
-}
+// #[get("/translations")]
+// pub fn r_translations(
+// lang: AcceptLanguage,
+// aj: AcceptJson,
+// ) -> Either<Json<&'static HashMap<&'static str, &'static str>>, String> {
+// let AcceptLanguage(lang) = lang;
+// let table = get_translation_table(&lang);
+// if *aj {
+// Either::Left(Json(table))
+// } else {
+// let mut s = String::new();
+// for (k, v) in table {
+// s += k;
+// s += "=";
+// s += v;
+// s += "\n";
+// }
+// Either::Right(s)
+// }
+// }
#[post("/api/create_session", data = "<data>")]
pub fn r_api_account_login(data: Json<CreateSessionParams>) -> MyResult<Value> {
diff --git a/logic/src/admin/log.rs b/server/src/logger.rs
index d266b64..9195b99 100644
--- a/logic/src/admin/log.rs
+++ b/server/src/logger.rs
@@ -4,7 +4,9 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
+use chrono::{DateTime, Utc};
use log::Level;
+use serde::{Deserialize, Serialize};
use std::{
collections::VecDeque,
sync::{Arc, LazyLock, RwLock},
@@ -15,11 +17,27 @@ const MAX_LOG_LEN: usize = 4096;
static LOGGER: LazyLock<Log> = LazyLock::new(Log::default);
-pub fn enable_logging() {
+pub fn setup_logger() {
log::set_logger(&*LOGGER).unwrap();
log::set_max_level(log::LevelFilter::Debug);
}
+pub struct LogLine {
+ pub time: DateTime<Utc>,
+ pub module: Option<&'static str>,
+ pub level: LogLevel,
+ pub message: String,
+}
+
+#[derive(Serialize, Deserialize, Clone, Copy, PartialEq)]
+enum LogLevel {
+ Trace,
+ Debug,
+ Info,
+ Warn,
+ Error,
+}
+
type LogBuffer = VecDeque<Arc<LogLine>>;
pub struct Log {
diff --git a/server/src/main.rs b/server/src/main.rs
index 8b0c128..e17083b 100644
--- a/server/src/main.rs
+++ b/server/src/main.rs
@@ -7,6 +7,7 @@
#![allow(clippy::needless_borrows_for_generic_args)]
#![recursion_limit = "4096"]
+use crate::logger::setup_logger;
use config::load_config;
use log::{error, info, warn};
use routes::build_rocket;
@@ -18,6 +19,7 @@ pub mod api;
pub mod compat;
pub mod config;
pub mod helper;
+pub mod logger;
pub mod logic;
pub mod routes;
pub mod ui;
@@ -41,7 +43,7 @@ static CONF: LazyLock<Config> = LazyLock::new(|| {
#[rocket::main]
async fn main() {
- enable_logging();
+ setup_logger();
info!("loading config...");
if let Err(e) = load_config().await {
error!("error {e:?}");
@@ -49,7 +51,7 @@ async fn main() {
}
#[cfg(feature = "bypass-auth")]
- log::warn!("authentification bypass enabled");
+ logger::warn!("authentification bypass enabled");
if let Err(e) = create_admin_account() {
error!("failed to create admin account: {e:?}");
diff --git a/server/src/routes.rs b/server/src/routes.rs
index c169d5e..2d3e790 100644
--- a/server/src/routes.rs
+++ b/server/src/routes.rs
@@ -29,7 +29,7 @@ use crate::ui::{
style::{r_assets_font, r_assets_js, r_assets_js_map, r_assets_style},
};
use crate::{
- api::{r_api_account_login, r_api_root, r_nodes_modified_since, r_translations, r_version},
+ api::{r_api_account_login, r_api_root, r_nodes_modified_since, r_version},
compat::{
jellyfin::{
r_jellyfin_artists, r_jellyfin_branding_configuration, r_jellyfin_branding_css,
@@ -160,7 +160,6 @@ pub fn build_rocket() -> Rocket<Build> {
r_nodes_modified_since,
r_api_root,
r_version,
- r_translations,
// Compat
r_jellyfin_artists,
r_jellyfin_branding_configuration,
diff --git a/ui/Cargo.toml b/ui/Cargo.toml
index 8752938..9bf5082 100644
--- a/ui/Cargo.toml
+++ b/ui/Cargo.toml
@@ -4,10 +4,13 @@ version = "0.1.0"
edition = "2024"
[dependencies]
-markup = "0.15.0"
+markup = { git = "https://github.com/metamuffin/markup.rs", rev = "2ee9aee" }
jellycommon = { path = "../common" }
humansize = "2.1.3"
serde = { version = "1.0.228", features = ["derive", "rc"] }
serde_json = "1.0.145"
vte = "0.15.0"
chrono = "0.4.43"
+jellyui-client-scripts = { path = "client-scripts" }
+jellyui-client-style = { path = "client-style" }
+jellyui-locale = { path = "locale" }
diff --git a/ui/locale/Cargo.toml b/ui/locale/Cargo.toml
new file mode 100644
index 0000000..5e2b14d
--- /dev/null
+++ b/ui/locale/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "jellyui-locale"
+version = "0.1.0"
+edition = "2024"
+
+[features]
+reload = []
diff --git a/ui/locale/src/lib.rs b/ui/locale/src/lib.rs
new file mode 100644
index 0000000..2028418
--- /dev/null
+++ b/ui/locale/src/lib.rs
@@ -0,0 +1,73 @@
+/*
+ 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) 2026 metamuffin <metamuffin.org>
+*/
+use std::{borrow::Cow, collections::HashMap, sync::LazyLock};
+
+macro_rules! languages {
+ ($($lang:literal),*) => {
+ #[cfg(feature = "reload")]
+ const LANGUAGES: &[&str] = &[$($lang),*];
+ #[cfg(not(feature = "reload"))]
+ const LANGUAGES: &[(&str, &str)] = &[$(($lang, include_str!(concat!("../../../locale/", $lang, ".ini")))),*];
+ };
+}
+languages!("en", "de");
+
+pub static LANG_TABLES: LazyLock<HashMap<&'static str, HashMap<&'static str, &'static str>>> =
+ LazyLock::new(|| {
+ let mut langs = LANGUAGES
+ .iter()
+ .map(|(k, v)| (*k, parse_ini(v)))
+ .collect::<HashMap<_, _>>();
+
+ let fallback = langs["en"].clone();
+ for l in langs.values_mut() {
+ for (k, fv) in &fallback {
+ if !l.contains_key(k) {
+ l.insert(k, fv);
+ }
+ }
+ }
+ langs
+ });
+
+fn parse_ini<'a>(source: &'a str) -> HashMap<&'a str, &'a str> {
+ source
+ .lines()
+ .filter_map(|line| {
+ let (key, value) = line.split_once("=")?;
+ Some((key.trim(), value.trim()))
+ })
+ .collect()
+}
+
+pub fn tr(lang: &str, key: &str) -> Cow<'static, str> {
+ let tr_map = LANG_TABLES.get(&lang).unwrap();
+ tr_map
+ .get(key)
+ .copied()
+ .unwrap_or("MISSING TRANSLATION")
+ .into()
+}
+
+pub fn escape(str: &str) -> String {
+ let mut o = String::with_capacity(str.len());
+ let mut last = 0;
+ for (index, byte) in str.bytes().enumerate() {
+ if let Some(esc) = match byte {
+ b'<' => Some("&lt;"),
+ b'>' => Some("&gt;"),
+ b'&' => Some("&amp;"),
+ b'"' => Some("&quot;"),
+ _ => None,
+ } {
+ o += &str[last..index];
+ o += esc;
+ last = index + 1;
+ }
+ }
+ o += &str[last..];
+ o
+}
diff --git a/ui/src/components/mod.rs b/ui/src/components/mod.rs
index 4d2dd62..07b050b 100644
--- a/ui/src/components/mod.rs
+++ b/ui/src/components/mod.rs
@@ -9,8 +9,11 @@ pub mod node_page;
pub mod props;
pub mod stats;
-use crate::{RenderInfo, components::message::Message};
-use jellycommon::{VIEW_MESSAGE, jellyobject::Object};
+use crate::{
+ RenderInfo,
+ components::{message::Message, node_page::NodePage},
+};
+use jellycommon::{jellyobject::Object, *};
use markup::define;
define! {
@@ -18,5 +21,8 @@ define! {
@if let Some(message) = view.get(VIEW_MESSAGE) {
@Message { ri, message }
}
+ @if let Some(nku) = view.get(VIEW_NODE_PAGE) {
+ @NodePage { ri, nku }
+ }
}
}
diff --git a/ui/src/components/node_page.rs b/ui/src/components/node_page.rs
index 4a594d6..405f50a 100644
--- a/ui/src/components/node_page.rs
+++ b/ui/src/components/node_page.rs
@@ -4,12 +4,13 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use crate::{RenderInfo, components::props::Props, locale::tr};
+use crate::{RenderInfo, components::props::Props};
use jellycommon::{
jellyobject::{Object, Tag, TypedTag},
routes::{u_image, u_node_slug_player},
*,
};
+use jellyui_locale::tr;
use std::marker::PhantomData;
markup::define! {
diff --git a/ui/src/components/props.rs b/ui/src/components/props.rs
index f23d72e..fa078e7 100644
--- a/ui/src/components/props.rs
+++ b/ui/src/components/props.rs
@@ -7,13 +7,13 @@
use crate::{
RenderInfo,
format::{format_count, format_duration},
- locale::tr,
};
use chrono::DateTime;
use jellycommon::{
jellyobject::{Object, TypedTag},
*,
};
+use jellyui_locale::tr;
use std::marker::PhantomData;
markup::define! {
diff --git a/ui/src/components/stats.rs b/ui/src/components/stats.rs
index 8dfb304..698430b 100644
--- a/ui/src/components/stats.rs
+++ b/ui/src/components/stats.rs
@@ -7,9 +7,9 @@
use crate::{
RenderInfo,
format::{format_duration, format_duration_long, format_size},
- locale::tr,
};
use jellycommon::{jellyobject::Object, *};
+use jellyui_locale::tr;
use markup::raw;
markup::define! {
diff --git a/ui/src/format.rs b/ui/src/format.rs
index 01982af..4eb8f84 100644
--- a/ui/src/format.rs
+++ b/ui/src/format.rs
@@ -8,17 +8,16 @@ use jellycommon::{
jellyobject::{Object, Tag},
*,
};
-
-use crate::locale::tr;
-use std::fmt::Write;
+use jellyui_locale::tr;
+use std::{borrow::Cow, fmt::Write};
pub fn format_duration(d: f64) -> String {
- format_duration_mode(LANG_ENG.0, d, false)
+ format_duration_mode("en", d, false)
}
-pub fn format_duration_long(lang: Language, d: f64) -> String {
+pub fn format_duration_long(lang: &str, d: f64) -> String {
format_duration_mode(lang, d, true)
}
-fn format_duration_mode(lang: Language, mut d: f64, long_units: bool) -> String {
+fn format_duration_mode(lang: &str, mut d: f64, long_units: bool) -> String {
let mut s = String::new();
let sign = if d > 0. { "" } else { "-" };
d = d.abs();
@@ -58,15 +57,15 @@ fn test_duration_short() {
#[test]
fn test_duration_long() {
assert_eq!(
- format_duration_long(LANG_ENG.0, 61.).as_str(),
+ format_duration_long("en", 61.).as_str(),
"1 minute and 1 second"
);
assert_eq!(
- format_duration_long(LANG_ENG.0, 121.).as_str(),
+ format_duration_long("en", 121.).as_str(),
"2 minutes and 1 second"
);
assert_eq!(
- format_duration_long(LANG_ENG.0, 3661.).as_str(),
+ format_duration_long("en", 3661.).as_str(),
"1 hour, 1 minute and 1 second"
);
}
@@ -74,7 +73,7 @@ fn test_duration_long() {
pub fn format_size(size: u64) -> String {
humansize::format_size(size, humansize::DECIMAL)
}
-pub fn format_kind(lang: Language, kind: Tag) -> &'static str {
+pub fn format_kind(lang: &str, kind: Tag) -> Cow<'static, str> {
tr(
lang,
match kind {
diff --git a/ui/src/lib.rs b/ui/src/lib.rs
index 72109d4..3b04b40 100644
--- a/ui/src/lib.rs
+++ b/ui/src/lib.rs
@@ -5,18 +5,15 @@
*/
mod components;
pub(crate) mod format;
-pub(crate) mod locale;
mod scaffold;
+pub use jellyui_client_scripts::*;
+pub use jellyui_client_style::*;
+
use crate::{components::View, scaffold::Scaffold};
-use jellycommon::{
- jellyobject::{Object, Tag},
- *,
-};
+use jellycommon::{jellyobject::Object, *};
use serde::{Deserialize, Serialize};
-pub type FlashM = Option<(String, String)>;
-
#[rustfmt::skip]
#[derive(Debug, Deserialize, Serialize, Default)]
pub struct Config {
@@ -27,7 +24,7 @@ pub struct Config {
pub struct RenderInfo<'a> {
pub user: Option<Object<'a>>,
- pub lang: Tag,
+ pub lang: &'a str,
pub status_message: Option<&'a str>,
pub config: &'a Config,
}
diff --git a/ui/src/locale.rs b/ui/src/locale.rs
deleted file mode 100644
index 08c73c2..0000000
--- a/ui/src/locale.rs
+++ /dev/null
@@ -1,52 +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) 2026 metamuffin <metamuffin.org>
-*/
-use jellycommon::*;
-use std::{collections::HashMap, sync::LazyLock};
-
-pub static LANG_TABLES: LazyLock<HashMap<Language, HashMap<&'static str, &'static str>>> =
- LazyLock::new(|| {
- let mut k = HashMap::new();
- for (lang, source) in [
- (LANG_ENG.0, include_str!("../../locale/en.ini")),
- (LANG_DEU.0, include_str!("../../locale/de.ini")),
- ] {
- // TODO fallback to english
- let tr_map = source
- .lines()
- .filter_map(|line| {
- let (key, value) = line.split_once("=")?;
- Some((key.trim(), value.trim()))
- })
- .collect::<HashMap<&'static str, &'static str>>();
- k.insert(lang, tr_map);
- }
- k
- });
-
-pub fn tr(lang: Language, key: &str) -> &'static str {
- let tr_map = LANG_TABLES.get(&lang).unwrap();
- tr_map.get(key).copied().unwrap_or("MISSING TRANSLATION")
-}
-
-pub fn escape(str: &str) -> String {
- let mut o = String::with_capacity(str.len());
- let mut last = 0;
- for (index, byte) in str.bytes().enumerate() {
- if let Some(esc) = match byte {
- b'<' => Some("&lt;"),
- b'>' => Some("&gt;"),
- b'&' => Some("&amp;"),
- b'"' => Some("&quot;"),
- _ => None,
- } {
- o += &str[last..index];
- o += esc;
- last = index + 1;
- }
- }
- o += &str[last..];
- o
-}
diff --git a/ui/src/scaffold.rs b/ui/src/scaffold.rs
index fee311a..8cfb432 100644
--- a/ui/src/scaffold.rs
+++ b/ui/src/scaffold.rs
@@ -4,10 +4,7 @@
Copyright (C) 2026 metamuffin <metamuffin.org>
*/
-use crate::{
- RenderInfo,
- locale::{escape, tr},
-};
+use crate::RenderInfo;
use jellycommon::{
routes::{
u_account_login, u_account_logout, u_account_register, u_account_settings,
@@ -15,6 +12,7 @@ use jellycommon::{
},
user::{USER_ADMIN, USER_NAME},
};
+use jellyui_locale::{escape, tr};
use markup::{Render, raw};
markup::define! {