1
1
import { parseString } from "xml2js"
2
2
3
- import type { RSSChannel , RSSItem , RSSResult } from "../types"
3
+ import type {
4
+ AtomElement ,
5
+ AtomResult ,
6
+ RSSChannel ,
7
+ RSSItem ,
8
+ RSSResult ,
9
+ } from "../types"
4
10
import { isValidDate } from "../utils/date"
5
11
6
12
/**
@@ -12,45 +18,101 @@ export const fetchRSS = async (xmlUrl: string | string[]) => {
12
18
const urls = Array . isArray ( xmlUrl ) ? xmlUrl : [ xmlUrl ]
13
19
const allItems : RSSItem [ ] [ ] = [ ]
14
20
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 ] : ""
21
27
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
+ } )
52
58
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
+ }
54
116
}
55
117
return allItems as RSSItem [ ] [ ]
56
118
}
0 commit comments