AnimeHay

Tự động lưu và khôi phục lịch sử xem phim khi AnimeHay đổi tên miền mới

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name        AnimeHay
// @namespace   https://greasyfork.org/en/scripts/492147-animehay
// @match       *://animehay.*/thong-tin-phim/*
// @match       *://animehay.*/xem-phim/*
// @match       *://hhtq3d*.*/*/
// @match       *://hhtq3d*.*/watch/*/
// @grant       GM_getValue
// @grant       GM_setValue
// @version     1.7
// @author      HVD
// @icon        https://i.imgur.com/EiOLZtO.png
// @description Tự động lưu và khôi phục lịch sử xem phim khi AnimeHay đổi tên miền mới
// @license MIT
// ==/UserScript==

function waitForElements(selector, observeElement) {
    return new Promise((resolve, reject) => {

      const elements = document.querySelectorAll(selector);
      if (elements.length > 0) return resolve(elements);

      observeElement = document.querySelector(observeElement) || document.body;
      const observer = new MutationObserver(() => {
        const elements = observeElement.querySelectorAll(selector);
        if (elements.length > 0) {
          observer.disconnect();
          resolve(elements);
        }
      });

      observer.observe(observeElement, {
        childList: true,
        subtree: true,
      });

      setTimeout(() => {
        observer.disconnect();
        reject(new Error(`Elements not found: ${selector}`));
      }, 10000);  // Timeout after 10 seconds
    });
}

const getValue = (key) => JSON.parse(GM_getValue(key, '{}'));
const setValue = (key, value) => GM_setValue(key, JSON.stringify(value));

class AnimeHay {
    constructor() {
        this.keyName = 'AnimeHay';
        this.maxHistory = 20;
        this.pathName = location.pathname;
        this.watchedMovies = getValue(this.keyName);
    }

    save(watchedMovies) {
        setValue(this.keyName, watchedMovies);
    }

    markViewed(watchedEps, name) {
        if (watchedEps.size === 0) return;

        if (watchedEps.size > this.maxHistory) watchedEps.delete([...watchedEps][0]);

        const epList = document.querySelectorAll('.list-item-episode a');
        for (const ep of watchedEps) {
            for (const item of epList) {
                const href = item.getAttribute('href');
                if (href.includes(ep)) {
                    item.style.backgroundColor = 'rgb(25 81 75)';
                    break;
                }
            }
        }
    }

    handleMovieInfoPage() {
        const name = this.pathName.replace(/\/thong-tin-phim\/|\-(\d+).html/g, '');
        const watchedEps = new Set(this.watchedMovies[name] ?? []);
        this.markViewed(watchedEps, name);
    }

    handleWatchMoviePage() {
        const [name, episode] = this.pathName.replace(/\/xem-phim\/|\.html/g, '').split('-tap-');
        const watchedEps = new Set(this.watchedMovies[name] ?? []);
        this.markViewed(watchedEps, name);

        if (watchedEps.has(episode)) return;

        watchedEps.add(episode);
        this.watchedMovies[name] = [...watchedEps];
        setValue(this.keyName, this.watchedMovies);
    }

    init() {
        if (this.pathName.includes('/thong-tin-phim/')) {
            this.handleMovieInfoPage();
        } else if (this.pathName.includes('/xem-phim/')) {
            this.handleWatchMoviePage();
        }
    }
}

class HHTQ {
    constructor() {
        this.keyName = 'HHTQ';
        this.maxHistory = 20;
        this.pathName = location.pathname;
        this.watchedMovies = getValue(this.keyName);
    }

    markViewed(watchedEps, name) {
        if (watchedEps.size === 0) return;

        if (watchedEps.size > this.maxHistory) watchedEps.delete([...watchedEps][0]);

        waitForElements('.halim-list-eps > li span', '#halim-list-server').then(epList => {
            for (const ep of watchedEps) {
                for (const item of epList) {
                    if (item.innerText == ep) {
                        item.style.backgroundColor = 'rgb(25 81 75)';
                        break;
                    }
                }
            }
            document.querySelector('#halim-list-server ul li span.active').style.backgroundColor = '';
        });

    }

    handleMovieInfoPage() {
        const name = this.pathName.replace(/^\/|\/$/g, '');
        const watchedEps = new Set(this.watchedMovies[name] ?? []);
        this.markViewed(watchedEps, name);
    }

    handleWatchMoviePage() {
        const [, name, episode] = this.pathName.match(/\/watch\/([^/]+)-eps-(\d+)-server-\d+\//);
        const watchedEps = new Set(this.watchedMovies[name] ?? []);
        this.markViewed(watchedEps, name);

        if (watchedEps.has(episode)) return;

        watchedEps.add(episode);
        this.watchedMovies[name] = [...watchedEps];
        setValue(this.keyName, this.watchedMovies);
    }

    init() {
        if (/^\/[^/]+\/$/.test(this.pathName)) {
            this.handleMovieInfoPage();
        } else if (this.pathName.includes('/watch/')) {

            if (!window._jwplayer) {
                const self = this;
                window._jwplayer = unsafeWindow.jwplayer;
                unsafeWindow.jwplayer = function(id) {
                  if (self.pathName != location.pathname) {
                    self.pathName = location.pathname;
                    self.handleWatchMoviePage();
                  }
                  return window._jwplayer(id);
                };
            }

            this.handleWatchMoviePage();
        }
    }
}

if (location.hostname.includes('animehay')) (new AnimeHay()).init();
else (new HHTQ()).init();