@@ -49,14 +49,14 @@ function parseChannelTitle($: ReturnType<typeof load>): string {
4949 return title || 'Rumble' ;
5050}
5151
52- function parseDescription ( $ : ReturnType < typeof load > ) : string | undefined {
52+ function parseDescription ( $ : ReturnType < typeof load > , fallback : string | undefined ) : string | undefined {
5353 const paragraphs = $ ( 'div[data-js="media_long_description_container"] > p.media-description' )
5454 . toArray ( )
5555 . map ( ( element ) => $ . html ( element ) )
5656 . filter ( Boolean )
5757 . join ( '' ) ;
5858
59- return paragraphs || $ ( 'meta[name="description"]' ) . attr ( 'content' ) ?. trim ( ) || undefined ;
59+ return paragraphs || $ ( 'meta[name="description"]' ) . attr ( 'content' ) ?. trim ( ) || fallback || undefined ;
6060}
6161
6262function parseStructuredVideoObject ( $ : ReturnType < typeof load > ) {
@@ -73,10 +73,14 @@ function parseStructuredVideoObject($: ReturnType<typeof load>) {
7373 for ( const entry of entries ) {
7474 if ( entry ?. [ '@type' ] === 'VideoObject' ) {
7575 return entry as {
76+ description ?: string ;
7677 embedUrl ?: string ;
78+ genre ?: string | string [ ] ;
79+ keywords ?: string | string [ ] ;
7780 author ?: {
7881 name ?: string ;
7982 } ;
83+ thumbnailUrl ?: string | string [ ] ;
8084 } ;
8185 }
8286 }
@@ -86,6 +90,34 @@ function parseStructuredVideoObject($: ReturnType<typeof load>) {
8690 }
8791}
8892
93+ function parseImage ( $ : ReturnType < typeof load > , videoObject : ReturnType < typeof parseStructuredVideoObject > ) {
94+ const thumbnailUrl = Array . isArray ( videoObject ?. thumbnailUrl ) ? videoObject . thumbnailUrl [ 0 ] : videoObject ?. thumbnailUrl ;
95+ const image = thumbnailUrl || $ ( 'meta[property="og:image"]' ) . attr ( 'content' ) ?. trim ( ) ;
96+
97+ return image ? new URL ( image , rootUrl ) . href : undefined ;
98+ }
99+
100+ async function mapLimit < T , R > ( values : T [ ] , limit : number , mapper : ( value : T , index : number ) => Promise < R > ) {
101+ const results = Array . from ( { length : values . length } ) as R [ ] ;
102+ let nextIndex = 0 ;
103+
104+ const worker = async ( ) : Promise < void > => {
105+ const currentIndex = nextIndex ;
106+ nextIndex += 1 ;
107+
108+ if ( currentIndex >= values . length ) {
109+ return ;
110+ }
111+
112+ results [ currentIndex ] = await mapper ( values [ currentIndex ] , currentIndex ) ;
113+ await worker ( ) ;
114+ } ;
115+
116+ await Promise . all ( Array . from ( { length : Math . min ( limit , values . length ) } , ( ) => worker ( ) ) ) ;
117+
118+ return results ;
119+ }
120+
89121function renderDescription ( image : string | undefined , description : string | undefined , embedUrl : string | undefined , includeEmbed : boolean ) : string | undefined {
90122 const parts : string [ ] = [ ] ;
91123
@@ -113,13 +145,13 @@ function fetchVideoDetails(link: string) {
113145
114146 const $ = load ( response ) ;
115147 const videoObject = parseStructuredVideoObject ( $ ) ;
116- const category = $ ( '.media-by--category a' ) . first ( ) . text ( ) . trim ( ) ;
148+ const image = parseImage ( $ , videoObject ) ;
117149
118150 return {
119151 author : videoObject ?. author ?. name || $ ( '.channel-header--title' ) . first ( ) . text ( ) . trim ( ) || undefined ,
120- category : category || undefined ,
121- description : parseDescription ( $ ) ,
152+ description : parseDescription ( $ , videoObject ?. description ?. trim ( ) ) ,
122153 embedUrl : videoObject ?. embedUrl ,
154+ image,
123155 } ;
124156 } ) ;
125157}
@@ -141,10 +173,11 @@ async function parseItemFromVideoElement($: ReturnType<typeof load>, videoElemen
141173
142174 const $img = $video . find ( 'img.thumbnail__image, .thumbnail__thumb img' ) . first ( ) ;
143175 const imageRaw = $img . attr ( 'src' ) || $img . attr ( 'data-src' ) ;
144- const image = imageRaw ? new URL ( imageRaw , rootUrl ) . href : undefined ;
176+ const listImage = imageRaw ? new URL ( imageRaw , rootUrl ) . href : undefined ;
145177 const pubDateRaw = $video . find ( 'time.videostream__time[datetime], time[datetime]' ) . first ( ) . attr ( 'datetime' ) ?. trim ( ) ;
146178 const pubDate = pubDateRaw ? parseDate ( pubDateRaw ) : undefined ;
147179 const details = await fetchVideoDetails ( url . href ) ;
180+ const image = listImage || details . image ;
148181
149182 const media = image
150183 ? {
@@ -163,7 +196,7 @@ async function parseItemFromVideoElement($: ReturnType<typeof load>, videoElemen
163196 return {
164197 title,
165198 author : details . author ,
166- category : details . category ? [ details . category ] : undefined ,
199+ image ,
167200 link : url . href ,
168201 description,
169202 itunes_item_image : image ,
@@ -189,19 +222,18 @@ async function handler(ctx) {
189222 const title = parseChannelTitle ( $ ) ;
190223
191224 const uniqueIds = new Set < string > ( ) ;
192- const items = await Promise . all (
193- $ ( '.channel-listing__container .videostream[data-video-id], .videostream.thumbnail__grid--item[data-video-id]' )
194- . toArray ( )
195- . map ( ( element ) => {
196- const videoId = $ ( element ) . attr ( 'data-video-id' ) ?. trim ( ) ;
197- if ( ! videoId || uniqueIds . has ( videoId ) ) {
198- return null ;
199- }
225+ const videoElements = $ ( '.channel-listing__container .videostream[data-video-id], .videostream.thumbnail__grid--item[data-video-id]' )
226+ . toArray ( )
227+ . filter ( ( element ) => {
228+ const videoId = $ ( element ) . attr ( 'data-video-id' ) ?. trim ( ) ;
229+ if ( ! videoId || uniqueIds . has ( videoId ) ) {
230+ return false ;
231+ }
200232
201- uniqueIds . add ( videoId ) ;
202- return parseItemFromVideoElement ( $ , element , includeEmbed ) ;
203- } )
204- ) ;
233+ uniqueIds . add ( videoId ) ;
234+ return true ;
235+ } ) ;
236+ const items = await mapLimit ( videoElements , 5 , ( element ) => parseItemFromVideoElement ( $ , element , includeEmbed ) ) ;
205237
206238 return {
207239 title : `Rumble - ${ title } ` ,
0 commit comments