Bilibili-Evolved/registry/lib/components/video/player/screenshot/screenshot.ts
2022-10-12 23:27:58 +08:00

90 lines
3.0 KiB
TypeScript

import { playerAgent } from '@/components/video/player-agent'
import { getFriendlyTitle } from '@/core/utils/title'
const canvas = document.createElement('canvas')
export class Screenshot {
url = ''
blob: Blob
timeStamp = new Date().getTime()
constructor(
public video: HTMLVideoElement,
public videoTime: number,
public withDanmaku = false,
) {
this.createUrl()
}
async createUrl() {
const { logError } = await import('@/core/utils/log')
if (this.withDanmaku) {
const videoWrapper = dq(playerAgent.query.video.wrap.selector) as HTMLElement
const rect = videoWrapper.getBoundingClientRect()
const playerRatio = rect.width / rect.height
const videoRatio = this.video.videoWidth / this.video.videoHeight
if (playerRatio >= videoRatio) {
// 竖屏视频(两侧黑边)
canvas.height = this.video.videoHeight
canvas.width = this.video.videoHeight * playerRatio
} else {
// 横屏视频(上下黑边)
canvas.width = this.video.videoWidth
canvas.height = this.video.videoWidth / playerRatio
}
} else {
canvas.width = this.video.videoWidth
canvas.height = this.video.videoHeight
}
const context = canvas.getContext('2d')
if (context === null) {
logError('视频截图失败: canvas 未创建或创建失败.')
return
}
const videoLeft = (canvas.width - this.video.videoWidth) / 2
const videoTop = (canvas.height - this.video.videoHeight) / 2
context.drawImage(this.video, videoLeft, videoTop)
if (this.withDanmaku) {
const danmakuCanvas = dq(
'canvas.bilibili-player-video-danmaku, canvas.dm-canvas',
) as HTMLCanvasElement
if (danmakuCanvas !== null) {
context.drawImage(danmakuCanvas, 0, 0, canvas.width, canvas.height)
}
}
try {
canvas.toBlob(blob => {
if (blob === null) {
logError('视频截图失败: 创建 blob 失败.')
return
}
this.blob = blob
this.url = URL.createObjectURL(blob)
}, 'image/png')
} catch (error) {
logError(
'视频截图失败: 操作被浏览器阻止. 这通常发生于电影的试看片段, 请在正片尝试使用截图功能.',
)
}
}
get filename() {
return `${getFriendlyTitle()} @${this.time.replace(/:/g, '-')} ${this.timeStamp.toString()}.png`
}
get id() {
return this.videoTime.toString() + this.timeStamp.toString()
}
get time() {
const hour = Math.trunc(this.videoTime / 3600).toString()
const minute = Math.trunc(this.videoTime / 60).toString()
const second = (this.videoTime % 60).toFixed(2)
if (hour === '0') {
return `${minute.padStart(2, '0')}:${second.padStart(5, '0')}`
}
return `${hour}:${minute.padStart(2, '0')}:${second.padStart(5, '0')}`
}
revoke() {
URL.revokeObjectURL(this.url)
}
}
export const takeScreenshot = (video: HTMLVideoElement, withDanmaku = false) => {
const time = video.currentTime
return new Screenshot(video, time, withDanmaku)
}