refactor(remote)!: send service options only in service_params

A service option named like a dl option (e.g. --profile) is no longer dropped or mixed with the dl value; the server casts nested values and keeps cli defaults nested.
This commit is contained in:
imSp4rky
2026-10-30 16:16:56 -06:00
parent 47f88b1b65
commit 5206b3cab6
12 changed files with 139 additions and 71 deletions
@@ -103,3 +103,19 @@ def test_client_sent_keys_uses_the_stamp():
def test_client_sent_keys_without_stamp_counts_all_keys():
assert dm.client_sent_keys({"workers": 1, "quality": None}) == {"workers", "quality"}
def test_service_defaults_and_options_travel_nested(env, monkeypatch):
import click
@click.command()
@click.option("--profile", type=int, default=1)
@click.option("--device", default="chrome")
def cli(**kwargs):
pass
monkeypatch.setattr(handlers.Services, "load", lambda service: SimpleNamespace(cli=cli))
params = env(body={"profile": "work", "service_params": {"profile": 2}})
assert params["profile"] == "work"
assert params["service_params"] == {"profile": 2, "device": "chrome"}
assert env(body={"device": "tv"})["service_params"] == {"profile": 1, "device": "tv"}
+6
View File
@@ -136,6 +136,12 @@ def test_make_remote_service_passes_only_cli_options(remote_config, monkeypatch)
assert built["args"][5]["EXAMPLE"] == {"x": 1}
def test_a_service_option_named_like_a_dl_value_passes_only_nested():
info = {"cli_params": [{"kind": "option", "name": "profile"}]}
assert remote_service.remote_service_params(info, {"service_params": {"profile": "QC"}}) == {"profile": "QC"}
assert remote_service.remote_service_params(info, {"profile": "QC"}) == {}
def test_remote_error_carries_its_reason_and_exits_a_cli_run():
err = remote_service.RemoteError("Server error [SESSION_NOT_FOUND]: gone", "SESSION_NOT_FOUND")
assert isinstance(err, SystemExit) and err.code == 1 and err.kind == "SESSION_NOT_FOUND"
@@ -645,9 +645,15 @@ def test_dl_selection_travels_nested_and_a_service_option_cannot_override_it() -
"forced_subs": True,
"exact_lang": True,
}
assert data["lang"] == 3
assert "lang" not in data and "a_lang" not in data
assert data["service_params"] == {"lang": 3, "a_lang": "x"}
def test_a_service_profile_option_travels_nested_and_keeps_the_credential_profile() -> None:
data = _sent_create_data({"profile": "work"}, {"profile": "QC"})
assert data["profile"] == "work"
assert data["service_params"] == {"profile": "QC"}
def test_no_dl_params_when_the_ctx_has_no_selection() -> None:
assert "dl_params" not in _sent_create_data({}, {})
+17 -10
View File
@@ -2,6 +2,7 @@
import json
import logging
from datetime import datetime
from types import SimpleNamespace
from uuid import UUID
@@ -97,8 +98,8 @@ def test_a_server_device_option_keeps_the_server_cdm_against_the_clients_choice(
nested = {"cdm_type": "widevine", "server_cdm": False, "service_params": {"server_side": True}}
assert handlers.choose_session_cdm(request("flat"), "EXAMPLE1", nested) == (True, 1080)
# service_params wins over the flat key, as it does when the service is built
nested["server_side"], nested["service_params"] = True, {"server_side": False}
# with service_params present a flat key is a dl value, as it is when the service is built
nested["server_side"], nested["service_params"] = True, {}
assert handlers.choose_session_cdm(request("flat"), "EXAMPLE1", nested) == (False, None)
@@ -318,8 +319,8 @@ def test_a_capped_download_job_cannot_ask_for_hybrid(hdr_range):
handlers.enforce_download_gates({"service": "EXAMPLE1", "quality": [1080], "range": ["HDR10"]}, request("flat"))
def test_the_download_gate_reads_the_service_click_defaults(monkeypatch):
"""A service's own click default overrides the serve default, and the gate sees what the job runs with."""
def test_a_service_click_default_stays_out_of_the_download_gate(monkeypatch):
"""A service's own option is not the dl option of the same name, so its default never trips the gate."""
import asyncio
from unshackle.core.api import download_manager as dm
@@ -329,15 +330,21 @@ def test_the_download_gate_reads_the_service_click_defaults(monkeypatch):
monkeypatch.setattr(
handlers.Services, "load", lambda service: SimpleNamespace(cli=SimpleNamespace(params=[option]))
)
jobs = []
def no_job():
raise AssertionError("a refused job must not reach the download manager")
class Manager:
async def start_workers(self):
pass
monkeypatch.setattr(dm, "get_download_manager", no_job)
def create_job(self, service, title_id, owner_key=None, **params):
jobs.append(params)
return SimpleNamespace(job_id="j", status=SimpleNamespace(value="queued"), created_time=datetime.now())
monkeypatch.setattr(dm, "get_download_manager", Manager)
body = {"service": "EXAMPLE1", "title_id": "t1", "quality": [1080]}
with pytest.raises(APIError) as ei:
asyncio.run(handlers.download_handler(body, request("flat")))
assert ei.value.error_code is APIErrorCode.SERVER_CDM_CAPPED
asyncio.run(handlers.download_handler(body, request("flat")))
assert jobs[0]["service_params"] == {"best_available": True}
assert not jobs[0].get("best_available")
@pytest.fixture
+25
View File
@@ -121,6 +121,31 @@ def test_transport_keys_block_profile_and_service_params() -> None:
assert "service_params" in LIST_HANDLER_TRANSPORT_KEYS
def test_service_params_are_the_only_service_options_when_present() -> None:
import click
from unshackle.core.api.errors import APIError
from unshackle.core.api.handlers import SESSION_TRANSPORT_KEYS, instantiate_service
@click.command()
@click.argument("title")
@click.option("--profile", default=None)
@click.option("--device", default=None)
@click.option("--count", type=int, default=1)
def cli(title: str, **kwargs: object) -> dict:
return kwargs
def build(data: dict) -> dict:
return instantiate_service(click.Context(click.Command("dl")), module, "t", data, SESSION_TRANSPORT_KEYS)
module = type("Svc", (), {"cli": cli})
nested = {"profile": "work", "device": "tv", "service_params": {"profile": "QC", "count": "2"}}
assert build(nested) == {"profile": "QC", "device": None, "count": 2}
assert build({"profile": "work", "device": "tv"}) == {"profile": None, "device": "tv", "count": 1}
with pytest.raises(APIError):
build({"service_params": {"count": "x"}})
def test_drain_server_logs_survives_client_systemexit() -> None:
"""RemoteClient raises SystemExit on transport errors; the drain must swallow it."""
from unshackle.core.remote_service import RemoteService