packages/bms/src/song-info/index.ts
import { match } from '../util/match'
import { assign } from '../util/lodash'
import { BMSChart } from '../bms/chart'
export interface ISongInfoData {
title: string
artist: string
genre: string
subtitles: string[]
subartists: string[]
difficulty: number
level: number
}
/**
* A SongInfo represents the song info, such as title, artist, genre.
*
* ## Example
*
* If you have a BMS like this:
*
* ```
* #TITLE Exargon [HYPER]
* ```
*
* Having parsed it using a {Compiler} into a {BMSChart},
* you can create a {SongInfo} using `fromBMSChart()`:
*
* ```js
* var info = SongInfo.fromBMSChart(bmsChart)
* ```
*
* Then you can query the song information by accessing its properties.
*
* ```js
* info.title // => 'Exargon'
* info.subtitles // => ['HYPER']
* ```
*/
export class SongInfo implements ISongInfoData {
title: string
artist: string
genre: string
subtitles: string[]
subartists: string[]
difficulty: number
level: number
/**
* Constructs a SongInfo with given data
* @param info the properties to set on this new instance
*/
constructor(info: { [propertyName: string]: any }) {
/** the song title */
this.title = 'NO TITLE'
/** the song artist */
this.artist = 'NO ARTIST'
/** the song genre */
this.genre = 'NO GENRE'
/**
* the song's subtitles, one line per element.
* The subtitle may be used to represent the difficulty name,
* such as NORMAL, HYPER, ANOTHER.
* @type {string[]}
*/
this.subtitles = []
/**
* the song's other artists, one artist per element.
* @type {string[]}
*/
this.subartists = []
/**
* the difficulty.
* Meaning of the number is same as BMS's [`#DIFFICULTY`](http:*hitkey.nekokan.dyndns.info/cmds.htm#DIFFICULTY) header.
*
* | difficulty | meaning |
* | ----------:|:--------:|
* | 1 | BEGINNER |
* | 2 | NORMAL |
* | 3 | HYPER |
* | 4 | ANOTHER |
* | 5 | INSANE |
*/
this.difficulty = 0
/**
* the level of the song.
* When converted from a BMS chart, this is the value of `#PLAYLEVEL` header.
*/
this.level = 0
if (info) assign(this, info)
}
/**
* Constructs a new {SongInfo} object from a {BMSChart}.
* @param chart A {BMSChart} to construct a {SongInfo} from
*/
static fromBMSChart(chart: BMSChart) {
void BMSChart
const info: Partial<ISongInfoData> = {}
let title = chart.headers.get('title')
const artist = chart.headers.get('artist')
const genre = chart.headers.get('genre')
const difficulty = +chart.headers.get('difficulty')! || 0
const level = +chart.headers.get('playlevel')! || 0
let subtitles = chart.headers.getAll('subtitle')
const subartists = chart.headers.getAll('subartist')
if (typeof title === 'string' && !subtitles) {
const extractSubtitle = function (m: string[]) {
title = m[1]
subtitles = [m[2]]
}
match(title)
.when(/^(.*\S)\s*-(.+?)-$/, extractSubtitle)
.when(/^(.*\S)\s*~(.+?)~$/, extractSubtitle)
.when(/^(.*\S)\s*\((.+?)\)$/, extractSubtitle)
.when(/^(.*\S)\s*\[(.+?)\]$/, extractSubtitle)
.when(/^(.*\S)\s*<(.+?)>$/, extractSubtitle)
}
if (title) info.title = title
if (artist) info.artist = artist
if (genre) info.genre = genre
if (subtitles) info.subtitles = subtitles
if (subartists) info.subartists = subartists
if (difficulty) info.difficulty = difficulty
if (level) info.level = level
return new SongInfo(info)
}
}