From 7fc1a5fb6a1dc7e45f01106de6cd39271dc60850 Mon Sep 17 00:00:00 2001 From: Xmader Date: Sun, 29 Nov 2020 17:47:39 -0500 Subject: [PATCH] feat: support s.musescore.com urls --- package.json | 2 +- src/cli.ts | 8 +++++--- src/main.ts | 11 +++++++---- src/meta.js | 1 + src/scoreinfo.ts | 35 +++++++++++++++++++++++++++++++++++ 5 files changed, 49 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index aef7b49..423ec90 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "content_scripts": [ { "matches": [ - "*://musescore.com/*/*" + "*://*.musescore.com/*/*" ], "js": [ "src/web-ext.js" diff --git a/src/cli.ts b/src/cli.ts index b5dc098..b0af2d5 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -6,7 +6,7 @@ import fs from 'fs' import path from 'path' import { fetchMscz, setMscz, MSCZ_URL_SYM } from './mscz' import { loadMscore, INDV_DOWNLOADS, WebMscore } from './mscore' -import { ScoreInfo, ScoreInfoHtml, ScoreInfoObj } from './scoreinfo' +import { ScoreInfo, ScoreInfoHtml, ScoreInfoObj, getActualId } from './scoreinfo' import { escapeFilename } from './utils' import i18n from './i18n' @@ -14,7 +14,8 @@ const inquirer: typeof import('inquirer') = require('inquirer') const ora: typeof import('ora') = require('ora') const chalk: typeof import('chalk') = require('chalk') -const SCORE_URL_PREFIX = 'https://musescore.com/' +const SCORE_URL_PREFIX = 'https://(s.)?musescore.com/' +const SCORE_URL_REG = /https:\/\/(s\.)?musescore\.com\// const EXT = '.mscz' interface Params { @@ -36,7 +37,7 @@ void (async () => { validate (input: string) { return input && ( - input.startsWith(SCORE_URL_PREFIX) || + !!input.match(SCORE_URL_REG) || (input.endsWith(EXT) && fs.statSync(input).isFile()) ) }, @@ -47,6 +48,7 @@ void (async () => { if (!isLocalFile) { // request scoreinfo scoreinfo = await ScoreInfoHtml.request(fileInit) + await getActualId(scoreinfo as any) // confirmation const { confirmed } = await inquirer.prompt({ diff --git a/src/main.ts b/src/main.ts index 13378e6..dc3459d 100644 --- a/src/main.ts +++ b/src/main.ts @@ -7,7 +7,7 @@ import { downloadMscz } from './mscz' import { getFileUrl } from './file' import { INDV_DOWNLOADS } from './mscore' import { BtnList, BtnAction, BtnListMode } from './btn' -import { ScoreInfoInPage, SheetInfoInPage } from './scoreinfo' +import { ScoreInfoInPage, SheetInfoInPage, getActualId } from './scoreinfo' import i18n from './i18n' const { saveAs } = FileSaver @@ -15,7 +15,10 @@ const { saveAs } = FileSaver const main = (): void => { const btnList = new BtnList() const scoreinfo = new ScoreInfoInPage(document) - const { fileName, id } = scoreinfo + const { fileName } = scoreinfo + + // eslint-disable-next-line no-void + void getActualId(scoreinfo) let indvPartBtn: HTMLButtonElement | null = null const fallback = () => { @@ -45,12 +48,12 @@ const main = (): void => { btnList.add({ name: i18n('DOWNLOAD')('MIDI'), - action: BtnAction.download(() => getFileUrl(id, 'midi'), fallback, 30 * 1000 /* 30s */), + action: BtnAction.download(() => getFileUrl(scoreinfo.id, 'midi'), fallback, 30 * 1000 /* 30s */), }) btnList.add({ name: i18n('DOWNLOAD')('MP3'), - action: BtnAction.download(() => getFileUrl(id, 'mp3'), fallback, 30 * 1000 /* 30s */), + action: BtnAction.download(() => getFileUrl(scoreinfo.id, 'mp3'), fallback, 30 * 1000 /* 30s */), }) indvPartBtn = btnList.add({ diff --git a/src/meta.js b/src/meta.js index b16d818..7bd0120 100644 --- a/src/meta.js +++ b/src/meta.js @@ -9,6 +9,7 @@ // @description download sheet music from musescore.com for free, no login or Musescore Pro required | 免登录、免 Musescore Pro,免费下载 musescore.com 上的曲谱 // @author Xmader // @match https://musescore.com/*/* +// @match https://s.musescore.com/*/* // @license MIT // @copyright Copyright (c) 2019-2020 Xmader // @grant unsafeWindow diff --git a/src/scoreinfo.ts b/src/scoreinfo.ts index 3133b87..908f0d4 100644 --- a/src/scoreinfo.ts +++ b/src/scoreinfo.ts @@ -43,11 +43,18 @@ export class ScoreInfoInPage extends ScoreInfo { const el = this.document.querySelector("meta[property='og:title']") as HTMLMetaElement return el.content } + + get baseUrl (): string { + const el = this.document.querySelector("meta[property='og:image']") as HTMLMetaElement + const m = el.content.match(/^(.+\/)score_/) as RegExpMatchArray + return m[1] + } } export class ScoreInfoHtml extends ScoreInfo { private readonly ID_REG = // private readonly TITLE_REG = // + private readonly BASEURL_REG = // constructor (private html: string) { super() } @@ -63,6 +70,12 @@ export class ScoreInfoHtml extends ScoreInfo { return m[1] } + get baseUrl (): string { + const m = this.html.match(this.BASEURL_REG) + if (!m) return '' + return m[1] + } + static async request (url: string, _fetch = getFetch()): Promise { const r = await _fetch(url) if (!r.ok) return new ScoreInfoHtml('') @@ -97,3 +110,25 @@ export class SheetInfoInPage extends SheetInfo { return url.split('@')[0] } } + +export const getActualId = async (scoreinfo: ScoreInfoInPage | ScoreInfoHtml, _fetch = getFetch()): Promise => { + if (scoreinfo.id <= 1000000000000) { + // actual id already + return scoreinfo.id + } + + const jsonPUrl = new URL(`${scoreinfo.baseUrl}space.jsonp`) + jsonPUrl.hostname = 's.musescore.com' + + const r = await _fetch(jsonPUrl.href) + const text = await r.text() + + const m = text.match(/^jsonp(\d+)/) as RegExpMatchArray + const id = +m[1] + + Object.defineProperty(scoreinfo, 'id', { + get () { return id }, + }) + + return id +}