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 = (