What this script does:-
When a pip window is maximized, it restores and brings to front all other pip windows and mutes them
Cycle/Maximize the pip windows using PgUp/PgDn
Defines various keyboard controls to manage the maximized/active pip window
Code: Select all
/* ffpiper.ahk
Firefox picture-in-picture manager/controller to manage multiple pip windows, each launched in a separate profile
about:config - set media.videocontrols.picture-in-picture.keyboard-controls.enabled to true
about:profiles - create and launch multiple profiles and pip each video from a separate profile
*/
#SingleInstance, Force
OnExit("exitFunc")
global G := {}
G.taskbar := 1
G.vols := {}
G.wpips := {}
global wctrl_id := 0
wctrl_index := 1
Loop {
Sleep, 300
wa_id := WinExist("A")
WinGetClass, wa_class, ahk_id %wa_id%
WinGetTitle, wa_title, ahk_id %wa_id%
WinGetPos, wa_x, wa_y, wa_w, wa_h, ahk_id %wa_id%
wa_is_pip := (wa_class = "MozillaDialogClass" and wa_title = "Picture-in-Picture")
if (!wa_is_pip)
continue
wctrl_id := wa_id
if (G.vols[wctrl_id] = "")
G.vols[wctrl_id] := 10
WinGet, wpip_ids, List, Picture-in-Picture ahk_class MozillaDialogClass
wpip_ids_str := ""
Loop, %wpip_ids% {
wpip_ids_str .= wpip_ids%A_Index% . "|"
}
Sort, wpip_ids_str, N D|
G.wpips := StrSplit(Trim(wpip_ids_str,"|"), "|")
For wi_index, wi_id in G.wpips {
if (wi_id = wctrl_id)
wctrl_index := wi_index
WinGetPos, wi_x, wi_y, wi_w, wi_h, ahk_id %wi_id%
if (wi_w = A_ScreenWidth) {
if (A_TimeIdle > 5000) {
CoordMode, Mouse, Screen
MouseGetPos, m_x, m_y
if (m_x < A_ScreenWidth-1 and m_y < A_ScreenHeight-1) {
hideControls()
}
}
wctrl_id := wi_id
wctrl_index := wi_index
hideTaskbar()
break
} else if (wi_index = G.wpips.MaxIndex()) {
showTaskbar()
}
}
if (wa_w = A_ScreenWidth and (G.wpips.Length() > 1)) {
wctrl_id := wa_id
SendInput ^{Up}
For wi_index, wi_id in G.wpips {
if (wi_id = wctrl_id)
wctrl_index := wi_index
WinGetPos, wi_x, wi_y, wi_w, wi_h, ahk_id %wi_id%
if (wi_w = A_ScreenWidth and wi_id != wa_id) {
restoreWin(wi_id)
WinActivate, ahk_id %wi_id%
SendInput ^{Down}
} else if (wi_w < A_ScreenWidth){
WinActivate, ahk_id %wi_id%
SendInput ^{Down}
}
}
}
}
Pause::
CoordMode, ToolTip, Screen
ToolTip, ffpiper:exit, A_ScreenWidth // 2, A_ScreenHeight // 2
sleep, 1000
ExitApp
return
#IfWinActive, Picture-in-Picture ahk_class MozillaDialogClass
{
p::
ControlSend,, {space}, ahk_id %wctrl_id%
osd("play/pause")
return
[::
ControlSend,, {Ctrl down}{left}{Ctrl up}, ahk_id %wctrl_id%
osd("seek : <<<")
return
]::
ControlSend,, {Ctrl down}{right}{Ctrl up}, ahk_id %wctrl_id%
osd("seek : >>>")
return
Backspace::
ControlSend,, {Home}, ahk_id %wctrl_id%
osd("seek : |<<")
return
\::
ControlSend,, {End}, ahk_id %wctrl_id%
osd("seek : >>|")
return
PgUp::
restoreWin(wctrl_id)
wctrl_index-1 < 1 ? wctrl_index := G.wpips.MaxIndex() : wctrl_index--
w_id := G.wpips[wctrl_index]
WinActivate, ahk_id %w_id%
maximizeWin(w_id)
WinActivate, ahk_id %w_id%
hideControls()
return
PgDn::
restoreWin(wctrl_id)
wctrl_index+1 > G.wpips.MaxIndex() ? wctrl_index := 1 : wctrl_index++
w_id := G.wpips[wctrl_index]
WinActivate, ahk_id %w_id%
maximizeWin(w_id)
WinActivate, ahk_id %w_id%
hideControls()
return
NumpadEnter::
WinGetPos, w_x, w_y, w_w, w_h, ahk_id %wctrl_id%
if (w_w < A_ScreenWidth) {
maximizeWin(wctrl_id)
WinActivate, ahk_id %wctrl_id%
hideControls()
} else {
restoreWin(wctrl_id)
}
return
m::
Numpad0::
ControlSend,, {Ctrl down}{Down}{Ctrl up}, ahk_id %wctrl_id%
osd("VOLUME : mute")
return
u::
n::
NumpadDot::
ControlSend,, {Ctrl down}{Up}{Ctrl up}, ahk_id %wctrl_id%
osd("VOLUME : unmute")
return
WheelDown::
NumpadSub::
ControlSend,, {Down}, ahk_id %wctrl_id%
if (G.vols[wctrl_id] > 0)
G.vols[wctrl_id] -= 1
osd("VOLUME : " . G.vols[wctrl_id] * 10)
return
WheelUp::
NumpadAdd::
ControlSend,, {Up}, ahk_id %wctrl_id%
if (G.vols[wctrl_id] < 10)
G.vols[wctrl_id] += 1
osd("VOLUME : " . G.vols[wctrl_id] * 10)
return
Numpad1::
Numpad2::
Numpad3::
Numpad4::
Numpad5::
Numpad6::
Numpad7::
Numpad8::
Numpad9::
num := SubStr(A_ThisHotkey,0)
ControlSend,, {Down 10}{Up %num%}, ahk_id %wctrl_id%
G.vols[wctrl_id] := num
osd("VOLUME : " . num*10)
return
NumpadMult::
ControlSend,, {Up 10}, ahk_id %wctrl_id%
G.vols[wctrl_id] := 10
osd("VOLUME : 100")
return
}
exitFunc() {
WinShow ahk_class Shell_TrayWnd
WinShow Start ahk_class Button
}
osd(text,time:=2000,options:="") {
WinGetPos w_x, w_y, w_w, w_h, ahk_id %wctrl_id%
CoordMode, ToolTip, Screen
ToolTip, %text%, w_x + (w_w//2 - (9*StrLen(text))//2), w_y ;adjust according to font size
SetTimer, RemoveOsd, %time%
return
RemoveOsd:
SetTimer, RemoveOsd, Off
ToolTip
return
}
restoreWin(id) {
PostMessage, 0x112, 0xF120,,,ahk_id %id%
}
maximizeWin(id) {
;PostMessage, 0x112, 0xF030,,,ahk_id %id%
WinGetPos, w_x, w_y, w_w, w_h, ahk_id %id%
x := w_w // 2
y := w_h // 2
ControlClick, x%x% y%y%, ahk_id %id%, , LEFT, 2
}
hideControls() {
MouseMove, A_ScreenWidth // 2 , A_ScreenHeight-100
MouseMove, A_ScreenWidth // 2 , A_ScreenHeight
}
hideTaskbar() {
if (G.taskbar) {
WinHide ahk_class Shell_TrayWnd
WinHide Start ahk_class Button
G.taskbar := 0
}
}
showTaskbar() {
if (!G.taskbar) {
WinShow ahk_class Shell_TrayWnd
WinShow Start ahk_class Button
G.taskbar := 1
}
}
/* Firefox Picture-in-Picture controls
Space - Toggle Play/Pause
Down Arrow - Volume Decrease
Up Arrow - Volume Increase
Accel + Down Arrow - Mute
Accel + Up Arrow - Unmute
Left Arrow - Seek back 15 seconds
Accel + Left Arrow - Seek back 10%
Right Arrow - Seek forward 15 seconds
Accel + Right Arrow - Seek forward 10%
Home - Seek to beginning
End - Seek to end
Accel is CtrlKey or MetaKey for macOS
*/