Dropout QOL Additions

Enjoy enhanced convenience and organization with the Dropout QOL Additions script. Never lose track of your progress again!

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name        Dropout QOL Additions
// @namespace   Violentmonkey Scripts
// @match       https://www.dropout.tv/*
// @grant       GM.listValues
// @grant       GM.getValue
// @grant       GM.setValue
// @version     1.0
// @author      TheTornadotTian
// @description Enjoy enhanced convenience and organization with the Dropout QOL Additions script. Never lose track of your progress again!
// @license MIT
// ==/UserScript==

(async function dropoutQOL() {
  if(!(await GM.getValue("watchedEpisodeIds"))) GM.setValue("watchedEpisodeIds", []);

  //Video Page Controls
  if(window.unsafeWindow.Page.PROPERTIES.VIEW_TYPE && window.unsafeWindow.Page.PROPERTIES.VIEW_TYPE === "video") {
    const videoId = window.unsafeWindow.Page.PROPERTIES.VIDEO_ID + "";
    const addToWatched = await GM.getValue("watchedEpisodeIds")
    
    if(!addToWatched.includes(videoId)) addToWatched.push(videoId);
    GM.setValue("watchedEpisodeIds", addToWatched);

    const ne = document.createElement("span");
    ne.style = "cursor: pointer"

    ne.onclick = async (e) => {
      const target = (e.target) ? e.target : e.srcElement;

      let watchedEpisodes = await GM.getValue("watchedEpisodeIds");

      if(watchedEpisodes.includes(videoId)) {
        watchedEpisodes = watchedEpisodes.filter(e => e !== videoId)
        target.innerText = "❌";
      } else {
        watchedEpisodes.push(videoId)
        target.innerText = "✅";
      }

      GM.setValue("watchedEpisodeIds", watchedEpisodes);
    }

    const watchedEpisodes = await GM.getValue("watchedEpisodeIds")
    ne.innerText = (watchedEpisodes.includes(videoId)) ? "✅" : "❌";

    document.querySelector("#watch-info > div > div > div.row.margin-vertical-medium > div.column.small-16.medium-8.large-10 > div.contain.margin-top-large.column.small-16 > h5").appendChild(ne);
  }

  //Episode Page Controls
  const episodeContainer = document.querySelector("body > main > section > section.episode-container.video-container.padding-bottom-large.padding-horizontal-medium > div:nth-child(2) > div > div > ul");
  if (episodeContainer)
    for (const ep of episodeContainer.children) {

      const ne = document.createElement("span");
      ne.style = "cursor: pointer"

      ne.onclick = async (e) => {
        const target = (e.target) ? e.target : e.srcElement;
        const epid = ep.getAttribute("data-item-id");

        let watchedEpisodes = await GM.getValue("watchedEpisodeIds");

        if(watchedEpisodes.includes(epid)) {
          watchedEpisodes = watchedEpisodes.filter(e => e !== epid)
          target.innerText = "❌";
        } else {
          watchedEpisodes.push(epid)
          target.innerText = "✅";
        }

        GM.setValue("watchedEpisodeIds", watchedEpisodes);
      }

      const watchedEpisodes = await GM.getValue("watchedEpisodeIds")
      ne.innerText = (watchedEpisodes.includes(ep.getAttribute("data-item-id"))) ? "✅" : "❌";
      ep.querySelector("div > div > div > span").appendChild(ne);

    }
})();