From 11a4058e162f24b9f0f74e89ee065fcbfa12a39d Mon Sep 17 00:00:00 2001 From: huseyin-emre-tigci Date: Mon, 17 Aug 2026 01:02:42 +0300 Subject: [PATCH] Let the device listing match ffmpeg 8's bracket prefix The dshow parser pinned the bracketed prefix to the `[dshow @ ...]` the older builds print, and ffmpeg 8 writes `[in#0 @ ...]` there instead: on a current install every line fell through the anchors, the device list came back empty, and a dictation with no microphone picked could not start at all. The prefix is now any bracketed tag, which the anchors and the quoted name still keep apart from the error lines. Pinned with a listing taken verbatim from ffmpeg 8.1.2 on Windows 11, third kind marker `(none)` included. Co-Authored-By: Claude Fable 5 --- dikte/audio.py | 8 +++++--- tests/test_audio.py | 16 ++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/dikte/audio.py b/dikte/audio.py index edcd5dc..ef24461 100644 --- a/dikte/audio.py +++ b/dikte/audio.py @@ -874,11 +874,13 @@ def _avfoundation_default_output(): # this listing in. Newer builds mark each device `(audio)` or `(video)`; older # ones print no marker and group the devices under a heading instead. Both are # anchored at each end, so that the error lines the command ends with, which -# quote the device name that was not found, are not read as devices. +# quote the device name that was not found, are not read as devices. The +# bracketed prefix is not pinned to a spelling: ffmpeg 8 writes `[in#0 @ ...]` +# where the versions before it wrote `[dshow @ ...]`. _DSHOW_ENTRY = re.compile( - r'^(?:\[dshow @ [^\]]*\]\s*)?"([^"]+)"\s*(?:\(([^)]*)\))?\s*$') + r'^(?:\[[^\]]*\]\s*)?"([^"]+)"\s*(?:\(([^)]*)\))?\s*$') _DSHOW_ALTERNATIVE = re.compile( - r'^(?:\[dshow @ [^\]]*\]\s*)?Alternative name\s+"([^"]+)"\s*$') + r'^(?:\[[^\]]*\]\s*)?Alternative name\s+"([^"]+)"\s*$') _DSHOW_HEADING = re.compile(r'DirectShow (audio|video) devices') # The last listing taken, so that a dictation does not pay for one of its own. diff --git a/tests/test_audio.py b/tests/test_audio.py index 7435443..a7ea353 100644 --- a/tests/test_audio.py +++ b/tests/test_audio.py @@ -982,6 +982,22 @@ class WindowsDevices(OnWindows, DikteTest): with self.listing(): self.assertNotIn("dummy", [name for _, name in audio.list_sources()]) + def test_a_listing_from_ffmpeg_8_which_renamed_the_prefix(self): + """ffmpeg 8 writes `[in#0 @ ...]` where older builds wrote `[dshow @ ...]`.""" + listing = ( + '[in#0 @ 00000238c3300ac0] "Integrated Camera" (video)\n' + '[in#0 @ 00000238c3300ac0] Alternative name "@device_pnp_\\..."\n' + '[in#0 @ 00000238c3300ac0] "OBS Virtual Camera" (none)\n' + '[in#0 @ 00000238c3300ac0] Alternative name "@device_sw_{860B}"\n' + '[in#0 @ 00000238c3300ac0] "Mikrofon Dizisi (IntelĀ® Smart Sound)" (audio)\n' + '[in#0 @ 00000238c3300ac0] Alternative name "@device_cm_{33D9}"\n' + "Error opening input file dummy.\n" + ).encode("utf-8") + with self.listing(stderr=listing): + self.assertEqual(audio.list_sources(), + [("@device_cm_{33D9}", + "Mikrofon Dizisi (IntelĀ® Smart Sound)")]) + def test_a_listing_from_an_ffmpeg_that_marks_nothing(self): """Older builds print a heading instead of an (audio) on every line.""" listing = (