OnlyFans to Coomer with Video Filter

Redirects from OnlyFans to Coomer and filters posts without videos.

// ==UserScript==
// @name         OnlyFans to Coomer with Video Filter
// @namespace    http://coomer.su
// @version      1.3
// @description  Redirects from OnlyFans to Coomer and filters posts without videos.
// @author       Dirk Digler
// @match        *://onlyfans.com/*
// @match        https://*.coomer.party/*/user/*
// @match        https://*.kemono.party/*/user/*
// @match        https://*.coomer.su/*/user/*
// @match        https://*.kemono.su/*/user/*
// @grant        none
// ==/UserScript==

(function() {
    'use strict';

    // 1. Redirect functionality
    const usernameRegex = /^\/([^\/]+)/;
    const match = window.location.pathname.match(usernameRegex);
    const username = match ? match[1] : null;

    if (username) {
        const coomerUrl = `https://coomer.su/onlyfans/user/${username}`;

        const redirectButton = document.createElement('a');
        redirectButton.href = coomerUrl;
        redirectButton.innerHTML = 'Coomer';
        redirectButton.target = '_blank';
        redirectButton.style = 'position: fixed; top: 10px; left: 10px; padding: 10px; background-color: #0091ea; color: #ffffff; text-decoration: none; border-radius: 5px; z-index: 9999;';
        document.body.appendChild(redirectButton);
    } else {
        console.error('Username not found in URL');
    }

    // 2. Filter videos functionality
    async function querySelectorAsync(element, selector) {
      return await new Promise(resolve => {
        let elm = element.querySelector(selector);
        if(elm) return resolve(elm);

        const observer = new MutationObserver(_ => {
          elm = element.querySelector(selector);
          if(elm) {
            resolve(elm);
            observer.disconnect();
          }
        })

        observer.observe(document.body, {
          childList: true,
          subtree: true
        })
      })
    }

    async function hasVideo(postUrl) {
      let cache = await GM_getValue(postUrl);
      if(cache !== undefined) return cache;

      return new Promise(resolve => {
        let request = new XMLHttpRequest();
        request.open("GET", postUrl, true);
        request.send(null);

        request.onreadystatechange = function() {
          if(request.readyState === 4) {
            let elm = document.createElement("html");
            elm.innerHTML = request.responseText;

            let res = elm.querySelector("video") !== null;
            resolve(res);

            GM_setValue(postUrl, res);
          }
        }
      })
    }

    async function filterVideos() {
      let posts = [...(await querySelectorAsync(document, ".card-list__items")).children];
      for(let post of posts) {
        let postUrl = (await querySelectorAsync(post, "a")).href;
        hasVideo(postUrl).then(res => {
          if(!res) post.remove();
        })
      }
    }

    // 3. Add button to trigger filtering
    (async() => {
      let filterBtn = document.createElement("button");
      filterBtn.textContent = "Filter Videos";
      filterBtn.onclick = function(e) {
        e.preventDefault();
        filterBtn.remove();
        filterVideos();
      }

      let searchInputForm = await querySelectorAsync(document, "form");
      searchInputForm.appendChild(filterBtn);
    })();
})();