Files
SignageHTML/rss.js

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;