jamp3
Version:
mp3, id3v1, id3v2 - reader & writer
116 lines (106 loc) • 3.36 kB
text/typescript
import {ID3v1Reader} from './id3v1.reader';
import {ID3v1Writer} from './id3v1.writer';
import {IID3V1} from './id3v1.types';
import {Readable} from 'stream';
import fse from 'fs-extra';
import {ITagID} from '../..';
import {updateFile} from '../common/update-file';
import {FileWriterStream} from '../common/stream-writer-file';
/**
* Class for
* - reading ID3v1
* - writing ID3v1
* - removing ID3v1
*
* Basic usage example:
*
* ```ts
* [[include:snippet_id3v1-read.ts]]
* ```
*/
export class ID3v1 {
/**
* Reads a filename & returns ID3v1 tag as Object
* @param filename the file to read
* @return a object returning i3v1 tag if found
*/
async read(filename: string): Promise<IID3V1.Tag | undefined> {
const reader = new ID3v1Reader();
return await reader.read(filename);
}
/**
* Reads a stream & returns ID3v1 tag as Object
* @param stream the stream to read (NodeJS.stream.Readable)
* @return a object returning i3v1 tag if found
*/
async readStream(stream: Readable): Promise<IID3V1.Tag | undefined> {
const reader = new ID3v1Reader();
return await reader.readStream(stream);
}
/**
* Removes ID3v1 Tag from a file with given options
* @param filename the file to read
* @param options remove options
* @return true if tag has been found and removed
*/
async remove(filename: string, options: IID3V1.RemoveOptions): Promise<boolean> {
const stat = await fse.stat(filename);
let removed = false;
await updateFile(filename, {id3v1: true}, !!options.keepBackup, () => true, async (layout, fileWriter): Promise<void> => {
let finish = stat.size;
for (const t of layout.tags) {
if (t.id === ITagID.ID3v1) {
removed = true;
if (finish > t.start) {
finish = t.start;
}
}
}
await fileWriter.copyRange(filename, 0, finish);
});
return removed;
}
/**
* Writes ID3v1 Tag from an ID3v1 object with given options
* @param filename the file to write
* @param tag the ID3v1 object to write
* @param version the ID3v1.v version to write
* @param options write options
*/
async write(filename: string, tag: IID3V1.ID3v1Tag, version: number, options: IID3V1.WriteOptions): Promise<void> {
const exists = await fse.pathExists(filename);
if (!exists) {
await this.writeTag(filename, tag, version);
} else {
await this.replaceTag(filename, tag, version, !!options.keepBackup);
}
}
private async writeTag(filename: string, tag: IID3V1.ID3v1Tag, version: number): Promise<void> {
const stream = new FileWriterStream();
await stream.open(filename);
const writer = new ID3v1Writer();
try {
await writer.write(stream, tag, version);
} catch (e: any) {
await stream.close();
return Promise.reject(e);
}
await stream.close();
}
private async replaceTag(filename: string, tag: IID3V1.ID3v1Tag, version: number, keepBackup: boolean): Promise<void> {
const stat = await fse.stat(filename);
await updateFile(filename, {id3v1: true}, keepBackup, () => true, async (layout, fileWriter): Promise<void> => {
let finish = stat.size;
for (const t of layout.tags) {
if (t.id === ITagID.ID3v1) {
if (finish > t.start) {
finish = t.start;
}
}
}
await fileWriter.copyRange(filename, 0, finish);
const writer = new ID3v1Writer();
await writer.write(fileWriter, tag, version);
});
}
}