3
0

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.

特定の文字列を含む動画を非表示にするChrome拡張機能を作った

Posted at

作ったもの

設定した文字列を含むタイトルの動画を非表示にするchrome拡張機能です。

拡張機能のアイコンをクリックするとテキストエリアが現れるので、そこにフィルタリングしたい文字列を入力して保存ボタンを押すだけで設定は完了です。

あとは自動で画面遷移時に対象動画を非表示にしてくれます。

ソースコードはこちら

デモ

設定画面

f.png

設定前

前.png

設定後

あと.png

作った動機

夏と言えばホラー!
私はホラーが大の苦手なので怖い動画は見たくない!
ならば消してしまえ!という感じで作りました。

仕組み

Event Pageでタブの変更を検知、Content ScriptでDOMの削除と監視を行っています。

event.ts
chrome.tabs.onUpdated.addListener((_, info, tab) => {
  const url: string = tab.url ?? '';

  // タブが読み込み完了かつurlに`www.youtube.com`を含む
  if (info.status === 'complete' && 0 < url.indexOf('www.youtube.com')) {
    chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      const currentUrl: string | undefined = tabs[0]?.url;
      const tabid: number | undefined = tabs[0]?.id;
      if (currentUrl != null && tabid != null) {
        // 指定タブにurlを送信(content scriptが受け取る)
        chrome.tabs.sendMessage(tabid, { message: currentUrl });
      }
    });
  }
});
content.ts

chrome.runtime.onMessage.addListener((request, _, sendResonse) => {
  const url: string = request.message;

  // ストレージに保存してある設定した文字列を取得
  chrome.storage.local.get(['filter'], (storage) => {
    if (storage.filter != null) {
      const filters: string[] = storage.filter
        .split('\n')
        .filter((v: string) => v !== '');
      const querySelector: string = '#video-title';

      let targetElement: HTMLElement | null | undefined = null;

      // 監視対象の要素を取得
      if (
        ~url.indexOf('/feed/subscriptions') ||
        url === 'https://www.youtube.com/'
      ) {
        targetElement = document.getElementById('contents');
      } else if (~url.indexOf('/watch?')) {
        targetElement = document
          .getElementById('related')
          ?.querySelector('#items');
      }

      if (targetElement != null) {
        const removeElement = remove(querySelector)(filters);
        // 初回のみすでに読み込んである動画要素に対してフィルター処理を実行
        removeElement(targetElement.childNodes as NodeListOf<HTMLElement>);

        // 監視対象要素を監視
        observe(removeElement, targetElement);
      }
    }
  });

  sendResonse();

  return;
});

const remove: (
  querySelector: string
) => (filters: string[]) => (targetNodes: NodeListOf<HTMLElement>) => void =
  (querySelector) => (filtres) => (targetNodes) => {
    targetNodes.forEach((node) => {
      node.querySelectorAll(querySelector).forEach((titleElement) => {
        filtres.forEach((filter) => {
          if (~titleElement.innerHTML.indexOf(filter)) {
            node.remove();
          }
        });
      });
    });
  };

function observe(
  func: (nodeList: NodeListOf<HTMLElement>) => void,
  targetElement: Node
) {
  const mutationObserver = new MutationObserver((mr) => {
    func(mr[0].addedNodes as NodeListOf<HTMLElement>);
  });

  const observeConfig: object = {
    attributes: false,
    attributeOldValue: false,
    characterData: false,
    characterDataOldValue: false,
    childList: true,
    subtree: false,
  };

  mutationObserver.observe(targetElement, observeConfig);
}

最後に

この拡張機能はストアに出す予定はないので、試してみたい方はリポジトリからDLしてみてください。

今回初めてTypeScriptを使っての開発をしてみました。
型のおかげで開発速度がそれなりに早くできたかと思います。(コードはイケてないのでもっと勉強が必要)

参考

API Reference - Chrome Developers

Chrome Extension の作り方 (その1: 3つの世界)

3
0
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
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?