fix: 修复大屏播放控制链路

前端:
- useMqttControl 指令分发补上 play_target(此前被静默丢弃,立即播放无法触发大屏跳转)
- MediaScreen 重构 playTarget:pendingTargetRef 统一刷新播放列表路径,
  消除与 playlist_changed 重载的竞态(避免跳转被旧索引覆盖)

后端:
- storage.add_to_playlist 自动识别 URL 来源(source=url),
  修复 URL 媒体加入播放列表后被当本地文件过滤、永远无法播放的问题
- _load 时规范化旧数据(source=None 的 http 条目自动补 url)
This commit is contained in:
Pine
2026-08-17 21:59:13 +08:00
parent c55835fb7d
commit 7bb0e9024a
3 changed files with 26 additions and 17 deletions
+15 -15
View File
@@ -228,6 +228,8 @@ export default function MediaScreen() {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [skip]);
const pendingTargetRef = useRef(null);
const reloadPlaylist = useCallback(async () => {
try {
const data = await api.getPlaylist();
@@ -246,7 +248,12 @@ export default function MediaScreen() {
setLoaded(true);
let newIndex = -1;
if (currentItem) {
const target = pendingTargetRef.current;
if (target) {
// 指定播放优先:跳转到目标媒体(一次性)
pendingTargetRef.current = null;
newIndex = newList.findIndex(item => item.relative_path === target);
} else if (currentItem) {
newIndex = newList.findIndex(item => item.relative_path === currentItem.relative_path);
}
if (newIndex >= 0) {
@@ -259,6 +266,13 @@ export default function MediaScreen() {
} catch {/* ignore */}
}, []);
// 指定媒体立即播放:先标记目标,再刷新播放列表(统一路径,避免竞态)
const playTarget = useCallback(async (path) => {
pendingTargetRef.current = path;
await reloadPlaylist();
setPaused(false);
}, [reloadPlaylist]);
const applySettings = useCallback((msg) => {
if (msg.volume !== undefined) {
const vol = msg.volume / 100;
@@ -295,20 +309,6 @@ export default function MediaScreen() {
}
}, []);
// 指定媒体立即播放:刷新播放列表并跳转到目标项
const playTarget = useCallback(async (path) => {
try {
const data = await api.getPlaylist();
const newList = data.files || [];
if (newList.length === 0) return;
setList(newList);
setLoaded(true);
const idx = newList.findIndex((it) => it.relative_path === path);
if (idx >= 0) setIndex(idx);
setPaused(false);
} catch { /* ignore */ }
}, []);
// MQTT 控制通道(后端 opc/display/command → window 事件)
useEffect(() => {
const handler = (e) => {
+1
View File
@@ -81,6 +81,7 @@ export function useMqttControl() {
case 'next':
case 'prev':
case 'set_mode':
case 'play_target':
case 'minimize':
case 'settings_changed':
case 'playlist_changed':