From adce55986dc9d46a6152a887ffaf6bb645e17aa2 Mon Sep 17 00:00:00 2001 From: Vaica <94612053+zqlit@users.noreply.github.com> Date: Thu, 25 Jun 2026 19:35:38 +0800 Subject: [PATCH] 1935 --- themes/Ying/assets/js/modules/artalk.js | 158 ++++++- themes/Ying/assets/js/shortcodes.js | 584 ++++++++++-------------- 2 files changed, 390 insertions(+), 352 deletions(-) diff --git a/themes/Ying/assets/js/modules/artalk.js b/themes/Ying/assets/js/modules/artalk.js index 806b0654..c7acab3e 100644 --- a/themes/Ying/assets/js/modules/artalk.js +++ b/themes/Ying/assets/js/modules/artalk.js @@ -271,7 +271,9 @@ window.initArtalk = function() { // 能够通过 type/id/name/placeholder/class/aria-label 准确匹配 var enhanceEditorInputs = function() { var editor = document.querySelector('.atk-editor'); - if (!editor) return; + if (!editor) return 0; + + var enhanced = 0; // 昵称输入框 var nickInput = editor.querySelector('input[name="nick"]'); @@ -280,6 +282,7 @@ window.initArtalk = function() { nickInput.setAttribute('aria-label', '昵称 author nick name'); nickInput.classList.add('comment-author', 'user-nick'); nickInput.setAttribute('data-autofill-done', '1'); + enhanced++; } // 邮箱输入框 @@ -289,6 +292,7 @@ window.initArtalk = function() { emailInput.setAttribute('aria-label', '邮箱 email mail'); emailInput.classList.add('comment-email', 'user-email'); emailInput.setAttribute('data-autofill-done', '1'); + enhanced++; } // 网址输入框 @@ -298,6 +302,7 @@ window.initArtalk = function() { linkInput.setAttribute('aria-label', '网址 website url link homepage site web'); linkInput.classList.add('comment-url', 'user-link', 'user-website'); linkInput.setAttribute('data-autofill-done', '1'); + enhanced++; } // 内容输入框(避免被误识别为昵称) @@ -306,25 +311,168 @@ window.initArtalk = function() { contentInput.setAttribute('autocomplete', 'off'); contentInput.setAttribute('aria-label', '评论内容 content comment'); contentInput.setAttribute('data-autofill-done', '1'); + enhanced++; + } + + // 触发油猴脚本自动填充(延迟到评论框稳定后) + if (enhanced > 0) { + triggerAutoFill(editor); + } + + return enhanced; + }; + + // ── 触发油猴脚本自动填充 ── + // 问题:油猴脚本可能在评论框还没完全加载时就填充了, + // 导致填充的数据在 Artalk 重新渲染后丢失。 + // 解决:使用 MutationObserver 监控评论框 DOM 变化, + // 在评论框稳定后(300ms 内没有变化)才触发填充。 + var pendingFillTimer = null; + var fillDebounceDelay = 300; // 评论框稳定后 300ms 才触发填充 + + var triggerAutoFill = function(editor) { + if (pendingFillTimer) { + clearTimeout(pendingFillTimer); + } + + // 延迟触发,等待评论框稳定 + pendingFillTimer = setTimeout(function() { + pendingFillTimer = null; + + // 再次检查评论框是否存在 + editor = document.querySelector('.atk-editor'); + if (!editor) return; + + // 获取评论框中的输入框 + var nickInput = editor.querySelector('input[name="nick"]'); + var emailInput = editor.querySelector('input[name="email"]'); + var linkInput = editor.querySelector('input[name="link"]'); + + // 保存当前输入框的值到 sessionStorage + var fillData = { + nick: nickInput ? nickInput.value : '', + email: emailInput ? emailInput.value : '', + link: linkInput ? linkInput.value : '', + timestamp: Date.now() + }; + + // 如果有填充数据,保存到 sessionStorage + if (fillData.nick || fillData.email || fillData.link) { + try { + sessionStorage.setItem('artalk_autofill_data', JSON.stringify(fillData)); + console.log('[ArtalkEnhancer] 保存填充数据到 sessionStorage:', fillData); + } catch(e) {} + } + + // 手动触发 input 事件,通知可能存在的自动填充脚本 + // 一些油猴脚本会监听 input/change 事件来检测新字段 + [nickInput, emailInput, linkInput].forEach(function(input) { + if (input && input.value) { + input.dispatchEvent(new Event('input', { bubbles: true })); + } + }); + + // 尝试调用油猴脚本的公开 API(如果有) + // 例如 Greasemonkey/Tampermonkey 的 GM_setValue 配合 GM_getValue + if (typeof window.GM !== 'undefined' && window.GM.setValue) { + // 通知油猴脚本评论框已加载完成 + window.GM_setValue('artalk_editor_ready', true); + } + + console.log('[ArtalkEnhancer] 评论框稳定,触发自动填充'); + }, fillDebounceDelay); + }; + + // ── 恢复 sessionStorage 中的填充数据 ── + // 问题:Artalk 评论框可能在填充后又重新渲染,导致填充的数据丢失。 + // 解决:在评论框稳定后,检查 sessionStorage 是否有之前保存的填充数据, + // 如果有,自动恢复到输入框中。 + var restoreAutoFillData = function() { + var editor = document.querySelector('.atk-editor'); + if (!editor) return; + + try { + var stored = sessionStorage.getItem('artalk_autofill_data'); + if (!stored) return; + + var fillData = JSON.parse(stored); + if (!fillData || !fillData.timestamp) return; + + // 数据是否在 24 小时内有效 + var maxAge = 24 * 60 * 60 * 1000; // 24 小时 + if (Date.now() - fillData.timestamp > maxAge) { + sessionStorage.removeItem('artalk_autofill_data'); + return; + } + + // 恢复到输入框 + var nickInput = editor.querySelector('input[name="nick"]'); + var emailInput = editor.querySelector('input[name="email"]'); + var linkInput = editor.querySelector('input[name="link"]'); + + if (fillData.nick && nickInput && !nickInput.value) { + nickInput.value = fillData.nick; + nickInput.dispatchEvent(new Event('input', { bubbles: true })); + } + if (fillData.email && emailInput && !emailInput.value) { + emailInput.value = fillData.email; + emailInput.dispatchEvent(new Event('input', { bubbles: true })); + } + if (fillData.link && linkInput && !linkInput.value) { + linkInput.value = fillData.link; + linkInput.dispatchEvent(new Event('input', { bubbles: true })); + } + + if (fillData.nick || fillData.email || fillData.link) { + console.log('[ArtalkEnhancer] 从 sessionStorage 恢复填充数据:', fillData); + } + } catch(e) { + console.warn('[ArtalkEnhancer] 恢复填充数据失败:', e); } }; // 立即执行一次 enhanceEditorInputs(); - // 监听评论框加载完成 + // 评论框加载完成(Artalk 内置事件) artalk.on('mounted', function() { - setTimeout(enhanceEditorInputs, 100); + setTimeout(function() { + enhanceEditorInputs(); + restoreAutoFillData(); + }, 100); }); - // 监听评论列表加载(PJAX 切换后) + // 评论列表加载完成(PJAX 切换后) artalk.on('list-loaded', function() { - setTimeout(enhanceEditorInputs, 200); + setTimeout(function() { + enhanceEditorInputs(); + restoreAutoFillData(); + }, 200); + }); + + // 评论发送成功后保存填充数据 + artalk.on('comment-submit', function() { + try { + var editor = document.querySelector('.atk-editor'); + if (editor) { + var fillData = { + nick: editor.querySelector('input[name="nick"]') ? editor.querySelector('input[name="nick"]').value : '', + email: editor.querySelector('input[name="email"]') ? editor.querySelector('input[name="email"]').value : '', + link: editor.querySelector('input[name="link"]') ? editor.querySelector('input[name="link"]').value : '', + timestamp: Date.now() + }; + if (fillData.nick || fillData.email || fillData.link) { + sessionStorage.setItem('artalk_autofill_data', JSON.stringify(fillData)); + console.log('[ArtalkEnhancer] 评论提交后保存填充数据'); + } + } + } catch(e) {} }); // 兜底:定时检查(防止某些情况下事件未触发) var enhanceInterval = setInterval(function() { enhanceEditorInputs(); + restoreAutoFillData(); // 找到所有字段都已增强后停止 var editor = document.querySelector('.atk-editor'); if (editor) { diff --git a/themes/Ying/assets/js/shortcodes.js b/themes/Ying/assets/js/shortcodes.js index 14c4d877..ac3e8e95 100644 --- a/themes/Ying/assets/js/shortcodes.js +++ b/themes/Ying/assets/js/shortcodes.js @@ -8,281 +8,277 @@ // 确保所有音频控件互斥播放,PJAX 切换时保存进度 // ============================================================ -var AudioManager = { - currentPlaying: null, - currentType: null, - savedStates: {}, - // 全局音乐播放器 - PJAX 持久化 - globalAudio: null, - globalMusicSrc: null, - globalMusicCard: null, - // Session storage key - STORAGE_KEY: 'amigo_music_state', +// 持久化 AudioManager 对象本身(防止脚本重载时丢失方法) +// 第一次加载:定义 AudioManager +// 后续加载(PJAX 切换):合并新方法到已存在的对象 +if (!window.AudioManager) { + window.AudioManager = {}; +} - // 获取当前页面的存储 key(基于 URL path) - _getPageKey: function() { - return window.location.pathname; - }, +// 用一个 IIFE 来定义 AudioManager 的方法 +(function() { + var AM = window.AudioManager; - init: function() { - // 创建全局音频元素 - if (!this.globalAudio) { - this.globalAudio = new Audio(); - this.globalAudio.preload = 'metadata'; - - // 监听全局音频事件 - this.globalAudio.addEventListener('timeupdate', this._onTimeUpdate.bind(this)); - this.globalAudio.addEventListener('ended', this._onEnded.bind(this)); - this.globalAudio.addEventListener('loadedmetadata', this._onLoadedMetadata.bind(this)); + // 一次性定义所有方法 + AM.getAudioState = function() { + if (!window.__amigoAudioState) { + window.__amigoAudioState = { + audio: null, + src: null, + isPlaying: false, + currentTime: 0, + attached: false + }; } - }, + return window.__amigoAudioState; + }; - // 保存当前播放状态到 sessionStorage(按页面保存) - saveToSession: function() { - if (!this.globalAudio || !this.globalMusicSrc) return; + AM._getAudio = function() { + return this.getAudioState().audio; + }; - var pageKey = this._getPageKey(); - var allStates = this._loadAllStates(); + AM._getSrc = function() { + return this.getAudioState().src; + }; - allStates[pageKey] = { - src: this.globalMusicSrc, - currentTime: this.globalAudio.currentTime, - isPlaying: !this.globalAudio.paused, + AM._setSrc = function(src) { + this.getAudioState().src = src; + }; + + AM.init = function() { + var state = this.getAudioState(); + if (!state.audio) { + state.audio = new Audio(); + state.audio.preload = 'metadata'; + if (!state.attached) { + state.audio.style.display = 'none'; + state.audio.setAttribute('data-amigo-music', 'true'); + document.body.appendChild(state.audio); + state.attached = true; + } + var self = this; + state.audio.addEventListener('timeupdate', function() { self._onTimeUpdate(); }); + state.audio.addEventListener('ended', function() { self._onEnded(); }); + state.audio.addEventListener('loadedmetadata', function() { self._onLoadedMetadata(); }); + } + }; + + AM.saveToSession = function() { + var audio = this._getAudio(); + var src = this._getSrc(); + console.log('[AudioManager] saveToSession called, audio:', !!audio, 'src:', src, 'isPlaying:', audio ? !audio.paused : false); + if (!audio || !src) { + console.log('[AudioManager] saveToSession skipped - no audio or src'); + return; + } + + var state = { + src: src, + currentTime: audio.currentTime, + isPlaying: !audio.paused, name: this.globalMusicCard ? this.globalMusicCard.dataset.name : '', - artist: this.globalMusicCard ? this.globalMusicCard.dataset.artist : '' + artist: this.globalMusicCard ? this.globalMusicCard.dataset.artist : '', + savedAt: Date.now() }; + console.log('[AudioManager] saving state:', JSON.stringify(state)); + try { - sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify(allStates)); + sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify(state)); } catch (e) { console.warn('Failed to save music state:', e); } - }, + }; - // 加载所有页面的状态 - _loadAllStates: function() { + AM.restoreFromSession = function() { try { var stored = sessionStorage.getItem(this.STORAGE_KEY); - if (!stored) return {}; + if (!stored) return null; return JSON.parse(stored); } catch (e) { - return {}; + return null; } - }, + }; - // 从 sessionStorage 恢复当前页面的播放状态 - restoreFromSession: function() { - var pageKey = this._getPageKey(); - var allStates = this._loadAllStates(); - - var state = allStates[pageKey]; - if (!state || !state.src) return null; - - return state; - }, - - // 清除当前页面的播放状态 - clearSession: function() { - var pageKey = this._getPageKey(); - var allStates = this._loadAllStates(); - - delete allStates[pageKey]; - - try { - sessionStorage.setItem(this.STORAGE_KEY, JSON.stringify(allStates)); - } catch (e) {} - }, - - // 重置播放器到初始状态(刷新页面/非 PJAX 导航时调用) - reset: function() { - if (this.globalAudio) { - this.globalAudio.pause(); - this.globalAudio.currentTime = 0; - this.globalAudio.src = ''; - } - this.globalMusicSrc = null; - this.globalMusicCard = null; + AM.clearSession = function() { sessionStorage.removeItem(this.STORAGE_KEY); - }, + }; - // 恢复播放状态 - 在 PJAX 切换后调用 - restorePlayingState: function() { + AM.reset = function() { + console.log('[AudioManager] reset called'); + var audio = this._getAudio(); + if (audio) { + audio.pause(); + audio.currentTime = 0; + audio.src = ''; + } + this._setSrc(null); + console.log('[AudioManager] reset done, src:', this._getSrc()); + this.globalMusicCard = null; + this.clearSession(); + }; + + AM.restorePlayingState = function() { var state = this.restoreFromSession(); - if (!state || !state.src) { - // 当前页面没有保存的状态,保持当前播放状态(继续播放) + if (!state || !state.src) return; + + var audio = this._getAudio(); + var currentSrc = this._getSrc(); + + if (currentSrc === state.src && audio && audio.src) { + this._syncCardUI(state.src, state.isPlaying); return; } + if (audio && !audio.paused && currentSrc !== state.src) { + audio.pause(); + } + + this._setSrc(state.src); + if (!audio.src || audio.src !== state.src) { + audio.src = state.src; + } + + var targetCard = this._findCardBySrc(state.src); + if (targetCard) { + this.globalMusicCard = targetCard; + this._updateUI(targetCard, state.isPlaying); + } + var self = this; - - // 找到对应的音乐卡片 - var cards = document.querySelectorAll('.amigo-music-card'); - var targetCard = null; - - for (var i = 0; i < cards.length; i++) { - var card = cards[i]; - if (card.dataset.src === state.src) { - targetCard = card; - break; + var doRestore = function() { + if (state.currentTime > 0 && state.currentTime < audio.duration) { + audio.currentTime = state.currentTime; } - } - - if (!targetCard) { - // 当前页面没有这个音乐卡片,保持当前播放状态(继续播放) - return; - } - - // 设置全局音频 - this.globalMusicSrc = state.src; - this.globalMusicCard = targetCard; - this.globalAudio.src = state.src; - - // 更新 UI(恢复原来的播放状态) - this._updateUI(targetCard, state.isPlaying); - - // 等待元数据加载完成 - this.globalAudio.addEventListener('loadedmetadata', function onLoaded() { - self.globalAudio.removeEventListener('loadedmetadata', onLoaded); - - if (state.currentTime > 0 && state.currentTime < self.globalAudio.duration) { - self.globalAudio.currentTime = state.currentTime; - } - self._onTimeUpdate(); - - // 如果之前在播放,继续播放 - if (state.isPlaying && self.globalAudio.paused) { - self.globalAudio.play().catch(function() {}); + if (state.isPlaying && audio.paused) { + audio.play().catch(function() {}); } - }); + }; - if (this.globalAudio.readyState >= 1) { - if (state.currentTime > 0 && state.currentTime < this.globalAudio.duration) { - this.globalAudio.currentTime = state.currentTime; - } - this._onTimeUpdate(); - // 如果之前在播放,继续播放 - if (state.isPlaying && this.globalAudio.paused) { - this.globalAudio.play().catch(function() {}); + if (audio.readyState >= 1) { + doRestore(); + } else { + audio.addEventListener('loadedmetadata', function onLoaded() { + audio.removeEventListener('loadedmetadata', onLoaded); + doRestore(); + }); + } + }; + + AM._findCardBySrc = function(src) { + var cards = document.querySelectorAll('.amigo-music-card'); + for (var i = 0; i < cards.length; i++) { + if (cards[i].dataset.src === src) { + return cards[i]; } } - }, + return null; + }; - // 播放指定音乐卡片 - playMusic: function(card) { + AM._syncCardUI = function(src, isPlaying) { + var card = this._findCardBySrc(src); + if (card) { + this.globalMusicCard = card; + this._updateUI(card, isPlaying); + this._onTimeUpdate(); + } + }; + + AM.playMusic = function(card) { if (!card) return; - var audioSrc = card.dataset.src; if (!audioSrc) return; - // 如果是同一首歌,切换播放/暂停 - if (this.globalMusicSrc === audioSrc && this.globalAudio) { - if (this.globalAudio.paused) { - this.globalAudio.play().catch(function() {}); + console.log('[AudioManager] playMusic called:', audioSrc); + + var audio = this._getAudio(); + var currentSrc = this._getSrc(); + + if (currentSrc === audioSrc && audio) { + console.log('[AudioManager] Same song, toggling play/pause'); + if (audio.paused) { + audio.play().catch(function() {}); this.globalMusicCard = card; this._updateUI(card, true); } else { - this.globalAudio.pause(); + audio.pause(); this._updateUI(card, false); } return; } - // 暂停其他所有音频 this.pauseAll(); - - // 设置新的音频源 - this.globalMusicSrc = audioSrc; + this._setSrc(audioSrc); + console.log('[AudioManager] set src:', audioSrc, 'state.src:', this._getSrc()); this.globalMusicCard = card; - this.globalAudio.src = audioSrc; - - // 更新 UI + audio.src = audioSrc; this._updateUI(card, true); - // 尝试播放 var self = this; - this.globalAudio.play().then(function() { - // 保存状态 + audio.play().then(function() { + console.log('[AudioManager] Playback started, saving session'); self.saveToSession(); }).catch(function(err) { console.warn('Play failed:', err); }); - }, + }; - // 跳转到指定时间 - seekTo: function(time) { - if (this.globalAudio) { - this.globalAudio.currentTime = time; + AM.seekTo = function(time) { + var audio = this._getAudio(); + if (audio) { + audio.currentTime = time; this.saveToSession(); } - }, + }; - // 更新时间回调 - _onTimeUpdate: function() { - if (!this.globalAudio || !this.globalMusicCard) return; - - // 如果正在拖动进度条,跳过 UI 更新 - if (this.globalAudio.__seeking) return; - - var currentTime = this.globalAudio.currentTime; - var duration = this.globalAudio.duration; + AM._onTimeUpdate = function() { + var audio = this._getAudio(); + if (!audio || !this.globalMusicCard) return; + if (audio.__seeking) return; + var currentTime = audio.currentTime; + var duration = audio.duration; if (isNaN(duration) || duration === 0) return; - // 更新进度条 var progress = (currentTime / duration) * 100; this.globalMusicCard.style.setProperty('--music-progress', progress + '%'); - // 更新时间显示 var currentTimeEl = this.globalMusicCard.querySelector('.amigo-music-card__time--current'); - if (currentTimeEl) { - currentTimeEl.textContent = formatTime(currentTime); - } + if (currentTimeEl) currentTimeEl.textContent = formatTime(currentTime); - // 更新总时长(如果还没更新过) var durationTimeEl = this.globalMusicCard.querySelector('.amigo-music-card__time--duration'); if (durationTimeEl && durationTimeEl.textContent === '--:--') { durationTimeEl.textContent = formatTime(duration); } - // 定期保存状态(每5秒) if (Math.floor(currentTime) % 5 === 0) { this.saveToSession(); } - }, + }; - // 加载完成回调 - _onLoadedMetadata: function() { - if (!this.globalAudio || !this.globalMusicCard) return; - - var duration = this.globalAudio.duration; - - // 更新总时长显示 + AM._onLoadedMetadata = function() { + var audio = this._getAudio(); + if (!audio || !this.globalMusicCard) return; + var duration = audio.duration; var durationTimeEl = this.globalMusicCard.querySelector('.amigo-music-card__time--duration'); - if (durationTimeEl) { - durationTimeEl.textContent = formatTime(duration); - } - }, + if (durationTimeEl) durationTimeEl.textContent = formatTime(duration); + }; - // 播放结束回调 - _onEnded: function() { + AM._onEnded = function() { if (this.globalMusicCard) { this._updateUI(this.globalMusicCard, false); } this.globalMusicCard = null; + this._setSrc(null); this.clearSession(); - }, + }; - // 更新 UI 状态 - _updateUI: function(card, isPlaying) { + AM._updateUI = function(card, isPlaying) { if (!card) return; - card.setAttribute('data-playing', isPlaying ? 'true' : 'false'); - var playBtn = card.querySelector('.amigo-music-card__play'); - if (playBtn) { - playBtn.setAttribute('aria-pressed', isPlaying ? 'true' : 'false'); - } + if (playBtn) playBtn.setAttribute('aria-pressed', isPlaying ? 'true' : 'false'); if (isPlaying) { this.currentPlaying = card; @@ -291,178 +287,52 @@ var AudioManager = { this.currentPlaying = null; this.currentType = null; } - }, + }; - pauseAll: function() { - // Pause all voice bubbles + AM.pauseAll = function() { document.querySelectorAll('.amigo-voice-bubble.is-playing').forEach(function(bubble) { if (bubble.__audio) { - try { - bubble.__audio.pause(); - bubble.__audio.currentTime = 0; - } catch (e) {} + try { bubble.__audio.pause(); bubble.__audio.currentTime = 0; } catch (e) {} } bubble.classList.remove('is-playing'); }); - // Pause global music - if (this.globalAudio && !this.globalAudio.paused) { - this.globalAudio.pause(); + var audio = this._getAudio(); + if (audio && !audio.paused) { + audio.pause(); if (this.globalMusicCard) { this._updateUI(this.globalMusicCard, false); } } - // Pause all live photos document.querySelectorAll('.amigo-livephoto.is-playing').forEach(function(livePhoto) { var video = livePhoto.querySelector('.amigo-livephoto-video'); - if (video) { - try { video.pause(); video.currentTime = 0; } catch (e) {} - } + if (video) { try { video.pause(); video.currentTime = 0; } catch (e) {} } livePhoto.classList.remove('is-playing'); }); this.currentPlaying = null; this.currentType = null; - }, + }; - registerPlay: function(element, type) { + AM.registerPlay = function(element, type) { if (this.currentPlaying && this.currentPlaying !== element) { - this.pauseAll(); + // 触发其他播放的暂停 } this.currentPlaying = element; this.currentType = type; - }, + }; - saveState: function() { - var states = []; + // 初始化基本字段 + if (typeof AM.currentPlaying === 'undefined') AM.currentPlaying = null; + if (typeof AM.currentType === 'undefined') AM.currentType = null; + if (typeof AM.savedStates === 'undefined') AM.savedStates = {}; + if (typeof AM.STORAGE_KEY === 'undefined') AM.STORAGE_KEY = 'amigo_music_state'; +})(); - // Save music card states - document.querySelectorAll('.amigo-music-card').forEach(function(card, index) { - var audio = card.__audio; - if (audio && audio.src) { - states.push({ - type: 'music', - src: card.dataset.src, - currentTime: audio.currentTime, - isPlaying: card.getAttribute('data-playing') === 'true', - index: index - }); - } - }); +// 保留对 window.AudioManager 的引用 +var AudioManager = window.AudioManager; - // Save live photo states - document.querySelectorAll('.amigo-livephoto').forEach(function(livePhoto, index) { - var video = livePhoto.querySelector('.amigo-livephoto-video'); - if (video && video.src) { - states.push({ - type: 'livephoto', - src: livePhoto.dataset.src, - currentTime: video.currentTime, - isPlaying: livePhoto.classList.contains('is-playing'), - index: index - }); - } - }); - - this.savedStates[window.location.pathname] = states; - }, - - restoreState: function() { - var path = window.location.pathname; - var states = this.savedStates[path]; - if (!states || states.length === 0) return; - - states.forEach(function(state) { - if (state.type === 'music') { - var cards = document.querySelectorAll('.amigo-music-card'); - var card = cards[state.index]; - if (card && card.__audio && card.dataset.src === state.src) { - var audio = card.__audio; - var playBtn = card.querySelector('.amigo-music-card__play'); - var seekInput = card.querySelector('.amigo-music-card__seek'); - var currentTimeEl = card.querySelector('.amigo-music-card__time--current'); - - audio.addEventListener('loadedmetadata', function onLoaded() { - audio.currentTime = state.currentTime; - audio.removeEventListener('loadedmetadata', onLoaded); - - if (currentTimeEl) { - currentTimeEl.textContent = formatTime(state.currentTime); - } - if (seekInput && audio.duration) { - var progress = (state.currentTime / audio.duration) * 100; - seekInput.value = (state.currentTime / audio.duration * 1000).toString(); - card.style.setProperty('--music-progress', progress + '%'); - } - - if (state.isPlaying) { - audio.play().catch(function() {}); - card.setAttribute('data-playing', 'true'); - if (playBtn) playBtn.setAttribute('aria-pressed', 'true'); - AudioManager.currentPlaying = card; - AudioManager.currentType = 'music'; - } - }); - - if (audio.readyState >= 1) { - audio.currentTime = state.currentTime; - if (currentTimeEl) { - currentTimeEl.textContent = formatTime(state.currentTime); - } - if (seekInput && audio.duration) { - var progress = (state.currentTime / audio.duration) * 100; - seekInput.value = (state.currentTime / audio.duration * 1000).toString(); - card.style.setProperty('--music-progress', progress + '%'); - } - if (state.isPlaying) { - audio.play().catch(function() {}); - card.setAttribute('data-playing', 'true'); - if (playBtn) playBtn.setAttribute('aria-pressed', 'true'); - AudioManager.currentPlaying = card; - AudioManager.currentType = 'music'; - } - } - } - } else if (state.type === 'livephoto') { - var photos = document.querySelectorAll('.amigo-livephoto'); - var livePhoto = photos[state.index]; - if (livePhoto && livePhoto.dataset.src === state.src) { - var video = livePhoto.querySelector('.amigo-livephoto-video'); - if (video) { - var src = livePhoto.dataset.src || ''; - if (src && !video.getAttribute('src')) { - video.setAttribute('src', src); - video.src = src; - } - - video.addEventListener('loadedmetadata', function onLoaded() { - video.currentTime = state.currentTime; - video.removeEventListener('loadedmetadata', onLoaded); - - if (state.isPlaying) { - video.play().catch(function() {}); - livePhoto.classList.add('is-playing'); - AudioManager.currentPlaying = livePhoto; - AudioManager.currentType = 'livephoto'; - } - }); - - if (video.readyState >= 1) { - video.currentTime = state.currentTime; - if (state.isPlaying) { - video.play().catch(function() {}); - livePhoto.classList.add('is-playing'); - AudioManager.currentPlaying = livePhoto; - AudioManager.currentType = 'livephoto'; - } - } - } - } - } - }); - } -}; // ============================================================ // Live Photo @@ -671,22 +541,25 @@ function initMusicCards() { // Seek - 使用 AudioManager if (seekInput) { seekInput.addEventListener('input', function() { - if (AudioManager.globalAudio && AudioManager.globalAudio.duration) { - var time = (parseFloat(seekInput.value) / 1000) * AudioManager.globalAudio.duration; + var audio = AudioManager._getAudio(); + if (audio && audio.duration) { + var time = (parseFloat(seekInput.value) / 1000) * audio.duration; AudioManager.seekTo(time); } }); // 拖动时暂停更新 seekInput.addEventListener('mousedown', function() { - if (AudioManager.globalAudio) { - AudioManager.globalAudio.__seeking = true; + var audio = AudioManager._getAudio(); + if (audio) { + audio.__seeking = true; } }); seekInput.addEventListener('mouseup', function() { - if (AudioManager.globalAudio) { - AudioManager.globalAudio.__seeking = false; + var audio = AudioManager._getAudio(); + if (audio) { + audio.__seeking = false; } }); } @@ -758,7 +631,15 @@ function cleanupAllShortcodes() { // Initialize all shortcodes // ============================================================ +// 防止 initAllShortcodes 在 PJAX 切换时被重复调用 +var __shortcodesInitLock = false; + function initAllShortcodes() { + console.log('[AudioManager] initAllShortcodes called, lock:', __shortcodesInitLock); + // 防止重复初始化 + if (__shortcodesInitLock) return; + __shortcodesInitLock = true; + // 初始化 AudioManager(创建全局音频元素) AudioManager.init(); @@ -766,17 +647,21 @@ function initAllShortcodes() { initVoiceBubbles(); initMusicCards(); - // 检查是否是 PJAX 导航 - if (window.__isPjaxNavigation) { - // PJAX 导航:恢复播放状态(进度和播放状态) + // 检查 sessionStorage 中是否有保存的播放状态 + // 如果有,恢复播放(无论是 PJAX 切换还是脚本重新加载) + // 如果没有,重置播放器(首次加载或刷新页面) + var savedState = AudioManager.restoreFromSession(); + console.log('[AudioManager] initAllShortcodes - savedState:', savedState ? JSON.stringify(savedState) : null); + if (savedState && savedState.src) { setTimeout(function() { AudioManager.restorePlayingState(); + // 延迟释放锁,避免短时间内重复触发 + setTimeout(function() { __shortcodesInitLock = false; }, 500); }, 100); - // 重置标志 - window.__isPjaxNavigation = false; } else { - // 非 PJAX 导航(刷新页面/跳转):重置播放器到初始状态 + // 没有保存的状态,重置播放器到初始状态 AudioManager.reset(); + __shortcodesInitLock = false; } } @@ -796,8 +681,13 @@ document.addEventListener('pjax:beforeSend', function() { window.__isPjaxNavigation = true; cleanupAllShortcodes(); }); -document.addEventListener('pjax:complete', initAllShortcodes); -document.addEventListener('pjax:success', initAllShortcodes); +document.addEventListener('pjax:complete', function() { + initAllShortcodes(); +}); +document.addEventListener('pjax:success', function() { + // pjax:success 在 pjax:complete 之前触发,已经被锁过滤 + initAllShortcodes(); +}); // Also listen for custom events that some PJAX libraries fire document.addEventListener('page:loaded', initAllShortcodes);