見出し画像

YouTubeの動画タイトル取得について調査中

こんにちは。サイボウズ株式会社 開発本部 People Experienceチーム コネクト支援チームの貴島(@jnkykn)です。サイボウズのエンジニアのポータルサイトCybozu Techを、開発者の酒井(@sakay_y)さんと一緒に運営管理しています。今週は、Videosに動画を掲載する際に、動画タイトルが取得できなくなったので調査をしています。


動画を掲載する機能の問題

一昨年、酒井(@sakay_y)さんのスライド掲載機能をもとに、私が追加した動画掲載機能は、当初順調に機能していました。しかし、先月追加したVideoのカードに動画のタイトルが掲載されなくなっていることに気づいた酒井(@sakay_y)さんが、タイトルが表示されるように修正してくださいました。これで安心と思っていたら、またタイトルが表示されなくなりました。

取得方法を変えて試してみる

手元で実行すると、酒井さんに修正していただく前のコードでもtitleが取得できるので、もとのmeta[property="og:title"]を取得する方法に戻してみました。

const meta = dom.window.document.querySelector('meta[property="og:title"]');
return meta?.getAttribute("content") ?? "";

しかし、結果はNGでした。

今度は、dom.window.document.titleを取得して、そこから" - YouTube"を除いてみようと再修正(酒井(@sakay_y)さんの修正も同じだったので、直接プロパティを取得する方法に修正)
ブラウザのconsoleで実行するとうまくいくので、今度こそ大丈夫なはずです。酒井(@sakay_y)さんの修正版も、もとのコードも、実はブラウザのconsoleでは期待通りの結果が得られていたので、少し嫌な予感がしました。

document.title.titleを" - YouTube"でsplitした0番目に、タイトルが取得される。
document.title.titleの" - YouTube"を””にreplaceすれば、タイトルが取得される。

実行してみると、なぜか置換したはずの"- YouTube"がタイトルとして設定されていました。どうして。。。

ビデオのタイトルとして"- YouTube"が設定されている

log出力してみる

ブラウザのコンソールで試すとうまくいくので、GHAの実行時に、titleに何が設定されているか?logを出力してみることにしました。

    const body = await getString(sourceURL);
    const dom = new JSDOM(body);
    const title = dom.window.document.title;
    console.log(`title is [${title ?? "undefined"}]`);
    return (title ?? "").replace(" - YouTube", "");

その、結果

Run cd /opt/actions-runner/_work/cybozu-tech/cybozu-tech/jobs/video-upload-job
title is [- YouTube]

どうして。。。

こうなったら、YouTube Data APIで取得するしか

お試し中


というわけで、タイトルを取得したいだけなのに、面倒だなぁと思いつつ、どんな風に使えるか連休中に試してみることにします。うまくいったら、Cybozu Techの動画掲載用のjobにも反映します。

YouTube Data APIの準備

個人アカウントで、YouTube Data APIが利用できるように設定して、API Exploererで目的の動画の情報を取得してみます。

API Explorerには、主要な言語のサンプルコードが表示されているので、JavaScriptのサンプルを見てみます。

<script src="https://apis.google.com/js/api.js"></script>
<script>
  /**
   * Sample JavaScript code for youtube.videos.list
   * See instructions for running APIs Explorer code samples locally:
   * https://developers.google.com/explorer-help/code-samples#javascript
   */

  function authenticate() {
    return gapi.auth2.getAuthInstance()
        .signIn({scope: "https://www.googleapis.com/auth/youtube.readonly"})
        .then(function() { console.log("Sign-in successful"); },
              function(err) { console.error("Error signing in", err); });
  }
  function loadClient() {
    gapi.client.setApiKey("YOUR_API_KEY");
    return gapi.client.load("https://www.googleapis.com/discovery/v1/apis/youtube/v3/rest")
        .then(function() { console.log("GAPI client loaded for API"); },
              function(err) { console.error("Error loading GAPI client for API", err); });
  }
  // Make sure the client is loaded and sign-in is complete before calling this method.
  function execute() {
    return gapi.client.youtube.videos.list({
      "part": [
        "snippet,contentDetails,statistics"
      ],
      "id": [
        "WgwiDSPSSr8"
      ]
    })
        .then(function(response) {
                // Handle the results here (response.result has the parsed body).
                console.log("Response", response);
              },
              function(err) { console.error("Execute error", err); });
  }
  gapi.load("client:auth2", function() {
    gapi.auth2.init({client_id: "YOUR_CLIENT_ID"});
  });
</script>
<button onclick="authenticate().then(loadClient)">authorize and load</button>
<button onclick="execute()">execute</button>

これをもとに、jobとして実行できるようにTypeScriptで呼び出しと、結果の表示を試してみたいと思ったのですが、こちらの記事が参考になりそうです。

IDが一致する動画を取得してみる


今回は、動画の情報を取得したいので、https://developers.google.com/youtube/v3/docs/videos/listを使います。

コードは、こんな感じで。

import { google } from "googleapis";

const main = async () => {
  const result = await searchYoutube("WgwiDSPSSr8");
  console.log(`タイトル[${result}]`);
};

// Youtubeの指定動画の、タイトルを取得する
export const searchYoutube = async (ID: string): Promise<string> => {
  const youtube = google.youtube({
    version: "v3",
    auth: process.env["YOUTUBE_DATA_API"] ?? "",
  });
  const searchRes = await youtube.videos.list(
    {
      "part": [
        "snippet,contentDetails,statistics"
      ],
      "id": [
        ID
      ]
    }
  );

  // 先頭1件を抽出
  const targetItems = searchRes.data.items ?? [];
    return targetItems.map((item) => (
      {
      title: item.snippet?.title ?? "",
    }))[0].title;
};

実行してみたら、何も帰ってこない😢

$ ./node_modules/.bin/ts-node getVideoTitle.ts 
$ 

今日はここまで

こういうときは、一晩寝かせるのが良いと思うので、明日すっきりした頭で再チャレンジします。
今日は、ここまで。(明日うまくいったら、続きを書きます)

追記(2024-11-04)

落ち着いてソースコードを見てみたら、()足りてませんでした。修正して実行したら、動画のタイトル取得ができました。

import { google } from "googleapis";

// Youtubeの指定動画の、タイトルを取得する
const searchYoutube = async (ID: string): Promise<string> => {
  const youtube = google.youtube({
    version: "v3",
    auth: process.env.YOUTUBE_DATA_API ?? "",
  });
  const searchRes = await youtube.videos.list(
    {
      "part": [
        "snippet,contentDetails,statistics"
      ],
      "id": [
        ID
      ]
    }
  );

  // 先頭1件を抽出
  const targetItems = searchRes.data.items ?? [];
    return targetItems.map((item) => (
      {
      title: item.snippet?.title ?? "",
    }))[0].title;
};

(async ()=>{

  try {
     const result = await searchYoutube("WgwiDSPSSr8");
    console.log(`タイトル[${result}]`);
  }
  catch(err){
    console.error(`実行失敗`);
  }
})();   // ←ここの"()"が抜けてました。

実行結果

$ node -r esbuild-register ./src/getVideoTitle.ts
タイトル[ITエンジニアの自由と繋がりの力〜“情報教育必修世代”の才能を社会はどう受け入れるべきか   NoMaps2024 CONFERENCE]
$

YouTube Data APIの使い方は確認できたので、本番でどう使うか?を酒井(@sakay_y)さんにご相談しようと思います。YouTube Data APIを使わずにdomの属性値が取得できる方が良いなと思っているので、そちらももう少し調査したいと思います。

この記事が気に入ったらサポートをしてみませんか?