您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
推荐投币收藏一键三连
当前为
// ==UserScript== // @name bilibili三连 // @version 0.0.12 // @include https://www.bilibili.com/video/av* // @include https://www.bilibili.com/video/BV* // @description 推荐投币收藏一键三连 // @grant GM_getValue // @grant GM_setValue // @run-at document-idle // @namespace http://greasyfork.icu/users/164996 // ==/UserScript== const click = s => { if (!s) return if (s instanceof HTMLElement) s.click() else { const n = document.querySelector(s) if (!n) return n.click() } return true } const waitForAll = (selectors, delay = 500, timeout = 50000) => new Promise(resolve => { let max_times = 1 + timeout / delay let times = 0 let nodes const f = () => { nodes = selectors.map(i => document.querySelector(i)) times = times + 1 if (Object.values(nodes).every(v => v != null)) { resolve(nodes) } else if (times >= max_times) { resolve([]) } else { setTimeout(f, delay) } } f() }) ;(async () => { const [position, app, _] = await waitForAll([ '#arc_toolbar_report span.collect', 'div#app>div.v-wrap', 'span.bilibili-player-video-info-people-text' ]) if (!position) return // state let like = GM_getValue('like', true) let coin = GM_getValue('coin', 0) let collect = GM_getValue('collect', true) let collection = GM_getValue('collection', '输入收藏夹名') // style const css = ` span#sanlian > div { display: none; position: absolute; color: SlateGray; background: white; border: 1px solid #e5e9ef; box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.14); border-radius: 2px; padding: 1em; cursor: default; z-index: 2; } span#sanlian span[id^='sanlian_'] * { color: SlateGrey; cursor: pointer; } span#sanlian span[id^='sanlian_'].sanlian_on * { color: SlateBlue; } span#sanlian span[id^='sanlian_']:hover * { color: DarkSlateBlue; } span#sanlian > div > input { border: 0; border-bottom: 1px solid; } span#sanlian span#sanlian_coin i { margin: 0; } .video-toolbar .ops > span { width: 88px; } .bili-dialog-m { display: block; } ` const style = document.createElement('style') style.type = 'text/css' style.appendChild(document.createTextNode(css)) document.head.appendChild(style) // remove leading space of coin text const coin_text = document.querySelector('#arc_toolbar_report span.coin > i') .nextSibling if (coin_text.nodeType == Node.TEXT_NODE) { coin_text.textContent = coin_text.textContent.trim() } position.insertAdjacentHTML( 'afterend', `<span id=sanlian title=推荐硬币收藏><i class=van-icon-tuodong></i>三连 <div> <span id=sanlian_like class=${like ? 'sanlian_on' : ''}> <i class=van-icon-videodetails_like ></i> </span> <span id=sanlian_coin class=${coin > 0 ? 'sanlian_on' : ''}> <i class=van-icon-videodetails_throw></i><span>x${coin}</span> </span> <span id=sanlian_collect class=${collect ? 'sanlian_on' : ''}> <i class=van-icon-videodetails_collec></i> </span> <input type="text" value=${collection}> </div> </span>` ) const s = document.querySelector('#sanlian') const i = document.querySelector('#sanlian>i') const x = document.querySelector('#sanlian>div') const like_btn = document.querySelector('#sanlian_like') const coin_btn = document.querySelector('#sanlian_coin') const coin_value = document.querySelector('#sanlian_coin span') const collect_btn = document.querySelector('#sanlian_collect') const collect_value = document.querySelector('#sanlian input') let dialog_style = style.sheet.rules dialog_style = dialog_style[dialog_style.length - 1].style like_btn.addEventListener('click', function() { const c = this.classList like = !like c.toggle('sanlian_on') GM_setValue('like', like) }) coin_btn.addEventListener('click', function() { const c = this.classList coin = (coin + 1) % 3 coin_value.innerHTML = 'x' + coin if (coin !== 2) c.toggle('sanlian_on') GM_setValue('coin', coin) }) coin_btn.addEventListener('contextmenu', function(e) { e.preventDefault() const c = this.classList coin = (coin + 2) % 3 coin_value.innerHTML = 'x' + coin if (coin !== 1) c.toggle('sanlian_on') GM_setValue('coin', coin) }) collect_btn.addEventListener('click', function() { const c = this.classList collect = !collect c.toggle('sanlian_on') GM_setValue('collect', collect) }) collect_value.addEventListener('keyup', function() { collection = collect_value.value GM_setValue('collection', collection) }) s.addEventListener('mouseover', () => { x.style.display = 'block' }) s.addEventListener('mouseout', () => { x.style.display = 'none' }) s.addEventListener('click', async e => { let t // http request timeout const timeout = 3500 if (![s, i].includes(e.target)) return dialog_style.display = 'none' let dt = setTimeout(() => { dialog_style.display = 'block' }, timeout) // like if (like) click('#arc_toolbar_report span.like:not(.on)') // coin if (coin > 0 && click('#arc_toolbar_report span.coin:not(.on)')) { await new Promise(resolve => { new MutationObserver(function() { this.disconnect() if (coin === 1) click('.mc-box.left-con') else click('.mc-box.right-con') t = setTimeout(() => { click('div.bili-dialog-m div.coin-operated-m i.close') }, timeout) new MutationObserver(function() { this.disconnect() clearTimeout(t) resolve() }).observe(app, { childList: true }) setTimeout(() => { click('div.coin-bottom > span') }, 0) }).observe(app, { childList: true, subtree: true }) }) } // collect if (collect && click('#arc_toolbar_report span.collect')) { await new Promise(resolve => { new MutationObserver(function(e) { if (e[0].target.nodeName !== 'UL') return this.disconnect() t = document.querySelectorAll( 'div.collection-m div.group-list input+i' ) // match or first t = [...t].find( i => i.nextElementSibling.textContent.trim() === collection ) || t[0] // already collect if (!t || t.previousElementSibling.checked || !click(t)) { click('i.close') return resolve() } // offline fallback t = setTimeout(() => { click('i.close') }, timeout) // wait for dialog close new MutationObserver(function() { this.disconnect() clearTimeout(t) resolve() }).observe(app, { childList: true }) const b = document.querySelector( 'div.collection-m button.submit-move' ) if (b.hasAttribute('disabled')) { new MutationObserver(function() { this.disconnect() click(b) }).observe(b, { attributes: true }) } else click(b) }).observe(app, { childList: true, subtree: true }) }) } clearTimeout(dt) dialog_style.display = 'block' }) })()