gui functions nearly done
This commit is contained in:
parent
114d6ba4da
commit
f65918db8b
1
Makefile
1
Makefile
|
@ -1,3 +1,4 @@
|
|||
mpd:
|
||||
mkdir -p /tmp/sanic/{music,playlists}
|
||||
touch /tmp/sanic/mpd_db
|
||||
mpd --no-daemon ./mpd.conf
|
||||
|
|
64
mpd.go
64
mpd.go
|
@ -3,7 +3,6 @@ package main
|
|||
import (
|
||||
"github.com/fhs/gompd/v2/mpd"
|
||||
"github.com/labstack/echo/v4"
|
||||
"log"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
@ -15,13 +14,13 @@ func updateDb(c echo.Context) error {
|
|||
// Connect to MPD server
|
||||
conn, err := mpd.Dial("tcp", "localhost:6600")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
jobId, err := conn.Update("")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
|
||||
return c.String(http.StatusOK, strconv.Itoa(jobId))
|
||||
|
@ -31,13 +30,13 @@ func previousTrack(c echo.Context) error {
|
|||
// Connect to MPD server
|
||||
conn, err := mpd.Dial("tcp", "localhost:6600")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
err = conn.Previous()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
|
||||
return c.String(http.StatusOK, "")
|
||||
|
@ -47,13 +46,13 @@ func nextTrack(c echo.Context) error {
|
|||
// Connect to MPD server
|
||||
conn, err := mpd.Dial("tcp", "localhost:6600")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
err = conn.Next()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
|
||||
return c.String(http.StatusOK, "")
|
||||
|
@ -63,13 +62,13 @@ func stopPlayback(c echo.Context) error {
|
|||
// Connect to MPD server
|
||||
conn, err := mpd.Dial("tcp", "localhost:6600")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
err = conn.Stop()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
|
||||
return c.String(http.StatusOK, "")
|
||||
|
@ -79,13 +78,24 @@ func resumePlayback(c echo.Context) error {
|
|||
// Connect to MPD server
|
||||
conn, err := mpd.Dial("tcp", "localhost:6600")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
err = conn.Pause(false)
|
||||
status, err := conn.Status()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
if status["state"] == "stop" {
|
||||
err := conn.Play(-1)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
} else {
|
||||
err = conn.Pause(false)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
return c.String(http.StatusOK, "")
|
||||
|
@ -95,13 +105,13 @@ func pausePlayback(c echo.Context) error {
|
|||
// Connect to MPD server
|
||||
conn, err := mpd.Dial("tcp", "localhost:6600")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
err = conn.Pause(true)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
|
||||
return c.String(http.StatusOK, "")
|
||||
|
@ -111,13 +121,13 @@ func seek(c echo.Context) error {
|
|||
// Connect to MPD server
|
||||
conn, err := mpd.Dial("tcp", "localhost:6600")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
seconds, err := strconv.Atoi(c.Param("seconds"))
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
|
||||
if seconds < 0 {
|
||||
|
@ -126,7 +136,7 @@ func seek(c echo.Context) error {
|
|||
|
||||
err = conn.SeekCur(time.Duration(seconds)*time.Second, false)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
|
||||
return c.String(http.StatusOK, "")
|
||||
|
@ -136,13 +146,13 @@ func toggleRepeat(c echo.Context) error {
|
|||
// Connect to MPD server
|
||||
conn, err := mpd.Dial("tcp", "localhost:6600")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
status, err := conn.Status()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
if status["repeat"] == "1" {
|
||||
err = conn.Repeat(false)
|
||||
|
@ -150,7 +160,7 @@ func toggleRepeat(c echo.Context) error {
|
|||
err = conn.Repeat(true)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
|
||||
return c.String(http.StatusOK, "")
|
||||
|
@ -160,21 +170,21 @@ func toggleRandom(c echo.Context) error {
|
|||
// Connect to MPD server
|
||||
conn, err := mpd.Dial("tcp", "localhost:6600")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
status, err := conn.Status()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
if status["toggleRandom"] == "1" {
|
||||
if status["random"] == "1" {
|
||||
err = conn.Random(false)
|
||||
} else {
|
||||
err = conn.Random(true)
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
|
||||
return c.String(http.StatusOK, "")
|
||||
|
@ -184,13 +194,13 @@ func setVolume(c echo.Context) error {
|
|||
// Connect to MPD server
|
||||
conn, err := mpd.Dial("tcp", "localhost:6600")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
level, err := strconv.Atoi(c.Param("level"))
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
|
||||
if level > 100 || level < 0 {
|
||||
|
@ -199,7 +209,7 @@ func setVolume(c echo.Context) error {
|
|||
|
||||
err = conn.SetVolume(level)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
|
||||
return c.String(http.StatusOK, "")
|
||||
|
|
24
server.go
24
server.go
|
@ -107,7 +107,12 @@ func wsServe(c echo.Context) error {
|
|||
// Connect to MPD server
|
||||
mpdConn, err := mpd.Dial("tcp", "localhost:6600")
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
//log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
err = websocket.Message.Send(ws, fmt.Sprintf("{\"mpd_error\":\"%s\"}", err.Error()))
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
}
|
||||
defer mpdConn.Close()
|
||||
|
||||
|
@ -121,16 +126,23 @@ func wsServe(c echo.Context) error {
|
|||
} else {
|
||||
log.Println(msg)
|
||||
if strings.ToLower(msg) == "#status" {
|
||||
// TODO: Get current MPD status and return it
|
||||
status, err := mpdConn.Status()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
jsonData, err := json.Marshal(status)
|
||||
currentsong, err := mpdConn.CurrentSong()
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
err = websocket.Message.Send(ws, fmt.Sprintf("{\"mpd_status\":%s}", string(jsonData)))
|
||||
jsonStatus, err := json.Marshal(status)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
jsonCurrentSong, err := json.Marshal(currentsong)
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
err = websocket.Message.Send(ws, fmt.Sprintf("{\"mpd_status\":%s,\"mpd_current_song\":%s}", string(jsonStatus), string(jsonCurrentSong)))
|
||||
if err != nil {
|
||||
c.Logger().Error(err)
|
||||
}
|
||||
|
|
172
static/controls.js
vendored
172
static/controls.js
vendored
|
@ -1,7 +1,9 @@
|
|||
const API_URL = `${document.location.protocol}://${document.location.host}/api`;
|
||||
const API_URL = `${document.location.protocol}//${document.location.host}/api`;
|
||||
const VOLUME_STEP = 5;
|
||||
|
||||
// Get control elements
|
||||
|
||||
const connection_state = document.getElementById("connection-state");
|
||||
const control_update_db = document.getElementById("control-update-db");
|
||||
const control_previous = document.getElementById("control-previous");
|
||||
const control_play_pause = document.getElementById("control-play-pause");
|
||||
|
@ -13,18 +15,31 @@ const control_shuffle = document.getElementById("control-shuffle");
|
|||
const control_xfade = document.getElementById("control-xfade");
|
||||
const control_xfade_minus = document.getElementById("control-xfade-minus");
|
||||
const control_xfade_plus = document.getElementById("control-xfade-plus");
|
||||
const control_volume = document.getElementById("control-volume");
|
||||
const control_volume_up = document.getElementById("control-volume-up");
|
||||
const control_volume_down = document.getElementById("control-volume-down");
|
||||
const queue_table = document.querySelector("#queue tbody");
|
||||
const control_track = document.getElementById("control-track");
|
||||
const control_time = document.getElementById("control-time");
|
||||
|
||||
// Add API calls to controls
|
||||
|
||||
control_update_db.addEventListener("click", e => {
|
||||
fetch(`${API_URL}/update_db`);
|
||||
console.log("Issuing database update")
|
||||
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})`)
|
||||
} else {
|
||||
console.error(`API returned ${r.status}: ${r.statusText}`)
|
||||
}
|
||||
});
|
||||
});
|
||||
control_previous.addEventListener("click", e => {
|
||||
fetch(`${API_URL}/previous_track`);
|
||||
});
|
||||
control_play_pause.addEventListener("click", e => {
|
||||
if (e.target.innerText === "⏵︎") { // Play
|
||||
if (e.target.innerHTML === "⏵︎") { // TODO: check is never true
|
||||
fetch(`${API_URL}/pause`);
|
||||
} else { // Pause
|
||||
fetch(`${API_URL}/play`);
|
||||
|
@ -40,10 +55,20 @@ control_progress.addEventListener("change", e => {
|
|||
fetch(`${API_URL}/seek/${e.target.value}`)
|
||||
});
|
||||
control_repeat.addEventListener("click", e => {
|
||||
if (e.target.innerHTML === "🔴 repeat") { // TODO: check is never true
|
||||
e.target.innerHTML = "🔘 repeat";
|
||||
} else {
|
||||
e.target.innerHTML = "🔴 repeat";
|
||||
}
|
||||
fetch(`${API_URL}/repeat`);
|
||||
});
|
||||
control_shuffle.addEventListener("click", e => {
|
||||
fetch(`${API_URL}/shuffle`);
|
||||
if (e.target.innerHTML === "🔴 shuffle") { // TODO: check is never true
|
||||
e.target.innerHTML = "🔘 shuffle";
|
||||
} else {
|
||||
e.target.innerHTML = "🔴 shuffle";
|
||||
}
|
||||
fetch(`${API_URL}/random`);
|
||||
});
|
||||
control_xfade_minus.addEventListener("click", e => {
|
||||
// TODO: not yet implemented
|
||||
|
@ -53,46 +78,129 @@ control_xfade_plus.addEventListener("click", e => {
|
|||
// TODO: not yet implemented
|
||||
fetch(`${API_URL}/xfade`);
|
||||
});
|
||||
control_volume_up.addEventListener("click", e => {
|
||||
const v = Math.min(parseInt(control_volume.value) + VOLUME_STEP, 100);
|
||||
fetch(`${API_URL}/volume/${v}`);
|
||||
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}`);
|
||||
control_volume.value = v;
|
||||
});
|
||||
control_volume.addEventListener("change", e => {
|
||||
fetch(`${API_URL}/volume/${e.target.value}`);
|
||||
});
|
||||
|
||||
// Create WebSocket connection.
|
||||
const socket = new WebSocket(`${document.location.protocol === "https" ? "wss" : "ws"}://${document.location.host}/ws`);
|
||||
const socket = new WebSocket(`${document.location.protocol === "https:" ? "wss" : "ws"}://${document.location.host}/ws`);
|
||||
|
||||
// Connection opened
|
||||
socket.addEventListener("open", (e) => {
|
||||
socket.send("Hello Server!");
|
||||
});
|
||||
|
||||
// Listen for messages
|
||||
// Listen for messages and update UI state
|
||||
socket.addEventListener("message", (e) => {
|
||||
console.log("Message from server");
|
||||
// Print out mpd response
|
||||
console.log(`DEBUG: ${e.data}`); // DEBUG
|
||||
|
||||
const msg = JSON.parse(e.data);
|
||||
if ("error" in msg.mpd_status) {
|
||||
console.error(msg.mpd_status.error);
|
||||
|
||||
if ("mpd_status" in msg) {
|
||||
if (msg.mpd_status == null) {
|
||||
connection_state.innerHTML = "❌ Disconnected"; // ✅ Check Mark Button
|
||||
} else {
|
||||
// print error if present
|
||||
if ("error" in msg.mpd_status) {
|
||||
console.error(msg.mpd_status.error);
|
||||
}
|
||||
|
||||
// update "Update DB" button
|
||||
if ("updating_db" in msg.mpd_status) {
|
||||
control_update_db.disabled = true;
|
||||
} else {
|
||||
if (control_update_db.disabled) {
|
||||
console.log("Database update done.")
|
||||
}
|
||||
control_update_db.disabled = false;
|
||||
}
|
||||
|
||||
// update play/pause button
|
||||
if ("state" in msg.mpd_status && msg.mpd_status.state === "play") {
|
||||
control_play_pause.innerHTML = "⏸︎"; // Pause
|
||||
} else {
|
||||
control_play_pause.innerHTML = "⏵︎"; // Play
|
||||
}
|
||||
|
||||
// update playback time
|
||||
if ("elapsed" in msg.mpd_status && "duration" in msg.mpd_status) {
|
||||
const elapsed_hours = Math.floor(msg.mpd_status.elapsed / 3600);
|
||||
const elapsed_minutes = Math.floor((msg.mpd_status.elapsed - elapsed_hours * 3600) / 60);
|
||||
const elapsed_seconds = Math.floor(msg.mpd_status.elapsed - elapsed_hours * 3600 - elapsed_minutes * 60);
|
||||
const duration_hours = Math.floor(msg.mpd_status.duration / 3600);
|
||||
const duration_minutes = Math.floor((msg.mpd_status.duration - duration_hours * 3600) / 60);
|
||||
const duration_seconds = Math.floor(msg.mpd_status.duration - duration_hours * 3600 - duration_minutes * 60);
|
||||
control_time.value = `${elapsed_hours}:${elapsed_minutes.toString().padStart(2, '0')}:${elapsed_seconds.toString().padStart(2, '0')}/${duration_hours}:${duration_minutes.toString().padStart(2, '0')}:${duration_seconds.toString().padStart(2, '0')}`;
|
||||
}
|
||||
if ("elapsed" in msg.mpd_status) {
|
||||
control_progress.value = msg.mpd_status.elapsed;
|
||||
}
|
||||
if ("duration" in msg.mpd_status) {
|
||||
control_progress.max = msg.mpd_status.duration;
|
||||
}
|
||||
|
||||
// update repeat state
|
||||
if ("repeat" in msg.mpd_status) {
|
||||
if (msg.mpd_status.repeat === "1") {
|
||||
control_repeat.innerHTML = "🔴 repeat"; // 🔴 Red Circle
|
||||
} else {
|
||||
control_repeat.innerHTML = "🔘 repeat"; // 🔘 Radio Button
|
||||
}
|
||||
}
|
||||
|
||||
// update shuffle state
|
||||
if ("random" in msg.mpd_status) {
|
||||
if (msg.mpd_status.random === "1") {
|
||||
control_shuffle.innerHTML = "🔴 shuffle"; // 🔴 Red Circle
|
||||
} else {
|
||||
control_shuffle.innerHTML = "🔘 shuffle"; // 🔘 Radio Button
|
||||
}
|
||||
}
|
||||
|
||||
// update crossfade state
|
||||
if ("xfade" in msg.mpd_status) {
|
||||
control_xfade.value = msg.mpd_status.xfade;
|
||||
}
|
||||
|
||||
// update volume
|
||||
if ("volume" in msg.mpd_status) {
|
||||
control_volume.value = msg.mpd_status.volume;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ("updating_db" in msg.mpd_status) {
|
||||
control_update_db.disable();
|
||||
} else {
|
||||
control_update_db.enable();
|
||||
// update song info
|
||||
if ("mpd_current_song" in msg && msg.mpd_current_song != null) {
|
||||
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}`
|
||||
} else {
|
||||
control_track.value = msg.mpd_current_song.file;
|
||||
}
|
||||
}
|
||||
if ("state" in msg.mpd_status && msg.mpd_status.state === "play") {
|
||||
control_play_pause.innerText = "⏸︎"; // Pause
|
||||
} else {
|
||||
control_play_pause.innerText = "⏵︎"; // Play
|
||||
}
|
||||
if ("elapsed" in msg.mpd_status) {
|
||||
control_progress.value = msg.mpd_status.elapsed;
|
||||
}
|
||||
if ("duration" in msg.mpd_status) {
|
||||
control_progress.max = msg.mpd_status.duration;
|
||||
}
|
||||
if ("repeat" in msg.mpd_status) {
|
||||
control_repeat.checked = msg.mpd_status.repeat;
|
||||
}
|
||||
if ("random" in msg.mpd_status) {
|
||||
control_shuffle.checked = msg.mpd_status.random;
|
||||
}
|
||||
if ("xfade" in msg.mpd_status) {
|
||||
control_xfade.value = msg.mpd_status.xfade;
|
||||
|
||||
if ("mpd_error" in msg) {
|
||||
console.error(`MPD Error: ${msg.mpd_error}`)
|
||||
}
|
||||
});
|
||||
|
||||
// Request MPD status every second
|
||||
window.setInterval(() => {
|
||||
if (socket.readyState === socket.OPEN) {
|
||||
socket.send("#status");
|
||||
connection_state.innerHTML = "✅ Connected"; // ❌ Cross Mark
|
||||
} else {
|
||||
connection_state.innerHTML = "❌ Disconnected"; // ✅ Check Mark Button
|
||||
}
|
||||
}, 1000);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<html lang="en" xmlns="http://www.w3.org/1999/html">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>Sanic</title>
|
||||
|
@ -9,13 +9,14 @@
|
|||
<body>
|
||||
<main>
|
||||
<div id="nav">
|
||||
<div>
|
||||
<div id="control-admin">
|
||||
<button id="connection-state">❌ Disconnected</button> <!-- ❌ Cross Mark -->
|
||||
<button>Login</button>
|
||||
<button>Config</button>
|
||||
<button id="control-update-db" disabled="disabled">Update DB</button>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<div class="spaced">
|
||||
<button id="control-previous">⏮︎</button> <!-- ⏮️ Last Track Button -->
|
||||
<button id="control-stop">⏹︎</button> <!-- ⏹️ Stop Button -->
|
||||
<button id="control-play-pause">⏵︎</button> <!-- ▶️ Play or ⏸️ Pause Button -->
|
||||
|
@ -27,24 +28,38 @@
|
|||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div>
|
||||
<div class="spaced">
|
||||
<button id="control-repeat">🔘 repeat</button>
|
||||
<button id="control-shuffle">🔘 shuffle</button>
|
||||
</div>
|
||||
<div class="spaced">
|
||||
<label for="control-xfade">xfade</label>
|
||||
<div>
|
||||
<div>
|
||||
<label for="control-repeat">repeat</label>
|
||||
<input type="checkbox" id="control-repeat" name="repeat" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="control-shuffle">shuffle</label>
|
||||
<input type="checkbox" id="control-shuffle" name="shuffle" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label for="control-xfade">xfade</label>
|
||||
<button id="control-xfade-minus">➖</button>
|
||||
<input type="number" id="control-xfade" name="xfade" value="00" />
|
||||
<button id="control-xfade-plus">➕</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="spaced">
|
||||
<button id="control-volume-down">🔉</button> <!-- 🔉 Speaker with sound wave -->
|
||||
<input id="control-volume" name="volume" type="range" min="0" max="100" value="50" />
|
||||
<button id="control-volume-up">🔊</button> <!-- 🔊 Speaker with sound waves -->
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<p>Now playing:</p>
|
||||
<div>
|
||||
<label for="control-track">Track:</label>
|
||||
<input type="text" id="control-track" name="track" disabled="disabled" />
|
||||
</div>
|
||||
<div>
|
||||
<label for="control-time">Time:</label>
|
||||
<input type="text" id="control-time" name="time" value="00:00:00/00:00:00" disabled="disabled" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<img id="sanic-logo" alt="sanic logo" src="/img/sanic-logo.webp" />
|
||||
<div>Sanic © 2023</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="queue">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/* #################### */
|
||||
/* ####### main ####### */
|
||||
/* #### structure ##### */
|
||||
/* #################### */
|
||||
|
||||
html, body { margin: 0; height: 100%; }
|
||||
|
@ -37,6 +37,21 @@ table {
|
|||
width: 100%;
|
||||
}
|
||||
|
||||
#control-admin {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#sanic-logo {
|
||||
max-width: 80%;
|
||||
max-height: 80%;
|
||||
}
|
||||
|
||||
.spaced {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
/* #################### */
|
||||
/* ###### debug ####### */
|
||||
/* #################### */
|
||||
|
@ -62,3 +77,58 @@ div {
|
|||
#control-xfade[type=number] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
#control-xfade {
|
||||
width: 2em;
|
||||
}
|
||||
|
||||
#control-previous,
|
||||
#control-play-pause,
|
||||
#control-stop,
|
||||
#control-next {
|
||||
width: 2.5em;
|
||||
height: 2.5em;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/*
|
||||
#control-track {
|
||||
transform: translateX(100%);
|
||||
-moz-transform: translateX(100%);
|
||||
-webkit-transform: translateX(100%);
|
||||
animation: scroll-left 20s linear infinite;
|
||||
-moz-animation: scroll-left 2s linear infinite;
|
||||
-webkit-animation: scroll-left 2s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes scroll-left {
|
||||
0% {
|
||||
transform: translateX(100%);
|
||||
-moz-transform: translateX(100%);
|
||||
-webkit-transform: translateX(100%);
|
||||
}
|
||||
100% {
|
||||
transform: translateX(-100%);
|
||||
-moz-transform: translateX(-100%);
|
||||
-webkit-transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
@-moz-keyframes scroll-left {
|
||||
0% {
|
||||
-moz-transform: translateX(100%);
|
||||
}
|
||||
100% {
|
||||
-moz-transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes scroll-left {
|
||||
0% {
|
||||
-webkit-transform: translateX(100%);
|
||||
}
|
||||
100% {
|
||||
-webkit-transform: translateX(-100%);
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
|
Loading…
Reference in a new issue