马冬什么?什么冬梅?马什么梅?
// ==UserScript== // @name B 站复读机 // @namespace bilibili-replayer // @version 0.3 // @description 马冬什么?什么冬梅?马什么梅? // @author dms // @match https://www.bilibili.com/video/BV* // @match https://www.bilibili.com/bangumi/play/ep* // @match https://www.bilibili.com/medialist/play/* // @icon https://www.google.com/s2/favicons?sz=64&domain=bilibili.com // @grant none // ==/UserScript== (function() { 'use strict'; const isNormalVide = /^(https?:\/\/(www\.)bilibili\.com\/video\/(?:BV|AV)\w+).*/i.test(window.location.href) const copyText = text => { const textArea = document.createElement('textarea') textArea.setAttribute('readonly', 'readonly') textArea.value = text document.body.appendChild(textArea) textArea.select() document.execCommand('copy') document.body.removeChild(textArea) } const createButton = (text, className, parent)=>{ const button = document.createElement('div') className.split(' ').forEach(c => button.classList.add(c) ) button.innerText = text parent.appendChild(button) return button } const createToolbar = ()=>{ const toolbarbox = document.createElement('div') toolbarbox.style = 'width: 100%; height: 2rem;' const container = document.body.querySelector('#playerWrap') || document.body.querySelector('#player_module') container.appendChild(toolbarbox) if(document.body.querySelector('#player_module')){ document.body.querySelector('#player_module').style.marginBottom = '2rem' } const toolbarShadow = toolbarbox.attachShadow({mode: 'closed'}) const toolbarStyle = document.createElement('style') toolbarStyle.innerHTML = ` #replayer-toolbar { width: 100% height: 2rem font-size: 1.2rem; line-height: 1.6rem; margin: .2rem 0; } #replayer-toolbar::after { content: " "; display: block; clear: both; } .tool-item { float: left; padding: 0 .5rem; border-radius: .2rem; } .tool-button { border: 1px solid rgba(0, 0, 0, .1); cursor: pointer; } .active-button { background-color: #00aeec; color: white; } .tool-right { float: right; } .tool-coffee { cursor: pointer; } .hide { display: none; } ` const getLinkClass = isNormalVide ? '' : ' hide' toolbarShadow.appendChild(toolbarStyle) const toolbar = document.createElement('div') toolbar.id = 'replayer-toolbar' toolbarShadow.appendChild(toolbar) createButton('Start:', 'tool-item tool-text', toolbar) const pointA = createButton('Point A', 'tool-item tool-button', toolbar) const toA = createButton('To here', 'tool-item tool-button', toolbar) const linkA = createButton('Link', 'tool-item tool-button'+getLinkClass, toolbar) createButton('|', 'tool-item tool-text', toolbar) createButton('End:', 'tool-item tool-text', toolbar) const pointB = createButton('Point B', 'tool-item tool-button', toolbar) const toB = createButton('To here', 'tool-item tool-button', toolbar) const linkB = createButton('Link', 'tool-item tool-button'+getLinkClass, toolbar) createButton('|', 'tool-item tool-text', toolbar) const Start = createButton('Replay it', 'tool-item tool-button', toolbar) createButton('|', 'tool-item tool-text'+getLinkClass, toolbar) createButton('Now:', 'tool-item tool-text'+getLinkClass, toolbar) const linkNow = createButton('Link', 'tool-item tool-button'+getLinkClass, toolbar) const coffee = createButton('Coffee!', 'tool-item tool-text tool-right tool-coffee', toolbar) const video = document.body.querySelector('#bilibili-player video') const points = [0, video.duration-1] const pointButtons = [pointA, pointB] const setPoint = (i, val)=>{ if(val && val.trim()){ if(!/^(\d+h)?(\d+m)?\d+(\.\d+)?$/i.test(val)){ alert('格式有误,请注意查看示例格式') return } if(typeof(val)==='string'){ const hms = val.split(/h|m/g) const h = /\d+h/.test(val) ? +hms[0] : 0 const m = /\d+m/.test(val) ? +hms[hms.length-2] : 0 const s = +hms[hms.length-1] points[i] = h*3600+m*60+s }else{ points[i] = val } pointButtons[i].classList.add('active-button') window.localStorage.setItem('Point_'+i, points[i]) return } points[i] = i ? video.duration-1 : 0 pointButtons[i].classList.remove('active-button') window.localStorage.removeItem('Point_'+i) } if(window.localStorage.getItem('Point_0')){ pointA.classList.add('active-button') points[0] = window.localStorage.getItem('Point_0') } if(window.localStorage.getItem('Point_1')){ pointB.classList.add('active-button') points[0] = window.localStorage.getItem('Point_1') } pointA.addEventListener('click', ()=>{ const pointAInput = prompt('请输入一个时间(单位:秒),默认值是当前时间点。取消则清除此时间点,恢复为默认值:视频开头。\n输入值可以包含分钟,例如:三分十二秒写作 3m12\n输入值可以包含小时,例如:一小时三分十二秒写作 1h3m12', video.currentTime) setPoint(0, pointAInput) }) pointB.addEventListener('click', ()=>{ const pointBInput = prompt('请输入一个时间(单位:秒),默认值是当前时间点。取消则清除此时间点,恢复为默认值:视频结尾(前一秒)。\n输入值可以包含分钟,例如:三分十二秒写作 3m12\n输入值可以包含小时,例如:一小时三分十二秒写作 1h3m12', video.currentTime) setPoint(1, pointBInput) }) let mainTimer = 0 Start.addEventListener('click', ()=>{ if(mainTimer){ clearInterval(mainTimer) mainTimer = 0 Start.classList.remove('active-button') return } Start.classList.add('active-button') mainTimer = setInterval(()=>{ const A = points[0] <= points[1] ? points[0] : points[1] const B = points[0] > points[1] ? points[0] : points[1] if(video.currentTime>=B){ video.currentTime = A } },200); }) toA.addEventListener('click', ()=>{ video.currentTime = points[0] }) toB.addEventListener('click', ()=>{ video.currentTime = points[1] }) const getLink = t=>{ const link = window.location.href.replace(/^(https?:\/\/(www\.)bilibili\.com\/video\/(?:BV|AV)\w+).*/i, '$1')+'?t='+t copyText(link) alert('带时间标记的链接已复制到剪切板。') } linkA.addEventListener('click', ()=>{ getLink(points[0]) }) linkB.addEventListener('click', ()=>{ getLink(points[1]) }) linkNow.addEventListener('click', ()=>{ getLink(video.currentTime) }) coffee.addEventListener('click', ()=>{ window.open('https://afdian.net/@daomishu', '_blank') }) } window.addEventListener('load',()=>{ const waitTimer = setInterval(()=>{ if(document.body.querySelector('#bilibili-player video')){ clearInterval(waitTimer) createToolbar() } }, 1000) }) })();