feat(service): add wait_for_approval and return the cache for a login with no prompt

A pair code shown in a log line never answers a prompt, so the server read that login as anonymous and kept the session cache.
This commit is contained in:
imSp4rky
2026-10-07 09:03:18 -06:00
parent 3fd69e8039
commit 5b0369e63a
12 changed files with 97 additions and 36 deletions
+18
View File
@@ -256,6 +256,24 @@ REST download job shows it as `input_prompt`. Locally it routes through the
shared Rich console (`prompt_user`) so the prompt renders correctly alongside
progress and log output.
For a login that the user approves on another device (a pair code, a QR code),
use `self.wait_for_approval(message, check, timeout)`, never a hand-written
loop. It shows the message, then calls `check` at each interval until `check`
returns a value. `check` returns `None` while the approval is pending.
```python
token = self.wait_for_approval(
f"Open {self.config['endpoints']['pair']} and enter code {code}",
check=lambda: self.poll_pair_token(code),
timeout=expires_in,
)
```
It raises `TimeoutError` after `timeout` seconds. Under `serve` mode it also stops
when the client deletes the remote session, so the loop does not continue on the
server with no client. The message goes out as a log line, so a remote
session shows it, but a REST download job does not relay it as `input_prompt`.
!!! warning "Make each token cache entry unique"
The cache above uses `tokens_{device}_{profile}` on purpose. A token cache
must include every dimension that changes the token (device, profile, or
+1 -1
View File
@@ -957,7 +957,7 @@ The `proxy` value must be a full proxy URI, unless the operator gives your API k
}
```
`server_account` is `true` when the server authenticated with one of its own accounts (`serve.server_accounts`). Otherwise the server uses only what the client sent, and never falls back to its own credentials. Only a client that sent its own cookies, credentials, or cache files gets cookies, auth headers, and the remote session cache back. A client that answered a login prompt (a device code or an OTP) gets them too. This applies only when the login then succeeds and the remote session does not use a server account. For any other remote session (a server account, or an anonymous login through the server's proxy) the tracks and segments responses carry no cookies and no auth headers. The server also drops secret-looking keys from track data, and the cache stays on the server. A server-account service also refuses a client proxy URI, because the server's login would pass through it.
`server_account` is `true` when the server authenticated with one of its own accounts (`serve.server_accounts`). Otherwise the server uses only what the client sent, and never falls back to its own credentials. When the login succeeds and the remote session does not use a server account, the login belongs to the client, and the client gets cookies, auth headers, and the remote session cache back. A remote session on the server's device is the exception: the client gets its cookies back, but no auth headers and no cache. For a server-account remote session the tracks and segments responses carry no cookies and no auth headers. The server also drops secret-looking keys from track data, and the cache stays on the server. A server-account service also refuses a client proxy URI, because the server's login would pass through it.
| Status | Error code | Meaning |
| --- | --- | --- |
+3 -3
View File
@@ -213,9 +213,9 @@ hood the client walks a remote session through its lifecycle.
deletes the remote session. If the login belongs to the client, the `DELETE`
request returns the updated cache files (for example a refreshed token) and the
client saves them locally, so the **next** remote session can skip interactive
authentication. The login belongs to the client when the client sent
cookies, credentials, or cache files, or answered a login prompt (a device code
or an OTP) that led to a successful login. A server-account login never does,
authentication. The login belongs to the client when it succeeds and the
remote session does not use a server account. This includes a pair code that
the user approves on another device. A server-account login never does,
and neither does a remote session on the server's device (see below).
The client saves only the returned files that pass the same profile check it
applies before it sends them, so a file for another profile cannot overwrite
+3
View File
@@ -89,6 +89,9 @@ These are abstract. A service will not load unless it defines all three:
`get_clearkey_license` (returns `None` by default). See [DRM hooks](#drm-hooks) below.
- **`request_input(prompt)`**: prompts the user. It goes through the remote-dl input
bridge, the REST download job relay, or the rich console.
- **`wait_for_approval(message, check, timeout, interval=3.0)`**: shows a message, then
calls `check` until it returns a value. Use it for a login that the user approves on
another device, such as a pair code.
- **Event callbacks**: `on_segment_downloaded`, `on_track_downloaded`,
`on_track_decrypted`, `on_track_repacked`, `on_track_multiplex` let a service react to
pipeline events (for example, MonaLisa decrypts each segment in `on_segment_downloaded`).