@@ -96,7 +96,7 @@ function parseRss(feedUrl: string, rss: Record<string, unknown>): ValidationResu
9696 title : title || "Untitled RSS Feed" ,
9797 description : stringValue ( channel . description ) ,
9898 homepageUrl : linkValue ( channel . link ) ,
99- kind : detectRssKind ( channel ) ,
99+ kind : detectRssKind ( channel , items ) ,
100100 language : stringValue ( channel . language ) ,
101101 imageUrl : imageValue ( channel . image ) ,
102102 lastPublishedAt : newestDate ( [ stringValue ( channel . lastBuildDate ) , stringValue ( channel . pubDate ) , ...sampleItems . map ( ( item ) => item . publishedAt ) ] ) ,
@@ -132,13 +132,30 @@ function parseAtom(feedUrl: string, feed: Record<string, unknown>): ValidationRe
132132 } ;
133133}
134134
135- function detectRssKind ( channel : Record < string , unknown > ) : FeedKind {
135+ function detectRssKind ( channel : Record < string , unknown > , items : Record < string , unknown > [ ] ) : FeedKind {
136136 if ( channel . itunes || channel [ "itunes:author" ] || channel . enclosure ) {
137137 return "podcast" ;
138138 }
139+ if ( items . some ( hasPodcastEnclosure ) ) {
140+ return "podcast" ;
141+ }
139142 return "blog" ;
140143}
141144
145+ function hasPodcastEnclosure ( item : Record < string , unknown > ) {
146+ return asArray ( item . enclosure )
147+ . filter ( isRecord )
148+ . some ( ( enclosure ) => {
149+ const type = stringValue ( enclosure . type ) ?. toLowerCase ( ) ;
150+ const url = stringValue ( enclosure . url ) ;
151+ return (
152+ type ?. startsWith ( "audio/" ) ||
153+ type ?. startsWith ( "video/" ) ||
154+ / \. ( m p 3 | m 4 a | m p 4 | m 4 v | o g g | o g a | w a v | a a c | f l a c ) (?: [ ? # ] .* ) ? $ / i. test ( url ?? "" )
155+ ) ;
156+ } ) ;
157+ }
158+
142159function imageValue ( value : unknown ) {
143160 if ( isRecord ( value ) ) {
144161 return stringValue ( value . url ) ;
0 commit comments