Repository: qutebrowser/qutebrowser

Issue:
Throttle/debounce/delay audible status ([A]) in tabs
This used to be done by Qt, but isn't anymore in more recent versions, so `[A]` can get added/removed somewhat erratically. We should delay it (maybe using `misc.throttle`?) so it's a bit smoother.

(I thought there was an issue about this already, but I don't think so?)


Discussion:
In addition to adding a delay, is it possible to replace `[A]` with an equivalent length of blank space to eliminate the sliding effect each time `[A]` appears or disappears? 
> In addition to adding a delay, is it possible to replace `[A]` with an equivalent length of blank space to eliminate the sliding effect each time `[A]` appears or disappears?

I believe that has been discussed before and some people didn't want that because it would make all the other tab titles have pointless leading whitespace, which seems sensible. And if you did it just for tabs that had ever been audible you may still be annoyed at it for tabs that only made noises occasionally. So maybe it would be equivalent whitespace for a only a while which would mean another timer. Possibly we could make it get the audibility prefixes from the custom function but that should probably wait until tabs are part of the extension API.
Also see #4089 which provides a hack to set those texts.
> > In addition to adding a delay, is it possible to replace `[A]` with an equivalent length of blank space to eliminate the sliding effect each time `[A]` appears or disappears?
> 
> I believe that has been discussed before and some people didn't want that because it would make all the other tab titles have pointless leading whitespace, which seems sensible.

Hey, What about displaying the [A] at the right edge of the Tab, so basically how it is done with the Speaker logo in Firefox and Chromium? For me, this does seem like a good idea, but i don't know how much programming-effort this would add.


@laalsaas You can do that by changing the `tabs.title.format` setting already.
Qt bug? We check `page.recentlyAudible()` which is "whether audio was _recently_ played". There's some stuff in Chromium [here](https://chromium.googlesource.com/chromium/src/+/master/chrome/browser/ui/recently_audible_helper.h) about setting the recently audible status based on a timer.
I guess we could open a QtWebEngine issue as well, but we'd still need a workaround until a fix actually arrives and we drop older versions. Given that a fix should be relatively simple, I don't really see the point of reporting this upstream.

(edit: Other than, perhaps, for reducing the overhead it takes - can't hurt to report it I guess, but I doubt this will be a high-priority bug)
Firstly **Quickfix**. Put the audio segment at the end (probably invisible, but won't cause a shift when the state changes. 
`config.set('tabs.title.format', '{index}:{current_title}:{audio}')`

Note: You can also move the index numbers if you never actually used them. 

This ticket was really difficult to find given a search for "A" doesn't work so well. :) So I'll add some keywords for others:
Youtube title flickering when playing "[A]" "A" audio AUDIBLE_STRING

Finally I noticed that there is a green bar to far left of all tab titles indicating the page has loaded and secure. Is it possible to just change that to blue when the page is secure and audio is playing? It's always there so wouldn't lead to shifts in the title and I think changing that would be much cleaner than an [A]. Just a thought. I can get not wanting to mess with a security indicator, but green is not what I care about, red and yellow are.




I tried out a  (seriously non-prod ready and even dangerous code) bodge implimentation of the above idea:
qutebrowser/mainwindow/tabwidget.py:147 (inside update_tab_title)
```py
        if fields['audio'] != '':
            self.set_tab_indicator_color(idx, QColor('pink'))
        else:
            self.set_tab_indicator_color(idx, QColor('green'))
```

Despite how bad the code is, I really love the effect. Even though it still changes when the audio is quite, this allows me to instantly spot which tab is my music or video from the tab with the annoying ad that started playing. This was always an issue for me in Chrome or Firefox when I had a lot of tabs. There would normally be one tab playing something I wanted and one playing something I didn't ask for and finding them still not super fast. Because it's a thin strip it isn't distracting when you are not looking for it and actually really nice when you are.

This screenshot doesn't show you the on off syncronisation, but shows a static example:
![audio-indicator](https://user-images.githubusercontent.com/71481/126903706-5587db5e-9e9d-438f-9fe2-84f3402fc69f.png)




Resolving patch:
diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py
--- a/qutebrowser/browser/webengine/webenginetab.py
+++ b/qutebrowser/browser/webengine/webenginetab.py
@@ -26,7 +26,8 @@
 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 @@ def __init__(self, tab, parent=None):
         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


Test patch: