Files
twitch4steamdeck/src/main/playback/playbackService.ts
T
Andre 722bbc6f43 feat: add German and English UI language support
A single-language interface makes the app less usable for part of the audience
and encourages more hardcoded copy as new screens and states are added.
Introducing a shared i18n path with a persisted language preference gives the
UI one consistent way to present text and lowers the risk of partial or
inconsistent localization over time.

The surrounding copy cleanup belongs in the same change because it aligns
existing labels, messages, and settings text with that translation structure
instead of leaving mixed localization approaches in place.
2026-05-16 11:44:06 +02:00

90 lines
2.5 KiB
TypeScript

import { EventEmitter } from 'node:events'
import { getAvailableQualities, getStreamUrl } from './streamlink'
import type { PlaybackEvent } from './types'
import * as history from '../store/historyRepo'
export interface HlsUrlPayload {
url: string
isLive: boolean
startPosition: number
durationSeconds: number
vodId?: string
}
export class PlaybackService extends EventEmitter {
async startLive(channelLogin: string, quality = 'best'): Promise<void> {
let hlsUrl: string
try {
hlsUrl = await getStreamUrl(`twitch.tv/${channelLogin}`, quality)
} catch (e) {
this.emit('playback-event', {
kind: 'error',
message: `Failed to resolve stream URL: ${e}`
} satisfies PlaybackEvent)
return
}
this.emit('playback-hls-url', {
url: hlsUrl,
isLive: true,
startPosition: 0,
durationSeconds: 0
} satisfies HlsUrlPayload)
this.emit('playback-event', { kind: 'started', isLive: true } satisfies PlaybackEvent)
}
getAvailableQualities(twitchUrl: string): Promise<string[]> {
return getAvailableQualities(twitchUrl)
}
async startVod(
vodId: string,
channelLogin: string,
title: string,
durationSeconds: number,
startSeconds?: number,
quality = 'best'
): Promise<void> {
const resumePos = history.getPosition(vodId)
history.upsertVod({
vod_id: vodId,
channel_login: channelLogin,
title,
duration_seconds: durationSeconds,
watched_at: Date.now()
})
let hlsUrl: string
try {
hlsUrl = await getStreamUrl(`https://www.twitch.tv/videos/${vodId}`, quality)
} catch (e) {
this.emit('playback-event', {
kind: 'error',
message: `Failed to resolve stream URL: ${e}`
} satisfies PlaybackEvent)
return
}
const effectiveStart = startSeconds !== undefined ? startSeconds : resumePos
this.emit('playback-hls-url', {
url: hlsUrl,
isLive: false,
startPosition: effectiveStart,
durationSeconds,
vodId
} satisfies HlsUrlPayload)
this.emit('playback-event', { kind: 'started', durationSeconds, isLive: false } satisfies PlaybackEvent)
}
stop(): void {
this.emit('playback-event', { kind: 'stopped' } satisfies PlaybackEvent)
}
updateVodPosition(vodId: string, positionSeconds: number, durationSeconds: number): void {
const pos = Math.floor(positionSeconds)
history.updatePosition(vodId, pos)
if (durationSeconds > 0 && pos / durationSeconds > 0.95) {
history.markCompleted(vodId)
}
}
}