YouTubeやdアニメなどの動画を操作できるショートカットを追加するChrome拡張を作った。

# なぜ作ろうとしたか
動機としては、私の使っているマウス(MX Master)で再生・一時停止のジェスチャーを追加できるのですが、そのジェスチャーでYouTubeやdアニメなどの動画を再生・一時停止できるようにしたかったからです。

# どんな物ができたか
結果としては「YouTube、ニコニコ動画、dアニメ、~~Amazonプライムビデオ~~、bilibili、Gyao、FOD、Hulu、paravi」の再生・一時停止に対応できました。(おそらく大抵のサイトで使える)
Windowsを想定してましたが、Vivaldiを利用してMacbookのタッチバーでのコントロールも可能でした。また、Bluetoothヘッドホンなどの再生・一時停止ボタンもこの拡張機能で受け取れるみたいです。

# Chromeウェブストア
動画再生・一時停止ショートカット

# 実装方法など
GitHubにもソースコードはありますが、説明してみようとおもいます。

## ファイルの構成
- manifest.json
- content_back.js
- background.js
- icon.png

だけですね。ショートカットの設定はChrome標準のものを使ったので簡単になりました。
そのため、この拡張機能でUIは追加してません。

## manifest.json

```json:manifest.json
{
    "manifest_version": 2,
    "name": "動画再生・一時停止ショートカット",
    "short_name": "PlayPauseCut",
    "description": "YouTube、ニコニコ動画、dアニメ、Amazonプライムビデオ...etc.などの再生・一時停止をChromeのショートカットキーでコントロール出来るようにする拡張機能です。",
    "version": "1.0.2",
    "icons": {
        "128": "icon.png"
    },
    "background": {
        "scripts": [
            "background.js"
        ],
        "persistent": true
    },
    "permissions": [
        "tabs"
    ],
    "content_scripts": [
        {
            "matches": [
                "https://www.youtube.com/*",
                "https://www.nicovideo.jp/*",
                "https://www.amazon.co.jp/*",
                "https://anime.dmkt-sp.jp/animestore/sc_d_pc*",
                "https://www.bilibili.com/video/*",
                "https://gyao.yahoo.co.jp/title/*",
                "https://fod.fujitv.co.jp/*",
                "https://www.happyon.jp/*",
                "https://www.paravi.jp/watch/*",
                "https://www.netflix.com/*",
                "https://video.unext.jp/*"
            ],
            "js": [
                "content_back.js"
            ]
        }
    ],
    "commands": {
        "toggle-play-pause-all": {
            "description": "すべて再生・一時停止(トグル)",
            "global": true
        },
        "toggle-pause-all": {
            "description": "すべて一時停止",
            "global": true
        },
        "toggle-play-all": {
            "description": "すべて再生",
            "global": true
        },
        "toggle-play-pause-active": {
            "description": "最後に使用したウィンドウのみ再生・一時停止(トグル)",
            "global": true
        },
        "toggle-play-pause-pin": {
            "description": "ピン止めしてるタブのみ再生・一時停止(トグル)",
            "global": true
        }
    }
}
```
特に説明することもないと思いますが`content_scripts`は、ここでURLのサイトへ接続した際に実行されるJavaScriptを指定できます。
`commands`はショートカットについて指定できます。`toggle-play-pause-all`とかは自分で名前を決めることができて、JavaScript側からどのショートカットが使われたか判定できます。
`permissions`でタブを追加してますが、これはタブがアクティブじゃない場合でもコントロールしたかったからです。このおかげで、タブがいっぱいあるなかから動画のタブを探して一時停止。なんていう面倒なことはしなくて良くなりました。

## background.js

