unlike.js
1 /** 2 * YouTube unlike — remove like from a video via InnerTube like API. 3 */ 4 import { cli, Strategy } from '@jackwener/opencli/registry'; 5 import { parseVideoId, prepareYoutubeApiPage, SAPISID_HASH_FN } from './utils.js'; 6 import { CommandExecutionError, AuthRequiredError } from '@jackwener/opencli/errors'; 7 8 cli({ 9 site: 'youtube', 10 name: 'unlike', 11 description: 'Remove like from a YouTube video', 12 domain: 'www.youtube.com', 13 strategy: Strategy.COOKIE, 14 args: [ 15 { name: 'url', required: true, positional: true, help: 'YouTube video URL or video ID' }, 16 ], 17 columns: ['status', 'message'], 18 func: async (page, kwargs) => { 19 const videoId = parseVideoId(String(kwargs.url)); 20 await prepareYoutubeApiPage(page); 21 const result = await page.evaluate(` 22 (async () => { 23 ${SAPISID_HASH_FN} 24 25 const cfg = window.ytcfg?.data_ || {}; 26 const apiKey = cfg.INNERTUBE_API_KEY; 27 const context = cfg.INNERTUBE_CONTEXT; 28 if (!apiKey || !context) return { error: 'config', message: 'YouTube config not found' }; 29 30 const authHash = await getSapisidHash('https://www.youtube.com'); 31 if (!authHash) return { error: 'auth', message: 'Not logged in (SAPISID cookie missing)' }; 32 33 const resp = await fetch('/youtubei/v1/like/removelike?key=' + apiKey + '&prettyPrint=false', { 34 method: 'POST', 35 credentials: 'include', 36 headers: { 37 'Content-Type': 'application/json', 38 'Authorization': authHash, 39 'X-Origin': 'https://www.youtube.com', 40 }, 41 body: JSON.stringify({ context, target: { videoId: ${JSON.stringify(videoId)} } }), 42 }); 43 44 if (resp.status === 401 || resp.status === 403) return { error: 'auth', message: 'Not logged in' }; 45 if (!resp.ok) { 46 const body = await resp.json().catch(() => ({})); 47 const errStatus = body?.error?.status || ''; 48 if (errStatus === 'UNAUTHENTICATED') return { error: 'auth', message: 'Not logged in' }; 49 return { error: 'http', message: 'HTTP ' + resp.status + (errStatus ? ' ' + errStatus : '') }; 50 } 51 return { ok: true }; 52 })() 53 `); 54 if (result?.error === 'auth') { 55 throw new AuthRequiredError('www.youtube.com'); 56 } 57 if (result?.error) { 58 throw new CommandExecutionError(result.message || 'Failed to remove like'); 59 } 60 return [{ status: 'success', message: 'Unliked: ' + videoId }]; 61 }, 62 });