11import { parseString } from "xml2js"
22
3- import type { RSSChannel , RSSItem , RSSResult } from "../types"
3+ import type {
4+ AtomElement ,
5+ AtomResult ,
6+ RSSChannel ,
7+ RSSItem ,
8+ RSSResult ,
9+ } from "../types"
410import { isValidDate } from "../utils/date"
511
612/**
@@ -12,45 +18,101 @@ export const fetchRSS = async (xmlUrl: string | string[]) => {
1218 const urls = Array . isArray ( xmlUrl ) ? xmlUrl : [ xmlUrl ]
1319 const allItems : RSSItem [ ] [ ] = [ ]
1420 for ( const url of urls ) {
15- const rssItems = ( await fetchXml ( url ) ) as RSSResult
16- if ( ! rssItems . rss ) continue
17- const [ mainChannel ] = rssItems . rss . channel as RSSChannel [ ]
18- const [ source ] = mainChannel . title
19- const [ sourceUrl ] = mainChannel . link
20- const channelImage = mainChannel . image ? mainChannel . image [ 0 ] . url [ 0 ] : ""
21+ const response = ( await fetchXml ( url ) ) as RSSResult | AtomResult
22+ if ( " rss" in response ) {
23+ const [ mainChannel ] = response . rss . channel as RSSChannel [ ]
24+ const [ source ] = mainChannel . title
25+ const [ sourceUrl ] = mainChannel . link
26+ const channelImage = mainChannel . image ? mainChannel . image [ 0 ] . url [ 0 ] : ""
2127
22- const parsedRssItems = mainChannel . item
23- // Filter out items with invalid dates
24- . filter ( ( item ) => {
25- if ( ! item . pubDate ) return false
26- const [ pubDate ] = item . pubDate
27- return isValidDate ( pubDate )
28- } )
29- // Sort by pubDate (most recent is first in array
30- . sort ( ( a , b ) => {
31- const dateA = new Date ( a . pubDate [ 0 ] )
32- const dateB = new Date ( b . pubDate [ 0 ] )
33- return dateB . getTime ( ) - dateA . getTime ( )
34- } )
35- // Map to RSSItem object
36- . map ( ( item ) => {
37- const getImgSrc = ( ) => {
38- if ( item . enclosure ) return item . enclosure [ 0 ] . $ . url
39- if ( item [ "media:content" ] ) return item [ "media:content" ] [ 0 ] . $ . url
40- return channelImage
41- }
42- return {
43- pubDate : item . pubDate [ 0 ] ,
44- title : item . title [ 0 ] ,
45- link : item . link [ 0 ] ,
46- imgSrc : getImgSrc ( ) ,
47- source,
48- sourceUrl,
49- sourceFeedUrl : url ,
50- } as RSSItem
51- } )
28+ const parsedRssItems = mainChannel . item
29+ // Filter out items with invalid dates
30+ . filter ( ( item ) => {
31+ if ( ! item . pubDate ) return false
32+ const [ pubDate ] = item . pubDate
33+ return isValidDate ( pubDate )
34+ } )
35+ // Sort by pubDate (most recent is first in array
36+ . sort ( ( a , b ) => {
37+ const dateA = new Date ( a . pubDate [ 0 ] )
38+ const dateB = new Date ( b . pubDate [ 0 ] )
39+ return dateB . getTime ( ) - dateA . getTime ( )
40+ } )
41+ // Map to RSSItem object
42+ . map ( ( item ) => {
43+ const getImgSrc = ( ) => {
44+ if ( item . enclosure ) return item . enclosure [ 0 ] . $ . url
45+ if ( item [ "media:content" ] ) return item [ "media:content" ] [ 0 ] . $ . url
46+ return channelImage
47+ }
48+ return {
49+ pubDate : item . pubDate [ 0 ] ,
50+ title : item . title [ 0 ] ,
51+ link : item . link [ 0 ] ,
52+ imgSrc : getImgSrc ( ) ,
53+ source,
54+ sourceUrl,
55+ sourceFeedUrl : url ,
56+ } as RSSItem
57+ } )
5258
53- allItems . push ( parsedRssItems )
59+ allItems . push ( parsedRssItems )
60+ } else if ( "feed" in response ) {
61+ const [ source ] = response . feed . title
62+ const [ sourceUrl ] = response . feed . id
63+ const feedImage = response . feed . icon ?. [ 0 ]
64+
65+ const parsedAtomItems = response . feed . entry
66+ // Filter out items with invalid dates
67+ . filter ( ( entry ) => {
68+ if ( ! entry . updated ) return false
69+ const [ published ] = entry . updated
70+ return isValidDate ( published )
71+ } )
72+ // Sort by published (most recent is first in array
73+ . sort ( ( a , b ) => {
74+ const dateA = new Date ( a . updated [ 0 ] )
75+ const dateB = new Date ( b . updated [ 0 ] )
76+ return dateB . getTime ( ) - dateA . getTime ( )
77+ } )
78+ // Map to RSSItem object
79+ . map ( ( entry ) => {
80+ const getString = ( el ?: AtomElement [ ] ) : string => {
81+ if ( ! el ) return ""
82+ const [ firstEl ] = el
83+ if ( typeof firstEl === "string" ) return firstEl
84+ return firstEl . _ || ""
85+ }
86+ const getHref = ( ) : string => {
87+ if ( ! entry . link ) {
88+ console . warn ( `No link found for RSS url: ${ url } ` )
89+ return ""
90+ }
91+ const link = entry . link [ 0 ]
92+ if ( typeof link === "string" ) return link
93+ return link . $ . href || ""
94+ }
95+ const getImgSrc = ( ) : string => {
96+ const imgRegEx = / h t t p s ? : \/ \/ [ ^ " ] * ?\. ( j p e ? g | p n g | w e b p ) / g
97+ const contentMatch = getString ( entry . content ) . match ( imgRegEx )
98+ if ( contentMatch ) return contentMatch [ 0 ]
99+ const summaryMatch = getString ( entry . summary ) . match ( imgRegEx )
100+ if ( summaryMatch ) return summaryMatch [ 0 ]
101+ return feedImage || ""
102+ }
103+ return {
104+ pubDate : entry . updated [ 0 ] ,
105+ title : getString ( entry . title ) ,
106+ link : getHref ( ) ,
107+ imgSrc : getImgSrc ( ) ,
108+ source,
109+ sourceUrl,
110+ sourceFeedUrl : url ,
111+ } as RSSItem
112+ } )
113+
114+ allItems . push ( parsedAtomItems )
115+ }
54116 }
55117 return allItems as RSSItem [ ] [ ]
56118}
0 commit comments