Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>News Feed Aggregator</title> | |
<style> | |
body { font-family: Arial, sans-serif; margin: 20px; } | |
#results { margin-top: 20px; } | |
.item { border-bottom: 1px solid #ddd; padding: 10px 0; } | |
.item a { font-weight: bold; text-decoration: none; color: #333; } | |
.item a:hover { text-decoration: underline; } | |
.meta { font-size: 0.85em; color: #666; } | |
</style> | |
</head> | |
<body> | |
<h1>News Feed Aggregator</h1> | |
<label for="source">Choose a source:</label> | |
<select id="source"> | |
<option value="all">All Sources</option> | |
<option value="http://feeds.bbci.co.uk/news/rss.xml">BBC</option> | |
<option value="https://rss.dw.com/rdf/rss-en-all">DW</option> | |
<option value="https://www.aljazeera.com/xml/rss/all.xml">Al Jazeera</option> | |
<option value="https://www.france24.com/en/rss">France24</option> | |
<option value="https://www.theguardian.com/world/rss">The Guardian</option> | |
<!-- Add more RSS feeds as needed --> | |
</select> | |
<br><br> | |
<label for="keywords">Keywords (comma separated):</label> | |
<input type="text" id="keywords" placeholder="e.g. economy, politics"> | |
<br><br> | |
<button onclick="loadNews()">Search</button> | |
<label> | |
Auto-refresh every: | |
<select id="refreshInterval" onchange="setAutoRefresh()"> | |
<option value="0">Off</option> | |
<option value="30">30s</option> | |
<option value="60">1 min</option> | |
<option value="300">5 min</option> | |
</select> | |
</label> | |
<div id="results"></div> | |
<script> | |
async function fetchFeed(url) { | |
const res = await fetch("https://armandhilton-newsfeed.hf.space/run/predict", { | |
method: "POST", | |
headers: { "Content-Type": "application/json" }, | |
body: JSON.stringify({ data: [url] }) | |
}); | |
const json = await res.json(); | |
return json.data[0]; // ← Fixed: this is the array of items | |
} | |
async function loadNews() { | |
const selectedSource = document.getElementById('source').value; | |
const keywordsInput = document.getElementById('keywords').value.trim().toLowerCase(); | |
const keywords = keywordsInput ? keywordsInput.split(',').map(k => k.trim()) : []; | |
let sourcesToFetch = []; | |
if (selectedSource === 'all') { | |
sourcesToFetch = Array.from(document.getElementById('source').options).slice(1).map(o => o.value); | |
} else { | |
sourcesToFetch = [selectedSource]; | |
} | |
let allItems = []; | |
for (let src of sourcesToFetch) { | |
try { | |
const items = await fetchFeed(src); | |
if (items) { | |
const filtered = items.filter(item => { | |
if (!keywords.length) return true; | |
const text = (item.title + ' ' + (item.description || '')).toLowerCase(); | |
return keywords.every(k => text.includes(k)); | |
}); | |
allItems = allItems.concat(filtered); | |
} | |
} catch (e) { | |
console.error("Failed to fetch feed", src, e); | |
} | |
} | |
allItems.sort((a, b) => new Date(b.pubDate) - new Date(a.pubDate)); | |
const resultsDiv = document.getElementById('results'); | |
resultsDiv.innerHTML = ""; | |
if (!allItems.length) { | |
resultsDiv.innerHTML = "<p>No results for given keywords.</p>"; | |
return; | |
} | |
for (let item of allItems) { | |
const div = document.createElement('div'); | |
div.className = 'item'; | |
div.innerHTML = ` | |
<a href="${item.link}" target="_blank">${item.title}</a> | |
<div class="meta">${item.pubDate}</div> | |
<p>${item.description || ""}</p> | |
`; | |
resultsDiv.appendChild(div); | |
} | |
} | |
function setAutoRefresh() { | |
const interval = parseInt(document.getElementById("refreshInterval").value); | |
if (refreshTimer) clearInterval(refreshTimer); | |
if (interval > 0) refreshTimer = setInterval(loadNews, interval * 1000); | |
} | |
// Initial load | |
loadNews(); | |
</script> | |
</body> | |
</html> | |