Jump to content
Sign in to follow this  
Bilgus

Spotify Grab Now Playing and Track Elapsed/Duration

Recommended Posts

Thanks to LarsJ's excellent UIASpy application I was able to grab the now playing and track elapsed / duration from Spotify in about 15 minutes

https://www.autoitscript.com/forum/topic/196833-uiaspy-ui-automation-spy-tool/

You will need junkew's CUIAutomation2.au3 from the above UDF

 

Spoiler
#include "CUIAutomation2.au3"
Global Enum $eSpotify_Elapsed = 0, $eSpotify_Duration, $eSpotify_NowPlaying, $eSpotify_Elems
Example()

Func Example()
    Local $a_oSpotify = Spotify_InitInterface()
    Local $iErr = @error
    Local $sErr = @extended
    If $iErr Then
        MsgBox(16, "Error", "Unable to parse Spotify App " & @CRLF & "Error (" & $iErr & ") " & $a_oSpotify)
        Return
    EndIf

    Local $sElapsed, $sDuration
    Local $sNowPlaying = ""
    Local $iElapsed = 0, $iDuration = 0

    For $j = 0 To 120
        If $iElapsed = 0 Then
            $a_oSpotify[$eSpotify_NowPlaying].GetCurrentPropertyValue($UIA_NamePropertyId, $sNowPlaying)
            $sNowPlaying = NowPlaying_Proper($sNowPlaying)
            $a_oSpotify[$eSpotify_Duration].GetCurrentPropertyValue($UIA_NamePropertyId, $sDuration)
            $iDuration = TimeStringtoSeconds($sDuration)
        EndIf

        $a_oSpotify[$eSpotify_Elapsed].GetCurrentPropertyValue($UIA_NamePropertyId, $sElapsed)
        $iElapsed = TimeStringtoSeconds($sElapsed)
        ToolTip($sNowPlaying & @CRLF & $iElapsed & " / " & $iDuration & "s")
        Sleep(500)
    Next

EndFunc   ;==>Example

Func TimeStringtoSeconds($sHMS)
    Local $aTime = StringSplit($sHMS, ":")
    Local $iMultiplier = 1
    Local $iSeconds = 0
    If IsArray($aTime) Then
        For $i = $aTime[0] To 1 Step -1
            $iSeconds += $aTime[$i] * $iMultiplier
            $iMultiplier *= 60
        Next
    EndIf
    Return $iSeconds
EndFunc   ;==>TimeStringtoSeconds

Func NowPlaying_Proper($sNowPlaying)
    Local Const $sSearchNowPlaying = "Now Playing: "
    Local Const $sDelim = " by "
    Local $sProper = ""
    Local $sRes = StringReplace($sNowPlaying, $sSearchNowPlaying, "")
    Local $aRes = StringSplit($sRes, $sDelim, 2 + 1) ;$STR_ENTIRESPLIT (1), $STR_NOCOUNT (2)

    If IsArray($aRes) Then $sProper = $aRes[1] & " - " & $aRes[0]

    Return $sProper
EndFunc   ;==>NowPlaying_Proper

