Let the Device field be picked, not typed blind
Diagnosing capture on a real TV meant reading pactlSources off the screen and typing an exact PulseAudio source name back in through the same remote-driven text field — no way to copy-paste, easy to mistype, and the one piece of information (which source, if any, is actually RUNNING) was buried in a JSON dump. Added two choice() pickers bound to the same capture.device setting: one built from pactlSources (pulse/auto backends), one built from alsaCapturePcms (alsa backend), both parsed from diagnostics the service already collects — no new Luna method needed. Diagnostics already run once at boot, so the picker is populated immediately, before the user ever presses "Run diagnostics" by hand. Picking a value writes straight into capture.device, and the plain text field stays as the fallback for anything the parser misses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
f3a4cddfd6
commit
0759cc00aa
+67
-1
@@ -188,6 +188,49 @@
|
||||
};
|
||||
}
|
||||
|
||||
// Typing a PulseAudio source name blind, off a diagnostics dump you can
|
||||
// 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.
|
||||
function pulseSourceOptions() {
|
||||
var diag = state.diagnostics && state.diagnostics.system;
|
||||
var text = diag && diag.pactlSources;
|
||||
var out = [{ value: '', label: 'Automatic (@DEFAULT_MONITOR@)' }];
|
||||
if (!text) {
|
||||
return out;
|
||||
}
|
||||
text.split('\n').forEach(function (line) {
|
||||
var cols = line.split('\t');
|
||||
var name = cols[1];
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
var running = cols[4] ? ' — ' + cols[4] : '';
|
||||
out.push({ value: name, label: name + running });
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
// alsaCapturePcms lines look like "00-01: ALC1220 Analog : ... : capture 1"
|
||||
// — "00-01" is card 0, device 1, so hw:0,1. Best-effort: a line that does
|
||||
// not start with that pattern is skipped rather than guessed at.
|
||||
function alsaDeviceOptions() {
|
||||
var diag = state.diagnostics && state.diagnostics.system;
|
||||
var lines = (diag && diag.alsaCapturePcms) || [];
|
||||
var out = [{ value: '', label: 'Automatic (default)' }];
|
||||
lines.forEach(function (line) {
|
||||
var m = /^(\d+)-(\d+):\s*(.*)$/.exec(line);
|
||||
if (!m) {
|
||||
return;
|
||||
}
|
||||
var hw = 'hw:' + parseInt(m[1], 10) + ',' + parseInt(m[2], 10);
|
||||
out.push({ value: hw, label: hw + ' — ' + m[3] });
|
||||
});
|
||||
return out;
|
||||
}
|
||||
|
||||
var CAPTURE_FIELDS = [
|
||||
{
|
||||
path: 'capture.backend', label: 'Backend', type: 'choice',
|
||||
@@ -199,7 +242,26 @@
|
||||
when: backendIs(['auto', 'pulse', 'alsa']),
|
||||
placeholder: 'blank = default monitor',
|
||||
hint: 'PulseAudio source name, or an ALSA PCM such as hw:0,0. '
|
||||
+ 'Run diagnostics to see what this TV has.',
|
||||
+ 'Run diagnostics, then use the picker below instead of typing.',
|
||||
},
|
||||
{
|
||||
path: 'capture.device', label: 'Pick a discovered source', type: 'choice',
|
||||
rebuild: true, wide: true,
|
||||
when: function (s) {
|
||||
return backendIs(['auto', 'pulse'])(s) && pulseSourceOptions().length > 1;
|
||||
},
|
||||
options: pulseSourceOptions,
|
||||
hint: 'From the last diagnostics run. Press Enter to cycle through '
|
||||
+ 'every source this TV reported; picking one fills the Device field above.',
|
||||
},
|
||||
{
|
||||
path: 'capture.device', label: 'Pick a discovered device', type: 'choice',
|
||||
rebuild: true, wide: true,
|
||||
when: function (s) {
|
||||
return backendIs(['alsa'])(s) && alsaDeviceOptions().length > 1;
|
||||
},
|
||||
options: alsaDeviceOptions,
|
||||
hint: 'From the last diagnostics run.',
|
||||
},
|
||||
{
|
||||
path: 'capture.server', label: 'PulseAudio server', type: 'text', wide: true,
|
||||
@@ -714,6 +776,10 @@
|
||||
|
||||
function runDiagnostics() {
|
||||
Luna.getDiagnostics(function (reply) {
|
||||
state.diagnostics = reply;
|
||||
// The Capture panel's device pickers are built from this same reply,
|
||||
// so refresh it if that's the panel currently open.
|
||||
renderCapture();
|
||||
var copy = JSON.parse(JSON.stringify(reply));
|
||||
delete copy.returnValue;
|
||||
showOutput(JSON.stringify(copy, null, 2));
|
||||
|
||||
+8
-1
@@ -173,7 +173,14 @@
|
||||
},
|
||||
binaries: { parec: false, pactl: true, pacat: false, arecord: true },
|
||||
pulseSockets: ['/var/run/pulse/native'],
|
||||
pactlSources: 'mock output',
|
||||
// Realistic shape: a TV that mixes several per-app sinks down to
|
||||
// one common output, the case the device picker exists for.
|
||||
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',
|
||||
].join('\n'),
|
||||
alsaCards: ['0 [Loopback]: Loopback - Loopback'],
|
||||
alsaCapturePcms: ['00-01: Loopback PCM : playback 1 : capture 1'],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user