支持手动token认证

This commit is contained in:
Koha9 2025-07-08 19:17:18 +09:00
parent 0c2a84f944
commit a8863f911b
6 changed files with 73 additions and 24 deletions

View File

@ -2,5 +2,6 @@
"theme": "auto", "theme": "auto",
"token": "", "token": "",
"server_url": "", "server_url": "",
"server_port": "32400" "server_port": "",
"server_scheme": ""
} }

View File

@ -14,11 +14,12 @@ templates = Jinja2Templates(directory=os.path.join(os.path.dirname(__file__), "t
app.mount("/static", StaticFiles(directory=os.path.join(os.path.dirname(__file__), "static")), name="static") app.mount("/static", StaticFiles(directory=os.path.join(os.path.dirname(__file__), "static")), name="static")
# 显示主页
@app.get("/", response_class=HTMLResponse) @app.get("/", response_class=HTMLResponse)
async def home(request: Request): async def home(request: Request):
config = load_config() config = load_config()
theme = config.get("theme", "auto") theme = config.get("theme", "auto")
scheme, host, port = get_server_settings(config) token,scheme, host, port = get_server_settings(config)
return templates.TemplateResponse( return templates.TemplateResponse(
"login.html", "login.html",
{ {
@ -26,22 +27,25 @@ async def home(request: Request):
"theme": theme, "theme": theme,
"path": "/login", "path": "/login",
"scheme": scheme, "scheme": scheme,
"token": token,
"server_url": host, "server_url": host,
"port": port, "port": port,
}, },
) )
# 登录页面和处理
@app.get("/login", response_class=HTMLResponse) @app.get("/login", response_class=HTMLResponse)
async def login_page(request: Request): async def login_page(request: Request):
config = load_config() config = load_config()
theme = config.get("theme", "auto") theme = config.get("theme", "auto")
scheme, host, port = get_server_settings(config) token, scheme, host, port = get_server_settings(config)
return templates.TemplateResponse( return templates.TemplateResponse(
"login.html", "login.html",
{ {
"request": request, "request": request,
"theme": theme, "theme": theme,
"path": "/login", "path": "/login",
"token": token,
"scheme": scheme, "scheme": scheme,
"server_url": host, "server_url": host,
"port": port, "port": port,
@ -53,19 +57,26 @@ async def login(
request: Request, request: Request,
user: str = Form(...), user: str = Form(...),
pw: str = Form(...), pw: str = Form(...),
token: str = Form(...),
scheme: str = Form("https"), scheme: str = Form("https"),
url: str = Form(...), url: str = Form(...),
port: str = Form("32400") port: str = Form("32400"),
): ):
config = load_config() config = load_config()
theme = config.get("theme", "auto") theme = config.get("theme", "auto")
# 尝试连接到 Plex 服务器
try: try:
full_url = url # 优先使用 token 连接,如果 token 为空则使用用户名和密码连接
if not full_url.startswith("http://") and not full_url.startswith("https://"): _, token_success = connect_plex(user, pw, token, scheme, url, port)
full_url = f"{scheme}://{url}" # 成功连接后保存配置到配置文件
connect_plex(config, user, pw, full_url, port) config.update({
"server_url": url,
"server_scheme": scheme,
"server_port": port,
"token": token_success,
})
save_config(config) save_config(config)
scheme, host, port = get_server_settings(config) token, scheme, host, port = get_server_settings(config)
return templates.TemplateResponse( return templates.TemplateResponse(
"login.html", "login.html",
{ {
@ -74,6 +85,7 @@ async def login(
"success": True, "success": True,
"theme": theme, "theme": theme,
"path": "/login", "path": "/login",
"token": token,
"scheme": scheme, "scheme": scheme,
"server_url": host, "server_url": host,
"port": port, "port": port,

View File

@ -12,6 +12,10 @@
<div class="mb-3"> <div class="mb-3">
<label for="pw" class="form-label">密码</label> <label for="pw" class="form-label">密码</label>
<input type="password" class="form-control" id="pw" name="pw"> <input type="password" class="form-control" id="pw" name="pw">
</div>
<div class="mb-3">
<label for="token" class="form-label">Token</label>
<input type="text" class="form-control" id="token" name="token" value="{{ token }}">
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label for="scheme" class="form-label">协议</label> <label for="scheme" class="form-label">协议</label>

6
app/utils/common.py Normal file
View File

@ -0,0 +1,6 @@
def str_is_empty(s: str) -> bool:
"""Check if a string is empty or contains only whitespace."""
if s is None:
return True
stripped = s.replace(" ", " ")
return not bool(stripped)

View File

@ -17,7 +17,8 @@ def save_config(new_config):
def get_server_settings(config): def get_server_settings(config):
"""Return (scheme, host, port) using defaults when not configured.""" """Return (scheme, host, port) using defaults when not configured."""
scheme = "https" scheme = config.get("server_scheme", "https")
token = config.get("token", "") or ""
host = "" host = ""
port = config.get("server_port", "32400") or "32400" port = config.get("server_port", "32400") or "32400"
@ -31,5 +32,5 @@ def get_server_settings(config):
port = str(parsed.port) port = str(parsed.port)
else: else:
host = url host = url
print(f"server_scheme: {scheme}, host: {host}, port: {port}")
return scheme, host, port return token, scheme, host, port

View File

@ -1,17 +1,15 @@
from plexapi.myplex import MyPlexAccount from plexapi.myplex import MyPlexAccount
from plexapi.server import PlexServer from plexapi.server import PlexServer
from urllib.parse import urlparse from urllib.parse import urlparse
from app.utils.common import str_is_empty
def build_plex_url(scheme, url, port="32400"):
def connect_plex(config, username, password, url, port="32400"): """Build a full Plex URL from scheme, url, and port."""
"""Return a connected PlexServer instance and update config with token and server info.""" # 如果url不以http://或https://开头则添加scheme
token = config.get("token") full_url = url
if not token: if not full_url.startswith("http://") and not full_url.startswith("https://"):
account = MyPlexAccount(username, password) full_url = f"{scheme}://{url}"
token = account.authenticationToken parsed = urlparse(full_url)
config["token"] = token
parsed = urlparse(url)
if parsed.scheme in ("http", "https"): if parsed.scheme in ("http", "https"):
netloc = parsed.netloc or parsed.path netloc = parsed.netloc or parsed.path
@ -20,6 +18,33 @@ def connect_plex(config, username, password, url, port="32400"):
base_url = f"{parsed.scheme}://{netloc}" base_url = f"{parsed.scheme}://{netloc}"
else: else:
base_url = f"http://{url}:{port}" base_url = f"http://{url}:{port}"
return base_url
def connect_plex(username, password, token, scheme, url, port="32400"):
"""Return a connected PlexServer instance and update config with token and server info."""
# 如果token存在且不为空则使用token连接
if not str_is_empty(token):
return connect_plex_with_token(token, scheme, url, port)
else:
return connect_plex_with_pw(username, password, scheme, url, port)
def connect_plex_with_pw(username, password, scheme, url, port="32400"):
"""Return a connected PlexServer instance and update config with token and server info."""
# url 初始化
base_url = build_plex_url(scheme, url, port)
# account 初始化
account = MyPlexAccount(username, password)
# token 获取
token = account.authenticationToken
server = PlexServer(base_url, token) server = PlexServer(base_url, token)
config.update({"server_url": url, "server_port": port}) return server, token
return server
def connect_plex_with_token(token, scheme, url, port="32400"):
"""Return a connected PlexServer instance using a token."""
# URL 初始化
base_url = build_plex_url(scheme, url, port)
server = PlexServer(base_url, token)
return server, token