您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds copy buttons to the VGMdb album pages to easily copy metadata.
// ==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(); })();