feat: support s.musescore.com urls
This commit is contained in:
		
							parent
							
								
									7485d884bf
								
							
						
					
					
						commit
						7fc1a5fb6a
					
				
					 5 changed files with 49 additions and 8 deletions
				
			
		|  | @ -25,7 +25,7 @@ | |||
|   "content_scripts": [ | ||||
|     { | ||||
|       "matches": [ | ||||
|         "*://musescore.com/*/*" | ||||
|         "*://*.musescore.com/*/*" | ||||
|       ], | ||||
|       "js": [ | ||||
|         "src/web-ext.js" | ||||
|  |  | |||
|  | @ -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<Params>({ | ||||
|  |  | |||
							
								
								
									
										11
									
								
								src/main.ts
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								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({ | ||||
|  |  | |||
|  | @ -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
 | ||||
|  |  | |||
|  | @ -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 = /<meta property="al:ios:url" content="musescore:\/\/score\/(\d+)">/ | ||||
|   private readonly TITLE_REG = /<meta property="og:title" content="(.*)">/ | ||||
|   private readonly BASEURL_REG = /<meta property="og:image" content="(.+\/)score_.*">/ | ||||
| 
 | ||||
|   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<ScoreInfoHtml> { | ||||
|     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<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…
	
	Add table
		Add a link
		
	
		Reference in a new issue