diff --git a/data/seed/categories.json b/data/seed/categories.json index 281b167..6fd2c05 100644 --- a/data/seed/categories.json +++ b/data/seed/categories.json @@ -1,17 +1,92 @@ [ - { "id": "starter", "label": "Starter pack", "icon": "★", "order": 0 }, - { "id": "dutch-public", "label": "Nederlandse publieke","icon": "🇳🇱", "order": 1 }, - { "id": "dutch-commercial", "label": "Nederlandse commercieel","icon": "🇳🇱", "order": 2 }, - { "id": "bbc", "label": "BBC family", "icon": "🇬🇧", "order": 3 }, - { "id": "fip", "label": "FIP family", "icon": "🇫🇷", "order": 4 }, - { "id": "underground", "label": "Underground & curated","icon": "🌐", "order": 5 }, - { "id": "ambient", "label": "Ambient & lo-fi", "icon": "🌫", "order": 6 }, - { "id": "electronic", "label": "Electronic", "icon": "⚡", "order": 7 }, - { "id": "jazz", "label": "Jazz & blues", "icon": "🎷", "order": 8 }, - { "id": "classical", "label": "Classical", "icon": "🎻", "order": 9 }, - { "id": "rock", "label": "Rock & indie", "icon": "🎸", "order": 10 }, - { "id": "reggae", "label": "Reggae & dub", "icon": "🌴", "order": 11 }, - { "id": "world", "label": "World & regional", "icon": "🌍", "order": 12 }, - { "id": "soma", "label": "SomaFM channels", "icon": "📻", "order": 13 }, - { "id": "nts", "label": "NTS infinite mixtapes","icon": "♾", "order": 14 } -] + { + "id": "starter", + "label": "Starter pack", + "icon": "★", + "order": 0 + }, + { + "id": "dutch-public", + "label": "Nederlandse publieke", + "icon": "🇳🇱", + "order": 1 + }, + { + "id": "dutch-commercial", + "label": "Nederlandse commercieel", + "icon": "🇳🇱", + "order": 2 + }, + { + "id": "bbc", + "label": "BBC family", + "icon": "🇬🇧", + "order": 3 + }, + { + "id": "fip", + "label": "FIP family", + "icon": "🇫🇷", + "order": 4 + }, + { + "id": "underground", + "label": "Underground & curated", + "icon": "🌐", + "order": 5 + }, + { + "id": "ambient", + "label": "Ambient & lo-fi", + "icon": "🌫", + "order": 6 + }, + { + "id": "electronic", + "label": "Electronic", + "icon": "⚡", + "order": 7 + }, + { + "id": "jazz", + "label": "Jazz & blues", + "icon": "🎷", + "order": 8 + }, + { + "id": "classical", + "label": "Classical", + "icon": "🎻", + "order": 9 + }, + { + "id": "rock", + "label": "Rock & indie", + "icon": "🎸", + "order": 10 + }, + { + "id": "reggae", + "label": "Reggae & dub", + "icon": "🌴", + "order": 11 + }, + { + "id": "world", + "label": "World & regional", + "icon": "🌍", + "order": 12 + }, + { + "id": "soma", + "label": "SomaFM channels", + "icon": "📻", + "order": 13 + }, + { + "id": "nts", + "label": "NTS infinite mixtapes", + "icon": "♾", + "order": 14 + } +] \ No newline at end of file diff --git a/data/seed/stations-allradio-nl.json b/data/seed/stations-allradio-nl.json index 14cb209..2531184 100644 --- a/data/seed/stations-allradio-nl.json +++ b/data/seed/stations-allradio-nl.json @@ -1449,4 +1449,4 @@ } ] } -] +] \ No newline at end of file diff --git a/data/seed/stations-extended.json b/data/seed/stations-extended.json index 952f087..ed5b356 100644 --- a/data/seed/stations-extended.json +++ b/data/seed/stations-extended.json @@ -1,514 +1,1820 @@ [ - { "slug": "npo-radio-1", "name": "NPO Radio 1", "category": "dutch-public", "country": "NL", "homepage": "https://www.nporadio1.nl/", - "genres": ["news", "talk", "sports"], "description": "Dutch public radio: news, sports, opinion.", - "streams": [ - { "url": "https://icecast.omroep.nl/radio1-bb-mp3", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 }, - { "url": "http://icecast.omroep.nl/radio1-sb-aac", "format": "aac", "bitrate": 32, "label": "AAC 32 (low)", "priority": 1 } - ] - }, - { "slug": "npo-radio-2", "name": "NPO Radio 2", "category": "dutch-public", "country": "NL", "homepage": "https://www.nporadio2.nl/", - "genres": ["pop", "rock", "adult"], "description": "Dutch public radio: pop and rock for adults.", - "streams": [ - { "url": "https://icecast.omroep.nl/radio2-bb-mp3", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - { "slug": "npo-radio-2-soul-jazz", "name": "NPO Radio 2 Soul & Jazz", "category": "dutch-public", "country": "NL", "homepage": "https://www.nporadio2.nl/soulenjazz", - "genres": ["soul", "jazz"], "description": "Dutch public soul & jazz station.", - "streams": [ - { "url": "https://icecast.omroep.nl/radio6-bb-mp3", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - { "slug": "npo-3fm", "name": "NPO 3FM", "category": "dutch-public", "country": "NL", "homepage": "https://www.npo3fm.nl/", - "genres": ["pop", "rock", "indie"], "description": "Dutch public youth-oriented pop/rock.", - "streams": [ - { "url": "https://icecast.omroep.nl/3fm-bb-mp3", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - { "slug": "npo-radio-4", "name": "NPO Radio 4", "category": "dutch-public", "country": "NL", "homepage": "https://www.nporadio4.nl/", - "genres": ["classical", "opera"], "description": "Dutch public classical and opera.", - "streams": [ - { "url": "https://icecast.omroep.nl/radio4-bb-mp3", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - { "slug": "npo-radio-4-concerten", "name": "NPO Radio 4 Concerten", "category": "classical", "country": "NL", "homepage": "https://www.nporadio4.nl/", - "genres": ["classical", "concert"], "description": "Live and recorded classical concerts.", - "streams": [ - { "url": "https://icecast.omroep.nl/radio4-eigentijds-mp3", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - { "slug": "npo-radio-5", "name": "NPO Radio 5", "category": "dutch-public", "country": "NL", "homepage": "https://www.nporadio5.nl/", - "genres": ["oldies", "nederlandstalig"], "description": "Hits and Dutch-language music.", - "streams": [ - { "url": "https://icecast.omroep.nl/radio5-bb-mp3", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - { "slug": "funx", "name": "FunX", "category": "dutch-public", "country": "NL", "homepage": "https://www.funx.nl/", - "genres": ["urban", "hip-hop", "r-n-b"], "description": "Multicultural Dutch youth radio.", - "streams": [ - { "url": "https://icecast.omroep.nl/funx-bb-mp3", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - { "slug": "funx-hiphop", "name": "FunX Hip-Hop", "category": "dutch-public", "country": "NL", "homepage": "https://www.funx.nl/funx-hiphop", - "genres": ["hip-hop"], "description": "FunX hip-hop channel.", - "streams": [ - { "url": "http://icecast.omroep.nl/funx-hiphopfb-bb-mp3", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - { "slug": "concertzender-baroque", "name": "Concertzender Baroque", "category": "classical", "country": "NL", "homepage": "https://www.concertzender.nl/", - "genres": ["baroque", "classical"], "description": "Baroque classical from Concertzender.", - "streams": [ - { "url": "http://streams.greenhost.nl:8080/barok", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "concertzender-old-music", "name": "Concertzender Oude Muziek", "category": "classical", "country": "NL", "homepage": "https://www.concertzender.nl/", - "genres": ["early-music", "classical"], "description": "Pre-classical compositions.", - "streams": [ - { "url": "http://streams.greenhost.nl:8080/oudemuziek", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "concertzender-world-of-jazz", "name": "Concertzender World of Jazz", "category": "jazz", "country": "NL", "homepage": "https://www.concertzender.nl/zender/world-of-jazz/", - "genres": ["jazz", "fusion", "world"], "description": "Jazz, fusion, and world music.", - "streams": [ - { "url": "http://streams.greenhost.nl:8080/jazz", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - - { "slug": "qmusic-nl", "name": "Q-music NL", "category": "dutch-commercial", "country": "NL", "homepage": "https://qmusic.nl/", - "genres": ["pop", "hits"], "description": "Dutch commercial hits radio.", - "streams": [ - { "url": "https://icecast-qmusicnl-cdp.triple-it.nl/Qmusic_nl_live.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "joe-nl", "name": "Joe NL", "category": "dutch-commercial", "country": "NL", "homepage": "https://www.joe.nl/", - "genres": ["adult", "rock", "hits"], "description": "Dutch adult contemporary commercial radio.", - "streams": [ - { "url": "https://icecast-qmusicnl-cdp.triple-it.nl/Joe_nl.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 }, - { "url": "https://icecast-qmusicnl-cdp.triple-it.nl/Joe_nl_high.aac", "format": "aac", "bitrate": 96, "label": "AAC+ 96", "priority": 1 } - ] - }, - { "slug": "sky-radio", "name": "Sky Radio", "category": "dutch-commercial", "country": "NL", "homepage": "https://www.skyradio.nl/", - "genres": ["pop", "hits"], "description": "Dutch hit radio.", - "streams": [ - { "url": "https://playerservices.streamtheworld.com/api/livestream-redirect/SRGSTR01.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "sky-radio-80s", "name": "Sky Radio 80's Hits", "category": "dutch-commercial", "country": "NL", "homepage": "https://www.skyradio.nl/", - "genres": ["80s", "pop"], "description": "All 80s hits, all the time.", - "streams": [ - { "url": "https://playerservices.streamtheworld.com/api/livestream-redirect/SRGSTR04.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "radio-538", "name": "Radio 538", "category": "dutch-commercial", "country": "NL", "homepage": "https://www.538.nl/", - "genres": ["dance", "pop"], "description": "Dutch dance and pop hits.", - "streams": [ - { "url": "https://playerservices.streamtheworld.com/api/livestream-redirect/RADIO538.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "slam", "name": "SLAM!", "category": "dutch-commercial", "country": "NL", "homepage": "https://www.slam.nl/", - "genres": ["dance", "edm"], "description": "Dance, EDM, club hits.", - "streams": [ - { "url": "http://stream.slam.nl/slam_mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 }, - { "url": "http://stream.radiocorp.nl/web10_mp3", "format": "mp3", "bitrate": 128, "label": "Non Stop", "priority": 1 } - ] - }, - { "slug": "radio-veronica", "name": "Radio Veronica", "category": "dutch-commercial", "country": "NL", "homepage": "https://www.radioveronica.nl/", - "genres": ["classic-rock", "pop"], "description": "Dutch classic rock.", - "streams": [ - { "url": "https://playerservices.streamtheworld.com/api/livestream-redirect/VERONICAAAC.aac", "format": "aac", "bitrate": 64, "label": "AAC+ 64", "priority": 0 } - ] - }, - { "slug": "bnr", "name": "BNR Nieuwsradio", "category": "dutch-commercial", "country": "NL", "homepage": "https://www.bnr.nl/", - "genres": ["news", "business"], "description": "Dutch business and news radio.", - "streams": [ - { "url": "https://stream.bnr.nl/bnr_aac_96_20", "format": "aac", "bitrate": 96, "label": "AAC+ 96", "priority": 0 }, - { "url": "https://stream.bnr.nl/bnr_mp3_128_20","format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 1 } - ] - }, - { "slug": "pinguin-radio", "name": "Pinguin Radio", "category": "dutch-commercial", "country": "NL", "homepage": "https://pinguinradio.com/", - "genres": ["alternative", "indie"], "description": "Dutch alternative and indie network.", - "streams": [ - { "url": "http://pr320.pinguinradio.com/", "format": "mp3", "bitrate": 320, "label": "MP3 320", "priority": 0 } - ] - }, - - { "slug": "bbc-radio-1", "name": "BBC Radio 1", "category": "bbc", "country": "GB", "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_radio_one", - "genres": ["pop", "dance", "rock"], "description": "Contemporary pop, dance, and rock from the UK.", - "streams": [ - { "url": "http://a.files.bbci.co.uk/ms6/live/3441A116-B12E-4D2F-ACA8-C1984642FA4B/audio/simulcast/hls/nonuk/pc_hd_abr_v2/ak/bbc_radio_one.m3u8", "format": "hls", "bitrate": 128, "label": "HLS Akamai", "priority": 0 } - ] - }, - { "slug": "bbc-1xtra", "name": "BBC Radio 1Xtra", "category": "bbc", "country": "GB", "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_1xtra", - "genres": ["hip-hop", "r-n-b", "afrobeats"], "description": "Black music and culture from the UK.", - "streams": [ - { "url": "http://as-hls-ww-live.akamaized.net/pool_92079267/live/ww/bbc_1xtra/bbc_1xtra.isml/bbc_1xtra-audio%3d128000.norewind.m3u8", "format": "hls", "bitrate": 128, "label": "HLS 128", "priority": 0 } - ] - }, - { "slug": "bbc-radio-2", "name": "BBC Radio 2", "category": "bbc", "country": "GB", "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_radio_two", - "genres": ["adult", "pop"], "description": "Adult contemporary and culture from the UK.", - "streams": [ - { "url": "http://as-hls-ww-live.akamaized.net/pool_74208725/live/ww/bbc_radio_two/bbc_radio_two.isml/bbc_radio_two-audio%3d128000.norewind.m3u8", "format": "hls", "bitrate": 128, "label": "HLS 128", "priority": 0 } - ] - }, - { "slug": "bbc-radio-3", "name": "BBC Radio 3", "category": "bbc", "country": "GB", "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_radio_three", - "genres": ["classical", "jazz", "world"], "description": "Classical, jazz, and world music.", - "streams": [ - { "url": "http://as-hls-ww-live.akamaized.net/pool_23461179/live/ww/bbc_radio_three/bbc_radio_three.isml/bbc_radio_three-audio%3d128000.norewind.m3u8", "format": "hls", "bitrate": 128, "label": "HLS 128", "priority": 0 } - ] - }, - { "slug": "bbc-radio-4", "name": "BBC Radio 4", "category": "bbc", "country": "GB", "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_radio_fourfm", - "genres": ["news", "drama", "comedy"], "description": "UK news, drama, and comedy.", - "streams": [ - { "url": "http://as-hls-ww-live.akamaized.net/pool_55057080/live/ww/bbc_radio_fourfm/bbc_radio_fourfm.isml/bbc_radio_fourfm-audio%3d128000.norewind.m3u8", "format": "hls", "bitrate": 128, "label": "HLS 128", "priority": 0 } - ] - }, - { "slug": "bbc-radio-4-extra", "name": "BBC Radio 4 Extra", "category": "bbc", "country": "GB", "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_radio_four_extra", - "genres": ["spoken-word", "comedy"], "description": "BBC Radio 4 Extra: spoken word, comedy, drama.", - "streams": [ - { "url": "http://as-hls-ww-live.akamaized.net/pool_26173715/live/ww/bbc_radio_four_extra/bbc_radio_four_extra.isml/bbc_radio_four_extra-audio%3d128000.norewind.m3u8", "format": "hls", "bitrate": 128, "label": "HLS 128", "priority": 0 } - ] - }, - { "slug": "bbc-5-live", "name": "BBC Radio 5 Live", "category": "bbc", "country": "GB", "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_radio_five_live", - "genres": ["news", "sports"], "description": "BBC news and sports talk.", - "streams": [ - { "url": "http://as-hls-ww-live.akamaized.net/pool_89021708/live/ww/bbc_radio_five_live/bbc_radio_five_live.isml/bbc_radio_five_live-audio%3d128000.norewind.m3u8", "format": "hls", "bitrate": 128, "label": "HLS 128", "priority": 0 } - ] - }, - { "slug": "bbc-asian-network", "name": "BBC Asian Network", "category": "bbc", "country": "GB", "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_asian_network", - "genres": ["asian", "bollywood"], "description": "BBC Asian Network: South Asian music and talk.", - "streams": [ - { "url": "http://as-hls-ww-live.akamaized.net/pool_22108647/live/ww/bbc_asian_network/bbc_asian_network.isml/bbc_asian_network-audio%3d128000.norewind.m3u8", "format": "hls", "bitrate": 128, "label": "HLS 128", "priority": 0 } - ] - }, - { "slug": "bbc-world-service", "name": "BBC World Service", "category": "bbc", "country": "GB", "homepage": "https://www.bbc.co.uk/worldserviceradio", - "genres": ["news", "world"], "description": "International news and cultural programs.", - "streams": [ - { "url": "https://stream.live.vc.bbcmedia.co.uk/bbc_world_service", "format": "mp3", "bitrate": 56, "label": "MP3 56", "priority": 0 } - ] - }, - - { "slug": "fip-rock", "name": "FIP Rock", "category": "fip", "country": "FR", "homepage": "https://www.radiofrance.fr/fip/radio-rock", - "genres": ["rock"], "description": "Rock channel from FIP.", - "streams": [ - { "url": "https://icecast.radiofrance.fr/fiprock-hifi.aac", "format": "aac", "bitrate": 192, "label": "HiFi AAC", "priority": 0 }, - { "url": "https://icecast.radiofrance.fr/fiprock-midfi.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 1 } - ] - }, - { "slug": "fip-pop", "name": "FIP Pop", "category": "fip", "country": "FR", "homepage": "https://www.radiofrance.fr/fip/radio-pop", - "genres": ["pop"], "description": "Pop channel from FIP.", - "streams": [ - { "url": "https://icecast.radiofrance.fr/fippop-hifi.aac", "format": "aac", "bitrate": 192, "label": "HiFi AAC", "priority": 0 }, - { "url": "https://icecast.radiofrance.fr/fippop-midfi.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 1 } - ] - }, - { "slug": "fip-groove", "name": "FIP Groove", "category": "fip", "country": "FR", "homepage": "https://www.radiofrance.fr/fip/radio-groove", - "genres": ["funk", "soul", "disco"], "description": "Funk, soul, disco grooves.", - "streams": [ - { "url": "https://icecast.radiofrance.fr/fipgroove-hifi.aac", "format": "aac", "bitrate": 192, "label": "HiFi AAC", "priority": 0 }, - { "url": "https://icecast.radiofrance.fr/fipgroove-midfi.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 1 } - ] - }, - { "slug": "fip-electro", "name": "FIP Electro", "category": "fip", "country": "FR", "homepage": "https://www.radiofrance.fr/fip/radio-electro", - "genres": ["electronic"], "description": "Electronic channel from FIP.", - "streams": [ - { "url": "https://icecast.radiofrance.fr/fipelectro-hifi.aac", "format": "aac", "bitrate": 192, "label": "HiFi AAC", "priority": 0 }, - { "url": "https://icecast.radiofrance.fr/fipelectro-midfi.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 1 } - ] - }, - { "slug": "fip-nouveautes", "name": "FIP Nouveautés", "category": "fip", "country": "FR", "homepage": "https://www.radiofrance.fr/fip/radio-nouveautes", - "genres": ["new", "eclectic"], "description": "FIP's new releases channel.", - "streams": [ - { "url": "https://icecast.radiofrance.fr/fipnouveautes-hifi.aac", "format": "aac", "bitrate": 192, "label": "HiFi AAC", "priority": 0 }, - { "url": "https://icecast.radiofrance.fr/fipnouveautes-midfi.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 1 } - ] - }, - { "slug": "fip-metal", "name": "FIP Métal", "category": "fip", "country": "FR", "homepage": "https://www.radiofrance.fr/fip/radio-metal", - "genres": ["metal"], "description": "Metal channel from FIP.", - "streams": [ - { "url": "https://icecast.radiofrance.fr/fipmetal-hifi.aac", "format": "aac", "bitrate": 192, "label": "HiFi AAC", "priority": 0 }, - { "url": "https://icecast.radiofrance.fr/fipmetal-midfi.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 1 } - ] - }, - { "slug": "fip-hiphop", "name": "FIP Hip-Hop", "category": "fip", "country": "FR", "homepage": "https://www.radiofrance.fr/fip/radio-hip-hop", - "genres": ["hip-hop"], "description": "Hip-hop channel from FIP.", - "streams": [ - { "url": "https://icecast.radiofrance.fr/fiphiphop-hifi.aac", "format": "aac", "bitrate": 192, "label": "HiFi AAC", "priority": 0 }, - { "url": "https://icecast.radiofrance.fr/fiphiphop-midfi.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 1 } - ] - }, - { "slug": "fip-monde", "name": "FIP Monde", "category": "fip", "country": "FR", "homepage": "https://www.radiofrance.fr/fip/radio-monde", - "genres": ["world"], "description": "World music channel from FIP.", - "streams": [ - { "url": "https://icecast.radiofrance.fr/fipworld-hifi.aac", "format": "aac", "bitrate": 192, "label": "HiFi AAC", "priority": 0 }, - { "url": "https://icecast.radiofrance.fr/fipworld-midfi.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 1 } - ] - }, - { "slug": "fip-sacre-francais", "name": "FIP Sacré Français", "category": "fip", "country": "FR", "homepage": "https://www.radiofrance.fr/fip/radio-sacre-francais", - "genres": ["french", "chanson"], "description": "French-language curated by FIP.", - "streams": [ - { "url": "https://icecast.radiofrance.fr/fipsacrefrancais-hifi.aac", "format": "aac", "bitrate": 192, "label": "HiFi AAC", "priority": 0 } - ] - }, - - { "slug": "somafm-deep-space-one", "name": "SomaFM — Deep Space One", "category": "soma", "country": "US", "homepage": "https://somafm.com/deepspaceone/", - "genres": ["ambient", "space"], "description": "Deep ambient electronic, experimental and space music.", - "streams": [ - { "url": "https://ice1.somafm.com/deepspaceone-128-mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "somafm-secret-agent", "name": "SomaFM — Secret Agent", "category": "soma", "country": "US", "homepage": "https://somafm.com/secretagent/", - "genres": ["lounge", "exotica", "spy"], "description": "The soundtrack for your stylish, mysterious dangerous life.", - "streams": [ - { "url": "https://ice1.somafm.com/secretagent-128-mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "somafm-defcon-radio", "name": "SomaFM — DEF CON Radio", "category": "soma", "country": "US", "homepage": "https://somafm.com/defcon/", - "genres": ["electronic", "industrial"], "description": "Music for hackers. Music for the underground.", - "streams": [ - { "url": "https://ice1.somafm.com/defcon-128-mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "somafm-beat-blender", "name": "SomaFM — Beat Blender", "category": "soma", "country": "US", "homepage": "https://somafm.com/beatblender/", - "genres": ["downtempo", "house"], "description": "A late-night blend of deep-house and downtempo chill.", - "streams": [ - { "url": "https://ice1.somafm.com/beatblender-128-mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "somafm-suburbs-of-goa", "name": "SomaFM — Suburbs of Goa", "category": "soma", "country": "US", "homepage": "https://somafm.com/suburbsofgoa/", - "genres": ["world", "ethnic", "electronic"], "description": "Desi-influenced ambient electronica.", - "streams": [ - { "url": "https://ice1.somafm.com/suburbsofgoa-128-mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "somafm-underground-80s", "name": "SomaFM — Underground 80s", "category": "soma", "country": "US", "homepage": "https://somafm.com/u80s/", - "genres": ["80s", "new-wave", "post-punk"], "description": "Early 80s UK Synthpop and a bit of New Wave.", - "streams": [ - { "url": "https://ice1.somafm.com/u80s-128-mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "somafm-lush", "name": "SomaFM — Lush", "category": "soma", "country": "US", "homepage": "https://somafm.com/lush/", - "genres": ["downtempo", "vocal"], "description": "Sensuous and mellow vocals, mostly female, with an electronic influence.", - "streams": [ - { "url": "https://ice1.somafm.com/lush-128-mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "somafm-mission-control", "name": "SomaFM — Mission Control", "category": "soma", "country": "US", "homepage": "https://somafm.com/missioncontrol/", - "genres": ["space", "ambient"], "description": "Ambient electronica + space mission audio.", - "streams": [ - { "url": "https://ice1.somafm.com/missioncontrol-128-mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "somafm-boot-liquor", "name": "SomaFM — Boot Liquor", "category": "soma", "country": "US", "homepage": "https://somafm.com/bootliquor/", - "genres": ["alt-country", "americana"], "description": "Americana roots music for true cowpokes.", - "streams": [ - { "url": "https://ice1.somafm.com/bootliquor-128-mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - - { "slug": "9128-live", "name": "9128.live", "category": "ambient", "country": "GB", "homepage": "https://9128.live/", - "genres": ["ambient", "drone", "experimental"], "description": "Ambient and drone curated by A Strangely Isolated Place.", - "streams": [ - { "url": "https://streams.radio.co/s0aa1e6f4a/low", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 }, - { "url": "https://streams.radio.co/s0aa1e6f4a/listen", "format": "mp3", "bitrate": 320, "label": "MP3 320", "priority": 1 } - ] - }, - { "slug": "ambient-sleeping-pill", "name": "Ambient Sleeping Pill", "category": "ambient", "country": "US", "homepage": "https://stereoscenic.com/", - "genres": ["ambient"], "description": "Beat-free stream for sleep or focus.", - "streams": [ - { "url": "https://radio.stereoscenic.com/asp-h", "format": "aac", "bitrate": 64, "label": "AAC 64", "priority": 0 } - ] - }, - { "slug": "echoes-cryosleep", "name": "Echoes of Bluemars — Cryosleep", "category": "ambient", "country": "US", "homepage": "https://echoesofbluemars.org/", - "genres": ["ambient", "drone"], "description": "Zero-beat ambient drone and drift music.", - "streams": [ - { "url": "https://streams.echoesofbluemars.org:8000/cryosleep", "format": "ogg", "bitrate": 96, "label": "OGG 96", "priority": 0 } - ] - }, - { "slug": "freecodecamp-radio", "name": "freeCodeCamp Code Radio", "category": "ambient", "country": "US", "homepage": "https://www.freecodecamp.org/news/code-radio/", - "genres": ["lo-fi", "instrumental"], "description": "24/7 instrumentals for focus.", - "streams": [ - { "url": "https://coderadio-admin-v2.freecodecamp.org/listen/coderadio/radio.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - - { "slug": "rinse-fm", "name": "Rinse FM", "category": "electronic", "country": "GB", "homepage": "https://rinse.fm/", - "genres": ["dance", "underground", "uk"], "description": "London underground dance music.", - "streams": [ - { "url": "https://stream.rcs.revma.com/an1ugyygzk8uv", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "refuge-worldwide", "name": "Refuge Worldwide", "category": "electronic", "country": "DE", "homepage": "https://refugeworldwide.com/", - "genres": ["dance", "techno"], "description": "Berlin-based station for dance music and techno.", - "streams": [ - { "url": "https://streaming.radio.co/s3699c5e49/listen", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - { "slug": "cashmere-radio", "name": "Cashmere Radio", "category": "electronic", "country": "DE", "homepage": "https://cashmereradio.com/", - "genres": ["experimental", "electronic"], "description": "Berlin-based experimental electronic.", - "streams": [ - { "url": "https://cashmere-radio.radiocult.fm/stream", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - { "slug": "nightride-fm", "name": "Nightride FM", "category": "electronic", "country": "FI", "homepage": "https://nightride.fm/", - "genres": ["synthwave", "darksynth", "retrowave"], "description": "Synthwave, darksynth, and retrowave.", - "streams": [ - { "url": "https://stream.nightride.fm/nightride.m4a", "format": "aac", "bitrate": 256, "label": "AAC 256", "priority": 0 } - ] - }, - { "slug": "dublin-digital-radio", "name": "Dublin Digital Radio", "category": "electronic", "country": "IE", "homepage": "https://listen.dublindigitalradio.com/", - "genres": ["electronic", "experimental"], "description": "Indie and experimental electronic from Dublin.", - "streams": [ - { "url": "https://dublin-digital-radio.radiocult.fm/stream", "format": "mp3", "bitrate": 256, "label": "MP3 256", "priority": 0 } - ] - }, - { "slug": "lyl-radio", "name": "LYL Radio", "category": "electronic", "country": "FR", "homepage": "https://lyl.live/", - "genres": ["experimental"], "description": "Experimental radio and sonic art from Lyon.", - "streams": [ - { "url": "https://icecast.lyl.live/live", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - - { "slug": "soho-radio", "name": "Soho Radio", "category": "underground", "country": "GB", "homepage": "https://sohoradiolondon.com/", - "genres": ["eclectic", "underground"], "description": "Culture and music from London and NYC.", - "streams": [ - { "url": "https://sohoradiomusic.doughunt.co.uk:8010/128mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "noods-radio", "name": "Noods Radio", "category": "underground", "country": "GB", "homepage": "https://noodsradio.com/", - "genres": ["eclectic", "underground"], "description": "Independent radio for music collectors from Bristol.", - "streams": [ - { "url": "https://noods-radio.radiocult.fm/stream", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - { "slug": "resonance-fm", "name": "Resonance 104.4 FM", "category": "underground", "country": "GB", "homepage": "https://www.resonancefm.com/", - "genres": ["arts", "experimental"], "description": "Cultural programming and art from London.", - "streams": [ - { "url": "http://stream.resonance.fm:8000/resonance", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "dandelion-radio", "name": "Dandelion Radio", "category": "underground", "country": "GB", "homepage": "https://dandelionradio.com/", - "genres": ["eclectic", "freeform"], "description": "Internet radio inspired by John Peel.", - "streams": [ - { "url": "http://stream.dandelionradio.com:9414/", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "radio-meuh", "name": "Radio Meuh", "category": "underground", "country": "FR", "homepage": "https://www.radiomeuh.com/", - "genres": ["electronic", "soul", "funk"], "description": "Electronic, soul, and funk from the French Alps.", - "streams": [ - { "url": "https://radiomeuh.ice.infomaniak.ch/radiomeuh-128.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "oroko-radio", "name": "Oroko Radio", "category": "underground", "country": "GH", "homepage": "https://oroko.live/", - "genres": ["afro", "indie", "soul"], "description": "Afro indie, folk, and soul from Accra, Ghana.", - "streams": [ - { "url": "https://oroko-radio.radiocult.fm/stream", "format": "mp3", "bitrate": 320, "label": "MP3 320", "priority": 0 } - ] - }, - { "slug": "radio-al-hara", "name": "Radio Al-Hara", "category": "underground", "country": "PS", "homepage": "https://www.radioalhara.net/", - "genres": ["experimental"], "description": "Underground Palestinian radio; experimental beats and talk.", - "streams": [ - { "url": "https://radio-alhara.radiocult.fm/stream", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - { "slug": "wfmu-rock-soul", "name": "WFMU — Rock 'n' Soul Ichiban", "category": "underground", "country": "US", "homepage": "https://wfmu.org/", - "genres": ["rock", "soul"], "description": "WFMU stream for rock, R&B, and soul.", - "streams": [ - { "url": "https://wfmu.org/wfmu_rock.pls", "format": "pls", "label": "PLS", "priority": 0 } - ] - }, - { "slug": "wfmu-sheena", "name": "WFMU — Sheena's Jungle Room", "category": "underground", "country": "US", "homepage": "https://wfmu.org/", - "genres": ["garage", "surf", "rockabilly"], "description": "WFMU stream for garage, surf, and rockabilly.", - "streams": [ - { "url": "https://wfmu.org/wfmu_sheena.pls", "format": "pls", "label": "PLS", "priority": 0 } - ] - }, - - { "slug": "jazz24", "name": "Jazz24", "category": "jazz", "country": "US", "homepage": "https://www.jazz24.org/", - "genres": ["jazz"], "description": "24/7 jazz from Seattle (KNKX).", - "streams": [ - { "url": "https://knkx-live-a.edge.audiocdn.com/6285_256k", "format": "aac", "bitrate": 256, "label": "AAC 256", "priority": 0 }, - { "url": "https://knkx-live-a.edge.audiocdn.com/6285_128k", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 1 } - ] - }, - { "slug": "wbgo", "name": "WBGO 88.3 — Jazz", "category": "jazz", "country": "US", "homepage": "https://wbgo.org/", - "genres": ["jazz", "blues"], "description": "Jazz and blues from Newark/New York.", - "streams": [ - { "url": "https://ais-sa8.cdnstream1.com/3629_128.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "tsf-jazz", "name": "TSF Jazz", "category": "jazz", "country": "FR", "homepage": "https://www.tsfjazz.com/", - "genres": ["jazz"], "description": "Jazz and talk from Paris.", - "streams": [ - { "url": "https://tsfjazz.ice.infomaniak.ch/tsfjazz-high.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "linn-jazz", "name": "Linn Jazz", "category": "jazz", "country": "GB", "homepage": "https://www.linn.co.uk/linn-radio", - "genres": ["jazz"], "description": "Jazz classics and originals from Linn Records.", - "streams": [ - { "url": "http://radio.linn.co.uk:8000/autodj", "format": "mp3", "bitrate": 320, "label": "MP3 320", "priority": 0 } - ] - }, - { "slug": "linn-classical", "name": "Linn Classical", "category": "classical", "country": "GB", "homepage": "https://www.linn.co.uk/linn-radio", - "genres": ["classical"], "description": "Classical recordings from Linn Records.", - "streams": [ - { "url": "http://radio.linn.co.uk:8004/autodj", "format": "mp3", "bitrate": 320, "label": "MP3 320", "priority": 0 } - ] - }, - { "slug": "radio-suisse-classique", "name": "Radio Suisse Classique", "category": "classical", "country": "CH", "homepage": "https://www.radioswissclassic.ch/en", - "genres": ["classical"], "description": "Swiss public radio for classical and opera.", - "streams": [ - { "url": "https://stream.srg-ssr.ch/m/rsc_de/mp3_128", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "wcrb-classical", "name": "WCRB 99.5 Classical", "category": "classical", "country": "US", "homepage": "https://www.classicalwcrb.org/", - "genres": ["classical"], "description": "Boston classical; features the Boston Symphony Orchestra.", - "streams": [ - { "url": "https://wgbh-live.streamguys1.com/classical-hi", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - - { "slug": "drdicks-dub-shack", "name": "Dr. Dick's Dub Shack", "category": "reggae", "country": "BM", "homepage": "https://drdicksdubshack.com/", - "genres": ["dub", "reggae", "roots"], "description": "Deep dub and roots from Bermuda.", - "streams": [ - { "url": "http://streamer.radio.co/s0635c8b0d/listen", "format": "mp3", "bitrate": 192, "label": "MP3 192", "priority": 0 } - ] - }, - { "slug": "alpha-boys-school", "name": "Alpha Boys School Radio", "category": "reggae", "country": "JM", "homepage": "https://alphaboysschoolradio.com/", - "genres": ["ska", "rocksteady", "reggae"], "description": "Ska and rocksteady from Kingston, Jamaica.", - "streams": [ - { "url": "http://alphaboys-live.streamguys1.com/alphaboys.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - - { "slug": "radio-nova", "name": "Radio Nova", "category": "world", "country": "FR", "homepage": "https://www.nova.fr/", - "genres": ["world", "jazz", "hip-hop"], "description": "French station for global sounds, jazz, and hip-hop.", - "streams": [ - { "url": "https://novazz.ice.infomaniak.ch/novazz-128.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 0 } - ] - }, - { "slug": "rfi-monde", "name": "RFI Monde", "category": "world", "country": "FR", "homepage": "https://www.rfi.fr/", - "genres": ["news", "world"], "description": "Global news and music from France in French.", - "streams": [ - { "url": "http://live02.rfi.fr/rfimonde-96k.mp3", "format": "mp3", "bitrate": 96, "label": "MP3 96", "priority": 0 } - ] - }, - { "slug": "radio-france-inter", "name": "France Inter", "category": "world", "country": "FR", "homepage": "https://www.radiofrance.fr/franceinter", - "genres": ["news", "talk"], "description": "French public talk radio.", - "streams": [ - { "url": "https://icecast.radiofrance.fr/franceinter-hifi.aac", "format": "aac", "bitrate": 192, "label": "HiFi AAC", "priority": 0 }, - { "url": "https://icecast.radiofrance.fr/franceinter-midfi.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 1 } - ] - }, - { "slug": "france-musique", "name": "France Musique", "category": "classical", "country": "FR", "homepage": "https://www.radiofrance.fr/francemusique", - "genres": ["classical", "jazz"], "description": "French classical and jazz public radio.", - "streams": [ - { "url": "https://icecast.radiofrance.fr/francemusique-hifi.aac", "format": "aac", "bitrate": 192, "label": "HiFi AAC", "priority": 0 } - ] - }, - { "slug": "france-culture", "name": "France Culture", "category": "world", "country": "FR", "homepage": "https://www.radiofrance.fr/franceculture", - "genres": ["culture", "talk"], "description": "Culture, ideas, talk in French.", - "streams": [ - { "url": "https://icecast.radiofrance.fr/franceculture-hifi.aac", "format": "aac", "bitrate": 192, "label": "HiFi AAC", "priority": 0 }, - { "url": "https://icecast.radiofrance.fr/franceculture-midfi.mp3", "format": "mp3", "bitrate": 128, "label": "MP3 128", "priority": 1 } - ] - } -] + { + "slug": "npo-radio-1", + "name": "NPO Radio 1", + "category": "dutch-public", + "country": "NL", + "homepage": "https://www.nporadio1.nl/", + "genres": [ + "news", + "talk", + "sports" + ], + "description": "Dutch public radio: news, sports, opinion.", + "streams": [ + { + "url": "https://icecast.omroep.nl/radio1-bb-mp3", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + }, + { + "url": "http://icecast.omroep.nl/radio1-sb-aac", + "format": "aac", + "bitrate": 32, + "label": "AAC 32 (low)", + "priority": 1 + } + ] + }, + { + "slug": "npo-radio-2", + "name": "NPO Radio 2", + "category": "dutch-public", + "country": "NL", + "homepage": "https://www.nporadio2.nl/", + "genres": [ + "pop", + "rock", + "adult" + ], + "description": "Dutch public radio: pop and rock for adults.", + "streams": [ + { + "url": "https://icecast.omroep.nl/radio2-bb-mp3", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "npo-radio-2-soul-jazz", + "name": "NPO Radio 2 Soul & Jazz", + "category": "dutch-public", + "country": "NL", + "homepage": "https://www.nporadio2.nl/soulenjazz", + "genres": [ + "soul", + "jazz" + ], + "description": "Dutch public soul & jazz station.", + "streams": [ + { + "url": "https://icecast.omroep.nl/radio6-bb-mp3", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "npo-3fm", + "name": "NPO 3FM", + "category": "dutch-public", + "country": "NL", + "homepage": "https://www.npo3fm.nl/", + "genres": [ + "pop", + "rock", + "indie" + ], + "description": "Dutch public youth-oriented pop/rock.", + "streams": [ + { + "url": "https://icecast.omroep.nl/3fm-bb-mp3", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "npo-radio-4", + "name": "NPO Radio 4", + "category": "dutch-public", + "country": "NL", + "homepage": "https://www.nporadio4.nl/", + "genres": [ + "classical", + "opera" + ], + "description": "Dutch public classical and opera.", + "streams": [ + { + "url": "https://icecast.omroep.nl/radio4-bb-mp3", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "npo-radio-4-concerten", + "name": "NPO Radio 4 Concerten", + "category": "classical", + "country": "NL", + "homepage": "https://www.nporadio4.nl/", + "genres": [ + "classical", + "concert" + ], + "description": "Live and recorded classical concerts.", + "streams": [ + { + "url": "https://icecast.omroep.nl/radio4-eigentijds-mp3", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "npo-radio-5", + "name": "NPO Radio 5", + "category": "dutch-public", + "country": "NL", + "homepage": "https://www.nporadio5.nl/", + "genres": [ + "oldies", + "nederlandstalig" + ], + "description": "Hits and Dutch-language music.", + "streams": [ + { + "url": "https://icecast.omroep.nl/radio5-bb-mp3", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "funx", + "name": "FunX", + "category": "dutch-public", + "country": "NL", + "homepage": "https://www.funx.nl/", + "genres": [ + "urban", + "hip-hop", + "r-n-b" + ], + "description": "Multicultural Dutch youth radio.", + "streams": [ + { + "url": "https://icecast.omroep.nl/funx-bb-mp3", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "funx-hiphop", + "name": "FunX Hip-Hop", + "category": "dutch-public", + "country": "NL", + "homepage": "https://www.funx.nl/funx-hiphop", + "genres": [ + "hip-hop" + ], + "description": "FunX hip-hop channel.", + "streams": [ + { + "url": "http://icecast.omroep.nl/funx-hiphopfb-bb-mp3", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "concertzender-baroque", + "name": "Concertzender Baroque", + "category": "classical", + "country": "NL", + "homepage": "https://www.concertzender.nl/", + "genres": [ + "baroque", + "classical" + ], + "description": "Baroque classical from Concertzender.", + "streams": [ + { + "url": "http://streams.greenhost.nl:8080/barok", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "concertzender-old-music", + "name": "Concertzender Oude Muziek", + "category": "classical", + "country": "NL", + "homepage": "https://www.concertzender.nl/", + "genres": [ + "early-music", + "classical" + ], + "description": "Pre-classical compositions.", + "streams": [ + { + "url": "http://streams.greenhost.nl:8080/oudemuziek", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "concertzender-world-of-jazz", + "name": "Concertzender World of Jazz", + "category": "jazz", + "country": "NL", + "homepage": "https://www.concertzender.nl/zender/world-of-jazz/", + "genres": [ + "jazz", + "fusion", + "world" + ], + "description": "Jazz, fusion, and world music.", + "streams": [ + { + "url": "http://streams.greenhost.nl:8080/jazz", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "qmusic-nl", + "name": "Q-music NL", + "category": "dutch-commercial", + "country": "NL", + "homepage": "https://qmusic.nl/", + "genres": [ + "pop", + "hits" + ], + "description": "Dutch commercial hits radio.", + "streams": [ + { + "url": "https://icecast-qmusicnl-cdp.triple-it.nl/Qmusic_nl_live.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "joe-nl", + "name": "Joe NL", + "category": "dutch-commercial", + "country": "NL", + "homepage": "https://www.joe.nl/", + "genres": [ + "adult", + "rock", + "hits" + ], + "description": "Dutch adult contemporary commercial radio.", + "streams": [ + { + "url": "https://icecast-qmusicnl-cdp.triple-it.nl/Joe_nl.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + }, + { + "url": "https://icecast-qmusicnl-cdp.triple-it.nl/Joe_nl_high.aac", + "format": "aac", + "bitrate": 96, + "label": "AAC+ 96", + "priority": 1 + } + ] + }, + { + "slug": "sky-radio", + "name": "Sky Radio", + "category": "dutch-commercial", + "country": "NL", + "homepage": "https://www.skyradio.nl/", + "genres": [ + "pop", + "hits" + ], + "description": "Dutch hit radio.", + "streams": [ + { + "url": "https://playerservices.streamtheworld.com/api/livestream-redirect/SRGSTR01.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "sky-radio-80s", + "name": "Sky Radio 80's Hits", + "category": "dutch-commercial", + "country": "NL", + "homepage": "https://www.skyradio.nl/", + "genres": [ + "80s", + "pop" + ], + "description": "All 80s hits, all the time.", + "streams": [ + { + "url": "https://playerservices.streamtheworld.com/api/livestream-redirect/SRGSTR04.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "radio-538", + "name": "Radio 538", + "category": "dutch-commercial", + "country": "NL", + "homepage": "https://www.538.nl/", + "genres": [ + "dance", + "pop" + ], + "description": "Dutch dance and pop hits.", + "streams": [ + { + "url": "https://playerservices.streamtheworld.com/api/livestream-redirect/RADIO538.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "slam", + "name": "SLAM!", + "category": "dutch-commercial", + "country": "NL", + "homepage": "https://www.slam.nl/", + "genres": [ + "dance", + "edm" + ], + "description": "Dance, EDM, club hits.", + "streams": [ + { + "url": "http://stream.slam.nl/slam_mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + }, + { + "url": "http://stream.radiocorp.nl/web10_mp3", + "format": "mp3", + "bitrate": 128, + "label": "Non Stop", + "priority": 1 + } + ] + }, + { + "slug": "radio-veronica", + "name": "Radio Veronica", + "category": "dutch-commercial", + "country": "NL", + "homepage": "https://www.radioveronica.nl/", + "genres": [ + "classic-rock", + "pop" + ], + "description": "Dutch classic rock.", + "streams": [ + { + "url": "https://playerservices.streamtheworld.com/api/livestream-redirect/VERONICAAAC.aac", + "format": "aac", + "bitrate": 64, + "label": "AAC+ 64", + "priority": 0 + } + ] + }, + { + "slug": "bnr", + "name": "BNR Nieuwsradio", + "category": "dutch-commercial", + "country": "NL", + "homepage": "https://www.bnr.nl/", + "genres": [ + "news", + "business" + ], + "description": "Dutch business and news radio.", + "streams": [ + { + "url": "https://stream.bnr.nl/bnr_aac_96_20", + "format": "aac", + "bitrate": 96, + "label": "AAC+ 96", + "priority": 0 + }, + { + "url": "https://stream.bnr.nl/bnr_mp3_128_20", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ] + }, + { + "slug": "pinguin-radio", + "name": "Pinguin Radio", + "category": "dutch-commercial", + "country": "NL", + "homepage": "https://pinguinradio.com/", + "genres": [ + "alternative", + "indie" + ], + "description": "Dutch alternative and indie network.", + "streams": [ + { + "url": "http://pr320.pinguinradio.com/", + "format": "mp3", + "bitrate": 320, + "label": "MP3 320", + "priority": 0 + } + ] + }, + { + "slug": "bbc-radio-1", + "name": "BBC Radio 1", + "category": "bbc", + "country": "GB", + "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_radio_one", + "genres": [ + "pop", + "dance", + "rock" + ], + "description": "Contemporary pop, dance, and rock from the UK.", + "streams": [ + { + "url": "http://a.files.bbci.co.uk/ms6/live/3441A116-B12E-4D2F-ACA8-C1984642FA4B/audio/simulcast/hls/nonuk/pc_hd_abr_v2/ak/bbc_radio_one.m3u8", + "format": "hls", + "bitrate": 128, + "label": "HLS Akamai", + "priority": 0 + } + ] + }, + { + "slug": "bbc-1xtra", + "name": "BBC Radio 1Xtra", + "category": "bbc", + "country": "GB", + "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_1xtra", + "genres": [ + "hip-hop", + "r-n-b", + "afrobeats" + ], + "description": "Black music and culture from the UK.", + "streams": [ + { + "url": "http://as-hls-ww-live.akamaized.net/pool_92079267/live/ww/bbc_1xtra/bbc_1xtra.isml/bbc_1xtra-audio%3d128000.norewind.m3u8", + "format": "hls", + "bitrate": 128, + "label": "HLS 128", + "priority": 0 + } + ] + }, + { + "slug": "bbc-radio-2", + "name": "BBC Radio 2", + "category": "bbc", + "country": "GB", + "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_radio_two", + "genres": [ + "adult", + "pop" + ], + "description": "Adult contemporary and culture from the UK.", + "streams": [ + { + "url": "http://as-hls-ww-live.akamaized.net/pool_74208725/live/ww/bbc_radio_two/bbc_radio_two.isml/bbc_radio_two-audio%3d128000.norewind.m3u8", + "format": "hls", + "bitrate": 128, + "label": "HLS 128", + "priority": 0 + } + ] + }, + { + "slug": "bbc-radio-3", + "name": "BBC Radio 3", + "category": "bbc", + "country": "GB", + "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_radio_three", + "genres": [ + "classical", + "jazz", + "world" + ], + "description": "Classical, jazz, and world music.", + "streams": [ + { + "url": "http://as-hls-ww-live.akamaized.net/pool_23461179/live/ww/bbc_radio_three/bbc_radio_three.isml/bbc_radio_three-audio%3d128000.norewind.m3u8", + "format": "hls", + "bitrate": 128, + "label": "HLS 128", + "priority": 0 + } + ] + }, + { + "slug": "bbc-radio-4", + "name": "BBC Radio 4", + "category": "bbc", + "country": "GB", + "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_radio_fourfm", + "genres": [ + "news", + "drama", + "comedy" + ], + "description": "UK news, drama, and comedy.", + "streams": [ + { + "url": "http://as-hls-ww-live.akamaized.net/pool_55057080/live/ww/bbc_radio_fourfm/bbc_radio_fourfm.isml/bbc_radio_fourfm-audio%3d128000.norewind.m3u8", + "format": "hls", + "bitrate": 128, + "label": "HLS 128", + "priority": 0 + } + ] + }, + { + "slug": "bbc-radio-4-extra", + "name": "BBC Radio 4 Extra", + "category": "bbc", + "country": "GB", + "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_radio_four_extra", + "genres": [ + "spoken-word", + "comedy" + ], + "description": "BBC Radio 4 Extra: spoken word, comedy, drama.", + "streams": [ + { + "url": "http://as-hls-ww-live.akamaized.net/pool_26173715/live/ww/bbc_radio_four_extra/bbc_radio_four_extra.isml/bbc_radio_four_extra-audio%3d128000.norewind.m3u8", + "format": "hls", + "bitrate": 128, + "label": "HLS 128", + "priority": 0 + } + ] + }, + { + "slug": "bbc-5-live", + "name": "BBC Radio 5 Live", + "category": "bbc", + "country": "GB", + "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_radio_five_live", + "genres": [ + "news", + "sports" + ], + "description": "BBC news and sports talk.", + "streams": [ + { + "url": "http://as-hls-ww-live.akamaized.net/pool_89021708/live/ww/bbc_radio_five_live/bbc_radio_five_live.isml/bbc_radio_five_live-audio%3d128000.norewind.m3u8", + "format": "hls", + "bitrate": 128, + "label": "HLS 128", + "priority": 0 + } + ] + }, + { + "slug": "bbc-asian-network", + "name": "BBC Asian Network", + "category": "bbc", + "country": "GB", + "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_asian_network", + "genres": [ + "asian", + "bollywood" + ], + "description": "BBC Asian Network: South Asian music and talk.", + "streams": [ + { + "url": "http://as-hls-ww-live.akamaized.net/pool_22108647/live/ww/bbc_asian_network/bbc_asian_network.isml/bbc_asian_network-audio%3d128000.norewind.m3u8", + "format": "hls", + "bitrate": 128, + "label": "HLS 128", + "priority": 0 + } + ] + }, + { + "slug": "bbc-world-service", + "name": "BBC World Service", + "category": "bbc", + "country": "GB", + "homepage": "https://www.bbc.co.uk/worldserviceradio", + "genres": [ + "news", + "world" + ], + "description": "International news and cultural programs.", + "streams": [ + { + "url": "https://stream.live.vc.bbcmedia.co.uk/bbc_world_service", + "format": "mp3", + "bitrate": 56, + "label": "MP3 56", + "priority": 0 + } + ] + }, + { + "slug": "fip-rock", + "name": "FIP Rock", + "category": "fip", + "country": "FR", + "homepage": "https://www.radiofrance.fr/fip/radio-rock", + "genres": [ + "rock" + ], + "description": "Rock channel from FIP.", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/fiprock-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + }, + { + "url": "https://icecast.radiofrance.fr/fiprock-midfi.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ] + }, + { + "slug": "fip-pop", + "name": "FIP Pop", + "category": "fip", + "country": "FR", + "homepage": "https://www.radiofrance.fr/fip/radio-pop", + "genres": [ + "pop" + ], + "description": "Pop channel from FIP.", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/fippop-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + }, + { + "url": "https://icecast.radiofrance.fr/fippop-midfi.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ] + }, + { + "slug": "fip-groove", + "name": "FIP Groove", + "category": "fip", + "country": "FR", + "homepage": "https://www.radiofrance.fr/fip/radio-groove", + "genres": [ + "funk", + "soul", + "disco" + ], + "description": "Funk, soul, disco grooves.", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/fipgroove-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + }, + { + "url": "https://icecast.radiofrance.fr/fipgroove-midfi.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ] + }, + { + "slug": "fip-electro", + "name": "FIP Electro", + "category": "fip", + "country": "FR", + "homepage": "https://www.radiofrance.fr/fip/radio-electro", + "genres": [ + "electronic" + ], + "description": "Electronic channel from FIP.", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/fipelectro-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + }, + { + "url": "https://icecast.radiofrance.fr/fipelectro-midfi.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ] + }, + { + "slug": "fip-nouveautes", + "name": "FIP Nouveautés", + "category": "fip", + "country": "FR", + "homepage": "https://www.radiofrance.fr/fip/radio-nouveautes", + "genres": [ + "new", + "eclectic" + ], + "description": "FIP's new releases channel.", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/fipnouveautes-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + }, + { + "url": "https://icecast.radiofrance.fr/fipnouveautes-midfi.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ] + }, + { + "slug": "fip-metal", + "name": "FIP Métal", + "category": "fip", + "country": "FR", + "homepage": "https://www.radiofrance.fr/fip/radio-metal", + "genres": [ + "metal" + ], + "description": "Metal channel from FIP.", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/fipmetal-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + }, + { + "url": "https://icecast.radiofrance.fr/fipmetal-midfi.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ] + }, + { + "slug": "fip-hiphop", + "name": "FIP Hip-Hop", + "category": "fip", + "country": "FR", + "homepage": "https://www.radiofrance.fr/fip/radio-hip-hop", + "genres": [ + "hip-hop" + ], + "description": "Hip-hop channel from FIP.", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/fiphiphop-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + }, + { + "url": "https://icecast.radiofrance.fr/fiphiphop-midfi.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ] + }, + { + "slug": "fip-monde", + "name": "FIP Monde", + "category": "fip", + "country": "FR", + "homepage": "https://www.radiofrance.fr/fip/radio-monde", + "genres": [ + "world" + ], + "description": "World music channel from FIP.", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/fipworld-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + }, + { + "url": "https://icecast.radiofrance.fr/fipworld-midfi.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ] + }, + { + "slug": "fip-sacre-francais", + "name": "FIP Sacré Français", + "category": "fip", + "country": "FR", + "homepage": "https://www.radiofrance.fr/fip/radio-sacre-francais", + "genres": [ + "french", + "chanson" + ], + "description": "French-language curated by FIP.", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/fipsacrefrancais-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + } + ] + }, + { + "slug": "somafm-deep-space-one", + "name": "SomaFM — Deep Space One", + "category": "soma", + "country": "US", + "homepage": "https://somafm.com/deepspaceone/", + "genres": [ + "ambient", + "space" + ], + "description": "Deep ambient electronic, experimental and space music.", + "streams": [ + { + "url": "https://ice1.somafm.com/deepspaceone-128-mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "somafm-secret-agent", + "name": "SomaFM — Secret Agent", + "category": "soma", + "country": "US", + "homepage": "https://somafm.com/secretagent/", + "genres": [ + "lounge", + "exotica", + "spy" + ], + "description": "The soundtrack for your stylish, mysterious dangerous life.", + "streams": [ + { + "url": "https://ice1.somafm.com/secretagent-128-mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "somafm-defcon-radio", + "name": "SomaFM — DEF CON Radio", + "category": "soma", + "country": "US", + "homepage": "https://somafm.com/defcon/", + "genres": [ + "electronic", + "industrial" + ], + "description": "Music for hackers. Music for the underground.", + "streams": [ + { + "url": "https://ice1.somafm.com/defcon-128-mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "somafm-beat-blender", + "name": "SomaFM — Beat Blender", + "category": "soma", + "country": "US", + "homepage": "https://somafm.com/beatblender/", + "genres": [ + "downtempo", + "house" + ], + "description": "A late-night blend of deep-house and downtempo chill.", + "streams": [ + { + "url": "https://ice1.somafm.com/beatblender-128-mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "somafm-suburbs-of-goa", + "name": "SomaFM — Suburbs of Goa", + "category": "soma", + "country": "US", + "homepage": "https://somafm.com/suburbsofgoa/", + "genres": [ + "world", + "ethnic", + "electronic" + ], + "description": "Desi-influenced ambient electronica.", + "streams": [ + { + "url": "https://ice1.somafm.com/suburbsofgoa-128-mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "somafm-underground-80s", + "name": "SomaFM — Underground 80s", + "category": "soma", + "country": "US", + "homepage": "https://somafm.com/u80s/", + "genres": [ + "80s", + "new-wave", + "post-punk" + ], + "description": "Early 80s UK Synthpop and a bit of New Wave.", + "streams": [ + { + "url": "https://ice1.somafm.com/u80s-128-mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "somafm-lush", + "name": "SomaFM — Lush", + "category": "soma", + "country": "US", + "homepage": "https://somafm.com/lush/", + "genres": [ + "downtempo", + "vocal" + ], + "description": "Sensuous and mellow vocals, mostly female, with an electronic influence.", + "streams": [ + { + "url": "https://ice1.somafm.com/lush-128-mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "somafm-mission-control", + "name": "SomaFM — Mission Control", + "category": "soma", + "country": "US", + "homepage": "https://somafm.com/missioncontrol/", + "genres": [ + "space", + "ambient" + ], + "description": "Ambient electronica + space mission audio.", + "streams": [ + { + "url": "https://ice1.somafm.com/missioncontrol-128-mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "somafm-boot-liquor", + "name": "SomaFM — Boot Liquor", + "category": "soma", + "country": "US", + "homepage": "https://somafm.com/bootliquor/", + "genres": [ + "alt-country", + "americana" + ], + "description": "Americana roots music for true cowpokes.", + "streams": [ + { + "url": "https://ice1.somafm.com/bootliquor-128-mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "9128-live", + "name": "9128.live", + "category": "ambient", + "country": "GB", + "homepage": "https://9128.live/", + "genres": [ + "ambient", + "drone", + "experimental" + ], + "description": "Ambient and drone curated by A Strangely Isolated Place.", + "streams": [ + { + "url": "https://streams.radio.co/s0aa1e6f4a/low", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + }, + { + "url": "https://streams.radio.co/s0aa1e6f4a/listen", + "format": "mp3", + "bitrate": 320, + "label": "MP3 320", + "priority": 1 + } + ] + }, + { + "slug": "ambient-sleeping-pill", + "name": "Ambient Sleeping Pill", + "category": "ambient", + "country": "US", + "homepage": "https://stereoscenic.com/", + "genres": [ + "ambient" + ], + "description": "Beat-free stream for sleep or focus.", + "streams": [ + { + "url": "https://radio.stereoscenic.com/asp-h", + "format": "aac", + "bitrate": 64, + "label": "AAC 64", + "priority": 0 + } + ] + }, + { + "slug": "echoes-cryosleep", + "name": "Echoes of Bluemars — Cryosleep", + "category": "ambient", + "country": "US", + "homepage": "https://echoesofbluemars.org/", + "genres": [ + "ambient", + "drone" + ], + "description": "Zero-beat ambient drone and drift music.", + "streams": [ + { + "url": "https://streams.echoesofbluemars.org:8000/cryosleep", + "format": "ogg", + "bitrate": 96, + "label": "OGG 96", + "priority": 0 + } + ] + }, + { + "slug": "freecodecamp-radio", + "name": "freeCodeCamp Code Radio", + "category": "ambient", + "country": "US", + "homepage": "https://www.freecodecamp.org/news/code-radio/", + "genres": [ + "lo-fi", + "instrumental" + ], + "description": "24/7 instrumentals for focus.", + "streams": [ + { + "url": "https://coderadio-admin-v2.freecodecamp.org/listen/coderadio/radio.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "rinse-fm", + "name": "Rinse FM", + "category": "electronic", + "country": "GB", + "homepage": "https://rinse.fm/", + "genres": [ + "dance", + "underground", + "uk" + ], + "description": "London underground dance music.", + "streams": [ + { + "url": "https://stream.rcs.revma.com/an1ugyygzk8uv", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "refuge-worldwide", + "name": "Refuge Worldwide", + "category": "electronic", + "country": "DE", + "homepage": "https://refugeworldwide.com/", + "genres": [ + "dance", + "techno" + ], + "description": "Berlin-based station for dance music and techno.", + "streams": [ + { + "url": "https://streaming.radio.co/s3699c5e49/listen", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "cashmere-radio", + "name": "Cashmere Radio", + "category": "electronic", + "country": "DE", + "homepage": "https://cashmereradio.com/", + "genres": [ + "experimental", + "electronic" + ], + "description": "Berlin-based experimental electronic.", + "streams": [ + { + "url": "https://cashmere-radio.radiocult.fm/stream", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "nightride-fm", + "name": "Nightride FM", + "category": "electronic", + "country": "FI", + "homepage": "https://nightride.fm/", + "genres": [ + "synthwave", + "darksynth", + "retrowave" + ], + "description": "Synthwave, darksynth, and retrowave.", + "streams": [ + { + "url": "https://stream.nightride.fm/nightride.m4a", + "format": "aac", + "bitrate": 256, + "label": "AAC 256", + "priority": 0 + } + ] + }, + { + "slug": "dublin-digital-radio", + "name": "Dublin Digital Radio", + "category": "electronic", + "country": "IE", + "homepage": "https://listen.dublindigitalradio.com/", + "genres": [ + "electronic", + "experimental" + ], + "description": "Indie and experimental electronic from Dublin.", + "streams": [ + { + "url": "https://dublin-digital-radio.radiocult.fm/stream", + "format": "mp3", + "bitrate": 256, + "label": "MP3 256", + "priority": 0 + } + ] + }, + { + "slug": "lyl-radio", + "name": "LYL Radio", + "category": "electronic", + "country": "FR", + "homepage": "https://lyl.live/", + "genres": [ + "experimental" + ], + "description": "Experimental radio and sonic art from Lyon.", + "streams": [ + { + "url": "https://icecast.lyl.live/live", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "soho-radio", + "name": "Soho Radio", + "category": "underground", + "country": "GB", + "homepage": "https://sohoradiolondon.com/", + "genres": [ + "eclectic", + "underground" + ], + "description": "Culture and music from London and NYC.", + "streams": [ + { + "url": "https://sohoradiomusic.doughunt.co.uk:8010/128mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "noods-radio", + "name": "Noods Radio", + "category": "underground", + "country": "GB", + "homepage": "https://noodsradio.com/", + "genres": [ + "eclectic", + "underground" + ], + "description": "Independent radio for music collectors from Bristol.", + "streams": [ + { + "url": "https://noods-radio.radiocult.fm/stream", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "resonance-fm", + "name": "Resonance 104.4 FM", + "category": "underground", + "country": "GB", + "homepage": "https://www.resonancefm.com/", + "genres": [ + "arts", + "experimental" + ], + "description": "Cultural programming and art from London.", + "streams": [ + { + "url": "http://stream.resonance.fm:8000/resonance", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "dandelion-radio", + "name": "Dandelion Radio", + "category": "underground", + "country": "GB", + "homepage": "https://dandelionradio.com/", + "genres": [ + "eclectic", + "freeform" + ], + "description": "Internet radio inspired by John Peel.", + "streams": [ + { + "url": "http://stream.dandelionradio.com:9414/", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "radio-meuh", + "name": "Radio Meuh", + "category": "underground", + "country": "FR", + "homepage": "https://www.radiomeuh.com/", + "genres": [ + "electronic", + "soul", + "funk" + ], + "description": "Electronic, soul, and funk from the French Alps.", + "streams": [ + { + "url": "https://radiomeuh.ice.infomaniak.ch/radiomeuh-128.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "oroko-radio", + "name": "Oroko Radio", + "category": "underground", + "country": "GH", + "homepage": "https://oroko.live/", + "genres": [ + "afro", + "indie", + "soul" + ], + "description": "Afro indie, folk, and soul from Accra, Ghana.", + "streams": [ + { + "url": "https://oroko-radio.radiocult.fm/stream", + "format": "mp3", + "bitrate": 320, + "label": "MP3 320", + "priority": 0 + } + ] + }, + { + "slug": "radio-al-hara", + "name": "Radio Al-Hara", + "category": "underground", + "country": "PS", + "homepage": "https://www.radioalhara.net/", + "genres": [ + "experimental" + ], + "description": "Underground Palestinian radio; experimental beats and talk.", + "streams": [ + { + "url": "https://radio-alhara.radiocult.fm/stream", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "wfmu-rock-soul", + "name": "WFMU — Rock 'n' Soul Ichiban", + "category": "underground", + "country": "US", + "homepage": "https://wfmu.org/", + "genres": [ + "rock", + "soul" + ], + "description": "WFMU stream for rock, R&B, and soul.", + "streams": [ + { + "url": "https://wfmu.org/wfmu_rock.pls", + "format": "pls", + "label": "PLS", + "priority": 0 + } + ] + }, + { + "slug": "wfmu-sheena", + "name": "WFMU — Sheena's Jungle Room", + "category": "underground", + "country": "US", + "homepage": "https://wfmu.org/", + "genres": [ + "garage", + "surf", + "rockabilly" + ], + "description": "WFMU stream for garage, surf, and rockabilly.", + "streams": [ + { + "url": "https://wfmu.org/wfmu_sheena.pls", + "format": "pls", + "label": "PLS", + "priority": 0 + } + ] + }, + { + "slug": "jazz24", + "name": "Jazz24", + "category": "jazz", + "country": "US", + "homepage": "https://www.jazz24.org/", + "genres": [ + "jazz" + ], + "description": "24/7 jazz from Seattle (KNKX).", + "streams": [ + { + "url": "https://knkx-live-a.edge.audiocdn.com/6285_256k", + "format": "aac", + "bitrate": 256, + "label": "AAC 256", + "priority": 0 + }, + { + "url": "https://knkx-live-a.edge.audiocdn.com/6285_128k", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ] + }, + { + "slug": "wbgo", + "name": "WBGO 88.3 — Jazz", + "category": "jazz", + "country": "US", + "homepage": "https://wbgo.org/", + "genres": [ + "jazz", + "blues" + ], + "description": "Jazz and blues from Newark/New York.", + "streams": [ + { + "url": "https://ais-sa8.cdnstream1.com/3629_128.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "tsf-jazz", + "name": "TSF Jazz", + "category": "jazz", + "country": "FR", + "homepage": "https://www.tsfjazz.com/", + "genres": [ + "jazz" + ], + "description": "Jazz and talk from Paris.", + "streams": [ + { + "url": "https://tsfjazz.ice.infomaniak.ch/tsfjazz-high.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "linn-jazz", + "name": "Linn Jazz", + "category": "jazz", + "country": "GB", + "homepage": "https://www.linn.co.uk/linn-radio", + "genres": [ + "jazz" + ], + "description": "Jazz classics and originals from Linn Records.", + "streams": [ + { + "url": "http://radio.linn.co.uk:8000/autodj", + "format": "mp3", + "bitrate": 320, + "label": "MP3 320", + "priority": 0 + } + ] + }, + { + "slug": "linn-classical", + "name": "Linn Classical", + "category": "classical", + "country": "GB", + "homepage": "https://www.linn.co.uk/linn-radio", + "genres": [ + "classical" + ], + "description": "Classical recordings from Linn Records.", + "streams": [ + { + "url": "http://radio.linn.co.uk:8004/autodj", + "format": "mp3", + "bitrate": 320, + "label": "MP3 320", + "priority": 0 + } + ] + }, + { + "slug": "radio-suisse-classique", + "name": "Radio Suisse Classique", + "category": "classical", + "country": "CH", + "homepage": "https://www.radioswissclassic.ch/en", + "genres": [ + "classical" + ], + "description": "Swiss public radio for classical and opera.", + "streams": [ + { + "url": "https://stream.srg-ssr.ch/m/rsc_de/mp3_128", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "wcrb-classical", + "name": "WCRB 99.5 Classical", + "category": "classical", + "country": "US", + "homepage": "https://www.classicalwcrb.org/", + "genres": [ + "classical" + ], + "description": "Boston classical; features the Boston Symphony Orchestra.", + "streams": [ + { + "url": "https://wgbh-live.streamguys1.com/classical-hi", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "drdicks-dub-shack", + "name": "Dr. Dick's Dub Shack", + "category": "reggae", + "country": "BM", + "homepage": "https://drdicksdubshack.com/", + "genres": [ + "dub", + "reggae", + "roots" + ], + "description": "Deep dub and roots from Bermuda.", + "streams": [ + { + "url": "http://streamer.radio.co/s0635c8b0d/listen", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "slug": "alpha-boys-school", + "name": "Alpha Boys School Radio", + "category": "reggae", + "country": "JM", + "homepage": "https://alphaboysschoolradio.com/", + "genres": [ + "ska", + "rocksteady", + "reggae" + ], + "description": "Ska and rocksteady from Kingston, Jamaica.", + "streams": [ + { + "url": "http://alphaboys-live.streamguys1.com/alphaboys.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "radio-nova", + "name": "Radio Nova", + "category": "world", + "country": "FR", + "homepage": "https://www.nova.fr/", + "genres": [ + "world", + "jazz", + "hip-hop" + ], + "description": "French station for global sounds, jazz, and hip-hop.", + "streams": [ + { + "url": "https://novazz.ice.infomaniak.ch/novazz-128.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "slug": "rfi-monde", + "name": "RFI Monde", + "category": "world", + "country": "FR", + "homepage": "https://www.rfi.fr/", + "genres": [ + "news", + "world" + ], + "description": "Global news and music from France in French.", + "streams": [ + { + "url": "http://live02.rfi.fr/rfimonde-96k.mp3", + "format": "mp3", + "bitrate": 96, + "label": "MP3 96", + "priority": 0 + } + ] + }, + { + "slug": "radio-france-inter", + "name": "France Inter", + "category": "world", + "country": "FR", + "homepage": "https://www.radiofrance.fr/franceinter", + "genres": [ + "news", + "talk" + ], + "description": "French public talk radio.", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/franceinter-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + }, + { + "url": "https://icecast.radiofrance.fr/franceinter-midfi.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ] + }, + { + "slug": "france-musique", + "name": "France Musique", + "category": "classical", + "country": "FR", + "homepage": "https://www.radiofrance.fr/francemusique", + "genres": [ + "classical", + "jazz" + ], + "description": "French classical and jazz public radio.", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/francemusique-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + } + ] + }, + { + "slug": "france-culture", + "name": "France Culture", + "category": "world", + "country": "FR", + "homepage": "https://www.radiofrance.fr/franceculture", + "genres": [ + "culture", + "talk" + ], + "description": "Culture, ideas, talk in French.", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/franceculture-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + }, + { + "url": "https://icecast.radiofrance.fr/franceculture-midfi.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ] + } +] \ No newline at end of file diff --git a/data/seed/stations-underground.json b/data/seed/stations-underground.json new file mode 100644 index 0000000..221a1ca --- /dev/null +++ b/data/seed/stations-underground.json @@ -0,0 +1,1206 @@ +[ + { + "uuid": "e8c409e9-ee6f-4670-84bf-02485247def0", + "slug": "rb-e8c409e9-foundation-fm", + "name": "Foundation FM", + "category": "underground", + "country": "GB", + "homepage": "https://foundation.fm/", + "genres": [ + "community radio", + "electronic", + "hip-hop", + "indie", + "jazz" + ], + "description": null, + "image_url": "https://foundation.fm/images/favicon/favicon.ico", + "source": "radiobrowser", + "source_ref": "e8c409e9-ee6f-4670-84bf-02485247def0", + "streams": [ + { + "url": "https://streamer.radio.co/s0628bdd53/listen", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "uuid": "7811e960-53ef-4e2c-a739-e2dff567ae86", + "slug": "rb-7811e960-aaja-radio-channel-1", + "name": "Aaja Radio | Channel 1", + "category": "underground", + "country": "GB", + "homepage": "https://aajamusic.com/radio", + "genres": [ + "music", + "variety", + "eclectic" + ], + "description": null, + "image_url": "https://aajamusic.com/_nuxt/icons/icon_64x64.fafdcd.png", + "source": "radiobrowser", + "source_ref": "7811e960-53ef-4e2c-a739-e2dff567ae86", + "streams": [ + { + "url": "https://aaja.radiocult.fm/stream", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "uuid": "ea6e4da5-087d-421c-95b6-5ccbedc58468", + "slug": "rb-ea6e4da5-aaja-radio-channel-2", + "name": "Aaja Radio | Channel 2", + "category": "underground", + "country": "GB", + "homepage": "https://aajamusic.com/radio", + "genres": [ + "music", + "variety", + "eclectic" + ], + "description": null, + "image_url": "https://aajamusic.com/_nuxt/icons/icon_64x64.fafdcd.png", + "source": "radiobrowser", + "source_ref": "ea6e4da5-087d-421c-95b6-5ccbedc58468", + "streams": [ + { + "url": "https://aaja-2.radiocult.fm/stream", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "uuid": "1cc8e314-8a4f-4225-af5f-69e064fcb238", + "slug": "rb-1cc8e314-bloop-london-radio", + "name": "Bloop London Radio", + "category": "underground", + "country": "GB", + "homepage": "https://blooplondon.com/", + "genres": [ + "electronic", + "house" + ], + "description": null, + "image_url": "https://blooplondon.com/favicon.ico", + "source": "radiobrowser", + "source_ref": "1cc8e314-8a4f-4225-af5f-69e064fcb238", + "streams": [ + { + "url": "https://radio.canstream.co.uk:8058/live.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "cac4c694-99b2-40eb-8957-6361e7d3768e", + "slug": "rb-cac4c694-reform-radio", + "name": "Reform Radio", + "category": "underground", + "country": "GB", + "homepage": "https://www.reformradio.co.uk/", + "genres": [ + "music", + "variety" + ], + "description": null, + "image_url": "https://www.reformradio.co.uk/favicon.ico", + "source": "radiobrowser", + "source_ref": "cac4c694-99b2-40eb-8957-6361e7d3768e", + "streams": [ + { + "url": "https://testform.out.airtime.pro/testform_a", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "uuid": "5c353e67-3f33-45b7-8a7f-eb4571907c4e", + "slug": "rb-5c353e67-1btn", + "name": "1BTN", + "category": "underground", + "country": "GB", + "homepage": "https://1btn.fm/", + "genres": [ + "electronic", + "funk", + "house", + "reggae", + "soul" + ], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "5c353e67-3f33-45b7-8a7f-eb4571907c4e", + "streams": [ + { + "url": "https://edge.clrmedia.co.uk/obfm_mp3", + "format": "mp3", + "bitrate": null, + "label": "MP3", + "priority": 0 + } + ] + }, + { + "uuid": "5f5a7aec-b11b-432c-9866-6df57e111aec", + "slug": "rb-5f5a7aec-sub-fm", + "name": "Sub.fm", + "category": "underground", + "country": "GB", + "homepage": "https://www.sub.fm/", + "genres": [], + "description": null, + "image_url": "https://www.sub.fm/wp-content/uploads/2017/07/cropped-black-sub-180x180.png", + "source": "radiobrowser", + "source_ref": "5f5a7aec-b11b-432c-9866-6df57e111aec", + "streams": [ + { + "url": "http://subfm.radioca.st/Sub.FM", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "uuid": "b876aaf5-bd27-4576-927c-802acd73b4ae", + "slug": "rb-b876aaf5-radio-wigwam", + "name": "Radio Wigwam", + "category": "underground", + "country": "GB", + "homepage": "https://radiowigwam.co.uk/", + "genres": [ + "indie rock" + ], + "description": null, + "image_url": "https://radiowigwam.co.uk/wp-content/uploads/2024/08/wiwamnewdark.jpg.webp", + "source": "radiobrowser", + "source_ref": "b876aaf5-bd27-4576-927c-802acd73b4ae", + "streams": [ + { + "url": "https://streaming.broadcast.radio/radio-wigwam", + "format": "mp3", + "bitrate": null, + "label": "MP3", + "priority": 0 + } + ] + }, + { + "uuid": "24273571-703e-4373-b715-d7e7680d7599", + "slug": "rb-24273571-skylab-radio", + "name": "Skylab Radio", + "category": "underground", + "country": "GB", + "homepage": "https://uksoutha.streaming.broadcast.radio:29690/skylab-radio-limited", + "genres": [], + "description": null, + "image_url": "https://static.wixstatic.com/media/c558e3_a1b2627549444e919504fa15988a9f57~mv2.png/v1/fill/w_198,h_184,al_c,q_85,usm_0.66_1.00_0.01,enc_avif,quality_auto/c558e3_a1b2627549444e919504fa15988a9f57~mv2.png", + "source": "radiobrowser", + "source_ref": "24273571-703e-4373-b715-d7e7680d7599", + "streams": [ + { + "url": "https://uksoutha.streaming.broadcast.radio:29690/skylab-radio-limited", + "format": "mp3", + "bitrate": null, + "label": "MP3", + "priority": 0 + } + ] + }, + { + "uuid": "5a04743b-6d98-4af5-9190-9aa44f030cab", + "slug": "rb-5a04743b-echobox", + "name": "Echobox", + "category": "underground", + "country": "NL", + "homepage": "https://www.echobox.radio/", + "genres": [], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "5a04743b-6d98-4af5-9190-9aa44f030cab", + "streams": [ + { + "url": "https://play.streamnerd.nl/echobox/echobox/icecast.audio", + "format": "mp3", + "bitrate": null, + "label": "MP3", + "priority": 0 + } + ] + }, + { + "uuid": "f5f9d68a-b14d-4e23-8de0-b1ea6e2151bf", + "slug": "rb-f5f9d68a-operator-radio", + "name": "Operator Radio", + "category": "underground", + "country": "NL", + "homepage": "https://operator-radio.com/", + "genres": [], + "description": null, + "image_url": "https://operator-radio.com/favicon.ico", + "source": "radiobrowser", + "source_ref": "f5f9d68a-b14d-4e23-8de0-b1ea6e2151bf", + "streams": [ + { + "url": "https://origin.streamnerd.nl/operator/operator/icecast.audio", + "format": "aac", + "bitrate": 320, + "label": "AAC+ 320", + "priority": 0 + } + ] + }, + { + "uuid": "bb5b96e7-2311-4b60-849c-a700e8a4e3be", + "slug": "rb-bb5b96e7-byte-fm", + "name": "Byte.fm", + "category": "underground", + "country": "DE", + "homepage": "https://www.byte.fm/", + "genres": [], + "description": null, + "image_url": "https://www.byte.fm/static/favicon/favicon.svg?v=2024.03.06.3490-6d33a785", + "source": "radiobrowser", + "source_ref": "bb5b96e7-2311-4b60-849c-a700e8a4e3be", + "streams": [ + { + "url": "https://streams.byte.fm/live/mp3-128/", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "6b4c2748-07ee-11e8-ae97-52543be04c81", + "slug": "rb-6b4c2748-radio-80000", + "name": "Radio 80000", + "category": "underground", + "country": "DE", + "homepage": "https://www.radio80k.de/", + "genres": [ + "community radio", + "freeform", + "variety" + ], + "description": null, + "image_url": "https://www.radio80k.de/app/uploads/2022/10/cropped-favicon-8000-192x192.gif", + "source": "radiobrowser", + "source_ref": "6b4c2748-07ee-11e8-ae97-52543be04c81", + "streams": [ + { + "url": "https://radio80k.out.airtime.pro:8000/radio80k_a", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "uuid": "961591c6-0601-11e8-ae97-52543be04c81", + "slug": "rb-961591c6-fluxfm", + "name": "FluxFM", + "category": "underground", + "country": "DE", + "homepage": "https://www.fluxfm.de/", + "genres": [ + "alternative", + "indie", + "rock" + ], + "description": null, + "image_url": "https://www.fluxfm.de/assets/favicons/apple-icon-120x120.png", + "source": "radiobrowser", + "source_ref": "961591c6-0601-11e8-ae97-52543be04c81", + "streams": [ + { + "url": "http://streams.fluxfm.de/live/mp3-320/audio/", + "format": "mp3", + "bitrate": null, + "label": "MP3", + "priority": 0 + } + ] + }, + { + "uuid": "9643c506-0601-11e8-ae97-52543be04c81", + "slug": "rb-9643c506-fluxfm-techno-underground", + "name": "FluxFM - Techno Underground", + "category": "underground", + "country": "DE", + "homepage": "http://www.fluxfm.de/", + "genres": [ + "techno" + ], + "description": null, + "image_url": "http://www.fluxfm.de/assets/favicons/apple-icon-120x120.png", + "source": "radiobrowser", + "source_ref": "9643c506-0601-11e8-ae97-52543be04c81", + "streams": [ + { + "url": "http://fluxfm.streamabc.net/flx-technounderground-mp3-128-7228171?sABC=69687sp6%230%23r30o443r1929r059s085628511796n57%23nhqvb&aw_0_1st.playerid=audio&amsparams=playerid:audio;skey:1768456134", + "format": "mp3", + "bitrate": null, + "label": "MP3", + "priority": 0 + } + ] + }, + { + "uuid": "960553a5-0601-11e8-ae97-52543be04c81", + "slug": "rb-960553a5-radio-eins", + "name": "Radio Eins", + "category": "underground", + "country": "DE", + "homepage": "http://www.radioeins.de/", + "genres": [ + "adult contemporary", + "alternative", + "ard", + "information", + "pop", + "public radio" + ], + "description": null, + "image_url": "http://www.radioeins.de/content/dam/rbb/rbb/logos/touch/rad-128.png", + "source": "radiobrowser", + "source_ref": "960553a5-0601-11e8-ae97-52543be04c81", + "streams": [ + { + "url": "http://f121.rndfnk.com/ard/rbb/radioeins/live/mp3/128/stream.mp3?cid=01FC1WH12KJ93TCQPDSE2E5PZ9&sid=38HoeEhwMU9ZjQaArYLcNuLu9LN&token=VXH8C52tOJ6o_G5uLXexxjt84DXyHGfH0RABfQljedk&tvf=8PpblQ7uihhmMTIxLnJuZGZuay5jb20", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "cfb85c6c-76b4-11ea-b1cf-52543be04c81", + "slug": "rb-cfb85c6c-radio-helsinki-98-5-mhz", + "name": "Radio Helsinki 98,5 Mhz", + "category": "underground", + "country": "FI", + "homepage": "https://www.radiohelsinki.fi/", + "genres": [], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "cfb85c6c-76b4-11ea-b1cf-52543be04c81", + "streams": [ + { + "url": "https://stream.radiohelsinki.fi/stream", + "format": "mp3", + "bitrate": 256, + "label": "MP3 256", + "priority": 0 + } + ] + }, + { + "uuid": "e6a7e493-d819-443a-ab67-d1295cbb2f83", + "slug": "rb-e6a7e493-radio-helsinki", + "name": "Radio Helsinki", + "category": "underground", + "country": "AT", + "homepage": "https://helsinki.at/", + "genres": [], + "description": null, + "image_url": "https://live.helsinki.at/img/helsinki.png", + "source": "radiobrowser", + "source_ref": "e6a7e493-d819-443a-ab67-d1295cbb2f83", + "streams": [ + { + "url": "https://live.helsinki.at:8088/live160.mp3", + "format": "mp3", + "bitrate": null, + "label": "MP3", + "priority": 0 + } + ] + }, + { + "uuid": "c7b1c000-075f-48d8-868d-3e4503ed06d7", + "slug": "rb-c7b1c000-rts-couleur-3", + "name": "RTS Couleur 3", + "category": "underground", + "country": "CH", + "homepage": "https://www.rts.ch/couleur3", + "genres": [], + "description": null, + "image_url": "https://www.rts.ch/favicon.ico", + "source": "radiobrowser", + "source_ref": "c7b1c000-075f-48d8-868d-3e4503ed06d7", + "streams": [ + { + "url": "http://stream.srg-ssr.ch/m/couleur3/mp3_128", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "a029e78b-67c0-4d09-9b35-206c7061c422", + "slug": "rb-a029e78b-movement-radio-1", + "name": "movement.radio 1", + "category": "underground", + "country": "GR", + "homepage": "http://www.movement.radio/", + "genres": [ + "ambient", + "bass", + "electro", + "electronic", + "experimental", + "hip-hop" + ], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "a029e78b-67c0-4d09-9b35-206c7061c422", + "streams": [ + { + "url": "https://movementathens.out.airtime.pro/movementathens_a", + "format": "aac", + "bitrate": 192, + "label": "AAC 192", + "priority": 0 + } + ] + }, + { + "uuid": "4d688959-d7e8-444b-acef-404d1cb623f9", + "slug": "rb-4d688959-movement-radio-2", + "name": "movement.radio 2", + "category": "underground", + "country": "GR", + "homepage": "http://www.movement.radio/", + "genres": [ + "ambient", + "bass", + "electro", + "electronic", + "experimental", + "hip-hop" + ], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "4d688959-d7e8-444b-acef-404d1cb623f9", + "streams": [ + { + "url": "https://movementathens2.out.airtime.pro/movementathens2_a", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "uuid": "960bec79-0601-11e8-ae97-52543be04c81", + "slug": "rb-960bec79-tilos-r-di", + "name": "Tilos Rádió", + "category": "underground", + "country": "HU", + "homepage": "http://tilos.hu/", + "genres": [ + "alternative", + "community radio", + "free radio", + "freeform", + "independent" + ], + "description": null, + "image_url": "http://tilos.hu/favicon.ico", + "source": "radiobrowser", + "source_ref": "960bec79-0601-11e8-ae97-52543be04c81", + "streams": [ + { + "url": "http://stream.tilos.hu/tilos", + "format": "mp3", + "bitrate": 256, + "label": "MP3 256", + "priority": 0 + } + ] + }, + { + "uuid": "3859f931-66c5-4264-afc1-682e186d7263", + "slug": "rb-3859f931-radio-campus-paris", + "name": "Radio Campus Paris", + "category": "underground", + "country": "FR", + "homepage": "https://www.radiocampusparis.org/", + "genres": [ + "modern", + "radio by young people for young people" + ], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "3859f931-66c5-4264-afc1-682e186d7263", + "streams": [ + { + "url": "https://www.radiocampusparis.org/stream/", + "format": "mp3", + "bitrate": 128000, + "label": "MP3 128000", + "priority": 0 + } + ] + }, + { + "uuid": "a707f84a-9ae4-40bf-a176-fcb4cdbc9fbe", + "slug": "rb-a707f84a-wmbr-88-1", + "name": "WMBR 88.1", + "category": "underground", + "country": "US", + "homepage": "https://wmbr.org/", + "genres": [], + "description": null, + "image_url": "https://wmbr.org/images/wmbr_top.gif", + "source": "radiobrowser", + "source_ref": "a707f84a-9ae4-40bf-a176-fcb4cdbc9fbe", + "streams": [ + { + "url": "https://wmbr.org:8002/hi", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "96187adc-0601-11e8-ae97-52543be04c81", + "slug": "rb-96187adc-whrb-95-3-harvard-radio-broadcasting-cam", + "name": "WHRB 95.3 - Harvard Radio Broadcasting - Cambridge, MA", + "category": "underground", + "country": "US", + "homepage": "https://www.whrb.org/", + "genres": [ + "university radio", + "variety" + ], + "description": null, + "image_url": "https://www.whrb.org/icons/icon-144x144.png?v=f4237d2445d95894bb92f02772bca78d", + "source": "radiobrowser", + "source_ref": "96187adc-0601-11e8-ae97-52543be04c81", + "streams": [ + { + "url": "http://stream.whrb.org:8000/whrb-mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "a256200d-2ec4-11e9-8f31-52543be04c81", + "slug": "rb-a256200d-kalx-90-7fm-berkeley", + "name": "KALX 90.7FM Berkeley", + "category": "underground", + "country": "US", + "homepage": "https://www.kalx.berkeley.edu/", + "genres": [ + "aac", + "berkeley", + "uc berkeley", + "university radio" + ], + "description": null, + "image_url": "https://www.kalx.berkeley.edu/favicon.ico", + "source": "radiobrowser", + "source_ref": "a256200d-2ec4-11e9-8f31-52543be04c81", + "streams": [ + { + "url": "https://stream.kalx.berkeley.edu:8443/kalx-320.aac", + "format": "aac", + "bitrate": 320, + "label": "AAC+ 320", + "priority": 0 + } + ] + }, + { + "uuid": "960dd86c-0601-11e8-ae97-52543be04c81", + "slug": "rb-960dd86c-wrek-91-1-atlanta-ga", + "name": "WREK 91.1 Atlanta, GA", + "category": "underground", + "country": "US", + "homepage": "http://www.wrek.org/", + "genres": [ + "atlanta", + "college radio", + "georgia", + "georgia tech", + "jazz" + ], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "960dd86c-0601-11e8-ae97-52543be04c81", + "streams": [ + { + "url": "http://streaming.wrek.org:8000/main/128kb.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "962b23be-0601-11e8-ae97-52543be04c81", + "slug": "rb-962b23be-wxyc-89-3-university-of-north-carolina-c", + "name": "WXYC 89.3 University of North Carolina - Chapel Hill, NC", + "category": "underground", + "country": "US", + "homepage": "http://www.wxyc.org/", + "genres": [ + "chapel hill", + "university radio" + ], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "962b23be-0601-11e8-ae97-52543be04c81", + "streams": [ + { + "url": "http://audio-mp3.ibiblio.org:8000/wxyc.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "bb15d622-7137-4269-8f33-71cccb208732", + "slug": "rb-bb15d622-kzsc-88-1-uc-santa-cruz-new-stream", + "name": "KZSC 88.1 - UC Santa Cruz (New Stream)", + "category": "underground", + "country": "US", + "homepage": "https://kzsc.org/", + "genres": [ + "university radio" + ], + "description": null, + "image_url": "https://kzsc.org/wp-content/uploads/2018/12/kzsc-88.1-logo-reverse-retina.png", + "source": "radiobrowser", + "source_ref": "bb15d622-7137-4269-8f33-71cccb208732", + "streams": [ + { + "url": "https://kzscfms1-geckohost.radioca.st/kzschigh", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "7a08d36e-7384-4cb3-a6b2-70f0a3738ae9", + "slug": "rb-7a08d36e-kdvs-davis", + "name": "KDVS Davis", + "category": "underground", + "country": "US", + "homepage": "https://kdvs.org/", + "genres": [ + "college radio", + "community radio", + "davis", + "local news", + "sacramento", + "uc davis" + ], + "description": null, + "image_url": "https://kdvs.org/favicon.ico", + "source": "radiobrowser", + "source_ref": "7a08d36e-7384-4cb3-a6b2-70f0a3738ae9", + "streams": [ + { + "url": "https://archives.kdvs.org/stream", + "format": "aac", + "bitrate": 128, + "label": "AAC 128", + "priority": 0 + } + ] + }, + { + "uuid": "fbd47272-fdc6-457c-ac93-98410bb4c777", + "slug": "rb-fbd47272-wprb-103-3-fm-princeton-nj", + "name": "WPRB 103.3 FM - Princeton, NJ", + "category": "underground", + "country": "US", + "homepage": "http://wprb.com/", + "genres": [ + "commercial", + "freeform", + "indie", + "new jersey", + "not-for-profit", + "student-managed" + ], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "fbd47272-fdc6-457c-ac93-98410bb4c777", + "streams": [ + { + "url": "http://wprb.streamguys1.com/live", + "format": "aac", + "bitrate": 96, + "label": "AAC 96", + "priority": 0 + } + ] + }, + { + "uuid": "2768b05d-96a2-4b74-9f92-c0fa7e979eb5", + "slug": "rb-2768b05d-wzbc", + "name": "WZBC ", + "category": "underground", + "country": "US", + "homepage": "https://www.wzbc.org/", + "genres": [ + "arts", + "chicago", + "community", + "politics" + ], + "description": null, + "image_url": "https://firebasestorage.googleapis.com/v0/b/radiogalaxy-580f4.appspot.com/o/images%2FIMG_20240419_134421735.jpg?alt=media&token=c7e2e30c-0343-4307-a85b-1870370d2286", + "source": "radiobrowser", + "source_ref": "2768b05d-96a2-4b74-9f92-c0fa7e979eb5", + "streams": [ + { + "url": "https://stream.wzbc.org/wzbc", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "0281ef6c-6f42-11e8-83fa-52543be04c81", + "slug": "rb-0281ef6c-kfjc", + "name": "KFJC ", + "category": "underground", + "country": null, + "homepage": null, + "genres": [], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "0281ef6c-6f42-11e8-83fa-52543be04c81", + "streams": [ + { + "url": "http://aac.kfjc.org/listen.pls", + "format": "aac", + "bitrate": 192, + "label": "AAC+ 192", + "priority": 0 + } + ] + }, + { + "uuid": "961e5d5c-0601-11e8-ae97-52543be04c81", + "slug": "rb-961e5d5c-kxlu-88-9fm-los-angeles-ca", + "name": "KXLU 88.9FM Los Angeles, CA", + "category": "underground", + "country": "US", + "homepage": "http://kxlu.com/", + "genres": [ + "college radio", + "freeform", + "los angeles" + ], + "description": null, + "image_url": "https://kxlu.com/wp-content/uploads/2019/02/kxlusplatblack-125x125.png", + "source": "radiobrowser", + "source_ref": "961e5d5c-0601-11e8-ae97-52543be04c81", + "streams": [ + { + "url": "http://www.ednixon.com:8120/stream", + "format": "mp3", + "bitrate": 160, + "label": "MP3 160", + "priority": 0 + } + ] + }, + { + "uuid": "4af97ba5-4546-4132-a94a-ebca09214cfb", + "slug": "rb-4af97ba5-kcsb-uc-santa-barbara", + "name": "KCSB UC Santa Barbara", + "category": "underground", + "country": "US", + "homepage": "https://www.kcsb.org/", + "genres": [ + "college radio" + ], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "4af97ba5-4546-4132-a94a-ebca09214cfb", + "streams": [ + { + "url": "https://kcsb.streamguys1.com/live", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "6a8e0467-f09b-456f-a7af-b98ed961a043", + "slug": "rb-6a8e0467-wluw-88-7-fm-chicago-sound-alliance", + "name": "WLUW 88.7 FM, Chicago Sound Alliance", + "category": "underground", + "country": "US", + "homepage": "https://wluw.org/", + "genres": [ + "independent radio", + "student radio" + ], + "description": null, + "image_url": "https://images.squarespace-cdn.com/content/v1/68d6f37eaca1b53e1b01eff4/79ab2be3-9880-49c0-80a6-a2f5c9a9a05d/favicon.ico", + "source": "radiobrowser", + "source_ref": "6a8e0467-f09b-456f-a7af-b98ed961a043", + "streams": [ + { + "url": "https://ice26.securenetsystems.net/WLUW?playSessionID=AD47E8CB-E610-56AC-09E96DB77F17E09B", + "format": "aac", + "bitrate": 64, + "label": "AAC+ 64", + "priority": 0 + } + ] + }, + { + "uuid": "962df676-0601-11e8-ae97-52543be04c81", + "slug": "rb-962df676-wusb-90-1-state-university-of-new-york-a", + "name": "WUSB 90.1 State University of New York at Stony Brook, NY", + "category": "underground", + "country": "US", + "homepage": "http://www.wusb.fm/", + "genres": [ + "freeform", + "long island", + "stony brook", + "university radio" + ], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "962df676-0601-11e8-ae97-52543be04c81", + "streams": [ + { + "url": "http://stream.wusb.stonybrook.edu:8090/;.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "1717c037-d5ce-409b-ab76-075ef7395154", + "slug": "rb-1717c037-bff-fm", + "name": "BFF.fm", + "category": "underground", + "country": "US", + "homepage": "https://bff.fm/", + "genres": [], + "description": null, + "image_url": "https://aw.bff.fm/assets/favicons/apple-touch-icon/d59257b71b2555397520753bc349890b785ca5d5.png", + "source": "radiobrowser", + "source_ref": "1717c037-d5ce-409b-ab76-075ef7395154", + "streams": [ + { + "url": "https://stream.bff.fm/1/mp3.mp3?_cacheBust=25055", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "7c84ac30-1a0f-42f7-8b0d-40b5c6548abf", + "slug": "rb-7c84ac30-dkfm-shoegaze-radio", + "name": "DKFM Shoegaze Radio", + "category": "underground", + "country": "CA", + "homepage": "https://decayfm.com/", + "genres": [], + "description": null, + "image_url": "https://i0.wp.com/decayfm.com/wp-content/uploads/2018/12/cropped-512-1.jpg?fit=180%2c180&ssl=1", + "source": "radiobrowser", + "source_ref": "7c84ac30-1a0f-42f7-8b0d-40b5c6548abf", + "streams": [ + { + "url": "https://kathy.torontocast.com:2005/stream", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "961455d5-0601-11e8-ae97-52543be04c81", + "slug": "rb-961455d5-idobi-anthm", + "name": "idobi Anthm", + "category": "underground", + "country": "US", + "homepage": "http://idobi.com/", + "genres": [ + "indie", + "indie rock", + "new music" + ], + "description": null, + "image_url": "https://idobi.com/wp-content/themes/idobi-2022/assets/img/idobi%20logo%20medium%20transparent.png", + "source": "radiobrowser", + "source_ref": "961455d5-0601-11e8-ae97-52543be04c81", + "streams": [ + { + "url": "http://idobianthm.idobi.com/;stream.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "9614550c-0601-11e8-ae97-52543be04c81", + "slug": "rb-9614550c-idobi-howl", + "name": "idobi Howl", + "category": "underground", + "country": "US", + "homepage": "http://idobi.com/", + "genres": [ + "hardcore", + "metal", + "new music" + ], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "9614550c-0601-11e8-ae97-52543be04c81", + "streams": [ + { + "url": "http://idobihowl.idobi.com/;stream.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "e2eff271-69e7-11ea-b1cf-52543be04c81", + "slug": "rb-e2eff271-radio-free-brooklyn", + "name": "Radio Free Brooklyn", + "category": "underground", + "country": "US", + "homepage": "https://radiofreebrooklyn.com/", + "genres": [], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "e2eff271-69e7-11ea-b1cf-52543be04c81", + "streams": [ + { + "url": "https://patmos.cdnstream.com/proxy/ttenney1/?mp=/listen&esPlayer&cb=239941.mp3", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ] + }, + { + "uuid": "6a34086c-64e1-4f8b-981a-ae792692aabf", + "slug": "rb-6a34086c-triple-r-102-7fm", + "name": "Triple R 102.7FM", + "category": "underground", + "country": "AU", + "homepage": "https://www.rrr.org.au/", + "genres": [ + "community radio", + "independent" + ], + "description": null, + "image_url": "https://www.rrr.org.au/assets/main/assets/public/favicon/favicon-192.a118bf57.png", + "source": "radiobrowser", + "source_ref": "6a34086c-64e1-4f8b-981a-ae792692aabf", + "streams": [ + { + "url": "https://ondemand.rrr.org.au/getstream?id=wshq", + "format": "aac", + "bitrate": null, + "label": "AAC", + "priority": 0 + } + ] + }, + { + "uuid": "abf0d086-aeaa-4c06-9196-bc9a0721b8b8", + "slug": "rb-abf0d086-pbs-106-7fm", + "name": "PBS 106.7FM", + "category": "underground", + "country": "AU", + "homepage": "https://www.pbsfm.org.au/", + "genres": [ + "australian music", + "blues", + "community radio", + "electronic", + "funk", + "global" + ], + "description": null, + "image_url": "https://www.pbsfm.org.au/", + "source": "radiobrowser", + "source_ref": "abf0d086-aeaa-4c06-9196-bc9a0721b8b8", + "streams": [ + { + "url": "https://playerservices.streamtheworld.com/api/livestream-redirect/3PBS_FMAACHIGH.aac", + "format": "aac", + "bitrate": 192, + "label": "AAC+ 192", + "priority": 0 + } + ] + }, + { + "uuid": "6c5babb6-2c8d-44dc-82fe-74ab38c87edf", + "slug": "rb-6c5babb6-fbi-radio", + "name": "FBi Radio", + "category": "underground", + "country": "AU", + "homepage": "https://fbiradio.com/", + "genres": [], + "description": null, + "image_url": "https://d1kt6vnx6cjjqh.cloudfront.net/wp-content/themes/fbi_sf/images/favicon.ico.gzip", + "source": "radiobrowser", + "source_ref": "6c5babb6-2c8d-44dc-82fe-74ab38c87edf", + "streams": [ + { + "url": "https://streamer.fbiradio.com/stream", + "format": "mp3", + "bitrate": 320, + "label": "MP3 320", + "priority": 0 + } + ] + }, + { + "uuid": "32f8169a-eaa5-4bd6-b391-122b5419bcd1", + "slug": "rb-32f8169a-2ser-107-3-fm", + "name": "2SER 107.3 FM", + "category": "underground", + "country": "AU", + "homepage": "https://2ser.com/", + "genres": [ + "alternative", + "college radio", + "community", + "country", + "dance", + "eclectic" + ], + "description": null, + "image_url": "https://d1dbgh6ga9ets8.cloudfront.net/wp-content/uploads/2017/12/logo.png", + "source": "radiobrowser", + "source_ref": "32f8169a-eaa5-4bd6-b391-122b5419bcd1", + "streams": [ + { + "url": "http://138.25.219.25:840/2ser128.MP3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "b6794d5d-2053-4390-82b5-5dc750972069", + "slug": "rb-b6794d5d-4zzz", + "name": "4zzz", + "category": "underground", + "country": "AU", + "homepage": "https://4zzz.org.au/", + "genres": [ + "brisbane", + "independent" + ], + "description": null, + "image_url": null, + "source": "radiobrowser", + "source_ref": "b6794d5d-2053-4390-82b5-5dc750972069", + "streams": [ + { + "url": "https://iheart.4zzz.org.au/4zzz", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ] + }, + { + "uuid": "3cae3864-217f-4032-b3dc-61b0b33741f1", + "slug": "rb-3cae3864-rtrfm", + "name": "RTRFM", + "category": "underground", + "country": "AU", + "homepage": "https://rtrfm.com.au/", + "genres": [], + "description": null, + "image_url": "https://rtrfm.com.au/wp-content/smush-webp/2024/03/cropped-RTRFM_Favicon-192x192.png.webp", + "source": "radiobrowser", + "source_ref": "3cae3864-217f-4032-b3dc-61b0b33741f1", + "streams": [ + { + "url": "https://live.rtrfm.com.au/stream1", + "format": "aac", + "bitrate": 64, + "label": "AAC+ 64", + "priority": 0 + } + ] + } +] diff --git a/data/seed/stations.json b/data/seed/stations.json index 6e4ea56..6d59d84 100644 --- a/data/seed/stations.json +++ b/data/seed/stations.json @@ -1,602 +1,602 @@ [ { - "slug": "fip", - "name": "FIP", - "homepage": "https://www.radiofrance.fr/fip", - "country": "FR", - "genres": [ - "eclectic", - "jazz", - "rock", - "world" - ], - "description": "French public radio mixing jazz, rock, and world music. Ad-free.", - "image_url": "https://www.radiofrance.fr/s3/cruiser-production/2022/05/04571615-2745-4f05-829f-e8d22b7ddee0/200x200_fip_ok.jpg", - "streams": [ - { - "url": "https://icecast.radiofrance.fr/fip-hifi.aac", - "format": "aac", - "bitrate": 192, - "label": "HiFi AAC", - "priority": 0 - }, - { - "url": "https://icecast.radiofrance.fr/fip-midfi.mp3", - "format": "mp3", - "bitrate": 128, - "label": "MP3 128", - "priority": 1 - } - ], - "category": "fip" + "slug": "fip", + "name": "FIP", + "homepage": "https://www.radiofrance.fr/fip", + "country": "FR", + "genres": [ + "eclectic", + "jazz", + "rock", + "world" + ], + "description": "French public radio mixing jazz, rock, and world music. Ad-free.", + "image_url": "https://www.radiofrance.fr/s3/cruiser-production/2022/05/04571615-2745-4f05-829f-e8d22b7ddee0/200x200_fip_ok.jpg", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/fip-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + }, + { + "url": "https://icecast.radiofrance.fr/fip-midfi.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ], + "category": "fip" }, { - "slug": "fip-jazz", - "name": "FIP Jazz", - "homepage": "https://www.radiofrance.fr/fip/radio-jazz", - "country": "FR", - "genres": [ - "jazz" - ], - "description": "Curated jazz in all forms from Radio France.", - "streams": [ - { - "url": "https://icecast.radiofrance.fr/fipjazz-hifi.aac", - "format": "aac", - "bitrate": 192, - "label": "HiFi AAC", - "priority": 0 - }, - { - "url": "https://icecast.radiofrance.fr/fipjazz-midfi.mp3", - "format": "mp3", - "bitrate": 128, - "label": "MP3 128", - "priority": 1 - } - ], - "category": "fip" + "slug": "fip-jazz", + "name": "FIP Jazz", + "homepage": "https://www.radiofrance.fr/fip/radio-jazz", + "country": "FR", + "genres": [ + "jazz" + ], + "description": "Curated jazz in all forms from Radio France.", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/fipjazz-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + }, + { + "url": "https://icecast.radiofrance.fr/fipjazz-midfi.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ], + "category": "fip" }, { - "slug": "fip-reggae", - "name": "FIP Reggae", - "homepage": "https://www.radiofrance.fr/fip/radio-reggae", - "country": "FR", - "genres": [ - "reggae", - "dub" - ], - "description": "Reggae channel from FIP.", - "streams": [ - { - "url": "https://icecast.radiofrance.fr/fipreggae-hifi.aac", - "format": "aac", - "bitrate": 192, - "label": "HiFi AAC", - "priority": 0 - }, - { - "url": "https://icecast.radiofrance.fr/fipreggae-midfi.mp3", - "format": "mp3", - "bitrate": 128, - "label": "MP3 128", - "priority": 1 - } - ], - "category": "fip" + "slug": "fip-reggae", + "name": "FIP Reggae", + "homepage": "https://www.radiofrance.fr/fip/radio-reggae", + "country": "FR", + "genres": [ + "reggae", + "dub" + ], + "description": "Reggae channel from FIP.", + "streams": [ + { + "url": "https://icecast.radiofrance.fr/fipreggae-hifi.aac", + "format": "aac", + "bitrate": 192, + "label": "HiFi AAC", + "priority": 0 + }, + { + "url": "https://icecast.radiofrance.fr/fipreggae-midfi.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ], + "category": "fip" }, { - "slug": "intergalactic-fm", - "name": "Intergalactic FM", - "homepage": "https://intergalactic.fm/", - "country": "NL", - "genres": [ - "electronic", - "italo", - "electro", - "house" - ], - "description": "Cult Den Haag station for varied electronic styles.", - "streams": [ - { - "url": "http://radio.intergalactic.fm/1", - "format": "mp3", - "bitrate": 320, - "label": "Channel 1 — Cybernetic Broadcasting", - "priority": 0 - }, - { - "url": "http://radio.intergalactic.fm/2", - "format": "mp3", - "bitrate": 256, - "label": "Channel 2 — Disco Fetish", - "priority": 1 - }, - { - "url": "http://radio.intergalactic.fm/3", - "format": "mp3", - "bitrate": 256, - "label": "Channel 3 — The Dream Machine", - "priority": 2 - } - ], - "category": "electronic" + "slug": "intergalactic-fm", + "name": "Intergalactic FM", + "homepage": "https://intergalactic.fm/", + "country": "NL", + "genres": [ + "electronic", + "italo", + "electro", + "house" + ], + "description": "Cult Den Haag station for varied electronic styles.", + "streams": [ + { + "url": "http://radio.intergalactic.fm/1", + "format": "mp3", + "bitrate": 320, + "label": "Channel 1 — Cybernetic Broadcasting", + "priority": 0 + }, + { + "url": "http://radio.intergalactic.fm/2", + "format": "mp3", + "bitrate": 256, + "label": "Channel 2 — Disco Fetish", + "priority": 1 + }, + { + "url": "http://radio.intergalactic.fm/3", + "format": "mp3", + "bitrate": 256, + "label": "Channel 3 — The Dream Machine", + "priority": 2 + } + ], + "category": "electronic" }, { - "slug": "somafm-groove-salad", - "name": "SomaFM — Groove Salad", - "homepage": "https://somafm.com/groovesalad/", - "country": "US", - "genres": [ - "ambient", - "downtempo", - "chill" - ], - "description": "A nicely chilled plate of ambient/downtempo beats and grooves.", - "image_url": "https://somafm.com/img3/groovesalad-400.jpg", - "streams": [ - { - "url": "https://ice1.somafm.com/groovesalad-128-mp3", - "format": "mp3", - "bitrate": 128, - "label": "MP3 128", - "priority": 0 - }, - { - "url": "https://ice1.somafm.com/groovesalad-256-mp3", - "format": "mp3", - "bitrate": 256, - "label": "MP3 256", - "priority": 1 - } - ], - "category": "soma" + "slug": "somafm-groove-salad", + "name": "SomaFM — Groove Salad", + "homepage": "https://somafm.com/groovesalad/", + "country": "US", + "genres": [ + "ambient", + "downtempo", + "chill" + ], + "description": "A nicely chilled plate of ambient/downtempo beats and grooves.", + "image_url": "https://somafm.com/img3/groovesalad-400.jpg", + "streams": [ + { + "url": "https://ice1.somafm.com/groovesalad-128-mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + }, + { + "url": "https://ice1.somafm.com/groovesalad-256-mp3", + "format": "mp3", + "bitrate": 256, + "label": "MP3 256", + "priority": 1 + } + ], + "category": "soma" }, { - "slug": "somafm-drone-zone", - "name": "SomaFM — Drone Zone", - "homepage": "https://somafm.com/dronezone/", - "country": "US", - "genres": [ - "ambient", - "drone" - ], - "description": "Served best chilled, safe with most medications. Atmospheric textures with minimal beats.", - "streams": [ - { - "url": "https://ice1.somafm.com/dronezone-128-mp3", - "format": "mp3", - "bitrate": 128, - "label": "MP3 128", - "priority": 0 - }, - { - "url": "https://ice1.somafm.com/dronezone-256-mp3", - "format": "mp3", - "bitrate": 256, - "label": "MP3 256", - "priority": 1 - } - ], - "category": "soma" + "slug": "somafm-drone-zone", + "name": "SomaFM — Drone Zone", + "homepage": "https://somafm.com/dronezone/", + "country": "US", + "genres": [ + "ambient", + "drone" + ], + "description": "Served best chilled, safe with most medications. Atmospheric textures with minimal beats.", + "streams": [ + { + "url": "https://ice1.somafm.com/dronezone-128-mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + }, + { + "url": "https://ice1.somafm.com/dronezone-256-mp3", + "format": "mp3", + "bitrate": 256, + "label": "MP3 256", + "priority": 1 + } + ], + "category": "soma" }, { - "slug": "somafm-indie-pop-rocks", - "name": "SomaFM — Indie Pop Rocks", - "homepage": "https://somafm.com/indiepop/", - "country": "US", - "genres": [ - "indie", - "pop" - ], - "description": "New and classic favorite indie pop tracks.", - "streams": [ - { - "url": "https://ice1.somafm.com/indiepop-128-mp3", - "format": "mp3", - "bitrate": 128, - "label": "MP3 128", - "priority": 0 - } - ], - "category": "soma" + "slug": "somafm-indie-pop-rocks", + "name": "SomaFM — Indie Pop Rocks", + "homepage": "https://somafm.com/indiepop/", + "country": "US", + "genres": [ + "indie", + "pop" + ], + "description": "New and classic favorite indie pop tracks.", + "streams": [ + { + "url": "https://ice1.somafm.com/indiepop-128-mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ], + "category": "soma" }, { - "slug": "somafm-metal-detector", - "name": "SomaFM — Metal Detector", - "homepage": "https://somafm.com/metal/", - "country": "US", - "genres": [ - "metal", - "doom", - "black" - ], - "description": "From black to doom, prog to sludge, thrash to post, stoner to crossover.", - "streams": [ - { - "url": "https://ice1.somafm.com/metal-128-mp3", - "format": "mp3", - "bitrate": 128, - "label": "MP3 128", - "priority": 0 - } - ], - "category": "soma" + "slug": "somafm-metal-detector", + "name": "SomaFM — Metal Detector", + "homepage": "https://somafm.com/metal/", + "country": "US", + "genres": [ + "metal", + "doom", + "black" + ], + "description": "From black to doom, prog to sludge, thrash to post, stoner to crossover.", + "streams": [ + { + "url": "https://ice1.somafm.com/metal-128-mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ], + "category": "soma" }, { - "slug": "nts-1", - "name": "NTS Radio 1", - "homepage": "https://www.nts.live/", - "country": "GB", - "genres": [ - "eclectic", - "underground" - ], - "description": "Underground radio with diverse shows from London and globally — channel 1.", - "streams": [ - { - "url": "https://stream-relay-geo.ntslive.net/stream", - "format": "aac", - "bitrate": 128, - "label": "AAC 128", - "priority": 0 - } - ], - "category": "nts" + "slug": "nts-1", + "name": "NTS Radio 1", + "homepage": "https://www.nts.live/", + "country": "GB", + "genres": [ + "eclectic", + "underground" + ], + "description": "Underground radio with diverse shows from London and globally — channel 1.", + "streams": [ + { + "url": "https://stream-relay-geo.ntslive.net/stream", + "format": "aac", + "bitrate": 128, + "label": "AAC 128", + "priority": 0 + } + ], + "category": "nts" }, { - "slug": "nts-2", - "name": "NTS Radio 2", - "homepage": "https://www.nts.live/", - "country": "GB", - "genres": [ - "eclectic", - "underground" - ], - "description": "NTS — channel 2.", - "streams": [ - { - "url": "https://stream-relay-geo.ntslive.net/stream2", - "format": "aac", - "bitrate": 128, - "label": "AAC 128", - "priority": 0 - } - ], - "category": "nts" + "slug": "nts-2", + "name": "NTS Radio 2", + "homepage": "https://www.nts.live/", + "country": "GB", + "genres": [ + "eclectic", + "underground" + ], + "description": "NTS — channel 2.", + "streams": [ + { + "url": "https://stream-relay-geo.ntslive.net/stream2", + "format": "aac", + "bitrate": 128, + "label": "AAC 128", + "priority": 0 + } + ], + "category": "nts" }, { - "slug": "kexp", - "name": "KEXP 90.3 FM", - "homepage": "https://www.kexp.org/listen/", - "country": "US", - "genres": [ - "indie", - "rock", - "eclectic" - ], - "description": "Indie rock from Seattle. Features live in-studio sessions.", - "streams": [ - { - "url": "https://kexp.streamguys1.com/kexp160.aac", - "format": "aac", - "bitrate": 160, - "label": "AAC 160", - "priority": 0 - }, - { - "url": "https://kexp.streamguys1.com/kexp128.mp3", - "format": "mp3", - "bitrate": 128, - "label": "MP3 128", - "priority": 1 - } - ], - "category": "underground" + "slug": "kexp", + "name": "KEXP 90.3 FM", + "homepage": "https://www.kexp.org/listen/", + "country": "US", + "genres": [ + "indie", + "rock", + "eclectic" + ], + "description": "Indie rock from Seattle. Features live in-studio sessions.", + "streams": [ + { + "url": "https://kexp.streamguys1.com/kexp160.aac", + "format": "aac", + "bitrate": 160, + "label": "AAC 160", + "priority": 0 + }, + { + "url": "https://kexp.streamguys1.com/kexp128.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 1 + } + ], + "category": "underground" }, { - "slug": "radio-paradise-main", - "name": "Radio Paradise — Main Mix", - "homepage": "https://radioparadise.com/", - "country": "US", - "genres": [ - "eclectic", - "rock", - "world" - ], - "description": "DJ-mixed rock, world, and electronic. Ad-free.", - "streams": [ - { - "url": "https://stream.radioparadise.com/aac-320", - "format": "aac", - "bitrate": 320, - "label": "AAC 320", - "priority": 0 - }, - { - "url": "https://stream.radioparadise.com/mp3-192", - "format": "mp3", - "bitrate": 192, - "label": "MP3 192", - "priority": 1 - }, - { - "url": "https://stream.radioparadise.com/flac", - "format": "unknown", - "label": "FLAC", - "priority": 2 - } - ], - "category": "starter" + "slug": "radio-paradise-main", + "name": "Radio Paradise — Main Mix", + "homepage": "https://radioparadise.com/", + "country": "US", + "genres": [ + "eclectic", + "rock", + "world" + ], + "description": "DJ-mixed rock, world, and electronic. Ad-free.", + "streams": [ + { + "url": "https://stream.radioparadise.com/aac-320", + "format": "aac", + "bitrate": 320, + "label": "AAC 320", + "priority": 0 + }, + { + "url": "https://stream.radioparadise.com/mp3-192", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 1 + }, + { + "url": "https://stream.radioparadise.com/flac", + "format": "unknown", + "label": "FLAC", + "priority": 2 + } + ], + "category": "starter" }, { - "slug": "radio-paradise-mellow", - "name": "Radio Paradise — Mellow Mix", - "homepage": "https://radioparadise.com/", - "country": "US", - "genres": [ - "mellow", - "acoustic" - ], - "description": "Mellow acoustic mix.", - "streams": [ - { - "url": "https://stream.radioparadise.com/mellow-320", - "format": "aac", - "bitrate": 320, - "label": "AAC 320", - "priority": 0 - } - ], - "category": "starter" + "slug": "radio-paradise-mellow", + "name": "Radio Paradise — Mellow Mix", + "homepage": "https://radioparadise.com/", + "country": "US", + "genres": [ + "mellow", + "acoustic" + ], + "description": "Mellow acoustic mix.", + "streams": [ + { + "url": "https://stream.radioparadise.com/mellow-320", + "format": "aac", + "bitrate": 320, + "label": "AAC 320", + "priority": 0 + } + ], + "category": "starter" }, { - "slug": "kcrw-eclectic", - "name": "KCRW Eclectic 24", - "homepage": "https://www.kcrw.com/", - "country": "US", - "genres": [ - "eclectic", - "indie" - ], - "description": "Public radio music stream from Santa Monica.", - "streams": [ - { - "url": "https://streams.kcrw.com/e24_aac", - "format": "aac", - "bitrate": 256, - "label": "AAC 256", - "priority": 0 - }, - { - "url": "https://streams.kcrw.com/e24_mp3", - "format": "mp3", - "bitrate": 192, - "label": "MP3 192", - "priority": 1 - } - ], - "category": "underground" + "slug": "kcrw-eclectic", + "name": "KCRW Eclectic 24", + "homepage": "https://www.kcrw.com/", + "country": "US", + "genres": [ + "eclectic", + "indie" + ], + "description": "Public radio music stream from Santa Monica.", + "streams": [ + { + "url": "https://streams.kcrw.com/e24_aac", + "format": "aac", + "bitrate": 256, + "label": "AAC 256", + "priority": 0 + }, + { + "url": "https://streams.kcrw.com/e24_mp3", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 1 + } + ], + "category": "underground" }, { - "slug": "dublab", - "name": "dublab", - "homepage": "https://dublab.com/", - "country": "US", - "genres": [ - "experimental", - "underground" - ], - "description": "Non-profit community collective for experimental and underground music.", - "streams": [ - { - "url": "https://dublab.out.airtime.pro/dublab_a", - "format": "mp3", - "bitrate": 128, - "label": "MP3 128", - "priority": 0 - } - ], - "category": "underground" + "slug": "dublab", + "name": "dublab", + "homepage": "https://dublab.com/", + "country": "US", + "genres": [ + "experimental", + "underground" + ], + "description": "Non-profit community collective for experimental and underground music.", + "streams": [ + { + "url": "https://dublab.out.airtime.pro/dublab_a", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ], + "category": "underground" }, { - "slug": "wfmu", - "name": "WFMU", - "homepage": "https://wfmu.org/", - "country": "US", - "genres": [ - "freeform", - "eclectic" - ], - "description": "Freeform radio from New Jersey; independent and non-commercial.", - "streams": [ - { - "url": "https://wfmu.org/wfmu.pls", - "format": "pls", - "label": "Main (PLS)", - "priority": 0 - } - ], - "category": "underground" + "slug": "wfmu", + "name": "WFMU", + "homepage": "https://wfmu.org/", + "country": "US", + "genres": [ + "freeform", + "eclectic" + ], + "description": "Freeform radio from New Jersey; independent and non-commercial.", + "streams": [ + { + "url": "https://wfmu.org/wfmu.pls", + "format": "pls", + "label": "Main (PLS)", + "priority": 0 + } + ], + "category": "underground" }, { - "slug": "bbc-radio-6-music", - "name": "BBC Radio 6 Music", - "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_6music", - "country": "GB", - "genres": [ - "alternative", - "eclectic" - ], - "description": "Alternative music, new releases, and deep cuts.", - "streams": [ - { - "url": "http://as-hls-ww-live.akamaized.net/pool_81827798/live/ww/bbc_6music/bbc_6music.isml/bbc_6music-audio%3d320000.norewind.m3u8", - "format": "hls", - "bitrate": 320, - "label": "HLS 320", - "priority": 0 - }, - { - "url": "http://as-hls-ww-live.akamaized.net/pool_81827798/live/ww/bbc_6music/bbc_6music.isml/bbc_6music-audio%3d128000.norewind.m3u8", - "format": "hls", - "bitrate": 128, - "label": "HLS 128", - "priority": 1 - } - ], - "category": "bbc" + "slug": "bbc-radio-6-music", + "name": "BBC Radio 6 Music", + "homepage": "https://www.bbc.co.uk/sounds/play/live:bbc_6music", + "country": "GB", + "genres": [ + "alternative", + "eclectic" + ], + "description": "Alternative music, new releases, and deep cuts.", + "streams": [ + { + "url": "http://as-hls-ww-live.akamaized.net/pool_81827798/live/ww/bbc_6music/bbc_6music.isml/bbc_6music-audio%3d320000.norewind.m3u8", + "format": "hls", + "bitrate": 320, + "label": "HLS 320", + "priority": 0 + }, + { + "url": "http://as-hls-ww-live.akamaized.net/pool_81827798/live/ww/bbc_6music/bbc_6music.isml/bbc_6music-audio%3d128000.norewind.m3u8", + "format": "hls", + "bitrate": 128, + "label": "HLS 128", + "priority": 1 + } + ], + "category": "bbc" }, { - "slug": "worldwide-fm", - "name": "Worldwide FM", - "homepage": "https://worldwidefm.net/", - "country": "GB", - "genres": [ - "eclectic", - "jazz", - "world" - ], - "description": "Platform for underground music and culture.", - "streams": [ - { - "url": "https://worldwide-fm.radiocult.fm/stream", - "format": "mp3", - "bitrate": 320, - "label": "MP3 320", - "priority": 0 - }, - { - "url": "https://worldwidefm.out.airtime.pro/worldwidefm_b", - "format": "mp3", - "bitrate": 192, - "label": "MP3 192", - "priority": 1 - }, - { - "url": "http://worldwidefm.out.airtime.pro:8000/worldwidefm_a", - "format": "mp3", - "bitrate": 128, - "label": "MP3 128", - "priority": 2 - } - ], - "category": "underground" + "slug": "worldwide-fm", + "name": "Worldwide FM", + "homepage": "https://worldwidefm.net/", + "country": "GB", + "genres": [ + "eclectic", + "jazz", + "world" + ], + "description": "Platform for underground music and culture.", + "streams": [ + { + "url": "https://worldwide-fm.radiocult.fm/stream", + "format": "mp3", + "bitrate": 320, + "label": "MP3 320", + "priority": 0 + }, + { + "url": "https://worldwidefm.out.airtime.pro/worldwidefm_b", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 1 + }, + { + "url": "http://worldwidefm.out.airtime.pro:8000/worldwidefm_a", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 2 + } + ], + "category": "underground" }, { - "slug": "nightwave-plaza", - "name": "Nightwave Plaza", - "homepage": "https://plaza.one/", - "country": "US", - "genres": [ - "vaporwave", - "future-funk", - "city-pop" - ], - "description": "24/7 vaporwave, future funk, and city pop.", - "streams": [ - { - "url": "https://radio.plaza.one/mp3", - "format": "mp3", - "bitrate": 192, - "label": "MP3 192", - "priority": 0 - }, - { - "url": "https://radio.plaza.one/ogg", - "format": "ogg", - "bitrate": 192, - "label": "OGG 192", - "priority": 1 - } - ], - "category": "electronic" + "slug": "nightwave-plaza", + "name": "Nightwave Plaza", + "homepage": "https://plaza.one/", + "country": "US", + "genres": [ + "vaporwave", + "future-funk", + "city-pop" + ], + "description": "24/7 vaporwave, future funk, and city pop.", + "streams": [ + { + "url": "https://radio.plaza.one/mp3", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + }, + { + "url": "https://radio.plaza.one/ogg", + "format": "ogg", + "bitrate": 192, + "label": "OGG 192", + "priority": 1 + } + ], + "category": "electronic" }, { - "slug": "wwoz", - "name": "WWOZ 90.7 New Orleans", - "homepage": "https://www.wwoz.org/", - "country": "US", - "genres": [ - "jazz", - "funk", - "soul", - "blues" - ], - "description": "New Orleans jazz, funk, and soul.", - "streams": [ - { - "url": "https://wwoz-sc.streamguys1.com/wwoz-hi.mp3", - "format": "mp3", - "bitrate": 128, - "label": "MP3 128", - "priority": 0 - } - ], - "category": "jazz" + "slug": "wwoz", + "name": "WWOZ 90.7 New Orleans", + "homepage": "https://www.wwoz.org/", + "country": "US", + "genres": [ + "jazz", + "funk", + "soul", + "blues" + ], + "description": "New Orleans jazz, funk, and soul.", + "streams": [ + { + "url": "https://wwoz-sc.streamguys1.com/wwoz-hi.mp3", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ], + "category": "jazz" }, { - "slug": "kiosk-radio", - "name": "Kiosk Radio Brussels", - "homepage": "https://kioskradio.com/", - "country": "BE", - "genres": [ - "electronic", - "jazz", - "eclectic" - ], - "description": "Electronic and jazz from a park wooden shack in Brussels.", - "streams": [ - { - "url": "https://kioskradiobxl.out.airtime.pro/kioskradiobxl_a", - "format": "mp3", - "bitrate": 192, - "label": "MP3 192", - "priority": 0 - } - ], - "category": "underground" + "slug": "kiosk-radio", + "name": "Kiosk Radio Brussels", + "homepage": "https://kioskradio.com/", + "country": "BE", + "genres": [ + "electronic", + "jazz", + "eclectic" + ], + "description": "Electronic and jazz from a park wooden shack in Brussels.", + "streams": [ + { + "url": "https://kioskradiobxl.out.airtime.pro/kioskradiobxl_a", + "format": "mp3", + "bitrate": 192, + "label": "MP3 192", + "priority": 0 + } + ], + "category": "underground" }, { - "slug": "le-mellotron", - "name": "Le Mellotron", - "homepage": "https://www.lemellotron.com/", - "country": "FR", - "genres": [ - "eclectic", - "electronic", - "soul" - ], - "description": "Parisian station with eclectic tastes.", - "streams": [ - { - "url": "https://listen.radioking.com/radio/477719/stream/534044", - "format": "mp3", - "bitrate": 128, - "label": "MP3 128", - "priority": 0 - } - ], - "category": "underground" + "slug": "le-mellotron", + "name": "Le Mellotron", + "homepage": "https://www.lemellotron.com/", + "country": "FR", + "genres": [ + "eclectic", + "electronic", + "soul" + ], + "description": "Parisian station with eclectic tastes.", + "streams": [ + { + "url": "https://listen.radioking.com/radio/477719/stream/534044", + "format": "mp3", + "bitrate": 128, + "label": "MP3 128", + "priority": 0 + } + ], + "category": "underground" }, { - "slug": "the-lot-radio", - "name": "The Lot Radio", - "homepage": "https://www.thelotradio.com/", - "country": "US", - "genres": [ - "electronic", - "underground" - ], - "description": "Independent hub streaming from a Brooklyn shipping container.", - "streams": [ - { - "url": "https://livepeercdn.studio/hls/85c28sa2o8wppm58/index.m3u8", - "format": "hls", - "label": "HLS", - "priority": 0 - } - ], - "category": "underground" + "slug": "the-lot-radio", + "name": "The Lot Radio", + "homepage": "https://www.thelotradio.com/", + "country": "US", + "genres": [ + "electronic", + "underground" + ], + "description": "Independent hub streaming from a Brooklyn shipping container.", + "streams": [ + { + "url": "https://livepeercdn.studio/hls/85c28sa2o8wppm58/index.m3u8", + "format": "hls", + "label": "HLS", + "priority": 0 + } + ], + "category": "underground" } -] +] \ No newline at end of file diff --git a/package.json b/package.json index 896d902..0f547ae 100644 --- a/package.json +++ b/package.json @@ -1,29 +1,29 @@ { - "name": "online-radio-explorer", - "version": "0.1.0", - "private": true, - "type": "module", - "description": "Touchscreen kiosk + admin for exploring and playing internet radio.", - "scripts": { - "dev": "concurrently -k -n web,api -c blue,green \"npm:dev:web\" \"npm:dev:api\"", - "dev:web": "vite", - "dev:api": "node --watch server/index.js", - "build": "vite build", - "start": "node server/index.js", - "seed": "node server/scripts/seed.js" - }, - "dependencies": { - "bcryptjs": "^2.4.3", - "better-sqlite3": "^11.3.0", - "cookie": "^1.0.1", - "dotenv": "^16.4.5", - "express": "^4.21.0", - "node-cron": "^3.0.3", - "ws": "^8.18.0" - }, - "devDependencies": { - "concurrently": "^9.0.1", - "hls.js": "^1.5.17", - "vite": "^5.4.8" - } -} + "name": "online-radio-explorer", + "version": "0.1.0", + "private": true, + "type": "module", + "description": "Touchscreen kiosk + admin for exploring and playing internet radio.", + "scripts": { + "dev": "concurrently -k -n web,api -c blue,green \"npm:dev:web\" \"npm:dev:api\"", + "dev:web": "vite", + "dev:api": "node --watch server/index.js", + "build": "vite build", + "start": "node server/index.js", + "seed": "node server/scripts/seed.js" + }, + "dependencies": { + "bcryptjs": "^2.4.3", + "better-sqlite3": "^11.3.0", + "cookie": "^1.0.1", + "dotenv": "^16.4.5", + "express": "^4.21.0", + "node-cron": "^3.0.3", + "ws": "^8.18.0" + }, + "devDependencies": { + "concurrently": "^9.0.1", + "hls.js": "^1.5.17", + "vite": "^5.4.8" + } +} \ No newline at end of file diff --git a/server/auth.js b/server/auth.js index 84edb92..480b008 100644 --- a/server/auth.js +++ b/server/auth.js @@ -6,27 +6,27 @@ const SESSION_DAYS = 30; const COOKIE_NAME = 'oradio_sid'; export function hashPassword(plain) { - return bcrypt.hashSync(plain, 10); + return bcrypt.hashSync(plain, 10); } export function verifyPassword(plain, hash) { - return bcrypt.compareSync(plain, hash); + return bcrypt.compareSync(plain, hash); } export function createSession(userId) { - const token = randomBytes(32).toString('hex'); - const expires = new Date(Date.now() + SESSION_DAYS * 86400e3).toISOString(); - getDb().prepare('INSERT INTO sessions (token, user_id, expires_at) VALUES (?, ?, ?)') - .run(token, userId, expires); - return { token, expires }; + const token = randomBytes(32).toString('hex'); + const expires = new Date(Date.now() + SESSION_DAYS * 86400e3).toISOString(); + getDb().prepare('INSERT INTO sessions (token, user_id, expires_at) VALUES (?, ?, ?)') + .run(token, userId, expires); + return { token, expires }; } export function destroySession(token) { - if (token) getDb().prepare('DELETE FROM sessions WHERE token = ?').run(token); + if (token) getDb().prepare('DELETE FROM sessions WHERE token = ?').run(token); } export function getUserBySession(token) { - if (!token) return null; - return getDb().prepare(` + if (!token) return null; + return getDb().prepare(` SELECT u.id, u.username, u.role FROM sessions s JOIN users u ON u.id = s.user_id WHERE s.token = ? AND s.expires_at > datetime('now') @@ -34,55 +34,55 @@ export function getUserBySession(token) { } export function readSessionToken(req) { - const raw = req.headers.cookie || ''; - for (const part of raw.split(';')) { - const [k, v] = part.trim().split('='); - if (k === COOKIE_NAME) return decodeURIComponent(v || ''); - } - return null; + const raw = req.headers.cookie || ''; + for (const part of raw.split(';')) { + const [k, v] = part.trim().split('='); + if (k === COOKIE_NAME) return decodeURIComponent(v || ''); + } + return null; } export function setSessionCookie(res, token, expires) { - const attrs = [ - `${COOKIE_NAME}=${encodeURIComponent(token)}`, - 'Path=/', - 'HttpOnly', - 'SameSite=Lax', - `Expires=${new Date(expires).toUTCString()}` - ]; - res.setHeader('Set-Cookie', attrs.join('; ')); + const attrs = [ + `${COOKIE_NAME}=${encodeURIComponent(token)}`, + 'Path=/', + 'HttpOnly', + 'SameSite=Lax', + `Expires=${new Date(expires).toUTCString()}` + ]; + res.setHeader('Set-Cookie', attrs.join('; ')); } export function clearSessionCookie(res) { - res.setHeader('Set-Cookie', `${COOKIE_NAME}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`); + res.setHeader('Set-Cookie', `${COOKIE_NAME}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`); } export function authMiddleware(req, _res, next) { - const token = readSessionToken(req); - req.session = { token }; - req.user = getUserBySession(token); - next(); + const token = readSessionToken(req); + req.session = { token }; + req.user = getUserBySession(token); + next(); } export function requireUser(req, res, next) { - if (!req.user) return res.status(401).json({ error: 'auth required' }); - next(); + if (!req.user) return res.status(401).json({ error: 'auth required' }); + next(); } export function requireAdmin(req, res, next) { - if (!req.user) return res.status(401).json({ error: 'auth required' }); - if (req.user.role !== 'admin') return res.status(403).json({ error: 'admin only' }); - next(); + if (!req.user) return res.status(401).json({ error: 'auth required' }); + if (req.user.role !== 'admin') return res.status(403).json({ error: 'admin only' }); + next(); } export function ensureBootstrapAdmin({ username, password }) { - if (!username || !password) return; - const db = getDb(); - const existing = db.prepare('SELECT id FROM users WHERE username = ?').get(username); - if (existing) return; - const info = db.prepare('INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)') - .run(username, hashPassword(password), 'admin'); - db.prepare('INSERT INTO profiles (user_id, display_name) VALUES (?, ?)') - .run(info.lastInsertRowid, username); - console.log(`[auth] bootstrap admin '${username}' created`); + if (!username || !password) return; + const db = getDb(); + const existing = db.prepare('SELECT id FROM users WHERE username = ?').get(username); + if (existing) return; + const info = db.prepare('INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)') + .run(username, hashPassword(password), 'admin'); + db.prepare('INSERT INTO profiles (user_id, display_name) VALUES (?, ?)') + .run(info.lastInsertRowid, username); + console.log(`[auth] bootstrap admin '${username}' created`); } diff --git a/server/db/index.js b/server/db/index.js index 1a4a472..412c6e6 100644 --- a/server/db/index.js +++ b/server/db/index.js @@ -9,51 +9,51 @@ const __dirname = dirname(fileURLToPath(import.meta.url)); let db; export function initDb(dbPath) { - const abs = resolve(dbPath); - mkdirSync(dirname(abs), { recursive: true }); - db = new Database(abs); - db.pragma('journal_mode = WAL'); - db.pragma('foreign_keys = ON'); - const schema = readFileSync(resolve(__dirname, 'schema.sql'), 'utf8'); - db.exec(schema); - runMigrations(db); - return db; + const abs = resolve(dbPath); + mkdirSync(dirname(abs), { recursive: true }); + db = new Database(abs); + db.pragma('journal_mode = WAL'); + db.pragma('foreign_keys = ON'); + const schema = readFileSync(resolve(__dirname, 'schema.sql'), 'utf8'); + db.exec(schema); + runMigrations(db); + return db; } export function getDb() { - if (!db) throw new Error('DB not initialized'); - return db; + if (!db) throw new Error('DB not initialized'); + return db; } // Idempotent migrations for upgrading older DBs that pre-date a column. function runMigrations(db) { - const stationCols = new Set(db.prepare("PRAGMA table_info(stations)").all().map((c) => c.name)); - if (!stationCols.has('uuid')) { - db.exec('ALTER TABLE stations ADD COLUMN uuid TEXT'); - } - if (!stationCols.has('category')) { - db.exec('ALTER TABLE stations ADD COLUMN category TEXT'); - } - const streamCols = new Set(db.prepare("PRAGMA table_info(streams)").all().map((c) => c.name)); - if (!streamCols.has('uuid')) { - db.exec('ALTER TABLE streams ADD COLUMN uuid TEXT'); - } + const stationCols = new Set(db.prepare("PRAGMA table_info(stations)").all().map((c) => c.name)); + if (!stationCols.has('uuid')) { + db.exec('ALTER TABLE stations ADD COLUMN uuid TEXT'); + } + if (!stationCols.has('category')) { + db.exec('ALTER TABLE stations ADD COLUMN category TEXT'); + } + const streamCols = new Set(db.prepare("PRAGMA table_info(streams)").all().map((c) => c.name)); + if (!streamCols.has('uuid')) { + db.exec('ALTER TABLE streams ADD COLUMN uuid TEXT'); + } - // Backfill UUIDs. For RB stations, prefer the existing source_ref so the - // public UUID matches the upstream Radio-Browser stationuuid. - const setStationUuid = db.prepare('UPDATE stations SET uuid = ? WHERE id = ?'); - for (const row of db.prepare("SELECT id, source, source_ref FROM stations WHERE uuid IS NULL OR uuid = ''").all()) { - const u = (row.source === 'radiobrowser' && row.source_ref) ? row.source_ref : randomUUID(); - setStationUuid.run(u, row.id); - } + // Backfill UUIDs. For RB stations, prefer the existing source_ref so the + // public UUID matches the upstream Radio-Browser stationuuid. + const setStationUuid = db.prepare('UPDATE stations SET uuid = ? WHERE id = ?'); + for (const row of db.prepare("SELECT id, source, source_ref FROM stations WHERE uuid IS NULL OR uuid = ''").all()) { + const u = (row.source === 'radiobrowser' && row.source_ref) ? row.source_ref : randomUUID(); + setStationUuid.run(u, row.id); + } - const setStreamUuid = db.prepare('UPDATE streams SET uuid = ? WHERE id = ?'); - for (const row of db.prepare("SELECT id FROM streams WHERE uuid IS NULL OR uuid = ''").all()) { - setStreamUuid.run(randomUUID(), row.id); - } + const setStreamUuid = db.prepare('UPDATE streams SET uuid = ? WHERE id = ?'); + for (const row of db.prepare("SELECT id FROM streams WHERE uuid IS NULL OR uuid = ''").all()) { + setStreamUuid.run(randomUUID(), row.id); + } - db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_stations_uuid ON stations(uuid)'); - db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_streams_uuid ON streams(uuid)'); - db.exec('CREATE INDEX IF NOT EXISTS idx_stations_category ON stations(category)'); + db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_stations_uuid ON stations(uuid)'); + db.exec('CREATE UNIQUE INDEX IF NOT EXISTS idx_streams_uuid ON streams(uuid)'); + db.exec('CREATE INDEX IF NOT EXISTS idx_stations_category ON stations(category)'); } diff --git a/server/db/schema.sql b/server/db/schema.sql index f0790a0..b9aa38a 100644 --- a/server/db/schema.sql +++ b/server/db/schema.sql @@ -78,3 +78,23 @@ CREATE TABLE IF NOT EXISTS play_history ( ); CREATE INDEX IF NOT EXISTS idx_history_user ON play_history(user_id, started_at DESC); +CREATE INDEX IF NOT EXISTS idx_history_station ON play_history(station_id); + +-- One vote per user per station. value is +1 (up) or -1 (down). Row is +-- deleted entirely when the user clears their vote, so the COUNT is exact. +CREATE TABLE IF NOT EXISTS station_votes ( + user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE, + station_id INTEGER NOT NULL REFERENCES stations(id) ON DELETE CASCADE, + value INTEGER NOT NULL CHECK (value IN (-1, 1)), + created_at TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (user_id, station_id) +); +CREATE INDEX IF NOT EXISTS idx_votes_station ON station_votes(station_id); + +-- Aggregate play counter. Cheaper than COUNT(*) over play_history every render +-- and lets anonymous/public listing show play counts without exposing history. +CREATE TABLE IF NOT EXISTS station_plays ( + station_id INTEGER PRIMARY KEY REFERENCES stations(id) ON DELETE CASCADE, + plays INTEGER NOT NULL DEFAULT 0, + last_played_at TEXT +); diff --git a/server/index.js b/server/index.js index 4a9d82d..15ac92d 100644 --- a/server/index.js +++ b/server/index.js @@ -22,8 +22,8 @@ const PORT = Number(process.env.PORT) || 4173; initDb(process.env.DB_PATH || './data/db/oradio.sqlite'); ensureBootstrapAdmin({ - username: process.env.ADMIN_BOOTSTRAP_USER, - password: process.env.ADMIN_BOOTSTRAP_PASSWORD + username: process.env.ADMIN_BOOTSTRAP_USER, + password: process.env.ADMIN_BOOTSTRAP_PASSWORD }); const seedResult = applySeedIfEmpty(); console.log('[seed]', seedResult); @@ -32,23 +32,24 @@ const app = express(); app.use(express.json({ limit: '512kb' })); app.use(authMiddleware); -app.use('/api/auth', authRoutes); +app.use('/api/auth', authRoutes); app.use('/api/stations', stationRoutes); -app.use('/api/me', meRoutes); -app.use('/api/admin', adminRoutes); -app.use('/api/v1', v1Routes); +app.use('/api/me', meRoutes); +app.use('/api/admin', adminRoutes); +app.use('/api/v1', v1Routes); // Static assets (built by Vite). In dev these don't exist; Vite serves them on :5173. const publicDir = resolve(__dirname, 'public'); if (existsSync(publicDir)) { - app.use(express.static(publicDir)); - app.get('/admin', (_req, res) => res.sendFile(resolve(publicDir, 'admin/index.html'))); - app.get('*', (_req, res) => res.sendFile(resolve(publicDir, 'index.html'))); + app.use(express.static(publicDir)); + app.get('/admin', (_req, res) => res.sendFile(resolve(publicDir, 'admin/index.html'))); + app.get('/docs', (_req, res) => res.sendFile(resolve(publicDir, 'docs/index.html'))); + app.get('*', (_req, res) => res.sendFile(resolve(publicDir, 'index.html'))); } app.use((err, _req, res, _next) => { - console.error(err); - res.status(500).json({ error: String(err.message || err) }); + console.error(err); + res.status(500).json({ error: String(err.message || err) }); }); const server = createServer(app); @@ -56,5 +57,5 @@ attachWs(server); scheduleHealthCheck(process.env.STREAM_CHECK_CRON); server.listen(PORT, () => { - console.log(`[oradio] api+ws on http://localhost:${PORT}`); + console.log(`[oradio] api+ws on http://localhost:${PORT}`); }); diff --git a/server/public/admin/index.html b/server/public/admin/index.html index 1f20493..650890e 100644 --- a/server/public/admin/index.html +++ b/server/public/admin/index.html @@ -1,14 +1,18 @@ -
+ +