play_shorts
Stream YouTube Shorts as an auto-advancing playlist from specific channels or subscriptions, automatically loading more content as you watch.
Instructions
Play Shorts as a continuous auto-advancing playlist. Automatically fetches more as you watch. Fetch from a specific channel or from your subscribed channels.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| source | Yes | "channel" or "subscriptions" | |
| channel_url | No | Required when source is "channel". YouTube channel URL. | |
| max_channels | No | When source is "subscriptions": channels to sample (default 15) | |
| shorts_per_channel | No | When source is "subscriptions": shorts per channel (default 3) | |
| limit | No | When source is "channel": max shorts (default 15) | |
| shuffle | No | Shuffle the playback order |
Implementation Reference
- src/index.ts:383-441 (handler)Tool registration and handler implementation for 'play_shorts' which plays YouTube Shorts in mpv, supports channel or subscription sources, and includes auto-refill for channel sources.
server.tool( 'play_shorts', 'Play Shorts as a continuous auto-advancing playlist. Automatically fetches more as you watch. Fetch from a specific channel or from your subscribed channels.', { source: z.enum(['channel', 'subscriptions']).describe('"channel" or "subscriptions"'), channel_url: z.string().url().optional().describe('Required when source is "channel". YouTube channel URL.'), max_channels: z.number().min(1).max(50).default(15).describe('When source is "subscriptions": channels to sample (default 15)'), shorts_per_channel: z.number().min(1).max(10).default(3).describe('When source is "subscriptions": shorts per channel (default 3)'), limit: z.number().min(1).max(50).default(15).describe('When source is "channel": max shorts (default 15)'), shuffle: z.boolean().default(false).describe('Shuffle the playback order'), }, async ({ source, channel_url, max_channels, shorts_per_channel, limit, shuffle }) => { const depErr = checkDeps(); if (depErr) return errorResult(depErr); if (source === 'channel') { if (!channel_url) return errorResult('channel_url is required when source is "channel".'); const urlErr = validateYouTubeUrl(channel_url); if (urlErr) return errorResult(urlErr); } let urls: string[]; try { if (source === 'channel') { const result = await fetchFeed(`${stripChannelSuffix(channel_url!)}/shorts`, limit); urls = (result.entries || []).map((e) => e.url as string).filter(Boolean); } else { const sub = await fetchSubscriptionShortUrls(max_channels, shorts_per_channel); urls = sub.urls; } } catch (err) { return errorResult(`Error fetching shorts: ${err instanceof Error ? err.message : String(err)}`); } if (urls.length === 0) return textResult('No shorts found.'); const playlistFile = mpv.writeTempPlaylist(urls); try { await mpv.launch({ playlistFile, shuffle, socketTimeoutMs: 15_000 }); } catch { return errorResult('mpv failed to start. Run `mpv <url>` manually to see the error.'); } // Auto-refill for channel source (subscription source is harder to paginate) if (source === 'channel' && channel_url) { const shortsUrl = `${stripChannelSuffix(channel_url)}/shorts`; mpv.startAutoRefill(urls.length, async (offset, batch) => { const more = await fetchFeed(shortsUrl, batch, offset + 1); return (more.entries || []).map((e) => e.url as string).filter(Boolean); }); } let title = 'Shorts playlist'; try { title = (await mpv.getProperty('media-title')) as string || title; } catch { /* loading */ } return textResult({ status: 'playing_shorts', title, total: urls.length, shuffle, source, autoRefill: source === 'channel' }); } );