first step

This commit is contained in:
2026-01-11 10:30:54 +01:00
parent f9b9ce0994
commit 34c3f0dc89
7 changed files with 1105 additions and 30 deletions

View File

@@ -7,7 +7,10 @@ let currentIndex = 0;
let isPlaying = false;
let currentMode = 'local'; // 'local' | 'cast'
let currentCastDevice = null;
const audio = new Audio();
// Local playback is handled natively by the Tauri backend (player_* commands).
// The WebView is a control surface only.
let localPlayerPollId = null;
// UI Elements
const stationNameEl = document.getElementById('station-name');
@@ -95,7 +98,9 @@ function restoreSavedVolume() {
volumeSlider.value = String(saved);
volumeValue.textContent = `${saved}%`;
const decimals = saved / 100;
audio.volume = decimals;
// Keep backend player volume in sync (best-effort).
invoke('player_set_volume', { volume: decimals }).catch(() => {});
// If currently in cast mode and a device is selected, propagate volume
if (currentMode === 'cast' && currentCastDevice) {
invoke('cast_set_volume', { deviceName: currentCastDevice, volume: decimals }).catch(()=>{});
@@ -103,6 +108,44 @@ function restoreSavedVolume() {
}
}
function stopLocalPlayerStatePolling() {
if (localPlayerPollId) {
try { clearInterval(localPlayerPollId); } catch (e) {}
localPlayerPollId = null;
}
}
function startLocalPlayerStatePolling() {
stopLocalPlayerStatePolling();
// Polling keeps the existing UI in sync with native buffering/reconnect.
localPlayerPollId = setInterval(async () => {
try {
if (!isPlaying || currentMode !== 'local') return;
const st = await invoke('player_get_state');
if (!st || !statusTextEl || !statusDotEl) return;
const status = String(st.status || '').toLowerCase();
if (status === 'buffering') {
statusTextEl.textContent = 'Buffering...';
statusDotEl.style.backgroundColor = 'var(--text-muted)';
} else if (status === 'playing') {
statusTextEl.textContent = 'Playing';
statusDotEl.style.backgroundColor = 'var(--success)';
} else if (status === 'error') {
statusTextEl.textContent = st.error ? `Error: ${st.error}` : 'Error';
statusDotEl.style.backgroundColor = 'var(--danger)';
} else {
// idle/stopped: keep UI consistent with our isPlaying flag
}
} catch (e) {
// Don't spam; just surface a minimal indicator.
try {
if (statusTextEl) statusTextEl.textContent = 'Error';
} catch (_) {}
}
}, 600);
}
async function loadStations() {
try {
// stop any existing pollers before reloading stations
@@ -896,12 +939,13 @@ async function play() {
statusDotEl.style.backgroundColor = 'var(--text-muted)'; // Grey/Yellow while loading
if (currentMode === 'local') {
audio.src = station.url;
audio.volume = volumeSlider.value / 100;
try {
await audio.play();
const vol = volumeSlider.value / 100;
await invoke('player_set_volume', { volume: vol }).catch(() => {});
await invoke('player_play', { url: station.url });
isPlaying = true;
updateUI();
startLocalPlayerStatePolling();
} catch (e) {
console.error('Playback failed', e);
statusTextEl.textContent = 'Error';
@@ -926,8 +970,12 @@ async function play() {
async function stop() {
if (currentMode === 'local') {
audio.pause();
audio.src = '';
stopLocalPlayerStatePolling();
try {
await invoke('player_stop');
} catch (e) {
console.error(e);
}
} else if (currentMode === 'cast' && currentCastDevice) {
try {
await invoke('cast_stop', { deviceName: currentCastDevice });
@@ -981,7 +1029,7 @@ function handleVolumeInput() {
const decimals = val / 100;
if (currentMode === 'local') {
audio.volume = decimals;
invoke('player_set_volume', { volume: decimals }).catch(() => {});
} else if (currentMode === 'cast' && currentCastDevice) {
invoke('cast_set_volume', { deviceName: currentCastDevice, volume: decimals });
}