Initial commit: Digital signage system for transit departures, weather, and news ticker
This commit is contained in:
251
rss.js
Normal file
251
rss.js
Normal file
@@ -0,0 +1,251 @@
|
||||
/**
|
||||
* 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;
|
||||
Reference in New Issue
Block a user