Bilibili-Evolved/src/video/download-danmaku.ts
2020-03-14 13:58:24 +08:00

160 lines
5.4 KiB
TypeScript

import { getFriendlyTitle } from './title'
import { DanmakuInfo } from './video-info'
import { DanmakuConverter } from './danmaku-converter/danmaku-converter'
export async function convertToAss(xml: string) {
const title = getFriendlyTitle()
let config: any = { title }
try {
await loadDanmakuSettingsPanel()
const getSliderFactor = (selector: string) => {
const transform = parseFloat((dq(selector) as HTMLElement).style.transform!.replace(/translateX\(([\d\.]+)/, '$1'))
return transform * 4 / 188
}
const getSliderIndex = (selector: string) => {
const transform = parseFloat((dq(selector) as HTMLElement).style.transform!.replace(/translateX\(([\d\.]+)/, '$1')) as 0 | 44 | 94 | 144 | 188
const index = {
0: 0,
44: 1,
94: 2,
144: 3,
188: 4
}[transform]
return index
}
config.font = (dq('.bilibili-player-video-danmaku-setting-right-font .bui-select-result') as HTMLElement).innerText
config.alpha = 1 - parseFloat((dq('.bilibili-player-setting-opacity .bui-bar') as HTMLElement).style.transform!.replace(/scaleX\(([\d\.]+)\)/, '$1'))
config.duration = (() => {
const scrollDuration = 18 - 3 * getSliderFactor('.bilibili-player-setting-speedplus .bui-thumb')
return (danmaku: { type: number }) => {
switch (danmaku.type) {
case 4:
case 5:
return 4 // stickyDuration
default:
return scrollDuration
}
}
})()
config.blockTypes = (() => {
let result: (number | string)[] = []
const blockValues = {
'.bilibili-player-block-filter-type[ftype=scroll]': [1, 2, 3],
'.bilibili-player-block-filter-type[ftype=top]': [5],
'.bilibili-player-block-filter-type[ftype=bottom]': [4],
'.bilibili-player-block-filter-type[ftype=color]': ['color']
// ".bilibili-player-block-filter-type[ftype=special]": [7, 8],
}
for (const [type, value] of Object.entries(blockValues)) {
if ((dq(type) as HTMLElement).classList.contains('disabled')) {
result = result.concat(value)
}
}
return result.concat(7, 8)
})()
const resolutionFactor = 1.4 - 0.2 * getSliderFactor('.bilibili-player-setting-fontsize .bui-thumb') // 改变分辨率来调整字体大小
config.resolution = {
x: 1920 * resolutionFactor,
y: 1080 * resolutionFactor
}
config.bottomMarginPercent = [0.75, 0.5, 0.25, 0, 0][getSliderIndex('.bilibili-player-setting-area .bui-thumb')]
if (config.bottomMarginPercent === 0 && (dq('.bilibili-player-video-danmaku-setting-left-preventshade input') as HTMLInputElement).checked) // 无显示区域限制时要检查是否开启防挡字幕
{
config.bottomMarginPercent = 0.15
}
config.bold = (dq('.bilibili-player-video-danmaku-setting-right-font-bold input') as HTMLInputElement).checked
} catch (error) {
// The default config
config = {
font: '微软雅黑',
alpha: 0.4,
duration: (danmaku: { type: number }) => {
switch (danmaku.type) {
case 4:
case 5:
return 4
default:
return 6
}
},
blockTypes: [7, 8],
resolution: {
x: 1920,
y: 1080
},
bottomMarginPercent: 0.15,
bold: false
}
}
const converter = new DanmakuConverter(config)
const assDocument = converter.convertToAssDocument(xml)
return assDocument.generateAss()
}
export async function downloadDanmaku(ass: boolean) {
const title = getFriendlyTitle()
const danmaku = new DanmakuInfo((unsafeWindow || window).cid!)
await danmaku.fetchInfo()
const blob = await (async () => {
if (ass === true) {
return new Blob([await convertToAss(danmaku.rawXML)], {
type: 'text/plain'
})
} else {
return new Blob([danmaku.rawXML], {
type: 'text/plain'
})
}
})()
const url = URL.createObjectURL(blob)
const link = dq('#danmaku-link') as HTMLAnchorElement
const oldUrl = link.getAttribute('href')
if (oldUrl) {
URL.revokeObjectURL(oldUrl)
}
// clearTimeout(timeout);
// (dq('#download-danmaku>span') as HTMLElement).innerHTML = '下载弹幕'
link.setAttribute('download', `${title}.${(ass ? 'ass' : 'xml')}`)
link.setAttribute('href', url)
link.click()
}
export default {
export: {
downloadDanmaku,
convertToAss,
},
widget: {
content: /* html */`
<button
class="gui-settings-flat-button"
id="download-danmaku">
<i class="icon-danmaku"></i>
<span>下载弹幕</span>
<a id="danmaku-link" style="display:none"></a>
</button>`,
condition: async () => {
let cid = await SpinQuery.select(() => (unsafeWindow || window).cid)
return Boolean(cid)
},
success: () => {
const link = dq('#danmaku-link')
const button = dq('#download-danmaku') as HTMLButtonElement
button.addEventListener('click', (e: MouseEvent) => {
if (e.target !== link) {
// const timeout = setTimeout(
// () => (dq('#download-danmaku>span') as HTMLElement).innerHTML = '请稍侯...',
// 200)
try {
button.disabled = true
downloadDanmaku(e.shiftKey)
} catch (error) {
logError(error)
} finally {
button.disabled = false
}
}
})
}
}
}