Radio and Files services are deleted.

This commit is contained in:
Alexander Andreev 2022-09-19 22:06:06 +04:00
parent 909c096c21
commit 08e6a17aa1
Signed by: Arav
GPG Key ID: 0388CC8FAA51063F
13 changed files with 0 additions and 747 deletions

View File

@ -1,3 +0,0 @@
exports.host = "0.0.0.0";
exports.port = 32202;
exports.share_path = "/srv/ftp";

View File

@ -1,117 +0,0 @@
const fs = require("fs");
const path = require("path");
const { ENOENT, ECONNRESET } = require("constants");
const Koa = require("koa");
const koaPug = require("koa-pug");
const koaRouter = require("koa-router");
const config = require("./config");
const util = require("../shared/util");
const SIZE_UNITS = [ "B", "KiB", "MiB", "GiB" ];
function addFileSizeUnit(size) {
let i = 0;
for (; size > 1024; size /= 1024) ++i;
return [i > 0 ? size.toFixed(2) : size, SIZE_UNITS[i]];
}
function buildCurrentPathLink(path) {
let currentPath = `<a href="/">root</a>`;
if (path.endsWith("/"))
path = path.slice(1, path.length-1);
const path_parts = path.split("/");
for (let i = 0; i < path_parts.length; ++i) {
let lnk = "";
for (let j = 0; j < i+1; ++j)
lnk += `/${path_parts[j]}`;
currentPath += `/<a href="${encodeURI(lnk)}/">${path_parts[i]}</a>`;
}
return currentPath;
}
function sortFuncByNameField(a, b) {
return (a.name > b.name) ? 1 : (a.name < b.name) ? -1 : 0;
}
async function getDirectoryList(dir_path, orig_url) {
let directories = [];
let files = [];
let total_files_size = 0;
let directory = await fs.promises.opendir(dir_path);
for await (const dirent of directory) {
const stat = await fs.promises.stat(path.join(dir_path, dirent.name));
const [s, u] = addFileSizeUnit(stat.size);
if (stat.isDirectory())
directories.push({
name: dirent.name,
link: `${encodeURIComponent(dirent.name)}/`,
datetime: stat.mtime,
size: "DIR" });
else {
total_files_size += stat.size;
files.push({
name: dirent.name,
link: `/files${orig_url}${encodeURIComponent(dirent.name)}`,
datetime: stat.mtime,
size: `${s} ${u}` });
}
}
directories.sort(sortFuncByNameField);
files.sort(sortFuncByNameField);
return [directories.concat(files), directories.length, files.length,
addFileSizeUnit(total_files_size).join(' ')];
}
function setRoutes() {
return koaRouter().get('/(.*)?', async ctx => {
const file_path = path.join(config.share_path, decodeURI(ctx.originalUrl));
let stat = await fs.promises.stat(file_path);
if (stat.isDirectory()) {
const [items, total_directories, total_files, total_files_size]
= await getDirectoryList(file_path, decodeURI(ctx.originalUrl));
await ctx.render('index', {
clientTZ: util.getClientTimezone(ctx),
main_site: util.getServiceByHost(ctx.header.host),
current_path: buildCurrentPathLink(decodeURI(ctx.originalUrl)),
total_files: total_files,
total_files_size: total_files_size,
total_directories: total_directories,
items: items });
}
});
}
const app = new Koa();
const pug = new koaPug({
viewPath: path.join(__dirname, "views"),
locals: {
moment: (date, tz) => util.datetime(date, util.date_formats.file_date, tz), },
app: app
});
app.proxy = true;
app
.use(setRoutes().routes())
// .on("error", err => { if (!err.code == ECONNRESET || !err.code == ENOENT) console.log(err.code); })
.listen(config.port, config.host);
console.log("Arav's dwelling / Files is up.");

View File

@ -1,170 +0,0 @@
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 400;
src: local('RobotoCondensed'), local('RobotoCondensed-Regular'),
url(/shared/fonts/RobotoCondensed-Regular.ttf); }
:root {
--background-color: #0a0a0a;
--primary-color: #cd2682;
--secondary-color: #9f2b68;
--text-color: #f5f5f5;
--text-indent: 1.6rem;
--overlay-background-color: #f5f5f574;
scrollbar-color: var(--primary-color) var(--background-color); }
@media (prefers-color-scheme: light) {
:root {
--background-color: #f5f5f5;
--primary-color: #9f2b68;
--secondary-color: #cd2682;
--text-color: #0a0a0a;
--overlay-background-color: #0a0a0a74; } }
* { margin: 0; }
::selection {
background-color: var(--secondary-color);
color: var(--background-color); }
a {
color: var(--primary-color);
text-decoration: none; }
a:hover {
color: var(--secondary-color);
text-decoration: underline;
text-decoration-style: dotted;
transition: .5s; }
p {
text-align: justify;
line-height: var(--text-indent);
text-indent: var(--text-indent); }
p:not(:last-child) { margin-bottom: .1rem; }
h1,
h2 {
font-size: 1.8rem;
font-variant: small-caps;
text-align: center;
margin-bottom: 1rem; }
h2 {
font-size: 1.4rem;
margin: 1rem 0; }
html { margin-left: calc(100vw - 100%); }
body {
background-color: var(--background-color);
color: var(--text-color);
font-family: 'Roboto Condensed', Roboto, sans-serif;
font-size: 1.1rem;
margin: 0 auto;
max-width: 960px;
width: 98%; }
header {
display: flex;
flex-wrap: wrap;
justify-content: space-between; }
#logo {
display: block;
width: 360px; }
#logo text { fill: var(--text-color); }
#logo .logo {
font-size: 2rem;
font-variant-caps: small-caps;
font-weight: bold; }
@media screen and (-webkit-min-device-pixel-ratio:0) {
#logo .logo { font-size: 2.082rem; } }
@-moz-document url-prefix() {
#logo .logo { font-size: 2rem; } }
#logo .under { font-size: .88rem; }
nav { margin-top: .5rem; }
nav a { font-variant: small-caps; }
nav h1 {
color: var(--secondary-color);
margin: 0; }
section { margin-top: 1rem; }
#overlay {
align-items: center;
background-color: var(--overlay-background-color);
display: flex;
flex-direction: column;
height: 100%;
left: 0;
max-height: 100%;
position: fixed;
top: 0;
visibility: hidden;
width: 100%; }
#overlay video,
#overlay audio,
#overlay img {
margin: auto;
max-height: 100%;
max-width: 86%;
}
#overlay span {
color: var(--background-color);
text-shadow: 0 0 .3rem var(--secondary-color);
}
table { overflow-y: scroll; width: 100%; }
tr { vertical-align: top; }
tr:hover { background-color: var(--primary-color); color: white; }
tr:hover a { color: white; }
th { text-align: left; }
th:nth-child(2),
th:last-child { width: 1%; white-space: nowrap; }
th,
td { line-break: strict; }
th:nth-child(2),
td:nth-child(2) { padding: 0 1rem; }
td a { display: block; width: 100%; }
td a:hover { transition: none; }
td:nth-child(2),
td:last-child { white-space: nowrap; }
footer {
font-size: .8rem;
text-align: center;
padding: 1rem 0; }
@media screen and (max-width: 640px) {
header { display: block; }
#logo {
margin: 0 auto;
width: 100%; }
nav {
width: 100%;
text-align: center; } }

View File

