/** * rss.js - A modular RSS feed manager component * Manages multiple RSS feeds and provides aggregated content to the ticker */ class RssManager { constructor(options = {}) { // Default options this.options = { proxyUrl: '/api/rss', updateInterval: 300000, // Update every 5 minutes maxItemsPerFeed: 10, ...options }; // State this.feeds = []; this.items = []; this.updateCallbacks = []; // Initialize this.init(); } /** * Initialize the RSS manager */ async init() { console.log('Initializing RssManager...'); try { // Load feeds from config await this.loadFeeds(); // Start periodic updates this.startUpdates(); // Listen for config changes document.addEventListener('configChanged', (event) => { if (event.detail.config.rssFeeds) { this.onConfigChanged(event.detail.config.rssFeeds); } }); console.log('RssManager initialized successfully'); } catch (error) { console.error('Error initializing RssManager:', error); } } /** * Load feeds from config */ async loadFeeds() { console.log('Loading RSS feeds...'); try { const response = await fetch('/api/config'); const config = await response.json(); if (config.rssFeeds) { this.feeds = config.rssFeeds; console.log('Loaded RSS feeds from config:', this.feeds); await this.refreshFeeds(); } } catch (error) { console.error('Error loading RSS feeds from config:', error); this.feeds = [{ name: "Travel Alerts", url: "https://travel.state.gov/content/travel/en/rss/rss.xml", enabled: true }, { name: "HD News", url: "https://www.hd.se/feeds/feed.xml", enabled: true }]; console.log('Using default RSS feeds:', this.feeds); await this.refreshFeeds(); } } /** * Start periodic updates */ startUpdates() { setInterval(() => this.refreshFeeds(), this.options.updateInterval); } /** * Refresh all enabled feeds */ async refreshFeeds() { console.log('Refreshing RSS feeds...'); try { const enabledFeeds = this.feeds.filter(feed => feed.enabled); console.log('Enabled feeds:', enabledFeeds); const feedPromises = enabledFeeds.map(feed => this.fetchFeed(feed)); const results = await Promise.all(feedPromises); // Combine and sort all items by date this.items = results .flat() .sort((a, b) => { const dateA = new Date(a.pubDate || 0); const dateB = new Date(b.pubDate || 0); return dateB - dateA; }); console.log('Fetched RSS items:', this.items.length); // Notify subscribers this.notifyUpdate(); } catch (error) { console.error('Error refreshing feeds:', error); } } /** * Fetch a single feed */ async fetchFeed(feed) { try { console.log(`Fetching feed ${feed.name} through proxy...`); const proxyUrl = `/api/rss?url=${encodeURIComponent(feed.url)}`; console.log('Proxy URL:', proxyUrl); const response = await fetch(proxyUrl); if (!response.ok) { throw new Error(`HTTP error! Status: ${response.status}`); } const data = await response.json(); console.log(`Received data for feed ${feed.name}:`, data); if (data.items && data.items.length > 0) { // Add feed name to each item return data.items .slice(0, this.options.maxItemsPerFeed) .map(item => ({ ...item, feedName: feed.name, feedUrl: feed.url })); } return []; } catch (error) { console.error(`Error fetching feed ${feed.name}:`, error); return [{ title: `Error fetching ${feed.name} feed`, link: feed.url, feedName: feed.name, feedUrl: feed.url, error: error.message }]; } } /** * Handle config changes */ async onConfigChanged(newFeeds) { console.log('RSS feeds configuration changed'); this.feeds = newFeeds; await this.refreshFeeds(); } /** * Subscribe to feed updates */ onUpdate(callback) { this.updateCallbacks.push(callback); } /** * Notify subscribers of updates */ notifyUpdate() { this.updateCallbacks.forEach(callback => { try { callback(this.items); } catch (error) { console.error('Error in update callback:', error); } }); } /** * Get current feed items */ getItems() { return this.items; } /** * Get feed configuration */ getFeeds() { return this.feeds; } /** * Add a new feed */ async addFeed(feed) { this.feeds.push({ name: feed.name, url: feed.url, enabled: feed.enabled ?? true }); await this.refreshFeeds(); } /** * Remove a feed */ async removeFeed(index) { if (index >= 0 && index < this.feeds.length) { this.feeds.splice(index, 1); await this.refreshFeeds(); } } /** * Update a feed */ async updateFeed(index, feed) { if (index >= 0 && index < this.feeds.length) { this.feeds[index] = { ...this.feeds[index], ...feed }; await this.refreshFeeds(); } } /** * Enable/disable a feed */ async toggleFeed(index, enabled) { if (index >= 0 && index < this.feeds.length) { this.feeds[index].enabled = enabled; await this.refreshFeeds(); } } } // Export the RssManager class for use in other modules window.RssManager = RssManager;