streamlink / streamlink-twitch-gui

A multi platform Twitch.tv browser for Streamlink
https://streamlink.github.io/streamlink-twitch-gui/
MIT License
2.67k stars 201 forks source link

Launched streams and ToastNotifications created by Twitch GUI create unusual windows wait cursor behaviour. #952

Open Sneakpeakcss opened 1 year ago

Sneakpeakcss commented 1 year ago

Checklist

Streamlink Twitch GUI version

v2.2.0

Streamlink version

5.1.2

Operating system, environment and configuration details

Windows 10 21H2

Description

This started happening after one of the updates few years ago, i've mentioned it last year on gitter and at that point i just blamed my old bloated system, but lately after doing a clean Windows10 installation and straight up installing only Streamlink + Streamlink Twitch GUI the problem still persists.

1)

When Twitch GUI creates a Toast Notification, windows wait cursor instantly activates and only dissapears after the notification either dissapears or is hidden manually. This behaviour only happens with Twitch GUI, any other software that sends notifications doesn't show any loading.

Twitch GUI v2.2.0:

https://user-images.githubusercontent.com/77424331/213590797-4122d711-8195-4a8c-b3d5-f667b81ce926.mp4

Debug Log ToastNotification GUI v2.2.0.log

The last version that doesn't act this way for me is v1.11.0 Since this version is so old it's impossible to test it on an automatically activated notification, however the test button still manages to show the proper behaviour.

Twitch GUI v1.11.0:

https://user-images.githubusercontent.com/77424331/213591724-17d2602d-4337-48d5-b596-e11412c10ca2.mp4

Debug Log ToastNotification GUI v1.11.0.log

And every single release starting from v1.12.0 that Upgraded snoretoast (Windows notification provider) to 0.7.0 presents the same problem.

Twitch GUI v1.12.0:

https://user-images.githubusercontent.com/77424331/213593682-c808292a-cb48-4cc9-a412-6f38aa93e677.mp4

Debug Log ToastNotification GUI v1.12.0.log

    2)

Similar behaviour happens when the player is launched by Twitch GUI, but in this case it sometimes creates an endless loop that never stops unless i manually 'unstuck' it, for example by opening a random text file.

I mainly use MPC-HC, but the same happens in default VLC installation, though i wasn't able to catch it being stuck in an endless loop, the prolonged loading on mouse cursor is still present:

Twitch GUI v2.2.0 + MPC-HC:

https://user-images.githubusercontent.com/77424331/213596242-f399408e-a88d-4df4-9948-c512d287366e.mp4

GUI v2.2.0 MPC-HC test.log

Twitch GUI v2.2.0 + VLC:

https://user-images.githubusercontent.com/77424331/213596277-80deaad4-56b4-40c5-9348-4a5e437e15d3.mp4

GUI v2.2.0 VLC test.log

While the loading might rarely stop very quickly by itself it usually takes a lot of time (as shown on the video examples), VLC seems to have less delay on that, but it still happens with it, it also stops the moment Twitch GUI window pops-out after closing the player manually. And once again this only happens when streams are launched by Twtich GUI, doing it either manually with streamlink or even opening them through Chatty doesn't even show it for a second, for example:

Launching streams manually using Streamlink + MPC-HC / VLC:

https://user-images.githubusercontent.com/77424331/213597210-6b194222-f038-4c22-a59f-dffa07258479.mp4

I couln't find any logs for MPC-HC, but i've managed to take two from VLC while launching a stream with Twitch GUI and PowerShell, however it doesn't seem to be there anything that would point at why this happens at all. TwitchGUI Test VLC Log.txt Streamlink Test VLC Log.txt

 

I've tried using both installer and portable versions of Twitch GUI, installing them on different SSDs / HDD or opening the application as administrator, different setting configurations in GUI or launching it with --disable-gpu… And nothing helped.

Nothing unusual shows in task manager, different streamlink versions never had any influence on this. I very vaguely remember long ago using Process Monitor to see if there's any difference in how snoretoast.exe behaved between v1.11.0 - v1.12.0, and there being something about how the older version cached something locally(?), but i'm not sure if any of that would be connected to either (player / toast) problems, and at the same time it's impossible to check if the v1.11.0 version has the same problem while launching streams.

At this point i'm wondering if this is specific to my hardware configuration since i can only assume that someone else at this point would be insanely bothered by random loading cursor (or maybe notifications are turned off by default(?) and not many people use that option?) I'm left with joking to myself that if it wasn't for Twitch GUI i wouldn't even know about wait cursor since i barely notice it outside of this scenario, so at least it has been somewhat (not)amusingly educational at my expense.

Debug log

No response

bastimeyer commented 1 year ago

First off, on WIndows, Streamlink Twitch GUI needs a helper utility called SnoreToast in order to display native notifications. Chromium still doesn't have support for native notifications, unlike on Linux (freedesktop notifications via DBUS) or macOS. This means that whenever a notification needs to be shown, snoretoast has to be executed and an action is performed according to its exit code. This isn't a perfect solution. There's also a named pipe which can be read from for other callbacks, but that's not being used or even relevant.