@ -1,56 +0,0 @@
const video_formats = ["webm", "mp4"];
const audio_formats = ["mp3", "flac", "opus", "ogg", "m4a"];
const image_formats = ["jpg", "jpeg", "gif", "png", "bmp", "webp"];
const overlay = document.getElementById("overlay");
let g_scale = 1;
let g_volume = 1.0;
function mousescroll(e) {
e.preventDefault();
g_scale = Math.min(Math.max(0.25, g_scale + (e.deltaY * -0.001)), 4);
e.target.style.transform = `scale(${g_scale})`;
}
function onvolumechange(e) {
g_volume = e.target.volume;
}
const ext_filter = (ext, pathname) => pathname.toLowerCase().endsWith(ext);
function to_overlay(eltyp, pathname) {
const el = document.createElement(eltyp);
const el_label = document.createElement("span");
el_label.textContent = decodeURI(pathname.substr(pathname.lastIndexOf("/") + 1));
if (eltyp !== "audio") el.addEventListener('wheel', mousescroll);
if (eltyp !== "img") {
el.autoplay = el.controls = true;
el.addEventListener("volumechange", onvolumechange);
el.volume = g_volume;
}
el.src = pathname;
overlay.appendChild(el);
overlay.appendChild(el_label);
overlay.style.visibility = "visible";
}
document.getElementById("overlay").addEventListener("click", e => {
e.target.firstChild.remove();
e.target.firstChild.remove();
e.target.style.visibility = "hidden";
g_scale = 1;
});
const file_links = Array.from(document.getElementsByTagName('tr')).slice(2).filter(e => e.lastChild.innerHTML != "DIR").map(l => l.firstChild.firstChild);
file_links.forEach(f => f.addEventListener('click', e => {
const pathname = e.target.pathname;
if (video_formats.some(ext => ext_filter(ext, pathname)))
to_overlay("video", pathname);
else if (audio_formats.some(ext => ext_filter(ext, pathname)))
to_overlay("audio", pathname);
else if (image_formats.some(ext => ext_filter(ext, pathname)))
to_overlay("img", pathname);
if (overlay.firstChild != null)
e.preventDefault();
}));

View File

@ -1,2 +0,0 @@
User-agent: *
Disallow: /assets/

View File

@ -1,44 +0,0 @@
doctype html
html(lang='en')
head
title Arav's dwelling / Files
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='My file share.')
link(rel='icon' type='image/svg+xml' href='/shared/img/favicon.svg' sizes='any')
link(href='/assets/css/main.css' rel='stylesheet')
script(src='/assets/js/main.js' defer)
body
header
svg#logo(viewBox='0 -25 216 40')
text.logo Arav's dwelling
text.under(y='11') Welcome to my sacred place, wanderer
nav
a(href=main_site) Back to main website
h1 Files
section#files
span#current-path!= current_path
p Files: #{total_files} (#{total_files_size}); Directories: #{total_directories}.
table
thead
tr
th Name
th Date
th Size
tbody
tr
td #[a(href="../") ../]
each item in items
tr
td #[a(href=item.link)= item.name]
td!= moment(item.datetime, clientTZ)
td= item.size
section#privacy
h2 Privacy statements
p I collect access logs that include access date and time, IP-address, User-Agent, referer URL that tells me where have you came from, request that you sent to me. In addition there are GeoIP information added based on your IP-address that includes country, region, and city for my convenience.
p This site makes use of JavaScript purely for convenient functionality, like being able to watch video, listen to music, and look images in an overlay without the need to open a file in a new tab or return back.
footer
| 2017&mdash;2022 Arav &lt;#[a(href='mailto:me@arav.top') me@arav.top]&gt;
div#overlay

View File

@ -1,2 +0,0 @@
exports.port = 32251;
exports.host = "127.0.0.1";

View File

@ -1,79 +0,0 @@
const fs = require("fs");
const path = require("path");
const uti = require("util");
const exec = uti.promisify(require("child_process").exec);
const Koa = require("koa");
const koaPug = require("koa-pug");
const koaRouter = require("koa-router");
const fetch = require("node-fetch");
const moment = require("moment-timezone");
const config = require("./config");
const util = require("../shared/util");
async function getRadioStatus() {
try {
let status = await fetch('http://radio.arav.home.arpa/status-json.xsl').then(r => r.json());
return {
server_start_iso8601: status.icestats.source.stream_start_iso8601,
server_start_date: util.datetime(status.icestats.source.stream_start_iso8601, util.date_formats.post_date),
song: `${status.icestats.source.artist} - ${status.icestats.source.title}`,
listener_peak: status.icestats.source.listener_peak,
listeners: status.icestats.source.listeners
}
} catch {
return {
server_start_iso8601: "n/a",
server_start_date: "n/a",
song: "n/a",
listener_peak: "n/a",
listeners: "n/a"
}
}
}
async function getLastPlayedSongs(count, tz) {
try {
const { stdout, _ } = await exec(`tail -n${count} /var/log/icecast/playlist.log | head -n-1 | cut -d"|" -f1,4`);
let songs = stdout.trim().split("\n");
for (let i = 0; i < songs.length; ++i) {
let [t, s] = songs[i].split('|');
t = moment(t, "DD/MMM/YYYY:HH:mm:ss ZZ").tz(tz).format("HH:mm");
let song = s.split(' - ');
songs[i] = { "start_time_local": t, "artist": song[0], "title": song[1] };
}
return songs;
} catch {
return [];
}
}
function setRoutes() {
return koaRouter().get('/', async ctx => {
await ctx.render('index', {
main_site: util.getServiceByHost(ctx.header.host),
radio_status: await getRadioStatus(),
last_songs: await getLastPlayedSongs(11, util.getClientTimezone(ctx))
});
})
.get('/stats', async ctx => {
ctx.body = await getRadioStatus();
})
.get('/lastsong', async ctx => {
ctx.body = (await getLastPlayedSongs(2, util.getClientTimezone(ctx)))[0];
});
}
const app = new Koa();
const pug = new koaPug({
viewPath: path.join(__dirname, "views"),
locals: {
moment: util.datetime },
app: app
});
app.proxy = true;
app
.use(setRoutes().routes())
.listen(config.port, config.host);

