1
1
import { parseString } from "xml2js"
2
2
3
- import type { RSSChannel , RSSItem , RSSResult } from "../types"
3
+ import type { AtomResult , RSSChannel , RSSItem , RSSResult } from "../types"
4
4
import { isValidDate } from "../utils/date"
5
5
6
6
/**
@@ -12,45 +12,102 @@ export const fetchRSS = async (xmlUrl: string | string[]) => {
12
12
const urls = Array . isArray ( xmlUrl ) ? xmlUrl : [ xmlUrl ]
13
13
const allItems : RSSItem [ ] [ ] = [ ]
14
14
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 ] : ""
15
+ const response = ( await fetchXml ( url ) ) as RSSResult | AtomResult
16
+ if ( " rss" in response ) {
17
+ const [ mainChannel ] = response . 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
21
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
- } )
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
+ } )
52
52
53
- allItems . push ( parsedRssItems )
53
+ allItems . push ( parsedRssItems )
54
+ } else if ( "feed" in response ) {
55
+ const [ source ] = response . feed . title
56
+ const [ sourceUrl ] = response . feed . id
57
+ const feedImage = response . feed . icon ?. [ 0 ]
58
+
59
+ const parsedAtomItems = response . feed . entry
60
+ // Filter out items with invalid dates
61
+ . filter ( ( entry ) => {
62
+ if ( ! entry . updated ) return false
63
+ const [ published ] = entry . updated
64
+ return isValidDate ( published )
65
+ } )
66
+ // Sort by published (most recent is first in array
67
+ . sort ( ( a , b ) => {
68
+ const dateA = new Date ( a . updated [ 0 ] )
69
+ const dateB = new Date ( b . updated [ 0 ] )
70
+ return dateB . getTime ( ) - dateA . getTime ( )
71
+ } )
72
+ // Map to RSSItem object
73
+ . map ( ( entry ) => {
74
+ const getImgSrc = ( ) : string => {
75
+ const imgRegEx = / h t t p s ? : \/ \/ [ ^ " ] * ?\. ( j p e ? g | p n g | w e b p ) / g
76
+ const content = entry . content ?. [ 0 ] ?. _ || ""
77
+ const summary = entry . summary ?. [ 0 ] ?. _ || ""
78
+ const contentMatch = content . match ( imgRegEx )
79
+ const summaryMatch = summary . match ( imgRegEx )
80
+ if ( contentMatch ) return contentMatch [ 0 ]
81
+ if ( summaryMatch ) return summaryMatch [ 0 ]
82
+ return feedImage || ""
83
+ }
84
+ const getLink = ( ) : string => {
85
+ if ( ! entry . link ) {
86
+ console . warn ( `No link found for RSS url: ${ url } ` )
87
+ return ""
88
+ }
89
+ const link = entry . link [ 0 ]
90
+ if ( typeof link === "string" ) return link
91
+ return link . $ . href || ""
92
+ }
93
+ const getTitle = ( ) : string => {
94
+ const title = entry . title [ 0 ]
95
+ if ( typeof title === "string" ) return title
96
+ return title . _ || ""
97
+ }
98
+ return {
99
+ pubDate : entry . updated [ 0 ] ,
100
+ title : getTitle ( ) ,
101
+ link : getLink ( ) ,
102
+ imgSrc : getImgSrc ( ) ,
103
+ source,
104
+ sourceUrl,
105
+ sourceFeedUrl : url ,
106
+ } as RSSItem
107
+ } )
108
+
109
+ allItems . push ( parsedAtomItems )
110
+ }
54
111
}
55
112
return allItems as RSSItem [ ] [ ]
56
113
}
0 commit comments