Func Spotify_InitInterface()
    ;Thanks LarsJ
    ;https://www.autoitscript.com/forum/topic/196833-uiaspy-ui-automation-spy-tool/
    Local Const $sSearchNowPlaying = "Now Playing: "
    Local $a_oSpotify[$eSpotify_Elems]
    Local $iErr = 1
    Local $sErr = "Unable to create $oUIAutomation object"
    Local $oAutomationElementArray, $iLength, $pElements
    Local $pTextField, $oTextField, $sTextField
    Local $oNowPlaying = 0
    ; Open Spotify

    ; Create UI Automation object
    Local $oUIAutomation = ObjCreateInterface($sCLSID_CUIAutomation, $sIID_IUIAutomation, $dtagIUIAutomation)
    While IsObj($oUIAutomation)
        $iErr = 0
        $sErr = "SUCCESS"
        ; Get Desktop element
        Local $pDesktop, $oDesktop
        $oUIAutomation.GetRootElement($pDesktop)
        $oDesktop = ObjCreateInterface($pDesktop, $sIID_IUIAutomationElement, $dtagIUIAutomationElement)
        If Not IsObj($oDesktop) Then
            $iErr = 2
            $sErr = "Unable to create $oDesktop object"
            ExitLoop
        EndIf

        ; --- Spotify window ---
        Local $pCondition_Spotify
        $oUIAutomation.CreatePropertyCondition($UIA_ValueValuePropertyId, "https://zlink.app.spotify.com/index.html", $pCondition_Spotify)
        If Not $pCondition_Spotify Then
            $iErr = 3
            $sErr = "Unable to create $pCondition_Spotify pointer"
            ExitLoop
        EndIf

        Local $pSpotify, $oSpotify
        $oDesktop.FindFirst($TreeScope_Descendants, $pCondition_Spotify, $pSpotify)
        $oSpotify = ObjCreateInterface($pSpotify, $sIID_IUIAutomationElement, $dtagIUIAutomationElement)
        If Not IsObj($oSpotify) Then
            ;Fallback
            $oUIAutomation.CreatePropertyConditionEx($UIA_ClassNamePropertyId, "Chrome_RenderWidgetHostHWND", $PropertyConditionFlags_IgnoreCase, $pCondition_Spotify)
            If Not $pCondition_Spotify Then
                $iErr = 4
                $sErr = "Unable to create $pCondition_Spotify pointer"
                ExitLoop
            EndIf
            $oDesktop.FindFirst($TreeScope_Descendants, $pCondition_Spotify, $pSpotify)
            $oSpotify = ObjCreateInterface($pSpotify, $sIID_IUIAutomationElement, $dtagIUIAutomationElement)

            If Not IsObj($oSpotify) Then
                $iErr = 5
                $sErr = "Unable to create $oSpotify object"
                ExitLoop
            EndIf
        EndIf

        ; --- Find Track / Artist ---
        Local $pCondition_Hyperlink
        $oUIAutomation.CreatePropertyCondition($UIA_ControlTypePropertyId, $UIA_HyperlinkControlTypeId, $pCondition_Hyperlink)
        If Not $pCondition_Hyperlink Then
            $iErr = 6
            $sErr = "Unable to create $pCondition_Hyperlink pointer"
            ExitLoop
        EndIf

        $oSpotify.FindAll($TreeScope_Descendants, $pCondition_Hyperlink, $pElements)
        If Not $pElements Then
            $iErr = 7
            $sErr = "Unable to find $pCondition_Hyperlink"
            ExitLoop
        EndIf

        $oAutomationElementArray = ObjCreateInterface($pElements, $sIID_IUIAutomationElementArray, $dtagIUIAutomationElementArray)
        $oAutomationElementArray.Length($iLength)
        If Not $iLength Then
            $iErr = 8
            $sErr = "Unable to find Now Playing object"
            ExitLoop
        EndIf

        For $i = 0 To $iLength - 1
            $oAutomationElementArray.GetElement($i, $pTextField)
            $oTextField = ObjCreateInterface($pTextField, $sIID_IUIAutomationElement, $dtagIUIAutomationElement)
            $oTextField.GetCurrentPropertyValue($UIA_NamePropertyId, $sTextField)
            ;;ConsoleWrite($sTextField & @CRLF)
            If StringRegExp($sTextField, "(?i)^" & $sSearchNowPlaying) Then ;Caseless match at beginning of string
                $oNowPlaying = $oTextField
                ExitLoop
            EndIf
        Next

        If Not IsObj($oNowPlaying) Then
            $iErr = 9
            $sErr = "Unable to find Now Playing object"
            ExitLoop
        EndIf

        ; --- Find Player controls ---
        Local $pCondition_PlayerControls
        $oUIAutomation.CreatePropertyConditionEx($UIA_NamePropertyId, "Player Controls", $PropertyConditionFlags_IgnoreCase, $pCondition_PlayerControls)
        If Not $pCondition_PlayerControls Then
            $iErr = 10
            $sErr = "Unable to create $pCondition_PlayerControls pointer"
            ExitLoop
        EndIf

        Local $iTries = 10
        While $iTries > 0
            Local $pPlayerControls, $oPlayerControls = 0
            $oSpotify.FindFirst($TreeScope_Subtree, $pCondition_PlayerControls, $pPlayerControls)
            If $pPlayerControls Then
                $oPlayerControls = ObjCreateInterface($pPlayerControls, $sIID_IUIAutomationElement, $dtagIUIAutomationElement)
            EndIf
            If Not IsObj($oPlayerControls) Then
                Sleep(1000)
                $iTries -= 1
            Else
                ExitLoop
            EndIf
        WEnd

        If Not IsObj($oPlayerControls) Then
            $iErr = 11
            $sErr = "Unable to create $oPlayerControls object"
            ExitLoop
        EndIf

        ; --- Find elapsed / duration ---
        Local $pCondition_TextControls
        $oUIAutomation.CreatePropertyCondition($UIA_ControlTypePropertyId, $UIA_TextControlTypeId, $pCondition_TextControls)
        If Not $pCondition_TextControls Then
            $iErr = 12
            $sErr = "Unable to create $pCondition_TextControls pointer"
            ExitLoop
        EndIf

        $oPlayerControls.FindAll($TreeScope_Descendants, $pCondition_TextControls, $pElements)
        If Not $pElements Then
            $iErr = 13
            $sErr = "Unable to find $pCondition_TextControls"
            ExitLoop
        EndIf

        $oAutomationElementArray = ObjCreateInterface($pElements, $sIID_IUIAutomationElementArray, $dtagIUIAutomationElementArray)
        $oAutomationElementArray.Length($iLength)
        If $iLength < $eSpotify_Elems - 1 Then
            $iErr = 14
            $sErr = "Unable to find elapsed or duration"
            ExitLoop
        EndIf

        For $i = 0 To $eSpotify_Elems - 2
            $oAutomationElementArray.GetElement($i, $pTextField)
            $oTextField = ObjCreateInterface($pTextField, $sIID_IUIAutomationElement, $dtagIUIAutomationElement)
            $a_oSpotify[$i] = $oTextField
        Next
        $a_oSpotify[$eSpotify_NowPlaying] = $oNowPlaying
        ExitLoop
    WEnd
    $oUIAutomation = 0
    $oAutomationElementArray = 0
    $oTextField = 0

    If $iErr Then
        Return SetError($iErr, 0, $sErr)
    Else
        Return $a_oSpotify
    EndIf
