MaruMaru 日本語歌詞假名匯出(方便離線保存)

Show ruby lyrics in new tab with title + artist (based on DOM flow)

目前為 2025-07-12 提交的版本,檢視 最新版本

// ==UserScript==
// @name         MaruMaru 日本語歌詞假名匯出(方便離線保存)
// @author       Hollen9
// @namespace    http://hollen9.com/
// @version      1.1
// @description  Show ruby lyrics in new tab with title + artist (based on DOM flow)
// @match        https://www.marumaru-x.com/japanese-song/play-*
// @grant        none
// @license      MIT
// ==/UserScript==

(function () {
    'use strict';

    function buildRubyHTML(rubyElement) {
        const ruby = document.createElement('ruby');
        rubyElement.childNodes.forEach(child => {
            if (child.nodeType === Node.ELEMENT_NODE) {
                const tag = child.tagName.toLowerCase();
                if (tag === 'rb' || tag === 'rt') {
                    const newEl = document.createElement(tag);
                    newEl.textContent = child.textContent;
                    ruby.appendChild(newEl);
                }
            } else if (child.nodeType === Node.TEXT_NODE) {
                ruby.appendChild(document.createTextNode(child.textContent));
            }
        });
        return ruby;
    }

    function processLine(p) {
        const result = document.createElement('p');
        p.childNodes.forEach(node => {
            if (node.nodeType === Node.TEXT_NODE) {
                result.appendChild(document.createTextNode(node.textContent));
            } else if (node.nodeType === Node.ELEMENT_NODE) {
                const tag = node.tagName.toLowerCase();
                if (tag === 'ruby') {
                    result.appendChild(buildRubyHTML(node));
                } else if (tag === 'br') {
                    result.appendChild(document.createElement('br'));
                } else {
                    result.appendChild(document.createTextNode(node.textContent));
                }
            }
        });
        return result.outerHTML;
    }

    function extractLyrics() {
        const lines = document.querySelectorAll('#lyrics-list .lyrics-source-display > p');
        return Array.from(lines).map(processLine).join('\n');
    }

    function getSongTitle() {
        const titleEl = document.querySelector('h2.song-name');
        if (!titleEl) return 'Untitled';

        let result = '';

        titleEl.childNodes.forEach(node => {
            if (node.nodeType === Node.TEXT_NODE) {
                result += node.textContent;
            } else if (node.nodeType === Node.ELEMENT_NODE) {
                const tag = node.tagName.toLowerCase();
                if (tag === 'ruby') {
                    node.childNodes.forEach(child => {
                        if (child.nodeType === Node.ELEMENT_NODE && child.tagName.toLowerCase() === 'rb') {
                            result += child.textContent;
                        } else if (child.nodeType === Node.TEXT_NODE) {
                            result += child.textContent;
                        }
                    });
                }
            }
        });

        return result.trim();
    }

    function getSongTitleHTML() {
        const titleEl = document.querySelector('h2.song-name');
        return titleEl ? titleEl.outerHTML : '<h2>Untitled</h2>';
    }

    function getArtistName() {
        const titleEl = document.querySelector('h2.song-name');
        let next = titleEl?.nextElementSibling;
        while (next && next.tagName.toLowerCase() !== 'h3') {
            next = next.nextElementSibling;
        }
        return next ? next.textContent.trim() : 'Unknown Artist';
    }

    function openInNewTab(htmlContent, titleText, artistText) {
        const win = window.open('', '_blank');
        const doc = win.document;
        doc.open();
        doc.write(`
        <!DOCTYPE html>
        <html lang="ja">
        <head>
            <meta charset="UTF-8">
            <title>${artistText} - ${titleText}</title>
            <style>
                body {
                    font-family: "Hiragino Kaku Gothic Pro", "Meiryo", sans-serif;
                    padding: 2em;
                    line-height: 1.8;
                    font-size: 18px;
                    background: #f9f9f9;
                    color: #222;
                }
                ruby {
                    ruby-position: over;
                }
                p {
                    margin-bottom: 1em;
                }
                h2, h3 {
                    margin-top: 0;
                    text-align: left;
                }
                h2 {
                    font-size: 1.6em;
                }
                h3 {
                    font-size: 1.2em;
                    color: #666;
                }
                #save-button {
                    position: fixed;
                    bottom: 24px;
                    right: 24px;
                    padding: 10px 16px;
                    font-size: 14px;
                    background-color: #444;
                    color: #fff;
                    border: none;
                    border-radius: 6px;
                    cursor: pointer;
                    z-index: 9999;
                }
                #save-button:hover {
                    background-color: #222;
                }
            </style>
        </head>
        <body>
            ${getSongTitleHTML()}
            <h3>${artistText}</h3>
            <hr>
            ${htmlContent}
            <button id="save-button">💾 另存 HTML</button>
            <script>
                document.getElementById('save-button').addEventListener('click', function () {
                const html = document.documentElement.outerHTML;
                const blob = new Blob([html], { type: 'text/html' });
                const url = URL.createObjectURL(blob);
                const a = document.createElement('a');
                a.href = url;
                a.download = document.title + '.html';
                a.click();
                URL.revokeObjectURL(url);
                });
           </script>
        </body>
        </html>
    `);
        doc.close();
    }

    function createButton() {
        const btn = document.createElement('button');
        btn.textContent = '📖 匯出';
        btn.style.position = 'fixed';
        btn.style.bottom = '24px';
        btn.style.right = '60px';
        btn.style.zIndex = 9999;
        btn.style.padding = '10px 16px';
        btn.style.background = '#222';
        btn.style.color = '#fff';
        btn.style.border = 'none';
        btn.style.borderRadius = '5px';
        btn.style.cursor = 'pointer';

        btn.addEventListener('click', () => {
            const songTitle = getSongTitle();
            const artistName = getArtistName();
            const lyrics = extractLyrics();
            openInNewTab(lyrics, songTitle, artistName);
        });

        document.body.appendChild(btn);
    }

    window.addEventListener('load', createButton);
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址