Closed yw4z closed 1 year ago
Yes, it is possible to get it directly via Powershell. I created some sample code (My PowerShell is a bit rusty):
# Importing GlobalSystemMediaTransportControlsSessionManager ( Source: https://superuser.com/a/1342416 )
[Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager,Windows.Media.Control,ContentType=WindowsRuntime] | Out-Null
Add-Type -AssemblyName System.Runtime.WindowsRuntime
# Adding Awaitable ( Source: https://superuser.com/a/1342416 )
$asTaskGeneric = ([System.WindowsRuntimeSystemExtensions].GetMethods() | ? { $_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1' })[0]
Function Await($WinRtTask, $ResultType) {
$asTask = $asTaskGeneric.MakeGenericMethod($ResultType)
$netTask = $asTask.Invoke($null, @($WinRtTask))
$netTask.Wait(-1) | Out-Null
$netTask.Result
}
# Getting the session manager ( Matches code: https://github.com/DubyaDude/WindowsMediaController/blob/61ba73aad7632bdda4ba415d9391882287de656e/WindowsMediaController/Main.cs#L63 )
$WindowsSessionManager = Await ([Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager]::RequestAsync()) ([Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager])
# Getting current sessions ( Matches code: https://github.com/DubyaDude/WindowsMediaController/blob/61ba73aad7632bdda4ba415d9391882287de656e/WindowsMediaController/Main.cs#L76 )
$controlSessionList = $WindowsSessionManager.GetSessions()
foreach ($controlSession in $controlSessionList) {
# Getting the session source app ( Matches code: https://github.com/DubyaDude/WindowsMediaController/blob/61ba73aad7632bdda4ba415d9391882287de656e/WindowsMediaController/Main.cs#L172 )
Write-Output $controlSession.SourceAppUserModelId
# Getting the session's playback info ( Matches code: https://github.com/DubyaDude/WindowsMediaController/blob/61ba73aad7632bdda4ba415d9391882287de656e/WindowsMediaController/Main.cs#L180 )
$playbackInfo = $controlSession.GetPlaybackInfo()
Write-Output $playbackInfo.PlaybackStatus
# Getting the session's media properties ( Matches code: https://github.com/DubyaDude/WindowsMediaController/blob/61ba73aad7632bdda4ba415d9391882287de656e/WindowsMediaController/Main.cs#L195 )
$mediaProperties = Await ($controlSession.TryGetMediaPropertiesAsync()) ([Windows.Media.Control.GlobalSystemMediaTransportControlsSessionMediaProperties])
Write-Output $mediaProperties.Title
Write-Output $mediaProperties.Artist
}
With the above code, I got the following output while listening to Spotify:
> ".\MTC_Sample.ps1"
Spotify.exe
Playing
Tokyo Drift (Fast & Furious) - From "The Fast And The Furious: Tokyo Drift" Soundtrack
Teriyaki Boyz
For exacts of what you can get from this, I'd recommend looking at this CS file in this repo: https://github.com/DubyaDude/WindowsMediaController/blob/master/WindowsMediaController/Main.cs And Microsofts documentation that I have linked in the README: https://github.com/DubyaDude/WindowsMediaController/blob/master/README.md#useful-microsoft-documentations
Let me know if you have any other questions :)
Hi, thanks for guide, you helped much i'm not sure but is there a way to implement OnAnyPlaybackStateChanged as background job i created a WPF app in powershell. backround jobs mostly freezing UI as far i see here is preview of my app, still wip tray icons included app
i worked on SystemMediaTransportControls a bit, and dropping result here for future readers
[Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager,Windows.Media.Control,ContentType=WindowsRuntime]|Out-Null
Add-Type -AssemblyName System.Runtime.WindowsRuntime
$asTaskGeneric=([System.WindowsRuntimeSystemExtensions].GetMethods()|?{$_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1'})[0]
Function Await($WinRtTask,$ResultType){
$netTask=($asTaskGeneric.MakeGenericMethod($ResultType)).Invoke($null,@($WinRtTask))
$netTask.Wait(-1)|Out-Null
$netTask.Result
}
$playingstatus=$false
$playinginfo="Play"
$playingArr=$false
$smtc=[Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager]
$smtcArr=@()
$i=-1
foreach($Session in (Await($smtc::RequestAsync())($smtc)).GetSessions()){
$a=@()
$i++
$a+="----------"
$a+=$Session.SourceAppUserModelId
$a+=$Session.GetPlaybackInfo().PlaybackStatus
$prop=Await($Session.TryGetMediaPropertiesAsync())([Windows.Media.Control.GlobalSystemMediaTransportControlsSessionMediaProperties])
$a+=$prop.Artist + " - " + $prop.Title
if($a -contains "Playing"){$playingArr=$i}
$a+="----------"
$smtcArr+=,$a
}
$smtcCount=$smtcArr.Count
write-host "********** $smtcCount Apps in Playback Session"
$smtcArr
if($playingArr -ne $false){
write-host "********** Playing App"
$smtcArr[$playingArr]
}else{
write-host "********** No Playback"
}
it prints apps in session also prints playing app in array for easier selection
********** 2 Apps in Playback Session
----------
MusicBee.exe
Paused
2X2A - Skin
----------
----------
vivaldi.exe
Playing
exocollective - pexØt - Closer
----------
********** Playing App
----------
vivaldi.exe
Playing
exocollective - pexØt - Closer
----------
If there is no playback prints
********** 0 Apps in Playback Session
********** No Playback
Unsure what you mean by background task, but maybe making it more 'event-based' rather then doing a consistent query of session, media, etc.
Using this: https://stackoverflow.com/a/64232782 I was able to get the methods for adding/removing to the events that are used in this project.
Using command:
[Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager].GetEvents() | select Name, *Method, EventHandlerType
I got the output:
Name : CurrentSessionChanged
AddMethod : System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken add_CurrentSessionChanged(Windows.Foundation.TypedEventHandler`2[Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager,Windows.Media.Control.CurrentSessionChangedEventArgs])
RemoveMethod : Void remove_CurrentSessionChanged(System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken)
RaiseMethod :
EventHandlerType : Windows.Foundation.TypedEventHandler`2[Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager,Windows.Media.Control.CurrentSessionChangedEventArgs]
Name : SessionsChanged
AddMethod : System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken add_SessionsChanged(Windows.Foundation.TypedEventHandler`2[Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager,Windows.Media.Control.SessionsChangedEventArgs])
RemoveMethod : Void remove_SessionsChanged(System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken)
RaiseMethod :
EventHandlerType : Windows.Foundation.TypedEventHandler`2[Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager,Windows.Media.Control.SessionsChangedEventArgs]
And running
[Windows.Media.Control.GlobalSystemMediaTransportControlsSession].GetEvents() | select Name, *Method, EventHandlerType
Results in:
Name : MediaPropertiesChanged
AddMethod : System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken add_MediaPropertiesChanged(Windows.Foundation.TypedEventHandler`2[Windows.Media.Control.GlobalSystemMediaTransportControlsSession,Windows.Media.Control.MediaPropertiesChangedEventArgs])
RemoveMethod : Void remove_MediaPropertiesChanged(System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken)
RaiseMethod :
EventHandlerType : Windows.Foundation.TypedEventHandler`2[Windows.Media.Control.GlobalSystemMediaTransportControlsSession,Windows.Media.Control.MediaPropertiesChangedEventArgs]
Name : PlaybackInfoChanged
AddMethod : System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken add_PlaybackInfoChanged(Windows.Foundation.TypedEventHandler`2[Windows.Media.Control.GlobalSystemMediaTransportControlsSession,Windows.Media.Control.PlaybackInfoChangedEventArgs])
RemoveMethod : Void remove_PlaybackInfoChanged(System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken)
RaiseMethod :
EventHandlerType : Windows.Foundation.TypedEventHandler`2[Windows.Media.Control.GlobalSystemMediaTransportControlsSession,Windows.Media.Control.PlaybackInfoChangedEventArgs]
Name : TimelinePropertiesChanged
AddMethod : System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken add_TimelinePropertiesChanged(Windows.Foundation.TypedEventHandler`2[Windows.Media.Control.GlobalSystemMediaTransportControlsSession,Windows.Media.Control.TimelinePropertiesChangedEventArgs])
RemoveMethod : Void remove_TimelinePropertiesChanged(System.Runtime.InteropServices.WindowsRuntime.EventRegistrationToken)
RaiseMethod :
EventHandlerType : Windows.Foundation.TypedEventHandler`2[Windows.Media.Control.GlobalSystemMediaTransportControlsSession,Windows.Media.Control.TimelinePropertiesChangedEventArgs]
Which makes it seems it's possible to get it to be event-based like this project is.
So instead of _WindowsSessionManager.SessionsChanged += ...
it'll be $WindowsSessionManager.add_SessionsChanged(...)
However, I couldn't get the event to work, let me know if you can.
Also, please be aware of issue https://github.com/DubyaDude/WindowsMediaController/issues/6 which seems to affect a few events I'd recommend you have the Sample CMD running alongside your PS version so make sure you're not encountering this bug. If you are, sign out and sign in to windows.
yep i have tried to add event with
Add-Type -AssemblyName System.Runtime.WindowsRuntime
[Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager,Windows.Media.Control,ContentType=WindowsRuntime]|Out-Null
$t=([System.WindowsRuntimeSystemExtensions].GetMethods()|?{$_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1'})[0]
function aw($w,$r){$m=($t.MakeGenericMethod($r)).Invoke($null,@($w));$m.Wait(-1)|Out-Null;$m.Result}
$AppDomain=[Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager];
$Action={Write-Host -ForegroundColor Green -Object 'Assembly was loaded!';};
Register-ObjectEvent -InputObject $AppDomain -EventName CurrentSessionChanged -Action $Action -OutVariable EventSubscription -SourceIdentifier CurrentSessionChanged;
returns
Register-ObjectEvent : Windows PowerShell cannot subscribe to Windows RT events.
thanks anyways
here is my taskbar code if you want to further experiment
(Get-Process -Id $pid).PriorityClass='idle'
Set-PSDebug -off
Add-Type -AssemblyName System.Windows.Forms,System.Runtime.WindowsRuntime
#thanks to [DubyaDude](https://github.com/DubyaDude) on imporing SMTC to powershell
[Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager,Windows.Media.Control,ContentType=WindowsRuntime]|Out-Null
$t=([System.WindowsRuntimeSystemExtensions].GetMethods()|?{$_.Name -eq 'AsTask' -and $_.GetParameters().Count -eq 1 -and $_.GetParameters()[0].ParameterType.Name -eq 'IAsyncOperation`1'})[0]
function aw($w,$r){$m=($t.MakeGenericMethod($r)).Invoke($null,@($w));$m.Wait(-1)|Out-Null;$m.Result}
# .\ parent folder selection not works with vbs, you should define complete path
$f="C:\ProgramData\Apps\System\Taskbar\TrayIcons"
function n{New-Object Windows.Forms.NotifyIcon}
function k($v){(New-object -com wscript.shell).SendKeys([char]$v)|Out-Null}
function i($v){New-Object System.Drawing.Icon("$f\ico\$v.ico")}
$p=n;$l=i("pl");$u=i("ps")
$p.add_click({k(179)})
function pf{
$x=@()
$ps=$false
$pt="Play"
$sm=[Windows.Media.Control.GlobalSystemMediaTransportControlsSessionManager]
foreach($s in (aw($sm::RequestAsync())($sm)).GetSessions()){
$a=@()
$a+=$s.GetPlaybackInfo().PlaybackStatus
$pr=aw($s.TryGetMediaPropertiesAsync())([Windows.Media.Control.GlobalSystemMediaTransportControlsSessionMediaProperties])
$a+=$pr.Artist+" - "+$pr.Title
if($a -contains "Playing"){$ps=$true;$pt=$a[1]}
$x+=$a
$s=$null
}
$p.icon=if($ps){$u}else{$l}
#sets tray icon tooltip to artist - track tile
$p.text=$pt
$x=$a=@()
}
$n=n;$n.icon=i("nx");$n.text="Next"
#Right click on next button sends previous shortcut
$n.add_MouseDown({if($_.Button -eq [Windows.Forms.MouseButtons]::left){k(176)}else{k(177)}})
$o=n;$o.icon=i("op");$o.text="Options"
$o.add_click({& "$f\Options.ps1"})
$o,$n,$p|%{$_.visible=$true}
#while keeps taskbar icons visible, and keeps script running
#reducing sleep time to increases CPU usage much
#clearing host keeps ram usage stable, otherwise smtc outputs increase ram usage over time
While(1){
pf|Out-Null
[System.GC]::Collect()
Clear-Host
[Microsoft.PowerShell.PSConsoleReadLine]::ClearHistory()
start-sleep -s 1
}
sorry for short variable names but it reduces RAM usage much on powershell scripts if you know any more method to reduce RAM usage im listening it takes around 30-40 mb atm, it goes to 70 mb after launching my popup WPF window
i prefer to not using sample cmd exe at this point smtc works nice, thanks to you
icon files icons.zip
im reducing priorty to low for easier process selection on windows startup
i launch with a vbs file that checks is there a powershell process with low priority. this one prevents multiple launches
set ws = CreateObject("WScript.Shell")
Set WMIs=GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\cimv2")
prV=0
'checks is there a powershell script running with low priority
For Each pr in WMIs.ExecQuery("Select * from Win32_Process Where Name = 'Powershell.exe'")
If(pr.Priority=4)then
prV=1
end if
Next
If(prV=0)then
ws.Run "powershell -NoProfile -NoLogo -WindowStyle hidden -NonInteractive -ExecutionPolicy Unrestricted -File C:\ProgramData\Apps\System\Taskbar\TrayIcons\media.ps1", 0, false
end if
I see, doing a bit of research on
Register-ObjectEvent : Windows PowerShell cannot subscribe to Windows RT events.
I found a post where they use .NET event wrapper class to be able to use WinRT events: https://deletethis.net/dave/2016-06/WinRT+Toast+from+PowerShell
I'll try to code something up with that if you don't get to it first, though might be a bit busy today.
Regarding the RAM, I've only used Powershell for some automation at my old job. So, I'm not too proficient in a lot of the details/complicated parts.
Unfortunately, seems I couldn't get it working. The issue of not supporting WinRT events seems to be a very long-standing one (https://github.com/PowerShell/PowerShell/issues/2181).
I'll close this as it's the most I can do in terms of helping you. If you have any further questions don't hesitate to ask.
thanks for all infos, you really helped much
Thanks for sharing this one im trying to get playback status, other infos would be nice too is that possible to get data on powershell