mirror of
https://github.com/kikootwo/ReadMeABook.git
synced 2026-06-02 20:30:10 +00:00
Add series metadata tagging and tests
Include series and seriesPart metadata when tagging audio files. For m4b output the code uses show and episode_id; for mp3 and flac it writes SERIES and SERIES-PART. Adds unit tests verifying tag output for .m4b, .mp3, and .flac and that tags are omitted when fields are absent.
This commit is contained in:
@@ -118,6 +118,14 @@ export async function tagAudioFileMetadata(
|
|||||||
args.push('-metadata', `ASIN="${escapeMetadata(metadata.asin)}"`);
|
args.push('-metadata', `ASIN="${escapeMetadata(metadata.asin)}"`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (metadata.series) {
|
||||||
|
args.push('-metadata', `SERIES="${escapeMetadata(metadata.series)}"`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (metadata.seriesPart) {
|
||||||
|
args.push('-metadata', `SERIES-PART="${escapeMetadata(metadata.seriesPart)}"`);
|
||||||
|
}
|
||||||
|
|
||||||
// Explicitly specify output format
|
// Explicitly specify output format
|
||||||
args.push('-f', 'flac');
|
args.push('-f', 'flac');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -114,6 +114,72 @@ describe('metadata tagger', () => {
|
|||||||
await expect(checkFfmpegAvailable()).resolves.toBe(false);
|
await expect(checkFfmpegAvailable()).resolves.toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('series metadata', () => {
|
||||||
|
it('writes show/episode_id for m4b when series/seriesPart provided', async () => {
|
||||||
|
fsMock.access.mockResolvedValue(undefined);
|
||||||
|
mockExecSuccess('done');
|
||||||
|
|
||||||
|
await tagAudioFileMetadata('/tmp/book.m4b', {
|
||||||
|
title: 'Book',
|
||||||
|
author: 'Author',
|
||||||
|
series: 'The Mistborn Saga',
|
||||||
|
seriesPart: '1',
|
||||||
|
});
|
||||||
|
|
||||||
|
const command = execMock.mock.calls[0][0] as string;
|
||||||
|
expect(command).toContain('-metadata show="The Mistborn Saga"');
|
||||||
|
expect(command).toContain('-metadata episode_id="1"');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('writes SERIES/SERIES-PART for mp3 when series/seriesPart provided', async () => {
|
||||||
|
fsMock.access.mockResolvedValue(undefined);
|
||||||
|
mockExecSuccess('done');
|
||||||
|
|
||||||
|
await tagAudioFileMetadata('/tmp/book.mp3', {
|
||||||
|
title: 'Book',
|
||||||
|
author: 'Author',
|
||||||
|
series: 'The Mistborn Saga',
|
||||||
|
seriesPart: '1.5',
|
||||||
|
});
|
||||||
|
|
||||||
|
const command = execMock.mock.calls[0][0] as string;
|
||||||
|
expect(command).toContain('-metadata SERIES="The Mistborn Saga"');
|
||||||
|
expect(command).toContain('-metadata SERIES-PART="1.5"');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('writes SERIES/SERIES-PART for flac when series/seriesPart provided', async () => {
|
||||||
|
fsMock.access.mockResolvedValue(undefined);
|
||||||
|
mockExecSuccess('done');
|
||||||
|
|
||||||
|
await tagAudioFileMetadata('/tmp/book.flac', {
|
||||||
|
title: 'Book',
|
||||||
|
author: 'Author',
|
||||||
|
series: 'The Mistborn Saga',
|
||||||
|
seriesPart: '2',
|
||||||
|
});
|
||||||
|
|
||||||
|
const command = execMock.mock.calls[0][0] as string;
|
||||||
|
expect(command).toContain('-metadata SERIES="The Mistborn Saga"');
|
||||||
|
expect(command).toContain('-metadata SERIES-PART="2"');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('omits series tags when fields are absent', async () => {
|
||||||
|
fsMock.access.mockResolvedValue(undefined);
|
||||||
|
mockExecSuccess('done');
|
||||||
|
|
||||||
|
await tagAudioFileMetadata('/tmp/book.m4b', {
|
||||||
|
title: 'Book',
|
||||||
|
author: 'Author',
|
||||||
|
});
|
||||||
|
|
||||||
|
const command = execMock.mock.calls[0][0] as string;
|
||||||
|
expect(command).not.toContain('show=');
|
||||||
|
expect(command).not.toContain('episode_id=');
|
||||||
|
expect(command).not.toContain('SERIES=');
|
||||||
|
expect(command).not.toContain('SERIES-PART=');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('metadata escaping', () => {
|
describe('metadata escaping', () => {
|
||||||
it('does NOT escape single quotes (they are literal in double-quoted shell strings)', async () => {
|
it('does NOT escape single quotes (they are literal in double-quoted shell strings)', async () => {
|
||||||
fsMock.access.mockResolvedValue(undefined);
|
fsMock.access.mockResolvedValue(undefined);
|
||||||
|
|||||||
Reference in New Issue
Block a user