Add a dropdown menu that allows users to select a library.

This commit is contained in:
2025-11-27 21:48:48 +09:00
parent 355886e797
commit 1b6dddacc8
4 changed files with 97 additions and 7 deletions
+74 -7
View File
@@ -1,5 +1,4 @@
import os
from typing import Tuple
from app.utils.config import server_config
from app.utils.playlist_merge import SyncMode, sync_all_playlists, TEST_PLAYLIST_DIR
from fastapi import FastAPI, Request, Form
@@ -48,7 +47,7 @@ SYNC_MODE_OPTIONS = [
]
def _get_cloud_playlists() -> Tuple[list[dict], str, dict]:
def _get_cloud_playlists() -> tuple[list[dict], str, dict, str, list[str]]:
"""Fetch playlists and connection state from the remote Plex server."""
server_config.load()
@@ -58,10 +57,12 @@ def _get_cloud_playlists() -> Tuple[list[dict], str, dict]:
"name": "未设置",
"domain": server_config.url or "未设置",
}
selected_library = server_config.library_name
music_libraries: list[str] = []
# no server url configured
if not server_config.url:
return playlists, status, server_info
return playlists, status, server_info, selected_library, music_libraries
status = "failed"
try:
@@ -79,7 +80,16 @@ def _get_cloud_playlists() -> Tuple[list[dict], str, dict]:
}
)
for playlist in plex_client.server.playlists():
music_libraries = plex_client.get_libs_name_list()
if not music_libraries:
server_config.set_and_save_config(library_name="")
return playlists, status, server_info, "", music_libraries
if not selected_library or selected_library not in music_libraries:
selected_library = music_libraries[0]
server_config.set_and_save_config(library_name=selected_library)
for playlist in plex_client.get_lib_playlists(selected_library) or []:
track_count = getattr(playlist, "itemCount", None)
if track_count is None:
try:
@@ -97,7 +107,8 @@ def _get_cloud_playlists() -> Tuple[list[dict], str, dict]:
status = "failed"
playlists.sort(key=lambda item: item["name"].lower())
return playlists, status, server_info
return playlists, status, server_info, selected_library, music_libraries
def _build_home_context(
request: Request,
@@ -109,7 +120,13 @@ def _build_home_context(
):
server_config.load()
local_playlists = scan_local_playlists(local_path)
cloud_playlists, connection_status, server_info = _get_cloud_playlists()
(
cloud_playlists,
connection_status,
server_info,
selected_library,
music_libraries,
) = _get_cloud_playlists()
return {
"request": request,
@@ -120,6 +137,8 @@ def _build_home_context(
"cloud_playlists": cloud_playlists,
"connection_status": connection_status,
"server_info": server_info,
"selected_library": selected_library,
"music_libraries": music_libraries,
"sync_modes": SYNC_MODE_OPTIONS,
"selected_mode": selected_mode,
"message": message,
@@ -225,6 +244,25 @@ async def save_path_rules(
@app.get("/login", response_class=HTMLResponse)
async def login_page(request: Request):
server_config.load()
music_libraries = []
selected_library = server_config.library_name
if server_config.url and server_config.token:
try:
plex_client.connect(
token=server_config.token,
scheme=server_config.scheme,
url=server_config.url,
port=server_config.port,
)
music_libraries = plex_client.get_libs_name_list()
if music_libraries:
if selected_library not in music_libraries:
selected_library = music_libraries[0]
else:
selected_library = ""
except Exception:
selected_library = ""
music_libraries = []
return templates.TemplateResponse(
"login.html",
{
@@ -235,6 +273,8 @@ async def login_page(request: Request):
"scheme": server_config.scheme,
"server_url": server_config.url,
"port": server_config.port,
"music_libraries": music_libraries,
"selected_library": selected_library,
},
)
@@ -248,6 +288,7 @@ async def login(
scheme: str = Form("https"),
url: str = Form(...),
port: str = Form("32400"),
library_name: str = Form(""),
):
# 尝试连接到 Plex 服务器
try:
@@ -261,9 +302,30 @@ async def login(
port=port,
)
# 成功连接后保存配置到配置文件
music_libraries: list[str] = []
selected_library = ""
if plex_client.connected:
try:
music_libraries = plex_client.get_libs_name_list()
except Exception as exc:
logger.warning(f"Unable to fetch music libraries: {exc}")
music_libraries = []
if music_libraries:
if library_name and library_name in music_libraries:
selected_library = library_name
else:
selected_library = music_libraries[0]
server_config.set_and_save_config(
token=token_success, scheme=scheme, url=url, port=port
token=token_success,
scheme=scheme,
url=url,
port=port,
library_name=selected_library,
)
else:
music_libraries = []
server_config.set_and_save_config(
token=token_success, scheme=scheme, url=url, port=port, library_name=""
)
return templates.TemplateResponse(
"login.html",
@@ -277,9 +339,12 @@ async def login(
"scheme": server_config.scheme,
"server_url": server_config.url,
"port": server_config.port,
"music_libraries": music_libraries,
"selected_library": selected_library,
},
)
except Exception as e:
music_libraries = []
return templates.TemplateResponse(
"login.html",
{
@@ -291,6 +356,8 @@ async def login(
"scheme": scheme,
"server_url": url,
"port": port,
"music_libraries": music_libraries,
"selected_library": "",
},
)
+1
View File
@@ -107,6 +107,7 @@
<div>
<h5 class="card-title mb-0">云端播放列表</h5>
<small class="text-body-secondary">来自已连接的 Plex 服务器</small>
<div class="text-body-secondary small mt-1">当前音乐库:{{ selected_library or '暂无可用音乐库' }}</div>
</div>
<form class="ms-lg-auto" method="get" action="/">
<input type="hidden" name="local_path" value="{{ local_path }}">
+13
View File
@@ -32,6 +32,19 @@
<label for="port" class="form-label">端口</label>
<input type="text" class="form-control" id="port" name="port" value="{{ port }}">
</div>
<div class="mb-3">
<label for="library_name" class="form-label">音乐库</label>
<select class="form-select" id="library_name" name="library_name" {% if not music_libraries %}disabled{% endif %}>
{% if music_libraries %}
{% for library in music_libraries %}
<option value="{{ library }}" {% if selected_library == library %}selected{% endif %}>{{ library }}</option>
{% endfor %}
{% else %}
<option value="" selected>暂无可用的音乐库</option>
{% endif %}
</select>
<div class="form-text">仅显示音乐类型的库,可在连接成功后自动刷新。</div>
</div>
<button type="submit" class="btn btn-primary">连接</button>
</form>
+9
View File
@@ -15,6 +15,7 @@ class ServerConfig:
self.url = ""
self.scheme = "https"
self.port = "32400"
self.library_name = ""
self.path_rules: list[dict[str, str]] = []
self.load()
@@ -38,6 +39,7 @@ class ServerConfig:
self.url = config.get("server_url", "")
self.scheme = config.get("server_scheme", "https")
self.port = config.get("server_port", "32400")
self.library_name = config.get("library_name", "")
self.path_rules = config.get("path_rules", []) or []
logger.info(f"Server config loaded: {self.__dict__}")
@@ -48,6 +50,7 @@ class ServerConfig:
"server_url": self.url,
"server_scheme": self.scheme,
"server_port": self.port,
"library_name": self.library_name,
"path_rules": self.path_rules,
}
with open(CONFIG_PATH, "w", encoding="utf-8") as f:
@@ -66,6 +69,9 @@ class ServerConfig:
def set_token(self, token: str) -> None:
self.token = token
def set_library(self, library_name: str) -> None:
self.library_name = library_name or ""
def set_theme(self, theme: str) -> None:
# check theme is valid
if theme not in ["auto", "dark", "light"]:
@@ -83,6 +89,7 @@ class ServerConfig:
url: str = None,
scheme: str = None,
port: str = None,
library_name: str | None = None,
path_rules: list[dict[str, str]] | None = None,
) -> None:
if theme is not None:
@@ -95,6 +102,8 @@ class ServerConfig:
self.set_scheme(scheme)
if port is not None:
self.set_port(port)
if library_name is not None:
self.set_library(library_name)
if path_rules is not None:
self.set_path_rules(path_rules)
self.save()