feat: support s.musescore.com urls
This commit is contained in:
parent
7485d884bf
commit
7fc1a5fb6a
|
@ -25,7 +25,7 @@
|
||||||
"content_scripts": [
|
"content_scripts": [
|
||||||
{
|
{
|
||||||
"matches": [
|
"matches": [
|
||||||
"*://musescore.com/*/*"
|
"*://*.musescore.com/*/*"
|
||||||
],
|
],
|
||||||
"js": [
|
"js": [
|
||||||
"src/web-ext.js"
|
"src/web-ext.js"
|
||||||
|
|
|
@ -6,7 +6,7 @@ import fs from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { fetchMscz, setMscz, MSCZ_URL_SYM } from './mscz'
|
import { fetchMscz, setMscz, MSCZ_URL_SYM } from './mscz'
|
||||||
import { loadMscore, INDV_DOWNLOADS, WebMscore } from './mscore'
|
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 { escapeFilename } from './utils'
|
||||||
import i18n from './i18n'
|
import i18n from './i18n'
|
||||||
|
|
||||||
|
@ -14,7 +14,8 @@ const inquirer: typeof import('inquirer') = require('inquirer')
|
||||||
const ora: typeof import('ora') = require('ora')
|
const ora: typeof import('ora') = require('ora')
|
||||||
const chalk: typeof import('chalk') = require('chalk')
|
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'
|
const EXT = '.mscz'
|
||||||
|
|
||||||
interface Params {
|
interface Params {
|
||||||
|
@ -36,7 +37,7 @@ void (async () => {
|
||||||
validate (input: string) {
|
validate (input: string) {
|
||||||
return input &&
|
return input &&
|
||||||
(
|
(
|
||||||
input.startsWith(SCORE_URL_PREFIX) ||
|
!!input.match(SCORE_URL_REG) ||
|
||||||
(input.endsWith(EXT) && fs.statSync(input).isFile())
|
(input.endsWith(EXT) && fs.statSync(input).isFile())
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
|
@ -47,6 +48,7 @@ void (async () => {
|
||||||
if (!isLocalFile) {
|
if (!isLocalFile) {
|
||||||
// request scoreinfo
|
// request scoreinfo
|
||||||
scoreinfo = await ScoreInfoHtml.request(fileInit)
|
scoreinfo = await ScoreInfoHtml.request(fileInit)
|
||||||
|
await getActualId(scoreinfo as any)
|
||||||
|
|
||||||
// confirmation
|
// confirmation
|
||||||
const { confirmed } = await inquirer.prompt<Params>({
|
const { confirmed } = await inquirer.prompt<Params>({
|
||||||
|
|
11
src/main.ts
11
src/main.ts
|
@ -7,7 +7,7 @@ import { downloadMscz } from './mscz'
|
||||||
import { getFileUrl } from './file'
|
import { getFileUrl } from './file'
|
||||||
import { INDV_DOWNLOADS } from './mscore'
|
import { INDV_DOWNLOADS } from './mscore'
|
||||||
import { BtnList, BtnAction, BtnListMode } from './btn'
|
import { BtnList, BtnAction, BtnListMode } from './btn'
|
||||||
import { ScoreInfoInPage, SheetInfoInPage } from './scoreinfo'
|
import { ScoreInfoInPage, SheetInfoInPage, getActualId } from './scoreinfo'
|
||||||
import i18n from './i18n'
|
import i18n from './i18n'
|
||||||
|
|
||||||
const { saveAs } = FileSaver
|
const { saveAs } = FileSaver
|
||||||
|
@ -15,7 +15,10 @@ const { saveAs } = FileSaver
|
||||||
const main = (): void => {
|
const main = (): void => {
|
||||||
const btnList = new BtnList()
|
const btnList = new BtnList()
|
||||||
const scoreinfo = new ScoreInfoInPage(document)
|
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
|
let indvPartBtn: HTMLButtonElement | null = null
|
||||||
const fallback = () => {
|
const fallback = () => {
|
||||||
|
@ -45,12 +48,12 @@ const main = (): void => {
|
||||||
|
|
||||||
btnList.add({
|
btnList.add({
|
||||||
name: i18n('DOWNLOAD')('MIDI'),
|
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({
|
btnList.add({
|
||||||
name: i18n('DOWNLOAD')('MP3'),
|
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({
|
indvPartBtn = btnList.add({
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
// @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/*/*
|
||||||
|
// @match https://s.musescore.com/*/*
|
||||||
// @license MIT
|
// @license MIT
|
||||||
// @copyright Copyright (c) 2019-2020 Xmader
|
// @copyright Copyright (c) 2019-2020 Xmader
|
||||||
// @grant unsafeWindow
|
// @grant unsafeWindow
|
||||||
|
|
|
@ -43,11 +43,18 @@ export class ScoreInfoInPage extends ScoreInfo {
|
||||||
const el = this.document.querySelector("meta[property='og:title']") as HTMLMetaElement
|
const el = this.document.querySelector("meta[property='og:title']") as HTMLMetaElement
|
||||||
return el.content
|
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 {
|
export class ScoreInfoHtml extends ScoreInfo {
|
||||||
private readonly ID_REG = /<meta property="al:ios:url" content="musescore:\/\/score\/(\d+)">/
|
private readonly ID_REG = /<meta property="al:ios:url" content="musescore:\/\/score\/(\d+)">/
|
||||||
private readonly TITLE_REG = /<meta property="og:title" content="(.*)">/
|
private readonly TITLE_REG = /<meta property="og:title" content="(.*)">/
|
||||||
|
private readonly BASEURL_REG = /<meta property="og:image" content="(.+\/)score_.*">/
|
||||||
|
|
||||||
constructor (private html: string) { super() }
|
constructor (private html: string) { super() }
|
||||||
|
|
||||||
|
@ -63,6 +70,12 @@ export class ScoreInfoHtml extends ScoreInfo {
|
||||||
return m[1]
|
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<ScoreInfoHtml> {
|
static async request (url: string, _fetch = getFetch()): Promise<ScoreInfoHtml> {
|
||||||
const r = await _fetch(url)
|
const r = await _fetch(url)
|
||||||
if (!r.ok) return new ScoreInfoHtml('')
|
if (!r.ok) return new ScoreInfoHtml('')
|
||||||
|
@ -97,3 +110,25 @@ export class SheetInfoInPage extends SheetInfo {
|
||||||
return url.split('@')[0]
|
return url.split('@')[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getActualId = async (scoreinfo: ScoreInfoInPage | ScoreInfoHtml, _fetch = getFetch()): Promise<number> => {
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue