1 (изменено: OmTatSat, 2021-08-15 10:32:13)

Тема: AHK: Spotify Global hot keys

В сети не нашел ни одного полностью рабочего варианта.
По этому делюсь своим вариантом, в некоторых местах использован код от других авторов.
Хоткеи работают всегда.
Spotify при сворачивании и закрытии окна прячется в трей.
Хоткеи повешаны на NumPad клавиатуру.
Можно включить переключение песен (нажатием "NumPad /") как внутренними хоткеями Spotify так и медиа клавишами виндовса - на win10 показывает OSD переключенной песни.
Добавил постоянные дополнительные хоткеи на медиа клавиши виндовса - для тех у кого нет мультимедиа клавиатуры.
Добавил хоткеи кнопок:
Добавить в медиатеку. Like
Мне не нравится этот трек. Dislike


;http://forum.script-coding.com/viewtopic.php?pid=149241#p149241

SetBatchLines, -1
globalmediakeys := 1
WinGet, activewin , ID, A
DetectHiddenWindows, On 
IfWinNotExist, ahk_exe Spotify.exe
{
	RunWait Spotify.exe, % A_AppData "\Spotify"
	sleep, 2000
}
else Run Spotify.exe
	global wntitle :="ahk_id"getSpotifyHwnd()
;ControlFocus, Chrome_RenderWidgetHostHWND1,  %wntitle%
sleep, 1400
send, !{tab}
sleep, 200
;send, !{tab}
WinActivate, "ahk_id"activewin
;ToolTip, %activewin%

Numpad9::																;Добавить в медиатеку. Like
{
oAcc := Acc_Get("Object", "4.1.2.1.1.1.2.1.1.3.1.1.3.1.1", 0, wntitle)
oAcc.accDoDefaultAction(0)
vOutput := oAcc.accname(0)
ToolTip, % vOutput
Sleep, 5000
ToolTip
return
}
4.1.2.1.1.1.2.1.1.3.1.1.3.1.1
Numpad3::																;Мне не нравится этот трек. Dislike
{
oAcc := Acc_Get("Object", "4.1.2.1.1.1.2.1.1.3.1.1.4", 0, wntitle)
oAcc.accDoDefaultAction(0)
Sleep, 500
	oAcc := Acc_Get("Object", "4.1.2.1.1.1.3.1.1.1", 0, wntitle) ;Мне не нравится этот трек
	;oAcc := Acc_Get("Object", "4.1.2.1.1.1.3.1.1.2", 0, wntitle) ;Мне не нравится группа
	Sleep, 200
	oAcc.accDoDefaultAction(0)
	;ToolTip, oAcc.accname(0)
	vOutput := oAcc.accname(0)
ToolTip, % vOutput
Sleep, 5000
ToolTip
return
}



Numpad5::						 ;  Play/Pause
{
		if (globalmediakeys = 1)
		Send      {Media_Play_Pause}
		else
spotifyKey("{Space}")
return
}
Numpad8::						 ;  Next
{
		if (globalmediakeys = 1)
		{
			;ToolTip, global
		Send      {Media_Next}
		return
		}
		else
spotifyKey("^{Right}")
return
}
Numpad2::						 ;  Previous
{
		if (globalmediakeys = 1)
		Send      {Media_Prev}
		else
spotifyKey("^{Left}")
return
}
Numpad6::spotifyKey("+{Right}")  ;  Seek forward
Numpad4::spotifyKey("+{Left}")   ;  Seek backward
NumpadAdd::						 ; Volume up
{
		if (globalmediakeys = 1)
		Send      {Volume_Up}
		else
spotifyKey("^{Up}")
return
}
NumpadSub::						 ;  Volume down
{
		if (globalmediakeys = 1)
		Send      {Volume_Down}
		else
spotifyKey("^{Down}")
return
}
Numpad0::
{
	IfWinActive, ahk_exe Spotify.exe
		{
			;ToolTip, act
		WinHide, %spotifyHwnd%
		
		AltTabEmu()
		}
	else
		{
			;ToolTip, else
		Run, Spotify.exe, % A_AppData "\Spotify"
		WinActivate, ahk_exe Spotify.exe
		}
	return
}




