Instantly share code, notes, and snippets.
Last active
June 8, 2025 06:28
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
Save Neppu-Nep/46cc935ab123547530a17332b868a322 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Monster Siren Record Download Button | |
// @namespace Nep | |
// @version 0.3 | |
// @description Adds a download button to Monster Siren music pages to download song records directly. | |
// @author Nep | |
// @match https://monster-siren.hypergryph.com/* | |
// @connect res01.hycdn.cn | |
// @grant GM_xmlhttpRequest | |
// @grant GM_addStyle | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
const BUTTON_ID = 'custom-download-btn'; | |
let currentSong = null; | |
function sanitizeFilename(filename) { | |
return filename.replace(/[<>:"/\\|?*]/g, '').trim(); | |
} | |
function handleDownloadClick() { | |
const button = document.getElementById(BUTTON_ID); | |
const originalText = button.textContent; | |
button.textContent = 'Downloading...'; | |
button.disabled = true; | |
const filename = sanitizeFilename(currentSong.name) + '.wav'; | |
GM_xmlhttpRequest({ | |
method: 'GET', | |
url: currentSong.sourceUrl, | |
headers: { | |
'Cache-Control': 'no-cache', | |
'Pragma': 'no-cache' | |
}, | |
responseType: 'blob', | |
onprogress: function(response) { | |
button.textContent = `Downloading... ${Math.round((response.loaded / response.total) * 100)}%`; | |
}, | |
onload: function(response) { | |
try { | |
const blob = new Blob([response.response], { type: 'application/octet-stream' }); | |
const objectUrl = URL.createObjectURL(blob); | |
const link = document.createElement('a'); | |
link.href = objectUrl; | |
link.download = filename || 'download'; | |
link.style.display = 'none'; | |
document.body.appendChild(link); | |
link.click(); | |
document.body.removeChild(link); | |
setTimeout(() => URL.revokeObjectURL(objectUrl), 1000); | |
button.textContent = `Downloaded ${filename}!`; | |
setTimeout(() => { | |
button.textContent = originalText; | |
button.disabled = false; | |
}, 2000); | |
} | |
catch (error) { | |
console.error('Error creating download:', error); | |
button.textContent = `Error downloading ${filename}`; | |
setTimeout(() => { | |
button.textContent = originalText; | |
button.disabled = false; | |
}, 2000); | |
} | |
}, | |
onerror: function(error) { | |
console.error('Download request failed:', error); | |
button.textContent = `Error downloading ${filename}`; | |
setTimeout(() => { | |
button.textContent = originalText; | |
button.disabled = false; | |
}, 2000); | |
} | |
}); | |
return false; | |
} | |
function injectDownloadButton() { | |
if (document.getElementById(BUTTON_ID)) { | |
return; | |
} | |
const controlContainer = document.querySelector('.infoAndControl___32k-O'); | |
if (!controlContainer) { | |
return; | |
} | |
const downloadButton = document.createElement('button'); | |
downloadButton.id = BUTTON_ID; | |
downloadButton.textContent = 'Download'; | |
downloadButton.className = 'custom-download-button'; | |
downloadButton.addEventListener('click', handleDownloadClick); | |
const currentUrl = window.location.href; | |
GM_xmlhttpRequest({ | |
method: 'GET', | |
url: "https://monster-siren.hypergryph.com/api/song/" + currentUrl.split('/').pop(), | |
headers: { | |
'Content-Type': 'application/json' | |
}, | |
onload: function(response) { | |
try { | |
const data = JSON.parse(response.responseText); | |
currentSong = data.data; | |
downloadButton.textContent = `Download ${sanitizeFilename(currentSong.name)}.wav`; | |
} | |
catch (error) { | |
console.error('Error fetching song:', error); | |
downloadButton.textContent = 'Error'; | |
} | |
}, | |
onerror: function(error) { | |
console.error('API request failed:', error); | |
downloadButton.textContent = 'Error'; | |
} | |
}); | |
downloadButton.style.position = 'fixed'; | |
downloadButton.style.top = '20px'; | |
downloadButton.style.right = '20px'; | |
downloadButton.style.zIndex = '9999'; | |
controlContainer.appendChild(downloadButton); | |
} | |
GM_addStyle(` | |
.custom-download-button { | |
background: #007bff; | |
color: white; | |
border: none; | |
padding: 10px 20px; | |
border-radius: 5px; | |
cursor: pointer; | |
font-size: 14px; | |
font-weight: bold; | |
transition: background-color 0.3s ease; | |
box-shadow: 0 2px 4px rgba(0,0,0,0.2); | |
} | |
.custom-download-button:hover:not(:disabled) { | |
background: #0056b3; | |
} | |
.custom-download-button:disabled { | |
background: #6c757d; | |
cursor: not-allowed; | |
} | |
.custom-download-button:active { | |
transform: translateY(1px); | |
} | |
`); | |
let currentUrl = window.location.href; | |
function handleUrlChange() { | |
const newUrl = window.location.href; | |
if (newUrl !== currentUrl) { | |
currentUrl = newUrl; | |
console.log('URL changed to:', newUrl); | |
const existingButton = document.getElementById(BUTTON_ID); | |
if (existingButton) { | |
existingButton.remove(); | |
} | |
setTimeout(injectDownloadButton, 100); | |
} | |
} | |
const originalPushState = history.pushState; | |
const originalReplaceState = history.replaceState; | |
history.pushState = function() { | |
originalPushState.apply(history, arguments); | |
setTimeout(handleUrlChange, 0); | |
}; | |
history.replaceState = function() { | |
originalReplaceState.apply(history, arguments); | |
setTimeout(handleUrlChange, 0); | |
}; | |
window.addEventListener('popstate', handleUrlChange); | |
setInterval(handleUrlChange, 1000); | |
const observer = new MutationObserver(function(mutations) { | |
handleUrlChange(); | |
if (!document.getElementById(BUTTON_ID)) { | |
injectDownloadButton(); | |
} | |
}); | |
observer.observe(document.body, { | |
childList: true, | |
subtree: true | |
}); | |
if (document.readyState === 'loading') { | |
document.addEventListener('DOMContentLoaded', injectDownloadButton); | |
} else { | |
injectDownloadButton(); | |
} | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment