summarizely-cli
Version:
YouTube summarizer that respects your existing subscriptions. No API keys required.
205 lines • 7.2 kB
JavaScript
;
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.VideoSummaryCache = exports.FileCache = void 0;
exports.getVideoCache = getVideoCache;
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const crypto_1 = __importDefault(require("crypto"));
const config_1 = require("./config");
class FileCache {
constructor(dir, ttl) {
const config = (0, config_1.getConfig)();
this.dir = dir || config.cache.dir;
this.ttl = ttl || config.cache.ttl;
this.ensureCacheDir();
}
ensureCacheDir() {
if (!fs_1.default.existsSync(this.dir)) {
fs_1.default.mkdirSync(this.dir, { recursive: true });
}
}
hashKey(key) {
return crypto_1.default.createHash('sha256').update(key).digest('hex');
}
getFilePath(key) {
const hash = this.hashKey(key);
const subdir = hash.substring(0, 2);
const dir = path_1.default.join(this.dir, subdir);
if (!fs_1.default.existsSync(dir)) {
fs_1.default.mkdirSync(dir, { recursive: true });
}
return path_1.default.join(dir, `${hash}.json`);
}
async get(key) {
try {
const filePath = this.getFilePath(key);
if (!fs_1.default.existsSync(filePath)) {
return null;
}
const content = fs_1.default.readFileSync(filePath, 'utf8');
const entry = JSON.parse(content);
// Check expiration
if (entry.expires && entry.expires < Date.now()) {
await this.delete(key);
return null;
}
return entry.value;
}
catch (error) {
// Cache read errors should not crash the app
console.error(`Cache read error for key ${key}:`, error);
return null;
}
}
async set(key, value, ttl) {
try {
const filePath = this.getFilePath(key);
const effectiveTtl = ttl !== undefined ? ttl : this.ttl;
const entry = {
value,
created: Date.now(),
expires: effectiveTtl > 0 ? Date.now() + effectiveTtl : undefined
};
fs_1.default.writeFileSync(filePath, JSON.stringify(entry, null, 2), 'utf8');
}
catch (error) {
// Cache write errors should not crash the app
console.error(`Cache write error for key ${key}:`, error);
}
}
async has(key) {
const value = await this.get(key);
return value !== null;
}
async delete(key) {
try {
const filePath = this.getFilePath(key);
if (fs_1.default.existsSync(filePath)) {
fs_1.default.unlinkSync(filePath);
}
}
catch (error) {
console.error(`Cache delete error for key ${key}:`, error);
}
}
async clear() {
try {
if (fs_1.default.existsSync(this.dir)) {
fs_1.default.rmSync(this.dir, { recursive: true, force: true });
this.ensureCacheDir();
}
}
catch (error) {
console.error('Cache clear error:', error);
}
}
async size() {
try {
let count = 0;
const subdirs = fs_1.default.readdirSync(this.dir);
for (const subdir of subdirs) {
const subdirPath = path_1.default.join(this.dir, subdir);
if (fs_1.default.statSync(subdirPath).isDirectory()) {
const files = fs_1.default.readdirSync(subdirPath);
count += files.filter(f => f.endsWith('.json')).length;
}
}
return count;
}
catch (error) {
console.error('Cache size error:', error);
return 0;
}
}
async cleanup() {
try {
const subdirs = fs_1.default.readdirSync(this.dir);
let cleaned = 0;
for (const subdir of subdirs) {
const subdirPath = path_1.default.join(this.dir, subdir);
if (!fs_1.default.statSync(subdirPath).isDirectory())
continue;
const files = fs_1.default.readdirSync(subdirPath);
for (const file of files) {
if (!file.endsWith('.json'))
continue;
const filePath = path_1.default.join(subdirPath, file);
try {
const content = fs_1.default.readFileSync(filePath, 'utf8');
const entry = JSON.parse(content);
if (entry.expires && entry.expires < Date.now()) {
fs_1.default.unlinkSync(filePath);
cleaned++;
}
}
catch {
// Remove corrupted cache files
fs_1.default.unlinkSync(filePath);
cleaned++;
}
}
}
if (cleaned > 0) {
console.log(`Cache cleanup: removed ${cleaned} expired entries`);
}
}
catch (error) {
console.error('Cache cleanup error:', error);
}
}
}
exports.FileCache = FileCache;
class VideoSummaryCache {
constructor(cache) {
this.cache = cache || new FileCache();
}
getCacheKey(url, provider, model) {
const parts = ['summary', url];
if (provider)
parts.push(provider);
if (model)
parts.push(model);
return parts.join(':');
}
async getSummary(url, provider, model) {
const config = (0, config_1.getConfig)();
if (!config.cache.enabled)
return null;
const key = this.getCacheKey(url, provider, model);
return await this.cache.get(key);
}
async setSummary(url, summary, provider, model, ttl) {
const config = (0, config_1.getConfig)();
if (!config.cache.enabled)
return;
const key = this.getCacheKey(url, provider, model);
await this.cache.set(key, summary, ttl);
}
async getTranscript(url) {
const config = (0, config_1.getConfig)();
if (!config.cache.enabled)
return null;
const key = `transcript:${url}`;
return await this.cache.get(key);
}
async setTranscript(url, transcript, ttl) {
const config = (0, config_1.getConfig)();
if (!config.cache.enabled)
return;
const key = `transcript:${url}`;
await this.cache.set(key, transcript, ttl);
}
}
exports.VideoSummaryCache = VideoSummaryCache;
// Singleton instance
let cacheInstance = null;
function getVideoCache() {
if (!cacheInstance) {
cacheInstance = new VideoSummaryCache();
}
return cacheInstance;
}
//# sourceMappingURL=cache.js.map