// 用Set存储字幕(自动去重,只保留首次出现的内容)
const captionsSet = new Set();
// 超时计时器ID
let timeoutId = null;
// 监控开关
let isObserving = true;

// 目标标签选择器(class包含"mejs__captions-text"的span)
const targetSelector = 'span[class*="mejs__captions-text"]';

// 处理新字幕(只添加未出现过的内容)
function handleNewCaption(text) {
  if (!text) return; // 忽略空文本

  // 只有文本未在Set中时,才添加并重置计时器
  if (!captionsSet.has(text)) {
    captionsSet.add(text);
    console.log('新增字幕:', text); // 实时打印新增的字幕

    // 重置10秒计时器(只有新内容才触发计时重置)
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => {
      isObserving = false;
      observer.disconnect(); // 停止监听
      console.log('\n===== 10秒无新字幕,监控结束 =====');
      console.log('完整字幕列表:');
      // 将Set转换为数组输出(按出现顺序)
      console.log(Array.from(captionsSet).join('\n'));
    }, 10000);
  }
}

// 提取节点文本
function getNodeText(node) {
  return node.textContent?.trim() || '';
}

// 初始扫描已有字幕(避免遗漏页面加载时已存在的)
document.querySelectorAll(targetSelector).forEach(node => {
  handleNewCaption(getNodeText(node));
});

// 创建DOM变化观察者(监听新增/更新/属性变化)
const observer = new MutationObserver((mutations) => {
  if (!isObserving) return;

  mutations.forEach(mutation => {
    // 1. 处理新增节点
    mutation.addedNodes.forEach(node => {
      if (node.nodeType !== 1) return; // 只处理元素节点
      const targets = node.matches(targetSelector) ? [node] : node.querySelectorAll(targetSelector);
      targets.forEach(span => handleNewCaption(getNodeText(span)));
    });

    // 2. 处理已有节点的文本内容变化
    if (mutation.type === 'characterData' && mutation.target.parentNode) {
      const parentSpan = mutation.target.parentNode.closest(targetSelector);
      if (parentSpan) {
        handleNewCaption(getNodeText(parentSpan));
      }
    }

    // 3. 处理节点class属性变化(编号更新时可能伴随文本变化)
    if (mutation.type === 'attributes' && mutation.target.matches(targetSelector)) {
      handleNewCaption(getNodeText(mutation.target));
    }
  });
});

// 启动监听(覆盖所有可能的字幕更新场景)
observer.observe(document.body, {
  childList: true,       // 监听节点新增/删除
  subtree: true,         // 监听所有后代节点
  characterData: true,   // 监听文本内容变化
  attributes: true,      // 监听属性变化(如class编号)
  attributeFilter: ['class'] // 只监听class属性(优化性能)
});

console.log('开始监控字幕(自动去重,10秒无新内容输出完整列表)...');


字幕捕获

主动结束监听并输出字幕

// 停止DOM监听
if (observer) observer.disconnect();
// 清除自动结束的计时器
if (timeoutId) clearTimeout(timeoutId);
// 标记为已停止
isObserving = false;
// 输出当前所有字幕
console.log('\n===== 手动结束监控 =====');
console.log('完整字幕列表:');
console.log(Array.from(captionsSet).join('\n'));

一旦获得视频脚本,知识复看将会变得简单许多。

可供日常学习使用。


收录于知乎专栏「js逆向实例」· 查看知乎原文