Greasy Fork

Greasy Fork is available in English.

Phigros谱面信息提取

提取Phigros谱面信息并导出为JSON

当前为 2025-05-01 提交的版本,查看 最新版本

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         Phigros谱面信息提取
// @namespace    http://tampermonkey.net/
// @version      3.1
// @description  提取Phigros谱面信息并导出为JSON
// @author       FrandreJoestar
// @match        https://mzh.moegirl.org.cn/Phigros/%E8%B0%B1%E9%9D%A2%E4%BF%A1%E6%81%AF
// @icon         https://img.moegirl.org.cn/common/a/ab/Phigros_Icon_3.0.0.png
// @grant        none
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    // 创建按钮
    const btn = document.createElement('button');
    btn.innerHTML = '导出谱面信息';
    btn.style.position = 'fixed';
    btn.style.top = '20px';
    btn.style.right = '20px';
    btn.style.zIndex = 9999;
    btn.style.padding = '12px 20px';
    btn.style.backgroundColor = '#673AB7';
    btn.style.color = 'white';
    btn.style.border = 'none';
    btn.style.borderRadius = '8px';
    btn.style.cursor = 'pointer';
    btn.style.boxShadow = '0 4px 6px rgba(0,0,0,0.1)';
    btn.style.fontSize = '16px';
    document.body.appendChild(btn);

    // 提取歌曲基础信息
    const infoExtractor = (table, label) => {
        const rows = table.querySelectorAll('tr');
        for (const row of rows) {
            const cells = Array.from(row.children);
            for (let i = 0; i < cells.length; i++) {
                const cell = cells[i];
                if (cell.textContent.trim() === label) {
                    let valueIndex = i + 1;
                    while (cells[valueIndex]?.hasAttribute('colspan')) {
                        const colspan = parseInt(cells[valueIndex].getAttribute('colspan'), 10) || 1;
                        valueIndex += colspan;
                    }
                    return cells[valueIndex]?.textContent.trim() || null;
                }
            }
        }
        return null;
    };

    // 提取歌曲基础信息
    const mtExtractor = (table, label) => {
        const rows = table.querySelectorAll('tr');
        for (const row of rows) {
            const cells = Array.from(row.children);
            for (let i = 0; i < cells.length; i++) {
                const cell = cells[i];
                if (cell.textContent.trim() === label) {
                    let valueIndex = i + 1;
                    while (cells[valueIndex]?.hasAttribute('colspan')) {
                        const colspan = parseInt(cells[valueIndex].getAttribute('colspan'), 10) || 1;
                        valueIndex += colspan;
                    }
                    return cells[valueIndex-2]?.textContent.trim() || null;
                }
            }
        }
        return null;
    };

    // 多标签提取器(bpm 和 时长)
    const multiLabelExtractor = (table, labels) => {
        for (const label of labels) {
            const value = mtExtractor(table, label);
            if (value) return value;
        }
        return null;
    };

    // 多标签提取器(信息)
    const multiLabelExtractor1 = (table, labels) => {
        for (const label of labels) {
            const value = infoExtractor(table, label);
            if (value) return value;
        }
        return null;
    };

    btn.addEventListener('click', () => {
        const result = Array.from(document.querySelectorAll('table.wikitable')).map(table => {
            const song = {
                name: table.querySelector('th').textContent.trim().replace(/\n/g, ''),
                duration: 0,
                bpm: parseInt(multiLabelExtractor(table, ['BPM'])?.replace(/\D/g, '') || 0),
                composer: multiLabelExtractor1(table, ['曲师', '作曲家']) || '未知',
                illustrator: multiLabelExtractor1(table, ['画师', '插图']) || '未知',
                difficulties: {}
            };

            // 处理时长信息
            const durationText = multiLabelExtractor(table, ['时长', '长度']);
            if (durationText) {
                const cleanTime = durationText.replace(/[^0-9:]/g, '');
                song.duration = cleanTime.split(':').reduce((acc, time) => (acc * 60) + parseInt(time || 0, 10), 0);
            }

            // 提取难度信息
            const diffHeader = Array.from(table.querySelectorAll('th')).find(th => th.textContent.includes('难度'));
            if (diffHeader) {
                let currentRow = diffHeader.closest('tr').nextElementSibling;
                while (currentRow && currentRow.querySelector('td')) {
                    const cells = currentRow.querySelectorAll('td');
                    if (cells.length >= 5) {
                        const diffName = cells[0].querySelector('b')?.textContent.trim();
                        if (diffName) {
                            song.difficulties[diffName] = {
                                level: parseInt(cells[1].textContent, 10) || 0,
                                constant: parseFloat(cells[2].textContent) || 0.0,
                                notes: parseInt(cells[3].textContent, 10) || 0,
                                mapper: cells[4].textContent.trim().replace(/\s+/g, ' ')
                            };
                        }
                    }
                    currentRow = currentRow.nextElementSibling;
                }
            }

            return song;
        });

        // 生成下载文件(保持不变)
        const timestamp = new Date().toISOString().slice(0, 19).replace(/[-T:]/g, '');
        const blob = new Blob([JSON.stringify(result, null, 2)], { type: 'application/json' });
        const url = URL.createObjectURL(blob);
        const link = document.createElement('a');
        link.href = url;
        link.download = `phigros_data_${timestamp}.json`;
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
        URL.revokeObjectURL(url);
    });
})();