diff options
author | alyx <alyx@aleteoryx.me> | 2023-11-22 19:49:27 -0500 |
---|---|---|
committer | alyx <alyx@aleteoryx.me> | 2023-11-22 19:49:27 -0500 |
commit | 41a5fa81b5c12feb8aab0ad82d42c135e93065f7 (patch) | |
tree | bfc1f1e7ba3d35acff28df0c3e369a32d163dcfa | |
parent | be166d93b7aabb5b260e21c10b1b247704858193 (diff) | |
download | lfm_embed-41a5fa81b5c12feb8aab0ad82d42c135e93065f7.tar.gz lfm_embed-41a5fa81b5c12feb8aab0ad82d42c135e93065f7.tar.bz2 lfm_embed-41a5fa81b5c12feb8aab0ad82d42c135e93065f7.zip |
Add custom font support.
-rw-r--r-- | README.md | 20 | ||||
-rw-r--r-- | src/config.rs | 2 | ||||
-rw-r--r-- | src/ctx.rs | 21 | ||||
-rw-r--r-- | src/font.rs | 10 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/main.rs | 6 | ||||
-rw-r--r-- | src/themes/plain.hbs | 11 |
7 files changed, 61 insertions, 10 deletions
@@ -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 { @@ -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<()> +} @@ -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}} |