import { getFriendlyTitle } from '../title'; // let canvas: HTMLCanvasElement | null = null; // let context: CanvasRenderingContext2D | null = null; class Screenshot { video: HTMLVideoElement; url = ""; time: number; private timeStamp = new Date().getTime(); constructor(video: HTMLVideoElement, time: number) { this.video = video; this.time = time; // this.url = URL.createObjectURL(this.blob); this.createUrl(); } async createUrl() { const canvas = document.createElement("canvas"); canvas.width = this.video.videoWidth; canvas.height = this.video.videoHeight; const context = canvas.getContext("2d"); if (context === null) { throw new Error("视频截图失败: canvas 未创建或创建失败."); } context.drawImage(this.video, 0, 0); canvas.toBlob(blob => { if (blob === null) { throw new Error("视频截图失败: 创建 blob 失败."); } this.url = URL.createObjectURL(blob); }, "image/png"); } get filename() { return getFriendlyTitle() + " @" + this.time.toString() + ".png"; } get id() { return this.time.toString() + this.timeStamp.toString(); } revoke() { URL.revokeObjectURL(this.url); } } export const takeScreenshot = (video: HTMLVideoElement) => { // if (canvas === null || context === null) // { // canvas = document.createElement("canvas"); // canvas.width = video.videoWidth; // canvas.height = video.videoHeight; // context = canvas.getContext("2d"); // } const canvas = document.createElement("canvas"); canvas.width = video.videoWidth; canvas.height = video.videoHeight; const time = video.currentTime; return new Screenshot(video, time); // return new Promise((resolve, reject) => // { // const context = canvas.getContext("2d"); // if (canvas === null || context === null) // { // reject("视频截图失败: canvas 未创建或创建失败."); // return; // } // context.drawImage(video, 0, 0); // canvas.toBlob(blob => // { // if (blob === null) // { // reject("视频截图失败: 创建 blob 失败."); // return; // } // resolve(new Screenshot(blob, time)); // }, "image/png"); // }); } resources.applyStyle("videoScreenshotStyle"); document.body.insertAdjacentHTML("beforeend", /*html*/` `); Vue.component("video-screenshot", { props: { objectUrl: String, filename: String, }, template: /*html*/`
`, methods: { discard() { this.$emit("discard"); }, }, }); const screenShotsList = new Vue({ el: ".video-screenshot-list", data: { screenshots: [] as Screenshot[], }, methods: { discard(screenshot: Screenshot) { this.screenshots.splice(this.screenshots.indexOf(screenshot), 1); screenshot.revoke(); } } }); Observer.videoChange(async () => { const video = await SpinQuery.select("#bofqi video") as HTMLVideoElement; const time = await SpinQuery.select(".bilibili-player-video-time"); if (video === null || time === null || document.querySelector(".video-take-screenshot")) { return; } time.insertAdjacentHTML("afterend", /*html*/`
`); const screenshotButton = document.querySelector(".video-take-screenshot"); if (screenshotButton === null) { return; } screenshotButton.addEventListener("click", () => { const screenshot = takeScreenshot(video); screenShotsList.screenshots.push(screenshot); }); }); export default { export: { takeScreenshot, screenShotsList, }, };