| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211 |
- import { MUSIC_CARD_EMPTY_ARTIST, MUSIC_CARD_EMPTY_TITLE } from './MusicCardConstants'
- const DEFAULT_TIME_TEXT = '00:00'
- const LYRIC_TIMESTAMP_PATTERN = /\[(\d{2}):(\d{2})(?:\.(\d{2,3}))?\]/g
- const isNonEmpty = (value?: string): boolean => {
- return !!value && value.trim() !== ''
- }
- export interface MusicCardSnapshot {
- title: string
- artist: string
- coverPath: string
- hasCoverImage: boolean
- hasSong: boolean
- isPlaying: boolean
- currentPositionMs: number
- durationMs: number
- currentTimeText: string
- durationTimeText: string
- lyricLine1: string
- lyricLine2: string
- hasLyric: boolean
- filePath: string
- updatedAtMs: number
- lyricText: string
- }
- export interface MusicCardLyricLines {
- line1: string
- line2: string
- hasLyric: boolean
- }
- export class MusicCardBindingData {
- title: string = MUSIC_CARD_EMPTY_TITLE
- artist: string = MUSIC_CARD_EMPTY_ARTIST
- coverPath: string = ''
- hasCoverImage: boolean = false
- hasSong: boolean = false
- isPlaying: boolean = false
- currentPositionMs: number = 0
- durationMs: number = 0
- currentTimeText: string = DEFAULT_TIME_TEXT
- durationTimeText: string = DEFAULT_TIME_TEXT
- lyricLine1: string = MUSIC_CARD_EMPTY_TITLE
- lyricLine2: string = MUSIC_CARD_EMPTY_ARTIST
- hasLyric: boolean = false
- filePath: string = ''
- updatedAtMs: number = 0
- }
- export function createEmptyMusicCardSnapshot(): MusicCardSnapshot {
- return {
- title: '',
- artist: '',
- coverPath: '',
- hasCoverImage: false,
- hasSong: false,
- isPlaying: false,
- currentPositionMs: 0,
- durationMs: 0,
- currentTimeText: DEFAULT_TIME_TEXT,
- durationTimeText: DEFAULT_TIME_TEXT,
- lyricLine1: MUSIC_CARD_EMPTY_TITLE,
- lyricLine2: MUSIC_CARD_EMPTY_ARTIST,
- hasLyric: false,
- filePath: '',
- updatedAtMs: 0,
- lyricText: ''
- }
- }
- export function resolveMusicCardLyricLines(
- lyricText: string,
- positionMs: number
- ): MusicCardLyricLines {
- const trimmedText = lyricText ? lyricText.trim() : ''
- if (trimmedText === '') {
- return {
- line1: '',
- line2: '',
- hasLyric: false
- }
- }
- const entries: Array<{ timeMs: number; text: string }> = []
- const rawLines = trimmedText.split(/\r?\n/)
- for (let i = 0; i < rawLines.length; i++) {
- const rawLine = rawLines[i].trim()
- if (rawLine === '') {
- continue
- }
- const timestamps: Array<{ timeMs: number }> = []
- let lastMatchEnd = 0
- LYRIC_TIMESTAMP_PATTERN.lastIndex = 0
- let match = LYRIC_TIMESTAMP_PATTERN.exec(rawLine)
- while (match) {
- const minutes = parseInt(match[1], 10)
- const seconds = parseInt(match[2], 10)
- const msPart = match[3]
- let msValue = 0
- if (msPart) {
- msValue = msPart.length === 2 ? parseInt(msPart, 10) * 10 : parseInt(msPart, 10)
- }
- const timeMs = minutes * 60 * 1000 + seconds * 1000 + msValue
- timestamps.push({ timeMs })
- lastMatchEnd = LYRIC_TIMESTAMP_PATTERN.lastIndex
- match = LYRIC_TIMESTAMP_PATTERN.exec(rawLine)
- }
- if (timestamps.length === 0) {
- continue
- }
- const text = rawLine.slice(lastMatchEnd).trim()
- if (text === '') {
- continue
- }
- for (let j = 0; j < timestamps.length; j++) {
- entries.push({
- timeMs: timestamps[j].timeMs,
- text
- })
- }
- }
- if (entries.length === 0) {
- return {
- line1: '',
- line2: '',
- hasLyric: false
- }
- }
- entries.sort((lhs, rhs) => lhs.timeMs - rhs.timeMs)
- let currentIndex = -1
- for (let i = 0; i < entries.length; i++) {
- if (entries[i].timeMs <= positionMs) {
- currentIndex = i
- } else {
- break
- }
- }
- if (currentIndex === -1) {
- currentIndex = 0
- }
- const currentLine = entries[currentIndex]
- const nextLine = entries[currentIndex + 1]
- return {
- line1: currentLine.text,
- line2: nextLine ? nextLine.text : '',
- hasLyric: true
- }
- }
- export function buildMusicCardBindingData(snapshot: MusicCardSnapshot): MusicCardBindingData {
- const source = snapshot || createEmptyMusicCardSnapshot()
- const lyricLines = resolveMusicCardLyricLines(source.lyricText, source.currentPositionMs)
- const data = new MusicCardBindingData()
- data.hasSong = Boolean(source.hasSong)
- data.title =
- data.hasSong && isNonEmpty(source.title) ? source.title : MUSIC_CARD_EMPTY_TITLE
- data.artist =
- data.hasSong && isNonEmpty(source.artist) ? source.artist : MUSIC_CARD_EMPTY_ARTIST
- data.coverPath = source.coverPath ?? ''
- data.hasCoverImage = Boolean(source.hasCoverImage)
- data.isPlaying = Boolean(source.isPlaying)
- data.currentPositionMs = source.currentPositionMs ?? 0
- data.durationMs = source.durationMs ?? 0
- data.currentTimeText = isNonEmpty(source.currentTimeText)
- ? source.currentTimeText
- : DEFAULT_TIME_TEXT
- data.durationTimeText = isNonEmpty(source.durationTimeText)
- ? source.durationTimeText
- : DEFAULT_TIME_TEXT
- data.filePath = source.filePath ?? ''
- data.updatedAtMs = source.updatedAtMs ?? 0
- let lyricLine1 = data.title
- let lyricLine2 = data.artist
- let hasLyric = false
- if (lyricLines.hasLyric) {
- lyricLine1 = lyricLines.line1
- lyricLine2 = lyricLines.line2
- hasLyric = true
- } else if (
- source.hasLyric &&
- (isNonEmpty(source.lyricLine1) || isNonEmpty(source.lyricLine2))
- ) {
- lyricLine1 = isNonEmpty(source.lyricLine1) ? source.lyricLine1 : data.title
- lyricLine2 = isNonEmpty(source.lyricLine2) ? source.lyricLine2 : data.artist
- hasLyric = true
- }
- data.lyricLine1 = lyricLine1
- data.lyricLine2 = lyricLine2
- data.hasLyric = hasLyric
- return data
- }
|