はじめに
私は、よく勉強しようと YouTube を見ても、おすすめに流れてくる誘惑をクリックしてしまいます。他のウェブサイトは Norton Family さん(カテゴリーによるウェブサイトの閲覧制限)と Microsoft Family Safety さん(パソコン全体の起動動時間制限)のお力を借りて見れなくしているのですが、YouTube はチャンネルごとに設定することができませんでした...。
もちろん、自制心をもてという話ですが、少しでも「あ、みたらだめだ💦」と気づけるように警告を表示する Chrome 拡張機能を作りました。学生の皆様は、ぜひご利用ください!
💡 Chrome で勉強用のユーザーを作成してもいいですが、Windows のユーザーアカウントごと分けておくことをおすすめします。
作品:Chrome 拡張機能「YouTube Study Focus」
作る過程をご紹介する前に、まずは出来上がったものを紹介します!
こちらからお試しください↓↓
作る過程
2年ほど前に作ろうと思って失敗しただけで、はじめての拡張機能開発だったので、「Chrome 拡張機能 作り方」でググってみました。
すると、Chrome拡張の作り方 (超概要)というめっちゃ分かりやすい記事に出会えました!本当にありがとうございました(泣
自分の場合は、ポップアップというよりもページを変えたかったので、少し変えて作りました。(一度作ってから知りました)
適当なフォルダを作って「manifest.json」と諸ファイルを作成
拡張機能用にフォルダを作成して、その中に src
フォルダと以下のようなファイルを作成します。
yt-study-focus/
└ src/
├ manifest.json
├ popup-dialog.html
├ whiteList.js
├ youtube.js
├ jquery-3.6.1.min.js
└ images/
└(後でアイコンを入れる)
manifest.json
「manifest.json」には、次のように記載します。
{
"name": "YouTube Study Focus",
"version": "0.0.1",
"manifest_version": 3,
"description": "勉強系以外の YouTube チャンネルの動画を再生する前に警告します",
"action": {
"default_popup": "popup-dialog.html"
},
"icons": {
"16": "icon/icon16.png",
"48": "icon/icon48.png",
"128": "icon/icon128.png"
},
"content_scripts": [
{
"js": [
"jquery-3.6.1.min.js",
"whiteList.js",
"youtube.js"
],
"matches": [
"https://youtube.com/*",
"https://www.youtube.com/*"
]
}
],
"author": "nao_hanpen"
}
jquery-3.6.1.min.js と whiteList.js
これは jQuery と勉強系チャンネルの配列を書いただけのファイルで、本質ではないので今回は省略します
youtube.js
ここで、①動画を再生されたら、②チャンネル ID を取得して、③ホワイトリストになければ警告を表示するということをします。
深夜テンションで書いたので汚いです、、書き方勉強します。
$(function() {
// 読み込んだら、loadCheck() を実行
loadCheck();
});
// YouTube は Single Page App 的なこと(ページ遷移しないでコンテンツを変更)をするので、タイトルの部分が変更されないかを監視します。
// 変更があったときも、loadCheck() を呼ぶので、このときに2重に監視しないように、監視済みかを設定しておきます。もっとスマートな方法もあると思います。
var is_observed = false;
/**
* チャンネル ID を取得して、ホワイトリストになければ警告を表示する
*/
function loadCheck() {
// 現在の URL を取得 (動画ページかどうかの判定に使用)
var now_url = location.href;
console.log( now_url );
// VideoID を取得
var videoId = getVideoId(now_url);
if ( videoId === "" ) {
// VideoID が取得できなければ 動画ページでないと判断して処理を停止
console.log("このページには動画が含まれません");
return;
}
console.log("VideoID: " + videoId);
// 処理中は再生を止める
var is_prevent_play = true; // 停止中かどうか(video が playing されたら停止スクリプトを実行するかどうか)
// ロード、再生、再生中に
$('video').on('load play playing', function(e) {
// 動画を停止
if( is_prevent_play ) $('video')[0].pause();
});
// 処理中にロード画面を追加
showLoad();
// チャンネル ID を取得
// 画面中の要素から取得しているので、YouTube は順にロードするようになっていて時間がかかる
// 1回目では取得できない。だいたい2~3回必要。
var channelURL = null; // チャンネル URL
var channelId = null; // チャンネル ID
var channelGetCount = 0; // 取得試行回数
getChannelId();
/**
* チャンネル ID を取得
*/
function getChannelId() {
channelGetCount++; // 試行回数を加算
if( $('#channel-name a.yt-simple-endpoint.style-scope.yt-formatted-string')[0] !== undefined ){
// 要素が存在したら
// チャンネル URL を取得
channelURL = $('#channel-name a.yt-simple-endpoint.style-scope.yt-formatted-string')[0].href;
console.log( "Channel URL:" + channelURL );
// チャンネル ID を抽出
channelId = channelURL.replace("https://www.youtube.com/channel/","");
channelId = channelId.replace("https://www.youtube.com/c/","");
console.log("ChannelId is #" + channelId);
// チャンネル ID を検証
checkChannelId();
// 要素の監視がまだされていなければ
if( is_observed === false ) {
// 監視する
const observer = new MutationObserver(function () {
var is_prevent_play = true;
loadCheck();
});
observer.observe($("ytd-watch-metadata #title")[0], { childList: true, subtree: true });
// フラグをオンに
is_observed = true;
}
}else{
// 要素が存在しなかったら
if( channelGetCount > 30 ) {
// 30 回以上試行してもできない場合は強制終了
console.log( "チャンネル ID を取得できませんでした。終了します" );
}else{
// 2秒後に再試行
console.log( "チャンネル ID を取得できませんでした。2秒後に再試行します..." );
setTimeout(getChannelId,2000);
}
}
}
/**
* チャンネル ID を検証 (ホワイトリストになければ警告を表示)
*/
function checkChannelId() {
console.log("チャンネル ID を検証します: " + channelId);
if( whiteChannel.indexOf(channelId) < 0 ) {
// ホワイトリストになければ、警告を表示
showBlock();
}else{
// ロード画面を閉じて動画を再生
hideLoad(); // ロード画面を閉じる
is_prevent_play = false; // 停止中フラグをオフに
$('video')[0].play(); // 動画を再生
}
}
/**
* ロード画面を表示
*/
function showLoad() {
$("body").append("<div id='loadingView'></div>");
$("#loadingView")[0].style.cssText = 'background: rgb(72 72 72 / 95%); content: ""; position: fixed; top: 0; left: 0; width: 100%; height: 100%; color: #fff; z-index: 2000; display: flex; justify-content: center; align-items: center; flex-direction: column;';
$("div#loadingView").append("<h1>Loading...</h1>");
}
/**
* ロード画面を非表示
*/
function hideLoad() {
if( $('#loadingView')[0] !== undefined ) {
$("#loadingView").remove();
}
}
/**
* 警告を表示
*/
function showBlock() {
hideLoad();
$("body").append("<div id='sendButton'></div>");
$("#sendButton")[0].style.cssText = 'background: rgb(204 0 0 / 95%); content: ""; position: fixed; top: 0; left: 0; width: 100%; height: 100%; color: #fff; z-index: 2000; display: flex; justify-content: center; align-items: center; flex-direction: column;';
$("div#sendButton").append("<h1>このコンテンツは時間を浪費する可能性があります</h1>");
$("div#sendButton").append("<p>時間を取り戻すことはできません</p>");
$("div#sendButton").append("<div id='agree-btn'>継続して時間を無駄にします</div>");
$("#agree-btn")[0].style.cssText = 'display: inline-block; margin-top: 20px; padding: 10px; background-color: #fff; color: rgb(88 88 88); border-radius: 6px; font-size: 14px; line-height: 1; font-weight: 600; cursor: pointer;';
$("#agree-btn").click(function() {
console.log('無視して動画を再生');
$("#sendButton").remove();
is_prevent_play = false;
$('video')[0].play();
});
}
}
/**
* URL から Video ID を抽出
* @param {str} url URL 文字列
* @return {str} ID または 存在しないときに空
*/
function getVideoId( url ) {
const { search } = new URL(url);
const params = new URLSearchParams(search);
if (params.has('v')) {
return params.get('v');
}
return '';
}
今後
この拡張機能は、今後、
・ユーザー専用ホワイトリスト
・ユーザー専用ブラックリスト
・保護者向け機能
・ホーム画面(おすすめ)から削除する機能
・コメント非表示機能
を実装する予定です。
「こんな機能あったらいいな」というものがありましたらお気軽にコメントください!
参考文献
- 「Chrome拡張の作り方 (超概要)」(2022/10/30 参照) https://qiita.com/RyBB/items/32b2a7b879f21b3edefc
- 「Chrome 拡張機能のマニフェストファイルの書き方」(2022/10/30 参照) https://qiita.com/mdstoy/items/9866544e37987337dc79
- 「Google Developers」(2022/10/30 参照) https://developer.chrome.com/docs/webstore/