Greasy Fork is available in English.
MidiShow免积分下载 (修复版)
// ==UserScript==
// @name MidiShowDownload
// @namespace https://greasyfork.org/zh-CN/users/1593463-nander
// @namespace2 https://lgc2333.top/
// @version 0.2.1
// @description MidiShow免积分下载 (修复版)
// @author LgCookie & mod nander
// @homepage https://github.com/lgc2333/GM/blob/main/packages/MidiShowDownload
// @match https://www.midishow.com/midi/*
// @match https://www.midishow.com/zh-tw/midi/*
// @match https://www.midishow.com/en/midi/*
// @license MIT
// @grant GM_addStyle
// ==/UserScript==
/* global $ JZZ PNotify */
;(function () {
'use strict'
const NAME = 'MidiShowDownload'
/** @type {string | null} */
let cachedDataURL = null
/**
* @param {string} str
* @returns {Uint8Array}
*/
function transformData(str) {
const arr = new Uint8Array(str.length)
for (let i = 0; i < str.length; i++) arr[i] = str.charCodeAt(i)
return arr
}
const originalSMF = JZZ.MIDI.SMF
const patchedSMF = /** @type {JZZ.MIDI.SMFConstructor} */ (
/** @this {JZZ.MIDI.SMF} */
function (data) {
const blob = new Blob([transformData(data)], { type: 'audio/midi' })
const url = URL.createObjectURL(blob)
if (cachedDataURL) URL.revokeObjectURL(cachedDataURL)
cachedDataURL = url
PNotify.success(`[${NAME}] 成功截获文件`)
return originalSMF.apply(this, [data])
}
)
JZZ.MIDI.SMF = patchedSMF
/**
* @param {string} url
* @param {string} filename
*/
function openSaveDialog(url, filename) {
const el = document.createElement('a')
el.href = url
el.download = filename
el.target = '_blank'
el.click()
}
async function download() {
const e = $('.ms-player-container')
const player = /** @type {JZZ.gui.Player | undefined} */ (
e.JzzPlayer().data('plugin_JzzPlayer')
)
if (!player) {
PNotify.error(`[${NAME}] 无法获取播放器实例`)
return
}
if (!cachedDataURL) {
await player.loadUrl()
}
if (!cachedDataURL) {
PNotify.error(`[${NAME}] 截获文件失败`)
return
}
const id = /** @type {string} */ (e.data('id'))
let title = e.find('h1.pl-md-player').text().trim()
if (!title) {
// 备用获取标题逻辑
title = $('.midi-title, h1').first().text().trim() || 'midi_file'
}
openSaveDialog(cachedDataURL, `${id || 'download'} - ${title}.mid`)
}
function setup() {
// 兼容新旧版布局的按钮查找器
const downloadArea = document.getElementById('download') || document.querySelector('.download-area, .midi-download-btn-group')
let targetBtn = downloadArea?.firstElementChild || document.querySelector('.btn-download, a[href*="download"]')
// 如果还是找不到具体按钮,尝试直接挂载到播放器下方或详情区域
if (!targetBtn) {
targetBtn = document.querySelector('.ms-player-container, .midi-info-box')
}
if (!targetBtn) {
console.error(`[${NAME}] 无法定位到可以挂载按钮的元素`)
return
}
GM_addStyle(`a.btn-midi-download-custom { background-color: #28a745; color: #fff; border-color: #28a745; } a.btn.disabled { filter: grayscale(1); pointer-events: none; }`)
// 创建一个高亮独立按钮,避免样式冲突
const btnHtml =
`<a class="btn btn-primary btn-midi-download-custom btn-sm mb-3 mr-2" href="javascript:void(0)" style="margin-left: 5px;">` +
`<span class="fa fa-download"></span> 免积分下载` +
`</a>`
targetBtn.insertAdjacentHTML('afterend', btnHtml)
const btn = /** @type {HTMLAnchorElement} */ (targetBtn.nextElementSibling)
if (!btn) return
btn.addEventListener('click', async (e) => {
e.preventDefault()
if (btn.classList.contains('disabled')) return
btn.classList.add('disabled')
try {
await download()
} catch (err) {
PNotify.error(`[${NAME}] 出现意外错误\n${/** @type {any} */ (err).toString()}`)
}
btn.classList.remove('disabled')
})
}
// 使用更保险的定时轮询确保页面元素加载完毕后挂载按钮
const checkTimer = setInterval(() => {
if (document.readyState === 'complete' || document.querySelector('.ms-player-container')) {
clearInterval(checkTimer)
setup()
}
}, 500)
})()