Tolerate space-padded pactl output, and show the running app version

The device-source parser assumed pactl list short sources is strictly
tab-separated, true for stock PulseAudio but not guaranteed for a
TV's own heavily customized audio stack (this one names sources
tpcm_output/tpmedia/tptts/... — clearly not vanilla). A different
separator would have silently produced zero parsed sources with no
error, and the picker's own `when` guard would then just hide the row
entirely rather than show anything broken. Split on any whitespace
run instead of a literal tab; source names never contain embedded
whitespace, so this is strictly more permissive with no new failure
mode. Confirmed end to end on real hardware: tptts.monitor lit up
during the accessibility voice guide and reached HyperHDR.

Also: the System panel now shows the app's actual running version,
read from a <meta> tag substituted at package time (tools/build.sh
stage()) from frontend/appinfo.json — not hand-maintained, so it can't
drift from what was actually built. Requested after a version bump
alone wasn't enough to tell whether a reinstall had truly picked up
new files versus served something cached along the way; this settles
that question by inspection instead of by inference. Bumped to 1.0.2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Rene Kievits
2026-08-26 14:11:29 +02:00
co-authored by Claude Opus 5
parent d2931bee63
commit f0f68a1aa7
9 changed files with 46 additions and 17 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{
"id": "org.webosbrew.audiocap",
"version": "1.0.1",
"version": "1.0.2",
"vendor": "Homebrew",
"type": "web",
"main": "index.html",
+4
View File
@@ -3,6 +3,10 @@
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=1920, initial-scale=1">
<!-- Substituted at package time from frontend/appinfo.json (tools/build.sh
stage()), so what the System panel shows is what actually got built
into this ipk — not something that can itself go stale in a cache. -->
<meta name="app-version" content="__APP_VERSION__">
<title>Audio Cap</title>
<link rel="stylesheet" href="css/app.css">
</head>
+17 -3
View File
@@ -13,6 +13,16 @@
var ELEVATE = '/media/developer/apps/usr/palm/services/'
+ 'org.webosbrew.hbchannel.service/elevate-service';
// Substituted into index.html at package time (tools/build.sh stage()).
// Unstaged — opened straight from the source tree, e.g. npm run serve —
// it is still the literal placeholder, which is exactly the tell that
// this isn't a packaged build.
function readAppVersion() {
var meta = document.querySelector('meta[name="app-version"]');
var content = meta && meta.getAttribute('content');
return content && content.indexOf('__') !== 0 ? content : 'dev build';
}
var state = {
settings: {},
status: null,
@@ -22,6 +32,7 @@
persistent: true,
bootLinked: false,
diagnostics: null,
appVersion: readAppVersion(),
};
var statusSub = null;
@@ -192,8 +203,10 @@
// only read on the TV itself, is exactly the kind of thing a D-pad picker
// exists for. Parsed from the same "pactl list short sources" text that
// System > Run diagnostics already fetches — nothing new to ask the
// service for. Format is tab-separated: index, name, driver, sample_spec,
// state.
// service for. Stock PulseAudio tab-separates columns (index, name,
// driver, sample_spec, state), but split on any whitespace run rather than
// a literal tab: a TV's own pactl-alike is free to pad with spaces
// instead, and source names never contain embedded whitespace themselves.
function pulseSourceOptions() {
var diag = state.diagnostics && state.diagnostics.system;
var text = diag && diag.pactlSources;
@@ -202,7 +215,7 @@
return out;
}
text.split('\n').forEach(function (line) {
var cols = line.split('\t');
var cols = line.trim().split(/\s+/);
var name = cols[1];
if (!name) {
return;
@@ -526,6 +539,7 @@
var info = $('config-path');
UI.clear(info);
info.appendChild(infoItem('App version', state.appVersion));
info.appendChild(infoItem('Path', state.configPath || '—'));
info.appendChild(infoItem('Storage', state.persistent
? 'Persistent' : 'Temporary (/tmp)'));
+6 -4
View File
@@ -175,11 +175,13 @@
pulseSockets: ['/var/run/pulse/native'],
// Realistic shape: a TV that mixes several per-app sinks down to
// one common output, the case the device picker exists for.
// Space-padded, not tab-separated — some TVs' own pactl-alike
// formats it that way, and the parser has to tolerate both.
pactlSources: [
'0\ttpcm_output.monitor\tmodule-combine-sink.c\ts16le 2ch 48000Hz\tRUNNING',
'1\ttpmedia.monitor\tmodule-alsa-card.c\ts16le 2ch 48000Hz\tIDLE',
'2\ttpeffects.monitor\tmodule-alsa-card.c\ts16le 2ch 48000Hz\tIDLE',
'3\ttptts.monitor\tmodule-alsa-card.c\ts16le 2ch 48000Hz\tSUSPENDED',
'0 tpcm_output.monitor module-combine-sink.c s16le 2ch 48000Hz RUNNING',
'1 tpmedia.monitor module-alsa-card.c s16le 2ch 48000Hz IDLE',
'2 tpeffects.monitor module-alsa-card.c s16le 2ch 48000Hz IDLE',
'3 tptts.monitor module-alsa-card.c s16le 2ch 48000Hz SUSPENDED',
].join('\n'),
alsaCards: ['0 [Loopback]: Loopback - Loopback'],
alsaCapturePcms: ['00-01: Loopback PCM : playback 1 : capture 1'],