From 94e4c2176e97aeed0f04217673bb16c166577a60 Mon Sep 17 00:00:00 2001 From: Said Turksever Date: Tue, 3 Nov 2026 14:50:23 +0000 Subject: [PATCH] fix(radio): canonicalize priority country aliases before querying --- changelog.d/1206-radio-priority-countries.md | 3 ++- docs/CURRENT-STATE.md | 1 + server/providers/radio/catalog.js | 8 +++--- src/data/radioProxy.test.mjs | 28 ++++++++++++++++++++ 4 files changed, 36 insertions(+), 4 deletions(-) diff --git a/changelog.d/1206-radio-priority-countries.md b/changelog.d/1206-radio-priority-countries.md index 75536344e..26d0825cf 100644 --- a/changelog.d/1206-radio-priority-countries.md +++ b/changelog.d/1206-radio-priority-countries.md @@ -1,5 +1,6 @@ - Add optional keyless Radio priority-country coverage with up to 40 stations - per configured country inside the existing 750-station cap. Country query + per configured country inside the existing 750-station cap; the UK alias uses + the canonical GB code. Country query failures do not block a healthy global catalog. Bound mirror attempts to three and retry failed or partial refreshes after 60 seconds on all installations, including those without priority countries configured (m3ayush, #1206). diff --git a/docs/CURRENT-STATE.md b/docs/CURRENT-STATE.md index 290082a35..6e1ad3f90 100644 --- a/docs/CURRENT-STATE.md +++ b/docs/CURRENT-STATE.md @@ -6315,6 +6315,7 @@ play action; the source does not relay or record streams. Optional `RADIO_PRIORITY_COUNTRIES` is a server-owned comma-separated list of up to eight ISO alpha-2 codes (for example `IN,PL`). Restart after changing it. +The `UK` alias is canonicalized to `GB` before deduplication and querying. It needs no API key and adds no POWER UP entry. An empty list preserves the existing global/tag-based directory. Invalid configuration fails at startup. diff --git a/server/providers/radio/catalog.js b/server/providers/radio/catalog.js index 26d1dfe64..1859700c6 100644 --- a/server/providers/radio/catalog.js +++ b/server/providers/radio/catalog.js @@ -39,12 +39,14 @@ export function parseRadioPriorityCountries(value = '') { const entries = Array.isArray(value) ? value : String(value).split(','); const countries = []; for (const entry of entries) { - const code = String(entry).trim().toUpperCase(); - if (!code) continue; - if (!/^[A-Z]{2}$/.test(code) || !normalizeRadioCountryInput(code).valid) + const input = String(entry).trim().toUpperCase(); + if (!input) continue; + const normalized = normalizeRadioCountryInput(input); + if (!/^[A-Z]{2}$/.test(input) || !normalized.valid) throw new TypeError( 'RADIO_PRIORITY_COUNTRIES requires ISO alpha-2 country codes', ); + const code = normalized.code; if (!countries.includes(code)) countries.push(code); } if (countries.length > PRIORITY_COUNTRY_LIMIT) diff --git a/src/data/radioProxy.test.mjs b/src/data/radioProxy.test.mjs index c3d9fdb51..2ab252bd0 100644 --- a/src/data/radioProxy.test.mjs +++ b/src/data/radioProxy.test.mjs @@ -545,11 +545,39 @@ test('priority-country configuration is bounded, normalized and validated before const { parseRadioPriorityCountries } = await import('../../server/providers/radio/catalog.js'); assert.deepEqual(parseRadioPriorityCountries(' in, PL,in, '), ['IN', 'PL']); assert.deepEqual(parseRadioPriorityCountries(''), []); + assert.deepEqual(parseRadioPriorityCountries('uk,GB,UK'), ['GB']); + assert.deepEqual(parseRadioPriorityCountries('US,CA,IN,PL,DE,FR,UK,GB,JP'), ['US', 'CA', 'IN', 'PL', 'DE', 'FR', 'GB', 'JP']); for (const value of ['ZZ', 'India', 'IN&limit=9999', 'US,CA,IN,PL,DE,FR,GB,JP,AU']) { assert.throws(() => createProxy({ priorityCountries: value }), /RADIO_PRIORITY_COUNTRIES/); } }); +test('UK aliases query and report GB once and retain British stations', async () => { + const countryQueries = []; + const middleware = createProxy({ + priorityCountries: 'UK,GB', + fetchImpl: async (url) => { + const parsed = new URL(url); + if (parsed.pathname === '/json/servers') + return responseJson([{ name: 'de1.api.radio-browser.info' }]); + const country = parsed.searchParams.get('countrycode'); + if (country) { + countryQueries.push(country); + return responseJson([station({ country: 'United Kingdom', countrycode: 'GB' })]); + } + return responseJson(healthyRowsForQuery(url)); + }, + }); + const response = await invoke(middleware, '/stations'); + assert.equal(response.status, 200); + const body = JSON.parse(response.body); + assert.deepEqual(countryQueries, ['GB']); + assert.deepEqual(body.coverage.priorityCountries, [ + { countryCode: 'GB', succeeded: true, stationCount: 1 }, + ]); + assert.equal(body.stations.find((row) => row.id === UUID)?.countryCode, 'GB'); +}); + test('low-click country stations survive a full global catalog, with bounded cached queries and deduplication', async () => { const calls = []; let active = 0;