Netflix Enhanced Progress Bar

Netflixの動画にプログレスバーを追加します。

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name                Netflix Enhanced Progress Bar
// @name:en             Netflix Enhanced Progress Bar
// @name:ja             Netflix Enhanced Progress Bar
// @name:zh-CN          Netflix Enhanced Progress Bar
// @name:ko             Netflix Enhanced Progress Bar
// @name:ru             Netflix Enhanced Progress Bar
// @name:de             Netflix Enhanced Progress Bar
// @description         Netflixの動画にプログレスバーを追加します。
// @description:en      Adds a progress bar to Netflix videos.
// @description:ja      Netflixの動画にプログレスバーを追加します。
// @description:zh-CN   为 Netflix 视频添加进度条。
// @description:ko      Netflix 동영상에 진행 막대를 추가합니다.
// @description:ru      Добавляет индикатор прогресса к видео на Netflix.
// @description:de      Fügt einen Fortschrittsbalken zu Netflix-Videos hinzu.
// @version             1.0.0
// @author              Yos_sy
// @match               *://*.netflix.com/*
// @namespace           http://tampermonkey.net/
// @icon                https://www.google.com/s2/favicons?sz=64&domain=netflix.com
// @license             MIT
// @grant               none
// ==/UserScript==

(function () {
  "use strict";

  // 定数定義
  const PROGRESS_UPDATE_INTERVAL = 1000; // プログレスバー更新間隔(ミリ秒)
  const VIDEO_CHECK_INTERVAL = 1000; // ビデオ要素チェック間隔(ミリ秒)

  class NetflixProgressBar {
    constructor() {
      this.progressBar = null; // カスタムプログレスバーの要素
      this.progress = null; // プログレスバーの進行状況を示す要素
      this.videoElement = null; // 現在のビデオ要素
      this.updateInterval = null; // プログレスバー更新のためのインターバルID
      this.videoCheckInterval = null; // ビデオ要素チェックのためのインターバルID
    }

    // 初期化メソッド
    init() {
      this.addStyles(); // カスタムスタイルをページに追加
      this.startVideoCheck(); // ビデオ要素の存在を定期的にチェック
      this.observeDOMChanges(); // DOMの変化を監視
      this.observeURLChanges(); // URLの変更を監視(SPA対応)
    }

    // スタイルを追加
    addStyles() {
      const style = document.createElement("style");
      style.textContent = `
        .netflixEnhancedProgressBar {
          position: absolute;
          bottom: 0;
          left: 0;
          width: 100%;
          height: 5px;
          background-color: rgba(128, 128, 128, 0.7);
          z-index: 9999;
        }
        .netflixEnhancedProgress {
          width: 0%;
          height: 100%;
          background-color: #e50914;
          transition: width 0.25s linear;
        }
      `;
      document.head.appendChild(style); // スタイルをheadに追加
    }

    // プログレスバー要素を作成
    createProgressBar() {
      this.progressBar = document.createElement("div");
      this.progressBar.className = "netflixEnhancedProgressBar"; // カスタムプログレスバーのクラス名を設定
      this.progress = document.createElement("div");
      this.progress.className = "netflixEnhancedProgress"; // プログレスバーの進行状況を示すクラス名を設定
      this.progressBar.appendChild(this.progress); // プログレスバーに進行状況を示す要素を追加
      return this.progressBar;
    }

    // プログレスバーの進行状況を更新
    updateProgress() {
      if (!this.videoElement || !this.progress) return; // ビデオ要素またはプログレスバーが存在しない場合は終了
      const percentage =
        (this.videoElement.currentTime / this.videoElement.duration) * 100; // 現在の再生時間を全体の再生時間で割り、パーセンテージを計算
      this.progress.style.width = `${percentage}%`; // プログレスバーの幅を更新
    }

    // プログレスバーの定期更新を開始
    startProgressUpdate() {
      this.stopProgressUpdate(); // 既存の更新を停止
      this.updateInterval = setInterval(
        () => this.updateProgress(),
        PROGRESS_UPDATE_INTERVAL
      ); // プログレスバーの更新を定期的に実行
    }

    // プログレスバーの更新を停止
    stopProgressUpdate() {
      if (this.updateInterval) {
        clearInterval(this.updateInterval); // インターバルをクリア
        this.updateInterval = null;
      }
    }

    // ビデオ要素の変更を処理
    handleVideoChange() {
      try {
        const newVideoElement = document.querySelector("video"); // 現在のページに存在するビデオ要素を取得
        if (newVideoElement !== this.videoElement) {
          this.cleanUp(); // 古いリソースをクリーンアップ
          this.videoElement = newVideoElement; // 新しいビデオ要素を設定
          if (this.videoElement) {
            this.createAndAttachProgressBar(); // プログレスバーを作成し、ビデオ要素に付加
            this.startProgressUpdate(); // プログレスバーの定期更新を開始
          }
        }
      } catch (error) {
        console.error("Error in handleVideoChange:", error); // エラーをコンソールに出力
      }
    }

    // プログレスバーを作成し、ビデオ要素に付加
    createAndAttachProgressBar() {
      if (!this.progressBar) {
        this.progressBar = this.createProgressBar(); // プログレスバーが存在しない場合、新規作成
      }
      this.videoElement.parentNode.appendChild(this.progressBar); // プログレスバーをビデオ要素の親要素に追加
    }

    // リソースのクリーンアップ
    cleanUp() {
      this.stopProgressUpdate(); // プログレスバーの定期更新を停止
      if (this.progressBar && this.progressBar.parentNode) {
        this.progressBar.parentNode.removeChild(this.progressBar); // プログレスバーをDOMから削除
      }
      this.progressBar = null; // プログレスバーの参照をクリア
      this.progress = null; // プログレスバーの進行状況要素の参照をクリア
    }

    // ビデオ要素の存在を定期的にチェック
    startVideoCheck() {
      this.videoCheckInterval = setInterval(
        () => this.handleVideoChange(),
        VIDEO_CHECK_INTERVAL
      ); // 定期的にビデオ要素の変更をチェック
    }

    // DOMの変化を監視
    observeDOMChanges() {
      const observer = new MutationObserver(() => this.handleVideoChange()); // DOM変化の監視を設定
      observer.observe(document.body, { childList: true, subtree: true }); // body要素以下の全ての変更を監視
    }

    // URLの変更を監視(SPA対応)
    observeURLChanges() {
      let lastUrl = location.href; // 最後に確認したURLを保存
      new MutationObserver(() => {
        const url = location.href;
        if (url !== lastUrl) {
          // URLが変更された場合
          lastUrl = url; // 新しいURLを保存
          this.handleVideoChange(); // ビデオ要素の変更を処理
        }
      }).observe(document, { subtree: true, childList: true }); // ドキュメント全体の変更を監視
    }
  }

  // プログレスバーのインスタンスを作成し、初期化
  const progressBar = new NetflixProgressBar();
  progressBar.init();
})();