```javascript:
chrome.commands.onCommand.addListener(function (command) {//ショートカットリスナー
    switch (command) {
        case "toggle-play-pause-all":
            getQuery({});//クエリのパラメータ、無し
            break;
        case "toggle-pause-all":
            getQuery({ audible: true });//クエリのパラメータ、音再生中のタブ
            break;
        case "toggle-play-all":
            getQuery({ audible: false })//クエリのパラメータ、音非再生中タブ;
            break;
        case "toggle-play-pause-active":
            getQuery({ lastFocusedWindow: true });//クエリのパラメータ、最後にフォーカスのあったタブ
            break;
        case "toggle-play-pause-pin":
            getQuery({ pinned: true });//クエリのパラメータ、ピン止めされてるタブ
            break;
    }
});
function getQuery(urls) {
    urls.url = ["https://www.youtube.com/*",
        "https://www.nicovideo.jp/*",
        "https://www.amazon.co.jp/*",
        "https://anime.dmkt-sp.jp/animestore/sc_d_pc*",
        "https://www.bilibili.com/video/*",
        "https://gyao.yahoo.co.jp/title/*",
        "https://fod.fujitv.co.jp/*",
        "https://www.happyon.jp/*",
        "https://www.paravi.jp/watch/*",
        "https://www.netflix.com/*",
        "https://video.unext.jp/*"];
    chrome.tabs.query(urls, function (tabs) {//クエリポン！
        getTab(tabs);
    });
}
function getTab(tabs) {
    tabs.forEach(function (tab) {
        if (~tab.url.indexOf("https://www.nicovideo.jp/")) {//ニコニコ動画だったら。
            chrome.tabs.sendMessage(tab.id, {
                mode: "niconico"
            }, function (msg) { });
        } else {
            chrome.tabs.sendMessage(tab.id, {//それ以外
                mode: "other"
            }, function (msg) { });
        }
    });

}
```
コメントはQiita用に即席で付けてみました。
`getQuery()`の引数はタブを取得する際のパラメータを指定してます。
このパラメータで各種ショートカット別に操作するタブを分けてます。
そのパラメータに`.url=[]`で対象のURLを絞り込んでます。
あとは`chrome.tabs.sendMessage()`で対象のタブにメッセージを送ってます。
そのメッセージを後述するJavaScript側で受け取って再生・一時停止を切り替えます。

## content_back.js
```javascript:content_back.js
chrome.runtime.onMessage.addListener(function (msg, sender, sendResponse) {//メッセージリスナー
    if (msg.mode === "niconico") {
        var playButton = document.getElementsByClassName("ActionButton ControllerButton PlayerPlayButton")[0];
        var pauseButton = document.getElementsByClassName("ActionButton ControllerButton PlayerPauseButton")[0];
        if (playButton != undefined) {//プレイボタンがあったら
            playButton.click();       //クリック！
        } else {                      //プレイボタンがundefinedだったらPauseボタンがあるはず
            pauseButton.click();      //なのでクリック！
        }
        sendResponse("ok");
    } else {
        var player = document.getElementsByTagName("video")[0];//videoタグを取得
        videoPlayPause(player);
        sendResponse("ok");
    }
});
function videoPlayPause(element) {
    if (element.paused) {//一時停止中なら
        element.play();
    } else {             //再生中なら
        element.pause();
    }
}
```
このcontent_back.jsはマニフェストで指定したURLのページすべてで読み込まれてます。
そのため、メッセージリスナーでbackground.jsからメッセージを受け取ったら処理がされるわけですが。
ニコニコ動画だけはHTML5のvideoタグがなかったので再生・一時停止ノボタンをclassで指定してクリックするという暴挙に出ました。
他のプレイヤーはHTML5のvideoタグが使われてるので、そのプレイヤーが再生中かどうかを取得し、再生・一時停止してます。

さて、冒頭の対応サイトでAmazonプライムビデオが打ち消し線になってましたが、理由としては`var player_dani = document.getElementsByTagName("video")[0];`この部分に問題があり、Amazonプライムビデオはどうやらこのvideoタグが複数あり、なおかつメインのコンテンツのvideoタグの順番が変わることもある。つまり添え字の部分が`[1]`の時もあれば`[0]`の時もあるので動きが不安定です。
良い解決策などあれば教えていただけると幸いです。

# 問題点
複数の動画タブがあった場合どうすれば良いか良い考えが思いつかなかったので全部のタブを操作してしまう。
Windowsの場合ショートカットがうまく受け取れない。※これはChromeとWindows自体の問題？
Macは自分で設定した大抵のショートカットはうまく動いた。
強引なコードを書いている気がする。

# 参考文献的URL
- [Chrome APIs - Extension APIs](https://developer.chrome.com/extensions/api_index)
- [Chrome拡張の開発方法まとめ　その1：概念編](https://qiita.com/k7a/items/26d7a22233ecdf48fed8)
- [Chromeブラウザの拡張機能を作ってみたい初心者向けに開発方法を紹介！【サンプルあり】](https://qiita.com/guru_taka/items/37a90766f4f845e963e5#_reference-e8a2c14b640f7f590a27)