2020-11-24 22:43:07 +00:00
/* eslint-disable @typescript-eslint/no-var-requires */
2020-11-24 22:32:44 +00:00
/* eslint-disable no-void */
import fs from 'fs'
2020-11-26 20:07:34 +00:00
import path from 'path'
2020-12-17 08:28:37 +00:00
import os from 'os'
2020-11-26 21:40:25 +00:00
import { fetchMscz , setMscz , MSCZ_URL_SYM } from './mscz'
2020-11-24 22:32:44 +00:00
import { loadMscore , INDV_DOWNLOADS , WebMscore } from './mscore'
2020-11-29 22:47:39 +00:00
import { ScoreInfo , ScoreInfoHtml , ScoreInfoObj , getActualId } from './scoreinfo'
2021-01-02 07:34:19 +00:00
import { getLibreScoreLink } from './librescore-link'
2020-11-24 22:35:44 +00:00
import { escapeFilename } from './utils'
2021-01-15 08:02:48 +00:00
import { isNpx , isLatest , installedVersion , latestVersion } from './npm-data'
2020-11-24 22:32:44 +00:00
import i18n from './i18n'
2020-11-24 22:43:07 +00:00
const inquirer : typeof import ( 'inquirer' ) = require ( 'inquirer' )
const ora : typeof import ( 'ora' ) = require ( 'ora' )
const chalk : typeof import ( 'chalk' ) = require ( 'chalk' )
2020-11-29 23:08:03 +00:00
const SCORE_URL_PREFIX = 'https://(s.)musescore.com/'
2020-11-29 22:47:39 +00:00
const SCORE_URL_REG = /https:\/\/(s\.)?musescore\.com\//
2020-11-26 21:40:25 +00:00
const EXT = '.mscz'
2020-11-24 22:32:44 +00:00
2020-11-26 19:58:03 +00:00
interface Params {
2020-11-26 21:53:01 +00:00
fileInit : string ;
2020-11-26 19:58:03 +00:00
confirmed : boolean ;
part : number ;
types : number [ ] ;
dest : string ;
}
2020-11-24 22:32:44 +00:00
void ( async ( ) = > {
2020-12-16 18:13:16 +00:00
// Determine platform and paste message
2020-12-17 08:28:37 +00:00
const platform = os . platform ( )
let pasteMessage = ''
2020-12-16 18:13:16 +00:00
if ( platform === 'win32' ) {
2020-12-17 08:28:37 +00:00
pasteMessage = 'right-click to paste'
2020-12-16 18:13:16 +00:00
} else if ( platform === 'linux' ) {
2020-12-17 08:28:37 +00:00
pasteMessage = 'usually Ctrl+Shift+V to paste'
} // For MacOS, no hint is needed because the paste shortcut is universal.
2020-12-16 18:13:16 +00:00
2020-11-26 21:40:25 +00:00
let scoreinfo : ScoreInfo
2021-01-02 07:34:19 +00:00
let librescoreLink : Promise < string > | undefined
2020-11-26 21:53:01 +00:00
// ask for the page url or path to local file
const { fileInit } = await inquirer . prompt < Params > ( {
type : 'input' ,
name : 'fileInit' ,
message : 'Score URL or path to local MSCZ file:' ,
2020-12-04 02:04:36 +00:00
suffix : '\n ' +
` (starts with " ${ SCORE_URL_PREFIX } " or local filepath ends with " ${ EXT } ") ` +
2020-12-16 18:13:16 +00:00
` ${ chalk . bgGray ( pasteMessage ) } \ n ` ,
2020-11-26 21:53:01 +00:00
validate ( input : string ) {
return input &&
(
2020-11-29 22:47:39 +00:00
! ! input . match ( SCORE_URL_REG ) ||
2020-11-26 21:53:01 +00:00
( input . endsWith ( EXT ) && fs . statSync ( input ) . isFile ( ) )
)
} ,
default : process . argv [ 2 ] ,
} )
2020-11-26 21:40:25 +00:00
2020-11-26 21:53:01 +00:00
const isLocalFile = fileInit . endsWith ( EXT )
if ( ! isLocalFile ) {
2020-11-26 21:40:25 +00:00
// request scoreinfo
2020-11-26 21:53:01 +00:00
scoreinfo = await ScoreInfoHtml . request ( fileInit )
2020-11-30 19:21:55 +00:00
try {
await getActualId ( scoreinfo as any )
} catch ( err ) {
console . error ( err )
}
2020-11-26 21:40:25 +00:00
// confirmation
const { confirmed } = await inquirer . prompt < Params > ( {
type : 'confirm' ,
name : 'confirmed' ,
message : 'Continue?' ,
prefix : ` ${ chalk . yellow ( '!' ) } ` +
` ID: ${ scoreinfo . id } \ n ` +
` Title: ${ scoreinfo . title } \ n ` ,
default : true ,
} )
if ( ! confirmed ) return
2021-01-02 07:34:19 +00:00
// initiate LibreScore link request
librescoreLink = getLibreScoreLink ( scoreinfo )
// print a blank line to the terminal
console . log ( )
2020-11-26 21:40:25 +00:00
} else {
scoreinfo = new ScoreInfoObj ( 0 , path . basename ( fileInit , EXT ) )
}
2020-11-24 22:32:44 +00:00
const spinner = ora ( {
text : i18n ( 'PROCESSING' ) ( ) ,
color : 'blue' ,
spinner : 'bounce' ,
indent : 0 ,
} ) . start ( )
let score : WebMscore
let metadata : import ( 'webmscore/schemas' ) . ScoreMetadata
try {
2020-11-26 21:40:25 +00:00
if ( ! isLocalFile ) {
// fetch mscz file from the dataset, and cache it for side effect
await fetchMscz ( scoreinfo )
} else {
// load local file
const data = await fs . promises . readFile ( fileInit )
await setMscz ( scoreinfo , data . buffer )
}
2020-11-24 22:32:44 +00:00
spinner . info ( 'MSCZ file loaded' )
2020-11-26 21:40:25 +00:00
if ( ! isLocalFile ) {
spinner . info ( ` File URL: ${ scoreinfo . store . get ( MSCZ_URL_SYM ) as string } ` )
}
2021-01-02 07:34:19 +00:00
if ( librescoreLink ) {
try {
2021-01-02 07:44:43 +00:00
spinner . info ( ` ${ i18n ( 'VIEW_IN_LIBRESCORE' ) ( ) } : ${ await librescoreLink } ` )
2021-01-02 07:34:19 +00:00
} catch { } // it doesn't affect the main feature
}
2020-11-24 22:32:44 +00:00
spinner . start ( )
// load score using webmscore
score = await loadMscore ( scoreinfo )
metadata = await score . metadata ( )
spinner . info ( 'Score loaded by webmscore' )
} catch ( err ) {
spinner . fail ( err . message )
return
}
spinner . succeed ( 'OK\n' )
// build part choices
const partChoices = metadata . excerpts . map ( p = > ( { name : p.title , value : p.id } ) )
// add the "full score" option as a "part"
partChoices . unshift ( { value : - 1 , name : i18n ( 'FULL_SCORE' ) ( ) } )
// build filetype choices
const typeChoices = INDV_DOWNLOADS . map ( ( d , i ) = > ( { name : d.name , value : i } ) )
// part selection
2020-11-26 19:58:03 +00:00
const { part } = await inquirer . prompt < Params > ( {
2020-11-24 22:32:44 +00:00
type : 'list' ,
name : 'part' ,
message : 'Part Selection' ,
choices : partChoices ,
} )
const partName = partChoices [ part + 1 ] . name
await score . setExcerptId ( part )
// filetype selection
2020-11-26 19:58:03 +00:00
const { types } = await inquirer . prompt < Params > ( {
2020-11-24 22:32:44 +00:00
type : 'checkbox' ,
name : 'types' ,
message : 'Filetype Selection' ,
choices : typeChoices ,
2020-11-26 20:02:25 +00:00
validate ( input : number [ ] ) {
return input . length >= 1
} ,
2020-11-24 22:32:44 +00:00
} )
const filetypes = types . map ( i = > INDV_DOWNLOADS [ i ] )
2020-11-26 19:49:54 +00:00
// destination directory
2020-11-26 19:58:03 +00:00
const { dest } = await inquirer . prompt < Params > ( {
2020-11-26 19:49:54 +00:00
type : 'input' ,
name : 'dest' ,
message : 'Destination Directory:' ,
validate ( input : string ) {
return input && fs . statSync ( input ) . isDirectory ( )
} ,
default : process . cwd ( ) ,
} )
2020-11-24 22:32:44 +00:00
// export files
2020-11-26 21:40:25 +00:00
const fileName = scoreinfo . fileName || await score . titleFilenameSafe ( )
2020-11-24 22:32:44 +00:00
spinner . start ( )
await Promise . all (
filetypes . map ( async ( d ) = > {
const data = await d . action ( score )
2020-11-26 20:07:34 +00:00
const n = ` ${ fileName } - ${ escapeFilename ( partName ) } . ${ d . fileExt } `
const f = path . join ( dest , n )
2020-11-24 22:32:44 +00:00
await fs . promises . writeFile ( f , data )
spinner . info ( ` Saved ${ chalk . underline ( f ) } ` )
spinner . start ( )
} ) ,
)
spinner . succeed ( 'OK' )
2021-01-15 08:02:48 +00:00
2021-01-20 15:44:00 +00:00
if ( ! ( await isNpx ( ) ) ) {
const installed = await installedVersion ( )
const latest = await latestVersion ( )
if ( ! isLatest ( installed , latest ) ) {
console . log ( chalk . yellowBright ( ` Your installed version ( ${ installed } ) of the musescore-downloader CLI is not the latest one ( ${ latest } )! \ nRun npm i -g musescore-downloader to update. ` ) )
}
2021-01-15 08:02:48 +00:00
}
2020-11-24 22:32:44 +00:00
} ) ( )