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.
- Right click on the album art in the bottom left and use the star context menu option.
- 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
}
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