feat(route/rumble): add channel feed - #21545
Conversation
|
Successfully generated as following: http://localhost:1200/rumble/c/MikhailaPeterson - Failed ❌http://localhost:1200/rumble/c/MikhailaPeterson/noembed - Failed ❌ |
|
I tested this route locally and it returns valid results there, but the GitHub Actions fails on the request to |
Auto ReviewNo clear rule violations found in the current diff. |
|
Successfully generated as following: http://localhost:1200/rumble/c/MikhailaPeterson - Failed ❌http://localhost:1200/rumble/c/MikhailaPeterson/noembed - Failed ❌ |
|
Successfully generated as following: http://localhost:1200/rumble/c/MikhailaPeterson - Failed ❌http://localhost:1200/rumble/c/MikhailaPeterson/noembed - Failed ❌ |
|
Successfully generated as following: http://localhost:1200/rumble/c/MikhailaPeterson - Failed ❌http://localhost:1200/rumble/c/MikhailaPeterson/noembed - Failed ❌ |
|
I realize, I should also clarify. The reason for no API usage is because Rumble doesn't seem to offer a web API for listing all the videos reliably. If it comes to my attention an area where it does I'd love to rewrite this since the site does often update their formatting. I'm happy to fix it if it does in the mean time though. |
|
Successfully generated as following: http://localhost:1200/rumble/c/MikhailaPeterson - Failed ❌http://localhost:1200/rumble/c/MikhailaPeterson/noembed - Failed ❌ |
|
Successfully generated as following: http://localhost:1200/rumble/c/MikhailaPeterson - Failed ❌http://localhost:1200/rumble/c/MikhailaPeterson/noembed - Failed ❌ |
|
Successfully generated as following: http://localhost:1200/rumble/c/MikhailaPeterson - Failed ❌http://localhost:1200/rumble/c/MikhailaPeterson/noembed - Failed ❌ |
|
Successfully generated as following: http://localhost:1200/rumble/c/MikhailaPeterson - Failed ❌http://localhost:1200/rumble/c/MikhailaPeterson/noembed - Failed ❌ |
| videoElements, | ||
| (element: Element) => { | ||
| const $video = $(element); | ||
| const $link = $video.find('.videostream__link[href]').first(); |
There was a problem hiding this comment.
Provide the URL where you do found multiple occurrences of .videostream__link[href] within a single .videostream.thumbnail__grid--item[data-video-id] which makes you used first().
| const url = new URL(href, rootUrl); | ||
| url.search = ''; | ||
|
|
||
| const $title = $video.find('.thumbnail__title').first(); |
There was a problem hiding this comment.
Provide the URL where you do found multiple occurrences of .thumbnail__title within a single .videostream.thumbnail__grid--item[data-video-id] which makes you used first().
| return null; | ||
| } | ||
|
|
||
| const imageRaw = $video.find('img.thumbnail__image, .thumbnail__thumb img').first().attr('src'); |
There was a problem hiding this comment.
Do not enumerate different combination of selectors of the same element. Beside that, provide the URL where you do found multiple occurrences of img.thumbnail__image or .thumbnail__thumb img within a single .videostream.thumbnail__grid--item[data-video-id] which makes you used first().
|
|
||
| const imageRaw = $video.find('img.thumbnail__image, .thumbnail__thumb img').first().attr('src'); | ||
| const listImage = imageRaw ? new URL(imageRaw, rootUrl).href : undefined; | ||
| const pubDateRaw = $video.find('time.videostream__time[datetime]').first().attr('datetime')?.trim(); |
There was a problem hiding this comment.
Provide the URL where you do found multiple occurrences of time.videostream__time[datetime] within a single .videostream.thumbnail__grid--item[data-video-id] which makes you used first().
| } | ||
|
|
||
| function parseStructuredVideoObject($: ReturnType<typeof load>): RumbleVideoObject | undefined { | ||
| const content = $('script[type="application/ld+json"]').first().text().trim(); |
There was a problem hiding this comment.
#21545 (comment) is marked as resolved so please provide the URL where you do found multiple occurrences of script[type="application/ld+json"] which makes you used .toArray() and first().
| return; | ||
| } | ||
|
|
||
| try { |
There was a problem hiding this comment.
#21545 (comment) is marked as resolved so please provide the URL where you do found a malformed JSON in script[type="application/ld+json"] that can't be parsed.
|
|
||
| try { | ||
| const parsed = JSON.parse(content); | ||
| const entries = Array.isArray(parsed) ? parsed : [parsed]; |
There was a problem hiding this comment.
Provide the URL where you didn’t find the JSON-LD as an array which requires [parsed].
| const videoObject = parseStructuredVideoObject($); | ||
| const image = listImage || parseImage($, videoObject); | ||
| const description = renderDescription(image, parseDescription($, videoObject?.description?.trim()), videoObject?.embedUrl, includeEmbed); | ||
| const author = videoObject?.author?.name || $('.channel-header--title').first().text().trim() || undefined; |
There was a problem hiding this comment.
#21545 (comment) is marked as resolved so please provide the URL where you do found multiple occurrences of .channel-header--title which makes you used first().
|
Successfully generated as following: http://localhost:1200/rumble/c/MikhailaPeterson - Failed ❌http://localhost:1200/rumble/c/MikhailaPeterson/noembed - Failed ❌ |
|
This PR is stale because it has been opened for more than 3 weeks with no activity. Comment or this will be closed in 7 days. |
|
Going to work on this soon. Bought a house recently. Haven't had time to finish this out yet. |
|
This PR is stale because it has been opened for more than 3 weeks with no activity. Comment or this will be closed in 7 days. |
|
I'm going attempt to address this soon. |
Add a Rumble channel route so channel videos can be subscribed to through RSSHub. Use stable server-rendered selectors and normalized item URLs to keep feed entries consistent.
Add full Rumble video descriptions to channel feeds and expose an /embed route variant for readers that support iframe players. Keep the default route image-based while deriving stable embed URLs from each video page.
Match YouTube's optional embed parameter behavior so the default feed includes embedded players and any trailing path segment disables embedding. Keep full Rumble video descriptions in both output modes.
Use structured detail-page data for Rumble descriptions and fallback thumbnails while keeping the channel listing on stable server-rendered markup. Limit detail fetch concurrency so the route is less likely to trip anti-bot protections.
|
Successfully generated as following: http://localhost:1200/rumble/c/MikhailaPeterson - Failed ❌http://localhost:1200/rumble/c/MikhailaPeterson/noembed - Failed ❌ |
TonyRL
left a comment
There was a problem hiding this comment.
Please remove all redundant trim().
|
|
||
| const response = await ofetch(videosUrl, { | ||
| headers: { | ||
| 'user-agent': config.trueUA, |
There was a problem hiding this comment.
Does the site only work with this specific UA instead of RSSHub's default UA which is a randomised version of Chrome on mac?
| headers: { | ||
| 'user-agent': config.trueUA, | ||
| }, | ||
| retryStatusCodes: [403], |
There was a problem hiding this comment.
Do not override the default
Line 17 in 42c6b49
|
|
||
| const $ = load(response); | ||
|
|
||
| const title = parseChannelTitle($); |
There was a problem hiding this comment.
Inline this function instead of spliting it to a function that called only once.
| } | ||
|
|
||
| const parsed = JSON.parse(content); | ||
| return Array.isArray(parsed?.items) ? parsed.items : []; |
There was a problem hiding this comment.
Please provide the site URL where you found parsed.items is not an array which requires Array.isArray() check.
| const videoUrl = video.url || video.relative_url; | ||
| if (!videoUrl) { | ||
| return null; | ||
| } |
There was a problem hiding this comment.
Please provide the site URL where you found the embedded video data JSON does not contain url and relative_url which requires using
RSSHub/lib/routes/rumble/channel.ts
Lines 178 to 180 in 61525a7
| headers: { | ||
| 'user-agent': config.trueUA, | ||
| }, | ||
| retryStatusCodes: [403], |
There was a problem hiding this comment.
Do not override the default
Line 17 in 42c6b49
| retryStatusCodes: [403], | ||
| }); | ||
|
|
||
| const $ = load(response); |
There was a problem hiding this comment.
The video detail page requires RNSC cookie and it will provide one if the request does not have it.
| const type = parsed?.['@type']; | ||
| return type === 'VideoObject' ? (parsed as RumbleVideoObject) : undefined; |
There was a problem hiding this comment.
Are you sure these can parse the VideoObject?
| const image = videoObject?.thumbnailUrl || $('meta[property="og:image"]').attr('content')?.trim(); | ||
|
|
||
| return image ? new URL(image, rootUrl).href : undefined; |
There was a problem hiding this comment.
Does any of the videoObject?.thumbnailUrl or $('meta[property="og:image"]').attr('content') start with relative path which requires URL normalisation?
| const videoObject = parseStructuredVideoObject($); | ||
| const image = listImage || parseImage($, videoObject); | ||
| const description = renderDescription(image, parseDescription($, videoObject?.description?.trim()), videoObject?.embedUrl, includeEmbed); | ||
| const author = videoObject?.author?.name; |
There was a problem hiding this comment.
Please provide the site URL where VideoObject has author attribute.



Involved Issue / 该 PR 相关 Issue
No issue involved. This was just something I wanted and thought others might like too.
Example for the Proposed Route(s) / 路由地址示例
New RSS Route Checklist / 新 RSS 路由检查表
PuppeteerNote / 说明
This adds a Rumble route that keeps the channel listing on server-rendered markup while using structured detail-page data for fallback descriptions and thumbnails. It also adds standard
imageoutput and limits detail-page fetch concurrency to reduce anti-bot risk.