[object Object]

← back to Allnewsdaily

initial scaffold — allnewsdaily v0.1 (97 outlets, YouTube live detection, red glowing LIVE pill)

7c2ebe766f4c5083edda78c0dcbd6c12dd45fff3 · 2026-05-20 10:43:43 -0700 · Steve Abrams

Files touched

Diff

commit 7c2ebe766f4c5083edda78c0dcbd6c12dd45fff3
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Wed May 20 10:43:43 2026 -0700

    initial scaffold — allnewsdaily v0.1 (97 outlets, YouTube live detection, red glowing LIVE pill)
---
 .deploy.conf          |    3 +
 .gitignore            |   10 +
 README.md             |   22 +
 data/outlets.json     | 1374 +++++++++++++++++++++++++++++++++++++++++++++++++
 package.json          |   17 +
 public/index.html     |  297 +++++++++++
 scripts/check-live.js |  134 +++++
 server.js             |   94 ++++
 8 files changed, 1951 insertions(+)

diff --git a/.deploy.conf b/.deploy.conf
new file mode 100644
index 0000000..a7c25e2
--- /dev/null
+++ b/.deploy.conf
@@ -0,0 +1,3 @@
+PROJECT_NAME=allnewsdaily
+DEPLOY_PATH=/root/Projects/allnewsdaily
+HEALTH_URL=http://127.0.0.1:9788/api/health
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..1f15e6d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,10 @@
+node_modules/
+.env
+.env.*
+*.log
+tmp/
+dist/
+build/
+.next/
+.DS_Store
+data/live-status.json
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..c6035fa
--- /dev/null
+++ b/README.md
@@ -0,0 +1,22 @@
+# All News Daily
+
+Global news outlet directory — logo + name + country, with a **red glowing LIVE pill** on any outlet currently broadcasting live (detected via YouTube `/channel/<id>/live`).
+
+- `data/outlets.json` — curated outlet list (logos via clearbit, YouTube channel IDs for live detection)
+- `scripts/check-live.js` — polls every channel's YouTube live page, writes `data/live-status.json`
+- `server.js` — Express on `:9788`, merges outlets + live status, runs the checker every 90s
+- `public/index.html` — grid view, sort + density slider, region/lang/category filters, RED glowing LIVE pill
+
+## Run
+
+```
+npm i
+node server.js
+# → http://localhost:9788
+```
+
+## Live detection
+
+The checker hits `https://www.youtube.com/channel/<id>/live`. If the response HTML
+contains `hlsManifestUrl` (or `"isLive":true` / `"isLiveNow":true`), the outlet is
+flagged live and the LIVE pill links to that exact stream.
diff --git a/data/outlets.json b/data/outlets.json
new file mode 100644
index 0000000..040645c
--- /dev/null
+++ b/data/outlets.json
@@ -0,0 +1,1374 @@
+[
+  {
+    "id": "cnn",
+    "name": "CNN",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.cnn.com",
+    "logo": "https://logo.clearbit.com/cnn.com",
+    "youtube": "UCupvZG-5ko_eiXAupbDfxWw",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@CNN/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "foxnews",
+    "name": "Fox News",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.foxnews.com",
+    "logo": "https://logo.clearbit.com/foxnews.com",
+    "youtube": "UCXIJgqnII2ZOINSWNOGFThA",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@FoxNews/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "msnbc",
+    "name": "MSNBC",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.msnbc.com",
+    "logo": "https://logo.clearbit.com/msnbc.com",
+    "youtube": "UCaXkIU1QidjPwiAYu6GcHjg",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@msnbc/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "nbcnews",
+    "name": "NBC News",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.nbcnews.com",
+    "logo": "https://logo.clearbit.com/nbcnews.com",
+    "youtube": "UCeY0bbntWzzVIaj2z3QigXg",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@NBCNews/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "cbsnews",
+    "name": "CBS News",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.cbsnews.com",
+    "logo": "https://logo.clearbit.com/cbsnews.com",
+    "youtube": "UC8p1vwvWtl6T73JiExfWs1g",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@cbsnews/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "abcnews",
+    "name": "ABC News",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://abcnews.go.com",
+    "logo": "https://logo.clearbit.com/abcnews.go.com",
+    "youtube": "UCBi2mrWuNuyYy4gbM6fU18Q",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@ABCNews/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "pbsnewshour",
+    "name": "PBS NewsHour",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.pbs.org/newshour/",
+    "logo": "https://logo.clearbit.com/pbs.org",
+    "youtube": "UC6ZFN9Tx6xh-skXCuRHCDpQ",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@PBSNewsHour/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "bloomberg",
+    "name": "Bloomberg",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.bloomberg.com",
+    "logo": "https://logo.clearbit.com/bloomberg.com",
+    "youtube": "UCIALMKvObZNtJ6AmdCLP7Lg",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@markets/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "finance"
+  },
+  {
+    "id": "cnbc",
+    "name": "CNBC",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.cnbc.com",
+    "logo": "https://logo.clearbit.com/cnbc.com",
+    "youtube": "UCvJJ_dzjViJCoLf5uKUTwoA",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@CNBCtelevision/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "finance"
+  },
+  {
+    "id": "wsj",
+    "name": "Wall Street Journal",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.wsj.com",
+    "logo": "https://logo.clearbit.com/wsj.com",
+    "youtube": "UCK7tptUDHh-RYDsdxO1-5QQ",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@wsj/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "finance"
+  },
+  {
+    "id": "nytimes",
+    "name": "The New York Times",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.nytimes.com",
+    "logo": "https://logo.clearbit.com/nytimes.com",
+    "youtube": "UCqnbDFdCpuN8CMEg0VuEBqA",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "washingtonpost",
+    "name": "The Washington Post",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.washingtonpost.com",
+    "logo": "https://logo.clearbit.com/washingtonpost.com",
+    "youtube": "UCHd62-u_v4DvJ8TCFtpi4GA",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@washingtonpost/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "usatoday",
+    "name": "USA Today",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.usatoday.com",
+    "logo": "https://logo.clearbit.com/usatoday.com",
+    "youtube": "UCP6HGa63sBC7-KHtkme-p-g",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "latimes",
+    "name": "Los Angeles Times",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.latimes.com",
+    "logo": "https://logo.clearbit.com/latimes.com",
+    "youtube": "UCTcyDLlNDvFKDgCt2tCnFog",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "npr",
+    "name": "NPR",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.npr.org",
+    "logo": "https://logo.clearbit.com/npr.org",
+    "youtube": "UCQobI8sZHKvm9OdykIyEoVw",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "axios",
+    "name": "Axios",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.axios.com",
+    "logo": "https://logo.clearbit.com/axios.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "politico",
+    "name": "Politico",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.politico.com",
+    "logo": "https://logo.clearbit.com/politico.com",
+    "youtube": "UC0E08e_oPxNZL_3Hd-yz1ZA",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "politics"
+  },
+  {
+    "id": "thehill",
+    "name": "The Hill",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://thehill.com",
+    "logo": "https://logo.clearbit.com/thehill.com",
+    "youtube": "UCEHvjJWoVPdMl2Iy4iqAg1A",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "politics"
+  },
+  {
+    "id": "ap",
+    "name": "Associated Press",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://apnews.com",
+    "logo": "https://logo.clearbit.com/apnews.com",
+    "youtube": "UC52X5wxOL_s5yw0dQk7NtgA",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "newswire"
+  },
+  {
+    "id": "reuters",
+    "name": "Reuters",
+    "country": "UK",
+    "flag": "🇬🇧",
+    "href": "https://www.reuters.com",
+    "logo": "https://logo.clearbit.com/reuters.com",
+    "youtube": "UChqUTb7kYRX8-EiaN3XFrSQ",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@Reuters/live",
+    "lang": "en",
+    "region": "Europe",
+    "category": "newswire"
+  },
+  {
+    "id": "bbc",
+    "name": "BBC News",
+    "country": "UK",
+    "flag": "🇬🇧",
+    "href": "https://www.bbc.com/news",
+    "logo": "https://logo.clearbit.com/bbc.com",
+    "youtube": "UC16niRr50-MSBwiO3YDb3RA",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@BBCNews/live",
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "skynews",
+    "name": "Sky News",
+    "country": "UK",
+    "flag": "🇬🇧",
+    "href": "https://news.sky.com",
+    "logo": "https://logo.clearbit.com/news.sky.com",
+    "youtube": "UCoMdktPbSTixAyNGwb-UYkQ",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@SkyNews/live",
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "guardian",
+    "name": "The Guardian",
+    "country": "UK",
+    "flag": "🇬🇧",
+    "href": "https://www.theguardian.com",
+    "logo": "https://logo.clearbit.com/theguardian.com",
+    "youtube": "UCIRYBXDze5krPDzAEOxFGVA",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@guardian/live",
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "times",
+    "name": "The Times",
+    "country": "UK",
+    "flag": "🇬🇧",
+    "href": "https://www.thetimes.com",
+    "logo": "https://logo.clearbit.com/thetimes.com",
+    "youtube": "UCRl0SXuLfm5gKaWPjmIxF-w",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "telegraph",
+    "name": "The Telegraph",
+    "country": "UK",
+    "flag": "🇬🇧",
+    "href": "https://www.telegraph.co.uk",
+    "logo": "https://logo.clearbit.com/telegraph.co.uk",
+    "youtube": "UCpVm7bg6pXKo1Pr6k5kxG9A",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@TheTelegraph/live",
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "ft",
+    "name": "Financial Times",
+    "country": "UK",
+    "flag": "🇬🇧",
+    "href": "https://www.ft.com",
+    "logo": "https://logo.clearbit.com/ft.com",
+    "youtube": "UCKZJWvV5RBxKLNPU2_v9rgg",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Europe",
+    "category": "finance"
+  },
+  {
+    "id": "economist",
+    "name": "The Economist",
+    "country": "UK",
+    "flag": "🇬🇧",
+    "href": "https://www.economist.com",
+    "logo": "https://logo.clearbit.com/economist.com",
+    "youtube": "UC0p5jTq6Xx_DosDFxVXnWaQ",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "dailymail",
+    "name": "Daily Mail",
+    "country": "UK",
+    "flag": "🇬🇧",
+    "href": "https://www.dailymail.co.uk",
+    "logo": "https://logo.clearbit.com/dailymail.co.uk",
+    "youtube": "UC1Z3jOyzVgcEwY8Hk5TQJ2g",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "independent",
+    "name": "The Independent",
+    "country": "UK",
+    "flag": "🇬🇧",
+    "href": "https://www.independent.co.uk",
+    "logo": "https://logo.clearbit.com/independent.co.uk",
+    "youtube": "UCXIJ2-RoAgyx2pc3kIcXY0Q",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "channel4news",
+    "name": "Channel 4 News",
+    "country": "UK",
+    "flag": "🇬🇧",
+    "href": "https://www.channel4.com/news",
+    "logo": "https://logo.clearbit.com/channel4.com",
+    "youtube": "UCRfFR6ZL-DyR82RVE_0EHsg",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "aljazeera",
+    "name": "Al Jazeera English",
+    "country": "QA",
+    "flag": "🇶🇦",
+    "href": "https://www.aljazeera.com",
+    "logo": "https://logo.clearbit.com/aljazeera.com",
+    "youtube": "UCNye-wNBqNL5ZzHSJj3l8Bg",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@aljazeeraenglish/live",
+    "lang": "en",
+    "region": "Middle East",
+    "category": "general"
+  },
+  {
+    "id": "france24",
+    "name": "France 24",
+    "country": "FR",
+    "flag": "🇫🇷",
+    "href": "https://www.france24.com/en/",
+    "logo": "https://logo.clearbit.com/france24.com",
+    "youtube": "UCQfwfsi5VrQ8yKZ-UWmAEFg",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@FRANCE24English/live",
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "dw",
+    "name": "DW News",
+    "country": "DE",
+    "flag": "🇩🇪",
+    "href": "https://www.dw.com",
+    "logo": "https://logo.clearbit.com/dw.com",
+    "youtube": "UCknLrEdhRCp1aegoMqRaCZg",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@dwnews/live",
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "euronews",
+    "name": "Euronews",
+    "country": "EU",
+    "flag": "🇪🇺",
+    "href": "https://www.euronews.com",
+    "logo": "https://logo.clearbit.com/euronews.com",
+    "youtube": "UCSrZ3UV4jOidv8ppoVuvW9Q",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@euronews/live",
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "rt",
+    "name": "RT (Russia Today)",
+    "country": "RU",
+    "flag": "🇷🇺",
+    "href": "https://www.rt.com",
+    "logo": "https://logo.clearbit.com/rt.com",
+    "youtube": "UCpwvZwUam-URkxB7g4USKpg",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "spiegel",
+    "name": "Der Spiegel",
+    "country": "DE",
+    "flag": "🇩🇪",
+    "href": "https://www.spiegel.de",
+    "logo": "https://logo.clearbit.com/spiegel.de",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "de",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "lemonde",
+    "name": "Le Monde",
+    "country": "FR",
+    "flag": "🇫🇷",
+    "href": "https://www.lemonde.fr",
+    "logo": "https://logo.clearbit.com/lemonde.fr",
+    "youtube": "UCnFnzKHwxjEx5dQ-Hw6T1Lw",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "fr",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "lefigaro",
+    "name": "Le Figaro",
+    "country": "FR",
+    "flag": "🇫🇷",
+    "href": "https://www.lefigaro.fr",
+    "logo": "https://logo.clearbit.com/lefigaro.fr",
+    "youtube": "UC1pdaJL7tOmkvAfBp-ZaiUg",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "fr",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "elpais",
+    "name": "El País",
+    "country": "ES",
+    "flag": "🇪🇸",
+    "href": "https://elpais.com",
+    "logo": "https://logo.clearbit.com/elpais.com",
+    "youtube": "UC4S6Y4qBN5RFcZ1aQAlW9aQ",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "es",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "corriere",
+    "name": "Corriere della Sera",
+    "country": "IT",
+    "flag": "🇮🇹",
+    "href": "https://www.corriere.it",
+    "logo": "https://logo.clearbit.com/corriere.it",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "it",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "tagesschau",
+    "name": "Tagesschau (ARD)",
+    "country": "DE",
+    "flag": "🇩🇪",
+    "href": "https://www.tagesschau.de",
+    "logo": "https://logo.clearbit.com/tagesschau.de",
+    "youtube": "UC5NOEUbkLheQcaaRldYW5GA",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@tagesschau/live",
+    "lang": "de",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "nhk",
+    "name": "NHK World-Japan",
+    "country": "JP",
+    "flag": "🇯🇵",
+    "href": "https://www3.nhk.or.jp/nhkworld/",
+    "logo": "https://logo.clearbit.com/nhk.or.jp",
+    "youtube": "UCSPEjw8F2nQDtmUKPFNF7_A",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@NHKWORLDJAPAN/live",
+    "lang": "en",
+    "region": "Asia",
+    "category": "general"
+  },
+  {
+    "id": "asahi",
+    "name": "Asahi Shimbun",
+    "country": "JP",
+    "flag": "🇯🇵",
+    "href": "https://www.asahi.com",
+    "logo": "https://logo.clearbit.com/asahi.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "ja",
+    "region": "Asia",
+    "category": "general"
+  },
+  {
+    "id": "scmp",
+    "name": "South China Morning Post",
+    "country": "HK",
+    "flag": "🇭🇰",
+    "href": "https://www.scmp.com",
+    "logo": "https://logo.clearbit.com/scmp.com",
+    "youtube": "UC4SUWizzKc1tptprBkWjX2Q",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Asia",
+    "category": "general"
+  },
+  {
+    "id": "cctv",
+    "name": "CGTN (China)",
+    "country": "CN",
+    "flag": "🇨🇳",
+    "href": "https://www.cgtn.com",
+    "logo": "https://logo.clearbit.com/cgtn.com",
+    "youtube": "UCdp4eqkLptbkpYTQ7wp9LRA",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@CGTN/live",
+    "lang": "en",
+    "region": "Asia",
+    "category": "general"
+  },
+  {
+    "id": "straitstimes",
+    "name": "The Straits Times",
+    "country": "SG",
+    "flag": "🇸🇬",
+    "href": "https://www.straitstimes.com",
+    "logo": "https://logo.clearbit.com/straitstimes.com",
+    "youtube": "UC4p_I9eiRewn2KoU-nawrDg",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Asia",
+    "category": "general"
+  },
+  {
+    "id": "channelnewsasia",
+    "name": "CNA (Channel News Asia)",
+    "country": "SG",
+    "flag": "🇸🇬",
+    "href": "https://www.channelnewsasia.com",
+    "logo": "https://logo.clearbit.com/channelnewsasia.com",
+    "youtube": "UCXKDugVmDBVtSV-DPzlT5BA",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@channelnewsasia/live",
+    "lang": "en",
+    "region": "Asia",
+    "category": "general"
+  },
+  {
+    "id": "abcaus",
+    "name": "ABC News (Australia)",
+    "country": "AU",
+    "flag": "🇦🇺",
+    "href": "https://www.abc.net.au/news",
+    "logo": "https://logo.clearbit.com/abc.net.au",
+    "youtube": "UCVgO39Bk5sMo66-6o6Spn6Q",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@ABCNewsIn90Seconds/live",
+    "lang": "en",
+    "region": "Oceania",
+    "category": "general"
+  },
+  {
+    "id": "smh",
+    "name": "Sydney Morning Herald",
+    "country": "AU",
+    "flag": "🇦🇺",
+    "href": "https://www.smh.com.au",
+    "logo": "https://logo.clearbit.com/smh.com.au",
+    "youtube": "UCmlF8rNyAQyx7gO9F0sLE5w",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Oceania",
+    "category": "general"
+  },
+  {
+    "id": "9news",
+    "name": "9 News Australia",
+    "country": "AU",
+    "flag": "🇦🇺",
+    "href": "https://www.9news.com.au",
+    "logo": "https://logo.clearbit.com/9news.com.au",
+    "youtube": "UCK7IIV6Q2junGSdYK3BmZMA",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@9NewsAUS/live",
+    "lang": "en",
+    "region": "Oceania",
+    "category": "general"
+  },
+  {
+    "id": "nzherald",
+    "name": "NZ Herald",
+    "country": "NZ",
+    "flag": "🇳🇿",
+    "href": "https://www.nzherald.co.nz",
+    "logo": "https://logo.clearbit.com/nzherald.co.nz",
+    "youtube": "UCyA-W5h99e1lwrSc7sa1JEw",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Oceania",
+    "category": "general"
+  },
+  {
+    "id": "cbc",
+    "name": "CBC News",
+    "country": "CA",
+    "flag": "🇨🇦",
+    "href": "https://www.cbc.ca/news",
+    "logo": "https://logo.clearbit.com/cbc.ca",
+    "youtube": "UCuFFtHWoLl5fauMMD5Ww2jA",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@CBCNews/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "globalnews",
+    "name": "Global News (Canada)",
+    "country": "CA",
+    "flag": "🇨🇦",
+    "href": "https://globalnews.ca",
+    "logo": "https://logo.clearbit.com/globalnews.ca",
+    "youtube": "UChLtXXpo4Ge1ReTEboVvTDg",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@globalnews/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "globo",
+    "name": "Globo News (Brazil)",
+    "country": "BR",
+    "flag": "🇧🇷",
+    "href": "https://g1.globo.com",
+    "logo": "https://logo.clearbit.com/globo.com",
+    "youtube": "UCo7uVfPnPSlmrJFqJTW1Vyw",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "pt",
+    "region": "South America",
+    "category": "general"
+  },
+  {
+    "id": "folha",
+    "name": "Folha de S.Paulo",
+    "country": "BR",
+    "flag": "🇧🇷",
+    "href": "https://www.folha.uol.com.br",
+    "logo": "https://logo.clearbit.com/folha.uol.com.br",
+    "youtube": "UCNqJoLb5ohR-Jt5LeJlNXvw",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "pt",
+    "region": "South America",
+    "category": "general"
+  },
+  {
+    "id": "clarin",
+    "name": "Clarín",
+    "country": "AR",
+    "flag": "🇦🇷",
+    "href": "https://www.clarin.com",
+    "logo": "https://logo.clearbit.com/clarin.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "es",
+    "region": "South America",
+    "category": "general"
+  },
+  {
+    "id": "lanacion",
+    "name": "La Nación",
+    "country": "AR",
+    "flag": "🇦🇷",
+    "href": "https://www.lanacion.com.ar",
+    "logo": "https://logo.clearbit.com/lanacion.com.ar",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "es",
+    "region": "South America",
+    "category": "general"
+  },
+  {
+    "id": "milenio",
+    "name": "Milenio",
+    "country": "MX",
+    "flag": "🇲🇽",
+    "href": "https://www.milenio.com",
+    "logo": "https://logo.clearbit.com/milenio.com",
+    "youtube": "UC2gKLqOIVDsJaLwBhCNCgmA",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@MilenioTV/live",
+    "lang": "es",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "reforma",
+    "name": "Reforma",
+    "country": "MX",
+    "flag": "🇲🇽",
+    "href": "https://www.reforma.com",
+    "logo": "https://logo.clearbit.com/reforma.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "es",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "hindustantimes",
+    "name": "Hindustan Times",
+    "country": "IN",
+    "flag": "🇮🇳",
+    "href": "https://www.hindustantimes.com",
+    "logo": "https://logo.clearbit.com/hindustantimes.com",
+    "youtube": "UCqOL4u7UQqilUUjQXNkrPaw",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@HindustanTimes/live",
+    "lang": "en",
+    "region": "Asia",
+    "category": "general"
+  },
+  {
+    "id": "thehindu",
+    "name": "The Hindu",
+    "country": "IN",
+    "flag": "🇮🇳",
+    "href": "https://www.thehindu.com",
+    "logo": "https://logo.clearbit.com/thehindu.com",
+    "youtube": "UCJi8M0hRKjz8SLyvObNZjsw",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Asia",
+    "category": "general"
+  },
+  {
+    "id": "ndtv",
+    "name": "NDTV",
+    "country": "IN",
+    "flag": "🇮🇳",
+    "href": "https://www.ndtv.com",
+    "logo": "https://logo.clearbit.com/ndtv.com",
+    "youtube": "UCZFMm1mMw0F81Z37aaEzTUA",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@ndtv/live",
+    "lang": "en",
+    "region": "Asia",
+    "category": "general"
+  },
+  {
+    "id": "indiatoday",
+    "name": "India Today",
+    "country": "IN",
+    "flag": "🇮🇳",
+    "href": "https://www.indiatoday.in",
+    "logo": "https://logo.clearbit.com/indiatoday.in",
+    "youtube": "UCYPvAwZP8pZhSMW8qs7cVCw",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@IndiaToday/live",
+    "lang": "en",
+    "region": "Asia",
+    "category": "general"
+  },
+  {
+    "id": "wionews",
+    "name": "WION",
+    "country": "IN",
+    "flag": "🇮🇳",
+    "href": "https://www.wionews.com",
+    "logo": "https://logo.clearbit.com/wionews.com",
+    "youtube": "UC_gUM8rL-Lrg6O3adPW9K1g",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@WION/live",
+    "lang": "en",
+    "region": "Asia",
+    "category": "general"
+  },
+  {
+    "id": "haaretz",
+    "name": "Haaretz",
+    "country": "IL",
+    "flag": "🇮🇱",
+    "href": "https://www.haaretz.com",
+    "logo": "https://logo.clearbit.com/haaretz.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "Middle East",
+    "category": "general"
+  },
+  {
+    "id": "jpost",
+    "name": "Jerusalem Post",
+    "country": "IL",
+    "flag": "🇮🇱",
+    "href": "https://www.jpost.com",
+    "logo": "https://logo.clearbit.com/jpost.com",
+    "youtube": "UCC_LolHN2pAAg-yMjyu6Cww",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Middle East",
+    "category": "general"
+  },
+  {
+    "id": "i24",
+    "name": "i24NEWS",
+    "country": "IL",
+    "flag": "🇮🇱",
+    "href": "https://www.i24news.tv",
+    "logo": "https://logo.clearbit.com/i24news.tv",
+    "youtube": "UC5dQbXmDP6tDfYrBxbF6vIA",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@i24NEWSEnglish/live",
+    "lang": "en",
+    "region": "Middle East",
+    "category": "general"
+  },
+  {
+    "id": "trtworld",
+    "name": "TRT World",
+    "country": "TR",
+    "flag": "🇹🇷",
+    "href": "https://www.trtworld.com",
+    "logo": "https://logo.clearbit.com/trtworld.com",
+    "youtube": "UC7fWeaHhqgM4Ry-RMpM2YYw",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@trtworld/live",
+    "lang": "en",
+    "region": "Middle East",
+    "category": "general"
+  },
+  {
+    "id": "arabnews",
+    "name": "Arab News",
+    "country": "SA",
+    "flag": "🇸🇦",
+    "href": "https://www.arabnews.com",
+    "logo": "https://logo.clearbit.com/arabnews.com",
+    "youtube": "UC0EuvBHMyJpZbDsx3GdyAcQ",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Middle East",
+    "category": "general"
+  },
+  {
+    "id": "khaleej",
+    "name": "Khaleej Times",
+    "country": "AE",
+    "flag": "🇦🇪",
+    "href": "https://www.khaleejtimes.com",
+    "logo": "https://logo.clearbit.com/khaleejtimes.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "Middle East",
+    "category": "general"
+  },
+  {
+    "id": "thenational",
+    "name": "The National (UAE)",
+    "country": "AE",
+    "flag": "🇦🇪",
+    "href": "https://www.thenationalnews.com",
+    "logo": "https://logo.clearbit.com/thenationalnews.com",
+    "youtube": "UCNGOLOZULAOQRO1JmEFmvSw",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Middle East",
+    "category": "general"
+  },
+  {
+    "id": "newsafrica",
+    "name": "Africa News",
+    "country": "AFRICA",
+    "flag": "🌍",
+    "href": "https://www.africanews.com",
+    "logo": "https://logo.clearbit.com/africanews.com",
+    "youtube": "UC2tA1l9NTbX1n7CmS4hF8Bw",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@africanews/live",
+    "lang": "en",
+    "region": "Africa",
+    "category": "general"
+  },
+  {
+    "id": "sabc",
+    "name": "SABC News",
+    "country": "ZA",
+    "flag": "🇿🇦",
+    "href": "https://www.sabcnews.com",
+    "logo": "https://logo.clearbit.com/sabcnews.com",
+    "youtube": "UCb-zh5xWuJJkjVPe7NPiyAQ",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@SABCNews/live",
+    "lang": "en",
+    "region": "Africa",
+    "category": "general"
+  },
+  {
+    "id": "enca",
+    "name": "eNCA",
+    "country": "ZA",
+    "flag": "🇿🇦",
+    "href": "https://www.enca.com",
+    "logo": "https://logo.clearbit.com/enca.com",
+    "youtube": "UCPyiBnHvBwlbX-3sSZx4qOg",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@enca/live",
+    "lang": "en",
+    "region": "Africa",
+    "category": "general"
+  },
+  {
+    "id": "vox",
+    "name": "Vox",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.vox.com",
+    "logo": "https://logo.clearbit.com/vox.com",
+    "youtube": "UCLXo7UDZvByw2ixzpQCufnA",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "vice",
+    "name": "VICE News",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.vice.com/en/section/news",
+    "logo": "https://logo.clearbit.com/vice.com",
+    "youtube": "UCZaT_X_mc0BI-djXOlfhqWQ",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "newsy",
+    "name": "Scripps News",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://scrippsnews.com",
+    "logo": "https://logo.clearbit.com/scrippsnews.com",
+    "youtube": "UCDR3lkqdjcRC4LkBYRkQzaQ",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@scrippsnews/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "ntdca",
+    "name": "NTD",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.ntd.com",
+    "logo": "https://logo.clearbit.com/ntd.com",
+    "youtube": "UC7nLfpz1iyu6CDOlXkJpA1Q",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@NTDNews/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "newsmax",
+    "name": "Newsmax",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.newsmax.com",
+    "logo": "https://logo.clearbit.com/newsmax.com",
+    "youtube": "UCx6h-dWzJ5NpAlja1YsApdg",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@NewsmaxTV/live",
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "oan",
+    "name": "OAN",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.oann.com",
+    "logo": "https://logo.clearbit.com/oann.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "thedaily",
+    "name": "The Daily Wire",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.dailywire.com",
+    "logo": "https://logo.clearbit.com/dailywire.com",
+    "youtube": "UCaeO5vkdj5xOQHp4UmIN6dw",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "thedrive",
+    "name": "The Daily Beast",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.thedailybeast.com",
+    "logo": "https://logo.clearbit.com/thedailybeast.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "huffpost",
+    "name": "HuffPost",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.huffpost.com",
+    "logo": "https://logo.clearbit.com/huffpost.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "theintercept",
+    "name": "The Intercept",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://theintercept.com",
+    "logo": "https://logo.clearbit.com/theintercept.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "propublica",
+    "name": "ProPublica",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.propublica.org",
+    "logo": "https://logo.clearbit.com/propublica.org",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "semafor",
+    "name": "Semafor",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.semafor.com",
+    "logo": "https://logo.clearbit.com/semafor.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "theatlantic",
+    "name": "The Atlantic",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.theatlantic.com",
+    "logo": "https://logo.clearbit.com/theatlantic.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "newyorker",
+    "name": "The New Yorker",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://www.newyorker.com",
+    "logo": "https://logo.clearbit.com/newyorker.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "yahoonews",
+    "name": "Yahoo News",
+    "country": "US",
+    "flag": "🇺🇸",
+    "href": "https://news.yahoo.com",
+    "logo": "https://logo.clearbit.com/yahoo.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "North America",
+    "category": "general"
+  },
+  {
+    "id": "thelocal",
+    "name": "The Local (Europe)",
+    "country": "EU",
+    "flag": "🇪🇺",
+    "href": "https://www.thelocal.com",
+    "logo": "https://logo.clearbit.com/thelocal.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "themoscowtimes",
+    "name": "The Moscow Times",
+    "country": "RU",
+    "flag": "🇷🇺",
+    "href": "https://www.themoscowtimes.com",
+    "logo": "https://logo.clearbit.com/themoscowtimes.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "kyivindependent",
+    "name": "Kyiv Independent",
+    "country": "UA",
+    "flag": "🇺🇦",
+    "href": "https://kyivindependent.com",
+    "logo": "https://logo.clearbit.com/kyivindependent.com",
+    "youtube": "UCi-NU5OqfP-c8kFGsXXxQbA",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "ukrayinska",
+    "name": "Ukrainska Pravda",
+    "country": "UA",
+    "flag": "🇺🇦",
+    "href": "https://www.pravda.com.ua",
+    "logo": "https://logo.clearbit.com/pravda.com.ua",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "uk",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "irishtimes",
+    "name": "The Irish Times",
+    "country": "IE",
+    "flag": "🇮🇪",
+    "href": "https://www.irishtimes.com",
+    "logo": "https://logo.clearbit.com/irishtimes.com",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "rte",
+    "name": "RTÉ News (Ireland)",
+    "country": "IE",
+    "flag": "🇮🇪",
+    "href": "https://www.rte.ie/news/",
+    "logo": "https://logo.clearbit.com/rte.ie",
+    "youtube": "UCYHigQOIfH2hbgcUm0F38_g",
+    "liveCheck": "youtube",
+    "liveStream": "https://www.youtube.com/@rtenews/live",
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "thejournal",
+    "name": "TheJournal.ie",
+    "country": "IE",
+    "flag": "🇮🇪",
+    "href": "https://www.thejournal.ie",
+    "logo": "https://logo.clearbit.com/thejournal.ie",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "rfi",
+    "name": "RFI",
+    "country": "FR",
+    "flag": "🇫🇷",
+    "href": "https://www.rfi.fr/en/",
+    "logo": "https://logo.clearbit.com/rfi.fr",
+    "youtube": null,
+    "liveCheck": null,
+    "liveStream": null,
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  },
+  {
+    "id": "bbcworld",
+    "name": "BBC World Service",
+    "country": "UK",
+    "flag": "🇬🇧",
+    "href": "https://www.bbc.com/news/world",
+    "logo": "https://logo.clearbit.com/bbc.com",
+    "youtube": "UC76xPagknI0lI4Z2QbSiP4w",
+    "liveCheck": "youtube",
+    "liveStream": null,
+    "lang": "en",
+    "region": "Europe",
+    "category": "general"
+  }
+]
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..6209578
--- /dev/null
+++ b/package.json
@@ -0,0 +1,17 @@
+{
+  "name": "allnewsdaily",
+  "version": "0.1.0",
+  "description": "All News Daily — global news outlet directory with live-broadcast indicators",
+  "main": "server.js",
+  "scripts": {
+    "start": "node server.js",
+    "dev": "node server.js",
+    "check-live": "node scripts/check-live.js"
+  },
+  "dependencies": {
+    "express": "^4.21.1"
+  },
+  "engines": {
+    "node": ">=18"
+  }
+}
diff --git a/public/index.html b/public/index.html
new file mode 100644
index 0000000..95e0da5
--- /dev/null
+++ b/public/index.html
@@ -0,0 +1,297 @@
+<!doctype html>
+<html lang="en">
+<head>
+  <meta charset="utf-8" />
+  <meta name="viewport" content="width=device-width,initial-scale=1" />
+  <title>All News Daily — global newsroom directory</title>
+  <meta name="description" content="Every major news outlet in the world — logo, link, and a red LIVE pill that glows when the network is broadcasting live." />
+  <link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='12' fill='%23dc2626'/%3E%3Ctext x='50%25' y='52%25' dominant-baseline='middle' text-anchor='middle' font-family='ui-sans-serif' font-weight='800' font-size='30' fill='white'%3EN%3C/text%3E%3C/svg%3E" />
+  <style>
+    :root {
+      --bg: #0b0c10;
+      --bg-card: #14161c;
+      --bg-card-hover: #1c1f28;
+      --fg: #e7e9ee;
+      --fg-dim: #8b91a0;
+      --border: #232733;
+      --accent: #dc2626;
+      --accent-glow: rgba(220, 38, 38, .55);
+      --cols: 6;
+    }
+    @media (prefers-color-scheme: light) {
+      :root {
+        --bg: #f6f7f9;
+        --bg-card: #ffffff;
+        --bg-card-hover: #fbfbfc;
+        --fg: #14161c;
+        --fg-dim: #5b6273;
+        --border: #e3e5ec;
+      }
+    }
+    * { box-sizing: border-box; }
+    html, body { margin: 0; padding: 0; background: var(--bg); color: var(--fg); font-family: -apple-system, BlinkMacSystemFont, "Inter", "SF Pro Text", system-ui, sans-serif; -webkit-font-smoothing: antialiased; }
+    header { position: sticky; top: 0; z-index: 10; background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: saturate(180%) blur(12px); border-bottom: 1px solid var(--border); }
+    .bar { display: flex; align-items: center; gap: 14px; padding: 12px 20px; flex-wrap: wrap; }
+    .brand { display: flex; align-items: center; gap: 10px; font-weight: 800; letter-spacing: -.01em; font-size: 18px; }
+    .brand .dot { width: 12px; height: 12px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent); animation: pulse 1.4s ease-in-out infinite; }
+    .brand small { font-weight: 500; color: var(--fg-dim); letter-spacing: 0; margin-left: 4px; font-size: 12px; }
+    .controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-left: auto; }
+    .controls select, .controls input[type="search"], .controls input[type="range"] { background: var(--bg-card); color: var(--fg); border: 1px solid var(--border); border-radius: 8px; padding: 7px 10px; font: inherit; font-size: 13px; }
+    .controls label { font-size: 12px; color: var(--fg-dim); display: flex; align-items: center; gap: 6px; }
+    .controls input[type="search"] { width: 200px; }
+    .controls input[type="range"] { padding: 0; width: 120px; vertical-align: middle; }
+    .controls .chip { padding: 5px 9px; border-radius: 999px; border: 1px solid var(--border); font-size: 12px; color: var(--fg-dim); }
+    .controls .chip strong { color: var(--fg); font-weight: 700; }
+    .controls .chip .live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); display: inline-block; vertical-align: middle; margin-right: 6px; box-shadow: 0 0 0 3px var(--accent-glow); animation: pulse 1.4s ease-in-out infinite; }
+
+    main { padding: 20px; }
+    .grid { display: grid; grid-template-columns: repeat(var(--cols), minmax(0, 1fr)); gap: 14px; }
+
+    .card {
+      position: relative;
+      background: var(--bg-card);
+      border: 1px solid var(--border);
+      border-radius: 12px;
+      padding: 18px 14px 14px;
+      display: flex;
+      flex-direction: column;
+      align-items: center;
+      justify-content: flex-start;
+      text-align: center;
+      gap: 10px;
+      transition: transform .12s ease, background .12s ease, border-color .12s ease, box-shadow .12s ease;
+      min-height: 160px;
+    }
+    .card:hover { background: var(--bg-card-hover); transform: translateY(-2px); border-color: color-mix(in srgb, var(--accent) 30%, var(--border)); }
+    .card a.outlet-link { color: inherit; text-decoration: none; display: contents; }
+    .card .logo-wrap {
+      width: 88px; height: 88px;
+      border-radius: 14px;
+      background: #fff;
+      border: 1px solid var(--border);
+      display: flex; align-items: center; justify-content: center;
+      overflow: hidden;
+      box-shadow: 0 1px 0 rgba(255,255,255,.04) inset;
+    }
+    .card .logo-wrap img { max-width: 78%; max-height: 78%; object-fit: contain; }
+    .card .logo-wrap.placeholder { background: linear-gradient(135deg, #1c1f28, #2a2e3a); color: #fff; font-weight: 800; font-size: 26px; letter-spacing: -.02em; }
+    .card .meta { display: flex; flex-direction: column; gap: 2px; align-items: center; min-height: 38px; }
+    .card .name { font-weight: 700; font-size: 14px; letter-spacing: -.01em; line-height: 1.15; }
+    .card .sub { color: var(--fg-dim); font-size: 11.5px; display: flex; align-items: center; gap: 5px; }
+    .card .flag { font-size: 14px; line-height: 1; }
+    .card .badge-row { position: absolute; top: 8px; left: 8px; right: 8px; display: flex; justify-content: space-between; align-items: center; pointer-events: none; }
+
+    /* The LIVE pill — RED, GLOWING when on live */
+    .live-pill {
+      pointer-events: auto;
+      display: inline-flex; align-items: center; gap: 5px;
+      padding: 3px 8px 3px 7px;
+      font-size: 10.5px; font-weight: 800; letter-spacing: .08em;
+      color: #fff; background: var(--accent);
+      border-radius: 999px;
+      text-transform: uppercase; text-decoration: none;
+      box-shadow: 0 0 0 3px var(--accent-glow), 0 0 18px 4px var(--accent-glow);
+      animation: pulse 1.4s ease-in-out infinite;
+    }
+    .live-pill::before {
+      content: ""; width: 6px; height: 6px; border-radius: 50%; background: #fff;
+      box-shadow: 0 0 6px #fff;
+    }
+    @keyframes pulse {
+      0%, 100% { box-shadow: 0 0 0 3px var(--accent-glow), 0 0 18px 4px var(--accent-glow); }
+      50%      { box-shadow: 0 0 0 6px color-mix(in srgb, var(--accent) 45%, transparent), 0 0 26px 10px var(--accent-glow); }
+    }
+    .card.is-live { border-color: color-mix(in srgb, var(--accent) 55%, var(--border)); }
+    .card.is-live::after {
+      content: ""; position: absolute; inset: -1px; border-radius: 13px; pointer-events: none;
+      box-shadow: 0 0 22px 1px color-mix(in srgb, var(--accent) 35%, transparent);
+    }
+
+    .empty { padding: 60px 20px; text-align: center; color: var(--fg-dim); }
+    footer { color: var(--fg-dim); font-size: 12px; padding: 24px 20px 40px; text-align: center; }
+    footer a { color: var(--fg-dim); }
+
+    @media (max-width: 1100px) { :root { --cols: 5; } }
+    @media (max-width: 900px)  { :root { --cols: 4; } }
+    @media (max-width: 680px)  { :root { --cols: 3; } .controls input[type="search"] { width: 140px; } }
+    @media (max-width: 460px)  { :root { --cols: 2; } }
+  </style>
+</head>
+<body>
+  <header>
+    <div class="bar">
+      <div class="brand"><span class="dot"></span>All News Daily<small>global newsroom directory</small></div>
+      <div class="controls">
+        <input id="q" type="search" placeholder="Search outlet / country…" autocomplete="off" />
+        <select id="region"><option value="">All regions</option></select>
+        <select id="lang"><option value="">All languages</option></select>
+        <select id="category"><option value="">All categories</option></select>
+        <select id="sort">
+          <option value="default">Sort: Default</option>
+          <option value="live">Live first</option>
+          <option value="name">Name A→Z</option>
+          <option value="country">Country A→Z</option>
+          <option value="region">Region A→Z</option>
+        </select>
+        <label>Density <input id="cols" type="range" min="3" max="10" value="6" /></label>
+        <span class="chip"><span class="live-dot"></span><strong id="liveCount">0</strong>&nbsp;live now</span>
+        <span class="chip"><strong id="totalCount">0</strong>&nbsp;outlets</span>
+      </div>
+    </div>
+  </header>
+
+  <main>
+    <section id="grid" class="grid"></section>
+    <div id="empty" class="empty" hidden>No outlets match those filters.</div>
+  </main>
+
+  <footer>
+    Live status refreshes every ~90s · click a logo to visit the outlet · click <span class="live-pill" style="animation:none">LIVE</span> to watch the live stream
+  </footer>
+
+  <script>
+    const grid = document.getElementById('grid');
+    const empty = document.getElementById('empty');
+    const $ = id => document.getElementById(id);
+
+    const state = {
+      outlets: [],
+      filters: { q: '', region: '', lang: '', category: '', sort: 'default' },
+      cols: parseInt(localStorage.getItem('and.cols') || '6', 10),
+    };
+
+    function applyCols(n) {
+      n = Math.max(3, Math.min(10, Number(n) || 6));
+      state.cols = n;
+      document.documentElement.style.setProperty('--cols', String(n));
+      localStorage.setItem('and.cols', String(n));
+      const slider = $('cols');
+      if (slider && Number(slider.value) !== n) slider.value = String(n);
+    }
+
+    function initials(name) {
+      return name
+        .replace(/\(.*?\)/g, '')
+        .split(/\s+/)
+        .filter(Boolean)
+        .slice(0, 2)
+        .map(s => s[0])
+        .join('')
+        .toUpperCase();
+    }
+
+    function logoHtml(o) {
+      if (!o.logo) return `<div class="logo-wrap placeholder">${initials(o.name)}</div>`;
+      return `<div class="logo-wrap"><img loading="lazy" decoding="async" alt="${o.name} logo" src="${o.logo}" onerror="this.parentElement.classList.add('placeholder');this.parentElement.innerHTML='${initials(o.name)}';"></div>`;
+    }
+
+    function cardHtml(o) {
+      const liveHref = o.liveVideoId
+        ? `https://www.youtube.com/watch?v=${o.liveVideoId}`
+        : o.liveStream || o.href;
+      const livePill = o.isLive
+        ? `<a class="live-pill" href="${liveHref}" target="_blank" rel="noopener" title="Watch ${o.name} live">Live</a>`
+        : '';
+      return `
+        <article class="card ${o.isLive ? 'is-live' : ''}" data-id="${o.id}">
+          <div class="badge-row">
+            <span></span>
+            ${livePill}
+          </div>
+          <a class="outlet-link" href="${o.href}" target="_blank" rel="noopener" title="Visit ${o.name}">
+            ${logoHtml(o)}
+            <div class="meta">
+              <div class="name">${o.name}</div>
+              <div class="sub"><span class="flag">${o.flag || ''}</span><span>${o.country} · ${o.region}</span></div>
+            </div>
+          </a>
+        </article>
+      `;
+    }
+
+    function render() {
+      const f = state.filters;
+      let list = state.outlets.slice();
+      if (f.q) {
+        const q = f.q.toLowerCase();
+        list = list.filter(o =>
+          o.name.toLowerCase().includes(q) ||
+          (o.country && o.country.toLowerCase().includes(q)) ||
+          (o.region && o.region.toLowerCase().includes(q)) ||
+          (o.lang && o.lang.toLowerCase().includes(q))
+        );
+      }
+      if (f.region) list = list.filter(o => o.region === f.region);
+      if (f.lang) list = list.filter(o => o.lang === f.lang);
+      if (f.category) list = list.filter(o => o.category === f.category);
+
+      if (f.sort === 'live') {
+        list.sort((a, b) => (b.isLive - a.isLive) || a.name.localeCompare(b.name));
+      } else if (f.sort === 'name') {
+        list.sort((a, b) => a.name.localeCompare(b.name));
+      } else if (f.sort === 'country') {
+        list.sort((a, b) => a.country.localeCompare(b.country) || a.name.localeCompare(b.name));
+      } else if (f.sort === 'region') {
+        list.sort((a, b) => a.region.localeCompare(b.region) || a.name.localeCompare(b.name));
+      } else {
+        // default — live first, then by name
+        list.sort((a, b) => (b.isLive - a.isLive) || a.name.localeCompare(b.name));
+      }
+
+      grid.innerHTML = list.map(cardHtml).join('');
+      empty.hidden = list.length > 0;
+
+      const liveCount = state.outlets.filter(o => o.isLive).length;
+      $('liveCount').textContent = liveCount;
+      $('totalCount').textContent = state.outlets.length;
+    }
+
+    function populateSelect(id, values, label) {
+      const el = $(id);
+      const cur = el.value;
+      el.innerHTML = `<option value="">${label}</option>` + values.map(v => `<option value="${v}">${v}</option>`).join('');
+      el.value = cur;
+    }
+
+    async function loadOutlets() {
+      try {
+        const r = await fetch('/api/outlets', { cache: 'no-store' });
+        const j = await r.json();
+        state.outlets = j.outlets || [];
+
+        const regions = [...new Set(state.outlets.map(o => o.region).filter(Boolean))].sort();
+        const langs = [...new Set(state.outlets.map(o => o.lang).filter(Boolean))].sort();
+        const cats = [...new Set(state.outlets.map(o => o.category).filter(Boolean))].sort();
+        populateSelect('region', regions, 'All regions');
+        populateSelect('lang', langs, 'All languages');
+        populateSelect('category', cats, 'All categories');
+
+        render();
+      } catch (e) {
+        console.error('loadOutlets failed', e);
+      }
+    }
+
+    function bindControls() {
+      $('q').addEventListener('input', e => { state.filters.q = e.target.value.trim(); render(); });
+      $('region').addEventListener('change', e => { state.filters.region = e.target.value; render(); });
+      $('lang').addEventListener('change', e => { state.filters.lang = e.target.value; render(); });
+      $('category').addEventListener('change', e => { state.filters.category = e.target.value; render(); });
+      $('sort').addEventListener('change', e => {
+        state.filters.sort = e.target.value;
+        localStorage.setItem('and.sort', e.target.value);
+        render();
+      });
+      $('cols').addEventListener('input', e => applyCols(e.target.value));
+
+      const savedSort = localStorage.getItem('and.sort');
+      if (savedSort) { $('sort').value = savedSort; state.filters.sort = savedSort; }
+      applyCols(state.cols);
+    }
+
+    bindControls();
+    loadOutlets();
+    setInterval(loadOutlets, 30000);  // refresh live status every 30s on the client
+  </script>
+</body>
+</html>
diff --git a/scripts/check-live.js b/scripts/check-live.js
new file mode 100644
index 0000000..9bff5ab
--- /dev/null
+++ b/scripts/check-live.js
@@ -0,0 +1,134 @@
+#!/usr/bin/env node
+// Polls each outlet's YouTube /channel/<id>/live URL.
+// YouTube returns the channel's currently-live broadcast page (200 + watch-page HTML)
+// or a "no live stream" placeholder. We detect liveness by looking for the
+// hlsManifestUrl / "isLive":true marker in the inline ytInitialPlayerResponse JSON.
+//
+// Writes data/live-status.json keyed by outlet.id:
+//   { "<id>": { isLive: bool, videoId: "...", checkedAt: ISO, error?: "..." } }
+
+const fs = require('fs');
+const path = require('path');
+const https = require('https');
+
+const OUTLETS_PATH = path.join(__dirname, '..', 'data', 'outlets.json');
+const STATUS_PATH = path.join(__dirname, '..', 'data', 'live-status.json');
+
+const UA =
+  'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 ' +
+  '(KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36';
+
+function fetchHtml(url, timeoutMs = 8000) {
+  return new Promise((resolve, reject) => {
+    const req = https.get(
+      url,
+      {
+        headers: {
+          'User-Agent': UA,
+          'Accept-Language': 'en-US,en;q=0.9',
+          Accept: 'text/html,application/xhtml+xml',
+        },
+      },
+      res => {
+        if (res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
+          // Follow one hop
+          fetchHtml(new URL(res.headers.location, url).toString(), timeoutMs).then(resolve, reject);
+          res.resume();
+          return;
+        }
+        if (res.statusCode !== 200) {
+          res.resume();
+          return reject(new Error('status ' + res.statusCode));
+        }
+        let body = '';
+        res.setEncoding('utf8');
+        res.on('data', d => {
+          body += d;
+          if (body.length > 4_000_000) {
+            req.destroy(new Error('body too big'));
+          }
+        });
+        res.on('end', () => resolve(body));
+      }
+    );
+    req.setTimeout(timeoutMs, () => req.destroy(new Error('timeout')));
+    req.on('error', reject);
+  });
+}
+
+function detectLive(html) {
+  // Marker 1 — hls manifest is present only when actively streaming
+  if (/"hlsManifestUrl":\s*"https:\/\/manifest\.googlevideo\.com/.test(html)) {
+    const m = html.match(/"videoId":\s*"([A-Za-z0-9_-]{11})"/);
+    return { isLive: true, videoId: m ? m[1] : null };
+  }
+  // Marker 2 — isLive:true inside videoDetails
+  if (/"videoDetails":\s*\{[^}]*"isLive":true/.test(html)) {
+    const m = html.match(/"videoId":\s*"([A-Za-z0-9_-]{11})"/);
+    return { isLive: true, videoId: m ? m[1] : null };
+  }
+  // Marker 3 — livestream microformat
+  if (/"liveBroadcastDetails":\s*\{[^}]*"isLiveNow":true/.test(html)) {
+    const m = html.match(/"videoId":\s*"([A-Za-z0-9_-]{11})"/);
+    return { isLive: true, videoId: m ? m[1] : null };
+  }
+  return { isLive: false, videoId: null };
+}
+
+async function checkOne(outlet) {
+  if (outlet.liveCheck !== 'youtube' || !outlet.youtube) {
+    return { id: outlet.id, isLive: false, videoId: null, skipped: true };
+  }
+  const url = `https://www.youtube.com/channel/${outlet.youtube}/live`;
+  try {
+    const html = await fetchHtml(url);
+    const { isLive, videoId } = detectLive(html);
+    return { id: outlet.id, isLive, videoId };
+  } catch (e) {
+    return { id: outlet.id, isLive: false, videoId: null, error: e.message };
+  }
+}
+
+async function runBatch(outlets, concurrency = 6) {
+  const results = {};
+  let idx = 0;
+  async function worker() {
+    while (idx < outlets.length) {
+      const o = outlets[idx++];
+      const r = await checkOne(o);
+      results[o.id] = {
+        isLive: r.isLive,
+        videoId: r.videoId,
+        checkedAt: new Date().toISOString(),
+        ...(r.error ? { error: r.error } : {}),
+      };
+    }
+  }
+  await Promise.all(Array.from({ length: concurrency }, worker));
+  return results;
+}
+
+async function main() {
+  const outlets = JSON.parse(fs.readFileSync(OUTLETS_PATH, 'utf8'));
+  const checkable = outlets.filter(o => o.liveCheck === 'youtube' && o.youtube);
+  console.log(`[check-live] checking ${checkable.length} of ${outlets.length} outlets`);
+  const t0 = Date.now();
+
+  const results = await runBatch(outlets);
+
+  const liveCount = Object.values(results).filter(r => r.isLive).length;
+  console.log(`[check-live] done in ${Date.now() - t0}ms — ${liveCount} live`);
+
+  // Merge with prior so we keep history if any outlet errored this tick
+  let prior = {};
+  try {
+    prior = JSON.parse(fs.readFileSync(STATUS_PATH, 'utf8'));
+  } catch (_) {}
+  const merged = { ...prior, ...results };
+  fs.writeFileSync(STATUS_PATH, JSON.stringify(merged, null, 2));
+}
+
+main().catch(e => {
+  console.error('[check-live] fatal', e);
+  process.exit(1);
+});
diff --git a/server.js b/server.js
new file mode 100644
index 0000000..9803b32
--- /dev/null
+++ b/server.js
@@ -0,0 +1,94 @@
+const express = require('express');
+const fs = require('fs');
+const path = require('path');
+const { spawn } = require('child_process');
+
+const app = express();
+const PORT = process.env.PORT || 9788;
+
+const OUTLETS_PATH = path.join(__dirname, 'data', 'outlets.json');
+const LIVE_STATUS_PATH = path.join(__dirname, 'data', 'live-status.json');
+
+function loadOutlets() {
+  try {
+    return JSON.parse(fs.readFileSync(OUTLETS_PATH, 'utf8'));
+  } catch (e) {
+    console.error('[outlets] load failed', e.message);
+    return [];
+  }
+}
+
+function loadLiveStatus() {
+  try {
+    if (!fs.existsSync(LIVE_STATUS_PATH)) return {};
+    return JSON.parse(fs.readFileSync(LIVE_STATUS_PATH, 'utf8'));
+  } catch (e) {
+    return {};
+  }
+}
+
+function mergeOutlets() {
+  const outlets = loadOutlets();
+  const status = loadLiveStatus();
+  return outlets.map(o => {
+    const s = status[o.id];
+    return {
+      ...o,
+      isLive: s ? !!s.isLive : false,
+      liveVideoId: s && s.videoId ? s.videoId : null,
+      lastCheck: s ? s.checkedAt : null
+    };
+  });
+}
+
+app.use(express.json());
+app.use('/static', express.static(path.join(__dirname, 'public')));
+
+app.get('/', (req, res) => {
+  res.sendFile(path.join(__dirname, 'public', 'index.html'));
+});
+
+app.get('/api/outlets', (req, res) => {
+  res.json({ outlets: mergeOutlets(), updatedAt: new Date().toISOString() });
+});
+
+app.get('/api/health', (req, res) => {
+  const outlets = loadOutlets();
+  const status = loadLiveStatus();
+  const liveCount = Object.values(status).filter(s => s && s.isLive).length;
+  res.json({
+    ok: true,
+    outlets: outlets.length,
+    liveOutlets: liveCount,
+    lastStatusFile: fs.existsSync(LIVE_STATUS_PATH)
+      ? fs.statSync(LIVE_STATUS_PATH).mtime
+      : null
+  });
+});
+
+app.get('/api/regions', (req, res) => {
+  const outlets = loadOutlets();
+  const regions = [...new Set(outlets.map(o => o.region))].sort();
+  const countries = [...new Set(outlets.map(o => o.country))].sort();
+  const langs = [...new Set(outlets.map(o => o.lang))].sort();
+  const categories = [...new Set(outlets.map(o => o.category))].sort();
+  res.json({ regions, countries, langs, categories });
+});
+
+function runChecker() {
+  const proc = spawn('node', [path.join(__dirname, 'scripts', 'check-live.js')], {
+    stdio: ['ignore', 'pipe', 'pipe']
+  });
+  proc.stdout.on('data', d => process.stdout.write('[checker] ' + d));
+  proc.stderr.on('data', d => process.stderr.write('[checker] ' + d));
+  proc.on('exit', code => console.log('[checker] exit ' + code));
+}
+
+const POLL_INTERVAL_MS = parseInt(process.env.POLL_INTERVAL_MS || '90000', 10);
+
+app.listen(PORT, () => {
+  console.log(`allnewsdaily listening on http://0.0.0.0:${PORT}`);
+  console.log(`outlets loaded: ${loadOutlets().length}`);
+  runChecker();
+  setInterval(runChecker, POLL_INTERVAL_MS);
+});

(oldest)  ·  back to Allnewsdaily  ·  ship: pm2-managed on :9788, 98 outlets, live-checker detecti 10cbb44 →