fix
This commit is contained in:
		@@ -107,7 +107,7 @@ function extractDomain(url: string): string | null {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Initialize database with proper error handling
 | 
					// Initialize database with proper error handling
 | 
				
			||||||
function initializeDatabase(): Database {
 | 
					async function initializeDatabase(): Promise<Database> {
 | 
				
			||||||
  // Ensure data directory exists
 | 
					  // Ensure data directory exists
 | 
				
			||||||
  if (!fs.existsSync(config.paths.dataDir)) {
 | 
					  if (!fs.existsSync(config.paths.dataDir)) {
 | 
				
			||||||
    fs.mkdirSync(config.paths.dataDir, { recursive: true });
 | 
					    fs.mkdirSync(config.paths.dataDir, { recursive: true });
 | 
				
			||||||
@@ -205,10 +205,12 @@ function initializeDatabase(): Database {
 | 
				
			|||||||
  // Perform database integrity checks and fixes
 | 
					  // Perform database integrity checks and fixes
 | 
				
			||||||
  performDatabaseIntegrityFixes(db);
 | 
					  performDatabaseIntegrityFixes(db);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  await migrateFeedsWithCategories();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return db;
 | 
					  return db;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const db = initializeDatabase();
 | 
					const db = await initializeDatabase();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export interface Feed {
 | 
					export interface Feed {
 | 
				
			||||||
  id: string;
 | 
					  id: string;
 | 
				
			||||||
@@ -579,13 +581,17 @@ export async function getFeedsByCategory(category?: string): Promise<Feed[]> {
 | 
				
			|||||||
  try {
 | 
					  try {
 | 
				
			||||||
    let stmt;
 | 
					    let stmt;
 | 
				
			||||||
    let rows;
 | 
					    let rows;
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    if (category) {
 | 
					    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[];
 | 
					      rows = stmt.all(category) as any[];
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      // If no category specified, return all active feeds
 | 
					      // 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[];
 | 
					      rows = stmt.all() as any[];
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -607,9 +613,11 @@ export async function getFeedsByCategory(category?: string): Promise<Feed[]> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
export async function getAllCategories(): Promise<string[]> {
 | 
					export async function getAllCategories(): Promise<string[]> {
 | 
				
			||||||
  try {
 | 
					  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[];
 | 
					    const rows = stmt.all() as any[];
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    return rows.map((row) => row.category).filter(Boolean);
 | 
					    return rows.map((row) => row.category).filter(Boolean);
 | 
				
			||||||
  } catch (error) {
 | 
					  } catch (error) {
 | 
				
			||||||
    console.error("Error getting all categories:", error);
 | 
					    console.error("Error getting all categories:", error);
 | 
				
			||||||
@@ -617,19 +625,21 @@ export async function getAllCategories(): Promise<string[]> {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export async function getFeedsGroupedByCategory(): Promise<{ [category: string]: Feed[] }> {
 | 
					export async function getFeedsGroupedByCategory(): Promise<{
 | 
				
			||||||
 | 
					  [category: string]: Feed[];
 | 
				
			||||||
 | 
					}> {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    const feeds = await getAllFeeds();
 | 
					    const feeds = await getAllFeeds();
 | 
				
			||||||
    const grouped: { [category: string]: Feed[] } = {};
 | 
					    const grouped: { [category: string]: Feed[] } = {};
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    for (const feed of feeds) {
 | 
					    for (const feed of feeds) {
 | 
				
			||||||
      const category = feed.category || 'Uncategorized';
 | 
					      const category = feed.category || "Uncategorized";
 | 
				
			||||||
      if (!grouped[category]) {
 | 
					      if (!grouped[category]) {
 | 
				
			||||||
        grouped[category] = [];
 | 
					        grouped[category] = [];
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
      grouped[category].push(feed);
 | 
					      grouped[category].push(feed);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    return grouped;
 | 
					    return grouped;
 | 
				
			||||||
  } catch (error) {
 | 
					  } catch (error) {
 | 
				
			||||||
    console.error("Error getting feeds grouped by category:", error);
 | 
					    console.error("Error getting feeds grouped by category:", error);
 | 
				
			||||||
@@ -1121,7 +1131,9 @@ export async function migrateFeedsWithCategories(): Promise<void> {
 | 
				
			|||||||
    console.log("🔄 Starting feed category migration...");
 | 
					    console.log("🔄 Starting feed category migration...");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Get all feeds without categories
 | 
					    // 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[];
 | 
					    const feedsWithoutCategories = stmt.all() as any[];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if (feedsWithoutCategories.length === 0) {
 | 
					    if (feedsWithoutCategories.length === 0) {
 | 
				
			||||||
@@ -1129,7 +1141,9 @@ export async function migrateFeedsWithCategories(): Promise<void> {
 | 
				
			|||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    console.log(`📋 Found ${feedsWithoutCategories.length} feeds without categories`);
 | 
					    console.log(
 | 
				
			||||||
 | 
					      `📋 Found ${feedsWithoutCategories.length} feeds without categories`,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Import LLM service
 | 
					    // Import LLM service
 | 
				
			||||||
    const { openAI_ClassifyFeed } = await import("./llm.js");
 | 
					    const { openAI_ClassifyFeed } = await import("./llm.js");
 | 
				
			||||||
@@ -1141,37 +1155,48 @@ export async function migrateFeedsWithCategories(): Promise<void> {
 | 
				
			|||||||
      try {
 | 
					      try {
 | 
				
			||||||
        // Use title for classification, fallback to URL if no title
 | 
					        // Use title for classification, fallback to URL if no title
 | 
				
			||||||
        const titleForClassification = feed.title || feed.url;
 | 
					        const titleForClassification = feed.title || feed.url;
 | 
				
			||||||
        
 | 
					
 | 
				
			||||||
        console.log(`🔍 Classifying feed: ${titleForClassification}`);
 | 
					        console.log(`🔍 Classifying feed: ${titleForClassification}`);
 | 
				
			||||||
        
 | 
					
 | 
				
			||||||
        // Classify the feed
 | 
					        // Classify the feed
 | 
				
			||||||
        const category = await openAI_ClassifyFeed(titleForClassification);
 | 
					        const category = await openAI_ClassifyFeed(titleForClassification);
 | 
				
			||||||
        
 | 
					
 | 
				
			||||||
        // Update the feed with the category
 | 
					        // 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);
 | 
					        updateStmt.run(category, feed.id);
 | 
				
			||||||
        
 | 
					
 | 
				
			||||||
        console.log(`✅ Assigned category "${category}" to feed: ${titleForClassification}`);
 | 
					        console.log(
 | 
				
			||||||
 | 
					          `✅ Assigned category "${category}" to feed: ${titleForClassification}`,
 | 
				
			||||||
 | 
					        );
 | 
				
			||||||
        processedCount++;
 | 
					        processedCount++;
 | 
				
			||||||
        
 | 
					
 | 
				
			||||||
        // Add a small delay to avoid rate limiting
 | 
					        // Add a small delay to avoid rate limiting
 | 
				
			||||||
        await new Promise(resolve => setTimeout(resolve, 1000));
 | 
					        await new Promise((resolve) => setTimeout(resolve, 1000));
 | 
				
			||||||
        
 | 
					 | 
				
			||||||
      } catch (error) {
 | 
					      } 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++;
 | 
					        errorCount++;
 | 
				
			||||||
        
 | 
					
 | 
				
			||||||
        // Set a default category for failed classifications
 | 
					        // Set a default category for failed classifications
 | 
				
			||||||
        const defaultCategory = "その他";
 | 
					        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);
 | 
					        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(`✅ Feed category migration completed`);
 | 
				
			||||||
    console.log(`📊 Processed: ${processedCount}, Errors: ${errorCount}, Total: ${feedsWithoutCategories.length}`);
 | 
					    console.log(
 | 
				
			||||||
    
 | 
					      `📊 Processed: ${processedCount}, Errors: ${errorCount}, Total: ${feedsWithoutCategories.length}`,
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
  } catch (error) {
 | 
					  } catch (error) {
 | 
				
			||||||
    console.error("❌ Error during feed category migration:", error);
 | 
					    console.error("❌ Error during feed category migration:", error);
 | 
				
			||||||
    throw error;
 | 
					    throw error;
 | 
				
			||||||
@@ -1186,11 +1211,15 @@ export async function getFeedCategoryMigrationStatus(): Promise<{
 | 
				
			|||||||
  migrationComplete: boolean;
 | 
					  migrationComplete: boolean;
 | 
				
			||||||
}> {
 | 
					}> {
 | 
				
			||||||
  try {
 | 
					  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 totalResult = totalStmt.get() as any;
 | 
				
			||||||
    const totalFeeds = totalResult.count;
 | 
					    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 withCategoriesResult = withCategoriesStmt.get() as any;
 | 
				
			||||||
    const feedsWithCategories = withCategoriesResult.count;
 | 
					    const feedsWithCategories = withCategoriesResult.count;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1212,9 +1241,11 @@ export async function getFeedCategoryMigrationStatus(): Promise<{
 | 
				
			|||||||
export async function deleteEpisode(episodeId: string): Promise<boolean> {
 | 
					export async function deleteEpisode(episodeId: string): Promise<boolean> {
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    // Get episode info first to find the audio file path
 | 
					    // 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;
 | 
					    const episode = episodeStmt.get(episodeId) as any;
 | 
				
			||||||
    
 | 
					
 | 
				
			||||||
    if (!episode) {
 | 
					    if (!episode) {
 | 
				
			||||||
      return false; // Episode not found
 | 
					      return false; // Episode not found
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -1226,13 +1257,19 @@ export async function deleteEpisode(episodeId: string): Promise<boolean> {
 | 
				
			|||||||
    // If database deletion successful, try to delete the audio file
 | 
					    // If database deletion successful, try to delete the audio file
 | 
				
			||||||
    if (result.changes > 0 && episode.audio_path) {
 | 
					    if (result.changes > 0 && episode.audio_path) {
 | 
				
			||||||
      try {
 | 
					      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)) {
 | 
					        if (fs.existsSync(fullAudioPath)) {
 | 
				
			||||||
          fs.unlinkSync(fullAudioPath);
 | 
					          fs.unlinkSync(fullAudioPath);
 | 
				
			||||||
          console.log(`🗑️  Deleted audio file: ${fullAudioPath}`);
 | 
					          console.log(`🗑  Deleted audio file: ${fullAudioPath}`);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
      } catch (fileError) {
 | 
					      } 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
 | 
					        // Don't fail the operation if file deletion fails
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user