Skip to content

Instantly share code, notes, and snippets.

@Neppu-Nep
Last active June 8, 2025 06:28
Show Gist options
  • Save Neppu-Nep/46cc935ab123547530a17332b868a322 to your computer and use it in GitHub Desktop.
Save Neppu-Nep/46cc935ab123547530a17332b868a322 to your computer and use it in GitHub Desktop.
// ==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