All files / Rindu/utils normalizeLrcLibLyrics.ts

5.55% Statements 2/36
0% Branches 0/12
0% Functions 0/11
6.06% Lines 2/33

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 12844x                                                                                                                                                                                                   2x                                                          
import { MILLISECONDS } from "./constants";
import { IlrclibResponse, ILyrics } from "types/lyrics";
 
function convertTimestampToMs(
  minutes: string,
  seconds: string,
  centiseconds: string
): number {
  return (
    parseInt(minutes) * MILLISECONDS.MINUTE +
    parseInt(seconds) * MILLISECONDS.SECOND +
    parseInt(centiseconds) * MILLISECONDS.CENTISECOND
  );
}
 
function parseSyncedLyrics(
  syncedLyrics: IlrclibResponse["syncedLyrics"]
): ILyrics["lyrics"]["lines"] {
  const lines = syncedLyrics?.split("\n").filter((line) => line.trim());
  const mappedLines = lines?.map((line, index, array) => {
    const timeMatch = RegExp(/\[(\d{2}):(\d{2})\.(\d{2})\]/).exec(line);
    Iif (!timeMatch) return null;
 
    const [, minutes, seconds, centiseconds] = timeMatch;
    const startTimeMs = convertTimestampToMs(
      minutes,
      seconds,
      centiseconds
    ).toString();
 
    let endTimeMs = startTimeMs;
    if (index < array.length - 1) {
      const nextTimeMatch = RegExp(/\[(\d{2}):(\d{2})\.(\d{2})\]/).exec(
        array[index + 1]
      );
      Iif (nextTimeMatch) {
        const [, nextMin, nextSec, nextCent] = nextTimeMatch;
        endTimeMs = convertTimestampToMs(nextMin, nextSec, nextCent).toString();
      }
    } else {
      endTimeMs = (parseInt(startTimeMs) + 5000).toString();
    }
 
    const words = line.replace(/\[\d{2}:\d{2}\.\d{2}\]/, "").trim();
 
    return {
      startTimeMs,
      endTimeMs,
      words,
      syllables: [],
    };
  });
 
  return (
    mappedLines?.filter(
      (line): line is NonNullable<typeof line> => line !== null
    ) || []
  );
}
 
function parseUnsyncedLyrics(
  plainLyrics: IlrclibResponse["plainLyrics"]
): ILyrics["lyrics"]["lines"] {
  const lines = plainLyrics?.split("\n").filter((line) => line.trim());
 
  return (
    lines?.map((line) => ({
      startTimeMs: "0",
      endTimeMs: "0",
      words: line.trim(),
      syllables: [],
    })) || []
  );
}
 
function getInstrumentalLine(): ILyrics["lyrics"]["lines"] {
  return [
    {
      startTimeMs: "0",
      endTimeMs: "0",
      words: "♪ Instrumental ♪",
      syllables: [],
    },
  ];
}
 
function getLines(lrclibResponse: IlrclibResponse, hasSync: boolean) {
  Iif (lrclibResponse.instrumental) {
    return getInstrumentalLine();
  }
 
  Iif (hasSync) {
    return parseSyncedLyrics(lrclibResponse.syncedLyrics);
  }
 
  return parseUnsyncedLyrics(lrclibResponse.plainLyrics);
}
 
export function normalizeLrcLibLyrics(
  lrclibResponse: IlrclibResponse
): ILyrics {
  const hasSync = !!lrclibResponse.syncedLyrics?.includes("[");
  const syncType = hasSync ? "LINE_SYNCED" : "UNSYNCED";
  const lines = getLines(lrclibResponse, hasSync);
 
  return {
    lyrics: {
      syncType: syncType,
      lines: lines,
      provider: "lrclib",
      providerLyricsId: lrclibResponse.id.toString(),
      providerDisplayName: "LRCLib",
      syncLyricsUri: `https://lrclib.net/api/get/${lrclibResponse.id}`,
      isDenseTypeface: false,
      alternatives: [],
      language: "en",
      isRtlLanguage: false,
      fullscreenAction: "FULLSCREEN_LYRICS",
    },
    colors: {
      background: -9013642,
      text: -16777216,
      highlightText: -1,
    },
    hasVocalRemoval: false,
  };
}