Misc fixes

This commit is contained in:
Erik 2024-03-27 21:44:03 +02:00
parent 729466a031
commit a8b52e9192
4 changed files with 80 additions and 25 deletions

View File

@ -1,7 +1,17 @@
export type DownloaderResult = {
filePath: string,
type BaseResult = {
existing?: boolean,
artist: string,
song: string,
album: string,
ext: string
};
ext: string,
}
type ExistingResult = {
existing: true
} & BaseResult
type DownloadedResult = {
filePath: string
} & BaseResult
export type DownloaderResult = ExistingResult | DownloadedResult;

View File

@ -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\.)?)?(?<domain>([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<void>
{
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');

View File

@ -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');
}
}
}

View File

@ -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<DownloaderResult>
{
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;