UNPKG

teralink

Version:

A next-generation, super-performant, v4-only Lavalink manager for Node.js with advanced features, smart search, enhanced error handling, performance monitoring, and optimized resource usage.

114 lines (101 loc) 4.85 kB
const { getImageUrl } = require("../handlers/fetchImage"); class Track { constructor(data, requester, node) { this.rawData = data; this.track = data.encoded; // Helper to check for garbled (Morse code) or missing text const isGarbled = (text) => !text || /^[\s・-]+$/.test(text); // Try to get the best possible title/author let title = data.info.title; let author = data.info.author; // Try alternative fields if garbled or missing if (isGarbled(title)) { if (data.info.metadata && data.info.metadata.title) title = data.info.metadata.title; else if (data.info.album) title = data.info.album; else if (data.info.name) title = data.info.name; } if (isGarbled(author)) { if (data.info.metadata && data.info.metadata.artist) author = data.info.metadata.artist; else if (Array.isArray(data.info.artists) && data.info.artists.length > 0) author = data.info.artists.join(', '); else if (data.info.artist) author = data.info.artist; } // Debug log if still garbled if (isGarbled(title) || isGarbled(author)) { if (node && node.tera && typeof node.tera.emit === 'function') { node.tera.emit('debug', `Track metadata garbled or missing. Raw data: ${JSON.stringify(data.info)}`); } } this.info = { identifier: data.info.identifier, seekable: data.info.isSeekable, author, length: data.info.length, stream: data.info.isStream, position: data.info.position, title, uri: data.info.uri, requester, sourceName: data.info.sourceName, isrc: data.info?.isrc || null, _cachedThumbnail: data.info.thumbnail ?? null, get thumbnail() { if (data.info.thumbnail) return data.info.thumbnail; if (node.rest.version === "v4") { if (data.info.artworkUrl) { this._cachedThumbnail = data.info.artworkUrl; return data.info.artworkUrl } else { return !this._cachedThumbnail ? (this._cachedThumbnail = getImageUrl(this)) : this._cachedThumbnail ?? null } } else { return !this._cachedThumbnail ? (this._cachedThumbnail = getImageUrl(this)) : this._cachedThumbnail ?? null; } } }; } /** * Resolve this track using the Teralink instance. * @param {Teralink} tera - The Teralink instance * @returns {Promise<Track|undefined>} */ async resolve(tera) { await new Promise((res) => setTimeout(res, 0)); const escapeRegExp = (str) => str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const query = [this.info.author, this.info.title].filter((x) => !!x).join(" - "); const result = await tera.resolve({ query, source: tera.options.defaultSearchPlatform, requester: this.info.requester }); if (!result || !result.tracks.length) { return; } const officialAudio = result.tracks.find((track) => { const author = [this.info.author, `${this.info.author} - Topic`]; return author.some((name) => new RegExp(`^${escapeRegExp(name)}$`, "i").test(track.info.author)) || new RegExp(`^${escapeRegExp(this.info.title)}$`, "i").test(track.info.title); }); if (officialAudio) { this.info.identifier = officialAudio.info.identifier; this.track = officialAudio.track; return this; } if (this.info.length) { const sameDuration = result.tracks.find((track) => track.info.length >= (this.info.length ? this.info.length : 0) - 2000 && track.info.length <= (this.info.length ? this.info.length : 0) + 2000); if (sameDuration) { this.info.identifier = sameDuration.info.identifier; this.track = sameDuration.track; return this; } const sameDurationAndTitle = result.tracks.find((track) => track.info.title === this.info.title && track.info.length >= (this.info.length ? this.info.length : 0) - 2000 && track.info.length <= (this.info.length ? this.info.length : 0) + 2000); if (sameDurationAndTitle) { this.info.identifier = sameDurationAndTitle.info.identifier; this.track = sameDurationAndTitle.track; return this; } } this.info.identifier = result.tracks[0].info.identifier; this.track = result.tracks[0].track; return this; } } module.exports = { Track };