// ==UserScript== // @name Bilidown联动脚本[音频mp3🎵,视频mp4📹,弹幕xml,ass🌴,封面🌾] // @namespace http://tampermonkey.net/ // @version 0.2.8 // @description 脚本负责获取音频,工具负责视频封面弹幕等等,相互联动,互相增强,好耶ヽ(✿゚▽゚)ノ // @author 王子周棋洛 // @match https://www.bilibili.com/video/* // @icon data:image/webp;base64,UklGRkoEAABXRUJQVlA4WAoAAAAwAAAAPwAAPwAASUNDUMgBAAAAAAHIAAAAAAQwAABtbnRyUkdCIFhZWiAH4AABAAEAAAAAAABhY3NwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAA9tYAAQAAAADTLQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAlkZXNjAAAA8AAAACRyWFlaAAABFAAAABRnWFlaAAABKAAAABRiWFlaAAABPAAAABR3dHB0AAABUAAAABRyVFJDAAABZAAAAChnVFJDAAABZAAAAChiVFJDAAABZAAAAChjcHJ0AAABjAAAADxtbHVjAAAAAAAAAAEAAAAMZW5VUwAAAAgAAAAcAHMAUgBHAEJYWVogAAAAAAAAb6IAADj1AAADkFhZWiAAAAAAAABimQAAt4UAABjaWFlaIAAAAAAAACSgAAAPhAAAts9YWVogAAAAAAAA9tYAAQAAAADTLXBhcmEAAAAAAAQAAAACZmYAAPKnAAANWQAAE9AAAApbAAAAAAAAAABtbHVjAAAAAAAAAAEAAAAMZW5VUwAAACAAAAAcAEcAbwBvAGcAbABlACAASQBuAGMALgAgADIAMAAxADZBTFBIoAAAAAFfoDaSDTr9kEn/UrmORERgqJdeT4zgpta2N3kpvwT2vncJ2etDlRAJkRAdkdAAefX7doaI/k+AJK1zTKOR3ieYT97EcH+aVhBe4GkOkmI6u5IgUT8dr2hK51EyjHm6EuLG5xYyr0N+96kins78P8p7nyrKGp/bIfYKMT6EKZ1HwVDUHlekmM6uIEgCO5CkGHqbCsKLUsxXer/ObaKRJAFWUDggtAEAAFAMAJ0BKkAAQAA/gbrNXrYxKSaoGAyqwDAJQBqv5i4QYRFzT/ewe1mJ22KucUYMTsK3jwwkf8stqz7QBvQGk6vMjaTmq2ViEiSLAR1tyBqGQgiVl9OQAsS9pi/vVcuzN4zM37Byu/vzBSbAANtKZ5uayFBaFvRGroBkEeijKjuBHPFsLGenK/cmNDrTzifuCSIJ/7i/iN3mTpvr+VDTxfR+AndcIgd4ITIALC+kAtGhEY+84lcBrJGGTL8sctYdjSwyxEymZswKkLHIQau61+hOR8OfHc9zjTsE1th9tHAMR4HsE2DBQYVAXK74u6HvEXONvCPNf+Ah+DAPYogluF+in3FCd0ceF2p704DGdxYqBDrVldeuslKulMg8w50VpbgiNKl4kAQ18A4yjKYOMrTZuNCp11UNCtSvjM9tCe1GXxDQY3fuAy24FtQaL12glbmxTCFblu/ZpuLBTgSCu1LCzsOLWdEZQAgrnNmoJkd4DzQjKGVrf4d1lQJiEfYQUxaBrK6syg07hhCi2hajnkjUg/XGWBJiQX5OvF7FLrdmlLZeCOW3tese1rensTisK4VgAAA= // @grant none // @run-at document-end // @license MIT // @downloadURL none // ==/UserScript== (function () { let audioState = 'pending' let my_xhr = null const currentVersion = GM_info.script.version let remoteVersion = null const updateUrl = 'https://update.greasyfork.icu/scripts/459377/Bilidown%E8%81%94%E5%8A%A8%E8%84%9A%E6%9C%AC%5B%E9%9F%B3%E9%A2%91mp3%F0%9F%8E%B5%EF%BC%8C%E8%A7%86%E9%A2%91mp4%F0%9F%93%B9%EF%BC%8C%E5%BC%B9%E5%B9%95xml%EF%BC%8Cass%F0%9F%8C%B4%EF%BC%8C%E5%B0%81%E9%9D%A2%F0%9F%8C%BE%5D.meta.js' const localStorageMap = { '封面': 'bilidown_script_cover', '音频': 'bilidown_script_audio', '视频': 'bilidown_script_video', 'bilidown客户端': 'bilidown_script_launch' } let checkElExist = (c) => document.querySelector(`.${c}`) let chooseEl = () => document.querySelectorAll('.video-info-detail-list') || document.querySelectorAll('.video-data') let renderBtn = () => Object.keys(localStorageMap).forEach(k => renderBtnLogic(`bilidown_script_btn bilidown_script_${k}_btn`, k)) let mount = (el, containers) => containers.forEach(item => item.appendChild(el)) let updateCheck = async () => { try { const re = await fetch(updateUrl) const text = await re.text() if (!text) return false let arr = text.split('\n') for (let i = 0; i < arr.length; i++) { if (!arr[i].includes('@version')) continue remoteVersion = arr[i].split('version')[1].trim() if (currentVersion != remoteVersion) return true } return false } catch (e) { return false } } let toggleDialog = () => { let dialog = document.querySelector('.bilidown_script_system_dialog') dialog.style.display = dialog.style.display === 'block' ? 'none' : 'block' if (dialog.style.display === 'block') echoDialogChecked() } let echoDialogChecked = () => { Object.values(localStorageMap).forEach((v, i) => { let value = JSON.parse(localStorage.getItem(v)) if (value && value === true) { document.querySelectorAll('.bilidown_script_system__dialog__content label input')[i].checked = true } }) } let removeAllBtn = () => { Object.keys(localStorageMap).forEach(k => { let target = document.querySelector(`.bilidown_script_${k}_btn`) if (target) target.remove() }) } let renderBtnLogic = (className, text) => { let key = JSON.parse(localStorage.getItem(localStorageMap[text])) if (key && key === true) { let btn = document.createElement('button') registerEvent(btn, text) btn.textContent = text btn.className = className mount(btn,chooseEl()) } } let registerEvent = (btn, text) => { if (!localStorageMap.hasOwnProperty(text)) return btn.addEventListener('click', e => { if (text === '封面') { window.open(__INITIAL_STATE__.videoData.pic, '_blank') } else if (text === '音频') { e.preventDefault() if (audioState === 'pending') { audioState = 'active' const url = `https://api.bilibili.com/x/player/playurl?avid=${__INITIAL_STATE__.aid}&bvid=${__INITIAL_STATE__.bvid}&cid=${__INITIAL_STATE__.videoData.cid}&fnval=4048` fetch(url).then(resp => resp.json()).then(i => { my_xhr = new XMLHttpRequest() my_xhr.responseType = 'blob' my_xhr.open('GET', i.data.dash.audio[0].base_url, true) my_xhr.onprogress = event => btn.textContent = `下载中 ${parseInt((event.loaded / event.total) * 100)}%` my_xhr.onload = () => { if (my_xhr.status === 200) { const reader = new FileReader() reader.readAsDataURL(my_xhr.response) reader.onload = e => { const a = document.createElement('a') a.download = `${document.querySelector(".video-title").textContent || "Hello World"}.mp3` a.href = e.target.result document.documentElement.appendChild(a) a.click() a.remove() my_xhr = null resetAudioAbout(btn) } } } my_xhr.onerror = () => resetAudioAbout(btn) my_xhr.onabort = () => resetAudioAbout(btn) my_xhr.ontimeout = () => resetAudioAbout(btn) my_xhr.send() }) } else { my_xhr.abort() btn.textContent = '已取消下载' setTimeout(() => resetAudioAbout(btn), 1000) } } else if (text === '视频') { location.href && window.open(`http://zhouql.vip/bilibili/?${location.href}`, '_blank') } else if (text === 'bilidown客户端') { location.href && window.open(`bilidown://parser?link=${location.href}`) } }) } let resetAudioAbout = (btn) => { btn.textContent = '音频' audioState = 'pending' } let injectStyle = (className, css) => { if (checkElExist(className)) return let target = document.querySelector(`.${className}`) if (target) return let style = document.createElement('style') style.className = className style.innerText = css document.head.appendChild(style) } let renderDialog = () => { if (checkElExist('bilidown_script_system_dialog')) return // create let target = document.createElement('div') target.className = 'bilidown_script_system_dialog' target.innerHTML = `