Update mpv_player.py
Fix bug with audio backend detection. ALSA was not being detected correctly.
This commit is contained in:
@@ -31,43 +31,69 @@ def _select_backend(player: MPV, device: Optional[str]) -> None:
|
|||||||
"""
|
"""
|
||||||
Selects the best available audio output backend for mpv.
|
Selects the best available audio output backend for mpv.
|
||||||
|
|
||||||
The selection follows a clear priority:
|
1. If a specific 'device' is given (e.g., 'alsa/plughw:CARD=...'), it is used directly.
|
||||||
1. A fully qualified device string (e.g., 'alsa/plughw:...') is used directly.
|
2. If 'device' is None, it queries mpv for available devices and picks the first
|
||||||
2. A simple device name tries PipeWire, then PulseAudio, then ALSA.
|
available backend in the preferred order (pipewire > pulse > alsa).
|
||||||
3. No device specified (or 'default') tries the default for PipeWire, PulseAudio, and ALSA in order.
|
3. If all else fails, it falls back to 'auto'.
|
||||||
"""
|
"""
|
||||||
candidates: List[Tuple[str, str]] = []
|
|
||||||
|
|
||||||
# Define the preferred order of audio outputs
|
# 1. Explicit Path: User provided a specific device. Use it directly.
|
||||||
preferred_ao_order = ["pipewire", "pulse", "alsa"]
|
|
||||||
|
|
||||||
if device:
|
if device:
|
||||||
# If a full device path is given (e.g., "alsa/plughw:...")
|
_LOGGER.debug(f"User specified audio device: {device}")
|
||||||
for prefix in preferred_ao_order:
|
|
||||||
if device.startswith(f"{prefix}/"):
|
|
||||||
candidates.append((prefix, device))
|
|
||||||
break
|
|
||||||
|
|
||||||
# If it's a simple name or "default"
|
|
||||||
if not candidates:
|
|
||||||
for ao in preferred_ao_order:
|
|
||||||
candidates.append((ao, f"{ao}/{device}"))
|
|
||||||
else:
|
|
||||||
# If no device is specified, try the default for each backend
|
|
||||||
for ao in preferred_ao_order:
|
|
||||||
candidates.append((ao, f"{ao}/default"))
|
|
||||||
|
|
||||||
# Try to set the audio output from the generated candidates
|
|
||||||
for ao, audio_device in candidates:
|
|
||||||
try:
|
try:
|
||||||
player["ao"] = ao
|
# If it's a full path like 'alsa/plughw:...'
|
||||||
player["audio-device"] = audio_device
|
if '/' in device:
|
||||||
_LOGGER.debug("mpv backend selected: ao=%s, audio-device=%s", ao, audio_device)
|
ao, _ = device.split('/', 1)
|
||||||
return
|
player["ao"] = ao
|
||||||
except Exception:
|
player["audio-device"] = device
|
||||||
continue
|
_LOGGER.info(f"mpv backend set (explicit): ao={ao}, audio-device={device}")
|
||||||
|
return
|
||||||
_LOGGER.warning("No suitable audio output backend could be found.")
|
else:
|
||||||
|
# Ambiguous name like 'default'. Fall through to auto-detection.
|
||||||
|
_LOGGER.debug(f"Ambiguous device name '{device}', falling back to auto-detection.")
|
||||||
|
except Exception as e:
|
||||||
|
_LOGGER.warning(f"Failed to parse explicit audio device {device}: {e}. Falling back to auto-detection.")
|
||||||
|
|
||||||
|
# 2. Automatic Path: No device specified or fallback from ambiguous name.
|
||||||
|
_LOGGER.debug("Starting audio backend auto-detection.")
|
||||||
|
|
||||||
|
available_device_names = set()
|
||||||
|
try:
|
||||||
|
available_devices = player.audio_device_list
|
||||||
|
if not available_devices:
|
||||||
|
_LOGGER.warning("mpv returned an empty list of audio devices. Will try defaults.")
|
||||||
|
else:
|
||||||
|
available_device_names = {dev['name'] for dev in available_devices}
|
||||||
|
_LOGGER.debug(f"Available audio devices found: {available_device_names}")
|
||||||
|
except Exception as e:
|
||||||
|
_LOGGER.error(f"Failed to query mpv for audio devices: {e}. Will try defaults.")
|
||||||
|
|
||||||
|
# Our preferred order of drivers
|
||||||
|
preferred_drivers = ["pipewire", "pulse", "alsa"]
|
||||||
|
|
||||||
|
if available_device_names:
|
||||||
|
for driver in preferred_drivers:
|
||||||
|
# Check if ANY device in the list starts with this driver's prefix
|
||||||
|
prefix = f"{driver}/"
|
||||||
|
# Also check for the simple driver name, e.g., 'alsa'
|
||||||
|
if any(name.startswith(prefix) for name in available_device_names) or (driver in available_device_names):
|
||||||
|
try:
|
||||||
|
# --- THIS IS THE FIX ---
|
||||||
|
# For pulse and pipewire, set audio-device to "default"
|
||||||
|
# For alsa, it's safer to use "alsa" as the device
|
||||||
|
device_name = "default" if driver in ["pipewire", "pulse"] else driver
|
||||||
|
|
||||||
|
player["ao"] = driver
|
||||||
|
player["audio-device"] = device_name
|
||||||
|
_LOGGER.info(f"Auto-detected and set active backend: ao={driver}, audio-device={device_name}")
|
||||||
|
return
|
||||||
|
except Exception as e:
|
||||||
|
_LOGGER.warning(f"Failed to set auto-detected driver {driver}: {e}")
|
||||||
|
|
||||||
|
# 3. Fallback Path: No devices found, or no prefix matched.
|
||||||
|
_LOGGER.warning("Could not find a preferred driver in mpv's list. Telling mpv to use 'auto'.")
|
||||||
|
player["ao"] = "auto"
|
||||||
|
player["audio-device"] = "auto"
|
||||||
|
|
||||||
|
|
||||||
class MpvMediaPlayer:
|
class MpvMediaPlayer:
|
||||||
@@ -78,7 +104,7 @@ class MpvMediaPlayer:
|
|||||||
device: Optional[str] = None,
|
device: Optional[str] = None,
|
||||||
initial_volume: float = 1.0
|
initial_volume: float = 1.0
|
||||||
) -> None:
|
) -> None:
|
||||||
self.loop = loop # <-- ADDED
|
self.loop = loop
|
||||||
self.player = MPV(
|
self.player = MPV(
|
||||||
video=False,
|
video=False,
|
||||||
terminal=False,
|
terminal=False,
|
||||||
@@ -91,7 +117,7 @@ class MpvMediaPlayer:
|
|||||||
msg_level=os.environ.get("LVA_MPV_MSG_LEVEL", "all=warn"),
|
msg_level=os.environ.get("LVA_MPV_MSG_LEVEL", "all=warn"),
|
||||||
)
|
)
|
||||||
|
|
||||||
_select_backend(self.player, device)
|
_select_backend(self.player, device) # <-- This is the updated function
|
||||||
self.set_volume(int(initial_volume * 100))
|
self.set_volume(int(initial_volume * 100))
|
||||||
|
|
||||||
self.is_playing: bool = False
|
self.is_playing: bool = False
|
||||||
@@ -194,7 +220,7 @@ class MpvMediaPlayer:
|
|||||||
if cb:
|
if cb:
|
||||||
try:
|
try:
|
||||||
# Use call_soon_threadsafe to run the callback on the main loop
|
# Use call_soon_threadsafe to run the callback on the main loop
|
||||||
self.loop.call_soon_threadsafe(cb) # <-- MODIFIED
|
self.loop.call_soon_threadsafe(cb)
|
||||||
except Exception:
|
except Exception:
|
||||||
_LOGGER.exception("Error scheduling done_callback")
|
_LOGGER.exception("Error scheduling done_callback")
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user