Audioausgabe pro Tab: YouTube und YouTube Musik auf verschiedene Geraete legen

In den Einstellungen (Gruppe "Audioausgabe", Felder "YouTube" und "YouTube Musik") laesst
sich getrennt waehlen, ueber welches Ausgabegeraet der Ton jedes Tabs laeuft - z.B. auf
getrennte Sonar-Kanaele. Die Auswahl gilt sofort, auch fuer bereits geoeffnete Seiten.

Warum ueber die Seite: QtWebEngine spielt beide Tabs ueber einen gemeinsamen Audio-Prozess
ab, Windows kann sie daher nicht einzeln routen. Chromium kann aber pro Medienelement ein
Geraet waehlen (setSinkId).

- audio_routing.py (neu): Router-Skript (findet das Geraet ueber seinen Namen, kennt den
  Hardware-ID-Suffix von Chromium, behandelt dynamisch erzeugte Elemente, Zielwechsel,
  Zuruecksetzen und fehlende Geraete), Geraeteliste (QMediaDevices), Freigabe der
  Geraetenamen (Chromium blendet sie ohne Mikrofon-Berechtigung aus)
- Freigabe nur solange ein Tab ein eigenes Geraet nutzt, nur fuer die beiden YouTube-
  Seiten, nur fuer die laufende Sitzung (Qt speichert sie nicht) - bei jedem Start und nach
  jedem Seitenaufbau neu erteilt, weil sie auf der YouTube-Startseite sonst nicht greift
- browser: BrowserTab.set_audio_output (Skript pro Seite; ohne Auswahl wird die Seite gar
  nicht angefasst)
- settings_tab: neue Gruppe, Hot-Plug-Aktualisierung, fehlendes Geraet bleibt als "nicht
  verfuegbar" gespeichert, Discord-Anzeige nennt das bearbeitete Feld
- config: audio.youtube_output / audio.music_output (leer = Systemstandard)
- README: Abschnitt inkl. Hinweis, dass Windows weiter einen Eintrag "Playtube" zeigt
This commit is contained in:
2026-09-19 21:14:36 +02:00
parent d28b5d05da
commit 3f3f86f597
6 changed files with 302 additions and 1 deletions
+29
View File
@@ -17,6 +17,8 @@ from PySide6.QtWebEngineCore import (
)
from PySide6.QtWebEngineWidgets import QWebEngineView
from .audio_routing import SCRIPT_NAME as AUDIO_SCRIPT_NAME
from .audio_routing import build_router_script
from .chrome_shim import CHROME_FULL, CHROME_MAJOR, CHROME_SHIM_JS
from .config import profile_dir
from .media_probe import MEDIA_PROBE_JS, NEXT_TRACK_JS, PREV_TRACK_JS, TOGGLE_PLAYBACK_JS
@@ -116,6 +118,7 @@ class BrowserTab(QWebEngineView):
def __init__(self, home_url: str, parent=None):
super().__init__(parent)
self._home_url = home_url
self._audio_output_set = False # war schon ein eigenes Ausgabegeraet gewaehlt? (siehe set_audio_output)
page = QWebEnginePage(get_shared_profile(), self)
self.setPage(page)
@@ -142,6 +145,32 @@ class BrowserTab(QWebEngineView):
def go_home(self) -> None:
self.load(QUrl(self._home_url))
def set_audio_output(self, device_name: str) -> None:
"""Legt den Ton dieses Tabs auf das Ausgabegeraet `device_name` (leer =
Systemstandard), siehe audio_routing.py. Wirkt sofort auf die geoeffnete Seite und
bleibt bei Seitenwechseln/Neuladen erhalten (Skript pro Seite)."""
page = self.page()
scripts = page.scripts()
for old in scripts.find(AUDIO_SCRIPT_NAME):
scripts.remove(old)
source = build_router_script(device_name)
if device_name:
script = QWebEngineScript()
script.setName(AUDIO_SCRIPT_NAME)
script.setInjectionPoint(QWebEngineScript.InjectionPoint.DocumentReady)
script.setWorldId(QWebEngineScript.ScriptWorldId.MainWorld)
script.setRunsOnSubFrames(False)
script.setSourceCode(source)
scripts.insert(script)
# Bereits geladene Seite sofort umstellen - auch zurueck auf den Systemstandard,
# falls vorher ein eigenes Geraet gewaehlt war. Ohne jemals gewaehltes Geraet wird die
# Seite gar nicht angefasst.
if device_name or self._audio_output_set:
page.runJavaScript(source)
self._audio_output_set = bool(device_name)
def toggle_playback(self) -> None:
self.page().runJavaScript(TOGGLE_PLAYBACK_JS)