ExploitedX Sites - Release Codes

Episode numbers for ExploitedX sites.

Você precisará instalar uma extensão como Tampermonkey, Greasemonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Violentmonkey para instalar este script.

Você precisará instalar uma extensão como Tampermonkey ou Userscripts para instalar este script.

Você precisará instalar uma extensão como o Tampermonkey para instalar este script.

Você precisará instalar um gerenciador de scripts de usuário para instalar este script.

(Eu já tenho um gerenciador de scripts de usuário, me deixe instalá-lo!)

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar uma extensão como o Stylus para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

Você precisará instalar um gerenciador de estilos de usuário para instalar este estilo.

(Eu já possuo um gerenciador de estilos de usuário, me deixar fazer a instalação!)

// ==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);
  });

})();