Update
This commit is contained in:
109
admin-server.ts
109
admin-server.ts
@ -14,6 +14,7 @@ import {
|
|||||||
getFeedRequests,
|
getFeedRequests,
|
||||||
updateFeedRequestStatus,
|
updateFeedRequestStatus,
|
||||||
} from "./services/database.js";
|
} from "./services/database.js";
|
||||||
|
import { Database } from "bun:sqlite";
|
||||||
import { batchProcess, addNewFeedUrl } from "./scripts/fetch_and_generate.js";
|
import { batchProcess, addNewFeedUrl } from "./scripts/fetch_and_generate.js";
|
||||||
import { batchScheduler } from "./services/batch-scheduler.js";
|
import { batchScheduler } from "./services/batch-scheduler.js";
|
||||||
|
|
||||||
@ -204,6 +205,114 @@ app.get("/api/admin/episodes/simple", async (c) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Database diagnostic endpoint
|
||||||
|
app.get("/api/admin/db-diagnostic", async (c) => {
|
||||||
|
try {
|
||||||
|
const db = new Database(config.paths.dbPath);
|
||||||
|
|
||||||
|
// 1. Check episodes table
|
||||||
|
const episodeCount = db.prepare("SELECT COUNT(*) as count FROM episodes").get() as any;
|
||||||
|
|
||||||
|
// 2. Check articles table
|
||||||
|
const articleCount = db.prepare("SELECT COUNT(*) as count FROM articles").get() as any;
|
||||||
|
|
||||||
|
// 3. Check feeds table
|
||||||
|
const feedCount = db.prepare("SELECT COUNT(*) as count FROM feeds").get() as any;
|
||||||
|
const activeFeedCount = db.prepare("SELECT COUNT(*) as count FROM feeds WHERE active = 1").get() as any;
|
||||||
|
const inactiveFeedCount = db.prepare("SELECT COUNT(*) as count FROM feeds WHERE active = 0 OR active IS NULL").get() as any;
|
||||||
|
|
||||||
|
// 4. Check orphaned episodes
|
||||||
|
const orphanedEpisodes = db.prepare(`
|
||||||
|
SELECT e.id, e.title, e.article_id
|
||||||
|
FROM episodes e
|
||||||
|
LEFT JOIN articles a ON e.article_id = a.id
|
||||||
|
WHERE a.id IS NULL
|
||||||
|
`).all() as any[];
|
||||||
|
|
||||||
|
// 5. Check orphaned articles
|
||||||
|
const orphanedArticles = db.prepare(`
|
||||||
|
SELECT a.id, a.title, a.feed_id
|
||||||
|
FROM articles a
|
||||||
|
LEFT JOIN feeds f ON a.feed_id = f.id
|
||||||
|
WHERE f.id IS NULL
|
||||||
|
`).all() as any[];
|
||||||
|
|
||||||
|
// 6. Check episodes with articles but feeds are inactive
|
||||||
|
const episodesInactiveFeeds = db.prepare(`
|
||||||
|
SELECT e.id, e.title, f.active, f.title as feed_title
|
||||||
|
FROM episodes e
|
||||||
|
JOIN articles a ON e.article_id = a.id
|
||||||
|
JOIN feeds f ON a.feed_id = f.id
|
||||||
|
WHERE f.active = 0 OR f.active IS NULL
|
||||||
|
`).all() as any[];
|
||||||
|
|
||||||
|
// 7. Test the JOIN query
|
||||||
|
const joinResult = db.prepare(`
|
||||||
|
SELECT COUNT(*) as count
|
||||||
|
FROM episodes e
|
||||||
|
JOIN articles a ON e.article_id = a.id
|
||||||
|
JOIN feeds f ON a.feed_id = f.id
|
||||||
|
WHERE f.active = 1
|
||||||
|
`).get() as any;
|
||||||
|
|
||||||
|
// 8. Sample feed details
|
||||||
|
const sampleFeeds = db.prepare(`
|
||||||
|
SELECT id, title, url, active, created_at
|
||||||
|
FROM feeds
|
||||||
|
ORDER BY created_at DESC
|
||||||
|
LIMIT 5
|
||||||
|
`).all() as any[];
|
||||||
|
|
||||||
|
// 9. Sample episode-article-feed chain
|
||||||
|
const sampleChain = db.prepare(`
|
||||||
|
SELECT
|
||||||
|
e.id as episode_id, e.title as episode_title,
|
||||||
|
a.id as article_id, a.title as article_title,
|
||||||
|
f.id as feed_id, f.title as feed_title, f.active
|
||||||
|
FROM episodes e
|
||||||
|
LEFT JOIN articles a ON e.article_id = a.id
|
||||||
|
LEFT JOIN feeds f ON a.feed_id = f.id
|
||||||
|
ORDER BY e.created_at DESC
|
||||||
|
LIMIT 5
|
||||||
|
`).all() as any[];
|
||||||
|
|
||||||
|
db.close();
|
||||||
|
|
||||||
|
const diagnosticResult = {
|
||||||
|
counts: {
|
||||||
|
episodes: episodeCount.count,
|
||||||
|
articles: articleCount.count,
|
||||||
|
feeds: feedCount.count,
|
||||||
|
activeFeeds: activeFeedCount.count,
|
||||||
|
inactiveFeeds: inactiveFeedCount.count,
|
||||||
|
},
|
||||||
|
orphaned: {
|
||||||
|
episodes: orphanedEpisodes.length,
|
||||||
|
episodeDetails: orphanedEpisodes.slice(0, 3),
|
||||||
|
articles: orphanedArticles.length,
|
||||||
|
articleDetails: orphanedArticles.slice(0, 3),
|
||||||
|
},
|
||||||
|
episodesFromInactiveFeeds: {
|
||||||
|
count: episodesInactiveFeeds.length,
|
||||||
|
details: episodesInactiveFeeds.slice(0, 3),
|
||||||
|
},
|
||||||
|
joinQuery: {
|
||||||
|
episodesWithActiveFeeds: joinResult.count,
|
||||||
|
},
|
||||||
|
samples: {
|
||||||
|
feeds: sampleFeeds,
|
||||||
|
episodeChain: sampleChain,
|
||||||
|
},
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return c.json(diagnosticResult);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error running database diagnostic:", error);
|
||||||
|
return c.json({ error: "Failed to run database diagnostic", details: error.message }, 500);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Feed requests management
|
// Feed requests management
|
||||||
app.get("/api/admin/feed-requests", async (c) => {
|
app.get("/api/admin/feed-requests", async (c) => {
|
||||||
try {
|
try {
|
||||||
|
@ -4,59 +4,88 @@ import crypto from "crypto";
|
|||||||
import { config } from "./config.js";
|
import { config } from "./config.js";
|
||||||
|
|
||||||
// Database integrity fixes function
|
// Database integrity fixes function
|
||||||
function performDatabaseIntegrityFixes(db: Database): void {
|
export function performDatabaseIntegrityFixes(db: Database): void {
|
||||||
console.log("🔧 Performing database integrity checks...");
|
console.log("🔧 Performing database integrity checks...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Fix 1: Set active flag to 1 for feeds where it's NULL
|
// Fix 1: Set active flag to 1 for feeds where it's NULL
|
||||||
const nullActiveFeeds = db.prepare("UPDATE feeds SET active = 1 WHERE active IS NULL").run();
|
const nullActiveFeeds = db
|
||||||
|
.prepare("UPDATE feeds SET active = 1 WHERE active IS NULL")
|
||||||
|
.run();
|
||||||
if (nullActiveFeeds.changes > 0) {
|
if (nullActiveFeeds.changes > 0) {
|
||||||
console.log(`✅ Fixed ${nullActiveFeeds.changes} feeds with NULL active flag`);
|
console.log(
|
||||||
|
`✅ Fixed ${nullActiveFeeds.changes} feeds with NULL active flag`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix 2: Fix orphaned articles (articles referencing non-existent feeds)
|
// Fix 2: Fix orphaned articles (articles referencing non-existent feeds)
|
||||||
const orphanedArticles = db.prepare(`
|
const orphanedArticles = db
|
||||||
|
.prepare(
|
||||||
|
`
|
||||||
SELECT a.id, a.link, a.title
|
SELECT a.id, a.link, a.title
|
||||||
FROM articles a
|
FROM articles a
|
||||||
LEFT JOIN feeds f ON a.feed_id = f.id
|
LEFT JOIN feeds f ON a.feed_id = f.id
|
||||||
WHERE f.id IS NULL
|
WHERE f.id IS NULL
|
||||||
`).all() as any[];
|
`,
|
||||||
|
)
|
||||||
|
.all() as any[];
|
||||||
|
|
||||||
if (orphanedArticles.length > 0) {
|
if (orphanedArticles.length > 0) {
|
||||||
console.log(`🔍 Found ${orphanedArticles.length} orphaned articles, attempting to fix...`);
|
console.log(
|
||||||
|
`🔍 Found ${orphanedArticles.length} orphaned articles, attempting to fix...`,
|
||||||
|
);
|
||||||
|
|
||||||
for (const article of orphanedArticles) {
|
for (const article of orphanedArticles) {
|
||||||
// Try to match article to feed based on URL domain
|
// Try to match article to feed based on URL domain
|
||||||
const articleDomain = extractDomain(article.link);
|
const articleDomain = extractDomain(article.link);
|
||||||
if (articleDomain) {
|
if (articleDomain) {
|
||||||
const matchingFeed = db.prepare(`
|
const matchingFeed = db
|
||||||
|
.prepare(
|
||||||
|
`
|
||||||
SELECT id FROM feeds
|
SELECT id FROM feeds
|
||||||
WHERE url LIKE ? OR url LIKE ?
|
WHERE url LIKE ? OR url LIKE ?
|
||||||
ORDER BY created_at DESC
|
ORDER BY created_at DESC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
`).get(`%${articleDomain}%`, `%${articleDomain.replace('www.', '')}%`) as any;
|
`,
|
||||||
|
)
|
||||||
|
.get(
|
||||||
|
`%${articleDomain}%`,
|
||||||
|
`%${articleDomain.replace("www.", "")}%`,
|
||||||
|
) as any;
|
||||||
|
|
||||||
if (matchingFeed) {
|
if (matchingFeed) {
|
||||||
db.prepare("UPDATE articles SET feed_id = ? WHERE id = ?")
|
db.prepare("UPDATE articles SET feed_id = ? WHERE id = ?").run(
|
||||||
.run(matchingFeed.id, article.id);
|
matchingFeed.id,
|
||||||
console.log(`✅ Fixed article "${article.title}" -> feed ${matchingFeed.id}`);
|
article.id,
|
||||||
|
);
|
||||||
|
console.log(
|
||||||
|
`✅ Fixed article "${article.title}" -> feed ${matchingFeed.id}`,
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
console.log(`⚠️ Could not find matching feed for article: ${article.title} (${articleDomain})`);
|
console.log(
|
||||||
|
`⚠️ Could not find matching feed for article: ${article.title} (${articleDomain})`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix 3: Ensure all episodes have valid article references
|
// Fix 3: Ensure all episodes have valid article references
|
||||||
const orphanedEpisodes = db.prepare(`
|
const orphanedEpisodes = db
|
||||||
|
.prepare(
|
||||||
|
`
|
||||||
SELECT e.id, e.title, e.article_id
|
SELECT e.id, e.title, e.article_id
|
||||||
FROM episodes e
|
FROM episodes e
|
||||||
LEFT JOIN articles a ON e.article_id = a.id
|
LEFT JOIN articles a ON e.article_id = a.id
|
||||||
WHERE a.id IS NULL
|
WHERE a.id IS NULL
|
||||||
`).all() as any[];
|
`,
|
||||||
|
)
|
||||||
|
.all() as any[];
|
||||||
|
|
||||||
if (orphanedEpisodes.length > 0) {
|
if (orphanedEpisodes.length > 0) {
|
||||||
console.log(`⚠️ Found ${orphanedEpisodes.length} episodes with invalid article references`);
|
console.log(
|
||||||
|
`⚠️ Found ${orphanedEpisodes.length} episodes with invalid article references`,
|
||||||
|
);
|
||||||
// We could delete these or try to fix them, but for now just log
|
// We could delete these or try to fix them, but for now just log
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -259,6 +288,17 @@ export async function saveFeed(
|
|||||||
createdAt,
|
createdAt,
|
||||||
feed.active !== undefined ? (feed.active ? 1 : 0) : 1, // Default to active=1 if not specified
|
feed.active !== undefined ? (feed.active ? 1 : 0) : 1, // Default to active=1 if not specified
|
||||||
);
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
performDatabaseIntegrityFixes(db);
|
||||||
|
console.log(`Feed saved: ${feed.url}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(
|
||||||
|
"Error performing integrity fixes after saving feed:",
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return id;
|
return id;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error saving feed:", error);
|
console.error("Error saving feed:", error);
|
||||||
@ -336,7 +376,9 @@ export async function fetchActiveFeeds(): Promise<Feed[]> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get episodes with feed information for enhanced display
|
// Get episodes with feed information for enhanced display
|
||||||
export async function fetchEpisodesWithFeedInfo(): Promise<EpisodeWithFeedInfo[]> {
|
export async function fetchEpisodesWithFeedInfo(): Promise<
|
||||||
|
EpisodeWithFeedInfo[]
|
||||||
|
> {
|
||||||
try {
|
try {
|
||||||
const stmt = db.prepare(`
|
const stmt = db.prepare(`
|
||||||
SELECT
|
SELECT
|
||||||
@ -386,7 +428,9 @@ export async function fetchEpisodesWithFeedInfo(): Promise<EpisodeWithFeedInfo[]
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get episodes by feed ID
|
// Get episodes by feed ID
|
||||||
export async function fetchEpisodesByFeedId(feedId: string): Promise<EpisodeWithFeedInfo[]> {
|
export async function fetchEpisodesByFeedId(
|
||||||
|
feedId: string,
|
||||||
|
): Promise<EpisodeWithFeedInfo[]> {
|
||||||
try {
|
try {
|
||||||
const stmt = db.prepare(`
|
const stmt = db.prepare(`
|
||||||
SELECT
|
SELECT
|
||||||
@ -436,7 +480,9 @@ export async function fetchEpisodesByFeedId(feedId: string): Promise<EpisodeWith
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Get single episode with source information
|
// Get single episode with source information
|
||||||
export async function fetchEpisodeWithSourceInfo(episodeId: string): Promise<EpisodeWithFeedInfo | null> {
|
export async function fetchEpisodeWithSourceInfo(
|
||||||
|
episodeId: string,
|
||||||
|
): Promise<EpisodeWithFeedInfo | null> {
|
||||||
try {
|
try {
|
||||||
const stmt = db.prepare(`
|
const stmt = db.prepare(`
|
||||||
SELECT
|
SELECT
|
||||||
@ -487,9 +533,7 @@ export async function fetchEpisodeWithSourceInfo(episodeId: string): Promise<Epi
|
|||||||
|
|
||||||
export async function getAllFeedsIncludingInactive(): Promise<Feed[]> {
|
export async function getAllFeedsIncludingInactive(): Promise<Feed[]> {
|
||||||
try {
|
try {
|
||||||
const stmt = db.prepare(
|
const stmt = db.prepare("SELECT * FROM feeds ORDER BY created_at DESC");
|
||||||
"SELECT * FROM feeds ORDER BY created_at DESC",
|
|
||||||
);
|
|
||||||
const rows = stmt.all() as any[];
|
const rows = stmt.all() as any[];
|
||||||
|
|
||||||
return rows.map((row) => ({
|
return rows.map((row) => ({
|
||||||
@ -522,7 +566,9 @@ export async function deleteFeed(feedId: string): Promise<boolean> {
|
|||||||
deleteEpisodesStmt.run(feedId);
|
deleteEpisodesStmt.run(feedId);
|
||||||
|
|
||||||
// Delete all articles for this feed
|
// Delete all articles for this feed
|
||||||
const deleteArticlesStmt = db.prepare("DELETE FROM articles WHERE feed_id = ?");
|
const deleteArticlesStmt = db.prepare(
|
||||||
|
"DELETE FROM articles WHERE feed_id = ?",
|
||||||
|
);
|
||||||
deleteArticlesStmt.run(feedId);
|
deleteArticlesStmt.run(feedId);
|
||||||
|
|
||||||
// Delete the feed itself
|
// Delete the feed itself
|
||||||
@ -539,7 +585,10 @@ export async function deleteFeed(feedId: string): Promise<boolean> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function toggleFeedActive(feedId: string, active: boolean): Promise<boolean> {
|
export async function toggleFeedActive(
|
||||||
|
feedId: string,
|
||||||
|
active: boolean,
|
||||||
|
): Promise<boolean> {
|
||||||
try {
|
try {
|
||||||
const stmt = db.prepare("UPDATE feeds SET active = ? WHERE id = ?");
|
const stmt = db.prepare("UPDATE feeds SET active = ? WHERE id = ?");
|
||||||
const result = stmt.run(active ? 1 : 0, feedId);
|
const result = stmt.run(active ? 1 : 0, feedId);
|
||||||
@ -816,7 +865,7 @@ export interface TTSQueueItem {
|
|||||||
retryCount: number;
|
retryCount: number;
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
lastAttemptedAt?: string;
|
lastAttemptedAt?: string;
|
||||||
status: 'pending' | 'processing' | 'failed';
|
status: "pending" | "processing" | "failed";
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function addToQueue(
|
export async function addToQueue(
|
||||||
@ -840,7 +889,9 @@ export async function addToQueue(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getQueueItems(limit: number = 10): Promise<TTSQueueItem[]> {
|
export async function getQueueItems(
|
||||||
|
limit: number = 10,
|
||||||
|
): Promise<TTSQueueItem[]> {
|
||||||
try {
|
try {
|
||||||
const stmt = db.prepare(`
|
const stmt = db.prepare(`
|
||||||
SELECT * FROM tts_queue
|
SELECT * FROM tts_queue
|
||||||
@ -867,7 +918,7 @@ export async function getQueueItems(limit: number = 10): Promise<TTSQueueItem[]>
|
|||||||
|
|
||||||
export async function updateQueueItemStatus(
|
export async function updateQueueItemStatus(
|
||||||
queueId: string,
|
queueId: string,
|
||||||
status: 'pending' | 'processing' | 'failed',
|
status: "pending" | "processing" | "failed",
|
||||||
lastAttemptedAt?: string,
|
lastAttemptedAt?: string,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
@ -897,7 +948,7 @@ export interface FeedRequest {
|
|||||||
url: string;
|
url: string;
|
||||||
requestedBy?: string;
|
requestedBy?: string;
|
||||||
requestMessage?: string;
|
requestMessage?: string;
|
||||||
status: 'pending' | 'approved' | 'rejected';
|
status: "pending" | "approved" | "rejected";
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
reviewedAt?: string;
|
reviewedAt?: string;
|
||||||
reviewedBy?: string;
|
reviewedBy?: string;
|
||||||
@ -905,7 +956,7 @@ export interface FeedRequest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function submitFeedRequest(
|
export async function submitFeedRequest(
|
||||||
request: Omit<FeedRequest, "id" | "createdAt" | "status">
|
request: Omit<FeedRequest, "id" | "createdAt" | "status">,
|
||||||
): Promise<string> {
|
): Promise<string> {
|
||||||
const id = crypto.randomUUID();
|
const id = crypto.randomUUID();
|
||||||
const createdAt = new Date().toISOString();
|
const createdAt = new Date().toISOString();
|
||||||
@ -914,7 +965,13 @@ export async function submitFeedRequest(
|
|||||||
const stmt = db.prepare(
|
const stmt = db.prepare(
|
||||||
"INSERT INTO feed_requests (id, url, requested_by, request_message, status, created_at) VALUES (?, ?, ?, ?, 'pending', ?)",
|
"INSERT INTO feed_requests (id, url, requested_by, request_message, status, created_at) VALUES (?, ?, ?, ?, 'pending', ?)",
|
||||||
);
|
);
|
||||||
stmt.run(id, request.url, request.requestedBy || null, request.requestMessage || null, createdAt);
|
stmt.run(
|
||||||
|
id,
|
||||||
|
request.url,
|
||||||
|
request.requestedBy || null,
|
||||||
|
request.requestMessage || null,
|
||||||
|
createdAt,
|
||||||
|
);
|
||||||
console.log(`Feed request submitted: ${request.url}`);
|
console.log(`Feed request submitted: ${request.url}`);
|
||||||
return id;
|
return id;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@ -951,7 +1008,7 @@ export async function getFeedRequests(status?: string): Promise<FeedRequest[]> {
|
|||||||
|
|
||||||
export async function updateFeedRequestStatus(
|
export async function updateFeedRequestStatus(
|
||||||
requestId: string,
|
requestId: string,
|
||||||
status: 'approved' | 'rejected',
|
status: "approved" | "rejected",
|
||||||
reviewedBy?: string,
|
reviewedBy?: string,
|
||||||
adminNotes?: string,
|
adminNotes?: string,
|
||||||
): Promise<boolean> {
|
): Promise<boolean> {
|
||||||
@ -960,7 +1017,13 @@ export async function updateFeedRequestStatus(
|
|||||||
const stmt = db.prepare(
|
const stmt = db.prepare(
|
||||||
"UPDATE feed_requests SET status = ?, reviewed_at = ?, reviewed_by = ?, admin_notes = ? WHERE id = ?",
|
"UPDATE feed_requests SET status = ?, reviewed_at = ?, reviewed_by = ?, admin_notes = ? WHERE id = ?",
|
||||||
);
|
);
|
||||||
const result = stmt.run(status, reviewedAt, reviewedBy || null, adminNotes || null, requestId);
|
const result = stmt.run(
|
||||||
|
status,
|
||||||
|
reviewedAt,
|
||||||
|
reviewedBy || null,
|
||||||
|
adminNotes || null,
|
||||||
|
requestId,
|
||||||
|
);
|
||||||
return result.changes > 0;
|
return result.changes > 0;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error updating feed request status:", error);
|
console.error("Error updating feed request status:", error);
|
||||||
|
@ -1,6 +1,10 @@
|
|||||||
import { promises as fs } from "fs";
|
import { promises as fs } from "fs";
|
||||||
import { dirname } from "path";
|
import { dirname } from "path";
|
||||||
import { Episode, fetchAllEpisodes } from "./database.js";
|
import {
|
||||||
|
Episode,
|
||||||
|
fetchAllEpisodes,
|
||||||
|
performDatabaseIntegrityFixes,
|
||||||
|
} from "./database.js";
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
import fsSync from "node:fs";
|
import fsSync from "node:fs";
|
||||||
import { config } from "./config.js";
|
import { config } from "./config.js";
|
||||||
@ -20,7 +24,10 @@ function createItemXml(episode: Episode): string {
|
|||||||
|
|
||||||
let fileSize = 0;
|
let fileSize = 0;
|
||||||
try {
|
try {
|
||||||
const audioPath = path.join(config.paths.podcastAudioDir, episode.audioPath);
|
const audioPath = path.join(
|
||||||
|
config.paths.podcastAudioDir,
|
||||||
|
episode.audioPath,
|
||||||
|
);
|
||||||
if (fsSync.existsSync(audioPath)) {
|
if (fsSync.existsSync(audioPath)) {
|
||||||
fileSize = fsSync.statSync(audioPath).size;
|
fileSize = fsSync.statSync(audioPath).size;
|
||||||
}
|
}
|
||||||
@ -47,9 +54,12 @@ export async function updatePodcastRSS(): Promise<void> {
|
|||||||
const episodes: Episode[] = await fetchAllEpisodes();
|
const episodes: Episode[] = await fetchAllEpisodes();
|
||||||
|
|
||||||
// Filter episodes to only include those with valid audio files
|
// Filter episodes to only include those with valid audio files
|
||||||
const validEpisodes = episodes.filter(episode => {
|
const validEpisodes = episodes.filter((episode) => {
|
||||||
try {
|
try {
|
||||||
const audioPath = path.join(config.paths.podcastAudioDir, episode.audioPath);
|
const audioPath = path.join(
|
||||||
|
config.paths.podcastAudioDir,
|
||||||
|
episode.audioPath,
|
||||||
|
);
|
||||||
return fsSync.existsSync(audioPath);
|
return fsSync.existsSync(audioPath);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.warn(`Audio file not found for episode: ${episode.title}`);
|
console.warn(`Audio file not found for episode: ${episode.title}`);
|
||||||
@ -57,7 +67,9 @@ export async function updatePodcastRSS(): Promise<void> {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log(`Found ${episodes.length} episodes, ${validEpisodes.length} with valid audio files`);
|
console.log(
|
||||||
|
`Found ${episodes.length} episodes, ${validEpisodes.length} with valid audio files`,
|
||||||
|
);
|
||||||
|
|
||||||
const lastBuildDate = new Date().toUTCString();
|
const lastBuildDate = new Date().toUTCString();
|
||||||
const itemsXml = validEpisodes.map(createItemXml).join("\n");
|
const itemsXml = validEpisodes.map(createItemXml).join("\n");
|
||||||
@ -82,7 +94,9 @@ export async function updatePodcastRSS(): Promise<void> {
|
|||||||
await fs.mkdir(dirname(outputPath), { recursive: true });
|
await fs.mkdir(dirname(outputPath), { recursive: true });
|
||||||
await fs.writeFile(outputPath, rssXml);
|
await fs.writeFile(outputPath, rssXml);
|
||||||
|
|
||||||
console.log(`RSS feed updated with ${validEpisodes.length} episodes (audio files verified)`);
|
console.log(
|
||||||
|
`RSS feed updated with ${validEpisodes.length} episodes (audio files verified)`,
|
||||||
|
);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("Error updating podcast RSS:", error);
|
console.error("Error updating podcast RSS:", error);
|
||||||
throw error;
|
throw error;
|
||||||
|
Reference in New Issue
Block a user