fix(json): serialize podcast durations as numeric seconds

Affected files:
- lib/views/json.test.ts
- lib/views/json.ts
This commit is contained in:
DIYgod
2026-10-14 15:07:46 +08:00
parent a6eb4fe8b2
commit 9672efd80d
2 changed files with 47 additions and 2 deletions
+27
View File
@@ -3,6 +3,33 @@ import { describe, expect, it } from 'vitest';
import jsonView from '@/views/json';
describe('JSON view', () => {
it.each([
[65, 65],
['65', 65],
['0:01:05', 65],
['01:02:03', 3723],
['90:00', 5400],
['0:00:01.5', 1.5],
[0, 0],
['invalid', undefined],
['0:99:00', undefined],
[-1, undefined],
[Infinity, undefined],
])('serializes podcast duration %s as numeric seconds', (duration, seconds) => {
const feed = JSON.parse(
jsonView({
title: 'Podcast',
item: [{ title: 'Episode', enclosure_url: 'https://example.com/audio.mp3', enclosure_type: 'audio/mpeg', itunes_duration: duration }],
})
);
const attachment = feed.items[0].attachments[0];
if (seconds === undefined) {
expect(attachment).not.toHaveProperty('duration_in_seconds');
} else {
expect(attachment.duration_in_seconds).toBe(seconds);
}
});
it('renders summary, authors, tags, attachments, and extras', () => {
const jsonOutput = jsonView({
title: 'JSON Feed',
+20 -2
View File
@@ -1,4 +1,22 @@
import type { Data } from '@/types';
import type { Data, DataItem } from '@/types';
const toDurationInSeconds = (duration: DataItem['itunes_duration']) => {
if (typeof duration === 'number') {
return Number.isFinite(duration) && duration >= 0 ? duration : undefined;
}
if (typeof duration !== 'string') {
return;
}
if (/^\d+(?:\.\d+)?$/.test(duration)) {
const seconds = Number(duration);
return Number.isFinite(seconds) ? seconds : undefined;
}
if (!/^\d+:(?:[0-5]?\d:)?[0-5]?\d(?:\.\d+)?$/.test(duration)) {
return;
}
const seconds = duration.split(':').reduce((total, part) => total * 60 + Number(part), 0);
return Number.isFinite(seconds) ? seconds : undefined;
};
/**
* This function should be used by RSSHub middleware only.
@@ -39,7 +57,7 @@ const json = (data: Data) => {
mime_type: item.enclosure_type,
title: item.enclosure_title,
size_in_bytes: item.enclosure_length,
duration_in_seconds: item.itunes_duration,
duration_in_seconds: toDurationInSeconds(item.itunes_duration),
},
]
: undefined),