v0.7.1 - refactor, generate full score PDF by webmscore

This commit is contained in:
Xmader 2020-05-19 00:43:16 -04:00
parent 6cc6a4a33b
commit b50552bf12
2 changed files with 477 additions and 410 deletions

863
dist/main.js vendored
View file

@ -3,7 +3,7 @@
// @namespace https://www.xmader.com/ // @namespace https://www.xmader.com/
// @homepageURL https://github.com/Xmader/musescore-downloader/ // @homepageURL https://github.com/Xmader/musescore-downloader/
// @supportURL https://github.com/Xmader/musescore-downloader/issues // @supportURL https://github.com/Xmader/musescore-downloader/issues
// @version 0.7.0 // @version 0.7.1
// @description download sheet music from musescore.com for free, no login or Musescore Pro required | 免登录、免 Musescore Pro免费下载 musescore.com 上的曲谱 // @description download sheet music from musescore.com for free, no login or Musescore Pro required | 免登录、免 Musescore Pro免费下载 musescore.com 上的曲谱
// @author Xmader // @author Xmader
// @match https://musescore.com/*/* // @match https://musescore.com/*/*
@ -27,11 +27,196 @@
}); });
} }
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
function createCommonjsModule(fn, module) {
return module = { exports: {} }, fn(module, module.exports), module.exports;
}
var FileSaver = createCommonjsModule(function (module, exports) {
(function (global, factory) {
{
factory();
}
})(commonjsGlobal, function () {
/*
* FileSaver.js
* A saveAs() FileSaver implementation.
*
* By Eli Grey, http://eligrey.com
*
* License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
* source : http://purl.eligrey.com/github/FileSaver.js
*/
// The one and only way of getting global scope in all environments
// https://stackoverflow.com/q/3277182/1008999
var _global = typeof window === 'object' && window.window === window ? window : typeof self === 'object' && self.self === self ? self : typeof commonjsGlobal === 'object' && commonjsGlobal.global === commonjsGlobal ? commonjsGlobal : void 0;
function bom(blob, opts) {
if (typeof opts === 'undefined') opts = {
autoBom: false
};else if (typeof opts !== 'object') {
console.warn('Deprecated: Expected third argument to be a object');
opts = {
autoBom: !opts
};
} // prepend BOM for UTF-8 XML and text/* types (including HTML)
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
return new Blob([String.fromCharCode(0xFEFF), blob], {
type: blob.type
});
}
return blob;
}
function download(url, name, opts) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.onload = function () {
saveAs(xhr.response, name, opts);
};
xhr.onerror = function () {
console.error('could not download file');
};
xhr.send();
}
function corsEnabled(url) {
var xhr = new XMLHttpRequest(); // use sync to avoid popup blocker
xhr.open('HEAD', url, false);
try {
xhr.send();
} catch (e) {}
return xhr.status >= 200 && xhr.status <= 299;
} // `a.click()` doesn't work for all browsers (#465)
function click(node) {
try {
node.dispatchEvent(new MouseEvent('click'));
} catch (e) {
var evt = document.createEvent('MouseEvents');
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
node.dispatchEvent(evt);
}
}
var saveAs = _global.saveAs || ( // probably in some web worker
typeof window !== 'object' || window !== _global ? function saveAs() {}
/* noop */
// Use download attribute first if possible (#193 Lumia mobile)
: 'download' in HTMLAnchorElement.prototype ? function saveAs(blob, name, opts) {
var URL = _global.URL || _global.webkitURL;
var a = document.createElement('a');
name = name || blob.name || 'download';
a.download = name;
a.rel = 'noopener'; // tabnabbing
// TODO: detect chrome extensions & packaged apps
// a.target = '_blank'
if (typeof blob === 'string') {
// Support regular links
a.href = blob;
if (a.origin !== location.origin) {
corsEnabled(a.href) ? download(blob, name, opts) : click(a, a.target = '_blank');
} else {
click(a);
}
} else {
// Support blobs
a.href = URL.createObjectURL(blob);
setTimeout(function () {
URL.revokeObjectURL(a.href);
}, 4E4); // 40s
setTimeout(function () {
click(a);
}, 0);
}
} // Use msSaveOrOpenBlob as a second approach
: 'msSaveOrOpenBlob' in navigator ? function saveAs(blob, name, opts) {
name = name || blob.name || 'download';
if (typeof blob === 'string') {
if (corsEnabled(blob)) {
download(blob, name, opts);
} else {
var a = document.createElement('a');
a.href = blob;
a.target = '_blank';
setTimeout(function () {
click(a);
});
}
} else {
navigator.msSaveOrOpenBlob(bom(blob, opts), name);
}
} // Fallback to using FileReader and a popup
: function saveAs(blob, name, opts, popup) {
// Open a popup immediately do go around popup blocker
// Mostly only available on user interaction and the fileReader is async so...
popup = popup || open('', '_blank');
if (popup) {
popup.document.title = popup.document.body.innerText = 'downloading...';
}
if (typeof blob === 'string') return download(blob, name, opts);
var force = blob.type === 'application/octet-stream';
var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari;
var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent);
if ((isChromeIOS || force && isSafari) && typeof FileReader === 'object') {
// Safari doesn't allow downloading of blob URLs
var reader = new FileReader();
reader.onloadend = function () {
var url = reader.result;
url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;');
if (popup) popup.location.href = url;else location = url;
popup = null; // reverse-tabnabbing #460
};
reader.readAsDataURL(blob);
} else {
var URL = _global.URL || _global.webkitURL;
var url = URL.createObjectURL(blob);
if (popup) popup.location = url;else location.href = url;
popup = null; // reverse-tabnabbing #460
setTimeout(function () {
URL.revokeObjectURL(url);
}, 4E4); // 40s
}
});
_global.saveAs = saveAs.saveAs = saveAs;
{
module.exports = saveAs;
}
});
});
const saveAs = FileSaver.saveAs;
const waitForDocumentLoaded = () => { const waitForDocumentLoaded = () => {
if (document.readyState !== "complete") { if (document.readyState !== 'complete') {
return new Promise(resolve => { return new Promise(resolve => {
document.addEventListener("readystatechange", () => { document.addEventListener('readystatechange', () => {
if (document.readyState == "complete") { if (document.readyState === 'complete') {
resolve(); resolve();
} }
}, { once: true }); }, { once: true });
@ -42,42 +227,6 @@
} }
}; };
/**
* the site key for Google reCAPTCHA v3
*/
const SITE_KEY = "6Ldxtt8UAAAAALvcRqWTlVOVIB7MmEWwN-zw_9fM";
let gr;
/**
* load reCAPTCHA
*/
const load = () => {
// load script
const script = document.createElement("script");
script.src = `https://www.recaptcha.net/recaptcha/api.js?render=${SITE_KEY}`;
script.async = true;
document.body.appendChild(script);
// add css
const style = document.createElement("style");
style.innerHTML = ".grecaptcha-badge { display: none !important; }";
document.head.appendChild(style);
return new Promise((resolve) => {
script.onload = () => {
const grecaptcha = window["grecaptcha"];
grecaptcha.ready(() => resolve(grecaptcha));
};
});
};
const init = () => {
if (!gr) {
gr = load();
}
return gr;
};
const execute = () => __awaiter(void 0, void 0, void 0, function* () {
const captcha = yield init();
return captcha.execute(SITE_KEY, { action: "downloadmscz" });
});
var global$1 = (typeof global !== "undefined" ? global : var global$1 = (typeof global !== "undefined" ? global :
typeof self !== "undefined" ? self : typeof self !== "undefined" ? self :
typeof window !== "undefined" ? window : {}); typeof window !== "undefined" ? window : {});
@ -26073,38 +26222,6 @@ Please pipe the document into a Node stream.\
}); });
/// <reference lib="webworker" /> /// <reference lib="webworker" />
const generatePDF = (imgURLs, imgType, width, height) => __awaiter(void 0, void 0, void 0, function* () {
// @ts-ignore
const pdf = new PDFDocument({
// compress: true,
size: [width, height],
autoFirstPage: false,
margin: 0,
layout: "portrait",
});
if (imgType == "png") {
const imgDataUrlList = yield Promise.all(imgURLs.map(fetchDataURL));
imgDataUrlList.forEach((data) => {
pdf.addPage();
pdf.image(data, {
width,
height,
});
});
}
else { // imgType == "svg"
const svgList = yield Promise.all(imgURLs.map(fetchText));
svgList.forEach((svg) => {
pdf.addPage();
source(pdf, svg, 0, 0, {
preserveAspectRatio: "none",
});
});
}
// @ts-ignore
const buf = yield pdf.getBuffer();
return buf.buffer;
});
const getDataURL = (blob) => { const getDataURL = (blob) => {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const reader = new FileReader(); const reader = new FileReader();
@ -26125,6 +26242,38 @@ Please pipe the document into a Node stream.\
const r = yield fetch(imgUrl); const r = yield fetch(imgUrl);
return r.text(); return r.text();
}); });
const generatePDF = (imgURLs, imgType, width, height) => __awaiter(void 0, void 0, void 0, function* () {
// @ts-ignore
const pdf = new PDFDocument({
// compress: true,
size: [width, height],
autoFirstPage: false,
margin: 0,
layout: 'portrait',
});
if (imgType === 'png') {
const imgDataUrlList = yield Promise.all(imgURLs.map(fetchDataURL));
imgDataUrlList.forEach((data) => {
pdf.addPage();
pdf.image(data, {
width,
height,
});
});
}
else { // imgType == "svg"
const svgList = yield Promise.all(imgURLs.map(fetchText));
svgList.forEach((svg) => {
pdf.addPage();
source(pdf, svg, 0, 0, {
preserveAspectRatio: 'none',
});
});
}
// @ts-ignore
const buf = yield pdf.getBuffer();
return buf.buffer;
});
onmessage = (e) => __awaiter(void 0, void 0, void 0, function* () { onmessage = (e) => __awaiter(void 0, void 0, void 0, function* () {
const [imgURLs, imgType, width, height,] = e.data; const [imgURLs, imgType, width, height,] = e.data;
const pdfBuf = yield generatePDF(imgURLs, imgType, width, height); const pdfBuf = yield generatePDF(imgURLs, imgType, width, height);
@ -26135,7 +26284,7 @@ Please pipe the document into a Node stream.\
}; };
const scriptUrlFromFunction = (fn) => { const scriptUrlFromFunction = (fn) => {
const blob = new Blob(["(" + fn.toString() + ")()"], { type: "application/javascript" }); const blob = new Blob(['(' + fn.toString() + ')()'], { type: 'application/javascript' });
return URL.createObjectURL(blob); return URL.createObjectURL(blob);
}; };
class PDFWorkerHelper extends Worker { class PDFWorkerHelper extends Worker {
@ -26152,208 +26301,77 @@ Please pipe the document into a Node stream.\
]; ];
this.postMessage(msg); this.postMessage(msg);
return new Promise((resolve) => { return new Promise((resolve) => {
this.addEventListener("message", (e) => { this.addEventListener('message', (e) => {
resolve(e.data); resolve(e.data);
}); });
}); });
} }
} }
var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; const scoreinfo = {
get playerdata() {
function createCommonjsModule(fn, module) { // @ts-ignore
return module = { exports: {} }, fn(module, module.exports), module.exports; return window.UGAPP.store.jmuse_settings.score_player;
} },
get id() {
var FileSaver = createCommonjsModule(function (module, exports) { return this.playerdata.json.id;
(function (global, factory) { },
{ get title() {
factory();
}
})(commonjsGlobal, function () {
/*
* FileSaver.js
* A saveAs() FileSaver implementation.
*
* By Eli Grey, http://eligrey.com
*
* License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
* source : http://purl.eligrey.com/github/FileSaver.js
*/
// The one and only way of getting global scope in all environments
// https://stackoverflow.com/q/3277182/1008999
var _global = typeof window === 'object' && window.window === window ? window : typeof self === 'object' && self.self === self ? self : typeof commonjsGlobal === 'object' && commonjsGlobal.global === commonjsGlobal ? commonjsGlobal : void 0;
function bom(blob, opts) {
if (typeof opts === 'undefined') opts = {
autoBom: false
};else if (typeof opts !== 'object') {
console.warn('Deprecated: Expected third argument to be a object');
opts = {
autoBom: !opts
};
} // prepend BOM for UTF-8 XML and text/* types (including HTML)
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
return new Blob([String.fromCharCode(0xFEFF), blob], {
type: blob.type
});
}
return blob;
}
function download(url, name, opts) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'blob';
xhr.onload = function () {
saveAs(xhr.response, name, opts);
};
xhr.onerror = function () {
console.error('could not download file');
};
xhr.send();
}
function corsEnabled(url) {
var xhr = new XMLHttpRequest(); // use sync to avoid popup blocker
xhr.open('HEAD', url, false);
try { try {
xhr.send(); return this.playerdata.json.metadata.title;
} catch (e) {} }
catch (_) {
return xhr.status >= 200 && xhr.status <= 299; return '';
} // `a.click()` doesn't work for all browsers (#465) }
},
get fileName() {
function click(node) { return this.title.replace(/[\s<>:{}"/\\|?*~.\0\cA-\cZ]+/g, '_');
},
get pageCount() {
try { try {
node.dispatchEvent(new MouseEvent('click')); return this.playerdata.json.metadata.pages;
} catch (e) {
var evt = document.createEvent('MouseEvents');
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80, 20, false, false, false, false, 0, null);
node.dispatchEvent(evt);
} }
catch (_) {
return document.querySelectorAll('img[src*=score_]').length;
} }
},
var saveAs = _global.saveAs || ( // probably in some web worker get baseUrl() {
typeof window !== 'object' || window !== _global ? function saveAs() {} return this.playerdata.urls.image_path;
/* noop */ },
// Use download attribute first if possible (#193 Lumia mobile) get mxlUrl() {
: 'download' in HTMLAnchorElement.prototype ? function saveAs(blob, name, opts) { return this.baseUrl + 'score.mxl';
var URL = _global.URL || _global.webkitURL; },
var a = document.createElement('a'); get midiUrl() {
name = name || blob.name || 'download'; return this.playerdata.urls.midi;
a.download = name; },
a.rel = 'noopener'; // tabnabbing get mp3Url() {
// TODO: detect chrome extensions & packaged apps return this.playerdata.urls.mp3;
// a.target = '_blank' },
get msczUrl() {
if (typeof blob === 'string') { // https://github.com/Xmader/cloudflare-worker-musescore-mscz
// Support regular links return `https://musescore.now.sh/api/mscz?id=${this.id}&token=`;
a.href = blob; },
get sheetImgType() {
if (a.origin !== location.origin) { try {
corsEnabled(a.href) ? download(blob, name, opts) : click(a, a.target = '_blank'); const imgE = document.querySelector('img[src*=score_]');
} else { const { pathname } = new URL(imgE.src);
click(a); // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const imgtype = pathname.match(/\.(\w+)$/)[1];
return imgtype;
} }
} else { catch (_) {
// Support blobs // return null
a.href = URL.createObjectURL(blob); return 'svg';
setTimeout(function () {
URL.revokeObjectURL(a.href);
}, 4E4); // 40s
setTimeout(function () {
click(a);
}, 0);
} }
} // Use msSaveOrOpenBlob as a second approach },
: 'msSaveOrOpenBlob' in navigator ? function saveAs(blob, name, opts) {
name = name || blob.name || 'download';
if (typeof blob === 'string') {
if (corsEnabled(blob)) {
download(blob, name, opts);
} else {
var a = document.createElement('a');
a.href = blob;
a.target = '_blank';
setTimeout(function () {
click(a);
});
}
} else {
navigator.msSaveOrOpenBlob(bom(blob, opts), name);
}
} // Fallback to using FileReader and a popup
: function saveAs(blob, name, opts, popup) {
// Open a popup immediately do go around popup blocker
// Mostly only available on user interaction and the fileReader is async so...
popup = popup || open('', '_blank');
if (popup) {
popup.document.title = popup.document.body.innerText = 'downloading...';
}
if (typeof blob === 'string') return download(blob, name, opts);
var force = blob.type === 'application/octet-stream';
var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari;
var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent);
if ((isChromeIOS || force && isSafari) && typeof FileReader === 'object') {
// Safari doesn't allow downloading of blob URLs
var reader = new FileReader();
reader.onloadend = function () {
var url = reader.result;
url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;');
if (popup) popup.location.href = url;else location = url;
popup = null; // reverse-tabnabbing #460
}; };
reader.readAsDataURL(blob);
} else {
var URL = _global.URL || _global.webkitURL;
var url = URL.createObjectURL(blob);
if (popup) popup.location = url;else location.href = url;
popup = null; // reverse-tabnabbing #460
setTimeout(function () {
URL.revokeObjectURL(url);
}, 4E4); // 40s
}
});
_global.saveAs = saveAs.saveAs = saveAs;
{
module.exports = saveAs;
}
});
});
const saveAs = FileSaver.saveAs;
const PROCESSING_TEXT = "Processing…";
const FAILED_TEXT = "❌Download Failed!";
const WEBMSCORE_URL = "https://cdn.jsdelivr.net/npm/webmscore@0.5/webmscore.js";
let pdfBlob; let pdfBlob;
let msczBufferP; const _downloadPDF = (imgURLs, imgType, name) => __awaiter(void 0, void 0, void 0, function* () {
const generatePDF = (imgURLs, imgType, name) => __awaiter(void 0, void 0, void 0, function* () {
if (pdfBlob) { if (pdfBlob) {
return saveAs(pdfBlob, `${name}.pdf`); return saveAs(pdfBlob, `${name}.pdf`);
} }
const cachedImg = document.querySelector("img[src*=score_]"); const cachedImg = document.querySelector('img[src*=score_]');
const { naturalWidth: width, naturalHeight: height } = cachedImg; const { naturalWidth: width, naturalHeight: height } = cachedImg;
const worker = new PDFWorkerHelper(); const worker = new PDFWorkerHelper();
const pdfArrayBuffer = yield worker.generatePDF(imgURLs, imgType, width, height); const pdfArrayBuffer = yield worker.generatePDF(imgURLs, imgType, width, height);
@ -26361,38 +26379,55 @@ Please pipe the document into a Node stream.\
pdfBlob = new Blob([pdfArrayBuffer]); pdfBlob = new Blob([pdfArrayBuffer]);
saveAs(pdfBlob, `${name}.pdf`); saveAs(pdfBlob, `${name}.pdf`);
}); });
const getPagesNumber = (scorePlayerData) => { const downloadPDF = () => __awaiter(void 0, void 0, void 0, function* () {
try { const imgType = scoreinfo.sheetImgType;
return scorePlayerData.json.metadata.pages; const pageCount = scoreinfo.pageCount;
} const sheetImgURLs = Array.from({ length: pageCount }).map((_, i) => {
catch (_) { return scoreinfo.baseUrl + `score_${i}.${imgType}`;
return document.querySelectorAll("img[src*=score_]").length; });
} return _downloadPDF(sheetImgURLs, imgType, scoreinfo.fileName);
});
/**
* the site key for Google reCAPTCHA v3
*/
const SITE_KEY = '6Ldxtt8UAAAAALvcRqWTlVOVIB7MmEWwN-zw_9fM';
let gr;
/**
* load reCAPTCHA
*/
const load = () => {
// load script
const script = document.createElement('script');
script.src = `https://www.recaptcha.net/recaptcha/api.js?render=${SITE_KEY}`;
script.async = true;
document.body.appendChild(script);
// add css
const style = document.createElement('style');
style.innerHTML = '.grecaptcha-badge { display: none !important; }';
document.head.appendChild(style);
return new Promise((resolve) => {
script.onload = () => {
const grecaptcha = window['grecaptcha'];
grecaptcha.ready(() => resolve(grecaptcha));
}; };
const getImgType = () => { });
try {
const imgE = document.querySelector("img[src*=score_]");
const { pathname } = new URL(imgE.src);
const imgtype = pathname.match(/\.(\w+)$/)[1];
return imgtype;
}
catch (_) {
return null;
}
}; };
const getTitle = (scorePlayerData) => { const init = () => {
try { if (!gr) {
return scorePlayerData.json.metadata.title; gr = load();
}
catch (_) {
return "";
} }
return gr;
}; };
const getScoreFileName = (scorePlayerData) => { const execute = () => __awaiter(void 0, void 0, void 0, function* () {
return getTitle(scorePlayerData).replace(/[\s<>:{}"/\\|?*~.\0\cA-\cZ]+/g, "_"); const captcha = yield init();
}; return captcha.execute(SITE_KEY, { action: 'downloadmscz' });
const fetchMscz = (url) => __awaiter(void 0, void 0, void 0, function* () { });
let msczBufferP;
const fetchMscz = () => __awaiter(void 0, void 0, void 0, function* () {
if (!msczBufferP) { if (!msczBufferP) {
const url = scoreinfo.msczUrl;
msczBufferP = (() => __awaiter(void 0, void 0, void 0, function* () { msczBufferP = (() => __awaiter(void 0, void 0, void 0, function* () {
const token = yield execute(); const token = yield execute();
const r = yield fetch(url + token); const r = yield fetch(url + token);
@ -26402,159 +26437,191 @@ Please pipe the document into a Node stream.\
} }
return msczBufferP; return msczBufferP;
}); });
const downloadMscz = () => __awaiter(void 0, void 0, void 0, function* () {
const data = new Blob([yield fetchMscz()]);
const filename = scoreinfo.fileName;
saveAs(data, `${filename}.mscz`);
});
/**
* Select the original Download Button
*/
const getDownloadBtn = () => {
const btnsDiv = document.querySelector('.score-right .buttons-wrapper') || document.querySelectorAll('aside section > div')[4];
const btn = btnsDiv.querySelector('button, .button');
btn.onclick = null;
// fix the icon of the download btn
// if the `btn` seleted was a `Print` btn, replace the `print` icon with the `download` icon
const svgPath = btn.querySelector('svg > path');
if (svgPath) {
svgPath.setAttribute('d', 'M9.6 2.4h4.8V12h2.784l-5.18 5.18L6.823 12H9.6V2.4zM19.2 19.2H4.8v2.4h14.4v-2.4z');
}
if (btn.nodeName.toLowerCase() === 'button') {
btn.setAttribute('style', 'width: 205px !important');
}
else {
btn.dataset.target = '';
}
return btn;
};
class BtnList {
constructor(templateBtn) {
this.templateBtn = templateBtn;
this.list = [];
}
add(options) {
const btn = this.templateBtn.cloneNode(true);
const textNode = [...btn.childNodes].find((x) => {
const txt = x.textContent;
return txt.includes('Download') || txt.includes('Print');
});
const setText = (str) => {
textNode.textContent = str;
};
setText(options.name);
btn.onclick = () => {
options.action(options.name, btn, setText);
};
this.list.push(btn);
return btn;
}
/**
* replace the template button with the list of new buttons
*/
commit() {
this.templateBtn.replaceWith(...this.list);
}
}
// eslint-disable-next-line @typescript-eslint/no-namespace
var BtnAction;
(function (BtnAction) {
BtnAction.PROCESSING_TEXT = 'Processing…';
BtnAction.ERROR_TEXT = '❌Download Failed!';
BtnAction.openUrl = (url) => {
return () => window.open(url);
};
BtnAction.process = (fn) => {
return (name, btn, setText) => __awaiter(this, void 0, void 0, function* () {
const _onclick = btn.onclick;
btn.onclick = null;
setText(BtnAction.PROCESSING_TEXT);
try {
yield fn();
setText(name);
}
catch (err) {
setText(BtnAction.ERROR_TEXT);
console.error(err);
}
btn.onclick = _onclick;
});
};
})(BtnAction || (BtnAction = {}));
const WEBMSCORE_URL = 'https://cdn.jsdelivr.net/npm/webmscore@0.5/webmscore.js';
const main = () => { const main = () => {
// @ts-ignore // @ts-ignore
if (!window.UGAPP || !window.UGAPP.store || !window.UGAPP.store.jmuse_settings) { if (!window.UGAPP || !window.UGAPP.store || !window.UGAPP.store.jmuse_settings) {
return; return;
} }
// init recaptcha // init recaptcha
// eslint-disable-next-line @typescript-eslint/no-floating-promises
init(); init();
// @ts-ignore const btnList = new BtnList(getDownloadBtn());
const scorePlayer = window.UGAPP.store.jmuse_settings.score_player; btnList.add({
const { id } = scorePlayer.json; name: 'Download MSCZ',
const baseURL = scorePlayer.urls.image_path; action: BtnAction.process(downloadMscz),
const filename = getScoreFileName(scorePlayer);
// https://github.com/Xmader/cloudflare-worker-musescore-mscz
const msczURL = `https://musescore.now.sh/api/mscz?id=${id}&token=`;
const mxlURL = baseURL + "score.mxl";
const { midi: midiURL, mp3: mp3URL } = scorePlayer.urls;
const btnsDiv = document.querySelector(".score-right .buttons-wrapper") || document.querySelectorAll("aside section > div")[4];
const downloadBtn = btnsDiv.querySelector("button, .button");
downloadBtn.onclick = null;
// fix the icon of the download btn
// if the `downloadBtn` seleted was a `Print` btn, replace the `print` icon with the `download` icon
const svgPath = downloadBtn.querySelector("svg > path");
if (svgPath) {
svgPath.setAttribute("d", "M9.6 2.4h4.8V12h2.784l-5.18 5.18L6.823 12H9.6V2.4zM19.2 19.2H4.8v2.4h14.4v-2.4z");
}
const imgType = getImgType() || "svg";
const sheetImgURLs = Array.from({ length: getPagesNumber(scorePlayer) }).fill(null).map((_, i) => {
return baseURL + `score_${i}.${imgType}`;
}); });
const downloadURLs = { btnList.add({
"MSCZ": null, name: 'Download PDF',
"PDF": null, action: BtnAction.process(downloadPDF),
"MusicXML": mxlURL,
"MIDI": midiURL,
"MP3": mp3URL,
"Parts": null,
};
const createBtn = (name) => {
const btn = downloadBtn.cloneNode(true);
if (btn.nodeName.toLowerCase() == "button") {
btn.setAttribute("style", "width: 205px !important");
}
else {
btn.dataset.target = "";
}
const textNode = [...btn.childNodes].find((x) => {
return x.textContent.includes("Download") || x.textContent.includes("Print");
}); });
textNode.textContent = `Download ${name}`; btnList.add({
return { name: 'Download MusicXML',
btn, action: BtnAction.openUrl(scoreinfo.mxlUrl),
textNode,
};
};
const newDownloadBtns = Object.keys(downloadURLs).map((name) => {
const url = downloadURLs[name];
const { btn, textNode } = createBtn(name);
if (name == "PDF") {
btn.onclick = () => __awaiter(void 0, void 0, void 0, function* () {
const filename = getScoreFileName(scorePlayer);
textNode.textContent = PROCESSING_TEXT;
try {
yield generatePDF(sheetImgURLs, imgType, filename);
textNode.textContent = "Download PDF";
}
catch (err) {
textNode.textContent = FAILED_TEXT;
console.error(err);
}
}); });
} btnList.add({
else if (name == "MSCZ") { name: 'Download MIDI',
btn.onclick = () => __awaiter(void 0, void 0, void 0, function* () { action: BtnAction.openUrl(scoreinfo.midiUrl),
textNode.textContent = PROCESSING_TEXT;
try {
const data = new Blob([yield fetchMscz(msczURL)]);
textNode.textContent = "Download MSCZ";
saveAs(data, `${filename}.mscz`);
}
catch (err) {
textNode.textContent = FAILED_TEXT;
console.error(err);
}
}); });
} btnList.add({
else if (name == "Parts") { // download individual parts name: 'Download MP3',
btn.title = "Download individual parts (BETA)"; action: BtnAction.openUrl(scoreinfo.mp3Url),
const cb = btn.onclick = () => __awaiter(void 0, void 0, void 0, function* () { });
btnList.add({
name: 'Individual Parts',
action(btnName, btn, setText) {
return __awaiter(this, void 0, void 0, function* () {
const _onclick = btn.onclick;
btn.onclick = null; btn.onclick = null;
textNode.textContent = PROCESSING_TEXT; setText(BtnAction.PROCESSING_TEXT);
const w = window.open(""); const w = window.open('');
const txt = document.createTextNode(PROCESSING_TEXT); const txt = document.createTextNode(BtnAction.PROCESSING_TEXT);
w.document.body.append(txt); w.document.body.append(txt);
// set page hooks // set page hooks
// eslint-disable-next-line prefer-const
let score;
const destroy = () => { const destroy = () => {
score.destroy(); score && score.destroy();
w.close(); w.close();
}; };
window.addEventListener("unload", destroy); window.addEventListener('unload', destroy);
w.addEventListener("beforeunload", () => { w.addEventListener('beforeunload', () => {
score.destroy(); score && score.destroy();
window.removeEventListener("unload", destroy); window.removeEventListener('unload', destroy);
textNode.textContent = "Download Parts"; setText(btnName);
btn.onclick = cb; btn.onclick = _onclick;
}); });
// load webmscore (https://github.com/LibreScore/webmscore) // load webmscore (https://github.com/LibreScore/webmscore)
const script = w.document.createElement("script"); const script = w.document.createElement('script');
script.src = WEBMSCORE_URL; script.src = WEBMSCORE_URL;
w.document.body.append(script); w.document.body.append(script);
yield new Promise(resolve => { script.onload = resolve; }); yield new Promise(resolve => { script.onload = resolve; });
// parse mscz data // parse mscz data
const data = new Uint8Array(new Uint8Array(yield fetchMscz(msczURL)) // copy its ArrayBuffer const data = new Uint8Array(new Uint8Array(yield fetchMscz()) // copy its ArrayBuffer
); );
const score = yield w["WebMscore"].load("mscz", data); score = yield w['WebMscore'].load('mscz', data);
yield score.generateExcerpts(); yield score.generateExcerpts();
const metadata = yield score.metadata(); const metadata = yield score.metadata();
console.log("score metadata loaded by webmscore", metadata); console.log('score metadata loaded by webmscore', metadata);
// render the part selection page // render the part selection page
txt.remove(); txt.remove();
const fieldset = w.document.createElement("fieldset"); const fieldset = w.document.createElement('fieldset');
metadata.excerpts.unshift({ id: -1, title: 'Full score' });
for (const excerpt of metadata.excerpts) { for (const excerpt of metadata.excerpts) {
const e = w.document.createElement("input"); const id = excerpt.id;
e.name = "score-part"; const partName = excerpt.title;
e.type = "radio"; const e = w.document.createElement('input');
e.value = excerpt.id; e.name = 'score-part';
const label = w.document.createElement("label"); e.type = 'radio';
label.innerText = excerpt.title; e.alt = partName;
const br = w.document.createElement("br"); e.value = id;
e.checked = id === 0; // initially select the first part
const label = w.document.createElement('label');
label.innerText = partName;
const br = w.document.createElement('br');
fieldset.append(e, label, br); fieldset.append(e, label, br);
} }
const submitBtn = w.document.createElement("input"); const submitBtn = w.document.createElement('input');
submitBtn.type = "submit"; submitBtn.type = 'submit';
submitBtn.value = "Download PDF"; submitBtn.value = 'Download PDF';
fieldset.append(submitBtn); fieldset.append(submitBtn);
w.document.body.append(fieldset); w.document.body.append(fieldset);
submitBtn.onclick = () => __awaiter(void 0, void 0, void 0, function* () { submitBtn.onclick = () => __awaiter(this, void 0, void 0, function* () {
const checked = w.document.querySelector("input:checked"); const checked = fieldset.querySelector('input:checked');
const id = checked.value; const id = checked.value;
const partName = checked.alt;
yield score.setExcerptId(id); yield score.setExcerptId(id);
const filename = scoreinfo.fileName;
const data = new Blob([yield score.savePdf()]); const data = new Blob([yield score.savePdf()]);
saveAs(data, `${filename}-part-${id}.pdf`); saveAs(data, `${filename} - ${partName}.pdf`);
}); });
}); });
} },
else { }).title = 'Download individual parts (BETA)';
btn.onclick = () => { btnList.commit();
window.open(url);
};
}
return btn;
});
downloadBtn.replaceWith(...newDownloadBtns);
}; };
// eslint-disable-next-line @typescript-eslint/no-floating-promises
waitForDocumentLoaded().then(main); waitForDocumentLoaded().then(main);
}.toString() + ')()') }.toString() + ')()')

View file

@ -1,6 +1,6 @@
{ {
"name": "musescore-downloader", "name": "musescore-downloader",
"version": "0.7.0", "version": "0.7.1",
"description": "download sheet music from musescore.com for free, no login or Musescore Pro required | 免登录、免 Musescore Pro免费下载 musescore.com 上的曲谱", "description": "download sheet music from musescore.com for free, no login or Musescore Pro required | 免登录、免 Musescore Pro免费下载 musescore.com 上的曲谱",
"main": "dist/main.js", "main": "dist/main.js",
"repository": { "repository": {