; ON/OFF use win mediakeys to see OSD when changed songs
NumpadDiv::
{
	;ToolTip, globalmediakeys=%globalmediakeys%
	sleep, 1000
	if(globalmediakeys = 1)
	{
	globalmediakeys := 0
	ToolTip, GlobalMediaKeys OFF
	sleep, 1000
	ToolTip
	return
	}
	if(globalmediakeys = 0)
	{
	globalmediakeys := 1
	ToolTip, GlobalMediaKeys ON
	sleep, 1000
	ToolTip
	return
	}
}

#IfWinNotExist, ahk_exe Spotify.exe
Numpad5::
{
	WinGet, activewin , ID, A
RunWait Spotify.exe, % A_AppData "\Spotify"
sleep, 2000
	global wntitle :="ahk_id"getSpotifyHwnd()
	spotifyKey("{Space}")
	send, !{tab}
WinActivate, "ahk_id"activewin
return
}
Numpad0::
{
	ToolTip, notexist
RunWait Spotify.exe, % A_AppData "\Spotify"
sleep, 2000
	global wntitle :="ahk_id"getSpotifyHwnd()
	send, !{tab}
	sleep, 200
	WinActivate, ahk_exe Spotify.exe
	return
}
#If


#If MouseIsOverButtons(wntitle)
LButton::
	CoordMode, Mouse
	MouseGetPos, mX, mY, Hwndc, , 2
	winGetPos, mmX, mmY,,, ahk_id%Hwndc%
	hwndnow := "ahk_id"Hwndc
	posX := mX - mmX
if(posX < 57 OR posX > 113 AND hwndnow != wntitle)
	{
	WinHide, A
	AltTabEmu()
	return
	}
if(posX > 57 OR posX < 113 AND hwndnow != wntitle)
	WinGet, MinMax, MinMax, A
if(MinMax = 0)
	WinMaximize, A
else
	WinRestore, A
return


;Permanent extra win media hotkeys
>!>Space::Send      {Media_Play_Pause}      ; AltR + Space
>!>Left::Send       {Media_Prev}            ; AltR + Left
>!>Right::Send      {Media_Next}            ; AltR + Right
>!>Delete::Send     {Volume_Mute}           ; AltR + Delete
>!>Up::Send         {Volume_Up}             ; AltR + Up
>!>Down::Send       {Volume_Down}           ; AltR + Down






; Get the HWND of the Spotify main window.
getSpotifyHwnd() {
	SetFormat, IntegerFast, hex
	WinGet, spotifyHwnd, ID, ahk_exe spotify.exe
	; We need the app's third top level window, so get next twice.
	spotifyHwnd := DllCall("GetWindow", "uint", spotifyHwnd, "uint", 2)
	spotifyHwnd := DllCall("GetWindow", "uint", spotifyHwnd, "uint", 2)
	Return spotifyHwnd
}


; Send a key to Spotify.
spotifyKey(key) {
		;spotifyHwnd := getSpotifyHwnd()
		; Chromium ignores keys when it isn't focused.
		; Focus the document window without bringing the app to the foreground.
		
	IfWinNotActive, ahk_exe Spotify.exe
		{
			ControlFocus, Chrome_RenderWidgetHostHWND1,  %wntitle%
			;ToolTip, contkeyssend
		ControlSend, , %key%, %wntitle%
		;AltTabEmu()
		return
		}
	IfWinActive, ahk_exe Spotify.exe
	{
		Send,  %key%
		;ToolTip, keyssend
		return
	}
	Return
}

AltTabEmu() {
	hWnd := WinExist("A")
	Loop {
		hWnd := DllCall("GetWindow", "Uint", hWnd, "Ptr", 2)
		WinGetTitle Title, ahk_id %hWnd%
	} Until Title
	WinActivate ahk_id %hWnd%
}

