Add a dropdown menu that allows users to select a library.
This commit is contained in:
+74
-7
@@ -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": "",
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -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 }}">
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user