Skip to content

Instantly share code, notes, and snippets.

@CL-Jeremy
Last active September 15, 2020 20:40
Show Gist options
  • Save CL-Jeremy/2240480d24df3b0f829c6cde50264e39 to your computer and use it in GitHub Desktop.
Save CL-Jeremy/2240480d24df3b0f829c6cde50264e39 to your computer and use it in GitHub Desktop.
Load external ass subtitle hosted with WsgiDAV 3.0.x for Bilibili's HTML5 player
// ==UserScript==
// @name Load external ass subtitle for Bilibili
// @version 0.2.2.3
// @include /:\/\/www\.bilibili\.com\/video\/(BV|[ab]v).*/
// @require https://code.jquery.com/jquery-1.12.4.min.js
// @require https://cdn.jsdelivr.net/gh/CL-Jeremy/ASS@next/dist/ass.min.js
// @require https://cdn.jsdelivr.net/npm/css-element-queries/src/ResizeSensor.js
// @grant GM_addStyle
// ==/UserScript==
(() => {
const domain = 'https://example.tld' // edit here: <protocol>://<domain>
const danmakuRepository = '/' // edit here: a directory (with trailing slash) on WsgiDAV containing
// ass files of names like: av<avid>-<...>_comments.ass
GM_addStyle('\
.ASS-dialogue > span {\
transform: scale(0.83);\
-webkit-transform: scale(0.83);\
-moz-osx-font-smoothing: grayscale;\
}')
const fullyLoaded = (callback) => {
if (document.readyState === 'complete') {
callback()
} else {
unsafeWindow.addEventListener('load', callback)
}
}
const actionObserver = (callback, filter) => new MutationObserver((mutations) => {
mutations.forEach((mutationRecord) => {
if (filter === undefined || filter(mutationRecord)) callback()
})
})
fullyLoaded(() => {
let ass
const bilibiliPlayerVideo = $('.bilibili-player-video')
const OpacitySlider = '.bilibili-player-setting-opacity'
const SettingsPopup = '.bilibili-player-video-danmaku-setting'
const PlayerBottom = '.bilibili-player-video-bottom-area'
const DanmakuSwitch = '.bilibili-player-video-danmaku-switch'
const updateVisibility = () => $('.choose_danmaku').text().match(/^关闭/) ? ass.show() : ass.hide()
const updateOpacity = () => $('.ASS-stage').css('opacity',
(($(OpacitySlider).find('.bui-bar').css('transform') || '').match(/\(([\d\.]*)/) ||
localStorage.bilibili_player_settings.match(/"opacity":"(.+?)"/)
)[1])
fetch(domain + danmakuRepository)
.then(res => res.text())
.then((text) => {
$($.parseHTML(text))
.filter('table')
.find('a')
.each((i, el) => {
const file = $.trim(el.href)
if (file.match(new RegExp(String.raw`av${$('meta[itemprop="url"]').attr('content').match(/\/video\/av(\d+)/)[1]}.*_comments\.ass$`))) {
fetch(file.replace(new RegExp(String.raw`^.*(${location.hostname})?${danmakuRepository}`), domain + danmakuRepository))
.then(res => res.text())
.then((text) => {
ass = new ASS(text, bilibiliPlayerVideo.find('video')[0], {
container: bilibiliPlayerVideo[0],
resampling: 'video-width'
})
new ResizeSensor(bilibiliPlayerVideo[0], () => ass.resize())
new ResizeSensor(bilibiliPlayerVideo[0], () => {
bilibiliPlayerVideo.attr('style', 'width: 100%; height: 100%;')
if ($('#bilibiliPlayer')[0].className.match(/fullscreen|mini/))
bilibiliPlayerVideo.find('video').attr('style', 'width: 100%; height: 100%;')
ass.resize()
updateOpacity()
})
const playerBottomObserver = actionObserver((mutations) => {
$(SettingsPopup).mouseover(() => {
const observer = actionObserver(updateOpacity);
observer.observe(
$(OpacitySlider).find('.bui-bar')[0],
{ attributes : true, attributeFilter : ['style'] }
)
$(SettingsPopup).mouseout(() => { observer.disconnect() })
})
var observeVisibility = () => { var o = actionObserver(() => { updateVisibility(); o.disconnect() }); return o }
$(DanmakuSwitch).click(() => observeVisibility().observe($('.choose_danmaku')[0], { childList: true }))
observeVisibility().observe($(DanmakuSwitch)[0], { childList: true })
$(DanmakuSwitch).append($('<span class="choose_danmaku">' + (
$(DanmakuSwitch).find('.bui-switch-dot').css('margin-left') !== '-18px' ? '开启': '关闭'
) + '弹幕</span>'))
observeVisibility().observe($('.choose_danmaku')[0], { childList: true })
playerBottomObserver.disconnect()
}, (mutationRecord) => {
if ($(DanmakuSwitch).find('.bui-switch-label')[0]) return true
for (const node of mutationRecord.addedNodes.values()) {
if (node.className === 'bui-switch-label') return true
}
return false
})
playerBottomObserver.observe($(PlayerBottom)[0], { childList: true, subtree: true })
})
}
})
})
})
})()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment