Merge pull request 'feat: new functionality' (#1) from feats into main

Reviewed-on: #1
Выглядит оче K.P.A.C.U.B.O!
This commit is contained in:
mayekkuzu 2024-11-25 12:05:05 +03:00
commit 07efbd58ec
5 changed files with 481 additions and 390 deletions

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.vscode

View File

@ -1,105 +1,128 @@
<!DOCTYPE html><html lang="en"><head> <!DOCTYPE html>
<html lang="en">
<head>
<!-- This website is not affiliated with either lainon.life or fauux. --> <!-- This website is not affiliated with either lainon.life or fauux. -->
<!-- https://github.com/debil03311/fauuxonlife --> <!-- https://github.com/debil03311/fauuxonlife -->
<meta charset="UTF-8"> <meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>fauuxon.life</title> <title>SCUF FM Radio</title>
<link rel="shortcut icon" href="./public/asset/favicon.ico" type="image/x-icon"> <link
rel="shortcut icon"
href="./public/asset/favicon.ico"
type="image/x-icon"
/>
<!-- Regular --> <!-- Regular -->
<meta name="theme-color" content="#d2738a"> <meta name="theme-color" content="#d2738a" />
<meta name="image" content="https://debil03311.github.io/fauuxonlife/public/asset/embed.png"> <meta name="image" content="./public/asset/embed.png" />
<meta name="title" content="lainon.life interface"> <meta name="title" content="SCUF FM Radio" />
<meta name="description" content="fauux-inspired, unaffiliated, third party interface for the lainon.life web radio"> <meta name="description" content="Сидим кайфуем!" />
<!-- OpenGraph --> <!-- OpenGraph -->
<meta property="og:type" content="website"> <meta property="og:type" content="website" />
<meta property="og:image" content="https://debil03311.github.io/fauuxonlife/public/asset/embed.png"> <meta property="og:image" content="../public/asset/embed.png" />
<meta property="og:site_name" content="fauuxon.life"> <meta property="og:site_name" content="SCUF FM Radio" />
<meta property="og:title" content="lainon.life interface"> <meta property="og:title" content="SCUF FM Radio" />
<meta property="og:description" content="fauux-inspired, unaffiliated, third party interface for the lainon.life web radio"> <meta property="og:description" content="Сидим кайфуем!" />
<!-- Twitter --> <!-- Twitter -->
<meta property="twitter:card" content="summary"> <meta property="twitter:card" content="summary" />
<meta property="twitter:image" content="https://debil03311.github.io/fauuxonlife/public/asset/embed.png"> <meta property="twitter:image" content="../public/asset/embed.png" />
<meta property="twitter:title" content="lainon.life interface"> <meta property="twitter:title" content="SCUF FM Radio" />
<meta property="twitter:description" content="fauux-inspired, unaffiliated, third party interface for the lainon.life web radio"> <meta property="twitter:description" content="Сидим кайфуем!" />
<link rel="stylesheet" href="./public/asset/icofont/icofont.min.css"> <link rel="stylesheet" href="./public/asset/icofont/icofont.min.css" />
<link rel="stylesheet" href="./public/css/index.css"> <link rel="stylesheet" href="./public/css/index.css" />
<link rel="stylesheet" href="./public/css/index.m.css"> <link rel="stylesheet" href="./public/css/index.m.css" />
</head><body> </head>
<body>
<div id="darken" class="overlay no-select"></div> <div id="darken" class="overlay no-select"></div>
<div id="scanlines" class="overlay no-select"></div> <div id="scanlines" class="overlay no-select"></div>
<div id="vignette" class="overlay no-select"></div> <div id="vignette" class="overlay no-select"></div>
<div id="warning-message" class="overlay hidden" onclick="this.classList.add('hidden')"> <div
<div id="warning-content"> id="warning-message"
<h2>Something went wrong.</h2> class="overlay hidden"
onclick="this.classList.add('hidden')"
<p> >
Failed to fetch the necessary data. Check to see if <a href="https://lainon.life">lainon.life</a> is down. <div id="warning-content">
</p> <h2>Что-то пошло не так, бля. Починим!</h2>
</div>
<p>
If it's not down then there's an issue with CORS, but you can probably it yourself! All you have to do is get an extension/add-on for your browser that enables CORS anywhere. If you're Chromium-based, check <a href="https://chrome.google.com/webstore/detail/allow-cors-access-control/lhobafahddgcelffkeicbaginigeejlf">this one</a> out. If you're on FireFox, I have no idea, maybe <a href="https://addons.mozilla.org/en-US/firefox/addon/cors-everywhere/">this one</a>? Do keep in mind that enforcing CORS at all times could break some other sites you may frequent.
</p>
</div>
</div> </div>
<audio id="audio" class="hidden" <audio
preload="none" id="audio"
src="https://lainon.life/radio/cafe.ogg" class="hidden"
preload="none"
src="https://lainon.life/radio/cafe.ogg"
></audio> ></audio>
<header> <header>
fauux(<a href="https://lainon.life/">lainon.life</a>): [<span class="listeners current" title="Listening">0</span>, <span class="listeners unique" title="UNIQUYE">0</span>, <span class="listeners total" title="Peak Listeners">0</span>] <span>=SCUF FM=</span>
<span class="header-stats">Stats</span>
<span>
[<span class="listeners current" title="Listening">0</span>,
<span class="listeners unique" title="Unique">0</span>,
<span class="listeners total" title="Peak Listeners">0</span>]
</span>
</header> </header>
<section id="player"> <section id="player">
<div id="progress-bar" <div id="progress-bar" style="width: 0%" class="no-select"></div>
style="width: 0%"
class="no-select">
</div>
<div id="volume-bar" <div id="volume-bar" style="width: 20%" class="no-select"></div>
style="width: 20%"
class="no-select">
</div>
<div id="current-details" class="no-select"> <div id="current-details" class="no-select">
<span id="current-song"> <span id="current-song">
<span id="current-title">NOT PLAYING</span> <span id="current-title">NOT PLAYING</span>
<span id="current-playlist">NULL</span> <span id="current-playlist">NULL</span>
</span> </span>
<span id="current-artist">NULL</span>
<span id="current-artist">NULL</span> </div>
</div>
</section> </section>
<section id="channels"> <section id="channels">
<i id="swing" <i
title="swing" id="swing"
class="channel icofont-ui-play" title="swing"
onclick="switchChannel(this.id)" class="channel icofont-ui-play"
></i> onclick="switchChannel(this.id)"
></i>
<i
id="pause"
title="pause"
class="channel icofont-ui-pause active"
onclick="pauseRadio()"
></i>
<i
id="mute"
title="mute"
class="control icofont-ui-mute"
onclick="toggleMute()"
></i>
</section> </section>
<section id="song-list"> <section id="song-list">
<div id="list-previous"></div> <div id="list-previous"></div>
<div id="list-current"> <div id="list-current">Press play to play. Vot tak vot!</div>
Select one of the channels above to start listening.<br>
This project is <a href="https://github.com/debil03311/fauuxonlife">open source</a> and unaffiliated with either lainon.life or fauux.
</div>
<div id="list-next"></div> <div id="list-next"></div>
</section> </section>
<script defer src="./public/js/index.js"></script> <section id="social">
</body></html> <a href="https://t.me/scuf_fm" target="_blank"
><i class="social icofont-telegram"></i
></a>
<a href="https://y2z.ru" target="_blank"
><i class="social icofont-web"></i
></a>
</section>
<script defer src="./public/js/index.js"></script>
</body>
</html>

View File

@ -1,150 +1,169 @@
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Zen+Maru+Gothic:wght@400;500;700&display=swap'); @import url("https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Zen+Maru+Gothic:wght@400;500;700&display=swap");
:root { :root {
--pink: #d2738a; --pink: #d2738a;
--pale: #c1b492; --pale: #c1b492;
--bgcolor: #1e1b1e; --bgcolor: #1e1b1e;
--white: #ddd;
} }
::selection { ::selection {
background-color: var(--pink); background-color: var(--pink);
color: #000; color: #000;
} }
::-webkit-scrollbar { ::-webkit-scrollbar {
--size: 8px; --size: 8px;
width: var(--size); width: var(--size);
height: var(--size); height: var(--size);
background-color: var(--bgcolor); background-color: var(--bgcolor);
} }
::-webkit-scrollbar-corner { ::-webkit-scrollbar-corner {
background-color: var(--bgcolor); background-color: var(--bgcolor);
} }
::-webkit-scrollbar-thumb { ::-webkit-scrollbar-thumb {
background-color: var(--pink); background-color: var(--pink);
box-shadow: inset 0 0 0 1px var(--bgcolor); box-shadow: inset 0 0 0 1px var(--bgcolor);
} }
* { * {
box-sizing: border-box; box-sizing: border-box;
} }
.hidden { .hidden {
display: none !important; display: none !important;
} }
.no-select { .no-select {
pointer-events: none !important; pointer-events: none !important;
user-select: none !important; user-select: none !important;
-moz-user-select: none !important; -moz-user-select: none !important;
-webkit-user-select: none !important; -webkit-user-select: none !important;
}
body {
min-height: 100vh;
background: #000 url("../asset/bg205.gif");
color: var(--pale);
font: 500 1em "Share Tech Mono", "Zen Maru Gothic", monospace;
line-height: 1.4em;
text-shadow: 0 0 10px #c1b49264;
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
a,
.listeners
{
color: var(--pink);
text-decoration: none;
text-shadow: 0 0 10px #d2738a64;
}
a:hover {
text-decoration: underline;
}
.overlay {
position: fixed;
top: 0; left: 0;
width: 100vw;
height: 100vh;
}
#darken {
background-color: #0006;
z-index: -1;
}
#scanlines {
background: url("../asset/scanlines.png");
opacity: .6;
z-index: 2;
}
#vignette {
box-shadow: inset 0 0 64px #000;
z-index: 2;
}
#warning-message {
background-color: #040004f4;
backdrop-filter: blur(4px);
padding: 2em;
cursor: pointer;
display: grid;
place-items: center;
z-index: 2;
}
#warning-message h1 {
color: var(--pink);
text-align: center;
}
#warning-content {
max-width: 800px;
}
header,
section
{
width: clamp(256px, 896px, 90%);
background-color: var(--bgcolor);
box-shadow: 0 0 16px #d2738a32;
padding: 1rem;
margin: .8rem;
} }
header { header {
text-align: center; display: flex;
font-size: 2.4rem; flex-direction: column;
font-weight: 800; align-items: center;
justify-content: center;
gap: 16px;
}
.header-stats {
font-size: 16px;
}
body {
min-height: 100vh;
background: #000 url("../asset/bg205.gif");
color: var(--pale);
font: 500 1em "Share Tech Mono", "Zen Maru Gothic", monospace;
line-height: 1.4em;
text-shadow: 0 0 10px #c1b49264;
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
a,
.listeners {
color: var(--pink);
text-decoration: none;
text-shadow: 0 0 10px #d2738a64;
}
a:hover {
text-decoration: underline;
}
.overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
}
#darken {
background-color: #0006;
z-index: -1;
}
#scanlines {
background: url("../asset/scanlines.png");
opacity: 0.6;
z-index: 2;
}
#vignette {
box-shadow: inset 0 0 64px #000;
z-index: 2;
}
#warning-message {
background-color: #040004f4;
backdrop-filter: blur(4px);
padding: 2em;
cursor: pointer;
display: grid;
place-items: center;
z-index: 2;
}
#warning-message h1 {
color: var(--pink);
text-align: center;
}
#warning-content {
max-width: 800px;
}
header,
section {
width: clamp(256px, 896px, 90%);
background-color: var(--bgcolor);
box-shadow: 0 0 16px #d2738a32;
padding: 1rem;
margin: 0.8rem;
}
header {
text-align: center;
font-size: 2.4rem;
font-weight: 800;
} }
#player { #player {
position: relative; position: relative;
background-color: var(--pale); background-color: var(--pale);
color: #000; color: #000;
text-shadow: 0 0 10px #0004; text-shadow: 0 0 10px #0004;
font-size: 1.4rem; font-size: 1.4rem;
cursor: e-resize; cursor: e-resize;
/* display: flex; transition: color 0.2s ease-in;
/* display: flex;
align-items: center; */ align-items: center; */
} }
#player.paused {
color: rgba(0, 0, 0, 0.3);
}
#progress-bar { #progress-bar {
position: absolute; position: absolute;
top: 0; left: 0; top: 0;
height: 100%; left: 0;
background-color: var(--pink); height: 100%;
background-color: var(--pink);
} }
/* #progress-bar::after { /* #progress-bar::after {
@ -158,42 +177,46 @@ header {
} */ } */
#volume-bar { #volume-bar {
position: absolute; position: absolute;
height: 3px; height: 4px;
top: 6px; top: 6px;
left: 0; left: 0;
background-color: var(--pale); background-color: var(--pale);
background-color: #000; background-color: #000;
box-shadow: 0 0 10px var(--pink); box-shadow: 0 0 10px var(--pink);
transition: opacity 0.2s ease-in;
}
#volume-bar.muted {
opacity: 0.3;
} }
#current-details { #current-details {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
#current-details span { #current-details span {
z-index: 1; z-index: 1;
} }
#current-song { #current-song {
margin: 8px 0; margin: 8px 0;
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: flex-start; align-items: flex-start;
} }
#current-title, #current-title,
#current-date #current-date {
{ font-size: 2rem;
font-size: 2rem; font-weight: 800;
font-weight: 800; line-height: 1em;
line-height: 1em;
} }
#current-date { #current-date {
text-align: right; text-align: right;
} }
/* #current-date::before { /* #current-date::before {
@ -205,51 +228,76 @@ header {
} */ } */
#current-album::before { #current-album::before {
content: "from "; content: "from ";
} }
#current-artist::before { #current-artist::before {
content: "by "; content: "by ";
} }
#song-list { #song-list {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
background-repeat: no-repeat;
background-size: contain;
background-position: right 72px top;
background-blend-mode: exclusion;
} }
#list-previous { #list-previous {
text-shadow: none; text-shadow: none;
opacity: .6; opacity: 0.6;
display: flex; display: flex;
flex-direction: column-reverse; flex-direction: column-reverse;
} }
#list-current { #list-current {
color: #DDD; color: #ddd;
font-weight: 600; font-weight: 600;
} }
.song-entry { .song-entry {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
} }
#channels { #channels {
font-size: 2rem; font-size: 2rem;
display: flex;
display: flex; justify-content: center;
justify-content: center; gap: 1em;
gap: 1em;
} }
.channel.active { #social {
color: #DDD; font-size: 2rem;
text-shadow: 0 0 32px var(--pink); display: flex;
pointer-events: none; justify-content: center;
gap: 1em;
} }
.channel:not(.active):hover { #social a:hover {
color: var(--pink); text-decoration: none;
cursor: pointer; }
#social .social:hover {
color: var(--white);
}
.channel.active,
.control.active {
color: #ddd;
text-shadow: 0 0 32px var(--pink);
pointer-events: none;
}
.control.active {
pointer-events: auto;
cursor: pointer;
}
.channel:not(.active):hover,
.control:not(.active):hover {
color: var(--pink);
cursor: pointer;
} }

