How to star a Spotify song with Autohotkey while it's minimized?
Asked Answered
B

2

8

How to star a Spotify song while it's minimized?

Okay so the Spotify application on Windows does not have inbuilt support for global hotkeys, and very basic hotkeys even when the application window is currently active. Frustratingly 'starring' the song that is currently playing does not have a keyboard shortcut, even when the window is active.

So I have an Autohotkey script that gives me global hotkeys for playback control, volume and copying the song title (including em dash fixing) but I've hit a brick wall trying to figure out how to star the current song.

To make matters even harder, I only want the Autohotkey script to star the song if it's not already starred. If it's already starred then just leave it alone.

I want all this to happen when the Application is in the tray, without opening the window.


EDIT: Semi-working solution

So I've come up with a reasonably solution, it opens up a context menu even when the program is minimized and does everything including not unstarring songs. Unfortunately the context menu means that it will minimize full-screen applications like games for a split second. Depending on the game that can be a pain, but it's the best I can do without fancy DLL calls and stuff.

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

;|---------------|
;|--[ SOURCES ]--|
;|---------------|
;Base Spotify Script from: http://www.autohotkey.com/board/topic/36239-spotify-global-hotkeys/
;Base Starring Script from: http://superuser.com/questions/324416/any-spotify-tweaks-with-keyboard-shortcut-to-star-tracks

;|------------------|
;|--[ SETTING UP ]--|
;|------------------|
DetectHiddenWindows, On ;Detect Spotify even if it's minimized
#IfWinExist ahk_class SpotifyMainWindow ;Only do the following if Spotify is running
spotify = ahk_class SpotifyMainWindow ;Set variable for Spotify Window Name

;|---------------|
;|--[ HOTKEYS ]--|
;|---------------|
; "CTRL + ALT + PAGEUP" for previous 
^!PGUP:: 
{
    ControlSend, ahk_parent, ^{Left}, %spotify% 
    return 
}

; "CTRL + ALT + PAGEDOWN" for next 
^!PGDN:: 
{ 
    ControlSend, ahk_parent, ^{Right}, %spotify% 
    return 
} 

; "CTRL + ALT + HOME" for pause
^!Home::
{ 
    ControlSend, ahk_parent, {Space}, %spotify% 
    return
} 

; "CTRL + ALT + END" for track-name
^!End:: 
{ 
    WinGetTitle, spotify_playing, %spotify% ;Get the title of Spotify which contains the track-name

    StringTrimLeft, trimmed_playing, spotify_playing, 10 ;Get rid of extra text and place into 'trimmed_playing'
    StringReplace, replaced_playing, trimmed_playing, –, -, All ;Replace en dash with normal dash and place into 'replaced_playing'

    clipboard = %replaced_playing% ;Copy the fixed text to clipboard
    return 
} 

; "CTRL + ALT + UP" for volume up
^!Up::
{ 
    ControlSend, ahk_parent, ^{Up}, %spotify% 
    return 
} 

; "CTRL + ALT + DOWN" for volume down
^!Down::
{ 
    ControlSend, ahk_parent, ^{Down}, %spotify% 
    return 
} 

; "CTRL + ALT + INSERT" for starring the current song
^!Insert::
{ 
    ;Store active window and mouse position.
    MouseGetPos, , , winID

    ;Right click near the song title in the "Now Playing" box.
    WinGetPos,  ,  ,  , spotifyHeight, %spotify%
    clickX := 100
    clickY := spotifyHeight-70 
    ControlClick, x%clickX% y%clickY% , %spotify%, , Right, , NA

    ;Get the contents of the context menu.
    WinWait, ahk_class #32768
    SendMessage, 0x1E1 ;MN_GETHMENU
    allContextMenuInfo := ErrorLevel

    ;The "Star" command is the 2nd menu item.
    ;If the song is Unstarred the text is Star, and vice versa. But sometimes some wierd characters are included.
    ;The only reliable way I found is to check if the first letter is S.
    menuText_StarUnstar := GetContextMenuItemText(allContextMenuInfo, 2)
    StringGetPos, positionOfS, menuText_StarUnstar, S

    ;If S is the first letter, star the song.
    notStarred := (%positionOfS% = 0)
    If notStarred 
    {
        ;Arrow down to the Star menu item and press enter.
        ControlSend, ahk_parent, {Down}{Down}{Enter}, %spotify% 
    } 
    Else 
    {
        ;Just close the context menu.
        ControlSend, ahk_parent, {Escape}, %spotify% 
    }

    ;Restore original window and mouse position.
    WinActivate ahk_id %winID%

    return
}

;|-----------------|
;|--[ FUNCTIONS ]--|
;|-----------------|

;Context menu helper function.
GetContextMenuItemText(hMenu, nPos)
{
    length := DllCall("GetMenuString"
            , "UInt", hMenu
            , "UInt", nPos
            , "UInt", 0 ; NULL
            , "Int", 0  ; Get length
            , "UInt", 0x0400)   ; MF_BYPOSITION
        VarSetCapacity(lpString, length + 1)
        length := DllCall("GetMenuString"
            , "UInt", hMenu
            , "UInt", nPos
            , "Str", lpString
            , "Int", length + 1
            , "UInt", 0x0400)
    return lpString
}

I modified some existing scripts that were hidden away on the internet. The sources of which can be found at the top of the source-code, it might of some help if anyone wants to try and get it working fully without the context-menu appearing.


How can you star a song when using the application normally?

There only seems to be 2 routes to star a song in Spotify.

  1. Right click on the album art in the bottom left and use the star context menu option.
  2. Right click on the song in the playlist list and use the star context menu option.

To make things awkward, the star menu option is replaced with the unstar menu option in the exact same place if something is already starred. They also have the same shortcut key when the menu is open (the t key). So you can't just do a blind menu selection without some sort of checking first.

So what possible routes could we take?

Right clicking the album art in the bottom left is the only realistic route.

If we can read the text from a context menu while minimized, we can tell whether something is already starred by the change in text from 'star' to 'unstar'. After that test, we can decide whether to hit the T key to actually star the song.

I also spent some time with Window Detective to see if I can just send a relatively simple PostMessage/SendMessage to star the song. But I have very little experience and I've no idea how to chain them together to get the result I want.

I've found out that the star and unstar context menu options are actually different:

Star   -> WM_MENUSELECT (0x11F) | item identifier = 11 | flags = NONE | MF_HILITE
Unstar -> WM_MENUSELECT (0x11F) | item identifier = 12 | flags = NONE | MF_HILITE

However I don't know how to actually chain together PostMessages/SendMessages to open the menu and then only select item #11 and then hit enter.

A list of messages that Spotify receives when starring something

Just in case this helps identify if doing a PostMessage/SendMessage route is possible.

->WM_RBUTTONDOWN (0x204)
->WM_RBUTTONUP (0x205)
->WM_MENUSELECT (0x11F)
<-WM_MENUSELECT (0x11F)
->WM_KEYDOWN (0x100)
->WM_MENUSELECT (0x11F)
<-WM_MENUSELECT (0x11F)
->WM_KEYUP (0x101)

Afterword

I've searched for a while trying to find good examples of PostMessages/SendMessages being used to do this sort of context menu selection but failed to find anything.

Thanks for your time.

Current Autohotkey Script

#NoEnv  ; Recommended for performance and compatibility with future AutoHotkey releases.
SendMode Input  ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir %A_ScriptDir%  ; Ensures a consistent starting directory.

; "CTRL + ALT + PAGEUP"  for previous 
^!PGUP:: 
{
    DetectHiddenWindows, On 
    ControlSend, ahk_parent, ^{Left}, ahk_class SpotifyMainWindow 
    DetectHiddenWindows, Off 
    return 
}

; "CTRL + ALT + PAGEDOWN"  for next 
^!PGDN:: 
{ 
    DetectHiddenWindows, On 
    ControlSend, ahk_parent, ^{Right}, ahk_class SpotifyMainWindow 
    DetectHiddenWindows, Off 
    return 
} 

; "CTRL + ALT + HOME"  for pause
^!Home::
{ 
    DetectHiddenWindows, On 
    ControlSend, ahk_parent, {space}, ahk_class SpotifyMainWindow 
    DetectHiddenWindows, Off 
    return
} 

; "CTRL + ALT + END"  for info 
^!End:: 
{ 
    DetectHiddenWindows, On 
    WinGetTitle, spotify_playing, ahk_class SpotifyMainWindow 
    DetectHiddenWindows, Off 

    StringTrimLeft, trimmed_playing, spotify_playing, 10 
    StringReplace, replaced_playing, trimmed_playing, –, -, All

    clipboard = %replaced_playing%
    return 
} 

; "CTRL + ALT + UP"  for volume up
^!Up::
{ 
    DetectHiddenWindows, On 
    ControlSend, ahk_parent, ^{Up}, ahk_class SpotifyMainWindow 
    DetectHiddenWindows, Off 
    return 
} 

; "CTRL + ALT + DOWN"  for volume down
^!Down::
{ 
    DetectHiddenWindows, On 
    ControlSend, ahk_parent, ^{Down}, ahk_class SpotifyMainWindow 
    DetectHiddenWindows, Off 
    return 
} 

