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;
|
||||||
@ -581,11 +583,15 @@ export async function getFeedsByCategory(category?: string): Promise<Feed[]> {
|
|||||||
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,7 +613,9 @@ 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);
|
||||||
@ -617,13 +625,15 @@ 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] = [];
|
||||||
}
|
}
|
||||||
@ -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");
|
||||||
@ -1148,30 +1162,41 @@ export async function migrateFeedsWithCategories(): Promise<void> {
|
|||||||
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,7 +1241,9 @@ 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) {
|
||||||
@ -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