Capture, decrypt, and restream toolkit with compiled-in app modules (RTE, TG4, BBC), on-device MITM proxy, streamd control plane, and www. BBC module.yaml is published (clear streams); other module values stay local.
152 lines
3.9 KiB
Go
152 lines
3.9 KiB
Go
package tg4
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
|
|
"drmdecryption/adb"
|
|
"drmdecryption/uiflow"
|
|
)
|
|
|
|
// openLivePage navigates to the Live page: if a menu entry is visible tap it,
|
|
// otherwise open the side drawer first. Ported from the Starlark hook this module
|
|
// used to carry, with every selector and coordinate now coming from config.
|
|
func (a *App) openLivePage(c *adb.Client) error {
|
|
c.DismissShadeIfFocused()
|
|
nodes, err := c.DumpUI(a.CacheDir())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if a.onLivePage(nodes) {
|
|
fmt.Println("[*] already on Live")
|
|
return nil
|
|
}
|
|
if live := a.findMenuLive(nodes); live != nil {
|
|
fmt.Printf("[*] tap Live @ %d,%d\n", live.CX(), live.CY())
|
|
_ = c.Tap(live.CX(), live.CY())
|
|
time.Sleep(time.Duration(a.cfg.Timeouts.AfterLiveTap))
|
|
return nil
|
|
}
|
|
|
|
if !a.openDrawer(c) {
|
|
fmt.Printf("[*] drawer fallback tap %d,%d\n", a.cfg.UI.DrawerFallbackX, a.cfg.UI.DrawerFallbackY)
|
|
_ = c.Tap(a.cfg.UI.DrawerFallbackX, a.cfg.UI.DrawerFallbackY)
|
|
time.Sleep(time.Duration(a.cfg.Timeouts.AfterDrawer))
|
|
}
|
|
|
|
deadline := time.Now().Add(time.Duration(a.cfg.Timeouts.LiveNav))
|
|
for time.Now().Before(deadline) {
|
|
nodes, err := c.DumpUI(a.CacheDir())
|
|
if err == nil {
|
|
if live := a.findMenuLive(nodes); live != nil {
|
|
fmt.Printf("[*] tap Live @ %d,%d\n", live.CX(), live.CY())
|
|
_ = c.Tap(live.CX(), live.CY())
|
|
time.Sleep(time.Duration(a.cfg.Timeouts.AfterLiveTap))
|
|
break
|
|
}
|
|
a.openDrawer(c)
|
|
}
|
|
time.Sleep(800 * time.Millisecond)
|
|
}
|
|
|
|
deadline = time.Now().Add(time.Duration(a.cfg.Timeouts.LiveNav))
|
|
for time.Now().Before(deadline) {
|
|
if nodes, err := c.DumpUI(a.CacheDir()); err == nil && a.onLivePage(nodes) {
|
|
fmt.Printf("[*] Live page ready\n")
|
|
return nil
|
|
}
|
|
time.Sleep(800 * time.Millisecond)
|
|
}
|
|
return fmt.Errorf("Live page did not open")
|
|
}
|
|
|
|
// onLivePage is true when the Live title is showing and at least one content card
|
|
// is on screen.
|
|
func (a *App) onLivePage(nodes []adb.Node) bool {
|
|
hasTitle := false
|
|
for _, n := range nodes {
|
|
if !strings.EqualFold(strings.TrimSpace(n.Text), a.cfg.UI.LiveLabel) {
|
|
continue
|
|
}
|
|
if strings.Contains(n.ResourceID, a.cfg.UI.LiveTitleResource) ||
|
|
(n.Y1 < a.cfg.UI.TitleMaxY && !n.Clickable) {
|
|
hasTitle = true
|
|
break
|
|
}
|
|
}
|
|
if !hasTitle {
|
|
return false
|
|
}
|
|
return len(uiflow.Cards(nodes, a.cfg.Cards)) > 0
|
|
}
|
|
|
|
// openDrawer opens the side menu. Returns false when the drawer button could not
|
|
// be identified, so the caller can fall back to a fixed tap.
|
|
func (a *App) openDrawer(c *adb.Client) bool {
|
|
nodes, err := c.DumpUI(a.CacheDir())
|
|
if err != nil {
|
|
return false
|
|
}
|
|
// Already open?
|
|
for _, n := range nodes {
|
|
desc := strings.TrimSpace(n.Desc)
|
|
for _, d := range a.cfg.UI.DrawerDesc {
|
|
if desc == d && d == "Closed" {
|
|
return true
|
|
}
|
|
}
|
|
if strings.EqualFold(strings.TrimSpace(n.Text), a.cfg.UI.LiveLabel) &&
|
|
n.Clickable && n.X2 < a.cfg.UI.MenuMaxX {
|
|
return true
|
|
}
|
|
}
|
|
ham := a.findDrawerButton(nodes)
|
|
if ham == nil {
|
|
return false
|
|
}
|
|
fmt.Printf("[*] tap menu %q @ %d,%d\n", ham.Desc, ham.CX(), ham.CY())
|
|
_ = c.Tap(ham.CX(), ham.CY())
|
|
time.Sleep(time.Duration(a.cfg.Timeouts.AfterDrawer))
|
|
return true
|
|
}
|
|
|
|
func (a *App) findDrawerButton(nodes []adb.Node) *adb.Node {
|
|
for i := range nodes {
|
|
n := &nodes[i]
|
|
if !n.Clickable {
|
|
continue
|
|
}
|
|
desc := strings.TrimSpace(n.Desc)
|
|
for _, d := range a.cfg.UI.DrawerDesc {
|
|
if desc == d {
|
|
return n
|
|
}
|
|
}
|
|
}
|
|
// Geometric fallback: a small clickable node in the top-left corner.
|
|
for i := range nodes {
|
|
n := &nodes[i]
|
|
if n.Clickable && n.X1 < 50 && n.Y1 < 200 && n.X2 < 200 && n.Y2 < 300 {
|
|
return n
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// findMenuLive locates the drawer's Live entry.
|
|
func (a *App) findMenuLive(nodes []adb.Node) *adb.Node {
|
|
for i := range nodes {
|
|
n := &nodes[i]
|
|
if !n.Clickable {
|
|
continue
|
|
}
|
|
if !strings.EqualFold(strings.TrimSpace(n.Text), a.cfg.UI.LiveLabel) {
|
|
continue
|
|
}
|
|
if n.X2 <= a.cfg.UI.MenuMaxX && n.Y1 > a.cfg.UI.MenuMinY && n.Y1 < a.cfg.UI.MenuMaxY {
|
|
return n
|
|
}
|
|
}
|
|
return nil
|
|
}
|