subscriptions.js
1 /** 2 * YouTube subscriptions — list of subscribed channels from /feed/channels. 3 */ 4 import { cli, Strategy } from '@jackwener/opencli/registry'; 5 import { CommandExecutionError, EmptyResultError } from '@jackwener/opencli/errors'; 6 import { extractSubscriptionChannel } from './utils.js'; 7 8 cli({ 9 site: 'youtube', 10 name: 'subscriptions', 11 description: 'List subscribed YouTube channels', 12 domain: 'www.youtube.com', 13 strategy: Strategy.COOKIE, 14 args: [ 15 { name: 'limit', type: 'int', default: 50, help: 'Max channels to return (default 50)' }, 16 ], 17 columns: ['rank', 'name', 'handle', 'subscribers', 'url'], 18 func: async (page, kwargs) => { 19 const limit = Math.min(kwargs.limit || 50, 1000); 20 await page.goto('https://www.youtube.com/feed/channels'); 21 await page.wait(3); 22 const data = await page.evaluate(` 23 (async () => { 24 const d = window.ytInitialData; 25 if (!d) return { error: 'YouTube data not found — are you logged in?' }; 26 27 const limit = ${limit}; 28 29 const items = d.contents?.twoColumnBrowseResultsRenderer 30 ?.tabs?.[0]?.tabRenderer?.content 31 ?.sectionListRenderer?.contents?.[0] 32 ?.itemSectionRenderer?.contents?.[0] 33 ?.shelfRenderer?.content 34 ?.expandedShelfContentsRenderer?.items || []; 35 36 const extractChannel = ${extractSubscriptionChannel.toString()}; 37 38 const channels = []; 39 for (const item of items) { 40 if (channels.length >= limit) break; 41 const ch = extractChannel(item.channelRenderer); 42 if (ch?.name) channels.push(ch); 43 } 44 45 return channels; 46 })() 47 `); 48 if (!Array.isArray(data)) { 49 const errMsg = data && typeof data === 'object' ? String(data.error || '') : ''; 50 throw new CommandExecutionError(errMsg || 'Failed to fetch subscriptions — make sure you are logged into YouTube'); 51 } 52 if (data.length === 0) { 53 throw new EmptyResultError('youtube subscriptions'); 54 } 55 return data.map((ch, i) => ({ rank: i + 1, ...ch })); 56 }, 57 });