Read personal dev settings from git-ignored local config
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -6,3 +6,9 @@ local.properties
|
|||||||
*.aab
|
*.aab
|
||||||
captures/
|
captures/
|
||||||
.idea/
|
.idea/
|
||||||
|
|
||||||
|
# Personal, machine-local files: dev settings (local/dev.env), notes, test data, backups. Never in git.
|
||||||
|
/local/
|
||||||
|
# Debug tour built from one's own server by tools/tour/build_tour.py.
|
||||||
|
/app/src/debug/assets/tour.json
|
||||||
|
__pycache__/
|
||||||
|
|||||||
25
tools/dev.env.example
Normal file
25
tools/dev.env.example
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
# Personal settings of the dev tools in tools/. Copy to local/dev.env (the whole local/ is git-ignored) and fill in.
|
||||||
|
# Plain KEY=value lines, no quotes and no $expansion: the shell scripts and the Python ones both read this file.
|
||||||
|
# A variable already set in the environment wins over the file.
|
||||||
|
|
||||||
|
# JDK 21 for Gradle and the desktop app (otherwise the JAVA_HOME of the environment, or java on PATH).
|
||||||
|
#JAVA_HOME=/path/to/jdk-21
|
||||||
|
# adb of the Android SDK (otherwise adb on PATH, or ~/Android/Sdk/platform-tools/adb).
|
||||||
|
#ADB=/path/to/Android/Sdk/platform-tools/adb
|
||||||
|
|
||||||
|
# The test TV for tools/tv/env.sh: adb address ip:port (classic adb over TCP is :5555; the port of
|
||||||
|
# «Беспроводная отладка» changes after a reboot).
|
||||||
|
TV_ADB=
|
||||||
|
# Jellyfin server the tools talk to, by its LAN address, e.g. http://192.168.1.10:8096.
|
||||||
|
JF_URL=
|
||||||
|
# Test account on that server. Test runs change its watch state; tools/linux/watchdata.py puts it back.
|
||||||
|
JF_USER=
|
||||||
|
|
||||||
|
# Series id for the baseline-profile walk over a series page (gen_profile in tools/tv/env.sh).
|
||||||
|
PROFILE_SERIES=
|
||||||
|
# Three consecutive episodes of one season, space-separated ids (S1E2 S1E3 S1E4), for tools/linux/k2.py.
|
||||||
|
K2_EPISODES=
|
||||||
|
# Any 1080p video item for tools/linux/l0/float_test.py.
|
||||||
|
L0_FLOAT_ITEM=
|
||||||
|
# Test cases of tools/linux/l0/run_all.sh, relative to the repo root: lines "<name> <itemId> [run_case.py options]".
|
||||||
|
L0_CASES=local/l0-cases.txt
|
||||||
28
tools/devenv.py
Normal file
28
tools/devenv.py
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
"""Personal settings of the dev tools: local/dev.env (git-ignored; template tools/dev.env.example).
|
||||||
|
|
||||||
|
A variable already set in the environment wins over the file.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||||
|
|
||||||
|
|
||||||
|
def load():
|
||||||
|
path = os.path.join(ROOT, "local", "dev.env")
|
||||||
|
if not os.path.exists(path):
|
||||||
|
return
|
||||||
|
for line in open(path, encoding="utf-8"):
|
||||||
|
line = line.strip()
|
||||||
|
if line and not line.startswith("#") and "=" in line:
|
||||||
|
k, v = line.split("=", 1)
|
||||||
|
os.environ.setdefault(k.strip(), v.strip())
|
||||||
|
|
||||||
|
|
||||||
|
def need(name):
|
||||||
|
"""The variable's value, or exit with a hint where to set it."""
|
||||||
|
load()
|
||||||
|
value = os.environ.get(name)
|
||||||
|
if not value:
|
||||||
|
sys.exit(f"{name} is not set: put it in local/dev.env (see tools/dev.env.example)")
|
||||||
|
return value
|
||||||
13
tools/devenv.sh
Normal file
13
tools/devenv.sh
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# Personal settings of the dev tools: local/dev.env (git-ignored; template tools/dev.env.example).
|
||||||
|
# Sourced by the shell scripts in tools/; a variable already set in the environment wins over the file.
|
||||||
|
_devenv_load() {
|
||||||
|
local f k v
|
||||||
|
f="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/local/dev.env"
|
||||||
|
[ -f "$f" ] || return 0
|
||||||
|
while IFS='=' read -r k v || [ -n "$k" ]; do
|
||||||
|
case $k in '' | \#*) continue ;; esac
|
||||||
|
[ -n "${!k+x}" ] || export "$k=$v"
|
||||||
|
done <"$f"
|
||||||
|
}
|
||||||
|
_devenv_load
|
||||||
|
unset -f _devenv_load
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
# (tools/linux/stand.sh) points them at its own directory.
|
# (tools/linux/stand.sh) points them at its own directory.
|
||||||
set -e
|
set -e
|
||||||
R=$(cd "$(dirname "$0")/../.." && pwd)
|
R=$(cd "$(dirname "$0")/../.." && pwd)
|
||||||
export JAVA_HOME=${JAVA_HOME:-$HOME/.jdks/jdk-21.0.12.1+1}
|
source "$R/tools/devenv.sh" # JAVA_HOME from local/dev.env, if set there
|
||||||
[ -n "$DESKTOP_NOBUILD" ] || (cd "$R" && ./gradlew -q :desktop:classes :desktop:desktopClasspath)
|
[ -n "$DESKTOP_NOBUILD" ] || (cd "$R" && ./gradlew -q :desktop:classes :desktop:desktopClasspath)
|
||||||
exec "$JAVA_HOME/bin/java" --add-opens=java.desktop/sun.awt.X11=ALL-UNNAMED --add-opens=java.desktop/sun.awt=ALL-UNNAMED \
|
exec "${JAVA_HOME:+$JAVA_HOME/bin/}java" --add-opens=java.desktop/sun.awt.X11=ALL-UNNAMED --add-opens=java.desktop/sun.awt=ALL-UNNAMED \
|
||||||
-cp "$(cat "$R/desktop/build/desktop.classpath")" ru.stenfredd.sleepyowl.desktop.MainKt "$@"
|
-cp "$(cat "$R/desktop/build/desktop.classpath")" ru.stenfredd.sleepyowl.desktop.MainKt "$@"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Checkpoint К2 on the test stand (LINUX_PLAN.md §3): the player window and the system integration, end to end.
|
"""Checkpoint К2 on the test stand (docs/desktop.md): the player window and the system integration, end to end.
|
||||||
|
|
||||||
tools/linux/k2.py runs it; PASS/FAIL per check, screenshots in $STAND/shots/k2-*.png
|
tools/linux/k2.py runs it; PASS/FAIL per check, screenshots in $STAND/shots/k2-*.png
|
||||||
|
|
||||||
@@ -7,18 +7,22 @@ Everything happens on the stand's display :87 and its own D-Bus (stand.sh): the
|
|||||||
are not touched (mpv plays into ao=null), the app runs niced. The player's state is read back from MPRIS (status, position,
|
are not touched (mpv plays into ao=null), the app runs niced. The player's state is read back from MPRIS (status, position,
|
||||||
volume, title), the windows from X11 (_NET_WM_STATE, geometry, frame), the screensaver from fake_screensaver.py.
|
volume, title), the windows from X11 (_NET_WM_STATE, geometry, frame), the screensaver from fake_screensaver.py.
|
||||||
The player settings start from scratch (first launch) and are put back afterwards, as is the test account's watch data.
|
The player settings start from scratch (first launch) and are put back afterwards, as is the test account's watch data.
|
||||||
|
Needs K2_EPISODES (three consecutive episodes) in local/dev.env, see tools/dev.env.example.
|
||||||
"""
|
"""
|
||||||
import os, re, shutil, subprocess, sys, time
|
import os, re, shutil, subprocess, sys, time
|
||||||
from Xlib import X, XK, display
|
from Xlib import X, XK, display
|
||||||
from Xlib.ext import xtest
|
from Xlib.ext import xtest
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
import devenv
|
||||||
|
|
||||||
R = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
R = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
STAND = os.environ.get("STAND") or os.path.expanduser("~/.cache/sleepyowl-test")
|
STAND = os.environ.get("STAND") or os.path.expanduser("~/.cache/sleepyowl-test")
|
||||||
STAND_SH = os.path.join(R, "tools/linux/stand.sh")
|
STAND_SH = os.path.join(R, "tools/linux/stand.sh")
|
||||||
PREFS = os.path.join(STAND, "config/sleepyowl")
|
PREFS = os.path.join(STAND, "config/sleepyowl")
|
||||||
LOG = os.path.join(STAND, "state/sleepyowl/log/sleepyowl.log")
|
LOG = os.path.join(STAND, "state/sleepyowl/log/sleepyowl.log")
|
||||||
E2, E3, E4 = "00000000000000000000000000000000", "00000000000000000000000000000000", "00000000000000000000000000000000"
|
E2, E3, E4 = devenv.need("K2_EPISODES").split()
|
||||||
results = []
|
results = []
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,9 @@ float_test.py <037|041>
|
|||||||
import json, os, socket, subprocess, sys, time
|
import json, os, socket, subprocess, sys, time
|
||||||
from Xlib import X, display
|
from Xlib import X, display
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", ".."))
|
||||||
|
import devenv
|
||||||
|
|
||||||
# mpv 0.37 из репозитория Mint (или распакованный из .deb без установки: apt download mpv; dpkg -x)
|
# mpv 0.37 из репозитория Mint (или распакованный из .deb без установки: apt download mpv; dpkg -x)
|
||||||
M037 = os.environ.get("MPV037", "mpv")
|
M037 = os.environ.get("MPV037", "mpv")
|
||||||
RUN = os.path.join(os.environ.get("XDG_RUNTIME_DIR", "/run/user/1000"), "l0")
|
RUN = os.path.join(os.environ.get("XDG_RUNTIME_DIR", "/run/user/1000"), "l0")
|
||||||
@@ -15,8 +18,8 @@ os.makedirs(RUN, exist_ok=True)
|
|||||||
ver = sys.argv[1]
|
ver = sys.argv[1]
|
||||||
sock = os.path.join(RUN, f"float{ver}.sock")
|
sock = os.path.join(RUN, f"float{ver}.sock")
|
||||||
if os.path.exists(sock): os.unlink(sock)
|
if os.path.exists(sock): os.unlink(sock)
|
||||||
item = "00000000000000000000000000000000"
|
item = devenv.need("L0_FLOAT_ITEM")
|
||||||
url = f"http://192.168.1.10:8096/Videos/{item}/stream?static=true&mediaSourceId={item}"
|
url = f"{devenv.need('JF_URL')}/Videos/{item}/stream?static=true&mediaSourceId={item}"
|
||||||
opts = ["--config=no", "--mute=yes", "--start=30%", "--hwdec=auto-safe", "--vo=gpu", "--gpu-api=opengl",
|
opts = ["--config=no", "--mute=yes", "--start=30%", "--hwdec=auto-safe", "--vo=gpu", "--gpu-api=opengl",
|
||||||
"--ontop", "--no-border", "--geometry=640x360-24-24", "--screen=0", "--keep-open=yes",
|
"--ontop", "--no-border", "--geometry=640x360-24-24", "--screen=0", "--keep-open=yes",
|
||||||
"--title=L0 float", f"--input-ipc-server={sock}"]
|
"--title=L0 float", f"--input-ipc-server={sock}"]
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# L0 (LINUX_PLAN.md §3, итоги §6). Нужны: mpv 0.37 ($MPV037 или mpv в PATH), Flatpak io.mpv.Mpv (0.41), python3-xlib для float_test.py.
|
# L0: проверка mpv на файлах медиатеки до начала ПК-версии (итоги в docs/desktop.md). Нужны: mpv 0.37 ($MPV037 или mpv в PATH), Flatpak io.mpv.Mpv (0.41), python3-xlib для float_test.py.
|
||||||
# Прогон всех файлов: 0.37 и 0.41 в vo=gpu (как render API), HDR/DV ещё в gpu-next 0.41, чересстрочные с деинтерлейсом.
|
# Прогон всех файлов: 0.37 и 0.41 в vo=gpu (как render API), HDR/DV ещё в gpu-next 0.41, чересстрочные с деинтерлейсом.
|
||||||
D=$(dirname "$0"); OUT=${L0_OUT:-${XDG_RUNTIME_DIR:-/tmp}/l0/out}; mkdir -p $OUT; R=$OUT/results.jsonl
|
# Список файлов — L0_CASES из local/dev.env (строки «<имя> <itemId> [опции run_case.py]»).
|
||||||
|
D=$(dirname "$0"); source "$D/../../devenv.sh"; CASES="$D/../../../${L0_CASES:?set L0_CASES in local/dev.env}"; OUT=${L0_OUT:-${XDG_RUNTIME_DIR:-/tmp}/l0/out}; mkdir -p $OUT; R=$OUT/results.jsonl
|
||||||
run() { timeout 150 python3 -I $D/run_case.py "$@" --play 15 >> $R || echo "{\"name\":\"$4\",\"ver\":\"$1\",\"vo\":\"$2\",\"error\":\"timeout\"}" >> $R; }
|
run() { timeout 150 python3 -I $D/run_case.py "$@" --play 15 >> $R || echo "{\"name\":\"$4\",\"ver\":\"$1\",\"vo\":\"$2\",\"error\":\"timeout\"}" >> $R; }
|
||||||
while read -r name id rest; do
|
while read -r name id rest; do
|
||||||
for ver in 037 041; do run $ver gpu $id $name $rest; done
|
for ver in 037 041; do run $ver gpu $id $name $rest; done
|
||||||
@@ -11,5 +12,5 @@ while read -r name id rest; do
|
|||||||
run 041 gpu $id ${name}_deint --extra=--deinterlace=yes
|
run 041 gpu $id ${name}_deint --extra=--deinterlace=yes
|
||||||
run 041 gpu $id ${name}_deintauto --extra=--deinterlace=auto;;
|
run 041 gpu $id ${name}_deintauto --extra=--deinterlace=auto;;
|
||||||
esac
|
esac
|
||||||
done < $D/cases.txt
|
done < "$CASES"
|
||||||
echo DONE
|
echo DONE
|
||||||
|
|||||||
@@ -1,16 +1,19 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""L0: один прогон файла в mpv с замерами через IPC.
|
"""L0: один прогон файла в mpv с замерами через IPC.
|
||||||
|
|
||||||
run_case.py <mpv: 037|041> <vo: gpu|gpu-next> <item_id> <name> [--sid N] [--aid N] [--sub-url URL]
|
run_case.py <mpv: 037|041> <vo: gpu|gpu-next> <item_id> <name> [--sid N] [--aid N] [--sub-url URL|/path on the server]
|
||||||
[--start SEC] [--play SEC] [--seek SEC] [--extra "--opt=val ..."]
|
[--start SEC] [--play SEC] [--seek SEC] [--extra "--opt=val ..."]
|
||||||
Печатает одну строку JSON с результатами в stdout.
|
Печатает одну строку JSON с результатами в stdout.
|
||||||
"""
|
"""
|
||||||
import argparse, json, os, shlex, socket, subprocess, sys, threading, time
|
import argparse, json, os, shlex, socket, subprocess, sys, threading, time
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), "..", ".."))
|
||||||
|
import devenv
|
||||||
|
|
||||||
OUT = os.environ.get("L0_OUT") or os.path.join(os.environ.get("XDG_RUNTIME_DIR", "/tmp"), "l0", "out")
|
OUT = os.environ.get("L0_OUT") or os.path.join(os.environ.get("XDG_RUNTIME_DIR", "/tmp"), "l0", "out")
|
||||||
# mpv 0.37 из репозитория Mint (или распакованный из .deb без установки: apt download mpv; dpkg -x)
|
# mpv 0.37 из репозитория Mint (или распакованный из .deb без установки: apt download mpv; dpkg -x)
|
||||||
M037 = os.environ.get("MPV037", "mpv")
|
M037 = os.environ.get("MPV037", "mpv")
|
||||||
SERVER = "http://192.168.1.10:8096"
|
SERVER = devenv.need("JF_URL")
|
||||||
# Токен-заглушка: сервер отдаёт static-поток и без токена; проверяем, что заголовок доходит и не ломает запрос.
|
# Токен-заглушка: сервер отдаёт static-поток и без токена; проверяем, что заголовок доходит и не ломает запрос.
|
||||||
HEADER = 'Authorization: MediaBrowser Token="0123456789abcdef0123456789abcdef"'
|
HEADER = 'Authorization: MediaBrowser Token="0123456789abcdef0123456789abcdef"'
|
||||||
|
|
||||||
@@ -49,6 +52,7 @@ if a.start is not None: opts.append(f"--start={a.start}")
|
|||||||
else: opts.append("--start=40%")
|
else: opts.append("--start=40%")
|
||||||
if a.sid: opts.append(f"--sid={a.sid}")
|
if a.sid: opts.append(f"--sid={a.sid}")
|
||||||
if a.aid: opts.append(f"--aid={a.aid}")
|
if a.aid: opts.append(f"--aid={a.aid}")
|
||||||
|
if a.sub_url and a.sub_url.startswith("/"): a.sub_url = SERVER + a.sub_url
|
||||||
if a.sub_url: opts += [f"--sub-files-append={a.sub_url}", "--sid=auto"]
|
if a.sub_url: opts += [f"--sub-files-append={a.sub_url}", "--sid=auto"]
|
||||||
opts += shlex.split(a.extra)
|
opts += shlex.split(a.extra)
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Test stand of the desktop app (LINUX_PLAN.md §3 «Как проверяем»): a private virtual display :87 with the real
|
# Test stand of the desktop app (docs/desktop.md): a private virtual display :87 with the real
|
||||||
# window manager (Muffin), so window, mouse and keyboard tests do not touch the user's screen. Muffin and the app share
|
# window manager (Muffin), so window, mouse and keyboard tests do not touch the user's screen. Muffin and the app share
|
||||||
# the stand's own D-Bus session bus: the app's MPRIS player and screensaver calls never reach the user's desktop.
|
# the stand's own D-Bus session bus: the app's MPRIS player and screensaver calls never reach the user's desktop.
|
||||||
# mpv plays into no sound device (ao=null, unless SLEEPYOWL_MPV_OPTIONS says otherwise): the stand never touches the
|
# mpv plays into no sound device (ao=null, unless SLEEPYOWL_MPV_OPTIONS says otherwise): the stand never touches the
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
# stand.sh log the app's log (sleepyowl.log)
|
# stand.sh log the app's log (sleepyowl.log)
|
||||||
# stand.sh bus CMD… run CMD on the stand's D-Bus (busctl --user, gdbus, dbus-monitor…)
|
# stand.sh bus CMD… run CMD on the stand's D-Bus (busctl --user, gdbus, dbus-monitor…)
|
||||||
#
|
#
|
||||||
# The app's files live in $STAND (default ~/.cache/sleepyowl-test): its own config with the test account,
|
# The app's files live in $STAND (default ~/.cache/sleepyowl-test): its own config with the test account (JF_USER),
|
||||||
# cache, log and lock — the user's own ~/.config/sleepyowl is never used. Gradle builds before `app` unless
|
# cache, log and lock — the user's own ~/.config/sleepyowl is never used. Gradle builds before `app` unless
|
||||||
# DESKTOP_NOBUILD=1. SLEEPYOWL_APP=<launcher> runs that instead of the Gradle build, e.g. the packaged app unpacked by
|
# DESKTOP_NOBUILD=1. SLEEPYOWL_APP=<launcher> runs that instead of the Gradle build, e.g. the packaged app unpacked by
|
||||||
# `dpkg-deb -x sleepyowl_*.deb DIR` → SLEEPYOWL_APP=DIR/opt/sleepyowl/bin/sleepyowl (also for k2.py).
|
# `dpkg-deb -x sleepyowl_*.deb DIR` → SLEEPYOWL_APP=DIR/opt/sleepyowl/bin/sleepyowl (also for k2.py).
|
||||||
@@ -22,7 +22,7 @@ set -e
|
|||||||
R=$(cd "$(dirname "$0")/../.." && pwd)
|
R=$(cd "$(dirname "$0")/../.." && pwd)
|
||||||
STAND=${STAND:-$HOME/.cache/sleepyowl-test}
|
STAND=${STAND:-$HOME/.cache/sleepyowl-test}
|
||||||
D=:87
|
D=:87
|
||||||
export JAVA_HOME=${JAVA_HOME:-$HOME/.jdks/jdk-21.0.12.1+1}
|
source "$R/tools/devenv.sh"
|
||||||
mkdir -p "$STAND/shots" "$STAND/run"
|
mkdir -p "$STAND/shots" "$STAND/run"
|
||||||
|
|
||||||
running() { [ -f "$STAND/run/$1.pid" ] && kill -0 "$(cat "$STAND/run/$1.pid")" 2>/dev/null; }
|
running() { [ -f "$STAND/run/$1.pid" ] && kill -0 "$(cat "$STAND/run/$1.pid")" 2>/dev/null; }
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""Watch data of the test account on the server, before and after a test run (LINUX_PLAN.md §3 «Как проверяем»).
|
"""Watch data of the test account on the server, before and after a test run (docs/desktop.md).
|
||||||
|
|
||||||
watchdata.py snapshot [FILE] saves the user's watch data (positions, played, counts, dates, favourites)
|
watchdata.py snapshot [FILE] saves the user's watch data (positions, played, counts, dates, favourites)
|
||||||
watchdata.py diff [FILE] what the run changed, compared with the snapshot
|
watchdata.py diff [FILE] what the run changed, compared with the snapshot
|
||||||
watchdata.py restore [FILE] puts the changed items back as they were in the snapshot
|
watchdata.py restore [FILE] puts the changed items back as they were in the snapshot
|
||||||
|
|
||||||
FILE defaults to $STAND/watchdata.json. The user is the test account: its token is taken from the test stand's saved accounts
|
FILE defaults to $STAND/watchdata.json. The user is JF_USER: its token is taken from the test stand's saved accounts
|
||||||
($STAND/config/sleepyowl/jellyfin_session.preferences_pb, STAND=~/.cache/sleepyowl-test) or from JF_TOKEN.
|
($STAND/config/sleepyowl/jellyfin_session.preferences_pb, STAND=~/.cache/sleepyowl-test) or from JF_TOKEN.
|
||||||
JF_USER picks another saved account by name. Server: JF_URL, default the LAN address.
|
Server JF_URL and user JF_USER come from local/dev.env (template tools/dev.env.example) or the environment.
|
||||||
|
|
||||||
Restore writes the saved UserData back (POST /UserItems/{id}/UserData). An item the run started from nothing (not in
|
Restore writes the saved UserData back (POST /UserItems/{id}/UserData). An item the run started from nothing (not in
|
||||||
the snapshot) is reset: played and its date removed (DELETE /UserPlayedItems), position and count back to 0.
|
the snapshot) is reset: played and its date removed (DELETE /UserPlayedItems), position and count back to 0.
|
||||||
@@ -15,9 +15,12 @@ The resume list follows LastPlayedDate, so its order comes back with the dates.
|
|||||||
"""
|
"""
|
||||||
import json, os, re, sys, urllib.request
|
import json, os, re, sys, urllib.request
|
||||||
|
|
||||||
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
||||||
|
import devenv
|
||||||
|
|
||||||
STAND = os.environ.get("STAND") or os.path.expanduser("~/.cache/sleepyowl-test")
|
STAND = os.environ.get("STAND") or os.path.expanduser("~/.cache/sleepyowl-test")
|
||||||
URL = os.environ.get("JF_URL", "http://192.168.1.10:8096")
|
URL = devenv.need("JF_URL")
|
||||||
USER = os.environ.get("JF_USER", "test")
|
USER = devenv.need("JF_USER")
|
||||||
FIELDS = ("PlaybackPositionTicks", "PlayCount", "Played", "LastPlayedDate", "IsFavorite")
|
FIELDS = ("PlaybackPositionTicks", "PlayCount", "Played", "LastPlayedDate", "IsFavorite")
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ subtitle codec) and the pairs that matter for engine choice (container x codec,
|
|||||||
Each entry also fixes which audio and subtitle track to select, so subtitle/audio codecs are actually exercised.
|
Each entry also fixes which audio and subtitle track to select, so subtitle/audio codecs are actually exercised.
|
||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
python3 tools/tour/build_tour.py --server http://192.168.1.10:8096 --token <token> [--out app/src/debug/assets/tour.json]
|
python3 tools/tour/build_tour.py --server http://<server>:8096 --token <token> [--out app/src/debug/assets/tour.json]
|
||||||
python3 tools/tour/build_tour.py --input library.json ... # reuse a saved /Items response
|
python3 tools/tour/build_tour.py --input library.json ... # reuse a saved /Items response
|
||||||
Token from the TV: `source tools/tv/env.sh; tv_token`.
|
Token from the TV: `source tools/tv/env.sh; tv_token`.
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Helpers for driving the test TV over adb. Usage: source tools/tv/env.sh
|
# Helpers for driving the test TV over adb. Usage: source tools/tv/env.sh
|
||||||
# TV_ADB: адрес из «Беспроводной отладки» (порт меняется после перезагрузки ТВ).
|
# TV_ADB (адрес ТВ), JF_URL (сервер), ADB, JAVA_HOME, PROFILE_SERIES — из local/dev.env (шаблон tools/dev.env.example).
|
||||||
export JAVA_HOME=${JAVA_HOME:-$HOME/.jdks/jdk-21.0.12.1+1}
|
source "$(dirname "${BASH_SOURCE[0]}")/../devenv.sh"
|
||||||
TV_ADB=${TV_ADB:-192.168.1.20:5555}
|
: "${TV_ADB:?set TV_ADB in local/dev.env}"
|
||||||
A="$HOME/Android/Sdk/platform-tools/adb -s $TV_ADB"
|
ADB=${ADB:-$(command -v adb || echo "$HOME/Android/Sdk/platform-tools/adb")}
|
||||||
|
A="$ADB -s $TV_ADB"
|
||||||
OUT=${OUT:-/tmp/movies-tv}; mkdir -p "$OUT"
|
OUT=${OUT:-/tmp/movies-tv}; mkdir -p "$OUT"
|
||||||
k() { for x in "$@"; do $A shell input keyevent "$x"; sleep 0.7; done; }
|
k() { for x in "$@"; do $A shell input keyevent "$x"; sleep 0.7; done; }
|
||||||
shot() { $A exec-out screencap -p > "$OUT/$1.png"; echo "$OUT/$1.png"; }
|
shot() { $A exec-out screencap -p > "$OUT/$1.png"; echo "$OUT/$1.png"; }
|
||||||
@@ -20,7 +21,7 @@ plog() { $A logcat -d | grep -E " (I|W|E) PlaybackSession" | sed 's/^.*PlaybackS
|
|||||||
tv_token() { $A exec-out run-as ru.stenfredd.sleepyowl cat files/datastore/jellyfin_session.preferences_pb | grep -aoE '"token":"[0-9a-f]{32}"' | head -1 | grep -oE '[0-9a-f]{32}'; }
|
tv_token() { $A exec-out run-as ru.stenfredd.sleepyowl cat files/datastore/jellyfin_session.preferences_pb | grep -aoE '"token":"[0-9a-f]{32}"' | head -1 | grep -oE '[0-9a-f]{32}'; }
|
||||||
# Что играет на ТВ по данным сервера: название, позиция (с), PlayMethod. X-Emby-Token сервер 12.x не принимает.
|
# Что играет на ТВ по данным сервера: название, позиция (с), PlayMethod. X-Emby-Token сервер 12.x не принимает.
|
||||||
sess() {
|
sess() {
|
||||||
curl -s -H "Authorization: MediaBrowser Token=\"${TV_TOKEN:=$(tv_token)}\"" "http://192.168.1.10:8096/Sessions?activeWithinSeconds=60" |
|
curl -s -H "Authorization: MediaBrowser Token=\"${TV_TOKEN:=$(tv_token)}\"" "${JF_URL:?set JF_URL in local/dev.env}/Sessions?activeWithinSeconds=60" |
|
||||||
python3 -c 'import json,sys
|
python3 -c 'import json,sys
|
||||||
for s in json.load(sys.stdin):
|
for s in json.load(sys.stdin):
|
||||||
n=s.get("NowPlayingItem")
|
n=s.get("NowPlayingItem")
|
||||||
@@ -51,8 +52,9 @@ install_release() {
|
|||||||
# Gradle может закончить с «failing tests» при успешном прогоне — тогда профиль копируется вручную.
|
# Gradle может закончить с «failing tests» при успешном прогоне — тогда профиль копируется вручную.
|
||||||
# PROFILE_SERIES — id сериала для прохода по странице сериала.
|
# PROFILE_SERIES — id сериала для прохода по странице сериала.
|
||||||
gen_profile() {
|
gen_profile() {
|
||||||
|
: "${PROFILE_SERIES:?set PROFILE_SERIES in local/dev.env}"
|
||||||
ANDROID_SERIAL=$TV_ADB ./gradlew :app:generateReleaseBaselineProfile -Pandroid.injected.androidTest.leaveApksInstalledAfterRun=true \
|
ANDROID_SERIAL=$TV_ADB ./gradlew :app:generateReleaseBaselineProfile -Pandroid.injected.androidTest.leaveApksInstalledAfterRun=true \
|
||||||
-Pandroid.testInstrumentationRunnerArguments.seriesId=${PROFILE_SERIES:-00000000000000000000000000000000}
|
-Pandroid.testInstrumentationRunnerArguments.seriesId=$PROFILE_SERIES
|
||||||
local src="baselineprofile/build/outputs/connected_android_test_additional_output/nonMinifiedRelease/connected"
|
local src="baselineprofile/build/outputs/connected_android_test_additional_output/nonMinifiedRelease/connected"
|
||||||
src=$(find "$src" -name 'BaselineProfileGenerator_generate-startup-prof.txt' | head -1)
|
src=$(find "$src" -name 'BaselineProfileGenerator_generate-startup-prof.txt' | head -1)
|
||||||
[ -n "$src" ] || { echo "профиль не найден"; return 1; }
|
[ -n "$src" ] || { echo "профиль не найден"; return 1; }
|
||||||
|
|||||||
Reference in New Issue
Block a user