add save playlist modal and other things
This commit is contained in:
parent
d97a33b800
commit
fa7e9bd497
143
static/controls.js
vendored
143
static/controls.js
vendored
|
@ -3,6 +3,11 @@ const VOLUME_STEP = 5;
|
|||
|
||||
// Get control elements
|
||||
|
||||
const dialog_save_playlist = document.getElementById("save-playlist");
|
||||
const control_playlist_name = document.getElementById("control-playlist-name");
|
||||
const dialog_save_playlist_submit = document.querySelector("#save-playlist button");
|
||||
const dialog_save_playlist_close = document.querySelector("#save-playlist .close");
|
||||
|
||||
const connection_state = document.getElementById("connection-state");
|
||||
const control_update_db = document.getElementById("control-update-db");
|
||||
const control_previous = document.getElementById("control-previous");
|
||||
|
@ -25,9 +30,57 @@ const tabs = document.getElementById("tabs");
|
|||
const tab_browser = document.getElementById("tab-browser");
|
||||
const tab_search = document.getElementById("tab-search");
|
||||
const tab_playlists = document.getElementById("tab-playlists");
|
||||
const control_playlist_list = document.getElementById("control-playlist-list");
|
||||
const control_replace_playlist = document.getElementById("control-replace-playlist");
|
||||
const control_attach_playlist = document.getElementById("control-attach-playlist");
|
||||
const control_save_playlist = document.getElementById("control-save-playlist");
|
||||
const control_delete_playlist = document.getElementById("control-delete-playlist");
|
||||
|
||||
// UI controls
|
||||
|
||||
control_replace_playlist.addEventListener("click", e => {
|
||||
fetch(`${API_URL}/`).then(async r => {
|
||||
if (r.status !== 200) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
control_attach_playlist.addEventListener("click", e => {
|
||||
fetch(`${API_URL}/`).then(async r => {
|
||||
if (r.status !== 200) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
control_save_playlist.addEventListener("click", e => {
|
||||
dialog_save_playlist.showModal()
|
||||
});
|
||||
|
||||
dialog_save_playlist_close.addEventListener("click", e => {
|
||||
dialog_save_playlist.close()
|
||||
});
|
||||
|
||||
dialog_save_playlist_submit.addEventListener("click", e => {
|
||||
fetch(`${API_URL}/playlists`, {method: "PUT"}).then(async r => {
|
||||
if (r.status === 201) {
|
||||
console.log(`Playlist "${control_playlist_name.value}" saved`)
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
control_delete_playlist.addEventListener("click", e => {
|
||||
const playlist_id = control_playlist_list.value;
|
||||
fetch(`${API_URL}/playlists/${playlist_id}`, {method: "DELETE"}).then(r => {
|
||||
if (r.status === 204) {
|
||||
console.log(`Playlist ${playlist_id} successfully deleted.`);
|
||||
} else {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
tab_browser.addEventListener("click", e => {
|
||||
if (!tab_browser.classList.contains("active")) {
|
||||
tab_browser.classList.add("active");
|
||||
|
@ -68,31 +121,55 @@ control_update_db.addEventListener("click", e => {
|
|||
fetch(`${API_URL}/update_db`).then(async r => {
|
||||
if (r.status === 200) {
|
||||
const job_id = await r.text();
|
||||
console.log(`Update started (Job ID: ${job_id})`)
|
||||
console.log(`Update started (Job ID: ${job_id})`);
|
||||
e.target.disabled = true;
|
||||
} else {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`)
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
control_previous.addEventListener("click", e => {
|
||||
fetch(`${API_URL}/previous_track`);
|
||||
fetch(`${API_URL}/previous_track`).then(async r => {
|
||||
if (200 >= r.status < 300) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
control_play_pause.addEventListener("click", e => {
|
||||
if (e.target.innerHTML === "⏵︎") { // TODO: check is never true
|
||||
fetch(`${API_URL}/pause`);
|
||||
fetch(`${API_URL}/pause`).then(async r => {
|
||||
if (200 >= r.status < 300) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
} else { // Pause
|
||||
fetch(`${API_URL}/play`);
|
||||
fetch(`${API_URL}/play`).then(async r => {
|
||||
if (200 >= r.status < 300) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
control_stop.addEventListener("click", e => {
|
||||
fetch(`${API_URL}/stop`);
|
||||
fetch(`${API_URL}/stop`).then(async r => {
|
||||
if (200 >= r.status < 300) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
control_next.addEventListener("click", e => {
|
||||
fetch(`${API_URL}/next_track`);
|
||||
fetch(`${API_URL}/next_track`).then(async r => {
|
||||
if (200 >= r.status < 300) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
control_progress.addEventListener("change", e => {
|
||||
fetch(`${API_URL}/seek/${e.target.value}`)
|
||||
fetch(`${API_URL}/seek/${e.target.value}`).then(async r => {
|
||||
if (200 >= r.status < 300) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
control_repeat.addEventListener("click", e => {
|
||||
if (e.target.dataset.state === "on") { // TODO: check is never true
|
||||
|
@ -102,7 +179,11 @@ control_repeat.addEventListener("click", e => {
|
|||
e.target.innerHTML = "🔴 repeat";
|
||||
e.target.dataset.state = "on";
|
||||
}
|
||||
fetch(`${API_URL}/repeat`);
|
||||
fetch(`${API_URL}/repeat`).then(async r => {
|
||||
if (200 >= r.status < 300) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
control_shuffle.addEventListener("click", e => {
|
||||
if (e.target.dataset.state === "on") { // TODO: check is never true
|
||||
|
@ -112,29 +193,53 @@ control_shuffle.addEventListener("click", e => {
|
|||
e.target.innerHTML = "🔴 shuffle";
|
||||
e.target.dataset.state = "on";
|
||||
}
|
||||
fetch(`${API_URL}/random`);
|
||||
fetch(`${API_URL}/random`).then(async r => {
|
||||
if (200 >= r.status < 300) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
control_xfade_minus.addEventListener("click", e => {
|
||||
// TODO: not yet implemented
|
||||
fetch(`${API_URL}/xfade`);
|
||||
fetch(`${API_URL}/xfade`).then(async r => {
|
||||
if (200 >= r.status < 300) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
control_xfade_plus.addEventListener("click", e => {
|
||||
// TODO: not yet implemented
|
||||
fetch(`${API_URL}/xfade`);
|
||||
fetch(`${API_URL}/xfade`).then(async r => {
|
||||
if (200 >= r.status < 300) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
control_volume_up.addEventListener("click", e => {
|
||||
const v = Math.min(parseInt(control_volume.value) + VOLUME_STEP, 100);
|
||||
fetch(`${API_URL}/volume/${v}`);
|
||||
fetch(`${API_URL}/volume/${v}`).then(async r => {
|
||||
if (200 >= r.status < 300) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
control_volume.value = v;
|
||||
|
||||
});
|
||||
control_volume_down.addEventListener("click", e => {
|
||||
const v = Math.max(parseInt(control_volume.value) - VOLUME_STEP, 0);
|
||||
fetch(`${API_URL}/volume/${v}`);
|
||||
fetch(`${API_URL}/volume/${v}`).then(async r => {
|
||||
if (200 >= r.status < 300) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
control_volume.value = v;
|
||||
});
|
||||
control_volume.addEventListener("change", e => {
|
||||
fetch(`${API_URL}/volume/${e.target.value}`);
|
||||
fetch(`${API_URL}/volume/${e.target.value}`).then(async r => {
|
||||
if (200 >= r.status < 300) {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Create WebSocket connection.
|
||||
|
@ -231,10 +336,14 @@ socket.addEventListener("message", (e) => {
|
|||
|
||||
// update song info
|
||||
if ("mpd_current_song" in msg && msg.mpd_current_song != null) {
|
||||
let track;
|
||||
if ("Artist" in msg.mpd_current_song && "Title" in msg.mpd_current_song) {
|
||||
control_track.value = `${msg.mpd_current_song.Artist} - ${msg.mpd_current_song.Title}`
|
||||
track = `<span>${msg.mpd_current_song.Artist} - ${msg.mpd_current_song.Title}</span>`
|
||||
} else {
|
||||
control_track.value = msg.mpd_current_song.file;
|
||||
track = `<span>${msg.mpd_current_song.file}</span>`;
|
||||
}
|
||||
if (control_track.innerHTML.toString() !== track) {
|
||||
control_track.innerHTML = track;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,17 @@
|
|||
<link rel="icon" href="/favicon.ico" sizes="16x16 32x32 48x48 64x64" type="image/png" />
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<dialog id="save-playlist">
|
||||
<h1>Save Playlist</h1>
|
||||
<button class="close">×</button>
|
||||
<form method="dialog">
|
||||
<label for="control-playlist-name">Name</label>
|
||||
<input type="text" id="control-playlist-name" name="playlist-name" autofocus />
|
||||
<button>Save</button>
|
||||
</form>
|
||||
</dialog>
|
||||
|
||||
<main>
|
||||
<div id="nav">
|
||||
<div id="control-admin">
|
||||
|
@ -48,7 +59,10 @@
|
|||
<div class="wide">
|
||||
<div>
|
||||
<label for="control-track">Now playing:</label>
|
||||
<input type="text" id="control-track" name="track" disabled="disabled" />
|
||||
<!--<input type="text" id="control-track" name="track" disabled="disabled" />-->
|
||||
<div class="marquee" id="control-track">
|
||||
<span>Fall On Your Sword - Shatner Of The Mount by Fall On Your Sword</span>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="control-time">Time:</label>
|
||||
|
@ -107,9 +121,10 @@
|
|||
<option value="1">basedrive</option><!-- TODO: Remove this line -->
|
||||
</select><!--/#control-playlist-list-->
|
||||
<div>
|
||||
<button>➕ New</button><!-- ➕ Plus -->
|
||||
<button>⬆ Attach</button><!-- ⬆️ Up Arrow -->
|
||||
<button>🗑️ Delete</button><!-- 🗑️ Wastebasket -->
|
||||
<button id="control-replace-playlist">🔄 Replace</button><!-- 🔄 Counterclockwise Arrows Button -->
|
||||
<button id="control-attach-playlist">⬆ Attach</button><!-- ⬆️ Up Arrow -->
|
||||
<button id="control-save-playlist">💾 Save</button><!-- 💾 Floppy Disk -->
|
||||
<button id="control-delete-playlist">🗑️ Delete</button><!-- 🗑️ Wastebasket -->
|
||||
</div>
|
||||
</div><!--/#playlist-browser-->
|
||||
</div><!--/#browser-->
|
||||
|
|
|
@ -4,6 +4,10 @@
|
|||
|
||||
--background-color: #041936;
|
||||
--text-color: #bbb;
|
||||
|
||||
--input-background-color: #28374a;
|
||||
--input-border-light: #545454;
|
||||
--input-border-dark: #3a3a3a;
|
||||
}
|
||||
|
||||
/* #################### */
|
||||
|
@ -193,12 +197,12 @@ a {
|
|||
}
|
||||
|
||||
button {
|
||||
background-color: #28374a;
|
||||
background-color: var(--input-background-color);
|
||||
color: var(--text-color);
|
||||
border-top-color: #545454;
|
||||
border-right-color: #3a3a3a;
|
||||
border-bottom-color: #3a3a3a;
|
||||
border-left-color: #545454;
|
||||
border-top-color: var(--input-border-light);
|
||||
border-right-color: var(--input-border-dark);
|
||||
border-bottom-color: var(--input-border-dark);
|
||||
border-left-color: var(--input-border-light);
|
||||
}
|
||||
|
||||
button[disabled] {
|
||||
|
@ -241,11 +245,47 @@ button .loader {
|
|||
}
|
||||
|
||||
input[type=text] {
|
||||
background-color: #28374a;
|
||||
background-color: var(--input-background-color);
|
||||
color: var(--text-color);
|
||||
border: 1px solid black;
|
||||
border-right-color: #545454;
|
||||
border-bottom-color: #545454;
|
||||
border-right-color: var(--input-border-light);
|
||||
border-bottom-color: var(--input-border-light);
|
||||
}
|
||||
|
||||
.marquee {
|
||||
display: flex;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
width: var(--ribbon-width);
|
||||
font-size: 10pt;
|
||||
background-color: var(--input-background-color);
|
||||
color: var(--text-color);
|
||||
border: 1px solid black;
|
||||
border-right-color: var(--input-border-light);
|
||||
border-bottom-color: var(--input-border-light);
|
||||
container-type: inline-size;
|
||||
}
|
||||
|
||||
.wide {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
}
|
||||
|
||||
.marquee > * {
|
||||
-webkit-animation: marquee 10s linear infinite both alternate;
|
||||
animation: marquee 10s linear infinite both alternate;
|
||||
}
|
||||
@-webkit-keyframes marquee {
|
||||
to {
|
||||
transform: translateX(min(100cqw - 100%, 0px));
|
||||
}
|
||||
}
|
||||
@keyframes marquee {
|
||||
to {
|
||||
transform: translateX(min(100cqw - 100%, 0px));
|
||||
}
|
||||
}
|
||||
|
||||
#nav {
|
||||
|
@ -258,8 +298,7 @@ input[type=text] {
|
|||
}
|
||||
|
||||
thead {
|
||||
background: rgb(15,29,47);
|
||||
background: linear-gradient(0deg, rgba(15,29,47,1) 0%, rgba(15,29,47,1) 50%, rgba(7,14,23,1) 100%);
|
||||
background: #0F1D2F linear-gradient(0deg, rgba(15, 29, 47, 1) 0%, rgba(15, 29, 47, 1) 50%, rgba(7, 14, 23, 1) 100%);
|
||||
}
|
||||
|
||||
th {
|
||||
|
@ -295,7 +334,7 @@ tbody td.actions {
|
|||
/* make arrow for currently playing song look nice */
|
||||
|
||||
#queue table tr.playing td:first-of-type::before {
|
||||
content: '\2BC8'; // ⯈
|
||||
content: '\2BC8'; /* ⯈ Black Medium Right-Pointing Triangle Centred */
|
||||
}
|
||||
|
||||
#queue table tr td:first-of-type {
|
||||
|
@ -310,11 +349,11 @@ tbody td.actions {
|
|||
}
|
||||
|
||||
table tr:nth-child(odd) td {
|
||||
background: #1e1f1a;
|
||||
background-color: #1e1f1a;
|
||||
}
|
||||
|
||||
table tr:nth-child(even) td {
|
||||
background: #171812;
|
||||
background-color: #171812;
|
||||
}
|
||||
|
||||
#queue table tr:nth-child(odd).playing td,
|
||||
|
@ -323,7 +362,7 @@ table tr:nth-child(even) td {
|
|||
}
|
||||
|
||||
table tr:hover td {
|
||||
background-color: #354158 !important; /* TODO: remove !important */
|
||||
background-color: #354158;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
|
@ -335,9 +374,9 @@ table tr:hover td {
|
|||
padding: 3pt;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
background-color: #28374a;
|
||||
background-color: var(--input-background-color);
|
||||
color: var(--text-color);
|
||||
border: 1px solid #545454;
|
||||
border: 1px solid var(--input-border-light);
|
||||
border-top-left-radius: 5pt;
|
||||
border-top-right-radius: 5pt;
|
||||
}
|
||||
|
@ -346,10 +385,6 @@ table tr:hover td {
|
|||
background-color: #1a1a1a;
|
||||
color: var(--text-color);
|
||||
border-bottom: none;
|
||||
/*border-top-color: #1a1a1a;*/
|
||||
/*border-right-color: #545454;*/
|
||||
/*border-bottom-color: #545454;*/
|
||||
/*border-left-color: #1a1a1a;*/
|
||||
}
|
||||
|
||||
#browser {
|
||||
|
@ -360,11 +395,11 @@ table tr:hover td {
|
|||
#control-playlist-list {
|
||||
font-size: 12pt;
|
||||
width: 100%;
|
||||
background-color: #28374a;
|
||||
background-color: var(--input-background-color);
|
||||
color: var(--text-color);
|
||||
border: 1px solid black;
|
||||
border-right-color: #545454;
|
||||
border-bottom-color: #545454;
|
||||
border-right-color: var(--input-border-light);
|
||||
border-bottom-color: var(--input-border-light);
|
||||
scrollbar-color: #490b00 #09101d; /* only in firefox: https://developer.mozilla.org/en-US/docs/Web/CSS/scrollbar-color */
|
||||
}
|
||||
|
||||
|
@ -373,3 +408,21 @@ footer svg {
|
|||
width: 12pt;
|
||||
height: 12pt;
|
||||
}
|
||||
|
||||
/*dialog {*/
|
||||
/* position: fixed;*/
|
||||
/* left: 50%;*/
|
||||
/* top: 50%;*/
|
||||
/* transform: translate(-50%, -50%);*/
|
||||
/*}*/
|
||||
|
||||
dialog {
|
||||
background-color: var(--background-color);
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
||||
dialog .close {
|
||||
position: absolute;
|
||||
top: 1pt;
|
||||
right: 1pt;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue