Age-calculator - stashdb.org (fixed)

Adds performer age at scene date after DOM settles

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

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

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name        Age-calculator - stashdb.org (fixed)
// @namespace   Violentmonkey Scripts
// @match       https://stashdb.org/performers/*
// @grant       none
// @version     1.2
// @author      Yahigod
// @license     MIT
// @description Adds performer age at scene date after DOM settles
// ==/UserScript==

(function () {
  'use strict';

  let observerTimer;
  const timeoutDuration = 900; // ms of no DOM changes before running
  let isScriptRunning = false;

  // --- Mutation observer with debounce ---
  const mutationObserver = new MutationObserver(() => {
    clearTimeout(observerTimer);
    observerTimer = setTimeout(runScriptIfNoMutations, timeoutDuration);
  });

  const body = document.querySelector('body');
  if (body) {
    mutationObserver.observe(body, { childList: true, subtree: true });
  }

  // Initial delayed attempt
  observerTimer = setTimeout(runScriptIfNoMutations, timeoutDuration);

  function runScriptIfNoMutations() {
    if (isScriptRunning) return;

    // Stop observer while we modify DOM
    mutationObserver.disconnect();
    isScriptRunning = true;

    try {
      // ---------------- YOUR ORIGINAL CODE (with minimal fixes) ----------------
      (function() {
        'use strict';

        // Function to calculate age
        function calculateAge(birthdate, sceneDate) {
          const birthDate = new Date(birthdate);
          const sceneDateObject = new Date(sceneDate);

          const ageInMilliseconds = sceneDateObject - birthDate;
          const ageInYears = ageInMilliseconds / (365 * 24 * 60 * 60 * 1000);

          const birthYear = birthDate.getFullYear();
          const sceneYear = sceneDateObject.getFullYear();

          const birthMonth = birthDate.getMonth();
          const sceneMonth = sceneDateObject.getMonth();

          const birthDay = birthDate.getDate();
          const sceneDay = sceneDateObject.getDate();

          let years = sceneYear - birthYear;
          let months = sceneMonth - birthMonth;
          let days = sceneDay - birthDay;

          if (days < 0) {
            months -= 1;
            days += 30; // Assuming an average of 30 days per month
          }

          if (months < 0) {
            years -= 1;
            months += 12;
          }

          let ageText = '';

          if (years > 0) {
            ageText += `${years}y `;
          }

          if (months > 0) {
            ageText += `${months}m `;
          }

          if (days > 0) {
            ageText += `${days}d `;
          }

          ageText += 'old';

          return ageText.trim();
        }

        // Extract and print birthdates
        const rows = document.querySelectorAll('tr');
        let birthdate;

        for (const row of rows) {
          const cells = row.querySelectorAll('td');

          if (cells.length == 2) {
            const birthdateCell = cells[0].textContent.trim();
            if (birthdateCell === "Birthdate") {
              // ✅ FIX #1: extract ONLY the ISO date even if the cell is polluted
              const match = cells[1].textContent.match(/\d{4}-\d{2}-\d{2}/);
              birthdate = match ? match[0] : undefined;
            }
          }
        }

        if (!birthdate) return; // can't do anything without a valid birthdate

        // Add age next to each date in SceneCard elements
        const sceneCards = document.querySelectorAll('.SceneCard.card');

        for (const sceneCard of sceneCards) {
          const dateStrong = sceneCard.querySelector('.card-footer strong');

          if (!dateStrong) continue;

          // ✅ FIX #2: prevent duplicates (looks for our tag)
          const footer = dateStrong.parentElement;
          if (footer && footer.querySelector('.age-calculator-age')) {
            continue;
          }

          const dateText = dateStrong.textContent.trim();
          const age = calculateAge(birthdate, dateText);

          // ✅ FIX #3: don't insert junk results like just "old"
          if (!/\d/.test(age)) continue;

          // ✅ FIX #4: put age UNDER the date with a real line break
          dateStrong.insertAdjacentHTML(
            'afterend',
            `<br><span class="age-calculator-age">${age}</span>`
          );
        }
      })();
      // ------------------------------------------------------------------------
    } catch (e) {
      console.error('[Age-calculator]', e);
    } finally {
      // Reconnect observer
      if (body) {
        mutationObserver.observe(body, { childList: true, subtree: true });
      }
      isScriptRunning = false;
    }
  }
})();