YouTube から興味ない動画を徹底的に消す。「Neo's YouTube Filter」を作った
以前から、YouTube 上の興味のない動画をいかに非表示にするか、ということに格闘してきた。
- 過去記事 : 2021-05-12 YouTube 環境を向上させるために入れている Chrome 拡張機能
- 過去記事 : 2022-01-10 YouTube ホームの見たくない動画を消すための術
- 過去記事 : 2022-07-05 YouTube トップの「興味なし」「チャンネルをおすすめに表示しない」を爆速押下する Bookmarklet
以前は Channel Blocker という拡張機能を使っていたのだが、コレが上手く動かなくなり、色々としびれを切らしたので自作するに至った。その名も「Neo's YouTube Filter」。
今回も例によって、Cloudflare Workers + D1 で「非表示にしたい情報」を管理している。ウェブ管理画面もあるが、本体はそこではない。
今回は youtube.com ドメイン上で自分の作ったスクリプトを動かして、見たくない動画の DOM を非表示にしつつ、そのための情報は Cloudflare Workers から取得するということが必要だったので、CORS 設定が重要になった。
public/_headersというファイルを置くと Cloudflare Workers 上での HTTP リクエストヘッダを編集できる。コレを利用して YouTube のドメインから Workers で提供する API や「メインスクリプト JS ファイル」へのアクセスを許可するようにした
/*
Access-Control-Allow-Origin: *
wrangler.jsoncでrun_worker_firstを指定することで、/apiで始まる API エンドポイントに対して SPA を介さず直アクセスができるようにした
{
"assets": {
"directory": "./build/client",
"not_found_handling": "single-page-application",
// Workers を先に動かすパス (SPA にフォールバックしないようにする)
"run_worker_first": [
"/api/*"
]
}
- Hono のエントリポイントである
server/index.tsの書き方を調整して、/api配下へのリクエストが SPA としてフォールバックされず到達するようにし、CORS 設定も入れた
import { Hono } from 'hono';
import { createHonoServer } from 'react-router-hono-server/cloudflare'; // `react-router-hono-server` というパッケージを使用
export const app = new Hono();
// CORS を許可する
app.use('*', cors({
origin: '*',
allowMethods: ['GET', 'POST', 'PATCH', 'PUT', 'DELETE', 'OPTIONS'],
allowHeaders: ['Content-Type', 'Authorization']
}));
app.route('/api', api); // ← API を別途実装…
export default await createHonoServer({ app }); // ← この書き方が重要だった・こうしないと `/api` 配下にブラウザアクセスしても到達できなかった
ということで、通常であれば別ドメインから Workers の API を直コールなんてされたくないので塞がっているワケだが、ココまでやれば開放できた。
「メインスクリプト JS ファイル」という文言が途中登場したが、コレは要するに、動画を非表示にする処理を含んだブックマークレットみたいなモノ。以下の2つの方法でこのファイルを取得し実行できる。
- ブックマークレットとして「メインスクリプト JS ファイル」を注入する
javascript:(async () => {
try {
const policy = trustedTypes.createPolicy('neos21-ytf', { createScript: code => code }); // YouTube 上で任意の JS コードを実行するためにはこの「ポリシー作成」が必要だった
const response = await fetch('https://ytf.neos21.workers.dev/ytf.js'); // 実際の様々な処理を持つコードを Workers からダウンロードする
const code = await response.text();
eval(policy.createScript(code)); // `eval()` で取得した JS ファイルを実行する
}
catch(error) {
console.error('スクリプト読み込みに失敗しました', error);
}
})();
- Tampermonkey スクリプトの
@requireを利用して「メインスクリプト JS ファイル」を注入する
// ==UserScript==
// @name Neo's YouTube Filter
// @namespace https://neos21.net/
// @version 2026-09-22
// @description Neo's YouTube Filter
// @author Neos21
// @match https://www.youtube.com/*
// @match https://m.youtube.com/*
// @require https://ytf.neos21.workers.dev/ytf.js
// @icon https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @run-at document-idle
// @grant none
// @noframes
// ==/UserScript==
どちらも ytf.js という、自分が作った「メインスクリプト JS ファイル」を読み込んでいる点が重要。
PC ブラウザの場合は Tampermonkey を使うことで、自分が用意した JS ファイルを常時読み込んで起動できる。
iPhone Safari などのように Tampermonkey が使えない環境では、ブックマークレットを起動する手間は一つ増えるが、全く同じ JS ファイルを使えるので同じ機能が動く。
こうして読み込んだメインスクリプト ytf.js は何をしているかというと、「ホーム」「動画ページ」「検索結果ページ」を表示している時だけ、「見たくない動画を非表示にする」処理を動かしている。
「見たくない動画」は、
- 動画 ID の単位でブロック
- チャンネル単位でブロック (識別子は「チャンネル ID」と「ハンドル」の2種類があるのだが、どちらかが分かれば良い)
- 動画タイトルとチャンネル名が、指定の文字列や正規表現に引っかかったらブロック
という3つのルールで封じることが出来る。つまり、
- 「ビートルズ」に関する動画は一生見たくないので、文字列でルール追加してブロック
- ハングル表記の動画は見る気がないので、
/\p{Script=Hangul}/iuという正規表現でブロック - このチャンネルはもう二度と見たくないので、チャンネル単位でブロック
- チャンネルごと非表示にするまでではないけど、この動画は頻繁にオススメされて鬱陶しいので、動画 ID 単位でブロック
といったことが出来る。
ブロックするルールはウェブ画面から追加・編集・削除が出来るが、YouTube 閲覧中も素早くブロックができるように、動画サムネイル上に「この動画をブロック」「このチャンネルをブロック」というボタンを重ねて表示している。コレが「メインスクリプト」による機能で、ボタンを押せば Workers の API に対して POST 操作を行い、ブロックしたいルールを DB 追加できるというワケ。
ブロックするルールは LocalStorage にキャッシュを持ってるので、API コール数も節約するようにしている。
おおよそやりたいことと実現方法は自分の中で想像できていたが、YouTube のページごとの DOM 構造を調べたりするのが大変で、ココはガッツリ Codex に調査・実装させた。
以前だったら自分で CSS クラス名や DOM 構造を調べて、「どの親要素まで上がって display: none すればいいか」とかを調整するしかなかったけど、今回は全く自分で調査せずに済んだ。AI さまさまですね。
スマホで表示した時の DOM 構造の違いとかのバグ取りも Codex がやってくれた。Codex が iPhone Safari の内容を直接見たりはできないので、「このブックマークレットを実行して、ページの情報がどのように取得できたか教えてください」と言われて、Codex の吐いた調査用のブックマークレットを実行して結果をコピーして答えてあげたりしていた。いや~マジで「コレがやりたかったんよ!」っていうことをやってくれる相方が出来た感じで、Codex スゲェっすね。w