feat: add quality selection, improve chapter panel UX, and show VOD view counts

Quality selection during playback:
- Add getAvailableQualities() in streamlink via --json, sorted by fixed order
- Expose playback:get-qualities IPC channel (handlers, preload, type declarations)
- Add quality panel in PlaybackOverlay:
  - Button + dropdown
  - Keyboard navigation (X: open, arrows: navigate, Enter: confirm, Escape: close)
- Quality change triggers streamlink restart; VOD resumes at current position
- Available in ChannelScreen (Live + VOD) and AppShell global overlay (Live)
- Add QualityIcon

Chapter panel UX improvements:
- Video no longer pauses when chapter panel opens
- PlaybackOverlay is hidden via conditional render instead
- Remove wasPlayingBeforeChapters state (no longer needed)
- X button seeks to video start when no chapters are found, with hint in panel UI

VOD cards and defaults:
- Show view count with eye icon on VOD cards (matching playback overlay style)
- Change default streamBadgeMode from 'language' to 'both'
This commit is contained in:
Andre
2026-05-10 12:13:34 +02:00
parent 23eeb9912c
commit 1251cc278a
14 changed files with 594 additions and 81 deletions
+5 -2
View File
@@ -22,6 +22,7 @@ const IPC = {
playbackStop: 'playback:stop',
playbackPause: 'playback:pause',
playbackReportPosition: 'playback:report-position',
playbackGetQualities: 'playback:get-qualities',
playbackEvent: 'playback:event',
playbackHlsUrl: 'playback:hls-url',
@@ -153,8 +154,10 @@ const api = {
playback: {
startLive: (channelLogin: string, quality?: string): Promise<void> =>
ipcRenderer.invoke(IPC.playbackStartLive, channelLogin, quality),
startVod: (vodId: string, channelLogin: string, title: string, durationSeconds: number, startSeconds?: number): Promise<void> =>
ipcRenderer.invoke(IPC.playbackStartVod, vodId, channelLogin, title, durationSeconds, startSeconds),
startVod: (vodId: string, channelLogin: string, title: string, durationSeconds: number, startSeconds?: number, quality?: string): Promise<void> =>
ipcRenderer.invoke(IPC.playbackStartVod, vodId, channelLogin, title, durationSeconds, startSeconds, quality),
getQualities: (twitchUrl: string): Promise<string[]> =>
ipcRenderer.invoke(IPC.playbackGetQualities, twitchUrl),
stop: (): Promise<void> => ipcRenderer.invoke(IPC.playbackStop),
pause: (): Promise<void> => ipcRenderer.invoke(IPC.playbackPause),
reportPosition: (vodId: string, positionSeconds: number, durationSeconds: number): Promise<void> =>