您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
简单的B+增强脚本
// ==UserScript== // @name Biliplus Evolved // @version 1.0.7 // @description 简单的B+增强脚本 // @author DeltaFlyer // @copyright 2025, DeltaFlyer(https://github.com/DeltaFlyerW) // @license MIT // @match https://*.biliplus.com/* // @run-at document-end // @grant unsafeWindow // @grant GM_xmlhttpRequest // @grant GM_setValue // @grant GM_getValue // @connect api.bilibili.com // @connect comment.bilibili.com // @connect bangumi.bilibili.com // @connect www.bilibili.com // @connect delflare505.win // @icon https://www.biliplus.com/favicon.ico // @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.9.1/jszip.min.js // @namespace https://greasyfork.org/users/927887 // ==/UserScript== 'use strict'; let toastText = (function () { let html = ` <style> .df-bubble-container { position: fixed; bottom: 20px; right: 20px; z-index: 1000; display: block !important; } .df-bubble { background-color: #333; color: white; padding: 10px 20px; border-radius: 5px; margin-bottom: 10px; opacity: 0; transition: opacity 0.5s ease-in-out; max-width: 300px; box-shadow: 0px 3px 6px rgba(0, 0, 0, 0.2); display: block !important; } .df-show-bubble { opacity: 1; } </style> <div class="df-bubble-container" id="bubbleContainer"></div>` document.body.insertAdjacentHTML("beforeend", html) let bubbleContainer = document.querySelector('.df-bubble-container') function createToast(text) { console.log('toast', text) const bubble = document.createElement('div'); bubble.classList.add('df-bubble'); bubble.textContent = text; bubbleContainer.appendChild(bubble); setTimeout(() => { bubble.classList.add('df-show-bubble'); setTimeout(() => { bubble.classList.remove('df-show-bubble'); setTimeout(() => { bubbleContainer.removeChild(bubble); }, 500); // Remove the bubble after fade out }, 3000); // Show bubble for 3 seconds }, 100); // Delay before showing the bubble } return createToast })(); async function sleep(time) { await new Promise((resolve) => setTimeout(resolve, time)); } async function xhrGet(url) { function isCors(url) { if (url[0] === '/') return false // Extract the domain from the URL const urlDomain = new URL(url).hostname; // Extract the domain from the current page's URL const currentDomain = window.location.hostname; // Check if the domains are different (CORS request) return urlDomain !== currentDomain; } console.log('Get', url); if (isCors(url)) { // Use GM_xmlhttpRequest for cross-origin requests return new Promise((resolve) => { GM_xmlhttpRequest({ method: 'GET', url: url, withCredentials: true, onload: (response) => { if (response.status === 200) { resolve(response.responseText); } else { resolve(null); } }, onerror: (error) => { console.error('GM_xmlhttpRequest error:', error); resolve(null); }, }); }); } else { // Use XMLHttpRequest for same-origin requests return new Promise((resolve) => { const xhr = new XMLHttpRequest(); xhr.open('GET', url, true); xhr.withCredentials = true; xhr.send(); xhr.onreadystatechange = async () => { if (xhr.readyState === 4) { if (xhr.status === 200) { resolve(xhr.responseText); } else { resolve(null); } } }; }); } } function downloadFile(fileName, content, type = 'text/plain;charset=utf-8') { let aLink = document.createElement('a'); let blob = content if (typeof (content) == 'string') blob = new Blob([content], {'type': type}) aLink.download = fileName; let url = URL.createObjectURL(blob) aLink.href = url aLink.click() URL.revokeObjectURL(url) } let bv2av = (function bv2av() { //https://github.com/TGSAN/bv2av.js/tree/master let s = (` const table = 'FcwAPNKTMug3GV5Lj7EJnHpWsx4tb8haYeviqBz6rkCy12mUSDQX9RdoZf'; const max_avid = 1n << 51n; const base = 58n; const bvid_len = 12n; const xor = 23442827791579n; const mask = 2251799813685247n; let tr = []; for (let i = 0; i < base; i++) { tr[table[i]] = i; } /** * avid to bvid * @param {bigint} avid * @returns {string} bvid */ function enc(avid) { let r = ['B', 'V']; let idx = bvid_len - 1n; let tmp = (max_avid | avid) ^ xor; while (tmp !== 0n) { r[idx] = table[tmp % base]; tmp /= base; idx -= 1n; } [r[3], r[9]] = [r[9], r[3]]; [r[4], r[7]] = [r[7], r[4]]; return r.join(''); } /** * bvid to avid * @param {string} bvid * @returns {bigint} avid */ function dec(bvid) { let r = bvid.split(''); [r[3], r[9]] = [r[9], r[3]]; [r[4], r[7]] = [r[7], r[4]]; let tmp = 0n; for (let char of r.slice(3)) { console.log(char) let idx = BigInt(tr[char]); tmp = tmp * base + idx; } let avid = (tmp & mask) ^ xor; return avid; }`) eval(s) return dec })(); function domInserted(target, handle) { const observer = new MutationObserver(mutationList => { console.log("mutationList", mutationList) mutationList.filter(m => m.type === 'childList').forEach(m => { for (let e of m.addedNodes) { handle({target: e}) } }) } ); observer.observe(target, {childList: true, subtree: true}); } class CustomEventEmitter { constructor() { this.eventListeners = {}; } addEventListener(event, callback) { if (!this.eventListeners[event]) { this.eventListeners[event] = []; } this.eventListeners[event].push(callback); } removeEventListener(event, callback) { if (this.eventListeners[event]) { const index = this.eventListeners[event].indexOf(callback); if (index !== -1) { this.eventListeners[event].splice(index, 1); } } } postMessage(data) { const event = 'message' console.log(data) if (this.eventListeners[event]) { this.eventListeners[event].forEach(callback => { callback({data: data}); }); } } } class ObjectRegistry { constructor() { this.registeredObjects = new Set(); } register(obj) { if (this.registeredObjects.has(obj)) { throw new Error('Object is already registered.'); } this.registeredObjects.add(obj); } } function xmlunEscape(content) { return content.replace(/, /g, ';') .replace(/&/g, '&') .replace(/</g, '<') .replace(/>/g, '>') .replace(/'/g, "'") .replace(/"/g, '"') } function validateTitle(title) { if (!title) return '' return title.replace(/[\/\\\:\*\?\"\<\>\|]/g, '_') } function getPartTitle(partInfo) { let partTitle = /^\d+$/.test(partInfo.page) ? 'p' : '' partTitle += partInfo.page + ' ' + validateTitle(partInfo.part) + '_' + partInfo.cid return partTitle } async function aidQuery(event, init, jump) { function transform(src) { let dst = {} dst.id = src.id dst.ver = 1 dst.aid = src.id dst.lastupdatets = Math.floor(new Date().getTime() / 1000) dst.lastupdate = new Date().toLocaleString(); if (src.title === "已失效视频") { let pic = document.querySelector('.detail_cover') if (pic) { dst.title = document.title.slice(0, document.title.indexOf(" - ")) dst.pic = pic.src } } else { dst.pic = src.cover dst.title = src.title } dst.description = src.intro dst.tid = src.tid dst.typename = "tid_" + src.tid dst.created = src.pubtime dst.created_at = new Date(src.pubtime * 1000).toLocaleString() dst.author = src.upper.name dst.mid = src.upper.mid dst.play = src.cnt_info.play.toString() dst.coins = src.cnt_info.coin dst.review = src.cnt_info.reply dst.video_review = src.cnt_info.danmaku dst.favorites = src.cnt_info.collect dst.tag = "tag_undefined" let list = [] for (let page of src.pages) { list.push({ "page": page.page, "type": page.from, "cid": page.id, "vid": undefined, "part": page.title + "_时长" + page.duration + "秒", "duration": page.duration }) } dst.list = list return dst } let aid let href = new URL(window.location.href) if (href.searchParams.has("get_info") || init) { aid = unsafeWindow.av } else { aid = window.prompt("请输入要查询的aid或bvid", unsafeWindow.av) console.log('input', aid) if (!/^\d+$/.exec(aid)) { if (/^av\d+$/.exec(aid) || /^AV\d+$/.exec(aid)) { aid = aid.substring(2) } else if (/^BV/.exec(aid)) { aid = bv2av(aid) } else { alert("请输入正确的视频号,bv号请以BV开头") return } } jump = true } if (jump) { if (href.toString().indexOf(`/all/video/av${aid}/`) === -1) { href = new URL(href.origin + `/all/video/av${aid}/`) } href.searchParams.set("get_info", '1') window.location.href = href.toString() } let url = `https://delflare505.win/getVideoInfo?aid=` + aid let response = await xhrGet(url) let body = JSON.parse(await response) console.log(body) let videoInfo = transform(body.data) unsafeWindow.videoInfo = videoInfo if (unsafeWindow.cloudmoe) { let cacheInfo = JSON.parse(JSON.stringify(videoInfo)) cacheInfo.isDetailed = true cacheInfo.keywords = "" cacheInfo = { code: 0, data: { id: cacheInfo.id, info: cacheInfo, parts: cacheInfo.list, }, } unsafeWindow.cloudmoe(cacheInfo) } else { unsafeWindow.view(videoInfo) } } function client() { let registeredTimestamp = new Date().getTime() console.log('biliplus script running') function getPageAid() { let aid if (/\/BV/.exec(window.location.href)) { aid = bv2av(/BV[a-zA-Z0-9]+/.exec(window.location.href)[0]) } else { aid = /av(\d+)/.exec(window.location.href)[1] } return Number(aid) } let createElement = function (sHtml) { // 创建一个可复用的包装元素 let recycled = document.createElement('div'), // 创建标签简易匹配 reg = /^<([a-zA-Z]+)(?=\s|\/>|>)[\s\S]*>$/, // 某些元素HTML标签必须插入特定的父标签内,才能产生合法元素 // 另规避:ie7-某些元素innerHTML只读 // 创建这些需要包装的父标签hash hash = { 'colgroup': 'table', 'col': 'colgroup', 'thead': 'table', 'tfoot': 'table', 'tbody': 'table', 'tr': 'tbody', 'th': 'tr', 'td': 'tr', 'optgroup': 'select', 'option': 'optgroup', 'legend': 'fieldset' }; // 闭包重载方法(预定义变量避免重复创建,调用执行更快,成员私有化) createElement = function (sHtml) { // 若不包含标签,调用内置方法创建并返回元素 if (!reg.test(sHtml)) { return document.createElement(sHtml); } // hash中是否包含匹配的标签名 let tagName = hash[RegExp.$1.toLowerCase()]; // 若无,向包装元素innerHTML,创建/截取并返回元素 if (!tagName) { recycled.innerHTML = sHtml; return recycled.removeChild(recycled.firstChild); } // 若匹配hash标签,迭代包装父标签,并保存迭代层次 let deep = 0, element = recycled; do { sHtml = '<' + tagName + '>' + sHtml + '</' + tagName + '>'; deep++; } while (tagName = hash[tagName]); element.innerHTML = sHtml; // 根据迭代层次截取被包装的子元素 do { element = element.removeChild(element.firstChild); } while (--deep > -1); // 最终返回需要创建的元素 return element; } // 执行方法并返回结果 return createElement(sHtml); } async function parseVideoInfo(aid) { let videoInfo if (unsafeWindow.videoInfo && window.location.href.includes('/video/')) { return unsafeWindow.videoInfo } if (aid === undefined) { aid = getPageAid() } try { let videoPage = await xhrGet('https://www.biliplus.com/video/av' + aid + '/') videoInfo = JSON.parse(xmlunEscape(/({"id":.*?})\);/.exec(videoPage)[1])) videoInfo['aid'] = videoInfo['id'] if (!videoInfo.list || videoInfo.list.length === 0) { throw ["No part found in videoInfo Normal, try cidHistory", JSON.stringify(videoInfo, undefined, "\t")] } } catch (e) { console.log(e) let videoPage = await xhrGet('https://www.biliplus.com/all/video/av' + aid + '/') let url = /(\/api\/view_all\?.*?)'/.exec(videoPage)[1] url = 'https://www.biliplus.com' + url let data = JSON.parse(xmlunEscape(await xhrGet(url)))['data'] videoInfo = data['info'] videoInfo['list'] = data['parts'] } if (videoInfo.created) { videoInfo.videoPublishDate = videoInfo.created } console.log(videoInfo) if (window.location.href.includes('/video/')) { unsafeWindow.videoInfo = videoInfo } return videoInfo } async function parseEpisodesInfo(aid) { if (aid === undefined) { aid = getPageAid() } let response = await xhrGet('https://api.bilibili.com/x/web-interface/wbi/view?aid=' + aid) let videoInfo = JSON.parse(response).data let i = 0 videoInfo.list = [] let episodes try { for (const season of videoInfo.ugc_season.sections) { for (const episode of season.episodes) { if (episode.aid === aid) { episodes = season.episodes } } } console.log(episodes.length) } catch (e) { alert("未找到合集信息") return } for (let episode of episodes) { i += 1 let partInfo = { page: i, title: episode.arc.title, part: episode.arc.title + "_时长" + episode.arc.duration + "秒", duration: episode.arc.duration, cid: episode.cid } videoInfo.list.push(partInfo) } console.log(videoInfo) return videoInfo } (function searchFix() { if (window.location.href.indexOf('api/do.php') === -1) return function searchOption() { let searchField = document.querySelector("#searchField > fieldset") let searchDiv = document.querySelector("#searchField > fieldset > div:nth-child(1)") searchDiv.insertAdjacentHTML('afterend', ` <style> .dropdown { background-color: #f2f2f2; padding: 4px; border-radius: 2px; } .dropdown option { padding: 10px; font-size: 14px; color: #333; } .dropdown option:hover { background-color: #e5e5e5; } </style> <select id="alive-section" class="dropdown"> <option value="连载动画">连载动画</option> <option value="完结动画">完结动画</option> <option value="国产动画">国产动画</option> <option value="欧美电影">欧美电影</option> <option value="日本电影">日本电影</option> <option value="国产电影">国产电影</option> <option value="布袋戏">布袋戏</option> <option value="国产剧">国产剧</option> <option value="海外剧">海外剧</option> <option value="" selected="">视频分区</option> </select> <select id="dead-section" class="dropdown"> <option value="连载剧集">连载剧集</option> <option value="完结剧集">完结剧集</option> <option value="国产">国产</option> <option value="日剧">日剧</option> <option value="美剧">美剧</option> <option value="其他">其他</option> <option value="特摄">特摄</option> <option value="剧场版">剧场版</option> <option value="" selected="">下架分区</option> </select> <select id="poster" class="dropdown"> <option value="928123">哔哩哔哩番剧</option> <option value="11783021">哔哩哔哩番剧出差</option> <option value="15773384">哔哩哔哩电影</option> <option value="4856007">迷影社</option> <option value="" selected="">UP主</option> </select> `) let aliveSection = searchField.querySelector("select[id='alive-section']") let deadSection = searchField.querySelector("select[id='dead-section']") let poster = searchField.querySelector("select[id='poster']") let searchInput = document.querySelector("#searchField > fieldset > div:nth-child(1) > input[type=search]") function setSection(section) { let content = searchInput.value if (section !== "") { section = ' @' + section } if (/ @\S+/.exec(content)) { content = content.replace(/ @\S+/, section) } else content += section searchInput.value = content } function setPoster(uid) { if (uid !== "") { uid = ' @m=' + uid } let content = searchInput.value if (/ @m=\d+/.exec(content)) { content = content.replace(/ @m=\d+/, uid) } else content += uid searchInput.value = content } aliveSection.addEventListener('change', (event) => { if (deadSection.value !== "") { deadSection.value = "" } setSection(event.target.value) }) deadSection.addEventListener('change', (event) => { if (aliveSection.value !== "") { aliveSection.value = "" } setSection(event.target.value) }) poster.addEventListener('change', (event) => { setPoster(event.target.value) }) } let getjson = unsafeWindow.parent.getjsonReal = unsafeWindow.parent.getjson let aidList = [] let irrelevantArchive = [] let allArchive = [] async function joinCallback(url, callback, n) { console.log("joinCallback") if (url[0] === '/') url = 'https:' + url let word = /word=(.*?)(&|$)/.exec(url)[1] let wordList = [] for (let keyword of decodeURIComponent(word).replace(/([^ ])@/, '$1 @').split(' ')) { if (keyword[0] !== '@') { wordList.push(keyword) } } let pn = /p=(\d+)/.exec(url) pn = pn ? pn[1] : '1' if (pn === '1') { aidList = [] irrelevantArchive = [] allArchive = [] } let request = xhrGet(url) let aidSearchUrl = '/api/search?word=' + word + '&page=' + pn let aid_request = xhrGet(aidSearchUrl) let searchResult = JSON.parse(await request) let archive = [] searchResult['data']['items']['archive'].forEach(function (item) { if (item.goto === 'av') { if (aidList.indexOf(item.param) === -1) { aidList.push(item.param) let isRelevant = false for (let keyword of wordList) { for (let key of ['title', 'desc']) { if (item[key].indexOf(keyword) !== -1) { isRelevant = true } } } if (isRelevant) { archive.push(item) } else { irrelevantArchive.push(item) } allArchive.push(item) } } else { archive.push(item) } }) try { let aidSearchResult = JSON.parse((await aid_request))['result'] aidSearchResult.forEach(function (video) { if (aidList.indexOf(video.aid) === -1) { let item = { author: video.author, cover: video.pic, created: new Date(video.created.replace(/-/g, '/')).getTime() / 1000, review: video.review, desc: video.description, goto: "av", param: video.aid, play: video.play, title: video.title, } let isRelevant = false for (let keyword of wordList) { for (let key of ['title', 'desc']) { if (item[key].toLowerCase().indexOf(keyword.toLowerCase()) !== -1) { isRelevant = true } } } if (isRelevant) { archive.push(item) } else { irrelevantArchive.push(item) } allArchive.push(item) } }) } catch (e) { console.log(e) } if (archive.length === 0) { archive = irrelevantArchive irrelevantArchive = [] } searchResult['data']['items']['archive'] = archive callback(searchResult, n) return } unsafeWindow.getjson = unsafeWindow.parent.getjson = function (url, callback, n) { console.log("getjson", arguments) if (url.indexOf("search_api") !== -1 && url.indexOf("source=biliplus") !== -1) { try { return joinCallback(url, callback, n) } catch (e) { console.log(e) return getjson(url, callback, n) } } else return getjson(url, callback, n) }; broadcastChannel.addEventListener('message', function (event) { console.log(event.data) if (event.data.type === 'aidComplete') { let elem = document.querySelector('[id="av' + event.data.aid + '"]') if (elem) elem.textContent = '下载完成' } if (event.data.type === 'aidDownloaded') { let elem = document.querySelector('[id="av' + event.data.aid + '"]') if (elem) elem.textContent = '已下载' } if (event.data.type === 'aidStart') { let elem = document.querySelector('[id="av' + event.data.aid + '"]') if (elem) elem.textContent = '开始下载' } if (event.data.type === 'cidComplete') { let elem = document.querySelector('[id="av' + event.data.aid + '"]') if (elem) elem.textContent = event.data.progress + "%" } }) function updatePointer(pointerElem) { if (pointerElem.querySelector("#videoDetail")) { return } let aidElem = pointerElem .querySelector('div[class="video-card-desc"]') if (!aidElem) { return } let link = pointerElem.getAttribute("data-link") if (link) { console.log(link) pointerElem.removeAttribute("data-link") pointerElem.addEventListener('click', async function (event) { console.log(event) event.preventDefault() if (event.target.className !== 'download') { unsafeWindow.openOrigin(link) } }) } let aid = parseInt(aidElem.textContent.slice(2)) if (!aidElem.querySelector('[class="download"]')) { let downloadButton = createElement('<div class="download" style="display:inline-block;float:right;border-radius:5px;border:1px solid #AAA;' + 'background:#DDD;padding:8px 20px;cursor:pointer" id="av' + aid + '">下载弹幕</div>') downloadButton.addEventListener('click', async function (event) { event.preventDefault() console.log('download', aid) if (downloadButton.textContent !== '下载弹幕') return downloadButton.textContent = '等待下载' let videoInfo = await parseVideoInfo(aid) broadcastChannel.postMessage({ type: 'biliplusDownloadDanmakuVideo', videoInfo: videoInfo, timestamp: registeredTimestamp }) }) aidElem.appendChild(downloadButton) } if (!pointerElem.querySelector("#videoDetail")) { let desc = pointerElem.querySelectorAll(".video-card-desc") let timeago = pointerElem.querySelector(".timeago") desc = desc[desc.length - 1] for (let video of allArchive) { if (video.param === aid && video.goto === 'av') { let text = '播放:' + video['play'] if (video['danmaku']) text += ' 弹幕:' + video['danmaku'] if (video['review']) text += ' 评论:' + video['review'] desc.insertAdjacentHTML('beforebegin', `<div id="videoDetail" class="video-card-desc">${text}</div> <div id="videoDetail" class="video-card-desc">${timeago.title}</div>`) } } } } let jqCreateElem = unsafeWindow._ unsafeWindow._ = function (e, t, i) { let result = jqCreateElem(e, t, i) if (t['className'] === 'pointer') { updatePointer(result) } return result } searchOption() unsafeWindow.openOrigin = unsafeWindow.open unsafeWindow.open = function (link) { console.log('window.open', link) if (link) { unsafeWindow.openOrigin(link) } } })(); (function historyFix() { if (window.location.href.indexOf('/video/') === -1) return const registry = new ObjectRegistry(); let aid = getPageAid() unsafeWindow.downloadVideoDanmaku = async function (aid, videoInfo) { registry.register('downloadVideoDanmaku') if (!videoInfo) { videoInfo = await parseVideoInfo(aid) } console.log('downloadVideoDanmaku', videoInfo) broadcastChannel.postMessage({ type: 'biliplusDownloadDanmakuVideo', videoInfo: videoInfo, timestamp: registeredTimestamp }) } function getEpisodePublishDate(episode) { let dateTimeString = episode.pub_real_time || episode.ctime if (!dateTimeString) { return null } const unixTimestamp = Date.parse(dateTimeString); return Math.floor(unixTimestamp / 1000); // Convert milliseconds to seconds } async function popupPageSelector(is_series) { let videoInfo if (!is_series) { videoInfo = await parseVideoInfo(aid) } else { videoInfo = await parseEpisodesInfo(aid) } console.log(videoInfo) function parseDuration(text) { if (!text) { return null } if (/^\d+$/.test(text)) { return parseFloat(text) } let match = /^(\d+)[::](\d+)$/.exec(text) if (!match) { alert(`请输入正确的时间: ${text}`) throw text } return parseFloat(match[1]) * 60 + parseInt(match[2]) } async function builder() { let partHtml = '' for (let partInfo of videoInfo.list) { let partTitle = getPartTitle(partInfo) let idPrefix = `part_cid_${partInfo.cid}` partHtml += `<tr draggable="true"><td> <input type="checkbox" checked id="${idPrefix}"></td><td>${partTitle}</td> <td><div class="part-option front-black" style="display: none"> <label for="${idPrefix}_front">前黑</label> <input type="text" class="p-number-input" name="${idPrefix}_front" id="${idPrefix}_front" /> </div> <div class="part-option back-black" style="display: none"> <label for="${idPrefix}_back">后黑</label> <input type="text" class="p-number-input" name="${idPrefix}_back" id="${idPrefix}_back" /> </div> <div class="part-option part-offset" style="display: none"> <label for="${idPrefix}_offset">偏移</label> <input type="text" class="p-number-input" name="${idPrefix}_offset" id="${idPrefix}_offset" value=""/> </div> </td> </tr> ` } let html = ` <div id="popup" class="popup" style="display: none"> <style> .popup { position: fixed; z-index: 1; left: 0; top: 0; width: 100%; height: 100%; overflow: auto; background-color: rgb(0,0,0); background-color: rgba(0,0,0,0.4); } .p-number-input { margin-left: 1px; width: 25px; border: 1px solid #aaa; /* Black solid border with 2px width */ border-radius: 4px; /* Optional: add rounded corners */ text-align: center; /* Center align the text */ } .p-number-input::-webkit-outer-spin-button, .p-number-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; } #draggableTable { display: block; max-height: 80vh; overflow-y: auto; border-collapse: collapse; width: 100%; } .popup-content { background-color: #fefefe; display: block; max-height: 90vh; overflow-y: auto; margin: 5vh auto; padding: 20px; border: 1px solid #888; width: min(80%, 400px); } .close-btn { color: #aaa; float: right; font-size: 28px; font-weight: bold; } .close-btn:hover, .close-btn:focus { color: black; text-decoration: none; cursor: pointer; } input[type="checkbox"] { transform: scale(1.5); margin-right: 10px; cursor: pointer; } #draggableTable td { padding: 10px; } </style> <div class="popup-content"> <span class="close-btn">×</span> <div class="button-row"> <button type="button" id="p-download">下载0项</button> <button type="button" id="p-reverse">反选</button> <button type="button" id="p-section">区间选择</button> <button type="button" id="p-concat">拼接</button> <button type="button" id="p-front-black" title="无视视频前段的时长及弹幕">前黑</button> <button type="button" id="p-back-black" title="无视视频后段的时长及弹幕">后黑</button> <button type="button" id="p-offset" title="指定分P的偏移时间">偏移</button> </div> <table id="draggableTable"> <tbody> ${partHtml} </tbody> </table> </div> </div> ` document.body.insertAdjacentHTML("beforeend", html) let popup = document.getElementById('popup') function js() { document.querySelector(".close-btn").onclick = function () { document.getElementById("popup").style.display = "none"; } let dragSrcEl = null; function handleDragStart(e) { dragSrcEl = this; e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/html', this.innerHTML); } function handleDragOver(e) { if (e.preventDefault) { e.preventDefault(); // Necessary. Allows us to drop. } e.dataTransfer.dropEffect = 'move'; return false; } function handleDrop(e) { if (e.stopPropagation) { e.stopPropagation(); // stops the browser from redirecting. } if (dragSrcEl !== this) { dragSrcEl.innerHTML = this.innerHTML; registerItem(dragSrcEl) this.innerHTML = e.dataTransfer.getData('text/html'); registerItem(this) } return false; } function handleDragEnd(e) { this.style.opacity = '1'; } function updateSelectedCount() { const selectedCount = document.querySelectorAll( '#draggableTable input[type="checkbox"]:checked').length; document.getElementById('p-download').innerText = `下载${selectedCount}项` } updateSelectedCount() let lastClick = null let currentClick = null function registerItem(item) { let checkbox = item.querySelector('input[type="checkbox"]'); item.addEventListener('dragstart', handleDragStart, false); item.addEventListener('dragover', handleDragOver, false); item.addEventListener('drop', handleDrop, false); item.addEventListener('dragend', handleDragEnd, false); item.addEventListener('click', function (e) { if (e.target.type !== 'checkbox') { checkbox.checked = !checkbox.checked } if (checkbox.checked) { lastClick = currentClick currentClick = item if (lastClick !== null) { document.getElementById('p-section').removeAttribute('disabled') } } updateSelectedCount() }) } document.querySelectorAll('#draggableTable tr').forEach(registerItem) document.getElementById('p-reverse').addEventListener('click', () => { document.querySelectorAll('#draggableTable input[type="checkbox"]').forEach(checkbox => { checkbox.checked = !checkbox.checked; }); updateSelectedCount() }) document.getElementById('p-section').addEventListener('click', () => { let parent = lastClick.parentElement let sectionStart = false for (let child of parent.childNodes) { if (child === lastClick || child === currentClick) { if (!sectionStart) { sectionStart = true } else { break } } if (sectionStart && child.querySelector) { child.querySelector("input").checked = true } } updateSelectedCount() }) document.getElementById('p-reverse').addEventListener('click', () => { }) document.getElementById('p-front-black').addEventListener('click', () => { checkDuration() let time = parseDuration(window.prompt("请输入前黑的秒数,拼接时将无视该时段的时长及弹幕")) if (isNaN(time)) { alert("请输入数字") } else { popup.querySelectorAll(".front-black").forEach(elem => { elem.style.display = 'flex' }) popup.querySelectorAll(`input[id$="_front"]`).forEach((elem) => { elem.value = time }) toastText(`应用前黑: ${time}秒`) pageSetting.frontBlack = time } }) document.getElementById('p-back-black').addEventListener('click', () => { checkDuration() let time = parseDuration(window.prompt("请输入后黑的秒数,拼接时将无视该时段的时长及弹幕")) if (isNaN(time)) { alert("请输入数字") } else { popup.querySelectorAll(".back-black").forEach(elem => { elem.style.display = 'flex' }) popup.querySelectorAll(`input[id$="_back"]`).forEach((elem) => { elem.value = time }) toastText(`应用后黑: ${time}秒`) pageSetting.backBlack = time } }) document.getElementById('p-offset').addEventListener('click', () => { popup.querySelectorAll(".part-offset").forEach(elem => { elem.style.display = 'flex' } ) }) document.getElementById('popup').addEventListener('click', function (event) { if (!event.target.closest('.popup-content')) { document.getElementById('popup').style.display = 'none'; } }); } js() return popup } function checkDuration() { if (!videoInfo.list[0].duration) { window.alert("该功能需要启用aid查询") aidQuery(null, true, true) throw "eee" } } let popup = document.getElementById('popup') if (!popup) { popup = await builder(aid) } if (popup.style.display === 'none') { popup.style.display = 'block' } popup.querySelector('#p-download').addEventListener('click', async (e) => { videoInfo = JSON.parse(JSON.stringify(videoInfo)) let cidMap = {} videoInfo.list.forEach((partInfo) => { cidMap[partInfo.cid] = partInfo }) videoInfo.list = [] for (let checkBox of document.querySelectorAll( '#draggableTable input[type="checkbox"]:checked')) { videoInfo.list.push(cidMap[Number(checkBox.id.substring(9))]) } videoInfo.isCache = true await unsafeWindow.downloadVideoDanmaku(aid, videoInfo) }) popup.querySelector('#p-concat').addEventListener('click', async (e) => { checkDuration() videoInfo = JSON.parse(JSON.stringify(videoInfo)) let cidMap = {} videoInfo.list.forEach((partInfo) => { cidMap[partInfo.cid] = partInfo }) videoInfo.list = [] for (let checkBox of document.querySelectorAll( '#draggableTable input[type="checkbox"]:checked')) { let partInfo = cidMap[Number(checkBox.id.substring(9))] let tr = checkBox.parentNode.parentNode let frontBlack = parseDuration(tr.querySelector("input[id$='_front']").value) let backBlack = parseDuration(tr.querySelector("input[id$='_back']").value) let partOffset = parseDuration(tr.querySelector("input[id$='_offset']").value) if (frontBlack) { partInfo.frontBlack = frontBlack } if (backBlack) { partInfo.backBlack = backBlack } if (partOffset) { partInfo.partOffset = partOffset } videoInfo.list.push(partInfo) } console.log("concat", videoInfo) if (pageSetting.frontBlack) { toastText(`应用前黑: ${pageSetting.frontBlack}秒`) } if (pageSetting.backBlack) { toastText(`应用后黑: ${pageSetting.backBlack}秒`) } broadcastChannel.postMessage({ type: 'concatDanmaku', videoInfo: videoInfo, timestamp: registeredTimestamp }) }) } unsafeWindow.popupPageSelector = popupPageSelector domInserted(document, async (msg) => { if (msg.target.id) { switch (msg.target.id) { case 'danmaku_container': { let historyButton = msg.target.querySelector('a[href^="/open/moepus.powered"]') let cid = /#(\d+)/.exec(historyButton.getAttribute('href'))[1] historyButton.onclick = async function () { console.log('biliplusDownloadDanmaku', { 'cid': cid, 'title': document.querySelector('[class="videotitle"]').textContent + ' ' + document.querySelector('[id="cid_' + cid + '"]').innerText, 'timestamp': registeredTimestamp }) broadcastChannel.postMessage({ type: 'biliplusDownloadDanmaku', cid: cid, title: document.querySelector('[class="videotitle"]').textContent + ' ' + document.querySelector('[id="cid_' + cid + '"]').innerText, timestamp: registeredTimestamp }) } historyButton.removeAttribute('href') let commentButton = msg.target.querySelector('a[href^="https://comment.bilibili.com"]') commentButton.removeAttribute('href') commentButton.removeAttribute('onClick') commentButton.addEventListener('click', async function () { registry.register('commentButton' + cid) let commentText = await xhrGet("/danmaku/" + cid + ".xml") if (!commentText || commentText.indexOf("<state>2</state>") !== -1) { broadcastChannel.postMessage({ type: 'biliplusDownloadDanmaku', ndanmu: /<maxlimit>(\d+)<\/maxlimit>/.exec(commentText)?.[1], cid: cid, title: document.querySelector('[class="videotitle"]').textContent + ' ' + document.querySelector('[id="cid_' + cid + '"]').innerText, history: false, timestamp: registeredTimestamp }) } else { downloadFile(cid + '.xml', commentText) } }) break } case 'favorite': { console.log(msg.target, msg.target.parentElement.firstChild) msg.target.parentElement.insertBefore( createElement('<span id="downloadDanmaku"><a href="javascript:" ' + 'onclick="window.downloadVideoDanmaku()">' + '<div class="solidbox pink" id="av' + getPageAid() + '">下载视频弹幕</div></a></span>'), document.querySelector('#attention')) break } case 'bgBlur': { let bangumi = unsafeWindow.bangumi if (!bangumi.isBangumi) break let bangumiList = document.querySelector("#bangumi_list") bangumiList.insertAdjacentHTML('beforeend', '' + '<div class="solidbox pointer">下载剧集弹幕</div>') bangumiList.lastChild.addEventListener('click', function (event) { registry.register('downloadBangumiDanmaku') console.log(event.target) if (event.target.textContent !== '下载中') { event.target.textContent = "下载中" } else { return } let apiCache = bangumi.apiCache let response = apiCache[Object.keys(apiCache)[0]].result let videoInfo = { aid: 'ss' + response.season_id, title: response.title, list: [], detail: response, } for (let episode of response.episodes) { if (episode.episode_type === -1 || episode.section_type === 1 || episode.section_type === 2) continue let partInfo = { page: episode.index ? episode.index : episode.title, part: episode.index_title ? episode.index_title : episode.long_title, cid: episode.cid, duration: episode.duration, videoPublishDate: getEpisodePublishDate(episode) } videoInfo.list.push(partInfo) } unsafeWindow.downloadVideoDanmaku(null, videoInfo) }) } } } }) if (unsafeWindow.cloudmoe) { unsafeWindow._cloudmoe = unsafeWindow.cloudmoe unsafeWindow.cloudmoe = function (json) { if (json.code !== 0 || !json.data) { toastText("使用AID查询信息") return aidQuery(undefined, true) } else { let result = unsafeWindow._cloudmoe(json) let container = document.getElementById("container") container.querySelector("#part1").insertAdjacentHTML("beforebegin", '<div class="solidbox pointer" onclick="window.downloadVideoDanmaku()">下载视频弹幕</div>' + '<div class="solidbox pointer" onclick="window.popupPageSelector(false)">分P下载或拼接</div>' + '<div class="solidbox pointer" onclick="window.popupPageSelector(true)">合集下载或拼接</div>' ) return result } } } })(); (async function mediaLinkFix() { function blockRefresh() { // Select the meta refresh tag let metaRefresh = document.querySelector('meta[http-equiv="refresh"]'); // If the meta tag exists, remove it if (metaRefresh) { metaRefresh.parentNode.removeChild(metaRefresh); console.log('Auto-refresh blocked.'); } } let url = window.location.href if (url.includes('media/md')) { let html = await xhrGet(url.replace("biliplus", "bilibili")) console.log(html) let seasonId = /"season_id":(\d+)/.exec(html) if (seasonId) { window.location.href = 'https://www.biliplus.com/bangumi/play/ss' + seasonId[1] } } else if (url.includes('anime/v/') && document.body.childNodes[0].textContent.includes('未能识别此地址')) { document.addEventListener("DOMContentLoaded", blockRefresh); blockRefresh() let episodeId = /\/anime\/v\/(\d+)/.exec(url)[1] let html = await xhrGet('https://www.bilibili.com/bangumi/play/ep' + episodeId) console.log(html) let seasonId = /"season_id":(\d+)/.exec(html) console.log(seasonId) if (seasonId) { window.location.href = 'https://www.biliplus.com/bangumi/play/ss' + seasonId[1] } } })(); } function panel() { function getLocalSetting(key) { let value = GM_getValue(key) console.log('get', key, value) try { return JSON.parse(value) } catch (e) { return {} } } function setDefaultValue(currentSetting, settingPanelOptions) { for (let option of settingPanelOptions) { if (option.id) { if (!currentSetting[option.id]) { currentSetting[option.id] = option.default } } else if (option.children) { for (let child of option.children) { if (child.id) { if (!currentSetting[child.id]) { currentSetting[child.id] = child.default } } } } } } function saveLocalSetting(key, value) { console.log('save', key, value) GM_setValue(key, JSON.stringify(value)) } let settingPanelOptions = [{type: 'section', 'label': '抓取时段:'}, { type: 'row', children: [{ 'type': 'numberInput', 'id': 'capturePeriodStart', label: "从视频发布起第", default: 0, suffixLabel: '天开始, ', splitter: ' ' }, { 'type': 'numberInput', 'id': 'capturePeriodEnd', label: "至第", default: -1, suffixLabel: '天结束', splitter: ' ' },] }, {type: 'sectionEnd'}, { type: 'row', children: [{type: 'checkbox', id: 'splitFileByTime', label: '按时间段分割弹幕文件'}] },] let currentSetting = getLocalSetting("danmakuSetting") setDefaultValue(currentSetting, settingPanelOptions) let showSettingPanel = (function (settingPanelOptions, changeHandle) { let panelStyles = ` <style> .section { font-size: 16px; margin-bottom: 10px; } .sectionEnd { border-top: 1px solid #ccc; margin-top: 10px; } #panel { position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background-color: #333; color: #fff; padding: 20px; border-radius: 5px; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5); z-index: 999999; } .slider-label{ width: 12ch; } .apply-button { display: flex; justify-content: center; align-items: center; background-color: #555; color: white; border: none; padding: 8px 12px; cursor: pointer; border-radius: 4px; text-decoration: none; margin-top: 10px; margin-left: auto; } .row { display: flex; align-items: center; margin-bottom: 10px; } .slider { flex: 1; } .slider-value { margin-left: 10px; font-size: 14px; color: #bbb; width: 4ch; } .selector { flex: 1; margin-right: 10px; padding: 5px; border: 1px solid #555; border-radius: 3px; background-color: #444; color: #fff; } .number-input { width: 6ch; padding: 5px; border: 1px solid #555; border-radius: 3px; background-color: #444; color: #fff; margin-right: 10px; margin-left: 5px; } .text-selector { width: 10ch; padding: 5px; border: 1px solid #555; border-radius: 3px; background-color: #444; color: #fff; margin-left: 5px; } .equal-row { display: flex; justify-content: space-between; align-items: center; } .checkbox-group { flex: 1; display: flex; justify-content: center; } </style> ` // Create the setting panel HTML string based on the provided options function createPanelHTML(options) { let html = '<div id="panel" style="display: none;">' options.forEach(option => { if (option.type === 'section') { html += `<div class="section">${option.label}</div>`; } else if (option.type === 'sectionEnd') { html += `<div class="sectionEnd"></div>`; } else if (option.type === 'slider') { html += `<div class="row"> <label class="slider-label" for="${option.id}">${option.label}:</label> <input type="range" class="slider" id="${option.id}" min="${option.range[0]}" max="${option.range[1]}" step="0.01" value="${currentSetting[option.id] || option.default}"> <span class="slider-value" id="${option.id}Value">${currentSetting[option.id] || option.default}</span> </div>`; } else if (option.type === 'equal-row' || option.type === 'row') { html += `<div class="${option.type}">`; option.children.forEach(child => { // Handle checkboxes if (child.type === 'checkbox') { const checked = currentSetting[child.id] !== undefined ? currentSetting[child.id] : child.default; html += `<div class="checkbox-group"> <label for="${child.id}">${child.label}:</label> <input type="checkbox" id="${child.id}" ${checked ? 'checked' : ''}> </div>`; } // Handle number input else if (child.type === 'numberInput') { html += `<label for="${child.id}">${child.label}${child.splitter || ':'}</label> <input type="number" class="number-input" id="${child.id}" value="${currentSetting[child.id] || child.default}"> `; if (child.suffixLabel) { html += `<span>${child.suffixLabel}</span>` } } // Handle text selector else if (child.type === 'textSelector') { let currentValue = currentSetting[child.id] || child.default html += `<label for="${child.id}">${child.label}:</label> <select class="selector text-selector" id="${child.id}">`; child.optionText.forEach((text, index) => { const value = child.optionValue[index]; const selected = currentValue === value ? 'selected' : ''; html += `<option value="${value}" ${selected}>${text}</option>`; }); html += `</select>`; } }); html += `</div>`; } }); html += '<button class="apply-button" id="applyButton">应用</button></div>'; return html; } function handle_change(changeHandle, id) { if (changeHandle && changeHandle[id]) { return changeHandle[id]; } return (value, id,) => { currentSetting[id] = value; } } function createSettingPanel(settingPanelOptions, changeHandle) { document.body.insertAdjacentHTML('beforeend', panelStyles); const panelHTML = createPanelHTML(settingPanelOptions); document.body.insertAdjacentHTML('beforeend', panelHTML); const panel = document.getElementById('panel'); panel.querySelector('#applyButton').addEventListener('click', () => { panel.style.display = 'none'; saveLocalSetting('danmakuSetting', currentSetting) }); const sliders = panel.querySelectorAll('.slider'); const sliderValues = panel.querySelectorAll('.slider-value'); sliders.forEach((elem, index) => { elem.addEventListener('input', () => { sliderValues[index].textContent = elem.value; handle_change(changeHandle, elem.id)(parseFloat(elem.value), elem.id); }); }); // Handle checkbox changes const checkboxes = panel.querySelectorAll('input[type="checkbox"]'); checkboxes.forEach(elem => { elem.addEventListener('change', () => { handle_change(changeHandle, elem.id)(Number(elem.checked), elem.id); }); }); // Handle number input changes const numberInputs = panel.querySelectorAll('.number-input'); numberInputs.forEach(elem => { elem.addEventListener('input', () => { const value = parseFloat(elem.value); if (!isNaN(value)) { handle_change(changeHandle, elem.id)(value, elem.id); } }); }); // Handle text selector changes const textSelectors = panel.querySelectorAll('.text-selector'); textSelectors.forEach(elem => { elem.addEventListener('change', () => { handle_change(changeHandle, elem.id)(elem.value, elem.id); }); }); return panel } let panel = createSettingPanel(settingPanelOptions, changeHandle) panel.style.display = 'none' return function () { if (panel.style.display !== 'block') { panel.style.display = 'block' } else { panel.style.display = 'none' } } })(settingPanelOptions, {}); async function findCidInfo() { let cid = window.prompt("请输入要查询的cid") if (!/^\d+$/.exec(cid)) { alert("请输入一个数字") return } let response = await fetch(`/api/cidinfo?cid=${cid}`) let body = await response.json() window.alert((JSON.stringify(body.data))) } (function createToolbar(config) { let html = ` <style> #triggerArea { position: fixed; top: 45%; left: 0; width: max(5%,50px); height: 30%; cursor: pointer; z-index: 999998; } #toolbar { position: fixed; top: 50%; left: -250px; transform: translateY(-50%); background-color: #333; color: #fff; padding: 10px; border-top-right-radius: 5px; border-bottom-right-radius: 5px; cursor: grab; transition: left 0.3s; z-index: 999999; } #toolbar:active { cursor: grabbing; } #toolbar button { display: block; margin: 5px 0; padding: 8px; background-color: #555; border: none; color: #fff; cursor: pointer; border-radius: 3px; } </style> <div id="triggerArea"></div> <div id="toolbar"></div> ` document.body.insertAdjacentHTML('beforeend', html) const triggerArea = document.getElementById('triggerArea'); const toolbar = document.getElementById('toolbar'); let isDragging = false; let isExpanded = false; let startY = 0; let initialTop = 0; let currentSetting = getLocalSetting('dfToolbar') if (currentSetting['offsetTopPercent']) { toolbar.setAttribute('offsetTop', currentSetting['offsetTopPercent'] * window.innerHeight) } console.log('createToolbar', config) for (let option of Object.keys(config.options)) { let button = document.createElement("button") button.innerText = option button.addEventListener('click', config.options[option]) toolbar.appendChild(button) } function expandToolbar() { if (!isExpanded) { toolbar.style.left = '0'; isExpanded = true; } } function collapseToolbar() { if (isExpanded) { toolbar.style.left = '-250px'; isExpanded = false; } } triggerArea.addEventListener('mouseenter', () => { expandToolbar(); }); triggerArea.addEventListener('mouseleave', () => { collapseToolbar(); if (isDragging) { isDragging = false dragEndHandle() } }); toolbar.addEventListener('mouseenter', () => { expandToolbar(); }); toolbar.addEventListener('mouseleave', () => { if (!isDragging) { collapseToolbar(); } }); toolbar.addEventListener('mousedown', (e) => { if (e.target === toolbar) { console.log(e.type, e) isDragging = true; startY = e.clientY; initialTop = toolbar.offsetTop; } }); let draggingHandle = (e) => { if (!isDragging) return; const deltaY = e.clientY - startY; toolbar.style.top = `${initialTop + deltaY}px`; } let dragEndHandle = (e) => { if (isDragging) { isDragging = false; currentSetting.offsetTopPercent = toolbar.offsetTop / window.innerHeight saveLocalSetting('dfToolbar', currentSetting) } } window.addEventListener('mousemove', draggingHandle); window.addEventListener('mouseup', dragEndHandle); expandToolbar() setTimeout(collapseToolbar, 3000) })({ options: { "下载选项": showSettingPanel, "CID 反查": findCidInfo, "AID 查询": aidQuery } }); return currentSetting } !function (z) { var y = { 1: [function (p, w) { w.exports = function (h, m) { for (var n = Array(arguments.length - 1), e = 0, d = 2, a = !0; d < arguments.length;) n[e++] = arguments[d++]; return new Promise(function (b, c) { n[e] = function (k) { if (a) if (a = !1, k) c(k); else { for (var l = Array(arguments.length - 1), q = 0; q < l.length;) l[q++] = arguments[q]; b.apply(null, l) } }; try { h.apply(m || null, n) } catch (k) { a && (a = !1, c(k)) } }) } }, {}], 2: [function (p, w, h) { h.length = function (e) { var d = e.length; if (!d) return 0; for (var a = 0; 1 < --d % 4 && "=" === e.charAt(d);) ++a; return Math.ceil(3 * e.length) / 4 - a }; var m = Array(64), n = Array(123); for (p = 0; 64 > p;) n[m[p] = 26 > p ? p + 65 : 52 > p ? p + 71 : 62 > p ? p - 4 : p - 59 | 43] = p++; h.encode = function (e, d, a) { for (var b, c = null, k = [], l = 0, q = 0; d < a;) { var f = e[d++]; switch (q) { case 0: k[l++] = m[f >> 2]; b = (3 & f) << 4; q = 1; break; case 1: k[l++] = m[b | f >> 4]; b = (15 & f) << 2; q = 2; break; case 2: k[l++] = m[b | f >> 6], k[l++] = m[63 & f], q = 0 } 8191 < l && ((c || (c = [])).push(String.fromCharCode.apply(String, k)), l = 0) } return q && (k[l++] = m[b], k[l++] = 61, 1 === q && (k[l++] = 61)), c ? (l && c.push(String.fromCharCode.apply(String, k.slice(0, l))), c.join("")) : String.fromCharCode.apply(String, k.slice(0, l)) }; h.decode = function (e, d, a) { for (var b, c = a, k = 0, l = 0; l < e.length;) { var q = e.charCodeAt(l++); if (61 === q && 1 < k) break; if ((q = n[q]) === z) throw Error("invalid encoding"); switch (k) { case 0: b = q; k = 1; break; case 1: d[a++] = b << 2 | (48 & q) >> 4; b = q; k = 2; break; case 2: d[a++] = (15 & b) << 4 | (60 & q) >> 2; b = q; k = 3; break; case 3: d[a++] = (3 & b) << 6 | q, k = 0 } } if (1 === k) throw Error("invalid encoding"); return a - c }; h.test = function (e) { return /^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(e) } }, {}], 3: [function (p, w) { function h() { this.t = {} } (w.exports = h).prototype.on = function (m, n, e) { return (this.t[m] || (this.t[m] = [])).push({fn: n, ctx: e || this}), this }; h.prototype.off = function (m, n) { if (m === z) this.t = {}; else if (n === z) this.t[m] = []; else for (var e = this.t[m], d = 0; d < e.length;) e[d].fn === n ? e.splice(d, 1) : ++d; return this }; h.prototype.emit = function (m) { var n = this.t[m]; if (n) { for (var e = [], d = 1; d < arguments.length;) e.push(arguments[d++]); for (d = 0; d < n.length;) n[d].fn.apply(n[d++].ctx, e) } return this } }, {}], 4: [function (p, w) { function h(a) { return "undefined" != typeof Float32Array ? function () { function b(v, g, t) { q[0] = v; g[t] = f[0]; g[t + 1] = f[1]; g[t + 2] = f[2]; g[t + 3] = f[3] } function c(v, g, t) { q[0] = v; g[t] = f[3]; g[t + 1] = f[2]; g[t + 2] = f[1]; g[t + 3] = f[0] } function k(v, g) { return f[0] = v[g], f[1] = v[g + 1], f[2] = v[g + 2], f[3] = v[g + 3], q[0] } function l(v, g) { return f[3] = v[g], f[2] = v[g + 1], f[1] = v[g + 2], f[0] = v[g + 3], q[0] } var q = new Float32Array([-0]), f = new Uint8Array(q.buffer), u = 128 === f[3]; a.writeFloatLE = u ? b : c; a.writeFloatBE = u ? c : b; a.readFloatLE = u ? k : l; a.readFloatBE = u ? l : k }() : function () { function b(k, l, q, f) { var u = 0 > l ? 1 : 0; if (u && (l = -l), 0 === l) k(0 < 1 / l ? 0 : 2147483648, q, f); else if (isNaN(l)) k(2143289344, q, f); else if (3.4028234663852886E38 < l) k((u << 31 | 2139095040) >>> 0, q, f); else if (1.1754943508222875E-38 > l) k((u << 31 | Math.round(l / 1.401298464324817E-45)) >>> 0, q, f); else { var v = Math.floor(Math.log(l) / Math.LN2); k((u << 31 | v + 127 << 23 | 8388607 & Math.round(l * Math.pow(2, -v) * 8388608)) >>> 0, q, f) } } function c(k, l, q) { q = k(l, q); k = 2 * (q >> 31) + 1; l = q >>> 23 & 255; q &= 8388607; return 255 === l ? q ? NaN : 1 / 0 * k : 0 === l ? 1.401298464324817E-45 * k * q : k * Math.pow(2, l - 150) * (q + 8388608) } a.writeFloatLE = b.bind(null, m); a.writeFloatBE = b.bind(null, n); a.readFloatLE = c.bind(null, e); a.readFloatBE = c.bind(null, d) }(), "undefined" != typeof Float64Array ? function () { function b(v, g, t) { q[0] = v; g[t] = f[0]; g[t + 1] = f[1]; g[t + 2] = f[2]; g[t + 3] = f[3]; g[t + 4] = f[4]; g[t + 5] = f[5]; g[t + 6] = f[6]; g[t + 7] = f[7] } function c(v, g, t) { q[0] = v; g[t] = f[7]; g[t + 1] = f[6]; g[t + 2] = f[5]; g[t + 3] = f[4]; g[t + 4] = f[3]; g[t + 5] = f[2]; g[t + 6] = f[1]; g[t + 7] = f[0] } function k(v, g) { return f[0] = v[g], f[1] = v[g + 1], f[2] = v[g + 2], f[3] = v[g + 3], f[4] = v[g + 4], f[5] = v[g + 5], f[6] = v[g + 6], f[7] = v[g + 7], q[0] } function l(v, g) { return f[7] = v[g], f[6] = v[g + 1], f[5] = v[g + 2], f[4] = v[g + 3], f[3] = v[g + 4], f[2] = v[g + 5], f[1] = v[g + 6], f[0] = v[g + 7], q[0] } var q = new Float64Array([-0]), f = new Uint8Array(q.buffer), u = 128 === f[7]; a.writeDoubleLE = u ? b : c; a.writeDoubleBE = u ? c : b; a.readDoubleLE = u ? k : l; a.readDoubleBE = u ? l : k }() : function () { function b(k, l, q, f, u, v) { var g = 0 > f ? 1 : 0; if (g && (f = -f), 0 === f) k(0, u, v + l), k(0 < 1 / f ? 0 : 2147483648, u, v + q); else if (isNaN(f)) k(0, u, v + l), k(2146959360, u, v + q); else if (1.7976931348623157E308 < f) k(0, u, v + l), k((g << 31 | 2146435072) >>> 0, u, v + q); else if (2.2250738585072014E-308 > f) k((f /= 4.9E-324) >>> 0, u, v + l), k((g << 31 | f / 4294967296) >>> 0, u, v + q); else { var t = Math.floor(Math.log(f) / Math.LN2); 1024 === t && (t = 1023); k(4503599627370496 * (f *= Math.pow(2, -t)) >>> 0, u, v + l); k((g << 31 | t + 1023 << 20 | 1048576 * f & 1048575) >>> 0, u, v + q) } } function c(k, l, q, f, u) { l = k(f, u + l); f = k(f, u + q); k = 2 * (f >> 31) + 1; q = f >>> 20 & 2047; l = 4294967296 * (1048575 & f) + l; return 2047 === q ? l ? NaN : 1 / 0 * k : 0 === q ? 4.9E-324 * k * l : k * Math.pow(2, q - 1075) * (l + 4503599627370496) } a.writeDoubleLE = b.bind(null, m, 0, 4); a.writeDoubleBE = b.bind(null, n, 4, 0); a.readDoubleLE = c.bind(null, e, 0, 4); a.readDoubleBE = c.bind(null, d, 4, 0) }(), a } function m(a, b, c) { b[c] = 255 & a; b[c + 1] = a >>> 8 & 255; b[c + 2] = a >>> 16 & 255; b[c + 3] = a >>> 24 } function n(a, b, c) { b[c] = a >>> 24; b[c + 1] = a >>> 16 & 255; b[c + 2] = a >>> 8 & 255; b[c + 3] = 255 & a } function e(a, b) { return (a[b] | a[b + 1] << 8 | a[b + 2] << 16 | a[b + 3] << 24) >>> 0 } function d(a, b) { return (a[b] << 24 | a[b + 1] << 16 | a[b + 2] << 8 | a[b + 3]) >>> 0 } w.exports = h(h) }, {}], 5: [function (p, w, h) { w.exports = function (m) { try { var n = eval("require")(m); if (n && (n.length || Object.keys(n).length)) return n } catch (e) { } return null } }, {}], 6: [function (p, w) { w.exports = function (h, m, n) { var e = n || 8192, d = e >>> 1, a = null, b = e; return function (c) { if (1 > c || d < c) return h(c); e < b + c && (a = h(e), b = 0); c = m.call(a, b, b += c); return 7 & b && (b = 1 + (7 | b)), c } } }, {}], 7: [function (p, w, h) { h.length = function (m) { for (var n = 0, e = 0, d = 0; d < m.length; ++d) 128 > (e = m.charCodeAt(d)) ? n += 1 : 2048 > e ? n += 2 : 55296 == (64512 & e) && 56320 == (64512 & m.charCodeAt(d + 1)) ? (++d, n += 4) : n += 3; return n }; h.read = function (m, n, e) { if (1 > e - n) return ""; for (var d, a = null, b = [], c = 0; n < e;) 128 > (d = m[n++]) ? b[c++] = d : 191 < d && 224 > d ? b[c++] = (31 & d) << 6 | 63 & m[n++] : 239 < d && 365 > d ? (d = ((7 & d) << 18 | (63 & m[n++]) << 12 | (63 & m[n++]) << 6 | 63 & m[n++]) - 65536, b[c++] = 55296 + (d >> 10), b[c++] = 56320 + (1023 & d)) : b[c++] = (15 & d) << 12 | (63 & m[n++]) << 6 | 63 & m[n++], 8191 < c && ((a || (a = [])).push(String.fromCharCode.apply(String, b)), c = 0); return a ? (c && a.push(String.fromCharCode.apply(String, b.slice(0, c))), a.join("")) : String.fromCharCode.apply(String, b.slice(0, c)) }; h.write = function (m, n, e) { for (var d, a, b = e, c = 0; c < m.length; ++c) 128 > (d = m.charCodeAt(c)) ? n[e++] = d : (2048 > d ? n[e++] = d >> 6 | 192 : (55296 == (64512 & d) && 56320 == (64512 & (a = m.charCodeAt(c + 1))) ? (d = 65536 + ((1023 & d) << 10) + (1023 & a), ++c, n[e++] = d >> 18 | 240, n[e++] = d >> 12 & 63 | 128) : n[e++] = d >> 12 | 224, n[e++] = d >> 6 & 63 | 128), n[e++] = 63 & d | 128); return e - b } }, {}], 8: [function (p, w, h) { function m() { n.Reader.n(n.BufferReader); n.util.n() } var n = h; n.build = "minimal"; n.Writer = p(16); n.BufferWriter = p(17); n.Reader = p(9); n.BufferReader = p(10); n.util = p(15); n.rpc = p(12); n.roots = p(11); n.configure = m; n.Writer.n(n.BufferWriter); m() }, {10: 10, 11: 11, 12: 12, 15: 15, 16: 16, 17: 17, 9: 9}], 9: [function (p, w) { function h(f, u) { return RangeError("index out of range: " + f.pos + " + " + (u || 1) + " > " + f.len) } function m(f) { this.buf = f; this.pos = 0; this.len = f.length } function n() { var f = new c(0, 0), u = 0; if (!(4 < this.len - this.pos)) { for (; 3 > u; ++u) { if (this.pos >= this.len) throw h(this); if (f.lo = (f.lo | (127 & this.buf[this.pos]) << 7 * u) >>> 0, 128 > this.buf[this.pos++]) return f } return f.lo = (f.lo | (127 & this.buf[this.pos++]) << 7 * u) >>> 0, f } for (; 4 > u; ++u) if (f.lo = (f.lo | (127 & this.buf[this.pos]) << 7 * u) >>> 0, 128 > this.buf[this.pos++]) return f; if (f.lo = (f.lo | (127 & this.buf[this.pos]) << 28) >>> 0, f.hi = (f.hi | (127 & this.buf[this.pos]) >> 4) >>> 0, 128 > this.buf[this.pos++]) return f; if (u = 0, 4 < this.len - this.pos) for (; 5 > u; ++u) { if (f.hi = (f.hi | (127 & this.buf[this.pos]) << 7 * u + 3) >>> 0, 128 > this.buf[this.pos++]) return f } else for (; 5 > u; ++u) { if (this.pos >= this.len) throw h(this); if (f.hi = (f.hi | (127 & this.buf[this.pos]) << 7 * u + 3) >>> 0, 128 > this.buf[this.pos++]) return f } throw Error("invalid varint encoding"); } function e(f, u) { return (f[u - 4] | f[u - 3] << 8 | f[u - 2] << 16 | f[u - 1] << 24) >>> 0 } function d() { if (this.pos + 8 > this.len) throw h(this, 8); return new c(e(this.buf, this.pos += 4), e(this.buf, this.pos += 4)) } w.exports = m; var a, b = p(15), c = b.LongBits, k = b.utf8, l, q = "undefined" != typeof Uint8Array ? function (f) { if (f instanceof Uint8Array || Array.isArray(f)) return new m(f); throw Error("illegal buffer"); } : function (f) { if (Array.isArray(f)) return new m(f); throw Error("illegal buffer"); }; m.create = b.Buffer ? function (f) { return (m.create = function (u) { return b.Buffer.isBuffer(u) ? new a(u) : q(u) })(f) } : q; m.prototype.i = b.Array.prototype.subarray || b.Array.prototype.slice; m.prototype.uint32 = (l = 4294967295, function () { if ((l = (127 & this.buf[this.pos]) >>> 0, 128 > this.buf[this.pos++]) || (l = (l | (127 & this.buf[this.pos]) << 7) >>> 0, 128 > this.buf[this.pos++]) || (l = (l | (127 & this.buf[this.pos]) << 14) >>> 0, 128 > this.buf[this.pos++]) || (l = (l | (127 & this.buf[this.pos]) << 21) >>> 0, 128 > this.buf[this.pos++]) || (l = (l | (15 & this.buf[this.pos]) << 28) >>> 0, 128 > this.buf[this.pos++])) return l; if ((this.pos += 5) > this.len) throw this.pos = this.len, h(this, 10); return l }); m.prototype.int32 = function () { return 0 | this.uint32() }; m.prototype.sint32 = function () { var f = this.uint32(); return f >>> 1 ^ -(1 & f) | 0 }; m.prototype.bool = function () { return 0 !== this.uint32() }; m.prototype.fixed32 = function () { if (this.pos + 4 > this.len) throw h(this, 4); return e(this.buf, this.pos += 4) }; m.prototype.sfixed32 = function () { if (this.pos + 4 > this.len) throw h(this, 4); return 0 | e(this.buf, this.pos += 4) }; m.prototype["float"] = function () { if (this.pos + 4 > this.len) throw h(this, 4); var f = b["float"].readFloatLE(this.buf, this.pos); return this.pos += 4, f }; m.prototype["double"] = function () { if (this.pos + 8 > this.len) throw h(this, 4); var f = b["float"].readDoubleLE(this.buf, this.pos); return this.pos += 8, f }; m.prototype.bytes = function () { var f = this.uint32(), u = this.pos, v = this.pos + f; if (v > this.len) throw h(this, f); return this.pos += f, Array.isArray(this.buf) ? this.buf.slice(u, v) : u === v ? new this.buf.constructor(0) : this.i.call(this.buf, u, v) }; m.prototype.string = function () { var f = this.bytes(); return k.read(f, 0, f.length) }; m.prototype.skip = function (f) { if ("number" == typeof f) { if (this.pos + f > this.len) throw h(this, f); this.pos += f } else { do if (this.pos >= this.len) throw h(this); while (128 & this.buf[this.pos++]) } return this }; m.prototype.skipType = function (f) { switch (f) { case 0: this.skip(); break; case 1: this.skip(8); break; case 2: this.skip(this.uint32()); break; case 3: for (; 4 != (f = 7 & this.uint32());) this.skipType(f); break; case 5: this.skip(4); break; default: throw Error("invalid wire type " + f + " at offset " + this.pos); } return this }; m.n = function (f) { a = f; var u = b.Long ? "toLong" : "toNumber"; b.merge(m.prototype, { int64: function () { return n.call(this)[u](!1) }, uint64: function () { return n.call(this)[u](!0) }, sint64: function () { return n.call(this).zzDecode()[u](!1) }, fixed64: function () { return d.call(this)[u](!0) }, sfixed64: function () { return d.call(this)[u](!1) } }) } }, {15: 15}], 10: [function (p, w) { function h(e) { m.call(this, e) } w.exports = h; var m = p(9); (h.prototype = Object.create(m.prototype)).constructor = h; var n = p(15); n.Buffer && (h.prototype.i = n.Buffer.prototype.slice); h.prototype.string = function () { var e = this.uint32(); return this.buf.utf8Slice(this.pos, this.pos = Math.min(this.pos + e, this.len)) } }, {15: 15, 9: 9}], 11: [function (p, w) { w.exports = {} }, {}], 12: [function (p, w, h) { h.Service = p(13) }, {13: 13}], 13: [function (p, w) { function h(n, e, d) { if ("function" != typeof n) throw TypeError("rpcImpl must be a function"); m.EventEmitter.call(this); this.rpcImpl = n; this.requestDelimited = !!e; this.responseDelimited = !!d } w.exports = h; var m = p(15); ((h.prototype = Object.create(m.EventEmitter.prototype)).constructor = h).prototype.rpcCall = function k(e, d, a, b, c) { if (!b) throw TypeError("request must be specified"); var l = this; if (!c) return m.asPromise(k, l, e, d, a, b); if (!l.rpcImpl) return setTimeout(function () { c(Error("already ended")) }, 0), z; try { return l.rpcImpl(e, d[l.requestDelimited ? "encodeDelimited" : "encode"](b).finish(), function (q, f) { if (q) return l.emit("error", q, e), c(q); if (null === f) return l.end(!0), z; if (!(f instanceof a)) try { f = a[l.responseDelimited ? "decodeDelimited" : "decode"](f) } catch (u) { return l.emit("error", u, e), c(u) } return l.emit("data", f, e), c(null, f) }) } catch (q) { return l.emit("error", q, e), setTimeout(function () { c(q) }, 0), z } }; h.prototype.end = function (e) { return this.rpcImpl && (e || this.rpcImpl(null, null, null), this.rpcImpl = null, this.emit("end").off()), this } }, {15: 15}], 14: [function (p, w) { function h(a, b) { this.lo = a >>> 0; this.hi = b >>> 0 } w.exports = h; var m = p(15), n = h.zero = new h(0, 0); n.toNumber = function () { return 0 }; n.zzEncode = n.zzDecode = function () { return this }; n.length = function () { return 1 }; var e = h.zeroHash = "\x00\x00\x00\x00\x00\x00\x00\x00"; h.fromNumber = function (a) { if (0 === a) return n; var b = 0 > a; b && (a = -a); var c = a >>> 0; a = (a - c) / 4294967296 >>> 0; return b && (a = ~a >>> 0, c = ~c >>> 0, 4294967295 < ++c && (c = 0, 4294967295 < ++a && (a = 0))), new h(c, a) }; h.from = function (a) { if ("number" == typeof a) return h.fromNumber(a); if (m.isString(a)) { if (!m.Long) return h.fromNumber(parseInt(a, 10)); a = m.Long.fromString(a) } return a.low || a.high ? new h(a.low >>> 0, a.high >>> 0) : n }; h.prototype.toNumber = function (a) { if (!a && this.hi >>> 31) { a = 1 + ~this.lo >>> 0; var b = ~this.hi >>> 0; return a || (b = b + 1 >>> 0), -(a + 4294967296 * b) } return this.lo + 4294967296 * this.hi }; h.prototype.toLong = function (a) { return m.Long ? new m.Long(0 | this.lo, 0 | this.hi, !!a) : { low: 0 | this.lo, high: 0 | this.hi, unsigned: !!a } }; var d = String.prototype.charCodeAt; h.fromHash = function (a) { return a === e ? n : new h((d.call(a, 0) | d.call(a, 1) << 8 | d.call(a, 2) << 16 | d.call(a, 3) << 24) >>> 0, (d.call(a, 4) | d.call(a, 5) << 8 | d.call(a, 6) << 16 | d.call(a, 7) << 24) >>> 0) }; h.prototype.toHash = function () { return String.fromCharCode(255 & this.lo, this.lo >>> 8 & 255, this.lo >>> 16 & 255, this.lo >>> 24, 255 & this.hi, this.hi >>> 8 & 255, this.hi >>> 16 & 255, this.hi >>> 24) }; h.prototype.zzEncode = function () { var a = this.hi >> 31; return this.hi = ((this.hi << 1 | this.lo >>> 31) ^ a) >>> 0, this.lo = (this.lo << 1 ^ a) >>> 0, this }; h.prototype.zzDecode = function () { var a = -(1 & this.lo); return this.lo = ((this.lo >>> 1 | this.hi << 31) ^ a) >>> 0, this.hi = (this.hi >>> 1 ^ a) >>> 0, this }; h.prototype.length = function () { var a = this.lo, b = (this.lo >>> 28 | this.hi << 4) >>> 0, c = this.hi >>> 24; return 0 === c ? 0 === b ? 16384 > a ? 128 > a ? 1 : 2 : 2097152 > a ? 3 : 4 : 16384 > b ? 128 > b ? 5 : 6 : 2097152 > b ? 7 : 8 : 128 > c ? 9 : 10 } }, {15: 15}], 15: [function (p, w, h) { function m(e, d, a) { for (var b = Object.keys(d), c = 0; c < b.length; ++c) e[b[c]] !== z && a || (e[b[c]] = d[b[c]]); return e } function n(e) { function d(a, b) { if (!(this instanceof d)) return new d(a, b); Object.defineProperty(this, "message", { get: function () { return a } }); Error.captureStackTrace ? Error.captureStackTrace(this, d) : Object.defineProperty(this, "stack", {value: Error().stack || ""}); b && m(this, b) } return (d.prototype = Object.create(Error.prototype)).constructor = d, Object.defineProperty(d.prototype, "name", { get: function () { return e } }), d.prototype.toString = function () { return this.name + ": " + this.message }, d } h.asPromise = p(1); h.base64 = p(2); h.EventEmitter = p(3); h["float"] = p(4); h.inquire = p(5); h.utf8 = p(7); h.pool = p(6); h.LongBits = p(14); h.global = "undefined" != typeof window && window || "undefined" != typeof global && global || "undefined" != typeof self && self || this; h.emptyArray = Object.freeze ? Object.freeze([]) : []; h.emptyObject = Object.freeze ? Object.freeze({}) : {}; h.isNode = !!(h.global.process && h.global.process.versions && h.global.process.versions.node); h.isInteger = Number.isInteger || function (e) { return "number" == typeof e && isFinite(e) && Math.floor(e) === e }; h.isString = function (e) { return "string" == typeof e || e instanceof String }; h.isObject = function (e) { return e && "object" == typeof e }; h.isset = h.isSet = function (e, d) { var a = e[d]; return !(null == a || !e.hasOwnProperty(d)) && ("object" != typeof a || 0 < (Array.isArray(a) ? a.length : Object.keys(a).length)) }; h.Buffer = function () { try { var e = h.inquire("buffer").Buffer; return e.prototype.utf8Write ? e : null } catch (d) { return null } }(); h.r = null; h.u = null; h.newBuffer = function (e) { return "number" == typeof e ? h.Buffer ? h.u(e) : new h.Array(e) : h.Buffer ? h.r(e) : "undefined" == typeof Uint8Array ? e : new Uint8Array(e) }; h.Array = "undefined" != typeof Uint8Array ? Uint8Array : Array; h.Long = h.global.dcodeIO && h.global.dcodeIO.Long || h.global.Long || h.inquire("long"); h.key2Re = /^true|false|0|1$/; h.key32Re = /^-?(?:0|[1-9][0-9]*)$/; h.key64Re = /^(?:[\\x00-\\xff]{8}|-?(?:0|[1-9][0-9]*))$/; h.longToHash = function (e) { return e ? h.LongBits.from(e).toHash() : h.LongBits.zeroHash }; h.longFromHash = function (e, d) { var a = h.LongBits.fromHash(e); return h.Long ? h.Long.fromBits(a.lo, a.hi, d) : a.toNumber(!!d) }; h.merge = m; h.lcFirst = function (e) { return e.charAt(0).toLowerCase() + e.substring(1) }; h.newError = n; h.ProtocolError = n("ProtocolError"); h.oneOfGetter = function (e) { for (var d = {}, a = 0; a < e.length; ++a) d[e[a]] = 1; return function () { for (var b = Object.keys(this), c = b.length - 1; -1 < c; --c) if (1 === d[b[c]] && this[b[c]] !== z && null !== this[b[c]]) return b[c] } }; h.oneOfSetter = function (e) { return function (d) { for (var a = 0; a < e.length; ++a) e[a] !== d && delete this[e[a]] } }; h.toJSONOptions = {longs: String, enums: String, bytes: String, json: !0}; h.n = function () { var e = h.Buffer; e ? (h.r = e.from !== Uint8Array.from && e.from || function (d, a) { return new e(d, a) }, h.u = e.allocUnsafe || function (d) { return new e(d) }) : h.r = h.u = null } }, {1: 1, 14: 14, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7}], 16: [function (p, w) { function h(g, t, x) { this.fn = g; this.len = t; this.next = z; this.val = x } function m() { } function n(g) { this.head = g.head; this.tail = g.tail; this.len = g.len; this.next = g.states } function e() { this.len = 0; this.tail = this.head = new h(m, 0, 0); this.states = null } function d(g, t, x) { t[x] = 255 & g } function a(g, t) { this.len = g; this.next = z; this.val = t } function b(g, t, x) { for (; g.hi;) t[x++] = 127 & g.lo | 128, g.lo = (g.lo >>> 7 | g.hi << 25) >>> 0, g.hi >>>= 7; for (; 127 < g.lo;) t[x++] = 127 & g.lo | 128, g.lo >>>= 7; t[x++] = g.lo } function c(g, t, x) { t[x] = 255 & g; t[x + 1] = g >>> 8 & 255; t[x + 2] = g >>> 16 & 255; t[x + 3] = g >>> 24 } w.exports = e; var k, l = p(15), q = l.LongBits, f = l.base64, u = l.utf8; e.create = l.Buffer ? function () { return (e.create = function () { return new k })() } : function () { return new e }; e.alloc = function (g) { return new l.Array(g) }; l.Array !== Array && (e.alloc = l.pool(e.alloc, l.Array.prototype.subarray)); e.prototype.e = function (g, t, x) { return this.tail = this.tail.next = new h(g, t, x), this.len += t, this }; (a.prototype = Object.create(h.prototype)).fn = function (g, t, x) { for (; 127 < g;) t[x++] = 127 & g | 128, g >>>= 7; t[x] = g }; e.prototype.uint32 = function (g) { return this.len += (this.tail = this.tail.next = new a(128 > (g >>>= 0) ? 1 : 16384 > g ? 2 : 2097152 > g ? 3 : 268435456 > g ? 4 : 5, g)).len, this }; e.prototype.int32 = function (g) { return 0 > g ? this.e(b, 10, q.fromNumber(g)) : this.uint32(g) }; e.prototype.sint32 = function (g) { return this.uint32((g << 1 ^ g >> 31) >>> 0) }; e.prototype.int64 = e.prototype.uint64 = function (g) { g = q.from(g); return this.e(b, g.length(), g) }; e.prototype.sint64 = function (g) { g = q.from(g).zzEncode(); return this.e(b, g.length(), g) }; e.prototype.bool = function (g) { return this.e(d, 1, g ? 1 : 0) }; e.prototype.sfixed32 = e.prototype.fixed32 = function (g) { return this.e(c, 4, g >>> 0) }; e.prototype.sfixed64 = e.prototype.fixed64 = function (g) { g = q.from(g); return this.e(c, 4, g.lo).e(c, 4, g.hi) }; e.prototype["float"] = function (g) { return this.e(l["float"].writeFloatLE, 4, g) }; e.prototype["double"] = function (g) { return this.e(l["float"].writeDoubleLE, 8, g) }; var v = l.Array.prototype.set ? function (g, t, x) { t.set(g, x) } : function (g, t, x) { for (var B = 0; B < g.length; ++B) t[x + B] = g[B] }; e.prototype.bytes = function (g) { var t = g.length >>> 0; if (!t) return this.e(d, 1, 0); if (l.isString(g)) { var x = e.alloc(t = f.length(g)); f.decode(g, x, 0); g = x } return this.uint32(t).e(v, t, g) }; e.prototype.string = function (g) { var t = u.length(g); return t ? this.uint32(t).e(u.write, t, g) : this.e(d, 1, 0) }; e.prototype.fork = function () { return this.states = new n(this), this.head = this.tail = new h(m, 0, 0), this.len = 0, this }; e.prototype.reset = function () { return this.states ? (this.head = this.states.head, this.tail = this.states.tail, this.len = this.states.len, this.states = this.states.next) : (this.head = this.tail = new h(m, 0, 0), this.len = 0), this }; e.prototype.ldelim = function () { var g = this.head, t = this.tail, x = this.len; return this.reset().uint32(x), x && (this.tail.next = g.next, this.tail = t, this.len += x), this }; e.prototype.finish = function () { for (var g = this.head.next, t = this.constructor.alloc(this.len), x = 0; g;) g.fn(g.val, t, x), x += g.len, g = g.next; return t }; e.n = function (g) { k = g } }, {15: 15}], 17: [function (p, w) { function h() { n.call(this) } function m(b, c, k) { 40 > b.length ? e.utf8.write(b, c, k) : c.utf8Write(b, k) } w.exports = h; var n = p(16); (h.prototype = Object.create(n.prototype)).constructor = h; var e = p(15), d = e.Buffer; h.alloc = function (b) { return (h.alloc = e.u)(b) }; var a = d && d.prototype instanceof Uint8Array && "set" === d.prototype.set.name ? function (b, c, k) { c.set(b, k) } : function (b, c, k) { if (b.copy) b.copy(c, k, 0, b.length); else for (var l = 0; l < b.length;) c[k++] = b[l++] }; h.prototype.bytes = function (b) { e.isString(b) && (b = e.r(b, "base64")); var c = b.length >>> 0; return this.uint32(c), c && this.e(a, c, b), this }; h.prototype.string = function (b) { var c = d.byteLength(b); return this.uint32(c), c && this.e(m, c, b), this } }, {15: 15, 16: 16}] }; var A = {}; var r = function h(w) { var m = A[w]; return m || y[w][0].call(m = A[w] = {exports: {}}, h, m, m.exports), m.exports }(8); r.util.global.protobuf = r; "function" == typeof define && define.amd && define(["long"], function (w) { return w && w.isLong && (r.util.Long = w, r.configure()), r }); "object" == typeof module && module && module.exports && (module.exports = r) }(); (function (z) { var y = z.Reader, A = z.Writer, r = z.util, p = z.roots["default"] || (z.roots["default"] = {}); p.bilibili = function () { var w = {}; w.community = function () { var h = {}; h.service = function () { var m = {}; m.dm = function () { var n = {}; n.v1 = function () { var e = {}; e.DmWebViewReply = function () { function d(a) { this.specialDms = []; if (a) for (var b = Object.keys(a), c = 0; c < b.length; ++c) null != a[b[c]] && (this[b[c]] = a[b[c]]) } d.prototype.state = 0; d.prototype.text = ""; d.prototype.textSide = ""; d.prototype.dmSge = null; d.prototype.flag = null; d.prototype.specialDms = r.emptyArray; d.create = function (a) { return new d(a) }; d.encode = function (a, b) { b || (b = A.create()); null != a.state && Object.hasOwnProperty.call(a, "state") && b.uint32(8).int32(a.state); null != a.text && Object.hasOwnProperty.call(a, "text") && b.uint32(18).string(a.text); null != a.textSide && Object.hasOwnProperty.call(a, "textSide") && b.uint32(26).string(a.textSide); null != a.dmSge && Object.hasOwnProperty.call(a, "dmSge") && p.bilibili.community.service.dm.v1.DmSegConfig.encode(a.dmSge, b.uint32(34).fork()).ldelim(); null != a.flag && Object.hasOwnProperty.call(a, "flag") && p.bilibili.community.service.dm.v1.DanmakuFlagConfig.encode(a.flag, b.uint32(42).fork()).ldelim(); if (null != a.specialDms && a.specialDms.length) for (var c = 0; c < a.specialDms.length; ++c) b.uint32(50).string(a.specialDms[c]); return b }; d.encodeDelimited = function (a, b) { return this.encode(a, b).ldelim() }; d.decode = function (a, b) { a instanceof y || (a = y.create(a)); for (var c = void 0 === b ? a.len : a.pos + b, k = new p.bilibili.community.service.dm.v1.DmWebViewReply; a.pos < c;) { var l = a.uint32(); switch (l >>> 3) { case 1: k.state = a.int32(); break; case 2: k.text = a.string(); break; case 3: k.textSide = a.string(); break; case 4: k.dmSge = p.bilibili.community.service.dm.v1.DmSegConfig.decode(a, a.uint32()); break; case 5: k.flag = p.bilibili.community.service.dm.v1.DanmakuFlagConfig.decode(a, a.uint32()); break; case 6: k.specialDms && k.specialDms.length || (k.specialDms = []); k.specialDms.push(a.string()); break; default: a.skipType(l & 7) } } return k }; d.decodeDelimited = function (a) { a instanceof y || (a = new y(a)); return this.decode(a, a.uint32()) }; d.verify = function (a) { if ("object" !== typeof a || null === a) return "object expected"; if (null != a.state && a.hasOwnProperty("state") && !r.isInteger(a.state)) return "state: integer expected"; if (null != a.text && a.hasOwnProperty("text") && !r.isString(a.text)) return "text: string expected"; if (null != a.textSide && a.hasOwnProperty("textSide") && !r.isString(a.textSide)) return "textSide: string expected"; if (null != a.dmSge && a.hasOwnProperty("dmSge")) { var b = p.bilibili.community.service.dm.v1.DmSegConfig.verify(a.dmSge); if (b) return "dmSge." + b } if (null != a.flag && a.hasOwnProperty("flag") && (b = p.bilibili.community.service.dm.v1.DanmakuFlagConfig.verify(a.flag))) return "flag." + b; if (null != a.specialDms && a.hasOwnProperty("specialDms")) { if (!Array.isArray(a.specialDms)) return "specialDms: array expected"; for (b = 0; b < a.specialDms.length; ++b) if (!r.isString(a.specialDms[b])) return "specialDms: string[] expected" } return null }; d.fromObject = function (a) { if (a instanceof p.bilibili.community.service.dm.v1.DmWebViewReply) return a; var b = new p.bilibili.community.service.dm.v1.DmWebViewReply; null != a.state && (b.state = a.state | 0); null != a.text && (b.text = String(a.text)); null != a.textSide && (b.textSide = String(a.textSide)); if (null != a.dmSge) { if ("object" !== typeof a.dmSge) throw TypeError(".bilibili.community.service.dm.v1.DmWebViewReply.dmSge: object expected"); b.dmSge = p.bilibili.community.service.dm.v1.DmSegConfig.fromObject(a.dmSge) } if (null != a.flag) { if ("object" !== typeof a.flag) throw TypeError(".bilibili.community.service.dm.v1.DmWebViewReply.flag: object expected"); b.flag = p.bilibili.community.service.dm.v1.DanmakuFlagConfig.fromObject(a.flag) } if (a.specialDms) { if (!Array.isArray(a.specialDms)) throw TypeError(".bilibili.community.service.dm.v1.DmWebViewReply.specialDms: array expected"); b.specialDms = []; for (var c = 0; c < a.specialDms.length; ++c) b.specialDms[c] = String(a.specialDms[c]) } return b }; d.toObject = function (a, b) { b || (b = {}); var c = {}; if (b.arrays || b.defaults) c.specialDms = []; b.defaults && (c.state = 0, c.text = "", c.textSide = "", c.dmSge = null, c.flag = null); null != a.state && a.hasOwnProperty("state") && (c.state = a.state); null != a.text && a.hasOwnProperty("text") && (c.text = a.text); null != a.textSide && a.hasOwnProperty("textSide") && (c.textSide = a.textSide); null != a.dmSge && a.hasOwnProperty("dmSge") && (c.dmSge = p.bilibili.community.service.dm.v1.DmSegConfig.toObject(a.dmSge, b)); null != a.flag && a.hasOwnProperty("flag") && (c.flag = p.bilibili.community.service.dm.v1.DanmakuFlagConfig.toObject(a.flag, b)); if (a.specialDms && a.specialDms.length) { c.specialDms = []; for (var k = 0; k < a.specialDms.length; ++k) c.specialDms[k] = a.specialDms[k] } return c }; d.prototype.toJSON = function () { return this.constructor.toObject(this, z.util.toJSONOptions) }; return d }(); e.DmSegConfig = function () { function d(a) { if (a) for (var b = Object.keys(a), c = 0; c < b.length; ++c) null != a[b[c]] && (this[b[c]] = a[b[c]]) } d.prototype.pageSize = r.Long ? r.Long.fromBits(0, 0, !1) : 0; d.prototype.total = r.Long ? r.Long.fromBits(0, 0, !1) : 0; d.create = function (a) { return new d(a) }; d.encode = function (a, b) { b || (b = A.create()); null != a.pageSize && Object.hasOwnProperty.call(a, "pageSize") && b.uint32(8).int64(a.pageSize); null != a.total && Object.hasOwnProperty.call(a, "total") && b.uint32(16).int64(a.total); return b }; d.encodeDelimited = function (a, b) { return this.encode(a, b).ldelim() }; d.decode = function (a, b) { a instanceof y || (a = y.create(a)); for (var c = void 0 === b ? a.len : a.pos + b, k = new p.bilibili.community.service.dm.v1.DmSegConfig; a.pos < c;) { var l = a.uint32(); switch (l >>> 3) { case 1: k.pageSize = a.int64(); break; case 2: k.total = a.int64(); break; default: a.skipType(l & 7) } } return k }; d.decodeDelimited = function (a) { a instanceof y || (a = new y(a)); return this.decode(a, a.uint32()) }; d.verify = function (a) { return "object" !== typeof a || null === a ? "object expected" : null == a.pageSize || !a.hasOwnProperty("pageSize") || r.isInteger(a.pageSize) || a.pageSize && r.isInteger(a.pageSize.low) && r.isInteger(a.pageSize.high) ? null == a.total || !a.hasOwnProperty("total") || r.isInteger(a.total) || a.total && r.isInteger(a.total.low) && r.isInteger(a.total.high) ? null : "total: integer|Long expected" : "pageSize: integer|Long expected" }; d.fromObject = function (a) { if (a instanceof p.bilibili.community.service.dm.v1.DmSegConfig) return a; var b = new p.bilibili.community.service.dm.v1.DmSegConfig; null != a.pageSize && (r.Long ? (b.pageSize = r.Long.fromValue(a.pageSize)).unsigned = !1 : "string" === typeof a.pageSize ? b.pageSize = parseInt(a.pageSize, 10) : "number" === typeof a.pageSize ? b.pageSize = a.pageSize : "object" === typeof a.pageSize && (b.pageSize = (new r.LongBits(a.pageSize.low >>> 0, a.pageSize.high >>> 0)).toNumber())); null != a.total && (r.Long ? (b.total = r.Long.fromValue(a.total)).unsigned = !1 : "string" === typeof a.total ? b.total = parseInt(a.total, 10) : "number" === typeof a.total ? b.total = a.total : "object" === typeof a.total && (b.total = (new r.LongBits(a.total.low >>> 0, a.total.high >>> 0)).toNumber())); return b }; d.toObject = function (a, b) { b || (b = {}); var c = {}; if (b.defaults) { if (r.Long) { var k = new r.Long(0, 0, !1); c.pageSize = b.longs === String ? k.toString() : b.longs === Number ? k.toNumber() : k } else c.pageSize = b.longs === String ? "0" : 0; r.Long ? (k = new r.Long(0, 0, !1), c.total = b.longs === String ? k.toString() : b.longs === Number ? k.toNumber() : k) : c.total = b.longs === String ? "0" : 0 } null != a.pageSize && a.hasOwnProperty("pageSize") && (c.pageSize = "number" === typeof a.pageSize ? b.longs === String ? String(a.pageSize) : a.pageSize : b.longs === String ? r.Long.prototype.toString.call(a.pageSize) : b.longs === Number ? (new r.LongBits(a.pageSize.low >>> 0, a.pageSize.high >>> 0)).toNumber() : a.pageSize); null != a.total && a.hasOwnProperty("total") && (c.total = "number" === typeof a.total ? b.longs === String ? String(a.total) : a.total : b.longs === String ? r.Long.prototype.toString.call(a.total) : b.longs === Number ? (new r.LongBits(a.total.low >>> 0, a.total.high >>> 0)).toNumber() : a.total); return c }; d.prototype.toJSON = function () { return this.constructor.toObject(this, z.util.toJSONOptions) }; return d }(); e.DanmakuFlagConfig = function () { function d(a) { if (a) for (var b = Object.keys(a), c = 0; c < b.length; ++c) null != a[b[c]] && (this[b[c]] = a[b[c]]) } d.prototype.recFlag = 0; d.prototype.recText = ""; d.prototype.recSwitch = 0; d.create = function (a) { return new d(a) }; d.encode = function (a, b) { b || (b = A.create()); null != a.recFlag && Object.hasOwnProperty.call(a, "recFlag") && b.uint32(8).int32(a.recFlag); null != a.recText && Object.hasOwnProperty.call(a, "recText") && b.uint32(18).string(a.recText); null != a.recSwitch && Object.hasOwnProperty.call(a, "recSwitch") && b.uint32(24).int32(a.recSwitch); return b }; d.encodeDelimited = function (a, b) { return this.encode(a, b).ldelim() }; d.decode = function (a, b) { a instanceof y || (a = y.create(a)); for (var c = void 0 === b ? a.len : a.pos + b, k = new p.bilibili.community.service.dm.v1.DanmakuFlagConfig; a.pos < c;) { var l = a.uint32(); switch (l >>> 3) { case 1: k.recFlag = a.int32(); break; case 2: k.recText = a.string(); break; case 3: k.recSwitch = a.int32(); break; default: a.skipType(l & 7) } } return k }; d.decodeDelimited = function (a) { a instanceof y || (a = new y(a)); return this.decode(a, a.uint32()) }; d.verify = function (a) { return "object" !== typeof a || null === a ? "object expected" : null != a.recFlag && a.hasOwnProperty("recFlag") && !r.isInteger(a.recFlag) ? "recFlag: integer expected" : null != a.recText && a.hasOwnProperty("recText") && !r.isString(a.recText) ? "recText: string expected" : null != a.recSwitch && a.hasOwnProperty("recSwitch") && !r.isInteger(a.recSwitch) ? "recSwitch: integer expected" : null }; d.fromObject = function (a) { if (a instanceof p.bilibili.community.service.dm.v1.DanmakuFlagConfig) return a; var b = new p.bilibili.community.service.dm.v1.DanmakuFlagConfig; null != a.recFlag && (b.recFlag = a.recFlag | 0); null != a.recText && (b.recText = String(a.recText)); null != a.recSwitch && (b.recSwitch = a.recSwitch | 0); return b }; d.toObject = function (a, b) { b || (b = {}); var c = {}; b.defaults && (c.recFlag = 0, c.recText = "", c.recSwitch = 0); null != a.recFlag && a.hasOwnProperty("recFlag") && (c.recFlag = a.recFlag); null != a.recText && a.hasOwnProperty("recText") && (c.recText = a.recText); null != a.recSwitch && a.hasOwnProperty("recSwitch") && (c.recSwitch = a.recSwitch); return c }; d.prototype.toJSON = function () { return this.constructor.toObject(this, z.util.toJSONOptions) }; return d }(); e.DmSegMobileReply = function () { function d(a) { this.elems = []; if (a) for (var b = Object.keys(a), c = 0; c < b.length; ++c) null != a[b[c]] && (this[b[c]] = a[b[c]]) } d.prototype.elems = r.emptyArray; d.create = function (a) { return new d(a) }; d.encode = function (a, b) { b || (b = A.create()); if (null != a.elems && a.elems.length) for (var c = 0; c < a.elems.length; ++c) p.bilibili.community.service.dm.v1.DanmakuElem.encode(a.elems[c], b.uint32(10).fork()).ldelim(); return b }; d.encodeDelimited = function (a, b) { return this.encode(a, b).ldelim() }; d.decode = function (a, b) { a instanceof y || (a = y.create(a)); for (var c = void 0 === b ? a.len : a.pos + b, k = new p.bilibili.community.service.dm.v1.DmSegMobileReply; a.pos < c;) { var l = a.uint32(); switch (l >>> 3) { case 1: k.elems && k.elems.length || (k.elems = []); k.elems.push(p.bilibili.community.service.dm.v1.DanmakuElem.decode(a, a.uint32())); break; default: a.skipType(l & 7) } } return k }; d.decodeDelimited = function (a) { a instanceof y || (a = new y(a)); return this.decode(a, a.uint32()) }; d.verify = function (a) { if ("object" !== typeof a || null === a) return "object expected"; if (null != a.elems && a.hasOwnProperty("elems")) { if (!Array.isArray(a.elems)) return "elems: array expected"; for (var b = 0; b < a.elems.length; ++b) { var c = p.bilibili.community.service.dm.v1.DanmakuElem.verify(a.elems[b]); if (c) return "elems." + c } } return null }; d.fromObject = function (a) { if (a instanceof p.bilibili.community.service.dm.v1.DmSegMobileReply) return a; var b = new p.bilibili.community.service.dm.v1.DmSegMobileReply; if (a.elems) { if (!Array.isArray(a.elems)) throw TypeError(".bilibili.community.service.dm.v1.DmSegMobileReply.elems: array expected"); b.elems = []; for (var c = 0; c < a.elems.length; ++c) { if ("object" !== typeof a.elems[c]) throw TypeError(".bilibili.community.service.dm.v1.DmSegMobileReply.elems: object expected"); b.elems[c] = p.bilibili.community.service.dm.v1.DanmakuElem.fromObject(a.elems[c]) } } return b }; d.toObject = function (a, b) { b || (b = {}); var c = {}; if (b.arrays || b.defaults) c.elems = []; if (a.elems && a.elems.length) { c.elems = []; for (var k = 0; k < a.elems.length; ++k) c.elems[k] = p.bilibili.community.service.dm.v1.DanmakuElem.toObject(a.elems[k], b) } return c }; d.prototype.toJSON = function () { return this.constructor.toObject(this, z.util.toJSONOptions) }; return d }(); e.DanmakuElem = function () { function d(a) { if (a) for (var b = Object.keys(a), c = 0; c < b.length; ++c) null != a[b[c]] && (this[b[c]] = a[b[c]]) } d.prototype.id = r.Long ? r.Long.fromBits(0, 0, !1) : 0; d.prototype.progress = 0; d.prototype.mode = 0; d.prototype.fontsize = 0; d.prototype.color = 0; d.prototype.midHash = ""; d.prototype.content = ""; d.prototype.ctime = r.Long ? r.Long.fromBits(0, 0, !1) : 0; d.prototype.weight = 0; d.prototype.action = ""; d.prototype.pool = 0; d.prototype.idStr = ""; d.create = function (a) { return new d(a) }; d.encode = function (a, b) { b || (b = A.create()); null != a.id && Object.hasOwnProperty.call(a, "id") && b.uint32(8).int64(a.id); null != a.progress && Object.hasOwnProperty.call(a, "progress") && b.uint32(16).int32(a.progress); null != a.mode && Object.hasOwnProperty.call(a, "mode") && b.uint32(24).int32(a.mode); null != a.fontsize && Object.hasOwnProperty.call(a, "fontsize") && b.uint32(32).int32(a.fontsize); null != a.color && Object.hasOwnProperty.call(a, "color") && b.uint32(40).uint32(a.color); null != a.midHash && Object.hasOwnProperty.call(a, "midHash") && b.uint32(50).string(a.midHash); null != a.content && Object.hasOwnProperty.call(a, "content") && b.uint32(58).string(a.content); null != a.ctime && Object.hasOwnProperty.call(a, "ctime") && b.uint32(64).int64(a.ctime); null != a.weight && Object.hasOwnProperty.call(a, "weight") && b.uint32(72).int32(a.weight); null != a.action && Object.hasOwnProperty.call(a, "action") && b.uint32(82).string(a.action); null != a.pool && Object.hasOwnProperty.call(a, "pool") && b.uint32(88).int32(a.pool); null != a.idStr && Object.hasOwnProperty.call(a, "idStr") && b.uint32(98).string(a.idStr); return b }; d.encodeDelimited = function (a, b) { return this.encode(a, b).ldelim() }; d.decode = function (a, b) { a instanceof y || (a = y.create(a)); for (var c = void 0 === b ? a.len : a.pos + b, k = new p.bilibili.community.service.dm.v1.DanmakuElem; a.pos < c;) { var l = a.uint32(); switch (l >>> 3) { case 1: k.id = a.int64(); break; case 2: k.progress = a.int32(); break; case 3: k.mode = a.int32(); break; case 4: k.fontsize = a.int32(); break; case 5: k.color = a.uint32(); break; case 6: k.midHash = a.string(); break; case 7: k.content = a.string(); break; case 8: k.ctime = a.int64(); break; case 9: k.weight = a.int32(); break; case 10: k.action = a.string(); break; case 11: k.pool = a.int32(); break; case 12: k.idStr = a.string(); break; default: a.skipType(l & 7) } } return k }; d.decodeDelimited = function (a) { a instanceof y || (a = new y(a)); return this.decode(a, a.uint32()) }; d.verify = function (a) { return "object" !== typeof a || null === a ? "object expected" : null == a.id || !a.hasOwnProperty("id") || r.isInteger(a.id) || a.id && r.isInteger(a.id.low) && r.isInteger(a.id.high) ? null != a.progress && a.hasOwnProperty("progress") && !r.isInteger(a.progress) ? "progress: integer expected" : null != a.mode && a.hasOwnProperty("mode") && !r.isInteger(a.mode) ? "mode: integer expected" : null != a.fontsize && a.hasOwnProperty("fontsize") && !r.isInteger(a.fontsize) ? "fontsize: integer expected" : null != a.color && a.hasOwnProperty("color") && !r.isInteger(a.color) ? "color: integer expected" : null != a.midHash && a.hasOwnProperty("midHash") && !r.isString(a.midHash) ? "midHash: string expected" : null != a.content && a.hasOwnProperty("content") && !r.isString(a.content) ? "content: string expected" : null == a.ctime || !a.hasOwnProperty("ctime") || r.isInteger(a.ctime) || a.ctime && r.isInteger(a.ctime.low) && r.isInteger(a.ctime.high) ? null != a.weight && a.hasOwnProperty("weight") && !r.isInteger(a.weight) ? "weight: integer expected" : null != a.action && a.hasOwnProperty("action") && !r.isString(a.action) ? "action: string expected" : null != a.pool && a.hasOwnProperty("pool") && !r.isInteger(a.pool) ? "pool: integer expected" : null != a.idStr && a.hasOwnProperty("idStr") && !r.isString(a.idStr) ? "idStr: string expected" : null : "ctime: integer|Long expected" : "id: integer|Long expected" }; d.fromObject = function (a) { if (a instanceof p.bilibili.community.service.dm.v1.DanmakuElem) return a; var b = new p.bilibili.community.service.dm.v1.DanmakuElem; null != a.id && (r.Long ? (b.id = r.Long.fromValue(a.id)).unsigned = !1 : "string" === typeof a.id ? b.id = parseInt(a.id, 10) : "number" === typeof a.id ? b.id = a.id : "object" === typeof a.id && (b.id = (new r.LongBits(a.id.low >>> 0, a.id.high >>> 0)).toNumber())); null != a.progress && (b.progress = a.progress | 0); null != a.mode && (b.mode = a.mode | 0); null != a.fontsize && (b.fontsize = a.fontsize | 0); null != a.color && (b.color = a.color >>> 0); null != a.midHash && (b.midHash = String(a.midHash)); null != a.content && (b.content = String(a.content)); null != a.ctime && (r.Long ? (b.ctime = r.Long.fromValue(a.ctime)).unsigned = !1 : "string" === typeof a.ctime ? b.ctime = parseInt(a.ctime, 10) : "number" === typeof a.ctime ? b.ctime = a.ctime : "object" === typeof a.ctime && (b.ctime = (new r.LongBits(a.ctime.low >>> 0, a.ctime.high >>> 0)).toNumber())); null != a.weight && (b.weight = a.weight | 0); null != a.action && (b.action = String(a.action)); null != a.pool && (b.pool = a.pool | 0); null != a.idStr && (b.idStr = String(a.idStr)); return b }; d.toObject = function (a, b) { b || (b = {}); var c = {}; if (b.defaults) { if (r.Long) { var k = new r.Long(0, 0, !1); c.id = b.longs === String ? k.toString() : b.longs === Number ? k.toNumber() : k } else c.id = b.longs === String ? "0" : 0; c.progress = 0; c.mode = 0; c.fontsize = 0; c.color = 0; c.midHash = ""; c.content = ""; r.Long ? (k = new r.Long(0, 0, !1), c.ctime = b.longs === String ? k.toString() : b.longs === Number ? k.toNumber() : k) : c.ctime = b.longs === String ? "0" : 0; c.weight = 0; c.action = ""; c.pool = 0; c.idStr = "" } null != a.id && a.hasOwnProperty("id") && (c.id = "number" === typeof a.id ? b.longs === String ? String(a.id) : a.id : b.longs === String ? r.Long.prototype.toString.call(a.id) : b.longs === Number ? (new r.LongBits(a.id.low >>> 0, a.id.high >>> 0)).toNumber() : a.id); null != a.progress && a.hasOwnProperty("progress") && (c.progress = a.progress); null != a.mode && a.hasOwnProperty("mode") && (c.mode = a.mode); null != a.fontsize && a.hasOwnProperty("fontsize") && (c.fontsize = a.fontsize); null != a.color && a.hasOwnProperty("color") && (c.color = a.color); null != a.midHash && a.hasOwnProperty("midHash") && (c.midHash = a.midHash); null != a.content && a.hasOwnProperty("content") && (c.content = a.content); null != a.ctime && a.hasOwnProperty("ctime") && (c.ctime = "number" === typeof a.ctime ? b.longs === String ? String(a.ctime) : a.ctime : b.longs === String ? r.Long.prototype.toString.call(a.ctime) : b.longs === Number ? (new r.LongBits(a.ctime.low >>> 0, a.ctime.high >>> 0)).toNumber() : a.ctime); null != a.weight && a.hasOwnProperty("weight") && (c.weight = a.weight); null != a.action && a.hasOwnProperty("action") && (c.action = a.action); null != a.pool && a.hasOwnProperty("pool") && (c.pool = a.pool); null != a.idStr && a.hasOwnProperty("idStr") && (c.idStr = a.idStr); return c }; d.prototype.toJSON = function () { return this.constructor.toObject(this, z.util.toJSONOptions) }; return d }(); return e }(); return n }(); return m }(); return h }(); return w }(); return p })(protobuf); let proto_seg = protobuf.roots["default"].bilibili.community.service.dm.v1.DmSegMobileReply; function server() { let videoPublishDate = null let [downloadDanmaku, downloadDanmakuToZip, allProtobufDanmu, concatDanmaku] = (function () { function htmlEscape(text, skipQuot) { if (!text) return text text = text.replace(/[\x00-\x08\x0b-\x0c\x0e-\x1f\ufffe\uffff]/g, ' ') function fn(match, pos, originalText) { switch (match) { case "<": return "<"; case ">": return ">"; case "&": return "&"; case "\"": return '"'; } } if (!skipQuot) { return text.replace(/[<>"&]/g, fn); } else { return text.replace(/[<>&]/g, fn); } } async function loadProtoDanmu(url, timeout = null, header = null, retry = 0) { if (header === null) { header = { "referer": 'https://www.bilibili.com/bangumi/play/ep790784', origin: 'https://www.bilibili.com', 'sec-fetch-site': 'same-site' } } while (true) { try { let result = await new Promise((resolve) => { GM_xmlhttpRequest({ method: 'GET', url: url, responseType: 'arraybuffer', headers: header, timeout: timeout || 30000, withCredentials: true, onload: (response) => { if (response.status === 200) { let lpdanmu; try { lpdanmu = proto_seg.decode(new Uint8Array(response.response)); } catch (e) { if (response.responseText.indexOf('-101') !== -1) { alert("历史弹幕下载失败. 请检查该浏览器中B站是否未登录账号\n" + response.responseText) window.location.href = "https://space.bilibili.com/0" } console.log('XhrError=', retry); if (retry < 3) { return resolve(loadProtoDanmu(url, timeout, header, retry + 1)); } else { return resolve(null); } } try { lpdanmu.elems.forEach((e) => { if (!e.progress) e.progress = 0; }); resolve(lpdanmu.elems); } catch (e) { console.log(e.stack); resolve([]); } } else if (response.status === 304) { resolve([]); } else { console.log(response.status, response); resolve(null); } }, onerror: (error) => { console.log('XhrError=', retry); retry += 1; if (retry > 3) { setTimeout(() => resolve(null), 10 * 1000); } else { setTimeout(() => resolve(null), retry * 2 * 1000); } }, }); }); if (pageSetting.suspendDownload) { return result } if (result === null) { retry += 1; } else { return result; } } catch (e) { console.log('XhrError=', retry); retry += 1; } await sleep(1000) if (retry > 3) { setTimeout(() => resolve(null), 10 * 1000); } else { setTimeout(() => resolve(null), retry * 2 * 1000); } } } function savedanmuStandalone(ldanmu, info = null) { var end, head; head = "<?xml version=\"1.0\" encoding=\"UTF-8\"?><i><chatserver>chat.bilibili.com</chatserver><chatid>0</chatid><mission>0</mission><maxlimit>0</maxlimit><state>0</state><real_name>0</real_name><source>DF</source>" end = "</i>"; if (info) { head += '<info>' + htmlEscape(JSON.stringify(info), true) + '</info>'; } return head + ldanmu.join('') + end } function danmuObject2XML(ldanmu) { for (let i = 0, length = ldanmu.length; i < length; i++) { let danmu = ldanmu[i] ldanmu[i] = `<d p="${(danmu.progress ? danmu.progress : 0) / 1000},${danmu.mode},${danmu.fontsize},${danmu.color},${danmu.ctime},${0},${danmu.midHash},${danmu.id}">${htmlEscape(danmu.content)}</d>` } return ldanmu } async function moreHistory(cid) { let date = new Date(); if (videoPublishDate && currentSetting.capturePeriodEnd !== -1) { date.setTime((videoPublishDate + currentSetting.capturePeriodEnd * 86400) * 1000) } else { date.setTime(date.getTime() - 86400000) } console.log('GetDanmuFor CID' + cid) let aldanmu = [], ldanmu = [] let firstdate = 0; let ndanmu, ondanmu let url = 'https://comment.bilibili.com/' + cid + '.xml' let sdanmu = await xhrGet(url) ondanmu = ndanmu = Number(/<maxlimit>(.*?)</.exec(sdanmu)[1]) while (true) { if (firstdate !== 0) { await sleep(2000) } if (firstdate === 0 || ldanmu.length >= Math.min(ondanmu, 5000) * 0.5) { let url = "https://api.bilibili.com/x/v2/dm/web/history/seg.so?type=1&date=" + dateObjectToDateStr(date) + "&oid=" + cid.toString(); console.log('ndanmu:', aldanmu.length, dateObjectToDateStr(date), url); ldanmu = await loadProtoDanmu(url) } if (ldanmu !== null) { aldanmu = mergeDanmu(aldanmu, ldanmu) } if (pageSetting.suspendDownload) { return [aldanmu, ondanmu] } document.title = aldanmu.length.toString() toastText(dateObjectToDateStr(date) + '/' + aldanmu.length) if (ldanmu.length < Math.min(ondanmu, 5000) * 0.5) { return [aldanmu, ondanmu] } if (ldanmu.length >= Math.min(ondanmu, 5000) * 0.5) { let tfirstdate = getMinDate(ldanmu) if (firstdate !== 0 && firstdate - tfirstdate < 86400) tfirstdate = firstdate - 86400; firstdate = tfirstdate; date.setTime(firstdate * 1000); } if (videoPublishDate && currentSetting.capturePeriodStart !== 0) { if (videoPublishDate + currentSetting.capturePeriodStart > date.getTime() / 1000) { return [aldanmu, ondanmu] } } } } function timestampToFullDateStr(timestamp) { const date = new Date(timestamp * 1000); // Convert seconds to milliseconds const year = date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, '0'); // Month is zero-indexed const day = date.getDate().toString().padStart(2, '0'); const hour = date.getHours().toString().padStart(2, '0'); const minute = date.getMinutes().toString().padStart(2, '0'); const second = date.getSeconds().toString().padStart(2, '0'); return `${year}-${month}-${day}-${hour}-${minute}-${second}`; } function dateObjectToDateStr(date) { const year = date.getFullYear(); const month = (date.getMonth() + 1).toString().padStart(2, '0'); // Month is zero-indexed const day = date.getDate().toString().padStart(2, '0'); return `${year}-${month}-${day}`; } function getMinDate(ldanmu) { let minDate = ldanmu[0].ctime for (let danmu of ldanmu) { if (minDate > danmu.ctime) { minDate = danmu.ctime } } return minDate } function mergeDanmu(oldanmu, nldanmu) { if (oldanmu.idPool === undefined) { let idPool = new Set() for (let danmu of oldanmu) { try { idPool.add(danmu.progress * danmu.content.length * parseInt(danmu.midHash, 16)) } catch (e) { console.log(danmu) console.log(e) throw e } } oldanmu.idPool = idPool } try { for (let danmu of nldanmu) { let ida = (danmu.progress ? danmu.progress : 1) * danmu.content.length * parseInt(danmu.midHash, 16) if (!oldanmu.idPool.has(ida)) { oldanmu.push(danmu) oldanmu.idPool.add(ida) } } } catch (e) { console.log() } return oldanmu } function poolSize2Duration(poolSize) { let lPoolSize = [[0, 100], [30, 300], [60, 500], [180, 1000], [600, 1500], [900, 3000], [1500, 4000], [2400, 6000], [3600, 8000],] for (let i = 0; i < lPoolSize.length; i += 1) { if (poolSize === lPoolSize[i][1]) { return lPoolSize[i][0] } } } async function allProtobufDanmu(cid, duration) { toastText("实时弹幕:", cid) let segIndex = 0, aldanmu = [] while (true) { segIndex += 1 let tldanmu = await loadProtoDanmu('https://api.bilibili.com/x/v2/dm/web/seg.so?type=1&oid=' + cid + '&segment_index=' + segIndex) mergeDanmu(aldanmu, tldanmu) toastText(aldanmu.length) if ((!duration || segIndex * 360 > duration) && (!tldanmu || tldanmu.length === 0)) { break } await sleep(500) } toastText('下载完成') return aldanmu } async function allProtobufDanmuXml(cid, title, ndanmu) { let result = new DownloadResult(await allProtobufDanmu(cid, poolSize2Duration(ndanmu))) downloadFile(validateTitle(title) + '.xml', result.toXml()) } async function downloadDanmaku(cid, info) { toastText("全弹幕:" + cid) if (currentSetting.capturePeriodStart !== 0 || currentSetting.capturePeriodEnd !== -1) { toastText("下载时段:第" + currentSetting.capturePeriodStart + '-' + currentSetting.capturePeriodEnd + '天') toastText(videoPublishDate ? "视频发布日期:" + dateObjectToDateStr(new Date(videoPublishDate * 1000)) : "未知日期:由弹幕判断") } let [ldanmu, ndanmu] = await moreHistory(cid) if (!info) { info = {} } info.cid = cid info.ndanmu = ndanmu if (!pageSetting.suspendDownload) { // if (ldanmu.length > ndanmu * 2 || ((currentSetting.capturePeriodStart !== 0 || currentSetting.capturePeriodEnd !== -1) && ldanmu.length > ndanmu * 0.8)) { let sldanmu = await allProtobufDanmu(cid, poolSize2Duration(ndanmu)) mergeDanmu(ldanmu, sldanmu) // } if (currentSetting.capturePeriodStart !== 0 || currentSetting.capturePeriodEnd !== -1) { let publishDate = videoPublishDate || getMinDate(ldanmu) let start = publishDate + currentSetting.capturePeriodStart * 86400 - 1 let end = currentSetting.capturePeriodEnd > 0 ? publishDate + currentSetting.capturePeriodEnd * 86400 : 1e12 console.log('before', ldanmu.length) ldanmu = ldanmu.filter(danmu => { return danmu.ctime > start && danmu.ctime < end }) console.log('after', ldanmu.length) } toastText('下载完成') return new DownloadResult(ldanmu, info) } else { toastText('下载中断, 下载临时弹幕') info.isSuspend = true return new DownloadResult(ldanmu, info) } } class DownloadResult { constructor(ldanmu, info) { this.ldanmu = ldanmu this.info = info } toXml(sortByProgress = true) { if (sortByProgress) { this.ldanmu.sort((a, b) => { return a.progress - b.progress }) } return savedanmuStandalone(danmuObject2XML(this.ldanmu), this.info) } dumpFile(title) { downloadFile(validateTitle(title) + '.xml', this.toXml()) } splitByTime(folder) { this.ldanmu.sort((a, b) => { return a.ctime - b.ctime }) let i = 0 const chunkSize = 3000 while (i * chunkSize < this.ldanmu.length) { let tldanmu = this.ldanmu.slice(i * chunkSize, (i + 1) * chunkSize) let lastTs = tldanmu[tldanmu.length - 1].ctime let fileName = timestampToFullDateStr(lastTs) + '_' + this.info.cid + '.xml' folder.file(fileName, new DownloadResult(tldanmu, this.info).toXml(false)) i += 1 } } } async function downloadDanmakuToZip(cid, folder, partTitle, partInfo, spiltFileFlag) { let result = await downloadDanmaku(cid, '', partInfo) if (!spiltFileFlag) { let sdanmu = result.toXml() await sleep(1); folder.file(partTitle + '.xml', sdanmu) await sleep(1); } else { let partFolder = folder.folder(partTitle) result.splitByTime(partFolder) } } async function concatDanmaku(videoInfo) { let aldanmu = [] let posOffset = 0 for (let partInfo of videoInfo.list) { let ldanmu = await allProtobufDanmu(partInfo.cid, partInfo.duration - partInfo.backBlack ?? 0) if (partInfo.backBlack) { ldanmu = ldanmu.filter((danmu) => { return danmu.progress < (partInfo.duration - partInfo.backBlack ?? 0) * 1000 }) } if (partInfo.frontBlack) { ldanmu.forEach((danmu) => { danmu.progress -= (partInfo.frontBlack ?? 0) * 1000 }) ldanmu = ldanmu.filter((danmu) => { return danmu.progress > 0 }) } if (partInfo.partOffset) { posOffset = partInfo.partOffset } ldanmu.forEach((danmu) => { danmu.progress = danmu.progress + posOffset * 1000 }) posOffset += partInfo.duration - (partInfo.backBlack ?? 0) - (partInfo.frontBlack ?? 0) aldanmu = aldanmu.concat(ldanmu) } let result = new DownloadResult(aldanmu, videoInfo) downloadFile( `av${videoInfo.id} ${validateTitle(videoInfo.title)} P${videoInfo.list[0].page} ${validateTitle(videoInfo.list[0].part ?? videoInfo.list[0].title ?? '')}.xml`, result.toXml() ) } return [downloadDanmaku, downloadDanmakuToZip, allProtobufDanmuXml, concatDanmaku ] })(); let downloadDanmakuVideo = (() => { return async function (videoInfo) { let zip = new JSZip(); let title let aid if (typeof videoInfo['aid'] === 'number') { aid = videoInfo['aid'] title = 'av' + videoInfo['aid'] + ' ' + validateTitle(videoInfo['title']) } else { title = videoInfo['aid'] + ' ' + validateTitle(videoInfo['title']) aid = Number(videoInfo['aid'].substring(2)) } title = validateTitle(title) console.log('title= ' + title) let folder = zip.folder(title) folder.file('videoInfo.json', JSON.stringify(videoInfo)) let i = 0 if (!videoInfo.isCache && (typeof videoInfo['aid'] === 'number' || videoInfo['aid'].startsWith('av'))) { try { let pageList = JSON.parse(await xhrGet(`https://api.bilibili.com/x/player/pagelist?aid=${aid}&jsonp=jsonp`)) if (pageList.code === 0) { for (let page of pageList['data']) { let matched = false for (let part of videoInfo['list']) { if (part.cid === page.cid) { part.duration = page.duration matched = true break } } if (!matched) { videoInfo['list'].push(page) } } } } catch (e) { console.error(e, e.stack) } } if (videoInfo.videoPublishDate) { videoPublishDate = videoInfo.videoPublishDate } for (let partInfo of videoInfo['list']) { i += 1 partInfo.title = partInfo.part partInfo.aid = aid let partTitle = getPartTitle(partInfo) if (partInfo.videoPublishDate) { videoPublishDate = partInfo.videoPublishDate } toastText(partTitle) let progress = (i * 100 / videoInfo['list'].length).toFixed(2) document.title = progress + ' %' await downloadDanmakuToZip(partInfo.cid, folder, partTitle, partInfo, currentSetting['splitFileByTime']) broadcastChannel.postMessage({ type: 'cidComplete', cid: partInfo.cid, aid: videoInfo.aid, progress: progress, }, '*'); if (partInfo.videoPublishDate) { videoPublishDate = null } } let result = await zip.generateAsync({ type: "blob", compression: "DEFLATE", compressionOptions: { level: 9 } }) videoPublishDate = null downloadFile(title + '.zip', result); } })() let downloadedCid = [] let downloadedAid = [] let downloadingVideo = [] broadcastChannel.addEventListener('message', async (e) => { if (e.data.type === 'biliplusDownloadDanmaku') { if (e.data.history !== false) { if (downloadedCid.indexOf(e.data.cid) !== -1) { return } downloadedCid.push(e.data.cid); (await downloadDanmaku(e.data.cid, e.data)).dumpFile(e.data.title) } else { await allProtobufDanmu(e.data.cid, e.data.title, e.data.ndanmu) } } if (e.data.type === 'biliplusDownloadDanmakuVideo') { if (downloadedAid.indexOf(e.data.videoInfo.aid) !== -1) { broadcastChannel.postMessage({type: 'aidDownloaded', aid: e.data.videoInfo.aid}, '*'); return } downloadedAid.push(e.data.videoInfo.aid) if (downloadingVideo.length === 0) { downloadingVideo.push(e.data.videoInfo) while (downloadingVideo.length !== 0) { let videoInfo = downloadingVideo[0] console.log('start', videoInfo.aid) broadcastChannel.postMessage({type: 'aidStart', aid: videoInfo.aid}, '*'); await new Promise((resolve) => setTimeout(resolve, 1)); await downloadDanmakuVideo(videoInfo) document.title = '下载完成' broadcastChannel.postMessage({type: 'aidComplete', aid: videoInfo.aid}, '*'); await sleep(1000) downloadingVideo = downloadingVideo.slice(1) } } else { console.log('wait', e.data.videoInfo.aid) downloadingVideo.push(e.data.videoInfo) } } if (e.data.type === 'concatDanmaku') { await concatDanmaku(e.data.videoInfo) } }); } sleep(200).then(() => { if (new URL(window.location.href).searchParams.has("get_info")) { aidQuery(undefined, true) } }) const broadcastChannel = new CustomEventEmitter() client() let currentSetting = panel() let pageSetting = { frontBlack: undefined, backBlack: undefined, suspendDownload: false } server()