diff --git a/index.html b/index.html index ceb3bd9..8636778 100644 --- a/index.html +++ b/index.html @@ -431,7 +431,7 @@ ); - const SideDrawer = ({ isOpen, onClose, view, setView, quality, setQuality, onClearCache, syncToken, setSyncToken, onSyncNow, onImportNetease, neteasePlaylistId, onStopNeteaseSync }) => { + const SideDrawer = ({ isOpen, onClose, view, setView, quality, setQuality, onClearCache, syncToken, setSyncToken, onSyncNow, onImportNetease, syncMode }) => { const [isSyncing, setIsSyncing] = useState(false); const [syncMsg, setSyncMsg] = useState(''); @@ -510,7 +510,9 @@
- +

- 私有云多端同步收藏列表 + {syncMode === 'netease_playlist' + ? '已关联歌单,将合并新歌并自动备份至云端' + : '使用相同的密钥在多端同步收藏列表'}

{syncMsg && {syncMsg}}
-
-
- - {neteasePlaylistId ? ( -
-
-
ID: {neteasePlaylistId}
-
自动更新中
-
- -
- ) : ( - - )} -

- {neteasePlaylistId ? '每15分钟自动检测新歌并加入收藏,随后触发云同步。' : '导入后将自动合并新歌到收藏列表。'} -

+ +

导入后将设置ID为同步密钥,并定期获取新歌

@@ -937,7 +921,7 @@ const [quality, setQuality] = useState(() => localStorage.getItem('th_quality') || '320k'); const [syncToken, setSyncToken] = useState(() => localStorage.getItem('th_sync_token') || ''); const [lastSuccessToken, setLastSuccessToken] = useState(() => localStorage.getItem('th_last_success_token') || ''); - const [neteasePlaylistId, setNeteasePlaylistId] = useState(() => localStorage.getItem('th_netease_playlist_id') || ''); + const [syncMode, setSyncMode] = useState(() => localStorage.getItem('th_sync_mode') || 'server'); // 'server' | 'netease_playlist' // UI State const [showFullPlayer, setShowFullPlayer] = useState(false); @@ -971,7 +955,7 @@ useEffect(() => { localStorage.setItem('th_quality', quality); }, [quality]); useEffect(() => { localStorage.setItem('th_sync_token', syncToken); }, [syncToken]); useEffect(() => { localStorage.setItem('th_last_success_token', lastSuccessToken); }, [lastSuccessToken]); - useEffect(() => { localStorage.setItem('th_netease_playlist_id', neteasePlaylistId); }, [neteasePlaylistId]); + useEffect(() => { localStorage.setItem('th_sync_mode', syncMode); }, [syncMode]); // Auto Sync Logic for Private Server useEffect(() => { @@ -987,36 +971,40 @@ // Auto Sync Logic for Netease Playlist (Interval based) useEffect(() => { - if (neteasePlaylistId) { + if (syncMode === 'netease_playlist' && syncToken) { const doSync = async () => { console.log("Auto syncing netease playlist..."); - const songs = await api.getPlaylist(neteasePlaylistId); - if (songs && songs.length > 0) { - setFavorites(prev => { - const newFavs = [...prev]; - let changed = false; - // Add new songs (reverse to keep latest at top if we prepend) - [...songs].reverse().forEach(song => { - if (!newFavs.find(s => s.id === song.id)) { - newFavs.unshift({ ...song, source: 'netease' }); - changed = true; - } + try { + const songs = await api.getPlaylist(syncToken); + if (songs && songs.length > 0) { + let hasChanges = false; + setFavorites(prev => { + const newFavs = [...prev]; + // Add new songs (reverse to keep latest at top if we prepend) + [...songs].reverse().forEach(song => { + if (!newFavs.find(s => s.id === song.id)) { + newFavs.unshift({ ...song, source: 'netease' }); + hasChanges = true; + } + }); + return hasChanges ? newFavs : prev; }); - return changed ? newFavs : prev; - }); + } + } catch (e) { + console.error("Auto sync netease playlist failed", e); } }; - // Initial sync on mount/mode change (if not already handled by import) - // We can skip initial immediate sync if we assume import did it, but it doesn't hurt to check. - // Actually, if we just refreshed page, we want to check. + // Initial sync on mount is skipped because import handles it, + // or if page refresh, server sync will pull first. + // But if we want to ensure we get new songs from netease on page load: doSync(); // Interval sync (15 minutes) const interval = setInterval(doSync, 15 * 60 * 1000); return () => clearInterval(interval); } - }, [neteasePlaylistId]); + }, [syncMode, syncToken]); // 1. Auto Sync: Incremental update based on diff (Server Mode) const autoSyncFavorites = async () => { @@ -1136,7 +1124,13 @@ return newFavs; }); - setNeteasePlaylistId(id); + setSyncToken(id); + setLastSuccessToken(id); + setSyncMode('netease_playlist'); + + // Trigger cloud sync immediately after state update + // Note: state update is async, so we might need to rely on the useEffect auto-sync + // which watches 'favorites' change. // Switch view to favorites to see result setView('favorites'); @@ -1151,11 +1145,12 @@ } }; - const stopNeteaseSync = () => { - if(confirm("确定要停止自动同步网易云歌单吗?已导入的歌曲不会被删除。")) { - setNeteasePlaylistId(''); + // Detect manual token change to reset mode + useEffect(() => { + if (!syncToken) { + setSyncMode('server'); } - }; + }, [syncToken]); useEffect(() => { if (currentSong) { @@ -1550,8 +1545,7 @@ setSyncToken={setSyncToken} onSyncNow={manualSyncFavorites} onImportNetease={importNeteasePlaylist} - neteasePlaylistId={neteasePlaylistId} - onStopNeteaseSync={stopNeteaseSync} + syncMode={syncMode} /> {/* Top Navigation / Search Bar */}