252 lines
6.8 KiB
JavaScript
252 lines
6.8 KiB
JavaScript
/**
|
|
* 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;
|