Bilibili Live Screenshot Helper

Bilibili Live Screenshot Tool – supports hotkey capture, burst mode, customizable hotkeys, burst interval settings, and menu language switch between Chinese and English.

התקן את הסקריפט?
סקריפטים מומלצים של יוצר זה

אולי תאהב גם את Bilibili Video Screenshot Helper.

התקן את הסקריפט
// ==UserScript==
// @name         Bilibili Live Screenshot Helper
// @name:zh-TW   Bilibili 直播截圖助手
// @name:zh-CN   Bilibili 直播截图助手
// @namespace    https://www.tampermonkey.net/
// @version      2.4
// @description  Bilibili Live Screenshot Tool – supports hotkey capture, burst mode, customizable hotkeys, burst interval settings, and menu language switch between Chinese and English.
// @description:zh-TW B站直播截圖工具,支援快捷鍵截圖、連拍功能,自定義快捷鍵、連拍間隔設定、中英菜單切換 
// @description:zh-CN B站直播截图工具,支援快捷键截图、连拍功能,自定义快捷键、连拍间隔设定、中英菜单切换
// @author       ChatGPT
// @match        https://live.bilibili.com/*
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @run-at       document-end
// @license MIT
// ==/UserScript==

(function () {
  'use strict';

  // === 預設設定 ===
  const DEFAULT_KEY = 'S';           // 預設截圖快捷鍵
  const DEFAULT_INTERVAL = 1000;     // 預設連拍間隔(毫秒)
  const MIN_INTERVAL = 100;          // 最小連拍間隔(毫秒)
  const SETTINGS_LOCK_KEY = 'screenshotHelperSettingsLock'; // prompt 彈窗鎖

  // === 多語言設定 ===
  const LANGS = {
    EN: {
      screenshot: 'Screenshot',
      keySetting: key => `Set Screenshot Key (Current: ${key})`,
      intervalSetting: val => `Set Burst Interval (Current: ${val}ms)`,
      langToggle: 'Language: EN',
      keyPrompt: 'Enter new key (A-Z)',
      intervalPrompt: 'Enter new interval in ms (>= 100)',
    },
    ZH: {
      screenshot: '截圖',
      keySetting: key => `設定截圖快捷鍵(目前:${key})`,
      intervalSetting: val => `設定連拍間隔(目前:${val} 毫秒)`,
      langToggle: '語言:中文',
      keyPrompt: '輸入新快捷鍵(A-Z)',
      intervalPrompt: '輸入新的連拍間隔(最小 100ms)',
    }
  };

  // === 取得/儲存設定值的封裝 ===
  function getSetting(key, def) {
    return GM_getValue(key, def);
  }
  function setSetting(key, val) {
    GM_setValue(key, val);
  }

  // === 讀取目前設定 ===
  let lang = getSetting('lang', 'EN');                    // 目前語言
  let currentKey = getSetting('hotkey', DEFAULT_KEY);     // 目前截圖快捷鍵
  let interval = getSetting('interval', DEFAULT_INTERVAL);// 目前連拍間隔

  // === 取得目前語言包 ===
  function t() {
    return LANGS[lang];
  }

  // === prompt 彈窗鎖,避免多重彈窗 ===
  async function promptWithLock(action) {
    if (localStorage.getItem(SETTINGS_LOCK_KEY) === '1') return;
    localStorage.setItem(SETTINGS_LOCK_KEY, '1');
    await new Promise(resolve => setTimeout(resolve, 0));
    localStorage.removeItem(SETTINGS_LOCK_KEY);
    action();
  }

  // === 註冊油猴右上角選單 ===
  function registerMenu() {
    // 快捷鍵設定
    GM_registerMenuCommand(t().keySetting(currentKey), () => {
      promptWithLock(() => {
        const input = prompt(t().keyPrompt, currentKey);
        if (input && /^[a-zA-Z]$/.test(input)) {
          const newKey = input.toUpperCase();
          if (newKey !== currentKey) {
            setSetting('hotkey', newKey);
            location.reload();
          }
        }
      });
    });

    // 連拍間隔設定
    GM_registerMenuCommand(t().intervalSetting(interval), () => {
      promptWithLock(() => {
        const input = prompt(t().intervalPrompt, interval);
        const val = parseInt(input, 10);
        if (!isNaN(val) && val >= MIN_INTERVAL && val !== interval) {
          setSetting('interval', val);
          location.reload();
        }
      });
    });

    // 語言切換
    GM_registerMenuCommand(t().langToggle, () => {
      promptWithLock(() => {
        const newLang = lang === 'EN' ? 'ZH' : 'EN';
        setSetting('lang', newLang);
        location.reload();
      });
    });
  }

  registerMenu();

  // === 產生截圖檔名(以本地時間為主) ===
  function generateFilename(video, canvas) {
    const pad = n => n.toString().padStart(2, '0');
    const padMs = n => n.toString().padStart(3, '0');
    const now = new Date();
    const h = pad(now.getHours());
    const m = pad(now.getMinutes());
    const s = pad(now.getSeconds());
    const ms = padMs(now.getMilliseconds());
    const year = now.getFullYear();
    const month = pad(now.getMonth() + 1);
    const day = pad(now.getDate());
    const roomIdMatch = location.pathname.match(/^\/(\d+)/);
    const roomId = roomIdMatch ? roomIdMatch[1] : 'UnknownRoom';
    // 新命名規則:[直播間ID]_年月日_小時_分鐘_秒_毫秒_解析度.png
    // 例如:123456_20240608_14_30_15_123_1920x1080.png
    return `[${roomId}]_${year}${month}${day}_${h}_${m}_${s}_${ms}_${canvas.width}x${canvas.height}.png`;
  }

  // === 截圖主函數 ===
  function takeScreenshot() {
    const video = document.querySelector('video');
    // 檢查 video 是否存在且可用
    if (!video || video.readyState < 2 || video.videoWidth === 0) return;

    // 建立 canvas 並繪製當前影像
    const canvas = document.createElement('canvas');
    canvas.width = video.videoWidth;
    canvas.height = video.videoHeight;
    const ctx = canvas.getContext('2d');
    ctx.drawImage(video, 0, 0, canvas.width, canvas.height);

    // 產生檔名
    const filename = generateFilename(video, canvas);

    // 下載圖片
    canvas.toBlob(blob => {
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = filename;
      a.style.display = 'none';
      document.body.appendChild(a);
      a.click();
      setTimeout(() => {
        URL.revokeObjectURL(url);
        a.remove();
      }, 100);
    }, 'image/png');
  }

  // === 快捷鍵與連拍監控 ===
  let holdTimer = null;   // 連拍計時器
  let isKeyDown = false;  // 是否已按下快捷鍵

  // 按下快捷鍵時觸發截圖與連拍
  document.addEventListener('keydown', e => {
    if (isKeyDown) return; // 防止重複觸發
    if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) return;
    if (e.key.toUpperCase() === currentKey) {
      isKeyDown = true;
      takeScreenshot();
      holdTimer = setInterval(takeScreenshot, interval);
    }
  });

  // 放開快捷鍵時停止連拍
  document.addEventListener('keyup', e => {
    if (e.key.toUpperCase() === currentKey && holdTimer) {
      clearInterval(holdTimer);
      holdTimer = null;
      isKeyDown = false;
    }
  });

  // 視窗失焦時自動停止連拍
  window.addEventListener('blur', () => {
    if (holdTimer) {
      clearInterval(holdTimer);
      holdTimer = null;
      isKeyDown = false;
    }
  });
})();