Files
pl8list-rewind/web/index.html
T
nvrl 300674b8ac Listen without a pl8list account
"start listening" on the sign-in page makes a guest: a user with a negative
id and a session of its own, so the feed, likes, follows and radio work as
for anyone. Signing in with pl8list later moves the guest's plays, likes and
follows to the account. Signing out as a guest deletes it; guests whose
session ran out are purged hourly. New guests are capped at 10 per IP and
hour, and that limiter forgets IPs hourly. The privacy policy covers guests.
2026-11-02 13:20:22 +01:00

314 lines
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>pl8list rewind</title>
<meta name="description" content="Endless riddim from pl8list. Like it, and it comes back.">
<meta name="theme-color" content="#000000">
<meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="rewind">
<link rel="manifest" href="/manifest.webmanifest">
<link rel="icon" href="/icons/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png">
<!-- Everything, hls.js included, is bundled and served by us: no visitor address
goes to a CDN or to pl8list. -->
<script type="module" src="/src/main.ts"></script>
</head>
<body>
<!-- Shared icons, referenced with <use href="#i-…">. -->
<svg class="sprite" aria-hidden="true">
<symbol id="i-heart" viewBox="0 0 24 24"><path fill="currentColor" d="M12 21s-7.5-4.6-10-9.3C.4 8.4 2.3 4.5 6 4.5c2.2 0 3.6 1.2 6 3.6 2.4-2.4 3.8-3.6 6-3.6 3.7 0 5.6 3.9 4 7.2C19.5 16.4 12 21 12 21z"/></symbol>
<symbol id="i-x" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" d="M6 6l12 12M18 6 6 18"/></symbol>
<symbol id="i-share" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" d="M12 3v12M7 8l5-5 5 5M5 13v6a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-6"/></symbol>
<symbol id="i-open" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" d="M7 17 17 7M9 7h8v8"/></symbol>
<symbol id="i-down" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" d="M12 4v16M6 14l6 6 6-6"/></symbol>
<symbol id="i-play" viewBox="0 0 24 24"><path fill="currentColor" d="M8 5.5v13a1 1 0 0 0 1.5.86l10.5-6.5a1 1 0 0 0 0-1.72L9.5 4.64A1 1 0 0 0 8 5.5z"/></symbol>
<symbol id="i-vol" viewBox="0 0 24 24"><path fill="currentColor" d="M4 9h4l5-4.5v15L8 15H4z"/><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M16.5 8.5a5 5 0 0 1 0 7M19 6a8.5 8.5 0 0 1 0 12"/></symbol>
<symbol id="i-mute" viewBox="0 0 24 24"><path fill="currentColor" d="M4 9h4l5-4.5v15L8 15H4z"/><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M16.5 9.5l5 5M21.5 9.5l-5 5"/></symbol>
<symbol id="i-rewind" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10" fill="none" stroke="currentColor" stroke-width="2"/><path fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" d="M14.27 7.54A5 5 0 1 0 16.76 13.55"/><path fill="currentColor" d="M17.66 10.79l1.13 4.26-4.56-1.48z"/><circle cx="12" cy="12" r="1.3" fill="currentColor"/></symbol>
<symbol id="i-sliders" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M4 7h9M17 7h3M4 17h3M11 17h9"/><circle cx="15" cy="7" r="2.2" fill="none" stroke="currentColor" stroke-width="2"/><circle cx="9" cy="17" r="2.2" fill="none" stroke="currentColor" stroke-width="2"/></symbol>
<symbol id="i-clock" viewBox="0 0 24 24"><circle cx="12" cy="12" r="8.5" fill="none" stroke="currentColor" stroke-width="2"/><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M12 7.5V12l3 2"/></symbol>
<symbol id="i-plus" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" d="M12 5v14M5 12h14"/></symbol>
<symbol id="i-check" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" d="M5 12.5l4.5 4.5L19 7.5"/></symbol>
<symbol id="i-radio" viewBox="0 0 24 24"><circle cx="12" cy="12" r="2.2" fill="currentColor"/><path fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" d="M8.5 8.5a5 5 0 0 0 0 7M15.5 8.5a5 5 0 0 1 0 7M5.6 5.6a9 9 0 0 0 0 12.8M18.4 5.6a9 9 0 0 1 0 12.8"/></symbol>
<symbol id="i-comment" viewBox="0 0 24 24"><path fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" d="M4.5 5.5h15v10h-8l-4.5 3.5v-3.5h-2.5z"/></symbol>
<symbol id="i-pause" viewBox="0 0 24 24"><path fill="currentColor" d="M7 4.5h3.5v15H7zM13.5 4.5H17v15h-3.5z"/></symbol>
</svg>
<header id="bar" hidden>
<span class="logo"><svg aria-hidden="true"><use href="#i-rewind"/></svg><span class="logo-pl8">pl8list </span><b>rewind</b></span>
<nav id="mode-tabs" aria-label="feed">
<button type="button" data-mode="classic">for you</button>
<button type="button" data-mode="riddim">riddim</button>
<button type="button" data-mode="fresh">fresh</button>
<button type="button" data-mode="deep">deep cuts</button>
</nav>
<button id="mode-chip" type="button" title="back to your feed" hidden>
<span class="chip-avatar" aria-hidden="true"></span><span class="chip-label"></span><span aria-hidden="true">×</span>
</button>
<div class="bar-end">
<div id="volume" hidden>
<button id="mute-btn" type="button" class="icon-btn" title="mute (m)" aria-label="mute" aria-pressed="false"><svg><use href="#i-vol"/></svg></button>
<input id="volume-slider" type="range" min="0" max="1" step="0.01" aria-label="volume">
</div>
<button id="history-btn" type="button" class="icon-btn" title="history (h)" aria-label="history" aria-controls="history"><svg><use href="#i-clock"/></svg></button>
<button id="likes-btn" type="button" class="icon-btn" title="likes" aria-label="likes" aria-controls="likes"><svg><use href="#i-heart"/></svg></button>
<button id="filters-btn" type="button" class="icon-btn" title="filters and settings" aria-label="filters and settings" aria-controls="filters"><svg><use href="#i-sliders"/></svg></button>
<button id="account-btn" type="button" class="avatar-btn" title="account" aria-label="account" aria-controls="account"></button>
</div>
</header>
<div id="login" hidden>
<div class="login-box">
<h1><svg class="login-mark" aria-hidden="true"><use href="#i-rewind"/></svg>pl8list <b>rewind</b></h1>
<p class="tagline">endless riddim from pl8list. like it, and it comes back.</p>
<div id="guest-start">
<button type="button" id="guest-btn" class="primary"><svg><use href="#i-play"/></svg> start listening</button>
<p class="or">no account needed. or sign in with pl8list to bring your follows and likes along.</p>
</div>
<form id="email-form">
<label for="email">sign in with your pl8list email</label>
<input id="email" type="email" name="email" autocomplete="email" required placeholder="you@example.com">
<button type="submit">send code</button>
</form>
<form id="code-form" hidden>
<label for="code">pl8list sent a 6-digit code to <span id="sent-to"></span></label>
<input id="code" name="code" inputmode="numeric" autocomplete="one-time-code" pattern="[0-9]{6}"
maxlength="6" required placeholder="123456">
<label class="check">
<input type="checkbox" id="connect" name="connect">
<span>stay connected to pl8list on this device
<small>keeps your feed and library in sync, and saves what you like and follow here to
your pl8list library. Your pl8list login is stored encrypted with a key only this browser
holds. Turn it off any time under your account.</small></span>
</label>
<button type="submit">sign in</button>
<div class="login-links">
<button type="button" id="resend" class="link">resend code</button>
<button type="button" id="restart" class="link">use another email</button>
</div>
</form>
<button type="button" id="login-cancel" class="link" hidden>cancel, keep using the app</button>
<p id="login-error" role="alert"></p>
<p class="fineprint">
This site is not affiliated with pl8list. Without an account we keep what you play, like and
follow under a random cookie, to pick tracks for you; signing out deletes it. Your email and code
go to pl8list, which signs this server in for you. Unless you choose to stay connected, we use
that pl8list login once to read your feed and library, then log it out; it is never stored. We
keep your pl8list handle, the artists you follow and what you play and like.
</p>
<p class="fineprint legal-links">
<a href="/datenschutz">Datenschutz / privacy</a> · <a href="/impressum">Impressum</a>
</p>
</div>
</div>
<form id="filters" aria-labelledby="filters-title">
<h2 id="filters-title">filters and settings</h2>
<fieldset>
<legend>filters</legend>
<label class="switch"><input type="checkbox" name="free"> <span>free downloads only</span></label>
<div class="bpm">
<div class="bpm-head"><span>bpm</span><output id="bpm-out">any</output></div>
<div class="range2">
<input type="range" name="bpm_min" min="60" max="200" step="1" value="60" aria-label="lowest bpm">
<input type="range" name="bpm_max" min="60" max="200" step="1" value="200" aria-label="highest bpm">
</div>
<div class="presets">
<button type="button" data-bpm="60-200">any</button>
<button type="button" data-bpm="138-142">140</button>
<button type="button" data-bpm="148-152">150</button>
<button type="button" data-bpm="165-178">170</button>
</div>
</div>
<div class="sheet-actions">
<button type="submit" class="primary">apply filters</button>
<button type="button" id="filters-clear" class="link">clear</button>
</div>
</fieldset>
<fieldset class="modes">
<legend>visuals</legend>
<label><input type="radio" name="visuals" value="liquid"> liquid</label>
<label><input type="radio" name="visuals" value="simple"> simple</label>
</fieldset>
<fieldset class="modes">
<legend>crossfade</legend>
<label><input type="radio" name="crossfade" value="0"> cut</label>
<label><input type="radio" name="crossfade" value="0.9"> short</label>
<label><input type="radio" name="crossfade" value="4"> long</label>
<label><input type="radio" name="crossfade" value="8"> dj mix</label>
</fieldset>
<label class="switch"><input type="checkbox" name="bass"> <span>bass boost</span></label>
<fieldset class="modes">
<legend>sleep timer <span id="sleep-left"></span></legend>
<label><input type="radio" name="sleep" value="off"> off</label>
<label><input type="radio" name="sleep" value="15"> 15 min</label>
<label><input type="radio" name="sleep" value="30"> 30 min</label>
<label><input type="radio" name="sleep" value="60"> 1 h</label>
<label><input type="radio" name="sleep" value="end"> end of track</label>
</fieldset>
<button type="button" id="help-open-settings" class="link">gestures and shortcuts</button>
</form>
<aside id="likes" aria-labelledby="likes-title">
<h2 id="likes-title">likes</h2>
<div class="sheet-tools">
<input id="likes-search" type="search" placeholder="search your likes" aria-label="search your likes" autocomplete="off">
<label class="switch small"><input type="checkbox" id="likes-shuffle"> <span>shuffle</span></label>
<button type="button" id="likes-play" class="primary"><svg><use href="#i-play"/></svg> play</button>
</div>
<ol id="likes-list" class="track-list"></ol>
</aside>
<aside id="history" aria-labelledby="history-title">
<h2 id="history-title">played lately</h2>
<p class="fineprint">kept on this device only.</p>
<ol id="history-list" class="track-list"></ol>
<button type="button" id="history-clear" class="link">clear history</button>
</aside>
<aside id="account" aria-labelledby="account-handle">
<h2 id="account-handle"></h2>
<p id="stats"></p>
<section id="sync" aria-labelledby="sync-title">
<h3 id="sync-title">pl8list sync</h3>
<p id="sync-status"></p>
<button type="button" id="sync-btn">resync with pl8list</button>
<button type="button" id="disconnect-btn" class="link" hidden>stop staying connected on this device</button>
</section>
<button type="button" id="logout-btn">sign out</button>
<button type="button" id="logout-all-btn" class="link">sign out on all devices</button>
<button type="button" id="help-open" class="link">gestures and shortcuts</button>
<a href="/account/export" download>download my data</a>
<button type="button" id="delete-open" class="link danger">delete account…</button>
<form id="delete-form" hidden>
<label for="delete-confirm">This deletes your likes and listening history here, right away.
Your pl8list account is not touched. Type <b id="delete-handle"></b> to confirm.</label>
<input id="delete-confirm" name="confirm" autocomplete="off" required>
<button type="submit" class="danger">delete my account</button>
</form>
<p class="fineprint legal-links">
<a href="/datenschutz">Datenschutz / privacy</a> · <a href="/impressum">Impressum</a>
</p>
</aside>
<main id="feed"></main>
<p id="feed-status" role="status" hidden></p>
<div id="start" hidden>
<button type="button" class="start-play" aria-label="start playing"><svg><use href="#i-play"/></svg></button>
<p class="start-track"></p>
<p class="start-hint">tap anywhere to start</p>
</div>
<button type="button" id="up-next" hidden>
<span class="up-next-avatar" aria-hidden="true"></span>
<span class="up-next-text"><small>up next</small><span class="up-next-title"></span></span>
<svg aria-hidden="true"><use href="#i-down"/></svg>
</button>
<!-- Artist radio: who the radio is built around, and who is next to them. -->
<div id="station" hidden>
<section class="station-box" role="dialog" aria-modal="true" aria-labelledby="station-name">
<button type="button" id="station-back" class="link" hidden></button>
<button type="button" id="station-close" class="icon-btn" aria-label="close"><svg><use href="#i-x"/></svg></button>
<div class="station-head">
<div class="station-avatar"><span class="initial"></span></div>
<div>
<p class="station-kicker">artist radio</p>
<h2 id="station-name"></h2>
<p id="station-sub"></p>
<div id="station-tags" class="chips"></div>
</div>
</div>
<h3>around them</h3>
<ol id="station-neighbours"></ol>
<p id="station-exploring" hidden>finding related artists…</p>
<button type="button" id="station-start" class="primary"><svg><use href="#i-play"/></svg> start radio</button>
</section>
</div>
<div id="radio-end" hidden>
<p></p>
<button type="button" id="radio-end-feed">back to your feed</button>
<button type="button" id="radio-end-next" class="primary"></button>
</div>
<div id="help" aria-labelledby="help-title">
<div class="help-box">
<h2 id="help-title">controls</h2>
<dl class="touch-only">
<dt>tap</dt><dd>play / pause</dd>
<dt>double-tap</dt><dd>like</dd>
<dt>swipe up / down</dt><dd>next / previous</dd>
<dt>drag waveform</dt><dd>seek</dd>
</dl>
<dl class="pointer-only">
<dt>click · space</dt><dd>play / pause</dd>
<dt>double-click · l</dt><dd>like</dd>
<dt>↓ j · ↑ k · wheel</dt><dd>next / previous</dd>
<dt>← →</dt><dd>seek 10 s</dd>
<dt>f</dt><dd>follow artist</dd>
<dt>r</dt><dd>artist radio</dd>
<dt>x</dt><dd>not for me</dd>
<dt>s</dt><dd>share</dd>
<dt>v</dt><dd>liquid / simple visuals</dd>
<dt>z</dt><dd>zen: just the drop</dd>
<dt>c</dt><dd>filters and settings</dd>
<dt>h</dt><dd>history</dd>
<dt>0–9 · , .</dt><dd>jump to 0–90 % · seek 5 s</dd>
<dt>m · + −</dt><dd>mute · volume</dd>
<dt>?</dt><dd>this help</dd>
</dl>
<p>Drag the liquid artwork sideways to spin it. Lock-screen and headphone buttons work too.</p>
<p class="pointer-only fineprint">Keys like r or x doing something else, such as reloading or
closing the tab? A vim-keys browser extension (Vimium and the like) takes them first:
turn it off for this site.</p>
<button type="button" id="help-close">got it</button>
</div>
</div>
<div id="toast" role="status" aria-live="polite"></div>
<template id="card-tpl">
<section class="card">
<div class="bg"></div>
<div class="glow"></div>
<div class="content">
<div class="art">
<span class="initial"></span>
<span class="paused-badge"><svg><use href="#i-play"/></svg></span>
<span class="spinner"></span>
</div>
<p class="why"></p>
<h1 class="title"></h1>
<div class="byline">
<span class="artist-avatar" aria-hidden="true"></span>
<a class="artist" target="_blank" rel="noopener"></a>
<button type="button" class="follow pill" title="follow artist (f)"><svg><use href="#i-plus"/></svg><span>follow</span></button>
<button type="button" class="radio pill" title="more like this artist (r)"><svg><use href="#i-radio"/></svg><span>radio</span></button>
</div>
<div class="chips"></div>
<p class="desc"></p>
<div class="wave-wrap">
<canvas class="wave"></canvas>
<span class="wave-tip"></span>
</div>
<div class="time"><span class="cur">0:00</span><span class="counts"></span><span class="dur"></span></div>
</div>
<nav class="actions">
<button type="button" class="act like" title="like (l)" aria-label="like"><span class="ring"><svg><use href="#i-heart"/></svg></span><span class="n saves"></span></button>
<button type="button" class="act dislike" title="not for me (x)" aria-label="not for me"><span class="ring"><svg><use href="#i-x"/></svg></span><span class="n">nope</span></button>
<button type="button" class="act share" title="share (s)" aria-label="share"><span class="ring"><svg><use href="#i-share"/></svg></span><span class="n">share</span></button>
<a class="act open" target="_blank" rel="noopener" title="open on pl8list" aria-label="open on pl8list"><span class="ring"><svg><use href="#i-comment"/></svg></span><span class="n comments"></span></a>
<button type="button" class="act next" title="next (j)" aria-label="next"><span class="ring"><svg><use href="#i-down"/></svg></span><span class="n">next</span></button>
</nav>
</section>
</template>
</body>
</html>