Teams会議のトランスクリプトを開催者以外でも保存!ブラウザで実行するVTT取得スクリプト

Teams会議のトランスクリプトを開催者以外でも保存!ブラウザで実行するVTT取得スクリプト
🐙

GitHubにてソースコード公開中!

本記事で紹介しているスクリプト(get-transcript.js)と使い方は、GitHubリポジトリで公開しています。今後のアップデートもこちらで行いますので、ぜひチェックしてみてください!
👉 リポジトリ:TeamsScript / GetTranscript を見る

こんにちは!今回は、Microsoft Teamsの会議録画から「トランスクリプト(文字起こし)」をファイルとして保存するスクリプトを作成したのでご紹介します。

Teams会議のトランスクリプトは議事録の作成や内容の振り返りにとても便利ですが、「録画は見られるのに、トランスクリプトがダウンロードできない…」という経験はありませんか?
今回作成したスクリプトを使えば、ブラウザで録画を再生している画面からトランスクリプトを読み取り、VTT形式のファイルとしてダウンロードすることができます!


1. スクリプトを作成した背景

Teams会議のトランスクリプトは、通常は会議の開催者しかダウンロードできません。
開催者以外の人がトランスクリプトファイルを入手するには、次のどちらかが必要になります。

  • 開催者に依頼して、共同開催者に設定してもらう
  • 開催者からトランスクリプトファイルを直接もらう

どちらも開催者とのやり取りが発生するため、ちょっと手間がかかりますよね。
一方で、録画をブラウザで表示するとトランスクリプト自体は画面に表示されています。そこで、画面に表示されているトランスクリプトを直接読み取ってファイルに保存できるようにしました。


2. スクリプトの使い方

使い方はシンプルで、ブラウザの開発者ツール(コンソール)にスクリプトを貼り付けて実行するだけです。特別なツールのインストールは必要ありません。

ステップ1: 会議の録画を開いてトランスクリプトを表示

まずはブラウザで対象となる会議の録画を開き、トランスクリプトを表示します。

ステップ2: トランスクリプト欄を一番上までスクロール

スクリプトは表示されている位置から下に向かって読み取っていくため、トランスクリプト欄を一番上までスクロールしておきます。
トランスクリプトを表示した直後は一番上になっているので、通常はそのままで大丈夫です。

ステップ3: 開発者ツールのコンソールを開く

キーボードの F12 または Ctrl + Shift + I を押して開発者ツールを開き、「コンソール(Console)」タブを選択します。

ステップ4: スクリプトを貼り付けて実行

後述する get-transcript.js の中身をすべてコピーしてコンソールに貼り付け、Enter キーで実行します。

⚠️ 注意事項:コンソールに貼り付けができない場合
ブラウザによっては、初めてコンソールに貼り付けようとすると警告が表示され、貼り付けがブロックされることがあります。
その場合は、コンソールに allow pasting と入力して Enter を押し、そのあと改めてスクリプトを貼り付けてください。

ステップ5: 完了するまで待つ

スクリプトを実行すると、トランスクリプト欄が自動でスクロールされ、発言の読み取りが進んでいきます。進み具合はコンソールに表示されます。
実行中は画面を操作しないでください。スクロール位置が変わると、正しく読み取れなくなる可能性があります。

最後まで読み取りが終わると、Teams_Transcript_Extracted.vtt というファイルが自動でダウンロードされます。


3. 出力されるVTTファイルのイメージ

ダウンロードされたファイルは、以下のようなVTT形式になっています。
発言ごとに「開始時刻 --> 終了時刻」と「話者名・発言内容」が記録されています。

WEBVTT

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/1-0
00:00:03.000 --> 00:00:10.000
<v 山田 太郎>それでは会議を始めます。</v>

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx/2-0
00:00:10.000 --> 00:00:15.000
<v 佐藤 花子>よろしくお願いします。</v>
💡 開催者に依頼しなくてもトランスクリプトが手に入る!
VTT形式はTeamsからダウンロードできるトランスクリプトと同じ形式なので、議事録の作成や、生成AIに渡して会議内容を要約してもらうといった用途にもそのまま活用できます。

4. スクリプトの仕組み

スクリプトの中では、大きく分けて次のような処理を行っています。

  1. 画面に表示されているトランスクリプトの発言から、話者名・タイムスタンプ・発言内容を読み取ります。
  2. トランスクリプト欄の最後の発言までスクロールし、新しい発言を読み込みながら取得を続けます。
  3. 新しい発言が読み込まれない状態が4回続いたら、最後まで到達したと判断して取得を終了します。
  4. 取得した発言を順番に並べてVTT形式に整形し、ファイルとしてダウンロードします。
💡 補足:タイムスタンプの取得方法

Teamsのトランスクリプト欄には、画面の読み上げ用に「話者名 1 時間 2 分間 3 秒間」のようなラベル(aria-label)が設定されています。スクリプトではこのラベルを正規表現で分解し、話者名と時刻を取り出しています。

  • 開始時刻:各発言のタイムスタンプ(秒単位)
  • 終了時刻:次の発言の開始時刻(最後の発言だけは開始時刻の5秒後)
