Compare commits
5
Commits
plugins/webamp
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
049718d078 | ||
|
|
091a9e002f | ||
|
|
fd08195cdc | ||
|
|
917f69fbce | ||
|
|
6baa7127f9 |
No files matched your search
+1
-1
@@ -6,7 +6,7 @@ primary_region = "iad"
|
|||||||
|
|
||||||
[env]
|
[env]
|
||||||
GITEA_URL = "https://teahub.xyz"
|
GITEA_URL = "https://teahub.xyz"
|
||||||
PAGES_SITES = '{"persimmons.app":"persimmons/app","phly.cloud":"phly/site","supertestnet.lol":"supertestnet/app#pages","yolopreneur.lol":"teahubxyz/time#gh-pages/ventures","gandora.lol":"gandora/site","www.gandora.lol":"gandora/site","gandora.yolopreneur.lol":"gandora/site","dev.gandora.lol":"gandora/site#dev","asmr.gandora.lol":"gandora/site#main/asmr"}'
|
PAGES_SITES = '{"persimmons.app":"persimmons/app","phly.cloud":"phly/site","supertestnet.lol":"supertestnet/app#pages","yolopreneur.lol":"teahubxyz/time#gh-pages/ventures","gandora.lol":"gandora/site","www.gandora.lol":"gandora/site","gandora.yolopreneur.lol":"gandora/site","dev.gandora.lol":"gandora/site#dev","asmr.gandora.lol":"gandora/site#main/asmr","maths.teahub.xyz":"maths/site"}'
|
||||||
PAGES_WILDCARDS = '{"yolopreneur.lol":"teahubxyz/time#gh-pages/ventures"}'
|
PAGES_WILDCARDS = '{"yolopreneur.lol":"teahubxyz/time#gh-pages/ventures"}'
|
||||||
PAGES_PULL_SECONDS = "300"
|
PAGES_PULL_SECONDS = "300"
|
||||||
|
|
||||||
|
|||||||
@@ -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 |
|
||||||
@@ -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,109 @@
|
|||||||
|
// teahub plugin: webamp — a Winamp-style player docked on every Teahub page.
|
||||||
|
// Loads Webamp from the CDN on first click, so pages stay fast for everyone else.
|
||||||
|
|
||||||
|
// Module scripts have no document.currentScript, so find our tag by its data attribute.
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Load the UMD bundle as a classic script. Hide any AMD/CommonJS globals on the page
|
||||||
|
// while it runs, so the bundle always attaches itself to window.Webamp.
|
||||||
|
function loadWebamp() {
|
||||||
|
if (window.Webamp) return Promise.resolve(window.Webamp);
|
||||||
|
return new Promise((ok, fail) => {
|
||||||
|
const saved = { define: window.define, module: window.module, exports: window.exports };
|
||||||
|
window.define = window.module = window.exports = undefined;
|
||||||
|
const restore = () => Object.assign(window, saved);
|
||||||
|
const s = document.createElement('script');
|
||||||
|
s.src = new URL('webamp.bundle.min.js', import.meta.url).href;
|
||||||
|
s.onload = () => {
|
||||||
|
restore();
|
||||||
|
window.Webamp ? ok(window.Webamp) : fail(new Error('Webamp global missing after load'));
|
||||||
|
};
|
||||||
|
s.onerror = () => { restore(); fail(new Error('could not load ' + s.src)); };
|
||||||
|
document.head.appendChild(s);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async function open() {
|
||||||
|
if (webamp) {
|
||||||
|
// Already open (maybe off-screen or hidden): close and reopen so it lands centered in view.
|
||||||
|
webamp.dispose();
|
||||||
|
webamp = null;
|
||||||
|
document.getElementById('teahub-webamp')?.remove();
|
||||||
|
}
|
||||||
|
btn.textContent = '… webamp';
|
||||||
|
try {
|
||||||
|
const Webamp = await loadWebamp();
|
||||||
|
if (!Webamp.browserIsSupported()) throw new Error('browser not supported');
|
||||||
|
webamp = new Webamp({ initialTracks: await loadTracks() });
|
||||||
|
webamp.onClose(() => {
|
||||||
|
webamp.dispose();
|
||||||
|
webamp = null;
|
||||||
|
try { localStorage.removeItem(KEY); } catch {}
|
||||||
|
});
|
||||||
|
const host = document.createElement('div');
|
||||||
|
host.id = 'teahub-webamp';
|
||||||
|
// Webamp centers itself in this node, so make it exactly the visible viewport.
|
||||||
|
host.style.cssText = 'position:fixed;left:0;top:0;width:100vw;height:100vh;pointer-events:none';
|
||||||
|
document.body.appendChild(host);
|
||||||
|
await webamp.renderWhenReady(host);
|
||||||
|
try { localStorage.setItem(KEY, '1'); } catch {}
|
||||||
|
btn.textContent = '▶ webamp';
|
||||||
|
} catch (err) {
|
||||||
|
console.error('[teahub webamp]', err);
|
||||||
|
webamp = null;
|
||||||
|
btn.textContent = '✕ webamp';
|
||||||
|
btn.title = String(err && err.message || err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Keep Webamp pinned to the viewport and above Gitea's own layers.
|
||||||
|
const style = document.createElement('style');
|
||||||
|
style.textContent = '#webamp{position:fixed!important;z-index:99990!important}';
|
||||||
|
document.head.appendChild(style);
|
||||||
|
|
||||||
|
// 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:99999',
|
||||||
|
'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=015"></script>
|
||||||
Reference in new issue
Block a user