feat: obtain img/mp3/midi urls using an API
This commit is contained in:
parent
0da95e6fce
commit
cca83f459f
6 changed files with 69 additions and 16 deletions
|
@ -14,6 +14,7 @@
|
|||
},
|
||||
"homepage": "https://github.com/Xmader/musescore-downloader#readme",
|
||||
"dependencies": {
|
||||
"md5": "^2.3.0",
|
||||
"pdfkit": "git+https://github.com/Xmader/pdfkit.git",
|
||||
"svg-to-pdfkit": "^0.1.8",
|
||||
"webmscore": "^0.10.4"
|
||||
|
@ -21,6 +22,7 @@
|
|||
"devDependencies": {
|
||||
"@rollup/plugin-json": "^4.0.0",
|
||||
"@types/file-saver": "^2.0.1",
|
||||
"@types/md5": "^2.2.0",
|
||||
"@types/pdfkit": "^0.10.4",
|
||||
"rollup": "^1.26.3",
|
||||
"rollup-plugin-commonjs": "^10.1.0",
|
||||
|
|
24
src/btn.ts
24
src/btn.ts
|
@ -1,5 +1,6 @@
|
|||
|
||||
import { loadMscore, WebMscore } from './mscore'
|
||||
import { saveAs } from './utils'
|
||||
|
||||
type BtnElement = HTMLButtonElement
|
||||
|
||||
|
@ -86,8 +87,27 @@ export namespace BtnAction {
|
|||
export const PROCESSING_TEXT = 'Processing…'
|
||||
export const ERROR_TEXT = '❌Download Failed!'
|
||||
|
||||
export const openUrl = (url: string): BtnAction => {
|
||||
return (): any => window.open(url)
|
||||
type Promisable<T> = T | Promise<T>
|
||||
type UrlInput = Promisable<string> | (() => Promisable<string>)
|
||||
|
||||
const normalizeUrlInput = (url: UrlInput) => {
|
||||
if (typeof url === 'function') return url()
|
||||
else return url
|
||||
}
|
||||
|
||||
export const openUrl = (url: UrlInput): BtnAction => {
|
||||
return process(async (): Promise<any> => {
|
||||
window.open(await normalizeUrlInput(url))
|
||||
})
|
||||
}
|
||||
|
||||
export const download = (url: UrlInput, filename?: string): BtnAction => {
|
||||
return process(async (): Promise<any> => {
|
||||
const _url = await normalizeUrlInput(url)
|
||||
const r = await fetch(_url)
|
||||
const blob = await r.blob()
|
||||
saveAs(blob, filename)
|
||||
})
|
||||
}
|
||||
|
||||
export const mscoreWindow = (fn: (w: Window, score: WebMscore, processingTextEl: ChildNode) => any): BtnAction => {
|
||||
|
|
32
src/file.ts
Normal file
32
src/file.ts
Normal file
|
@ -0,0 +1,32 @@
|
|||
|
||||
import scoreinfo from './scoreinfo'
|
||||
import md5 from 'md5'
|
||||
|
||||
const MAGIC = String(4 * 11 * 1607 * 2767) // '195649036' // I don't know what this is
|
||||
const AUTH_LEN = 4
|
||||
|
||||
type FileType = 'img' | 'mp3' | 'midi'
|
||||
|
||||
const getApiUrl = (type: FileType, index: number): string => {
|
||||
// proxy
|
||||
return `https://musescore.now.sh/api/jmuse?id=${scoreinfo.id}&type=${type}&index=${index}`
|
||||
}
|
||||
|
||||
const getApiAuth = (type: FileType, index: number): string => {
|
||||
const str = String(scoreinfo.id) + type + String(index) + MAGIC
|
||||
return md5(str).slice(0, AUTH_LEN)
|
||||
}
|
||||
|
||||
export const getFileUrl = async (type: FileType, index = 0): Promise<string> => {
|
||||
const url = getApiUrl(type, index)
|
||||
const auth = getApiAuth(type, index)
|
||||
|
||||
const r = await fetch(url, {
|
||||
headers: {
|
||||
Authorization: auth,
|
||||
},
|
||||
})
|
||||
|
||||
const { info } = await r.json()
|
||||
return info.url as string
|
||||
}
|
|
@ -3,6 +3,7 @@ import './meta'
|
|||
import { waitForDocumentLoaded, saveAs } from './utils'
|
||||
import { downloadPDF } from './pdf'
|
||||
import { downloadMscz } from './mscz'
|
||||
import { getFileUrl } from './file'
|
||||
import { getDownloadBtn, BtnList, BtnAction } from './btn'
|
||||
import * as recaptcha from './recaptcha'
|
||||
import scoreinfo from './scoreinfo'
|
||||
|
@ -16,6 +17,7 @@ const main = (): void => {
|
|||
recaptcha.init()
|
||||
|
||||
const btnList = new BtnList(getDownloadBtn())
|
||||
const filename = scoreinfo.fileName
|
||||
|
||||
btnList.add({
|
||||
name: 'Download MSCZ',
|
||||
|
@ -32,19 +34,19 @@ const main = (): void => {
|
|||
action: BtnAction.mscoreWindow(async (w, score) => {
|
||||
const mxl = await score.saveMxl()
|
||||
const data = new Blob([mxl])
|
||||
saveAs(data, `${scoreinfo.fileName}.mxl`)
|
||||
saveAs(data, `${filename}.mxl`)
|
||||
w.close()
|
||||
}),
|
||||
})
|
||||
|
||||
btnList.add({
|
||||
name: 'Download MIDI',
|
||||
action: BtnAction.openUrl(scoreinfo.midiUrl),
|
||||
action: BtnAction.download(() => getFileUrl('midi'), `${filename}.mid`),
|
||||
})
|
||||
|
||||
btnList.add({
|
||||
name: 'Download MP3',
|
||||
action: BtnAction.openUrl(scoreinfo.mp3Url),
|
||||
action: BtnAction.download(() => getFileUrl('mp3'), `${filename}.mp3`),
|
||||
})
|
||||
|
||||
btnList.add({
|
||||
|
@ -95,7 +97,6 @@ const main = (): void => {
|
|||
|
||||
await score.setExcerptId(+id)
|
||||
|
||||
const filename = scoreinfo.fileName
|
||||
const data = new Blob([await score.savePdf()])
|
||||
saveAs(data, `${filename} - ${partName}.pdf`)
|
||||
}
|
||||
|
|
10
src/pdf.ts
10
src/pdf.ts
|
@ -1,5 +1,6 @@
|
|||
|
||||
import { PDFWorkerHelper } from './worker-helper'
|
||||
import { getFileUrl } from './file'
|
||||
import { saveAs } from './utils'
|
||||
import scoreinfo from './scoreinfo'
|
||||
|
||||
|
@ -26,9 +27,14 @@ export const downloadPDF = async (): Promise<void> => {
|
|||
const imgType = scoreinfo.sheetImgType
|
||||
const pageCount = scoreinfo.pageCount
|
||||
|
||||
const sheetImgURLs = Array.from({ length: pageCount }).map((_, i) => {
|
||||
return scoreinfo.baseUrl + `score_${i}.${imgType}`
|
||||
const rs = Array.from({ length: pageCount }).map((_, i) => {
|
||||
if (i === 0) { // The url to the first page is static. We don't need to use API to obtain it.
|
||||
return scoreinfo.baseUrl + `score_${i}.${imgType}`
|
||||
} else { // obtain image urls using the API
|
||||
return getFileUrl('img', i)
|
||||
}
|
||||
})
|
||||
const sheetImgURLs = await Promise.all(rs)
|
||||
|
||||
return _downloadPDF(sheetImgURLs, imgType, scoreinfo.fileName)
|
||||
}
|
||||
|
|
|
@ -38,14 +38,6 @@ const scoreinfo = {
|
|||
return origin + pathname.split('/').slice(0, -1).join('/') + '/'
|
||||
},
|
||||
|
||||
get midiUrl (this: typeof scoreinfo): string {
|
||||
return this.baseUrl + 'score.mid'
|
||||
},
|
||||
|
||||
get mp3Url (this: typeof scoreinfo): string {
|
||||
return this.baseUrl + 'score.mp3'
|
||||
},
|
||||
|
||||
get msczUrl (this: typeof scoreinfo): string {
|
||||
// https://github.com/Xmader/cloudflare-worker-musescore-mscz
|
||||
return `https://musescore.now.sh/api/mscz?id=${this.id}&token=`
|
||||
|
|
Loading…
Reference in a new issue