您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
B站视频播放页、番剧播放页、直播间添加获取封面的按钮
当前为
// ==UserScript== // @id BilibiliCover@Laster2800 // @name B站封面获取 // @version 3.0.1 // @namespace laster2800 // @author Laster2800 // @description B站视频播放页、番剧播放页、直播间添加获取封面的按钮 // @include *://www.bilibili.com/video/* // @include *://www.bilibili.com/bangumi/play/* // @include *://live.bilibili.com/* // @exclude *://live.bilibili.com/ // @exclude *://live.bilibili.com/p/html/live-web-mng/* // @grant GM_addStyle // ==/UserScript== (function() { if (/\/video\//.test(location.href)) { executeAfterConditionPass({ condition: () => { var app = document.querySelector('#app') var vueLoad = app && app.__vue__ if (!vueLoad) { return false } return document.querySelector('#arc_toolbar_report') }, callback: addVideoBtn, }) } else if (/\/bangumi\/play\//.test(location.href)) { executeAfterConditionPass({ condition: () => { var app = document.querySelector('#app') var vueLoad = app && app.__vue__ if (!vueLoad) { return false } return document.querySelector('#toolbar_module') }, callback: addBangumiBtn, }) } else if (/live\.bilibili\.com\/\d/) { executeAfterConditionPass({ condition: () => { var hiVm = document.querySelector('#head-info-vm') var vueLoad = hiVm && hiVm.__vue__ if (!vueLoad) { return false } return hiVm.querySelector('.room-info-down-row') }, callback: addLiveBtn, }) } })() function addVideoBtn(atr) { var coverMeta = document.querySelector('head meta[itemprop=image]') var coverUrl = coverMeta && coverMeta.content var cover = document.createElement('a') var errorMsg = '获取失败,若非网络问题请提供反馈' cover.innerText = '获取封面' cover.onclick = () => coverUrl ? window.open(coverUrl) : alert(errorMsg) cover.title = coverUrl || errorMsg cover.className = 'appeal-text' atr.appendChild(cover) } function addBangumiBtn(tm) { GM_addStyle(` .cover_btn { float: right; cursor: pointer; font-size: 12px; margin-right: 16px; line-height: 36px; color: #505050; } .cover_btn:hover { color: #00a1d6; }`) var coverMeta = document.querySelector('head meta[property="og:image"]') var coverUrl = coverMeta && coverMeta.content var cover = document.createElement('a') var errorMsg = '获取失败,若非网络问题请提供反馈' cover.innerText = '获取封面' cover.onclick = () => coverUrl ? window.open(coverUrl) : alert(errorMsg) cover.title = coverUrl || errorMsg cover.className = 'cover_btn' tm.appendChild(cover) } function addLiveBtn(ridr) { GM_addStyle(` .cover_btn { cursor: pointer; margin-left: 18px; color: rgb(153, 153, 153); } .cover_btn:hover { color: #23ade5; }`) var nw = __NEPTUNE_IS_MY_WAIFU__ // 此window非彼window,不能通过window获取 var bir = nw && nw.baseInfoRes var data = bir && bir.data var coverUrl = data && data.user_cover var kfUrl = data && data.keyframe var cover = document.createElement('a') cover.innerText = '获取封面' cover.target = '_blank' if (coverUrl) { cover.href = coverUrl cover.title = coverUrl } else if (kfUrl) { cover.href = kfUrl cover.title = '直播间没有设置封面,或者因不明原因无法获取到封面,点击获取关键帧:\n' + kfUrl } else { var errorMsg = '获取失败,若非网络问题请提供反馈' cover.onclick = () => alert(errorMsg) cover.title = errorMsg } cover.className = 'cover_btn' ridr.appendChild(cover) } /** * 在条件满足后执行操作 * * 当条件满足后,如果不存在终止条件,那么直接执行 callback(result)。 * * 当条件满足后,如果存在终止条件,且 stopTimeout 大于 0,则还会在接下来的 stopTimeout 时间内判断是否满足终止条件,称为终止条件的二次判断。 * 如果在此期间,终止条件通过,则表示依然不满足条件,故执行 stopCallback() 而非 callback(result)。 * 如果在此期间,终止条件一直失败,则顺利通过检测,执行 callback(result)。 * * @param {Object} [options={}] 选项 * @param {Function} [options.condition] 条件,当 condition() 返回的 result 为真值时满足条件 * @param {Function} [options.callback] 当满足条件时执行 callback(result) * @param {number} [options.interval=100] 检测时间间隔(单位:ms) * @param {number} [options.timeout=5000] 检测超时时间,检测时间超过该值时终止检测(单位:ms) * @param {Function} [options.onTimeout] 检测超时时执行 onTimeout() * @param {Function} [options.stopCondition] 终止条件,当 stopCondition() 返回的 stopResult 为真值时终止检测 * @param {Function} [options.stopCallback] 终止条件达成时执行 stopCallback()(包括终止条件的二次判断达成) * @param {number} [options.stopInterval=50] 终止条件二次判断期间的检测时间间隔(单位:ms) * @param {number} [options.stopTimeout=0] 终止条件二次判断期间的检测超时时间(单位:ms) */ function executeAfterConditionPass(options) { var defaultOptions = { condition: () => true, callback: result => console.log(result), interval: 100, timeout: 5000, onTimeout: null, stopCondition: null, stopCallback: null, stopInterval: 50, stopTimeout: 0, } var o = { ...defaultOptions, ...options } if (!o.callback instanceof Function) { return } var cnt = 0 var maxCnt = o.timeout / o.interval var tid = setInterval(() => { var result = o.condition() var stopResult = o.stopCondition && o.stopCondition() if (stopResult) { clearInterval(tid) o.stopCallback instanceof Function && o.stopCallback() } else if (++cnt > maxCnt) { clearInterval(tid) o.onTimeout instanceof Function && o.onTimeout() } else if (result) { clearInterval(tid) if (o.stopCondition && o.stopTimeout > 0) { executeAfterConditionPass({ condition: o.stopCondition, callback: o.stopCallback, interval: o.stopInterval, timeout: o.stopTimeout, onTimeout: () => o.callback(result) }) } else { o.callback(result) } } }, o.interval) } /** * 在元素加载完成后执行操作 * * 当元素加载成功后,如果没有设置终止元素选择器,那么直接执行 callback(element)。 * * 当元素加载成功后,如果没有设置终止元素选择器,且 stopTimeout 大于 0,则还会在接下来的 stopTimeout 时间内判断终止元素是否加载成功,称为终止元素的二次加载。 * 如果在此期间,终止元素加载成功,则表示依然不满足条件,故执行 stopCallback() 而非 callback(element)。 * 如果在此期间,终止元素加载失败,则顺利通过检测,执行 callback(element)。 * * @param {Object} [options={}] 选项 * @param {Function} [options.selector] 该选择器指定要等待加载的元素 element * @param {Function} [options.callback] 当 element 加载成功时执行 callback(element) * @param {number} [options.interval=100] 检测时间间隔(单位:ms) * @param {number} [options.timeout=5000] 检测超时时间,检测时间超过该值时终止检测(单位:ms) * @param {Function} [options.onTimeout] 检测超时时执行 onTimeout() * @param {Function} [options.stopCondition] 该选择器指定终止元素 stopElement,若该元素加载成功则终止检测 * @param {Function} [options.stopCallback] 终止元素加载成功后执行 stopCallback()(包括终止元素的二次加载) * @param {number} [options.stopInterval=50] 终止元素二次加载期间的检测时间间隔(单位:ms) * @param {number} [options.stopTimeout=0] 终止元素二次加载期间的检测超时时间(单位:ms) */ function executeAfterElementLoad(options) { var defaultOptions = { selector: '', callback: el => console.log(el), interval: 100, timeout: 5000, onTimeout: null, stopSelector: null, stopCallback: null, stopInterval: 50, stopTimeout: 0, } var o = { ...defaultOptions, ...options } executeAfterConditionPass({ ...o, condition: () => document.querySelector(o.selector), stopCondition: o.stopSelector && (() => document.querySelector(o.stopSelector)), }) }