From 2a7ebfe63933a2cdfe6409e04e41b1f17fd3fd99 Mon Sep 17 00:00:00 2001 From: Satsuki Akiba Date: Sun, 8 Jun 2025 18:07:34 +0900 Subject: [PATCH] fix --- services/database.ts | 107 +++++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 35 deletions(-) diff --git a/services/database.ts b/services/database.ts index 89060aa..18f2040 100644 --- a/services/database.ts +++ b/services/database.ts @@ -107,7 +107,7 @@ function extractDomain(url: string): string | null { } // Initialize database with proper error handling -function initializeDatabase(): Database { +async function initializeDatabase(): Promise { // Ensure data directory exists if (!fs.existsSync(config.paths.dataDir)) { fs.mkdirSync(config.paths.dataDir, { recursive: true }); @@ -205,10 +205,12 @@ function initializeDatabase(): Database { // Perform database integrity checks and fixes performDatabaseIntegrityFixes(db); + await migrateFeedsWithCategories(); + return db; } -const db = initializeDatabase(); +const db = await initializeDatabase(); export interface Feed { id: string; @@ -579,13 +581,17 @@ export async function getFeedsByCategory(category?: string): Promise { try { let stmt; let rows; - + if (category) { - stmt = db.prepare("SELECT * FROM feeds WHERE category = ? AND active = 1 ORDER BY created_at DESC"); + stmt = db.prepare( + "SELECT * FROM feeds WHERE category = ? AND active = 1 ORDER BY created_at DESC", + ); rows = stmt.all(category) as any[]; } else { // If no category specified, return all active feeds - stmt = db.prepare("SELECT * FROM feeds WHERE active = 1 ORDER BY created_at DESC"); + stmt = db.prepare( + "SELECT * FROM feeds WHERE active = 1 ORDER BY created_at DESC", + ); rows = stmt.all() as any[]; } @@ -607,9 +613,11 @@ export async function getFeedsByCategory(category?: string): Promise { export async function getAllCategories(): Promise { try { - const stmt = db.prepare("SELECT DISTINCT category FROM feeds WHERE category IS NOT NULL AND active = 1 ORDER BY category"); + const stmt = db.prepare( + "SELECT DISTINCT category FROM feeds WHERE category IS NOT NULL AND active = 1 ORDER BY category", + ); const rows = stmt.all() as any[]; - + return rows.map((row) => row.category).filter(Boolean); } catch (error) { console.error("Error getting all categories:", error); @@ -617,19 +625,21 @@ export async function getAllCategories(): Promise { } } -export async function getFeedsGroupedByCategory(): Promise<{ [category: string]: Feed[] }> { +export async function getFeedsGroupedByCategory(): Promise<{ + [category: string]: Feed[]; +}> { try { const feeds = await getAllFeeds(); const grouped: { [category: string]: Feed[] } = {}; - + for (const feed of feeds) { - const category = feed.category || 'Uncategorized'; + const category = feed.category || "Uncategorized"; if (!grouped[category]) { grouped[category] = []; } grouped[category].push(feed); } - + return grouped; } catch (error) { console.error("Error getting feeds grouped by category:", error); @@ -1121,7 +1131,9 @@ export async function migrateFeedsWithCategories(): Promise { console.log("๐Ÿ”„ Starting feed category migration..."); // Get all feeds without categories - const stmt = db.prepare("SELECT * FROM feeds WHERE category IS NULL OR category = ''"); + const stmt = db.prepare( + "SELECT * FROM feeds WHERE category IS NULL OR category = ''", + ); const feedsWithoutCategories = stmt.all() as any[]; if (feedsWithoutCategories.length === 0) { @@ -1129,7 +1141,9 @@ export async function migrateFeedsWithCategories(): Promise { return; } - console.log(`๐Ÿ“‹ Found ${feedsWithoutCategories.length} feeds without categories`); + console.log( + `๐Ÿ“‹ Found ${feedsWithoutCategories.length} feeds without categories`, + ); // Import LLM service const { openAI_ClassifyFeed } = await import("./llm.js"); @@ -1141,37 +1155,48 @@ export async function migrateFeedsWithCategories(): Promise { try { // Use title for classification, fallback to URL if no title const titleForClassification = feed.title || feed.url; - + console.log(`๐Ÿ” Classifying feed: ${titleForClassification}`); - + // Classify the feed const category = await openAI_ClassifyFeed(titleForClassification); - + // Update the feed with the category - const updateStmt = db.prepare("UPDATE feeds SET category = ? WHERE id = ?"); + const updateStmt = db.prepare( + "UPDATE feeds SET category = ? WHERE id = ?", + ); updateStmt.run(category, feed.id); - - console.log(`โœ… Assigned category "${category}" to feed: ${titleForClassification}`); + + console.log( + `โœ… Assigned category "${category}" to feed: ${titleForClassification}`, + ); processedCount++; - + // Add a small delay to avoid rate limiting - await new Promise(resolve => setTimeout(resolve, 1000)); - + await new Promise((resolve) => setTimeout(resolve, 1000)); } catch (error) { - console.error(`โŒ Failed to classify feed ${feed.title || feed.url}:`, error); + console.error( + `โŒ Failed to classify feed ${feed.title || feed.url}:`, + error, + ); errorCount++; - + // Set a default category for failed classifications const defaultCategory = "ใใฎไป–"; - const updateStmt = db.prepare("UPDATE feeds SET category = ? WHERE id = ?"); + const updateStmt = db.prepare( + "UPDATE feeds SET category = ? WHERE id = ?", + ); updateStmt.run(defaultCategory, feed.id); - console.log(`โš ๏ธ Assigned default category "${defaultCategory}" to feed: ${feed.title || feed.url}`); + console.log( + `! Assigned default category "${defaultCategory}" to feed: ${feed.title || feed.url}`, + ); } } console.log(`โœ… Feed category migration completed`); - console.log(`๐Ÿ“Š Processed: ${processedCount}, Errors: ${errorCount}, Total: ${feedsWithoutCategories.length}`); - + console.log( + `๐Ÿ“Š Processed: ${processedCount}, Errors: ${errorCount}, Total: ${feedsWithoutCategories.length}`, + ); } catch (error) { console.error("โŒ Error during feed category migration:", error); throw error; @@ -1186,11 +1211,15 @@ export async function getFeedCategoryMigrationStatus(): Promise<{ migrationComplete: boolean; }> { try { - const totalStmt = db.prepare("SELECT COUNT(*) as count FROM feeds WHERE active = 1"); + const totalStmt = db.prepare( + "SELECT COUNT(*) as count FROM feeds WHERE active = 1", + ); const totalResult = totalStmt.get() as any; const totalFeeds = totalResult.count; - const withCategoriesStmt = db.prepare("SELECT COUNT(*) as count FROM feeds WHERE active = 1 AND category IS NOT NULL AND category != ''"); + const withCategoriesStmt = db.prepare( + "SELECT COUNT(*) as count FROM feeds WHERE active = 1 AND category IS NOT NULL AND category != ''", + ); const withCategoriesResult = withCategoriesStmt.get() as any; const feedsWithCategories = withCategoriesResult.count; @@ -1212,9 +1241,11 @@ export async function getFeedCategoryMigrationStatus(): Promise<{ export async function deleteEpisode(episodeId: string): Promise { try { // Get episode info first to find the audio file path - const episodeStmt = db.prepare("SELECT audio_path FROM episodes WHERE id = ?"); + const episodeStmt = db.prepare( + "SELECT audio_path FROM episodes WHERE id = ?", + ); const episode = episodeStmt.get(episodeId) as any; - + if (!episode) { return false; // Episode not found } @@ -1226,13 +1257,19 @@ export async function deleteEpisode(episodeId: string): Promise { // If database deletion successful, try to delete the audio file if (result.changes > 0 && episode.audio_path) { try { - const fullAudioPath = path.join(config.paths.projectRoot, episode.audio_path); + const fullAudioPath = path.join( + config.paths.projectRoot, + episode.audio_path, + ); if (fs.existsSync(fullAudioPath)) { fs.unlinkSync(fullAudioPath); - console.log(`๐Ÿ—‘๏ธ Deleted audio file: ${fullAudioPath}`); + console.log(`๐Ÿ—‘ Deleted audio file: ${fullAudioPath}`); } } catch (fileError) { - console.warn(`โš ๏ธ Failed to delete audio file ${episode.audio_path}:`, fileError); + console.warn( + `! Failed to delete audio file ${episode.audio_path}:`, + fileError, + ); // Don't fail the operation if file deletion fails } }