Skip to content

Commit

Permalink
✨ feat: add translated lyric for ncm
Browse files Browse the repository at this point in the history
Signed-off-by: SimonShiki <sinangentoo@gmail.com>
  • Loading branch information
SimonShiki committed Aug 12, 2024
1 parent 9432a02 commit a7957e4
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 9 deletions.
20 changes: 12 additions & 8 deletions src/storages/ncm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import type { SetStateAction, WritableAtom } from 'jotai';
import { backendStorage } from '../utils/local-utitity';
import { fetchArraybuffer } from '../utils/chunk-transformer';
import { currentSongJotai } from '../jotais/play';
import { mergeLyrics } from '../utils/lyric-parser';

interface NCMSearchResult {
id: number;
Expand Down Expand Up @@ -313,14 +314,17 @@ export class NCM implements AbstractStorage {
backendStorage.set('cachedNCMSong', songlist);
});
sharedStore.sub(currentSongJotai, async () => {
const currentSong = sharedStore.get(currentSongJotai);
if (!currentSong || currentSong.storage !== 'ncm' || currentSong.lyrics) return;
try {
const currentSong = sharedStore.get(currentSongJotai);
if (!currentSong || currentSong.storage !== 'ncm' || currentSong.lyrics) return;

const res = await fetch(`${this.config.api}lyric?id=${currentSong.id}`);
const {lrc: {lyric}} = await res.json();

const lyricJotai = focusAtom(currentSongJotai as WritableAtom<Song<string>, [SetStateAction<Song<string>>], void>, (optic) => optic.prop('lyrics'));
sharedStore.set(lyricJotai, lyric);
const res = await fetch(`${this.config.api}lyric?id=${currentSong.id}`);
const { lrc: { lyric }, tlyric: { lyric: tlyric } } = await res.json();
const lyricJotai = focusAtom(currentSongJotai as WritableAtom<Song<string>, [SetStateAction<Song<string>>], void>, (optic) => optic.prop('lyrics'));
sharedStore.set(lyricJotai, mergeLyrics(lyric, tlyric));
} catch (e) {
console.error('Error occurred while getting remote lyrics', e);
}
});
}

Expand Down Expand Up @@ -423,7 +427,7 @@ export class NCM implements AbstractStorage {
const res = await fetch(`${this.config.api}album?id=${song.album.id}`);
const { album } = await res.json();
return ({
id: `ncm-${song.id}`,
id: song.id,
name: song.name,
duration: song.duration,
mtime: song.album.publishTime ?? 0,
Expand Down
41 changes: 40 additions & 1 deletion src/utils/lyric-parser.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export function parseLyrics(raw: string): ParsedLyrics | string {
export function parseLyrics (raw: string): ParsedLyrics | string {
const lines = raw.split('\n');
const parsedLyrics: ParsedLyrics = { lines: [] };
let isStandardFormat = false;
Expand Down Expand Up @@ -52,6 +52,45 @@ export function parseLyrics(raw: string): ParsedLyrics | string {
return parsedLyrics;
}

export function mergeLyrics (rawLyrics: string, translatedLyrics?: string | null) {
if (!translatedLyrics) {
return rawLyrics;
}

const rawParsed = parseLyrics(rawLyrics);
const translatedParsed = parseLyrics(translatedLyrics);

if (typeof rawParsed === 'string' || typeof translatedParsed === 'string') {
// If either parsing failed, return the raw lyrics
return rawLyrics;
}

const mergedLines: string[] = [];
const translatedMap = new Map(translatedParsed.lines.map(line => [line.time, line.content]));

for (const line of rawParsed.lines) {
const translatedContent = translatedMap.get(line.time);
if (translatedContent) {
mergedLines.push(`[${formatTime(line.time)}]${line.content}`);
mergedLines.push(`[${formatTime(line.time)}]${translatedContent}`);
} else {
mergedLines.push(`[${formatTime(line.time)}]${line.content}`);
}
}

// Add the [by:] line if present in either lyrics
const byLine = rawParsed.by ? `[by:${rawParsed.by}]` : (translatedParsed.by ? `[by:${translatedParsed.by}]` : '');

return byLine + (byLine ? '\n' : '') + mergedLines.join('\n');
}

function formatTime (ms: number) {
const minutes = Math.floor(ms / 60000);
const seconds = Math.floor((ms % 60000) / 1000);
const milliseconds = ms % 1000;
return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(milliseconds).padStart(3, '0')}`;
}

export interface ParsedLyrics {
by?: string;
lines: Line[];
Expand Down

0 comments on commit a7957e4

Please sign in to comment.