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

- {syncMode === 'netease_playlist' - ? '当前为网易云歌单同步模式,每15分钟自动刷新' - : '使用相同的密钥在多端同步收藏列表'} + 私有云多端同步收藏列表

{syncMsg && {syncMsg}}
+
+
- -

导入后将开启自动同步,每15分钟获取新歌

+ + {neteasePlaylistId ? ( +
+
+
ID: {neteasePlaylistId}
+
自动更新中
+
+ +
+ ) : ( + + )} +

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

@@ -921,7 +937,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 [syncMode, setSyncMode] = useState(() => localStorage.getItem('th_sync_mode') || 'server'); // 'server' | 'netease_playlist' + const [neteasePlaylistId, setNeteasePlaylistId] = useState(() => localStorage.getItem('th_netease_playlist_id') || ''); // UI State const [showFullPlayer, setShowFullPlayer] = useState(false); @@ -955,26 +971,26 @@ 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_sync_mode', syncMode); }, [syncMode]); + useEffect(() => { localStorage.setItem('th_netease_playlist_id', neteasePlaylistId); }, [neteasePlaylistId]); // Auto Sync Logic for Private Server useEffect(() => { - // Only auto-sync if we are in server mode, have a token, it matches the last successfully synced token, + // Only auto-sync if we have a token, it matches the last successfully synced token, // and there are actual changes compared to last sync. - if (syncMode === 'server' && syncToken && syncToken === lastSuccessToken && JSON.stringify(favorites) !== JSON.stringify(lastSyncedFavorites)) { + if (syncToken && syncToken === lastSuccessToken && JSON.stringify(favorites) !== JSON.stringify(lastSyncedFavorites)) { const timer = setTimeout(() => { autoSyncFavorites(); }, 1000); return () => clearTimeout(timer); } - }, [favorites, syncToken, lastSuccessToken, lastSyncedFavorites, syncMode]); + }, [favorites, syncToken, lastSuccessToken, lastSyncedFavorites]); // Auto Sync Logic for Netease Playlist (Interval based) useEffect(() => { - if (syncMode === 'netease_playlist' && syncToken) { + if (neteasePlaylistId) { const doSync = async () => { console.log("Auto syncing netease playlist..."); - const songs = await api.getPlaylist(syncToken); + const songs = await api.getPlaylist(neteasePlaylistId); if (songs && songs.length > 0) { setFavorites(prev => { const newFavs = [...prev]; @@ -991,18 +1007,20 @@ } }; - // Initial sync on mount/mode change + // 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. doSync(); // Interval sync (15 minutes) const interval = setInterval(doSync, 15 * 60 * 1000); return () => clearInterval(interval); } - }, [syncMode, syncToken]); + }, [neteasePlaylistId]); // 1. Auto Sync: Incremental update based on diff (Server Mode) const autoSyncFavorites = async () => { - if (!syncToken || syncToken !== lastSuccessToken || syncMode !== 'server') return; + if (!syncToken || syncToken !== lastSuccessToken) return; try { // Get latest remote state const remoteFavorites = await syncService.get('favorites', syncToken) || []; @@ -1046,25 +1064,6 @@ const manualSyncFavorites = async () => { if (!syncToken) return; - // If in Netease Mode, manual sync means fetching playlist again - if (syncMode === 'netease_playlist') { - const songs = await api.getPlaylist(syncToken); - if (songs && songs.length > 0) { - setFavorites(prev => { - const newFavs = [...prev]; - [...songs].reverse().forEach(song => { - if (!newFavs.find(s => s.id === song.id)) { - newFavs.unshift({ ...song, source: 'netease' }); - } - }); - return newFavs; - }); - } else { - throw new Error("获取歌单失败"); - } - return; - } - // Server Mode Sync // Determine Mode: Switch (Overwrite Local) or Sync (Merge) @@ -1136,10 +1135,9 @@ alert(`成功导入 ${addedCount} 首新歌 (共 ${songs.length} 首)`); return newFavs; }); - // Set sync state - setSyncToken(id); - setLastSuccessToken(id); // Treat as synced - setSyncMode('netease_playlist'); + + setNeteasePlaylistId(id); + // Switch view to favorites to see result setView('favorites'); } else { @@ -1153,26 +1151,11 @@ } }; - // Allow user to switch back to server mode manually by clearing token or just editing it - // We need to detect if user manually edits input to something that is not the playlist id - useEffect(() => { - if (syncMode === 'netease_playlist' && syncToken !== lastSuccessToken) { - // If user changes token, revert to server mode implicitly? - // Or wait for them to click sync? - // For now, let's keep it simple. If they click "Import Netease", it forces Netease mode. - // If they type a new token and click Sync, it will try to fetch playlist if mode is netease, - // likely fail, then user might be confused. - // Better: If user types in input, we assume they might want to switch mode if it fails? - // Let's rely on the import button to set the mode. - // If user wants to go back to server mode, they can clear token or we add a toggle? - // The input label changes, so user knows. + const stopNeteaseSync = () => { + if(confirm("确定要停止自动同步网易云歌单吗?已导入的歌曲不会被删除。")) { + setNeteasePlaylistId(''); } - - // If user clears token, reset mode to server (default) - if (!syncToken) { - setSyncMode('server'); - } - }, [syncToken]); + }; useEffect(() => { if (currentSong) { @@ -1567,7 +1550,8 @@ setSyncToken={setSyncToken} onSyncNow={manualSyncFavorites} onImportNetease={importNeteasePlaylist} - syncMode={syncMode} + neteasePlaylistId={neteasePlaylistId} + onStopNeteaseSync={stopNeteaseSync} /> {/* Top Navigation / Search Bar */}