The custom /radio/stream-player/ page is served over HTTPS, so the browser
still applies mixed-content restrictions and upgrades http:// audio to https://,
which fails for streams without TLS support.
Fix: window.open(url, '_blank') navigates the tab directly to the HTTP URL.
The tab itself is then HTTP, bypassing mixed-content restrictions entirely.
Browser plays the stream natively with its built-in audio player.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
window.open() doesn't transfer the user gesture to the new tab, so autoplay
is blocked. Previously play().catch(()=>{}) swallowed the error silently while
setting playing=true, leaving the UI in a broken state.
Now: if play() rejects, reset state and show "Click Play to start". The user's
click on the Play button IS a user gesture, so it succeeds on the second attempt.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Instead of trying a HTTPS upgrade (which fails for IP-based streams):
- playStation() detects http:// URL on https:// page, opens /radio/stream-player/
with url, name, vol as query params, then returns — main stream is already
stopped by the stopPlayback(false) call at the top of playStation()
- New view stream_player renders a standalone minimal player page
- Template: auto-plays on load, correct volume from URL param, volume changes
synced back to localStorage so main window picks it up next time,
live track metadata via SSE, tab title updates on track change,
close-tab button
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Python booleans (True/False) in saved_stations (is_favorite field) and
history (scrobbled field) were being rendered literally into JS via
|safe, causing 'True is not defined' ReferenceError that broke all JS
including book loading.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Shows live progress counter (↻ 3/42) while fetching feeds sequentially,
then reloads the current view when done.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Annotates feed queryset with Max(episodes__pub_date) so feeds are sorted
by when their latest episode was published, not when the feed was last fetched.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Auto-play next episode from queue when current episode ends
- Sleep timer (N minutes or end-of-episode) with countdown in button
- In-feed episode filter (client-side search)
- Auto-queue new episodes per feed (⚡Q toggle, inserts at top of queue)
- More playback speeds: 1¾× and 2½× added
- Progress bars + structured meta line in all episode list views (feed, inbox, queue)
- Queue drag-and-drop reorder
- Feed list search filter and sort options (A–Z, Z–A, recently added/refreshed)
- DB migration: PodcastFeed.auto_queue, EpisodeProgress.dismissed
- Inbox: dismiss episodes without marking played, checkboxes for multi-select,
bulk actions (add to queue, mark played, download, dismiss), load-more pagination
- Refresh button in single feed view header
- Hourly background refresh of all subscribed feeds
- Full Media Session API for radio and podcast: Windows taskbar thumbnail buttons
(play/pause/stop/next/seek) now work correctly for both modes
- Playing an episode auto-adds it to the queue if not already there
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- getOrCreateEncKey() now generates a random AES-GCM-256 key if none
found in localStorage, instead of throwing an error
- Removed enc-key-prompt div from player.html entirely
- Simplified initBookDropZone() — removed prompt show/hide logic
- book-upload-area always visible, no longer hidden behind prompt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Bypasses unreliable login-form interception; user enters password once
per device to derive the same PBKDF2 key cross-device.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>