/*
PLAN: I want to make this star the current song if it's unstarred
NOTES: 
    WM_MENUSELECT for star seems to be item identifier 11, unstar is 12
    Star   -> WM_MENUSELECT (0x11F) | item identifier = 11 | flags = NONE | MF_HILITE
    Unstar -> WM_MENUSELECT (0x11F) | item identifier = 12 | flags = NONE | MF_HILITE
*/
; "CTRL + ALT + INSERT"  for starring the current song
^!Insert::
{ 
    DetectHiddenWindows, On 

    DetectHiddenWindows, Off 
    return 
}
Briefing answered 27/5, 2013 at 15:3 Comment(3)
Thinking in a whole different direction: Have you thought about using one of the existing APIs (e.g. Apps API) to star your tracks? I don't know if it can be done easily, but maybe there's a way to fetch the information of the current track with AHK, and then identify and star it with an API. Generally, you could try to watch the requests produced by spotify when starring tracks (e.g. with URL Snooper, WireShark) and then manufacture them on your own.Laddie
If you don't need to know if the song is starred already, the controlclick x.. y.. can also be done with Spotify being minimized. Thinking realistically: When you want to do this manually, you have to see whether the song is starred already, too. So you are already automating everything there is. If you cannot go without that, consider adding these songs to a new customized playlist and add all of them to your songs before closing spotify.Bayle
Use this: github.com/CloakerSmoker/Spotify.ahkCreodont
T
4

The one I currently use whilst playing games is pretty simple:

MouseGetPos, x, y, origwin
WinActivate, ahk_class SpotifyMainWindow
click 183, 1000 ;
WinActivate, ahk_id %origwin%
MouseMove, %x%, %y%

A neat replacement for your other code; Spotify responds to media buttons so these work a treat:

^!Left::Media_Prev
^!Right::Media_Next
^!Up::Volume_Up
^!Down::Volume_Down
^!Space::Media_Play_Pause

Edit: Spotify changed their layout. The + moves with the length of the song title. To make it work more consistently I changed the function to right click and 'Save to your music.'

FavouriteSpotifySong()
{
    WinGetPos, , , SPOTIFY_X_AXIS, SPOTIFY_Y_AXIS, ahk_class SpotifyMainWindow
    yPos := SPOTIFY_Y_AXIS - 30
    ControlClick, X25 Y%yPos%, ahk_class SpotifyMainWindow,, RIGHT
    Sleep 75
    yPos -= 54
    ControlClick, X33 Y%yPos%, ahk_class SpotifyMainWindow
}
Tewfik answered 26/10, 2016 at 22:19 Comment(0)
A
0
SpotifyLove(flag)
{            
WinGetActiveTitle, CurOpen
MouseGetPos, MouseX, MouseY

IfWinExist ahk_exe Spotify.exe ; check if spotify is open
    winactivate ahk_exe Spotify.exe
else
    return
WinWait ahk_exe Spotify.exe

Click, 65 940 0; Spotify has a 60x60 pixel window with the menu in the top left corner. I have not find another way to activate the the main window except for this click. Any other solutions to are much appreciated.

WinGetPos, X, Y, W, H, A ; get the dimensions of the spotify window

; start searching for image in a rectangle. The first point of the rectangle X1,Y1 is 100 pixels above the bottom left corner. The other point of the rectangle X2,Y2 is 600 pixels right of the left edge and along the bottom of the window.

X1 := 0
X2 := 600
Y1 := H - 100
Y2 := H

; imagesearch is really crappy. *70 gives the function some leeway to find the right image in the window. We are searching for the heart. 

ImageSearch, OutputVarX, OutputVarY, X1, Y1, X2, Y2, *70 Z:\Pictures\heart.jpg

; click on heart if we like the song, click on the dislike button and "i dont like this song" otherwise.

A := OutputVarX+15
B := OutputVarY+15
if (flag == 1)
{
    Click, %A% %B%
} 
else {
    HateX := OutputVarX+50
    DontLikeSongX := HateX+10
    DontLikeSongY := OutputVarY-10
    Click, %HateX%, %OutputVarY%
    Sleep, 200
    Click, %DontLikeSongX%, %DontLikeSongY%
}

; return to previous window with the same mouse position.
WinRestore, %CurOpen%
WinMaximize, %CurOpen%
WinActivate, %CurOpen%
MouseMove, %MouseX%, %MouseY% 
}

^+S::
SpotifyLove(1)
Return

;^+d::
;SpotifyLove(0)
;Return

I found this on the forum of AutoHotKey long ago and recently came back to AutoHotkey and had to re-tweak and spent an hour to get it right. It isn't perfect still as it's using ImageSearch. Make sure to take a snippet of the empty heart from the spotify app and mention it's path to JPG (take as less borders as you can around the empty heart screenshot image) and mention the path of it. It gets right around 95% of the times but also doesn't take away the problem of opening the spotify app for a brief moment so not so useful during gaming. I have commented the part of the disliking the song as it wasn't working at all.

The shortcut I have set is : "Ctrl + Shift + S"

EDIT : Adding the link for empty_heart from Spotify Windows App.(https://i.sstatic.net/0ttCS.jpg)

Keep in mind that, since the heart position keeps changing for different title lengths in Spotify, the accuracy is only 95% or so.

The current UI of Spotify at the time of posting this, should also be visible in the above imgur link.

Alienate answered 12/7, 2021 at 12:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.