Skip to content

feat(media): add open_media_source callback option#684

Open
stylebending wants to merge 5 commits intoamnweb:mainfrom
stylebending:add-media-source-callback
Open

feat(media): add open_media_source callback option#684
stylebending wants to merge 5 commits intoamnweb:mainfrom
stylebending:add-media-source-callback

Conversation

@stylebending
Copy link

@stylebending stylebending commented Feb 6, 2026

Description

This PR addresses Discussion #683 to add a callback option to the Media Widget to open/activate the application currently playing media by clicking the widget.

Changes

  • docs/widgets/(Widget)-Media.md: Added open_media_source to callback documentation.
  • src/core/utils/win32/aumid.py: Added activate_app_by_aumid(aumid: str) utility function. This uses EnumWindows to find the window handle (HWND) associated with a given App User Model ID (AUMID) and brings it to the foreground.
  • src/core/widgets/yasb/media.py:
    • Imported activate_app_by_aumid.
    • Added _open_media_source callback method.
    • Registered open_media_source as a valid callback string.

Configuration

Users can now configure this action in their config.yaml:

widgets:
  media:
    options:
      callbacks:
        on_left: "toggle_media_menu"
        on_middle: "toggle_play_pause"
        on_right: "open_media_source"

Rationale

While source_apps.py exists for mapping AUMIDs to display names, it does not provide window handles. The new activate_app_by_aumid function fills this gap by dynamically finding the window owned by the media source's AUMID, for activating any media application.

If any tweaks are needed, or if I'm taking the wrong approach, I would love to hear the feedback to help out!

@Video-Nomad
Copy link
Contributor

Video-Nomad commented Feb 6, 2026

Thanks for the PR! Quickly testing it and it only works for my browser as an audio source. Spotify app and YouTube Music (third party) app does not react to this callback.

Edit: it works when the application is on another workspace (komorebi or glazewm) but not when it's minimized to taskbar/tray. I assumed this callback will also open the app, but it just changes the focus for me.

@stylebending
Copy link
Author

it works when the application is on another workspace (komorebi or glazewm) but not when it's minimized to taskbar/tray. I assumed this callback will also open the app, but it just changes the focus for me.

Thanks for testing and the feedback, much appreciated! I've pushed a fix to handle minimized windows and also to focus the source window to make sure it is able to receive input immediately. This should be ready for another review.

@Video-Nomad
Copy link
Contributor

Video-Nomad commented Feb 8, 2026

So tried testing multiple apps and some apps are working properly now like web browsers (Edge, Vivaldi), Spotify, Windows Media Player (the modern one) and various PWAs I have.
What's not working for some reason is the unofficial YouTube Music app, MPC-HC fork and telegram when it's playing a voice message for example, even though they report their media information to the media widget...
Might be something very specific about those, but other than that seems like the PR works as expected.

@amnweb
Copy link
Owner

amnweb commented Feb 8, 2026

def restore_window(hwnd: int) -> None:
if not send_sys_command(hwnd, win32con.SC_RESTORE):
try:
u32.ShowWindowAsync(int(hwnd), win32con.SW_RESTORE)
except Exception:
u32.ShowWindow(hwnd, win32con.SW_RESTORE)
def maximize_window(hwnd: int) -> None:
if not send_sys_command(hwnd, win32con.SC_MAXIMIZE):
try:
u32.ShowWindowAsync(int(hwnd), win32con.SW_MAXIMIZE)
except Exception:
u32.ShowWindow(hwnd, win32con.SW_MAXIMIZE)
def minimize_window(hwnd: int) -> None:
if not send_sys_command(hwnd, win32con.SC_MINIMIZE):
try:
u32.ShowWindowAsync(int(hwnd), win32con.SW_MINIMIZE)
except Exception:
u32.ShowWindow(hwnd, win32con.SW_FORCEMINIMIZE)
def show_window(hwnd: int) -> None:
try:
u32.ShowWindowAsync(int(hwnd), win32con.SW_SHOW)
except Exception:
u32.ShowWindow(hwnd, win32con.SW_SHOW)

@stylebending what about these functions? Or do you still need to define restore if minimized inside AUMID?

@stylebending
Copy link
Author

Updated! I'm now using restore_window and force_foreground_focus from window_actions.py to handle the restoration logic, thanks for the suggestion @amnweb! Added a fallback that checks process names (via source_apps.py mapping) if the exact AUMID match fails. This should fix activation for apps like unofficial YouTube Music clients and MPC-HC, I don't have them myself it would be awesome if whenever you have the spare time you could test those again @Video-Nomad Thanks again for helping out!

Tested with GlazeWM, Brave, Chrome, Firefox, Spotify, Windows Media Player, seems to be working fine for all of them.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants