diff --git a/scripts/generate_circle_data.js b/scripts/generate_circle_data.js index 8381d691..8629c07a 100644 --- a/scripts/generate_circle_data.js +++ b/scripts/generate_circle_data.js @@ -9,7 +9,7 @@ const MAX_POSTS_PER_FRIEND = 5; const MAX_TOTAL_POSTS = 100; // Limit total output size const parser = new Parser({ - timeout: 5000, + timeout: 10000, // 10s timeout headers: { 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36' } @@ -20,44 +20,10 @@ async function fetchFeed(url) { const feed = await parser.parseURL(url); return feed; } catch (error) { - // console.error(`Failed to fetch ${url}: ${error.message}`); throw error; } } -async function getRssUrl(friend) { - // If RSS is explicitly provided (4th element in array) - if (friend.length >= 4 && friend[3]) { - return friend[3]; - } - - // Otherwise, try to guess - const baseUrl = friend[1].replace(/\/$/, ''); - const guesses = [ - `${baseUrl}/atom.xml`, - `${baseUrl}/rss.xml`, - `${baseUrl}/feed`, - `${baseUrl}/feed/`, - `${baseUrl}/index.xml` - ]; - - for (const url of guesses) { - try { - // Simple HEAD request or just try to parse - // Since we use rss-parser, just trying to parse is easiest check - // But doing this for every friend is slow. - // For now, we only use explicit RSS if provided, or simple guess if not. - // To speed up, we'll just try the first guess or return null if strict. - // But to be helpful, let's try the most common one: atom.xml - // ideally we should check HEAD, but let's just return the first guess for now - // and let the parser fail if it's wrong. - // BETTER STRATEGY: Return a list of candidates and try them. - } catch (e) {} - } - - return `${baseUrl}/atom.xml`; // Default guess -} - async function main() { console.log('Starting Friend Circle generation...'); @@ -71,12 +37,17 @@ async function main() { let allPosts = []; - // Process friends in batches to avoid overwhelming network - const BATCH_SIZE = 10; + // Revert to processing one by one or small batches with sequential candidate checks + // to ensure reliability over speed. const friends = data.friends; - for (let i = 0; i < friends.length; i += BATCH_SIZE) { - const batch = friends.slice(i, i + BATCH_SIZE); + // Using a simple loop to process friends to ensure stability + // We can run a few in parallel, but let's keep candidate checking sequential + + const CONCURRENCY = 5; // Conservative concurrency + + for (let i = 0; i < friends.length; i += CONCURRENCY) { + const batch = friends.slice(i, i + CONCURRENCY); const promises = batch.map(async (friend) => { const name = friend[0]; const blogUrl = friend[1]; @@ -93,32 +64,35 @@ async function main() { candidates.push(`${cleanUrl}/rss.xml`); candidates.push(`${cleanUrl}/feed`); candidates.push(`${cleanUrl}/feed/`); - candidates.push(`${cleanUrl}/feed.xml`); candidates.push(`${cleanUrl}/index.xml`); - candidates.push(`${cleanUrl}/rss`); - candidates.push(`${cleanUrl}/atom`); } let feed = null; - try { - // Try all candidates in parallel and take the first success - feed = await Promise.any(candidates.map(url => fetchFeed(url))); - // console.log(`[${name}] Fetched RSS successfully.`); - } catch (err) { - // All candidates failed - // console.error(`[${name}] All RSS candidates failed:`, err.errors); - console.log(`[${name}] No valid RSS found. Candidates: ${candidates.join(', ')}`); + + // Sequential candidate check (Reliable) + for (const url of candidates) { + try { + feed = await fetchFeed(url); + // console.log(`[${name}] Fetched RSS successfully: ${url}`); + break; // Found one, stop checking + } catch (e) { + // Continue to next candidate + } + } + + if (!feed) { + console.log(`[${name}] No valid RSS found.`); return; } // Extract posts + if (!feed.items) return; + const posts = feed.items.slice(0, MAX_POSTS_PER_FRIEND).map(item => { let img = null; // Try to find an image in content - // Priority: content:encoded > content > description const content = item['content:encoded'] || item.content || item.description || ''; - // Better regex to capture src with single or double quotes const imgMatch = content.match(/]+src=['"]([^'"]+)['"]/i); if (imgMatch) { @@ -130,11 +104,8 @@ async function main() { // Get a short snippet let snippet = ''; if (content) { - // Remove HTML tags snippet = content.replace(/<[^>]+>/g, ''); - // Remove excessive whitespace snippet = snippet.replace(/\s+/g, ' ').trim(); - // Truncate if (snippet.length > 120) { snippet = snippet.substring(0, 120) + '...'; } @@ -143,7 +114,7 @@ async function main() { return { title: item.title, link: item.link, - date: item.isoDate || item.pubDate, // RSS parser standardizes this + date: item.isoDate || item.pubDate, author: name, avatar: avatar, blogUrl: blogUrl, @@ -156,7 +127,7 @@ async function main() { }); await Promise.all(promises); - process.stdout.write(`Processed ${Math.min(i + BATCH_SIZE, friends.length)}/${friends.length} friends...\r`); + process.stdout.write(`Processed ${Math.min(i + CONCURRENCY, friends.length)}/${friends.length} friends...\r`); } console.log('\nSorting and saving...'); diff --git a/themes/Ying/assets/css/main.css b/themes/Ying/assets/css/main.css index 240dcc33..d0be7d31 100644 --- a/themes/Ying/assets/css/main.css +++ b/themes/Ying/assets/css/main.css @@ -648,7 +648,7 @@ ul { display: inline-block; list-style: none; margin-left: 0; - margin-right: 15px; + margin-right: 16px; margin-top: 20px; text-transform: capitalize; }