From 00246389bc54d6efc58e81c09fc7b379b24ed0aa Mon Sep 17 00:00:00 2001 From: Marco Mooren Date: Mon, 11 May 2026 02:06:48 +0200 Subject: [PATCH] Add API documentation and underground station importer - Introduced a new HTML documentation page for the oradio API, including a JavaScript file to handle dynamic content and API requests. - Added a CSS file for styling the documentation page. - Implemented an underground station importer script that fetches data from Radio-Browser and writes it to a JSON file. - Created a stats module to compute and manage vote and play statistics for radio stations. - Added a polyfill for modulepreload to ensure compatibility with older browsers. --- data/seed/categories.json | 107 +- data/seed/stations-allradio-nl.json | 2 +- data/seed/stations-extended.json | 2332 +++++++++++++---- data/seed/stations-underground.json | 1206 +++++++++ data/seed/stations.json | 1110 ++++---- package.json | 56 +- server/auth.js | 86 +- server/db/index.js | 72 +- server/db/schema.sql | 20 + server/index.js | 25 +- server/public/admin/index.html | 20 +- server/public/assets/admin-BRU0y9A4.js | 1 + server/public/assets/admin-CVu6KAFb.js | 1 - server/public/assets/docs-CJfnRuXm.js | 5 + server/public/assets/docs-z3ZiwvpP.css | 1 + server/public/assets/dom-BZgKDOeX.js | 1 - server/public/assets/dom-BvorgAdo.js | 1 + .../{kiosk-DBnbAN5w.js => kiosk-C37Mmo8O.js} | 38 +- server/public/assets/kiosk-CL6_kPws.css | 1 - server/public/assets/kiosk-CdZttV5P.css | 1 + .../assets/modulepreload-polyfill-B5Qt9EMX.js | 1 + server/public/docs/index.html | 24 + server/public/index.html | 20 +- server/routes/admin.js | 88 +- server/routes/auth.js | 86 +- server/routes/me.js | 61 +- server/routes/stations.js | 228 +- server/routes/v1.js | 269 +- server/scripts/import-allradio-nl.js | 312 +-- server/scripts/import-underground.js | 189 ++ server/scripts/report-streams.js | 4 +- server/sources/iconScraper.js | 194 +- server/sources/radiobrowser.js | 86 +- server/sources/seed.js | 156 +- server/stations.js | 176 +- server/stats.js | 125 + server/streams/checker.js | 28 +- server/streams/probe.js | 96 +- server/streams/resolver.js | 50 +- server/ws.js | 74 +- vite.config.js | 37 +- web/admin/index.html | 13 +- web/admin/main.js | 510 ++-- web/docs/index.html | 23 + web/docs/main.js | 241 ++ web/docs/style.css | 171 ++ web/index.html | 13 +- web/main.js | 185 +- web/shared/api.js | 32 +- web/shared/dom.js | 26 +- web/shared/ws.js | 40 +- web/style.css | 111 + 52 files changed, 6280 insertions(+), 2475 deletions(-) create mode 100644 data/seed/stations-underground.json create mode 100644 server/public/assets/admin-BRU0y9A4.js delete mode 100644 server/public/assets/admin-CVu6KAFb.js create mode 100644 server/public/assets/docs-CJfnRuXm.js create mode 100644 server/public/assets/docs-z3ZiwvpP.css delete mode 100644 server/public/assets/dom-BZgKDOeX.js create mode 100644 server/public/assets/dom-BvorgAdo.js rename server/public/assets/{kiosk-DBnbAN5w.js => kiosk-C37Mmo8O.js} (64%) delete mode 100644 server/public/assets/kiosk-CL6_kPws.css create mode 100644 server/public/assets/kiosk-CdZttV5P.css create mode 100644 server/public/assets/modulepreload-polyfill-B5Qt9EMX.js create mode 100644 server/public/docs/index.html create mode 100644 server/scripts/import-underground.js create mode 100644 server/stats.js create mode 100644 web/docs/index.html create mode 100644 web/docs/main.js create mode 100644 web/docs/style.css 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 @@ - + + Radio Admin - - - - - + + + + + + +
- - + + + \ No newline at end of file diff --git a/server/public/assets/admin-BRU0y9A4.js b/server/public/assets/admin-BRU0y9A4.js new file mode 100644 index 0000000..c1be104 --- /dev/null +++ b/server/public/assets/admin-BRU0y9A4.js @@ -0,0 +1 @@ +import"./modulepreload-polyfill-B5Qt9EMX.js";import{a as i,c as g,e}from"./dom-BvorgAdo.js";const b=document.getElementById("app"),n={user:null,view:"stations",stations:[],users:[],system:null,search:""};async function v(){try{n.user=await i.get("/api/auth/me")}catch{return C()}if(n.user.role!=="admin"){b.innerHTML=`

Admin only

Signed in as ${n.user.username} (${n.user.role}).

`;return}await u(),m()}async function u(){const a=[i.get("/api/stations?all=1")];n.view==="users"&&a.push(i.get("/api/auth/users")),n.view==="system"&&a.push(i.get("/api/admin/system"));const[t,s,p]=await Promise.all(a);n.stations=t,n.view==="users"&&(n.users=s||[]),n.view==="system"&&(n.system=s||p||null)}function C(){g(b),b.appendChild(e("div",{class:"login"},e("form",{onSubmit:async a=>{a.preventDefault();const t=new FormData(a.target);try{n.user=await i.post("/api/auth/login",{username:t.get("username"),password:t.get("password")}),await v()}catch(s){a.target.querySelector(".err").textContent=s.message}}},e("h1",{},"Admin sign in"),e("input",{name:"username",placeholder:"Username",required:!0}),e("input",{name:"password",type:"password",placeholder:"Password",required:!0}),e("div",{class:"err"}),e("button",{class:"btn primary",type:"submit"},"Sign in"))))}function m(){g(b);const a=e("aside",{class:"side"},e("h1",{},"Online Radio Explorer"),...["stations","import","users","system"].map(s=>e("button",{class:`nav ${n.view===s?"active":""}`,onClick:async()=>{n.view=s,await u(),m()}},k(s))),e("div",{class:"me"},`Signed in as ${n.user.username}`,e("br"),e("a",{href:"#",onClick:async s=>{s.preventDefault(),await i.post("/api/auth/logout"),location.reload()}},"Sign out"))),t=e("main",{class:"main"});n.view==="stations"?S(t):n.view==="import"?I(t):n.view==="users"?$(t):n.view==="system"&&E(t),b.appendChild(e("div",{class:"shell"},a,t))}function k(a){return{stations:"Stations",import:"Import",users:"Users",system:"System"}[a]}function S(a){a.appendChild(e("div",{class:"bar"},e("input",{placeholder:"Search…",value:n.search,onInput:s=>{n.search=s.target.value,w()}}),e("button",{class:"btn primary",onClick:()=>f()},"+ Add station"),e("button",{class:"btn",onClick:async()=>{await i.post("/api/admin/health-check"),alert("Health check finished"),await u(),m()}},"Run health check")));const t=e("div",{id:"tableWrap"});a.appendChild(t),w()}function w(){const a=document.getElementById("tableWrap");if(!a)return;g(a);const t=n.search.toLowerCase(),s=n.stations.filter(o=>!t||o.name.toLowerCase().includes(t)||(o.country||"").toLowerCase().includes(t)||(o.genres||[]).some(c=>c.toLowerCase().includes(t))),p=e("table",{},e("thead",{},e("tr",{},e("th",{},"Name"),e("th",{},"Source"),e("th",{},"Genres"),e("th",{},"Country"),e("th",{},"Enabled"),e("th",{},"Actions"))),e("tbody",{},...s.map(o=>e("tr",{},e("td",{},e("strong",{},o.name),e("br"),e("small",{},o.homepage||"")),e("td",{},o.source),e("td",{},...(o.genres||[]).slice(0,4).map(c=>e("span",{class:"tag"},c))),e("td",{},o.country||""),e("td",{},o.enabled?"βœ…":"β›”"),e("td",{},e("button",{class:"btn",onClick:()=>f(o.id)},"Edit")," ",e("button",{class:"btn danger",onClick:async()=>{confirm(`Delete ${o.name}?`)&&(await i.del(`/api/stations/${o.id}`),await u(),m())}},"Delete"))))));a.appendChild(p)}async function f(a){const t=a?await i.get(`/api/stations/${a}`):{name:"",genres:[],streams:[],enabled:!0},s=e("dialog"),p=e("div",{class:"streams"});function o(){var l;g(p),p.appendChild(e("div",{style:{fontWeight:600,marginBottom:"6px"}},"Streams")),(l=t.streams)!=null&&l.length||p.appendChild(e("div",{style:{color:"#6b7280"}},"No streams yet."));for(const r of t.streams||[])p.appendChild(e("div",{class:"stream-row"},e("select",{onChange:d=>r.format=d.target.value},...["mp3","aac","hls","m3u","pls","ogg","unknown"].map(d=>e("option",{value:d,selected:r.format===d},d))),e("input",{value:r.url,placeholder:"https://…",onInput:d=>r.url=d.target.value}),e("input",{type:"number",placeholder:"kbps",value:r.bitrate||"",onInput:d=>r.bitrate=Number(d.target.value)||null}),e("input",{value:r.label||"",placeholder:"Label",onInput:d=>r.label=d.target.value}),r.last_status?e("span",{class:`pill ${r.last_status==="up"?"up":"down"}`},r.last_status):e("span"),e("button",{class:"btn danger",type:"button",onClick:()=>{t.streams=t.streams.filter(d=>d!==r),o()}},"Γ—")));p.appendChild(e("button",{class:"btn",type:"button",onClick:()=>{var r;t.streams=[...t.streams||[],{url:"",format:"mp3",priority:((r=t.streams)==null?void 0:r.length)||0}],o()}},"+ Add stream"))}const c=e("form",{method:"dialog",onSubmit:async l=>{l.preventDefault();const r={name:t.name,homepage:t.homepage,country:t.country,genres:t.genres,description:t.description,image_url:t.image_url,enabled:t.enabled};if(a){await i.patch(`/api/stations/${a}`,r);const d=await i.get(`/api/stations/${a}`);for(const y of d.streams||[])await i.del(`/api/stations/${a}/streams/${y.id}`);for(const y of t.streams||[])y.url&&await i.post(`/api/stations/${a}/streams`,y)}else r.streams=(t.streams||[]).filter(d=>d.url),await i.post("/api/stations",r);s.close(),await u(),m()}},e("h2",{},a?"Edit station":"Add station"),e("div",{class:"row"},e("label",{},"Name"),e("input",{value:t.name,onInput:l=>t.name=l.target.value,required:!0})),e("div",{class:"row"},e("label",{},"Homepage"),e("input",{value:t.homepage||"",onInput:l=>t.homepage=l.target.value})),e("div",{class:"row"},e("label",{},"Country"),e("input",{value:t.country||"",maxlength:4,onInput:l=>t.country=l.target.value})),e("div",{class:"row"},e("label",{},"Genres"),e("input",{value:(t.genres||[]).join(", "),onInput:l=>t.genres=l.target.value.split(",").map(r=>r.trim()).filter(Boolean)})),e("div",{class:"row"},e("label",{},"Image URL"),e("input",{value:t.image_url||"",onInput:l=>t.image_url=l.target.value})),e("div",{class:"row col"},e("textarea",{rows:2,placeholder:"Description",onInput:l=>t.description=l.target.value},t.description||"")),e("div",{class:"row"},e("label",{},"Enabled"),e("input",{type:"checkbox",checked:t.enabled,onChange:l=>t.enabled=l.target.checked})),p,e("div",{class:"actions"},e("button",{class:"btn",type:"button",onClick:()=>s.close()},"Cancel"),e("button",{class:"btn primary",type:"submit"},"Save")));o(),s.appendChild(c),document.body.appendChild(s),s.showModal(),s.addEventListener("close",()=>s.remove())}function I(a){let t=[];const s=e("div");a.appendChild(e("h2",{},"Import from Radio-Browser")),a.appendChild(e("div",{class:"bar"},e("input",{id:"rbq",placeholder:"Search by name…"}),e("input",{id:"rbcountry",placeholder:"Country (e.g. NL)",style:{minWidth:"120px"}}),e("input",{id:"rbtag",placeholder:"Tag/genre"}),e("button",{class:"btn primary",onClick:async()=>{const o=new URLSearchParams({q:document.getElementById("rbq").value,country:document.getElementById("rbcountry").value,tag:document.getElementById("rbtag").value});t=await i.get(`/api/stations/sources/radiobrowser/search?${o}`),p()}},"Search"))),a.appendChild(s);function p(){if(g(s),!t.length){s.appendChild(e("p",{},"No results yet."));return}const o=e("table",{},e("thead",{},e("tr",{},e("th",{},"Name"),e("th",{},"Country"),e("th",{},"Tags"),e("th",{},"Stream"),e("th",{},""))),e("tbody",{},...t.map(c=>{var l,r;return e("tr",{},e("td",{},c.name),e("td",{},c.country||""),e("td",{},...(c.genres||[]).slice(0,4).map(d=>e("span",{class:"tag"},d))),e("td",{},e("small",{},(((l=c.streams[0])==null?void 0:l.format)||"")+" "+(((r=c.streams[0])==null?void 0:r.bitrate)||""))),e("td",{},e("button",{class:"btn primary",onClick:async()=>{await i.post("/api/stations/sources/radiobrowser/import",c),alert(`Imported ${c.name}`)}},"Import")))})));s.appendChild(o)}}function $(a){a.appendChild(e("div",{class:"bar"},e("h2",{style:{margin:0,flex:1}},"Users"),e("button",{class:"btn primary",onClick:D},"+ Add user"))),a.appendChild(e("table",{},e("thead",{},e("tr",{},e("th",{},"Username"),e("th",{},"Role"),e("th",{},"Created"),e("th",{},""))),e("tbody",{},...n.users.map(t=>e("tr",{},e("td",{},t.username),e("td",{},t.role),e("td",{},t.created_at),e("td",{},e("button",{class:"btn",onClick:async()=>{const s=prompt(`New password for ${t.username}:`);s&&(await i.patch(`/api/auth/users/${t.id}`,{password:s}),alert("Updated"))}},"Reset PW")," ",e("button",{class:"btn",onClick:async()=>{const s=t.role==="admin"?"user":"admin";await i.patch(`/api/auth/users/${t.id}`,{role:s}),await u(),m()}},"Toggle role")," ",t.id!==n.user.id?e("button",{class:"btn danger",onClick:async()=>{confirm(`Delete ${t.username}?`)&&(await i.del(`/api/auth/users/${t.id}`),await u(),m())}},"Delete"):null))))))}function D(){const a=e("dialog");a.appendChild(e("form",{method:"dialog",onSubmit:async t=>{t.preventDefault();const s=new FormData(t.target);await i.post("/api/auth/users",{username:s.get("username"),password:s.get("password"),role:s.get("role")}),a.close(),await u(),m()}},e("h2",{},"New user"),e("div",{class:"row"},e("label",{},"Username"),e("input",{name:"username",required:!0})),e("div",{class:"row"},e("label",{},"Password"),e("input",{name:"password",type:"password",required:!0})),e("div",{class:"row"},e("label",{},"Role"),e("select",{name:"role"},e("option",{value:"user"},"user"),e("option",{value:"admin"},"admin"))),e("div",{class:"actions"},e("button",{class:"btn",type:"button",onClick:()=>a.close()},"Cancel"),e("button",{class:"btn primary",type:"submit"},"Create")))),document.body.appendChild(a),a.showModal(),a.addEventListener("close",()=>a.remove())}function E(a){const t=n.system||{};a.appendChild(e("h2",{},"System")),a.appendChild(e("div",{class:"system-grid"},h("Stations",t.stations),h("Streams",t.streams),h("Users",t.users),h("Favorites",t.favorites),h("Node",t.node),h("Uptime (s)",t.uptime_s))),a.appendChild(e("div",{class:"bar",style:{marginTop:"16px"}},e("button",{class:"btn",onClick:async()=>{await i.post("/api/admin/health-check"),alert("Health check finished"),await u(),m()}},"Run health check"),e("button",{class:"btn",onClick:async()=>{const s=await i.post("/api/admin/reseed");alert(JSON.stringify(s))}},"Reseed (if empty)")))}function h(a,t){return e("div",{class:"stat"},e("div",{class:"k"},a),e("div",{class:"v"},t??"β€”"))}v(); diff --git a/server/public/assets/admin-CVu6KAFb.js b/server/public/assets/admin-CVu6KAFb.js deleted file mode 100644 index 4f5492c..0000000 --- a/server/public/assets/admin-CVu6KAFb.js +++ /dev/null @@ -1 +0,0 @@ -import{a as i,c as g,e}from"./dom-BZgKDOeX.js";const b=document.getElementById("app"),n={user:null,view:"stations",stations:[],users:[],system:null,search:""};async function v(){try{n.user=await i.get("/api/auth/me")}catch{return C()}if(n.user.role!=="admin"){b.innerHTML=`

Admin only

Signed in as ${n.user.username} (${n.user.role}).

`;return}await u(),m()}async function u(){const a=[i.get("/api/stations?all=1")];n.view==="users"&&a.push(i.get("/api/auth/users")),n.view==="system"&&a.push(i.get("/api/admin/system"));const[t,s,p]=await Promise.all(a);n.stations=t,n.view==="users"&&(n.users=s||[]),n.view==="system"&&(n.system=s||p||null)}function C(){g(b),b.appendChild(e("div",{class:"login"},e("form",{onSubmit:async a=>{a.preventDefault();const t=new FormData(a.target);try{n.user=await i.post("/api/auth/login",{username:t.get("username"),password:t.get("password")}),await v()}catch(s){a.target.querySelector(".err").textContent=s.message}}},e("h1",{},"Admin sign in"),e("input",{name:"username",placeholder:"Username",required:!0}),e("input",{name:"password",type:"password",placeholder:"Password",required:!0}),e("div",{class:"err"}),e("button",{class:"btn primary",type:"submit"},"Sign in"))))}function m(){g(b);const a=e("aside",{class:"side"},e("h1",{},"Online Radio Explorer"),...["stations","import","users","system"].map(s=>e("button",{class:`nav ${n.view===s?"active":""}`,onClick:async()=>{n.view=s,await u(),m()}},k(s))),e("div",{class:"me"},`Signed in as ${n.user.username}`,e("br"),e("a",{href:"#",onClick:async s=>{s.preventDefault(),await i.post("/api/auth/logout"),location.reload()}},"Sign out"))),t=e("main",{class:"main"});n.view==="stations"?S(t):n.view==="import"?I(t):n.view==="users"?$(t):n.view==="system"&&E(t),b.appendChild(e("div",{class:"shell"},a,t))}function k(a){return{stations:"Stations",import:"Import",users:"Users",system:"System"}[a]}function S(a){a.appendChild(e("div",{class:"bar"},e("input",{placeholder:"Search…",value:n.search,onInput:s=>{n.search=s.target.value,w()}}),e("button",{class:"btn primary",onClick:()=>f()},"+ Add station"),e("button",{class:"btn",onClick:async()=>{await i.post("/api/admin/health-check"),alert("Health check finished"),await u(),m()}},"Run health check")));const t=e("div",{id:"tableWrap"});a.appendChild(t),w()}function w(){const a=document.getElementById("tableWrap");if(!a)return;g(a);const t=n.search.toLowerCase(),s=n.stations.filter(o=>!t||o.name.toLowerCase().includes(t)||(o.country||"").toLowerCase().includes(t)||(o.genres||[]).some(c=>c.toLowerCase().includes(t))),p=e("table",{},e("thead",{},e("tr",{},e("th",{},"Name"),e("th",{},"Source"),e("th",{},"Genres"),e("th",{},"Country"),e("th",{},"Enabled"),e("th",{},"Actions"))),e("tbody",{},...s.map(o=>e("tr",{},e("td",{},e("strong",{},o.name),e("br"),e("small",{},o.homepage||"")),e("td",{},o.source),e("td",{},...(o.genres||[]).slice(0,4).map(c=>e("span",{class:"tag"},c))),e("td",{},o.country||""),e("td",{},o.enabled?"βœ…":"β›”"),e("td",{},e("button",{class:"btn",onClick:()=>f(o.id)},"Edit")," ",e("button",{class:"btn danger",onClick:async()=>{confirm(`Delete ${o.name}?`)&&(await i.del(`/api/stations/${o.id}`),await u(),m())}},"Delete"))))));a.appendChild(p)}async function f(a){const t=a?await i.get(`/api/stations/${a}`):{name:"",genres:[],streams:[],enabled:!0},s=e("dialog"),p=e("div",{class:"streams"});function o(){var l;g(p),p.appendChild(e("div",{style:{fontWeight:600,marginBottom:"6px"}},"Streams")),(l=t.streams)!=null&&l.length||p.appendChild(e("div",{style:{color:"#6b7280"}},"No streams yet."));for(const r of t.streams||[])p.appendChild(e("div",{class:"stream-row"},e("select",{onChange:d=>r.format=d.target.value},...["mp3","aac","hls","m3u","pls","ogg","unknown"].map(d=>e("option",{value:d,selected:r.format===d},d))),e("input",{value:r.url,placeholder:"https://…",onInput:d=>r.url=d.target.value}),e("input",{type:"number",placeholder:"kbps",value:r.bitrate||"",onInput:d=>r.bitrate=Number(d.target.value)||null}),e("input",{value:r.label||"",placeholder:"Label",onInput:d=>r.label=d.target.value}),r.last_status?e("span",{class:`pill ${r.last_status==="up"?"up":"down"}`},r.last_status):e("span"),e("button",{class:"btn danger",type:"button",onClick:()=>{t.streams=t.streams.filter(d=>d!==r),o()}},"Γ—")));p.appendChild(e("button",{class:"btn",type:"button",onClick:()=>{var r;t.streams=[...t.streams||[],{url:"",format:"mp3",priority:((r=t.streams)==null?void 0:r.length)||0}],o()}},"+ Add stream"))}const c=e("form",{method:"dialog",onSubmit:async l=>{l.preventDefault();const r={name:t.name,homepage:t.homepage,country:t.country,genres:t.genres,description:t.description,image_url:t.image_url,enabled:t.enabled};if(a){await i.patch(`/api/stations/${a}`,r);const d=await i.get(`/api/stations/${a}`);for(const y of d.streams||[])await i.del(`/api/stations/${a}/streams/${y.id}`);for(const y of t.streams||[])y.url&&await i.post(`/api/stations/${a}/streams`,y)}else r.streams=(t.streams||[]).filter(d=>d.url),await i.post("/api/stations",r);s.close(),await u(),m()}},e("h2",{},a?"Edit station":"Add station"),e("div",{class:"row"},e("label",{},"Name"),e("input",{value:t.name,onInput:l=>t.name=l.target.value,required:!0})),e("div",{class:"row"},e("label",{},"Homepage"),e("input",{value:t.homepage||"",onInput:l=>t.homepage=l.target.value})),e("div",{class:"row"},e("label",{},"Country"),e("input",{value:t.country||"",maxlength:4,onInput:l=>t.country=l.target.value})),e("div",{class:"row"},e("label",{},"Genres"),e("input",{value:(t.genres||[]).join(", "),onInput:l=>t.genres=l.target.value.split(",").map(r=>r.trim()).filter(Boolean)})),e("div",{class:"row"},e("label",{},"Image URL"),e("input",{value:t.image_url||"",onInput:l=>t.image_url=l.target.value})),e("div",{class:"row col"},e("textarea",{rows:2,placeholder:"Description",onInput:l=>t.description=l.target.value},t.description||"")),e("div",{class:"row"},e("label",{},"Enabled"),e("input",{type:"checkbox",checked:t.enabled,onChange:l=>t.enabled=l.target.checked})),p,e("div",{class:"actions"},e("button",{class:"btn",type:"button",onClick:()=>s.close()},"Cancel"),e("button",{class:"btn primary",type:"submit"},"Save")));o(),s.appendChild(c),document.body.appendChild(s),s.showModal(),s.addEventListener("close",()=>s.remove())}function I(a){let t=[];const s=e("div");a.appendChild(e("h2",{},"Import from Radio-Browser")),a.appendChild(e("div",{class:"bar"},e("input",{id:"rbq",placeholder:"Search by name…"}),e("input",{id:"rbcountry",placeholder:"Country (e.g. NL)",style:{minWidth:"120px"}}),e("input",{id:"rbtag",placeholder:"Tag/genre"}),e("button",{class:"btn primary",onClick:async()=>{const o=new URLSearchParams({q:document.getElementById("rbq").value,country:document.getElementById("rbcountry").value,tag:document.getElementById("rbtag").value});t=await i.get(`/api/stations/sources/radiobrowser/search?${o}`),p()}},"Search"))),a.appendChild(s);function p(){if(g(s),!t.length){s.appendChild(e("p",{},"No results yet."));return}const o=e("table",{},e("thead",{},e("tr",{},e("th",{},"Name"),e("th",{},"Country"),e("th",{},"Tags"),e("th",{},"Stream"),e("th",{},""))),e("tbody",{},...t.map(c=>{var l,r;return e("tr",{},e("td",{},c.name),e("td",{},c.country||""),e("td",{},...(c.genres||[]).slice(0,4).map(d=>e("span",{class:"tag"},d))),e("td",{},e("small",{},(((l=c.streams[0])==null?void 0:l.format)||"")+" "+(((r=c.streams[0])==null?void 0:r.bitrate)||""))),e("td",{},e("button",{class:"btn primary",onClick:async()=>{await i.post("/api/stations/sources/radiobrowser/import",c),alert(`Imported ${c.name}`)}},"Import")))})));s.appendChild(o)}}function $(a){a.appendChild(e("div",{class:"bar"},e("h2",{style:{margin:0,flex:1}},"Users"),e("button",{class:"btn primary",onClick:D},"+ Add user"))),a.appendChild(e("table",{},e("thead",{},e("tr",{},e("th",{},"Username"),e("th",{},"Role"),e("th",{},"Created"),e("th",{},""))),e("tbody",{},...n.users.map(t=>e("tr",{},e("td",{},t.username),e("td",{},t.role),e("td",{},t.created_at),e("td",{},e("button",{class:"btn",onClick:async()=>{const s=prompt(`New password for ${t.username}:`);s&&(await i.patch(`/api/auth/users/${t.id}`,{password:s}),alert("Updated"))}},"Reset PW")," ",e("button",{class:"btn",onClick:async()=>{const s=t.role==="admin"?"user":"admin";await i.patch(`/api/auth/users/${t.id}`,{role:s}),await u(),m()}},"Toggle role")," ",t.id!==n.user.id?e("button",{class:"btn danger",onClick:async()=>{confirm(`Delete ${t.username}?`)&&(await i.del(`/api/auth/users/${t.id}`),await u(),m())}},"Delete"):null))))))}function D(){const a=e("dialog");a.appendChild(e("form",{method:"dialog",onSubmit:async t=>{t.preventDefault();const s=new FormData(t.target);await i.post("/api/auth/users",{username:s.get("username"),password:s.get("password"),role:s.get("role")}),a.close(),await u(),m()}},e("h2",{},"New user"),e("div",{class:"row"},e("label",{},"Username"),e("input",{name:"username",required:!0})),e("div",{class:"row"},e("label",{},"Password"),e("input",{name:"password",type:"password",required:!0})),e("div",{class:"row"},e("label",{},"Role"),e("select",{name:"role"},e("option",{value:"user"},"user"),e("option",{value:"admin"},"admin"))),e("div",{class:"actions"},e("button",{class:"btn",type:"button",onClick:()=>a.close()},"Cancel"),e("button",{class:"btn primary",type:"submit"},"Create")))),document.body.appendChild(a),a.showModal(),a.addEventListener("close",()=>a.remove())}function E(a){const t=n.system||{};a.appendChild(e("h2",{},"System")),a.appendChild(e("div",{class:"system-grid"},h("Stations",t.stations),h("Streams",t.streams),h("Users",t.users),h("Favorites",t.favorites),h("Node",t.node),h("Uptime (s)",t.uptime_s))),a.appendChild(e("div",{class:"bar",style:{marginTop:"16px"}},e("button",{class:"btn",onClick:async()=>{await i.post("/api/admin/health-check"),alert("Health check finished"),await u(),m()}},"Run health check"),e("button",{class:"btn",onClick:async()=>{const s=await i.post("/api/admin/reseed");alert(JSON.stringify(s))}},"Reseed (if empty)")))}function h(a,t){return e("div",{class:"stat"},e("div",{class:"k"},a),e("div",{class:"v"},t??"β€”"))}v(); diff --git a/server/public/assets/docs-CJfnRuXm.js b/server/public/assets/docs-CJfnRuXm.js new file mode 100644 index 0000000..41b0f84 --- /dev/null +++ b/server/public/assets/docs-CJfnRuXm.js @@ -0,0 +1,5 @@ +import"./modulepreload-polyfill-B5Qt9EMX.js";const p=`${location.origin}/api/v1`,u=`${location.origin}/api`;document.getElementById("base").textContent=p;const C=[{group:"Public (v1)",items:[{id:"health",method:"GET",path:"/health",summary:"Service heartbeat plus enabled-station count.",tryable:!0},{id:"categories",method:"GET",path:"/categories",summary:"All categories with their station counts.",tryable:!0},{id:"stations-list",method:"GET",path:"/stations",summary:"Paginated station list. Filterable and sortable.",params:[{name:"q",desc:"Substring filter on name / genres / country."},{name:"category",desc:"Category id (see /categories)."},{name:"country",desc:"ISO country code, case-insensitive."},{name:"genre",desc:"Substring match against any genre."},{name:"sort",desc:"hot | top | plays | controversial | name (default: name)."},{name:"limit",desc:"Max items returned (default 200, cap 1000)."}],tryable:!0,tryQuery:"limit=3&sort=hot"},{id:"random",method:"GET",path:"/stations/random",summary:"Pick one random enabled station. Same filters as /stations. Pass redirect=stream for a 302 to the resolved audio URL.",params:[{name:"category",desc:"Restrict pool to a category."},{name:"country",desc:"Restrict pool to a country."},{name:"genre",desc:"Restrict pool by genre substring."},{name:"redirect",desc:'Set to "stream" to 302-redirect to the resolved stream URL.'}],tryable:!0,examples:[`mpv ${p}/stations/random?redirect=stream`,`curl -sLI "${p}/stations/random?redirect=stream" | grep -i location`]},{id:"station",method:"GET",path:"/stations/{uuid}",summary:"Full detail for one station, including its streams.",params:[{name:"uuid",desc:"Station UUID (see list response)."}]},{id:"station-stream",method:"GET",path:"/stations/{uuid}/stream",summary:"302-redirect to the resolved stream URL. Picks the highest-priority stream that was last seen up.",params:[{name:"uuid",desc:"Station UUID."},{name:"format",desc:"Optional preferred format (mp3, aac, ogg, hls)."}]},{id:"stream-by-uuid",method:"GET",path:"/stations/{uuid}/streams/{streamUuid}",summary:"Resolve and 302 to a specific stream. Pass redirect=0 to return JSON metadata instead.",params:[{name:"uuid",desc:"Station UUID."},{name:"streamUuid",desc:"Stream UUID."},{name:"redirect",desc:'Set to "0" to return JSON instead of redirecting.'}]}]},{group:"Authenticated (cookie session)",items:[{id:"me",method:"GET",path:"/auth/me",base:u,summary:"Current signed-in user, or 401.",tryable:!0},{id:"favorites",method:"GET",path:"/me/favorites",base:u,summary:"Your favorites, ordered.",tryable:!0},{id:"favorites-random",method:"GET",path:"/me/favorites/random",base:u,summary:'One random favorite β€” used by the kiosk dice button in "favorites" mode.',tryable:!0},{id:"history",method:"GET",path:"/me/history",base:u,summary:"Recent play history (last 50).",tryable:!0}]},{group:"Rate limit",items:[{id:"rate",method:"INFO",path:"120 req / minute / IP",summary:"Public /api/v1 endpoints share a per-IP token bucket. Headers X-RateLimit-Limit and X-RateLimit-Remaining tell you where you stand."}]}],f=document.getElementById("app");function e(t,i,...d){const s=document.createElement(t);if(i)for(const[a,o]of Object.entries(i))o==null||o===!1||(a==="class"?s.className=o:a.startsWith("on")&&typeof o=="function"?s.addEventListener(a.slice(2).toLowerCase(),o):s.setAttribute(a,o));for(const a of d)a==null||a===!1||s.appendChild(typeof a=="string"?document.createTextNode(a):a);return s}function E(t){return e("span",{class:`m m-${t.toLowerCase()}`},t)}function T(t){var a,o;const d=`${t.base||p}${t.path}`,s=e("article",{class:"ep",id:t.id});if(s.appendChild(e("header",{class:"ep-head"},E(t.method),e("code",{class:"ep-path"},d))),s.appendChild(e("p",{class:"ep-sum"},t.summary)),(a=t.params)!=null&&a.length){const n=e("table",{class:"params"},e("thead",{},e("tr",{},e("th",{},"Parameter"),e("th",{},"Description"))),e("tbody",{},...t.params.map(m=>e("tr",{},e("td",{},e("code",{},m.name)),e("td",{},m.desc)))));s.appendChild(n)}if((o=t.examples)!=null&&o.length&&s.appendChild(e("div",{class:"examples"},e("div",{class:"examples-h"},"Examples"),...t.examples.map(n=>e("pre",{},e("code",{},n))))),t.tryable&&t.method==="GET"){const n=e("pre",{class:"try-out"},'Click "Try it" to send a live request.'),m=e("input",{class:"try-q",type:"text",placeholder:"?key=value (optional)",value:t.tryQuery?`?${t.tryQuery}`:""}),c=e("button",{class:"try-btn",onClick:async()=>{c.disabled=!0,c.textContent="…";let l=m.value.trim();l&&!l.startsWith("?")&&(l=`?${l}`);const h=`${d}${l}`,g=performance.now();try{const r=await fetch(h,{credentials:"same-origin",redirect:"manual"}),v=Math.round(performance.now()-g);let y;r.type==="opaqueredirect"||r.status>=300&&r.status<400?y="(redirect β€” open in new tab to follow)":y=(r.headers.get("content-type")||"").includes("json")?JSON.stringify(await r.json(),null,2):(await r.text()).slice(0,4e3),n.textContent=`${r.status} ${r.statusText||""} Β· ${v} ms +${h} + +${y}`}catch(r){n.textContent=`error: ${r.message||r} +${h}`}finally{c.disabled=!1,c.textContent="Try it"}}},"Try it"),b=e("a",{class:"try-open",target:"_blank",rel:"noopener",href:d},"Open β†—");s.appendChild(e("div",{class:"try"},e("div",{class:"try-row"},m,c,b),n))}return s}for(const t of C){f.appendChild(e("h2",{class:"group"},t.group));for(const i of t.items)f.appendChild(T(i))} diff --git a/server/public/assets/docs-z3ZiwvpP.css b/server/public/assets/docs-z3ZiwvpP.css new file mode 100644 index 0000000..2491d33 --- /dev/null +++ b/server/public/assets/docs-z3ZiwvpP.css @@ -0,0 +1 @@ +:root{--bg-0: #07080b;--bg-1: #0e1116;--bg-2: #161a22;--bg-3: #1f242e;--line: #262b36;--fg: #e9ecf2;--muted: #8a90a0;--muted-2: #5d6373;--accent: #ff7a3d;--accent-2: #ffb37a;--good: #4ec9a6;--bad: #ec6a6a;--info: #6ab7ff;font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,sans-serif;color-scheme:dark}*{box-sizing:border-box}html,body{margin:0;padding:0;background:var(--bg-0);color:var(--fg)}a{color:var(--accent-2);text-decoration:none}a:hover{text-decoration:underline}.docs-header{position:sticky;top:0;z-index:10;background:#07080beb;border-bottom:1px solid var(--line);-webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px)}.docs-header-inner{max-width:980px;margin:0 auto;padding:14px 20px;display:flex;align-items:center;gap:16px}.docs-header h1{margin:0;font-size:18px;letter-spacing:-.01em}.docs-header .back{color:var(--muted);font-size:13px;padding:6px 10px;border:1px solid var(--line);border-radius:8px}.docs-header .back:hover{color:var(--fg);background:var(--bg-2);text-decoration:none}.docs-header .base{margin-left:auto;font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:12px;color:var(--muted);padding:4px 10px;background:var(--bg-2);border:1px solid var(--line);border-radius:8px}#app{max-width:980px;margin:0 auto;padding:24px 20px 80px}h2.group{margin:32px 0 12px;font-size:13px;text-transform:uppercase;letter-spacing:.1em;color:var(--muted)}h2.group:first-child{margin-top:0}.ep{background:var(--bg-1);border:1px solid var(--line);border-radius:12px;padding:16px;margin-bottom:14px}.ep-head{display:flex;align-items:center;gap:10px;flex-wrap:wrap}.ep-path{font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:13px;color:var(--fg);background:var(--bg-2);padding:4px 10px;border-radius:6px;border:1px solid var(--line);overflow-wrap:anywhere}.ep-sum{margin:10px 0 0;color:var(--muted);font-size:14px;line-height:1.5}.m{display:inline-block;font-size:11px;font-weight:800;letter-spacing:.06em;padding:4px 8px;border-radius:6px;color:#07080b}.m-get{background:var(--good)}.m-post{background:var(--accent)}.m-put{background:#d9b14a}.m-delete{background:var(--bad);color:#fff}.m-info{background:var(--info)}.params{width:100%;border-collapse:collapse;margin-top:14px;font-size:13px}.params th{text-align:left;font-weight:600;color:var(--muted);text-transform:uppercase;font-size:11px;letter-spacing:.06em;padding:8px 10px;border-bottom:1px solid var(--line)}.params td{padding:8px 10px;border-bottom:1px solid var(--line);color:var(--fg)}.params td:first-child{width:160px}.params code{font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:12px;color:var(--accent-2);background:var(--bg-2);padding:2px 6px;border-radius:4px}.examples{margin-top:14px}.examples-h{font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px}.examples pre{margin:0 0 8px;padding:10px 12px;background:var(--bg-0);border:1px solid var(--line);border-radius:8px;font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:12.5px;color:var(--accent-2);overflow-x:auto}.try{margin-top:14px}.try-row{display:flex;gap:8px;align-items:center;margin-bottom:8px}.try-q{flex:1;background:var(--bg-2);color:var(--fg);border:1px solid var(--line);border-radius:8px;padding:8px 12px;font-size:13px;font-family:ui-monospace,SF Mono,Menlo,monospace;outline:none}.try-q:focus{border-color:var(--accent)}.try-btn{background:var(--accent);color:#1a0a00;border:0;border-radius:8px;padding:8px 16px;font-size:13px;font-weight:700;cursor:pointer;font-family:inherit}.try-btn:hover{background:#ff8a55}.try-btn:disabled{opacity:.6;cursor:default}.try-open{color:var(--muted);font-size:12px;padding:6px 10px;border:1px solid var(--line);border-radius:8px}.try-open:hover{color:var(--fg);background:var(--bg-2);text-decoration:none}.try-out{margin:0;padding:12px;background:var(--bg-0);border:1px solid var(--line);border-radius:8px;font-family:ui-monospace,SF Mono,Menlo,monospace;font-size:12px;color:var(--fg);max-height:320px;overflow:auto;white-space:pre-wrap;word-break:break-word} diff --git a/server/public/assets/dom-BZgKDOeX.js b/server/public/assets/dom-BZgKDOeX.js deleted file mode 100644 index 677dcae..0000000 --- a/server/public/assets/dom-BZgKDOeX.js +++ /dev/null @@ -1 +0,0 @@ -(function(){const n=document.createElement("link").relList;if(n&&n.supports&&n.supports("modulepreload"))return;for(const e of document.querySelectorAll('link[rel="modulepreload"]'))s(e);new MutationObserver(e=>{for(const t of e)if(t.type==="childList")for(const c of t.addedNodes)c.tagName==="LINK"&&c.rel==="modulepreload"&&s(c)}).observe(document,{childList:!0,subtree:!0});function o(e){const t={};return e.integrity&&(t.integrity=e.integrity),e.referrerPolicy&&(t.referrerPolicy=e.referrerPolicy),e.crossOrigin==="use-credentials"?t.credentials="include":e.crossOrigin==="anonymous"?t.credentials="omit":t.credentials="same-origin",t}function s(e){if(e.ep)return;e.ep=!0;const t=o(e);fetch(e.href,t)}})();async function i(r,n,o){const s=await fetch(n,{method:r,credentials:"same-origin",headers:o?{"Content-Type":"application/json"}:{},body:o?JSON.stringify(o):void 0});if(s.status===204)return null;const t=(s.headers.get("content-type")||"").includes("json")?await s.json():await s.text();if(!s.ok)throw Object.assign(new Error((t==null?void 0:t.error)||s.statusText),{status:s.status,data:t});return t}const l={get:r=>i("GET",r),post:(r,n)=>i("POST",r,n),put:(r,n)=>i("PUT",r,n),patch:(r,n)=>i("PATCH",r,n),del:r=>i("DELETE",r)};function a(r,n={},...o){const s=document.createElement(r);for(const[e,t]of Object.entries(n||{}))e==="class"?s.className=t:e==="style"&&typeof t=="object"?Object.assign(s.style,t):e.startsWith("on")&&typeof t=="function"?s.addEventListener(e.slice(2).toLowerCase(),t):e==="html"?s.innerHTML=t:t!==!1&&t!=null&&s.setAttribute(e,t===!0?"":t);for(const e of o.flat())e==null||e===!1||s.appendChild(e instanceof Node?e:document.createTextNode(String(e)));return s}function f(r){for(;r.firstChild;)r.removeChild(r.firstChild)}export{l as a,f as c,a as e}; diff --git a/server/public/assets/dom-BvorgAdo.js b/server/public/assets/dom-BvorgAdo.js new file mode 100644 index 0000000..0c5b70a --- /dev/null +++ b/server/public/assets/dom-BvorgAdo.js @@ -0,0 +1 @@ +async function a(t,i,o){const s=await fetch(i,{method:t,credentials:"same-origin",headers:o?{"Content-Type":"application/json"}:{},body:o?JSON.stringify(o):void 0});if(s.status===204)return null;const e=(s.headers.get("content-type")||"").includes("json")?await s.json():await s.text();if(!s.ok)throw Object.assign(new Error((e==null?void 0:e.error)||s.statusText),{status:s.status,data:e});return e}const c={get:t=>a("GET",t),post:(t,i)=>a("POST",t,i),put:(t,i)=>a("PUT",t,i),patch:(t,i)=>a("PATCH",t,i),del:t=>a("DELETE",t)};function r(t,i={},...o){const s=document.createElement(t);for(const[n,e]of Object.entries(i||{}))n==="class"?s.className=e:n==="style"&&typeof e=="object"?Object.assign(s.style,e):n.startsWith("on")&&typeof e=="function"?s.addEventListener(n.slice(2).toLowerCase(),e):n==="html"?s.innerHTML=e:e!==!1&&e!=null&&s.setAttribute(n,e===!0?"":e);for(const n of o.flat())n==null||n===!1||s.appendChild(n instanceof Node?n:document.createTextNode(String(n)));return s}function l(t){for(;t.firstChild;)t.removeChild(t.firstChild)}export{c as a,l as c,r as e}; diff --git a/server/public/assets/kiosk-DBnbAN5w.js b/server/public/assets/kiosk-C37Mmo8O.js similarity index 64% rename from server/public/assets/kiosk-DBnbAN5w.js rename to server/public/assets/kiosk-C37Mmo8O.js index 6e7a29a..6e0e3ef 100644 --- a/server/public/assets/kiosk-DBnbAN5w.js +++ b/server/public/assets/kiosk-C37Mmo8O.js @@ -1,40 +1,40 @@ -import{a as Pe,c as xi,e as M}from"./dom-BZgKDOeX.js";function eo(a){let e,t=0,s=!1;function i(){const r=location.protocol==="https:"?"wss":"ws";e=new WebSocket(`${r}://${location.host}/ws`),e.addEventListener("open",()=>{t=0}),e.addEventListener("message",n=>{try{a(JSON.parse(n.data))}catch{}}),e.addEventListener("close",()=>{s||(t=Math.min(t+1,6),setTimeout(i,500*2**t))}),e.addEventListener("error",()=>e.close())}return i(),{send(r){(e==null?void 0:e.readyState)===WebSocket.OPEN&&e.send(JSON.stringify(r))},close(){s=!0,e==null||e.close()}}}const N=Number.isFinite||function(a){return typeof a=="number"&&isFinite(a)},to=Number.isSafeInteger||function(a){return typeof a=="number"&&Math.abs(a)<=so},so=Number.MAX_SAFE_INTEGER||9007199254740991;let H=function(a){return a.NETWORK_ERROR="networkError",a.MEDIA_ERROR="mediaError",a.KEY_SYSTEM_ERROR="keySystemError",a.MUX_ERROR="muxError",a.OTHER_ERROR="otherError",a}({}),R=function(a){return a.KEY_SYSTEM_NO_KEYS="keySystemNoKeys",a.KEY_SYSTEM_NO_ACCESS="keySystemNoAccess",a.KEY_SYSTEM_NO_SESSION="keySystemNoSession",a.KEY_SYSTEM_NO_CONFIGURED_LICENSE="keySystemNoConfiguredLicense",a.KEY_SYSTEM_LICENSE_REQUEST_FAILED="keySystemLicenseRequestFailed",a.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED="keySystemServerCertificateRequestFailed",a.KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED="keySystemServerCertificateUpdateFailed",a.KEY_SYSTEM_SESSION_UPDATE_FAILED="keySystemSessionUpdateFailed",a.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED="keySystemStatusOutputRestricted",a.KEY_SYSTEM_STATUS_INTERNAL_ERROR="keySystemStatusInternalError",a.KEY_SYSTEM_DESTROY_MEDIA_KEYS_ERROR="keySystemDestroyMediaKeysError",a.KEY_SYSTEM_DESTROY_CLOSE_SESSION_ERROR="keySystemDestroyCloseSessionError",a.KEY_SYSTEM_DESTROY_REMOVE_SESSION_ERROR="keySystemDestroyRemoveSessionError",a.MANIFEST_LOAD_ERROR="manifestLoadError",a.MANIFEST_LOAD_TIMEOUT="manifestLoadTimeOut",a.MANIFEST_PARSING_ERROR="manifestParsingError",a.MANIFEST_INCOMPATIBLE_CODECS_ERROR="manifestIncompatibleCodecsError",a.LEVEL_EMPTY_ERROR="levelEmptyError",a.LEVEL_LOAD_ERROR="levelLoadError",a.LEVEL_LOAD_TIMEOUT="levelLoadTimeOut",a.LEVEL_PARSING_ERROR="levelParsingError",a.LEVEL_SWITCH_ERROR="levelSwitchError",a.AUDIO_TRACK_LOAD_ERROR="audioTrackLoadError",a.AUDIO_TRACK_LOAD_TIMEOUT="audioTrackLoadTimeOut",a.SUBTITLE_LOAD_ERROR="subtitleTrackLoadError",a.SUBTITLE_TRACK_LOAD_TIMEOUT="subtitleTrackLoadTimeOut",a.FRAG_LOAD_ERROR="fragLoadError",a.FRAG_LOAD_TIMEOUT="fragLoadTimeOut",a.FRAG_DECRYPT_ERROR="fragDecryptError",a.FRAG_PARSING_ERROR="fragParsingError",a.FRAG_GAP="fragGap",a.REMUX_ALLOC_ERROR="remuxAllocError",a.KEY_LOAD_ERROR="keyLoadError",a.KEY_LOAD_TIMEOUT="keyLoadTimeOut",a.BUFFER_ADD_CODEC_ERROR="bufferAddCodecError",a.BUFFER_INCOMPATIBLE_CODECS_ERROR="bufferIncompatibleCodecsError",a.BUFFER_APPEND_ERROR="bufferAppendError",a.BUFFER_APPENDING_ERROR="bufferAppendingError",a.BUFFER_STALLED_ERROR="bufferStalledError",a.BUFFER_FULL_ERROR="bufferFullError",a.BUFFER_SEEK_OVER_HOLE="bufferSeekOverHole",a.BUFFER_NUDGE_ON_STALL="bufferNudgeOnStall",a.ASSET_LIST_LOAD_ERROR="assetListLoadError",a.ASSET_LIST_LOAD_TIMEOUT="assetListLoadTimeout",a.ASSET_LIST_PARSING_ERROR="assetListParsingError",a.INTERSTITIAL_ASSET_ITEM_ERROR="interstitialAssetItemError",a.INTERNAL_EXCEPTION="internalException",a.INTERNAL_ABORTED="aborted",a.ATTACH_MEDIA_ERROR="attachMediaError",a.UNKNOWN="unknown",a}({}),m=function(a){return a.MEDIA_ATTACHING="hlsMediaAttaching",a.MEDIA_ATTACHED="hlsMediaAttached",a.MEDIA_DETACHING="hlsMediaDetaching",a.MEDIA_DETACHED="hlsMediaDetached",a.MEDIA_ENDED="hlsMediaEnded",a.STALL_RESOLVED="hlsStallResolved",a.BUFFER_RESET="hlsBufferReset",a.BUFFER_CODECS="hlsBufferCodecs",a.BUFFER_CREATED="hlsBufferCreated",a.BUFFER_APPENDING="hlsBufferAppending",a.BUFFER_APPENDED="hlsBufferAppended",a.BUFFER_EOS="hlsBufferEos",a.BUFFERED_TO_END="hlsBufferedToEnd",a.BUFFER_FLUSHING="hlsBufferFlushing",a.BUFFER_FLUSHED="hlsBufferFlushed",a.MANIFEST_LOADING="hlsManifestLoading",a.MANIFEST_LOADED="hlsManifestLoaded",a.MANIFEST_PARSED="hlsManifestParsed",a.LEVEL_SWITCHING="hlsLevelSwitching",a.LEVEL_SWITCHED="hlsLevelSwitched",a.LEVEL_LOADING="hlsLevelLoading",a.LEVEL_LOADED="hlsLevelLoaded",a.LEVEL_UPDATED="hlsLevelUpdated",a.LEVEL_PTS_UPDATED="hlsLevelPtsUpdated",a.LEVELS_UPDATED="hlsLevelsUpdated",a.AUDIO_TRACKS_UPDATED="hlsAudioTracksUpdated",a.AUDIO_TRACK_SWITCHING="hlsAudioTrackSwitching",a.AUDIO_TRACK_SWITCHED="hlsAudioTrackSwitched",a.AUDIO_TRACK_LOADING="hlsAudioTrackLoading",a.AUDIO_TRACK_LOADED="hlsAudioTrackLoaded",a.AUDIO_TRACK_UPDATED="hlsAudioTrackUpdated",a.SUBTITLE_TRACKS_UPDATED="hlsSubtitleTracksUpdated",a.SUBTITLE_TRACKS_CLEARED="hlsSubtitleTracksCleared",a.SUBTITLE_TRACK_SWITCH="hlsSubtitleTrackSwitch",a.SUBTITLE_TRACK_LOADING="hlsSubtitleTrackLoading",a.SUBTITLE_TRACK_LOADED="hlsSubtitleTrackLoaded",a.SUBTITLE_TRACK_UPDATED="hlsSubtitleTrackUpdated",a.SUBTITLE_FRAG_PROCESSED="hlsSubtitleFragProcessed",a.CUES_PARSED="hlsCuesParsed",a.NON_NATIVE_TEXT_TRACKS_FOUND="hlsNonNativeTextTracksFound",a.INIT_PTS_FOUND="hlsInitPtsFound",a.FRAG_LOADING="hlsFragLoading",a.FRAG_LOAD_EMERGENCY_ABORTED="hlsFragLoadEmergencyAborted",a.FRAG_LOADED="hlsFragLoaded",a.FRAG_DECRYPTED="hlsFragDecrypted",a.FRAG_PARSING_INIT_SEGMENT="hlsFragParsingInitSegment",a.FRAG_PARSING_USERDATA="hlsFragParsingUserdata",a.FRAG_PARSING_METADATA="hlsFragParsingMetadata",a.FRAG_PARSED="hlsFragParsed",a.FRAG_BUFFERED="hlsFragBuffered",a.FRAG_CHANGED="hlsFragChanged",a.FPS_DROP="hlsFpsDrop",a.FPS_DROP_LEVEL_CAPPING="hlsFpsDropLevelCapping",a.MAX_AUTO_LEVEL_UPDATED="hlsMaxAutoLevelUpdated",a.ERROR="hlsError",a.DESTROYING="hlsDestroying",a.KEY_LOADING="hlsKeyLoading",a.KEY_LOADED="hlsKeyLoaded",a.LIVE_BACK_BUFFER_REACHED="hlsLiveBackBufferReached",a.BACK_BUFFER_REACHED="hlsBackBufferReached",a.STEERING_MANIFEST_LOADED="hlsSteeringManifestLoaded",a.ASSET_LIST_LOADING="hlsAssetListLoading",a.ASSET_LIST_LOADED="hlsAssetListLoaded",a.INTERSTITIALS_UPDATED="hlsInterstitialsUpdated",a.INTERSTITIALS_BUFFERED_TO_BOUNDARY="hlsInterstitialsBufferedToBoundary",a.INTERSTITIAL_ASSET_PLAYER_CREATED="hlsInterstitialAssetPlayerCreated",a.INTERSTITIAL_STARTED="hlsInterstitialStarted",a.INTERSTITIAL_ASSET_STARTED="hlsInterstitialAssetStarted",a.INTERSTITIAL_ASSET_ENDED="hlsInterstitialAssetEnded",a.INTERSTITIAL_ASSET_ERROR="hlsInterstitialAssetError",a.INTERSTITIAL_ENDED="hlsInterstitialEnded",a.INTERSTITIALS_PRIMARY_RESUMED="hlsInterstitialsPrimaryResumed",a.PLAYOUT_LIMIT_REACHED="hlsPlayoutLimitReached",a.EVENT_CUE_ENTER="hlsEventCueEnter",a}({});var J={MANIFEST:"manifest",LEVEL:"level",AUDIO_TRACK:"audioTrack",SUBTITLE_TRACK:"subtitleTrack"},U={MAIN:"main",AUDIO:"audio",SUBTITLE:"subtitle"};class yt{constructor(e,t=0,s=0){this.halfLife=void 0,this.alpha_=void 0,this.estimate_=void 0,this.totalWeight_=void 0,this.halfLife=e,this.alpha_=e?Math.exp(Math.log(.5)/e):0,this.estimate_=t,this.totalWeight_=s}sample(e,t){const s=Math.pow(this.alpha_,e);this.estimate_=t*(1-s)+s*this.estimate_,this.totalWeight_+=e}getTotalWeight(){return this.totalWeight_}getEstimate(){if(this.alpha_){const e=1-Math.pow(this.alpha_,this.totalWeight_);if(e)return this.estimate_/e}return this.estimate_}}class io{constructor(e,t,s,i=100){this.defaultEstimate_=void 0,this.minWeight_=void 0,this.minDelayMs_=void 0,this.slow_=void 0,this.fast_=void 0,this.defaultTTFB_=void 0,this.ttfb_=void 0,this.defaultEstimate_=s,this.minWeight_=.001,this.minDelayMs_=50,this.slow_=new yt(e),this.fast_=new yt(t),this.defaultTTFB_=i,this.ttfb_=new yt(e)}update(e,t){const{slow_:s,fast_:i,ttfb_:r}=this;s.halfLife!==e&&(this.slow_=new yt(e,s.getEstimate(),s.getTotalWeight())),i.halfLife!==t&&(this.fast_=new yt(t,i.getEstimate(),i.getTotalWeight())),r.halfLife!==e&&(this.ttfb_=new yt(e,r.getEstimate(),r.getTotalWeight()))}sample(e,t){e=Math.max(e,this.minDelayMs_);const s=8*t,i=e/1e3,r=s/i;this.fast_.sample(i,r),this.slow_.sample(i,r)}sampleTTFB(e){const t=e/1e3,s=Math.sqrt(2)*Math.exp(-Math.pow(t,2)/2);this.ttfb_.sample(s,Math.max(e,5))}canEstimate(){return this.fast_.getTotalWeight()>=this.minWeight_}getEstimate(){return this.canEstimate()?Math.min(this.fast_.getEstimate(),this.slow_.getEstimate()):this.defaultEstimate_}getEstimateTTFB(){return this.ttfb_.getTotalWeight()>=this.minWeight_?this.ttfb_.getEstimate():this.defaultTTFB_}get defaultEstimate(){return this.defaultEstimate_}destroy(){}}function ro(a,e,t){return(e=ao(e))in a?Object.defineProperty(a,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):a[e]=t,a}function ne(){return ne=Object.assign?Object.assign.bind():function(a){for(var e=1;e`):st}function er(a,e,t){return e[a]?e[a].bind(e):lo(a,t)}const ri=ii();function co(a,e,t){const s=ii();if(typeof console=="object"&&a===!0||typeof a=="object"){const i=["debug","log","info","warn","error"];i.forEach(r=>{s[r]=er(r,a,t)});try{s.log(`Debug logs enabled for "${e}" in hls.js version 1.6.16`)}catch{return ii()}i.forEach(r=>{ri[r]=er(r,a)})}else ne(ri,s);return s}const re=ri;function nt(a=!0){return typeof self>"u"?void 0:(a||!self.MediaSource)&&self.ManagedMediaSource||self.MediaSource||self.WebKitMediaSource}function ho(a){return typeof self<"u"&&a===self.ManagedMediaSource}function Tn(a,e){const t=Object.keys(a),s=Object.keys(e),i=t.length,r=s.length;return!i||!r||i===r&&!t.some(n=>s.indexOf(n)===-1)}function _e(a,e=!1){if(typeof TextDecoder<"u"){const l=new TextDecoder("utf-8").decode(a);if(e){const h=l.indexOf("\0");return h!==-1?l.substring(0,h):l}return l.replace(/\0/g,"")}const t=a.length;let s,i,r,n="",o=0;for(;o>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:n+=String.fromCharCode(s);break;case 12:case 13:i=a[o++],n+=String.fromCharCode((s&31)<<6|i&63);break;case 14:i=a[o++],r=a[o++],n+=String.fromCharCode((s&15)<<12|(i&63)<<6|(r&63)<<0);break}}return n}function ye(a){let e="";for(let t=0;t1||i===1&&(t=this.levelkeys[s[0]])!=null&&t.encrypted)return!0}return!1}get programDateTime(){return this._programDateTime===null&&this.rawProgramDateTime&&(this.programDateTime=Date.parse(this.rawProgramDateTime)),this._programDateTime}set programDateTime(e){if(!N(e)){this._programDateTime=this.rawProgramDateTime=null;return}this._programDateTime=e}get ref(){return ue(this)?(this._ref||(this._ref={base:this.base,start:this.start,duration:this.duration,sn:this.sn,programDateTime:this.programDateTime}),this._ref):null}addStart(e){this.setStart(this.start+e)}setStart(e){this.start=e,this._ref&&(this._ref.start=e)}setDuration(e){this.duration=e,this._ref&&(this._ref.duration=e)}setKeyFormat(e){const t=this.levelkeys;if(t){var s;const i=t[e];i&&!((s=this._decryptdata)!=null&&s.keyId)&&(this._decryptdata=i.getDecryptData(this.sn,t))}}abortRequests(){var e,t;(e=this.loader)==null||e.abort(),(t=this.keyLoader)==null||t.abort()}setElementaryStreamInfo(e,t,s,i,r,n=!1){const{elementaryStreams:o}=this,c=o[e];if(!c){o[e]={startPTS:t,endPTS:s,startDTS:i,endDTS:r,partial:n};return}c.startPTS=Math.min(c.startPTS,t),c.endPTS=Math.max(c.endPTS,s),c.startDTS=Math.min(c.startDTS,i),c.endDTS=Math.max(c.endDTS,r)}}class go extends vn{constructor(e,t,s,i,r){super(s),this.fragOffset=0,this.duration=0,this.gap=!1,this.independent=!1,this.relurl=void 0,this.fragment=void 0,this.index=void 0,this.duration=e.decimalFloatingPoint("DURATION"),this.gap=e.bool("GAP"),this.independent=e.bool("INDEPENDENT"),this.relurl=e.enumeratedString("URI"),this.fragment=t,this.index=i;const n=e.enumeratedString("BYTERANGE");n&&this.setByteRange(n,r),r&&(this.fragOffset=r.fragOffset+r.duration)}get start(){return this.fragment.start+this.fragOffset}get end(){return this.start+this.duration}get loaded(){const{elementaryStreams:e}=this;return!!(e.audio||e.video||e.audiovideo)}}function xn(a,e){const t=Object.getPrototypeOf(a);if(t){const s=Object.getOwnPropertyDescriptor(t,e);return s||xn(t,e)}}function mo(a,e){const t=xn(a,e);t&&(t.enumerable=!0,Object.defineProperty(a,e,t))}const sr=Math.pow(2,32)-1,po=[].push,An={video:1,audio:2,id3:3,text:4};function de(a){return String.fromCharCode.apply(null,a)}function In(a,e){const t=a[e]<<8|a[e+1];return t<0?65536+t:t}function q(a,e){const t=Ln(a,e);return t<0?4294967296+t:t}function ir(a,e){let t=q(a,e);return t*=Math.pow(2,32),t+=q(a,e+4),t}function Ln(a,e){return a[e]<<24|a[e+1]<<16|a[e+2]<<8|a[e+3]}function yo(a){const e=a.byteLength;for(let t=0;t8&&a[t+4]===109&&a[t+5]===111&&a[t+6]===111&&a[t+7]===102)return!0;t=s>1?t+s:e}return!1}function Z(a,e){const t=[];if(!e.length)return t;const s=a.byteLength;for(let i=0;i1?i+r:s;if(n===e[0])if(e.length===1)t.push(a.subarray(i+8,o));else{const c=Z(a.subarray(i+8,o),e.slice(1));c.length&&po.apply(t,c)}i=o}return t}function Eo(a){const e=[],t=a[0];let s=8;const i=q(a,s);s+=4;let r=0,n=0;t===0?(r=q(a,s),n=q(a,s+4),s+=8):(r=ir(a,s),n=ir(a,s+8),s+=16),s+=2;let o=a.length+n;const c=In(a,s);s+=2;for(let l=0;l>>31===1)return re.warn("SIDX has hierarchical references (not supported)"),null;const g=q(a,h);h+=4,e.push({referenceSize:d,subsegmentDuration:g,info:{duration:g/i,start:o,end:o+d-1}}),o+=d,h+=4,s=h}return{earliestPresentationTime:r,timescale:i,version:t,referencesCount:c,references:e}}function Rn(a){const e=[],t=Z(a,["moov","trak"]);for(let i=0;i{const r=q(i,4),n=e[r];n&&(n.default={duration:q(i,12),flags:q(i,20)})}),e}function To(a){const e=a.subarray(8),t=e.subarray(86),s=de(e.subarray(4,8));let i=s,r;const n=s==="enca"||s==="encv";if(n){const l=Z(e,[s])[0].subarray(s==="enca"?28:78);Z(l,["sinf"]).forEach(u=>{const d=Z(u,["schm"])[0];if(d){const f=de(d.subarray(4,8));if(f==="cbcs"||f==="cenc"){const g=Z(u,["frma"])[0];g&&(i=de(g))}}})}const o=i;switch(i){case"avc1":case"avc2":case"avc3":case"avc4":{const c=Z(t,["avcC"])[0];c&&c.length>3&&(i+="."+zt(c[1])+zt(c[2])+zt(c[3]),r=Xt(o==="avc1"?"dva1":"dvav",t));break}case"mp4a":{const c=Z(e,[s])[0],l=Z(c.subarray(28),["esds"])[0];if(l&&l.length>7){let h=4;if(l[h++]!==3)break;h=Fs(l,h),h+=2;const u=l[h++];if(u&128&&(h+=2),u&64&&(h+=l[h++]),l[h++]!==4)break;h=Fs(l,h);const d=l[h++];if(d===64)i+="."+zt(d);else break;if(h+=12,l[h++]!==5)break;h=Fs(l,h);const f=l[h++];let g=(f&248)>>3;g===31&&(g+=1+((f&7)<<3)+((l[h]&224)>>5)),i+="."+g}break}case"hvc1":case"hev1":{const c=Z(t,["hvcC"])[0];if(c&&c.length>12){const l=c[1],h=["","A","B","C"][l>>6],u=l&31,d=q(c,2),f=(l&32)>>5?"H":"L",g=c[12],y=c.subarray(6,12);i+="."+h+u,i+="."+So(d).toString(16).toUpperCase(),i+="."+f+g;let p="";for(let E=y.length;E--;){const T=y[E];(T||p)&&(p="."+T.toString(16).toUpperCase()+p)}i+=p}r=Xt(o=="hev1"?"dvhe":"dvh1",t);break}case"dvh1":case"dvhe":case"dvav":case"dva1":case"dav1":{i=Xt(i,t)||i;break}case"vp09":{const c=Z(t,["vpcC"])[0];if(c&&c.length>6){const l=c[4],h=c[5],u=c[6]>>4&15;i+="."+Ge(l)+"."+Ge(h)+"."+Ge(u)}break}case"av01":{const c=Z(t,["av1C"])[0];if(c&&c.length>2){const l=c[1]>>>5,h=c[1]&31,u=c[2]>>>7?"H":"M",d=(c[2]&64)>>6,f=(c[2]&32)>>5,g=l===2&&d?f?12:10:d?10:8,y=(c[2]&16)>>4,p=(c[2]&8)>>3,E=(c[2]&4)>>2,T=c[2]&3;i+="."+l+"."+Ge(h)+u+"."+Ge(g)+"."+y+"."+p+E+T+"."+Ge(1)+"."+Ge(1)+"."+Ge(1)+"."+0,r=Xt("dav1",t)}break}}return{codec:i,encrypted:n,supplemental:r}}function Xt(a,e){const t=Z(e,["dvvC"]),s=t.length?t[0]:Z(e,["dvcC"])[0];if(s){const i=s[2]>>1&127,r=s[2]<<5&32|s[3]>>3&31;return a+"."+Ge(i)+"."+Ge(r)}}function So(a){let e=0;for(let t=0;t<32;t++)e|=(a>>t&1)<<31-t;return e>>>0}function Fs(a,e){const t=e+5;for(;a[e++]&128&&e{const r=s.subarray(8,24);r.some(n=>n!==0)||(re.log(`[eme] Patching keyId in 'enc${i?"a":"v"}>sinf>>tenc' box: ${ye(r)} -> ${ye(t)}`),s.set(t,8))})}function xo(a){const e=[];return bn(a,t=>e.push(t.subarray(8,24))),e}function bn(a,e){Z(a,["moov","trak"]).forEach(s=>{const i=Z(s,["mdia","minf","stbl","stsd"])[0];if(!i)return;const r=i.subarray(8);let n=Z(r,["enca"]);const o=n.length>0;o||(n=Z(r,["encv"])),n.forEach(c=>{const l=o?c.subarray(28):c.subarray(78);Z(l,["sinf"]).forEach(u=>{const d=_n(u);d&&e(d,o)})})})}function _n(a){const e=Z(a,["schm"])[0];if(e){const t=de(e.subarray(4,8));if(t==="cbcs"||t==="cenc"){const s=Z(a,["schi","tenc"])[0];if(s)return s}}}function Ao(a,e,t){const s={},i=Z(a,["moof","traf"]);for(let r=0;rs[r].duration)){let r=1/0,n=0;const o=Z(a,["sidx"]);for(let c=0;cu+d.info.duration||0,0);n=Math.max(n,h+l.earliestPresentationTime/l.timescale)}}n&&N(n)&&Object.keys(s).forEach(c=>{s[c].duration||(s[c].duration=n*s[c].timescale-s[c].start)})}return s}function Io(a){const e={valid:null,remainder:null},t=Z(a,["moof"]);if(t.length<2)return e.remainder=a,e;const s=t[t.length-1];return e.valid=a.slice(0,s.byteOffset-8),e.remainder=a.slice(s.byteOffset-8),e}function ke(a,e){const t=new Uint8Array(a.length+e.length);return t.set(a),t.set(e,a.length),t}function rr(a,e){const t=[],s=e.samples,i=e.timescale,r=e.id;let n=!1;return Z(s,["moof"]).map(c=>{const l=c.byteOffset-8;Z(c,["traf"]).map(u=>{const d=Z(u,["tfdt"]).map(f=>{const g=f[0];let y=q(f,4);return g===1&&(y*=Math.pow(2,32),y+=q(f,8)),y/i})[0];return d!==void 0&&(a=d),Z(u,["tfhd"]).map(f=>{const g=q(f,4),y=q(f,0)&16777215,p=(y&1)!==0,E=(y&2)!==0,T=(y&8)!==0;let S=0;const v=(y&16)!==0;let x=0;const D=(y&32)!==0;let A=8;g===r&&(p&&(A+=8),E&&(A+=4),T&&(S=q(f,A),A+=4),v&&(x=q(f,A),A+=4),D&&(A+=4),e.type==="video"&&(n=_s(e.codec)),Z(u,["trun"]).map(_=>{const b=_[0],I=q(_,0)&16777215,P=(I&1)!==0;let F=0;const $=(I&4)!==0,Y=(I&256)!==0;let G=0;const k=(I&512)!==0;let K=0;const B=(I&1024)!==0,V=(I&2048)!==0;let W=0;const w=q(_,4);let O=8;P&&(F=q(_,O),O+=4),$&&(O+=4);let z=F+l;for(let se=0;se>1&63;return t===39||t===40}else return(e&31)===6}function Li(a,e,t,s){const i=Dn(a);let r=0;r+=e;let n=0,o=0,c=0;for(;r=i.length)break;c=i[r++],n+=c}while(c===255);o=0;do{if(r>=i.length)break;c=i[r++],o+=c}while(c===255);const l=i.length-r;let h=r;if(ol){re.error(`Malformed SEI payload. ${o} is too small, only ${l} bytes left to parse.`);break}if(n===4){if(i[h++]===181){const d=In(i,h);if(h+=2,d===49){const f=q(i,h);if(h+=4,f===1195456820){const g=i[h++];if(g===3){const y=i[h++],p=31&y,E=64&y,T=E?2+p*3:0,S=new Uint8Array(T);if(E){S[0]=y;for(let v=1;v16){const u=[];for(let g=0;g<16;g++){const y=i[h++].toString(16);u.push(y.length==1?"0"+y:y),(g===3||g===5||g===7||g===9)&&u.push("-")}const d=o-16,f=new Uint8Array(d);for(let g=0;g>24&255,r[1]=s>>16&255,r[2]=s>>8&255,r[3]=s&255,r.set(a,4),i=0,s=8;i0?(r=new Uint8Array(4),e.length>0&&new DataView(r.buffer).setUint32(0,e.length,!1)):r=new Uint8Array;const n=new Uint8Array(4);return t.byteLength>0&&new DataView(n.buffer).setUint32(0,t.byteLength,!1),bo([112,115,115,104],new Uint8Array([s,0,0,0]),a,r,i,n,t)}function Do(a){const e=[];if(a instanceof ArrayBuffer){const t=a.byteLength;let s=0;for(;s+32>>24;if(r!==0&&r!==1)return{offset:t,size:e};const n=a.buffer,o=ye(new Uint8Array(n,t+12,16));let c=null,l=null,h=0;if(r===0)h=28;else{const d=a.getUint32(28);if(!d||s<32+d*16)return{offset:t,size:e};c=[];for(let f=0;f/\(Windows.+Firefox\//i.test(navigator.userAgent),Ct={audio:{a3ds:1,"ac-3":.95,"ac-4":1,alac:.9,alaw:1,dra1:1,"dts+":1,"dts-":1,dtsc:1,dtse:1,dtsh:1,"ec-3":.9,enca:1,fLaC:.9,flac:.9,FLAC:.9,g719:1,g726:1,m4ae:1,mha1:1,mha2:1,mhm1:1,mhm2:1,mlpa:1,mp4a:1,"raw ":1,Opus:1,opus:1,samr:1,sawb:1,sawp:1,sevc:1,sqcp:1,ssmv:1,twos:1,ulaw:1},video:{avc1:1,avc2:1,avc3:1,avc4:1,avcp:1,av01:.8,dav1:.8,drac:1,dva1:1,dvav:1,dvh1:.7,dvhe:.7,encv:1,hev1:.75,hvc1:.75,mjp2:1,mp4v:1,mvc1:1,mvc2:1,mvc3:1,mvc4:1,resv:1,rv60:1,s263:1,svc1:1,svc2:1,"vc-1":1,vp08:1,vp09:.9},text:{stpp:1,wvtt:1}};function Ri(a,e){const t=Ct[e];return!!t&&!!t[a.slice(0,4)]}function $t(a,e,t=!0){return!a.split(",").some(s=>!bi(s,e,t))}function bi(a,e,t=!0){var s;const i=nt(t);return(s=i==null?void 0:i.isTypeSupported(Gt(a,e)))!=null?s:!1}function Gt(a,e){return`${e}/mp4;codecs=${a}`}function nr(a){if(a){const e=a.substring(0,4);return Ct.video[e]}return 2}function ys(a){const e=Cn();return a.split(",").reduce((t,s)=>{const r=e&&_s(s)?9:Ct.video[s];return r?(r*2+t)/(t?3:2):(Ct.audio[s]+t)/(t?2:1)},0)}const Ms={};function Po(a,e=!0){if(Ms[a])return Ms[a];const t={flac:["flac","fLaC","FLAC"],opus:["opus","Opus"],"mp4a.40.34":["mp3"]}[a];for(let i=0;iPo(t.toLowerCase(),e))}function wo(a,e){const t=[];if(a){const s=a.split(",");for(let i=0;i4||["ac-3","ec-3","alac","fLaC","Opus"].indexOf(a)!==-1)&&(ar(a,"audio")||ar(a,"video")))return a;if(e){const t=e.split(",");if(t.length>1){if(a){for(let s=t.length;s--;)if(t[s].substring(0,4)===a.substring(0,4))return t[s]}return t[0]}}return e||a}function ar(a,e){return Ri(a,e)&&bi(a,e)}function Oo(a){const e=a.split(",");for(let t=0;t2&&s[0]==="avc1"&&(e[t]=`avc1.${parseInt(s[1]).toString(16)}${("000"+parseInt(s[2]).toString(16)).slice(-4)}`)}return e.join(",")}function Fo(a){if(a.startsWith("av01.")){const e=a.split("."),t=["0","111","01","01","01","0"];for(let s=e.length;s>4&&s<10;s++)e[s]=t[s-4];return e.join(".")}return a}function or(a){const e=nt(a)||{isTypeSupported:()=>!1};return{mpeg:e.isTypeSupported("audio/mpeg"),mp3:e.isTypeSupported('audio/mp4; codecs="mp3"'),ac3:e.isTypeSupported('audio/mp4; codecs="ac-3"')}}function ni(a){return a.replace(/^.+codecs=["']?([^"']+).*$/,"$1")}const Mo={supported:!0,powerEfficient:!0,smooth:!0},No={supported:!1,smooth:!1,powerEfficient:!1},Pn={supported:!0,configurations:[],decodingInfoResults:[Mo]};function kn(a,e){return{supported:!1,configurations:e,decodingInfoResults:[No],error:a}}function Bo(a,e,t,s,i,r){const n=a.videoCodec,o=a.audioCodec?a.audioGroups:null,c=r==null?void 0:r.audioCodec,l=r==null?void 0:r.channels,h=l?parseInt(l):c?1/0:2;let u=null;if(o!=null&&o.length)try{o.length===1&&o[0]?u=e.groups[o[0]].channels:u=o.reduce((d,f)=>{if(f){const g=e.groups[f];if(!g)throw new Error(`Audio track group ${f} not found`);Object.keys(g.channels).forEach(y=>{d[y]=(d[y]||0)+g.channels[y]})}return d},{2:0})}catch{return!0}return n!==void 0&&(n.split(",").some(d=>_s(d))||a.width>1920&&a.height>1088||a.height>1920&&a.width>1088||a.frameRate>Math.max(s,30)||a.videoRange!=="SDR"&&a.videoRange!==t||a.bitrate>Math.max(i,8e6))||!!u&&N(h)&&Object.keys(u).some(d=>parseInt(d)>h)}function wn(a,e,t,s={}){const i=a.videoCodec;if(!i&&!a.audioCodec||!t)return Promise.resolve(Pn);const r=[],n=Uo(a),o=n.length,c=$o(a,e,o>0),l=c.length;for(let h=o||1*l||1;h--;){const u={type:"media-source"};if(o&&(u.video=n[h%o]),l){u.audio=c[h%l];const d=u.audio.bitrate;u.video&&d&&(u.video.bitrate-=d)}r.push(u)}if(i){const h=navigator.userAgent;if(i.split(",").some(u=>_s(u))&&Cn())return Promise.resolve(kn(new Error(`Overriding Windows Firefox HEVC MediaCapabilities result based on user-agent string: (${h})`),r))}return Promise.all(r.map(h=>{const u=Ko(h);return s[u]||(s[u]=t.decodingInfo(h))})).then(h=>({supported:!h.some(u=>!u.supported),configurations:r,decodingInfoResults:h})).catch(h=>({supported:!1,configurations:r,decodingInfoResults:[],error:h}))}function Uo(a){var e;const t=(e=a.videoCodec)==null?void 0:e.split(","),s=On(a),i=a.width||640,r=a.height||480,n=a.frameRate||30,o=a.videoRange.toLowerCase();return t?t.map(c=>{const l={contentType:Gt(Fo(c),"video"),width:i,height:r,bitrate:s,framerate:n};return o!=="sdr"&&(l.transferFunction=o),l}):[]}function $o(a,e,t){var s;const i=(s=a.audioCodec)==null?void 0:s.split(","),r=On(a);return i&&a.audioGroups?a.audioGroups.reduce((n,o)=>{var c;const l=o?(c=e.groups[o])==null?void 0:c.tracks:null;return l?l.reduce((h,u)=>{if(u.groupId===o){const d=parseFloat(u.channels||"");i.forEach(f=>{const g={contentType:Gt(f,"audio"),bitrate:t?Go(f,r):r};d&&(g.channels=""+d),h.push(g)})}return h},n):n},[]):[]}function Go(a,e){if(e<=1)return 1;let t=128e3;return a==="ec-3"?t=768e3:a==="ac-3"&&(t=64e4),Math.min(e/2,t)}function On(a){return Math.ceil(Math.max(a.bitrate*.9,a.averageBitrate)/1e3)*1e3||1}function Ko(a){let e="";const{audio:t,video:s}=a;if(s){const i=ni(s.contentType);e+=`${i}_r${s.height}x${s.width}f${Math.ceil(s.framerate)}${s.transferFunction||"sd"}_${Math.ceil(s.bitrate/1e5)}`}if(t){const i=ni(t.contentType);e+=`${s?"_":""}${i}_c${t.channels}`}return e}const ai=["NONE","TYPE-0","TYPE-1",null];function Vo(a){return ai.indexOf(a)>-1}const Ts=["SDR","PQ","HLG"];function Ho(a){return!!a&&Ts.indexOf(a)>-1}var hs={No:"",Yes:"YES",v2:"v2"};function lr(a){const{canSkipUntil:e,canSkipDateRanges:t,age:s}=a,i=s!!s).map(s=>s.substring(0,4)).join(","),"supplemental"in e){var t;this.supplemental=e.supplemental;const s=(t=e.supplemental)==null?void 0:t.videoCodec;s&&s!==e.videoCodec&&(this.codecSet+=`,${s.substring(0,4)}`)}this.addGroupId("audio",e.attrs.AUDIO),this.addGroupId("text",e.attrs.SUBTITLES)}get maxBitrate(){return Math.max(this.realBitrate,this.bitrate)}get averageBitrate(){return this._avgBitrate||this.realBitrate||this.bitrate}get attrs(){return this._attrs[0]}get codecs(){return this.attrs.CODECS||""}get pathwayId(){return this.attrs["PATHWAY-ID"]||"."}get videoRange(){return this.attrs["VIDEO-RANGE"]||"SDR"}get score(){return this.attrs.optionalFloat("SCORE",0)}get uri(){return this.url[0]||""}hasAudioGroup(e){return hr(this._audioGroups,e)}hasSubtitleGroup(e){return hr(this._subtitleGroups,e)}get audioGroups(){return this._audioGroups}get subtitleGroups(){return this._subtitleGroups}addGroupId(e,t){if(t){if(e==="audio"){let s=this._audioGroups;s||(s=this._audioGroups=[]),s.indexOf(t)===-1&&s.push(t)}else if(e==="text"){let s=this._subtitleGroups;s||(s=this._subtitleGroups=[]),s.indexOf(t)===-1&&s.push(t)}}}get urlId(){return 0}set urlId(e){}get audioGroupIds(){return this.audioGroups?[this.audioGroupId]:void 0}get textGroupIds(){return this.subtitleGroups?[this.textGroupId]:void 0}get audioGroupId(){var e;return(e=this.audioGroups)==null?void 0:e[0]}get textGroupId(){var e;return(e=this.subtitleGroups)==null?void 0:e[0]}addFallback(){}}function hr(a,e){return!e||!a?!1:a.indexOf(e)!==-1}function Yo(){if(typeof matchMedia=="function"){const a=matchMedia("(dynamic-range: high)"),e=matchMedia("bad query");if(a.media!==e.media)return a.matches===!0}return!1}function Wo(a,e){let t=!1,s=[];if(a&&(t=a!=="SDR",s=[a]),e){s=e.allowedVideoRanges||Ts.slice(0);const i=s.join("")!=="SDR"&&!e.videoCodec;t=e.preferHDR!==void 0?e.preferHDR:i&&Yo(),t||(s=["SDR"])}return{preferHDR:t,allowedVideoRanges:s}}const qo=a=>{const e=new WeakSet;return(t,s)=>{if(a&&(s=a(t,s)),typeof s=="object"&&s!==null){if(e.has(s))return;e.add(s)}return s}},oe=(a,e)=>JSON.stringify(a,qo(e));function jo(a,e,t,s,i){const r=Object.keys(a),n=s==null?void 0:s.channels,o=s==null?void 0:s.audioCodec,c=i==null?void 0:i.videoCodec,l=n&&parseInt(n)===2;let h=!1,u=!1,d=1/0,f=1/0,g=1/0,y=1/0,p=0,E=[];const{preferHDR:T,allowedVideoRanges:S}=Wo(e,i);for(let _=r.length;_--;){const b=a[r[_]];h||(h=b.channels[2]>0),d=Math.min(d,b.minHeight),f=Math.min(f,b.minFramerate),g=Math.min(g,b.minBitrate),S.filter(P=>b.videoRanges[P]>0).length>0&&(u=!0)}d=N(d)?d:0,f=N(f)?f:0;const v=Math.max(1080,d),x=Math.max(30,f);g=N(g)?g:t,t=Math.max(g,t),u||(e=void 0);const D=r.length>1;return{codecSet:r.reduce((_,b)=>{const I=a[b];if(b===_)return _;if(E=u?S.filter(P=>I.videoRanges[P]>0):[],D){if(I.minBitrate>t)return Ue(b,`min bitrate of ${I.minBitrate} > current estimate of ${t}`),_;if(!I.hasDefaultAudio)return Ue(b,"no renditions with default or auto-select sound found"),_;if(o&&b.indexOf(o.substring(0,4))%5!==0)return Ue(b,`audio codec preference "${o}" not found`),_;if(n&&!l){if(!I.channels[n])return Ue(b,`no renditions with ${n} channel sound found (channels options: ${Object.keys(I.channels)})`),_}else if((!o||l)&&h&&I.channels[2]===0)return Ue(b,"no renditions with stereo sound found"),_;if(I.minHeight>v)return Ue(b,`min resolution of ${I.minHeight} > maximum of ${v}`),_;if(I.minFramerate>x)return Ue(b,`min framerate of ${I.minFramerate} > maximum of ${x}`),_;if(!E.some(P=>I.videoRanges[P]>0))return Ue(b,`no variants with VIDEO-RANGE of ${oe(E)} found`),_;if(c&&b.indexOf(c.substring(0,4))%5!==0)return Ue(b,`video codec preference "${c}" not found`),_;if(I.maxScore=ys(_)||I.fragmentError>a[_].fragmentError)?_:(y=I.minIndex,p=I.maxScore,b)},void 0),videoRanges:E,preferHDR:T,minFramerate:f,minBitrate:g,minIndex:y}}function Ue(a,e){re.log(`[abr] start candidates with "${a}" ignored because ${e}`)}function Fn(a){return a.reduce((e,t)=>{let s=e.groups[t.groupId];s||(s=e.groups[t.groupId]={tracks:[],channels:{2:0},hasDefault:!1,hasAutoSelect:!1}),s.tracks.push(t);const i=t.channels||"2";return s.channels[i]=(s.channels[i]||0)+1,s.hasDefault=s.hasDefault||t.default,s.hasAutoSelect=s.hasAutoSelect||t.autoselect,s.hasDefault&&(e.hasDefaultAudio=!0),s.hasAutoSelect&&(e.hasAutoSelectAudio=!0),e},{hasDefaultAudio:!1,hasAutoSelectAudio:!1,groups:{}})}function Xo(a,e,t,s){return a.slice(t,s+1).reduce((i,r,n)=>{if(!r.codecSet)return i;const o=r.audioGroups;let c=i[r.codecSet];c||(i[r.codecSet]=c={minBitrate:1/0,minHeight:1/0,minFramerate:1/0,minIndex:n,maxScore:0,videoRanges:{SDR:0},channels:{2:0},hasDefaultAudio:!o,fragmentError:0}),c.minBitrate=Math.min(c.minBitrate,r.bitrate);const l=Math.min(r.height,r.width);return c.minHeight=Math.min(c.minHeight,l),c.minFramerate=Math.min(c.minFramerate,r.frameRate),c.minIndex=Math.min(c.minIndex,n),c.maxScore=Math.max(c.maxScore,r.score),c.fragmentError+=r.fragmentError,c.videoRanges[r.videoRange]=(c.videoRanges[r.videoRange]||0)+1,o&&o.forEach(h=>{if(!h)return;const u=e.groups[h];u&&(c.hasDefaultAudio=c.hasDefaultAudio||e.hasDefaultAudio?u.hasDefault:u.hasAutoSelect||!e.hasDefaultAudio&&!e.hasAutoSelectAudio,Object.keys(u.channels).forEach(d=>{c.channels[d]=(c.channels[d]||0)+u.channels[d]}))}),i},{})}function ur(a){if(!a)return a;const{lang:e,assocLang:t,characteristics:s,channels:i,audioCodec:r}=a;return{lang:e,assocLang:t,characteristics:s,channels:i,audioCodec:r}}function He(a,e,t){if("attrs"in a){const s=e.indexOf(a);if(s!==-1)return s}for(let s=0;ss.indexOf(i)===-1)}function ft(a,e){const{audioCodec:t,channels:s}=a;return(t===void 0||(e.audioCodec||"").substring(0,4)===t.substring(0,4))&&(s===void 0||s===(e.channels||"2"))}function Zo(a,e,t,s,i){const r=e[s],o=e.reduce((d,f,g)=>{const y=f.uri;return(d[y]||(d[y]=[])).push(g),d},{})[r.uri];o.length>1&&(s=Math.max.apply(Math,o));const c=r.videoRange,l=r.frameRate,h=r.codecSet.substring(0,4),u=dr(e,s,d=>{if(d.videoRange!==c||d.frameRate!==l||d.codecSet.substring(0,4)!==h)return!1;const f=d.audioGroups,g=t.filter(y=>!f||f.indexOf(y.groupId)!==-1);return He(a,g,i)>-1});return u>-1?u:dr(e,s,d=>{const f=d.audioGroups,g=t.filter(y=>!f||f.indexOf(y.groupId)!==-1);return He(a,g,i)>-1})}function dr(a,e,t){for(let s=e;s>-1;s--)if(t(a[s]))return s;for(let s=e+1;s{var s;const{fragCurrent:i,partCurrent:r,hls:n}=this,{autoLevelEnabled:o,media:c}=n;if(!i||!c)return;const l=performance.now(),h=r?r.stats:i.stats,u=r?r.duration:i.duration,d=l-h.loading.start,f=n.minAutoLevel,g=i.level,y=this._nextAutoLevel;if(h.aborted||h.loaded&&h.loaded===h.total||g<=f){this.clearTimer(),this._nextAutoLevel=-1;return}if(!o)return;const p=y>-1&&y!==g,E=!!t||p;if(!E&&(c.paused||!c.playbackRate||!c.readyState))return;const T=n.mainForwardBufferInfo;if(!E&&T===null)return;const S=this.bwEstimator.getEstimateTTFB(),v=Math.abs(c.playbackRate);if(d<=Math.max(S,1e3*(u/(v*2))))return;const x=T?T.len/v:0,D=h.loading.first?h.loading.first-h.loading.start:-1,A=h.loaded&&D>-1,_=this.getBwEstimate(),b=n.levels,I=b[g],P=Math.max(h.loaded,Math.round(u*(i.bitrate||I.averageBitrate)/8));let F=A?d-D:d;F<1&&A&&(F=Math.min(d,h.loaded*8/_));const $=A?h.loaded*1e3/F:0,Y=S/1e3,G=$?(P-h.loaded)/$:P*8/_+Y;if(G<=x)return;const k=$?$*8:_,K=((s=(t==null?void 0:t.details)||this.hls.latestLevelDetails)==null?void 0:s.live)===!0,B=this.hls.config.abrBandWidthUpFactor;let V=Number.POSITIVE_INFINITY,W;for(W=g-1;W>f;W--){const se=b[W].maxBitrate,Q=!b[W].details||K;if(V=this.getTimeToLoadFrag(Y,k,u*se,Q),V=G||V>u*10)return;A?this.bwEstimator.sample(d-Math.min(S,D),h.loaded):this.bwEstimator.sampleTTFB(d);const w=b[W].maxBitrate;this.getBwEstimate()*B>w&&this.resetEstimator(w);const O=this.findBestLevel(w,f,W,0,x,1,1);O>-1&&(W=O),this.warn(`Fragment ${i.sn}${r?" part "+r.index:""} of level ${g} is loading too slowly; +import"./modulepreload-polyfill-B5Qt9EMX.js";import{a as Te,c as Ii,e as k}from"./dom-BvorgAdo.js";function io(a){let e,t=0,s=!1;function i(){const r=location.protocol==="https:"?"wss":"ws";e=new WebSocket(`${r}://${location.host}/ws`),e.addEventListener("open",()=>{t=0}),e.addEventListener("message",n=>{try{a(JSON.parse(n.data))}catch{}}),e.addEventListener("close",()=>{s||(t=Math.min(t+1,6),setTimeout(i,500*2**t))}),e.addEventListener("error",()=>e.close())}return i(),{send(r){(e==null?void 0:e.readyState)===WebSocket.OPEN&&e.send(JSON.stringify(r))},close(){s=!0,e==null||e.close()}}}const B=Number.isFinite||function(a){return typeof a=="number"&&isFinite(a)},ro=Number.isSafeInteger||function(a){return typeof a=="number"&&Math.abs(a)<=no},no=Number.MAX_SAFE_INTEGER||9007199254740991;let Y=function(a){return a.NETWORK_ERROR="networkError",a.MEDIA_ERROR="mediaError",a.KEY_SYSTEM_ERROR="keySystemError",a.MUX_ERROR="muxError",a.OTHER_ERROR="otherError",a}({}),R=function(a){return a.KEY_SYSTEM_NO_KEYS="keySystemNoKeys",a.KEY_SYSTEM_NO_ACCESS="keySystemNoAccess",a.KEY_SYSTEM_NO_SESSION="keySystemNoSession",a.KEY_SYSTEM_NO_CONFIGURED_LICENSE="keySystemNoConfiguredLicense",a.KEY_SYSTEM_LICENSE_REQUEST_FAILED="keySystemLicenseRequestFailed",a.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED="keySystemServerCertificateRequestFailed",a.KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED="keySystemServerCertificateUpdateFailed",a.KEY_SYSTEM_SESSION_UPDATE_FAILED="keySystemSessionUpdateFailed",a.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED="keySystemStatusOutputRestricted",a.KEY_SYSTEM_STATUS_INTERNAL_ERROR="keySystemStatusInternalError",a.KEY_SYSTEM_DESTROY_MEDIA_KEYS_ERROR="keySystemDestroyMediaKeysError",a.KEY_SYSTEM_DESTROY_CLOSE_SESSION_ERROR="keySystemDestroyCloseSessionError",a.KEY_SYSTEM_DESTROY_REMOVE_SESSION_ERROR="keySystemDestroyRemoveSessionError",a.MANIFEST_LOAD_ERROR="manifestLoadError",a.MANIFEST_LOAD_TIMEOUT="manifestLoadTimeOut",a.MANIFEST_PARSING_ERROR="manifestParsingError",a.MANIFEST_INCOMPATIBLE_CODECS_ERROR="manifestIncompatibleCodecsError",a.LEVEL_EMPTY_ERROR="levelEmptyError",a.LEVEL_LOAD_ERROR="levelLoadError",a.LEVEL_LOAD_TIMEOUT="levelLoadTimeOut",a.LEVEL_PARSING_ERROR="levelParsingError",a.LEVEL_SWITCH_ERROR="levelSwitchError",a.AUDIO_TRACK_LOAD_ERROR="audioTrackLoadError",a.AUDIO_TRACK_LOAD_TIMEOUT="audioTrackLoadTimeOut",a.SUBTITLE_LOAD_ERROR="subtitleTrackLoadError",a.SUBTITLE_TRACK_LOAD_TIMEOUT="subtitleTrackLoadTimeOut",a.FRAG_LOAD_ERROR="fragLoadError",a.FRAG_LOAD_TIMEOUT="fragLoadTimeOut",a.FRAG_DECRYPT_ERROR="fragDecryptError",a.FRAG_PARSING_ERROR="fragParsingError",a.FRAG_GAP="fragGap",a.REMUX_ALLOC_ERROR="remuxAllocError",a.KEY_LOAD_ERROR="keyLoadError",a.KEY_LOAD_TIMEOUT="keyLoadTimeOut",a.BUFFER_ADD_CODEC_ERROR="bufferAddCodecError",a.BUFFER_INCOMPATIBLE_CODECS_ERROR="bufferIncompatibleCodecsError",a.BUFFER_APPEND_ERROR="bufferAppendError",a.BUFFER_APPENDING_ERROR="bufferAppendingError",a.BUFFER_STALLED_ERROR="bufferStalledError",a.BUFFER_FULL_ERROR="bufferFullError",a.BUFFER_SEEK_OVER_HOLE="bufferSeekOverHole",a.BUFFER_NUDGE_ON_STALL="bufferNudgeOnStall",a.ASSET_LIST_LOAD_ERROR="assetListLoadError",a.ASSET_LIST_LOAD_TIMEOUT="assetListLoadTimeout",a.ASSET_LIST_PARSING_ERROR="assetListParsingError",a.INTERSTITIAL_ASSET_ITEM_ERROR="interstitialAssetItemError",a.INTERNAL_EXCEPTION="internalException",a.INTERNAL_ABORTED="aborted",a.ATTACH_MEDIA_ERROR="attachMediaError",a.UNKNOWN="unknown",a}({}),m=function(a){return a.MEDIA_ATTACHING="hlsMediaAttaching",a.MEDIA_ATTACHED="hlsMediaAttached",a.MEDIA_DETACHING="hlsMediaDetaching",a.MEDIA_DETACHED="hlsMediaDetached",a.MEDIA_ENDED="hlsMediaEnded",a.STALL_RESOLVED="hlsStallResolved",a.BUFFER_RESET="hlsBufferReset",a.BUFFER_CODECS="hlsBufferCodecs",a.BUFFER_CREATED="hlsBufferCreated",a.BUFFER_APPENDING="hlsBufferAppending",a.BUFFER_APPENDED="hlsBufferAppended",a.BUFFER_EOS="hlsBufferEos",a.BUFFERED_TO_END="hlsBufferedToEnd",a.BUFFER_FLUSHING="hlsBufferFlushing",a.BUFFER_FLUSHED="hlsBufferFlushed",a.MANIFEST_LOADING="hlsManifestLoading",a.MANIFEST_LOADED="hlsManifestLoaded",a.MANIFEST_PARSED="hlsManifestParsed",a.LEVEL_SWITCHING="hlsLevelSwitching",a.LEVEL_SWITCHED="hlsLevelSwitched",a.LEVEL_LOADING="hlsLevelLoading",a.LEVEL_LOADED="hlsLevelLoaded",a.LEVEL_UPDATED="hlsLevelUpdated",a.LEVEL_PTS_UPDATED="hlsLevelPtsUpdated",a.LEVELS_UPDATED="hlsLevelsUpdated",a.AUDIO_TRACKS_UPDATED="hlsAudioTracksUpdated",a.AUDIO_TRACK_SWITCHING="hlsAudioTrackSwitching",a.AUDIO_TRACK_SWITCHED="hlsAudioTrackSwitched",a.AUDIO_TRACK_LOADING="hlsAudioTrackLoading",a.AUDIO_TRACK_LOADED="hlsAudioTrackLoaded",a.AUDIO_TRACK_UPDATED="hlsAudioTrackUpdated",a.SUBTITLE_TRACKS_UPDATED="hlsSubtitleTracksUpdated",a.SUBTITLE_TRACKS_CLEARED="hlsSubtitleTracksCleared",a.SUBTITLE_TRACK_SWITCH="hlsSubtitleTrackSwitch",a.SUBTITLE_TRACK_LOADING="hlsSubtitleTrackLoading",a.SUBTITLE_TRACK_LOADED="hlsSubtitleTrackLoaded",a.SUBTITLE_TRACK_UPDATED="hlsSubtitleTrackUpdated",a.SUBTITLE_FRAG_PROCESSED="hlsSubtitleFragProcessed",a.CUES_PARSED="hlsCuesParsed",a.NON_NATIVE_TEXT_TRACKS_FOUND="hlsNonNativeTextTracksFound",a.INIT_PTS_FOUND="hlsInitPtsFound",a.FRAG_LOADING="hlsFragLoading",a.FRAG_LOAD_EMERGENCY_ABORTED="hlsFragLoadEmergencyAborted",a.FRAG_LOADED="hlsFragLoaded",a.FRAG_DECRYPTED="hlsFragDecrypted",a.FRAG_PARSING_INIT_SEGMENT="hlsFragParsingInitSegment",a.FRAG_PARSING_USERDATA="hlsFragParsingUserdata",a.FRAG_PARSING_METADATA="hlsFragParsingMetadata",a.FRAG_PARSED="hlsFragParsed",a.FRAG_BUFFERED="hlsFragBuffered",a.FRAG_CHANGED="hlsFragChanged",a.FPS_DROP="hlsFpsDrop",a.FPS_DROP_LEVEL_CAPPING="hlsFpsDropLevelCapping",a.MAX_AUTO_LEVEL_UPDATED="hlsMaxAutoLevelUpdated",a.ERROR="hlsError",a.DESTROYING="hlsDestroying",a.KEY_LOADING="hlsKeyLoading",a.KEY_LOADED="hlsKeyLoaded",a.LIVE_BACK_BUFFER_REACHED="hlsLiveBackBufferReached",a.BACK_BUFFER_REACHED="hlsBackBufferReached",a.STEERING_MANIFEST_LOADED="hlsSteeringManifestLoaded",a.ASSET_LIST_LOADING="hlsAssetListLoading",a.ASSET_LIST_LOADED="hlsAssetListLoaded",a.INTERSTITIALS_UPDATED="hlsInterstitialsUpdated",a.INTERSTITIALS_BUFFERED_TO_BOUNDARY="hlsInterstitialsBufferedToBoundary",a.INTERSTITIAL_ASSET_PLAYER_CREATED="hlsInterstitialAssetPlayerCreated",a.INTERSTITIAL_STARTED="hlsInterstitialStarted",a.INTERSTITIAL_ASSET_STARTED="hlsInterstitialAssetStarted",a.INTERSTITIAL_ASSET_ENDED="hlsInterstitialAssetEnded",a.INTERSTITIAL_ASSET_ERROR="hlsInterstitialAssetError",a.INTERSTITIAL_ENDED="hlsInterstitialEnded",a.INTERSTITIALS_PRIMARY_RESUMED="hlsInterstitialsPrimaryResumed",a.PLAYOUT_LIMIT_REACHED="hlsPlayoutLimitReached",a.EVENT_CUE_ENTER="hlsEventCueEnter",a}({});var J={MANIFEST:"manifest",LEVEL:"level",AUDIO_TRACK:"audioTrack",SUBTITLE_TRACK:"subtitleTrack"},U={MAIN:"main",AUDIO:"audio",SUBTITLE:"subtitle"};class Et{constructor(e,t=0,s=0){this.halfLife=void 0,this.alpha_=void 0,this.estimate_=void 0,this.totalWeight_=void 0,this.halfLife=e,this.alpha_=e?Math.exp(Math.log(.5)/e):0,this.estimate_=t,this.totalWeight_=s}sample(e,t){const s=Math.pow(this.alpha_,e);this.estimate_=t*(1-s)+s*this.estimate_,this.totalWeight_+=e}getTotalWeight(){return this.totalWeight_}getEstimate(){if(this.alpha_){const e=1-Math.pow(this.alpha_,this.totalWeight_);if(e)return this.estimate_/e}return this.estimate_}}class ao{constructor(e,t,s,i=100){this.defaultEstimate_=void 0,this.minWeight_=void 0,this.minDelayMs_=void 0,this.slow_=void 0,this.fast_=void 0,this.defaultTTFB_=void 0,this.ttfb_=void 0,this.defaultEstimate_=s,this.minWeight_=.001,this.minDelayMs_=50,this.slow_=new Et(e),this.fast_=new Et(t),this.defaultTTFB_=i,this.ttfb_=new Et(e)}update(e,t){const{slow_:s,fast_:i,ttfb_:r}=this;s.halfLife!==e&&(this.slow_=new Et(e,s.getEstimate(),s.getTotalWeight())),i.halfLife!==t&&(this.fast_=new Et(t,i.getEstimate(),i.getTotalWeight())),r.halfLife!==e&&(this.ttfb_=new Et(e,r.getEstimate(),r.getTotalWeight()))}sample(e,t){e=Math.max(e,this.minDelayMs_);const s=8*t,i=e/1e3,r=s/i;this.fast_.sample(i,r),this.slow_.sample(i,r)}sampleTTFB(e){const t=e/1e3,s=Math.sqrt(2)*Math.exp(-Math.pow(t,2)/2);this.ttfb_.sample(s,Math.max(e,5))}canEstimate(){return this.fast_.getTotalWeight()>=this.minWeight_}getEstimate(){return this.canEstimate()?Math.min(this.fast_.getEstimate(),this.slow_.getEstimate()):this.defaultEstimate_}getEstimateTTFB(){return this.ttfb_.getTotalWeight()>=this.minWeight_?this.ttfb_.getEstimate():this.defaultTTFB_}get defaultEstimate(){return this.defaultEstimate_}destroy(){}}function oo(a,e,t){return(e=co(e))in a?Object.defineProperty(a,e,{value:t,enumerable:!0,configurable:!0,writable:!0}):a[e]=t,a}function ne(){return ne=Object.assign?Object.assign.bind():function(a){for(var e=1;e`):it}function sr(a,e,t){return e[a]?e[a].bind(e):uo(a,t)}const ni=ri();function fo(a,e,t){const s=ri();if(typeof console=="object"&&a===!0||typeof a=="object"){const i=["debug","log","info","warn","error"];i.forEach(r=>{s[r]=sr(r,a,t)});try{s.log(`Debug logs enabled for "${e}" in hls.js version 1.6.16`)}catch{return ri()}i.forEach(r=>{ni[r]=sr(r,a)})}else ne(ni,s);return s}const re=ni;function at(a=!0){return typeof self>"u"?void 0:(a||!self.MediaSource)&&self.ManagedMediaSource||self.MediaSource||self.WebKitMediaSource}function go(a){return typeof self<"u"&&a===self.ManagedMediaSource}function xn(a,e){const t=Object.keys(a),s=Object.keys(e),i=t.length,r=s.length;return!i||!r||i===r&&!t.some(n=>s.indexOf(n)===-1)}function De(a,e=!1){if(typeof TextDecoder<"u"){const l=new TextDecoder("utf-8").decode(a);if(e){const h=l.indexOf("\0");return h!==-1?l.substring(0,h):l}return l.replace(/\0/g,"")}const t=a.length;let s,i,r,n="",o=0;for(;o>4){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:n+=String.fromCharCode(s);break;case 12:case 13:i=a[o++],n+=String.fromCharCode((s&31)<<6|i&63);break;case 14:i=a[o++],r=a[o++],n+=String.fromCharCode((s&15)<<12|(i&63)<<6|(r&63)<<0);break}}return n}function ye(a){let e="";for(let t=0;t1||i===1&&(t=this.levelkeys[s[0]])!=null&&t.encrypted)return!0}return!1}get programDateTime(){return this._programDateTime===null&&this.rawProgramDateTime&&(this.programDateTime=Date.parse(this.rawProgramDateTime)),this._programDateTime}set programDateTime(e){if(!B(e)){this._programDateTime=this.rawProgramDateTime=null;return}this._programDateTime=e}get ref(){return de(this)?(this._ref||(this._ref={base:this.base,start:this.start,duration:this.duration,sn:this.sn,programDateTime:this.programDateTime}),this._ref):null}addStart(e){this.setStart(this.start+e)}setStart(e){this.start=e,this._ref&&(this._ref.start=e)}setDuration(e){this.duration=e,this._ref&&(this._ref.duration=e)}setKeyFormat(e){const t=this.levelkeys;if(t){var s;const i=t[e];i&&!((s=this._decryptdata)!=null&&s.keyId)&&(this._decryptdata=i.getDecryptData(this.sn,t))}}abortRequests(){var e,t;(e=this.loader)==null||e.abort(),(t=this.keyLoader)==null||t.abort()}setElementaryStreamInfo(e,t,s,i,r,n=!1){const{elementaryStreams:o}=this,c=o[e];if(!c){o[e]={startPTS:t,endPTS:s,startDTS:i,endDTS:r,partial:n};return}c.startPTS=Math.min(c.startPTS,t),c.endPTS=Math.max(c.endPTS,s),c.startDTS=Math.min(c.startDTS,i),c.endDTS=Math.max(c.endDTS,r)}}class yo extends In{constructor(e,t,s,i,r){super(s),this.fragOffset=0,this.duration=0,this.gap=!1,this.independent=!1,this.relurl=void 0,this.fragment=void 0,this.index=void 0,this.duration=e.decimalFloatingPoint("DURATION"),this.gap=e.bool("GAP"),this.independent=e.bool("INDEPENDENT"),this.relurl=e.enumeratedString("URI"),this.fragment=t,this.index=i;const n=e.enumeratedString("BYTERANGE");n&&this.setByteRange(n,r),r&&(this.fragOffset=r.fragOffset+r.duration)}get start(){return this.fragment.start+this.fragOffset}get end(){return this.start+this.duration}get loaded(){const{elementaryStreams:e}=this;return!!(e.audio||e.video||e.audiovideo)}}function Ln(a,e){const t=Object.getPrototypeOf(a);if(t){const s=Object.getOwnPropertyDescriptor(t,e);return s||Ln(t,e)}}function Eo(a,e){const t=Ln(a,e);t&&(t.enumerable=!0,Object.defineProperty(a,e,t))}const rr=Math.pow(2,32)-1,To=[].push,Rn={video:1,audio:2,id3:3,text:4};function ue(a){return String.fromCharCode.apply(null,a)}function bn(a,e){const t=a[e]<<8|a[e+1];return t<0?65536+t:t}function j(a,e){const t=_n(a,e);return t<0?4294967296+t:t}function nr(a,e){let t=j(a,e);return t*=Math.pow(2,32),t+=j(a,e+4),t}function _n(a,e){return a[e]<<24|a[e+1]<<16|a[e+2]<<8|a[e+3]}function So(a){const e=a.byteLength;for(let t=0;t8&&a[t+4]===109&&a[t+5]===111&&a[t+6]===111&&a[t+7]===102)return!0;t=s>1?t+s:e}return!1}function Z(a,e){const t=[];if(!e.length)return t;const s=a.byteLength;for(let i=0;i1?i+r:s;if(n===e[0])if(e.length===1)t.push(a.subarray(i+8,o));else{const c=Z(a.subarray(i+8,o),e.slice(1));c.length&&To.apply(t,c)}i=o}return t}function vo(a){const e=[],t=a[0];let s=8;const i=j(a,s);s+=4;let r=0,n=0;t===0?(r=j(a,s),n=j(a,s+4),s+=8):(r=nr(a,s),n=nr(a,s+8),s+=16),s+=2;let o=a.length+n;const c=bn(a,s);s+=2;for(let l=0;l>>31===1)return re.warn("SIDX has hierarchical references (not supported)"),null;const g=j(a,h);h+=4,e.push({referenceSize:u,subsegmentDuration:g,info:{duration:g/i,start:o,end:o+u-1}}),o+=u,h+=4,s=h}return{earliestPresentationTime:r,timescale:i,version:t,referencesCount:c,references:e}}function Dn(a){const e=[],t=Z(a,["moov","trak"]);for(let i=0;i{const r=j(i,4),n=e[r];n&&(n.default={duration:j(i,12),flags:j(i,20)})}),e}function xo(a){const e=a.subarray(8),t=e.subarray(86),s=ue(e.subarray(4,8));let i=s,r;const n=s==="enca"||s==="encv";if(n){const l=Z(e,[s])[0].subarray(s==="enca"?28:78);Z(l,["sinf"]).forEach(d=>{const u=Z(d,["schm"])[0];if(u){const f=ue(u.subarray(4,8));if(f==="cbcs"||f==="cenc"){const g=Z(d,["frma"])[0];g&&(i=ue(g))}}})}const o=i;switch(i){case"avc1":case"avc2":case"avc3":case"avc4":{const c=Z(t,["avcC"])[0];c&&c.length>3&&(i+="."+Qt(c[1])+Qt(c[2])+Qt(c[3]),r=zt(o==="avc1"?"dva1":"dvav",t));break}case"mp4a":{const c=Z(e,[s])[0],l=Z(c.subarray(28),["esds"])[0];if(l&&l.length>7){let h=4;if(l[h++]!==3)break;h=Ms(l,h),h+=2;const d=l[h++];if(d&128&&(h+=2),d&64&&(h+=l[h++]),l[h++]!==4)break;h=Ms(l,h);const u=l[h++];if(u===64)i+="."+Qt(u);else break;if(h+=12,l[h++]!==5)break;h=Ms(l,h);const f=l[h++];let g=(f&248)>>3;g===31&&(g+=1+((f&7)<<3)+((l[h]&224)>>5)),i+="."+g}break}case"hvc1":case"hev1":{const c=Z(t,["hvcC"])[0];if(c&&c.length>12){const l=c[1],h=["","A","B","C"][l>>6],d=l&31,u=j(c,2),f=(l&32)>>5?"H":"L",g=c[12],y=c.subarray(6,12);i+="."+h+d,i+="."+Ao(u).toString(16).toUpperCase(),i+="."+f+g;let p="";for(let E=y.length;E--;){const T=y[E];(T||p)&&(p="."+T.toString(16).toUpperCase()+p)}i+=p}r=zt(o=="hev1"?"dvhe":"dvh1",t);break}case"dvh1":case"dvhe":case"dvav":case"dva1":case"dav1":{i=zt(i,t)||i;break}case"vp09":{const c=Z(t,["vpcC"])[0];if(c&&c.length>6){const l=c[4],h=c[5],d=c[6]>>4&15;i+="."+Ke(l)+"."+Ke(h)+"."+Ke(d)}break}case"av01":{const c=Z(t,["av1C"])[0];if(c&&c.length>2){const l=c[1]>>>5,h=c[1]&31,d=c[2]>>>7?"H":"M",u=(c[2]&64)>>6,f=(c[2]&32)>>5,g=l===2&&u?f?12:10:u?10:8,y=(c[2]&16)>>4,p=(c[2]&8)>>3,E=(c[2]&4)>>2,T=c[2]&3;i+="."+l+"."+Ke(h)+d+"."+Ke(g)+"."+y+"."+p+E+T+"."+Ke(1)+"."+Ke(1)+"."+Ke(1)+"."+0,r=zt("dav1",t)}break}}return{codec:i,encrypted:n,supplemental:r}}function zt(a,e){const t=Z(e,["dvvC"]),s=t.length?t[0]:Z(e,["dvcC"])[0];if(s){const i=s[2]>>1&127,r=s[2]<<5&32|s[3]>>3&31;return a+"."+Ke(i)+"."+Ke(r)}}function Ao(a){let e=0;for(let t=0;t<32;t++)e|=(a>>t&1)<<31-t;return e>>>0}function Ms(a,e){const t=e+5;for(;a[e++]&128&&e{const r=s.subarray(8,24);r.some(n=>n!==0)||(re.log(`[eme] Patching keyId in 'enc${i?"a":"v"}>sinf>>tenc' box: ${ye(r)} -> ${ye(t)}`),s.set(t,8))})}function Lo(a){const e=[];return Cn(a,t=>e.push(t.subarray(8,24))),e}function Cn(a,e){Z(a,["moov","trak"]).forEach(s=>{const i=Z(s,["mdia","minf","stbl","stsd"])[0];if(!i)return;const r=i.subarray(8);let n=Z(r,["enca"]);const o=n.length>0;o||(n=Z(r,["encv"])),n.forEach(c=>{const l=o?c.subarray(28):c.subarray(78);Z(l,["sinf"]).forEach(d=>{const u=Pn(d);u&&e(u,o)})})})}function Pn(a){const e=Z(a,["schm"])[0];if(e){const t=ue(e.subarray(4,8));if(t==="cbcs"||t==="cenc"){const s=Z(a,["schi","tenc"])[0];if(s)return s}}}function Ro(a,e,t){const s={},i=Z(a,["moof","traf"]);for(let r=0;rs[r].duration)){let r=1/0,n=0;const o=Z(a,["sidx"]);for(let c=0;cd+u.info.duration||0,0);n=Math.max(n,h+l.earliestPresentationTime/l.timescale)}}n&&B(n)&&Object.keys(s).forEach(c=>{s[c].duration||(s[c].duration=n*s[c].timescale-s[c].start)})}return s}function bo(a){const e={valid:null,remainder:null},t=Z(a,["moof"]);if(t.length<2)return e.remainder=a,e;const s=t[t.length-1];return e.valid=a.slice(0,s.byteOffset-8),e.remainder=a.slice(s.byteOffset-8),e}function we(a,e){const t=new Uint8Array(a.length+e.length);return t.set(a),t.set(e,a.length),t}function ar(a,e){const t=[],s=e.samples,i=e.timescale,r=e.id;let n=!1;return Z(s,["moof"]).map(c=>{const l=c.byteOffset-8;Z(c,["traf"]).map(d=>{const u=Z(d,["tfdt"]).map(f=>{const g=f[0];let y=j(f,4);return g===1&&(y*=Math.pow(2,32),y+=j(f,8)),y/i})[0];return u!==void 0&&(a=u),Z(d,["tfhd"]).map(f=>{const g=j(f,4),y=j(f,0)&16777215,p=(y&1)!==0,E=(y&2)!==0,T=(y&8)!==0;let S=0;const v=(y&16)!==0;let x=0;const D=(y&32)!==0;let A=8;g===r&&(p&&(A+=8),E&&(A+=4),T&&(S=j(f,A),A+=4),v&&(x=j(f,A),A+=4),D&&(A+=4),e.type==="video"&&(n=Ds(e.codec)),Z(d,["trun"]).map(_=>{const b=_[0],I=j(_,0)&16777215,P=(I&1)!==0;let M=0;const G=(I&4)!==0,W=(I&256)!==0;let K=0;const w=(I&512)!==0;let V=0;const $=(I&1024)!==0,H=(I&2048)!==0;let q=0;const O=j(_,4);let F=8;P&&(M=j(_,F),F+=4),G&&(F+=4);let z=M+l;for(let se=0;se>1&63;return t===39||t===40}else return(e&31)===6}function bi(a,e,t,s){const i=kn(a);let r=0;r+=e;let n=0,o=0,c=0;for(;r=i.length)break;c=i[r++],n+=c}while(c===255);o=0;do{if(r>=i.length)break;c=i[r++],o+=c}while(c===255);const l=i.length-r;let h=r;if(ol){re.error(`Malformed SEI payload. ${o} is too small, only ${l} bytes left to parse.`);break}if(n===4){if(i[h++]===181){const u=bn(i,h);if(h+=2,u===49){const f=j(i,h);if(h+=4,f===1195456820){const g=i[h++];if(g===3){const y=i[h++],p=31&y,E=64&y,T=E?2+p*3:0,S=new Uint8Array(T);if(E){S[0]=y;for(let v=1;v16){const d=[];for(let g=0;g<16;g++){const y=i[h++].toString(16);d.push(y.length==1?"0"+y:y),(g===3||g===5||g===7||g===9)&&d.push("-")}const u=o-16,f=new Uint8Array(u);for(let g=0;g>24&255,r[1]=s>>16&255,r[2]=s>>8&255,r[3]=s&255,r.set(a,4),i=0,s=8;i0?(r=new Uint8Array(4),e.length>0&&new DataView(r.buffer).setUint32(0,e.length,!1)):r=new Uint8Array;const n=new Uint8Array(4);return t.byteLength>0&&new DataView(n.buffer).setUint32(0,t.byteLength,!1),Co([112,115,115,104],new Uint8Array([s,0,0,0]),a,r,i,n,t)}function ko(a){const e=[];if(a instanceof ArrayBuffer){const t=a.byteLength;let s=0;for(;s+32>>24;if(r!==0&&r!==1)return{offset:t,size:e};const n=a.buffer,o=ye(new Uint8Array(n,t+12,16));let c=null,l=null,h=0;if(r===0)h=28;else{const u=a.getUint32(28);if(!u||s<32+u*16)return{offset:t,size:e};c=[];for(let f=0;f/\(Windows.+Firefox\//i.test(navigator.userAgent),Pt={audio:{a3ds:1,"ac-3":.95,"ac-4":1,alac:.9,alaw:1,dra1:1,"dts+":1,"dts-":1,dtsc:1,dtse:1,dtsh:1,"ec-3":.9,enca:1,fLaC:.9,flac:.9,FLAC:.9,g719:1,g726:1,m4ae:1,mha1:1,mha2:1,mhm1:1,mhm2:1,mlpa:1,mp4a:1,"raw ":1,Opus:1,opus:1,samr:1,sawb:1,sawp:1,sevc:1,sqcp:1,ssmv:1,twos:1,ulaw:1},video:{avc1:1,avc2:1,avc3:1,avc4:1,avcp:1,av01:.8,dav1:.8,drac:1,dva1:1,dvav:1,dvh1:.7,dvhe:.7,encv:1,hev1:.75,hvc1:.75,mjp2:1,mp4v:1,mvc1:1,mvc2:1,mvc3:1,mvc4:1,resv:1,rv60:1,s263:1,svc1:1,svc2:1,"vc-1":1,vp08:1,vp09:.9},text:{stpp:1,wvtt:1}};function _i(a,e){const t=Pt[e];return!!t&&!!t[a.slice(0,4)]}function Ut(a,e,t=!0){return!a.split(",").some(s=>!Di(s,e,t))}function Di(a,e,t=!0){var s;const i=at(t);return(s=i==null?void 0:i.isTypeSupported(Gt(a,e)))!=null?s:!1}function Gt(a,e){return`${e}/mp4;codecs=${a}`}function or(a){if(a){const e=a.substring(0,4);return Pt.video[e]}return 2}function Es(a){const e=wn();return a.split(",").reduce((t,s)=>{const r=e&&Ds(s)?9:Pt.video[s];return r?(r*2+t)/(t?3:2):(Pt.audio[s]+t)/(t?2:1)},0)}const Ns={};function Oo(a,e=!0){if(Ns[a])return Ns[a];const t={flac:["flac","fLaC","FLAC"],opus:["opus","Opus"],"mp4a.40.34":["mp3"]}[a];for(let i=0;iOo(t.toLowerCase(),e))}function Mo(a,e){const t=[];if(a){const s=a.split(",");for(let i=0;i4||["ac-3","ec-3","alac","fLaC","Opus"].indexOf(a)!==-1)&&(lr(a,"audio")||lr(a,"video")))return a;if(e){const t=e.split(",");if(t.length>1){if(a){for(let s=t.length;s--;)if(t[s].substring(0,4)===a.substring(0,4))return t[s]}return t[0]}}return e||a}function lr(a,e){return _i(a,e)&&Di(a,e)}function No(a){const e=a.split(",");for(let t=0;t2&&s[0]==="avc1"&&(e[t]=`avc1.${parseInt(s[1]).toString(16)}${("000"+parseInt(s[2]).toString(16)).slice(-4)}`)}return e.join(",")}function Bo(a){if(a.startsWith("av01.")){const e=a.split("."),t=["0","111","01","01","01","0"];for(let s=e.length;s>4&&s<10;s++)e[s]=t[s-4];return e.join(".")}return a}function cr(a){const e=at(a)||{isTypeSupported:()=>!1};return{mpeg:e.isTypeSupported("audio/mpeg"),mp3:e.isTypeSupported('audio/mp4; codecs="mp3"'),ac3:e.isTypeSupported('audio/mp4; codecs="ac-3"')}}function ai(a){return a.replace(/^.+codecs=["']?([^"']+).*$/,"$1")}const $o={supported:!0,powerEfficient:!0,smooth:!0},Uo={supported:!1,smooth:!1,powerEfficient:!1},On={supported:!0,configurations:[],decodingInfoResults:[$o]};function Fn(a,e){return{supported:!1,configurations:e,decodingInfoResults:[Uo],error:a}}function Go(a,e,t,s,i,r){const n=a.videoCodec,o=a.audioCodec?a.audioGroups:null,c=r==null?void 0:r.audioCodec,l=r==null?void 0:r.channels,h=l?parseInt(l):c?1/0:2;let d=null;if(o!=null&&o.length)try{o.length===1&&o[0]?d=e.groups[o[0]].channels:d=o.reduce((u,f)=>{if(f){const g=e.groups[f];if(!g)throw new Error(`Audio track group ${f} not found`);Object.keys(g.channels).forEach(y=>{u[y]=(u[y]||0)+g.channels[y]})}return u},{2:0})}catch{return!0}return n!==void 0&&(n.split(",").some(u=>Ds(u))||a.width>1920&&a.height>1088||a.height>1920&&a.width>1088||a.frameRate>Math.max(s,30)||a.videoRange!=="SDR"&&a.videoRange!==t||a.bitrate>Math.max(i,8e6))||!!d&&B(h)&&Object.keys(d).some(u=>parseInt(u)>h)}function Mn(a,e,t,s={}){const i=a.videoCodec;if(!i&&!a.audioCodec||!t)return Promise.resolve(On);const r=[],n=Ko(a),o=n.length,c=Vo(a,e,o>0),l=c.length;for(let h=o||1*l||1;h--;){const d={type:"media-source"};if(o&&(d.video=n[h%o]),l){d.audio=c[h%l];const u=d.audio.bitrate;d.video&&u&&(d.video.bitrate-=u)}r.push(d)}if(i){const h=navigator.userAgent;if(i.split(",").some(d=>Ds(d))&&wn())return Promise.resolve(Fn(new Error(`Overriding Windows Firefox HEVC MediaCapabilities result based on user-agent string: (${h})`),r))}return Promise.all(r.map(h=>{const d=Yo(h);return s[d]||(s[d]=t.decodingInfo(h))})).then(h=>({supported:!h.some(d=>!d.supported),configurations:r,decodingInfoResults:h})).catch(h=>({supported:!1,configurations:r,decodingInfoResults:[],error:h}))}function Ko(a){var e;const t=(e=a.videoCodec)==null?void 0:e.split(","),s=Nn(a),i=a.width||640,r=a.height||480,n=a.frameRate||30,o=a.videoRange.toLowerCase();return t?t.map(c=>{const l={contentType:Gt(Bo(c),"video"),width:i,height:r,bitrate:s,framerate:n};return o!=="sdr"&&(l.transferFunction=o),l}):[]}function Vo(a,e,t){var s;const i=(s=a.audioCodec)==null?void 0:s.split(","),r=Nn(a);return i&&a.audioGroups?a.audioGroups.reduce((n,o)=>{var c;const l=o?(c=e.groups[o])==null?void 0:c.tracks:null;return l?l.reduce((h,d)=>{if(d.groupId===o){const u=parseFloat(d.channels||"");i.forEach(f=>{const g={contentType:Gt(f,"audio"),bitrate:t?Ho(f,r):r};u&&(g.channels=""+u),h.push(g)})}return h},n):n},[]):[]}function Ho(a,e){if(e<=1)return 1;let t=128e3;return a==="ec-3"?t=768e3:a==="ac-3"&&(t=64e4),Math.min(e/2,t)}function Nn(a){return Math.ceil(Math.max(a.bitrate*.9,a.averageBitrate)/1e3)*1e3||1}function Yo(a){let e="";const{audio:t,video:s}=a;if(s){const i=ai(s.contentType);e+=`${i}_r${s.height}x${s.width}f${Math.ceil(s.framerate)}${s.transferFunction||"sd"}_${Math.ceil(s.bitrate/1e5)}`}if(t){const i=ai(t.contentType);e+=`${s?"_":""}${i}_c${t.channels}`}return e}const oi=["NONE","TYPE-0","TYPE-1",null];function Wo(a){return oi.indexOf(a)>-1}const Ss=["SDR","PQ","HLG"];function qo(a){return!!a&&Ss.indexOf(a)>-1}var ds={No:"",Yes:"YES",v2:"v2"};function hr(a){const{canSkipUntil:e,canSkipDateRanges:t,age:s}=a,i=s!!s).map(s=>s.substring(0,4)).join(","),"supplemental"in e){var t;this.supplemental=e.supplemental;const s=(t=e.supplemental)==null?void 0:t.videoCodec;s&&s!==e.videoCodec&&(this.codecSet+=`,${s.substring(0,4)}`)}this.addGroupId("audio",e.attrs.AUDIO),this.addGroupId("text",e.attrs.SUBTITLES)}get maxBitrate(){return Math.max(this.realBitrate,this.bitrate)}get averageBitrate(){return this._avgBitrate||this.realBitrate||this.bitrate}get attrs(){return this._attrs[0]}get codecs(){return this.attrs.CODECS||""}get pathwayId(){return this.attrs["PATHWAY-ID"]||"."}get videoRange(){return this.attrs["VIDEO-RANGE"]||"SDR"}get score(){return this.attrs.optionalFloat("SCORE",0)}get uri(){return this.url[0]||""}hasAudioGroup(e){return ur(this._audioGroups,e)}hasSubtitleGroup(e){return ur(this._subtitleGroups,e)}get audioGroups(){return this._audioGroups}get subtitleGroups(){return this._subtitleGroups}addGroupId(e,t){if(t){if(e==="audio"){let s=this._audioGroups;s||(s=this._audioGroups=[]),s.indexOf(t)===-1&&s.push(t)}else if(e==="text"){let s=this._subtitleGroups;s||(s=this._subtitleGroups=[]),s.indexOf(t)===-1&&s.push(t)}}}get urlId(){return 0}set urlId(e){}get audioGroupIds(){return this.audioGroups?[this.audioGroupId]:void 0}get textGroupIds(){return this.subtitleGroups?[this.textGroupId]:void 0}get audioGroupId(){var e;return(e=this.audioGroups)==null?void 0:e[0]}get textGroupId(){var e;return(e=this.subtitleGroups)==null?void 0:e[0]}addFallback(){}}function ur(a,e){return!e||!a?!1:a.indexOf(e)!==-1}function jo(){if(typeof matchMedia=="function"){const a=matchMedia("(dynamic-range: high)"),e=matchMedia("bad query");if(a.media!==e.media)return a.matches===!0}return!1}function Xo(a,e){let t=!1,s=[];if(a&&(t=a!=="SDR",s=[a]),e){s=e.allowedVideoRanges||Ss.slice(0);const i=s.join("")!=="SDR"&&!e.videoCodec;t=e.preferHDR!==void 0?e.preferHDR:i&&jo(),t||(s=["SDR"])}return{preferHDR:t,allowedVideoRanges:s}}const zo=a=>{const e=new WeakSet;return(t,s)=>{if(a&&(s=a(t,s)),typeof s=="object"&&s!==null){if(e.has(s))return;e.add(s)}return s}},oe=(a,e)=>JSON.stringify(a,zo(e));function Qo(a,e,t,s,i){const r=Object.keys(a),n=s==null?void 0:s.channels,o=s==null?void 0:s.audioCodec,c=i==null?void 0:i.videoCodec,l=n&&parseInt(n)===2;let h=!1,d=!1,u=1/0,f=1/0,g=1/0,y=1/0,p=0,E=[];const{preferHDR:T,allowedVideoRanges:S}=Xo(e,i);for(let _=r.length;_--;){const b=a[r[_]];h||(h=b.channels[2]>0),u=Math.min(u,b.minHeight),f=Math.min(f,b.minFramerate),g=Math.min(g,b.minBitrate),S.filter(P=>b.videoRanges[P]>0).length>0&&(d=!0)}u=B(u)?u:0,f=B(f)?f:0;const v=Math.max(1080,u),x=Math.max(30,f);g=B(g)?g:t,t=Math.max(g,t),d||(e=void 0);const D=r.length>1;return{codecSet:r.reduce((_,b)=>{const I=a[b];if(b===_)return _;if(E=d?S.filter(P=>I.videoRanges[P]>0):[],D){if(I.minBitrate>t)return Ue(b,`min bitrate of ${I.minBitrate} > current estimate of ${t}`),_;if(!I.hasDefaultAudio)return Ue(b,"no renditions with default or auto-select sound found"),_;if(o&&b.indexOf(o.substring(0,4))%5!==0)return Ue(b,`audio codec preference "${o}" not found`),_;if(n&&!l){if(!I.channels[n])return Ue(b,`no renditions with ${n} channel sound found (channels options: ${Object.keys(I.channels)})`),_}else if((!o||l)&&h&&I.channels[2]===0)return Ue(b,"no renditions with stereo sound found"),_;if(I.minHeight>v)return Ue(b,`min resolution of ${I.minHeight} > maximum of ${v}`),_;if(I.minFramerate>x)return Ue(b,`min framerate of ${I.minFramerate} > maximum of ${x}`),_;if(!E.some(P=>I.videoRanges[P]>0))return Ue(b,`no variants with VIDEO-RANGE of ${oe(E)} found`),_;if(c&&b.indexOf(c.substring(0,4))%5!==0)return Ue(b,`video codec preference "${c}" not found`),_;if(I.maxScore=Es(_)||I.fragmentError>a[_].fragmentError)?_:(y=I.minIndex,p=I.maxScore,b)},void 0),videoRanges:E,preferHDR:T,minFramerate:f,minBitrate:g,minIndex:y}}function Ue(a,e){re.log(`[abr] start candidates with "${a}" ignored because ${e}`)}function Bn(a){return a.reduce((e,t)=>{let s=e.groups[t.groupId];s||(s=e.groups[t.groupId]={tracks:[],channels:{2:0},hasDefault:!1,hasAutoSelect:!1}),s.tracks.push(t);const i=t.channels||"2";return s.channels[i]=(s.channels[i]||0)+1,s.hasDefault=s.hasDefault||t.default,s.hasAutoSelect=s.hasAutoSelect||t.autoselect,s.hasDefault&&(e.hasDefaultAudio=!0),s.hasAutoSelect&&(e.hasAutoSelectAudio=!0),e},{hasDefaultAudio:!1,hasAutoSelectAudio:!1,groups:{}})}function Zo(a,e,t,s){return a.slice(t,s+1).reduce((i,r,n)=>{if(!r.codecSet)return i;const o=r.audioGroups;let c=i[r.codecSet];c||(i[r.codecSet]=c={minBitrate:1/0,minHeight:1/0,minFramerate:1/0,minIndex:n,maxScore:0,videoRanges:{SDR:0},channels:{2:0},hasDefaultAudio:!o,fragmentError:0}),c.minBitrate=Math.min(c.minBitrate,r.bitrate);const l=Math.min(r.height,r.width);return c.minHeight=Math.min(c.minHeight,l),c.minFramerate=Math.min(c.minFramerate,r.frameRate),c.minIndex=Math.min(c.minIndex,n),c.maxScore=Math.max(c.maxScore,r.score),c.fragmentError+=r.fragmentError,c.videoRanges[r.videoRange]=(c.videoRanges[r.videoRange]||0)+1,o&&o.forEach(h=>{if(!h)return;const d=e.groups[h];d&&(c.hasDefaultAudio=c.hasDefaultAudio||e.hasDefaultAudio?d.hasDefault:d.hasAutoSelect||!e.hasDefaultAudio&&!e.hasAutoSelectAudio,Object.keys(d.channels).forEach(u=>{c.channels[u]=(c.channels[u]||0)+d.channels[u]}))}),i},{})}function fr(a){if(!a)return a;const{lang:e,assocLang:t,characteristics:s,channels:i,audioCodec:r}=a;return{lang:e,assocLang:t,characteristics:s,channels:i,audioCodec:r}}function We(a,e,t){if("attrs"in a){const s=e.indexOf(a);if(s!==-1)return s}for(let s=0;ss.indexOf(i)===-1)}function ft(a,e){const{audioCodec:t,channels:s}=a;return(t===void 0||(e.audioCodec||"").substring(0,4)===t.substring(0,4))&&(s===void 0||s===(e.channels||"2"))}function tl(a,e,t,s,i){const r=e[s],o=e.reduce((u,f,g)=>{const y=f.uri;return(u[y]||(u[y]=[])).push(g),u},{})[r.uri];o.length>1&&(s=Math.max.apply(Math,o));const c=r.videoRange,l=r.frameRate,h=r.codecSet.substring(0,4),d=gr(e,s,u=>{if(u.videoRange!==c||u.frameRate!==l||u.codecSet.substring(0,4)!==h)return!1;const f=u.audioGroups,g=t.filter(y=>!f||f.indexOf(y.groupId)!==-1);return We(a,g,i)>-1});return d>-1?d:gr(e,s,u=>{const f=u.audioGroups,g=t.filter(y=>!f||f.indexOf(y.groupId)!==-1);return We(a,g,i)>-1})}function gr(a,e,t){for(let s=e;s>-1;s--)if(t(a[s]))return s;for(let s=e+1;s{var s;const{fragCurrent:i,partCurrent:r,hls:n}=this,{autoLevelEnabled:o,media:c}=n;if(!i||!c)return;const l=performance.now(),h=r?r.stats:i.stats,d=r?r.duration:i.duration,u=l-h.loading.start,f=n.minAutoLevel,g=i.level,y=this._nextAutoLevel;if(h.aborted||h.loaded&&h.loaded===h.total||g<=f){this.clearTimer(),this._nextAutoLevel=-1;return}if(!o)return;const p=y>-1&&y!==g,E=!!t||p;if(!E&&(c.paused||!c.playbackRate||!c.readyState))return;const T=n.mainForwardBufferInfo;if(!E&&T===null)return;const S=this.bwEstimator.getEstimateTTFB(),v=Math.abs(c.playbackRate);if(u<=Math.max(S,1e3*(d/(v*2))))return;const x=T?T.len/v:0,D=h.loading.first?h.loading.first-h.loading.start:-1,A=h.loaded&&D>-1,_=this.getBwEstimate(),b=n.levels,I=b[g],P=Math.max(h.loaded,Math.round(d*(i.bitrate||I.averageBitrate)/8));let M=A?u-D:u;M<1&&A&&(M=Math.min(u,h.loaded*8/_));const G=A?h.loaded*1e3/M:0,W=S/1e3,K=G?(P-h.loaded)/G:P*8/_+W;if(K<=x)return;const w=G?G*8:_,V=((s=(t==null?void 0:t.details)||this.hls.latestLevelDetails)==null?void 0:s.live)===!0,$=this.hls.config.abrBandWidthUpFactor;let H=Number.POSITIVE_INFINITY,q;for(q=g-1;q>f;q--){const se=b[q].maxBitrate,Q=!b[q].details||V;if(H=this.getTimeToLoadFrag(W,w,d*se,Q),H=K||H>d*10)return;A?this.bwEstimator.sample(u-Math.min(S,D),h.loaded):this.bwEstimator.sampleTTFB(u);const O=b[q].maxBitrate;this.getBwEstimate()*$>O&&this.resetEstimator(O);const F=this.findBestLevel(O,f,q,0,x,1,1);F>-1&&(q=F),this.warn(`Fragment ${i.sn}${r?" part "+r.index:""} of level ${g} is loading too slowly; Fragment duration: ${i.duration.toFixed(3)} Time to underbuffer: ${x.toFixed(3)} s - Estimated load time for current fragment: ${G.toFixed(3)} s - Estimated load time for down switch fragment: ${V.toFixed(3)} s + Estimated load time for current fragment: ${K.toFixed(3)} s + Estimated load time for down switch fragment: ${H.toFixed(3)} s TTFB estimate: ${D|0} ms - Current BW estimate: ${N(_)?_|0:"Unknown"} bps + Current BW estimate: ${B(_)?_|0:"Unknown"} bps New BW estimate: ${this.getBwEstimate()|0} bps - Switching to level ${W} @ ${w|0} bps`),n.nextLoadLevel=n.nextAutoLevel=W,this.clearTimer();const z=()=>{if(this.clearTimer(),this.fragCurrent===i&&this.hls.loadLevel===W&&W>0){const se=this.getStarvationDelay();if(this.warn(`Aborting inflight request ${W>0?"and switching down":""} + Switching to level ${q} @ ${O|0} bps`),n.nextLoadLevel=n.nextAutoLevel=q,this.clearTimer();const z=()=>{if(this.clearTimer(),this.fragCurrent===i&&this.hls.loadLevel===q&&q>0){const se=this.getStarvationDelay();if(this.warn(`Aborting inflight request ${q>0?"and switching down":""} Fragment duration: ${i.duration.toFixed(3)} s - Time to underbuffer: ${se.toFixed(3)} s`),i.abortRequests(),this.fragCurrent=this.partCurrent=null,W>f){let Q=this.findBestLevel(this.hls.levels[f].bitrate,f,W,0,se,1,1);Q===-1&&(Q=f),this.hls.nextLoadLevel=this.hls.nextAutoLevel=Q,this.resetEstimator(this.hls.levels[Q].bitrate)}}};p||G>V*2?z():this.timer=self.setInterval(z,V*1e3),n.trigger(m.FRAG_LOAD_EMERGENCY_ABORTED,{frag:i,part:r,stats:h})},this.hls=e,this.bwEstimator=this.initEstimator(),this.registerListeners()}resetEstimator(e){e&&(this.log(`setting initial bwe to ${e}`),this.hls.config.abrEwmaDefaultEstimate=e),this.firstSelection=-1,this.bwEstimator=this.initEstimator()}initEstimator(){const e=this.hls.config;return new io(e.abrEwmaSlowVoD,e.abrEwmaFastVoD,e.abrEwmaDefaultEstimate)}registerListeners(){const{hls:e}=this;e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.FRAG_LOADING,this.onFragLoading,this),e.on(m.FRAG_LOADED,this.onFragLoaded,this),e.on(m.FRAG_BUFFERED,this.onFragBuffered,this),e.on(m.LEVEL_SWITCHING,this.onLevelSwitching,this),e.on(m.LEVEL_LOADED,this.onLevelLoaded,this),e.on(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(m.MAX_AUTO_LEVEL_UPDATED,this.onMaxAutoLevelUpdated,this),e.on(m.ERROR,this.onError,this)}unregisterListeners(){const{hls:e}=this;e&&(e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.FRAG_LOADING,this.onFragLoading,this),e.off(m.FRAG_LOADED,this.onFragLoaded,this),e.off(m.FRAG_BUFFERED,this.onFragBuffered,this),e.off(m.LEVEL_SWITCHING,this.onLevelSwitching,this),e.off(m.LEVEL_LOADED,this.onLevelLoaded,this),e.off(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(m.MAX_AUTO_LEVEL_UPDATED,this.onMaxAutoLevelUpdated,this),e.off(m.ERROR,this.onError,this))}destroy(){this.unregisterListeners(),this.clearTimer(),this.hls=this._abandonRulesCheck=this.supportedCache=null,this.fragCurrent=this.partCurrent=null}onManifestLoading(e,t){this.lastLoadedFragLevel=-1,this.firstSelection=-1,this.lastLevelLoadSec=0,this.supportedCache={},this.fragCurrent=this.partCurrent=null,this.onLevelsUpdated(),this.clearTimer()}onLevelsUpdated(){this.lastLoadedFragLevel>-1&&this.fragCurrent&&(this.lastLoadedFragLevel=this.fragCurrent.level),this._nextAutoLevel=-1,this.onMaxAutoLevelUpdated(),this.codecTiers=null,this.audioTracksByGroup=null}onMaxAutoLevelUpdated(){this.firstSelection=-1,this.nextAutoLevelKey=""}onFragLoading(e,t){const s=t.frag;if(!this.ignoreFragment(s)){if(!s.bitrateTest){var i;this.fragCurrent=s,this.partCurrent=(i=t.part)!=null?i:null}this.clearTimer(),this.timer=self.setInterval(this._abandonRulesCheck,100)}}onLevelSwitching(e,t){this.clearTimer()}onError(e,t){if(!t.fatal)switch(t.details){case R.BUFFER_ADD_CODEC_ERROR:case R.BUFFER_APPEND_ERROR:this.lastLoadedFragLevel=-1,this.firstSelection=-1;break;case R.FRAG_LOAD_TIMEOUT:{const s=t.frag,{fragCurrent:i,partCurrent:r}=this;if(s&&i&&s.sn===i.sn&&s.level===i.level){const n=performance.now(),o=r?r.stats:s.stats,c=n-o.loading.start,l=o.loading.first?o.loading.first-o.loading.start:-1;if(o.loaded&&l>-1){const u=this.bwEstimator.getEstimateTTFB();this.bwEstimator.sample(c-Math.min(u,l),o.loaded)}else this.bwEstimator.sampleTTFB(c)}break}}}getTimeToLoadFrag(e,t,s,i){const r=e+s/t,n=i?e+this.lastLevelLoadSec:0;return r+n}onLevelLoaded(e,t){const s=this.hls.config,{loading:i}=t.stats,r=i.end-i.first;N(r)&&(this.lastLevelLoadSec=r/1e3),t.details.live?this.bwEstimator.update(s.abrEwmaSlowLive,s.abrEwmaFastLive):this.bwEstimator.update(s.abrEwmaSlowVoD,s.abrEwmaFastVoD),this.timer>-1&&this._abandonRulesCheck(t.levelInfo)}onFragLoaded(e,{frag:t,part:s}){const i=s?s.stats:t.stats;if(t.type===U.MAIN&&this.bwEstimator.sampleTTFB(i.loading.first-i.loading.start),!this.ignoreFragment(t)){if(this.clearTimer(),t.level===this._nextAutoLevel&&(this._nextAutoLevel=-1),this.firstSelection=-1,this.hls.config.abrMaxWithRealBitrate){const r=s?s.duration:t.duration,n=this.hls.levels[t.level],o=(n.loaded?n.loaded.bytes:0)+i.loaded,c=(n.loaded?n.loaded.duration:0)+r;n.loaded={bytes:o,duration:c},n.realBitrate=Math.round(8*o/c)}if(t.bitrateTest){const r={stats:i,frag:t,part:s,id:t.type};this.onFragBuffered(m.FRAG_BUFFERED,r),t.bitrateTest=!1}else this.lastLoadedFragLevel=t.level}}onFragBuffered(e,t){const{frag:s,part:i}=t,r=i!=null&&i.stats.loaded?i.stats:s.stats;if(r.aborted||this.ignoreFragment(s))return;const n=r.parsing.end-r.loading.start-Math.min(r.loading.first-r.loading.start,this.bwEstimator.getEstimateTTFB());this.bwEstimator.sample(n,r.loaded),r.bwEstimate=this.getBwEstimate(),s.bitrateTest?this.bitrateTestDelay=n/1e3:this.bitrateTestDelay=0}ignoreFragment(e){return e.type!==U.MAIN||e.sn==="initSegment"}clearTimer(){this.timer>-1&&(self.clearInterval(this.timer),this.timer=-1)}get firstAutoLevel(){const{maxAutoLevel:e,minAutoLevel:t}=this.hls,s=this.getBwEstimate(),i=this.hls.config.maxStarvationDelay,r=this.findBestLevel(s,t,e,0,i,1,1);if(r>-1)return r;const n=this.hls.firstLevel,o=Math.min(Math.max(n,t),e);return this.warn(`Could not find best starting auto level. Defaulting to first in playlist ${n} clamped to ${o}`),o}get forcedAutoLevel(){return this.nextAutoLevelKey?-1:this._nextAutoLevel}get nextAutoLevel(){const e=this.forcedAutoLevel,s=this.bwEstimator.canEstimate(),i=this.lastLoadedFragLevel>-1;if(e!==-1&&(!s||!i||this.nextAutoLevelKey===this.getAutoLevelKey()))return e;const r=s&&i?this.getNextABRAutoLevel():this.firstAutoLevel;if(e!==-1){const n=this.hls.levels;if(n.length>Math.max(e,r)&&n[e].loadError<=n[r].loadError)return e}return this._nextAutoLevel=r,this.nextAutoLevelKey=this.getAutoLevelKey(),r}getAutoLevelKey(){return`${this.getBwEstimate()}_${this.getStarvationDelay().toFixed(2)}`}getNextABRAutoLevel(){const{fragCurrent:e,partCurrent:t,hls:s}=this;if(s.levels.length<=1)return s.loadLevel;const{maxAutoLevel:i,config:r,minAutoLevel:n}=s,o=t?t.duration:e?e.duration:0,c=this.getBwEstimate(),l=this.getStarvationDelay();let h=r.abrBandWidthFactor,u=r.abrBandWidthUpFactor;if(l){const p=this.findBestLevel(c,n,i,l,0,h,u);if(p>=0)return this.rebufferNotice=-1,p}let d=o?Math.min(o,r.maxStarvationDelay):r.maxStarvationDelay;if(!l){const p=this.bitrateTestDelay;p&&(d=(o?Math.min(o,r.maxLoadingDelay):r.maxLoadingDelay)-p,this.info(`bitrate test took ${Math.round(1e3*p)}ms, set first fragment max fetchDuration to ${Math.round(1e3*d)} ms`),h=u=1)}const f=this.findBestLevel(c,n,i,l,d,h,u);if(this.rebufferNotice!==f&&(this.rebufferNotice=f,this.info(`${l?"rebuffering expected":"buffer is empty"}, optimal quality level ${f}`)),f>-1)return f;const g=s.levels[n],y=s.loadLevelObj;return y&&(g==null?void 0:g.bitrate)=t;k--){var G;const K=g[k],B=k>u;if(!K)continue;if(E.useMediaCapabilities&&!K.supportedResult&&!K.supportedPromise){const Q=navigator.mediaCapabilities;typeof(Q==null?void 0:Q.decodingInfo)=="function"&&Bo(K,I,D,A,e,_)?(K.supportedPromise=wn(K,I,Q,this.supportedCache),K.supportedPromise.then(ee=>{if(!this.hls)return;K.supportedResult=ee;const Te=this.hls.levels,me=Te.indexOf(K);ee.error?this.warn(`MediaCapabilities decodingInfo error: "${ee.error}" for level ${me} ${oe(ee)}`):ee.supported?ee.decodingInfoResults.some(De=>De.smooth===!1||De.powerEfficient===!1)&&this.log(`MediaCapabilities decodingInfo for level ${me} not smooth or powerEfficient: ${oe(ee)}`):(this.warn(`Unsupported MediaCapabilities decodingInfo result for level ${me} ${oe(ee)}`),me>-1&&Te.length>1&&(this.log(`Removing unsupported level ${me}`),this.hls.removeLevel(me),this.hls.loadLevel===-1&&(this.hls.nextLoadLevel=0)))}).catch(ee=>{this.warn(`Error handling MediaCapabilities decodingInfo: ${ee}`)})):K.supportedResult=Pn}if((x&&K.codecSet!==x||D&&K.videoRange!==D||B&&A>K.frameRate||!B&&A>0&&AQ.smooth===!1))&&(!v||k!==P)){Y.push(k);continue}const V=K.details,W=(f?V==null?void 0:V.partTarget:V==null?void 0:V.averagetargetduration)||F;let w;B?w=o*e:w=n*e;const O=F&&i>=F*2&&r===0?K.averageBitrate:K.maxBitrate,z=this.getTimeToLoadFrag($,w,O*W,V===void 0);if(w>=O&&(k===h||K.loadError===0&&K.fragmentError===0)&&(z<=$||!N(z)||S&&!this.bitrateTestDelay||z${k} adjustedbw(${Math.round(w)})-bitrate=${Math.round(w-O)} ttfb:${$.toFixed(1)} avgDuration:${W.toFixed(1)} maxFetchDuration:${l.toFixed(1)} fetchDuration:${z.toFixed(1)} firstSelection:${v} codecSet:${K.codecSet} videoRange:${K.videoRange} hls.loadLevel:${p}`)),v&&(this.firstSelection=k),k}}return-1}set nextAutoLevel(e){const t=this.deriveNextAutoLevel(e);this._nextAutoLevel!==t&&(this.nextAutoLevelKey="",this._nextAutoLevel=t)}deriveNextAutoLevel(e){const{maxAutoLevel:t,minAutoLevel:s}=this.hls;return Math.min(Math.max(e,s),t)}}const Mn={search:function(a,e){let t=0,s=a.length-1,i=null,r=null;for(;t<=s;){i=(t+s)/2|0,r=a[i];const n=e(r);if(n>0)t=i+1;else if(n<0)s=i-1;else return r}return null}};function el(a,e,t){if(e===null||!Array.isArray(a)||!a.length||!N(e))return null;const s=a[0].programDateTime;if(e<(s||0))return null;const i=a[a.length-1].endProgramDateTime;if(e>=(i||0))return null;for(let r=0;r0&&o<15e-7&&(t+=15e-7),r&&a.level!==r.level&&r.end<=a.end&&(r=e[2+a.sn-e[0].sn]||null)}else t===0&&e[0].start===0&&(r=e[0]);if(r&&((!a||a.level===r.level)&&fr(t,s,r)===0||tl(r,a,Math.min(i,s))))return r;const n=Mn.search(e,fr.bind(null,t,s));return n&&(n!==a||!r)?n:r}function tl(a,e,t){if(e&&e.start===0&&e.level0){const s=e.tagList.reduce((i,r)=>(r[0]==="INF"&&(i+=parseFloat(r[1])),i),t);return a.start<=s}return!1}function fr(a=0,e=0,t){if(t.start<=a&&t.start+t.duration>a)return 0;const s=Math.min(e,t.duration+(t.deltaPTS?t.deltaPTS:0));return t.start+t.duration-s<=a?1:t.start-s>a&&t.start?-1:0}function sl(a,e,t){const s=Math.min(e,t.duration+(t.deltaPTS?t.deltaPTS:0))*1e3;return(t.endProgramDateTime||0)-s>a}function Nn(a,e,t){if(a&&a.startCC<=e&&a.endCC>=e){let s=a.fragments;const{fragmentHint:i}=a;i&&(s=s.concat(i));let r;return Mn.search(s,n=>n.cce?-1:(r=n,n.end<=t?1:n.start>t?-1:0)),r||null}return null}function vs(a){switch(a.details){case R.FRAG_LOAD_TIMEOUT:case R.KEY_LOAD_TIMEOUT:case R.LEVEL_LOAD_TIMEOUT:case R.MANIFEST_LOAD_TIMEOUT:return!0}return!1}function Bn(a){return a.details.startsWith("key")}function Un(a){return Bn(a)&&!!a.frag&&!a.frag.decryptdata}function gr(a,e){const t=vs(e);return a.default[`${t?"timeout":"error"}Retry`]}function _i(a,e){const t=a.backoff==="linear"?1:Math.pow(2,e);return Math.min(t*a.retryDelayMs,a.maxRetryDelayMs)}function mr(a){return ie(ie({},a),{errorRetry:null,timeoutRetry:null})}function xs(a,e,t,s){if(!a)return!1;const i=s==null?void 0:s.code,r=e499)}function oi(a){return a===0&&navigator.onLine===!1}var pe={DoNothing:0,SendAlternateToPenaltyBox:2,RemoveAlternatePermanently:3,RetryRequest:5},Le={None:0,MoveAllAlternatesMatchingHost:1,MoveAllAlternatesMatchingHDCP:2,MoveAllAlternatesMatchingKey:4};class rl extends we{constructor(e){super("error-controller",e.logger),this.hls=void 0,this.playlistError=0,this.hls=e,this.registerListeners()}registerListeners(){const e=this.hls;e.on(m.ERROR,this.onError,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.LEVEL_UPDATED,this.onLevelUpdated,this)}unregisterListeners(){const e=this.hls;e&&(e.off(m.ERROR,this.onError,this),e.off(m.ERROR,this.onErrorOut,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.LEVEL_UPDATED,this.onLevelUpdated,this))}destroy(){this.unregisterListeners(),this.hls=null}startLoad(e){}stopLoad(){this.playlistError=0}getVariantLevelIndex(e){return(e==null?void 0:e.type)===U.MAIN?e.level:this.getVariantIndex()}getVariantIndex(){var e;const t=this.hls,s=t.currentLevel;return(e=t.loadLevelObj)!=null&&e.details||s===-1?t.loadLevel:s}variantHasKey(e,t){if(e){var s;if((s=e.details)!=null&&s.hasKey(t))return!0;const i=e.audioGroups;if(i)return this.hls.allAudioTracks.filter(n=>i.indexOf(n.groupId)>=0).some(n=>{var o;return(o=n.details)==null?void 0:o.hasKey(t)})}return!1}onManifestLoading(){this.playlistError=0}onLevelUpdated(){this.playlistError=0}onError(e,t){var s;if(t.fatal)return;const i=this.hls,r=t.context;switch(t.details){case R.FRAG_LOAD_ERROR:case R.FRAG_LOAD_TIMEOUT:case R.KEY_LOAD_ERROR:case R.KEY_LOAD_TIMEOUT:t.errorAction=this.getFragRetryOrSwitchAction(t);return;case R.FRAG_PARSING_ERROR:if((s=t.frag)!=null&&s.gap){t.errorAction=Rt();return}case R.FRAG_GAP:case R.FRAG_DECRYPT_ERROR:{t.errorAction=this.getFragRetryOrSwitchAction(t),t.errorAction.action=pe.SendAlternateToPenaltyBox;return}case R.LEVEL_EMPTY_ERROR:case R.LEVEL_PARSING_ERROR:{var n;const c=t.parent===U.MAIN?t.level:i.loadLevel;t.details===R.LEVEL_EMPTY_ERROR&&((n=t.context)!=null&&(n=n.levelDetails)!=null&&n.live)?t.errorAction=this.getPlaylistRetryOrSwitchAction(t,c):(t.levelRetry=!1,t.errorAction=this.getLevelSwitchAction(t,c))}return;case R.LEVEL_LOAD_ERROR:case R.LEVEL_LOAD_TIMEOUT:typeof(r==null?void 0:r.level)=="number"&&(t.errorAction=this.getPlaylistRetryOrSwitchAction(t,r.level));return;case R.AUDIO_TRACK_LOAD_ERROR:case R.AUDIO_TRACK_LOAD_TIMEOUT:case R.SUBTITLE_LOAD_ERROR:case R.SUBTITLE_TRACK_LOAD_TIMEOUT:if(r){const c=i.loadLevelObj;if(c&&(r.type===J.AUDIO_TRACK&&c.hasAudioGroup(r.groupId)||r.type===J.SUBTITLE_TRACK&&c.hasSubtitleGroup(r.groupId))){t.errorAction=this.getPlaylistRetryOrSwitchAction(t,i.loadLevel),t.errorAction.action=pe.SendAlternateToPenaltyBox,t.errorAction.flags=Le.MoveAllAlternatesMatchingHost;return}}return;case R.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED:t.errorAction={action:pe.SendAlternateToPenaltyBox,flags:Le.MoveAllAlternatesMatchingHDCP};return;case R.KEY_SYSTEM_SESSION_UPDATE_FAILED:case R.KEY_SYSTEM_STATUS_INTERNAL_ERROR:case R.KEY_SYSTEM_NO_SESSION:t.errorAction={action:pe.SendAlternateToPenaltyBox,flags:Le.MoveAllAlternatesMatchingKey};return;case R.BUFFER_ADD_CODEC_ERROR:case R.REMUX_ALLOC_ERROR:case R.BUFFER_APPEND_ERROR:if(!t.errorAction){var o;t.errorAction=this.getLevelSwitchAction(t,(o=t.level)!=null?o:i.loadLevel)}return;case R.INTERNAL_EXCEPTION:case R.BUFFER_APPENDING_ERROR:case R.BUFFER_FULL_ERROR:case R.LEVEL_SWITCH_ERROR:case R.BUFFER_STALLED_ERROR:case R.BUFFER_SEEK_OVER_HOLE:case R.BUFFER_NUDGE_ON_STALL:t.errorAction=Rt();return}t.type===H.KEY_SYSTEM_ERROR&&(t.levelRetry=!1,t.errorAction=Rt())}getPlaylistRetryOrSwitchAction(e,t){const s=this.hls,i=gr(s.config.playlistLoadPolicy,e),r=this.playlistError++;if(xs(i,r,vs(e),e.response))return{action:pe.RetryRequest,flags:Le.None,retryConfig:i,retryCount:r};const o=this.getLevelSwitchAction(e,t);return i&&(o.retryConfig=i,o.retryCount=r),o}getFragRetryOrSwitchAction(e){const t=this.hls,s=this.getVariantLevelIndex(e.frag),i=t.levels[s],{fragLoadPolicy:r,keyLoadPolicy:n}=t.config,o=gr(Bn(e)?n:r,e),c=t.levels.reduce((h,u)=>h+u.fragmentError,0);if(i&&(e.details!==R.FRAG_GAP&&i.fragmentError++,!Un(e)&&xs(o,c,vs(e),e.response)))return{action:pe.RetryRequest,flags:Le.None,retryConfig:o,retryCount:c};const l=this.getLevelSwitchAction(e,s);return o&&(l.retryConfig=o,l.retryCount=c),l}getLevelSwitchAction(e,t){const s=this.hls;t==null&&(t=s.loadLevel);const i=this.hls.levels[t];if(i){var r,n;const l=e.details;i.loadError++,l===R.BUFFER_APPEND_ERROR&&i.fragmentError++;let h=-1;const{levels:u,loadLevel:d,minAutoLevel:f,maxAutoLevel:g}=s;!s.autoLevelEnabled&&!s.config.preserveManualLevelOnError&&(s.loadLevel=-1);const y=(r=e.frag)==null?void 0:r.type,E=(y===U.AUDIO&&l===R.FRAG_PARSING_ERROR||e.sourceBufferName==="audio"&&(l===R.BUFFER_ADD_CODEC_ERROR||l===R.BUFFER_APPEND_ERROR))&&u.some(({audioCodec:D})=>i.audioCodec!==D),S=e.sourceBufferName==="video"&&(l===R.BUFFER_ADD_CODEC_ERROR||l===R.BUFFER_APPEND_ERROR)&&u.some(({codecSet:D,audioCodec:A})=>i.codecSet!==D&&i.audioCodec===A),{type:v,groupId:x}=(n=e.context)!=null?n:{};for(let D=u.length;D--;){const A=(D+d)%u.length;if(A!==d&&A>=f&&A<=g&&u[A].loadError===0){var o,c;const _=u[A];if(l===R.FRAG_GAP&&y===U.MAIN&&e.frag){const b=u[A].details;if(b){const I=mt(e.frag,b.fragments,e.frag.start);if(I!=null&&I.gap)continue}}else{if(v===J.AUDIO_TRACK&&_.hasAudioGroup(x)||v===J.SUBTITLE_TRACK&&_.hasSubtitleGroup(x))continue;if(y===U.AUDIO&&(o=i.audioGroups)!=null&&o.some(b=>_.hasAudioGroup(b))||y===U.SUBTITLE&&(c=i.subtitleGroups)!=null&&c.some(b=>_.hasSubtitleGroup(b))||E&&i.audioCodec===_.audioCodec||S&&i.codecSet===_.codecSet||!E&&i.codecSet!==_.codecSet)continue}h=A;break}}if(h>-1&&s.loadLevel!==h)return e.levelRetry=!0,this.playlistError=0,{action:pe.SendAlternateToPenaltyBox,flags:Le.None,nextAutoLevel:h}}return{action:pe.SendAlternateToPenaltyBox,flags:Le.MoveAllAlternatesMatchingHost}}onErrorOut(e,t){var s;switch((s=t.errorAction)==null?void 0:s.action){case pe.DoNothing:break;case pe.SendAlternateToPenaltyBox:this.sendAlternateToPenaltyBox(t),!t.errorAction.resolved&&t.details!==R.FRAG_GAP?t.fatal=!0:/MediaSource readyState: ended/.test(t.error.message)&&(this.warn(`MediaSource ended after "${t.sourceBufferName}" sourceBuffer append error. Attempting to recover from media error.`),this.hls.recoverMediaError());break}if(t.fatal){this.hls.stopLoad();return}}sendAlternateToPenaltyBox(e){const t=this.hls,s=e.errorAction;if(!s)return;const{flags:i}=s,r=s.nextAutoLevel;switch(i){case Le.None:this.switchLevel(e,r);break;case Le.MoveAllAlternatesMatchingHDCP:{const c=this.getVariantLevelIndex(e.frag),l=t.levels[c],h=l==null?void 0:l.attrs["HDCP-LEVEL"];if(s.hdcpLevel=h,h==="NONE")this.warn("HDCP policy resticted output with HDCP-LEVEL=NONE");else if(h){t.maxHdcpLevel=ai[ai.indexOf(h)-1],s.resolved=!0,this.warn(`Restricting playback to HDCP-LEVEL of "${t.maxHdcpLevel}" or lower`);break}}case Le.MoveAllAlternatesMatchingKey:{const c=e.decryptdata;if(c){const l=this.hls.levels,h=l.length;for(let d=h;d--;)if(this.variantHasKey(l[d],c)){var n,o;this.log(`Banned key found in level ${d} (${l[d].bitrate}bps) or audio group "${(n=l[d].audioGroups)==null?void 0:n.join(",")}" (${(o=e.frag)==null?void 0:o.type} fragment) ${ye(c.keyId||[])}`),l[d].fragmentError++,l[d].loadError++,this.log(`Removing level ${d} with key error (${e.error})`),this.hls.removeLevel(d)}const u=e.frag;if(this.hls.levels.length{const c=this.fragments[o];if(!c||n>=c.body.sn)return;if(!c.buffered&&(!c.loaded||r)){c.body.type===s&&this.removeFragment(c.body);return}const l=c.range[e];if(l){if(l.time.length===0){this.removeFragment(c.body);return}l.time.some(h=>{const u=!this.isTimeBuffered(h.startPTS,h.endPTS,t);return u&&this.removeFragment(c.body),u})}})}detectPartialFragments(e){const t=this.timeRanges;if(!t||e.frag.sn==="initSegment")return;const s=e.frag,i=Et(s),r=this.fragments[i];if(!r||r.buffered&&s.gap)return;const n=!s.relurl;Object.keys(t).forEach(o=>{const c=s.elementaryStreams[o];if(!c)return;const l=t[o],h=n||c.partial===!0;r.range[o]=this.getBufferedTimes(s,e.part,h,l)}),r.loaded=null,Object.keys(r.range).length?(this.bufferedEnd(r,s),Qt(r)||this.removeParts(s.sn-1,s.type)):this.removeFragment(r.body)}bufferedEnd(e,t){e.buffered=!0,(e.body.endList=t.endList||e.body.endList)&&(this.endListFragments[e.body.type]=e)}removeParts(e,t){const s=this.activePartLists[t];s&&(this.activePartLists[t]=pr(s,i=>i.fragment.sn>=e))}fragBuffered(e,t){const s=Et(e);let i=this.fragments[s];!i&&t&&(i=this.fragments[s]={body:e,appendedPTS:null,loaded:null,buffered:!1,range:Object.create(null)},e.gap&&(this.hasGaps=!0)),i&&(i.loaded=null,this.bufferedEnd(i,e))}getBufferedTimes(e,t,s,i){const r={time:[],partial:s},n=e.start,o=e.end,c=e.minEndPTS||o,l=e.maxStartPTS||n;for(let h=0;h=u&&c<=d){r.time.push({startPTS:Math.max(n,i.start(h)),endPTS:Math.min(o,i.end(h))});break}else if(nu){const f=Math.max(n,i.start(h)),g=Math.min(o,i.end(h));g>f&&(r.partial=!0,r.time.push({startPTS:f,endPTS:g}))}else if(o<=u)break}return r}getPartialFragment(e){let t=null,s,i,r,n=0;const{bufferPadding:o,fragments:c}=this;return Object.keys(c).forEach(l=>{const h=c[l];h&&Qt(h)&&(i=h.body.start-o,r=h.body.end+o,e>=i&&e<=r&&(s=Math.min(e-i,r-e),n<=s&&(t=h.body,n=s)))}),t}isEndListAppended(e){const t=this.endListFragments[e];return t!==void 0&&(t.buffered||Qt(t))}getState(e){const t=Et(e),s=this.fragments[t];return s?s.buffered?Qt(s)?fe.PARTIAL:fe.OK:fe.APPENDING:fe.NOT_LOADED}isTimeBuffered(e,t,s){let i,r;for(let n=0;n=i&&t<=r)return!0;if(t<=i)return!1}return!1}onManifestLoading(){this.removeAllFragments()}onFragLoaded(e,t){if(t.frag.sn==="initSegment"||t.frag.bitrateTest)return;const s=t.frag,i=t.part?null:t,r=Et(s);this.fragments[r]={body:s,appendedPTS:null,loaded:i,buffered:!1,range:Object.create(null)}}onBufferAppended(e,t){const{frag:s,part:i,timeRanges:r,type:n}=t;if(s.sn==="initSegment")return;const o=s.type;if(i){let l=this.activePartLists[o];l||(this.activePartLists[o]=l=[]),l.push(i)}this.timeRanges=r;const c=r[n];this.detectEvictedFragments(n,c,o,i)}onFragBuffered(e,t){this.detectPartialFragments(t)}hasFragment(e){const t=Et(e);return!!this.fragments[t]}hasFragments(e){const{fragments:t}=this,s=Object.keys(t);if(!e)return s.length>0;for(let i=s.length;i--;){const r=t[s[i]];if((r==null?void 0:r.body.type)===e)return!0}return!1}hasParts(e){var t;return!!((t=this.activePartLists[e])!=null&&t.length)}removeFragmentsInRange(e,t,s,i,r){i&&!this.hasGaps||Object.keys(this.fragments).forEach(n=>{const o=this.fragments[n];if(!o)return;const c=o.body;c.type!==s||i&&!c.gap||c.starte&&(o.buffered||r)&&this.removeFragment(c)})}removeFragment(e){const t=Et(e);e.clearElementaryStreamInfo();const s=this.activePartLists[e.type];if(s){const i=e.sn;this.activePartLists[e.type]=pr(s,r=>r.fragment.sn!==i)}delete this.fragments[t],e.endList&&delete this.endListFragments[e.type]}removeAllFragments(){var e;this.fragments=Object.create(null),this.endListFragments=Object.create(null),this.activePartLists=Object.create(null),this.hasGaps=!1;const t=(e=this.hls)==null||(e=e.latestLevelDetails)==null?void 0:e.partList;t&&t.forEach(s=>s.clearElementaryStreamInfo())}}function Qt(a){var e,t,s;return a.buffered&&!!(a.body.gap||(e=a.range.video)!=null&&e.partial||(t=a.range.audio)!=null&&t.partial||(s=a.range.audiovideo)!=null&&s.partial)}function Et(a){return`${a.type}_${a.level}_${a.sn}`}function pr(a,e){return a.filter(t=>{const s=e(t);return s||t.clearElementaryStreamInfo(),s})}var at={cbc:0,ctr:1};class al{constructor(e,t,s){this.subtle=void 0,this.aesIV=void 0,this.aesMode=void 0,this.subtle=e,this.aesIV=t,this.aesMode=s}decrypt(e,t){switch(this.aesMode){case at.cbc:return this.subtle.decrypt({name:"AES-CBC",iv:this.aesIV},t,e);case at.ctr:return this.subtle.decrypt({name:"AES-CTR",counter:this.aesIV,length:64},t,e);default:throw new Error(`[AESCrypto] invalid aes mode ${this.aesMode}`)}}}function ol(a){const e=a.byteLength,t=e&&new DataView(a.buffer).getUint8(e-1);return t?a.slice(0,e-t):a}class ll{constructor(){this.rcon=[0,1,2,4,8,16,32,64,128,27,54],this.subMix=[new Uint32Array(256),new Uint32Array(256),new Uint32Array(256),new Uint32Array(256)],this.invSubMix=[new Uint32Array(256),new Uint32Array(256),new Uint32Array(256),new Uint32Array(256)],this.sBox=new Uint32Array(256),this.invSBox=new Uint32Array(256),this.key=new Uint32Array(0),this.ksRows=0,this.keySize=0,this.keySchedule=void 0,this.invKeySchedule=void 0,this.initTable()}uint8ArrayToUint32Array_(e){const t=new DataView(e),s=new Uint32Array(4);for(let i=0;i<4;i++)s[i]=t.getUint32(i*4);return s}initTable(){const e=this.sBox,t=this.invSBox,s=this.subMix,i=s[0],r=s[1],n=s[2],o=s[3],c=this.invSubMix,l=c[0],h=c[1],u=c[2],d=c[3],f=new Uint32Array(256);let g=0,y=0,p=0;for(p=0;p<256;p++)p<128?f[p]=p<<1:f[p]=p<<1^283;for(p=0;p<256;p++){let E=y^y<<1^y<<2^y<<3^y<<4;E=E>>>8^E&255^99,e[g]=E,t[E]=g;const T=f[g],S=f[T],v=f[S];let x=f[E]*257^E*16843008;i[g]=x<<24|x>>>8,r[g]=x<<16|x>>>16,n[g]=x<<8|x>>>24,o[g]=x,x=v*16843009^S*65537^T*257^g*16843008,l[E]=x<<24|x>>>8,h[E]=x<<16|x>>>16,u[E]=x<<8|x>>>24,d[E]=x,g?(g=T^f[f[f[v^T]]],y^=f[f[y]]):g=y=1}}expandKey(e){const t=this.uint8ArrayToUint32Array_(e);let s=!0,i=0;for(;i{const o=ArrayBuffer.isView(e)?e:new Uint8Array(e);this.softwareDecrypt(o,t,s,i);const c=this.flush();c?r(c.buffer):n(new Error("[softwareDecrypt] Failed to decrypt data"))}):this.webCryptoDecrypt(new Uint8Array(e),t,s,i)}softwareDecrypt(e,t,s,i){const{currentIV:r,currentResult:n,remainderData:o}=this;if(i!==at.cbc||t.byteLength!==16)return re.warn("SoftwareDecrypt: can only handle AES-128-CBC"),null;this.logOnce("JS AES decrypt"),o&&(e=ke(o,e),this.remainderData=null);const c=this.getValidChunk(e);if(!c.length)return null;r&&(s=r);let l=this.softwareDecrypter;l||(l=this.softwareDecrypter=new ll),l.expandKey(t);const h=n;return this.currentResult=l.decrypt(c.buffer,0,s),this.currentIV=c.slice(-16).buffer,h||null}webCryptoDecrypt(e,t,s,i){if(this.key!==t||!this.fastAesKey){if(!this.subtle)return Promise.resolve(this.onWebCryptoError(e,t,s,i));this.key=t,this.fastAesKey=new cl(this.subtle,t,i)}return this.fastAesKey.expandKey().then(r=>this.subtle?(this.logOnce("WebCrypto AES decrypt"),new al(this.subtle,new Uint8Array(s),i).decrypt(e.buffer,r)):Promise.reject(new Error("web crypto not initialized"))).catch(r=>(re.warn(`[decrypter]: WebCrypto Error, disable WebCrypto API, ${r.name}: ${r.message}`),this.onWebCryptoError(e,t,s,i)))}onWebCryptoError(e,t,s,i){const r=this.enableSoftwareAES;if(r){this.useSoftware=!0,this.logEnabled=!0,this.softwareDecrypt(e,t,s,i);const n=this.flush();if(n)return n.buffer}throw new Error("WebCrypto"+(r?" and softwareDecrypt":"")+": failed to decrypt data")}getValidChunk(e){let t=e;const s=e.length-e.length%ul;return s!==e.length&&(t=e.slice(0,s),this.remainderData=e.slice(s)),t}logOnce(e){this.logEnabled&&(re.log(`[decrypter]: ${e}`),this.logEnabled=!1)}}const yr=Math.pow(2,17);class dl{constructor(e){this.config=void 0,this.loader=null,this.partLoadTimeout=-1,this.config=e}destroy(){this.loader&&(this.loader.destroy(),this.loader=null)}abort(){this.loader&&this.loader.abort()}load(e,t){const s=e.url;if(!s)return Promise.reject(new ze({type:H.NETWORK_ERROR,details:R.FRAG_LOAD_ERROR,fatal:!1,frag:e,error:new Error(`Fragment does not have a ${s?"part list":"url"}`),networkDetails:null}));this.abort();const i=this.config,r=i.fLoader,n=i.loader;return new Promise((o,c)=>{if(this.loader&&this.loader.destroy(),e.gap)if(e.tagList.some(g=>g[0]==="GAP")){c(Tr(e));return}else e.gap=!1;const l=this.loader=r?new r(i):new n(i),h=Er(e);e.loader=l;const u=mr(i.fragLoadPolicy.default),d={loadPolicy:u,timeout:u.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0,highWaterMark:e.sn==="initSegment"?1/0:yr};e.stats=l.stats;const f={onSuccess:(g,y,p,E)=>{this.resetLoader(e,l);let T=g.data;p.resetIV&&e.decryptdata&&(e.decryptdata.iv=new Uint8Array(T.slice(0,16)),T=T.slice(16)),o({frag:e,part:null,payload:T,networkDetails:E})},onError:(g,y,p,E)=>{this.resetLoader(e,l),c(new ze({type:H.NETWORK_ERROR,details:R.FRAG_LOAD_ERROR,fatal:!1,frag:e,response:ie({url:s,data:void 0},g),error:new Error(`HTTP Error ${g.code} ${g.text}`),networkDetails:p,stats:E}))},onAbort:(g,y,p)=>{this.resetLoader(e,l),c(new ze({type:H.NETWORK_ERROR,details:R.INTERNAL_ABORTED,fatal:!1,frag:e,error:new Error("Aborted"),networkDetails:p,stats:g}))},onTimeout:(g,y,p)=>{this.resetLoader(e,l),c(new ze({type:H.NETWORK_ERROR,details:R.FRAG_LOAD_TIMEOUT,fatal:!1,frag:e,error:new Error(`Timeout after ${d.timeout}ms`),networkDetails:p,stats:g}))}};t&&(f.onProgress=(g,y,p,E)=>t({frag:e,part:null,payload:p,networkDetails:E})),l.load(h,d,f)})}loadPart(e,t,s){this.abort();const i=this.config,r=i.fLoader,n=i.loader;return new Promise((o,c)=>{if(this.loader&&this.loader.destroy(),e.gap||t.gap){c(Tr(e,t));return}const l=this.loader=r?new r(i):new n(i),h=Er(e,t);e.loader=l;const u=mr(i.fragLoadPolicy.default),d={loadPolicy:u,timeout:u.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0,highWaterMark:yr};t.stats=l.stats,l.load(h,d,{onSuccess:(f,g,y,p)=>{this.resetLoader(e,l),this.updateStatsFromPart(e,t);const E={frag:e,part:t,payload:f.data,networkDetails:p};s(E),o(E)},onError:(f,g,y,p)=>{this.resetLoader(e,l),c(new ze({type:H.NETWORK_ERROR,details:R.FRAG_LOAD_ERROR,fatal:!1,frag:e,part:t,response:ie({url:h.url,data:void 0},f),error:new Error(`HTTP Error ${f.code} ${f.text}`),networkDetails:y,stats:p}))},onAbort:(f,g,y)=>{e.stats.aborted=t.stats.aborted,this.resetLoader(e,l),c(new ze({type:H.NETWORK_ERROR,details:R.INTERNAL_ABORTED,fatal:!1,frag:e,part:t,error:new Error("Aborted"),networkDetails:y,stats:f}))},onTimeout:(f,g,y)=>{this.resetLoader(e,l),c(new ze({type:H.NETWORK_ERROR,details:R.FRAG_LOAD_TIMEOUT,fatal:!1,frag:e,part:t,error:new Error(`Timeout after ${d.timeout}ms`),networkDetails:y,stats:f}))}})})}updateStatsFromPart(e,t){const s=e.stats,i=t.stats,r=i.total;if(s.loaded+=i.loaded,r){const c=Math.round(e.duration/t.duration),l=Math.min(Math.round(s.loaded/r),c),u=(c-l)*Math.round(s.loaded/l);s.total=s.loaded+u}else s.total=Math.max(s.loaded,s.total);const n=s.loading,o=i.loading;n.start?n.first+=o.first-o.start:(n.start=o.start,n.first=o.first),n.end=o.end}resetLoader(e,t){e.loader=null,this.loader===t&&(self.clearTimeout(this.partLoadTimeout),this.loader=null),t.destroy()}}function Er(a,e=null){const t=e||a,s={frag:a,part:e,responseType:"arraybuffer",url:t.url,headers:{},rangeStart:0,rangeEnd:0},i=t.byteRangeStartOffset,r=t.byteRangeEndOffset;if(N(i)&&N(r)){var n;let o=i,c=r;if(a.sn==="initSegment"&&fl((n=a.decryptdata)==null?void 0:n.method)){const l=r-i;l%16&&(c=r+(16-l%16)),i!==0&&(s.resetIV=!0,o=i-16)}s.rangeStart=o,s.rangeEnd=c}return s}function Tr(a,e){const t=new Error(`GAP ${a.gap?"tag":"attribute"} found`),s={type:H.MEDIA_ERROR,details:R.FRAG_GAP,fatal:!1,frag:a,error:t,networkDetails:null};return e&&(s.part=e),(e||a).stats.aborted=!0,new ze(s)}function fl(a){return a==="AES-128"||a==="AES-256"}class ze extends Error{constructor(e){super(e.error.message),this.data=void 0,this.data=e}}class $n extends we{constructor(e,t){super(e,t),this._boundTick=void 0,this._tickTimer=null,this._tickInterval=null,this._tickCallCount=0,this._boundTick=this.tick.bind(this)}destroy(){this.onHandlerDestroying(),this.onHandlerDestroyed()}onHandlerDestroying(){this.clearNextTick(),this.clearInterval()}onHandlerDestroyed(){}hasInterval(){return!!this._tickInterval}hasNextTick(){return!!this._tickTimer}setInterval(e){return this._tickInterval?!1:(this._tickCallCount=0,this._tickInterval=self.setInterval(this._boundTick,e),!0)}clearInterval(){return this._tickInterval?(self.clearInterval(this._tickInterval),this._tickInterval=null,!0):!1}clearNextTick(){return this._tickTimer?(self.clearTimeout(this._tickTimer),this._tickTimer=null,!0):!1}tick(){this._tickCallCount++,this._tickCallCount===1&&(this.doTick(),this._tickCallCount>1&&this.tickImmediate(),this._tickCallCount=0)}tickImmediate(){this.clearNextTick(),this._tickTimer=self.setTimeout(this._boundTick,0)}doTick(){}}class Ci{constructor(e,t,s,i=0,r=-1,n=!1){this.level=void 0,this.sn=void 0,this.part=void 0,this.id=void 0,this.size=void 0,this.partial=void 0,this.transmuxing=Zt(),this.buffering={audio:Zt(),video:Zt(),audiovideo:Zt()},this.level=e,this.sn=t,this.id=s,this.size=i,this.part=r,this.partial=n}}function Zt(){return{start:0,executeStart:0,executeEnd:0,end:0}}const Sr={length:0,start:()=>0,end:()=>0};class X{static isBuffered(e,t){if(e){const s=X.getBuffered(e);for(let i=s.length;i--;)if(t>=s.start(i)&&t<=s.end(i))return!0}return!1}static bufferedRanges(e){if(e){const t=X.getBuffered(e);return X.timeRangesToArray(t)}return[]}static timeRangesToArray(e){const t=[];for(let s=0;s1&&e.sort((h,u)=>h.start-u.start||u.end-h.end);let i=-1,r=[];if(s)for(let h=0;h=e[h].start&&t<=e[h].end&&(i=h);const u=r.length;if(u){const d=r[u-1].end;e[h].start-dd&&(r[u-1].end=e[h].end):r.push(e[h])}else r.push(e[h])}else r=e;let n=0,o,c=t,l=t;for(let h=0;h=u&&t<=d&&(i=h),t+s>=u&&t{const i=s.substring(2,s.length-1),r=t==null?void 0:t[i];return r===void 0?(a.playlistParsingError||(a.playlistParsingError=new Error(`Missing preceding EXT-X-DEFINE tag for Variable Reference: "${i}"`)),s):r})}return e}function xr(a,e,t){let s=a.variableList;s||(a.variableList=s={});let i,r;if("QUERYPARAM"in e){i=e.QUERYPARAM;try{const n=new self.URL(t).searchParams;if(n.has(i))r=n.get(i);else throw new Error(`"${i}" does not match any query parameter in URI: "${t}"`)}catch(n){a.playlistParsingError||(a.playlistParsingError=new Error(`EXT-X-DEFINE QUERYPARAM: ${n.message}`))}}else i=e.NAME,r=e.VALUE;i in s?a.playlistParsingError||(a.playlistParsingError=new Error(`EXT-X-DEFINE duplicate Variable Name declarations: "${i}"`)):s[i]=r||""}function gl(a,e,t){const s=e.IMPORT;if(t&&s in t){let i=a.variableList;i||(a.variableList=i={}),i[s]=t[s]}else a.playlistParsingError||(a.playlistParsingError=new Error(`EXT-X-DEFINE IMPORT attribute not found in Multivariant Playlist: "${s}"`))}const ml=/^(\d+)x(\d+)$/,Ar=/(.+?)=(".*?"|.*?)(?:,|$)/g;class le{constructor(e,t){typeof e=="string"&&(e=le.parseAttrList(e,t)),ne(this,e)}get clientAttrs(){return Object.keys(this).filter(e=>e.substring(0,2)==="X-")}decimalInteger(e){const t=parseInt(this[e],10);return t>Number.MAX_SAFE_INTEGER?1/0:t}hexadecimalInteger(e){if(this[e]){let t=(this[e]||"0x").slice(2);t=(t.length&1?"0":"")+t;const s=new Uint8Array(t.length/2);for(let i=0;iNumber.MAX_SAFE_INTEGER?1/0:t}decimalFloatingPoint(e){return parseFloat(this[e])}optionalFloat(e,t){const s=this[e];return s?parseFloat(s):t}enumeratedString(e){return this[e]}enumeratedStringList(e,t){const s=this[e];return(s?s.split(/[ ,]+/):[]).reduce((i,r)=>(i[r.toLowerCase()]=!0,i),t)}bool(e){return this[e]==="YES"}decimalResolution(e){const t=ml.exec(this[e]);if(t!==null)return{width:parseInt(t[1],10),height:parseInt(t[2],10)}}static parseAttrList(e,t){let s;const i={},r='"';for(Ar.lastIndex=0;(s=Ar.exec(e))!==null;){const n=s[1].trim();let o=s[2];const c=o.indexOf(r)===0&&o.lastIndexOf(r)===o.length-1;let l=!1;if(c)o=o.slice(1,-1);else switch(n){case"IV":case"SCTE35-CMD":case"SCTE35-IN":case"SCTE35-OUT":l=!0}if(t&&(c||l))o=li(t,o);else if(!l&&!c)switch(n){case"CLOSED-CAPTIONS":if(o==="NONE")break;case"ALLOWED-CPC":case"CLASS":case"ASSOC-LANGUAGE":case"AUDIO":case"BYTERANGE":case"CHANNELS":case"CHARACTERISTICS":case"CODECS":case"DATA-ID":case"END-DATE":case"GROUP-ID":case"ID":case"IMPORT":case"INSTREAM-ID":case"KEYFORMAT":case"KEYFORMATVERSIONS":case"LANGUAGE":case"NAME":case"PATHWAY-ID":case"QUERYPARAM":case"RECENTLY-REMOVED-DATERANGES":case"SERVER-URI":case"STABLE-RENDITION-ID":case"STABLE-VARIANT-ID":case"START-DATE":case"SUBTITLES":case"SUPPLEMENTAL-CODECS":case"URI":case"VALUE":case"VIDEO":case"X-ASSET-LIST":case"X-ASSET-URI":re.warn(`${e}: attribute ${n} is missing quotes`)}i[n]=o}return i}}const pl="com.apple.hls.interstitial";function yl(a){return a!=="ID"&&a!=="CLASS"&&a!=="CUE"&&a!=="START-DATE"&&a!=="DURATION"&&a!=="END-DATE"&&a!=="END-ON-NEXT"}function El(a){return a==="SCTE35-OUT"||a==="SCTE35-IN"||a==="SCTE35-CMD"}class Kn{constructor(e,t,s=0){var i;if(this.attr=void 0,this.tagAnchor=void 0,this.tagOrder=void 0,this._startDate=void 0,this._endDate=void 0,this._dateAtEnd=void 0,this._cue=void 0,this._badValueForSameId=void 0,this.tagAnchor=(t==null?void 0:t.tagAnchor)||null,this.tagOrder=(i=t==null?void 0:t.tagOrder)!=null?i:s,t){const r=t.attr;for(const n in r)if(Object.prototype.hasOwnProperty.call(e,n)&&e[n]!==r[n]){re.warn(`DATERANGE tag attribute: "${n}" does not match for tags with ID: "${e.ID}"`),this._badValueForSameId=n;break}e=ne(new le({}),r,e)}if(this.attr=e,t?(this._startDate=t._startDate,this._cue=t._cue,this._endDate=t._endDate,this._dateAtEnd=t._dateAtEnd):this._startDate=new Date(e["START-DATE"]),"END-DATE"in this.attr){const r=(t==null?void 0:t.endDate)||new Date(this.attr["END-DATE"]);N(r.getTime())&&(this._endDate=r)}}get id(){return this.attr.ID}get class(){return this.attr.CLASS}get cue(){const e=this._cue;return e===void 0?this._cue=this.attr.enumeratedStringList(this.attr.CUE?"CUE":"X-CUE",{pre:!1,post:!1,once:!1}):e}get startTime(){const{tagAnchor:e}=this;return e===null||e.programDateTime===null?(re.warn(`Expected tagAnchor Fragment with PDT set for DateRange "${this.id}": ${e}`),NaN):e.start+(this.startDate.getTime()-e.programDateTime)/1e3}get startDate(){return this._startDate}get endDate(){const e=this._endDate||this._dateAtEnd;if(e)return e;const t=this.duration;return t!==null?this._dateAtEnd=new Date(this._startDate.getTime()+t*1e3):null}get duration(){if("DURATION"in this.attr){const e=this.attr.decimalFloatingPoint("DURATION");if(N(e))return e}else if(this._endDate)return(this._endDate.getTime()-this._startDate.getTime())/1e3;return null}get plannedDuration(){return"PLANNED-DURATION"in this.attr?this.attr.decimalFloatingPoint("PLANNED-DURATION"):null}get endOnNext(){return this.attr.bool("END-ON-NEXT")}get isInterstitial(){return this.class===pl}get isValid(){return!!this.id&&!this._badValueForSameId&&N(this.startDate.getTime())&&(this.duration===null||this.duration>=0)&&(!this.endOnNext||!!this.class)&&(!this.attr.CUE||!this.cue.pre&&!this.cue.post||this.cue.pre!==this.cue.post)&&(!this.isInterstitial||"X-ASSET-URI"in this.attr||"X-ASSET-LIST"in this.attr)}}const Tl=10;class Sl{constructor(e){this.PTSKnown=!1,this.alignedSliding=!1,this.averagetargetduration=void 0,this.endCC=0,this.endSN=0,this.fragments=void 0,this.fragmentHint=void 0,this.partList=null,this.dateRanges=void 0,this.dateRangeTagCount=0,this.live=!0,this.requestScheduled=-1,this.ageHeader=0,this.advancedDateTime=void 0,this.updated=!0,this.advanced=!0,this.misses=0,this.startCC=0,this.startSN=0,this.startTimeOffset=null,this.targetduration=0,this.totalduration=0,this.type=null,this.url=void 0,this.m3u8="",this.version=null,this.canBlockReload=!1,this.canSkipUntil=0,this.canSkipDateRanges=!1,this.skippedSegments=0,this.recentlyRemovedDateranges=void 0,this.partHoldBack=0,this.holdBack=0,this.partTarget=0,this.preloadHint=void 0,this.renditionReports=void 0,this.tuneInGoal=0,this.deltaUpdateFailed=void 0,this.driftStartTime=0,this.driftEndTime=0,this.driftStart=0,this.driftEnd=0,this.encryptedFragments=void 0,this.playlistParsingError=null,this.variableList=null,this.hasVariableRefs=!1,this.appliedTimelineOffset=void 0,this.fragments=[],this.encryptedFragments=[],this.dateRanges={},this.url=e}reloaded(e){if(!e){this.advanced=!0,this.updated=!0;return}const t=this.lastPartSn-e.lastPartSn,s=this.lastPartIndex-e.lastPartIndex;this.updated=this.endSN!==e.endSN||!!s||!!t||!this.live,this.advanced=this.endSN>e.endSN||t>0||t===0&&s>0,this.updated||this.advanced?this.misses=Math.floor(e.misses*.6):this.misses=e.misses+1}hasKey(e){return this.encryptedFragments.some(t=>{let s=t.decryptdata;return s||(t.setKeyFormat(e.keyFormat),s=t.decryptdata),!!s&&e.matches(s)})}get hasProgramDateTime(){return this.fragments.length?N(this.fragments[this.fragments.length-1].programDateTime):!1}get levelTargetDuration(){return this.averagetargetduration||this.targetduration||Tl}get drift(){const e=this.driftEndTime-this.driftStartTime;return e>0?(this.driftEnd-this.driftStart)*1e3/e:1}get edge(){return this.partEnd||this.fragmentEnd}get partEnd(){var e;return(e=this.partList)!=null&&e.length?this.partList[this.partList.length-1].end:this.fragmentEnd}get fragmentEnd(){return this.fragments.length?this.fragments[this.fragments.length-1].end:0}get fragmentStart(){return this.fragments.length?this.fragments[0].start:0}get age(){return this.advancedDateTime?Math.max(Date.now()-this.advancedDateTime,0)/1e3:0}get lastPartIndex(){var e;return(e=this.partList)!=null&&e.length?this.partList[this.partList.length-1].index:-1}get maxPartIndex(){const e=this.partList;if(e){const t=this.lastPartIndex;if(t!==-1){for(let s=e.length;s--;)if(e[s].index>t)return e[s].index;return t}}return 0}get lastPartSn(){var e;return(e=this.partList)!=null&&e.length?this.partList[this.partList.length-1].fragment.sn:this.endSN}get expired(){if(this.live&&this.age&&this.misses<3){const e=this.partEnd-this.fragmentStart;return this.age>Math.max(e,this.totalduration)+this.levelTargetDuration}return!1}}function As(a,e){return a.length===e.length?!a.some((t,s)=>t!==e[s]):!1}function Ir(a,e){return!a&&!e?!0:!a||!e?!1:As(a,e)}function bt(a){return a==="AES-128"||a==="AES-256"||a==="AES-256-CTR"}function Pi(a){switch(a){case"AES-128":case"AES-256":return at.cbc;case"AES-256-CTR":return at.ctr;default:throw new Error(`invalid full segment method ${a}`)}}function ki(a){return Uint8Array.from(atob(a),e=>e.charCodeAt(0))}function ci(a){return Uint8Array.from(unescape(encodeURIComponent(a)),e=>e.charCodeAt(0))}function vl(a){const e=ci(a).subarray(0,16),t=new Uint8Array(16);return t.set(e,16-e.length),t}function Vn(a){const e=function(s,i,r){const n=s[i];s[i]=s[r],s[r]=n};e(a,0,3),e(a,1,2),e(a,4,5),e(a,6,7)}function Hn(a){const e=a.split(":");let t=null;if(e[0]==="data"&&e.length===2){const s=e[1].split(";"),i=s[s.length-1].split(",");if(i.length===2){const r=i[0]==="base64",n=i[1];r?(s.splice(-1,1),t=ki(n)):t=vl(n)}}return t}const Is=typeof self<"u"?self:void 0;var ce={CLEARKEY:"org.w3.clearkey",FAIRPLAY:"com.apple.fps",PLAYREADY:"com.microsoft.playready",WIDEVINE:"com.widevine.alpha"},Ee={CLEARKEY:"org.w3.clearkey",FAIRPLAY:"com.apple.streamingkeydelivery",PLAYREADY:"com.microsoft.playready",WIDEVINE:"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"};function us(a){switch(a){case Ee.FAIRPLAY:return ce.FAIRPLAY;case Ee.PLAYREADY:return ce.PLAYREADY;case Ee.WIDEVINE:return ce.WIDEVINE;case Ee.CLEARKEY:return ce.CLEARKEY}}function Ns(a){switch(a){case ce.FAIRPLAY:return Ee.FAIRPLAY;case ce.PLAYREADY:return Ee.PLAYREADY;case ce.WIDEVINE:return Ee.WIDEVINE;case ce.CLEARKEY:return Ee.CLEARKEY}}function Ft(a){const{drmSystems:e,widevineLicenseUrl:t}=a,s=e?[ce.FAIRPLAY,ce.WIDEVINE,ce.PLAYREADY,ce.CLEARKEY].filter(i=>!!e[i]):[];return!s[ce.WIDEVINE]&&t&&s.push(ce.WIDEVINE),s}const Yn=function(a){return Is!=null&&(a=Is.navigator)!=null&&a.requestMediaKeySystemAccess?self.navigator.requestMediaKeySystemAccess.bind(self.navigator):null}();function xl(a,e,t,s){let i;switch(a){case ce.FAIRPLAY:i=["cenc","sinf"];break;case ce.WIDEVINE:case ce.PLAYREADY:i=["cenc"];break;case ce.CLEARKEY:i=["cenc","keyids"];break;default:throw new Error(`Unknown key-system: ${a}`)}return Al(i,e,t,s)}function Al(a,e,t,s){return[{initDataTypes:a,persistentState:s.persistentState||"optional",distinctiveIdentifier:s.distinctiveIdentifier||"optional",sessionTypes:s.sessionTypes||[s.sessionType||"temporary"],audioCapabilities:e.map(r=>({contentType:`audio/mp4; codecs=${r}`,robustness:s.audioRobustness||"",encryptionScheme:s.audioEncryptionScheme||null})),videoCapabilities:t.map(r=>({contentType:`video/mp4; codecs=${r}`,robustness:s.videoRobustness||"",encryptionScheme:s.videoEncryptionScheme||null}))}]}function Il(a){var e;return!!a&&(a.sessionType==="persistent-license"||!!((e=a.sessionTypes)!=null&&e.some(t=>t==="persistent-license")))}function Wn(a){const e=new Uint16Array(a.buffer,a.byteOffset,a.byteLength/2),t=String.fromCharCode.apply(null,Array.from(e)),s=t.substring(t.indexOf("<"),t.length),n=new DOMParser().parseFromString(s,"text/xml").getElementsByTagName("KID")[0];if(n){const o=n.childNodes[0]?n.childNodes[0].nodeValue:n.getAttribute("VALUE");if(o){const c=ki(o).subarray(0,16);return Vn(c),c}}return null}let Tt={};class rt{static clearKeyUriToKeyIdMap(){Tt={}}static setKeyIdForUri(e,t){Tt[e]=t}static addKeyIdForUri(e){const t=Object.keys(Tt).length%Number.MAX_SAFE_INTEGER,s=new Uint8Array(16);return new DataView(s.buffer,12,4).setUint32(0,t),Tt[e]=s,s}constructor(e,t,s,i=[1],r=null,n){this.uri=void 0,this.method=void 0,this.keyFormat=void 0,this.keyFormatVersions=void 0,this.encrypted=void 0,this.isCommonEncryption=void 0,this.iv=null,this.key=null,this.keyId=null,this.pssh=null,this.method=e,this.uri=t,this.keyFormat=s,this.keyFormatVersions=i,this.iv=r,this.encrypted=e?e!=="NONE":!1,this.isCommonEncryption=this.encrypted&&!bt(e),n!=null&&n.startsWith("0x")&&(this.keyId=new Uint8Array(Sn(n)))}matches(e){return e.uri===this.uri&&e.method===this.method&&e.encrypted===this.encrypted&&e.keyFormat===this.keyFormat&&As(e.keyFormatVersions,this.keyFormatVersions)&&Ir(e.iv,this.iv)&&Ir(e.keyId,this.keyId)}isSupported(){if(this.method){if(bt(this.method)||this.method==="NONE")return!0;if(this.keyFormat==="identity")return this.method==="SAMPLE-AES";switch(this.keyFormat){case Ee.FAIRPLAY:case Ee.WIDEVINE:case Ee.PLAYREADY:case Ee.CLEARKEY:return["SAMPLE-AES","SAMPLE-AES-CENC","SAMPLE-AES-CTR"].indexOf(this.method)!==-1}}return!1}getDecryptData(e,t){if(!this.encrypted||!this.uri)return null;if(bt(this.method)){let r=this.iv;return r||(typeof e!="number"&&(re.warn(`missing IV for initialization segment with method="${this.method}" - compliance issue`),e=0),r=Rl(e)),new rt(this.method,this.uri,"identity",this.keyFormatVersions,r)}if(this.keyId){const r=Tt[this.uri];if(r&&!As(this.keyId,r)&&rt.setKeyIdForUri(this.uri,this.keyId),this.pssh)return this}const s=Hn(this.uri);if(s)switch(this.keyFormat){case Ee.WIDEVINE:if(this.pssh=s,!this.keyId){const r=Do(s.buffer);if(r.length){var i;const n=r[0];this.keyId=(i=n.kids)!=null&&i.length?n.kids[0]:null}}this.keyId||(this.keyId=Lr(t));break;case Ee.PLAYREADY:{const r=new Uint8Array([154,4,240,121,152,64,66,134,171,146,230,91,224,136,95,149]);this.pssh=_o(r,null,s),this.keyId=Wn(s);break}default:{let r=s.subarray(0,16);if(r.length!==16){const n=new Uint8Array(16);n.set(r,16-r.length),r=n}this.keyId=r;break}}if(!this.keyId||this.keyId.byteLength!==16){let r;r=Ll(t),r||(r=Lr(t),r||(r=Tt[this.uri])),r&&(this.keyId=r,rt.setKeyIdForUri(this.uri,r))}return this}}function Ll(a){const e=a==null?void 0:a[Ee.WIDEVINE];return e?e.keyId:null}function Lr(a){const e=a==null?void 0:a[Ee.PLAYREADY];if(e){const t=Hn(e.uri);if(t)return Wn(t)}return null}function Rl(a){const e=new Uint8Array(16);for(let t=12;t<16;t++)e[t]=a>>8*(15-t)&255;return e}const Rr=/#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-(SESSION-DATA|SESSION-KEY|DEFINE|CONTENT-STEERING|START):([^\r\n]*)[\r\n]+/g,br=/#EXT-X-MEDIA:(.*)/g,bl=/^#EXT(?:INF|-X-TARGETDURATION):/m,Bs=new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source,/(?!#) *(\S[^\r\n]*)/.source,/#.*/.source].join("|"),"g"),_l=new RegExp([/#EXT-X-(PROGRAM-DATE-TIME|BYTERANGE|DATERANGE|DEFINE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/.source,/#EXT-X-(BITRATE|DISCONTINUITY-SEQUENCE|MEDIA-SEQUENCE|TARGETDURATION|VERSION): *(\d+)/.source,/#EXT-X-(DISCONTINUITY|ENDLIST|GAP|INDEPENDENT-SEGMENTS)/.source,/(#)([^:]*):(.*)/.source,/(#)(.*)(?:.*)\r?\n?/.source].join("|"));class Ye{static findGroup(e,t){for(let s=0;s0&&r.length({id:l.attrs.AUDIO,audioCodec:l.audioCodec})),SUBTITLES:n.map(l=>({id:l.attrs.SUBTITLES,textCodec:l.textCodec})),"CLOSED-CAPTIONS":[]};let c=0;for(br.lastIndex=0;(i=br.exec(e))!==null;){const l=new le(i[1],s),h=l.TYPE;if(h){const u=o[h],d=r[h]||[];r[h]=d;const f=l.LANGUAGE,g=l["ASSOC-LANGUAGE"],y=l.CHANNELS,p=l.CHARACTERISTICS,E=l["INSTREAM-ID"],T={attrs:l,bitrate:0,id:c++,groupId:l["GROUP-ID"]||"",name:l.NAME||f||"",type:h,default:l.bool("DEFAULT"),autoselect:l.bool("AUTOSELECT"),forced:l.bool("FORCED"),lang:f,url:l.URI?Ye.resolve(l.URI,t):""};if(g&&(T.assocLang=g),y&&(T.channels=y),p&&(T.characteristics=p),E&&(T.instreamId=E),u!=null&&u.length){const S=Ye.findGroup(u,T.groupId)||u[0];Pr(T,S,"audioCodec"),Pr(T,S,"textCodec")}d.push(T)}}return r}static parseLevelPlaylist(e,t,s,i,r,n){var o;const c={url:t},l=new Sl(t),h=l.fragments,u=[];let d=null,f=0,g=0,y=0,p=0,E=0,T=null,S=new Os(i,c),v,x,D,A=-1,_=!1,b=null,I;if(Bs.lastIndex=0,l.m3u8=e,l.hasVariableRefs=vr(e),((o=Bs.exec(e))==null?void 0:o[0])!=="#EXTM3U")return l.playlistParsingError=new Error("Missing format identifier #EXTM3U"),l;for(;(v=Bs.exec(e))!==null;){_&&(_=!1,S=new Os(i,c),S.playlistOffset=y,S.setStart(y),S.sn=f,S.cc=p,E&&(S.bitrate=E),S.level=s,d&&(S.initSegment=d,d.rawProgramDateTime&&(S.rawProgramDateTime=d.rawProgramDateTime,d.rawProgramDateTime=null),b&&(S.setByteRange(b),b=null)));const Y=v[1];if(Y){S.duration=parseFloat(Y);const G=(" "+v[2]).slice(1);S.title=G||null,S.tagList.push(G?["INF",Y,G]:["INF",Y])}else if(v[3]){if(N(S.duration)){S.playlistOffset=y,S.setStart(y),D&&wr(S,D,l),S.sn=f,S.level=s,S.cc=p,h.push(S);const G=(" "+v[3]).slice(1);S.relurl=li(l,G),hi(S,T,u),T=S,y+=S.duration,f++,g=0,_=!0}}else{if(v=v[0].match(_l),!v){re.warn("No matches on slow regex match for level playlist!");continue}for(x=1;x0&&Or(l,G,v),f=l.startSN=parseInt(k);break;case"SKIP":{l.skippedSegments&&je(l,G,v);const B=new le(k,l),V=B.decimalInteger("SKIPPED-SEGMENTS");if(N(V)){l.skippedSegments+=V;for(let w=V;w--;)h.push(null);f+=V}const W=B.enumeratedString("RECENTLY-REMOVED-DATERANGES");W&&(l.recentlyRemovedDateranges=(l.recentlyRemovedDateranges||[]).concat(W.split(" ")));break}case"TARGETDURATION":l.targetduration!==0&&je(l,G,v),l.targetduration=Math.max(parseInt(k),1);break;case"VERSION":l.version!==null&&je(l,G,v),l.version=parseInt(k);break;case"INDEPENDENT-SEGMENTS":break;case"ENDLIST":l.live||je(l,G,v),l.live=!1;break;case"#":(k||K)&&S.tagList.push(K?[k,K]:[k]);break;case"DISCONTINUITY":p++,S.tagList.push(["DIS"]);break;case"GAP":S.gap=!0,S.tagList.push([G]);break;case"BITRATE":S.tagList.push([G,k]),E=parseInt(k)*1e3,N(E)?S.bitrate=E:E=0;break;case"DATERANGE":{const B=new le(k,l),V=new Kn(B,l.dateRanges[B.ID],l.dateRangeTagCount);l.dateRangeTagCount++,V.isValid||l.skippedSegments?l.dateRanges[V.id]=V:re.warn(`Ignoring invalid DATERANGE tag: "${k}"`),S.tagList.push(["EXT-X-DATERANGE",k]);break}case"DEFINE":{{const B=new le(k,l);"IMPORT"in B?gl(l,B,n):xr(l,B,t)}break}case"DISCONTINUITY-SEQUENCE":l.startCC!==0?je(l,G,v):h.length>0&&Or(l,G,v),l.startCC=p=parseInt(k);break;case"KEY":{const B=_r(k,t,l);if(B.isSupported()){if(B.method==="NONE"){D=void 0;break}D||(D={});const V=D[B.keyFormat];V!=null&&V.matches(B)||(V&&(D=ne({},D)),D[B.keyFormat]=B)}else re.warn(`[Keys] Ignoring unsupported EXT-X-KEY tag: "${k}"`);break}case"START":l.startTimeOffset=Dr(k);break;case"MAP":{const B=new le(k,l);if(S.duration){const V=new Os(i,c);kr(V,B,s,D),d=V,S.initSegment=d,d.rawProgramDateTime&&!S.rawProgramDateTime&&(S.rawProgramDateTime=d.rawProgramDateTime)}else{const V=S.byteRangeEndOffset;if(V){const W=S.byteRangeStartOffset;b=`${V-W}@${W}`}else b=null;kr(S,B,s,D),d=S,_=!0}d.cc=p;break}case"SERVER-CONTROL":{I&&je(l,G,v),I=new le(k),l.canBlockReload=I.bool("CAN-BLOCK-RELOAD"),l.canSkipUntil=I.optionalFloat("CAN-SKIP-UNTIL",0),l.canSkipDateRanges=l.canSkipUntil>0&&I.bool("CAN-SKIP-DATERANGES"),l.partHoldBack=I.optionalFloat("PART-HOLD-BACK",0),l.holdBack=I.optionalFloat("HOLD-BACK",0);break}case"PART-INF":{l.partTarget&&je(l,G,v);const B=new le(k);l.partTarget=B.decimalFloatingPoint("PART-TARGET");break}case"PART":{let B=l.partList;B||(B=l.partList=[]);const V=g>0?B[B.length-1]:void 0,W=g++,w=new le(k,l),O=new go(w,S,c,W,V);B.push(O),S.duration+=O.duration;break}case"PRELOAD-HINT":{const B=new le(k,l);l.preloadHint=B;break}case"RENDITION-REPORT":{const B=new le(k,l);l.renditionReports=l.renditionReports||[],l.renditionReports.push(B);break}default:re.warn(`line parsed but not handled: ${v}`);break}}}T&&!T.relurl?(h.pop(),y-=T.duration,l.partList&&(l.fragmentHint=T)):l.partList&&(hi(S,T,u),S.cc=p,l.fragmentHint=S,D&&wr(S,D,l)),l.targetduration||(l.playlistParsingError=new Error("Missing Target Duration"));const P=h.length,F=h[0],$=h[P-1];if(y+=l.skippedSegments*l.targetduration,y>0&&P&&$){l.averagetargetduration=y/P;const Y=$.sn;l.endSN=Y!=="initSegment"?Y:0,l.live||($.endList=!0),A>0&&(Cl(h,A),F&&u.unshift(F))}return l.fragmentHint&&(y+=l.fragmentHint.duration),l.totalduration=y,u.length&&l.dateRangeTagCount&&F&&qn(u,l),l.endCC=p,l}}function qn(a,e){let t=a.length;if(!t)if(e.hasProgramDateTime){const o=e.fragments[e.fragments.length-1];a.push(o),t++}else return;const s=a[t-1],i=e.live?1/0:e.totalduration,r=Object.keys(e.dateRanges);for(let o=r.length;o--;){const c=e.dateRanges[r[o]],l=c.startDate.getTime();c.tagAnchor=s.ref;for(let h=t;h--;){var n;if(((n=a[h])==null?void 0:n.sn)=o||s===0){var n;const c=(((n=t[s+1])==null?void 0:n.start)||i)-r.start;if(e<=o+c*1e3){const l=t[s].sn-a.startSN;if(l<0)return-1;const h=a.fragments;if(h.length>t.length){const d=(t[s+1]||h[h.length-1]).sn-a.startSN;for(let f=d;f>l;f--){const g=h[f].programDateTime;if(e>=g&&es);["video","audio","text"].forEach(s=>{const i=t.filter(r=>Ri(r,s));i.length&&(e[`${s}Codec`]=i.map(r=>r.split("/")[0]).join(","),t=t.filter(r=>i.indexOf(r)===-1))}),e.unknownCodecs=t}function Pr(a,e,t){const s=e[t];s&&(a[t]=s)}function Cl(a,e){let t=a[e];for(let s=e;s--;){const i=a[s];if(!i)return;i.programDateTime=t.programDateTime-i.duration*1e3,t=i}}function hi(a,e,t){a.rawProgramDateTime?t.push(a):e!=null&&e.programDateTime&&(a.programDateTime=e.endProgramDateTime)}function kr(a,e,t,s){a.relurl=e.URI,e.BYTERANGE&&a.setByteRange(e.BYTERANGE),a.level=t,a.sn="initSegment",s&&(a.levelkeys=s),a.initSegment=null}function wr(a,e,t){a.levelkeys=e;const{encryptedFragments:s}=t;(!s.length||s[s.length-1].levelkeys!==e)&&Object.keys(e).some(i=>e[i].isCommonEncryption)&&s.push(a)}function je(a,e,t){a.playlistParsingError=new Error(`#EXT-X-${e} must not appear more than once (${t[0]})`)}function Or(a,e,t){a.playlistParsingError=new Error(`#EXT-X-${e} must appear before the first Media Segment (${t[0]})`)}function Us(a,e){const t=e.startPTS;if(N(t)){let s=0,i;e.sn>a.sn?(s=t-a.start,i=a):(s=a.start-t,i=e),i.duration!==s&&i.setDuration(s)}else e.sn>a.sn?a.cc===e.cc&&a.minEndPTS?e.setStart(a.start+(a.minEndPTS-a.start)):e.setStart(a.start+a.duration):e.setStart(Math.max(a.start-e.duration,0))}function jn(a,e,t,s,i,r,n){s-t<=0&&(n.warn("Fragment should have a positive duration",e),s=t+e.duration,r=i+e.duration);let c=t,l=s;const h=e.startPTS,u=e.endPTS;if(N(h)){const E=Math.abs(h-t);a&&E>a.totalduration?n.warn(`media timestamps and playlist times differ by ${E}s for level ${e.level} ${a.url}`):N(e.deltaPTS)?e.deltaPTS=Math.max(E,e.deltaPTS):e.deltaPTS=E,c=Math.max(t,h),t=Math.min(t,h),i=e.startDTS!==void 0?Math.min(i,e.startDTS):i,l=Math.min(s,u),s=Math.max(s,u),r=e.endDTS!==void 0?Math.max(r,e.endDTS):r}const d=t-e.start;e.start!==0&&e.setStart(t),e.setDuration(s-e.start),e.startPTS=t,e.maxStartPTS=c,e.startDTS=i,e.endPTS=s,e.minEndPTS=l,e.endDTS=r;const f=e.sn;if(!a||fa.endSN)return 0;let g;const y=f-a.startSN,p=a.fragments;for(p[y]=e,g=y;g>0;g--)Us(p[g],p[g-1]);for(g=y;g=0;h--){const u=i[h].initSegment;if(u){s=u;break}}a.fragmentHint&&delete a.fragmentHint.endPTS;let r;Ol(a,e,(h,u,d,f)=>{if((!e.startCC||e.skippedSegments)&&u.cc!==h.cc){const g=h.cc-u.cc;for(let y=d;y{var u;h&&(!h.initSegment||h.initSegment.relurl===((u=s)==null?void 0:u.relurl))&&(h.initSegment=s)}),e.skippedSegments){if(e.deltaUpdateFailed=n.some(h=>!h),e.deltaUpdateFailed){t.warn("[level-helper] Previous playlist missing segments skipped in delta playlist");for(let h=e.skippedSegments;h--;)n.shift();e.startSN=n[0].sn}else{e.canSkipDateRanges&&(e.dateRanges=kl(a.dateRanges,e,t));const h=a.fragments.filter(u=>u.rawProgramDateTime);if(a.hasProgramDateTime&&!e.hasProgramDateTime)for(let u=1;u{u.elementaryStreams=h.elementaryStreams,u.stats=h.stats}),r?jn(e,r,r.startPTS,r.endPTS,r.startDTS,r.endDTS,t):Xn(a,e),n.length&&(e.totalduration=e.edge-n[0].start),e.driftStartTime=a.driftStartTime,e.driftStart=a.driftStart;const l=e.advancedDateTime;if(e.advanced&&l){const h=e.edge;e.driftStart||(e.driftStartTime=l,e.driftStart=h),e.driftEndTime=l,e.driftEnd=h}else e.driftEndTime=a.driftEndTime,e.driftEnd=a.driftEnd,e.advancedDateTime=a.advancedDateTime;e.requestScheduled===-1&&(e.requestScheduled=a.requestScheduled)}function kl(a,e,t){const{dateRanges:s,recentlyRemovedDateranges:i}=e,r=ne({},a);i&&i.forEach(c=>{delete r[c]});const o=Object.keys(r).length;return o?(Object.keys(s).forEach(c=>{const l=r[c],h=new Kn(s[c].attr,l);h.isValid?(r[c]=h,l||(h.tagOrder+=o)):t.warn(`Ignoring invalid Playlist Delta Update DATERANGE tag: "${oe(s[c].attr)}"`)}),r):s}function wl(a,e,t){if(a&&e){let s=0;for(let i=0,r=a.length;i<=r;i++){const n=a[i],o=e[i+s];n&&o&&n.index===o.index&&n.fragment.sn===o.fragment.sn?t(n,o):s--}}}function Ol(a,e,t){const s=e.skippedSegments,i=Math.max(a.startSN,e.startSN)-e.startSN,r=(a.fragmentHint?1:0)+(s?e.endSN:Math.min(a.endSN,e.endSN))-e.startSN,n=e.startSN-a.startSN,o=e.fragmentHint?e.fragments.concat(e.fragmentHint):e.fragments,c=a.fragmentHint?a.fragments.concat(a.fragmentHint):a.fragments;for(let l=i;l<=r;l++){const h=c[n+l];let u=o[l];if(s&&!u&&h&&(u=e.fragments[l]=h),h&&u){t(h,u,l,o);const d=h.relurl,f=u.relurl;if(d&&Fl(d,f)){e.playlistParsingError=Fr(`media sequence mismatch ${u.sn}:`,a,e,h,u);return}else if(h.cc!==u.cc){e.playlistParsingError=Fr(`discontinuity sequence mismatch (${h.cc}!=${u.cc})`,a,e,h,u);return}}}}function Fr(a,e,t,s,i){return new Error(`${a} ${i.url} + Time to underbuffer: ${se.toFixed(3)} s`),i.abortRequests(),this.fragCurrent=this.partCurrent=null,q>f){let Q=this.findBestLevel(this.hls.levels[f].bitrate,f,q,0,se,1,1);Q===-1&&(Q=f),this.hls.nextLoadLevel=this.hls.nextAutoLevel=Q,this.resetEstimator(this.hls.levels[Q].bitrate)}}};p||K>H*2?z():this.timer=self.setInterval(z,H*1e3),n.trigger(m.FRAG_LOAD_EMERGENCY_ABORTED,{frag:i,part:r,stats:h})},this.hls=e,this.bwEstimator=this.initEstimator(),this.registerListeners()}resetEstimator(e){e&&(this.log(`setting initial bwe to ${e}`),this.hls.config.abrEwmaDefaultEstimate=e),this.firstSelection=-1,this.bwEstimator=this.initEstimator()}initEstimator(){const e=this.hls.config;return new ao(e.abrEwmaSlowVoD,e.abrEwmaFastVoD,e.abrEwmaDefaultEstimate)}registerListeners(){const{hls:e}=this;e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.FRAG_LOADING,this.onFragLoading,this),e.on(m.FRAG_LOADED,this.onFragLoaded,this),e.on(m.FRAG_BUFFERED,this.onFragBuffered,this),e.on(m.LEVEL_SWITCHING,this.onLevelSwitching,this),e.on(m.LEVEL_LOADED,this.onLevelLoaded,this),e.on(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(m.MAX_AUTO_LEVEL_UPDATED,this.onMaxAutoLevelUpdated,this),e.on(m.ERROR,this.onError,this)}unregisterListeners(){const{hls:e}=this;e&&(e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.FRAG_LOADING,this.onFragLoading,this),e.off(m.FRAG_LOADED,this.onFragLoaded,this),e.off(m.FRAG_BUFFERED,this.onFragBuffered,this),e.off(m.LEVEL_SWITCHING,this.onLevelSwitching,this),e.off(m.LEVEL_LOADED,this.onLevelLoaded,this),e.off(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(m.MAX_AUTO_LEVEL_UPDATED,this.onMaxAutoLevelUpdated,this),e.off(m.ERROR,this.onError,this))}destroy(){this.unregisterListeners(),this.clearTimer(),this.hls=this._abandonRulesCheck=this.supportedCache=null,this.fragCurrent=this.partCurrent=null}onManifestLoading(e,t){this.lastLoadedFragLevel=-1,this.firstSelection=-1,this.lastLevelLoadSec=0,this.supportedCache={},this.fragCurrent=this.partCurrent=null,this.onLevelsUpdated(),this.clearTimer()}onLevelsUpdated(){this.lastLoadedFragLevel>-1&&this.fragCurrent&&(this.lastLoadedFragLevel=this.fragCurrent.level),this._nextAutoLevel=-1,this.onMaxAutoLevelUpdated(),this.codecTiers=null,this.audioTracksByGroup=null}onMaxAutoLevelUpdated(){this.firstSelection=-1,this.nextAutoLevelKey=""}onFragLoading(e,t){const s=t.frag;if(!this.ignoreFragment(s)){if(!s.bitrateTest){var i;this.fragCurrent=s,this.partCurrent=(i=t.part)!=null?i:null}this.clearTimer(),this.timer=self.setInterval(this._abandonRulesCheck,100)}}onLevelSwitching(e,t){this.clearTimer()}onError(e,t){if(!t.fatal)switch(t.details){case R.BUFFER_ADD_CODEC_ERROR:case R.BUFFER_APPEND_ERROR:this.lastLoadedFragLevel=-1,this.firstSelection=-1;break;case R.FRAG_LOAD_TIMEOUT:{const s=t.frag,{fragCurrent:i,partCurrent:r}=this;if(s&&i&&s.sn===i.sn&&s.level===i.level){const n=performance.now(),o=r?r.stats:s.stats,c=n-o.loading.start,l=o.loading.first?o.loading.first-o.loading.start:-1;if(o.loaded&&l>-1){const d=this.bwEstimator.getEstimateTTFB();this.bwEstimator.sample(c-Math.min(d,l),o.loaded)}else this.bwEstimator.sampleTTFB(c)}break}}}getTimeToLoadFrag(e,t,s,i){const r=e+s/t,n=i?e+this.lastLevelLoadSec:0;return r+n}onLevelLoaded(e,t){const s=this.hls.config,{loading:i}=t.stats,r=i.end-i.first;B(r)&&(this.lastLevelLoadSec=r/1e3),t.details.live?this.bwEstimator.update(s.abrEwmaSlowLive,s.abrEwmaFastLive):this.bwEstimator.update(s.abrEwmaSlowVoD,s.abrEwmaFastVoD),this.timer>-1&&this._abandonRulesCheck(t.levelInfo)}onFragLoaded(e,{frag:t,part:s}){const i=s?s.stats:t.stats;if(t.type===U.MAIN&&this.bwEstimator.sampleTTFB(i.loading.first-i.loading.start),!this.ignoreFragment(t)){if(this.clearTimer(),t.level===this._nextAutoLevel&&(this._nextAutoLevel=-1),this.firstSelection=-1,this.hls.config.abrMaxWithRealBitrate){const r=s?s.duration:t.duration,n=this.hls.levels[t.level],o=(n.loaded?n.loaded.bytes:0)+i.loaded,c=(n.loaded?n.loaded.duration:0)+r;n.loaded={bytes:o,duration:c},n.realBitrate=Math.round(8*o/c)}if(t.bitrateTest){const r={stats:i,frag:t,part:s,id:t.type};this.onFragBuffered(m.FRAG_BUFFERED,r),t.bitrateTest=!1}else this.lastLoadedFragLevel=t.level}}onFragBuffered(e,t){const{frag:s,part:i}=t,r=i!=null&&i.stats.loaded?i.stats:s.stats;if(r.aborted||this.ignoreFragment(s))return;const n=r.parsing.end-r.loading.start-Math.min(r.loading.first-r.loading.start,this.bwEstimator.getEstimateTTFB());this.bwEstimator.sample(n,r.loaded),r.bwEstimate=this.getBwEstimate(),s.bitrateTest?this.bitrateTestDelay=n/1e3:this.bitrateTestDelay=0}ignoreFragment(e){return e.type!==U.MAIN||e.sn==="initSegment"}clearTimer(){this.timer>-1&&(self.clearInterval(this.timer),this.timer=-1)}get firstAutoLevel(){const{maxAutoLevel:e,minAutoLevel:t}=this.hls,s=this.getBwEstimate(),i=this.hls.config.maxStarvationDelay,r=this.findBestLevel(s,t,e,0,i,1,1);if(r>-1)return r;const n=this.hls.firstLevel,o=Math.min(Math.max(n,t),e);return this.warn(`Could not find best starting auto level. Defaulting to first in playlist ${n} clamped to ${o}`),o}get forcedAutoLevel(){return this.nextAutoLevelKey?-1:this._nextAutoLevel}get nextAutoLevel(){const e=this.forcedAutoLevel,s=this.bwEstimator.canEstimate(),i=this.lastLoadedFragLevel>-1;if(e!==-1&&(!s||!i||this.nextAutoLevelKey===this.getAutoLevelKey()))return e;const r=s&&i?this.getNextABRAutoLevel():this.firstAutoLevel;if(e!==-1){const n=this.hls.levels;if(n.length>Math.max(e,r)&&n[e].loadError<=n[r].loadError)return e}return this._nextAutoLevel=r,this.nextAutoLevelKey=this.getAutoLevelKey(),r}getAutoLevelKey(){return`${this.getBwEstimate()}_${this.getStarvationDelay().toFixed(2)}`}getNextABRAutoLevel(){const{fragCurrent:e,partCurrent:t,hls:s}=this;if(s.levels.length<=1)return s.loadLevel;const{maxAutoLevel:i,config:r,minAutoLevel:n}=s,o=t?t.duration:e?e.duration:0,c=this.getBwEstimate(),l=this.getStarvationDelay();let h=r.abrBandWidthFactor,d=r.abrBandWidthUpFactor;if(l){const p=this.findBestLevel(c,n,i,l,0,h,d);if(p>=0)return this.rebufferNotice=-1,p}let u=o?Math.min(o,r.maxStarvationDelay):r.maxStarvationDelay;if(!l){const p=this.bitrateTestDelay;p&&(u=(o?Math.min(o,r.maxLoadingDelay):r.maxLoadingDelay)-p,this.info(`bitrate test took ${Math.round(1e3*p)}ms, set first fragment max fetchDuration to ${Math.round(1e3*u)} ms`),h=d=1)}const f=this.findBestLevel(c,n,i,l,u,h,d);if(this.rebufferNotice!==f&&(this.rebufferNotice=f,this.info(`${l?"rebuffering expected":"buffer is empty"}, optimal quality level ${f}`)),f>-1)return f;const g=s.levels[n],y=s.loadLevelObj;return y&&(g==null?void 0:g.bitrate)=t;w--){var K;const V=g[w],$=w>d;if(!V)continue;if(E.useMediaCapabilities&&!V.supportedResult&&!V.supportedPromise){const Q=navigator.mediaCapabilities;typeof(Q==null?void 0:Q.decodingInfo)=="function"&&Go(V,I,D,A,e,_)?(V.supportedPromise=Mn(V,I,Q,this.supportedCache),V.supportedPromise.then(ee=>{if(!this.hls)return;V.supportedResult=ee;const Se=this.hls.levels,me=Se.indexOf(V);ee.error?this.warn(`MediaCapabilities decodingInfo error: "${ee.error}" for level ${me} ${oe(ee)}`):ee.supported?ee.decodingInfoResults.some(Pe=>Pe.smooth===!1||Pe.powerEfficient===!1)&&this.log(`MediaCapabilities decodingInfo for level ${me} not smooth or powerEfficient: ${oe(ee)}`):(this.warn(`Unsupported MediaCapabilities decodingInfo result for level ${me} ${oe(ee)}`),me>-1&&Se.length>1&&(this.log(`Removing unsupported level ${me}`),this.hls.removeLevel(me),this.hls.loadLevel===-1&&(this.hls.nextLoadLevel=0)))}).catch(ee=>{this.warn(`Error handling MediaCapabilities decodingInfo: ${ee}`)})):V.supportedResult=On}if((x&&V.codecSet!==x||D&&V.videoRange!==D||$&&A>V.frameRate||!$&&A>0&&AQ.smooth===!1))&&(!v||w!==P)){W.push(w);continue}const H=V.details,q=(f?H==null?void 0:H.partTarget:H==null?void 0:H.averagetargetduration)||M;let O;$?O=o*e:O=n*e;const F=M&&i>=M*2&&r===0?V.averageBitrate:V.maxBitrate,z=this.getTimeToLoadFrag(G,O,F*q,H===void 0);if(O>=F&&(w===h||V.loadError===0&&V.fragmentError===0)&&(z<=G||!B(z)||S&&!this.bitrateTestDelay||z${w} adjustedbw(${Math.round(O)})-bitrate=${Math.round(O-F)} ttfb:${G.toFixed(1)} avgDuration:${q.toFixed(1)} maxFetchDuration:${l.toFixed(1)} fetchDuration:${z.toFixed(1)} firstSelection:${v} codecSet:${V.codecSet} videoRange:${V.videoRange} hls.loadLevel:${p}`)),v&&(this.firstSelection=w),w}}return-1}set nextAutoLevel(e){const t=this.deriveNextAutoLevel(e);this._nextAutoLevel!==t&&(this.nextAutoLevelKey="",this._nextAutoLevel=t)}deriveNextAutoLevel(e){const{maxAutoLevel:t,minAutoLevel:s}=this.hls;return Math.min(Math.max(e,s),t)}}const $n={search:function(a,e){let t=0,s=a.length-1,i=null,r=null;for(;t<=s;){i=(t+s)/2|0,r=a[i];const n=e(r);if(n>0)t=i+1;else if(n<0)s=i-1;else return r}return null}};function il(a,e,t){if(e===null||!Array.isArray(a)||!a.length||!B(e))return null;const s=a[0].programDateTime;if(e<(s||0))return null;const i=a[a.length-1].endProgramDateTime;if(e>=(i||0))return null;for(let r=0;r0&&o<15e-7&&(t+=15e-7),r&&a.level!==r.level&&r.end<=a.end&&(r=e[2+a.sn-e[0].sn]||null)}else t===0&&e[0].start===0&&(r=e[0]);if(r&&((!a||a.level===r.level)&&mr(t,s,r)===0||rl(r,a,Math.min(i,s))))return r;const n=$n.search(e,mr.bind(null,t,s));return n&&(n!==a||!r)?n:r}function rl(a,e,t){if(e&&e.start===0&&e.level0){const s=e.tagList.reduce((i,r)=>(r[0]==="INF"&&(i+=parseFloat(r[1])),i),t);return a.start<=s}return!1}function mr(a=0,e=0,t){if(t.start<=a&&t.start+t.duration>a)return 0;const s=Math.min(e,t.duration+(t.deltaPTS?t.deltaPTS:0));return t.start+t.duration-s<=a?1:t.start-s>a&&t.start?-1:0}function nl(a,e,t){const s=Math.min(e,t.duration+(t.deltaPTS?t.deltaPTS:0))*1e3;return(t.endProgramDateTime||0)-s>a}function Un(a,e,t){if(a&&a.startCC<=e&&a.endCC>=e){let s=a.fragments;const{fragmentHint:i}=a;i&&(s=s.concat(i));let r;return $n.search(s,n=>n.cce?-1:(r=n,n.end<=t?1:n.start>t?-1:0)),r||null}return null}function xs(a){switch(a.details){case R.FRAG_LOAD_TIMEOUT:case R.KEY_LOAD_TIMEOUT:case R.LEVEL_LOAD_TIMEOUT:case R.MANIFEST_LOAD_TIMEOUT:return!0}return!1}function Gn(a){return a.details.startsWith("key")}function Kn(a){return Gn(a)&&!!a.frag&&!a.frag.decryptdata}function pr(a,e){const t=xs(e);return a.default[`${t?"timeout":"error"}Retry`]}function Ci(a,e){const t=a.backoff==="linear"?1:Math.pow(2,e);return Math.min(t*a.retryDelayMs,a.maxRetryDelayMs)}function yr(a){return ie(ie({},a),{errorRetry:null,timeoutRetry:null})}function As(a,e,t,s){if(!a)return!1;const i=s==null?void 0:s.code,r=e499)}function li(a){return a===0&&navigator.onLine===!1}var pe={DoNothing:0,SendAlternateToPenaltyBox:2,RemoveAlternatePermanently:3,RetryRequest:5},Re={None:0,MoveAllAlternatesMatchingHost:1,MoveAllAlternatesMatchingHDCP:2,MoveAllAlternatesMatchingKey:4};class ol extends Oe{constructor(e){super("error-controller",e.logger),this.hls=void 0,this.playlistError=0,this.hls=e,this.registerListeners()}registerListeners(){const e=this.hls;e.on(m.ERROR,this.onError,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.LEVEL_UPDATED,this.onLevelUpdated,this)}unregisterListeners(){const e=this.hls;e&&(e.off(m.ERROR,this.onError,this),e.off(m.ERROR,this.onErrorOut,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.LEVEL_UPDATED,this.onLevelUpdated,this))}destroy(){this.unregisterListeners(),this.hls=null}startLoad(e){}stopLoad(){this.playlistError=0}getVariantLevelIndex(e){return(e==null?void 0:e.type)===U.MAIN?e.level:this.getVariantIndex()}getVariantIndex(){var e;const t=this.hls,s=t.currentLevel;return(e=t.loadLevelObj)!=null&&e.details||s===-1?t.loadLevel:s}variantHasKey(e,t){if(e){var s;if((s=e.details)!=null&&s.hasKey(t))return!0;const i=e.audioGroups;if(i)return this.hls.allAudioTracks.filter(n=>i.indexOf(n.groupId)>=0).some(n=>{var o;return(o=n.details)==null?void 0:o.hasKey(t)})}return!1}onManifestLoading(){this.playlistError=0}onLevelUpdated(){this.playlistError=0}onError(e,t){var s;if(t.fatal)return;const i=this.hls,r=t.context;switch(t.details){case R.FRAG_LOAD_ERROR:case R.FRAG_LOAD_TIMEOUT:case R.KEY_LOAD_ERROR:case R.KEY_LOAD_TIMEOUT:t.errorAction=this.getFragRetryOrSwitchAction(t);return;case R.FRAG_PARSING_ERROR:if((s=t.frag)!=null&&s.gap){t.errorAction=bt();return}case R.FRAG_GAP:case R.FRAG_DECRYPT_ERROR:{t.errorAction=this.getFragRetryOrSwitchAction(t),t.errorAction.action=pe.SendAlternateToPenaltyBox;return}case R.LEVEL_EMPTY_ERROR:case R.LEVEL_PARSING_ERROR:{var n;const c=t.parent===U.MAIN?t.level:i.loadLevel;t.details===R.LEVEL_EMPTY_ERROR&&((n=t.context)!=null&&(n=n.levelDetails)!=null&&n.live)?t.errorAction=this.getPlaylistRetryOrSwitchAction(t,c):(t.levelRetry=!1,t.errorAction=this.getLevelSwitchAction(t,c))}return;case R.LEVEL_LOAD_ERROR:case R.LEVEL_LOAD_TIMEOUT:typeof(r==null?void 0:r.level)=="number"&&(t.errorAction=this.getPlaylistRetryOrSwitchAction(t,r.level));return;case R.AUDIO_TRACK_LOAD_ERROR:case R.AUDIO_TRACK_LOAD_TIMEOUT:case R.SUBTITLE_LOAD_ERROR:case R.SUBTITLE_TRACK_LOAD_TIMEOUT:if(r){const c=i.loadLevelObj;if(c&&(r.type===J.AUDIO_TRACK&&c.hasAudioGroup(r.groupId)||r.type===J.SUBTITLE_TRACK&&c.hasSubtitleGroup(r.groupId))){t.errorAction=this.getPlaylistRetryOrSwitchAction(t,i.loadLevel),t.errorAction.action=pe.SendAlternateToPenaltyBox,t.errorAction.flags=Re.MoveAllAlternatesMatchingHost;return}}return;case R.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED:t.errorAction={action:pe.SendAlternateToPenaltyBox,flags:Re.MoveAllAlternatesMatchingHDCP};return;case R.KEY_SYSTEM_SESSION_UPDATE_FAILED:case R.KEY_SYSTEM_STATUS_INTERNAL_ERROR:case R.KEY_SYSTEM_NO_SESSION:t.errorAction={action:pe.SendAlternateToPenaltyBox,flags:Re.MoveAllAlternatesMatchingKey};return;case R.BUFFER_ADD_CODEC_ERROR:case R.REMUX_ALLOC_ERROR:case R.BUFFER_APPEND_ERROR:if(!t.errorAction){var o;t.errorAction=this.getLevelSwitchAction(t,(o=t.level)!=null?o:i.loadLevel)}return;case R.INTERNAL_EXCEPTION:case R.BUFFER_APPENDING_ERROR:case R.BUFFER_FULL_ERROR:case R.LEVEL_SWITCH_ERROR:case R.BUFFER_STALLED_ERROR:case R.BUFFER_SEEK_OVER_HOLE:case R.BUFFER_NUDGE_ON_STALL:t.errorAction=bt();return}t.type===Y.KEY_SYSTEM_ERROR&&(t.levelRetry=!1,t.errorAction=bt())}getPlaylistRetryOrSwitchAction(e,t){const s=this.hls,i=pr(s.config.playlistLoadPolicy,e),r=this.playlistError++;if(As(i,r,xs(e),e.response))return{action:pe.RetryRequest,flags:Re.None,retryConfig:i,retryCount:r};const o=this.getLevelSwitchAction(e,t);return i&&(o.retryConfig=i,o.retryCount=r),o}getFragRetryOrSwitchAction(e){const t=this.hls,s=this.getVariantLevelIndex(e.frag),i=t.levels[s],{fragLoadPolicy:r,keyLoadPolicy:n}=t.config,o=pr(Gn(e)?n:r,e),c=t.levels.reduce((h,d)=>h+d.fragmentError,0);if(i&&(e.details!==R.FRAG_GAP&&i.fragmentError++,!Kn(e)&&As(o,c,xs(e),e.response)))return{action:pe.RetryRequest,flags:Re.None,retryConfig:o,retryCount:c};const l=this.getLevelSwitchAction(e,s);return o&&(l.retryConfig=o,l.retryCount=c),l}getLevelSwitchAction(e,t){const s=this.hls;t==null&&(t=s.loadLevel);const i=this.hls.levels[t];if(i){var r,n;const l=e.details;i.loadError++,l===R.BUFFER_APPEND_ERROR&&i.fragmentError++;let h=-1;const{levels:d,loadLevel:u,minAutoLevel:f,maxAutoLevel:g}=s;!s.autoLevelEnabled&&!s.config.preserveManualLevelOnError&&(s.loadLevel=-1);const y=(r=e.frag)==null?void 0:r.type,E=(y===U.AUDIO&&l===R.FRAG_PARSING_ERROR||e.sourceBufferName==="audio"&&(l===R.BUFFER_ADD_CODEC_ERROR||l===R.BUFFER_APPEND_ERROR))&&d.some(({audioCodec:D})=>i.audioCodec!==D),S=e.sourceBufferName==="video"&&(l===R.BUFFER_ADD_CODEC_ERROR||l===R.BUFFER_APPEND_ERROR)&&d.some(({codecSet:D,audioCodec:A})=>i.codecSet!==D&&i.audioCodec===A),{type:v,groupId:x}=(n=e.context)!=null?n:{};for(let D=d.length;D--;){const A=(D+u)%d.length;if(A!==u&&A>=f&&A<=g&&d[A].loadError===0){var o,c;const _=d[A];if(l===R.FRAG_GAP&&y===U.MAIN&&e.frag){const b=d[A].details;if(b){const I=pt(e.frag,b.fragments,e.frag.start);if(I!=null&&I.gap)continue}}else{if(v===J.AUDIO_TRACK&&_.hasAudioGroup(x)||v===J.SUBTITLE_TRACK&&_.hasSubtitleGroup(x))continue;if(y===U.AUDIO&&(o=i.audioGroups)!=null&&o.some(b=>_.hasAudioGroup(b))||y===U.SUBTITLE&&(c=i.subtitleGroups)!=null&&c.some(b=>_.hasSubtitleGroup(b))||E&&i.audioCodec===_.audioCodec||S&&i.codecSet===_.codecSet||!E&&i.codecSet!==_.codecSet)continue}h=A;break}}if(h>-1&&s.loadLevel!==h)return e.levelRetry=!0,this.playlistError=0,{action:pe.SendAlternateToPenaltyBox,flags:Re.None,nextAutoLevel:h}}return{action:pe.SendAlternateToPenaltyBox,flags:Re.MoveAllAlternatesMatchingHost}}onErrorOut(e,t){var s;switch((s=t.errorAction)==null?void 0:s.action){case pe.DoNothing:break;case pe.SendAlternateToPenaltyBox:this.sendAlternateToPenaltyBox(t),!t.errorAction.resolved&&t.details!==R.FRAG_GAP?t.fatal=!0:/MediaSource readyState: ended/.test(t.error.message)&&(this.warn(`MediaSource ended after "${t.sourceBufferName}" sourceBuffer append error. Attempting to recover from media error.`),this.hls.recoverMediaError());break}if(t.fatal){this.hls.stopLoad();return}}sendAlternateToPenaltyBox(e){const t=this.hls,s=e.errorAction;if(!s)return;const{flags:i}=s,r=s.nextAutoLevel;switch(i){case Re.None:this.switchLevel(e,r);break;case Re.MoveAllAlternatesMatchingHDCP:{const c=this.getVariantLevelIndex(e.frag),l=t.levels[c],h=l==null?void 0:l.attrs["HDCP-LEVEL"];if(s.hdcpLevel=h,h==="NONE")this.warn("HDCP policy resticted output with HDCP-LEVEL=NONE");else if(h){t.maxHdcpLevel=oi[oi.indexOf(h)-1],s.resolved=!0,this.warn(`Restricting playback to HDCP-LEVEL of "${t.maxHdcpLevel}" or lower`);break}}case Re.MoveAllAlternatesMatchingKey:{const c=e.decryptdata;if(c){const l=this.hls.levels,h=l.length;for(let u=h;u--;)if(this.variantHasKey(l[u],c)){var n,o;this.log(`Banned key found in level ${u} (${l[u].bitrate}bps) or audio group "${(n=l[u].audioGroups)==null?void 0:n.join(",")}" (${(o=e.frag)==null?void 0:o.type} fragment) ${ye(c.keyId||[])}`),l[u].fragmentError++,l[u].loadError++,this.log(`Removing level ${u} with key error (${e.error})`),this.hls.removeLevel(u)}const d=e.frag;if(this.hls.levels.length{const c=this.fragments[o];if(!c||n>=c.body.sn)return;if(!c.buffered&&(!c.loaded||r)){c.body.type===s&&this.removeFragment(c.body);return}const l=c.range[e];if(l){if(l.time.length===0){this.removeFragment(c.body);return}l.time.some(h=>{const d=!this.isTimeBuffered(h.startPTS,h.endPTS,t);return d&&this.removeFragment(c.body),d})}})}detectPartialFragments(e){const t=this.timeRanges;if(!t||e.frag.sn==="initSegment")return;const s=e.frag,i=Tt(s),r=this.fragments[i];if(!r||r.buffered&&s.gap)return;const n=!s.relurl;Object.keys(t).forEach(o=>{const c=s.elementaryStreams[o];if(!c)return;const l=t[o],h=n||c.partial===!0;r.range[o]=this.getBufferedTimes(s,e.part,h,l)}),r.loaded=null,Object.keys(r.range).length?(this.bufferedEnd(r,s),Zt(r)||this.removeParts(s.sn-1,s.type)):this.removeFragment(r.body)}bufferedEnd(e,t){e.buffered=!0,(e.body.endList=t.endList||e.body.endList)&&(this.endListFragments[e.body.type]=e)}removeParts(e,t){const s=this.activePartLists[t];s&&(this.activePartLists[t]=Er(s,i=>i.fragment.sn>=e))}fragBuffered(e,t){const s=Tt(e);let i=this.fragments[s];!i&&t&&(i=this.fragments[s]={body:e,appendedPTS:null,loaded:null,buffered:!1,range:Object.create(null)},e.gap&&(this.hasGaps=!0)),i&&(i.loaded=null,this.bufferedEnd(i,e))}getBufferedTimes(e,t,s,i){const r={time:[],partial:s},n=e.start,o=e.end,c=e.minEndPTS||o,l=e.maxStartPTS||n;for(let h=0;h=d&&c<=u){r.time.push({startPTS:Math.max(n,i.start(h)),endPTS:Math.min(o,i.end(h))});break}else if(nd){const f=Math.max(n,i.start(h)),g=Math.min(o,i.end(h));g>f&&(r.partial=!0,r.time.push({startPTS:f,endPTS:g}))}else if(o<=d)break}return r}getPartialFragment(e){let t=null,s,i,r,n=0;const{bufferPadding:o,fragments:c}=this;return Object.keys(c).forEach(l=>{const h=c[l];h&&Zt(h)&&(i=h.body.start-o,r=h.body.end+o,e>=i&&e<=r&&(s=Math.min(e-i,r-e),n<=s&&(t=h.body,n=s)))}),t}isEndListAppended(e){const t=this.endListFragments[e];return t!==void 0&&(t.buffered||Zt(t))}getState(e){const t=Tt(e),s=this.fragments[t];return s?s.buffered?Zt(s)?fe.PARTIAL:fe.OK:fe.APPENDING:fe.NOT_LOADED}isTimeBuffered(e,t,s){let i,r;for(let n=0;n=i&&t<=r)return!0;if(t<=i)return!1}return!1}onManifestLoading(){this.removeAllFragments()}onFragLoaded(e,t){if(t.frag.sn==="initSegment"||t.frag.bitrateTest)return;const s=t.frag,i=t.part?null:t,r=Tt(s);this.fragments[r]={body:s,appendedPTS:null,loaded:i,buffered:!1,range:Object.create(null)}}onBufferAppended(e,t){const{frag:s,part:i,timeRanges:r,type:n}=t;if(s.sn==="initSegment")return;const o=s.type;if(i){let l=this.activePartLists[o];l||(this.activePartLists[o]=l=[]),l.push(i)}this.timeRanges=r;const c=r[n];this.detectEvictedFragments(n,c,o,i)}onFragBuffered(e,t){this.detectPartialFragments(t)}hasFragment(e){const t=Tt(e);return!!this.fragments[t]}hasFragments(e){const{fragments:t}=this,s=Object.keys(t);if(!e)return s.length>0;for(let i=s.length;i--;){const r=t[s[i]];if((r==null?void 0:r.body.type)===e)return!0}return!1}hasParts(e){var t;return!!((t=this.activePartLists[e])!=null&&t.length)}removeFragmentsInRange(e,t,s,i,r){i&&!this.hasGaps||Object.keys(this.fragments).forEach(n=>{const o=this.fragments[n];if(!o)return;const c=o.body;c.type!==s||i&&!c.gap||c.starte&&(o.buffered||r)&&this.removeFragment(c)})}removeFragment(e){const t=Tt(e);e.clearElementaryStreamInfo();const s=this.activePartLists[e.type];if(s){const i=e.sn;this.activePartLists[e.type]=Er(s,r=>r.fragment.sn!==i)}delete this.fragments[t],e.endList&&delete this.endListFragments[e.type]}removeAllFragments(){var e;this.fragments=Object.create(null),this.endListFragments=Object.create(null),this.activePartLists=Object.create(null),this.hasGaps=!1;const t=(e=this.hls)==null||(e=e.latestLevelDetails)==null?void 0:e.partList;t&&t.forEach(s=>s.clearElementaryStreamInfo())}}function Zt(a){var e,t,s;return a.buffered&&!!(a.body.gap||(e=a.range.video)!=null&&e.partial||(t=a.range.audio)!=null&&t.partial||(s=a.range.audiovideo)!=null&&s.partial)}function Tt(a){return`${a.type}_${a.level}_${a.sn}`}function Er(a,e){return a.filter(t=>{const s=e(t);return s||t.clearElementaryStreamInfo(),s})}var ot={cbc:0,ctr:1};class cl{constructor(e,t,s){this.subtle=void 0,this.aesIV=void 0,this.aesMode=void 0,this.subtle=e,this.aesIV=t,this.aesMode=s}decrypt(e,t){switch(this.aesMode){case ot.cbc:return this.subtle.decrypt({name:"AES-CBC",iv:this.aesIV},t,e);case ot.ctr:return this.subtle.decrypt({name:"AES-CTR",counter:this.aesIV,length:64},t,e);default:throw new Error(`[AESCrypto] invalid aes mode ${this.aesMode}`)}}}function hl(a){const e=a.byteLength,t=e&&new DataView(a.buffer).getUint8(e-1);return t?a.slice(0,e-t):a}class dl{constructor(){this.rcon=[0,1,2,4,8,16,32,64,128,27,54],this.subMix=[new Uint32Array(256),new Uint32Array(256),new Uint32Array(256),new Uint32Array(256)],this.invSubMix=[new Uint32Array(256),new Uint32Array(256),new Uint32Array(256),new Uint32Array(256)],this.sBox=new Uint32Array(256),this.invSBox=new Uint32Array(256),this.key=new Uint32Array(0),this.ksRows=0,this.keySize=0,this.keySchedule=void 0,this.invKeySchedule=void 0,this.initTable()}uint8ArrayToUint32Array_(e){const t=new DataView(e),s=new Uint32Array(4);for(let i=0;i<4;i++)s[i]=t.getUint32(i*4);return s}initTable(){const e=this.sBox,t=this.invSBox,s=this.subMix,i=s[0],r=s[1],n=s[2],o=s[3],c=this.invSubMix,l=c[0],h=c[1],d=c[2],u=c[3],f=new Uint32Array(256);let g=0,y=0,p=0;for(p=0;p<256;p++)p<128?f[p]=p<<1:f[p]=p<<1^283;for(p=0;p<256;p++){let E=y^y<<1^y<<2^y<<3^y<<4;E=E>>>8^E&255^99,e[g]=E,t[E]=g;const T=f[g],S=f[T],v=f[S];let x=f[E]*257^E*16843008;i[g]=x<<24|x>>>8,r[g]=x<<16|x>>>16,n[g]=x<<8|x>>>24,o[g]=x,x=v*16843009^S*65537^T*257^g*16843008,l[E]=x<<24|x>>>8,h[E]=x<<16|x>>>16,d[E]=x<<8|x>>>24,u[E]=x,g?(g=T^f[f[f[v^T]]],y^=f[f[y]]):g=y=1}}expandKey(e){const t=this.uint8ArrayToUint32Array_(e);let s=!0,i=0;for(;i{const o=ArrayBuffer.isView(e)?e:new Uint8Array(e);this.softwareDecrypt(o,t,s,i);const c=this.flush();c?r(c.buffer):n(new Error("[softwareDecrypt] Failed to decrypt data"))}):this.webCryptoDecrypt(new Uint8Array(e),t,s,i)}softwareDecrypt(e,t,s,i){const{currentIV:r,currentResult:n,remainderData:o}=this;if(i!==ot.cbc||t.byteLength!==16)return re.warn("SoftwareDecrypt: can only handle AES-128-CBC"),null;this.logOnce("JS AES decrypt"),o&&(e=we(o,e),this.remainderData=null);const c=this.getValidChunk(e);if(!c.length)return null;r&&(s=r);let l=this.softwareDecrypter;l||(l=this.softwareDecrypter=new dl),l.expandKey(t);const h=n;return this.currentResult=l.decrypt(c.buffer,0,s),this.currentIV=c.slice(-16).buffer,h||null}webCryptoDecrypt(e,t,s,i){if(this.key!==t||!this.fastAesKey){if(!this.subtle)return Promise.resolve(this.onWebCryptoError(e,t,s,i));this.key=t,this.fastAesKey=new ul(this.subtle,t,i)}return this.fastAesKey.expandKey().then(r=>this.subtle?(this.logOnce("WebCrypto AES decrypt"),new cl(this.subtle,new Uint8Array(s),i).decrypt(e.buffer,r)):Promise.reject(new Error("web crypto not initialized"))).catch(r=>(re.warn(`[decrypter]: WebCrypto Error, disable WebCrypto API, ${r.name}: ${r.message}`),this.onWebCryptoError(e,t,s,i)))}onWebCryptoError(e,t,s,i){const r=this.enableSoftwareAES;if(r){this.useSoftware=!0,this.logEnabled=!0,this.softwareDecrypt(e,t,s,i);const n=this.flush();if(n)return n.buffer}throw new Error("WebCrypto"+(r?" and softwareDecrypt":"")+": failed to decrypt data")}getValidChunk(e){let t=e;const s=e.length-e.length%gl;return s!==e.length&&(t=e.slice(0,s),this.remainderData=e.slice(s)),t}logOnce(e){this.logEnabled&&(re.log(`[decrypter]: ${e}`),this.logEnabled=!1)}}const Tr=Math.pow(2,17);class ml{constructor(e){this.config=void 0,this.loader=null,this.partLoadTimeout=-1,this.config=e}destroy(){this.loader&&(this.loader.destroy(),this.loader=null)}abort(){this.loader&&this.loader.abort()}load(e,t){const s=e.url;if(!s)return Promise.reject(new Ze({type:Y.NETWORK_ERROR,details:R.FRAG_LOAD_ERROR,fatal:!1,frag:e,error:new Error(`Fragment does not have a ${s?"part list":"url"}`),networkDetails:null}));this.abort();const i=this.config,r=i.fLoader,n=i.loader;return new Promise((o,c)=>{if(this.loader&&this.loader.destroy(),e.gap)if(e.tagList.some(g=>g[0]==="GAP")){c(vr(e));return}else e.gap=!1;const l=this.loader=r?new r(i):new n(i),h=Sr(e);e.loader=l;const d=yr(i.fragLoadPolicy.default),u={loadPolicy:d,timeout:d.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0,highWaterMark:e.sn==="initSegment"?1/0:Tr};e.stats=l.stats;const f={onSuccess:(g,y,p,E)=>{this.resetLoader(e,l);let T=g.data;p.resetIV&&e.decryptdata&&(e.decryptdata.iv=new Uint8Array(T.slice(0,16)),T=T.slice(16)),o({frag:e,part:null,payload:T,networkDetails:E})},onError:(g,y,p,E)=>{this.resetLoader(e,l),c(new Ze({type:Y.NETWORK_ERROR,details:R.FRAG_LOAD_ERROR,fatal:!1,frag:e,response:ie({url:s,data:void 0},g),error:new Error(`HTTP Error ${g.code} ${g.text}`),networkDetails:p,stats:E}))},onAbort:(g,y,p)=>{this.resetLoader(e,l),c(new Ze({type:Y.NETWORK_ERROR,details:R.INTERNAL_ABORTED,fatal:!1,frag:e,error:new Error("Aborted"),networkDetails:p,stats:g}))},onTimeout:(g,y,p)=>{this.resetLoader(e,l),c(new Ze({type:Y.NETWORK_ERROR,details:R.FRAG_LOAD_TIMEOUT,fatal:!1,frag:e,error:new Error(`Timeout after ${u.timeout}ms`),networkDetails:p,stats:g}))}};t&&(f.onProgress=(g,y,p,E)=>t({frag:e,part:null,payload:p,networkDetails:E})),l.load(h,u,f)})}loadPart(e,t,s){this.abort();const i=this.config,r=i.fLoader,n=i.loader;return new Promise((o,c)=>{if(this.loader&&this.loader.destroy(),e.gap||t.gap){c(vr(e,t));return}const l=this.loader=r?new r(i):new n(i),h=Sr(e,t);e.loader=l;const d=yr(i.fragLoadPolicy.default),u={loadPolicy:d,timeout:d.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0,highWaterMark:Tr};t.stats=l.stats,l.load(h,u,{onSuccess:(f,g,y,p)=>{this.resetLoader(e,l),this.updateStatsFromPart(e,t);const E={frag:e,part:t,payload:f.data,networkDetails:p};s(E),o(E)},onError:(f,g,y,p)=>{this.resetLoader(e,l),c(new Ze({type:Y.NETWORK_ERROR,details:R.FRAG_LOAD_ERROR,fatal:!1,frag:e,part:t,response:ie({url:h.url,data:void 0},f),error:new Error(`HTTP Error ${f.code} ${f.text}`),networkDetails:y,stats:p}))},onAbort:(f,g,y)=>{e.stats.aborted=t.stats.aborted,this.resetLoader(e,l),c(new Ze({type:Y.NETWORK_ERROR,details:R.INTERNAL_ABORTED,fatal:!1,frag:e,part:t,error:new Error("Aborted"),networkDetails:y,stats:f}))},onTimeout:(f,g,y)=>{this.resetLoader(e,l),c(new Ze({type:Y.NETWORK_ERROR,details:R.FRAG_LOAD_TIMEOUT,fatal:!1,frag:e,part:t,error:new Error(`Timeout after ${u.timeout}ms`),networkDetails:y,stats:f}))}})})}updateStatsFromPart(e,t){const s=e.stats,i=t.stats,r=i.total;if(s.loaded+=i.loaded,r){const c=Math.round(e.duration/t.duration),l=Math.min(Math.round(s.loaded/r),c),d=(c-l)*Math.round(s.loaded/l);s.total=s.loaded+d}else s.total=Math.max(s.loaded,s.total);const n=s.loading,o=i.loading;n.start?n.first+=o.first-o.start:(n.start=o.start,n.first=o.first),n.end=o.end}resetLoader(e,t){e.loader=null,this.loader===t&&(self.clearTimeout(this.partLoadTimeout),this.loader=null),t.destroy()}}function Sr(a,e=null){const t=e||a,s={frag:a,part:e,responseType:"arraybuffer",url:t.url,headers:{},rangeStart:0,rangeEnd:0},i=t.byteRangeStartOffset,r=t.byteRangeEndOffset;if(B(i)&&B(r)){var n;let o=i,c=r;if(a.sn==="initSegment"&&pl((n=a.decryptdata)==null?void 0:n.method)){const l=r-i;l%16&&(c=r+(16-l%16)),i!==0&&(s.resetIV=!0,o=i-16)}s.rangeStart=o,s.rangeEnd=c}return s}function vr(a,e){const t=new Error(`GAP ${a.gap?"tag":"attribute"} found`),s={type:Y.MEDIA_ERROR,details:R.FRAG_GAP,fatal:!1,frag:a,error:t,networkDetails:null};return e&&(s.part=e),(e||a).stats.aborted=!0,new Ze(s)}function pl(a){return a==="AES-128"||a==="AES-256"}class Ze extends Error{constructor(e){super(e.error.message),this.data=void 0,this.data=e}}class Vn extends Oe{constructor(e,t){super(e,t),this._boundTick=void 0,this._tickTimer=null,this._tickInterval=null,this._tickCallCount=0,this._boundTick=this.tick.bind(this)}destroy(){this.onHandlerDestroying(),this.onHandlerDestroyed()}onHandlerDestroying(){this.clearNextTick(),this.clearInterval()}onHandlerDestroyed(){}hasInterval(){return!!this._tickInterval}hasNextTick(){return!!this._tickTimer}setInterval(e){return this._tickInterval?!1:(this._tickCallCount=0,this._tickInterval=self.setInterval(this._boundTick,e),!0)}clearInterval(){return this._tickInterval?(self.clearInterval(this._tickInterval),this._tickInterval=null,!0):!1}clearNextTick(){return this._tickTimer?(self.clearTimeout(this._tickTimer),this._tickTimer=null,!0):!1}tick(){this._tickCallCount++,this._tickCallCount===1&&(this.doTick(),this._tickCallCount>1&&this.tickImmediate(),this._tickCallCount=0)}tickImmediate(){this.clearNextTick(),this._tickTimer=self.setTimeout(this._boundTick,0)}doTick(){}}class ki{constructor(e,t,s,i=0,r=-1,n=!1){this.level=void 0,this.sn=void 0,this.part=void 0,this.id=void 0,this.size=void 0,this.partial=void 0,this.transmuxing=Jt(),this.buffering={audio:Jt(),video:Jt(),audiovideo:Jt()},this.level=e,this.sn=t,this.id=s,this.size=i,this.part=r,this.partial=n}}function Jt(){return{start:0,executeStart:0,executeEnd:0,end:0}}const xr={length:0,start:()=>0,end:()=>0};class X{static isBuffered(e,t){if(e){const s=X.getBuffered(e);for(let i=s.length;i--;)if(t>=s.start(i)&&t<=s.end(i))return!0}return!1}static bufferedRanges(e){if(e){const t=X.getBuffered(e);return X.timeRangesToArray(t)}return[]}static timeRangesToArray(e){const t=[];for(let s=0;s1&&e.sort((h,d)=>h.start-d.start||d.end-h.end);let i=-1,r=[];if(s)for(let h=0;h=e[h].start&&t<=e[h].end&&(i=h);const d=r.length;if(d){const u=r[d-1].end;e[h].start-uu&&(r[d-1].end=e[h].end):r.push(e[h])}else r.push(e[h])}else r=e;let n=0,o,c=t,l=t;for(let h=0;h=d&&t<=u&&(i=h),t+s>=d&&t{const i=s.substring(2,s.length-1),r=t==null?void 0:t[i];return r===void 0?(a.playlistParsingError||(a.playlistParsingError=new Error(`Missing preceding EXT-X-DEFINE tag for Variable Reference: "${i}"`)),s):r})}return e}function Ir(a,e,t){let s=a.variableList;s||(a.variableList=s={});let i,r;if("QUERYPARAM"in e){i=e.QUERYPARAM;try{const n=new self.URL(t).searchParams;if(n.has(i))r=n.get(i);else throw new Error(`"${i}" does not match any query parameter in URI: "${t}"`)}catch(n){a.playlistParsingError||(a.playlistParsingError=new Error(`EXT-X-DEFINE QUERYPARAM: ${n.message}`))}}else i=e.NAME,r=e.VALUE;i in s?a.playlistParsingError||(a.playlistParsingError=new Error(`EXT-X-DEFINE duplicate Variable Name declarations: "${i}"`)):s[i]=r||""}function yl(a,e,t){const s=e.IMPORT;if(t&&s in t){let i=a.variableList;i||(a.variableList=i={}),i[s]=t[s]}else a.playlistParsingError||(a.playlistParsingError=new Error(`EXT-X-DEFINE IMPORT attribute not found in Multivariant Playlist: "${s}"`))}const El=/^(\d+)x(\d+)$/,Lr=/(.+?)=(".*?"|.*?)(?:,|$)/g;class le{constructor(e,t){typeof e=="string"&&(e=le.parseAttrList(e,t)),ne(this,e)}get clientAttrs(){return Object.keys(this).filter(e=>e.substring(0,2)==="X-")}decimalInteger(e){const t=parseInt(this[e],10);return t>Number.MAX_SAFE_INTEGER?1/0:t}hexadecimalInteger(e){if(this[e]){let t=(this[e]||"0x").slice(2);t=(t.length&1?"0":"")+t;const s=new Uint8Array(t.length/2);for(let i=0;iNumber.MAX_SAFE_INTEGER?1/0:t}decimalFloatingPoint(e){return parseFloat(this[e])}optionalFloat(e,t){const s=this[e];return s?parseFloat(s):t}enumeratedString(e){return this[e]}enumeratedStringList(e,t){const s=this[e];return(s?s.split(/[ ,]+/):[]).reduce((i,r)=>(i[r.toLowerCase()]=!0,i),t)}bool(e){return this[e]==="YES"}decimalResolution(e){const t=El.exec(this[e]);if(t!==null)return{width:parseInt(t[1],10),height:parseInt(t[2],10)}}static parseAttrList(e,t){let s;const i={},r='"';for(Lr.lastIndex=0;(s=Lr.exec(e))!==null;){const n=s[1].trim();let o=s[2];const c=o.indexOf(r)===0&&o.lastIndexOf(r)===o.length-1;let l=!1;if(c)o=o.slice(1,-1);else switch(n){case"IV":case"SCTE35-CMD":case"SCTE35-IN":case"SCTE35-OUT":l=!0}if(t&&(c||l))o=ci(t,o);else if(!l&&!c)switch(n){case"CLOSED-CAPTIONS":if(o==="NONE")break;case"ALLOWED-CPC":case"CLASS":case"ASSOC-LANGUAGE":case"AUDIO":case"BYTERANGE":case"CHANNELS":case"CHARACTERISTICS":case"CODECS":case"DATA-ID":case"END-DATE":case"GROUP-ID":case"ID":case"IMPORT":case"INSTREAM-ID":case"KEYFORMAT":case"KEYFORMATVERSIONS":case"LANGUAGE":case"NAME":case"PATHWAY-ID":case"QUERYPARAM":case"RECENTLY-REMOVED-DATERANGES":case"SERVER-URI":case"STABLE-RENDITION-ID":case"STABLE-VARIANT-ID":case"START-DATE":case"SUBTITLES":case"SUPPLEMENTAL-CODECS":case"URI":case"VALUE":case"VIDEO":case"X-ASSET-LIST":case"X-ASSET-URI":re.warn(`${e}: attribute ${n} is missing quotes`)}i[n]=o}return i}}const Tl="com.apple.hls.interstitial";function Sl(a){return a!=="ID"&&a!=="CLASS"&&a!=="CUE"&&a!=="START-DATE"&&a!=="DURATION"&&a!=="END-DATE"&&a!=="END-ON-NEXT"}function vl(a){return a==="SCTE35-OUT"||a==="SCTE35-IN"||a==="SCTE35-CMD"}class Yn{constructor(e,t,s=0){var i;if(this.attr=void 0,this.tagAnchor=void 0,this.tagOrder=void 0,this._startDate=void 0,this._endDate=void 0,this._dateAtEnd=void 0,this._cue=void 0,this._badValueForSameId=void 0,this.tagAnchor=(t==null?void 0:t.tagAnchor)||null,this.tagOrder=(i=t==null?void 0:t.tagOrder)!=null?i:s,t){const r=t.attr;for(const n in r)if(Object.prototype.hasOwnProperty.call(e,n)&&e[n]!==r[n]){re.warn(`DATERANGE tag attribute: "${n}" does not match for tags with ID: "${e.ID}"`),this._badValueForSameId=n;break}e=ne(new le({}),r,e)}if(this.attr=e,t?(this._startDate=t._startDate,this._cue=t._cue,this._endDate=t._endDate,this._dateAtEnd=t._dateAtEnd):this._startDate=new Date(e["START-DATE"]),"END-DATE"in this.attr){const r=(t==null?void 0:t.endDate)||new Date(this.attr["END-DATE"]);B(r.getTime())&&(this._endDate=r)}}get id(){return this.attr.ID}get class(){return this.attr.CLASS}get cue(){const e=this._cue;return e===void 0?this._cue=this.attr.enumeratedStringList(this.attr.CUE?"CUE":"X-CUE",{pre:!1,post:!1,once:!1}):e}get startTime(){const{tagAnchor:e}=this;return e===null||e.programDateTime===null?(re.warn(`Expected tagAnchor Fragment with PDT set for DateRange "${this.id}": ${e}`),NaN):e.start+(this.startDate.getTime()-e.programDateTime)/1e3}get startDate(){return this._startDate}get endDate(){const e=this._endDate||this._dateAtEnd;if(e)return e;const t=this.duration;return t!==null?this._dateAtEnd=new Date(this._startDate.getTime()+t*1e3):null}get duration(){if("DURATION"in this.attr){const e=this.attr.decimalFloatingPoint("DURATION");if(B(e))return e}else if(this._endDate)return(this._endDate.getTime()-this._startDate.getTime())/1e3;return null}get plannedDuration(){return"PLANNED-DURATION"in this.attr?this.attr.decimalFloatingPoint("PLANNED-DURATION"):null}get endOnNext(){return this.attr.bool("END-ON-NEXT")}get isInterstitial(){return this.class===Tl}get isValid(){return!!this.id&&!this._badValueForSameId&&B(this.startDate.getTime())&&(this.duration===null||this.duration>=0)&&(!this.endOnNext||!!this.class)&&(!this.attr.CUE||!this.cue.pre&&!this.cue.post||this.cue.pre!==this.cue.post)&&(!this.isInterstitial||"X-ASSET-URI"in this.attr||"X-ASSET-LIST"in this.attr)}}const xl=10;class Al{constructor(e){this.PTSKnown=!1,this.alignedSliding=!1,this.averagetargetduration=void 0,this.endCC=0,this.endSN=0,this.fragments=void 0,this.fragmentHint=void 0,this.partList=null,this.dateRanges=void 0,this.dateRangeTagCount=0,this.live=!0,this.requestScheduled=-1,this.ageHeader=0,this.advancedDateTime=void 0,this.updated=!0,this.advanced=!0,this.misses=0,this.startCC=0,this.startSN=0,this.startTimeOffset=null,this.targetduration=0,this.totalduration=0,this.type=null,this.url=void 0,this.m3u8="",this.version=null,this.canBlockReload=!1,this.canSkipUntil=0,this.canSkipDateRanges=!1,this.skippedSegments=0,this.recentlyRemovedDateranges=void 0,this.partHoldBack=0,this.holdBack=0,this.partTarget=0,this.preloadHint=void 0,this.renditionReports=void 0,this.tuneInGoal=0,this.deltaUpdateFailed=void 0,this.driftStartTime=0,this.driftEndTime=0,this.driftStart=0,this.driftEnd=0,this.encryptedFragments=void 0,this.playlistParsingError=null,this.variableList=null,this.hasVariableRefs=!1,this.appliedTimelineOffset=void 0,this.fragments=[],this.encryptedFragments=[],this.dateRanges={},this.url=e}reloaded(e){if(!e){this.advanced=!0,this.updated=!0;return}const t=this.lastPartSn-e.lastPartSn,s=this.lastPartIndex-e.lastPartIndex;this.updated=this.endSN!==e.endSN||!!s||!!t||!this.live,this.advanced=this.endSN>e.endSN||t>0||t===0&&s>0,this.updated||this.advanced?this.misses=Math.floor(e.misses*.6):this.misses=e.misses+1}hasKey(e){return this.encryptedFragments.some(t=>{let s=t.decryptdata;return s||(t.setKeyFormat(e.keyFormat),s=t.decryptdata),!!s&&e.matches(s)})}get hasProgramDateTime(){return this.fragments.length?B(this.fragments[this.fragments.length-1].programDateTime):!1}get levelTargetDuration(){return this.averagetargetduration||this.targetduration||xl}get drift(){const e=this.driftEndTime-this.driftStartTime;return e>0?(this.driftEnd-this.driftStart)*1e3/e:1}get edge(){return this.partEnd||this.fragmentEnd}get partEnd(){var e;return(e=this.partList)!=null&&e.length?this.partList[this.partList.length-1].end:this.fragmentEnd}get fragmentEnd(){return this.fragments.length?this.fragments[this.fragments.length-1].end:0}get fragmentStart(){return this.fragments.length?this.fragments[0].start:0}get age(){return this.advancedDateTime?Math.max(Date.now()-this.advancedDateTime,0)/1e3:0}get lastPartIndex(){var e;return(e=this.partList)!=null&&e.length?this.partList[this.partList.length-1].index:-1}get maxPartIndex(){const e=this.partList;if(e){const t=this.lastPartIndex;if(t!==-1){for(let s=e.length;s--;)if(e[s].index>t)return e[s].index;return t}}return 0}get lastPartSn(){var e;return(e=this.partList)!=null&&e.length?this.partList[this.partList.length-1].fragment.sn:this.endSN}get expired(){if(this.live&&this.age&&this.misses<3){const e=this.partEnd-this.fragmentStart;return this.age>Math.max(e,this.totalduration)+this.levelTargetDuration}return!1}}function Is(a,e){return a.length===e.length?!a.some((t,s)=>t!==e[s]):!1}function Rr(a,e){return!a&&!e?!0:!a||!e?!1:Is(a,e)}function _t(a){return a==="AES-128"||a==="AES-256"||a==="AES-256-CTR"}function wi(a){switch(a){case"AES-128":case"AES-256":return ot.cbc;case"AES-256-CTR":return ot.ctr;default:throw new Error(`invalid full segment method ${a}`)}}function Oi(a){return Uint8Array.from(atob(a),e=>e.charCodeAt(0))}function hi(a){return Uint8Array.from(unescape(encodeURIComponent(a)),e=>e.charCodeAt(0))}function Il(a){const e=hi(a).subarray(0,16),t=new Uint8Array(16);return t.set(e,16-e.length),t}function Wn(a){const e=function(s,i,r){const n=s[i];s[i]=s[r],s[r]=n};e(a,0,3),e(a,1,2),e(a,4,5),e(a,6,7)}function qn(a){const e=a.split(":");let t=null;if(e[0]==="data"&&e.length===2){const s=e[1].split(";"),i=s[s.length-1].split(",");if(i.length===2){const r=i[0]==="base64",n=i[1];r?(s.splice(-1,1),t=Oi(n)):t=Il(n)}}return t}const Ls=typeof self<"u"?self:void 0;var ce={CLEARKEY:"org.w3.clearkey",FAIRPLAY:"com.apple.fps",PLAYREADY:"com.microsoft.playready",WIDEVINE:"com.widevine.alpha"},Ee={CLEARKEY:"org.w3.clearkey",FAIRPLAY:"com.apple.streamingkeydelivery",PLAYREADY:"com.microsoft.playready",WIDEVINE:"urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"};function us(a){switch(a){case Ee.FAIRPLAY:return ce.FAIRPLAY;case Ee.PLAYREADY:return ce.PLAYREADY;case Ee.WIDEVINE:return ce.WIDEVINE;case Ee.CLEARKEY:return ce.CLEARKEY}}function Bs(a){switch(a){case ce.FAIRPLAY:return Ee.FAIRPLAY;case ce.PLAYREADY:return Ee.PLAYREADY;case ce.WIDEVINE:return Ee.WIDEVINE;case ce.CLEARKEY:return Ee.CLEARKEY}}function Mt(a){const{drmSystems:e,widevineLicenseUrl:t}=a,s=e?[ce.FAIRPLAY,ce.WIDEVINE,ce.PLAYREADY,ce.CLEARKEY].filter(i=>!!e[i]):[];return!s[ce.WIDEVINE]&&t&&s.push(ce.WIDEVINE),s}const jn=function(a){return Ls!=null&&(a=Ls.navigator)!=null&&a.requestMediaKeySystemAccess?self.navigator.requestMediaKeySystemAccess.bind(self.navigator):null}();function Ll(a,e,t,s){let i;switch(a){case ce.FAIRPLAY:i=["cenc","sinf"];break;case ce.WIDEVINE:case ce.PLAYREADY:i=["cenc"];break;case ce.CLEARKEY:i=["cenc","keyids"];break;default:throw new Error(`Unknown key-system: ${a}`)}return Rl(i,e,t,s)}function Rl(a,e,t,s){return[{initDataTypes:a,persistentState:s.persistentState||"optional",distinctiveIdentifier:s.distinctiveIdentifier||"optional",sessionTypes:s.sessionTypes||[s.sessionType||"temporary"],audioCapabilities:e.map(r=>({contentType:`audio/mp4; codecs=${r}`,robustness:s.audioRobustness||"",encryptionScheme:s.audioEncryptionScheme||null})),videoCapabilities:t.map(r=>({contentType:`video/mp4; codecs=${r}`,robustness:s.videoRobustness||"",encryptionScheme:s.videoEncryptionScheme||null}))}]}function bl(a){var e;return!!a&&(a.sessionType==="persistent-license"||!!((e=a.sessionTypes)!=null&&e.some(t=>t==="persistent-license")))}function Xn(a){const e=new Uint16Array(a.buffer,a.byteOffset,a.byteLength/2),t=String.fromCharCode.apply(null,Array.from(e)),s=t.substring(t.indexOf("<"),t.length),n=new DOMParser().parseFromString(s,"text/xml").getElementsByTagName("KID")[0];if(n){const o=n.childNodes[0]?n.childNodes[0].nodeValue:n.getAttribute("VALUE");if(o){const c=Oi(o).subarray(0,16);return Wn(c),c}}return null}let St={};class nt{static clearKeyUriToKeyIdMap(){St={}}static setKeyIdForUri(e,t){St[e]=t}static addKeyIdForUri(e){const t=Object.keys(St).length%Number.MAX_SAFE_INTEGER,s=new Uint8Array(16);return new DataView(s.buffer,12,4).setUint32(0,t),St[e]=s,s}constructor(e,t,s,i=[1],r=null,n){this.uri=void 0,this.method=void 0,this.keyFormat=void 0,this.keyFormatVersions=void 0,this.encrypted=void 0,this.isCommonEncryption=void 0,this.iv=null,this.key=null,this.keyId=null,this.pssh=null,this.method=e,this.uri=t,this.keyFormat=s,this.keyFormatVersions=i,this.iv=r,this.encrypted=e?e!=="NONE":!1,this.isCommonEncryption=this.encrypted&&!_t(e),n!=null&&n.startsWith("0x")&&(this.keyId=new Uint8Array(An(n)))}matches(e){return e.uri===this.uri&&e.method===this.method&&e.encrypted===this.encrypted&&e.keyFormat===this.keyFormat&&Is(e.keyFormatVersions,this.keyFormatVersions)&&Rr(e.iv,this.iv)&&Rr(e.keyId,this.keyId)}isSupported(){if(this.method){if(_t(this.method)||this.method==="NONE")return!0;if(this.keyFormat==="identity")return this.method==="SAMPLE-AES";switch(this.keyFormat){case Ee.FAIRPLAY:case Ee.WIDEVINE:case Ee.PLAYREADY:case Ee.CLEARKEY:return["SAMPLE-AES","SAMPLE-AES-CENC","SAMPLE-AES-CTR"].indexOf(this.method)!==-1}}return!1}getDecryptData(e,t){if(!this.encrypted||!this.uri)return null;if(_t(this.method)){let r=this.iv;return r||(typeof e!="number"&&(re.warn(`missing IV for initialization segment with method="${this.method}" - compliance issue`),e=0),r=Dl(e)),new nt(this.method,this.uri,"identity",this.keyFormatVersions,r)}if(this.keyId){const r=St[this.uri];if(r&&!Is(this.keyId,r)&&nt.setKeyIdForUri(this.uri,this.keyId),this.pssh)return this}const s=qn(this.uri);if(s)switch(this.keyFormat){case Ee.WIDEVINE:if(this.pssh=s,!this.keyId){const r=ko(s.buffer);if(r.length){var i;const n=r[0];this.keyId=(i=n.kids)!=null&&i.length?n.kids[0]:null}}this.keyId||(this.keyId=br(t));break;case Ee.PLAYREADY:{const r=new Uint8Array([154,4,240,121,152,64,66,134,171,146,230,91,224,136,95,149]);this.pssh=Po(r,null,s),this.keyId=Xn(s);break}default:{let r=s.subarray(0,16);if(r.length!==16){const n=new Uint8Array(16);n.set(r,16-r.length),r=n}this.keyId=r;break}}if(!this.keyId||this.keyId.byteLength!==16){let r;r=_l(t),r||(r=br(t),r||(r=St[this.uri])),r&&(this.keyId=r,nt.setKeyIdForUri(this.uri,r))}return this}}function _l(a){const e=a==null?void 0:a[Ee.WIDEVINE];return e?e.keyId:null}function br(a){const e=a==null?void 0:a[Ee.PLAYREADY];if(e){const t=qn(e.uri);if(t)return Xn(t)}return null}function Dl(a){const e=new Uint8Array(16);for(let t=12;t<16;t++)e[t]=a>>8*(15-t)&255;return e}const _r=/#EXT-X-STREAM-INF:([^\r\n]*)(?:[\r\n](?:#[^\r\n]*)?)*([^\r\n]+)|#EXT-X-(SESSION-DATA|SESSION-KEY|DEFINE|CONTENT-STEERING|START):([^\r\n]*)[\r\n]+/g,Dr=/#EXT-X-MEDIA:(.*)/g,Cl=/^#EXT(?:INF|-X-TARGETDURATION):/m,$s=new RegExp([/#EXTINF:\s*(\d*(?:\.\d+)?)(?:,(.*)\s+)?/.source,/(?!#) *(\S[^\r\n]*)/.source,/#.*/.source].join("|"),"g"),Pl=new RegExp([/#EXT-X-(PROGRAM-DATE-TIME|BYTERANGE|DATERANGE|DEFINE|KEY|MAP|PART|PART-INF|PLAYLIST-TYPE|PRELOAD-HINT|RENDITION-REPORT|SERVER-CONTROL|SKIP|START):(.+)/.source,/#EXT-X-(BITRATE|DISCONTINUITY-SEQUENCE|MEDIA-SEQUENCE|TARGETDURATION|VERSION): *(\d+)/.source,/#EXT-X-(DISCONTINUITY|ENDLIST|GAP|INDEPENDENT-SEGMENTS)/.source,/(#)([^:]*):(.*)/.source,/(#)(.*)(?:.*)\r?\n?/.source].join("|"));class qe{static findGroup(e,t){for(let s=0;s0&&r.length({id:l.attrs.AUDIO,audioCodec:l.audioCodec})),SUBTITLES:n.map(l=>({id:l.attrs.SUBTITLES,textCodec:l.textCodec})),"CLOSED-CAPTIONS":[]};let c=0;for(Dr.lastIndex=0;(i=Dr.exec(e))!==null;){const l=new le(i[1],s),h=l.TYPE;if(h){const d=o[h],u=r[h]||[];r[h]=u;const f=l.LANGUAGE,g=l["ASSOC-LANGUAGE"],y=l.CHANNELS,p=l.CHARACTERISTICS,E=l["INSTREAM-ID"],T={attrs:l,bitrate:0,id:c++,groupId:l["GROUP-ID"]||"",name:l.NAME||f||"",type:h,default:l.bool("DEFAULT"),autoselect:l.bool("AUTOSELECT"),forced:l.bool("FORCED"),lang:f,url:l.URI?qe.resolve(l.URI,t):""};if(g&&(T.assocLang=g),y&&(T.channels=y),p&&(T.characteristics=p),E&&(T.instreamId=E),d!=null&&d.length){const S=qe.findGroup(d,T.groupId)||d[0];wr(T,S,"audioCodec"),wr(T,S,"textCodec")}u.push(T)}}return r}static parseLevelPlaylist(e,t,s,i,r,n){var o;const c={url:t},l=new Al(t),h=l.fragments,d=[];let u=null,f=0,g=0,y=0,p=0,E=0,T=null,S=new Fs(i,c),v,x,D,A=-1,_=!1,b=null,I;if($s.lastIndex=0,l.m3u8=e,l.hasVariableRefs=Ar(e),((o=$s.exec(e))==null?void 0:o[0])!=="#EXTM3U")return l.playlistParsingError=new Error("Missing format identifier #EXTM3U"),l;for(;(v=$s.exec(e))!==null;){_&&(_=!1,S=new Fs(i,c),S.playlistOffset=y,S.setStart(y),S.sn=f,S.cc=p,E&&(S.bitrate=E),S.level=s,u&&(S.initSegment=u,u.rawProgramDateTime&&(S.rawProgramDateTime=u.rawProgramDateTime,u.rawProgramDateTime=null),b&&(S.setByteRange(b),b=null)));const W=v[1];if(W){S.duration=parseFloat(W);const K=(" "+v[2]).slice(1);S.title=K||null,S.tagList.push(K?["INF",W,K]:["INF",W])}else if(v[3]){if(B(S.duration)){S.playlistOffset=y,S.setStart(y),D&&Fr(S,D,l),S.sn=f,S.level=s,S.cc=p,h.push(S);const K=(" "+v[3]).slice(1);S.relurl=ci(l,K),di(S,T,d),T=S,y+=S.duration,f++,g=0,_=!0}}else{if(v=v[0].match(Pl),!v){re.warn("No matches on slow regex match for level playlist!");continue}for(x=1;x0&&Mr(l,K,v),f=l.startSN=parseInt(w);break;case"SKIP":{l.skippedSegments&&ze(l,K,v);const $=new le(w,l),H=$.decimalInteger("SKIPPED-SEGMENTS");if(B(H)){l.skippedSegments+=H;for(let O=H;O--;)h.push(null);f+=H}const q=$.enumeratedString("RECENTLY-REMOVED-DATERANGES");q&&(l.recentlyRemovedDateranges=(l.recentlyRemovedDateranges||[]).concat(q.split(" ")));break}case"TARGETDURATION":l.targetduration!==0&&ze(l,K,v),l.targetduration=Math.max(parseInt(w),1);break;case"VERSION":l.version!==null&&ze(l,K,v),l.version=parseInt(w);break;case"INDEPENDENT-SEGMENTS":break;case"ENDLIST":l.live||ze(l,K,v),l.live=!1;break;case"#":(w||V)&&S.tagList.push(V?[w,V]:[w]);break;case"DISCONTINUITY":p++,S.tagList.push(["DIS"]);break;case"GAP":S.gap=!0,S.tagList.push([K]);break;case"BITRATE":S.tagList.push([K,w]),E=parseInt(w)*1e3,B(E)?S.bitrate=E:E=0;break;case"DATERANGE":{const $=new le(w,l),H=new Yn($,l.dateRanges[$.ID],l.dateRangeTagCount);l.dateRangeTagCount++,H.isValid||l.skippedSegments?l.dateRanges[H.id]=H:re.warn(`Ignoring invalid DATERANGE tag: "${w}"`),S.tagList.push(["EXT-X-DATERANGE",w]);break}case"DEFINE":{{const $=new le(w,l);"IMPORT"in $?yl(l,$,n):Ir(l,$,t)}break}case"DISCONTINUITY-SEQUENCE":l.startCC!==0?ze(l,K,v):h.length>0&&Mr(l,K,v),l.startCC=p=parseInt(w);break;case"KEY":{const $=Cr(w,t,l);if($.isSupported()){if($.method==="NONE"){D=void 0;break}D||(D={});const H=D[$.keyFormat];H!=null&&H.matches($)||(H&&(D=ne({},D)),D[$.keyFormat]=$)}else re.warn(`[Keys] Ignoring unsupported EXT-X-KEY tag: "${w}"`);break}case"START":l.startTimeOffset=Pr(w);break;case"MAP":{const $=new le(w,l);if(S.duration){const H=new Fs(i,c);Or(H,$,s,D),u=H,S.initSegment=u,u.rawProgramDateTime&&!S.rawProgramDateTime&&(S.rawProgramDateTime=u.rawProgramDateTime)}else{const H=S.byteRangeEndOffset;if(H){const q=S.byteRangeStartOffset;b=`${H-q}@${q}`}else b=null;Or(S,$,s,D),u=S,_=!0}u.cc=p;break}case"SERVER-CONTROL":{I&&ze(l,K,v),I=new le(w),l.canBlockReload=I.bool("CAN-BLOCK-RELOAD"),l.canSkipUntil=I.optionalFloat("CAN-SKIP-UNTIL",0),l.canSkipDateRanges=l.canSkipUntil>0&&I.bool("CAN-SKIP-DATERANGES"),l.partHoldBack=I.optionalFloat("PART-HOLD-BACK",0),l.holdBack=I.optionalFloat("HOLD-BACK",0);break}case"PART-INF":{l.partTarget&&ze(l,K,v);const $=new le(w);l.partTarget=$.decimalFloatingPoint("PART-TARGET");break}case"PART":{let $=l.partList;$||($=l.partList=[]);const H=g>0?$[$.length-1]:void 0,q=g++,O=new le(w,l),F=new yo(O,S,c,q,H);$.push(F),S.duration+=F.duration;break}case"PRELOAD-HINT":{const $=new le(w,l);l.preloadHint=$;break}case"RENDITION-REPORT":{const $=new le(w,l);l.renditionReports=l.renditionReports||[],l.renditionReports.push($);break}default:re.warn(`line parsed but not handled: ${v}`);break}}}T&&!T.relurl?(h.pop(),y-=T.duration,l.partList&&(l.fragmentHint=T)):l.partList&&(di(S,T,d),S.cc=p,l.fragmentHint=S,D&&Fr(S,D,l)),l.targetduration||(l.playlistParsingError=new Error("Missing Target Duration"));const P=h.length,M=h[0],G=h[P-1];if(y+=l.skippedSegments*l.targetduration,y>0&&P&&G){l.averagetargetduration=y/P;const W=G.sn;l.endSN=W!=="initSegment"?W:0,l.live||(G.endList=!0),A>0&&(wl(h,A),M&&d.unshift(M))}return l.fragmentHint&&(y+=l.fragmentHint.duration),l.totalduration=y,d.length&&l.dateRangeTagCount&&M&&zn(d,l),l.endCC=p,l}}function zn(a,e){let t=a.length;if(!t)if(e.hasProgramDateTime){const o=e.fragments[e.fragments.length-1];a.push(o),t++}else return;const s=a[t-1],i=e.live?1/0:e.totalduration,r=Object.keys(e.dateRanges);for(let o=r.length;o--;){const c=e.dateRanges[r[o]],l=c.startDate.getTime();c.tagAnchor=s.ref;for(let h=t;h--;){var n;if(((n=a[h])==null?void 0:n.sn)=o||s===0){var n;const c=(((n=t[s+1])==null?void 0:n.start)||i)-r.start;if(e<=o+c*1e3){const l=t[s].sn-a.startSN;if(l<0)return-1;const h=a.fragments;if(h.length>t.length){const u=(t[s+1]||h[h.length-1]).sn-a.startSN;for(let f=u;f>l;f--){const g=h[f].programDateTime;if(e>=g&&es);["video","audio","text"].forEach(s=>{const i=t.filter(r=>_i(r,s));i.length&&(e[`${s}Codec`]=i.map(r=>r.split("/")[0]).join(","),t=t.filter(r=>i.indexOf(r)===-1))}),e.unknownCodecs=t}function wr(a,e,t){const s=e[t];s&&(a[t]=s)}function wl(a,e){let t=a[e];for(let s=e;s--;){const i=a[s];if(!i)return;i.programDateTime=t.programDateTime-i.duration*1e3,t=i}}function di(a,e,t){a.rawProgramDateTime?t.push(a):e!=null&&e.programDateTime&&(a.programDateTime=e.endProgramDateTime)}function Or(a,e,t,s){a.relurl=e.URI,e.BYTERANGE&&a.setByteRange(e.BYTERANGE),a.level=t,a.sn="initSegment",s&&(a.levelkeys=s),a.initSegment=null}function Fr(a,e,t){a.levelkeys=e;const{encryptedFragments:s}=t;(!s.length||s[s.length-1].levelkeys!==e)&&Object.keys(e).some(i=>e[i].isCommonEncryption)&&s.push(a)}function ze(a,e,t){a.playlistParsingError=new Error(`#EXT-X-${e} must not appear more than once (${t[0]})`)}function Mr(a,e,t){a.playlistParsingError=new Error(`#EXT-X-${e} must appear before the first Media Segment (${t[0]})`)}function Us(a,e){const t=e.startPTS;if(B(t)){let s=0,i;e.sn>a.sn?(s=t-a.start,i=a):(s=a.start-t,i=e),i.duration!==s&&i.setDuration(s)}else e.sn>a.sn?a.cc===e.cc&&a.minEndPTS?e.setStart(a.start+(a.minEndPTS-a.start)):e.setStart(a.start+a.duration):e.setStart(Math.max(a.start-e.duration,0))}function Qn(a,e,t,s,i,r,n){s-t<=0&&(n.warn("Fragment should have a positive duration",e),s=t+e.duration,r=i+e.duration);let c=t,l=s;const h=e.startPTS,d=e.endPTS;if(B(h)){const E=Math.abs(h-t);a&&E>a.totalduration?n.warn(`media timestamps and playlist times differ by ${E}s for level ${e.level} ${a.url}`):B(e.deltaPTS)?e.deltaPTS=Math.max(E,e.deltaPTS):e.deltaPTS=E,c=Math.max(t,h),t=Math.min(t,h),i=e.startDTS!==void 0?Math.min(i,e.startDTS):i,l=Math.min(s,d),s=Math.max(s,d),r=e.endDTS!==void 0?Math.max(r,e.endDTS):r}const u=t-e.start;e.start!==0&&e.setStart(t),e.setDuration(s-e.start),e.startPTS=t,e.maxStartPTS=c,e.startDTS=i,e.endPTS=s,e.minEndPTS=l,e.endDTS=r;const f=e.sn;if(!a||fa.endSN)return 0;let g;const y=f-a.startSN,p=a.fragments;for(p[y]=e,g=y;g>0;g--)Us(p[g],p[g-1]);for(g=y;g=0;h--){const d=i[h].initSegment;if(d){s=d;break}}a.fragmentHint&&delete a.fragmentHint.endPTS;let r;Nl(a,e,(h,d,u,f)=>{if((!e.startCC||e.skippedSegments)&&d.cc!==h.cc){const g=h.cc-d.cc;for(let y=u;y{var d;h&&(!h.initSegment||h.initSegment.relurl===((d=s)==null?void 0:d.relurl))&&(h.initSegment=s)}),e.skippedSegments){if(e.deltaUpdateFailed=n.some(h=>!h),e.deltaUpdateFailed){t.warn("[level-helper] Previous playlist missing segments skipped in delta playlist");for(let h=e.skippedSegments;h--;)n.shift();e.startSN=n[0].sn}else{e.canSkipDateRanges&&(e.dateRanges=Fl(a.dateRanges,e,t));const h=a.fragments.filter(d=>d.rawProgramDateTime);if(a.hasProgramDateTime&&!e.hasProgramDateTime)for(let d=1;d{d.elementaryStreams=h.elementaryStreams,d.stats=h.stats}),r?Qn(e,r,r.startPTS,r.endPTS,r.startDTS,r.endDTS,t):Zn(a,e),n.length&&(e.totalduration=e.edge-n[0].start),e.driftStartTime=a.driftStartTime,e.driftStart=a.driftStart;const l=e.advancedDateTime;if(e.advanced&&l){const h=e.edge;e.driftStart||(e.driftStartTime=l,e.driftStart=h),e.driftEndTime=l,e.driftEnd=h}else e.driftEndTime=a.driftEndTime,e.driftEnd=a.driftEnd,e.advancedDateTime=a.advancedDateTime;e.requestScheduled===-1&&(e.requestScheduled=a.requestScheduled)}function Fl(a,e,t){const{dateRanges:s,recentlyRemovedDateranges:i}=e,r=ne({},a);i&&i.forEach(c=>{delete r[c]});const o=Object.keys(r).length;return o?(Object.keys(s).forEach(c=>{const l=r[c],h=new Yn(s[c].attr,l);h.isValid?(r[c]=h,l||(h.tagOrder+=o)):t.warn(`Ignoring invalid Playlist Delta Update DATERANGE tag: "${oe(s[c].attr)}"`)}),r):s}function Ml(a,e,t){if(a&&e){let s=0;for(let i=0,r=a.length;i<=r;i++){const n=a[i],o=e[i+s];n&&o&&n.index===o.index&&n.fragment.sn===o.fragment.sn?t(n,o):s--}}}function Nl(a,e,t){const s=e.skippedSegments,i=Math.max(a.startSN,e.startSN)-e.startSN,r=(a.fragmentHint?1:0)+(s?e.endSN:Math.min(a.endSN,e.endSN))-e.startSN,n=e.startSN-a.startSN,o=e.fragmentHint?e.fragments.concat(e.fragmentHint):e.fragments,c=a.fragmentHint?a.fragments.concat(a.fragmentHint):a.fragments;for(let l=i;l<=r;l++){const h=c[n+l];let d=o[l];if(s&&!d&&h&&(d=e.fragments[l]=h),h&&d){t(h,d,l,o);const u=h.relurl,f=d.relurl;if(u&&Bl(u,f)){e.playlistParsingError=Nr(`media sequence mismatch ${d.sn}:`,a,e,h,d);return}else if(h.cc!==d.cc){e.playlistParsingError=Nr(`discontinuity sequence mismatch (${h.cc}!=${d.cc})`,a,e,h,d);return}}}}function Nr(a,e,t,s,i){return new Error(`${a} ${i.url} Playlist starting @${e.startSN} ${e.m3u8} Playlist starting @${t.startSN} -${t.m3u8}`)}function Xn(a,e,t=!0){const s=e.startSN+e.skippedSegments-a.startSN,i=a.fragments,r=s>=0;let n=0;if(r&&se){const r=s[s.length-1].duration*1e3;r{var s;(s=e.details)==null||s.fragments.forEach(i=>{i.level=t,i.initSegment&&(i.initSegment.level=t)})})}function Fl(a,e){return a!==e&&e?Nr(a)!==Nr(e):!1}function Nr(a){return a.replace(/\?[^?]*$/,"")}function Nt(a,e){for(let s=0,i=a.length;sa.startCC)}function Br(a,e){const t=a.start+e;a.startPTS=t,a.setStart(t),a.endPTS=t+a.duration}function ea(a,e){const t=e.fragments;for(let s=0,i=t.length;s{const{config:n,fragCurrent:o,media:c,mediaBuffer:l,state:h}=this,u=c?c.currentTime:0,d=X.bufferInfo(l||c,u,n.maxBufferHole),f=!d.len;if(this.log(`Media seeking to ${N(u)?u.toFixed(3):u}, state: ${h}, ${f?"out of":"in"} buffer`),this.state===C.ENDED)this.resetLoadingState();else if(o){const g=n.maxFragLookUpTolerance,y=o.start-g,p=o.start+o.duration+g;if(f||pd.end){const E=u>p;(ug&&(this.lastCurrentTime=u),!this.loadingParts){const y=Math.max(d.end,u),p=this.shouldLoadParts(this.getLevelDetails(),y);p&&(this.log(`LL-Part loading ON after seeking to ${u.toFixed(2)} with buffer @${y.toFixed(2)}`),this.loadingParts=p)}}this.hls.hasEnoughToStart||(this.log(`Setting ${f?"startPosition":"nextLoadPosition"} to ${u} for seek without enough to start`),this.nextLoadPosition=u,f&&(this.startPosition=u)),f&&this.state===C.IDLE&&this.tickImmediate()},this.onMediaEnded=()=>{this.log("setting startPosition to 0 because media ended"),this.startPosition=this.lastCurrentTime=0},this.playlistType=r,this.hls=e,this.fragmentLoader=new dl(e.config),this.keyLoader=s,this.fragmentTracker=t,this.config=e.config,this.decrypter=new Di(e.config)}registerListeners(){const{hls:e}=this;e.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(m.ERROR,this.onError,this)}unregisterListeners(){const{hls:e}=this;e.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(m.ERROR,this.onError,this)}doTick(){this.onTickEnd()}onTickEnd(){}startLoad(e){}stopLoad(){if(this.state===C.STOPPED)return;this.fragmentLoader.abort(),this.keyLoader.abort(this.playlistType);const e=this.fragCurrent;e!=null&&e.loader&&(e.abortRequests(),this.fragmentTracker.removeFragment(e)),this.resetTransmuxer(),this.fragCurrent=null,this.fragPrevious=null,this.clearInterval(),this.clearNextTick(),this.state=C.STOPPED}get startPositionValue(){const{nextLoadPosition:e,startPosition:t}=this;return t===-1&&e?e:t}get bufferingEnabled(){return this.buffering}pauseBuffering(){this.buffering=!1}resumeBuffering(){this.buffering=!0}get inFlightFrag(){return{frag:this.fragCurrent,state:this.state}}_streamEnded(e,t){if(t.live||!this.media)return!1;const s=e.end||0,i=this.config.timelineOffset||0;if(s<=i)return!1;const r=e.buffered;this.config.maxBufferHole&&r&&r.length>1&&(e=X.bufferedInfo(r,e.start,0));const n=e.nextStart;if(n&&n>i&&n{const n=r.frag;if(this.fragContextChanged(n)){this.warn(`${n.type} sn: ${n.sn}${r.part?" part: "+r.part.index:""} of ${this.fragInfo(n,!1,r.part)}) was dropped during download.`),this.fragmentTracker.removeFragment(n);return}n.stats.chunkCount++,this._handleFragmentLoadProgress(r)};this._doFragLoad(e,t,s,i).then(r=>{if(!r)return;const n=this.state,o=r.frag;if(this.fragContextChanged(o)){(n===C.FRAG_LOADING||!this.fragCurrent&&n===C.PARSING)&&(this.fragmentTracker.removeFragment(o),this.state=C.IDLE);return}"payload"in r&&(this.log(`Loaded ${o.type} sn: ${o.sn} of ${this.playlistLabel()} ${o.level}`),this.hls.trigger(m.FRAG_LOADED,r)),this._handleFragmentLoadComplete(r)}).catch(r=>{this.state===C.STOPPED||this.state===C.ERROR||(this.warn(`Frag error: ${(r==null?void 0:r.message)||r}`),this.resetFragmentLoading(e))})}clearTrackerIfNeeded(e){var t;const{fragmentTracker:s}=this;if(s.getState(e)===fe.APPENDING){const r=e.type,n=this.getFwdBufferInfo(this.mediaBuffer,r),o=Math.max(e.duration,n?n.len:this.config.maxBufferLength),c=this.backtrackFragment;((c?e.sn-c.sn:0)===1||this.reduceMaxBufferLength(o,e.duration))&&s.removeFragment(e)}else((t=this.mediaBuffer)==null?void 0:t.buffered.length)===0?s.removeAllFragments():s.hasParts(e.type)&&(s.detectPartialFragments({frag:e,part:null,stats:e.stats,id:e.type}),s.getState(e)===fe.PARTIAL&&s.removeFragment(e))}checkLiveUpdate(e){if(e.updated&&!e.live){const t=e.fragments[e.fragments.length-1];this.fragmentTracker.detectPartialFragments({frag:t,part:null,stats:t.stats,id:t.type})}e.fragments[0]||(e.deltaUpdateFailed=!0)}waitForLive(e){const t=e.details;return(t==null?void 0:t.live)&&t.type!=="EVENT"&&(this.levelLastLoaded!==e||t.expired)}flushMainBuffer(e,t,s=null){if(!(e-t))return;const i={startOffset:e,endOffset:t,type:s};this.hls.trigger(m.BUFFER_FLUSHING,i)}_loadInitSegment(e,t){this._doFragLoad(e,t).then(s=>{const i=s==null?void 0:s.frag;if(!i||this.fragContextChanged(i)||!this.levels)throw new Error("init load aborted");return s}).then(s=>{const{hls:i}=this,{frag:r,payload:n}=s,o=r.decryptdata;if(n&&n.byteLength>0&&o!=null&&o.key&&o.iv&&bt(o.method)){const c=self.performance.now();return this.decrypter.decrypt(new Uint8Array(n),o.key.buffer,o.iv.buffer,Pi(o.method)).catch(l=>{throw i.trigger(m.ERROR,{type:H.MEDIA_ERROR,details:R.FRAG_DECRYPT_ERROR,fatal:!1,error:l,reason:l.message,frag:r}),l}).then(l=>{const h=self.performance.now();return i.trigger(m.FRAG_DECRYPTED,{frag:r,payload:l,stats:{tstart:c,tdecrypt:h}}),s.payload=l,this.completeInitSegmentLoad(s)})}return this.completeInitSegmentLoad(s)}).catch(s=>{this.state===C.STOPPED||this.state===C.ERROR||(this.warn(s),this.resetFragmentLoading(e))})}completeInitSegmentLoad(e){const{levels:t}=this;if(!t)throw new Error("init load aborted, missing levels");const s=e.frag.stats;this.state!==C.STOPPED&&(this.state=C.IDLE),e.frag.data=new Uint8Array(e.payload),s.parsing.start=s.buffering.start=self.performance.now(),s.parsing.end=s.buffering.end=self.performance.now(),this.tick()}unhandledEncryptionError(e,t){var s,i;const r=e.tracks;if(r&&!t.encrypted&&((s=r.audio)!=null&&s.encrypted||(i=r.video)!=null&&i.encrypted)&&(!this.config.emeEnabled||!this.keyLoader.emeController)){const n=this.media,o=new Error(`Encrypted track with no key in ${this.fragInfo(t)} (media ${n?"attached mediaKeys: "+n.mediaKeys:"detached"})`);return this.warn(o.message),!n||n.mediaKeys?!1:(this.hls.trigger(m.ERROR,{type:H.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_NO_KEYS,fatal:!1,error:o,frag:t}),this.resetTransmuxer(),!0)}return!1}fragContextChanged(e){const{fragCurrent:t}=this;return!e||!t||e.sn!==t.sn||e.level!==t.level}fragBufferedComplete(e,t){const s=this.mediaBuffer?this.mediaBuffer:this.media;if(this.log(`Buffered ${e.type} sn: ${e.sn}${t?" part: "+t.index:""} of ${this.fragInfo(e,!1,t)} > buffer:${s?Bl.toString(X.getBuffered(s)):"(detached)"})`),ue(e)){var i;if(e.type!==U.SUBTITLE){const n=e.elementaryStreams;if(!Object.keys(n).some(o=>!!n[o])){this.state=C.IDLE;return}}const r=(i=this.levels)==null?void 0:i[e.level];r!=null&&r.fragmentError&&(this.log(`Resetting level fragment error count of ${r.fragmentError} on frag buffered`),r.fragmentError=0)}this.state=C.IDLE}_handleFragmentLoadComplete(e){const{transmuxer:t}=this;if(!t)return;const{frag:s,part:i,partsLoaded:r}=e,n=!r||r.length===0||r.some(c=>!c),o=new Ci(s.level,s.sn,s.stats.chunkCount+1,0,i?i.index:-1,!n);t.flush(o)}_handleFragmentLoadProgress(e){}_doFragLoad(e,t,s=null,i){var r;this.fragCurrent=e;const n=t.details;if(!this.levels||!n)throw new Error(`frag load aborted, missing level${n?"":" detail"}s`);let o=null;if(e.encrypted&&!((r=e.decryptdata)!=null&&r.key)){if(this.log(`Loading key for ${e.sn} of [${n.startSN}-${n.endSN}], ${this.playlistLabel()} ${e.level}`),this.state=C.KEY_LOADING,this.fragCurrent=e,o=this.keyLoader.load(e).then(d=>{if(!this.fragContextChanged(d.frag))return this.hls.trigger(m.KEY_LOADED,d),this.state===C.KEY_LOADING&&(this.state=C.IDLE),d}),this.hls.trigger(m.KEY_LOADING,{frag:e}),this.fragCurrent===null)return this.log("context changed in KEY_LOADING"),Promise.resolve(null)}else e.encrypted||(o=this.keyLoader.loadClear(e,n.encryptedFragments,this.startFragRequested),o&&this.log("[eme] blocking frag load until media-keys acquired"));const c=this.fragPrevious;if(ue(e)&&(!c||e.sn!==c.sn)){const d=this.shouldLoadParts(t.details,e.end);d!==this.loadingParts&&(this.log(`LL-Part loading ${d?"ON":"OFF"} loading sn ${c==null?void 0:c.sn}->${e.sn}`),this.loadingParts=d)}if(s=Math.max(e.start,s||0),this.loadingParts&&ue(e)){const d=n.partList;if(d&&i){s>n.fragmentEnd&&n.fragmentHint&&(e=n.fragmentHint);const f=this.getNextPart(d,e,s);if(f>-1){const g=d[f];e=this.fragCurrent=g.fragment,this.log(`Loading ${e.type} sn: ${e.sn} part: ${g.index} (${f}/${d.length-1}) of ${this.fragInfo(e,!1,g)}) cc: ${e.cc} [${n.startSN}-${n.endSN}], target: ${parseFloat(s.toFixed(3))}`),this.nextLoadPosition=g.start+g.duration,this.state=C.FRAG_LOADING;let y;return o?y=o.then(p=>!p||this.fragContextChanged(p.frag)?null:this.doFragPartsLoad(e,g,t,i)).catch(p=>this.handleFragLoadError(p)):y=this.doFragPartsLoad(e,g,t,i).catch(p=>this.handleFragLoadError(p)),this.hls.trigger(m.FRAG_LOADING,{frag:e,part:g,targetBufferTime:s}),this.fragCurrent===null?Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING parts")):y}else if(!e.url||this.loadedEndOfParts(d,s))return Promise.resolve(null)}}if(ue(e)&&this.loadingParts){var l;this.log(`LL-Part loading OFF after next part miss @${s.toFixed(2)} Check buffer at sn: ${e.sn} loaded parts: ${(l=n.partList)==null?void 0:l.filter(d=>d.loaded).map(d=>`[${d.start}-${d.end}]`)}`),this.loadingParts=!1}else if(!e.url)return Promise.resolve(null);this.log(`Loading ${e.type} sn: ${e.sn} of ${this.fragInfo(e,!1)}) cc: ${e.cc} ${"["+n.startSN+"-"+n.endSN+"]"}, target: ${parseFloat(s.toFixed(3))}`),N(e.sn)&&!this.bitrateTest&&(this.nextLoadPosition=e.start+e.duration),this.state=C.FRAG_LOADING;const h=this.config.progressive&&e.type!==U.SUBTITLE;let u;return h&&o?u=o.then(d=>!d||this.fragContextChanged(d.frag)?null:this.fragmentLoader.load(e,i)).catch(d=>this.handleFragLoadError(d)):u=Promise.all([this.fragmentLoader.load(e,h?i:void 0),o]).then(([d])=>(!h&&i&&i(d),d)).catch(d=>this.handleFragLoadError(d)),this.hls.trigger(m.FRAG_LOADING,{frag:e,targetBufferTime:s}),this.fragCurrent===null?Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING")):u}doFragPartsLoad(e,t,s,i){return new Promise((r,n)=>{var o;const c=[],l=(o=s.details)==null?void 0:o.partList,h=u=>{this.fragmentLoader.loadPart(e,u,i).then(d=>{c[u.index]=d;const f=d.part;this.hls.trigger(m.FRAG_LOADED,d);const g=Mr(s.details,e.sn,u.index+1)||Zn(l,e.sn,u.index+1);if(g)h(g);else return r({frag:e,part:f,partsLoaded:c})}).catch(n)};h(t)})}handleFragLoadError(e){if("data"in e){const t=e.data;t.frag&&t.details===R.INTERNAL_ABORTED?this.handleFragLoadAborted(t.frag,t.part):t.frag&&t.type===H.KEY_SYSTEM_ERROR?(t.frag.abortRequests(),this.resetStartWhenNotLoaded(),this.resetFragmentLoading(t.frag)):this.hls.trigger(m.ERROR,t)}else this.hls.trigger(m.ERROR,{type:H.OTHER_ERROR,details:R.INTERNAL_EXCEPTION,err:e,error:e,fatal:!0});return null}_handleTransmuxerFlush(e){const t=this.getCurrentContext(e);if(!t||this.state!==C.PARSING){!this.fragCurrent&&this.state!==C.STOPPED&&this.state!==C.ERROR&&(this.state=C.IDLE);return}const{frag:s,part:i,level:r}=t,n=self.performance.now();s.stats.parsing.end=n,i&&(i.stats.parsing.end=n);const o=this.getLevelDetails(),l=o&&s.sn>o.endSN||this.shouldLoadParts(o,s.end);l!==this.loadingParts&&(this.log(`LL-Part loading ${l?"ON":"OFF"} after parsing segment ending @${s.end.toFixed(2)}`),this.loadingParts=l),this.updateLevelTiming(s,i,r,e.partial)}shouldLoadParts(e,t){if(this.config.lowLatencyMode){if(!e)return this.loadingParts;if(e.partList){var s;const r=e.partList[0];if(r.fragment.type===U.SUBTITLE)return!1;const n=r.end+(((s=e.fragmentHint)==null?void 0:s.duration)||0);if(t>=n){var i;if((this.hls.hasEnoughToStart?((i=this.media)==null?void 0:i.currentTime)||this.lastCurrentTime:this.getLoadPosition())>r.start-r.fragment.duration)return!0}}}return!1}getCurrentContext(e){const{levels:t,fragCurrent:s}=this,{level:i,sn:r,part:n}=e;if(!(t!=null&&t[i]))return this.warn(`Levels object was unset while buffering fragment ${r} of ${this.playlistLabel()} ${i}. The current chunk will not be buffered.`),null;const o=t[i],c=o.details,l=n>-1?Mr(c,r,n):null,h=l?l.fragment:Qn(c,r,s);return h?(s&&s!==h&&(h.stats=s.stats),{frag:h,part:l,level:o}):null}bufferFragmentData(e,t,s,i,r){if(this.state!==C.PARSING)return;const{data1:n,data2:o}=e;let c=n;if(o&&(c=ke(n,o)),!c.length)return;const l=this.initPTS[t.cc],h=l?-l.baseTime/l.timescale:void 0,u={type:e.type,frag:t,part:s,chunkMeta:i,offset:h,parent:t.type,data:c};if(this.hls.trigger(m.BUFFER_APPENDING,u),e.dropped&&e.independent&&!s){if(r)return;this.flushBufferGap(t)}}flushBufferGap(e){const t=this.media;if(!t)return;if(!X.isBuffered(t,t.currentTime)){this.flushMainBuffer(0,e.start);return}const s=t.currentTime,i=X.bufferInfo(t,s,0),r=e.duration,n=Math.min(this.config.maxFragLookUpTolerance*2,r*.25),o=Math.max(Math.min(e.start-n,i.end-n),s+n);e.start-o>n&&this.flushMainBuffer(o,e.start)}getFwdBufferInfo(e,t){var s;const i=this.getLoadPosition();if(!N(i))return null;const n=this.lastCurrentTime>i||(s=this.media)!=null&&s.paused?0:this.config.maxBufferHole;return this.getFwdBufferInfoAtPos(e,i,t,n)}getFwdBufferInfoAtPos(e,t,s,i){const r=X.bufferInfo(e,t,i);if(r.len===0&&r.nextStart!==void 0){const n=this.fragmentTracker.getBufferedFrag(t,s);if(n&&(r.nextStart<=n.end||n.gap)){const o=Math.max(Math.min(r.nextStart,n.end)-t,i);return X.bufferInfo(e,t,o)}}return r}getMaxBufferLength(e){const{config:t}=this;let s;return e?s=Math.max(8*t.maxBufferSize/e,t.maxBufferLength):s=t.maxBufferLength,Math.min(s,t.maxMaxBufferLength)}exceedsMaxBuffer(e,t,s){const i=e.nextStart;if(i&&s.start>i){const r=e.buffered;if(r){let n=e.len;const o=e.bufferedIndex;for(let c=r.length-1;c>o;c--)r[c].start=t}}return!1}reduceMaxBufferLength(e,t){const s=this.config,i=Math.max(Math.min(e-t,s.maxBufferLength),t),r=Math.max(e-t*3,s.maxMaxBufferLength/2,i);return r>=i?(s.maxMaxBufferLength=r,this.warn(`Reduce max buffer length to ${r}s`),!0):!1}getAppendedFrag(e,t=U.MAIN){const s=this.fragmentTracker?this.fragmentTracker.getAppendedFrag(e,t):null;return s&&"fragment"in s?s.fragment:s}getNextFragment(e,t){const s=t.fragments,i=s.length;if(!i)return null;const{config:r}=this,n=t.fragmentStart,o=r.lowLatencyMode&&!!t.partList;let c=null;if(t.live){const u=r.initialLiveManifestSize;if(i=n?(p=f,E=f===d?"config":"next load start"):g!==null?(p=g,E="live edge"):(p=e,E="buffer pos"),p=r){const c=n.sn;return this.loopSn!==c&&(this.log(`buffer full after gaps in "${i}" playlist starting at sn: ${c}`),this.loopSn=c),null}}return this.loopSn=void 0,n}get primaryPrefetch(){if(Ur(this.config)){var e;if((e=this.hls.interstitialsManager)==null||(e=e.playingItem)==null?void 0:e.event)return!0}return!1}filterReplacedPrimary(e,t){if(!e)return e;if(Ur(this.config)&&e.type!==U.SUBTITLE){const s=this.hls.interstitialsManager,i=s==null?void 0:s.bufferingItem;if(i){const n=i.event;if(n){if(n.appendInPlace||Math.abs(e.start-i.start)>1||i.start===0)return null}else if(e.end<=i.start&&(t==null?void 0:t.live)===!1||e.start>i.end&&i.nextEvent&&(i.nextEvent.appendInPlace||e.start-i.end>1))return null}const r=s==null?void 0:s.playerQueue;if(r)for(let n=r.length;n--;){const o=r[n].interstitial;if(o.appendInPlace&&e.start>=o.startTime&&e.end<=o.resumeTime)return null}}return e}mapToInitFragWhenRequired(e){return e!=null&&e.initSegment&&!e.initSegment.data&&!this.bitrateTest?e.initSegment:e}getNextPart(e,t,s){let i=-1,r=!1,n=!0;for(let o=0,c=e.length;o-1&&ss.start)return!0}return!1}getInitialLiveFragment(e){const t=e.fragments,s=this.fragPrevious;let i=null;if(s){if(e.hasProgramDateTime&&(i=el(t,s.endProgramDateTime,this.config.maxFragLookUpTolerance),i&&this.log(`Live playlist, switching playlist, load frag with same PDT: ${s.programDateTime}`)),!i){const r=s.sn+1;if(r>=e.startSN&&r<=e.endSN){const n=t[r-e.startSN];s.cc===n.cc&&(i=n,this.log(`Live playlist, switching playlist, load frag with next SN: ${i.sn}`))}i||(i=Nn(e,s.cc,s.end),i&&this.log(`Live playlist, switching playlist, load frag with same CC: ${i.sn}`))}}else{const r=this.hls.liveSyncPosition;r!==null&&(i=this.getFragmentAtPosition(r,this.bitrateTest?e.fragmentEnd:e.edge,e))}return i}getFragmentAtPosition(e,t,s){const{config:i}=this;let{fragPrevious:r}=this,{fragments:n,endSN:o}=s;const{fragmentHint:c}=s,{maxFragLookUpTolerance:l}=i,h=s.partList,u=!!(this.loadingParts&&h!=null&&h.length&&c);u&&!this.bitrateTest&&h[h.length-1].fragment.sn===c.sn&&(n=n.concat(c),o=c.sn);let d;if(et-l||(f=this.media)!=null&&f.paused||!this.startFragRequested?0:l;d=mt(r,n,e,y)}else d=n[n.length-1];if(d){const g=d.sn-s.startSN,y=this.fragmentTracker.getState(d);if((y===fe.OK||y===fe.PARTIAL&&d.gap)&&(r=d),r&&d.sn===r.sn&&(!u||h[0].fragment.sn>d.sn||!s.live)&&d.level===r.level){const E=n[g+1];d.sn${e.startSN} fragments: ${i}`),c}return r}waitForCdnTuneIn(e){return e.live&&e.canBlockReload&&e.partTarget&&e.tuneInGoal>Math.max(e.partHoldBack,e.partTarget*3)}setStartPosition(e,t){let s=this.startPosition;s=0&&(s=this.nextLoadPosition),s}handleFragLoadAborted(e,t){this.transmuxer&&e.type===this.playlistType&&ue(e)&&e.stats.aborted&&(this.log(`Fragment ${e.sn}${t?" part "+t.index:""} of ${this.playlistLabel()} ${e.level} was aborted`),this.resetFragmentLoading(e))}resetFragmentLoading(e){(!this.fragCurrent||!this.fragContextChanged(e)&&this.state!==C.FRAG_LOADING_WAITING_RETRY)&&(this.state=C.IDLE)}onFragmentOrKeyLoadError(e,t){var s;if(t.chunkMeta&&!t.frag){const E=this.getCurrentContext(t.chunkMeta);E&&(t.frag=E.frag)}const i=t.frag;if(!i||i.type!==e||!this.levels)return;if(this.fragContextChanged(i)){var r;this.warn(`Frag load error must match current frag to retry ${i.url} > ${(r=this.fragCurrent)==null?void 0:r.url}`);return}const n=t.details===R.FRAG_GAP;n&&this.fragmentTracker.fragBuffered(i,!0);const o=t.errorAction;if(!o){this.state=C.ERROR;return}const{action:c,flags:l,retryCount:h=0,retryConfig:u}=o,d=!!u,f=d&&c===pe.RetryRequest,g=d&&!o.resolved&&l===Le.MoveAllAlternatesMatchingHost,y=(s=this.hls.latestLevelDetails)==null?void 0:s.live;if(!f&&g&&ue(i)&&!i.endList&&y&&!Un(t))this.resetFragmentErrors(e),this.treatAsGap(i),o.resolved=!0;else if((f||g)&&h=t||s&&!oi(0))&&(s&&this.log("Connection restored (online)"),this.resetStartWhenNotLoaded(),this.state=C.IDLE)}reduceLengthAndFlushBuffer(e){if(this.state===C.PARSING||this.state===C.PARSED){const t=e.frag,s=e.parent,i=this.getFwdBufferInfo(this.mediaBuffer,s),r=i&&i.len>.5;r&&this.reduceMaxBufferLength(i.len,(t==null?void 0:t.duration)||10);const n=!r;return n&&this.warn(`Buffer full error while media.currentTime (${this.getLoadPosition()}) is not buffered, flush ${s} buffer`),t&&(this.fragmentTracker.removeFragment(t),this.nextLoadPosition=t.start),this.resetLoadingState(),n}return!1}resetFragmentErrors(e){e===U.AUDIO&&(this.fragCurrent=null),this.hls.hasEnoughToStart||(this.startFragRequested=!1),this.state!==C.STOPPED&&(this.state=C.IDLE)}afterBufferFlushed(e,t,s){if(!e)return;const i=X.getBuffered(e);this.fragmentTracker.detectEvictedFragments(t,i,s),this.state===C.ENDED&&this.resetLoadingState()}resetLoadingState(){this.log("Reset loading state"),this.fragCurrent=null,this.fragPrevious=null,this.state!==C.STOPPED&&(this.state=C.IDLE)}resetStartWhenNotLoaded(){if(!this.hls.hasEnoughToStart){this.startFragRequested=!1;const e=this.levelLastLoaded,t=e?e.details:null;t!=null&&t.live?(this.log("resetting startPosition for live start"),this.startPosition=-1,this.setStartPosition(t,t.fragmentStart),this.resetLoadingState()):this.nextLoadPosition=this.startPosition}}resetWhenMissingContext(e){this.log(`Loading context changed while buffering sn ${e.sn} of ${this.playlistLabel()} ${e.level===-1?"":e.level}. This chunk will not be buffered.`),this.removeUnbufferedFrags(),this.resetStartWhenNotLoaded(),this.resetLoadingState()}removeUnbufferedFrags(e=0){this.fragmentTracker.removeFragmentsInRange(e,1/0,this.playlistType,!1,!0)}updateLevelTiming(e,t,s,i){const r=s.details;if(!r){this.warn("level.details undefined");return}if(!Object.keys(e.elementaryStreams).reduce((c,l)=>{const h=e.elementaryStreams[l];if(h){const u=h.endPTS-h.startPTS;if(u<=0)return this.warn(`Could not parse fragment ${e.sn} ${l} duration reliably (${u})`),c||!1;const d=i?0:jn(r,e,h.startPTS,h.endPTS,h.startDTS,h.endDTS,this);return this.hls.trigger(m.LEVEL_PTS_UPDATED,{details:r,level:s,drift:d,type:l,frag:e,start:h.startPTS,end:h.endPTS}),!0}return c},!1)){var o;const c=((o=this.transmuxer)==null?void 0:o.error)===null;if((s.fragmentError===0||c&&(s.fragmentError<2||e.endList))&&this.treatAsGap(e,s),c){const l=new Error(`Found no media in fragment ${e.sn} of ${this.playlistLabel()} ${e.level} resetting transmuxer to fallback to playlist timing`);if(this.warn(l.message),this.hls.trigger(m.ERROR,{type:H.MEDIA_ERROR,details:R.FRAG_PARSING_ERROR,fatal:!1,error:l,frag:e,reason:`Found no media in msn ${e.sn} of ${this.playlistLabel()} "${s.url}"`}),!this.hls)return;this.resetTransmuxer()}}this.state=C.PARSED,this.log(`Parsed ${e.type} sn: ${e.sn}${t?" part: "+t.index:""} of ${this.fragInfo(e,!1,t)})`),this.hls.trigger(m.FRAG_PARSED,{frag:e,part:t})}playlistLabel(){return this.playlistType===U.MAIN?"level":"track"}fragInfo(e,t=!0,s){var i,r;return`${this.playlistLabel()} ${e.level} (${s?"part":"frag"}:[${((i=t&&!s?e.startPTS:(s||e).start)!=null?i:NaN).toFixed(3)}-${((r=t&&!s?e.endPTS:(s||e).end)!=null?r:NaN).toFixed(3)}]${s&&e.type==="main"?"INDEPENDENT="+(s.independent?"YES":"NO"):""}`}treatAsGap(e,t){t&&t.fragmentError++,e.gap=!0,this.fragmentTracker.removeFragment(e),this.fragmentTracker.fragBuffered(e,!0)}resetTransmuxer(){var e;(e=this.transmuxer)==null||e.reset()}recoverWorkerError(e){e.event==="demuxerWorker"&&(this.fragmentTracker.removeAllFragments(),this.transmuxer&&(this.transmuxer.destroy(),this.transmuxer=null),this.resetStartWhenNotLoaded(),this.resetLoadingState())}set state(e){const t=this._state;t!==e&&(this._state=e,this.log(`${t}->${e}`))}get state(){return this._state}}function Ur(a){return!!a.interstitialsController&&a.enableInterstitialPlayback!==!1}class sa{constructor(){this.chunks=[],this.dataLength=0}push(e){this.chunks.push(e),this.dataLength+=e.length}flush(){const{chunks:e,dataLength:t}=this;let s;if(e.length)e.length===1?s=e[0]:s=Ul(e,t);else return new Uint8Array(0);return this.reset(),s}reset(){this.chunks.length=0,this.dataLength=0}}function Ul(a,e){const t=new Uint8Array(e);let s=0;for(let i=0;i0)return a.subarray(t,t+s)}function Wl(a,e,t,s){const i=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350],r=e[t+2],n=r>>2&15;if(n>12){const f=new Error(`invalid ADTS sampling index:${n}`);a.emit(m.ERROR,m.ERROR,{type:H.MEDIA_ERROR,details:R.FRAG_PARSING_ERROR,fatal:!0,error:f,reason:f.message});return}const o=(r>>6&3)+1,c=e[t+3]>>6&3|(r&1)<<2,l="mp4a.40."+o,h=i[n];let u=n;(o===5||o===29)&&(u-=3);const d=[o<<3|(u&14)>>1,(u&1)<<7|c<<3];return re.log(`manifest codec:${s}, parsed codec:${l}, channels:${c}, rate:${h} (ADTS object type:${o} sampling index:${n})`),{config:d,samplerate:h,channelCount:c,codec:l,parsedCodec:l,manifestCodec:s}}function ra(a,e){return a[e]===255&&(a[e+1]&246)===240}function na(a,e){return a[e+1]&1?7:9}function Mi(a,e){return(a[e+3]&3)<<11|a[e+4]<<3|(a[e+5]&224)>>>5}function ql(a,e){return e+5=a.length)return!1;const s=Mi(a,e);if(s<=t)return!1;const i=e+s;return i===a.length||Rs(a,i)}return!1}function aa(a,e,t,s,i){if(!a.samplerate){const r=Wl(e,t,s,i);if(!r)return;ne(a,r)}}function oa(a){return 1024*9e4/a}function zl(a,e){const t=na(a,e);if(e+t<=a.length){const s=Mi(a,e)-t;if(s>0)return{headerLength:t,frameLength:s}}}function la(a,e,t,s,i){const r=oa(a.samplerate),n=s+i*r,o=zl(e,t);let c;if(o){const{frameLength:u,headerLength:d}=o,f=d+u,g=Math.max(0,t+f-e.length);g?(c=new Uint8Array(f-d),c.set(e.subarray(t+d,e.length),0)):c=e.subarray(t+d,t+f);const y={unit:c,pts:n};return g||a.samples.push(y),{sample:y,length:f,missing:g}}const l=e.length-t;return c=new Uint8Array(l),c.set(e.subarray(t,e.length),0),{sample:{unit:c,pts:n},length:l,missing:-1}}function Ql(a,e){return Fi(a,e)&&Ds(a,e+6)+10<=a.length-e}function Zl(a){return a instanceof ArrayBuffer?a:a.byteOffset==0&&a.byteLength==a.buffer.byteLength?a.buffer:new Uint8Array(a).buffer}function Gs(a,e=0,t=1/0){return Jl(a,e,t,Uint8Array)}function Jl(a,e,t,s){const i=ec(a);let r=1;"BYTES_PER_ELEMENT"in s&&(r=s.BYTES_PER_ELEMENT);const n=tc(a)?a.byteOffset:0,o=(n+a.byteLength)/r,c=(n+e)/r,l=Math.floor(Math.max(0,Math.min(c,o))),h=Math.floor(Math.min(l+Math.max(t,0),o));return new s(i,l,h-l)}function ec(a){return a instanceof ArrayBuffer?a:a.buffer}function tc(a){return a&&a.buffer instanceof ArrayBuffer&&a.byteLength!==void 0&&a.byteOffset!==void 0}function sc(a){const e={key:a.type,description:"",data:"",mimeType:null,pictureType:null},t=3;if(a.size<2)return;if(a.data[0]!==t){console.log("Ignore frame with unrecognized character encoding");return}const s=a.data.subarray(1).indexOf(0);if(s===-1)return;const i=_e(Gs(a.data,1,s)),r=a.data[2+s],n=a.data.subarray(3+s).indexOf(0);if(n===-1)return;const o=_e(Gs(a.data,3+s,n));let c;return i==="-->"?c=_e(Gs(a.data,4+s+n)):c=Zl(a.data.subarray(4+s+n)),e.mimeType=i,e.pictureType=r,e.description=o,e.data=c,e}function ic(a){if(a.size<2)return;const e=_e(a.data,!0),t=new Uint8Array(a.data.subarray(e.length+1));return{key:a.type,info:e,data:t.buffer}}function rc(a){if(a.size<2)return;if(a.type==="TXXX"){let t=1;const s=_e(a.data.subarray(t),!0);t+=s.length+1;const i=_e(a.data.subarray(t));return{key:a.type,info:s,data:i}}const e=_e(a.data.subarray(1));return{key:a.type,info:"",data:e}}function nc(a){if(a.type==="WXXX"){if(a.size<2)return;let t=1;const s=_e(a.data.subarray(t),!0);t+=s.length+1;const i=_e(a.data.subarray(t));return{key:a.type,info:s,data:i}}const e=_e(a.data);return{key:a.type,info:"",data:e}}function ac(a){return a.type==="PRIV"?ic(a):a.type[0]==="W"?nc(a):a.type==="APIC"?sc(a):rc(a)}function oc(a){const e=String.fromCharCode(a[0],a[1],a[2],a[3]),t=Ds(a,4),s=10;return{type:e,size:t,data:a.subarray(s,s+t)}}const Jt=10,lc=10;function ca(a){let e=0;const t=[];for(;Fi(a,e);){const s=Ds(a,e+6);a[e+5]>>6&1&&(e+=Jt),e+=Jt;const i=e+s;for(;e+lc0&&o.samples.push({pts:this.lastPTS,dts:this.lastPTS,data:s,type:be.audioId3,duration:Number.POSITIVE_INFINITY});i{if(N(a))return a*90;const s=t?t.baseTime*9e4/t.timescale:0;return e*9e4+s};let es=null;const uc=[32,64,96,128,160,192,224,256,288,320,352,384,416,448,32,48,56,64,80,96,112,128,160,192,224,256,320,384,32,40,48,56,64,80,96,112,128,160,192,224,256,320,32,48,56,64,80,96,112,128,144,160,176,192,224,256,8,16,24,32,40,48,56,64,80,96,112,128,144,160],dc=[44100,48e3,32e3,22050,24e3,16e3,11025,12e3,8e3],fc=[[0,72,144,12],[0,0,0,0],[0,72,144,12],[0,144,144,12]],gc=[0,1,1,4];function ua(a,e,t,s,i){if(t+24>e.length)return;const r=da(e,t);if(r&&t+r.frameLength<=e.length){const n=r.samplesPerFrame*9e4/r.sampleRate,o=s+i*n,c={unit:e.subarray(t,t+r.frameLength),pts:o,dts:o};return a.config=[],a.channelCount=r.channelCount,a.samplerate=r.sampleRate,a.samples.push(c),{sample:c,length:r.frameLength,missing:0}}}function da(a,e){const t=a[e+1]>>3&3,s=a[e+1]>>1&3,i=a[e+2]>>4&15,r=a[e+2]>>2&3;if(t!==1&&i!==0&&i!==15&&r!==3){const n=a[e+2]>>1&1,o=a[e+3]>>6,c=t===3?3-s:s===3?3:4,l=uc[c*14+i-1]*1e3,u=dc[(t===3?0:t===2?1:2)*3+r],d=o===3?1:2,f=fc[t][s],g=gc[s],y=f*8*g,p=Math.floor(f*l/u+n)*g;if(es===null){const S=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);es=S?parseInt(S[1]):0}return!!es&&es<=87&&s===2&&l>=224e3&&o===0&&(a[e+3]=a[e+3]|128),{sampleRate:u,channelCount:d,frameLength:p,samplesPerFrame:y}}}function Ui(a,e){return a[e]===255&&(a[e+1]&224)===224&&(a[e+1]&6)!==0}function fa(a,e){return e+1{let t=0,s=5;e+=s;const i=new Uint32Array(1),r=new Uint32Array(1),n=new Uint8Array(1);for(;s>0;){n[0]=a[e];const o=Math.min(s,8),c=8-o;r[0]=4278190080>>>24+c<>c,t=t?t<e.length||e[t]!==11||e[t+1]!==119)return-1;const r=e[t+4]>>6;if(r>=3)return-1;const o=[48e3,44100,32e3][r],c=e[t+4]&63,h=[64,69,96,64,70,96,80,87,120,80,88,120,96,104,144,96,105,144,112,121,168,112,122,168,128,139,192,128,140,192,160,174,240,160,175,240,192,208,288,192,209,288,224,243,336,224,244,336,256,278,384,256,279,384,320,348,480,320,349,480,384,417,576,384,418,576,448,487,672,448,488,672,512,557,768,512,558,768,640,696,960,640,697,960,768,835,1152,768,836,1152,896,975,1344,896,976,1344,1024,1114,1536,1024,1115,1536,1152,1253,1728,1152,1254,1728,1280,1393,1920,1280,1394,1920][c*3+r]*2;if(t+h>e.length)return-1;const u=e[t+6]>>5;let d=0;u===2?d+=2:(u&1&&u!==1&&(d+=2),u&4&&(d+=2));const f=(e[t+6]<<8|e[t+7])>>12-d&1,y=[2,1,2,3,3,4,4,5][u]+f,p=e[t+5]>>3,E=e[t+5]&7,T=new Uint8Array([r<<6|p<<1|E>>2,(E&3)<<6|u<<3|f<<2|c>>4,c<<4&224]),S=1536/o*9e4,v=s+i*S,x=e.subarray(t,t+h);return a.config=T,a.channelCount=y,a.samplerate=o,a.samples.push({unit:x,pts:v}),h}class Ec extends Bi{resetInitSegment(e,t,s,i){super.resetInitSegment(e,t,s,i),this._audioTrack={container:"audio/mpeg",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"mp3",samples:[],manifestCodec:t,duration:i,inputTimeScale:9e4,dropped:0}}static probe(e){if(!e)return!1;const t=Ht(e,0);let s=(t==null?void 0:t.length)||0;if(t&&e[s]===11&&e[s+1]===119&&Ni(t)!==void 0&&ma(e,s)<=16)return!1;for(let i=e.length;s{const n=Ro(r);if(Tc.test(n.schemeIdUri)){const o=Gr(n,t);let c=n.eventDuration===4294967295?Number.POSITIVE_INFINITY:n.eventDuration/n.timeScale;c<=.001&&(c=Number.POSITIVE_INFINITY);const l=n.payload;s.samples.push({data:l,len:l.byteLength,dts:o,pts:o,type:be.emsg,duration:c})}else if(this.config.enableEmsgKLVMetadata&&n.schemeIdUri.startsWith("urn:misb:KLV:bin:1910.1")){const o=Gr(n,t);s.samples.push({data:n.payload,len:n.payload.byteLength,dts:o,pts:o,type:be.misbklv,duration:Number.POSITIVE_INFINITY})}})}return s}demuxSampleAes(e,t,s){return Promise.reject(new Error("The MP4 demuxer does not support SAMPLE-AES decryption"))}destroy(){this.config=null,this.remainderData=null,this.videoTrack=this.audioTrack=this.id3Track=this.txtTrack=void 0}}function Gr(a,e){return N(a.presentationTime)?a.presentationTime/a.timeScale:e+a.presentationTimeDelta/a.timeScale}class vc{constructor(e,t,s){this.keyData=void 0,this.decrypter=void 0,this.keyData=s,this.decrypter=new Di(t,{removePKCS7Padding:!1})}decryptBuffer(e){return this.decrypter.decrypt(e,this.keyData.key.buffer,this.keyData.iv.buffer,at.cbc)}decryptAacSample(e,t,s){const i=e[t].unit;if(i.length<=16)return;const r=i.subarray(16,i.length-i.length%16),n=r.buffer.slice(r.byteOffset,r.byteOffset+r.length);this.decryptBuffer(n).then(o=>{const c=new Uint8Array(o);i.set(c,16),this.decrypter.isSync()||this.decryptAacSamples(e,t+1,s)}).catch(s)}decryptAacSamples(e,t,s){for(;;t++){if(t>=e.length){s();return}if(!(e[t].unit.length<32)&&(this.decryptAacSample(e,t,s),!this.decrypter.isSync()))return}}getAvcEncryptedData(e){const t=Math.floor((e.length-48)/160)*16+16,s=new Int8Array(t);let i=0;for(let r=32;r{r.data=this.getAvcDecryptedUnit(n,c),this.decrypter.isSync()||this.decryptAvcSamples(e,t,s+1,i)}).catch(i)}decryptAvcSamples(e,t,s,i){if(e instanceof Uint8Array)throw new Error("Cannot decrypt samples of type Uint8Array");for(;;t++,s=0){if(t>=e.length){i();return}const r=e[t].units;for(;!(s>=r.length);s++){const n=r[s];if(!(n.data.length<=48||n.type!==1&&n.type!==5)&&(this.decryptAvcSample(e,t,s,i,n),!this.decrypter.isSync()))return}}}}class ya{constructor(){this.VideoSample=null}createVideoSample(e,t,s){return{key:e,frame:!1,pts:t,dts:s,units:[],length:0}}getLastNalUnit(e){var t;let s=this.VideoSample,i;if((!s||s.units.length===0)&&(s=e[e.length-1]),(t=s)!=null&&t.units){const r=s.units;i=r[r.length-1]}return i}pushAccessUnit(e,t){if(e.units.length&&e.frame){if(e.pts===void 0){const s=t.samples,i=s.length;if(i){const r=s[i-1];e.pts=r.pts,e.dts=r.dts}else{t.dropped++;return}}t.samples.push(e)}}parseNALu(e,t,s){const i=t.byteLength;let r=e.naluState||0;const n=r,o=[];let c=0,l,h,u,d=-1,f=0;for(r===-1&&(d=0,f=this.getNALuType(t,0),r=0,c=1);c=0){const g={data:t.subarray(d,h),type:f};o.push(g)}else{const g=this.getLastNalUnit(e.samples);g&&(n&&c<=4-n&&g.state&&(g.data=g.data.subarray(0,g.data.byteLength-n)),h>0&&(g.data=ke(g.data,t.subarray(0,h)),g.state=0))}c=0&&r>=0){const g={data:t.subarray(d,i),type:f,state:r};o.push(g)}if(o.length===0){const g=this.getLastNalUnit(e.samples);g&&(g.data=ke(g.data,t))}return e.naluState=r,o}}class Bt{constructor(e){this.data=void 0,this.bytesAvailable=void 0,this.word=void 0,this.bitsAvailable=void 0,this.data=e,this.bytesAvailable=e.byteLength,this.word=0,this.bitsAvailable=0}loadWord(){const e=this.data,t=this.bytesAvailable,s=e.byteLength-t,i=new Uint8Array(4),r=Math.min(4,t);if(r===0)throw new Error("no bytes available");i.set(e.subarray(s,s+r)),this.word=new DataView(i.buffer).getUint32(0),this.bitsAvailable=r*8,this.bytesAvailable-=r}skipBits(e){let t;e=Math.min(e,this.bytesAvailable*8+this.bitsAvailable),this.bitsAvailable>e?(this.word<<=e,this.bitsAvailable-=e):(e-=this.bitsAvailable,t=e>>3,e-=t<<3,this.bytesAvailable-=t,this.loadWord(),this.word<<=e,this.bitsAvailable-=e)}readBits(e){let t=Math.min(this.bitsAvailable,e);const s=this.word>>>32-t;if(e>32&&re.error("Cannot read more than 32 bits at a time"),this.bitsAvailable-=t,this.bitsAvailable>0)this.word<<=t;else if(this.bytesAvailable>0)this.loadWord();else throw new Error("no bits available");return t=e-t,t>0&&this.bitsAvailable?s<>>e)return this.word<<=e,this.bitsAvailable-=e,e;return this.loadWord(),e+this.skipLZ()}skipUEG(){this.skipBits(1+this.skipLZ())}skipEG(){this.skipBits(1+this.skipLZ())}readUEG(){const e=this.skipLZ();return this.readBits(e+1)-1}readEG(){const e=this.readUEG();return 1&e?1+e>>>1:-1*(e>>>1)}readBoolean(){return this.readBits(1)===1}readUByte(){return this.readBits(8)}readUShort(){return this.readBits(16)}readUInt(){return this.readBits(32)}}class xc extends ya{parsePES(e,t,s,i){const r=this.parseNALu(e,s.data,i);let n=this.VideoSample,o,c=!1;s.data=null,n&&r.length&&!e.audFound&&(this.pushAccessUnit(n,e),n=this.VideoSample=this.createVideoSample(!1,s.pts,s.dts)),r.forEach(l=>{var h,u;switch(l.type){case 1:{let y=!1;o=!0;const p=l.data;if(c&&p.length>4){const E=this.readSliceType(p);(E===2||E===4||E===7||E===9)&&(y=!0)}if(y){var d;(d=n)!=null&&d.frame&&!n.key&&(this.pushAccessUnit(n,e),n=this.VideoSample=null)}n||(n=this.VideoSample=this.createVideoSample(!0,s.pts,s.dts)),n.frame=!0,n.key=y;break}case 5:o=!0,(h=n)!=null&&h.frame&&!n.key&&(this.pushAccessUnit(n,e),n=this.VideoSample=null),n||(n=this.VideoSample=this.createVideoSample(!0,s.pts,s.dts)),n.key=!0,n.frame=!0;break;case 6:{o=!0,Li(l.data,1,s.pts,t.samples);break}case 7:{var f,g;o=!0,c=!0;const y=l.data,p=this.readSPS(y);if(!e.sps||e.width!==p.width||e.height!==p.height||((f=e.pixelRatio)==null?void 0:f[0])!==p.pixelRatio[0]||((g=e.pixelRatio)==null?void 0:g[1])!==p.pixelRatio[1]){e.width=p.width,e.height=p.height,e.pixelRatio=p.pixelRatio,e.sps=[y];const E=y.subarray(1,4);let T="avc1.";for(let S=0;S<3;S++){let v=E[S].toString(16);v.length<2&&(v="0"+v),T+=v}e.codec=T}break}case 8:o=!0,e.pps=[l.data];break;case 9:o=!0,e.audFound=!0,(u=n)!=null&&u.frame&&(this.pushAccessUnit(n,e),n=null),n||(n=this.VideoSample=this.createVideoSample(!1,s.pts,s.dts));break;case 12:o=!0;break;default:o=!1;break}n&&o&&n.units.push(l)}),i&&n&&(this.pushAccessUnit(n,e),this.VideoSample=null)}getNALuType(e,t){return e[t]&31}readSliceType(e){const t=new Bt(e);return t.readUByte(),t.readUEG(),t.readUEG()}skipScalingList(e,t){let s=8,i=8,r;for(let n=0;n{var h,u;switch(l.type){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9:n||(n=this.VideoSample=this.createVideoSample(!1,s.pts,s.dts)),n.frame=!0,o=!0;break;case 16:case 17:case 18:case 21:if(o=!0,c){var d;(d=n)!=null&&d.frame&&!n.key&&(this.pushAccessUnit(n,e),n=this.VideoSample=null)}n||(n=this.VideoSample=this.createVideoSample(!0,s.pts,s.dts)),n.key=!0,n.frame=!0;break;case 19:case 20:o=!0,(h=n)!=null&&h.frame&&!n.key&&(this.pushAccessUnit(n,e),n=this.VideoSample=null),n||(n=this.VideoSample=this.createVideoSample(!0,s.pts,s.dts)),n.key=!0,n.frame=!0;break;case 39:o=!0,Li(l.data,2,s.pts,t.samples);break;case 32:o=!0,e.vps||(typeof e.params!="object"&&(e.params={}),e.params=ne(e.params,this.readVPS(l.data)),this.initVPS=l.data),e.vps=[l.data];break;case 33:if(o=!0,c=!0,e.vps!==void 0&&e.vps[0]!==this.initVPS&&e.sps!==void 0&&!this.matchSPS(e.sps[0],l.data)&&(this.initVPS=e.vps[0],e.sps=e.pps=void 0),!e.sps){const f=this.readSPS(l.data);e.width=f.width,e.height=f.height,e.pixelRatio=f.pixelRatio,e.codec=f.codecString,e.sps=[],typeof e.params!="object"&&(e.params={});for(const g in f.params)e.params[g]=f.params[g]}this.pushParameterSet(e.sps,l.data,e.vps),n||(n=this.VideoSample=this.createVideoSample(!0,s.pts,s.dts)),n.key=!0;break;case 34:if(o=!0,typeof e.params=="object"){if(!e.pps){e.pps=[];const f=this.readPPS(l.data);for(const g in f)e.params[g]=f[g]}this.pushParameterSet(e.pps,l.data,e.vps)}break;case 35:o=!0,e.audFound=!0,(u=n)!=null&&u.frame&&(this.pushAccessUnit(n,e),n=null),n||(n=this.VideoSample=this.createVideoSample(!1,s.pts,s.dts));break;default:o=!1;break}n&&o&&n.units.push(l)}),i&&n&&(this.pushAccessUnit(n,e),this.VideoSample=null)}pushParameterSet(e,t,s){(s&&s[0]===this.initVPS||!s&&!e.length)&&e.push(t)}getNALuType(e,t){return(e[t]&126)>>>1}ebsp2rbsp(e){const t=new Uint8Array(e.byteLength);let s=0;for(let i=0;i=2&&e[i]===3&&e[i-1]===0&&e[i-2]===0||(t[s]=e[i],s++);return new Uint8Array(t.buffer,0,s)}pushAccessUnit(e,t){super.pushAccessUnit(e,t),this.initVPS&&(this.initVPS=null)}readVPS(e){const t=new Bt(e);t.readUByte(),t.readUByte(),t.readBits(4),t.skipBits(2),t.readBits(6);const s=t.readBits(3),i=t.readBoolean();return{numTemporalLayers:s+1,temporalIdNested:i}}readSPS(e){const t=new Bt(this.ebsp2rbsp(e));t.readUByte(),t.readUByte(),t.readBits(4);const s=t.readBits(3);t.readBoolean();const i=t.readBits(2),r=t.readBoolean(),n=t.readBits(5),o=t.readUByte(),c=t.readUByte(),l=t.readUByte(),h=t.readUByte(),u=t.readUByte(),d=t.readUByte(),f=t.readUByte(),g=t.readUByte(),y=t.readUByte(),p=t.readUByte(),E=t.readUByte(),T=[],S=[];for(let te=0;te0)for(let te=s;te<8;te++)t.readBits(2);for(let te=0;te1&&t.readEG();for(let pt=0;pt0&&ht<16?(O=ji[ht-1],z=jt[ht-1]):ht===255&&(O=t.readBits(16),z=t.readBits(16))}if(t.readBoolean()&&t.readBoolean(),t.readBoolean()&&(t.readBits(3),t.readBoolean(),t.readBoolean()&&(t.readUByte(),t.readUByte(),t.readUByte())),t.readBoolean()&&(t.readUEG(),t.readUEG()),t.readBoolean(),t.readBoolean(),t.readBoolean(),Te=t.readBoolean(),Te&&(t.skipUEG(),t.skipUEG(),t.skipUEG(),t.skipUEG()),t.readBoolean()&&(Q=t.readBits(32),ee=t.readBits(32),t.readBoolean()&&t.readUEG(),t.readBoolean())){const jt=t.readBoolean(),Xi=t.readBoolean();let kt=!1;(jt||Xi)&&(kt=t.readBoolean(),kt&&(t.readUByte(),t.readBits(5),t.readBoolean(),t.readBits(5)),t.readBits(4),t.readBits(4),kt&&t.readBits(4),t.readBits(5),t.readBits(5),t.readBits(5));for(let zi=0;zi<=s;zi++){se=t.readBoolean();const Ja=se||t.readBoolean();let Qi=!1;Ja?t.readEG():Qi=t.readBoolean();const Zi=Qi?1:t.readUEG()+1;if(jt)for(let wt=0;wt>te&1)<<31-te)>>>0;let ks=Ps.toString(16);return n===1&&ks==="2"&&(ks="6"),{codecString:`hvc1.${lt}${n}.${ks}.${r?"H":"L"}${E}.B0`,params:{general_tier_flag:r,general_profile_idc:n,general_profile_space:i,general_profile_compatibility_flags:[o,c,l,h],general_constraint_indicator_flags:[u,d,f,g,y,p],general_level_idc:E,bit_depth:F+8,bit_depth_luma_minus8:F,bit_depth_chroma_minus8:$,min_spatial_segmentation_idc:w,chroma_format_idc:v,frame_rate:{fixed:se,fps:ee/Q}},width:De,height:Be,pixelRatio:[O,z]}}readPPS(e){const t=new Bt(this.ebsp2rbsp(e));t.readUByte(),t.readUByte(),t.skipUEG(),t.skipUEG(),t.skipBits(2),t.skipBits(3),t.skipBits(2),t.skipUEG(),t.skipUEG(),t.skipEG(),t.skipBits(2),t.readBoolean()&&t.skipUEG(),t.skipEG(),t.skipEG(),t.skipBits(4);const i=t.readBoolean(),r=t.readBoolean();let n=1;return r&&i?n=0:r?n=3:i&&(n=2),{parallelismType:n}}matchSPS(e,t){return String.fromCharCode.apply(null,e).substr(3)===String.fromCharCode.apply(null,t).substr(3)}}const ge=188;class it{constructor(e,t,s,i){this.logger=void 0,this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.sampleAes=null,this.pmtParsed=!1,this.audioCodec=void 0,this.videoCodec=void 0,this._pmtId=-1,this._videoTrack=void 0,this._audioTrack=void 0,this._id3Track=void 0,this._txtTrack=void 0,this.aacOverFlow=null,this.remainderData=null,this.videoParser=void 0,this.observer=e,this.config=t,this.typeSupported=s,this.logger=i,this.videoParser=null}static probe(e,t){const s=it.syncOffset(e);return s>0&&t.warn(`MPEG2-TS detected but first sync word found @ offset ${s}`),s!==-1}static syncOffset(e){const t=e.length;let s=Math.min(ge*5,t-ge)+1,i=0;for(;i1&&(n===0&&o>2||c+ge>s))return n}else{if(o)return-1;break}i++}return-1}static createTrack(e,t){return{container:e==="video"||e==="audio"?"video/mp2t":void 0,type:e,id:An[e],pid:-1,inputTimeScale:9e4,sequenceNumber:0,samples:[],dropped:0,duration:e==="audio"?t:void 0}}resetInitSegment(e,t,s,i){this.pmtParsed=!1,this._pmtId=-1,this._videoTrack=it.createTrack("video"),this._videoTrack.duration=i,this._audioTrack=it.createTrack("audio",i),this._id3Track=it.createTrack("id3"),this._txtTrack=it.createTrack("text"),this._audioTrack.segmentCodec="aac",this.videoParser=null,this.aacOverFlow=null,this.remainderData=null,this.audioCodec=t,this.videoCodec=s}resetTimeStamp(){}resetContiguity(){const{_audioTrack:e,_videoTrack:t,_id3Track:s}=this;e&&(e.pesData=null),t&&(t.pesData=null),s&&(s.pesData=null),this.aacOverFlow=null,this.remainderData=null}demux(e,t,s=!1,i=!1){s||(this.sampleAes=null);let r;const n=this._videoTrack,o=this._audioTrack,c=this._id3Track,l=this._txtTrack;let h=n.pid,u=n.pesData,d=o.pid,f=c.pid,g=o.pesData,y=c.pesData,p=null,E=this.pmtParsed,T=this._pmtId,S=e.length;if(this.remainderData&&(e=ke(this.remainderData,e),S=e.length,this.remainderData=null),S>4;let P;if(I>1){if(P=A+5+e[A+4],P===A+ge)continue}else P=A+4;switch(b){case h:_&&(u&&(r=St(u,this.logger))&&(this.readyVideoParser(n.segmentCodec),this.videoParser!==null&&this.videoParser.parsePES(n,l,r,!1)),u={data:[],size:0}),u&&(u.data.push(e.subarray(P,A+ge)),u.size+=A+ge-P);break;case d:if(_){if(g&&(r=St(g,this.logger)))switch(o.segmentCodec){case"aac":this.parseAACPES(o,r);break;case"mp3":this.parseMPEGPES(o,r);break;case"ac3":this.parseAC3PES(o,r);break}g={data:[],size:0}}g&&(g.data.push(e.subarray(P,A+ge)),g.size+=A+ge-P);break;case f:_&&(y&&(r=St(y,this.logger))&&this.parseID3PES(c,r),y={data:[],size:0}),y&&(y.data.push(e.subarray(P,A+ge)),y.size+=A+ge-P);break;case 0:_&&(P+=e[P]+1),T=this._pmtId=Ic(e,P);break;case T:{_&&(P+=e[P]+1);const F=Lc(e,P,this.typeSupported,s,this.observer,this.logger);h=F.videoPid,h>0&&(n.pid=h,n.segmentCodec=F.segmentVideoCodec),d=F.audioPid,d>0&&(o.pid=d,o.segmentCodec=F.segmentAudioCodec),f=F.id3Pid,f>0&&(c.pid=f),p!==null&&!E&&(this.logger.warn(`MPEG-TS PMT found at ${A} after unknown PID '${p}'. Backtracking to sync byte @${v} to parse all TS packets.`),p=null,A=v-188),E=this.pmtParsed=!0;break}case 17:case 8191:break;default:p=b;break}}else x++;x>0&&fi(this.observer,new Error(`Found ${x} TS packet/s that do not start with 0x47`),void 0,this.logger),n.pesData=u,o.pesData=g,c.pesData=y;const D={audioTrack:o,videoTrack:n,id3Track:c,textTrack:l};return i&&this.extractRemainingSamples(D),D}flush(){const{remainderData:e}=this;this.remainderData=null;let t;return e?t=this.demux(e,-1,!1,!0):t={videoTrack:this._videoTrack,audioTrack:this._audioTrack,id3Track:this._id3Track,textTrack:this._txtTrack},this.extractRemainingSamples(t),this.sampleAes?this.decrypt(t,this.sampleAes):t}extractRemainingSamples(e){const{audioTrack:t,videoTrack:s,id3Track:i,textTrack:r}=e,n=s.pesData,o=t.pesData,c=i.pesData;let l;if(n&&(l=St(n,this.logger))?(this.readyVideoParser(s.segmentCodec),this.videoParser!==null&&(this.videoParser.parsePES(s,r,l,!0),s.pesData=null)):s.pesData=n,o&&(l=St(o,this.logger))){switch(t.segmentCodec){case"aac":this.parseAACPES(t,l);break;case"mp3":this.parseMPEGPES(t,l);break;case"ac3":this.parseAC3PES(t,l);break}t.pesData=null}else o!=null&&o.size&&this.logger.log("last AAC PES packet truncated,might overlap between fragments"),t.pesData=o;c&&(l=St(c,this.logger))?(this.parseID3PES(i,l),i.pesData=null):i.pesData=c}demuxSampleAes(e,t,s){const i=this.demux(e,s,!0,!this.config.progressive),r=this.sampleAes=new vc(this.observer,this.config,t);return this.decrypt(i,r)}readyVideoParser(e){this.videoParser===null&&(e==="avc"?this.videoParser=new xc:e==="hevc"&&(this.videoParser=new Ac))}decrypt(e,t){return new Promise(s=>{const{audioTrack:i,videoTrack:r}=e;i.samples&&i.segmentCodec==="aac"?t.decryptAacSamples(i.samples,0,()=>{r.samples?t.decryptAvcSamples(r.samples,0,0,()=>{s(e)}):s(e)}):r.samples&&t.decryptAvcSamples(r.samples,0,0,()=>{s(e)})})}destroy(){this.observer&&this.observer.removeAllListeners(),this.config=this.logger=this.observer=null,this.aacOverFlow=this.videoParser=this.remainderData=this.sampleAes=null,this._videoTrack=this._audioTrack=this._id3Track=this._txtTrack=void 0}parseAACPES(e,t){let s=0;const i=this.aacOverFlow;let r=t.data;if(i){this.aacOverFlow=null;const u=i.missing,d=i.sample.unit.byteLength;if(u===-1)r=ke(i.sample.unit,r);else{const f=d-u;i.sample.unit.set(r.subarray(0,u),f),e.samples.push(i.sample),s=i.missing}}let n,o;for(n=s,o=r.length;n0;)o+=c}}parseID3PES(e,t){if(t.pts===void 0){this.logger.warn("[tsdemuxer]: ID3 PES unknown PTS");return}const s=ne({},t,{type:this._videoTrack?be.emsg:be.audioId3,duration:Number.POSITIVE_INFINITY});e.samples.push(s)}}function di(a,e){return((a[e+1]&31)<<8)+a[e+2]}function Ic(a,e){return(a[e+10]&31)<<8|a[e+11]}function Lc(a,e,t,s,i,r){const n={audioPid:-1,videoPid:-1,id3Pid:-1,segmentVideoCodec:"avc",segmentAudioCodec:"aac"},o=(a[e+1]&15)<<8|a[e+2],c=e+3+o-4,l=(a[e+10]&15)<<8|a[e+11];for(e+=12+l;e0){let d=e+5,f=u;for(;f>2;){switch(a[d]){case 106:t.ac3!==!0?r.log("AC-3 audio found, not supported in this browser for now"):(n.audioPid=h,n.segmentAudioCodec="ac3");break}const y=a[d+1]+2;d+=y,f-=y}}break;case 194:case 135:return fi(i,new Error("Unsupported EC-3 in M2TS found"),void 0,r),n;case 36:n.videoPid===-1&&(n.videoPid=h,n.segmentVideoCodec="hevc",r.log("HEVC in M2TS found"));break}e+=u+5}return n}function fi(a,e,t,s){s.warn(`parsing error: ${e.message}`),a.emit(m.ERROR,m.ERROR,{type:H.MEDIA_ERROR,details:R.FRAG_PARSING_ERROR,fatal:!1,levelRetry:t,error:e,reason:e.message})}function Ks(a,e){e.log(`${a} with AES-128-CBC encryption found in unencrypted stream`)}function St(a,e){let t=0,s,i,r,n,o;const c=a.data;if(!a||a.size===0)return null;for(;c[0].length<19&&c.length>1;)c[0]=ke(c[0],c[1]),c.splice(1,1);if(s=c[0],(s[0]<<16)+(s[1]<<8)+s[2]===1){if(i=(s[4]<<8)+s[5],i&&i>a.size-6)return null;const h=s[7];h&192&&(n=(s[9]&14)*536870912+(s[10]&255)*4194304+(s[11]&254)*16384+(s[12]&255)*128+(s[13]&254)/2,h&64?(o=(s[14]&14)*536870912+(s[15]&255)*4194304+(s[16]&254)*16384+(s[17]&255)*128+(s[18]&254)/2,n-o>60*9e4&&(e.warn(`${Math.round((n-o)/9e4)}s delta between PTS and DTS, align them`),n=o)):o=n),r=s[8];let u=r+9;if(a.size<=u)return null;a.size-=u;const d=new Uint8Array(a.size);for(let f=0,g=c.length;fy){u-=y;continue}else s=s.subarray(u),y-=u,u=0;d.set(s,t),t+=y}return i&&(i-=r+3),{data:d,pts:n,dts:o,len:i}}return null}class Rc{static getSilentFrame(e,t){switch(e){case"mp4a.40.2":if(t===1)return new Uint8Array([0,200,0,128,35,128]);if(t===2)return new Uint8Array([33,0,73,144,2,25,0,35,128]);if(t===3)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,142]);if(t===4)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,128,44,128,8,2,56]);if(t===5)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,130,48,4,153,0,33,144,2,56]);if(t===6)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,130,48,4,153,0,33,144,2,0,178,0,32,8,224]);break;default:if(t===1)return new Uint8Array([1,64,34,128,163,78,230,128,186,8,0,0,0,28,6,241,193,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);if(t===2)return new Uint8Array([1,64,34,128,163,94,230,128,186,8,0,0,0,0,149,0,6,241,161,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);if(t===3)return new Uint8Array([1,64,34,128,163,94,230,128,186,8,0,0,0,0,149,0,6,241,161,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);break}}}const et=Math.pow(2,32)-1;class L{static init(){L.types={avc1:[],avcC:[],hvc1:[],hvcC:[],btrt:[],dinf:[],dref:[],esds:[],ftyp:[],hdlr:[],mdat:[],mdhd:[],mdia:[],mfhd:[],minf:[],moof:[],moov:[],mp4a:[],".mp3":[],dac3:[],"ac-3":[],mvex:[],mvhd:[],pasp:[],sdtp:[],stbl:[],stco:[],stsc:[],stsd:[],stsz:[],stts:[],tfdt:[],tfhd:[],traf:[],trak:[],trun:[],trex:[],tkhd:[],vmhd:[],smhd:[]};let e;for(e in L.types)L.types.hasOwnProperty(e)&&(L.types[e]=[e.charCodeAt(0),e.charCodeAt(1),e.charCodeAt(2),e.charCodeAt(3)]);const t=new Uint8Array([0,0,0,0,0,0,0,0,118,105,100,101,0,0,0,0,0,0,0,0,0,0,0,0,86,105,100,101,111,72,97,110,100,108,101,114,0]),s=new Uint8Array([0,0,0,0,0,0,0,0,115,111,117,110,0,0,0,0,0,0,0,0,0,0,0,0,83,111,117,110,100,72,97,110,100,108,101,114,0]);L.HDLR_TYPES={video:t,audio:s};const i=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,12,117,114,108,32,0,0,0,1]),r=new Uint8Array([0,0,0,0,0,0,0,0]);L.STTS=L.STSC=L.STCO=r,L.STSZ=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0]),L.VMHD=new Uint8Array([0,0,0,1,0,0,0,0,0,0,0,0]),L.SMHD=new Uint8Array([0,0,0,0,0,0,0,0]),L.STSD=new Uint8Array([0,0,0,0,0,0,0,1]);const n=new Uint8Array([105,115,111,109]),o=new Uint8Array([97,118,99,49]),c=new Uint8Array([0,0,0,1]);L.FTYP=L.box(L.types.ftyp,n,c,n,o),L.DINF=L.box(L.types.dinf,L.box(L.types.dref,i))}static box(e,...t){let s=8,i=t.length;const r=i;for(;i--;)s+=t[i].byteLength;const n=new Uint8Array(s);for(n[0]=s>>24&255,n[1]=s>>16&255,n[2]=s>>8&255,n[3]=s&255,n.set(e,4),i=0,s=8;i>24&255,e>>16&255,e>>8&255,e&255,s>>24,s>>16&255,s>>8&255,s&255,i>>24,i>>16&255,i>>8&255,i&255,85,196,0,0]))}static mdia(e){return L.box(L.types.mdia,L.mdhd(e.timescale||0,e.duration||0),L.hdlr(e.type),L.minf(e))}static mfhd(e){return L.box(L.types.mfhd,new Uint8Array([0,0,0,0,e>>24,e>>16&255,e>>8&255,e&255]))}static minf(e){return e.type==="audio"?L.box(L.types.minf,L.box(L.types.smhd,L.SMHD),L.DINF,L.stbl(e)):L.box(L.types.minf,L.box(L.types.vmhd,L.VMHD),L.DINF,L.stbl(e))}static moof(e,t,s){return L.box(L.types.moof,L.mfhd(e),L.traf(s,t))}static moov(e){let t=e.length;const s=[];for(;t--;)s[t]=L.trak(e[t]);return L.box.apply(null,[L.types.moov,L.mvhd(e[0].timescale||0,e[0].duration||0)].concat(s).concat(L.mvex(e)))}static mvex(e){let t=e.length;const s=[];for(;t--;)s[t]=L.trex(e[t]);return L.box.apply(null,[L.types.mvex,...s])}static mvhd(e,t){t*=e;const s=Math.floor(t/(et+1)),i=Math.floor(t%(et+1)),r=new Uint8Array([1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,e>>24&255,e>>16&255,e>>8&255,e&255,s>>24,s>>16&255,s>>8&255,s&255,i>>24,i>>16&255,i>>8&255,i&255,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]);return L.box(L.types.mvhd,r)}static sdtp(e){const t=e.samples||[],s=new Uint8Array(4+t.length);let i,r;for(i=0;i>>8&255),t.push(n&255),t=t.concat(Array.prototype.slice.call(r));for(i=0;i>>8&255),s.push(n&255),s=s.concat(Array.prototype.slice.call(r));const o=L.box(L.types.avcC,new Uint8Array([1,t[3],t[4],t[5],255,224|e.sps.length].concat(t).concat([e.pps.length]).concat(s))),c=e.width,l=e.height,h=e.pixelRatio[0],u=e.pixelRatio[1];return L.box(L.types.avc1,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,c>>8&255,c&255,l>>8&255,l&255,0,72,0,0,0,72,0,0,0,0,0,0,0,1,18,100,97,105,108,121,109,111,116,105,111,110,47,104,108,115,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,17,17]),o,L.box(L.types.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])),L.box(L.types.pasp,new Uint8Array([h>>24,h>>16&255,h>>8&255,h&255,u>>24,u>>16&255,u>>8&255,u&255])))}static esds(e){const t=e.config;return new Uint8Array([0,0,0,0,3,25,0,1,0,4,17,64,21,0,0,0,0,0,0,0,0,0,0,0,5,2,...t,6,1,2])}static audioStsd(e){const t=e.samplerate||0;return new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,e.channelCount||0,0,16,0,0,0,0,t>>8&255,t&255,0,0])}static mp4a(e){return L.box(L.types.mp4a,L.audioStsd(e),L.box(L.types.esds,L.esds(e)))}static mp3(e){return L.box(L.types[".mp3"],L.audioStsd(e))}static ac3(e){return L.box(L.types["ac-3"],L.audioStsd(e),L.box(L.types.dac3,e.config))}static stsd(e){const{segmentCodec:t}=e;if(e.type==="audio"){if(t==="aac")return L.box(L.types.stsd,L.STSD,L.mp4a(e));if(t==="ac3"&&e.config)return L.box(L.types.stsd,L.STSD,L.ac3(e));if(t==="mp3"&&e.codec==="mp3")return L.box(L.types.stsd,L.STSD,L.mp3(e))}else if(e.pps&&e.sps){if(t==="avc")return L.box(L.types.stsd,L.STSD,L.avc1(e));if(t==="hevc"&&e.vps)return L.box(L.types.stsd,L.STSD,L.hvc1(e))}else throw new Error("video track missing pps or sps");throw new Error(`unsupported ${e.type} segment codec (${t}/${e.codec})`)}static tkhd(e){const t=e.id,s=(e.duration||0)*(e.timescale||0),i=e.width||0,r=e.height||0,n=Math.floor(s/(et+1)),o=Math.floor(s%(et+1));return L.box(L.types.tkhd,new Uint8Array([1,0,0,7,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,t>>24&255,t>>16&255,t>>8&255,t&255,0,0,0,0,n>>24,n>>16&255,n>>8&255,n&255,o>>24,o>>16&255,o>>8&255,o&255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,i>>8&255,i&255,0,0,r>>8&255,r&255,0,0]))}static traf(e,t){const s=L.sdtp(e),i=e.id,r=Math.floor(t/(et+1)),n=Math.floor(t%(et+1));return L.box(L.types.traf,L.box(L.types.tfhd,new Uint8Array([0,0,0,0,i>>24,i>>16&255,i>>8&255,i&255])),L.box(L.types.tfdt,new Uint8Array([1,0,0,0,r>>24,r>>16&255,r>>8&255,r&255,n>>24,n>>16&255,n>>8&255,n&255])),L.trun(e,s.length+16+20+8+16+8+8),s)}static trak(e){return e.duration=e.duration||4294967295,L.box(L.types.trak,L.tkhd(e),L.mdia(e))}static trex(e){const t=e.id;return L.box(L.types.trex,new Uint8Array([0,0,0,0,t>>24,t>>16&255,t>>8&255,t&255,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]))}static trun(e,t){const s=e.samples||[],i=s.length,r=12+16*i,n=new Uint8Array(r);let o,c,l,h,u,d;for(t+=8+r,n.set([e.type==="video"?1:0,0,15,1,i>>>24&255,i>>>16&255,i>>>8&255,i&255,t>>>24&255,t>>>16&255,t>>>8&255,t&255],0),o=0;o>>24&255,l>>>16&255,l>>>8&255,l&255,h>>>24&255,h>>>16&255,h>>>8&255,h&255,u.isLeading<<2|u.dependsOn,u.isDependedOn<<6|u.hasRedundancy<<4|u.paddingValue<<1|u.isNonSync,u.degradPrio&61440,u.degradPrio&15,d>>>24&255,d>>>16&255,d>>>8&255,d&255],12+16*o);return L.box(L.types.trun,n)}static initSegment(e){L.types||L.init();const t=L.moov(e);return ke(L.FTYP,t)}static hvc1(e){const t=e.params,s=[e.vps,e.sps,e.pps],i=4,r=new Uint8Array([1,t.general_profile_space<<6|(t.general_tier_flag?32:0)|t.general_profile_idc,t.general_profile_compatibility_flags[0],t.general_profile_compatibility_flags[1],t.general_profile_compatibility_flags[2],t.general_profile_compatibility_flags[3],t.general_constraint_indicator_flags[0],t.general_constraint_indicator_flags[1],t.general_constraint_indicator_flags[2],t.general_constraint_indicator_flags[3],t.general_constraint_indicator_flags[4],t.general_constraint_indicator_flags[5],t.general_level_idc,240|t.min_spatial_segmentation_idc>>8,255&t.min_spatial_segmentation_idc,252|t.parallelismType,252|t.chroma_format_idc,248|t.bit_depth_luma_minus8,248|t.bit_depth_chroma_minus8,0,parseInt(t.frame_rate.fps),i-1|t.temporal_id_nested<<2|t.num_temporal_layers<<3|(t.frame_rate.fixed?64:0),s.length]);let n=r.length;for(let g=0;g>8,s[g][y].length&255]),n),n+=2,o.set(s[g][y],n),n+=s[g][y].length}const l=L.box(L.types.hvcC,o),h=e.width,u=e.height,d=e.pixelRatio[0],f=e.pixelRatio[1];return L.box(L.types.hvc1,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,h>>8&255,h&255,u>>8&255,u&255,0,72,0,0,0,72,0,0,0,0,0,0,0,1,18,100,97,105,108,121,109,111,116,105,111,110,47,104,108,115,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,17,17]),l,L.box(L.types.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])),L.box(L.types.pasp,new Uint8Array([d>>24,d>>16&255,d>>8&255,d&255,f>>24,f>>16&255,f>>8&255,f&255])))}}L.types=void 0;L.HDLR_TYPES=void 0;L.STTS=void 0;L.STSC=void 0;L.STCO=void 0;L.STSZ=void 0;L.VMHD=void 0;L.SMHD=void 0;L.STSD=void 0;L.FTYP=void 0;L.DINF=void 0;const Ea=9e4;function $i(a,e,t=1,s=!1){const i=a*e*t;return s?Math.round(i):i}function bc(a,e,t=1,s=!1){return $i(a,e,1/t,s)}function Ot(a,e=!1){return $i(a,1e3,1/Ea,e)}function _c(a,e=1){return $i(a,Ea,1/e)}function Kr(a){const{baseTime:e,timescale:t,trackId:s}=a;return`${e/t} (${e}/${t}) trackId: ${s}`}const Dc=10*1e3,Cc=1024,Pc=1152,kc=1536;let vt=null,Vs=null;function Vr(a,e,t,s){return{duration:e,size:t,cts:s,flags:{isLeading:0,isDependedOn:0,hasRedundancy:0,degradPrio:0,dependsOn:a?2:1,isNonSync:a?0:1}}}class ds extends we{constructor(e,t,s,i){if(super("mp4-remuxer",i),this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.ISGenerated=!1,this._initPTS=null,this._initDTS=null,this.nextVideoTs=null,this.nextAudioTs=null,this.videoSampleDuration=null,this.isAudioContiguous=!1,this.isVideoContiguous=!1,this.videoTrackConfig=void 0,this.observer=e,this.config=t,this.typeSupported=s,this.ISGenerated=!1,vt===null){const n=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);vt=n?parseInt(n[1]):0}if(Vs===null){const r=navigator.userAgent.match(/Safari\/(\d+)/i);Vs=r?parseInt(r[1]):0}}destroy(){this.config=this.videoTrackConfig=this._initPTS=this._initDTS=null}resetTimeStamp(e){const t=this._initPTS;(!t||!e||e.trackId!==t.trackId||e.baseTime!==t.baseTime||e.timescale!==t.timescale)&&this.log(`Reset initPTS: ${t&&Kr(t)} > ${e&&Kr(e)}`),this._initPTS=this._initDTS=e}resetNextTimestamp(){this.log("reset next timestamp"),this.isVideoContiguous=!1,this.isAudioContiguous=!1}resetInitSegment(){this.log("ISGenerated flag reset"),this.ISGenerated=!1,this.videoTrackConfig=void 0}getVideoStartPts(e){let t=!1;const s=e[0].pts,i=e.reduce((r,n)=>{let o=n.pts,c=o-r;return c<-4294967296&&(t=!0,o=Re(o,s),c=o-r),c>0?r:o},s);return t&&this.debug("PTS rollover detected"),i}remux(e,t,s,i,r,n,o,c){let l,h,u,d,f,g,y=r,p=r;const E=e.pid>-1,T=t.pid>-1,S=t.samples.length,v=e.samples.length>0,x=o&&S>0||S>1;if((!E||v)&&(!T||x)||this.ISGenerated||o){if(this.ISGenerated){var A,_,b,I;const Y=this.videoTrackConfig;(Y&&(t.width!==Y.width||t.height!==Y.height||((A=t.pixelRatio)==null?void 0:A[0])!==((_=Y.pixelRatio)==null?void 0:_[0])||((b=t.pixelRatio)==null?void 0:b[1])!==((I=Y.pixelRatio)==null?void 0:I[1]))||!Y&&x||this.nextAudioTs===null&&v)&&this.resetInitSegment()}this.ISGenerated||(u=this.generateIS(e,t,r,n));const P=this.isVideoContiguous;let F=-1,$;if(x&&(F=wc(t.samples),!P&&this.config.forceKeyFrameOnDiscontinuity))if(g=!0,F>0){this.warn(`Dropped ${F} out of ${S} video samples due to a missing keyframe`);const Y=this.getVideoStartPts(t.samples);t.samples=t.samples.slice(F),t.dropped+=F,p+=(t.samples[0].pts-Y)/t.inputTimeScale,$=p}else F===-1&&(this.warn(`No keyframe found out of ${S} video samples`),g=!1);if(this.ISGenerated){if(v&&x){const Y=this.getVideoStartPts(t.samples),k=(Re(e.samples[0].pts,Y)-Y)/t.inputTimeScale;y+=Math.max(0,k),p+=Math.max(0,-k)}if(v){if(e.samplerate||(this.warn("regenerate InitSegment as audio detected"),u=this.generateIS(e,t,r,n)),h=this.remuxAudio(e,y,this.isAudioContiguous,n,T||x||c===U.AUDIO?p:void 0),x){const Y=h?h.endPTS-h.startPTS:0;t.inputTimeScale||(this.warn("regenerate InitSegment as video detected"),u=this.generateIS(e,t,r,n)),l=this.remuxVideo(t,p,P,Y)}}else x&&(l=this.remuxVideo(t,p,P,0));l&&(l.firstKeyFrame=F,l.independent=F!==-1,l.firstKeyFramePTS=$)}}return this.ISGenerated&&this._initPTS&&this._initDTS&&(s.samples.length&&(f=Ta(s,r,this._initPTS,this._initDTS)),i.samples.length&&(d=Sa(i,r,this._initPTS))),{audio:h,video:l,initSegment:u,independent:g,text:d,id3:f}}computeInitPts(e,t,s,i){const r=Math.round(s*t);let n=Re(e,r);if(n0?w-1:w].dts&&(T=!0)}T&&n.sort(function(w,O){const z=w.dts-O.dts,se=w.pts-O.pts;return z||se}),g=n[0].dts,y=n[n.length-1].dts;const v=y-g,x=v?Math.round(v/(c-1)):f||e.inputTimeScale/30;if(s){const w=g-S,O=w>x,z=w<-1;if((O||z)&&(O?this.warn(`${(e.segmentCodec||"").toUpperCase()}: ${Ot(w,!0)} ms (${w}dts) hole between fragments detected at ${t.toFixed(3)}`):this.warn(`${(e.segmentCodec||"").toUpperCase()}: ${Ot(-w,!0)} ms (${w}dts) overlapping between fragments detected at ${t.toFixed(3)}`),!z||S>=n[0].pts||vt)){g=S;const se=n[0].pts-w;if(O)n[0].dts=g,n[0].pts=se;else{let Q=!0;for(let ee=0;eese&&Q);ee++){const Te=n[ee].pts;if(n[ee].dts-=w,n[ee].pts-=w,ee0?O.dts-n[w-1].dts:x;if(Q=w>0?O.pts-n[w-1].pts:x,Te.stretchShortVideoTrack&&this.nextAudioTs!==null){const De=Math.floor(Te.maxBufferHole*r),Be=(i?p+i*r:this.nextAudioTs+h)-O.pts;Be>De?(f=Be-me,f<0?f=me:F=!0,this.log(`It is approximately ${Be/90} ms to the next segment; using duration ${f/90} ms for the last video frame.`)):f=me}else f=me}const ee=Math.round(O.pts-O.dts);$=Math.min($,f),G=Math.max(G,f),Y=Math.min(Y,Q),k=Math.max(k,Q),o.push(Vr(O.key,f,se,ee))}if(o.length){if(vt){if(vt<70){const w=o[0].flags;w.dependsOn=2,w.isNonSync=0}}else if(Vs&&k-Y0&&(i&&Math.abs(S-(E+T))<9e3||Math.abs(Re(y[0].pts,S)-(E+T))<20*h),y.forEach(function(k){k.pts=Re(k.pts,S)}),!s||E<0){const k=y.length;if(y=y.filter(K=>K.pts>=0),k!==y.length&&this.warn(`Removed ${y.length-k} of ${k} samples (initPTS ${T} / ${n})`),!y.length)return;r===0?E=0:i&&!g?E=Math.max(0,S-T):E=y[0].pts-T}if(e.segmentCodec==="aac"){const k=this.config.maxAudioFramesDrift;for(let K=0,B=E+T;K=k*h&&O0){A+=p;try{D=new Uint8Array(A)}catch(O){this.observer.emit(m.ERROR,m.ERROR,{type:H.MUX_ERROR,details:R.REMUX_ALLOC_ERROR,fatal:!1,error:O,bytes:A,reason:`fail allocating audio mdat ${A}`});return}d||(new DataView(D.buffer).setUint32(0,A),D.set(L.types.mdat,4))}else return;D.set(V,p);const w=V.byteLength;p+=w,f.push(Vr(!0,l,w,0)),x=W}const b=f.length;if(!b)return;const I=f[f.length-1];E=x-T,this.nextAudioTs=E+c*I.duration;const P=d?new Uint8Array(0):L.moof(e.sequenceNumber++,v/c,ne({},e,{samples:f}));e.samples=[];const F=(v-T)/n,$=this.nextAudioTs/n,G={data1:P,data2:D,startPTS:F,endPTS:$,startDTS:F,endDTS:$,type:"audio",hasAudio:!0,hasVideo:!1,nb:b};return this.isAudioContiguous=!0,G}}function Re(a,e){let t;if(e===null)return a;for(e4294967296;)a+=t;return a}function wc(a){for(let e=0;en.pts-o.pts);const r=a.samples;return a.samples=[],{samples:r}}class Oc extends we{constructor(e,t,s,i){super("passthrough-remuxer",i),this.emitInitSegment=!1,this.audioCodec=void 0,this.videoCodec=void 0,this.initData=void 0,this.initPTS=null,this.initTracks=void 0,this.lastEndTime=null,this.isVideoContiguous=!1}destroy(){}resetTimeStamp(e){this.lastEndTime=null;const t=this.initPTS;t&&e&&t.baseTime===e.baseTime&&t.timescale===e.timescale||(this.initPTS=e)}resetNextTimestamp(){this.isVideoContiguous=!1,this.lastEndTime=null}resetInitSegment(e,t,s,i){this.audioCodec=t,this.videoCodec=s,this.generateInitSegment(e,i),this.emitInitSegment=!0}generateInitSegment(e,t){let{audioCodec:s,videoCodec:i}=this;if(!(e!=null&&e.byteLength)){this.initTracks=void 0,this.initData=void 0;return}const{audio:r,video:n}=this.initData=Rn(e);if(t)vo(e,t);else{const c=r||n;c!=null&&c.encrypted&&this.warn(`Init segment with encrypted track with has no key ("${c.codec}")!`)}r&&(s=Hr(r,ae.AUDIO,this)),n&&(i=Hr(n,ae.VIDEO,this));const o={};r&&n?o.audiovideo={container:"video/mp4",codec:s+","+i,supplemental:n.supplemental,encrypted:n.encrypted,initSegment:e,id:"main"}:r?o.audio={container:"audio/mp4",codec:s,encrypted:r.encrypted,initSegment:e,id:"audio"}:n?o.video={container:"video/mp4",codec:i,supplemental:n.supplemental,encrypted:n.encrypted,initSegment:e,id:"main"}:this.warn("initSegment does not contain moov or trak boxes."),this.initTracks=o}remux(e,t,s,i,r,n){var o,c;let{initPTS:l,lastEndTime:h}=this;const u={audio:void 0,video:void 0,text:i,id3:s,initSegment:void 0};N(h)||(h=this.lastEndTime=r||0);const d=t.samples;if(!d.length)return u;const f={initPTS:void 0,timescale:void 0,trackId:void 0};let g=this.initData;if((o=g)!=null&&o.length||(this.generateInitSegment(d),g=this.initData),!((c=g)!=null&&c.length))return this.warn("Failed to generate initSegment."),u;this.emitInitSegment&&(f.tracks=this.initTracks,this.emitInitSegment=!1);const y=Ao(d,g,this),p=g.audio?y[g.audio.id]:null,E=g.video?y[g.video.id]:null,T=ts(E,1/0),S=ts(p,1/0),v=ts(E,0,!0),x=ts(p,0,!0);let D=r,A=0;const _=p&&(!E||!l&&S0?this.lastEndTime=P:(this.warn("Duration parsed from mp4 should be greater than zero"),this.resetNextTimestamp());const F=!!g.audio,$=!!g.video;let Y="";F&&(Y+="audio"),$&&(Y+="video");const G=(g.audio?g.audio.encrypted:!1)||(g.video?g.video.encrypted:!1),k={data1:d,startPTS:I,startDTS:I,endPTS:P,endDTS:P,type:Y,hasAudio:F,hasVideo:$,nb:1,dropped:0,encrypted:G};u.audio=F&&!$?k:void 0,u.video=$?k:void 0;const K=E==null?void 0:E.sampleCount;if(K){const B=E.keyFrameIndex,V=B!==-1;k.nb=K,k.dropped=B===0||this.isVideoContiguous?0:V?B:K,k.independent=V,k.firstKeyFrame=B,V&&E.keyFrameStart&&(k.firstKeyFramePTS=(E.keyFrameStart-l.baseTime)/l.timescale),this.isVideoContiguous||(u.independent=V),this.isVideoContiguous||(this.isVideoContiguous=V),k.dropped&&this.warn(`fmp4 does not start with IDR: firstIDR ${B}/${K} dropped: ${k.dropped} start: ${k.firstKeyFramePTS||"NA"}`)}return u.initSegment=f,u.id3=Ta(s,r,l,l),i.samples.length&&(u.text=Sa(i,r,l)),u}}function ts(a,e,t=!1){return(a==null?void 0:a.start)!==void 0?(a.start+(t?a.duration:0))/a.timescale:e}function Fc(a,e,t,s){if(a===null)return!0;const i=Math.max(s,1),r=e-a.baseTime/a.timescale;return Math.abs(r-t)>i}function Hr(a,e,t){const s=a.codec;return s&&s.length>4?s:e===ae.AUDIO?s==="ec-3"||s==="ac-3"||s==="alac"?s:s==="fLaC"||s==="Opus"?Es(s,!1):(t.warn(`Unhandled audio codec "${s}" in mp4 MAP`),s||"mp4a"):(t.warn(`Unhandled video codec "${s}" in mp4 MAP`),s||"avc1")}let Qe;try{Qe=self.performance.now.bind(self.performance)}catch{Qe=Date.now}const fs=[{demux:Sc,remux:Oc},{demux:it,remux:ds},{demux:pc,remux:ds},{demux:Ec,remux:ds}];fs.splice(2,0,{demux:yc,remux:ds});class Yr{constructor(e,t,s,i,r,n){this.asyncResult=!1,this.logger=void 0,this.observer=void 0,this.typeSupported=void 0,this.config=void 0,this.id=void 0,this.demuxer=void 0,this.remuxer=void 0,this.decrypter=void 0,this.probe=void 0,this.decryptionPromise=null,this.transmuxConfig=void 0,this.currentTransmuxState=void 0,this.observer=e,this.typeSupported=t,this.config=s,this.id=r,this.logger=n}configure(e){this.transmuxConfig=e,this.decrypter&&this.decrypter.reset()}push(e,t,s,i){const r=s.transmuxing;r.executeStart=Qe();let n=new Uint8Array(e);const{currentTransmuxState:o,transmuxConfig:c}=this;i&&(this.currentTransmuxState=i);const{contiguous:l,discontinuity:h,trackSwitch:u,accurateTimeOffset:d,timeOffset:f,initSegmentChange:g}=i||o,{audioCodec:y,videoCodec:p,defaultInitPts:E,duration:T,initSegmentData:S}=c,v=Mc(n,t);if(v&&bt(v.method)){const _=this.getDecrypter(),b=Pi(v.method);if(_.isSync()){let I=_.softwareDecrypt(n,v.key.buffer,v.iv.buffer,b);if(s.part>-1){const F=_.flush();I=F&&F.buffer}if(!I)return r.executeEnd=Qe(),Hs(s);n=new Uint8Array(I)}else return this.asyncResult=!0,this.decryptionPromise=_.webCryptoDecrypt(n,v.key.buffer,v.iv.buffer,b).then(I=>{const P=this.push(I,null,s);return this.decryptionPromise=null,P}),this.decryptionPromise}const x=this.needsProbing(h,u);if(x){const _=this.configureTransmuxer(n);if(_)return this.logger.warn(`[transmuxer] ${_.message}`),this.observer.emit(m.ERROR,m.ERROR,{type:H.MEDIA_ERROR,details:R.FRAG_PARSING_ERROR,fatal:!1,error:_,reason:_.message}),r.executeEnd=Qe(),Hs(s)}(h||u||g||x)&&this.resetInitSegment(S,y,p,T,t),(h||g||x)&&this.resetInitialTimestamp(E),l||this.resetContiguity();const D=this.transmux(n,v,f,d,s);this.asyncResult=Yt(D);const A=this.currentTransmuxState;return A.contiguous=!0,A.discontinuity=!1,A.trackSwitch=!1,r.executeEnd=Qe(),D}flush(e){const t=e.transmuxing;t.executeStart=Qe();const{decrypter:s,currentTransmuxState:i,decryptionPromise:r}=this;if(r)return this.asyncResult=!0,r.then(()=>this.flush(e));const n=[],{timeOffset:o}=i;if(s){const u=s.flush();u&&n.push(this.push(u.buffer,null,e))}const{demuxer:c,remuxer:l}=this;if(!c||!l){t.executeEnd=Qe();const u=[Hs(e)];return this.asyncResult?Promise.resolve(u):u}const h=c.flush(o);return Yt(h)?(this.asyncResult=!0,h.then(u=>(this.flushRemux(n,u,e),n))):(this.flushRemux(n,h,e),this.asyncResult?Promise.resolve(n):n)}flushRemux(e,t,s){const{audioTrack:i,videoTrack:r,id3Track:n,textTrack:o}=t,{accurateTimeOffset:c,timeOffset:l}=this.currentTransmuxState;this.logger.log(`[transmuxer.ts]: Flushed ${this.id} sn: ${s.sn}${s.part>-1?" part: "+s.part:""} of ${this.id===U.MAIN?"level":"track"} ${s.level}`);const h=this.remuxer.remux(i,r,n,o,l,c,!0,this.id);e.push({remuxResult:h,chunkMeta:s}),s.transmuxing.executeEnd=Qe()}resetInitialTimestamp(e){const{demuxer:t,remuxer:s}=this;!t||!s||(t.resetTimeStamp(e),s.resetTimeStamp(e))}resetContiguity(){const{demuxer:e,remuxer:t}=this;!e||!t||(e.resetContiguity(),t.resetNextTimestamp())}resetInitSegment(e,t,s,i,r){const{demuxer:n,remuxer:o}=this;!n||!o||(n.resetInitSegment(e,t,s,i),o.resetInitSegment(e,t,s,r))}destroy(){this.demuxer&&(this.demuxer.destroy(),this.demuxer=void 0),this.remuxer&&(this.remuxer.destroy(),this.remuxer=void 0)}transmux(e,t,s,i,r){let n;return t&&t.method==="SAMPLE-AES"?n=this.transmuxSampleAes(e,t,s,i,r):n=this.transmuxUnencrypted(e,s,i,r),n}transmuxUnencrypted(e,t,s,i){const{audioTrack:r,videoTrack:n,id3Track:o,textTrack:c}=this.demuxer.demux(e,t,!1,!this.config.progressive);return{remuxResult:this.remuxer.remux(r,n,o,c,t,s,!1,this.id),chunkMeta:i}}transmuxSampleAes(e,t,s,i,r){return this.demuxer.demuxSampleAes(e,t,s).then(n=>({remuxResult:this.remuxer.remux(n.audioTrack,n.videoTrack,n.id3Track,n.textTrack,s,i,!1,this.id),chunkMeta:r}))}configureTransmuxer(e){const{config:t,observer:s,typeSupported:i}=this;let r;for(let u=0,d=fs.length;u0&&(e==null?void 0:e.key)!=null&&e.iv!==null&&e.method!=null&&(t=e),t}const Hs=a=>({remuxResult:{},chunkMeta:a});function Yt(a){return"then"in a&&a.then instanceof Function}class Nc{constructor(e,t,s,i,r){this.audioCodec=void 0,this.videoCodec=void 0,this.initSegmentData=void 0,this.duration=void 0,this.defaultInitPts=void 0,this.audioCodec=e,this.videoCodec=t,this.initSegmentData=s,this.duration=i,this.defaultInitPts=r||null}}class Bc{constructor(e,t,s,i,r,n){this.discontinuity=void 0,this.contiguous=void 0,this.accurateTimeOffset=void 0,this.trackSwitch=void 0,this.timeOffset=void 0,this.initSegmentChange=void 0,this.discontinuity=e,this.contiguous=t,this.accurateTimeOffset=s,this.trackSwitch=i,this.timeOffset=r,this.initSegmentChange=n}}let Wr=0;class va{constructor(e,t,s,i){this.error=null,this.hls=void 0,this.id=void 0,this.instanceNo=Wr++,this.observer=void 0,this.frag=null,this.part=null,this.useWorker=void 0,this.workerContext=null,this.transmuxer=null,this.onTransmuxComplete=void 0,this.onFlush=void 0,this.onWorkerMessage=c=>{const l=c.data,h=this.hls;if(!(!h||!(l!=null&&l.event)||l.instanceNo!==this.instanceNo))switch(l.event){case"init":{var u;const d=(u=this.workerContext)==null?void 0:u.objectURL;d&&self.URL.revokeObjectURL(d);break}case"transmuxComplete":{this.handleTransmuxComplete(l.data);break}case"flush":{this.onFlush(l.data);break}case"workerLog":{h.logger[l.data.logType]&&h.logger[l.data.logType](l.data.message);break}default:{l.data=l.data||{},l.data.frag=this.frag,l.data.part=this.part,l.data.id=this.id,h.trigger(l.event,l.data);break}}},this.onWorkerError=c=>{if(!this.hls)return;const l=new Error(`${c.message} (${c.filename}:${c.lineno})`);this.hls.config.enableWorker=!1,this.hls.logger.warn(`Error in "${this.id}" Web Worker, fallback to inline`),this.hls.trigger(m.ERROR,{type:H.OTHER_ERROR,details:R.INTERNAL_EXCEPTION,fatal:!1,event:"demuxerWorker",error:l})};const r=e.config;this.hls=e,this.id=t,this.useWorker=!!r.enableWorker,this.onTransmuxComplete=s,this.onFlush=i;const n=(c,l)=>{l=l||{},l.frag=this.frag||void 0,c===m.ERROR&&(l=l,l.parent=this.id,l.part=this.part,this.error=l.error),this.hls.trigger(c,l)};this.observer=new Oi,this.observer.on(m.FRAG_DECRYPTED,n),this.observer.on(m.ERROR,n);const o=or(r.preferManagedMediaSource);if(this.useWorker&&typeof Worker<"u"){const c=this.hls.logger;if(r.workerPath||Kl()){try{r.workerPath?(c.log(`loading Web Worker ${r.workerPath} for "${t}"`),this.workerContext=Hl(r.workerPath)):(c.log(`injecting Web Worker for "${t}"`),this.workerContext=Vl());const{worker:h}=this.workerContext;h.addEventListener("message",this.onWorkerMessage),h.addEventListener("error",this.onWorkerError),h.postMessage({instanceNo:this.instanceNo,cmd:"init",typeSupported:o,id:t,config:oe(r)})}catch(h){c.warn(`Error setting up "${t}" Web Worker, fallback to inline`,h),this.terminateWorker(),this.error=null,this.transmuxer=new Yr(this.observer,o,r,"",t,e.logger)}return}}this.transmuxer=new Yr(this.observer,o,r,"",t,e.logger)}reset(){if(this.frag=null,this.part=null,this.workerContext){const e=this.instanceNo;this.instanceNo=Wr++;const t=this.hls.config,s=or(t.preferManagedMediaSource);this.workerContext.worker.postMessage({instanceNo:this.instanceNo,cmd:"reset",resetNo:e,typeSupported:s,id:this.id,config:oe(t)})}}terminateWorker(){if(this.workerContext){const{worker:e}=this.workerContext;this.workerContext=null,e.removeEventListener("message",this.onWorkerMessage),e.removeEventListener("error",this.onWorkerError),Yl(this.hls.config.workerPath)}}destroy(){if(this.workerContext)this.terminateWorker(),this.onWorkerMessage=this.onWorkerError=null;else{const t=this.transmuxer;t&&(t.destroy(),this.transmuxer=null)}const e=this.observer;e&&e.removeAllListeners(),this.frag=null,this.part=null,this.observer=null,this.hls=null}push(e,t,s,i,r,n,o,c,l,h){var u,d;l.transmuxing.start=self.performance.now();const{instanceNo:f,transmuxer:g}=this,y=n?n.start:r.start,p=r.decryptdata,E=this.frag,T=!(E&&r.cc===E.cc),S=!(E&&l.level===E.level),v=E?l.sn-E.sn:-1,x=this.part?l.part-this.part.index:-1,D=v===0&&l.id>1&&l.id===(E==null?void 0:E.stats.chunkCount),A=!S&&(v===1||v===0&&(x===1||D&&x<=0)),_=self.performance.now();(S||v||r.stats.parsing.start===0)&&(r.stats.parsing.start=_),n&&(x||!A)&&(n.stats.parsing.start=_);const b=!(E&&((u=r.initSegment)==null?void 0:u.url)===((d=E.initSegment)==null?void 0:d.url)),I=new Bc(T,A,c,S,y,b);if(!A||T||b){this.hls.logger.log(`[transmuxer-interface]: Starting new transmux session for ${r.type} sn: ${l.sn}${l.part>-1?" part: "+l.part:""} ${this.id===U.MAIN?"level":"track"}: ${l.level} id: ${l.id} +${t.m3u8}`)}function Zn(a,e,t=!0){const s=e.startSN+e.skippedSegments-a.startSN,i=a.fragments,r=s>=0;let n=0;if(r&&se){const r=s[s.length-1].duration*1e3;r{var s;(s=e.details)==null||s.fragments.forEach(i=>{i.level=t,i.initSegment&&(i.initSegment.level=t)})})}function Bl(a,e){return a!==e&&e?$r(a)!==$r(e):!1}function $r(a){return a.replace(/\?[^?]*$/,"")}function Nt(a,e){for(let s=0,i=a.length;sa.startCC)}function Ur(a,e){const t=a.start+e;a.startPTS=t,a.setStart(t),a.endPTS=t+a.duration}function ia(a,e){const t=e.fragments;for(let s=0,i=t.length;s{const{config:n,fragCurrent:o,media:c,mediaBuffer:l,state:h}=this,d=c?c.currentTime:0,u=X.bufferInfo(l||c,d,n.maxBufferHole),f=!u.len;if(this.log(`Media seeking to ${B(d)?d.toFixed(3):d}, state: ${h}, ${f?"out of":"in"} buffer`),this.state===C.ENDED)this.resetLoadingState();else if(o){const g=n.maxFragLookUpTolerance,y=o.start-g,p=o.start+o.duration+g;if(f||pu.end){const E=d>p;(dg&&(this.lastCurrentTime=d),!this.loadingParts){const y=Math.max(u.end,d),p=this.shouldLoadParts(this.getLevelDetails(),y);p&&(this.log(`LL-Part loading ON after seeking to ${d.toFixed(2)} with buffer @${y.toFixed(2)}`),this.loadingParts=p)}}this.hls.hasEnoughToStart||(this.log(`Setting ${f?"startPosition":"nextLoadPosition"} to ${d} for seek without enough to start`),this.nextLoadPosition=d,f&&(this.startPosition=d)),f&&this.state===C.IDLE&&this.tickImmediate()},this.onMediaEnded=()=>{this.log("setting startPosition to 0 because media ended"),this.startPosition=this.lastCurrentTime=0},this.playlistType=r,this.hls=e,this.fragmentLoader=new ml(e.config),this.keyLoader=s,this.fragmentTracker=t,this.config=e.config,this.decrypter=new Pi(e.config)}registerListeners(){const{hls:e}=this;e.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(m.ERROR,this.onError,this)}unregisterListeners(){const{hls:e}=this;e.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(m.ERROR,this.onError,this)}doTick(){this.onTickEnd()}onTickEnd(){}startLoad(e){}stopLoad(){if(this.state===C.STOPPED)return;this.fragmentLoader.abort(),this.keyLoader.abort(this.playlistType);const e=this.fragCurrent;e!=null&&e.loader&&(e.abortRequests(),this.fragmentTracker.removeFragment(e)),this.resetTransmuxer(),this.fragCurrent=null,this.fragPrevious=null,this.clearInterval(),this.clearNextTick(),this.state=C.STOPPED}get startPositionValue(){const{nextLoadPosition:e,startPosition:t}=this;return t===-1&&e?e:t}get bufferingEnabled(){return this.buffering}pauseBuffering(){this.buffering=!1}resumeBuffering(){this.buffering=!0}get inFlightFrag(){return{frag:this.fragCurrent,state:this.state}}_streamEnded(e,t){if(t.live||!this.media)return!1;const s=e.end||0,i=this.config.timelineOffset||0;if(s<=i)return!1;const r=e.buffered;this.config.maxBufferHole&&r&&r.length>1&&(e=X.bufferedInfo(r,e.start,0));const n=e.nextStart;if(n&&n>i&&n{const n=r.frag;if(this.fragContextChanged(n)){this.warn(`${n.type} sn: ${n.sn}${r.part?" part: "+r.part.index:""} of ${this.fragInfo(n,!1,r.part)}) was dropped during download.`),this.fragmentTracker.removeFragment(n);return}n.stats.chunkCount++,this._handleFragmentLoadProgress(r)};this._doFragLoad(e,t,s,i).then(r=>{if(!r)return;const n=this.state,o=r.frag;if(this.fragContextChanged(o)){(n===C.FRAG_LOADING||!this.fragCurrent&&n===C.PARSING)&&(this.fragmentTracker.removeFragment(o),this.state=C.IDLE);return}"payload"in r&&(this.log(`Loaded ${o.type} sn: ${o.sn} of ${this.playlistLabel()} ${o.level}`),this.hls.trigger(m.FRAG_LOADED,r)),this._handleFragmentLoadComplete(r)}).catch(r=>{this.state===C.STOPPED||this.state===C.ERROR||(this.warn(`Frag error: ${(r==null?void 0:r.message)||r}`),this.resetFragmentLoading(e))})}clearTrackerIfNeeded(e){var t;const{fragmentTracker:s}=this;if(s.getState(e)===fe.APPENDING){const r=e.type,n=this.getFwdBufferInfo(this.mediaBuffer,r),o=Math.max(e.duration,n?n.len:this.config.maxBufferLength),c=this.backtrackFragment;((c?e.sn-c.sn:0)===1||this.reduceMaxBufferLength(o,e.duration))&&s.removeFragment(e)}else((t=this.mediaBuffer)==null?void 0:t.buffered.length)===0?s.removeAllFragments():s.hasParts(e.type)&&(s.detectPartialFragments({frag:e,part:null,stats:e.stats,id:e.type}),s.getState(e)===fe.PARTIAL&&s.removeFragment(e))}checkLiveUpdate(e){if(e.updated&&!e.live){const t=e.fragments[e.fragments.length-1];this.fragmentTracker.detectPartialFragments({frag:t,part:null,stats:t.stats,id:t.type})}e.fragments[0]||(e.deltaUpdateFailed=!0)}waitForLive(e){const t=e.details;return(t==null?void 0:t.live)&&t.type!=="EVENT"&&(this.levelLastLoaded!==e||t.expired)}flushMainBuffer(e,t,s=null){if(!(e-t))return;const i={startOffset:e,endOffset:t,type:s};this.hls.trigger(m.BUFFER_FLUSHING,i)}_loadInitSegment(e,t){this._doFragLoad(e,t).then(s=>{const i=s==null?void 0:s.frag;if(!i||this.fragContextChanged(i)||!this.levels)throw new Error("init load aborted");return s}).then(s=>{const{hls:i}=this,{frag:r,payload:n}=s,o=r.decryptdata;if(n&&n.byteLength>0&&o!=null&&o.key&&o.iv&&_t(o.method)){const c=self.performance.now();return this.decrypter.decrypt(new Uint8Array(n),o.key.buffer,o.iv.buffer,wi(o.method)).catch(l=>{throw i.trigger(m.ERROR,{type:Y.MEDIA_ERROR,details:R.FRAG_DECRYPT_ERROR,fatal:!1,error:l,reason:l.message,frag:r}),l}).then(l=>{const h=self.performance.now();return i.trigger(m.FRAG_DECRYPTED,{frag:r,payload:l,stats:{tstart:c,tdecrypt:h}}),s.payload=l,this.completeInitSegmentLoad(s)})}return this.completeInitSegmentLoad(s)}).catch(s=>{this.state===C.STOPPED||this.state===C.ERROR||(this.warn(s),this.resetFragmentLoading(e))})}completeInitSegmentLoad(e){const{levels:t}=this;if(!t)throw new Error("init load aborted, missing levels");const s=e.frag.stats;this.state!==C.STOPPED&&(this.state=C.IDLE),e.frag.data=new Uint8Array(e.payload),s.parsing.start=s.buffering.start=self.performance.now(),s.parsing.end=s.buffering.end=self.performance.now(),this.tick()}unhandledEncryptionError(e,t){var s,i;const r=e.tracks;if(r&&!t.encrypted&&((s=r.audio)!=null&&s.encrypted||(i=r.video)!=null&&i.encrypted)&&(!this.config.emeEnabled||!this.keyLoader.emeController)){const n=this.media,o=new Error(`Encrypted track with no key in ${this.fragInfo(t)} (media ${n?"attached mediaKeys: "+n.mediaKeys:"detached"})`);return this.warn(o.message),!n||n.mediaKeys?!1:(this.hls.trigger(m.ERROR,{type:Y.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_NO_KEYS,fatal:!1,error:o,frag:t}),this.resetTransmuxer(),!0)}return!1}fragContextChanged(e){const{fragCurrent:t}=this;return!e||!t||e.sn!==t.sn||e.level!==t.level}fragBufferedComplete(e,t){const s=this.mediaBuffer?this.mediaBuffer:this.media;if(this.log(`Buffered ${e.type} sn: ${e.sn}${t?" part: "+t.index:""} of ${this.fragInfo(e,!1,t)} > buffer:${s?Gl.toString(X.getBuffered(s)):"(detached)"})`),de(e)){var i;if(e.type!==U.SUBTITLE){const n=e.elementaryStreams;if(!Object.keys(n).some(o=>!!n[o])){this.state=C.IDLE;return}}const r=(i=this.levels)==null?void 0:i[e.level];r!=null&&r.fragmentError&&(this.log(`Resetting level fragment error count of ${r.fragmentError} on frag buffered`),r.fragmentError=0)}this.state=C.IDLE}_handleFragmentLoadComplete(e){const{transmuxer:t}=this;if(!t)return;const{frag:s,part:i,partsLoaded:r}=e,n=!r||r.length===0||r.some(c=>!c),o=new ki(s.level,s.sn,s.stats.chunkCount+1,0,i?i.index:-1,!n);t.flush(o)}_handleFragmentLoadProgress(e){}_doFragLoad(e,t,s=null,i){var r;this.fragCurrent=e;const n=t.details;if(!this.levels||!n)throw new Error(`frag load aborted, missing level${n?"":" detail"}s`);let o=null;if(e.encrypted&&!((r=e.decryptdata)!=null&&r.key)){if(this.log(`Loading key for ${e.sn} of [${n.startSN}-${n.endSN}], ${this.playlistLabel()} ${e.level}`),this.state=C.KEY_LOADING,this.fragCurrent=e,o=this.keyLoader.load(e).then(u=>{if(!this.fragContextChanged(u.frag))return this.hls.trigger(m.KEY_LOADED,u),this.state===C.KEY_LOADING&&(this.state=C.IDLE),u}),this.hls.trigger(m.KEY_LOADING,{frag:e}),this.fragCurrent===null)return this.log("context changed in KEY_LOADING"),Promise.resolve(null)}else e.encrypted||(o=this.keyLoader.loadClear(e,n.encryptedFragments,this.startFragRequested),o&&this.log("[eme] blocking frag load until media-keys acquired"));const c=this.fragPrevious;if(de(e)&&(!c||e.sn!==c.sn)){const u=this.shouldLoadParts(t.details,e.end);u!==this.loadingParts&&(this.log(`LL-Part loading ${u?"ON":"OFF"} loading sn ${c==null?void 0:c.sn}->${e.sn}`),this.loadingParts=u)}if(s=Math.max(e.start,s||0),this.loadingParts&&de(e)){const u=n.partList;if(u&&i){s>n.fragmentEnd&&n.fragmentHint&&(e=n.fragmentHint);const f=this.getNextPart(u,e,s);if(f>-1){const g=u[f];e=this.fragCurrent=g.fragment,this.log(`Loading ${e.type} sn: ${e.sn} part: ${g.index} (${f}/${u.length-1}) of ${this.fragInfo(e,!1,g)}) cc: ${e.cc} [${n.startSN}-${n.endSN}], target: ${parseFloat(s.toFixed(3))}`),this.nextLoadPosition=g.start+g.duration,this.state=C.FRAG_LOADING;let y;return o?y=o.then(p=>!p||this.fragContextChanged(p.frag)?null:this.doFragPartsLoad(e,g,t,i)).catch(p=>this.handleFragLoadError(p)):y=this.doFragPartsLoad(e,g,t,i).catch(p=>this.handleFragLoadError(p)),this.hls.trigger(m.FRAG_LOADING,{frag:e,part:g,targetBufferTime:s}),this.fragCurrent===null?Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING parts")):y}else if(!e.url||this.loadedEndOfParts(u,s))return Promise.resolve(null)}}if(de(e)&&this.loadingParts){var l;this.log(`LL-Part loading OFF after next part miss @${s.toFixed(2)} Check buffer at sn: ${e.sn} loaded parts: ${(l=n.partList)==null?void 0:l.filter(u=>u.loaded).map(u=>`[${u.start}-${u.end}]`)}`),this.loadingParts=!1}else if(!e.url)return Promise.resolve(null);this.log(`Loading ${e.type} sn: ${e.sn} of ${this.fragInfo(e,!1)}) cc: ${e.cc} ${"["+n.startSN+"-"+n.endSN+"]"}, target: ${parseFloat(s.toFixed(3))}`),B(e.sn)&&!this.bitrateTest&&(this.nextLoadPosition=e.start+e.duration),this.state=C.FRAG_LOADING;const h=this.config.progressive&&e.type!==U.SUBTITLE;let d;return h&&o?d=o.then(u=>!u||this.fragContextChanged(u.frag)?null:this.fragmentLoader.load(e,i)).catch(u=>this.handleFragLoadError(u)):d=Promise.all([this.fragmentLoader.load(e,h?i:void 0),o]).then(([u])=>(!h&&i&&i(u),u)).catch(u=>this.handleFragLoadError(u)),this.hls.trigger(m.FRAG_LOADING,{frag:e,targetBufferTime:s}),this.fragCurrent===null?Promise.reject(new Error("frag load aborted, context changed in FRAG_LOADING")):d}doFragPartsLoad(e,t,s,i){return new Promise((r,n)=>{var o;const c=[],l=(o=s.details)==null?void 0:o.partList,h=d=>{this.fragmentLoader.loadPart(e,d,i).then(u=>{c[d.index]=u;const f=u.part;this.hls.trigger(m.FRAG_LOADED,u);const g=Br(s.details,e.sn,d.index+1)||ta(l,e.sn,d.index+1);if(g)h(g);else return r({frag:e,part:f,partsLoaded:c})}).catch(n)};h(t)})}handleFragLoadError(e){if("data"in e){const t=e.data;t.frag&&t.details===R.INTERNAL_ABORTED?this.handleFragLoadAborted(t.frag,t.part):t.frag&&t.type===Y.KEY_SYSTEM_ERROR?(t.frag.abortRequests(),this.resetStartWhenNotLoaded(),this.resetFragmentLoading(t.frag)):this.hls.trigger(m.ERROR,t)}else this.hls.trigger(m.ERROR,{type:Y.OTHER_ERROR,details:R.INTERNAL_EXCEPTION,err:e,error:e,fatal:!0});return null}_handleTransmuxerFlush(e){const t=this.getCurrentContext(e);if(!t||this.state!==C.PARSING){!this.fragCurrent&&this.state!==C.STOPPED&&this.state!==C.ERROR&&(this.state=C.IDLE);return}const{frag:s,part:i,level:r}=t,n=self.performance.now();s.stats.parsing.end=n,i&&(i.stats.parsing.end=n);const o=this.getLevelDetails(),l=o&&s.sn>o.endSN||this.shouldLoadParts(o,s.end);l!==this.loadingParts&&(this.log(`LL-Part loading ${l?"ON":"OFF"} after parsing segment ending @${s.end.toFixed(2)}`),this.loadingParts=l),this.updateLevelTiming(s,i,r,e.partial)}shouldLoadParts(e,t){if(this.config.lowLatencyMode){if(!e)return this.loadingParts;if(e.partList){var s;const r=e.partList[0];if(r.fragment.type===U.SUBTITLE)return!1;const n=r.end+(((s=e.fragmentHint)==null?void 0:s.duration)||0);if(t>=n){var i;if((this.hls.hasEnoughToStart?((i=this.media)==null?void 0:i.currentTime)||this.lastCurrentTime:this.getLoadPosition())>r.start-r.fragment.duration)return!0}}}return!1}getCurrentContext(e){const{levels:t,fragCurrent:s}=this,{level:i,sn:r,part:n}=e;if(!(t!=null&&t[i]))return this.warn(`Levels object was unset while buffering fragment ${r} of ${this.playlistLabel()} ${i}. The current chunk will not be buffered.`),null;const o=t[i],c=o.details,l=n>-1?Br(c,r,n):null,h=l?l.fragment:ea(c,r,s);return h?(s&&s!==h&&(h.stats=s.stats),{frag:h,part:l,level:o}):null}bufferFragmentData(e,t,s,i,r){if(this.state!==C.PARSING)return;const{data1:n,data2:o}=e;let c=n;if(o&&(c=we(n,o)),!c.length)return;const l=this.initPTS[t.cc],h=l?-l.baseTime/l.timescale:void 0,d={type:e.type,frag:t,part:s,chunkMeta:i,offset:h,parent:t.type,data:c};if(this.hls.trigger(m.BUFFER_APPENDING,d),e.dropped&&e.independent&&!s){if(r)return;this.flushBufferGap(t)}}flushBufferGap(e){const t=this.media;if(!t)return;if(!X.isBuffered(t,t.currentTime)){this.flushMainBuffer(0,e.start);return}const s=t.currentTime,i=X.bufferInfo(t,s,0),r=e.duration,n=Math.min(this.config.maxFragLookUpTolerance*2,r*.25),o=Math.max(Math.min(e.start-n,i.end-n),s+n);e.start-o>n&&this.flushMainBuffer(o,e.start)}getFwdBufferInfo(e,t){var s;const i=this.getLoadPosition();if(!B(i))return null;const n=this.lastCurrentTime>i||(s=this.media)!=null&&s.paused?0:this.config.maxBufferHole;return this.getFwdBufferInfoAtPos(e,i,t,n)}getFwdBufferInfoAtPos(e,t,s,i){const r=X.bufferInfo(e,t,i);if(r.len===0&&r.nextStart!==void 0){const n=this.fragmentTracker.getBufferedFrag(t,s);if(n&&(r.nextStart<=n.end||n.gap)){const o=Math.max(Math.min(r.nextStart,n.end)-t,i);return X.bufferInfo(e,t,o)}}return r}getMaxBufferLength(e){const{config:t}=this;let s;return e?s=Math.max(8*t.maxBufferSize/e,t.maxBufferLength):s=t.maxBufferLength,Math.min(s,t.maxMaxBufferLength)}exceedsMaxBuffer(e,t,s){const i=e.nextStart;if(i&&s.start>i){const r=e.buffered;if(r){let n=e.len;const o=e.bufferedIndex;for(let c=r.length-1;c>o;c--)r[c].start=t}}return!1}reduceMaxBufferLength(e,t){const s=this.config,i=Math.max(Math.min(e-t,s.maxBufferLength),t),r=Math.max(e-t*3,s.maxMaxBufferLength/2,i);return r>=i?(s.maxMaxBufferLength=r,this.warn(`Reduce max buffer length to ${r}s`),!0):!1}getAppendedFrag(e,t=U.MAIN){const s=this.fragmentTracker?this.fragmentTracker.getAppendedFrag(e,t):null;return s&&"fragment"in s?s.fragment:s}getNextFragment(e,t){const s=t.fragments,i=s.length;if(!i)return null;const{config:r}=this,n=t.fragmentStart,o=r.lowLatencyMode&&!!t.partList;let c=null;if(t.live){const d=r.initialLiveManifestSize;if(i=n?(p=f,E=f===u?"config":"next load start"):g!==null?(p=g,E="live edge"):(p=e,E="buffer pos"),p=r){const c=n.sn;return this.loopSn!==c&&(this.log(`buffer full after gaps in "${i}" playlist starting at sn: ${c}`),this.loopSn=c),null}}return this.loopSn=void 0,n}get primaryPrefetch(){if(Gr(this.config)){var e;if((e=this.hls.interstitialsManager)==null||(e=e.playingItem)==null?void 0:e.event)return!0}return!1}filterReplacedPrimary(e,t){if(!e)return e;if(Gr(this.config)&&e.type!==U.SUBTITLE){const s=this.hls.interstitialsManager,i=s==null?void 0:s.bufferingItem;if(i){const n=i.event;if(n){if(n.appendInPlace||Math.abs(e.start-i.start)>1||i.start===0)return null}else if(e.end<=i.start&&(t==null?void 0:t.live)===!1||e.start>i.end&&i.nextEvent&&(i.nextEvent.appendInPlace||e.start-i.end>1))return null}const r=s==null?void 0:s.playerQueue;if(r)for(let n=r.length;n--;){const o=r[n].interstitial;if(o.appendInPlace&&e.start>=o.startTime&&e.end<=o.resumeTime)return null}}return e}mapToInitFragWhenRequired(e){return e!=null&&e.initSegment&&!e.initSegment.data&&!this.bitrateTest?e.initSegment:e}getNextPart(e,t,s){let i=-1,r=!1,n=!0;for(let o=0,c=e.length;o-1&&ss.start)return!0}return!1}getInitialLiveFragment(e){const t=e.fragments,s=this.fragPrevious;let i=null;if(s){if(e.hasProgramDateTime&&(i=il(t,s.endProgramDateTime,this.config.maxFragLookUpTolerance),i&&this.log(`Live playlist, switching playlist, load frag with same PDT: ${s.programDateTime}`)),!i){const r=s.sn+1;if(r>=e.startSN&&r<=e.endSN){const n=t[r-e.startSN];s.cc===n.cc&&(i=n,this.log(`Live playlist, switching playlist, load frag with next SN: ${i.sn}`))}i||(i=Un(e,s.cc,s.end),i&&this.log(`Live playlist, switching playlist, load frag with same CC: ${i.sn}`))}}else{const r=this.hls.liveSyncPosition;r!==null&&(i=this.getFragmentAtPosition(r,this.bitrateTest?e.fragmentEnd:e.edge,e))}return i}getFragmentAtPosition(e,t,s){const{config:i}=this;let{fragPrevious:r}=this,{fragments:n,endSN:o}=s;const{fragmentHint:c}=s,{maxFragLookUpTolerance:l}=i,h=s.partList,d=!!(this.loadingParts&&h!=null&&h.length&&c);d&&!this.bitrateTest&&h[h.length-1].fragment.sn===c.sn&&(n=n.concat(c),o=c.sn);let u;if(et-l||(f=this.media)!=null&&f.paused||!this.startFragRequested?0:l;u=pt(r,n,e,y)}else u=n[n.length-1];if(u){const g=u.sn-s.startSN,y=this.fragmentTracker.getState(u);if((y===fe.OK||y===fe.PARTIAL&&u.gap)&&(r=u),r&&u.sn===r.sn&&(!d||h[0].fragment.sn>u.sn||!s.live)&&u.level===r.level){const E=n[g+1];u.sn${e.startSN} fragments: ${i}`),c}return r}waitForCdnTuneIn(e){return e.live&&e.canBlockReload&&e.partTarget&&e.tuneInGoal>Math.max(e.partHoldBack,e.partTarget*3)}setStartPosition(e,t){let s=this.startPosition;s=0&&(s=this.nextLoadPosition),s}handleFragLoadAborted(e,t){this.transmuxer&&e.type===this.playlistType&&de(e)&&e.stats.aborted&&(this.log(`Fragment ${e.sn}${t?" part "+t.index:""} of ${this.playlistLabel()} ${e.level} was aborted`),this.resetFragmentLoading(e))}resetFragmentLoading(e){(!this.fragCurrent||!this.fragContextChanged(e)&&this.state!==C.FRAG_LOADING_WAITING_RETRY)&&(this.state=C.IDLE)}onFragmentOrKeyLoadError(e,t){var s;if(t.chunkMeta&&!t.frag){const E=this.getCurrentContext(t.chunkMeta);E&&(t.frag=E.frag)}const i=t.frag;if(!i||i.type!==e||!this.levels)return;if(this.fragContextChanged(i)){var r;this.warn(`Frag load error must match current frag to retry ${i.url} > ${(r=this.fragCurrent)==null?void 0:r.url}`);return}const n=t.details===R.FRAG_GAP;n&&this.fragmentTracker.fragBuffered(i,!0);const o=t.errorAction;if(!o){this.state=C.ERROR;return}const{action:c,flags:l,retryCount:h=0,retryConfig:d}=o,u=!!d,f=u&&c===pe.RetryRequest,g=u&&!o.resolved&&l===Re.MoveAllAlternatesMatchingHost,y=(s=this.hls.latestLevelDetails)==null?void 0:s.live;if(!f&&g&&de(i)&&!i.endList&&y&&!Kn(t))this.resetFragmentErrors(e),this.treatAsGap(i),o.resolved=!0;else if((f||g)&&h=t||s&&!li(0))&&(s&&this.log("Connection restored (online)"),this.resetStartWhenNotLoaded(),this.state=C.IDLE)}reduceLengthAndFlushBuffer(e){if(this.state===C.PARSING||this.state===C.PARSED){const t=e.frag,s=e.parent,i=this.getFwdBufferInfo(this.mediaBuffer,s),r=i&&i.len>.5;r&&this.reduceMaxBufferLength(i.len,(t==null?void 0:t.duration)||10);const n=!r;return n&&this.warn(`Buffer full error while media.currentTime (${this.getLoadPosition()}) is not buffered, flush ${s} buffer`),t&&(this.fragmentTracker.removeFragment(t),this.nextLoadPosition=t.start),this.resetLoadingState(),n}return!1}resetFragmentErrors(e){e===U.AUDIO&&(this.fragCurrent=null),this.hls.hasEnoughToStart||(this.startFragRequested=!1),this.state!==C.STOPPED&&(this.state=C.IDLE)}afterBufferFlushed(e,t,s){if(!e)return;const i=X.getBuffered(e);this.fragmentTracker.detectEvictedFragments(t,i,s),this.state===C.ENDED&&this.resetLoadingState()}resetLoadingState(){this.log("Reset loading state"),this.fragCurrent=null,this.fragPrevious=null,this.state!==C.STOPPED&&(this.state=C.IDLE)}resetStartWhenNotLoaded(){if(!this.hls.hasEnoughToStart){this.startFragRequested=!1;const e=this.levelLastLoaded,t=e?e.details:null;t!=null&&t.live?(this.log("resetting startPosition for live start"),this.startPosition=-1,this.setStartPosition(t,t.fragmentStart),this.resetLoadingState()):this.nextLoadPosition=this.startPosition}}resetWhenMissingContext(e){this.log(`Loading context changed while buffering sn ${e.sn} of ${this.playlistLabel()} ${e.level===-1?"":e.level}. This chunk will not be buffered.`),this.removeUnbufferedFrags(),this.resetStartWhenNotLoaded(),this.resetLoadingState()}removeUnbufferedFrags(e=0){this.fragmentTracker.removeFragmentsInRange(e,1/0,this.playlistType,!1,!0)}updateLevelTiming(e,t,s,i){const r=s.details;if(!r){this.warn("level.details undefined");return}if(!Object.keys(e.elementaryStreams).reduce((c,l)=>{const h=e.elementaryStreams[l];if(h){const d=h.endPTS-h.startPTS;if(d<=0)return this.warn(`Could not parse fragment ${e.sn} ${l} duration reliably (${d})`),c||!1;const u=i?0:Qn(r,e,h.startPTS,h.endPTS,h.startDTS,h.endDTS,this);return this.hls.trigger(m.LEVEL_PTS_UPDATED,{details:r,level:s,drift:u,type:l,frag:e,start:h.startPTS,end:h.endPTS}),!0}return c},!1)){var o;const c=((o=this.transmuxer)==null?void 0:o.error)===null;if((s.fragmentError===0||c&&(s.fragmentError<2||e.endList))&&this.treatAsGap(e,s),c){const l=new Error(`Found no media in fragment ${e.sn} of ${this.playlistLabel()} ${e.level} resetting transmuxer to fallback to playlist timing`);if(this.warn(l.message),this.hls.trigger(m.ERROR,{type:Y.MEDIA_ERROR,details:R.FRAG_PARSING_ERROR,fatal:!1,error:l,frag:e,reason:`Found no media in msn ${e.sn} of ${this.playlistLabel()} "${s.url}"`}),!this.hls)return;this.resetTransmuxer()}}this.state=C.PARSED,this.log(`Parsed ${e.type} sn: ${e.sn}${t?" part: "+t.index:""} of ${this.fragInfo(e,!1,t)})`),this.hls.trigger(m.FRAG_PARSED,{frag:e,part:t})}playlistLabel(){return this.playlistType===U.MAIN?"level":"track"}fragInfo(e,t=!0,s){var i,r;return`${this.playlistLabel()} ${e.level} (${s?"part":"frag"}:[${((i=t&&!s?e.startPTS:(s||e).start)!=null?i:NaN).toFixed(3)}-${((r=t&&!s?e.endPTS:(s||e).end)!=null?r:NaN).toFixed(3)}]${s&&e.type==="main"?"INDEPENDENT="+(s.independent?"YES":"NO"):""}`}treatAsGap(e,t){t&&t.fragmentError++,e.gap=!0,this.fragmentTracker.removeFragment(e),this.fragmentTracker.fragBuffered(e,!0)}resetTransmuxer(){var e;(e=this.transmuxer)==null||e.reset()}recoverWorkerError(e){e.event==="demuxerWorker"&&(this.fragmentTracker.removeAllFragments(),this.transmuxer&&(this.transmuxer.destroy(),this.transmuxer=null),this.resetStartWhenNotLoaded(),this.resetLoadingState())}set state(e){const t=this._state;t!==e&&(this._state=e,this.log(`${t}->${e}`))}get state(){return this._state}}function Gr(a){return!!a.interstitialsController&&a.enableInterstitialPlayback!==!1}class na{constructor(){this.chunks=[],this.dataLength=0}push(e){this.chunks.push(e),this.dataLength+=e.length}flush(){const{chunks:e,dataLength:t}=this;let s;if(e.length)e.length===1?s=e[0]:s=Kl(e,t);else return new Uint8Array(0);return this.reset(),s}reset(){this.chunks.length=0,this.dataLength=0}}function Kl(a,e){const t=new Uint8Array(e);let s=0;for(let i=0;i0)return a.subarray(t,t+s)}function Xl(a,e,t,s){const i=[96e3,88200,64e3,48e3,44100,32e3,24e3,22050,16e3,12e3,11025,8e3,7350],r=e[t+2],n=r>>2&15;if(n>12){const f=new Error(`invalid ADTS sampling index:${n}`);a.emit(m.ERROR,m.ERROR,{type:Y.MEDIA_ERROR,details:R.FRAG_PARSING_ERROR,fatal:!0,error:f,reason:f.message});return}const o=(r>>6&3)+1,c=e[t+3]>>6&3|(r&1)<<2,l="mp4a.40."+o,h=i[n];let d=n;(o===5||o===29)&&(d-=3);const u=[o<<3|(d&14)>>1,(d&1)<<7|c<<3];return re.log(`manifest codec:${s}, parsed codec:${l}, channels:${c}, rate:${h} (ADTS object type:${o} sampling index:${n})`),{config:u,samplerate:h,channelCount:c,codec:l,parsedCodec:l,manifestCodec:s}}function oa(a,e){return a[e]===255&&(a[e+1]&246)===240}function la(a,e){return a[e+1]&1?7:9}function Bi(a,e){return(a[e+3]&3)<<11|a[e+4]<<3|(a[e+5]&224)>>>5}function zl(a,e){return e+5=a.length)return!1;const s=Bi(a,e);if(s<=t)return!1;const i=e+s;return i===a.length||bs(a,i)}return!1}function ca(a,e,t,s,i){if(!a.samplerate){const r=Xl(e,t,s,i);if(!r)return;ne(a,r)}}function ha(a){return 1024*9e4/a}function Jl(a,e){const t=la(a,e);if(e+t<=a.length){const s=Bi(a,e)-t;if(s>0)return{headerLength:t,frameLength:s}}}function da(a,e,t,s,i){const r=ha(a.samplerate),n=s+i*r,o=Jl(e,t);let c;if(o){const{frameLength:d,headerLength:u}=o,f=u+d,g=Math.max(0,t+f-e.length);g?(c=new Uint8Array(f-u),c.set(e.subarray(t+u,e.length),0)):c=e.subarray(t+u,t+f);const y={unit:c,pts:n};return g||a.samples.push(y),{sample:y,length:f,missing:g}}const l=e.length-t;return c=new Uint8Array(l),c.set(e.subarray(t,e.length),0),{sample:{unit:c,pts:n},length:l,missing:-1}}function ec(a,e){return Ni(a,e)&&Cs(a,e+6)+10<=a.length-e}function tc(a){return a instanceof ArrayBuffer?a:a.byteOffset==0&&a.byteLength==a.buffer.byteLength?a.buffer:new Uint8Array(a).buffer}function Ks(a,e=0,t=1/0){return sc(a,e,t,Uint8Array)}function sc(a,e,t,s){const i=ic(a);let r=1;"BYTES_PER_ELEMENT"in s&&(r=s.BYTES_PER_ELEMENT);const n=rc(a)?a.byteOffset:0,o=(n+a.byteLength)/r,c=(n+e)/r,l=Math.floor(Math.max(0,Math.min(c,o))),h=Math.floor(Math.min(l+Math.max(t,0),o));return new s(i,l,h-l)}function ic(a){return a instanceof ArrayBuffer?a:a.buffer}function rc(a){return a&&a.buffer instanceof ArrayBuffer&&a.byteLength!==void 0&&a.byteOffset!==void 0}function nc(a){const e={key:a.type,description:"",data:"",mimeType:null,pictureType:null},t=3;if(a.size<2)return;if(a.data[0]!==t){console.log("Ignore frame with unrecognized character encoding");return}const s=a.data.subarray(1).indexOf(0);if(s===-1)return;const i=De(Ks(a.data,1,s)),r=a.data[2+s],n=a.data.subarray(3+s).indexOf(0);if(n===-1)return;const o=De(Ks(a.data,3+s,n));let c;return i==="-->"?c=De(Ks(a.data,4+s+n)):c=tc(a.data.subarray(4+s+n)),e.mimeType=i,e.pictureType=r,e.description=o,e.data=c,e}function ac(a){if(a.size<2)return;const e=De(a.data,!0),t=new Uint8Array(a.data.subarray(e.length+1));return{key:a.type,info:e,data:t.buffer}}function oc(a){if(a.size<2)return;if(a.type==="TXXX"){let t=1;const s=De(a.data.subarray(t),!0);t+=s.length+1;const i=De(a.data.subarray(t));return{key:a.type,info:s,data:i}}const e=De(a.data.subarray(1));return{key:a.type,info:"",data:e}}function lc(a){if(a.type==="WXXX"){if(a.size<2)return;let t=1;const s=De(a.data.subarray(t),!0);t+=s.length+1;const i=De(a.data.subarray(t));return{key:a.type,info:s,data:i}}const e=De(a.data);return{key:a.type,info:"",data:e}}function cc(a){return a.type==="PRIV"?ac(a):a.type[0]==="W"?lc(a):a.type==="APIC"?nc(a):oc(a)}function hc(a){const e=String.fromCharCode(a[0],a[1],a[2],a[3]),t=Cs(a,4),s=10;return{type:e,size:t,data:a.subarray(s,s+t)}}const es=10,dc=10;function ua(a){let e=0;const t=[];for(;Ni(a,e);){const s=Cs(a,e+6);a[e+5]>>6&1&&(e+=es),e+=es;const i=e+s;for(;e+dc0&&o.samples.push({pts:this.lastPTS,dts:this.lastPTS,data:s,type:_e.audioId3,duration:Number.POSITIVE_INFINITY});i{if(B(a))return a*90;const s=t?t.baseTime*9e4/t.timescale:0;return e*9e4+s};let ts=null;const gc=[32,64,96,128,160,192,224,256,288,320,352,384,416,448,32,48,56,64,80,96,112,128,160,192,224,256,320,384,32,40,48,56,64,80,96,112,128,160,192,224,256,320,32,48,56,64,80,96,112,128,144,160,176,192,224,256,8,16,24,32,40,48,56,64,80,96,112,128,144,160],mc=[44100,48e3,32e3,22050,24e3,16e3,11025,12e3,8e3],pc=[[0,72,144,12],[0,0,0,0],[0,72,144,12],[0,144,144,12]],yc=[0,1,1,4];function ga(a,e,t,s,i){if(t+24>e.length)return;const r=ma(e,t);if(r&&t+r.frameLength<=e.length){const n=r.samplesPerFrame*9e4/r.sampleRate,o=s+i*n,c={unit:e.subarray(t,t+r.frameLength),pts:o,dts:o};return a.config=[],a.channelCount=r.channelCount,a.samplerate=r.sampleRate,a.samples.push(c),{sample:c,length:r.frameLength,missing:0}}}function ma(a,e){const t=a[e+1]>>3&3,s=a[e+1]>>1&3,i=a[e+2]>>4&15,r=a[e+2]>>2&3;if(t!==1&&i!==0&&i!==15&&r!==3){const n=a[e+2]>>1&1,o=a[e+3]>>6,c=t===3?3-s:s===3?3:4,l=gc[c*14+i-1]*1e3,d=mc[(t===3?0:t===2?1:2)*3+r],u=o===3?1:2,f=pc[t][s],g=yc[s],y=f*8*g,p=Math.floor(f*l/d+n)*g;if(ts===null){const S=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);ts=S?parseInt(S[1]):0}return!!ts&&ts<=87&&s===2&&l>=224e3&&o===0&&(a[e+3]=a[e+3]|128),{sampleRate:d,channelCount:u,frameLength:p,samplesPerFrame:y}}}function Gi(a,e){return a[e]===255&&(a[e+1]&224)===224&&(a[e+1]&6)!==0}function pa(a,e){return e+1{let t=0,s=5;e+=s;const i=new Uint32Array(1),r=new Uint32Array(1),n=new Uint8Array(1);for(;s>0;){n[0]=a[e];const o=Math.min(s,8),c=8-o;r[0]=4278190080>>>24+c<>c,t=t?t<e.length||e[t]!==11||e[t+1]!==119)return-1;const r=e[t+4]>>6;if(r>=3)return-1;const o=[48e3,44100,32e3][r],c=e[t+4]&63,h=[64,69,96,64,70,96,80,87,120,80,88,120,96,104,144,96,105,144,112,121,168,112,122,168,128,139,192,128,140,192,160,174,240,160,175,240,192,208,288,192,209,288,224,243,336,224,244,336,256,278,384,256,279,384,320,348,480,320,349,480,384,417,576,384,418,576,448,487,672,448,488,672,512,557,768,512,558,768,640,696,960,640,697,960,768,835,1152,768,836,1152,896,975,1344,896,976,1344,1024,1114,1536,1024,1115,1536,1152,1253,1728,1152,1254,1728,1280,1393,1920,1280,1394,1920][c*3+r]*2;if(t+h>e.length)return-1;const d=e[t+6]>>5;let u=0;d===2?u+=2:(d&1&&d!==1&&(u+=2),d&4&&(u+=2));const f=(e[t+6]<<8|e[t+7])>>12-u&1,y=[2,1,2,3,3,4,4,5][d]+f,p=e[t+5]>>3,E=e[t+5]&7,T=new Uint8Array([r<<6|p<<1|E>>2,(E&3)<<6|d<<3|f<<2|c>>4,c<<4&224]),S=1536/o*9e4,v=s+i*S,x=e.subarray(t,t+h);return a.config=T,a.channelCount=y,a.samplerate=o,a.samples.push({unit:x,pts:v}),h}class vc extends Ui{resetInitSegment(e,t,s,i){super.resetInitSegment(e,t,s,i),this._audioTrack={container:"audio/mpeg",type:"audio",id:2,pid:-1,sequenceNumber:0,segmentCodec:"mp3",samples:[],manifestCodec:t,duration:i,inputTimeScale:9e4,dropped:0}}static probe(e){if(!e)return!1;const t=Ht(e,0);let s=(t==null?void 0:t.length)||0;if(t&&e[s]===11&&e[s+1]===119&&$i(t)!==void 0&&Ea(e,s)<=16)return!1;for(let i=e.length;s{const n=Do(r);if(xc.test(n.schemeIdUri)){const o=Vr(n,t);let c=n.eventDuration===4294967295?Number.POSITIVE_INFINITY:n.eventDuration/n.timeScale;c<=.001&&(c=Number.POSITIVE_INFINITY);const l=n.payload;s.samples.push({data:l,len:l.byteLength,dts:o,pts:o,type:_e.emsg,duration:c})}else if(this.config.enableEmsgKLVMetadata&&n.schemeIdUri.startsWith("urn:misb:KLV:bin:1910.1")){const o=Vr(n,t);s.samples.push({data:n.payload,len:n.payload.byteLength,dts:o,pts:o,type:_e.misbklv,duration:Number.POSITIVE_INFINITY})}})}return s}demuxSampleAes(e,t,s){return Promise.reject(new Error("The MP4 demuxer does not support SAMPLE-AES decryption"))}destroy(){this.config=null,this.remainderData=null,this.videoTrack=this.audioTrack=this.id3Track=this.txtTrack=void 0}}function Vr(a,e){return B(a.presentationTime)?a.presentationTime/a.timeScale:e+a.presentationTimeDelta/a.timeScale}class Ic{constructor(e,t,s){this.keyData=void 0,this.decrypter=void 0,this.keyData=s,this.decrypter=new Pi(t,{removePKCS7Padding:!1})}decryptBuffer(e){return this.decrypter.decrypt(e,this.keyData.key.buffer,this.keyData.iv.buffer,ot.cbc)}decryptAacSample(e,t,s){const i=e[t].unit;if(i.length<=16)return;const r=i.subarray(16,i.length-i.length%16),n=r.buffer.slice(r.byteOffset,r.byteOffset+r.length);this.decryptBuffer(n).then(o=>{const c=new Uint8Array(o);i.set(c,16),this.decrypter.isSync()||this.decryptAacSamples(e,t+1,s)}).catch(s)}decryptAacSamples(e,t,s){for(;;t++){if(t>=e.length){s();return}if(!(e[t].unit.length<32)&&(this.decryptAacSample(e,t,s),!this.decrypter.isSync()))return}}getAvcEncryptedData(e){const t=Math.floor((e.length-48)/160)*16+16,s=new Int8Array(t);let i=0;for(let r=32;r{r.data=this.getAvcDecryptedUnit(n,c),this.decrypter.isSync()||this.decryptAvcSamples(e,t,s+1,i)}).catch(i)}decryptAvcSamples(e,t,s,i){if(e instanceof Uint8Array)throw new Error("Cannot decrypt samples of type Uint8Array");for(;;t++,s=0){if(t>=e.length){i();return}const r=e[t].units;for(;!(s>=r.length);s++){const n=r[s];if(!(n.data.length<=48||n.type!==1&&n.type!==5)&&(this.decryptAvcSample(e,t,s,i,n),!this.decrypter.isSync()))return}}}}class Sa{constructor(){this.VideoSample=null}createVideoSample(e,t,s){return{key:e,frame:!1,pts:t,dts:s,units:[],length:0}}getLastNalUnit(e){var t;let s=this.VideoSample,i;if((!s||s.units.length===0)&&(s=e[e.length-1]),(t=s)!=null&&t.units){const r=s.units;i=r[r.length-1]}return i}pushAccessUnit(e,t){if(e.units.length&&e.frame){if(e.pts===void 0){const s=t.samples,i=s.length;if(i){const r=s[i-1];e.pts=r.pts,e.dts=r.dts}else{t.dropped++;return}}t.samples.push(e)}}parseNALu(e,t,s){const i=t.byteLength;let r=e.naluState||0;const n=r,o=[];let c=0,l,h,d,u=-1,f=0;for(r===-1&&(u=0,f=this.getNALuType(t,0),r=0,c=1);c=0){const g={data:t.subarray(u,h),type:f};o.push(g)}else{const g=this.getLastNalUnit(e.samples);g&&(n&&c<=4-n&&g.state&&(g.data=g.data.subarray(0,g.data.byteLength-n)),h>0&&(g.data=we(g.data,t.subarray(0,h)),g.state=0))}c=0&&r>=0){const g={data:t.subarray(u,i),type:f,state:r};o.push(g)}if(o.length===0){const g=this.getLastNalUnit(e.samples);g&&(g.data=we(g.data,t))}return e.naluState=r,o}}class Bt{constructor(e){this.data=void 0,this.bytesAvailable=void 0,this.word=void 0,this.bitsAvailable=void 0,this.data=e,this.bytesAvailable=e.byteLength,this.word=0,this.bitsAvailable=0}loadWord(){const e=this.data,t=this.bytesAvailable,s=e.byteLength-t,i=new Uint8Array(4),r=Math.min(4,t);if(r===0)throw new Error("no bytes available");i.set(e.subarray(s,s+r)),this.word=new DataView(i.buffer).getUint32(0),this.bitsAvailable=r*8,this.bytesAvailable-=r}skipBits(e){let t;e=Math.min(e,this.bytesAvailable*8+this.bitsAvailable),this.bitsAvailable>e?(this.word<<=e,this.bitsAvailable-=e):(e-=this.bitsAvailable,t=e>>3,e-=t<<3,this.bytesAvailable-=t,this.loadWord(),this.word<<=e,this.bitsAvailable-=e)}readBits(e){let t=Math.min(this.bitsAvailable,e);const s=this.word>>>32-t;if(e>32&&re.error("Cannot read more than 32 bits at a time"),this.bitsAvailable-=t,this.bitsAvailable>0)this.word<<=t;else if(this.bytesAvailable>0)this.loadWord();else throw new Error("no bits available");return t=e-t,t>0&&this.bitsAvailable?s<>>e)return this.word<<=e,this.bitsAvailable-=e,e;return this.loadWord(),e+this.skipLZ()}skipUEG(){this.skipBits(1+this.skipLZ())}skipEG(){this.skipBits(1+this.skipLZ())}readUEG(){const e=this.skipLZ();return this.readBits(e+1)-1}readEG(){const e=this.readUEG();return 1&e?1+e>>>1:-1*(e>>>1)}readBoolean(){return this.readBits(1)===1}readUByte(){return this.readBits(8)}readUShort(){return this.readBits(16)}readUInt(){return this.readBits(32)}}class Lc extends Sa{parsePES(e,t,s,i){const r=this.parseNALu(e,s.data,i);let n=this.VideoSample,o,c=!1;s.data=null,n&&r.length&&!e.audFound&&(this.pushAccessUnit(n,e),n=this.VideoSample=this.createVideoSample(!1,s.pts,s.dts)),r.forEach(l=>{var h,d;switch(l.type){case 1:{let y=!1;o=!0;const p=l.data;if(c&&p.length>4){const E=this.readSliceType(p);(E===2||E===4||E===7||E===9)&&(y=!0)}if(y){var u;(u=n)!=null&&u.frame&&!n.key&&(this.pushAccessUnit(n,e),n=this.VideoSample=null)}n||(n=this.VideoSample=this.createVideoSample(!0,s.pts,s.dts)),n.frame=!0,n.key=y;break}case 5:o=!0,(h=n)!=null&&h.frame&&!n.key&&(this.pushAccessUnit(n,e),n=this.VideoSample=null),n||(n=this.VideoSample=this.createVideoSample(!0,s.pts,s.dts)),n.key=!0,n.frame=!0;break;case 6:{o=!0,bi(l.data,1,s.pts,t.samples);break}case 7:{var f,g;o=!0,c=!0;const y=l.data,p=this.readSPS(y);if(!e.sps||e.width!==p.width||e.height!==p.height||((f=e.pixelRatio)==null?void 0:f[0])!==p.pixelRatio[0]||((g=e.pixelRatio)==null?void 0:g[1])!==p.pixelRatio[1]){e.width=p.width,e.height=p.height,e.pixelRatio=p.pixelRatio,e.sps=[y];const E=y.subarray(1,4);let T="avc1.";for(let S=0;S<3;S++){let v=E[S].toString(16);v.length<2&&(v="0"+v),T+=v}e.codec=T}break}case 8:o=!0,e.pps=[l.data];break;case 9:o=!0,e.audFound=!0,(d=n)!=null&&d.frame&&(this.pushAccessUnit(n,e),n=null),n||(n=this.VideoSample=this.createVideoSample(!1,s.pts,s.dts));break;case 12:o=!0;break;default:o=!1;break}n&&o&&n.units.push(l)}),i&&n&&(this.pushAccessUnit(n,e),this.VideoSample=null)}getNALuType(e,t){return e[t]&31}readSliceType(e){const t=new Bt(e);return t.readUByte(),t.readUEG(),t.readUEG()}skipScalingList(e,t){let s=8,i=8,r;for(let n=0;n{var h,d;switch(l.type){case 0:case 1:case 2:case 3:case 4:case 5:case 6:case 7:case 8:case 9:n||(n=this.VideoSample=this.createVideoSample(!1,s.pts,s.dts)),n.frame=!0,o=!0;break;case 16:case 17:case 18:case 21:if(o=!0,c){var u;(u=n)!=null&&u.frame&&!n.key&&(this.pushAccessUnit(n,e),n=this.VideoSample=null)}n||(n=this.VideoSample=this.createVideoSample(!0,s.pts,s.dts)),n.key=!0,n.frame=!0;break;case 19:case 20:o=!0,(h=n)!=null&&h.frame&&!n.key&&(this.pushAccessUnit(n,e),n=this.VideoSample=null),n||(n=this.VideoSample=this.createVideoSample(!0,s.pts,s.dts)),n.key=!0,n.frame=!0;break;case 39:o=!0,bi(l.data,2,s.pts,t.samples);break;case 32:o=!0,e.vps||(typeof e.params!="object"&&(e.params={}),e.params=ne(e.params,this.readVPS(l.data)),this.initVPS=l.data),e.vps=[l.data];break;case 33:if(o=!0,c=!0,e.vps!==void 0&&e.vps[0]!==this.initVPS&&e.sps!==void 0&&!this.matchSPS(e.sps[0],l.data)&&(this.initVPS=e.vps[0],e.sps=e.pps=void 0),!e.sps){const f=this.readSPS(l.data);e.width=f.width,e.height=f.height,e.pixelRatio=f.pixelRatio,e.codec=f.codecString,e.sps=[],typeof e.params!="object"&&(e.params={});for(const g in f.params)e.params[g]=f.params[g]}this.pushParameterSet(e.sps,l.data,e.vps),n||(n=this.VideoSample=this.createVideoSample(!0,s.pts,s.dts)),n.key=!0;break;case 34:if(o=!0,typeof e.params=="object"){if(!e.pps){e.pps=[];const f=this.readPPS(l.data);for(const g in f)e.params[g]=f[g]}this.pushParameterSet(e.pps,l.data,e.vps)}break;case 35:o=!0,e.audFound=!0,(d=n)!=null&&d.frame&&(this.pushAccessUnit(n,e),n=null),n||(n=this.VideoSample=this.createVideoSample(!1,s.pts,s.dts));break;default:o=!1;break}n&&o&&n.units.push(l)}),i&&n&&(this.pushAccessUnit(n,e),this.VideoSample=null)}pushParameterSet(e,t,s){(s&&s[0]===this.initVPS||!s&&!e.length)&&e.push(t)}getNALuType(e,t){return(e[t]&126)>>>1}ebsp2rbsp(e){const t=new Uint8Array(e.byteLength);let s=0;for(let i=0;i=2&&e[i]===3&&e[i-1]===0&&e[i-2]===0||(t[s]=e[i],s++);return new Uint8Array(t.buffer,0,s)}pushAccessUnit(e,t){super.pushAccessUnit(e,t),this.initVPS&&(this.initVPS=null)}readVPS(e){const t=new Bt(e);t.readUByte(),t.readUByte(),t.readBits(4),t.skipBits(2),t.readBits(6);const s=t.readBits(3),i=t.readBoolean();return{numTemporalLayers:s+1,temporalIdNested:i}}readSPS(e){const t=new Bt(this.ebsp2rbsp(e));t.readUByte(),t.readUByte(),t.readBits(4);const s=t.readBits(3);t.readBoolean();const i=t.readBits(2),r=t.readBoolean(),n=t.readBits(5),o=t.readUByte(),c=t.readUByte(),l=t.readUByte(),h=t.readUByte(),d=t.readUByte(),u=t.readUByte(),f=t.readUByte(),g=t.readUByte(),y=t.readUByte(),p=t.readUByte(),E=t.readUByte(),T=[],S=[];for(let te=0;te0)for(let te=s;te<8;te++)t.readBits(2);for(let te=0;te1&&t.readEG();for(let yt=0;yt0&&ht<16?(F=zi[ht-1],z=Xt[ht-1]):ht===255&&(F=t.readBits(16),z=t.readBits(16))}if(t.readBoolean()&&t.readBoolean(),t.readBoolean()&&(t.readBits(3),t.readBoolean(),t.readBoolean()&&(t.readUByte(),t.readUByte(),t.readUByte())),t.readBoolean()&&(t.readUEG(),t.readUEG()),t.readBoolean(),t.readBoolean(),t.readBoolean(),Se=t.readBoolean(),Se&&(t.skipUEG(),t.skipUEG(),t.skipUEG(),t.skipUEG()),t.readBoolean()&&(Q=t.readBits(32),ee=t.readBits(32),t.readBoolean()&&t.readUEG(),t.readBoolean())){const Xt=t.readBoolean(),Qi=t.readBoolean();let wt=!1;(Xt||Qi)&&(wt=t.readBoolean(),wt&&(t.readUByte(),t.readBits(5),t.readBoolean(),t.readBits(5)),t.readBits(4),t.readBits(4),wt&&t.readBits(4),t.readBits(5),t.readBits(5),t.readBits(5));for(let Zi=0;Zi<=s;Zi++){se=t.readBoolean();const so=se||t.readBoolean();let Ji=!1;so?t.readEG():Ji=t.readBoolean();const er=Ji?1:t.readUEG()+1;if(Xt)for(let Ot=0;Ot>te&1)<<31-te)>>>0;let ws=ks.toString(16);return n===1&&ws==="2"&&(ws="6"),{codecString:`hvc1.${lt}${n}.${ws}.${r?"H":"L"}${E}.B0`,params:{general_tier_flag:r,general_profile_idc:n,general_profile_space:i,general_profile_compatibility_flags:[o,c,l,h],general_constraint_indicator_flags:[d,u,f,g,y,p],general_level_idc:E,bit_depth:M+8,bit_depth_luma_minus8:M,bit_depth_chroma_minus8:G,min_spatial_segmentation_idc:O,chroma_format_idc:v,frame_rate:{fixed:se,fps:ee/Q}},width:Pe,height:$e,pixelRatio:[F,z]}}readPPS(e){const t=new Bt(this.ebsp2rbsp(e));t.readUByte(),t.readUByte(),t.skipUEG(),t.skipUEG(),t.skipBits(2),t.skipBits(3),t.skipBits(2),t.skipUEG(),t.skipUEG(),t.skipEG(),t.skipBits(2),t.readBoolean()&&t.skipUEG(),t.skipEG(),t.skipEG(),t.skipBits(4);const i=t.readBoolean(),r=t.readBoolean();let n=1;return r&&i?n=0:r?n=3:i&&(n=2),{parallelismType:n}}matchSPS(e,t){return String.fromCharCode.apply(null,e).substr(3)===String.fromCharCode.apply(null,t).substr(3)}}const ge=188;class rt{constructor(e,t,s,i){this.logger=void 0,this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.sampleAes=null,this.pmtParsed=!1,this.audioCodec=void 0,this.videoCodec=void 0,this._pmtId=-1,this._videoTrack=void 0,this._audioTrack=void 0,this._id3Track=void 0,this._txtTrack=void 0,this.aacOverFlow=null,this.remainderData=null,this.videoParser=void 0,this.observer=e,this.config=t,this.typeSupported=s,this.logger=i,this.videoParser=null}static probe(e,t){const s=rt.syncOffset(e);return s>0&&t.warn(`MPEG2-TS detected but first sync word found @ offset ${s}`),s!==-1}static syncOffset(e){const t=e.length;let s=Math.min(ge*5,t-ge)+1,i=0;for(;i1&&(n===0&&o>2||c+ge>s))return n}else{if(o)return-1;break}i++}return-1}static createTrack(e,t){return{container:e==="video"||e==="audio"?"video/mp2t":void 0,type:e,id:Rn[e],pid:-1,inputTimeScale:9e4,sequenceNumber:0,samples:[],dropped:0,duration:e==="audio"?t:void 0}}resetInitSegment(e,t,s,i){this.pmtParsed=!1,this._pmtId=-1,this._videoTrack=rt.createTrack("video"),this._videoTrack.duration=i,this._audioTrack=rt.createTrack("audio",i),this._id3Track=rt.createTrack("id3"),this._txtTrack=rt.createTrack("text"),this._audioTrack.segmentCodec="aac",this.videoParser=null,this.aacOverFlow=null,this.remainderData=null,this.audioCodec=t,this.videoCodec=s}resetTimeStamp(){}resetContiguity(){const{_audioTrack:e,_videoTrack:t,_id3Track:s}=this;e&&(e.pesData=null),t&&(t.pesData=null),s&&(s.pesData=null),this.aacOverFlow=null,this.remainderData=null}demux(e,t,s=!1,i=!1){s||(this.sampleAes=null);let r;const n=this._videoTrack,o=this._audioTrack,c=this._id3Track,l=this._txtTrack;let h=n.pid,d=n.pesData,u=o.pid,f=c.pid,g=o.pesData,y=c.pesData,p=null,E=this.pmtParsed,T=this._pmtId,S=e.length;if(this.remainderData&&(e=we(this.remainderData,e),S=e.length,this.remainderData=null),S>4;let P;if(I>1){if(P=A+5+e[A+4],P===A+ge)continue}else P=A+4;switch(b){case h:_&&(d&&(r=vt(d,this.logger))&&(this.readyVideoParser(n.segmentCodec),this.videoParser!==null&&this.videoParser.parsePES(n,l,r,!1)),d={data:[],size:0}),d&&(d.data.push(e.subarray(P,A+ge)),d.size+=A+ge-P);break;case u:if(_){if(g&&(r=vt(g,this.logger)))switch(o.segmentCodec){case"aac":this.parseAACPES(o,r);break;case"mp3":this.parseMPEGPES(o,r);break;case"ac3":this.parseAC3PES(o,r);break}g={data:[],size:0}}g&&(g.data.push(e.subarray(P,A+ge)),g.size+=A+ge-P);break;case f:_&&(y&&(r=vt(y,this.logger))&&this.parseID3PES(c,r),y={data:[],size:0}),y&&(y.data.push(e.subarray(P,A+ge)),y.size+=A+ge-P);break;case 0:_&&(P+=e[P]+1),T=this._pmtId=bc(e,P);break;case T:{_&&(P+=e[P]+1);const M=_c(e,P,this.typeSupported,s,this.observer,this.logger);h=M.videoPid,h>0&&(n.pid=h,n.segmentCodec=M.segmentVideoCodec),u=M.audioPid,u>0&&(o.pid=u,o.segmentCodec=M.segmentAudioCodec),f=M.id3Pid,f>0&&(c.pid=f),p!==null&&!E&&(this.logger.warn(`MPEG-TS PMT found at ${A} after unknown PID '${p}'. Backtracking to sync byte @${v} to parse all TS packets.`),p=null,A=v-188),E=this.pmtParsed=!0;break}case 17:case 8191:break;default:p=b;break}}else x++;x>0&&gi(this.observer,new Error(`Found ${x} TS packet/s that do not start with 0x47`),void 0,this.logger),n.pesData=d,o.pesData=g,c.pesData=y;const D={audioTrack:o,videoTrack:n,id3Track:c,textTrack:l};return i&&this.extractRemainingSamples(D),D}flush(){const{remainderData:e}=this;this.remainderData=null;let t;return e?t=this.demux(e,-1,!1,!0):t={videoTrack:this._videoTrack,audioTrack:this._audioTrack,id3Track:this._id3Track,textTrack:this._txtTrack},this.extractRemainingSamples(t),this.sampleAes?this.decrypt(t,this.sampleAes):t}extractRemainingSamples(e){const{audioTrack:t,videoTrack:s,id3Track:i,textTrack:r}=e,n=s.pesData,o=t.pesData,c=i.pesData;let l;if(n&&(l=vt(n,this.logger))?(this.readyVideoParser(s.segmentCodec),this.videoParser!==null&&(this.videoParser.parsePES(s,r,l,!0),s.pesData=null)):s.pesData=n,o&&(l=vt(o,this.logger))){switch(t.segmentCodec){case"aac":this.parseAACPES(t,l);break;case"mp3":this.parseMPEGPES(t,l);break;case"ac3":this.parseAC3PES(t,l);break}t.pesData=null}else o!=null&&o.size&&this.logger.log("last AAC PES packet truncated,might overlap between fragments"),t.pesData=o;c&&(l=vt(c,this.logger))?(this.parseID3PES(i,l),i.pesData=null):i.pesData=c}demuxSampleAes(e,t,s){const i=this.demux(e,s,!0,!this.config.progressive),r=this.sampleAes=new Ic(this.observer,this.config,t);return this.decrypt(i,r)}readyVideoParser(e){this.videoParser===null&&(e==="avc"?this.videoParser=new Lc:e==="hevc"&&(this.videoParser=new Rc))}decrypt(e,t){return new Promise(s=>{const{audioTrack:i,videoTrack:r}=e;i.samples&&i.segmentCodec==="aac"?t.decryptAacSamples(i.samples,0,()=>{r.samples?t.decryptAvcSamples(r.samples,0,0,()=>{s(e)}):s(e)}):r.samples&&t.decryptAvcSamples(r.samples,0,0,()=>{s(e)})})}destroy(){this.observer&&this.observer.removeAllListeners(),this.config=this.logger=this.observer=null,this.aacOverFlow=this.videoParser=this.remainderData=this.sampleAes=null,this._videoTrack=this._audioTrack=this._id3Track=this._txtTrack=void 0}parseAACPES(e,t){let s=0;const i=this.aacOverFlow;let r=t.data;if(i){this.aacOverFlow=null;const d=i.missing,u=i.sample.unit.byteLength;if(d===-1)r=we(i.sample.unit,r);else{const f=u-d;i.sample.unit.set(r.subarray(0,d),f),e.samples.push(i.sample),s=i.missing}}let n,o;for(n=s,o=r.length;n0;)o+=c}}parseID3PES(e,t){if(t.pts===void 0){this.logger.warn("[tsdemuxer]: ID3 PES unknown PTS");return}const s=ne({},t,{type:this._videoTrack?_e.emsg:_e.audioId3,duration:Number.POSITIVE_INFINITY});e.samples.push(s)}}function fi(a,e){return((a[e+1]&31)<<8)+a[e+2]}function bc(a,e){return(a[e+10]&31)<<8|a[e+11]}function _c(a,e,t,s,i,r){const n={audioPid:-1,videoPid:-1,id3Pid:-1,segmentVideoCodec:"avc",segmentAudioCodec:"aac"},o=(a[e+1]&15)<<8|a[e+2],c=e+3+o-4,l=(a[e+10]&15)<<8|a[e+11];for(e+=12+l;e0){let u=e+5,f=d;for(;f>2;){switch(a[u]){case 106:t.ac3!==!0?r.log("AC-3 audio found, not supported in this browser for now"):(n.audioPid=h,n.segmentAudioCodec="ac3");break}const y=a[u+1]+2;u+=y,f-=y}}break;case 194:case 135:return gi(i,new Error("Unsupported EC-3 in M2TS found"),void 0,r),n;case 36:n.videoPid===-1&&(n.videoPid=h,n.segmentVideoCodec="hevc",r.log("HEVC in M2TS found"));break}e+=d+5}return n}function gi(a,e,t,s){s.warn(`parsing error: ${e.message}`),a.emit(m.ERROR,m.ERROR,{type:Y.MEDIA_ERROR,details:R.FRAG_PARSING_ERROR,fatal:!1,levelRetry:t,error:e,reason:e.message})}function Vs(a,e){e.log(`${a} with AES-128-CBC encryption found in unencrypted stream`)}function vt(a,e){let t=0,s,i,r,n,o;const c=a.data;if(!a||a.size===0)return null;for(;c[0].length<19&&c.length>1;)c[0]=we(c[0],c[1]),c.splice(1,1);if(s=c[0],(s[0]<<16)+(s[1]<<8)+s[2]===1){if(i=(s[4]<<8)+s[5],i&&i>a.size-6)return null;const h=s[7];h&192&&(n=(s[9]&14)*536870912+(s[10]&255)*4194304+(s[11]&254)*16384+(s[12]&255)*128+(s[13]&254)/2,h&64?(o=(s[14]&14)*536870912+(s[15]&255)*4194304+(s[16]&254)*16384+(s[17]&255)*128+(s[18]&254)/2,n-o>60*9e4&&(e.warn(`${Math.round((n-o)/9e4)}s delta between PTS and DTS, align them`),n=o)):o=n),r=s[8];let d=r+9;if(a.size<=d)return null;a.size-=d;const u=new Uint8Array(a.size);for(let f=0,g=c.length;fy){d-=y;continue}else s=s.subarray(d),y-=d,d=0;u.set(s,t),t+=y}return i&&(i-=r+3),{data:u,pts:n,dts:o,len:i}}return null}class Dc{static getSilentFrame(e,t){switch(e){case"mp4a.40.2":if(t===1)return new Uint8Array([0,200,0,128,35,128]);if(t===2)return new Uint8Array([33,0,73,144,2,25,0,35,128]);if(t===3)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,142]);if(t===4)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,128,44,128,8,2,56]);if(t===5)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,130,48,4,153,0,33,144,2,56]);if(t===6)return new Uint8Array([0,200,0,128,32,132,1,38,64,8,100,0,130,48,4,153,0,33,144,2,0,178,0,32,8,224]);break;default:if(t===1)return new Uint8Array([1,64,34,128,163,78,230,128,186,8,0,0,0,28,6,241,193,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);if(t===2)return new Uint8Array([1,64,34,128,163,94,230,128,186,8,0,0,0,0,149,0,6,241,161,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);if(t===3)return new Uint8Array([1,64,34,128,163,94,230,128,186,8,0,0,0,0,149,0,6,241,161,10,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,90,94]);break}}}const tt=Math.pow(2,32)-1;class L{static init(){L.types={avc1:[],avcC:[],hvc1:[],hvcC:[],btrt:[],dinf:[],dref:[],esds:[],ftyp:[],hdlr:[],mdat:[],mdhd:[],mdia:[],mfhd:[],minf:[],moof:[],moov:[],mp4a:[],".mp3":[],dac3:[],"ac-3":[],mvex:[],mvhd:[],pasp:[],sdtp:[],stbl:[],stco:[],stsc:[],stsd:[],stsz:[],stts:[],tfdt:[],tfhd:[],traf:[],trak:[],trun:[],trex:[],tkhd:[],vmhd:[],smhd:[]};let e;for(e in L.types)L.types.hasOwnProperty(e)&&(L.types[e]=[e.charCodeAt(0),e.charCodeAt(1),e.charCodeAt(2),e.charCodeAt(3)]);const t=new Uint8Array([0,0,0,0,0,0,0,0,118,105,100,101,0,0,0,0,0,0,0,0,0,0,0,0,86,105,100,101,111,72,97,110,100,108,101,114,0]),s=new Uint8Array([0,0,0,0,0,0,0,0,115,111,117,110,0,0,0,0,0,0,0,0,0,0,0,0,83,111,117,110,100,72,97,110,100,108,101,114,0]);L.HDLR_TYPES={video:t,audio:s};const i=new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,12,117,114,108,32,0,0,0,1]),r=new Uint8Array([0,0,0,0,0,0,0,0]);L.STTS=L.STSC=L.STCO=r,L.STSZ=new Uint8Array([0,0,0,0,0,0,0,0,0,0,0,0]),L.VMHD=new Uint8Array([0,0,0,1,0,0,0,0,0,0,0,0]),L.SMHD=new Uint8Array([0,0,0,0,0,0,0,0]),L.STSD=new Uint8Array([0,0,0,0,0,0,0,1]);const n=new Uint8Array([105,115,111,109]),o=new Uint8Array([97,118,99,49]),c=new Uint8Array([0,0,0,1]);L.FTYP=L.box(L.types.ftyp,n,c,n,o),L.DINF=L.box(L.types.dinf,L.box(L.types.dref,i))}static box(e,...t){let s=8,i=t.length;const r=i;for(;i--;)s+=t[i].byteLength;const n=new Uint8Array(s);for(n[0]=s>>24&255,n[1]=s>>16&255,n[2]=s>>8&255,n[3]=s&255,n.set(e,4),i=0,s=8;i>24&255,e>>16&255,e>>8&255,e&255,s>>24,s>>16&255,s>>8&255,s&255,i>>24,i>>16&255,i>>8&255,i&255,85,196,0,0]))}static mdia(e){return L.box(L.types.mdia,L.mdhd(e.timescale||0,e.duration||0),L.hdlr(e.type),L.minf(e))}static mfhd(e){return L.box(L.types.mfhd,new Uint8Array([0,0,0,0,e>>24,e>>16&255,e>>8&255,e&255]))}static minf(e){return e.type==="audio"?L.box(L.types.minf,L.box(L.types.smhd,L.SMHD),L.DINF,L.stbl(e)):L.box(L.types.minf,L.box(L.types.vmhd,L.VMHD),L.DINF,L.stbl(e))}static moof(e,t,s){return L.box(L.types.moof,L.mfhd(e),L.traf(s,t))}static moov(e){let t=e.length;const s=[];for(;t--;)s[t]=L.trak(e[t]);return L.box.apply(null,[L.types.moov,L.mvhd(e[0].timescale||0,e[0].duration||0)].concat(s).concat(L.mvex(e)))}static mvex(e){let t=e.length;const s=[];for(;t--;)s[t]=L.trex(e[t]);return L.box.apply(null,[L.types.mvex,...s])}static mvhd(e,t){t*=e;const s=Math.floor(t/(tt+1)),i=Math.floor(t%(tt+1)),r=new Uint8Array([1,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,e>>24&255,e>>16&255,e>>8&255,e&255,s>>24,s>>16&255,s>>8&255,s&255,i>>24,i>>16&255,i>>8&255,i&255,0,1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255]);return L.box(L.types.mvhd,r)}static sdtp(e){const t=e.samples||[],s=new Uint8Array(4+t.length);let i,r;for(i=0;i>>8&255),t.push(n&255),t=t.concat(Array.prototype.slice.call(r));for(i=0;i>>8&255),s.push(n&255),s=s.concat(Array.prototype.slice.call(r));const o=L.box(L.types.avcC,new Uint8Array([1,t[3],t[4],t[5],255,224|e.sps.length].concat(t).concat([e.pps.length]).concat(s))),c=e.width,l=e.height,h=e.pixelRatio[0],d=e.pixelRatio[1];return L.box(L.types.avc1,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,c>>8&255,c&255,l>>8&255,l&255,0,72,0,0,0,72,0,0,0,0,0,0,0,1,18,100,97,105,108,121,109,111,116,105,111,110,47,104,108,115,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,17,17]),o,L.box(L.types.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])),L.box(L.types.pasp,new Uint8Array([h>>24,h>>16&255,h>>8&255,h&255,d>>24,d>>16&255,d>>8&255,d&255])))}static esds(e){const t=e.config;return new Uint8Array([0,0,0,0,3,25,0,1,0,4,17,64,21,0,0,0,0,0,0,0,0,0,0,0,5,2,...t,6,1,2])}static audioStsd(e){const t=e.samplerate||0;return new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,e.channelCount||0,0,16,0,0,0,0,t>>8&255,t&255,0,0])}static mp4a(e){return L.box(L.types.mp4a,L.audioStsd(e),L.box(L.types.esds,L.esds(e)))}static mp3(e){return L.box(L.types[".mp3"],L.audioStsd(e))}static ac3(e){return L.box(L.types["ac-3"],L.audioStsd(e),L.box(L.types.dac3,e.config))}static stsd(e){const{segmentCodec:t}=e;if(e.type==="audio"){if(t==="aac")return L.box(L.types.stsd,L.STSD,L.mp4a(e));if(t==="ac3"&&e.config)return L.box(L.types.stsd,L.STSD,L.ac3(e));if(t==="mp3"&&e.codec==="mp3")return L.box(L.types.stsd,L.STSD,L.mp3(e))}else if(e.pps&&e.sps){if(t==="avc")return L.box(L.types.stsd,L.STSD,L.avc1(e));if(t==="hevc"&&e.vps)return L.box(L.types.stsd,L.STSD,L.hvc1(e))}else throw new Error("video track missing pps or sps");throw new Error(`unsupported ${e.type} segment codec (${t}/${e.codec})`)}static tkhd(e){const t=e.id,s=(e.duration||0)*(e.timescale||0),i=e.width||0,r=e.height||0,n=Math.floor(s/(tt+1)),o=Math.floor(s%(tt+1));return L.box(L.types.tkhd,new Uint8Array([1,0,0,7,0,0,0,0,0,0,0,2,0,0,0,0,0,0,0,3,t>>24&255,t>>16&255,t>>8&255,t&255,0,0,0,0,n>>24,n>>16&255,n>>8&255,n&255,o>>24,o>>16&255,o>>8&255,o&255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,64,0,0,0,i>>8&255,i&255,0,0,r>>8&255,r&255,0,0]))}static traf(e,t){const s=L.sdtp(e),i=e.id,r=Math.floor(t/(tt+1)),n=Math.floor(t%(tt+1));return L.box(L.types.traf,L.box(L.types.tfhd,new Uint8Array([0,0,0,0,i>>24,i>>16&255,i>>8&255,i&255])),L.box(L.types.tfdt,new Uint8Array([1,0,0,0,r>>24,r>>16&255,r>>8&255,r&255,n>>24,n>>16&255,n>>8&255,n&255])),L.trun(e,s.length+16+20+8+16+8+8),s)}static trak(e){return e.duration=e.duration||4294967295,L.box(L.types.trak,L.tkhd(e),L.mdia(e))}static trex(e){const t=e.id;return L.box(L.types.trex,new Uint8Array([0,0,0,0,t>>24,t>>16&255,t>>8&255,t&255,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,1]))}static trun(e,t){const s=e.samples||[],i=s.length,r=12+16*i,n=new Uint8Array(r);let o,c,l,h,d,u;for(t+=8+r,n.set([e.type==="video"?1:0,0,15,1,i>>>24&255,i>>>16&255,i>>>8&255,i&255,t>>>24&255,t>>>16&255,t>>>8&255,t&255],0),o=0;o>>24&255,l>>>16&255,l>>>8&255,l&255,h>>>24&255,h>>>16&255,h>>>8&255,h&255,d.isLeading<<2|d.dependsOn,d.isDependedOn<<6|d.hasRedundancy<<4|d.paddingValue<<1|d.isNonSync,d.degradPrio&61440,d.degradPrio&15,u>>>24&255,u>>>16&255,u>>>8&255,u&255],12+16*o);return L.box(L.types.trun,n)}static initSegment(e){L.types||L.init();const t=L.moov(e);return we(L.FTYP,t)}static hvc1(e){const t=e.params,s=[e.vps,e.sps,e.pps],i=4,r=new Uint8Array([1,t.general_profile_space<<6|(t.general_tier_flag?32:0)|t.general_profile_idc,t.general_profile_compatibility_flags[0],t.general_profile_compatibility_flags[1],t.general_profile_compatibility_flags[2],t.general_profile_compatibility_flags[3],t.general_constraint_indicator_flags[0],t.general_constraint_indicator_flags[1],t.general_constraint_indicator_flags[2],t.general_constraint_indicator_flags[3],t.general_constraint_indicator_flags[4],t.general_constraint_indicator_flags[5],t.general_level_idc,240|t.min_spatial_segmentation_idc>>8,255&t.min_spatial_segmentation_idc,252|t.parallelismType,252|t.chroma_format_idc,248|t.bit_depth_luma_minus8,248|t.bit_depth_chroma_minus8,0,parseInt(t.frame_rate.fps),i-1|t.temporal_id_nested<<2|t.num_temporal_layers<<3|(t.frame_rate.fixed?64:0),s.length]);let n=r.length;for(let g=0;g>8,s[g][y].length&255]),n),n+=2,o.set(s[g][y],n),n+=s[g][y].length}const l=L.box(L.types.hvcC,o),h=e.width,d=e.height,u=e.pixelRatio[0],f=e.pixelRatio[1];return L.box(L.types.hvc1,new Uint8Array([0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,h>>8&255,h&255,d>>8&255,d&255,0,72,0,0,0,72,0,0,0,0,0,0,0,1,18,100,97,105,108,121,109,111,116,105,111,110,47,104,108,115,46,106,115,0,0,0,0,0,0,0,0,0,0,0,0,0,0,24,17,17]),l,L.box(L.types.btrt,new Uint8Array([0,28,156,128,0,45,198,192,0,45,198,192])),L.box(L.types.pasp,new Uint8Array([u>>24,u>>16&255,u>>8&255,u&255,f>>24,f>>16&255,f>>8&255,f&255])))}}L.types=void 0;L.HDLR_TYPES=void 0;L.STTS=void 0;L.STSC=void 0;L.STCO=void 0;L.STSZ=void 0;L.VMHD=void 0;L.SMHD=void 0;L.STSD=void 0;L.FTYP=void 0;L.DINF=void 0;const va=9e4;function Ki(a,e,t=1,s=!1){const i=a*e*t;return s?Math.round(i):i}function Cc(a,e,t=1,s=!1){return Ki(a,e,1/t,s)}function Ft(a,e=!1){return Ki(a,1e3,1/va,e)}function Pc(a,e=1){return Ki(a,va,1/e)}function Hr(a){const{baseTime:e,timescale:t,trackId:s}=a;return`${e/t} (${e}/${t}) trackId: ${s}`}const kc=10*1e3,wc=1024,Oc=1152,Fc=1536;let xt=null,Hs=null;function Yr(a,e,t,s){return{duration:e,size:t,cts:s,flags:{isLeading:0,isDependedOn:0,hasRedundancy:0,degradPrio:0,dependsOn:a?2:1,isNonSync:a?0:1}}}class fs extends Oe{constructor(e,t,s,i){if(super("mp4-remuxer",i),this.observer=void 0,this.config=void 0,this.typeSupported=void 0,this.ISGenerated=!1,this._initPTS=null,this._initDTS=null,this.nextVideoTs=null,this.nextAudioTs=null,this.videoSampleDuration=null,this.isAudioContiguous=!1,this.isVideoContiguous=!1,this.videoTrackConfig=void 0,this.observer=e,this.config=t,this.typeSupported=s,this.ISGenerated=!1,xt===null){const n=(navigator.userAgent||"").match(/Chrome\/(\d+)/i);xt=n?parseInt(n[1]):0}if(Hs===null){const r=navigator.userAgent.match(/Safari\/(\d+)/i);Hs=r?parseInt(r[1]):0}}destroy(){this.config=this.videoTrackConfig=this._initPTS=this._initDTS=null}resetTimeStamp(e){const t=this._initPTS;(!t||!e||e.trackId!==t.trackId||e.baseTime!==t.baseTime||e.timescale!==t.timescale)&&this.log(`Reset initPTS: ${t&&Hr(t)} > ${e&&Hr(e)}`),this._initPTS=this._initDTS=e}resetNextTimestamp(){this.log("reset next timestamp"),this.isVideoContiguous=!1,this.isAudioContiguous=!1}resetInitSegment(){this.log("ISGenerated flag reset"),this.ISGenerated=!1,this.videoTrackConfig=void 0}getVideoStartPts(e){let t=!1;const s=e[0].pts,i=e.reduce((r,n)=>{let o=n.pts,c=o-r;return c<-4294967296&&(t=!0,o=be(o,s),c=o-r),c>0?r:o},s);return t&&this.debug("PTS rollover detected"),i}remux(e,t,s,i,r,n,o,c){let l,h,d,u,f,g,y=r,p=r;const E=e.pid>-1,T=t.pid>-1,S=t.samples.length,v=e.samples.length>0,x=o&&S>0||S>1;if((!E||v)&&(!T||x)||this.ISGenerated||o){if(this.ISGenerated){var A,_,b,I;const W=this.videoTrackConfig;(W&&(t.width!==W.width||t.height!==W.height||((A=t.pixelRatio)==null?void 0:A[0])!==((_=W.pixelRatio)==null?void 0:_[0])||((b=t.pixelRatio)==null?void 0:b[1])!==((I=W.pixelRatio)==null?void 0:I[1]))||!W&&x||this.nextAudioTs===null&&v)&&this.resetInitSegment()}this.ISGenerated||(d=this.generateIS(e,t,r,n));const P=this.isVideoContiguous;let M=-1,G;if(x&&(M=Mc(t.samples),!P&&this.config.forceKeyFrameOnDiscontinuity))if(g=!0,M>0){this.warn(`Dropped ${M} out of ${S} video samples due to a missing keyframe`);const W=this.getVideoStartPts(t.samples);t.samples=t.samples.slice(M),t.dropped+=M,p+=(t.samples[0].pts-W)/t.inputTimeScale,G=p}else M===-1&&(this.warn(`No keyframe found out of ${S} video samples`),g=!1);if(this.ISGenerated){if(v&&x){const W=this.getVideoStartPts(t.samples),w=(be(e.samples[0].pts,W)-W)/t.inputTimeScale;y+=Math.max(0,w),p+=Math.max(0,-w)}if(v){if(e.samplerate||(this.warn("regenerate InitSegment as audio detected"),d=this.generateIS(e,t,r,n)),h=this.remuxAudio(e,y,this.isAudioContiguous,n,T||x||c===U.AUDIO?p:void 0),x){const W=h?h.endPTS-h.startPTS:0;t.inputTimeScale||(this.warn("regenerate InitSegment as video detected"),d=this.generateIS(e,t,r,n)),l=this.remuxVideo(t,p,P,W)}}else x&&(l=this.remuxVideo(t,p,P,0));l&&(l.firstKeyFrame=M,l.independent=M!==-1,l.firstKeyFramePTS=G)}}return this.ISGenerated&&this._initPTS&&this._initDTS&&(s.samples.length&&(f=xa(s,r,this._initPTS,this._initDTS)),i.samples.length&&(u=Aa(i,r,this._initPTS))),{audio:h,video:l,initSegment:d,independent:g,text:u,id3:f}}computeInitPts(e,t,s,i){const r=Math.round(s*t);let n=be(e,r);if(n0?O-1:O].dts&&(T=!0)}T&&n.sort(function(O,F){const z=O.dts-F.dts,se=O.pts-F.pts;return z||se}),g=n[0].dts,y=n[n.length-1].dts;const v=y-g,x=v?Math.round(v/(c-1)):f||e.inputTimeScale/30;if(s){const O=g-S,F=O>x,z=O<-1;if((F||z)&&(F?this.warn(`${(e.segmentCodec||"").toUpperCase()}: ${Ft(O,!0)} ms (${O}dts) hole between fragments detected at ${t.toFixed(3)}`):this.warn(`${(e.segmentCodec||"").toUpperCase()}: ${Ft(-O,!0)} ms (${O}dts) overlapping between fragments detected at ${t.toFixed(3)}`),!z||S>=n[0].pts||xt)){g=S;const se=n[0].pts-O;if(F)n[0].dts=g,n[0].pts=se;else{let Q=!0;for(let ee=0;eese&&Q);ee++){const Se=n[ee].pts;if(n[ee].dts-=O,n[ee].pts-=O,ee0?F.dts-n[O-1].dts:x;if(Q=O>0?F.pts-n[O-1].pts:x,Se.stretchShortVideoTrack&&this.nextAudioTs!==null){const Pe=Math.floor(Se.maxBufferHole*r),$e=(i?p+i*r:this.nextAudioTs+h)-F.pts;$e>Pe?(f=$e-me,f<0?f=me:M=!0,this.log(`It is approximately ${$e/90} ms to the next segment; using duration ${f/90} ms for the last video frame.`)):f=me}else f=me}const ee=Math.round(F.pts-F.dts);G=Math.min(G,f),K=Math.max(K,f),W=Math.min(W,Q),w=Math.max(w,Q),o.push(Yr(F.key,f,se,ee))}if(o.length){if(xt){if(xt<70){const O=o[0].flags;O.dependsOn=2,O.isNonSync=0}}else if(Hs&&w-W0&&(i&&Math.abs(S-(E+T))<9e3||Math.abs(be(y[0].pts,S)-(E+T))<20*h),y.forEach(function(w){w.pts=be(w.pts,S)}),!s||E<0){const w=y.length;if(y=y.filter(V=>V.pts>=0),w!==y.length&&this.warn(`Removed ${y.length-w} of ${w} samples (initPTS ${T} / ${n})`),!y.length)return;r===0?E=0:i&&!g?E=Math.max(0,S-T):E=y[0].pts-T}if(e.segmentCodec==="aac"){const w=this.config.maxAudioFramesDrift;for(let V=0,$=E+T;V=w*h&&F0){A+=p;try{D=new Uint8Array(A)}catch(F){this.observer.emit(m.ERROR,m.ERROR,{type:Y.MUX_ERROR,details:R.REMUX_ALLOC_ERROR,fatal:!1,error:F,bytes:A,reason:`fail allocating audio mdat ${A}`});return}u||(new DataView(D.buffer).setUint32(0,A),D.set(L.types.mdat,4))}else return;D.set(H,p);const O=H.byteLength;p+=O,f.push(Yr(!0,l,O,0)),x=q}const b=f.length;if(!b)return;const I=f[f.length-1];E=x-T,this.nextAudioTs=E+c*I.duration;const P=u?new Uint8Array(0):L.moof(e.sequenceNumber++,v/c,ne({},e,{samples:f}));e.samples=[];const M=(v-T)/n,G=this.nextAudioTs/n,K={data1:P,data2:D,startPTS:M,endPTS:G,startDTS:M,endDTS:G,type:"audio",hasAudio:!0,hasVideo:!1,nb:b};return this.isAudioContiguous=!0,K}}function be(a,e){let t;if(e===null)return a;for(e4294967296;)a+=t;return a}function Mc(a){for(let e=0;en.pts-o.pts);const r=a.samples;return a.samples=[],{samples:r}}class Nc extends Oe{constructor(e,t,s,i){super("passthrough-remuxer",i),this.emitInitSegment=!1,this.audioCodec=void 0,this.videoCodec=void 0,this.initData=void 0,this.initPTS=null,this.initTracks=void 0,this.lastEndTime=null,this.isVideoContiguous=!1}destroy(){}resetTimeStamp(e){this.lastEndTime=null;const t=this.initPTS;t&&e&&t.baseTime===e.baseTime&&t.timescale===e.timescale||(this.initPTS=e)}resetNextTimestamp(){this.isVideoContiguous=!1,this.lastEndTime=null}resetInitSegment(e,t,s,i){this.audioCodec=t,this.videoCodec=s,this.generateInitSegment(e,i),this.emitInitSegment=!0}generateInitSegment(e,t){let{audioCodec:s,videoCodec:i}=this;if(!(e!=null&&e.byteLength)){this.initTracks=void 0,this.initData=void 0;return}const{audio:r,video:n}=this.initData=Dn(e);if(t)Io(e,t);else{const c=r||n;c!=null&&c.encrypted&&this.warn(`Init segment with encrypted track with has no key ("${c.codec}")!`)}r&&(s=Wr(r,ae.AUDIO,this)),n&&(i=Wr(n,ae.VIDEO,this));const o={};r&&n?o.audiovideo={container:"video/mp4",codec:s+","+i,supplemental:n.supplemental,encrypted:n.encrypted,initSegment:e,id:"main"}:r?o.audio={container:"audio/mp4",codec:s,encrypted:r.encrypted,initSegment:e,id:"audio"}:n?o.video={container:"video/mp4",codec:i,supplemental:n.supplemental,encrypted:n.encrypted,initSegment:e,id:"main"}:this.warn("initSegment does not contain moov or trak boxes."),this.initTracks=o}remux(e,t,s,i,r,n){var o,c;let{initPTS:l,lastEndTime:h}=this;const d={audio:void 0,video:void 0,text:i,id3:s,initSegment:void 0};B(h)||(h=this.lastEndTime=r||0);const u=t.samples;if(!u.length)return d;const f={initPTS:void 0,timescale:void 0,trackId:void 0};let g=this.initData;if((o=g)!=null&&o.length||(this.generateInitSegment(u),g=this.initData),!((c=g)!=null&&c.length))return this.warn("Failed to generate initSegment."),d;this.emitInitSegment&&(f.tracks=this.initTracks,this.emitInitSegment=!1);const y=Ro(u,g,this),p=g.audio?y[g.audio.id]:null,E=g.video?y[g.video.id]:null,T=ss(E,1/0),S=ss(p,1/0),v=ss(E,0,!0),x=ss(p,0,!0);let D=r,A=0;const _=p&&(!E||!l&&S0?this.lastEndTime=P:(this.warn("Duration parsed from mp4 should be greater than zero"),this.resetNextTimestamp());const M=!!g.audio,G=!!g.video;let W="";M&&(W+="audio"),G&&(W+="video");const K=(g.audio?g.audio.encrypted:!1)||(g.video?g.video.encrypted:!1),w={data1:u,startPTS:I,startDTS:I,endPTS:P,endDTS:P,type:W,hasAudio:M,hasVideo:G,nb:1,dropped:0,encrypted:K};d.audio=M&&!G?w:void 0,d.video=G?w:void 0;const V=E==null?void 0:E.sampleCount;if(V){const $=E.keyFrameIndex,H=$!==-1;w.nb=V,w.dropped=$===0||this.isVideoContiguous?0:H?$:V,w.independent=H,w.firstKeyFrame=$,H&&E.keyFrameStart&&(w.firstKeyFramePTS=(E.keyFrameStart-l.baseTime)/l.timescale),this.isVideoContiguous||(d.independent=H),this.isVideoContiguous||(this.isVideoContiguous=H),w.dropped&&this.warn(`fmp4 does not start with IDR: firstIDR ${$}/${V} dropped: ${w.dropped} start: ${w.firstKeyFramePTS||"NA"}`)}return d.initSegment=f,d.id3=xa(s,r,l,l),i.samples.length&&(d.text=Aa(i,r,l)),d}}function ss(a,e,t=!1){return(a==null?void 0:a.start)!==void 0?(a.start+(t?a.duration:0))/a.timescale:e}function Bc(a,e,t,s){if(a===null)return!0;const i=Math.max(s,1),r=e-a.baseTime/a.timescale;return Math.abs(r-t)>i}function Wr(a,e,t){const s=a.codec;return s&&s.length>4?s:e===ae.AUDIO?s==="ec-3"||s==="ac-3"||s==="alac"?s:s==="fLaC"||s==="Opus"?Ts(s,!1):(t.warn(`Unhandled audio codec "${s}" in mp4 MAP`),s||"mp4a"):(t.warn(`Unhandled video codec "${s}" in mp4 MAP`),s||"avc1")}let Je;try{Je=self.performance.now.bind(self.performance)}catch{Je=Date.now}const gs=[{demux:Ac,remux:Nc},{demux:rt,remux:fs},{demux:Tc,remux:fs},{demux:vc,remux:fs}];gs.splice(2,0,{demux:Sc,remux:fs});class qr{constructor(e,t,s,i,r,n){this.asyncResult=!1,this.logger=void 0,this.observer=void 0,this.typeSupported=void 0,this.config=void 0,this.id=void 0,this.demuxer=void 0,this.remuxer=void 0,this.decrypter=void 0,this.probe=void 0,this.decryptionPromise=null,this.transmuxConfig=void 0,this.currentTransmuxState=void 0,this.observer=e,this.typeSupported=t,this.config=s,this.id=r,this.logger=n}configure(e){this.transmuxConfig=e,this.decrypter&&this.decrypter.reset()}push(e,t,s,i){const r=s.transmuxing;r.executeStart=Je();let n=new Uint8Array(e);const{currentTransmuxState:o,transmuxConfig:c}=this;i&&(this.currentTransmuxState=i);const{contiguous:l,discontinuity:h,trackSwitch:d,accurateTimeOffset:u,timeOffset:f,initSegmentChange:g}=i||o,{audioCodec:y,videoCodec:p,defaultInitPts:E,duration:T,initSegmentData:S}=c,v=$c(n,t);if(v&&_t(v.method)){const _=this.getDecrypter(),b=wi(v.method);if(_.isSync()){let I=_.softwareDecrypt(n,v.key.buffer,v.iv.buffer,b);if(s.part>-1){const M=_.flush();I=M&&M.buffer}if(!I)return r.executeEnd=Je(),Ys(s);n=new Uint8Array(I)}else return this.asyncResult=!0,this.decryptionPromise=_.webCryptoDecrypt(n,v.key.buffer,v.iv.buffer,b).then(I=>{const P=this.push(I,null,s);return this.decryptionPromise=null,P}),this.decryptionPromise}const x=this.needsProbing(h,d);if(x){const _=this.configureTransmuxer(n);if(_)return this.logger.warn(`[transmuxer] ${_.message}`),this.observer.emit(m.ERROR,m.ERROR,{type:Y.MEDIA_ERROR,details:R.FRAG_PARSING_ERROR,fatal:!1,error:_,reason:_.message}),r.executeEnd=Je(),Ys(s)}(h||d||g||x)&&this.resetInitSegment(S,y,p,T,t),(h||g||x)&&this.resetInitialTimestamp(E),l||this.resetContiguity();const D=this.transmux(n,v,f,u,s);this.asyncResult=Yt(D);const A=this.currentTransmuxState;return A.contiguous=!0,A.discontinuity=!1,A.trackSwitch=!1,r.executeEnd=Je(),D}flush(e){const t=e.transmuxing;t.executeStart=Je();const{decrypter:s,currentTransmuxState:i,decryptionPromise:r}=this;if(r)return this.asyncResult=!0,r.then(()=>this.flush(e));const n=[],{timeOffset:o}=i;if(s){const d=s.flush();d&&n.push(this.push(d.buffer,null,e))}const{demuxer:c,remuxer:l}=this;if(!c||!l){t.executeEnd=Je();const d=[Ys(e)];return this.asyncResult?Promise.resolve(d):d}const h=c.flush(o);return Yt(h)?(this.asyncResult=!0,h.then(d=>(this.flushRemux(n,d,e),n))):(this.flushRemux(n,h,e),this.asyncResult?Promise.resolve(n):n)}flushRemux(e,t,s){const{audioTrack:i,videoTrack:r,id3Track:n,textTrack:o}=t,{accurateTimeOffset:c,timeOffset:l}=this.currentTransmuxState;this.logger.log(`[transmuxer.ts]: Flushed ${this.id} sn: ${s.sn}${s.part>-1?" part: "+s.part:""} of ${this.id===U.MAIN?"level":"track"} ${s.level}`);const h=this.remuxer.remux(i,r,n,o,l,c,!0,this.id);e.push({remuxResult:h,chunkMeta:s}),s.transmuxing.executeEnd=Je()}resetInitialTimestamp(e){const{demuxer:t,remuxer:s}=this;!t||!s||(t.resetTimeStamp(e),s.resetTimeStamp(e))}resetContiguity(){const{demuxer:e,remuxer:t}=this;!e||!t||(e.resetContiguity(),t.resetNextTimestamp())}resetInitSegment(e,t,s,i,r){const{demuxer:n,remuxer:o}=this;!n||!o||(n.resetInitSegment(e,t,s,i),o.resetInitSegment(e,t,s,r))}destroy(){this.demuxer&&(this.demuxer.destroy(),this.demuxer=void 0),this.remuxer&&(this.remuxer.destroy(),this.remuxer=void 0)}transmux(e,t,s,i,r){let n;return t&&t.method==="SAMPLE-AES"?n=this.transmuxSampleAes(e,t,s,i,r):n=this.transmuxUnencrypted(e,s,i,r),n}transmuxUnencrypted(e,t,s,i){const{audioTrack:r,videoTrack:n,id3Track:o,textTrack:c}=this.demuxer.demux(e,t,!1,!this.config.progressive);return{remuxResult:this.remuxer.remux(r,n,o,c,t,s,!1,this.id),chunkMeta:i}}transmuxSampleAes(e,t,s,i,r){return this.demuxer.demuxSampleAes(e,t,s).then(n=>({remuxResult:this.remuxer.remux(n.audioTrack,n.videoTrack,n.id3Track,n.textTrack,s,i,!1,this.id),chunkMeta:r}))}configureTransmuxer(e){const{config:t,observer:s,typeSupported:i}=this;let r;for(let d=0,u=gs.length;d0&&(e==null?void 0:e.key)!=null&&e.iv!==null&&e.method!=null&&(t=e),t}const Ys=a=>({remuxResult:{},chunkMeta:a});function Yt(a){return"then"in a&&a.then instanceof Function}class Uc{constructor(e,t,s,i,r){this.audioCodec=void 0,this.videoCodec=void 0,this.initSegmentData=void 0,this.duration=void 0,this.defaultInitPts=void 0,this.audioCodec=e,this.videoCodec=t,this.initSegmentData=s,this.duration=i,this.defaultInitPts=r||null}}class Gc{constructor(e,t,s,i,r,n){this.discontinuity=void 0,this.contiguous=void 0,this.accurateTimeOffset=void 0,this.trackSwitch=void 0,this.timeOffset=void 0,this.initSegmentChange=void 0,this.discontinuity=e,this.contiguous=t,this.accurateTimeOffset=s,this.trackSwitch=i,this.timeOffset=r,this.initSegmentChange=n}}let jr=0;class Ia{constructor(e,t,s,i){this.error=null,this.hls=void 0,this.id=void 0,this.instanceNo=jr++,this.observer=void 0,this.frag=null,this.part=null,this.useWorker=void 0,this.workerContext=null,this.transmuxer=null,this.onTransmuxComplete=void 0,this.onFlush=void 0,this.onWorkerMessage=c=>{const l=c.data,h=this.hls;if(!(!h||!(l!=null&&l.event)||l.instanceNo!==this.instanceNo))switch(l.event){case"init":{var d;const u=(d=this.workerContext)==null?void 0:d.objectURL;u&&self.URL.revokeObjectURL(u);break}case"transmuxComplete":{this.handleTransmuxComplete(l.data);break}case"flush":{this.onFlush(l.data);break}case"workerLog":{h.logger[l.data.logType]&&h.logger[l.data.logType](l.data.message);break}default:{l.data=l.data||{},l.data.frag=this.frag,l.data.part=this.part,l.data.id=this.id,h.trigger(l.event,l.data);break}}},this.onWorkerError=c=>{if(!this.hls)return;const l=new Error(`${c.message} (${c.filename}:${c.lineno})`);this.hls.config.enableWorker=!1,this.hls.logger.warn(`Error in "${this.id}" Web Worker, fallback to inline`),this.hls.trigger(m.ERROR,{type:Y.OTHER_ERROR,details:R.INTERNAL_EXCEPTION,fatal:!1,event:"demuxerWorker",error:l})};const r=e.config;this.hls=e,this.id=t,this.useWorker=!!r.enableWorker,this.onTransmuxComplete=s,this.onFlush=i;const n=(c,l)=>{l=l||{},l.frag=this.frag||void 0,c===m.ERROR&&(l=l,l.parent=this.id,l.part=this.part,this.error=l.error),this.hls.trigger(c,l)};this.observer=new Mi,this.observer.on(m.FRAG_DECRYPTED,n),this.observer.on(m.ERROR,n);const o=cr(r.preferManagedMediaSource);if(this.useWorker&&typeof Worker<"u"){const c=this.hls.logger;if(r.workerPath||Yl()){try{r.workerPath?(c.log(`loading Web Worker ${r.workerPath} for "${t}"`),this.workerContext=ql(r.workerPath)):(c.log(`injecting Web Worker for "${t}"`),this.workerContext=Wl());const{worker:h}=this.workerContext;h.addEventListener("message",this.onWorkerMessage),h.addEventListener("error",this.onWorkerError),h.postMessage({instanceNo:this.instanceNo,cmd:"init",typeSupported:o,id:t,config:oe(r)})}catch(h){c.warn(`Error setting up "${t}" Web Worker, fallback to inline`,h),this.terminateWorker(),this.error=null,this.transmuxer=new qr(this.observer,o,r,"",t,e.logger)}return}}this.transmuxer=new qr(this.observer,o,r,"",t,e.logger)}reset(){if(this.frag=null,this.part=null,this.workerContext){const e=this.instanceNo;this.instanceNo=jr++;const t=this.hls.config,s=cr(t.preferManagedMediaSource);this.workerContext.worker.postMessage({instanceNo:this.instanceNo,cmd:"reset",resetNo:e,typeSupported:s,id:this.id,config:oe(t)})}}terminateWorker(){if(this.workerContext){const{worker:e}=this.workerContext;this.workerContext=null,e.removeEventListener("message",this.onWorkerMessage),e.removeEventListener("error",this.onWorkerError),jl(this.hls.config.workerPath)}}destroy(){if(this.workerContext)this.terminateWorker(),this.onWorkerMessage=this.onWorkerError=null;else{const t=this.transmuxer;t&&(t.destroy(),this.transmuxer=null)}const e=this.observer;e&&e.removeAllListeners(),this.frag=null,this.part=null,this.observer=null,this.hls=null}push(e,t,s,i,r,n,o,c,l,h){var d,u;l.transmuxing.start=self.performance.now();const{instanceNo:f,transmuxer:g}=this,y=n?n.start:r.start,p=r.decryptdata,E=this.frag,T=!(E&&r.cc===E.cc),S=!(E&&l.level===E.level),v=E?l.sn-E.sn:-1,x=this.part?l.part-this.part.index:-1,D=v===0&&l.id>1&&l.id===(E==null?void 0:E.stats.chunkCount),A=!S&&(v===1||v===0&&(x===1||D&&x<=0)),_=self.performance.now();(S||v||r.stats.parsing.start===0)&&(r.stats.parsing.start=_),n&&(x||!A)&&(n.stats.parsing.start=_);const b=!(E&&((d=r.initSegment)==null?void 0:d.url)===((u=E.initSegment)==null?void 0:u.url)),I=new Gc(T,A,c,S,y,b);if(!A||T||b){this.hls.logger.log(`[transmuxer-interface]: Starting new transmux session for ${r.type} sn: ${l.sn}${l.part>-1?" part: "+l.part:""} ${this.id===U.MAIN?"level":"track"}: ${l.level} id: ${l.id} discontinuity: ${T} trackSwitch: ${S} contiguous: ${A} accurateTimeOffset: ${c} timeOffset: ${y} - initSegmentChange: ${b}`);const P=new Nc(s,i,t,o,h);this.configureTransmuxer(P)}if(this.frag=r,this.part=n,this.workerContext)this.workerContext.worker.postMessage({instanceNo:f,cmd:"demux",data:e,decryptdata:p,chunkMeta:l,state:I},e instanceof ArrayBuffer?[e]:[]);else if(g){const P=g.push(e,p,l,I);Yt(P)?P.then(F=>{this.handleTransmuxComplete(F)}).catch(F=>{this.transmuxerError(F,l,"transmuxer-interface push error")}):this.handleTransmuxComplete(P)}}flush(e){e.transmuxing.start=self.performance.now();const{instanceNo:t,transmuxer:s}=this;if(this.workerContext)this.workerContext.worker.postMessage({instanceNo:t,cmd:"flush",chunkMeta:e});else if(s){const i=s.flush(e);Yt(i)?i.then(r=>{this.handleFlushResult(r,e)}).catch(r=>{this.transmuxerError(r,e,"transmuxer-interface flush error")}):this.handleFlushResult(i,e)}}transmuxerError(e,t,s){this.hls&&(this.error=e,this.hls.trigger(m.ERROR,{type:H.MEDIA_ERROR,details:R.FRAG_PARSING_ERROR,chunkMeta:t,frag:this.frag||void 0,part:this.part||void 0,fatal:!1,error:e,err:e,reason:s}))}handleFlushResult(e,t){e.forEach(s=>{this.handleTransmuxComplete(s)}),this.onFlush(t)}configureTransmuxer(e){const{instanceNo:t,transmuxer:s}=this;this.workerContext?this.workerContext.worker.postMessage({instanceNo:t,cmd:"configure",config:e}):s&&s.configure(e)}handleTransmuxComplete(e){e.chunkMeta.transmuxing.end=self.performance.now(),this.onTransmuxComplete(e)}}const qr=100;class Uc extends wi{constructor(e,t,s){super(e,t,s,"audio-stream-controller",U.AUDIO),this.mainAnchor=null,this.mainFragLoading=null,this.audioOnly=!1,this.bufferedTrack=null,this.switchingTrack=null,this.trackId=-1,this.waitingData=null,this.mainDetails=null,this.flushing=!1,this.bufferFlushed=!1,this.cachedTrackLoadedData=null,this.registerListeners()}onHandlerDestroying(){this.unregisterListeners(),super.onHandlerDestroying(),this.resetItem()}resetItem(){this.mainDetails=this.mainAnchor=this.mainFragLoading=this.bufferedTrack=this.switchingTrack=this.waitingData=this.cachedTrackLoadedData=null}registerListeners(){super.registerListeners();const{hls:e}=this;e.on(m.LEVEL_LOADED,this.onLevelLoaded,this),e.on(m.AUDIO_TRACKS_UPDATED,this.onAudioTracksUpdated,this),e.on(m.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),e.on(m.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),e.on(m.BUFFER_RESET,this.onBufferReset,this),e.on(m.BUFFER_CREATED,this.onBufferCreated,this),e.on(m.BUFFER_FLUSHING,this.onBufferFlushing,this),e.on(m.BUFFER_FLUSHED,this.onBufferFlushed,this),e.on(m.INIT_PTS_FOUND,this.onInitPtsFound,this),e.on(m.FRAG_LOADING,this.onFragLoading,this),e.on(m.FRAG_BUFFERED,this.onFragBuffered,this)}unregisterListeners(){const{hls:e}=this;e&&(super.unregisterListeners(),e.off(m.LEVEL_LOADED,this.onLevelLoaded,this),e.off(m.AUDIO_TRACKS_UPDATED,this.onAudioTracksUpdated,this),e.off(m.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),e.off(m.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),e.off(m.BUFFER_RESET,this.onBufferReset,this),e.off(m.BUFFER_CREATED,this.onBufferCreated,this),e.off(m.BUFFER_FLUSHING,this.onBufferFlushing,this),e.off(m.BUFFER_FLUSHED,this.onBufferFlushed,this),e.off(m.INIT_PTS_FOUND,this.onInitPtsFound,this),e.off(m.FRAG_LOADING,this.onFragLoading,this),e.off(m.FRAG_BUFFERED,this.onFragBuffered,this))}onInitPtsFound(e,{frag:t,id:s,initPTS:i,timescale:r,trackId:n}){if(s===U.MAIN){const o=t.cc,c=this.fragCurrent;if(this.initPTS[o]={baseTime:i,timescale:r,trackId:n},this.log(`InitPTS for cc: ${o} found from main: ${i/r} (${i}/${r}) trackId: ${n}`),this.mainAnchor=t,this.state===C.WAITING_INIT_PTS){const l=this.waitingData;(!l&&!this.loadingParts||l&&l.frag.cc!==o)&&this.syncWithAnchor(t,l==null?void 0:l.frag)}else!this.hls.hasEnoughToStart&&c&&c.cc!==o?(c.abortRequests(),this.syncWithAnchor(t,c)):this.state===C.IDLE&&this.tick()}}getLoadPosition(){return!this.startFragRequested&&this.nextLoadPosition>=0?this.nextLoadPosition:super.getLoadPosition()}syncWithAnchor(e,t){var s;const i=((s=this.mainFragLoading)==null?void 0:s.frag)||null;if(t&&(i==null?void 0:i.cc)===t.cc)return;const r=(i||e).cc,n=this.getLevelDetails(),o=this.getLoadPosition(),c=Nn(n,r,o);c&&(this.log(`Syncing with main frag at ${c.start} cc ${c.cc}`),this.startFragRequested=!1,this.nextLoadPosition=c.start,this.resetLoadingState(),this.state===C.IDLE&&this.doTickIdle())}startLoad(e,t){if(!this.levels){this.startPosition=e,this.state=C.STOPPED;return}const s=this.lastCurrentTime;this.stopLoad(),this.setInterval(qr),s>0&&e===-1?(this.log(`Override startPosition with lastCurrentTime @${s.toFixed(3)}`),e=s,this.state=C.IDLE):this.state=C.WAITING_TRACK,this.nextLoadPosition=this.lastCurrentTime=e+this.timelineOffset,this.startPosition=t?-1:e,this.tick()}doTick(){switch(this.state){case C.IDLE:this.doTickIdle();break;case C.WAITING_TRACK:{const{levels:e,trackId:t}=this,s=e==null?void 0:e[t],i=s==null?void 0:s.details;if(i&&!this.waitForLive(s)){if(this.waitForCdnTuneIn(i))break;this.state=C.WAITING_INIT_PTS}break}case C.FRAG_LOADING_WAITING_RETRY:{this.checkRetryDate();break}case C.WAITING_INIT_PTS:{const e=this.waitingData;if(e){const{frag:t,part:s,cache:i,complete:r}=e,n=this.mainAnchor;if(this.initPTS[t.cc]!==void 0){this.waitingData=null,this.state=C.FRAG_LOADING;const o=i.flush().buffer,c={frag:t,part:s,payload:o,networkDetails:null};this._handleFragmentLoadProgress(c),r&&super._handleFragmentLoadComplete(c)}else n&&n.cc!==e.frag.cc&&this.syncWithAnchor(n,e.frag)}else this.state=C.IDLE}}this.onTickEnd()}resetLoadingState(){const e=this.waitingData;e&&(this.fragmentTracker.removeFragment(e.frag),this.waitingData=null),super.resetLoadingState()}onTickEnd(){const{media:e}=this;e!=null&&e.readyState&&(this.lastCurrentTime=e.currentTime)}doTickIdle(){var e;const{hls:t,levels:s,media:i,trackId:r}=this,n=t.config;if(!this.buffering||!i&&!this.primaryPrefetch&&(this.startFragRequested||!n.startFragPrefetch)||!(s!=null&&s[r]))return;const o=s[r],c=o.details;if(!c||this.waitForLive(o)||this.waitForCdnTuneIn(c)){this.state=C.WAITING_TRACK,this.startFragRequested=!1;return}const l=this.mediaBuffer?this.mediaBuffer:this.media;this.bufferFlushed&&l&&(this.bufferFlushed=!1,this.afterBufferFlushed(l,ae.AUDIO,U.AUDIO));const h=this.getFwdBufferInfo(l,U.AUDIO);if(h===null)return;if(!this.switchingTrack&&this._streamEnded(h,c)){t.trigger(m.BUFFER_EOS,{type:"audio"}),this.state=C.ENDED;return}const u=h.len,d=t.maxBufferLength,f=c.fragments,g=f[0].start,y=this.getLoadPosition(),p=this.flushing?y:h.end;if(this.switchingTrack&&i){const S=y;c.PTSKnown&&Sg||h.nextStart)&&(this.log("Alt audio track ahead of main track, seek to start of alt audio track"),i.currentTime=g+.05)}if(u>=d&&!this.switchingTrack&&pT.end){const v=this.fragmentTracker.getFragAtPos(p,U.MAIN);v&&v.end>T.end&&(T=v,this.mainFragLoading={frag:v,targetBufferTime:null})}if(E.start>T.end)return}this.loadFragment(E,o,p)}onMediaDetaching(e,t){this.bufferFlushed=this.flushing=!1,super.onMediaDetaching(e,t)}onAudioTracksUpdated(e,{audioTracks:t}){this.resetTransmuxer(),this.levels=t.map(s=>new Kt(s))}onAudioTrackSwitching(e,t){const s=!!t.url;this.trackId=t.id;const{fragCurrent:i}=this;i&&(i.abortRequests(),this.removeUnbufferedFrags(i.start)),this.resetLoadingState(),s?(this.switchingTrack=t,this.flushAudioIfNeeded(t),this.state!==C.STOPPED&&(this.setInterval(qr),this.state=C.IDLE,this.tick())):(this.resetTransmuxer(),this.switchingTrack=null,this.bufferedTrack=t,this.clearInterval())}onManifestLoading(){super.onManifestLoading(),this.bufferFlushed=this.flushing=this.audioOnly=!1,this.resetItem(),this.trackId=-1}onLevelLoaded(e,t){this.mainDetails=t.details;const s=this.cachedTrackLoadedData;s&&(this.cachedTrackLoadedData=null,this.onAudioTrackLoaded(m.AUDIO_TRACK_LOADED,s))}onAudioTrackLoaded(e,t){var s;const{levels:i}=this,{details:r,id:n,groupId:o,track:c}=t;if(!i){this.warn(`Audio tracks reset while loading track ${n} "${c.name}" of "${o}"`);return}const l=this.mainDetails;if(!l||r.endCC>l.endCC||l.expired){this.cachedTrackLoadedData=t,this.state!==C.STOPPED&&(this.state=C.WAITING_TRACK);return}this.cachedTrackLoadedData=null,this.log(`Audio track ${n} "${c.name}" of "${o}" loaded [${r.startSN},${r.endSN}]${r.lastPartSn?`[part-${r.lastPartSn}-${r.lastPartIndex}]`:""},duration:${r.totalduration}`);const h=i[n];let u=0;if(r.live||(s=h.details)!=null&&s.live){if(this.checkLiveUpdate(r),r.deltaUpdateFailed)return;if(h.details){var d;u=this.alignPlaylists(r,h.details,(d=this.levelLastLoaded)==null?void 0:d.details)}r.alignedSliding||(ta(r,l),r.alignedSliding||Ls(r,l),u=r.fragmentStart)}h.details=r,this.levelLastLoaded=h,this.startFragRequested||this.setStartPosition(l,u),this.hls.trigger(m.AUDIO_TRACK_UPDATED,{details:r,id:n,groupId:t.groupId}),this.state===C.WAITING_TRACK&&!this.waitForCdnTuneIn(r)&&(this.state=C.IDLE),this.tick()}_handleFragmentLoadProgress(e){var t;const s=e.frag,{part:i,payload:r}=e,{config:n,trackId:o,levels:c}=this;if(!c){this.warn(`Audio tracks were reset while fragment load was in progress. Fragment ${s.sn} of level ${s.level} will not be buffered`);return}const l=c[o];if(!l){this.warn("Audio track is undefined on fragment load progress");return}const h=l.details;if(!h){this.warn("Audio track details undefined on fragment load progress"),this.removeUnbufferedFrags(s.start);return}const u=n.defaultAudioCodec||l.audioCodec||"mp4a.40.2";let d=this.transmuxer;d||(d=this.transmuxer=new va(this.hls,U.AUDIO,this._handleTransmuxComplete.bind(this),this._handleTransmuxerFlush.bind(this)));const f=this.initPTS[s.cc],g=(t=s.initSegment)==null?void 0:t.data;if(f!==void 0){const p=i?i.index:-1,E=p!==-1,T=new Ci(s.level,s.sn,s.stats.chunkCount,r.byteLength,p,E);d.push(r,g,u,"",s,i,h.totalduration,!1,T,f)}else{this.log(`Unknown video PTS for cc ${s.cc}, waiting for video PTS before demuxing audio frag ${s.sn} of [${h.startSN} ,${h.endSN}],track ${o}`);const{cache:y}=this.waitingData=this.waitingData||{frag:s,part:i,cache:new sa,complete:!1};y.push(new Uint8Array(r)),this.state!==C.STOPPED&&(this.state=C.WAITING_INIT_PTS)}}_handleFragmentLoadComplete(e){if(this.waitingData){this.waitingData.complete=!0;return}super._handleFragmentLoadComplete(e)}onBufferReset(){this.mediaBuffer=null}onBufferCreated(e,t){this.bufferFlushed=this.flushing=!1;const s=t.tracks.audio;s&&(this.mediaBuffer=s.buffer||null)}onFragLoading(e,t){!this.audioOnly&&t.frag.type===U.MAIN&&ue(t.frag)&&(this.mainFragLoading=t,this.state===C.IDLE&&this.tick())}onFragBuffered(e,t){const{frag:s,part:i}=t;if(s.type!==U.AUDIO){!this.audioOnly&&s.type===U.MAIN&&!s.elementaryStreams.video&&!s.elementaryStreams.audiovideo&&(this.audioOnly=!0,this.mainFragLoading=null);return}if(this.fragContextChanged(s)){this.warn(`Fragment ${s.sn}${i?" p: "+i.index:""} of level ${s.level} finished buffering, but was aborted. state: ${this.state}, audioSwitch: ${this.switchingTrack?this.switchingTrack.name:"false"}`);return}if(ue(s)){this.fragPrevious=s;const r=this.switchingTrack;r&&(this.bufferedTrack=r,this.switchingTrack=null,this.hls.trigger(m.AUDIO_TRACK_SWITCHED,ie({},r)))}this.fragBufferedComplete(s,i),this.media&&this.tick()}onError(e,t){var s;if(t.fatal){this.state=C.ERROR;return}switch(t.details){case R.FRAG_GAP:case R.FRAG_PARSING_ERROR:case R.FRAG_DECRYPT_ERROR:case R.FRAG_LOAD_ERROR:case R.FRAG_LOAD_TIMEOUT:case R.KEY_LOAD_ERROR:case R.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(U.AUDIO,t);break;case R.AUDIO_TRACK_LOAD_ERROR:case R.AUDIO_TRACK_LOAD_TIMEOUT:case R.LEVEL_PARSING_ERROR:!t.levelRetry&&this.state===C.WAITING_TRACK&&((s=t.context)==null?void 0:s.type)===J.AUDIO_TRACK&&(this.state=C.IDLE);break;case R.BUFFER_ADD_CODEC_ERROR:case R.BUFFER_APPEND_ERROR:if(t.parent!=="audio")return;this.reduceLengthAndFlushBuffer(t)||this.resetLoadingState();break;case R.BUFFER_FULL_ERROR:if(t.parent!=="audio")return;this.reduceLengthAndFlushBuffer(t)&&(this.bufferedTrack=null,super.flushMainBuffer(0,Number.POSITIVE_INFINITY,"audio"));break;case R.INTERNAL_EXCEPTION:this.recoverWorkerError(t);break}}onBufferFlushing(e,{type:t}){t!==ae.VIDEO&&(this.flushing=!0)}onBufferFlushed(e,{type:t}){if(t!==ae.VIDEO){this.flushing=!1,this.bufferFlushed=!0,this.state===C.ENDED&&(this.state=C.IDLE);const s=this.mediaBuffer||this.media;s&&(this.afterBufferFlushed(s,t,U.AUDIO),this.tick())}}_handleTransmuxComplete(e){var t;const s="audio",{hls:i}=this,{remuxResult:r,chunkMeta:n}=e,o=this.getCurrentContext(n);if(!o){this.resetWhenMissingContext(n);return}const{frag:c,part:l,level:h}=o,{details:u}=h,{audio:d,text:f,id3:g,initSegment:y}=r;if(this.fragContextChanged(c)||!u){this.fragmentTracker.removeFragment(c);return}if(this.state=C.PARSING,this.switchingTrack&&d&&this.completeAudioSwitch(this.switchingTrack),y!=null&&y.tracks){const p=c.initSegment||c;if(this.unhandledEncryptionError(y,c))return;this._bufferInitSegment(h,y.tracks,p,n),i.trigger(m.FRAG_PARSING_INIT_SEGMENT,{frag:p,id:s,tracks:y.tracks})}if(d){const{startPTS:p,endPTS:E,startDTS:T,endDTS:S}=d;l&&(l.elementaryStreams[ae.AUDIO]={startPTS:p,endPTS:E,startDTS:T,endDTS:S}),c.setElementaryStreamInfo(ae.AUDIO,p,E,T,S),this.bufferFragmentData(d,c,l,n)}if(g!=null&&(t=g.samples)!=null&&t.length){const p=ne({id:s,frag:c,details:u},g);i.trigger(m.FRAG_PARSING_METADATA,p)}if(f){const p=ne({id:s,frag:c,details:u},f);i.trigger(m.FRAG_PARSING_USERDATA,p)}}_bufferInitSegment(e,t,s,i){if(this.state!==C.PARSING||(t.video&&delete t.video,t.audiovideo&&delete t.audiovideo,!t.audio))return;const r=t.audio;r.id=U.AUDIO;const n=e.audioCodec;this.log(`Init audio buffer, container:${r.container}, codecs[level/parsed]=[${n}/${r.codec}]`),n&&n.split(",").length===1&&(r.levelCodec=n),this.hls.trigger(m.BUFFER_CODECS,t);const o=r.initSegment;if(o!=null&&o.byteLength){const c={type:"audio",frag:s,part:null,chunkMeta:i,parent:s.type,data:o};this.hls.trigger(m.BUFFER_APPENDING,c)}this.tickImmediate()}loadFragment(e,t,s){const i=this.fragmentTracker.getState(e);if(this.switchingTrack||i===fe.NOT_LOADED||i===fe.PARTIAL){var r;if(!ue(e))this._loadInitSegment(e,t);else if((r=t.details)!=null&&r.live&&!this.initPTS[e.cc]){this.log(`Waiting for video PTS in continuity counter ${e.cc} of live stream before loading audio fragment ${e.sn} of level ${this.trackId}`),this.state=C.WAITING_INIT_PTS;const n=this.mainDetails;n&&n.fragmentStart!==t.details.fragmentStart&&Ls(t.details,n)}else super.loadFragment(e,t,s)}else this.clearTrackerIfNeeded(e)}flushAudioIfNeeded(e){if(this.media&&this.bufferedTrack){const{name:t,lang:s,assocLang:i,characteristics:r,audioCodec:n,channels:o}=this.bufferedTrack;gt({name:t,lang:s,assocLang:i,characteristics:r,audioCodec:n,channels:o},e,ft)||(Ss(e.url,this.hls)?(this.log("Switching audio track : flushing all audio"),super.flushMainBuffer(0,Number.POSITIVE_INFINITY,"audio"),this.bufferedTrack=null):this.bufferedTrack=e)}}completeAudioSwitch(e){const{hls:t}=this;this.flushAudioIfNeeded(e),this.bufferedTrack=e,this.switchingTrack=null,t.trigger(m.AUDIO_TRACK_SWITCHED,ie({},e))}}class Gi extends we{constructor(e,t){super(t,e.logger),this.hls=void 0,this.canLoad=!1,this.timer=-1,this.hls=e}destroy(){this.clearTimer(),this.hls=this.log=this.warn=null}clearTimer(){this.timer!==-1&&(self.clearTimeout(this.timer),this.timer=-1)}startLoad(){this.canLoad=!0,this.loadPlaylist()}stopLoad(){this.canLoad=!1,this.clearTimer()}switchParams(e,t,s){const i=t==null?void 0:t.renditionReports;if(i){let r=-1;for(let n=0;n=0&&h>t.partTarget&&(c+=1)}const l=s&&lr(s);return new cr(o,c>=0?c:void 0,l)}}}loadPlaylist(e){this.clearTimer()}loadingPlaylist(e,t){this.clearTimer()}shouldLoadPlaylist(e){return this.canLoad&&!!e&&!!e.url&&(!e.details||e.details.live)}getUrlWithDirectives(e,t){if(t)try{return t.addDirectives(e)}catch(s){this.warn(`Could not construct new URL with HLS Delivery Directives: ${s}`)}return e}playlistLoaded(e,t,s){const{details:i,stats:r}=t,n=self.performance.now(),o=r.loading.first?Math.max(0,n-r.loading.first):0;i.advancedDateTime=Date.now()-o;const c=this.hls.config.timelineOffset;if(c!==i.appliedTimelineOffset){const h=Math.max(c||0,0);i.appliedTimelineOffset=h,i.fragments.forEach(u=>{u.setStart(u.playlistOffset+h)})}if(i.live||s!=null&&s.live){const h="levelInfo"in t?t.levelInfo:t.track;if(i.reloaded(s),s&&i.fragments.length>0){Pl(s,i,this);const T=i.playlistParsingError;if(T){this.warn(T);const S=this.hls;if(!S.config.ignorePlaylistParsingErrors){var l;const{networkDetails:v}=t;S.trigger(m.ERROR,{type:H.NETWORK_ERROR,details:R.LEVEL_PARSING_ERROR,fatal:!1,url:i.url,error:T,reason:T.message,level:t.level||void 0,parent:(l=i.fragments[0])==null?void 0:l.type,networkDetails:v,stats:r});return}i.playlistParsingError=null}}i.requestScheduled===-1&&(i.requestScheduled=r.loading.start);const u=this.hls.mainForwardBufferInfo,d=u?u.end-u.len:0,f=(i.edge-d)*1e3,g=zn(i,f);if(i.requestScheduled+g0){if(b>i.targetduration*3)this.log(`Playlist last advanced ${_.toFixed(2)}s ago. Omitting segment and part directives.`),p=void 0,E=void 0;else if(s!=null&&s.tuneInGoal&&b-i.partTarget>s.tuneInGoal)this.warn(`CDN Tune-in goal increased from: ${s.tuneInGoal} to: ${I} with playlist age: ${i.age}`),I=0;else{const P=Math.floor(I/i.targetduration);if(p+=P,E!==void 0){const F=Math.round(I%i.targetduration/i.partTarget);E+=F}this.log(`CDN Tune-in age: ${i.ageHeader}s last advanced ${_.toFixed(2)}s goal: ${I} skip sn ${P} to part ${E}`)}i.tuneInGoal=I}if(y=this.getDeliveryDirectives(i,t.deliveryDirectives,p,E),T||!A){i.requestScheduled=n,this.loadingPlaylist(h,y);return}}else(i.canBlockReload||i.canSkipUntil)&&(y=this.getDeliveryDirectives(i,t.deliveryDirectives,p,E));y&&p!==void 0&&i.canBlockReload&&(i.requestScheduled=r.loading.first+Math.max(g-o*2,g/2)),this.scheduleLoading(h,y,i)}else this.clearTimer()}scheduleLoading(e,t,s){const i=s||e.details;if(!i){this.loadingPlaylist(e,t);return}const r=self.performance.now(),n=i.requestScheduled;if(r>=n){this.loadingPlaylist(e,t);return}const o=n-r;this.log(`reload live playlist ${e.name||e.bitrate+"bps"} in ${Math.round(o)} ms`),this.clearTimer(),this.timer=self.setTimeout(()=>this.loadingPlaylist(e,t),o)}getDeliveryDirectives(e,t,s,i){let r=lr(e);return t!=null&&t.skip&&e.deltaUpdateFailed&&(s=t.msn,i=t.part,r=hs.No),new cr(s,i,r)}checkRetry(e){const t=e.details,s=vs(e),i=e.errorAction,{action:r,retryCount:n=0,retryConfig:o}=i||{},c=!!i&&!!o&&(r===pe.RetryRequest||!i.resolved&&r===pe.SendAlternateToPenaltyBox);if(c){var l;if(n>=o.maxNumRetry)return!1;if(s&&(l=e.context)!=null&&l.deliveryDirectives)this.warn(`Retrying playlist loading ${n+1}/${o.maxNumRetry} after "${t}" without delivery-directives`),this.loadPlaylist();else{const h=_i(o,n);this.clearTimer(),this.timer=self.setTimeout(()=>this.loadPlaylist(),h),this.warn(`Retrying playlist loading ${n+1}/${o.maxNumRetry} after "${t}" in ${h}ms`)}e.levelRetry=!0,i.resolved=!0}return c}}function xa(a,e){if(a.length!==e.length)return!1;for(let t=0;ta[i]!==e[i])}function gi(a,e){return e.label.toLowerCase()===a.name.toLowerCase()&&(!e.language||e.language.toLowerCase()===(a.lang||"").toLowerCase())}class $c extends Gi{constructor(e){super(e,"audio-track-controller"),this.tracks=[],this.groupIds=null,this.tracksInGroup=[],this.trackId=-1,this.currentTrack=null,this.selectDefaultTrack=!0,this.registerListeners()}registerListeners(){const{hls:e}=this;e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.MANIFEST_PARSED,this.onManifestParsed,this),e.on(m.LEVEL_LOADING,this.onLevelLoading,this),e.on(m.LEVEL_SWITCHING,this.onLevelSwitching,this),e.on(m.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),e.on(m.ERROR,this.onError,this)}unregisterListeners(){const{hls:e}=this;e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.MANIFEST_PARSED,this.onManifestParsed,this),e.off(m.LEVEL_LOADING,this.onLevelLoading,this),e.off(m.LEVEL_SWITCHING,this.onLevelSwitching,this),e.off(m.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),e.off(m.ERROR,this.onError,this)}destroy(){this.unregisterListeners(),this.tracks.length=0,this.tracksInGroup.length=0,this.currentTrack=null,super.destroy()}onManifestLoading(){this.tracks=[],this.tracksInGroup=[],this.groupIds=null,this.currentTrack=null,this.trackId=-1,this.selectDefaultTrack=!0}onManifestParsed(e,t){this.tracks=t.audioTracks||[]}onAudioTrackLoaded(e,t){const{id:s,groupId:i,details:r}=t,n=this.tracksInGroup[s];if(!n||n.groupId!==i){this.warn(`Audio track with id:${s} and group:${i} not found in active group ${n==null?void 0:n.groupId}`);return}const o=n.details;n.details=t.details,this.log(`Audio track ${s} "${n.name}" lang:${n.lang} group:${i} loaded [${r.startSN}-${r.endSN}]`),s===this.trackId&&this.playlistLoaded(s,t,o)}onLevelLoading(e,t){this.switchLevel(t.level)}onLevelSwitching(e,t){this.switchLevel(t.level)}switchLevel(e){const t=this.hls.levels[e];if(!t)return;const s=t.audioGroups||null,i=this.groupIds;let r=this.currentTrack;if(!s||(i==null?void 0:i.length)!==(s==null?void 0:s.length)||s!=null&&s.some(o=>(i==null?void 0:i.indexOf(o))===-1)){this.groupIds=s,this.trackId=-1,this.currentTrack=null;const o=this.tracks.filter(d=>!s||s.indexOf(d.groupId)!==-1);if(o.length)this.selectDefaultTrack&&!o.some(d=>d.default)&&(this.selectDefaultTrack=!1),o.forEach((d,f)=>{d.id=f});else if(!r&&!this.tracksInGroup.length)return;this.tracksInGroup=o;const c=this.hls.config.audioPreference;if(!r&&c){const d=He(c,o,ft);if(d>-1)r=o[d];else{const f=He(c,this.tracks);r=this.tracks[f]}}let l=this.findTrackId(r);l===-1&&r&&(l=this.findTrackId(null));const h={audioTracks:o};this.log(`Updating audio tracks, ${o.length} track(s) found in group(s): ${s==null?void 0:s.join(",")}`),this.hls.trigger(m.AUDIO_TRACKS_UPDATED,h);const u=this.trackId;if(l!==-1&&u===-1)this.setAudioTrack(l);else if(o.length&&u===-1){var n;const d=new Error(`No audio track selected for current audio group-ID(s): ${(n=this.groupIds)==null?void 0:n.join(",")} track count: ${o.length}`);this.warn(d.message),this.hls.trigger(m.ERROR,{type:H.MEDIA_ERROR,details:R.AUDIO_TRACK_LOAD_ERROR,fatal:!0,error:d})}}}onError(e,t){t.fatal||!t.context||t.context.type===J.AUDIO_TRACK&&t.context.id===this.trackId&&(!this.groupIds||this.groupIds.indexOf(t.context.groupId)!==-1)&&this.checkRetry(t)}get allAudioTracks(){return this.tracks}get audioTracks(){return this.tracksInGroup}get audioTrack(){return this.trackId}set audioTrack(e){this.selectDefaultTrack=!1,this.setAudioTrack(e)}setAudioOption(e){const t=this.hls;if(t.config.audioPreference=e,e){const s=this.allAudioTracks;if(this.selectDefaultTrack=!1,s.length){const i=this.currentTrack;if(i&>(e,i,ft))return i;const r=He(e,this.tracksInGroup,ft);if(r>-1){const n=this.tracksInGroup[r];return this.setAudioTrack(r),n}else if(i){let n=t.loadLevel;n===-1&&(n=t.firstAutoLevel);const o=Zo(e,t.levels,s,n,ft);if(o===-1)return null;t.nextLoadLevel=o}if(e.channels||e.audioCodec){const n=He(e,s);if(n>-1)return s[n]}}}return null}setAudioTrack(e){const t=this.tracksInGroup;if(e<0||e>=t.length){this.warn(`Invalid audio track id: ${e}`);return}this.selectDefaultTrack=!1;const s=this.currentTrack,i=t[e],r=i.details&&!i.details.live;if(e===this.trackId&&i===s&&r||(this.log(`Switching to audio-track ${e} "${i.name}" lang:${i.lang} group:${i.groupId} channels:${i.channels}`),this.trackId=e,this.currentTrack=i,this.hls.trigger(m.AUDIO_TRACK_SWITCHING,ie({},i)),r))return;const n=this.switchParams(i.url,s==null?void 0:s.details,i.details);this.loadPlaylist(n)}findTrackId(e){const t=this.tracksInGroup;for(let s=0;s{const s={label:"async-blocker",execute:t,onStart:()=>{},onComplete:()=>{},onError:()=>{}};this.append(s,e)})}prependBlocker(e){return new Promise(t=>{if(this.queues){const s={label:"async-blocker-prepend",execute:t,onStart:()=>{},onComplete:()=>{},onError:()=>{}};this.queues[e].unshift(s)}})}removeBlockers(){this.queues!==null&&[this.queues.video,this.queues.audio,this.queues.audiovideo].forEach(e=>{var t;const s=(t=e[0])==null?void 0:t.label;(s==="async-blocker"||s==="async-blocker-prepend")&&(e[0].execute(),e.splice(0,1))})}unblockAudio(e){if(this.queues===null)return;this.queues.audio[0]===e&&this.shiftAndExecuteNext("audio")}executeNext(e){if(this.queues===null||this.tracks===null)return;const t=this.queues[e];if(t.length){const i=t[0];try{i.execute()}catch(r){var s;if(i.onError(r),this.queues===null||this.tracks===null)return;const n=(s=this.tracks[e])==null?void 0:s.buffer;n!=null&&n.updating||this.shiftAndExecuteNext(e)}}}shiftAndExecuteNext(e){this.queues!==null&&(this.queues[e].shift(),this.executeNext(e))}current(e){var t;return((t=this.queues)==null?void 0:t[e][0])||null}toString(){const{queues:e,tracks:t}=this;return e===null||t===null?"":` + initSegmentChange: ${b}`);const P=new Uc(s,i,t,o,h);this.configureTransmuxer(P)}if(this.frag=r,this.part=n,this.workerContext)this.workerContext.worker.postMessage({instanceNo:f,cmd:"demux",data:e,decryptdata:p,chunkMeta:l,state:I},e instanceof ArrayBuffer?[e]:[]);else if(g){const P=g.push(e,p,l,I);Yt(P)?P.then(M=>{this.handleTransmuxComplete(M)}).catch(M=>{this.transmuxerError(M,l,"transmuxer-interface push error")}):this.handleTransmuxComplete(P)}}flush(e){e.transmuxing.start=self.performance.now();const{instanceNo:t,transmuxer:s}=this;if(this.workerContext)this.workerContext.worker.postMessage({instanceNo:t,cmd:"flush",chunkMeta:e});else if(s){const i=s.flush(e);Yt(i)?i.then(r=>{this.handleFlushResult(r,e)}).catch(r=>{this.transmuxerError(r,e,"transmuxer-interface flush error")}):this.handleFlushResult(i,e)}}transmuxerError(e,t,s){this.hls&&(this.error=e,this.hls.trigger(m.ERROR,{type:Y.MEDIA_ERROR,details:R.FRAG_PARSING_ERROR,chunkMeta:t,frag:this.frag||void 0,part:this.part||void 0,fatal:!1,error:e,err:e,reason:s}))}handleFlushResult(e,t){e.forEach(s=>{this.handleTransmuxComplete(s)}),this.onFlush(t)}configureTransmuxer(e){const{instanceNo:t,transmuxer:s}=this;this.workerContext?this.workerContext.worker.postMessage({instanceNo:t,cmd:"configure",config:e}):s&&s.configure(e)}handleTransmuxComplete(e){e.chunkMeta.transmuxing.end=self.performance.now(),this.onTransmuxComplete(e)}}const Xr=100;class Kc extends Fi{constructor(e,t,s){super(e,t,s,"audio-stream-controller",U.AUDIO),this.mainAnchor=null,this.mainFragLoading=null,this.audioOnly=!1,this.bufferedTrack=null,this.switchingTrack=null,this.trackId=-1,this.waitingData=null,this.mainDetails=null,this.flushing=!1,this.bufferFlushed=!1,this.cachedTrackLoadedData=null,this.registerListeners()}onHandlerDestroying(){this.unregisterListeners(),super.onHandlerDestroying(),this.resetItem()}resetItem(){this.mainDetails=this.mainAnchor=this.mainFragLoading=this.bufferedTrack=this.switchingTrack=this.waitingData=this.cachedTrackLoadedData=null}registerListeners(){super.registerListeners();const{hls:e}=this;e.on(m.LEVEL_LOADED,this.onLevelLoaded,this),e.on(m.AUDIO_TRACKS_UPDATED,this.onAudioTracksUpdated,this),e.on(m.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),e.on(m.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),e.on(m.BUFFER_RESET,this.onBufferReset,this),e.on(m.BUFFER_CREATED,this.onBufferCreated,this),e.on(m.BUFFER_FLUSHING,this.onBufferFlushing,this),e.on(m.BUFFER_FLUSHED,this.onBufferFlushed,this),e.on(m.INIT_PTS_FOUND,this.onInitPtsFound,this),e.on(m.FRAG_LOADING,this.onFragLoading,this),e.on(m.FRAG_BUFFERED,this.onFragBuffered,this)}unregisterListeners(){const{hls:e}=this;e&&(super.unregisterListeners(),e.off(m.LEVEL_LOADED,this.onLevelLoaded,this),e.off(m.AUDIO_TRACKS_UPDATED,this.onAudioTracksUpdated,this),e.off(m.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),e.off(m.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),e.off(m.BUFFER_RESET,this.onBufferReset,this),e.off(m.BUFFER_CREATED,this.onBufferCreated,this),e.off(m.BUFFER_FLUSHING,this.onBufferFlushing,this),e.off(m.BUFFER_FLUSHED,this.onBufferFlushed,this),e.off(m.INIT_PTS_FOUND,this.onInitPtsFound,this),e.off(m.FRAG_LOADING,this.onFragLoading,this),e.off(m.FRAG_BUFFERED,this.onFragBuffered,this))}onInitPtsFound(e,{frag:t,id:s,initPTS:i,timescale:r,trackId:n}){if(s===U.MAIN){const o=t.cc,c=this.fragCurrent;if(this.initPTS[o]={baseTime:i,timescale:r,trackId:n},this.log(`InitPTS for cc: ${o} found from main: ${i/r} (${i}/${r}) trackId: ${n}`),this.mainAnchor=t,this.state===C.WAITING_INIT_PTS){const l=this.waitingData;(!l&&!this.loadingParts||l&&l.frag.cc!==o)&&this.syncWithAnchor(t,l==null?void 0:l.frag)}else!this.hls.hasEnoughToStart&&c&&c.cc!==o?(c.abortRequests(),this.syncWithAnchor(t,c)):this.state===C.IDLE&&this.tick()}}getLoadPosition(){return!this.startFragRequested&&this.nextLoadPosition>=0?this.nextLoadPosition:super.getLoadPosition()}syncWithAnchor(e,t){var s;const i=((s=this.mainFragLoading)==null?void 0:s.frag)||null;if(t&&(i==null?void 0:i.cc)===t.cc)return;const r=(i||e).cc,n=this.getLevelDetails(),o=this.getLoadPosition(),c=Un(n,r,o);c&&(this.log(`Syncing with main frag at ${c.start} cc ${c.cc}`),this.startFragRequested=!1,this.nextLoadPosition=c.start,this.resetLoadingState(),this.state===C.IDLE&&this.doTickIdle())}startLoad(e,t){if(!this.levels){this.startPosition=e,this.state=C.STOPPED;return}const s=this.lastCurrentTime;this.stopLoad(),this.setInterval(Xr),s>0&&e===-1?(this.log(`Override startPosition with lastCurrentTime @${s.toFixed(3)}`),e=s,this.state=C.IDLE):this.state=C.WAITING_TRACK,this.nextLoadPosition=this.lastCurrentTime=e+this.timelineOffset,this.startPosition=t?-1:e,this.tick()}doTick(){switch(this.state){case C.IDLE:this.doTickIdle();break;case C.WAITING_TRACK:{const{levels:e,trackId:t}=this,s=e==null?void 0:e[t],i=s==null?void 0:s.details;if(i&&!this.waitForLive(s)){if(this.waitForCdnTuneIn(i))break;this.state=C.WAITING_INIT_PTS}break}case C.FRAG_LOADING_WAITING_RETRY:{this.checkRetryDate();break}case C.WAITING_INIT_PTS:{const e=this.waitingData;if(e){const{frag:t,part:s,cache:i,complete:r}=e,n=this.mainAnchor;if(this.initPTS[t.cc]!==void 0){this.waitingData=null,this.state=C.FRAG_LOADING;const o=i.flush().buffer,c={frag:t,part:s,payload:o,networkDetails:null};this._handleFragmentLoadProgress(c),r&&super._handleFragmentLoadComplete(c)}else n&&n.cc!==e.frag.cc&&this.syncWithAnchor(n,e.frag)}else this.state=C.IDLE}}this.onTickEnd()}resetLoadingState(){const e=this.waitingData;e&&(this.fragmentTracker.removeFragment(e.frag),this.waitingData=null),super.resetLoadingState()}onTickEnd(){const{media:e}=this;e!=null&&e.readyState&&(this.lastCurrentTime=e.currentTime)}doTickIdle(){var e;const{hls:t,levels:s,media:i,trackId:r}=this,n=t.config;if(!this.buffering||!i&&!this.primaryPrefetch&&(this.startFragRequested||!n.startFragPrefetch)||!(s!=null&&s[r]))return;const o=s[r],c=o.details;if(!c||this.waitForLive(o)||this.waitForCdnTuneIn(c)){this.state=C.WAITING_TRACK,this.startFragRequested=!1;return}const l=this.mediaBuffer?this.mediaBuffer:this.media;this.bufferFlushed&&l&&(this.bufferFlushed=!1,this.afterBufferFlushed(l,ae.AUDIO,U.AUDIO));const h=this.getFwdBufferInfo(l,U.AUDIO);if(h===null)return;if(!this.switchingTrack&&this._streamEnded(h,c)){t.trigger(m.BUFFER_EOS,{type:"audio"}),this.state=C.ENDED;return}const d=h.len,u=t.maxBufferLength,f=c.fragments,g=f[0].start,y=this.getLoadPosition(),p=this.flushing?y:h.end;if(this.switchingTrack&&i){const S=y;c.PTSKnown&&Sg||h.nextStart)&&(this.log("Alt audio track ahead of main track, seek to start of alt audio track"),i.currentTime=g+.05)}if(d>=u&&!this.switchingTrack&&pT.end){const v=this.fragmentTracker.getFragAtPos(p,U.MAIN);v&&v.end>T.end&&(T=v,this.mainFragLoading={frag:v,targetBufferTime:null})}if(E.start>T.end)return}this.loadFragment(E,o,p)}onMediaDetaching(e,t){this.bufferFlushed=this.flushing=!1,super.onMediaDetaching(e,t)}onAudioTracksUpdated(e,{audioTracks:t}){this.resetTransmuxer(),this.levels=t.map(s=>new Kt(s))}onAudioTrackSwitching(e,t){const s=!!t.url;this.trackId=t.id;const{fragCurrent:i}=this;i&&(i.abortRequests(),this.removeUnbufferedFrags(i.start)),this.resetLoadingState(),s?(this.switchingTrack=t,this.flushAudioIfNeeded(t),this.state!==C.STOPPED&&(this.setInterval(Xr),this.state=C.IDLE,this.tick())):(this.resetTransmuxer(),this.switchingTrack=null,this.bufferedTrack=t,this.clearInterval())}onManifestLoading(){super.onManifestLoading(),this.bufferFlushed=this.flushing=this.audioOnly=!1,this.resetItem(),this.trackId=-1}onLevelLoaded(e,t){this.mainDetails=t.details;const s=this.cachedTrackLoadedData;s&&(this.cachedTrackLoadedData=null,this.onAudioTrackLoaded(m.AUDIO_TRACK_LOADED,s))}onAudioTrackLoaded(e,t){var s;const{levels:i}=this,{details:r,id:n,groupId:o,track:c}=t;if(!i){this.warn(`Audio tracks reset while loading track ${n} "${c.name}" of "${o}"`);return}const l=this.mainDetails;if(!l||r.endCC>l.endCC||l.expired){this.cachedTrackLoadedData=t,this.state!==C.STOPPED&&(this.state=C.WAITING_TRACK);return}this.cachedTrackLoadedData=null,this.log(`Audio track ${n} "${c.name}" of "${o}" loaded [${r.startSN},${r.endSN}]${r.lastPartSn?`[part-${r.lastPartSn}-${r.lastPartIndex}]`:""},duration:${r.totalduration}`);const h=i[n];let d=0;if(r.live||(s=h.details)!=null&&s.live){if(this.checkLiveUpdate(r),r.deltaUpdateFailed)return;if(h.details){var u;d=this.alignPlaylists(r,h.details,(u=this.levelLastLoaded)==null?void 0:u.details)}r.alignedSliding||(ra(r,l),r.alignedSliding||Rs(r,l),d=r.fragmentStart)}h.details=r,this.levelLastLoaded=h,this.startFragRequested||this.setStartPosition(l,d),this.hls.trigger(m.AUDIO_TRACK_UPDATED,{details:r,id:n,groupId:t.groupId}),this.state===C.WAITING_TRACK&&!this.waitForCdnTuneIn(r)&&(this.state=C.IDLE),this.tick()}_handleFragmentLoadProgress(e){var t;const s=e.frag,{part:i,payload:r}=e,{config:n,trackId:o,levels:c}=this;if(!c){this.warn(`Audio tracks were reset while fragment load was in progress. Fragment ${s.sn} of level ${s.level} will not be buffered`);return}const l=c[o];if(!l){this.warn("Audio track is undefined on fragment load progress");return}const h=l.details;if(!h){this.warn("Audio track details undefined on fragment load progress"),this.removeUnbufferedFrags(s.start);return}const d=n.defaultAudioCodec||l.audioCodec||"mp4a.40.2";let u=this.transmuxer;u||(u=this.transmuxer=new Ia(this.hls,U.AUDIO,this._handleTransmuxComplete.bind(this),this._handleTransmuxerFlush.bind(this)));const f=this.initPTS[s.cc],g=(t=s.initSegment)==null?void 0:t.data;if(f!==void 0){const p=i?i.index:-1,E=p!==-1,T=new ki(s.level,s.sn,s.stats.chunkCount,r.byteLength,p,E);u.push(r,g,d,"",s,i,h.totalduration,!1,T,f)}else{this.log(`Unknown video PTS for cc ${s.cc}, waiting for video PTS before demuxing audio frag ${s.sn} of [${h.startSN} ,${h.endSN}],track ${o}`);const{cache:y}=this.waitingData=this.waitingData||{frag:s,part:i,cache:new na,complete:!1};y.push(new Uint8Array(r)),this.state!==C.STOPPED&&(this.state=C.WAITING_INIT_PTS)}}_handleFragmentLoadComplete(e){if(this.waitingData){this.waitingData.complete=!0;return}super._handleFragmentLoadComplete(e)}onBufferReset(){this.mediaBuffer=null}onBufferCreated(e,t){this.bufferFlushed=this.flushing=!1;const s=t.tracks.audio;s&&(this.mediaBuffer=s.buffer||null)}onFragLoading(e,t){!this.audioOnly&&t.frag.type===U.MAIN&&de(t.frag)&&(this.mainFragLoading=t,this.state===C.IDLE&&this.tick())}onFragBuffered(e,t){const{frag:s,part:i}=t;if(s.type!==U.AUDIO){!this.audioOnly&&s.type===U.MAIN&&!s.elementaryStreams.video&&!s.elementaryStreams.audiovideo&&(this.audioOnly=!0,this.mainFragLoading=null);return}if(this.fragContextChanged(s)){this.warn(`Fragment ${s.sn}${i?" p: "+i.index:""} of level ${s.level} finished buffering, but was aborted. state: ${this.state}, audioSwitch: ${this.switchingTrack?this.switchingTrack.name:"false"}`);return}if(de(s)){this.fragPrevious=s;const r=this.switchingTrack;r&&(this.bufferedTrack=r,this.switchingTrack=null,this.hls.trigger(m.AUDIO_TRACK_SWITCHED,ie({},r)))}this.fragBufferedComplete(s,i),this.media&&this.tick()}onError(e,t){var s;if(t.fatal){this.state=C.ERROR;return}switch(t.details){case R.FRAG_GAP:case R.FRAG_PARSING_ERROR:case R.FRAG_DECRYPT_ERROR:case R.FRAG_LOAD_ERROR:case R.FRAG_LOAD_TIMEOUT:case R.KEY_LOAD_ERROR:case R.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(U.AUDIO,t);break;case R.AUDIO_TRACK_LOAD_ERROR:case R.AUDIO_TRACK_LOAD_TIMEOUT:case R.LEVEL_PARSING_ERROR:!t.levelRetry&&this.state===C.WAITING_TRACK&&((s=t.context)==null?void 0:s.type)===J.AUDIO_TRACK&&(this.state=C.IDLE);break;case R.BUFFER_ADD_CODEC_ERROR:case R.BUFFER_APPEND_ERROR:if(t.parent!=="audio")return;this.reduceLengthAndFlushBuffer(t)||this.resetLoadingState();break;case R.BUFFER_FULL_ERROR:if(t.parent!=="audio")return;this.reduceLengthAndFlushBuffer(t)&&(this.bufferedTrack=null,super.flushMainBuffer(0,Number.POSITIVE_INFINITY,"audio"));break;case R.INTERNAL_EXCEPTION:this.recoverWorkerError(t);break}}onBufferFlushing(e,{type:t}){t!==ae.VIDEO&&(this.flushing=!0)}onBufferFlushed(e,{type:t}){if(t!==ae.VIDEO){this.flushing=!1,this.bufferFlushed=!0,this.state===C.ENDED&&(this.state=C.IDLE);const s=this.mediaBuffer||this.media;s&&(this.afterBufferFlushed(s,t,U.AUDIO),this.tick())}}_handleTransmuxComplete(e){var t;const s="audio",{hls:i}=this,{remuxResult:r,chunkMeta:n}=e,o=this.getCurrentContext(n);if(!o){this.resetWhenMissingContext(n);return}const{frag:c,part:l,level:h}=o,{details:d}=h,{audio:u,text:f,id3:g,initSegment:y}=r;if(this.fragContextChanged(c)||!d){this.fragmentTracker.removeFragment(c);return}if(this.state=C.PARSING,this.switchingTrack&&u&&this.completeAudioSwitch(this.switchingTrack),y!=null&&y.tracks){const p=c.initSegment||c;if(this.unhandledEncryptionError(y,c))return;this._bufferInitSegment(h,y.tracks,p,n),i.trigger(m.FRAG_PARSING_INIT_SEGMENT,{frag:p,id:s,tracks:y.tracks})}if(u){const{startPTS:p,endPTS:E,startDTS:T,endDTS:S}=u;l&&(l.elementaryStreams[ae.AUDIO]={startPTS:p,endPTS:E,startDTS:T,endDTS:S}),c.setElementaryStreamInfo(ae.AUDIO,p,E,T,S),this.bufferFragmentData(u,c,l,n)}if(g!=null&&(t=g.samples)!=null&&t.length){const p=ne({id:s,frag:c,details:d},g);i.trigger(m.FRAG_PARSING_METADATA,p)}if(f){const p=ne({id:s,frag:c,details:d},f);i.trigger(m.FRAG_PARSING_USERDATA,p)}}_bufferInitSegment(e,t,s,i){if(this.state!==C.PARSING||(t.video&&delete t.video,t.audiovideo&&delete t.audiovideo,!t.audio))return;const r=t.audio;r.id=U.AUDIO;const n=e.audioCodec;this.log(`Init audio buffer, container:${r.container}, codecs[level/parsed]=[${n}/${r.codec}]`),n&&n.split(",").length===1&&(r.levelCodec=n),this.hls.trigger(m.BUFFER_CODECS,t);const o=r.initSegment;if(o!=null&&o.byteLength){const c={type:"audio",frag:s,part:null,chunkMeta:i,parent:s.type,data:o};this.hls.trigger(m.BUFFER_APPENDING,c)}this.tickImmediate()}loadFragment(e,t,s){const i=this.fragmentTracker.getState(e);if(this.switchingTrack||i===fe.NOT_LOADED||i===fe.PARTIAL){var r;if(!de(e))this._loadInitSegment(e,t);else if((r=t.details)!=null&&r.live&&!this.initPTS[e.cc]){this.log(`Waiting for video PTS in continuity counter ${e.cc} of live stream before loading audio fragment ${e.sn} of level ${this.trackId}`),this.state=C.WAITING_INIT_PTS;const n=this.mainDetails;n&&n.fragmentStart!==t.details.fragmentStart&&Rs(t.details,n)}else super.loadFragment(e,t,s)}else this.clearTrackerIfNeeded(e)}flushAudioIfNeeded(e){if(this.media&&this.bufferedTrack){const{name:t,lang:s,assocLang:i,characteristics:r,audioCodec:n,channels:o}=this.bufferedTrack;gt({name:t,lang:s,assocLang:i,characteristics:r,audioCodec:n,channels:o},e,ft)||(vs(e.url,this.hls)?(this.log("Switching audio track : flushing all audio"),super.flushMainBuffer(0,Number.POSITIVE_INFINITY,"audio"),this.bufferedTrack=null):this.bufferedTrack=e)}}completeAudioSwitch(e){const{hls:t}=this;this.flushAudioIfNeeded(e),this.bufferedTrack=e,this.switchingTrack=null,t.trigger(m.AUDIO_TRACK_SWITCHED,ie({},e))}}class Vi extends Oe{constructor(e,t){super(t,e.logger),this.hls=void 0,this.canLoad=!1,this.timer=-1,this.hls=e}destroy(){this.clearTimer(),this.hls=this.log=this.warn=null}clearTimer(){this.timer!==-1&&(self.clearTimeout(this.timer),this.timer=-1)}startLoad(){this.canLoad=!0,this.loadPlaylist()}stopLoad(){this.canLoad=!1,this.clearTimer()}switchParams(e,t,s){const i=t==null?void 0:t.renditionReports;if(i){let r=-1;for(let n=0;n=0&&h>t.partTarget&&(c+=1)}const l=s&&hr(s);return new dr(o,c>=0?c:void 0,l)}}}loadPlaylist(e){this.clearTimer()}loadingPlaylist(e,t){this.clearTimer()}shouldLoadPlaylist(e){return this.canLoad&&!!e&&!!e.url&&(!e.details||e.details.live)}getUrlWithDirectives(e,t){if(t)try{return t.addDirectives(e)}catch(s){this.warn(`Could not construct new URL with HLS Delivery Directives: ${s}`)}return e}playlistLoaded(e,t,s){const{details:i,stats:r}=t,n=self.performance.now(),o=r.loading.first?Math.max(0,n-r.loading.first):0;i.advancedDateTime=Date.now()-o;const c=this.hls.config.timelineOffset;if(c!==i.appliedTimelineOffset){const h=Math.max(c||0,0);i.appliedTimelineOffset=h,i.fragments.forEach(d=>{d.setStart(d.playlistOffset+h)})}if(i.live||s!=null&&s.live){const h="levelInfo"in t?t.levelInfo:t.track;if(i.reloaded(s),s&&i.fragments.length>0){Ol(s,i,this);const T=i.playlistParsingError;if(T){this.warn(T);const S=this.hls;if(!S.config.ignorePlaylistParsingErrors){var l;const{networkDetails:v}=t;S.trigger(m.ERROR,{type:Y.NETWORK_ERROR,details:R.LEVEL_PARSING_ERROR,fatal:!1,url:i.url,error:T,reason:T.message,level:t.level||void 0,parent:(l=i.fragments[0])==null?void 0:l.type,networkDetails:v,stats:r});return}i.playlistParsingError=null}}i.requestScheduled===-1&&(i.requestScheduled=r.loading.start);const d=this.hls.mainForwardBufferInfo,u=d?d.end-d.len:0,f=(i.edge-u)*1e3,g=Jn(i,f);if(i.requestScheduled+g0){if(b>i.targetduration*3)this.log(`Playlist last advanced ${_.toFixed(2)}s ago. Omitting segment and part directives.`),p=void 0,E=void 0;else if(s!=null&&s.tuneInGoal&&b-i.partTarget>s.tuneInGoal)this.warn(`CDN Tune-in goal increased from: ${s.tuneInGoal} to: ${I} with playlist age: ${i.age}`),I=0;else{const P=Math.floor(I/i.targetduration);if(p+=P,E!==void 0){const M=Math.round(I%i.targetduration/i.partTarget);E+=M}this.log(`CDN Tune-in age: ${i.ageHeader}s last advanced ${_.toFixed(2)}s goal: ${I} skip sn ${P} to part ${E}`)}i.tuneInGoal=I}if(y=this.getDeliveryDirectives(i,t.deliveryDirectives,p,E),T||!A){i.requestScheduled=n,this.loadingPlaylist(h,y);return}}else(i.canBlockReload||i.canSkipUntil)&&(y=this.getDeliveryDirectives(i,t.deliveryDirectives,p,E));y&&p!==void 0&&i.canBlockReload&&(i.requestScheduled=r.loading.first+Math.max(g-o*2,g/2)),this.scheduleLoading(h,y,i)}else this.clearTimer()}scheduleLoading(e,t,s){const i=s||e.details;if(!i){this.loadingPlaylist(e,t);return}const r=self.performance.now(),n=i.requestScheduled;if(r>=n){this.loadingPlaylist(e,t);return}const o=n-r;this.log(`reload live playlist ${e.name||e.bitrate+"bps"} in ${Math.round(o)} ms`),this.clearTimer(),this.timer=self.setTimeout(()=>this.loadingPlaylist(e,t),o)}getDeliveryDirectives(e,t,s,i){let r=hr(e);return t!=null&&t.skip&&e.deltaUpdateFailed&&(s=t.msn,i=t.part,r=ds.No),new dr(s,i,r)}checkRetry(e){const t=e.details,s=xs(e),i=e.errorAction,{action:r,retryCount:n=0,retryConfig:o}=i||{},c=!!i&&!!o&&(r===pe.RetryRequest||!i.resolved&&r===pe.SendAlternateToPenaltyBox);if(c){var l;if(n>=o.maxNumRetry)return!1;if(s&&(l=e.context)!=null&&l.deliveryDirectives)this.warn(`Retrying playlist loading ${n+1}/${o.maxNumRetry} after "${t}" without delivery-directives`),this.loadPlaylist();else{const h=Ci(o,n);this.clearTimer(),this.timer=self.setTimeout(()=>this.loadPlaylist(),h),this.warn(`Retrying playlist loading ${n+1}/${o.maxNumRetry} after "${t}" in ${h}ms`)}e.levelRetry=!0,i.resolved=!0}return c}}function La(a,e){if(a.length!==e.length)return!1;for(let t=0;ta[i]!==e[i])}function mi(a,e){return e.label.toLowerCase()===a.name.toLowerCase()&&(!e.language||e.language.toLowerCase()===(a.lang||"").toLowerCase())}class Vc extends Vi{constructor(e){super(e,"audio-track-controller"),this.tracks=[],this.groupIds=null,this.tracksInGroup=[],this.trackId=-1,this.currentTrack=null,this.selectDefaultTrack=!0,this.registerListeners()}registerListeners(){const{hls:e}=this;e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.MANIFEST_PARSED,this.onManifestParsed,this),e.on(m.LEVEL_LOADING,this.onLevelLoading,this),e.on(m.LEVEL_SWITCHING,this.onLevelSwitching,this),e.on(m.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),e.on(m.ERROR,this.onError,this)}unregisterListeners(){const{hls:e}=this;e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.MANIFEST_PARSED,this.onManifestParsed,this),e.off(m.LEVEL_LOADING,this.onLevelLoading,this),e.off(m.LEVEL_SWITCHING,this.onLevelSwitching,this),e.off(m.AUDIO_TRACK_LOADED,this.onAudioTrackLoaded,this),e.off(m.ERROR,this.onError,this)}destroy(){this.unregisterListeners(),this.tracks.length=0,this.tracksInGroup.length=0,this.currentTrack=null,super.destroy()}onManifestLoading(){this.tracks=[],this.tracksInGroup=[],this.groupIds=null,this.currentTrack=null,this.trackId=-1,this.selectDefaultTrack=!0}onManifestParsed(e,t){this.tracks=t.audioTracks||[]}onAudioTrackLoaded(e,t){const{id:s,groupId:i,details:r}=t,n=this.tracksInGroup[s];if(!n||n.groupId!==i){this.warn(`Audio track with id:${s} and group:${i} not found in active group ${n==null?void 0:n.groupId}`);return}const o=n.details;n.details=t.details,this.log(`Audio track ${s} "${n.name}" lang:${n.lang} group:${i} loaded [${r.startSN}-${r.endSN}]`),s===this.trackId&&this.playlistLoaded(s,t,o)}onLevelLoading(e,t){this.switchLevel(t.level)}onLevelSwitching(e,t){this.switchLevel(t.level)}switchLevel(e){const t=this.hls.levels[e];if(!t)return;const s=t.audioGroups||null,i=this.groupIds;let r=this.currentTrack;if(!s||(i==null?void 0:i.length)!==(s==null?void 0:s.length)||s!=null&&s.some(o=>(i==null?void 0:i.indexOf(o))===-1)){this.groupIds=s,this.trackId=-1,this.currentTrack=null;const o=this.tracks.filter(u=>!s||s.indexOf(u.groupId)!==-1);if(o.length)this.selectDefaultTrack&&!o.some(u=>u.default)&&(this.selectDefaultTrack=!1),o.forEach((u,f)=>{u.id=f});else if(!r&&!this.tracksInGroup.length)return;this.tracksInGroup=o;const c=this.hls.config.audioPreference;if(!r&&c){const u=We(c,o,ft);if(u>-1)r=o[u];else{const f=We(c,this.tracks);r=this.tracks[f]}}let l=this.findTrackId(r);l===-1&&r&&(l=this.findTrackId(null));const h={audioTracks:o};this.log(`Updating audio tracks, ${o.length} track(s) found in group(s): ${s==null?void 0:s.join(",")}`),this.hls.trigger(m.AUDIO_TRACKS_UPDATED,h);const d=this.trackId;if(l!==-1&&d===-1)this.setAudioTrack(l);else if(o.length&&d===-1){var n;const u=new Error(`No audio track selected for current audio group-ID(s): ${(n=this.groupIds)==null?void 0:n.join(",")} track count: ${o.length}`);this.warn(u.message),this.hls.trigger(m.ERROR,{type:Y.MEDIA_ERROR,details:R.AUDIO_TRACK_LOAD_ERROR,fatal:!0,error:u})}}}onError(e,t){t.fatal||!t.context||t.context.type===J.AUDIO_TRACK&&t.context.id===this.trackId&&(!this.groupIds||this.groupIds.indexOf(t.context.groupId)!==-1)&&this.checkRetry(t)}get allAudioTracks(){return this.tracks}get audioTracks(){return this.tracksInGroup}get audioTrack(){return this.trackId}set audioTrack(e){this.selectDefaultTrack=!1,this.setAudioTrack(e)}setAudioOption(e){const t=this.hls;if(t.config.audioPreference=e,e){const s=this.allAudioTracks;if(this.selectDefaultTrack=!1,s.length){const i=this.currentTrack;if(i&>(e,i,ft))return i;const r=We(e,this.tracksInGroup,ft);if(r>-1){const n=this.tracksInGroup[r];return this.setAudioTrack(r),n}else if(i){let n=t.loadLevel;n===-1&&(n=t.firstAutoLevel);const o=tl(e,t.levels,s,n,ft);if(o===-1)return null;t.nextLoadLevel=o}if(e.channels||e.audioCodec){const n=We(e,s);if(n>-1)return s[n]}}}return null}setAudioTrack(e){const t=this.tracksInGroup;if(e<0||e>=t.length){this.warn(`Invalid audio track id: ${e}`);return}this.selectDefaultTrack=!1;const s=this.currentTrack,i=t[e],r=i.details&&!i.details.live;if(e===this.trackId&&i===s&&r||(this.log(`Switching to audio-track ${e} "${i.name}" lang:${i.lang} group:${i.groupId} channels:${i.channels}`),this.trackId=e,this.currentTrack=i,this.hls.trigger(m.AUDIO_TRACK_SWITCHING,ie({},i)),r))return;const n=this.switchParams(i.url,s==null?void 0:s.details,i.details);this.loadPlaylist(n)}findTrackId(e){const t=this.tracksInGroup;for(let s=0;s{const s={label:"async-blocker",execute:t,onStart:()=>{},onComplete:()=>{},onError:()=>{}};this.append(s,e)})}prependBlocker(e){return new Promise(t=>{if(this.queues){const s={label:"async-blocker-prepend",execute:t,onStart:()=>{},onComplete:()=>{},onError:()=>{}};this.queues[e].unshift(s)}})}removeBlockers(){this.queues!==null&&[this.queues.video,this.queues.audio,this.queues.audiovideo].forEach(e=>{var t;const s=(t=e[0])==null?void 0:t.label;(s==="async-blocker"||s==="async-blocker-prepend")&&(e[0].execute(),e.splice(0,1))})}unblockAudio(e){if(this.queues===null)return;this.queues.audio[0]===e&&this.shiftAndExecuteNext("audio")}executeNext(e){if(this.queues===null||this.tracks===null)return;const t=this.queues[e];if(t.length){const i=t[0];try{i.execute()}catch(r){var s;if(i.onError(r),this.queues===null||this.tracks===null)return;const n=(s=this.tracks[e])==null?void 0:s.buffer;n!=null&&n.updating||this.shiftAndExecuteNext(e)}}}shiftAndExecuteNext(e){this.queues!==null&&(this.queues[e].shift(),this.executeNext(e))}current(e){var t;return((t=this.queues)==null?void 0:t[e][0])||null}toString(){const{queues:e,tracks:t}=this;return e===null||t===null?"":` ${this.list("video")} ${this.list("audio")} -${this.list("audiovideo")}}`}list(e){var t,s;return(t=this.queues)!=null&&t[e]||(s=this.tracks)!=null&&s[e]?`${e}: (${this.listSbInfo(e)}) ${this.listOps(e)}`:""}listSbInfo(e){var t;const s=(t=this.tracks)==null?void 0:t[e],i=s==null?void 0:s.buffer;return i?`SourceBuffer${i.updating?" updating":""}${s.ended?" ended":""}${s.ending?" ending":""}`:"none"}listOps(e){var t;return((t=this.queues)==null?void 0:t[e].map(s=>s.label).join(", "))||""}}const jr=/(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/,Aa="HlsJsTrackRemovedError";class Kc extends Error{constructor(e){super(e),this.name=Aa}}class Vc extends we{constructor(e,t){super("buffer-controller",e.logger),this.hls=void 0,this.fragmentTracker=void 0,this.details=null,this._objectUrl=null,this.operationQueue=null,this.bufferCodecEventsTotal=0,this.media=null,this.mediaSource=null,this.lastMpegAudioChunk=null,this.blockedAudioAppend=null,this.lastVideoAppendEnd=0,this.appendSource=void 0,this.transferData=void 0,this.overrides=void 0,this.appendErrors={audio:0,video:0,audiovideo:0},this.tracks={},this.sourceBuffers=[[null,null],[null,null]],this._onEndStreaming=s=>{var i;this.hls&&((i=this.mediaSource)==null?void 0:i.readyState)==="open"&&this.hls.pauseBuffering()},this._onStartStreaming=s=>{this.hls&&this.hls.resumeBuffering()},this._onMediaSourceOpen=s=>{const{media:i,mediaSource:r}=this;s&&this.log("Media source opened"),!(!i||!r)&&(r.removeEventListener("sourceopen",this._onMediaSourceOpen),i.removeEventListener("emptied",this._onMediaEmptied),this.updateDuration(),this.hls.trigger(m.MEDIA_ATTACHED,{media:i,mediaSource:r}),this.mediaSource!==null&&this.checkPendingTracks())},this._onMediaSourceClose=()=>{this.log("Media source closed")},this._onMediaSourceEnded=()=>{this.log("Media source ended")},this._onMediaEmptied=()=>{const{mediaSrc:s,_objectUrl:i}=this;s!==i&&this.error(`Media element src was set while attaching MediaSource (${i} > ${s})`)},this.hls=e,this.fragmentTracker=t,this.appendSource=ho(nt(e.config.preferManagedMediaSource)),this.initTracks(),this.registerListeners()}hasSourceTypes(){return Object.keys(this.tracks).length>0}destroy(){this.unregisterListeners(),this.details=null,this.lastMpegAudioChunk=this.blockedAudioAppend=null,this.transferData=this.overrides=void 0,this.operationQueue&&(this.operationQueue.destroy(),this.operationQueue=null),this.hls=this.fragmentTracker=null,this._onMediaSourceOpen=this._onMediaSourceClose=null,this._onMediaSourceEnded=null,this._onStartStreaming=this._onEndStreaming=null}registerListeners(){const{hls:e}=this;e.on(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.MANIFEST_PARSED,this.onManifestParsed,this),e.on(m.BUFFER_RESET,this.onBufferReset,this),e.on(m.BUFFER_APPENDING,this.onBufferAppending,this),e.on(m.BUFFER_CODECS,this.onBufferCodecs,this),e.on(m.BUFFER_EOS,this.onBufferEos,this),e.on(m.BUFFER_FLUSHING,this.onBufferFlushing,this),e.on(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(m.FRAG_PARSED,this.onFragParsed,this),e.on(m.FRAG_CHANGED,this.onFragChanged,this),e.on(m.ERROR,this.onError,this)}unregisterListeners(){const{hls:e}=this;e.off(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.MANIFEST_PARSED,this.onManifestParsed,this),e.off(m.BUFFER_RESET,this.onBufferReset,this),e.off(m.BUFFER_APPENDING,this.onBufferAppending,this),e.off(m.BUFFER_CODECS,this.onBufferCodecs,this),e.off(m.BUFFER_EOS,this.onBufferEos,this),e.off(m.BUFFER_FLUSHING,this.onBufferFlushing,this),e.off(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(m.FRAG_PARSED,this.onFragParsed,this),e.off(m.FRAG_CHANGED,this.onFragChanged,this),e.off(m.ERROR,this.onError,this)}transferMedia(){const{media:e,mediaSource:t}=this;if(!e)return null;const s={};if(this.operationQueue){const r=this.isUpdating();r||this.operationQueue.removeBlockers();const n=this.isQueued();(r||n)&&this.warn(`Transfering MediaSource with${n?" operations in queue":""}${r?" updating SourceBuffer(s)":""} ${this.operationQueue}`),this.operationQueue.destroy()}const i=this.transferData;return!this.sourceBufferCount&&i&&i.mediaSource===t?ne(s,i.tracks):this.sourceBuffers.forEach(r=>{const[n]=r;n&&(s[n]=ne({},this.tracks[n]),this.removeBuffer(n)),r[0]=r[1]=null}),{media:e,mediaSource:t,tracks:s}}initTracks(){const e={};this.sourceBuffers=[[null,null],[null,null]],this.tracks=e,this.resetQueue(),this.resetAppendErrors(),this.lastMpegAudioChunk=this.blockedAudioAppend=null,this.lastVideoAppendEnd=0}onManifestLoading(){this.bufferCodecEventsTotal=0,this.details=null}onManifestParsed(e,t){var s;let i=2;(t.audio&&!t.video||!t.altAudio)&&(i=1),this.bufferCodecEventsTotal=i,this.log(`${i} bufferCodec event(s) expected.`),(s=this.transferData)!=null&&s.mediaSource&&this.sourceBufferCount&&i&&this.bufferCreated()}onMediaAttaching(e,t){const s=this.media=t.media;this.transferData=this.overrides=void 0;const i=nt(this.appendSource);if(i){const r=!!t.mediaSource;(r||t.overrides)&&(this.transferData=t,this.overrides=t.overrides);const n=this.mediaSource=t.mediaSource||new i;if(this.assignMediaSource(n),r)this._objectUrl=s.src,this.attachTransferred();else{const o=this._objectUrl=self.URL.createObjectURL(n);if(this.appendSource)try{s.removeAttribute("src");const c=self.ManagedMediaSource;s.disableRemotePlayback=s.disableRemotePlayback||c&&n instanceof c,Xr(s),Hc(s,o),s.load()}catch{s.src=o}else s.src=o}s.addEventListener("emptied",this._onMediaEmptied)}}assignMediaSource(e){var t,s;this.log(`${((t=this.transferData)==null?void 0:t.mediaSource)===e?"transferred":"created"} media source: ${(s=e.constructor)==null?void 0:s.name}`),e.addEventListener("sourceopen",this._onMediaSourceOpen),e.addEventListener("sourceended",this._onMediaSourceEnded),e.addEventListener("sourceclose",this._onMediaSourceClose),this.appendSource&&(e.addEventListener("startstreaming",this._onStartStreaming),e.addEventListener("endstreaming",this._onEndStreaming))}attachTransferred(){const e=this.media,t=this.transferData;if(!t||!e)return;const s=this.tracks,i=t.tracks,r=i?Object.keys(i):null,n=r?r.length:0,o=()=>{Promise.resolve().then(()=>{this.media&&this.mediaSourceOpenOrEnded&&this._onMediaSourceOpen()})};if(i&&r&&n){if(!this.tracksReady){this.hls.config.startFragPrefetch=!0,this.log("attachTransferred: waiting for SourceBuffer track info");return}if(this.log(`attachTransferred: (bufferCodecEventsTotal ${this.bufferCodecEventsTotal}) +${this.list("audiovideo")}}`}list(e){var t,s;return(t=this.queues)!=null&&t[e]||(s=this.tracks)!=null&&s[e]?`${e}: (${this.listSbInfo(e)}) ${this.listOps(e)}`:""}listSbInfo(e){var t;const s=(t=this.tracks)==null?void 0:t[e],i=s==null?void 0:s.buffer;return i?`SourceBuffer${i.updating?" updating":""}${s.ended?" ended":""}${s.ending?" ending":""}`:"none"}listOps(e){var t;return((t=this.queues)==null?void 0:t[e].map(s=>s.label).join(", "))||""}}const zr=/(avc[1234]|hvc1|hev1|dvh[1e]|vp09|av01)(?:\.[^.,]+)+/,Ra="HlsJsTrackRemovedError";class Yc extends Error{constructor(e){super(e),this.name=Ra}}class Wc extends Oe{constructor(e,t){super("buffer-controller",e.logger),this.hls=void 0,this.fragmentTracker=void 0,this.details=null,this._objectUrl=null,this.operationQueue=null,this.bufferCodecEventsTotal=0,this.media=null,this.mediaSource=null,this.lastMpegAudioChunk=null,this.blockedAudioAppend=null,this.lastVideoAppendEnd=0,this.appendSource=void 0,this.transferData=void 0,this.overrides=void 0,this.appendErrors={audio:0,video:0,audiovideo:0},this.tracks={},this.sourceBuffers=[[null,null],[null,null]],this._onEndStreaming=s=>{var i;this.hls&&((i=this.mediaSource)==null?void 0:i.readyState)==="open"&&this.hls.pauseBuffering()},this._onStartStreaming=s=>{this.hls&&this.hls.resumeBuffering()},this._onMediaSourceOpen=s=>{const{media:i,mediaSource:r}=this;s&&this.log("Media source opened"),!(!i||!r)&&(r.removeEventListener("sourceopen",this._onMediaSourceOpen),i.removeEventListener("emptied",this._onMediaEmptied),this.updateDuration(),this.hls.trigger(m.MEDIA_ATTACHED,{media:i,mediaSource:r}),this.mediaSource!==null&&this.checkPendingTracks())},this._onMediaSourceClose=()=>{this.log("Media source closed")},this._onMediaSourceEnded=()=>{this.log("Media source ended")},this._onMediaEmptied=()=>{const{mediaSrc:s,_objectUrl:i}=this;s!==i&&this.error(`Media element src was set while attaching MediaSource (${i} > ${s})`)},this.hls=e,this.fragmentTracker=t,this.appendSource=go(at(e.config.preferManagedMediaSource)),this.initTracks(),this.registerListeners()}hasSourceTypes(){return Object.keys(this.tracks).length>0}destroy(){this.unregisterListeners(),this.details=null,this.lastMpegAudioChunk=this.blockedAudioAppend=null,this.transferData=this.overrides=void 0,this.operationQueue&&(this.operationQueue.destroy(),this.operationQueue=null),this.hls=this.fragmentTracker=null,this._onMediaSourceOpen=this._onMediaSourceClose=null,this._onMediaSourceEnded=null,this._onStartStreaming=this._onEndStreaming=null}registerListeners(){const{hls:e}=this;e.on(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.MANIFEST_PARSED,this.onManifestParsed,this),e.on(m.BUFFER_RESET,this.onBufferReset,this),e.on(m.BUFFER_APPENDING,this.onBufferAppending,this),e.on(m.BUFFER_CODECS,this.onBufferCodecs,this),e.on(m.BUFFER_EOS,this.onBufferEos,this),e.on(m.BUFFER_FLUSHING,this.onBufferFlushing,this),e.on(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(m.FRAG_PARSED,this.onFragParsed,this),e.on(m.FRAG_CHANGED,this.onFragChanged,this),e.on(m.ERROR,this.onError,this)}unregisterListeners(){const{hls:e}=this;e.off(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.MANIFEST_PARSED,this.onManifestParsed,this),e.off(m.BUFFER_RESET,this.onBufferReset,this),e.off(m.BUFFER_APPENDING,this.onBufferAppending,this),e.off(m.BUFFER_CODECS,this.onBufferCodecs,this),e.off(m.BUFFER_EOS,this.onBufferEos,this),e.off(m.BUFFER_FLUSHING,this.onBufferFlushing,this),e.off(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(m.FRAG_PARSED,this.onFragParsed,this),e.off(m.FRAG_CHANGED,this.onFragChanged,this),e.off(m.ERROR,this.onError,this)}transferMedia(){const{media:e,mediaSource:t}=this;if(!e)return null;const s={};if(this.operationQueue){const r=this.isUpdating();r||this.operationQueue.removeBlockers();const n=this.isQueued();(r||n)&&this.warn(`Transfering MediaSource with${n?" operations in queue":""}${r?" updating SourceBuffer(s)":""} ${this.operationQueue}`),this.operationQueue.destroy()}const i=this.transferData;return!this.sourceBufferCount&&i&&i.mediaSource===t?ne(s,i.tracks):this.sourceBuffers.forEach(r=>{const[n]=r;n&&(s[n]=ne({},this.tracks[n]),this.removeBuffer(n)),r[0]=r[1]=null}),{media:e,mediaSource:t,tracks:s}}initTracks(){const e={};this.sourceBuffers=[[null,null],[null,null]],this.tracks=e,this.resetQueue(),this.resetAppendErrors(),this.lastMpegAudioChunk=this.blockedAudioAppend=null,this.lastVideoAppendEnd=0}onManifestLoading(){this.bufferCodecEventsTotal=0,this.details=null}onManifestParsed(e,t){var s;let i=2;(t.audio&&!t.video||!t.altAudio)&&(i=1),this.bufferCodecEventsTotal=i,this.log(`${i} bufferCodec event(s) expected.`),(s=this.transferData)!=null&&s.mediaSource&&this.sourceBufferCount&&i&&this.bufferCreated()}onMediaAttaching(e,t){const s=this.media=t.media;this.transferData=this.overrides=void 0;const i=at(this.appendSource);if(i){const r=!!t.mediaSource;(r||t.overrides)&&(this.transferData=t,this.overrides=t.overrides);const n=this.mediaSource=t.mediaSource||new i;if(this.assignMediaSource(n),r)this._objectUrl=s.src,this.attachTransferred();else{const o=this._objectUrl=self.URL.createObjectURL(n);if(this.appendSource)try{s.removeAttribute("src");const c=self.ManagedMediaSource;s.disableRemotePlayback=s.disableRemotePlayback||c&&n instanceof c,Qr(s),qc(s,o),s.load()}catch{s.src=o}else s.src=o}s.addEventListener("emptied",this._onMediaEmptied)}}assignMediaSource(e){var t,s;this.log(`${((t=this.transferData)==null?void 0:t.mediaSource)===e?"transferred":"created"} media source: ${(s=e.constructor)==null?void 0:s.name}`),e.addEventListener("sourceopen",this._onMediaSourceOpen),e.addEventListener("sourceended",this._onMediaSourceEnded),e.addEventListener("sourceclose",this._onMediaSourceClose),this.appendSource&&(e.addEventListener("startstreaming",this._onStartStreaming),e.addEventListener("endstreaming",this._onEndStreaming))}attachTransferred(){const e=this.media,t=this.transferData;if(!t||!e)return;const s=this.tracks,i=t.tracks,r=i?Object.keys(i):null,n=r?r.length:0,o=()=>{Promise.resolve().then(()=>{this.media&&this.mediaSourceOpenOrEnded&&this._onMediaSourceOpen()})};if(i&&r&&n){if(!this.tracksReady){this.hls.config.startFragPrefetch=!0,this.log("attachTransferred: waiting for SourceBuffer track info");return}if(this.log(`attachTransferred: (bufferCodecEventsTotal ${this.bufferCodecEventsTotal}) required tracks: ${oe(s,(c,l)=>c==="initSegment"?void 0:l)}; -transfer tracks: ${oe(i,(c,l)=>c==="initSegment"?void 0:l)}}`),!Tn(i,s)){t.mediaSource=null,t.tracks=void 0;const c=e.currentTime,l=this.details,h=Math.max(c,(l==null?void 0:l.fragments[0].start)||0);if(h-c>1){this.log(`attachTransferred: waiting for playback to reach new tracks start time ${c} -> ${h}`);return}this.warn(`attachTransferred: resetting MediaSource for incompatible tracks ("${Object.keys(i)}"->"${Object.keys(s)}") start time: ${h} currentTime: ${c}`),this.onMediaDetaching(m.MEDIA_DETACHING,{}),this.onMediaAttaching(m.MEDIA_ATTACHING,t),e.currentTime=h;return}this.transferData=void 0,r.forEach(c=>{const l=c,h=i[l];if(h){const u=h.buffer;if(u){const d=this.fragmentTracker,f=h.id;if(d.hasFragments(f)||d.hasParts(f)){const p=X.getBuffered(u);d.detectEvictedFragments(l,p,f,null,!0)}const g=Ys(l),y=[l,u];this.sourceBuffers[g]=y,u.updating&&this.operationQueue&&this.operationQueue.prependBlocker(l),this.trackSourceBuffer(l,h)}}}),o(),this.bufferCreated()}else this.log("attachTransferred: MediaSource w/o SourceBuffers"),o()}get mediaSourceOpenOrEnded(){var e;const t=(e=this.mediaSource)==null?void 0:e.readyState;return t==="open"||t==="ended"}onMediaDetaching(e,t){const s=!!t.transferMedia;this.transferData=this.overrides=void 0;const{media:i,mediaSource:r,_objectUrl:n}=this;if(r){if(this.log(`media source ${s?"transferring":"detaching"}`),s)this.sourceBuffers.forEach(([o])=>{o&&this.removeBuffer(o)}),this.resetQueue();else{if(this.mediaSourceOpenOrEnded){const o=r.readyState==="open";try{const c=r.sourceBuffers;for(let l=c.length;l--;)o&&c[l].abort(),r.removeSourceBuffer(c[l]);o&&r.endOfStream()}catch(c){this.warn(`onMediaDetaching: ${c.message} while calling endOfStream`)}}this.sourceBufferCount&&this.onBufferReset()}r.removeEventListener("sourceopen",this._onMediaSourceOpen),r.removeEventListener("sourceended",this._onMediaSourceEnded),r.removeEventListener("sourceclose",this._onMediaSourceClose),this.appendSource&&(r.removeEventListener("startstreaming",this._onStartStreaming),r.removeEventListener("endstreaming",this._onEndStreaming)),this.mediaSource=null,this._objectUrl=null}i&&(i.removeEventListener("emptied",this._onMediaEmptied),s||(n&&self.URL.revokeObjectURL(n),this.mediaSrc===n?(i.removeAttribute("src"),this.appendSource&&Xr(i),i.load()):this.warn("media|source.src was changed by a third party - skip cleanup")),this.media=null),this.hls.trigger(m.MEDIA_DETACHED,t)}onBufferReset(){this.sourceBuffers.forEach(([e])=>{e&&this.resetBuffer(e)}),this.initTracks()}resetBuffer(e){var t;const s=(t=this.tracks[e])==null?void 0:t.buffer;if(this.removeBuffer(e),s)try{var i;(i=this.mediaSource)!=null&&i.sourceBuffers.length&&this.mediaSource.removeSourceBuffer(s)}catch(r){this.warn(`onBufferReset ${e}`,r)}delete this.tracks[e]}removeBuffer(e){this.removeBufferListeners(e),this.sourceBuffers[Ys(e)]=[null,null];const t=this.tracks[e];t&&(t.buffer=void 0)}resetQueue(){this.operationQueue&&this.operationQueue.destroy(),this.operationQueue=new Gc(this.tracks)}onBufferCodecs(e,t){var s;const i=this.tracks,r=Object.keys(t);this.log(`BUFFER_CODECS: "${r}" (current SB count ${this.sourceBufferCount})`);const n="audiovideo"in t&&(i.audio||i.video)||i.audiovideo&&("audio"in t||"video"in t),o=!n&&this.sourceBufferCount&&this.media&&r.some(c=>!i[c]);if(n||o){this.warn(`Unsupported transition between "${Object.keys(i)}" and "${r}" SourceBuffers`);return}r.forEach(c=>{var l,h;const u=t[c],{id:d,codec:f,levelCodec:g,container:y,metadata:p,supplemental:E}=u;let T=i[c];const S=(l=this.transferData)==null||(l=l.tracks)==null?void 0:l[c],v=S!=null&&S.buffer?S:T,x=(v==null?void 0:v.pendingCodec)||(v==null?void 0:v.codec),D=v==null?void 0:v.levelCodec;T||(T=i[c]={buffer:void 0,listeners:[],codec:f,supplemental:E,container:y,levelCodec:g,metadata:p,id:d});const A=cs(x,D),_=A==null?void 0:A.replace(jr,"$1");let b=cs(f,g);const I=(h=b)==null?void 0:h.replace(jr,"$1");b&&A&&_!==I&&(c.slice(0,5)==="audio"&&(b=Es(b,this.appendSource)),this.log(`switching codec ${x} to ${b}`),b!==(T.pendingCodec||T.codec)&&(T.pendingCodec=b),T.container=y,this.appendChangeType(c,y,b))}),(this.tracksReady||this.sourceBufferCount)&&(t.tracks=this.sourceBufferTracks),!this.sourceBufferCount&&(this.bufferCodecEventsTotal>1&&!this.tracks.video&&!t.video&&((s=t.audio)==null?void 0:s.id)==="main"&&(this.log("Main audio-only"),this.bufferCodecEventsTotal=1),this.mediaSourceOpenOrEnded&&this.checkPendingTracks())}get sourceBufferTracks(){return Object.keys(this.tracks).reduce((e,t)=>{const s=this.tracks[t];return e[t]={id:s.id,container:s.container,codec:s.codec,levelCodec:s.levelCodec},e},{})}appendChangeType(e,t,s){const i=`${t};codecs=${s}`,r={label:`change-type=${i}`,execute:()=>{const n=this.tracks[e];if(n){const o=n.buffer;o!=null&&o.changeType&&(this.log(`changing ${e} sourceBuffer type to ${i}`),o.changeType(i),n.codec=s,n.container=t)}this.shiftAndExecuteNext(e)},onStart:()=>{},onComplete:()=>{},onError:n=>{this.warn(`Failed to change ${e} SourceBuffer type`,n)}};this.append(r,e,this.isPending(this.tracks[e]))}blockAudio(e){var t;const s=e.start,i=s+e.duration*.05;if(((t=this.fragmentTracker.getAppendedFrag(s,U.MAIN))==null?void 0:t.gap)===!0)return;const n={label:"block-audio",execute:()=>{var o;const c=this.tracks.video;(this.lastVideoAppendEnd>i||c!=null&&c.buffer&&X.isBuffered(c.buffer,i)||((o=this.fragmentTracker.getAppendedFrag(i,U.MAIN))==null?void 0:o.gap)===!0)&&(this.blockedAudioAppend=null,this.shiftAndExecuteNext("audio"))},onStart:()=>{},onComplete:()=>{},onError:o=>{this.warn("Error executing block-audio operation",o)}};this.blockedAudioAppend={op:n,frag:e},this.append(n,"audio",!0)}unblockAudio(){const{blockedAudioAppend:e,operationQueue:t}=this;e&&t&&(this.blockedAudioAppend=null,t.unblockAudio(e.op))}onBufferAppending(e,t){const{tracks:s}=this,{data:i,type:r,parent:n,frag:o,part:c,chunkMeta:l,offset:h}=t,u=l.buffering[r],{sn:d,cc:f}=o,g=self.performance.now();u.start=g;const y=o.stats.buffering,p=c?c.stats.buffering:null;y.start===0&&(y.start=g),p&&p.start===0&&(p.start=g);const E=s.audio;let T=!1;r==="audio"&&(E==null?void 0:E.container)==="audio/mpeg"&&(T=!this.lastMpegAudioChunk||l.id===1||this.lastMpegAudioChunk.sn!==l.sn,this.lastMpegAudioChunk=l);const S=s.video,v=S==null?void 0:S.buffer;if(v&&d!=="initSegment"){const A=c||o,_=this.blockedAudioAppend;if(r==="audio"&&n!=="main"&&!this.blockedAudioAppend&&!(S.ending||S.ended)){const I=A.start+A.duration*.05,P=v.buffered,F=this.currentOp("video");!P.length&&!F?this.blockAudio(A):!F&&!X.isBuffered(v,I)&&this.lastVideoAppendEndI||b{var A;u.executeStart=self.performance.now();const _=(A=this.tracks[r])==null?void 0:A.buffer;_&&(T?this.updateTimestampOffset(_,x,.1,r,d,f):h!==void 0&&N(h)&&this.updateTimestampOffset(_,h,1e-6,r,d,f)),this.appendExecutor(i,r)},onStart:()=>{},onComplete:()=>{const A=self.performance.now();u.executeEnd=u.end=A,y.first===0&&(y.first=A),p&&p.first===0&&(p.first=A);const _={};this.sourceBuffers.forEach(([b,I])=>{b&&(_[b]=X.getBuffered(I))}),this.appendErrors[r]=0,r==="audio"||r==="video"?this.appendErrors.audiovideo=0:(this.appendErrors.audio=0,this.appendErrors.video=0),this.hls.trigger(m.BUFFER_APPENDED,{type:r,frag:o,part:c,chunkMeta:l,parent:o.type,timeRanges:_})},onError:A=>{var _;const b={type:H.MEDIA_ERROR,parent:o.type,details:R.BUFFER_APPEND_ERROR,sourceBufferName:r,frag:o,part:c,chunkMeta:l,error:A,err:A,fatal:!1},I=(_=this.media)==null?void 0:_.error;if(A.code===DOMException.QUOTA_EXCEEDED_ERR||A.name=="QuotaExceededError"||"quota"in A)b.details=R.BUFFER_FULL_ERROR;else if(A.code===DOMException.INVALID_STATE_ERR&&this.mediaSourceOpenOrEnded&&!I)b.errorAction=Rt(!0);else if(A.name===Aa&&this.sourceBufferCount===0)b.errorAction=Rt(!0);else{const P=++this.appendErrors[r];this.warn(`Failed ${P}/${this.hls.config.appendErrorMaxRetry} times to append segment in "${r}" sourceBuffer (${I||"no media error"})`),(P>=this.hls.config.appendErrorMaxRetry||I)&&(b.fatal=!0)}this.hls.trigger(m.ERROR,b)}};this.log(`queuing "${r}" append sn: ${d}${c?" p: "+c.index:""} of ${o.type===U.MAIN?"level":"track"} ${o.level} cc: ${f}`),this.append(D,r,this.isPending(this.tracks[r]))}getFlushOp(e,t,s){return this.log(`queuing "${e}" remove ${t}-${s}`),{label:"remove",execute:()=>{this.removeExecutor(e,t,s)},onStart:()=>{},onComplete:()=>{this.hls.trigger(m.BUFFER_FLUSHED,{type:e})},onError:i=>{this.warn(`Failed to remove ${t}-${s} from "${e}" SourceBuffer`,i)}}}onBufferFlushing(e,t){const{type:s,startOffset:i,endOffset:r}=t;s?this.append(this.getFlushOp(s,i,r),s):this.sourceBuffers.forEach(([n])=>{n&&this.append(this.getFlushOp(n,i,r),n)})}onFragParsed(e,t){const{frag:s,part:i}=t,r=[],n=i?i.elementaryStreams:s.elementaryStreams;n[ae.AUDIOVIDEO]?r.push("audiovideo"):(n[ae.AUDIO]&&r.push("audio"),n[ae.VIDEO]&&r.push("video"));const o=()=>{const c=self.performance.now();s.stats.buffering.end=c,i&&(i.stats.buffering.end=c);const l=i?i.stats:s.stats;this.hls.trigger(m.FRAG_BUFFERED,{frag:s,part:i,stats:l,id:s.type})};r.length===0&&this.warn(`Fragments must have at least one ElementaryStreamType set. type: ${s.type} level: ${s.level} sn: ${s.sn}`),this.blockBuffers(o,r).catch(c=>{this.warn(`Fragment buffered callback ${c}`),this.stepOperationQueue(this.sourceBufferTypes)})}onFragChanged(e,t){this.trimBuffers()}get bufferedToEnd(){return this.sourceBufferCount>0&&!this.sourceBuffers.some(([e])=>{if(e){const t=this.tracks[e];if(t)return!t.ended||t.ending}return!1})}onBufferEos(e,t){var s;this.sourceBuffers.forEach(([n])=>{if(n){const o=this.tracks[n];(!t.type||t.type===n)&&(o.ending=!0,o.ended||(o.ended=!0,this.log(`${n} buffer reached EOS`)))}});const i=((s=this.overrides)==null?void 0:s.endOfStream)!==!1;this.sourceBufferCount>0&&!this.sourceBuffers.some(([n])=>{var o;return n&&!((o=this.tracks[n])!=null&&o.ended)})?i?(this.log("Queueing EOS"),this.blockUntilOpen(()=>{this.tracksEnded();const{mediaSource:n}=this;if(!n||n.readyState!=="open"){n&&this.log(`Could not call mediaSource.endOfStream(). mediaSource.readyState: ${n.readyState}`);return}this.log("Calling mediaSource.endOfStream()"),n.endOfStream(),this.hls.trigger(m.BUFFERED_TO_END,void 0)})):(this.tracksEnded(),this.hls.trigger(m.BUFFERED_TO_END,void 0)):t.type==="video"&&this.unblockAudio()}tracksEnded(){this.sourceBuffers.forEach(([e])=>{if(e!==null){const t=this.tracks[e];t&&(t.ending=!1)}})}onLevelUpdated(e,{details:t}){t.fragments.length&&(this.details=t,this.updateDuration())}updateDuration(){this.blockUntilOpen(()=>{const e=this.getDurationAndRange();e&&this.updateMediaSource(e)})}onError(e,t){if(t.details===R.BUFFER_APPEND_ERROR&&t.frag){var s;const i=(s=t.errorAction)==null?void 0:s.nextAutoLevel;N(i)&&i!==t.frag.level&&this.resetAppendErrors()}}resetAppendErrors(){this.appendErrors={audio:0,video:0,audiovideo:0}}trimBuffers(){const{hls:e,details:t,media:s}=this;if(!s||t===null||!this.sourceBufferCount)return;const i=e.config,r=s.currentTime,n=t.levelTargetDuration,o=t.live&&i.liveBackBufferLength!==null?i.liveBackBufferLength:i.backBufferLength;if(N(o)&&o>=0){const l=Math.max(o,n),h=Math.floor(r/n)*n-l;this.flushBackBuffer(r,n,h)}const c=i.frontBufferFlushThreshold;if(N(c)&&c>0){const l=Math.max(i.maxBufferLength,c),h=Math.max(l,n),u=Math.floor(r/n)*n+h;this.flushFrontBuffer(r,n,u)}}flushBackBuffer(e,t,s){this.sourceBuffers.forEach(([i,r])=>{if(r){const o=X.getBuffered(r);if(o.length>0&&s>o.start(0)){var n;this.hls.trigger(m.BACK_BUFFER_REACHED,{bufferEnd:s});const c=this.tracks[i];if((n=this.details)!=null&&n.live)this.hls.trigger(m.LIVE_BACK_BUFFER_REACHED,{bufferEnd:s});else if(c!=null&&c.ended){this.log(`Cannot flush ${i} back buffer while SourceBuffer is in ended state`);return}this.hls.trigger(m.BUFFER_FLUSHING,{startOffset:0,endOffset:s,type:i})}}})}flushFrontBuffer(e,t,s){this.sourceBuffers.forEach(([i,r])=>{if(r){const n=X.getBuffered(r),o=n.length;if(o<2)return;const c=n.start(o-1),l=n.end(o-1);if(s>c||e>=c&&e<=l)return;this.hls.trigger(m.BUFFER_FLUSHING,{startOffset:c,endOffset:1/0,type:i})}})}getDurationAndRange(){var e;const{details:t,mediaSource:s}=this;if(!t||!this.media||(s==null?void 0:s.readyState)!=="open")return null;const i=t.edge;if(t.live&&this.hls.config.liveDurationInfinity){if(t.fragments.length&&s.setLiveSeekableRange){const l=Math.max(0,t.fragmentStart),h=Math.max(l,i);return{duration:1/0,start:l,end:h}}return{duration:1/0}}const r=(e=this.overrides)==null?void 0:e.duration;if(r)return N(r)?{duration:r}:null;const n=this.media.duration,o=N(s.duration)?s.duration:0;return i>o&&i>n||!N(n)?{duration:i}:null}updateMediaSource({duration:e,start:t,end:s}){const i=this.mediaSource;!this.media||!i||i.readyState!=="open"||(i.duration!==e&&(N(e)&&this.log(`Updating MediaSource duration to ${e.toFixed(3)}`),i.duration=e),t!==void 0&&s!==void 0&&(this.log(`MediaSource duration is set to ${i.duration}. Setting seekable range to ${t}-${s}.`),i.setLiveSeekableRange(t,s)))}get tracksReady(){const e=this.pendingTrackCount;return e>0&&(e>=this.bufferCodecEventsTotal||this.isPending(this.tracks.audiovideo))}checkPendingTracks(){const{bufferCodecEventsTotal:e,pendingTrackCount:t,tracks:s}=this;if(this.log(`checkPendingTracks (pending: ${t} codec events expected: ${e}) ${oe(s)}`),this.tracksReady){var i;const r=(i=this.transferData)==null?void 0:i.tracks;r&&Object.keys(r).length?this.attachTransferred():this.createSourceBuffers()}}bufferCreated(){if(this.sourceBufferCount){const e={};this.sourceBuffers.forEach(([t,s])=>{if(t){const i=this.tracks[t];e[t]={buffer:s,container:i.container,codec:i.codec,supplemental:i.supplemental,levelCodec:i.levelCodec,id:i.id,metadata:i.metadata}}}),this.hls.trigger(m.BUFFER_CREATED,{tracks:e}),this.log(`SourceBuffers created. Running queue: ${this.operationQueue}`),this.sourceBuffers.forEach(([t])=>{this.executeNext(t)})}else{const e=new Error("could not create source buffer for media codec(s)");this.hls.trigger(m.ERROR,{type:H.MEDIA_ERROR,details:R.BUFFER_INCOMPATIBLE_CODECS_ERROR,fatal:!0,error:e,reason:e.message})}}createSourceBuffers(){const{tracks:e,sourceBuffers:t,mediaSource:s}=this;if(!s)throw new Error("createSourceBuffers called when mediaSource was null");for(const r in e){const n=r,o=e[n];if(this.isPending(o)){const c=this.getTrackCodec(o,n),l=`${o.container};codecs=${c}`;o.codec=c,this.log(`creating sourceBuffer(${l})${this.currentOp(n)?" Queued":""} ${oe(o)}`);try{const h=s.addSourceBuffer(l),u=Ys(n),d=[n,h];t[u]=d,o.buffer=h}catch(h){var i;this.error(`error while trying to add sourceBuffer: ${h.message}`),this.shiftAndExecuteNext(n),(i=this.operationQueue)==null||i.removeBlockers(),delete this.tracks[n],this.hls.trigger(m.ERROR,{type:H.MEDIA_ERROR,details:R.BUFFER_ADD_CODEC_ERROR,fatal:!1,error:h,sourceBufferName:n,mimeType:l,parent:o.id});return}this.trackSourceBuffer(n,o)}}this.bufferCreated()}getTrackCodec(e,t){const s=e.supplemental;let i=e.codec;s&&(t==="video"||t==="audiovideo")&&$t(s,"video")&&(i=wo(i,s));const r=cs(i,e.levelCodec);return r?t.slice(0,5)==="audio"?Es(r,this.appendSource):r:""}trackSourceBuffer(e,t){const s=t.buffer;if(!s)return;const i=this.getTrackCodec(t,e);this.tracks[e]={buffer:s,codec:i,container:t.container,levelCodec:t.levelCodec,supplemental:t.supplemental,metadata:t.metadata,id:t.id,listeners:[]},this.removeBufferListeners(e),this.addBufferListener(e,"updatestart",this.onSBUpdateStart),this.addBufferListener(e,"updateend",this.onSBUpdateEnd),this.addBufferListener(e,"error",this.onSBUpdateError),this.appendSource&&this.addBufferListener(e,"bufferedchange",(r,n)=>{const o=n.removedRanges;o!=null&&o.length&&this.hls.trigger(m.BUFFER_FLUSHED,{type:r})})}get mediaSrc(){var e,t;const s=((e=this.media)==null||(t=e.querySelector)==null?void 0:t.call(e,"source"))||this.media;return s==null?void 0:s.src}onSBUpdateStart(e){const t=this.currentOp(e);t&&t.onStart()}onSBUpdateEnd(e){var t;if(((t=this.mediaSource)==null?void 0:t.readyState)==="closed"){this.resetBuffer(e);return}const s=this.currentOp(e);s&&(s.onComplete(),this.shiftAndExecuteNext(e))}onSBUpdateError(e,t){var s;const i=new Error(`${e} SourceBuffer error. MediaSource readyState: ${(s=this.mediaSource)==null?void 0:s.readyState}`);this.error(`${i}`,t),this.hls.trigger(m.ERROR,{type:H.MEDIA_ERROR,details:R.BUFFER_APPENDING_ERROR,sourceBufferName:e,error:i,fatal:!1});const r=this.currentOp(e);r&&r.onError(i)}updateTimestampOffset(e,t,s,i,r,n){const o=t-e.timestampOffset;Math.abs(o)>=s&&(this.log(`Updating ${i} SourceBuffer timestampOffset to ${t} (sn: ${r} cc: ${n})`),e.timestampOffset=t)}removeExecutor(e,t,s){const{media:i,mediaSource:r}=this,n=this.tracks[e],o=n==null?void 0:n.buffer;if(!i||!r||!o){this.warn(`Attempting to remove from the ${e} SourceBuffer, but it does not exist`),this.shiftAndExecuteNext(e);return}const c=N(i.duration)?i.duration:1/0,l=N(r.duration)?r.duration:1/0,h=Math.max(0,t),u=Math.min(s,c,l);u>h&&(!n.ending||n.ended)?(n.ended=!1,this.log(`Removing [${h},${u}] from the ${e} SourceBuffer`),o.remove(h,u)):this.shiftAndExecuteNext(e)}appendExecutor(e,t){const s=this.tracks[t],i=s==null?void 0:s.buffer;if(!i)throw new Kc(`Attempting to append to the ${t} SourceBuffer, but it does not exist`);s.ending=!1,s.ended=!1,i.appendBuffer(e)}blockUntilOpen(e){if(this.isUpdating()||this.isQueued())this.blockBuffers(e).catch(t=>{this.warn(`SourceBuffer blocked callback ${t}`),this.stepOperationQueue(this.sourceBufferTypes)});else try{e()}catch(t){this.warn(`Callback run without blocking ${this.operationQueue} ${t}`)}}isUpdating(){return this.sourceBuffers.some(([e,t])=>e&&t.updating)}isQueued(){return this.sourceBuffers.some(([e])=>e&&!!this.currentOp(e))}isPending(e){return!!e&&!e.buffer}blockBuffers(e,t=this.sourceBufferTypes){if(!t.length)return this.log("Blocking operation requested, but no SourceBuffers exist"),Promise.resolve().then(e);const{operationQueue:s}=this,i=t.map(n=>this.appendBlocker(n));return t.length>1&&!!this.blockedAudioAppend&&this.unblockAudio(),Promise.all(i).then(n=>{s===this.operationQueue&&(e(),this.stepOperationQueue(this.sourceBufferTypes))})}stepOperationQueue(e){e.forEach(t=>{var s;const i=(s=this.tracks[t])==null?void 0:s.buffer;!i||i.updating||this.shiftAndExecuteNext(t)})}append(e,t,s){this.operationQueue&&this.operationQueue.append(e,t,s)}appendBlocker(e){if(this.operationQueue)return this.operationQueue.appendBlocker(e)}currentOp(e){return this.operationQueue?this.operationQueue.current(e):null}executeNext(e){e&&this.operationQueue&&this.operationQueue.executeNext(e)}shiftAndExecuteNext(e){this.operationQueue&&this.operationQueue.shiftAndExecuteNext(e)}get pendingTrackCount(){return Object.keys(this.tracks).reduce((e,t)=>e+(this.isPending(this.tracks[t])?1:0),0)}get sourceBufferCount(){return this.sourceBuffers.reduce((e,[t])=>e+(t?1:0),0)}get sourceBufferTypes(){return this.sourceBuffers.map(([e])=>e).filter(e=>!!e)}addBufferListener(e,t,s){const i=this.tracks[e];if(!i)return;const r=i.buffer;if(!r)return;const n=s.bind(this,e);i.listeners.push({event:t,listener:n}),r.addEventListener(t,n)}removeBufferListeners(e){const t=this.tracks[e];if(!t)return;const s=t.buffer;s&&(t.listeners.forEach(i=>{s.removeEventListener(i.event,i.listener)}),t.listeners.length=0)}}function Xr(a){const e=a.querySelectorAll("source");[].slice.call(e).forEach(t=>{a.removeChild(t)})}function Hc(a,e){const t=self.document.createElement("source");t.type="video/mp4",t.src=e,a.appendChild(t)}function Ys(a){return a==="audio"?1:0}class Ki{constructor(e){this.hls=void 0,this.autoLevelCapping=void 0,this.firstLevel=void 0,this.media=void 0,this.restrictedLevels=void 0,this.timer=void 0,this.clientRect=void 0,this.streamController=void 0,this.hls=e,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.firstLevel=-1,this.media=null,this.restrictedLevels=[],this.timer=void 0,this.clientRect=null,this.registerListeners()}setStreamController(e){this.streamController=e}destroy(){this.hls&&this.unregisterListener(),this.timer&&this.stopCapping(),this.media=null,this.clientRect=null,this.hls=this.streamController=null}registerListeners(){const{hls:e}=this;e.on(m.FPS_DROP_LEVEL_CAPPING,this.onFpsDropLevelCapping,this),e.on(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(m.MANIFEST_PARSED,this.onManifestParsed,this),e.on(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(m.BUFFER_CODECS,this.onBufferCodecs,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this)}unregisterListener(){const{hls:e}=this;e.off(m.FPS_DROP_LEVEL_CAPPING,this.onFpsDropLevelCapping,this),e.off(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(m.MANIFEST_PARSED,this.onManifestParsed,this),e.off(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(m.BUFFER_CODECS,this.onBufferCodecs,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this)}onFpsDropLevelCapping(e,t){const s=this.hls.levels[t.droppedLevel];this.isLevelAllowed(s)&&this.restrictedLevels.push({bitrate:s.bitrate,height:s.height,width:s.width})}onMediaAttaching(e,t){this.media=t.media instanceof HTMLVideoElement?t.media:null,this.clientRect=null,this.timer&&this.hls.levels.length&&this.detectPlayerSize()}onManifestParsed(e,t){const s=this.hls;this.restrictedLevels=[],this.firstLevel=t.firstLevel,s.config.capLevelToPlayerSize&&t.video&&this.startCapping()}onLevelsUpdated(e,t){this.timer&&N(this.autoLevelCapping)&&this.detectPlayerSize()}onBufferCodecs(e,t){this.hls.config.capLevelToPlayerSize&&t.video&&this.startCapping()}onMediaDetaching(){this.stopCapping(),this.media=null}detectPlayerSize(){if(this.media){if(this.mediaHeight<=0||this.mediaWidth<=0){this.clientRect=null;return}const e=this.hls.levels;if(e.length){const t=this.hls,s=this.getMaxLevel(e.length-1);s!==this.autoLevelCapping&&t.logger.log(`Setting autoLevelCapping to ${s}: ${e[s].height}p@${e[s].bitrate} for media ${this.mediaWidth}x${this.mediaHeight}`),t.autoLevelCapping=s,t.autoLevelEnabled&&t.autoLevelCapping>this.autoLevelCapping&&this.streamController&&this.streamController.nextLevelSwitch(),this.autoLevelCapping=t.autoLevelCapping}}}getMaxLevel(e){const t=this.hls.levels;if(!t.length)return-1;const s=t.filter((i,r)=>this.isLevelAllowed(i)&&r<=e);return this.clientRect=null,Ki.getMaxLevelByMediaSize(s,this.mediaWidth,this.mediaHeight)}startCapping(){this.timer||(this.autoLevelCapping=Number.POSITIVE_INFINITY,self.clearInterval(this.timer),this.timer=self.setInterval(this.detectPlayerSize.bind(this),1e3),this.detectPlayerSize())}stopCapping(){this.restrictedLevels=[],this.firstLevel=-1,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.timer&&(self.clearInterval(this.timer),this.timer=void 0)}getDimensions(){if(this.clientRect)return this.clientRect;const e=this.media,t={width:0,height:0};if(e){const s=e.getBoundingClientRect();t.width=s.width,t.height=s.height,!t.width&&!t.height&&(t.width=s.right-s.left||e.width||0,t.height=s.bottom-s.top||e.height||0)}return this.clientRect=t,t}get mediaWidth(){return this.getDimensions().width*this.contentScaleFactor}get mediaHeight(){return this.getDimensions().height*this.contentScaleFactor}get contentScaleFactor(){let e=1;if(!this.hls.config.ignoreDevicePixelRatio)try{e=self.devicePixelRatio}catch{}return Math.min(e,this.hls.config.maxDevicePixelRatio)}isLevelAllowed(e){return!this.restrictedLevels.some(s=>e.bitrate===s.bitrate&&e.width===s.width&&e.height===s.height)}static getMaxLevelByMediaSize(e,t,s){if(!(e!=null&&e.length))return-1;const i=(o,c)=>c?o.width!==c.width||o.height!==c.height:!0;let r=e.length-1;const n=Math.max(t,s);for(let o=0;o=n||c.height>=n)&&i(c,e[o+1])){r=o;break}}return r}}const Yc={MANIFEST:"m",AUDIO:"a",VIDEO:"v",MUXED:"av",INIT:"i",CAPTION:"c",TIMED_TEXT:"tt",KEY:"k",OTHER:"o"},xe=Yc,Wc={HLS:"h"},qc=Wc;class We{constructor(e,t){Array.isArray(e)&&(e=e.map(s=>s instanceof We?s:new We(s))),this.value=e,this.params=t}}const jc="Dict";function Xc(a){return Array.isArray(a)?JSON.stringify(a):a instanceof Map?"Map{}":a instanceof Set?"Set{}":typeof a=="object"?JSON.stringify(a):String(a)}function zc(a,e,t,s){return new Error(`failed to ${a} "${Xc(e)}" as ${t}`,{cause:s})}function qe(a,e,t){return zc("serialize",a,e,t)}class Ia{constructor(e){this.description=e}}const zr="Bare Item",Qc="Boolean";function Zc(a){if(typeof a!="boolean")throw qe(a,Qc);return a?"?1":"?0"}function Jc(a){return btoa(String.fromCharCode(...a))}const eh="Byte Sequence";function th(a){if(ArrayBuffer.isView(a)===!1)throw qe(a,eh);return`:${Jc(a)}:`}const sh="Integer";function ih(a){return a<-999999999999999||99999999999999912)throw qe(a,nh);const t=e.toString();return t.includes(".")?t:`${t}.0`}const oh="String",lh=/[\x00-\x1f\x7f]+/;function ch(a){if(lh.test(a))throw qe(a,oh);return`"${a.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function hh(a){return a.description||a.toString().slice(7,-1)}const uh="Token";function Qr(a){const e=hh(a);if(/^([a-zA-Z*])([!#$%&'*+\-.^_`|~\w:/]*)$/.test(e)===!1)throw qe(e,uh);return e}function mi(a){switch(typeof a){case"number":if(!N(a))throw qe(a,zr);return Number.isInteger(a)?La(a):ah(a);case"string":return ch(a);case"symbol":return Qr(a);case"boolean":return Zc(a);case"object":if(a instanceof Date)return rh(a);if(a instanceof Uint8Array)return th(a);if(a instanceof Ia)return Qr(a);default:throw qe(a,zr)}}const dh="Key";function pi(a){if(/^[a-z*][a-z0-9\-_.*]*$/.test(a)===!1)throw qe(a,dh);return a}function Vi(a){return a==null?"":Object.entries(a).map(([e,t])=>t===!0?`;${pi(e)}`:`;${pi(e)}=${mi(t)}`).join("")}function ba(a){return a instanceof We?`${mi(a.value)}${Vi(a.params)}`:mi(a)}function fh(a){return`(${a.value.map(ba).join(" ")})${Vi(a.params)}`}function gh(a,e={whitespace:!0}){if(typeof a!="object"||a==null)throw qe(a,jc);const t=a instanceof Map?a.entries():Object.entries(a),s=e!=null&&e.whitespace?" ":"";return Array.from(t).map(([i,r])=>{r instanceof We||(r=new We(r));let n=pi(i);return r.value===!0?n+=Vi(r.params):(n+="=",Array.isArray(r.value)?n+=fh(r):n+=ba(r)),n}).join(`,${s}`)}function _a(a,e){return gh(a,e)}const $e="CMCD-Object",he="CMCD-Request",ut="CMCD-Session",tt="CMCD-Status",mh={br:$e,ab:$e,d:$e,ot:$e,tb:$e,tpb:$e,lb:$e,tab:$e,lab:$e,url:$e,pb:he,bl:he,tbl:he,dl:he,ltc:he,mtp:he,nor:he,nrr:he,rc:he,sn:he,sta:he,su:he,ttfb:he,ttfbb:he,ttlb:he,cmsdd:he,cmsds:he,smrt:he,df:he,cs:he,ts:he,cid:ut,pr:ut,sf:ut,sid:ut,st:ut,v:ut,msd:ut,bs:tt,bsd:tt,cdn:tt,rtp:tt,bg:tt,pt:tt,ec:tt,e:tt},ph={REQUEST:he};function yh(a){return Object.keys(a).reduce((e,t)=>{var s;return(s=a[t])===null||s===void 0||s.forEach(i=>e[i]=t),e},{})}function Eh(a,e){const t={};if(!a)return t;const s=Object.keys(a),i=e?yh(e):{};return s.reduce((r,n)=>{var o;const c=mh[n]||i[n]||ph.REQUEST,l=(o=r[c])!==null&&o!==void 0?o:r[c]={};return l[n]=a[n],r},t)}function Th(a){return["ot","sf","st","e","sta"].includes(a)}function Sh(a){return typeof a=="number"?N(a):a!=null&&a!==""&&a!==!1}const Da="event";function vh(a,e){const t=new URL(a),s=new URL(e);if(t.origin!==s.origin)return a;const i=t.pathname.split("/").slice(1),r=s.pathname.split("/").slice(1,-1);for(;i[0]===r[0];)i.shift(),r.shift();for(;r.length;)r.shift(),i.unshift("..");return i.join("/")+t.search+t.hash}const gs=a=>Math.round(a),yi=(a,e)=>Array.isArray(a)?a.map(t=>yi(t,e)):a instanceof We&&typeof a.value=="string"?new We(yi(a.value,e),a.params):(e.baseUrl&&(a=vh(a,e.baseUrl)),e.version===1?encodeURIComponent(a):a),ss=a=>gs(a/100)*100,xh=(a,e)=>{let t=a;return e.version>=2&&(a instanceof We&&typeof a.value=="string"?t=new We([a]):typeof a=="string"&&(t=[a])),yi(t,e)},Ah={br:gs,d:gs,bl:ss,dl:ss,mtp:ss,nor:xh,rtp:ss,tb:gs},Ca="request",Pa="response",Hi=["ab","bg","bl","br","bs","bsd","cdn","cid","cs","df","ec","lab","lb","ltc","msd","mtp","pb","pr","pt","sf","sid","sn","st","sta","tab","tb","tbl","tpb","ts","v"],Ih=["e"],Lh=/^[a-zA-Z0-9-.]+-[a-zA-Z0-9-.]+$/;function Cs(a){return Lh.test(a)}function Rh(a){return Hi.includes(a)||Ih.includes(a)||Cs(a)}const ka=["d","dl","nor","ot","rtp","su"];function bh(a){return Hi.includes(a)||ka.includes(a)||Cs(a)}const _h=["cmsdd","cmsds","rc","smrt","ttfb","ttfbb","ttlb","url"];function Dh(a){return Hi.includes(a)||ka.includes(a)||_h.includes(a)||Cs(a)}const Ch=["bl","br","bs","cid","d","dl","mtp","nor","nrr","ot","pr","rtp","sf","sid","st","su","tb","v"];function Ph(a){return Ch.includes(a)||Cs(a)}const kh={[Pa]:Dh,[Da]:Rh,[Ca]:bh};function wa(a,e={}){const t={};if(a==null||typeof a!="object")return t;const s=e.version||a.v||1,i=e.reportingMode||Ca,r=s===1?Ph:kh[i];let n=Object.keys(a).filter(r);const o=e.filter;typeof o=="function"&&(n=n.filter(o));const c=i===Pa||i===Da;c&&!n.includes("ts")&&n.push("ts"),s>1&&!n.includes("v")&&n.push("v");const l=ne({},Ah,e.formatters),h={version:s,reportingMode:i,baseUrl:e.baseUrl};return n.sort().forEach(u=>{let d=a[u];const f=l[u];if(typeof f=="function"&&(d=f(d,h)),u==="v"){if(s===1)return;d=s}u=="pr"&&d===1||(c&&u==="ts"&&!N(d)&&(d=Date.now()),Sh(d)&&(Th(u)&&typeof d=="string"&&(d=new Ia(d)),t[u]=d))}),t}function wh(a,e={}){const t={};if(!a)return t;const s=wa(a,e),i=Eh(s,e==null?void 0:e.customHeaderMap);return Object.entries(i).reduce((r,[n,o])=>{const c=_a(o,{whitespace:!1});return c&&(r[n]=c),r},t)}function Oh(a,e,t){return ne(a,wh(e,t))}const Fh="CMCD";function Mh(a,e={}){return a?_a(wa(a,e),{whitespace:!1}):""}function Nh(a,e={}){if(!a)return"";const t=Mh(a,e);return encodeURIComponent(t)}function Bh(a,e={}){if(!a)return"";const t=Nh(a,e);return`${Fh}=${t}`}const Zr=/CMCD=[^&#]+/;function Uh(a,e,t){const s=Bh(e,t);if(!s)return a;if(Zr.test(a))return a.replace(Zr,s);const i=a.includes("?")?"&":"?";return`${a}${i}${s}`}class $h{constructor(e){this.hls=void 0,this.config=void 0,this.media=void 0,this.sid=void 0,this.cid=void 0,this.useHeaders=!1,this.includeKeys=void 0,this.initialized=!1,this.starved=!1,this.buffering=!0,this.audioBuffer=void 0,this.videoBuffer=void 0,this.onWaiting=()=>{this.initialized&&(this.starved=!0),this.buffering=!0},this.onPlaying=()=>{this.initialized||(this.initialized=!0),this.buffering=!1},this.applyPlaylistData=i=>{try{this.apply(i,{ot:xe.MANIFEST,su:!this.initialized})}catch(r){this.hls.logger.warn("Could not generate manifest CMCD data.",r)}},this.applyFragmentData=i=>{try{const{frag:r,part:n}=i,o=this.hls.levels[r.level],c=this.getObjectType(r),l={d:(n||r).duration*1e3,ot:c};(c===xe.VIDEO||c===xe.AUDIO||c==xe.MUXED)&&(l.br=o.bitrate/1e3,l.tb=this.getTopBandwidth(c)/1e3,l.bl=this.getBufferLength(c));const h=n?this.getNextPart(n):this.getNextFrag(r);h!=null&&h.url&&h.url!==r.url&&(l.nor=h.url),this.apply(i,l)}catch(r){this.hls.logger.warn("Could not generate segment CMCD data.",r)}},this.hls=e;const t=this.config=e.config,{cmcd:s}=t;s!=null&&(t.pLoader=this.createPlaylistLoader(),t.fLoader=this.createFragmentLoader(),this.sid=s.sessionId||e.sessionId,this.cid=s.contentId,this.useHeaders=s.useHeaders===!0,this.includeKeys=s.includeKeys,this.registerListeners())}registerListeners(){const e=this.hls;e.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(m.MEDIA_DETACHED,this.onMediaDetached,this),e.on(m.BUFFER_CREATED,this.onBufferCreated,this)}unregisterListeners(){const e=this.hls;e.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(m.MEDIA_DETACHED,this.onMediaDetached,this),e.off(m.BUFFER_CREATED,this.onBufferCreated,this)}destroy(){this.unregisterListeners(),this.onMediaDetached(),this.hls=this.config=this.audioBuffer=this.videoBuffer=null,this.onWaiting=this.onPlaying=this.media=null}onMediaAttached(e,t){this.media=t.media,this.media.addEventListener("waiting",this.onWaiting),this.media.addEventListener("playing",this.onPlaying)}onMediaDetached(){this.media&&(this.media.removeEventListener("waiting",this.onWaiting),this.media.removeEventListener("playing",this.onPlaying),this.media=null)}onBufferCreated(e,t){var s,i;this.audioBuffer=(s=t.tracks.audio)==null?void 0:s.buffer,this.videoBuffer=(i=t.tracks.video)==null?void 0:i.buffer}createData(){var e;return{v:1,sf:qc.HLS,sid:this.sid,cid:this.cid,pr:(e=this.media)==null?void 0:e.playbackRate,mtp:this.hls.bandwidthEstimate/1e3}}apply(e,t={}){ne(t,this.createData());const s=t.ot===xe.INIT||t.ot===xe.VIDEO||t.ot===xe.MUXED;this.starved&&s&&(t.bs=!0,t.su=!0,this.starved=!1),t.su==null&&(t.su=this.buffering);const{includeKeys:i}=this;i&&(t=Object.keys(t).reduce((n,o)=>(i.includes(o)&&(n[o]=t[o]),n),{}));const r={baseUrl:e.url};this.useHeaders?(e.headers||(e.headers={}),Oh(e.headers,t,r)):e.url=Uh(e.url,t,r)}getNextFrag(e){var t;const s=(t=this.hls.levels[e.level])==null?void 0:t.details;if(s){const i=e.sn-s.startSN;return s.fragments[i+1]}}getNextPart(e){var t;const{index:s,fragment:i}=e,r=(t=this.hls.levels[i.level])==null||(t=t.details)==null?void 0:t.partList;if(r){const{sn:n}=i;for(let o=r.length-1;o>=0;o--){const c=r[o];if(c.index===s&&c.fragment.sn===n)return r[o+1]}}}getObjectType(e){const{type:t}=e;if(t==="subtitle")return xe.TIMED_TEXT;if(e.sn==="initSegment")return xe.INIT;if(t==="audio")return xe.AUDIO;if(t==="main")return this.hls.audioTracks.length?xe.VIDEO:xe.MUXED}getTopBandwidth(e){let t=0,s;const i=this.hls;if(e===xe.AUDIO)s=i.audioTracks;else{const r=i.maxAutoLevel,n=r>-1?r+1:i.levels.length;s=i.levels.slice(0,n)}return s.forEach(r=>{r.bitrate>t&&(t=r.bitrate)}),t>0?t:NaN}getBufferLength(e){const t=this.media,s=e===xe.AUDIO?this.audioBuffer:this.videoBuffer;return!s||!t?NaN:X.bufferInfo(s,t.currentTime,this.config.maxBufferHole).len*1e3}createPlaylistLoader(){const{pLoader:e}=this.config,t=this.applyPlaylistData,s=e||this.config.loader;return class{constructor(r){this.loader=void 0,this.loader=new s(r)}get stats(){return this.loader.stats}get context(){return this.loader.context}destroy(){this.loader.destroy()}abort(){this.loader.abort()}load(r,n,o){t(r),this.loader.load(r,n,o)}}}createFragmentLoader(){const{fLoader:e}=this.config,t=this.applyFragmentData,s=e||this.config.loader;return class{constructor(r){this.loader=void 0,this.loader=new s(r)}get stats(){return this.loader.stats}get context(){return this.loader.context}destroy(){this.loader.destroy()}abort(){this.loader.abort()}load(r,n,o){t(r),this.loader.load(r,n,o)}}}}const Gh=3e5;class Kh extends we{constructor(e){super("content-steering",e.logger),this.hls=void 0,this.loader=null,this.uri=null,this.pathwayId=".",this._pathwayPriority=null,this.timeToLoad=300,this.reloadTimer=-1,this.updated=0,this.started=!1,this.enabled=!0,this.levels=null,this.audioTracks=null,this.subtitleTracks=null,this.penalizedPathways={},this.hls=e,this.registerListeners()}registerListeners(){const e=this.hls;e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(m.MANIFEST_PARSED,this.onManifestParsed,this),e.on(m.ERROR,this.onError,this)}unregisterListeners(){const e=this.hls;e&&(e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(m.MANIFEST_PARSED,this.onManifestParsed,this),e.off(m.ERROR,this.onError,this))}pathways(){return(this.levels||[]).reduce((e,t)=>(e.indexOf(t.pathwayId)===-1&&e.push(t.pathwayId),e),[])}get pathwayPriority(){return this._pathwayPriority}set pathwayPriority(e){this.updatePathwayPriority(e)}startLoad(){if(this.started=!0,this.clearTimeout(),this.enabled&&this.uri){if(this.updated){const e=this.timeToLoad*1e3-(performance.now()-this.updated);if(e>0){this.scheduleRefresh(this.uri,e);return}}this.loadSteeringManifest(this.uri)}}stopLoad(){this.started=!1,this.loader&&(this.loader.destroy(),this.loader=null),this.clearTimeout()}clearTimeout(){this.reloadTimer!==-1&&(self.clearTimeout(this.reloadTimer),this.reloadTimer=-1)}destroy(){this.unregisterListeners(),this.stopLoad(),this.hls=null,this.levels=this.audioTracks=this.subtitleTracks=null}removeLevel(e){const t=this.levels;t&&(this.levels=t.filter(s=>s!==e))}onManifestLoading(){this.stopLoad(),this.enabled=!0,this.timeToLoad=300,this.updated=0,this.uri=null,this.pathwayId=".",this.levels=this.audioTracks=this.subtitleTracks=null}onManifestLoaded(e,t){const{contentSteering:s}=t;s!==null&&(this.pathwayId=s.pathwayId,this.uri=s.uri,this.started&&this.startLoad())}onManifestParsed(e,t){this.audioTracks=t.audioTracks,this.subtitleTracks=t.subtitleTracks}onError(e,t){const{errorAction:s}=t;if((s==null?void 0:s.action)===pe.SendAlternateToPenaltyBox&&s.flags===Le.MoveAllAlternatesMatchingHost){const i=this.levels;let r=this._pathwayPriority,n=this.pathwayId;if(t.context){const{groupId:o,pathwayId:c,type:l}=t.context;o&&i?n=this.getPathwayForGroupId(o,l,n):c&&(n=c)}n in this.penalizedPathways||(this.penalizedPathways[n]=performance.now()),!r&&i&&(r=this.pathways()),r&&r.length>1&&(this.updatePathwayPriority(r),s.resolved=this.pathwayId!==n),t.details===R.BUFFER_APPEND_ERROR&&!t.fatal?s.resolved=!0:s.resolved||this.warn(`Could not resolve ${t.details} ("${t.error.message}") with content-steering for Pathway: ${n} levels: ${i&&i.length} priorities: ${oe(r)} penalized: ${oe(this.penalizedPathways)}`)}}filterParsedLevels(e){this.levels=e;let t=this.getLevelsForPathway(this.pathwayId);if(t.length===0){const s=e[0].pathwayId;this.log(`No levels found in Pathway ${this.pathwayId}. Setting initial Pathway to "${s}"`),t=this.getLevelsForPathway(s),this.pathwayId=s}return t.length!==e.length&&this.log(`Found ${t.length}/${e.length} levels in Pathway "${this.pathwayId}"`),t}getLevelsForPathway(e){return this.levels===null?[]:this.levels.filter(t=>e===t.pathwayId)}updatePathwayPriority(e){this._pathwayPriority=e;let t;const s=this.penalizedPathways,i=performance.now();Object.keys(s).forEach(r=>{i-s[r]>Gh&&delete s[r]});for(let r=0;r0){this.log(`Setting Pathway to "${n}"`),this.pathwayId=n,Jn(t),this.hls.trigger(m.LEVELS_UPDATED,{levels:t});const l=this.hls.levels[o];c&&l&&this.levels&&(l.attrs["STABLE-VARIANT-ID"]!==c.attrs["STABLE-VARIANT-ID"]&&l.bitrate!==c.bitrate&&this.log(`Unstable Pathways change from bitrate ${c.bitrate} to ${l.bitrate}`),this.hls.nextLoadLevel=o);break}}}getPathwayForGroupId(e,t,s){const i=this.getLevelsForPathway(s).concat(this.levels||[]);for(let r=0;r{const{ID:n,"BASE-ID":o,"URI-REPLACEMENT":c}=r;if(t.some(h=>h.pathwayId===n))return;const l=this.getLevelsForPathway(o).map(h=>{const u=new le(h.attrs);u["PATHWAY-ID"]=n;const d=u.AUDIO&&`${u.AUDIO}_clone_${n}`,f=u.SUBTITLES&&`${u.SUBTITLES}_clone_${n}`;d&&(s[u.AUDIO]=d,u.AUDIO=d),f&&(i[u.SUBTITLES]=f,u.SUBTITLES=f);const g=Oa(h.uri,u["STABLE-VARIANT-ID"],"PER-VARIANT-URIS",c),y=new Kt({attrs:u,audioCodec:h.audioCodec,bitrate:h.bitrate,height:h.height,name:h.name,url:g,videoCodec:h.videoCodec,width:h.width});if(h.audioGroups)for(let p=1;p{this.log(`Loaded steering manifest: "${i}"`);const g=h.data;if((g==null?void 0:g.VERSION)!==1){this.log(`Steering VERSION ${g.VERSION} not supported!`);return}this.updated=performance.now(),this.timeToLoad=g.TTL;const{"RELOAD-URI":y,"PATHWAY-CLONES":p,"PATHWAY-PRIORITY":E}=g;if(y)try{this.uri=new self.URL(y,i).href}catch{this.enabled=!1,this.log(`Failed to parse Steering Manifest RELOAD-URI: ${y}`);return}this.scheduleRefresh(this.uri||d.url),p&&this.clonePathways(p);const T={steeringManifest:g,url:i.toString()};this.hls.trigger(m.STEERING_MANIFEST_LOADED,T),E&&this.updatePathwayPriority(E)},onError:(h,u,d,f)=>{if(this.log(`Error loading steering manifest: ${h.code} ${h.text} (${u.url})`),this.stopLoad(),h.code===410){this.enabled=!1,this.log(`Steering manifest ${u.url} no longer available`);return}let g=this.timeToLoad*1e3;if(h.code===429){const y=this.loader;if(typeof(y==null?void 0:y.getResponseHeader)=="function"){const p=y.getResponseHeader("Retry-After");p&&(g=parseFloat(p)*1e3)}this.log(`Steering manifest ${u.url} rate limited`);return}this.scheduleRefresh(this.uri||u.url,g)},onTimeout:(h,u,d)=>{this.log(`Timeout loading steering manifest (${u.url})`),this.scheduleRefresh(this.uri||u.url)}};this.log(`Requesting steering manifest: ${i}`),this.loader.load(r,c,l)}scheduleRefresh(e,t=this.timeToLoad*1e3){this.clearTimeout(),this.reloadTimer=self.setTimeout(()=>{var s;const i=(s=this.hls)==null?void 0:s.media;if(i&&!i.ended){this.loadSteeringManifest(e);return}this.scheduleRefresh(e,this.timeToLoad*1e3)},t)}}function Jr(a,e,t,s){a&&Object.keys(e).forEach(i=>{const r=a.filter(n=>n.groupId===i).map(n=>{const o=ne({},n);return o.details=void 0,o.attrs=new le(o.attrs),o.url=o.attrs.URI=Oa(n.url,n.attrs["STABLE-RENDITION-ID"],"PER-RENDITION-URIS",t),o.groupId=o.attrs["GROUP-ID"]=e[i],o.attrs["PATHWAY-ID"]=s,o});a.push(...r)})}function Oa(a,e,t,s){const{HOST:i,PARAMS:r,[t]:n}=s;let o;e&&(o=n==null?void 0:n[e],o&&(a=o));const c=new self.URL(a);return i&&!o&&(c.host=i),r&&Object.keys(r).sort().forEach(l=>{l&&c.searchParams.set(l,r[l])}),c.href}class _t extends we{constructor(e){super("eme",e.logger),this.hls=void 0,this.config=void 0,this.media=null,this.mediaResolved=void 0,this.keyFormatPromise=null,this.keySystemAccessPromises={},this._requestLicenseFailureCount=0,this.mediaKeySessions=[],this.keyIdToKeySessionPromise={},this.mediaKeys=null,this.setMediaKeysQueue=_t.CDMCleanupPromise?[_t.CDMCleanupPromise]:[],this.bannedKeyIds={},this.onMediaEncrypted=t=>{const{initDataType:s,initData:i}=t,r=`"${t.type}" event: init data type: "${s}"`;if(this.debug(r),i!==null){if(!this.keyFormatPromise){let n=Object.keys(this.keySystemAccessPromises);n.length||(n=Ft(this.config));const o=n.map(Ns).filter(c=>!!c);this.keyFormatPromise=this.getKeyFormatPromise(o)}this.keyFormatPromise.then(n=>{const o=us(n);if(s!=="sinf"||o!==ce.FAIRPLAY){this.log(`Ignoring "${t.type}" event with init data type: "${s}" for selected key-system ${o}`);return}let c;try{const f=de(new Uint8Array(i)),g=ki(JSON.parse(f).sinf),y=_n(g);if(!y)throw new Error("'schm' box missing or not cbcs/cenc with schi > tenc");c=new Uint8Array(y.subarray(8,24))}catch(f){this.warn(`${r} Failed to parse sinf: ${f}`);return}const l=ye(c),{keyIdToKeySessionPromise:h,mediaKeySessions:u}=this;let d=h[l];for(let f=0;fthis.generateRequestWithPreferredKeySession(g,s,i,"encrypted-event-key-match")),d.catch(E=>this.handleError(E));break}}d||this.handleError(new Error(`Key ID ${l} not encountered in playlist. Key-system sessions ${u.length}.`))}).catch(n=>this.handleError(n))}},this.onWaitingForKey=t=>{this.log(`"${t.type}" event`)},this.hls=e,this.config=e.config,this.registerListeners()}destroy(){this.onDestroying(),this.onMediaDetached();const e=this.config;e.requestMediaKeySystemAccessFunc=null,e.licenseXhrSetup=e.licenseResponseCallback=void 0,e.drmSystems=e.drmSystemOptions={},this.hls=this.config=this.keyIdToKeySessionPromise=null,this.onMediaEncrypted=this.onWaitingForKey=null}registerListeners(){this.hls.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.on(m.MEDIA_DETACHED,this.onMediaDetached,this),this.hls.on(m.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.on(m.MANIFEST_LOADED,this.onManifestLoaded,this),this.hls.on(m.DESTROYING,this.onDestroying,this)}unregisterListeners(){this.hls.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.off(m.MEDIA_DETACHED,this.onMediaDetached,this),this.hls.off(m.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.off(m.MANIFEST_LOADED,this.onManifestLoaded,this),this.hls.off(m.DESTROYING,this.onDestroying,this)}getLicenseServerUrl(e){const{drmSystems:t,widevineLicenseUrl:s}=this.config,i=t==null?void 0:t[e];if(i)return i.licenseUrl;if(e===ce.WIDEVINE&&s)return s}getLicenseServerUrlOrThrow(e){const t=this.getLicenseServerUrl(e);if(t===void 0)throw new Error(`no license server URL configured for key-system "${e}"`);return t}getServerCertificateUrl(e){const{drmSystems:t}=this.config,s=t==null?void 0:t[e];if(s)return s.serverCertificateUrl;this.log(`No Server Certificate in config.drmSystems["${e}"]`)}attemptKeySystemAccess(e){const t=this.hls.levels,s=(n,o,c)=>!!n&&c.indexOf(n)===o,i=t.map(n=>n.audioCodec).filter(s),r=t.map(n=>n.videoCodec).filter(s);return i.length+r.length===0&&r.push("avc1.42e01e"),new Promise((n,o)=>{const c=l=>{const h=l.shift();this.getMediaKeysPromise(h,i,r).then(u=>n({keySystem:h,mediaKeys:u})).catch(u=>{l.length?c(l):u instanceof Ie?o(u):o(new Ie({type:H.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_NO_ACCESS,error:u,fatal:!0},u.message))})};c(e)})}requestMediaKeySystemAccess(e,t){const{requestMediaKeySystemAccessFunc:s}=this.config;if(typeof s!="function"){let i=`Configured requestMediaKeySystemAccess is not a function ${s}`;return Yn===null&&self.location.protocol==="http:"&&(i=`navigator.requestMediaKeySystemAccess is not available over insecure protocol ${location.protocol}`),Promise.reject(new Error(i))}return s(e,t)}getMediaKeysPromise(e,t,s){var i;const r=xl(e,t,s,this.config.drmSystemOptions||{});let n=this.keySystemAccessPromises[e],o=(i=n)==null?void 0:i.keySystemAccess;if(!o){this.log(`Requesting encrypted media "${e}" key-system access with config: ${oe(r)}`),o=this.requestMediaKeySystemAccess(e,r);const c=n=this.keySystemAccessPromises[e]={keySystemAccess:o};return o.catch(l=>{this.log(`Failed to obtain access to key-system "${e}": ${l}`)}),o.then(l=>{this.log(`Access for key-system "${l.keySystem}" obtained`);const h=this.fetchServerCertificate(e);this.log(`Create media-keys for "${e}"`);const u=c.mediaKeys=l.createMediaKeys().then(d=>(this.log(`Media-keys created for "${e}"`),c.hasMediaKeys=!0,h.then(f=>f?this.setMediaKeysServerCertificate(d,e,f):d)));return u.catch(d=>{this.error(`Failed to create media-keys for "${e}"}: ${d}`)}),u})}return o.then(()=>n.mediaKeys)}createMediaKeySessionContext({decryptdata:e,keySystem:t,mediaKeys:s}){this.log(`Creating key-system session "${t}" keyId: ${ye(e.keyId||[])} keyUri: ${e.uri}`);const i=s.createSession(),r={decryptdata:e,keySystem:t,mediaKeys:s,mediaKeysSession:i,keyStatus:"status-pending"};return this.mediaKeySessions.push(r),r}renewKeySession(e){const t=e.decryptdata;if(t.pssh){const s=this.createMediaKeySessionContext(e),i=is(t),r="cenc";this.keyIdToKeySessionPromise[i]=this.generateRequestWithPreferredKeySession(s,r,t.pssh.buffer,"expired")}else this.warn("Could not renew expired session. Missing pssh initData.");this.removeSession(e)}updateKeySession(e,t){const s=e.mediaKeysSession;return this.log(`Updating key-session "${s.sessionId}" for keyId ${ye(e.decryptdata.keyId||[])} - } (data length: ${t.byteLength})`),s.update(t)}getSelectedKeySystemFormats(){return Object.keys(this.keySystemAccessPromises).map(e=>({keySystem:e,hasMediaKeys:this.keySystemAccessPromises[e].hasMediaKeys})).filter(({hasMediaKeys:e})=>!!e).map(({keySystem:e})=>Ns(e)).filter(e=>!!e)}getKeySystemAccess(e){return this.getKeySystemSelectionPromise(e).then(({keySystem:t,mediaKeys:s})=>this.attemptSetMediaKeys(t,s))}selectKeySystem(e){return new Promise((t,s)=>{this.getKeySystemSelectionPromise(e).then(({keySystem:i})=>{const r=Ns(i);r?t(r):s(new Error(`Unable to find format for key-system "${i}"`))}).catch(s)})}selectKeySystemFormat(e){const t=Object.keys(e.levelkeys||{});return this.keyFormatPromise||(this.log(`Selecting key-system from fragment (sn: ${e.sn} ${e.type}: ${e.level}) key formats ${t.join(", ")}`),this.keyFormatPromise=this.getKeyFormatPromise(t)),this.keyFormatPromise}getKeyFormatPromise(e){const t=Ft(this.config),s=e.map(us).filter(i=>!!i&&t.indexOf(i)!==-1);return this.selectKeySystem(s)}getKeyStatus(e){const{mediaKeySessions:t}=this;for(let s=0;s(this.throwIfDestroyed(),this.log(`Handle encrypted media sn: ${e.frag.sn} ${e.frag.type}: ${e.frag.level} using key ${r}`),this.attemptSetMediaKeys(c,l).then(()=>(this.throwIfDestroyed(),this.createMediaKeySessionContext({keySystem:c,mediaKeys:l,decryptdata:t}))))).then(c=>{const l="cenc",h=t.pssh?t.pssh.buffer:null;return this.generateRequestWithPreferredKeySession(c,l,h,"playlist-key")});return o.catch(c=>this.handleError(c,e.frag)),this.keyIdToKeySessionPromise[s]=o,o}return n.catch(o=>{if(o instanceof Ie){const c=ie({},o.data);this.getKeyStatus(t)==="internal-error"&&(c.decryptdata=t);const l=new Ie(c,o.message);this.handleError(l,e.frag)}}),n}throwIfDestroyed(e="Invalid state"){if(!this.hls)throw new Error("invalid state")}handleError(e,t){if(this.hls)if(e instanceof Ie){t&&(e.data.frag=t);const s=e.data.decryptdata;this.error(`${e.message}${s?` (${ye(s.keyId||[])})`:""}`),this.hls.trigger(m.ERROR,e.data)}else this.error(e.message),this.hls.trigger(m.ERROR,{type:H.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_NO_KEYS,error:e,fatal:!0})}getKeySystemForKeyPromise(e){const t=is(e),s=this.keyIdToKeySessionPromise[t];if(!s){const i=us(e.keyFormat),r=i?[i]:Ft(this.config);return this.attemptKeySystemAccess(r)}return s}getKeySystemSelectionPromise(e){if(e.length||(e=Ft(this.config)),e.length===0)throw new Ie({type:H.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_NO_CONFIGURED_LICENSE,fatal:!0},`Missing key-system license configuration options ${oe({drmSystems:this.config.drmSystems})}`);return this.attemptKeySystemAccess(e)}attemptSetMediaKeys(e,t){if(this.mediaResolved=void 0,this.mediaKeys===t)return Promise.resolve();const s=this.setMediaKeysQueue.slice();this.log(`Setting media-keys for "${e}"`);const i=Promise.all(s).then(()=>this.media?this.media.setMediaKeys(t):new Promise((r,n)=>{this.mediaResolved=()=>{if(this.mediaResolved=void 0,!this.media)return n(new Error("Attempted to set mediaKeys without media element attached"));this.mediaKeys=t,this.media.setMediaKeys(t).then(r).catch(n)}}));return this.mediaKeys=t,this.setMediaKeysQueue.push(i),i.then(()=>{this.log(`Media-keys set for "${e}"`),s.push(i),this.setMediaKeysQueue=this.setMediaKeysQueue.filter(r=>s.indexOf(r)===-1)})}generateRequestWithPreferredKeySession(e,t,s,i){var r;const n=(r=this.config.drmSystems)==null||(r=r[e.keySystem])==null?void 0:r.generateRequest;if(n)try{const g=n.call(this.hls,t,s,e);if(!g)throw new Error("Invalid response from configured generateRequest filter");t=g.initDataType,s=g.initData?g.initData:null,e.decryptdata.pssh=s?new Uint8Array(s):null}catch(g){if(this.warn(g.message),this.hls&&this.hls.config.debug)throw g}if(s===null)return this.log(`Skipping key-session request for "${i}" (no initData)`),Promise.resolve(e);const o=is(e.decryptdata),c=e.decryptdata.uri;this.log(`Generating key-session request for "${i}" keyId: ${o} URI: ${c} (init data type: ${t} length: ${s.byteLength})`);const l=new Oi,h=e._onmessage=g=>{const y=e.mediaKeysSession;if(!y){l.emit("error",new Error("invalid state"));return}const{messageType:p,message:E}=g;this.log(`"${p}" message event for session "${y.sessionId}" message size: ${E.byteLength}`),p==="license-request"||p==="license-renewal"?this.renewLicense(e,E).catch(T=>{l.eventNames().length?l.emit("error",T):this.handleError(T)}):p==="license-release"?e.keySystem===ce.FAIRPLAY&&this.updateKeySession(e,ci("acknowledged")).then(()=>this.removeSession(e)).catch(T=>this.handleError(T)):this.warn(`unhandled media key message type "${p}"`)},u=(g,y)=>{y.keyStatus=g;let p;g.startsWith("usable")?l.emit("resolved"):g==="internal-error"||g==="output-restricted"||g==="output-downscaled"?p=en(g,y.decryptdata):g==="expired"?p=new Error(`key expired (keyId: ${o})`):g==="released"?p=new Error("key released"):g==="status-pending"||this.warn(`unhandled key status change "${g}" (keyId: ${o})`),p&&(l.eventNames().length?l.emit("error",p):this.handleError(p))},d=e._onkeystatuseschange=g=>{if(!e.mediaKeysSession){l.emit("error",new Error("invalid state"));return}const p=this.getKeyStatuses(e);if(!Object.keys(p).some(v=>p[v]!=="status-pending"))return;if(p[o]==="expired"){this.log(`Expired key ${oe(p)} in key-session "${e.mediaKeysSession.sessionId}"`),this.renewKeySession(e);return}let T=p[o];if(T)u(T,e);else{var S;e.keyStatusTimeouts||(e.keyStatusTimeouts={}),(S=e.keyStatusTimeouts)[o]||(S[o]=self.setTimeout(()=>{if(!e.mediaKeysSession||!this.mediaKeys)return;const x=this.getKeyStatus(e.decryptdata);if(x&&x!=="status-pending")return this.log(`No status for keyId ${o} in key-session "${e.mediaKeysSession.sessionId}". Using session key-status ${x} from other session.`),u(x,e);this.log(`key status for ${o} in key-session "${e.mediaKeysSession.sessionId}" timed out after 1000ms`),T="internal-error",u(T,e)},1e3)),this.log(`No status for keyId ${o} (${oe(p)}).`)}};ve(e.mediaKeysSession,"message",h),ve(e.mediaKeysSession,"keystatuseschange",d);const f=new Promise((g,y)=>{l.on("error",y),l.on("resolved",g)});return e.mediaKeysSession.generateRequest(t,s).then(()=>{this.log(`Request generated for key-session "${e.mediaKeysSession.sessionId}" keyId: ${o} URI: ${c}`)}).catch(g=>{throw new Ie({type:H.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_NO_SESSION,error:g,decryptdata:e.decryptdata,fatal:!1},`Error generating key-session request: ${g}`)}).then(()=>f).catch(g=>(l.removeAllListeners(),this.removeSession(e).then(()=>{throw g}))).then(()=>(l.removeAllListeners(),e))}getKeyStatuses(e){const t={};return e.mediaKeysSession.keyStatuses.forEach((s,i)=>{if(typeof i=="string"&&typeof s=="object"){const o=i;i=s,s=o}const r="buffer"in i?new Uint8Array(i.buffer,i.byteOffset,i.byteLength):new Uint8Array(i);if(e.keySystem===ce.PLAYREADY&&r.length===16){const o=ye(r);t[o]=s,Vn(r)}const n=ye(r);s==="internal-error"&&(this.bannedKeyIds[n]=s),this.log(`key status change "${s}" for keyStatuses keyId: ${n} key-session "${e.mediaKeysSession.sessionId}"`),t[n]=s}),t}fetchServerCertificate(e){const t=this.config,s=t.loader,i=new s(t),r=this.getServerCertificateUrl(e);return r?(this.log(`Fetching server certificate for "${e}"`),new Promise((n,o)=>{const c={responseType:"arraybuffer",url:r},l=t.certLoadPolicy.default,h={loadPolicy:l,timeout:l.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0},u={onSuccess:(d,f,g,y)=>{n(d.data)},onError:(d,f,g,y)=>{o(new Ie({type:H.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED,fatal:!0,networkDetails:g,response:ie({url:c.url,data:void 0},d)},`"${e}" certificate request failed (${r}). Status: ${d.code} (${d.text})`))},onTimeout:(d,f,g)=>{o(new Ie({type:H.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED,fatal:!0,networkDetails:g,response:{url:c.url,data:void 0}},`"${e}" certificate request timed out (${r})`))},onAbort:(d,f,g)=>{o(new Error("aborted"))}};i.load(c,h,u)})):Promise.resolve()}setMediaKeysServerCertificate(e,t,s){return new Promise((i,r)=>{e.setServerCertificate(s).then(n=>{this.log(`setServerCertificate ${n?"success":"not supported by CDM"} (${s.byteLength}) on "${t}"`),i(e)}).catch(n=>{r(new Ie({type:H.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED,error:n,fatal:!0},n.message))})})}renewLicense(e,t){return this.requestLicense(e,new Uint8Array(t)).then(s=>this.updateKeySession(e,new Uint8Array(s)).catch(i=>{throw new Ie({type:H.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_SESSION_UPDATE_FAILED,decryptdata:e.decryptdata,error:i,fatal:!1},i.message)}))}unpackPlayReadyKeyMessage(e,t){const s=String.fromCharCode.apply(null,new Uint16Array(t.buffer));if(!s.includes("PlayReadyKeyMessage"))return e.setRequestHeader("Content-Type","text/xml; charset=utf-8"),t;const i=new DOMParser().parseFromString(s,"application/xml"),r=i.querySelectorAll("HttpHeader");if(r.length>0){let h;for(let u=0,d=r.length;u in key message");return ci(atob(l))}setupLicenseXHR(e,t,s,i){const r=this.config.licenseXhrSetup;return r?Promise.resolve().then(()=>{if(!s.decryptdata)throw new Error("Key removed");return r.call(this.hls,e,t,s,i)}).catch(n=>{if(!s.decryptdata)throw n;return e.open("POST",t,!0),r.call(this.hls,e,t,s,i)}).then(n=>(e.readyState||e.open("POST",t,!0),{xhr:e,licenseChallenge:n||i})):(e.open("POST",t,!0),Promise.resolve({xhr:e,licenseChallenge:i}))}requestLicense(e,t){const s=this.config.keyLoadPolicy.default;return new Promise((i,r)=>{const n=this.getLicenseServerUrlOrThrow(e.keySystem);this.log(`Sending license request to URL: ${n}`);const o=new XMLHttpRequest;o.responseType="arraybuffer",o.onreadystatechange=()=>{if(!this.hls||!e.mediaKeysSession)return r(new Error("invalid state"));if(o.readyState===4)if(o.status===200){this._requestLicenseFailureCount=0;let c=o.response;this.log(`License received ${c instanceof ArrayBuffer?c.byteLength:c}`);const l=this.config.licenseResponseCallback;if(l)try{c=l.call(this.hls,o,n,e)}catch(h){this.error(h)}i(c)}else{const c=s.errorRetry,l=c?c.maxNumRetry:0;if(this._requestLicenseFailureCount++,this._requestLicenseFailureCount>l||o.status>=400&&o.status<500)r(new Ie({type:H.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_LICENSE_REQUEST_FAILED,decryptdata:e.decryptdata,fatal:!0,networkDetails:o,response:{url:n,data:void 0,code:o.status,text:o.statusText}},`License Request XHR failed (${n}). Status: ${o.status} (${o.statusText})`));else{const h=l-this._requestLicenseFailureCount+1;this.warn(`Retrying license request, ${h} attempts left`),this.requestLicense(e,t).then(i,r)}}},e.licenseXhr&&e.licenseXhr.readyState!==XMLHttpRequest.DONE&&e.licenseXhr.abort(),e.licenseXhr=o,this.setupLicenseXHR(o,n,e,t).then(({xhr:c,licenseChallenge:l})=>{e.keySystem==ce.PLAYREADY&&(l=this.unpackPlayReadyKeyMessage(c,l)),c.send(l)}).catch(r)})}onDestroying(){this.unregisterListeners(),this._clear()}onMediaAttached(e,t){if(!this.config.emeEnabled)return;const s=t.media;this.media=s,ve(s,"encrypted",this.onMediaEncrypted),ve(s,"waitingforkey",this.onWaitingForKey);const i=this.mediaResolved;i?i():this.mediaKeys=s.mediaKeys}onMediaDetached(){const e=this.media;e&&(Ae(e,"encrypted",this.onMediaEncrypted),Ae(e,"waitingforkey",this.onWaitingForKey),this.media=null,this.mediaKeys=null)}_clear(){var e;this._requestLicenseFailureCount=0,this.keyIdToKeySessionPromise={},this.bannedKeyIds={};const t=this.mediaResolved;if(t&&t(),!this.mediaKeys&&!this.mediaKeySessions.length)return;const s=this.media,i=this.mediaKeySessions.slice();this.mediaKeySessions=[],this.mediaKeys=null,rt.clearKeyUriToKeyIdMap();const r=i.length;_t.CDMCleanupPromise=Promise.all(i.map(n=>this.removeSession(n)).concat((s==null||(e=s.setMediaKeys(null))==null?void 0:e.catch(n=>{this.log(`Could not clear media keys: ${n}`),this.hls&&this.hls.trigger(m.ERROR,{type:H.OTHER_ERROR,details:R.KEY_SYSTEM_DESTROY_MEDIA_KEYS_ERROR,fatal:!1,error:new Error(`Could not clear media keys: ${n}`)})}))||Promise.resolve())).catch(n=>{this.log(`Could not close sessions and clear media keys: ${n}`),this.hls&&this.hls.trigger(m.ERROR,{type:H.OTHER_ERROR,details:R.KEY_SYSTEM_DESTROY_CLOSE_SESSION_ERROR,fatal:!1,error:new Error(`Could not close sessions and clear media keys: ${n}`)})}).then(()=>{r&&this.log("finished closing key sessions and clearing media keys")})}onManifestLoading(){this._clear()}onManifestLoaded(e,{sessionKeys:t}){if(!(!t||!this.config.emeEnabled)&&!this.keyFormatPromise){const s=t.reduce((i,r)=>(i.indexOf(r.keyFormat)===-1&&i.push(r.keyFormat),i),[]);this.log(`Selecting key-system from session-keys ${s.join(", ")}`),this.keyFormatPromise=this.getKeyFormatPromise(s)}}removeSession(e){const{mediaKeysSession:t,licenseXhr:s,decryptdata:i}=e;if(t){this.log(`Remove licenses and keys and close session "${t.sessionId}" keyId: ${ye((i==null?void 0:i.keyId)||[])}`),e._onmessage&&(t.removeEventListener("message",e._onmessage),e._onmessage=void 0),e._onkeystatuseschange&&(t.removeEventListener("keystatuseschange",e._onkeystatuseschange),e._onkeystatuseschange=void 0),s&&s.readyState!==XMLHttpRequest.DONE&&s.abort(),e.mediaKeysSession=e.decryptdata=e.licenseXhr=void 0;const r=this.mediaKeySessions.indexOf(e);r>-1&&this.mediaKeySessions.splice(r,1);const{keyStatusTimeouts:n}=e;n&&Object.keys(n).forEach(l=>self.clearTimeout(n[l]));const{drmSystemOptions:o}=this.config;return(Il(o)?new Promise((l,h)=>{self.setTimeout(()=>h(new Error("MediaKeySession.remove() timeout")),8e3),t.remove().then(l).catch(h)}):Promise.resolve()).catch(l=>{this.log(`Could not remove session: ${l}`),this.hls&&this.hls.trigger(m.ERROR,{type:H.OTHER_ERROR,details:R.KEY_SYSTEM_DESTROY_REMOVE_SESSION_ERROR,fatal:!1,error:new Error(`Could not remove session: ${l}`)})}).then(()=>t.close()).catch(l=>{this.log(`Could not close session: ${l}`),this.hls&&this.hls.trigger(m.ERROR,{type:H.OTHER_ERROR,details:R.KEY_SYSTEM_DESTROY_CLOSE_SESSION_ERROR,fatal:!1,error:new Error(`Could not close session: ${l}`)})})}return Promise.resolve()}}_t.CDMCleanupPromise=void 0;function is(a){if(!a)throw new Error("Could not read keyId of undefined decryptdata");if(a.keyId===null)throw new Error("keyId is null");return ye(a.keyId)}function Vh(a,e){if(a.keyId&&e.mediaKeysSession.keyStatuses.has(a.keyId))return e.mediaKeysSession.keyStatuses.get(a.keyId);if(a.matches(e.decryptdata))return e.keyStatus}class Ie extends Error{constructor(e,t){super(t),this.data=void 0,e.error||(e.error=new Error(t)),this.data=e,e.err=e.error}}function en(a,e){const t=a==="output-restricted",s=t?R.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED:R.KEY_SYSTEM_STATUS_INTERNAL_ERROR;return new Ie({type:H.KEY_SYSTEM_ERROR,details:s,fatal:!1,decryptdata:e},t?"HDCP level output restricted":`key status changed to "${a}"`)}class Hh{constructor(e){this.hls=void 0,this.isVideoPlaybackQualityAvailable=!1,this.timer=void 0,this.media=null,this.lastTime=void 0,this.lastDroppedFrames=0,this.lastDecodedFrames=0,this.streamController=void 0,this.hls=e,this.registerListeners()}setStreamController(e){this.streamController=e}registerListeners(){this.hls.on(m.MEDIA_ATTACHING,this.onMediaAttaching,this),this.hls.on(m.MEDIA_DETACHING,this.onMediaDetaching,this)}unregisterListeners(){this.hls.off(m.MEDIA_ATTACHING,this.onMediaAttaching,this),this.hls.off(m.MEDIA_DETACHING,this.onMediaDetaching,this)}destroy(){this.timer&&clearInterval(this.timer),this.unregisterListeners(),this.isVideoPlaybackQualityAvailable=!1,this.media=null}onMediaAttaching(e,t){const s=this.hls.config;if(s.capLevelOnFPSDrop){const i=t.media instanceof self.HTMLVideoElement?t.media:null;this.media=i,i&&typeof i.getVideoPlaybackQuality=="function"&&(this.isVideoPlaybackQualityAvailable=!0),self.clearInterval(this.timer),this.timer=self.setInterval(this.checkFPSInterval.bind(this),s.fpsDroppedMonitoringPeriod)}}onMediaDetaching(){this.media=null}checkFPS(e,t,s){const i=performance.now();if(t){if(this.lastTime){const r=i-this.lastTime,n=s-this.lastDroppedFrames,o=t-this.lastDecodedFrames,c=1e3*n/r,l=this.hls;if(l.trigger(m.FPS_DROP,{currentDropped:n,currentDecoded:o,totalDroppedFrames:s}),c>0&&n>l.config.fpsDroppedMonitoringThreshold*o){let h=l.currentLevel;l.logger.warn("drop FPS ratio greater than max allowed value for currentLevel: "+h),h>0&&(l.autoLevelCapping===-1||l.autoLevelCapping>=h)&&(h=h-1,l.trigger(m.FPS_DROP_LEVEL_CAPPING,{level:h,droppedLevel:l.currentLevel}),l.autoLevelCapping=h,this.streamController.nextLevelSwitch())}}this.lastTime=i,this.lastDroppedFrames=s,this.lastDecodedFrames=t}}checkFPSInterval(){const e=this.media;if(e)if(this.isVideoPlaybackQualityAvailable){const t=e.getVideoPlaybackQuality();this.checkFPS(e,t.totalVideoFrames,t.droppedVideoFrames)}else this.checkFPS(e,e.webkitDecodedFrameCount,e.webkitDroppedFrameCount)}}function Fa(a,e){let t;try{t=new Event("addtrack")}catch{t=document.createEvent("Event"),t.initEvent("addtrack",!1,!1)}t.track=a,e.dispatchEvent(t)}function Ma(a,e){const t=a.mode;if(t==="disabled"&&(a.mode="hidden"),a.cues&&!a.cues.getCueById(e.id))try{if(a.addCue(e),!a.cues.getCueById(e.id))throw new Error(`addCue is failed for: ${e}`)}catch(s){re.debug(`[texttrack-utils]: ${s}`);try{const i=new self.TextTrackCue(e.startTime,e.endTime,e.text);i.id=e.id,a.addCue(i)}catch(i){re.debug(`[texttrack-utils]: Legacy TextTrackCue fallback failed: ${i}`)}}t==="disabled"&&(a.mode=t)}function It(a,e){const t=a.mode;if(t==="disabled"&&(a.mode="hidden"),a.cues)for(let s=a.cues.length;s--;)e&&a.cues[s].removeEventListener("enter",e),a.removeCue(a.cues[s]);t==="disabled"&&(a.mode=t)}function Ei(a,e,t,s){const i=a.mode;if(i==="disabled"&&(a.mode="hidden"),a.cues&&a.cues.length>0){const r=Wh(a.cues,e,t);for(let n=0;na[t].endTime)return-1;let s=0,i=t,r;for(;s<=i;)if(r=Math.floor((i+s)/2),ea[r].startTime&&s-1)for(let r=i,n=a.length;r=e&&o.endTime<=t)s.push(o);else if(o.startTime>t)return s}return s}function ms(a){const e=[];for(let t=0;tthis.pollTrackChange(0),this.onTextTracksChanged=()=>{if(this.useTextTrackPolling||self.clearInterval(this.subtitlePollingInterval),!this.media||!this.hls.config.renderTextTracksNatively)return;let t=null;const s=ms(this.media.textTracks);for(let r=0;r-1&&this.toggleTrackModes()}registerListeners(){const{hls:e}=this;e.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.MANIFEST_PARSED,this.onManifestParsed,this),e.on(m.LEVEL_LOADING,this.onLevelLoading,this),e.on(m.LEVEL_SWITCHING,this.onLevelSwitching,this),e.on(m.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),e.on(m.ERROR,this.onError,this)}unregisterListeners(){const{hls:e}=this;e.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.MANIFEST_PARSED,this.onManifestParsed,this),e.off(m.LEVEL_LOADING,this.onLevelLoading,this),e.off(m.LEVEL_SWITCHING,this.onLevelSwitching,this),e.off(m.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),e.off(m.ERROR,this.onError,this)}onMediaAttached(e,t){this.media=t.media,this.media&&(this.queuedDefaultTrack>-1&&(this.subtitleTrack=this.queuedDefaultTrack,this.queuedDefaultTrack=-1),this.useTextTrackPolling=!(this.media.textTracks&&"onchange"in this.media.textTracks),this.useTextTrackPolling?this.pollTrackChange(500):this.media.textTracks.addEventListener("change",this.asyncPollTrackChange))}pollTrackChange(e){self.clearInterval(this.subtitlePollingInterval),this.subtitlePollingInterval=self.setInterval(this.onTextTracksChanged,e)}onMediaDetaching(e,t){const s=this.media;if(!s)return;const i=!!t.transferMedia;if(self.clearInterval(this.subtitlePollingInterval),this.useTextTrackPolling||s.textTracks.removeEventListener("change",this.asyncPollTrackChange),this.trackId>-1&&(this.queuedDefaultTrack=this.trackId),this.subtitleTrack=-1,this.media=null,i)return;ms(s.textTracks).forEach(n=>{It(n)})}onManifestLoading(){this.tracks=[],this.groupIds=null,this.tracksInGroup=[],this.trackId=-1,this.currentTrack=null,this.selectDefaultTrack=!0}onManifestParsed(e,t){this.tracks=t.subtitleTracks}onSubtitleTrackLoaded(e,t){const{id:s,groupId:i,details:r}=t,n=this.tracksInGroup[s];if(!n||n.groupId!==i){this.warn(`Subtitle track with id:${s} and group:${i} not found in active group ${n==null?void 0:n.groupId}`);return}const o=n.details;n.details=t.details,this.log(`Subtitle track ${s} "${n.name}" lang:${n.lang} group:${i} loaded [${r.startSN}-${r.endSN}]`),s===this.trackId&&this.playlistLoaded(s,t,o)}onLevelLoading(e,t){this.switchLevel(t.level)}onLevelSwitching(e,t){this.switchLevel(t.level)}switchLevel(e){const t=this.hls.levels[e];if(!t)return;const s=t.subtitleGroups||null,i=this.groupIds;let r=this.currentTrack;if(!s||(i==null?void 0:i.length)!==(s==null?void 0:s.length)||s!=null&&s.some(n=>(i==null?void 0:i.indexOf(n))===-1)){this.groupIds=s,this.trackId=-1,this.currentTrack=null;const n=this.tracks.filter(h=>!s||s.indexOf(h.groupId)!==-1);if(n.length)this.selectDefaultTrack&&!n.some(h=>h.default)&&(this.selectDefaultTrack=!1),n.forEach((h,u)=>{h.id=u});else if(!r&&!this.tracksInGroup.length)return;this.tracksInGroup=n;const o=this.hls.config.subtitlePreference;if(!r&&o){this.selectDefaultTrack=!1;const h=He(o,n);if(h>-1)r=n[h];else{const u=He(o,this.tracks);r=this.tracks[u]}}let c=this.findTrackId(r);c===-1&&r&&(c=this.findTrackId(null));const l={subtitleTracks:n};this.log(`Updating subtitle tracks, ${n.length} track(s) found in "${s==null?void 0:s.join(",")}" group-id`),this.hls.trigger(m.SUBTITLE_TRACKS_UPDATED,l),c!==-1&&this.trackId===-1&&this.setSubtitleTrack(c)}}findTrackId(e){const t=this.tracksInGroup,s=this.selectDefaultTrack;for(let i=0;i-1){const r=this.tracksInGroup[i];return this.setSubtitleTrack(i),r}else{if(s)return null;{const r=He(e,t);if(r>-1)return t[r]}}}}return null}loadPlaylist(e){super.loadPlaylist(),this.shouldLoadPlaylist(this.currentTrack)&&this.scheduleLoading(this.currentTrack,e)}loadingPlaylist(e,t){super.loadingPlaylist(e,t);const s=e.id,i=e.groupId,r=this.getUrlWithDirectives(e.url,t),n=e.details,o=n==null?void 0:n.age;this.log(`Loading subtitle ${s} "${e.name}" lang:${e.lang} group:${i}${(t==null?void 0:t.msn)!==void 0?" at sn "+t.msn+" part "+t.part:""}${o&&n.live?" age "+o.toFixed(1)+(n.type&&" "+n.type||""):""} ${r}`),this.hls.trigger(m.SUBTITLE_TRACK_LOADING,{url:r,id:s,groupId:i,deliveryDirectives:t||null,track:e})}toggleTrackModes(){const{media:e}=this;if(!e)return;const t=ms(e.textTracks),s=this.currentTrack;let i;if(s&&(i=t.filter(r=>gi(s,r))[0],i||this.warn(`Unable to find subtitle TextTrack with name "${s.name}" and language "${s.lang}"`)),[].slice.call(t).forEach(r=>{r.mode!=="disabled"&&r!==i&&(r.mode="disabled")}),i){const r=this.subtitleDisplay?"showing":"hidden";i.mode!==r&&(i.mode=r)}}setSubtitleTrack(e){const t=this.tracksInGroup;if(!this.media){this.queuedDefaultTrack=e;return}if(e<-1||e>=t.length||!N(e)){this.warn(`Invalid subtitle track id: ${e}`);return}this.selectDefaultTrack=!1;const s=this.currentTrack,i=t[e]||null;if(this.trackId=e,this.currentTrack=i,this.toggleTrackModes(),!i){this.hls.trigger(m.SUBTITLE_TRACK_SWITCH,{id:e});return}const r=!!i.details&&!i.details.live;if(e===this.trackId&&i===s&&r)return;this.log(`Switching to subtitle-track ${e}`+(i?` "${i.name}" lang:${i.lang} group:${i.groupId}`:""));const{id:n,groupId:o="",name:c,type:l,url:h}=i;this.hls.trigger(m.SUBTITLE_TRACK_SWITCH,{id:n,groupId:o,name:c,type:l,url:h});const u=this.switchParams(i.url,s==null?void 0:s.details,i.details);this.loadPlaylist(u)}}function jh(){try{return crypto.randomUUID()}catch{try{const e=URL.createObjectURL(new Blob),t=e.toString();return URL.revokeObjectURL(e),t.slice(t.lastIndexOf("/")+1)}catch{let t=new Date().getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,i=>{const r=(t+Math.random()*16)%16|0;return t=Math.floor(t/16),(i=="x"?r:r&3|8).toString(16)})}}}function Ut(a){let e=5381,t=a.length;for(;t;)e=e*33^a.charCodeAt(--t);return(e>>>0).toString()}const Dt=.025;let bs=function(a){return a[a.Point=0]="Point",a[a.Range=1]="Range",a}({});function Xh(a,e,t){return`${a.identifier}-${t+1}-${Ut(e)}`}class zh{constructor(e,t){this.base=void 0,this._duration=null,this._timelineStart=null,this.appendInPlaceDisabled=void 0,this.appendInPlaceStarted=void 0,this.dateRange=void 0,this.hasPlayed=!1,this.cumulativeDuration=0,this.resumeOffset=NaN,this.playoutLimit=NaN,this.restrictions={skip:!1,jump:!1},this.snapOptions={out:!1,in:!1},this.assetList=[],this.assetListLoader=void 0,this.assetListResponse=null,this.resumeAnchor=void 0,this.error=void 0,this.resetOnResume=void 0,this.base=t,this.dateRange=e,this.setDateRange(e)}setDateRange(e){this.dateRange=e,this.resumeOffset=e.attr.optionalFloat("X-RESUME-OFFSET",this.resumeOffset),this.playoutLimit=e.attr.optionalFloat("X-PLAYOUT-LIMIT",this.playoutLimit),this.restrictions=e.attr.enumeratedStringList("X-RESTRICT",this.restrictions),this.snapOptions=e.attr.enumeratedStringList("X-SNAP",this.snapOptions)}reset(){var e;this.appendInPlaceStarted=!1,(e=this.assetListLoader)==null||e.destroy(),this.assetListLoader=void 0,this.supplementsPrimary||(this.assetListResponse=null,this.assetList=[],this._duration=null)}isAssetPastPlayoutLimit(e){var t;if(e>0&&e>=this.assetList.length)return!0;const s=this.playoutLimit;return e<=0||isNaN(s)?!1:s===0?!0:(((t=this.assetList[e])==null?void 0:t.startOffset)||0)>s}findAssetIndex(e){return this.assetList.indexOf(e)}get identifier(){return this.dateRange.id}get startDate(){return this.dateRange.startDate}get startTime(){const e=this.dateRange.startTime;if(this.snapOptions.out){const t=this.dateRange.tagAnchor;if(t)return Ws(e,t)}return e}get startOffset(){return this.cue.pre?0:this.startTime}get startIsAligned(){if(this.startTime===0||this.snapOptions.out)return!0;const e=this.dateRange.tagAnchor;if(e){const t=this.dateRange.startTime,s=Ws(t,e);return t-s<.1}return!1}get resumptionOffset(){const e=this.resumeOffset,t=N(e)?e:this.duration;return this.cumulativeDuration+t}get resumeTime(){const e=this.startOffset+this.resumptionOffset;if(this.snapOptions.in){const t=this.resumeAnchor;if(t)return Ws(e,t)}return e}get appendInPlace(){return this.appendInPlaceStarted?!0:this.appendInPlaceDisabled?!1:!!(!this.cue.once&&!this.cue.pre&&this.startIsAligned&&(isNaN(this.playoutLimit)&&isNaN(this.resumeOffset)||this.resumeOffset&&this.duration&&Math.abs(this.resumeOffset-this.duration)0||this.assetListResponse!==null}toString(){return Qh(this)}}function Ws(a,e){return a-e.start":a.cue.post?"":""}${a.timelineStart.toFixed(2)}-${a.resumeTime.toFixed(2)}]`}function At(a){const e=a.timelineStart,t=a.duration||0;return`["${a.identifier}" ${e.toFixed(2)}-${(e+t).toFixed(2)}]`}class Zh{constructor(e,t,s,i){this.hls=void 0,this.interstitial=void 0,this.assetItem=void 0,this.tracks=null,this.hasDetails=!1,this.mediaAttached=null,this._currentTime=void 0,this._bufferedEosTime=void 0,this.checkPlayout=()=>{this.reachedPlayout(this.currentTime)&&this.hls&&this.hls.trigger(m.PLAYOUT_LIMIT_REACHED,{})};const r=this.hls=new e(t);this.interstitial=s,this.assetItem=i;const n=()=>{this.hasDetails=!0};r.once(m.LEVEL_LOADED,n),r.once(m.AUDIO_TRACK_LOADED,n),r.once(m.SUBTITLE_TRACK_LOADED,n),r.on(m.MEDIA_ATTACHING,(o,{media:c})=>{this.removeMediaListeners(),this.mediaAttached=c,this.interstitial.playoutLimit&&(c.addEventListener("timeupdate",this.checkPlayout),this.appendInPlace&&r.on(m.BUFFER_APPENDED,()=>{const h=this.bufferedEnd;this.reachedPlayout(h)&&(this._bufferedEosTime=h,r.trigger(m.BUFFERED_TO_END,void 0))}))})}get appendInPlace(){return this.interstitial.appendInPlace}loadSource(){const e=this.hls;if(e)if(e.url)e.levels.length&&!e.started&&e.startLoad(-1,!0);else{let t=this.assetItem.uri;try{t=Na(t,e.config.primarySessionId||"").href}catch{}e.loadSource(t)}}bufferedInPlaceToEnd(e){var t;if(!this.appendInPlace)return!1;if((t=this.hls)!=null&&t.bufferedToEnd)return!0;if(!e)return!1;const s=Math.min(this._bufferedEosTime||1/0,this.duration),i=this.timelineOffset,r=X.bufferInfo(e,i,0);return this.getAssetTime(r.end)>=s-.02}reachedPlayout(e){const s=this.interstitial.playoutLimit;return this.startOffset+e>=s}get destroyed(){var e;return!((e=this.hls)!=null&&e.userConfig)}get assetId(){return this.assetItem.identifier}get interstitialId(){return this.assetItem.parentIdentifier}get media(){var e;return((e=this.hls)==null?void 0:e.media)||null}get bufferedEnd(){const e=this.media||this.mediaAttached;if(!e)return this._bufferedEosTime?this._bufferedEosTime:this.currentTime;const t=X.bufferInfo(e,e.currentTime,.001);return this.getAssetTime(t.end)}get currentTime(){const e=this.media||this.mediaAttached;return e?this.getAssetTime(e.currentTime):this._currentTime||0}get duration(){const e=this.assetItem.duration;if(!e)return 0;const t=this.interstitial.playoutLimit;if(t){const s=t-this.startOffset;if(s>0&&s1/9e4&&this.hls){if(this.hasDetails)throw new Error("Cannot set timelineOffset after playlists are loaded");this.hls.config.timelineOffset=e}}}getAssetTime(e){const t=this.timelineOffset,s=this.duration;return Math.min(Math.max(0,e-t),s)}removeMediaListeners(){const e=this.mediaAttached;e&&(this._currentTime=e.currentTime,this.bufferSnapShot(),e.removeEventListener("timeupdate",this.checkPlayout))}bufferSnapShot(){if(this.mediaAttached){var e;(e=this.hls)!=null&&e.bufferedToEnd&&(this._bufferedEosTime=this.bufferedEnd)}}destroy(){this.removeMediaListeners(),this.hls&&this.hls.destroy(),this.hls=null,this.tracks=this.mediaAttached=this.checkPlayout=null}attachMedia(e){var t;this.loadSource(),(t=this.hls)==null||t.attachMedia(e)}detachMedia(){var e;this.removeMediaListeners(),this.mediaAttached=null,(e=this.hls)==null||e.detachMedia()}resumeBuffering(){var e;(e=this.hls)==null||e.resumeBuffering()}pauseBuffering(){var e;(e=this.hls)==null||e.pauseBuffering()}transferMedia(){var e;return this.bufferSnapShot(),((e=this.hls)==null?void 0:e.transferMedia())||null}resetDetails(){const e=this.hls;if(e&&this.hasDetails){e.stopLoad();const t=s=>delete s.details;e.levels.forEach(t),e.allAudioTracks.forEach(t),e.allSubtitleTracks.forEach(t),this.hasDetails=!1}}on(e,t,s){var i;(i=this.hls)==null||i.on(e,t)}once(e,t,s){var i;(i=this.hls)==null||i.once(e,t)}off(e,t,s){var i;(i=this.hls)==null||i.off(e,t)}toString(){var e;return`HlsAssetPlayer: ${At(this.assetItem)} ${(e=this.hls)==null?void 0:e.sessionId} ${this.appendInPlace?"append-in-place":""}`}}const tn=.033;class Jh extends we{constructor(e,t){super("interstitials-sched",t),this.onScheduleUpdate=void 0,this.eventMap={},this.events=null,this.items=null,this.durations={primary:0,playout:0,integrated:0},this.onScheduleUpdate=e}destroy(){this.reset(),this.onScheduleUpdate=null}reset(){this.eventMap={},this.setDurations(0,0,0),this.events&&this.events.forEach(e=>e.reset()),this.events=this.items=null}resetErrorsInRange(e,t){return this.events?this.events.reduce((s,i)=>e<=i.startOffset&&t>i.startOffset?(delete i.error,s+1):s,0):0}get duration(){const e=this.items;return e?e[e.length-1].end:0}get length(){return this.items?this.items.length:0}getEvent(e){return e&&this.eventMap[e]||null}hasEvent(e){return e in this.eventMap}findItemIndex(e,t){if(e.event)return this.findEventIndex(e.event.identifier);let s=-1;e.nextEvent?s=this.findEventIndex(e.nextEvent.identifier)-1:e.previousEvent&&(s=this.findEventIndex(e.previousEvent.identifier)+1);const i=this.items;if(i)for(i[s]||(t===void 0&&(t=e.start),s=this.findItemIndexAtTime(t));s>=0&&(r=i[s])!=null&&r.event;){var r;s--}return s}findItemIndexAtTime(e,t){const s=this.items;if(s)for(let i=0;ir.start&&e1)for(let r=0;ro&&(t!o.includes(l.identifier)):[];n.length&&n.sort((l,h)=>{const u=l.cue.pre,d=l.cue.post,f=h.cue.pre,g=h.cue.post;if(u&&!f)return-1;if(f&&!u||d&&!g)return 1;if(g&&!d)return-1;if(!u&&!f&&!d&&!g){const y=l.startTime,p=h.startTime;if(y!==p)return y-p}return l.dateRange.tagOrder-h.dateRange.tagOrder}),this.events=n,c.forEach(l=>{this.removeEvent(l)}),this.updateSchedule(e,c)}updateSchedule(e,t=[],s=!1){const i=this.events||[];if(i.length||t.length||this.length<2){const r=this.items,n=this.parseSchedule(i,e);(s||t.length||(r==null?void 0:r.length)!==n.length||n.some((c,l)=>Math.abs(c.playout.start-r[l].playout.start)>.005||Math.abs(c.playout.end-r[l].playout.end)>.005))&&(this.items=n,this.onScheduleUpdate(t,r))}}parseDateRanges(e,t,s){const i=[],r=Object.keys(e);for(let n=0;n!c.error&&!(c.cue.once&&c.hasPlayed)),e.length){this.resolveOffsets(e,t);let c=0,l=0;if(e.forEach((h,u)=>{const d=h.cue.pre,f=h.cue.post,g=e[u-1]||null,y=h.appendInPlace,p=f?r:h.startOffset,E=h.duration,T=h.timelineOccupancy===bs.Range?E:0,S=h.resumptionOffset,v=(g==null?void 0:g.startTime)===p,x=p+h.cumulativeDuration;let D=y?x+E:p+S;if(d||!f&&p<=0){const _=l;l+=T,h.timelineStart=x;const b=n;n+=E,s.push({event:h,start:x,end:D,playout:{start:b,end:n},integrated:{start:_,end:l}})}else if(p<=r){if(!v){const I=p-c;if(I>tn){const P=c,F=l;l+=I;const $=n;n+=I;const Y={previousEvent:e[u-1]||null,nextEvent:h,start:P,end:P+I,playout:{start:$,end:n},integrated:{start:F,end:l}};s.push(Y)}else I>0&&g&&(g.cumulativeDuration+=I,s[s.length-1].end=p)}f&&(D=x),h.timelineStart=x;const _=l;l+=T;const b=n;n+=E,s.push({event:h,start:x,end:D,playout:{start:b,end:n},integrated:{start:_,end:l}})}else return;const A=h.resumeTime;f||A>r?c=r:c=A}),c{const l=o.cue.pre,h=o.cue.post,u=l?0:h?i:o.startTime;this.updateAssetDurations(o),n===u?o.cumulativeDuration=r:(r=0,n=u),!h&&o.snapOptions.in&&(o.resumeAnchor=mt(null,s.fragments,o.startOffset+o.resumptionOffset,0,0)||void 0),o.appendInPlace&&!o.appendInPlaceStarted&&(this.primaryCanResumeInPlaceAt(o,t)||(o.appendInPlace=!1)),!o.appendInPlace&&c+1Dt?(this.log(`"${e.identifier}" resumption ${s} not aligned with estimated timeline end ${i}`),!1):!Object.keys(t).some(n=>{const o=t[n].details,c=o.edge;if(s>=c)return this.log(`"${e.identifier}" resumption ${s} past ${n} playlist end ${c}`),!1;const l=mt(null,o.fragments,s);if(!l)return this.log(`"${e.identifier}" resumption ${s} does not align with any fragments in ${n} playlist (${o.fragStart}-${o.fragmentEnd})`),!0;const h=n==="audio"?.175:0;return Math.abs(l.start-s){const p=d.data,E=p==null?void 0:p.ASSETS;if(!Array.isArray(E)){const T=this.assignAssetListError(e,R.ASSET_LIST_PARSING_ERROR,new Error("Invalid interstitial asset list"),g.url,f,y);this.hls.trigger(m.ERROR,T);return}e.assetListResponse=p,this.hls.trigger(m.ASSET_LIST_LOADED,{event:e,assetListResponse:p,networkDetails:y})},onError:(d,f,g,y)=>{const p=this.assignAssetListError(e,R.ASSET_LIST_LOAD_ERROR,new Error(`Error loading X-ASSET-LIST: HTTP status ${d.code} ${d.text} (${f.url})`),f.url,y,g);this.hls.trigger(m.ERROR,p)},onTimeout:(d,f,g)=>{const y=this.assignAssetListError(e,R.ASSET_LIST_LOAD_TIMEOUT,new Error(`Timeout loading X-ASSET-LIST (${f.url})`),f.url,d,g);this.hls.trigger(m.ERROR,y)}};return o.load(c,h,u),this.hls.trigger(m.ASSET_LIST_LOADING,{event:e}),o}assignAssetListError(e,t,s,i,r,n){return e.error=s,{type:H.NETWORK_ERROR,details:t,fatal:!1,interstitial:e,url:i,error:s,networkDetails:n,stats:r}}}function sn(a){var e;a==null||(e=a.play())==null||e.catch(()=>{})}function xt(a,e){return`[${a}] Advancing timeline position to ${e}`}class tu extends we{constructor(e,t){super("interstitials",e.logger),this.HlsPlayerClass=void 0,this.hls=void 0,this.assetListLoader=void 0,this.mediaSelection=null,this.altSelection=null,this.media=null,this.detachedData=null,this.requiredTracks=null,this.manager=null,this.playerQueue=[],this.bufferedPos=-1,this.timelinePos=-1,this.schedule=void 0,this.playingItem=null,this.bufferingItem=null,this.waitingItem=null,this.endedItem=null,this.playingAsset=null,this.endedAsset=null,this.bufferingAsset=null,this.shouldPlay=!1,this.onPlay=()=>{this.shouldPlay=!0},this.onPause=()=>{this.shouldPlay=!1},this.onSeeking=()=>{const s=this.currentTime;if(s===void 0||this.playbackDisabled||!this.schedule)return;const i=s-this.timelinePos;if(Math.abs(i)<1/7056e5)return;const n=i<=-.01;this.timelinePos===-1&&!this.effectivePlayingItem&&this.checkStart(),this.timelinePos=s,this.bufferedPos=s;const o=this.playingItem;if(!o){this.checkBuffer();return}if(n&&this.schedule.resetErrorsInRange(s,s-i)&&this.updateSchedule(!0),this.checkBuffer(),n&&s=o.end){var c;const f=this.findItemIndex(o);let g=this.schedule.findItemIndexAtTime(s);if(g===-1&&(g=f+(n?-1:1),this.log(`seeked ${n?"back ":""}to position not covered by schedule ${s} (resolving from ${f} to ${g})`)),!this.isInterstitial(o)&&(c=this.media)!=null&&c.paused&&(this.shouldPlay=!1),!n&&g>f){const y=this.schedule.findJumpRestrictedIndex(f+1,g);if(y>f){this.setSchedulePosition(y);return}}this.setSchedulePosition(g);return}const l=this.playingAsset;if(!l){if(this.playingLastItem&&this.isInterstitial(o)){const f=o.event.assetList[0];f&&(this.endedItem=this.playingItem,this.playingItem=null,this.setScheduleToAssetAtTime(s,f))}return}const h=l.timelineStart,u=l.duration||0;if(n&&s=h+u){var d;(d=o.event)!=null&&d.appendInPlace&&(this.clearAssetPlayers(o.event,o),this.flushFrontBuffer(s)),this.setScheduleToAssetAtTime(s,l)}},this.onTimeupdate=()=>{const s=this.currentTime;if(s===void 0||this.playbackDisabled)return;if(this.timelinePos===-1&&!this.effectivePlayingItem&&this.checkStart(),s>this.timelinePos)this.timelinePos=s,s>this.bufferedPos&&this.checkBuffer();else return;const i=this.playingItem;if(!i||this.playingLastItem)return;if(s>=i.end){this.timelinePos=i.end;const o=this.findItemIndex(i);this.setSchedulePosition(o+1)}const r=this.playingAsset;if(!r)return;const n=r.timelineStart+(r.duration||0);s>=n&&this.setScheduleToAssetAtTime(s,r)},this.onScheduleUpdate=(s,i)=>{const r=this.schedule;if(!r)return;const n=this.playingItem,o=r.events||[],c=r.items||[],l=r.durations,h=s.map(y=>y.identifier),u=!!(o.length||h.length);(u||i)&&this.log(`INTERSTITIALS_UPDATED (${o.length}): ${o} -Schedule: ${c.map(y=>Fe(y))} pos: ${this.timelinePos}`),h.length&&this.log(`Removed events ${h}`);let d=null,f=null;n&&(d=this.updateItem(n,this.timelinePos),this.itemsMatch(n,d)?this.playingItem=d:this.waitingItem=this.endedItem=null),this.waitingItem=this.updateItem(this.waitingItem),this.endedItem=this.updateItem(this.endedItem);const g=this.bufferingItem;if(g&&(f=this.updateItem(g,this.bufferedPos),this.itemsMatch(g,f)?this.bufferingItem=f:g.event&&(this.bufferingItem=this.playingItem,this.clearInterstitial(g.event,null))),s.forEach(y=>{y.assetList.forEach(p=>{this.clearAssetPlayer(p.identifier,null)})}),this.playerQueue.forEach(y=>{if(y.interstitial.appendInPlace){const p=y.assetItem.timelineStart,E=y.timelineOffset-p;if(E)try{y.timelineOffset=p}catch(T){Math.abs(E)>Dt&&this.warn(`${T} ("${y.assetId}" ${y.timelineOffset}->${p})`)}}}),u||i){if(this.hls.trigger(m.INTERSTITIALS_UPDATED,{events:o.slice(0),schedule:c.slice(0),durations:l,removedIds:h}),this.isInterstitial(n)&&h.includes(n.event.identifier)){this.warn(`Interstitial "${n.event.identifier}" removed while playing`),this.primaryFallback(n.event);return}n&&this.trimInPlace(d,n),g&&f!==d&&this.trimInPlace(f,g),this.checkBuffer()}},this.hls=e,this.HlsPlayerClass=t,this.assetListLoader=new eu(e),this.schedule=new Jh(this.onScheduleUpdate,e.logger),this.registerListeners()}registerListeners(){const e=this.hls;e&&(e.on(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(m.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),e.on(m.AUDIO_TRACK_UPDATED,this.onAudioTrackUpdated,this),e.on(m.SUBTITLE_TRACK_SWITCH,this.onSubtitleTrackSwitch,this),e.on(m.SUBTITLE_TRACK_UPDATED,this.onSubtitleTrackUpdated,this),e.on(m.EVENT_CUE_ENTER,this.onInterstitialCueEnter,this),e.on(m.ASSET_LIST_LOADED,this.onAssetListLoaded,this),e.on(m.BUFFER_APPENDED,this.onBufferAppended,this),e.on(m.BUFFER_FLUSHED,this.onBufferFlushed,this),e.on(m.BUFFERED_TO_END,this.onBufferedToEnd,this),e.on(m.MEDIA_ENDED,this.onMediaEnded,this),e.on(m.ERROR,this.onError,this),e.on(m.DESTROYING,this.onDestroying,this))}unregisterListeners(){const e=this.hls;e&&(e.off(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(m.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),e.off(m.AUDIO_TRACK_UPDATED,this.onAudioTrackUpdated,this),e.off(m.SUBTITLE_TRACK_SWITCH,this.onSubtitleTrackSwitch,this),e.off(m.SUBTITLE_TRACK_UPDATED,this.onSubtitleTrackUpdated,this),e.off(m.EVENT_CUE_ENTER,this.onInterstitialCueEnter,this),e.off(m.ASSET_LIST_LOADED,this.onAssetListLoaded,this),e.off(m.BUFFER_CODECS,this.onBufferCodecs,this),e.off(m.BUFFER_APPENDED,this.onBufferAppended,this),e.off(m.BUFFER_FLUSHED,this.onBufferFlushed,this),e.off(m.BUFFERED_TO_END,this.onBufferedToEnd,this),e.off(m.MEDIA_ENDED,this.onMediaEnded,this),e.off(m.ERROR,this.onError,this),e.off(m.DESTROYING,this.onDestroying,this))}startLoad(){this.resumeBuffering()}stopLoad(){this.pauseBuffering()}resumeBuffering(){var e;(e=this.getBufferingPlayer())==null||e.resumeBuffering()}pauseBuffering(){var e;(e=this.getBufferingPlayer())==null||e.pauseBuffering()}destroy(){this.unregisterListeners(),this.stopLoad(),this.assetListLoader&&this.assetListLoader.destroy(),this.emptyPlayerQueue(),this.clearScheduleState(),this.schedule&&this.schedule.destroy(),this.media=this.detachedData=this.mediaSelection=this.requiredTracks=this.altSelection=this.schedule=this.manager=null,this.hls=this.HlsPlayerClass=this.log=null,this.assetListLoader=null,this.onPlay=this.onPause=this.onSeeking=this.onTimeupdate=null,this.onScheduleUpdate=null}onDestroying(){const e=this.primaryMedia||this.media;e&&this.removeMediaListeners(e)}removeMediaListeners(e){Ae(e,"play",this.onPlay),Ae(e,"pause",this.onPause),Ae(e,"seeking",this.onSeeking),Ae(e,"timeupdate",this.onTimeupdate)}onMediaAttaching(e,t){const s=this.media=t.media;ve(s,"seeking",this.onSeeking),ve(s,"timeupdate",this.onTimeupdate),ve(s,"play",this.onPlay),ve(s,"pause",this.onPause)}onMediaAttached(e,t){const s=this.effectivePlayingItem,i=this.detachedData;if(this.detachedData=null,s===null)this.checkStart();else if(!i){this.clearScheduleState();const r=this.findItemIndex(s);this.setSchedulePosition(r)}}clearScheduleState(){this.log("clear schedule state"),this.playingItem=this.bufferingItem=this.waitingItem=this.endedItem=this.playingAsset=this.endedAsset=this.bufferingAsset=null}onMediaDetaching(e,t){const s=!!t.transferMedia,i=this.media;if(this.media=null,!s&&(i&&this.removeMediaListeners(i),this.detachedData)){const r=this.getBufferingPlayer();r&&(this.log(`Removing schedule state for detachedData and ${r}`),this.playingAsset=this.endedAsset=this.bufferingAsset=this.bufferingItem=this.waitingItem=this.detachedData=null,r.detachMedia()),this.shouldPlay=!1}}get interstitialsManager(){if(!this.hls)return null;if(this.manager)return this.manager;const e=this,t=()=>e.bufferingItem||e.waitingItem,s=u=>u&&e.getAssetPlayer(u.identifier),i=(u,d,f,g,y)=>{if(u){let p=u[d].start;const E=u.event;if(E){if(d==="playout"||E.timelineOccupancy!==bs.Point){const T=s(f);(T==null?void 0:T.interstitial)===E&&(p+=T.assetItem.startOffset+T[y])}}else{const T=g==="bufferedPos"?n():e[g];p+=T-u.start}return p}return 0},r=(u,d)=>{var f;if(u!==0&&d!=="primary"&&(f=e.schedule)!=null&&f.length){var g;const y=e.schedule.findItemIndexAtTime(u),p=(g=e.schedule.items)==null?void 0:g[y];if(p){const E=p[d].start-p.start;return u+E}}return u},n=()=>{const u=e.bufferedPos;return u===Number.MAX_VALUE?o("primary"):Math.max(u,0)},o=u=>{var d,f;return(d=e.primaryDetails)!=null&&d.live?e.primaryDetails.edge:((f=e.schedule)==null?void 0:f.durations[u])||0},c=(u,d)=>{var f,g;const y=e.effectivePlayingItem;if(y!=null&&(f=y.event)!=null&&f.restrictions.skip||!e.schedule)return;e.log(`seek to ${u} "${d}"`);const p=e.effectivePlayingItem,E=e.schedule.findItemIndexAtTime(u,d),T=(g=e.schedule.items)==null?void 0:g[E],S=e.getBufferingPlayer(),v=S==null?void 0:S.interstitial,x=v==null?void 0:v.appendInPlace,D=p&&e.itemsMatch(p,T);if(p&&(x||D)){const A=s(e.playingAsset),_=(A==null?void 0:A.media)||e.primaryMedia;if(_){const b=d==="primary"?_.currentTime:i(p,d,e.playingAsset,"timelinePos","currentTime"),I=u-b,P=(x?b:_.currentTime)+I;if(P>=0&&(!A||x||P<=A.duration)){_.currentTime=P;return}}}if(T){let A=u;if(d!=="primary"){const b=T[d].start,I=u-b;A=T.start+I}const _=!e.isInterstitial(T);if((!e.isInterstitial(p)||p.event.appendInPlace)&&(_||T.event.appendInPlace)){const b=e.media||(x?S==null?void 0:S.media:null);b&&(b.currentTime=A)}else if(p){const b=e.findItemIndex(p);if(E>b){const P=e.schedule.findJumpRestrictedIndex(b+1,E);if(P>b){e.setSchedulePosition(P);return}}let I=0;if(_)e.timelinePos=A,e.checkBuffer();else{const P=T.event.assetList,F=u-(T[d]||T).start;for(let $=P.length;$--;){const Y=P[$];if(Y.duration&&F>=Y.startOffset&&F{const u=e.effectivePlayingItem;if(e.isInterstitial(u))return u;const d=t();return e.isInterstitial(d)?d:null},h={get bufferedEnd(){const u=t(),d=e.bufferingItem;if(d&&d===u){var f;return i(d,"playout",e.bufferingAsset,"bufferedPos","bufferedEnd")-d.playout.start||((f=e.bufferingAsset)==null?void 0:f.startOffset)||0}return 0},get currentTime(){const u=l(),d=e.effectivePlayingItem;return d&&d===u?i(d,"playout",e.effectivePlayingAsset,"timelinePos","currentTime")-d.playout.start:0},set currentTime(u){const d=l(),f=e.effectivePlayingItem;f&&f===d&&c(u+f.playout.start,"playout")},get duration(){const u=l();return u?u.playout.end-u.playout.start:0},get assetPlayers(){var u;const d=(u=l())==null?void 0:u.event.assetList;return d?d.map(f=>e.getAssetPlayer(f.identifier)):[]},get playingIndex(){var u;const d=(u=l())==null?void 0:u.event;return d&&e.effectivePlayingAsset?d.findAssetIndex(e.effectivePlayingAsset):-1},get scheduleItem(){return l()}};return this.manager={get events(){var u;return((u=e.schedule)==null||(u=u.events)==null?void 0:u.slice(0))||[]},get schedule(){var u;return((u=e.schedule)==null||(u=u.items)==null?void 0:u.slice(0))||[]},get interstitialPlayer(){return l()?h:null},get playerQueue(){return e.playerQueue.slice(0)},get bufferingAsset(){return e.bufferingAsset},get bufferingItem(){return t()},get bufferingIndex(){const u=t();return e.findItemIndex(u)},get playingAsset(){return e.effectivePlayingAsset},get playingItem(){return e.effectivePlayingItem},get playingIndex(){const u=e.effectivePlayingItem;return e.findItemIndex(u)},primary:{get bufferedEnd(){return n()},get currentTime(){const u=e.timelinePos;return u>0?u:0},set currentTime(u){c(u,"primary")},get duration(){return o("primary")},get seekableStart(){var u;return((u=e.primaryDetails)==null?void 0:u.fragmentStart)||0}},integrated:{get bufferedEnd(){return i(t(),"integrated",e.bufferingAsset,"bufferedPos","bufferedEnd")},get currentTime(){return i(e.effectivePlayingItem,"integrated",e.effectivePlayingAsset,"timelinePos","currentTime")},set currentTime(u){c(u,"integrated")},get duration(){return o("integrated")},get seekableStart(){var u;return r(((u=e.primaryDetails)==null?void 0:u.fragmentStart)||0,"integrated")}},skip:()=>{const u=e.effectivePlayingItem,d=u==null?void 0:u.event;if(d&&!d.restrictions.skip){const f=e.findItemIndex(u);if(d.appendInPlace){const g=u.playout.start+u.event.duration;c(g+.001,"playout")}else e.advanceAfterAssetEnded(d,f,1/0)}}}}get effectivePlayingItem(){return this.waitingItem||this.playingItem||this.endedItem}get effectivePlayingAsset(){return this.playingAsset||this.endedAsset}get playingLastItem(){var e;const t=this.playingItem,s=(e=this.schedule)==null?void 0:e.items;return!this.playbackStarted||!t||!s?!1:this.findItemIndex(t)===s.length-1}get playbackStarted(){return this.effectivePlayingItem!==null}get currentTime(){var e,t;if(this.mediaSelection===null)return;const s=this.waitingItem||this.playingItem;if(this.isInterstitial(s)&&!s.event.appendInPlace)return;let i=this.media;!i&&(e=this.bufferingItem)!=null&&(e=e.event)!=null&&e.appendInPlace&&(i=this.primaryMedia);const r=(t=i)==null?void 0:t.currentTime;if(!(r===void 0||!N(r)))return r}get primaryMedia(){var e;return this.media||((e=this.detachedData)==null?void 0:e.media)||null}isInterstitial(e){return!!(e!=null&&e.event)}retreiveMediaSource(e,t){const s=this.getAssetPlayer(e);s&&this.transferMediaFromPlayer(s,t)}transferMediaFromPlayer(e,t){const s=e.interstitial.appendInPlace,i=e.media;if(s&&i===this.primaryMedia){if(this.bufferingAsset=null,(!t||this.isInterstitial(t)&&!t.event.appendInPlace)&&t&&i){this.detachedData={media:i};return}const r=e.transferMedia();this.log(`transfer MediaSource from ${e} ${oe(r)}`),this.detachedData=r}else t&&i&&(this.shouldPlay||(this.shouldPlay=!i.paused))}transferMediaTo(e,t){var s,i;if(e.media===t)return;let r=null;const n=this.hls,o=e!==n,c=o&&e.interstitial.appendInPlace,l=(s=this.detachedData)==null?void 0:s.mediaSource;let h;if(n.media)c&&(r=n.transferMedia(),this.detachedData=r),h="Primary";else if(l){const g=this.getBufferingPlayer();g?(r=g.transferMedia(),h=`${g}`):h="detached MediaSource"}else h="detached media";if(!r){if(l)r=this.detachedData,this.log(`using detachedData: MediaSource ${oe(r)}`);else if(!this.detachedData||n.media===t){const g=this.playerQueue;g.length>1&&g.forEach(y=>{if(o&&y.interstitial.appendInPlace!==c){const p=y.interstitial;this.clearInterstitial(y.interstitial,null),p.appendInPlace=!1,p.appendInPlace&&this.warn(`Could not change append strategy for queued assets ${p}`)}}),this.hls.detachMedia(),this.detachedData={media:t}}}const u=r&&"mediaSource"in r&&((i=r.mediaSource)==null?void 0:i.readyState)!=="closed",d=u&&r?r:t;this.log(`${u?"transfering MediaSource":"attaching media"} to ${o?e:"Primary"} from ${h} (media.currentTime: ${t.currentTime})`);const f=this.schedule;if(d===r&&f){const g=o&&e.assetId===f.assetIdAtEnd;d.overrides={duration:f.duration,endOfStream:!o||g,cueRemoval:!o}}e.attachMedia(d)}onInterstitialCueEnter(){this.onTimeupdate()}checkStart(){const e=this.schedule,t=e==null?void 0:e.events;if(!t||this.playbackDisabled||!this.media)return;this.bufferedPos===-1&&(this.bufferedPos=0);const s=this.timelinePos,i=this.effectivePlayingItem;if(s===-1){const r=this.hls.startPosition;if(this.timelinePos=r,t.length===0)this.setSchedulePosition(0);else if(t[0].cue.pre){this.log(xt("checkStart (preroll)",r));const n=e.findEventIndex(t[0].identifier);this.setSchedulePosition(n)}else if(r>=0||!this.primaryLive){this.log(xt("checkStart",r));const n=this.timelinePos=r>0?r:0,o=e.findItemIndexAtTime(n);this.setSchedulePosition(o)}else this.hls.liveSyncPosition===0?this.setSchedulePosition(0):this.log("[checkStart] waiting for live start")}else if(i&&!this.playingItem){this.log(xt("checkStart (playing item)",i.start));const r=e.findItemIndex(i);this.setSchedulePosition(r)}}advanceAssetBuffering(e,t){const s=e.event,i=s.findAssetIndex(t),r=qs(s,i);if(!s.isAssetPastPlayoutLimit(r))this.bufferedToEvent(e,r);else if(this.schedule){var n;const o=(n=this.schedule.items)==null?void 0:n[this.findItemIndex(e)+1];o&&this.bufferedToItem(o)}}advanceAfterAssetEnded(e,t,s){const i=qs(e,s);if(e.isAssetPastPlayoutLimit(i)){if(this.schedule){const r=this.schedule.items;if(r){const n=t+1,o=r.length;if(n>=o){this.setSchedulePosition(-1);return}const c=e.resumeTime;this.timelinePos=0?i[e]:null;this.log(`setSchedulePosition ${e}, ${t} (${r&&Fe(r)}) pos: ${this.timelinePos}`);const n=this.waitingItem||this.playingItem,o=this.playingLastItem;if(this.isInterstitial(n)){const h=n.event,u=this.playingAsset,d=u==null?void 0:u.identifier,f=d?this.getAssetPlayer(d):null;if(f&&d&&(!this.eventItemsMatch(n,r)||t!==void 0&&d!==h.assetList[t].identifier)){var c;const g=h.findAssetIndex(u);if(this.log(`INTERSTITIAL_ASSET_ENDED ${g+1}/${h.assetList.length} ${At(u)}`),this.endedAsset=u,this.playingAsset=null,this.hls.trigger(m.INTERSTITIAL_ASSET_ENDED,{asset:u,assetListIndex:g,event:h,schedule:i.slice(0),scheduleIndex:e,player:f}),n!==this.playingItem){this.itemsMatch(n,this.playingItem)&&!this.playingAsset&&this.advanceAfterAssetEnded(h,this.findItemIndex(this.playingItem),g);return}this.retreiveMediaSource(d,r),f.media&&!((c=this.detachedData)!=null&&c.mediaSource)&&f.detachMedia()}if(!this.eventItemsMatch(n,r)&&(this.endedItem=n,this.playingItem=null,this.log(`INTERSTITIAL_ENDED ${h} ${Fe(n)}`),h.hasPlayed=!0,this.hls.trigger(m.INTERSTITIAL_ENDED,{event:h,schedule:i.slice(0),scheduleIndex:e}),h.cue.once)){var l;this.updateSchedule();const g=(l=this.schedule)==null?void 0:l.items;if(r&&g){const y=this.findItemIndex(r);this.advanceSchedule(y,g,t,n,o)}return}}this.advanceSchedule(e,i,t,n,o)}advanceSchedule(e,t,s,i,r){const n=this.schedule;if(!n)return;const o=t[e]||null,c=this.primaryMedia,l=this.playerQueue;if(l.length&&l.forEach(h=>{const u=h.interstitial,d=n.findEventIndex(u.identifier);(de+1)&&this.clearInterstitial(u,o)}),this.isInterstitial(o)){this.timelinePos=Math.min(Math.max(this.timelinePos,o.start),o.end);const h=o.event;if(s===void 0){s=n.findAssetIndex(h,this.timelinePos);const g=qs(h,s-1);if(h.isAssetPastPlayoutLimit(g)||h.appendInPlace&&this.timelinePos===o.end){this.advanceAfterAssetEnded(h,e,s);return}s=g}const u=this.waitingItem;this.assetsBuffered(o,c)||this.setBufferingItem(o);let d=this.preloadAssets(h,s);if(this.eventItemsMatch(o,u||i)||(this.waitingItem=o,this.log(`INTERSTITIAL_STARTED ${Fe(o)} ${h.appendInPlace?"append in place":""}`),this.hls.trigger(m.INTERSTITIAL_STARTED,{event:h,schedule:t.slice(0),scheduleIndex:e})),!h.assetListLoaded){this.log(`Waiting for ASSET-LIST to complete loading ${h}`);return}if(h.assetListLoader&&(h.assetListLoader.destroy(),h.assetListLoader=void 0),!c){this.log(`Waiting for attachMedia to start Interstitial ${h}`);return}this.waitingItem=this.endedItem=null,this.playingItem=o;const f=h.assetList[s];if(!f){this.advanceAfterAssetEnded(h,e,s||0);return}if(d||(d=this.getAssetPlayer(f.identifier)),d===null||d.destroyed){const g=h.assetList.length;this.warn(`asset ${s+1}/${g} player destroyed ${h}`),d=this.createAssetPlayer(h,f,s),d.loadSource()}if(!this.eventItemsMatch(o,this.bufferingItem)&&h.appendInPlace&&this.isAssetBuffered(f))return;this.startAssetPlayer(d,s,t,e,c),this.shouldPlay&&sn(d.media)}else o?(this.resumePrimary(o,e,i),this.shouldPlay&&sn(this.hls.media)):r&&this.isInterstitial(i)&&(this.endedItem=null,this.playingItem=i,i.event.appendInPlace||this.attachPrimary(n.durations.primary,null))}get playbackDisabled(){return this.hls.config.enableInterstitialPlayback===!1}get primaryDetails(){var e;return(e=this.mediaSelection)==null?void 0:e.main.details}get primaryLive(){var e;return!!((e=this.primaryDetails)!=null&&e.live)}resumePrimary(e,t,s){var i,r;if(this.playingItem=e,this.playingAsset=this.endedAsset=null,this.waitingItem=this.endedItem=null,this.bufferedToItem(e),this.log(`resuming ${Fe(e)}`),!((i=this.detachedData)!=null&&i.mediaSource)){let o=this.timelinePos;(o=e.end)&&(o=this.getPrimaryResumption(e,t),this.log(xt("resumePrimary",o)),this.timelinePos=o),this.attachPrimary(o,e)}if(!s)return;const n=(r=this.schedule)==null?void 0:r.items;n&&(this.log(`INTERSTITIALS_PRIMARY_RESUMED ${Fe(e)}`),this.hls.trigger(m.INTERSTITIALS_PRIMARY_RESUMED,{schedule:n.slice(0),scheduleIndex:t}),this.checkBuffer())}getPrimaryResumption(e,t){const s=e.start;if(this.primaryLive){const i=this.primaryDetails;if(t===0)return this.hls.startPosition;if(i&&(si.edge))return this.hls.liveSyncPosition||-1}return s}isAssetBuffered(e){const t=this.getAssetPlayer(e.identifier);return t!=null&&t.hls?t.hls.bufferedToEnd:X.bufferInfo(this.primaryMedia,this.timelinePos,0).end+1>=e.timelineStart+(e.duration||0)}attachPrimary(e,t,s){t?this.setBufferingItem(t):this.bufferingItem=this.playingItem,this.bufferingAsset=null;const i=this.primaryMedia;if(!i)return;const r=this.hls;r.media?this.checkBuffer():(this.transferMediaTo(r,i),s&&this.startLoadingPrimaryAt(e,s)),s||(this.log(xt("attachPrimary",e)),this.timelinePos=e,this.startLoadingPrimaryAt(e,s))}startLoadingPrimaryAt(e,t){var s;const i=this.hls;!i.loadingEnabled||!i.media||Math.abs((((s=i.mainForwardBufferInfo)==null?void 0:s.start)||i.media.currentTime)-e)>.5?i.startLoad(e,t):i.bufferingEnabled||i.resumeBuffering()}onManifestLoading(){var e;this.stopLoad(),(e=this.schedule)==null||e.reset(),this.emptyPlayerQueue(),this.clearScheduleState(),this.shouldPlay=!1,this.bufferedPos=this.timelinePos=-1,this.mediaSelection=this.altSelection=this.manager=this.requiredTracks=null,this.hls.off(m.BUFFER_CODECS,this.onBufferCodecs,this),this.hls.on(m.BUFFER_CODECS,this.onBufferCodecs,this)}onLevelUpdated(e,t){if(t.level===-1||!this.schedule)return;const s=this.hls.levels[t.level];if(!s.details)return;const i=ie(ie({},this.mediaSelection||this.altSelection),{},{main:s});this.mediaSelection=i,this.schedule.parseInterstitialDateRanges(i,this.hls.config.interstitialAppendInPlace),!this.effectivePlayingItem&&this.schedule.items&&this.checkStart()}onAudioTrackUpdated(e,t){const s=this.hls.audioTracks[t.id],i=this.mediaSelection;if(!i){this.altSelection=ie(ie({},this.altSelection),{},{audio:s});return}const r=ie(ie({},i),{},{audio:s});this.mediaSelection=r}onSubtitleTrackUpdated(e,t){const s=this.hls.subtitleTracks[t.id],i=this.mediaSelection;if(!i){this.altSelection=ie(ie({},this.altSelection),{},{subtitles:s});return}const r=ie(ie({},i),{},{subtitles:s});this.mediaSelection=r}onAudioTrackSwitching(e,t){const s=ur(t);this.playerQueue.forEach(({hls:i})=>i&&(i.setAudioOption(t)||i.setAudioOption(s)))}onSubtitleTrackSwitch(e,t){const s=ur(t);this.playerQueue.forEach(({hls:i})=>i&&(i.setSubtitleOption(t)||t.id!==-1&&i.setSubtitleOption(s)))}onBufferCodecs(e,t){const s=t.tracks;s&&(this.requiredTracks=s)}onBufferAppended(e,t){this.checkBuffer()}onBufferFlushed(e,t){const s=this.playingItem;if(s&&!this.itemsMatch(s,this.bufferingItem)&&!this.isInterstitial(s)){const i=this.timelinePos;this.bufferedPos=i,this.checkBuffer()}}onBufferedToEnd(e){if(!this.schedule)return;const t=this.schedule.events;if(this.bufferedPos.25){e.event.assetList.forEach((r,n)=>{e.event.isAssetPastPlayoutLimit(n)&&this.clearAssetPlayer(r.identifier,null)});const s=e.end+.25,i=X.bufferInfo(this.primaryMedia,s,0);(i.end>s||(i.nextStart||0)>s)&&(this.log(`trim buffered interstitial ${Fe(e)} (was ${Fe(t)})`),this.attachPrimary(s,null,!0),this.flushFrontBuffer(s))}}itemsMatch(e,t){return!!t&&(e===t||e.event&&t.event&&this.eventItemsMatch(e,t)||!e.event&&!t.event&&this.findItemIndex(e)===this.findItemIndex(t))}eventItemsMatch(e,t){var s;return!!t&&(e===t||e.event.identifier===((s=t.event)==null?void 0:s.identifier))}findItemIndex(e,t){return e&&this.schedule?this.schedule.findItemIndex(e,t):-1}updateSchedule(e=!1){var t;const s=this.mediaSelection;s&&((t=this.schedule)==null||t.updateSchedule(s,[],e))}checkBuffer(e){var t;const s=(t=this.schedule)==null?void 0:t.items;if(!s)return;const i=X.bufferInfo(this.primaryMedia,this.timelinePos,0);e&&(this.bufferedPos=this.timelinePos),e||(e=i.len<1),this.updateBufferedPos(i.end,s,e)}updateBufferedPos(e,t,s){const i=this.schedule,r=this.bufferingItem;if(this.bufferedPos>e||!i)return;if(t.length===1&&this.itemsMatch(t[0],r)){this.bufferedPos=e;return}const n=this.playingItem,o=this.findItemIndex(n);let c=i.findItemIndexAtTime(e);if(this.bufferedPos=r.end||(l=d.event)!=null&&l.appendInPlace&&e+.01>=d.start)&&(c=u),this.isInterstitial(r)){const f=r.event;if(u-o>1&&f.appendInPlace===!1||f.assetList.length===0&&f.assetListLoader)return}if(this.bufferedPos=e,c>h&&c>o)this.bufferedToItem(d);else{const f=this.primaryDetails;this.primaryLive&&f&&e>f.edge-f.targetduration&&d.start{const r=this.getAssetPlayer(i.identifier);return!(r!=null&&r.bufferedInPlaceToEnd(t))})}setBufferingItem(e){const t=this.bufferingItem,s=this.schedule;if(!this.itemsMatch(e,t)&&s){const{items:i,events:r}=s;if(!i||!r)return t;const n=this.isInterstitial(e),o=this.getBufferingPlayer();this.bufferingItem=e,this.bufferedPos=Math.max(e.start,Math.min(e.end,this.timelinePos));const c=o?o.remaining:t?t.end-this.timelinePos:0;if(this.log(`INTERSTITIALS_BUFFERED_TO_BOUNDARY ${Fe(e)}`+(t?` (${c.toFixed(2)} remaining)`:"")),!this.playbackDisabled)if(n){const l=s.findAssetIndex(e.event,this.bufferedPos);e.event.assetList.forEach((h,u)=>{const d=this.getAssetPlayer(h.identifier);d&&(u===l&&d.loadSource(),d.resumeBuffering())})}else this.hls.resumeBuffering(),this.playerQueue.forEach(l=>l.pauseBuffering());this.hls.trigger(m.INTERSTITIALS_BUFFERED_TO_BOUNDARY,{events:r.slice(0),schedule:i.slice(0),bufferingIndex:this.findItemIndex(e),playingIndex:this.findItemIndex(this.playingItem)})}else this.bufferingItem!==e&&(this.bufferingItem=e);return t}bufferedToItem(e,t=0){const s=this.setBufferingItem(e);if(!this.playbackDisabled){if(this.isInterstitial(e))this.bufferedToEvent(e,t);else if(s!==null){this.bufferingAsset=null;const i=this.detachedData;i?i.mediaSource?this.attachPrimary(e.start,e,!0):this.preloadPrimary(e):this.preloadPrimary(e)}}}preloadPrimary(e){const t=this.findItemIndex(e),s=this.getPrimaryResumption(e,t);this.startLoadingPrimaryAt(s)}bufferedToEvent(e,t){const s=e.event,i=s.assetList.length===0&&!s.assetListLoader,r=s.cue.once;if(i||!r){const n=this.preloadAssets(s,t);if(n!=null&&n.interstitial.appendInPlace){const o=this.primaryMedia;o&&this.bufferAssetPlayer(n,o)}}}preloadAssets(e,t){const s=e.assetUrl,i=e.assetList.length,r=i===0&&!e.assetListLoader,n=e.cue.once;if(r){const c=e.timelineStart;if(e.appendInPlace){var o;const d=this.playingItem;!this.isInterstitial(d)&&(d==null||(o=d.nextEvent)==null?void 0:o.identifier)===e.identifier&&this.flushFrontBuffer(c+.25)}let l,h=0;if(!this.playingItem&&this.primaryLive&&(h=this.hls.startPosition,h===-1&&(h=this.hls.liveSyncPosition||0)),h&&!(e.cue.pre||e.cue.post)){const d=h-c;d>0&&(l=Math.round(d*1e3)/1e3)}if(this.log(`Load interstitial asset ${t+1}/${s?1:i} ${e}${l?` live-start: ${h} start-offset: ${l}`:""}`),s)return this.createAsset(e,0,0,c,e.duration,s);const u=this.assetListLoader.loadAssetList(e,l);u&&(e.assetListLoader=u)}else if(!n&&i){for(let l=t;l{this.hls.trigger(m.BUFFER_FLUSHING,{startOffset:e,endOffset:1/0,type:i})})}getAssetPlayerQueueIndex(e){const t=this.playerQueue;for(let s=0;s1){const x=t.duration;x&&v{if(v.live){var x;const _=new Error(`Interstitials MUST be VOD assets ${e}`),b={fatal:!0,type:H.OTHER_ERROR,details:R.INTERSTITIAL_ASSET_ITEM_ERROR,error:_},I=((x=this.schedule)==null?void 0:x.findEventIndex(e.identifier))||-1;this.handleAssetItemError(b,e,I,s,_.message);return}const D=v.edge-v.fragmentStart,A=t.duration;(y||A===null||D>A)&&(y=!1,this.log(`Interstitial asset "${u}" duration change ${A} > ${D}`),t.duration=D,this.updateSchedule())};g.on(m.LEVEL_UPDATED,(v,{details:x})=>p(x)),g.on(m.LEVEL_PTS_UPDATED,(v,{details:x})=>p(x)),g.on(m.EVENT_CUE_ENTER,()=>this.onInterstitialCueEnter());const E=(v,x)=>{const D=this.getAssetPlayer(u);if(D&&x.tracks){D.off(m.BUFFER_CODECS,E),D.tracks=x.tracks;const A=this.primaryMedia;this.bufferingAsset===D.assetItem&&A&&!D.media&&this.bufferAssetPlayer(D,A)}};g.on(m.BUFFER_CODECS,E);const T=()=>{var v;const x=this.getAssetPlayer(u);if(this.log(`buffered to end of asset ${x}`),!x||!this.schedule)return;const D=this.schedule.findEventIndex(e.identifier),A=(v=this.schedule.items)==null?void 0:v[D];this.isInterstitial(A)&&this.advanceAssetBuffering(A,t)};g.on(m.BUFFERED_TO_END,T);const S=v=>()=>{if(!this.getAssetPlayer(u)||!this.schedule)return;this.shouldPlay=!0;const D=this.schedule.findEventIndex(e.identifier);this.advanceAfterAssetEnded(e,D,v)};return g.once(m.MEDIA_ENDED,S(s)),g.once(m.PLAYOUT_LIMIT_REACHED,S(1/0)),g.on(m.ERROR,(v,x)=>{if(!this.schedule)return;const D=this.getAssetPlayer(u);if(x.details===R.BUFFER_STALLED_ERROR){if(D!=null&&D.appendInPlace){this.handleInPlaceStall(e);return}this.onTimeupdate(),this.checkBuffer(!0);return}this.handleAssetItemError(x,e,this.schedule.findEventIndex(e.identifier),s,`Asset player error ${x.error} ${e}`)}),g.on(m.DESTROYING,()=>{if(!this.getAssetPlayer(u)||!this.schedule)return;const x=new Error(`Asset player destroyed unexpectedly ${u}`),D={fatal:!0,type:H.OTHER_ERROR,details:R.INTERSTITIAL_ASSET_ITEM_ERROR,error:x};this.handleAssetItemError(D,e,this.schedule.findEventIndex(e.identifier),s,x.message)}),this.log(`INTERSTITIAL_ASSET_PLAYER_CREATED ${At(t)}`),this.hls.trigger(m.INTERSTITIAL_ASSET_PLAYER_CREATED,{asset:t,assetListIndex:s,event:e,player:g}),g}clearInterstitial(e,t){this.clearAssetPlayers(e,t),e.reset()}clearAssetPlayers(e,t){e.assetList.forEach(s=>{this.clearAssetPlayer(s.identifier,t)})}resetAssetPlayer(e){const t=this.getAssetPlayerQueueIndex(e);if(t!==-1){this.log(`reset asset player "${e}" after error`);const s=this.playerQueue[t];this.transferMediaFromPlayer(s,null),s.resetDetails()}}clearAssetPlayer(e,t){const s=this.getAssetPlayerQueueIndex(e);if(s!==-1){const i=this.playerQueue[s];this.log(`clear ${i} toSegment: ${t&&Fe(t)}`),this.transferMediaFromPlayer(i,t),this.playerQueue.splice(s,1),i.destroy()}}emptyPlayerQueue(){let e;for(;e=this.playerQueue.pop();)e.destroy();this.playerQueue=[]}startAssetPlayer(e,t,s,i,r){const{interstitial:n,assetItem:o,assetId:c}=e,l=n.assetList.length,h=this.playingAsset;this.endedAsset=null,this.playingAsset=o,(!h||h.identifier!==c)&&(h&&(this.clearAssetPlayer(h.identifier,s[i]),delete h.error),this.log(`INTERSTITIAL_ASSET_STARTED ${t+1}/${l} ${At(o)}`),this.hls.trigger(m.INTERSTITIAL_ASSET_STARTED,{asset:o,assetListIndex:t,event:n,schedule:s.slice(0),scheduleIndex:i,player:e})),this.bufferAssetPlayer(e,r)}bufferAssetPlayer(e,t){var s,i;if(!this.schedule)return;const{interstitial:r,assetItem:n}=e,o=this.schedule.findEventIndex(r.identifier),c=(s=this.schedule.items)==null?void 0:s[o];if(!c)return;e.loadSource(),this.setBufferingItem(c),this.bufferingAsset=n;const l=this.getBufferingPlayer();if(l===e)return;const h=r.appendInPlace;if(h&&(l==null?void 0:l.interstitial.appendInPlace)===!1)return;const u=(l==null?void 0:l.tracks)||((i=this.detachedData)==null?void 0:i.tracks)||this.requiredTracks;if(h&&n!==this.playingAsset){if(!e.tracks){this.log(`Waiting for track info before buffering ${e}`);return}if(u&&!Tn(u,e.tracks)){const d=new Error(`Asset ${At(n)} SourceBuffer tracks ('${Object.keys(e.tracks)}') are not compatible with primary content tracks ('${Object.keys(u)}')`),f={fatal:!0,type:H.OTHER_ERROR,details:R.INTERSTITIAL_ASSET_ITEM_ERROR,error:d},g=r.findAssetIndex(n);this.handleAssetItemError(f,r,o,g,d.message);return}}this.transferMediaTo(e,t)}handleInPlaceStall(e){const t=this.schedule,s=this.primaryMedia;if(!t||!s)return;const i=s.currentTime,r=t.findAssetIndex(e,i),n=e.assetList[r];if(n){const o=this.getAssetPlayer(n.identifier);if(o){const c=o.currentTime||i-n.timelineStart,l=o.duration-c;if(this.warn(`Stalled at ${c} of ${c+l} in ${o} ${e} (media.currentTime: ${i})`),c&&(l/s.playbackRate<.5||o.bufferedInPlaceToEnd(s))&&o.hls){const h=t.findEventIndex(e.identifier);this.advanceAfterAssetEnded(e,h,r)}}}}advanceInPlace(e){const t=this.primaryMedia;t&&t.currentTime!y.error))t.error=g;else for(let y=i;y{const E=parseFloat(y.DURATION);this.createAsset(r,p,h,c+h,E,y.URI),h+=E}),r.duration=h,this.log(`Loaded asset-list with duration: ${h} (was: ${l}) ${r}`);const u=this.waitingItem,d=(u==null?void 0:u.event.identifier)===n;this.updateSchedule();const f=(i=this.bufferingItem)==null?void 0:i.event;if(d){var g;const y=this.schedule.findEventIndex(n),p=(g=this.schedule.items)==null?void 0:g[y];if(p){if(!this.playingItem&&this.timelinePos>p.end&&this.schedule.findItemIndexAtTime(this.timelinePos)!==y){r.error=new Error(`Interstitial ${o.length?"no longer within playback range":"asset-list is empty"} ${this.timelinePos} ${r}`),this.log(r.error.message),this.updateSchedule(!0),this.primaryFallback(r);return}this.setBufferingItem(p)}this.setSchedulePosition(y)}else if((f==null?void 0:f.identifier)===n){const y=r.assetList[0];if(y){const p=this.getAssetPlayer(y.identifier);if(f.appendInPlace){const E=this.primaryMedia;p&&E&&this.bufferAssetPlayer(p,E)}else p&&p.loadSource()}}}onError(e,t){if(this.schedule)switch(t.details){case R.ASSET_LIST_PARSING_ERROR:case R.ASSET_LIST_LOAD_ERROR:case R.ASSET_LIST_LOAD_TIMEOUT:{const s=t.interstitial;s&&(this.updateSchedule(!0),this.primaryFallback(s));break}case R.BUFFER_STALLED_ERROR:{const s=this.endedItem||this.waitingItem||this.playingItem;if(this.isInterstitial(s)&&s.event.appendInPlace){this.handleInPlaceStall(s.event);return}this.log(`Primary player stall @${this.timelinePos} bufferedPos: ${this.bufferedPos}`),this.onTimeupdate(),this.checkBuffer(!0);break}}}}const rn=500;class su extends wi{constructor(e,t,s){super(e,t,s,"subtitle-stream-controller",U.SUBTITLE),this.currentTrackId=-1,this.tracksBuffered=[],this.mainDetails=null,this.registerListeners()}onHandlerDestroying(){this.unregisterListeners(),super.onHandlerDestroying(),this.mainDetails=null}registerListeners(){super.registerListeners();const{hls:e}=this;e.on(m.LEVEL_LOADED,this.onLevelLoaded,this),e.on(m.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),e.on(m.SUBTITLE_TRACK_SWITCH,this.onSubtitleTrackSwitch,this),e.on(m.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),e.on(m.SUBTITLE_FRAG_PROCESSED,this.onSubtitleFragProcessed,this),e.on(m.BUFFER_FLUSHING,this.onBufferFlushing,this)}unregisterListeners(){super.unregisterListeners();const{hls:e}=this;e.off(m.LEVEL_LOADED,this.onLevelLoaded,this),e.off(m.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),e.off(m.SUBTITLE_TRACK_SWITCH,this.onSubtitleTrackSwitch,this),e.off(m.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),e.off(m.SUBTITLE_FRAG_PROCESSED,this.onSubtitleFragProcessed,this),e.off(m.BUFFER_FLUSHING,this.onBufferFlushing,this)}startLoad(e,t){this.stopLoad(),this.state=C.IDLE,this.setInterval(rn),this.nextLoadPosition=this.lastCurrentTime=e+this.timelineOffset,this.startPosition=t?-1:e,this.tick()}onManifestLoading(){super.onManifestLoading(),this.mainDetails=null}onMediaDetaching(e,t){this.tracksBuffered=[],super.onMediaDetaching(e,t)}onLevelLoaded(e,t){this.mainDetails=t.details}onSubtitleFragProcessed(e,t){const{frag:s,success:i}=t;if(this.fragContextChanged(s)||(ue(s)&&(this.fragPrevious=s),this.state=C.IDLE),!i)return;const r=this.tracksBuffered[this.currentTrackId];if(!r)return;let n;const o=s.start;for(let l=0;l=r[l].start&&o<=r[l].end){n=r[l];break}const c=s.start+s.duration;n?n.end=c:(n={start:o,end:c},r.push(n)),this.fragmentTracker.fragBuffered(s),this.fragBufferedComplete(s,null),this.media&&this.tick()}onBufferFlushing(e,t){const{startOffset:s,endOffset:i}=t;if(s===0&&i!==Number.POSITIVE_INFINITY){const r=i-1;if(r<=0)return;t.endOffsetSubtitles=Math.max(0,r),this.tracksBuffered.forEach(n=>{for(let o=0;onew Kt(s));return}this.tracksBuffered=[],this.levels=t.map(s=>{const i=new Kt(s);return this.tracksBuffered[i.id]=[],i}),this.fragmentTracker.removeFragmentsInRange(0,Number.POSITIVE_INFINITY,U.SUBTITLE),this.fragPrevious=null,this.mediaBuffer=null}onSubtitleTrackSwitch(e,t){var s;if(this.currentTrackId=t.id,!((s=this.levels)!=null&&s.length)||this.currentTrackId===-1){this.clearInterval();return}const i=this.levels[this.currentTrackId];i!=null&&i.details?this.mediaBuffer=this.mediaBufferTimeRanges:this.mediaBuffer=null,i&&this.state!==C.STOPPED&&this.setInterval(rn)}onSubtitleTrackLoaded(e,t){var s;const{currentTrackId:i,levels:r}=this,{details:n,id:o}=t;if(!r){this.warn(`Subtitle tracks were reset while loading level ${o}`);return}const c=r[o];if(o>=r.length||!c)return;this.log(`Subtitle track ${o} loaded [${n.startSN},${n.endSN}]${n.lastPartSn?`[part-${n.lastPartSn}-${n.lastPartIndex}]`:""},duration:${n.totalduration}`),this.mediaBuffer=this.mediaBufferTimeRanges;let l=0;if(n.live||(s=c.details)!=null&&s.live){if(n.deltaUpdateFailed)return;const u=this.mainDetails;if(!u){this.startFragRequested=!1;return}const d=u.fragments[0];if(!c.details)n.hasProgramDateTime&&u.hasProgramDateTime?(Ls(n,u),l=n.fragmentStart):d&&(l=d.start,ui(n,l));else{var h;l=this.alignPlaylists(n,c.details,(h=this.levelLastLoaded)==null?void 0:h.details),l===0&&d&&(l=d.start,ui(n,l))}u&&!this.startFragRequested&&this.setStartPosition(u,l)}c.details=n,this.levelLastLoaded=c,o===i&&(this.hls.trigger(m.SUBTITLE_TRACK_UPDATED,{details:n,id:o,groupId:t.groupId}),this.tick(),n.live&&!this.fragCurrent&&this.media&&this.state===C.IDLE&&(mt(null,n.fragments,this.media.currentTime,0)||(this.warn("Subtitle playlist not aligned with playback"),c.details=void 0)))}_handleFragmentLoadComplete(e){const{frag:t,payload:s}=e,i=t.decryptdata,r=this.hls;if(!this.fragContextChanged(t)&&s&&s.byteLength>0&&i!=null&&i.key&&i.iv&&bt(i.method)){const n=performance.now();this.decrypter.decrypt(new Uint8Array(s),i.key.buffer,i.iv.buffer,Pi(i.method)).catch(o=>{throw r.trigger(m.ERROR,{type:H.MEDIA_ERROR,details:R.FRAG_DECRYPT_ERROR,fatal:!1,error:o,reason:o.message,frag:t}),o}).then(o=>{const c=performance.now();r.trigger(m.FRAG_DECRYPTED,{frag:t,payload:o,stats:{tstart:n,tdecrypt:c}})}).catch(o=>{this.warn(`${o.name}: ${o.message}`),this.state=C.IDLE})}}doTick(){if(!this.media){this.state=C.IDLE;return}if(this.state===C.IDLE){const{currentTrackId:e,levels:t}=this,s=t==null?void 0:t[e];if(!s||!t.length||!s.details||this.waitForLive(s))return;const{config:i}=this,r=this.getLoadPosition(),n=X.bufferedInfo(this.tracksBuffered[this.currentTrackId]||[],r,i.maxBufferHole),{end:o,len:c}=n,l=s.details,h=this.hls.maxBufferLength+l.levelTargetDuration;if(c>h)return;const u=l.fragments,d=u.length,f=l.edge;let g=null;const y=this.fragPrevious;if(of-T?0:T;g=mt(y,u,Math.max(u[0].start,o),S),!g&&y&&y.start{if(i=i>>>0,i>r-1)throw new DOMException(`Failed to execute '${s}' on 'TimeRanges': The index provided (${i}) is greater than the maximum bound (${r})`);return e[i][s]};this.buffered={get length(){return e.length},end(s){return t("end",s,e.length)},start(s){return t("start",s,e.length)}}}}const ru={42:225,92:233,94:237,95:243,96:250,123:231,124:247,125:209,126:241,127:9608,128:174,129:176,130:189,131:191,132:8482,133:162,134:163,135:9834,136:224,137:32,138:232,139:226,140:234,141:238,142:244,143:251,144:193,145:201,146:211,147:218,148:220,149:252,150:8216,151:161,152:42,153:8217,154:9473,155:169,156:8480,157:8226,158:8220,159:8221,160:192,161:194,162:199,163:200,164:202,165:203,166:235,167:206,168:207,169:239,170:212,171:217,172:249,173:219,174:171,175:187,176:195,177:227,178:205,179:204,180:236,181:210,182:242,183:213,184:245,185:123,186:125,187:92,188:94,189:95,190:124,191:8764,192:196,193:228,194:214,195:246,196:223,197:165,198:164,199:9475,200:197,201:229,202:216,203:248,204:9487,205:9491,206:9495,207:9499},Ba=a=>String.fromCharCode(ru[a]||a),Me=15,Xe=100,nu={17:1,18:3,21:5,22:7,23:9,16:11,19:12,20:14},au={17:2,18:4,21:6,22:8,23:10,19:13,20:15},ou={25:1,26:3,29:5,30:7,31:9,24:11,27:12,28:14},lu={25:2,26:4,29:6,30:8,31:10,27:13,28:15},cu=["white","green","blue","cyan","red","yellow","magenta","black","transparent"];class hu{constructor(){this.time=null,this.verboseLevel=0}log(e,t){if(this.verboseLevel>=e){const s=typeof t=="function"?t():t;re.log(`${this.time} [${e}] ${s}`)}}}const dt=function(e){const t=[];for(let s=0;sXe&&(this.logger.log(3,"Too large cursor position "+this.pos),this.pos=Xe)}moveCursor(e){const t=this.pos+e;if(e>1)for(let s=this.pos+1;s=144&&this.backSpace();const t=Ba(e);if(this.pos>=Xe){this.logger.log(0,()=>"Cannot insert "+e.toString(16)+" ("+t+") at position "+this.pos+". Skipping it!");return}this.chars[this.pos].setChar(t,this.currPenState),this.moveCursor(1)}clearFromPos(e){let t;for(t=e;t"pacData = "+oe(e));let t=e.row-1;if(this.nrRollUpRows&&t"bkgData = "+oe(e)),this.backSpace(),this.setPen(e),this.insertChar(32)}setRollUpRows(e){this.nrRollUpRows=e}rollUp(){if(this.nrRollUpRows===null){this.logger.log(3,"roll_up but nrRollUpRows not set yet");return}this.logger.log(1,()=>this.getDisplayText());const e=this.currRow+1-this.nrRollUpRows,t=this.rows.splice(e,1)[0];t.clear(),this.rows.splice(this.currRow,0,t),this.logger.log(2,"Rolling up")}getDisplayText(e){e=e||!1;const t=[];let s="",i=-1;for(let r=0;r0&&(e?s="["+t.join(" | ")+"]":s=t.join(` -`)),s}getTextAndFormat(){return this.rows}}class nn{constructor(e,t,s){this.chNr=void 0,this.outputFilter=void 0,this.mode=void 0,this.verbose=void 0,this.displayedMemory=void 0,this.nonDisplayedMemory=void 0,this.lastOutputScreen=void 0,this.currRollUpRow=void 0,this.writeScreen=void 0,this.cueStartTime=void 0,this.logger=void 0,this.chNr=e,this.outputFilter=t,this.mode=null,this.verbose=0,this.displayedMemory=new js(s),this.nonDisplayedMemory=new js(s),this.lastOutputScreen=new js(s),this.currRollUpRow=this.displayedMemory.rows[Me-1],this.writeScreen=this.displayedMemory,this.mode=null,this.cueStartTime=null,this.logger=s}reset(){this.mode=null,this.displayedMemory.reset(),this.nonDisplayedMemory.reset(),this.lastOutputScreen.reset(),this.outputFilter.reset(),this.currRollUpRow=this.displayedMemory.rows[Me-1],this.writeScreen=this.displayedMemory,this.mode=null,this.cueStartTime=null}getHandler(){return this.outputFilter}setHandler(e){this.outputFilter=e}setPAC(e){this.writeScreen.setPAC(e)}setBkgData(e){this.writeScreen.setBkgData(e)}setMode(e){e!==this.mode&&(this.mode=e,this.logger.log(2,()=>"MODE="+e),this.mode==="MODE_POP-ON"?this.writeScreen=this.nonDisplayedMemory:(this.writeScreen=this.displayedMemory,this.writeScreen.reset()),this.mode!=="MODE_ROLL-UP"&&(this.displayedMemory.nrRollUpRows=null,this.nonDisplayedMemory.nrRollUpRows=null),this.mode=e)}insertChars(e){for(let s=0;st+": "+this.writeScreen.getDisplayText(!0)),(this.mode==="MODE_PAINT-ON"||this.mode==="MODE_ROLL-UP")&&(this.logger.log(1,()=>"DISPLAYED: "+this.displayedMemory.getDisplayText(!0)),this.outputDataUpdate())}ccRCL(){this.logger.log(2,"RCL - Resume Caption Loading"),this.setMode("MODE_POP-ON")}ccBS(){this.logger.log(2,"BS - BackSpace"),this.mode!=="MODE_TEXT"&&(this.writeScreen.backSpace(),this.writeScreen===this.displayedMemory&&this.outputDataUpdate())}ccAOF(){}ccAON(){}ccDER(){this.logger.log(2,"DER- Delete to End of Row"),this.writeScreen.clearToEndOfRow(),this.outputDataUpdate()}ccRU(e){this.logger.log(2,"RU("+e+") - Roll Up"),this.writeScreen=this.displayedMemory,this.setMode("MODE_ROLL-UP"),this.writeScreen.setRollUpRows(e)}ccFON(){this.logger.log(2,"FON - Flash On"),this.writeScreen.setPen({flash:!0})}ccRDC(){this.logger.log(2,"RDC - Resume Direct Captioning"),this.setMode("MODE_PAINT-ON")}ccTR(){this.logger.log(2,"TR"),this.setMode("MODE_TEXT")}ccRTD(){this.logger.log(2,"RTD"),this.setMode("MODE_TEXT")}ccEDM(){this.logger.log(2,"EDM - Erase Displayed Memory"),this.displayedMemory.reset(),this.outputDataUpdate(!0)}ccCR(){this.logger.log(2,"CR - Carriage Return"),this.writeScreen.rollUp(),this.outputDataUpdate(!0)}ccENM(){this.logger.log(2,"ENM - Erase Non-displayed Memory"),this.nonDisplayedMemory.reset()}ccEOC(){if(this.logger.log(2,"EOC - End Of Caption"),this.mode==="MODE_POP-ON"){const e=this.displayedMemory;this.displayedMemory=this.nonDisplayedMemory,this.nonDisplayedMemory=e,this.writeScreen=this.nonDisplayedMemory,this.logger.log(1,()=>"DISP: "+this.displayedMemory.getDisplayText())}this.outputDataUpdate(!0)}ccTO(e){this.logger.log(2,"TO("+e+") - Tab Offset"),this.writeScreen.moveCursor(e)}ccMIDROW(e){const t={flash:!1};if(t.underline=e%2===1,t.italics=e>=46,t.italics)t.foreground="white";else{const s=Math.floor(e/2)-16,i=["white","green","blue","cyan","red","yellow","magenta"];t.foreground=i[s]}this.logger.log(2,"MIDROW: "+oe(t)),this.writeScreen.setPen(t)}outputDataUpdate(e=!1){const t=this.logger.time;t!==null&&this.outputFilter&&(this.cueStartTime===null&&!this.displayedMemory.isEmpty()?this.cueStartTime=t:this.displayedMemory.equals(this.lastOutputScreen)||(this.outputFilter.newCue(this.cueStartTime,t,this.lastOutputScreen),e&&this.outputFilter.dispatchCue&&this.outputFilter.dispatchCue(),this.cueStartTime=this.displayedMemory.isEmpty()?null:t),this.lastOutputScreen.copy(this.displayedMemory))}cueSplitAtTime(e){this.outputFilter&&(this.displayedMemory.isEmpty()||(this.outputFilter.newCue&&this.outputFilter.newCue(this.cueStartTime,e,this.displayedMemory),this.cueStartTime=e))}}class an{constructor(e,t,s){this.channels=void 0,this.currentChannel=0,this.cmdHistory=gu(),this.logger=void 0;const i=this.logger=new hu;this.channels=[null,new nn(e,t,i),new nn(e+1,s,i)]}getHandler(e){return this.channels[e].getHandler()}setHandler(e,t){this.channels[e].setHandler(t)}addData(e,t){this.logger.time=e;for(let s=0;s"["+dt([t[s],t[s+1]])+"] -> ("+dt([i,r])+")");const c=this.cmdHistory;if(i>=16&&i<=31){if(fu(i,r,c)){rs(null,null,c),this.logger.log(3,()=>"Repeated command ("+dt([i,r])+") is dropped");continue}rs(i,r,this.cmdHistory),n=this.parseCmd(i,r),n||(n=this.parseMidrow(i,r)),n||(n=this.parsePAC(i,r)),n||(n=this.parseBackgroundAttributes(i,r))}else rs(null,null,c);if(!n&&(o=this.parseChars(i,r),o)){const h=this.currentChannel;h&&h>0?this.channels[h].insertChars(o):this.logger.log(2,"No channel found yet. TEXT-MODE?")}!n&&!o&&this.logger.log(2,()=>"Couldn't parse cleaned data "+dt([i,r])+" orig: "+dt([t[s],t[s+1]]))}}parseCmd(e,t){const s=(e===20||e===28||e===21||e===29)&&t>=32&&t<=47,i=(e===23||e===31)&&t>=33&&t<=35;if(!(s||i))return!1;const r=e===20||e===21||e===23?1:2,n=this.channels[r];return e===20||e===21||e===28||e===29?t===32?n.ccRCL():t===33?n.ccBS():t===34?n.ccAOF():t===35?n.ccAON():t===36?n.ccDER():t===37?n.ccRU(2):t===38?n.ccRU(3):t===39?n.ccRU(4):t===40?n.ccFON():t===41?n.ccRDC():t===42?n.ccTR():t===43?n.ccRTD():t===44?n.ccEDM():t===45?n.ccCR():t===46?n.ccENM():t===47&&n.ccEOC():n.ccTO(t-32),this.currentChannel=r,!0}parseMidrow(e,t){let s=0;if((e===17||e===25)&&t>=32&&t<=47){if(e===17?s=1:s=2,s!==this.currentChannel)return this.logger.log(0,"Mismatch channel in midrow parsing"),!1;const i=this.channels[s];return i?(i.ccMIDROW(t),this.logger.log(3,()=>"MIDROW ("+dt([e,t])+")"),!0):!1}return!1}parsePAC(e,t){let s;const i=(e>=17&&e<=23||e>=25&&e<=31)&&t>=64&&t<=127,r=(e===16||e===24)&&t>=64&&t<=95;if(!(i||r))return!1;const n=e<=23?1:2;t>=64&&t<=95?s=n===1?nu[e]:ou[e]:s=n===1?au[e]:lu[e];const o=this.channels[n];return o?(o.setPAC(this.interpretPAC(s,t)),this.currentChannel=n,!0):!1}interpretPAC(e,t){let s;const i={color:null,italics:!1,indent:null,underline:!1,row:e};return t>95?s=t-96:s=t-64,i.underline=(s&1)===1,s<=13?i.color=["white","green","blue","cyan","red","yellow","magenta","white"][Math.floor(s/2)]:s<=15?(i.italics=!0,i.color="white"):i.indent=Math.floor((s-16)/2)*4,i}parseChars(e,t){let s,i=null,r=null;if(e>=25?(s=2,r=e-8):(s=1,r=e),r>=17&&r<=19){let n;r===17?n=t+80:r===18?n=t+112:n=t+144,this.logger.log(2,()=>"Special char '"+Ba(n)+"' in channel "+s),i=[n]}else e>=32&&e<=127&&(i=t===0?[e]:[e,t]);return i&&this.logger.log(3,()=>"Char codes = "+dt(i).join(",")),i}parseBackgroundAttributes(e,t){const s=(e===16||e===24)&&t>=32&&t<=47,i=(e===23||e===31)&&t>=45&&t<=47;if(!(s||i))return!1;let r;const n={};e===16||e===24?(r=Math.floor((t-32)/2),n.background=cu[r],t%2===1&&(n.background=n.background+"_semi")):t===45?n.background="transparent":(n.foreground="black",t===47&&(n.underline=!0));const o=e<=23?1:2;return this.channels[o].setBkgData(n),!0}reset(){for(let e=0;e100)throw new Error("Position must be between 0 and 100.");D=I,this.hasBeenReset=!0}})),Object.defineProperty(h,"positionAlign",r({},u,{get:function(){return A},set:function(I){const P=i(I);if(!P)throw new SyntaxError("An invalid or illegal string was specified.");A=P,this.hasBeenReset=!0}})),Object.defineProperty(h,"size",r({},u,{get:function(){return _},set:function(I){if(I<0||I>100)throw new Error("Size must be between 0 and 100.");_=I,this.hasBeenReset=!0}})),Object.defineProperty(h,"align",r({},u,{get:function(){return b},set:function(I){const P=i(I);if(!P)throw new SyntaxError("An invalid or illegal string was specified.");b=P,this.hasBeenReset=!0}})),h.displayState=void 0}return n.prototype.getCueAsHTML=function(){return self.WebVTT.convertCueToDOMTree(self,this.text)},n}();class mu{decode(e,t){if(!e)return"";if(typeof e!="string")throw new Error("Error - expected string data.");return decodeURIComponent(encodeURIComponent(e))}}function $a(a){function e(s,i,r,n){return(s|0)*3600+(i|0)*60+(r|0)+parseFloat(n||0)}const t=a.match(/^(?:(\d+):)?(\d{2}):(\d{2})(\.\d+)?/);return t?parseFloat(t[2])>59?e(t[2],t[3],0,t[4]):e(t[1],t[2],t[3],t[4]):null}class pu{constructor(){this.values=Object.create(null)}set(e,t){!this.get(e)&&t!==""&&(this.values[e]=t)}get(e,t,s){return s?this.has(e)?this.values[e]:t[s]:this.has(e)?this.values[e]:t}has(e){return e in this.values}alt(e,t,s){for(let i=0;i=0&&s<=100)return this.set(e,s),!0}return!1}}function Ga(a,e,t,s){const i=s?a.split(s):[a];for(const r in i){if(typeof i[r]!="string")continue;const n=i[r].split(t);if(n.length!==2)continue;const o=n[0],c=n[1];e(o,c)}}const Ti=new Yi(0,0,""),ns=Ti.align==="middle"?"middle":"center";function yu(a,e,t){const s=a;function i(){const o=$a(a);if(o===null)throw new Error("Malformed timestamp: "+s);return a=a.replace(/^[^\sa-zA-Z-]+/,""),o}function r(o,c){const l=new pu;Ga(o,function(d,f){let g;switch(d){case"region":for(let y=t.length-1;y>=0;y--)if(t[y].id===f){l.set(d,t[y].region);break}break;case"vertical":l.alt(d,f,["rl","lr"]);break;case"line":g=f.split(","),l.integer(d,g[0]),l.percent(d,g[0])&&l.set("snapToLines",!1),l.alt(d,g[0],["auto"]),g.length===2&&l.alt("lineAlign",g[1],["start",ns,"end"]);break;case"position":g=f.split(","),l.percent(d,g[0]),g.length===2&&l.alt("positionAlign",g[1],["start",ns,"end","line-left","line-right","auto"]);break;case"size":l.percent(d,f);break;case"align":l.alt(d,f,["start",ns,"end","left","right"]);break}},/:/,/\s/),c.region=l.get("region",null),c.vertical=l.get("vertical","");let h=l.get("line","auto");h==="auto"&&Ti.line===-1&&(h=-1),c.line=h,c.lineAlign=l.get("lineAlign","start"),c.snapToLines=l.get("snapToLines",!0),c.size=l.get("size",100),c.align=l.get("align",ns);let u=l.get("position","auto");u==="auto"&&Ti.position===50&&(u=c.align==="start"||c.align==="left"?0:c.align==="end"||c.align==="right"?100:50),c.position=u}function n(){a=a.replace(/^\s+/,"")}if(n(),e.startTime=i(),n(),a.slice(0,3)!=="-->")throw new Error("Malformed time stamp (time stamps must be separated by '-->'): "+s);a=a.slice(3),n(),e.endTime=i(),n(),r(a,e)}function Ka(a){return a.replace(//gi,` -`)}class Eu{constructor(){this.state="INITIAL",this.buffer="",this.decoder=new mu,this.regionList=[],this.cue=null,this.oncue=void 0,this.onparsingerror=void 0,this.onflush=void 0}parse(e){const t=this;e&&(t.buffer+=t.decoder.decode(e,{stream:!0}));function s(){let r=t.buffer,n=0;for(r=Ka(r);nc==="initSegment"?void 0:l)}}`),!xn(i,s)){t.mediaSource=null,t.tracks=void 0;const c=e.currentTime,l=this.details,h=Math.max(c,(l==null?void 0:l.fragments[0].start)||0);if(h-c>1){this.log(`attachTransferred: waiting for playback to reach new tracks start time ${c} -> ${h}`);return}this.warn(`attachTransferred: resetting MediaSource for incompatible tracks ("${Object.keys(i)}"->"${Object.keys(s)}") start time: ${h} currentTime: ${c}`),this.onMediaDetaching(m.MEDIA_DETACHING,{}),this.onMediaAttaching(m.MEDIA_ATTACHING,t),e.currentTime=h;return}this.transferData=void 0,r.forEach(c=>{const l=c,h=i[l];if(h){const d=h.buffer;if(d){const u=this.fragmentTracker,f=h.id;if(u.hasFragments(f)||u.hasParts(f)){const p=X.getBuffered(d);u.detectEvictedFragments(l,p,f,null,!0)}const g=Ws(l),y=[l,d];this.sourceBuffers[g]=y,d.updating&&this.operationQueue&&this.operationQueue.prependBlocker(l),this.trackSourceBuffer(l,h)}}}),o(),this.bufferCreated()}else this.log("attachTransferred: MediaSource w/o SourceBuffers"),o()}get mediaSourceOpenOrEnded(){var e;const t=(e=this.mediaSource)==null?void 0:e.readyState;return t==="open"||t==="ended"}onMediaDetaching(e,t){const s=!!t.transferMedia;this.transferData=this.overrides=void 0;const{media:i,mediaSource:r,_objectUrl:n}=this;if(r){if(this.log(`media source ${s?"transferring":"detaching"}`),s)this.sourceBuffers.forEach(([o])=>{o&&this.removeBuffer(o)}),this.resetQueue();else{if(this.mediaSourceOpenOrEnded){const o=r.readyState==="open";try{const c=r.sourceBuffers;for(let l=c.length;l--;)o&&c[l].abort(),r.removeSourceBuffer(c[l]);o&&r.endOfStream()}catch(c){this.warn(`onMediaDetaching: ${c.message} while calling endOfStream`)}}this.sourceBufferCount&&this.onBufferReset()}r.removeEventListener("sourceopen",this._onMediaSourceOpen),r.removeEventListener("sourceended",this._onMediaSourceEnded),r.removeEventListener("sourceclose",this._onMediaSourceClose),this.appendSource&&(r.removeEventListener("startstreaming",this._onStartStreaming),r.removeEventListener("endstreaming",this._onEndStreaming)),this.mediaSource=null,this._objectUrl=null}i&&(i.removeEventListener("emptied",this._onMediaEmptied),s||(n&&self.URL.revokeObjectURL(n),this.mediaSrc===n?(i.removeAttribute("src"),this.appendSource&&Qr(i),i.load()):this.warn("media|source.src was changed by a third party - skip cleanup")),this.media=null),this.hls.trigger(m.MEDIA_DETACHED,t)}onBufferReset(){this.sourceBuffers.forEach(([e])=>{e&&this.resetBuffer(e)}),this.initTracks()}resetBuffer(e){var t;const s=(t=this.tracks[e])==null?void 0:t.buffer;if(this.removeBuffer(e),s)try{var i;(i=this.mediaSource)!=null&&i.sourceBuffers.length&&this.mediaSource.removeSourceBuffer(s)}catch(r){this.warn(`onBufferReset ${e}`,r)}delete this.tracks[e]}removeBuffer(e){this.removeBufferListeners(e),this.sourceBuffers[Ws(e)]=[null,null];const t=this.tracks[e];t&&(t.buffer=void 0)}resetQueue(){this.operationQueue&&this.operationQueue.destroy(),this.operationQueue=new Hc(this.tracks)}onBufferCodecs(e,t){var s;const i=this.tracks,r=Object.keys(t);this.log(`BUFFER_CODECS: "${r}" (current SB count ${this.sourceBufferCount})`);const n="audiovideo"in t&&(i.audio||i.video)||i.audiovideo&&("audio"in t||"video"in t),o=!n&&this.sourceBufferCount&&this.media&&r.some(c=>!i[c]);if(n||o){this.warn(`Unsupported transition between "${Object.keys(i)}" and "${r}" SourceBuffers`);return}r.forEach(c=>{var l,h;const d=t[c],{id:u,codec:f,levelCodec:g,container:y,metadata:p,supplemental:E}=d;let T=i[c];const S=(l=this.transferData)==null||(l=l.tracks)==null?void 0:l[c],v=S!=null&&S.buffer?S:T,x=(v==null?void 0:v.pendingCodec)||(v==null?void 0:v.codec),D=v==null?void 0:v.levelCodec;T||(T=i[c]={buffer:void 0,listeners:[],codec:f,supplemental:E,container:y,levelCodec:g,metadata:p,id:u});const A=hs(x,D),_=A==null?void 0:A.replace(zr,"$1");let b=hs(f,g);const I=(h=b)==null?void 0:h.replace(zr,"$1");b&&A&&_!==I&&(c.slice(0,5)==="audio"&&(b=Ts(b,this.appendSource)),this.log(`switching codec ${x} to ${b}`),b!==(T.pendingCodec||T.codec)&&(T.pendingCodec=b),T.container=y,this.appendChangeType(c,y,b))}),(this.tracksReady||this.sourceBufferCount)&&(t.tracks=this.sourceBufferTracks),!this.sourceBufferCount&&(this.bufferCodecEventsTotal>1&&!this.tracks.video&&!t.video&&((s=t.audio)==null?void 0:s.id)==="main"&&(this.log("Main audio-only"),this.bufferCodecEventsTotal=1),this.mediaSourceOpenOrEnded&&this.checkPendingTracks())}get sourceBufferTracks(){return Object.keys(this.tracks).reduce((e,t)=>{const s=this.tracks[t];return e[t]={id:s.id,container:s.container,codec:s.codec,levelCodec:s.levelCodec},e},{})}appendChangeType(e,t,s){const i=`${t};codecs=${s}`,r={label:`change-type=${i}`,execute:()=>{const n=this.tracks[e];if(n){const o=n.buffer;o!=null&&o.changeType&&(this.log(`changing ${e} sourceBuffer type to ${i}`),o.changeType(i),n.codec=s,n.container=t)}this.shiftAndExecuteNext(e)},onStart:()=>{},onComplete:()=>{},onError:n=>{this.warn(`Failed to change ${e} SourceBuffer type`,n)}};this.append(r,e,this.isPending(this.tracks[e]))}blockAudio(e){var t;const s=e.start,i=s+e.duration*.05;if(((t=this.fragmentTracker.getAppendedFrag(s,U.MAIN))==null?void 0:t.gap)===!0)return;const n={label:"block-audio",execute:()=>{var o;const c=this.tracks.video;(this.lastVideoAppendEnd>i||c!=null&&c.buffer&&X.isBuffered(c.buffer,i)||((o=this.fragmentTracker.getAppendedFrag(i,U.MAIN))==null?void 0:o.gap)===!0)&&(this.blockedAudioAppend=null,this.shiftAndExecuteNext("audio"))},onStart:()=>{},onComplete:()=>{},onError:o=>{this.warn("Error executing block-audio operation",o)}};this.blockedAudioAppend={op:n,frag:e},this.append(n,"audio",!0)}unblockAudio(){const{blockedAudioAppend:e,operationQueue:t}=this;e&&t&&(this.blockedAudioAppend=null,t.unblockAudio(e.op))}onBufferAppending(e,t){const{tracks:s}=this,{data:i,type:r,parent:n,frag:o,part:c,chunkMeta:l,offset:h}=t,d=l.buffering[r],{sn:u,cc:f}=o,g=self.performance.now();d.start=g;const y=o.stats.buffering,p=c?c.stats.buffering:null;y.start===0&&(y.start=g),p&&p.start===0&&(p.start=g);const E=s.audio;let T=!1;r==="audio"&&(E==null?void 0:E.container)==="audio/mpeg"&&(T=!this.lastMpegAudioChunk||l.id===1||this.lastMpegAudioChunk.sn!==l.sn,this.lastMpegAudioChunk=l);const S=s.video,v=S==null?void 0:S.buffer;if(v&&u!=="initSegment"){const A=c||o,_=this.blockedAudioAppend;if(r==="audio"&&n!=="main"&&!this.blockedAudioAppend&&!(S.ending||S.ended)){const I=A.start+A.duration*.05,P=v.buffered,M=this.currentOp("video");!P.length&&!M?this.blockAudio(A):!M&&!X.isBuffered(v,I)&&this.lastVideoAppendEndI||b{var A;d.executeStart=self.performance.now();const _=(A=this.tracks[r])==null?void 0:A.buffer;_&&(T?this.updateTimestampOffset(_,x,.1,r,u,f):h!==void 0&&B(h)&&this.updateTimestampOffset(_,h,1e-6,r,u,f)),this.appendExecutor(i,r)},onStart:()=>{},onComplete:()=>{const A=self.performance.now();d.executeEnd=d.end=A,y.first===0&&(y.first=A),p&&p.first===0&&(p.first=A);const _={};this.sourceBuffers.forEach(([b,I])=>{b&&(_[b]=X.getBuffered(I))}),this.appendErrors[r]=0,r==="audio"||r==="video"?this.appendErrors.audiovideo=0:(this.appendErrors.audio=0,this.appendErrors.video=0),this.hls.trigger(m.BUFFER_APPENDED,{type:r,frag:o,part:c,chunkMeta:l,parent:o.type,timeRanges:_})},onError:A=>{var _;const b={type:Y.MEDIA_ERROR,parent:o.type,details:R.BUFFER_APPEND_ERROR,sourceBufferName:r,frag:o,part:c,chunkMeta:l,error:A,err:A,fatal:!1},I=(_=this.media)==null?void 0:_.error;if(A.code===DOMException.QUOTA_EXCEEDED_ERR||A.name=="QuotaExceededError"||"quota"in A)b.details=R.BUFFER_FULL_ERROR;else if(A.code===DOMException.INVALID_STATE_ERR&&this.mediaSourceOpenOrEnded&&!I)b.errorAction=bt(!0);else if(A.name===Ra&&this.sourceBufferCount===0)b.errorAction=bt(!0);else{const P=++this.appendErrors[r];this.warn(`Failed ${P}/${this.hls.config.appendErrorMaxRetry} times to append segment in "${r}" sourceBuffer (${I||"no media error"})`),(P>=this.hls.config.appendErrorMaxRetry||I)&&(b.fatal=!0)}this.hls.trigger(m.ERROR,b)}};this.log(`queuing "${r}" append sn: ${u}${c?" p: "+c.index:""} of ${o.type===U.MAIN?"level":"track"} ${o.level} cc: ${f}`),this.append(D,r,this.isPending(this.tracks[r]))}getFlushOp(e,t,s){return this.log(`queuing "${e}" remove ${t}-${s}`),{label:"remove",execute:()=>{this.removeExecutor(e,t,s)},onStart:()=>{},onComplete:()=>{this.hls.trigger(m.BUFFER_FLUSHED,{type:e})},onError:i=>{this.warn(`Failed to remove ${t}-${s} from "${e}" SourceBuffer`,i)}}}onBufferFlushing(e,t){const{type:s,startOffset:i,endOffset:r}=t;s?this.append(this.getFlushOp(s,i,r),s):this.sourceBuffers.forEach(([n])=>{n&&this.append(this.getFlushOp(n,i,r),n)})}onFragParsed(e,t){const{frag:s,part:i}=t,r=[],n=i?i.elementaryStreams:s.elementaryStreams;n[ae.AUDIOVIDEO]?r.push("audiovideo"):(n[ae.AUDIO]&&r.push("audio"),n[ae.VIDEO]&&r.push("video"));const o=()=>{const c=self.performance.now();s.stats.buffering.end=c,i&&(i.stats.buffering.end=c);const l=i?i.stats:s.stats;this.hls.trigger(m.FRAG_BUFFERED,{frag:s,part:i,stats:l,id:s.type})};r.length===0&&this.warn(`Fragments must have at least one ElementaryStreamType set. type: ${s.type} level: ${s.level} sn: ${s.sn}`),this.blockBuffers(o,r).catch(c=>{this.warn(`Fragment buffered callback ${c}`),this.stepOperationQueue(this.sourceBufferTypes)})}onFragChanged(e,t){this.trimBuffers()}get bufferedToEnd(){return this.sourceBufferCount>0&&!this.sourceBuffers.some(([e])=>{if(e){const t=this.tracks[e];if(t)return!t.ended||t.ending}return!1})}onBufferEos(e,t){var s;this.sourceBuffers.forEach(([n])=>{if(n){const o=this.tracks[n];(!t.type||t.type===n)&&(o.ending=!0,o.ended||(o.ended=!0,this.log(`${n} buffer reached EOS`)))}});const i=((s=this.overrides)==null?void 0:s.endOfStream)!==!1;this.sourceBufferCount>0&&!this.sourceBuffers.some(([n])=>{var o;return n&&!((o=this.tracks[n])!=null&&o.ended)})?i?(this.log("Queueing EOS"),this.blockUntilOpen(()=>{this.tracksEnded();const{mediaSource:n}=this;if(!n||n.readyState!=="open"){n&&this.log(`Could not call mediaSource.endOfStream(). mediaSource.readyState: ${n.readyState}`);return}this.log("Calling mediaSource.endOfStream()"),n.endOfStream(),this.hls.trigger(m.BUFFERED_TO_END,void 0)})):(this.tracksEnded(),this.hls.trigger(m.BUFFERED_TO_END,void 0)):t.type==="video"&&this.unblockAudio()}tracksEnded(){this.sourceBuffers.forEach(([e])=>{if(e!==null){const t=this.tracks[e];t&&(t.ending=!1)}})}onLevelUpdated(e,{details:t}){t.fragments.length&&(this.details=t,this.updateDuration())}updateDuration(){this.blockUntilOpen(()=>{const e=this.getDurationAndRange();e&&this.updateMediaSource(e)})}onError(e,t){if(t.details===R.BUFFER_APPEND_ERROR&&t.frag){var s;const i=(s=t.errorAction)==null?void 0:s.nextAutoLevel;B(i)&&i!==t.frag.level&&this.resetAppendErrors()}}resetAppendErrors(){this.appendErrors={audio:0,video:0,audiovideo:0}}trimBuffers(){const{hls:e,details:t,media:s}=this;if(!s||t===null||!this.sourceBufferCount)return;const i=e.config,r=s.currentTime,n=t.levelTargetDuration,o=t.live&&i.liveBackBufferLength!==null?i.liveBackBufferLength:i.backBufferLength;if(B(o)&&o>=0){const l=Math.max(o,n),h=Math.floor(r/n)*n-l;this.flushBackBuffer(r,n,h)}const c=i.frontBufferFlushThreshold;if(B(c)&&c>0){const l=Math.max(i.maxBufferLength,c),h=Math.max(l,n),d=Math.floor(r/n)*n+h;this.flushFrontBuffer(r,n,d)}}flushBackBuffer(e,t,s){this.sourceBuffers.forEach(([i,r])=>{if(r){const o=X.getBuffered(r);if(o.length>0&&s>o.start(0)){var n;this.hls.trigger(m.BACK_BUFFER_REACHED,{bufferEnd:s});const c=this.tracks[i];if((n=this.details)!=null&&n.live)this.hls.trigger(m.LIVE_BACK_BUFFER_REACHED,{bufferEnd:s});else if(c!=null&&c.ended){this.log(`Cannot flush ${i} back buffer while SourceBuffer is in ended state`);return}this.hls.trigger(m.BUFFER_FLUSHING,{startOffset:0,endOffset:s,type:i})}}})}flushFrontBuffer(e,t,s){this.sourceBuffers.forEach(([i,r])=>{if(r){const n=X.getBuffered(r),o=n.length;if(o<2)return;const c=n.start(o-1),l=n.end(o-1);if(s>c||e>=c&&e<=l)return;this.hls.trigger(m.BUFFER_FLUSHING,{startOffset:c,endOffset:1/0,type:i})}})}getDurationAndRange(){var e;const{details:t,mediaSource:s}=this;if(!t||!this.media||(s==null?void 0:s.readyState)!=="open")return null;const i=t.edge;if(t.live&&this.hls.config.liveDurationInfinity){if(t.fragments.length&&s.setLiveSeekableRange){const l=Math.max(0,t.fragmentStart),h=Math.max(l,i);return{duration:1/0,start:l,end:h}}return{duration:1/0}}const r=(e=this.overrides)==null?void 0:e.duration;if(r)return B(r)?{duration:r}:null;const n=this.media.duration,o=B(s.duration)?s.duration:0;return i>o&&i>n||!B(n)?{duration:i}:null}updateMediaSource({duration:e,start:t,end:s}){const i=this.mediaSource;!this.media||!i||i.readyState!=="open"||(i.duration!==e&&(B(e)&&this.log(`Updating MediaSource duration to ${e.toFixed(3)}`),i.duration=e),t!==void 0&&s!==void 0&&(this.log(`MediaSource duration is set to ${i.duration}. Setting seekable range to ${t}-${s}.`),i.setLiveSeekableRange(t,s)))}get tracksReady(){const e=this.pendingTrackCount;return e>0&&(e>=this.bufferCodecEventsTotal||this.isPending(this.tracks.audiovideo))}checkPendingTracks(){const{bufferCodecEventsTotal:e,pendingTrackCount:t,tracks:s}=this;if(this.log(`checkPendingTracks (pending: ${t} codec events expected: ${e}) ${oe(s)}`),this.tracksReady){var i;const r=(i=this.transferData)==null?void 0:i.tracks;r&&Object.keys(r).length?this.attachTransferred():this.createSourceBuffers()}}bufferCreated(){if(this.sourceBufferCount){const e={};this.sourceBuffers.forEach(([t,s])=>{if(t){const i=this.tracks[t];e[t]={buffer:s,container:i.container,codec:i.codec,supplemental:i.supplemental,levelCodec:i.levelCodec,id:i.id,metadata:i.metadata}}}),this.hls.trigger(m.BUFFER_CREATED,{tracks:e}),this.log(`SourceBuffers created. Running queue: ${this.operationQueue}`),this.sourceBuffers.forEach(([t])=>{this.executeNext(t)})}else{const e=new Error("could not create source buffer for media codec(s)");this.hls.trigger(m.ERROR,{type:Y.MEDIA_ERROR,details:R.BUFFER_INCOMPATIBLE_CODECS_ERROR,fatal:!0,error:e,reason:e.message})}}createSourceBuffers(){const{tracks:e,sourceBuffers:t,mediaSource:s}=this;if(!s)throw new Error("createSourceBuffers called when mediaSource was null");for(const r in e){const n=r,o=e[n];if(this.isPending(o)){const c=this.getTrackCodec(o,n),l=`${o.container};codecs=${c}`;o.codec=c,this.log(`creating sourceBuffer(${l})${this.currentOp(n)?" Queued":""} ${oe(o)}`);try{const h=s.addSourceBuffer(l),d=Ws(n),u=[n,h];t[d]=u,o.buffer=h}catch(h){var i;this.error(`error while trying to add sourceBuffer: ${h.message}`),this.shiftAndExecuteNext(n),(i=this.operationQueue)==null||i.removeBlockers(),delete this.tracks[n],this.hls.trigger(m.ERROR,{type:Y.MEDIA_ERROR,details:R.BUFFER_ADD_CODEC_ERROR,fatal:!1,error:h,sourceBufferName:n,mimeType:l,parent:o.id});return}this.trackSourceBuffer(n,o)}}this.bufferCreated()}getTrackCodec(e,t){const s=e.supplemental;let i=e.codec;s&&(t==="video"||t==="audiovideo")&&Ut(s,"video")&&(i=Mo(i,s));const r=hs(i,e.levelCodec);return r?t.slice(0,5)==="audio"?Ts(r,this.appendSource):r:""}trackSourceBuffer(e,t){const s=t.buffer;if(!s)return;const i=this.getTrackCodec(t,e);this.tracks[e]={buffer:s,codec:i,container:t.container,levelCodec:t.levelCodec,supplemental:t.supplemental,metadata:t.metadata,id:t.id,listeners:[]},this.removeBufferListeners(e),this.addBufferListener(e,"updatestart",this.onSBUpdateStart),this.addBufferListener(e,"updateend",this.onSBUpdateEnd),this.addBufferListener(e,"error",this.onSBUpdateError),this.appendSource&&this.addBufferListener(e,"bufferedchange",(r,n)=>{const o=n.removedRanges;o!=null&&o.length&&this.hls.trigger(m.BUFFER_FLUSHED,{type:r})})}get mediaSrc(){var e,t;const s=((e=this.media)==null||(t=e.querySelector)==null?void 0:t.call(e,"source"))||this.media;return s==null?void 0:s.src}onSBUpdateStart(e){const t=this.currentOp(e);t&&t.onStart()}onSBUpdateEnd(e){var t;if(((t=this.mediaSource)==null?void 0:t.readyState)==="closed"){this.resetBuffer(e);return}const s=this.currentOp(e);s&&(s.onComplete(),this.shiftAndExecuteNext(e))}onSBUpdateError(e,t){var s;const i=new Error(`${e} SourceBuffer error. MediaSource readyState: ${(s=this.mediaSource)==null?void 0:s.readyState}`);this.error(`${i}`,t),this.hls.trigger(m.ERROR,{type:Y.MEDIA_ERROR,details:R.BUFFER_APPENDING_ERROR,sourceBufferName:e,error:i,fatal:!1});const r=this.currentOp(e);r&&r.onError(i)}updateTimestampOffset(e,t,s,i,r,n){const o=t-e.timestampOffset;Math.abs(o)>=s&&(this.log(`Updating ${i} SourceBuffer timestampOffset to ${t} (sn: ${r} cc: ${n})`),e.timestampOffset=t)}removeExecutor(e,t,s){const{media:i,mediaSource:r}=this,n=this.tracks[e],o=n==null?void 0:n.buffer;if(!i||!r||!o){this.warn(`Attempting to remove from the ${e} SourceBuffer, but it does not exist`),this.shiftAndExecuteNext(e);return}const c=B(i.duration)?i.duration:1/0,l=B(r.duration)?r.duration:1/0,h=Math.max(0,t),d=Math.min(s,c,l);d>h&&(!n.ending||n.ended)?(n.ended=!1,this.log(`Removing [${h},${d}] from the ${e} SourceBuffer`),o.remove(h,d)):this.shiftAndExecuteNext(e)}appendExecutor(e,t){const s=this.tracks[t],i=s==null?void 0:s.buffer;if(!i)throw new Yc(`Attempting to append to the ${t} SourceBuffer, but it does not exist`);s.ending=!1,s.ended=!1,i.appendBuffer(e)}blockUntilOpen(e){if(this.isUpdating()||this.isQueued())this.blockBuffers(e).catch(t=>{this.warn(`SourceBuffer blocked callback ${t}`),this.stepOperationQueue(this.sourceBufferTypes)});else try{e()}catch(t){this.warn(`Callback run without blocking ${this.operationQueue} ${t}`)}}isUpdating(){return this.sourceBuffers.some(([e,t])=>e&&t.updating)}isQueued(){return this.sourceBuffers.some(([e])=>e&&!!this.currentOp(e))}isPending(e){return!!e&&!e.buffer}blockBuffers(e,t=this.sourceBufferTypes){if(!t.length)return this.log("Blocking operation requested, but no SourceBuffers exist"),Promise.resolve().then(e);const{operationQueue:s}=this,i=t.map(n=>this.appendBlocker(n));return t.length>1&&!!this.blockedAudioAppend&&this.unblockAudio(),Promise.all(i).then(n=>{s===this.operationQueue&&(e(),this.stepOperationQueue(this.sourceBufferTypes))})}stepOperationQueue(e){e.forEach(t=>{var s;const i=(s=this.tracks[t])==null?void 0:s.buffer;!i||i.updating||this.shiftAndExecuteNext(t)})}append(e,t,s){this.operationQueue&&this.operationQueue.append(e,t,s)}appendBlocker(e){if(this.operationQueue)return this.operationQueue.appendBlocker(e)}currentOp(e){return this.operationQueue?this.operationQueue.current(e):null}executeNext(e){e&&this.operationQueue&&this.operationQueue.executeNext(e)}shiftAndExecuteNext(e){this.operationQueue&&this.operationQueue.shiftAndExecuteNext(e)}get pendingTrackCount(){return Object.keys(this.tracks).reduce((e,t)=>e+(this.isPending(this.tracks[t])?1:0),0)}get sourceBufferCount(){return this.sourceBuffers.reduce((e,[t])=>e+(t?1:0),0)}get sourceBufferTypes(){return this.sourceBuffers.map(([e])=>e).filter(e=>!!e)}addBufferListener(e,t,s){const i=this.tracks[e];if(!i)return;const r=i.buffer;if(!r)return;const n=s.bind(this,e);i.listeners.push({event:t,listener:n}),r.addEventListener(t,n)}removeBufferListeners(e){const t=this.tracks[e];if(!t)return;const s=t.buffer;s&&(t.listeners.forEach(i=>{s.removeEventListener(i.event,i.listener)}),t.listeners.length=0)}}function Qr(a){const e=a.querySelectorAll("source");[].slice.call(e).forEach(t=>{a.removeChild(t)})}function qc(a,e){const t=self.document.createElement("source");t.type="video/mp4",t.src=e,a.appendChild(t)}function Ws(a){return a==="audio"?1:0}class Hi{constructor(e){this.hls=void 0,this.autoLevelCapping=void 0,this.firstLevel=void 0,this.media=void 0,this.restrictedLevels=void 0,this.timer=void 0,this.clientRect=void 0,this.streamController=void 0,this.hls=e,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.firstLevel=-1,this.media=null,this.restrictedLevels=[],this.timer=void 0,this.clientRect=null,this.registerListeners()}setStreamController(e){this.streamController=e}destroy(){this.hls&&this.unregisterListener(),this.timer&&this.stopCapping(),this.media=null,this.clientRect=null,this.hls=this.streamController=null}registerListeners(){const{hls:e}=this;e.on(m.FPS_DROP_LEVEL_CAPPING,this.onFpsDropLevelCapping,this),e.on(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(m.MANIFEST_PARSED,this.onManifestParsed,this),e.on(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(m.BUFFER_CODECS,this.onBufferCodecs,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this)}unregisterListener(){const{hls:e}=this;e.off(m.FPS_DROP_LEVEL_CAPPING,this.onFpsDropLevelCapping,this),e.off(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(m.MANIFEST_PARSED,this.onManifestParsed,this),e.off(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(m.BUFFER_CODECS,this.onBufferCodecs,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this)}onFpsDropLevelCapping(e,t){const s=this.hls.levels[t.droppedLevel];this.isLevelAllowed(s)&&this.restrictedLevels.push({bitrate:s.bitrate,height:s.height,width:s.width})}onMediaAttaching(e,t){this.media=t.media instanceof HTMLVideoElement?t.media:null,this.clientRect=null,this.timer&&this.hls.levels.length&&this.detectPlayerSize()}onManifestParsed(e,t){const s=this.hls;this.restrictedLevels=[],this.firstLevel=t.firstLevel,s.config.capLevelToPlayerSize&&t.video&&this.startCapping()}onLevelsUpdated(e,t){this.timer&&B(this.autoLevelCapping)&&this.detectPlayerSize()}onBufferCodecs(e,t){this.hls.config.capLevelToPlayerSize&&t.video&&this.startCapping()}onMediaDetaching(){this.stopCapping(),this.media=null}detectPlayerSize(){if(this.media){if(this.mediaHeight<=0||this.mediaWidth<=0){this.clientRect=null;return}const e=this.hls.levels;if(e.length){const t=this.hls,s=this.getMaxLevel(e.length-1);s!==this.autoLevelCapping&&t.logger.log(`Setting autoLevelCapping to ${s}: ${e[s].height}p@${e[s].bitrate} for media ${this.mediaWidth}x${this.mediaHeight}`),t.autoLevelCapping=s,t.autoLevelEnabled&&t.autoLevelCapping>this.autoLevelCapping&&this.streamController&&this.streamController.nextLevelSwitch(),this.autoLevelCapping=t.autoLevelCapping}}}getMaxLevel(e){const t=this.hls.levels;if(!t.length)return-1;const s=t.filter((i,r)=>this.isLevelAllowed(i)&&r<=e);return this.clientRect=null,Hi.getMaxLevelByMediaSize(s,this.mediaWidth,this.mediaHeight)}startCapping(){this.timer||(this.autoLevelCapping=Number.POSITIVE_INFINITY,self.clearInterval(this.timer),this.timer=self.setInterval(this.detectPlayerSize.bind(this),1e3),this.detectPlayerSize())}stopCapping(){this.restrictedLevels=[],this.firstLevel=-1,this.autoLevelCapping=Number.POSITIVE_INFINITY,this.timer&&(self.clearInterval(this.timer),this.timer=void 0)}getDimensions(){if(this.clientRect)return this.clientRect;const e=this.media,t={width:0,height:0};if(e){const s=e.getBoundingClientRect();t.width=s.width,t.height=s.height,!t.width&&!t.height&&(t.width=s.right-s.left||e.width||0,t.height=s.bottom-s.top||e.height||0)}return this.clientRect=t,t}get mediaWidth(){return this.getDimensions().width*this.contentScaleFactor}get mediaHeight(){return this.getDimensions().height*this.contentScaleFactor}get contentScaleFactor(){let e=1;if(!this.hls.config.ignoreDevicePixelRatio)try{e=self.devicePixelRatio}catch{}return Math.min(e,this.hls.config.maxDevicePixelRatio)}isLevelAllowed(e){return!this.restrictedLevels.some(s=>e.bitrate===s.bitrate&&e.width===s.width&&e.height===s.height)}static getMaxLevelByMediaSize(e,t,s){if(!(e!=null&&e.length))return-1;const i=(o,c)=>c?o.width!==c.width||o.height!==c.height:!0;let r=e.length-1;const n=Math.max(t,s);for(let o=0;o=n||c.height>=n)&&i(c,e[o+1])){r=o;break}}return r}}const jc={MANIFEST:"m",AUDIO:"a",VIDEO:"v",MUXED:"av",INIT:"i",CAPTION:"c",TIMED_TEXT:"tt",KEY:"k",OTHER:"o"},Ae=jc,Xc={HLS:"h"},zc=Xc;class je{constructor(e,t){Array.isArray(e)&&(e=e.map(s=>s instanceof je?s:new je(s))),this.value=e,this.params=t}}const Qc="Dict";function Zc(a){return Array.isArray(a)?JSON.stringify(a):a instanceof Map?"Map{}":a instanceof Set?"Set{}":typeof a=="object"?JSON.stringify(a):String(a)}function Jc(a,e,t,s){return new Error(`failed to ${a} "${Zc(e)}" as ${t}`,{cause:s})}function Xe(a,e,t){return Jc("serialize",a,e,t)}class ba{constructor(e){this.description=e}}const Zr="Bare Item",eh="Boolean";function th(a){if(typeof a!="boolean")throw Xe(a,eh);return a?"?1":"?0"}function sh(a){return btoa(String.fromCharCode(...a))}const ih="Byte Sequence";function rh(a){if(ArrayBuffer.isView(a)===!1)throw Xe(a,ih);return`:${sh(a)}:`}const nh="Integer";function ah(a){return a<-999999999999999||99999999999999912)throw Xe(a,lh);const t=e.toString();return t.includes(".")?t:`${t}.0`}const hh="String",dh=/[\x00-\x1f\x7f]+/;function uh(a){if(dh.test(a))throw Xe(a,hh);return`"${a.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`}function fh(a){return a.description||a.toString().slice(7,-1)}const gh="Token";function Jr(a){const e=fh(a);if(/^([a-zA-Z*])([!#$%&'*+\-.^_`|~\w:/]*)$/.test(e)===!1)throw Xe(e,gh);return e}function pi(a){switch(typeof a){case"number":if(!B(a))throw Xe(a,Zr);return Number.isInteger(a)?_a(a):ch(a);case"string":return uh(a);case"symbol":return Jr(a);case"boolean":return th(a);case"object":if(a instanceof Date)return oh(a);if(a instanceof Uint8Array)return rh(a);if(a instanceof ba)return Jr(a);default:throw Xe(a,Zr)}}const mh="Key";function yi(a){if(/^[a-z*][a-z0-9\-_.*]*$/.test(a)===!1)throw Xe(a,mh);return a}function Yi(a){return a==null?"":Object.entries(a).map(([e,t])=>t===!0?`;${yi(e)}`:`;${yi(e)}=${pi(t)}`).join("")}function Ca(a){return a instanceof je?`${pi(a.value)}${Yi(a.params)}`:pi(a)}function ph(a){return`(${a.value.map(Ca).join(" ")})${Yi(a.params)}`}function yh(a,e={whitespace:!0}){if(typeof a!="object"||a==null)throw Xe(a,Qc);const t=a instanceof Map?a.entries():Object.entries(a),s=e!=null&&e.whitespace?" ":"";return Array.from(t).map(([i,r])=>{r instanceof je||(r=new je(r));let n=yi(i);return r.value===!0?n+=Yi(r.params):(n+="=",Array.isArray(r.value)?n+=ph(r):n+=Ca(r)),n}).join(`,${s}`)}function Pa(a,e){return yh(a,e)}const Ge="CMCD-Object",he="CMCD-Request",dt="CMCD-Session",st="CMCD-Status",Eh={br:Ge,ab:Ge,d:Ge,ot:Ge,tb:Ge,tpb:Ge,lb:Ge,tab:Ge,lab:Ge,url:Ge,pb:he,bl:he,tbl:he,dl:he,ltc:he,mtp:he,nor:he,nrr:he,rc:he,sn:he,sta:he,su:he,ttfb:he,ttfbb:he,ttlb:he,cmsdd:he,cmsds:he,smrt:he,df:he,cs:he,ts:he,cid:dt,pr:dt,sf:dt,sid:dt,st:dt,v:dt,msd:dt,bs:st,bsd:st,cdn:st,rtp:st,bg:st,pt:st,ec:st,e:st},Th={REQUEST:he};function Sh(a){return Object.keys(a).reduce((e,t)=>{var s;return(s=a[t])===null||s===void 0||s.forEach(i=>e[i]=t),e},{})}function vh(a,e){const t={};if(!a)return t;const s=Object.keys(a),i=e?Sh(e):{};return s.reduce((r,n)=>{var o;const c=Eh[n]||i[n]||Th.REQUEST,l=(o=r[c])!==null&&o!==void 0?o:r[c]={};return l[n]=a[n],r},t)}function xh(a){return["ot","sf","st","e","sta"].includes(a)}function Ah(a){return typeof a=="number"?B(a):a!=null&&a!==""&&a!==!1}const ka="event";function Ih(a,e){const t=new URL(a),s=new URL(e);if(t.origin!==s.origin)return a;const i=t.pathname.split("/").slice(1),r=s.pathname.split("/").slice(1,-1);for(;i[0]===r[0];)i.shift(),r.shift();for(;r.length;)r.shift(),i.unshift("..");return i.join("/")+t.search+t.hash}const ms=a=>Math.round(a),Ei=(a,e)=>Array.isArray(a)?a.map(t=>Ei(t,e)):a instanceof je&&typeof a.value=="string"?new je(Ei(a.value,e),a.params):(e.baseUrl&&(a=Ih(a,e.baseUrl)),e.version===1?encodeURIComponent(a):a),is=a=>ms(a/100)*100,Lh=(a,e)=>{let t=a;return e.version>=2&&(a instanceof je&&typeof a.value=="string"?t=new je([a]):typeof a=="string"&&(t=[a])),Ei(t,e)},Rh={br:ms,d:ms,bl:is,dl:is,mtp:is,nor:Lh,rtp:is,tb:ms},wa="request",Oa="response",Wi=["ab","bg","bl","br","bs","bsd","cdn","cid","cs","df","ec","lab","lb","ltc","msd","mtp","pb","pr","pt","sf","sid","sn","st","sta","tab","tb","tbl","tpb","ts","v"],bh=["e"],_h=/^[a-zA-Z0-9-.]+-[a-zA-Z0-9-.]+$/;function Ps(a){return _h.test(a)}function Dh(a){return Wi.includes(a)||bh.includes(a)||Ps(a)}const Fa=["d","dl","nor","ot","rtp","su"];function Ch(a){return Wi.includes(a)||Fa.includes(a)||Ps(a)}const Ph=["cmsdd","cmsds","rc","smrt","ttfb","ttfbb","ttlb","url"];function kh(a){return Wi.includes(a)||Fa.includes(a)||Ph.includes(a)||Ps(a)}const wh=["bl","br","bs","cid","d","dl","mtp","nor","nrr","ot","pr","rtp","sf","sid","st","su","tb","v"];function Oh(a){return wh.includes(a)||Ps(a)}const Fh={[Oa]:kh,[ka]:Dh,[wa]:Ch};function Ma(a,e={}){const t={};if(a==null||typeof a!="object")return t;const s=e.version||a.v||1,i=e.reportingMode||wa,r=s===1?Oh:Fh[i];let n=Object.keys(a).filter(r);const o=e.filter;typeof o=="function"&&(n=n.filter(o));const c=i===Oa||i===ka;c&&!n.includes("ts")&&n.push("ts"),s>1&&!n.includes("v")&&n.push("v");const l=ne({},Rh,e.formatters),h={version:s,reportingMode:i,baseUrl:e.baseUrl};return n.sort().forEach(d=>{let u=a[d];const f=l[d];if(typeof f=="function"&&(u=f(u,h)),d==="v"){if(s===1)return;u=s}d=="pr"&&u===1||(c&&d==="ts"&&!B(u)&&(u=Date.now()),Ah(u)&&(xh(d)&&typeof u=="string"&&(u=new ba(u)),t[d]=u))}),t}function Mh(a,e={}){const t={};if(!a)return t;const s=Ma(a,e),i=vh(s,e==null?void 0:e.customHeaderMap);return Object.entries(i).reduce((r,[n,o])=>{const c=Pa(o,{whitespace:!1});return c&&(r[n]=c),r},t)}function Nh(a,e,t){return ne(a,Mh(e,t))}const Bh="CMCD";function $h(a,e={}){return a?Pa(Ma(a,e),{whitespace:!1}):""}function Uh(a,e={}){if(!a)return"";const t=$h(a,e);return encodeURIComponent(t)}function Gh(a,e={}){if(!a)return"";const t=Uh(a,e);return`${Bh}=${t}`}const en=/CMCD=[^&#]+/;function Kh(a,e,t){const s=Gh(e,t);if(!s)return a;if(en.test(a))return a.replace(en,s);const i=a.includes("?")?"&":"?";return`${a}${i}${s}`}class Vh{constructor(e){this.hls=void 0,this.config=void 0,this.media=void 0,this.sid=void 0,this.cid=void 0,this.useHeaders=!1,this.includeKeys=void 0,this.initialized=!1,this.starved=!1,this.buffering=!0,this.audioBuffer=void 0,this.videoBuffer=void 0,this.onWaiting=()=>{this.initialized&&(this.starved=!0),this.buffering=!0},this.onPlaying=()=>{this.initialized||(this.initialized=!0),this.buffering=!1},this.applyPlaylistData=i=>{try{this.apply(i,{ot:Ae.MANIFEST,su:!this.initialized})}catch(r){this.hls.logger.warn("Could not generate manifest CMCD data.",r)}},this.applyFragmentData=i=>{try{const{frag:r,part:n}=i,o=this.hls.levels[r.level],c=this.getObjectType(r),l={d:(n||r).duration*1e3,ot:c};(c===Ae.VIDEO||c===Ae.AUDIO||c==Ae.MUXED)&&(l.br=o.bitrate/1e3,l.tb=this.getTopBandwidth(c)/1e3,l.bl=this.getBufferLength(c));const h=n?this.getNextPart(n):this.getNextFrag(r);h!=null&&h.url&&h.url!==r.url&&(l.nor=h.url),this.apply(i,l)}catch(r){this.hls.logger.warn("Could not generate segment CMCD data.",r)}},this.hls=e;const t=this.config=e.config,{cmcd:s}=t;s!=null&&(t.pLoader=this.createPlaylistLoader(),t.fLoader=this.createFragmentLoader(),this.sid=s.sessionId||e.sessionId,this.cid=s.contentId,this.useHeaders=s.useHeaders===!0,this.includeKeys=s.includeKeys,this.registerListeners())}registerListeners(){const e=this.hls;e.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(m.MEDIA_DETACHED,this.onMediaDetached,this),e.on(m.BUFFER_CREATED,this.onBufferCreated,this)}unregisterListeners(){const e=this.hls;e.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(m.MEDIA_DETACHED,this.onMediaDetached,this),e.off(m.BUFFER_CREATED,this.onBufferCreated,this)}destroy(){this.unregisterListeners(),this.onMediaDetached(),this.hls=this.config=this.audioBuffer=this.videoBuffer=null,this.onWaiting=this.onPlaying=this.media=null}onMediaAttached(e,t){this.media=t.media,this.media.addEventListener("waiting",this.onWaiting),this.media.addEventListener("playing",this.onPlaying)}onMediaDetached(){this.media&&(this.media.removeEventListener("waiting",this.onWaiting),this.media.removeEventListener("playing",this.onPlaying),this.media=null)}onBufferCreated(e,t){var s,i;this.audioBuffer=(s=t.tracks.audio)==null?void 0:s.buffer,this.videoBuffer=(i=t.tracks.video)==null?void 0:i.buffer}createData(){var e;return{v:1,sf:zc.HLS,sid:this.sid,cid:this.cid,pr:(e=this.media)==null?void 0:e.playbackRate,mtp:this.hls.bandwidthEstimate/1e3}}apply(e,t={}){ne(t,this.createData());const s=t.ot===Ae.INIT||t.ot===Ae.VIDEO||t.ot===Ae.MUXED;this.starved&&s&&(t.bs=!0,t.su=!0,this.starved=!1),t.su==null&&(t.su=this.buffering);const{includeKeys:i}=this;i&&(t=Object.keys(t).reduce((n,o)=>(i.includes(o)&&(n[o]=t[o]),n),{}));const r={baseUrl:e.url};this.useHeaders?(e.headers||(e.headers={}),Nh(e.headers,t,r)):e.url=Kh(e.url,t,r)}getNextFrag(e){var t;const s=(t=this.hls.levels[e.level])==null?void 0:t.details;if(s){const i=e.sn-s.startSN;return s.fragments[i+1]}}getNextPart(e){var t;const{index:s,fragment:i}=e,r=(t=this.hls.levels[i.level])==null||(t=t.details)==null?void 0:t.partList;if(r){const{sn:n}=i;for(let o=r.length-1;o>=0;o--){const c=r[o];if(c.index===s&&c.fragment.sn===n)return r[o+1]}}}getObjectType(e){const{type:t}=e;if(t==="subtitle")return Ae.TIMED_TEXT;if(e.sn==="initSegment")return Ae.INIT;if(t==="audio")return Ae.AUDIO;if(t==="main")return this.hls.audioTracks.length?Ae.VIDEO:Ae.MUXED}getTopBandwidth(e){let t=0,s;const i=this.hls;if(e===Ae.AUDIO)s=i.audioTracks;else{const r=i.maxAutoLevel,n=r>-1?r+1:i.levels.length;s=i.levels.slice(0,n)}return s.forEach(r=>{r.bitrate>t&&(t=r.bitrate)}),t>0?t:NaN}getBufferLength(e){const t=this.media,s=e===Ae.AUDIO?this.audioBuffer:this.videoBuffer;return!s||!t?NaN:X.bufferInfo(s,t.currentTime,this.config.maxBufferHole).len*1e3}createPlaylistLoader(){const{pLoader:e}=this.config,t=this.applyPlaylistData,s=e||this.config.loader;return class{constructor(r){this.loader=void 0,this.loader=new s(r)}get stats(){return this.loader.stats}get context(){return this.loader.context}destroy(){this.loader.destroy()}abort(){this.loader.abort()}load(r,n,o){t(r),this.loader.load(r,n,o)}}}createFragmentLoader(){const{fLoader:e}=this.config,t=this.applyFragmentData,s=e||this.config.loader;return class{constructor(r){this.loader=void 0,this.loader=new s(r)}get stats(){return this.loader.stats}get context(){return this.loader.context}destroy(){this.loader.destroy()}abort(){this.loader.abort()}load(r,n,o){t(r),this.loader.load(r,n,o)}}}}const Hh=3e5;class Yh extends Oe{constructor(e){super("content-steering",e.logger),this.hls=void 0,this.loader=null,this.uri=null,this.pathwayId=".",this._pathwayPriority=null,this.timeToLoad=300,this.reloadTimer=-1,this.updated=0,this.started=!1,this.enabled=!0,this.levels=null,this.audioTracks=null,this.subtitleTracks=null,this.penalizedPathways={},this.hls=e,this.registerListeners()}registerListeners(){const e=this.hls;e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(m.MANIFEST_PARSED,this.onManifestParsed,this),e.on(m.ERROR,this.onError,this)}unregisterListeners(){const e=this.hls;e&&(e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(m.MANIFEST_PARSED,this.onManifestParsed,this),e.off(m.ERROR,this.onError,this))}pathways(){return(this.levels||[]).reduce((e,t)=>(e.indexOf(t.pathwayId)===-1&&e.push(t.pathwayId),e),[])}get pathwayPriority(){return this._pathwayPriority}set pathwayPriority(e){this.updatePathwayPriority(e)}startLoad(){if(this.started=!0,this.clearTimeout(),this.enabled&&this.uri){if(this.updated){const e=this.timeToLoad*1e3-(performance.now()-this.updated);if(e>0){this.scheduleRefresh(this.uri,e);return}}this.loadSteeringManifest(this.uri)}}stopLoad(){this.started=!1,this.loader&&(this.loader.destroy(),this.loader=null),this.clearTimeout()}clearTimeout(){this.reloadTimer!==-1&&(self.clearTimeout(this.reloadTimer),this.reloadTimer=-1)}destroy(){this.unregisterListeners(),this.stopLoad(),this.hls=null,this.levels=this.audioTracks=this.subtitleTracks=null}removeLevel(e){const t=this.levels;t&&(this.levels=t.filter(s=>s!==e))}onManifestLoading(){this.stopLoad(),this.enabled=!0,this.timeToLoad=300,this.updated=0,this.uri=null,this.pathwayId=".",this.levels=this.audioTracks=this.subtitleTracks=null}onManifestLoaded(e,t){const{contentSteering:s}=t;s!==null&&(this.pathwayId=s.pathwayId,this.uri=s.uri,this.started&&this.startLoad())}onManifestParsed(e,t){this.audioTracks=t.audioTracks,this.subtitleTracks=t.subtitleTracks}onError(e,t){const{errorAction:s}=t;if((s==null?void 0:s.action)===pe.SendAlternateToPenaltyBox&&s.flags===Re.MoveAllAlternatesMatchingHost){const i=this.levels;let r=this._pathwayPriority,n=this.pathwayId;if(t.context){const{groupId:o,pathwayId:c,type:l}=t.context;o&&i?n=this.getPathwayForGroupId(o,l,n):c&&(n=c)}n in this.penalizedPathways||(this.penalizedPathways[n]=performance.now()),!r&&i&&(r=this.pathways()),r&&r.length>1&&(this.updatePathwayPriority(r),s.resolved=this.pathwayId!==n),t.details===R.BUFFER_APPEND_ERROR&&!t.fatal?s.resolved=!0:s.resolved||this.warn(`Could not resolve ${t.details} ("${t.error.message}") with content-steering for Pathway: ${n} levels: ${i&&i.length} priorities: ${oe(r)} penalized: ${oe(this.penalizedPathways)}`)}}filterParsedLevels(e){this.levels=e;let t=this.getLevelsForPathway(this.pathwayId);if(t.length===0){const s=e[0].pathwayId;this.log(`No levels found in Pathway ${this.pathwayId}. Setting initial Pathway to "${s}"`),t=this.getLevelsForPathway(s),this.pathwayId=s}return t.length!==e.length&&this.log(`Found ${t.length}/${e.length} levels in Pathway "${this.pathwayId}"`),t}getLevelsForPathway(e){return this.levels===null?[]:this.levels.filter(t=>e===t.pathwayId)}updatePathwayPriority(e){this._pathwayPriority=e;let t;const s=this.penalizedPathways,i=performance.now();Object.keys(s).forEach(r=>{i-s[r]>Hh&&delete s[r]});for(let r=0;r0){this.log(`Setting Pathway to "${n}"`),this.pathwayId=n,sa(t),this.hls.trigger(m.LEVELS_UPDATED,{levels:t});const l=this.hls.levels[o];c&&l&&this.levels&&(l.attrs["STABLE-VARIANT-ID"]!==c.attrs["STABLE-VARIANT-ID"]&&l.bitrate!==c.bitrate&&this.log(`Unstable Pathways change from bitrate ${c.bitrate} to ${l.bitrate}`),this.hls.nextLoadLevel=o);break}}}getPathwayForGroupId(e,t,s){const i=this.getLevelsForPathway(s).concat(this.levels||[]);for(let r=0;r{const{ID:n,"BASE-ID":o,"URI-REPLACEMENT":c}=r;if(t.some(h=>h.pathwayId===n))return;const l=this.getLevelsForPathway(o).map(h=>{const d=new le(h.attrs);d["PATHWAY-ID"]=n;const u=d.AUDIO&&`${d.AUDIO}_clone_${n}`,f=d.SUBTITLES&&`${d.SUBTITLES}_clone_${n}`;u&&(s[d.AUDIO]=u,d.AUDIO=u),f&&(i[d.SUBTITLES]=f,d.SUBTITLES=f);const g=Na(h.uri,d["STABLE-VARIANT-ID"],"PER-VARIANT-URIS",c),y=new Kt({attrs:d,audioCodec:h.audioCodec,bitrate:h.bitrate,height:h.height,name:h.name,url:g,videoCodec:h.videoCodec,width:h.width});if(h.audioGroups)for(let p=1;p{this.log(`Loaded steering manifest: "${i}"`);const g=h.data;if((g==null?void 0:g.VERSION)!==1){this.log(`Steering VERSION ${g.VERSION} not supported!`);return}this.updated=performance.now(),this.timeToLoad=g.TTL;const{"RELOAD-URI":y,"PATHWAY-CLONES":p,"PATHWAY-PRIORITY":E}=g;if(y)try{this.uri=new self.URL(y,i).href}catch{this.enabled=!1,this.log(`Failed to parse Steering Manifest RELOAD-URI: ${y}`);return}this.scheduleRefresh(this.uri||u.url),p&&this.clonePathways(p);const T={steeringManifest:g,url:i.toString()};this.hls.trigger(m.STEERING_MANIFEST_LOADED,T),E&&this.updatePathwayPriority(E)},onError:(h,d,u,f)=>{if(this.log(`Error loading steering manifest: ${h.code} ${h.text} (${d.url})`),this.stopLoad(),h.code===410){this.enabled=!1,this.log(`Steering manifest ${d.url} no longer available`);return}let g=this.timeToLoad*1e3;if(h.code===429){const y=this.loader;if(typeof(y==null?void 0:y.getResponseHeader)=="function"){const p=y.getResponseHeader("Retry-After");p&&(g=parseFloat(p)*1e3)}this.log(`Steering manifest ${d.url} rate limited`);return}this.scheduleRefresh(this.uri||d.url,g)},onTimeout:(h,d,u)=>{this.log(`Timeout loading steering manifest (${d.url})`),this.scheduleRefresh(this.uri||d.url)}};this.log(`Requesting steering manifest: ${i}`),this.loader.load(r,c,l)}scheduleRefresh(e,t=this.timeToLoad*1e3){this.clearTimeout(),this.reloadTimer=self.setTimeout(()=>{var s;const i=(s=this.hls)==null?void 0:s.media;if(i&&!i.ended){this.loadSteeringManifest(e);return}this.scheduleRefresh(e,this.timeToLoad*1e3)},t)}}function tn(a,e,t,s){a&&Object.keys(e).forEach(i=>{const r=a.filter(n=>n.groupId===i).map(n=>{const o=ne({},n);return o.details=void 0,o.attrs=new le(o.attrs),o.url=o.attrs.URI=Na(n.url,n.attrs["STABLE-RENDITION-ID"],"PER-RENDITION-URIS",t),o.groupId=o.attrs["GROUP-ID"]=e[i],o.attrs["PATHWAY-ID"]=s,o});a.push(...r)})}function Na(a,e,t,s){const{HOST:i,PARAMS:r,[t]:n}=s;let o;e&&(o=n==null?void 0:n[e],o&&(a=o));const c=new self.URL(a);return i&&!o&&(c.host=i),r&&Object.keys(r).sort().forEach(l=>{l&&c.searchParams.set(l,r[l])}),c.href}class Dt extends Oe{constructor(e){super("eme",e.logger),this.hls=void 0,this.config=void 0,this.media=null,this.mediaResolved=void 0,this.keyFormatPromise=null,this.keySystemAccessPromises={},this._requestLicenseFailureCount=0,this.mediaKeySessions=[],this.keyIdToKeySessionPromise={},this.mediaKeys=null,this.setMediaKeysQueue=Dt.CDMCleanupPromise?[Dt.CDMCleanupPromise]:[],this.bannedKeyIds={},this.onMediaEncrypted=t=>{const{initDataType:s,initData:i}=t,r=`"${t.type}" event: init data type: "${s}"`;if(this.debug(r),i!==null){if(!this.keyFormatPromise){let n=Object.keys(this.keySystemAccessPromises);n.length||(n=Mt(this.config));const o=n.map(Bs).filter(c=>!!c);this.keyFormatPromise=this.getKeyFormatPromise(o)}this.keyFormatPromise.then(n=>{const o=us(n);if(s!=="sinf"||o!==ce.FAIRPLAY){this.log(`Ignoring "${t.type}" event with init data type: "${s}" for selected key-system ${o}`);return}let c;try{const f=ue(new Uint8Array(i)),g=Oi(JSON.parse(f).sinf),y=Pn(g);if(!y)throw new Error("'schm' box missing or not cbcs/cenc with schi > tenc");c=new Uint8Array(y.subarray(8,24))}catch(f){this.warn(`${r} Failed to parse sinf: ${f}`);return}const l=ye(c),{keyIdToKeySessionPromise:h,mediaKeySessions:d}=this;let u=h[l];for(let f=0;fthis.generateRequestWithPreferredKeySession(g,s,i,"encrypted-event-key-match")),u.catch(E=>this.handleError(E));break}}u||this.handleError(new Error(`Key ID ${l} not encountered in playlist. Key-system sessions ${d.length}.`))}).catch(n=>this.handleError(n))}},this.onWaitingForKey=t=>{this.log(`"${t.type}" event`)},this.hls=e,this.config=e.config,this.registerListeners()}destroy(){this.onDestroying(),this.onMediaDetached();const e=this.config;e.requestMediaKeySystemAccessFunc=null,e.licenseXhrSetup=e.licenseResponseCallback=void 0,e.drmSystems=e.drmSystemOptions={},this.hls=this.config=this.keyIdToKeySessionPromise=null,this.onMediaEncrypted=this.onWaitingForKey=null}registerListeners(){this.hls.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.on(m.MEDIA_DETACHED,this.onMediaDetached,this),this.hls.on(m.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.on(m.MANIFEST_LOADED,this.onManifestLoaded,this),this.hls.on(m.DESTROYING,this.onDestroying,this)}unregisterListeners(){this.hls.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),this.hls.off(m.MEDIA_DETACHED,this.onMediaDetached,this),this.hls.off(m.MANIFEST_LOADING,this.onManifestLoading,this),this.hls.off(m.MANIFEST_LOADED,this.onManifestLoaded,this),this.hls.off(m.DESTROYING,this.onDestroying,this)}getLicenseServerUrl(e){const{drmSystems:t,widevineLicenseUrl:s}=this.config,i=t==null?void 0:t[e];if(i)return i.licenseUrl;if(e===ce.WIDEVINE&&s)return s}getLicenseServerUrlOrThrow(e){const t=this.getLicenseServerUrl(e);if(t===void 0)throw new Error(`no license server URL configured for key-system "${e}"`);return t}getServerCertificateUrl(e){const{drmSystems:t}=this.config,s=t==null?void 0:t[e];if(s)return s.serverCertificateUrl;this.log(`No Server Certificate in config.drmSystems["${e}"]`)}attemptKeySystemAccess(e){const t=this.hls.levels,s=(n,o,c)=>!!n&&c.indexOf(n)===o,i=t.map(n=>n.audioCodec).filter(s),r=t.map(n=>n.videoCodec).filter(s);return i.length+r.length===0&&r.push("avc1.42e01e"),new Promise((n,o)=>{const c=l=>{const h=l.shift();this.getMediaKeysPromise(h,i,r).then(d=>n({keySystem:h,mediaKeys:d})).catch(d=>{l.length?c(l):d instanceof Le?o(d):o(new Le({type:Y.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_NO_ACCESS,error:d,fatal:!0},d.message))})};c(e)})}requestMediaKeySystemAccess(e,t){const{requestMediaKeySystemAccessFunc:s}=this.config;if(typeof s!="function"){let i=`Configured requestMediaKeySystemAccess is not a function ${s}`;return jn===null&&self.location.protocol==="http:"&&(i=`navigator.requestMediaKeySystemAccess is not available over insecure protocol ${location.protocol}`),Promise.reject(new Error(i))}return s(e,t)}getMediaKeysPromise(e,t,s){var i;const r=Ll(e,t,s,this.config.drmSystemOptions||{});let n=this.keySystemAccessPromises[e],o=(i=n)==null?void 0:i.keySystemAccess;if(!o){this.log(`Requesting encrypted media "${e}" key-system access with config: ${oe(r)}`),o=this.requestMediaKeySystemAccess(e,r);const c=n=this.keySystemAccessPromises[e]={keySystemAccess:o};return o.catch(l=>{this.log(`Failed to obtain access to key-system "${e}": ${l}`)}),o.then(l=>{this.log(`Access for key-system "${l.keySystem}" obtained`);const h=this.fetchServerCertificate(e);this.log(`Create media-keys for "${e}"`);const d=c.mediaKeys=l.createMediaKeys().then(u=>(this.log(`Media-keys created for "${e}"`),c.hasMediaKeys=!0,h.then(f=>f?this.setMediaKeysServerCertificate(u,e,f):u)));return d.catch(u=>{this.error(`Failed to create media-keys for "${e}"}: ${u}`)}),d})}return o.then(()=>n.mediaKeys)}createMediaKeySessionContext({decryptdata:e,keySystem:t,mediaKeys:s}){this.log(`Creating key-system session "${t}" keyId: ${ye(e.keyId||[])} keyUri: ${e.uri}`);const i=s.createSession(),r={decryptdata:e,keySystem:t,mediaKeys:s,mediaKeysSession:i,keyStatus:"status-pending"};return this.mediaKeySessions.push(r),r}renewKeySession(e){const t=e.decryptdata;if(t.pssh){const s=this.createMediaKeySessionContext(e),i=rs(t),r="cenc";this.keyIdToKeySessionPromise[i]=this.generateRequestWithPreferredKeySession(s,r,t.pssh.buffer,"expired")}else this.warn("Could not renew expired session. Missing pssh initData.");this.removeSession(e)}updateKeySession(e,t){const s=e.mediaKeysSession;return this.log(`Updating key-session "${s.sessionId}" for keyId ${ye(e.decryptdata.keyId||[])} + } (data length: ${t.byteLength})`),s.update(t)}getSelectedKeySystemFormats(){return Object.keys(this.keySystemAccessPromises).map(e=>({keySystem:e,hasMediaKeys:this.keySystemAccessPromises[e].hasMediaKeys})).filter(({hasMediaKeys:e})=>!!e).map(({keySystem:e})=>Bs(e)).filter(e=>!!e)}getKeySystemAccess(e){return this.getKeySystemSelectionPromise(e).then(({keySystem:t,mediaKeys:s})=>this.attemptSetMediaKeys(t,s))}selectKeySystem(e){return new Promise((t,s)=>{this.getKeySystemSelectionPromise(e).then(({keySystem:i})=>{const r=Bs(i);r?t(r):s(new Error(`Unable to find format for key-system "${i}"`))}).catch(s)})}selectKeySystemFormat(e){const t=Object.keys(e.levelkeys||{});return this.keyFormatPromise||(this.log(`Selecting key-system from fragment (sn: ${e.sn} ${e.type}: ${e.level}) key formats ${t.join(", ")}`),this.keyFormatPromise=this.getKeyFormatPromise(t)),this.keyFormatPromise}getKeyFormatPromise(e){const t=Mt(this.config),s=e.map(us).filter(i=>!!i&&t.indexOf(i)!==-1);return this.selectKeySystem(s)}getKeyStatus(e){const{mediaKeySessions:t}=this;for(let s=0;s(this.throwIfDestroyed(),this.log(`Handle encrypted media sn: ${e.frag.sn} ${e.frag.type}: ${e.frag.level} using key ${r}`),this.attemptSetMediaKeys(c,l).then(()=>(this.throwIfDestroyed(),this.createMediaKeySessionContext({keySystem:c,mediaKeys:l,decryptdata:t}))))).then(c=>{const l="cenc",h=t.pssh?t.pssh.buffer:null;return this.generateRequestWithPreferredKeySession(c,l,h,"playlist-key")});return o.catch(c=>this.handleError(c,e.frag)),this.keyIdToKeySessionPromise[s]=o,o}return n.catch(o=>{if(o instanceof Le){const c=ie({},o.data);this.getKeyStatus(t)==="internal-error"&&(c.decryptdata=t);const l=new Le(c,o.message);this.handleError(l,e.frag)}}),n}throwIfDestroyed(e="Invalid state"){if(!this.hls)throw new Error("invalid state")}handleError(e,t){if(this.hls)if(e instanceof Le){t&&(e.data.frag=t);const s=e.data.decryptdata;this.error(`${e.message}${s?` (${ye(s.keyId||[])})`:""}`),this.hls.trigger(m.ERROR,e.data)}else this.error(e.message),this.hls.trigger(m.ERROR,{type:Y.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_NO_KEYS,error:e,fatal:!0})}getKeySystemForKeyPromise(e){const t=rs(e),s=this.keyIdToKeySessionPromise[t];if(!s){const i=us(e.keyFormat),r=i?[i]:Mt(this.config);return this.attemptKeySystemAccess(r)}return s}getKeySystemSelectionPromise(e){if(e.length||(e=Mt(this.config)),e.length===0)throw new Le({type:Y.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_NO_CONFIGURED_LICENSE,fatal:!0},`Missing key-system license configuration options ${oe({drmSystems:this.config.drmSystems})}`);return this.attemptKeySystemAccess(e)}attemptSetMediaKeys(e,t){if(this.mediaResolved=void 0,this.mediaKeys===t)return Promise.resolve();const s=this.setMediaKeysQueue.slice();this.log(`Setting media-keys for "${e}"`);const i=Promise.all(s).then(()=>this.media?this.media.setMediaKeys(t):new Promise((r,n)=>{this.mediaResolved=()=>{if(this.mediaResolved=void 0,!this.media)return n(new Error("Attempted to set mediaKeys without media element attached"));this.mediaKeys=t,this.media.setMediaKeys(t).then(r).catch(n)}}));return this.mediaKeys=t,this.setMediaKeysQueue.push(i),i.then(()=>{this.log(`Media-keys set for "${e}"`),s.push(i),this.setMediaKeysQueue=this.setMediaKeysQueue.filter(r=>s.indexOf(r)===-1)})}generateRequestWithPreferredKeySession(e,t,s,i){var r;const n=(r=this.config.drmSystems)==null||(r=r[e.keySystem])==null?void 0:r.generateRequest;if(n)try{const g=n.call(this.hls,t,s,e);if(!g)throw new Error("Invalid response from configured generateRequest filter");t=g.initDataType,s=g.initData?g.initData:null,e.decryptdata.pssh=s?new Uint8Array(s):null}catch(g){if(this.warn(g.message),this.hls&&this.hls.config.debug)throw g}if(s===null)return this.log(`Skipping key-session request for "${i}" (no initData)`),Promise.resolve(e);const o=rs(e.decryptdata),c=e.decryptdata.uri;this.log(`Generating key-session request for "${i}" keyId: ${o} URI: ${c} (init data type: ${t} length: ${s.byteLength})`);const l=new Mi,h=e._onmessage=g=>{const y=e.mediaKeysSession;if(!y){l.emit("error",new Error("invalid state"));return}const{messageType:p,message:E}=g;this.log(`"${p}" message event for session "${y.sessionId}" message size: ${E.byteLength}`),p==="license-request"||p==="license-renewal"?this.renewLicense(e,E).catch(T=>{l.eventNames().length?l.emit("error",T):this.handleError(T)}):p==="license-release"?e.keySystem===ce.FAIRPLAY&&this.updateKeySession(e,hi("acknowledged")).then(()=>this.removeSession(e)).catch(T=>this.handleError(T)):this.warn(`unhandled media key message type "${p}"`)},d=(g,y)=>{y.keyStatus=g;let p;g.startsWith("usable")?l.emit("resolved"):g==="internal-error"||g==="output-restricted"||g==="output-downscaled"?p=sn(g,y.decryptdata):g==="expired"?p=new Error(`key expired (keyId: ${o})`):g==="released"?p=new Error("key released"):g==="status-pending"||this.warn(`unhandled key status change "${g}" (keyId: ${o})`),p&&(l.eventNames().length?l.emit("error",p):this.handleError(p))},u=e._onkeystatuseschange=g=>{if(!e.mediaKeysSession){l.emit("error",new Error("invalid state"));return}const p=this.getKeyStatuses(e);if(!Object.keys(p).some(v=>p[v]!=="status-pending"))return;if(p[o]==="expired"){this.log(`Expired key ${oe(p)} in key-session "${e.mediaKeysSession.sessionId}"`),this.renewKeySession(e);return}let T=p[o];if(T)d(T,e);else{var S;e.keyStatusTimeouts||(e.keyStatusTimeouts={}),(S=e.keyStatusTimeouts)[o]||(S[o]=self.setTimeout(()=>{if(!e.mediaKeysSession||!this.mediaKeys)return;const x=this.getKeyStatus(e.decryptdata);if(x&&x!=="status-pending")return this.log(`No status for keyId ${o} in key-session "${e.mediaKeysSession.sessionId}". Using session key-status ${x} from other session.`),d(x,e);this.log(`key status for ${o} in key-session "${e.mediaKeysSession.sessionId}" timed out after 1000ms`),T="internal-error",d(T,e)},1e3)),this.log(`No status for keyId ${o} (${oe(p)}).`)}};xe(e.mediaKeysSession,"message",h),xe(e.mediaKeysSession,"keystatuseschange",u);const f=new Promise((g,y)=>{l.on("error",y),l.on("resolved",g)});return e.mediaKeysSession.generateRequest(t,s).then(()=>{this.log(`Request generated for key-session "${e.mediaKeysSession.sessionId}" keyId: ${o} URI: ${c}`)}).catch(g=>{throw new Le({type:Y.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_NO_SESSION,error:g,decryptdata:e.decryptdata,fatal:!1},`Error generating key-session request: ${g}`)}).then(()=>f).catch(g=>(l.removeAllListeners(),this.removeSession(e).then(()=>{throw g}))).then(()=>(l.removeAllListeners(),e))}getKeyStatuses(e){const t={};return e.mediaKeysSession.keyStatuses.forEach((s,i)=>{if(typeof i=="string"&&typeof s=="object"){const o=i;i=s,s=o}const r="buffer"in i?new Uint8Array(i.buffer,i.byteOffset,i.byteLength):new Uint8Array(i);if(e.keySystem===ce.PLAYREADY&&r.length===16){const o=ye(r);t[o]=s,Wn(r)}const n=ye(r);s==="internal-error"&&(this.bannedKeyIds[n]=s),this.log(`key status change "${s}" for keyStatuses keyId: ${n} key-session "${e.mediaKeysSession.sessionId}"`),t[n]=s}),t}fetchServerCertificate(e){const t=this.config,s=t.loader,i=new s(t),r=this.getServerCertificateUrl(e);return r?(this.log(`Fetching server certificate for "${e}"`),new Promise((n,o)=>{const c={responseType:"arraybuffer",url:r},l=t.certLoadPolicy.default,h={loadPolicy:l,timeout:l.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0},d={onSuccess:(u,f,g,y)=>{n(u.data)},onError:(u,f,g,y)=>{o(new Le({type:Y.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED,fatal:!0,networkDetails:g,response:ie({url:c.url,data:void 0},u)},`"${e}" certificate request failed (${r}). Status: ${u.code} (${u.text})`))},onTimeout:(u,f,g)=>{o(new Le({type:Y.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_SERVER_CERTIFICATE_REQUEST_FAILED,fatal:!0,networkDetails:g,response:{url:c.url,data:void 0}},`"${e}" certificate request timed out (${r})`))},onAbort:(u,f,g)=>{o(new Error("aborted"))}};i.load(c,h,d)})):Promise.resolve()}setMediaKeysServerCertificate(e,t,s){return new Promise((i,r)=>{e.setServerCertificate(s).then(n=>{this.log(`setServerCertificate ${n?"success":"not supported by CDM"} (${s.byteLength}) on "${t}"`),i(e)}).catch(n=>{r(new Le({type:Y.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_SERVER_CERTIFICATE_UPDATE_FAILED,error:n,fatal:!0},n.message))})})}renewLicense(e,t){return this.requestLicense(e,new Uint8Array(t)).then(s=>this.updateKeySession(e,new Uint8Array(s)).catch(i=>{throw new Le({type:Y.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_SESSION_UPDATE_FAILED,decryptdata:e.decryptdata,error:i,fatal:!1},i.message)}))}unpackPlayReadyKeyMessage(e,t){const s=String.fromCharCode.apply(null,new Uint16Array(t.buffer));if(!s.includes("PlayReadyKeyMessage"))return e.setRequestHeader("Content-Type","text/xml; charset=utf-8"),t;const i=new DOMParser().parseFromString(s,"application/xml"),r=i.querySelectorAll("HttpHeader");if(r.length>0){let h;for(let d=0,u=r.length;d in key message");return hi(atob(l))}setupLicenseXHR(e,t,s,i){const r=this.config.licenseXhrSetup;return r?Promise.resolve().then(()=>{if(!s.decryptdata)throw new Error("Key removed");return r.call(this.hls,e,t,s,i)}).catch(n=>{if(!s.decryptdata)throw n;return e.open("POST",t,!0),r.call(this.hls,e,t,s,i)}).then(n=>(e.readyState||e.open("POST",t,!0),{xhr:e,licenseChallenge:n||i})):(e.open("POST",t,!0),Promise.resolve({xhr:e,licenseChallenge:i}))}requestLicense(e,t){const s=this.config.keyLoadPolicy.default;return new Promise((i,r)=>{const n=this.getLicenseServerUrlOrThrow(e.keySystem);this.log(`Sending license request to URL: ${n}`);const o=new XMLHttpRequest;o.responseType="arraybuffer",o.onreadystatechange=()=>{if(!this.hls||!e.mediaKeysSession)return r(new Error("invalid state"));if(o.readyState===4)if(o.status===200){this._requestLicenseFailureCount=0;let c=o.response;this.log(`License received ${c instanceof ArrayBuffer?c.byteLength:c}`);const l=this.config.licenseResponseCallback;if(l)try{c=l.call(this.hls,o,n,e)}catch(h){this.error(h)}i(c)}else{const c=s.errorRetry,l=c?c.maxNumRetry:0;if(this._requestLicenseFailureCount++,this._requestLicenseFailureCount>l||o.status>=400&&o.status<500)r(new Le({type:Y.KEY_SYSTEM_ERROR,details:R.KEY_SYSTEM_LICENSE_REQUEST_FAILED,decryptdata:e.decryptdata,fatal:!0,networkDetails:o,response:{url:n,data:void 0,code:o.status,text:o.statusText}},`License Request XHR failed (${n}). Status: ${o.status} (${o.statusText})`));else{const h=l-this._requestLicenseFailureCount+1;this.warn(`Retrying license request, ${h} attempts left`),this.requestLicense(e,t).then(i,r)}}},e.licenseXhr&&e.licenseXhr.readyState!==XMLHttpRequest.DONE&&e.licenseXhr.abort(),e.licenseXhr=o,this.setupLicenseXHR(o,n,e,t).then(({xhr:c,licenseChallenge:l})=>{e.keySystem==ce.PLAYREADY&&(l=this.unpackPlayReadyKeyMessage(c,l)),c.send(l)}).catch(r)})}onDestroying(){this.unregisterListeners(),this._clear()}onMediaAttached(e,t){if(!this.config.emeEnabled)return;const s=t.media;this.media=s,xe(s,"encrypted",this.onMediaEncrypted),xe(s,"waitingforkey",this.onWaitingForKey);const i=this.mediaResolved;i?i():this.mediaKeys=s.mediaKeys}onMediaDetached(){const e=this.media;e&&(Ie(e,"encrypted",this.onMediaEncrypted),Ie(e,"waitingforkey",this.onWaitingForKey),this.media=null,this.mediaKeys=null)}_clear(){var e;this._requestLicenseFailureCount=0,this.keyIdToKeySessionPromise={},this.bannedKeyIds={};const t=this.mediaResolved;if(t&&t(),!this.mediaKeys&&!this.mediaKeySessions.length)return;const s=this.media,i=this.mediaKeySessions.slice();this.mediaKeySessions=[],this.mediaKeys=null,nt.clearKeyUriToKeyIdMap();const r=i.length;Dt.CDMCleanupPromise=Promise.all(i.map(n=>this.removeSession(n)).concat((s==null||(e=s.setMediaKeys(null))==null?void 0:e.catch(n=>{this.log(`Could not clear media keys: ${n}`),this.hls&&this.hls.trigger(m.ERROR,{type:Y.OTHER_ERROR,details:R.KEY_SYSTEM_DESTROY_MEDIA_KEYS_ERROR,fatal:!1,error:new Error(`Could not clear media keys: ${n}`)})}))||Promise.resolve())).catch(n=>{this.log(`Could not close sessions and clear media keys: ${n}`),this.hls&&this.hls.trigger(m.ERROR,{type:Y.OTHER_ERROR,details:R.KEY_SYSTEM_DESTROY_CLOSE_SESSION_ERROR,fatal:!1,error:new Error(`Could not close sessions and clear media keys: ${n}`)})}).then(()=>{r&&this.log("finished closing key sessions and clearing media keys")})}onManifestLoading(){this._clear()}onManifestLoaded(e,{sessionKeys:t}){if(!(!t||!this.config.emeEnabled)&&!this.keyFormatPromise){const s=t.reduce((i,r)=>(i.indexOf(r.keyFormat)===-1&&i.push(r.keyFormat),i),[]);this.log(`Selecting key-system from session-keys ${s.join(", ")}`),this.keyFormatPromise=this.getKeyFormatPromise(s)}}removeSession(e){const{mediaKeysSession:t,licenseXhr:s,decryptdata:i}=e;if(t){this.log(`Remove licenses and keys and close session "${t.sessionId}" keyId: ${ye((i==null?void 0:i.keyId)||[])}`),e._onmessage&&(t.removeEventListener("message",e._onmessage),e._onmessage=void 0),e._onkeystatuseschange&&(t.removeEventListener("keystatuseschange",e._onkeystatuseschange),e._onkeystatuseschange=void 0),s&&s.readyState!==XMLHttpRequest.DONE&&s.abort(),e.mediaKeysSession=e.decryptdata=e.licenseXhr=void 0;const r=this.mediaKeySessions.indexOf(e);r>-1&&this.mediaKeySessions.splice(r,1);const{keyStatusTimeouts:n}=e;n&&Object.keys(n).forEach(l=>self.clearTimeout(n[l]));const{drmSystemOptions:o}=this.config;return(bl(o)?new Promise((l,h)=>{self.setTimeout(()=>h(new Error("MediaKeySession.remove() timeout")),8e3),t.remove().then(l).catch(h)}):Promise.resolve()).catch(l=>{this.log(`Could not remove session: ${l}`),this.hls&&this.hls.trigger(m.ERROR,{type:Y.OTHER_ERROR,details:R.KEY_SYSTEM_DESTROY_REMOVE_SESSION_ERROR,fatal:!1,error:new Error(`Could not remove session: ${l}`)})}).then(()=>t.close()).catch(l=>{this.log(`Could not close session: ${l}`),this.hls&&this.hls.trigger(m.ERROR,{type:Y.OTHER_ERROR,details:R.KEY_SYSTEM_DESTROY_CLOSE_SESSION_ERROR,fatal:!1,error:new Error(`Could not close session: ${l}`)})})}return Promise.resolve()}}Dt.CDMCleanupPromise=void 0;function rs(a){if(!a)throw new Error("Could not read keyId of undefined decryptdata");if(a.keyId===null)throw new Error("keyId is null");return ye(a.keyId)}function Wh(a,e){if(a.keyId&&e.mediaKeysSession.keyStatuses.has(a.keyId))return e.mediaKeysSession.keyStatuses.get(a.keyId);if(a.matches(e.decryptdata))return e.keyStatus}class Le extends Error{constructor(e,t){super(t),this.data=void 0,e.error||(e.error=new Error(t)),this.data=e,e.err=e.error}}function sn(a,e){const t=a==="output-restricted",s=t?R.KEY_SYSTEM_STATUS_OUTPUT_RESTRICTED:R.KEY_SYSTEM_STATUS_INTERNAL_ERROR;return new Le({type:Y.KEY_SYSTEM_ERROR,details:s,fatal:!1,decryptdata:e},t?"HDCP level output restricted":`key status changed to "${a}"`)}class qh{constructor(e){this.hls=void 0,this.isVideoPlaybackQualityAvailable=!1,this.timer=void 0,this.media=null,this.lastTime=void 0,this.lastDroppedFrames=0,this.lastDecodedFrames=0,this.streamController=void 0,this.hls=e,this.registerListeners()}setStreamController(e){this.streamController=e}registerListeners(){this.hls.on(m.MEDIA_ATTACHING,this.onMediaAttaching,this),this.hls.on(m.MEDIA_DETACHING,this.onMediaDetaching,this)}unregisterListeners(){this.hls.off(m.MEDIA_ATTACHING,this.onMediaAttaching,this),this.hls.off(m.MEDIA_DETACHING,this.onMediaDetaching,this)}destroy(){this.timer&&clearInterval(this.timer),this.unregisterListeners(),this.isVideoPlaybackQualityAvailable=!1,this.media=null}onMediaAttaching(e,t){const s=this.hls.config;if(s.capLevelOnFPSDrop){const i=t.media instanceof self.HTMLVideoElement?t.media:null;this.media=i,i&&typeof i.getVideoPlaybackQuality=="function"&&(this.isVideoPlaybackQualityAvailable=!0),self.clearInterval(this.timer),this.timer=self.setInterval(this.checkFPSInterval.bind(this),s.fpsDroppedMonitoringPeriod)}}onMediaDetaching(){this.media=null}checkFPS(e,t,s){const i=performance.now();if(t){if(this.lastTime){const r=i-this.lastTime,n=s-this.lastDroppedFrames,o=t-this.lastDecodedFrames,c=1e3*n/r,l=this.hls;if(l.trigger(m.FPS_DROP,{currentDropped:n,currentDecoded:o,totalDroppedFrames:s}),c>0&&n>l.config.fpsDroppedMonitoringThreshold*o){let h=l.currentLevel;l.logger.warn("drop FPS ratio greater than max allowed value for currentLevel: "+h),h>0&&(l.autoLevelCapping===-1||l.autoLevelCapping>=h)&&(h=h-1,l.trigger(m.FPS_DROP_LEVEL_CAPPING,{level:h,droppedLevel:l.currentLevel}),l.autoLevelCapping=h,this.streamController.nextLevelSwitch())}}this.lastTime=i,this.lastDroppedFrames=s,this.lastDecodedFrames=t}}checkFPSInterval(){const e=this.media;if(e)if(this.isVideoPlaybackQualityAvailable){const t=e.getVideoPlaybackQuality();this.checkFPS(e,t.totalVideoFrames,t.droppedVideoFrames)}else this.checkFPS(e,e.webkitDecodedFrameCount,e.webkitDroppedFrameCount)}}function Ba(a,e){let t;try{t=new Event("addtrack")}catch{t=document.createEvent("Event"),t.initEvent("addtrack",!1,!1)}t.track=a,e.dispatchEvent(t)}function $a(a,e){const t=a.mode;if(t==="disabled"&&(a.mode="hidden"),a.cues&&!a.cues.getCueById(e.id))try{if(a.addCue(e),!a.cues.getCueById(e.id))throw new Error(`addCue is failed for: ${e}`)}catch(s){re.debug(`[texttrack-utils]: ${s}`);try{const i=new self.TextTrackCue(e.startTime,e.endTime,e.text);i.id=e.id,a.addCue(i)}catch(i){re.debug(`[texttrack-utils]: Legacy TextTrackCue fallback failed: ${i}`)}}t==="disabled"&&(a.mode=t)}function Lt(a,e){const t=a.mode;if(t==="disabled"&&(a.mode="hidden"),a.cues)for(let s=a.cues.length;s--;)e&&a.cues[s].removeEventListener("enter",e),a.removeCue(a.cues[s]);t==="disabled"&&(a.mode=t)}function Ti(a,e,t,s){const i=a.mode;if(i==="disabled"&&(a.mode="hidden"),a.cues&&a.cues.length>0){const r=Xh(a.cues,e,t);for(let n=0;na[t].endTime)return-1;let s=0,i=t,r;for(;s<=i;)if(r=Math.floor((i+s)/2),ea[r].startTime&&s-1)for(let r=i,n=a.length;r=e&&o.endTime<=t)s.push(o);else if(o.startTime>t)return s}return s}function ps(a){const e=[];for(let t=0;tthis.pollTrackChange(0),this.onTextTracksChanged=()=>{if(this.useTextTrackPolling||self.clearInterval(this.subtitlePollingInterval),!this.media||!this.hls.config.renderTextTracksNatively)return;let t=null;const s=ps(this.media.textTracks);for(let r=0;r-1&&this.toggleTrackModes()}registerListeners(){const{hls:e}=this;e.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.MANIFEST_PARSED,this.onManifestParsed,this),e.on(m.LEVEL_LOADING,this.onLevelLoading,this),e.on(m.LEVEL_SWITCHING,this.onLevelSwitching,this),e.on(m.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),e.on(m.ERROR,this.onError,this)}unregisterListeners(){const{hls:e}=this;e.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.MANIFEST_PARSED,this.onManifestParsed,this),e.off(m.LEVEL_LOADING,this.onLevelLoading,this),e.off(m.LEVEL_SWITCHING,this.onLevelSwitching,this),e.off(m.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),e.off(m.ERROR,this.onError,this)}onMediaAttached(e,t){this.media=t.media,this.media&&(this.queuedDefaultTrack>-1&&(this.subtitleTrack=this.queuedDefaultTrack,this.queuedDefaultTrack=-1),this.useTextTrackPolling=!(this.media.textTracks&&"onchange"in this.media.textTracks),this.useTextTrackPolling?this.pollTrackChange(500):this.media.textTracks.addEventListener("change",this.asyncPollTrackChange))}pollTrackChange(e){self.clearInterval(this.subtitlePollingInterval),this.subtitlePollingInterval=self.setInterval(this.onTextTracksChanged,e)}onMediaDetaching(e,t){const s=this.media;if(!s)return;const i=!!t.transferMedia;if(self.clearInterval(this.subtitlePollingInterval),this.useTextTrackPolling||s.textTracks.removeEventListener("change",this.asyncPollTrackChange),this.trackId>-1&&(this.queuedDefaultTrack=this.trackId),this.subtitleTrack=-1,this.media=null,i)return;ps(s.textTracks).forEach(n=>{Lt(n)})}onManifestLoading(){this.tracks=[],this.groupIds=null,this.tracksInGroup=[],this.trackId=-1,this.currentTrack=null,this.selectDefaultTrack=!0}onManifestParsed(e,t){this.tracks=t.subtitleTracks}onSubtitleTrackLoaded(e,t){const{id:s,groupId:i,details:r}=t,n=this.tracksInGroup[s];if(!n||n.groupId!==i){this.warn(`Subtitle track with id:${s} and group:${i} not found in active group ${n==null?void 0:n.groupId}`);return}const o=n.details;n.details=t.details,this.log(`Subtitle track ${s} "${n.name}" lang:${n.lang} group:${i} loaded [${r.startSN}-${r.endSN}]`),s===this.trackId&&this.playlistLoaded(s,t,o)}onLevelLoading(e,t){this.switchLevel(t.level)}onLevelSwitching(e,t){this.switchLevel(t.level)}switchLevel(e){const t=this.hls.levels[e];if(!t)return;const s=t.subtitleGroups||null,i=this.groupIds;let r=this.currentTrack;if(!s||(i==null?void 0:i.length)!==(s==null?void 0:s.length)||s!=null&&s.some(n=>(i==null?void 0:i.indexOf(n))===-1)){this.groupIds=s,this.trackId=-1,this.currentTrack=null;const n=this.tracks.filter(h=>!s||s.indexOf(h.groupId)!==-1);if(n.length)this.selectDefaultTrack&&!n.some(h=>h.default)&&(this.selectDefaultTrack=!1),n.forEach((h,d)=>{h.id=d});else if(!r&&!this.tracksInGroup.length)return;this.tracksInGroup=n;const o=this.hls.config.subtitlePreference;if(!r&&o){this.selectDefaultTrack=!1;const h=We(o,n);if(h>-1)r=n[h];else{const d=We(o,this.tracks);r=this.tracks[d]}}let c=this.findTrackId(r);c===-1&&r&&(c=this.findTrackId(null));const l={subtitleTracks:n};this.log(`Updating subtitle tracks, ${n.length} track(s) found in "${s==null?void 0:s.join(",")}" group-id`),this.hls.trigger(m.SUBTITLE_TRACKS_UPDATED,l),c!==-1&&this.trackId===-1&&this.setSubtitleTrack(c)}}findTrackId(e){const t=this.tracksInGroup,s=this.selectDefaultTrack;for(let i=0;i-1){const r=this.tracksInGroup[i];return this.setSubtitleTrack(i),r}else{if(s)return null;{const r=We(e,t);if(r>-1)return t[r]}}}}return null}loadPlaylist(e){super.loadPlaylist(),this.shouldLoadPlaylist(this.currentTrack)&&this.scheduleLoading(this.currentTrack,e)}loadingPlaylist(e,t){super.loadingPlaylist(e,t);const s=e.id,i=e.groupId,r=this.getUrlWithDirectives(e.url,t),n=e.details,o=n==null?void 0:n.age;this.log(`Loading subtitle ${s} "${e.name}" lang:${e.lang} group:${i}${(t==null?void 0:t.msn)!==void 0?" at sn "+t.msn+" part "+t.part:""}${o&&n.live?" age "+o.toFixed(1)+(n.type&&" "+n.type||""):""} ${r}`),this.hls.trigger(m.SUBTITLE_TRACK_LOADING,{url:r,id:s,groupId:i,deliveryDirectives:t||null,track:e})}toggleTrackModes(){const{media:e}=this;if(!e)return;const t=ps(e.textTracks),s=this.currentTrack;let i;if(s&&(i=t.filter(r=>mi(s,r))[0],i||this.warn(`Unable to find subtitle TextTrack with name "${s.name}" and language "${s.lang}"`)),[].slice.call(t).forEach(r=>{r.mode!=="disabled"&&r!==i&&(r.mode="disabled")}),i){const r=this.subtitleDisplay?"showing":"hidden";i.mode!==r&&(i.mode=r)}}setSubtitleTrack(e){const t=this.tracksInGroup;if(!this.media){this.queuedDefaultTrack=e;return}if(e<-1||e>=t.length||!B(e)){this.warn(`Invalid subtitle track id: ${e}`);return}this.selectDefaultTrack=!1;const s=this.currentTrack,i=t[e]||null;if(this.trackId=e,this.currentTrack=i,this.toggleTrackModes(),!i){this.hls.trigger(m.SUBTITLE_TRACK_SWITCH,{id:e});return}const r=!!i.details&&!i.details.live;if(e===this.trackId&&i===s&&r)return;this.log(`Switching to subtitle-track ${e}`+(i?` "${i.name}" lang:${i.lang} group:${i.groupId}`:""));const{id:n,groupId:o="",name:c,type:l,url:h}=i;this.hls.trigger(m.SUBTITLE_TRACK_SWITCH,{id:n,groupId:o,name:c,type:l,url:h});const d=this.switchParams(i.url,s==null?void 0:s.details,i.details);this.loadPlaylist(d)}}function Qh(){try{return crypto.randomUUID()}catch{try{const e=URL.createObjectURL(new Blob),t=e.toString();return URL.revokeObjectURL(e),t.slice(t.lastIndexOf("/")+1)}catch{let t=new Date().getTime();return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,i=>{const r=(t+Math.random()*16)%16|0;return t=Math.floor(t/16),(i=="x"?r:r&3|8).toString(16)})}}}function $t(a){let e=5381,t=a.length;for(;t;)e=e*33^a.charCodeAt(--t);return(e>>>0).toString()}const Ct=.025;let _s=function(a){return a[a.Point=0]="Point",a[a.Range=1]="Range",a}({});function Zh(a,e,t){return`${a.identifier}-${t+1}-${$t(e)}`}class Jh{constructor(e,t){this.base=void 0,this._duration=null,this._timelineStart=null,this.appendInPlaceDisabled=void 0,this.appendInPlaceStarted=void 0,this.dateRange=void 0,this.hasPlayed=!1,this.cumulativeDuration=0,this.resumeOffset=NaN,this.playoutLimit=NaN,this.restrictions={skip:!1,jump:!1},this.snapOptions={out:!1,in:!1},this.assetList=[],this.assetListLoader=void 0,this.assetListResponse=null,this.resumeAnchor=void 0,this.error=void 0,this.resetOnResume=void 0,this.base=t,this.dateRange=e,this.setDateRange(e)}setDateRange(e){this.dateRange=e,this.resumeOffset=e.attr.optionalFloat("X-RESUME-OFFSET",this.resumeOffset),this.playoutLimit=e.attr.optionalFloat("X-PLAYOUT-LIMIT",this.playoutLimit),this.restrictions=e.attr.enumeratedStringList("X-RESTRICT",this.restrictions),this.snapOptions=e.attr.enumeratedStringList("X-SNAP",this.snapOptions)}reset(){var e;this.appendInPlaceStarted=!1,(e=this.assetListLoader)==null||e.destroy(),this.assetListLoader=void 0,this.supplementsPrimary||(this.assetListResponse=null,this.assetList=[],this._duration=null)}isAssetPastPlayoutLimit(e){var t;if(e>0&&e>=this.assetList.length)return!0;const s=this.playoutLimit;return e<=0||isNaN(s)?!1:s===0?!0:(((t=this.assetList[e])==null?void 0:t.startOffset)||0)>s}findAssetIndex(e){return this.assetList.indexOf(e)}get identifier(){return this.dateRange.id}get startDate(){return this.dateRange.startDate}get startTime(){const e=this.dateRange.startTime;if(this.snapOptions.out){const t=this.dateRange.tagAnchor;if(t)return qs(e,t)}return e}get startOffset(){return this.cue.pre?0:this.startTime}get startIsAligned(){if(this.startTime===0||this.snapOptions.out)return!0;const e=this.dateRange.tagAnchor;if(e){const t=this.dateRange.startTime,s=qs(t,e);return t-s<.1}return!1}get resumptionOffset(){const e=this.resumeOffset,t=B(e)?e:this.duration;return this.cumulativeDuration+t}get resumeTime(){const e=this.startOffset+this.resumptionOffset;if(this.snapOptions.in){const t=this.resumeAnchor;if(t)return qs(e,t)}return e}get appendInPlace(){return this.appendInPlaceStarted?!0:this.appendInPlaceDisabled?!1:!!(!this.cue.once&&!this.cue.pre&&this.startIsAligned&&(isNaN(this.playoutLimit)&&isNaN(this.resumeOffset)||this.resumeOffset&&this.duration&&Math.abs(this.resumeOffset-this.duration)0||this.assetListResponse!==null}toString(){return ed(this)}}function qs(a,e){return a-e.start":a.cue.post?"":""}${a.timelineStart.toFixed(2)}-${a.resumeTime.toFixed(2)}]`}function It(a){const e=a.timelineStart,t=a.duration||0;return`["${a.identifier}" ${e.toFixed(2)}-${(e+t).toFixed(2)}]`}class td{constructor(e,t,s,i){this.hls=void 0,this.interstitial=void 0,this.assetItem=void 0,this.tracks=null,this.hasDetails=!1,this.mediaAttached=null,this._currentTime=void 0,this._bufferedEosTime=void 0,this.checkPlayout=()=>{this.reachedPlayout(this.currentTime)&&this.hls&&this.hls.trigger(m.PLAYOUT_LIMIT_REACHED,{})};const r=this.hls=new e(t);this.interstitial=s,this.assetItem=i;const n=()=>{this.hasDetails=!0};r.once(m.LEVEL_LOADED,n),r.once(m.AUDIO_TRACK_LOADED,n),r.once(m.SUBTITLE_TRACK_LOADED,n),r.on(m.MEDIA_ATTACHING,(o,{media:c})=>{this.removeMediaListeners(),this.mediaAttached=c,this.interstitial.playoutLimit&&(c.addEventListener("timeupdate",this.checkPlayout),this.appendInPlace&&r.on(m.BUFFER_APPENDED,()=>{const h=this.bufferedEnd;this.reachedPlayout(h)&&(this._bufferedEosTime=h,r.trigger(m.BUFFERED_TO_END,void 0))}))})}get appendInPlace(){return this.interstitial.appendInPlace}loadSource(){const e=this.hls;if(e)if(e.url)e.levels.length&&!e.started&&e.startLoad(-1,!0);else{let t=this.assetItem.uri;try{t=Ua(t,e.config.primarySessionId||"").href}catch{}e.loadSource(t)}}bufferedInPlaceToEnd(e){var t;if(!this.appendInPlace)return!1;if((t=this.hls)!=null&&t.bufferedToEnd)return!0;if(!e)return!1;const s=Math.min(this._bufferedEosTime||1/0,this.duration),i=this.timelineOffset,r=X.bufferInfo(e,i,0);return this.getAssetTime(r.end)>=s-.02}reachedPlayout(e){const s=this.interstitial.playoutLimit;return this.startOffset+e>=s}get destroyed(){var e;return!((e=this.hls)!=null&&e.userConfig)}get assetId(){return this.assetItem.identifier}get interstitialId(){return this.assetItem.parentIdentifier}get media(){var e;return((e=this.hls)==null?void 0:e.media)||null}get bufferedEnd(){const e=this.media||this.mediaAttached;if(!e)return this._bufferedEosTime?this._bufferedEosTime:this.currentTime;const t=X.bufferInfo(e,e.currentTime,.001);return this.getAssetTime(t.end)}get currentTime(){const e=this.media||this.mediaAttached;return e?this.getAssetTime(e.currentTime):this._currentTime||0}get duration(){const e=this.assetItem.duration;if(!e)return 0;const t=this.interstitial.playoutLimit;if(t){const s=t-this.startOffset;if(s>0&&s1/9e4&&this.hls){if(this.hasDetails)throw new Error("Cannot set timelineOffset after playlists are loaded");this.hls.config.timelineOffset=e}}}getAssetTime(e){const t=this.timelineOffset,s=this.duration;return Math.min(Math.max(0,e-t),s)}removeMediaListeners(){const e=this.mediaAttached;e&&(this._currentTime=e.currentTime,this.bufferSnapShot(),e.removeEventListener("timeupdate",this.checkPlayout))}bufferSnapShot(){if(this.mediaAttached){var e;(e=this.hls)!=null&&e.bufferedToEnd&&(this._bufferedEosTime=this.bufferedEnd)}}destroy(){this.removeMediaListeners(),this.hls&&this.hls.destroy(),this.hls=null,this.tracks=this.mediaAttached=this.checkPlayout=null}attachMedia(e){var t;this.loadSource(),(t=this.hls)==null||t.attachMedia(e)}detachMedia(){var e;this.removeMediaListeners(),this.mediaAttached=null,(e=this.hls)==null||e.detachMedia()}resumeBuffering(){var e;(e=this.hls)==null||e.resumeBuffering()}pauseBuffering(){var e;(e=this.hls)==null||e.pauseBuffering()}transferMedia(){var e;return this.bufferSnapShot(),((e=this.hls)==null?void 0:e.transferMedia())||null}resetDetails(){const e=this.hls;if(e&&this.hasDetails){e.stopLoad();const t=s=>delete s.details;e.levels.forEach(t),e.allAudioTracks.forEach(t),e.allSubtitleTracks.forEach(t),this.hasDetails=!1}}on(e,t,s){var i;(i=this.hls)==null||i.on(e,t)}once(e,t,s){var i;(i=this.hls)==null||i.once(e,t)}off(e,t,s){var i;(i=this.hls)==null||i.off(e,t)}toString(){var e;return`HlsAssetPlayer: ${It(this.assetItem)} ${(e=this.hls)==null?void 0:e.sessionId} ${this.appendInPlace?"append-in-place":""}`}}const rn=.033;class sd extends Oe{constructor(e,t){super("interstitials-sched",t),this.onScheduleUpdate=void 0,this.eventMap={},this.events=null,this.items=null,this.durations={primary:0,playout:0,integrated:0},this.onScheduleUpdate=e}destroy(){this.reset(),this.onScheduleUpdate=null}reset(){this.eventMap={},this.setDurations(0,0,0),this.events&&this.events.forEach(e=>e.reset()),this.events=this.items=null}resetErrorsInRange(e,t){return this.events?this.events.reduce((s,i)=>e<=i.startOffset&&t>i.startOffset?(delete i.error,s+1):s,0):0}get duration(){const e=this.items;return e?e[e.length-1].end:0}get length(){return this.items?this.items.length:0}getEvent(e){return e&&this.eventMap[e]||null}hasEvent(e){return e in this.eventMap}findItemIndex(e,t){if(e.event)return this.findEventIndex(e.event.identifier);let s=-1;e.nextEvent?s=this.findEventIndex(e.nextEvent.identifier)-1:e.previousEvent&&(s=this.findEventIndex(e.previousEvent.identifier)+1);const i=this.items;if(i)for(i[s]||(t===void 0&&(t=e.start),s=this.findItemIndexAtTime(t));s>=0&&(r=i[s])!=null&&r.event;){var r;s--}return s}findItemIndexAtTime(e,t){const s=this.items;if(s)for(let i=0;ir.start&&e1)for(let r=0;ro&&(t!o.includes(l.identifier)):[];n.length&&n.sort((l,h)=>{const d=l.cue.pre,u=l.cue.post,f=h.cue.pre,g=h.cue.post;if(d&&!f)return-1;if(f&&!d||u&&!g)return 1;if(g&&!u)return-1;if(!d&&!f&&!u&&!g){const y=l.startTime,p=h.startTime;if(y!==p)return y-p}return l.dateRange.tagOrder-h.dateRange.tagOrder}),this.events=n,c.forEach(l=>{this.removeEvent(l)}),this.updateSchedule(e,c)}updateSchedule(e,t=[],s=!1){const i=this.events||[];if(i.length||t.length||this.length<2){const r=this.items,n=this.parseSchedule(i,e);(s||t.length||(r==null?void 0:r.length)!==n.length||n.some((c,l)=>Math.abs(c.playout.start-r[l].playout.start)>.005||Math.abs(c.playout.end-r[l].playout.end)>.005))&&(this.items=n,this.onScheduleUpdate(t,r))}}parseDateRanges(e,t,s){const i=[],r=Object.keys(e);for(let n=0;n!c.error&&!(c.cue.once&&c.hasPlayed)),e.length){this.resolveOffsets(e,t);let c=0,l=0;if(e.forEach((h,d)=>{const u=h.cue.pre,f=h.cue.post,g=e[d-1]||null,y=h.appendInPlace,p=f?r:h.startOffset,E=h.duration,T=h.timelineOccupancy===_s.Range?E:0,S=h.resumptionOffset,v=(g==null?void 0:g.startTime)===p,x=p+h.cumulativeDuration;let D=y?x+E:p+S;if(u||!f&&p<=0){const _=l;l+=T,h.timelineStart=x;const b=n;n+=E,s.push({event:h,start:x,end:D,playout:{start:b,end:n},integrated:{start:_,end:l}})}else if(p<=r){if(!v){const I=p-c;if(I>rn){const P=c,M=l;l+=I;const G=n;n+=I;const W={previousEvent:e[d-1]||null,nextEvent:h,start:P,end:P+I,playout:{start:G,end:n},integrated:{start:M,end:l}};s.push(W)}else I>0&&g&&(g.cumulativeDuration+=I,s[s.length-1].end=p)}f&&(D=x),h.timelineStart=x;const _=l;l+=T;const b=n;n+=E,s.push({event:h,start:x,end:D,playout:{start:b,end:n},integrated:{start:_,end:l}})}else return;const A=h.resumeTime;f||A>r?c=r:c=A}),c{const l=o.cue.pre,h=o.cue.post,d=l?0:h?i:o.startTime;this.updateAssetDurations(o),n===d?o.cumulativeDuration=r:(r=0,n=d),!h&&o.snapOptions.in&&(o.resumeAnchor=pt(null,s.fragments,o.startOffset+o.resumptionOffset,0,0)||void 0),o.appendInPlace&&!o.appendInPlaceStarted&&(this.primaryCanResumeInPlaceAt(o,t)||(o.appendInPlace=!1)),!o.appendInPlace&&c+1Ct?(this.log(`"${e.identifier}" resumption ${s} not aligned with estimated timeline end ${i}`),!1):!Object.keys(t).some(n=>{const o=t[n].details,c=o.edge;if(s>=c)return this.log(`"${e.identifier}" resumption ${s} past ${n} playlist end ${c}`),!1;const l=pt(null,o.fragments,s);if(!l)return this.log(`"${e.identifier}" resumption ${s} does not align with any fragments in ${n} playlist (${o.fragStart}-${o.fragmentEnd})`),!0;const h=n==="audio"?.175:0;return Math.abs(l.start-s){const p=u.data,E=p==null?void 0:p.ASSETS;if(!Array.isArray(E)){const T=this.assignAssetListError(e,R.ASSET_LIST_PARSING_ERROR,new Error("Invalid interstitial asset list"),g.url,f,y);this.hls.trigger(m.ERROR,T);return}e.assetListResponse=p,this.hls.trigger(m.ASSET_LIST_LOADED,{event:e,assetListResponse:p,networkDetails:y})},onError:(u,f,g,y)=>{const p=this.assignAssetListError(e,R.ASSET_LIST_LOAD_ERROR,new Error(`Error loading X-ASSET-LIST: HTTP status ${u.code} ${u.text} (${f.url})`),f.url,y,g);this.hls.trigger(m.ERROR,p)},onTimeout:(u,f,g)=>{const y=this.assignAssetListError(e,R.ASSET_LIST_LOAD_TIMEOUT,new Error(`Timeout loading X-ASSET-LIST (${f.url})`),f.url,u,g);this.hls.trigger(m.ERROR,y)}};return o.load(c,h,d),this.hls.trigger(m.ASSET_LIST_LOADING,{event:e}),o}assignAssetListError(e,t,s,i,r,n){return e.error=s,{type:Y.NETWORK_ERROR,details:t,fatal:!1,interstitial:e,url:i,error:s,networkDetails:n,stats:r}}}function nn(a){var e;a==null||(e=a.play())==null||e.catch(()=>{})}function At(a,e){return`[${a}] Advancing timeline position to ${e}`}class rd extends Oe{constructor(e,t){super("interstitials",e.logger),this.HlsPlayerClass=void 0,this.hls=void 0,this.assetListLoader=void 0,this.mediaSelection=null,this.altSelection=null,this.media=null,this.detachedData=null,this.requiredTracks=null,this.manager=null,this.playerQueue=[],this.bufferedPos=-1,this.timelinePos=-1,this.schedule=void 0,this.playingItem=null,this.bufferingItem=null,this.waitingItem=null,this.endedItem=null,this.playingAsset=null,this.endedAsset=null,this.bufferingAsset=null,this.shouldPlay=!1,this.onPlay=()=>{this.shouldPlay=!0},this.onPause=()=>{this.shouldPlay=!1},this.onSeeking=()=>{const s=this.currentTime;if(s===void 0||this.playbackDisabled||!this.schedule)return;const i=s-this.timelinePos;if(Math.abs(i)<1/7056e5)return;const n=i<=-.01;this.timelinePos===-1&&!this.effectivePlayingItem&&this.checkStart(),this.timelinePos=s,this.bufferedPos=s;const o=this.playingItem;if(!o){this.checkBuffer();return}if(n&&this.schedule.resetErrorsInRange(s,s-i)&&this.updateSchedule(!0),this.checkBuffer(),n&&s=o.end){var c;const f=this.findItemIndex(o);let g=this.schedule.findItemIndexAtTime(s);if(g===-1&&(g=f+(n?-1:1),this.log(`seeked ${n?"back ":""}to position not covered by schedule ${s} (resolving from ${f} to ${g})`)),!this.isInterstitial(o)&&(c=this.media)!=null&&c.paused&&(this.shouldPlay=!1),!n&&g>f){const y=this.schedule.findJumpRestrictedIndex(f+1,g);if(y>f){this.setSchedulePosition(y);return}}this.setSchedulePosition(g);return}const l=this.playingAsset;if(!l){if(this.playingLastItem&&this.isInterstitial(o)){const f=o.event.assetList[0];f&&(this.endedItem=this.playingItem,this.playingItem=null,this.setScheduleToAssetAtTime(s,f))}return}const h=l.timelineStart,d=l.duration||0;if(n&&s=h+d){var u;(u=o.event)!=null&&u.appendInPlace&&(this.clearAssetPlayers(o.event,o),this.flushFrontBuffer(s)),this.setScheduleToAssetAtTime(s,l)}},this.onTimeupdate=()=>{const s=this.currentTime;if(s===void 0||this.playbackDisabled)return;if(this.timelinePos===-1&&!this.effectivePlayingItem&&this.checkStart(),s>this.timelinePos)this.timelinePos=s,s>this.bufferedPos&&this.checkBuffer();else return;const i=this.playingItem;if(!i||this.playingLastItem)return;if(s>=i.end){this.timelinePos=i.end;const o=this.findItemIndex(i);this.setSchedulePosition(o+1)}const r=this.playingAsset;if(!r)return;const n=r.timelineStart+(r.duration||0);s>=n&&this.setScheduleToAssetAtTime(s,r)},this.onScheduleUpdate=(s,i)=>{const r=this.schedule;if(!r)return;const n=this.playingItem,o=r.events||[],c=r.items||[],l=r.durations,h=s.map(y=>y.identifier),d=!!(o.length||h.length);(d||i)&&this.log(`INTERSTITIALS_UPDATED (${o.length}): ${o} +Schedule: ${c.map(y=>Me(y))} pos: ${this.timelinePos}`),h.length&&this.log(`Removed events ${h}`);let u=null,f=null;n&&(u=this.updateItem(n,this.timelinePos),this.itemsMatch(n,u)?this.playingItem=u:this.waitingItem=this.endedItem=null),this.waitingItem=this.updateItem(this.waitingItem),this.endedItem=this.updateItem(this.endedItem);const g=this.bufferingItem;if(g&&(f=this.updateItem(g,this.bufferedPos),this.itemsMatch(g,f)?this.bufferingItem=f:g.event&&(this.bufferingItem=this.playingItem,this.clearInterstitial(g.event,null))),s.forEach(y=>{y.assetList.forEach(p=>{this.clearAssetPlayer(p.identifier,null)})}),this.playerQueue.forEach(y=>{if(y.interstitial.appendInPlace){const p=y.assetItem.timelineStart,E=y.timelineOffset-p;if(E)try{y.timelineOffset=p}catch(T){Math.abs(E)>Ct&&this.warn(`${T} ("${y.assetId}" ${y.timelineOffset}->${p})`)}}}),d||i){if(this.hls.trigger(m.INTERSTITIALS_UPDATED,{events:o.slice(0),schedule:c.slice(0),durations:l,removedIds:h}),this.isInterstitial(n)&&h.includes(n.event.identifier)){this.warn(`Interstitial "${n.event.identifier}" removed while playing`),this.primaryFallback(n.event);return}n&&this.trimInPlace(u,n),g&&f!==u&&this.trimInPlace(f,g),this.checkBuffer()}},this.hls=e,this.HlsPlayerClass=t,this.assetListLoader=new id(e),this.schedule=new sd(this.onScheduleUpdate,e.logger),this.registerListeners()}registerListeners(){const e=this.hls;e&&(e.on(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(m.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),e.on(m.AUDIO_TRACK_UPDATED,this.onAudioTrackUpdated,this),e.on(m.SUBTITLE_TRACK_SWITCH,this.onSubtitleTrackSwitch,this),e.on(m.SUBTITLE_TRACK_UPDATED,this.onSubtitleTrackUpdated,this),e.on(m.EVENT_CUE_ENTER,this.onInterstitialCueEnter,this),e.on(m.ASSET_LIST_LOADED,this.onAssetListLoaded,this),e.on(m.BUFFER_APPENDED,this.onBufferAppended,this),e.on(m.BUFFER_FLUSHED,this.onBufferFlushed,this),e.on(m.BUFFERED_TO_END,this.onBufferedToEnd,this),e.on(m.MEDIA_ENDED,this.onMediaEnded,this),e.on(m.ERROR,this.onError,this),e.on(m.DESTROYING,this.onDestroying,this))}unregisterListeners(){const e=this.hls;e&&(e.off(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(m.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),e.off(m.AUDIO_TRACK_UPDATED,this.onAudioTrackUpdated,this),e.off(m.SUBTITLE_TRACK_SWITCH,this.onSubtitleTrackSwitch,this),e.off(m.SUBTITLE_TRACK_UPDATED,this.onSubtitleTrackUpdated,this),e.off(m.EVENT_CUE_ENTER,this.onInterstitialCueEnter,this),e.off(m.ASSET_LIST_LOADED,this.onAssetListLoaded,this),e.off(m.BUFFER_CODECS,this.onBufferCodecs,this),e.off(m.BUFFER_APPENDED,this.onBufferAppended,this),e.off(m.BUFFER_FLUSHED,this.onBufferFlushed,this),e.off(m.BUFFERED_TO_END,this.onBufferedToEnd,this),e.off(m.MEDIA_ENDED,this.onMediaEnded,this),e.off(m.ERROR,this.onError,this),e.off(m.DESTROYING,this.onDestroying,this))}startLoad(){this.resumeBuffering()}stopLoad(){this.pauseBuffering()}resumeBuffering(){var e;(e=this.getBufferingPlayer())==null||e.resumeBuffering()}pauseBuffering(){var e;(e=this.getBufferingPlayer())==null||e.pauseBuffering()}destroy(){this.unregisterListeners(),this.stopLoad(),this.assetListLoader&&this.assetListLoader.destroy(),this.emptyPlayerQueue(),this.clearScheduleState(),this.schedule&&this.schedule.destroy(),this.media=this.detachedData=this.mediaSelection=this.requiredTracks=this.altSelection=this.schedule=this.manager=null,this.hls=this.HlsPlayerClass=this.log=null,this.assetListLoader=null,this.onPlay=this.onPause=this.onSeeking=this.onTimeupdate=null,this.onScheduleUpdate=null}onDestroying(){const e=this.primaryMedia||this.media;e&&this.removeMediaListeners(e)}removeMediaListeners(e){Ie(e,"play",this.onPlay),Ie(e,"pause",this.onPause),Ie(e,"seeking",this.onSeeking),Ie(e,"timeupdate",this.onTimeupdate)}onMediaAttaching(e,t){const s=this.media=t.media;xe(s,"seeking",this.onSeeking),xe(s,"timeupdate",this.onTimeupdate),xe(s,"play",this.onPlay),xe(s,"pause",this.onPause)}onMediaAttached(e,t){const s=this.effectivePlayingItem,i=this.detachedData;if(this.detachedData=null,s===null)this.checkStart();else if(!i){this.clearScheduleState();const r=this.findItemIndex(s);this.setSchedulePosition(r)}}clearScheduleState(){this.log("clear schedule state"),this.playingItem=this.bufferingItem=this.waitingItem=this.endedItem=this.playingAsset=this.endedAsset=this.bufferingAsset=null}onMediaDetaching(e,t){const s=!!t.transferMedia,i=this.media;if(this.media=null,!s&&(i&&this.removeMediaListeners(i),this.detachedData)){const r=this.getBufferingPlayer();r&&(this.log(`Removing schedule state for detachedData and ${r}`),this.playingAsset=this.endedAsset=this.bufferingAsset=this.bufferingItem=this.waitingItem=this.detachedData=null,r.detachMedia()),this.shouldPlay=!1}}get interstitialsManager(){if(!this.hls)return null;if(this.manager)return this.manager;const e=this,t=()=>e.bufferingItem||e.waitingItem,s=d=>d&&e.getAssetPlayer(d.identifier),i=(d,u,f,g,y)=>{if(d){let p=d[u].start;const E=d.event;if(E){if(u==="playout"||E.timelineOccupancy!==_s.Point){const T=s(f);(T==null?void 0:T.interstitial)===E&&(p+=T.assetItem.startOffset+T[y])}}else{const T=g==="bufferedPos"?n():e[g];p+=T-d.start}return p}return 0},r=(d,u)=>{var f;if(d!==0&&u!=="primary"&&(f=e.schedule)!=null&&f.length){var g;const y=e.schedule.findItemIndexAtTime(d),p=(g=e.schedule.items)==null?void 0:g[y];if(p){const E=p[u].start-p.start;return d+E}}return d},n=()=>{const d=e.bufferedPos;return d===Number.MAX_VALUE?o("primary"):Math.max(d,0)},o=d=>{var u,f;return(u=e.primaryDetails)!=null&&u.live?e.primaryDetails.edge:((f=e.schedule)==null?void 0:f.durations[d])||0},c=(d,u)=>{var f,g;const y=e.effectivePlayingItem;if(y!=null&&(f=y.event)!=null&&f.restrictions.skip||!e.schedule)return;e.log(`seek to ${d} "${u}"`);const p=e.effectivePlayingItem,E=e.schedule.findItemIndexAtTime(d,u),T=(g=e.schedule.items)==null?void 0:g[E],S=e.getBufferingPlayer(),v=S==null?void 0:S.interstitial,x=v==null?void 0:v.appendInPlace,D=p&&e.itemsMatch(p,T);if(p&&(x||D)){const A=s(e.playingAsset),_=(A==null?void 0:A.media)||e.primaryMedia;if(_){const b=u==="primary"?_.currentTime:i(p,u,e.playingAsset,"timelinePos","currentTime"),I=d-b,P=(x?b:_.currentTime)+I;if(P>=0&&(!A||x||P<=A.duration)){_.currentTime=P;return}}}if(T){let A=d;if(u!=="primary"){const b=T[u].start,I=d-b;A=T.start+I}const _=!e.isInterstitial(T);if((!e.isInterstitial(p)||p.event.appendInPlace)&&(_||T.event.appendInPlace)){const b=e.media||(x?S==null?void 0:S.media:null);b&&(b.currentTime=A)}else if(p){const b=e.findItemIndex(p);if(E>b){const P=e.schedule.findJumpRestrictedIndex(b+1,E);if(P>b){e.setSchedulePosition(P);return}}let I=0;if(_)e.timelinePos=A,e.checkBuffer();else{const P=T.event.assetList,M=d-(T[u]||T).start;for(let G=P.length;G--;){const W=P[G];if(W.duration&&M>=W.startOffset&&M{const d=e.effectivePlayingItem;if(e.isInterstitial(d))return d;const u=t();return e.isInterstitial(u)?u:null},h={get bufferedEnd(){const d=t(),u=e.bufferingItem;if(u&&u===d){var f;return i(u,"playout",e.bufferingAsset,"bufferedPos","bufferedEnd")-u.playout.start||((f=e.bufferingAsset)==null?void 0:f.startOffset)||0}return 0},get currentTime(){const d=l(),u=e.effectivePlayingItem;return u&&u===d?i(u,"playout",e.effectivePlayingAsset,"timelinePos","currentTime")-u.playout.start:0},set currentTime(d){const u=l(),f=e.effectivePlayingItem;f&&f===u&&c(d+f.playout.start,"playout")},get duration(){const d=l();return d?d.playout.end-d.playout.start:0},get assetPlayers(){var d;const u=(d=l())==null?void 0:d.event.assetList;return u?u.map(f=>e.getAssetPlayer(f.identifier)):[]},get playingIndex(){var d;const u=(d=l())==null?void 0:d.event;return u&&e.effectivePlayingAsset?u.findAssetIndex(e.effectivePlayingAsset):-1},get scheduleItem(){return l()}};return this.manager={get events(){var d;return((d=e.schedule)==null||(d=d.events)==null?void 0:d.slice(0))||[]},get schedule(){var d;return((d=e.schedule)==null||(d=d.items)==null?void 0:d.slice(0))||[]},get interstitialPlayer(){return l()?h:null},get playerQueue(){return e.playerQueue.slice(0)},get bufferingAsset(){return e.bufferingAsset},get bufferingItem(){return t()},get bufferingIndex(){const d=t();return e.findItemIndex(d)},get playingAsset(){return e.effectivePlayingAsset},get playingItem(){return e.effectivePlayingItem},get playingIndex(){const d=e.effectivePlayingItem;return e.findItemIndex(d)},primary:{get bufferedEnd(){return n()},get currentTime(){const d=e.timelinePos;return d>0?d:0},set currentTime(d){c(d,"primary")},get duration(){return o("primary")},get seekableStart(){var d;return((d=e.primaryDetails)==null?void 0:d.fragmentStart)||0}},integrated:{get bufferedEnd(){return i(t(),"integrated",e.bufferingAsset,"bufferedPos","bufferedEnd")},get currentTime(){return i(e.effectivePlayingItem,"integrated",e.effectivePlayingAsset,"timelinePos","currentTime")},set currentTime(d){c(d,"integrated")},get duration(){return o("integrated")},get seekableStart(){var d;return r(((d=e.primaryDetails)==null?void 0:d.fragmentStart)||0,"integrated")}},skip:()=>{const d=e.effectivePlayingItem,u=d==null?void 0:d.event;if(u&&!u.restrictions.skip){const f=e.findItemIndex(d);if(u.appendInPlace){const g=d.playout.start+d.event.duration;c(g+.001,"playout")}else e.advanceAfterAssetEnded(u,f,1/0)}}}}get effectivePlayingItem(){return this.waitingItem||this.playingItem||this.endedItem}get effectivePlayingAsset(){return this.playingAsset||this.endedAsset}get playingLastItem(){var e;const t=this.playingItem,s=(e=this.schedule)==null?void 0:e.items;return!this.playbackStarted||!t||!s?!1:this.findItemIndex(t)===s.length-1}get playbackStarted(){return this.effectivePlayingItem!==null}get currentTime(){var e,t;if(this.mediaSelection===null)return;const s=this.waitingItem||this.playingItem;if(this.isInterstitial(s)&&!s.event.appendInPlace)return;let i=this.media;!i&&(e=this.bufferingItem)!=null&&(e=e.event)!=null&&e.appendInPlace&&(i=this.primaryMedia);const r=(t=i)==null?void 0:t.currentTime;if(!(r===void 0||!B(r)))return r}get primaryMedia(){var e;return this.media||((e=this.detachedData)==null?void 0:e.media)||null}isInterstitial(e){return!!(e!=null&&e.event)}retreiveMediaSource(e,t){const s=this.getAssetPlayer(e);s&&this.transferMediaFromPlayer(s,t)}transferMediaFromPlayer(e,t){const s=e.interstitial.appendInPlace,i=e.media;if(s&&i===this.primaryMedia){if(this.bufferingAsset=null,(!t||this.isInterstitial(t)&&!t.event.appendInPlace)&&t&&i){this.detachedData={media:i};return}const r=e.transferMedia();this.log(`transfer MediaSource from ${e} ${oe(r)}`),this.detachedData=r}else t&&i&&(this.shouldPlay||(this.shouldPlay=!i.paused))}transferMediaTo(e,t){var s,i;if(e.media===t)return;let r=null;const n=this.hls,o=e!==n,c=o&&e.interstitial.appendInPlace,l=(s=this.detachedData)==null?void 0:s.mediaSource;let h;if(n.media)c&&(r=n.transferMedia(),this.detachedData=r),h="Primary";else if(l){const g=this.getBufferingPlayer();g?(r=g.transferMedia(),h=`${g}`):h="detached MediaSource"}else h="detached media";if(!r){if(l)r=this.detachedData,this.log(`using detachedData: MediaSource ${oe(r)}`);else if(!this.detachedData||n.media===t){const g=this.playerQueue;g.length>1&&g.forEach(y=>{if(o&&y.interstitial.appendInPlace!==c){const p=y.interstitial;this.clearInterstitial(y.interstitial,null),p.appendInPlace=!1,p.appendInPlace&&this.warn(`Could not change append strategy for queued assets ${p}`)}}),this.hls.detachMedia(),this.detachedData={media:t}}}const d=r&&"mediaSource"in r&&((i=r.mediaSource)==null?void 0:i.readyState)!=="closed",u=d&&r?r:t;this.log(`${d?"transfering MediaSource":"attaching media"} to ${o?e:"Primary"} from ${h} (media.currentTime: ${t.currentTime})`);const f=this.schedule;if(u===r&&f){const g=o&&e.assetId===f.assetIdAtEnd;u.overrides={duration:f.duration,endOfStream:!o||g,cueRemoval:!o}}e.attachMedia(u)}onInterstitialCueEnter(){this.onTimeupdate()}checkStart(){const e=this.schedule,t=e==null?void 0:e.events;if(!t||this.playbackDisabled||!this.media)return;this.bufferedPos===-1&&(this.bufferedPos=0);const s=this.timelinePos,i=this.effectivePlayingItem;if(s===-1){const r=this.hls.startPosition;if(this.timelinePos=r,t.length===0)this.setSchedulePosition(0);else if(t[0].cue.pre){this.log(At("checkStart (preroll)",r));const n=e.findEventIndex(t[0].identifier);this.setSchedulePosition(n)}else if(r>=0||!this.primaryLive){this.log(At("checkStart",r));const n=this.timelinePos=r>0?r:0,o=e.findItemIndexAtTime(n);this.setSchedulePosition(o)}else this.hls.liveSyncPosition===0?this.setSchedulePosition(0):this.log("[checkStart] waiting for live start")}else if(i&&!this.playingItem){this.log(At("checkStart (playing item)",i.start));const r=e.findItemIndex(i);this.setSchedulePosition(r)}}advanceAssetBuffering(e,t){const s=e.event,i=s.findAssetIndex(t),r=js(s,i);if(!s.isAssetPastPlayoutLimit(r))this.bufferedToEvent(e,r);else if(this.schedule){var n;const o=(n=this.schedule.items)==null?void 0:n[this.findItemIndex(e)+1];o&&this.bufferedToItem(o)}}advanceAfterAssetEnded(e,t,s){const i=js(e,s);if(e.isAssetPastPlayoutLimit(i)){if(this.schedule){const r=this.schedule.items;if(r){const n=t+1,o=r.length;if(n>=o){this.setSchedulePosition(-1);return}const c=e.resumeTime;this.timelinePos=0?i[e]:null;this.log(`setSchedulePosition ${e}, ${t} (${r&&Me(r)}) pos: ${this.timelinePos}`);const n=this.waitingItem||this.playingItem,o=this.playingLastItem;if(this.isInterstitial(n)){const h=n.event,d=this.playingAsset,u=d==null?void 0:d.identifier,f=u?this.getAssetPlayer(u):null;if(f&&u&&(!this.eventItemsMatch(n,r)||t!==void 0&&u!==h.assetList[t].identifier)){var c;const g=h.findAssetIndex(d);if(this.log(`INTERSTITIAL_ASSET_ENDED ${g+1}/${h.assetList.length} ${It(d)}`),this.endedAsset=d,this.playingAsset=null,this.hls.trigger(m.INTERSTITIAL_ASSET_ENDED,{asset:d,assetListIndex:g,event:h,schedule:i.slice(0),scheduleIndex:e,player:f}),n!==this.playingItem){this.itemsMatch(n,this.playingItem)&&!this.playingAsset&&this.advanceAfterAssetEnded(h,this.findItemIndex(this.playingItem),g);return}this.retreiveMediaSource(u,r),f.media&&!((c=this.detachedData)!=null&&c.mediaSource)&&f.detachMedia()}if(!this.eventItemsMatch(n,r)&&(this.endedItem=n,this.playingItem=null,this.log(`INTERSTITIAL_ENDED ${h} ${Me(n)}`),h.hasPlayed=!0,this.hls.trigger(m.INTERSTITIAL_ENDED,{event:h,schedule:i.slice(0),scheduleIndex:e}),h.cue.once)){var l;this.updateSchedule();const g=(l=this.schedule)==null?void 0:l.items;if(r&&g){const y=this.findItemIndex(r);this.advanceSchedule(y,g,t,n,o)}return}}this.advanceSchedule(e,i,t,n,o)}advanceSchedule(e,t,s,i,r){const n=this.schedule;if(!n)return;const o=t[e]||null,c=this.primaryMedia,l=this.playerQueue;if(l.length&&l.forEach(h=>{const d=h.interstitial,u=n.findEventIndex(d.identifier);(ue+1)&&this.clearInterstitial(d,o)}),this.isInterstitial(o)){this.timelinePos=Math.min(Math.max(this.timelinePos,o.start),o.end);const h=o.event;if(s===void 0){s=n.findAssetIndex(h,this.timelinePos);const g=js(h,s-1);if(h.isAssetPastPlayoutLimit(g)||h.appendInPlace&&this.timelinePos===o.end){this.advanceAfterAssetEnded(h,e,s);return}s=g}const d=this.waitingItem;this.assetsBuffered(o,c)||this.setBufferingItem(o);let u=this.preloadAssets(h,s);if(this.eventItemsMatch(o,d||i)||(this.waitingItem=o,this.log(`INTERSTITIAL_STARTED ${Me(o)} ${h.appendInPlace?"append in place":""}`),this.hls.trigger(m.INTERSTITIAL_STARTED,{event:h,schedule:t.slice(0),scheduleIndex:e})),!h.assetListLoaded){this.log(`Waiting for ASSET-LIST to complete loading ${h}`);return}if(h.assetListLoader&&(h.assetListLoader.destroy(),h.assetListLoader=void 0),!c){this.log(`Waiting for attachMedia to start Interstitial ${h}`);return}this.waitingItem=this.endedItem=null,this.playingItem=o;const f=h.assetList[s];if(!f){this.advanceAfterAssetEnded(h,e,s||0);return}if(u||(u=this.getAssetPlayer(f.identifier)),u===null||u.destroyed){const g=h.assetList.length;this.warn(`asset ${s+1}/${g} player destroyed ${h}`),u=this.createAssetPlayer(h,f,s),u.loadSource()}if(!this.eventItemsMatch(o,this.bufferingItem)&&h.appendInPlace&&this.isAssetBuffered(f))return;this.startAssetPlayer(u,s,t,e,c),this.shouldPlay&&nn(u.media)}else o?(this.resumePrimary(o,e,i),this.shouldPlay&&nn(this.hls.media)):r&&this.isInterstitial(i)&&(this.endedItem=null,this.playingItem=i,i.event.appendInPlace||this.attachPrimary(n.durations.primary,null))}get playbackDisabled(){return this.hls.config.enableInterstitialPlayback===!1}get primaryDetails(){var e;return(e=this.mediaSelection)==null?void 0:e.main.details}get primaryLive(){var e;return!!((e=this.primaryDetails)!=null&&e.live)}resumePrimary(e,t,s){var i,r;if(this.playingItem=e,this.playingAsset=this.endedAsset=null,this.waitingItem=this.endedItem=null,this.bufferedToItem(e),this.log(`resuming ${Me(e)}`),!((i=this.detachedData)!=null&&i.mediaSource)){let o=this.timelinePos;(o=e.end)&&(o=this.getPrimaryResumption(e,t),this.log(At("resumePrimary",o)),this.timelinePos=o),this.attachPrimary(o,e)}if(!s)return;const n=(r=this.schedule)==null?void 0:r.items;n&&(this.log(`INTERSTITIALS_PRIMARY_RESUMED ${Me(e)}`),this.hls.trigger(m.INTERSTITIALS_PRIMARY_RESUMED,{schedule:n.slice(0),scheduleIndex:t}),this.checkBuffer())}getPrimaryResumption(e,t){const s=e.start;if(this.primaryLive){const i=this.primaryDetails;if(t===0)return this.hls.startPosition;if(i&&(si.edge))return this.hls.liveSyncPosition||-1}return s}isAssetBuffered(e){const t=this.getAssetPlayer(e.identifier);return t!=null&&t.hls?t.hls.bufferedToEnd:X.bufferInfo(this.primaryMedia,this.timelinePos,0).end+1>=e.timelineStart+(e.duration||0)}attachPrimary(e,t,s){t?this.setBufferingItem(t):this.bufferingItem=this.playingItem,this.bufferingAsset=null;const i=this.primaryMedia;if(!i)return;const r=this.hls;r.media?this.checkBuffer():(this.transferMediaTo(r,i),s&&this.startLoadingPrimaryAt(e,s)),s||(this.log(At("attachPrimary",e)),this.timelinePos=e,this.startLoadingPrimaryAt(e,s))}startLoadingPrimaryAt(e,t){var s;const i=this.hls;!i.loadingEnabled||!i.media||Math.abs((((s=i.mainForwardBufferInfo)==null?void 0:s.start)||i.media.currentTime)-e)>.5?i.startLoad(e,t):i.bufferingEnabled||i.resumeBuffering()}onManifestLoading(){var e;this.stopLoad(),(e=this.schedule)==null||e.reset(),this.emptyPlayerQueue(),this.clearScheduleState(),this.shouldPlay=!1,this.bufferedPos=this.timelinePos=-1,this.mediaSelection=this.altSelection=this.manager=this.requiredTracks=null,this.hls.off(m.BUFFER_CODECS,this.onBufferCodecs,this),this.hls.on(m.BUFFER_CODECS,this.onBufferCodecs,this)}onLevelUpdated(e,t){if(t.level===-1||!this.schedule)return;const s=this.hls.levels[t.level];if(!s.details)return;const i=ie(ie({},this.mediaSelection||this.altSelection),{},{main:s});this.mediaSelection=i,this.schedule.parseInterstitialDateRanges(i,this.hls.config.interstitialAppendInPlace),!this.effectivePlayingItem&&this.schedule.items&&this.checkStart()}onAudioTrackUpdated(e,t){const s=this.hls.audioTracks[t.id],i=this.mediaSelection;if(!i){this.altSelection=ie(ie({},this.altSelection),{},{audio:s});return}const r=ie(ie({},i),{},{audio:s});this.mediaSelection=r}onSubtitleTrackUpdated(e,t){const s=this.hls.subtitleTracks[t.id],i=this.mediaSelection;if(!i){this.altSelection=ie(ie({},this.altSelection),{},{subtitles:s});return}const r=ie(ie({},i),{},{subtitles:s});this.mediaSelection=r}onAudioTrackSwitching(e,t){const s=fr(t);this.playerQueue.forEach(({hls:i})=>i&&(i.setAudioOption(t)||i.setAudioOption(s)))}onSubtitleTrackSwitch(e,t){const s=fr(t);this.playerQueue.forEach(({hls:i})=>i&&(i.setSubtitleOption(t)||t.id!==-1&&i.setSubtitleOption(s)))}onBufferCodecs(e,t){const s=t.tracks;s&&(this.requiredTracks=s)}onBufferAppended(e,t){this.checkBuffer()}onBufferFlushed(e,t){const s=this.playingItem;if(s&&!this.itemsMatch(s,this.bufferingItem)&&!this.isInterstitial(s)){const i=this.timelinePos;this.bufferedPos=i,this.checkBuffer()}}onBufferedToEnd(e){if(!this.schedule)return;const t=this.schedule.events;if(this.bufferedPos.25){e.event.assetList.forEach((r,n)=>{e.event.isAssetPastPlayoutLimit(n)&&this.clearAssetPlayer(r.identifier,null)});const s=e.end+.25,i=X.bufferInfo(this.primaryMedia,s,0);(i.end>s||(i.nextStart||0)>s)&&(this.log(`trim buffered interstitial ${Me(e)} (was ${Me(t)})`),this.attachPrimary(s,null,!0),this.flushFrontBuffer(s))}}itemsMatch(e,t){return!!t&&(e===t||e.event&&t.event&&this.eventItemsMatch(e,t)||!e.event&&!t.event&&this.findItemIndex(e)===this.findItemIndex(t))}eventItemsMatch(e,t){var s;return!!t&&(e===t||e.event.identifier===((s=t.event)==null?void 0:s.identifier))}findItemIndex(e,t){return e&&this.schedule?this.schedule.findItemIndex(e,t):-1}updateSchedule(e=!1){var t;const s=this.mediaSelection;s&&((t=this.schedule)==null||t.updateSchedule(s,[],e))}checkBuffer(e){var t;const s=(t=this.schedule)==null?void 0:t.items;if(!s)return;const i=X.bufferInfo(this.primaryMedia,this.timelinePos,0);e&&(this.bufferedPos=this.timelinePos),e||(e=i.len<1),this.updateBufferedPos(i.end,s,e)}updateBufferedPos(e,t,s){const i=this.schedule,r=this.bufferingItem;if(this.bufferedPos>e||!i)return;if(t.length===1&&this.itemsMatch(t[0],r)){this.bufferedPos=e;return}const n=this.playingItem,o=this.findItemIndex(n);let c=i.findItemIndexAtTime(e);if(this.bufferedPos=r.end||(l=u.event)!=null&&l.appendInPlace&&e+.01>=u.start)&&(c=d),this.isInterstitial(r)){const f=r.event;if(d-o>1&&f.appendInPlace===!1||f.assetList.length===0&&f.assetListLoader)return}if(this.bufferedPos=e,c>h&&c>o)this.bufferedToItem(u);else{const f=this.primaryDetails;this.primaryLive&&f&&e>f.edge-f.targetduration&&u.start{const r=this.getAssetPlayer(i.identifier);return!(r!=null&&r.bufferedInPlaceToEnd(t))})}setBufferingItem(e){const t=this.bufferingItem,s=this.schedule;if(!this.itemsMatch(e,t)&&s){const{items:i,events:r}=s;if(!i||!r)return t;const n=this.isInterstitial(e),o=this.getBufferingPlayer();this.bufferingItem=e,this.bufferedPos=Math.max(e.start,Math.min(e.end,this.timelinePos));const c=o?o.remaining:t?t.end-this.timelinePos:0;if(this.log(`INTERSTITIALS_BUFFERED_TO_BOUNDARY ${Me(e)}`+(t?` (${c.toFixed(2)} remaining)`:"")),!this.playbackDisabled)if(n){const l=s.findAssetIndex(e.event,this.bufferedPos);e.event.assetList.forEach((h,d)=>{const u=this.getAssetPlayer(h.identifier);u&&(d===l&&u.loadSource(),u.resumeBuffering())})}else this.hls.resumeBuffering(),this.playerQueue.forEach(l=>l.pauseBuffering());this.hls.trigger(m.INTERSTITIALS_BUFFERED_TO_BOUNDARY,{events:r.slice(0),schedule:i.slice(0),bufferingIndex:this.findItemIndex(e),playingIndex:this.findItemIndex(this.playingItem)})}else this.bufferingItem!==e&&(this.bufferingItem=e);return t}bufferedToItem(e,t=0){const s=this.setBufferingItem(e);if(!this.playbackDisabled){if(this.isInterstitial(e))this.bufferedToEvent(e,t);else if(s!==null){this.bufferingAsset=null;const i=this.detachedData;i?i.mediaSource?this.attachPrimary(e.start,e,!0):this.preloadPrimary(e):this.preloadPrimary(e)}}}preloadPrimary(e){const t=this.findItemIndex(e),s=this.getPrimaryResumption(e,t);this.startLoadingPrimaryAt(s)}bufferedToEvent(e,t){const s=e.event,i=s.assetList.length===0&&!s.assetListLoader,r=s.cue.once;if(i||!r){const n=this.preloadAssets(s,t);if(n!=null&&n.interstitial.appendInPlace){const o=this.primaryMedia;o&&this.bufferAssetPlayer(n,o)}}}preloadAssets(e,t){const s=e.assetUrl,i=e.assetList.length,r=i===0&&!e.assetListLoader,n=e.cue.once;if(r){const c=e.timelineStart;if(e.appendInPlace){var o;const u=this.playingItem;!this.isInterstitial(u)&&(u==null||(o=u.nextEvent)==null?void 0:o.identifier)===e.identifier&&this.flushFrontBuffer(c+.25)}let l,h=0;if(!this.playingItem&&this.primaryLive&&(h=this.hls.startPosition,h===-1&&(h=this.hls.liveSyncPosition||0)),h&&!(e.cue.pre||e.cue.post)){const u=h-c;u>0&&(l=Math.round(u*1e3)/1e3)}if(this.log(`Load interstitial asset ${t+1}/${s?1:i} ${e}${l?` live-start: ${h} start-offset: ${l}`:""}`),s)return this.createAsset(e,0,0,c,e.duration,s);const d=this.assetListLoader.loadAssetList(e,l);d&&(e.assetListLoader=d)}else if(!n&&i){for(let l=t;l{this.hls.trigger(m.BUFFER_FLUSHING,{startOffset:e,endOffset:1/0,type:i})})}getAssetPlayerQueueIndex(e){const t=this.playerQueue;for(let s=0;s1){const x=t.duration;x&&v{if(v.live){var x;const _=new Error(`Interstitials MUST be VOD assets ${e}`),b={fatal:!0,type:Y.OTHER_ERROR,details:R.INTERSTITIAL_ASSET_ITEM_ERROR,error:_},I=((x=this.schedule)==null?void 0:x.findEventIndex(e.identifier))||-1;this.handleAssetItemError(b,e,I,s,_.message);return}const D=v.edge-v.fragmentStart,A=t.duration;(y||A===null||D>A)&&(y=!1,this.log(`Interstitial asset "${d}" duration change ${A} > ${D}`),t.duration=D,this.updateSchedule())};g.on(m.LEVEL_UPDATED,(v,{details:x})=>p(x)),g.on(m.LEVEL_PTS_UPDATED,(v,{details:x})=>p(x)),g.on(m.EVENT_CUE_ENTER,()=>this.onInterstitialCueEnter());const E=(v,x)=>{const D=this.getAssetPlayer(d);if(D&&x.tracks){D.off(m.BUFFER_CODECS,E),D.tracks=x.tracks;const A=this.primaryMedia;this.bufferingAsset===D.assetItem&&A&&!D.media&&this.bufferAssetPlayer(D,A)}};g.on(m.BUFFER_CODECS,E);const T=()=>{var v;const x=this.getAssetPlayer(d);if(this.log(`buffered to end of asset ${x}`),!x||!this.schedule)return;const D=this.schedule.findEventIndex(e.identifier),A=(v=this.schedule.items)==null?void 0:v[D];this.isInterstitial(A)&&this.advanceAssetBuffering(A,t)};g.on(m.BUFFERED_TO_END,T);const S=v=>()=>{if(!this.getAssetPlayer(d)||!this.schedule)return;this.shouldPlay=!0;const D=this.schedule.findEventIndex(e.identifier);this.advanceAfterAssetEnded(e,D,v)};return g.once(m.MEDIA_ENDED,S(s)),g.once(m.PLAYOUT_LIMIT_REACHED,S(1/0)),g.on(m.ERROR,(v,x)=>{if(!this.schedule)return;const D=this.getAssetPlayer(d);if(x.details===R.BUFFER_STALLED_ERROR){if(D!=null&&D.appendInPlace){this.handleInPlaceStall(e);return}this.onTimeupdate(),this.checkBuffer(!0);return}this.handleAssetItemError(x,e,this.schedule.findEventIndex(e.identifier),s,`Asset player error ${x.error} ${e}`)}),g.on(m.DESTROYING,()=>{if(!this.getAssetPlayer(d)||!this.schedule)return;const x=new Error(`Asset player destroyed unexpectedly ${d}`),D={fatal:!0,type:Y.OTHER_ERROR,details:R.INTERSTITIAL_ASSET_ITEM_ERROR,error:x};this.handleAssetItemError(D,e,this.schedule.findEventIndex(e.identifier),s,x.message)}),this.log(`INTERSTITIAL_ASSET_PLAYER_CREATED ${It(t)}`),this.hls.trigger(m.INTERSTITIAL_ASSET_PLAYER_CREATED,{asset:t,assetListIndex:s,event:e,player:g}),g}clearInterstitial(e,t){this.clearAssetPlayers(e,t),e.reset()}clearAssetPlayers(e,t){e.assetList.forEach(s=>{this.clearAssetPlayer(s.identifier,t)})}resetAssetPlayer(e){const t=this.getAssetPlayerQueueIndex(e);if(t!==-1){this.log(`reset asset player "${e}" after error`);const s=this.playerQueue[t];this.transferMediaFromPlayer(s,null),s.resetDetails()}}clearAssetPlayer(e,t){const s=this.getAssetPlayerQueueIndex(e);if(s!==-1){const i=this.playerQueue[s];this.log(`clear ${i} toSegment: ${t&&Me(t)}`),this.transferMediaFromPlayer(i,t),this.playerQueue.splice(s,1),i.destroy()}}emptyPlayerQueue(){let e;for(;e=this.playerQueue.pop();)e.destroy();this.playerQueue=[]}startAssetPlayer(e,t,s,i,r){const{interstitial:n,assetItem:o,assetId:c}=e,l=n.assetList.length,h=this.playingAsset;this.endedAsset=null,this.playingAsset=o,(!h||h.identifier!==c)&&(h&&(this.clearAssetPlayer(h.identifier,s[i]),delete h.error),this.log(`INTERSTITIAL_ASSET_STARTED ${t+1}/${l} ${It(o)}`),this.hls.trigger(m.INTERSTITIAL_ASSET_STARTED,{asset:o,assetListIndex:t,event:n,schedule:s.slice(0),scheduleIndex:i,player:e})),this.bufferAssetPlayer(e,r)}bufferAssetPlayer(e,t){var s,i;if(!this.schedule)return;const{interstitial:r,assetItem:n}=e,o=this.schedule.findEventIndex(r.identifier),c=(s=this.schedule.items)==null?void 0:s[o];if(!c)return;e.loadSource(),this.setBufferingItem(c),this.bufferingAsset=n;const l=this.getBufferingPlayer();if(l===e)return;const h=r.appendInPlace;if(h&&(l==null?void 0:l.interstitial.appendInPlace)===!1)return;const d=(l==null?void 0:l.tracks)||((i=this.detachedData)==null?void 0:i.tracks)||this.requiredTracks;if(h&&n!==this.playingAsset){if(!e.tracks){this.log(`Waiting for track info before buffering ${e}`);return}if(d&&!xn(d,e.tracks)){const u=new Error(`Asset ${It(n)} SourceBuffer tracks ('${Object.keys(e.tracks)}') are not compatible with primary content tracks ('${Object.keys(d)}')`),f={fatal:!0,type:Y.OTHER_ERROR,details:R.INTERSTITIAL_ASSET_ITEM_ERROR,error:u},g=r.findAssetIndex(n);this.handleAssetItemError(f,r,o,g,u.message);return}}this.transferMediaTo(e,t)}handleInPlaceStall(e){const t=this.schedule,s=this.primaryMedia;if(!t||!s)return;const i=s.currentTime,r=t.findAssetIndex(e,i),n=e.assetList[r];if(n){const o=this.getAssetPlayer(n.identifier);if(o){const c=o.currentTime||i-n.timelineStart,l=o.duration-c;if(this.warn(`Stalled at ${c} of ${c+l} in ${o} ${e} (media.currentTime: ${i})`),c&&(l/s.playbackRate<.5||o.bufferedInPlaceToEnd(s))&&o.hls){const h=t.findEventIndex(e.identifier);this.advanceAfterAssetEnded(e,h,r)}}}}advanceInPlace(e){const t=this.primaryMedia;t&&t.currentTime!y.error))t.error=g;else for(let y=i;y{const E=parseFloat(y.DURATION);this.createAsset(r,p,h,c+h,E,y.URI),h+=E}),r.duration=h,this.log(`Loaded asset-list with duration: ${h} (was: ${l}) ${r}`);const d=this.waitingItem,u=(d==null?void 0:d.event.identifier)===n;this.updateSchedule();const f=(i=this.bufferingItem)==null?void 0:i.event;if(u){var g;const y=this.schedule.findEventIndex(n),p=(g=this.schedule.items)==null?void 0:g[y];if(p){if(!this.playingItem&&this.timelinePos>p.end&&this.schedule.findItemIndexAtTime(this.timelinePos)!==y){r.error=new Error(`Interstitial ${o.length?"no longer within playback range":"asset-list is empty"} ${this.timelinePos} ${r}`),this.log(r.error.message),this.updateSchedule(!0),this.primaryFallback(r);return}this.setBufferingItem(p)}this.setSchedulePosition(y)}else if((f==null?void 0:f.identifier)===n){const y=r.assetList[0];if(y){const p=this.getAssetPlayer(y.identifier);if(f.appendInPlace){const E=this.primaryMedia;p&&E&&this.bufferAssetPlayer(p,E)}else p&&p.loadSource()}}}onError(e,t){if(this.schedule)switch(t.details){case R.ASSET_LIST_PARSING_ERROR:case R.ASSET_LIST_LOAD_ERROR:case R.ASSET_LIST_LOAD_TIMEOUT:{const s=t.interstitial;s&&(this.updateSchedule(!0),this.primaryFallback(s));break}case R.BUFFER_STALLED_ERROR:{const s=this.endedItem||this.waitingItem||this.playingItem;if(this.isInterstitial(s)&&s.event.appendInPlace){this.handleInPlaceStall(s.event);return}this.log(`Primary player stall @${this.timelinePos} bufferedPos: ${this.bufferedPos}`),this.onTimeupdate(),this.checkBuffer(!0);break}}}}const an=500;class nd extends Fi{constructor(e,t,s){super(e,t,s,"subtitle-stream-controller",U.SUBTITLE),this.currentTrackId=-1,this.tracksBuffered=[],this.mainDetails=null,this.registerListeners()}onHandlerDestroying(){this.unregisterListeners(),super.onHandlerDestroying(),this.mainDetails=null}registerListeners(){super.registerListeners();const{hls:e}=this;e.on(m.LEVEL_LOADED,this.onLevelLoaded,this),e.on(m.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),e.on(m.SUBTITLE_TRACK_SWITCH,this.onSubtitleTrackSwitch,this),e.on(m.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),e.on(m.SUBTITLE_FRAG_PROCESSED,this.onSubtitleFragProcessed,this),e.on(m.BUFFER_FLUSHING,this.onBufferFlushing,this)}unregisterListeners(){super.unregisterListeners();const{hls:e}=this;e.off(m.LEVEL_LOADED,this.onLevelLoaded,this),e.off(m.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),e.off(m.SUBTITLE_TRACK_SWITCH,this.onSubtitleTrackSwitch,this),e.off(m.SUBTITLE_TRACK_LOADED,this.onSubtitleTrackLoaded,this),e.off(m.SUBTITLE_FRAG_PROCESSED,this.onSubtitleFragProcessed,this),e.off(m.BUFFER_FLUSHING,this.onBufferFlushing,this)}startLoad(e,t){this.stopLoad(),this.state=C.IDLE,this.setInterval(an),this.nextLoadPosition=this.lastCurrentTime=e+this.timelineOffset,this.startPosition=t?-1:e,this.tick()}onManifestLoading(){super.onManifestLoading(),this.mainDetails=null}onMediaDetaching(e,t){this.tracksBuffered=[],super.onMediaDetaching(e,t)}onLevelLoaded(e,t){this.mainDetails=t.details}onSubtitleFragProcessed(e,t){const{frag:s,success:i}=t;if(this.fragContextChanged(s)||(de(s)&&(this.fragPrevious=s),this.state=C.IDLE),!i)return;const r=this.tracksBuffered[this.currentTrackId];if(!r)return;let n;const o=s.start;for(let l=0;l=r[l].start&&o<=r[l].end){n=r[l];break}const c=s.start+s.duration;n?n.end=c:(n={start:o,end:c},r.push(n)),this.fragmentTracker.fragBuffered(s),this.fragBufferedComplete(s,null),this.media&&this.tick()}onBufferFlushing(e,t){const{startOffset:s,endOffset:i}=t;if(s===0&&i!==Number.POSITIVE_INFINITY){const r=i-1;if(r<=0)return;t.endOffsetSubtitles=Math.max(0,r),this.tracksBuffered.forEach(n=>{for(let o=0;onew Kt(s));return}this.tracksBuffered=[],this.levels=t.map(s=>{const i=new Kt(s);return this.tracksBuffered[i.id]=[],i}),this.fragmentTracker.removeFragmentsInRange(0,Number.POSITIVE_INFINITY,U.SUBTITLE),this.fragPrevious=null,this.mediaBuffer=null}onSubtitleTrackSwitch(e,t){var s;if(this.currentTrackId=t.id,!((s=this.levels)!=null&&s.length)||this.currentTrackId===-1){this.clearInterval();return}const i=this.levels[this.currentTrackId];i!=null&&i.details?this.mediaBuffer=this.mediaBufferTimeRanges:this.mediaBuffer=null,i&&this.state!==C.STOPPED&&this.setInterval(an)}onSubtitleTrackLoaded(e,t){var s;const{currentTrackId:i,levels:r}=this,{details:n,id:o}=t;if(!r){this.warn(`Subtitle tracks were reset while loading level ${o}`);return}const c=r[o];if(o>=r.length||!c)return;this.log(`Subtitle track ${o} loaded [${n.startSN},${n.endSN}]${n.lastPartSn?`[part-${n.lastPartSn}-${n.lastPartIndex}]`:""},duration:${n.totalduration}`),this.mediaBuffer=this.mediaBufferTimeRanges;let l=0;if(n.live||(s=c.details)!=null&&s.live){if(n.deltaUpdateFailed)return;const d=this.mainDetails;if(!d){this.startFragRequested=!1;return}const u=d.fragments[0];if(!c.details)n.hasProgramDateTime&&d.hasProgramDateTime?(Rs(n,d),l=n.fragmentStart):u&&(l=u.start,ui(n,l));else{var h;l=this.alignPlaylists(n,c.details,(h=this.levelLastLoaded)==null?void 0:h.details),l===0&&u&&(l=u.start,ui(n,l))}d&&!this.startFragRequested&&this.setStartPosition(d,l)}c.details=n,this.levelLastLoaded=c,o===i&&(this.hls.trigger(m.SUBTITLE_TRACK_UPDATED,{details:n,id:o,groupId:t.groupId}),this.tick(),n.live&&!this.fragCurrent&&this.media&&this.state===C.IDLE&&(pt(null,n.fragments,this.media.currentTime,0)||(this.warn("Subtitle playlist not aligned with playback"),c.details=void 0)))}_handleFragmentLoadComplete(e){const{frag:t,payload:s}=e,i=t.decryptdata,r=this.hls;if(!this.fragContextChanged(t)&&s&&s.byteLength>0&&i!=null&&i.key&&i.iv&&_t(i.method)){const n=performance.now();this.decrypter.decrypt(new Uint8Array(s),i.key.buffer,i.iv.buffer,wi(i.method)).catch(o=>{throw r.trigger(m.ERROR,{type:Y.MEDIA_ERROR,details:R.FRAG_DECRYPT_ERROR,fatal:!1,error:o,reason:o.message,frag:t}),o}).then(o=>{const c=performance.now();r.trigger(m.FRAG_DECRYPTED,{frag:t,payload:o,stats:{tstart:n,tdecrypt:c}})}).catch(o=>{this.warn(`${o.name}: ${o.message}`),this.state=C.IDLE})}}doTick(){if(!this.media){this.state=C.IDLE;return}if(this.state===C.IDLE){const{currentTrackId:e,levels:t}=this,s=t==null?void 0:t[e];if(!s||!t.length||!s.details||this.waitForLive(s))return;const{config:i}=this,r=this.getLoadPosition(),n=X.bufferedInfo(this.tracksBuffered[this.currentTrackId]||[],r,i.maxBufferHole),{end:o,len:c}=n,l=s.details,h=this.hls.maxBufferLength+l.levelTargetDuration;if(c>h)return;const d=l.fragments,u=d.length,f=l.edge;let g=null;const y=this.fragPrevious;if(of-T?0:T;g=pt(y,d,Math.max(d[0].start,o),S),!g&&y&&y.start{if(i=i>>>0,i>r-1)throw new DOMException(`Failed to execute '${s}' on 'TimeRanges': The index provided (${i}) is greater than the maximum bound (${r})`);return e[i][s]};this.buffered={get length(){return e.length},end(s){return t("end",s,e.length)},start(s){return t("start",s,e.length)}}}}const od={42:225,92:233,94:237,95:243,96:250,123:231,124:247,125:209,126:241,127:9608,128:174,129:176,130:189,131:191,132:8482,133:162,134:163,135:9834,136:224,137:32,138:232,139:226,140:234,141:238,142:244,143:251,144:193,145:201,146:211,147:218,148:220,149:252,150:8216,151:161,152:42,153:8217,154:9473,155:169,156:8480,157:8226,158:8220,159:8221,160:192,161:194,162:199,163:200,164:202,165:203,166:235,167:206,168:207,169:239,170:212,171:217,172:249,173:219,174:171,175:187,176:195,177:227,178:205,179:204,180:236,181:210,182:242,183:213,184:245,185:123,186:125,187:92,188:94,189:95,190:124,191:8764,192:196,193:228,194:214,195:246,196:223,197:165,198:164,199:9475,200:197,201:229,202:216,203:248,204:9487,205:9491,206:9495,207:9499},Ga=a=>String.fromCharCode(od[a]||a),Ne=15,Qe=100,ld={17:1,18:3,21:5,22:7,23:9,16:11,19:12,20:14},cd={17:2,18:4,21:6,22:8,23:10,19:13,20:15},hd={25:1,26:3,29:5,30:7,31:9,24:11,27:12,28:14},dd={25:2,26:4,29:6,30:8,31:10,27:13,28:15},ud=["white","green","blue","cyan","red","yellow","magenta","black","transparent"];class fd{constructor(){this.time=null,this.verboseLevel=0}log(e,t){if(this.verboseLevel>=e){const s=typeof t=="function"?t():t;re.log(`${this.time} [${e}] ${s}`)}}}const ut=function(e){const t=[];for(let s=0;sQe&&(this.logger.log(3,"Too large cursor position "+this.pos),this.pos=Qe)}moveCursor(e){const t=this.pos+e;if(e>1)for(let s=this.pos+1;s=144&&this.backSpace();const t=Ga(e);if(this.pos>=Qe){this.logger.log(0,()=>"Cannot insert "+e.toString(16)+" ("+t+") at position "+this.pos+". Skipping it!");return}this.chars[this.pos].setChar(t,this.currPenState),this.moveCursor(1)}clearFromPos(e){let t;for(t=e;t"pacData = "+oe(e));let t=e.row-1;if(this.nrRollUpRows&&t"bkgData = "+oe(e)),this.backSpace(),this.setPen(e),this.insertChar(32)}setRollUpRows(e){this.nrRollUpRows=e}rollUp(){if(this.nrRollUpRows===null){this.logger.log(3,"roll_up but nrRollUpRows not set yet");return}this.logger.log(1,()=>this.getDisplayText());const e=this.currRow+1-this.nrRollUpRows,t=this.rows.splice(e,1)[0];t.clear(),this.rows.splice(this.currRow,0,t),this.logger.log(2,"Rolling up")}getDisplayText(e){e=e||!1;const t=[];let s="",i=-1;for(let r=0;r0&&(e?s="["+t.join(" | ")+"]":s=t.join(` +`)),s}getTextAndFormat(){return this.rows}}class on{constructor(e,t,s){this.chNr=void 0,this.outputFilter=void 0,this.mode=void 0,this.verbose=void 0,this.displayedMemory=void 0,this.nonDisplayedMemory=void 0,this.lastOutputScreen=void 0,this.currRollUpRow=void 0,this.writeScreen=void 0,this.cueStartTime=void 0,this.logger=void 0,this.chNr=e,this.outputFilter=t,this.mode=null,this.verbose=0,this.displayedMemory=new Xs(s),this.nonDisplayedMemory=new Xs(s),this.lastOutputScreen=new Xs(s),this.currRollUpRow=this.displayedMemory.rows[Ne-1],this.writeScreen=this.displayedMemory,this.mode=null,this.cueStartTime=null,this.logger=s}reset(){this.mode=null,this.displayedMemory.reset(),this.nonDisplayedMemory.reset(),this.lastOutputScreen.reset(),this.outputFilter.reset(),this.currRollUpRow=this.displayedMemory.rows[Ne-1],this.writeScreen=this.displayedMemory,this.mode=null,this.cueStartTime=null}getHandler(){return this.outputFilter}setHandler(e){this.outputFilter=e}setPAC(e){this.writeScreen.setPAC(e)}setBkgData(e){this.writeScreen.setBkgData(e)}setMode(e){e!==this.mode&&(this.mode=e,this.logger.log(2,()=>"MODE="+e),this.mode==="MODE_POP-ON"?this.writeScreen=this.nonDisplayedMemory:(this.writeScreen=this.displayedMemory,this.writeScreen.reset()),this.mode!=="MODE_ROLL-UP"&&(this.displayedMemory.nrRollUpRows=null,this.nonDisplayedMemory.nrRollUpRows=null),this.mode=e)}insertChars(e){for(let s=0;st+": "+this.writeScreen.getDisplayText(!0)),(this.mode==="MODE_PAINT-ON"||this.mode==="MODE_ROLL-UP")&&(this.logger.log(1,()=>"DISPLAYED: "+this.displayedMemory.getDisplayText(!0)),this.outputDataUpdate())}ccRCL(){this.logger.log(2,"RCL - Resume Caption Loading"),this.setMode("MODE_POP-ON")}ccBS(){this.logger.log(2,"BS - BackSpace"),this.mode!=="MODE_TEXT"&&(this.writeScreen.backSpace(),this.writeScreen===this.displayedMemory&&this.outputDataUpdate())}ccAOF(){}ccAON(){}ccDER(){this.logger.log(2,"DER- Delete to End of Row"),this.writeScreen.clearToEndOfRow(),this.outputDataUpdate()}ccRU(e){this.logger.log(2,"RU("+e+") - Roll Up"),this.writeScreen=this.displayedMemory,this.setMode("MODE_ROLL-UP"),this.writeScreen.setRollUpRows(e)}ccFON(){this.logger.log(2,"FON - Flash On"),this.writeScreen.setPen({flash:!0})}ccRDC(){this.logger.log(2,"RDC - Resume Direct Captioning"),this.setMode("MODE_PAINT-ON")}ccTR(){this.logger.log(2,"TR"),this.setMode("MODE_TEXT")}ccRTD(){this.logger.log(2,"RTD"),this.setMode("MODE_TEXT")}ccEDM(){this.logger.log(2,"EDM - Erase Displayed Memory"),this.displayedMemory.reset(),this.outputDataUpdate(!0)}ccCR(){this.logger.log(2,"CR - Carriage Return"),this.writeScreen.rollUp(),this.outputDataUpdate(!0)}ccENM(){this.logger.log(2,"ENM - Erase Non-displayed Memory"),this.nonDisplayedMemory.reset()}ccEOC(){if(this.logger.log(2,"EOC - End Of Caption"),this.mode==="MODE_POP-ON"){const e=this.displayedMemory;this.displayedMemory=this.nonDisplayedMemory,this.nonDisplayedMemory=e,this.writeScreen=this.nonDisplayedMemory,this.logger.log(1,()=>"DISP: "+this.displayedMemory.getDisplayText())}this.outputDataUpdate(!0)}ccTO(e){this.logger.log(2,"TO("+e+") - Tab Offset"),this.writeScreen.moveCursor(e)}ccMIDROW(e){const t={flash:!1};if(t.underline=e%2===1,t.italics=e>=46,t.italics)t.foreground="white";else{const s=Math.floor(e/2)-16,i=["white","green","blue","cyan","red","yellow","magenta"];t.foreground=i[s]}this.logger.log(2,"MIDROW: "+oe(t)),this.writeScreen.setPen(t)}outputDataUpdate(e=!1){const t=this.logger.time;t!==null&&this.outputFilter&&(this.cueStartTime===null&&!this.displayedMemory.isEmpty()?this.cueStartTime=t:this.displayedMemory.equals(this.lastOutputScreen)||(this.outputFilter.newCue(this.cueStartTime,t,this.lastOutputScreen),e&&this.outputFilter.dispatchCue&&this.outputFilter.dispatchCue(),this.cueStartTime=this.displayedMemory.isEmpty()?null:t),this.lastOutputScreen.copy(this.displayedMemory))}cueSplitAtTime(e){this.outputFilter&&(this.displayedMemory.isEmpty()||(this.outputFilter.newCue&&this.outputFilter.newCue(this.cueStartTime,e,this.displayedMemory),this.cueStartTime=e))}}class ln{constructor(e,t,s){this.channels=void 0,this.currentChannel=0,this.cmdHistory=yd(),this.logger=void 0;const i=this.logger=new fd;this.channels=[null,new on(e,t,i),new on(e+1,s,i)]}getHandler(e){return this.channels[e].getHandler()}setHandler(e,t){this.channels[e].setHandler(t)}addData(e,t){this.logger.time=e;for(let s=0;s"["+ut([t[s],t[s+1]])+"] -> ("+ut([i,r])+")");const c=this.cmdHistory;if(i>=16&&i<=31){if(pd(i,r,c)){ns(null,null,c),this.logger.log(3,()=>"Repeated command ("+ut([i,r])+") is dropped");continue}ns(i,r,this.cmdHistory),n=this.parseCmd(i,r),n||(n=this.parseMidrow(i,r)),n||(n=this.parsePAC(i,r)),n||(n=this.parseBackgroundAttributes(i,r))}else ns(null,null,c);if(!n&&(o=this.parseChars(i,r),o)){const h=this.currentChannel;h&&h>0?this.channels[h].insertChars(o):this.logger.log(2,"No channel found yet. TEXT-MODE?")}!n&&!o&&this.logger.log(2,()=>"Couldn't parse cleaned data "+ut([i,r])+" orig: "+ut([t[s],t[s+1]]))}}parseCmd(e,t){const s=(e===20||e===28||e===21||e===29)&&t>=32&&t<=47,i=(e===23||e===31)&&t>=33&&t<=35;if(!(s||i))return!1;const r=e===20||e===21||e===23?1:2,n=this.channels[r];return e===20||e===21||e===28||e===29?t===32?n.ccRCL():t===33?n.ccBS():t===34?n.ccAOF():t===35?n.ccAON():t===36?n.ccDER():t===37?n.ccRU(2):t===38?n.ccRU(3):t===39?n.ccRU(4):t===40?n.ccFON():t===41?n.ccRDC():t===42?n.ccTR():t===43?n.ccRTD():t===44?n.ccEDM():t===45?n.ccCR():t===46?n.ccENM():t===47&&n.ccEOC():n.ccTO(t-32),this.currentChannel=r,!0}parseMidrow(e,t){let s=0;if((e===17||e===25)&&t>=32&&t<=47){if(e===17?s=1:s=2,s!==this.currentChannel)return this.logger.log(0,"Mismatch channel in midrow parsing"),!1;const i=this.channels[s];return i?(i.ccMIDROW(t),this.logger.log(3,()=>"MIDROW ("+ut([e,t])+")"),!0):!1}return!1}parsePAC(e,t){let s;const i=(e>=17&&e<=23||e>=25&&e<=31)&&t>=64&&t<=127,r=(e===16||e===24)&&t>=64&&t<=95;if(!(i||r))return!1;const n=e<=23?1:2;t>=64&&t<=95?s=n===1?ld[e]:hd[e]:s=n===1?cd[e]:dd[e];const o=this.channels[n];return o?(o.setPAC(this.interpretPAC(s,t)),this.currentChannel=n,!0):!1}interpretPAC(e,t){let s;const i={color:null,italics:!1,indent:null,underline:!1,row:e};return t>95?s=t-96:s=t-64,i.underline=(s&1)===1,s<=13?i.color=["white","green","blue","cyan","red","yellow","magenta","white"][Math.floor(s/2)]:s<=15?(i.italics=!0,i.color="white"):i.indent=Math.floor((s-16)/2)*4,i}parseChars(e,t){let s,i=null,r=null;if(e>=25?(s=2,r=e-8):(s=1,r=e),r>=17&&r<=19){let n;r===17?n=t+80:r===18?n=t+112:n=t+144,this.logger.log(2,()=>"Special char '"+Ga(n)+"' in channel "+s),i=[n]}else e>=32&&e<=127&&(i=t===0?[e]:[e,t]);return i&&this.logger.log(3,()=>"Char codes = "+ut(i).join(",")),i}parseBackgroundAttributes(e,t){const s=(e===16||e===24)&&t>=32&&t<=47,i=(e===23||e===31)&&t>=45&&t<=47;if(!(s||i))return!1;let r;const n={};e===16||e===24?(r=Math.floor((t-32)/2),n.background=ud[r],t%2===1&&(n.background=n.background+"_semi")):t===45?n.background="transparent":(n.foreground="black",t===47&&(n.underline=!0));const o=e<=23?1:2;return this.channels[o].setBkgData(n),!0}reset(){for(let e=0;e100)throw new Error("Position must be between 0 and 100.");D=I,this.hasBeenReset=!0}})),Object.defineProperty(h,"positionAlign",r({},d,{get:function(){return A},set:function(I){const P=i(I);if(!P)throw new SyntaxError("An invalid or illegal string was specified.");A=P,this.hasBeenReset=!0}})),Object.defineProperty(h,"size",r({},d,{get:function(){return _},set:function(I){if(I<0||I>100)throw new Error("Size must be between 0 and 100.");_=I,this.hasBeenReset=!0}})),Object.defineProperty(h,"align",r({},d,{get:function(){return b},set:function(I){const P=i(I);if(!P)throw new SyntaxError("An invalid or illegal string was specified.");b=P,this.hasBeenReset=!0}})),h.displayState=void 0}return n.prototype.getCueAsHTML=function(){return self.WebVTT.convertCueToDOMTree(self,this.text)},n}();class Ed{decode(e,t){if(!e)return"";if(typeof e!="string")throw new Error("Error - expected string data.");return decodeURIComponent(encodeURIComponent(e))}}function Va(a){function e(s,i,r,n){return(s|0)*3600+(i|0)*60+(r|0)+parseFloat(n||0)}const t=a.match(/^(?:(\d+):)?(\d{2}):(\d{2})(\.\d+)?/);return t?parseFloat(t[2])>59?e(t[2],t[3],0,t[4]):e(t[1],t[2],t[3],t[4]):null}class Td{constructor(){this.values=Object.create(null)}set(e,t){!this.get(e)&&t!==""&&(this.values[e]=t)}get(e,t,s){return s?this.has(e)?this.values[e]:t[s]:this.has(e)?this.values[e]:t}has(e){return e in this.values}alt(e,t,s){for(let i=0;i=0&&s<=100)return this.set(e,s),!0}return!1}}function Ha(a,e,t,s){const i=s?a.split(s):[a];for(const r in i){if(typeof i[r]!="string")continue;const n=i[r].split(t);if(n.length!==2)continue;const o=n[0],c=n[1];e(o,c)}}const Si=new qi(0,0,""),as=Si.align==="middle"?"middle":"center";function Sd(a,e,t){const s=a;function i(){const o=Va(a);if(o===null)throw new Error("Malformed timestamp: "+s);return a=a.replace(/^[^\sa-zA-Z-]+/,""),o}function r(o,c){const l=new Td;Ha(o,function(u,f){let g;switch(u){case"region":for(let y=t.length-1;y>=0;y--)if(t[y].id===f){l.set(u,t[y].region);break}break;case"vertical":l.alt(u,f,["rl","lr"]);break;case"line":g=f.split(","),l.integer(u,g[0]),l.percent(u,g[0])&&l.set("snapToLines",!1),l.alt(u,g[0],["auto"]),g.length===2&&l.alt("lineAlign",g[1],["start",as,"end"]);break;case"position":g=f.split(","),l.percent(u,g[0]),g.length===2&&l.alt("positionAlign",g[1],["start",as,"end","line-left","line-right","auto"]);break;case"size":l.percent(u,f);break;case"align":l.alt(u,f,["start",as,"end","left","right"]);break}},/:/,/\s/),c.region=l.get("region",null),c.vertical=l.get("vertical","");let h=l.get("line","auto");h==="auto"&&Si.line===-1&&(h=-1),c.line=h,c.lineAlign=l.get("lineAlign","start"),c.snapToLines=l.get("snapToLines",!0),c.size=l.get("size",100),c.align=l.get("align",as);let d=l.get("position","auto");d==="auto"&&Si.position===50&&(d=c.align==="start"||c.align==="left"?0:c.align==="end"||c.align==="right"?100:50),c.position=d}function n(){a=a.replace(/^\s+/,"")}if(n(),e.startTime=i(),n(),a.slice(0,3)!=="-->")throw new Error("Malformed time stamp (time stamps must be separated by '-->'): "+s);a=a.slice(3),n(),e.endTime=i(),n(),r(a,e)}function Ya(a){return a.replace(//gi,` +`)}class vd{constructor(){this.state="INITIAL",this.buffer="",this.decoder=new Ed,this.regionList=[],this.cue=null,this.oncue=void 0,this.onparsingerror=void 0,this.onflush=void 0}parse(e){const t=this;e&&(t.buffer+=t.decoder.decode(e,{stream:!0}));function s(){let r=t.buffer,n=0;for(r=Ya(r);n")===-1){t.cue.id=r;continue}case"CUE":if(!t.cue){t.state="BADCUE";continue}try{yu(r,t.cue,t.regionList)}catch{t.cue=null,t.state="BADCUE";continue}t.state="CUETEXT";continue;case"CUETEXT":{const o=r.indexOf("-->")!==-1;if(!r||o&&(n=!0)){t.oncue&&t.cue&&t.oncue(t.cue),t.cue=null,t.state="ID";continue}if(t.cue===null)continue;t.cue.text&&(t.cue.text+=` +`&&++n,t.buffer=r.slice(n),o}function i(r){Ha(r,function(n,o){},/:/)}try{let r="";if(t.state==="INITIAL"){if(!/\r\n|\n/.test(t.buffer))return this;r=s();const o=r.match(/^()?WEBVTT([ \t].*)?$/);if(!(o!=null&&o[0]))throw new Error("Malformed WebVTT signature.");t.state="HEADER"}let n=!1;for(;t.buffer;){if(!/\r\n|\n/.test(t.buffer))return this;switch(n?n=!1:r=s(),t.state){case"HEADER":/:/.test(r)?i(r):r||(t.state="ID");continue;case"NOTE":r||(t.state="ID");continue;case"ID":if(/^NOTE($|[ \t])/.test(r)){t.state="NOTE";break}if(!r)continue;if(t.cue=new qi(0,0,""),t.state="CUE",r.indexOf("-->")===-1){t.cue.id=r;continue}case"CUE":if(!t.cue){t.state="BADCUE";continue}try{Sd(r,t.cue,t.regionList)}catch{t.cue=null,t.state="BADCUE";continue}t.state="CUETEXT";continue;case"CUETEXT":{const o=r.indexOf("-->")!==-1;if(!r||o&&(n=!0)){t.oncue&&t.cue&&t.oncue(t.cue),t.cue=null,t.state="ID";continue}if(t.cue===null)continue;t.cue.text&&(t.cue.text+=` `),t.cue.text+=r}continue;case"BADCUE":r||(t.state="ID")}}}catch{t.state==="CUETEXT"&&t.cue&&t.oncue&&t.oncue(t.cue),t.cue=null,t.state=t.state==="INITIAL"?"BADWEBVTT":"BADCUE"}return this}flush(){const e=this;try{if((e.cue||e.state==="HEADER")&&(e.buffer+=` -`,e.parse()),e.state==="INITIAL"||e.state==="BADWEBVTT")throw new Error("Malformed WebVTT signature.")}catch(t){e.onparsingerror&&e.onparsingerror(t)}return e.onflush&&e.onflush(),this}}const Tu=/\r\n|\n\r|\n|\r/g,Xs=function(e,t,s=0){return e.slice(s,s+t.length)===t},Su=function(e){let t=parseInt(e.slice(-3));const s=parseInt(e.slice(-6,-4)),i=parseInt(e.slice(-9,-7)),r=e.length>9?parseInt(e.substring(0,e.indexOf(":"))):0;if(!N(t)||!N(s)||!N(i)||!N(r))throw Error(`Malformed X-TIMESTAMP-MAP: Local:${e}`);return t+=1e3*s,t+=60*1e3*i,t+=60*60*1e3*r,t};function Wi(a,e,t){return Ut(a.toString())+Ut(e.toString())+Ut(t)}const vu=function(e,t,s){let i=e[t],r=e[i.prevCC];if(!r||!r.new&&i.new){e.ccOffset=e.presentationOffset=i.start,i.new=!1;return}for(;(n=r)!=null&&n.new;){var n;e.ccOffset+=i.start-r.start,i.new=!1,i=r,r=e[i.prevCC]}e.presentationOffset=s};function xu(a,e,t,s,i,r,n){const o=new Eu,c=_e(new Uint8Array(a)).trim().replace(Tu,` +`,e.parse()),e.state==="INITIAL"||e.state==="BADWEBVTT")throw new Error("Malformed WebVTT signature.")}catch(t){e.onparsingerror&&e.onparsingerror(t)}return e.onflush&&e.onflush(),this}}const xd=/\r\n|\n\r|\n|\r/g,zs=function(e,t,s=0){return e.slice(s,s+t.length)===t},Ad=function(e){let t=parseInt(e.slice(-3));const s=parseInt(e.slice(-6,-4)),i=parseInt(e.slice(-9,-7)),r=e.length>9?parseInt(e.substring(0,e.indexOf(":"))):0;if(!B(t)||!B(s)||!B(i)||!B(r))throw Error(`Malformed X-TIMESTAMP-MAP: Local:${e}`);return t+=1e3*s,t+=60*1e3*i,t+=60*60*1e3*r,t};function ji(a,e,t){return $t(a.toString())+$t(e.toString())+$t(t)}const Id=function(e,t,s){let i=e[t],r=e[i.prevCC];if(!r||!r.new&&i.new){e.ccOffset=e.presentationOffset=i.start,i.new=!1;return}for(;(n=r)!=null&&n.new;){var n;e.ccOffset+=i.start-r.start,i.new=!1,i=r,r=e[i.prevCC]}e.presentationOffset=s};function Ld(a,e,t,s,i,r,n){const o=new vd,c=De(new Uint8Array(a)).trim().replace(xd,` `).split(` -`),l=[],h=e?_c(e.baseTime,e.timescale):0;let u="00:00.000",d=0,f=0,g,y=!0;o.oncue=function(p){const E=t[s];let T=t.ccOffset;const S=(d-h)/9e4;if(E!=null&&E.new&&(f!==void 0?T=t.ccOffset=E.start:vu(t,s,S)),S){if(!e){g=new Error("Missing initPTS for VTT MPEGTS");return}T=S-t.presentationOffset}const v=p.endTime-p.startTime,x=Re((p.startTime+T-f)*9e4,i*9e4)/9e4;p.startTime=Math.max(x,0),p.endTime=Math.max(x+v,0);const D=p.text.trim();p.text=decodeURIComponent(encodeURIComponent(D)),p.id||(p.id=Wi(p.startTime,p.endTime,D)),p.endTime>0&&l.push(p)},o.onparsingerror=function(p){g=p},o.onflush=function(){if(g){n(g);return}r(l)},c.forEach(p=>{if(y)if(Xs(p,"X-TIMESTAMP-MAP=")){y=!1,p.slice(16).split(",").forEach(E=>{Xs(E,"LOCAL:")?u=E.slice(6):Xs(E,"MPEGTS:")&&(d=parseInt(E.slice(7)))});try{f=Su(u)/1e3}catch(E){g=E}return}else p===""&&(y=!1);o.parse(p+` -`)}),o.flush()}const zs="stpp.ttml.im1t",Va=/^(\d{2,}):(\d{2}):(\d{2}):(\d{2})\.?(\d+)?$/,Ha=/^(\d*(?:\.\d*)?)(h|m|s|ms|f|t)$/,Au={left:"start",center:"center",right:"end",start:"start",end:"end"};function on(a,e,t,s){const i=Z(new Uint8Array(a),["mdat"]);if(i.length===0){s(new Error("Could not parse IMSC1 mdat"));return}const r=i.map(o=>_e(o)),n=bc(e.baseTime,1,e.timescale);try{r.forEach(o=>t(Iu(o,n)))}catch(o){s(o)}}function Iu(a,e){const i=new DOMParser().parseFromString(a,"text/xml").getElementsByTagName("tt")[0];if(!i)throw new Error("Invalid ttml");const r={frameRate:30,subFrameRate:1,frameRateMultiplier:0,tickRate:0},n=Object.keys(r).reduce((u,d)=>(u[d]=i.getAttribute(`ttp:${d}`)||r[d],u),{}),o=i.getAttribute("xml:space")!=="preserve",c=ln(Qs(i,"styling","style")),l=ln(Qs(i,"layout","region")),h=Qs(i,"body","[begin]");return[].map.call(h,u=>{const d=Ya(u,o);if(!d||!u.hasAttribute("begin"))return null;const f=Js(u.getAttribute("begin"),n),g=Js(u.getAttribute("dur"),n);let y=Js(u.getAttribute("end"),n);if(f===null)throw cn(u);if(y===null){if(g===null)throw cn(u);y=f+g}const p=new Yi(f-e,y-e,d);p.id=Wi(p.startTime,p.endTime,p.text);const E=l[u.getAttribute("region")],T=c[u.getAttribute("style")],S=Lu(E,T,c),{textAlign:v}=S;if(v){const x=Au[v];x&&(p.lineAlign=x),p.align=v}return ne(p,S),p}).filter(u=>u!==null)}function Qs(a,e,t){const s=a.getElementsByTagName(e)[0];return s?[].slice.call(s.querySelectorAll(t)):[]}function ln(a){return a.reduce((e,t)=>{const s=t.getAttribute("xml:id");return s&&(e[s]=t),e},{})}function Ya(a,e){return[].slice.call(a.childNodes).reduce((t,s,i)=>{var r;return s.nodeName==="br"&&i?t+` -`:(r=s.childNodes)!=null&&r.length?Ya(s,e):e?t+s.textContent.trim().replace(/\s+/g," "):t+s.textContent},"")}function Lu(a,e,t){const s="http://www.w3.org/ns/ttml#styling";let i=null;const r=["displayAlign","textAlign","color","backgroundColor","fontSize","fontFamily"],n=a!=null&&a.hasAttribute("style")?a.getAttribute("style"):null;return n&&t.hasOwnProperty(n)&&(i=t[n]),r.reduce((o,c)=>{const l=Zs(e,s,c)||Zs(a,s,c)||Zs(i,s,c);return l&&(o[c]=l),o},{})}function Zs(a,e,t){return a&&a.hasAttributeNS(e,t)?a.getAttributeNS(e,t):null}function cn(a){return new Error(`Could not parse ttml timestamp ${a}`)}function Js(a,e){if(!a)return null;let t=$a(a);return t===null&&(Va.test(a)?t=Ru(a,e):Ha.test(a)&&(t=bu(a,e))),t}function Ru(a,e){const t=Va.exec(a),s=(t[4]|0)+(t[5]|0)/e.subFrameRate;return(t[1]|0)*3600+(t[2]|0)*60+(t[3]|0)+s/e.frameRate}function bu(a,e){const t=Ha.exec(a),s=Number(t[1]);switch(t[2]){case"h":return s*3600;case"m":return s*60;case"ms":return s*1e3;case"f":return s/e.frameRate;case"t":return s/e.tickRate}return s}class as{constructor(e,t){this.timelineController=void 0,this.cueRanges=[],this.trackName=void 0,this.startTime=null,this.endTime=null,this.screen=null,this.timelineController=e,this.trackName=t}dispatchCue(){this.startTime!==null&&(this.timelineController.addCues(this.trackName,this.startTime,this.endTime,this.screen,this.cueRanges),this.startTime=null)}newCue(e,t,s){(this.startTime===null||this.startTime>e)&&(this.startTime=e),this.endTime=t,this.screen=s,this.timelineController.createCaptionsTrack(this.trackName)}reset(){this.cueRanges=[],this.startTime=null}}class _u{constructor(e){this.hls=void 0,this.media=null,this.config=void 0,this.enabled=!0,this.Cues=void 0,this.textTracks=[],this.tracks=[],this.initPTS=[],this.unparsedVttFrags=[],this.captionsTracks={},this.nonNativeCaptionsTracks={},this.cea608Parser1=void 0,this.cea608Parser2=void 0,this.lastCc=-1,this.lastSn=-1,this.lastPartIndex=-1,this.prevCC=-1,this.vttCCs=un(),this.captionsProperties=void 0,this.hls=e,this.config=e.config,this.Cues=e.config.cueHandler,this.captionsProperties={textTrack1:{label:this.config.captionsTextTrack1Label,languageCode:this.config.captionsTextTrack1LanguageCode},textTrack2:{label:this.config.captionsTextTrack2Label,languageCode:this.config.captionsTextTrack2LanguageCode},textTrack3:{label:this.config.captionsTextTrack3Label,languageCode:this.config.captionsTextTrack3LanguageCode},textTrack4:{label:this.config.captionsTextTrack4Label,languageCode:this.config.captionsTextTrack4LanguageCode}},e.on(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(m.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),e.on(m.FRAG_LOADING,this.onFragLoading,this),e.on(m.FRAG_LOADED,this.onFragLoaded,this),e.on(m.FRAG_PARSING_USERDATA,this.onFragParsingUserdata,this),e.on(m.FRAG_DECRYPTED,this.onFragDecrypted,this),e.on(m.INIT_PTS_FOUND,this.onInitPtsFound,this),e.on(m.SUBTITLE_TRACKS_CLEARED,this.onSubtitleTracksCleared,this),e.on(m.BUFFER_FLUSHING,this.onBufferFlushing,this)}destroy(){const{hls:e}=this;e.off(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(m.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),e.off(m.FRAG_LOADING,this.onFragLoading,this),e.off(m.FRAG_LOADED,this.onFragLoaded,this),e.off(m.FRAG_PARSING_USERDATA,this.onFragParsingUserdata,this),e.off(m.FRAG_DECRYPTED,this.onFragDecrypted,this),e.off(m.INIT_PTS_FOUND,this.onInitPtsFound,this),e.off(m.SUBTITLE_TRACKS_CLEARED,this.onSubtitleTracksCleared,this),e.off(m.BUFFER_FLUSHING,this.onBufferFlushing,this),this.hls=this.config=this.media=null,this.cea608Parser1=this.cea608Parser2=void 0}initCea608Parsers(){const e=new as(this,"textTrack1"),t=new as(this,"textTrack2"),s=new as(this,"textTrack3"),i=new as(this,"textTrack4");this.cea608Parser1=new an(1,e,t),this.cea608Parser2=new an(3,s,i)}addCues(e,t,s,i,r){let n=!1;for(let o=r.length;o--;){const c=r[o],l=Du(c[0],c[1],t,s);if(l>=0&&(c[0]=Math.min(c[0],t),c[1]=Math.max(c[1],s),n=!0,l/(s-t)>.5))return}if(n||r.push([t,s]),this.config.renderTextTracksNatively){const o=this.captionsTracks[e];this.Cues.newCue(o,t,s,i)}else{const o=this.Cues.newCue(null,t,s,i);this.hls.trigger(m.CUES_PARSED,{type:"captions",cues:o,track:e})}}onInitPtsFound(e,{frag:t,id:s,initPTS:i,timescale:r,trackId:n}){const{unparsedVttFrags:o}=this;s===U.MAIN&&(this.initPTS[t.cc]={baseTime:i,timescale:r,trackId:n}),o.length&&(this.unparsedVttFrags=[],o.forEach(c=>{this.initPTS[c.frag.cc]?this.onFragLoaded(m.FRAG_LOADED,c):this.hls.trigger(m.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:c.frag,error:new Error("Subtitle discontinuity domain does not match main")})}))}getExistingTrack(e,t){const{media:s}=this;if(s)for(let i=0;i{It(i[r]),delete i[r]}),this.nonNativeCaptionsTracks={}}onManifestLoading(){this.lastCc=-1,this.lastSn=-1,this.lastPartIndex=-1,this.prevCC=-1,this.vttCCs=un(),this._cleanTracks(),this.tracks=[],this.captionsTracks={},this.nonNativeCaptionsTracks={},this.textTracks=[],this.unparsedVttFrags=[],this.initPTS=[],this.cea608Parser1&&this.cea608Parser2&&(this.cea608Parser1.reset(),this.cea608Parser2.reset())}_cleanTracks(){const{media:e}=this;if(!e)return;const t=e.textTracks;if(t)for(let s=0;sr.textCodec===zs);if(this.config.enableWebVTT||i&&this.config.enableIMSC1){if(xa(this.tracks,s)){this.tracks=s;return}if(this.textTracks=[],this.tracks=s,this.config.renderTextTracksNatively){const n=this.media,o=n?ms(n.textTracks):null;if(this.tracks.forEach((c,l)=>{let h;if(o){let u=null;for(let d=0;dl!==null).map(l=>l.label);c.length&&this.hls.logger.warn(`Media element contains unused subtitle tracks: ${c.join(", ")}. Replace media element for each source to clear TextTracks and captions menu.`)}}else if(this.tracks.length){const n=this.tracks.map(o=>({label:o.name,kind:o.type.toLowerCase(),default:o.default,subtitleTrack:o}));this.hls.trigger(m.NON_NATIVE_TEXT_TRACKS_FOUND,{tracks:n})}}}onManifestLoaded(e,t){this.config.enableCEA708Captions&&t.captions&&t.captions.forEach(s=>{const i=/(?:CC|SERVICE)([1-4])/.exec(s.instreamId);if(!i)return;const r=`textTrack${i[1]}`,n=this.captionsProperties[r];n&&(n.label=s.name,s.lang&&(n.languageCode=s.lang),n.media=s)})}closedCaptionsForLevel(e){const t=this.hls.levels[e.level];return t==null?void 0:t.attrs["CLOSED-CAPTIONS"]}onFragLoading(e,t){if(this.enabled&&t.frag.type===U.MAIN){var s,i;const{cea608Parser1:r,cea608Parser2:n,lastSn:o}=this,{cc:c,sn:l}=t.frag,h=(s=(i=t.part)==null?void 0:i.index)!=null?s:-1;r&&n&&(l!==o+1||l===o&&h!==this.lastPartIndex+1||c!==this.lastCc)&&(r.reset(),n.reset()),this.lastCc=c,this.lastSn=l,this.lastPartIndex=h}}onFragLoaded(e,t){const{frag:s,payload:i}=t;if(s.type===U.SUBTITLE)if(i.byteLength){const r=s.decryptdata,n="stats"in t;if(r==null||!r.encrypted||n){const o=this.tracks[s.level],c=this.vttCCs;c[s.cc]||(c[s.cc]={start:s.start,prevCC:this.prevCC,new:!0},this.prevCC=s.cc),o&&o.textCodec===zs?this._parseIMSC1(s,i):this._parseVTTs(t)}}else this.hls.trigger(m.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:s,error:new Error("Empty subtitle payload")})}_parseIMSC1(e,t){const s=this.hls;on(t,this.initPTS[e.cc],i=>{this._appendCues(i,e.level),s.trigger(m.SUBTITLE_FRAG_PROCESSED,{success:!0,frag:e})},i=>{s.logger.log(`Failed to parse IMSC1: ${i}`),s.trigger(m.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:e,error:i})})}_parseVTTs(e){var t;const{frag:s,payload:i}=e,{initPTS:r,unparsedVttFrags:n}=this,o=r.length-1;if(!r[s.cc]&&o===-1){n.push(e);return}const c=this.hls,l=(t=s.initSegment)!=null&&t.data?ke(s.initSegment.data,new Uint8Array(i)).buffer:i;xu(l,this.initPTS[s.cc],this.vttCCs,s.cc,s.start,h=>{this._appendCues(h,s.level),c.trigger(m.SUBTITLE_FRAG_PROCESSED,{success:!0,frag:s})},h=>{const u=h.message==="Missing initPTS for VTT MPEGTS";u?n.push(e):this._fallbackToIMSC1(s,i),c.logger.log(`Failed to parse VTT cue: ${h}`),!(u&&o>s.cc)&&c.trigger(m.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:s,error:h})})}_fallbackToIMSC1(e,t){const s=this.tracks[e.level];s.textCodec||on(t,this.initPTS[e.cc],()=>{s.textCodec=zs,this._parseIMSC1(e,t)},()=>{s.textCodec="wvtt"})}_appendCues(e,t){const s=this.hls;if(this.config.renderTextTracksNatively){const i=this.textTracks[t];if(!i||i.mode==="disabled")return;e.forEach(r=>Ma(i,r))}else{const i=this.tracks[t];if(!i)return;const r=i.default?"default":"subtitles"+t;s.trigger(m.CUES_PARSED,{type:"subtitles",cues:e,track:r})}}onFragDecrypted(e,t){const{frag:s}=t;s.type===U.SUBTITLE&&this.onFragLoaded(m.FRAG_LOADED,t)}onSubtitleTracksCleared(){this.tracks=[],this.captionsTracks={}}onFragParsingUserdata(e,t){if(!this.enabled||!this.config.enableCEA708Captions)return;const{frag:s,samples:i}=t;if(!(s.type===U.MAIN&&this.closedCaptionsForLevel(s)==="NONE"))for(let r=0;rEi(o[c],t,s))}if(this.config.renderTextTracksNatively&&t===0&&i!==void 0){const{textTracks:o}=this;Object.keys(o).forEach(c=>Ei(o[c],t,i))}}}extractCea608Data(e){const t=[[],[]],s=e[0]&31;let i=2;for(let r=0;r=16?c--:c++;const f=Ka(l.trim()),g=Wi(e,t,f);a!=null&&(u=a.cues)!=null&&u.getCueById(g)||(n=new h(e,t,f),n.id=g,n.line=d+1,n.align="left",n.position=10+Math.min(80,Math.floor(c*8/32)*10),i.push(n))}return a&&i.length&&(i.sort((d,f)=>d.line==="auto"||f.line==="auto"?0:d.line>8&&f.line>8?f.line-d.line:d.line-f.line),i.forEach(d=>Ma(a,d))),i}};function ku(){if(self.fetch&&self.AbortController&&self.ReadableStream&&self.Request)try{return new self.ReadableStream({}),!0}catch{}return!1}const wu=/(\d+)-(\d+)\/(\d+)/;class dn{constructor(e){this.fetchSetup=void 0,this.requestTimeout=void 0,this.request=null,this.response=null,this.controller=void 0,this.context=null,this.config=null,this.callbacks=null,this.stats=void 0,this.loader=null,this.fetchSetup=e.fetchSetup||Nu,this.controller=new self.AbortController,this.stats=new Ii}destroy(){this.loader=this.callbacks=this.context=this.config=this.request=null,this.abortInternal(),this.response=null,this.fetchSetup=this.controller=this.stats=null}abortInternal(){this.controller&&!this.stats.loading.end&&(this.stats.aborted=!0,this.controller.abort())}abort(){var e;this.abortInternal(),(e=this.callbacks)!=null&&e.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.response)}load(e,t,s){const i=this.stats;if(i.loading.start)throw new Error("Loader can only be used once.");i.loading.start=self.performance.now();const r=Ou(e,this.controller.signal),n=e.responseType==="arraybuffer",o=n?"byteLength":"length",{maxTimeToFirstByteMs:c,maxLoadTimeMs:l}=t.loadPolicy;this.context=e,this.config=t,this.callbacks=s,this.request=this.fetchSetup(e,r),self.clearTimeout(this.requestTimeout),t.timeout=c&&N(c)?c:l,this.requestTimeout=self.setTimeout(()=>{this.callbacks&&(this.abortInternal(),this.callbacks.onTimeout(i,e,this.response))},t.timeout),(Yt(this.request)?this.request.then(self.fetch):self.fetch(this.request)).then(u=>{var d;this.response=this.loader=u;const f=Math.max(self.performance.now(),i.loading.start);if(self.clearTimeout(this.requestTimeout),t.timeout=l,this.requestTimeout=self.setTimeout(()=>{this.callbacks&&(this.abortInternal(),this.callbacks.onTimeout(i,e,this.response))},l-(f-i.loading.start)),!u.ok){const{status:y,statusText:p}=u;throw new Bu(p||"fetch, bad network response",y,u)}i.loading.first=f,i.total=Mu(u.headers)||i.total;const g=(d=this.callbacks)==null?void 0:d.onProgress;return g&&N(t.highWaterMark)?this.loadProgressively(u,i,e,t.highWaterMark,g):n?u.arrayBuffer():e.responseType==="json"?u.json():u.text()}).then(u=>{var d,f;const g=this.response;if(!g)throw new Error("loader destroyed");self.clearTimeout(this.requestTimeout),i.loading.end=Math.max(self.performance.now(),i.loading.first);const y=u[o];y&&(i.loaded=i.total=y);const p={url:g.url,data:u,code:g.status},E=(d=this.callbacks)==null?void 0:d.onProgress;E&&!N(t.highWaterMark)&&E(i,e,u,g),(f=this.callbacks)==null||f.onSuccess(p,i,e,g)}).catch(u=>{var d;if(self.clearTimeout(this.requestTimeout),i.aborted)return;const f=u&&u.code||0,g=u?u.message:null;(d=this.callbacks)==null||d.onError({code:f,text:g},e,u?u.details:null,i)})}getCacheAge(){let e=null;if(this.response){const t=this.response.headers.get("age");e=t?parseFloat(t):null}return e}getResponseHeader(e){return this.response?this.response.headers.get(e):null}loadProgressively(e,t,s,i=0,r){const n=new sa,o=e.body.getReader(),c=()=>o.read().then(l=>{if(l.done)return n.dataLength&&r(t,s,n.flush().buffer,e),Promise.resolve(new ArrayBuffer(0));const h=l.value,u=h.length;return t.loaded+=u,u=i&&r(t,s,n.flush().buffer,e)):r(t,s,h.buffer,e),c()}).catch(()=>Promise.reject());return c()}}function Ou(a,e){const t={method:"GET",mode:"cors",credentials:"same-origin",signal:e,headers:new self.Headers(ne({},a.headers))};return a.rangeEnd&&t.headers.set("Range","bytes="+a.rangeStart+"-"+String(a.rangeEnd-1)),t}function Fu(a){const e=wu.exec(a);if(e)return parseInt(e[2])-parseInt(e[1])+1}function Mu(a){const e=a.get("Content-Range");if(e){const s=Fu(e);if(N(s))return s}const t=a.get("Content-Length");if(t)return parseInt(t)}function Nu(a,e){return new self.Request(a.url,e)}class Bu extends Error{constructor(e,t,s){super(e),this.code=void 0,this.details=void 0,this.code=t,this.details=s}}const Uu=/^age:\s*[\d.]+\s*$/im;class qa{constructor(e){this.xhrSetup=void 0,this.requestTimeout=void 0,this.retryTimeout=void 0,this.retryDelay=void 0,this.config=null,this.callbacks=null,this.context=null,this.loader=null,this.stats=void 0,this.xhrSetup=e&&e.xhrSetup||null,this.stats=new Ii,this.retryDelay=0}destroy(){this.callbacks=null,this.abortInternal(),this.loader=null,this.config=null,this.context=null,this.xhrSetup=null}abortInternal(){const e=this.loader;self.clearTimeout(this.requestTimeout),self.clearTimeout(this.retryTimeout),e&&(e.onreadystatechange=null,e.onprogress=null,e.readyState!==4&&(this.stats.aborted=!0,e.abort()))}abort(){var e;this.abortInternal(),(e=this.callbacks)!=null&&e.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.loader)}load(e,t,s){if(this.stats.loading.start)throw new Error("Loader can only be used once.");this.stats.loading.start=self.performance.now(),this.context=e,this.config=t,this.callbacks=s,this.loadInternal()}loadInternal(){const{config:e,context:t}=this;if(!e||!t)return;const s=this.loader=new self.XMLHttpRequest,i=this.stats;i.loading.first=0,i.loaded=0,i.aborted=!1;const r=this.xhrSetup;r?Promise.resolve().then(()=>{if(!(this.loader!==s||this.stats.aborted))return r(s,t.url)}).catch(n=>{if(!(this.loader!==s||this.stats.aborted))return s.open("GET",t.url,!0),r(s,t.url)}).then(()=>{this.loader!==s||this.stats.aborted||this.openAndSendXhr(s,t,e)}).catch(n=>{var o;(o=this.callbacks)==null||o.onError({code:s.status,text:n.message},t,s,i)}):this.openAndSendXhr(s,t,e)}openAndSendXhr(e,t,s){e.readyState||e.open("GET",t.url,!0);const i=t.headers,{maxTimeToFirstByteMs:r,maxLoadTimeMs:n}=s.loadPolicy;if(i)for(const o in i)e.setRequestHeader(o,i[o]);t.rangeEnd&&e.setRequestHeader("Range","bytes="+t.rangeStart+"-"+(t.rangeEnd-1)),e.onreadystatechange=this.readystatechange.bind(this),e.onprogress=this.loadprogress.bind(this),e.responseType=t.responseType,self.clearTimeout(this.requestTimeout),s.timeout=r&&N(r)?r:n,this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),s.timeout),e.send()}readystatechange(){const{context:e,loader:t,stats:s}=this;if(!e||!t)return;const i=t.readyState,r=this.config;if(!s.aborted&&i>=2&&(s.loading.first===0&&(s.loading.first=Math.max(self.performance.now(),s.loading.start),r.timeout!==r.loadPolicy.maxLoadTimeMs&&(self.clearTimeout(this.requestTimeout),r.timeout=r.loadPolicy.maxLoadTimeMs,this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),r.loadPolicy.maxLoadTimeMs-(s.loading.first-s.loading.start)))),i===4)){self.clearTimeout(this.requestTimeout),t.onreadystatechange=null,t.onprogress=null;const l=t.status,h=t.responseType==="text"?t.responseText:null;if(l>=200&&l<300){const g=h??t.response;if(g!=null){var n,o;s.loading.end=Math.max(self.performance.now(),s.loading.first);const y=t.responseType==="arraybuffer"?g.byteLength:g.length;s.loaded=s.total=y,s.bwEstimate=s.total*8e3/(s.loading.end-s.loading.first);const p=(n=this.callbacks)==null?void 0:n.onProgress;p&&p(s,e,g,t);const E={url:t.responseURL,data:g,code:l};(o=this.callbacks)==null||o.onSuccess(E,s,e,t);return}}const u=r.loadPolicy.errorRetry,d=s.retry,f={url:e.url,data:void 0,code:l};if(xs(u,d,!1,f))this.retry(u);else{var c;re.error(`${l} while loading ${e.url}`),(c=this.callbacks)==null||c.onError({code:l,text:t.statusText},e,t,s)}}}loadtimeout(){if(!this.config)return;const e=this.config.loadPolicy.timeoutRetry,t=this.stats.retry;if(xs(e,t,!0))this.retry(e);else{var s;re.warn(`timeout while loading ${(s=this.context)==null?void 0:s.url}`);const i=this.callbacks;i&&(this.abortInternal(),i.onTimeout(this.stats,this.context,this.loader))}}retry(e){const{context:t,stats:s}=this;this.retryDelay=_i(e,s.retry),s.retry++,re.warn(`${status?"HTTP Status "+status:"Timeout"} while loading ${t==null?void 0:t.url}, retrying ${s.retry}/${e.maxNumRetry} in ${this.retryDelay}ms`),this.abortInternal(),this.loader=null,self.clearTimeout(this.retryTimeout),this.retryTimeout=self.setTimeout(this.loadInternal.bind(this),this.retryDelay)}loadprogress(e){const t=this.stats;t.loaded=e.loaded,e.lengthComputable&&(t.total=e.total)}getCacheAge(){let e=null;if(this.loader&&Uu.test(this.loader.getAllResponseHeaders())){const t=this.loader.getResponseHeader("age");e=t?parseFloat(t):null}return e}getResponseHeader(e){return this.loader&&new RegExp(`^${e}:\\s*[\\d.]+\\s*$`,"im").test(this.loader.getAllResponseHeaders())?this.loader.getResponseHeader(e):null}}const $u={maxTimeToFirstByteMs:8e3,maxLoadTimeMs:2e4,timeoutRetry:null,errorRetry:null},Gu=ie(ie({autoStartLoad:!0,startPosition:-1,defaultAudioCodec:void 0,debug:!1,capLevelOnFPSDrop:!1,capLevelToPlayerSize:!1,ignoreDevicePixelRatio:!1,maxDevicePixelRatio:Number.POSITIVE_INFINITY,preferManagedMediaSource:!0,initialLiveManifestSize:1,maxBufferLength:30,backBufferLength:1/0,frontBufferFlushThreshold:1/0,startOnSegmentBoundary:!1,maxBufferSize:60*1e3*1e3,maxFragLookUpTolerance:.25,maxBufferHole:.1,detectStallWithCurrentTimeMs:1250,highBufferWatchdogPeriod:2,nudgeOffset:.1,nudgeMaxRetry:3,nudgeOnVideoHole:!0,liveSyncMode:"edge",liveSyncDurationCount:3,liveSyncOnStallIncrease:1,liveMaxLatencyDurationCount:1/0,liveSyncDuration:void 0,liveMaxLatencyDuration:void 0,maxLiveSyncPlaybackRate:1,liveDurationInfinity:!1,liveBackBufferLength:null,maxMaxBufferLength:600,enableWorker:!0,workerPath:null,enableSoftwareAES:!0,startLevel:void 0,startFragPrefetch:!1,fpsDroppedMonitoringPeriod:5e3,fpsDroppedMonitoringThreshold:.2,appendErrorMaxRetry:3,ignorePlaylistParsingErrors:!1,loader:qa,fLoader:void 0,pLoader:void 0,xhrSetup:void 0,licenseXhrSetup:void 0,licenseResponseCallback:void 0,abrController:Jo,bufferController:Vc,capLevelController:Ki,errorController:rl,fpsController:Hh,stretchShortVideoTrack:!1,maxAudioFramesDrift:1,forceKeyFrameOnDiscontinuity:!0,abrEwmaFastLive:3,abrEwmaSlowLive:9,abrEwmaFastVoD:3,abrEwmaSlowVoD:9,abrEwmaDefaultEstimate:5e5,abrEwmaDefaultEstimateMax:5e6,abrBandWidthFactor:.95,abrBandWidthUpFactor:.7,abrMaxWithRealBitrate:!1,maxStarvationDelay:4,maxLoadingDelay:4,minAutoBitrate:0,emeEnabled:!1,widevineLicenseUrl:void 0,drmSystems:{},drmSystemOptions:{},requestMediaKeySystemAccessFunc:Yn,requireKeySystemAccessOnStart:!1,testBandwidth:!0,progressive:!1,lowLatencyMode:!0,cmcd:void 0,enableDateRangeMetadataCues:!0,enableEmsgMetadataCues:!0,enableEmsgKLVMetadata:!1,enableID3MetadataCues:!0,enableInterstitialPlayback:!0,interstitialAppendInPlace:!0,interstitialLiveLookAhead:10,useMediaCapabilities:!0,preserveManualLevelOnError:!1,certLoadPolicy:{default:$u},keyLoadPolicy:{default:{maxTimeToFirstByteMs:8e3,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:2e4,backoff:"linear"},errorRetry:{maxNumRetry:8,retryDelayMs:1e3,maxRetryDelayMs:2e4,backoff:"linear"}}},manifestLoadPolicy:{default:{maxTimeToFirstByteMs:1/0,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},playlistLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:2,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},fragLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:12e4,timeoutRetry:{maxNumRetry:4,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:6,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},steeringManifestLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},interstitialAssetListLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:3e4,timeoutRetry:{maxNumRetry:0,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:0,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},manifestLoadingTimeOut:1e4,manifestLoadingMaxRetry:1,manifestLoadingRetryDelay:1e3,manifestLoadingMaxRetryTimeout:64e3,levelLoadingTimeOut:1e4,levelLoadingMaxRetry:4,levelLoadingRetryDelay:1e3,levelLoadingMaxRetryTimeout:64e3,fragLoadingTimeOut:2e4,fragLoadingMaxRetry:6,fragLoadingRetryDelay:1e3,fragLoadingMaxRetryTimeout:64e3},Ku()),{},{subtitleStreamController:su,subtitleTrackController:qh,timelineController:_u,audioStreamController:Uc,audioTrackController:$c,emeController:_t,cmcdController:$h,contentSteeringController:Kh,interstitialsController:tu});function Ku(){return{cueHandler:Pu,enableWebVTT:!0,enableIMSC1:!0,enableCEA708Captions:!0,captionsTextTrack1Label:"English",captionsTextTrack1LanguageCode:"en",captionsTextTrack2Label:"Spanish",captionsTextTrack2LanguageCode:"es",captionsTextTrack3Label:"Unknown CC",captionsTextTrack3LanguageCode:"",captionsTextTrack4Label:"Unknown CC",captionsTextTrack4LanguageCode:"",renderTextTracksNatively:!0}}function Vu(a,e,t){if((e.liveSyncDurationCount||e.liveMaxLatencyDurationCount)&&(e.liveSyncDuration||e.liveMaxLatencyDuration))throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration");if(e.liveMaxLatencyDurationCount!==void 0&&(e.liveSyncDurationCount===void 0||e.liveMaxLatencyDurationCount<=e.liveSyncDurationCount))throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be greater than "liveSyncDurationCount"');if(e.liveMaxLatencyDuration!==void 0&&(e.liveSyncDuration===void 0||e.liveMaxLatencyDuration<=e.liveSyncDuration))throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be greater than "liveSyncDuration"');const s=Si(a),i=["manifest","level","frag"],r=["TimeOut","MaxRetry","RetryDelay","MaxRetryTimeout"];return i.forEach(n=>{const o=`${n==="level"?"playlist":n}LoadPolicy`,c=e[o]===void 0,l=[];r.forEach(h=>{const u=`${n}Loading${h}`,d=e[u];if(d!==void 0&&c){l.push(u);const f=s[o].default;switch(e[o]={default:f},h){case"TimeOut":f.maxLoadTimeMs=d,f.maxTimeToFirstByteMs=d;break;case"MaxRetry":f.errorRetry.maxNumRetry=d,f.timeoutRetry.maxNumRetry=d;break;case"RetryDelay":f.errorRetry.retryDelayMs=d,f.timeoutRetry.retryDelayMs=d;break;case"MaxRetryTimeout":f.errorRetry.maxRetryDelayMs=d,f.timeoutRetry.maxRetryDelayMs=d;break}}}),l.length&&t.warn(`hls.js config: "${l.join('", "')}" setting(s) are deprecated, use "${o}": ${oe(e[o])}`)}),ie(ie({},s),e)}function Si(a){return a&&typeof a=="object"?Array.isArray(a)?a.map(Si):Object.keys(a).reduce((e,t)=>(e[t]=Si(a[t]),e),{}):a}function Hu(a,e){const t=a.loader;t!==dn&&t!==qa?(e.log("[config]: Custom loader detected, cannot enable progressive streaming"),a.progressive=!1):ku()&&(a.loader=dn,a.progressive=!0,a.enableSoftwareAES=!0,e.log("[config]: Progressive streaming enabled, using FetchLoader"))}const ps=2,Yu=.1,Wu=.05,qu=100;class ju extends $n{constructor(e,t){super("gap-controller",e.logger),this.hls=void 0,this.fragmentTracker=void 0,this.media=null,this.mediaSource=void 0,this.nudgeRetry=0,this.stallReported=!1,this.stalled=null,this.moved=!1,this.seeking=!1,this.buffered={},this.lastCurrentTime=0,this.ended=0,this.waiting=0,this.onMediaPlaying=()=>{this.ended=0,this.waiting=0},this.onMediaWaiting=()=>{var s;(s=this.media)!=null&&s.seeking||(this.waiting=self.performance.now(),this.tick())},this.onMediaEnded=()=>{if(this.hls){var s;this.ended=((s=this.media)==null?void 0:s.currentTime)||1,this.hls.trigger(m.MEDIA_ENDED,{stalled:!1})}},this.hls=e,this.fragmentTracker=t,this.registerListeners()}registerListeners(){const{hls:e}=this;e&&(e.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.BUFFER_APPENDED,this.onBufferAppended,this))}unregisterListeners(){const{hls:e}=this;e&&(e.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.BUFFER_APPENDED,this.onBufferAppended,this))}destroy(){super.destroy(),this.unregisterListeners(),this.media=this.hls=this.fragmentTracker=null,this.mediaSource=void 0}onMediaAttached(e,t){this.setInterval(qu),this.mediaSource=t.mediaSource;const s=this.media=t.media;ve(s,"playing",this.onMediaPlaying),ve(s,"waiting",this.onMediaWaiting),ve(s,"ended",this.onMediaEnded)}onMediaDetaching(e,t){this.clearInterval();const{media:s}=this;s&&(Ae(s,"playing",this.onMediaPlaying),Ae(s,"waiting",this.onMediaWaiting),Ae(s,"ended",this.onMediaEnded),this.media=null),this.mediaSource=void 0}onBufferAppended(e,t){this.buffered=t.timeRanges}get hasBuffered(){return Object.keys(this.buffered).length>0}tick(){var e;if(!((e=this.media)!=null&&e.readyState)||!this.hasBuffered)return;const t=this.media.currentTime;this.poll(t,this.lastCurrentTime),this.lastCurrentTime=t}poll(e,t){var s,i;const r=(s=this.hls)==null?void 0:s.config;if(!r)return;const n=this.media;if(!n)return;const{seeking:o}=n,c=this.seeking&&!o,l=!this.seeking&&o,h=n.paused&&!o||n.ended||n.playbackRate===0;if(this.seeking=o,e!==t){t&&(this.ended=0),this.moved=!0,o||(this.nudgeRetry=0,r.nudgeOnVideoHole&&!h&&e>t&&this.nudgeOnVideoHole(e,t)),this.waiting===0&&this.stallResolved(e);return}if(l||c){c&&this.stallResolved(e);return}if(h){this.nudgeRetry=0,this.stallResolved(e),!this.ended&&n.ended&&this.hls&&(this.ended=e||1,this.hls.trigger(m.MEDIA_ENDED,{stalled:!1}));return}if(!X.getBuffered(n).length){this.nudgeRetry=0;return}const u=X.bufferInfo(n,e,0),d=u.nextStart||0,f=this.fragmentTracker;if(o&&f&&this.hls){const D=fn(this.hls.inFlightFragments,e),A=u.len>ps,_=!d||D||d-e>ps&&!f.getPartialFragment(e);if(A||_)return;this.moved=!1}const g=(i=this.hls)==null?void 0:i.latestLevelDetails;if(!this.moved&&this.stalled!==null&&f){if(!(u.len>0)&&!d)return;const A=Math.max(d,u.start||0)-e,b=!!(g!=null&&g.live)?g.targetduration*2:ps,I=os(e,f);if(A>0&&(A<=b||I)){n.paused||this._trySkipBufferHole(I);return}}const y=r.detectStallWithCurrentTimeMs,p=self.performance.now(),E=this.waiting;let T=this.stalled;if(T===null)if(E>0&&p-E=y||E)&&this.hls){var v;if(((v=this.mediaSource)==null?void 0:v.readyState)==="ended"&&!(g!=null&&g.live)&&Math.abs(e-((g==null?void 0:g.edge)||0))<1){if(this.ended)return;this.ended=e||1,this.hls.trigger(m.MEDIA_ENDED,{stalled:!0});return}if(this._reportStall(u),!this.media||!this.hls)return}const x=X.bufferInfo(n,e,r.maxBufferHole);this._tryFixBufferStall(x,S,e)}stallResolved(e){const t=this.stalled;if(t&&this.hls&&(this.stalled=null,this.stallReported)){const s=self.performance.now()-t;this.log(`playback not stuck anymore @${e}, after ${Math.round(s)}ms`),this.stallReported=!1,this.waiting=0,this.hls.trigger(m.STALL_RESOLVED,{})}}nudgeOnVideoHole(e,t){var s;const i=this.buffered.video;if(this.hls&&this.media&&this.fragmentTracker&&(s=this.buffered.audio)!=null&&s.length&&i&&i.length>1&&e>i.end(0)){const r=X.bufferedInfo(X.timeRangesToArray(this.buffered.audio),e,0);if(r.len>1&&t>=r.start){const n=X.timeRangesToArray(i),o=X.bufferedInfo(n,t,0).bufferedIndex;if(o>-1&&oo)&&h-l<1&&e-l<2){const u=new Error(`nudging playhead to flush pipeline after video hole. currentTime: ${e} hole: ${l} -> ${h} buffered index: ${c}`);this.warn(u.message),this.media.currentTime+=1e-6;let d=os(e,this.fragmentTracker);d&&"fragment"in d?d=d.fragment:d||(d=void 0);const f=X.bufferInfo(this.media,e,0);this.hls.trigger(m.ERROR,{type:H.MEDIA_ERROR,details:R.BUFFER_SEEK_OVER_HOLE,fatal:!1,error:u,reason:u.message,frag:d,buffer:f.len,bufferInfo:f})}}}}}_tryFixBufferStall(e,t,s){var i,r;const{fragmentTracker:n,media:o}=this,c=(i=this.hls)==null?void 0:i.config;if(!o||!n||!c)return;const l=(r=this.hls)==null?void 0:r.latestLevelDetails,h=os(s,n);if((h||l!=null&&l.live&&s1&&e.len>c.maxBufferHole||e.nextStart&&(e.nextStart-sc.highBufferWatchdogPeriod*1e3||this.waiting)&&(this.warn("Trying to nudge playhead over buffer-hole"),this._tryNudgeBuffer(e))}adjacentTraversal(e,t){const s=this.fragmentTracker,i=e.nextStart;if(s&&i){const r=s.getFragAtPos(t,U.MAIN),n=s.getFragAtPos(i,U.MAIN);if(r&&n)return n.sn-r.sn<2}return!1}_reportStall(e){const{hls:t,media:s,stallReported:i,stalled:r}=this;if(!i&&r!==null&&s&&t){this.stallReported=!0;const n=new Error(`Playback stalling at @${s.currentTime} due to low buffer (${oe(e)})`);this.warn(n.message),t.trigger(m.ERROR,{type:H.MEDIA_ERROR,details:R.BUFFER_STALLED_ERROR,fatal:!1,error:n,buffer:e.len,bufferInfo:e,stalled:{start:r}})}}_trySkipBufferHole(e){var t;const{fragmentTracker:s,media:i}=this,r=(t=this.hls)==null?void 0:t.config;if(!i||!s||!r)return 0;const n=i.currentTime,o=X.bufferInfo(i,n,0),c=n0&&o.len<1&&i.readyState<3,d=c-n;if(d>0&&(h||u)){if(d>r.maxBufferHole){let g=!1;if(n===0){const y=s.getAppendedFrag(0,U.MAIN);y&&c"u"))return self.VTTCue||self.TextTrackCue}function ei(a,e,t,s,i){let r=new a(e,t,"");try{r.value=s,i&&(r.type=i)}catch{r=new a(e,t,oe(i?ie({type:i},s):s))}return r}const ls=(()=>{const a=vi();try{a&&new a(0,Number.POSITIVE_INFINITY,"")}catch{return Number.MAX_VALUE}return Number.POSITIVE_INFINITY})();class zu{constructor(e){this.hls=void 0,this.id3Track=null,this.media=null,this.dateRangeCuesAppended={},this.removeCues=!0,this.assetCue=void 0,this.onEventCueEnter=()=>{this.hls&&this.hls.trigger(m.EVENT_CUE_ENTER,{})},this.hls=e,this._registerListeners()}destroy(){this._unregisterListeners(),this.id3Track=null,this.media=null,this.dateRangeCuesAppended={},this.hls=this.onEventCueEnter=null}_registerListeners(){const{hls:e}=this;e&&(e.on(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),e.on(m.BUFFER_FLUSHING,this.onBufferFlushing,this),e.on(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(m.LEVEL_PTS_UPDATED,this.onLevelPtsUpdated,this))}_unregisterListeners(){const{hls:e}=this;e&&(e.off(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),e.off(m.BUFFER_FLUSHING,this.onBufferFlushing,this),e.off(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(m.LEVEL_PTS_UPDATED,this.onLevelPtsUpdated,this))}onMediaAttaching(e,t){var s;this.media=t.media,((s=t.overrides)==null?void 0:s.cueRemoval)===!1&&(this.removeCues=!1)}onMediaAttached(){var e;const t=(e=this.hls)==null?void 0:e.latestLevelDetails;t&&this.updateDateRangeCues(t)}onMediaDetaching(e,t){this.media=null,!t.transferMedia&&(this.id3Track&&(this.removeCues&&It(this.id3Track,this.onEventCueEnter),this.id3Track=null),this.dateRangeCuesAppended={})}onManifestLoading(){this.dateRangeCuesAppended={}}createTrack(e){const t=this.getID3Track(e.textTracks);return t.mode="hidden",t}getID3Track(e){if(this.media){for(let t=0;tls&&(u=ls),u-h<=0&&(u=h+Xu);for(let f=0;fh.type===be.audioId3&&c:i==="video"?l=h=>h.type===be.emsg&&o:l=h=>h.type===be.audioId3&&c||h.type===be.emsg&&o,Ei(r,t,s,l)}}onLevelUpdated(e,{details:t}){this.updateDateRangeCues(t,!0)}onLevelPtsUpdated(e,t){Math.abs(t.drift)>.01&&this.updateDateRangeCues(t.details)}updateDateRangeCues(e,t){if(!this.hls||!this.media)return;const{assetPlayerId:s,timelineOffset:i,enableDateRangeMetadataCues:r,interstitialsController:n}=this.hls.config;if(!r)return;const o=vi();if(s&&i&&!n){const{fragmentStart:y,fragmentEnd:p}=e;let E=this.assetCue;E?(E.startTime=y,E.endTime=p):o&&(E=this.assetCue=ei(o,y,p,{assetPlayerId:this.hls.config.assetPlayerId},"hlsjs.interstitial.asset"),E&&(E.id=s,this.id3Track||(this.id3Track=this.createTrack(this.media)),this.id3Track.addCue(E),E.addEventListener("enter",this.onEventCueEnter)))}if(!e.hasProgramDateTime)return;const{id3Track:c}=this,{dateRanges:l}=e,h=Object.keys(l);let u=this.dateRangeCuesAppended;if(c&&t){var d;if((d=c.cues)!=null&&d.length){const y=Object.keys(u).filter(p=>!h.includes(p));for(let p=y.length;p--;){var f;const E=y[p],T=(f=u[E])==null?void 0:f.cues;delete u[E],T&&Object.keys(T).forEach(S=>{const v=T[S];if(v){v.removeEventListener("enter",this.onEventCueEnter);try{c.removeCue(v)}catch{}}})}}else u=this.dateRangeCuesAppended={}}const g=e.fragments[e.fragments.length-1];if(!(h.length===0||!N(g==null?void 0:g.programDateTime))){this.id3Track||(this.id3Track=this.createTrack(this.media));for(let y=0;y{if(F!==E.id){const $=l[F];if($.class===E.class&&$.startDate>E.startDate&&(!P||E.startDate.01&&(F.startTime=T,F.endTime=D);else if(o){let $=E.attr[P];El(P)&&($=Sn($));const G=ei(o,T,D,{key:P,data:$},be.dateRange);G&&(G.id=p,this.id3Track.addCue(G),v[P]=G,n&&(P==="X-ASSET-LIST"||P==="X-ASSET-URL")&&G.addEventListener("enter",this.onEventCueEnter))}}u[p]={cues:v,dateRange:E,durationKnown:x}}}}}class Qu{constructor(e){this.hls=void 0,this.config=void 0,this.media=null,this.currentTime=0,this.stallCount=0,this._latency=null,this._targetLatencyUpdated=!1,this.onTimeupdate=()=>{const{media:t}=this,s=this.levelDetails;if(!t||!s)return;this.currentTime=t.currentTime;const i=this.computeLatency();if(i===null)return;this._latency=i;const{lowLatencyMode:r,maxLiveSyncPlaybackRate:n}=this.config;if(!r||n===1||!s.live)return;const o=this.targetLatency;if(o===null)return;const c=i-o,l=Math.min(this.maxLatency,o+s.targetduration);if(c.05&&this.forwardBufferLength>1){const u=Math.min(2,Math.max(1,n)),d=Math.round(2/(1+Math.exp(-.75*c-this.edgeStalled))*20)/20,f=Math.min(u,Math.max(1,d));this.changeMediaPlaybackRate(t,f)}else t.playbackRate!==1&&t.playbackRate!==0&&this.changeMediaPlaybackRate(t,1)},this.hls=e,this.config=e.config,this.registerListeners()}get levelDetails(){var e;return((e=this.hls)==null?void 0:e.latestLevelDetails)||null}get latency(){return this._latency||0}get maxLatency(){const{config:e}=this;if(e.liveMaxLatencyDuration!==void 0)return e.liveMaxLatencyDuration;const t=this.levelDetails;return t?e.liveMaxLatencyDurationCount*t.targetduration:0}get targetLatency(){const e=this.levelDetails;if(e===null||this.hls===null)return null;const{holdBack:t,partHoldBack:s,targetduration:i}=e,{liveSyncDuration:r,liveSyncDurationCount:n,lowLatencyMode:o}=this.config,c=this.hls.userConfig;let l=o&&s||t;(this._targetLatencyUpdated||c.liveSyncDuration||c.liveSyncDurationCount||l===0)&&(l=r!==void 0?r:n*i);const h=i;return l+Math.min(this.stallCount*this.config.liveSyncOnStallIncrease,h)}set targetLatency(e){this.stallCount=0,this.config.liveSyncDuration=e,this._targetLatencyUpdated=!0}get liveSyncPosition(){const e=this.estimateLiveEdge(),t=this.targetLatency;if(e===null||t===null)return null;const s=this.levelDetails;if(s===null)return null;const i=s.edge,r=e-t-this.edgeStalled,n=i-s.totalduration,o=i-(this.config.lowLatencyMode&&s.partTarget||s.targetduration);return Math.min(Math.max(n,r),o)}get drift(){const e=this.levelDetails;return e===null?1:e.drift}get edgeStalled(){const e=this.levelDetails;if(e===null)return 0;const t=(this.config.lowLatencyMode&&e.partTarget||e.targetduration)*3;return Math.max(e.age-t,0)}get forwardBufferLength(){const{media:e}=this,t=this.levelDetails;if(!e||!t)return 0;const s=e.buffered.length;return(s?e.buffered.end(s-1):t.edge)-this.currentTime}destroy(){this.unregisterListeners(),this.onMediaDetaching(),this.hls=null}registerListeners(){const{hls:e}=this;e&&(e.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(m.ERROR,this.onError,this))}unregisterListeners(){const{hls:e}=this;e&&(e.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(m.ERROR,this.onError,this))}onMediaAttached(e,t){this.media=t.media,this.media.addEventListener("timeupdate",this.onTimeupdate)}onMediaDetaching(){this.media&&(this.media.removeEventListener("timeupdate",this.onTimeupdate),this.media=null)}onManifestLoading(){this._latency=null,this.stallCount=0}onLevelUpdated(e,{details:t}){t.advanced&&this.onTimeupdate(),!t.live&&this.media&&this.media.removeEventListener("timeupdate",this.onTimeupdate)}onError(e,t){var s;t.details===R.BUFFER_STALLED_ERROR&&(this.stallCount++,this.hls&&(s=this.levelDetails)!=null&&s.live&&this.hls.logger.warn("[latency-controller]: Stall detected, adjusting target latency"))}changeMediaPlaybackRate(e,t){var s,i;e.playbackRate!==t&&((s=this.hls)==null||s.logger.debug(`[latency-controller]: latency=${this.latency.toFixed(3)}, targetLatency=${(i=this.targetLatency)==null?void 0:i.toFixed(3)}, forwardBufferLength=${this.forwardBufferLength.toFixed(3)}: adjusting playback rate from ${e.playbackRate} to ${t}`),e.playbackRate=t)}estimateLiveEdge(){const e=this.levelDetails;return e===null?null:e.edge+e.age}computeLatency(){const e=this.estimateLiveEdge();return e===null?null:e-this.currentTime}}class Zu extends Gi{constructor(e,t){super(e,"level-controller"),this._levels=[],this._firstLevel=-1,this._maxAutoLevel=-1,this._startLevel=void 0,this.currentLevel=null,this.currentLevelIndex=-1,this.manualLevelIndex=-1,this.steering=void 0,this.onParsedComplete=void 0,this.steering=t,this._registerListeners()}_registerListeners(){const{hls:e}=this;e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(m.LEVEL_LOADED,this.onLevelLoaded,this),e.on(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(m.FRAG_BUFFERED,this.onFragBuffered,this),e.on(m.ERROR,this.onError,this)}_unregisterListeners(){const{hls:e}=this;e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(m.LEVEL_LOADED,this.onLevelLoaded,this),e.off(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(m.FRAG_BUFFERED,this.onFragBuffered,this),e.off(m.ERROR,this.onError,this)}destroy(){this._unregisterListeners(),this.steering=null,this.resetLevels(),super.destroy()}stopLoad(){this._levels.forEach(t=>{t.loadError=0,t.fragmentError=0}),super.stopLoad()}resetLevels(){this._startLevel=void 0,this.manualLevelIndex=-1,this.currentLevelIndex=-1,this.currentLevel=null,this._levels=[],this._maxAutoLevel=-1}onManifestLoading(e,t){this.resetLevels()}onManifestLoaded(e,t){const s=this.hls.config.preferManagedMediaSource,i=[],r={},n={};let o=!1,c=!1,l=!1;t.levels.forEach(h=>{const u=h.attrs;let{audioCodec:d,videoCodec:f}=h;d&&(h.audioCodec=d=Es(d,s)||void 0),f&&(f=h.videoCodec=Oo(f));const{width:g,height:y,unknownCodecs:p}=h,E=(p==null?void 0:p.length)||0;if(o||(o=!!(g&&y)),c||(c=!!f),l||(l=!!d),E||d&&!this.isAudioSupported(d)||f&&!this.isVideoSupported(f)){this.log(`Some or all CODECS not supported "${u.CODECS}"`);return}const{CODECS:T,"FRAME-RATE":S,"HDCP-LEVEL":v,"PATHWAY-ID":x,RESOLUTION:D,"VIDEO-RANGE":A}=u,b=`${`${x||"."}-`}${h.bitrate}-${D}-${S}-${T}-${A}-${v}`;if(r[b])if(r[b].uri!==h.url&&!h.attrs["PATHWAY-ID"]){const I=n[b]+=1;h.attrs["PATHWAY-ID"]=new Array(I+1).join(".");const P=this.createLevel(h);r[b]=P,i.push(P)}else r[b].addGroupId("audio",u.AUDIO),r[b].addGroupId("text",u.SUBTITLES);else{const I=this.createLevel(h);r[b]=I,n[b]=1,i.push(I)}}),this.filterAndSortMediaOptions(i,t,o,c,l)}createLevel(e){const t=new Kt(e),s=e.supplemental;if(s!=null&&s.videoCodec&&!this.isVideoSupported(s.videoCodec)){const i=new Error(`SUPPLEMENTAL-CODECS not supported "${s.videoCodec}"`);this.log(i.message),t.supportedResult=kn(i,[])}return t}isAudioSupported(e){return $t(e,"audio",this.hls.config.preferManagedMediaSource)}isVideoSupported(e){return $t(e,"video",this.hls.config.preferManagedMediaSource)}filterAndSortMediaOptions(e,t,s,i,r){var n;let o=[],c=[],l=e;const h=((n=t.stats)==null?void 0:n.parsing)||{};if((s||i)&&r&&(l=l.filter(({videoCodec:T,videoRange:S,width:v,height:x})=>(!!T||!!(v&&x))&&Ho(S))),l.length===0){Promise.resolve().then(()=>{if(this.hls){let T="no level with compatible codecs found in manifest",S=T;t.levels.length&&(S=`one or more CODECS in variant not supported: ${oe(t.levels.map(x=>x.attrs.CODECS).filter((x,D,A)=>A.indexOf(x)===D))}`,this.warn(S),T+=` (${S})`);const v=new Error(T);this.hls.trigger(m.ERROR,{type:H.MEDIA_ERROR,details:R.MANIFEST_INCOMPATIBLE_CODECS_ERROR,fatal:!0,url:t.url,error:v,reason:S})}}),h.end=performance.now();return}t.audioTracks&&(o=t.audioTracks.filter(T=>!T.audioCodec||this.isAudioSupported(T.audioCodec)),mn(o)),t.subtitles&&(c=t.subtitles,mn(c));const u=l.slice(0);l.sort((T,S)=>{if(T.attrs["HDCP-LEVEL"]!==S.attrs["HDCP-LEVEL"])return(T.attrs["HDCP-LEVEL"]||"")>(S.attrs["HDCP-LEVEL"]||"")?1:-1;if(s&&T.height!==S.height)return T.height-S.height;if(T.frameRate!==S.frameRate)return T.frameRate-S.frameRate;if(T.videoRange!==S.videoRange)return Ts.indexOf(T.videoRange)-Ts.indexOf(S.videoRange);if(T.videoCodec!==S.videoCodec){const v=nr(T.videoCodec),x=nr(S.videoCodec);if(v!==x)return x-v}if(T.uri===S.uri&&T.codecSet!==S.codecSet){const v=ys(T.codecSet),x=ys(S.codecSet);if(v!==x)return x-v}return T.averageBitrate!==S.averageBitrate?T.averageBitrate-S.averageBitrate:0});let d=u[0];if(this.steering&&(l=this.steering.filterParsedLevels(l),l.length!==u.length)){for(let T=0;Tv&&v===this.hls.abrEwmaDefaultEstimate&&(this.hls.bandwidthEstimate=x)}break}const g=r&&!i,y=this.hls.config,p=!!(y.audioStreamController&&y.audioTrackController),E={levels:l,audioTracks:o,subtitleTracks:c,sessionData:t.sessionData,sessionKeys:t.sessionKeys,firstLevel:this._firstLevel,stats:t.stats,audio:r,video:i,altAudio:p&&!g&&o.some(T=>!!T.url)};h.end=performance.now(),this.hls.trigger(m.MANIFEST_PARSED,E)}get levels(){return this._levels.length===0?null:this._levels}get loadLevelObj(){return this.currentLevel}get level(){return this.currentLevelIndex}set level(e){const t=this._levels;if(t.length===0)return;if(e<0||e>=t.length){const h=new Error("invalid level idx"),u=e<0;if(this.hls.trigger(m.ERROR,{type:H.OTHER_ERROR,details:R.LEVEL_SWITCH_ERROR,level:e,fatal:u,error:h,reason:h.message}),u)return;e=Math.min(e,t.length-1)}const s=this.currentLevelIndex,i=this.currentLevel,r=i?i.attrs["PATHWAY-ID"]:void 0,n=t[e],o=n.attrs["PATHWAY-ID"];if(this.currentLevelIndex=e,this.currentLevel=n,s===e&&i&&r===o)return;this.log(`Switching to level ${e} (${n.height?n.height+"p ":""}${n.videoRange?n.videoRange+" ":""}${n.codecSet?n.codecSet+" ":""}@${n.bitrate})${o?" with Pathway "+o:""} from level ${s}${r?" with Pathway "+r:""}`);const c={level:e,attrs:n.attrs,details:n.details,bitrate:n.bitrate,averageBitrate:n.averageBitrate,maxBitrate:n.maxBitrate,realBitrate:n.realBitrate,width:n.width,height:n.height,codecSet:n.codecSet,audioCodec:n.audioCodec,videoCodec:n.videoCodec,audioGroups:n.audioGroups,subtitleGroups:n.subtitleGroups,loaded:n.loaded,loadError:n.loadError,fragmentError:n.fragmentError,name:n.name,id:n.id,uri:n.uri,url:n.url,urlId:0,audioGroupIds:n.audioGroupIds,textGroupIds:n.textGroupIds};this.hls.trigger(m.LEVEL_SWITCHING,c);const l=n.details;if(!l||l.live){const h=this.switchParams(n.uri,i==null?void 0:i.details,l);this.loadPlaylist(h)}}get manualLevel(){return this.manualLevelIndex}set manualLevel(e){this.manualLevelIndex=e,this._startLevel===void 0&&(this._startLevel=e),e!==-1&&(this.level=e)}get firstLevel(){return this._firstLevel}set firstLevel(e){this._firstLevel=e}get startLevel(){if(this._startLevel===void 0){const e=this.hls.config.startLevel;return e!==void 0?e:this.hls.firstAutoLevel}return this._startLevel}set startLevel(e){this._startLevel=e}get pathways(){return this.steering?this.steering.pathways():[]}get pathwayPriority(){return this.steering?this.steering.pathwayPriority:null}set pathwayPriority(e){if(this.steering){const t=this.steering.pathways(),s=e.filter(i=>t.indexOf(i)!==-1);if(e.length<1){this.warn(`pathwayPriority ${e} should contain at least one pathway from list: ${t}`);return}this.steering.pathwayPriority=s}}onError(e,t){t.fatal||!t.context||t.context.type===J.LEVEL&&t.context.level===this.level&&this.checkRetry(t)}onFragBuffered(e,{frag:t}){if(t!==void 0&&t.type===U.MAIN){const s=t.elementaryStreams;if(!Object.keys(s).some(r=>!!s[r]))return;const i=this._levels[t.level];i!=null&&i.loadError&&(this.log(`Resetting level error count of ${i.loadError} on frag buffered`),i.loadError=0)}}onLevelLoaded(e,t){var s;const{level:i,details:r}=t,n=t.levelInfo;if(!n){var o;this.warn(`Invalid level index ${i}`),(o=t.deliveryDirectives)!=null&&o.skip&&(r.deltaUpdateFailed=!0);return}if(n===this.currentLevel||t.withoutMultiVariant){n.fragmentError===0&&(n.loadError=0);let c=n.details;c===t.details&&c.advanced&&(c=void 0),this.playlistLoaded(i,t,c)}else(s=t.deliveryDirectives)!=null&&s.skip&&(r.deltaUpdateFailed=!0)}loadPlaylist(e){super.loadPlaylist(),this.shouldLoadPlaylist(this.currentLevel)&&this.scheduleLoading(this.currentLevel,e)}loadingPlaylist(e,t){super.loadingPlaylist(e,t);const s=this.getUrlWithDirectives(e.uri,t),i=this.currentLevelIndex,r=e.attrs["PATHWAY-ID"],n=e.details,o=n==null?void 0:n.age;this.log(`Loading level index ${i}${(t==null?void 0:t.msn)!==void 0?" at sn "+t.msn+" part "+t.part:""}${r?" Pathway "+r:""}${o&&n.live?" age "+o.toFixed(1)+(n.type&&" "+n.type||""):""} ${s}`),this.hls.trigger(m.LEVEL_LOADING,{url:s,level:i,levelInfo:e,pathwayId:e.attrs["PATHWAY-ID"],id:0,deliveryDirectives:t||null})}get nextLoadLevel(){return this.manualLevelIndex!==-1?this.manualLevelIndex:this.hls.nextAutoLevel}set nextLoadLevel(e){this.level=e,this.manualLevelIndex===-1&&(this.hls.nextAutoLevel=e)}removeLevel(e){var t;if(this._levels.length===1)return;const s=this._levels.filter((r,n)=>n!==e?!0:(this.steering&&this.steering.removeLevel(r),r===this.currentLevel&&(this.currentLevel=null,this.currentLevelIndex=-1,r.details&&r.details.fragments.forEach(o=>o.level=-1)),!1));Jn(s),this._levels=s,this.currentLevelIndex>-1&&(t=this.currentLevel)!=null&&t.details&&(this.currentLevelIndex=this.currentLevel.details.fragments[0].level),this.manualLevelIndex>-1&&(this.manualLevelIndex=this.currentLevelIndex);const i=s.length-1;this._firstLevel=Math.min(this._firstLevel,i),this._startLevel&&(this._startLevel=Math.min(this._startLevel,i)),this.hls.trigger(m.LEVELS_UPDATED,{levels:s})}onLevelsUpdated(e,{levels:t}){this._levels=t}checkMaxAutoUpdated(){const{autoLevelCapping:e,maxAutoLevel:t,maxHdcpLevel:s}=this.hls;this._maxAutoLevel!==t&&(this._maxAutoLevel=t,this.hls.trigger(m.MAX_AUTO_LEVEL_UPDATED,{autoLevelCapping:e,levels:this.levels,maxAutoLevel:t,minAutoLevel:this.hls.minAutoLevel,maxHdcpLevel:s}))}}function mn(a){const e={};a.forEach(t=>{const s=t.groupId||"";t.id=e[s]=e[s]||0,e[s]++})}function ja(){return self.SourceBuffer||self.WebKitSourceBuffer}function Xa(){if(!nt())return!1;const e=ja();return!e||e.prototype&&typeof e.prototype.appendBuffer=="function"&&typeof e.prototype.remove=="function"}function Ju(){if(!Xa())return!1;const a=nt();return typeof(a==null?void 0:a.isTypeSupported)=="function"&&(["avc1.42E01E,mp4a.40.2","av01.0.01M.08","vp09.00.50.08"].some(e=>a.isTypeSupported(Gt(e,"video")))||["mp4a.40.2","fLaC"].some(e=>a.isTypeSupported(Gt(e,"audio"))))}function ed(){var a;const e=ja();return typeof(e==null||(a=e.prototype)==null?void 0:a.changeType)=="function"}const td=100;class sd extends wi{constructor(e,t,s){super(e,t,s,"stream-controller",U.MAIN),this.audioCodecSwap=!1,this.level=-1,this._forceStartLoad=!1,this._hasEnoughToStart=!1,this.altAudio=0,this.audioOnly=!1,this.fragPlaying=null,this.fragLastKbps=0,this.couldBacktrack=!1,this.backtrackFragment=null,this.audioCodecSwitch=!1,this.videoBuffer=null,this.onMediaPlaying=()=>{this.tick()},this.onMediaSeeked=()=>{const i=this.media,r=i?i.currentTime:null;if(r===null||!N(r)||(this.log(`Media seeked to ${r.toFixed(3)}`),!this.getBufferedFrag(r)))return;const n=this.getFwdBufferInfoAtPos(i,r,U.MAIN,0);if(n===null||n.len===0){this.warn(`Main forward buffer length at ${r} on "seeked" event ${n?n.len:"empty"})`);return}this.tick()},this.registerListeners()}registerListeners(){super.registerListeners();const{hls:e}=this;e.on(m.MANIFEST_PARSED,this.onManifestParsed,this),e.on(m.LEVEL_LOADING,this.onLevelLoading,this),e.on(m.LEVEL_LOADED,this.onLevelLoaded,this),e.on(m.FRAG_LOAD_EMERGENCY_ABORTED,this.onFragLoadEmergencyAborted,this),e.on(m.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),e.on(m.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),e.on(m.BUFFER_CREATED,this.onBufferCreated,this),e.on(m.BUFFER_FLUSHED,this.onBufferFlushed,this),e.on(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(m.FRAG_BUFFERED,this.onFragBuffered,this)}unregisterListeners(){super.unregisterListeners();const{hls:e}=this;e.off(m.MANIFEST_PARSED,this.onManifestParsed,this),e.off(m.LEVEL_LOADED,this.onLevelLoaded,this),e.off(m.FRAG_LOAD_EMERGENCY_ABORTED,this.onFragLoadEmergencyAborted,this),e.off(m.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),e.off(m.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),e.off(m.BUFFER_CREATED,this.onBufferCreated,this),e.off(m.BUFFER_FLUSHED,this.onBufferFlushed,this),e.off(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(m.FRAG_BUFFERED,this.onFragBuffered,this)}onHandlerDestroying(){this.onMediaPlaying=this.onMediaSeeked=null,this.unregisterListeners(),super.onHandlerDestroying()}startLoad(e,t){if(this.levels){const{lastCurrentTime:s,hls:i}=this;if(this.stopLoad(),this.setInterval(td),this.level=-1,!this.startFragRequested){let r=i.startLevel;r===-1&&(i.config.testBandwidth&&this.levels.length>1?(r=0,this.bitrateTest=!0):r=i.firstAutoLevel),i.nextLoadLevel=r,this.level=i.loadLevel,this._hasEnoughToStart=!!t}s>0&&e===-1&&!t&&(this.log(`Override startPosition with lastCurrentTime @${s.toFixed(3)}`),e=s),this.state=C.IDLE,this.nextLoadPosition=this.lastCurrentTime=e+this.timelineOffset,this.startPosition=t?-1:e,this.tick()}else this._forceStartLoad=!0,this.state=C.STOPPED}stopLoad(){this._forceStartLoad=!1,super.stopLoad()}doTick(){switch(this.state){case C.WAITING_LEVEL:{const{levels:e,level:t}=this,s=e==null?void 0:e[t],i=s==null?void 0:s.details;if(i&&(!i.live||this.levelLastLoaded===s&&!this.waitForLive(s))){if(this.waitForCdnTuneIn(i))break;this.state=C.IDLE;break}else if(this.hls.nextLoadLevel!==this.level){this.state=C.IDLE;break}break}case C.FRAG_LOADING_WAITING_RETRY:this.checkRetryDate();break}this.state===C.IDLE&&this.doTickIdle(),this.onTickEnd()}onTickEnd(){var e;super.onTickEnd(),(e=this.media)!=null&&e.readyState&&this.media.seeking===!1&&(this.lastCurrentTime=this.media.currentTime),this.checkFragmentChanged()}doTickIdle(){const{hls:e,levelLastLoaded:t,levels:s,media:i}=this;if(t===null||!i&&!this.primaryPrefetch&&(this.startFragRequested||!e.config.startFragPrefetch)||this.altAudio&&this.audioOnly)return;const r=this.buffering?e.nextLoadLevel:e.loadLevel;if(!(s!=null&&s[r]))return;const n=s[r],o=this.getMainFwdBufferInfo();if(o===null)return;const c=this.getLevelDetails();if(c&&this._streamEnded(o,c)){const y={};this.altAudio===2&&(y.type="video"),this.hls.trigger(m.BUFFER_EOS,y),this.state=C.ENDED;return}if(!this.buffering)return;e.loadLevel!==r&&e.manualLevel===-1&&this.log(`Adapting to level ${r} from level ${this.level}`),this.level=e.nextLoadLevel=r;const l=n.details;if(!l||this.state===C.WAITING_LEVEL||this.waitForLive(n)){this.level=r,this.state=C.WAITING_LEVEL,this.startFragRequested=!1;return}const h=o.len,u=this.getMaxBufferLength(n.maxBitrate);if(h>=u)return;this.backtrackFragment&&this.backtrackFragment.start>o.end&&(this.backtrackFragment=null);const d=this.backtrackFragment?this.backtrackFragment.start:o.end;let f=this.getNextFragment(d,l);if(this.couldBacktrack&&!this.fragPrevious&&f&&ue(f)&&this.fragmentTracker.getState(f)!==fe.OK){var g;const p=((g=this.backtrackFragment)!=null?g:f).sn-l.startSN,E=l.fragments[p-1];E&&f.cc===E.cc&&(f=E,this.fragmentTracker.removeFragment(E))}else this.backtrackFragment&&o.len&&(this.backtrackFragment=null);if(f&&this.isLoopLoading(f,d)){if(!f.gap){const p=this.audioOnly&&!this.altAudio?ae.AUDIO:ae.VIDEO,E=(p===ae.VIDEO?this.videoBuffer:this.mediaBuffer)||this.media;E&&this.afterBufferFlushed(E,p,U.MAIN)}f=this.getNextFragmentLoopLoading(f,l,o,U.MAIN,u)}f&&(this.exceedsMaxBuffer(o,u,f)||(f.initSegment&&!f.initSegment.data&&!this.bitrateTest&&(f=f.initSegment),this.loadFragment(f,n,d)))}loadFragment(e,t,s){const i=this.fragmentTracker.getState(e);i===fe.NOT_LOADED||i===fe.PARTIAL?ue(e)?this.bitrateTest?(this.log(`Fragment ${e.sn} of level ${e.level} is being downloaded to test bitrate and will not be buffered`),this._loadBitrateTestFrag(e,t)):super.loadFragment(e,t,s):this._loadInitSegment(e,t):this.clearTrackerIfNeeded(e)}getBufferedFrag(e){return this.fragmentTracker.getBufferedFrag(e,U.MAIN)}followingBufferedFrag(e){return e?this.getBufferedFrag(e.end+.5):null}immediateLevelSwitch(){if(this.abortCurrentFrag(),this.flushMainBuffer(0,Number.POSITIVE_INFINITY),this.altAudio!==0){var e;(((e=this.getLevelDetails())==null?void 0:e.fragmentStart)||0)>this.lastCurrentTime&&super.flushMainBuffer(0,Number.POSITIVE_INFINITY,"audio")}}nextLevelSwitch(){const{levels:e,media:t}=this;if(t!=null&&t.readyState){let s;const i=this.getAppendedFrag(t.currentTime);i&&i.start>1&&this.flushMainBuffer(0,i.start-1);const r=this.getLevelDetails();if(r!=null&&r.live){const o=this.getMainFwdBufferInfo();if(!o||o.len=n-t.maxFragLookUpTolerance&&r<=o;if(i!==null&&s.duration>i&&(r{this.hls&&this.hls.trigger(m.AUDIO_TRACK_SWITCHED,t)}),s.trigger(m.BUFFER_FLUSHING,{startOffset:0,endOffset:Number.POSITIVE_INFINITY,type:null});return}s.trigger(m.AUDIO_TRACK_SWITCHED,t)}}onAudioTrackSwitched(e,t){const s=Ss(t.url,this.hls);if(s){const i=this.videoBuffer;i&&this.mediaBuffer!==i&&(this.log("Switching on alternate audio, use video.buffered to schedule main fragment loading"),this.mediaBuffer=i)}this.altAudio=s?2:0,this.tick()}onBufferCreated(e,t){const s=t.tracks;let i,r,n=!1;for(const o in s){const c=s[o];if(c.id==="main"){if(r=o,i=c,o==="video"){const l=s[o];l&&(this.videoBuffer=l.buffer)}}else n=!0}n&&i?(this.log(`Alternate track found, use ${r}.buffered to schedule main fragment loading`),this.mediaBuffer=i.buffer):this.mediaBuffer=this.media}onFragBuffered(e,t){const{frag:s,part:i}=t,r=s.type===U.MAIN;if(r){if(this.fragContextChanged(s)){this.warn(`Fragment ${s.sn}${i?" p: "+i.index:""} of level ${s.level} finished buffering, but was aborted. state: ${this.state}`),this.state===C.PARSED&&(this.state=C.IDLE);return}const o=i?i.stats:s.stats;this.fragLastKbps=Math.round(8*o.total/(o.buffering.end-o.loading.first)),ue(s)&&(this.fragPrevious=s),this.fragBufferedComplete(s,i)}const n=this.media;n&&(!this._hasEnoughToStart&&X.getBuffered(n).length&&(this._hasEnoughToStart=!0,this.seekToStartPos()),r&&this.tick())}get hasEnoughToStart(){return this._hasEnoughToStart}onError(e,t){var s;if(t.fatal){this.state=C.ERROR;return}switch(t.details){case R.FRAG_GAP:case R.FRAG_PARSING_ERROR:case R.FRAG_DECRYPT_ERROR:case R.FRAG_LOAD_ERROR:case R.FRAG_LOAD_TIMEOUT:case R.KEY_LOAD_ERROR:case R.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(U.MAIN,t);break;case R.LEVEL_LOAD_ERROR:case R.LEVEL_LOAD_TIMEOUT:case R.LEVEL_PARSING_ERROR:!t.levelRetry&&this.state===C.WAITING_LEVEL&&((s=t.context)==null?void 0:s.type)===J.LEVEL&&(this.state=C.IDLE);break;case R.BUFFER_ADD_CODEC_ERROR:case R.BUFFER_APPEND_ERROR:if(t.parent!=="main")return;this.reduceLengthAndFlushBuffer(t)&&this.resetLoadingState();break;case R.BUFFER_FULL_ERROR:if(t.parent!=="main")return;this.reduceLengthAndFlushBuffer(t)&&(!this.config.interstitialsController&&this.config.assetPlayerId?this._hasEnoughToStart=!0:this.flushMainBuffer(0,Number.POSITIVE_INFINITY));break;case R.INTERNAL_EXCEPTION:this.recoverWorkerError(t);break}}onFragLoadEmergencyAborted(){this.state=C.IDLE,this._hasEnoughToStart||(this.startFragRequested=!1,this.nextLoadPosition=this.lastCurrentTime),this.tickImmediate()}onBufferFlushed(e,{type:t}){if(t!==ae.AUDIO||!this.altAudio){const s=(t===ae.VIDEO?this.videoBuffer:this.mediaBuffer)||this.media;s&&(this.afterBufferFlushed(s,t,U.MAIN),this.tick())}}onLevelsUpdated(e,t){this.level>-1&&this.fragCurrent&&(this.level=this.fragCurrent.level,this.level===-1&&this.resetWhenMissingContext(this.fragCurrent)),this.levels=t.levels}swapAudioCodec(){this.audioCodecSwap=!this.audioCodecSwap}seekToStartPos(){const{media:e}=this;if(!e)return;const t=e.currentTime;let s=this.startPosition;if(s>=0&&t0&&(c{const{hls:i}=this,r=s==null?void 0:s.frag;if(!r||this.fragContextChanged(r))return;t.fragmentError=0,this.state=C.IDLE,this.startFragRequested=!1,this.bitrateTest=!1;const n=r.stats;n.parsing.start=n.parsing.end=n.buffering.start=n.buffering.end=self.performance.now(),i.trigger(m.FRAG_LOADED,s),r.bitrateTest=!1}).catch(s=>{this.state===C.STOPPED||this.state===C.ERROR||(this.warn(s),this.resetFragmentLoading(e))})}_handleTransmuxComplete(e){const t=this.playlistType,{hls:s}=this,{remuxResult:i,chunkMeta:r}=e,n=this.getCurrentContext(r);if(!n){this.resetWhenMissingContext(r);return}const{frag:o,part:c,level:l}=n,{video:h,text:u,id3:d,initSegment:f}=i,{details:g}=l,y=this.altAudio?void 0:i.audio;if(this.fragContextChanged(o)){this.fragmentTracker.removeFragment(o);return}if(this.state=C.PARSING,f){const p=f.tracks;if(p){const v=o.initSegment||o;if(this.unhandledEncryptionError(f,o))return;this._bufferInitSegment(l,p,v,r),s.trigger(m.FRAG_PARSING_INIT_SEGMENT,{frag:v,id:t,tracks:p})}const E=f.initPTS,T=f.timescale,S=this.initPTS[o.cc];if(N(E)&&(!S||S.baseTime!==E||S.timescale!==T)){const v=f.trackId;this.initPTS[o.cc]={baseTime:E,timescale:T,trackId:v},s.trigger(m.INIT_PTS_FOUND,{frag:o,id:t,initPTS:E,timescale:T,trackId:v})}}if(h&&g){y&&h.type==="audiovideo"&&this.logMuxedErr(o);const p=g.fragments[o.sn-1-g.startSN],E=o.sn===g.startSN,T=!p||o.cc>p.cc;if(i.independent!==!1){const{startPTS:S,endPTS:v,startDTS:x,endDTS:D}=h;if(c)c.elementaryStreams[h.type]={startPTS:S,endPTS:v,startDTS:x,endDTS:D};else if(h.firstKeyFrame&&h.independent&&r.id===1&&!T&&(this.couldBacktrack=!0),h.dropped&&h.independent){const A=this.getMainFwdBufferInfo(),_=(A?A.end:this.getLoadPosition())+this.config.maxBufferHole,b=h.firstKeyFramePTS?h.firstKeyFramePTS:S;if(!E&&_ps&&(o.gap=!0);o.setElementaryStreamInfo(h.type,S,v,x,D),this.backtrackFragment&&(this.backtrackFragment=o),this.bufferFragmentData(h,o,c,r,E||T)}else if(E||T)o.gap=!0;else{this.backtrack(o);return}}if(y){const{startPTS:p,endPTS:E,startDTS:T,endDTS:S}=y;c&&(c.elementaryStreams[ae.AUDIO]={startPTS:p,endPTS:E,startDTS:T,endDTS:S}),o.setElementaryStreamInfo(ae.AUDIO,p,E,T,S),this.bufferFragmentData(y,o,c,r)}if(g&&d!=null&&d.samples.length){const p={id:t,frag:o,details:g,samples:d.samples};s.trigger(m.FRAG_PARSING_METADATA,p)}if(g&&u){const p={id:t,frag:o,details:g,samples:u.samples};s.trigger(m.FRAG_PARSING_USERDATA,p)}}logMuxedErr(e){this.warn(`${ue(e)?"Media":"Init"} segment with muxed audiovideo where only video expected: ${e.url}`)}_bufferInitSegment(e,t,s,i){if(this.state!==C.PARSING)return;this.audioOnly=!!t.audio&&!t.video,this.altAudio&&!this.audioOnly&&(delete t.audio,t.audiovideo&&this.logMuxedErr(s));const{audio:r,video:n,audiovideo:o}=t;if(r){const l=e.audioCodec;let h=cs(r.codec,l);h==="mp4a"&&(h="mp4a.40.5");const u=navigator.userAgent.toLowerCase();if(this.audioCodecSwitch){h&&(h.indexOf("mp4a.40.5")!==-1?h="mp4a.40.2":h="mp4a.40.5");const d=r.metadata;d&&"channelCount"in d&&(d.channelCount||1)!==1&&u.indexOf("firefox")===-1&&(h="mp4a.40.5")}h&&h.indexOf("mp4a.40.5")!==-1&&u.indexOf("android")!==-1&&r.container!=="audio/mpeg"&&(h="mp4a.40.2",this.log(`Android: force audio codec to ${h}`)),l&&l!==h&&this.log(`Swapping manifest audio codec "${l}" for "${h}"`),r.levelCodec=h,r.id=U.MAIN,this.log(`Init audio buffer, container:${r.container}, codecs[selected/level/parsed]=[${h||""}/${l||""}/${r.codec}]`),delete t.audiovideo}if(n){n.levelCodec=e.videoCodec,n.id=U.MAIN;const l=n.codec;if((l==null?void 0:l.length)===4)switch(l){case"hvc1":case"hev1":n.codec="hvc1.1.6.L120.90";break;case"av01":n.codec="av01.0.04M.08";break;case"avc1":n.codec="avc1.42e01e";break}this.log(`Init video buffer, container:${n.container}, codecs[level/parsed]=[${e.videoCodec||""}/${l}]${n.codec!==l?" parsed-corrected="+n.codec:""}${n.supplemental?" supplemental="+n.supplemental:""}`),delete t.audiovideo}o&&(this.log(`Init audiovideo buffer, container:${o.container}, codecs[level/parsed]=[${e.codecs}/${o.codec}]`),delete t.video,delete t.audio);const c=Object.keys(t);if(c.length){if(this.hls.trigger(m.BUFFER_CODECS,t),!this.hls)return;c.forEach(l=>{const u=t[l].initSegment;u!=null&&u.byteLength&&this.hls.trigger(m.BUFFER_APPENDING,{type:l,data:u,frag:s,part:null,chunkMeta:i,parent:s.type})})}this.tickImmediate()}getMainFwdBufferInfo(){const e=this.mediaBuffer&&this.altAudio===2?this.mediaBuffer:this.media;return this.getFwdBufferInfo(e,U.MAIN)}get maxBufferLength(){const{levels:e,level:t}=this,s=e==null?void 0:e[t];return s?this.getMaxBufferLength(s.maxBitrate):this.config.maxBufferLength}backtrack(e){this.couldBacktrack=!0,this.backtrackFragment=e,this.resetTransmuxer(),this.flushBufferGap(e),this.fragmentTracker.removeFragment(e),this.fragPrevious=null,this.nextLoadPosition=e.start,this.state=C.IDLE}checkFragmentChanged(){const e=this.media;let t=null;if(e&&e.readyState>1&&e.seeking===!1){const s=e.currentTime;if(X.isBuffered(e,s)?t=this.getAppendedFrag(s):X.isBuffered(e,s+.1)&&(t=this.getAppendedFrag(s+.1)),t){this.backtrackFragment=null;const i=this.fragPlaying,r=t.level;(!i||t.sn!==i.sn||i.level!==r)&&(this.fragPlaying=t,this.hls.trigger(m.FRAG_CHANGED,{frag:t}),(!i||i.level!==r)&&this.hls.trigger(m.LEVEL_SWITCHED,{level:r}))}}}get nextLevel(){const e=this.nextBufferedFrag;return e?e.level:-1}get currentFrag(){var e;if(this.fragPlaying)return this.fragPlaying;const t=((e=this.media)==null?void 0:e.currentTime)||this.lastCurrentTime;return N(t)?this.getAppendedFrag(t):null}get currentProgramDateTime(){var e;const t=((e=this.media)==null?void 0:e.currentTime)||this.lastCurrentTime;if(N(t)){const s=this.getLevelDetails(),i=this.currentFrag||(s?mt(null,s.fragments,t):null);if(i){const r=i.programDateTime;if(r!==null){const n=r+(t-i.start)*1e3;return new Date(n)}}}return null}get currentLevel(){const e=this.currentFrag;return e?e.level:-1}get nextBufferedFrag(){const e=this.currentFrag;return e?this.followingBufferedFrag(e):null}get forceStartLoad(){return this._forceStartLoad}}class id extends we{constructor(e,t){super("key-loader",t),this.config=void 0,this.keyIdToKeyInfo={},this.emeController=null,this.config=e}abort(e){for(const s in this.keyIdToKeyInfo){const i=this.keyIdToKeyInfo[s].loader;if(i){var t;if(e&&e!==((t=i.context)==null?void 0:t.frag.type))return;i.abort()}}}detach(){for(const e in this.keyIdToKeyInfo){const t=this.keyIdToKeyInfo[e];(t.mediaKeySessionContext||t.decryptdata.isCommonEncryption)&&delete this.keyIdToKeyInfo[e]}}destroy(){this.detach();for(const e in this.keyIdToKeyInfo){const t=this.keyIdToKeyInfo[e].loader;t&&t.destroy()}this.keyIdToKeyInfo={}}createKeyLoadError(e,t=R.KEY_LOAD_ERROR,s,i,r){return new ze({type:H.NETWORK_ERROR,details:t,fatal:!1,frag:e,response:r,error:s,networkDetails:i})}loadClear(e,t,s){if(this.emeController&&this.config.emeEnabled&&!this.emeController.getSelectedKeySystemFormats().length){if(t.length)for(let i=0,r=t.length;i{if(!this.emeController)return;n.setKeyFormat(o);const c=us(o);if(c)return this.emeController.getKeySystemAccess([c])})}if(this.config.requireKeySystemAccessOnStart){const i=Ft(this.config);if(i.length)return this.emeController.getKeySystemAccess(i)}}return null}load(e){return!e.decryptdata&&e.encrypted&&this.emeController&&this.config.emeEnabled?this.emeController.selectKeySystemFormat(e).then(t=>this.loadInternal(e,t)):this.loadInternal(e)}loadInternal(e,t){var s,i;t&&e.setKeyFormat(t);const r=e.decryptdata;if(!r){const l=new Error(t?`Expected frag.decryptdata to be defined after setting format ${t}`:`Missing decryption data on fragment in onKeyLoading (emeEnabled with controller: ${this.emeController&&this.config.emeEnabled})`);return Promise.reject(this.createKeyLoadError(e,R.KEY_LOAD_ERROR,l))}const n=r.uri;if(!n)return Promise.reject(this.createKeyLoadError(e,R.KEY_LOAD_ERROR,new Error(`Invalid key URI: "${n}"`)));const o=ti(r);let c=this.keyIdToKeyInfo[o];if((s=c)!=null&&s.decryptdata.key)return r.key=c.decryptdata.key,Promise.resolve({frag:e,keyInfo:c});if(this.emeController&&(i=c)!=null&&i.keyLoadPromise)switch(this.emeController.getKeyStatus(c.decryptdata)){case"usable":case"usable-in-future":return c.keyLoadPromise.then(h=>{const{keyInfo:u}=h;return r.key=u.decryptdata.key,{frag:e,keyInfo:u}})}switch(this.log(`${this.keyIdToKeyInfo[o]?"Rel":"L"}oading${r.keyId?" keyId: "+ye(r.keyId):""} URI: ${r.uri} from ${e.type} ${e.level}`),c=this.keyIdToKeyInfo[o]={decryptdata:r,keyLoadPromise:null,loader:null,mediaKeySessionContext:null},r.method){case"SAMPLE-AES":case"SAMPLE-AES-CENC":case"SAMPLE-AES-CTR":return r.keyFormat==="identity"?this.loadKeyHTTP(c,e):this.loadKeyEME(c,e);case"AES-128":case"AES-256":case"AES-256-CTR":return this.loadKeyHTTP(c,e);default:return Promise.reject(this.createKeyLoadError(e,R.KEY_LOAD_ERROR,new Error(`Key supplied with unsupported METHOD: "${r.method}"`)))}}loadKeyEME(e,t){const s={frag:t,keyInfo:e};if(this.emeController&&this.config.emeEnabled){var i;if(!e.decryptdata.keyId&&(i=t.initSegment)!=null&&i.data){const n=xo(t.initSegment.data);if(n.length){let o=n[0];o.some(c=>c!==0)?(this.log(`Using keyId found in init segment ${ye(o)}`),rt.setKeyIdForUri(e.decryptdata.uri,o)):(o=rt.addKeyIdForUri(e.decryptdata.uri),this.log(`Generating keyId to patch media ${ye(o)}`)),e.decryptdata.keyId=o}}if(!e.decryptdata.keyId&&!ue(t))return Promise.resolve(s);const r=this.emeController.loadKey(s);return(e.keyLoadPromise=r.then(n=>(e.mediaKeySessionContext=n,s))).catch(n=>{throw e.keyLoadPromise=null,"data"in n&&(n.data.frag=t),n})}return Promise.resolve(s)}loadKeyHTTP(e,t){const s=this.config,i=s.loader,r=new i(s);return t.keyLoader=e.loader=r,e.keyLoadPromise=new Promise((n,o)=>{const c={keyInfo:e,frag:t,responseType:"arraybuffer",url:e.decryptdata.uri},l=s.keyLoadPolicy.default,h={loadPolicy:l,timeout:l.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0},u={onSuccess:(d,f,g,y)=>{const{frag:p,keyInfo:E}=g,T=ti(E.decryptdata);if(!p.decryptdata||E!==this.keyIdToKeyInfo[T])return o(this.createKeyLoadError(p,R.KEY_LOAD_ERROR,new Error("after key load, decryptdata unset or changed"),y));E.decryptdata.key=p.decryptdata.key=new Uint8Array(d.data),p.keyLoader=null,E.loader=null,n({frag:p,keyInfo:E})},onError:(d,f,g,y)=>{this.resetLoader(f),o(this.createKeyLoadError(t,R.KEY_LOAD_ERROR,new Error(`HTTP Error ${d.code} loading key ${d.text}`),g,ie({url:c.url,data:void 0},d)))},onTimeout:(d,f,g)=>{this.resetLoader(f),o(this.createKeyLoadError(t,R.KEY_LOAD_TIMEOUT,new Error("key loading timed out"),g))},onAbort:(d,f,g)=>{this.resetLoader(f),o(this.createKeyLoadError(t,R.INTERNAL_ABORTED,new Error("key loading aborted"),g))}};r.load(c,h,u)})}resetLoader(e){const{frag:t,keyInfo:s,url:i}=e,r=s.loader;t.keyLoader===r&&(t.keyLoader=null,s.loader=null);const n=ti(s.decryptdata)||i;delete this.keyIdToKeyInfo[n],r&&r.destroy()}}function ti(a){if(a.keyFormat!==Ee.FAIRPLAY){const e=a.keyId;if(e)return ye(e)}return a.uri}function pn(a){const{type:e}=a;switch(e){case J.AUDIO_TRACK:return U.AUDIO;case J.SUBTITLE_TRACK:return U.SUBTITLE;default:return U.MAIN}}function si(a,e){let t=a.url;return(t===void 0||t.indexOf("data:")===0)&&(t=e.url),t}class rd{constructor(e){this.hls=void 0,this.loaders=Object.create(null),this.variableList=null,this.onManifestLoaded=this.checkAutostartLoad,this.hls=e,this.registerListeners()}startLoad(e){}stopLoad(){this.destroyInternalLoaders()}registerListeners(){const{hls:e}=this;e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.LEVEL_LOADING,this.onLevelLoading,this),e.on(m.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),e.on(m.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this),e.on(m.LEVELS_UPDATED,this.onLevelsUpdated,this)}unregisterListeners(){const{hls:e}=this;e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.LEVEL_LOADING,this.onLevelLoading,this),e.off(m.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),e.off(m.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this),e.off(m.LEVELS_UPDATED,this.onLevelsUpdated,this)}createInternalLoader(e){const t=this.hls.config,s=t.pLoader,i=t.loader,r=s||i,n=new r(t);return this.loaders[e.type]=n,n}getInternalLoader(e){return this.loaders[e.type]}resetInternalLoader(e){this.loaders[e]&&delete this.loaders[e]}destroyInternalLoaders(){for(const e in this.loaders){const t=this.loaders[e];t&&t.destroy(),this.resetInternalLoader(e)}}destroy(){this.variableList=null,this.unregisterListeners(),this.destroyInternalLoaders()}onManifestLoading(e,t){const{url:s}=t;this.variableList=null,this.load({id:null,level:0,responseType:"text",type:J.MANIFEST,url:s,deliveryDirectives:null,levelOrTrack:null})}onLevelLoading(e,t){const{id:s,level:i,pathwayId:r,url:n,deliveryDirectives:o,levelInfo:c}=t;this.load({id:s,level:i,pathwayId:r,responseType:"text",type:J.LEVEL,url:n,deliveryDirectives:o,levelOrTrack:c})}onAudioTrackLoading(e,t){const{id:s,groupId:i,url:r,deliveryDirectives:n,track:o}=t;this.load({id:s,groupId:i,level:null,responseType:"text",type:J.AUDIO_TRACK,url:r,deliveryDirectives:n,levelOrTrack:o})}onSubtitleTrackLoading(e,t){const{id:s,groupId:i,url:r,deliveryDirectives:n,track:o}=t;this.load({id:s,groupId:i,level:null,responseType:"text",type:J.SUBTITLE_TRACK,url:r,deliveryDirectives:n,levelOrTrack:o})}onLevelsUpdated(e,t){const s=this.loaders[J.LEVEL];if(s){const i=s.context;i&&!t.levels.some(r=>r===i.levelOrTrack)&&(s.abort(),delete this.loaders[J.LEVEL])}}load(e){var t;const s=this.hls.config;let i=this.getInternalLoader(e);if(i){const l=this.hls.logger,h=i.context;if(h&&h.levelOrTrack===e.levelOrTrack&&(h.url===e.url||h.deliveryDirectives&&!e.deliveryDirectives)){h.url===e.url?l.log(`[playlist-loader]: ignore ${e.url} ongoing request`):l.log(`[playlist-loader]: ignore ${e.url} in favor of ${h.url}`);return}l.log(`[playlist-loader]: aborting previous loader for type: ${e.type}`),i.abort()}let r;if(e.type===J.MANIFEST?r=s.manifestLoadPolicy.default:r=ne({},s.playlistLoadPolicy.default,{timeoutRetry:null,errorRetry:null}),i=this.createInternalLoader(e),N((t=e.deliveryDirectives)==null?void 0:t.part)){let l;if(e.type===J.LEVEL&&e.level!==null?l=this.hls.levels[e.level].details:e.type===J.AUDIO_TRACK&&e.id!==null?l=this.hls.audioTracks[e.id].details:e.type===J.SUBTITLE_TRACK&&e.id!==null&&(l=this.hls.subtitleTracks[e.id].details),l){const h=l.partTarget,u=l.targetduration;if(h&&u){const d=Math.max(h*3,u*.8)*1e3;r=ne({},r,{maxTimeToFirstByteMs:Math.min(d,r.maxTimeToFirstByteMs),maxLoadTimeMs:Math.min(d,r.maxTimeToFirstByteMs)})}}}const n=r.errorRetry||r.timeoutRetry||{},o={loadPolicy:r,timeout:r.maxLoadTimeMs,maxRetry:n.maxNumRetry||0,retryDelay:n.retryDelayMs||0,maxRetryDelay:n.maxRetryDelayMs||0},c={onSuccess:(l,h,u,d)=>{const f=this.getInternalLoader(u);this.resetInternalLoader(u.type);const g=l.data;h.parsing.start=performance.now(),Ye.isMediaPlaylist(g)||u.type!==J.MANIFEST?this.handleTrackOrLevelPlaylist(l,h,u,d||null,f):this.handleMasterPlaylist(l,h,u,d)},onError:(l,h,u,d)=>{this.handleNetworkError(h,u,!1,l,d)},onTimeout:(l,h,u)=>{this.handleNetworkError(h,u,!0,void 0,l)}};i.load(e,o,c)}checkAutostartLoad(){if(!this.hls)return;const{config:{autoStartLoad:e,startPosition:t},forceStartLoad:s}=this.hls;(e||s)&&(this.hls.logger.log(`${e?"auto":"force"} startLoad with configured startPosition ${t}`),this.hls.startLoad(t))}handleMasterPlaylist(e,t,s,i){const r=this.hls,n=e.data,o=si(e,s),c=Ye.parseMasterPlaylist(n,o);if(c.playlistParsingError){t.parsing.end=performance.now(),this.handleManifestParsingError(e,s,c.playlistParsingError,i,t);return}const{contentSteering:l,levels:h,sessionData:u,sessionKeys:d,startTimeOffset:f,variableList:g}=c;this.variableList=g,h.forEach(T=>{const{unknownCodecs:S}=T;if(S){const{preferManagedMediaSource:v}=this.hls.config;let{audioCodec:x,videoCodec:D}=T;for(let A=S.length;A--;){const _=S[A];$t(_,"audio",v)?(T.audioCodec=x=x?`${x},${_}`:_,Ct.audio[x.substring(0,4)]=2,S.splice(A,1)):$t(_,"video",v)&&(T.videoCodec=D=D?`${D},${_}`:_,Ct.video[D.substring(0,4)]=2,S.splice(A,1))}}});const{AUDIO:y=[],SUBTITLES:p,"CLOSED-CAPTIONS":E}=Ye.parseMasterPlaylistMedia(n,o,c);y.length&&!y.some(S=>!S.url)&&h[0].audioCodec&&!h[0].attrs.AUDIO&&(this.hls.logger.log("[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one"),y.unshift({type:"main",name:"main",groupId:"main",default:!1,autoselect:!1,forced:!1,id:-1,attrs:new le({}),bitrate:0,url:""})),r.trigger(m.MANIFEST_LOADED,{levels:h,audioTracks:y,subtitles:p,captions:E,contentSteering:l,url:o,stats:t,networkDetails:i,sessionData:u,sessionKeys:d,startTimeOffset:f,variableList:g})}handleTrackOrLevelPlaylist(e,t,s,i,r){const n=this.hls,{id:o,level:c,type:l}=s,h=si(e,s),u=N(c)?c:N(o)?o:0,d=pn(s),f=Ye.parseLevelPlaylist(e.data,h,u,d,0,this.variableList);if(l===J.MANIFEST){const g={attrs:new le({}),bitrate:0,details:f,name:"",url:h};f.requestScheduled=t.loading.start+zn(f,0),n.trigger(m.MANIFEST_LOADED,{levels:[g],audioTracks:[],url:h,stats:t,networkDetails:i,sessionData:null,sessionKeys:null,contentSteering:null,startTimeOffset:null,variableList:null})}t.parsing.end=performance.now(),s.levelDetails=f,this.handlePlaylistLoaded(f,e,t,s,i,r)}handleManifestParsingError(e,t,s,i,r){this.hls.trigger(m.ERROR,{type:H.NETWORK_ERROR,details:R.MANIFEST_PARSING_ERROR,fatal:t.type===J.MANIFEST,url:e.url,err:s,error:s,reason:s.message,response:e,context:t,networkDetails:i,stats:r})}handleNetworkError(e,t,s=!1,i,r){let n=`A network ${s?"timeout":"error"+(i?" (status "+i.code+")":"")} occurred while loading ${e.type}`;e.type===J.LEVEL?n+=`: ${e.level} id: ${e.id}`:(e.type===J.AUDIO_TRACK||e.type===J.SUBTITLE_TRACK)&&(n+=` id: ${e.id} group-id: "${e.groupId}"`);const o=new Error(n);this.hls.logger.warn(`[playlist-loader]: ${n}`);let c=R.UNKNOWN,l=!1;const h=this.getInternalLoader(e);switch(e.type){case J.MANIFEST:c=s?R.MANIFEST_LOAD_TIMEOUT:R.MANIFEST_LOAD_ERROR,l=!0;break;case J.LEVEL:c=s?R.LEVEL_LOAD_TIMEOUT:R.LEVEL_LOAD_ERROR,l=!1;break;case J.AUDIO_TRACK:c=s?R.AUDIO_TRACK_LOAD_TIMEOUT:R.AUDIO_TRACK_LOAD_ERROR,l=!1;break;case J.SUBTITLE_TRACK:c=s?R.SUBTITLE_TRACK_LOAD_TIMEOUT:R.SUBTITLE_LOAD_ERROR,l=!1;break}h&&this.resetInternalLoader(e.type);const u={type:H.NETWORK_ERROR,details:c,fatal:l,url:e.url,loader:h,context:e,error:o,networkDetails:t,stats:r};if(i){const d=(t==null?void 0:t.url)||e.url;u.response=ie({url:d,data:void 0},i)}this.hls.trigger(m.ERROR,u)}handlePlaylistLoaded(e,t,s,i,r,n){const o=this.hls,{type:c,level:l,levelOrTrack:h,id:u,groupId:d,deliveryDirectives:f}=i,g=si(t,i),y=pn(i);let p=typeof i.level=="number"&&y===U.MAIN?l:void 0;const E=e.playlistParsingError;if(E){if(this.hls.logger.warn(`${E} ${e.url}`),!o.config.ignorePlaylistParsingErrors){o.trigger(m.ERROR,{type:H.NETWORK_ERROR,details:R.LEVEL_PARSING_ERROR,fatal:!1,url:g,error:E,reason:E.message,response:t,context:i,level:p,parent:y,networkDetails:r,stats:s});return}e.playlistParsingError=null}if(!e.fragments.length){const T=e.playlistParsingError=new Error("No Segments found in Playlist");o.trigger(m.ERROR,{type:H.NETWORK_ERROR,details:R.LEVEL_EMPTY_ERROR,fatal:!1,url:g,error:T,reason:T.message,response:t,context:i,level:p,parent:y,networkDetails:r,stats:s});return}switch(e.live&&n&&(n.getCacheAge&&(e.ageHeader=n.getCacheAge()||0),(!n.getCacheAge||isNaN(e.ageHeader))&&(e.ageHeader=0)),c){case J.MANIFEST:case J.LEVEL:if(p){if(!h)p=0;else if(h!==o.levels[p]){const T=o.levels.indexOf(h);T>-1&&(p=T)}}o.trigger(m.LEVEL_LOADED,{details:e,levelInfo:h||o.levels[0],level:p||0,id:u||0,stats:s,networkDetails:r,deliveryDirectives:f,withoutMultiVariant:c===J.MANIFEST});break;case J.AUDIO_TRACK:o.trigger(m.AUDIO_TRACK_LOADED,{details:e,track:h,id:u||0,groupId:d||"",stats:s,networkDetails:r,deliveryDirectives:f});break;case J.SUBTITLE_TRACK:o.trigger(m.SUBTITLE_TRACK_LOADED,{details:e,track:h,id:u||0,groupId:d||"",stats:s,networkDetails:r,deliveryDirectives:f});break}}}class Ve{static get version(){return Vt}static isMSESupported(){return Xa()}static isSupported(){return Ju()}static getMediaSource(){return nt()}static get Events(){return m}static get MetadataSchema(){return be}static get ErrorTypes(){return H}static get ErrorDetails(){return R}static get DefaultConfig(){return Ve.defaultConfig?Ve.defaultConfig:Gu}static set DefaultConfig(e){Ve.defaultConfig=e}constructor(e={}){this.config=void 0,this.userConfig=void 0,this.logger=void 0,this.coreComponents=void 0,this.networkControllers=void 0,this._emitter=new Oi,this._autoLevelCapping=-1,this._maxHdcpLevel=null,this.abrController=void 0,this.bufferController=void 0,this.capLevelController=void 0,this.latencyController=void 0,this.levelController=void 0,this.streamController=void 0,this.audioStreamController=void 0,this.subtititleStreamController=void 0,this.audioTrackController=void 0,this.subtitleTrackController=void 0,this.interstitialsController=void 0,this.gapController=void 0,this.emeController=void 0,this.cmcdController=void 0,this._media=null,this._url=null,this._sessionId=void 0,this.triggeringException=void 0,this.started=!1;const t=this.logger=co(e.debug||!1,"Hls instance",e.assetPlayerId),s=this.config=Vu(Ve.DefaultConfig,e,t);this.userConfig=e,s.progressive&&Hu(s,t);const{abrController:i,bufferController:r,capLevelController:n,errorController:o,fpsController:c}=s,l=new o(this),h=this.abrController=new i(this),u=new nl(this),d=s.interstitialsController,f=d?this.interstitialsController=new d(this,Ve):null,g=this.bufferController=new r(this,u),y=this.capLevelController=new n(this),p=new c(this),E=new rd(this),T=s.contentSteeringController,S=T?new T(this):null,v=this.levelController=new Zu(this,S),x=new zu(this),D=new id(this.config,this.logger),A=this.streamController=new sd(this,u,D),_=this.gapController=new ju(this,u);y.setStreamController(A),p.setStreamController(A);const b=[E,v,A];f&&b.splice(1,0,f),S&&b.splice(1,0,S),this.networkControllers=b;const I=[h,g,_,y,p,x,u];this.audioTrackController=this.createController(s.audioTrackController,b);const P=s.audioStreamController;P&&b.push(this.audioStreamController=new P(this,u,D)),this.subtitleTrackController=this.createController(s.subtitleTrackController,b);const F=s.subtitleStreamController;F&&b.push(this.subtititleStreamController=new F(this,u,D)),this.createController(s.timelineController,I),D.emeController=this.emeController=this.createController(s.emeController,I),this.cmcdController=this.createController(s.cmcdController,I),this.latencyController=this.createController(Qu,I),this.coreComponents=I,b.push(l);const $=l.onErrorOut;typeof $=="function"&&this.on(m.ERROR,$,l),this.on(m.MANIFEST_LOADED,E.onManifestLoaded,E)}createController(e,t){if(e){const s=new e(this);return t&&t.push(s),s}return null}on(e,t,s=this){this._emitter.on(e,t,s)}once(e,t,s=this){this._emitter.once(e,t,s)}removeAllListeners(e){this._emitter.removeAllListeners(e)}off(e,t,s=this,i){this._emitter.off(e,t,s,i)}listeners(e){return this._emitter.listeners(e)}emit(e,t,s){return this._emitter.emit(e,t,s)}trigger(e,t){if(this.config.debug)return this.emit(e,e,t);try{return this.emit(e,e,t)}catch(s){if(this.logger.error("An internal error happened while handling event "+e+'. Error message: "'+s.message+'". Here is a stacktrace:',s),!this.triggeringException){this.triggeringException=!0;const i=e===m.ERROR;this.trigger(m.ERROR,{type:H.OTHER_ERROR,details:R.INTERNAL_EXCEPTION,fatal:i,event:e,error:s}),this.triggeringException=!1}}return!1}listenerCount(e){return this._emitter.listenerCount(e)}destroy(){this.logger.log("destroy"),this.trigger(m.DESTROYING,void 0),this.detachMedia(),this.removeAllListeners(),this._autoLevelCapping=-1,this._url=null,this.networkControllers.forEach(t=>t.destroy()),this.networkControllers.length=0,this.coreComponents.forEach(t=>t.destroy()),this.coreComponents.length=0;const e=this.config;e.xhrSetup=e.fetchSetup=void 0,this.userConfig=null}attachMedia(e){if(!e||"media"in e&&!e.media){const r=new Error(`attachMedia failed: invalid argument (${e})`);this.trigger(m.ERROR,{type:H.OTHER_ERROR,details:R.ATTACH_MEDIA_ERROR,fatal:!0,error:r});return}this.logger.log("attachMedia"),this._media&&(this.logger.warn("media must be detached before attaching"),this.detachMedia());const t="media"in e,s=t?e.media:e,i=t?e:{media:s};this._media=s,this.trigger(m.MEDIA_ATTACHING,i)}detachMedia(){this.logger.log("detachMedia"),this.trigger(m.MEDIA_DETACHING,{}),this._media=null}transferMedia(){this._media=null;const e=this.bufferController.transferMedia();return this.trigger(m.MEDIA_DETACHING,{transferMedia:e}),e}loadSource(e){this.stopLoad();const t=this.media,s=this._url,i=this._url=Ai.buildAbsoluteURL(self.location.href,e,{alwaysNormalize:!0});this._autoLevelCapping=-1,this._maxHdcpLevel=null,this.logger.log(`loadSource:${i}`),t&&s&&(s!==i||this.bufferController.hasSourceTypes())&&(this.detachMedia(),this.attachMedia(t)),this.trigger(m.MANIFEST_LOADING,{url:e})}get url(){return this._url}get hasEnoughToStart(){return this.streamController.hasEnoughToStart}get startPosition(){return this.streamController.startPositionValue}startLoad(e=-1,t){this.logger.log(`startLoad(${e+(t?", ":"")})`),this.started=!0,this.resumeBuffering();for(let s=0;s{e.resumeBuffering&&e.resumeBuffering()}))}pauseBuffering(){this.bufferingEnabled&&(this.logger.log("pause buffering"),this.networkControllers.forEach(e=>{e.pauseBuffering&&e.pauseBuffering()}))}get inFlightFragments(){const e={[U.MAIN]:this.streamController.inFlightFrag};return this.audioStreamController&&(e[U.AUDIO]=this.audioStreamController.inFlightFrag),this.subtititleStreamController&&(e[U.SUBTITLE]=this.subtititleStreamController.inFlightFrag),e}swapAudioCodec(){this.logger.log("swapAudioCodec"),this.streamController.swapAudioCodec()}recoverMediaError(){this.logger.log("recoverMediaError");const e=this._media,t=e==null?void 0:e.currentTime;this.detachMedia(),e&&(this.attachMedia(e),t&&this.startLoad(t))}removeLevel(e){this.levelController.removeLevel(e)}get sessionId(){let e=this._sessionId;return e||(e=this._sessionId=jh()),e}get levels(){const e=this.levelController.levels;return e||[]}get latestLevelDetails(){return this.streamController.getLevelDetails()||null}get loadLevelObj(){return this.levelController.loadLevelObj}get currentLevel(){return this.streamController.currentLevel}set currentLevel(e){this.logger.log(`set currentLevel:${e}`),this.levelController.manualLevel=e,this.streamController.immediateLevelSwitch()}get nextLevel(){return this.streamController.nextLevel}set nextLevel(e){this.logger.log(`set nextLevel:${e}`),this.levelController.manualLevel=e,this.streamController.nextLevelSwitch()}get loadLevel(){return this.levelController.level}set loadLevel(e){this.logger.log(`set loadLevel:${e}`),this.levelController.manualLevel=e}get nextLoadLevel(){return this.levelController.nextLoadLevel}set nextLoadLevel(e){this.levelController.nextLoadLevel=e}get firstLevel(){return Math.max(this.levelController.firstLevel,this.minAutoLevel)}set firstLevel(e){this.logger.log(`set firstLevel:${e}`),this.levelController.firstLevel=e}get startLevel(){const e=this.levelController.startLevel;return e===-1&&this.abrController.forcedAutoLevel>-1?this.abrController.forcedAutoLevel:e}set startLevel(e){this.logger.log(`set startLevel:${e}`),e!==-1&&(e=Math.max(e,this.minAutoLevel)),this.levelController.startLevel=e}get capLevelToPlayerSize(){return this.config.capLevelToPlayerSize}set capLevelToPlayerSize(e){const t=!!e;t!==this.config.capLevelToPlayerSize&&(t?this.capLevelController.startCapping():(this.capLevelController.stopCapping(),this.autoLevelCapping=-1,this.streamController.nextLevelSwitch()),this.config.capLevelToPlayerSize=t)}get autoLevelCapping(){return this._autoLevelCapping}get bandwidthEstimate(){const{bwEstimator:e}=this.abrController;return e?e.getEstimate():NaN}set bandwidthEstimate(e){this.abrController.resetEstimator(e)}get abrEwmaDefaultEstimate(){const{bwEstimator:e}=this.abrController;return e?e.defaultEstimate:NaN}get ttfbEstimate(){const{bwEstimator:e}=this.abrController;return e?e.getEstimateTTFB():NaN}set autoLevelCapping(e){this._autoLevelCapping!==e&&(this.logger.log(`set autoLevelCapping:${e}`),this._autoLevelCapping=e,this.levelController.checkMaxAutoUpdated())}get maxHdcpLevel(){return this._maxHdcpLevel}set maxHdcpLevel(e){Vo(e)&&this._maxHdcpLevel!==e&&(this._maxHdcpLevel=e,this.levelController.checkMaxAutoUpdated())}get autoLevelEnabled(){return this.levelController.manualLevel===-1}get manualLevel(){return this.levelController.manualLevel}get minAutoLevel(){const{levels:e,config:{minAutoBitrate:t}}=this;if(!e)return 0;const s=e.length;for(let i=0;i=t)return i;return 0}get maxAutoLevel(){const{levels:e,autoLevelCapping:t,maxHdcpLevel:s}=this;let i;if(t===-1&&e!=null&&e.length?i=e.length-1:i=t,s)for(let r=i;r--;){const n=e[r].attrs["HDCP-LEVEL"];if(n&&n<=s)return r}return i}get firstAutoLevel(){return this.abrController.firstAutoLevel}get nextAutoLevel(){return this.abrController.nextAutoLevel}set nextAutoLevel(e){this.abrController.nextAutoLevel=e}get playingDate(){return this.streamController.currentProgramDateTime}get mainForwardBufferInfo(){return this.streamController.getMainFwdBufferInfo()}get maxBufferLength(){return this.streamController.maxBufferLength}setAudioOption(e){var t;return((t=this.audioTrackController)==null?void 0:t.setAudioOption(e))||null}setSubtitleOption(e){var t;return((t=this.subtitleTrackController)==null?void 0:t.setSubtitleOption(e))||null}get allAudioTracks(){const e=this.audioTrackController;return e?e.allAudioTracks:[]}get audioTracks(){const e=this.audioTrackController;return e?e.audioTracks:[]}get audioTrack(){const e=this.audioTrackController;return e?e.audioTrack:-1}set audioTrack(e){const t=this.audioTrackController;t&&(t.audioTrack=e)}get allSubtitleTracks(){const e=this.subtitleTrackController;return e?e.allSubtitleTracks:[]}get subtitleTracks(){const e=this.subtitleTrackController;return e?e.subtitleTracks:[]}get subtitleTrack(){const e=this.subtitleTrackController;return e?e.subtitleTrack:-1}get media(){return this._media}set subtitleTrack(e){const t=this.subtitleTrackController;t&&(t.subtitleTrack=e)}get subtitleDisplay(){const e=this.subtitleTrackController;return e?e.subtitleDisplay:!1}set subtitleDisplay(e){const t=this.subtitleTrackController;t&&(t.subtitleDisplay=e)}get lowLatencyMode(){return this.config.lowLatencyMode}set lowLatencyMode(e){this.config.lowLatencyMode=e}get liveSyncPosition(){return this.latencyController.liveSyncPosition}get latency(){return this.latencyController.latency}get maxLatency(){return this.latencyController.maxLatency}get targetLatency(){return this.latencyController.targetLatency}set targetLatency(e){this.latencyController.targetLatency=e}get drift(){return this.latencyController.drift}get forceStartLoad(){return this.streamController.forceStartLoad}get pathways(){return this.levelController.pathways}get pathwayPriority(){return this.levelController.pathwayPriority}set pathwayPriority(e){this.levelController.pathwayPriority=e}get bufferedToEnd(){var e;return!!((e=this.bufferController)!=null&&e.bufferedToEnd)}get interstitialsManager(){var e;return((e=this.interstitialsController)==null?void 0:e.interstitialsManager)||null}getMediaDecodingInfo(e,t=this.allAudioTracks){const s=Fn(t);return wn(e,s,navigator.mediaCapabilities)}}Ve.defaultConfig=void 0;class nd{constructor({onState:e}){this.audio=new Audio,this.audio.preload="none",this.hls=null,this.station=null,this.onState=e||(()=>{}),this.audio.addEventListener("playing",()=>this.emit({playing:!0,loading:!1,error:null})),this.audio.addEventListener("pause",()=>this.emit({playing:!1,loading:!1})),this.audio.addEventListener("waiting",()=>this.emit({loading:!0})),this.audio.addEventListener("error",()=>{var r;const t=(r=this.audio.error)==null?void 0:r.code,i={1:"aborted",2:"network",3:"decode",4:"src not supported"}[t]||`code ${t}`;console.warn("[player] audio error",i,this.audio.currentSrc),this.emit({playing:!1,loading:!1,error:`stream error: ${i}`})})}emit(e){var t,s,i;this.onState({stationId:((t=this.station)==null?void 0:t.id)??null,stationName:((s=this.station)==null?void 0:s.name)??null,genres:((i=this.station)==null?void 0:i.genres)||[],volume:this.audio.volume,...e})}setVolume(e){this.audio.volume=Math.max(0,Math.min(1,e)),this.emit({})}stop(){this.audio.pause(),this.audio.removeAttribute("src"),this.audio.load(),this.hls&&(this.hls.destroy(),this.hls=null)}togglePause(){this.station&&(this.audio.paused?this.audio.play().catch(()=>{}):this.audio.pause())}async play(e){this.stop(),this.station=e,this.emit({playing:!1,loading:!0});let t;try{t=(await Pe.post(`/api/stations/${e.id}/resolve`)).resolved}catch(i){this.emit({playing:!1,loading:!1,error:i.message});return}const s=t.url;t.format==="hls"?Ve.isSupported()?(this.hls=new Ve({enableWorker:!0}),this.hls.loadSource(s),this.hls.attachMedia(this.audio),this.hls.on(Ve.Events.MANIFEST_PARSED,()=>this.audio.play().catch(()=>{}))):this.audio.canPlayType("application/vnd.apple.mpegurl")?(this.audio.src=s,this.audio.play().catch(()=>{})):this.emit({playing:!1,loading:!1,error:"HLS not supported"}):(this.audio.src=s,this.audio.play().catch(()=>{}))}}const Lt=document.getElementById("app"),j={user:null,tab:"favorites",stations:[],categories:[],selectedCategory:null,favorites:[],history:[],query:"",player:{stationId:null,stationName:null,genres:[],playing:!1,loading:!1,volume:.7}},Je=new nd({onState:a=>{j.player={...j.player,...a},ot()}});async function za(){try{j.user=await Pe.get("/api/auth/me")}catch{od();return}await qi(),eo(ad),ot(),md()}async function qi(){const[a,e,t,s]=await Promise.all([Pe.get("/api/stations"),Pe.get("/api/me/favorites").catch(()=>[]),Pe.get("/api/me/history").catch(()=>[]),Pe.get("/api/v1/categories").catch(()=>[])]);j.stations=a,j.favorites=e,j.history=t,j.categories=s}function ad(a){if(a.type==="command")if(a.action==="play"&&a.stationId){const e=j.stations.find(t=>t.id===a.stationId);e&&Je.play(e)}else a.action==="pause"?Je.togglePause():a.action==="volume"?Je.setVolume(a.value):a.action==="stop"&&Je.stop()}function od(){xi(Lt);const a=M("div",{class:"login"},M("form",{onSubmit:async e=>{e.preventDefault();const t=new FormData(e.target);try{j.user=await Pe.post("/api/auth/login",{username:t.get("username"),password:t.get("password")}),await za()}catch(s){e.target.querySelector(".err").textContent=s.message}}},M("h1",{},"Sign in"),M("input",{name:"username",placeholder:"Username",autocomplete:"username",required:!0}),M("input",{name:"password",type:"password",placeholder:"Password",autocomplete:"current-password",required:!0}),M("div",{class:"err"}),M("button",{type:"submit"},"Continue")));Lt.appendChild(a)}let Ze=0;function ot(){if(!j.user)return;const a=Lt.querySelector(".grid");a&&a.scrollTop>0&&(Ze=a.scrollTop),qt(),xi(Lt);const e=j.player,t=new Set(j.favorites.map(c=>c.id)),s=M("section",{class:"now"},M("div",{class:"meta"},M("div",{class:"name"},e.stationName||"Select a station"),M("div",{class:"sub"},e.loading?"Connecting…":e.playing?"On air":e.error?e.error:e.stationId?"Paused":"Idle"),M("div",{class:"tags"},...(e.genres||[]).slice(0,4).map(c=>M("span",{class:"tag"},c)))),M("div",{class:"controls"},M("button",{class:`btn-play ${e.loading?"loading":""}`,title:e.playing?"Pause":"Play",onClick:()=>e.stationId?Je.togglePause():j.favorites[0]&&Je.play(j.favorites[0])},e.playing?"❚❚":"β–Ά"),M("button",{class:"btn-stop",title:"Stop",disabled:!e.stationId,onClick:()=>Je.stop()},"β– "),M("div",{class:"vol"},M("span",{class:"vol-icon"},e.volume===0?"πŸ”‡":e.volume<.5?"πŸ”ˆ":"πŸ”Š"),M("input",{type:"range",min:0,max:1,step:.05,value:e.volume,"aria-label":"Volume",onInput:c=>Je.setVolume(Number(c.target.value))}),M("span",{class:"val"},Math.round(e.volume*100))))),i=j.user.role==="admin",r=M("div",{class:"header"},M("div",{class:"tabs"},...["favorites","browse","recent"].map(c=>M("button",{class:`tab ${j.tab===c?"active":""}`,onClick:()=>{j.tab=c,Ze=0,ot()}},c==="favorites"?"β˜… Favorites":c==="browse"?"🌐 Browse":"⏱ Recent"))),M("div",{class:"header-tools"},M("input",{class:"search",type:"search",placeholder:"Search…",value:j.query,onInput:c=>{j.query=c.target.value,hd()}}),i?M("button",{class:"btn-add",title:"Add station",onClick:gd},"+"):null)),n=M("section",{class:"lib"},r);j.tab==="browse"&&j.categories.length&&n.appendChild(ld());const o=M("div",{class:"grid"});o.id="grid",o.addEventListener("scroll",()=>{Ze=o.scrollTop},{passive:!0}),n.appendChild(o),Lt.appendChild(s),Lt.appendChild(n),Qa(o,t),Ze&&(o.scrollTop=Ze,requestAnimationFrame(()=>{Ze&&(o.scrollTop=Ze)}))}function ld(){return M("div",{class:"chips"},M("button",{class:`chip ${j.selectedCategory?"":"active"}`,onClick:()=>{j.selectedCategory=null,Ze=0,ot()}},`All (${j.stations.length})`),...j.categories.filter(a=>a.count>0).map(a=>M("button",{class:`chip ${j.selectedCategory===a.id?"active":""}`,onClick:()=>{j.selectedCategory=a.id,Ze=0,ot()}},`${a.icon||""} ${a.label} (${a.count})`.trim())))}function cd(){let a=[];if(j.tab==="favorites")a=j.favorites;else if(j.tab==="browse")a=j.stations,j.selectedCategory&&(a=a.filter(t=>t.category===j.selectedCategory));else if(j.tab==="recent"){const t=new Set;a=j.history.filter(s=>!t.has(s.station_id)&&t.add(s.station_id)).map(s=>j.stations.find(i=>i.id===s.station_id)).filter(Boolean)}const e=j.query.trim().toLowerCase();return e&&(a=a.filter(t=>t.name.toLowerCase().includes(e)||(t.country||"").toLowerCase().includes(e)||(t.genres||[]).some(s=>s.toLowerCase().includes(e)))),a}function hd(){const a=document.getElementById("grid");if(!a)return;const e=new Set(j.favorites.map(t=>t.id));Qa(a,e)}function Qa(a,e){xi(a);const t=cd();if(!t.length){a.appendChild(M("div",{class:"empty"},j.tab==="favorites"?"No favorites yet β€” long-press or tap β˜… on a station.":j.query?"No matches.":"Nothing here yet."));return}const s=j.player;for(const i of t){const r=M("div",{class:`card ${s.stationId===i.id?"playing":""}`,role:"button",tabindex:0,onClick:()=>{Je.play(i),dd(i.id)},onContextMenu:n=>{n.preventDefault(),yn(n.clientX,n.clientY,i)}},M("div",{class:"art"},i.image_url?M("img",{class:"art-img",src:i.image_url,alt:"",loading:"lazy",referrerpolicy:"no-referrer",onError:n=>{const o=n.target.parentNode;n.target.remove(),o&&o.appendChild(M("span",{class:"art-glyph"},"β™ͺ"))}}):M("span",{class:"art-glyph"},"β™ͺ")),M("div",{class:"card-body"},M("div",{class:"n"},i.name),M("div",{class:"g"},(i.genres||[]).slice(0,3).join(" Β· ")||i.country||"β€”")),M("button",{class:`fav ${e.has(i.id)?"on":""}`,title:e.has(i.id)?"Remove favorite":"Add favorite",onClick:n=>{n.stopPropagation(),ud(i)}},e.has(i.id)?"β˜…":"β˜†"),M("button",{class:"more",title:"API endpoints",onClick:n=>{n.stopPropagation();const o=n.currentTarget.getBoundingClientRect();yn(o.right,o.bottom,i)}},"β‹―"));a.appendChild(r)}}async function ud(a){j.favorites.some(t=>t.id===a.id)?await Pe.del(`/api/me/favorites/${a.id}`):await Pe.put(`/api/me/favorites/${a.id}`,{position:j.favorites.length}),j.favorites=await Pe.get("/api/me/favorites"),ot()}function dd(a){j.history.unshift({station_id:a,started_at:new Date().toISOString()})}let Ne=null;function qt(){Ne&&(Ne.remove(),Ne=null)}function fd(a){if(!a.uuid)return[];const e=`${location.origin}/api/v1`;return[{label:"Station detail",url:`${e}/stations/${a.uuid}`},{label:"Stream redirect",url:`${e}/stations/${a.uuid}/stream`},{label:"MP3 stream",url:`${e}/stations/${a.uuid}/stream?format=mp3`},{label:"AAC stream",url:`${e}/stations/${a.uuid}/stream?format=aac`},{label:"HLS stream",url:`${e}/stations/${a.uuid}/stream?format=hls`},{label:"All stations",url:`${e}/stations`},{label:"Health",url:`${e}/health`}]}function yn(a,e,t){qt();const s=fd(t);Ne=M("div",{class:"ctx-menu",role:"menu"},M("div",{class:"ctx-title"},t.name),M("div",{class:"ctx-sub"},t.uuid?`uuid Β· ${t.uuid}`:"no uuid"),...s.length?s.map(c=>M("div",{class:"ctx-row"},M("div",{class:"ctx-row-text"},M("div",{class:"ctx-label"},c.label),M("div",{class:"ctx-url"},c.url)),M("button",{class:"ctx-btn",title:"Copy",onClick:async l=>{l.stopPropagation();try{await navigator.clipboard.writeText(c.url),Mt("Copied")}catch{Mt("Copy failed")}}},"⧉"),M("button",{class:"ctx-btn",title:"Open",onClick:l=>{l.stopPropagation(),window.open(c.url,"_blank","noopener")}},"β†—"))):[M("div",{class:"ctx-empty"},"No public API for this station yet (missing uuid).")],j.user.role==="admin"?M("button",{class:"ctx-danger",onClick:async()=>{if(qt(),!!confirm(`Delete ${t.name}?`))try{await Pe.del(`/api/stations/${t.id}`),await qi(),ot(),Mt("Deleted")}catch(c){Mt(c.message||"Delete failed")}}},"πŸ—‘ Delete"):null),document.body.appendChild(Ne);const i=Ne.offsetWidth,r=Ne.offsetHeight,n=Math.min(a,window.innerWidth-i-8),o=Math.min(e,window.innerHeight-r-8);Ne.style.left=`${Math.max(8,n)}px`,Ne.style.top=`${Math.max(8,o)}px`}document.addEventListener("click",a=>{Ne&&!Ne.contains(a.target)&&qt()});document.addEventListener("keydown",a=>{a.key==="Escape"&&qt()});async function gd(){const a=document.createElement("dialog");a.className="add-station";const e={name:"",country:"",genres:"",image_url:"",homepage:"",streamUrl:"",streamFormat:"mp3"},t=M("div",{class:"err"});a.appendChild(M("form",{method:"dialog",onSubmit:async s=>{s.preventDefault(),t.textContent="";const i={name:e.name.trim(),country:e.country.trim()||null,homepage:e.homepage.trim()||null,image_url:e.image_url.trim()||null,genres:e.genres.split(",").map(r=>r.trim()).filter(Boolean),streams:e.streamUrl.trim()?[{url:e.streamUrl.trim(),format:e.streamFormat,priority:0}]:[]};if(!i.name){t.textContent="Name is required.";return}try{await Pe.post("/api/stations",i),a.close(),await qi(),ot(),Mt("Station added")}catch(r){t.textContent=r.message||"Failed to add station"}}},M("h2",{},"Add station"),M("label",{},"Name",M("input",{required:!0,autofocus:!0,onInput:s=>e.name=s.target.value})),M("div",{class:"row2"},M("label",{},"Country",M("input",{maxlength:4,placeholder:"NL",onInput:s=>e.country=s.target.value})),M("label",{},"Genres",M("input",{placeholder:"jazz, electronic",onInput:s=>e.genres=s.target.value}))),M("label",{},"Homepage",M("input",{type:"url",placeholder:"https://…",onInput:s=>e.homepage=s.target.value})),M("label",{},"Image URL",M("input",{type:"url",placeholder:"https://…/logo.png",onInput:s=>e.image_url=s.target.value})),M("div",{class:"row2"},M("label",{},"Stream URL",M("input",{type:"url",placeholder:"https://…/stream",onInput:s=>e.streamUrl=s.target.value})),M("label",{},"Format",M("select",{onChange:s=>e.streamFormat=s.target.value},...["mp3","aac","ogg","hls","m3u","pls","unknown"].map(s=>M("option",{value:s,selected:s==="mp3"},s))))),t,M("div",{class:"actions"},M("button",{class:"btn-ghost",type:"button",onClick:()=>a.close()},"Cancel"),M("button",{class:"btn-primary",type:"submit"},"Add")))),document.body.appendChild(a),a.showModal(),a.addEventListener("close",()=>a.remove())}let En=null;function Mt(a){const e=document.querySelector(".toast");e&&e.remove();const t=M("div",{class:"toast"},a);document.body.appendChild(t),clearTimeout(En),En=setTimeout(()=>t.remove(),2200)}async function md(){var a;try{await((a=navigator.wakeLock)==null?void 0:a.request("screen"))}catch{}document.addEventListener("visibilitychange",()=>{var e;document.visibilityState==="visible"&&((e=navigator.wakeLock)==null||e.request("screen").catch(()=>{}))})}document.addEventListener("contextmenu",a=>{window.matchMedia("(display-mode: fullscreen)").matches&&a.preventDefault()});za(); +`),l=[],h=e?Pc(e.baseTime,e.timescale):0;let d="00:00.000",u=0,f=0,g,y=!0;o.oncue=function(p){const E=t[s];let T=t.ccOffset;const S=(u-h)/9e4;if(E!=null&&E.new&&(f!==void 0?T=t.ccOffset=E.start:Id(t,s,S)),S){if(!e){g=new Error("Missing initPTS for VTT MPEGTS");return}T=S-t.presentationOffset}const v=p.endTime-p.startTime,x=be((p.startTime+T-f)*9e4,i*9e4)/9e4;p.startTime=Math.max(x,0),p.endTime=Math.max(x+v,0);const D=p.text.trim();p.text=decodeURIComponent(encodeURIComponent(D)),p.id||(p.id=ji(p.startTime,p.endTime,D)),p.endTime>0&&l.push(p)},o.onparsingerror=function(p){g=p},o.onflush=function(){if(g){n(g);return}r(l)},c.forEach(p=>{if(y)if(zs(p,"X-TIMESTAMP-MAP=")){y=!1,p.slice(16).split(",").forEach(E=>{zs(E,"LOCAL:")?d=E.slice(6):zs(E,"MPEGTS:")&&(u=parseInt(E.slice(7)))});try{f=Ad(d)/1e3}catch(E){g=E}return}else p===""&&(y=!1);o.parse(p+` +`)}),o.flush()}const Qs="stpp.ttml.im1t",Wa=/^(\d{2,}):(\d{2}):(\d{2}):(\d{2})\.?(\d+)?$/,qa=/^(\d*(?:\.\d*)?)(h|m|s|ms|f|t)$/,Rd={left:"start",center:"center",right:"end",start:"start",end:"end"};function cn(a,e,t,s){const i=Z(new Uint8Array(a),["mdat"]);if(i.length===0){s(new Error("Could not parse IMSC1 mdat"));return}const r=i.map(o=>De(o)),n=Cc(e.baseTime,1,e.timescale);try{r.forEach(o=>t(bd(o,n)))}catch(o){s(o)}}function bd(a,e){const i=new DOMParser().parseFromString(a,"text/xml").getElementsByTagName("tt")[0];if(!i)throw new Error("Invalid ttml");const r={frameRate:30,subFrameRate:1,frameRateMultiplier:0,tickRate:0},n=Object.keys(r).reduce((d,u)=>(d[u]=i.getAttribute(`ttp:${u}`)||r[u],d),{}),o=i.getAttribute("xml:space")!=="preserve",c=hn(Zs(i,"styling","style")),l=hn(Zs(i,"layout","region")),h=Zs(i,"body","[begin]");return[].map.call(h,d=>{const u=ja(d,o);if(!u||!d.hasAttribute("begin"))return null;const f=ei(d.getAttribute("begin"),n),g=ei(d.getAttribute("dur"),n);let y=ei(d.getAttribute("end"),n);if(f===null)throw dn(d);if(y===null){if(g===null)throw dn(d);y=f+g}const p=new qi(f-e,y-e,u);p.id=ji(p.startTime,p.endTime,p.text);const E=l[d.getAttribute("region")],T=c[d.getAttribute("style")],S=_d(E,T,c),{textAlign:v}=S;if(v){const x=Rd[v];x&&(p.lineAlign=x),p.align=v}return ne(p,S),p}).filter(d=>d!==null)}function Zs(a,e,t){const s=a.getElementsByTagName(e)[0];return s?[].slice.call(s.querySelectorAll(t)):[]}function hn(a){return a.reduce((e,t)=>{const s=t.getAttribute("xml:id");return s&&(e[s]=t),e},{})}function ja(a,e){return[].slice.call(a.childNodes).reduce((t,s,i)=>{var r;return s.nodeName==="br"&&i?t+` +`:(r=s.childNodes)!=null&&r.length?ja(s,e):e?t+s.textContent.trim().replace(/\s+/g," "):t+s.textContent},"")}function _d(a,e,t){const s="http://www.w3.org/ns/ttml#styling";let i=null;const r=["displayAlign","textAlign","color","backgroundColor","fontSize","fontFamily"],n=a!=null&&a.hasAttribute("style")?a.getAttribute("style"):null;return n&&t.hasOwnProperty(n)&&(i=t[n]),r.reduce((o,c)=>{const l=Js(e,s,c)||Js(a,s,c)||Js(i,s,c);return l&&(o[c]=l),o},{})}function Js(a,e,t){return a&&a.hasAttributeNS(e,t)?a.getAttributeNS(e,t):null}function dn(a){return new Error(`Could not parse ttml timestamp ${a}`)}function ei(a,e){if(!a)return null;let t=Va(a);return t===null&&(Wa.test(a)?t=Dd(a,e):qa.test(a)&&(t=Cd(a,e))),t}function Dd(a,e){const t=Wa.exec(a),s=(t[4]|0)+(t[5]|0)/e.subFrameRate;return(t[1]|0)*3600+(t[2]|0)*60+(t[3]|0)+s/e.frameRate}function Cd(a,e){const t=qa.exec(a),s=Number(t[1]);switch(t[2]){case"h":return s*3600;case"m":return s*60;case"ms":return s*1e3;case"f":return s/e.frameRate;case"t":return s/e.tickRate}return s}class os{constructor(e,t){this.timelineController=void 0,this.cueRanges=[],this.trackName=void 0,this.startTime=null,this.endTime=null,this.screen=null,this.timelineController=e,this.trackName=t}dispatchCue(){this.startTime!==null&&(this.timelineController.addCues(this.trackName,this.startTime,this.endTime,this.screen,this.cueRanges),this.startTime=null)}newCue(e,t,s){(this.startTime===null||this.startTime>e)&&(this.startTime=e),this.endTime=t,this.screen=s,this.timelineController.createCaptionsTrack(this.trackName)}reset(){this.cueRanges=[],this.startTime=null}}class Pd{constructor(e){this.hls=void 0,this.media=null,this.config=void 0,this.enabled=!0,this.Cues=void 0,this.textTracks=[],this.tracks=[],this.initPTS=[],this.unparsedVttFrags=[],this.captionsTracks={},this.nonNativeCaptionsTracks={},this.cea608Parser1=void 0,this.cea608Parser2=void 0,this.lastCc=-1,this.lastSn=-1,this.lastPartIndex=-1,this.prevCC=-1,this.vttCCs=fn(),this.captionsProperties=void 0,this.hls=e,this.config=e.config,this.Cues=e.config.cueHandler,this.captionsProperties={textTrack1:{label:this.config.captionsTextTrack1Label,languageCode:this.config.captionsTextTrack1LanguageCode},textTrack2:{label:this.config.captionsTextTrack2Label,languageCode:this.config.captionsTextTrack2LanguageCode},textTrack3:{label:this.config.captionsTextTrack3Label,languageCode:this.config.captionsTextTrack3LanguageCode},textTrack4:{label:this.config.captionsTextTrack4Label,languageCode:this.config.captionsTextTrack4LanguageCode}},e.on(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(m.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),e.on(m.FRAG_LOADING,this.onFragLoading,this),e.on(m.FRAG_LOADED,this.onFragLoaded,this),e.on(m.FRAG_PARSING_USERDATA,this.onFragParsingUserdata,this),e.on(m.FRAG_DECRYPTED,this.onFragDecrypted,this),e.on(m.INIT_PTS_FOUND,this.onInitPtsFound,this),e.on(m.SUBTITLE_TRACKS_CLEARED,this.onSubtitleTracksCleared,this),e.on(m.BUFFER_FLUSHING,this.onBufferFlushing,this)}destroy(){const{hls:e}=this;e.off(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(m.SUBTITLE_TRACKS_UPDATED,this.onSubtitleTracksUpdated,this),e.off(m.FRAG_LOADING,this.onFragLoading,this),e.off(m.FRAG_LOADED,this.onFragLoaded,this),e.off(m.FRAG_PARSING_USERDATA,this.onFragParsingUserdata,this),e.off(m.FRAG_DECRYPTED,this.onFragDecrypted,this),e.off(m.INIT_PTS_FOUND,this.onInitPtsFound,this),e.off(m.SUBTITLE_TRACKS_CLEARED,this.onSubtitleTracksCleared,this),e.off(m.BUFFER_FLUSHING,this.onBufferFlushing,this),this.hls=this.config=this.media=null,this.cea608Parser1=this.cea608Parser2=void 0}initCea608Parsers(){const e=new os(this,"textTrack1"),t=new os(this,"textTrack2"),s=new os(this,"textTrack3"),i=new os(this,"textTrack4");this.cea608Parser1=new ln(1,e,t),this.cea608Parser2=new ln(3,s,i)}addCues(e,t,s,i,r){let n=!1;for(let o=r.length;o--;){const c=r[o],l=kd(c[0],c[1],t,s);if(l>=0&&(c[0]=Math.min(c[0],t),c[1]=Math.max(c[1],s),n=!0,l/(s-t)>.5))return}if(n||r.push([t,s]),this.config.renderTextTracksNatively){const o=this.captionsTracks[e];this.Cues.newCue(o,t,s,i)}else{const o=this.Cues.newCue(null,t,s,i);this.hls.trigger(m.CUES_PARSED,{type:"captions",cues:o,track:e})}}onInitPtsFound(e,{frag:t,id:s,initPTS:i,timescale:r,trackId:n}){const{unparsedVttFrags:o}=this;s===U.MAIN&&(this.initPTS[t.cc]={baseTime:i,timescale:r,trackId:n}),o.length&&(this.unparsedVttFrags=[],o.forEach(c=>{this.initPTS[c.frag.cc]?this.onFragLoaded(m.FRAG_LOADED,c):this.hls.trigger(m.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:c.frag,error:new Error("Subtitle discontinuity domain does not match main")})}))}getExistingTrack(e,t){const{media:s}=this;if(s)for(let i=0;i{Lt(i[r]),delete i[r]}),this.nonNativeCaptionsTracks={}}onManifestLoading(){this.lastCc=-1,this.lastSn=-1,this.lastPartIndex=-1,this.prevCC=-1,this.vttCCs=fn(),this._cleanTracks(),this.tracks=[],this.captionsTracks={},this.nonNativeCaptionsTracks={},this.textTracks=[],this.unparsedVttFrags=[],this.initPTS=[],this.cea608Parser1&&this.cea608Parser2&&(this.cea608Parser1.reset(),this.cea608Parser2.reset())}_cleanTracks(){const{media:e}=this;if(!e)return;const t=e.textTracks;if(t)for(let s=0;sr.textCodec===Qs);if(this.config.enableWebVTT||i&&this.config.enableIMSC1){if(La(this.tracks,s)){this.tracks=s;return}if(this.textTracks=[],this.tracks=s,this.config.renderTextTracksNatively){const n=this.media,o=n?ps(n.textTracks):null;if(this.tracks.forEach((c,l)=>{let h;if(o){let d=null;for(let u=0;ul!==null).map(l=>l.label);c.length&&this.hls.logger.warn(`Media element contains unused subtitle tracks: ${c.join(", ")}. Replace media element for each source to clear TextTracks and captions menu.`)}}else if(this.tracks.length){const n=this.tracks.map(o=>({label:o.name,kind:o.type.toLowerCase(),default:o.default,subtitleTrack:o}));this.hls.trigger(m.NON_NATIVE_TEXT_TRACKS_FOUND,{tracks:n})}}}onManifestLoaded(e,t){this.config.enableCEA708Captions&&t.captions&&t.captions.forEach(s=>{const i=/(?:CC|SERVICE)([1-4])/.exec(s.instreamId);if(!i)return;const r=`textTrack${i[1]}`,n=this.captionsProperties[r];n&&(n.label=s.name,s.lang&&(n.languageCode=s.lang),n.media=s)})}closedCaptionsForLevel(e){const t=this.hls.levels[e.level];return t==null?void 0:t.attrs["CLOSED-CAPTIONS"]}onFragLoading(e,t){if(this.enabled&&t.frag.type===U.MAIN){var s,i;const{cea608Parser1:r,cea608Parser2:n,lastSn:o}=this,{cc:c,sn:l}=t.frag,h=(s=(i=t.part)==null?void 0:i.index)!=null?s:-1;r&&n&&(l!==o+1||l===o&&h!==this.lastPartIndex+1||c!==this.lastCc)&&(r.reset(),n.reset()),this.lastCc=c,this.lastSn=l,this.lastPartIndex=h}}onFragLoaded(e,t){const{frag:s,payload:i}=t;if(s.type===U.SUBTITLE)if(i.byteLength){const r=s.decryptdata,n="stats"in t;if(r==null||!r.encrypted||n){const o=this.tracks[s.level],c=this.vttCCs;c[s.cc]||(c[s.cc]={start:s.start,prevCC:this.prevCC,new:!0},this.prevCC=s.cc),o&&o.textCodec===Qs?this._parseIMSC1(s,i):this._parseVTTs(t)}}else this.hls.trigger(m.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:s,error:new Error("Empty subtitle payload")})}_parseIMSC1(e,t){const s=this.hls;cn(t,this.initPTS[e.cc],i=>{this._appendCues(i,e.level),s.trigger(m.SUBTITLE_FRAG_PROCESSED,{success:!0,frag:e})},i=>{s.logger.log(`Failed to parse IMSC1: ${i}`),s.trigger(m.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:e,error:i})})}_parseVTTs(e){var t;const{frag:s,payload:i}=e,{initPTS:r,unparsedVttFrags:n}=this,o=r.length-1;if(!r[s.cc]&&o===-1){n.push(e);return}const c=this.hls,l=(t=s.initSegment)!=null&&t.data?we(s.initSegment.data,new Uint8Array(i)).buffer:i;Ld(l,this.initPTS[s.cc],this.vttCCs,s.cc,s.start,h=>{this._appendCues(h,s.level),c.trigger(m.SUBTITLE_FRAG_PROCESSED,{success:!0,frag:s})},h=>{const d=h.message==="Missing initPTS for VTT MPEGTS";d?n.push(e):this._fallbackToIMSC1(s,i),c.logger.log(`Failed to parse VTT cue: ${h}`),!(d&&o>s.cc)&&c.trigger(m.SUBTITLE_FRAG_PROCESSED,{success:!1,frag:s,error:h})})}_fallbackToIMSC1(e,t){const s=this.tracks[e.level];s.textCodec||cn(t,this.initPTS[e.cc],()=>{s.textCodec=Qs,this._parseIMSC1(e,t)},()=>{s.textCodec="wvtt"})}_appendCues(e,t){const s=this.hls;if(this.config.renderTextTracksNatively){const i=this.textTracks[t];if(!i||i.mode==="disabled")return;e.forEach(r=>$a(i,r))}else{const i=this.tracks[t];if(!i)return;const r=i.default?"default":"subtitles"+t;s.trigger(m.CUES_PARSED,{type:"subtitles",cues:e,track:r})}}onFragDecrypted(e,t){const{frag:s}=t;s.type===U.SUBTITLE&&this.onFragLoaded(m.FRAG_LOADED,t)}onSubtitleTracksCleared(){this.tracks=[],this.captionsTracks={}}onFragParsingUserdata(e,t){if(!this.enabled||!this.config.enableCEA708Captions)return;const{frag:s,samples:i}=t;if(!(s.type===U.MAIN&&this.closedCaptionsForLevel(s)==="NONE"))for(let r=0;rTi(o[c],t,s))}if(this.config.renderTextTracksNatively&&t===0&&i!==void 0){const{textTracks:o}=this;Object.keys(o).forEach(c=>Ti(o[c],t,i))}}}extractCea608Data(e){const t=[[],[]],s=e[0]&31;let i=2;for(let r=0;r=16?c--:c++;const f=Ya(l.trim()),g=ji(e,t,f);a!=null&&(d=a.cues)!=null&&d.getCueById(g)||(n=new h(e,t,f),n.id=g,n.line=u+1,n.align="left",n.position=10+Math.min(80,Math.floor(c*8/32)*10),i.push(n))}return a&&i.length&&(i.sort((u,f)=>u.line==="auto"||f.line==="auto"?0:u.line>8&&f.line>8?f.line-u.line:u.line-f.line),i.forEach(u=>$a(a,u))),i}};function Fd(){if(self.fetch&&self.AbortController&&self.ReadableStream&&self.Request)try{return new self.ReadableStream({}),!0}catch{}return!1}const Md=/(\d+)-(\d+)\/(\d+)/;class gn{constructor(e){this.fetchSetup=void 0,this.requestTimeout=void 0,this.request=null,this.response=null,this.controller=void 0,this.context=null,this.config=null,this.callbacks=null,this.stats=void 0,this.loader=null,this.fetchSetup=e.fetchSetup||Ud,this.controller=new self.AbortController,this.stats=new Ri}destroy(){this.loader=this.callbacks=this.context=this.config=this.request=null,this.abortInternal(),this.response=null,this.fetchSetup=this.controller=this.stats=null}abortInternal(){this.controller&&!this.stats.loading.end&&(this.stats.aborted=!0,this.controller.abort())}abort(){var e;this.abortInternal(),(e=this.callbacks)!=null&&e.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.response)}load(e,t,s){const i=this.stats;if(i.loading.start)throw new Error("Loader can only be used once.");i.loading.start=self.performance.now();const r=Nd(e,this.controller.signal),n=e.responseType==="arraybuffer",o=n?"byteLength":"length",{maxTimeToFirstByteMs:c,maxLoadTimeMs:l}=t.loadPolicy;this.context=e,this.config=t,this.callbacks=s,this.request=this.fetchSetup(e,r),self.clearTimeout(this.requestTimeout),t.timeout=c&&B(c)?c:l,this.requestTimeout=self.setTimeout(()=>{this.callbacks&&(this.abortInternal(),this.callbacks.onTimeout(i,e,this.response))},t.timeout),(Yt(this.request)?this.request.then(self.fetch):self.fetch(this.request)).then(d=>{var u;this.response=this.loader=d;const f=Math.max(self.performance.now(),i.loading.start);if(self.clearTimeout(this.requestTimeout),t.timeout=l,this.requestTimeout=self.setTimeout(()=>{this.callbacks&&(this.abortInternal(),this.callbacks.onTimeout(i,e,this.response))},l-(f-i.loading.start)),!d.ok){const{status:y,statusText:p}=d;throw new Gd(p||"fetch, bad network response",y,d)}i.loading.first=f,i.total=$d(d.headers)||i.total;const g=(u=this.callbacks)==null?void 0:u.onProgress;return g&&B(t.highWaterMark)?this.loadProgressively(d,i,e,t.highWaterMark,g):n?d.arrayBuffer():e.responseType==="json"?d.json():d.text()}).then(d=>{var u,f;const g=this.response;if(!g)throw new Error("loader destroyed");self.clearTimeout(this.requestTimeout),i.loading.end=Math.max(self.performance.now(),i.loading.first);const y=d[o];y&&(i.loaded=i.total=y);const p={url:g.url,data:d,code:g.status},E=(u=this.callbacks)==null?void 0:u.onProgress;E&&!B(t.highWaterMark)&&E(i,e,d,g),(f=this.callbacks)==null||f.onSuccess(p,i,e,g)}).catch(d=>{var u;if(self.clearTimeout(this.requestTimeout),i.aborted)return;const f=d&&d.code||0,g=d?d.message:null;(u=this.callbacks)==null||u.onError({code:f,text:g},e,d?d.details:null,i)})}getCacheAge(){let e=null;if(this.response){const t=this.response.headers.get("age");e=t?parseFloat(t):null}return e}getResponseHeader(e){return this.response?this.response.headers.get(e):null}loadProgressively(e,t,s,i=0,r){const n=new na,o=e.body.getReader(),c=()=>o.read().then(l=>{if(l.done)return n.dataLength&&r(t,s,n.flush().buffer,e),Promise.resolve(new ArrayBuffer(0));const h=l.value,d=h.length;return t.loaded+=d,d=i&&r(t,s,n.flush().buffer,e)):r(t,s,h.buffer,e),c()}).catch(()=>Promise.reject());return c()}}function Nd(a,e){const t={method:"GET",mode:"cors",credentials:"same-origin",signal:e,headers:new self.Headers(ne({},a.headers))};return a.rangeEnd&&t.headers.set("Range","bytes="+a.rangeStart+"-"+String(a.rangeEnd-1)),t}function Bd(a){const e=Md.exec(a);if(e)return parseInt(e[2])-parseInt(e[1])+1}function $d(a){const e=a.get("Content-Range");if(e){const s=Bd(e);if(B(s))return s}const t=a.get("Content-Length");if(t)return parseInt(t)}function Ud(a,e){return new self.Request(a.url,e)}class Gd extends Error{constructor(e,t,s){super(e),this.code=void 0,this.details=void 0,this.code=t,this.details=s}}const Kd=/^age:\s*[\d.]+\s*$/im;class za{constructor(e){this.xhrSetup=void 0,this.requestTimeout=void 0,this.retryTimeout=void 0,this.retryDelay=void 0,this.config=null,this.callbacks=null,this.context=null,this.loader=null,this.stats=void 0,this.xhrSetup=e&&e.xhrSetup||null,this.stats=new Ri,this.retryDelay=0}destroy(){this.callbacks=null,this.abortInternal(),this.loader=null,this.config=null,this.context=null,this.xhrSetup=null}abortInternal(){const e=this.loader;self.clearTimeout(this.requestTimeout),self.clearTimeout(this.retryTimeout),e&&(e.onreadystatechange=null,e.onprogress=null,e.readyState!==4&&(this.stats.aborted=!0,e.abort()))}abort(){var e;this.abortInternal(),(e=this.callbacks)!=null&&e.onAbort&&this.callbacks.onAbort(this.stats,this.context,this.loader)}load(e,t,s){if(this.stats.loading.start)throw new Error("Loader can only be used once.");this.stats.loading.start=self.performance.now(),this.context=e,this.config=t,this.callbacks=s,this.loadInternal()}loadInternal(){const{config:e,context:t}=this;if(!e||!t)return;const s=this.loader=new self.XMLHttpRequest,i=this.stats;i.loading.first=0,i.loaded=0,i.aborted=!1;const r=this.xhrSetup;r?Promise.resolve().then(()=>{if(!(this.loader!==s||this.stats.aborted))return r(s,t.url)}).catch(n=>{if(!(this.loader!==s||this.stats.aborted))return s.open("GET",t.url,!0),r(s,t.url)}).then(()=>{this.loader!==s||this.stats.aborted||this.openAndSendXhr(s,t,e)}).catch(n=>{var o;(o=this.callbacks)==null||o.onError({code:s.status,text:n.message},t,s,i)}):this.openAndSendXhr(s,t,e)}openAndSendXhr(e,t,s){e.readyState||e.open("GET",t.url,!0);const i=t.headers,{maxTimeToFirstByteMs:r,maxLoadTimeMs:n}=s.loadPolicy;if(i)for(const o in i)e.setRequestHeader(o,i[o]);t.rangeEnd&&e.setRequestHeader("Range","bytes="+t.rangeStart+"-"+(t.rangeEnd-1)),e.onreadystatechange=this.readystatechange.bind(this),e.onprogress=this.loadprogress.bind(this),e.responseType=t.responseType,self.clearTimeout(this.requestTimeout),s.timeout=r&&B(r)?r:n,this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),s.timeout),e.send()}readystatechange(){const{context:e,loader:t,stats:s}=this;if(!e||!t)return;const i=t.readyState,r=this.config;if(!s.aborted&&i>=2&&(s.loading.first===0&&(s.loading.first=Math.max(self.performance.now(),s.loading.start),r.timeout!==r.loadPolicy.maxLoadTimeMs&&(self.clearTimeout(this.requestTimeout),r.timeout=r.loadPolicy.maxLoadTimeMs,this.requestTimeout=self.setTimeout(this.loadtimeout.bind(this),r.loadPolicy.maxLoadTimeMs-(s.loading.first-s.loading.start)))),i===4)){self.clearTimeout(this.requestTimeout),t.onreadystatechange=null,t.onprogress=null;const l=t.status,h=t.responseType==="text"?t.responseText:null;if(l>=200&&l<300){const g=h??t.response;if(g!=null){var n,o;s.loading.end=Math.max(self.performance.now(),s.loading.first);const y=t.responseType==="arraybuffer"?g.byteLength:g.length;s.loaded=s.total=y,s.bwEstimate=s.total*8e3/(s.loading.end-s.loading.first);const p=(n=this.callbacks)==null?void 0:n.onProgress;p&&p(s,e,g,t);const E={url:t.responseURL,data:g,code:l};(o=this.callbacks)==null||o.onSuccess(E,s,e,t);return}}const d=r.loadPolicy.errorRetry,u=s.retry,f={url:e.url,data:void 0,code:l};if(As(d,u,!1,f))this.retry(d);else{var c;re.error(`${l} while loading ${e.url}`),(c=this.callbacks)==null||c.onError({code:l,text:t.statusText},e,t,s)}}}loadtimeout(){if(!this.config)return;const e=this.config.loadPolicy.timeoutRetry,t=this.stats.retry;if(As(e,t,!0))this.retry(e);else{var s;re.warn(`timeout while loading ${(s=this.context)==null?void 0:s.url}`);const i=this.callbacks;i&&(this.abortInternal(),i.onTimeout(this.stats,this.context,this.loader))}}retry(e){const{context:t,stats:s}=this;this.retryDelay=Ci(e,s.retry),s.retry++,re.warn(`${status?"HTTP Status "+status:"Timeout"} while loading ${t==null?void 0:t.url}, retrying ${s.retry}/${e.maxNumRetry} in ${this.retryDelay}ms`),this.abortInternal(),this.loader=null,self.clearTimeout(this.retryTimeout),this.retryTimeout=self.setTimeout(this.loadInternal.bind(this),this.retryDelay)}loadprogress(e){const t=this.stats;t.loaded=e.loaded,e.lengthComputable&&(t.total=e.total)}getCacheAge(){let e=null;if(this.loader&&Kd.test(this.loader.getAllResponseHeaders())){const t=this.loader.getResponseHeader("age");e=t?parseFloat(t):null}return e}getResponseHeader(e){return this.loader&&new RegExp(`^${e}:\\s*[\\d.]+\\s*$`,"im").test(this.loader.getAllResponseHeaders())?this.loader.getResponseHeader(e):null}}const Vd={maxTimeToFirstByteMs:8e3,maxLoadTimeMs:2e4,timeoutRetry:null,errorRetry:null},Hd=ie(ie({autoStartLoad:!0,startPosition:-1,defaultAudioCodec:void 0,debug:!1,capLevelOnFPSDrop:!1,capLevelToPlayerSize:!1,ignoreDevicePixelRatio:!1,maxDevicePixelRatio:Number.POSITIVE_INFINITY,preferManagedMediaSource:!0,initialLiveManifestSize:1,maxBufferLength:30,backBufferLength:1/0,frontBufferFlushThreshold:1/0,startOnSegmentBoundary:!1,maxBufferSize:60*1e3*1e3,maxFragLookUpTolerance:.25,maxBufferHole:.1,detectStallWithCurrentTimeMs:1250,highBufferWatchdogPeriod:2,nudgeOffset:.1,nudgeMaxRetry:3,nudgeOnVideoHole:!0,liveSyncMode:"edge",liveSyncDurationCount:3,liveSyncOnStallIncrease:1,liveMaxLatencyDurationCount:1/0,liveSyncDuration:void 0,liveMaxLatencyDuration:void 0,maxLiveSyncPlaybackRate:1,liveDurationInfinity:!1,liveBackBufferLength:null,maxMaxBufferLength:600,enableWorker:!0,workerPath:null,enableSoftwareAES:!0,startLevel:void 0,startFragPrefetch:!1,fpsDroppedMonitoringPeriod:5e3,fpsDroppedMonitoringThreshold:.2,appendErrorMaxRetry:3,ignorePlaylistParsingErrors:!1,loader:za,fLoader:void 0,pLoader:void 0,xhrSetup:void 0,licenseXhrSetup:void 0,licenseResponseCallback:void 0,abrController:sl,bufferController:Wc,capLevelController:Hi,errorController:ol,fpsController:qh,stretchShortVideoTrack:!1,maxAudioFramesDrift:1,forceKeyFrameOnDiscontinuity:!0,abrEwmaFastLive:3,abrEwmaSlowLive:9,abrEwmaFastVoD:3,abrEwmaSlowVoD:9,abrEwmaDefaultEstimate:5e5,abrEwmaDefaultEstimateMax:5e6,abrBandWidthFactor:.95,abrBandWidthUpFactor:.7,abrMaxWithRealBitrate:!1,maxStarvationDelay:4,maxLoadingDelay:4,minAutoBitrate:0,emeEnabled:!1,widevineLicenseUrl:void 0,drmSystems:{},drmSystemOptions:{},requestMediaKeySystemAccessFunc:jn,requireKeySystemAccessOnStart:!1,testBandwidth:!0,progressive:!1,lowLatencyMode:!0,cmcd:void 0,enableDateRangeMetadataCues:!0,enableEmsgMetadataCues:!0,enableEmsgKLVMetadata:!1,enableID3MetadataCues:!0,enableInterstitialPlayback:!0,interstitialAppendInPlace:!0,interstitialLiveLookAhead:10,useMediaCapabilities:!0,preserveManualLevelOnError:!1,certLoadPolicy:{default:Vd},keyLoadPolicy:{default:{maxTimeToFirstByteMs:8e3,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:2e4,backoff:"linear"},errorRetry:{maxNumRetry:8,retryDelayMs:1e3,maxRetryDelayMs:2e4,backoff:"linear"}}},manifestLoadPolicy:{default:{maxTimeToFirstByteMs:1/0,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},playlistLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:2,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},fragLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:12e4,timeoutRetry:{maxNumRetry:4,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:6,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},steeringManifestLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:2e4,timeoutRetry:{maxNumRetry:2,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:1,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},interstitialAssetListLoadPolicy:{default:{maxTimeToFirstByteMs:1e4,maxLoadTimeMs:3e4,timeoutRetry:{maxNumRetry:0,retryDelayMs:0,maxRetryDelayMs:0},errorRetry:{maxNumRetry:0,retryDelayMs:1e3,maxRetryDelayMs:8e3}}},manifestLoadingTimeOut:1e4,manifestLoadingMaxRetry:1,manifestLoadingRetryDelay:1e3,manifestLoadingMaxRetryTimeout:64e3,levelLoadingTimeOut:1e4,levelLoadingMaxRetry:4,levelLoadingRetryDelay:1e3,levelLoadingMaxRetryTimeout:64e3,fragLoadingTimeOut:2e4,fragLoadingMaxRetry:6,fragLoadingRetryDelay:1e3,fragLoadingMaxRetryTimeout:64e3},Yd()),{},{subtitleStreamController:nd,subtitleTrackController:zh,timelineController:Pd,audioStreamController:Kc,audioTrackController:Vc,emeController:Dt,cmcdController:Vh,contentSteeringController:Yh,interstitialsController:rd});function Yd(){return{cueHandler:Od,enableWebVTT:!0,enableIMSC1:!0,enableCEA708Captions:!0,captionsTextTrack1Label:"English",captionsTextTrack1LanguageCode:"en",captionsTextTrack2Label:"Spanish",captionsTextTrack2LanguageCode:"es",captionsTextTrack3Label:"Unknown CC",captionsTextTrack3LanguageCode:"",captionsTextTrack4Label:"Unknown CC",captionsTextTrack4LanguageCode:"",renderTextTracksNatively:!0}}function Wd(a,e,t){if((e.liveSyncDurationCount||e.liveMaxLatencyDurationCount)&&(e.liveSyncDuration||e.liveMaxLatencyDuration))throw new Error("Illegal hls.js config: don't mix up liveSyncDurationCount/liveMaxLatencyDurationCount and liveSyncDuration/liveMaxLatencyDuration");if(e.liveMaxLatencyDurationCount!==void 0&&(e.liveSyncDurationCount===void 0||e.liveMaxLatencyDurationCount<=e.liveSyncDurationCount))throw new Error('Illegal hls.js config: "liveMaxLatencyDurationCount" must be greater than "liveSyncDurationCount"');if(e.liveMaxLatencyDuration!==void 0&&(e.liveSyncDuration===void 0||e.liveMaxLatencyDuration<=e.liveSyncDuration))throw new Error('Illegal hls.js config: "liveMaxLatencyDuration" must be greater than "liveSyncDuration"');const s=vi(a),i=["manifest","level","frag"],r=["TimeOut","MaxRetry","RetryDelay","MaxRetryTimeout"];return i.forEach(n=>{const o=`${n==="level"?"playlist":n}LoadPolicy`,c=e[o]===void 0,l=[];r.forEach(h=>{const d=`${n}Loading${h}`,u=e[d];if(u!==void 0&&c){l.push(d);const f=s[o].default;switch(e[o]={default:f},h){case"TimeOut":f.maxLoadTimeMs=u,f.maxTimeToFirstByteMs=u;break;case"MaxRetry":f.errorRetry.maxNumRetry=u,f.timeoutRetry.maxNumRetry=u;break;case"RetryDelay":f.errorRetry.retryDelayMs=u,f.timeoutRetry.retryDelayMs=u;break;case"MaxRetryTimeout":f.errorRetry.maxRetryDelayMs=u,f.timeoutRetry.maxRetryDelayMs=u;break}}}),l.length&&t.warn(`hls.js config: "${l.join('", "')}" setting(s) are deprecated, use "${o}": ${oe(e[o])}`)}),ie(ie({},s),e)}function vi(a){return a&&typeof a=="object"?Array.isArray(a)?a.map(vi):Object.keys(a).reduce((e,t)=>(e[t]=vi(a[t]),e),{}):a}function qd(a,e){const t=a.loader;t!==gn&&t!==za?(e.log("[config]: Custom loader detected, cannot enable progressive streaming"),a.progressive=!1):Fd()&&(a.loader=gn,a.progressive=!0,a.enableSoftwareAES=!0,e.log("[config]: Progressive streaming enabled, using FetchLoader"))}const ys=2,jd=.1,Xd=.05,zd=100;class Qd extends Vn{constructor(e,t){super("gap-controller",e.logger),this.hls=void 0,this.fragmentTracker=void 0,this.media=null,this.mediaSource=void 0,this.nudgeRetry=0,this.stallReported=!1,this.stalled=null,this.moved=!1,this.seeking=!1,this.buffered={},this.lastCurrentTime=0,this.ended=0,this.waiting=0,this.onMediaPlaying=()=>{this.ended=0,this.waiting=0},this.onMediaWaiting=()=>{var s;(s=this.media)!=null&&s.seeking||(this.waiting=self.performance.now(),this.tick())},this.onMediaEnded=()=>{if(this.hls){var s;this.ended=((s=this.media)==null?void 0:s.currentTime)||1,this.hls.trigger(m.MEDIA_ENDED,{stalled:!1})}},this.hls=e,this.fragmentTracker=t,this.registerListeners()}registerListeners(){const{hls:e}=this;e&&(e.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.BUFFER_APPENDED,this.onBufferAppended,this))}unregisterListeners(){const{hls:e}=this;e&&(e.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.BUFFER_APPENDED,this.onBufferAppended,this))}destroy(){super.destroy(),this.unregisterListeners(),this.media=this.hls=this.fragmentTracker=null,this.mediaSource=void 0}onMediaAttached(e,t){this.setInterval(zd),this.mediaSource=t.mediaSource;const s=this.media=t.media;xe(s,"playing",this.onMediaPlaying),xe(s,"waiting",this.onMediaWaiting),xe(s,"ended",this.onMediaEnded)}onMediaDetaching(e,t){this.clearInterval();const{media:s}=this;s&&(Ie(s,"playing",this.onMediaPlaying),Ie(s,"waiting",this.onMediaWaiting),Ie(s,"ended",this.onMediaEnded),this.media=null),this.mediaSource=void 0}onBufferAppended(e,t){this.buffered=t.timeRanges}get hasBuffered(){return Object.keys(this.buffered).length>0}tick(){var e;if(!((e=this.media)!=null&&e.readyState)||!this.hasBuffered)return;const t=this.media.currentTime;this.poll(t,this.lastCurrentTime),this.lastCurrentTime=t}poll(e,t){var s,i;const r=(s=this.hls)==null?void 0:s.config;if(!r)return;const n=this.media;if(!n)return;const{seeking:o}=n,c=this.seeking&&!o,l=!this.seeking&&o,h=n.paused&&!o||n.ended||n.playbackRate===0;if(this.seeking=o,e!==t){t&&(this.ended=0),this.moved=!0,o||(this.nudgeRetry=0,r.nudgeOnVideoHole&&!h&&e>t&&this.nudgeOnVideoHole(e,t)),this.waiting===0&&this.stallResolved(e);return}if(l||c){c&&this.stallResolved(e);return}if(h){this.nudgeRetry=0,this.stallResolved(e),!this.ended&&n.ended&&this.hls&&(this.ended=e||1,this.hls.trigger(m.MEDIA_ENDED,{stalled:!1}));return}if(!X.getBuffered(n).length){this.nudgeRetry=0;return}const d=X.bufferInfo(n,e,0),u=d.nextStart||0,f=this.fragmentTracker;if(o&&f&&this.hls){const D=mn(this.hls.inFlightFragments,e),A=d.len>ys,_=!u||D||u-e>ys&&!f.getPartialFragment(e);if(A||_)return;this.moved=!1}const g=(i=this.hls)==null?void 0:i.latestLevelDetails;if(!this.moved&&this.stalled!==null&&f){if(!(d.len>0)&&!u)return;const A=Math.max(u,d.start||0)-e,b=!!(g!=null&&g.live)?g.targetduration*2:ys,I=ls(e,f);if(A>0&&(A<=b||I)){n.paused||this._trySkipBufferHole(I);return}}const y=r.detectStallWithCurrentTimeMs,p=self.performance.now(),E=this.waiting;let T=this.stalled;if(T===null)if(E>0&&p-E=y||E)&&this.hls){var v;if(((v=this.mediaSource)==null?void 0:v.readyState)==="ended"&&!(g!=null&&g.live)&&Math.abs(e-((g==null?void 0:g.edge)||0))<1){if(this.ended)return;this.ended=e||1,this.hls.trigger(m.MEDIA_ENDED,{stalled:!0});return}if(this._reportStall(d),!this.media||!this.hls)return}const x=X.bufferInfo(n,e,r.maxBufferHole);this._tryFixBufferStall(x,S,e)}stallResolved(e){const t=this.stalled;if(t&&this.hls&&(this.stalled=null,this.stallReported)){const s=self.performance.now()-t;this.log(`playback not stuck anymore @${e}, after ${Math.round(s)}ms`),this.stallReported=!1,this.waiting=0,this.hls.trigger(m.STALL_RESOLVED,{})}}nudgeOnVideoHole(e,t){var s;const i=this.buffered.video;if(this.hls&&this.media&&this.fragmentTracker&&(s=this.buffered.audio)!=null&&s.length&&i&&i.length>1&&e>i.end(0)){const r=X.bufferedInfo(X.timeRangesToArray(this.buffered.audio),e,0);if(r.len>1&&t>=r.start){const n=X.timeRangesToArray(i),o=X.bufferedInfo(n,t,0).bufferedIndex;if(o>-1&&oo)&&h-l<1&&e-l<2){const d=new Error(`nudging playhead to flush pipeline after video hole. currentTime: ${e} hole: ${l} -> ${h} buffered index: ${c}`);this.warn(d.message),this.media.currentTime+=1e-6;let u=ls(e,this.fragmentTracker);u&&"fragment"in u?u=u.fragment:u||(u=void 0);const f=X.bufferInfo(this.media,e,0);this.hls.trigger(m.ERROR,{type:Y.MEDIA_ERROR,details:R.BUFFER_SEEK_OVER_HOLE,fatal:!1,error:d,reason:d.message,frag:u,buffer:f.len,bufferInfo:f})}}}}}_tryFixBufferStall(e,t,s){var i,r;const{fragmentTracker:n,media:o}=this,c=(i=this.hls)==null?void 0:i.config;if(!o||!n||!c)return;const l=(r=this.hls)==null?void 0:r.latestLevelDetails,h=ls(s,n);if((h||l!=null&&l.live&&s1&&e.len>c.maxBufferHole||e.nextStart&&(e.nextStart-sc.highBufferWatchdogPeriod*1e3||this.waiting)&&(this.warn("Trying to nudge playhead over buffer-hole"),this._tryNudgeBuffer(e))}adjacentTraversal(e,t){const s=this.fragmentTracker,i=e.nextStart;if(s&&i){const r=s.getFragAtPos(t,U.MAIN),n=s.getFragAtPos(i,U.MAIN);if(r&&n)return n.sn-r.sn<2}return!1}_reportStall(e){const{hls:t,media:s,stallReported:i,stalled:r}=this;if(!i&&r!==null&&s&&t){this.stallReported=!0;const n=new Error(`Playback stalling at @${s.currentTime} due to low buffer (${oe(e)})`);this.warn(n.message),t.trigger(m.ERROR,{type:Y.MEDIA_ERROR,details:R.BUFFER_STALLED_ERROR,fatal:!1,error:n,buffer:e.len,bufferInfo:e,stalled:{start:r}})}}_trySkipBufferHole(e){var t;const{fragmentTracker:s,media:i}=this,r=(t=this.hls)==null?void 0:t.config;if(!i||!s||!r)return 0;const n=i.currentTime,o=X.bufferInfo(i,n,0),c=n0&&o.len<1&&i.readyState<3,u=c-n;if(u>0&&(h||d)){if(u>r.maxBufferHole){let g=!1;if(n===0){const y=s.getAppendedFrag(0,U.MAIN);y&&c"u"))return self.VTTCue||self.TextTrackCue}function ti(a,e,t,s,i){let r=new a(e,t,"");try{r.value=s,i&&(r.type=i)}catch{r=new a(e,t,oe(i?ie({type:i},s):s))}return r}const cs=(()=>{const a=xi();try{a&&new a(0,Number.POSITIVE_INFINITY,"")}catch{return Number.MAX_VALUE}return Number.POSITIVE_INFINITY})();class Jd{constructor(e){this.hls=void 0,this.id3Track=null,this.media=null,this.dateRangeCuesAppended={},this.removeCues=!0,this.assetCue=void 0,this.onEventCueEnter=()=>{this.hls&&this.hls.trigger(m.EVENT_CUE_ENTER,{})},this.hls=e,this._registerListeners()}destroy(){this._unregisterListeners(),this.id3Track=null,this.media=null,this.dateRangeCuesAppended={},this.hls=this.onEventCueEnter=null}_registerListeners(){const{hls:e}=this;e&&(e.on(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),e.on(m.BUFFER_FLUSHING,this.onBufferFlushing,this),e.on(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(m.LEVEL_PTS_UPDATED,this.onLevelPtsUpdated,this))}_unregisterListeners(){const{hls:e}=this;e&&(e.off(m.MEDIA_ATTACHING,this.onMediaAttaching,this),e.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.FRAG_PARSING_METADATA,this.onFragParsingMetadata,this),e.off(m.BUFFER_FLUSHING,this.onBufferFlushing,this),e.off(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(m.LEVEL_PTS_UPDATED,this.onLevelPtsUpdated,this))}onMediaAttaching(e,t){var s;this.media=t.media,((s=t.overrides)==null?void 0:s.cueRemoval)===!1&&(this.removeCues=!1)}onMediaAttached(){var e;const t=(e=this.hls)==null?void 0:e.latestLevelDetails;t&&this.updateDateRangeCues(t)}onMediaDetaching(e,t){this.media=null,!t.transferMedia&&(this.id3Track&&(this.removeCues&&Lt(this.id3Track,this.onEventCueEnter),this.id3Track=null),this.dateRangeCuesAppended={})}onManifestLoading(){this.dateRangeCuesAppended={}}createTrack(e){const t=this.getID3Track(e.textTracks);return t.mode="hidden",t}getID3Track(e){if(this.media){for(let t=0;tcs&&(d=cs),d-h<=0&&(d=h+Zd);for(let f=0;fh.type===_e.audioId3&&c:i==="video"?l=h=>h.type===_e.emsg&&o:l=h=>h.type===_e.audioId3&&c||h.type===_e.emsg&&o,Ti(r,t,s,l)}}onLevelUpdated(e,{details:t}){this.updateDateRangeCues(t,!0)}onLevelPtsUpdated(e,t){Math.abs(t.drift)>.01&&this.updateDateRangeCues(t.details)}updateDateRangeCues(e,t){if(!this.hls||!this.media)return;const{assetPlayerId:s,timelineOffset:i,enableDateRangeMetadataCues:r,interstitialsController:n}=this.hls.config;if(!r)return;const o=xi();if(s&&i&&!n){const{fragmentStart:y,fragmentEnd:p}=e;let E=this.assetCue;E?(E.startTime=y,E.endTime=p):o&&(E=this.assetCue=ti(o,y,p,{assetPlayerId:this.hls.config.assetPlayerId},"hlsjs.interstitial.asset"),E&&(E.id=s,this.id3Track||(this.id3Track=this.createTrack(this.media)),this.id3Track.addCue(E),E.addEventListener("enter",this.onEventCueEnter)))}if(!e.hasProgramDateTime)return;const{id3Track:c}=this,{dateRanges:l}=e,h=Object.keys(l);let d=this.dateRangeCuesAppended;if(c&&t){var u;if((u=c.cues)!=null&&u.length){const y=Object.keys(d).filter(p=>!h.includes(p));for(let p=y.length;p--;){var f;const E=y[p],T=(f=d[E])==null?void 0:f.cues;delete d[E],T&&Object.keys(T).forEach(S=>{const v=T[S];if(v){v.removeEventListener("enter",this.onEventCueEnter);try{c.removeCue(v)}catch{}}})}}else d=this.dateRangeCuesAppended={}}const g=e.fragments[e.fragments.length-1];if(!(h.length===0||!B(g==null?void 0:g.programDateTime))){this.id3Track||(this.id3Track=this.createTrack(this.media));for(let y=0;y{if(M!==E.id){const G=l[M];if(G.class===E.class&&G.startDate>E.startDate&&(!P||E.startDate.01&&(M.startTime=T,M.endTime=D);else if(o){let G=E.attr[P];vl(P)&&(G=An(G));const K=ti(o,T,D,{key:P,data:G},_e.dateRange);K&&(K.id=p,this.id3Track.addCue(K),v[P]=K,n&&(P==="X-ASSET-LIST"||P==="X-ASSET-URL")&&K.addEventListener("enter",this.onEventCueEnter))}}d[p]={cues:v,dateRange:E,durationKnown:x}}}}}class eu{constructor(e){this.hls=void 0,this.config=void 0,this.media=null,this.currentTime=0,this.stallCount=0,this._latency=null,this._targetLatencyUpdated=!1,this.onTimeupdate=()=>{const{media:t}=this,s=this.levelDetails;if(!t||!s)return;this.currentTime=t.currentTime;const i=this.computeLatency();if(i===null)return;this._latency=i;const{lowLatencyMode:r,maxLiveSyncPlaybackRate:n}=this.config;if(!r||n===1||!s.live)return;const o=this.targetLatency;if(o===null)return;const c=i-o,l=Math.min(this.maxLatency,o+s.targetduration);if(c.05&&this.forwardBufferLength>1){const d=Math.min(2,Math.max(1,n)),u=Math.round(2/(1+Math.exp(-.75*c-this.edgeStalled))*20)/20,f=Math.min(d,Math.max(1,u));this.changeMediaPlaybackRate(t,f)}else t.playbackRate!==1&&t.playbackRate!==0&&this.changeMediaPlaybackRate(t,1)},this.hls=e,this.config=e.config,this.registerListeners()}get levelDetails(){var e;return((e=this.hls)==null?void 0:e.latestLevelDetails)||null}get latency(){return this._latency||0}get maxLatency(){const{config:e}=this;if(e.liveMaxLatencyDuration!==void 0)return e.liveMaxLatencyDuration;const t=this.levelDetails;return t?e.liveMaxLatencyDurationCount*t.targetduration:0}get targetLatency(){const e=this.levelDetails;if(e===null||this.hls===null)return null;const{holdBack:t,partHoldBack:s,targetduration:i}=e,{liveSyncDuration:r,liveSyncDurationCount:n,lowLatencyMode:o}=this.config,c=this.hls.userConfig;let l=o&&s||t;(this._targetLatencyUpdated||c.liveSyncDuration||c.liveSyncDurationCount||l===0)&&(l=r!==void 0?r:n*i);const h=i;return l+Math.min(this.stallCount*this.config.liveSyncOnStallIncrease,h)}set targetLatency(e){this.stallCount=0,this.config.liveSyncDuration=e,this._targetLatencyUpdated=!0}get liveSyncPosition(){const e=this.estimateLiveEdge(),t=this.targetLatency;if(e===null||t===null)return null;const s=this.levelDetails;if(s===null)return null;const i=s.edge,r=e-t-this.edgeStalled,n=i-s.totalduration,o=i-(this.config.lowLatencyMode&&s.partTarget||s.targetduration);return Math.min(Math.max(n,r),o)}get drift(){const e=this.levelDetails;return e===null?1:e.drift}get edgeStalled(){const e=this.levelDetails;if(e===null)return 0;const t=(this.config.lowLatencyMode&&e.partTarget||e.targetduration)*3;return Math.max(e.age-t,0)}get forwardBufferLength(){const{media:e}=this,t=this.levelDetails;if(!e||!t)return 0;const s=e.buffered.length;return(s?e.buffered.end(s-1):t.edge)-this.currentTime}destroy(){this.unregisterListeners(),this.onMediaDetaching(),this.hls=null}registerListeners(){const{hls:e}=this;e&&(e.on(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.on(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.on(m.ERROR,this.onError,this))}unregisterListeners(){const{hls:e}=this;e&&(e.off(m.MEDIA_ATTACHED,this.onMediaAttached,this),e.off(m.MEDIA_DETACHING,this.onMediaDetaching,this),e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.LEVEL_UPDATED,this.onLevelUpdated,this),e.off(m.ERROR,this.onError,this))}onMediaAttached(e,t){this.media=t.media,this.media.addEventListener("timeupdate",this.onTimeupdate)}onMediaDetaching(){this.media&&(this.media.removeEventListener("timeupdate",this.onTimeupdate),this.media=null)}onManifestLoading(){this._latency=null,this.stallCount=0}onLevelUpdated(e,{details:t}){t.advanced&&this.onTimeupdate(),!t.live&&this.media&&this.media.removeEventListener("timeupdate",this.onTimeupdate)}onError(e,t){var s;t.details===R.BUFFER_STALLED_ERROR&&(this.stallCount++,this.hls&&(s=this.levelDetails)!=null&&s.live&&this.hls.logger.warn("[latency-controller]: Stall detected, adjusting target latency"))}changeMediaPlaybackRate(e,t){var s,i;e.playbackRate!==t&&((s=this.hls)==null||s.logger.debug(`[latency-controller]: latency=${this.latency.toFixed(3)}, targetLatency=${(i=this.targetLatency)==null?void 0:i.toFixed(3)}, forwardBufferLength=${this.forwardBufferLength.toFixed(3)}: adjusting playback rate from ${e.playbackRate} to ${t}`),e.playbackRate=t)}estimateLiveEdge(){const e=this.levelDetails;return e===null?null:e.edge+e.age}computeLatency(){const e=this.estimateLiveEdge();return e===null?null:e-this.currentTime}}class tu extends Vi{constructor(e,t){super(e,"level-controller"),this._levels=[],this._firstLevel=-1,this._maxAutoLevel=-1,this._startLevel=void 0,this.currentLevel=null,this.currentLevelIndex=-1,this.manualLevelIndex=-1,this.steering=void 0,this.onParsedComplete=void 0,this.steering=t,this._registerListeners()}_registerListeners(){const{hls:e}=this;e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.on(m.LEVEL_LOADED,this.onLevelLoaded,this),e.on(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(m.FRAG_BUFFERED,this.onFragBuffered,this),e.on(m.ERROR,this.onError,this)}_unregisterListeners(){const{hls:e}=this;e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.MANIFEST_LOADED,this.onManifestLoaded,this),e.off(m.LEVEL_LOADED,this.onLevelLoaded,this),e.off(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(m.FRAG_BUFFERED,this.onFragBuffered,this),e.off(m.ERROR,this.onError,this)}destroy(){this._unregisterListeners(),this.steering=null,this.resetLevels(),super.destroy()}stopLoad(){this._levels.forEach(t=>{t.loadError=0,t.fragmentError=0}),super.stopLoad()}resetLevels(){this._startLevel=void 0,this.manualLevelIndex=-1,this.currentLevelIndex=-1,this.currentLevel=null,this._levels=[],this._maxAutoLevel=-1}onManifestLoading(e,t){this.resetLevels()}onManifestLoaded(e,t){const s=this.hls.config.preferManagedMediaSource,i=[],r={},n={};let o=!1,c=!1,l=!1;t.levels.forEach(h=>{const d=h.attrs;let{audioCodec:u,videoCodec:f}=h;u&&(h.audioCodec=u=Ts(u,s)||void 0),f&&(f=h.videoCodec=No(f));const{width:g,height:y,unknownCodecs:p}=h,E=(p==null?void 0:p.length)||0;if(o||(o=!!(g&&y)),c||(c=!!f),l||(l=!!u),E||u&&!this.isAudioSupported(u)||f&&!this.isVideoSupported(f)){this.log(`Some or all CODECS not supported "${d.CODECS}"`);return}const{CODECS:T,"FRAME-RATE":S,"HDCP-LEVEL":v,"PATHWAY-ID":x,RESOLUTION:D,"VIDEO-RANGE":A}=d,b=`${`${x||"."}-`}${h.bitrate}-${D}-${S}-${T}-${A}-${v}`;if(r[b])if(r[b].uri!==h.url&&!h.attrs["PATHWAY-ID"]){const I=n[b]+=1;h.attrs["PATHWAY-ID"]=new Array(I+1).join(".");const P=this.createLevel(h);r[b]=P,i.push(P)}else r[b].addGroupId("audio",d.AUDIO),r[b].addGroupId("text",d.SUBTITLES);else{const I=this.createLevel(h);r[b]=I,n[b]=1,i.push(I)}}),this.filterAndSortMediaOptions(i,t,o,c,l)}createLevel(e){const t=new Kt(e),s=e.supplemental;if(s!=null&&s.videoCodec&&!this.isVideoSupported(s.videoCodec)){const i=new Error(`SUPPLEMENTAL-CODECS not supported "${s.videoCodec}"`);this.log(i.message),t.supportedResult=Fn(i,[])}return t}isAudioSupported(e){return Ut(e,"audio",this.hls.config.preferManagedMediaSource)}isVideoSupported(e){return Ut(e,"video",this.hls.config.preferManagedMediaSource)}filterAndSortMediaOptions(e,t,s,i,r){var n;let o=[],c=[],l=e;const h=((n=t.stats)==null?void 0:n.parsing)||{};if((s||i)&&r&&(l=l.filter(({videoCodec:T,videoRange:S,width:v,height:x})=>(!!T||!!(v&&x))&&qo(S))),l.length===0){Promise.resolve().then(()=>{if(this.hls){let T="no level with compatible codecs found in manifest",S=T;t.levels.length&&(S=`one or more CODECS in variant not supported: ${oe(t.levels.map(x=>x.attrs.CODECS).filter((x,D,A)=>A.indexOf(x)===D))}`,this.warn(S),T+=` (${S})`);const v=new Error(T);this.hls.trigger(m.ERROR,{type:Y.MEDIA_ERROR,details:R.MANIFEST_INCOMPATIBLE_CODECS_ERROR,fatal:!0,url:t.url,error:v,reason:S})}}),h.end=performance.now();return}t.audioTracks&&(o=t.audioTracks.filter(T=>!T.audioCodec||this.isAudioSupported(T.audioCodec)),yn(o)),t.subtitles&&(c=t.subtitles,yn(c));const d=l.slice(0);l.sort((T,S)=>{if(T.attrs["HDCP-LEVEL"]!==S.attrs["HDCP-LEVEL"])return(T.attrs["HDCP-LEVEL"]||"")>(S.attrs["HDCP-LEVEL"]||"")?1:-1;if(s&&T.height!==S.height)return T.height-S.height;if(T.frameRate!==S.frameRate)return T.frameRate-S.frameRate;if(T.videoRange!==S.videoRange)return Ss.indexOf(T.videoRange)-Ss.indexOf(S.videoRange);if(T.videoCodec!==S.videoCodec){const v=or(T.videoCodec),x=or(S.videoCodec);if(v!==x)return x-v}if(T.uri===S.uri&&T.codecSet!==S.codecSet){const v=Es(T.codecSet),x=Es(S.codecSet);if(v!==x)return x-v}return T.averageBitrate!==S.averageBitrate?T.averageBitrate-S.averageBitrate:0});let u=d[0];if(this.steering&&(l=this.steering.filterParsedLevels(l),l.length!==d.length)){for(let T=0;Tv&&v===this.hls.abrEwmaDefaultEstimate&&(this.hls.bandwidthEstimate=x)}break}const g=r&&!i,y=this.hls.config,p=!!(y.audioStreamController&&y.audioTrackController),E={levels:l,audioTracks:o,subtitleTracks:c,sessionData:t.sessionData,sessionKeys:t.sessionKeys,firstLevel:this._firstLevel,stats:t.stats,audio:r,video:i,altAudio:p&&!g&&o.some(T=>!!T.url)};h.end=performance.now(),this.hls.trigger(m.MANIFEST_PARSED,E)}get levels(){return this._levels.length===0?null:this._levels}get loadLevelObj(){return this.currentLevel}get level(){return this.currentLevelIndex}set level(e){const t=this._levels;if(t.length===0)return;if(e<0||e>=t.length){const h=new Error("invalid level idx"),d=e<0;if(this.hls.trigger(m.ERROR,{type:Y.OTHER_ERROR,details:R.LEVEL_SWITCH_ERROR,level:e,fatal:d,error:h,reason:h.message}),d)return;e=Math.min(e,t.length-1)}const s=this.currentLevelIndex,i=this.currentLevel,r=i?i.attrs["PATHWAY-ID"]:void 0,n=t[e],o=n.attrs["PATHWAY-ID"];if(this.currentLevelIndex=e,this.currentLevel=n,s===e&&i&&r===o)return;this.log(`Switching to level ${e} (${n.height?n.height+"p ":""}${n.videoRange?n.videoRange+" ":""}${n.codecSet?n.codecSet+" ":""}@${n.bitrate})${o?" with Pathway "+o:""} from level ${s}${r?" with Pathway "+r:""}`);const c={level:e,attrs:n.attrs,details:n.details,bitrate:n.bitrate,averageBitrate:n.averageBitrate,maxBitrate:n.maxBitrate,realBitrate:n.realBitrate,width:n.width,height:n.height,codecSet:n.codecSet,audioCodec:n.audioCodec,videoCodec:n.videoCodec,audioGroups:n.audioGroups,subtitleGroups:n.subtitleGroups,loaded:n.loaded,loadError:n.loadError,fragmentError:n.fragmentError,name:n.name,id:n.id,uri:n.uri,url:n.url,urlId:0,audioGroupIds:n.audioGroupIds,textGroupIds:n.textGroupIds};this.hls.trigger(m.LEVEL_SWITCHING,c);const l=n.details;if(!l||l.live){const h=this.switchParams(n.uri,i==null?void 0:i.details,l);this.loadPlaylist(h)}}get manualLevel(){return this.manualLevelIndex}set manualLevel(e){this.manualLevelIndex=e,this._startLevel===void 0&&(this._startLevel=e),e!==-1&&(this.level=e)}get firstLevel(){return this._firstLevel}set firstLevel(e){this._firstLevel=e}get startLevel(){if(this._startLevel===void 0){const e=this.hls.config.startLevel;return e!==void 0?e:this.hls.firstAutoLevel}return this._startLevel}set startLevel(e){this._startLevel=e}get pathways(){return this.steering?this.steering.pathways():[]}get pathwayPriority(){return this.steering?this.steering.pathwayPriority:null}set pathwayPriority(e){if(this.steering){const t=this.steering.pathways(),s=e.filter(i=>t.indexOf(i)!==-1);if(e.length<1){this.warn(`pathwayPriority ${e} should contain at least one pathway from list: ${t}`);return}this.steering.pathwayPriority=s}}onError(e,t){t.fatal||!t.context||t.context.type===J.LEVEL&&t.context.level===this.level&&this.checkRetry(t)}onFragBuffered(e,{frag:t}){if(t!==void 0&&t.type===U.MAIN){const s=t.elementaryStreams;if(!Object.keys(s).some(r=>!!s[r]))return;const i=this._levels[t.level];i!=null&&i.loadError&&(this.log(`Resetting level error count of ${i.loadError} on frag buffered`),i.loadError=0)}}onLevelLoaded(e,t){var s;const{level:i,details:r}=t,n=t.levelInfo;if(!n){var o;this.warn(`Invalid level index ${i}`),(o=t.deliveryDirectives)!=null&&o.skip&&(r.deltaUpdateFailed=!0);return}if(n===this.currentLevel||t.withoutMultiVariant){n.fragmentError===0&&(n.loadError=0);let c=n.details;c===t.details&&c.advanced&&(c=void 0),this.playlistLoaded(i,t,c)}else(s=t.deliveryDirectives)!=null&&s.skip&&(r.deltaUpdateFailed=!0)}loadPlaylist(e){super.loadPlaylist(),this.shouldLoadPlaylist(this.currentLevel)&&this.scheduleLoading(this.currentLevel,e)}loadingPlaylist(e,t){super.loadingPlaylist(e,t);const s=this.getUrlWithDirectives(e.uri,t),i=this.currentLevelIndex,r=e.attrs["PATHWAY-ID"],n=e.details,o=n==null?void 0:n.age;this.log(`Loading level index ${i}${(t==null?void 0:t.msn)!==void 0?" at sn "+t.msn+" part "+t.part:""}${r?" Pathway "+r:""}${o&&n.live?" age "+o.toFixed(1)+(n.type&&" "+n.type||""):""} ${s}`),this.hls.trigger(m.LEVEL_LOADING,{url:s,level:i,levelInfo:e,pathwayId:e.attrs["PATHWAY-ID"],id:0,deliveryDirectives:t||null})}get nextLoadLevel(){return this.manualLevelIndex!==-1?this.manualLevelIndex:this.hls.nextAutoLevel}set nextLoadLevel(e){this.level=e,this.manualLevelIndex===-1&&(this.hls.nextAutoLevel=e)}removeLevel(e){var t;if(this._levels.length===1)return;const s=this._levels.filter((r,n)=>n!==e?!0:(this.steering&&this.steering.removeLevel(r),r===this.currentLevel&&(this.currentLevel=null,this.currentLevelIndex=-1,r.details&&r.details.fragments.forEach(o=>o.level=-1)),!1));sa(s),this._levels=s,this.currentLevelIndex>-1&&(t=this.currentLevel)!=null&&t.details&&(this.currentLevelIndex=this.currentLevel.details.fragments[0].level),this.manualLevelIndex>-1&&(this.manualLevelIndex=this.currentLevelIndex);const i=s.length-1;this._firstLevel=Math.min(this._firstLevel,i),this._startLevel&&(this._startLevel=Math.min(this._startLevel,i)),this.hls.trigger(m.LEVELS_UPDATED,{levels:s})}onLevelsUpdated(e,{levels:t}){this._levels=t}checkMaxAutoUpdated(){const{autoLevelCapping:e,maxAutoLevel:t,maxHdcpLevel:s}=this.hls;this._maxAutoLevel!==t&&(this._maxAutoLevel=t,this.hls.trigger(m.MAX_AUTO_LEVEL_UPDATED,{autoLevelCapping:e,levels:this.levels,maxAutoLevel:t,minAutoLevel:this.hls.minAutoLevel,maxHdcpLevel:s}))}}function yn(a){const e={};a.forEach(t=>{const s=t.groupId||"";t.id=e[s]=e[s]||0,e[s]++})}function Qa(){return self.SourceBuffer||self.WebKitSourceBuffer}function Za(){if(!at())return!1;const e=Qa();return!e||e.prototype&&typeof e.prototype.appendBuffer=="function"&&typeof e.prototype.remove=="function"}function su(){if(!Za())return!1;const a=at();return typeof(a==null?void 0:a.isTypeSupported)=="function"&&(["avc1.42E01E,mp4a.40.2","av01.0.01M.08","vp09.00.50.08"].some(e=>a.isTypeSupported(Gt(e,"video")))||["mp4a.40.2","fLaC"].some(e=>a.isTypeSupported(Gt(e,"audio"))))}function iu(){var a;const e=Qa();return typeof(e==null||(a=e.prototype)==null?void 0:a.changeType)=="function"}const ru=100;class nu extends Fi{constructor(e,t,s){super(e,t,s,"stream-controller",U.MAIN),this.audioCodecSwap=!1,this.level=-1,this._forceStartLoad=!1,this._hasEnoughToStart=!1,this.altAudio=0,this.audioOnly=!1,this.fragPlaying=null,this.fragLastKbps=0,this.couldBacktrack=!1,this.backtrackFragment=null,this.audioCodecSwitch=!1,this.videoBuffer=null,this.onMediaPlaying=()=>{this.tick()},this.onMediaSeeked=()=>{const i=this.media,r=i?i.currentTime:null;if(r===null||!B(r)||(this.log(`Media seeked to ${r.toFixed(3)}`),!this.getBufferedFrag(r)))return;const n=this.getFwdBufferInfoAtPos(i,r,U.MAIN,0);if(n===null||n.len===0){this.warn(`Main forward buffer length at ${r} on "seeked" event ${n?n.len:"empty"})`);return}this.tick()},this.registerListeners()}registerListeners(){super.registerListeners();const{hls:e}=this;e.on(m.MANIFEST_PARSED,this.onManifestParsed,this),e.on(m.LEVEL_LOADING,this.onLevelLoading,this),e.on(m.LEVEL_LOADED,this.onLevelLoaded,this),e.on(m.FRAG_LOAD_EMERGENCY_ABORTED,this.onFragLoadEmergencyAborted,this),e.on(m.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),e.on(m.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),e.on(m.BUFFER_CREATED,this.onBufferCreated,this),e.on(m.BUFFER_FLUSHED,this.onBufferFlushed,this),e.on(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.on(m.FRAG_BUFFERED,this.onFragBuffered,this)}unregisterListeners(){super.unregisterListeners();const{hls:e}=this;e.off(m.MANIFEST_PARSED,this.onManifestParsed,this),e.off(m.LEVEL_LOADED,this.onLevelLoaded,this),e.off(m.FRAG_LOAD_EMERGENCY_ABORTED,this.onFragLoadEmergencyAborted,this),e.off(m.AUDIO_TRACK_SWITCHING,this.onAudioTrackSwitching,this),e.off(m.AUDIO_TRACK_SWITCHED,this.onAudioTrackSwitched,this),e.off(m.BUFFER_CREATED,this.onBufferCreated,this),e.off(m.BUFFER_FLUSHED,this.onBufferFlushed,this),e.off(m.LEVELS_UPDATED,this.onLevelsUpdated,this),e.off(m.FRAG_BUFFERED,this.onFragBuffered,this)}onHandlerDestroying(){this.onMediaPlaying=this.onMediaSeeked=null,this.unregisterListeners(),super.onHandlerDestroying()}startLoad(e,t){if(this.levels){const{lastCurrentTime:s,hls:i}=this;if(this.stopLoad(),this.setInterval(ru),this.level=-1,!this.startFragRequested){let r=i.startLevel;r===-1&&(i.config.testBandwidth&&this.levels.length>1?(r=0,this.bitrateTest=!0):r=i.firstAutoLevel),i.nextLoadLevel=r,this.level=i.loadLevel,this._hasEnoughToStart=!!t}s>0&&e===-1&&!t&&(this.log(`Override startPosition with lastCurrentTime @${s.toFixed(3)}`),e=s),this.state=C.IDLE,this.nextLoadPosition=this.lastCurrentTime=e+this.timelineOffset,this.startPosition=t?-1:e,this.tick()}else this._forceStartLoad=!0,this.state=C.STOPPED}stopLoad(){this._forceStartLoad=!1,super.stopLoad()}doTick(){switch(this.state){case C.WAITING_LEVEL:{const{levels:e,level:t}=this,s=e==null?void 0:e[t],i=s==null?void 0:s.details;if(i&&(!i.live||this.levelLastLoaded===s&&!this.waitForLive(s))){if(this.waitForCdnTuneIn(i))break;this.state=C.IDLE;break}else if(this.hls.nextLoadLevel!==this.level){this.state=C.IDLE;break}break}case C.FRAG_LOADING_WAITING_RETRY:this.checkRetryDate();break}this.state===C.IDLE&&this.doTickIdle(),this.onTickEnd()}onTickEnd(){var e;super.onTickEnd(),(e=this.media)!=null&&e.readyState&&this.media.seeking===!1&&(this.lastCurrentTime=this.media.currentTime),this.checkFragmentChanged()}doTickIdle(){const{hls:e,levelLastLoaded:t,levels:s,media:i}=this;if(t===null||!i&&!this.primaryPrefetch&&(this.startFragRequested||!e.config.startFragPrefetch)||this.altAudio&&this.audioOnly)return;const r=this.buffering?e.nextLoadLevel:e.loadLevel;if(!(s!=null&&s[r]))return;const n=s[r],o=this.getMainFwdBufferInfo();if(o===null)return;const c=this.getLevelDetails();if(c&&this._streamEnded(o,c)){const y={};this.altAudio===2&&(y.type="video"),this.hls.trigger(m.BUFFER_EOS,y),this.state=C.ENDED;return}if(!this.buffering)return;e.loadLevel!==r&&e.manualLevel===-1&&this.log(`Adapting to level ${r} from level ${this.level}`),this.level=e.nextLoadLevel=r;const l=n.details;if(!l||this.state===C.WAITING_LEVEL||this.waitForLive(n)){this.level=r,this.state=C.WAITING_LEVEL,this.startFragRequested=!1;return}const h=o.len,d=this.getMaxBufferLength(n.maxBitrate);if(h>=d)return;this.backtrackFragment&&this.backtrackFragment.start>o.end&&(this.backtrackFragment=null);const u=this.backtrackFragment?this.backtrackFragment.start:o.end;let f=this.getNextFragment(u,l);if(this.couldBacktrack&&!this.fragPrevious&&f&&de(f)&&this.fragmentTracker.getState(f)!==fe.OK){var g;const p=((g=this.backtrackFragment)!=null?g:f).sn-l.startSN,E=l.fragments[p-1];E&&f.cc===E.cc&&(f=E,this.fragmentTracker.removeFragment(E))}else this.backtrackFragment&&o.len&&(this.backtrackFragment=null);if(f&&this.isLoopLoading(f,u)){if(!f.gap){const p=this.audioOnly&&!this.altAudio?ae.AUDIO:ae.VIDEO,E=(p===ae.VIDEO?this.videoBuffer:this.mediaBuffer)||this.media;E&&this.afterBufferFlushed(E,p,U.MAIN)}f=this.getNextFragmentLoopLoading(f,l,o,U.MAIN,d)}f&&(this.exceedsMaxBuffer(o,d,f)||(f.initSegment&&!f.initSegment.data&&!this.bitrateTest&&(f=f.initSegment),this.loadFragment(f,n,u)))}loadFragment(e,t,s){const i=this.fragmentTracker.getState(e);i===fe.NOT_LOADED||i===fe.PARTIAL?de(e)?this.bitrateTest?(this.log(`Fragment ${e.sn} of level ${e.level} is being downloaded to test bitrate and will not be buffered`),this._loadBitrateTestFrag(e,t)):super.loadFragment(e,t,s):this._loadInitSegment(e,t):this.clearTrackerIfNeeded(e)}getBufferedFrag(e){return this.fragmentTracker.getBufferedFrag(e,U.MAIN)}followingBufferedFrag(e){return e?this.getBufferedFrag(e.end+.5):null}immediateLevelSwitch(){if(this.abortCurrentFrag(),this.flushMainBuffer(0,Number.POSITIVE_INFINITY),this.altAudio!==0){var e;(((e=this.getLevelDetails())==null?void 0:e.fragmentStart)||0)>this.lastCurrentTime&&super.flushMainBuffer(0,Number.POSITIVE_INFINITY,"audio")}}nextLevelSwitch(){const{levels:e,media:t}=this;if(t!=null&&t.readyState){let s;const i=this.getAppendedFrag(t.currentTime);i&&i.start>1&&this.flushMainBuffer(0,i.start-1);const r=this.getLevelDetails();if(r!=null&&r.live){const o=this.getMainFwdBufferInfo();if(!o||o.len=n-t.maxFragLookUpTolerance&&r<=o;if(i!==null&&s.duration>i&&(r{this.hls&&this.hls.trigger(m.AUDIO_TRACK_SWITCHED,t)}),s.trigger(m.BUFFER_FLUSHING,{startOffset:0,endOffset:Number.POSITIVE_INFINITY,type:null});return}s.trigger(m.AUDIO_TRACK_SWITCHED,t)}}onAudioTrackSwitched(e,t){const s=vs(t.url,this.hls);if(s){const i=this.videoBuffer;i&&this.mediaBuffer!==i&&(this.log("Switching on alternate audio, use video.buffered to schedule main fragment loading"),this.mediaBuffer=i)}this.altAudio=s?2:0,this.tick()}onBufferCreated(e,t){const s=t.tracks;let i,r,n=!1;for(const o in s){const c=s[o];if(c.id==="main"){if(r=o,i=c,o==="video"){const l=s[o];l&&(this.videoBuffer=l.buffer)}}else n=!0}n&&i?(this.log(`Alternate track found, use ${r}.buffered to schedule main fragment loading`),this.mediaBuffer=i.buffer):this.mediaBuffer=this.media}onFragBuffered(e,t){const{frag:s,part:i}=t,r=s.type===U.MAIN;if(r){if(this.fragContextChanged(s)){this.warn(`Fragment ${s.sn}${i?" p: "+i.index:""} of level ${s.level} finished buffering, but was aborted. state: ${this.state}`),this.state===C.PARSED&&(this.state=C.IDLE);return}const o=i?i.stats:s.stats;this.fragLastKbps=Math.round(8*o.total/(o.buffering.end-o.loading.first)),de(s)&&(this.fragPrevious=s),this.fragBufferedComplete(s,i)}const n=this.media;n&&(!this._hasEnoughToStart&&X.getBuffered(n).length&&(this._hasEnoughToStart=!0,this.seekToStartPos()),r&&this.tick())}get hasEnoughToStart(){return this._hasEnoughToStart}onError(e,t){var s;if(t.fatal){this.state=C.ERROR;return}switch(t.details){case R.FRAG_GAP:case R.FRAG_PARSING_ERROR:case R.FRAG_DECRYPT_ERROR:case R.FRAG_LOAD_ERROR:case R.FRAG_LOAD_TIMEOUT:case R.KEY_LOAD_ERROR:case R.KEY_LOAD_TIMEOUT:this.onFragmentOrKeyLoadError(U.MAIN,t);break;case R.LEVEL_LOAD_ERROR:case R.LEVEL_LOAD_TIMEOUT:case R.LEVEL_PARSING_ERROR:!t.levelRetry&&this.state===C.WAITING_LEVEL&&((s=t.context)==null?void 0:s.type)===J.LEVEL&&(this.state=C.IDLE);break;case R.BUFFER_ADD_CODEC_ERROR:case R.BUFFER_APPEND_ERROR:if(t.parent!=="main")return;this.reduceLengthAndFlushBuffer(t)&&this.resetLoadingState();break;case R.BUFFER_FULL_ERROR:if(t.parent!=="main")return;this.reduceLengthAndFlushBuffer(t)&&(!this.config.interstitialsController&&this.config.assetPlayerId?this._hasEnoughToStart=!0:this.flushMainBuffer(0,Number.POSITIVE_INFINITY));break;case R.INTERNAL_EXCEPTION:this.recoverWorkerError(t);break}}onFragLoadEmergencyAborted(){this.state=C.IDLE,this._hasEnoughToStart||(this.startFragRequested=!1,this.nextLoadPosition=this.lastCurrentTime),this.tickImmediate()}onBufferFlushed(e,{type:t}){if(t!==ae.AUDIO||!this.altAudio){const s=(t===ae.VIDEO?this.videoBuffer:this.mediaBuffer)||this.media;s&&(this.afterBufferFlushed(s,t,U.MAIN),this.tick())}}onLevelsUpdated(e,t){this.level>-1&&this.fragCurrent&&(this.level=this.fragCurrent.level,this.level===-1&&this.resetWhenMissingContext(this.fragCurrent)),this.levels=t.levels}swapAudioCodec(){this.audioCodecSwap=!this.audioCodecSwap}seekToStartPos(){const{media:e}=this;if(!e)return;const t=e.currentTime;let s=this.startPosition;if(s>=0&&t0&&(c{const{hls:i}=this,r=s==null?void 0:s.frag;if(!r||this.fragContextChanged(r))return;t.fragmentError=0,this.state=C.IDLE,this.startFragRequested=!1,this.bitrateTest=!1;const n=r.stats;n.parsing.start=n.parsing.end=n.buffering.start=n.buffering.end=self.performance.now(),i.trigger(m.FRAG_LOADED,s),r.bitrateTest=!1}).catch(s=>{this.state===C.STOPPED||this.state===C.ERROR||(this.warn(s),this.resetFragmentLoading(e))})}_handleTransmuxComplete(e){const t=this.playlistType,{hls:s}=this,{remuxResult:i,chunkMeta:r}=e,n=this.getCurrentContext(r);if(!n){this.resetWhenMissingContext(r);return}const{frag:o,part:c,level:l}=n,{video:h,text:d,id3:u,initSegment:f}=i,{details:g}=l,y=this.altAudio?void 0:i.audio;if(this.fragContextChanged(o)){this.fragmentTracker.removeFragment(o);return}if(this.state=C.PARSING,f){const p=f.tracks;if(p){const v=o.initSegment||o;if(this.unhandledEncryptionError(f,o))return;this._bufferInitSegment(l,p,v,r),s.trigger(m.FRAG_PARSING_INIT_SEGMENT,{frag:v,id:t,tracks:p})}const E=f.initPTS,T=f.timescale,S=this.initPTS[o.cc];if(B(E)&&(!S||S.baseTime!==E||S.timescale!==T)){const v=f.trackId;this.initPTS[o.cc]={baseTime:E,timescale:T,trackId:v},s.trigger(m.INIT_PTS_FOUND,{frag:o,id:t,initPTS:E,timescale:T,trackId:v})}}if(h&&g){y&&h.type==="audiovideo"&&this.logMuxedErr(o);const p=g.fragments[o.sn-1-g.startSN],E=o.sn===g.startSN,T=!p||o.cc>p.cc;if(i.independent!==!1){const{startPTS:S,endPTS:v,startDTS:x,endDTS:D}=h;if(c)c.elementaryStreams[h.type]={startPTS:S,endPTS:v,startDTS:x,endDTS:D};else if(h.firstKeyFrame&&h.independent&&r.id===1&&!T&&(this.couldBacktrack=!0),h.dropped&&h.independent){const A=this.getMainFwdBufferInfo(),_=(A?A.end:this.getLoadPosition())+this.config.maxBufferHole,b=h.firstKeyFramePTS?h.firstKeyFramePTS:S;if(!E&&_ys&&(o.gap=!0);o.setElementaryStreamInfo(h.type,S,v,x,D),this.backtrackFragment&&(this.backtrackFragment=o),this.bufferFragmentData(h,o,c,r,E||T)}else if(E||T)o.gap=!0;else{this.backtrack(o);return}}if(y){const{startPTS:p,endPTS:E,startDTS:T,endDTS:S}=y;c&&(c.elementaryStreams[ae.AUDIO]={startPTS:p,endPTS:E,startDTS:T,endDTS:S}),o.setElementaryStreamInfo(ae.AUDIO,p,E,T,S),this.bufferFragmentData(y,o,c,r)}if(g&&u!=null&&u.samples.length){const p={id:t,frag:o,details:g,samples:u.samples};s.trigger(m.FRAG_PARSING_METADATA,p)}if(g&&d){const p={id:t,frag:o,details:g,samples:d.samples};s.trigger(m.FRAG_PARSING_USERDATA,p)}}logMuxedErr(e){this.warn(`${de(e)?"Media":"Init"} segment with muxed audiovideo where only video expected: ${e.url}`)}_bufferInitSegment(e,t,s,i){if(this.state!==C.PARSING)return;this.audioOnly=!!t.audio&&!t.video,this.altAudio&&!this.audioOnly&&(delete t.audio,t.audiovideo&&this.logMuxedErr(s));const{audio:r,video:n,audiovideo:o}=t;if(r){const l=e.audioCodec;let h=hs(r.codec,l);h==="mp4a"&&(h="mp4a.40.5");const d=navigator.userAgent.toLowerCase();if(this.audioCodecSwitch){h&&(h.indexOf("mp4a.40.5")!==-1?h="mp4a.40.2":h="mp4a.40.5");const u=r.metadata;u&&"channelCount"in u&&(u.channelCount||1)!==1&&d.indexOf("firefox")===-1&&(h="mp4a.40.5")}h&&h.indexOf("mp4a.40.5")!==-1&&d.indexOf("android")!==-1&&r.container!=="audio/mpeg"&&(h="mp4a.40.2",this.log(`Android: force audio codec to ${h}`)),l&&l!==h&&this.log(`Swapping manifest audio codec "${l}" for "${h}"`),r.levelCodec=h,r.id=U.MAIN,this.log(`Init audio buffer, container:${r.container}, codecs[selected/level/parsed]=[${h||""}/${l||""}/${r.codec}]`),delete t.audiovideo}if(n){n.levelCodec=e.videoCodec,n.id=U.MAIN;const l=n.codec;if((l==null?void 0:l.length)===4)switch(l){case"hvc1":case"hev1":n.codec="hvc1.1.6.L120.90";break;case"av01":n.codec="av01.0.04M.08";break;case"avc1":n.codec="avc1.42e01e";break}this.log(`Init video buffer, container:${n.container}, codecs[level/parsed]=[${e.videoCodec||""}/${l}]${n.codec!==l?" parsed-corrected="+n.codec:""}${n.supplemental?" supplemental="+n.supplemental:""}`),delete t.audiovideo}o&&(this.log(`Init audiovideo buffer, container:${o.container}, codecs[level/parsed]=[${e.codecs}/${o.codec}]`),delete t.video,delete t.audio);const c=Object.keys(t);if(c.length){if(this.hls.trigger(m.BUFFER_CODECS,t),!this.hls)return;c.forEach(l=>{const d=t[l].initSegment;d!=null&&d.byteLength&&this.hls.trigger(m.BUFFER_APPENDING,{type:l,data:d,frag:s,part:null,chunkMeta:i,parent:s.type})})}this.tickImmediate()}getMainFwdBufferInfo(){const e=this.mediaBuffer&&this.altAudio===2?this.mediaBuffer:this.media;return this.getFwdBufferInfo(e,U.MAIN)}get maxBufferLength(){const{levels:e,level:t}=this,s=e==null?void 0:e[t];return s?this.getMaxBufferLength(s.maxBitrate):this.config.maxBufferLength}backtrack(e){this.couldBacktrack=!0,this.backtrackFragment=e,this.resetTransmuxer(),this.flushBufferGap(e),this.fragmentTracker.removeFragment(e),this.fragPrevious=null,this.nextLoadPosition=e.start,this.state=C.IDLE}checkFragmentChanged(){const e=this.media;let t=null;if(e&&e.readyState>1&&e.seeking===!1){const s=e.currentTime;if(X.isBuffered(e,s)?t=this.getAppendedFrag(s):X.isBuffered(e,s+.1)&&(t=this.getAppendedFrag(s+.1)),t){this.backtrackFragment=null;const i=this.fragPlaying,r=t.level;(!i||t.sn!==i.sn||i.level!==r)&&(this.fragPlaying=t,this.hls.trigger(m.FRAG_CHANGED,{frag:t}),(!i||i.level!==r)&&this.hls.trigger(m.LEVEL_SWITCHED,{level:r}))}}}get nextLevel(){const e=this.nextBufferedFrag;return e?e.level:-1}get currentFrag(){var e;if(this.fragPlaying)return this.fragPlaying;const t=((e=this.media)==null?void 0:e.currentTime)||this.lastCurrentTime;return B(t)?this.getAppendedFrag(t):null}get currentProgramDateTime(){var e;const t=((e=this.media)==null?void 0:e.currentTime)||this.lastCurrentTime;if(B(t)){const s=this.getLevelDetails(),i=this.currentFrag||(s?pt(null,s.fragments,t):null);if(i){const r=i.programDateTime;if(r!==null){const n=r+(t-i.start)*1e3;return new Date(n)}}}return null}get currentLevel(){const e=this.currentFrag;return e?e.level:-1}get nextBufferedFrag(){const e=this.currentFrag;return e?this.followingBufferedFrag(e):null}get forceStartLoad(){return this._forceStartLoad}}class au extends Oe{constructor(e,t){super("key-loader",t),this.config=void 0,this.keyIdToKeyInfo={},this.emeController=null,this.config=e}abort(e){for(const s in this.keyIdToKeyInfo){const i=this.keyIdToKeyInfo[s].loader;if(i){var t;if(e&&e!==((t=i.context)==null?void 0:t.frag.type))return;i.abort()}}}detach(){for(const e in this.keyIdToKeyInfo){const t=this.keyIdToKeyInfo[e];(t.mediaKeySessionContext||t.decryptdata.isCommonEncryption)&&delete this.keyIdToKeyInfo[e]}}destroy(){this.detach();for(const e in this.keyIdToKeyInfo){const t=this.keyIdToKeyInfo[e].loader;t&&t.destroy()}this.keyIdToKeyInfo={}}createKeyLoadError(e,t=R.KEY_LOAD_ERROR,s,i,r){return new Ze({type:Y.NETWORK_ERROR,details:t,fatal:!1,frag:e,response:r,error:s,networkDetails:i})}loadClear(e,t,s){if(this.emeController&&this.config.emeEnabled&&!this.emeController.getSelectedKeySystemFormats().length){if(t.length)for(let i=0,r=t.length;i{if(!this.emeController)return;n.setKeyFormat(o);const c=us(o);if(c)return this.emeController.getKeySystemAccess([c])})}if(this.config.requireKeySystemAccessOnStart){const i=Mt(this.config);if(i.length)return this.emeController.getKeySystemAccess(i)}}return null}load(e){return!e.decryptdata&&e.encrypted&&this.emeController&&this.config.emeEnabled?this.emeController.selectKeySystemFormat(e).then(t=>this.loadInternal(e,t)):this.loadInternal(e)}loadInternal(e,t){var s,i;t&&e.setKeyFormat(t);const r=e.decryptdata;if(!r){const l=new Error(t?`Expected frag.decryptdata to be defined after setting format ${t}`:`Missing decryption data on fragment in onKeyLoading (emeEnabled with controller: ${this.emeController&&this.config.emeEnabled})`);return Promise.reject(this.createKeyLoadError(e,R.KEY_LOAD_ERROR,l))}const n=r.uri;if(!n)return Promise.reject(this.createKeyLoadError(e,R.KEY_LOAD_ERROR,new Error(`Invalid key URI: "${n}"`)));const o=si(r);let c=this.keyIdToKeyInfo[o];if((s=c)!=null&&s.decryptdata.key)return r.key=c.decryptdata.key,Promise.resolve({frag:e,keyInfo:c});if(this.emeController&&(i=c)!=null&&i.keyLoadPromise)switch(this.emeController.getKeyStatus(c.decryptdata)){case"usable":case"usable-in-future":return c.keyLoadPromise.then(h=>{const{keyInfo:d}=h;return r.key=d.decryptdata.key,{frag:e,keyInfo:d}})}switch(this.log(`${this.keyIdToKeyInfo[o]?"Rel":"L"}oading${r.keyId?" keyId: "+ye(r.keyId):""} URI: ${r.uri} from ${e.type} ${e.level}`),c=this.keyIdToKeyInfo[o]={decryptdata:r,keyLoadPromise:null,loader:null,mediaKeySessionContext:null},r.method){case"SAMPLE-AES":case"SAMPLE-AES-CENC":case"SAMPLE-AES-CTR":return r.keyFormat==="identity"?this.loadKeyHTTP(c,e):this.loadKeyEME(c,e);case"AES-128":case"AES-256":case"AES-256-CTR":return this.loadKeyHTTP(c,e);default:return Promise.reject(this.createKeyLoadError(e,R.KEY_LOAD_ERROR,new Error(`Key supplied with unsupported METHOD: "${r.method}"`)))}}loadKeyEME(e,t){const s={frag:t,keyInfo:e};if(this.emeController&&this.config.emeEnabled){var i;if(!e.decryptdata.keyId&&(i=t.initSegment)!=null&&i.data){const n=Lo(t.initSegment.data);if(n.length){let o=n[0];o.some(c=>c!==0)?(this.log(`Using keyId found in init segment ${ye(o)}`),nt.setKeyIdForUri(e.decryptdata.uri,o)):(o=nt.addKeyIdForUri(e.decryptdata.uri),this.log(`Generating keyId to patch media ${ye(o)}`)),e.decryptdata.keyId=o}}if(!e.decryptdata.keyId&&!de(t))return Promise.resolve(s);const r=this.emeController.loadKey(s);return(e.keyLoadPromise=r.then(n=>(e.mediaKeySessionContext=n,s))).catch(n=>{throw e.keyLoadPromise=null,"data"in n&&(n.data.frag=t),n})}return Promise.resolve(s)}loadKeyHTTP(e,t){const s=this.config,i=s.loader,r=new i(s);return t.keyLoader=e.loader=r,e.keyLoadPromise=new Promise((n,o)=>{const c={keyInfo:e,frag:t,responseType:"arraybuffer",url:e.decryptdata.uri},l=s.keyLoadPolicy.default,h={loadPolicy:l,timeout:l.maxLoadTimeMs,maxRetry:0,retryDelay:0,maxRetryDelay:0},d={onSuccess:(u,f,g,y)=>{const{frag:p,keyInfo:E}=g,T=si(E.decryptdata);if(!p.decryptdata||E!==this.keyIdToKeyInfo[T])return o(this.createKeyLoadError(p,R.KEY_LOAD_ERROR,new Error("after key load, decryptdata unset or changed"),y));E.decryptdata.key=p.decryptdata.key=new Uint8Array(u.data),p.keyLoader=null,E.loader=null,n({frag:p,keyInfo:E})},onError:(u,f,g,y)=>{this.resetLoader(f),o(this.createKeyLoadError(t,R.KEY_LOAD_ERROR,new Error(`HTTP Error ${u.code} loading key ${u.text}`),g,ie({url:c.url,data:void 0},u)))},onTimeout:(u,f,g)=>{this.resetLoader(f),o(this.createKeyLoadError(t,R.KEY_LOAD_TIMEOUT,new Error("key loading timed out"),g))},onAbort:(u,f,g)=>{this.resetLoader(f),o(this.createKeyLoadError(t,R.INTERNAL_ABORTED,new Error("key loading aborted"),g))}};r.load(c,h,d)})}resetLoader(e){const{frag:t,keyInfo:s,url:i}=e,r=s.loader;t.keyLoader===r&&(t.keyLoader=null,s.loader=null);const n=si(s.decryptdata)||i;delete this.keyIdToKeyInfo[n],r&&r.destroy()}}function si(a){if(a.keyFormat!==Ee.FAIRPLAY){const e=a.keyId;if(e)return ye(e)}return a.uri}function En(a){const{type:e}=a;switch(e){case J.AUDIO_TRACK:return U.AUDIO;case J.SUBTITLE_TRACK:return U.SUBTITLE;default:return U.MAIN}}function ii(a,e){let t=a.url;return(t===void 0||t.indexOf("data:")===0)&&(t=e.url),t}class ou{constructor(e){this.hls=void 0,this.loaders=Object.create(null),this.variableList=null,this.onManifestLoaded=this.checkAutostartLoad,this.hls=e,this.registerListeners()}startLoad(e){}stopLoad(){this.destroyInternalLoaders()}registerListeners(){const{hls:e}=this;e.on(m.MANIFEST_LOADING,this.onManifestLoading,this),e.on(m.LEVEL_LOADING,this.onLevelLoading,this),e.on(m.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),e.on(m.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this),e.on(m.LEVELS_UPDATED,this.onLevelsUpdated,this)}unregisterListeners(){const{hls:e}=this;e.off(m.MANIFEST_LOADING,this.onManifestLoading,this),e.off(m.LEVEL_LOADING,this.onLevelLoading,this),e.off(m.AUDIO_TRACK_LOADING,this.onAudioTrackLoading,this),e.off(m.SUBTITLE_TRACK_LOADING,this.onSubtitleTrackLoading,this),e.off(m.LEVELS_UPDATED,this.onLevelsUpdated,this)}createInternalLoader(e){const t=this.hls.config,s=t.pLoader,i=t.loader,r=s||i,n=new r(t);return this.loaders[e.type]=n,n}getInternalLoader(e){return this.loaders[e.type]}resetInternalLoader(e){this.loaders[e]&&delete this.loaders[e]}destroyInternalLoaders(){for(const e in this.loaders){const t=this.loaders[e];t&&t.destroy(),this.resetInternalLoader(e)}}destroy(){this.variableList=null,this.unregisterListeners(),this.destroyInternalLoaders()}onManifestLoading(e,t){const{url:s}=t;this.variableList=null,this.load({id:null,level:0,responseType:"text",type:J.MANIFEST,url:s,deliveryDirectives:null,levelOrTrack:null})}onLevelLoading(e,t){const{id:s,level:i,pathwayId:r,url:n,deliveryDirectives:o,levelInfo:c}=t;this.load({id:s,level:i,pathwayId:r,responseType:"text",type:J.LEVEL,url:n,deliveryDirectives:o,levelOrTrack:c})}onAudioTrackLoading(e,t){const{id:s,groupId:i,url:r,deliveryDirectives:n,track:o}=t;this.load({id:s,groupId:i,level:null,responseType:"text",type:J.AUDIO_TRACK,url:r,deliveryDirectives:n,levelOrTrack:o})}onSubtitleTrackLoading(e,t){const{id:s,groupId:i,url:r,deliveryDirectives:n,track:o}=t;this.load({id:s,groupId:i,level:null,responseType:"text",type:J.SUBTITLE_TRACK,url:r,deliveryDirectives:n,levelOrTrack:o})}onLevelsUpdated(e,t){const s=this.loaders[J.LEVEL];if(s){const i=s.context;i&&!t.levels.some(r=>r===i.levelOrTrack)&&(s.abort(),delete this.loaders[J.LEVEL])}}load(e){var t;const s=this.hls.config;let i=this.getInternalLoader(e);if(i){const l=this.hls.logger,h=i.context;if(h&&h.levelOrTrack===e.levelOrTrack&&(h.url===e.url||h.deliveryDirectives&&!e.deliveryDirectives)){h.url===e.url?l.log(`[playlist-loader]: ignore ${e.url} ongoing request`):l.log(`[playlist-loader]: ignore ${e.url} in favor of ${h.url}`);return}l.log(`[playlist-loader]: aborting previous loader for type: ${e.type}`),i.abort()}let r;if(e.type===J.MANIFEST?r=s.manifestLoadPolicy.default:r=ne({},s.playlistLoadPolicy.default,{timeoutRetry:null,errorRetry:null}),i=this.createInternalLoader(e),B((t=e.deliveryDirectives)==null?void 0:t.part)){let l;if(e.type===J.LEVEL&&e.level!==null?l=this.hls.levels[e.level].details:e.type===J.AUDIO_TRACK&&e.id!==null?l=this.hls.audioTracks[e.id].details:e.type===J.SUBTITLE_TRACK&&e.id!==null&&(l=this.hls.subtitleTracks[e.id].details),l){const h=l.partTarget,d=l.targetduration;if(h&&d){const u=Math.max(h*3,d*.8)*1e3;r=ne({},r,{maxTimeToFirstByteMs:Math.min(u,r.maxTimeToFirstByteMs),maxLoadTimeMs:Math.min(u,r.maxTimeToFirstByteMs)})}}}const n=r.errorRetry||r.timeoutRetry||{},o={loadPolicy:r,timeout:r.maxLoadTimeMs,maxRetry:n.maxNumRetry||0,retryDelay:n.retryDelayMs||0,maxRetryDelay:n.maxRetryDelayMs||0},c={onSuccess:(l,h,d,u)=>{const f=this.getInternalLoader(d);this.resetInternalLoader(d.type);const g=l.data;h.parsing.start=performance.now(),qe.isMediaPlaylist(g)||d.type!==J.MANIFEST?this.handleTrackOrLevelPlaylist(l,h,d,u||null,f):this.handleMasterPlaylist(l,h,d,u)},onError:(l,h,d,u)=>{this.handleNetworkError(h,d,!1,l,u)},onTimeout:(l,h,d)=>{this.handleNetworkError(h,d,!0,void 0,l)}};i.load(e,o,c)}checkAutostartLoad(){if(!this.hls)return;const{config:{autoStartLoad:e,startPosition:t},forceStartLoad:s}=this.hls;(e||s)&&(this.hls.logger.log(`${e?"auto":"force"} startLoad with configured startPosition ${t}`),this.hls.startLoad(t))}handleMasterPlaylist(e,t,s,i){const r=this.hls,n=e.data,o=ii(e,s),c=qe.parseMasterPlaylist(n,o);if(c.playlistParsingError){t.parsing.end=performance.now(),this.handleManifestParsingError(e,s,c.playlistParsingError,i,t);return}const{contentSteering:l,levels:h,sessionData:d,sessionKeys:u,startTimeOffset:f,variableList:g}=c;this.variableList=g,h.forEach(T=>{const{unknownCodecs:S}=T;if(S){const{preferManagedMediaSource:v}=this.hls.config;let{audioCodec:x,videoCodec:D}=T;for(let A=S.length;A--;){const _=S[A];Ut(_,"audio",v)?(T.audioCodec=x=x?`${x},${_}`:_,Pt.audio[x.substring(0,4)]=2,S.splice(A,1)):Ut(_,"video",v)&&(T.videoCodec=D=D?`${D},${_}`:_,Pt.video[D.substring(0,4)]=2,S.splice(A,1))}}});const{AUDIO:y=[],SUBTITLES:p,"CLOSED-CAPTIONS":E}=qe.parseMasterPlaylistMedia(n,o,c);y.length&&!y.some(S=>!S.url)&&h[0].audioCodec&&!h[0].attrs.AUDIO&&(this.hls.logger.log("[playlist-loader]: audio codec signaled in quality level, but no embedded audio track signaled, create one"),y.unshift({type:"main",name:"main",groupId:"main",default:!1,autoselect:!1,forced:!1,id:-1,attrs:new le({}),bitrate:0,url:""})),r.trigger(m.MANIFEST_LOADED,{levels:h,audioTracks:y,subtitles:p,captions:E,contentSteering:l,url:o,stats:t,networkDetails:i,sessionData:d,sessionKeys:u,startTimeOffset:f,variableList:g})}handleTrackOrLevelPlaylist(e,t,s,i,r){const n=this.hls,{id:o,level:c,type:l}=s,h=ii(e,s),d=B(c)?c:B(o)?o:0,u=En(s),f=qe.parseLevelPlaylist(e.data,h,d,u,0,this.variableList);if(l===J.MANIFEST){const g={attrs:new le({}),bitrate:0,details:f,name:"",url:h};f.requestScheduled=t.loading.start+Jn(f,0),n.trigger(m.MANIFEST_LOADED,{levels:[g],audioTracks:[],url:h,stats:t,networkDetails:i,sessionData:null,sessionKeys:null,contentSteering:null,startTimeOffset:null,variableList:null})}t.parsing.end=performance.now(),s.levelDetails=f,this.handlePlaylistLoaded(f,e,t,s,i,r)}handleManifestParsingError(e,t,s,i,r){this.hls.trigger(m.ERROR,{type:Y.NETWORK_ERROR,details:R.MANIFEST_PARSING_ERROR,fatal:t.type===J.MANIFEST,url:e.url,err:s,error:s,reason:s.message,response:e,context:t,networkDetails:i,stats:r})}handleNetworkError(e,t,s=!1,i,r){let n=`A network ${s?"timeout":"error"+(i?" (status "+i.code+")":"")} occurred while loading ${e.type}`;e.type===J.LEVEL?n+=`: ${e.level} id: ${e.id}`:(e.type===J.AUDIO_TRACK||e.type===J.SUBTITLE_TRACK)&&(n+=` id: ${e.id} group-id: "${e.groupId}"`);const o=new Error(n);this.hls.logger.warn(`[playlist-loader]: ${n}`);let c=R.UNKNOWN,l=!1;const h=this.getInternalLoader(e);switch(e.type){case J.MANIFEST:c=s?R.MANIFEST_LOAD_TIMEOUT:R.MANIFEST_LOAD_ERROR,l=!0;break;case J.LEVEL:c=s?R.LEVEL_LOAD_TIMEOUT:R.LEVEL_LOAD_ERROR,l=!1;break;case J.AUDIO_TRACK:c=s?R.AUDIO_TRACK_LOAD_TIMEOUT:R.AUDIO_TRACK_LOAD_ERROR,l=!1;break;case J.SUBTITLE_TRACK:c=s?R.SUBTITLE_TRACK_LOAD_TIMEOUT:R.SUBTITLE_LOAD_ERROR,l=!1;break}h&&this.resetInternalLoader(e.type);const d={type:Y.NETWORK_ERROR,details:c,fatal:l,url:e.url,loader:h,context:e,error:o,networkDetails:t,stats:r};if(i){const u=(t==null?void 0:t.url)||e.url;d.response=ie({url:u,data:void 0},i)}this.hls.trigger(m.ERROR,d)}handlePlaylistLoaded(e,t,s,i,r,n){const o=this.hls,{type:c,level:l,levelOrTrack:h,id:d,groupId:u,deliveryDirectives:f}=i,g=ii(t,i),y=En(i);let p=typeof i.level=="number"&&y===U.MAIN?l:void 0;const E=e.playlistParsingError;if(E){if(this.hls.logger.warn(`${E} ${e.url}`),!o.config.ignorePlaylistParsingErrors){o.trigger(m.ERROR,{type:Y.NETWORK_ERROR,details:R.LEVEL_PARSING_ERROR,fatal:!1,url:g,error:E,reason:E.message,response:t,context:i,level:p,parent:y,networkDetails:r,stats:s});return}e.playlistParsingError=null}if(!e.fragments.length){const T=e.playlistParsingError=new Error("No Segments found in Playlist");o.trigger(m.ERROR,{type:Y.NETWORK_ERROR,details:R.LEVEL_EMPTY_ERROR,fatal:!1,url:g,error:T,reason:T.message,response:t,context:i,level:p,parent:y,networkDetails:r,stats:s});return}switch(e.live&&n&&(n.getCacheAge&&(e.ageHeader=n.getCacheAge()||0),(!n.getCacheAge||isNaN(e.ageHeader))&&(e.ageHeader=0)),c){case J.MANIFEST:case J.LEVEL:if(p){if(!h)p=0;else if(h!==o.levels[p]){const T=o.levels.indexOf(h);T>-1&&(p=T)}}o.trigger(m.LEVEL_LOADED,{details:e,levelInfo:h||o.levels[0],level:p||0,id:d||0,stats:s,networkDetails:r,deliveryDirectives:f,withoutMultiVariant:c===J.MANIFEST});break;case J.AUDIO_TRACK:o.trigger(m.AUDIO_TRACK_LOADED,{details:e,track:h,id:d||0,groupId:u||"",stats:s,networkDetails:r,deliveryDirectives:f});break;case J.SUBTITLE_TRACK:o.trigger(m.SUBTITLE_TRACK_LOADED,{details:e,track:h,id:d||0,groupId:u||"",stats:s,networkDetails:r,deliveryDirectives:f});break}}}class Ye{static get version(){return Vt}static isMSESupported(){return Za()}static isSupported(){return su()}static getMediaSource(){return at()}static get Events(){return m}static get MetadataSchema(){return _e}static get ErrorTypes(){return Y}static get ErrorDetails(){return R}static get DefaultConfig(){return Ye.defaultConfig?Ye.defaultConfig:Hd}static set DefaultConfig(e){Ye.defaultConfig=e}constructor(e={}){this.config=void 0,this.userConfig=void 0,this.logger=void 0,this.coreComponents=void 0,this.networkControllers=void 0,this._emitter=new Mi,this._autoLevelCapping=-1,this._maxHdcpLevel=null,this.abrController=void 0,this.bufferController=void 0,this.capLevelController=void 0,this.latencyController=void 0,this.levelController=void 0,this.streamController=void 0,this.audioStreamController=void 0,this.subtititleStreamController=void 0,this.audioTrackController=void 0,this.subtitleTrackController=void 0,this.interstitialsController=void 0,this.gapController=void 0,this.emeController=void 0,this.cmcdController=void 0,this._media=null,this._url=null,this._sessionId=void 0,this.triggeringException=void 0,this.started=!1;const t=this.logger=fo(e.debug||!1,"Hls instance",e.assetPlayerId),s=this.config=Wd(Ye.DefaultConfig,e,t);this.userConfig=e,s.progressive&&qd(s,t);const{abrController:i,bufferController:r,capLevelController:n,errorController:o,fpsController:c}=s,l=new o(this),h=this.abrController=new i(this),d=new ll(this),u=s.interstitialsController,f=u?this.interstitialsController=new u(this,Ye):null,g=this.bufferController=new r(this,d),y=this.capLevelController=new n(this),p=new c(this),E=new ou(this),T=s.contentSteeringController,S=T?new T(this):null,v=this.levelController=new tu(this,S),x=new Jd(this),D=new au(this.config,this.logger),A=this.streamController=new nu(this,d,D),_=this.gapController=new Qd(this,d);y.setStreamController(A),p.setStreamController(A);const b=[E,v,A];f&&b.splice(1,0,f),S&&b.splice(1,0,S),this.networkControllers=b;const I=[h,g,_,y,p,x,d];this.audioTrackController=this.createController(s.audioTrackController,b);const P=s.audioStreamController;P&&b.push(this.audioStreamController=new P(this,d,D)),this.subtitleTrackController=this.createController(s.subtitleTrackController,b);const M=s.subtitleStreamController;M&&b.push(this.subtititleStreamController=new M(this,d,D)),this.createController(s.timelineController,I),D.emeController=this.emeController=this.createController(s.emeController,I),this.cmcdController=this.createController(s.cmcdController,I),this.latencyController=this.createController(eu,I),this.coreComponents=I,b.push(l);const G=l.onErrorOut;typeof G=="function"&&this.on(m.ERROR,G,l),this.on(m.MANIFEST_LOADED,E.onManifestLoaded,E)}createController(e,t){if(e){const s=new e(this);return t&&t.push(s),s}return null}on(e,t,s=this){this._emitter.on(e,t,s)}once(e,t,s=this){this._emitter.once(e,t,s)}removeAllListeners(e){this._emitter.removeAllListeners(e)}off(e,t,s=this,i){this._emitter.off(e,t,s,i)}listeners(e){return this._emitter.listeners(e)}emit(e,t,s){return this._emitter.emit(e,t,s)}trigger(e,t){if(this.config.debug)return this.emit(e,e,t);try{return this.emit(e,e,t)}catch(s){if(this.logger.error("An internal error happened while handling event "+e+'. Error message: "'+s.message+'". Here is a stacktrace:',s),!this.triggeringException){this.triggeringException=!0;const i=e===m.ERROR;this.trigger(m.ERROR,{type:Y.OTHER_ERROR,details:R.INTERNAL_EXCEPTION,fatal:i,event:e,error:s}),this.triggeringException=!1}}return!1}listenerCount(e){return this._emitter.listenerCount(e)}destroy(){this.logger.log("destroy"),this.trigger(m.DESTROYING,void 0),this.detachMedia(),this.removeAllListeners(),this._autoLevelCapping=-1,this._url=null,this.networkControllers.forEach(t=>t.destroy()),this.networkControllers.length=0,this.coreComponents.forEach(t=>t.destroy()),this.coreComponents.length=0;const e=this.config;e.xhrSetup=e.fetchSetup=void 0,this.userConfig=null}attachMedia(e){if(!e||"media"in e&&!e.media){const r=new Error(`attachMedia failed: invalid argument (${e})`);this.trigger(m.ERROR,{type:Y.OTHER_ERROR,details:R.ATTACH_MEDIA_ERROR,fatal:!0,error:r});return}this.logger.log("attachMedia"),this._media&&(this.logger.warn("media must be detached before attaching"),this.detachMedia());const t="media"in e,s=t?e.media:e,i=t?e:{media:s};this._media=s,this.trigger(m.MEDIA_ATTACHING,i)}detachMedia(){this.logger.log("detachMedia"),this.trigger(m.MEDIA_DETACHING,{}),this._media=null}transferMedia(){this._media=null;const e=this.bufferController.transferMedia();return this.trigger(m.MEDIA_DETACHING,{transferMedia:e}),e}loadSource(e){this.stopLoad();const t=this.media,s=this._url,i=this._url=Li.buildAbsoluteURL(self.location.href,e,{alwaysNormalize:!0});this._autoLevelCapping=-1,this._maxHdcpLevel=null,this.logger.log(`loadSource:${i}`),t&&s&&(s!==i||this.bufferController.hasSourceTypes())&&(this.detachMedia(),this.attachMedia(t)),this.trigger(m.MANIFEST_LOADING,{url:e})}get url(){return this._url}get hasEnoughToStart(){return this.streamController.hasEnoughToStart}get startPosition(){return this.streamController.startPositionValue}startLoad(e=-1,t){this.logger.log(`startLoad(${e+(t?", ":"")})`),this.started=!0,this.resumeBuffering();for(let s=0;s{e.resumeBuffering&&e.resumeBuffering()}))}pauseBuffering(){this.bufferingEnabled&&(this.logger.log("pause buffering"),this.networkControllers.forEach(e=>{e.pauseBuffering&&e.pauseBuffering()}))}get inFlightFragments(){const e={[U.MAIN]:this.streamController.inFlightFrag};return this.audioStreamController&&(e[U.AUDIO]=this.audioStreamController.inFlightFrag),this.subtititleStreamController&&(e[U.SUBTITLE]=this.subtititleStreamController.inFlightFrag),e}swapAudioCodec(){this.logger.log("swapAudioCodec"),this.streamController.swapAudioCodec()}recoverMediaError(){this.logger.log("recoverMediaError");const e=this._media,t=e==null?void 0:e.currentTime;this.detachMedia(),e&&(this.attachMedia(e),t&&this.startLoad(t))}removeLevel(e){this.levelController.removeLevel(e)}get sessionId(){let e=this._sessionId;return e||(e=this._sessionId=Qh()),e}get levels(){const e=this.levelController.levels;return e||[]}get latestLevelDetails(){return this.streamController.getLevelDetails()||null}get loadLevelObj(){return this.levelController.loadLevelObj}get currentLevel(){return this.streamController.currentLevel}set currentLevel(e){this.logger.log(`set currentLevel:${e}`),this.levelController.manualLevel=e,this.streamController.immediateLevelSwitch()}get nextLevel(){return this.streamController.nextLevel}set nextLevel(e){this.logger.log(`set nextLevel:${e}`),this.levelController.manualLevel=e,this.streamController.nextLevelSwitch()}get loadLevel(){return this.levelController.level}set loadLevel(e){this.logger.log(`set loadLevel:${e}`),this.levelController.manualLevel=e}get nextLoadLevel(){return this.levelController.nextLoadLevel}set nextLoadLevel(e){this.levelController.nextLoadLevel=e}get firstLevel(){return Math.max(this.levelController.firstLevel,this.minAutoLevel)}set firstLevel(e){this.logger.log(`set firstLevel:${e}`),this.levelController.firstLevel=e}get startLevel(){const e=this.levelController.startLevel;return e===-1&&this.abrController.forcedAutoLevel>-1?this.abrController.forcedAutoLevel:e}set startLevel(e){this.logger.log(`set startLevel:${e}`),e!==-1&&(e=Math.max(e,this.minAutoLevel)),this.levelController.startLevel=e}get capLevelToPlayerSize(){return this.config.capLevelToPlayerSize}set capLevelToPlayerSize(e){const t=!!e;t!==this.config.capLevelToPlayerSize&&(t?this.capLevelController.startCapping():(this.capLevelController.stopCapping(),this.autoLevelCapping=-1,this.streamController.nextLevelSwitch()),this.config.capLevelToPlayerSize=t)}get autoLevelCapping(){return this._autoLevelCapping}get bandwidthEstimate(){const{bwEstimator:e}=this.abrController;return e?e.getEstimate():NaN}set bandwidthEstimate(e){this.abrController.resetEstimator(e)}get abrEwmaDefaultEstimate(){const{bwEstimator:e}=this.abrController;return e?e.defaultEstimate:NaN}get ttfbEstimate(){const{bwEstimator:e}=this.abrController;return e?e.getEstimateTTFB():NaN}set autoLevelCapping(e){this._autoLevelCapping!==e&&(this.logger.log(`set autoLevelCapping:${e}`),this._autoLevelCapping=e,this.levelController.checkMaxAutoUpdated())}get maxHdcpLevel(){return this._maxHdcpLevel}set maxHdcpLevel(e){Wo(e)&&this._maxHdcpLevel!==e&&(this._maxHdcpLevel=e,this.levelController.checkMaxAutoUpdated())}get autoLevelEnabled(){return this.levelController.manualLevel===-1}get manualLevel(){return this.levelController.manualLevel}get minAutoLevel(){const{levels:e,config:{minAutoBitrate:t}}=this;if(!e)return 0;const s=e.length;for(let i=0;i=t)return i;return 0}get maxAutoLevel(){const{levels:e,autoLevelCapping:t,maxHdcpLevel:s}=this;let i;if(t===-1&&e!=null&&e.length?i=e.length-1:i=t,s)for(let r=i;r--;){const n=e[r].attrs["HDCP-LEVEL"];if(n&&n<=s)return r}return i}get firstAutoLevel(){return this.abrController.firstAutoLevel}get nextAutoLevel(){return this.abrController.nextAutoLevel}set nextAutoLevel(e){this.abrController.nextAutoLevel=e}get playingDate(){return this.streamController.currentProgramDateTime}get mainForwardBufferInfo(){return this.streamController.getMainFwdBufferInfo()}get maxBufferLength(){return this.streamController.maxBufferLength}setAudioOption(e){var t;return((t=this.audioTrackController)==null?void 0:t.setAudioOption(e))||null}setSubtitleOption(e){var t;return((t=this.subtitleTrackController)==null?void 0:t.setSubtitleOption(e))||null}get allAudioTracks(){const e=this.audioTrackController;return e?e.allAudioTracks:[]}get audioTracks(){const e=this.audioTrackController;return e?e.audioTracks:[]}get audioTrack(){const e=this.audioTrackController;return e?e.audioTrack:-1}set audioTrack(e){const t=this.audioTrackController;t&&(t.audioTrack=e)}get allSubtitleTracks(){const e=this.subtitleTrackController;return e?e.allSubtitleTracks:[]}get subtitleTracks(){const e=this.subtitleTrackController;return e?e.subtitleTracks:[]}get subtitleTrack(){const e=this.subtitleTrackController;return e?e.subtitleTrack:-1}get media(){return this._media}set subtitleTrack(e){const t=this.subtitleTrackController;t&&(t.subtitleTrack=e)}get subtitleDisplay(){const e=this.subtitleTrackController;return e?e.subtitleDisplay:!1}set subtitleDisplay(e){const t=this.subtitleTrackController;t&&(t.subtitleDisplay=e)}get lowLatencyMode(){return this.config.lowLatencyMode}set lowLatencyMode(e){this.config.lowLatencyMode=e}get liveSyncPosition(){return this.latencyController.liveSyncPosition}get latency(){return this.latencyController.latency}get maxLatency(){return this.latencyController.maxLatency}get targetLatency(){return this.latencyController.targetLatency}set targetLatency(e){this.latencyController.targetLatency=e}get drift(){return this.latencyController.drift}get forceStartLoad(){return this.streamController.forceStartLoad}get pathways(){return this.levelController.pathways}get pathwayPriority(){return this.levelController.pathwayPriority}set pathwayPriority(e){this.levelController.pathwayPriority=e}get bufferedToEnd(){var e;return!!((e=this.bufferController)!=null&&e.bufferedToEnd)}get interstitialsManager(){var e;return((e=this.interstitialsController)==null?void 0:e.interstitialsManager)||null}getMediaDecodingInfo(e,t=this.allAudioTracks){const s=Bn(t);return Mn(e,s,navigator.mediaCapabilities)}}Ye.defaultConfig=void 0;class lu{constructor({onState:e}){this.audio=new Audio,this.audio.preload="none",this.hls=null,this.station=null,this.onState=e||(()=>{}),this.audio.addEventListener("playing",()=>this.emit({playing:!0,loading:!1,error:null})),this.audio.addEventListener("pause",()=>this.emit({playing:!1,loading:!1})),this.audio.addEventListener("waiting",()=>this.emit({loading:!0})),this.audio.addEventListener("error",()=>{var r;const t=(r=this.audio.error)==null?void 0:r.code,i={1:"aborted",2:"network",3:"decode",4:"src not supported"}[t]||`code ${t}`;console.warn("[player] audio error",i,this.audio.currentSrc),this.emit({playing:!1,loading:!1,error:`stream error: ${i}`})})}emit(e){var t,s,i;this.onState({stationId:((t=this.station)==null?void 0:t.id)??null,stationName:((s=this.station)==null?void 0:s.name)??null,genres:((i=this.station)==null?void 0:i.genres)||[],volume:this.audio.volume,...e})}setVolume(e){this.audio.volume=Math.max(0,Math.min(1,e)),this.emit({})}stop(){this.audio.pause(),this.audio.removeAttribute("src"),this.audio.load(),this.hls&&(this.hls.destroy(),this.hls=null)}togglePause(){this.station&&(this.audio.paused?this.audio.play().catch(()=>{}):this.audio.pause())}async play(e){this.stop(),this.station=e,this.emit({playing:!1,loading:!0});let t;try{t=(await Te.post(`/api/stations/${e.id}/resolve`)).resolved}catch(i){this.emit({playing:!1,loading:!1,error:i.message});return}const s=t.url;t.format==="hls"?Ye.isSupported()?(this.hls=new Ye({enableWorker:!0}),this.hls.loadSource(s),this.hls.attachMedia(this.audio),this.hls.on(Ye.Events.MANIFEST_PARSED,()=>this.audio.play().catch(()=>{}))):this.audio.canPlayType("application/vnd.apple.mpegurl")?(this.audio.src=s,this.audio.play().catch(()=>{})):this.emit({playing:!1,loading:!1,error:"HLS not supported"}):(this.audio.src=s,this.audio.play().catch(()=>{}))}}const Rt=document.getElementById("app"),N={user:null,tab:"favorites",stations:[],categories:[],selectedCategory:null,favorites:[],history:[],query:"",sort:"hot",randomMode:localStorage.getItem("oradio.randomMode")==="favorites"?"favorites":"all",player:{stationId:null,stationName:null,genres:[],playing:!1,loading:!1,volume:.7,votes:null}},mt=new lu({onState:a=>{N.player={...N.player,...a},Ce()}});async function Ja(){try{N.user=await Te.get("/api/auth/me")}catch{du();return}await Xi(),io(hu),Ce(),vu()}async function Xi(){const[a,e,t,s]=await Promise.all([Te.get(`/api/stations?sort=${encodeURIComponent(N.sort)}`),Te.get("/api/me/favorites").catch(()=>[]),Te.get("/api/me/history").catch(()=>[]),Te.get("/api/v1/categories").catch(()=>[])]);N.stations=a,N.favorites=e,N.history=t,N.categories=s}async function cu(){N.stations=await Te.get(`/api/stations?sort=${encodeURIComponent(N.sort)}`)}function hu(a){if(a.type==="command")if(a.action==="play"&&a.stationId){const e=N.stations.find(t=>t.id===a.stationId);e&&qt(e)}else a.action==="pause"?mt.togglePause():a.action==="volume"?mt.setVolume(a.value):a.action==="stop"&&mt.stop()}function du(){Ii(Rt);const a=k("div",{class:"login"},k("form",{onSubmit:async e=>{e.preventDefault();const t=new FormData(e.target);try{N.user=await Te.post("/api/auth/login",{username:t.get("username"),password:t.get("password")}),await Ja()}catch(s){e.target.querySelector(".err").textContent=s.message}}},k("h1",{},"Sign in"),k("input",{name:"username",placeholder:"Username",autocomplete:"username",required:!0}),k("input",{name:"password",type:"password",placeholder:"Password",autocomplete:"current-password",required:!0}),k("div",{class:"err"}),k("button",{type:"submit"},"Continue")));Rt.appendChild(a)}let Ve=0;function Ce(){if(!N.user)return;const a=Rt.querySelector(".grid");a&&a.scrollTop>0&&(Ve=a.scrollTop),jt(),Ii(Rt);const e=N.player,t=new Set(N.favorites.map(l=>l.id)),s=e.votes,i=k("section",{class:"now"},k("div",{class:"meta"},k("div",{class:"name"},e.stationName||"Select a station"),k("div",{class:"sub"},e.loading?"Connecting…":e.playing?"On air":e.error?e.error:e.stationId?"Paused":"Idle"),k("div",{class:"tags"},...(e.genres||[]).slice(0,4).map(l=>k("span",{class:"tag"},l)))),k("div",{class:"controls"},k("div",{class:"vote-group",title:"Vote on current station"},k("button",{class:`vote up ${(s==null?void 0:s.myVote)===1?"on":""}`,disabled:!e.stationId,title:"Upvote",onClick:()=>Tn(1)},k("span",{class:"vote-icon"},"β–²"),k("span",{class:"vote-count"},String((s==null?void 0:s.up)??0))),k("button",{class:`vote down ${(s==null?void 0:s.myVote)===-1?"on":""}`,disabled:!e.stationId,title:"Downvote",onClick:()=>Tn(-1)},k("span",{class:"vote-icon"},"β–Ό"),k("span",{class:"vote-count"},String((s==null?void 0:s.down)??0)))),k("button",{class:`btn-play ${e.loading?"loading":""}`,title:e.playing?"Pause":"Play",onClick:()=>e.stationId?mt.togglePause():N.favorites[0]&&qt(N.favorites[0])},e.playing?"❚❚":"β–Ά"),k("button",{class:"btn-stop",title:"Stop",disabled:!e.stationId,onClick:()=>mt.stop()},"β– "),k("div",{class:"vol"},k("span",{class:"vol-icon"},e.volume===0?"πŸ”‡":e.volume<.5?"πŸ”ˆ":"πŸ”Š"),k("input",{type:"range",min:0,max:1,step:.05,value:e.volume,"aria-label":"Volume",onInput:l=>mt.setVolume(Number(l.target.value))}),k("span",{class:"val"},Math.round(e.volume*100))))),r=N.user.role==="admin",n=k("div",{class:"header"},k("div",{class:"tabs"},...["favorites","browse","recent"].map(l=>k("button",{class:`tab ${N.tab===l?"active":""}`,onClick:()=>{N.tab=l,Ve=0,Ce()}},l==="favorites"?"β˜… Favorites":l==="browse"?"🌐 Browse":"⏱ Recent"))),k("div",{class:"header-tools"},N.tab==="browse"?k("select",{class:"sort",title:"Sort browse list",onChange:l=>{N.sort=l.target.value,Ve=0,cu().then(Ce)}},k("option",{value:"hot",selected:N.sort==="hot"},"πŸ”₯ Hot (smart)"),k("option",{value:"top",selected:N.sort==="top"},"β–² Top voted"),k("option",{value:"plays",selected:N.sort==="plays"},"β–Ά Most played"),k("option",{value:"controversial",selected:N.sort==="controversial"},"⚑ Controversial"),k("option",{value:"name",selected:N.sort==="name"},"A β†’ Z")):null,k("input",{class:"search",type:"search",placeholder:"Search…",value:N.query,onInput:l=>{N.query=l.target.value,gu()}}),k("button",{class:"btn-random",title:`Play random station (mode: ${N.randomMode}). Right-click to switch mode.`,onClick:yu,onContextMenu:l=>{l.preventDefault(),pu()}},k("span",{class:"rand-icon"},"🎲"),k("span",{class:"rand-mode"},N.randomMode==="favorites"?"β˜…":"All")),k("a",{class:"btn-docs",href:"/docs/",target:"_blank",rel:"noopener",title:"Open API reference"},"API"),r?k("button",{class:"btn-add",title:"Add station",onClick:Su},"+"):null)),o=k("section",{class:"lib"},n);N.tab==="browse"&&N.categories.length&&o.appendChild(uu());const c=k("div",{class:"grid"});c.id="grid",c.addEventListener("scroll",()=>{Ve=c.scrollTop},{passive:!0}),o.appendChild(c),Rt.appendChild(i),Rt.appendChild(o),eo(c,t),Ve&&(c.scrollTop=Ve,requestAnimationFrame(()=>{Ve&&(c.scrollTop=Ve)}))}function uu(){return k("div",{class:"chips"},k("button",{class:`chip ${N.selectedCategory?"":"active"}`,onClick:()=>{N.selectedCategory=null,Ve=0,Ce()}},`All (${N.stations.length})`),...N.categories.filter(a=>a.count>0).map(a=>k("button",{class:`chip ${N.selectedCategory===a.id?"active":""}`,onClick:()=>{N.selectedCategory=a.id,Ve=0,Ce()}},`${a.icon||""} ${a.label} (${a.count})`.trim())))}function fu(){let a=[];if(N.tab==="favorites")a=N.favorites;else if(N.tab==="browse")a=N.stations,N.selectedCategory&&(a=a.filter(t=>t.category===N.selectedCategory));else if(N.tab==="recent"){const t=new Set;a=N.history.filter(s=>!t.has(s.station_id)&&t.add(s.station_id)).map(s=>N.stations.find(i=>i.id===s.station_id)).filter(Boolean)}const e=N.query.trim().toLowerCase();return e&&(a=a.filter(t=>t.name.toLowerCase().includes(e)||(t.country||"").toLowerCase().includes(e)||(t.genres||[]).some(s=>s.toLowerCase().includes(e)))),a}function gu(){const a=document.getElementById("grid");if(!a)return;const e=new Set(N.favorites.map(t=>t.id));eo(a,e)}function eo(a,e){Ii(a);const t=fu();if(!t.length){a.appendChild(k("div",{class:"empty"},N.tab==="favorites"?"No favorites yet β€” long-press or tap β˜… on a station.":N.query?"No matches.":"Nothing here yet."));return}const s=N.player;for(const i of t){const r=typeof i.score=="number"?i.score:0,n=(i.up??0)-(i.down??0),o=n>0?"pos":n<0?"neg":"neu",c=k("div",{class:`card ${s.stationId===i.id?"playing":""}`,role:"button",tabindex:0,onClick:()=>qt(i),onContextMenu:l=>{l.preventDefault(),Sn(l.clientX,l.clientY,i)}},k("div",{class:"art"},i.image_url?k("img",{class:"art-img",src:i.image_url,alt:"",loading:"lazy",referrerpolicy:"no-referrer",onError:l=>{const h=l.target.parentNode;l.target.remove(),h&&h.appendChild(k("span",{class:"art-glyph"},"β™ͺ"))}}):k("span",{class:"art-glyph"},"β™ͺ")),k("div",{class:"card-body"},k("div",{class:"n"},i.name),k("div",{class:"g"},(i.genres||[]).slice(0,3).join(" Β· ")||i.country||"β€”")),k("div",{class:`score-badge ${o}`,title:`β–²${i.up??0} Β· β–Ό${i.down??0} Β· β–Ά${i.plays??0} Β· score ${r.toFixed(2)}`},n>0?`+${n}`:String(n)),k("button",{class:`fav ${e.has(i.id)?"on":""}`,title:e.has(i.id)?"Remove favorite":"Add favorite",onClick:l=>{l.stopPropagation(),mu(i)}},e.has(i.id)?"β˜…":"β˜†"),k("button",{class:"more",title:"API endpoints",onClick:l=>{l.stopPropagation();const h=l.currentTarget.getBoundingClientRect();Sn(h.right,h.bottom,i)}},"β‹―"));a.appendChild(c)}}async function mu(a){N.favorites.some(t=>t.id===a.id)?await Te.del(`/api/me/favorites/${a.id}`):await Te.put(`/api/me/favorites/${a.id}`,{position:N.favorites.length}),N.favorites=await Te.get("/api/me/favorites"),Ce()}function pu(){N.randomMode=N.randomMode==="favorites"?"all":"favorites",localStorage.setItem("oradio.randomMode",N.randomMode),et(`Random mode: ${N.randomMode==="favorites"?"favorites only":"all stations"}`),Ce()}async function yu(){try{const a=N.randomMode==="favorites"?"/api/me/favorites/random":"/api/v1/stations/random",e=await Te.get(a);let t=e;if(t.id==null&&(t=N.stations.find(s=>s.uuid===e.uuid)||null),!t){et("Random station not in cache");return}qt(t)}catch(a){const e=N.randomMode==="favorites"?N.favorites:N.stations;if(!e.length){et(a.message||"No stations available");return}qt(e[Math.floor(Math.random()*e.length)])}}function Eu(a){N.history.unshift({station_id:a,started_at:new Date().toISOString()})}async function qt(a){N.player.votes=null,mt.play(a),Eu(a.id);try{const e=await Te.post(`/api/stations/${a.id}/play`);N.player.stationId===a.id&&(N.player.votes=e,Ai(a.id,e),Ce())}catch{try{const t=await Te.get(`/api/stations/${a.id}/votes`);N.player.stationId===a.id&&(N.player.votes=t,Ai(a.id,t),Ce())}catch{}}}async function Tn(a){var i;const e=N.player.stationId;if(!e)return;const s=(((i=N.player.votes)==null?void 0:i.myVote)||0)===a?0:a;try{const r=await Te.post(`/api/stations/${e}/vote`,{value:s});N.player.votes=r,Ai(e,r),Ce()}catch(r){et(r.message||"Vote failed")}}function Ai(a,e){const t=[N.stations,N.favorites];for(const s of t){const i=s.find(r=>r.id===a);i&&(i.up=e.up,i.down=e.down,i.plays=e.plays,i.score=e.score,i.my_vote=e.myVote)}}let Be=null;function jt(){Be&&(Be.remove(),Be=null)}function Tu(a){const e=location.origin,t=`${e}/api/v1`,s=[];return a.id!=null&&s.push({label:"Station (original)",url:`${e}/api/stations/${a.id}`}),a.uuid&&s.push({label:"Station detail",url:`${t}/stations/${a.uuid}`},{label:"Stream redirect",url:`${t}/stations/${a.uuid}/stream`},{label:"MP3 stream",url:`${t}/stations/${a.uuid}/stream?format=mp3`},{label:"AAC stream",url:`${t}/stations/${a.uuid}/stream?format=aac`},{label:"HLS stream",url:`${t}/stations/${a.uuid}/stream?format=hls`}),s.push({label:"All stations",url:`${t}/stations`},{label:"Health",url:`${t}/health`}),s}function Sn(a,e,t){jt();const s=Tu(t);Be=k("div",{class:"ctx-menu",role:"menu"},k("div",{class:"ctx-title"},t.name),k("div",{class:"ctx-sub"},t.uuid?`uuid Β· ${t.uuid}`:t.id!=null?`id Β· ${t.id} (no uuid β€” public v1 hidden)`:"no identifier"),...s.length?s.map(c=>k("div",{class:"ctx-row"},k("div",{class:"ctx-row-text"},k("div",{class:"ctx-label"},c.label),k("div",{class:"ctx-url"},c.url)),k("button",{class:"ctx-btn",title:"Copy",onClick:async l=>{l.stopPropagation();try{await navigator.clipboard.writeText(c.url),et("Copied")}catch{et("Copy failed")}}},"⧉"),k("button",{class:"ctx-btn",title:"Open",onClick:l=>{l.stopPropagation(),window.open(c.url,"_blank","noopener")}},"β†—"))):[k("div",{class:"ctx-empty"},"No public API for this station yet (missing uuid).")],N.user.role==="admin"?k("button",{class:"ctx-danger",onClick:async()=>{if(jt(),!!confirm(`Delete ${t.name}?`))try{await Te.del(`/api/stations/${t.id}`),await Xi(),Ce(),et("Deleted")}catch(c){et(c.message||"Delete failed")}}},"πŸ—‘ Delete"):null),document.body.appendChild(Be);const i=Be.offsetWidth,r=Be.offsetHeight,n=Math.min(a,window.innerWidth-i-8),o=Math.min(e,window.innerHeight-r-8);Be.style.left=`${Math.max(8,n)}px`,Be.style.top=`${Math.max(8,o)}px`}document.addEventListener("click",a=>{Be&&!Be.contains(a.target)&&jt()});document.addEventListener("keydown",a=>{a.key==="Escape"&&jt()});async function Su(){const a=document.createElement("dialog");a.className="add-station";const e={name:"",country:"",genres:"",image_url:"",homepage:"",streamUrl:"",streamFormat:"mp3"},t=k("div",{class:"err"});a.appendChild(k("form",{method:"dialog",onSubmit:async s=>{s.preventDefault(),t.textContent="";const i={name:e.name.trim(),country:e.country.trim()||null,homepage:e.homepage.trim()||null,image_url:e.image_url.trim()||null,genres:e.genres.split(",").map(r=>r.trim()).filter(Boolean),streams:e.streamUrl.trim()?[{url:e.streamUrl.trim(),format:e.streamFormat,priority:0}]:[]};if(!i.name){t.textContent="Name is required.";return}try{await Te.post("/api/stations",i),a.close(),await Xi(),Ce(),et("Station added")}catch(r){t.textContent=r.message||"Failed to add station"}}},k("h2",{},"Add station"),k("label",{},"Name",k("input",{required:!0,autofocus:!0,onInput:s=>e.name=s.target.value})),k("div",{class:"row2"},k("label",{},"Country",k("input",{maxlength:4,placeholder:"NL",onInput:s=>e.country=s.target.value})),k("label",{},"Genres",k("input",{placeholder:"jazz, electronic",onInput:s=>e.genres=s.target.value}))),k("label",{},"Homepage",k("input",{type:"url",placeholder:"https://…",onInput:s=>e.homepage=s.target.value})),k("label",{},"Image URL",k("input",{type:"url",placeholder:"https://…/logo.png",onInput:s=>e.image_url=s.target.value})),k("div",{class:"row2"},k("label",{},"Stream URL",k("input",{type:"url",placeholder:"https://…/stream",onInput:s=>e.streamUrl=s.target.value})),k("label",{},"Format",k("select",{onChange:s=>e.streamFormat=s.target.value},...["mp3","aac","ogg","hls","m3u","pls","unknown"].map(s=>k("option",{value:s,selected:s==="mp3"},s))))),t,k("div",{class:"actions"},k("button",{class:"btn-ghost",type:"button",onClick:()=>a.close()},"Cancel"),k("button",{class:"btn-primary",type:"submit"},"Add")))),document.body.appendChild(a),a.showModal(),a.addEventListener("close",()=>a.remove())}let vn=null;function et(a){const e=document.querySelector(".toast");e&&e.remove();const t=k("div",{class:"toast"},a);document.body.appendChild(t),clearTimeout(vn),vn=setTimeout(()=>t.remove(),2200)}async function vu(){var a;try{await((a=navigator.wakeLock)==null?void 0:a.request("screen"))}catch{}document.addEventListener("visibilitychange",()=>{var e;document.visibilityState==="visible"&&((e=navigator.wakeLock)==null||e.request("screen").catch(()=>{}))})}document.addEventListener("contextmenu",a=>{window.matchMedia("(display-mode: fullscreen)").matches&&a.preventDefault()});Ja(); diff --git a/server/public/assets/kiosk-CL6_kPws.css b/server/public/assets/kiosk-CL6_kPws.css deleted file mode 100644 index 94db46c..0000000 --- a/server/public/assets/kiosk-CL6_kPws.css +++ /dev/null @@ -1 +0,0 @@ -:root{--bg-0: #07080b;--bg-1: #0e1116;--bg-2: #161a22;--bg-3: #1f242e;--line: #262b36;--fg: #e9ecf2;--muted: #8a90a0;--muted-2: #5d6373;--accent: #ff7a3d;--accent-2: #ffb37a;--accent-glow: rgba(255, 122, 61, .35);--good: #4ec9a6;--bad: #ec6a6a;--radius-sm: 10px;--radius: 14px;--radius-lg: 20px;--pad: 16px;--shadow-sm: 0 1px 2px rgba(0,0,0,.4);--shadow: 0 8px 24px rgba(0,0,0,.45);--shadow-lg: 0 18px 40px rgba(0,0,0,.55);font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-feature-settings:"ss01","cv11";color-scheme:dark}*{box-sizing:border-box}html,body{margin:0;padding:0;background:radial-gradient(1200px 600px at 30% -10%,rgba(255,122,61,.08),transparent 60%),radial-gradient(900px 500px at 100% 110%,rgba(78,201,166,.06),transparent 60%),var(--bg-0);color:var(--fg)}body{-webkit-tap-highlight-color:transparent;touch-action:manipulation;-webkit-user-select:none;user-select:none;overflow:hidden}button{font:inherit;color:inherit;background:none;border:0;cursor:pointer;padding:0}input,select,textarea{font:inherit;color:inherit}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--bg-3);border-radius:8px}::-webkit-scrollbar-thumb:hover{background:#2c323e}.kiosk #app{width:1080px;height:660px;margin:0 auto;display:grid;grid-template-rows:92px 1fr;gap:12px;padding:12px}.now{display:grid;grid-template-columns:1fr auto;gap:16px;padding:10px 16px;background:linear-gradient(135deg,#ffffff0a,#ffffff03),var(--bg-1);border:1px solid var(--line);border-radius:var(--radius);align-items:center;box-shadow:var(--shadow-sm);position:relative;overflow:hidden}.now:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background:radial-gradient(400px 120px at 0% 0%,var(--accent-glow),transparent 70%);opacity:.5;pointer-events:none}.now>*{position:relative}@keyframes pulse{0%,to{box-shadow:0 0 0 0 var(--accent-glow)}50%{box-shadow:0 0 0 6px transparent}}.now .meta{min-width:0;display:flex;flex-direction:column;gap:4px}.now .meta .name{font-size:19px;font-weight:700;letter-spacing:-.01em;line-height:1.15;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.now .meta .sub{color:var(--muted);font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;align-items:center;gap:6px}.now .meta .tags{display:flex;gap:5px;flex-wrap:wrap;margin-top:2px}.tag{font-size:11px;font-weight:500;padding:2px 8px;border-radius:999px;background:#ffb37a1a;color:var(--accent-2);border:1px solid rgba(255,179,122,.18)}.now .controls{display:flex;gap:10px;align-items:center}.btn-play,.btn-stop{width:46px;height:46px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:18px;transition:transform 80ms ease,background .12s ease,box-shadow .12s ease}.btn-play{background:var(--accent);color:#1a0a00;font-weight:900;box-shadow:0 6px 20px var(--accent-glow)}.btn-play:hover{background:#ff8a55}.btn-play:active{transform:scale(.94)}.btn-play.loading{opacity:.65}.btn-stop{background:var(--bg-2);color:var(--muted);border:1px solid var(--line)}.btn-stop:not(:disabled):hover{background:var(--bg-3);color:var(--fg)}.btn-stop:disabled{opacity:.35;cursor:default}.vol{width:170px;display:flex;align-items:center;gap:8px;padding:6px 10px;background:var(--bg-2);border:1px solid var(--line);border-radius:999px}.vol .vol-icon{font-size:13px}.vol input[type=range]{flex:1;height:18px;accent-color:var(--accent)}.vol .val{width:28px;text-align:right;color:var(--muted);font-variant-numeric:tabular-nums;font-size:11px}.lib{background:var(--bg-1);border:1px solid var(--line);border-radius:var(--radius);padding:10px 10px 6px;display:flex;flex-direction:column;min-height:0;gap:8px;box-shadow:var(--shadow-sm)}.header{display:flex;align-items:center;gap:8px}.tabs{display:flex;gap:4px;flex:1;min-width:0}.tab{padding:9px 14px;border-radius:10px;background:transparent;color:var(--muted);font-size:13px;font-weight:600;min-height:38px;border:1px solid transparent;transition:background .12s ease,color .12s ease,border-color .12s ease}.tab:hover{color:var(--fg);background:var(--bg-2)}.tab.active{background:linear-gradient(180deg,#ff7a3d2e,#ff7a3d14);color:var(--accent-2);border-color:#ff7a3d4d}.header-tools{display:flex;gap:6px;align-items:center}.search{width:220px;padding:8px 12px;height:36px;background:var(--bg-2);color:var(--fg);border:1px solid var(--line);border-radius:999px;font-size:13px;outline:none;transition:border-color .12s ease,box-shadow .12s ease}.search::placeholder{color:var(--muted-2)}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)}.btn-add{width:36px;height:36px;border-radius:50%;background:var(--accent);color:#1a0a00;font-size:22px;font-weight:800;line-height:1;box-shadow:0 4px 12px var(--accent-glow);transition:transform 80ms ease,background .12s ease}.btn-add:hover{background:#ff8a55}.btn-add:active{transform:scale(.94)}.chips{display:flex;flex-wrap:wrap;gap:5px;max-height:64px;overflow-y:auto;padding:2px}.chip{padding:4px 10px;border-radius:999px;background:var(--bg-2);color:var(--muted);border:1px solid var(--line);font-size:11px;font-weight:600;min-height:26px;transition:background .12s,color .12s,border-color .12s}.chip:hover{color:var(--fg)}.chip.active{background:#ff7a3d2e;color:var(--accent-2);border-color:#ff7a3d66}.grid{flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:4px;padding:2px 4px 6px 2px}.card{display:grid;grid-template-columns:44px 1fr auto auto;align-items:center;gap:12px;padding:6px 10px 6px 6px;background:var(--bg-2);border:1px solid transparent;border-radius:10px;min-height:56px;text-align:left;cursor:pointer;transition:background .1s ease,border-color .1s ease,transform 80ms ease;position:relative}.card:hover{background:var(--bg-3)}.card:active{transform:scale(.995)}.card.playing{background:linear-gradient(90deg,rgba(255,122,61,.14),var(--bg-2) 60%);border-color:#ff7a3d59}.card.playing:before{content:"";position:absolute;left:0;top:8px;bottom:8px;width:3px;border-radius:0 3px 3px 0;background:var(--accent)}.card .art{width:44px;height:44px;border-radius:8px;background:var(--bg-3) center/cover no-repeat;display:flex;align-items:center;justify-content:center;font-size:16px;color:var(--muted-2);flex-shrink:0;border:1px solid var(--line);overflow:hidden}.card .art .art-img{width:100%;height:100%;object-fit:cover;display:block}.card .card-body{min-width:0}.card .n{font-weight:600;font-size:14px;line-height:1.2;letter-spacing:-.005em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.card .g{font-size:11.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px}.card .fav,.card .more{width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:16px;color:var(--muted);transition:background .1s,color .1s}.card .fav:hover,.card .more:hover{background:#ffffff0f;color:var(--fg)}.card .fav.on{color:var(--accent)}.card .more{font-weight:700;letter-spacing:1px}.empty{color:var(--muted);padding:32px 16px;text-align:center;font-size:13px}.login{position:fixed;top:0;right:0;bottom:0;left:0;background:radial-gradient(800px 500px at 50% 0%,rgba(255,122,61,.1),transparent 60%),#07080bf7;display:flex;align-items:center;justify-content:center;z-index:50}.login form{background:var(--bg-1);border:1px solid var(--line);padding:32px;border-radius:var(--radius-lg);display:flex;flex-direction:column;gap:14px;width:380px;box-shadow:var(--shadow-lg)}.login h1{margin:0 0 8px;font-size:24px;letter-spacing:-.01em}.login input{background:var(--bg-2);border:1px solid var(--line);color:var(--fg);padding:13px 14px;border-radius:var(--radius-sm);font-size:15px;outline:none;transition:border-color .12s,box-shadow .12s}.login input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)}.login button{background:var(--accent);color:#1a0a00;font-weight:700;padding:13px;border-radius:var(--radius-sm);font-size:15px;box-shadow:0 6px 18px var(--accent-glow);transition:background .12s}.login button:hover{background:#ff8a55}.login .err{color:var(--bad);font-size:13px;min-height:18px}dialog.add-station{border:1px solid var(--line);background:var(--bg-1);color:var(--fg);border-radius:var(--radius-lg);padding:0;max-width:520px;width:90%;box-shadow:var(--shadow-lg)}dialog.add-station::backdrop{background:#07080ba6;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}dialog.add-station form{padding:24px;display:flex;flex-direction:column;gap:12px}dialog.add-station h2{margin:0 0 4px;font-size:18px;letter-spacing:-.01em}dialog.add-station label{display:flex;flex-direction:column;gap:4px;font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.06em}dialog.add-station input,dialog.add-station select{background:var(--bg-2);border:1px solid var(--line);color:var(--fg);padding:9px 11px;border-radius:var(--radius-sm);font-size:14px;outline:none;transition:border-color .12s,box-shadow .12s}dialog.add-station input:focus,dialog.add-station select:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)}dialog.add-station .row2{display:grid;grid-template-columns:1fr 1fr;gap:12px}dialog.add-station .err{color:var(--bad);font-size:12px;min-height:14px}dialog.add-station .actions{display:flex;gap:8px;justify-content:flex-end;margin-top:4px}.btn-primary{background:var(--accent);color:#1a0a00;font-weight:700;padding:9px 16px;border-radius:var(--radius-sm);font-size:14px;transition:background .12s}.btn-primary:hover{background:#ff8a55}.btn-ghost{background:transparent;color:var(--muted);padding:9px 16px;border-radius:var(--radius-sm);font-size:14px;border:1px solid var(--line);transition:color .12s,background .12s}.btn-ghost:hover{color:var(--fg);background:var(--bg-2)}.ctx-menu{position:fixed;z-index:100;min-width:360px;max-width:460px;background:var(--bg-1);color:var(--fg);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow-lg);padding:10px;display:flex;flex-direction:column;gap:2px;animation:ctxIn .1s ease-out}@keyframes ctxIn{0%{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:none}}.ctx-title{font-weight:700;font-size:13px;padding:4px 8px 0;letter-spacing:-.005em}.ctx-sub{font-size:10.5px;color:var(--muted-2);padding:0 8px 8px;border-bottom:1px solid var(--line);font-family:ui-monospace,SF Mono,Menlo,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ctx-row{display:grid;grid-template-columns:1fr auto auto;gap:6px;align-items:center;padding:6px 8px;border-radius:8px}.ctx-row:hover{background:var(--bg-2)}.ctx-row-text{min-width:0}.ctx-label{font-size:12px;color:var(--fg);font-weight:500}.ctx-url{font-size:11px;color:var(--muted);font-family:ui-monospace,SF Mono,Menlo,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ctx-btn{width:28px;height:28px;border-radius:6px;background:var(--bg-2);color:var(--muted);font-size:13px;display:flex;align-items:center;justify-content:center;transition:background .1s,color .1s;border:1px solid var(--line)}.ctx-btn:hover{background:var(--bg-3);color:var(--fg)}.ctx-empty{padding:8px;color:var(--muted);font-size:12px}.ctx-danger{margin-top:4px;padding:7px 10px;border-radius:8px;background:transparent;color:var(--bad);border:1px solid rgba(236,106,106,.25);font-size:12px;font-weight:600;text-align:left}.ctx-danger:hover{background:#ec6a6a1a}.toast{position:fixed;bottom:20px;left:50%;transform:translate(-50%);background:var(--bg-1);padding:10px 18px;border-radius:999px;font-size:13px;color:var(--fg);z-index:200;border:1px solid var(--line);box-shadow:var(--shadow);animation:toastIn .18s ease-out}@keyframes toastIn{0%{opacity:0;transform:translate(-50%,8px)}to{opacity:1;transform:translate(-50%)}}:root{--bg-0: #000000;--bg-1: #0a0a0a;--bg-2: #141414;--bg-3: #1f1f1f;--line: #2e2e2e;--fg: #ffffff;--muted: #a0a0a0;--muted-2: #6a6a6a;--accent: #ff5b00;--accent-2: #ff5b00;--accent-glow: transparent;--good: #00d27a;--bad: #ff3030;--radius-sm: 0;--radius: 0;--radius-lg: 0;--shadow-sm: none;--shadow: none;--shadow-lg: none}html,body{background:var(--bg-0)!important}*,*:before,*:after{border-radius:0!important}button,input,select,textarea,dialog{border-radius:0!important}::-webkit-scrollbar-thumb{background:var(--bg-3)!important}::-webkit-scrollbar-thumb:hover{background:var(--line)!important}.now{background:var(--bg-1)!important;box-shadow:none!important;border-color:var(--line)!important}.now:before{display:none!important}@keyframes pulse{0%,to{border-color:var(--accent)}50%{border-color:var(--line)}}.now .meta .name{text-transform:uppercase;letter-spacing:.01em;font-weight:800}.tag{background:var(--bg-2)!important;color:var(--fg)!important;border-color:var(--line)!important;text-transform:uppercase;letter-spacing:.05em;font-weight:700}.btn-play,.btn-stop,.btn-add{box-shadow:none!important;border:1px solid var(--line);transition:background 80ms linear,color 80ms linear,transform 60ms linear!important}.btn-play,.btn-add{background:var(--accent)!important;color:#000!important;border-color:var(--accent)!important}.btn-play:hover,.btn-add:hover{background:#fff!important;color:#000!important;border-color:#fff!important}.btn-stop{background:var(--bg-2)!important;color:var(--fg)!important}.btn-stop:not(:disabled):hover{background:#fff!important;color:#000!important;border-color:#fff!important}.vol{background:var(--bg-2)!important;border-color:var(--line)!important}.lib{background:var(--bg-1)!important;box-shadow:none!important;border-color:var(--line)!important}.tabs{gap:0!important}.tab{border:1px solid var(--line)!important;margin-right:-1px!important;background:transparent!important;text-transform:uppercase;letter-spacing:.06em;font-weight:700;font-size:12px}.tab:hover{background:var(--bg-2)!important;color:var(--fg)!important}.tab.active{background:var(--accent)!important;color:#000!important;border-color:var(--accent)!important;position:relative;z-index:1}.search{background:var(--bg-2)!important;border-color:var(--line)!important}.search:focus{border-color:var(--accent)!important;box-shadow:none!important}.chip{background:var(--bg-2)!important;border-color:var(--line)!important;text-transform:uppercase;letter-spacing:.04em;font-weight:700}.chip:hover{color:var(--fg)!important;border-color:var(--fg)!important}.chip.active{background:var(--accent)!important;color:#000!important;border-color:var(--accent)!important}.grid{gap:0!important}.card{background:var(--bg-1)!important;border:1px solid var(--line)!important;margin-bottom:-1px;transition:background 60ms linear,border-color 60ms linear!important}.card:hover{background:var(--bg-2)!important;border-color:var(--muted-2)!important;z-index:1}.card:active{transform:none!important}.card.playing{background:var(--bg-2)!important;border-color:var(--accent)!important;z-index:2}.card.playing:before{left:0!important;top:0!important;bottom:0!important;width:4px!important;background:var(--accent)!important}.card .art{box-shadow:none!important}.card .n{font-weight:700}.card .g{text-transform:uppercase;letter-spacing:.03em}.card .fav:hover,.card .more:hover{background:var(--bg-3)!important;color:var(--fg)!important;border:1px solid var(--line)!important}.empty{text-transform:uppercase;letter-spacing:.06em}.login{background:#000!important}.login form{border:1px solid #fff!important;box-shadow:none!important}.login h1{text-transform:uppercase;letter-spacing:.04em;font-weight:900}.login input{background:var(--bg-2)!important;border-color:var(--line)!important}.login input:focus{border-color:var(--accent)!important;box-shadow:none!important}.login button{background:var(--accent)!important;color:#000!important;border:1px solid var(--accent)!important;box-shadow:none!important;text-transform:uppercase;letter-spacing:.08em;font-weight:900}.login button:hover{background:#fff!important;color:#000!important;border-color:#fff!important}dialog.add-station{border:1px solid #fff!important;box-shadow:none!important}dialog.add-station::backdrop{background:#000000d9!important;-webkit-backdrop-filter:none!important;backdrop-filter:none!important}dialog.add-station h2{text-transform:uppercase;letter-spacing:.04em;font-weight:900}dialog.add-station label{letter-spacing:.08em;font-weight:700}dialog.add-station input,dialog.add-station select{background:var(--bg-2)!important;border-color:var(--line)!important}dialog.add-station input:focus,dialog.add-station select:focus{border-color:var(--accent)!important;box-shadow:none!important}.btn-primary{background:var(--accent)!important;color:#000!important;border:1px solid var(--accent)!important;text-transform:uppercase;letter-spacing:.06em;font-weight:900}.btn-primary:hover{background:#fff!important;color:#000!important;border-color:#fff!important}.btn-ghost{background:transparent!important;border:1px solid var(--line)!important;text-transform:uppercase;letter-spacing:.06em;font-weight:700}.btn-ghost:hover{color:var(--fg)!important;background:var(--bg-2)!important;border-color:var(--fg)!important}.ctx-menu{border:1px solid #fff!important;box-shadow:none!important}.ctx-title{text-transform:uppercase;letter-spacing:.06em;font-weight:900;font-size:12px}.ctx-row{border:1px solid transparent}.ctx-row:hover{background:var(--bg-2)!important;border-color:var(--line)}.ctx-btn{background:var(--bg-2)!important;border:1px solid var(--line)!important;color:var(--muted)}.ctx-btn:hover{background:#fff!important;color:#000!important;border-color:#fff!important}.ctx-danger{background:transparent!important;border:1px solid var(--bad)!important;color:var(--bad)!important;text-transform:uppercase;letter-spacing:.06em;font-weight:800}.ctx-danger:hover{background:var(--bad)!important;color:#000!important}.toast{background:#fff!important;color:#000!important;border:1px solid #fff!important;box-shadow:none!important;text-transform:uppercase;letter-spacing:.06em;font-weight:800;font-size:12px} diff --git a/server/public/assets/kiosk-CdZttV5P.css b/server/public/assets/kiosk-CdZttV5P.css new file mode 100644 index 0000000..084e28c --- /dev/null +++ b/server/public/assets/kiosk-CdZttV5P.css @@ -0,0 +1 @@ +:root{--bg-0: #07080b;--bg-1: #0e1116;--bg-2: #161a22;--bg-3: #1f242e;--line: #262b36;--fg: #e9ecf2;--muted: #8a90a0;--muted-2: #5d6373;--accent: #ff7a3d;--accent-2: #ffb37a;--accent-glow: rgba(255, 122, 61, .35);--good: #4ec9a6;--bad: #ec6a6a;--radius-sm: 10px;--radius: 14px;--radius-lg: 20px;--pad: 16px;--shadow-sm: 0 1px 2px rgba(0,0,0,.4);--shadow: 0 8px 24px rgba(0,0,0,.45);--shadow-lg: 0 18px 40px rgba(0,0,0,.55);font-family:Inter,system-ui,-apple-system,Segoe UI,Roboto,sans-serif;font-feature-settings:"ss01","cv11";color-scheme:dark}*{box-sizing:border-box}html,body{margin:0;padding:0;background:radial-gradient(1200px 600px at 30% -10%,rgba(255,122,61,.08),transparent 60%),radial-gradient(900px 500px at 100% 110%,rgba(78,201,166,.06),transparent 60%),var(--bg-0);color:var(--fg)}body{-webkit-tap-highlight-color:transparent;touch-action:manipulation;-webkit-user-select:none;user-select:none;overflow:hidden}button{font:inherit;color:inherit;background:none;border:0;cursor:pointer;padding:0}input,select,textarea{font:inherit;color:inherit}::-webkit-scrollbar{width:8px;height:8px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:var(--bg-3);border-radius:8px}::-webkit-scrollbar-thumb:hover{background:#2c323e}.kiosk #app{width:1080px;height:660px;margin:0 auto;display:grid;grid-template-rows:92px 1fr;gap:12px;padding:12px}.now{display:grid;grid-template-columns:1fr auto;gap:16px;padding:10px 16px;background:linear-gradient(135deg,#ffffff0a,#ffffff03),var(--bg-1);border:1px solid var(--line);border-radius:var(--radius);align-items:center;box-shadow:var(--shadow-sm);position:relative;overflow:hidden}.now:before{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background:radial-gradient(400px 120px at 0% 0%,var(--accent-glow),transparent 70%);opacity:.5;pointer-events:none}.now>*{position:relative}@keyframes pulse{0%,to{box-shadow:0 0 0 0 var(--accent-glow)}50%{box-shadow:0 0 0 6px transparent}}.now .meta{min-width:0;display:flex;flex-direction:column;gap:4px}.now .meta .name{font-size:19px;font-weight:700;letter-spacing:-.01em;line-height:1.15;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.now .meta .sub{color:var(--muted);font-size:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;display:flex;align-items:center;gap:6px}.now .meta .tags{display:flex;gap:5px;flex-wrap:wrap;margin-top:2px}.tag{font-size:11px;font-weight:500;padding:2px 8px;border-radius:999px;background:#ffb37a1a;color:var(--accent-2);border:1px solid rgba(255,179,122,.18)}.now .controls{display:flex;gap:10px;align-items:center}.btn-play,.btn-stop{width:46px;height:46px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:18px;transition:transform 80ms ease,background .12s ease,box-shadow .12s ease}.btn-play{background:var(--accent);color:#1a0a00;font-weight:900;box-shadow:0 6px 20px var(--accent-glow)}.btn-play:hover{background:#ff8a55}.btn-play:active{transform:scale(.94)}.btn-play.loading{opacity:.65}.btn-stop{background:var(--bg-2);color:var(--muted);border:1px solid var(--line)}.btn-stop:not(:disabled):hover{background:var(--bg-3);color:var(--fg)}.btn-stop:disabled{opacity:.35;cursor:default}.vol{width:170px;display:flex;align-items:center;gap:8px;padding:6px 10px;background:var(--bg-2);border:1px solid var(--line);border-radius:999px}.vol .vol-icon{font-size:13px}.vol input[type=range]{flex:1;height:18px;accent-color:var(--accent)}.vol .val{width:28px;text-align:right;color:var(--muted);font-variant-numeric:tabular-nums;font-size:11px}.lib{background:var(--bg-1);border:1px solid var(--line);border-radius:var(--radius);padding:10px 10px 6px;display:flex;flex-direction:column;min-height:0;gap:8px;box-shadow:var(--shadow-sm)}.header{display:flex;align-items:center;gap:8px}.tabs{display:flex;gap:4px;flex:1;min-width:0}.tab{padding:9px 14px;border-radius:10px;background:transparent;color:var(--muted);font-size:13px;font-weight:600;min-height:38px;border:1px solid transparent;transition:background .12s ease,color .12s ease,border-color .12s ease}.tab:hover{color:var(--fg);background:var(--bg-2)}.tab.active{background:linear-gradient(180deg,#ff7a3d2e,#ff7a3d14);color:var(--accent-2);border-color:#ff7a3d4d}.header-tools{display:flex;gap:6px;align-items:center}.search{width:220px;padding:8px 12px;height:36px;background:var(--bg-2);color:var(--fg);border:1px solid var(--line);border-radius:999px;font-size:13px;outline:none;transition:border-color .12s ease,box-shadow .12s ease}.search::placeholder{color:var(--muted-2)}.search:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)}.btn-add{width:36px;height:36px;border-radius:50%;background:var(--accent);color:#1a0a00;font-size:22px;font-weight:800;line-height:1;box-shadow:0 4px 12px var(--accent-glow);transition:transform 80ms ease,background .12s ease}.btn-add:hover{background:#ff8a55}.btn-add:active{transform:scale(.94)}.btn-random{display:flex;align-items:center;gap:6px;height:36px;padding:0 12px;border-radius:999px;background:var(--bg-2);color:var(--fg);border:1px solid var(--line);font-size:13px;font-weight:600;transition:background .12s,border-color .12s,transform 80ms}.btn-random:hover{background:var(--bg-3);border-color:#ff7a3d66}.btn-random:active{transform:scale(.96)}.btn-random .rand-icon{font-size:15px;line-height:1}.btn-random .rand-mode{font-size:11px;padding:2px 6px;border-radius:999px;background:#ff7a3d2e;color:var(--accent-2);border:1px solid rgba(255,122,61,.3);letter-spacing:.02em}.btn-docs{display:flex;align-items:center;justify-content:center;height:36px;padding:0 12px;border-radius:999px;background:var(--bg-2);color:var(--muted);border:1px solid var(--line);font-size:12px;font-weight:700;letter-spacing:.06em;text-decoration:none;transition:background .12s,color .12s,border-color .12s}.btn-docs:hover{background:var(--bg-3);color:var(--fg);border-color:#4ec9a666}.chips{display:flex;flex-wrap:wrap;gap:5px;max-height:64px;overflow-y:auto;padding:2px}.chip{padding:4px 10px;border-radius:999px;background:var(--bg-2);color:var(--muted);border:1px solid var(--line);font-size:11px;font-weight:600;min-height:26px;transition:background .12s,color .12s,border-color .12s}.chip:hover{color:var(--fg)}.chip.active{background:#ff7a3d2e;color:var(--accent-2);border-color:#ff7a3d66}.grid{flex:1;min-height:0;overflow-y:auto;display:flex;flex-direction:column;gap:4px;padding:2px 4px 6px 2px}.card{display:grid;grid-template-columns:44px 1fr auto auto;align-items:center;gap:12px;padding:6px 10px 6px 6px;background:var(--bg-2);border:1px solid transparent;border-radius:10px;min-height:56px;text-align:left;cursor:pointer;transition:background .1s ease,border-color .1s ease,transform 80ms ease;position:relative}.card:hover{background:var(--bg-3)}.card:active{transform:scale(.995)}.card.playing{background:linear-gradient(90deg,rgba(255,122,61,.14),var(--bg-2) 60%);border-color:#ff7a3d59}.card.playing:before{content:"";position:absolute;left:0;top:8px;bottom:8px;width:3px;border-radius:0 3px 3px 0;background:var(--accent)}.card .art{width:44px;height:44px;border-radius:8px;background:var(--bg-3) center/cover no-repeat;display:flex;align-items:center;justify-content:center;font-size:16px;color:var(--muted-2);flex-shrink:0;border:1px solid var(--line);overflow:hidden}.card .art .art-img{width:100%;height:100%;object-fit:cover;display:block}.card .card-body{min-width:0}.card .n{font-weight:600;font-size:14px;line-height:1.2;letter-spacing:-.005em;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.card .g{font-size:11.5px;color:var(--muted);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin-top:2px}.card .fav,.card .more{width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:16px;color:var(--muted);transition:background .1s,color .1s}.card .fav:hover,.card .more:hover{background:#ffffff0f;color:var(--fg)}.card .fav.on{color:var(--accent)}.card .more{font-weight:700;letter-spacing:1px}.empty{color:var(--muted);padding:32px 16px;text-align:center;font-size:13px}.login{position:fixed;top:0;right:0;bottom:0;left:0;background:radial-gradient(800px 500px at 50% 0%,rgba(255,122,61,.1),transparent 60%),#07080bf7;display:flex;align-items:center;justify-content:center;z-index:50}.login form{background:var(--bg-1);border:1px solid var(--line);padding:32px;border-radius:var(--radius-lg);display:flex;flex-direction:column;gap:14px;width:380px;box-shadow:var(--shadow-lg)}.login h1{margin:0 0 8px;font-size:24px;letter-spacing:-.01em}.login input{background:var(--bg-2);border:1px solid var(--line);color:var(--fg);padding:13px 14px;border-radius:var(--radius-sm);font-size:15px;outline:none;transition:border-color .12s,box-shadow .12s}.login input:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)}.login button{background:var(--accent);color:#1a0a00;font-weight:700;padding:13px;border-radius:var(--radius-sm);font-size:15px;box-shadow:0 6px 18px var(--accent-glow);transition:background .12s}.login button:hover{background:#ff8a55}.login .err{color:var(--bad);font-size:13px;min-height:18px}dialog.add-station{border:1px solid var(--line);background:var(--bg-1);color:var(--fg);border-radius:var(--radius-lg);padding:0;max-width:520px;width:90%;box-shadow:var(--shadow-lg)}dialog.add-station::backdrop{background:#07080ba6;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}dialog.add-station form{padding:24px;display:flex;flex-direction:column;gap:12px}dialog.add-station h2{margin:0 0 4px;font-size:18px;letter-spacing:-.01em}dialog.add-station label{display:flex;flex-direction:column;gap:4px;font-size:11px;color:var(--muted);text-transform:uppercase;letter-spacing:.06em}dialog.add-station input,dialog.add-station select{background:var(--bg-2);border:1px solid var(--line);color:var(--fg);padding:9px 11px;border-radius:var(--radius-sm);font-size:14px;outline:none;transition:border-color .12s,box-shadow .12s}dialog.add-station input:focus,dialog.add-station select:focus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-glow)}dialog.add-station .row2{display:grid;grid-template-columns:1fr 1fr;gap:12px}dialog.add-station .err{color:var(--bad);font-size:12px;min-height:14px}dialog.add-station .actions{display:flex;gap:8px;justify-content:flex-end;margin-top:4px}.btn-primary{background:var(--accent);color:#1a0a00;font-weight:700;padding:9px 16px;border-radius:var(--radius-sm);font-size:14px;transition:background .12s}.btn-primary:hover{background:#ff8a55}.btn-ghost{background:transparent;color:var(--muted);padding:9px 16px;border-radius:var(--radius-sm);font-size:14px;border:1px solid var(--line);transition:color .12s,background .12s}.btn-ghost:hover{color:var(--fg);background:var(--bg-2)}.ctx-menu{position:fixed;z-index:100;min-width:360px;max-width:460px;background:var(--bg-1);color:var(--fg);border:1px solid var(--line);border-radius:var(--radius);box-shadow:var(--shadow-lg);padding:10px;display:flex;flex-direction:column;gap:2px;animation:ctxIn .1s ease-out}@keyframes ctxIn{0%{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:none}}.ctx-title{font-weight:700;font-size:13px;padding:4px 8px 0;letter-spacing:-.005em}.ctx-sub{font-size:10.5px;color:var(--muted-2);padding:0 8px 8px;border-bottom:1px solid var(--line);font-family:ui-monospace,SF Mono,Menlo,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ctx-row{display:grid;grid-template-columns:1fr auto auto;gap:6px;align-items:center;padding:6px 8px;border-radius:8px}.ctx-row:hover{background:var(--bg-2)}.ctx-row-text{min-width:0}.ctx-label{font-size:12px;color:var(--fg);font-weight:500}.ctx-url{font-size:11px;color:var(--muted);font-family:ui-monospace,SF Mono,Menlo,monospace;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.ctx-btn{width:28px;height:28px;border-radius:6px;background:var(--bg-2);color:var(--muted);font-size:13px;display:flex;align-items:center;justify-content:center;transition:background .1s,color .1s;border:1px solid var(--line)}.ctx-btn:hover{background:var(--bg-3);color:var(--fg)}.ctx-empty{padding:8px;color:var(--muted);font-size:12px}.ctx-danger{margin-top:4px;padding:7px 10px;border-radius:8px;background:transparent;color:var(--bad);border:1px solid rgba(236,106,106,.25);font-size:12px;font-weight:600;text-align:left}.ctx-danger:hover{background:#ec6a6a1a}.toast{position:fixed;bottom:20px;left:50%;transform:translate(-50%);background:var(--bg-1);padding:10px 18px;border-radius:999px;font-size:13px;color:var(--fg);z-index:200;border:1px solid var(--line);box-shadow:var(--shadow);animation:toastIn .18s ease-out}@keyframes toastIn{0%{opacity:0;transform:translate(-50%,8px)}to{opacity:1;transform:translate(-50%)}}:root{--bg-0: #000000;--bg-1: #0a0a0a;--bg-2: #141414;--bg-3: #1f1f1f;--line: #2e2e2e;--fg: #ffffff;--muted: #a0a0a0;--muted-2: #6a6a6a;--accent: #ff5b00;--accent-2: #ff5b00;--accent-glow: transparent;--good: #00d27a;--bad: #ff3030;--radius-sm: 0;--radius: 0;--radius-lg: 0;--shadow-sm: none;--shadow: none;--shadow-lg: none}html,body{background:var(--bg-0)!important}*,*:before,*:after{border-radius:0!important}button,input,select,textarea,dialog{border-radius:0!important}::-webkit-scrollbar-thumb{background:var(--bg-3)!important}::-webkit-scrollbar-thumb:hover{background:var(--line)!important}.now{background:var(--bg-1)!important;box-shadow:none!important;border-color:var(--line)!important}.now:before{display:none!important}@keyframes pulse{0%,to{border-color:var(--accent)}50%{border-color:var(--line)}}.now .meta .name{text-transform:uppercase;letter-spacing:.01em;font-weight:800}.tag{background:var(--bg-2)!important;color:var(--fg)!important;border-color:var(--line)!important;text-transform:uppercase;letter-spacing:.05em;font-weight:700}.btn-play,.btn-stop,.btn-add{box-shadow:none!important;border:1px solid var(--line);transition:background 80ms linear,color 80ms linear,transform 60ms linear!important}.btn-play,.btn-add{background:var(--accent)!important;color:#000!important;border-color:var(--accent)!important}.btn-play:hover,.btn-add:hover{background:#fff!important;color:#000!important;border-color:#fff!important}.btn-stop{background:var(--bg-2)!important;color:var(--fg)!important}.btn-stop:not(:disabled):hover{background:#fff!important;color:#000!important;border-color:#fff!important}.vol{background:var(--bg-2)!important;border-color:var(--line)!important}.lib{background:var(--bg-1)!important;box-shadow:none!important;border-color:var(--line)!important}.tabs{gap:0!important}.tab{border:1px solid var(--line)!important;margin-right:-1px!important;background:transparent!important;text-transform:uppercase;letter-spacing:.06em;font-weight:700;font-size:12px}.tab:hover{background:var(--bg-2)!important;color:var(--fg)!important}.tab.active{background:var(--accent)!important;color:#000!important;border-color:var(--accent)!important;position:relative;z-index:1}.search{background:var(--bg-2)!important;border-color:var(--line)!important}.search:focus{border-color:var(--accent)!important;box-shadow:none!important}.chip{background:var(--bg-2)!important;border-color:var(--line)!important;text-transform:uppercase;letter-spacing:.04em;font-weight:700}.chip:hover{color:var(--fg)!important;border-color:var(--fg)!important}.chip.active{background:var(--accent)!important;color:#000!important;border-color:var(--accent)!important}.grid{gap:0!important}.card{background:var(--bg-1)!important;border:1px solid var(--line)!important;margin-bottom:-1px;transition:background 60ms linear,border-color 60ms linear!important}.card:hover{background:var(--bg-2)!important;border-color:var(--muted-2)!important;z-index:1}.card:active{transform:none!important}.card.playing{background:var(--bg-2)!important;border-color:var(--accent)!important;z-index:2}.card.playing:before{left:0!important;top:0!important;bottom:0!important;width:4px!important;background:var(--accent)!important}.card .art{box-shadow:none!important}.card .n{font-weight:700}.card .g{text-transform:uppercase;letter-spacing:.03em}.card .fav:hover,.card .more:hover{background:var(--bg-3)!important;color:var(--fg)!important;border:1px solid var(--line)!important}.empty{text-transform:uppercase;letter-spacing:.06em}.login{background:#000!important}.login form{border:1px solid #fff!important;box-shadow:none!important}.login h1{text-transform:uppercase;letter-spacing:.04em;font-weight:900}.login input{background:var(--bg-2)!important;border-color:var(--line)!important}.login input:focus{border-color:var(--accent)!important;box-shadow:none!important}.login button{background:var(--accent)!important;color:#000!important;border:1px solid var(--accent)!important;box-shadow:none!important;text-transform:uppercase;letter-spacing:.08em;font-weight:900}.login button:hover{background:#fff!important;color:#000!important;border-color:#fff!important}dialog.add-station{border:1px solid #fff!important;box-shadow:none!important}dialog.add-station::backdrop{background:#000000d9!important;-webkit-backdrop-filter:none!important;backdrop-filter:none!important}dialog.add-station h2{text-transform:uppercase;letter-spacing:.04em;font-weight:900}dialog.add-station label{letter-spacing:.08em;font-weight:700}dialog.add-station input,dialog.add-station select{background:var(--bg-2)!important;border-color:var(--line)!important}dialog.add-station input:focus,dialog.add-station select:focus{border-color:var(--accent)!important;box-shadow:none!important}.btn-primary{background:var(--accent)!important;color:#000!important;border:1px solid var(--accent)!important;text-transform:uppercase;letter-spacing:.06em;font-weight:900}.btn-primary:hover{background:#fff!important;color:#000!important;border-color:#fff!important}.btn-ghost{background:transparent!important;border:1px solid var(--line)!important;text-transform:uppercase;letter-spacing:.06em;font-weight:700}.btn-ghost:hover{color:var(--fg)!important;background:var(--bg-2)!important;border-color:var(--fg)!important}.ctx-menu{border:1px solid #fff!important;box-shadow:none!important}.ctx-title{text-transform:uppercase;letter-spacing:.06em;font-weight:900;font-size:12px}.ctx-row{border:1px solid transparent}.ctx-row:hover{background:var(--bg-2)!important;border-color:var(--line)}.ctx-btn{background:var(--bg-2)!important;border:1px solid var(--line)!important;color:var(--muted)}.ctx-btn:hover{background:#fff!important;color:#000!important;border-color:#fff!important}.ctx-danger{background:transparent!important;border:1px solid var(--bad)!important;color:var(--bad)!important;text-transform:uppercase;letter-spacing:.06em;font-weight:800}.ctx-danger:hover{background:var(--bad)!important;color:#000!important}.toast{background:#fff!important;color:#000!important;border:1px solid #fff!important;box-shadow:none!important;text-transform:uppercase;letter-spacing:.06em;font-weight:800;font-size:12px}.vote-group{display:flex;gap:0;margin-right:6px}.vote{display:flex;align-items:center;gap:6px;height:46px;min-width:64px;padding:0 12px;background:var(--bg-2)!important;color:var(--fg);border:1px solid var(--line)!important;font-weight:800;font-size:14px;font-variant-numeric:tabular-nums;transition:background 80ms linear,color 80ms linear,border-color 80ms linear!important}.vote+.vote{margin-left:-1px}.vote:disabled{opacity:.35;cursor:not-allowed}.vote .vote-icon{font-size:17px;line-height:1}.vote .vote-count{font-size:13px;letter-spacing:.02em}.vote.up:not(:disabled):hover{background:var(--good)!important;color:#000!important;border-color:var(--good)!important}.vote.down:not(:disabled):hover{background:var(--bad)!important;color:#000!important;border-color:var(--bad)!important}.vote.up.on{background:var(--good)!important;color:#000!important;border-color:var(--good)!important}.vote.down.on{background:var(--bad)!important;color:#000!important;border-color:var(--bad)!important}.sort{height:36px;padding:0 28px 0 12px;background:var(--bg-2)!important;color:var(--fg);border:1px solid var(--line)!important;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;-webkit-appearance:none;-moz-appearance:none;appearance:none;background-image:linear-gradient(45deg,transparent 50%,var(--muted) 50%),linear-gradient(135deg,var(--muted) 50%,transparent 50%);background-position:calc(100% - 14px) 50%,calc(100% - 9px) 50%;background-size:5px 5px,5px 5px;background-repeat:no-repeat;outline:none;cursor:pointer}.sort:focus,.sort:hover{border-color:var(--accent)!important}.sort option{background:var(--bg-1);color:var(--fg)}.card{grid-template-columns:44px 1fr auto auto auto!important}.score-badge{display:inline-flex;align-items:center;justify-content:center;min-width:36px;height:26px;padding:0 8px;background:var(--bg-2);border:1px solid var(--line);color:var(--muted);font-weight:800;font-size:12px;font-variant-numeric:tabular-nums;letter-spacing:.02em}.score-badge.pos{color:var(--good);border-color:#00d27a59}.score-badge.neg{color:var(--bad);border-color:#ff303059}.score-badge.neu{color:var(--muted-2)}.card.playing .score-badge{border-color:var(--accent)} diff --git a/server/public/assets/modulepreload-polyfill-B5Qt9EMX.js b/server/public/assets/modulepreload-polyfill-B5Qt9EMX.js new file mode 100644 index 0000000..3e0a78a --- /dev/null +++ b/server/public/assets/modulepreload-polyfill-B5Qt9EMX.js @@ -0,0 +1 @@ +(function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const e of document.querySelectorAll('link[rel="modulepreload"]'))i(e);new MutationObserver(e=>{for(const r of e)if(r.type==="childList")for(const o of r.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&i(o)}).observe(document,{childList:!0,subtree:!0});function s(e){const r={};return e.integrity&&(r.integrity=e.integrity),e.referrerPolicy&&(r.referrerPolicy=e.referrerPolicy),e.crossOrigin==="use-credentials"?r.credentials="include":e.crossOrigin==="anonymous"?r.credentials="omit":r.credentials="same-origin",r}function i(e){if(e.ep)return;e.ep=!0;const r=s(e);fetch(e.href,r)}})(); diff --git a/server/public/docs/index.html b/server/public/docs/index.html new file mode 100644 index 0000000..9348808 --- /dev/null +++ b/server/public/docs/index.html @@ -0,0 +1,24 @@ + + + + + + + oradio Β· API reference + + + + + + +
+
+ ← Kiosk +

oradio API

+ +
+
+
+ + + diff --git a/server/public/index.html b/server/public/index.html index dd0cee5..694cafa 100644 --- a/server/public/index.html +++ b/server/public/index.html @@ -1,14 +1,18 @@ - + + Radio Kiosk - - - - - + + + + + + +
- - + + + \ No newline at end of file diff --git a/server/routes/admin.js b/server/routes/admin.js index 37619b5..6b20d55 100644 --- a/server/routes/admin.js +++ b/server/routes/admin.js @@ -10,64 +10,64 @@ export const router = Router(); router.use(requireAdmin); router.post('/health-check', async (_req, res) => { - const n = await runHealthCheck(); - res.json({ checked: n }); + const n = await runHealthCheck(); + res.json({ checked: n }); }); router.post('/reseed', (_req, res) => { - res.json(applySeedIfEmpty()); + res.json(applySeedIfEmpty()); }); router.get('/system', (_req, res) => { - const db = getDb(); - res.json({ - stations: db.prepare('SELECT COUNT(*) AS n FROM stations').get().n, - streams: db.prepare('SELECT COUNT(*) AS n FROM streams').get().n, - users: db.prepare('SELECT COUNT(*) AS n FROM users').get().n, - favorites: db.prepare('SELECT COUNT(*) AS n FROM favorites').get().n, - node: process.version, - uptime_s: Math.round(process.uptime()) - }); + const db = getDb(); + res.json({ + stations: db.prepare('SELECT COUNT(*) AS n FROM stations').get().n, + streams: db.prepare('SELECT COUNT(*) AS n FROM streams').get().n, + users: db.prepare('SELECT COUNT(*) AS n FROM users').get().n, + favorites: db.prepare('SELECT COUNT(*) AS n FROM favorites').get().n, + node: process.version, + uptime_s: Math.round(process.uptime()) + }); }); // Scrape an icon for a single station. router.post('/stations/:id/scrape-icon', async (req, res) => { - const id = Number(req.params.id); - const st = getStation(id); - if (!st) return res.status(404).json({ error: 'not found' }); - const url = await scrapeIcon(st); - if (!url) return res.status(404).json({ error: 'no icon found' }); - const updated = updateStation(id, { image_url: url }); - res.json({ id, image_url: url, station: updated }); + const id = Number(req.params.id); + const st = getStation(id); + if (!st) return res.status(404).json({ error: 'not found' }); + const url = await scrapeIcon(st); + if (!url) return res.status(404).json({ error: 'no icon found' }); + const updated = updateStation(id, { image_url: url }); + res.json({ id, image_url: url, station: updated }); }); // Bulk: scrape icons for every station (optionally only those missing one). router.post('/scrape-icons', async (req, res) => { - const onlyMissing = req.query.all !== '1'; - const stations = listStations({ enabled: null }).filter((s) => !onlyMissing || !s.image_url); - const results = { total: stations.length, updated: 0, skipped: 0, failed: 0, items: [] }; - // Limit concurrency to avoid hammering hosts. - const concurrency = 4; - let i = 0; - async function worker() { - while (i < stations.length) { - const s = stations[i++]; - try { - const url = await scrapeIcon(s); - if (url) { - updateStation(s.id, { image_url: url }); - results.updated++; - results.items.push({ id: s.id, name: s.name, image_url: url }); - } else { - results.failed++; - results.items.push({ id: s.id, name: s.name, image_url: null }); + const onlyMissing = req.query.all !== '1'; + const stations = listStations({ enabled: null }).filter((s) => !onlyMissing || !s.image_url); + const results = { total: stations.length, updated: 0, skipped: 0, failed: 0, items: [] }; + // Limit concurrency to avoid hammering hosts. + const concurrency = 4; + let i = 0; + async function worker() { + while (i < stations.length) { + const s = stations[i++]; + try { + const url = await scrapeIcon(s); + if (url) { + updateStation(s.id, { image_url: url }); + results.updated++; + results.items.push({ id: s.id, name: s.name, image_url: url }); + } else { + results.failed++; + results.items.push({ id: s.id, name: s.name, image_url: null }); + } + } catch (err) { + results.failed++; + results.items.push({ id: s.id, name: s.name, error: String(err?.message || err) }); + } } - } catch (err) { - results.failed++; - results.items.push({ id: s.id, name: s.name, error: String(err?.message || err) }); - } } - } - await Promise.all(Array.from({ length: concurrency }, worker)); - res.json(results); + await Promise.all(Array.from({ length: concurrency }, worker)); + res.json(results); }); diff --git a/server/routes/auth.js b/server/routes/auth.js index 176db9d..77dff2b 100644 --- a/server/routes/auth.js +++ b/server/routes/auth.js @@ -1,71 +1,71 @@ import { Router } from 'express'; import { - verifyPassword, createSession, destroySession, setSessionCookie, clearSessionCookie, - hashPassword, requireAdmin + verifyPassword, createSession, destroySession, setSessionCookie, clearSessionCookie, + hashPassword, requireAdmin } from '../auth.js'; import { getDb } from '../db/index.js'; export const router = Router(); router.post('/login', (req, res) => { - const { username, password } = req.body || {}; - if (!username || !password) return res.status(400).json({ error: 'username + password required' }); - const user = getDb().prepare('SELECT * FROM users WHERE username = ?').get(username); - if (!user || !verifyPassword(password, user.password_hash)) { - return res.status(401).json({ error: 'invalid credentials' }); - } - const { token, expires } = createSession(user.id); - setSessionCookie(res, token, expires); - res.json({ id: user.id, username: user.username, role: user.role }); + const { username, password } = req.body || {}; + if (!username || !password) return res.status(400).json({ error: 'username + password required' }); + const user = getDb().prepare('SELECT * FROM users WHERE username = ?').get(username); + if (!user || !verifyPassword(password, user.password_hash)) { + return res.status(401).json({ error: 'invalid credentials' }); + } + const { token, expires } = createSession(user.id); + setSessionCookie(res, token, expires); + res.json({ id: user.id, username: user.username, role: user.role }); }); router.post('/logout', (req, res) => { - destroySession(req.session?.token); - clearSessionCookie(res); - res.json({ ok: true }); + destroySession(req.session?.token); + clearSessionCookie(res); + res.json({ ok: true }); }); router.get('/me', (req, res) => { - if (!req.user) return res.status(401).json({ error: 'not signed in' }); - res.json(req.user); + if (!req.user) return res.status(401).json({ error: 'not signed in' }); + res.json(req.user); }); // Admin-only user management router.get('/users', requireAdmin, (_req, res) => { - const users = getDb().prepare('SELECT id, username, role, created_at FROM users ORDER BY username').all(); - res.json(users); + const users = getDb().prepare('SELECT id, username, role, created_at FROM users ORDER BY username').all(); + res.json(users); }); router.post('/users', requireAdmin, (req, res) => { - const { username, password, role = 'user' } = req.body || {}; - if (!username || !password) return res.status(400).json({ error: 'username + password required' }); - if (!['admin', 'user'].includes(role)) return res.status(400).json({ error: 'bad role' }); - try { - const info = getDb().prepare('INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)') - .run(username, hashPassword(password), role); - getDb().prepare('INSERT INTO profiles (user_id, display_name) VALUES (?, ?)') - .run(info.lastInsertRowid, username); - res.status(201).json({ id: info.lastInsertRowid, username, role }); - } catch (err) { - if (String(err).includes('UNIQUE')) return res.status(409).json({ error: 'username taken' }); - throw err; - } + const { username, password, role = 'user' } = req.body || {}; + if (!username || !password) return res.status(400).json({ error: 'username + password required' }); + if (!['admin', 'user'].includes(role)) return res.status(400).json({ error: 'bad role' }); + try { + const info = getDb().prepare('INSERT INTO users (username, password_hash, role) VALUES (?, ?, ?)') + .run(username, hashPassword(password), role); + getDb().prepare('INSERT INTO profiles (user_id, display_name) VALUES (?, ?)') + .run(info.lastInsertRowid, username); + res.status(201).json({ id: info.lastInsertRowid, username, role }); + } catch (err) { + if (String(err).includes('UNIQUE')) return res.status(409).json({ error: 'username taken' }); + throw err; + } }); router.patch('/users/:id', requireAdmin, (req, res) => { - const id = Number(req.params.id); - const { password, role } = req.body || {}; - const db = getDb(); - if (password) db.prepare('UPDATE users SET password_hash = ? WHERE id = ?').run(hashPassword(password), id); - if (role && ['admin', 'user'].includes(role)) { - db.prepare('UPDATE users SET role = ? WHERE id = ?').run(role, id); - } - res.json({ ok: true }); + const id = Number(req.params.id); + const { password, role } = req.body || {}; + const db = getDb(); + if (password) db.prepare('UPDATE users SET password_hash = ? WHERE id = ?').run(hashPassword(password), id); + if (role && ['admin', 'user'].includes(role)) { + db.prepare('UPDATE users SET role = ? WHERE id = ?').run(role, id); + } + res.json({ ok: true }); }); router.delete('/users/:id', requireAdmin, (req, res) => { - const id = Number(req.params.id); - if (id === req.user.id) return res.status(400).json({ error: 'cannot delete self' }); - getDb().prepare('DELETE FROM users WHERE id = ?').run(id); - res.json({ ok: true }); + const id = Number(req.params.id); + if (id === req.user.id) return res.status(400).json({ error: 'cannot delete self' }); + getDb().prepare('DELETE FROM users WHERE id = ?').run(id); + res.json({ ok: true }); }); diff --git a/server/routes/me.js b/server/routes/me.js index 32de2e6..35b1a0d 100644 --- a/server/routes/me.js +++ b/server/routes/me.js @@ -1,63 +1,86 @@ import { Router } from 'express'; import { requireUser } from '../auth.js'; import { getDb } from '../db/index.js'; +import { getStatsMap } from '../stats.js'; export const router = Router(); router.use(requireUser); router.get('/favorites', (req, res) => { - const rows = getDb().prepare(` + const rows = getDb().prepare(` SELECT s.*, f.position FROM favorites f JOIN stations s ON s.id = f.station_id WHERE f.user_id = ? AND s.enabled = 1 ORDER BY f.position ASC, f.created_at ASC `).all(req.user.id); - res.json(rows.map((r) => ({ - id: r.id, uuid: r.uuid, name: r.name, slug: r.slug, homepage: r.homepage, country: r.country, - genres: r.genres ? JSON.parse(r.genres) : [], image_url: r.image_url, category: r.category, position: r.position - }))); + const stats = getStatsMap(req.user.id); + res.json(rows.map((r) => { + const st = stats.get(r.id) || { up: 0, down: 0, plays: 0, myVote: 0, score: 0 }; + return { + id: r.id, uuid: r.uuid, name: r.name, slug: r.slug, homepage: r.homepage, country: r.country, + genres: r.genres ? JSON.parse(r.genres) : [], image_url: r.image_url, category: r.category, position: r.position, + up: st.up, down: st.down, plays: st.plays, my_vote: st.myVote, score: st.score + }; + })); +}); + +// Pick one random favorite. Returns 404 if the user has none. +router.get('/favorites/random', (req, res) => { + const rows = getDb().prepare(` + SELECT s.* FROM favorites f JOIN stations s ON s.id = f.station_id + WHERE f.user_id = ? AND s.enabled = 1 + `).all(req.user.id); + if (!rows.length) return res.status(404).json({ error: 'no favorites' }); + const r = rows[Math.floor(Math.random() * rows.length)]; + const stats = getStatsMap(req.user.id).get(r.id) || { up: 0, down: 0, plays: 0, myVote: 0, score: 0 }; + res.set('Cache-Control', 'no-store'); + res.json({ + id: r.id, uuid: r.uuid, name: r.name, slug: r.slug, homepage: r.homepage, country: r.country, + genres: r.genres ? JSON.parse(r.genres) : [], image_url: r.image_url, category: r.category, + up: stats.up, down: stats.down, plays: stats.plays, my_vote: stats.myVote, score: stats.score + }); }); router.put('/favorites/:stationId', (req, res) => { - const stationId = Number(req.params.stationId); - const position = Number(req.body?.position ?? 0); - getDb().prepare(` + const stationId = Number(req.params.stationId); + const position = Number(req.body?.position ?? 0); + getDb().prepare(` INSERT INTO favorites (user_id, station_id, position) VALUES (?, ?, ?) ON CONFLICT(user_id, station_id) DO UPDATE SET position = excluded.position `).run(req.user.id, stationId, position); - res.json({ ok: true }); + res.json({ ok: true }); }); router.delete('/favorites/:stationId', (req, res) => { - getDb().prepare('DELETE FROM favorites WHERE user_id = ? AND station_id = ?') - .run(req.user.id, Number(req.params.stationId)); - res.json({ ok: true }); + getDb().prepare('DELETE FROM favorites WHERE user_id = ? AND station_id = ?') + .run(req.user.id, Number(req.params.stationId)); + res.json({ ok: true }); }); router.get('/profile', (req, res) => { - const row = getDb().prepare('SELECT * FROM profiles WHERE user_id = ?').get(req.user.id); - res.json(row || { user_id: req.user.id, display_name: req.user.username, theme: 'dark', default_volume: 0.7 }); + const row = getDb().prepare('SELECT * FROM profiles WHERE user_id = ?').get(req.user.id); + res.json(row || { user_id: req.user.id, display_name: req.user.username, theme: 'dark', default_volume: 0.7 }); }); router.patch('/profile', (req, res) => { - const { display_name, theme, default_volume } = req.body || {}; - getDb().prepare(` + const { display_name, theme, default_volume } = req.body || {}; + getDb().prepare(` INSERT INTO profiles (user_id, display_name, theme, default_volume) VALUES (?, ?, ?, ?) ON CONFLICT(user_id) DO UPDATE SET display_name = COALESCE(excluded.display_name, profiles.display_name), theme = COALESCE(excluded.theme, profiles.theme), default_volume = COALESCE(excluded.default_volume, profiles.default_volume) `).run(req.user.id, display_name ?? null, theme ?? null, default_volume ?? null); - res.json({ ok: true }); + res.json({ ok: true }); }); router.get('/history', (req, res) => { - const rows = getDb().prepare(` + const rows = getDb().prepare(` SELECT h.*, s.name AS station_name, s.slug AS station_slug FROM play_history h JOIN stations s ON s.id = h.station_id WHERE h.user_id = ? ORDER BY h.started_at DESC LIMIT 50 `).all(req.user.id); - res.json(rows); + res.json(rows); }); diff --git a/server/routes/stations.js b/server/routes/stations.js index b0b45c4..6acc4cd 100644 --- a/server/routes/stations.js +++ b/server/routes/stations.js @@ -1,41 +1,77 @@ import { Router } from 'express'; import { - listStations, getStation, getStreamsForStation, - createStation, updateStation, deleteStation, addStream, deleteStream + listStations, getStation, getStreamsForStation, + createStation, updateStation, deleteStation, addStream, deleteStream } from '../stations.js'; import { resolveStream } from '../streams/resolver.js'; import { requireAdmin, requireUser } from '../auth.js'; import * as radiobrowser from '../sources/radiobrowser.js'; +import { castVote, getStationStats, getStatsMap, recordPlay, sortByMode } from '../stats.js'; export const router = Router(); router.get('/', (req, res) => { - const stations = listStations({ - q: req.query.q || undefined, - source: req.query.source || undefined, - enabled: req.query.all ? null : true - }); - res.json(stations); + const stations = listStations({ + q: req.query.q || undefined, + source: req.query.source || undefined, + enabled: req.query.all ? null : true + }); + const statsMap = getStatsMap(req.user?.id || null); + for (const s of stations) { + const st = statsMap.get(s.id) || { up: 0, down: 0, plays: 0, myVote: 0, score: 0 }; + s.up = st.up; s.down = st.down; s.plays = st.plays; + s.my_vote = st.myVote; s.score = st.score; + } + sortByMode(stations, req.query.sort, statsMap); + res.json(stations); }); router.get('/:id', (req, res) => { - const id = Number(req.params.id); - const station = getStation(id); - if (!station) return res.status(404).json({ error: 'not found' }); - station.streams = getStreamsForStation(id); - res.json(station); + const id = Number(req.params.id); + const station = getStation(id); + if (!station) return res.status(404).json({ error: 'not found' }); + station.streams = getStreamsForStation(id); + Object.assign(station, getStationStats(id, req.user?.id || null)); + res.json(station); +}); + +// --- voting --- +router.get('/:id/votes', (req, res) => { + const id = Number(req.params.id); + if (!getStation(id)) return res.status(404).json({ error: 'not found' }); + res.json(getStationStats(id, req.user?.id || null)); +}); + +router.post('/:id/vote', requireUser, (req, res) => { + const id = Number(req.params.id); + if (!getStation(id)) return res.status(404).json({ error: 'not found' }); + const raw = req.body?.value; + const value = raw === 1 || raw === '1' || raw === 'up' ? 1 + : raw === -1 || raw === '-1' || raw === 'down' ? -1 + : raw === 0 || raw === '0' || raw === null || raw === 'clear' ? 0 + : NaN; + if (Number.isNaN(value)) return res.status(400).json({ error: 'value must be 1, -1 or 0' }); + res.json(castVote(req.user.id, id, value)); +}); + +// Lightweight play-count ping (called when the kiosk actually starts a station). +router.post('/:id/play', requireUser, (req, res) => { + const id = Number(req.params.id); + if (!getStation(id)) return res.status(404).json({ error: 'not found' }); + recordPlay(id); + res.json(getStationStats(id, req.user.id)); }); router.post('/:id/resolve', requireUser, async (req, res) => { - const id = Number(req.params.id); - const streams = getStreamsForStation(id); - if (!streams.length) return res.status(404).json({ error: 'no streams' }); - const preferred = req.body?.streamId - ? streams.find((s) => s.id === Number(req.body.streamId)) - : streams[0]; - if (!preferred) return res.status(404).json({ error: 'stream not found' }); - const resolved = await resolveStream({ url: preferred.url, format: preferred.format }); - res.json({ stream: preferred, resolved }); + const id = Number(req.params.id); + const streams = getStreamsForStation(id); + if (!streams.length) return res.status(404).json({ error: 'no streams' }); + const preferred = req.body?.streamId + ? streams.find((s) => s.id === Number(req.body.streamId)) + : streams[0]; + if (!preferred) return res.status(404).json({ error: 'stream not found' }); + const resolved = await resolveStream({ url: preferred.url, format: preferred.format }); + res.json({ stream: preferred, resolved }); }); // Same-origin streaming proxy. Adds the CORS headers Icecast/SHOUTcast servers @@ -43,108 +79,108 @@ router.post('/:id/resolve', requireUser, async (req, res) => { // real spectrum. HLS is excluded β€” the manifest plus every segment would need // rewriting; clients fall back to the direct URL with no analyser there. router.get('/:id/proxy', requireUser, async (req, res) => { - const id = Number(req.params.id); - const streams = getStreamsForStation(id); - if (!streams.length) return res.status(404).json({ error: 'no streams' }); - const preferred = req.query.streamId - ? streams.find((s) => s.id === Number(req.query.streamId)) - : streams[0]; - if (!preferred) return res.status(404).json({ error: 'stream not found' }); - const resolved = await resolveStream({ url: preferred.url, format: preferred.format }); - if (resolved.format === 'hls') return res.status(415).json({ error: 'hls not proxied' }); + const id = Number(req.params.id); + const streams = getStreamsForStation(id); + if (!streams.length) return res.status(404).json({ error: 'no streams' }); + const preferred = req.query.streamId + ? streams.find((s) => s.id === Number(req.query.streamId)) + : streams[0]; + if (!preferred) return res.status(404).json({ error: 'stream not found' }); + const resolved = await resolveStream({ url: preferred.url, format: preferred.format }); + if (resolved.format === 'hls') return res.status(415).json({ error: 'hls not proxied' }); - const controller = new AbortController(); - req.on('close', () => controller.abort()); + const controller = new AbortController(); + req.on('close', () => controller.abort()); - let upstream; - try { - upstream = await fetch(resolved.url, { - redirect: 'follow', - signal: controller.signal, - headers: { 'User-Agent': 'oradio-kiosk/1.0', 'Icy-MetaData': '0' } - }); - } catch (err) { - return res.status(502).json({ error: `upstream: ${err.message || err}` }); - } - if (!upstream.ok || !upstream.body) { - return res.status(502).json({ error: `upstream HTTP ${upstream.status}` }); - } - - const ct = upstream.headers.get('content-type') || guessContentType(resolved.format); - res.status(200); - res.set('Content-Type', ct); - res.set('Cache-Control', 'no-store'); - res.set('Access-Control-Allow-Origin', '*'); - res.set('Access-Control-Expose-Headers', 'Content-Type'); - - // Pipe the WHATWG ReadableStream into the Express response. - const reader = upstream.body.getReader(); - const pump = async () => { + let upstream; try { - while (true) { - const { value, done } = await reader.read(); - if (done) break; - if (!res.write(Buffer.from(value))) { - await new Promise((r) => res.once('drain', r)); - } - } - } catch { /* client disconnect or upstream abort */ } - finally { - try { reader.cancel(); } catch {} - res.end(); + upstream = await fetch(resolved.url, { + redirect: 'follow', + signal: controller.signal, + headers: { 'User-Agent': 'oradio-kiosk/1.0', 'Icy-MetaData': '0' } + }); + } catch (err) { + return res.status(502).json({ error: `upstream: ${err.message || err}` }); } - }; - pump(); + if (!upstream.ok || !upstream.body) { + return res.status(502).json({ error: `upstream HTTP ${upstream.status}` }); + } + + const ct = upstream.headers.get('content-type') || guessContentType(resolved.format); + res.status(200); + res.set('Content-Type', ct); + res.set('Cache-Control', 'no-store'); + res.set('Access-Control-Allow-Origin', '*'); + res.set('Access-Control-Expose-Headers', 'Content-Type'); + + // Pipe the WHATWG ReadableStream into the Express response. + const reader = upstream.body.getReader(); + const pump = async () => { + try { + while (true) { + const { value, done } = await reader.read(); + if (done) break; + if (!res.write(Buffer.from(value))) { + await new Promise((r) => res.once('drain', r)); + } + } + } catch { /* client disconnect or upstream abort */ } + finally { + try { reader.cancel(); } catch { } + res.end(); + } + }; + pump(); }); function guessContentType(format) { - switch (format) { - case 'mp3': return 'audio/mpeg'; - case 'aac': return 'audio/aac'; - case 'ogg': return 'audio/ogg'; - default: return 'application/octet-stream'; - } + switch (format) { + case 'mp3': return 'audio/mpeg'; + case 'aac': return 'audio/aac'; + case 'ogg': return 'audio/ogg'; + default: return 'application/octet-stream'; + } } // --- admin mutations --- router.post('/', requireAdmin, (req, res) => { - const station = createStation({ ...req.body, source: req.body.source || 'manual' }, req.user.id); - res.status(201).json(station); + const station = createStation({ ...req.body, source: req.body.source || 'manual' }, req.user.id); + res.status(201).json(station); }); router.patch('/:id', requireAdmin, (req, res) => { - const station = updateStation(Number(req.params.id), req.body || {}); - if (!station) return res.status(404).json({ error: 'not found' }); - res.json(station); + const station = updateStation(Number(req.params.id), req.body || {}); + if (!station) return res.status(404).json({ error: 'not found' }); + res.json(station); }); router.delete('/:id', requireAdmin, (req, res) => { - if (!deleteStation(Number(req.params.id))) return res.status(404).json({ error: 'not found' }); - res.json({ ok: true }); + if (!deleteStation(Number(req.params.id))) return res.status(404).json({ error: 'not found' }); + res.json({ ok: true }); }); router.post('/:id/streams', requireAdmin, (req, res) => { - const stream = addStream(Number(req.params.id), req.body || {}); - res.status(201).json(stream); + const stream = addStream(Number(req.params.id), req.body || {}); + res.status(201).json(stream); }); router.delete('/:id/streams/:streamId', requireAdmin, (req, res) => { - if (!deleteStream(Number(req.params.streamId))) return res.status(404).json({ error: 'not found' }); - res.json({ ok: true }); + if (!deleteStream(Number(req.params.streamId))) return res.status(404).json({ error: 'not found' }); + res.json({ ok: true }); }); // --- Radio-Browser passthrough for the admin importer --- router.get('/sources/radiobrowser/search', requireAdmin, async (req, res) => { - const results = await radiobrowser.search({ - name: req.query.q, - country: req.query.country, - tag: req.query.tag, - limit: Number(req.query.limit) || 30 - }); - res.json(results); + const results = await radiobrowser.search({ + name: req.query.q, + country: req.query.country, + tag: req.query.tag, + limit: Number(req.query.limit) || 30 + }); + res.json(results); }); router.post('/sources/radiobrowser/import', requireAdmin, (req, res) => { - const station = createStation({ ...req.body, source: 'radiobrowser' }, req.user.id); - res.status(201).json(station); + const station = createStation({ ...req.body, source: 'radiobrowser' }, req.user.id); + res.status(201).json(station); }); diff --git a/server/routes/v1.js b/server/routes/v1.js index 8fa56a1..de82bc5 100644 --- a/server/routes/v1.js +++ b/server/routes/v1.js @@ -4,21 +4,22 @@ import { Router } from 'express'; import { - listStations, getStationByUuid, getStreamsForStation, getStreamByUuid + listStations, getStationByUuid, getStreamsForStation, getStreamByUuid } from '../stations.js'; import { resolveStream } from '../streams/resolver.js'; import { getDb } from '../db/index.js'; import { loadCategoriesFile } from '../sources/seed.js'; +import { getStationStats, getStatsMap, sortByMode } from '../stats.js'; export const router = Router(); // CORS for public endpoints. Browser-side integrations can hit the API // from any origin; we don't expose any user data here. router.use((_req, res, next) => { - res.set('Access-Control-Allow-Origin', '*'); - res.set('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS'); - res.set('Access-Control-Allow-Headers', 'Content-Type'); - next(); + res.set('Access-Control-Allow-Origin', '*'); + res.set('Access-Control-Allow-Methods', 'GET, HEAD, OPTIONS'); + res.set('Access-Control-Allow-Headers', 'Content-Type'); + next(); }); // Tiny in-memory token bucket per IP. 120 req/min is plenty for human use @@ -27,141 +28,193 @@ const buckets = new Map(); const RATE = 120; const WINDOW_MS = 60_000; router.use((req, res, next) => { - const key = req.ip || 'unknown'; - const now = Date.now(); - const b = buckets.get(key) || { count: 0, reset: now + WINDOW_MS }; - if (now > b.reset) { b.count = 0; b.reset = now + WINDOW_MS; } - b.count += 1; - buckets.set(key, b); - res.set('X-RateLimit-Limit', String(RATE)); - res.set('X-RateLimit-Remaining', String(Math.max(0, RATE - b.count))); - if (b.count > RATE) return res.status(429).json({ error: 'rate limited' }); - next(); + const key = req.ip || 'unknown'; + const now = Date.now(); + const b = buckets.get(key) || { count: 0, reset: now + WINDOW_MS }; + if (now > b.reset) { b.count = 0; b.reset = now + WINDOW_MS; } + b.count += 1; + buckets.set(key, b); + res.set('X-RateLimit-Limit', String(RATE)); + res.set('X-RateLimit-Remaining', String(Math.max(0, RATE - b.count))); + if (b.count > RATE) return res.status(429).json({ error: 'rate limited' }); + next(); }); function publicStation(s) { - if (!s) return null; - return { - uuid: s.uuid, - name: s.name, - slug: s.slug, - homepage: s.homepage, - country: s.country, - genres: s.genres, - description: s.description, - image_url: s.image_url, - category: s.category, - enabled: s.enabled - }; + if (!s) return null; + return { + uuid: s.uuid, + name: s.name, + slug: s.slug, + homepage: s.homepage, + country: s.country, + genres: s.genres, + description: s.description, + image_url: s.image_url, + category: s.category, + enabled: s.enabled, + up: s.up ?? 0, + down: s.down ?? 0, + plays: s.plays ?? 0, + score: s.score ?? 0 + }; } function publicStream(s) { - if (!s) return null; - return { - uuid: s.uuid, - url: s.url, - format: s.format, - bitrate: s.bitrate, - label: s.label, - priority: s.priority, - last_status: s.last_status, - last_checked_at: s.last_checked_at - }; + if (!s) return null; + return { + uuid: s.uuid, + url: s.url, + format: s.format, + bitrate: s.bitrate, + label: s.label, + priority: s.priority, + last_status: s.last_status, + last_checked_at: s.last_checked_at + }; } router.get('/health', (_req, res) => { - const stations = getDb().prepare('SELECT COUNT(*) AS n FROM stations WHERE enabled = 1').get().n; - res.json({ ok: true, stations }); + const stations = getDb().prepare('SELECT COUNT(*) AS n FROM stations WHERE enabled = 1').get().n; + res.json({ ok: true, stations }); }); router.get('/categories', (_req, res) => { - const rows = getDb().prepare(` + const rows = getDb().prepare(` SELECT category AS id, COUNT(*) AS count FROM stations WHERE enabled = 1 AND category IS NOT NULL AND category <> '' GROUP BY category `).all(); - const counts = new Map(rows.map((r) => [r.id, r.count])); - const meta = loadCategoriesFile(); - const seen = new Set(); - const out = []; - for (const m of meta) { - seen.add(m.id); - out.push({ ...m, count: counts.get(m.id) || 0 }); - } - for (const [id, count] of counts) { - if (seen.has(id)) continue; - out.push({ id, label: id, icon: '', order: 999, count }); - } - out.sort((a, b) => (a.order ?? 999) - (b.order ?? 999) || String(a.id).localeCompare(String(b.id))); - res.json(out); + const counts = new Map(rows.map((r) => [r.id, r.count])); + const meta = loadCategoriesFile(); + const seen = new Set(); + const out = []; + for (const m of meta) { + seen.add(m.id); + out.push({ ...m, count: counts.get(m.id) || 0 }); + } + for (const [id, count] of counts) { + if (seen.has(id)) continue; + out.push({ id, label: id, icon: '', order: 999, count }); + } + out.sort((a, b) => (a.order ?? 999) - (b.order ?? 999) || String(a.id).localeCompare(String(b.id))); + res.json(out); }); router.get('/stations', (req, res) => { - const limit = Math.min(Number(req.query.limit) || 200, 1000); - let items = listStations({ - q: req.query.q || undefined, - category: req.query.category || undefined, - enabled: req.query.all ? null : true - }); - if (req.query.country) { - const c = String(req.query.country).toUpperCase(); - items = items.filter((s) => (s.country || '').toUpperCase() === c); - } - if (req.query.genre) { - const g = String(req.query.genre).toLowerCase(); - items = items.filter((s) => (s.genres || []).some((x) => x.toLowerCase().includes(g))); - } - res.json({ - total: items.length, - items: items.slice(0, limit).map(publicStation) - }); + const limit = Math.min(Number(req.query.limit) || 200, 1000); + let items = listStations({ + q: req.query.q || undefined, + category: req.query.category || undefined, + enabled: req.query.all ? null : true + }); + if (req.query.country) { + const c = String(req.query.country).toUpperCase(); + items = items.filter((s) => (s.country || '').toUpperCase() === c); + } + if (req.query.genre) { + const g = String(req.query.genre).toLowerCase(); + items = items.filter((s) => (s.genres || []).some((x) => x.toLowerCase().includes(g))); + } + const statsMap = getStatsMap(null); + for (const s of items) { + const st = statsMap.get(s.id) || { up: 0, down: 0, plays: 0, score: 0 }; + s.up = st.up; s.down = st.down; s.plays = st.plays; s.score = st.score; + } + sortByMode(items, req.query.sort, statsMap); + res.json({ + total: items.length, + items: items.slice(0, limit).map(publicStation) + }); +}); + +// Pick a random enabled station. Optional filters narrow the pool. +// `redirect=stream` issues a 302 to the resolved stream URL β€” handy for +// `mpv http://host/api/v1/stations/random?redirect=stream`. +router.get('/stations/random', async (req, res) => { + let items = listStations({ + category: req.query.category || undefined, + enabled: true + }); + if (req.query.country) { + const c = String(req.query.country).toUpperCase(); + items = items.filter((s) => (s.country || '').toUpperCase() === c); + } + if (req.query.genre) { + const g = String(req.query.genre).toLowerCase(); + items = items.filter((s) => (s.genres || []).some((x) => x.toLowerCase().includes(g))); + } + if (!items.length) return res.status(404).json({ error: 'no stations match' }); + const pick = items[Math.floor(Math.random() * items.length)]; + Object.assign(pick, getStationStats(pick.id, null)); + + if (req.query.redirect === 'stream') { + const streams = getStreamsForStation(pick.id); + if (!streams.length) return res.status(404).json({ error: 'no streams' }); + const ordered = [...streams].sort((a, b) => { + const au = a.last_status === 'up' ? 0 : 1; + const bu = b.last_status === 'up' ? 0 : 1; + return au - bu || a.priority - b.priority; + }); + const resolved = await resolveStream({ url: ordered[0].url, format: ordered[0].format }); + res.set('Cache-Control', 'no-store'); + res.set('X-Station-Uuid', pick.uuid); + res.set('X-Station-Name', encodeURIComponent(pick.name)); + return res.redirect(302, resolved.url); + } + + const out = publicStation(pick); + out.streams = getStreamsForStation(pick.id).map(publicStream); + res.set('Cache-Control', 'no-store'); + res.json(out); }); router.get('/stations/:uuid', (req, res) => { - const s = getStationByUuid(req.params.uuid); - if (!s) return res.status(404).json({ error: 'not found' }); - const out = publicStation(s); - out.streams = getStreamsForStation(s.id).map(publicStream); - res.json(out); + const s = getStationByUuid(req.params.uuid); + if (!s) return res.status(404).json({ error: 'not found' }); + Object.assign(s, getStationStats(s.id, null)); + const out = publicStation(s); + out.streams = getStreamsForStation(s.id).map(publicStream); + res.json(out); }); // 302 redirect to the resolved stream URL. Pure convenience for CLI players // (`mpv http://host/api/v1/stations//stream`) and smart-home scripts. router.get('/stations/:uuid/stream', async (req, res) => { - const s = getStationByUuid(req.params.uuid); - if (!s) return res.status(404).json({ error: 'station not found' }); - let streams = getStreamsForStation(s.id); - if (!streams.length) return res.status(404).json({ error: 'no streams' }); + const s = getStationByUuid(req.params.uuid); + if (!s) return res.status(404).json({ error: 'station not found' }); + let streams = getStreamsForStation(s.id); + if (!streams.length) return res.status(404).json({ error: 'no streams' }); - if (req.query.format) { - const fmt = String(req.query.format).toLowerCase(); - const filtered = streams.filter((x) => x.format === fmt); - if (filtered.length) streams = filtered; - } - // Prefer streams known to be up; fall back to priority order otherwise. - const ordered = [...streams].sort((a, b) => { - const au = a.last_status === 'up' ? 0 : 1; - const bu = b.last_status === 'up' ? 0 : 1; - return au - bu || a.priority - b.priority; - }); - const pick = ordered[0]; - const resolved = await resolveStream({ url: pick.url, format: pick.format }); - res.set('Cache-Control', 'no-store'); - res.redirect(302, resolved.url); + if (req.query.format) { + const fmt = String(req.query.format).toLowerCase(); + const filtered = streams.filter((x) => x.format === fmt); + if (filtered.length) streams = filtered; + } + // Prefer streams known to be up; fall back to priority order otherwise. + const ordered = [...streams].sort((a, b) => { + const au = a.last_status === 'up' ? 0 : 1; + const bu = b.last_status === 'up' ? 0 : 1; + return au - bu || a.priority - b.priority; + }); + const pick = ordered[0]; + const resolved = await resolveStream({ url: pick.url, format: pick.format }); + res.set('Cache-Control', 'no-store'); + res.redirect(302, resolved.url); }); router.get('/stations/:uuid/streams/:streamUuid', async (req, res) => { - const station = getStationByUuid(req.params.uuid); - if (!station) return res.status(404).json({ error: 'station not found' }); - const stream = getStreamByUuid(req.params.streamUuid); - if (!stream || stream.station_id !== station.id) return res.status(404).json({ error: 'stream not found' }); - if (req.query.redirect === '0') { - return res.json(publicStream(stream)); - } - const resolved = await resolveStream({ url: stream.url, format: stream.format }); - res.set('Cache-Control', 'no-store'); - res.redirect(302, resolved.url); + const station = getStationByUuid(req.params.uuid); + if (!station) return res.status(404).json({ error: 'station not found' }); + const stream = getStreamByUuid(req.params.streamUuid); + if (!stream || stream.station_id !== station.id) return res.status(404).json({ error: 'stream not found' }); + if (req.query.redirect === '0') { + return res.json(publicStream(stream)); + } + const resolved = await resolveStream({ url: stream.url, format: stream.format }); + res.set('Cache-Control', 'no-store'); + res.redirect(302, resolved.url); }); // Reject any non-GET method explicitly so the public surface can never be diff --git a/server/scripts/import-allradio-nl.js b/server/scripts/import-allradio-nl.js index e0605b7..baa4ced 100644 --- a/server/scripts/import-allradio-nl.js +++ b/server/scripts/import-allradio-nl.js @@ -20,184 +20,184 @@ const UA = 'OnlineRadioExplorer/0.1 (+import-allradio-nl)'; // Station names taken from https://www.allradio.net/country/3 (pages 1+2), // minus duplicates and minus ones already seeded under stations-extended.json. const NAMES = [ - // public broadcasters not yet seeded - ['NPO 3FM Alternative', 'dutch-public'], - ['NPO 3FM KX', 'dutch-public'], - ['NPO FunX NL', 'dutch-public'], - ['NPO FunX Reggae', 'dutch-public'], - ['NPO 2', 'dutch-public'], - ['Radio Rijnmond', 'dutch-public'], - ['Omroep Gelderland', 'dutch-public'], - ['Omroep West', 'dutch-public'], - // commercials - ['Radio 10', 'dutch-commercial'], - ['Radio 10 80\'s Hits', 'dutch-commercial'], - ['Radio 10 60\'s & 70\'s Hits', 'dutch-commercial'], - ['Radio 538 Nonstop', 'dutch-commercial'], - ['538 Dance Department', 'dutch-commercial'], - ['538 TOP 50', 'dutch-commercial'], - ['Sky Radio Hits', 'dutch-commercial'], - ['Sky Radio 90\'s Hits', 'dutch-commercial'], - ['Sky Radio 101 FM', 'dutch-commercial'], - ['SLAM FM', 'dutch-commercial'], - ['Veronica Rockradio', 'dutch-commercial'], - ['Veronica TOP1000 AllerTijden', 'dutch-commercial'], - ['JAMM FM', 'dutch-commercial'], - ['RADIONL', 'dutch-commercial'], - ['Grand Prix Radio', 'dutch-commercial'], - ['XXL Stenders', 'dutch-commercial'], - ['Sublime - Live', 'dutch-commercial'], - ['Sublime - Soul', 'dutch-commercial'], - // rock & alt - ['KINK', 'rock'], - ['KINK CLASSICS', 'rock'], - ['Baars classic Rock', 'rock'], - ['ISKC Rock Radio', 'rock'], - ['ICE RADIO', 'rock'], - // electronic / dance / hard - ['Jungletrain.net', 'electronic'], - ['Real Hardstyle Radio', 'electronic'], - ['Hardstyle Radio NL', 'electronic'], - ['Hardcore Power', 'electronic'], - ['Freak31', 'electronic'], - ['Decibel', 'electronic'], - ['Decibel EURODANCE', 'electronic'], - ['Intense Radio', 'electronic'], - ['Deep Radio', 'electronic'], - ['Fantasy Radio - Italo Disco Euro Dance HiNRG', 'electronic'], - ['MixPerfect Radio', 'electronic'], - ['Dancegroove Radio', 'electronic'], - ['DANCEableRADIO', 'electronic'], - // jazz / lounge / classical - ['Jazz de Ville - Jazz', 'jazz'], - ['Jazz de Ville - Chill', 'jazz'], - ['Hi On Line Jazz Radio', 'jazz'], - ['Hi On Line Classical Radio', 'classical'], - ['Hi On Line Lounge Radio', 'ambient'], - ['Hi On Line World Radio', 'world'], - ['Hi On Line Latin Radio', 'world'], - ['Hi On Line Radio - Pop', 'dutch-commercial'], - ['ClassicFM - Chillout', 'classical'], - ['Classic NL', 'classical'], - // niche / community / piraten - ['Pinguin Blues', 'jazz'], - ['Pinguin Ska World', 'reggae'], - ['Lachende Piraat', 'world'], - ['Oude Piraten Hits', 'world'], - ['Radio Caroline 319 Gold', 'world'], - ['Radio Nostalgia', 'world'], - ['Slow Radio Gold', 'world'], - ['Olympia Classics', 'world'], - ['Peaceful Radio', 'ambient'], - ['Amsterdam Funk Channel', 'electronic'], - ['247Spice', 'world'], - ['SH Radio', 'world'], - ['Rivierenland Radio', 'world'], - ['Grolloo Radio', 'rock'], - ['All Oldies Channel', 'world'], - ['i-turn radio', 'world'], - ['NPO 3FM Serious Radio', 'dutch-public'] + // public broadcasters not yet seeded + ['NPO 3FM Alternative', 'dutch-public'], + ['NPO 3FM KX', 'dutch-public'], + ['NPO FunX NL', 'dutch-public'], + ['NPO FunX Reggae', 'dutch-public'], + ['NPO 2', 'dutch-public'], + ['Radio Rijnmond', 'dutch-public'], + ['Omroep Gelderland', 'dutch-public'], + ['Omroep West', 'dutch-public'], + // commercials + ['Radio 10', 'dutch-commercial'], + ['Radio 10 80\'s Hits', 'dutch-commercial'], + ['Radio 10 60\'s & 70\'s Hits', 'dutch-commercial'], + ['Radio 538 Nonstop', 'dutch-commercial'], + ['538 Dance Department', 'dutch-commercial'], + ['538 TOP 50', 'dutch-commercial'], + ['Sky Radio Hits', 'dutch-commercial'], + ['Sky Radio 90\'s Hits', 'dutch-commercial'], + ['Sky Radio 101 FM', 'dutch-commercial'], + ['SLAM FM', 'dutch-commercial'], + ['Veronica Rockradio', 'dutch-commercial'], + ['Veronica TOP1000 AllerTijden', 'dutch-commercial'], + ['JAMM FM', 'dutch-commercial'], + ['RADIONL', 'dutch-commercial'], + ['Grand Prix Radio', 'dutch-commercial'], + ['XXL Stenders', 'dutch-commercial'], + ['Sublime - Live', 'dutch-commercial'], + ['Sublime - Soul', 'dutch-commercial'], + // rock & alt + ['KINK', 'rock'], + ['KINK CLASSICS', 'rock'], + ['Baars classic Rock', 'rock'], + ['ISKC Rock Radio', 'rock'], + ['ICE RADIO', 'rock'], + // electronic / dance / hard + ['Jungletrain.net', 'electronic'], + ['Real Hardstyle Radio', 'electronic'], + ['Hardstyle Radio NL', 'electronic'], + ['Hardcore Power', 'electronic'], + ['Freak31', 'electronic'], + ['Decibel', 'electronic'], + ['Decibel EURODANCE', 'electronic'], + ['Intense Radio', 'electronic'], + ['Deep Radio', 'electronic'], + ['Fantasy Radio - Italo Disco Euro Dance HiNRG', 'electronic'], + ['MixPerfect Radio', 'electronic'], + ['Dancegroove Radio', 'electronic'], + ['DANCEableRADIO', 'electronic'], + // jazz / lounge / classical + ['Jazz de Ville - Jazz', 'jazz'], + ['Jazz de Ville - Chill', 'jazz'], + ['Hi On Line Jazz Radio', 'jazz'], + ['Hi On Line Classical Radio', 'classical'], + ['Hi On Line Lounge Radio', 'ambient'], + ['Hi On Line World Radio', 'world'], + ['Hi On Line Latin Radio', 'world'], + ['Hi On Line Radio - Pop', 'dutch-commercial'], + ['ClassicFM - Chillout', 'classical'], + ['Classic NL', 'classical'], + // niche / community / piraten + ['Pinguin Blues', 'jazz'], + ['Pinguin Ska World', 'reggae'], + ['Lachende Piraat', 'world'], + ['Oude Piraten Hits', 'world'], + ['Radio Caroline 319 Gold', 'world'], + ['Radio Nostalgia', 'world'], + ['Slow Radio Gold', 'world'], + ['Olympia Classics', 'world'], + ['Peaceful Radio', 'ambient'], + ['Amsterdam Funk Channel', 'electronic'], + ['247Spice', 'world'], + ['SH Radio', 'world'], + ['Rivierenland Radio', 'world'], + ['Grolloo Radio', 'rock'], + ['All Oldies Channel', 'world'], + ['i-turn radio', 'world'], + ['NPO 3FM Serious Radio', 'dutch-public'] ]; function detectFormat(codec, url) { - const c = (codec || '').toLowerCase(); - if (c.includes('mp3')) return 'mp3'; - if (c.includes('aac')) return 'aac'; - if (c.includes('ogg') || c.includes('vorbis') || c.includes('opus')) return 'ogg'; - if (url?.endsWith('.m3u8')) return 'hls'; - if (url?.endsWith('.m3u')) return 'm3u'; - if (url?.endsWith('.pls')) return 'pls'; - return 'unknown'; + const c = (codec || '').toLowerCase(); + if (c.includes('mp3')) return 'mp3'; + if (c.includes('aac')) return 'aac'; + if (c.includes('ogg') || c.includes('vorbis') || c.includes('opus')) return 'ogg'; + if (url?.endsWith('.m3u8')) return 'hls'; + if (url?.endsWith('.m3u')) return 'm3u'; + if (url?.endsWith('.pls')) return 'pls'; + return 'unknown'; } function slugify(name) { - return name.toLowerCase() - .replace(/[^a-z0-9]+/g, '-') - .replace(/^-+|-+$/g, '') - .slice(0, 80); + return name.toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, '') + .slice(0, 80); } async function rbSearch(name) { - const url = `${RB}/json/stations/search?name=${encodeURIComponent(name)}&countrycode=NL&limit=5&hidebroken=true&order=clickcount&reverse=true`; - const res = await fetch(url, { headers: { 'User-Agent': UA } }); - if (!res.ok) throw new Error(`RB ${res.status}`); - const list = await res.json(); - // also try without country filter as fallback (some entries have wrong country) - if (!list.length) { - const r2 = await fetch(`${RB}/json/stations/search?name=${encodeURIComponent(name)}&limit=5&hidebroken=true&order=clickcount&reverse=true`, { headers: { 'User-Agent': UA } }); - if (r2.ok) return r2.json(); - } - return list; + const url = `${RB}/json/stations/search?name=${encodeURIComponent(name)}&countrycode=NL&limit=5&hidebroken=true&order=clickcount&reverse=true`; + const res = await fetch(url, { headers: { 'User-Agent': UA } }); + if (!res.ok) throw new Error(`RB ${res.status}`); + const list = await res.json(); + // also try without country filter as fallback (some entries have wrong country) + if (!list.length) { + const r2 = await fetch(`${RB}/json/stations/search?name=${encodeURIComponent(name)}&limit=5&hidebroken=true&order=clickcount&reverse=true`, { headers: { 'User-Agent': UA } }); + if (r2.ok) return r2.json(); + } + return list; } function pickBest(list, target) { - if (!list.length) return null; - const t = target.toLowerCase().trim(); - const exact = list.find((s) => (s.name || '').toLowerCase().trim() === t); - return exact || list[0]; + if (!list.length) return null; + const t = target.toLowerCase().trim(); + const exact = list.find((s) => (s.name || '').toLowerCase().trim() === t); + return exact || list[0]; } function toEntry(s, category) { - const stream = { - url: s.url_resolved || s.url, - format: detectFormat(s.codec, s.url_resolved || s.url), - bitrate: s.bitrate || null, - label: s.codec ? `${s.codec} ${s.bitrate || ''}`.trim() : null, - priority: 0 - }; - return { - uuid: s.stationuuid, - slug: `rb-${s.stationuuid.slice(0, 8)}-${slugify(s.name).slice(0, 40)}`, - name: s.name, - category, - country: s.countrycode || 'NL', - homepage: s.homepage || null, - genres: (s.tags || '').split(',').map((t) => t.trim()).filter(Boolean).slice(0, 5), - description: null, - image_url: s.favicon || null, - source: 'radiobrowser', - source_ref: s.stationuuid, - streams: [stream] - }; + const stream = { + url: s.url_resolved || s.url, + format: detectFormat(s.codec, s.url_resolved || s.url), + bitrate: s.bitrate || null, + label: s.codec ? `${s.codec} ${s.bitrate || ''}`.trim() : null, + priority: 0 + }; + return { + uuid: s.stationuuid, + slug: `rb-${s.stationuuid.slice(0, 8)}-${slugify(s.name).slice(0, 40)}`, + name: s.name, + category, + country: s.countrycode || 'NL', + homepage: s.homepage || null, + genres: (s.tags || '').split(',').map((t) => t.trim()).filter(Boolean).slice(0, 5), + description: null, + image_url: s.favicon || null, + source: 'radiobrowser', + source_ref: s.stationuuid, + streams: [stream] + }; } async function main() { - const out = []; - const seenUuids = new Set(); + const out = []; + const seenUuids = new Set(); - // Vintage Obscura β€” direct, no RB. - out.push({ - slug: 'vintage-obscura', - name: 'Vintage Obscura Radio', - category: 'underground', - country: 'US', - homepage: 'https://vintageobscura.net/', - genres: ['vintage', 'obscure', 'curated', 'reddit'], - description: 'Curated rare music discovered daily by /r/vintageobscura. All tracks <30k YouTube views, pre-2000.', - image_url: 'https://vintageobscura.net/img/vintage-obscura-logo.png', - streams: [ - { url: 'https://radio.vintageobscura.net/stream', format: 'mp3', bitrate: 128, label: 'MP3 128', priority: 0 } - ] - }); + // Vintage Obscura β€” direct, no RB. + out.push({ + slug: 'vintage-obscura', + name: 'Vintage Obscura Radio', + category: 'underground', + country: 'US', + homepage: 'https://vintageobscura.net/', + genres: ['vintage', 'obscure', 'curated', 'reddit'], + description: 'Curated rare music discovered daily by /r/vintageobscura. All tracks <30k YouTube views, pre-2000.', + image_url: 'https://vintageobscura.net/img/vintage-obscura-logo.png', + streams: [ + { url: 'https://radio.vintageobscura.net/stream', format: 'mp3', bitrate: 128, label: 'MP3 128', priority: 0 } + ] + }); - for (const [name, category] of NAMES) { - try { - const hits = await rbSearch(name); - const pick = pickBest(hits, name); - if (!pick) { console.warn(' miss:', name); continue; } - if (seenUuids.has(pick.stationuuid)) { console.warn(' dup:', name, '->', pick.name); continue; } - seenUuids.add(pick.stationuuid); - out.push(toEntry(pick, category)); - console.log(' ok :', name, '->', pick.name, `(${pick.codec || '?'} ${pick.bitrate || ''})`); - } catch (err) { - console.warn(' err:', name, err.message); + for (const [name, category] of NAMES) { + try { + const hits = await rbSearch(name); + const pick = pickBest(hits, name); + if (!pick) { console.warn(' miss:', name); continue; } + if (seenUuids.has(pick.stationuuid)) { console.warn(' dup:', name, '->', pick.name); continue; } + seenUuids.add(pick.stationuuid); + out.push(toEntry(pick, category)); + console.log(' ok :', name, '->', pick.name, `(${pick.codec || '?'} ${pick.bitrate || ''})`); + } catch (err) { + console.warn(' err:', name, err.message); + } + // gentle pacing + await new Promise((r) => setTimeout(r, 80)); } - // gentle pacing - await new Promise((r) => setTimeout(r, 80)); - } - fs.writeFileSync(OUT, JSON.stringify(out, null, 2) + '\n', 'utf8'); - console.log(`\nwrote ${out.length} entries to ${path.relative(ROOT, OUT)}`); + fs.writeFileSync(OUT, JSON.stringify(out, null, 2) + '\n', 'utf8'); + console.log(`\nwrote ${out.length} entries to ${path.relative(ROOT, OUT)}`); } main().catch((e) => { console.error(e); process.exit(1); }); diff --git a/server/scripts/import-underground.js b/server/scripts/import-underground.js new file mode 100644 index 0000000..36ba208 --- /dev/null +++ b/server/scripts/import-underground.js @@ -0,0 +1,189 @@ +// One-shot importer: resolves a curated list of underground / experimental / +// DJ-led stations against Radio-Browser, then writes +// data/seed/stations-underground.json. Re-running is safe: the seeder merges +// by Radio-Browser UUID. +// +// Usage: node server/scripts/import-underground.js +// +// All entries are real DJ-broadcast / community / college / free-form stations +// (no playlist bots), spread across punk, house, jazz, eclectic, underground. + +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const ROOT = path.resolve(__dirname, '..', '..'); +const OUT = path.join(ROOT, 'data', 'seed', 'stations-underground.json'); +const RB = 'https://de1.api.radio-browser.info'; +const UA = 'OnlineRadioExplorer/0.1 (+import-underground)'; + +// Each entry: [searchName, countryCodeOrNull, expectedNameSubstring] +// expectedNameSubstring is a lowercase substring used to filter out wrong-station +// hits when the same name is shared (e.g. two "Skylab Radio"s in different countries). +const PICKS = [ + // === UK / IE underground & DJ-led === + ['Foundation FM', 'GB', 'foundation fm'], + ['Aaja Radio | Channel 1', 'GB', 'aaja'], + ['Aaja Radio | Channel 2', 'GB', 'aaja'], + ['Bloop London Radio', 'GB', 'bloop london'], + ['Reform Radio', 'GB', 'reform radio'], + ['1BTN', 'GB', '1btn'], + ['Sub.fm', 'GB', 'sub.fm'], + ['Radio Wigwam', 'GB', 'wigwam'], + ['Skylab Radio', 'GB', 'skylab'], + + // === NL underground === + ['Echobox', 'NL', 'echobox'], + ['Operator Radio', 'NL', 'operator'], + + // === DE / AT / CH underground === + ['byte.fm', 'DE', 'byte'], + ['Radio 80000', 'DE', '80000'], + ['FluxFM', 'DE', 'fluxfm'], + ['FluxFM - Techno Underground', 'DE', 'techno'], + ['Radio Eins', 'DE', 'radio eins'], + ['Radio Helsinki 98,5 Mhz', 'FI', 'helsinki'], + ['Radio Helsinki', 'AT', 'helsinki'], + ['RTS Couleur 3', 'CH', 'couleur 3'], + + // === GR / HU / FR / EE underground === + ['Movement', 'GR', 'movement.radio 1'], + ['Movement', 'GR', 'movement.radio 2'], + ['Tilos RΓ‘diΓ³', 'HU', 'tilos'], + ['Radio Campus Paris', 'FR', 'radio campus paris'], + + // === US college & free-form (the punk-friendly ones) === + ['WMBR 88.1', 'US', 'wmbr'], + ['WHRB 95.3', 'US', 'whrb'], + ['KALX 90.7FM Berkeley', 'US', 'kalx'], + ['WREK 91.1', 'US', 'wrek'], + ['WXYC 89.3', 'US', 'wxyc'], + ['KZSC 88.1', 'US', 'kzsc'], + ['KDVS Davis', 'US', 'kdvs'], + ['WPRB 103.3 FM', 'US', 'wprb'], + ['WZBC', 'US', 'wzbc'], + ['KFJC', null, 'kfjc'], + ['KXLU 88.9FM', 'US', 'kxlu'], + ['KCSB', 'US', 'kcsb'], + ['WLUW', 'US', 'wluw'], + ['WUSB 90.1', 'US', 'wusb'], + ['BFF.fm', 'US', 'bff.fm'], + + // === Punk / shoegaze / noise leaning === + ['DKFM Shoegaze Radio', 'CA', 'dkfm'], + ['idobi Anthm', 'US', 'anthm'], + ['idobi Howl', 'US', 'howl'], + ['Radio Free Brooklyn', 'US', 'radio free brooklyn'], + + // === AU community indie === + ['Triple R 102.7', 'AU', 'triple r'], + ['PBS 106.7FM', 'AU', 'pbs 106.7'], + ['FBi Radio', 'AU', 'fbi radio'], + ['2SER 107.3 FM', 'AU', '2ser'], + ['4zzz', 'AU', '4zzz'], + ['RTRFM', 'AU', 'rtrfm'] +]; + +function detectFormat(codec, url) { + const c = (codec || '').toLowerCase(); + if (c.includes('mp3')) return 'mp3'; + if (c.includes('aac')) return 'aac'; + if (c.includes('ogg') || c.includes('vorbis') || c.includes('opus')) return 'ogg'; + if (url?.endsWith('.m3u8')) return 'hls'; + if (url?.endsWith('.m3u')) return 'm3u'; + if (url?.endsWith('.pls')) return 'pls'; + return 'unknown'; +} + +function slugify(name) { + return name.toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, '') + .slice(0, 80); +} + +async function rbSearch(name, country) { + const params = new URLSearchParams({ + name, limit: '8', hidebroken: 'true', order: 'clickcount', reverse: 'true' + }); + if (country) params.set('countrycode', country); + const res = await fetch(`${RB}/json/stations/search?${params}`, { headers: { 'User-Agent': UA } }); + if (!res.ok) throw new Error(`RB ${res.status}`); + return res.json(); +} + +function pickBest(list, target, expectSub) { + if (!list.length) return null; + const sub = (expectSub || '').toLowerCase(); + const t = target.toLowerCase().trim(); + // 1. exact name match + const exact = list.find((s) => (s.name || '').toLowerCase().trim() === t); + if (exact) return exact; + // 2. expected substring match + if (sub) { + const subMatch = list.find((s) => (s.name || '').toLowerCase().includes(sub)); + if (subMatch) return subMatch; + } + // 3. fall back to top hit + return list[0]; +} + +function toEntry(s) { + const stream = { + url: s.url_resolved || s.url, + format: detectFormat(s.codec, s.url_resolved || s.url), + bitrate: s.bitrate || null, + label: s.codec ? `${s.codec} ${s.bitrate || ''}`.trim() : null, + priority: 0 + }; + return { + uuid: s.stationuuid, + slug: `rb-${s.stationuuid.slice(0, 8)}-${slugify(s.name).slice(0, 40)}`, + name: s.name, + category: 'underground', + country: s.countrycode || null, + homepage: s.homepage || null, + genres: (s.tags || '').split(',').map((t) => t.trim()).filter(Boolean).slice(0, 6), + description: null, + image_url: s.favicon || null, + source: 'radiobrowser', + source_ref: s.stationuuid, + streams: [stream] + }; +} + +async function main() { + const out = []; + const seenUuids = new Set(); + let okCount = 0; + let missCount = 0; + let dupCount = 0; + + for (const [name, country, expectSub] of PICKS) { + try { + const hits = await rbSearch(name, country); + const pick = pickBest(hits, name, expectSub); + if (!pick) { console.warn(' miss:', name); missCount++; continue; } + if (seenUuids.has(pick.stationuuid)) { + console.warn(' dup :', name, '->', pick.name); + dupCount++; + continue; + } + seenUuids.add(pick.stationuuid); + out.push(toEntry(pick)); + console.log(' ok :', name.padEnd(32), '->', pick.name, `(${pick.codec || '?'} ${pick.bitrate || ''})`); + okCount++; + } catch (err) { + console.warn(' err :', name, err.message); + missCount++; + } + await new Promise((r) => setTimeout(r, 80)); + } + + fs.writeFileSync(OUT, JSON.stringify(out, null, 2) + '\n', 'utf8'); + console.log(`\nwrote ${out.length} entries to ${path.relative(ROOT, OUT)}`); + console.log(` ok=${okCount} miss=${missCount} dup=${dupCount}`); +} + +main().catch((e) => { console.error(e); process.exit(1); }); diff --git a/server/scripts/report-streams.js b/server/scripts/report-streams.js index 12d66d3..631b76b 100644 --- a/server/scripts/report-streams.js +++ b/server/scripts/report-streams.js @@ -7,6 +7,6 @@ const rows = db.prepare(` ORDER BY (st.last_status = 'up'), s.name `).all(); for (const r of rows) { - const tag = r.last_status === 'up' ? 'OK ' : 'BAD'; - console.log(tag, (r.last_status || '').padEnd(14), r.format.padEnd(5), r.name, '->', r.url); + const tag = r.last_status === 'up' ? 'OK ' : 'BAD'; + console.log(tag, (r.last_status || '').padEnd(14), r.format.padEnd(5), r.name, '->', r.url); } diff --git a/server/sources/iconScraper.js b/server/sources/iconScraper.js index 4be55c3..152b23f 100644 --- a/server/sources/iconScraper.js +++ b/server/sources/iconScraper.js @@ -13,116 +13,116 @@ const MAX_HTML_BYTES = 256 * 1024; const RB_BASE = 'https://de1.api.radio-browser.info'; function withTimeout(ms) { - const ctl = new AbortController(); - const t = setTimeout(() => ctl.abort(), ms); - return { signal: ctl.signal, done: () => clearTimeout(t) }; + const ctl = new AbortController(); + const t = setTimeout(() => ctl.abort(), ms); + return { signal: ctl.signal, done: () => clearTimeout(t) }; } async function fetchText(url) { - const t = withTimeout(FETCH_TIMEOUT_MS); - try { - const res = await fetch(url, { - headers: { 'User-Agent': UA, 'Accept': 'text/html,application/xhtml+xml' }, - redirect: 'follow', - signal: t.signal - }); - if (!res.ok) return null; - const reader = res.body?.getReader(); - if (!reader) return null; - let received = 0; - const chunks = []; - while (true) { - const { done, value } = await reader.read(); - if (done) break; - received += value.length; - chunks.push(value); - if (received >= MAX_HTML_BYTES) { try { await reader.cancel(); } catch {} break; } - } - return Buffer.concat(chunks.map((c) => Buffer.from(c))).toString('utf8'); - } catch { - return null; - } finally { t.done(); } + const t = withTimeout(FETCH_TIMEOUT_MS); + try { + const res = await fetch(url, { + headers: { 'User-Agent': UA, 'Accept': 'text/html,application/xhtml+xml' }, + redirect: 'follow', + signal: t.signal + }); + if (!res.ok) return null; + const reader = res.body?.getReader(); + if (!reader) return null; + let received = 0; + const chunks = []; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + received += value.length; + chunks.push(value); + if (received >= MAX_HTML_BYTES) { try { await reader.cancel(); } catch { } break; } + } + return Buffer.concat(chunks.map((c) => Buffer.from(c))).toString('utf8'); + } catch { + return null; + } finally { t.done(); } } async function head(url) { - const t = withTimeout(FETCH_TIMEOUT_MS); - try { - const res = await fetch(url, { method: 'HEAD', headers: { 'User-Agent': UA }, signal: t.signal, redirect: 'follow' }); - return res.ok; - } catch { return false; } finally { t.done(); } + const t = withTimeout(FETCH_TIMEOUT_MS); + try { + const res = await fetch(url, { method: 'HEAD', headers: { 'User-Agent': UA }, signal: t.signal, redirect: 'follow' }); + return res.ok; + } catch { return false; } finally { t.done(); } } function abs(base, href) { - if (!href) return null; - try { return new URL(href, base).toString(); } catch { return null; } + if (!href) return null; + try { return new URL(href, base).toString(); } catch { return null; } } // Extract candidate icon URLs from raw HTML. Returns array of { href, size } sorted best-first. function parseIconCandidates(html, baseUrl) { - const out = []; - // - const linkRe = /]*?)\/?>/gi; - let m; - while ((m = linkRe.exec(html))) { - const attrs = m[1]; - const rel = (/\brel\s*=\s*["']([^"']+)["']/i.exec(attrs) || [])[1] || ''; - if (!/icon/i.test(rel)) continue; - const href = (/\bhref\s*=\s*["']([^"']+)["']/i.exec(attrs) || [])[1]; - if (!href) continue; - const sizes = (/\bsizes\s*=\s*["']([^"']+)["']/i.exec(attrs) || [])[1] || ''; - const sz = parseInt((/(\d+)x\d+/.exec(sizes) || [])[1] || '0', 10); - const apple = /apple-touch-icon/i.test(rel) ? 64 : 0; // bias: apple-touch-icons usually larger PNGs - const u = abs(baseUrl, href); - if (u) out.push({ href: u, score: sz + apple }); - } - // - const metaRe = /]*?)\/?>/gi; - while ((m = metaRe.exec(html))) { - const attrs = m[1]; - const prop = (/\b(?:property|name)\s*=\s*["']([^"']+)["']/i.exec(attrs) || [])[1] || ''; - if (!/^og:image|^twitter:image/i.test(prop)) continue; - const content = (/\bcontent\s*=\s*["']([^"']+)["']/i.exec(attrs) || [])[1]; - const u = abs(baseUrl, content); - if (u) out.push({ href: u, score: 200 }); // og:image preferred - } - out.sort((a, b) => b.score - a.score); - // de-dupe preserving order - const seen = new Set(); - return out.filter((c) => (seen.has(c.href) ? false : (seen.add(c.href), true))); + const out = []; + // + const linkRe = /]*?)\/?>/gi; + let m; + while ((m = linkRe.exec(html))) { + const attrs = m[1]; + const rel = (/\brel\s*=\s*["']([^"']+)["']/i.exec(attrs) || [])[1] || ''; + if (!/icon/i.test(rel)) continue; + const href = (/\bhref\s*=\s*["']([^"']+)["']/i.exec(attrs) || [])[1]; + if (!href) continue; + const sizes = (/\bsizes\s*=\s*["']([^"']+)["']/i.exec(attrs) || [])[1] || ''; + const sz = parseInt((/(\d+)x\d+/.exec(sizes) || [])[1] || '0', 10); + const apple = /apple-touch-icon/i.test(rel) ? 64 : 0; // bias: apple-touch-icons usually larger PNGs + const u = abs(baseUrl, href); + if (u) out.push({ href: u, score: sz + apple }); + } + // + const metaRe = /]*?)\/?>/gi; + while ((m = metaRe.exec(html))) { + const attrs = m[1]; + const prop = (/\b(?:property|name)\s*=\s*["']([^"']+)["']/i.exec(attrs) || [])[1] || ''; + if (!/^og:image|^twitter:image/i.test(prop)) continue; + const content = (/\bcontent\s*=\s*["']([^"']+)["']/i.exec(attrs) || [])[1]; + const u = abs(baseUrl, content); + if (u) out.push({ href: u, score: 200 }); // og:image preferred + } + out.sort((a, b) => b.score - a.score); + // de-dupe preserving order + const seen = new Set(); + return out.filter((c) => (seen.has(c.href) ? false : (seen.add(c.href), true))); } async function fromRadioBrowserByName(name) { - if (!name) return null; - try { - const url = `${RB_BASE}/json/stations/search?name=${encodeURIComponent(name)}&limit=3&hidebroken=true&order=clickcount&reverse=true`; - const t = withTimeout(FETCH_TIMEOUT_MS); - const res = await fetch(url, { headers: { 'User-Agent': UA }, signal: t.signal }); - t.done(); - if (!res.ok) return null; - const list = await res.json(); - const target = name.toLowerCase().trim(); - const exact = list.find((s) => (s.name || '').toLowerCase().trim() === target); - const pick = exact || list[0]; - if (pick?.favicon) return pick.favicon; - } catch {} - return null; + if (!name) return null; + try { + const url = `${RB_BASE}/json/stations/search?name=${encodeURIComponent(name)}&limit=3&hidebroken=true&order=clickcount&reverse=true`; + const t = withTimeout(FETCH_TIMEOUT_MS); + const res = await fetch(url, { headers: { 'User-Agent': UA }, signal: t.signal }); + t.done(); + if (!res.ok) return null; + const list = await res.json(); + const target = name.toLowerCase().trim(); + const exact = list.find((s) => (s.name || '').toLowerCase().trim() === target); + const pick = exact || list[0]; + if (pick?.favicon) return pick.favicon; + } catch { } + return null; } async function fromHomepage(homepage) { - if (!homepage) return null; - let base; - try { base = new URL(homepage); } catch { return null; } - const html = await fetchText(base.toString()); - if (html) { - const cands = parseIconCandidates(html, base.toString()); - for (const c of cands) { - if (await head(c.href)) return c.href; + if (!homepage) return null; + let base; + try { base = new URL(homepage); } catch { return null; } + const html = await fetchText(base.toString()); + if (html) { + const cands = parseIconCandidates(html, base.toString()); + for (const c of cands) { + if (await head(c.href)) return c.href; + } } - } - // last resort: /favicon.ico - const ico = `${base.origin}/favicon.ico`; - if (await head(ico)) return ico; - return null; + // last resort: /favicon.ico + const ico = `${base.origin}/favicon.ico`; + if (await head(ico)) return ico; + return null; } /** @@ -131,11 +131,11 @@ async function fromHomepage(homepage) { * @returns {Promise} */ export async function scrapeIcon(station) { - if (!station) return null; - // For non-RB stations, RB often still has an entry β†’ cheap win. - if (station.source !== 'radiobrowser') { - const rb = await fromRadioBrowserByName(station.name); - if (rb) return rb; - } - return fromHomepage(station.homepage); + if (!station) return null; + // For non-RB stations, RB often still has an entry β†’ cheap win. + if (station.source !== 'radiobrowser') { + const rb = await fromRadioBrowserByName(station.name); + if (rb) return rb; + } + return fromHomepage(station.homepage); } diff --git a/server/sources/radiobrowser.js b/server/sources/radiobrowser.js index 46ee937..8bb8184 100644 --- a/server/sources/radiobrowser.js +++ b/server/sources/radiobrowser.js @@ -2,64 +2,64 @@ // Docs: https://api.radio-browser.info/ const SERVERS = [ - 'https://de1.api.radio-browser.info', - 'https://nl1.api.radio-browser.info', - 'https://at1.api.radio-browser.info' + 'https://de1.api.radio-browser.info', + 'https://nl1.api.radio-browser.info', + 'https://at1.api.radio-browser.info' ]; let activeServer = SERVERS[0]; async function rb(path, params) { - const url = new URL(path, activeServer); - if (params) for (const [k, v] of Object.entries(params)) { - if (v != null) url.searchParams.set(k, String(v)); - } - const res = await fetch(url, { headers: { 'User-Agent': 'OnlineRadioExplorer/0.1' } }); - if (!res.ok) throw new Error(`Radio-Browser ${res.status}`); - return res.json(); + const url = new URL(path, activeServer); + if (params) for (const [k, v] of Object.entries(params)) { + if (v != null) url.searchParams.set(k, String(v)); + } + const res = await fetch(url, { headers: { 'User-Agent': 'OnlineRadioExplorer/0.1' } }); + if (!res.ok) throw new Error(`Radio-Browser ${res.status}`); + return res.json(); } export async function search({ name, country, tag, limit = 30 }) { - const list = await rb('/json/stations/search', { - name, country, tag, limit, hidebroken: true, order: 'votes', reverse: true - }); - return list.map(toCanonical); + const list = await rb('/json/stations/search', { + name, country, tag, limit, hidebroken: true, order: 'votes', reverse: true + }); + return list.map(toCanonical); } export async function byUuid(uuid) { - const list = await rb('/json/stations/byuuid', { uuids: uuid }); - return list[0] ? toCanonical(list[0]) : null; + const list = await rb('/json/stations/byuuid', { uuids: uuid }); + return list[0] ? toCanonical(list[0]) : null; } function detectFormat(codec, url) { - const c = (codec || '').toLowerCase(); - if (c.includes('mp3')) return 'mp3'; - if (c.includes('aac')) return 'aac'; - if (c.includes('ogg') || c.includes('vorbis') || c.includes('opus')) return 'ogg'; - if (url?.endsWith('.m3u8')) return 'hls'; - if (url?.endsWith('.m3u')) return 'm3u'; - if (url?.endsWith('.pls')) return 'pls'; - return 'unknown'; + const c = (codec || '').toLowerCase(); + if (c.includes('mp3')) return 'mp3'; + if (c.includes('aac')) return 'aac'; + if (c.includes('ogg') || c.includes('vorbis') || c.includes('opus')) return 'ogg'; + if (url?.endsWith('.m3u8')) return 'hls'; + if (url?.endsWith('.m3u')) return 'm3u'; + if (url?.endsWith('.pls')) return 'pls'; + return 'unknown'; } function toCanonical(s) { - return { - uuid: s.stationuuid || undefined, - name: s.name, - slug: `rb-${s.stationuuid}`, - homepage: s.homepage || null, - country: s.countrycode || s.country || null, - genres: (s.tags || '').split(',').map((t) => t.trim()).filter(Boolean), - description: null, - image_url: s.favicon || null, - source: 'radiobrowser', - source_ref: s.stationuuid, - streams: [{ - url: s.url_resolved || s.url, - format: detectFormat(s.codec, s.url_resolved || s.url), - bitrate: s.bitrate || null, - label: s.codec ? `${s.codec} ${s.bitrate || ''}`.trim() : null, - priority: 0 - }] - }; + return { + uuid: s.stationuuid || undefined, + name: s.name, + slug: `rb-${s.stationuuid}`, + homepage: s.homepage || null, + country: s.countrycode || s.country || null, + genres: (s.tags || '').split(',').map((t) => t.trim()).filter(Boolean), + description: null, + image_url: s.favicon || null, + source: 'radiobrowser', + source_ref: s.stationuuid, + streams: [{ + url: s.url_resolved || s.url, + format: detectFormat(s.codec, s.url_resolved || s.url), + bitrate: s.bitrate || null, + label: s.codec ? `${s.codec} ${s.bitrate || ''}`.trim() : null, + priority: 0 + }] + }; } diff --git a/server/sources/seed.js b/server/sources/seed.js index 6567191..cb2630d 100644 --- a/server/sources/seed.js +++ b/server/sources/seed.js @@ -9,43 +9,43 @@ const SEED_DIR = resolve(__dirname, '../../data/seed'); // Deterministic UUID v5-style derived from slug; stable across DB rebuilds. function uuidFromSlug(slug) { - const h = createHash('sha1').update('oradio:' + slug).digest('hex'); - return [ - h.slice(0, 8), - h.slice(8, 12), - '5' + h.slice(13, 16), - '8' + h.slice(17, 20), - h.slice(20, 32) - ].join('-'); + const h = createHash('sha1').update('oradio:' + slug).digest('hex'); + return [ + h.slice(0, 8), + h.slice(8, 12), + '5' + h.slice(13, 16), + '8' + h.slice(17, 20), + h.slice(20, 32) + ].join('-'); } function loadAllSeedFiles() { - const files = readdirSync(SEED_DIR) - .filter((f) => f.startsWith('stations') && f.endsWith('.json')) - .sort(); - const all = []; - for (const f of files) { - try { - const data = JSON.parse(readFileSync(join(SEED_DIR, f), 'utf8')); - if (Array.isArray(data)) all.push(...data); - } catch (err) { - console.warn(`[seed] failed to load ${f}:`, err.message); + const files = readdirSync(SEED_DIR) + .filter((f) => f.startsWith('stations') && f.endsWith('.json')) + .sort(); + const all = []; + for (const f of files) { + try { + const data = JSON.parse(readFileSync(join(SEED_DIR, f), 'utf8')); + if (Array.isArray(data)) all.push(...data); + } catch (err) { + console.warn(`[seed] failed to load ${f}:`, err.message); + } } - } - return all; + return all; } export function loadSeedFile() { - return loadAllSeedFiles(); + return loadAllSeedFiles(); } export function loadCategoriesFile() { - try { - const txt = readFileSync(join(SEED_DIR, 'categories.json'), 'utf8'); - return JSON.parse(txt); - } catch { - return []; - } + try { + const txt = readFileSync(join(SEED_DIR, 'categories.json'), 'utf8'); + return JSON.parse(txt); + } catch { + return []; + } } /** @@ -53,70 +53,70 @@ export function loadCategoriesFile() { * the database. Existing stations are left untouched (admin edits are preserved). */ export function applySeed() { - const db = getDb(); + const db = getDb(); - const stationByUuid = db.prepare('SELECT id FROM stations WHERE uuid = ?'); - const streamByUuid = db.prepare('SELECT id FROM streams WHERE uuid = ?'); + const stationByUuid = db.prepare('SELECT id FROM stations WHERE uuid = ?'); + const streamByUuid = db.prepare('SELECT id FROM streams WHERE uuid = ?'); - const insertStation = db.prepare(` + const insertStation = db.prepare(` INSERT INTO stations (uuid, name, slug, homepage, country, genres, description, image_url, category, source, source_ref) VALUES (@uuid, @name, @slug, @homepage, @country, @genres, @description, @image_url, @category, 'seed', @slug) `); - const insertStream = db.prepare(` + const insertStream = db.prepare(` INSERT INTO streams (uuid, station_id, url, format, bitrate, label, priority) VALUES (@uuid, @station_id, @url, @format, @bitrate, @label, @priority) `); - const entries = loadAllSeedFiles(); - let inserted = 0; - let streamsInserted = 0; - let skipped = 0; + const entries = loadAllSeedFiles(); + let inserted = 0; + let streamsInserted = 0; + let skipped = 0; - const tx = db.transaction((list) => { - for (const s of list) { - const uuid = s.uuid || uuidFromSlug(s.slug); - const existing = stationByUuid.get(uuid); - if (existing) { - skipped++; - continue; - } - const info = insertStation.run({ - uuid, - name: s.name, - slug: s.slug, - homepage: s.homepage ?? null, - country: s.country ?? null, - genres: JSON.stringify(s.genres ?? []), - description: s.description ?? null, - image_url: s.image_url ?? null, - category: s.category ?? null - }); - const stationId = info.lastInsertRowid; - let priority = 0; - for (const st of s.streams ?? []) { - const streamUuid = st.uuid || randomUUID(); - if (streamByUuid.get(streamUuid)) continue; - insertStream.run({ - uuid: streamUuid, - station_id: stationId, - url: st.url, - format: st.format ?? 'unknown', - bitrate: st.bitrate ?? null, - label: st.label ?? null, - priority: st.priority ?? priority - }); - streamsInserted++; - priority++; - } - inserted++; - } - }); - tx(entries); + const tx = db.transaction((list) => { + for (const s of list) { + const uuid = s.uuid || uuidFromSlug(s.slug); + const existing = stationByUuid.get(uuid); + if (existing) { + skipped++; + continue; + } + const info = insertStation.run({ + uuid, + name: s.name, + slug: s.slug, + homepage: s.homepage ?? null, + country: s.country ?? null, + genres: JSON.stringify(s.genres ?? []), + description: s.description ?? null, + image_url: s.image_url ?? null, + category: s.category ?? null + }); + const stationId = info.lastInsertRowid; + let priority = 0; + for (const st of s.streams ?? []) { + const streamUuid = st.uuid || randomUUID(); + if (streamByUuid.get(streamUuid)) continue; + insertStream.run({ + uuid: streamUuid, + station_id: stationId, + url: st.url, + format: st.format ?? 'unknown', + bitrate: st.bitrate ?? null, + label: st.label ?? null, + priority: st.priority ?? priority + }); + streamsInserted++; + priority++; + } + inserted++; + } + }); + tx(entries); - return { inserted, streamsInserted, skipped, total: entries.length }; + return { inserted, streamsInserted, skipped, total: entries.length }; } // Back-compat shim: bootstrap and reseed call applySeedIfEmpty(); now always merges. export function applySeedIfEmpty() { - return applySeed(); + return applySeed(); } diff --git a/server/stations.js b/server/stations.js index d765921..34336f3 100644 --- a/server/stations.js +++ b/server/stations.js @@ -2,141 +2,141 @@ import { randomUUID } from 'node:crypto'; import { getDb } from './db/index.js'; function rowToStation(row) { - if (!row) return null; - return { - id: row.id, - uuid: row.uuid, - name: row.name, - slug: row.slug, - homepage: row.homepage, - country: row.country, - genres: row.genres ? JSON.parse(row.genres) : [], - description: row.description, - image_url: row.image_url, - source: row.source, - source_ref: row.source_ref, - category: row.category, - enabled: !!row.enabled, - created_at: row.created_at, - updated_at: row.updated_at - }; + if (!row) return null; + return { + id: row.id, + uuid: row.uuid, + name: row.name, + slug: row.slug, + homepage: row.homepage, + country: row.country, + genres: row.genres ? JSON.parse(row.genres) : [], + description: row.description, + image_url: row.image_url, + source: row.source, + source_ref: row.source_ref, + category: row.category, + enabled: !!row.enabled, + created_at: row.created_at, + updated_at: row.updated_at + }; } export function listStations({ q, source, category, enabled = true } = {}) { - const db = getDb(); - const where = []; - const params = []; - if (enabled !== null) { where.push('enabled = ?'); params.push(enabled ? 1 : 0); } - if (source) { where.push('source = ?'); params.push(source); } - if (category) { where.push('category = ?'); params.push(category); } - if (q) { where.push('(name LIKE ? OR genres LIKE ? OR country LIKE ?)'); params.push(`%${q}%`, `%${q}%`, `%${q}%`); } - const sql = `SELECT * FROM stations ${where.length ? 'WHERE ' + where.join(' AND ') : ''} ORDER BY name COLLATE NOCASE`; - return db.prepare(sql).all(...params).map(rowToStation); + const db = getDb(); + const where = []; + const params = []; + if (enabled !== null) { where.push('enabled = ?'); params.push(enabled ? 1 : 0); } + if (source) { where.push('source = ?'); params.push(source); } + if (category) { where.push('category = ?'); params.push(category); } + if (q) { where.push('(name LIKE ? OR genres LIKE ? OR country LIKE ?)'); params.push(`%${q}%`, `%${q}%`, `%${q}%`); } + const sql = `SELECT * FROM stations ${where.length ? 'WHERE ' + where.join(' AND ') : ''} ORDER BY name COLLATE NOCASE`; + return db.prepare(sql).all(...params).map(rowToStation); } export function getStation(id) { - return rowToStation(getDb().prepare('SELECT * FROM stations WHERE id = ?').get(id)); + return rowToStation(getDb().prepare('SELECT * FROM stations WHERE id = ?').get(id)); } export function getStationByUuid(uuid) { - return rowToStation(getDb().prepare('SELECT * FROM stations WHERE uuid = ?').get(uuid)); + return rowToStation(getDb().prepare('SELECT * FROM stations WHERE uuid = ?').get(uuid)); } export function getStreamsForStation(stationId) { - return getDb().prepare( - 'SELECT * FROM streams WHERE station_id = ? ORDER BY priority ASC, id ASC' - ).all(stationId); + return getDb().prepare( + 'SELECT * FROM streams WHERE station_id = ? ORDER BY priority ASC, id ASC' + ).all(stationId); } export function getStreamByUuid(uuid) { - return getDb().prepare('SELECT * FROM streams WHERE uuid = ?').get(uuid); + return getDb().prepare('SELECT * FROM streams WHERE uuid = ?').get(uuid); } export function slugify(name) { - return name.toLowerCase() - .replace(/[^a-z0-9]+/g, '-') - .replace(/^-+|-+$/g, '') - .slice(0, 80) || `station-${Date.now()}`; + return name.toLowerCase() + .replace(/[^a-z0-9]+/g, '-') + .replace(/^-+|-+$/g, '') + .slice(0, 80) || `station-${Date.now()}`; } export function uniqueSlug(base) { - const db = getDb(); - let slug = base, n = 1; - while (db.prepare('SELECT 1 FROM stations WHERE slug = ?').get(slug)) { - n += 1; - slug = `${base}-${n}`; - } - return slug; + const db = getDb(); + let slug = base, n = 1; + while (db.prepare('SELECT 1 FROM stations WHERE slug = ?').get(slug)) { + n += 1; + slug = `${base}-${n}`; + } + return slug; } export function createStation(input, userId) { - const db = getDb(); - const slug = input.slug || uniqueSlug(slugify(input.name)); - const uuid = input.uuid || randomUUID(); - const info = db.prepare(` + const db = getDb(); + const slug = input.slug || uniqueSlug(slugify(input.name)); + const uuid = input.uuid || randomUUID(); + const info = db.prepare(` INSERT INTO stations (uuid, name, slug, homepage, country, genres, description, image_url, source, source_ref, category, created_by) VALUES (@uuid, @name, @slug, @homepage, @country, @genres, @description, @image_url, @source, @source_ref, @category, @created_by) `).run({ - uuid, - name: input.name, - slug, - homepage: input.homepage ?? null, - country: input.country ?? null, - genres: JSON.stringify(input.genres ?? []), - description: input.description ?? null, - image_url: input.image_url ?? null, - source: input.source ?? 'manual', - source_ref: input.source_ref ?? null, - category: input.category ?? null, - created_by: userId ?? null - }); - const id = info.lastInsertRowid; - for (const s of input.streams ?? []) { - db.prepare(`INSERT INTO streams (uuid, station_id, url, format, bitrate, label, priority) + uuid, + name: input.name, + slug, + homepage: input.homepage ?? null, + country: input.country ?? null, + genres: JSON.stringify(input.genres ?? []), + description: input.description ?? null, + image_url: input.image_url ?? null, + source: input.source ?? 'manual', + source_ref: input.source_ref ?? null, + category: input.category ?? null, + created_by: userId ?? null + }); + const id = info.lastInsertRowid; + for (const s of input.streams ?? []) { + db.prepare(`INSERT INTO streams (uuid, station_id, url, format, bitrate, label, priority) VALUES (?, ?, ?, ?, ?, ?, ?)`) - .run(s.uuid || randomUUID(), id, s.url, s.format ?? 'unknown', s.bitrate ?? null, s.label ?? null, s.priority ?? 0); - } - return getStation(id); + .run(s.uuid || randomUUID(), id, s.url, s.format ?? 'unknown', s.bitrate ?? null, s.label ?? null, s.priority ?? 0); + } + return getStation(id); } export function updateStation(id, patch) { - const db = getDb(); - const cur = getStation(id); - if (!cur) return null; - const next = { ...cur, ...patch }; - db.prepare(` + const db = getDb(); + const cur = getStation(id); + if (!cur) return null; + const next = { ...cur, ...patch }; + db.prepare(` UPDATE stations SET name=@name, homepage=@homepage, country=@country, genres=@genres, description=@description, image_url=@image_url, category=@category, enabled=@enabled, updated_at=datetime('now') WHERE id=@id `).run({ - id, - name: next.name, - homepage: next.homepage ?? null, - country: next.country ?? null, - genres: JSON.stringify(next.genres ?? []), - description: next.description ?? null, - image_url: next.image_url ?? null, - category: next.category ?? null, - enabled: next.enabled ? 1 : 0 - }); - return getStation(id); + id, + name: next.name, + homepage: next.homepage ?? null, + country: next.country ?? null, + genres: JSON.stringify(next.genres ?? []), + description: next.description ?? null, + image_url: next.image_url ?? null, + category: next.category ?? null, + enabled: next.enabled ? 1 : 0 + }); + return getStation(id); } export function deleteStation(id) { - return getDb().prepare('DELETE FROM stations WHERE id = ?').run(id).changes > 0; + return getDb().prepare('DELETE FROM stations WHERE id = ?').run(id).changes > 0; } export function addStream(stationId, s) { - const uuid = s.uuid || randomUUID(); - const info = getDb().prepare(` + const uuid = s.uuid || randomUUID(); + const info = getDb().prepare(` INSERT INTO streams (uuid, station_id, url, format, bitrate, label, priority) VALUES (?, ?, ?, ?, ?, ?, ?) `).run(uuid, stationId, s.url, s.format ?? 'unknown', s.bitrate ?? null, s.label ?? null, s.priority ?? 0); - return getDb().prepare('SELECT * FROM streams WHERE id = ?').get(info.lastInsertRowid); + return getDb().prepare('SELECT * FROM streams WHERE id = ?').get(info.lastInsertRowid); } export function deleteStream(streamId) { - return getDb().prepare('DELETE FROM streams WHERE id = ?').run(streamId).changes > 0; + return getDb().prepare('DELETE FROM streams WHERE id = ?').run(streamId).changes > 0; } diff --git a/server/stats.js b/server/stats.js new file mode 100644 index 0000000..511f586 --- /dev/null +++ b/server/stats.js @@ -0,0 +1,125 @@ +// Vote + play stats and the ranking algorithm. +// +// Score combines two signals: +// - voteZ = (up - down) / sqrt(up + down + 1) z-like, penalizes small N +// - playLog = log10(plays + 1) gentle popularity boost +// - score = voteZ + 0.5 * playLog +// +// Net effect: +// * A handful of downvotes on an obscure station sinks it hard. +// * One stray upvote on a brand new station barely moves it. +// * Popular stations float up only if they aren't being actively buried. +// * Established + positively-voted stations dominate the top. + +import { getDb } from './db/index.js'; + +export function computeScore({ up = 0, down = 0, plays = 0 } = {}) { + const n = up + down; + const voteZ = n === 0 ? 0 : (up - down) / Math.sqrt(n + 1); + const playLog = Math.log10(plays + 1); + return voteZ + 0.5 * playLog; +} + +export function getStationStats(stationId, userId = null) { + const db = getDb(); + const v = db.prepare(` + SELECT + COALESCE(SUM(CASE WHEN value = 1 THEN 1 ELSE 0 END), 0) AS up, + COALESCE(SUM(CASE WHEN value = -1 THEN 1 ELSE 0 END), 0) AS down + FROM station_votes WHERE station_id = ? + `).get(stationId) || { up: 0, down: 0 }; + const p = db.prepare('SELECT plays FROM station_plays WHERE station_id = ?').get(stationId); + const plays = p?.plays || 0; + let myVote = 0; + if (userId) { + const r = db.prepare('SELECT value FROM station_votes WHERE user_id = ? AND station_id = ?').get(userId, stationId); + myVote = r?.value || 0; + } + return { up: v.up, down: v.down, plays, myVote, score: computeScore({ up: v.up, down: v.down, plays }) }; +} + +// Bulk stats for many stations in one query. Returns a Map. +export function getStatsMap(userId = null) { + const db = getDb(); + const rows = db.prepare(` + SELECT + s.id AS station_id, + COALESCE(v.up, 0) AS up, + COALESCE(v.down, 0) AS down, + COALESCE(p.plays, 0) AS plays + FROM stations s + LEFT JOIN ( + SELECT station_id, + SUM(CASE WHEN value = 1 THEN 1 ELSE 0 END) AS up, + SUM(CASE WHEN value = -1 THEN 1 ELSE 0 END) AS down + FROM station_votes GROUP BY station_id + ) v ON v.station_id = s.id + LEFT JOIN station_plays p ON p.station_id = s.id + `).all(); + + const my = new Map(); + if (userId) { + for (const r of db.prepare('SELECT station_id, value FROM station_votes WHERE user_id = ?').all(userId)) { + my.set(r.station_id, r.value); + } + } + const out = new Map(); + for (const r of rows) { + const myVote = my.get(r.station_id) || 0; + out.set(r.station_id, { + up: r.up, down: r.down, plays: r.plays, myVote, + score: computeScore({ up: r.up, down: r.down, plays: r.plays }) + }); + } + return out; +} + +export function castVote(userId, stationId, value) { + const db = getDb(); + if (value === 0 || value == null) { + db.prepare('DELETE FROM station_votes WHERE user_id = ? AND station_id = ?').run(userId, stationId); + } else if (value === 1 || value === -1) { + db.prepare(` + INSERT INTO station_votes (user_id, station_id, value) VALUES (?, ?, ?) + ON CONFLICT(user_id, station_id) DO UPDATE SET value = excluded.value, created_at = CURRENT_TIMESTAMP + `).run(userId, stationId, value); + } else { + throw new Error('vote value must be -1, 0 or 1'); + } + return getStationStats(stationId, userId); +} + +export function recordPlay(stationId) { + getDb().prepare(` + INSERT INTO station_plays (station_id, plays, last_played_at) VALUES (?, 1, datetime('now')) + ON CONFLICT(station_id) DO UPDATE SET + plays = station_plays.plays + 1, + last_played_at = datetime('now') + `).run(stationId); +} + +// Sort helper used by routes. Mutates the array. +export function sortByMode(items, mode, statsMap) { + const s = (id) => statsMap.get(id) || { up: 0, down: 0, plays: 0, score: 0 }; + switch (mode) { + case 'hot': + items.sort((a, b) => s(b.id).score - s(a.id).score || a.name.localeCompare(b.name)); + break; + case 'top': + items.sort((a, b) => (s(b.id).up - s(b.id).down) - (s(a.id).up - s(a.id).down) || a.name.localeCompare(b.name)); + break; + case 'plays': + items.sort((a, b) => s(b.id).plays - s(a.id).plays || a.name.localeCompare(b.name)); + break; + case 'controversial': + items.sort((a, b) => { + const A = s(a.id), B = s(b.id); + return Math.min(B.up, B.down) - Math.min(A.up, A.down) || a.name.localeCompare(b.name); + }); + break; + case 'name': + default: + items.sort((a, b) => a.name.localeCompare(b.name)); + } + return items; +} diff --git a/server/streams/checker.js b/server/streams/checker.js index cdbe635..04facdb 100644 --- a/server/streams/checker.js +++ b/server/streams/checker.js @@ -5,21 +5,21 @@ import { probeStream } from './probe.js'; const probe = probeStream; export async function runHealthCheck() { - const db = getDb(); - const streams = db.prepare('SELECT id, url FROM streams').all(); - const update = db.prepare( - "UPDATE streams SET last_status = ?, last_checked_at = datetime('now') WHERE id = ?" - ); - for (const s of streams) { - const status = await probe(s.url); - update.run(status, s.id); - } - return streams.length; + const db = getDb(); + const streams = db.prepare('SELECT id, url FROM streams').all(); + const update = db.prepare( + "UPDATE streams SET last_status = ?, last_checked_at = datetime('now') WHERE id = ?" + ); + for (const s of streams) { + const status = await probe(s.url); + update.run(status, s.id); + } + return streams.length; } export function scheduleHealthCheck(expr) { - if (!expr) return null; - return cron.schedule(expr, () => { - runHealthCheck().catch((err) => console.error('[health]', err)); - }); + if (!expr) return null; + return cron.schedule(expr, () => { + runHealthCheck().catch((err) => console.error('[health]', err)); + }); } diff --git a/server/streams/probe.js b/server/streams/probe.js index ea39dc8..1949f7e 100644 --- a/server/streams/probe.js +++ b/server/streams/probe.js @@ -11,58 +11,58 @@ const TIMEOUT = 8000; const UA = 'Mozilla/5.0 OnlineRadioExplorer/0.1'; export function probeStream(rawUrl) { - return new Promise((resolve) => { - let url; - try { url = new URL(rawUrl); } catch { return resolve('err-badurl'); } + return new Promise((resolve) => { + let url; + try { url = new URL(rawUrl); } catch { return resolve('err-badurl'); } - const isTls = url.protocol === 'https:'; - const port = Number(url.port) || (isTls ? 443 : 80); - const path = (url.pathname || '/') + (url.search || ''); - const host = url.hostname; + const isTls = url.protocol === 'https:'; + const port = Number(url.port) || (isTls ? 443 : 80); + const path = (url.pathname || '/') + (url.search || ''); + const host = url.hostname; - const opts = { host, port, servername: host }; - const connect = isTls ? tls.connect : net.connect; - const sock = connect(opts); + const opts = { host, port, servername: host }; + const connect = isTls ? tls.connect : net.connect; + const sock = connect(opts); - let settled = false; - const finish = (status) => { - if (settled) return; - settled = true; - try { sock.destroy(); } catch {} - resolve(status); - }; + let settled = false; + const finish = (status) => { + if (settled) return; + settled = true; + try { sock.destroy(); } catch { } + resolve(status); + }; - sock.setTimeout(TIMEOUT); - sock.on('timeout', () => finish('err-timeout')); - sock.on('error', () => finish('err-fetch')); + sock.setTimeout(TIMEOUT); + sock.on('timeout', () => finish('err-timeout')); + sock.on('error', () => finish('err-fetch')); - sock.on('connect', () => { - const req = - `GET ${path} HTTP/1.0\r\n` + - `Host: ${host}\r\n` + - `User-Agent: ${UA}\r\n` + - `Icy-MetaData: 1\r\n` + - `Accept: */*\r\n` + - `Connection: close\r\n\r\n`; - sock.write(req); + sock.on('connect', () => { + const req = + `GET ${path} HTTP/1.0\r\n` + + `Host: ${host}\r\n` + + `User-Agent: ${UA}\r\n` + + `Icy-MetaData: 1\r\n` + + `Accept: */*\r\n` + + `Connection: close\r\n\r\n`; + sock.write(req); + }); + + let buf = ''; + sock.on('data', (chunk) => { + buf += chunk.toString('latin1'); + const eol = buf.indexOf('\n'); + if (eol < 0) return; + const statusLine = buf.slice(0, eol).trim(); + // Accept: HTTP/1.x 2xx, ICY 2xx, SOURCE 2xx + const m = statusLine.match(/^(?:HTTP\/\d\.\d|ICY|SOURCE)\s+(\d{3})/i); + if (!m) return finish(`bad-${statusLine.slice(0, 16)}`); + const code = Number(m[1]); + if (code >= 200 && code < 400) finish('up'); + else finish(`http-${code}`); + }); + + sock.on('end', () => { + if (!settled) finish(buf ? 'err-empty' : 'err-fetch'); + }); }); - - let buf = ''; - sock.on('data', (chunk) => { - buf += chunk.toString('latin1'); - const eol = buf.indexOf('\n'); - if (eol < 0) return; - const statusLine = buf.slice(0, eol).trim(); - // Accept: HTTP/1.x 2xx, ICY 2xx, SOURCE 2xx - const m = statusLine.match(/^(?:HTTP\/\d\.\d|ICY|SOURCE)\s+(\d{3})/i); - if (!m) return finish(`bad-${statusLine.slice(0, 16)}`); - const code = Number(m[1]); - if (code >= 200 && code < 400) finish('up'); - else finish(`http-${code}`); - }); - - sock.on('end', () => { - if (!settled) finish(buf ? 'err-empty' : 'err-fetch'); - }); - }); } diff --git a/server/streams/resolver.js b/server/streams/resolver.js index b5f2751..2e00452 100644 --- a/server/streams/resolver.js +++ b/server/streams/resolver.js @@ -2,39 +2,39 @@ // HLS (.m3u8) is left as-is so hls.js can fetch it. export function detectFormatFromUrl(url) { - const u = url.toLowerCase().split('?')[0]; - if (u.endsWith('.m3u8')) return 'hls'; - if (u.endsWith('.m3u')) return 'm3u'; - if (u.endsWith('.pls')) return 'pls'; - if (u.endsWith('.aac')) return 'aac'; - if (u.endsWith('.mp3')) return 'mp3'; - if (u.endsWith('.ogg') || u.endsWith('.opus')) return 'ogg'; - return 'unknown'; + const u = url.toLowerCase().split('?')[0]; + if (u.endsWith('.m3u8')) return 'hls'; + if (u.endsWith('.m3u')) return 'm3u'; + if (u.endsWith('.pls')) return 'pls'; + if (u.endsWith('.aac')) return 'aac'; + if (u.endsWith('.mp3')) return 'mp3'; + if (u.endsWith('.ogg') || u.endsWith('.opus')) return 'ogg'; + return 'unknown'; } function parsePls(text) { - const m = text.match(/^File\d+\s*=\s*(.+)$/im); - return m ? m[1].trim() : null; + const m = text.match(/^File\d+\s*=\s*(.+)$/im); + return m ? m[1].trim() : null; } function parseM3u(text) { - const lines = text.split(/\r?\n/).map((l) => l.trim()).filter(Boolean); - return lines.find((l) => !l.startsWith('#')) || null; + const lines = text.split(/\r?\n/).map((l) => l.trim()).filter(Boolean); + return lines.find((l) => !l.startsWith('#')) || null; } export async function resolveStream({ url, format }) { - const fmt = format && format !== 'unknown' ? format : detectFormatFromUrl(url); - if (fmt === 'pls' || fmt === 'm3u') { - try { - const res = await fetch(url, { redirect: 'follow' }); - if (!res.ok) throw new Error(`HTTP ${res.status}`); - const text = await res.text(); - const direct = fmt === 'pls' ? parsePls(text) : parseM3u(text); - if (!direct) throw new Error('No direct URL found in playlist'); - return { url: direct, format: detectFormatFromUrl(direct) }; - } catch (err) { - return { url, format: fmt, error: String(err.message || err) }; + const fmt = format && format !== 'unknown' ? format : detectFormatFromUrl(url); + if (fmt === 'pls' || fmt === 'm3u') { + try { + const res = await fetch(url, { redirect: 'follow' }); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + const text = await res.text(); + const direct = fmt === 'pls' ? parsePls(text) : parseM3u(text); + if (!direct) throw new Error('No direct URL found in playlist'); + return { url: direct, format: detectFormatFromUrl(direct) }; + } catch (err) { + return { url, format: fmt, error: String(err.message || err) }; + } } - } - return { url, format: fmt }; + return { url, format: fmt }; } diff --git a/server/ws.js b/server/ws.js index a8a8115..8261378 100644 --- a/server/ws.js +++ b/server/ws.js @@ -5,52 +5,52 @@ import { getUserBySession, readSessionToken } from './auth.js'; const channels = new Map(); // userId -> Set export function attachWs(server) { - const wss = new WebSocketServer({ noServer: true }); + const wss = new WebSocketServer({ noServer: true }); - server.on('upgrade', (req, socket, head) => { - if (!req.url.startsWith('/ws')) return socket.destroy(); - const token = readSessionToken(req); - const user = getUserBySession(token); - if (!user) { - socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n'); - socket.destroy(); - return; - } - wss.handleUpgrade(req, socket, head, (ws) => { - ws.user = user; - addClient(user.id, ws); - ws.on('close', () => removeClient(user.id, ws)); - ws.on('message', (raw) => { - let msg; - try { msg = JSON.parse(raw.toString()); } catch { return; } - // Re-broadcast every message to all connections of the same user. - // (e.g. phone sends `{type:"command", action:"play", stationId:7}` β†’ kiosk receives) - broadcastToUser(user.id, msg, ws); - }); - ws.send(JSON.stringify({ type: 'hello', user: { id: user.id, username: user.username, role: user.role } })); + server.on('upgrade', (req, socket, head) => { + if (!req.url.startsWith('/ws')) return socket.destroy(); + const token = readSessionToken(req); + const user = getUserBySession(token); + if (!user) { + socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n'); + socket.destroy(); + return; + } + wss.handleUpgrade(req, socket, head, (ws) => { + ws.user = user; + addClient(user.id, ws); + ws.on('close', () => removeClient(user.id, ws)); + ws.on('message', (raw) => { + let msg; + try { msg = JSON.parse(raw.toString()); } catch { return; } + // Re-broadcast every message to all connections of the same user. + // (e.g. phone sends `{type:"command", action:"play", stationId:7}` β†’ kiosk receives) + broadcastToUser(user.id, msg, ws); + }); + ws.send(JSON.stringify({ type: 'hello', user: { id: user.id, username: user.username, role: user.role } })); + }); }); - }); - return wss; + return wss; } function addClient(userId, ws) { - if (!channels.has(userId)) channels.set(userId, new Set()); - channels.get(userId).add(ws); + if (!channels.has(userId)) channels.set(userId, new Set()); + channels.get(userId).add(ws); } function removeClient(userId, ws) { - const set = channels.get(userId); - if (!set) return; - set.delete(ws); - if (!set.size) channels.delete(userId); + const set = channels.get(userId); + if (!set) return; + set.delete(ws); + if (!set.size) channels.delete(userId); } export function broadcastToUser(userId, msg, except) { - const set = channels.get(userId); - if (!set) return; - const payload = JSON.stringify(msg); - for (const ws of set) { - if (ws === except) continue; - if (ws.readyState === ws.OPEN) ws.send(payload); - } + const set = channels.get(userId); + if (!set) return; + const payload = JSON.stringify(msg); + for (const ws of set) { + if (ws === except) continue; + if (ws.readyState === ws.OPEN) ws.send(payload); + } } diff --git a/vite.config.js b/vite.config.js index 291d5b3..9f7d0c9 100644 --- a/vite.config.js +++ b/vite.config.js @@ -2,23 +2,24 @@ import { defineConfig } from 'vite'; import { resolve } from 'node:path'; export default defineConfig({ - root: 'web', - publicDir: false, - server: { - port: 5173, - proxy: { - '/api': 'http://localhost:4173', - '/ws': { target: 'ws://localhost:4173', ws: true } + root: 'web', + publicDir: false, + server: { + port: 5173, + proxy: { + '/api': 'http://localhost:4173', + '/ws': { target: 'ws://localhost:4173', ws: true } + } + }, + build: { + outDir: '../server/public', + emptyOutDir: true, + rollupOptions: { + input: { + kiosk: resolve(__dirname, 'web/index.html'), + admin: resolve(__dirname, 'web/admin/index.html'), + docs: resolve(__dirname, 'web/docs/index.html') + } + } } - }, - build: { - outDir: '../server/public', - emptyOutDir: true, - rollupOptions: { - input: { - kiosk: resolve(__dirname, 'web/index.html'), - admin: resolve(__dirname, 'web/admin/index.html') - } - } - } }); diff --git a/web/admin/index.html b/web/admin/index.html index 8d28b66..f0ece89 100644 --- a/web/admin/index.html +++ b/web/admin/index.html @@ -1,13 +1,16 @@ - + + Radio Admin - - + + +
- - + + + \ No newline at end of file diff --git a/web/admin/main.js b/web/admin/main.js index 97dc3a8..c38bf9f 100644 --- a/web/admin/main.js +++ b/web/admin/main.js @@ -5,289 +5,313 @@ const app = document.getElementById('app'); const state = { user: null, view: 'stations', stations: [], users: [], system: null, search: '' }; async function bootstrap() { - try { state.user = await api.get('/api/auth/me'); } - catch { return showLogin(); } - if (state.user.role !== 'admin') { - app.innerHTML = ``; - return; - } - await refresh(); - render(); + try { state.user = await api.get('/api/auth/me'); } + catch { return showLogin(); } + if (state.user.role !== 'admin') { + app.innerHTML = ``; + return; + } + await refresh(); + render(); } async function refresh() { - const tasks = [api.get('/api/stations?all=1')]; - if (state.view === 'users') tasks.push(api.get('/api/auth/users')); - if (state.view === 'system') tasks.push(api.get('/api/admin/system')); - const [stations, more1, more2] = await Promise.all(tasks); - state.stations = stations; - if (state.view === 'users') state.users = more1 || []; - if (state.view === 'system') state.system = more1 || more2 || null; + const tasks = [api.get('/api/stations?all=1')]; + if (state.view === 'users') tasks.push(api.get('/api/auth/users')); + if (state.view === 'system') tasks.push(api.get('/api/admin/system')); + const [stations, more1, more2] = await Promise.all(tasks); + state.stations = stations; + if (state.view === 'users') state.users = more1 || []; + if (state.view === 'system') state.system = more1 || more2 || null; } function showLogin() { - clear(app); - app.appendChild(el('div', { class: 'login' }, - el('form', { onSubmit: async (e) => { - e.preventDefault(); - const fd = new FormData(e.target); - try { - state.user = await api.post('/api/auth/login', { username: fd.get('username'), password: fd.get('password') }); - await bootstrap(); - } catch (err) { e.target.querySelector('.err').textContent = err.message; } - } }, - el('h1', {}, 'Admin sign in'), - el('input', { name: 'username', placeholder: 'Username', required: true }), - el('input', { name: 'password', type: 'password', placeholder: 'Password', required: true }), - el('div', { class: 'err' }), - el('button', { class: 'btn primary', type: 'submit' }, 'Sign in') - ))); + clear(app); + app.appendChild(el('div', { class: 'login' }, + el('form', { + onSubmit: async (e) => { + e.preventDefault(); + const fd = new FormData(e.target); + try { + state.user = await api.post('/api/auth/login', { username: fd.get('username'), password: fd.get('password') }); + await bootstrap(); + } catch (err) { e.target.querySelector('.err').textContent = err.message; } + } + }, + el('h1', {}, 'Admin sign in'), + el('input', { name: 'username', placeholder: 'Username', required: true }), + el('input', { name: 'password', type: 'password', placeholder: 'Password', required: true }), + el('div', { class: 'err' }), + el('button', { class: 'btn primary', type: 'submit' }, 'Sign in') + ))); } function render() { - clear(app); - const side = el('aside', { class: 'side' }, - el('h1', {}, 'Online Radio Explorer'), - ...['stations', 'import', 'users', 'system'].map((v) => - el('button', { class: `nav ${state.view === v ? 'active' : ''}`, - onClick: async () => { state.view = v; await refresh(); render(); } }, label(v))), - el('div', { class: 'me' }, `Signed in as ${state.user.username}`, - el('br'), - el('a', { href: '#', onClick: async (e) => { e.preventDefault(); await api.post('/api/auth/logout'); location.reload(); } }, 'Sign out')) - ); - const main = el('main', { class: 'main' }); - if (state.view === 'stations') renderStations(main); - else if (state.view === 'import') renderImport(main); - else if (state.view === 'users') renderUsers(main); - else if (state.view === 'system') renderSystem(main); - app.appendChild(el('div', { class: 'shell' }, side, main)); + clear(app); + const side = el('aside', { class: 'side' }, + el('h1', {}, 'Online Radio Explorer'), + ...['stations', 'import', 'users', 'system'].map((v) => + el('button', { + class: `nav ${state.view === v ? 'active' : ''}`, + onClick: async () => { state.view = v; await refresh(); render(); } + }, label(v))), + el('div', { class: 'me' }, `Signed in as ${state.user.username}`, + el('br'), + el('a', { href: '#', onClick: async (e) => { e.preventDefault(); await api.post('/api/auth/logout'); location.reload(); } }, 'Sign out')) + ); + const main = el('main', { class: 'main' }); + if (state.view === 'stations') renderStations(main); + else if (state.view === 'import') renderImport(main); + else if (state.view === 'users') renderUsers(main); + else if (state.view === 'system') renderSystem(main); + app.appendChild(el('div', { class: 'shell' }, side, main)); } function label(v) { - return ({ stations: 'Stations', import: 'Import', users: 'Users', system: 'System' })[v]; + return ({ stations: 'Stations', import: 'Import', users: 'Users', system: 'System' })[v]; } // ---------- Stations ---------- function renderStations(root) { - root.appendChild(el('div', { class: 'bar' }, - el('input', { placeholder: 'Search…', value: state.search, - onInput: (e) => { state.search = e.target.value; renderStationsTable(); } }), - el('button', { class: 'btn primary', onClick: () => openStationDialog() }, '+ Add station'), - el('button', { class: 'btn', onClick: async () => { await api.post('/api/admin/health-check'); alert('Health check finished'); await refresh(); render(); } }, 'Run health check') - )); - const tableWrap = el('div', { id: 'tableWrap' }); - root.appendChild(tableWrap); - renderStationsTable(); + root.appendChild(el('div', { class: 'bar' }, + el('input', { + placeholder: 'Search…', value: state.search, + onInput: (e) => { state.search = e.target.value; renderStationsTable(); } + }), + el('button', { class: 'btn primary', onClick: () => openStationDialog() }, '+ Add station'), + el('button', { class: 'btn', onClick: async () => { await api.post('/api/admin/health-check'); alert('Health check finished'); await refresh(); render(); } }, 'Run health check') + )); + const tableWrap = el('div', { id: 'tableWrap' }); + root.appendChild(tableWrap); + renderStationsTable(); } function renderStationsTable() { - const wrap = document.getElementById('tableWrap'); - if (!wrap) return; - clear(wrap); - const q = state.search.toLowerCase(); - const filtered = state.stations.filter((s) => - !q || s.name.toLowerCase().includes(q) || (s.country || '').toLowerCase().includes(q) || - (s.genres || []).some((g) => g.toLowerCase().includes(q)) - ); - const table = el('table', {}, - el('thead', {}, el('tr', {}, - el('th', {}, 'Name'), el('th', {}, 'Source'), el('th', {}, 'Genres'), - el('th', {}, 'Country'), el('th', {}, 'Enabled'), el('th', {}, 'Actions'))), - el('tbody', {}, ...filtered.map((s) => el('tr', {}, - el('td', {}, el('strong', {}, s.name), el('br'), el('small', {}, s.homepage || '')), - el('td', {}, s.source), - el('td', {}, ...(s.genres || []).slice(0, 4).map((g) => el('span', { class: 'tag' }, g))), - el('td', {}, s.country || ''), - el('td', {}, s.enabled ? 'βœ…' : 'β›”'), - el('td', {}, - el('button', { class: 'btn', onClick: () => openStationDialog(s.id) }, 'Edit'), - ' ', - el('button', { class: 'btn danger', onClick: async () => { - if (confirm(`Delete ${s.name}?`)) { await api.del(`/api/stations/${s.id}`); await refresh(); render(); } - } }, 'Delete') - ) - ))) - ); - wrap.appendChild(table); + const wrap = document.getElementById('tableWrap'); + if (!wrap) return; + clear(wrap); + const q = state.search.toLowerCase(); + const filtered = state.stations.filter((s) => + !q || s.name.toLowerCase().includes(q) || (s.country || '').toLowerCase().includes(q) || + (s.genres || []).some((g) => g.toLowerCase().includes(q)) + ); + const table = el('table', {}, + el('thead', {}, el('tr', {}, + el('th', {}, 'Name'), el('th', {}, 'Source'), el('th', {}, 'Genres'), + el('th', {}, 'Country'), el('th', {}, 'Enabled'), el('th', {}, 'Actions'))), + el('tbody', {}, ...filtered.map((s) => el('tr', {}, + el('td', {}, el('strong', {}, s.name), el('br'), el('small', {}, s.homepage || '')), + el('td', {}, s.source), + el('td', {}, ...(s.genres || []).slice(0, 4).map((g) => el('span', { class: 'tag' }, g))), + el('td', {}, s.country || ''), + el('td', {}, s.enabled ? 'βœ…' : 'β›”'), + el('td', {}, + el('button', { class: 'btn', onClick: () => openStationDialog(s.id) }, 'Edit'), + ' ', + el('button', { + class: 'btn danger', onClick: async () => { + if (confirm(`Delete ${s.name}?`)) { await api.del(`/api/stations/${s.id}`); await refresh(); render(); } + } + }, 'Delete') + ) + ))) + ); + wrap.appendChild(table); } async function openStationDialog(id) { - const station = id ? await api.get(`/api/stations/${id}`) : { name: '', genres: [], streams: [], enabled: true }; - const dlg = el('dialog'); - const streamsBox = el('div', { class: 'streams' }); + const station = id ? await api.get(`/api/stations/${id}`) : { name: '', genres: [], streams: [], enabled: true }; + const dlg = el('dialog'); + const streamsBox = el('div', { class: 'streams' }); - function paintStreams() { - clear(streamsBox); - streamsBox.appendChild(el('div', { style: { fontWeight: 600, marginBottom: '6px' } }, 'Streams')); - if (!station.streams?.length) streamsBox.appendChild(el('div', { style: { color: '#6b7280' } }, 'No streams yet.')); - for (const s of station.streams || []) { - streamsBox.appendChild(el('div', { class: 'stream-row' }, - el('select', { onChange: (e) => s.format = e.target.value }, - ...['mp3','aac','hls','m3u','pls','ogg','unknown'].map((f) => - el('option', { value: f, selected: s.format === f }, f))), - el('input', { value: s.url, placeholder: 'https://…', onInput: (e) => s.url = e.target.value }), - el('input', { type: 'number', placeholder: 'kbps', value: s.bitrate || '', onInput: (e) => s.bitrate = Number(e.target.value) || null }), - el('input', { value: s.label || '', placeholder: 'Label', onInput: (e) => s.label = e.target.value }), - s.last_status ? el('span', { class: `pill ${s.last_status === 'up' ? 'up' : 'down'}` }, s.last_status) : el('span'), - el('button', { class: 'btn danger', type: 'button', onClick: () => { station.streams = station.streams.filter((x) => x !== s); paintStreams(); } }, 'Γ—') - )); + function paintStreams() { + clear(streamsBox); + streamsBox.appendChild(el('div', { style: { fontWeight: 600, marginBottom: '6px' } }, 'Streams')); + if (!station.streams?.length) streamsBox.appendChild(el('div', { style: { color: '#6b7280' } }, 'No streams yet.')); + for (const s of station.streams || []) { + streamsBox.appendChild(el('div', { class: 'stream-row' }, + el('select', { onChange: (e) => s.format = e.target.value }, + ...['mp3', 'aac', 'hls', 'm3u', 'pls', 'ogg', 'unknown'].map((f) => + el('option', { value: f, selected: s.format === f }, f))), + el('input', { value: s.url, placeholder: 'https://…', onInput: (e) => s.url = e.target.value }), + el('input', { type: 'number', placeholder: 'kbps', value: s.bitrate || '', onInput: (e) => s.bitrate = Number(e.target.value) || null }), + el('input', { value: s.label || '', placeholder: 'Label', onInput: (e) => s.label = e.target.value }), + s.last_status ? el('span', { class: `pill ${s.last_status === 'up' ? 'up' : 'down'}` }, s.last_status) : el('span'), + el('button', { class: 'btn danger', type: 'button', onClick: () => { station.streams = station.streams.filter((x) => x !== s); paintStreams(); } }, 'Γ—') + )); + } + streamsBox.appendChild(el('button', { + class: 'btn', type: 'button', onClick: () => { + station.streams = [...(station.streams || []), { url: '', format: 'mp3', priority: (station.streams?.length || 0) }]; + paintStreams(); + } + }, '+ Add stream')); } - streamsBox.appendChild(el('button', { class: 'btn', type: 'button', onClick: () => { - station.streams = [...(station.streams || []), { url: '', format: 'mp3', priority: (station.streams?.length || 0) }]; - paintStreams(); - } }, '+ Add stream')); - } - const form = el('form', { method: 'dialog', onSubmit: async (e) => { - e.preventDefault(); - const payload = { - name: station.name, homepage: station.homepage, country: station.country, - genres: station.genres, description: station.description, image_url: station.image_url, - enabled: station.enabled - }; - if (id) { - await api.patch(`/api/stations/${id}`, payload); - // sync streams: simple approach β€” delete all & re-add - const fresh = await api.get(`/api/stations/${id}`); - for (const s of fresh.streams || []) await api.del(`/api/stations/${id}/streams/${s.id}`); - for (const s of station.streams || []) if (s.url) await api.post(`/api/stations/${id}/streams`, s); - } else { - payload.streams = (station.streams || []).filter((s) => s.url); - await api.post('/api/stations', payload); - } - dlg.close(); - await refresh(); - render(); - } }, - el('h2', {}, id ? 'Edit station' : 'Add station'), - el('div', { class: 'row' }, el('label', {}, 'Name'), el('input', { value: station.name, onInput: (e) => station.name = e.target.value, required: true })), - el('div', { class: 'row' }, el('label', {}, 'Homepage'), el('input', { value: station.homepage || '', onInput: (e) => station.homepage = e.target.value })), - el('div', { class: 'row' }, el('label', {}, 'Country'), el('input', { value: station.country || '', maxlength: 4, onInput: (e) => station.country = e.target.value })), - el('div', { class: 'row' }, el('label', {}, 'Genres'), el('input', { value: (station.genres || []).join(', '), onInput: (e) => station.genres = e.target.value.split(',').map((s) => s.trim()).filter(Boolean) })), - el('div', { class: 'row' }, el('label', {}, 'Image URL'),el('input', { value: station.image_url || '', onInput: (e) => station.image_url = e.target.value })), - el('div', { class: 'row col' }, el('textarea', { rows: 2, placeholder: 'Description', onInput: (e) => station.description = e.target.value }, station.description || '')), - el('div', { class: 'row' }, el('label', {}, 'Enabled'), el('input', { type: 'checkbox', checked: station.enabled, onChange: (e) => station.enabled = e.target.checked })), - streamsBox, - el('div', { class: 'actions' }, - el('button', { class: 'btn', type: 'button', onClick: () => dlg.close() }, 'Cancel'), - el('button', { class: 'btn primary', type: 'submit' }, 'Save')) - ); - paintStreams(); - dlg.appendChild(form); - document.body.appendChild(dlg); - dlg.showModal(); - dlg.addEventListener('close', () => dlg.remove()); + const form = el('form', { + method: 'dialog', onSubmit: async (e) => { + e.preventDefault(); + const payload = { + name: station.name, homepage: station.homepage, country: station.country, + genres: station.genres, description: station.description, image_url: station.image_url, + enabled: station.enabled + }; + if (id) { + await api.patch(`/api/stations/${id}`, payload); + // sync streams: simple approach β€” delete all & re-add + const fresh = await api.get(`/api/stations/${id}`); + for (const s of fresh.streams || []) await api.del(`/api/stations/${id}/streams/${s.id}`); + for (const s of station.streams || []) if (s.url) await api.post(`/api/stations/${id}/streams`, s); + } else { + payload.streams = (station.streams || []).filter((s) => s.url); + await api.post('/api/stations', payload); + } + dlg.close(); + await refresh(); + render(); + } + }, + el('h2', {}, id ? 'Edit station' : 'Add station'), + el('div', { class: 'row' }, el('label', {}, 'Name'), el('input', { value: station.name, onInput: (e) => station.name = e.target.value, required: true })), + el('div', { class: 'row' }, el('label', {}, 'Homepage'), el('input', { value: station.homepage || '', onInput: (e) => station.homepage = e.target.value })), + el('div', { class: 'row' }, el('label', {}, 'Country'), el('input', { value: station.country || '', maxlength: 4, onInput: (e) => station.country = e.target.value })), + el('div', { class: 'row' }, el('label', {}, 'Genres'), el('input', { value: (station.genres || []).join(', '), onInput: (e) => station.genres = e.target.value.split(',').map((s) => s.trim()).filter(Boolean) })), + el('div', { class: 'row' }, el('label', {}, 'Image URL'), el('input', { value: station.image_url || '', onInput: (e) => station.image_url = e.target.value })), + el('div', { class: 'row col' }, el('textarea', { rows: 2, placeholder: 'Description', onInput: (e) => station.description = e.target.value }, station.description || '')), + el('div', { class: 'row' }, el('label', {}, 'Enabled'), el('input', { type: 'checkbox', checked: station.enabled, onChange: (e) => station.enabled = e.target.checked })), + streamsBox, + el('div', { class: 'actions' }, + el('button', { class: 'btn', type: 'button', onClick: () => dlg.close() }, 'Cancel'), + el('button', { class: 'btn primary', type: 'submit' }, 'Save')) + ); + paintStreams(); + dlg.appendChild(form); + document.body.appendChild(dlg); + dlg.showModal(); + dlg.addEventListener('close', () => dlg.remove()); } // ---------- Import (Radio-Browser) ---------- function renderImport(root) { - let results = []; - const resultsBox = el('div'); - root.appendChild(el('h2', {}, 'Import from Radio-Browser')); - root.appendChild(el('div', { class: 'bar' }, - el('input', { id: 'rbq', placeholder: 'Search by name…' }), - el('input', { id: 'rbcountry', placeholder: 'Country (e.g. NL)', style: { minWidth: '120px' } }), - el('input', { id: 'rbtag', placeholder: 'Tag/genre' }), - el('button', { class: 'btn primary', onClick: async () => { - const params = new URLSearchParams({ - q: document.getElementById('rbq').value, - country: document.getElementById('rbcountry').value, - tag: document.getElementById('rbtag').value - }); - results = await api.get(`/api/stations/sources/radiobrowser/search?${params}`); - paint(); - } }, 'Search') - )); - root.appendChild(resultsBox); - function paint() { - clear(resultsBox); - if (!results.length) { resultsBox.appendChild(el('p', {}, 'No results yet.')); return; } - const table = el('table', {}, - el('thead', {}, el('tr', {}, el('th', {}, 'Name'), el('th', {}, 'Country'), el('th', {}, 'Tags'), el('th', {}, 'Stream'), el('th', {}, ''))), - el('tbody', {}, ...results.map((s) => el('tr', {}, - el('td', {}, s.name), - el('td', {}, s.country || ''), - el('td', {}, ...(s.genres || []).slice(0, 4).map((g) => el('span', { class: 'tag' }, g))), - el('td', {}, el('small', {}, (s.streams[0]?.format || '') + ' ' + (s.streams[0]?.bitrate || ''))), - el('td', {}, el('button', { class: 'btn primary', onClick: async () => { - await api.post('/api/stations/sources/radiobrowser/import', s); - alert(`Imported ${s.name}`); - } }, 'Import')) - ))) - ); - resultsBox.appendChild(table); - } + let results = []; + const resultsBox = el('div'); + root.appendChild(el('h2', {}, 'Import from Radio-Browser')); + root.appendChild(el('div', { class: 'bar' }, + el('input', { id: 'rbq', placeholder: 'Search by name…' }), + el('input', { id: 'rbcountry', placeholder: 'Country (e.g. NL)', style: { minWidth: '120px' } }), + el('input', { id: 'rbtag', placeholder: 'Tag/genre' }), + el('button', { + class: 'btn primary', onClick: async () => { + const params = new URLSearchParams({ + q: document.getElementById('rbq').value, + country: document.getElementById('rbcountry').value, + tag: document.getElementById('rbtag').value + }); + results = await api.get(`/api/stations/sources/radiobrowser/search?${params}`); + paint(); + } + }, 'Search') + )); + root.appendChild(resultsBox); + function paint() { + clear(resultsBox); + if (!results.length) { resultsBox.appendChild(el('p', {}, 'No results yet.')); return; } + const table = el('table', {}, + el('thead', {}, el('tr', {}, el('th', {}, 'Name'), el('th', {}, 'Country'), el('th', {}, 'Tags'), el('th', {}, 'Stream'), el('th', {}, ''))), + el('tbody', {}, ...results.map((s) => el('tr', {}, + el('td', {}, s.name), + el('td', {}, s.country || ''), + el('td', {}, ...(s.genres || []).slice(0, 4).map((g) => el('span', { class: 'tag' }, g))), + el('td', {}, el('small', {}, (s.streams[0]?.format || '') + ' ' + (s.streams[0]?.bitrate || ''))), + el('td', {}, el('button', { + class: 'btn primary', onClick: async () => { + await api.post('/api/stations/sources/radiobrowser/import', s); + alert(`Imported ${s.name}`); + } + }, 'Import')) + ))) + ); + resultsBox.appendChild(table); + } } // ---------- Users ---------- function renderUsers(root) { - root.appendChild(el('div', { class: 'bar' }, - el('h2', { style: { margin: 0, flex: 1 } }, 'Users'), - el('button', { class: 'btn primary', onClick: openUserDialog }, '+ Add user') - )); - root.appendChild(el('table', {}, - el('thead', {}, el('tr', {}, el('th', {}, 'Username'), el('th', {}, 'Role'), el('th', {}, 'Created'), el('th', {}, ''))), - el('tbody', {}, ...state.users.map((u) => el('tr', {}, - el('td', {}, u.username), - el('td', {}, u.role), - el('td', {}, u.created_at), - el('td', {}, - el('button', { class: 'btn', onClick: async () => { - const pw = prompt(`New password for ${u.username}:`); - if (pw) { await api.patch(`/api/auth/users/${u.id}`, { password: pw }); alert('Updated'); } - } }, 'Reset PW'), - ' ', - el('button', { class: 'btn', onClick: async () => { - const r = u.role === 'admin' ? 'user' : 'admin'; - await api.patch(`/api/auth/users/${u.id}`, { role: r }); - await refresh(); render(); - } }, 'Toggle role'), - ' ', - u.id !== state.user.id ? el('button', { class: 'btn danger', onClick: async () => { - if (confirm(`Delete ${u.username}?`)) { await api.del(`/api/auth/users/${u.id}`); await refresh(); render(); } - } }, 'Delete') : null - ) - ))) - )); + root.appendChild(el('div', { class: 'bar' }, + el('h2', { style: { margin: 0, flex: 1 } }, 'Users'), + el('button', { class: 'btn primary', onClick: openUserDialog }, '+ Add user') + )); + root.appendChild(el('table', {}, + el('thead', {}, el('tr', {}, el('th', {}, 'Username'), el('th', {}, 'Role'), el('th', {}, 'Created'), el('th', {}, ''))), + el('tbody', {}, ...state.users.map((u) => el('tr', {}, + el('td', {}, u.username), + el('td', {}, u.role), + el('td', {}, u.created_at), + el('td', {}, + el('button', { + class: 'btn', onClick: async () => { + const pw = prompt(`New password for ${u.username}:`); + if (pw) { await api.patch(`/api/auth/users/${u.id}`, { password: pw }); alert('Updated'); } + } + }, 'Reset PW'), + ' ', + el('button', { + class: 'btn', onClick: async () => { + const r = u.role === 'admin' ? 'user' : 'admin'; + await api.patch(`/api/auth/users/${u.id}`, { role: r }); + await refresh(); render(); + } + }, 'Toggle role'), + ' ', + u.id !== state.user.id ? el('button', { + class: 'btn danger', onClick: async () => { + if (confirm(`Delete ${u.username}?`)) { await api.del(`/api/auth/users/${u.id}`); await refresh(); render(); } + } + }, 'Delete') : null + ) + ))) + )); } function openUserDialog() { - const dlg = el('dialog'); - dlg.appendChild(el('form', { method: 'dialog', onSubmit: async (e) => { - e.preventDefault(); - const fd = new FormData(e.target); - await api.post('/api/auth/users', { - username: fd.get('username'), password: fd.get('password'), role: fd.get('role') - }); - dlg.close(); - await refresh(); render(); - } }, - el('h2', {}, 'New user'), - el('div', { class: 'row' }, el('label', {}, 'Username'), el('input', { name: 'username', required: true })), - el('div', { class: 'row' }, el('label', {}, 'Password'), el('input', { name: 'password', type: 'password', required: true })), - el('div', { class: 'row' }, el('label', {}, 'Role'), - el('select', { name: 'role' }, el('option', { value: 'user' }, 'user'), el('option', { value: 'admin' }, 'admin'))), - el('div', { class: 'actions' }, - el('button', { class: 'btn', type: 'button', onClick: () => dlg.close() }, 'Cancel'), - el('button', { class: 'btn primary', type: 'submit' }, 'Create')) - )); - document.body.appendChild(dlg); - dlg.showModal(); - dlg.addEventListener('close', () => dlg.remove()); + const dlg = el('dialog'); + dlg.appendChild(el('form', { + method: 'dialog', onSubmit: async (e) => { + e.preventDefault(); + const fd = new FormData(e.target); + await api.post('/api/auth/users', { + username: fd.get('username'), password: fd.get('password'), role: fd.get('role') + }); + dlg.close(); + await refresh(); render(); + } + }, + el('h2', {}, 'New user'), + el('div', { class: 'row' }, el('label', {}, 'Username'), el('input', { name: 'username', required: true })), + el('div', { class: 'row' }, el('label', {}, 'Password'), el('input', { name: 'password', type: 'password', required: true })), + el('div', { class: 'row' }, el('label', {}, 'Role'), + el('select', { name: 'role' }, el('option', { value: 'user' }, 'user'), el('option', { value: 'admin' }, 'admin'))), + el('div', { class: 'actions' }, + el('button', { class: 'btn', type: 'button', onClick: () => dlg.close() }, 'Cancel'), + el('button', { class: 'btn primary', type: 'submit' }, 'Create')) + )); + document.body.appendChild(dlg); + dlg.showModal(); + dlg.addEventListener('close', () => dlg.remove()); } // ---------- System ---------- function renderSystem(root) { - const s = state.system || {}; - root.appendChild(el('h2', {}, 'System')); - root.appendChild(el('div', { class: 'system-grid' }, - stat('Stations', s.stations), stat('Streams', s.streams), stat('Users', s.users), - stat('Favorites', s.favorites), stat('Node', s.node), stat('Uptime (s)', s.uptime_s) - )); - root.appendChild(el('div', { class: 'bar', style: { marginTop: '16px' } }, - el('button', { class: 'btn', onClick: async () => { await api.post('/api/admin/health-check'); alert('Health check finished'); await refresh(); render(); } }, 'Run health check'), - el('button', { class: 'btn', onClick: async () => { const r = await api.post('/api/admin/reseed'); alert(JSON.stringify(r)); } }, 'Reseed (if empty)') - )); + const s = state.system || {}; + root.appendChild(el('h2', {}, 'System')); + root.appendChild(el('div', { class: 'system-grid' }, + stat('Stations', s.stations), stat('Streams', s.streams), stat('Users', s.users), + stat('Favorites', s.favorites), stat('Node', s.node), stat('Uptime (s)', s.uptime_s) + )); + root.appendChild(el('div', { class: 'bar', style: { marginTop: '16px' } }, + el('button', { class: 'btn', onClick: async () => { await api.post('/api/admin/health-check'); alert('Health check finished'); await refresh(); render(); } }, 'Run health check'), + el('button', { class: 'btn', onClick: async () => { const r = await api.post('/api/admin/reseed'); alert(JSON.stringify(r)); } }, 'Reseed (if empty)') + )); } function stat(k, v) { return el('div', { class: 'stat' }, el('div', { class: 'k' }, k), el('div', { class: 'v' }, v ?? 'β€”')); } diff --git a/web/docs/index.html b/web/docs/index.html new file mode 100644 index 0000000..ddc9904 --- /dev/null +++ b/web/docs/index.html @@ -0,0 +1,23 @@ + + + + + + + oradio Β· API reference + + + + +
+
+ ← Kiosk +

oradio API

+ +
+
+
+ + + + diff --git a/web/docs/main.js b/web/docs/main.js new file mode 100644 index 0000000..2bb73a1 --- /dev/null +++ b/web/docs/main.js @@ -0,0 +1,241 @@ +// Minimal, dependency-free API reference page for oradio. +// Lists every public endpoint, lets the user fire a live request and inspect +// the response. Intended as a reference companion to the kiosk. + +const BASE = `${location.origin}/api/v1`; +const INTERNAL = `${location.origin}/api`; +document.getElementById('base').textContent = BASE; + +const endpoints = [ + { + group: 'Public (v1)', + items: [ + { + id: 'health', + method: 'GET', + path: '/health', + summary: 'Service heartbeat plus enabled-station count.', + tryable: true + }, + { + id: 'categories', + method: 'GET', + path: '/categories', + summary: 'All categories with their station counts.', + tryable: true + }, + { + id: 'stations-list', + method: 'GET', + path: '/stations', + summary: 'Paginated station list. Filterable and sortable.', + params: [ + { name: 'q', desc: 'Substring filter on name / genres / country.' }, + { name: 'category', desc: 'Category id (see /categories).' }, + { name: 'country', desc: 'ISO country code, case-insensitive.' }, + { name: 'genre', desc: 'Substring match against any genre.' }, + { name: 'sort', desc: 'hot | top | plays | controversial | name (default: name).' }, + { name: 'limit', desc: 'Max items returned (default 200, cap 1000).' } + ], + tryable: true, + tryQuery: 'limit=3&sort=hot' + }, + { + id: 'random', + method: 'GET', + path: '/stations/random', + summary: 'Pick one random enabled station. Same filters as /stations. Pass redirect=stream for a 302 to the resolved audio URL.', + params: [ + { name: 'category', desc: 'Restrict pool to a category.' }, + { name: 'country', desc: 'Restrict pool to a country.' }, + { name: 'genre', desc: 'Restrict pool by genre substring.' }, + { name: 'redirect', desc: 'Set to "stream" to 302-redirect to the resolved stream URL.' } + ], + tryable: true, + examples: [ + `mpv ${BASE}/stations/random?redirect=stream`, + `curl -sLI "${BASE}/stations/random?redirect=stream" | grep -i location` + ] + }, + { + id: 'station', + method: 'GET', + path: '/stations/{uuid}', + summary: 'Full detail for one station, including its streams.', + params: [{ name: 'uuid', desc: 'Station UUID (see list response).' }] + }, + { + id: 'station-stream', + method: 'GET', + path: '/stations/{uuid}/stream', + summary: '302-redirect to the resolved stream URL. Picks the highest-priority stream that was last seen up.', + params: [ + { name: 'uuid', desc: 'Station UUID.' }, + { name: 'format', desc: 'Optional preferred format (mp3, aac, ogg, hls).' } + ] + }, + { + id: 'stream-by-uuid', + method: 'GET', + path: '/stations/{uuid}/streams/{streamUuid}', + summary: 'Resolve and 302 to a specific stream. Pass redirect=0 to return JSON metadata instead.', + params: [ + { name: 'uuid', desc: 'Station UUID.' }, + { name: 'streamUuid', desc: 'Stream UUID.' }, + { name: 'redirect', desc: 'Set to "0" to return JSON instead of redirecting.' } + ] + } + ] + }, + { + group: 'Authenticated (cookie session)', + items: [ + { + id: 'me', + method: 'GET', + path: '/auth/me', + base: INTERNAL, + summary: 'Current signed-in user, or 401.', + tryable: true + }, + { + id: 'favorites', + method: 'GET', + path: '/me/favorites', + base: INTERNAL, + summary: 'Your favorites, ordered.', + tryable: true + }, + { + id: 'favorites-random', + method: 'GET', + path: '/me/favorites/random', + base: INTERNAL, + summary: 'One random favorite β€” used by the kiosk dice button in "favorites" mode.', + tryable: true + }, + { + id: 'history', + method: 'GET', + path: '/me/history', + base: INTERNAL, + summary: 'Recent play history (last 50).', + tryable: true + } + ] + }, + { + group: 'Rate limit', + items: [ + { + id: 'rate', + method: 'INFO', + path: '120 req / minute / IP', + summary: 'Public /api/v1 endpoints share a per-IP token bucket. Headers X-RateLimit-Limit and X-RateLimit-Remaining tell you where you stand.' + } + ] + } +]; + +const app = document.getElementById('app'); + +function el(tag, attrs, ...children) { + const n = document.createElement(tag); + if (attrs) { + for (const [k, v] of Object.entries(attrs)) { + if (v == null || v === false) continue; + if (k === 'class') n.className = v; + else if (k.startsWith('on') && typeof v === 'function') n.addEventListener(k.slice(2).toLowerCase(), v); + else n.setAttribute(k, v); + } + } + for (const c of children) { + if (c == null || c === false) continue; + n.appendChild(typeof c === 'string' ? document.createTextNode(c) : c); + } + return n; +} + +function methodChip(m) { + return el('span', { class: `m m-${m.toLowerCase()}` }, m); +} + +function renderEndpoint(ep) { + const base = ep.base || BASE; + const fullUrl = `${base}${ep.path}`; + const card = el('article', { class: 'ep', id: ep.id }); + card.appendChild(el('header', { class: 'ep-head' }, + methodChip(ep.method), + el('code', { class: 'ep-path' }, fullUrl) + )); + card.appendChild(el('p', { class: 'ep-sum' }, ep.summary)); + + if (ep.params?.length) { + const tbl = el('table', { class: 'params' }, + el('thead', {}, el('tr', {}, el('th', {}, 'Parameter'), el('th', {}, 'Description'))), + el('tbody', {}, ...ep.params.map((p) => + el('tr', {}, el('td', {}, el('code', {}, p.name)), el('td', {}, p.desc)) + )) + ); + card.appendChild(tbl); + } + + if (ep.examples?.length) { + card.appendChild(el('div', { class: 'examples' }, + el('div', { class: 'examples-h' }, 'Examples'), + ...ep.examples.map((e) => el('pre', {}, el('code', {}, e))) + )); + } + + if (ep.tryable && ep.method === 'GET') { + const out = el('pre', { class: 'try-out' }, 'Click "Try it" to send a live request.'); + const queryInput = el('input', { + class: 'try-q', + type: 'text', + placeholder: '?key=value (optional)', + value: ep.tryQuery ? `?${ep.tryQuery}` : '' + }); + const button = el('button', { + class: 'try-btn', + onClick: async () => { + button.disabled = true; + button.textContent = '…'; + let q = queryInput.value.trim(); + if (q && !q.startsWith('?')) q = `?${q}`; + const url = `${fullUrl}${q}`; + const t0 = performance.now(); + try { + const res = await fetch(url, { credentials: 'same-origin', redirect: 'manual' }); + const ms = Math.round(performance.now() - t0); + let body; + if (res.type === 'opaqueredirect' || (res.status >= 300 && res.status < 400)) { + body = `(redirect β€” open in new tab to follow)`; + } else { + const ct = res.headers.get('content-type') || ''; + body = ct.includes('json') + ? JSON.stringify(await res.json(), null, 2) + : (await res.text()).slice(0, 4000); + } + out.textContent = `${res.status} ${res.statusText || ''} Β· ${ms} ms\n${url}\n\n${body}`; + } catch (err) { + out.textContent = `error: ${err.message || err}\n${url}`; + } finally { + button.disabled = false; + button.textContent = 'Try it'; + } + } + }, 'Try it'); + const openLink = el('a', { class: 'try-open', target: '_blank', rel: 'noopener', href: fullUrl }, 'Open β†—'); + card.appendChild(el('div', { class: 'try' }, + el('div', { class: 'try-row' }, queryInput, button, openLink), + out + )); + } + + return card; +} + +for (const group of endpoints) { + app.appendChild(el('h2', { class: 'group' }, group.group)); + for (const ep of group.items) app.appendChild(renderEndpoint(ep)); +} diff --git a/web/docs/style.css b/web/docs/style.css new file mode 100644 index 0000000..bde60c8 --- /dev/null +++ b/web/docs/style.css @@ -0,0 +1,171 @@ +:root { + --bg-0: #07080b; + --bg-1: #0e1116; + --bg-2: #161a22; + --bg-3: #1f242e; + --line: #262b36; + --fg: #e9ecf2; + --muted: #8a90a0; + --muted-2: #5d6373; + --accent: #ff7a3d; + --accent-2: #ffb37a; + --good: #4ec9a6; + --bad: #ec6a6a; + --info: #6ab7ff; + font-family: "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif; + color-scheme: dark; +} + +* { box-sizing: border-box; } +html, body { margin: 0; padding: 0; background: var(--bg-0); color: var(--fg); } +a { color: var(--accent-2); text-decoration: none; } +a:hover { text-decoration: underline; } + +.docs-header { + position: sticky; top: 0; z-index: 10; + background: rgba(7, 8, 11, 0.92); + border-bottom: 1px solid var(--line); + backdrop-filter: blur(8px); +} +.docs-header-inner { + max-width: 980px; margin: 0 auto; + padding: 14px 20px; + display: flex; align-items: center; gap: 16px; +} +.docs-header h1 { + margin: 0; font-size: 18px; letter-spacing: -0.01em; +} +.docs-header .back { + color: var(--muted); font-size: 13px; + padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px; +} +.docs-header .back:hover { color: var(--fg); background: var(--bg-2); text-decoration: none; } +.docs-header .base { + margin-left: auto; + font-family: ui-monospace, "SF Mono", Menlo, monospace; + font-size: 12px; color: var(--muted); + padding: 4px 10px; background: var(--bg-2); + border: 1px solid var(--line); border-radius: 8px; +} + +#app { + max-width: 980px; margin: 0 auto; + padding: 24px 20px 80px; +} +h2.group { + margin: 32px 0 12px; + font-size: 13px; text-transform: uppercase; letter-spacing: 0.1em; + color: var(--muted); +} +h2.group:first-child { margin-top: 0; } + +.ep { + background: var(--bg-1); + border: 1px solid var(--line); + border-radius: 12px; + padding: 16px; + margin-bottom: 14px; +} +.ep-head { + display: flex; align-items: center; gap: 10px; + flex-wrap: wrap; +} +.ep-path { + font-family: ui-monospace, "SF Mono", Menlo, monospace; + font-size: 13px; color: var(--fg); + background: var(--bg-2); + padding: 4px 10px; border-radius: 6px; + border: 1px solid var(--line); + overflow-wrap: anywhere; +} +.ep-sum { + margin: 10px 0 0; color: var(--muted); + font-size: 14px; line-height: 1.5; +} + +.m { + display: inline-block; + font-size: 11px; font-weight: 800; letter-spacing: 0.06em; + padding: 4px 8px; border-radius: 6px; + color: #07080b; +} +.m-get { background: var(--good); } +.m-post { background: var(--accent); } +.m-put { background: #d9b14a; } +.m-delete { background: var(--bad); color: #fff; } +.m-info { background: var(--info); } + +.params { + width: 100%; border-collapse: collapse; + margin-top: 14px; + font-size: 13px; +} +.params th { + text-align: left; font-weight: 600; color: var(--muted); + text-transform: uppercase; font-size: 11px; letter-spacing: 0.06em; + padding: 8px 10px; border-bottom: 1px solid var(--line); +} +.params td { + padding: 8px 10px; border-bottom: 1px solid var(--line); + color: var(--fg); +} +.params td:first-child { width: 160px; } +.params code { + font-family: ui-monospace, "SF Mono", Menlo, monospace; + font-size: 12px; color: var(--accent-2); + background: var(--bg-2); + padding: 2px 6px; border-radius: 4px; +} + +.examples { margin-top: 14px; } +.examples-h { + font-size: 11px; color: var(--muted); + text-transform: uppercase; letter-spacing: 0.06em; + margin-bottom: 6px; +} +.examples pre { + margin: 0 0 8px; padding: 10px 12px; + background: var(--bg-0); border: 1px solid var(--line); + border-radius: 8px; + font-family: ui-monospace, "SF Mono", Menlo, monospace; + font-size: 12.5px; color: var(--accent-2); + overflow-x: auto; +} + +.try { margin-top: 14px; } +.try-row { + display: flex; gap: 8px; align-items: center; + margin-bottom: 8px; +} +.try-q { + flex: 1; + background: var(--bg-2); color: var(--fg); + border: 1px solid var(--line); border-radius: 8px; + padding: 8px 12px; font-size: 13px; + font-family: ui-monospace, "SF Mono", Menlo, monospace; + outline: none; +} +.try-q:focus { border-color: var(--accent); } +.try-btn { + background: var(--accent); color: #1a0a00; + border: 0; border-radius: 8px; + padding: 8px 16px; font-size: 13px; font-weight: 700; + cursor: pointer; + font-family: inherit; +} +.try-btn:hover { background: #ff8a55; } +.try-btn:disabled { opacity: 0.6; cursor: default; } +.try-open { + color: var(--muted); font-size: 12px; + padding: 6px 10px; border: 1px solid var(--line); border-radius: 8px; +} +.try-open:hover { color: var(--fg); background: var(--bg-2); text-decoration: none; } +.try-out { + margin: 0; padding: 12px; + background: var(--bg-0); border: 1px solid var(--line); + border-radius: 8px; + font-family: ui-monospace, "SF Mono", Menlo, monospace; + font-size: 12px; color: var(--fg); + max-height: 320px; overflow: auto; + white-space: pre-wrap; word-break: break-word; +} diff --git a/web/index.html b/web/index.html index 68828c4..7c9d0c7 100644 --- a/web/index.html +++ b/web/index.html @@ -1,13 +1,16 @@ - + + Radio Kiosk - - + + +
- - + + + \ No newline at end of file diff --git a/web/main.js b/web/main.js index adf9cb3..3278a70 100644 --- a/web/main.js +++ b/web/main.js @@ -13,7 +13,9 @@ const state = { favorites: [], history: [], query: '', - player: { stationId: null, stationName: null, genres: [], playing: false, loading: false, volume: 0.7 } + sort: 'hot', // hot | top | plays | name | controversial β€” applied in Browse + randomMode: localStorage.getItem('oradio.randomMode') === 'favorites' ? 'favorites' : 'all', + player: { stationId: null, stationName: null, genres: [], playing: false, loading: false, volume: 0.7, votes: null } }; const player = new Player({ @@ -39,7 +41,7 @@ async function bootstrap() { async function refreshAll() { const [stations, favs, history, categories] = await Promise.all([ - api.get('/api/stations'), + api.get(`/api/stations?sort=${encodeURIComponent(state.sort)}`), api.get('/api/me/favorites').catch(() => []), api.get('/api/me/history').catch(() => []), api.get('/api/v1/categories').catch(() => []) @@ -50,11 +52,15 @@ async function refreshAll() { state.categories = categories; } +async function refreshStations() { + state.stations = await api.get(`/api/stations?sort=${encodeURIComponent(state.sort)}`); +} + function handleWs(msg) { if (msg.type === 'command') { if (msg.action === 'play' && msg.stationId) { const st = state.stations.find((s) => s.id === msg.stationId); - if (st) player.play(st); + if (st) playStation(st); } else if (msg.action === 'pause') player.togglePause(); else if (msg.action === 'volume') player.setVolume(msg.value); else if (msg.action === 'stop') player.stop(); @@ -99,6 +105,7 @@ function render() { const p = state.player; const favIds = new Set(state.favorites.map((f) => f.id)); + const v = p.votes; // { up, down, plays, myVote, score } or null const now = el('section', { class: 'now' }, el('div', { class: 'meta' }, @@ -108,10 +115,26 @@ function render() { el('div', { class: 'tags' }, ...(p.genres || []).slice(0, 4).map((g) => el('span', { class: 'tag' }, g))) ), el('div', { class: 'controls' }, + el('div', { class: 'vote-group', title: 'Vote on current station' }, + el('button', { + class: `vote up ${v?.myVote === 1 ? 'on' : ''}`, + disabled: !p.stationId, + title: 'Upvote', + onClick: () => votePlayer(1) + }, el('span', { class: 'vote-icon' }, 'β–²'), + el('span', { class: 'vote-count' }, String(v?.up ?? 0))), + el('button', { + class: `vote down ${v?.myVote === -1 ? 'on' : ''}`, + disabled: !p.stationId, + title: 'Downvote', + onClick: () => votePlayer(-1) + }, el('span', { class: 'vote-icon' }, 'β–Ό'), + el('span', { class: 'vote-count' }, String(v?.down ?? 0))) + ), el('button', { class: `btn-play ${p.loading ? 'loading' : ''}`, title: p.playing ? 'Pause' : 'Play', - onClick: () => p.stationId ? player.togglePause() : (state.favorites[0] && player.play(state.favorites[0])) + onClick: () => p.stationId ? player.togglePause() : (state.favorites[0] && playStation(state.favorites[0])) }, p.playing ? '❚❚' : 'β–Ά'), el('button', { class: 'btn-stop', @@ -143,10 +166,36 @@ function render() { ) ), el('div', { class: 'header-tools' }, + state.tab === 'browse' + ? el('select', { + class: 'sort', + title: 'Sort browse list', + onChange: (e) => { state.sort = e.target.value; savedGridScroll = 0; refreshStations().then(render); } + }, + el('option', { value: 'hot', selected: state.sort === 'hot' }, 'πŸ”₯ Hot (smart)'), + el('option', { value: 'top', selected: state.sort === 'top' }, 'β–² Top voted'), + el('option', { value: 'plays', selected: state.sort === 'plays' }, 'β–Ά Most played'), + el('option', { value: 'controversial', selected: state.sort === 'controversial' }, '⚑ Controversial'), + el('option', { value: 'name', selected: state.sort === 'name' }, 'A β†’ Z') + ) + : null, el('input', { class: 'search', type: 'search', placeholder: 'Search…', value: state.query, onInput: (e) => { state.query = e.target.value; renderGrid(); } }), + el('button', { + class: 'btn-random', + title: `Play random station (mode: ${state.randomMode}). Right-click to switch mode.`, + onClick: playRandom, + onContextMenu: (e) => { e.preventDefault(); toggleRandomMode(); } + }, + el('span', { class: 'rand-icon' }, '🎲'), + el('span', { class: 'rand-mode' }, state.randomMode === 'favorites' ? 'β˜…' : 'All') + ), + el('a', { + class: 'btn-docs', href: '/docs/', target: '_blank', rel: 'noopener', + title: 'Open API reference' + }, 'API'), isAdmin ? el('button', { class: 'btn-add', title: 'Add station', onClick: openAddStation }, '+') : null ) ); @@ -222,11 +271,14 @@ function paintGrid(grid, favIds) { } const p = state.player; for (const s of items) { + const score = typeof s.score === 'number' ? s.score : 0; + const net = (s.up ?? 0) - (s.down ?? 0); + const badgeClass = net > 0 ? 'pos' : net < 0 ? 'neg' : 'neu'; const card = el('div', { class: `card ${p.stationId === s.id ? 'playing' : ''}`, role: 'button', tabindex: 0, - onClick: () => { player.play(s); recordHistory(s.id); }, + onClick: () => playStation(s), onContextMenu: (e) => { e.preventDefault(); openContextMenu(e.clientX, e.clientY, s); } }, el('div', { class: 'art' }, @@ -249,6 +301,9 @@ function paintGrid(grid, favIds) { el('div', { class: 'g' }, (s.genres || []).slice(0, 3).join(' Β· ') || (s.country || 'β€”')) ), + el('div', { class: `score-badge ${badgeClass}`, title: `β–²${s.up ?? 0} Β· β–Ό${s.down ?? 0} Β· β–Ά${s.plays ?? 0} Β· score ${score.toFixed(2)}` }, + net > 0 ? `+${net}` : String(net) + ), el('button', { class: `fav ${favIds.has(s.id) ? 'on' : ''}`, title: favIds.has(s.id) ? 'Remove favorite' : 'Add favorite', @@ -276,35 +331,135 @@ async function toggleFavorite(station) { render(); } +function toggleRandomMode() { + state.randomMode = state.randomMode === 'favorites' ? 'all' : 'favorites'; + localStorage.setItem('oradio.randomMode', state.randomMode); + toast(`Random mode: ${state.randomMode === 'favorites' ? 'favorites only' : 'all stations'}`); + render(); +} + +// Pick a random station and play it. Uses the public /api/v1/stations/random +// for "all" mode, /api/me/favorites/random for "favorites" mode. Falls back +// to a local random pick if the network call fails. +async function playRandom() { + try { + const ep = state.randomMode === 'favorites' + ? '/api/me/favorites/random' + : '/api/v1/stations/random'; + const remote = await api.get(ep); + // The kiosk player needs the internal numeric id (used by /resolve etc.). + // The favorites endpoint returns it directly; the v1 endpoint does not, + // so resolve via the cached station list (or skip if missing). + let station = remote; + if (station.id == null) { + station = state.stations.find((s) => s.uuid === remote.uuid) || null; + } + if (!station) { toast('Random station not in cache'); return; } + playStation(station); + } catch (err) { + // Fallback: pick locally so the button still does something offline. + const pool = state.randomMode === 'favorites' ? state.favorites : state.stations; + if (!pool.length) { toast(err.message || 'No stations available'); return; } + playStation(pool[Math.floor(Math.random() * pool.length)]); + } +} + function recordHistory(stationId) { // Server-side history insertion can be added later; for now, optimistic local insert. state.history.unshift({ station_id: stationId, started_at: new Date().toISOString() }); } +// Play wrapper: starts playback, pings server play counter, fetches vote state +// so the up/down buttons in the now-playing bar reflect the current station. +async function playStation(station) { + state.player.votes = null; + player.play(station); + recordHistory(station.id); + try { + const stats = await api.post(`/api/stations/${station.id}/play`); + // Only apply if user hasn't switched stations in the meantime. + if (state.player.stationId === station.id) { + state.player.votes = stats; + // Refresh listing stats in the background so the score badge updates. + mergeStats(station.id, stats); + render(); + } + } catch (err) { + try { + const stats = await api.get(`/api/stations/${station.id}/votes`); + if (state.player.stationId === station.id) { + state.player.votes = stats; + mergeStats(station.id, stats); + render(); + } + } catch { /* ignore */ } + } +} + +async function votePlayer(value) { + const id = state.player.stationId; + if (!id) return; + // Toggle off when clicking the already-active button. + const cur = state.player.votes?.myVote || 0; + const next = cur === value ? 0 : value; + try { + const stats = await api.post(`/api/stations/${id}/vote`, { value: next }); + state.player.votes = stats; + mergeStats(id, stats); + render(); + } catch (err) { + toast(err.message || 'Vote failed'); + } +} + +function mergeStats(stationId, stats) { + const list = [state.stations, state.favorites]; + for (const arr of list) { + const hit = arr.find((s) => s.id === stationId); + if (hit) { + hit.up = stats.up; hit.down = stats.down; + hit.plays = stats.plays; hit.score = stats.score; + hit.my_vote = stats.myVote; + } + } +} + // ---- API endpoints context menu ---- let menuEl = null; function closeContextMenu() { if (menuEl) { menuEl.remove(); menuEl = null; } } function apiEndpoints(s) { - if (!s.uuid) return []; - const base = `${location.origin}/api/v1`; - return [ - { label: 'Station detail', url: `${base}/stations/${s.uuid}` }, - { label: 'Stream redirect', url: `${base}/stations/${s.uuid}/stream` }, - { label: 'MP3 stream', url: `${base}/stations/${s.uuid}/stream?format=mp3` }, - { label: 'AAC stream', url: `${base}/stations/${s.uuid}/stream?format=aac` }, - { label: 'HLS stream', url: `${base}/stations/${s.uuid}/stream?format=hls` }, + const origin = location.origin; + const base = `${origin}/api/v1`; + const items = []; + // Original (internal) endpoint β€” always available, keyed by station id. + if (s.id != null) { + items.push({ label: 'Station (original)', url: `${origin}/api/stations/${s.id}` }); + } + // Public v1 endpoints β€” require uuid. + if (s.uuid) { + items.push( + { label: 'Station detail', url: `${base}/stations/${s.uuid}` }, + { label: 'Stream redirect', url: `${base}/stations/${s.uuid}/stream` }, + { label: 'MP3 stream', url: `${base}/stations/${s.uuid}/stream?format=mp3` }, + { label: 'AAC stream', url: `${base}/stations/${s.uuid}/stream?format=aac` }, + { label: 'HLS stream', url: `${base}/stations/${s.uuid}/stream?format=hls` } + ); + } + items.push( { label: 'All stations', url: `${base}/stations` }, { label: 'Health', url: `${base}/health` } - ]; + ); + return items; } function openContextMenu(x, y, station) { closeContextMenu(); const items = apiEndpoints(station); menuEl = el('div', { class: 'ctx-menu', role: 'menu' }, el('div', { class: 'ctx-title' }, station.name), - el('div', { class: 'ctx-sub' }, station.uuid ? `uuid Β· ${station.uuid}` : 'no uuid'), + el('div', { class: 'ctx-sub' }, + station.uuid ? `uuid Β· ${station.uuid}` : (station.id != null ? `id Β· ${station.id} (no uuid β€” public v1 hidden)` : 'no identifier')), ...(items.length ? items.map((it) => el('div', { class: 'ctx-row' }, el('div', { class: 'ctx-row-text' }, el('div', { class: 'ctx-label' }, it.label), diff --git a/web/shared/api.js b/web/shared/api.js index e18d488..ed5302d 100644 --- a/web/shared/api.js +++ b/web/shared/api.js @@ -1,21 +1,21 @@ async function http(method, path, body) { - const res = await fetch(path, { - method, - credentials: 'same-origin', - headers: body ? { 'Content-Type': 'application/json' } : {}, - body: body ? JSON.stringify(body) : undefined - }); - if (res.status === 204) return null; - const ct = res.headers.get('content-type') || ''; - const data = ct.includes('json') ? await res.json() : await res.text(); - if (!res.ok) throw Object.assign(new Error(data?.error || res.statusText), { status: res.status, data }); - return data; + const res = await fetch(path, { + method, + credentials: 'same-origin', + headers: body ? { 'Content-Type': 'application/json' } : {}, + body: body ? JSON.stringify(body) : undefined + }); + if (res.status === 204) return null; + const ct = res.headers.get('content-type') || ''; + const data = ct.includes('json') ? await res.json() : await res.text(); + if (!res.ok) throw Object.assign(new Error(data?.error || res.statusText), { status: res.status, data }); + return data; } export const api = { - get: (p) => http('GET', p), - post: (p, b) => http('POST', p, b), - put: (p, b) => http('PUT', p, b), - patch: (p, b) => http('PATCH', p, b), - del: (p) => http('DELETE', p) + get: (p) => http('GET', p), + post: (p, b) => http('POST', p, b), + put: (p, b) => http('PUT', p, b), + patch: (p, b) => http('PATCH', p, b), + del: (p) => http('DELETE', p) }; diff --git a/web/shared/dom.js b/web/shared/dom.js index 3ae733e..2996ec8 100644 --- a/web/shared/dom.js +++ b/web/shared/dom.js @@ -1,17 +1,17 @@ export function el(tag, props = {}, ...children) { - const node = document.createElement(tag); - for (const [k, v] of Object.entries(props || {})) { - if (k === 'class') node.className = v; - else if (k === 'style' && typeof v === 'object') Object.assign(node.style, v); - else if (k.startsWith('on') && typeof v === 'function') node.addEventListener(k.slice(2).toLowerCase(), v); - else if (k === 'html') node.innerHTML = v; - else if (v !== false && v != null) node.setAttribute(k, v === true ? '' : v); - } - for (const c of children.flat()) { - if (c == null || c === false) continue; - node.appendChild(c instanceof Node ? c : document.createTextNode(String(c))); - } - return node; + const node = document.createElement(tag); + for (const [k, v] of Object.entries(props || {})) { + if (k === 'class') node.className = v; + else if (k === 'style' && typeof v === 'object') Object.assign(node.style, v); + else if (k.startsWith('on') && typeof v === 'function') node.addEventListener(k.slice(2).toLowerCase(), v); + else if (k === 'html') node.innerHTML = v; + else if (v !== false && v != null) node.setAttribute(k, v === true ? '' : v); + } + for (const c of children.flat()) { + if (c == null || c === false) continue; + node.appendChild(c instanceof Node ? c : document.createTextNode(String(c))); + } + return node; } export function clear(node) { while (node.firstChild) node.removeChild(node.firstChild); } diff --git a/web/shared/ws.js b/web/shared/ws.js index e00f98d..0d93b07 100644 --- a/web/shared/ws.js +++ b/web/shared/ws.js @@ -1,22 +1,22 @@ export function connectWs(onMessage) { - let ws, retry = 0, closed = false; - function open() { - const proto = location.protocol === 'https:' ? 'wss' : 'ws'; - ws = new WebSocket(`${proto}://${location.host}/ws`); - ws.addEventListener('open', () => { retry = 0; }); - ws.addEventListener('message', (ev) => { - try { onMessage(JSON.parse(ev.data)); } catch {} - }); - ws.addEventListener('close', () => { - if (closed) return; - retry = Math.min(retry + 1, 6); - setTimeout(open, 500 * 2 ** retry); - }); - ws.addEventListener('error', () => ws.close()); - } - open(); - return { - send(msg) { if (ws?.readyState === WebSocket.OPEN) ws.send(JSON.stringify(msg)); }, - close() { closed = true; ws?.close(); } - }; + let ws, retry = 0, closed = false; + function open() { + const proto = location.protocol === 'https:' ? 'wss' : 'ws'; + ws = new WebSocket(`${proto}://${location.host}/ws`); + ws.addEventListener('open', () => { retry = 0; }); + ws.addEventListener('message', (ev) => { + try { onMessage(JSON.parse(ev.data)); } catch { } + }); + ws.addEventListener('close', () => { + if (closed) return; + retry = Math.min(retry + 1, 6); + setTimeout(open, 500 * 2 ** retry); + }); + ws.addEventListener('error', () => ws.close()); + } + open(); + return { + send(msg) { if (ws?.readyState === WebSocket.OPEN) ws.send(JSON.stringify(msg)); }, + close() { closed = true; ws?.close(); } + }; } diff --git a/web/style.css b/web/style.css index b6b783d..0ed43f1 100644 --- a/web/style.css +++ b/web/style.css @@ -189,6 +189,37 @@ input, select, textarea { font: inherit; color: inherit; } .btn-add:hover { background: #ff8a55; } .btn-add:active { transform: scale(0.94); } +.btn-random { + display: flex; align-items: center; gap: 6px; + height: 36px; padding: 0 12px; + border-radius: 999px; + background: var(--bg-2); color: var(--fg); + border: 1px solid var(--line); + font-size: 13px; font-weight: 600; + transition: background 120ms, border-color 120ms, transform 80ms; +} +.btn-random:hover { background: var(--bg-3); border-color: rgba(255,122,61,0.4); } +.btn-random:active { transform: scale(0.96); } +.btn-random .rand-icon { font-size: 15px; line-height: 1; } +.btn-random .rand-mode { + font-size: 11px; padding: 2px 6px; border-radius: 999px; + background: rgba(255,122,61,0.18); color: var(--accent-2); + border: 1px solid rgba(255,122,61,0.30); + letter-spacing: 0.02em; +} + +.btn-docs { + display: flex; align-items: center; justify-content: center; + height: 36px; padding: 0 12px; + border-radius: 999px; + background: var(--bg-2); color: var(--muted); + border: 1px solid var(--line); + font-size: 12px; font-weight: 700; letter-spacing: 0.06em; + text-decoration: none; + transition: background 120ms, color 120ms, border-color 120ms; +} +.btn-docs:hover { background: var(--bg-3); color: var(--fg); border-color: rgba(78,201,166,0.4); } + .chips { display: flex; flex-wrap: wrap; gap: 5px; max-height: 64px; overflow-y: auto; @@ -656,3 +687,83 @@ dialog.add-station select:focus { border-color: var(--accent) !important; box-sh box-shadow: none !important; text-transform: uppercase; letter-spacing: 0.06em; font-weight: 800; font-size: 12px; } + +/* ============================================================ + Voting: up/down buttons in the now-playing bar, score badge + on every card, and a sort dropdown for the Browse tab. + ============================================================ */ + +.vote-group { + display: flex; + gap: 0; + margin-right: 6px; +} +.vote { + display: flex; align-items: center; gap: 6px; + height: 46px; min-width: 64px; + padding: 0 12px; + background: var(--bg-2) !important; + color: var(--fg); + border: 1px solid var(--line) !important; + font-weight: 800; font-size: 14px; + font-variant-numeric: tabular-nums; + transition: background 80ms linear, color 80ms linear, border-color 80ms linear !important; +} +.vote + .vote { margin-left: -1px; } +.vote:disabled { opacity: 0.35; cursor: not-allowed; } +.vote .vote-icon { font-size: 17px; line-height: 1; } +.vote .vote-count { font-size: 13px; letter-spacing: 0.02em; } +.vote.up:not(:disabled):hover { + background: var(--good) !important; color: #000 !important; border-color: var(--good) !important; +} +.vote.down:not(:disabled):hover { + background: var(--bad) !important; color: #000 !important; border-color: var(--bad) !important; +} +.vote.up.on { + background: var(--good) !important; color: #000 !important; border-color: var(--good) !important; +} +.vote.down.on { + background: var(--bad) !important; color: #000 !important; border-color: var(--bad) !important; +} + +/* Sort dropdown next to search */ +.sort { + height: 36px; + padding: 0 28px 0 12px; + background: var(--bg-2) !important; + color: var(--fg); + border: 1px solid var(--line) !important; + font-size: 12px; font-weight: 700; + text-transform: uppercase; letter-spacing: 0.05em; + appearance: none; + background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), + linear-gradient(135deg, var(--muted) 50%, transparent 50%); + background-position: calc(100% - 14px) 50%, calc(100% - 9px) 50%; + background-size: 5px 5px, 5px 5px; + background-repeat: no-repeat; + outline: none; + cursor: pointer; +} +.sort:focus, .sort:hover { border-color: var(--accent) !important; } +.sort option { background: var(--bg-1); color: var(--fg); } + +/* Grid card: add a column for the score badge. */ +.card { + grid-template-columns: 44px 1fr auto auto auto !important; +} +.score-badge { + display: inline-flex; align-items: center; justify-content: center; + min-width: 36px; height: 26px; + padding: 0 8px; + background: var(--bg-2); + border: 1px solid var(--line); + color: var(--muted); + font-weight: 800; font-size: 12px; + font-variant-numeric: tabular-nums; + letter-spacing: 0.02em; +} +.score-badge.pos { color: var(--good); border-color: rgba(0,210,122,0.35); } +.score-badge.neg { color: var(--bad); border-color: rgba(255,48,48,0.35); } +.score-badge.neu { color: var(--muted-2); } +.card.playing .score-badge { border-color: var(--accent); } +