feat: implement RSS feed parsing service and add library page UI for podcast management

This commit is contained in:
Michael committed 2026-04-16 13:50:10 +02:00
1 parent 277d966e50
commit ba376930b0
2 files changed
+37 -13

No files matched your search

+22 -6
View File
@@ -4,10 +4,17 @@ import { Episode } from '../models/Episode';
import { getRedis } from '../config/redis';
const parser = new Parser({
timeout: 20000, // 20 second timeout
timeout: 30000, // Increased to 30 seconds for complex feeds
headers: {
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36',
'Accept': 'application/rss+xml, application/xml, text/xml, */*'
},
customFields: {
item: [
['media:content', 'mediaContent'],
['itunes:summary', 'itunesSummary'],
['itunes:subtitle', 'itunesSubtitle'],
['itunes:duration', 'itunesDuration'],
['enclosure', 'enclosure'],
],
},
@@ -40,10 +47,10 @@ export const rssParserService = {
return {
title: feed.title,
description: feed.description || '',
author: feed.author || feed.creator || '',
description: feed.description || feed.itunes?.summary || feed.itunes?.subtitle || '',
author: feed.author || feed.creator || feed.itunes?.author || '',
imageUrl: feed.image?.url || feed.itunes?.image,
language: feed.language || 'en',
language: feed.language?.split('-')[0].toLowerCase() || '',
episodes: feed.items || [],
};
} catch (error: any) {
@@ -66,9 +73,18 @@ export const rssParserService = {
return item.mediaContent.url;
}
// Try iTunes:duration for audio detection
// Try finding mp3 in link or guid
const mp3Regex = /\.mp3($|\?)/i;
if (item.link && mp3Regex.test(item.link)) {
return item.link;
}
if (item.guid && mp3Regex.test(item.guid)) {
return item.guid;
}
// Try finding mp3 in content:encoded
if (item['content:encoded']) {
const match = item['content:encoded'].match(/href=["']([^"']*\.mp3)["']/);
const match = item['content:encoded'].match(/href=["']([^"']*\.mp3[^"']*)["']/);
if (match) return match[1];
}
+15 -7
View File
@@ -231,13 +231,21 @@ export default function Library() {
</button>
</div>
) : (
<button
onClick={() => handleUnsubscribeRequest(podcast.subscriptionId || podcast._id)}
className="mt-auto pt-3 border-t border-light-100 flex items-center gap-2 text-sm text-light-500 hover:text-red-600 transition-colors w-full"
>
<Trash2 className="w-4 h-4" />
Se désabonner
</button>
<div className="mt-auto">
{podcast.episodeCount === 0 && (
<p className="text-[10px] text-orange-500 font-medium mb-3 flex items-center gap-1">
<Loader className="w-3 h-3 animate-spin" />
Episodes en cours de synchronisation...
</p>
)}
<button
onClick={() => handleUnsubscribeRequest(podcast.subscriptionId || podcast._id)}
className="pt-3 border-t border-light-100 flex items-center gap-2 text-sm text-light-500 hover:text-red-600 transition-colors w-full"
>
<Trash2 className="w-4 h-4" />
Se désabonner
</button>
</div>
)}
</div>
))}