Initial Commit
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
import { describe, expect, it } from "@suaveplan/testing/runner";
|
||||
import { parseId3v1 } from "./id3v1.js";
|
||||
|
||||
/**
|
||||
* Build a minimal ID3v1 tag buffer.
|
||||
* Returns a 128-byte buffer starting at offset 0 (the entire buffer IS the tag).
|
||||
*/
|
||||
function buildId3v1Tag({
|
||||
title = "",
|
||||
artist = "",
|
||||
album = "",
|
||||
year = "",
|
||||
comment = "",
|
||||
track = 0,
|
||||
genreIdx = 255,
|
||||
}: {
|
||||
title?: string;
|
||||
artist?: string;
|
||||
album?: string;
|
||||
year?: string;
|
||||
comment?: string;
|
||||
track?: number;
|
||||
genreIdx?: number;
|
||||
}): Uint8Array {
|
||||
const buf = new Uint8Array(128);
|
||||
buf[0] = 0x54;
|
||||
buf[1] = 0x41;
|
||||
buf[2] = 0x47; // TAG
|
||||
|
||||
const enc = new TextEncoder();
|
||||
const writeField = (str: string, offset: number, maxLen: number) => {
|
||||
const bytes = enc.encode(str);
|
||||
const len = Math.min(bytes.length, maxLen);
|
||||
buf.set(bytes.subarray(0, len), offset);
|
||||
};
|
||||
|
||||
writeField(title, 3, 30);
|
||||
writeField(artist, 33, 30);
|
||||
writeField(album, 63, 30);
|
||||
writeField(year, 93, 4);
|
||||
|
||||
if (track > 0) {
|
||||
writeField(comment, 97, 28);
|
||||
buf[125] = 0x00;
|
||||
buf[126] = track;
|
||||
} else {
|
||||
writeField(comment, 97, 30);
|
||||
}
|
||||
|
||||
buf[127] = genreIdx;
|
||||
return buf;
|
||||
}
|
||||
|
||||
describe("id3v1 parser", () => {
|
||||
it("parses title, artist, album, year", { timeout: 5000 }, () => {
|
||||
const buf = buildId3v1Tag({
|
||||
title: "My Song",
|
||||
artist: "My Artist",
|
||||
album: "My Album",
|
||||
year: "2023",
|
||||
});
|
||||
const track = parseId3v1(buf);
|
||||
expect(track.title).toBe("My Song");
|
||||
expect(track.artist).toBe("My Artist");
|
||||
expect(track.album).toBe("My Album");
|
||||
expect(track.year).toBe(2023);
|
||||
expect(track.format).toBe("id3v1");
|
||||
});
|
||||
|
||||
it("parses genre by index", { timeout: 5000 }, () => {
|
||||
const buf = buildId3v1Tag({ genreIdx: 17 });
|
||||
const track = parseId3v1(buf);
|
||||
expect(track.genre).toBe("Rock");
|
||||
});
|
||||
|
||||
it("parses track number (ID3v1.1)", { timeout: 5000 }, () => {
|
||||
const buf = buildId3v1Tag({ comment: "Nice track", track: 5 });
|
||||
const track = parseId3v1(buf);
|
||||
expect(track.trackNumber).toBe(5);
|
||||
expect(track.comment).toBe("Nice track");
|
||||
});
|
||||
|
||||
it("parses comment without track number", { timeout: 5000 }, () => {
|
||||
const buf = buildId3v1Tag({ comment: "Full comment here" });
|
||||
const track = parseId3v1(buf);
|
||||
expect(track.comment).toBe("Full comment here");
|
||||
expect(track.trackNumber).toBeUndefined();
|
||||
});
|
||||
|
||||
it("strips trailing nulls and whitespace", { timeout: 5000 }, () => {
|
||||
const buf = buildId3v1Tag({ title: "Song " });
|
||||
const track = parseId3v1(buf);
|
||||
expect(track.title).toBe("Song");
|
||||
});
|
||||
|
||||
it("returns empty track when buffer too short", { timeout: 5000 }, () => {
|
||||
const track = parseId3v1(new Uint8Array(64));
|
||||
expect(track).toEqual({});
|
||||
});
|
||||
|
||||
it("returns empty track when TAG magic missing", { timeout: 5000 }, () => {
|
||||
const buf = new Uint8Array(128);
|
||||
// No TAG magic
|
||||
const track = parseId3v1(buf);
|
||||
expect(track).toEqual({});
|
||||
});
|
||||
|
||||
it("handles unknown genre index gracefully", { timeout: 5000 }, () => {
|
||||
const buf = buildId3v1Tag({ genreIdx: 200 }); // Beyond known list
|
||||
const track = parseId3v1(buf);
|
||||
expect(track.genre).toBeUndefined();
|
||||
});
|
||||
|
||||
it("returns rawTags with parsed fields", { timeout: 5000 }, () => {
|
||||
const buf = buildId3v1Tag({ title: "Test", artist: "Artist" });
|
||||
const track = parseId3v1(buf);
|
||||
expect(track.rawTags?.["TIT2"]).toBe("Test");
|
||||
expect(track.rawTags?.["TPE1"]).toBe("Artist");
|
||||
});
|
||||
|
||||
it("ignores non-numeric year string", { timeout: 5000 }, () => {
|
||||
// year = "abcd" → parseInt returns NaN → year should not be set
|
||||
const buf = buildId3v1Tag({ title: "Song", year: "abcd" });
|
||||
const track = parseId3v1(buf);
|
||||
expect(track.year).toBeUndefined();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user