/ clis / youtube / subscriptions.js
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  });