// ==UserScript==
// @name AniGamer Screenshot Helper
// @name:zh-TW 動畫瘋截圖助手
// @name:zh-CN 动画疯截图助手
// @namespace https://ani.gamer.com.tw/
// @version 1.5
// @description AniGamer Screenshot Tool – supports hotkey capture, burst mode, customizable hotkeys, burst interval settings, and menu language switch between Chinese and English.
// @description:zh-TW 動畫瘋截圖工具,支援快捷鍵截圖、連拍模式、支援自定義快捷鍵、連拍間隔設定、中英菜單切換
// @description:zh-CN 动画疯截图工具,支援快捷键截图、连拍模式、支援自定义快捷键、连拍间隔设定、中英菜单切换
// @author You
// @match https://ani.gamer.com.tw/*
// @grant GM_registerMenuCommand
// @grant GM_getValue
// @grant GM_setValue
// @license MIT
// ==/UserScript==
(function () {
'use strict';
const defaultShortcut = 'S';
const defaultInterval = 1000;
const minInterval = 100;
const defaultLang = GM_getValue('lang', 'EN');
let lang = defaultLang;
let shortcutKey = GM_getValue('screenshotKey', defaultShortcut);
let interval = parseInt(GM_getValue('burstInterval', defaultInterval), 10);
let isPressing = false;
let burstTimer = null;
const i18n = {
EN: {
langSwitch: 'LANG EN',
setKey: key => `Set Shortcut Key (Current: ${key})`,
setInterval: ms => `Set Burst Interval (Current: ${ms}ms)`,
inputKey: 'Enter a new shortcut key (one character):',
inputInterval: 'Enter new burst interval in ms (min: 100):',
updatedKey: 'Shortcut key updated. Please refresh the page.',
updatedInterval: 'Burst interval updated. Please refresh the page.',
invalidInterval: 'Invalid input. Must be ≥ 100.'
},
ZH: {
langSwitch: '語言 中文',
setKey: key => `設定快捷鍵(目前:${key})`,
setInterval: ms => `設定連拍間隔(目前:${ms}ms)`,
inputKey: '請輸入新的截圖快捷鍵(單一字母):',
inputInterval: '請輸入新的連拍間隔(單位:毫秒,最小值:100):',
updatedKey: '快捷鍵已更新,請重新整理頁面。',
updatedInterval: '連拍間隔已更新,請重新整理頁面。',
invalidInterval: '請輸入一個不小於 100 的有效數字。'
}
};
const t = i18n[lang];
function captureScreenshot() {
const video = document.querySelector('video');
if (!video || video.readyState < 2) return;
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 pad = (n, len = 2) => n.toString().padStart(len, '0');
const time = video.currentTime;
const h = pad(Math.floor(time / 3600));
const m = pad(Math.floor((time % 3600) / 60));
const s = pad(Math.floor(time % 60));
const ms = pad(Math.floor((time % 1) * 1000), 3); // 毫秒 3 位數
const videoTime = `${h}_${m}_${s}_${ms}`;
// 標題處理(保留完整,移除非法字元)
const rawTitle = document.title;
const cleanedTitle = rawTitle.replace(/[\s\\/:*?"<>|]/g, '_');
const resolution = `${canvas.width}x${canvas.height}`;
const filename = `${videoTime}_${cleanedTitle}_${resolution}.png`;
canvas.toBlob(blob => {
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = filename;
a.click();
URL.revokeObjectURL(a.href);
}, 'image/png');
}
window.addEventListener('keydown', e => {
if (e.key.toUpperCase() === shortcutKey.toUpperCase() && !isPressing) {
isPressing = true;
captureScreenshot();
burstTimer = setInterval(() => {
captureScreenshot();
}, interval);
}
});
window.addEventListener('keyup', e => {
if (e.key.toUpperCase() === shortcutKey.toUpperCase()) {
isPressing = false;
clearInterval(burstTimer);
}
});
GM_registerMenuCommand(t.setKey(shortcutKey), () => {
const input = prompt(t.inputKey, shortcutKey);
if (input && input.length === 1) {
GM_setValue('screenshotKey', input.toUpperCase());
alert(t.updatedKey);
}
});
GM_registerMenuCommand(t.setInterval(interval), () => {
const input = prompt(t.inputInterval, interval);
const newVal = parseInt(input, 10);
if (!isNaN(newVal) && newVal >= minInterval) {
GM_setValue('burstInterval', newVal);
alert(t.updatedInterval);
} else {
alert(t.invalidInterval);
}
});
GM_registerMenuCommand(t.langSwitch, () => {
const newLang = lang === 'EN' ? 'ZH' : 'EN';
GM_setValue('lang', newLang);
location.reload();
});
})();