17
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ZOZOテクノロジーズ #4Advent Calendar 2019

Day 10

YouTubeの英語字幕をディクテーションできるChrome拡張機能を作る(海外ドラマで英語学習がしたい!)

Last updated at Posted at 2019-12-09

ZOZOテクノロジーズ #4 Advent Calendar 2019 の10日目です。
昨日は@pakioさんの「Laravel + Vue + Vuetifyでbundle sizeの削減を試みてみる」でした。

なんと先日、英単語の日本語訳とレベルを同時に取得でき、しかもローカル環境だけで動く素晴らしいJavaScriptライブラリに出会いました! この記事ではそのライブラリを使ってChrome拡張機能を作っていきます。

作っているもの

YouTubeの英語字幕をディクテーション(リスニング)できるChrome拡張機能「YouTube-Dictation」
github.com/tippy3/youtube-dictation
qiita用スクリーンショット.png
わかりにくいスクショで恐縮です。
つまりは動画の下に字幕を出して、ユーザーが英単語をディクテーションするということです。

実際に作った流れ

Chrome拡張機能はJavaScript, HTML, CSSで開発します。めちゃくちゃ簡単です。

1 manifest.json

まずは各ファイルを用意し、エントリーポイントとなるmanifest.jsonを作ります。

{
  "name": "YouTube-Dictation",
  "short_name": "YouTube-Dictation",
  "description": "YouTubeで楽しく英語学習。好きな動画の英語字幕でディクテーション(リスニング)ができる拡張機能です。",
  "version": "1.0",
  "icons": {
    "128": "images/128.png"
  },
  "content_scripts": [{
    "matches": ["https://*.youtube.com/*", "http://*.youtube.com/*"],
    "run_at": "document_end",
    "css": ["css/main.css"],
    "js": ["js/superagent-master/superagent.js", "js/underscore-master/underscore.js", "js/javascript-lemmatizer-master/js/lemmatizer.js", "js/kantan-ej-dictionary-master/kantan-ej-dictionary.js", "js/main.js"]
  }],
  "web_accessible_resources": [
    "js/javascript-lemmatizer-master/dict/*.json"
  ],
  "permissions": [
    "storage"
  ],
  "manifest_version": 2
}

概要をざっくり説明すると、

content_scriptsは表示しているWebページに挿入されるJavaScriptです。表示中のDOMを操作したり、キーイベントを追加したりすることができます。他にも拡張機能のアイコンを押されたときに実行されるpage_actionなどがあります。

今回はcontent_scriptsでYouTubeのURLにマッチしたときにCSSとJavaScriptを読み込みます。JavaScriptは依存関係のあるライブラリを順に読み込み、最後に自分の処理を書くmain.jsを読み込みます。"run_at"でWebページの読み込み後に実行するようにしています。

また、ライブラリにJSONを使うものがあったのでweb_accessible_resourcesにファイル名を書きました。これを書かないとJavaScriptからJSONにアクセスできません。また、Web Storageを使うライブラリがあったのでpermissionsに"storage"を入れました。

ここからmain.jsをガリガリ書いていきます。

2 UIを出力する

このやり方は完全に自己流なのですが、JavaScriptでDOMをappendChildしてUIを出力します。なんか頭の悪い感じもしますが動くのでヨシ!

let html = document.createElement("div");
html.id = "yt-dictation-container";
html.innerHTML = '<p id="yt-dictation-text">YouTube-Dictation</p>...長いので省略';
html.addEventListener('click', clickEvent);
document.body.appendChild(html);

3 AjaxでAPIを叩き字幕を取得する

AjaxするためのライブラリsuperagentでJSONを取得します。使い方はjQueryの.ajax()とほぼ同じです。

function requestCCList(){
  SUPERAGENT
  .get(YOUTUBE_CC_API)
  .accept('json')
  .query({
    type: 'list',
    v: video_id
  })
  .end(responseCCList);
}

function responseCCList(err, res){
// 略。下記参照
}

実際には、まずは字幕一覧を取得し、もし英語の字幕があったらその字幕を取得する、という2段構えになっています。
また、字幕を取得後、行ごとの配列を作成し、さらに単語ごとの配列を作成しています。

4 ディクテーションする英単語を選ぶ

