musescore-downloader

[Fork] again, nothing to see here.
git clone https://git.jojolepro.com/musescore-downloader.git
Log | Files | Refs | README | LICENSE

main.ts (5311B)


      1 import "./meta"
      2 
      3 import { ScorePlayerData } from "./types"
      4 import { waitForDocumentLoaded } from "./utils"
      5 import * as recaptcha from "./recaptcha"
      6 
      7 import { PDFWorkerHelper } from "./worker-helper"
      8 import FileSaver from "file-saver/dist/FileSaver.js"
      9 
     10 const saveAs: typeof import("file-saver").saveAs = FileSaver.saveAs
     11 
     12 let pdfBlob: Blob
     13 
     14 const generatePDF = async (imgURLs: string[], imgType: "svg" | "png", name?: string) => {
     15     if (pdfBlob) {
     16         return saveAs(pdfBlob, `${name}.pdf`)
     17     }
     18 
     19     const cachedImg = document.querySelector("img[src*=score_]") as HTMLImageElement
     20     const { naturalWidth: width, naturalHeight: height } = cachedImg
     21 
     22     const worker = new PDFWorkerHelper()
     23     const pdfArrayBuffer = await worker.generatePDF(imgURLs, imgType, width, height)
     24     worker.terminate()
     25 
     26     pdfBlob = new Blob([pdfArrayBuffer])
     27 
     28     saveAs(pdfBlob, `${name}.pdf`)
     29 }
     30 
     31 const getPagesNumber = (scorePlayerData: ScorePlayerData) => {
     32     try {
     33         return scorePlayerData.json.metadata.pages
     34     } catch (_) {
     35         return document.querySelectorAll("img[src*=score_]").length
     36     }
     37 }
     38 
     39 const getImgType = (): "svg" | "png" => {
     40     try {
     41         const imgE: HTMLImageElement = document.querySelector("img[src*=score_]")
     42         const { pathname } = new URL(imgE.src)
     43         const imgtype = pathname.match(/\.(\w+)$/)[1]
     44         return imgtype as "svg" | "png"
     45     } catch (_) {
     46         return null
     47     }
     48 }
     49 
     50 const getTitle = (scorePlayerData: ScorePlayerData) => {
     51     try {
     52         return scorePlayerData.json.metadata.title
     53     } catch (_) {
     54         return ""
     55     }
     56 }
     57 
     58 const getScoreFileName = (scorePlayerData: ScorePlayerData) => {
     59     return getTitle(scorePlayerData).replace(/[\s<>:{}"/\\|?*~.\0\cA-\cZ]+/g, "_")
     60 }
     61 
     62 const main = () => {
     63 
     64     // @ts-ignore
     65     if (!window.UGAPP || !window.UGAPP.store || !window.UGAPP.store.jmuse_settings) { return }
     66 
     67     // init recaptcha
     68     recaptcha.init()
     69 
     70     // @ts-ignore
     71     const scorePlayer: ScorePlayerData = window.UGAPP.store.jmuse_settings.score_player
     72 
     73     const { id } = scorePlayer.json
     74     const baseURL = scorePlayer.urls.image_path
     75 
     76     // const msczURL = `https://musescore.com/static/musescore/scoredata/score/${getIndexPath(id)}/${id}/score_${vid}_${scoreHexId}.mscz`
     77 
     78     // https://github.com/Xmader/cloudflare-worker-musescore-mscz
     79     const msczURL = `https://musescore.now.sh/api/mscz?id=${id}&token=`
     80 
     81     const mxlURL = baseURL + "score.mxl"
     82     const { midi: midiURL, mp3: mp3URL } = scorePlayer.urls
     83 
     84     const btnsDiv = document.querySelector(".score-right .buttons-wrapper") || document.querySelectorAll("aside section > div")[4]
     85     const downloadBtn = btnsDiv.querySelector("button, .button") as HTMLElement
     86     downloadBtn.onclick = null
     87 
     88     // fix the icon of the download btn
     89     // if the `downloadBtn` seleted was a `Print` btn, replace the `print` icon with the `download` icon
     90     const svgPath: SVGPathElement = downloadBtn.querySelector("svg > path")
     91     if(svgPath) {
     92         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")
     93     }
     94 
     95     const imgType = getImgType() || "svg"
     96 
     97     const sheetImgURLs = Array.from({ length: getPagesNumber(scorePlayer) }).fill(null).map((_, i) => {
     98         return baseURL + `score_${i}.${imgType}`
     99     })
    100 
    101     const downloadURLs = {
    102         "MSCZ": msczURL,
    103         "PDF": null,
    104         "MusicXML": mxlURL,
    105         "MIDI": midiURL,
    106         "MP3": mp3URL,
    107     }
    108 
    109     const createBtn = (name: string) => {
    110         const btn = downloadBtn.cloneNode(true) as HTMLElement
    111 
    112         if (btn.nodeName.toLowerCase() == "button") {
    113             btn.setAttribute("style", "width: 205px !important")
    114         } else {
    115             btn.dataset.target = ""
    116         }
    117 
    118         const textNode = [...btn.childNodes].find((x) => {
    119             return x.textContent.includes("Download") || x.textContent.includes("Print")
    120         })
    121         textNode.textContent = `Download ${name}`
    122 
    123         return {
    124             btn,
    125             textNode,
    126         }
    127     }
    128 
    129     const newDownloadBtns = Object.keys(downloadURLs).map((name) => {
    130         const url = downloadURLs[name]
    131         const { btn, textNode } = createBtn(name)
    132 
    133         if (name == "PDF") {
    134             btn.onclick = () => {
    135                 const text = textNode.textContent
    136                 const filename = getScoreFileName(scorePlayer)
    137 
    138                 textNode.textContent = "Processing…"
    139 
    140                 generatePDF(sheetImgURLs, imgType, filename).then(() => {
    141                     textNode.textContent = text
    142                 })
    143             }
    144         } else if (name == "MSCZ") {
    145             btn.onclick = async () => {
    146                 const text = textNode.textContent
    147                 textNode.textContent = "Processing…"
    148 
    149                 const token = await recaptcha.execute()
    150                 const filename = getScoreFileName(scorePlayer)
    151 
    152                 const r = await fetch(url + token)
    153                 const data = await r.blob()
    154 
    155                 textNode.textContent = text
    156 
    157                 saveAs(data, `${filename}.mscz`)
    158             }
    159         } else {
    160             btn.onclick = () => {
    161                 window.open(url)
    162             }
    163         }
    164 
    165         return btn
    166     })
    167 
    168     downloadBtn.replaceWith(...newDownloadBtns)
    169 
    170 }
    171 
    172 waitForDocumentLoaded().then(main)