feat: download individual parts
This commit is contained in:
parent
208be7f4c9
commit
205875931d
1 changed files with 93 additions and 13 deletions
106
src/main.ts
106
src/main.ts
|
@ -9,7 +9,12 @@ import FileSaver from "file-saver/dist/FileSaver.js"
|
||||||
|
|
||||||
const saveAs: typeof import("file-saver").saveAs = FileSaver.saveAs
|
const saveAs: typeof import("file-saver").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: Blob
|
let pdfBlob: Blob
|
||||||
|
let msczBufferP: Promise<ArrayBuffer>
|
||||||
|
|
||||||
const generatePDF = async (imgURLs: string[], imgType: "svg" | "png", name?: string) => {
|
const generatePDF = async (imgURLs: string[], imgType: "svg" | "png", name?: string) => {
|
||||||
if (pdfBlob) {
|
if (pdfBlob) {
|
||||||
|
@ -59,6 +64,19 @@ const getScoreFileName = (scorePlayerData: ScorePlayerData) => {
|
||||||
return getTitle(scorePlayerData).replace(/[\s<>:{}"/\\|?*~.\0\cA-\cZ]+/g, "_")
|
return getTitle(scorePlayerData).replace(/[\s<>:{}"/\\|?*~.\0\cA-\cZ]+/g, "_")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const fetchMscz = async (url: string): Promise<ArrayBuffer> => {
|
||||||
|
if (!msczBufferP) {
|
||||||
|
msczBufferP = (async () => {
|
||||||
|
const token = await recaptcha.execute()
|
||||||
|
const r = await fetch(url + token)
|
||||||
|
const data = await r.arrayBuffer()
|
||||||
|
return data
|
||||||
|
})()
|
||||||
|
}
|
||||||
|
|
||||||
|
return msczBufferP
|
||||||
|
}
|
||||||
|
|
||||||
const main = () => {
|
const main = () => {
|
||||||
|
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
|
@ -73,7 +91,7 @@ const main = () => {
|
||||||
const { id } = scorePlayer.json
|
const { id } = scorePlayer.json
|
||||||
const baseURL = scorePlayer.urls.image_path
|
const baseURL = scorePlayer.urls.image_path
|
||||||
|
|
||||||
// const msczURL = `https://musescore.com/static/musescore/scoredata/score/${getIndexPath(id)}/${id}/score_${vid}_${scoreHexId}.mscz`
|
const filename = getScoreFileName(scorePlayer)
|
||||||
|
|
||||||
// https://github.com/Xmader/cloudflare-worker-musescore-mscz
|
// https://github.com/Xmader/cloudflare-worker-musescore-mscz
|
||||||
const msczURL = `https://musescore.now.sh/api/mscz?id=${id}&token=`
|
const msczURL = `https://musescore.now.sh/api/mscz?id=${id}&token=`
|
||||||
|
@ -99,11 +117,12 @@ const main = () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
const downloadURLs = {
|
const downloadURLs = {
|
||||||
"MSCZ": msczURL,
|
"MSCZ": null,
|
||||||
"PDF": null,
|
"PDF": null,
|
||||||
"MusicXML": mxlURL,
|
"MusicXML": mxlURL,
|
||||||
"MIDI": midiURL,
|
"MIDI": midiURL,
|
||||||
"MP3": mp3URL,
|
"MP3": mp3URL,
|
||||||
|
"Parts": null,
|
||||||
}
|
}
|
||||||
|
|
||||||
const createBtn = (name: string) => {
|
const createBtn = (name: string) => {
|
||||||
|
@ -134,35 +153,96 @@ const main = () => {
|
||||||
btn.onclick = async () => {
|
btn.onclick = async () => {
|
||||||
const filename = getScoreFileName(scorePlayer)
|
const filename = getScoreFileName(scorePlayer)
|
||||||
|
|
||||||
textNode.textContent = "Processing…"
|
textNode.textContent = PROCESSING_TEXT
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await generatePDF(sheetImgURLs, imgType, filename)
|
await generatePDF(sheetImgURLs, imgType, filename)
|
||||||
textNode.textContent = "Download PDF"
|
textNode.textContent = "Download PDF"
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
textNode.textContent = "❌Download Failed!"
|
textNode.textContent = FAILED_TEXT
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (name == "MSCZ") {
|
} else if (name == "MSCZ") {
|
||||||
btn.onclick = async () => {
|
btn.onclick = async () => {
|
||||||
textNode.textContent = "Processing…"
|
textNode.textContent = PROCESSING_TEXT
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const token = await recaptcha.execute()
|
const data = new Blob([await fetchMscz(msczURL)])
|
||||||
const filename = getScoreFileName(scorePlayer)
|
|
||||||
|
|
||||||
const r = await fetch(url + token)
|
|
||||||
const data = await r.blob()
|
|
||||||
|
|
||||||
textNode.textContent = "Download MSCZ"
|
textNode.textContent = "Download MSCZ"
|
||||||
|
|
||||||
saveAs(data, `${filename}.mscz`)
|
saveAs(data, `${filename}.mscz`)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
textNode.textContent = "❌Download Failed!"
|
textNode.textContent = FAILED_TEXT
|
||||||
console.error(err)
|
console.error(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} else if (name == "Parts") { // download individual parts
|
||||||
|
btn.title = "Download individual parts (BETA)"
|
||||||
|
const cb = btn.onclick = async () => {
|
||||||
|
btn.onclick = null
|
||||||
|
textNode.textContent = PROCESSING_TEXT
|
||||||
|
|
||||||
|
const w = window.open("")
|
||||||
|
const txt = document.createTextNode(PROCESSING_TEXT)
|
||||||
|
w.document.body.append(txt)
|
||||||
|
|
||||||
|
// set page hooks
|
||||||
|
const destroy = () => {
|
||||||
|
score.destroy()
|
||||||
|
w.close()
|
||||||
|
}
|
||||||
|
window.addEventListener("unload", destroy)
|
||||||
|
w.addEventListener("beforeunload", () => {
|
||||||
|
score.destroy()
|
||||||
|
window.removeEventListener("unload", destroy)
|
||||||
|
textNode.textContent = "Download Parts"
|
||||||
|
btn.onclick = cb
|
||||||
|
})
|
||||||
|
|
||||||
|
// load webmscore (https://github.com/LibreScore/webmscore)
|
||||||
|
const script = w.document.createElement("script")
|
||||||
|
script.src = WEBMSCORE_URL
|
||||||
|
w.document.body.append(script)
|
||||||
|
await new Promise(resolve => { script.onload = resolve })
|
||||||
|
|
||||||
|
// parse mscz data
|
||||||
|
const data = new Uint8Array(
|
||||||
|
new Uint8Array(await fetchMscz(msczURL)) // copy its ArrayBuffer
|
||||||
|
)
|
||||||
|
const score = await w["WebMscore"].load("mscz", data)
|
||||||
|
await score.generateExcerpts()
|
||||||
|
const metadata = await score.metadata()
|
||||||
|
console.log("score metadata loaded by webmscore", metadata)
|
||||||
|
|
||||||
|
// render the part selection page
|
||||||
|
txt.remove()
|
||||||
|
const fieldset = w.document.createElement("fieldset")
|
||||||
|
for (const excerpt of metadata.excerpts) {
|
||||||
|
const e = w.document.createElement("input")
|
||||||
|
e.name = "score-part"
|
||||||
|
e.type = "radio"
|
||||||
|
e.value = excerpt.id
|
||||||
|
const label = w.document.createElement("label")
|
||||||
|
label.innerText = excerpt.title
|
||||||
|
const br = w.document.createElement("br")
|
||||||
|
fieldset.append(e, label, br)
|
||||||
|
}
|
||||||
|
const submitBtn = w.document.createElement("input")
|
||||||
|
submitBtn.type = "submit"
|
||||||
|
submitBtn.value = "Download PDF"
|
||||||
|
fieldset.append(submitBtn)
|
||||||
|
w.document.body.append(fieldset)
|
||||||
|
|
||||||
|
submitBtn.onclick = async () => {
|
||||||
|
const checked: HTMLInputElement = w.document.querySelector("input:checked")
|
||||||
|
const id = checked.value
|
||||||
|
|
||||||
|
await score.setExcerptId(id)
|
||||||
|
|
||||||
|
const data = new Blob([await score.savePdf()])
|
||||||
|
saveAs(data, `${filename}-part-${id}.pdf`)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
btn.onclick = () => {
|
btn.onclick = () => {
|
||||||
window.open(url)
|
window.open(url)
|
||||||
|
|
Loading…
Reference in a new issue