ExploitedX Sites - Release Codes

Episode numbers for ExploitedX sites.

Versione datata 17/06/2024. Vedi la nuova versione l'ultima versione.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

You will need to install an extension such as Tampermonkey to install this script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name        ExploitedX Sites - Release Codes
// @author      peolic
// @version     1.6
// @description Episode numbers for ExploitedX sites.
// @namespace   https://github.com/peolic
// ===== Exploited College Girls
// @match       http*://*.exploitedcollegegirls.com/
// @match       http*://*.exploitedcollegegirls.com/models/*
// @match       http*://*.exploitedcollegegirls.com/categories/movies_*
// @match       http*://*.exploitedcollegegirls.com/updates/page_*
// ===== Backroom Casting Couch
// @match       http*://*.backroomcastingcouch.com/
// @match       http*://*.backroomcastingcouch.com/models/*
// @match       http*://*.backroomcastingcouch.com/categories/movies_*
// @match       http*://*.backroomcastingcouch.com/updates/page_*
// ===== Black Ambush
// @match       http*://*.blackambush.com/
// @match       http*://*.blackambush.com/models/*
// @match       http*://*.blackambush.com/categories/movies_*
// @match       http*://*.blackambush.com/updates/page_*
// ===== BBC Surprise
// @match       http*://*.bbcsurprise.com/
// @match       http*://*.bbcsurprise.com/models/*
// @match       http*://*.bbcsurprise.com/categories/movies_*
// @match       http*://*.bbcsurprise.com/updates/page_*
// ===== Hot MILFs Fuck
// @match       http*://*.hotmilfsfuck.com/
// @match       http*://*.hotmilfsfuck.com/models/*
// @match       http*://*.hotmilfsfuck.com/categories/movies_*
// @match       http*://*.hotmilfsfuck.com/updates/page_*
// ===== ExCoGi Girls
// @match       http*://*.excogigirls.com/
// @match       http*://*.excogigirls.com/models/*
// @match       http*://*.excogigirls.com/categories/movies_*
// @match       http*://*.excogigirls.com/updates/page_*
// =====
// @grant       none
// @homepageURL https://github.com/peolic/userscripts
// ==/UserScript==

//@ts-check
(() => {
  /**
   * @param {HTMLDivElement} item video item element
   * @param {HTMLDivElement} [infoDiv] release info element
   * @returns {void}
   * @throws no target error
   */
  function injectDefault(item, infoDiv) {
    if (!infoDiv)
      throw new Error('no element to inject');
    let target = item.querySelector('.rating-div') || item.querySelector('.content-div .text-right');
    if (target)
      return target.prepend(infoDiv);
    target = item.querySelector('.content-div');
    if (target)
      return target.append(infoDiv);
    throw new Error('no target');
  }

  /**
   * @typedef SiteType
   * @property {RegExp} pattern
   * @property {(item: HTMLDivElement, infoDiv?: HTMLDivElement) => void} inject
   * @property {string} [textColor]
   * @property {Partial<CSSStyleDeclaration>} [style]
   */

  // https://regex101.com/r/hPTK77/2
  const hostnamePattern = /^(?:www\.)?([a-z.]+)$/i;

  /** @type {{ [hostname: string]: Partial<SiteType> }} */
  const SITES = {
    'exploitedcollegegirls.com': {
      textColor: '#000000',
      // inject: injectDefault,
    },
    'backroomcastingcouch.com': {
      textColor: '#dd0066',
      style: {
        position: 'absolute',
        right: '0',
      },
      inject: (item, infoDiv) => {
        let target = item.querySelector('.content-div');
        if (!target)
          throw new Error('no target');

        if (window.location.pathname.startsWith('/models/')) {
          // Make video cards larger, like on the other network sites
          /** @type {HTMLDivElement} */ (item.parentElement).classList.add('col-lg-9');
          const row = /** @type {HTMLDivElement} */ (target.querySelector('div'));
          row.children[0].classList.add('col-lg-8');
          row.children[1].classList.add('col-lg-4');
          if (!infoDiv)
            return;
        }

        if (!infoDiv)
          throw new Error('no element to inject');

        target.prepend(infoDiv);
      },
    },
    'blackambush.com': {
      textColor: '#ffa901',
      // inject: injectDefault,
    },
    'bbcsurprise.com': {
      textColor: '#ffa901',
      // inject: injectDefault,
    },
    'hotmilfsfuck.com': {
      textColor: '#d52023',
      // inject: injectDefault,
    },
    'excogigirls.com': {
      textColor: '#000000',
      // inject: injectDefault,
    },
  };

  // https://regex101.com/r/BtryUh/6
  const globalPattern = /(?<site>ecgg?|brcc|ba|blackambush|bbcs?|hmf).*?(?<release>\d{4})/;

  /** @type {SiteType} */
  const defaultSite = {
    pattern: globalPattern,
    inject: injectDefault,
  };

  /** @type {NodeListOf<HTMLDivElement>} */
  (document.querySelectorAll('.item-video')).forEach((item) => {
    const baseHostname = window.location.hostname.match(hostnamePattern)?.[1] ?? window.location.hostname;
    const options = { ...defaultSite, ...SITES[baseHostname] };
    const { pattern, textColor, inject, style } = options;

    const videoSrc = item.querySelector('video > source')?.getAttribute('src');
    if (!videoSrc) {
      inject(item);
      return;
    }

    const filename = videoSrc.split(/\//g).slice(-1)[0];
    const result = filename.match(pattern);
    const { site, release } = result?.groups ?? {};

    const infoDiv = document.createElement('div');
    Object.assign(infoDiv.style, { position: 'relative' });
    const info = document.createElement('span');
    Object.assign(info.style, {
      color: textColor,
      fontWeight: 600,
      ...(style ?? {
        position: 'absolute',
        right: '0',
        top: '-22px',
      }),
    });
    info.innerText = release !== undefined ? `#${release}` : filename;
    info.title = `${site}-${release}\n${filename}`;
    infoDiv.appendChild(info);

    inject(item, infoDiv);
  });

})();