@0xsharon/synco 0.0.0-stage → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/README.md +182 -2
- package/dist/cli.cjs +984 -0
- package/dist/index.cjs +1024 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +248 -0
- package/dist/index.d.ts +248 -0
- package/dist/index.mjs +960 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +67 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/lyrics.ts","../src/types.ts","../src/utils.ts","../src/providers/base.ts","../src/providers/lrclib.ts","../src/providers/megalobiz.ts","../src/providers/musixmatch.ts","../src/providers/netease.ts","../src/providers/genius.ts","../src/providers/deezer.ts","../src/providers/lyricsify.ts"],"sourcesContent":["import { Lyrics } from './lyrics.js';\nimport { TargetType, type SearchOptions, type ILRCProvider } from './types.js';\nimport {\n Lrclib,\n Megalobiz,\n Musixmatch,\n NetEase,\n Genius,\n Deezer,\n Lyricsify,\n LRCProvider,\n} from './providers/index.js';\nimport { Logger } from './utils.js';\n\nexport * from './types.js';\nexport * from './lyrics.js';\nexport * from './utils.js';\nexport * from './providers/index.js';\n\n/**\n * Searches for LRC format (synchronized) lyrics for a given track.\n */\nexport async function search(\n searchTerm: string,\n options?: SearchOptions\n): Promise<string | null>;\nexport async function search(\n searchTerm: string,\n plainOnly?: boolean,\n syncedOnly?: boolean,\n savePath?: string,\n providers?: string[],\n lang?: string,\n enhanced?: boolean\n): Promise<string | null>;\nexport async function search(\n searchTerm: string,\n optOrPlainOnly?: SearchOptions | boolean,\n syncedOnly?: boolean,\n savePath?: string,\n providers?: string[],\n lang?: string,\n enhanced?: boolean\n): Promise<string | null> {\n const options: SearchOptions =\n typeof optOrPlainOnly === 'object' && optOrPlainOnly !== null\n ? optOrPlainOnly\n : {\n plainOnly: Boolean(optOrPlainOnly),\n syncedOnly: Boolean(syncedOnly),\n savePath,\n providers,\n lang,\n enhanced: Boolean(enhanced),\n };\n\n const logger = new Logger('synco', Boolean(options.verbose));\n\n if (options.plainOnly && options.syncedOnly) {\n logger.error('--plain-only and --synced-only flags cannot be used together.');\n return null;\n }\n\n let targetType = TargetType.PREFER_SYNCED;\n if (options.plainOnly) {\n targetType = TargetType.PLAINTEXT;\n } else if (options.syncedOnly) {\n targetType = TargetType.SYNCED_ONLY;\n }\n\n const lyrics = new Lyrics();\n\n const providerInstances: ILRCProvider[] = [\n new Musixmatch({\n lang: options.lang,\n enhanced: options.enhanced,\n musixmatchToken: options.musixmatchToken,\n timeoutMs: options.timeoutMs,\n verbose: options.verbose,\n }),\n new Lrclib({\n timeoutMs: options.timeoutMs,\n verbose: options.verbose,\n }),\n new NetEase({\n timeoutMs: options.timeoutMs,\n verbose: options.verbose,\n }),\n new Megalobiz({\n timeoutMs: options.timeoutMs,\n verbose: options.verbose,\n }),\n new Genius({\n timeoutMs: options.timeoutMs,\n verbose: options.verbose,\n }),\n ];\n\n const selectedProviders = selectProviders(providerInstances, options.providers, logger);\n\n for (const provider of selectedProviders) {\n logger.debug(`Looking for an LRC on ${provider.name}`);\n try {\n const result = await provider.getLrc(searchTerm);\n if (result) {\n lyrics.update(result);\n }\n } catch (err: any) {\n logger.error(`An error occurred while searching for an LRC on ${provider.name}:`, err?.message ?? err);\n if (options.lang) {\n logger.error('Aborting, since `lang` is only supported by Musixmatch');\n return null;\n }\n continue;\n }\n\n if (lyrics.isPreferred(targetType)) {\n logger.info(`Lyrics found for \"${searchTerm}\" on ${provider.name}`);\n break;\n } else if (lyrics.isAcceptable(targetType)) {\n logger.info(`Found plaintext lyrics on ${provider.name}, but continuing search for synced lyrics`);\n } else {\n logger.debug(`No suitable lyrics found on ${provider.name}, continuing search...`);\n }\n }\n\n if (!lyrics.isAcceptable(targetType)) {\n logger.info(`No suitable lyrics found for \"${searchTerm}\" :(`);\n return null;\n }\n\n if (options.savePath) {\n const formattedPath = options.savePath.replace(/{search_term}/g, searchTerm);\n await lyrics.saveLrcFile(formattedPath, targetType);\n }\n\n return lyrics.toStr(targetType);\n}\n\nfunction selectProviders(\n allProviders: ILRCProvider[],\n requestedNames?: string[],\n logger?: Logger\n): ILRCProvider[] {\n if (!requestedNames || requestedNames.length === 0) {\n return allProviders;\n }\n\n const lookup = new Set(requestedNames.map((name) => name.toLowerCase().trim()));\n const filtered = allProviders.filter((p) => lookup.has(p.name.toLowerCase()));\n\n if (filtered.length === 0) {\n logger?.error(\n `Providers [${requestedNames.join(', ')}] not found in the list of available providers.`\n );\n return [];\n }\n\n return filtered;\n}\n\n// Support callable default export as well as object properties\nexport interface SyncoCallable {\n (searchTerm: string, options?: SearchOptions): Promise<string | null>;\n search: typeof search;\n Lyrics: typeof Lyrics;\n TargetType: typeof TargetType;\n LRCProvider: typeof LRCProvider;\n Lrclib: typeof Lrclib;\n Megalobiz: typeof Megalobiz;\n Musixmatch: typeof Musixmatch;\n NetEase: typeof NetEase;\n Genius: typeof Genius;\n Deezer: typeof Deezer;\n Lyricsify: typeof Lyricsify;\n}\n\nexport const synco = Object.assign(\n function (searchTerm: string, options?: SearchOptions) {\n return search(searchTerm, options);\n },\n {\n search,\n Lyrics,\n TargetType,\n LRCProvider,\n Lrclib,\n Megalobiz,\n Musixmatch,\n NetEase,\n Genius,\n Deezer,\n Lyricsify,\n }\n) as SyncoCallable;\n\nexport const syncedlyrics = synco;\nexport default synco;\n\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { TargetType, type ILyrics } from './types.js';\nimport { identifyLyricsType, syncedToPlaintext } from './utils.js';\n\nexport class Lyrics implements ILyrics {\n public synced?: string | null = null;\n public unsynced?: string | null = null;\n\n constructor(synced?: string | null, unsynced?: string | null) {\n this.synced = synced ?? null;\n this.unsynced = unsynced ?? null;\n }\n\n public addUnknown(unknown?: string | null): void {\n if (!unknown || typeof unknown !== 'string') return;\n const trimmed = unknown.trim();\n if (!trimmed) return;\n\n const type = identifyLyricsType(trimmed);\n if (type === 'synced') {\n this.synced = trimmed;\n } else if (type === 'plaintext') {\n this.unsynced = trimmed;\n }\n }\n\n public update(other?: ILyrics | null): void {\n if (!other) return;\n if (other.synced) {\n this.synced = other.synced;\n }\n if (other.unsynced) {\n this.unsynced = other.unsynced;\n }\n }\n\n public isPreferred(targetType: TargetType): boolean {\n return Boolean(\n this.synced || (targetType === TargetType.PLAINTEXT && this.unsynced)\n );\n }\n\n public isAcceptable(targetType: TargetType): boolean {\n return Boolean(\n this.synced || (targetType !== TargetType.SYNCED_ONLY && this.unsynced)\n );\n }\n\n public toStr(targetType: TargetType): string | null {\n if (targetType === TargetType.PLAINTEXT) {\n if (this.unsynced) return this.unsynced;\n if (this.synced) return syncedToPlaintext(this.synced);\n return null;\n }\n if (targetType === TargetType.PREFER_SYNCED) {\n return this.synced || this.unsynced || null;\n }\n return this.synced || null;\n }\n\n public async saveLrcFile(filePath: string, targetType: TargetType): Promise<void> {\n const text = this.toStr(targetType);\n if (!text) return;\n const resolvedPath = path.resolve(filePath);\n await fs.promises.mkdir(path.dirname(resolvedPath), { recursive: true });\n await fs.promises.writeFile(resolvedPath, text, 'utf-8');\n }\n}\n\n","export enum TargetType {\n PLAINTEXT = 'PLAINTEXT',\n PREFER_SYNCED = 'PREFER_SYNCED',\n SYNCED_ONLY = 'SYNCED_ONLY',\n}\n\nexport type LyricsType = 'synced' | 'plaintext' | 'invalid';\n\nexport interface SearchOptions {\n /** Only look for plain text (not synced) lyrics */\n plainOnly?: boolean;\n /** Only look for synced lyrics */\n syncedOnly?: boolean;\n /** Path to save `.lrc` lyrics (e.g. \"{search_term}.lrc\"). No saving if omitted */\n savePath?: string;\n /** List of provider names to include (e.g. ['lrclib', 'megalobiz', 'netease', 'musixmatch', 'genius']) */\n providers?: string[];\n /** Language code of the translation along with the lyrics (ISO 639-1 format, e.g. 'es', 'de') */\n lang?: string;\n /** Return word-by-word (karaoke style) synced lyrics if available */\n enhanced?: boolean;\n /** Custom Musixmatch user token if available */\n musixmatchToken?: string;\n /** Minimum similarity score between 0 and 100 (default: 65) */\n minScore?: number;\n /** Enable debug logging */\n verbose?: boolean;\n /** Request timeout in milliseconds (default: 10000) */\n timeoutMs?: number;\n}\n\nexport interface ProviderOptions {\n lang?: string;\n enhanced?: boolean;\n musixmatchToken?: string;\n timeoutMs?: number;\n verbose?: boolean;\n}\n\nexport interface ILRCProvider {\n name: string;\n getLrc(searchTerm: string): Promise<ILyrics | null>;\n getLrcById?(trackId: string): Promise<ILyrics | null>;\n}\n\nexport interface ILyrics {\n synced?: string | null;\n unsynced?: string | null;\n addUnknown(unknown?: string | null): void;\n update(other?: ILyrics | null): void;\n isPreferred(targetType: TargetType): boolean;\n isAcceptable(targetType: TargetType): boolean;\n toStr(targetType: TargetType): string | null;\n saveLrcFile(filePath: string, targetType: TargetType): Promise<void>;\n}\n\n","import os from 'node:os';\nimport path from 'node:path';\nimport type { LyricsType } from './types.js';\n\nexport const R_FEAT = /\\((feat.+)\\)/i;\n\n/**\n * Converts synced LRC string to plain text by stripping timestamps.\n */\nexport function syncedToPlaintext(syncedLyrics: string): string {\n if (!syncedLyrics) return '';\n return syncedLyrics\n .replace(/\\[\\d+:\\d+(?:\\.\\d+)?\\]\\s*/g, '')\n .replace(/<\\d+:\\d+(?:\\.\\d+)?>\\s*/g, '')\n .trim();\n}\n\n/**\n * Identifies whether an LRC string contains timestamps (synced) or is plaintext.\n */\nexport function identifyLyricsType(lrc: string): LyricsType {\n if (!lrc || typeof lrc !== 'string' || !lrc.trim()) {\n return 'invalid';\n }\n const lines = lrc.split('\\n').map((l) => l.trim()).filter(Boolean);\n if (lines.length === 0) return 'invalid';\n\n const timeTagRegex = /\\[\\d{1,2}:\\d{2}(?:\\.\\d{1,3})?\\]/;\n const taggedLines = lines.filter((line) => timeTagRegex.test(line));\n\n // If at least 25% of lines or 3+ lines have timestamps, consider it synced\n if (taggedLines.length >= 2 || (taggedLines.length / lines.length) >= 0.25) {\n return 'synced';\n }\n return 'plaintext';\n}\n\n/**\n * Checks whether an LRC string has a translation included below lines.\n */\nexport function hasTranslation(lrc: string): boolean {\n if (!lrc) return false;\n const lines = lrc.split('\\n').slice(3, 15);\n for (let i = 0; i < lines.length; i++) {\n const line = lines[i];\n if (line.includes('[')) {\n if (i + 1 < lines.length) {\n const nextLine = lines[i + 1];\n if (nextLine.includes('(')) {\n return true;\n }\n }\n }\n }\n return false;\n}\n\n/**\n * Formats seconds into mm:ss.xx format.\n */\nexport function formatTime(timeInSeconds: number): string {\n const totalSeconds = Math.max(0, timeInSeconds);\n let minutes = Math.floor(totalSeconds / 60);\n let seconds = Math.floor(totalSeconds % 60);\n let hundredths = Math.round((totalSeconds - Math.floor(totalSeconds)) * 100);\n\n if (hundredths >= 100) {\n hundredths = 0;\n seconds += 1;\n if (seconds >= 60) {\n seconds = 0;\n minutes += 1;\n }\n }\n\n return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}.${String(hundredths).padStart(2, '0')}`;\n}\n\n/**\n * Normalizes string by lowering case and stripping non-alphanumeric punctuation.\n */\nexport function defaultProcess(str: string): string {\n return str.toLowerCase().replace(/[^\\p{L}\\p{N}\\s]/gu, ' ').trim();\n}\n\n/**\n * Indel distance calculation (Levenshtein with cost 1 for insert/delete, cost 2 for substitution).\n */\nexport function indelDistance(s1: string, s2: string): number {\n const m = s1.length;\n const n = s2.length;\n const dp: number[][] = Array.from({ length: m + 1 }, () => new Array(n + 1).fill(0));\n\n for (let i = 0; i <= m; i++) dp[i][0] = i;\n for (let j = 0; j <= n; j++) dp[0][j] = j;\n\n for (let i = 1; i <= m; i++) {\n for (let j = 1; j <= n; j++) {\n if (s1[i - 1] === s2[j - 1]) {\n dp[i][j] = dp[i - 1][j - 1];\n } else {\n dp[i][j] = Math.min(dp[i - 1][j] + 1, dp[i][j - 1] + 1);\n }\n }\n }\n return dp[m][n];\n}\n\n/**\n * Computes the ratio score (0 - 100) between two strings using Indel distance.\n */\nexport function fuzzRatio(s1: string, s2: string): number {\n if (!s1 && !s2) return 100;\n if (!s1 || !s2) return 0;\n const dist = indelDistance(s1, s2);\n const totalLen = s1.length + s2.length;\n return totalLen === 0 ? 100 : ((totalLen - dist) / totalLen) * 100;\n}\n\n/**\n * Calculates RapidFuzz-compatible token_set_ratio.\n */\nexport function tokenSetRatio(s1: string, s2: string): number {\n const p1 = defaultProcess(s1);\n const p2 = defaultProcess(s2);\n if (!p1 && !p2) return 100;\n if (!p1 || !p2) return 0;\n\n const t1 = Array.from(new Set(p1.split(/\\s+/).filter(Boolean)));\n const t2 = Array.from(new Set(p2.split(/\\s+/).filter(Boolean)));\n\n const set1 = new Set(t1);\n const set2 = new Set(t2);\n\n const intersection = t1.filter((x) => set2.has(x)).sort();\n const diff1 = t1.filter((x) => !set2.has(x)).sort();\n const diff2 = t2.filter((x) => !set1.has(x)).sort();\n\n const s0 = intersection.join(' ');\n const s1Rest = diff1.join(' ');\n const s2Rest = diff2.join(' ');\n\n const combined1 = [s0, s1Rest].filter(Boolean).join(' ');\n const combined2 = [s0, s2Rest].filter(Boolean).join(' ');\n\n if (!s0) {\n return fuzzRatio(combined1, combined2);\n }\n\n return Math.max(\n fuzzRatio(s0, combined1),\n fuzzRatio(s0, combined2),\n fuzzRatio(combined1, combined2)\n );\n}\n\n/**\n * Returns similarity score between two strings (0 - 100), accounting for feature credits.\n */\nexport function strScore(a: string, b: string): number {\n let lowerA = (a || '').toLowerCase();\n let lowerB = (b || '').toLowerCase();\n if (!lowerB.includes('feat')) {\n lowerA = lowerA.replace(R_FEAT, '');\n lowerB = lowerB.replace(R_FEAT, '');\n }\n return tokenSetRatio(lowerA, lowerB);\n}\n\n/**\n * Returns true if the similarity score between two strings is >= minScore.\n */\nexport function strSame(a: string, b: string, minScore = 65): boolean {\n return Math.round(strScore(a, b)) >= minScore;\n}\n\n/**\n * Sorts results by similarity of comparison key against the search term (descending).\n */\nexport function sortResults<T>(\n results: T[],\n searchTerm: string,\n compareKey: (item: T) => string\n): T[] {\n return [...results].sort((a, b) => {\n const scoreA = strScore(compareKey(a), searchTerm);\n const scoreB = strScore(compareKey(b), searchTerm);\n return scoreB - scoreA;\n });\n}\n\n/**\n * Returns the best match from results if its similarity score meets minScore.\n */\nexport function getBestMatch<T>(\n results: T[],\n searchTerm: string,\n compareKey: (item: T) => string,\n minScore = 65\n): T | null {\n if (!results || results.length === 0) return null;\n const sorted = sortResults(results, searchTerm, compareKey);\n const bestMatch = sorted[0];\n const valueToCompare = compareKey(bestMatch);\n if (!strSame(valueToCompare, searchTerm, minScore)) {\n return null;\n }\n return bestMatch;\n}\n\n/**\n * Resolves or creates a system cache directory for the library.\n */\nexport function getCachePath(libName = 'synco'): string {\n const platform = process.platform;\n let baseDir: string;\n\n if (platform === 'win32') {\n baseDir = process.env.LOCALAPPDATA || path.join(os.homedir(), 'AppData', 'Local');\n } else if (platform === 'darwin') {\n baseDir = path.join(os.homedir(), 'Library', 'Caches');\n } else {\n baseDir = process.env.XDG_CACHE_HOME || path.join(os.homedir(), '.cache');\n }\n\n return path.join(baseDir, libName);\n}\n\n/**\n * Simple logger for library and CLI output.\n */\nexport class Logger {\n constructor(private context: string, private verbose: boolean = false) {}\n\n public debug(...args: unknown[]): void {\n if (this.verbose) {\n console.error(`[${this.context}] [DEBUG]`, ...args);\n }\n }\n\n public info(...args: unknown[]): void {\n if (this.verbose) {\n console.error(`[${this.context}] [INFO]`, ...args);\n }\n }\n\n public warn(...args: unknown[]): void {\n console.error(`[${this.context}] [WARN]`, ...args);\n }\n\n public error(...args: unknown[]): void {\n console.error(`[${this.context}] [ERROR]`, ...args);\n }\n}\n","import axios, { type AxiosInstance, type AxiosRequestConfig } from 'axios';\nimport type { ILRCProvider, ILyrics, ProviderOptions } from '../types.js';\nimport { Logger } from '../utils.js';\n\nexport abstract class LRCProvider implements ILRCProvider {\n public abstract readonly name: string;\n protected session: AxiosInstance;\n protected logger: Logger;\n protected options: ProviderOptions;\n\n constructor(options: ProviderOptions = {}) {\n this.options = options;\n this.logger = new Logger(this.constructor.name, Boolean(options.verbose));\n this.session = axios.create({\n timeout: options.timeoutMs ?? 10000,\n headers: {\n 'User-Agent':\n 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 synco/1.0.0',\n Accept: 'application/json, text/plain, */*',\n },\n });\n }\n\n public toString(): string {\n return this.name;\n }\n\n public abstract getLrc(searchTerm: string): Promise<ILyrics | null>;\n\n public getLrcById?(trackId: string): Promise<ILyrics | null>;\n\n protected async get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T | null> {\n try {\n const response = await this.session.get<T>(url, config);\n return response.data;\n } catch (err: any) {\n this.logger.debug(`GET request failed for ${url}:`, err?.message ?? err);\n return null;\n }\n }\n\n protected async post<T = any>(\n url: string,\n data?: any,\n config?: AxiosRequestConfig\n ): Promise<T | null> {\n try {\n const response = await this.session.post<T>(url, data, config);\n return response.data;\n } catch (err: any) {\n this.logger.debug(`POST request failed for ${url}:`, err?.message ?? err);\n return null;\n }\n }\n}\n\n","import { LRCProvider } from './base.js';\nimport { Lyrics } from '../lyrics.js';\nimport { sortResults, type Logger } from '../utils.js';\nimport type { ILyrics, ProviderOptions } from '../types.js';\n\ninterface LrclibTrack {\n id: number;\n trackName: string;\n artistName: string;\n albumName?: string;\n duration?: number;\n instrumental?: boolean;\n plainLyrics?: string;\n syncedLyrics?: string;\n}\n\nexport class Lrclib extends LRCProvider {\n public readonly name = 'Lrclib';\n\n public static readonly ROOT_URL = 'https://lrclib.net';\n public static readonly API_ENDPOINT = `${Lrclib.ROOT_URL}/api`;\n public static readonly SEARCH_ENDPOINT = `${Lrclib.API_ENDPOINT}/search`;\n public static readonly LRC_ENDPOINT = `${Lrclib.API_ENDPOINT}/get/`;\n\n constructor(options: ProviderOptions = {}) {\n super(options);\n }\n\n public async getLrcById(trackId: string | number): Promise<ILyrics | null> {\n const url = `${Lrclib.LRC_ENDPOINT}${trackId}`;\n const data = await this.get<LrclibTrack>(url);\n if (!data) return null;\n\n const lyrics = new Lyrics();\n lyrics.synced = data.syncedLyrics || null;\n lyrics.unsynced = data.plainLyrics || null;\n return lyrics;\n }\n\n public async getLrc(searchTerm: string): Promise<ILyrics | null> {\n const data = await this.get<LrclibTrack[]>(Lrclib.SEARCH_ENDPOINT, {\n params: { q: searchTerm },\n });\n\n if (!data || !Array.isArray(data) || data.length === 0) {\n this.logger.debug(`No tracks found on Lrclib for \"${searchTerm}\"`);\n return null;\n }\n\n const sorted = sortResults(\n data,\n searchTerm,\n (t) => `${t.artistName} - ${t.trackName}`\n );\n\n const best = sorted[0];\n if (!best) return null;\n\n // If search result already contains lyrics, return them directly\n if (best.syncedLyrics || best.plainLyrics) {\n return new Lyrics(best.syncedLyrics || null, best.plainLyrics || null);\n }\n\n return this.getLrcById(best.id);\n }\n}\n\n","import * as cheerio from 'cheerio';\nimport { LRCProvider } from './base.js';\nimport { Lyrics } from '../lyrics.js';\nimport { getBestMatch, sortResults } from '../utils.js';\nimport type { ILyrics, ProviderOptions } from '../types.js';\n\ninterface MegalobizResult {\n id: number;\n title: string;\n artist: string;\n duration?: number;\n lrc_url?: string;\n url?: string;\n}\n\ninterface MegalobizApiResponse {\n count?: number;\n results?: MegalobizResult[];\n}\n\nexport class Megalobiz extends LRCProvider {\n public readonly name = 'Megalobiz';\n\n public static readonly ROOT_URL = 'https://www.megalobiz.com';\n public static readonly API_ENDPOINT = 'https://api.megalobiz.com/api/public/v1';\n\n constructor(options: ProviderOptions = {}) {\n super(options);\n }\n\n public async getLrc(searchTerm: string): Promise<ILyrics | null> {\n // 1. Try official public API first\n const apiResult = await this.getViaApi(searchTerm);\n if (apiResult) return apiResult;\n\n // 2. Fallback to website search if API is unavailable\n return this.getViaWeb(searchTerm);\n }\n\n private async getViaApi(searchTerm: string): Promise<ILyrics | null> {\n try {\n const data = await this.get<MegalobizApiResponse>(\n `${Megalobiz.API_ENDPOINT}/search`,\n {\n params: { q: searchTerm },\n headers: {\n 'User-Agent': 'synco-node/1.0 (https://github.com/0xSHARON/synco)',\n },\n }\n );\n\n const items = data?.results;\n if (!items || !Array.isArray(items) || items.length === 0) {\n return null;\n }\n\n const match = getBestMatch(\n items,\n searchTerm,\n (t) => `${t.artist} ${t.title}`,\n 60\n );\n\n const chosen = match || items[0];\n const lrcUrl = chosen.lrc_url || `${Megalobiz.API_ENDPOINT}/lrc/${chosen.id}?format=lrc`;\n\n const lrcResponse = await this.session.get<string>(lrcUrl, {\n headers: {\n 'User-Agent': 'synco-node/1.0 (https://github.com/0xSHARON/synco)',\n },\n responseType: 'text',\n });\n\n if (lrcResponse.status === 200 && lrcResponse.data) {\n const lyrics = new Lyrics();\n lyrics.addUnknown(lrcResponse.data);\n return lyrics;\n }\n } catch (err: any) {\n this.logger.debug('Megalobiz API error:', err?.message ?? err);\n }\n return null;\n }\n\n private async getViaWeb(searchTerm: string): Promise<ILyrics | null> {\n try {\n const query = searchTerm.replace(/\\s+/g, '+');\n const searchUrl = `${Megalobiz.ROOT_URL}/search/all?qry=${query}&searchButton.x=0&searchButton.y=0`;\n const html = await this.get<string>(searchUrl, { responseType: 'text' as any });\n if (!html || typeof html !== 'string') return null;\n\n const $ = cheerio.load(html);\n const links: { href: string; text: string }[] = [];\n\n $('a[href^=\"/lrc/maker/\"]').each((_, el) => {\n const href = $(el).attr('href');\n if (href) {\n const rawText = $(el).text().replace(/by/gi, '').trim();\n const words = rawText.split(/\\s+/).slice(0, searchTerm.split(/\\s+/).length + 1);\n links.push({ href, text: words.join(' ') });\n }\n });\n\n if (links.length === 0) return null;\n\n const match = getBestMatch(links, searchTerm, (item) => item.text, 55);\n if (!match) return null;\n\n const lrcPageUrl = `${Megalobiz.ROOT_URL}${match.href}`;\n const pageHtml = await this.get<string>(lrcPageUrl, { responseType: 'text' as any });\n if (!pageHtml) return null;\n\n const lrcId = match.href.split('.').pop();\n const page$ = cheerio.load(pageHtml);\n const lrcText = page$(`div#lrc_${lrcId}_details`).text();\n\n if (lrcText) {\n const lyrics = new Lyrics();\n lyrics.addUnknown(lrcText);\n return lyrics;\n }\n } catch (err: any) {\n this.logger.debug('Megalobiz web scraper error:', err?.message ?? err);\n }\n return null;\n }\n}\n\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { LRCProvider } from './base.js';\nimport { Lyrics } from '../lyrics.js';\nimport { getBestMatch, formatTime, getCachePath } from '../utils.js';\nimport type { ILyrics, ProviderOptions } from '../types.js';\n\ninterface CachedTokenData {\n token: string;\n expiration_time: number;\n}\n\nexport class Musixmatch extends LRCProvider {\n public readonly name = 'Musixmatch';\n\n public static readonly ROOT_URL = 'https://apic-desktop.musixmatch.com/ws/1.1/';\n private token: string | null = null;\n private lang?: string;\n private enhanced: boolean;\n\n constructor(options: ProviderOptions = {}) {\n super(options);\n this.lang = options.lang;\n this.enhanced = Boolean(options.enhanced);\n if (options.musixmatchToken || process.env.MUSIXMATCH_TOKEN) {\n this.token = options.musixmatchToken || process.env.MUSIXMATCH_TOKEN || null;\n }\n }\n\n private async requestApi<T = any>(\n action: string,\n params: Record<string, string | number> = {}\n ): Promise<T | null> {\n if (action !== 'token.get' && !this.token) {\n await this.ensureToken();\n }\n\n const query: Record<string, string | number> = {\n app_id: 'web-desktop-app-v1.0',\n t: Date.now(),\n ...params,\n };\n\n if (this.token) {\n query.usertoken = this.token;\n }\n\n const url = `${Musixmatch.ROOT_URL}${action}`;\n return this.get<T>(url, { params: query });\n }\n\n private async ensureToken(): Promise<void> {\n const tokenFile = path.join(getCachePath('synco'), 'musixmatch_token.json');\n const currentTime = Math.floor(Date.now() / 1000);\n\n try {\n if (fs.existsSync(tokenFile)) {\n const raw = fs.readFileSync(tokenFile, 'utf-8');\n const cached: CachedTokenData = JSON.parse(raw);\n if (\n cached.token &&\n cached.token !== '00000000000000000000000000000000000000000000000000000000' &&\n cached.expiration_time &&\n currentTime < cached.expiration_time\n ) {\n this.token = cached.token;\n return;\n }\n }\n } catch {\n // Ignore cache read failures\n }\n\n const res = await this.requestApi<any>('token.get', { user_language: 'en' });\n const statusCode = res?.message?.header?.status_code;\n\n if (statusCode === 401) {\n // Retry once after 2 seconds\n await new Promise((resolve) => setTimeout(resolve, 2000));\n return this.ensureToken();\n }\n\n const newToken = res?.message?.body?.user_token;\n if (newToken && typeof newToken === 'string') {\n this.token = newToken;\n const tokenData: CachedTokenData = {\n token: newToken,\n expiration_time: currentTime + 600, // 10 minutes expiration\n };\n try {\n fs.mkdirSync(path.dirname(tokenFile), { recursive: true });\n fs.writeFileSync(tokenFile, JSON.stringify(tokenData, null, 2), 'utf-8');\n } catch {\n // Cache write is non-fatal\n }\n }\n }\n\n public async getLrcById(trackId: string | number): Promise<ILyrics | null> {\n const subtitleRes = await this.requestApi<any>('track.subtitle.get', {\n track_id: trackId,\n subtitle_format: 'lrc',\n });\n\n let translationList: any[] = [];\n if (this.lang) {\n const translationRes = await this.requestApi<any>('crowd.track.translations.get', {\n track_id: trackId,\n subtitle_format: 'lrc',\n translation_fields_set: 'minimal',\n selected_language: this.lang,\n });\n translationList = translationRes?.message?.body?.translations_list || [];\n if (translationList.length === 0) {\n this.logger.debug(`Could not find translations in ${this.lang} for track ${trackId}`);\n }\n }\n\n const body = subtitleRes?.message?.body;\n let lrcStr = body?.subtitle?.subtitle_body;\n if (!lrcStr || typeof lrcStr !== 'string') {\n return null;\n }\n\n if (this.lang && translationList.length > 0) {\n for (const item of translationList) {\n const org = item?.translation?.subtitle_matched_line;\n const tr = item?.translation?.description;\n if (org && tr) {\n lrcStr = lrcStr.replace(org, `${org}\\n(${tr})`);\n }\n }\n }\n\n const lyrics = new Lyrics();\n lyrics.synced = lrcStr;\n return lyrics;\n }\n\n public async getLrcWordByWord(trackId: string | number): Promise<ILyrics | null> {\n const res = await this.requestApi<any>('track.richsync.get', {\n track_id: trackId,\n });\n\n if (res?.message?.header?.status_code !== 200) {\n return null;\n }\n\n const rawRichsync = res?.message?.body?.richsync?.richsync_body;\n if (!rawRichsync) return null;\n\n try {\n const items = typeof rawRichsync === 'string' ? JSON.parse(rawRichsync) : rawRichsync;\n let lrcStr = '';\n\n for (const item of items) {\n const baseTs = parseFloat(item.ts);\n lrcStr += `[${formatTime(baseTs)}] `;\n if (Array.isArray(item.l)) {\n for (const line of item.l) {\n const wordOffset = parseFloat(line.o);\n const wordTime = formatTime(baseTs + wordOffset);\n lrcStr += `<${wordTime}> ${line.c} `;\n }\n }\n lrcStr += '\\n';\n }\n\n if (lrcStr.trim()) {\n const lyrics = new Lyrics();\n lyrics.synced = lrcStr;\n return lyrics;\n }\n } catch (err: any) {\n this.logger.debug('Failed to parse Musixmatch word-by-word JSON:', err?.message ?? err);\n }\n\n return null;\n }\n\n public async getLrc(searchTerm: string): Promise<ILyrics | null> {\n const res = await this.requestApi<any>('track.search', {\n q: searchTerm,\n page_size: 5,\n page: 1,\n });\n\n const statusCode = res?.message?.header?.status_code;\n if (statusCode !== 200) {\n this.logger.debug(`Musixmatch track.search returned status code ${statusCode}`);\n return null;\n }\n\n const tracks = res?.message?.body?.track_list;\n if (!tracks || !Array.isArray(tracks) || tracks.length === 0) {\n return null;\n }\n\n const match = getBestMatch(\n tracks,\n searchTerm,\n (t) => `${t.track?.track_name} ${t.track?.artist_name}`\n );\n\n if (!match?.track?.track_id) {\n return null;\n }\n\n const trackId = match.track.track_id;\n\n if (this.enhanced) {\n const enhancedLyrics = await this.getLrcWordByWord(trackId);\n if (enhancedLyrics && enhancedLyrics.synced) {\n return enhancedLyrics;\n }\n }\n\n return this.getLrcById(trackId);\n }\n}\n\n","import { LRCProvider } from './base.js';\nimport { Lyrics } from '../lyrics.js';\nimport { getBestMatch } from '../utils.js';\nimport type { ILyrics, ProviderOptions } from '../types.js';\n\ninterface NetEaseArtist {\n id: number;\n name: string;\n}\n\ninterface NetEaseSong {\n id: number;\n name: string;\n artists?: NetEaseArtist[];\n}\n\nexport class NetEase extends LRCProvider {\n public readonly name = 'NetEase';\n\n public static readonly API_ENDPOINT_METADATA = 'https://music.163.com/api/search/pc';\n public static readonly API_ENDPOINT_LYRICS = 'https://music.163.com/api/song/lyric';\n\n constructor(options: ProviderOptions = {}) {\n super(options);\n this.session.defaults.headers.common.Cookie =\n 'NMTID=00OAVK3xqDG726ITU6jopU6jF2yMk0AAAGCO8l1BA; JSESSIONID-WYYY=8KQo11YK2GZP45RMlz8Kn80vHZ9%2FGvwzRKQXXy0iQoFKycWdBlQjbfT0MJrFa6hwRfmpfBYKeHliUPH287JC3hNW99WQjrh9b9RmKT%2Fg1Exc2VwHZcsqi7ITxQgfEiee50po28x5xTTZXKoP%2FRMctN2jpDeg57kdZrXz%2FD%2FWghb%5C4DuZ%3A1659124633932; _iuqxldmzr_=32; _ntes_nnid=0db6667097883aa9596ecfe7f188c3ec,1659122833973; _ntes_nuid=0db6667097883aa9596ecfe7f188c3ec;';\n this.session.defaults.headers.common.Referer = 'https://music.163.com/';\n }\n\n public async searchTrack(searchTerm: string): Promise<NetEaseSong | null> {\n const data = await this.get<any>(NetEase.API_ENDPOINT_METADATA, {\n params: {\n limit: 10,\n type: 1,\n offset: 0,\n s: searchTerm,\n },\n });\n\n const songs: NetEaseSong[] = data?.result?.songs;\n if (!songs || !Array.isArray(songs) || songs.length === 0) {\n return null;\n }\n\n const match = getBestMatch(\n songs,\n searchTerm,\n (t) => `${t.name} ${t.artists?.[0]?.name ?? ''}`\n );\n\n return match || songs[0];\n }\n\n public async getLrcById(trackId: string | number): Promise<ILyrics | null> {\n const data = await this.get<any>(NetEase.API_ENDPOINT_LYRICS, {\n params: {\n id: trackId,\n lv: 1,\n },\n });\n\n const lyricText = data?.lrc?.lyric;\n if (!lyricText || typeof lyricText !== 'string') {\n return null;\n }\n\n const lyrics = new Lyrics();\n lyrics.addUnknown(lyricText);\n return lyrics;\n }\n\n public async getLrc(searchTerm: string): Promise<ILyrics | null> {\n const track = await this.searchTrack(searchTerm);\n if (!track?.id) return null;\n return this.getLrcById(track.id);\n }\n}\n\n","import * as cheerio from 'cheerio';\nimport { LRCProvider } from './base.js';\nimport { Lyrics } from '../lyrics.js';\nimport type { ILyrics, ProviderOptions } from '../types.js';\n\nexport class Genius extends LRCProvider {\n public readonly name = 'Genius';\n\n public static readonly SEARCH_ENDPOINT = 'https://genius.com/api/search/multi';\n\n constructor(options: ProviderOptions = {}) {\n super(options);\n this.session.defaults.headers.common.Cookie = 'obuid=e3ee67e0-7df9-4181-8324-d977c6dc9250;';\n this.session.defaults.headers.common.Referer = 'https://genius.com/';\n }\n\n public async getLrc(searchTerm: string): Promise<ILyrics | null> {\n try {\n const data = await this.get<any>(Genius.SEARCH_ENDPOINT, {\n params: {\n per_page: 5,\n q: searchTerm,\n },\n });\n\n const sections = data?.response?.sections;\n if (!sections || !Array.isArray(sections)) return null;\n\n // Section 1 is usually the song hits\n const songSection = sections.find((s: any) => s.type === 'song') || sections[1] || sections[0];\n const hits = songSection?.hits;\n if (!hits || !Array.isArray(hits) || hits.length === 0) return null;\n\n const songUrl = hits[0]?.result?.url;\n if (!songUrl) return null;\n\n const pageHtml = await this.get<string>(songUrl, {\n responseType: 'text' as any,\n });\n\n if (!pageHtml || typeof pageHtml !== 'string') return null;\n\n const $ = cheerio.load(pageHtml);\n const containers = $('div[data-lyrics-container=\"true\"]');\n if (containers.length === 0) return null;\n\n let lrcStr = '';\n containers.each((_, el) => {\n // Replace <br> with newlines\n $(el).find('br').replaceWith('\\n');\n const text = $(el).text().trim();\n if (text) {\n lrcStr += `${text}\\n\\n`;\n }\n });\n\n const cleaned = lrcStr.trim();\n if (!cleaned) return null;\n\n const lyrics = new Lyrics();\n lyrics.unsynced = cleaned;\n return lyrics;\n } catch (err: any) {\n this.logger.debug('Genius provider error:', err?.message ?? err);\n return null;\n }\n }\n}\n\n","import { LRCProvider } from './base.js';\nimport { Lyrics } from '../lyrics.js';\nimport { getBestMatch } from '../utils.js';\nimport type { ILyrics, ProviderOptions } from '../types.js';\n\nexport class Deezer extends LRCProvider {\n public readonly name = 'Deezer';\n\n public static readonly SEARCH_ENDPOINT = 'https://api.deezer.com/search';\n public static readonly API_ENDPOINT = 'https://www.deezer.com/ajax/gw-light.php';\n private token = 'null';\n\n constructor(options: ProviderOptions = {}) {\n super(options);\n }\n\n private async initializeToken(): Promise<void> {\n if (this.token !== 'null') return;\n try {\n const data = await this.apiCall('deezer.getUserData');\n const checkForm = data?.results?.checkForm;\n if (checkForm) {\n this.token = checkForm;\n }\n } catch {\n // Ignore token initialization failures\n }\n }\n\n private async apiCall(method: string, jsonData?: any): Promise<any> {\n return this.post(\n Deezer.API_ENDPOINT,\n jsonData,\n {\n params: {\n api_version: '1.0',\n api_token: this.token,\n input: '3',\n method,\n },\n }\n );\n }\n\n public async getLrcById(trackId: string | number): Promise<ILyrics | null> {\n await this.initializeToken();\n const res = await this.apiCall('song.getLyrics', { sng_id: trackId });\n const results = res?.results;\n if (!results) return null;\n\n const lyrics = new Lyrics();\n const syncJson = results.LYRICS_SYNC_JSON;\n\n if (!syncJson || !Array.isArray(syncJson)) {\n lyrics.unsynced = results.LYRICS_TEXT || null;\n return lyrics.unsynced ? lyrics : null;\n }\n\n let lrcStr = '';\n for (const chunk of syncJson) {\n if (chunk?.lrc_timestamp && chunk?.line) {\n lrcStr += `${chunk.lrc_timestamp} ${chunk.line}\\n`;\n }\n }\n\n lyrics.synced = lrcStr.trim() ? lrcStr : null;\n return lyrics;\n }\n\n public async getLrc(searchTerm: string): Promise<ILyrics | null> {\n const data = await this.get<any>(Deezer.SEARCH_ENDPOINT, {\n params: { q: searchTerm },\n });\n\n const tracks = data?.data;\n if (!tracks || !Array.isArray(tracks) || tracks.length === 0) {\n return null;\n }\n\n const match = getBestMatch(\n tracks,\n searchTerm,\n (t) => `${t.title} ${t.artist?.name ?? ''}`\n );\n\n if (!match?.id) return null;\n return this.getLrcById(match.id);\n }\n}\n\n","import * as cheerio from 'cheerio';\nimport { LRCProvider } from './base.js';\nimport { Lyrics } from '../lyrics.js';\nimport { getBestMatch } from '../utils.js';\nimport type { ILyrics, ProviderOptions } from '../types.js';\n\nexport class Lyricsify extends LRCProvider {\n public readonly name = 'Lyricsify';\n\n public static readonly ROOT_URL = 'https://www.lyricsify.com';\n public static readonly SEARCH_ENDPOINT = `${Lyricsify.ROOT_URL}/search`;\n\n constructor(options: ProviderOptions = {}) {\n super(options);\n }\n\n public async getLrc(searchTerm: string): Promise<ILyrics | null> {\n try {\n const html = await this.get<string>(Lyricsify.SEARCH_ENDPOINT, {\n params: { q: searchTerm },\n responseType: 'text' as any,\n });\n\n if (!html || typeof html !== 'string') return null;\n\n const $ = cheerio.load(html);\n const links: { href: string; text: string }[] = [];\n\n $('a[href^=\"/lyric/\"]').each((_, el) => {\n const href = $(el).attr('href');\n if (href) {\n links.push({\n href,\n text: $(el).text().toLowerCase().replace(/-/g, ' ').trim(),\n });\n }\n });\n\n if (links.length === 0) return null;\n\n const match = getBestMatch(links, searchTerm, (item) => item.text, 55);\n if (!match) return null;\n\n const lrcId = match.href.split('.').pop();\n const pageHtml = await this.get<string>(`${Lyricsify.ROOT_URL}${match.href}`, {\n responseType: 'text' as any,\n });\n\n if (!pageHtml) return null;\n\n const page$ = cheerio.load(pageHtml);\n const lrcText = page$(`div#lyrics_${lrcId}_details`).text();\n\n if (lrcText) {\n const lyrics = new Lyrics();\n lyrics.addUnknown(lrcText);\n return lyrics;\n }\n } catch (err: any) {\n this.logger.debug('Lyricsify provider error:', err?.message ?? err);\n }\n return null;\n }\n}\n\n"],"mappings":"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","names":["import_node_path","TargetType","path","os","path","fs","axios","import_node_fs","import_node_path","path","fs","cheerio","cheerio"]}
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { AxiosInstance, AxiosRequestConfig } from 'axios';
|
|
2
|
+
|
|
3
|
+
declare enum TargetType {
|
|
4
|
+
PLAINTEXT = "PLAINTEXT",
|
|
5
|
+
PREFER_SYNCED = "PREFER_SYNCED",
|
|
6
|
+
SYNCED_ONLY = "SYNCED_ONLY"
|
|
7
|
+
}
|
|
8
|
+
type LyricsType = 'synced' | 'plaintext' | 'invalid';
|
|
9
|
+
interface SearchOptions {
|
|
10
|
+
/** Only look for plain text (not synced) lyrics */
|
|
11
|
+
plainOnly?: boolean;
|
|
12
|
+
/** Only look for synced lyrics */
|
|
13
|
+
syncedOnly?: boolean;
|
|
14
|
+
/** Path to save `.lrc` lyrics (e.g. "{search_term}.lrc"). No saving if omitted */
|
|
15
|
+
savePath?: string;
|
|
16
|
+
/** List of provider names to include (e.g. ['lrclib', 'megalobiz', 'netease', 'musixmatch', 'genius']) */
|
|
17
|
+
providers?: string[];
|
|
18
|
+
/** Language code of the translation along with the lyrics (ISO 639-1 format, e.g. 'es', 'de') */
|
|
19
|
+
lang?: string;
|
|
20
|
+
/** Return word-by-word (karaoke style) synced lyrics if available */
|
|
21
|
+
enhanced?: boolean;
|
|
22
|
+
/** Custom Musixmatch user token if available */
|
|
23
|
+
musixmatchToken?: string;
|
|
24
|
+
/** Minimum similarity score between 0 and 100 (default: 65) */
|
|
25
|
+
minScore?: number;
|
|
26
|
+
/** Enable debug logging */
|
|
27
|
+
verbose?: boolean;
|
|
28
|
+
/** Request timeout in milliseconds (default: 10000) */
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
}
|
|
31
|
+
interface ProviderOptions {
|
|
32
|
+
lang?: string;
|
|
33
|
+
enhanced?: boolean;
|
|
34
|
+
musixmatchToken?: string;
|
|
35
|
+
timeoutMs?: number;
|
|
36
|
+
verbose?: boolean;
|
|
37
|
+
}
|
|
38
|
+
interface ILRCProvider {
|
|
39
|
+
name: string;
|
|
40
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
41
|
+
getLrcById?(trackId: string): Promise<ILyrics | null>;
|
|
42
|
+
}
|
|
43
|
+
interface ILyrics {
|
|
44
|
+
synced?: string | null;
|
|
45
|
+
unsynced?: string | null;
|
|
46
|
+
addUnknown(unknown?: string | null): void;
|
|
47
|
+
update(other?: ILyrics | null): void;
|
|
48
|
+
isPreferred(targetType: TargetType): boolean;
|
|
49
|
+
isAcceptable(targetType: TargetType): boolean;
|
|
50
|
+
toStr(targetType: TargetType): string | null;
|
|
51
|
+
saveLrcFile(filePath: string, targetType: TargetType): Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
declare class Lyrics implements ILyrics {
|
|
55
|
+
synced?: string | null;
|
|
56
|
+
unsynced?: string | null;
|
|
57
|
+
constructor(synced?: string | null, unsynced?: string | null);
|
|
58
|
+
addUnknown(unknown?: string | null): void;
|
|
59
|
+
update(other?: ILyrics | null): void;
|
|
60
|
+
isPreferred(targetType: TargetType): boolean;
|
|
61
|
+
isAcceptable(targetType: TargetType): boolean;
|
|
62
|
+
toStr(targetType: TargetType): string | null;
|
|
63
|
+
saveLrcFile(filePath: string, targetType: TargetType): Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
declare const R_FEAT: RegExp;
|
|
67
|
+
/**
|
|
68
|
+
* Converts synced LRC string to plain text by stripping timestamps.
|
|
69
|
+
*/
|
|
70
|
+
declare function syncedToPlaintext(syncedLyrics: string): string;
|
|
71
|
+
/**
|
|
72
|
+
* Identifies whether an LRC string contains timestamps (synced) or is plaintext.
|
|
73
|
+
*/
|
|
74
|
+
declare function identifyLyricsType(lrc: string): LyricsType;
|
|
75
|
+
/**
|
|
76
|
+
* Checks whether an LRC string has a translation included below lines.
|
|
77
|
+
*/
|
|
78
|
+
declare function hasTranslation(lrc: string): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Formats seconds into mm:ss.xx format.
|
|
81
|
+
*/
|
|
82
|
+
declare function formatTime(timeInSeconds: number): string;
|
|
83
|
+
/**
|
|
84
|
+
* Normalizes string by lowering case and stripping non-alphanumeric punctuation.
|
|
85
|
+
*/
|
|
86
|
+
declare function defaultProcess(str: string): string;
|
|
87
|
+
/**
|
|
88
|
+
* Indel distance calculation (Levenshtein with cost 1 for insert/delete, cost 2 for substitution).
|
|
89
|
+
*/
|
|
90
|
+
declare function indelDistance(s1: string, s2: string): number;
|
|
91
|
+
/**
|
|
92
|
+
* Computes the ratio score (0 - 100) between two strings using Indel distance.
|
|
93
|
+
*/
|
|
94
|
+
declare function fuzzRatio(s1: string, s2: string): number;
|
|
95
|
+
/**
|
|
96
|
+
* Calculates RapidFuzz-compatible token_set_ratio.
|
|
97
|
+
*/
|
|
98
|
+
declare function tokenSetRatio(s1: string, s2: string): number;
|
|
99
|
+
/**
|
|
100
|
+
* Returns similarity score between two strings (0 - 100), accounting for feature credits.
|
|
101
|
+
*/
|
|
102
|
+
declare function strScore(a: string, b: string): number;
|
|
103
|
+
/**
|
|
104
|
+
* Returns true if the similarity score between two strings is >= minScore.
|
|
105
|
+
*/
|
|
106
|
+
declare function strSame(a: string, b: string, minScore?: number): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Sorts results by similarity of comparison key against the search term (descending).
|
|
109
|
+
*/
|
|
110
|
+
declare function sortResults<T>(results: T[], searchTerm: string, compareKey: (item: T) => string): T[];
|
|
111
|
+
/**
|
|
112
|
+
* Returns the best match from results if its similarity score meets minScore.
|
|
113
|
+
*/
|
|
114
|
+
declare function getBestMatch<T>(results: T[], searchTerm: string, compareKey: (item: T) => string, minScore?: number): T | null;
|
|
115
|
+
/**
|
|
116
|
+
* Resolves or creates a system cache directory for the library.
|
|
117
|
+
*/
|
|
118
|
+
declare function getCachePath(libName?: string): string;
|
|
119
|
+
/**
|
|
120
|
+
* Simple logger for library and CLI output.
|
|
121
|
+
*/
|
|
122
|
+
declare class Logger {
|
|
123
|
+
private context;
|
|
124
|
+
private verbose;
|
|
125
|
+
constructor(context: string, verbose?: boolean);
|
|
126
|
+
debug(...args: unknown[]): void;
|
|
127
|
+
info(...args: unknown[]): void;
|
|
128
|
+
warn(...args: unknown[]): void;
|
|
129
|
+
error(...args: unknown[]): void;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
declare abstract class LRCProvider implements ILRCProvider {
|
|
133
|
+
abstract readonly name: string;
|
|
134
|
+
protected session: AxiosInstance;
|
|
135
|
+
protected logger: Logger;
|
|
136
|
+
protected options: ProviderOptions;
|
|
137
|
+
constructor(options?: ProviderOptions);
|
|
138
|
+
toString(): string;
|
|
139
|
+
abstract getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
140
|
+
getLrcById?(trackId: string): Promise<ILyrics | null>;
|
|
141
|
+
protected get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T | null>;
|
|
142
|
+
protected post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T | null>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
declare class Lrclib extends LRCProvider {
|
|
146
|
+
readonly name = "Lrclib";
|
|
147
|
+
static readonly ROOT_URL = "https://lrclib.net";
|
|
148
|
+
static readonly API_ENDPOINT: string;
|
|
149
|
+
static readonly SEARCH_ENDPOINT: string;
|
|
150
|
+
static readonly LRC_ENDPOINT: string;
|
|
151
|
+
constructor(options?: ProviderOptions);
|
|
152
|
+
getLrcById(trackId: string | number): Promise<ILyrics | null>;
|
|
153
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
declare class Megalobiz extends LRCProvider {
|
|
157
|
+
readonly name = "Megalobiz";
|
|
158
|
+
static readonly ROOT_URL = "https://www.megalobiz.com";
|
|
159
|
+
static readonly API_ENDPOINT = "https://api.megalobiz.com/api/public/v1";
|
|
160
|
+
constructor(options?: ProviderOptions);
|
|
161
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
162
|
+
private getViaApi;
|
|
163
|
+
private getViaWeb;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
declare class Musixmatch extends LRCProvider {
|
|
167
|
+
readonly name = "Musixmatch";
|
|
168
|
+
static readonly ROOT_URL = "https://apic-desktop.musixmatch.com/ws/1.1/";
|
|
169
|
+
private token;
|
|
170
|
+
private lang?;
|
|
171
|
+
private enhanced;
|
|
172
|
+
constructor(options?: ProviderOptions);
|
|
173
|
+
private requestApi;
|
|
174
|
+
private ensureToken;
|
|
175
|
+
getLrcById(trackId: string | number): Promise<ILyrics | null>;
|
|
176
|
+
getLrcWordByWord(trackId: string | number): Promise<ILyrics | null>;
|
|
177
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
interface NetEaseArtist {
|
|
181
|
+
id: number;
|
|
182
|
+
name: string;
|
|
183
|
+
}
|
|
184
|
+
interface NetEaseSong {
|
|
185
|
+
id: number;
|
|
186
|
+
name: string;
|
|
187
|
+
artists?: NetEaseArtist[];
|
|
188
|
+
}
|
|
189
|
+
declare class NetEase extends LRCProvider {
|
|
190
|
+
readonly name = "NetEase";
|
|
191
|
+
static readonly API_ENDPOINT_METADATA = "https://music.163.com/api/search/pc";
|
|
192
|
+
static readonly API_ENDPOINT_LYRICS = "https://music.163.com/api/song/lyric";
|
|
193
|
+
constructor(options?: ProviderOptions);
|
|
194
|
+
searchTrack(searchTerm: string): Promise<NetEaseSong | null>;
|
|
195
|
+
getLrcById(trackId: string | number): Promise<ILyrics | null>;
|
|
196
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
declare class Genius extends LRCProvider {
|
|
200
|
+
readonly name = "Genius";
|
|
201
|
+
static readonly SEARCH_ENDPOINT = "https://genius.com/api/search/multi";
|
|
202
|
+
constructor(options?: ProviderOptions);
|
|
203
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
declare class Deezer extends LRCProvider {
|
|
207
|
+
readonly name = "Deezer";
|
|
208
|
+
static readonly SEARCH_ENDPOINT = "https://api.deezer.com/search";
|
|
209
|
+
static readonly API_ENDPOINT = "https://www.deezer.com/ajax/gw-light.php";
|
|
210
|
+
private token;
|
|
211
|
+
constructor(options?: ProviderOptions);
|
|
212
|
+
private initializeToken;
|
|
213
|
+
private apiCall;
|
|
214
|
+
getLrcById(trackId: string | number): Promise<ILyrics | null>;
|
|
215
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
declare class Lyricsify extends LRCProvider {
|
|
219
|
+
readonly name = "Lyricsify";
|
|
220
|
+
static readonly ROOT_URL = "https://www.lyricsify.com";
|
|
221
|
+
static readonly SEARCH_ENDPOINT: string;
|
|
222
|
+
constructor(options?: ProviderOptions);
|
|
223
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Searches for LRC format (synchronized) lyrics for a given track.
|
|
228
|
+
*/
|
|
229
|
+
declare function search(searchTerm: string, options?: SearchOptions): Promise<string | null>;
|
|
230
|
+
declare function search(searchTerm: string, plainOnly?: boolean, syncedOnly?: boolean, savePath?: string, providers?: string[], lang?: string, enhanced?: boolean): Promise<string | null>;
|
|
231
|
+
interface SyncoCallable {
|
|
232
|
+
(searchTerm: string, options?: SearchOptions): Promise<string | null>;
|
|
233
|
+
search: typeof search;
|
|
234
|
+
Lyrics: typeof Lyrics;
|
|
235
|
+
TargetType: typeof TargetType;
|
|
236
|
+
LRCProvider: typeof LRCProvider;
|
|
237
|
+
Lrclib: typeof Lrclib;
|
|
238
|
+
Megalobiz: typeof Megalobiz;
|
|
239
|
+
Musixmatch: typeof Musixmatch;
|
|
240
|
+
NetEase: typeof NetEase;
|
|
241
|
+
Genius: typeof Genius;
|
|
242
|
+
Deezer: typeof Deezer;
|
|
243
|
+
Lyricsify: typeof Lyricsify;
|
|
244
|
+
}
|
|
245
|
+
declare const synco: SyncoCallable;
|
|
246
|
+
declare const syncedlyrics: SyncoCallable;
|
|
247
|
+
|
|
248
|
+
export { Deezer, Genius, type ILRCProvider, type ILyrics, LRCProvider, Logger, Lrclib, Lyrics, type LyricsType, Lyricsify, Megalobiz, Musixmatch, NetEase, type ProviderOptions, R_FEAT, type SearchOptions, type SyncoCallable, TargetType, synco as default, defaultProcess, formatTime, fuzzRatio, getBestMatch, getCachePath, hasTranslation, identifyLyricsType, indelDistance, search, sortResults, strSame, strScore, syncedToPlaintext, syncedlyrics, synco, tokenSetRatio };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { AxiosInstance, AxiosRequestConfig } from 'axios';
|
|
2
|
+
|
|
3
|
+
declare enum TargetType {
|
|
4
|
+
PLAINTEXT = "PLAINTEXT",
|
|
5
|
+
PREFER_SYNCED = "PREFER_SYNCED",
|
|
6
|
+
SYNCED_ONLY = "SYNCED_ONLY"
|
|
7
|
+
}
|
|
8
|
+
type LyricsType = 'synced' | 'plaintext' | 'invalid';
|
|
9
|
+
interface SearchOptions {
|
|
10
|
+
/** Only look for plain text (not synced) lyrics */
|
|
11
|
+
plainOnly?: boolean;
|
|
12
|
+
/** Only look for synced lyrics */
|
|
13
|
+
syncedOnly?: boolean;
|
|
14
|
+
/** Path to save `.lrc` lyrics (e.g. "{search_term}.lrc"). No saving if omitted */
|
|
15
|
+
savePath?: string;
|
|
16
|
+
/** List of provider names to include (e.g. ['lrclib', 'megalobiz', 'netease', 'musixmatch', 'genius']) */
|
|
17
|
+
providers?: string[];
|
|
18
|
+
/** Language code of the translation along with the lyrics (ISO 639-1 format, e.g. 'es', 'de') */
|
|
19
|
+
lang?: string;
|
|
20
|
+
/** Return word-by-word (karaoke style) synced lyrics if available */
|
|
21
|
+
enhanced?: boolean;
|
|
22
|
+
/** Custom Musixmatch user token if available */
|
|
23
|
+
musixmatchToken?: string;
|
|
24
|
+
/** Minimum similarity score between 0 and 100 (default: 65) */
|
|
25
|
+
minScore?: number;
|
|
26
|
+
/** Enable debug logging */
|
|
27
|
+
verbose?: boolean;
|
|
28
|
+
/** Request timeout in milliseconds (default: 10000) */
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
}
|
|
31
|
+
interface ProviderOptions {
|
|
32
|
+
lang?: string;
|
|
33
|
+
enhanced?: boolean;
|
|
34
|
+
musixmatchToken?: string;
|
|
35
|
+
timeoutMs?: number;
|
|
36
|
+
verbose?: boolean;
|
|
37
|
+
}
|
|
38
|
+
interface ILRCProvider {
|
|
39
|
+
name: string;
|
|
40
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
41
|
+
getLrcById?(trackId: string): Promise<ILyrics | null>;
|
|
42
|
+
}
|
|
43
|
+
interface ILyrics {
|
|
44
|
+
synced?: string | null;
|
|
45
|
+
unsynced?: string | null;
|
|
46
|
+
addUnknown(unknown?: string | null): void;
|
|
47
|
+
update(other?: ILyrics | null): void;
|
|
48
|
+
isPreferred(targetType: TargetType): boolean;
|
|
49
|
+
isAcceptable(targetType: TargetType): boolean;
|
|
50
|
+
toStr(targetType: TargetType): string | null;
|
|
51
|
+
saveLrcFile(filePath: string, targetType: TargetType): Promise<void>;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
declare class Lyrics implements ILyrics {
|
|
55
|
+
synced?: string | null;
|
|
56
|
+
unsynced?: string | null;
|
|
57
|
+
constructor(synced?: string | null, unsynced?: string | null);
|
|
58
|
+
addUnknown(unknown?: string | null): void;
|
|
59
|
+
update(other?: ILyrics | null): void;
|
|
60
|
+
isPreferred(targetType: TargetType): boolean;
|
|
61
|
+
isAcceptable(targetType: TargetType): boolean;
|
|
62
|
+
toStr(targetType: TargetType): string | null;
|
|
63
|
+
saveLrcFile(filePath: string, targetType: TargetType): Promise<void>;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
declare const R_FEAT: RegExp;
|
|
67
|
+
/**
|
|
68
|
+
* Converts synced LRC string to plain text by stripping timestamps.
|
|
69
|
+
*/
|
|
70
|
+
declare function syncedToPlaintext(syncedLyrics: string): string;
|
|
71
|
+
/**
|
|
72
|
+
* Identifies whether an LRC string contains timestamps (synced) or is plaintext.
|
|
73
|
+
*/
|
|
74
|
+
declare function identifyLyricsType(lrc: string): LyricsType;
|
|
75
|
+
/**
|
|
76
|
+
* Checks whether an LRC string has a translation included below lines.
|
|
77
|
+
*/
|
|
78
|
+
declare function hasTranslation(lrc: string): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Formats seconds into mm:ss.xx format.
|
|
81
|
+
*/
|
|
82
|
+
declare function formatTime(timeInSeconds: number): string;
|
|
83
|
+
/**
|
|
84
|
+
* Normalizes string by lowering case and stripping non-alphanumeric punctuation.
|
|
85
|
+
*/
|
|
86
|
+
declare function defaultProcess(str: string): string;
|
|
87
|
+
/**
|
|
88
|
+
* Indel distance calculation (Levenshtein with cost 1 for insert/delete, cost 2 for substitution).
|
|
89
|
+
*/
|
|
90
|
+
declare function indelDistance(s1: string, s2: string): number;
|
|
91
|
+
/**
|
|
92
|
+
* Computes the ratio score (0 - 100) between two strings using Indel distance.
|
|
93
|
+
*/
|
|
94
|
+
declare function fuzzRatio(s1: string, s2: string): number;
|
|
95
|
+
/**
|
|
96
|
+
* Calculates RapidFuzz-compatible token_set_ratio.
|
|
97
|
+
*/
|
|
98
|
+
declare function tokenSetRatio(s1: string, s2: string): number;
|
|
99
|
+
/**
|
|
100
|
+
* Returns similarity score between two strings (0 - 100), accounting for feature credits.
|
|
101
|
+
*/
|
|
102
|
+
declare function strScore(a: string, b: string): number;
|
|
103
|
+
/**
|
|
104
|
+
* Returns true if the similarity score between two strings is >= minScore.
|
|
105
|
+
*/
|
|
106
|
+
declare function strSame(a: string, b: string, minScore?: number): boolean;
|
|
107
|
+
/**
|
|
108
|
+
* Sorts results by similarity of comparison key against the search term (descending).
|
|
109
|
+
*/
|
|
110
|
+
declare function sortResults<T>(results: T[], searchTerm: string, compareKey: (item: T) => string): T[];
|
|
111
|
+
/**
|
|
112
|
+
* Returns the best match from results if its similarity score meets minScore.
|
|
113
|
+
*/
|
|
114
|
+
declare function getBestMatch<T>(results: T[], searchTerm: string, compareKey: (item: T) => string, minScore?: number): T | null;
|
|
115
|
+
/**
|
|
116
|
+
* Resolves or creates a system cache directory for the library.
|
|
117
|
+
*/
|
|
118
|
+
declare function getCachePath(libName?: string): string;
|
|
119
|
+
/**
|
|
120
|
+
* Simple logger for library and CLI output.
|
|
121
|
+
*/
|
|
122
|
+
declare class Logger {
|
|
123
|
+
private context;
|
|
124
|
+
private verbose;
|
|
125
|
+
constructor(context: string, verbose?: boolean);
|
|
126
|
+
debug(...args: unknown[]): void;
|
|
127
|
+
info(...args: unknown[]): void;
|
|
128
|
+
warn(...args: unknown[]): void;
|
|
129
|
+
error(...args: unknown[]): void;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
declare abstract class LRCProvider implements ILRCProvider {
|
|
133
|
+
abstract readonly name: string;
|
|
134
|
+
protected session: AxiosInstance;
|
|
135
|
+
protected logger: Logger;
|
|
136
|
+
protected options: ProviderOptions;
|
|
137
|
+
constructor(options?: ProviderOptions);
|
|
138
|
+
toString(): string;
|
|
139
|
+
abstract getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
140
|
+
getLrcById?(trackId: string): Promise<ILyrics | null>;
|
|
141
|
+
protected get<T = any>(url: string, config?: AxiosRequestConfig): Promise<T | null>;
|
|
142
|
+
protected post<T = any>(url: string, data?: any, config?: AxiosRequestConfig): Promise<T | null>;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
declare class Lrclib extends LRCProvider {
|
|
146
|
+
readonly name = "Lrclib";
|
|
147
|
+
static readonly ROOT_URL = "https://lrclib.net";
|
|
148
|
+
static readonly API_ENDPOINT: string;
|
|
149
|
+
static readonly SEARCH_ENDPOINT: string;
|
|
150
|
+
static readonly LRC_ENDPOINT: string;
|
|
151
|
+
constructor(options?: ProviderOptions);
|
|
152
|
+
getLrcById(trackId: string | number): Promise<ILyrics | null>;
|
|
153
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
declare class Megalobiz extends LRCProvider {
|
|
157
|
+
readonly name = "Megalobiz";
|
|
158
|
+
static readonly ROOT_URL = "https://www.megalobiz.com";
|
|
159
|
+
static readonly API_ENDPOINT = "https://api.megalobiz.com/api/public/v1";
|
|
160
|
+
constructor(options?: ProviderOptions);
|
|
161
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
162
|
+
private getViaApi;
|
|
163
|
+
private getViaWeb;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
declare class Musixmatch extends LRCProvider {
|
|
167
|
+
readonly name = "Musixmatch";
|
|
168
|
+
static readonly ROOT_URL = "https://apic-desktop.musixmatch.com/ws/1.1/";
|
|
169
|
+
private token;
|
|
170
|
+
private lang?;
|
|
171
|
+
private enhanced;
|
|
172
|
+
constructor(options?: ProviderOptions);
|
|
173
|
+
private requestApi;
|
|
174
|
+
private ensureToken;
|
|
175
|
+
getLrcById(trackId: string | number): Promise<ILyrics | null>;
|
|
176
|
+
getLrcWordByWord(trackId: string | number): Promise<ILyrics | null>;
|
|
177
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
interface NetEaseArtist {
|
|
181
|
+
id: number;
|
|
182
|
+
name: string;
|
|
183
|
+
}
|
|
184
|
+
interface NetEaseSong {
|
|
185
|
+
id: number;
|
|
186
|
+
name: string;
|
|
187
|
+
artists?: NetEaseArtist[];
|
|
188
|
+
}
|
|
189
|
+
declare class NetEase extends LRCProvider {
|
|
190
|
+
readonly name = "NetEase";
|
|
191
|
+
static readonly API_ENDPOINT_METADATA = "https://music.163.com/api/search/pc";
|
|
192
|
+
static readonly API_ENDPOINT_LYRICS = "https://music.163.com/api/song/lyric";
|
|
193
|
+
constructor(options?: ProviderOptions);
|
|
194
|
+
searchTrack(searchTerm: string): Promise<NetEaseSong | null>;
|
|
195
|
+
getLrcById(trackId: string | number): Promise<ILyrics | null>;
|
|
196
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
declare class Genius extends LRCProvider {
|
|
200
|
+
readonly name = "Genius";
|
|
201
|
+
static readonly SEARCH_ENDPOINT = "https://genius.com/api/search/multi";
|
|
202
|
+
constructor(options?: ProviderOptions);
|
|
203
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
declare class Deezer extends LRCProvider {
|
|
207
|
+
readonly name = "Deezer";
|
|
208
|
+
static readonly SEARCH_ENDPOINT = "https://api.deezer.com/search";
|
|
209
|
+
static readonly API_ENDPOINT = "https://www.deezer.com/ajax/gw-light.php";
|
|
210
|
+
private token;
|
|
211
|
+
constructor(options?: ProviderOptions);
|
|
212
|
+
private initializeToken;
|
|
213
|
+
private apiCall;
|
|
214
|
+
getLrcById(trackId: string | number): Promise<ILyrics | null>;
|
|
215
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
declare class Lyricsify extends LRCProvider {
|
|
219
|
+
readonly name = "Lyricsify";
|
|
220
|
+
static readonly ROOT_URL = "https://www.lyricsify.com";
|
|
221
|
+
static readonly SEARCH_ENDPOINT: string;
|
|
222
|
+
constructor(options?: ProviderOptions);
|
|
223
|
+
getLrc(searchTerm: string): Promise<ILyrics | null>;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Searches for LRC format (synchronized) lyrics for a given track.
|
|
228
|
+
*/
|
|
229
|
+
declare function search(searchTerm: string, options?: SearchOptions): Promise<string | null>;
|
|
230
|
+
declare function search(searchTerm: string, plainOnly?: boolean, syncedOnly?: boolean, savePath?: string, providers?: string[], lang?: string, enhanced?: boolean): Promise<string | null>;
|
|
231
|
+
interface SyncoCallable {
|
|
232
|
+
(searchTerm: string, options?: SearchOptions): Promise<string | null>;
|
|
233
|
+
search: typeof search;
|
|
234
|
+
Lyrics: typeof Lyrics;
|
|
235
|
+
TargetType: typeof TargetType;
|
|
236
|
+
LRCProvider: typeof LRCProvider;
|
|
237
|
+
Lrclib: typeof Lrclib;
|
|
238
|
+
Megalobiz: typeof Megalobiz;
|
|
239
|
+
Musixmatch: typeof Musixmatch;
|
|
240
|
+
NetEase: typeof NetEase;
|
|
241
|
+
Genius: typeof Genius;
|
|
242
|
+
Deezer: typeof Deezer;
|
|
243
|
+
Lyricsify: typeof Lyricsify;
|
|
244
|
+
}
|
|
245
|
+
declare const synco: SyncoCallable;
|
|
246
|
+
declare const syncedlyrics: SyncoCallable;
|
|
247
|
+
|
|
248
|
+
export { Deezer, Genius, type ILRCProvider, type ILyrics, LRCProvider, Logger, Lrclib, Lyrics, type LyricsType, Lyricsify, Megalobiz, Musixmatch, NetEase, type ProviderOptions, R_FEAT, type SearchOptions, type SyncoCallable, TargetType, synco as default, defaultProcess, formatTime, fuzzRatio, getBestMatch, getCachePath, hasTranslation, identifyLyricsType, indelDistance, search, sortResults, strSame, strScore, syncedToPlaintext, syncedlyrics, synco, tokenSetRatio };
|