bilibili-screenshot

快速获取Bilibili当前视频的截图并复制到剪贴板

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        bilibili-screenshot
// @description 快速获取Bilibili当前视频的截图并复制到剪贴板
// @namespace   github.com/frostime
// @match       *://www.bilibili.com/video/*
// @icon        https://www.google.com/s2/favicons?sz=64&domain=bilibili.com
// @version     0.2.3
// @author      frostime
// @license     MIT
// @grant       none
// ==/UserScript==
(function () {
    'use strict';

    // 创建按钮组容器
    const createButtonGroup = () => {
        const container = document.createElement('div');
        container.style.position = 'fixed';
        container.style.bottom = '20px';
        container.style.right = '20px';
        container.style.zIndex = '9999';
        // 创建按钮容器
        const buttonContainer = document.createElement('div');
        buttonContainer.style.display = 'flex';
        buttonContainer.style.flexDirection = 'column';
        buttonContainer.style.gap = '10px';
        buttonContainer.style.transition = 'transform 0.3s ease, opacity 0.3s ease';
        buttonContainer.style.transformOrigin = 'bottom right';
        buttonContainer.style.opacity = '0';
        buttonContainer.style.transform = 'scale(0.8) translateY(-10px)';
        buttonContainer.style.pointerEvents = 'none'; // 初始状态不可点击
        buttonContainer.style.position = 'absolute';
        buttonContainer.style.bottom = '50px'; // 在折叠按钮上方留出空间
        buttonContainer.style.right = '0';
        buttonContainer.style.width = '120px'; // 设置固定宽度
        // 创建折叠/展开按钮
        const toggleButton = document.createElement('button');
        toggleButton.innerHTML = '⚙️'; // 使用齿轮图标
        toggleButton.style.width = '40px';
        toggleButton.style.height = '40px';
        toggleButton.style.borderRadius = '50%';
        toggleButton.style.backgroundColor = '#00A1D6';
        toggleButton.style.color = '#fff';
        toggleButton.style.border = 'none';
        toggleButton.style.cursor = 'pointer';
        toggleButton.style.fontSize = '20px';
        toggleButton.style.position = 'absolute';
        toggleButton.style.bottom = '0';
        toggleButton.style.right = '0';
        toggleButton.style.transition = 'transform 0.3s ease';
        toggleButton.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)';
        let isExpanded = false;
        toggleButton.addEventListener('click', () => {
            isExpanded = !isExpanded;
            if (isExpanded) {
                buttonContainer.style.opacity = '1';
                buttonContainer.style.transform = 'scale(1) translateY(0)';
                buttonContainer.style.pointerEvents = 'auto';
                toggleButton.style.transform = 'rotate(180deg)';
            }
            else {
                buttonContainer.style.opacity = '0';
                buttonContainer.style.transform = 'scale(0.8) translateY(-10px)';
                buttonContainer.style.pointerEvents = 'none';
                toggleButton.style.transform = 'rotate(0deg)';
            }
        });
        container.appendChild(buttonContainer);
        container.appendChild(toggleButton);
        return {
            container,
            buttonContainer
        };
    };
    // 创建单个按钮
    const createButton = (config) => {
        const button = document.createElement('button');
        button.innerText = config.text;
        button.style.padding = '8px 12px';
        button.style.backgroundColor = config.backgroundColor || '#00A1D6';
        button.style.color = '#fff';
        button.style.border = 'none';
        button.style.borderRadius = '5px';
        button.style.cursor = 'pointer';
        button.style.fontSize = '12px';
        button.style.width = '100%'; // 让按钮填满容器宽度
        button.style.whiteSpace = 'nowrap'; // 防止文字换行
        button.style.textAlign = 'center'; // 文字居中
        button.addEventListener('click', config.onClick);
        return button;
    };
    // 复制视频截图
    const copyScreenshot = async () => {
        const video = document.querySelector('video');
        if (!video) {
            showMessage('无法找到视频元素');
            return;
        }
        const canvas = document.createElement('canvas');
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
        const context = canvas.getContext('2d');
        if (context) {
            context.drawImage(video, 0, 0, canvas.width, canvas.height);
            canvas.toBlob(async (blob) => {
                if (blob) {
                    try {
                        await navigator.clipboard.write([
                            new ClipboardItem({
                                'image/png': blob
                            })
                        ]);
                        showMessage('截图已复制到剪贴板!');
                    }
                    catch (err) {
                        console.error('复制截图失败: ', err);
                        showMessage('复制截图失败,请检查浏览器支持的权限。');
                    }
                }
            }, 'image/png');
        }
    };
    // 复制分享链接
    const copyShareLink = async (timestamp = true) => {
        // 1. 获取当前 URL
        const currentUrl = new URL(window.location.href);
        const baseUrl = currentUrl.origin + currentUrl.pathname;
        // h1.video-title 
        let title = document.querySelector('h1.video-title')?.textContent;
        // 替换 title 内部的 [ ] 符号,防止 markdown 格式化错误,替换为 【 】
        title = title?.replace(/\[([^\]]+)\]/g, '【$1】');
        const copyLink = (text) => {
            navigator.clipboard.writeText(text);
            showMessage('复制分享链接');
        };
        if (timestamp === false) {
            const text = `[${title}](${baseUrl})`;
            copyLink(text);
            return;
        }
        // 获取视频时间戳 div.bpx-player-ctrl-time-label span.bpx-player-ctrl-time-current
        const current = document.querySelector('div.bpx-player-ctrl-time-label span.bpx-player-ctrl-time-current')?.textContent;
        if (!current) {
            showMessage('无法找到视频时间戳');
            return;
        }
        const parts = current.split(':');
        let time = 0;
        if (parts.length === 3) {
            time = parseInt(parts[0]) * 3600 + parseInt(parts[1]) * 60 + parseInt(parts[2]);
        }
        else {
            time = parseInt(parts[0]) * 60 + parseInt(parts[1]);
        }
        const text = `[空降到 ${current}](${baseUrl}?t=${time})`;
        copyLink(text);
    };
    // 消息框显示功能
    const showMessage = (message) => {
        const messageBox = document.createElement('div');
        messageBox.innerText = message;
        messageBox.style.position = 'fixed';
        messageBox.style.bottom = '70px';
        messageBox.style.right = '20px';
        messageBox.style.zIndex = '9999';
        messageBox.style.padding = '10px 20px';
        messageBox.style.backgroundColor = 'rgba(0, 0, 0, 0.7)';
        messageBox.style.color = '#fff';
        messageBox.style.borderRadius = '5px';
        messageBox.style.boxShadow = '0 2px 4px rgba(0, 0, 0, 0.2)';
        messageBox.style.fontSize = '14px';
        messageBox.style.transition = 'opacity 0.5s';
        messageBox.style.opacity = '1';
        document.body.appendChild(messageBox);
        setTimeout(() => {
            messageBox.style.opacity = '0';
            setTimeout(() => {
                messageBox.remove();
            }, 250);
        }, 1500);
    };
    // 初始化按钮组
    const { container, buttonContainer } = createButtonGroup();
    // 创建截图按钮
    const screenshotButton = createButton({
        text: '截图并复制',
        onClick: copyScreenshot
    });
    // 创建分享链接按钮
    const shareLinkButton = createButton({
        text: '视频分享链接',
        onClick: () => copyShareLink(false),
        backgroundColor: '#FF6699'
    });
    const preciseJumpButton = createButton({
        text: '精准空降链接',
        onClick: () => copyShareLink(true),
        backgroundColor: '#FF6699'
    });
    // 添加按钮到按钮容器
    buttonContainer.appendChild(screenshotButton);
    buttonContainer.appendChild(shareLinkButton);
    buttonContainer.appendChild(preciseJumpButton);
    // 将按钮组添加到页面
    document.body.appendChild(container);

})();
//# sourceMappingURL=bundle.user.js.map