MouseIsOverButtons(WinTitle) {
MouseGetPos, , , Win
winGetPos, mmX, mmY, w,h, ahk_id%win%
WinGet, procname, ProcessName , ahk_id%win%
;ToolTip, % WinTitle "`n" Win "`n" mmX "x" mmY "`n" w "x" h "`n" "wntitle: " wntitle "`n""procname: " procname
	return (procname = "Spotify.exe" AND WinTitle != "ahk_id"Win AND w > 100 AND w < 200)
   }
   
   
   
; http://www.autohotkey.com/board/topic/77303-acc-library-ahk-l-updated-09272012/
; https://dl.dropbox.com/u/47573473/Web%20Server/AHK_L/Acc.ahk
;------------------------------------------------------------------------------
; Acc.ahk Standard Library
; by Sean
; Updated by jethrow:
; 	Modified ComObjEnwrap params from (9,pacc) --> (9,pacc,1)
; 	Changed ComObjUnwrap to ComObjValue in order to avoid AddRef (thanks fincs)
; 	Added Acc_GetRoleText & Acc_GetStateText
; 	Added additional functions - commented below
; 	Removed original Acc_Children function
; last updated 2/25/2010
;------------------------------------------------------------------------------

Acc_Init()
{
	Static	h
	If Not	h
		h:=DllCall("LoadLibrary","Str","oleacc","Ptr")
}
Acc_ObjectFromEvent(ByRef _idChild_, hWnd, idObject, idChild)
{
	Acc_Init()
	If	DllCall("oleacc\AccessibleObjectFromEvent", "Ptr", hWnd, "UInt", idObject, "UInt", idChild, "Ptr*", pacc, "Ptr", VarSetCapacity(varChild,8+2*A_PtrSize,0)*0+&varChild)=0
	Return	ComObjEnwrap(9,pacc,1), _idChild_:=NumGet(varChild,8,"UInt")
}

Acc_ObjectFromPoint(ByRef _idChild_ = "", x = "", y = "")
{
	Acc_Init()
	If	DllCall("oleacc\AccessibleObjectFromPoint", "Int64", x==""||y==""?0*DllCall("GetCursorPos","Int64*",pt)+pt:x&0xFFFFFFFF|y<<32, "Ptr*", pacc, "Ptr", VarSetCapacity(varChild,8+2*A_PtrSize,0)*0+&varChild)=0
	Return	ComObjEnwrap(9,pacc,1), _idChild_:=NumGet(varChild,8,"UInt")
}

Acc_ObjectFromWindow(hWnd, idObject = -4)
{
	Acc_Init()
	If	DllCall("oleacc\AccessibleObjectFromWindow", "Ptr", hWnd, "UInt", idObject&=0xFFFFFFFF, "Ptr", -VarSetCapacity(IID,16)+NumPut(idObject==0xFFFFFFF0?0x46000000000000C0:0x719B3800AA000C81,NumPut(idObject==0xFFFFFFF0?0x0000000000020400:0x11CF3C3D618736E0,IID,"Int64"),"Int64"), "Ptr*", pacc)=0
	Return	ComObjEnwrap(9,pacc,1)
}

Acc_WindowFromObject(pacc)
{
	If	DllCall("oleacc\WindowFromAccessibleObject", "Ptr", IsObject(pacc)?ComObjValue(pacc):pacc, "Ptr*", hWnd)=0
	Return	hWnd
}

Acc_GetRoleText(nRole)
{
	nSize := DllCall("oleacc\GetRoleText", "Uint", nRole, "Ptr", 0, "Uint", 0)
	VarSetCapacity(sRole, (A_IsUnicode?2:1)*nSize)
	DllCall("oleacc\GetRoleText", "Uint", nRole, "str", sRole, "Uint", nSize+1)
	Return	sRole
}

Acc_GetStateText(nState)
{
	nSize := DllCall("oleacc\GetStateText", "Uint", nState, "Ptr", 0, "Uint", 0)
	VarSetCapacity(sState, (A_IsUnicode?2:1)*nSize)
	DllCall("oleacc\GetStateText", "Uint", nState, "str", sState, "Uint", nSize+1)
	Return	sState
}

Acc_SetWinEventHook(eventMin, eventMax, pCallback)
{
	Return	DllCall("SetWinEventHook", "Uint", eventMin, "Uint", eventMax, "Uint", 0, "Ptr", pCallback, "Uint", 0, "Uint", 0, "Uint", 0)
}

