Whenever audio is playing in windows 10, whether it is from Spotify, Firefox, or a game. When you turn the volume, windows has a thing in the corner that says the song artist, title, and what app is playing like the photo below (sometimes it only says what app is playing sound if a game is playing the sound)
I want to somehow get that data with python. My end goal, is to mute an application if it is playing something I don't like, such as an advertisement.
Turns out this is possible without a workaround and by accessing this info directly using the Windows Runtime API (winrt
).
All code shown uses Python 3 and the winrt
library installed via pip
The following code allows for you to collect a dictionary of the media information available to Windows using the winrt wrapper for the Windows Runtime API. It does not rely on a window's title/application name changing as in the other answers here.
import asyncio
from winrt.windows.media.control import \
GlobalSystemMediaTransportControlsSessionManager as MediaManager
async def get_media_info():
sessions = await MediaManager.request_async()
# This source_app_user_model_id check and if statement is optional
# Use it if you want to only get a certain player/program's media
# (e.g. only chrome.exe's media not any other program's).
# To get the ID, use a breakpoint() to run sessions.get_current_session()
# while the media you want to get is playing.
# Then set TARGET_ID to the string this call returns.
current_session = sessions.get_current_session()
if current_session: # there needs to be a media session running
if current_session.source_app_user_model_id == TARGET_ID:
info = await current_session.try_get_media_properties_async()
# song_attr[0] != '_' ignores system attributes
info_dict = {song_attr: info.__getattribute__(song_attr) for song_attr in dir(info) if song_attr[0] != '_'}
# converts winrt vector to list
info_dict['genres'] = list(info_dict['genres'])
return info_dict
# It could be possible to select a program from a list of current
# available ones. I just haven't implemented this here for my use case.
# See references for more information.
raise Exception('TARGET_PROGRAM is not the current media session')
if __name__ == '__main__':
current_media_info = asyncio.run(get_media_info())
current_media_info
will be a dictionary in the following format and information can then be accessed as required within the program:
{
'album_artist': str,
'album_title': str,
'album_track_count': int,
'artist': str,
'genres': list,
'playback_type': int,
'subtitle': str,
'thumbnail':
<_winrt_Windows_Storage_Streams.IRandomAccessStreamReference object at ?>,
'title': str,
'track_number': int,
}
As the OP says that their end goal is to control media, this should be possible with the same libraries. See here for more information possibly (I didn't need this in my case):
await current_session.try_pause_async()
)It is in fact possible to also 'scrape' the album art/media thumbnail (displayed on the right in the OP's screenshot) of the media currently playing (although the OP didn't ask for this but someone might want to do it):
from winrt.windows.storage.streams import \
DataReader, Buffer, InputStreamOptions
async def read_stream_into_buffer(stream_ref, buffer):
readable_stream = await stream_ref.open_read_async()
readable_stream.read_async(buffer, buffer.capacity, InputStreamOptions.READ_AHEAD)
# create the current_media_info dict with the earlier code first
thumb_stream_ref = current_media_info['thumbnail']
# 5MB (5 million byte) buffer - thumbnail unlikely to be larger
thumb_read_buffer = Buffer(5000000)
# copies data from data stream reference into buffer created above
asyncio.run(read_stream_into_buffer(thumb_stream_ref, thumb_read_buffer))
# reads data (as bytes) from buffer
buffer_reader = DataReader.from_buffer(thumb_read_buffer)
byte_buffer = buffer_reader.read_bytes(thumb_read_buffer.length)
with open('media_thumb.jpg', 'wb+') as fobj:
fobj.write(bytearray(byte_buffer))
This will save a media_thumb.jpg
to the current working directory (cwd) which can then be used elsewhere for whatever.
Please note that I haven't tested or tried this and is merely a pointer for anyone who may want to experiment:
sessions.get_sessions()
above)As opposed to current use of
sessions.get_current_session()
above)I am getting the titles of the windows to get the song information. Usually, the application name is displayed in the title, but when it is playing a song, the song name is shown. Here is a function that returns a list of all the window titles.
from __future__ import print_function
import ctypes
def get_titles():
EnumWindows = ctypes.windll.user32.EnumWindows
EnumWindowsProc = ctypes.WINFUNCTYPE(ctypes.c_bool, ctypes.POINTER(ctypes.c_int), ctypes.POINTER(ctypes.c_int))
GetWindowText = ctypes.windll.user32.GetWindowTextW
GetWindowTextLength = ctypes.windll.user32.GetWindowTextLengthW
IsWindowVisible = ctypes.windll.user32.IsWindowVisible
titles = []
def foreach_window(hwnd, lParam):
if IsWindowVisible(hwnd):
length = GetWindowTextLength(hwnd)
buff = ctypes.create_unicode_buffer(length + 1)
GetWindowText(hwnd, buff, length + 1)
titles.append(buff.value)
return True
EnumWindows(EnumWindowsProc(foreach_window), 0)
return titles
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With