Author SHA1 Message Date
teahubxyzandClaude Opus 5.5 4506a1b3de plugins/webamp: self-host the Webamp bundle
webamp@1.5.0 ships no .mjs build, so the unpkg import 404'd (seen in the
browser as a CORS error). Load the UMD bundle served from Teahub's own
assets instead; bump cache key to v=011.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HLEj1MiGohaV4qJE4NE1Xq
2026-10-22 18:30:30 +00:00
teahubxyz 6baa7127f9 plugins: add webamp, the first Teahub plugin (#22) 2026-10-22 18:24:44 +00:00
4 changed files with 108 additions and 0 deletions

No files matched your search

+13
View File
@@ -0,0 +1,13 @@
# Teahub plugins
Each folder is one plugin: a `plugin.json` manifest plus the files it drops into Gitea's custom dir (`$GITEA_CUSTOM`, `/data/gitea` on prod).
- `slots` — templates appended into Gitea's custom template slots (e.g. `footer`).
- `assets` — files served under `/assets/...`.
- `config` — plugin settings.
Install today is manual: back up the slot template, append the plugin's template, copy assets, restart Gitea. An install Action that reads the manifest is next.
| Plugin | Status |
|---|---|
| webamp | live on prod |
+16
View File
@@ -0,0 +1,16 @@
{
"name": "webamp",
"title": "Webamp",
"version": "0.1.1",
"description": "A Winamp-style player on every Teahub page, fed by gandora.",
"slots": {
"footer": "templates/custom/footer.tmpl"
},
"assets": ["public/assets/plugins/webamp/webamp-mod.js"],
"vendor": {
"public/assets/plugins/webamp/webamp.bundle.min.js": "https://registry.npmjs.org/webamp/-/webamp-1.5.0.tgz#package/built/webamp.bundle.min.js"
},
"config": {
"playlist_url": "https://gandora.yolopreneur.lol/playlist.json"
}
}
@@ -0,0 +1,77 @@
// teahub plugin: webamp — a Winamp-style player docked on every Teahub page.
// Loads the self-hosted Webamp bundle on first click, so pages stay fast for everyone else.
// Module scripts have no document.currentScript, so find our tag by its src.
const script = document.querySelector('script[src*="plugins/webamp/webamp-mod.js"]');
const PLAYLIST_URL = script?.dataset.playlist;
const KEY = 'teahub-webamp-open';
const FALLBACK = [
{
metaData: { artist: 'DJ Mike Llama', title: "Llama Whippin' Intro" },
url: 'https://cdn.jsdelivr.net/gh/captbaritone/webamp@43434d82/mp3/llama-2.91.mp3',
duration: 5.322286,
},
];
async function loadTracks() {
if (!PLAYLIST_URL) return FALLBACK;
try {
const res = await fetch(PLAYLIST_URL, { mode: 'cors' });
if (!res.ok) throw new Error(res.status);
const list = await res.json();
// gandora format: [{ title, artist, url, duration? }]
const tracks = list
.filter((t) => t && t.url)
.map((t) => ({
metaData: { artist: t.artist || 'gandora', title: t.title || 'untitled' },
url: t.url,
...(t.duration ? { duration: t.duration } : {}),
}));
return tracks.length ? tracks : FALLBACK;
} catch {
return FALLBACK;
}
}
let webamp = null;
function loadWebamp() {
if (window.Webamp) return Promise.resolve(window.Webamp);
return new Promise((ok, fail) => {
const s = document.createElement('script');
s.src = new URL('webamp.bundle.min.js', import.meta.url).href;
s.onload = () => ok(window.Webamp);
s.onerror = fail;
document.head.appendChild(s);
});
}
async function open() {
if (webamp) return;
const Webamp = await loadWebamp();
if (!Webamp.browserIsSupported()) return;
webamp = new Webamp({ initialTracks: await loadTracks() });
webamp.onClose(() => {
webamp.dispose();
webamp = null;
try { localStorage.removeItem(KEY); } catch {}
});
await webamp.renderWhenReady(document.body.appendChild(document.createElement('div')));
try { localStorage.setItem(KEY, '1'); } catch {}
}
// Small launcher in the corner: the "sound system" button.
const btn = document.createElement('button');
btn.textContent = '▶ webamp';
btn.title = 'Open Webamp';
btn.style.cssText = [
'position:fixed', 'right:12px', 'bottom:12px', 'z-index:9999',
'font:12px/1 monospace', 'padding:6px 10px', 'border-radius:4px',
'border:1px solid #1d3', 'background:#000', 'color:#1d3', 'cursor:pointer',
].join(';');
btn.addEventListener('click', open);
document.body.appendChild(btn);
// Reopen if it was open on the last page.
try { if (localStorage.getItem(KEY)) open(); } catch {}
@@ -0,0 +1,2 @@
{{/* teahub plugin: webamp */}}
<script type="module" nonce="{{ctx.CspScriptNonce}}" src="{{AssetUrlPrefix}}/plugins/webamp/webamp-mod.js?v=011" data-playlist="https://gandora.yolopreneur.lol/playlist.json"></script>