View File

@ -1,154 +0,0 @@
@font-face {
font-family: 'Roboto Condensed';
font-style: normal;
font-weight: 400;
src: local('RobotoCondensed'), local('RobotoCondensed-Regular'),
url(/shared/fonts/RobotoCondensed-Regular.ttf); }
:root {
--background-color: #0a0a0a;
--primary-color: #cd2682;
--secondary-color: #9f2b68;
--text-color: #f5f5f5;
--text-indent: 1.6rem;
scrollbar-color: var(--primary-color) var(--background-color); }
@media (prefers-color-scheme: light) {
:root {
--background-color: #f5f5f5;
--primary-color: #9f2b68;
--secondary-color: #cd2682;
--text-color: #0a0a0a; } }
* { margin: 0; }
::selection {
background-color: var(--secondary-color);
color: var(--background-color); }
a,
button {
color: var(--primary-color);
text-decoration: none; }
a:hover,
button:hover {
color: var(--secondary-color);
cursor: pointer;
text-decoration: underline dotted;
transition: .5s; }
button {
background: none;
border: none;
font: inherit;
padding: 0; }
p {
text-align: justify;
line-height: var(--text-indent);
text-indent: var(--text-indent); }
p:not(:last-child) { margin-bottom: .1rem; }
h1,
h2 {
font-size: 1.8rem;
font-variant: small-caps;
text-align: center;
margin-bottom: 1rem; }
h2 {
font-size: 1.4rem;
margin: 1rem 0; }
small { font-size: .8rem; }
small.player-links a { margin: 0 .2rem; }
audio {
background-color: var(--primary-color);
box-shadow: 5px 5px var(--primary-color);
width: 100%; }
@media screen and (-webkit-min-device-pixel-ratio:0) {
audio::-webkit-media-controls-panel {
background-color: var(--secondary-color); }
audio { border-radius: 1.6rem; } }
@-moz-document url-prefix() {
audio { border-radius: 0; } }
html { margin-left: calc(100vw - 100%); }
body {
background-color: var(--background-color);
color: var(--text-color);
font-family: 'Roboto Condensed', Roboto, sans-serif;
font-size: 1.1rem;
margin: 0 auto;
max-width: 960px;
width: 98%; }
header {
display: flex;
flex-wrap: wrap;
justify-content: space-between; }
#logo {
display: block;
width: 360px; }
#logo text { fill: var(--text-color); }
#logo .logo {
font-size: 2rem;
font-variant-caps: small-caps;
font-weight: bold; }
@media screen and (-webkit-min-device-pixel-ratio:0) {
#logo .logo { font-size: 2.082rem; } }
@-moz-document url-prefix() {
#logo .logo { font-size: 2rem; } }
#logo .under { font-size: .88rem; }
nav { margin-top: .5rem; }
nav a { font-variant: small-caps; }
nav h1 {
color: var(--secondary-color);
margin: 0; }
section { margin-top: 1rem; }
#last-played {
margin: 0 auto;
min-width: 80%;
width: 80%; }
#last-played tbody tr {
display: grid;
gap: .5rem;
grid-template-columns: 3rem 1fr 1fr; }
#last-played tbody tr td:nth-child(2) { text-align: right; }
footer {
font-size: .8rem;
text-align: center;
padding: 1rem 0; }
@media screen and (max-width: 640px) {
header { display: block; }
#logo {
margin: 0 auto;
width: 100%; }
nav {
width: 100%;
text-align: center; } }