Acc_UnhookWinEvent(hHook)
{
	Return	DllCall("UnhookWinEvent", "Ptr", hHook)
}
/*	Win Events:
	pCallback := RegisterCallback("WinEventProc")
	WinEventProc(hHook, event, hWnd, idObject, idChild, eventThread, eventTime)
	{
		Critical
		Acc := Acc_ObjectFromEvent(_idChild_, hWnd, idObject, idChild)
		; Code Here:
	}
*/

; Written by jethrow
Acc_Role(Acc, ChildId=0) {
	try return ComObjType(Acc,"Name")="IAccessible"?Acc_GetRoleText(Acc.accRole(ChildId)):"invalid object"
}
Acc_State(Acc, ChildId=0) {
	try return ComObjType(Acc,"Name")="IAccessible"?Acc_GetStateText(Acc.accState(ChildId)):"invalid object"
}
Acc_Location(Acc, ChildId=0, byref Position="") { ; adapted from Sean's code
	try Acc.accLocation(ComObj(0x4003,&x:=0), ComObj(0x4003,&y:=0), ComObj(0x4003,&w:=0), ComObj(0x4003,&h:=0), ChildId)
	catch
		return
	Position := "x" NumGet(x,0,"int") " y" NumGet(y,0,"int") " w" NumGet(w,0,"int") " h" NumGet(h,0,"int")
	return	{x:NumGet(x,0,"int"), y:NumGet(y,0,"int"), w:NumGet(w,0,"int"), h:NumGet(h,0,"int")}
}
Acc_Parent(Acc) { 
	try parent:=Acc.accParent
	return parent?Acc_Query(parent):
}
Acc_Child(Acc, ChildId=0) {
	try child:=Acc.accChild(ChildId)
	return child?Acc_Query(child):
}
Acc_Query(Acc) { ; thanks Lexikos - www.autohotkey.com/forum/viewtopic.php?t=81731&p=509530#509530
	try return ComObj(9, ComObjQuery(Acc,"{618736e0-3c3d-11cf-810c-00aa00389b71}"), 1)
}
Acc_Error(p="") {
	static setting:=0
	return p=""?setting:setting:=p
}
Acc_Children(Acc) {
	if ComObjType(Acc,"Name") != "IAccessible"
		ErrorLevel := "Invalid IAccessible Object"
	else {
		Acc_Init(), cChildren:=Acc.accChildCount, Children:=[]
		if DllCall("oleacc\AccessibleChildren", "Ptr",ComObjValue(Acc), "Int",0, "Int",cChildren, "Ptr",VarSetCapacity(varChildren,cChildren*(8+2*A_PtrSize),0)*0+&varChildren, "Int*",cChildren)=0 {
			Loop %cChildren%
				i:=(A_Index-1)*(A_PtrSize*2+8)+8, child:=NumGet(varChildren,i), Children.Insert(NumGet(varChildren,i-8)=9?Acc_Query(child):child), NumGet(varChildren,i-8)=9?ObjRelease(child):
			return Children.MaxIndex()?Children:
		} else
			ErrorLevel := "AccessibleChildren DllCall Failed"
	}
	if Acc_Error()
		throw Exception(ErrorLevel,-1)
}
Acc_ChildrenByRole(Acc, Role) {
	if ComObjType(Acc,"Name")!="IAccessible"
		ErrorLevel := "Invalid IAccessible Object"
	else {
		Acc_Init(), cChildren:=Acc.accChildCount, Children:=[]
		if DllCall("oleacc\AccessibleChildren", "Ptr",ComObjValue(Acc), "Int",0, "Int",cChildren, "Ptr",VarSetCapacity(varChildren,cChildren*(8+2*A_PtrSize),0)*0+&varChildren, "Int*",cChildren)=0 {
			Loop %cChildren% {
				i:=(A_Index-1)*(A_PtrSize*2+8)+8, child:=NumGet(varChildren,i)
				if NumGet(varChildren,i-8)=9
					AccChild:=Acc_Query(child), ObjRelease(child), Acc_Role(AccChild)=Role?Children.Insert(AccChild):
				else
					Acc_Role(Acc, child)=Role?Children.Insert(child):
			}
			return Children.MaxIndex()?Children:, ErrorLevel:=0
		} else
			ErrorLevel := "AccessibleChildren DllCall Failed"
	}
	if Acc_Error()
		throw Exception(ErrorLevel,-1)
}
Acc_Get(Cmd, ChildPath="", ChildID=0, WinTitle="", WinText="", ExcludeTitle="", ExcludeText="") {
	static properties := {Action:"DefaultAction", DoAction:"DoDefaultAction", Keyboard:"KeyboardShortcut"}
	AccObj :=   IsObject(WinTitle)? WinTitle
			:   Acc_ObjectFromWindow( WinExist(WinTitle, WinText, ExcludeTitle, ExcludeText), 0 )
	if ComObjType(AccObj, "Name") != "IAccessible"
		ErrorLevel := "Could not access an IAccessible Object"
	else {
		StringReplace, ChildPath, ChildPath, _, %A_Space%, All
		AccError:=Acc_Error(), Acc_Error(true)
		Loop Parse, ChildPath, ., %A_Space%
			try {
				if A_LoopField is digit
					Children:=Acc_Children(AccObj), m2:=A_LoopField ; mimic "m2" output in else-statement
				else
					RegExMatch(A_LoopField, "(\D*)(\d*)", m), Children:=Acc_ChildrenByRole(AccObj, m1), m2:=(m2?m2:1)
				if Not Children.HasKey(m2)
					throw
				AccObj := Children[m2]
			} catch {
				ErrorLevel:="Cannot access ChildPath Item #" A_Index " -> " A_LoopField, Acc_Error(AccError)
				if Acc_Error()
					throw Exception("Cannot access ChildPath Item", -1, "Item #" A_Index " -> " A_LoopField)
				return
			}
		Acc_Error(AccError)
		StringReplace, Cmd, Cmd, %A_Space%, , All
		properties.HasKey(Cmd)? Cmd:=properties[Cmd]:
		try {
			if (Cmd = "Location")
				AccObj.accLocation(ComObj(0x4003,&x:=0), ComObj(0x4003,&y:=0), ComObj(0x4003,&w:=0), ComObj(0x4003,&h:=0), ChildId)
			  , ret_val := "x" NumGet(x,0,"int") " y" NumGet(y,0,"int") " w" NumGet(w,0,"int") " h" NumGet(h,0,"int")
			else if (Cmd = "Object")
				ret_val := AccObj
			else if Cmd in Role,State
				ret_val := Acc_%Cmd%(AccObj, ChildID+0)
			else if Cmd in ChildCount,Selection,Focus
				ret_val := AccObj["acc" Cmd]
			else
				ret_val := AccObj["acc" Cmd](ChildID+0)
		} catch {
			ErrorLevel := """" Cmd """ Cmd Not Implemented"
			if Acc_Error()
				throw Exception("Cmd Not Implemented", -1, Cmd)
			return
		}
		return ret_val, ErrorLevel:=0
	}
	if Acc_Error()
		throw Exception(ErrorLevel,-1)
}

2 (изменено: OmTatSat, 2021-08-15 01:01:53)

Re: AHK: Spotify Global hot keys

Добавил хоткеи кнопок:
Добавить в медиатеку. Like
Мне не нравится этот трек. Dislike

3 (изменено: OmTatSat, 2021-08-15 10:32:52)

Re: AHK: Spotify Global hot keys

Фикс кнопки Numpad5 при выключенном Spotify.
Фикс кнопки Numpad3.

4

Re: AHK: Spotify Global hot keys

Ни из названия темы, ни из первого сообщения не понял, о чём тема.

5 (изменено: OmTatSat, 2021-08-15 16:39:40)

Re: AHK: Spotify Global hot keys

ypppu наверное из-за того, что вы не пользуетесь программой Spotify.
В ней нет возможности управлять хоткеями при не активном окне. И в сети не видел полностью устраивающего решения этой проблемы.
Так-то сервис топовый, хорошо подбирает музыку под вкусы человека.