EndFunc   ;==>Spotify_InitInterface

 

 

Edited by Bilgus

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Recently Browsing   0 members

    No registered users viewing this page.

  • Similar Content

    • By Sayed
      Hi there, 
      I'm new in AutoIt forms and using AutoIt to automate desktop application (able to automate the application normally but facing issue when I've to re-run the application twice within the same script...so need help in this please)
      here is the steps then followed by the issue in a brief : 
      1- run application . 
      2- do some actions (click menus,activate windows,set texts..)
      3- close the application. 
      4- run the application again & access the same controls.
      5- open the same windows again (like step 2)
      6- perform some validations (by getting texts from some text boxes)
      7- close the application again (and repeat 1-7 for 15 times in average )
      The issue 
      * all controls are accessible in the first run and actions done successfully on controls (for steps 1-3) BUT from the second run of the application from step-4 it's able to set focus only the main application window.
      Note: only unique properties used to while mapping the controls. 
      Error that appear in the console :
      UIAWrappers.au3" (1673) : ==> Array variable has incorrect number of subscripts or subscript dimension range exceeded.: $x = Int($t[1] + ($t[3] / 2)) $x = Int($t[1] + (^ ERROR  
      Simple spy code  of one of the controls that has this strange issue(menubar&view menu Item): 
      ;~ *** Standard code maintainable *** #include "UIAWrappers.au3" AutoItSetOption("MustDeclareVars", 1) _UIA_setVar("oP1","Title:=XXX;controltype:=UIA_WindowControlTypeId;class:=WindowsForms10.Window.8.app") ;main app form xxx _UIA_setVar("oP2","Title:=menuStrip1;controltype:=UIA_MenuBarControlTypeId;class:=WindowsForms10.Window.8.app") ;menuStrip1 ;~ $oUIElement=_UIA_getObjectByFindAll("View.mainwindow", "title:=View;ControlType:=UIA_MenuItemControlTypeId", $treescope_subtree) _UIA_setVar("oUIElement","Title:=View;controltype:=UIA_MenuItemControlTypeId;class:=") ;ControlType:=UIA_MenuItemControlTypeId;classname:=") ;~ Actions split away from logical/technical definition above can come from configfiles ;~_UIA_Action("oP1","highlight") _UIA_Action("oP1","setfocus") ;~_UIA_Action("oP2","highlight") _UIA_Action("oP2","setfocus") _UIA_action("oUIElement","highlight") ;~_UIA_action("oUIElement","click")  
       
    • By boomingranny
      ;****************************************************************** ;Spotify Automation: Start spotify and play a playlist ;v1.01 27/04/2018 ;------------------------------------------------------------------ ;by Daniel Barnes ;------------------------------------------------------------------ ;v1.01 27/04/2018 ;restarts spotify if it fails the first time ;works on computers that minimize Spotify to tray ;instead of closing it, and have a different ui element selected ;such as the Devices available option when spotify is started ;------------------------------------------------------------------ ;v1.00 25/04/2018 ;initial version ;------------------------------------------------------------------ ;Replace the below with the Spotify URI ;If you right click the playlist> share> copy spotify uri ;you can find the uri $spotifyURI = "spotify:user:12177300374:playlist:0iaTAvHRSaIkD23ofl2tck" ;****************************************************************** ;if this script breaks in the future, figure this out using the AutoIt Window Info Tool Const $SpotifyWindowIdentifier = "[Title:Spotify;Class:Chrome_WidgetWin_0]" ;NOTE ABOUT SHUFFLE: ;I couldn't automate turning shuffling on or off ;I can toggle it, but I cannot figure out the current state ;If you want to shuffle, turn on shuffle in Spotify prior ;to running this script PlaySpotifyPlaylist($spotifyURI) Func PlaySpotifyPlaylist($spotifyURI,$retry = 0) ;terminate spotify process on subsequent attempts If $retry Then echo ("Closing existing Spotify process") ProcessClose("Spotify.exe") endif echo ("Launching Spotify URI") ShellExecute($spotifyURI) ;Pause any existing song playing (if there is any) ;this is because Spotify's window title is only 'Spotify' when it is not playing any songs If Not WinExists($SpotifyWindowIdentifier) Then echo ("Pausing existing song (To find spotify window)") $timer = TimerInit() While Not WinExists($SpotifyWindowIdentifier) Send("{MEDIA_PLAY_PAUSE}") Sleep(500) If TimerDiff($timer) > 5 * 1000 Then ErrorMsg ("Timed out trying pause existing song") WEnd endif ;Get Spotify Window Handle $hwndSpotify = WinGetHandle($SpotifyWindowIdentifier) echo ("Attempting to play playlist") $timer = TimerInit() While WinGetTitle($hwndSpotify) = "Spotify" ;once it is playing the song, the title changes to the name of the song WinActivate($hwndSpotify) ControlSend($hwndSpotify,"","","{ENTER}") ;space will play what was previously playing (even another playlist or song), enter will play the playlist ;) Sleep(1000) If TimerDiff($timer) > 5 * 1000 Then If $retry Then ErrorMsg ("Timed out trying to play playlist") else Return PlaySpotifyPlaylist($spotifyURI,$retry+1) endif endif WEnd EndFunc Func echo($text) ConsoleWrite ($text&@CRLF) EndFunc Func ErrorMsg($text) MsgBox(16,StringTrimRight(@ScriptName,4),$text) Exit EndFunc  
    • By AliOzturk
      Hello guys
      I would like to be able to change songs on spotify using shourtcuts on my keyboard due to the fact i don't own a keyboard with extra keys providing this feature.
      Therefor I've made a script binding my keys to make this possible, but i have a few issues and I think there is a easier or better way of creating this script. I hope someone can have a look and tell me what i could improve.
      Some of the issues are:
      I have to wait a little time before i will be able to change song right after i just changed. (I can't spam ALT + Right) Doesn't work when im inside a game. This is actually a big issue for me.  
      My current code which works:
      HotKeySet("!{left}", "Previous") HotKeySet("!{right}","naeste") HotKeySet("!{down}","current") HotKeySet("{DEL}","quit") While 1 Sleep(100) WEnd Func naeste() Sleep(400) ControlSend("Spotify", "", "", "^{right}") EndFunc Func Previous() Sleep(400) ControlSend("Spotify", "", "", "^{left}") EndFunc Func Current() Local $song = WinGetTitle("Spotify", "") TrayTip("", $song, 1) EndFunc Func quit() Exit 0 EndFunc - AliOzturk
×
×
  • Create New...