working playback

This commit is contained in:
2026-02-05 21:18:44 -05:00
parent e0fa76fb32
commit 6b00871c32
10 changed files with 331 additions and 37 deletions

View File

@@ -53,7 +53,7 @@ export function useAppKeyboard(options: ShortcutOptions) {
return
}
if (key.name === "enter") {
if (key.name === "return") {
options.onAction?.("enter")
return
}

View File

@@ -22,6 +22,7 @@ import {
} from "../utils/audio-player"
import { emit, on } from "../utils/event-bus"
import { useAppStore } from "../stores/app"
import { useProgressStore } from "../stores/progress"
import type { Episode } from "../types/episode"
export interface AudioControls {
@@ -53,6 +54,7 @@ export interface AudioControls {
let backend: AudioBackend | null = null
let pollTimer: ReturnType<typeof setInterval> | null = null
let refCount = 0
let pollCount = 0 // Counts poll ticks for throttling progress saves
const [isPlaying, setIsPlaying] = createSignal(false)
const [position, setPosition] = createSignal(0)
@@ -76,6 +78,7 @@ function ensureBackend(): AudioBackend {
function startPolling(): void {
stopPolling()
pollCount = 0
pollTimer = setInterval(async () => {
if (!backend || !isPlaying()) return
try {
@@ -84,10 +87,26 @@ function startPolling(): void {
setPosition(pos)
if (dur > 0) setDuration(dur)
// Save progress every ~5 seconds (10 ticks * 500ms)
pollCount++
if (pollCount % 10 === 0) {
const ep = currentEpisode()
if (ep) {
const progressStore = useProgressStore()
progressStore.update(ep.id, pos, dur > 0 ? dur : duration(), speed())
}
}
// Check if backend stopped playing (track ended)
if (!backend.isPlaying() && isPlaying()) {
setIsPlaying(false)
stopPolling()
// Save final position on track end
const ep = currentEpisode()
if (ep) {
const progressStore = useProgressStore()
progressStore.update(ep.id, pos, dur > 0 ? dur : duration(), speed())
}
}
} catch {
// Backend may have been disposed
@@ -113,18 +132,27 @@ async function play(episode: Episode): Promise<void> {
try {
const appStore = useAppStore()
const progressStore = useProgressStore()
const storeSpeed = appStore.state().settings.playbackSpeed
const vol = volume()
const spd = storeSpeed || speed()
// Resume from saved progress if available and not completed
const savedProgress = progressStore.get(episode.id)
let startPos = 0
if (savedProgress && !progressStore.isCompleted(episode.id)) {
startPos = savedProgress.position
}
await b.play(episode.audioUrl, {
volume: vol,
speed: spd,
startPosition: startPos > 0 ? startPos : undefined,
})
setCurrentEpisode(episode)
setIsPlaying(true)
setPosition(0)
setPosition(startPos)
setSpeed(spd)
if (episode.duration) setDuration(episode.duration)
@@ -143,7 +171,12 @@ async function pause(): Promise<void> {
setIsPlaying(false)
stopPolling()
const ep = currentEpisode()
if (ep) emit("player.pause", { episodeId: ep.id })
if (ep) {
// Save progress on pause
const progressStore = useProgressStore()
progressStore.update(ep.id, position(), duration(), speed())
emit("player.pause", { episodeId: ep.id })
}
} catch (err) {
setError(err instanceof Error ? err.message : "Pause failed")
}
@@ -173,6 +206,12 @@ async function togglePlayback(): Promise<void> {
async function stop(): Promise<void> {
if (!backend) return
try {
// Save progress before stopping
const ep = currentEpisode()
if (ep) {
const progressStore = useProgressStore()
progressStore.update(ep.id, position(), duration(), speed())
}
await backend.stop()
setIsPlaying(false)
setPosition(0)