All checks were successful
Build and Publish Docker Image / build-and-push (push) Successful in 43s
The player now holds a queue instead of a single item, so albums play through and single tracks share the same code path. Adds a "stream" item kind for expiring, remotely-hosted sources: Bandcamp signs its mp3 URLs for ~24h, so an item carries the page it came from and re-resolves itself on error or on a stale restored session, falling back to the iframe embed if that fails too. Gated behind GERBEUR_BANDCAMP_PLAYER (default "embed"), injected into a meta tag the same way as site-name/site-emoji. Also: player header gains artwork and a subtitle, volume persists across queue advances and reloads, and cross-origin streams get a plain seek bar rather than a waveform that can never decode. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SbtjNsT5wvuABnfqhegZEJ
63 lines
2.1 KiB
TypeScript
63 lines
2.1 KiB
TypeScript
import { defineConfig } from "vite";
|
||
import react from "@vitejs/plugin-react-swc";
|
||
import { lingui } from "@lingui/vite-plugin";
|
||
|
||
const SITE_NAME = process.env.GERBEUR_SITE_NAME || "gerbeur";
|
||
const SITE_EMOJI = process.env.GERBEUR_SITE_EMOJI || "🚚";
|
||
const BANDCAMP_PLAYER = process.env.GERBEUR_BANDCAMP_PLAYER?.trim() === "native" ? "native" : "embed";
|
||
|
||
// Cache-busting token for the favicon URLs (see index.html). Changes when the
|
||
// emoji changes, so browsers don't keep serving a stale cached icon. Mirrors
|
||
// Twemoji's filename convention (kept trivial here to avoid the frontend build
|
||
// depending on backend code; the canonical version lives in api/lib/site-icons.ts).
|
||
function emojiToCodepoint(emoji: string): string {
|
||
const cleaned = emoji.includes("") ? emoji : emoji.replace(/️/g, "");
|
||
return [...cleaned].map((ch) => ch.codePointAt(0)!.toString(16)).join("-");
|
||
}
|
||
const ICON_VERSION = emojiToCodepoint(SITE_EMOJI);
|
||
|
||
export default defineConfig({
|
||
server: {
|
||
port: 3000,
|
||
watch: {
|
||
ignored: ["**/api/**"],
|
||
},
|
||
},
|
||
build: {
|
||
rollupOptions: {
|
||
output: {
|
||
manualChunks(id) {
|
||
if (id.includes("react-markdown") || id.includes("remark-gfm") || id.includes("remark-parse") || id.includes("mdast") || id.includes("micromark") || id.includes("unist")) {
|
||
return "vendor-markdown";
|
||
}
|
||
if (id.includes("node_modules")) {
|
||
return "vendor";
|
||
}
|
||
},
|
||
},
|
||
},
|
||
},
|
||
plugins: [
|
||
{
|
||
// In dev the API server isn't serving index.html, so replace the placeholders here.
|
||
// In production the Oak server does the replacement at runtime (see api/lib/static.ts).
|
||
name: "inject-site-name",
|
||
transformIndexHtml: {
|
||
order: "pre",
|
||
handler: (html, ctx) =>
|
||
ctx.server
|
||
? html
|
||
.replaceAll("__SITE_NAME__", SITE_NAME)
|
||
.replaceAll("__SITE_EMOJI__", SITE_EMOJI)
|
||
.replaceAll("__ICON_VERSION__", ICON_VERSION)
|
||
.replaceAll("__BANDCAMP_PLAYER__", BANDCAMP_PLAYER)
|
||
: html,
|
||
},
|
||
},
|
||
lingui(),
|
||
react({
|
||
plugins: [["@lingui/swc-plugin", {}]],
|
||
}),
|
||
],
|
||
});
|