From adff7f95a402d20b17b0950055a9ee35c7d708ad Mon Sep 17 00:00:00 2001 From: lyeoh Date: Mon, 18 Oct 2021 13:30:07 -0500 Subject: Delay changing audible status Closes #6168 --- qutebrowser/mainwindow/tabbedbrowser.py | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index e081284ee..2bff8508d 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -24,6 +24,7 @@ import functools import weakref import datetime import dataclasses +import time from typing import ( Any, Deque, List, Mapping, MutableMapping, MutableSequence, Optional, Tuple) @@ -33,6 +34,7 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl from qutebrowser.config import config from qutebrowser.keyinput import modeman from qutebrowser.mainwindow import tabwidget, mainwindow +from qutebrowser.misc import throttle from qutebrowser.browser import signalfilter, browsertab, history from qutebrowser.utils import (log, usertypes, utils, qtutils, objreg, urlutils, message, jinja, version) @@ -219,6 +221,11 @@ class TabbedBrowser(QWidget): self.cur_fullscreen_requested.connect(self.widget.tabBar().maybe_hide) self.widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) + # WORKAROUND for recentlyAudibleChanged being emitted without delay + # from the moment that the audio is paused + self._on_audio_changed_throttle = throttle.Throttle( + self._on_audio_changed, 2000, parent=self) + # load_finished instead of load_started as WORKAROUND for # https://bugreports.qt.io/browse/QTBUG-65223 self.cur_load_finished.connect(self._leave_modes_on_load) @@ -363,7 +370,7 @@ class TabbedBrowser(QWidget): tab.audio.muted_changed.connect( functools.partial(self._on_audio_changed, tab)) tab.audio.recently_audible_changed.connect( - functools.partial(self._on_audio_changed, tab)) + functools.partial(self._delayed_recently_audible_changed, tab)) tab.new_tab_requested.connect(self.tabopen) if not self.is_private: tab.history_item_triggered.connect( @@ -912,6 +919,17 @@ class TabbedBrowser(QWidget): self.widget.update_tab_favicon(tab) self.widget.update_tab_title(idx) + def _delayed_recently_audible_changed(self, tab, _muted): + if tab.audio.is_recently_audible(): + # Cancel and ignore any pending call to remove audible status [A] + self._on_audio_changed_throttle.cancel() + self._on_audio_changed_throttle._pending_call = None + self._on_audio_changed_throttle._last_call_ms = None + else: + # Fake a call, to delay the upcoming call + self._on_audio_changed_throttle._last_call_ms = int(time.monotonic() * 1000) + self._on_audio_changed_throttle(tab, _muted) + def _on_audio_changed(self, tab, _muted): """Update audio field in tab when mute or recentlyAudible changed.""" try: -- cgit v1.2.3-54-g00ecf From a3af87958ed4223405127c38fc3877b69d8bf0ca Mon Sep 17 00:00:00 2001 From: lyeoh Date: Tue, 19 Oct 2021 17:39:52 -0500 Subject: Add keyword for time delay argument --- qutebrowser/mainwindow/tabbedbrowser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 2bff8508d..72403b49b 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -224,7 +224,7 @@ class TabbedBrowser(QWidget): # WORKAROUND for recentlyAudibleChanged being emitted without delay # from the moment that the audio is paused self._on_audio_changed_throttle = throttle.Throttle( - self._on_audio_changed, 2000, parent=self) + self._on_audio_changed, delay_ms=2000, parent=self) # load_finished instead of load_started as WORKAROUND for # https://bugreports.qt.io/browse/QTBUG-65223 -- cgit v1.2.3-54-g00ecf From d0e7732fccf0999dd3fb65a7d59fa72b0161dacd Mon Sep 17 00:00:00 2001 From: lyeoh Date: Tue, 19 Oct 2021 17:56:03 -0500 Subject: Add comment about intended 2s delay --- qutebrowser/mainwindow/tabbedbrowser.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 72403b49b..cf5671f16 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -221,8 +221,10 @@ class TabbedBrowser(QWidget): self.cur_fullscreen_requested.connect(self.widget.tabBar().maybe_hide) self.widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - # WORKAROUND for recentlyAudibleChanged being emitted without delay - # from the moment that the audio is paused + # Throttle calls to _on_audio_changed as WORKAROUND for recentlyAudibleChanged + # being emitted without delay from the moment that audio is dropped. + # delay_ms=2000 implements the intended two-second delay, specified at + # https://doc.qt.io/qt-5/qwebenginepage.html#recentlyAudibleChanged self._on_audio_changed_throttle = throttle.Throttle( self._on_audio_changed, delay_ms=2000, parent=self) -- cgit v1.2.3-54-g00ecf From 928840acdc97e96b0f2d219cf4d44b6b18b41e42 Mon Sep 17 00:00:00 2001 From: lyeoh Date: Thu, 28 Oct 2021 08:40:38 -0500 Subject: Refactor _delayed_recently_audible_changed - to use a generic QTimer instead of a Throttle - to work with multiple tabs intermittently dropping audio --- qutebrowser/mainwindow/tabbedbrowser.py | 39 ++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index cf5671f16..24a33de2b 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -24,7 +24,6 @@ import functools import weakref import datetime import dataclasses -import time from typing import ( Any, Deque, List, Mapping, MutableMapping, MutableSequence, Optional, Tuple) @@ -34,7 +33,6 @@ from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl from qutebrowser.config import config from qutebrowser.keyinput import modeman from qutebrowser.mainwindow import tabwidget, mainwindow -from qutebrowser.misc import throttle from qutebrowser.browser import signalfilter, browsertab, history from qutebrowser.utils import (log, usertypes, utils, qtutils, objreg, urlutils, message, jinja, version) @@ -221,12 +219,7 @@ class TabbedBrowser(QWidget): self.cur_fullscreen_requested.connect(self.widget.tabBar().maybe_hide) self.widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - # Throttle calls to _on_audio_changed as WORKAROUND for recentlyAudibleChanged - # being emitted without delay from the moment that audio is dropped. - # delay_ms=2000 implements the intended two-second delay, specified at - # https://doc.qt.io/qt-5/qwebenginepage.html#recentlyAudibleChanged - self._on_audio_changed_throttle = throttle.Throttle( - self._on_audio_changed, delay_ms=2000, parent=self) + self._audio_muted_timers: Mapping[QWidget, QTimer] = {} # load_finished instead of load_started as WORKAROUND for # https://bugreports.qt.io/browse/QTBUG-65223 @@ -921,16 +914,32 @@ class TabbedBrowser(QWidget): self.widget.update_tab_favicon(tab) self.widget.update_tab_title(idx) + # Delay calls to _on_audio_changed as WORKAROUND for recentlyAudibleChanged being + # emitted without delay from the moment that audio is dropped. def _delayed_recently_audible_changed(self, tab, _muted): + # implements the intended two-second delay, specified at + # https://doc.qt.io/qt-5/qwebenginepage.html#recentlyAudibleChanged + delay_ms = 2000 + # Get this tab's timer if available + timer = self._audio_muted_timers.pop(tab, None) + # Stop any active timer and immediately display [A] if tab is audible, + # otherwise start a timer to update audio field if tab.audio.is_recently_audible(): - # Cancel and ignore any pending call to remove audible status [A] - self._on_audio_changed_throttle.cancel() - self._on_audio_changed_throttle._pending_call = None - self._on_audio_changed_throttle._last_call_ms = None + if timer and timer.isActive(): + timer.stop() + self._on_audio_changed(tab, _muted) else: - # Fake a call, to delay the upcoming call - self._on_audio_changed_throttle._last_call_ms = int(time.monotonic() * 1000) - self._on_audio_changed_throttle(tab, _muted) + # Ignore all subsequent calls while the tab is muted with an active timer + if timer and timer.isActive(): + self._audio_muted_timers[tab] = timer + return + new_timer = QTimer(self) + new_timer.setSingleShot(True) + new_timer.timeout.connect( + functools.partial(self._on_audio_changed, tab, _muted)) + # Store timer in case audio starts within the delay time window + self._audio_muted_timers[tab] = new_timer + new_timer.start(delay_ms) def _on_audio_changed(self, tab, _muted): """Update audio field in tab when mute or recentlyAudible changed.""" -- cgit v1.2.3-54-g00ecf From 07a334b3da70d3bbf788a972e36a767403f2f7b8 Mon Sep 17 00:00:00 2001 From: lyeoh Date: Thu, 28 Oct 2021 09:02:14 -0500 Subject: Fix type hint --- qutebrowser/mainwindow/tabbedbrowser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 24a33de2b..58f0f2397 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -25,7 +25,7 @@ import weakref import datetime import dataclasses from typing import ( - Any, Deque, List, Mapping, MutableMapping, MutableSequence, Optional, Tuple) + Any, Deque, Dict, List, Mapping, MutableMapping, MutableSequence, Optional, Tuple) from PyQt5.QtWidgets import QSizePolicy, QWidget, QApplication from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl @@ -219,7 +219,7 @@ class TabbedBrowser(QWidget): self.cur_fullscreen_requested.connect(self.widget.tabBar().maybe_hide) self.widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - self._audio_muted_timers: Mapping[QWidget, QTimer] = {} + self._audio_muted_timers: Dict[QWidget, QTimer] = {} # load_finished instead of load_started as WORKAROUND for # https://bugreports.qt.io/browse/QTBUG-65223 -- cgit v1.2.3-54-g00ecf From 73908d46f6abc6ee9459462681a8ae4523ea0959 Mon Sep 17 00:00:00 2001 From: lyeoh Date: Fri, 29 Oct 2021 12:21:13 -0500 Subject: Port logic and timer to WebEngineAudio --- qutebrowser/browser/webengine/webenginetab.py | 30 ++++++++++++++++++++++-- qutebrowser/mainwindow/tabbedbrowser.py | 33 ++------------------------- 2 files changed, 30 insertions(+), 33 deletions(-) diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index ace23d14a..4434d5302 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -26,7 +26,8 @@ import re import html as html_utils from typing import cast, Union, Optional -from PyQt5.QtCore import pyqtSignal, pyqtSlot, Qt, QPoint, QPointF, QUrl, QObject +from PyQt5.QtCore import (pyqtSignal, pyqtSlot, Qt, QPoint, QPointF, QTimer, QUrl, + QObject) from PyQt5.QtNetwork import QAuthenticator from PyQt5.QtWidgets import QWidget from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineScript, QWebEngineHistory @@ -797,13 +798,38 @@ class WebEngineAudio(browsertab.AbstractAudio): super().__init__(tab, parent) self._overridden = False + # Implements the intended two-second delay specified at + # https://doc.qt.io/qt-5/qwebenginepage.html#recentlyAudibleChanged + delay_ms = 2000 + self._audio_muted_timer = QTimer(self) + self._audio_muted_timer.setSingleShot(True) + self._audio_muted_timer.setInterval(delay_ms) + def _connect_signals(self): page = self._widget.page() page.audioMutedChanged.connect(self.muted_changed) - page.recentlyAudibleChanged.connect(self.recently_audible_changed) + page.recentlyAudibleChanged.connect(self._delayed_recently_audible_changed) self._tab.url_changed.connect(self._on_url_changed) config.instance.changed.connect(self._on_config_changed) + # WORKAROUND for recentlyAudibleChanged being emitted without delay from the moment + # that audio is dropped. + def _delayed_recently_audible_changed(self, recently_audible): + timer = self._audio_muted_timer + # Stop any active timer and immediately display [A] if tab is audible, + # otherwise start a timer to update audio field + if recently_audible: + if timer.isActive(): + timer.stop() + self.recently_audible_changed.emit(recently_audible) + else: + # Ignore all subsequent calls while the tab is muted with an active timer + if timer.isActive(): + return + timer.timeout.connect( + functools.partial(self.recently_audible_changed.emit, recently_audible)) + timer.start() + def set_muted(self, muted: bool, override: bool = False) -> None: was_muted = self.is_muted() self._overridden = override diff --git a/qutebrowser/mainwindow/tabbedbrowser.py b/qutebrowser/mainwindow/tabbedbrowser.py index 58f0f2397..e081284ee 100644 --- a/qutebrowser/mainwindow/tabbedbrowser.py +++ b/qutebrowser/mainwindow/tabbedbrowser.py @@ -25,7 +25,7 @@ import weakref import datetime import dataclasses from typing import ( - Any, Deque, Dict, List, Mapping, MutableMapping, MutableSequence, Optional, Tuple) + Any, Deque, List, Mapping, MutableMapping, MutableSequence, Optional, Tuple) from PyQt5.QtWidgets import QSizePolicy, QWidget, QApplication from PyQt5.QtCore import pyqtSignal, pyqtSlot, QTimer, QUrl @@ -219,8 +219,6 @@ class TabbedBrowser(QWidget): self.cur_fullscreen_requested.connect(self.widget.tabBar().maybe_hide) self.widget.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) - self._audio_muted_timers: Dict[QWidget, QTimer] = {} - # load_finished instead of load_started as WORKAROUND for # https://bugreports.qt.io/browse/QTBUG-65223 self.cur_load_finished.connect(self._leave_modes_on_load) @@ -365,7 +363,7 @@ class TabbedBrowser(QWidget): tab.audio.muted_changed.connect( functools.partial(self._on_audio_changed, tab)) tab.audio.recently_audible_changed.connect( - functools.partial(self._delayed_recently_audible_changed, tab)) + functools.partial(self._on_audio_changed, tab)) tab.new_tab_requested.connect(self.tabopen) if not self.is_private: tab.history_item_triggered.connect( @@ -914,33 +912,6 @@ class TabbedBrowser(QWidget): self.widget.update_tab_favicon(tab) self.widget.update_tab_title(idx) - # Delay calls to _on_audio_changed as WORKAROUND for recentlyAudibleChanged being - # emitted without delay from the moment that audio is dropped. - def _delayed_recently_audible_changed(self, tab, _muted): - # implements the intended two-second delay, specified at - # https://doc.qt.io/qt-5/qwebenginepage.html#recentlyAudibleChanged - delay_ms = 2000 - # Get this tab's timer if available - timer = self._audio_muted_timers.pop(tab, None) - # Stop any active timer and immediately display [A] if tab is audible, - # otherwise start a timer to update audio field - if tab.audio.is_recently_audible(): - if timer and timer.isActive(): - timer.stop() - self._on_audio_changed(tab, _muted) - else: - # Ignore all subsequent calls while the tab is muted with an active timer - if timer and timer.isActive(): - self._audio_muted_timers[tab] = timer - return - new_timer = QTimer(self) - new_timer.setSingleShot(True) - new_timer.timeout.connect( - functools.partial(self._on_audio_changed, tab, _muted)) - # Store timer in case audio starts within the delay time window - self._audio_muted_timers[tab] = new_timer - new_timer.start(delay_ms) - def _on_audio_changed(self, tab, _muted): """Update audio field in tab when mute or recentlyAudible changed.""" try: -- cgit v1.2.3-54-g00ecf