Download static/js/user-data-sync.js from harii66/gt: direct link, hf CLI and curl.
- Browser
- Download file 7.27 kB
-
https://huggingface.co/spaces/harii66/gt/resolve/main/static/js/user-data-sync.js
- Command line
-
hf download hf://spaces/harii66/gt/static/js/user-data-sync.js
-
curl -L -o user-data-sync.js https://huggingface.co/spaces/harii66/gt/resolve/main/static/js/user-data-sync.js
7.27 kB
| (function() { | |
| 'use strict'; | |
| class UserDataSync { | |
| constructor() { | |
| this.username = null; | |
| this.data = { | |
| favorite_channels: [], | |
| download_concurrency: 16, | |
| batch_download_concurrency: 3, | |
| fab_position: { bottom: 30, right: 30 }, | |
| playback_history: [], | |
| program_reminders: [] | |
| }; | |
| this.isInitialized = false; | |
| this.pendingUpdates = {}; | |
| this.saveTimer = null; | |
| } | |
| /** | |
| * 初始化用户数据 | |
| */ | |
| init(username) { | |
| if (!username) { | |
| console.warn('⚠️ UserDataSync: 未提供用户名'); | |
| return false; | |
| } | |
| this.username = username; | |
| // ✅ 从 sessionStorage 加载(登录时后端已写入) | |
| const userDataKey = `user_data_${username}`; | |
| const savedData = sessionStorage.getItem(userDataKey); | |
| if (savedData) { | |
| try { | |
| const parsed = JSON.parse(savedData); | |
| this.data = { ...this.data, ...parsed }; | |
| console.log('✅ 用户数据已加载:', Object.keys(this.data)); | |
| } catch (e) { | |
| console.error('❌ 解析用户数据失败:', e); | |
| } | |
| } | |
| this.isInitialized = true; | |
| return true; | |
| } | |
| /** | |
| * 标记数据已修改(防抖保存) | |
| */ | |
| markChanged(key) { | |
| if (!this.isInitialized) return; | |
| this.pendingUpdates[key] = this.data[key]; | |
| // 防抖:1秒后保存 | |
| if (this.saveTimer) { | |
| clearTimeout(this.saveTimer); | |
| } | |
| this.saveTimer = setTimeout(() => { | |
| this.save(); | |
| }, 1000); | |
| } | |
| /** | |
| * 立即保存到后端 | |
| */ | |
| async save(force = false) { | |
| if (!this.isInitialized || !this.username) return false; | |
| if (!force && Object.keys(this.pendingUpdates).length === 0) return true; | |
| const updates = force ? this.data : this.pendingUpdates; | |
| try { | |
| // ✅ 保存到 sessionStorage(同步) | |
| const userDataKey = `user_data_${this.username}`; | |
| sessionStorage.setItem(userDataKey, JSON.stringify(this.data)); | |
| // ✅ 通过后端保存到 Redis | |
| const response = await fetch('/api/user/data/sync', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json' | |
| }, | |
| body: JSON.stringify({ | |
| username: this.username, | |
| data: updates | |
| }) | |
| }); | |
| if (response.ok) { | |
| console.log('✅ 用户数据已同步到 Redis:', Object.keys(updates)); | |
| this.pendingUpdates = {}; | |
| return true; | |
| } else { | |
| console.warn('⚠️ 同步失败,数据已保存到本地'); | |
| return false; | |
| } | |
| } catch (error) { | |
| console.error('❌ 同步失败:', error); | |
| return false; | |
| } | |
| } | |
| // ==================== 便捷方法 ==================== | |
| getFavorites() { | |
| return this.data.favorite_channels || []; | |
| } | |
| setFavorites(favorites) { | |
| this.data.favorite_channels = Array.isArray(favorites) ? favorites : []; | |
| this.markChanged('favorite_channels'); | |
| } | |
| getDownloadConcurrency() { | |
| return this.data.download_concurrency || 16; | |
| } | |
| setDownloadConcurrency(concurrency) { | |
| const value = parseInt(concurrency); | |
| if (value >= 1 && value <= 32) { | |
| this.data.download_concurrency = value; | |
| this.markChanged('download_concurrency'); | |
| } | |
| } | |
| getBatchConcurrency() { | |
| return this.data.batch_download_concurrency || 3; | |
| } | |
| setBatchConcurrency(concurrency) { | |
| const value = parseInt(concurrency); | |
| if (value >= 1 && value <= 10) { | |
| this.data.batch_download_concurrency = value; | |
| this.markChanged('batch_download_concurrency'); | |
| } | |
| } | |
| getFabPosition() { | |
| return this.data.fab_position || { bottom: 30, right: 30 }; | |
| } | |
| setFabPosition(position) { | |
| if (position && typeof position === 'object') { | |
| this.data.fab_position = position; | |
| this.markChanged('fab_position'); | |
| } | |
| } | |
| getPlaybackHistory() { | |
| return this.data.playback_history || []; | |
| } | |
| addPlaybackHistory(item) { | |
| if (!Array.isArray(this.data.playback_history)) { | |
| this.data.playback_history = []; | |
| } | |
| // 去重 | |
| this.data.playback_history = this.data.playback_history.filter( | |
| h => h.path !== item.path | |
| ); | |
| // 添加到开头 | |
| this.data.playback_history.unshift(item); | |
| // 最多保留 50 条 | |
| if (this.data.playback_history.length > 50) { | |
| this.data.playback_history = this.data.playback_history.slice(0, 50); | |
| } | |
| this.markChanged('playback_history'); | |
| } | |
| getProgramReminders() { | |
| return this.data.program_reminders || []; | |
| } | |
| addProgramReminder(reminder) { | |
| if (!Array.isArray(this.data.program_reminders)) { | |
| this.data.program_reminders = []; | |
| } | |
| const exists = this.data.program_reminders.some( | |
| r => r.title === reminder.title && r.startTime === reminder.startTime | |
| ); | |
| if (!exists) { | |
| this.data.program_reminders.push(reminder); | |
| this.markChanged('program_reminders'); | |
| } | |
| } | |
| removeProgramReminder(reminderId) { | |
| if (Array.isArray(this.data.program_reminders)) { | |
| this.data.program_reminders = this.data.program_reminders.filter( | |
| r => r.id !== reminderId | |
| ); | |
| this.markChanged('program_reminders'); | |
| } | |
| } | |
| } | |
| // 创建全局单例 | |
| window.userDataSync = new UserDataSync(); | |
| // 页面卸载时保存 | |
| window.addEventListener('beforeunload', () => { | |
| if (window.userDataSync && window.userDataSync.isInitialized) { | |
| window.userDataSync.save(true); | |
| } | |
| }); | |
| console.log('✅ UserDataSync 已加载(无需 API)'); | |
| })(); |