**この章がこの記事の本編です。**はじめは英単語をランダムに出題していたのですが、ユーザーのレベルに合わせた英単語を出題したいと思いました。そこで使ったのが以下のライブラリです。

  • kantan-ej-dictionary
    • 英単語の和訳とSVLレベルを取得できるとても素晴らしいライブラリ
  • javascript-lemmatizer
    • 英単語の原型を取得できるとても素晴らしいライブラリ

英単語のレベル分けには、アルクによるSVL12000(重要英単語12000語を1から12までの12段階にレベルわけしたもの)を採用し、それを和訳とともに取得できるライブラリkantan-ej-dictionaryを使ってみました。

しかし、複数形や過去形だとkantan-ej-dictionaryにヒットしないため、原型でヒットしなかった場合はjavascript-lemmatizerを使って英単語を原型に戻し、再度検索しました。

なんとこれらライブラリはいずれもローカルだけで動きます。しゅばらしい。。。

あとは取得した英単語のレベルを使い、最もユーザーのレベルにあった英単語を出題します。
実際のコードが下記です。英単語ごとにループを回し、以下の処理を行います。

let candidates = []; // 問題にする単語の候補(単語のインデックス番号が入る)
let min_diff = 99;

cc.words.forEach((word,index)=>{

  const original_word = removeSymbol(word);
  let result = kanten_dictionary[original_word]; // まずは英単語を辞書で検索

  if(!result || !result.svl_level){
    // 辞書にヒットしなかった場合、英単語を原型に戻す
    const lemma = LEMMATIZER.lemmas( original_word );
    if(lemma.length==0){
      return false; // 原型に戻せなかった場合
    }else{
      result = lemma[0][0];
    }
    result = kanten_dictionary[result]; // 原型で辞書を検索
    if(!result || !result.svl_level){
      return false; // 原型でも辞書にヒットしなかった場合
    }
  }

  // 単語のレベルとユーザーのレベルの差を求める
  const diff = Math.abs( result.svl_level - USER_SVL_LEVEL );
  if( diff<min_diff || min_diff==99 ){
    // よりレベルの近い単語が見つかった場合、その単語を候補にする
    min_diff = diff;
    candidates = [];
    candidates.push(index);
  }else if( diff==min_diff ){
    // レベルの差が同じ単語が見つかった場合、その単語を候補に入れる
    candidates.push(index);
  }

});

あとは出題する英単語をinputタグに変え、動画の再生時間に合わせて字幕をUIに出力します。入力の判定処理やキーイベントなども書いて、、、完成!!!せず、本日を迎えてしまいました。

実は一旦ストアへリリースしたのですが、

  • このテキトーなUIを改善しなきゃなぁ
  • ユーザーのレベルは決め打ちじゃなくて動的に決めたいなぁ
  • 英単語の和訳も表示したいなぁ
  • 間違えた単語をセーブして復習できるようにしたいなぁ

などの欲が出た結果、このアドベントカレンダーに間に合わなくなりました。ごぺんなさい🐧

ここからはストアへの公開についてです。

Chromeウェブストアへの公開方法

Q: ストアへの登録は難しいですか?
A: 面倒くさいですが難しくはありません。

1 まずはデベロッパー登録($5)

無料で公開する場合もデベロッパー登録が必要です。氏名・住所・電話番号等を入力し、登録料5ドルをクレジットカードで支払います。登録料は1回払えばOKです。

2 作った拡張機能を登録

拡張機能の名前や説明、販売する国や金額を入力していきます。途中で縦横比別にサムネイル画像を登録していくのですが、これがとても面倒くさかったです。アイコンやスクリーンショット含めて5種類も画像を作らされました。

3 Googleによる審査を待つ

無料公開でもGoogleによる審査があります。私の場合は公開申請後1日以内に通ることが多かったです。

これでようやく公開!(してないけど)
記事本文はここまでです。

謝辞

共同開発者の@motonari728に深く感謝申し上げます。また一緒に食事でも行きましょう。

もっとChrome拡張機能について知りたくなったら(参考文献)

QiitaにはChrome拡張機能に関する記事がたくさんあります。はじめに読むべき2本をオススメとして紹介します。

次回予告

明日は@252525さんです。お楽しみに!
最後までお読みいただきありがとうございました。

17
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?