ffpiper: Firefox multi pip (picture in picture) manager/controller

Post your working scripts, libraries and tools for AHK v1.1 and older
audience
Posts: 1
Joined: 15 Aug 2020, 18:12

ffpiper: Firefox multi pip (picture in picture) manager/controller

15 Aug 2020, 18:24

Just sharing a script to manage multiple pip windows in firefox. Only used on windows 7 and single monitor. Currently Firefox only supports 1 pip window but one hack to get around that is to launch multiple pip windows from separate profiles. But even that doesn't quite work as you expect - for example if you maximize a pip window, it will cover all the other pip windows.

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
*/
smbs
Posts: 98
Joined: 27 Feb 2014, 11:07

Re: ffpiper: Firefox multi pip (picture in picture) manager/controller

19 Sep 2021, 14:14

Hi there Firefox now supports multiple pips
Can you update your script?
Many thanx

Return to “Scripts and Functions (v1)”

Who is online

Users browsing this forum: No registered users and 100 guests