summarizely-cli
Version:
YouTube summarizer that respects your existing subscriptions. No API keys required.
149 lines • 5.68 kB
JavaScript
"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