aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoralyx <alyx@aleteoryx.me>2023-11-22 19:49:27 -0500
committeralyx <alyx@aleteoryx.me>2023-11-22 19:49:27 -0500
commit41a5fa81b5c12feb8aab0ad82d42c135e93065f7 (patch)
treebfc1f1e7ba3d35acff28df0c3e369a32d163dcfa
parentbe166d93b7aabb5b260e21c10b1b247704858193 (diff)
downloadlfm_embed-41a5fa81b5c12feb8aab0ad82d42c135e93065f7.tar.gz
lfm_embed-41a5fa81b5c12feb8aab0ad82d42c135e93065f7.tar.bz2
lfm_embed-41a5fa81b5c12feb8aab0ad82d42c135e93065f7.zip
Add custom font support.
-rw-r--r--README.md20
-rw-r--r--src/config.rs2
-rw-r--r--src/ctx.rs21
-rw-r--r--src/font.rs10
-rw-r--r--src/lib.rs1
-rw-r--r--src/main.rs6
-rw-r--r--src/themes/plain.hbs11
7 files changed, 61 insertions, 10 deletions
diff --git a/README.md b/README.md
index 739b94a..4d5ae8f 100644
--- a/README.md
+++ b/README.md
@@ -160,7 +160,15 @@ Themes should have, roughly, the structure below:
<html lang="en"> <!-- Or really whatever you want, but I speak English. -->
<head>
<meta charset="UTF-8">
- <style> /* styles */ </style>
+ <style>
+ {{#if font.css}}
+ {{{font.css}}}
+ {{/if}}
+ {{#if (or font.name font.css)}}
+ * { font-family: '{{#if font.name}}{{font.name}}{{/if}}{{#if font.url}}included_font{{/if}}' }
+ {{/if}}
+ /* actual styles */
+ </style>
</head>
<body>
{{#if error}}<p>{{error}}</p>{{else}}
@@ -225,7 +233,15 @@ If there was an error, the object will just be `{ error: String }`, otherwise it
// the most recently played track.
now_playing: Boolean,
},
-
+
+ // Custom font info.
+ font: {
+ // Set if the theme should replace the custom font with something.
+ name: String?
+ // Will contain CSS which includes a custom font as 'included_font'.
+ css: String?
+ }
+
// A set of extraneous query parameters.
//
// This should be considered UNTRUSTED, as the requester has full
diff --git a/src/config.rs b/src/config.rs
index 3b36ad2..4c5d4b7 100644
--- a/src/config.rs
+++ b/src/config.rs
@@ -62,7 +62,7 @@ pub struct State {
http: Client,
default_refresh: Duration,
- whitelist_refresh: Duration
+ whitelist_refresh: Duration,
}
impl State {
diff --git a/src/ctx.rs b/src/ctx.rs
index 63b54ff..904667d 100644
--- a/src/ctx.rs
+++ b/src/ctx.rs
@@ -2,6 +2,7 @@ use reqwest::StatusCode;
use super::deserialize as de;
use std::sync::Arc;
use std::collections::BTreeMap;
+use super::font::FontQuery;
pub mod model {
use std::sync::Arc;
@@ -66,6 +67,13 @@ pub mod model {
pub loved: bool
}
+ #[derive(serde::Serialize, Debug)]
+ #[serde(untagged)]
+ pub enum Font {
+ Url { css: Arc<str> },
+ Name { name: Arc<str> },
+ }
+
/// The context passed in to all themes.
///
/// Serialized as untagged, so themes should check if `error` is set and decide whether to show an error state or try rendering user info.
@@ -75,15 +83,15 @@ pub mod model {
/// Contains text explaining a potential error.
Error { error: &'static str },
/// Contains data about a user and what they're listening to.
- Data { user: User, scrobble: Scrobble, query: BTreeMap<String, String> }
+ Data { user: User, scrobble: Scrobble, font: Option<Font>, query: BTreeMap<String, String>, }
}
}
#[derive(Debug)]
pub struct ResponseCtx(pub model::Data, pub StatusCode);
-impl From<(Result<Arc<(de::User, de::Track)>, (StatusCode, &'static str)>, BTreeMap<String, String>)> for ResponseCtx {
- fn from(v: (Result<Arc<(de::User, de::Track)>, (StatusCode, &'static str)>, BTreeMap<String, String>)) -> ResponseCtx {
- let (v, q) = v;
+impl From<(Result<Arc<(de::User, de::Track)>, (StatusCode, &'static str)>, Option<FontQuery>, BTreeMap<String, String>)> for ResponseCtx {
+ fn from(v: (Result<Arc<(de::User, de::Track)>, (StatusCode, &'static str)>, Option<FontQuery>, BTreeMap<String, String>)) -> ResponseCtx {
+ let (v, f, q) = v;
match v {
Ok(a) => {
let (user, track) = a.as_ref();
@@ -115,6 +123,11 @@ impl From<(Result<Arc<(de::User, de::Track)>, (StatusCode, &'static str)>, BTree
url: track.url.clone(),
loved: track.loved.unwrap_or(false)
},
+ font: match f {
+ Some(FontQuery { font: Some(s), include_font: None }) => Some(model::Font::Name { name: s }),
+ Some(FontQuery { include_font: Some(s), .. }) => Some(model::Font::Url { css: format!("@font-face {{ font-family: 'included_font'; src: url('{}'); }}", s.replace("\\", "\\\\").replace("'", "\\'")).into() }),
+ _ => None,
+ },
query: q
}, StatusCode::OK)
},
diff --git a/src/font.rs b/src/font.rs
new file mode 100644
index 0000000..f9f4f4d
--- /dev/null
+++ b/src/font.rs
@@ -0,0 +1,10 @@
+use std::sync::Arc;
+
+#[derive(serde::Deserialize, Debug, Default)]
+#[serde(default)]
+#[serde(rename = "kebab-case")]
+pub struct FontQuery {
+ pub font: Option<Arc<str>>,
+ pub include_font: Option<Arc<str>>,
+// pub small_font: Option<()>
+}
diff --git a/src/lib.rs b/src/lib.rs
index 0d1d299..8226852 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -4,6 +4,7 @@ pub mod deserialize;
pub mod cache;
pub mod config;
pub mod ctx;
+pub mod font;
pub use config::STATE;
pub use ctx::ResponseCtx;
diff --git a/src/main.rs b/src/main.rs
index b787049..f8b0e7b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -6,6 +6,7 @@ use std::collections::BTreeMap;
use std::fs::File;
use std::sync::Arc;
use lfm_embed::{STATE, ResponseCtx};
+use lfm_embed::font::FontQuery;
use warp::Filter;
#[derive(serde::Deserialize, Debug)]
@@ -13,6 +14,9 @@ struct UserQuery {
#[serde(default)]
theme: Option<Arc<str>>,
#[serde(flatten)]
+ #[serde(default)]
+ font: Option<FontQuery>,
+ #[serde(flatten)]
rest: BTreeMap<String, String>
}
@@ -40,7 +44,7 @@ async fn main() {
.then(|s, q: UserQuery| async move {
log::debug!(target: "lfm::server::user", "Handling request for user `{s}` with {q:?}");
let (ctx, dur) = STATE.get_userinfo(&s).await;
- let ResponseCtx(mut data, status) = (ctx, q.rest).into();
+ let ResponseCtx(mut data, status) = (ctx, q.font, q.rest).into();
let theme = q.theme.filter(|a| STATE.handlebars().has_template(&a)).unwrap_or_else(|| STATE.default_theme());
log::debug!(target: "lfm::server::user", "Using theme {theme}");
diff --git a/src/themes/plain.hbs b/src/themes/plain.hbs
index fe33d28..bbd0589 100644
--- a/src/themes/plain.hbs
+++ b/src/themes/plain.hbs
@@ -1,5 +1,5 @@
<!doctype html>
-<html lang="en" style="font-size: 0.5cm; margin: 0.5rem; overflow: hidden;">
+<html lang="en" style="margin: 20px; overflow: hidden;">
<head>
<meta charset="UTF-8"/>
<title>{{#if error}}Error!{{else}}@{{user.name}}'s Last.fm Stats{{/if}}</title>
@@ -11,6 +11,13 @@
a:visited { color: pink }
a { color: cyan; }
{{/if}}
+ * { font-size: {{#if font}}17px{{else}}20px{{/if}}; }
+ {{#if font.css}}
+ {{{font.css}}}
+ {{/if}}
+ {{#if (or font.name font.css)}}
+ * { font-family: '{{#if font.name}}{{font.name}}{{/if}}{{#if font.url}}included_font{{/if}}' }
+ {{/if}}
p { margin: 0px; padding: 0px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
</style>
</head>
@@ -18,7 +25,7 @@
{{#if error}}
<p style="white-space: unset;">{{error}}</p>
{{else}}
- <a style="float: left;" target="_blank" href="{{scrobble.url}}"><img src="{{scrobble.image_url}}" style="height: 4.0rem; border: solid var(--b) 0.2rem; margin: 0.1rem;" /></a>
+ <a style="float: left;" target="_blank" href="{{scrobble.url}}"><img src="{{scrobble.image_url}}" style="height: 80px; border: solid var(--b) 4px; margin: 2px;" /></a>
<p><a target="_blank" href="{{user.url}}">@{{user.name}}</a>{{#if scrobble.now_playing}}
is scrobbling{{else}}'s last scrobble was
{{/if}}