您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
MidiShow免积分下载
当前为
// ==UserScript== // @name MidiShowDownload // @namespace https://lgc2333.top/ // @version 0.1.0 // @description MidiShow免积分下载 // @author LgCookie // @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 kBase sBase $ PNotify */ ;(function () { 'use strict' const name = 'MidiShowDownload' /** @type {Record<string, string>} */ const fetchedDataUrls = {} /** * @param {string} hexString * @returns {string} */ function decodeHexToString(hexString) { let decodedString = '' for (let i = 0; i < hexString.length; i += 2) { const hexPair = hexString.substring(i, i + 2) if (hexPair === '00') break decodedString += String.fromCharCode(Number.parseInt(hexPair, 16)) } return decodedString } /** * @param {string} encoded * @param {string} key * @returns {string} */ function midiShowDecode(encoded, key) { let result = '' for (let i = 0; i < encoded.length; ) { const char1_val = key.indexOf(encoded.charAt(i++)) const char2_val = key.indexOf(encoded.charAt(i++)) const char3_val = key.indexOf(encoded.charAt(i++)) const char4_val = key.indexOf(encoded.charAt(i++)) const byte1 = (char1_val << 2) | (char2_val >> 4) const byte2 = ((char2_val & 15) << 4) | (char3_val >> 2) const byte3 = ((char3_val & 3) << 6) | char4_val result += String.fromCharCode(byte1) if (char3_val !== 64) result += String.fromCharCode(byte2) if (char4_val !== 64) result += String.fromCharCode(byte3) } return result } /** * @param {string} str * @returns {Uint8Array} */ function strDataToUint8Array(str) { const arr = new Uint8Array(str.length) for (let i = 0; i < str.length; i++) { arr[i] = str.charCodeAt(i) } return arr } /** * @param {string} fileId * @param {string} fileMId * @returns {Promise<Blob | undefined>} */ async function downloadMidi(fileId, fileMId) { const req1 = $.ajax({ method: 'GET', url: fileMId .replace(/^tokeno#:@!/, 'token') .replace(kBase, sBase) .replace('.mid?', '.js?'), dataType: 'jsonp', cache: true, jsonp: false, jsonpCallback: 'e', }) const req2 = $.ajax({ method: 'POST', url: `${$.MS.langUrl('/midi/new-file')}?id=${fileId}`, dataType: 'text', data: { id: fileId }, }) /** @typedef {[string, JQuery.Ajax.SuccessTextStatus, JQuery.jqXHR]} Resp */ const [[res1], [res2, , xhr2]] = /** @type {[Resp, Resp]} */ ( await $.when(req1, req2).then((p1, p2) => [p1, p2]) ) const eTag = xhr2.getResponseHeader('ETag') if (!eTag) { PNotify.error({ title: name, text: '未找到 ETag' }) return } const key = decodeHexToString(eTag) + res2.substring(56) const dataStr = midiShowDecode(res2.substring(28, 56), key) + // .substr(from: 28, length: 28) midiShowDecode(res1, key) + midiShowDecode(res2.substring(0, 28), key) // .substr(from: 0, length: 28) return new Blob([strDataToUint8Array(dataStr)], { type: 'audio/midi' }) } /** * @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 saveCurrentMidi() { const el = /** @type {HTMLDivElement | null} */ ( document.querySelector('.ms-player-container[data-id][data-mid]') ) if (!el) { PNotify.error({ title: name, text: '找不到播放器' }) return } const fileId = /** @type {string} */ (el.dataset.id) let url = fetchedDataUrls[fileId] if (!url) { const fileMId = /** @type {string} */ (el.dataset.mid) const blob = await downloadMidi(fileId, fileMId) if (!blob) return url = URL.createObjectURL(blob) fetchedDataUrls[fileId] = url } const filenameEl = /** @type {HTMLHeadingElement | null} */ ( el.querySelector('h1.pl-md-player') ) // eslint-disable-next-line unicorn/prefer-dom-node-text-content const fileBaseName = filenameEl ? `${fileId} - ${filenameEl.innerText}` : fileId openSaveDialog(url, fileBaseName) } window.addEventListener('load', () => { const downloadArea = /** @type {HTMLDivElement | null} */ ( document.getElementById('download') ) const originalDownBtn = downloadArea?.firstElementChild if (!originalDownBtn) { PNotify.error({ title: name, text: '添加下载按钮失败:定位不到目标元素' }) return } GM_addStyle(`a.btn.btn-primary.disabled { filter: grayscale(1); }`) const btnHtml = `<a class="btn btn-primary btn-sm mb-3 mr-2" href="javascript:void">` + `<span class="fa fa-download"></span> ${name}` + `</a>` originalDownBtn.insertAdjacentHTML('afterend', btnHtml) const btn = /** @type {HTMLAnchorElement} */ (originalDownBtn.nextElementSibling) btn.addEventListener('click', async () => { if (btn.classList.contains('disabled')) return btn.classList.add('disabled') try { await saveCurrentMidi() } catch (e) { PNotify.error({ title: name, text: `出现意外错误\n${e}` }) } btn.classList.remove('disabled') }) }) window.addEventListener('beforeunload', () => { Object.values(fetchedDataUrls).forEach((v) => URL.revokeObjectURL(v)) }) })()
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址