YouTube Channels & Captions
xnews follows YouTube channels through their public per-channel Atom feeds, which are free and need no Google account or API key, and reads transcripts from a video's caption tracks.
Subscribe to channels
ts
import { fetchYoutubeChannelVideos, fetchYoutubeSubscriptions } from "@xbbg/xnews";
const subs = await fetchYoutubeSubscriptions(["@CNBCtelevision", "@YahooFinance"], {
hideShorts: true, // read each channel's long-form uploads playlist instead
limit: 50, // caps the merged list, not each channel
});
console.log(subs.items.length); // merged videos, newest first, kind "video"
console.log(subs.partial); // true when any channel failed
for (const channel of subs.channels) {
console.log(channel.channel, channel.channelId, channel.items.length, channel.error ?? "");
}
const one = await fetchYoutubeChannelVideos("UCrp_UI8XtuYfpiqluWLD7Lw", { limit: 5 });
console.log(one.map((video) => `${video.publishedAt} ${video.title}`));- Channel references. Pass
UC…channel ids, HTTPSyoutube.comchannel URLs, or@handles. Handles and URLs are resolved to canonical ids by reading the channel page once;UC…ids need no lookup. - Feed depth. Each feed carries only the 15 or so most recent uploads. Poll on an interval and keep your own history if you need more.
- Independent failures. One bad channel never breaks the batch: its error lands in
channels[i].errorandpartialbecomestrue. YouTube's feed endpoint intermittently answers 404 for every channel during certain hours; treat that as an upstream outage and keep cached results until it recovers. - Shorts.
hideShorts: trueswaps aUC…channel id for its long-form uploads playlist, which excludes Shorts at the source. - Date windows.
sinceanduntilare enforced locally.
The youtube provider id exists in NewsProvider, but YouTube is channel-based rather than subject-based. Requesting it in a company or topic feed reports unsupported; use the functions above instead.
COMPANY_COMMENTARY_YOUTUBE_CHANNELS lists verified channels where executives speak in their own words. See Earnings Calls & Commentary.
Read a transcript
fetchYoutubeTranscript accepts a video URL or id, for example any item from a subscription feed:
ts
import { fetchYoutubeSubscriptions, fetchYoutubeTranscript } from "@xbbg/xnews";
const subs = await fetchYoutubeSubscriptions(["@CNBCtelevision"], { hideShorts: true, limit: 5 });
const video = subs.items[0];
if (video) {
const transcript = await fetchYoutubeTranscript(video.url, { languages: ["en"] });
console.log(transcript.videoId, transcript.languageCode, transcript.generated);
console.log(transcript.segments[0]); // { text, startMs, durationMs }
console.log(transcript.text.slice(0, 500)); // the whole transcript as one string
}How it works:
- Caption tracks are listed through YouTube's player API, with the watch page as a fallback, and both timed-text formats (
srv1andsrv3) are parsed. - Track choice: an exact language match first, then the same base language (
enmatchesen-US), manual captions over auto-generated ones, then any available track.generatedtells you whether you got automatic captions. - No captions: the call throws when the video advertises no caption tracks or the caption payload is gated. Use Realtime Transcription to transcribe the audio instead.
- Caption URL safety: the caption URL comes from YouTube's response, so before following it xnews requires HTTPS, no embedded credentials, and a host under
youtube.com,youtube-nocookie.com, orgooglevideo.com. Anything else is rejected (this check shipped in 0.2.1).
Lower-level helpers
| Function | Purpose |
|---|---|
resolveYoutubeChannelId(channel) | Resolve a handle or channel URL to a UC… id |
isYoutubeChannelId(value) | Check whether a string is already a channel id |
extractYoutubeVideoId(video) | Get the 11-character id from a bare id, a watch, Shorts, embed, or live URL, or a youtu.be link |
youtubeChannelFeedUrl(channelId, { hideShorts }) | The Atom feed URL (network-free, in /catalog) |
parseYoutubeChannelVideos(xml) | Parse feed XML you fetched yourself (in /parsers) |
parseYoutubeCaptionTracks, pickYoutubeCaptionTrack, parseYoutubeTranscriptSegments | The caption pipeline, step by step |
Related
- Realtime Transcription: audio transcription with Moonshine or OpenRouter.
- Watchlists & Watchers: persisting seen ids for your own polling loop.
