jellyfin-qbittorrent-monitor: fix hysterisis
All checks were successful
Build and Deploy / mreow (push) Successful in 2m41s
Build and Deploy / yarn (push) Successful in 1m0s
Build and Deploy / muffin (push) Successful in 1m14s

This commit is contained in:
2026-05-14 21:11:21 -04:00
parent b2ad5abf1b
commit 958f373be6
2 changed files with 105 additions and 1 deletions

View File

@@ -586,6 +586,97 @@ pkgs.testers.runNixOSTest {
time.sleep(2)
# === FLICKER TEST ===
#
# Session flicker (brief appeardisappearreappear cycles) is the
# production reality when xmrig starves the transcode CPU and the
# client's buffer oscillates. The hysteresis timer must measure
# CONTIGUOUS absence, not cumulative gaps across flickers. A 5s
# stop delay with flickers of 2s absent / 2s present must NOT
# accumulate into a spurious unlimited transition.
server.succeed("systemctl stop monitor-test || true")
time.sleep(1)
server.succeed(f"""
systemd-run --unit=monitor-flicker \
--setenv=JELLYFIN_URL=http://localhost:8096 \
--setenv=JELLYFIN_API_KEY={token} \
--setenv=QBITTORRENT_URL=http://localhost:8080 \
--setenv=CHECK_INTERVAL=1 \
--setenv=STREAMING_START_DELAY=1 \
--setenv=STREAMING_STOP_DELAY=5 \
--setenv=TOTAL_BANDWIDTH_BUDGET=50000000 \
--setenv=SERVICE_BUFFER=2000000 \
--setenv=DEFAULT_STREAM_BITRATE=10000000 \
--setenv=MIN_TORRENT_SPEED=100 \
{python} {monitor}
""")
time.sleep(2)
assert not is_throttled(), "Should start unthrottled"
with subtest("Session flicker does not accumulate hysteresis timer"):
# Start playback
playback_flicker = {
"ItemId": movie_id,
"MediaSourceId": media_source_id,
"PlaySessionId": "test-play-session-flicker",
"CanSeek": True,
"IsPaused": False,
}
start_cmd = f"curl -sf -X POST 'http://{server_ip}:8096/Sessions/Playing' -d '{json.dumps(playback_flicker)}' -H 'Content-Type:application/json' -H 'X-Emby-Authorization:{client_auth}, Token={client_token}'"
client.succeed(start_cmd)
time.sleep(2)
assert is_throttled(), "Should throttle when streaming starts"
# Flicker 1: stop 2s, restart 2s
playback_flicker["PositionTicks"] = 50000000
stop_flicker = f"curl -sf -X POST 'http://{server_ip}:8096/Sessions/Playing/Stopped' -d '{json.dumps(playback_flicker)}' -H 'Content-Type:application/json' -H 'X-Emby-Authorization:{client_auth}, Token={client_token}'"
client.succeed(stop_flicker)
time.sleep(2)
client.succeed(start_cmd)
time.sleep(2)
# Flicker 2: stop 2s, restart 2s (total absent = 4s < 5s delay)
client.succeed(stop_flicker)
time.sleep(2)
client.succeed(start_cmd)
time.sleep(2)
# Should still be throttled flicker gaps didn't accumulate
assert is_throttled(), "Should remain throttled after session flickers (contiguous absence < 5s)"
# Genuine stop: absent for full stop delay
client.succeed(stop_flicker)
time.sleep(6)
assert not is_throttled(), "Should unthrottle after contiguous 5s+ absence"
# Restart after genuine stop
client.succeed(start_cmd)
time.sleep(2)
assert is_throttled(), "Should re-throttle after restart"
# Clean up
client.succeed(stop_flicker)
time.sleep(2)
# Restore the fast-polling monitor for subsequent tests
server.succeed("systemctl stop monitor-flicker || true")
time.sleep(1)
server.succeed(f"""
systemd-run --unit=monitor-test \
--setenv=JELLYFIN_URL=http://localhost:8096 \
--setenv=JELLYFIN_API_KEY={token} \
--setenv=QBITTORRENT_URL=http://localhost:8080 \
--setenv=CHECK_INTERVAL=1 \
--setenv=STREAMING_START_DELAY=1 \
--setenv=STREAMING_STOP_DELAY=1 \
--setenv=TOTAL_BANDWIDTH_BUDGET=50000000 \
--setenv=SERVICE_BUFFER=2000000 \
--setenv=DEFAULT_STREAM_BITRATE=10000000 \
--setenv=MIN_TORRENT_SPEED=100 \
{python} {monitor}
""")
time.sleep(2)
# === SERVICE RESTART TESTS ===
with subtest("qBittorrent restart during throttled state re-applies throttling"):