UNPKG

create-tiny-sb

Version:

Bootstrap your osu storyboard with tiny-osbjs.

152 lines (133 loc) 3.83 kB
import { color, Color, DefaultPallete, fade, Layer, Origin, scale } from '@osbjs/tiny-osbjs' import { createText, createTxtGenContext, ejectAllTextImages, FontProps, maxLineHeight, measureLineWidth, useFont, useTxtGenContext } from '@osbjs/txtgen-tiny-osbjs' import { readFileSync } from 'fs' import { Cue, parseSync } from 'subtitle' import { beatmapFolder } from '../config' export type LyricsOptions = { /** * Path to non-system font you want to use. */ fontPath?: string /** * Font properties used to generate text. */ fontProps: FontProps /** * The scale factor applied to the generated text image. */ fontScale?: number /** * Generate image for each character instead of a full sentence. */ perCharacter?: boolean /** * Fade in/out duration. */ fadeDuration?: number /** * Opacity. */ opacity?: number /** * Y coordinate of each sentence. */ y?: number /** * Text color. */ textColor?: Color /** * If you are rendering each character, we will not render the space character * but instead we will add a gap to the x coordinate calculation between each letter. */ spaceWidth?: number } function useDefaultConfigIfEmpty(options: LyricsOptions) { return { perCharacter: false, fadeDuration: 300, opacity: 1, y: 400, textColor: DefaultPallete.White, fontScale: 1, ...options } } type Lyric = { text: string startTime: number endTime: number } /** * `subtitle` only supports srt and vtt file. */ function loadLyricsFile(lyricsFilePath: string): Lyric[] { const input = readFileSync(lyricsFilePath, 'utf8') if (lyricsFilePath.match(/.*\.(srt|vtt)$/)) { let data = parseSync(input) return data .filter((s) => s.type == 'cue') .map((s) => { const { start: startTime, end: endTime, text } = s.data as Cue return { startTime, endTime, text } }) } else if (lyricsFilePath.match(/.*\.json$/)) { return JSON.parse(input) } else { throw new Error('Unsupported file type') } } /** * Read the subtitle file and generate lyrics. * @param lyricsFilePath Path to the lyrics file. * @param osbFolderPath Relative path to the folder that will be used to save generated text images, ex: `sb/lyrics`. * @param options Read the type definition to see which options you can override. */ export default function Lyrics(lyricsFilePath: string, osbFolderPath: string, options: LyricsOptions) { const { perCharacter, fadeDuration, opacity, y, textColor, fontProps, fontPath, fontScale } = useDefaultConfigIfEmpty(options) if (fontPath) useFont(fontPath, fontProps.name) const txtgenContext = createTxtGenContext(osbFolderPath, beatmapFolder, fontProps) useTxtGenContext(txtgenContext) const lyrics = loadLyricsFile(lyricsFilePath) if (perCharacter) { lyrics.forEach(({ text, startTime, endTime }) => { let letterY = y // may have multiple lines text.split('\n').forEach((line) => { const lineHeight = maxLineHeight(line) * fontScale const lineWidth = measureLineWidth(line) * fontScale let letterX = 320 - lineWidth / 2 line.split('').forEach((letter) => { createText(letter, Layer.Background, Origin.Centre, [letterX, letterY], ({ width }) => { scale(startTime, fontScale) fade([startTime, startTime + fadeDuration], 0, opacity) fade([endTime - fadeDuration, endTime], opacity, 0) color(startTime, textColor) letterX += width * fontScale }) }) letterY += lineHeight }) }) } else { lyrics.forEach(({ text, startTime, endTime }) => { createText(text, Layer.Background, Origin.Centre, [320, y], () => { scale(startTime, fontScale) fade([startTime, startTime + fadeDuration], 0, opacity) fade([endTime - fadeDuration, endTime], opacity, 0) color(startTime, textColor) }) }) } ejectAllTextImages() }