mirror of
https://github.com/the1812/Bilibili-Evolved.git
synced 2025-11-04 21:22:45 +08:00
91 lines
3.1 KiB
TypeScript
91 lines
3.1 KiB
TypeScript
import { playerAgent } from '@/components/video/player-agent'
|
|
import { getFriendlyTitle } from '@/core/utils/title'
|
|
|
|
const canvas = document.createElement('canvas')
|
|
export class Screenshot {
|
|
readonly mimeType = 'image/png'
|
|
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)
|
|
}, this.mimeType)
|
|
} 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)
|
|
}
|