UNPKG

summarizely-cli

Version:

YouTube summarizer that respects your existing subscriptions. No API keys required.

149 lines 5.68 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.hasYtDlp = hasYtDlp; exports.getYtDlpInstallHint = getYtDlpInstallHint; exports.fetchCaptions = fetchCaptions; const fs_1 = __importDefault(require("fs")); const path_1 = __importDefault(require("path")); const child_process_1 = require("child_process"); const vtt_1 = require("./vtt"); const utils_1 = require("./utils"); const logger_1 = require("./logger"); function hasYtDlp() { const r = (0, child_process_1.spawnSync)('yt-dlp', ['--version'], { encoding: 'utf8' }); return r.status === 0; } function getYtDlpInstallHint() { const platform = process.platform; if (platform === 'darwin') return 'brew install yt-dlp'; if (platform === 'win32') return 'winget install yt-dlp # or: choco install yt-dlp'; return 'pipx install yt-dlp # or: pip install --user yt-dlp'; } function fetchCaptions(url) { // Try yt-dlp first if (hasYtDlp()) { const c = fetchWithYtDlp(url); if (c) return c; } else { (0, logger_1.logFail)('yt-dlp', 'not installed'); } // JS fallback (stub for v1; return null to signal guidance) const fb = fetchWithJsFallback(url); if (fb) return fb; return null; } function fetchWithYtDlp(url) { const tmp = (0, utils_1.tmpDir)('summarizely-'); const base = '%(id)s.%(ext)s'; // Single yt-dlp call to get both captions and metadata const subArgs = [ '--skip-download', '--write-auto-sub', '--write-info-json', // This writes metadata to a .info.json file '--sub-lang', 'en', '--sub-format', 'vtt', '-o', path_1.default.join(tmp, base), url, ]; (0, logger_1.logStart)('yt-dlp auto-sub'); const r = (0, child_process_1.spawnSync)('yt-dlp', subArgs, { encoding: 'utf8' }); if (r.status !== 0) { (0, logger_1.logFail)('yt-dlp auto-sub', `exit code: ${r.status}`); // Try manual-sub if auto-sub failed (0, logger_1.logStart)('yt-dlp manual-sub'); const r2 = (0, child_process_1.spawnSync)('yt-dlp', [ '--skip-download', '--write-sub', '--write-info-json', '--sub-lang', 'en', '--sub-format', 'vtt', '-o', path_1.default.join(tmp, base), url ], { encoding: 'utf8' }); if (r2.status !== 0) { (0, logger_1.logFail)('yt-dlp manual-sub', `exit code: ${r2.status}`); try { fs_1.default.rmSync(tmp, { recursive: true, force: true }); } catch { } return null; } (0, logger_1.logOk)('yt-dlp manual-sub'); } else { (0, logger_1.logOk)('yt-dlp auto-sub'); } // Find and parse the info.json file const jsonFiles = fs_1.default.readdirSync(tmp).filter((f) => f.endsWith('.info.json')); if (jsonFiles.length === 0) { try { fs_1.default.rmSync(tmp, { recursive: true, force: true }); } catch { } return null; } const jsonPath = path_1.default.join(tmp, jsonFiles[0]); let info; try { const jsonContent = fs_1.default.readFileSync(jsonPath, 'utf8'); info = JSON.parse(jsonContent); } catch { try { fs_1.default.rmSync(tmp, { recursive: true, force: true }); } catch { } return null; } // Extract metadata from the info.json file const videoId = info.id || (0, utils_1.youtubeIdFromUrl)(url) || 'video'; const title = info.title || 'YouTube Video'; const channel = info.channel || info.uploader || undefined; const channelId = info.channel_id || info.uploader_id || undefined; const durationSec = typeof info.duration === 'number' ? info.duration : undefined; const viewCount = typeof info.view_count === 'number' ? info.view_count : undefined; const likeCount = typeof info.like_count === 'number' ? info.like_count : undefined; const commentCount = typeof info.comment_count === 'number' ? info.comment_count : undefined; const averageRating = typeof info.average_rating === 'number' ? info.average_rating : undefined; // upload_date from yt-dlp is YYYYMMDD; convert to ISO date (YYYY-MM-DD) when present let published; if (typeof info.upload_date === 'string' && /^(\d{8})$/.test(info.upload_date)) { const y = info.upload_date.slice(0, 4); const m = info.upload_date.slice(4, 6); const d = info.upload_date.slice(6, 8); published = `${y}-${m}-${d}`; } // Find VTT file const vtts = fs_1.default.readdirSync(tmp).filter((f) => f.endsWith('.vtt')); if (vtts.length === 0) { try { fs_1.default.rmSync(tmp, { recursive: true, force: true }); } catch { } return null; } const vttPath = path_1.default.join(tmp, vtts[0]); const vtt = fs_1.default.readFileSync(vttPath, 'utf8'); const transcript = (0, vtt_1.parseVttToTranscript)(vtt); // cleanup temp dir try { fs_1.default.rmSync(tmp, { recursive: true, force: true }); } catch { } if (!transcript.trim()) return null; return { title, videoId, url, transcript, vtt, channel, channelId, durationSec, published, viewCount, likeCount, commentCount, averageRating }; } function fetchWithJsFallback(_url) { // Placeholder for v1: require yt-dlp for reliability. return null; } //# sourceMappingURL=captions.js.map