Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- <!DOCTYPE html>
- <html lang="en">
- <head>
- <meta charset="UTF-8">
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
- <title>Simple RSS Reader</title>
- <style>
- body { font-family: Arial, sans-serif; }
- #feedItems { margin-top: 20px; }
- .feed { margin-bottom: 10px; }
- a { text-decoration: none; color: blue; }
- a:visited { color: purple; }
- summary {background-color: Aliceblue;}
- details {border: 1px solid white; background-color: beige;}
- </style>
- </head>
- <body>
- <h1>Simple RSS Reader</h1>
- <input type="text" id="feedUrl" placeholder="Enter RSS Feed URL(s) - comma/semicolon separated">
- <button id="subscribe">Subscribe</button>
- <button id="export">Export</button>
- <div id="subscriptions"></div>
- <div id="feedItems"></div>
- <script>
- let allEpisodes = []; // Array to store episodes from all feeds
- let podcastTitles = {}; // Object to store podcast titles by feed URL
- let podcastDescriptions = {}; // Object to store podcast descriptions by feed URL
- // Function to load subscribed feeds from local storage
- function loadFeeds() {
- const feeds = JSON.parse(localStorage.getItem('subscribedFeeds')) || [];
- feeds.forEach(feed => fetchFeed(feed));
- displaySubscriptions(feeds); // Display subscriptions
- }
- // Function to display subscriptions
- function displaySubscriptions(feeds) {
- const subscriptionsDiv = document.getElementById('subscriptions');
- subscriptionsDiv.innerHTML = '<h4>Subscriptions:</h4>';
- feeds.forEach(feed => {
- const podcastTitle = podcastTitles[feed] || feed; // Use stored title or feed URL
- const podcastDescription = podcastDescriptions[feed] || '';
- subscriptionsDiv.innerHTML += `
- <details>
- <summary><a href="${feed}" target="_blank">${podcastTitle}</a> <button onclick="removeFeed('${feed}')">Remove</button></summary>
- ${podcastDescription || 'No description available'}
- </details>`;
- });
- }
- // Function to fetch a single feed
- function fetchFeed(feedUrl) {
- fetch(feedUrl)
- .then(response => {
- if (!response.ok) {
- throw new Error('Network response was not ok: ' + response.statusText);
- }
- return response.text();
- })
- .then(str => {
- const parser = new window.DOMParser();
- const data = parser.parseFromString(str, "text/xml");
- const items = Array.from(data.querySelectorAll("item"));
- if (items.length === 0) {
- console.error("No items found in the feed.");
- return;
- }
- const podcastTitle = data.querySelector("title").textContent; // Get podcast title
- const podcastDescription = data.querySelector("description") ? data.querySelector("description").textContent : '';
- podcastTitles[feedUrl] = podcastTitle; // Store podcast title by feed URL
- podcastDescriptions[feedUrl] = podcastDescription; // Store podcast description by feed URL
- // Extract episodes and add to allEpisodes array
- items.forEach(el => {
- const title = el.querySelector("title").textContent;
- const audioUrl = el.querySelector("enclosure").getAttribute("url");
- const pubDate = new Date(el.querySelector("pubDate").textContent);
- const episodeDescription = el.querySelector("description") ? el.querySelector("description").textContent : '';
- allEpisodes.push({ podcastTitle, title, audioUrl, pubDate, episodeDescription });
- });
- // Sort all episodes by publication date (recent to old)
- allEpisodes.sort((a, b) => b.pubDate - a.pubDate);
- // Display all episodes
- displayEpisodes();
- const currentFeeds = JSON.parse(localStorage.getItem('subscribedFeeds')) || [];
- displaySubscriptions(currentFeeds); // Update subscriptions display
- })
- .catch(error => {
- console.error("Error fetching the feed:", error);
- });
- }
- // Function to display all episodes
- function displayEpisodes() {
- let html = '<h4>Episodes:</h4>';
- allEpisodes.forEach((episode, index) => {
- html += `<details><summary>${episode.podcastTitle} - <a href="${episode.audioUrl}" target="_blank">${episode.title}</a></summary>${episode.episodeDescription || 'No description available'}</details>`;
- });
- document.getElementById('feedItems').innerHTML = html;
- }
- // Function to subscribe to feed(s)
- document.getElementById('subscribe').addEventListener('click', function() {
- const input = document.getElementById('feedUrl').value;
- if (input) {
- // First try splitting by comma or semicolon, then fallback to whitespace if no commas/semicolons
- let urls;
- if (input.includes(',') || input.includes(';')) {
- urls = input.split(/[,;]+/).map(url => url.trim()).filter(url => url);
- } else {
- urls = input.split(/\s+/).map(url => url.trim()).filter(url => url);
- }
- const feeds = JSON.parse(localStorage.getItem('subscribedFeeds')) || [];
- let added = 0;
- urls.forEach(url => {
- if (url && !feeds.includes(url)) {
- feeds.push(url);
- added++;
- fetchFeed(url);
- }
- });
- if (added > 0) {
- localStorage.setItem('subscribedFeeds', JSON.stringify(feeds));
- }
- document.getElementById('feedUrl').value = '';
- }
- });
- // Export function
- document.getElementById('export').addEventListener('click', function() {
- const feeds = JSON.parse(localStorage.getItem('subscribedFeeds')) || [];
- if (feeds.length > 0) {
- const exportText = feeds.join(', ');
- if (navigator.clipboard && navigator.clipboard.writeText) {
- navigator.clipboard.writeText(exportText).then(() => {
- alert('Feeds copied to clipboard!');
- }).catch(() => {
- prompt('Copy this text:', exportText);
- });
- } else {
- prompt('Copy this text:', exportText);
- }
- }
- });
- // Function to remove a feed
- function removeFeed(feedUrl) {
- let feeds = JSON.parse(localStorage.getItem('subscribedFeeds')) || [];
- feeds = feeds.filter(feed => feed !== feedUrl);
- localStorage.setItem('subscribedFeeds', JSON.stringify(feeds));
- allEpisodes = []; // Reset the episodes array
- delete podcastTitles[feedUrl]; // Remove the podcast title from the object
- delete podcastDescriptions[feedUrl]; // Remove the podcast description from the object
- loadFeeds(); // Reload feeds to update the display
- }
- // Load feeds on page load
- loadFeeds();
- </script>
- </body>
- </html>
Advertisement
Add Comment
Please, Sign In to add comment