function base64ToUrl(base64, mimeType = '') { // Split the base64 string if it contains a data URL prefix const [prefix, data] = base64.includes(',') ? base64.split(',') : ['', base64]; // Try to extract the MIME type from the prefix if not provided const mime = mimeType || (prefix.match(/data:(.*?);base64/) || [])[1] || 'application/octet-stream'; // Decode base64 to raw binary data const binary = atob(data); const len = binary.length; const bytes = new Uint8Array(len); for (let i = 0; i < len; i++) { bytes[i] = binary.charCodeAt(i); } // Create a blob and generate a URL const blob = new Blob([bytes], { type: mime }); return URL.createObjectURL(blob); } function getElementByXpath(path, timeout = 5000) { return new Promise((resolve, reject) => { const intervalTime = 500; let elapsedTime = 0; const interval = setInterval(() => { let node = document.evaluate(path, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; if (node) { clearInterval(interval); resolve(node); } elapsedTime += intervalTime; if (elapsedTime >= timeout) { clearInterval(interval); reject(new Error(`XPath "${path}" not found within ${timeout}ms`)); } }, intervalTime); }); } async function uploadFileFromURL(url, filename, file_element) { try { if (!file_element) throw new Error("File input element not found."); const response = await fetch(url); if (!response.ok) throw new Error(`Failed to fetch file: ${response.statusText}`); const blob = await response.blob(); const newFile = new File([blob], filename, { type: blob.type }); const dataTransfer = new DataTransfer(); if (file_element.multiple) { for (const file of file_element.files) { dataTransfer.items.add(file); } } dataTransfer.items.add(newFile); file_element.files = dataTransfer.files; file_element.dispatchEvent(new Event("change", { bubbles: true })); } catch (error) { console.error("Error:", error); } } function waitForElement(selector, timeout = 5000) { return new Promise((resolve, reject) => { const intervalTime = 500; let elapsedTime = 0; const interval = setInterval(() => { const element = document.querySelector(selector); if (element) { clearInterval(interval); resolve(element); } elapsedTime += intervalTime; if (elapsedTime >= timeout) { clearInterval(interval); reject(new Error(`Element "${selector}" not found within ${timeout}ms`)); } }, intervalTime); }); } document.addEventListener("DOMContentLoaded", function () { getElementByXpath("//*[text()='Hide top bar']", 5000) .then((node) => { node.scrollIntoView(); node.click(); return waitForElement("#midi_file_input"); }) .then((file_upload) => { if (!file_upload) throw new Error("File input element not found."); // Wait for stability before proceeding return new Promise(resolve => setTimeout(() => resolve(file_upload), 500)); }) .then((file_upload) => { return waitForElement(".show_top_button").then((node) => { node.remove(); selector = document.getElementById("renderer_mode_selector"); selector.selectedIndex = 2; selector.dispatchEvent(new Event('change')); // return uploadFileFromURL("/output.mid", "output.mid", file_upload); }); }) .catch((error) => { console.error("Error:", error); }); }); window.addEventListener("message", async (event) => { if (event.data?.type === "load-midi" && event.data.name) { const url = event.data.url; const file_name = event.data.name const input = document.getElementById("midi_file_input"); if (url && input) { await uploadFileFromURL(url, file_name, input); } } });