Jade was replaced with Templ. Also some changes were made to a layout. A ServeAsset() handler was introduced.
This commit is contained in:
parent
7c34a3a632
commit
670b6ea032
@ -11,6 +11,7 @@
|
|||||||
--secondary-color: #9f2b68;
|
--secondary-color: #9f2b68;
|
||||||
--text-color: #f5f5f5;
|
--text-color: #f5f5f5;
|
||||||
--text-indent: 1.6rem;
|
--text-indent: 1.6rem;
|
||||||
|
color-scheme: light dark;
|
||||||
scrollbar-color: var(--primary-color) var(--background-color); }
|
scrollbar-color: var(--primary-color) var(--background-color); }
|
||||||
|
|
||||||
@media (prefers-color-scheme: light) {
|
@media (prefers-color-scheme: light) {
|
||||||
@ -28,6 +29,10 @@
|
|||||||
|
|
||||||
.right { text-align: right; }
|
.right { text-align: right; }
|
||||||
|
|
||||||
|
.small { font-size: .8rem; }
|
||||||
|
|
||||||
|
.small.player-links a { margin: 0 .2rem; }
|
||||||
|
|
||||||
a,
|
a,
|
||||||
button {
|
button {
|
||||||
color: var(--primary-color);
|
color: var(--primary-color);
|
||||||
@ -64,10 +69,6 @@ h2 {
|
|||||||
font-size: 1.4rem;
|
font-size: 1.4rem;
|
||||||
margin: 1rem 0; }
|
margin: 1rem 0; }
|
||||||
|
|
||||||
small { font-size: .8rem; }
|
|
||||||
|
|
||||||
small.player-links a { margin: 0 .2rem; }
|
|
||||||
|
|
||||||
audio {
|
audio {
|
||||||
background-color: var(--primary-color);
|
background-color: var(--primary-color);
|
||||||
width: 100%; }
|
width: 100%; }
|
||||||
@ -105,11 +106,11 @@ header svg text:last-child { font-size: .88rem; }
|
|||||||
@-moz-document url-prefix() {
|
@-moz-document url-prefix() {
|
||||||
header svg text:first-child { font-size: 2rem; } }
|
header svg text:first-child { font-size: 2rem; } }
|
||||||
|
|
||||||
nav { margin-top: .5rem; }
|
header nav { margin-top: .5rem; }
|
||||||
|
|
||||||
nav a { font-variant: small-caps; }
|
header nav a { font-variant: small-caps; }
|
||||||
|
|
||||||
nav h1 {
|
header nav h1 {
|
||||||
color: var(--secondary-color);
|
color: var(--secondary-color);
|
||||||
margin: 0; }
|
margin: 0; }
|
||||||
|
|
||||||
@ -147,20 +148,19 @@ input#radio-volume {
|
|||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
align-items: center; }
|
align-items: center; }
|
||||||
|
|
||||||
#player div:first-child div {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
}
|
|
||||||
|
|
||||||
#last-songs {
|
#last-songs {
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
min-width: 80%;
|
min-width: 80%;
|
||||||
width: 80%; }
|
width: 80%; }
|
||||||
|
|
||||||
#last-songs tbody tr {
|
#last-songs :is(thead tr, tbody tr) {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: .5rem;
|
gap: .5rem;
|
||||||
grid-template-columns: 3rem 2rem 1fr; }
|
grid-template-columns: 3rem 3rem 1fr; }
|
||||||
|
|
||||||
|
#last-songs thead tr {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
footer {
|
footer {
|
||||||
font-size: .8rem;
|
font-size: .8rem;
|
||||||
|
@ -15,23 +15,24 @@ async function updateStatus() {
|
|||||||
$("radio-duration").textContent = "";
|
$("radio-duration").textContent = "";
|
||||||
$("radio-listeners").textContent =
|
$("radio-listeners").textContent =
|
||||||
$("radio-listener-peak").textContent = "0";
|
$("radio-listener-peak").textContent = "0";
|
||||||
$("last-songs").firstChild.remove();
|
$("last-songs").lastChild.remove();
|
||||||
return [-1, null];
|
return [-1, null];
|
||||||
}
|
}
|
||||||
|
|
||||||
const s = await resp.json();
|
const s = await resp.json();
|
||||||
|
|
||||||
if (undefined != s.most_listened_song) {
|
if (undefined != s.last_songs) {
|
||||||
$("radio-mls-song").textContent = s.most_listened_song.song;
|
$("last-songs").lastChild.remove();
|
||||||
$("radio-mls-listeners").textContent = s.most_listened_song.listeners;
|
$("last-songs").appendChild(document.createElement("tbody"));
|
||||||
$("radio-mls-date").textContent = (new Intl.DateTimeFormat('en-GB',
|
for (let i = 0; i < s.last_songs.length; ++i) {
|
||||||
{timeStyle: "long",
|
let row = $("last-songs").lastChild.insertRow();
|
||||||
dateStyle: "long",
|
row.insertCell().appendChild(document.createTextNode(formatStartAt(new Date(s.last_songs[i].start_at))));
|
||||||
timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone}))
|
row.insertCell().appendChild(document.createTextNode((s.last_songs[i].listeners == 0 ? "" : s.last_songs[i].listeners + "/") + (s.last_songs[i].max_listeners == 0 ? "" : s.last_songs[i].max_listeners)));
|
||||||
.format(new Date(s.most_listened_song.date))
|
row.insertCell().appendChild(document.createTextNode(`${s.last_songs[i].artist} - ${s.last_songs[i].title}`));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (undefined == s.current_song)
|
if (undefined == s.current_song || undefined == s.current_song.duration_msec)
|
||||||
return [-1, null];
|
return [-1, null];
|
||||||
|
|
||||||
$("radio-song").textContent = `${s.current_song.artist} - ${s.current_song.title}`;
|
$("radio-song").textContent = `${s.current_song.artist} - ${s.current_song.title}`;
|
||||||
@ -39,17 +40,6 @@ async function updateStatus() {
|
|||||||
$("radio-listeners").textContent = s.listeners.current;
|
$("radio-listeners").textContent = s.listeners.current;
|
||||||
$("radio-listener-peak").textContent = s.listeners.peak;
|
$("radio-listener-peak").textContent = s.listeners.peak;
|
||||||
|
|
||||||
if (undefined != s.last_songs) {
|
|
||||||
$("last-songs").firstChild.remove();
|
|
||||||
$("last-songs").appendChild(document.createElement("tbody"));
|
|
||||||
for (let i = 0; i < s.last_songs.length; ++i) {
|
|
||||||
let row = $("last-songs").insertRow();
|
|
||||||
row.insertCell().appendChild(document.createTextNode(formatStartAt(new Date(s.last_songs[i].start_at))));
|
|
||||||
row.insertCell().appendChild(document.createTextNode(s.last_songs[i].listeners == 0 ? "" : s.last_songs[i].listeners));
|
|
||||||
row.insertCell().appendChild(document.createTextNode(`${s.last_songs[i].artist} - ${s.last_songs[i].title}`));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return [s.current_song.duration_msec, new Date(s.current_song.start_at)];
|
return [s.current_song.duration_msec, new Date(s.current_song.start_at)];
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,8 +84,11 @@ audio.hidden = true;
|
|||||||
const audio_src = audio.childNodes[0].src;
|
const audio_src = audio.childNodes[0].src;
|
||||||
|
|
||||||
const volume = $("radio-volume");
|
const volume = $("radio-volume");
|
||||||
|
volume.value = +(localStorage.getItem("volume") || 50) * 100.0;
|
||||||
audio.volume = volume.value / 100.0;
|
audio.volume = volume.value / 100.0;
|
||||||
volume.addEventListener("input", e => audio.volume = e.target.value / 100.0);
|
volume.addEventListener("input", e => {
|
||||||
|
audio.volume = e.target.value / 100.0;
|
||||||
|
localStorage.setItem("volume", audio.volume); });
|
||||||
|
|
||||||
$("player").style.display = $("player").firstChild.style.display = "flex";
|
$("player").style.display = $("player").firstChild.style.display = "flex";
|
||||||
|
|
||||||
|
112
web/index.templ
Normal file
112
web/index.templ
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
import "dwelling-radio/internal/radio"
|
||||||
|
import "strconv"
|
||||||
|
import "dwelling-radio/pkg/utils"
|
||||||
|
import "net/http"
|
||||||
|
|
||||||
|
templ Index(curSong *radio.Song, sl []radio.Song, slLen int64, lstnrs *radio.ListenerCounter, r *http.Request) {
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<meta name="theme-color" content="#cd2682" />
|
||||||
|
<meta name="color-scheme" content="light dark" />
|
||||||
|
|
||||||
|
<title>Arav's dwelling / Radio</title>
|
||||||
|
|
||||||
|
<meta name="author" content={ "Alexander \"Arav\" Andreev" } />
|
||||||
|
<meta name="description" content="Internet-radio broadcasting from under my desk." />
|
||||||
|
<meta name="keywords" content="self-host radio home-radio various music" />
|
||||||
|
|
||||||
|
<link rel="canonical" href={ utils.Site(r.Host) } />
|
||||||
|
|
||||||
|
<link rel="icon" href="/assets/img/favicon.svg" sizes="any" type="image/svg+xml" />
|
||||||
|
<link rel="stylesheet" href="/assets/css/main.css" />
|
||||||
|
|
||||||
|
<script src="/assets/js/main.js" defer />
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<header>
|
||||||
|
<svg viewBox="0 -25 216 40">
|
||||||
|
<text>Arav's dwelling</text>
|
||||||
|
<text y="11">Welcome to my sacred place, wanderer</text>
|
||||||
|
</svg>
|
||||||
|
<nav>
|
||||||
|
<a href={ templ.URL(utils.MainSite(r.Host)) }>Back to home</a>
|
||||||
|
<h1>Radio</h1>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
|
<section id="banner">
|
||||||
|
<video playsinline autoplay loop muted>
|
||||||
|
<source src="/assets/img/stopit.mp4" type="video/mp4" />
|
||||||
|
</video>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<div class="small player-links">
|
||||||
|
<a href="/filelist">filelist</a>
|
||||||
|
<a href="/playlist">playlist</a>
|
||||||
|
<a href="/live/stream.ogg">direct link</a>
|
||||||
|
(<a href="http://radio.arav.su:8000/stream.ogg">http</a>
|
||||||
|
<a href="http://wsmkgnmhmzqm7kyzv7jnzzafvgm7xlmlfvzhgorpapd5or2arnhuktqd.onion/live/stream.ogg">Tor</a>
|
||||||
|
<a href="http://radio.arav.i2p/live/stream.ogg">I2P</a>
|
||||||
|
<a href="http://[300:a98d:d6d0:8a08::e]/live/stream.ogg">Ygg</a>)
|
||||||
|
<a href="https://dir.xiph.org/search?q=arav's+dwelling">Xiph</a>
|
||||||
|
| OGG 128 Kb/s
|
||||||
|
</div>
|
||||||
|
<div id="player">
|
||||||
|
<div>
|
||||||
|
<button id="radio-play" />
|
||||||
|
<input id="radio-volume" type="range" min="0" max="100" orient="vertical" />
|
||||||
|
</div>
|
||||||
|
<audio>
|
||||||
|
<source src="/live/stream.ogg" type="audio/ogg" />
|
||||||
|
Your browser doesn't support an audio element, it's sad... But you always can take the <a href="/playlist">playlist</a>!
|
||||||
|
</audio>
|
||||||
|
<div>
|
||||||
|
if curSong != nil {
|
||||||
|
<p>Now playing: <span id="radio-song">{ curSong.Artist } - { curSong.Title }</span> ( <span id="radio-duration-estimate"></span> <span id="radio-duration">{ curSong.DurationString() }</span> )</p>
|
||||||
|
} else {
|
||||||
|
<p>Now playing: <span id="radio-song"></span> ( <span id="radio-duration-estimate"></span> <span id="radio-duration"></span> )</p>
|
||||||
|
}
|
||||||
|
<p>Current/peak listeners: <span id="radio-listeners">{ strconv.FormatInt(lstnrs.Current(), 10) }</span> / <span id="radio-listener-peak">{ strconv.FormatInt(lstnrs.Peak(), 10) }</span></p>
|
||||||
|
<p class="small">Notice: information updates every new song. But you can <button id="radio-update">update</button> it forcibly.</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
if sl != nil && len(sl) != 0 {
|
||||||
|
<section>
|
||||||
|
<h2>Last { strconv.FormatInt(slLen, 10) } songs</h2>
|
||||||
|
<table id="last-songs">
|
||||||
|
<thead class="small">
|
||||||
|
<tr>
|
||||||
|
<td>Start</td>
|
||||||
|
<td><abbr title="Overall/Max listeners">O/M</abbr></td>
|
||||||
|
<td>Song</td>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
for _, song := range sl {
|
||||||
|
<tr>
|
||||||
|
<td>{ utils.ToClientTimezone(song.StartAt, r).Format("15:04") }</td>
|
||||||
|
if song.MaxListeners != 0 {
|
||||||
|
<td>{ strconv.FormatInt(song.Listeners, 10) }/{ strconv.FormatInt(song.MaxListeners, 10) }</td>
|
||||||
|
} else {
|
||||||
|
<td></td>
|
||||||
|
}
|
||||||
|
<td>{ song.Artist } - { song.Title }</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
}
|
||||||
|
<footer>
|
||||||
|
2017—2024 Alexander "Arav" Andreev <<a href="mailto:me@arav.su">me@arav.su</a>> <a href={ templ.URL(utils.MainSite(r.Host) + "/privacy") }>Privacy statements</a>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
}
|
||||||
|
// const MostListenedDateFormat string = "02 January 2006 at 15:04:05 MST"
|
@ -1,71 +0,0 @@
|
|||||||
:go:func Index(mainSite string, songList *radio.SongList, listeners *radio.ListenerCounter, mls *radio.MostListenedSong, r *http.Request)
|
|
||||||
|
|
||||||
:go:import "dwelling-radio/internal/radio"
|
|
||||||
:go:import "dwelling-radio/pkg/utils"
|
|
||||||
|
|
||||||
doctype html
|
|
||||||
html(lang='en')
|
|
||||||
head
|
|
||||||
title Arav's dwelling / Radio
|
|
||||||
meta(charset='utf-8')
|
|
||||||
meta(http-equiv='X-UA-Compatible' content='IE=edge')
|
|
||||||
meta(name='viewport' content='width=device-width, initial-scale=1.0')
|
|
||||||
meta(name='theme-color' content='#cd2682')
|
|
||||||
meta(name='description' content='Internet-radio broadcasting from under my desk.')
|
|
||||||
link(rel='icon' href='/assets/img/favicon.svg' sizes='any' type='image/svg+xml')
|
|
||||||
link(href='/assets/css/main.css' rel='stylesheet')
|
|
||||||
script(src='/assets/js/main.js' defer='')
|
|
||||||
body
|
|
||||||
header
|
|
||||||
svg(viewBox='0 -25 216 40')
|
|
||||||
text Arav's dwelling
|
|
||||||
text(y='11') Welcome to my sacred place, wanderer
|
|
||||||
nav
|
|
||||||
a(href=mainSite) Back to main website
|
|
||||||
h1 Radio
|
|
||||||
section#banner
|
|
||||||
video(playsinline='' autoplay='' loop='' muted='')
|
|
||||||
source(src="/assets/img/stopit.mp4", type="video/mp4")
|
|
||||||
section
|
|
||||||
small.player-links
|
|
||||||
a(href='/filelist') filelist
|
|
||||||
a(href='/playlist') playlist (.m3u)
|
|
||||||
a(href='/live/stream.ogg') direct link
|
|
||||||
a(href='http://radio.arav.su:8000/stream.ogg') direct link (http)
|
|
||||||
a(href='http://wsmkgnmhmzqm7kyzv7jnzzafvgm7xlmlfvzhgorpapd5or2arnhuktqd.onion/live/stream.ogg') direct link (Tor)
|
|
||||||
a(href='http://radio.arav.i2p/live/stream.ogg') direct link (I2P)
|
|
||||||
a(href="https://dir.xiph.org/search?q=arav's+dwelling") Xiph
|
|
||||||
| OGG 128 Kb/s
|
|
||||||
div#player
|
|
||||||
div
|
|
||||||
div
|
|
||||||
button#radio-play
|
|
||||||
input#radio-volume(type="range" min="0" max="100" orient="vertical")
|
|
||||||
audio(preload='none' controls='' playsinline='')
|
|
||||||
source(src='/live/stream.ogg' type='audio/ogg')
|
|
||||||
| Your browser doesn't support an audio element, it's sad... But you always can take the #[a(href='/playlist') playlist]!
|
|
||||||
div
|
|
||||||
if (songList.Current() != nil)
|
|
||||||
- cur := *songList.Current()
|
|
||||||
p Now playing: #[span#radio-song #{cur.Artist} - #{cur.Title}] ( #[span#radio-duration-estimate ] #[span#radio-duration #{cur.DurationString()}] )
|
|
||||||
else
|
|
||||||
p Now playing: #[span#radio-song ] ( #[span#radio-duration-estimate ] #[span#radio-duration ] )
|
|
||||||
p Current/peak listeners: #[span#radio-listeners #{listeners.Current()}] / #[span#radio-listener-peak #{listeners.Peak()}]
|
|
||||||
p
|
|
||||||
small Notice: information updates every new song. But you can #[button#radio-update update] it forcibly.
|
|
||||||
section
|
|
||||||
h2 Last #{songList.MaxLen()} songs
|
|
||||||
table#last-songs
|
|
||||||
tbody
|
|
||||||
each song in songList.List()
|
|
||||||
tr
|
|
||||||
td= utils.ToClientTimezone(song.StartAt, r).Format("15:04")
|
|
||||||
if song.MaxListeners != 0
|
|
||||||
td= song.MaxListeners
|
|
||||||
else
|
|
||||||
td
|
|
||||||
td #{song.Artist} - #{song.Title}
|
|
||||||
if mls != nil
|
|
||||||
p.right Most listened song was "#[span#radio-mls-song #{mls.Song}]" on #[span#radio-mls-date #{utils.ToClientTimezone(mls.Date, r).Format(radio.MostListenedDateFormat)}] with #[b#radio-mls-listeners #{mls.Listeners}] listeners.
|
|
||||||
footer
|
|
||||||
| 2017—2024 Alexander "Arav" Andreev <#[a(href='mailto:me@arav.su') me@arav.su]> #[a(href=mainSite+'/privacy') Privacy statements]
|
|
25
web/web.go
25
web/web.go
@ -6,9 +6,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
// To install a Jade compiler: go install github.com/Joker/jade/cmd/jade@latest
|
|
||||||
//go:generate $GOPATH/bin/jade -pkg=web -writer templates/index.pug
|
|
||||||
|
|
||||||
//go:embed assets
|
//go:embed assets
|
||||||
var assetsDir embed.FS
|
var assetsDir embed.FS
|
||||||
|
|
||||||
@ -17,11 +14,21 @@ func Assets() http.FileSystem {
|
|||||||
return http.FS(f)
|
return http.FS(f)
|
||||||
}
|
}
|
||||||
|
|
||||||
func AssetsGetFile(path string) []byte {
|
func ServeAsset(path, mime, attachement string) func(http.ResponseWriter, *http.Request) {
|
||||||
data, err := assetsDir.ReadFile("assets/" + path)
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
if err != nil {
|
if mime != "" {
|
||||||
panic(err)
|
w.Header().Add("Content-Type", mime)
|
||||||
}
|
}
|
||||||
|
|
||||||
return data
|
if attachement != "" {
|
||||||
|
w.Header().Add("Content-Disposition", "attachment; filename=\""+attachement+"\"")
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := assetsDir.ReadFile("assets/" + path)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Write(data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user