View File

@ -1,14 +1,30 @@
@media only screen and (max-width: 800px) { @media only screen and (max-width: 800px) {
header { header {
font-size: 1.4rem; font-size: 1.4rem;
} }
#current-song { #current-song {
line-height: 1.4em; line-height: 1.4em;
flex-direction: column; flex-direction: column;
} }
#current-date { #current-date {
font-size: 1rem; font-size: 1rem;
} }
#song-list {
background-size: cover;
background-position: center;
font-size: 1.2rem;
}
#list-previous {
opacity: 0.9;
}
.entry-details,
.entry-length {
text-shadow: 1px 1px 0 #000, -1px 1px 0 #000, -1px -1px 0 #000,
1px -1px 0 #000;
}
} }

View File

@ -1,25 +1,26 @@
// HTML Elements // HTML Elements
const e_songDetails = { const e_songDetails = {
title: document.getElementById("current-title"), title: document.getElementById("current-title"),
playlist: document.getElementById("current-playlist"), playlist: document.getElementById("current-playlist"),
artist: document.getElementById("current-artist"), artist: document.getElementById("current-artist"),
};
} const e_playlist = document.getElementById("current-playlist");
const e_playlist = document.getElementById("current-playlist") const e_songListSection = document.getElementById("song-list");
const e_songList = { const e_songList = {
next: document.getElementById("list-next"), next: document.getElementById("list-next"),
current: document.getElementById("list-current"), current: document.getElementById("list-current"),
previous: document.getElementById("list-previous"), previous: document.getElementById("list-previous"),
} };
const e_listeners = { const e_listeners = {
current: document.querySelector(".listeners.current"), current: document.querySelector(".listeners.current"),
total: document.querySelector(".listeners.total"), total: document.querySelector(".listeners.total"),
unique: document.querySelector(".listeners.unique"), unique: document.querySelector(".listeners.unique"),
} };
const e_audio = document.getElementById("audio"); const e_audio = document.getElementById("audio");
e_audio.volume = 0.2; e_audio.volume = 0.2;
@ -35,33 +36,28 @@ const e_channels = document.querySelectorAll(".channel");
* @param {int} limit - The maximum returnable integer * @param {int} limit - The maximum returnable integer
*/ */
const rRng=(limit)=> Math.floor(Math.random() * limit); const rRng = (limit) => Math.floor(Math.random() * limit);
/** /**
* Return a random item from a given array. * Return a random item from a given array.
* @param {Array} array - The maximum returnable integer * @param {Array} array - The maximum returnable integer
*/ */
const rArr=(array)=> array[rRng(array.length)]; const rArr = (array) => array[rRng(array.length)];
// :^) // :^)
const lainon = { const lainon = {
life: "https://radio.scuf.ru/", life: "https://radio.scuf.ru/",
} };
// Set a random background image // Set a random background image
const backgrounds = [ const backgrounds = ["bg205.gif", "bg_307.gif", "bg_315.gif", "patternTV2.gif"];
"bg205.gif",
"bg_307.gif",
"bg_315.gif",
"patternTV2.gif",
];
document.body.style.setProperty( document.body.style.setProperty(
"background-image", "background-image",
`url("./public/asset/${rArr(backgrounds)}")` `url("./public/asset/${rArr(backgrounds)}")`
); );
// Default settings // Default settings
@ -76,21 +72,20 @@ let progressStep = 0;
*/ */
async function fetchData() { async function fetchData() {
// Since lainon.life doesn't allow CORS, the request must be // Since lainon.life doesn't allow CORS, the request must be
// made through a proxy. This "crosscloak" is my fork of // made through a proxy. This "crosscloak" is my fork of
// cors-anywhere, and just in case you're thinking about it, // cors-anywhere, and just in case you're thinking about it,
// no, it's not public and your domain is not on the whitelist. // no, it's not public and your domain is not on the whitelist.
const data = await const data = await fetch(`https://radio.scuf.ru/api/nowplaying/scuf_fm`)
fetch(`https://radio.scuf.ru/api/nowplaying/scuf_fm`) .then((response) => response.json())
.then((response)=> response.json()) .catch((ERROR) => {
.catch((ERROR)=> { console.error(ERROR);
console.error(ERROR); e_warningMessage.classList.remove("hidden");
e_warningMessage.classList.remove("hidden"); });
});
parseData(data); parseData(data);
return data; return data;
} }
/** /**
@ -99,50 +94,48 @@ async function fetchData() {
*/ */
function parseData(radioData) { function parseData(radioData) {
// Update listeners // Update listeners
for (const listenerType in radioData.listeners) { for (const listenerType in radioData.listeners) {
e_listeners[listenerType].innerText = radioData.listeners[listenerType] e_listeners[listenerType].innerText = radioData.listeners[listenerType];
} }
// Go over the radioData object. // Go over the radioData object.
// If the object exists, set the corresponding element's // If the object exists, set the corresponding element's
// innerText to its value, or otherwise to say that it // innerText to its value, or otherwise to say that it
// wasn't found. // wasn't found.
for (const itemType in e_songDetails) { for (const itemType in e_songDetails) {
e_songDetails[itemType].innerText = ( e_songDetails[itemType].innerText = radioData.now_playing.song[itemType]
(radioData.now_playing.song[itemType]) ? radioData.now_playing.song[itemType]
? radioData.now_playing.song[itemType] : `${itemType.toUpperCase()} NOT FOUND`;
: `${itemType.toUpperCase()} NOT FOUND` }
);
}
e_playlist.innerText = radioData.now_playing.playlist e_playlist.innerText = radioData.now_playing.playlist;
// Current song's length in seconds // Current song's length in seconds
const songLength = radioData.now_playing.duration; const songLength = radioData.now_playing.duration;
// Increment for the progress bar as a percentage of 100 // Increment for the progress bar as a percentage of 100
progressStep = (1/songLength) * 100; progressStep = (1 / songLength) * 100;
// Set the progress bar to display the current song's elapsed time // Set the progress bar to display the current song's elapsed time
e_progressBar.style.setProperty( e_progressBar.style.setProperty(
"width", "width",
(progressStep * Math.floor(radioData.now_playing.elapsed)) + "%" progressStep * Math.floor(radioData.now_playing.elapsed) + "%"
); );
// Clear the entire song list e_songListSection.style = `background-image: url(${radioData.now_playing.song.art})`;
for (const element in e_songList)
e_songList[element].innerHTML = "";
// Fill in the upcoming songs // Clear the entire song list
const {song,duration} = radioData.playing_next for (const element in e_songList) e_songList[element].innerHTML = "";
const e_songEntry = document.createElement("div");
e_songEntry.className = "song-entry";
e_songEntry.innerHTML = ` // Fill in the upcoming songs
const { song, duration } = radioData.playing_next;
const e_songEntry = document.createElement("div");
e_songEntry.className = "song-entry";
e_songEntry.innerHTML = `
<div class="entry-details"> <div class="entry-details">
<span>Next FLEX: </span>
<span class="entry-artist">${song.artist}</span> <span class="entry-artist">${song.artist}</span>
- -
<span class="entry-title">${song.title}</span> <span class="entry-title">${song.title}</span>
@ -150,39 +143,45 @@ function parseData(radioData) {
<span class="entry-length">${toMinSec(duration).join(":")}</span> <span class="entry-length">${toMinSec(duration).join(":")}</span>
`; `;
e_songList.next.appendChild(e_songEntry); e_songList.next.appendChild(e_songEntry);
// Display the current song in the song list
// Display the current song in the song list e_songList.current.innerHTML = `
e_songList.current.innerHTML = `
<div class="song-entry"> <div class="song-entry">
<div class="entry-details"> <div class="entry-details">
<span>Current FLEX: </span> <span class="entry-artist">${
<span class="entry-artist">${radioData.now_playing.song.artist}</span> radioData.now_playing.song.artist
}</span>
- -
<span class="entry-title">${radioData.now_playing.song.title}</span> <span class="entry-title">${
radioData.now_playing.song.title
}</span>
</div> </div>
<span class="entry-length">${toMinSec(radioData.now_playing.duration).join(":")}</span> <span class="entry-length">${toMinSec(
radioData.now_playing.duration
).join(":")}</span>
</div> </div>
`; `;
// Fill in the previous songs // Fill in the previous songs
for (const song of radioData.song_history) { for (const song of radioData.song_history) {
const e_songEntry = document.createElement("div"); const e_songEntry = document.createElement("div");
e_songEntry.className = "song-entry"; e_songEntry.className = "song-entry";
e_songEntry.innerHTML = ` e_songEntry.innerHTML = `
<div class="entry-details"> <div class="entry-details">
<span class="entry-artist">${song.song.artist}</span> <span class="entry-artist">${song.song.artist}</span>
- -
<span class="entry-title">${song.song.title}</span> <span class="entry-title">${song.song.title}</span>
</div> </div>
<span class="entry-length">${toMinSec(song.duration).join(":")}</span> <span class="entry-length">${toMinSec(song.duration).join(
":"
)}</span>
`; `;
e_songList.previous.appendChild(e_songEntry); e_songList.previous.appendChild(e_songEntry);
} }
} }
/** /**
@ -192,10 +191,7 @@ function parseData(radioData) {
*/ */
function toMinSec(totalSeconds) { function toMinSec(totalSeconds) {
return [ return [padZero(Math.floor(totalSeconds / 60)), padZero(totalSeconds % 60)];
padZero(Math.floor(totalSeconds/60)),
padZero(totalSeconds % 60)
];
} }
/** /**
@ -205,9 +201,7 @@ function toMinSec(totalSeconds) {
*/ */
function padZero(integer) { function padZero(integer) {
return (integer < 10) return integer < 10 ? "0" + integer : "" + integer;
? "0" + integer
: "" + integer
} }
/** /**
@ -216,27 +210,45 @@ function padZero(integer) {
*/ */
function switchChannel(channelName) { function switchChannel(channelName) {
console.log(e_audio.paused,isPlaying) e_player.classList.remove("paused");
if (e_audio.paused === false) { // Remove active class from all DOM channel elements
document.getElementById(channelName).classList.remove("active"); e_channels.forEach((el) => el.classList.remove("active"));
e_audio.pause()
isPlaying = false
currentChannel = "cafe"
e_audio.src = undefined
return
}
// Remove active class from all DOM channel elements
e_channels.forEach((el)=> el.classList.remove("active"));
// Add it to the clicked one // Add it to the clicked one
document.getElementById(channelName).classList.add("active"); document.getElementById(channelName).classList.add("active");
currentChannel = channelName; currentChannel = channelName;
e_audio.src = `https://radio.scuf.ru/listen/scuf_fm/radio.mp3`; e_audio.src = `https://radio.scuf.ru/listen/scuf_fm/radio.mp3`;
e_audio.play(); e_audio.play();
fetchData(); fetchData();
isPlaying = true; isPlaying = true;
}
function pauseRadio() {
if (!isPlaying) {
return;
}
e_player.classList.add("paused");
progressStep = 0;
e_channels.forEach((el) => el.classList.remove("active"));
document.getElementById("pause").classList.add("active");
e_audio.pause();
isPlaying = false;
}
function toggleMute() {
if (e_audio.muted) {
document.getElementById("mute").classList.remove("active");
e_volumeBar.classList.remove("muted");
e_audio.muted = false;
} else {
document.getElementById("mute").classList.add("active");
e_volumeBar.classList.add("muted");
e_audio.muted = true;
}
} }
// Change the volume // Change the volume
@ -244,57 +256,48 @@ function switchChannel(channelName) {
let volumeUpdate; // Interval container let volumeUpdate; // Interval container
function setVolume(mouseEvent) { function setVolume(mouseEvent) {
// If anything other than LMB is pressed, return early // If anything other than LMB is pressed, return early
if (mouseEvent.buttons != 1) return; if (mouseEvent.buttons != 1) return;
// Horizontal coordinate of the click // Horizontal coordinate of the click
const clickPosition = mouseEvent.offsetX; const clickPosition = mouseEvent.offsetX;
// Calculate new bar position and volume // Calculate new bar position and volume
let volumeBarWidth = (100 / mouseEvent.target.clientWidth) * clickPosition; let volumeBarWidth = (100 / mouseEvent.target.clientWidth) * clickPosition;
let audioVolume = (1 / mouseEvent.target.clientWidth) * clickPosition; let audioVolume = (1 / mouseEvent.target.clientWidth) * clickPosition;
// Resize the volume bar to the new width // Resize the volume bar to the new width
e_volumeBar.style.setProperty( e_volumeBar.style.setProperty("width", `${volumeBarWidth}%`);
"width",
`${volumeBarWidth}%`
);
// Set the audio volume // Set the audio volume
e_audio.volume = audioVolume; e_audio.volume = audioVolume;
} }
e_player.onmousemove = setVolume; e_player.onmousemove = setVolume;
e_player.onmousedown = setVolume; e_player.onmousedown = setVolume;
// Refresh radio data when the tab is focused // Refresh radio data when the tab is focused
window.onfocus=()=> { window.onfocus = () => {
if (isPlaying) fetchData(); if (isPlaying) fetchData();
} };
// Update the progress bar every second // Update the progress bar every second
const updateProgressBar = setInterval(()=> { const updateProgressBar = setInterval(() => {
// Current percentage // Current percentage
const width = parseFloat(e_progressBar.style.width); const width = parseFloat(e_progressBar.style.width);
// Future percentage // Future percentage
const futureWidth = width + progressStep; const futureWidth = width + progressStep;
// If the future percentage doesn't exceed 100% // If the future percentage doesn't exceed 100%
// increment the progress bar. // increment the progress bar.
if (futureWidth < 100) { if (futureWidth < 100) {
e_progressBar.style.setProperty( e_progressBar.style.setProperty("width", futureWidth + "%");
"width", }
futureWidth + "%"
);
}
// Otherwise, if it does exceed 100%, that means // Otherwise, if it does exceed 100%, that means
// the song has changed, so fetch the new data. // the song has changed, so fetch the new data.
else if (futureWidth >= 100) fetchData();
else if (futureWidth >= 100)
fetchData();
}, 1000); }, 1000);