Greasy Fork is available in English.

YouTube不重看

删除首页看过一定进度的视频,只会记录最大进度,仅对以新窗口打开的页面有效,ctrl+i可导入记录

// ==UserScript==
// @name         YouTube不重看
// @namespace    蒋晓楠
// @version      20240331
// @description  删除首页看过一定进度的视频,只会记录最大进度,仅对以新窗口打开的页面有效,ctrl+i可导入记录
// @author       蒋晓楠
// @license      MIT
// @match        https://www.youtube.com/
// @match        https://www.youtube.com/watch?v=*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=youtube.com
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_registerMenuCommand
// @grant        GM_addElement
// ==/UserScript==
//清理视频记录延时,单位秒
const ClearVideoSavedPercentDelay = 30;
//清理视频记录几率,0到100的整数
const ClearVideoSavedPercentRate = 2;
//移除视频间隔,单位秒
const RemoveVideoInterval = 1;
//以下不懂的不要修改
//修改配置
function SetConfig(Key, Value) {
    GM_setValue("Config:" + Key, Value);
}

//获取配置
function GetConfig(Key, Default) {
    return GM_getValue("Config:" + Key, Default);
}

//删除配置
function RemoveConfig(Key) {
    GM_deleteValue("Config:" + Key);
}

//获取当前时间
function GetNowTime() {
    return parseInt((new Date().getTime() / 86400000).toString());
}

//获取视频记录进度
function GetVideoSavedPercent(Identifier) {
    let Video = GM_getValue("Data", {})[Identifier];
    return Video === undefined ? 0 : Video.Percent;
}

//设置视频记录进度
function SetVideoSavedPercent(Identifier, Percent) {
    let AllVideos = GM_getValue("Data", {});
    AllVideos[Identifier] = {Percent: Percent, Expire: GetNewExpire()};
    GM_setValue("Data", AllVideos);
}

//清理视频记录进度
function ClearVideoSavedPercent() {
    let NowDay = GetNowTime();
    let AllVideos = GM_getValue("Data", {});
    for (const Key in AllVideos) {
        if (AllVideos[Key].Expire < NowDay) {
            delete AllVideos[Key];
        }
    }
    console.log(AllVideos);
    GM_setValue("Data", AllVideos);
    console.log("已清理过期的播放记录");
}

//获取新的过期时间
function GetNewExpire() {
    return GetNowTime() + GetConfig("Duration", 180);
}

//获取视频标识符
function GetVideoIdentifier(Url) {
    return Object.fromEntries((new URL(Url)).searchParams).v;
}

function InitHomePage() {
    let RemovePercent = GetConfig("RemovePercent", 90);
    setInterval(() => {
        document.querySelectorAll("ytd-rich-item-renderer:not(.JXNProcessed)").forEach((Item) => {
            Item.classList.add("JXNProcessed");
            let Link = Item.__shady_native_querySelector("#video-title-link")
            if (Link !== null) {
                let Percent = GetVideoSavedPercent(GetVideoIdentifier(Link.href));
                if (Percent >= RemovePercent) {
                    console.log("删除[" + Item.__shady_native_querySelector("#video-title").__shady_native_textContent + "]进度" + Percent);
                    //用隐藏代替删除,因为删除了还会重新在原位置出现
                    Item.style.display = "none";
                }
            }
        });
    }, RemoveVideoInterval * 1000);
}


function InitVideoPage() {
    let Identifier = GetVideoIdentifier(location.href);
    let OldPercent = GetVideoSavedPercent(Identifier);
    let VideoElement = document.querySelector(".video-stream.html5-main-video");
    //等待直到视频元素正确加载
    let CheckInterval = setInterval(function () {
        if (!isNaN(VideoElement.duration)) {
            clearInterval(CheckInterval);
            let Duration = VideoElement.duration;
            console.log("原进度", OldPercent, "总时间", Duration);
            VideoElement.ontimeupdate = () => {
                let NowPercent = parseInt((VideoElement.currentTime / Duration * 100).toFixed());
                if (NowPercent > OldPercent) {
                    console.log("新进度", NowPercent);
                    SetVideoSavedPercent(Identifier, NowPercent);
                    OldPercent = NowPercent;
                }
            }
            VideoElement.ended = () => {
                if (OldPercent !== 100) {
                    SetVideoSavedPercent(Identifier, 100);
                }
            }
        }
    }, 1000);
}

function InitImport() {
    let ImportFile = GM_addElement("input", {
        type: "file",
        hidden: true,
        accept: "application/json"
    });
    ImportFile.onchange = () => {
        if (ImportFile.files.length > 0) {
            let JsonList = ImportFile.files[0];
            let Reader = new FileReader();
            Reader.onload = (Result) => {
                try {
                    GM_setValue("Data", JSON.parse(Result.target.result));
                    alert("导入完成");
                } catch (e) {
                    alert("读取的文件格式不正确");
                }
            };
            Reader.readAsText(JsonList);
        }
    };
    window.onkeydown = (e) => {
        if (e.ctrlKey && e.key === "i") {
            if (confirm("导入记录将会覆盖当前数据,是否继续?")) {
                ImportFile.click();
            }
        }
    };
}

function ShowInfo() {
    setTimeout(() => {
        console.log("当前时间", GetNowTime());
        console.log("所有记录", GM_getValue("Data", {}));
    }, 1000);
}

function Run() {
    GM_registerMenuCommand("设置移除百分比", () => {
        let Interval = prompt("达到这个的才会删除", GetConfig("RemovePercent", 90));
        console.log(Interval)
        if (Interval !== null) {
            SetConfig("RemovePercent", parseInt(Interval));
        }
    });
    GM_registerMenuCommand("设置记录进度有效期", () => {
        let Duration = prompt("设置记录视频播放进度有效期,单位天,超过这个时间的记录会被删除", "180");
        console.log(Duration)
        if (Duration !== null) {
            SetConfig("Duration", parseInt(Duration));
        }
    });
    GM_registerMenuCommand("导出记录", () => {
        let ExportJson = document.createElement("a");
        ExportJson.download = "Youtube不重看.json";
        ExportJson.href = URL.createObjectURL(new Blob([JSON.stringify(GM_getValue("Data", {}))]));
        ExportJson.click();
    });
    InitImport();
    setTimeout(() => {
        if (1 + Math.round(Math.random() * 99) <= ClearVideoSavedPercentRate) {
            ClearVideoSavedPercent();
        }
    }, ClearVideoSavedPercentDelay * 1000);
    ShowInfo();
    location.pathname === "/watch" ? InitVideoPage() : InitHomePage();
}

Run();