Web
This commit is contained in:
@@ -199,8 +199,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div id="qr-modal" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.75);backdrop-filter:blur(4px);z-index:300;align-items:center;justify-content:center;">
|
||||||
|
<div style="background:var(--surface);border:1px solid var(--border);border-radius:14px;padding:2rem;width:100%;max-width:340px;text-align:center;animation:fadeUp .25s ease;">
|
||||||
|
<h3 id="qr-title" style="font-size:1rem;margin-bottom:1rem;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;"></h3>
|
||||||
|
<canvas id="qr-canvas" style="margin:0 auto 1rem;display:block;border-radius:8px;"></canvas>
|
||||||
|
<div id="qr-url" style="font-size:.72rem;color:var(--muted);word-break:break-all;margin-bottom:1.25rem;"></div>
|
||||||
|
<div style="display:flex;gap:.6rem;justify-content:center;">
|
||||||
|
<button class="btn sm" onclick="copyLiveUrl()">Kopiér link</button>
|
||||||
|
<button class="btn sm" onclick="document.getElementById('qr-modal').style.display='none'">Luk</button>
|
||||||
|
</div>
|
||||||
|
<div id="copy-msg" style="font-size:.75rem;color:var(--green);margin-top:.6rem;height:1rem;"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div id="login-modal">
|
<div id="login-modal">
|
||||||
<div class="login-box">
|
|
||||||
<h3>Log ind</h3>
|
<h3>Log ind</h3>
|
||||||
<div id="login-msg"></div>
|
<div id="login-msg"></div>
|
||||||
<div class="form-row"><label>Brugernavn eller e-mail</label><input type="text" id="inp-user" placeholder="dit@email.dk"></div>
|
<div class="form-row"><label>Brugernavn eller e-mail</label><input type="text" id="inp-user" placeholder="dit@email.dk"></div>
|
||||||
@@ -212,6 +224,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrious/4.0.2/qrious.min.js"></script>
|
||||||
<script>
|
<script>
|
||||||
const API = '/api';
|
const API = '/api';
|
||||||
let token = localStorage.getItem('ld_token') || '';
|
let token = localStorage.getItem('ld_token') || '';
|
||||||
@@ -326,6 +339,8 @@ async function loadMyPlaylists() {
|
|||||||
onclick="toggleVis('${p.id}','${vis}')">
|
onclick="toggleVis('${p.id}','${vis}')">
|
||||||
${vis === 'public' ? 'Gør privat' : 'Gør public'}
|
${vis === 'public' ? 'Gør privat' : 'Gør public'}
|
||||||
</button>
|
</button>
|
||||||
|
<a class="btn sm" href="/live.html?id=${p.id}" target="_blank" title="Åbn storskærm">📺</a>
|
||||||
|
<button class="btn sm" onclick="showQR('${p.id}','${esc(p.name)}')" title="QR-kode">QR</button>
|
||||||
</div>
|
</div>
|
||||||
</div>`;
|
</div>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
@@ -409,6 +424,34 @@ document.getElementById('btn-copy').onclick = async () => {
|
|||||||
} catch(e) { btn.textContent = '⚠ ' + e.message; btn.disabled = false; }
|
} catch(e) { btn.textContent = '⚠ ' + e.message; btn.disabled = false; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let currentQRUrl = '';
|
||||||
|
|
||||||
|
function showQR(id, name) {
|
||||||
|
const url = `${location.protocol}//${location.host}/live.html?id=${id}`;
|
||||||
|
currentQRUrl = url;
|
||||||
|
document.getElementById('qr-title').textContent = name;
|
||||||
|
document.getElementById('qr-url').textContent = url;
|
||||||
|
document.getElementById('copy-msg').textContent = '';
|
||||||
|
document.getElementById('qr-modal').style.display = 'flex';
|
||||||
|
|
||||||
|
// Tegn QR med et simpelt bibliotek
|
||||||
|
const canvas = document.getElementById('qr-canvas');
|
||||||
|
if (window.QRious) {
|
||||||
|
new QRious({ element: canvas, value: url, size: 220, backgroundAlpha: 0, foreground: '#eceef4' });
|
||||||
|
} else {
|
||||||
|
// Fallback: vis bare URL hvis bibliotek ikke er loadet
|
||||||
|
canvas.style.display = 'none';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyLiveUrl() {
|
||||||
|
navigator.clipboard.writeText(currentQRUrl).then(() => {
|
||||||
|
const msg = document.getElementById('copy-msg');
|
||||||
|
msg.textContent = '✓ Kopieret!';
|
||||||
|
setTimeout(() => msg.textContent = '', 2000);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
function esc(s) { return (s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
function esc(s) { return (s||'').replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); }
|
||||||
|
|
||||||
updateAuthUI();
|
updateAuthUI();
|
||||||
|
|||||||
@@ -1073,6 +1073,8 @@ class MainWindow(QMainWindow):
|
|||||||
self._btn_play.setText("⏸")
|
self._btn_play.setText("⏸")
|
||||||
|
|
||||||
def _stop(self):
|
def _stop(self):
|
||||||
|
# Annuller evt. igangværende demo_then_play
|
||||||
|
self._demo_then_play_pending = False
|
||||||
# Annuller evt. igangværende countdown
|
# Annuller evt. igangværende countdown
|
||||||
if getattr(self, "_waiting_for_auto", False):
|
if getattr(self, "_waiting_for_auto", False):
|
||||||
self._waiting_for_auto = False
|
self._waiting_for_auto = False
|
||||||
@@ -1154,6 +1156,10 @@ class MainWindow(QMainWindow):
|
|||||||
self._btn_play.setText("▶")
|
self._btn_play.setText("▶")
|
||||||
self._vu.reset()
|
self._vu.reset()
|
||||||
|
|
||||||
|
# Hvis demo_then_play venter på at starte sang — lad timeren klare det
|
||||||
|
if getattr(self, "_demo_then_play_pending", False):
|
||||||
|
return
|
||||||
|
|
||||||
# Synkroniser current_idx til playlist_panel
|
# Synkroniser current_idx til playlist_panel
|
||||||
self._playlist_panel._current_idx = self._current_idx
|
self._playlist_panel._current_idx = self._current_idx
|
||||||
|
|
||||||
@@ -1178,12 +1184,12 @@ class MainWindow(QMainWindow):
|
|||||||
self._waiting_for_auto = False
|
self._waiting_for_auto = False
|
||||||
self._set_status(f"Klar: {next_song.get('title','')} — tryk ▶ for at starte")
|
self._set_status(f"Klar: {next_song.get('title','')} — tryk ▶ for at starte")
|
||||||
|
|
||||||
elif mode in ("auto_demo", "auto_play"):
|
elif mode in ("auto_demo", "auto_play", "demo_then_play"):
|
||||||
self._waiting_for_auto = True
|
self._waiting_for_auto = True
|
||||||
self._countdown_secs = delay
|
self._countdown_secs = delay
|
||||||
self._countdown_mode = mode
|
self._countdown_mode = mode
|
||||||
self._countdown_title = next_song.get("title", "")
|
self._countdown_title = next_song.get("title", "")
|
||||||
label = "Auto-demo" if mode == "auto_demo" else "Auto-play"
|
label = "Auto-demo" if mode in ("auto_demo", "demo_then_play") else "Auto-play"
|
||||||
self._set_status(f"{label} om {delay}s — {self._countdown_title}")
|
self._set_status(f"{label} om {delay}s — {self._countdown_title}")
|
||||||
|
|
||||||
if not hasattr(self, "_countdown_timer"):
|
if not hasattr(self, "_countdown_timer"):
|
||||||
@@ -1208,20 +1214,20 @@ class MainWindow(QMainWindow):
|
|||||||
self._countdown_timer.stop()
|
self._countdown_timer.stop()
|
||||||
return
|
return
|
||||||
self._countdown_secs -= 1
|
self._countdown_secs -= 1
|
||||||
label = "Auto-demo" if self._countdown_mode == "auto_demo" else "Auto-play"
|
label = "Auto-play" if self._countdown_mode == "auto_play" else "Auto-demo"
|
||||||
if self._countdown_secs > 0:
|
if self._countdown_secs > 0:
|
||||||
self._set_status(f"{label} om {self._countdown_secs}s — {self._countdown_title}")
|
self._set_status(f"{label} om {self._countdown_secs}s — {self._countdown_title}")
|
||||||
else:
|
else:
|
||||||
self._countdown_timer.stop()
|
self._countdown_timer.stop()
|
||||||
if self._countdown_mode == "auto_demo":
|
if self._countdown_mode == "auto_play":
|
||||||
self._auto_demo_next()
|
|
||||||
else:
|
|
||||||
self._auto_play_next()
|
self._auto_play_next()
|
||||||
|
else:
|
||||||
|
self._auto_demo_next() # auto_demo og demo_then_play starter begge med demo
|
||||||
|
|
||||||
def _auto_demo_next(self):
|
def _auto_demo_next(self):
|
||||||
"""Afspil demo af den næste klargjorte sang automatisk."""
|
"""Afspil demo af den næste klargjorte sang automatisk."""
|
||||||
if not getattr(self, "_waiting_for_auto", False):
|
if not getattr(self, "_waiting_for_auto", False):
|
||||||
return # Brugeren har allerede trykket ▶ manuelt
|
return
|
||||||
self._waiting_for_auto = False
|
self._waiting_for_auto = False
|
||||||
self._playlist_panel.set_current(self._current_idx)
|
self._playlist_panel.set_current(self._current_idx)
|
||||||
self._player.play_demo(self._demo_seconds, self._demo_fade_seconds)
|
self._player.play_demo(self._demo_seconds, self._demo_fade_seconds)
|
||||||
@@ -1230,6 +1236,24 @@ class MainWindow(QMainWindow):
|
|||||||
self._btn_play.setText("⏸")
|
self._btn_play.setText("⏸")
|
||||||
self._song_ended = False
|
self._song_ended = False
|
||||||
|
|
||||||
|
# Hvis demo_then_play: start sangen automatisk når demo er færdig
|
||||||
|
if getattr(self, "_countdown_mode", "") == "demo_then_play":
|
||||||
|
delay = self._settings.get("after_song_delay", 2)
|
||||||
|
total = self._demo_seconds + self._demo_fade_seconds + delay
|
||||||
|
self._demo_then_play_pending = True
|
||||||
|
QTimer.singleShot(total * 1000, self._auto_play_after_demo)
|
||||||
|
|
||||||
|
def _auto_play_after_demo(self):
|
||||||
|
"""Start sangen efter demo er færdig (bruges af demo_then_play)."""
|
||||||
|
self._demo_then_play_pending = False
|
||||||
|
self._song_ended = False
|
||||||
|
self._player.stop()
|
||||||
|
self._demo_active = False
|
||||||
|
self._btn_demo.setChecked(False)
|
||||||
|
self._playlist_panel.set_current(self._current_idx)
|
||||||
|
self._player.play()
|
||||||
|
self._btn_play.setText("⏸")
|
||||||
|
|
||||||
def _auto_play_next(self):
|
def _auto_play_next(self):
|
||||||
"""Start næste sang automatisk."""
|
"""Start næste sang automatisk."""
|
||||||
if not getattr(self, "_waiting_for_auto", False):
|
if not getattr(self, "_waiting_for_auto", False):
|
||||||
|
|||||||
@@ -216,18 +216,21 @@ class SettingsDialog(QDialog):
|
|||||||
grp3_layout = QVBoxLayout(grp3)
|
grp3_layout = QVBoxLayout(grp3)
|
||||||
grp3_layout.setSpacing(8)
|
grp3_layout.setSpacing(8)
|
||||||
|
|
||||||
self._radio_manual = QRadioButton("Manuel — marker næste klar, vent på ▶")
|
self._radio_manual = QRadioButton("Manuel — marker næste klar, vent på ▶")
|
||||||
self._radio_auto_demo = QRadioButton("Auto-demo — afspil demo af næste sang automatisk")
|
self._radio_auto_demo = QRadioButton("Auto-demo — afspil demo af næste sang automatisk")
|
||||||
self._radio_auto_play = QRadioButton("Auto-play — start næste sang automatisk")
|
self._radio_auto_play = QRadioButton("Auto-play — start næste sang automatisk")
|
||||||
|
self._radio_demo_then_play = QRadioButton("Auto-demo → auto-play — demo, pause, så spiller sangen automatisk")
|
||||||
|
|
||||||
self._after_song_group = QButtonGroup(self)
|
self._after_song_group = QButtonGroup(self)
|
||||||
self._after_song_group.addButton(self._radio_manual, 0)
|
self._after_song_group.addButton(self._radio_manual, 0)
|
||||||
self._after_song_group.addButton(self._radio_auto_demo, 1)
|
self._after_song_group.addButton(self._radio_auto_demo, 1)
|
||||||
self._after_song_group.addButton(self._radio_auto_play, 2)
|
self._after_song_group.addButton(self._radio_auto_play, 2)
|
||||||
|
self._after_song_group.addButton(self._radio_demo_then_play, 3)
|
||||||
|
|
||||||
grp3_layout.addWidget(self._radio_manual)
|
grp3_layout.addWidget(self._radio_manual)
|
||||||
grp3_layout.addWidget(self._radio_auto_demo)
|
grp3_layout.addWidget(self._radio_auto_demo)
|
||||||
grp3_layout.addWidget(self._radio_auto_play)
|
grp3_layout.addWidget(self._radio_auto_play)
|
||||||
|
grp3_layout.addWidget(self._radio_demo_then_play)
|
||||||
|
|
||||||
delay_row = QHBoxLayout()
|
delay_row = QHBoxLayout()
|
||||||
delay_row.addWidget(QLabel(" Pause før næste starter:"))
|
delay_row.addWidget(QLabel(" Pause før næste starter:"))
|
||||||
@@ -482,6 +485,8 @@ class SettingsDialog(QDialog):
|
|||||||
self._radio_auto_demo.setChecked(True)
|
self._radio_auto_demo.setChecked(True)
|
||||||
elif mode == "auto_play":
|
elif mode == "auto_play":
|
||||||
self._radio_auto_play.setChecked(True)
|
self._radio_auto_play.setChecked(True)
|
||||||
|
elif mode == "demo_then_play":
|
||||||
|
self._radio_demo_then_play.setChecked(True)
|
||||||
else:
|
else:
|
||||||
self._radio_manual.setChecked(True)
|
self._radio_manual.setChecked(True)
|
||||||
self._spin_after_delay.setValue(v.get("after_song_delay", 2))
|
self._spin_after_delay.setValue(v.get("after_song_delay", 2))
|
||||||
@@ -507,8 +512,9 @@ class SettingsDialog(QDialog):
|
|||||||
"audio_device_main": self._combo_main_device.currentData() or "",
|
"audio_device_main": self._combo_main_device.currentData() or "",
|
||||||
"audio_device_preview":self._combo_preview_device.currentData() or "",
|
"audio_device_preview":self._combo_preview_device.currentData() or "",
|
||||||
"after_song_mode": (
|
"after_song_mode": (
|
||||||
"auto_demo" if self._radio_auto_demo.isChecked() else
|
"auto_demo" if self._radio_auto_demo.isChecked() else
|
||||||
"auto_play" if self._radio_auto_play.isChecked() else
|
"auto_play" if self._radio_auto_play.isChecked() else
|
||||||
|
"demo_then_play" if self._radio_demo_then_play.isChecked() else
|
||||||
"manual"
|
"manual"
|
||||||
),
|
),
|
||||||
"after_song_delay": self._spin_after_delay.value(),
|
"after_song_delay": self._spin_after_delay.value(),
|
||||||
|
|||||||
Reference in New Issue
Block a user