⚠️ 注意事項
  • 日本語表示のTeamsを前提にしています。タイムスタンプを日本語の読み上げ用ラベルから取り出しているため、ほかの言語で表示している場合は時刻を正しく取得できません。
  • Teamsの画面構成(HTMLのクラス名など)が変わると、動かなくなる可能性があります。
  • 利用にあたっては、所属組織のルールや会議参加者の意向に従ってください。

5. スクリプトのソースコード公開

GitHubに公開している get-transcript.js のコード本体です。以下のコードをコピーして、コンソールに貼り付けてご利用ください。

(async () => {
  console.log("【開始】トランスクリプトの抽出を開始します。画面を操作せずにお待ちください...");

  const extractedData = new Map();
  let stagnantCount = 0;
  let lastProcessedIndex = -1;
  const sessionUuid = crypto.randomUUID ? crypto.randomUUID() : "123e4567-e89b-12d3-a456-426614174000";

  const formatTime = (h, m, s) => {
    const hh = String(h).padStart(2, '0');
    const mm = String(m).padStart(2, '0');
    const ss = String(s).padStart(2, '0');
    return `${hh}:${mm}:${ss}.000`;
  };

  const extractVisibleItems = () => {
    const entries = document.querySelectorAll('[class*="baseEntry-"]');
    entries.forEach(entry => {
      const textEl = entry.querySelector('[class*="entryText-"]');
      if (!textEl) return;

      const indexStr = textEl.getAttribute('aria-posinset') || textEl.id.replace(/\D/g, '');
      const index = parseInt(indexStr, 10);
      const text = textEl.innerText.trim();
      const ariaLabel = entry.getAttribute('aria-label') || "";
      const match = ariaLabel.match(/(.+?)\s+(?:(\d+)\s*時間\s*)?(?:(\d+)\s*分間\s*)?(?:(\d+)\s*秒間)$/);
      let name = ariaLabel, h = 0, m = 0, s = 0;

      if (match) {
        name = match[1]; h = parseInt(match[2] || '0', 10); m = parseInt(match[3] || '0', 10); s = parseInt(match[4] || '0', 10);
      }
      if (text && !extractedData.has(index)) {
        extractedData.set(index, { name, h, m, s, text });
      }
    });
  };

  extractVisibleItems();

  while (true) {
    const entries = document.querySelectorAll('[class*="baseEntry-"]');
    if (entries.length === 0) break;

    const lastEntry = entries[entries.length - 1];
    const textEl = lastEntry.querySelector('[class*="entryText-"]');
    const currentIndex = textEl ? parseInt(textEl.getAttribute('aria-posinset') || textEl.id.replace(/\D/g, ''), 10) : -1;

    lastEntry.scrollIntoView({ behavior: 'smooth', block: 'end' });
    await new Promise(r => setTimeout(r, 1500));
    extractVisibleItems();

    if (currentIndex === lastProcessedIndex) {
      stagnantCount++;
      console.log(`読込待機中... (${stagnantCount}/4)`);
      if (stagnantCount >= 4) {
        console.log("【完了】終端に到達しました。");
        break;
      }
    } else {
      stagnantCount = 0;
      lastProcessedIndex = currentIndex;
    }
  }

  console.log(`【抽出完了】合計 ${extractedData.size} 件の発言を取得しました。VTT形式でファイルを作成します...`);
  let outputText = "WEBVTT\n\n";
  const sortedKeys = Array.from(extractedData.keys()).sort((a, b) => a - b);

  for (let i = 0; i < sortedKeys.length; i++) {
    const key = sortedKeys[i];
    const data = extractedData.get(key);
    const nextData = i + 1 < sortedKeys.length ? extractedData.get(sortedKeys[i + 1]) : null;
    const startTime = formatTime(data.h, data.m, data.s);
    let endTime = "";
    if (nextData) {
      endTime = formatTime(nextData.h, nextData.m, nextData.s);
    } else {
      let endS = data.s + 5, endM = data.m, endH = data.h;
      if (endS >= 60) { endS -= 60; endM++; }
      if (endM >= 60) { endM -= 60; endH++; }
      endTime = formatTime(endH, endM, endS);
    }
    outputText += `${sessionUuid}/${key}-0\n${startTime} --> ${endTime}\n<v ${data.name}>${data.text}</v>\n\n`;
  }

  const blob = new Blob([outputText], { type: 'text/vtt;charset=utf-8' });
  const url = URL.createObjectURL(blob);
  const a = document.createElement('a');
  a.href = url;
  a.download = 'Teams_Transcript_Extracted.vtt';
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  URL.revokeObjectURL(url);
  console.log("【🎉 成功】Teams_Transcript_Extracted.vtt のダウンロードが完了しました!");
})();

まとめ

今回は、Teams会議の録画画面からトランスクリプトを読み取り、VTT形式のファイルとして保存するスクリプトをご紹介しました。
開催者にお願いしなくても、録画を見られる環境さえあればトランスクリプトを手元に残せるので、議事録作成や会議の振り返りがぐっとラクになります。ぜひ試してみてくださいね!

コメント

このブログの人気の投稿

Power Automateでファイル名から拡張子を取得

PowerAppsで座席表を作成する

NotebookLMのローカル版「Open Notebook」をLM Studioで完全オフライン構築してみた