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:
co-authored by
Claude Opus 5
parent
d2931bee63
commit
f0f68a1aa7
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"id": "org.webosbrew.audiocap",
|
"id": "org.webosbrew.audiocap",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"vendor": "Homebrew",
|
"vendor": "Homebrew",
|
||||||
"type": "web",
|
"type": "web",
|
||||||
"main": "index.html",
|
"main": "index.html",
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<meta name="viewport" content="width=1920, initial-scale=1">
|
<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>
|
<title>Audio Cap</title>
|
||||||
<link rel="stylesheet" href="css/app.css">
|
<link rel="stylesheet" href="css/app.css">
|
||||||
</head>
|
</head>
|
||||||
|
|||||||
+17
-3
@@ -13,6 +13,16 @@
|
|||||||
var ELEVATE = '/media/developer/apps/usr/palm/services/'
|
var ELEVATE = '/media/developer/apps/usr/palm/services/'
|
||||||
+ 'org.webosbrew.hbchannel.service/elevate-service';
|
+ '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 = {
|
var state = {
|
||||||
settings: {},
|
settings: {},
|
||||||
status: null,
|
status: null,
|
||||||
@@ -22,6 +32,7 @@
|
|||||||
persistent: true,
|
persistent: true,
|
||||||
bootLinked: false,
|
bootLinked: false,
|
||||||
diagnostics: null,
|
diagnostics: null,
|
||||||
|
appVersion: readAppVersion(),
|
||||||
};
|
};
|
||||||
|
|
||||||
var statusSub = null;
|
var statusSub = null;
|
||||||
@@ -192,8 +203,10 @@
|
|||||||
// only read on the TV itself, is exactly the kind of thing a D-pad picker
|
// 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
|
// exists for. Parsed from the same "pactl list short sources" text that
|
||||||
// System > Run diagnostics already fetches — nothing new to ask the
|
// System > Run diagnostics already fetches — nothing new to ask the
|
||||||
// service for. Format is tab-separated: index, name, driver, sample_spec,
|
// service for. Stock PulseAudio tab-separates columns (index, name,
|
||||||
// state.
|
// 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() {
|
function pulseSourceOptions() {
|
||||||
var diag = state.diagnostics && state.diagnostics.system;
|
var diag = state.diagnostics && state.diagnostics.system;
|
||||||
var text = diag && diag.pactlSources;
|
var text = diag && diag.pactlSources;
|
||||||
@@ -202,7 +215,7 @@
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
text.split('\n').forEach(function (line) {
|
text.split('\n').forEach(function (line) {
|
||||||
var cols = line.split('\t');
|
var cols = line.trim().split(/\s+/);
|
||||||
var name = cols[1];
|
var name = cols[1];
|
||||||
if (!name) {
|
if (!name) {
|
||||||
return;
|
return;
|
||||||
@@ -526,6 +539,7 @@
|
|||||||
|
|
||||||
var info = $('config-path');
|
var info = $('config-path');
|
||||||
UI.clear(info);
|
UI.clear(info);
|
||||||
|
info.appendChild(infoItem('App version', state.appVersion));
|
||||||
info.appendChild(infoItem('Path', state.configPath || '—'));
|
info.appendChild(infoItem('Path', state.configPath || '—'));
|
||||||
info.appendChild(infoItem('Storage', state.persistent
|
info.appendChild(infoItem('Storage', state.persistent
|
||||||
? 'Persistent' : 'Temporary (/tmp)'));
|
? 'Persistent' : 'Temporary (/tmp)'));
|
||||||
|
|||||||
+6
-4
@@ -175,11 +175,13 @@
|
|||||||
pulseSockets: ['/var/run/pulse/native'],
|
pulseSockets: ['/var/run/pulse/native'],
|
||||||
// Realistic shape: a TV that mixes several per-app sinks down to
|
// Realistic shape: a TV that mixes several per-app sinks down to
|
||||||
// one common output, the case the device picker exists for.
|
// 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: [
|
pactlSources: [
|
||||||
'0\ttpcm_output.monitor\tmodule-combine-sink.c\ts16le 2ch 48000Hz\tRUNNING',
|
'0 tpcm_output.monitor module-combine-sink.c s16le 2ch 48000Hz RUNNING',
|
||||||
'1\ttpmedia.monitor\tmodule-alsa-card.c\ts16le 2ch 48000Hz\tIDLE',
|
'1 tpmedia.monitor module-alsa-card.c s16le 2ch 48000Hz IDLE',
|
||||||
'2\ttpeffects.monitor\tmodule-alsa-card.c\ts16le 2ch 48000Hz\tIDLE',
|
'2 tpeffects.monitor module-alsa-card.c s16le 2ch 48000Hz IDLE',
|
||||||
'3\ttptts.monitor\tmodule-alsa-card.c\ts16le 2ch 48000Hz\tSUSPENDED',
|
'3 tptts.monitor module-alsa-card.c s16le 2ch 48000Hz SUSPENDED',
|
||||||
].join('\n'),
|
].join('\n'),
|
||||||
alsaCards: ['0 [Loopback]: Loopback - Loopback'],
|
alsaCards: ['0 [Loopback]: Loopback - Loopback'],
|
||||||
alsaCapturePcms: ['00-01: Loopback PCM : playback 1 : capture 1'],
|
alsaCapturePcms: ['00-01: Loopback PCM : playback 1 : capture 1'],
|
||||||
|
|||||||
Generated
+2
-2
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"name": "lgtv-audio-cap",
|
"name": "lgtv-audio-cap",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"lockfileVersion": 3,
|
"lockfileVersion": 3,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"packages": {
|
"packages": {
|
||||||
"": {
|
"": {
|
||||||
"name": "lgtv-audio-cap",
|
"name": "lgtv-audio-cap",
|
||||||
"version": "1.0.0",
|
"version": "1.0.1",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@webosose/ares-cli": "^2.4.0",
|
"@webosose/ares-cli": "^2.4.0",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "lgtv-audio-cap",
|
"name": "lgtv-audio-cap",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Captures audio on an LG webOS 5/6 TV and streams it out \u2014 HyperHDR first, plus raw UDP, TCP and HTTP.",
|
"description": "Captures audio on an LG webOS 5/6 TV and streams it out \u2014 HyperHDR first, plus raw UDP, TCP and HTTP.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"id": "org.webosbrew.audiocap.service",
|
"id": "org.webosbrew.audiocap.service",
|
||||||
"version": "1.0.1",
|
"version": "1.0.2",
|
||||||
"description": "Captures TV audio and streams it to HyperHDR and other receivers",
|
"description": "Captures TV audio and streams it to HyperHDR and other receivers",
|
||||||
"main": "audiocap-service"
|
"main": "audiocap-service"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,6 +114,13 @@ async function main() {
|
|||||||
check('mock bus in use', window.Luna.available === false);
|
check('mock bus in use', window.Luna.available === false);
|
||||||
check('settings loaded', !!(window.App.state.settings.capture));
|
check('settings loaded', !!(window.App.state.settings.capture));
|
||||||
eq('config path shown', $('config-path').textContent.indexOf('/var/lib/webosbrew') >= 0, true);
|
eq('config path shown', $('config-path').textContent.indexOf('/var/lib/webosbrew') >= 0, true);
|
||||||
|
// This loads the raw source tree's index.html, not a packaged build, so
|
||||||
|
// the __APP_VERSION__ placeholder was never substituted — the fallback
|
||||||
|
// is the correct, honest thing to see here.
|
||||||
|
eq('unpackaged run shows the dev-build fallback, not a stale version',
|
||||||
|
window.App.state.appVersion, 'dev build');
|
||||||
|
check('app version shown in the System panel',
|
||||||
|
$('config-path').textContent.indexOf('dev build') >= 0);
|
||||||
|
|
||||||
console.log('status feed');
|
console.log('status feed');
|
||||||
eq('starts stopped', $('state-pill').textContent, 'Stopped');
|
eq('starts stopped', $('state-pill').textContent, 'Stopped');
|
||||||
|
|||||||
+7
-5
@@ -143,12 +143,14 @@ stage() {
|
|||||||
cp -R "$ROOT/frontend/." "$STAGE_APP/"
|
cp -R "$ROOT/frontend/." "$STAGE_APP/"
|
||||||
# The mock only exists so the UI can be opened in a desktop browser.
|
# The mock only exists so the UI can be opened in a desktop browser.
|
||||||
rm -f "$STAGE_APP/js/mock.js"
|
rm -f "$STAGE_APP/js/mock.js"
|
||||||
python3 - "$STAGE_APP/index.html" <<'EOF'
|
python3 - "$STAGE_APP/index.html" "$ROOT/frontend/appinfo.json" <<'EOF'
|
||||||
import re, sys
|
import json, re, sys
|
||||||
path = sys.argv[1]
|
html_path, appinfo_path = sys.argv[1], sys.argv[2]
|
||||||
html = open(path).read()
|
html = open(html_path).read()
|
||||||
html = re.sub(r'\s*<script src="js/mock\.js"></script>', '', html)
|
html = re.sub(r'\s*<script src="js/mock\.js"></script>', '', html)
|
||||||
open(path, "w").write(html)
|
version = json.load(open(appinfo_path))["version"]
|
||||||
|
html = html.replace("__APP_VERSION__", version)
|
||||||
|
open(html_path, "w").write(html)
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
cp "$ROOT/servicefiles/services.json" "$STAGE_SERVICE/"
|
cp "$ROOT/servicefiles/services.json" "$STAGE_SERVICE/"
|
||||||
|
|||||||
Reference in New Issue
Block a user