diff options
author | Aleteoryx <alyx@aleteoryx.me> | 2024-07-29 19:17:03 +0100 |
---|---|---|
committer | Aleteoryx <alyx@aleteoryx.me> | 2024-07-29 19:17:03 +0100 |
commit | 4f3556a3030ba97d3eec590cdd5d1e7046f53588 (patch) | |
tree | 58d99f1d307d2a3faa801c1e9009126bdb82157a | |
parent | e9ca1e6b14fe635fb940f5bae8560c68acca3d12 (diff) | |
download | lfm_embed-4f3556a3030ba97d3eec590cdd5d1e7046f53588.tar.gz lfm_embed-4f3556a3030ba97d3eec590cdd5d1e7046f53588.tar.bz2 lfm_embed-4f3556a3030ba97d3eec590cdd5d1e7046f53588.zip |
-rw-r--r-- | README.md | 403 |
1 files changed, 1 insertions, 402 deletions
@@ -1,402 +1 @@ -# `lfm_embed` -A simple webserver for rendering a last.fm embed. - -More specifically, this displays a simple webpage with info about your current or most recent last.fm scrobble. -Its intended use is to be put in an iframe on a personal site or whatever. -While it is self-hostable, there is an official public instance at [scrobble.observer](https://scrobble.observer). - -Release builds available [here](https://aleteoryx.me/downloads2/lfm_embed). - -Index: - -- [Usage](#usage) -- [Self-Hosting](#self-hosting) -- [Configuration](#configuration) -- [Theming](#theming) -- [Contributing](#contributing) -- [Legal](#legal) - -# Usage - -`lfm_embed` serves one thing: -Requests to `/user/<last.fm username>` will generate a page displaying your current or last last.fm scrobble. -The look and feel of this is down to the configured theme for your request. - -As the user, you may select a theme supported by the server by passing `?theme=` query string. -Check with your host for a list of supported themes. - -Individual themes may also support parameters, via additional query parameters. - -## Builtin Themes - -### `plain` - -A minimal theme, displaying just the username, album art, and track info. -It is the default fallback for an unset or unknown theme. - -![A screenshot of the plain theme, displaying album art, and the text "@vvinrg is scrobbling Sweet Tooth from Sleepyhead by Cavetown".](screenshots/plain-light.png) - -#### Parameters - -- `?dark`: toggle the theme's dark mode. - -# Self-Hosting -`lfm_embed` is, as it stands, designed to use a reverse proxy. -A future release may include HTTPS support, but currently, it will only listen on `localhost`, prohibiting it from public access. -Once configured, a request of the form `http://localhost:9999/user/<last.fm username>` will render out an embed with the default theme. - -As it stands, there are no plans to support displaying users with private listen history. - -# Configuration -Configuration should be done via the environment. -`lfm_embed` also supports reading from a standard `.env` file. -The following are the environment variables which `lfm_embed` understands. - -## Core Config - -### `LMFE_LASTFM_API_KEY` (Required) -Your last.fm API key. You'll need to create one [here](https://www.last.fm/api/account/create) for self-hosting. - -### `LMFE_GOOGLE_API_KEY` -Your google API key. This is necessary for custom google fonts to work. You can create one [here](https://developers.google.com/fonts/docs/developer_api#APIKey), and you'll need some familiarity with the Google cloud API console - -### `LFME_PORT` (Default: `9999`) -The port to serve on locally. - -### `LFME_NO_REFRESH` (Default: `0`) -If set to `1`, disable outputting of the HTTP `Refresh: <cache refresh>` header, used to provide live status updates. - -## Logging - -### `LFME_LOG_LEVEL` (Default: `"warn"`) -The loglevel. This is actually parsed as an [`env_logger`](https://docs.rs/env_logger/latest/env_logger) filter string. -Read the docs for more info. - -### `LFME_LOG_FILE` -If set, logs will be written to the specified file. Otherwise, logs are written to `stderr`. - -## User Perms - -### `LFME_ALLOWLIST_MODE` (Default: `"open"`) -The following(case-insensitive) values are supported: - -- `open`: Allow requests for all users. - -- `exclusive`: Only allow requests for users in `LFME_ALLOWLIST`, returning HTTP 403 for all others. `LFME_ALLOWLIST` _must_ be set if this mode is enabled. - -If the user requested has their listen history private, a 403 will be returned. - -### `LFME_ALLOWLIST` (Default: `""`) -This is expected to be a sequence of comma-separated usernames. -Leading/trailing whitespace will be stripped, and unicode is fully supported. - -### `LFME_ALLOWLIST_REFRESH` (Default: `"1m"`) -The amount of time to cache allowlisted user info for. -It is interpreted as a sequence of `<num><suffix>` values, where `num` is a positive integer, -and `suffix` is one of `y`,`mon`,`w`,`d`,`h`,`m`,`s`, `ms`, `µs`, or `ns`, each of which -corresponds to a self-explanatory unit of time. -For most practical applications, one should only use `m` and `s`, as caching your current listen for more than that time has the potential to omit most songs. -Parsing is delegated to the [`duration_str`](https://docs.rs/duration-str/latest/duration_str/) crate, and further info may be found there. - -### `LFME_DEFAULT_REFRESH` (Default: `"5m"`) -The amount of time to cache non-allowlisted user info for. -See `LFME_ALLOWLIST_REFRESH` for more info. - -## Themes - -### `LFME_THEME_DIR` -If set, must be a valid path to a directory containing a tree of [Handlebars](https://handlebarsjs.com/guide/#language-features) and [Lua](https://www.lua.org/) files, ending in `LFME_THEME_EXT_HBS` and `LFME_THEME_EXT_LUA`, respectively. -They will be registered as themes on top of the builtin ones, with each theme's name being their filename minus the extension. -Same-named themes will override builtin ones. - -See [Theming](#theming) for details on writing custom themes. - -Theme names are the same as their path, minus the extension. A directory like: -``` -themes/ - mytheme.hbs - myothertheme.lua - myunrelatedfile.css - alices-themes/ - mytheme.lua - mysuperawesometheme.hbs -``` -results in the following themes: -``` -mytheme -myothertheme -alices-themes/mytheme -alices-themes/mysuperawesometheme -``` - -By default, these are loaded and compiled once, at startup. - -### `LFME_THEME_EXT_HBS` (Default: `.hbs`) -The file extension for handlebars themes in `LFME_THEME_DIR`. - -Note: This behaves more like a suffix than a file extension. You must include the leading dot for a file extension. - -### `LFME_THEME_EXT_LUA` (Default: `.lua`) -The file extension for lua themes in `LFME_THEME_DIR`. - -Note: This behaves more like a suffix than a file extension. You must include the leading dot for a file extension. - -### `LFME_THEME_DEV` (Default: `0`) -If set to `1`, themes will be reloaded on edit. - -Note: Even with this mode, adding a new handlebars theme requires a full reload. -Handlebars hemes are only enumerated once, at startup. (This is a limitation of the [`handlebars`](https://docs.rs/handlebars/latest/handlebars) implementation in use, and may change.) - -### `LFME_THEME_DEFAULT` (Default: `"plain"`) -The theme to use when no query string is present. - -## Example Configuration -```ini -LFME_API_KEY=0123456789abcdef0123456789abcdef -LFME_LOG_LEVEL=error -LFME_PORT=3000 - -LFME_ALLOWLIST_REFRESH=30s -LFME_ALLOWLIST_MODE=exclusive -LFME_ALLOWLIST=a_precious_basket_case, realRiversCuomo, Pixiesfan12345 -``` - -# Theming - -Custom themes are, as stated above, [Handlebars](https://handlebarsjs.com/guide/#language-features) and [Lua](https://www.lua.org/) files. -They are expected to produce a complete HTML doc, which should contain info about a user's scrobbles. - -You should have a passing familiarity with either language before reading below. - -## Handlebars Themes - -Handlebars is a simple templating language, and great for themes that don't do much complicated. -The builtin `plain` theme is written in handlebars, and any themes that want to just relay the basic scrobble info should be written in it. - -If you're looking to do more complicated effects, or need server-side computation, see below on writing [Lua Themes](#lua-themes). -Handlebars should __not__ be used if you're looking to do advanced effects. - -All builtin handlebars themes are located at [`/src/theming/hbs`](https://git.aleteoryx.me/cgit/lfm_embed/tree/src/theming/hbs). - -The following [helpers](https://handlebarsjs.com/guide/expressions.html#helpers) are provided, on top of the handlebars [builtins](https://handlebarsjs.com/guide/builtin-helpers.html): - -- `(eq Object Object) => Boolean`: Check equality between its args. - -- `(ne Object Object) => Boolean`: Check inequality between its args. - -- `(gt Number|String Number|String) => Boolean`: Check if the first arg is greater than the second. - -- `(gte Number|String Number|String) => Boolean`: Check if the first arg is greater than or equal to the second. - -- `(lt Number|String Number|String) => Boolean`: Check if the first arg is less than the second. - -- `(lte Number|String Number|String) => Boolean`: Check if the first arg is less than or equal to the second. - -- `(and Boolean Boolean) => Boolean`: Boolean AND gate. - -- `(or Boolean Boolean) => Boolean`: Boolean OR gate. - -- `(not Boolean) => Boolean`: Boolean NOT gate. - -- `(uri_encode String) => String`: URI-encode input text, making the output suitable to be included as part of a link or other URL. - -### Writing a Handlebars Theme - -Handlebars themes should have, roughly, the skeleton below: - -```html -<!DOCTYPE html> -<html lang="en"> <!-- Or really whatever you want, but I speak English. --> - <head> - <meta charset="UTF-8"> - <style> - {{#if font.css}} - {{{font.css}}} - {{/if}} - /* cursed hack to deal with weird rendering in chrome */ - * { font-size: {{#if font}}17px{{else}}{{#if font.scale}}calc(20px * {{font.scale}}){{else}}20px{{/if}}{{/if}}; } - {{#if font.name }} - * { font-family: '{{font.name}}' } - {{/if}} - /* actual styles */ - </style> - </head> - <body> - {{#if error}}<p>{{error}}</p>{{else}} - <!-- theme contents here --> - {{/if}} - </body> -</html> -``` - -See below for the [Context Object](#the-context-object) - -## Lua Themes - -Lua is a powerful scripting language, for themes that need to do a lot of math on the server. -`lfm_embed` uses the [Luau](https://luau-lang.org/) implementation, specicially. - -You are provided with only the `table`, `string`, `utf8`, `bit`, and `math` stdlib modules. -You are also provided with the `html` global. - -- `html(el: String, tbl: Table) => String`: Generates the HTML for `<el>`, with the sequential entries in `tbl` as concatenated text, and the named entries as attributes. Sequential entries wrapped in a table will be escaped. - -- `html.<el>(tbl: Table) => String` Functions like `html`, but with the index name as the element type. It is intended to be called like `html.a{"Homepage", href = "/"}`. - -- `html.root(tbl: Table) => String` Generates the root of an HTML document, including doctype, processing `tbl` as normal. - -Example: - -```lua -print(html.root{html.body{html.p{"Check out my cool site!"}}}) --- <!DOCTYPE html> --- <html><body><p>Check out my cool site!</p></body></html> - -print(html.p{ "This <b>doesn't</b> get escaped, ", {"but this <b>does</b>!"} }) --- <p>This <b>doesn't</b> get escaped, but this <b>does</b>!</p> -``` - -### Writing a Lua Theme - -Lua theme scripts are expected to return a function which takes one argument, the [Context Object](#the-context-object), and return a fully formed html document. -They may contain anything as far as locals go, but are not allowed to manipulate the global context. -The returned function will be repeatedly called for any incoming request. - -Lua themes should have, roughly, the skeleton below: - -```lua -local stylefmt = [[ -/* font css */ -%s -* { font-size: %spx; } -%s -/* actual styles go here*/ -]] - -return function(ctx) - local contents - local style - if ctx.error then - contents = html.p{{ctx.error}} - style = "" - else - contents = "" - if not ctx.font then ctx.font = {} end - local font_setter = "" - if ctx.font.name then - font_setter = string.format("* { font-family: '%s'; }", ctx.font.name) - end - local font_px - -- hack to deal with chrome being weird - if ctx.font then font_px = 17 - elseif ctx.font.scale then font_px = 20 * ctx.font.scale - else font_px = 20 end - style = string.format(stylefmt, ctx.font.css or "", font_px, font_setter) - end - return html.root{lang = "en", - html.head{ - html.meta{charset = "UTF-8"}, - html.style{style} - }, - html.body{contents} - } -end -``` - -## The Context Object - -You will be passed one of 2 context objects, depending on if there was an error dealing with the remote backends. -If there was an error, the object will just be `{ error: String }`, otherwise it will be the following: - -```js -{ - // The user the request was made on the behalf of. - user: { - // Their username. - name: String, - // Their "Display Name". - realname: String, - // Link to user's profile picture. - image_url: String, - // Link to user's profile. - url: String, - - // Total scrobbles. - scrobble_count: Number, - // Number of artists in library. - artist_count: Number, - // Number of tracks in library. - track_count: Number, - // Number of albums in library. - album_count: Number, - - // True if user subscribes to last.fm pro. - pro_subscriber: Boolean - }, - - // The user's most current, or most scrobble. - scrobble: { - // The name of the track. - name: String, - // The name of its album. - album: String, - // The artist who made it. - artist: { - // Their name. - name: String, - // Link to their profile image. - image_url: String, - // Link to the artist's last.fm page. - url: String - }, - // A link to the track image. - image_url: String, - // A link to the track's last.fm page. - url: String, - - // True if the user has loved the track. - loved: Boolean, - // True if the user is currently scrobbling it, false if it's just - // 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?, - // Will contain a factor of the base font scale to scale the theme's font by, for custom font reasons. - scale: Number? - }, - - // A set of extraneous query parameters. - // - // This should be considered UNTRUSTED, as the requester has full - // control over it. Use the provided `html_escape`, - // `html_attr_escape`, and `uri_encode` helpers when inlining - // any contained text. - query: Object -} -``` - -# Contributing - -[E-Mail me](mailto:alyx@aleteoryx.me) if you'd like to help out, submit a custom theme, or request a feature. - -# Legal -```txt - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program. If not, see <https://www.gnu.org/licenses/>. -``` +All development has been moved to [amehut](https://amehut.dev/~aleteoryx/scrobble.observer/), this repository is no longer used. |