Sleazy Fork is available in English.

Mark Watched Videos for SpankBang

Marks videos that you've previously seen as watched, across the entire site.

La data de 19-05-2021. Vezi ultima versiune.

// ==UserScript==
// @namespace   LewdPursuits
// @name        Mark Watched Videos for SpankBang
// @match       *://spankbang.com/*
// @exclude-match     *://spankbang.com/users/history*
// @version     0.5.0
// @author      LewdPursuits
// @description Marks videos that you've previously seen as watched, across the entire site.
// @license     GPL-3.0-or-later
// @require     https://cdn.jsdelivr.net/npm/idb-keyval@5/dist/iife/index-min.js
// ==/UserScript==

const historyURL = new URL("https://spankbang.com/users/history");
// This creates the CSS needed to gray out the thumbnail and display the Watched text over it
// The style element is added to the bottom of the body so it's the last style sheet processed
// this ensures these styles take highest priority
const style = document.createElement("style");
style.textContent = `img.watched {
		filter: grayscale(80%);
	}
	div.centered{
		position: absolute;
		color: white;
		height: 100%;
		width: 100%;
		transform: translate(0, -100%);
		z-index: 999;
		text-align: center;
	}
	div.centered p {
		position: relative;
		top: 40%;
		font-size: 1.5rem;
		background: rgba(0,0,0,0.5);
		display: inline;
		padding: 2%;
	}`;
document.body.appendChild(style);
async function getHistoryPage() {
    // This fetch loads the first page of user history
    const response = await fetch(historyURL.href);
    const parser = new DOMParser();
    if (!response.ok) {
        throw new Error(`HTTP error. Status: ${response.status}`);
    }
    // We turn the response into a string representing the page as text
    // We run the text through a DOM parser, which turns it into a useable HTML document
    return parser.parseFromString(await response.text(), "text/html");
}
async function checkStoreLength(db) {
    const store = await db.getAllKeys("videos");
    if (store.length === 0) {
        await buildVideoHistory(db);
    }
    return db;
}
// TODO Add capability to build keyval store from whole history not just first page
async function buildVideoHistory(db) {
    const doc = await getHistoryPage();
    // This gets the heading that says the number of watched videos, uses regex for 1 or more numbers
    // then gets the matched number as a string and converts it to the number type  
    //const num = Number(doc.querySelector<HTMLHeadingElement>("div.data h2").innerText.match(/\d+/)[0])
    const videos = Array.from(doc.querySelectorAll('div[id^="v_id"]'));
    const toAdd = videos.map(e => {
        const thumb = e.querySelector("a.thumb");
        const _name = e.querySelector("a.n");
        return { id: e.id, url: thumb.href, name: _name.innerText };
    });
    const tx = db.transaction("videos", "readwrite");
    const store = tx.objectStore("videos");
    return Promise.all(toAdd.map(e => store.add(e)));
}
async function tagAsWatched(db) {
    // We check for the existance of any watched videos on the current page
    // If there are any, we move to the thumbnail and add the .watched class
    // This applys the CSS style above, and allows us to easily find the videos again
    const names = Array.from(document.querySelectorAll('div[id^="v_id"]'));
    const store = db.transaction("videos").store;
    const keys = await store.getAllKeys();
    function tagImg(e) {
        if (keys.includes(e.id)) {
            const img = e.querySelector("a picture img");
            //console.log(`Marking ${e.innerText} as watched`)
            img.classList.add("watched");
            return img;
        }
    }
    names.forEach(tagImg);
    return db;
}
function filterWatched() {
    const docQuery = Array.from(document.querySelectorAll("img.watched"));
    function makeDiv(e) {
        let newPara = document.createElement("p");
        newPara.textContent = "Watched";
        let newDiv = document.createElement("div");
        newDiv.classList.add("centered");
        newDiv.appendChild(newPara);
        return e.parentElement.parentElement.appendChild(newDiv);
    }
    return (docQuery.length > 0) ? docQuery.map(makeDiv) : [];
}
async function addPageToStore(db) {
    const loc = `${window.location}`;
    const div = document.querySelector("div#video");
    const _id = div ? `v_id_${div.dataset.videoid}` : "";
    const heading = document.querySelector("div.left h1");
    const title = heading ? heading.innerText : "";
    if (!/spankbang\.com\/\w+\/video\//.test(loc)) {
        return;
    }
    let readStore = db.transaction("videos").store;
    const lookup = await readStore.get(loc);
    if (lookup !== undefined) {
        return;
    }
    let writeStore = db.transaction("videos", "readwrite").store;
    return writeStore.add({ id: _id, url: loc, name: title });
}
idb.openDB("history", 1, {
    upgrade(db) {
        const store = db.createObjectStore("videos", {
            keyPath: "id",
            autoIncrement: false,
        });
        store.createIndex("url", "url");
    }
})
    .then(checkStoreLength)
    .then(tagAsWatched)
    .then(addPageToStore)
    .then(filterWatched)
    .catch(e => console.trace(e));