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