VGMdb Metadata copy

Adds copy buttons to the VGMdb album pages to easily copy metadata.

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         VGMdb Metadata copy
// @namespace    https://vgmdb.net/
// @version      1.8
// @description  Adds copy buttons to the VGMdb album pages to easily copy metadata.
// @author       kahpaibe
// @match        https://vgmdb.net/album/*
// @grant        none
// @run-at       document-end
// @license MIT
// ==/UserScript==

(function() {
  'use strict';

  const PREFIX = '';
  const SUFFIX = '';

  // Helper function to style buttons consistently
  const styleButton = (button) => {
    button.title = 'Copy this text to clipboard';
    button.style.marginLeft = '8px';
    button.style.padding = '1px 6px';
    button.style.fontSize = '0.75em';
    button.style.color = '#CEFFFF';
    button.style.background = 'transparent';
    button.style.border = '1px solid #CEFFFF';
    button.style.cursor = 'pointer';
    button.style.transition = 'background 0.3s, color 0.3s';
    button.style.verticalAlign = 'middle';
  };

  // Helper function to create and append a "COPY" button next to a field
  const addCopyButton = (innerText, labelText, selector, tooltipText, fieldValue) => {
    const rows = document.querySelectorAll('#album_infobit_large tr');

    rows.forEach(row => {
      const labelCell = row.querySelector('td span.label b');
      if (labelCell && labelCell.textContent.trim() === labelText) {
        const valueCell = row.cells[1];
        let field = fieldValue || valueCell.textContent.trim();
        if (selector) {
          const link = valueCell.querySelector(selector);
          if (link) {
            field = link.textContent.trim();
          }
        }

        const button = document.createElement('span');
        button.innerText = innerText;  // Set innerText here
        button.title = tooltipText;
        styleButton(button);

        button.onclick = () => {
          navigator.clipboard.writeText(field).then(() => {
            const original = button.innerText;
            button.innerText = '✔ COPIED';
            setTimeout(() => button.innerText = original, 1500);
          });
        };

        labelCell.parentElement.appendChild(button);
      }
    });
  };

  // Function to handle Release Date buttons
  const addReleaseDateButtons = () => {
    const rows = document.querySelectorAll('#album_infobit_large tr');
    const releaseDateRow = Array.from(rows).find(row => {
      const labelCell = row.querySelector('td span.label b');
      return labelCell && labelCell.textContent.trim() === 'Release Date';
    });

    if (!releaseDateRow) return;

    const valueCell = releaseDateRow.cells[1];
    const dateLink = valueCell.querySelector('a[title^="View albums released on"]');
    const eventLink = valueCell.querySelector('a.link_event');

    let formattedDate = '';
    if (dateLink) {
      const releaseDateStr = dateLink.textContent.trim();
      const dateObj = new Date(releaseDateStr);
      formattedDate = `${dateObj.getFullYear()}.${('0' + (dateObj.getMonth() + 1)).slice(-2)}.${('0' + dateObj.getDate()).slice(-2)}`;
      addCopyButton('⎘', 'Release Date', null, 'Copy Release Date to clipboard', formattedDate);
    }

    let event = '';
    if (eventLink) {
      event = eventLink.textContent.trim();
      addCopyButton('⎘', 'Release Date', null, 'Copy Event to clipboard', event);
    }

    if (dateLink) {
      const combined = event ? `[${formattedDate}][${event}]` : `[${formattedDate}]`;
      addCopyButton('⎘', 'Release Date', null, 'Copy formatted release date to clipboard', combined);
    }
  };

  const addTitleCopyButtons = () => {
    const visibleTitles = document.querySelectorAll('.albumtitle');

    visibleTitles.forEach(span => {
      const computedStyle = window.getComputedStyle(span);
      if (computedStyle.display === 'none' || computedStyle.visibility === 'hidden') {
        return;
      }

      // Skip titles inside <a> tags (like in the album thumb list)
      if (span.closest('a')) {
        return;
      }

      const fragments = [];
      let currentText = '';

      // Break apart the nodes and <br> inside this span
      span.childNodes.forEach(node => {
        if (node.nodeName === 'BR') {
          if (currentText.trim()) fragments.push(currentText.trim());
          fragments.push('<br>');
          currentText = '';
        } else if (node.nodeType === Node.TEXT_NODE) {
          currentText += node.textContent;
        } else {
          currentText += node.outerHTML || node.textContent;
        }
      });

      if (currentText.trim()) {
        fragments.push(currentText.trim());
      }

      // Clear the span and re-inject each line with its own copy button
      span.innerHTML = '';

      fragments.forEach(fragment => {
        if (fragment === '<br>') {
          span.appendChild(document.createElement('br'));
          return;
        }

        const lineSpan = document.createElement('span');
        lineSpan.textContent = fragment;

        const button = document.createElement('button');
        styleButton(button);
        button.innerText = '⎘';  // Ensure button has text
        button.title = 'Copy this title to clipboard';

        button.addEventListener('click', () => {
          navigator.clipboard.writeText(fragment).then(() => {
            const original = button.innerText;
            button.innerText = '✔ COPIED!';
            setTimeout(() => button.innerText = original, 1000);
          });
        });

        span.appendChild(lineSpan);
        span.appendChild(button);

        // Add a second button for copying formatted string
        const formattedStringButton = document.createElement('button');
        styleButton(formattedStringButton);
        formattedStringButton.innerText = '⎘'; // Same button style
        formattedStringButton.title = 'Copy formatted album info to clipboard';

        // Fetch Publisher and Catalog Number dynamically within the context
        const publisherCell = Array.from(document.querySelectorAll('td span.label b')).find(b => b.textContent.trim() === 'Publisher');
        const publisher = publisherCell ? publisherCell.closest('tr').querySelector('td:nth-child(2)').textContent.trim() : '';

        const catalogNumberCell = Array.from(document.querySelectorAll('td span.label b')).find(b => b.textContent.trim() === 'Catalog Number');
        const catalogNumber = catalogNumberCell ? catalogNumberCell.closest('tr').querySelector('td:nth-child(2)').textContent.trim() : '';


        // Re-fetch Release Date and Event here to avoid assumptions
        const rows = document.querySelectorAll('#album_infobit_large tr');
        let formattedDate = '';
        let event = '';

        rows.forEach(row => {
          const labelCell = row.querySelector('td span.label b');
          if (labelCell && labelCell.textContent.trim() === 'Release Date') {
            const valueCell = row.cells[1];
            const dateLink = valueCell.querySelector('a[title^="View albums released on"]');
            const eventLink = valueCell.querySelector('a.link_event');

            if (dateLink) {
              const releaseDateStr = dateLink.textContent.trim();
              const dateObj = new Date(releaseDateStr);
              formattedDate = `${dateObj.getFullYear()}.${('0' + (dateObj.getMonth() + 1)).slice(-2)}.${('0' + dateObj.getDate()).slice(-2)}`;
            }

            if (eventLink) {
              event = eventLink.textContent.trim();
            }
          }
        });

        // Construct the formatted string
        let formattedString = PREFIX;
        formattedString += `[${formattedDate}]`;
        if (event) {
          formattedString += `[${event}]`;
        }
        if (publisher) {
          formattedString += ` ${publisher} -`;
        }
        formattedString += ` ${fragment}`;
        if (catalogNumber) {
          formattedString += ` {${catalogNumber}}`;
        } else {
          formattedString += ' {nocat#}';
        }
        formattedString += SUFFIX;


        formattedStringButton.addEventListener('click', () => {
          navigator.clipboard.writeText(formattedString).then(() => {
            const original = formattedStringButton.innerText;
            formattedStringButton.innerText = '✔ COPIED!';
            setTimeout(() => formattedStringButton.innerText = original, 1000);
          });
        });

        // Append button to the DOM (you can adjust where this button goes)
        document.body.appendChild(formattedStringButton);

        span.appendChild(formattedStringButton);

      });
    });
  };


  // Main function to initialize the copy buttons
  const initializeCopyButtons = () => {
    // Add metadata buttons
    addCopyButton('⎘', 'Catalog Number', null, 'Copy Catalog Number to clipboard');
    addCopyButton('⎘', 'Publisher', '.productname', 'Copy Publisher to clipboard');
    addReleaseDateButtons();  // Add the buttons for Release Date, Event, and Date+Event

    // Add inline copy buttons for album titles and secondary names
    addTitleCopyButtons();
  };

  // Initialize the script
  initializeCopyButtons();

})();