Greasy Fork is available in English.
幫巴哈姆特動畫瘋加上封面 & 自動播放 & 留言區的直連連結 & 彈幕熱圖
// ==UserScript== // @name 巴哈姆特動畫瘋小幫手:封面圖 & 自動開始 & 留言連結 & 彈幕熱圖 // @namespace http://tampermonkey.net/ // @version 1.7.5 // @description 幫巴哈姆特動畫瘋加上封面 & 自動播放 & 留言區的直連連結 & 彈幕熱圖 // @author Rplus // @match https://ani.gamer.com.tw/animeVideo.php?sn=* // @license WTFPL // @grant GM_registerMenuCommand // @grant GM.setValue // @grant GM.getValue // @run-at document-end // ==/UserScript== (async function() { let options = await GM.getValue('options'); if (!options) { options = { cover: true, autostart: true, permalink: true, heatmap: true, heatmapVisibility: true, }; GM.setValue('options', options); }; let optionsText = { cover: '封面圖', autostart: '自動開始', permalink: '留言連結', heatmap: '彈幕熱圖', }; function triggerConfig(type) { options[type] = !options[type]; GM.setValue('options', options); } GM_registerMenuCommand(getMenuText('cover'), () => triggerConfig('cover'), 'C'); GM_registerMenuCommand(getMenuText('autostart'), () => triggerConfig('autostart'), 'A'); GM_registerMenuCommand(getMenuText('permalink'), () => triggerConfig('permalink'), 'P'); GM_registerMenuCommand(getMenuText('heatmap'), () => triggerConfig('heatmap'), 'H'); GM_registerMenuCommand('init', () => init(), 'I'); function getMenuText(type) { return `${options[type] ? '✅ 已啟用' : '❎ 已停用'}:${optionsText[type]}`; } unsafeWindow.addEventListener('load', init); unsafeWindow.navigation.addEventListener('navigate', () => { setTimeout(init, 2000); }); unsafeWindow.BahaWall.showUpload = (e) => { e.closest('.reply-input')?.querySelector('input[type="file"]')?.click(); } document.querySelector('#w-post-box').addEventListener('click', (e) => { if (e.target.tagName !== 'INPUT' || e.target.type !== 'file') { return; } e.target.accept = 'image/*'; }) function init() { if (options.cover) { initCover(); } // latest duration // animefun.breakPoint.breakPoint if (options.autostart) { // auto start when it is not comment permalink if (location.search.indexOf('pcid') === -1) { checkReady(); } } // right click to add permalink if (options.permalink) { document.querySelector('.webview_commendlist').addEventListener('contextmenu', right_click_comment_to_add_permalink); } // danmu heatmap if (options.heatmap) { danmuHelper(); } } function right_click_comment_to_add_permalink(e) { let span = e.target; if (span.className !== 'reply_time') { return; } let a = span.parentElement.querySelector('a.reply_menu'); if (!a) { return; } e.preventDefault(); let config = JSON.parse(a.dataset?.tippyMenuComment); let qs = new URLSearchParams({ sn: new URLSearchParams(location.search).get('sn'), pcid: config.pid || config.cid, }); if (config.pid) { qs.append('cid', config.cid); } let url = `https://ani.gamer.com.tw/animeVideo.php?` + qs.toString(); span.className += ' inited'; span.innerHTML = `<a href="${url}" target="_blank"># ${span.textContent}</a>`; } function initCover() { let cover = (unsafeWindow.ani_video_html5_api?.poster !== location.href && unsafeWindow.ani_video_html5_api?.poster) || unsafeWindow.animefun.poster; // insert poster // document.querySelector('h1')?.insertAdjacentHTML('afterbegin', ` // <a href="${cover}" target="_blank"> // <img src="${cover}" style="float: left; height: 2em; margin-top: 4px; margin-right: 8px;" /> // </a>`); let h1 = document.querySelector('h1'); if (h1) { h1.innerHTML = ` <a href="${cover}" target="_blank"> <img src="${cover}" style="float: left; height: 2em; margin-top: 4px; margin-right: 8px;" /> </a>${h1.textContent}`; } // insert published time let timeTag = document.querySelector('.anime_info_detail .uploadtime'); let time = timeTag?.textContent.split(':')?.[1]; if (time) { timeTag.textContent += ` (${getRelatedDays(time)}天前)`; } } function getRelatedDays(time = new Date()) { return ((new Date() - new Date(time))/(1000*60*60*24)).toFixed(); } function checkReady(counter = 0) { setTimeout(() => { console.log(111, unsafeWindow.adult); if (unsafeWindow.AnimeRoute && unsafeWindow.adult && unsafeWindow.animefun) { unsafeWindow.adult.click(); } else { if (counter < 40) { checkReady(counter + 1); } } }, 1000) } function danmuHelper() { console.log('danmuHelper') let _danmu = unsafeWindow.animefun?.danmu; let sn = new URLSearchParams(location.search)?.get('sn'); if (_danmu && _danmu.length) { danmuAnal(_danmu); } else { fetch('https://ani.gamer.com.tw/ajax/danmuGet.php', { headers: { "Content-Type": "application/x-www-form-urlencoded; charset=UTF-8", }, cache: 'force-cache', body: `sn=${sn}`, method: "POST", }).then(r => r.json()).then(danmuAnal); } } function updateVideoDuration(e) { let key = ''; let target = document.querySelector('.danmu-heatmap'); let new_v = document.getElementById('ani_video_html5_api')?.duration; let old_v = parseFloat(getComputedStyle(target).getPropertyValue('--video-duration')); if (new_v && old_v && new_v > old_v && isFinite(new_v)) { console.log('video-source-duration', {new_v, old_v}); target.style.setProperty('--video-source-duration', new_v); document.getElementById('ani_video_html5_api').removeEventListener('durationchange', updateVideoDuration); } } let timer = 0; function obVideo() { if (!document.getElementById('ani_video_html5_api') && timer < 20) { setTimeout(obVideo, 1000); } else { document.getElementById('ani_video_html5_api').addEventListener('durationchange', updateVideoDuration); } } function danmuAnal(danmu) { console.log('danmuAnal') // let byUser = danmu.reduce((all, i) => { // let uid = i.userid; // if (!all[uid]) { all[uid] = []; } // all[uid].push(i); // return all; // }, {}); let danmu_duration = danmu[danmu.length - 1].time / 10; obVideo(); let danmu_set_item = document.querySelector('.ani-setting-item.danmu'); if (!danmu_set_item) { document.querySelector('#ani-tab-content-2 .ani-setting-section:not(.is-seperate) .ani-setting-item')?.insertAdjacentHTML('afterend', ` <div class="ani-setting-item ani-flex ani-setting-item--danmu"> <div class="ani-setting-label">彈幕熱圖</div> <div class="ani-set-flex-right"> <div class="ani-checkbox"> <label class="ani-checkbox__label"> <input type="checkbox" id="danmu-heatmap-ckbox" ${options.heatmapVisibility ? 'checked' : ''} /> <div class="ani-checkbox__button"></div> </label> </div> </div> </div> `); } document.querySelector('#danmu-heatmap-ckbox')?.addEventListener('change', (e) => { document.querySelector('.danmu-heatmap').hidden = !e.target.checked; triggerConfig('heatmapVisibility'); }); let d1 = document.querySelector('.danmu-heatmap'); let s1 = document.querySelector('.danmu-heatmap-style'); console.log({d1}) if (d1) { d1.remove(); } if (s1) { s1.remove(); } // heatmap let dots = `<div class="danmu-heatmap" ${options.heatmapVisibility ? '' : 'hidden'}>` + danmu.map(i => { return `<i data-time="${i.time / 10}" style="--danmu-time: ${i.time / 10}" title="${i.text}"></i>`; }).join('') + '</div>'; let dots_style = `<style class="danmu-heatmap-style"> .danmu-heatmap { --video-duration: var(--video-source-duration, ${danmu_duration}); position: absolute; left: 0; right: 0; top: 100%; z-index: 1; height: 1em; overflow: hidden; background-color: #000; } .danmu-heatmap i { position: absolute; left: calc(var(--danmu-time, 1) / var(--video-duration) * 100%); margin-left: -0.25em; width: 0.5em; height: 1em; background: #fff; opacity: var(--dh-op, ${danmu.length > 1000 ? 0.05 : 0.1}); } .danmu-heatmap i:hover { opacity: .8; z-index: 2; background: #ff0; } .reply_time a { color: unset; } </style>`; let videoframe = document.querySelector('.videoframe'); videoframe.style.position = 'relative'; videoframe.insertAdjacentHTML('beforeend', dots + dots_style); videoframe.querySelector('.danmu-heatmap').addEventListener('click', danmuJump); unsafeWindow.navigation.addEventListener('navigate', () => { videoframe.querySelector('.danmu-heatmap').removeEventListener('click', danmuJump); }); } function danmuJump(e) { if (e.target.tagName !== 'I') { return; } jumpVideoTime(+e.target.dataset?.time); } function jumpVideoTime(time = 0) { document.getElementById('ani_video_html5_api').currentTime = time; } })();