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

View File

@@ -5,7 +5,7 @@
# (tools/linux/stand.sh) points them at its own directory.
set -e
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)
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 "$@"

View File

@@ -1,5 +1,5 @@
#!/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
@@ -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,
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.
Needs K2_EPISODES (three consecutive episodes) in local/dev.env, see tools/dev.env.example.
"""
import os, re, shutil, subprocess, sys, time
from Xlib import X, XK, display
from Xlib.ext import xtest
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__))))
STAND = os.environ.get("STAND") or os.path.expanduser("~/.cache/sleepyowl-test")
STAND_SH = os.path.join(R, "tools/linux/stand.sh")
PREFS = os.path.join(STAND, "config/sleepyowl")
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 = []

View File

@@ -8,6 +8,9 @@ float_test.py <037|041>
import json, os, socket, subprocess, sys, time
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)
M037 = os.environ.get("MPV037", "mpv")
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]
sock = os.path.join(RUN, f"float{ver}.sock")
if os.path.exists(sock): os.unlink(sock)
item = "00000000000000000000000000000000"
url = f"http://192.168.1.10:8096/Videos/{item}/stream?static=true&mediaSourceId={item}"
item = devenv.need("L0_FLOAT_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",
"--ontop", "--no-border", "--geometry=640x360-24-24", "--screen=0", "--keep-open=yes",
"--title=L0 float", f"--input-ipc-server={sock}"]

View File

@@ -1,7 +1,8 @@
#!/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, чересстрочные с деинтерлейсом.
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; }
while read -r name id rest; do
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}_deintauto --extra=--deinterlace=auto;;
esac
done < $D/cases.txt
done < "$CASES"
echo DONE

View File

@@ -1,16 +1,19 @@
#!/usr/bin/env python3
"""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 ..."]
Печатает одну строку JSON с результатами в stdout.
"""
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")
# mpv 0.37 из репозитория Mint (или распакованный из .deb без установки: apt download mpv; dpkg -x)
M037 = os.environ.get("MPV037", "mpv")
SERVER = "http://192.168.1.10:8096"
SERVER = devenv.need("JF_URL")
# Токен-заглушка: сервер отдаёт static-поток и без токена; проверяем, что заголовок доходит и не ломает запрос.
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%")
if a.sid: opts.append(f"--sid={a.sid}")
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"]
opts += shlex.split(a.extra)

View File

@@ -1,5 +1,5 @@
#!/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
# 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
@@ -14,7 +14,7 @@
# 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…)
#
# 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
# 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).
@@ -22,7 +22,7 @@ set -e
R=$(cd "$(dirname "$0")/../.." && pwd)
STAND=${STAND:-$HOME/.cache/sleepyowl-test}
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"
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
"""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 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
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.
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
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
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")
URL = os.environ.get("JF_URL", "http://192.168.1.10:8096")
USER = os.environ.get("JF_USER", "test")
URL = devenv.need("JF_URL")
USER = devenv.need("JF_USER")
FIELDS = ("PlaybackPositionTicks", "PlayCount", "Played", "LastPlayedDate", "IsFavorite")