Read personal dev settings from git-ignored local config

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-27 13:23:58 +03:00
parent b077388b74
commit 2f38d27b3f
13 changed files with 115 additions and 26 deletions

6
.gitignore vendored
View File

@@ -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
View 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
View 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
View 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

View File

@@ -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 "$@"

View File

@@ -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 = []

View File

@@ -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}"]

View File

@@ -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

View File

@@ -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)

View File

@@ -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; }

View File

@@ -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")

View File

@@ -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`.
""" """

View File

@@ -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; }