The last version that doesn't act this way for me is v1.11.0

The v1.12.0 release of Streamlink Twitch GUI bumped NW.js from 0.45.5 to 0.52.2. This included a bump of Chromium from 81.x to 89.x and NodeJS from v14.0.0 to v15.12.0. The change of the bundled snoretoast version in that release is irrelevant.

I don't use Windows, but the reason for the spinning/loading/hourglass cursor is how Windows handles process startups and GUI initializations. Windows basically waits until there is something for the user to see and it does that by showing a different cursor during that time.

The cursor replacement (called feedback mode) can be disabled via the low level Windows APIs (STARTUPINFO / STARTF_FORCEOFFFEEDBACK), but this stuff is managed by NW.js / NodeJS, and I don't have access to any of this from the application code and the public NodeJS JS APIs that are being used by the application code, namely child_process.spawn(cmd, args, options).

There was an issue in an old NW.js release in 2016 where the application itself was showing a spinning mouse cursor, but it's unrelated to this issue.

I don't know much about the NW.js and NodeJS internals, but something must've changed in regards to how child process spawning is implemented and how CreateProcess in the win32 API is called. The STARTF_FORCEOFFFEEDBACK bitmask needs to be applied to the dwFlags parameter value.

Spawning the child processes in detached mode unfortunately doesn't help, I just tested that in my Win10 VM.

This issue needs to be reported upstream on the NW.js issue tracker and an NW.js reproduction/demonstration app needs to be created.


As a small workaround, I could enable "Chromium Rich Notifications" on all versions of Windows, not just 7 (which btw will be dropped soon), which are notifications that are rendered by Chromium itself, so no helper process is needed. That wouldn't be ideal though, not just because those notifications don't follow the system theme, but also because of how the notification type selection and its order is implemented. If the order doesn't get changed, then rich notifications would need to be selected explitly, and if the order gets changed, then people who have chosen the automatic selection will use the non-native notifications by default.

bastimeyer commented 1 year ago

Apparently, setting shell: true on the spawn options prevents the spinning cursor, and most likely any wrapper script would work as well, but both solutions are very problematic on Windows due to how shell tokenization works, which is insanely stupid. Since notifications contain user-generated content, I don't feel confident at all with this.

bastimeyer commented 1 year ago

NodeJS's I/O is done via libuv, and here's the Windows implementation of the uv_spawn function and the git-blame of its dwFlags, which indicates no changes: https://github.com/libuv/libuv/blame/v1.44.2/src/win/process.c#L1060 STARTF_FORCEOFFFEEDBACK was never set here, so I'm not sure what changed.

Here's some of the call stack: https://github.com/nwjs/node/blob/nw71/lib/internal/child_process.js#L395 https://github.com/nwjs/node/blob/nw71/lib/internal/child_process.js#L266 https://github.com/nwjs/node/blob/nw71/lib/internal/child_process.js#L44 https://github.com/nwjs/node/blob/nw71/src/process_wrap.cc#L144 https://github.com/nwjs/node/blob/nw71/src/process_wrap.cc#L264

bastimeyer commented 1 year ago

I'm currently having a look at refactoring the providers of the NotificationService. There's a solution for the mouse cursor problem by using Windows native notifications via the Chromium API directly, so that executing snoretoast is not necessary.

Chromium has added support for native "ActionCenter" notifications several years ago, but similar to notifications via snoretoast, only applications that have a shortcut installed in the system's start menu with an attached AppID are allowed to send ActionCenter notifications. If such a shortcut does not exist, then a "rich" notification gets shown by Chromium, which is rendered by the browser itself.

The snoretoast provider has a setup step implemented which installs the shortcut upon first execution via snoretoast.exe -install "name" "path-to-app.exe" "app-id". Another solution is creating the shortcut in the Twitch GUI's Windows installer, but that would only be a one-time action, and that could lead to the user not being able to see ActionCenter notifications once the shortcut got removed or replaced. And users which don't use the installer would also not be able to see ActionCenter notifications. Those shortcuts unfortunately can't be created via regular methods, as those don't support setting the AppID property. This is only possible by using certain Windows APIs.

Chrome on Windows faces same problem. Its own installer (and/or internal update mechanism - not sure) creates a shortcut at %ProgramData%\Microsoft\Windows\Start Menu\Programs\Google Chrome.lnk with its embedded AppID. Without this shortcut, ActionCenter notifications can't be sent. Quick DOM API example:

await window.Notification.requestPermission();
new window.Notification("title");

Streamlink Twitch GUI already implements notifications via the Chromium API, but this is only enabled for macOS and Linux as the native provider, and for Windows 7 as the rich provider.

If the shortcut could be created with the correct AppID, then the rich provider could be removed in favor of a single native provider (or something similar). Unfortunately, NW.js doesn't register the right AppID that is defined in the app's package.json manifest, which is streamlink-twitch-gui (or Streamlink Twitch GUI). The shortcut could still be created by running snoretoast.exe -install ... if the right AppID could be figured out. Then ActionCenter notifications can be shown via the Chromium or DOM APIs.