View File

@ -1,9 +0,0 @@
#EXTM3U
#EXTINF:-1,Arav's dwelling / Radio
http://radio.arav.top:8000/stream.ogg
#EXTINF:-1,Arav's dwelling / Radio (HTTPS)
https://radio.arav.top/live/stream.ogg
#EXTINF:-1,Arav's dwelling / Radio on Tor
http://wsmkgnmhmzqm7kyzv7jnzzafvgm7xlmlfvzhgorpapd5or2arnhuktqd.onion/live/stream.ogg
#EXTINF:-1,Arav's dwelling / Radio on I2P
http://radio.arav.i2p/live/stream.ogg

View File

@ -1,48 +0,0 @@
function $(id) { return document.getElementById(id); }
function updateRadioStatus() {
fetch("/stats")
.then(r => r.json())
.then(r => {
$("radio-status").innerHTML =
`On-air since <time datetime="${r.server_start_iso8601}">${r.server_start_date}</time>`;
$("radio-song").textContent = r.song;
$("radio-listeners").textContent = r.listeners;
$("radio-listener-peak").textContent = r.listener_peak;
}).catch(() => {
$("radio-status").textContent = "Radio is offline.";
$("radio-song").textContent =
$("radio-listeners").textContent =
$("radio-listener-peak").textContent = "n/a";
});
}
function updateLastPlayedSong() {
fetch('/lastsong')
.then(r => r.json())
.then(last_played => {
let cur_artist = $('last-played').firstChild.lastChild.children[1].innerText;
let cur_title = $('last-played').firstChild.lastChild.lastChild.innerText;
if (last_played.artist == cur_artist && last_played.title == cur_title)
return;
$('last-played').firstChild.firstChild.remove();
let row = $('last-played').insertRow();
let start_time = row.insertCell();
start_time.appendChild(document.createTextNode(last_played.start_time_local));
let artist_cell = row.insertCell();
artist_cell.appendChild(document.createTextNode(last_played.artist));
let title_cell = row.insertCell();
title_cell.appendChild(document.createTextNode(last_played.title));
});
}
document.getElementById("btn-update").addEventListener("click", () => {
updateLastPlayedSong();
updateRadioStatus();
})
setInterval(updateRadioStatus, 45000);
setInterval(updateLastPlayedSong, 45000);

View File

@ -1,3 +0,0 @@
User-agent: *
Disallow: /assets/
Disallow: /live/

View File

@ -1,60 +0,0 @@
mixin radioStatus(date, iso)
if (date != "n/a")
p #[span#radio-status On-air since #[time(datetime=iso)= date]]
else
p #[span#radio-status Radio is offline.]
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='/shared/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#logo(viewBox='0 -25 216 40')
text.logo Arav's dwelling
text.under(y='11') Welcome to my sacred place, wanderer
nav
a(href=main_site) Back to main website
h1 Radio
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.top: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
audio(preload='none' controls)
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]!
+radioStatus(radio_status.server_start_date, radio_status.server_start_iso8601)
p Now playing: #[span#radio-song= radio_status.song]
p Current/peak listeners: #[span#radio-listeners= radio_status.listeners] / #[span#radio-listener-peak= radio_status.listener_peak]
p
small Notice: information updates every 45 seconds. But you can #[button(id='btn-update') update] it forcibly.
if (last_songs)
section
h2 Last 10 songs
table#last-played
each song in last_songs
tr
td= song.start_time_local
td= song.artist
td= song.title
section
p The largest number of simultaneous listeners was #[b 7] at #[time(datetime='2022-02-19') 19 February 2022], and the song was &quot;Röyksopp - 49 Percent&quot;.
section
h2 Privacy statements
p Logs are collected and include access date and time, IP-address, User-Agent, referer URL, request. This website makes use of JavaScript to update a radio status and last 10 songs list.
footer
| 2017&mdash;2022 Arav &lt;#[a(href='mailto:me@arav.top') me@arav.top]&gt;