mirror of
https://github.com/unshackle-dl/unshackle.git
synced 2026-11-12 20:15:10 +00:00
refactor(console): route interactive prompts through a themed helper
Adds prompt_user() and uses it for local, remote, and Proton VPN input. Replaces click.prompt on the remote auth path, which ignored the theme and overlapped active Live displays. Remote auth now exits on EOF instead of POSTing an empty answer and spinning the poll loop.
This commit is contained in:
@@ -242,7 +242,7 @@ use `self.request_input(prompt)`, never a bare `input()`. Under `serve` mode
|
|||||||
there is no local terminal, so a bare `input()` would hang the server waiting on
|
there is no local terminal, so a bare `input()` would hang the server waiting on
|
||||||
stdin that never arrives; `request_input` instead relays the prompt to the
|
stdin that never arrives; `request_input` instead relays the prompt to the
|
||||||
remote client through the attached `InputBridge`. Locally it routes through the
|
remote client through the attached `InputBridge`. Locally it routes through the
|
||||||
shared Rich console (`console.input`) so the prompt renders correctly alongside
|
shared Rich console (`prompt_user`) so the prompt renders correctly alongside
|
||||||
progress and log output.
|
progress and log output.
|
||||||
|
|
||||||
!!! warning "Key token caches by whatever varies per session"
|
!!! warning "Key token caches by whatever varies per session"
|
||||||
|
|||||||
@@ -398,7 +398,8 @@ session API and a good template for any client.
|
|||||||
- **Retries.** The download-side HTTP session mounts an adapter with
|
- **Retries.** The download-side HTTP session mounts an adapter with
|
||||||
`Retry(total=5, backoff_factor=0.2, status_forcelist=[429, 500, 502, 503, 504])`.
|
`Retry(total=5, backoff_factor=0.2, status_forcelist=[429, 500, 502, 503, 504])`.
|
||||||
- **Flow.** `authenticate()` → `create` (+ poll `prompt` every 2s up to a 600s
|
- **Flow.** `authenticate()` → `create` (+ poll `prompt` every 2s up to a 600s
|
||||||
deadline, answering `pending_input` prompts) → `get_titles()` → `get_tracks()`
|
deadline, answering `pending_input` prompts; with stdin closed the client logs an
|
||||||
|
error and raises `SystemExit(1)` instead of posting an empty answer) → `get_titles()` → `get_tracks()`
|
||||||
(merging returned `session_headers`/`session_cookies`, re-parsing `manifests`) →
|
(merging returned `session_headers`/`session_cookies`, re-parsing `manifests`) →
|
||||||
license via proxy or `server_cdm` → `close()` (`DELETE`, saving any returned
|
license via proxy or `server_cdm` → `close()` (`DELETE`, saving any returned
|
||||||
`cache`).
|
`cache`).
|
||||||
|
|||||||
@@ -427,4 +427,20 @@ console = ComfyConsole(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
__all__ = ("ComfyLogRenderer", "ComfyRichHandler", "ComfyConsole", "GradientPulseBarColumn", "SyncLive", "console")
|
def prompt_user(prompt: str) -> str:
|
||||||
|
"""Ask the user for input on the shared console, themed and indented like the rest of the output."""
|
||||||
|
indent = " " * 5
|
||||||
|
body = Text(indent + prompt.rstrip("\n ").replace("\n", "\n" + indent), style="text")
|
||||||
|
body.append("\n" + indent + "> ", style="rule.text")
|
||||||
|
return console.input(body)
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = (
|
||||||
|
"ComfyLogRenderer",
|
||||||
|
"ComfyRichHandler",
|
||||||
|
"ComfyConsole",
|
||||||
|
"GradientPulseBarColumn",
|
||||||
|
"SyncLive",
|
||||||
|
"console",
|
||||||
|
"prompt_user",
|
||||||
|
)
|
||||||
|
|||||||
@@ -11,10 +11,9 @@ from pathlib import Path
|
|||||||
from typing import Optional
|
from typing import Optional
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from rich.text import Text
|
|
||||||
|
|
||||||
from unshackle.core.config import config
|
from unshackle.core.config import config
|
||||||
from unshackle.core.console import console
|
from unshackle.core.console import prompt_user
|
||||||
from unshackle.core.proxies.proxy import Proxy
|
from unshackle.core.proxies.proxy import Proxy
|
||||||
|
|
||||||
log = logging.getLogger("proxies.proton")
|
log = logging.getLogger("proxies.proton")
|
||||||
@@ -326,13 +325,12 @@ class ProtonVPN(Proxy):
|
|||||||
log.error("Proton: unexpected TV login response")
|
log.error("Proton: unexpected TV login response")
|
||||||
return False
|
return False
|
||||||
|
|
||||||
indent = " " * 5
|
|
||||||
prompt = (
|
prompt = (
|
||||||
"Proton: open https://account.proton.me/vpn/tv/code\n"
|
"Proton: open https://account.proton.me/vpn/tv/code\n"
|
||||||
f"Enter code {user_code}, then press Enter to continue..."
|
f"Enter code {user_code}, then press Enter to continue..."
|
||||||
)
|
)
|
||||||
try:
|
try:
|
||||||
console.input(Text(indent + prompt.replace("\n", "\n" + indent), style="text"))
|
prompt_user(prompt)
|
||||||
except EOFError:
|
except EOFError:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ from rich.padding import Padding
|
|||||||
from rich.rule import Rule
|
from rich.rule import Rule
|
||||||
|
|
||||||
from unshackle.core.config import config
|
from unshackle.core.config import config
|
||||||
from unshackle.core.console import console
|
from unshackle.core.console import console, prompt_user
|
||||||
from unshackle.core.constants import AnyTrack
|
from unshackle.core.constants import AnyTrack
|
||||||
from unshackle.core.credential import Credential
|
from unshackle.core.credential import Credential
|
||||||
from unshackle.core.titles import Title_T, Titles_T, remap_titles
|
from unshackle.core.titles import Title_T, Titles_T, remap_titles
|
||||||
@@ -616,7 +616,11 @@ class RemoteService:
|
|||||||
|
|
||||||
if status == "pending_input":
|
if status == "pending_input":
|
||||||
prompt = resp.get("prompt", "Enter input: ")
|
prompt = resp.get("prompt", "Enter input: ")
|
||||||
user_response = click.prompt(prompt.rstrip("\n "), default="", show_default=False)
|
try:
|
||||||
|
user_response = prompt_user(prompt)
|
||||||
|
except EOFError:
|
||||||
|
log.error("Remote auth needs interactive input but stdin is closed")
|
||||||
|
raise SystemExit(1) from None
|
||||||
self.client.post(
|
self.client.post(
|
||||||
f"/api/session/{self._session_id}/prompt",
|
f"/api/session/{self._session_id}/prompt",
|
||||||
{"response": user_response},
|
{"response": user_response},
|
||||||
|
|||||||
@@ -16,11 +16,10 @@ import requests
|
|||||||
from requests.adapters import HTTPAdapter, Retry
|
from requests.adapters import HTTPAdapter, Retry
|
||||||
from rich.padding import Padding
|
from rich.padding import Padding
|
||||||
from rich.rule import Rule
|
from rich.rule import Rule
|
||||||
from rich.text import Text
|
|
||||||
|
|
||||||
from unshackle.core.cacher import Cacher
|
from unshackle.core.cacher import Cacher
|
||||||
from unshackle.core.config import config
|
from unshackle.core.config import config
|
||||||
from unshackle.core.console import console
|
from unshackle.core.console import console, prompt_user
|
||||||
from unshackle.core.constants import AnyTrack
|
from unshackle.core.constants import AnyTrack
|
||||||
from unshackle.core.credential import Credential
|
from unshackle.core.credential import Credential
|
||||||
from unshackle.core.drm import DRM_T
|
from unshackle.core.drm import DRM_T
|
||||||
@@ -369,9 +368,7 @@ class Service(metaclass=ABCMeta):
|
|||||||
"""
|
"""
|
||||||
if self._input_bridge is not None:
|
if self._input_bridge is not None:
|
||||||
return self._input_bridge.request_input(prompt)
|
return self._input_bridge.request_input(prompt)
|
||||||
indent = " " * 5
|
return prompt_user(prompt)
|
||||||
padded = indent + prompt.replace("\n", "\n" + indent)
|
|
||||||
return console.input(Text(padded, style="text"))
|
|
||||||
|
|
||||||
def search(self) -> Generator[SearchResult, None, None]:
|
def search(self) -> Generator[SearchResult, None, None]:
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user