src/player-state.ts (2033 bytes)
1 // The state line of the audio player, as SubRead Overlay reports it. Pure. 2 // 3 // SubRead Overlay is an Android app with notification access. It reads the 4 // media session of the player and gives one line to other apps: 5 // 6 // playing=1;position=96153;speed=1.0;package=de.ph1b.audiobook 7 // 8 // The position is in milliseconds, for the moment of the query. A problem is 9 // one line `error=<reason>`. This is a port of player_state.lua from the 10 // SubRead plugin for KOReader. 11 12 export interface PlayerState { 13 playing: boolean; 14 /** Position in milliseconds. Null when the player reports none. */ 15 positionMs: number | null; 16 /** Playback speed. 1 when the line has none, or a value of zero or less. */ 17 speed: number; 18 /** Package name of the player app. Empty when the line has none. */ 19 package: string; 20 } 21 22 export interface PlayerError { 23 /** `no_overlay`, `no_notification_access`, `no_player`, `empty` or `malformed`. */ 24 error: string; 25 } 26 27 export function isPlayerError(x: PlayerState | PlayerError): x is PlayerError { 28 return 'error' in x; 29 } 30 31 const FIELD = /([\w]+)=([^;]*)/g; 32 33 /** Parses a state line. */ 34 export function parseState(line: string | null | undefined): PlayerState | PlayerError { 35 if (typeof line !== 'string' || line === '') return { error: 'empty' }; 36 const fields = new Map<string, string>(); 37 for (const m of line.matchAll(FIELD)) fields.set(m[1]!, m[2]!); 38 const error = fields.get('error'); 39 if (error !== undefined) return { error }; 40 const playing = fields.get('playing'); 41 const position = fields.get('position'); 42 if (playing === undefined || position === undefined) return { error: 'malformed' }; 43 const positionMs = Number(position); 44 if (position.trim() === '' || Number.isNaN(positionMs)) return { error: 'malformed' }; 45 let speed = Number(fields.get('speed') ?? '1'); 46 if (Number.isNaN(speed) || speed <= 0) speed = 1; 47 return { 48 playing: playing === '1', 49 positionMs: positionMs >= 0 ? positionMs : null, 50 speed, 51 package: fields.get('package') ?? '', 52 }; 53 }