From a8b52e91924bcc83dbc9ab0c3d8d251a33eb2ce6 Mon Sep 17 00:00:00 2001 From: "Navy.gif" Date: Wed, 27 Mar 2024 21:44:03 +0200 Subject: [PATCH] Misc fixes --- @types/Downloader.d.ts | 18 ++++-- src/client/components/MusicLibrary.ts | 21 +++++-- src/client/components/commands/Request.ts | 2 +- src/client/components/downloaders/Spotify.ts | 64 +++++++++++++++----- 4 files changed, 80 insertions(+), 25 deletions(-) diff --git a/@types/Downloader.d.ts b/@types/Downloader.d.ts index d25cbee..45057a7 100644 --- a/@types/Downloader.d.ts +++ b/@types/Downloader.d.ts @@ -1,7 +1,17 @@ -export type DownloaderResult = { - filePath: string, +type BaseResult = { + existing?: boolean, artist: string, song: string, album: string, - ext: string -}; \ No newline at end of file + ext: string, +} + +type ExistingResult = { + existing: true +} & BaseResult + +type DownloadedResult = { + filePath: string +} & BaseResult + +export type DownloaderResult = ExistingResult | DownloadedResult; \ No newline at end of file diff --git a/src/client/components/MusicLibrary.ts b/src/client/components/MusicLibrary.ts index 71c9332..86a1d1f 100644 --- a/src/client/components/MusicLibrary.ts +++ b/src/client/components/MusicLibrary.ts @@ -11,6 +11,7 @@ import { MusicIndexEntry, MusicQuery } from '../../../@types/MusicPlayer.js'; import similarity from 'similarity'; import MusicDownloader from './MusicDownloader.js'; import MusicPlayerError from '../../errors/MusicPlayerError.js'; +import { DownloaderResult } from '../../../@types/Downloader.js'; const linkReg = /(https?:\/\/(www\.)?)?(?([a-z0-9-]{1,63}\.)?([a-z0-9-]{1,63})(\.[a-z0-9-]{2,63})(\.[a-z0-9-]{2,63})?)(\/[^()\s]*)?/iu; class MusicLibrary implements Initialisable @@ -55,7 +56,7 @@ class MusicLibrary implements Initialisable stop (): void | Promise { - this.saveIndex(); + this.#saveIndex(); } async download (keyword: string) @@ -69,7 +70,7 @@ class MusicLibrary implements Initialisable if (!domain) throw new MusicPlayerError('Invalid link'); - let result = null; + let result: DownloaderResult | null = null; if (domain.includes('spotify.com')) result = await this.#downloader.download('spotify', keyword); else @@ -78,6 +79,12 @@ class MusicLibrary implements Initialisable if (!result) return null; + if (result.existing) + { + const entry = this.#index.find((e) => e.title === result!.song); + return entry; + } + const targetDir = path.join(this.#path, result.artist); if (!fs.existsSync(targetDir)) fs.mkdirSync(targetDir); @@ -173,7 +180,7 @@ class MusicLibrary implements Initialisable const end = Date.now(); const newFiles = this.#index.size - initialSize; this.#logger.info(`Library scan took ${end - start} ms, ${this.#index.size} (${newFiles} new) files indexed`); - this.saveIndex(); + this.#saveIndex(); return newFiles; } @@ -183,6 +190,12 @@ class MusicLibrary implements Initialisable if (this.#index.has(fp) && !force) return; + if (force) + { + this.#currentId = 0; + this.#index.clear(); + } + // Expensive call const metadata = await parseFile(fp, { skipCovers: true }); const { common } = metadata; @@ -225,7 +238,7 @@ class MusicLibrary implements Initialisable this.#logger.info(`Index loaded with ${this.#index.size} entries`); } - saveIndex () + #saveIndex () { this.#logger.info('Saving index to file'); const cachePath = path.join(process.cwd(), 'cache'); diff --git a/src/client/components/commands/Request.ts b/src/client/components/commands/Request.ts index 2de46e4..2b7b2de 100644 --- a/src/client/components/commands/Request.ts +++ b/src/client/components/commands/Request.ts @@ -38,7 +38,7 @@ class RequestCommand extends Command if (err instanceof MusicPlayerError) return response.edit(`**Error while requesting song:**\n${err.message}`); this.logger.error(err as Error); - return 'Internal error, this has been logged'; + return response.edit('Internal error, this has been logged'); } } } diff --git a/src/client/components/downloaders/Spotify.ts b/src/client/components/downloaders/Spotify.ts index 1195c8e..15e2759 100644 --- a/src/client/components/downloaders/Spotify.ts +++ b/src/client/components/downloaders/Spotify.ts @@ -5,6 +5,7 @@ import { DownloaderResult } from '../../../../@types/Downloader.js'; import Downloader from '../../../interfaces/Downloader.js'; import DiscordClient from '../../DiscordClient.js'; import path from 'node:path'; +import MusicPlayerError from '../../../errors/MusicPlayerError.js'; class SpotifyDownloader extends Downloader { @@ -25,7 +26,7 @@ class SpotifyDownloader extends Downloader '--download-real-time true', '--md-allgenres true', '--skip-previously-downloaded true', - '--output "{artist} - {song_name} - {album}.{ext}"', + '--output "{artist} --- {song_name} --- {album}.{ext}"', '--print-downloads true', ]; if (process.env.CREDENTIAL_PATH) @@ -48,6 +49,7 @@ class SpotifyDownloader extends Downloader }); }); } + override download (url: string): Promise { if (!this.#available) @@ -57,37 +59,39 @@ class SpotifyDownloader extends Downloader const match = url.match(/spotify\.com\/track\/(\w+)/u); if (!match) - throw new Error('Bad URL'); + throw new MusicPlayerError('The bot will only process track URLs'); const [ , id ] = match; if (!id) throw new Error('Could not parse ID from url'); this.logger.debug('Song ID', id); + const existing = this.#getSongData(id); + if (existing) + { + const { artist, song, ext, album } = existing; + return Promise.resolve({ + existing: true, + artist, + song, + ext, + album + }); + } + return new Promise((resolve, reject) => { childProcess.exec(`"${this.#executable}" --root-path "${this.downloadDir}" ${this.#options.join(' ')} ${url}`, (error, stdout, stderr) => { if (error) return reject(error); - const songIdsPath = path.join(this.downloadDir!, '.song_ids'); - this.logger.debug('.song_ids path', songIdsPath); - if (!fs.existsSync(songIdsPath)) - return reject(new Error('Something went wrong with the download')); - const file = fs.readFileSync(songIdsPath, { encoding: 'utf-8' }); - // console.log(file); - const lines = file.split('\n'); - const line = lines.find(ln => ln.startsWith(id))?.replace('\r', ''); - this.logger.debug('Song entry', line ?? 'undefined'); - if (!line) + const data = this.#getSongData(id); + if (!data) throw new Error('Failed to find file reference'); - const elements = line.split('\t'); - const fileName = elements[elements.length - 1]; + const { fileName, artist, song, album, ext } = data; const filePath = path.join(this.downloadDir!, fileName); - const [ name, ext ] = fileName.split('.'); - const [ artist, song, album ] = name.split('-'); if (stderr && !stderr.includes('charmap')) this.logger.debug('stderr', stderr); @@ -105,6 +109,34 @@ class SpotifyDownloader extends Downloader }); } + #getSongData (id: string) + { + const songIdsPath = path.join(this.downloadDir!, '.song_ids'); + if (!fs.existsSync(songIdsPath)) + throw new Error('Something went wrong with the download'); + + const file = fs.readFileSync(songIdsPath, { encoding: 'utf-8' }); + const lines = file.split('\n'); + const line = lines.find(ln => ln.startsWith(id))?.replace('\r', ''); + + this.logger.debug('Song entry', line ?? 'undefined'); + if (!line) + return null; + + const elements = line.split('\t'); + const fileName = elements[elements.length - 1]; + const [ name, ext ] = fileName.split('.'); + const [ artist, song, album ] = name.split('---'); + + return { + fileName, + artist, + song, + album, + ext + }; + } + override get available (): boolean { return this.#available;