mirror of
https://github.com/iptv-org/epg.git
synced 2025-05-09 08:30:06 -04:00
Merge branch 'master' into patch-2025.01.2
This commit is contained in:
commit
979db51d7e
182 changed files with 7599 additions and 8012 deletions
|
@ -1,39 +0,0 @@
|
||||||
{
|
|
||||||
"env": {
|
|
||||||
"node": true,
|
|
||||||
"es2021": true,
|
|
||||||
"jest": true
|
|
||||||
},
|
|
||||||
"extends": [
|
|
||||||
"eslint:recommended",
|
|
||||||
"plugin:@typescript-eslint/recommended",
|
|
||||||
"prettier"
|
|
||||||
],
|
|
||||||
"parser": "@typescript-eslint/parser",
|
|
||||||
"parserOptions": {
|
|
||||||
"ecmaVersion": "latest",
|
|
||||||
"sourceType": "module"
|
|
||||||
},
|
|
||||||
"plugins": [
|
|
||||||
"@typescript-eslint"
|
|
||||||
],
|
|
||||||
"rules": {
|
|
||||||
"@typescript-eslint/no-var-requires": "off",
|
|
||||||
"no-case-declarations": "off",
|
|
||||||
"linebreak-style": [
|
|
||||||
"error",
|
|
||||||
"windows"
|
|
||||||
],
|
|
||||||
"quotes": [
|
|
||||||
"error",
|
|
||||||
"single",
|
|
||||||
{
|
|
||||||
"avoidEscape": true
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"semi": [
|
|
||||||
"error",
|
|
||||||
"never"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
42
.github/workflows/check.yml
vendored
Normal file
42
.github/workflows/check.yml
vendored
Normal file
|
@ -0,0 +1,42 @@
|
||||||
|
name: check
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
pull_request:
|
||||||
|
types: [opened, synchronize, reopened, edited]
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
jobs:
|
||||||
|
check:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 2
|
||||||
|
- uses: tj-actions/changed-files@v45
|
||||||
|
id: files
|
||||||
|
with:
|
||||||
|
since_last_remote_commit: true
|
||||||
|
files_yaml: |
|
||||||
|
js:
|
||||||
|
- tests/**/*.{js,ts}
|
||||||
|
- scripts/**/*.{js,ts}
|
||||||
|
- sites/**/*.{js,ts}
|
||||||
|
channels:
|
||||||
|
- sites/**/*.channels.xml
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
if: ${{ !env.ACT && (steps.files.outputs.js_any_changed == 'true' || steps.files.outputs.channels_any_changed == 'true') }}
|
||||||
|
with:
|
||||||
|
node-version: 22
|
||||||
|
cache: 'npm'
|
||||||
|
- name: install dependencies
|
||||||
|
if: steps.files.outputs.js_any_changed == 'true' || steps.files.outputs.channels_any_changed == 'true'
|
||||||
|
run: SKIP_POSTINSTALL=1 npm install
|
||||||
|
- name: check changed js-files
|
||||||
|
if: steps.files.outputs.js_any_changed == 'true'
|
||||||
|
run: |
|
||||||
|
npx eslint ${{ steps.files.outputs.js_all_changed_files }}
|
||||||
|
- name: check changed *.channels.xml
|
||||||
|
if: steps.files.outputs.channels_any_changed == 'true'
|
||||||
|
run: |
|
||||||
|
npm run channels:lint -- ${{ steps.files.outputs.channels_all_changed_files }}
|
2
.husky/pre-commit
Normal file
2
.husky/pre-commit
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
npm run lint
|
||||||
|
npm run channels:lint
|
|
@ -1,7 +1,7 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
tabWidth: 2,
|
tabWidth: 2,
|
||||||
useTabs: false,
|
useTabs: false,
|
||||||
endOfLine: 'lf',
|
endOfLine: 'crlf',
|
||||||
semi: false,
|
semi: false,
|
||||||
singleQuote: true,
|
singleQuote: true,
|
||||||
printWidth: 100,
|
printWidth: 100,
|
||||||
|
|
1
.sites/.gitignore
vendored
Normal file
1
.sites/.gitignore
vendored
Normal file
|
@ -0,0 +1 @@
|
||||||
|
_table.md
|
207
.sites/_table.md
207
.sites/_table.md
|
@ -1,207 +0,0 @@
|
||||||
<table>
|
|
||||||
<thead>
|
|
||||||
<tr><th align="left">Site</th><th align="left">Status</th><th align="left">Notes</th></tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
<tr><td><a href="sites/9tv.co.il">9tv.co.il</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/abc.net.au">abc.net.au</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/allente.dk">allente.dk</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/allente.fi">allente.fi</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/allente.no">allente.no</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/allente.se">allente.se</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/andorradifusio.ad">andorradifusio.ad</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/anteltv.com.uy">anteltv.com.uy</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/arianaafgtv.com">arianaafgtv.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/arianatelevision.com">arianatelevision.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/arirang.com">arirang.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/artonline.tv">artonline.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/awilime.com">awilime.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/bein.com">bein.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/beinsports.com">beinsports.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/berrymedia.co.kr">berrymedia.co.kr</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/cablego.com.pe">cablego.com.pe</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/cableplus.com.uy">cableplus.com.uy</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/canalplus-haiti.com">canalplus-haiti.com</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2237</td></tr>
|
|
||||||
<tr><td><a href="sites/canalplus.com">canalplus.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/cgates.lt">cgates.lt</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/chada.ma">chada.ma</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/chaines-tv.orange.fr">chaines-tv.orange.fr</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2395</td></tr>
|
|
||||||
<tr><td><a href="sites/clickthecity.com">clickthecity.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/comteco.com.bo">comteco.com.bo</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2239</td></tr>
|
|
||||||
<tr><td><a href="sites/content.astro.com.my">content.astro.com.my</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/cosmote.gr">cosmote.gr</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/cubmu.com">cubmu.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/dens.tv">dens.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/digiturk.com.tr">digiturk.com.tr</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2304, https://github.com/iptv-org/epg/issues/2547</td></tr>
|
|
||||||
<tr><td><a href="sites/directv.com">directv.com</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2284</td></tr>
|
|
||||||
<tr><td><a href="sites/directv.com.ar">directv.com.ar</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2339</td></tr>
|
|
||||||
<tr><td><a href="sites/directv.com.uy">directv.com.uy</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/dishtv.in">dishtv.in</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2445</td></tr>
|
|
||||||
<tr><td><a href="sites/dsmart.com.tr">dsmart.com.tr</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/dstv.com">dstv.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/elcinema.com">elcinema.com</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2541</td></tr>
|
|
||||||
<tr><td><a href="sites/ena.skylifetv.co.kr">ena.skylifetv.co.kr</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/energeek.cl">energeek.cl</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/entertainment.ie">entertainment.ie</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/firstmedia.com">firstmedia.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/flixed.io">flixed.io</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/foxsports.com.au">foxsports.com.au</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/foxtel.com.au">foxtel.com.au</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/frikanalen.no">frikanalen.no</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/gatotv.com">gatotv.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/getafteritmedia.com">getafteritmedia.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/guida.tv">guida.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/guidatv.sky.it">guidatv.sky.it</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/hd-plus.de">hd-plus.de</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2173</td></tr>
|
|
||||||
<tr><td><a href="sites/horizon.tv">horizon.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/hoy.tv">hoy.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/i.mjh.nz">i.mjh.nz</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/i24news.tv">i24news.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/iltalehti.fi">iltalehti.fi</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2396</td></tr>
|
|
||||||
<tr><td><a href="sites/indihometv.com">indihometv.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/ionplustv.com">ionplustv.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/ipko.tv">ipko.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/kan.org.il">kan.org.il</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2273</td></tr>
|
|
||||||
<tr><td><a href="sites/knr.gl">knr.gl</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/kplus.vn">kplus.vn</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2240</td></tr>
|
|
||||||
<tr><td><a href="sites/kvf.fo">kvf.fo</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/m.tv.sms.cz">m.tv.sms.cz</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2241</td></tr>
|
|
||||||
<tr><td><a href="sites/m.tving.com">m.tving.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/magticom.ge">magticom.ge</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/mako.co.il">mako.co.il</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/maxtv.hrvatskitelekom.hr">maxtv.hrvatskitelekom.hr</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2509</td></tr>
|
|
||||||
<tr><td><a href="sites/maxtvgo.mk">maxtvgo.mk</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/mediagenie.co.kr">mediagenie.co.kr</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/mediaklikk.hu">mediaklikk.hu</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/mediasetinfinity.mediaset.it">mediasetinfinity.mediaset.it</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/melita.com">melita.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/meo.pt">meo.pt</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2446</td></tr>
|
|
||||||
<tr><td><a href="sites/meuguia.tv">meuguia.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/mewatch.sg">mewatch.sg</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/mi.tv">mi.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/mncvision.id">mncvision.id</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/moji.id">moji.id</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/mon-programme-tv.be">mon-programme-tv.be</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/movistarplus.es">movistarplus.es</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2498</td></tr>
|
|
||||||
<tr><td><a href="sites/mtel.ba">mtel.ba</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/mts.rs">mts.rs</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/mujtvprogram.cz">mujtvprogram.cz</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/musor.tv">musor.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/mysky.com.ph">mysky.com.ph</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/mytelly.co.uk">mytelly.co.uk</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/mytvsuper.com">mytvsuper.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/neo.io">neo.io</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/nhkworldpremium.com">nhkworldpremium.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/nhl.com">nhl.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/nostv.pt">nostv.pt</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/novacyprus.com">novacyprus.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/novasports.gr">novasports.gr</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/nowplayer.now.com">nowplayer.now.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/nuevosiglo.com.uy">nuevosiglo.com.uy</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/nzxmltv.com">nzxmltv.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/ontvtonight.com">ontvtonight.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/osn.com">osn.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/pbsguam.org">pbsguam.org</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/pickx.be">pickx.be</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/player.ee.co.uk">player.ee.co.uk</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/playtv.unifi.com.my">playtv.unifi.com.my</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/plex.tv">plex.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/programacion-tv.elpais.com">programacion-tv.elpais.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/programacion.tcc.com.uy">programacion.tcc.com.uy</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/programetv.ro">programetv.ro</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/programme-tv.net">programme-tv.net</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/programme-tv.vini.pf">programme-tv.vini.pf</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/programme.tvb.com">programme.tvb.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/programtv.onet.pl">programtv.onet.pl</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/raiplay.it">raiplay.it</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/reportv.com.ar">reportv.com.ar</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/rev.bs">rev.bs</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2255</td></tr>
|
|
||||||
<tr><td><a href="sites/rotana.net">rotana.net</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/rtb.gov.bn">rtb.gov.bn</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2257</td></tr>
|
|
||||||
<tr><td><a href="sites/rthk.hk">rthk.hk</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/rtmklik.rtm.gov.my">rtmklik.rtm.gov.my</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/rtp.pt">rtp.pt</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/ruv.is">ruv.is</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/s.mxtv.jp">s.mxtv.jp</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/sat.tv">sat.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/shahid.mbc.net">shahid.mbc.net</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/siba.com.co">siba.com.co</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/singtel.com">singtel.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/sjonvarp.is">sjonvarp.is</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/sky.co.nz">sky.co.nz</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/sky.com">sky.com</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2516, https://github.com/iptv-org/epg/issues/2501</td></tr>
|
|
||||||
<tr><td><a href="sites/sky.de">sky.de</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/skylife.co.kr">skylife.co.kr</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/skyperfectv.co.jp">skyperfectv.co.jp</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/snrt.ma">snrt.ma</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/sporttv.pt">sporttv.pt</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/starhubtvplus.com">starhubtvplus.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/startimestv.com">startimestv.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/streamingtvguides.com">streamingtvguides.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/superguidatv.it">superguidatv.it</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/taiwanplus.com">taiwanplus.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tapdmv.com">tapdmv.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/telenet.tv">telenet.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/teliatv.ee">teliatv.ee</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/telkussa.fi">telkussa.fi</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/telsu.fi">telsu.fi</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tivu.tv">tivu.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/toonamiaftermath.com">toonamiaftermath.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/turksatkablo.com.tr">turksatkablo.com.tr</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv-programme.telecablesat.fr">tv-programme.telecablesat.fr</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv.blue.ch">tv.blue.ch</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv.cctv.com">tv.cctv.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv.dir.bg">tv.dir.bg</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv.lv">tv.lv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv.magenta.at">tv.magenta.at</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv.mail.ru">tv.mail.ru</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv.movistar.com.pe">tv.movistar.com.pe</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv.nu">tv.nu</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv.post.lu">tv.post.lu</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv.trueid.net">tv.trueid.net</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv.yandex.ru">tv.yandex.ru</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv.yettel.hu">tv.yettel.hu</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2263</td></tr>
|
|
||||||
<tr><td><a href="sites/tv24.co.uk">tv24.co.uk</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv24.se">tv24.se</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tv2go.t-2.net">tv2go.t-2.net</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tva.tv">tva.tv</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2264</td></tr>
|
|
||||||
<tr><td><a href="sites/tvarenasport.com">tvarenasport.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tvarenasport.hr">tvarenasport.hr</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tvcesoir.fr">tvcesoir.fr</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tvcubana.icrt.cu">tvcubana.icrt.cu</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tvgids.nl">tvgids.nl</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2400</td></tr>
|
|
||||||
<tr><td><a href="sites/tvguide.com">tvguide.com</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2542</td></tr>
|
|
||||||
<tr><td><a href="sites/tvguide.myjcom.jp">tvguide.myjcom.jp</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tvhebdo.com">tvhebdo.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tvheute.at">tvheute.at</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tvim.tv">tvim.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tvireland.ie">tvireland.ie</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tvmi.mt">tvmi.mt</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tvmusor.hu">tvmusor.hu</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tvpassport.com">tvpassport.com</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2272</td></tr>
|
|
||||||
<tr><td><a href="sites/tvplus.com.tr">tvplus.com.tr</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/tvprofil.com">tvprofil.com</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2399</td></tr>
|
|
||||||
<tr><td><a href="sites/tvtv.us">tvtv.us</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2176</td></tr>
|
|
||||||
<tr><td><a href="sites/v3.myafn.dodmedia.osd.mil">v3.myafn.dodmedia.osd.mil</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/vidio.com">vidio.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/virginmediatelevision.ie">virginmediatelevision.ie</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/virgintvgo.virginmedia.com">virgintvgo.virginmedia.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/visionplus.id">visionplus.id</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/vivacom.bg">vivacom.bg</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2270</td></tr>
|
|
||||||
<tr><td><a href="sites/vtm.be">vtm.be</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/walesi.com.fj">walesi.com.fj</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/watch.sportsnet.ca">watch.sportsnet.ca</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/watchyour.tv">watchyour.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/wavve.com">wavve.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/web.magentatv.de">web.magentatv.de</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/webtv.delta.nl">webtv.delta.nl</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/winplay.co">winplay.co</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/worldfishingnetwork.com">worldfishingnetwork.com</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/www3.nhk.or.jp">www3.nhk.or.jp</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/xumo.tv">xumo.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/zap.co.ao">zap.co.ao</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/ziggogo.tv">ziggogo.tv</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/znbc.co.zm">znbc.co.zm</a></td><td>🟢</td><td></td></tr>
|
|
||||||
<tr><td><a href="sites/zuragt.mn">zuragt.mn</a></td><td>🟢</td><td></td></tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Sites
|
# Sites
|
||||||
|
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
#include "./.sites/_table.md"
|
#include "./.sites/_table.md"
|
||||||
|
|
|
@ -147,6 +147,7 @@ For scripts to work, you must have [Node.js](https://nodejs.org/en) installed on
|
||||||
|
|
||||||
To run scripts use the `npm run <script-name>` command.
|
To run scripts use the `npm run <script-name>` command.
|
||||||
|
|
||||||
|
- `act:check`: allows to test the [check](https://github.com/iptv-org/iptv/blob/master/.github/workflows/check.yml) workflow locally. Depends on [nektos/act](https://github.com/nektos/act).
|
||||||
- `act:update`: allows to test the [update](https://github.com/iptv-org/iptv/blob/master/.github/workflows/update.yml) workflow locally. Depends on [nektos/act](https://github.com/nektos/act).
|
- `act:update`: allows to test the [update](https://github.com/iptv-org/iptv/blob/master/.github/workflows/update.yml) workflow locally. Depends on [nektos/act](https://github.com/nektos/act).
|
||||||
- `api:load`: downloads the latest channels data from the [iptv-org/api](https://github.com/iptv-org/api).
|
- `api:load`: downloads the latest channels data from the [iptv-org/api](https://github.com/iptv-org/api).
|
||||||
- `api:generate`: generates a JSON file with all channels for the [iptv-org/api](https://github.com/iptv-org/api) repository.
|
- `api:generate`: generates a JSON file with all channels for the [iptv-org/api](https://github.com/iptv-org/api) repository.
|
||||||
|
|
427
SITES.md
427
SITES.md
|
@ -1,212 +1,215 @@
|
||||||
# Sites
|
# Sites
|
||||||
|
|
||||||
<!-- prettier-ignore -->
|
<!-- prettier-ignore -->
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr><th align="left">Site</th><th align="left">Status</th><th align="left">Notes</th></tr>
|
<tr><th align="left">Site</th><th align="left">Status</th><th align="left">Notes</th></tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr><td><a href="sites/9tv.co.il">9tv.co.il</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/9tv.co.il">9tv.co.il</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/abc.net.au">abc.net.au</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/abc.net.au">abc.net.au</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/allente.dk">allente.dk</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/allente.dk">allente.dk</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/allente.fi">allente.fi</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/allente.fi">allente.fi</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/allente.no">allente.no</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/allente.no">allente.no</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/allente.se">allente.se</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/allente.se">allente.se</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/andorradifusio.ad">andorradifusio.ad</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/andorradifusio.ad">andorradifusio.ad</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/anteltv.com.uy">anteltv.com.uy</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/anteltv.com.uy">anteltv.com.uy</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/arianaafgtv.com">arianaafgtv.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/arianaafgtv.com">arianaafgtv.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/arianatelevision.com">arianatelevision.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/arianatelevision.com">arianatelevision.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/arirang.com">arirang.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/arirang.com">arirang.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/artonline.tv">artonline.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/artonline.tv">artonline.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/awilime.com">awilime.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/awilime.com">awilime.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/bein.com">bein.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/bein.com">bein.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/beinsports.com">beinsports.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/beinsports.com">beinsports.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/berrymedia.co.kr">berrymedia.co.kr</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/berrymedia.co.kr">berrymedia.co.kr</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/cablego.com.pe">cablego.com.pe</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/cablego.com.pe">cablego.com.pe</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/cableplus.com.uy">cableplus.com.uy</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/cableplus.com.uy">cableplus.com.uy</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/canalplus-haiti.com">canalplus-haiti.com</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2237</td></tr>
|
<tr><td><a href="sites/canalplus-haiti.com">canalplus-haiti.com</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2237</td></tr>
|
||||||
<tr><td><a href="sites/canalplus.com">canalplus.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/canalplus.com">canalplus.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/cgates.lt">cgates.lt</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/cgates.lt">cgates.lt</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/chada.ma">chada.ma</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/chada.ma">chada.ma</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/chaines-tv.orange.fr">chaines-tv.orange.fr</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2395</td></tr>
|
<tr><td><a href="sites/chaines-tv.orange.fr">chaines-tv.orange.fr</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/clickthecity.com">clickthecity.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/clickthecity.com">clickthecity.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/comteco.com.bo">comteco.com.bo</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2239</td></tr>
|
<tr><td><a href="sites/comteco.com.bo">comteco.com.bo</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2239</td></tr>
|
||||||
<tr><td><a href="sites/content.astro.com.my">content.astro.com.my</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/content.astro.com.my">content.astro.com.my</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/cosmotetv.gr">cosmotetv.gr</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/cosmotetv.gr">cosmotetv.gr</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/cubmu.com">cubmu.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/cubmu.com">cubmu.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/dens.tv">dens.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/cyta.com.cy">cyta.com.cy</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/digiturk.com.tr">digiturk.com.tr</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2304, https://github.com/iptv-org/epg/issues/2547</td></tr>
|
<tr><td><a href="sites/dens.tv">dens.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/directv.com">directv.com</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2284</td></tr>
|
<tr><td><a href="sites/digiturk.com.tr">digiturk.com.tr</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2304, https://github.com/iptv-org/epg/issues/2547</td></tr>
|
||||||
<tr><td><a href="sites/directv.com.ar">directv.com.ar</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2339</td></tr>
|
<tr><td><a href="sites/directv.com">directv.com</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2284</td></tr>
|
||||||
<tr><td><a href="sites/directv.com.uy">directv.com.uy</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/directv.com.ar">directv.com.ar</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2339</td></tr>
|
||||||
<tr><td><a href="sites/dishtv.in">dishtv.in</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2445</td></tr>
|
<tr><td><a href="sites/directv.com.uy">directv.com.uy</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/dsmart.com.tr">dsmart.com.tr</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/dishtv.in">dishtv.in</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2445</td></tr>
|
||||||
<tr><td><a href="sites/dstv.com">dstv.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/dsmart.com.tr">dsmart.com.tr</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/elcinema.com">elcinema.com</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2541</td></tr>
|
<tr><td><a href="sites/dstv.com">dstv.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/ena.skylifetv.co.kr">ena.skylifetv.co.kr</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/elcinema.com">elcinema.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/energeek.cl">energeek.cl</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/ena.skylifetv.co.kr">ena.skylifetv.co.kr</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/entertainment.ie">entertainment.ie</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/energeek.cl">energeek.cl</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/firstmedia.com">firstmedia.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/entertainment.ie">entertainment.ie</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/flixed.io">flixed.io</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/firstmedia.com">firstmedia.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/foxsports.com.au">foxsports.com.au</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/flixed.io">flixed.io</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/foxtel.com.au">foxtel.com.au</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/foxsports.com.au">foxsports.com.au</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/frikanalen.no">frikanalen.no</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/foxtel.com.au">foxtel.com.au</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/gatotv.com">gatotv.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/frikanalen.no">frikanalen.no</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/getafteritmedia.com">getafteritmedia.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/gatotv.com">gatotv.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/guida.tv">guida.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/getafteritmedia.com">getafteritmedia.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/guidatv.sky.it">guidatv.sky.it</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/guida.tv">guida.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/hd-plus.de">hd-plus.de</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2173</td></tr>
|
<tr><td><a href="sites/guidatv.sky.it">guidatv.sky.it</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/horizon.tv">horizon.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/hd-plus.de">hd-plus.de</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2173</td></tr>
|
||||||
<tr><td><a href="sites/hoy.tv">hoy.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/horizon.tv">horizon.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/i.mjh.nz">i.mjh.nz</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2556</td></tr>
|
<tr><td><a href="sites/hoy.tv">hoy.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/i24news.tv">i24news.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/i.mjh.nz">i.mjh.nz</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2556</td></tr>
|
||||||
<tr><td><a href="sites/iltalehti.fi">iltalehti.fi</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2396</td></tr>
|
<tr><td><a href="sites/i24news.tv">i24news.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/indihometv.com">indihometv.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/iltalehti.fi">iltalehti.fi</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2396</td></tr>
|
||||||
<tr><td><a href="sites/ionplustv.com">ionplustv.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/indihometv.com">indihometv.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/ipko.tv">ipko.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/ionplustv.com">ionplustv.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/kan.org.il">kan.org.il</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2273</td></tr>
|
<tr><td><a href="sites/ipko.tv">ipko.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/knr.gl">knr.gl</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/kan.org.il">kan.org.il</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2273</td></tr>
|
||||||
<tr><td><a href="sites/kplus.vn">kplus.vn</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2240</td></tr>
|
<tr><td><a href="sites/knr.gl">knr.gl</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/kvf.fo">kvf.fo</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/kplus.vn">kplus.vn</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2240</td></tr>
|
||||||
<tr><td><a href="sites/m.tv.sms.cz">m.tv.sms.cz</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2241</td></tr>
|
<tr><td><a href="sites/kvf.fo">kvf.fo</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/m.tving.com">m.tving.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/m.tv.sms.cz">m.tv.sms.cz</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2241</td></tr>
|
||||||
<tr><td><a href="sites/magticom.ge">magticom.ge</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/m.tving.com">m.tving.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/mako.co.il">mako.co.il</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/magticom.ge">magticom.ge</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/maxtv.hrvatskitelekom.hr">maxtv.hrvatskitelekom.hr</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2509</td></tr>
|
<tr><td><a href="sites/mako.co.il">mako.co.il</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/maxtvgo.mk">maxtvgo.mk</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/maxtv.hrvatskitelekom.hr">maxtv.hrvatskitelekom.hr</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2509</td></tr>
|
||||||
<tr><td><a href="sites/mediagenie.co.kr">mediagenie.co.kr</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/maxtvgo.mk">maxtvgo.mk</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/mediaklikk.hu">mediaklikk.hu</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/mediagenie.co.kr">mediagenie.co.kr</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/mediasetinfinity.mediaset.it">mediasetinfinity.mediaset.it</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/mediaklikk.hu">mediaklikk.hu</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/melita.com">melita.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/mediasetinfinity.mediaset.it">mediasetinfinity.mediaset.it</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/meo.pt">meo.pt</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2446</td></tr>
|
<tr><td><a href="sites/melita.com">melita.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/meuguia.tv">meuguia.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/meo.pt">meo.pt</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2446</td></tr>
|
||||||
<tr><td><a href="sites/mewatch.sg">mewatch.sg</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/meuguia.tv">meuguia.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/mi.tv">mi.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/mewatch.sg">mewatch.sg</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/mncvision.id">mncvision.id</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/mi.tv">mi.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/moji.id">moji.id</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/mncvision.id">mncvision.id</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/mon-programme-tv.be">mon-programme-tv.be</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/moji.id">moji.id</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/movistarplus.es">movistarplus.es</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2498</td></tr>
|
<tr><td><a href="sites/mon-programme-tv.be">mon-programme-tv.be</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/mtel.ba">mtel.ba</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/movistarplus.es">movistarplus.es</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2498</td></tr>
|
||||||
<tr><td><a href="sites/mts.rs">mts.rs</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/mtel.ba">mtel.ba</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/mujtvprogram.cz">mujtvprogram.cz</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/mts.rs">mts.rs</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/musor.tv">musor.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/mujtvprogram.cz">mujtvprogram.cz</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/mysky.com.ph">mysky.com.ph</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/musor.tv">musor.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/mytelly.co.uk">mytelly.co.uk</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/mysky.com.ph">mysky.com.ph</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/mytvsuper.com">mytvsuper.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/mytelly.co.uk">mytelly.co.uk</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/neo.io">neo.io</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/mytvsuper.com">mytvsuper.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/nhkworldpremium.com">nhkworldpremium.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/neo.io">neo.io</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/nhl.com">nhl.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/nhkworldpremium.com">nhkworldpremium.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/nostv.pt">nostv.pt</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/nhl.com">nhl.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/novacyprus.com">novacyprus.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/nostv.pt">nostv.pt</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/novasports.gr">novasports.gr</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/novacyprus.com">novacyprus.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/nowplayer.now.com">nowplayer.now.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/novasports.gr">novasports.gr</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/nuevosiglo.com.uy">nuevosiglo.com.uy</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/nowplayer.now.com">nowplayer.now.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/nzxmltv.com">nzxmltv.com</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2557</td></tr>
|
<tr><td><a href="sites/nuevosiglo.com.uy">nuevosiglo.com.uy</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/ontvtonight.com">ontvtonight.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/nzxmltv.com">nzxmltv.com</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2557</td></tr>
|
||||||
<tr><td><a href="sites/orangetv.orange.es">orangetv.orange.es</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/ontvtonight.com">ontvtonight.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/osn.com">osn.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/orangetv.orange.es">orangetv.orange.es</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/pbsguam.org">pbsguam.org</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/osn.com">osn.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/pickx.be">pickx.be</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/pbsguam.org">pbsguam.org</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/player.ee.co.uk">player.ee.co.uk</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/pickx.be">pickx.be</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/playtv.unifi.com.my">playtv.unifi.com.my</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/player.ee.co.uk">player.ee.co.uk</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/plex.tv">plex.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/playtv.unifi.com.my">playtv.unifi.com.my</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/programacion-tv.elpais.com">programacion-tv.elpais.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/plex.tv">plex.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/programacion.tcc.com.uy">programacion.tcc.com.uy</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/pluto.tv">pluto.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/programetv.ro">programetv.ro</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/programacion-tv.elpais.com">programacion-tv.elpais.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/programme-tv.net">programme-tv.net</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/programacion.tcc.com.uy">programacion.tcc.com.uy</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/programme-tv.vini.pf">programme-tv.vini.pf</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/programetv.ro">programetv.ro</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/programme.tvb.com">programme.tvb.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/programme-tv.net">programme-tv.net</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/programtv.onet.pl">programtv.onet.pl</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/programme-tv.vini.pf">programme-tv.vini.pf</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/raiplay.it">raiplay.it</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/programme.tvb.com">programme.tvb.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/reportv.com.ar">reportv.com.ar</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/programtv.onet.pl">programtv.onet.pl</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/rev.bs">rev.bs</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2255</td></tr>
|
<tr><td><a href="sites/raiplay.it">raiplay.it</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/rotana.net">rotana.net</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/reportv.com.ar">reportv.com.ar</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/rtb.gov.bn">rtb.gov.bn</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2257</td></tr>
|
<tr><td><a href="sites/rev.bs">rev.bs</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2255</td></tr>
|
||||||
<tr><td><a href="sites/rthk.hk">rthk.hk</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/rotana.net">rotana.net</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/rtmklik.rtm.gov.my">rtmklik.rtm.gov.my</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/rtb.gov.bn">rtb.gov.bn</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2257</td></tr>
|
||||||
<tr><td><a href="sites/rtp.pt">rtp.pt</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/rthk.hk">rthk.hk</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/ruv.is">ruv.is</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/rtmklik.rtm.gov.my">rtmklik.rtm.gov.my</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/s.mxtv.jp">s.mxtv.jp</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/rtp.pt">rtp.pt</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/sat.tv">sat.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/ruv.is">ruv.is</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/shahid.mbc.net">shahid.mbc.net</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/s.mxtv.jp">s.mxtv.jp</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/siba.com.co">siba.com.co</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/sat.tv">sat.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/singtel.com">singtel.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/shahid.mbc.net">shahid.mbc.net</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/sjonvarp.is">sjonvarp.is</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/siba.com.co">siba.com.co</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/sky.co.nz">sky.co.nz</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/singtel.com">singtel.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/sky.com">sky.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/sjonvarp.is">sjonvarp.is</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/sky.de">sky.de</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/sky.co.nz">sky.co.nz</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/skylife.co.kr">skylife.co.kr</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/sky.com">sky.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/skyperfectv.co.jp">skyperfectv.co.jp</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/sky.de">sky.de</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/snrt.ma">snrt.ma</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/skylife.co.kr">skylife.co.kr</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/sporttv.pt">sporttv.pt</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/skyperfectv.co.jp">skyperfectv.co.jp</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/starhubtvplus.com">starhubtvplus.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/snrt.ma">snrt.ma</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/startimestv.com">startimestv.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/sporttv.pt">sporttv.pt</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/streamingtvguides.com">streamingtvguides.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/starhubtvplus.com">starhubtvplus.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/superguidatv.it">superguidatv.it</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/startimestv.com">startimestv.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/taiwanplus.com">taiwanplus.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/stod2.is">stod2.is</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tapdmv.com">tapdmv.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/streamingtvguides.com">streamingtvguides.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/telenet.tv">telenet.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/superguidatv.it">superguidatv.it</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/teliatv.ee">teliatv.ee</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/taiwanplus.com">taiwanplus.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/telkussa.fi">telkussa.fi</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tapdmv.com">tapdmv.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/telsu.fi">telsu.fi</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/telenet.tv">telenet.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tivie.id">tivie.id</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/teliatv.ee">teliatv.ee</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tivu.tv">tivu.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/telkussa.fi">telkussa.fi</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/toonamiaftermath.com">toonamiaftermath.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/telsu.fi">telsu.fi</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/turksatkablo.com.tr">turksatkablo.com.tr</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tivie.id">tivie.id</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv-programme.telecablesat.fr">tv-programme.telecablesat.fr</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tivu.tv">tivu.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv.blue.ch">tv.blue.ch</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/toonamiaftermath.com">toonamiaftermath.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv.cctv.com">tv.cctv.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/turksatkablo.com.tr">turksatkablo.com.tr</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv.dir.bg">tv.dir.bg</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tv-programme.telecablesat.fr">tv-programme.telecablesat.fr</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv.lv">tv.lv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tv.blue.ch">tv.blue.ch</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv.magenta.at">tv.magenta.at</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tv.cctv.com">tv.cctv.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv.mail.ru">tv.mail.ru</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tv.dir.bg">tv.dir.bg</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv.movistar.com.pe">tv.movistar.com.pe</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tv.lv">tv.lv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv.nu">tv.nu</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tv.magenta.at">tv.magenta.at</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv.post.lu">tv.post.lu</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tv.mail.ru">tv.mail.ru</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv.trueid.net">tv.trueid.net</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tv.movistar.com.pe">tv.movistar.com.pe</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv.yandex.ru">tv.yandex.ru</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tv.nu">tv.nu</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv.yettel.hu">tv.yettel.hu</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2263</td></tr>
|
<tr><td><a href="sites/tv.post.lu">tv.post.lu</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv24.co.uk">tv24.co.uk</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tv.trueid.net">tv.trueid.net</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv24.se">tv24.se</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tv.yandex.ru">tv.yandex.ru</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tv2go.t-2.net">tv2go.t-2.net</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tv.yettel.hu">tv.yettel.hu</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2263</td></tr>
|
||||||
<tr><td><a href="sites/tva.tv">tva.tv</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2264</td></tr>
|
<tr><td><a href="sites/tv24.co.uk">tv24.co.uk</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tvarenasport.com">tvarenasport.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tv24.se">tv24.se</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tvarenasport.hr">tvarenasport.hr</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tv2go.t-2.net">tv2go.t-2.net</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tvcesoir.fr">tvcesoir.fr</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tva.tv">tva.tv</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2264</td></tr>
|
||||||
<tr><td><a href="sites/tvcubana.icrt.cu">tvcubana.icrt.cu</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tvarenasport.com">tvarenasport.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tvgids.nl">tvgids.nl</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2400</td></tr>
|
<tr><td><a href="sites/tvarenasport.hr">tvarenasport.hr</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tvguide.com">tvguide.com</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2542</td></tr>
|
<tr><td><a href="sites/tvcesoir.fr">tvcesoir.fr</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tvguide.myjcom.jp">tvguide.myjcom.jp</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tvcubana.icrt.cu">tvcubana.icrt.cu</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tvhebdo.com">tvhebdo.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tvgids.nl">tvgids.nl</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2400</td></tr>
|
||||||
<tr><td><a href="sites/tvheute.at">tvheute.at</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tvguide.com">tvguide.com</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2542</td></tr>
|
||||||
<tr><td><a href="sites/tvim.tv">tvim.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tvguide.myjcom.jp">tvguide.myjcom.jp</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tvireland.ie">tvireland.ie</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tvhebdo.com">tvhebdo.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tvmi.mt">tvmi.mt</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tvheute.at">tvheute.at</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tvmusor.hu">tvmusor.hu</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tvim.tv">tvim.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tvpassport.com">tvpassport.com</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2272</td></tr>
|
<tr><td><a href="sites/tvireland.ie">tvireland.ie</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tvplus.com.tr">tvplus.com.tr</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tvmi.mt">tvmi.mt</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tvprofil.com">tvprofil.com</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2399</td></tr>
|
<tr><td><a href="sites/tvmusor.hu">tvmusor.hu</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/tvtv.us">tvtv.us</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2176</td></tr>
|
<tr><td><a href="sites/tvpassport.com">tvpassport.com</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2272</td></tr>
|
||||||
<tr><td><a href="sites/v3.myafn.dodmedia.osd.mil">v3.myafn.dodmedia.osd.mil</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tvplus.com.tr">tvplus.com.tr</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/vidio.com">vidio.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tvprofil.com">tvprofil.com</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2399</td></tr>
|
||||||
<tr><td><a href="sites/virginmediatelevision.ie">virginmediatelevision.ie</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/tvtv.us">tvtv.us</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2176</td></tr>
|
||||||
<tr><td><a href="sites/virgintvgo.virginmedia.com">virgintvgo.virginmedia.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/v3.myafn.dodmedia.osd.mil">v3.myafn.dodmedia.osd.mil</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/visionplus.id">visionplus.id</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/vidio.com">vidio.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/vivacom.bg">vivacom.bg</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2270</td></tr>
|
<tr><td><a href="sites/virginmediatelevision.ie">virginmediatelevision.ie</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/vtm.be">vtm.be</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/virgintvgo.virginmedia.com">virgintvgo.virginmedia.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/walesi.com.fj">walesi.com.fj</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/visionplus.id">visionplus.id</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/watch.sportsnet.ca">watch.sportsnet.ca</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/vivacom.bg">vivacom.bg</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2270</td></tr>
|
||||||
<tr><td><a href="sites/watchyour.tv">watchyour.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/vtm.be">vtm.be</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/wavve.com">wavve.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/walesi.com.fj">walesi.com.fj</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/web.magentatv.de">web.magentatv.de</a></td><td>🔴</td><td>https://github.com/iptv-org/epg/issues/2555</td></tr>
|
<tr><td><a href="sites/watch.sportsnet.ca">watch.sportsnet.ca</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/webtv.delta.nl">webtv.delta.nl</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/watchyour.tv">watchyour.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/winplay.co">winplay.co</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/wavve.com">wavve.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/worldfishingnetwork.com">worldfishingnetwork.com</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/web.magentatv.de">web.magentatv.de</a></td><td>🟡</td><td>https://github.com/iptv-org/epg/issues/2570</td></tr>
|
||||||
<tr><td><a href="sites/www3.nhk.or.jp">www3.nhk.or.jp</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/webtv.delta.nl">webtv.delta.nl</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/xumo.tv">xumo.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/winplay.co">winplay.co</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/zap.co.ao">zap.co.ao</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/worldfishingnetwork.com">worldfishingnetwork.com</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/ziggogo.tv">ziggogo.tv</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/www3.nhk.or.jp">www3.nhk.or.jp</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/znbc.co.zm">znbc.co.zm</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/xumo.tv">xumo.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
<tr><td><a href="sites/zuragt.mn">zuragt.mn</a></td><td>🟢</td><td></td></tr>
|
<tr><td><a href="sites/zap.co.ao">zap.co.ao</a></td><td>🟢</td><td></td></tr>
|
||||||
</tbody>
|
<tr><td><a href="sites/ziggogo.tv">ziggogo.tv</a></td><td>🟢</td><td></td></tr>
|
||||||
</table>
|
<tr><td><a href="sites/znbc.co.zm">znbc.co.zm</a></td><td>🟢</td><td></td></tr>
|
||||||
|
<tr><td><a href="sites/zuragt.mn">zuragt.mn</a></td><td>🟢</td><td></td></tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
52
eslint.config.mjs
Normal file
52
eslint.config.mjs
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
import typescriptEslint from '@typescript-eslint/eslint-plugin'
|
||||||
|
import globals from 'globals'
|
||||||
|
import tsParser from '@typescript-eslint/parser'
|
||||||
|
import path from 'node:path'
|
||||||
|
import { fileURLToPath } from 'node:url'
|
||||||
|
import js from '@eslint/js'
|
||||||
|
import { FlatCompat } from '@eslint/eslintrc'
|
||||||
|
|
||||||
|
const __filename = fileURLToPath(import.meta.url)
|
||||||
|
const __dirname = path.dirname(__filename)
|
||||||
|
const compat = new FlatCompat({
|
||||||
|
baseDirectory: __dirname,
|
||||||
|
recommendedConfig: js.configs.recommended,
|
||||||
|
allConfig: js.configs.all
|
||||||
|
})
|
||||||
|
|
||||||
|
export default [
|
||||||
|
...compat.extends('eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'),
|
||||||
|
{
|
||||||
|
plugins: {
|
||||||
|
'@typescript-eslint': typescriptEslint
|
||||||
|
},
|
||||||
|
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
...globals.node,
|
||||||
|
...globals.jest
|
||||||
|
},
|
||||||
|
|
||||||
|
parser: tsParser,
|
||||||
|
ecmaVersion: 'latest',
|
||||||
|
sourceType: 'module'
|
||||||
|
},
|
||||||
|
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/no-require-imports': 'off',
|
||||||
|
'@typescript-eslint/no-var-requires': 'off',
|
||||||
|
'no-case-declarations': 'off',
|
||||||
|
'linebreak-style': ['error', 'windows'],
|
||||||
|
|
||||||
|
quotes: [
|
||||||
|
'error',
|
||||||
|
'single',
|
||||||
|
{
|
||||||
|
avoidEscape: true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
semi: ['error', 'never']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
631
package-lock.json
generated
631
package-lock.json
generated
|
@ -9,11 +9,15 @@
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alex_neo/jest-expect-message": "^1.0.5",
|
"@alex_neo/jest-expect-message": "^1.0.5",
|
||||||
|
"@eslint/eslintrc": "^3.2.0",
|
||||||
|
"@eslint/js": "^9.17.0",
|
||||||
"@freearhey/core": "^0.3.1",
|
"@freearhey/core": "^0.3.1",
|
||||||
"@freearhey/search-js": "^0.1.1",
|
"@freearhey/search-js": "^0.1.1",
|
||||||
"@ntlab/sfetch": "^1.0.0",
|
"@ntlab/sfetch": "^1.0.0",
|
||||||
"@octokit/plugin-paginate-rest": "^11.3.6",
|
"@octokit/plugin-paginate-rest": "^11.3.6",
|
||||||
"@octokit/plugin-rest-endpoint-methods": "^13.2.6",
|
"@octokit/plugin-rest-endpoint-methods": "^13.2.6",
|
||||||
|
"@swc/core": "^1.10.4",
|
||||||
|
"@swc/jest": "^0.2.37",
|
||||||
"@types/cli-progress": "^3.11.3",
|
"@types/cli-progress": "^3.11.3",
|
||||||
"@types/fs-extra": "^11.0.2",
|
"@types/fs-extra": "^11.0.2",
|
||||||
"@types/inquirer": "^9.0.3",
|
"@types/inquirer": "^9.0.3",
|
||||||
|
@ -39,9 +43,12 @@
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"fs-extra": "^10.0.1",
|
"fs-extra": "^10.0.1",
|
||||||
"glob": "^7.2.0",
|
"glob": "^7.2.0",
|
||||||
|
"globals": "^15.14.0",
|
||||||
|
"husky": "^9.1.7",
|
||||||
"iconv-lite": "^0.4.24",
|
"iconv-lite": "^0.4.24",
|
||||||
"inquirer": "^8.2.6",
|
"inquirer": "^8.2.6",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
|
"jest-offline": "^1.0.1",
|
||||||
"langs": "^2.0.0",
|
"langs": "^2.0.0",
|
||||||
"libxmljs2": "^0.35.0",
|
"libxmljs2": "^0.35.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
@ -58,12 +65,12 @@
|
||||||
"run-script-os": "^1.1.6",
|
"run-script-os": "^1.1.6",
|
||||||
"serve": "^14.2.4",
|
"serve": "^14.2.4",
|
||||||
"signale": "^1.4.0",
|
"signale": "^1.4.0",
|
||||||
|
"skip-postinstall": "^1.0.0",
|
||||||
"srcset": "^4.0.0",
|
"srcset": "^4.0.0",
|
||||||
"table2array": "^0.0.2",
|
"table2array": "^0.0.2",
|
||||||
"tabletojson": "^2.0.7",
|
"tabletojson": "^2.0.7",
|
||||||
"tough-cookie": "^5.0.0",
|
"tough-cookie": "^5.0.0",
|
||||||
"transliteration": "^2.2.0",
|
"transliteration": "^2.2.0",
|
||||||
"ts-jest": "^29.1.1",
|
|
||||||
"tsx": "^4.19.2",
|
"tsx": "^4.19.2",
|
||||||
"unzipit": "^1.4.0",
|
"unzipit": "^1.4.0",
|
||||||
"wildcard-match": "^5.1.2"
|
"wildcard-match": "^5.1.2"
|
||||||
|
@ -639,6 +646,14 @@
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@babel/traverse/node_modules/globals": {
|
||||||
|
"version": "11.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||||
|
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@babel/types": {
|
"node_modules/@babel/types": {
|
||||||
"version": "7.23.3",
|
"version": "7.23.3",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.3.tgz",
|
||||||
|
@ -1508,6 +1523,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@jest/create-cache-key-function": {
|
||||||
|
"version": "29.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz",
|
||||||
|
"integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==",
|
||||||
|
"dependencies": {
|
||||||
|
"@jest/types": "^29.6.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@jest/environment": {
|
"node_modules/@jest/environment": {
|
||||||
"version": "29.7.0",
|
"version": "29.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
|
||||||
|
@ -2246,6 +2272,222 @@
|
||||||
"@sinonjs/commons": "^3.0.0"
|
"@sinonjs/commons": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@swc/core": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-ut3zfiTLORMxhr6y/GBxkHmzcGuVpwJYX4qyXWuBKkpw/0g0S5iO1/wW7RnLnZbAi8wS/n0atRZoaZlXWBkeJg==",
|
||||||
|
"hasInstallScript": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/counter": "^0.1.3",
|
||||||
|
"@swc/types": "^0.1.17"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"type": "opencollective",
|
||||||
|
"url": "https://opencollective.com/swc"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"@swc/core-darwin-arm64": "1.10.4",
|
||||||
|
"@swc/core-darwin-x64": "1.10.4",
|
||||||
|
"@swc/core-linux-arm-gnueabihf": "1.10.4",
|
||||||
|
"@swc/core-linux-arm64-gnu": "1.10.4",
|
||||||
|
"@swc/core-linux-arm64-musl": "1.10.4",
|
||||||
|
"@swc/core-linux-x64-gnu": "1.10.4",
|
||||||
|
"@swc/core-linux-x64-musl": "1.10.4",
|
||||||
|
"@swc/core-win32-arm64-msvc": "1.10.4",
|
||||||
|
"@swc/core-win32-ia32-msvc": "1.10.4",
|
||||||
|
"@swc/core-win32-x64-msvc": "1.10.4"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@swc/helpers": "*"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"@swc/helpers": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-darwin-arm64": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-sV/eurLhkjn/197y48bxKP19oqcLydSel42Qsy2zepBltqUx+/zZ8+/IS0Bi7kaWVFxerbW1IPB09uq8Zuvm3g==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-darwin-x64": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-gjYNU6vrAUO4+FuovEo9ofnVosTFXkF0VDuo1MKPItz6e2pxc2ale4FGzLw0Nf7JB1sX4a8h06CN16/pLJ8Q2w==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"darwin"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-arm-gnueabihf": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-zd7fXH5w8s+Sfvn2oO464KDWl+ZX1MJiVmE4Pdk46N3PEaNwE0koTfgx2vQRqRG4vBBobzVvzICC3618WcefOA==",
|
||||||
|
"cpu": [
|
||||||
|
"arm"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-arm64-gnu": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-+UGfoHDxsMZgFD3tABKLeEZHqLNOkxStu+qCG7atGBhS4Slri6h6zijVvf4yI5X3kbXdvc44XV/hrP/Klnui2A==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-arm64-musl": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-cDDj2/uYsOH0pgAnDkovLZvKJpFmBMyXkxEG6Q4yw99HbzO6QzZ5HDGWGWVq/6dLgYKlnnmpjZCPPQIu01mXEg==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-x64-gnu": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-qJXh9D6Kf5xSdGWPINpLGixAbB5JX8JcbEJpRamhlDBoOcQC79dYfOMEIxWPhTS1DGLyFakAx2FX/b2VmQmj0g==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-linux-x64-musl": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-A76lIAeyQnHCVt0RL/pG+0er8Qk9+acGJqSZOZm67Ve3B0oqMd871kPtaHBM0BW3OZAhoILgfHW3Op9Q3mx3Cw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"linux"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-win32-arm64-msvc": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-e6j5kBu4fIY7fFxFxnZI0MlEovRvp50Lg59Fw+DVbtqHk3C85dckcy5xKP+UoXeuEmFceauQDczUcGs19SRGSQ==",
|
||||||
|
"cpu": [
|
||||||
|
"arm64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-win32-ia32-msvc": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-RSYHfdKgNXV/amY5Tqk1EWVsyQnhlsM//jeqMLw5Fy9rfxP592W9UTumNikNRPdjI8wKKzNMXDb1U29tQjN0dg==",
|
||||||
|
"cpu": [
|
||||||
|
"ia32"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/core-win32-x64-msvc": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-1ujYpaqfqNPYdwKBlvJnOqcl+Syn3UrQ4XE0Txz6zMYgyh6cdU6a3pxqLqIUSJ12MtXRA9ZUhEz1ekU3LfLWXw==",
|
||||||
|
"cpu": [
|
||||||
|
"x64"
|
||||||
|
],
|
||||||
|
"optional": true,
|
||||||
|
"os": [
|
||||||
|
"win32"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/counter": {
|
||||||
|
"version": "0.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
||||||
|
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="
|
||||||
|
},
|
||||||
|
"node_modules/@swc/jest": {
|
||||||
|
"version": "0.2.37",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.37.tgz",
|
||||||
|
"integrity": "sha512-CR2BHhmXKGxTiFr21DYPRHQunLkX3mNIFGFkxBGji6r9uyIR5zftTOVYj1e0sFNMV2H7mf/+vpaglqaryBtqfQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@jest/create-cache-key-function": "^29.7.0",
|
||||||
|
"@swc/counter": "^0.1.3",
|
||||||
|
"jsonc-parser": "^3.2.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"npm": ">= 7.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"@swc/core": "*"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/@swc/types": {
|
||||||
|
"version": "0.1.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz",
|
||||||
|
"integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"@swc/counter": "^0.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/@szmarczak/http-timer": {
|
"node_modules/@szmarczak/http-timer": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
|
||||||
|
@ -3326,17 +3568,6 @@
|
||||||
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/bs-logger": {
|
|
||||||
"version": "0.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
|
|
||||||
"integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
|
|
||||||
"dependencies": {
|
|
||||||
"fast-json-stable-stringify": "2.x"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">= 6"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/bser": {
|
"node_modules/bser": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
|
||||||
|
@ -5262,11 +5493,14 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/globals": {
|
"node_modules/globals": {
|
||||||
"version": "11.12.0",
|
"version": "15.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz",
|
||||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
"integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/got": {
|
"node_modules/got": {
|
||||||
|
@ -5417,6 +5651,20 @@
|
||||||
"node": ">=10.17.0"
|
"node": ">=10.17.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/husky": {
|
||||||
|
"version": "9.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
|
||||||
|
"integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==",
|
||||||
|
"bin": {
|
||||||
|
"husky": "bin.js"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/typicode"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/iconv-lite": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
|
@ -6209,6 +6457,14 @@
|
||||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jest-offline": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-offline/-/jest-offline-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-pcYJ8rVxWP3SS9de15iSQY87ErLGGgMC4qtVcRLb/qemrefI1IgnAzOusp0eemGu7JoAGlb4oBGnZorehu95KA==",
|
||||||
|
"dependencies": {
|
||||||
|
"mitm": "^1.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/jest-pnp-resolver": {
|
"node_modules/jest-pnp-resolver": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
|
||||||
|
@ -6562,6 +6818,11 @@
|
||||||
"node": ">=6"
|
"node": ">=6"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/jsonc-parser": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="
|
||||||
|
},
|
||||||
"node_modules/jsonfile": {
|
"node_modules/jsonfile": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||||
|
@ -6721,11 +6982,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
},
|
},
|
||||||
"node_modules/lodash.memoize": {
|
|
||||||
"version": "4.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
|
||||||
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="
|
|
||||||
},
|
|
||||||
"node_modules/lodash.merge": {
|
"node_modules/lodash.merge": {
|
||||||
"version": "4.6.2",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||||
|
@ -6796,7 +7052,9 @@
|
||||||
"node_modules/make-error": {
|
"node_modules/make-error": {
|
||||||
"version": "1.3.6",
|
"version": "1.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
|
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||||
|
"optional": true,
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"node_modules/make-fetch-happen": {
|
"node_modules/make-fetch-happen": {
|
||||||
"version": "13.0.1",
|
"version": "13.0.1",
|
||||||
|
@ -7083,6 +7341,25 @@
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
||||||
},
|
},
|
||||||
|
"node_modules/mitm": {
|
||||||
|
"version": "1.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/mitm/-/mitm-1.7.3.tgz",
|
||||||
|
"integrity": "sha512-linie/mGisDH73C7aiW6JmstA5XskXd15JBJAEeNQBdH3/L0dJdE/yZ+rw/y2zT7Fcib5KAnL5OvxYOOFQbsgw==",
|
||||||
|
"dependencies": {
|
||||||
|
"semver": ">= 5 < 6"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 0.10.24"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/mitm/node_modules/semver": {
|
||||||
|
"version": "5.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||||
|
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
|
||||||
|
"bin": {
|
||||||
|
"semver": "bin/semver"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/mkdirp": {
|
"node_modules/mkdirp": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
|
@ -8910,6 +9187,15 @@
|
||||||
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
||||||
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
|
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
|
||||||
},
|
},
|
||||||
|
"node_modules/skip-postinstall": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/skip-postinstall/-/skip-postinstall-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-IUVEmm4v7Ubzrp9JDG15oTzMB+abJdHcduXMRzBlHnHRrmpQ/QoPtYCRaorP+abAULTGEh87gPPyyMK5H1X1Dg==",
|
||||||
|
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
|
||||||
|
"bin": {
|
||||||
|
"skip-postinstall": "index.js"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/slash": {
|
"node_modules/slash": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||||
|
@ -9406,86 +9692,6 @@
|
||||||
"typescript": ">=4.2.0"
|
"typescript": ">=4.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ts-jest": {
|
|
||||||
"version": "29.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz",
|
|
||||||
"integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==",
|
|
||||||
"dependencies": {
|
|
||||||
"bs-logger": "0.x",
|
|
||||||
"fast-json-stable-stringify": "2.x",
|
|
||||||
"jest-util": "^29.0.0",
|
|
||||||
"json5": "^2.2.3",
|
|
||||||
"lodash.memoize": "4.x",
|
|
||||||
"make-error": "1.x",
|
|
||||||
"semver": "^7.5.3",
|
|
||||||
"yargs-parser": "^21.0.1"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"ts-jest": "cli.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@babel/core": ">=7.0.0-beta.0 <8",
|
|
||||||
"@jest/types": "^29.0.0",
|
|
||||||
"babel-jest": "^29.0.0",
|
|
||||||
"jest": "^29.0.0",
|
|
||||||
"typescript": ">=4.3 <6"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"@babel/core": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"@jest/types": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"babel-jest": {
|
|
||||||
"optional": true
|
|
||||||
},
|
|
||||||
"esbuild": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ts-jest/node_modules/lru-cache": {
|
|
||||||
"version": "6.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
|
||||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
|
||||||
"dependencies": {
|
|
||||||
"yallist": "^4.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ts-jest/node_modules/semver": {
|
|
||||||
"version": "7.5.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
|
||||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
|
||||||
"dependencies": {
|
|
||||||
"lru-cache": "^6.0.0"
|
|
||||||
},
|
|
||||||
"bin": {
|
|
||||||
"semver": "bin/semver.js"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=10"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ts-jest/node_modules/yallist": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
|
||||||
},
|
|
||||||
"node_modules/ts-jest/node_modules/yargs-parser": {
|
|
||||||
"version": "21.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
|
||||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
|
|
||||||
"engines": {
|
|
||||||
"node": ">=12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ts-node": {
|
"node_modules/ts-node": {
|
||||||
"version": "10.9.1",
|
"version": "10.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||||
|
@ -10484,6 +10690,13 @@
|
||||||
"@babel/types": "^7.23.3",
|
"@babel/types": "^7.23.3",
|
||||||
"debug": "^4.1.0",
|
"debug": "^4.1.0",
|
||||||
"globals": "^11.1.0"
|
"globals": "^11.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"globals": {
|
||||||
|
"version": "11.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||||
|
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@babel/types": {
|
"@babel/types": {
|
||||||
|
@ -10994,6 +11207,14 @@
|
||||||
"strip-ansi": "^6.0.0"
|
"strip-ansi": "^6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@jest/create-cache-key-function": {
|
||||||
|
"version": "29.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz",
|
||||||
|
"integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==",
|
||||||
|
"requires": {
|
||||||
|
"@jest/types": "^29.6.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@jest/environment": {
|
"@jest/environment": {
|
||||||
"version": "29.7.0",
|
"version": "29.7.0",
|
||||||
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
|
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
|
||||||
|
@ -11591,6 +11812,108 @@
|
||||||
"@sinonjs/commons": "^3.0.0"
|
"@sinonjs/commons": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@swc/core": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core/-/core-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-ut3zfiTLORMxhr6y/GBxkHmzcGuVpwJYX4qyXWuBKkpw/0g0S5iO1/wW7RnLnZbAi8wS/n0atRZoaZlXWBkeJg==",
|
||||||
|
"requires": {
|
||||||
|
"@swc/core-darwin-arm64": "1.10.4",
|
||||||
|
"@swc/core-darwin-x64": "1.10.4",
|
||||||
|
"@swc/core-linux-arm-gnueabihf": "1.10.4",
|
||||||
|
"@swc/core-linux-arm64-gnu": "1.10.4",
|
||||||
|
"@swc/core-linux-arm64-musl": "1.10.4",
|
||||||
|
"@swc/core-linux-x64-gnu": "1.10.4",
|
||||||
|
"@swc/core-linux-x64-musl": "1.10.4",
|
||||||
|
"@swc/core-win32-arm64-msvc": "1.10.4",
|
||||||
|
"@swc/core-win32-ia32-msvc": "1.10.4",
|
||||||
|
"@swc/core-win32-x64-msvc": "1.10.4",
|
||||||
|
"@swc/counter": "^0.1.3",
|
||||||
|
"@swc/types": "^0.1.17"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@swc/core-darwin-arm64": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-sV/eurLhkjn/197y48bxKP19oqcLydSel42Qsy2zepBltqUx+/zZ8+/IS0Bi7kaWVFxerbW1IPB09uq8Zuvm3g==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@swc/core-darwin-x64": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-gjYNU6vrAUO4+FuovEo9ofnVosTFXkF0VDuo1MKPItz6e2pxc2ale4FGzLw0Nf7JB1sX4a8h06CN16/pLJ8Q2w==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@swc/core-linux-arm-gnueabihf": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-zd7fXH5w8s+Sfvn2oO464KDWl+ZX1MJiVmE4Pdk46N3PEaNwE0koTfgx2vQRqRG4vBBobzVvzICC3618WcefOA==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@swc/core-linux-arm64-gnu": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-+UGfoHDxsMZgFD3tABKLeEZHqLNOkxStu+qCG7atGBhS4Slri6h6zijVvf4yI5X3kbXdvc44XV/hrP/Klnui2A==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@swc/core-linux-arm64-musl": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-cDDj2/uYsOH0pgAnDkovLZvKJpFmBMyXkxEG6Q4yw99HbzO6QzZ5HDGWGWVq/6dLgYKlnnmpjZCPPQIu01mXEg==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@swc/core-linux-x64-gnu": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-qJXh9D6Kf5xSdGWPINpLGixAbB5JX8JcbEJpRamhlDBoOcQC79dYfOMEIxWPhTS1DGLyFakAx2FX/b2VmQmj0g==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@swc/core-linux-x64-musl": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-A76lIAeyQnHCVt0RL/pG+0er8Qk9+acGJqSZOZm67Ve3B0oqMd871kPtaHBM0BW3OZAhoILgfHW3Op9Q3mx3Cw==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@swc/core-win32-arm64-msvc": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-e6j5kBu4fIY7fFxFxnZI0MlEovRvp50Lg59Fw+DVbtqHk3C85dckcy5xKP+UoXeuEmFceauQDczUcGs19SRGSQ==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@swc/core-win32-ia32-msvc": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-RSYHfdKgNXV/amY5Tqk1EWVsyQnhlsM//jeqMLw5Fy9rfxP592W9UTumNikNRPdjI8wKKzNMXDb1U29tQjN0dg==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@swc/core-win32-x64-msvc": {
|
||||||
|
"version": "1.10.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.10.4.tgz",
|
||||||
|
"integrity": "sha512-1ujYpaqfqNPYdwKBlvJnOqcl+Syn3UrQ4XE0Txz6zMYgyh6cdU6a3pxqLqIUSJ12MtXRA9ZUhEz1ekU3LfLWXw==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"@swc/counter": {
|
||||||
|
"version": "0.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
|
||||||
|
"integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ=="
|
||||||
|
},
|
||||||
|
"@swc/jest": {
|
||||||
|
"version": "0.2.37",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/jest/-/jest-0.2.37.tgz",
|
||||||
|
"integrity": "sha512-CR2BHhmXKGxTiFr21DYPRHQunLkX3mNIFGFkxBGji6r9uyIR5zftTOVYj1e0sFNMV2H7mf/+vpaglqaryBtqfQ==",
|
||||||
|
"requires": {
|
||||||
|
"@jest/create-cache-key-function": "^29.7.0",
|
||||||
|
"@swc/counter": "^0.1.3",
|
||||||
|
"jsonc-parser": "^3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@swc/types": {
|
||||||
|
"version": "0.1.17",
|
||||||
|
"resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.17.tgz",
|
||||||
|
"integrity": "sha512-V5gRru+aD8YVyCOMAjMpWR1Ui577DD5KSJsHP8RAxopAH22jFz6GZd/qxqjO6MJHQhcsjvjOFXyDhyLQUnMveQ==",
|
||||||
|
"requires": {
|
||||||
|
"@swc/counter": "^0.1.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"@szmarczak/http-timer": {
|
"@szmarczak/http-timer": {
|
||||||
"version": "4.0.6",
|
"version": "4.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
|
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
|
||||||
|
@ -12372,14 +12695,6 @@
|
||||||
"update-browserslist-db": "^1.0.11"
|
"update-browserslist-db": "^1.0.11"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"bs-logger": {
|
|
||||||
"version": "0.2.6",
|
|
||||||
"resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
|
|
||||||
"integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==",
|
|
||||||
"requires": {
|
|
||||||
"fast-json-stable-stringify": "2.x"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"bser": {
|
"bser": {
|
||||||
"version": "2.1.1",
|
"version": "2.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
|
||||||
|
@ -13782,9 +14097,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"version": "11.12.0",
|
"version": "15.14.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz",
|
||||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
|
"integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig=="
|
||||||
},
|
},
|
||||||
"got": {
|
"got": {
|
||||||
"version": "11.8.5",
|
"version": "11.8.5",
|
||||||
|
@ -13888,6 +14203,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||||
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
|
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
|
||||||
},
|
},
|
||||||
|
"husky": {
|
||||||
|
"version": "9.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz",
|
||||||
|
"integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA=="
|
||||||
|
},
|
||||||
"iconv-lite": {
|
"iconv-lite": {
|
||||||
"version": "0.4.24",
|
"version": "0.4.24",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||||
|
@ -14438,6 +14758,14 @@
|
||||||
"jest-util": "^29.7.0"
|
"jest-util": "^29.7.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jest-offline": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jest-offline/-/jest-offline-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-pcYJ8rVxWP3SS9de15iSQY87ErLGGgMC4qtVcRLb/qemrefI1IgnAzOusp0eemGu7JoAGlb4oBGnZorehu95KA==",
|
||||||
|
"requires": {
|
||||||
|
"mitm": "^1.3.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"jest-pnp-resolver": {
|
"jest-pnp-resolver": {
|
||||||
"version": "1.2.3",
|
"version": "1.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
|
||||||
|
@ -14721,6 +15049,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||||
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
|
"integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="
|
||||||
},
|
},
|
||||||
|
"jsonc-parser": {
|
||||||
|
"version": "3.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
|
||||||
|
"integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ=="
|
||||||
|
},
|
||||||
"jsonfile": {
|
"jsonfile": {
|
||||||
"version": "6.1.0",
|
"version": "6.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||||
|
@ -14849,11 +15182,6 @@
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
},
|
},
|
||||||
"lodash.memoize": {
|
|
||||||
"version": "4.1.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz",
|
|
||||||
"integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag=="
|
|
||||||
},
|
|
||||||
"lodash.merge": {
|
"lodash.merge": {
|
||||||
"version": "4.6.2",
|
"version": "4.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||||
|
@ -14908,7 +15236,9 @@
|
||||||
"make-error": {
|
"make-error": {
|
||||||
"version": "1.3.6",
|
"version": "1.3.6",
|
||||||
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
"resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
|
||||||
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw=="
|
"integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
|
||||||
|
"optional": true,
|
||||||
|
"peer": true
|
||||||
},
|
},
|
||||||
"make-fetch-happen": {
|
"make-fetch-happen": {
|
||||||
"version": "13.0.1",
|
"version": "13.0.1",
|
||||||
|
@ -15138,6 +15468,21 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"mitm": {
|
||||||
|
"version": "1.7.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/mitm/-/mitm-1.7.3.tgz",
|
||||||
|
"integrity": "sha512-linie/mGisDH73C7aiW6JmstA5XskXd15JBJAEeNQBdH3/L0dJdE/yZ+rw/y2zT7Fcib5KAnL5OvxYOOFQbsgw==",
|
||||||
|
"requires": {
|
||||||
|
"semver": ">= 5 < 6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"semver": {
|
||||||
|
"version": "5.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
|
||||||
|
"integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"mkdirp": {
|
"mkdirp": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
|
||||||
|
@ -16465,6 +16810,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
"resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz",
|
||||||
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
|
"integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg=="
|
||||||
},
|
},
|
||||||
|
"skip-postinstall": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/skip-postinstall/-/skip-postinstall-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-IUVEmm4v7Ubzrp9JDG15oTzMB+abJdHcduXMRzBlHnHRrmpQ/QoPtYCRaorP+abAULTGEh87gPPyyMK5H1X1Dg=="
|
||||||
|
},
|
||||||
"slash": {
|
"slash": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||||
|
@ -16818,49 +17168,6 @@
|
||||||
"integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==",
|
"integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"ts-jest": {
|
|
||||||
"version": "29.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.1.tgz",
|
|
||||||
"integrity": "sha512-D6xjnnbP17cC85nliwGiL+tpoKN0StpgE0TeOjXQTU6MVCfsB4v7aW05CgQ/1OywGb0x/oy9hHFnN+sczTiRaA==",
|
|
||||||
"requires": {
|
|
||||||
"bs-logger": "0.x",
|
|
||||||
"fast-json-stable-stringify": "2.x",
|
|
||||||
"jest-util": "^29.0.0",
|
|
||||||
"json5": "^2.2.3",
|
|
||||||
"lodash.memoize": "4.x",
|
|
||||||
"make-error": "1.x",
|
|
||||||
"semver": "^7.5.3",
|
|
||||||
"yargs-parser": "^21.0.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"lru-cache": {
|
|
||||||
"version": "6.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
|
|
||||||
"integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
|
|
||||||
"requires": {
|
|
||||||
"yallist": "^4.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"semver": {
|
|
||||||
"version": "7.5.4",
|
|
||||||
"resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz",
|
|
||||||
"integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==",
|
|
||||||
"requires": {
|
|
||||||
"lru-cache": "^6.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"yallist": {
|
|
||||||
"version": "4.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
|
||||||
"integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
|
|
||||||
},
|
|
||||||
"yargs-parser": {
|
|
||||||
"version": "21.1.1",
|
|
||||||
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
|
|
||||||
"integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ts-node": {
|
"ts-node": {
|
||||||
"version": "10.9.1",
|
"version": "10.9.1",
|
||||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||||
|
|
20
package.json
20
package.json
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"name": "epg",
|
"name": "epg",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
"act:check": "act pull_request -W .github/workflows/check.yml",
|
||||||
"act:update": "act workflow_dispatch -W .github/workflows/update.yml",
|
"act:update": "act workflow_dispatch -W .github/workflows/update.yml",
|
||||||
"api:load": "npx tsx scripts/commands/api/load.ts",
|
"api:load": "npx tsx scripts/commands/api/load.ts",
|
||||||
"api:generate": "npx tsx scripts/commands/api/generate.ts",
|
"api:generate": "npx tsx scripts/commands/api/generate.ts",
|
||||||
|
@ -10,29 +11,37 @@
|
||||||
"channels:validate": "npx tsx scripts/commands/channels/validate.ts",
|
"channels:validate": "npx tsx scripts/commands/channels/validate.ts",
|
||||||
"sites:update": "npx tsx scripts/commands/sites/update.ts",
|
"sites:update": "npx tsx scripts/commands/sites/update.ts",
|
||||||
"grab": "npx tsx scripts/commands/epg/grab.ts",
|
"grab": "npx tsx scripts/commands/epg/grab.ts",
|
||||||
"lint": "npx eslint \"{scripts,tests}/**/*.{ts,js}\"",
|
"lint": "npx eslint \"{scripts,tests,sites}/**/*.{ts,js}\"",
|
||||||
"test": "run-script-os",
|
"test": "run-script-os",
|
||||||
"test:win32": "SET \"TZ=Pacific/Nauru\" && npx jest --runInBand",
|
"test:win32": "SET \"TZ=Pacific/Nauru\" && npx jest --runInBand",
|
||||||
"test:default": "TZ=Pacific/Nauru npx jest --runInBand",
|
"test:default": "TZ=Pacific/Nauru npx jest --runInBand",
|
||||||
"postinstall": "npm run api:load"
|
"postinstall": "skip-postinstall || npm run api:load",
|
||||||
|
"prepare": "husky"
|
||||||
},
|
},
|
||||||
"private": true,
|
"private": true,
|
||||||
"author": "Arhey",
|
"author": "Arhey",
|
||||||
"license": "UNLICENSED",
|
"license": "UNLICENSED",
|
||||||
"jest": {
|
"jest": {
|
||||||
|
"setupFiles": [
|
||||||
|
"<rootDir>/node_modules/jest-offline"
|
||||||
|
],
|
||||||
"transform": {
|
"transform": {
|
||||||
"^.+\\.(ts|js)$": "ts-jest"
|
"^.+\\.(ts|js)$": "@swc/jest"
|
||||||
},
|
},
|
||||||
"testRegex": "(tests|sites)/(.*?/)?.*test.(js|ts)$",
|
"testRegex": "(tests|sites)/(.*?/)?.*test.(js|ts)$",
|
||||||
"testTimeout": 10000
|
"testTimeout": 10000
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@alex_neo/jest-expect-message": "^1.0.5",
|
"@alex_neo/jest-expect-message": "^1.0.5",
|
||||||
|
"@eslint/eslintrc": "^3.2.0",
|
||||||
|
"@eslint/js": "^9.17.0",
|
||||||
"@freearhey/core": "^0.3.1",
|
"@freearhey/core": "^0.3.1",
|
||||||
"@freearhey/search-js": "^0.1.1",
|
"@freearhey/search-js": "^0.1.1",
|
||||||
"@ntlab/sfetch": "^1.0.0",
|
"@ntlab/sfetch": "^1.0.0",
|
||||||
"@octokit/plugin-paginate-rest": "^11.3.6",
|
"@octokit/plugin-paginate-rest": "^11.3.6",
|
||||||
"@octokit/plugin-rest-endpoint-methods": "^13.2.6",
|
"@octokit/plugin-rest-endpoint-methods": "^13.2.6",
|
||||||
|
"@swc/core": "^1.10.4",
|
||||||
|
"@swc/jest": "^0.2.37",
|
||||||
"@types/cli-progress": "^3.11.3",
|
"@types/cli-progress": "^3.11.3",
|
||||||
"@types/fs-extra": "^11.0.2",
|
"@types/fs-extra": "^11.0.2",
|
||||||
"@types/inquirer": "^9.0.3",
|
"@types/inquirer": "^9.0.3",
|
||||||
|
@ -58,9 +67,12 @@
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
"fs-extra": "^10.0.1",
|
"fs-extra": "^10.0.1",
|
||||||
"glob": "^7.2.0",
|
"glob": "^7.2.0",
|
||||||
|
"globals": "^15.14.0",
|
||||||
|
"husky": "^9.1.7",
|
||||||
"iconv-lite": "^0.4.24",
|
"iconv-lite": "^0.4.24",
|
||||||
"inquirer": "^8.2.6",
|
"inquirer": "^8.2.6",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
|
"jest-offline": "^1.0.1",
|
||||||
"langs": "^2.0.0",
|
"langs": "^2.0.0",
|
||||||
"libxmljs2": "^0.35.0",
|
"libxmljs2": "^0.35.0",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
|
@ -77,12 +89,12 @@
|
||||||
"run-script-os": "^1.1.6",
|
"run-script-os": "^1.1.6",
|
||||||
"serve": "^14.2.4",
|
"serve": "^14.2.4",
|
||||||
"signale": "^1.4.0",
|
"signale": "^1.4.0",
|
||||||
|
"skip-postinstall": "^1.0.0",
|
||||||
"srcset": "^4.0.0",
|
"srcset": "^4.0.0",
|
||||||
"table2array": "^0.0.2",
|
"table2array": "^0.0.2",
|
||||||
"tabletojson": "^2.0.7",
|
"tabletojson": "^2.0.7",
|
||||||
"tough-cookie": "^5.0.0",
|
"tough-cookie": "^5.0.0",
|
||||||
"transliteration": "^2.2.0",
|
"transliteration": "^2.2.0",
|
||||||
"ts-jest": "^29.1.1",
|
|
||||||
"tsx": "^4.19.2",
|
"tsx": "^4.19.2",
|
||||||
"unzipit": "^1.4.0",
|
"unzipit": "^1.4.0",
|
||||||
"wildcard-match": "^5.1.2"
|
"wildcard-match": "^5.1.2"
|
||||||
|
|
|
@ -1,51 +1,51 @@
|
||||||
import { Logger, Storage, Collection } from '@freearhey/core'
|
import { Logger, Storage, Collection } from '@freearhey/core'
|
||||||
import { ChannelsParser } from '../../core'
|
import { ChannelsParser } from '../../core'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { SITES_DIR, API_DIR } from '../../constants'
|
import { SITES_DIR, API_DIR } from '../../constants'
|
||||||
import { Channel } from 'epg-grabber'
|
import { Channel } from 'epg-grabber'
|
||||||
|
|
||||||
type OutputItem = {
|
type OutputItem = {
|
||||||
channel: string | null
|
channel: string | null
|
||||||
site: string
|
site: string
|
||||||
site_id: string
|
site_id: string
|
||||||
site_name: string
|
site_name: string
|
||||||
lang: string
|
lang: string
|
||||||
}
|
}
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const logger = new Logger()
|
const logger = new Logger()
|
||||||
|
|
||||||
logger.start('staring...')
|
logger.start('staring...')
|
||||||
|
|
||||||
logger.info('loading channels...')
|
logger.info('loading channels...')
|
||||||
const sitesStorage = new Storage(SITES_DIR)
|
const sitesStorage = new Storage(SITES_DIR)
|
||||||
const parser = new ChannelsParser({ storage: sitesStorage })
|
const parser = new ChannelsParser({ storage: sitesStorage })
|
||||||
|
|
||||||
let files: string[] = []
|
let files: string[] = []
|
||||||
files = await sitesStorage.list('**/*.channels.xml')
|
files = await sitesStorage.list('**/*.channels.xml')
|
||||||
|
|
||||||
let parsedChannels = new Collection()
|
let parsedChannels = new Collection()
|
||||||
for (const filepath of files) {
|
for (const filepath of files) {
|
||||||
parsedChannels = parsedChannels.concat(await parser.parse(filepath))
|
parsedChannels = parsedChannels.concat(await parser.parse(filepath))
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.info(` found ${parsedChannels.count()} channel(s)`)
|
logger.info(` found ${parsedChannels.count()} channel(s)`)
|
||||||
|
|
||||||
const output = parsedChannels.map((channel: Channel): OutputItem => {
|
const output = parsedChannels.map((channel: Channel): OutputItem => {
|
||||||
return {
|
return {
|
||||||
channel: channel.xmltv_id || null,
|
channel: channel.xmltv_id || null,
|
||||||
site: channel.site || '',
|
site: channel.site || '',
|
||||||
site_id: channel.site_id || '',
|
site_id: channel.site_id || '',
|
||||||
site_name: channel.name,
|
site_name: channel.name,
|
||||||
lang: channel.lang || ''
|
lang: channel.lang || ''
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const apiStorage = new Storage(API_DIR)
|
const apiStorage = new Storage(API_DIR)
|
||||||
const outputFilename = 'guides.json'
|
const outputFilename = 'guides.json'
|
||||||
await apiStorage.save('guides.json', output.toJSON())
|
await apiStorage.save('guides.json', output.toJSON())
|
||||||
|
|
||||||
logger.info(`saved to "${path.join(API_DIR, outputFilename)}"`)
|
logger.info(`saved to "${path.join(API_DIR, outputFilename)}"`)
|
||||||
}
|
}
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -43,7 +43,7 @@ async function main() {
|
||||||
const channelsIndex = sj.createIndex(channelsContent)
|
const channelsIndex = sj.createIndex(channelsContent)
|
||||||
|
|
||||||
const buffer = new Dictionary()
|
const buffer = new Dictionary()
|
||||||
for (let option of options.all()) {
|
for (const option of options.all()) {
|
||||||
const channel: Channel = option.channel
|
const channel: Channel = option.channel
|
||||||
if (channel.xmltv_id) {
|
if (channel.xmltv_id) {
|
||||||
if (channel.xmltv_id !== '-') {
|
if (channel.xmltv_id !== '-') {
|
||||||
|
@ -150,7 +150,7 @@ function getOptions(channelsIndex, channel: Channel) {
|
||||||
const query = channel.name
|
const query = channel.name
|
||||||
.replace(/\s(SD|TV|HD|SD\/HD|HDTV)$/i, '')
|
.replace(/\s(SD|TV|HD|SD\/HD|HDTV)$/i, '')
|
||||||
.replace(/(\(|\)|,)/gi, '')
|
.replace(/(\(|\)|,)/gi, '')
|
||||||
.replace(/\-/gi, ' ')
|
.replace(/-/gi, ' ')
|
||||||
.replace(/\+/gi, '')
|
.replace(/\+/gi, '')
|
||||||
const similar = channelsIndex.search(query).map(item => new ApiChannel(item))
|
const similar = channelsIndex.search(query).map(item => new ApiChannel(item))
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import chalk from 'chalk'
|
import chalk from 'chalk'
|
||||||
import libxml, { ValidationError } from 'libxmljs2'
|
import libxml, { ValidationError } from 'libxmljs2'
|
||||||
import { program } from 'commander'
|
import { program } from 'commander'
|
||||||
import { Logger, Storage, File } from '@freearhey/core'
|
import { Storage, File } from '@freearhey/core'
|
||||||
|
|
||||||
const xsd = `<?xml version="1.0" encoding="UTF-8"?>
|
const xsd = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
|
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
|
||||||
|
@ -23,26 +23,14 @@ const xsd = `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
</xs:element>
|
</xs:element>
|
||||||
</xs:schema>`
|
</xs:schema>`
|
||||||
|
|
||||||
program
|
program.argument('[filepath]', 'Path to *.channels.xml files to validate').parse(process.argv)
|
||||||
.option(
|
|
||||||
'-c, --channels <path>',
|
|
||||||
'Path to channels.xml file to validate',
|
|
||||||
'sites/**/*.channels.xml'
|
|
||||||
)
|
|
||||||
.parse(process.argv)
|
|
||||||
|
|
||||||
const options = program.opts()
|
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const logger = new Logger()
|
|
||||||
const storage = new Storage()
|
const storage = new Storage()
|
||||||
|
|
||||||
logger.info('options:')
|
|
||||||
logger.tree(options)
|
|
||||||
|
|
||||||
let errors: ValidationError[] = []
|
let errors: ValidationError[] = []
|
||||||
|
|
||||||
const files: string[] = await storage.list(options.channels)
|
const files = program.args.length ? program.args : await storage.list('sites/**/*.channels.xml')
|
||||||
for (const filepath of files) {
|
for (const filepath of files) {
|
||||||
const file = new File(filepath)
|
const file = new File(filepath)
|
||||||
if (file.extension() !== 'xml') continue
|
if (file.extension() !== 'xml') continue
|
||||||
|
@ -51,11 +39,15 @@ async function main() {
|
||||||
|
|
||||||
let localErrors: ValidationError[] = []
|
let localErrors: ValidationError[] = []
|
||||||
|
|
||||||
const xsdDoc = libxml.parseXml(xsd)
|
try {
|
||||||
const doc = libxml.parseXml(xml)
|
const xsdDoc = libxml.parseXml(xsd)
|
||||||
|
const doc = libxml.parseXml(xml)
|
||||||
|
|
||||||
if (!doc.validate(xsdDoc)) {
|
if (!doc.validate(xsdDoc)) {
|
||||||
localErrors = doc.validationErrors
|
localErrors = doc.validationErrors
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
localErrors.push(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localErrors.length) {
|
if (localErrors.length) {
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Logger, File, Collection, Storage } from '@freearhey/core'
|
||||||
import { ChannelsParser, XML } from '../../core'
|
import { ChannelsParser, XML } from '../../core'
|
||||||
import { Channel } from 'epg-grabber'
|
import { Channel } from 'epg-grabber'
|
||||||
import { Command } from 'commander'
|
import { Command } from 'commander'
|
||||||
import path from 'path'
|
import { pathToFileURL } from 'node:url'
|
||||||
|
|
||||||
const program = new Command()
|
const program = new Command()
|
||||||
program
|
program
|
||||||
|
@ -26,7 +26,7 @@ async function main() {
|
||||||
const logger = new Logger()
|
const logger = new Logger()
|
||||||
const file = new File(options.config)
|
const file = new File(options.config)
|
||||||
const dir = file.dirname()
|
const dir = file.dirname()
|
||||||
const config = require(path.resolve(options.config))
|
const config = (await import(pathToFileURL(options.config))).default
|
||||||
const outputFilepath = options.output || `${dir}/${config.site}.channels.xml`
|
const outputFilepath = options.output || `${dir}/${config.site}.channels.xml`
|
||||||
|
|
||||||
let channels = new Collection()
|
let channels = new Collection()
|
||||||
|
|
|
@ -47,7 +47,6 @@ async function main() {
|
||||||
|
|
||||||
const parsedChannels = await parser.parse(filepath)
|
const parsedChannels = await parser.parse(filepath)
|
||||||
|
|
||||||
const bufferById = new Dictionary()
|
|
||||||
const bufferBySiteId = new Dictionary()
|
const bufferBySiteId = new Dictionary()
|
||||||
const errors: ValidationError[] = []
|
const errors: ValidationError[] = []
|
||||||
parsedChannels.forEach((channel: Channel) => {
|
parsedChannels.forEach((channel: Channel) => {
|
||||||
|
|
|
@ -1,58 +1,58 @@
|
||||||
import { Logger, Storage, Collection, Dictionary } from '@freearhey/core'
|
import { Logger, Storage, Collection } from '@freearhey/core'
|
||||||
import { IssueLoader, HTMLTable, Markdown } from '../../core'
|
import { IssueLoader, HTMLTable, Markdown } from '../../core'
|
||||||
import { Issue, Site } from '../../models'
|
import { Issue, Site } from '../../models'
|
||||||
import { SITES_DIR, DOT_SITES_DIR } from '../../constants'
|
import { SITES_DIR, DOT_SITES_DIR } from '../../constants'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
|
|
||||||
async function main() {
|
async function main() {
|
||||||
const logger = new Logger({ disabled: true })
|
const logger = new Logger({ disabled: true })
|
||||||
const loader = new IssueLoader()
|
const loader = new IssueLoader()
|
||||||
const storage = new Storage(SITES_DIR)
|
const storage = new Storage(SITES_DIR)
|
||||||
const sites = new Collection()
|
const sites = new Collection()
|
||||||
|
|
||||||
logger.info('loading list of sites')
|
logger.info('loading list of sites')
|
||||||
const folders = await storage.list('*/')
|
const folders = await storage.list('*/')
|
||||||
|
|
||||||
logger.info('loading issues...')
|
logger.info('loading issues...')
|
||||||
const issues = await loadIssues(loader)
|
const issues = await loadIssues(loader)
|
||||||
|
|
||||||
logger.info('putting the data together...')
|
logger.info('putting the data together...')
|
||||||
folders.forEach((domain: string) => {
|
folders.forEach((domain: string) => {
|
||||||
const filteredIssues = issues.filter((issue: Issue) => domain === issue.data.get('site'))
|
const filteredIssues = issues.filter((issue: Issue) => domain === issue.data.get('site'))
|
||||||
const site = new Site({
|
const site = new Site({
|
||||||
domain,
|
domain,
|
||||||
issues: filteredIssues
|
issues: filteredIssues
|
||||||
})
|
})
|
||||||
|
|
||||||
sites.add(site)
|
sites.add(site)
|
||||||
})
|
})
|
||||||
|
|
||||||
logger.info('creating sites table...')
|
logger.info('creating sites table...')
|
||||||
let data = new Collection()
|
const data = new Collection()
|
||||||
sites.forEach((site: Site) => {
|
sites.forEach((site: Site) => {
|
||||||
data.add([
|
data.add([
|
||||||
`<a href="sites/${site.domain}">${site.domain}</a>`,
|
`<a href="sites/${site.domain}">${site.domain}</a>`,
|
||||||
site.getStatus().emoji,
|
site.getStatus().emoji,
|
||||||
site.getIssues().all().join(', ')
|
site.getIssues().all().join(', ')
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
const table = new HTMLTable(data.all(), [{ name: 'Site' }, { name: 'Status' }, { name: 'Notes' }])
|
const table = new HTMLTable(data.all(), [{ name: 'Site' }, { name: 'Status' }, { name: 'Notes' }])
|
||||||
|
|
||||||
const readmeStorage = new Storage(DOT_SITES_DIR)
|
const readmeStorage = new Storage(DOT_SITES_DIR)
|
||||||
await readmeStorage.save('_table.md', table.toString())
|
await readmeStorage.save('_table.md', table.toString())
|
||||||
|
|
||||||
logger.info('updating sites.md...')
|
logger.info('updating sites.md...')
|
||||||
const configPath = path.join(DOT_SITES_DIR, 'config.json')
|
const configPath = path.join(DOT_SITES_DIR, 'config.json')
|
||||||
const sitesMarkdown = new Markdown(configPath)
|
const sitesMarkdown = new Markdown(configPath)
|
||||||
sitesMarkdown.compile()
|
sitesMarkdown.compile()
|
||||||
}
|
}
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
||||||
async function loadIssues(loader: IssueLoader) {
|
async function loadIssues(loader: IssueLoader) {
|
||||||
const issuesWithStatusWarning = await loader.load({ labels: ['broken guide', 'status:warning'] })
|
const issuesWithStatusWarning = await loader.load({ labels: ['broken guide', 'status:warning'] })
|
||||||
const issuesWithStatusDown = await loader.load({ labels: ['broken guide', 'status:down'] })
|
const issuesWithStatusDown = await loader.load({ labels: ['broken guide', 'status:down'] })
|
||||||
|
|
||||||
return issuesWithStatusWarning.concat(issuesWithStatusDown)
|
return issuesWithStatusWarning.concat(issuesWithStatusDown)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const dayjs = require('dayjs')
|
import dayjs from 'dayjs'
|
||||||
const utc = require('dayjs/plugin/utc')
|
import utc from 'dayjs/plugin/utc'
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = {}
|
const date = {}
|
||||||
|
@ -10,4 +11,4 @@ date.getUTC = function (d = null) {
|
||||||
return dayjs.utc().startOf('d')
|
return dayjs.utc().startOf('d')
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = date
|
export default date
|
||||||
|
|
|
@ -1,46 +1,46 @@
|
||||||
type Column = {
|
type Column = {
|
||||||
name: string
|
name: string
|
||||||
nowrap?: boolean
|
nowrap?: boolean
|
||||||
align?: string
|
align?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type DataItem = string[]
|
type DataItem = string[]
|
||||||
|
|
||||||
export class HTMLTable {
|
export class HTMLTable {
|
||||||
data: DataItem[]
|
data: DataItem[]
|
||||||
columns: Column[]
|
columns: Column[]
|
||||||
|
|
||||||
constructor(data: DataItem[], columns: Column[]) {
|
constructor(data: DataItem[], columns: Column[]) {
|
||||||
this.data = data
|
this.data = data
|
||||||
this.columns = columns
|
this.columns = columns
|
||||||
}
|
}
|
||||||
|
|
||||||
toString() {
|
toString() {
|
||||||
let output = '<table>\n'
|
let output = '<table>\r\n'
|
||||||
|
|
||||||
output += ' <thead>\n <tr>'
|
output += ' <thead>\r\n <tr>'
|
||||||
for (const column of this.columns) {
|
for (const column of this.columns) {
|
||||||
output += `<th align="left">${column.name}</th>`
|
output += `<th align="left">${column.name}</th>`
|
||||||
}
|
}
|
||||||
output += '</tr>\n </thead>\n'
|
output += '</tr>\r\n </thead>\r\n'
|
||||||
|
|
||||||
output += ' <tbody>\n'
|
output += ' <tbody>\r\n'
|
||||||
for (const item of this.data) {
|
for (const item of this.data) {
|
||||||
output += ' <tr>'
|
output += ' <tr>'
|
||||||
let i = 0
|
let i = 0
|
||||||
for (const prop in item) {
|
for (const prop in item) {
|
||||||
const column = this.columns[i]
|
const column = this.columns[i]
|
||||||
const nowrap = column.nowrap ? ' nowrap' : ''
|
const nowrap = column.nowrap ? ' nowrap' : ''
|
||||||
const align = column.align ? ` align="${column.align}"` : ''
|
const align = column.align ? ` align="${column.align}"` : ''
|
||||||
output += `<td${align}${nowrap}>${item[prop]}</td>`
|
output += `<td${align}${nowrap}>${item[prop]}</td>`
|
||||||
i++
|
i++
|
||||||
}
|
}
|
||||||
output += '</tr>\n'
|
output += '</tr>\r\n'
|
||||||
}
|
}
|
||||||
output += ' </tbody>\n'
|
output += ' </tbody>\r\n'
|
||||||
|
|
||||||
output += '</table>'
|
output += '</table>'
|
||||||
|
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,41 @@
|
||||||
import { Collection } from '@freearhey/core'
|
import { Collection } from '@freearhey/core'
|
||||||
import { restEndpointMethods } from '@octokit/plugin-rest-endpoint-methods'
|
import { restEndpointMethods } from '@octokit/plugin-rest-endpoint-methods'
|
||||||
import { paginateRest } from '@octokit/plugin-paginate-rest'
|
import { paginateRest } from '@octokit/plugin-paginate-rest'
|
||||||
import { Octokit } from '@octokit/core'
|
import { Octokit } from '@octokit/core'
|
||||||
import { IssueParser } from './'
|
import { IssueParser } from './'
|
||||||
import { TESTING, OWNER, REPO } from '../constants'
|
import { TESTING, OWNER, REPO } from '../constants'
|
||||||
|
|
||||||
const CustomOctokit = Octokit.plugin(paginateRest, restEndpointMethods)
|
const CustomOctokit = Octokit.plugin(paginateRest, restEndpointMethods)
|
||||||
const octokit = new CustomOctokit()
|
const octokit = new CustomOctokit()
|
||||||
|
|
||||||
export class IssueLoader {
|
export class IssueLoader {
|
||||||
async load({ labels }: { labels: string[] | string }) {
|
async load({ labels }: { labels: string[] | string }) {
|
||||||
labels = Array.isArray(labels) ? labels.join(',') : labels
|
labels = Array.isArray(labels) ? labels.join(',') : labels
|
||||||
let issues: object[] = []
|
let issues: object[] = []
|
||||||
if (TESTING) {
|
if (TESTING) {
|
||||||
switch (labels) {
|
switch (labels) {
|
||||||
case 'broken guide,status:warning':
|
case 'broken guide,status:warning':
|
||||||
issues = require('../../tests/__data__/input/issues/broken_guide_warning.js')
|
issues = (await import('../../tests/__data__/input/issues/broken_guide_warning.mjs'))
|
||||||
break
|
.default
|
||||||
case 'broken guide,status:down':
|
break
|
||||||
issues = require('../../tests/__data__/input/issues/broken_guide_down.js')
|
case 'broken guide,status:down':
|
||||||
break
|
issues = (await import('../../tests/__data__/input/issues/broken_guide_down.mjs')).default
|
||||||
}
|
break
|
||||||
} else {
|
}
|
||||||
issues = await octokit.paginate(octokit.rest.issues.listForRepo, {
|
} else {
|
||||||
owner: OWNER,
|
issues = await octokit.paginate(octokit.rest.issues.listForRepo, {
|
||||||
repo: REPO,
|
owner: OWNER,
|
||||||
per_page: 100,
|
repo: REPO,
|
||||||
labels,
|
per_page: 100,
|
||||||
headers: {
|
labels,
|
||||||
'X-GitHub-Api-Version': '2022-11-28'
|
headers: {
|
||||||
}
|
'X-GitHub-Api-Version': '2022-11-28'
|
||||||
})
|
}
|
||||||
}
|
})
|
||||||
|
}
|
||||||
const parser = new IssueParser()
|
|
||||||
|
const parser = new IssueParser()
|
||||||
return new Collection(issues).map(parser.parse)
|
|
||||||
}
|
return new Collection(issues).map(parser.parse)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,34 +1,34 @@
|
||||||
import { Dictionary } from '@freearhey/core'
|
import { Dictionary } from '@freearhey/core'
|
||||||
import { Issue } from '../models'
|
import { Issue } from '../models'
|
||||||
|
|
||||||
const FIELDS = new Dictionary({
|
const FIELDS = new Dictionary({
|
||||||
Site: 'site'
|
Site: 'site'
|
||||||
})
|
})
|
||||||
|
|
||||||
export class IssueParser {
|
export class IssueParser {
|
||||||
parse(issue: { number: number; body: string; labels: { name: string }[] }): Issue {
|
parse(issue: { number: number; body: string; labels: { name: string }[] }): Issue {
|
||||||
const fields = issue.body.split('###')
|
const fields = issue.body.split('###')
|
||||||
|
|
||||||
const data = new Dictionary()
|
const data = new Dictionary()
|
||||||
fields.forEach((field: string) => {
|
fields.forEach((field: string) => {
|
||||||
let parsed = field.split(/\r?\n/).filter(Boolean)
|
const parsed = field.split(/\r?\n/).filter(Boolean)
|
||||||
let _label = parsed.shift()
|
let _label = parsed.shift()
|
||||||
_label = _label ? _label.trim() : ''
|
_label = _label ? _label.trim() : ''
|
||||||
let _value = parsed.join('\r\n')
|
let _value = parsed.join('\r\n')
|
||||||
_value = _value ? _value.trim() : ''
|
_value = _value ? _value.trim() : ''
|
||||||
|
|
||||||
if (!_label || !_value) return data
|
if (!_label || !_value) return data
|
||||||
|
|
||||||
const id: string = FIELDS.get(_label)
|
const id: string = FIELDS.get(_label)
|
||||||
const value: string = _value === '_No response_' || _value === 'None' ? '' : _value
|
const value: string = _value === '_No response_' || _value === 'None' ? '' : _value
|
||||||
|
|
||||||
if (!id) return
|
if (!id) return
|
||||||
|
|
||||||
data.set(id, value)
|
data.set(id, value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const labels = issue.labels.map(label => label.name)
|
const labels = issue.labels.map(label => label.name)
|
||||||
|
|
||||||
return new Issue({ number: issue.number, labels, data })
|
return new Issue({ number: issue.number, labels, data })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import markdownInclude from 'markdown-include'
|
import markdownInclude from 'markdown-include'
|
||||||
|
|
||||||
export class Markdown {
|
export class Markdown {
|
||||||
filepath: string
|
filepath: string
|
||||||
|
|
||||||
constructor(filepath: string) {
|
constructor(filepath: string) {
|
||||||
this.filepath = filepath
|
this.filepath = filepath
|
||||||
}
|
}
|
||||||
|
|
||||||
compile() {
|
compile() {
|
||||||
markdownInclude.compileFiles(this.filepath)
|
markdownInclude.compileFiles(this.filepath)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { Storage, Collection, DateTime, Logger } from '@freearhey/core'
|
import { Storage, Collection, DateTime, Logger } from '@freearhey/core'
|
||||||
import { ChannelsParser, ConfigLoader, ApiChannel, Queue } from './'
|
import { ChannelsParser, ConfigLoader, ApiChannel, Queue } from './'
|
||||||
import { SITES_DIR, DATA_DIR } from '../constants'
|
import { SITES_DIR, DATA_DIR } from '../constants'
|
||||||
import { Channel, SiteConfig } from 'epg-grabber'
|
import { SiteConfig } from 'epg-grabber'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { GrabOptions } from '../commands/epg/grab'
|
import { GrabOptions } from '../commands/epg/grab'
|
||||||
|
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
export * from './issue'
|
export * from './issue'
|
||||||
export * from './site'
|
export * from './site'
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
import { Dictionary } from '@freearhey/core'
|
import { Dictionary } from '@freearhey/core'
|
||||||
import { OWNER, REPO } from '../constants'
|
import { OWNER, REPO } from '../constants'
|
||||||
|
|
||||||
type IssueProps = {
|
type IssueProps = {
|
||||||
number: number
|
number: number
|
||||||
labels: string[]
|
labels: string[]
|
||||||
data: Dictionary
|
data: Dictionary
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Issue {
|
export class Issue {
|
||||||
number: number
|
number: number
|
||||||
labels: string[]
|
labels: string[]
|
||||||
data: Dictionary
|
data: Dictionary
|
||||||
|
|
||||||
constructor({ number, labels, data }: IssueProps) {
|
constructor({ number, labels, data }: IssueProps) {
|
||||||
this.number = number
|
this.number = number
|
||||||
this.labels = labels
|
this.labels = labels
|
||||||
this.data = data
|
this.data = data
|
||||||
}
|
}
|
||||||
|
|
||||||
getURL() {
|
getURL() {
|
||||||
return `https://github.com/${OWNER}/${REPO}/issues/${this.number}`
|
return `https://github.com/${OWNER}/${REPO}/issues/${this.number}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,57 +1,57 @@
|
||||||
import { Collection } from '@freearhey/core'
|
import { Collection } from '@freearhey/core'
|
||||||
import { Issue } from './'
|
import { Issue } from './'
|
||||||
|
|
||||||
enum StatusCode {
|
enum StatusCode {
|
||||||
DOWN = 'down',
|
DOWN = 'down',
|
||||||
WARNING = 'warning',
|
WARNING = 'warning',
|
||||||
OK = 'ok'
|
OK = 'ok'
|
||||||
}
|
}
|
||||||
|
|
||||||
type Status = {
|
type Status = {
|
||||||
code: StatusCode
|
code: StatusCode
|
||||||
emoji: string
|
emoji: string
|
||||||
}
|
}
|
||||||
|
|
||||||
type SiteProps = {
|
type SiteProps = {
|
||||||
domain: string
|
domain: string
|
||||||
issues: Collection
|
issues: Collection
|
||||||
}
|
}
|
||||||
|
|
||||||
export class Site {
|
export class Site {
|
||||||
domain: string
|
domain: string
|
||||||
issues: Collection
|
issues: Collection
|
||||||
|
|
||||||
constructor({ domain, issues }: SiteProps) {
|
constructor({ domain, issues }: SiteProps) {
|
||||||
this.domain = domain
|
this.domain = domain
|
||||||
this.issues = issues
|
this.issues = issues
|
||||||
}
|
}
|
||||||
|
|
||||||
getStatus(): Status {
|
getStatus(): Status {
|
||||||
const issuesWithStatusDown = this.issues.filter((issue: Issue) =>
|
const issuesWithStatusDown = this.issues.filter((issue: Issue) =>
|
||||||
issue.labels.find(label => label === 'status:down')
|
issue.labels.find(label => label === 'status:down')
|
||||||
)
|
)
|
||||||
if (issuesWithStatusDown.notEmpty())
|
if (issuesWithStatusDown.notEmpty())
|
||||||
return {
|
return {
|
||||||
code: StatusCode.DOWN,
|
code: StatusCode.DOWN,
|
||||||
emoji: '🔴'
|
emoji: '🔴'
|
||||||
}
|
}
|
||||||
|
|
||||||
const issuesWithStatusWarning = this.issues.filter((issue: Issue) =>
|
const issuesWithStatusWarning = this.issues.filter((issue: Issue) =>
|
||||||
issue.labels.find(label => label === 'status:warning')
|
issue.labels.find(label => label === 'status:warning')
|
||||||
)
|
)
|
||||||
if (issuesWithStatusWarning.notEmpty())
|
if (issuesWithStatusWarning.notEmpty())
|
||||||
return {
|
return {
|
||||||
code: StatusCode.WARNING,
|
code: StatusCode.WARNING,
|
||||||
emoji: '🟡'
|
emoji: '🟡'
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
code: StatusCode.OK,
|
code: StatusCode.OK,
|
||||||
emoji: '🟢'
|
emoji: '🟢'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getIssues(): Collection {
|
getIssues(): Collection {
|
||||||
return this.issues.map((issue: Issue) => issue.getURL())
|
return this.issues.map((issue: Issue) => issue.getURL())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,7 +92,7 @@ function parseItems(content, channel) {
|
||||||
const [, channelId] = channel.site_id.split('#')
|
const [, channelId] = channel.site_id.split('#')
|
||||||
const channelData = data.schedule.find(i => i.channel == channelId)
|
const channelData = data.schedule.find(i => i.channel == channelId)
|
||||||
return channelData.listing && Array.isArray(channelData.listing) ? channelData.listing : []
|
return channelData.listing && Array.isArray(channelData.listing) ? channelData.listing : []
|
||||||
} catch (err) {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
const { parser, url, request } = require('./awilime.com.config.js')
|
const { parser, url } = require('./awilime.com.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const cheerio = require('cheerio')
|
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
|
@ -62,7 +61,7 @@ function parseItems(content) {
|
||||||
let data
|
let data
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(content)
|
data = JSON.parse(content)
|
||||||
} catch (error) {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,4 @@
|
||||||
const { parser, url } = require('./beinsports.com.config.js')
|
const { parser, url } = require('./beinsports.com.config.js')
|
||||||
const fs = require('fs')
|
|
||||||
const path = require('path')
|
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
|
@ -1,54 +1,55 @@
|
||||||
const axios = require('axios');
|
const cheerio = require('cheerio')
|
||||||
const cheerio = require('cheerio');
|
const dayjs = require('dayjs')
|
||||||
const dayjs = require('dayjs');
|
const utc = require('dayjs/plugin/utc')
|
||||||
const utc = require('dayjs/plugin/utc');
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const timezone = require('dayjs/plugin/timezone');
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat');
|
|
||||||
|
dayjs.extend(utc)
|
||||||
dayjs.extend(utc);
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(timezone);
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(customParseFormat);
|
|
||||||
|
module.exports = {
|
||||||
module.exports = {
|
site: 'chada.ma',
|
||||||
site: 'chada.ma',
|
channels: 'chada.ma.channels.xml',
|
||||||
channels: 'chada.ma.channels.xml',
|
days: 1,
|
||||||
days: 1,
|
request: {
|
||||||
request: {
|
cache: {
|
||||||
cache: {
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
}
|
||||||
}
|
},
|
||||||
},
|
url() {
|
||||||
url() {
|
return 'https://chada.ma/fr/chada-tv/grille-tv/'
|
||||||
return 'https://chada.ma/fr/chada-tv/grille-tv/';
|
},
|
||||||
},
|
parser: function ({ content }) {
|
||||||
parser: function ({ content }) {
|
const $ = cheerio.load(content)
|
||||||
const $ = cheerio.load(content);
|
const programs = []
|
||||||
const programs = [];
|
|
||||||
|
$('#stopfix .posts-area h2').each((i, element) => {
|
||||||
$('#stopfix .posts-area h2').each((i, element) => {
|
const timeRange = $(element).text().trim()
|
||||||
const timeRange = $(element).text().trim();
|
const [start, stop] = timeRange.split(' - ').map(t => parseProgramTime(t.trim()))
|
||||||
const [start, stop] = timeRange.split(' - ').map(t => parseProgramTime(t.trim()));
|
|
||||||
|
const titleElement = $(element).next('div').next('h3')
|
||||||
const titleElement = $(element).next('div').next('h3');
|
const title = titleElement.text().trim()
|
||||||
const title = titleElement.text().trim();
|
|
||||||
|
const description = titleElement.next('div').text().trim() || 'No description available'
|
||||||
const description = titleElement.next('div').text().trim() || 'No description available';
|
|
||||||
|
programs.push({
|
||||||
programs.push({
|
title,
|
||||||
title,
|
description,
|
||||||
description,
|
start,
|
||||||
start,
|
stop
|
||||||
stop
|
})
|
||||||
});
|
})
|
||||||
});
|
|
||||||
|
return programs
|
||||||
return programs;
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
function parseProgramTime(timeStr) {
|
||||||
function parseProgramTime(timeStr) {
|
const timeZone = 'Africa/Casablanca'
|
||||||
const timeZone = 'Africa/Casablanca';
|
const currentDate = dayjs().format('YYYY-MM-DD')
|
||||||
const currentDate = dayjs().format('YYYY-MM-DD');
|
|
||||||
|
return dayjs
|
||||||
return dayjs.tz(`${currentDate} ${timeStr}`, 'YYYY-MM-DD HH:mm', timeZone).format('YYYY-MM-DDTHH:mm:ssZ');
|
.tz(`${currentDate} ${timeStr}`, 'YYYY-MM-DD HH:mm', timeZone)
|
||||||
}
|
.format('YYYY-MM-DDTHH:mm:ssZ')
|
||||||
|
}
|
||||||
|
|
|
@ -1,60 +1,58 @@
|
||||||
const { parser, url } = require('./chada.ma.config.js')
|
const { parser, url } = require('./chada.ma.config.js')
|
||||||
const axios = require('axios')
|
const dayjs = require('dayjs')
|
||||||
const dayjs = require('dayjs')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const cheerio = require('cheerio')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
dayjs.extend(utc)
|
||||||
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(timezone)
|
|
||||||
dayjs.extend(customParseFormat)
|
jest.mock('axios')
|
||||||
|
|
||||||
jest.mock('axios')
|
const mockHtmlContent = `
|
||||||
|
<div class="pm0 col-md-8" id="stopfix">
|
||||||
const mockHtmlContent = `
|
<h2 class="posts-date">Programmes d'Aujourd'hui</h2>
|
||||||
<div class="pm0 col-md-8" id="stopfix">
|
<div class="posts-area">
|
||||||
<h2 class="posts-date">Programmes d'Aujourd'hui</h2>
|
<h2> <i class="fas fa-circle"></i>00:00 - 09:00</h2>
|
||||||
<div class="posts-area">
|
<div class="relativeme">
|
||||||
<h2> <i class="fas fa-circle"></i>00:00 - 09:00</h2>
|
<a href="https://chada.ma/fr/emissions/bloc-prime-clips/">
|
||||||
<div class="relativeme">
|
<img class="programthumb" src="https://chada.ma/wp-content/uploads/2023/11/Autres-slides-clips-la-couverture.jpg">
|
||||||
<a href="https://chada.ma/fr/emissions/bloc-prime-clips/">
|
</a>
|
||||||
<img class="programthumb" src="https://chada.ma/wp-content/uploads/2023/11/Autres-slides-clips-la-couverture.jpg">
|
</div>
|
||||||
</a>
|
<h3>Bloc Prime + Clips</h3>
|
||||||
</div>
|
<div class="authorbox"></div>
|
||||||
<h3>Bloc Prime + Clips</h3>
|
<div class="ssprogramme row"></div>
|
||||||
<div class="authorbox"></div>
|
</div>
|
||||||
<div class="ssprogramme row"></div>
|
</div>
|
||||||
</div>
|
`
|
||||||
</div>
|
|
||||||
`;
|
it('can generate valid url', () => {
|
||||||
|
expect(url()).toBe('https://chada.ma/fr/chada-tv/grille-tv/')
|
||||||
it('can generate valid url', () => {
|
})
|
||||||
expect(url()).toBe('https://chada.ma/fr/chada-tv/grille-tv/')
|
|
||||||
});
|
it('can parse response', () => {
|
||||||
|
const content = mockHtmlContent
|
||||||
it('can parse response', () => {
|
|
||||||
const content = mockHtmlContent
|
const result = parser({ content }).map(p => {
|
||||||
|
p.start = dayjs(p.start).tz('Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ')
|
||||||
const result = parser({ content }).map(p => {
|
p.stop = dayjs(p.stop).tz('Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ')
|
||||||
p.start = dayjs(p.start).tz('Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ')
|
return p
|
||||||
p.stop = dayjs(p.stop).tz('Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ')
|
})
|
||||||
return p
|
|
||||||
})
|
expect(result).toMatchObject([
|
||||||
|
{
|
||||||
expect(result).toMatchObject([
|
title: 'Bloc Prime + Clips',
|
||||||
{
|
description: 'No description available',
|
||||||
title: "Bloc Prime + Clips",
|
start: dayjs.tz('00:00', 'HH:mm', 'Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ'),
|
||||||
description: "No description available",
|
stop: dayjs.tz('09:00', 'HH:mm', 'Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ')
|
||||||
start: dayjs.tz('00:00', 'HH:mm', 'Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ'),
|
}
|
||||||
stop: dayjs.tz('09:00', 'HH:mm', 'Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ')
|
])
|
||||||
}
|
})
|
||||||
])
|
|
||||||
})
|
it('can handle empty guide', () => {
|
||||||
|
const result = parser({
|
||||||
it('can handle empty guide', () => {
|
content: '<div class="pm0 col-md-8" id="stopfix"><div class="posts-area"></div></div>'
|
||||||
const result = parser({
|
})
|
||||||
content: '<div class="pm0 col-md-8" id="stopfix"><div class="posts-area"></div></div>'
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
|
||||||
})
|
|
||||||
|
|
|
@ -16,9 +16,12 @@ module.exports = {
|
||||||
const start = parseStart(item)
|
const start = parseStart(item)
|
||||||
const stop = parseStop(item, start)
|
const stop = parseStop(item, start)
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.season?.serie?.title ? item.season.serie.title : item.title,
|
title: item.title,
|
||||||
|
subTitle: item.season?.serie?.title,
|
||||||
category: item.genreDetailed,
|
category: item.genreDetailed,
|
||||||
description: item.synopsis,
|
description: item.synopsis,
|
||||||
|
season: parseSeason(item),
|
||||||
|
episode: parseEpisode(item),
|
||||||
image: parseImage(item),
|
image: parseImage(item),
|
||||||
start: start.toJSON(),
|
start: start.toJSON(),
|
||||||
stop: stop.toJSON()
|
stop: stop.toJSON()
|
||||||
|
@ -29,7 +32,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const html = await axios
|
const html = await axios
|
||||||
.get(`https://chaines-tv.orange.fr/programme-tv?filtres=all`)
|
.get('https://chaines-tv.orange.fr/programme-tv?filtres=all')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
|
@ -61,6 +64,14 @@ function parseStop(item, start) {
|
||||||
return start.add(item.duration, 's')
|
return start.add(item.duration, 's')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseSeason(item) {
|
||||||
|
return item.season?.number
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseEpisode(item) {
|
||||||
|
return item.episodeNumber
|
||||||
|
}
|
||||||
|
|
||||||
function parseItems(content, channel) {
|
function parseItems(content, channel) {
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,9 @@ it('can parse response', () => {
|
||||||
start: '2021-11-07T23:35:00.000Z',
|
start: '2021-11-07T23:35:00.000Z',
|
||||||
stop: '2021-11-08T00:20:00.000Z',
|
stop: '2021-11-08T00:20:00.000Z',
|
||||||
title: 'Tête de liste',
|
title: 'Tête de liste',
|
||||||
|
subTitle: 'Esprits criminels',
|
||||||
|
season: 10,
|
||||||
|
episode: 12,
|
||||||
description:
|
description:
|
||||||
"Un tueur en série prend un plaisir pervers à prévenir les autorités de Tallahassee avant chaque nouveau meurtre. Rossi apprend le décès d'un de ses vieux amis.",
|
"Un tueur en série prend un plaisir pervers à prévenir les autorités de Tallahassee avant chaque nouveau meurtre. Rossi apprend le décès d'un de ses vieux amis.",
|
||||||
category: 'Série Suspense',
|
category: 'Série Suspense',
|
||||||
|
|
|
@ -40,7 +40,7 @@ module.exports = {
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://contenthub-api.eco.astro.com.my/channel/all.json`)
|
.get('https://contenthub-api.eco.astro.com.my/channel/all.json')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
|
@ -85,7 +85,7 @@ function parseItems(content, date) {
|
||||||
const schedules = data.response.schedule
|
const schedules = data.response.schedule
|
||||||
|
|
||||||
return schedules[date.format('YYYY-MM-DD')] || []
|
return schedules[date.format('YYYY-MM-DD')] || []
|
||||||
} catch (e) {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,81 +1,85 @@
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'cosmotetv.gr',
|
site: 'cosmotetv.gr',
|
||||||
days: 5,
|
days: 5,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
},
|
},
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: {
|
headers: {
|
||||||
'referer': 'https://www.cosmotetv.gr/',
|
referer: 'https://www.cosmotetv.gr/',
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
'User-Agent':
|
||||||
'Accept': '*/*',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36',
|
||||||
'Accept-Language': 'en-US,en;q=0.9',
|
Accept: '*/*',
|
||||||
'Accept-Encoding': 'gzip, deflate, br, zstd',
|
'Accept-Language': 'en-US,en;q=0.9',
|
||||||
'Origin': 'https://www.cosmotetv.gr',
|
'Accept-Encoding': 'gzip, deflate, br, zstd',
|
||||||
'Sec-Ch-Ua': '"Not.A/Brand";v="24", "Chromium";v="131", "Google Chrome";v="131"',
|
Origin: 'https://www.cosmotetv.gr',
|
||||||
'Sec-Ch-Ua-Mobile': '?0',
|
'Sec-Ch-Ua': '"Not.A/Brand";v="24", "Chromium";v="131", "Google Chrome";v="131"',
|
||||||
'Sec-Ch-Ua-Platform': '"Windows"',
|
'Sec-Ch-Ua-Mobile': '?0',
|
||||||
'Sec-Fetch-Dest': 'empty',
|
'Sec-Ch-Ua-Platform': '"Windows"',
|
||||||
'Sec-Fetch-Mode': 'cors',
|
'Sec-Fetch-Dest': 'empty',
|
||||||
'Sec-Fetch-Site': 'cross-site'
|
'Sec-Fetch-Mode': 'cors',
|
||||||
}
|
'Sec-Fetch-Site': 'cross-site'
|
||||||
},
|
}
|
||||||
url: function ({date, channel}) {
|
},
|
||||||
const startOfDay = dayjs(date).startOf('day').utc().unix()
|
url: function ({ date, channel }) {
|
||||||
const endOfDay = dayjs(date).endOf('day').utc().unix()
|
const startOfDay = dayjs(date).startOf('day').utc().unix()
|
||||||
return `https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/listings/el?from=${startOfDay}&to=${endOfDay}&callSigns=${channel.site_id}&endingIncludedInRange=false`
|
const endOfDay = dayjs(date).endOf('day').utc().unix()
|
||||||
},
|
return `https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/listings/el?from=${startOfDay}&to=${endOfDay}&callSigns=${channel.site_id}&endingIncludedInRange=false`
|
||||||
parser: function ({ date, content }) {
|
},
|
||||||
let programs = []
|
parser: function ({ content }) {
|
||||||
const data = JSON.parse(content)
|
let programs = []
|
||||||
data.channels.forEach(channel => {
|
const data = JSON.parse(content)
|
||||||
channel.items.forEach(item => {
|
data.channels.forEach(channel => {
|
||||||
const start = dayjs(item.startTime).utc().toISOString()
|
channel.items.forEach(item => {
|
||||||
const stop = dayjs(item.endTime).utc().toISOString()
|
const start = dayjs(item.startTime).utc().toISOString()
|
||||||
programs.push({
|
const stop = dayjs(item.endTime).utc().toISOString()
|
||||||
title: item.title,
|
programs.push({
|
||||||
description: item.description || 'No description available',
|
title: item.title,
|
||||||
category: item.qoe.genre,
|
description: item.description || 'No description available',
|
||||||
image: item.thumbnails.standard,
|
category: item.qoe.genre,
|
||||||
start,
|
image: item.thumbnails.standard,
|
||||||
stop
|
start,
|
||||||
})
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
return programs
|
})
|
||||||
},
|
return programs
|
||||||
async channels() {
|
},
|
||||||
const axios = require('axios')
|
async channels() {
|
||||||
try {
|
const axios = require('axios')
|
||||||
const response = await axios.get('https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/channels/all/el', {
|
try {
|
||||||
headers: this.request.headers
|
const response = await axios.get(
|
||||||
})
|
'https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/channels/all/el',
|
||||||
const data = response.data
|
{
|
||||||
|
headers: this.request.headers
|
||||||
if (data && data.channels) {
|
}
|
||||||
return data.channels.map(item => ({
|
)
|
||||||
lang: 'el',
|
const data = response.data
|
||||||
site_id: item.callSign,
|
|
||||||
name: item.title,
|
if (data && data.channels) {
|
||||||
//logo: item.logos.square
|
return data.channels.map(item => ({
|
||||||
}))
|
lang: 'el',
|
||||||
} else {
|
site_id: item.callSign,
|
||||||
console.error('Unexpected response structure:', data)
|
name: item.title
|
||||||
return []
|
//logo: item.logos.square
|
||||||
}
|
}))
|
||||||
} catch (error) {
|
} else {
|
||||||
console.error('Error fetching channel data:', error)
|
console.error('Unexpected response structure:', data)
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
} catch (error) {
|
||||||
}
|
console.error('Error fetching channel data:', error)
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,81 +1,76 @@
|
||||||
const { parser, url, channels } = require('./cosmotetv.gr.config.js')
|
const { parser, url } = require('./cosmotetv.gr.config.js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const axios = require('axios')
|
|
||||||
|
dayjs.extend(utc)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(timezone)
|
|
||||||
|
jest.mock('axios')
|
||||||
jest.mock('axios')
|
|
||||||
|
const date = dayjs.utc('2024-12-26', 'YYYY-MM-DD').startOf('d')
|
||||||
const date = dayjs.utc('2024-12-26', 'YYYY-MM-DD').startOf('d')
|
const channel = { site_id: 'vouli', xmltv_id: 'HellenicParliamentTV.gr' }
|
||||||
const channel = { site_id: 'vouli', xmltv_id: 'HellenicParliamentTV.gr' }
|
|
||||||
|
const mockEpgData = {
|
||||||
const mockChannelData = {
|
channels: [
|
||||||
"channels": [
|
{
|
||||||
{
|
items: [
|
||||||
"guid": "XTV100000954",
|
{
|
||||||
"title": "ΒΟΥΛΗ HD",
|
startTime: '2024-12-26T23:00:00+00:00',
|
||||||
"callSign": "vouli",
|
endTime: '2024-12-27T00:00:00+00:00',
|
||||||
"logos": {
|
title: 'Τι Λέει ο Νόμος',
|
||||||
"square": "https://tr.static.cdn.cosmotetvott.gr/ote-prod/channel_logos/vouli1-normal.png",
|
description:
|
||||||
"wide": "https://tr.static.cdn.cosmotetvott.gr/ote-prod/channel_logos/vouli1-wide.png"
|
'νημερωτική εκπομπή. Συζήτηση με τους εισηγητές των κομμάτων για το νομοθετικό έργο.',
|
||||||
}
|
qoe: {
|
||||||
}
|
genre: 'Special'
|
||||||
]
|
},
|
||||||
}
|
thumbnails: {
|
||||||
|
standard:
|
||||||
const mockEpgData = {
|
'https://gr-ermou-prod-cache05.static.cdn.cosmotetvott.gr/ote-prod/70/280/040029714812000800_1734415727199.jpg'
|
||||||
"channels": [
|
}
|
||||||
{
|
}
|
||||||
"items": [
|
]
|
||||||
{
|
}
|
||||||
"startTime": "2024-12-26T23:00:00+00:00",
|
]
|
||||||
"endTime": "2024-12-27T00:00:00+00:00",
|
}
|
||||||
"title": "Τι Λέει ο Νόμος",
|
|
||||||
"description": "νημερωτική εκπομπή. Συζήτηση με τους εισηγητές των κομμάτων για το νομοθετικό έργο.",
|
it('can generate valid url', () => {
|
||||||
"qoe": {
|
const startOfDay = dayjs(date).startOf('day').utc().unix()
|
||||||
"genre": "Special"
|
const endOfDay = dayjs(date).endOf('day').utc().unix()
|
||||||
},
|
expect(url({ date, channel })).toBe(
|
||||||
"thumbnails": {
|
`https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/listings/el?from=${startOfDay}&to=${endOfDay}&callSigns=${channel.site_id}&endingIncludedInRange=false`
|
||||||
"standard": "https://gr-ermou-prod-cache05.static.cdn.cosmotetvott.gr/ote-prod/70/280/040029714812000800_1734415727199.jpg"
|
)
|
||||||
}
|
})
|
||||||
}
|
|
||||||
]
|
it('can parse response', () => {
|
||||||
}
|
const content = JSON.stringify(mockEpgData)
|
||||||
]
|
const result = parser({ date, content }).map(p => {
|
||||||
}
|
p.start = dayjs(p.start).toISOString()
|
||||||
|
p.stop = dayjs(p.stop).toISOString()
|
||||||
it('can generate valid url', () => {
|
return p
|
||||||
const startOfDay = dayjs(date).startOf('day').utc().unix()
|
})
|
||||||
const endOfDay = dayjs(date).endOf('day').utc().unix()
|
|
||||||
expect(url({ date, channel })).toBe(`https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/listings/el?from=${startOfDay}&to=${endOfDay}&callSigns=${channel.site_id}&endingIncludedInRange=false`)
|
expect(result).toMatchObject([
|
||||||
})
|
{
|
||||||
|
title: 'Τι Λέει ο Νόμος',
|
||||||
it('can parse response', () => {
|
description:
|
||||||
const content = JSON.stringify(mockEpgData)
|
'νημερωτική εκπομπή. Συζήτηση με τους εισηγητές των κομμάτων για το νομοθετικό έργο.',
|
||||||
const result = parser({ date, content }).map(p => {
|
category: 'Special',
|
||||||
p.start = dayjs(p.start).toISOString()
|
image:
|
||||||
p.stop = dayjs(p.stop).toISOString()
|
'https://gr-ermou-prod-cache05.static.cdn.cosmotetvott.gr/ote-prod/70/280/040029714812000800_1734415727199.jpg',
|
||||||
return p
|
start: '2024-12-26T23:00:00.000Z',
|
||||||
})
|
stop: '2024-12-27T00:00:00.000Z'
|
||||||
|
}
|
||||||
expect(result).toMatchObject([
|
])
|
||||||
{
|
})
|
||||||
title: "Τι Λέει ο Νόμος",
|
|
||||||
description: "νημερωτική εκπομπή. Συζήτηση με τους εισηγητές των κομμάτων για το νομοθετικό έργο.",
|
it('can handle empty guide', () => {
|
||||||
category: "Special",
|
const result = parser({
|
||||||
image: "https://gr-ermou-prod-cache05.static.cdn.cosmotetvott.gr/ote-prod/70/280/040029714812000800_1734415727199.jpg",
|
date,
|
||||||
start: "2024-12-26T23:00:00.000Z",
|
channel,
|
||||||
stop: "2024-12-27T00:00:00.000Z"
|
content: '{"date":"2024-12-26","categories":[],"channels":[]}'
|
||||||
}
|
})
|
||||||
])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
|
||||||
const result = parser({ date, channel, content: '{"date":"2024-12-26","categories":[],"channels":[]}' });
|
|
||||||
expect(result).toMatchObject([])
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,102 +1,114 @@
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'cubmu.com',
|
site: 'cubmu.com',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel, date }) {
|
url({ channel, date }) {
|
||||||
return `https://servicebuss.transvision.co.id/v2/cms/getEPGData?app_id=cubmu&tvs_platform_id=standalone&schedule_date=${date.format('YYYY-MM-DD')}&channel_id=${channel.site_id}`
|
return `https://servicebuss.transvision.co.id/v2/cms/getEPGData?app_id=cubmu&tvs_platform_id=standalone&schedule_date=${date.format(
|
||||||
},
|
'YYYY-MM-DD'
|
||||||
parser({ content, channel }) {
|
)}&channel_id=${channel.site_id}`
|
||||||
const programs = []
|
},
|
||||||
const items = parseItems(content)
|
parser({ content, channel }) {
|
||||||
items.forEach(item => {
|
const programs = []
|
||||||
programs.push({
|
const items = parseItems(content)
|
||||||
title: parseTitle(item),
|
items.forEach(item => {
|
||||||
description: parseDescription(item, channel.lang),
|
programs.push({
|
||||||
episode: parseEpisode(item),
|
title: parseTitle(item),
|
||||||
start: parseStart(item).toISOString(),
|
description: parseDescription(item, channel.lang),
|
||||||
stop: parseStop(item).toISOString()
|
episode: parseEpisode(item),
|
||||||
})
|
start: parseStart(item).toISOString(),
|
||||||
})
|
stop: parseStop(item).toISOString()
|
||||||
|
})
|
||||||
return programs
|
})
|
||||||
},
|
|
||||||
async channels({ lang = 'id' }) {
|
return programs
|
||||||
const axios = require('axios')
|
},
|
||||||
const cheerio = require('cheerio')
|
async channels({ lang = 'id' }) {
|
||||||
const result = await axios
|
const axios = require('axios')
|
||||||
.get('https://cubmu.com/live-tv')
|
const cheerio = require('cheerio')
|
||||||
.then(response => response.data)
|
const result = await axios
|
||||||
.catch(console.error)
|
.get('https://cubmu.com/live-tv')
|
||||||
|
.then(response => response.data)
|
||||||
const $ = cheerio.load(result)
|
.catch(console.error)
|
||||||
|
|
||||||
// retrieve service api data
|
const $ = cheerio.load(result)
|
||||||
const config = JSON.parse($('#__NEXT_DATA__').text()).runtimeConfig || {}
|
|
||||||
|
// retrieve service api data
|
||||||
const options = {
|
const config = JSON.parse($('#__NEXT_DATA__').text()).runtimeConfig || {}
|
||||||
headers: {
|
|
||||||
Origin: 'https://cubmu.com',
|
const options = {
|
||||||
Referer: 'https://cubmu.com/live-tv'
|
headers: {
|
||||||
}
|
Origin: 'https://cubmu.com',
|
||||||
}
|
Referer: 'https://cubmu.com/live-tv'
|
||||||
// login to service bus
|
}
|
||||||
const token = await axios
|
}
|
||||||
.post(`https://servicebuss.transvision.co.id/tvs/login/external?email=${config.email}&password=${config.password}&deviceId=${config.deviceId}&deviceType=${config.deviceType}&deviceModel=${config.deviceModel}&deviceToken=&serial=&platformId=${config.platformId}`, options)
|
// login to service bus
|
||||||
.then(response => response.data)
|
await axios
|
||||||
.catch(console.error)
|
.post(
|
||||||
// list channels
|
`https://servicebuss.transvision.co.id/tvs/login/external?email=${config.email}&password=${config.password}&deviceId=${config.deviceId}&deviceType=${config.deviceType}&deviceModel=${config.deviceModel}&deviceToken=&serial=&platformId=${config.platformId}`,
|
||||||
const subscribedChannels = await axios
|
options
|
||||||
.post(`https://servicebuss.transvision.co.id/tvs/subscribe_product/list?platformId=${config.platformId}`, options)
|
)
|
||||||
.then(response => response.data)
|
.then(response => response.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
// list channels
|
||||||
const channels = []
|
const subscribedChannels = await axios
|
||||||
const included = []
|
.post(
|
||||||
if (Array.isArray(subscribedChannels.channelPackageList)) {
|
`https://servicebuss.transvision.co.id/tvs/subscribe_product/list?platformId=${config.platformId}`,
|
||||||
subscribedChannels.channelPackageList.forEach(pkg => {
|
options
|
||||||
pkg.channelList.forEach(channel => {
|
)
|
||||||
if (included.indexOf(channel.id) < 0) {
|
.then(response => response.data)
|
||||||
included.push(channel.id)
|
.catch(console.error)
|
||||||
channels.push({
|
|
||||||
lang,
|
const channels = []
|
||||||
site_id: channel.id,
|
const included = []
|
||||||
name: channel.name
|
if (Array.isArray(subscribedChannels.channelPackageList)) {
|
||||||
})
|
subscribedChannels.channelPackageList.forEach(pkg => {
|
||||||
}
|
pkg.channelList.forEach(channel => {
|
||||||
})
|
if (included.indexOf(channel.id) < 0) {
|
||||||
})
|
included.push(channel.id)
|
||||||
}
|
channels.push({
|
||||||
|
lang,
|
||||||
return channels
|
site_id: channel.id,
|
||||||
}
|
name: channel.name
|
||||||
}
|
})
|
||||||
|
}
|
||||||
function parseItems(content) {
|
})
|
||||||
return content ? JSON.parse(content.trim()).result || [] : []
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseTitle(item) {
|
return channels
|
||||||
return item.scehedule_title
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseDescription(item, lang = 'id') {
|
function parseItems(content) {
|
||||||
return lang === 'id' ? item.schedule_json.primarySynopsis : item.schedule_json.secondarySynopsis
|
return content ? JSON.parse(content.trim()).result || [] : []
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseEpisode(item) {
|
function parseTitle(item) {
|
||||||
return item.schedule_json.episodeName
|
return item.scehedule_title
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart(item) {
|
function parseDescription(item, lang = 'id') {
|
||||||
return dayjs.tz(item.schedule_date, 'YYYY-MM-DD HH:mm:ss', 'Asia/Jakarta')
|
return lang === 'id' ? item.schedule_json.primarySynopsis : item.schedule_json.secondarySynopsis
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStop(item) {
|
function parseEpisode(item) {
|
||||||
return dayjs.tz([item.schedule_date.split(' ')[0], item.schedule_end_time].join(' '), 'YYYY-MM-DD HH:mm:ss', 'Asia/Jakarta')
|
return item.schedule_json.episodeName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function parseStart(item) {
|
||||||
|
return dayjs.tz(item.schedule_date, 'YYYY-MM-DD HH:mm:ss', 'Asia/Jakarta')
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseStop(item) {
|
||||||
|
return dayjs.tz(
|
||||||
|
[item.schedule_date.split(' ')[0], item.schedule_end_time].join(' '),
|
||||||
|
'YYYY-MM-DD HH:mm:ss',
|
||||||
|
'Asia/Jakarta'
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -1,47 +1,47 @@
|
||||||
const { url, parser } = require('./cubmu.com.config.js')
|
const { url, parser } = require('./cubmu.com.config.js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2023-11-05', 'DD/MM/YYYY').startOf('d')
|
const date = dayjs.utc('2023-11-05', 'DD/MM/YYYY').startOf('d')
|
||||||
const channel = { site_id: '4028c68574537fcd0174be43042758d8', xmltv_id: 'TransTV.id', lang: 'id' }
|
const channel = { site_id: '4028c68574537fcd0174be43042758d8', xmltv_id: 'TransTV.id', lang: 'id' }
|
||||||
const channelEn = Object.assign({}, channel, { lang: 'en' })
|
const channelEn = Object.assign({}, channel, { lang: 'en' })
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel, date })).toBe(
|
expect(url({ channel, date })).toBe(
|
||||||
'https://servicebuss.transvision.co.id/v2/cms/getEPGData?app_id=cubmu&tvs_platform_id=standalone&schedule_date=2023-11-05&channel_id=4028c68574537fcd0174be43042758d8'
|
'https://servicebuss.transvision.co.id/v2/cms/getEPGData?app_id=cubmu&tvs_platform_id=standalone&schedule_date=2023-11-05&channel_id=4028c68574537fcd0174be43042758d8'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content =
|
const content =
|
||||||
'{"result":[{"channel_id":"4028c68574537fcd0174be43042758d8","channel_name":"Trans TV","scehedule_title":"CNN Tech News","schedule_date":"2023-11-05 01:30:00","schedule_end_time":"02:00:00","schedule_json":{"availability":0,"channelId":"4028c68574537fcd0174be43042758d8","channelName":"Trans TV","duration":1800,"editable":true,"episodeName":"","imageUrl":"https://cdnjkt2.transvision.co.id:1001/catchup/schedule/thumbnail/4028c68574537fcd0174be43042758d8/4028c6858b8b3621018b9330e3701a7e/458x640","imageUrlWide":"https://cdnjkt2.transvision.co.id:1001/catchup/schedule/thumbnail/4028c68574537fcd0174be43042758d8/4028c6858b8b3621018b9330e3701a7e/320x180","name":"CNN Tech News","ottImageUrl":"","primarySynopsis":"CNN Indonesia Tech News adalah berita teknologi yang membawa pemirsa ke dunia teknologi yang penuh dengan informasi, pendidikan, hiburan sampai informasi kesehatan terkini.","scheduleId":"4028c6858b8b3621018b9330e3701a7e","scheduleTime":"18:30:00","secondarySynopsis":"CNN Indonesia Tech News is tech news brings viewers into the world of technology that provides information, education, entertainment to the latest health information.","startDt":"20231104183000","url":""},"schedule_start_time":"01:30:00"}]}'
|
'{"result":[{"channel_id":"4028c68574537fcd0174be43042758d8","channel_name":"Trans TV","scehedule_title":"CNN Tech News","schedule_date":"2023-11-05 01:30:00","schedule_end_time":"02:00:00","schedule_json":{"availability":0,"channelId":"4028c68574537fcd0174be43042758d8","channelName":"Trans TV","duration":1800,"editable":true,"episodeName":"","imageUrl":"https://cdnjkt2.transvision.co.id:1001/catchup/schedule/thumbnail/4028c68574537fcd0174be43042758d8/4028c6858b8b3621018b9330e3701a7e/458x640","imageUrlWide":"https://cdnjkt2.transvision.co.id:1001/catchup/schedule/thumbnail/4028c68574537fcd0174be43042758d8/4028c6858b8b3621018b9330e3701a7e/320x180","name":"CNN Tech News","ottImageUrl":"","primarySynopsis":"CNN Indonesia Tech News adalah berita teknologi yang membawa pemirsa ke dunia teknologi yang penuh dengan informasi, pendidikan, hiburan sampai informasi kesehatan terkini.","scheduleId":"4028c6858b8b3621018b9330e3701a7e","scheduleTime":"18:30:00","secondarySynopsis":"CNN Indonesia Tech News is tech news brings viewers into the world of technology that provides information, education, entertainment to the latest health information.","startDt":"20231104183000","url":""},"schedule_start_time":"01:30:00"}]}'
|
||||||
|
|
||||||
const idResults = parser({ content, channel })
|
const idResults = parser({ content, channel })
|
||||||
expect(idResults).toMatchObject([
|
expect(idResults).toMatchObject([
|
||||||
{
|
{
|
||||||
start: '2023-11-04T18:30:00.000Z',
|
start: '2023-11-04T18:30:00.000Z',
|
||||||
stop: '2023-11-04T19:00:00.000Z',
|
stop: '2023-11-04T19:00:00.000Z',
|
||||||
title: 'CNN Tech News',
|
title: 'CNN Tech News',
|
||||||
description:
|
description:
|
||||||
'CNN Indonesia Tech News adalah berita teknologi yang membawa pemirsa ke dunia teknologi yang penuh dengan informasi, pendidikan, hiburan sampai informasi kesehatan terkini.'
|
'CNN Indonesia Tech News adalah berita teknologi yang membawa pemirsa ke dunia teknologi yang penuh dengan informasi, pendidikan, hiburan sampai informasi kesehatan terkini.'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
|
|
||||||
const enResults = parser({ content, channel: channelEn })
|
const enResults = parser({ content, channel: channelEn })
|
||||||
expect(enResults).toMatchObject([
|
expect(enResults).toMatchObject([
|
||||||
{
|
{
|
||||||
start: '2023-11-04T18:30:00.000Z',
|
start: '2023-11-04T18:30:00.000Z',
|
||||||
stop: '2023-11-04T19:00:00.000Z',
|
stop: '2023-11-04T19:00:00.000Z',
|
||||||
title: 'CNN Tech News',
|
title: 'CNN Tech News',
|
||||||
description:
|
description:
|
||||||
'CNN Indonesia Tech News is tech news brings viewers into the world of technology that provides information, education, entertainment to the latest health information.'
|
'CNN Indonesia Tech News is tech news brings viewers into the world of technology that provides information, education, entertainment to the latest health information.'
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const results = parser({ content: '' })
|
const results = parser({ content: '' })
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,60 +1,59 @@
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
const cheerio = require('cheerio')
|
|
||||||
|
dayjs.extend(utc)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(customParseFormat)
|
|
||||||
|
module.exports = {
|
||||||
module.exports = {
|
site: 'cyta.com.cy',
|
||||||
site: 'cyta.com.cy',
|
days: 7,
|
||||||
days: 7,
|
request: {
|
||||||
request: {
|
cache: {
|
||||||
cache: {
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
}
|
||||||
}
|
},
|
||||||
},
|
url: function ({ date, channel }) {
|
||||||
url: function ({date, channel}) {
|
// Get the epoch timestamp
|
||||||
// Get the epoch timestamp
|
const todayEpoch = date.startOf('day').utc().valueOf()
|
||||||
const todayEpoch = date.startOf('day').utc().valueOf()
|
// Get the epoch timestamp for the next day
|
||||||
// Get the epoch timestamp for the next day
|
const nextDayEpoch = date.add(1, 'day').startOf('day').utc().valueOf()
|
||||||
const nextDayEpoch = date.add(1, 'day').startOf('day').utc().valueOf()
|
return `https://epg.cyta.com.cy/api/mediacatalog/fetchEpg?startTimeEpoch=${todayEpoch}&endTimeEpoch=${nextDayEpoch}&language=1&channelIds=${channel.site_id}`
|
||||||
return `https://epg.cyta.com.cy/api/mediacatalog/fetchEpg?startTimeEpoch=${todayEpoch}&endTimeEpoch=${nextDayEpoch}&language=1&channelIds=${channel.site_id}`
|
},
|
||||||
},
|
parser: function ({ content }) {
|
||||||
parser: function ({content}) {
|
const data = JSON.parse(content)
|
||||||
const data = JSON.parse(content)
|
const programs = []
|
||||||
const programs = []
|
|
||||||
|
data.channelEpgs.forEach(channel => {
|
||||||
data.channelEpgs.forEach(channel => {
|
channel.epgPlayables.forEach(epg => {
|
||||||
channel.epgPlayables.forEach(epg => {
|
const start = new Date(epg.startTime).toISOString()
|
||||||
const start = new Date(epg.startTime).toISOString();
|
const stop = new Date(epg.endTime).toISOString()
|
||||||
const stop = new Date(epg.endTime).toISOString();
|
|
||||||
|
programs.push({
|
||||||
programs.push({
|
title: epg.name,
|
||||||
title: epg.name,
|
start,
|
||||||
start,
|
stop
|
||||||
stop
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
return programs
|
||||||
return programs
|
},
|
||||||
},
|
async channels() {
|
||||||
async channels() {
|
const axios = require('axios')
|
||||||
const axios = require('axios')
|
const data = await axios
|
||||||
const data = await axios
|
.get('https://epg.cyta.com.cy/api/mediacatalog/fetchChannels?language=1')
|
||||||
.get(`https://epg.cyta.com.cy/api/mediacatalog/fetchChannels?language=1`)
|
.then(r => r.data)
|
||||||
.then(r => r.data)
|
.catch(console.log)
|
||||||
.catch(console.log)
|
|
||||||
|
return data.channels.map(item => {
|
||||||
return data.channels.map(item => {
|
return {
|
||||||
return {
|
lang: 'el',
|
||||||
lang: 'el',
|
site_id: item.id,
|
||||||
site_id: item.id,
|
name: item.name
|
||||||
name: item.name
|
}
|
||||||
}
|
})
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -1,53 +1,49 @@
|
||||||
const { url, parser } = require('./cyta.com.cy.config.js')
|
const { url, parser } = require('./cyta.com.cy.config.js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
const date = dayjs.utc('2025-01-03', 'YYYY-MM-DD').startOf('day')
|
const date = dayjs.utc('2025-01-03', 'YYYY-MM-DD').startOf('day')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '561066',
|
site_id: '561066',
|
||||||
xmltv_id: 'RIK1.cy'
|
xmltv_id: 'RIK1.cy'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
const generatedUrl = url({ date, channel })
|
const generatedUrl = url({ date, channel })
|
||||||
expect(generatedUrl).toBe(
|
expect(generatedUrl).toBe(
|
||||||
'https://epg.cyta.com.cy/api/mediacatalog/fetchEpg?startTimeEpoch=1735862400000&endTimeEpoch=1735948800000&language=1&channelIds=561066'
|
'https://epg.cyta.com.cy/api/mediacatalog/fetchEpg?startTimeEpoch=1735862400000&endTimeEpoch=1735948800000&language=1&channelIds=561066'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = `
|
const content = `
|
||||||
{
|
{
|
||||||
"channelEpgs": [
|
"channelEpgs": [
|
||||||
{
|
{
|
||||||
"epgPlayables": [
|
"epgPlayables": [
|
||||||
{ "name": "Πρώτη Ενημέρωση", "startTime": 1735879500000, "endTime": 1735889400000 }
|
{ "name": "Πρώτη Ενημέρωση", "startTime": 1735879500000, "endTime": 1735889400000 }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
const result = parser({ content }).map(p => {
|
const result = parser({ content })
|
||||||
p.start = p.start
|
|
||||||
p.stop = p.stop
|
expect(result).toMatchObject([
|
||||||
return p
|
{
|
||||||
})
|
title: 'Πρώτη Ενημέρωση',
|
||||||
|
start: '2025-01-03T04:45:00.000Z',
|
||||||
expect(result).toMatchObject([
|
stop: '2025-01-03T07:30:00.000Z'
|
||||||
{
|
}
|
||||||
title: 'Πρώτη Ενημέρωση',
|
])
|
||||||
start: '2025-01-03T04:45:00.000Z',
|
})
|
||||||
stop: '2025-01-03T07:30:00.000Z'
|
|
||||||
}
|
it('can handle empty guide', () => {
|
||||||
])
|
const result = parser({
|
||||||
})
|
content: '{"channelEpgs":[]}'
|
||||||
|
})
|
||||||
it('can handle empty guide', () => {
|
expect(result).toMatchObject([])
|
||||||
const result = parser({
|
})
|
||||||
content: '{"channelEpgs":[]}'
|
|
||||||
})
|
|
||||||
expect(result).toMatchObject([])
|
|
||||||
})
|
|
||||||
|
|
|
@ -13,9 +13,9 @@ module.exports = {
|
||||||
site: 'dens.tv',
|
site: 'dens.tv',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel, date }) {
|
url({ channel, date }) {
|
||||||
return `https://www.dens.tv/api/dens3/tv/TvChannels/listEpgByDate?date=${date.format('YYYY-MM-DD')}&id_channel=${
|
return `https://www.dens.tv/api/dens3/tv/TvChannels/listEpgByDate?date=${date.format(
|
||||||
channel.site_id
|
'YYYY-MM-DD'
|
||||||
}&app_type=10`
|
)}&id_channel=${channel.site_id}&app_type=10`
|
||||||
},
|
},
|
||||||
parser({ content }) {
|
parser({ content }) {
|
||||||
// parsing
|
// parsing
|
||||||
|
@ -25,8 +25,9 @@ module.exports = {
|
||||||
if (Array.isArray(response?.data)) {
|
if (Array.isArray(response?.data)) {
|
||||||
response.data.forEach(item => {
|
response.data.forEach(item => {
|
||||||
const title = item.title
|
const title = item.title
|
||||||
const [, , , season, , , episode] = title.match(/( (Season |Season|S)(\d+))?( (Episode|Ep) (\d+))/) ||
|
const [, , , season, , , episode] = title.match(
|
||||||
[null, null, null, null, null, null, null]
|
/( (Season |Season|S)(\d+))?( (Episode|Ep) (\d+))/
|
||||||
|
) || [null, null, null, null, null, null, null]
|
||||||
programs.push({
|
programs.push({
|
||||||
title,
|
title,
|
||||||
description: item.description,
|
description: item.description,
|
||||||
|
@ -52,7 +53,7 @@ module.exports = {
|
||||||
const channels = []
|
const channels = []
|
||||||
for (const id_category of Object.values(categories)) {
|
for (const id_category of Object.values(categories)) {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://www.dens.tv/api/dens3/tv/TvChannels/listByCategory`, {
|
.get('https://www.dens.tv/api/dens3/tv/TvChannels/listByCategory', {
|
||||||
params: { id_category }
|
params: { id_category }
|
||||||
})
|
})
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
|
|
|
@ -10,7 +10,9 @@ const date = dayjs.utc('2024-11-24').startOf('d')
|
||||||
const channel = { site_id: '38', xmltv_id: 'AniplusAsia.sg', lang: 'id' }
|
const channel = { site_id: '38', xmltv_id: 'AniplusAsia.sg', lang: 'id' }
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel, date })).toBe('https://www.dens.tv/api/dens3/tv/TvChannels/listEpgByDate?date=2024-11-24&id_channel=38&app_type=10')
|
expect(url({ channel, date })).toBe(
|
||||||
|
'https://www.dens.tv/api/dens3/tv/TvChannels/listEpgByDate?date=2024-11-24&id_channel=38&app_type=10'
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
|
|
|
@ -65,7 +65,7 @@ module.exports = {
|
||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
|
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://www.digiturk.com.tr/`, {
|
.get('https://www.digiturk.com.tr/', {
|
||||||
headers: {
|
headers: {
|
||||||
'User-Agent':
|
'User-Agent':
|
||||||
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
|
'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/119.0.0.0 Safari/537.36'
|
||||||
|
|
|
@ -60,7 +60,7 @@ module.exports = {
|
||||||
$('.pgrid').each((i, el) => {
|
$('.pgrid').each((i, el) => {
|
||||||
const onclick = $(el).find('.chnl-logo').attr('onclick')
|
const onclick = $(el).find('.chnl-logo').attr('onclick')
|
||||||
const number = $(el).find('.cnl-fav > a > span').text().trim()
|
const number = $(el).find('.cnl-fav > a > span').text().trim()
|
||||||
const [, name, site_id] = onclick.match(/ShowChannelGuid\('([^']+)','([^']+)'/) || [
|
const [, , site_id] = onclick.match(/ShowChannelGuid\('([^']+)','([^']+)'/) || [
|
||||||
null,
|
null,
|
||||||
'',
|
'',
|
||||||
''
|
''
|
||||||
|
|
|
@ -49,7 +49,7 @@ module.exports = {
|
||||||
const channels = []
|
const channels = []
|
||||||
for (let provider of providers) {
|
for (let provider of providers) {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.post(`https://www.guida.tv/guide/schedule`, null, {
|
.post('https://www.guida.tv/guide/schedule', null, {
|
||||||
params: {
|
params: {
|
||||||
provider,
|
provider,
|
||||||
region: 'Italy',
|
region: 'Italy',
|
||||||
|
@ -81,7 +81,7 @@ module.exports = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseStart($item, date, channel) {
|
function parseStart($item, date) {
|
||||||
const timeString = $item('td:eq(0)').text().trim()
|
const timeString = $item('td:eq(0)').text().trim()
|
||||||
const dateString = `${date.format('YYYY-MM-DD')} ${timeString}`
|
const dateString = `${date.format('YYYY-MM-DD')} ${timeString}`
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,7 @@ module.exports = {
|
||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
|
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://guidatv.sky.it/canali`)
|
.get('https://guidatv.sky.it/canali')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
|
|
|
@ -1,63 +1,63 @@
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const convert = require('xml-js')
|
const convert = require('xml-js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
|
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'hoy.tv',
|
site: 'hoy.tv',
|
||||||
days: 2,
|
days: 2,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 60 * 60 * 1000 // 1h
|
ttl: 60 * 60 * 1000 // 1h
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
url: function ({ channel, date }) {
|
url: function ({ channel, date }) {
|
||||||
return `https://epg-file.hoy.tv/hoy/OTT${channel.site_id}${date.format('YYYYMMDD')}.xml`
|
return `https://epg-file.hoy.tv/hoy/OTT${channel.site_id}${date.format('YYYYMMDD')}.xml`
|
||||||
},
|
},
|
||||||
parser({ content, channel, date }) {
|
parser({ content, date }) {
|
||||||
const data = convert.xml2js(content, {
|
const data = convert.xml2js(content, {
|
||||||
compact: true,
|
compact: true,
|
||||||
ignoreDeclaration: true,
|
ignoreDeclaration: true,
|
||||||
ignoreAttributes: true
|
ignoreAttributes: true
|
||||||
})
|
})
|
||||||
|
|
||||||
const programs = []
|
const programs = []
|
||||||
|
|
||||||
for (let item of data.ProgramGuide.Channel.EpgItem) {
|
for (let item of data.ProgramGuide.Channel.EpgItem) {
|
||||||
const start = dayjs.tz(item.EpgStartDateTime._text, 'YYYY-MM-DD HH:mm:ss', 'Asia/Hong_Kong')
|
const start = dayjs.tz(item.EpgStartDateTime._text, 'YYYY-MM-DD HH:mm:ss', 'Asia/Hong_Kong')
|
||||||
|
|
||||||
if (! date.isSame(start, 'day')) {
|
if (!date.isSame(start, 'day')) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
const epIndex = item.EpisodeInfo.EpisodeIndex._text
|
const epIndex = item.EpisodeInfo.EpisodeIndex._text
|
||||||
const subtitle = parseInt(epIndex) > 0 ? `第${epIndex}集` : undefined
|
const subtitle = parseInt(epIndex) > 0 ? `第${epIndex}集` : undefined
|
||||||
|
|
||||||
programs.push({
|
programs.push({
|
||||||
title: `${item.ComScore.ns_st_pr._text}${item.EpgOtherInfo?._text || ''}`,
|
title: `${item.ComScore.ns_st_pr._text}${item.EpgOtherInfo?._text || ''}`,
|
||||||
sub_title: subtitle,
|
sub_title: subtitle,
|
||||||
description: item.EpisodeInfo.EpisodeLongDescription._text,
|
description: item.EpisodeInfo.EpisodeLongDescription._text,
|
||||||
start,
|
start,
|
||||||
stop: dayjs.tz(item.EpgEndDateTime._text, 'YYYY-MM-DD HH:mm:ss', 'Asia/Hong_Kong'),
|
stop: dayjs.tz(item.EpgEndDateTime._text, 'YYYY-MM-DD HH:mm:ss', 'Asia/Hong_Kong')
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels({ lang }) {
|
async channels() {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get('https://api2.hoy.tv/api/v2/a/channel')
|
.get('https://api2.hoy.tv/api/v2/a/channel')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
return data.data.map(c => {
|
return data.data.map(c => {
|
||||||
return {
|
return {
|
||||||
site_id: c.videos.id,
|
site_id: c.videos.id,
|
||||||
name: c.name.zh_hk,
|
name: c.name.zh_hk,
|
||||||
lang: 'zh',
|
lang: 'zh'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,116 +1,115 @@
|
||||||
const { parser, url } = require('./hoy.tv.config.js')
|
const { parser, url } = require('./hoy.tv.config.js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
|
|
||||||
const date = dayjs.utc('2024-09-13', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2024-09-13', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '76',
|
site_id: '76',
|
||||||
xmltv_id: 'HOYIBC.hk',
|
xmltv_id: 'HOYIBC.hk',
|
||||||
lang: 'zh'
|
lang: 'zh'
|
||||||
}
|
}
|
||||||
const content = `<?xml version="1.0" encoding="UTF-8" ?>
|
const content = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
<ProgramGuide>
|
<ProgramGuide>
|
||||||
<Channel id="76">
|
<Channel id="76">
|
||||||
<EpgItem>
|
<EpgItem>
|
||||||
<EpgStartDateTime>2024-09-13 11:30:00</EpgStartDateTime>
|
<EpgStartDateTime>2024-09-13 11:30:00</EpgStartDateTime>
|
||||||
<EpgEndDateTime>2024-09-13 12:30:00</EpgEndDateTime>
|
<EpgEndDateTime>2024-09-13 12:30:00</EpgEndDateTime>
|
||||||
<EpgOtherInfo>[PG]</EpgOtherInfo>
|
<EpgOtherInfo>[PG]</EpgOtherInfo>
|
||||||
<DisableLive>false</DisableLive>
|
<DisableLive>false</DisableLive>
|
||||||
<DisableVod>false</DisableVod>
|
<DisableVod>false</DisableVod>
|
||||||
<VODLicPeriod>2024-09-27 11:30:00</VODLicPeriod>
|
<VODLicPeriod>2024-09-27 11:30:00</VODLicPeriod>
|
||||||
<ProgramInfo>
|
<ProgramInfo>
|
||||||
<ProgramId>0</ProgramId>
|
<ProgramId>0</ProgramId>
|
||||||
<ProgramTitle></ProgramTitle>
|
<ProgramTitle></ProgramTitle>
|
||||||
<ProgramPos>0</ProgramPos>
|
<ProgramPos>0</ProgramPos>
|
||||||
<FirstRunDateTime></FirstRunDateTime>
|
<FirstRunDateTime></FirstRunDateTime>
|
||||||
<ProgramThumbnailUrl>http://tv.fantv.hk/images/thumbnail_1920_1080_fantv.jpg</ProgramThumbnailUrl>
|
<ProgramThumbnailUrl>http://tv.fantv.hk/images/thumbnail_1920_1080_fantv.jpg</ProgramThumbnailUrl>
|
||||||
</ProgramInfo>
|
</ProgramInfo>
|
||||||
<EpisodeInfo>
|
<EpisodeInfo>
|
||||||
<EpisodeId>EQ00135</EpisodeId>
|
<EpisodeId>EQ00135</EpisodeId>
|
||||||
<EpisodeIndex>46</EpisodeIndex>
|
<EpisodeIndex>46</EpisodeIndex>
|
||||||
<EpisodeShortDescription>點講都係一家人</EpisodeShortDescription>
|
<EpisodeShortDescription>點講都係一家人</EpisodeShortDescription>
|
||||||
<EpisodeLongDescription></EpisodeLongDescription>
|
<EpisodeLongDescription></EpisodeLongDescription>
|
||||||
<EpisodeThumbnailUrl>http://tv.fantv.hk/images/nosuchthumbnail.jpg</EpisodeThumbnailUrl>
|
<EpisodeThumbnailUrl>http://tv.fantv.hk/images/nosuchthumbnail.jpg</EpisodeThumbnailUrl>
|
||||||
</EpisodeInfo>
|
</EpisodeInfo>
|
||||||
<ComScore>
|
<ComScore>
|
||||||
<ns_st_stc></ns_st_stc>
|
<ns_st_stc></ns_st_stc>
|
||||||
<ns_st_pr>點講都係一家人</ns_st_pr>
|
<ns_st_pr>點講都係一家人</ns_st_pr>
|
||||||
<ns_st_tpr>0</ns_st_tpr>
|
<ns_st_tpr>0</ns_st_tpr>
|
||||||
<ns_st_tep>EQ00135</ns_st_tep>
|
<ns_st_tep>EQ00135</ns_st_tep>
|
||||||
<ns_st_ep>點講都係一家人 Episode 46</ns_st_ep>
|
<ns_st_ep>點講都係一家人 Episode 46</ns_st_ep>
|
||||||
<ns_st_li>1</ns_st_li>
|
<ns_st_li>1</ns_st_li>
|
||||||
<ns_st_tdt>20240913</ns_st_tdt>
|
<ns_st_tdt>20240913</ns_st_tdt>
|
||||||
<ns_st_tm>1130</ns_st_tm>
|
<ns_st_tm>1130</ns_st_tm>
|
||||||
<ns_st_ty>0001</ns_st_ty>
|
<ns_st_ty>0001</ns_st_ty>
|
||||||
<ns_st_cl>3704000</ns_st_cl>
|
<ns_st_cl>3704000</ns_st_cl>
|
||||||
</ComScore>
|
</ComScore>
|
||||||
</EpgItem>
|
</EpgItem>
|
||||||
<EpgItem>
|
<EpgItem>
|
||||||
<EpgStartDateTime>2024-09-13 12:30:00</EpgStartDateTime>
|
<EpgStartDateTime>2024-09-13 12:30:00</EpgStartDateTime>
|
||||||
<EpgEndDateTime>2024-09-13 13:30:00</EpgEndDateTime>
|
<EpgEndDateTime>2024-09-13 13:30:00</EpgEndDateTime>
|
||||||
<EpgOtherInfo></EpgOtherInfo>
|
<EpgOtherInfo></EpgOtherInfo>
|
||||||
<DisableLive>false</DisableLive>
|
<DisableLive>false</DisableLive>
|
||||||
<DisableVod>false</DisableVod>
|
<DisableVod>false</DisableVod>
|
||||||
<VODLicPeriod>2024-09-27 12:30:00</VODLicPeriod>
|
<VODLicPeriod>2024-09-27 12:30:00</VODLicPeriod>
|
||||||
<ProgramInfo>
|
<ProgramInfo>
|
||||||
<ProgramId>0</ProgramId>
|
<ProgramId>0</ProgramId>
|
||||||
<ProgramTitle></ProgramTitle>
|
<ProgramTitle></ProgramTitle>
|
||||||
<ProgramPos>0</ProgramPos>
|
<ProgramPos>0</ProgramPos>
|
||||||
<FirstRunDateTime></FirstRunDateTime>
|
<FirstRunDateTime></FirstRunDateTime>
|
||||||
<ProgramThumbnailUrl>http://tv.fantv.hk/images/thumbnail_1920_1080_fantv.jpg</ProgramThumbnailUrl>
|
<ProgramThumbnailUrl>http://tv.fantv.hk/images/thumbnail_1920_1080_fantv.jpg</ProgramThumbnailUrl>
|
||||||
</ProgramInfo>
|
</ProgramInfo>
|
||||||
<EpisodeInfo>
|
<EpisodeInfo>
|
||||||
<EpisodeId>ED00311</EpisodeId>
|
<EpisodeId>ED00311</EpisodeId>
|
||||||
<EpisodeIndex>0</EpisodeIndex>
|
<EpisodeIndex>0</EpisodeIndex>
|
||||||
<EpisodeShortDescription>麝香之路</EpisodeShortDescription>
|
<EpisodeShortDescription>麝香之路</EpisodeShortDescription>
|
||||||
<EpisodeLongDescription>Ep. 2 .The Secret of disappeared kingdom.shows the mysterious disappearance of the ancient Tibetan kingdom which gained world</EpisodeLongDescription>
|
<EpisodeLongDescription>Ep. 2 .The Secret of disappeared kingdom.shows the mysterious disappearance of the ancient Tibetan kingdom which gained world</EpisodeLongDescription>
|
||||||
<EpisodeThumbnailUrl>http://tv.fantv.hk/images/nosuchthumbnail.jpg</EpisodeThumbnailUrl>
|
<EpisodeThumbnailUrl>http://tv.fantv.hk/images/nosuchthumbnail.jpg</EpisodeThumbnailUrl>
|
||||||
</EpisodeInfo>
|
</EpisodeInfo>
|
||||||
<ComScore>
|
<ComScore>
|
||||||
<ns_st_stc></ns_st_stc>
|
<ns_st_stc></ns_st_stc>
|
||||||
<ns_st_pr>麝香之路</ns_st_pr>
|
<ns_st_pr>麝香之路</ns_st_pr>
|
||||||
<ns_st_tpr>0</ns_st_tpr>
|
<ns_st_tpr>0</ns_st_tpr>
|
||||||
<ns_st_tep>ED00311</ns_st_tep>
|
<ns_st_tep>ED00311</ns_st_tep>
|
||||||
<ns_st_ep>麝香之路 2024-09-13</ns_st_ep>
|
<ns_st_ep>麝香之路 2024-09-13</ns_st_ep>
|
||||||
<ns_st_li>1</ns_st_li>
|
<ns_st_li>1</ns_st_li>
|
||||||
<ns_st_tdt>20240913</ns_st_tdt>
|
<ns_st_tdt>20240913</ns_st_tdt>
|
||||||
<ns_st_tm>1230</ns_st_tm>
|
<ns_st_tm>1230</ns_st_tm>
|
||||||
<ns_st_ty>0001</ns_st_ty>
|
<ns_st_ty>0001</ns_st_ty>
|
||||||
<ns_st_cl>3704000</ns_st_cl>
|
<ns_st_cl>3704000</ns_st_cl>
|
||||||
</ComScore>
|
</ComScore>
|
||||||
</EpgItem>
|
</EpgItem>
|
||||||
</Channel>
|
</Channel>
|
||||||
</ProgramGuide>`
|
</ProgramGuide>`
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel, date })).toBe(
|
expect(url({ channel, date })).toBe('https://epg-file.hoy.tv/hoy/OTT7620240913.xml')
|
||||||
'https://epg-file.hoy.tv/hoy/OTT7620240913.xml'
|
})
|
||||||
)
|
|
||||||
})
|
it('can parse response', () => {
|
||||||
|
const result = parser({ content, channel, date }).map(p => {
|
||||||
it('can parse response', () => {
|
p.start = p.start.toJSON()
|
||||||
const result = parser({ content, channel, date }).map(p => {
|
p.stop = p.stop.toJSON()
|
||||||
p.start = p.start.toJSON()
|
return p
|
||||||
p.stop = p.stop.toJSON()
|
})
|
||||||
return p
|
|
||||||
})
|
expect(result).toMatchObject([
|
||||||
|
{
|
||||||
expect(result).toMatchObject([
|
start: '2024-09-13T03:30:00.000Z',
|
||||||
{
|
stop: '2024-09-13T04:30:00.000Z',
|
||||||
start: '2024-09-13T03:30:00.000Z',
|
title: '點講都係一家人[PG]',
|
||||||
stop: '2024-09-13T04:30:00.000Z',
|
sub_title: '第46集'
|
||||||
title: '點講都係一家人[PG]',
|
},
|
||||||
sub_title: '第46集',
|
{
|
||||||
},
|
start: '2024-09-13T04:30:00.000Z',
|
||||||
{
|
stop: '2024-09-13T05:30:00.000Z',
|
||||||
start: '2024-09-13T04:30:00.000Z',
|
title: '麝香之路',
|
||||||
stop: '2024-09-13T05:30:00.000Z',
|
description:
|
||||||
title: '麝香之路',
|
'Ep. 2 .The Secret of disappeared kingdom.shows the mysterious disappearance of the ancient Tibetan kingdom which gained world'
|
||||||
description: 'Ep. 2 .The Secret of disappeared kingdom.shows the mysterious disappearance of the ancient Tibetan kingdom which gained world',
|
}
|
||||||
}
|
])
|
||||||
])
|
})
|
||||||
})
|
|
||||||
|
|
|
@ -172,14 +172,17 @@ function parseItems(content, channel, date) {
|
||||||
if (!data || !Array.isArray(data.programs)) return []
|
if (!data || !Array.isArray(data.programs)) return []
|
||||||
|
|
||||||
return data.programs
|
return data.programs
|
||||||
.filter(p => p.channel === site_id && dayjs(p.start, 'YYYYMMDDHHmmss ZZ').isBetween(curr_day, next_day))
|
.filter(
|
||||||
|
p =>
|
||||||
|
p.channel === site_id && dayjs(p.start, 'YYYYMMDDHHmmss ZZ').isBetween(curr_day, next_day)
|
||||||
|
)
|
||||||
.map(p => {
|
.map(p => {
|
||||||
if (Array.isArray(p.date) && p.date.length) {
|
if (Array.isArray(p.date) && p.date.length) {
|
||||||
p.date = p.date[0]
|
p.date = p.date[0]
|
||||||
}
|
}
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
} catch (error) {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,73 +1,80 @@
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'ipko.tv',
|
site: 'ipko.tv',
|
||||||
timezone: 'Europe/Belgrade',
|
timezone: 'Europe/Belgrade',
|
||||||
days: 5,
|
days: 5,
|
||||||
url({ date, channel }) { return 'https://stargate.ipko.tv/api/titan.tv.WebEpg/GetWebEpgData' },
|
url() {
|
||||||
request: {
|
return 'https://stargate.ipko.tv/api/titan.tv.WebEpg/GetWebEpgData'
|
||||||
method: 'POST',
|
},
|
||||||
headers: {
|
request: {
|
||||||
'Host': 'stargate.ipko.tv',
|
method: 'POST',
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
|
headers: {
|
||||||
'Accept': 'application/json, text/plain, */*',
|
Host: 'stargate.ipko.tv',
|
||||||
'Accept-Language': 'nl,en-US;q=0.7,en;q=0.3',
|
'User-Agent':
|
||||||
'Content-Type': 'application/json',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
|
||||||
'X-AppLayout': '1',
|
Accept: 'application/json, text/plain, */*',
|
||||||
'x-language': 'sq',
|
'Accept-Language': 'nl,en-US;q=0.7,en;q=0.3',
|
||||||
'Origin': 'https://ipko.tv',
|
'Content-Type': 'application/json',
|
||||||
'Sec-Fetch-Dest': 'empty',
|
'X-AppLayout': '1',
|
||||||
'Sec-Fetch-Mode': 'cors',
|
'x-language': 'sq',
|
||||||
'Sec-Fetch-Site': 'cross-site',
|
Origin: 'https://ipko.tv',
|
||||||
'Sec-GPC': '1',
|
'Sec-Fetch-Dest': 'empty',
|
||||||
'Connection': 'keep-alive'
|
'Sec-Fetch-Mode': 'cors',
|
||||||
},
|
'Sec-Fetch-Site': 'cross-site',
|
||||||
data({ channel, date }) {
|
'Sec-GPC': '1',
|
||||||
const todayEpoch = date.startOf('day').unix();
|
Connection: 'keep-alive'
|
||||||
const nextDayEpoch = date.add(1, 'day').startOf('day').unix();
|
},
|
||||||
return JSON.stringify({
|
data({ channel, date }) {
|
||||||
ch_ext_id: channel.site_id,
|
const todayEpoch = date.startOf('day').unix()
|
||||||
from: todayEpoch,
|
const nextDayEpoch = date.add(1, 'day').startOf('day').unix()
|
||||||
to: nextDayEpoch
|
return JSON.stringify({
|
||||||
})
|
ch_ext_id: channel.site_id,
|
||||||
}
|
from: todayEpoch,
|
||||||
},
|
to: nextDayEpoch
|
||||||
parser: function ({ content }) {
|
})
|
||||||
const programs = [];
|
}
|
||||||
const data = JSON.parse(content);
|
},
|
||||||
data.shows.forEach(show => {
|
parser: function ({ content }) {
|
||||||
const start = dayjs.unix(show.show_start).utc();
|
const programs = []
|
||||||
const stop = dayjs.unix(show.show_end).utc();
|
const data = JSON.parse(content)
|
||||||
const programData = {
|
data.shows.forEach(show => {
|
||||||
title: show.title,
|
const start = dayjs.unix(show.show_start).utc()
|
||||||
description: show.summary || 'No description available',
|
const stop = dayjs.unix(show.show_end).utc()
|
||||||
start: start.toISOString(),
|
const programData = {
|
||||||
stop: stop.toISOString(),
|
title: show.title,
|
||||||
thumbnail: show.thumbnail
|
description: show.summary || 'No description available',
|
||||||
}
|
start: start.toISOString(),
|
||||||
programs.push(programData)
|
stop: stop.toISOString(),
|
||||||
})
|
thumbnail: show.thumbnail
|
||||||
return programs
|
}
|
||||||
},
|
programs.push(programData)
|
||||||
async channels() {
|
})
|
||||||
const response = await axios.post('https://stargate.ipko.tv/api/titan.tv.WebEpg/ZapList', JSON.stringify({ includeRadioStations: true }), {
|
return programs
|
||||||
headers: this.request.headers
|
},
|
||||||
});
|
async channels() {
|
||||||
|
const response = await axios.post(
|
||||||
const data = response.data.data;
|
'https://stargate.ipko.tv/api/titan.tv.WebEpg/ZapList',
|
||||||
return data.map(item => ({
|
JSON.stringify({ includeRadioStations: true }),
|
||||||
lang: 'sq',
|
{
|
||||||
name: String(item.channel.title),
|
headers: this.request.headers
|
||||||
site_id: String(item.channel.id),
|
}
|
||||||
//logo: String(item.channel.logo)
|
)
|
||||||
}))
|
|
||||||
}
|
const data = response.data.data
|
||||||
}
|
return data.map(item => ({
|
||||||
|
lang: 'sq',
|
||||||
|
name: String(item.channel.title),
|
||||||
|
site_id: String(item.channel.id)
|
||||||
|
//logo: String(item.channel.logo)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,115 +1,111 @@
|
||||||
const { parser, url } = require('./ipko.tv.config.js')
|
const { parser, url } = require('./ipko.tv.config.js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2024-12-24', 'YYYY-MM-DD').startOf('day')
|
const date = dayjs.utc('2024-12-24', 'YYYY-MM-DD').startOf('day')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'ipko-promo',
|
site_id: 'ipko-promo',
|
||||||
xmltv_id: 'IPKOPROMO'
|
xmltv_id: 'IPKOPROMO'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ date, channel })).toBe('https://stargate.ipko.tv/api/titan.tv.WebEpg/GetWebEpgData')
|
expect(url({ date, channel })).toBe('https://stargate.ipko.tv/api/titan.tv.WebEpg/GetWebEpgData')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = `
|
const content = `
|
||||||
{
|
{
|
||||||
"shows": [
|
"shows": [
|
||||||
{
|
{
|
||||||
"title": "IPKO Promo",
|
"title": "IPKO Promo",
|
||||||
"show_start": 1735012800,
|
"show_start": 1735012800,
|
||||||
"show_end": 1735020000,
|
"show_end": 1735020000,
|
||||||
"timestamp": "5:00 - 7:00",
|
"timestamp": "5:00 - 7:00",
|
||||||
"show_id": "EPG_TvProfil_IPKOPROMO_296105567",
|
"show_id": "EPG_TvProfil_IPKOPROMO_296105567",
|
||||||
"thumbnail": "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg",
|
"thumbnail": "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg",
|
||||||
"is_adult": false,
|
"is_adult": false,
|
||||||
"friendly_id": "ipko_promo_4cf3",
|
"friendly_id": "ipko_promo_4cf3",
|
||||||
"pg": "",
|
"pg": "",
|
||||||
"genres": [],
|
"genres": [],
|
||||||
"year": 0,
|
"year": 0,
|
||||||
"summary": "",
|
"summary": "",
|
||||||
"categories": "Other",
|
"categories": "Other",
|
||||||
"stb_only": false,
|
"stb_only": false,
|
||||||
"is_live": false,
|
"is_live": false,
|
||||||
"original_title": "IPKO Promo"
|
"original_title": "IPKO Promo"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "IPKO Promo",
|
"title": "IPKO Promo",
|
||||||
"show_start": 1735020000,
|
"show_start": 1735020000,
|
||||||
"show_end": 1735027200,
|
"show_end": 1735027200,
|
||||||
"timestamp": "7:00 - 9:00",
|
"timestamp": "7:00 - 9:00",
|
||||||
"show_id": "EPG_TvProfil_IPKOPROMO_296105568",
|
"show_id": "EPG_TvProfil_IPKOPROMO_296105568",
|
||||||
"thumbnail": "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg",
|
"thumbnail": "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg",
|
||||||
"is_adult": false,
|
"is_adult": false,
|
||||||
"friendly_id": "ipko_promo_416b",
|
"friendly_id": "ipko_promo_416b",
|
||||||
"pg": "",
|
"pg": "",
|
||||||
"genres": [],
|
"genres": [],
|
||||||
"year": 0,
|
"year": 0,
|
||||||
"summary": "",
|
"summary": "",
|
||||||
"categories": "Other",
|
"categories": "Other",
|
||||||
"stb_only": false,
|
"stb_only": false,
|
||||||
"is_live": false,
|
"is_live": false,
|
||||||
"original_title": "IPKO Promo"
|
"original_title": "IPKO Promo"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "IPKO Promo",
|
"title": "IPKO Promo",
|
||||||
"show_start": 1735027200,
|
"show_start": 1735027200,
|
||||||
"show_end": 1735034400,
|
"show_end": 1735034400,
|
||||||
"timestamp": "9:00 - 11:00",
|
"timestamp": "9:00 - 11:00",
|
||||||
"show_id": "EPG_TvProfil_IPKOPROMO_296105569",
|
"show_id": "EPG_TvProfil_IPKOPROMO_296105569",
|
||||||
"thumbnail": "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg",
|
"thumbnail": "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg",
|
||||||
"is_adult": false,
|
"is_adult": false,
|
||||||
"friendly_id": "ipko_promo_2e23",
|
"friendly_id": "ipko_promo_2e23",
|
||||||
"pg": "",
|
"pg": "",
|
||||||
"genres": [],
|
"genres": [],
|
||||||
"year": 0,
|
"year": 0,
|
||||||
"summary": "",
|
"summary": "",
|
||||||
"categories": "Other",
|
"categories": "Other",
|
||||||
"stb_only": false,
|
"stb_only": false,
|
||||||
"is_live": false,
|
"is_live": false,
|
||||||
"original_title": "IPKO Promo"
|
"original_title": "IPKO Promo"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}`
|
}`
|
||||||
|
|
||||||
const result = parser({ content, channel }).map(p => {
|
const result = parser({ content, channel })
|
||||||
p.start = p.start
|
|
||||||
p.stop = p.stop
|
expect(result).toMatchObject([
|
||||||
return p
|
{
|
||||||
})
|
title: 'IPKO Promo',
|
||||||
|
description: 'No description available',
|
||||||
expect(result).toMatchObject([
|
start: '2024-12-24T04:00:00.000Z',
|
||||||
{
|
stop: '2024-12-24T06:00:00.000Z',
|
||||||
title: "IPKO Promo",
|
thumbnail: 'https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg'
|
||||||
description: "No description available",
|
},
|
||||||
start: "2024-12-24T04:00:00.000Z",
|
{
|
||||||
stop: "2024-12-24T06:00:00.000Z",
|
title: 'IPKO Promo',
|
||||||
thumbnail: "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg"
|
description: 'No description available',
|
||||||
},
|
start: '2024-12-24T06:00:00.000Z',
|
||||||
{
|
stop: '2024-12-24T08:00:00.000Z',
|
||||||
title: "IPKO Promo",
|
thumbnail: 'https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg'
|
||||||
description: "No description available",
|
},
|
||||||
start: "2024-12-24T06:00:00.000Z",
|
{
|
||||||
stop: "2024-12-24T08:00:00.000Z",
|
title: 'IPKO Promo',
|
||||||
thumbnail: "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg"
|
description: 'No description available',
|
||||||
},
|
start: '2024-12-24T08:00:00.000Z',
|
||||||
{
|
stop: '2024-12-24T10:00:00.000Z',
|
||||||
title: "IPKO Promo",
|
thumbnail: 'https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg'
|
||||||
description: "No description available",
|
}
|
||||||
start: "2024-12-24T08:00:00.000Z",
|
])
|
||||||
stop: "2024-12-24T10:00:00.000Z",
|
})
|
||||||
thumbnail: "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg"
|
|
||||||
}
|
it('can handle empty guide', () => {
|
||||||
])
|
const result = parser({
|
||||||
})
|
content: '{"shows":[]}'
|
||||||
|
})
|
||||||
it('can handle empty guide', () => {
|
expect(result).toMatchObject([])
|
||||||
const result = parser({
|
})
|
||||||
content: '{"shows":[]}'
|
|
||||||
})
|
|
||||||
expect(result).toMatchObject([])
|
|
||||||
})
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ module.exports = {
|
||||||
async channels() {
|
async channels() {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://m.tv.sms.cz/?zmen_stanice=true`)
|
.get('https://m.tv.sms.cz/?zmen_stanice=true')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
|
|
|
@ -77,8 +77,8 @@ function parseItems(content) {
|
||||||
let data
|
let data
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(content)
|
data = JSON.parse(content)
|
||||||
} catch (error) {
|
} catch {
|
||||||
console.log(error.message)
|
return []
|
||||||
}
|
}
|
||||||
if (!data || !Array.isArray(data)) return []
|
if (!data || !Array.isArray(data)) return []
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ module.exports = {
|
||||||
async channels() {
|
async channels() {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://player.maxtvtogo.tportal.hr:8082/OTT4Proxy/proxy/epg/channels`)
|
.get('https://player.maxtvtogo.tportal.hr:8082/OTT4Proxy/proxy/epg/channels')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
|
|
|
@ -56,7 +56,7 @@ function parseStop($item) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return dayjs(timeString, 'YYYY-MM-DD HH:mm:ssZZ')
|
return dayjs(timeString, 'YYYY-MM-DD HH:mm:ssZZ')
|
||||||
} catch (err) {
|
} catch {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,93 +1,97 @@
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'mediasetinfinity.mediaset.it',
|
site: 'mediasetinfinity.mediaset.it',
|
||||||
days: 2,
|
days: 2,
|
||||||
url: function ({date, channel}) {
|
url: function ({ date, channel }) {
|
||||||
// Get the epoch timestamp
|
// Get the epoch timestamp
|
||||||
const todayEpoch = date.startOf('day').utc().valueOf()
|
const todayEpoch = date.startOf('day').utc().valueOf()
|
||||||
// Get the epoch timestamp for the next day
|
// Get the epoch timestamp for the next day
|
||||||
const nextDayEpoch = date.add(1, 'day').startOf('day').utc().valueOf()
|
const nextDayEpoch = date.add(1, 'day').startOf('day').utc().valueOf()
|
||||||
return `https://api-ott-prod-fe.mediaset.net/PROD/play/feed/allListingFeedEpg/v2.0?byListingTime=${todayEpoch}~${nextDayEpoch}&byCallSign=${channel.site_id}`
|
return `https://api-ott-prod-fe.mediaset.net/PROD/play/feed/allListingFeedEpg/v2.0?byListingTime=${todayEpoch}~${nextDayEpoch}&byCallSign=${channel.site_id}`
|
||||||
},
|
},
|
||||||
parser: function ({content}) {
|
parser: function ({ content }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
const data = JSON.parse(content)
|
const data = JSON.parse(content)
|
||||||
|
|
||||||
if (!data.response || !data.response.entries || !data.response.entries[0] || !data.response.entries[0].listings) {
|
if (
|
||||||
// If the structure is not as expected, return an empty array
|
!data.response ||
|
||||||
return programs
|
!data.response.entries ||
|
||||||
}
|
!data.response.entries[0] ||
|
||||||
|
!data.response.entries[0].listings
|
||||||
const listings = data.response.entries[0].listings
|
) {
|
||||||
|
// If the structure is not as expected, return an empty array
|
||||||
listings.forEach((listing) => {
|
return programs
|
||||||
const title = listing.mediasetlisting$epgTitle
|
}
|
||||||
const subTitle = listing.program.title
|
|
||||||
const season = parseSeason(listing)
|
const listings = data.response.entries[0].listings
|
||||||
const episode = parseEpisode(listing)
|
|
||||||
|
listings.forEach(listing => {
|
||||||
|
const title = listing.mediasetlisting$epgTitle
|
||||||
if (listing.program.title && listing.startTime && listing.endTime) {
|
const subTitle = listing.program.title
|
||||||
programs.push({
|
const season = parseSeason(listing)
|
||||||
title: title || subTitle,
|
const episode = parseEpisode(listing)
|
||||||
sub_title: title && title != subTitle ? subTitle : null,
|
|
||||||
description: listing.program.description || null,
|
if (listing.program.title && listing.startTime && listing.endTime) {
|
||||||
category: listing.program.mediasetprogram$skyGenre || null,
|
programs.push({
|
||||||
season: episode && !season ? '0' : season,
|
title: title || subTitle,
|
||||||
episode: episode,
|
sub_title: title && title != subTitle ? subTitle : null,
|
||||||
start: parseTime(listing.startTime),
|
description: listing.program.description || null,
|
||||||
stop: parseTime(listing.endTime),
|
category: listing.program.mediasetprogram$skyGenre || null,
|
||||||
image: getMaxResolutionThumbnails(listing)
|
season: episode && !season ? '0' : season,
|
||||||
})
|
episode: episode,
|
||||||
}
|
start: parseTime(listing.startTime),
|
||||||
})
|
stop: parseTime(listing.endTime),
|
||||||
|
image: getMaxResolutionThumbnails(listing)
|
||||||
return programs
|
})
|
||||||
}
|
}
|
||||||
}
|
})
|
||||||
|
|
||||||
|
return programs
|
||||||
function parseTime(timestamp) {
|
}
|
||||||
return dayjs(timestamp).utc().format('YYYY-MM-DD HH:mm')
|
}
|
||||||
}
|
|
||||||
|
function parseTime(timestamp) {
|
||||||
function parseSeason(item) {
|
return dayjs(timestamp).utc().format('YYYY-MM-DD HH:mm')
|
||||||
if (!item.mediasetlisting$shortDescription) return null
|
}
|
||||||
const season = item.mediasetlisting$shortDescription.match(/S(\d+)\s/)
|
|
||||||
return season ? season[1] : null
|
function parseSeason(item) {
|
||||||
}
|
if (!item.mediasetlisting$shortDescription) return null
|
||||||
|
const season = item.mediasetlisting$shortDescription.match(/S(\d+)\s/)
|
||||||
function parseEpisode(item) {
|
return season ? season[1] : null
|
||||||
if (!item.mediasetlisting$shortDescription) return null
|
}
|
||||||
const episode = item.mediasetlisting$shortDescription.match(/Ep(\d+)\s/)
|
|
||||||
return episode ? episode[1] : null
|
function parseEpisode(item) {
|
||||||
}
|
if (!item.mediasetlisting$shortDescription) return null
|
||||||
|
const episode = item.mediasetlisting$shortDescription.match(/Ep(\d+)\s/)
|
||||||
function getMaxResolutionThumbnails(item) {
|
return episode ? episode[1] : null
|
||||||
const thumbnails = item.program.thumbnails || null
|
}
|
||||||
const maxResolutionThumbnails = {}
|
|
||||||
|
function getMaxResolutionThumbnails(item) {
|
||||||
for (const key in thumbnails) {
|
const thumbnails = item.program.thumbnails || null
|
||||||
const type = key.split('-')[0] // Estrarre il tipo di thumbnail
|
const maxResolutionThumbnails = {}
|
||||||
const {width, height, url, title} = thumbnails[key]
|
|
||||||
|
for (const key in thumbnails) {
|
||||||
if (!maxResolutionThumbnails[type] ||
|
const type = key.split('-')[0] // Estrarre il tipo di thumbnail
|
||||||
(width * height > maxResolutionThumbnails[type].width * maxResolutionThumbnails[type].height)) {
|
const { width, height, url, title } = thumbnails[key]
|
||||||
maxResolutionThumbnails[type] = {width, height, url, title}
|
|
||||||
}
|
if (
|
||||||
}
|
!maxResolutionThumbnails[type] ||
|
||||||
if (maxResolutionThumbnails.image_keyframe_poster)
|
width * height > maxResolutionThumbnails[type].width * maxResolutionThumbnails[type].height
|
||||||
return maxResolutionThumbnails.image_keyframe_poster.url
|
) {
|
||||||
else if (maxResolutionThumbnails.image_header_poster)
|
maxResolutionThumbnails[type] = { width, height, url, title }
|
||||||
return maxResolutionThumbnails.image_header_poster.url
|
}
|
||||||
else
|
}
|
||||||
return null
|
if (maxResolutionThumbnails.image_keyframe_poster)
|
||||||
}
|
return maxResolutionThumbnails.image_keyframe_poster.url
|
||||||
|
else if (maxResolutionThumbnails.image_header_poster)
|
||||||
|
return maxResolutionThumbnails.image_header_poster.url
|
||||||
|
else return null
|
||||||
|
}
|
||||||
|
|
|
@ -1,46 +1,53 @@
|
||||||
const {parser, url} = require('./mediasetinfinity.mediaset.it.config.js')
|
const { parser, url } = require('./mediasetinfinity.mediaset.it.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2024-01-20', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2024-01-20', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'LB', xmltv_id: '20.it'
|
site_id: 'LB',
|
||||||
}
|
xmltv_id: '20.it'
|
||||||
|
}
|
||||||
it('can generate valid url', () => {
|
|
||||||
expect(url({
|
it('can generate valid url', () => {
|
||||||
channel,
|
expect(
|
||||||
date
|
url({
|
||||||
})).toBe('https://api-ott-prod-fe.mediaset.net/PROD/play/feed/allListingFeedEpg/v2.0?byListingTime=1705708800000~1705795200000&byCallSign=LB')
|
channel,
|
||||||
})
|
date
|
||||||
|
})
|
||||||
it('can parse response', () => {
|
).toBe(
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
'https://api-ott-prod-fe.mediaset.net/PROD/play/feed/allListingFeedEpg/v2.0?byListingTime=1705708800000~1705795200000&byCallSign=LB'
|
||||||
const results = parser({content, date}).map(p => {
|
)
|
||||||
return p
|
})
|
||||||
})
|
|
||||||
|
it('can parse response', () => {
|
||||||
expect(results[3]).toMatchObject({
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
||||||
start: '2024-01-20 02:14',
|
const results = parser({ content, date }).map(p => {
|
||||||
stop: '2024-01-20 02:54',
|
return p
|
||||||
title: 'Chicago Fire',
|
})
|
||||||
sub_title: 'Ep. 22 - Io non ti lascio',
|
|
||||||
description: 'Severide e Kidd continuano a indagare su un vecchio caso doloso di Benny. Notizie inaspettate portano Brett a meditare su una grande decisione.',
|
expect(results[3]).toMatchObject({
|
||||||
category: 'Intrattenimento',
|
start: '2024-01-20 02:14',
|
||||||
season: '7',
|
stop: '2024-01-20 02:54',
|
||||||
episode: '22',
|
title: 'Chicago Fire',
|
||||||
image: 'https://static2.mediasetplay.mediaset.it/Mediaset_Italia_Production_-_Main/F309370301002204/media/0/0/1ef76b73-3173-43bd-9c16-73986a0ec131/46896726-11e7-4438-b947-d2ae53f58c0b.jpg'
|
sub_title: 'Ep. 22 - Io non ti lascio',
|
||||||
})
|
description:
|
||||||
})
|
'Severide e Kidd continuano a indagare su un vecchio caso doloso di Benny. Notizie inaspettate portano Brett a meditare su una grande decisione.',
|
||||||
|
category: 'Intrattenimento',
|
||||||
it('can handle empty guide', () => {
|
season: '7',
|
||||||
const result = parser({
|
episode: '22',
|
||||||
content: '[]'
|
image:
|
||||||
})
|
'https://static2.mediasetplay.mediaset.it/Mediaset_Italia_Production_-_Main/F309370301002204/media/0/0/1ef76b73-3173-43bd-9c16-73986a0ec131/46896726-11e7-4438-b947-d2ae53f58c0b.jpg'
|
||||||
expect(result).toMatchObject([])
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
it('can handle empty guide', () => {
|
||||||
|
const result = parser({
|
||||||
|
content: '[]'
|
||||||
|
})
|
||||||
|
expect(result).toMatchObject([])
|
||||||
|
})
|
||||||
|
|
|
@ -40,7 +40,7 @@ module.exports = {
|
||||||
async channels() {
|
async channels() {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.post(`https://authservice.apps.meo.pt/Services/GridTv/GridTvMng.svc/getGridAnon`, null, {
|
.post('https://authservice.apps.meo.pt/Services/GridTv/GridTvMng.svc/getGridAnon', null, {
|
||||||
headers: {
|
headers: {
|
||||||
Origin: 'https://www.meo.pt'
|
Origin: 'https://www.meo.pt'
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,101 +1,105 @@
|
||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'meuguia.tv',
|
site: 'meuguia.tv',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel }) {
|
url({ channel }) {
|
||||||
return `https://meuguia.tv/programacao/canal/${channel.site_id}`
|
return `https://meuguia.tv/programacao/canal/${channel.site_id}`
|
||||||
},
|
},
|
||||||
parser({ content, date }) {
|
parser({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
parseItems(content, date).forEach(item => {
|
parseItems(content, date).forEach(item => {
|
||||||
if (dayjs.utc(item.start).isSame(date, 'day')) {
|
if (dayjs.utc(item.start).isSame(date, 'day')) {
|
||||||
programs.push(item)
|
programs.push(item)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const channels = []
|
const channels = []
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const baseUrl = 'https://meuguia.tv'
|
const baseUrl = 'https://meuguia.tv'
|
||||||
|
|
||||||
let seq = 0
|
let seq = 0
|
||||||
const queues = [baseUrl]
|
const queues = [baseUrl]
|
||||||
while (true) {
|
while (true) {
|
||||||
if (!queues.length) {
|
if (!queues.length) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
const url = queues.shift()
|
const url = queues.shift()
|
||||||
const content = await axios
|
const content = await axios
|
||||||
.get(url)
|
.get(url)
|
||||||
.then(response => response.data)
|
.then(response => response.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
if (content) {
|
if (content) {
|
||||||
const [ $, items ] = getItems(content)
|
const [$, items] = getItems(content)
|
||||||
if (seq === 0) {
|
if (seq === 0) {
|
||||||
queues.push(...items.map(category => baseUrl + $(category).attr('href')))
|
queues.push(...items.map(category => baseUrl + $(category).attr('href')))
|
||||||
} else {
|
} else {
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const href = $(item).attr('href')
|
const href = $(item).attr('href')
|
||||||
channels.push({
|
channels.push({
|
||||||
lang: 'pt',
|
lang: 'pt',
|
||||||
site_id: href.substr(href.lastIndexOf('/') + 1),
|
site_id: href.substr(href.lastIndexOf('/') + 1),
|
||||||
name: $(item).find('.licontent h2').text().trim()
|
name: $(item).find('.licontent h2').text().trim()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
seq++
|
seq++
|
||||||
}
|
}
|
||||||
|
|
||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function getItems(content) {
|
function getItems(content) {
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
return [$, $('div.mw ul li a').toArray()]
|
return [$, $('div.mw ul li a').toArray()]
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, date) {
|
function parseItems(content, date) {
|
||||||
const result = []
|
const result = []
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
let lastDate
|
let lastDate
|
||||||
for (const item of $('ul.mw li').toArray()) {
|
for (const item of $('ul.mw li').toArray()) {
|
||||||
const $item = $(item)
|
const $item = $(item)
|
||||||
if ($item.hasClass('subheader')) {
|
if ($item.hasClass('subheader')) {
|
||||||
lastDate = `${$item.text().split(', ')[1]}/${date.format('YYYY')}`
|
lastDate = `${$item.text().split(', ')[1]}/${date.format('YYYY')}`
|
||||||
} else if ($item.hasClass('divider')) {
|
} else if ($item.hasClass('divider')) {
|
||||||
// ignore
|
// ignore
|
||||||
} else if (lastDate) {
|
} else if (lastDate) {
|
||||||
const data = { title: $item.find('a').attr('title').trim() }
|
const data = { title: $item.find('a').attr('title').trim() }
|
||||||
const ep = data.title.match(/T(\d+) EP(\d+)/)
|
const ep = data.title.match(/T(\d+) EP(\d+)/)
|
||||||
if (ep) {
|
if (ep) {
|
||||||
data.season = parseInt(ep[1])
|
data.season = parseInt(ep[1])
|
||||||
data.episode = parseInt(ep[2])
|
data.episode = parseInt(ep[2])
|
||||||
}
|
}
|
||||||
data.start = dayjs.tz(`${lastDate} ${$item.find('.time').text()}`, 'DD/MM/YYYY HH:mm', 'America/Sao_Paulo')
|
data.start = dayjs.tz(
|
||||||
result.push(data)
|
`${lastDate} ${$item.find('.time').text()}`,
|
||||||
}
|
'DD/MM/YYYY HH:mm',
|
||||||
}
|
'America/Sao_Paulo'
|
||||||
// use stop time from next item
|
)
|
||||||
if (result.length > 1) {
|
result.push(data)
|
||||||
for (let i = 0; i < result.length - 1; i++) {
|
}
|
||||||
result[i].stop = result[i + 1].start
|
}
|
||||||
}
|
// use stop time from next item
|
||||||
}
|
if (result.length > 1) {
|
||||||
|
for (let i = 0; i < result.length - 1; i++) {
|
||||||
return result
|
result[i].stop = result[i + 1].start
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
|
@ -1,60 +1,60 @@
|
||||||
const { parser, url } = require('./meuguia.tv.config.js')
|
const { parser, url } = require('./meuguia.tv.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2023-11-21').startOf('d')
|
const date = dayjs.utc('2023-11-21').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'AXN',
|
site_id: 'AXN',
|
||||||
xmltv_id: 'AXN.id'
|
xmltv_id: 'AXN.id'
|
||||||
}
|
}
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel })).toBe('https://meuguia.tv/programacao/canal/AXN')
|
expect(url({ channel })).toBe('https://meuguia.tv/programacao/canal/AXN')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||||
const result = parser({ content, channel, date }).map(p => {
|
const result = parser({ content, channel, date }).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
if (p.stop) {
|
if (p.stop) {
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
}
|
}
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(result).toMatchObject([
|
expect(result).toMatchObject([
|
||||||
{
|
{
|
||||||
title: 'Hawaii Five-0 : T10 EP4 - Tiny Is the Flower, Yet It Scents the Grasses Around It',
|
title: 'Hawaii Five-0 : T10 EP4 - Tiny Is the Flower, Yet It Scents the Grasses Around It',
|
||||||
start: '2023-11-21T21:20:00.000Z',
|
start: '2023-11-21T21:20:00.000Z',
|
||||||
stop: '2023-11-21T22:15:00.000Z',
|
stop: '2023-11-21T22:15:00.000Z',
|
||||||
season: 10,
|
season: 10,
|
||||||
episode: 4
|
episode: 4
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title:
|
title:
|
||||||
"Hawaii Five-0 : T10 EP5 - Don't Blame Ghosts and Spirits for One's Troubles; A Human Is Responsible",
|
"Hawaii Five-0 : T10 EP5 - Don't Blame Ghosts and Spirits for One's Troubles; A Human Is Responsible",
|
||||||
start: '2023-11-21T22:15:00.000Z',
|
start: '2023-11-21T22:15:00.000Z',
|
||||||
stop: '2023-11-21T23:10:00.000Z',
|
stop: '2023-11-21T23:10:00.000Z',
|
||||||
season: 10,
|
season: 10,
|
||||||
episode: 5
|
episode: 5
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: 'NCIS : T5 EP15 - In the Zone',
|
title: 'NCIS : T5 EP15 - In the Zone',
|
||||||
start: '2023-11-21T23:10:00.000Z',
|
start: '2023-11-21T23:10:00.000Z',
|
||||||
season: 5,
|
season: 5,
|
||||||
episode: 15
|
episode: 15
|
||||||
}
|
}
|
||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({
|
const result = parser({
|
||||||
date,
|
date,
|
||||||
channel,
|
channel,
|
||||||
content: '<!DOCTYPE html><html><head></head><body></body></html>'
|
content: '<!DOCTYPE html><html><head></head><body></body></html>'
|
||||||
})
|
})
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|
|
@ -42,7 +42,7 @@ module.exports = {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://www.mewatch.sg/channel-guide`)
|
.get('https://www.mewatch.sg/channel-guide')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,7 @@ dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
doFetch
|
doFetch.setCheckResult(false).setDebugger(debug)
|
||||||
.setCheckResult(false)
|
|
||||||
.setDebugger(debug)
|
|
||||||
|
|
||||||
const languages = { en: 'english', id: 'indonesia' }
|
const languages = { en: 'english', id: 'indonesia' }
|
||||||
const cookies = {}
|
const cookies = {}
|
||||||
|
@ -125,7 +123,7 @@ async function parseItems(content, date, cookies) {
|
||||||
const url = $item.find('a').attr('href')
|
const url = $item.find('a').attr('href')
|
||||||
const headers = {
|
const headers = {
|
||||||
'X-Requested-With': 'XMLHttpRequest',
|
'X-Requested-With': 'XMLHttpRequest',
|
||||||
Cookie: cookies,
|
Cookie: cookies
|
||||||
}
|
}
|
||||||
queues.push({ i: $item, url, params: { headers, timeout } })
|
queues.push({ i: $item, url, params: { headers, timeout } })
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,8 +48,10 @@ function parseItems(context) {
|
||||||
schDayPrograms.forEach((program, i) => {
|
schDayPrograms.forEach((program, i) => {
|
||||||
const itemDay = {
|
const itemDay = {
|
||||||
progStart: parseStart($(schDayMonth), $(program)),
|
progStart: parseStart($(schDayMonth), $(program)),
|
||||||
progStop: parseStop($(schDayMonth), schDayPrograms[i + 1] ?
|
progStop: parseStop(
|
||||||
$(schDayPrograms[i + 1]) : null),
|
$(schDayMonth),
|
||||||
|
schDayPrograms[i + 1] ? $(schDayPrograms[i + 1]) : null
|
||||||
|
),
|
||||||
progTitle: parseTitle($(program)),
|
progTitle: parseTitle($(program)),
|
||||||
progDesc: parseDescription($(program))
|
progDesc: parseDescription($(program))
|
||||||
}
|
}
|
||||||
|
@ -91,7 +93,9 @@ function parseStop(schDayMonth, itemNext) {
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return dayjs.tz(
|
return dayjs.tz(
|
||||||
`${currentYear}-${monthDate[0]}-${(parseInt(monthDate[1]) + 1).toString().padStart(2, '0')} 00:00`,
|
`${currentYear}-${monthDate[0]}-${(parseInt(monthDate[1]) + 1)
|
||||||
|
.toString()
|
||||||
|
.padStart(2, '0')} 00:00`,
|
||||||
'YYYY-MMM-DD HH:mm',
|
'YYYY-MMM-DD HH:mm',
|
||||||
tz
|
tz
|
||||||
)
|
)
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -41,7 +41,7 @@ module.exports = {
|
||||||
const pages = Array.from(Array(totalPages).keys())
|
const pages = Array.from(Array(totalPages).keys())
|
||||||
for (let page of pages) {
|
for (let page of pages) {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://mtel.ba/oec/epg/program`, {
|
.get('https://mtel.ba/oec/epg/program', {
|
||||||
params: { page, date: dayjs().format('YYYY-MM-DD') },
|
params: { page, date: dayjs().format('YYYY-MM-DD') },
|
||||||
headers: {
|
headers: {
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
@ -65,7 +65,7 @@ module.exports = {
|
||||||
|
|
||||||
async function getTotalPageCount() {
|
async function getTotalPageCount() {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://mtel.ba/oec/epg/program`, {
|
.get('https://mtel.ba/oec/epg/program', {
|
||||||
params: { page: 0, date: dayjs().format('YYYY-MM-DD') },
|
params: { page: 0, date: dayjs().format('YYYY-MM-DD') },
|
||||||
headers: {
|
headers: {
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
|
|
@ -43,7 +43,7 @@ module.exports = {
|
||||||
const pages = Array.from(Array(totalPages).keys())
|
const pages = Array.from(Array(totalPages).keys())
|
||||||
for (let page of pages) {
|
for (let page of pages) {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://mts.rs/oec/epg/program`, {
|
.get('https://mts.rs/oec/epg/program', {
|
||||||
params: { page, date: dayjs().format('YYYY-MM-DD') },
|
params: { page, date: dayjs().format('YYYY-MM-DD') },
|
||||||
headers: {
|
headers: {
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
@ -67,7 +67,7 @@ module.exports = {
|
||||||
|
|
||||||
async function getTotalPageCount() {
|
async function getTotalPageCount() {
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://mts.rs/oec/epg/program`, {
|
.get('https://mts.rs/oec/epg/program', {
|
||||||
params: { page: 0, date: dayjs().format('YYYY-MM-DD') },
|
params: { page: 0, date: dayjs().format('YYYY-MM-DD') },
|
||||||
headers: {
|
headers: {
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
'X-Requested-With': 'XMLHttpRequest'
|
||||||
|
@ -84,8 +84,8 @@ function parseContent(content, channel) {
|
||||||
let data
|
let data
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(content)
|
data = JSON.parse(content)
|
||||||
} catch (error) {
|
} catch {
|
||||||
console.log(error)
|
return []
|
||||||
}
|
}
|
||||||
if (!data || !data.channels || !data.channels.length) return null
|
if (!data || !data.channels || !data.channels.length) return null
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ module.exports = {
|
||||||
|
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.post(
|
.post(
|
||||||
`https://services.mujtvprogram.cz/tvprogram2services/services/tvchannellist_mobile.php`,
|
'https://services.mujtvprogram.cz/tvprogram2services/services/tvchannellist_mobile.php',
|
||||||
params,
|
params,
|
||||||
{
|
{
|
||||||
headers: {
|
headers: {
|
||||||
|
@ -86,7 +86,7 @@ function parseItems(content) {
|
||||||
if (!data) return []
|
if (!data) return []
|
||||||
const programmes = data['tv-program-programmes'].programme
|
const programmes = data['tv-program-programmes'].programme
|
||||||
return programmes && Array.isArray(programmes) ? programmes : []
|
return programmes && Array.isArray(programmes) ? programmes : []
|
||||||
} catch (err) {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
'User-Agent':
|
'User-Agent':
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 OPR/115.0.0.0',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36 OPR/115.0.0.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
|
|
|
@ -26,12 +26,11 @@ it('can generate valid url for today', () => {
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||||
const results = parser({ content, date })
|
const results = parser({ content, date }).map(p => {
|
||||||
.map(p => {
|
p.start = p.start.toJSON()
|
||||||
p.start = p.start.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
return p
|
||||||
return p
|
})
|
||||||
})
|
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2022-11-19T23:00:00.000Z',
|
start: '2022-11-19T23:00:00.000Z',
|
||||||
|
|
|
@ -23,14 +23,15 @@ module.exports = {
|
||||||
channel.site_id
|
channel.site_id
|
||||||
}.html?dt=${date.format('YYYY-MM-DD')}`
|
}.html?dt=${date.format('YYYY-MM-DD')}`
|
||||||
},
|
},
|
||||||
async parser({ content, date, channel }) {
|
async parser({ content, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
|
|
||||||
if (content) {
|
if (content) {
|
||||||
const queues = []
|
const queues = []
|
||||||
const $ = cheerio.load(content)
|
const $ = cheerio.load(content)
|
||||||
|
|
||||||
$('table.table > tbody > tr').toArray()
|
$('table.table > tbody > tr')
|
||||||
|
.toArray()
|
||||||
.forEach(el => {
|
.forEach(el => {
|
||||||
const td = $(el).find('td:eq(1)')
|
const td = $(el).find('td:eq(1)')
|
||||||
const title = td.find('h5 a')
|
const title = td.find('h5 a')
|
||||||
|
@ -66,12 +67,16 @@ module.exports = {
|
||||||
const subTitle = parseText($('.tab-pane > h5 > strong'))
|
const subTitle = parseText($('.tab-pane > h5 > strong'))
|
||||||
const description = parseText($('.tab-pane > .tvbody > p'))
|
const description = parseText($('.tab-pane > .tvbody > p'))
|
||||||
const image = $('.program-media-image img').attr('src')
|
const image = $('.program-media-image img').attr('src')
|
||||||
const category = $('.schedule-attributes-genres span').toArray()
|
const category = $('.schedule-attributes-genres span')
|
||||||
|
.toArray()
|
||||||
.map(el => $(el).text())
|
.map(el => $(el).text())
|
||||||
const casts = $('.single-cast-head:not([id])').toArray()
|
const casts = $('.single-cast-head:not([id])')
|
||||||
|
.toArray()
|
||||||
.map(el => {
|
.map(el => {
|
||||||
const cast = { name: parseText($(el).find('a')) }
|
const cast = { name: parseText($(el).find('a')) }
|
||||||
const [, role] = $(el).text().match(/\((.*)\)/) || [null, null]
|
const [, role] = $(el)
|
||||||
|
.text()
|
||||||
|
.match(/\((.*)\)/) || [null, null]
|
||||||
if (role) {
|
if (role) {
|
||||||
cast.role = role
|
cast.role = role
|
||||||
}
|
}
|
||||||
|
@ -102,7 +107,7 @@ module.exports = {
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,11 +120,17 @@ module.exports = {
|
||||||
// process form -> provider
|
// process form -> provider
|
||||||
if (queue.t === 'p') {
|
if (queue.t === 'p') {
|
||||||
const $ = cheerio.load(res)
|
const $ = cheerio.load(res)
|
||||||
$('#guide_provider option').toArray()
|
$('#guide_provider option')
|
||||||
|
.toArray()
|
||||||
.forEach(el => {
|
.forEach(el => {
|
||||||
const opt = $(el)
|
const opt = $(el)
|
||||||
const provider = opt.attr('value')
|
const provider = opt.attr('value')
|
||||||
queues.push({ t: 'r', method: 'post', url: 'https://www.mytelly.co.uk/getregions', params: { provider } })
|
queues.push({
|
||||||
|
t: 'r',
|
||||||
|
method: 'post',
|
||||||
|
url: 'https://www.mytelly.co.uk/getregions',
|
||||||
|
params: { provider }
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
// process provider -> region
|
// process provider -> region
|
||||||
|
@ -135,26 +146,30 @@ module.exports = {
|
||||||
u_time: now.format('HHmm'),
|
u_time: now.format('HHmm'),
|
||||||
is_mobile: 1
|
is_mobile: 1
|
||||||
}
|
}
|
||||||
queues.push({ t: 's', method: 'post', url: 'https://www.mytelly.co.uk/tv-guide/schedule', params })
|
queues.push({
|
||||||
|
t: 's',
|
||||||
|
method: 'post',
|
||||||
|
url: 'https://www.mytelly.co.uk/tv-guide/schedule',
|
||||||
|
params
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// process schedule -> channels
|
// process schedule -> channels
|
||||||
if (queue.t === 's') {
|
if (queue.t === 's') {
|
||||||
const $ = cheerio.load(res)
|
const $ = cheerio.load(res)
|
||||||
$('.channelname')
|
$('.channelname').each((i, el) => {
|
||||||
.each((i, el) => {
|
const name = $(el).find('center > a:eq(1)').text()
|
||||||
const name = $(el).find('center > a:eq(1)').text()
|
const url = $(el).find('center > a:eq(1)').attr('href')
|
||||||
const url = $(el).find('center > a:eq(1)').attr('href')
|
const [, number, slug] = url.match(/\/(\d+)\/(.*)\.html$/)
|
||||||
const [, number, slug] = url.match(/\/(\d+)\/(.*)\.html$/)
|
const site_id = `${number}/${slug}`
|
||||||
const site_id = `${number}/${slug}`
|
if (channels[site_id] === undefined) {
|
||||||
if (channels[site_id] === undefined) {
|
channels[site_id] = {
|
||||||
channels[site_id] = {
|
lang: 'en',
|
||||||
lang: 'en',
|
site_id,
|
||||||
site_id,
|
name
|
||||||
name
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -178,13 +193,10 @@ function parseTime(date, time) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseText($item) {
|
function parseText($item) {
|
||||||
let text = $item.text()
|
let text = $item.text().replace(/\t/g, '').replace(/\n/g, ' ').trim()
|
||||||
.replace(/\t/g, '')
|
|
||||||
.replace(/\n/g, ' ')
|
|
||||||
.trim()
|
|
||||||
while (true) {
|
while (true) {
|
||||||
if (text.match(/ /)) {
|
if (text.match(/\s\s/)) {
|
||||||
text = text.replace(/ /g, ' ')
|
text = text.replace(/\s\s/g, ' ')
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
|
|
|
@ -17,16 +17,18 @@ const channel = {
|
||||||
xmltv_id: 'BBCOneLondon.uk'
|
xmltv_id: 'BBCOneLondon.uk'
|
||||||
}
|
}
|
||||||
|
|
||||||
axios.get.mockImplementation((url, opts) => {
|
axios.get.mockImplementation(url => {
|
||||||
if (
|
if (
|
||||||
url === 'https://www.mytelly.co.uk/tv-guide/listings/programme?cid=713&pid=1906433&tm=2024-12-07+00%3A00%3A00'
|
url ===
|
||||||
|
'https://www.mytelly.co.uk/tv-guide/listings/programme?cid=713&pid=1906433&tm=2024-12-07+00%3A00%3A00'
|
||||||
) {
|
) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: fs.readFileSync(path.join(__dirname, '__data__', 'programme.html'))
|
data: fs.readFileSync(path.join(__dirname, '__data__', 'programme.html'))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if (
|
if (
|
||||||
url === 'https://www.mytelly.co.uk/tv-guide/listings/programme?cid=713&pid=5656624&tm=2024-12-07+23%3A35%3A00'
|
url ===
|
||||||
|
'https://www.mytelly.co.uk/tv-guide/listings/programme?cid=713&pid=5656624&tm=2024-12-07+23%3A35%3A00'
|
||||||
) {
|
) {
|
||||||
return Promise.resolve({
|
return Promise.resolve({
|
||||||
data: fs.readFileSync(path.join(__dirname, '__data__', 'programme2.html'))
|
data: fs.readFileSync(path.join(__dirname, '__data__', 'programme2.html'))
|
||||||
|
@ -57,7 +59,8 @@ it('can parse response', async () => {
|
||||||
title: 'Captain Phillips',
|
title: 'Captain Phillips',
|
||||||
description:
|
description:
|
||||||
'An American cargo ship sets a dangerous course around the coast of Somalia, while inland, four men are pressed into service as pirates by the local warlords. The captain is taken hostage when the raiding party hijacks the vessel, resulting in a tense five-day crisis. Fact-based thriller, starring Tom Hanks and Barkhad Abdi',
|
'An American cargo ship sets a dangerous course around the coast of Somalia, while inland, four men are pressed into service as pirates by the local warlords. The captain is taken hostage when the raiding party hijacks the vessel, resulting in a tense five-day crisis. Fact-based thriller, starring Tom Hanks and Barkhad Abdi',
|
||||||
image: 'https://d16ia5iwuvax6y.cloudfront.net/uk-prog-images/c44ce7b0d3ae602c0c93ece5af140815.jpg?k=VeeNdUjml3bSHdlZ0OXbGLy%2BmsLdYPwTV6iAxGkzq4dsylOCGGE7OWlqwSWt0cd0Qtrin4DkEMC0Zzdp8ZeNk2vNIQzjMF0DG0h3IeTR5NM%3D',
|
image:
|
||||||
|
'https://d16ia5iwuvax6y.cloudfront.net/uk-prog-images/c44ce7b0d3ae602c0c93ece5af140815.jpg?k=VeeNdUjml3bSHdlZ0OXbGLy%2BmsLdYPwTV6iAxGkzq4dsylOCGGE7OWlqwSWt0cd0Qtrin4DkEMC0Zzdp8ZeNk2vNIQzjMF0DG0h3IeTR5NM%3D',
|
||||||
category: ['Factual', 'Movie/Drama', 'Thriller']
|
category: ['Factual', 'Movie/Drama', 'Thriller']
|
||||||
})
|
})
|
||||||
expect(results[1]).toMatchObject({
|
expect(results[1]).toMatchObject({
|
||||||
|
@ -67,7 +70,8 @@ it('can parse response', async () => {
|
||||||
subTitle: 'Past and Pressure Season 6, Episode 5',
|
subTitle: 'Past and Pressure Season 6, Episode 5',
|
||||||
description:
|
description:
|
||||||
'The artists are tasked with writing a song about their heritage. For some, the pressure of the competition proves too much for them to match. In their final challenge, they are put face to face with industry experts who grill them about their plans after the competition. Some impress, while others leave the mentors confused',
|
'The artists are tasked with writing a song about their heritage. For some, the pressure of the competition proves too much for them to match. In their final challenge, they are put face to face with industry experts who grill them about their plans after the competition. Some impress, while others leave the mentors confused',
|
||||||
image: 'https://d16ia5iwuvax6y.cloudfront.net/uk-prog-images/2039278182b27cc279570b9ab9b89379.jpg?k=VeeNdUjml3bSHdlZ0OXbGLy%2BmsLdYPwTV6iAxGkzq4cDhR7jXTNFW3tgwQCdOPUobhXwlT81mIsqOe93HPusDG6tw1aoeYOgafojtynNWxc%3D',
|
image:
|
||||||
|
'https://d16ia5iwuvax6y.cloudfront.net/uk-prog-images/2039278182b27cc279570b9ab9b89379.jpg?k=VeeNdUjml3bSHdlZ0OXbGLy%2BmsLdYPwTV6iAxGkzq4cDhR7jXTNFW3tgwQCdOPUobhXwlT81mIsqOe93HPusDG6tw1aoeYOgafojtynNWxc%3D',
|
||||||
category: ['Challenge/Reality Show', 'Show/Game Show'],
|
category: ['Challenge/Reality Show', 'Show/Game Show'],
|
||||||
season: 6,
|
season: 6,
|
||||||
episode: 5
|
episode: 5
|
||||||
|
|
|
@ -1,73 +1,80 @@
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'neo.io',
|
site: 'neo.io',
|
||||||
timezone: 'Europe/Ljubljana',
|
timezone: 'Europe/Ljubljana',
|
||||||
days: 5,
|
days: 5,
|
||||||
url({ date, channel }) { return 'https://stargate.telekom.si/api/titan.tv.WebEpg/GetWebEpgData' },
|
url() {
|
||||||
request: {
|
return 'https://stargate.telekom.si/api/titan.tv.WebEpg/GetWebEpgData'
|
||||||
method: 'POST',
|
},
|
||||||
headers: {
|
request: {
|
||||||
'Host': 'stargate.telekom.si',
|
method: 'POST',
|
||||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
|
headers: {
|
||||||
'Accept': 'application/json, text/plain, */*',
|
Host: 'stargate.telekom.si',
|
||||||
'Accept-Language': 'nl,en-US;q=0.7,en;q=0.3',
|
'User-Agent':
|
||||||
'Content-Type': 'application/json',
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
|
||||||
'X-AppLayout': '1',
|
Accept: 'application/json, text/plain, */*',
|
||||||
'x-language': 'sl',
|
'Accept-Language': 'nl,en-US;q=0.7,en;q=0.3',
|
||||||
'Origin': 'https://neo.io',
|
'Content-Type': 'application/json',
|
||||||
'Sec-Fetch-Dest': 'empty',
|
'X-AppLayout': '1',
|
||||||
'Sec-Fetch-Mode': 'cors',
|
'x-language': 'sl',
|
||||||
'Sec-Fetch-Site': 'cross-site',
|
Origin: 'https://neo.io',
|
||||||
'Sec-GPC': '1',
|
'Sec-Fetch-Dest': 'empty',
|
||||||
'Connection': 'keep-alive'
|
'Sec-Fetch-Mode': 'cors',
|
||||||
},
|
'Sec-Fetch-Site': 'cross-site',
|
||||||
data({ channel, date }) {
|
'Sec-GPC': '1',
|
||||||
const todayEpoch = date.startOf('day').unix();
|
Connection: 'keep-alive'
|
||||||
const nextDayEpoch = date.add(1, 'day').startOf('day').unix();
|
},
|
||||||
return JSON.stringify({
|
data({ channel, date }) {
|
||||||
ch_ext_id: channel.site_id,
|
const todayEpoch = date.startOf('day').unix()
|
||||||
from: todayEpoch,
|
const nextDayEpoch = date.add(1, 'day').startOf('day').unix()
|
||||||
to: nextDayEpoch
|
return JSON.stringify({
|
||||||
})
|
ch_ext_id: channel.site_id,
|
||||||
}
|
from: todayEpoch,
|
||||||
},
|
to: nextDayEpoch
|
||||||
parser: function ({ content }) {
|
})
|
||||||
const programs = [];
|
}
|
||||||
const data = JSON.parse(content);
|
},
|
||||||
data.shows.forEach(show => {
|
parser: function ({ content }) {
|
||||||
const start = dayjs.unix(show.show_start).utc();
|
const programs = []
|
||||||
const stop = dayjs.unix(show.show_end).utc();
|
const data = JSON.parse(content)
|
||||||
const programData = {
|
data.shows.forEach(show => {
|
||||||
title: show.title,
|
const start = dayjs.unix(show.show_start).utc()
|
||||||
description: show.summary || 'No description available',
|
const stop = dayjs.unix(show.show_end).utc()
|
||||||
start: start.toISOString(),
|
const programData = {
|
||||||
stop: stop.toISOString(),
|
title: show.title,
|
||||||
thumbnail: show.thumbnail
|
description: show.summary || 'No description available',
|
||||||
}
|
start: start.toISOString(),
|
||||||
programs.push(programData)
|
stop: stop.toISOString(),
|
||||||
})
|
thumbnail: show.thumbnail
|
||||||
return programs
|
}
|
||||||
},
|
programs.push(programData)
|
||||||
async channels() {
|
})
|
||||||
const response = await axios.post('https://stargate.telekom.si/api/titan.tv.WebEpg/ZapList', JSON.stringify({ includeRadioStations: true }), {
|
return programs
|
||||||
headers: this.request.headers
|
},
|
||||||
});
|
async channels() {
|
||||||
|
const response = await axios.post(
|
||||||
const data = response.data.data;
|
'https://stargate.telekom.si/api/titan.tv.WebEpg/ZapList',
|
||||||
return data.map(item => ({
|
JSON.stringify({ includeRadioStations: true }),
|
||||||
lang: 'sq',
|
{
|
||||||
name: String(item.channel.title),
|
headers: this.request.headers
|
||||||
site_id: String(item.channel.id),
|
}
|
||||||
//logo: String(item.channel.logo)
|
)
|
||||||
}))
|
|
||||||
}
|
const data = response.data.data
|
||||||
}
|
return data.map(item => ({
|
||||||
|
lang: 'sq',
|
||||||
|
name: String(item.channel.title),
|
||||||
|
site_id: String(item.channel.id)
|
||||||
|
//logo: String(item.channel.logo)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,121 +1,124 @@
|
||||||
const { parser, url } = require('./neo.io.config.js')
|
const { parser, url } = require('./neo.io.config.js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2024-12-26', 'YYYY-MM-DD').startOf('day')
|
const date = dayjs.utc('2024-12-26', 'YYYY-MM-DD').startOf('day')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'tv-slo-1',
|
site_id: 'tv-slo-1',
|
||||||
xmltv_id: 'TVSLO1.si'
|
xmltv_id: 'TVSLO1.si'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ date, channel })).toBe('https://stargate.telekom.si/api/titan.tv.WebEpg/GetWebEpgData')
|
expect(url({ date, channel })).toBe(
|
||||||
})
|
'https://stargate.telekom.si/api/titan.tv.WebEpg/GetWebEpgData'
|
||||||
|
)
|
||||||
it('can parse response', () => {
|
})
|
||||||
const content = `
|
|
||||||
{
|
it('can parse response', () => {
|
||||||
"shows": [
|
const content = `
|
||||||
{
|
{
|
||||||
"title": "Napovedujemo",
|
"shows": [
|
||||||
"show_start": 1735185900,
|
{
|
||||||
"show_end": 1735192200,
|
"title": "Napovedujemo",
|
||||||
"timestamp": "5:05 - 6:50",
|
"show_start": 1735185900,
|
||||||
"show_id": "CUP_IECOM_SLO1_10004660",
|
"show_end": 1735192200,
|
||||||
"thumbnail": "https://ngimg.siol.tv/sioltv/mtcmsprod/52/0/0/5200d01a-fe5f-487e-835a-274e77227a6b.jpg",
|
"timestamp": "5:05 - 6:50",
|
||||||
"is_adult": false,
|
"show_id": "CUP_IECOM_SLO1_10004660",
|
||||||
"friendly_id": "napovedujemo_db48",
|
"thumbnail": "https://ngimg.siol.tv/sioltv/mtcmsprod/52/0/0/5200d01a-fe5f-487e-835a-274e77227a6b.jpg",
|
||||||
"pg": "",
|
"is_adult": false,
|
||||||
"genres": [
|
"friendly_id": "napovedujemo_db48",
|
||||||
"napovednik"
|
"pg": "",
|
||||||
],
|
"genres": [
|
||||||
"year": 0,
|
"napovednik"
|
||||||
"summary": "Vabilo k ogledu naših oddaj.",
|
],
|
||||||
"categories": "Ostalo",
|
"year": 0,
|
||||||
"stb_only": false,
|
"summary": "Vabilo k ogledu naših oddaj.",
|
||||||
"is_live": false,
|
"categories": "Ostalo",
|
||||||
"original_title": "Napovedujemo"
|
"stb_only": false,
|
||||||
},
|
"is_live": false,
|
||||||
{
|
"original_title": "Napovedujemo"
|
||||||
"title": "S0E0 - Hrabri zajčki: Prvi sneg",
|
},
|
||||||
"show_start": 1735192200,
|
{
|
||||||
"show_end": 1735192800,
|
"title": "S0E0 - Hrabri zajčki: Prvi sneg",
|
||||||
"timestamp": "6:50 - 7:00",
|
"show_start": 1735192200,
|
||||||
"show_id": "CUP_IECOM_SLO1_79637910",
|
"show_end": 1735192800,
|
||||||
"thumbnail": "https://ngimg.siol.tv/sioltv/mtcmsprod/d6/4/5/d6456f4a-4f0a-4825-90c1-1749abd59688.jpg",
|
"timestamp": "6:50 - 7:00",
|
||||||
"is_adult": false,
|
"show_id": "CUP_IECOM_SLO1_79637910",
|
||||||
"friendly_id": "hrabri_zajcki_prvi_sneg_1619",
|
"thumbnail": "https://ngimg.siol.tv/sioltv/mtcmsprod/d6/4/5/d6456f4a-4f0a-4825-90c1-1749abd59688.jpg",
|
||||||
"pg": "",
|
"is_adult": false,
|
||||||
"genres": [
|
"friendly_id": "hrabri_zajcki_prvi_sneg_1619",
|
||||||
"risanka"
|
"pg": "",
|
||||||
],
|
"genres": [
|
||||||
"year": 2020,
|
"risanka"
|
||||||
"summary": "Hrabri zajčki so prispeli v borov gozd in izkusili prvi sneg. Bob in Bu še nikoli nista videla snega. Mami kuha korenčkov kakav, Bu in Bob pa kmalu spoznata novega prijatelja, losa Danija.",
|
],
|
||||||
"categories": "Otroški/Mladinski",
|
"year": 2020,
|
||||||
"stb_only": false,
|
"summary": "Hrabri zajčki so prispeli v borov gozd in izkusili prvi sneg. Bob in Bu še nikoli nista videla snega. Mami kuha korenčkov kakav, Bu in Bob pa kmalu spoznata novega prijatelja, losa Danija.",
|
||||||
"is_live": false,
|
"categories": "Otroški/Mladinski",
|
||||||
"original_title": "S0E0 - Brave Bunnies"
|
"stb_only": false,
|
||||||
},
|
"is_live": false,
|
||||||
{
|
"original_title": "S0E0 - Brave Bunnies"
|
||||||
"title": "Dobro jutro",
|
},
|
||||||
"show_start": 1735192800,
|
{
|
||||||
"show_end": 1735203900,
|
"title": "Dobro jutro",
|
||||||
"timestamp": "7:00 - 10:05",
|
"show_start": 1735192800,
|
||||||
"show_id": "CUP_IECOM_SLO1_79637911",
|
"show_end": 1735203900,
|
||||||
"thumbnail": "https://ngimg.siol.tv/sioltv/mtcmsprod/e1/2/d/e12d8eb4-693a-43d3-89d4-fd96dade9f0f.jpg",
|
"timestamp": "7:00 - 10:05",
|
||||||
"is_adult": false,
|
"show_id": "CUP_IECOM_SLO1_79637911",
|
||||||
"friendly_id": "dobro_jutro_2f10",
|
"thumbnail": "https://ngimg.siol.tv/sioltv/mtcmsprod/e1/2/d/e12d8eb4-693a-43d3-89d4-fd96dade9f0f.jpg",
|
||||||
"pg": "",
|
"is_adult": false,
|
||||||
"genres": [
|
"friendly_id": "dobro_jutro_2f10",
|
||||||
"zabavna oddaja"
|
"pg": "",
|
||||||
],
|
"genres": [
|
||||||
"year": 2024,
|
"zabavna oddaja"
|
||||||
"summary": "Oddaja Dobro jutro poleg informativnih in zabavnih vsebin podaja koristne nasvete o najrazličnejših tematikah iz vsakdanjega življenja.",
|
],
|
||||||
"categories": "Razvedrilni program",
|
"year": 2024,
|
||||||
"stb_only": false,
|
"summary": "Oddaja Dobro jutro poleg informativnih in zabavnih vsebin podaja koristne nasvete o najrazličnejših tematikah iz vsakdanjega življenja.",
|
||||||
"is_live": false,
|
"categories": "Razvedrilni program",
|
||||||
"original_title": "Dobro jutro"
|
"stb_only": false,
|
||||||
}
|
"is_live": false,
|
||||||
]
|
"original_title": "Dobro jutro"
|
||||||
}`
|
}
|
||||||
|
]
|
||||||
const result = parser({ content, channel }).map(p => {
|
}`
|
||||||
p.start = p.start
|
|
||||||
p.stop = p.stop
|
const result = parser({ content, channel })
|
||||||
return p
|
|
||||||
})
|
expect(result).toMatchObject([
|
||||||
|
{
|
||||||
expect(result).toMatchObject([
|
title: 'Napovedujemo',
|
||||||
{
|
description: 'Vabilo k ogledu naših oddaj.',
|
||||||
title: "Napovedujemo",
|
start: '2024-12-26T04:05:00.000Z',
|
||||||
description: "Vabilo k ogledu naših oddaj.",
|
stop: '2024-12-26T05:50:00.000Z',
|
||||||
start: "2024-12-26T04:05:00.000Z",
|
thumbnail:
|
||||||
stop: "2024-12-26T05:50:00.000Z",
|
'https://ngimg.siol.tv/sioltv/mtcmsprod/52/0/0/5200d01a-fe5f-487e-835a-274e77227a6b.jpg'
|
||||||
thumbnail: "https://ngimg.siol.tv/sioltv/mtcmsprod/52/0/0/5200d01a-fe5f-487e-835a-274e77227a6b.jpg"
|
},
|
||||||
},
|
{
|
||||||
{
|
title: 'S0E0 - Hrabri zajčki: Prvi sneg',
|
||||||
title: "S0E0 - Hrabri zajčki: Prvi sneg",
|
description:
|
||||||
description: "Hrabri zajčki so prispeli v borov gozd in izkusili prvi sneg. Bob in Bu še nikoli nista videla snega. Mami kuha korenčkov kakav, Bu in Bob pa kmalu spoznata novega prijatelja, losa Danija.",
|
'Hrabri zajčki so prispeli v borov gozd in izkusili prvi sneg. Bob in Bu še nikoli nista videla snega. Mami kuha korenčkov kakav, Bu in Bob pa kmalu spoznata novega prijatelja, losa Danija.',
|
||||||
start: "2024-12-26T05:50:00.000Z",
|
start: '2024-12-26T05:50:00.000Z',
|
||||||
stop: "2024-12-26T06:00:00.000Z",
|
stop: '2024-12-26T06:00:00.000Z',
|
||||||
thumbnail: "https://ngimg.siol.tv/sioltv/mtcmsprod/d6/4/5/d6456f4a-4f0a-4825-90c1-1749abd59688.jpg"
|
thumbnail:
|
||||||
},
|
'https://ngimg.siol.tv/sioltv/mtcmsprod/d6/4/5/d6456f4a-4f0a-4825-90c1-1749abd59688.jpg'
|
||||||
{
|
},
|
||||||
title: "Dobro jutro",
|
{
|
||||||
description: "Oddaja Dobro jutro poleg informativnih in zabavnih vsebin podaja koristne nasvete o najrazličnejših tematikah iz vsakdanjega življenja.",
|
title: 'Dobro jutro',
|
||||||
start: "2024-12-26T06:00:00.000Z",
|
description:
|
||||||
stop: "2024-12-26T09:05:00.000Z",
|
'Oddaja Dobro jutro poleg informativnih in zabavnih vsebin podaja koristne nasvete o najrazličnejših tematikah iz vsakdanjega življenja.',
|
||||||
thumbnail: "https://ngimg.siol.tv/sioltv/mtcmsprod/e1/2/d/e12d8eb4-693a-43d3-89d4-fd96dade9f0f.jpg"
|
start: '2024-12-26T06:00:00.000Z',
|
||||||
}
|
stop: '2024-12-26T09:05:00.000Z',
|
||||||
])
|
thumbnail:
|
||||||
})
|
'https://ngimg.siol.tv/sioltv/mtcmsprod/e1/2/d/e12d8eb4-693a-43d3-89d4-fd96dade9f0f.jpg'
|
||||||
|
}
|
||||||
it('can handle empty guide', () => {
|
])
|
||||||
const result = parser({
|
})
|
||||||
content: '{"shows":[]}'
|
|
||||||
})
|
it('can handle empty guide', () => {
|
||||||
expect(result).toMatchObject([])
|
const result = parser({
|
||||||
})
|
content: '{"shows":[]}'
|
||||||
|
})
|
||||||
|
expect(result).toMatchObject([])
|
||||||
|
})
|
||||||
|
|
|
@ -50,7 +50,7 @@ function parseItems(content, date) {
|
||||||
if (!data || !data.item || !Array.isArray(data.item.episodes)) return []
|
if (!data || !data.item || !Array.isArray(data.item.episodes)) return []
|
||||||
|
|
||||||
return data.item.episodes.filter(ep => ep.schedule.startsWith(date.format('YYYY-MM-DD')))
|
return data.item.episodes.filter(ep => ep.schedule.startsWith(date.format('YYYY-MM-DD')))
|
||||||
} catch (err) {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +1,46 @@
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'nhl.com',
|
site: 'nhl.com',
|
||||||
// I'm not sure what `endDate` represents but they only return 1 day of
|
// I'm not sure what `endDate` represents but they only return 1 day of
|
||||||
// results, with `endTime`s ocassionally in the following day.
|
// results, with `endTime`s ocassionally in the following day.
|
||||||
days: 1,
|
days: 1,
|
||||||
url: ({ date }) => `https://api-web.nhle.com/v1/network/tv-schedule/${date.toJSON().split("T")[0]}`,
|
url: ({ date }) =>
|
||||||
parser({ content }) {
|
`https://api-web.nhle.com/v1/network/tv-schedule/${date.toJSON().split('T')[0]}`,
|
||||||
const programs = []
|
parser({ content }) {
|
||||||
const items = parseItems(content)
|
const programs = []
|
||||||
for (const item of items) {
|
const items = parseItems(content)
|
||||||
programs.push({
|
for (const item of items) {
|
||||||
title: item.title,
|
programs.push({
|
||||||
description: item.description === item.title ? undefined : item.description,
|
title: item.title,
|
||||||
category: "Sports",
|
description: item.description === item.title ? undefined : item.description,
|
||||||
// image: parseImage(item),
|
category: 'Sports',
|
||||||
start: parseStart(item),
|
// image: parseImage(item),
|
||||||
stop: parseStop(item)
|
start: parseStart(item),
|
||||||
})
|
stop: parseStop(item)
|
||||||
}
|
})
|
||||||
|
}
|
||||||
return programs
|
|
||||||
}
|
return programs
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Unfortunately I couldn't determine how these are
|
|
||||||
// supposed to be formatted. Pointers appreciated!
|
// Unfortunately I couldn't determine how these are
|
||||||
// function parseImage(item) {
|
// supposed to be formatted. Pointers appreciated!
|
||||||
// const uri = item.broadcastImageUrl
|
// function parseImage(item) {
|
||||||
|
// const uri = item.broadcastImageUrl
|
||||||
// return uri ? `https://???/${uri}` : null
|
|
||||||
// }
|
// return uri ? `https://???/${uri}` : null
|
||||||
|
// }
|
||||||
function parseStart(item) {
|
|
||||||
return dayjs(item.startTime)
|
function parseStart(item) {
|
||||||
}
|
return dayjs(item.startTime)
|
||||||
|
}
|
||||||
function parseStop(item) {
|
|
||||||
return dayjs(item.endTime)
|
function parseStop(item) {
|
||||||
}
|
return dayjs(item.endTime)
|
||||||
|
}
|
||||||
function parseItems(content) {
|
|
||||||
return JSON.parse(content).broadcasts
|
function parseItems(content) {
|
||||||
}
|
return JSON.parse(content).broadcasts
|
||||||
|
}
|
||||||
|
|
|
@ -1,44 +1,44 @@
|
||||||
const { parser, url } = require('./nhl.com.config.js')
|
const { parser, url } = require('./nhl.com.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2024-11-21', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2024-11-21', 'YYYY-MM-DD').startOf('d')
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ date })).toBe(
|
expect(url({ date })).toBe('https://api-web.nhle.com/v1/network/tv-schedule/2024-11-21')
|
||||||
'https://api-web.nhle.com/v1/network/tv-schedule/2024-11-21'
|
})
|
||||||
)
|
|
||||||
})
|
it('can parse response', () => {
|
||||||
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
||||||
it('can parse response', () => {
|
let results = parser({ content, date })
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'))
|
results = results.map(p => {
|
||||||
let results = parser({ content, date })
|
p.start = p.start.toJSON()
|
||||||
results = results.map(p => {
|
p.stop = p.stop.toJSON()
|
||||||
p.start = p.start.toJSON()
|
return p
|
||||||
p.stop = p.stop.toJSON()
|
})
|
||||||
return p
|
|
||||||
})
|
expect(results[0]).toMatchObject({
|
||||||
|
start: '2024-11-21T12:00:00.000Z',
|
||||||
expect(results[0]).toMatchObject({
|
stop: '2024-11-21T13:00:00.000Z',
|
||||||
start: '2024-11-21T12:00:00.000Z',
|
title: 'On The Fly',
|
||||||
stop: '2024-11-21T13:00:00.000Z',
|
category: 'Sports'
|
||||||
title: 'On The Fly',
|
})
|
||||||
category: 'Sports',
|
})
|
||||||
})
|
|
||||||
})
|
it('can handle empty guide', () => {
|
||||||
|
const results = parser({
|
||||||
it('can handle empty guide', () => {
|
content: JSON.stringify({
|
||||||
const results = parser({ content: JSON.stringify({
|
// extra props not necessary but they form a valid response
|
||||||
// extra props not necessary but they form a valid response
|
date: '2024-11-21',
|
||||||
date: "2024-11-21",
|
startDate: '2024-11-07',
|
||||||
startDate: "2024-11-07",
|
endDate: '2024-12-05',
|
||||||
endDate: "2024-12-05",
|
broadcasts: []
|
||||||
broadcasts: [],
|
})
|
||||||
}) })
|
})
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,68 +1,68 @@
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
'X-Apikey': 'xe1dgrShwdR1DVOKGmsj8Ut4QLlGyOFI',
|
'X-Apikey': 'xe1dgrShwdR1DVOKGmsj8Ut4QLlGyOFI',
|
||||||
'X-Core-Appversion': '2.14.0.1',
|
'X-Core-Appversion': '2.14.0.1',
|
||||||
'X-Core-Contentratinglimit': '0',
|
'X-Core-Contentratinglimit': '0',
|
||||||
'X-Core-Deviceid': '',
|
'X-Core-Deviceid': '',
|
||||||
'X-Core-Devicetype': 'web',
|
'X-Core-Devicetype': 'web',
|
||||||
Origin: 'https://nostv.pt',
|
Origin: 'https://nostv.pt',
|
||||||
'User-Agent':
|
'User-Agent':
|
||||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'nostv.pt',
|
site: 'nostv.pt',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel, date }) {
|
url({ channel, date }) {
|
||||||
return `https://tyr-prod.apigee.net/nostv/ott/schedule/range/contents/guest?channels=${
|
return `https://tyr-prod.apigee.net/nostv/ott/schedule/range/contents/guest?channels=${
|
||||||
channel.site_id
|
channel.site_id
|
||||||
}&minDate=${date.format('YYYY-MM-DD')}T00:00:00Z&maxDate=${date.format(
|
}&minDate=${date.format('YYYY-MM-DD')}T00:00:00Z&maxDate=${date.format(
|
||||||
'YYYY-MM-DD'
|
'YYYY-MM-DD'
|
||||||
)}T23:59:59Z&isDateInclusive=true&client_id=${headers['X-Apikey']}`
|
)}T23:59:59Z&isDateInclusive=true&client_id=${headers['X-Apikey']}`
|
||||||
},
|
},
|
||||||
request: { headers },
|
request: { headers },
|
||||||
parser({ content }) {
|
parser({ content }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
if (content) {
|
if (content) {
|
||||||
const items = Array.isArray(content) ? content : JSON.parse(content)
|
const items = Array.isArray(content) ? content : JSON.parse(content)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.Metadata?.Title,
|
title: item.Metadata?.Title,
|
||||||
sub_title: item.Metadata?.SubTitle ? item.Metadata?.SubTitle : null,
|
sub_title: item.Metadata?.SubTitle ? item.Metadata?.SubTitle : null,
|
||||||
description: item.Metadata?.Description,
|
description: item.Metadata?.Description,
|
||||||
season: item.Metadata?.Season,
|
season: item.Metadata?.Season,
|
||||||
episode: item.Metadata?.Episode,
|
episode: item.Metadata?.Episode,
|
||||||
image: item.Images
|
image: item.Images
|
||||||
? `https://mage.stream.nos.pt/v1/nostv_mage/Images?sourceUri=${item.Images[0].Url}&profile=ott_1_452x340&client_id=${headers['X-Apikey']}`
|
? `https://mage.stream.nos.pt/v1/nostv_mage/Images?sourceUri=${item.Images[0].Url}&profile=ott_1_452x340&client_id=${headers['X-Apikey']}`
|
||||||
: null,
|
: null,
|
||||||
start: dayjs.utc(item.UtcDateTimeStart),
|
start: dayjs.utc(item.UtcDateTimeStart),
|
||||||
stop: dayjs.utc(item.UtcDateTimeEnd)
|
stop: dayjs.utc(item.UtcDateTimeEnd)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const result = await axios
|
const result = await axios
|
||||||
.get(
|
.get(
|
||||||
`https://tyr-prod.apigee.net/nostv/ott/channels/guest?client_id=${headers['X-Apikey']}`,
|
`https://tyr-prod.apigee.net/nostv/ott/channels/guest?client_id=${headers['X-Apikey']}`,
|
||||||
{ headers }
|
{ headers }
|
||||||
)
|
)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
return result.map(item => {
|
return result.map(item => {
|
||||||
return {
|
return {
|
||||||
lang: 'pt',
|
lang: 'pt',
|
||||||
site_id: item.ServiceId,
|
site_id: item.ServiceId,
|
||||||
name: item.Name
|
name: item.Name
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,51 +1,51 @@
|
||||||
const { parser, url } = require('./nostv.pt.config.js')
|
const { parser, url } = require('./nostv.pt.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2023-12-11').startOf('d')
|
const date = dayjs.utc('2023-12-11').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '510',
|
site_id: '510',
|
||||||
xmltv_id: 'SPlus.pt'
|
xmltv_id: 'SPlus.pt'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel, date })).toBe(
|
expect(url({ channel, date })).toBe(
|
||||||
'https://tyr-prod.apigee.net/nostv/ott/schedule/range/contents/guest?channels=510&minDate=2023-12-11T00:00:00Z&maxDate=2023-12-11T23:59:59Z&isDateInclusive=true&client_id=xe1dgrShwdR1DVOKGmsj8Ut4QLlGyOFI'
|
'https://tyr-prod.apigee.net/nostv/ott/schedule/range/contents/guest?channels=510&minDate=2023-12-11T00:00:00Z&maxDate=2023-12-11T23:59:59Z&isDateInclusive=true&client_id=xe1dgrShwdR1DVOKGmsj8Ut4QLlGyOFI'
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json'))
|
||||||
const results = parser({ content }).map(p => {
|
const results = parser({ content }).map(p => {
|
||||||
p.start = p.start.toJSON()
|
p.start = p.start.toJSON()
|
||||||
p.stop = p.stop.toJSON()
|
p.stop = p.stop.toJSON()
|
||||||
return p
|
return p
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2023-12-11T16:30:00.000Z',
|
start: '2023-12-11T16:30:00.000Z',
|
||||||
stop: '2023-12-11T17:00:00.000Z',
|
stop: '2023-12-11T17:00:00.000Z',
|
||||||
title: 'Village Vets',
|
title: 'Village Vets',
|
||||||
description:
|
description:
|
||||||
'A história de dois melhores amigos veterinários e o seu extraordinário trabalho na Austrália.',
|
'A história de dois melhores amigos veterinários e o seu extraordinário trabalho na Austrália.',
|
||||||
season: 1,
|
season: 1,
|
||||||
episode: 12,
|
episode: 12,
|
||||||
image:
|
image:
|
||||||
'https://mage.stream.nos.pt/v1/nostv_mage/Images?sourceUri=http://vip.pam.local.internal/PAM.Images/Store/8329ed1aec5d4c0faa2056972256ff9f&profile=ott_1_452x340&client_id=xe1dgrShwdR1DVOKGmsj8Ut4QLlGyOFI'
|
'https://mage.stream.nos.pt/v1/nostv_mage/Images?sourceUri=http://vip.pam.local.internal/PAM.Images/Store/8329ed1aec5d4c0faa2056972256ff9f&profile=ott_1_452x340&client_id=xe1dgrShwdR1DVOKGmsj8Ut4QLlGyOFI'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', async () => {
|
it('can handle empty guide', async () => {
|
||||||
const results = await parser({
|
const results = await parser({
|
||||||
date,
|
date,
|
||||||
content: '[]'
|
content: '[]'
|
||||||
})
|
})
|
||||||
|
|
||||||
expect(results).toMatchObject([])
|
expect(results).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|
|
@ -55,7 +55,7 @@ module.exports = {
|
||||||
.map(function () {
|
.map(function () {
|
||||||
return {
|
return {
|
||||||
lang: 'es',
|
lang: 'es',
|
||||||
site_id: $(this).attr('alt').replace(/\&/gi, '&'),
|
site_id: $(this).attr('alt').replace(/&/gi, '&'),
|
||||||
name: $(this).attr('alt')
|
name: $(this).attr('alt')
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,81 +1,81 @@
|
||||||
const parser = require('epg-parser')
|
const parser = require('epg-parser')
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'nzxmltv.com',
|
site: 'nzxmltv.com',
|
||||||
days: 2,
|
days: 2,
|
||||||
request: {
|
request: {
|
||||||
cache: {
|
cache: {
|
||||||
ttl: 3600000 // 1 hour
|
ttl: 3600000 // 1 hour
|
||||||
},
|
},
|
||||||
maxContentLength: 104857600 // 100 MB
|
maxContentLength: 104857600 // 100 MB
|
||||||
},
|
},
|
||||||
url({ channel }) {
|
url({ channel }) {
|
||||||
const [path] = channel.site_id.split('#')
|
const [path] = channel.site_id.split('#')
|
||||||
|
|
||||||
return `https://nzxmltv.com/${path}.xml`
|
return `https://nzxmltv.com/${path}.xml`
|
||||||
},
|
},
|
||||||
parser({ content, channel, date }) {
|
parser({ content, channel, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
parseItems(content, channel, date).forEach(item => {
|
parseItems(content, channel, date).forEach(item => {
|
||||||
const program = {
|
const program = {
|
||||||
title: item.title?.[0]?.value,
|
title: item.title?.[0]?.value,
|
||||||
description: item.desc?.[0]?.value,
|
description: item.desc?.[0]?.value,
|
||||||
icon: item.icon?.[0],
|
icon: item.icon?.[0],
|
||||||
start: item.start,
|
start: item.start,
|
||||||
stop: item.stop
|
stop: item.stop
|
||||||
}
|
}
|
||||||
if (item.episodeNum) {
|
if (item.episodeNum) {
|
||||||
item.episodeNum.forEach(ep => {
|
item.episodeNum.forEach(ep => {
|
||||||
if (ep.system === 'xmltv_ns') {
|
if (ep.system === 'xmltv_ns') {
|
||||||
const [season, episode, _] = ep.value.split('.')
|
const [season, episode] = ep.value.split('.')
|
||||||
program.season = parseInt(season) + 1
|
program.season = parseInt(season) + 1
|
||||||
program.episode = parseInt(episode) + 1
|
program.episode = parseInt(episode) + 1
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
programs.push(program)
|
programs.push(program)
|
||||||
})
|
})
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels({ provider }) {
|
async channels({ provider }) {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const cheerio = require('cheerio')
|
const cheerio = require('cheerio')
|
||||||
|
|
||||||
const providers = {
|
const providers = {
|
||||||
freeview: 'xmltv/guide',
|
freeview: 'xmltv/guide',
|
||||||
sky: 'sky/guide',
|
sky: 'sky/guide',
|
||||||
redbull: 'iptv/redbull',
|
redbull: 'iptv/redbull',
|
||||||
pluto: 'iptv/plutotv'
|
pluto: 'iptv/plutotv'
|
||||||
}
|
}
|
||||||
|
|
||||||
const channels = []
|
const channels = []
|
||||||
const path = providers[provider]
|
const path = providers[provider]
|
||||||
const xml = await axios
|
const xml = await axios
|
||||||
.get(`https://nzxmltv.com/${path}.xml`)
|
.get(`https://nzxmltv.com/${path}.xml`)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
const $ = cheerio.load(xml)
|
const $ = cheerio.load(xml)
|
||||||
$('tv channel').each((i, el) => {
|
$('tv channel').each((i, el) => {
|
||||||
const disp = $(el).find('display-name')
|
const disp = $(el).find('display-name')
|
||||||
const channelId = $(el).attr('id')
|
const channelId = $(el).attr('id')
|
||||||
|
|
||||||
channels.push({
|
channels.push({
|
||||||
lang: disp.attr('lang').substr(0, 2),
|
lang: disp.attr('lang').substr(0, 2),
|
||||||
site_id: `${path}#${channelId}`,
|
site_id: `${path}#${channelId}`,
|
||||||
name: disp.text().trim()
|
name: disp.text().trim()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
return channels
|
return channels
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function parseItems(content, channel, date) {
|
function parseItems(content, channel, date) {
|
||||||
const { programs } = parser.parse(content)
|
const { programs } = parser.parse(content)
|
||||||
const [, channelId] = channel.site_id.split('#')
|
const [, channelId] = channel.site_id.split('#')
|
||||||
|
|
||||||
return programs.filter(p => p.channel === channelId && date.isSame(p.start, 'day'))
|
return programs.filter(p => p.channel === channelId && date.isSame(p.start, 'day'))
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,40 +1,40 @@
|
||||||
const { parser, url } = require('./nzxmltv.com.config.js')
|
const { parser, url } = require('./nzxmltv.com.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
|
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const date = dayjs.utc('2023-11-21').startOf('d')
|
const date = dayjs.utc('2023-11-21').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'xmltv/guide#1',
|
site_id: 'xmltv/guide#1',
|
||||||
xmltv_id: 'TVNZ1.nz'
|
xmltv_id: 'TVNZ1.nz'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ channel })).toBe('https://nzxmltv.com/xmltv/guide.xml')
|
expect(url({ channel })).toBe('https://nzxmltv.com/xmltv/guide.xml')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response', () => {
|
it('can parse response', () => {
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.xml'))
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.xml'))
|
||||||
const results = parser({ content, channel, date })
|
const results = parser({ content, channel, date })
|
||||||
|
|
||||||
expect(results[0]).toMatchObject({
|
expect(results[0]).toMatchObject({
|
||||||
start: '2023-11-21T10:30:00.000Z',
|
start: '2023-11-21T10:30:00.000Z',
|
||||||
stop: '2023-11-21T11:25:00.000Z',
|
stop: '2023-11-21T11:25:00.000Z',
|
||||||
title: 'Sunday',
|
title: 'Sunday',
|
||||||
description:
|
description:
|
||||||
'On Sunday, an unmissable show with stories about divorce, weight loss, and the incomprehensible devastation of Gaza.',
|
'On Sunday, an unmissable show with stories about divorce, weight loss, and the incomprehensible devastation of Gaza.',
|
||||||
season: 2023,
|
season: 2023,
|
||||||
episode: 37,
|
episode: 37,
|
||||||
icon: 'https://www.thetvdb.com/banners/posters/5dbebff2986f2.jpg'
|
icon: 'https://www.thetvdb.com/banners/posters/5dbebff2986f2.jpg'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can handle empty guide', () => {
|
it('can handle empty guide', () => {
|
||||||
const result = parser({ content: '', channel, date })
|
const result = parser({ content: '', channel, date })
|
||||||
expect(result).toMatchObject([])
|
expect(result).toMatchObject([])
|
||||||
})
|
})
|
||||||
|
|
|
@ -132,7 +132,7 @@ module.exports = {
|
||||||
const $ = cheerio.load(data)
|
const $ = cheerio.load(data)
|
||||||
$('.channelname').each((i, el) => {
|
$('.channelname').each((i, el) => {
|
||||||
let name = $(el).find('center > a:eq(1)').text()
|
let name = $(el).find('center > a:eq(1)').text()
|
||||||
name = name.replace(/\-\-/gi, '-')
|
name = name.replace(/--/gi, '-')
|
||||||
const url = $(el).find('center > a:eq(1)').attr('href')
|
const url = $(el).find('center > a:eq(1)').attr('href')
|
||||||
if (!url) return
|
if (!url) return
|
||||||
const [, number, slug] = url.match(/\/(\d+)\/(.*)\.html$/)
|
const [, number, slug] = url.match(/\/(\d+)\/(.*)\.html$/)
|
||||||
|
|
|
@ -1,113 +1,108 @@
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const API_PROGRAM_ENDPOINT = 'https://epg.orangetv.orange.es/epg/Smartphone_Android/1_PRO'
|
const API_PROGRAM_ENDPOINT = 'https://epg.orangetv.orange.es/epg/Smartphone_Android/1_PRO'
|
||||||
const API_CHANNEL_ENDPOINT = 'https://pc.orangetv.orange.es/pc/api/rtv/v1/GetChannelList?bouquet_id=1&model_external_id=PC&filter_unsupported_channels=false&client=json'
|
const API_CHANNEL_ENDPOINT =
|
||||||
const API_IMAGE_ENDPOINT = 'https://pc.orangetv.orange.es/pc/api/rtv/v1/images'
|
'https://pc.orangetv.orange.es/pc/api/rtv/v1/GetChannelList?bouquet_id=1&model_external_id=PC&filter_unsupported_channels=false&client=json'
|
||||||
|
const API_IMAGE_ENDPOINT = 'https://pc.orangetv.orange.es/pc/api/rtv/v1/images'
|
||||||
module.exports = {
|
|
||||||
site: 'orangetv.orange.es',
|
module.exports = {
|
||||||
days: 2,
|
site: 'orangetv.orange.es',
|
||||||
request: {
|
days: 2,
|
||||||
cache: {
|
request: {
|
||||||
ttl: 60 * 60 * 1000 // 1 hour
|
cache: {
|
||||||
}
|
ttl: 60 * 60 * 1000 // 1 hour
|
||||||
},
|
}
|
||||||
url({ date }) {
|
},
|
||||||
return `${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_1.json`
|
url({ date }) {
|
||||||
},
|
return `${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_1.json`
|
||||||
async parser({ content, channel, date }) {
|
},
|
||||||
let programs = []
|
async parser({ content, channel, date }) {
|
||||||
let items = parseItems(content, channel)
|
let programs = []
|
||||||
if (!items.length) return programs
|
let items = parseItems(content, channel)
|
||||||
|
if (!items.length) return programs
|
||||||
const promises = [
|
|
||||||
axios.get(
|
const promises = [
|
||||||
`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_1.json`,
|
axios.get(`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_1.json`),
|
||||||
),
|
axios.get(`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_2.json`),
|
||||||
axios.get(
|
axios.get(`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_3.json`)
|
||||||
`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_2.json`,
|
]
|
||||||
),
|
|
||||||
axios.get(
|
await Promise.allSettled(promises)
|
||||||
`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_3.json`,
|
.then(results => {
|
||||||
),
|
results.forEach(r => {
|
||||||
]
|
if (r.status === 'fulfilled') {
|
||||||
|
const parsed = parseItems(r.value.data, channel)
|
||||||
await Promise.allSettled(promises)
|
|
||||||
.then(results => {
|
items = items
|
||||||
results.forEach(r => {
|
.filter((item, index) => items.findIndex(oi => oi.id === item.id) === index)
|
||||||
if (r.status === 'fulfilled') {
|
.concat(parsed)
|
||||||
const parsed = parseItems(r.value.data, channel)
|
}
|
||||||
|
})
|
||||||
items = items.filter((item, index) => items.findIndex(oi => oi.id === item.id) === index).concat(parsed)
|
})
|
||||||
}
|
.catch(console.error)
|
||||||
})
|
|
||||||
})
|
items.forEach(item => {
|
||||||
.catch(console.error)
|
programs.push({
|
||||||
|
title: item.name,
|
||||||
items.forEach(item => {
|
description: item.description,
|
||||||
programs.push({
|
category: parseGenres(item),
|
||||||
title: item.name,
|
season: item.seriesSeason || null,
|
||||||
description: item.description,
|
episode: item.episodeId || null,
|
||||||
category: parseGenres(item),
|
icon: parseIcon(item),
|
||||||
season: item.seriesSeason || null,
|
start: dayjs.utc(item.startDate) || null,
|
||||||
episode: item.episodeId || null,
|
stop: dayjs.utc(item.endDate) || null
|
||||||
icon: parseIcon(item),
|
})
|
||||||
start: dayjs.utc(item.startDate) || null,
|
})
|
||||||
stop: dayjs.utc(item.endDate) || null,
|
|
||||||
})
|
return programs
|
||||||
})
|
},
|
||||||
|
async channels() {
|
||||||
return programs
|
const axios = require('axios')
|
||||||
},
|
const data = await axios
|
||||||
async channels() {
|
.get(API_CHANNEL_ENDPOINT)
|
||||||
const axios = require('axios')
|
.then(r => r.data)
|
||||||
const data = await axios
|
.catch(console.log)
|
||||||
.get(API_CHANNEL_ENDPOINT)
|
return data.response.map(item => {
|
||||||
.then(r => r.data)
|
return {
|
||||||
.catch(console.log)
|
lang: 'es',
|
||||||
return data.response.map(item => {
|
name: item.name,
|
||||||
return {
|
site_id: item.externalChannelId
|
||||||
lang: 'es',
|
}
|
||||||
name: item.name,
|
})
|
||||||
site_id: item.externalChannelId
|
}
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
function parseIcon(item) {
|
||||||
}
|
if (item.attachments.length > 0) {
|
||||||
|
const cover = item.attachments.find(i => i.name === 'COVER' || i.name === 'cover')
|
||||||
function parseIcon(item){
|
|
||||||
|
if (cover) {
|
||||||
if(item.attachments.length > 0){
|
return `${API_IMAGE_ENDPOINT}${cover.value}`
|
||||||
const cover = item.attachments.find(i => i.name === "COVER" || i.name === "cover")
|
}
|
||||||
|
}
|
||||||
if(cover)
|
|
||||||
{
|
return ''
|
||||||
return `${API_IMAGE_ENDPOINT}${cover.value}`;
|
}
|
||||||
}
|
|
||||||
}
|
function parseGenres(item) {
|
||||||
|
return item.genres.map(i => i.name)
|
||||||
return ''
|
}
|
||||||
}
|
|
||||||
|
function parseItems(content, channel) {
|
||||||
function parseGenres(item){
|
const json =
|
||||||
return item.genres.map(i => i.name);
|
typeof content === 'string' ? JSON.parse(content) : Array.isArray(content) ? content : []
|
||||||
}
|
|
||||||
|
if (!Array.isArray(json)) {
|
||||||
function parseItems(content, channel) {
|
return []
|
||||||
const json = typeof content === 'string' ? JSON.parse(content) : Array.isArray(content) ? content : []
|
}
|
||||||
|
|
||||||
if (!Array.isArray(json)) {
|
const channelData = json.find(i => i.channelExternalId == channel.site_id)
|
||||||
return [];
|
|
||||||
}
|
if (!channelData) return []
|
||||||
|
|
||||||
const channelData = json.find(i => i.channelExternalId == channel.site_id);
|
return channelData.programs
|
||||||
|
}
|
||||||
if(!channelData)
|
|
||||||
return [];
|
|
||||||
|
|
||||||
return channelData.programs;
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,49 +1,54 @@
|
||||||
const { parser, url } = require('./orangetv.orange.es.config.js')
|
const { parser, url } = require('./orangetv.orange.es.config.js')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
|
|
||||||
const date = dayjs.utc('2024-12-01', 'YYYY-MM-DD').startOf('d')
|
const date = dayjs.utc('2024-12-01', 'YYYY-MM-DD').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: '1010',
|
site_id: '1010',
|
||||||
xmltv_id: 'La1.es'
|
xmltv_id: 'La1.es'
|
||||||
}
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
it('can generate valid url', () => {
|
||||||
expect(url({ date })).toBe(`https://epg.orangetv.orange.es/epg/Smartphone_Android/1_PRO/${date.format('YYYYMMDD')}_8h_1.json`)
|
expect(url({ date })).toBe(
|
||||||
})
|
`https://epg.orangetv.orange.es/epg/Smartphone_Android/1_PRO/${date.format(
|
||||||
|
'YYYYMMDD'
|
||||||
it('can parse response', async () => {
|
)}_8h_1.json`
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json')).toString()
|
)
|
||||||
let results = await parser({ content, channel, date })
|
})
|
||||||
results = results.map(p => {
|
|
||||||
p.start = p.start.toJSON()
|
it('can parse response', async () => {
|
||||||
p.stop = p.stop.toJSON()
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json')).toString()
|
||||||
return p
|
let results = await parser({ content, channel, date })
|
||||||
})
|
results = results.map(p => {
|
||||||
|
p.start = p.start.toJSON()
|
||||||
expect(results.length).toBe(4)
|
p.stop = p.stop.toJSON()
|
||||||
|
return p
|
||||||
var sampleResult = results[0];
|
})
|
||||||
|
|
||||||
expect(sampleResult).toMatchObject({
|
expect(results.length).toBe(4)
|
||||||
start: '2024-11-30T22:36:51.000Z',
|
|
||||||
stop: '2024-11-30T23:57:25.000Z',
|
var sampleResult = results[0]
|
||||||
category: ['Cine', 'Romance', 'Comedia', 'Comedia Romántica'],
|
|
||||||
description: 'Charlie trabaja como director en una escuela de primaria y goza de una placentera existencia junto a sus amigos. A pesar de ello, no es feliz porque cada vez que se enamora pierde la cordura.',
|
expect(sampleResult).toMatchObject({
|
||||||
title: 'Loco de amor'
|
start: '2024-11-30T22:36:51.000Z',
|
||||||
})
|
stop: '2024-11-30T23:57:25.000Z',
|
||||||
})
|
category: ['Cine', 'Romance', 'Comedia', 'Comedia Romántica'],
|
||||||
|
description:
|
||||||
it('can handle empty guide', () => {
|
'Charlie trabaja como director en una escuela de primaria y goza de una placentera existencia junto a sus amigos. A pesar de ello, no es feliz porque cada vez que se enamora pierde la cordura.',
|
||||||
const result = parser({
|
title: 'Loco de amor'
|
||||||
date,
|
})
|
||||||
channel,
|
})
|
||||||
content: '{}'
|
|
||||||
})
|
it('can handle empty guide', () => {
|
||||||
expect(result).toMatchObject({})
|
const result = parser({
|
||||||
})
|
date,
|
||||||
|
channel,
|
||||||
|
content: '{}'
|
||||||
|
})
|
||||||
|
expect(result).toMatchObject({})
|
||||||
|
})
|
||||||
|
|
|
@ -5,7 +5,12 @@ const timezone = require('dayjs/plugin/timezone')
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
dayjs.extend(timezone)
|
dayjs.extend(timezone)
|
||||||
|
|
||||||
const packages = { 'OSNTV CONNECT': 3720, 'OSNTV PRIME': 3733, 'ALFA': 1281, 'OSN PINOY PLUS EXTRA': 3519 }
|
const packages = {
|
||||||
|
'OSNTV CONNECT': 3720,
|
||||||
|
'OSNTV PRIME': 3733,
|
||||||
|
ALFA: 1281,
|
||||||
|
'OSN PINOY PLUS EXTRA': 3519
|
||||||
|
}
|
||||||
const country = 'AE'
|
const country = 'AE'
|
||||||
const tz = 'Asia/Dubai'
|
const tz = 'Asia/Dubai'
|
||||||
|
|
||||||
|
@ -13,11 +18,9 @@ module.exports = {
|
||||||
site: 'osn.com',
|
site: 'osn.com',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ channel, date }) {
|
url({ channel, date }) {
|
||||||
return `https://www.osn.com/api/TVScheduleWebService.asmx/time?dt=${
|
return `https://www.osn.com/api/TVScheduleWebService.asmx/time?dt=${encodeURIComponent(
|
||||||
encodeURIComponent(date.format('MM/DD/YYYY'))
|
date.format('MM/DD/YYYY')
|
||||||
}&co=${country}&ch=${
|
)}&co=${country}&ch=${channel.site_id}&mo=false&hr=0`
|
||||||
channel.site_id
|
|
||||||
}&mo=false&hr=0`
|
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
headers({ channel }) {
|
headers({ channel }) {
|
||||||
|
@ -46,7 +49,9 @@ module.exports = {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
for (const pkg of Object.values(packages)) {
|
for (const pkg of Object.values(packages)) {
|
||||||
const channels = await axios
|
const channels = await axios
|
||||||
.get(`https://www.osn.com/api/tvchannels.ashx?culture=en-US&packageId=${pkg}&country=${country}`)
|
.get(
|
||||||
|
`https://www.osn.com/api/tvchannels.ashx?culture=en-US&packageId=${pkg}&country=${country}`
|
||||||
|
)
|
||||||
.then(response => response.data)
|
.then(response => response.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
|
|
|
@ -28,32 +28,30 @@ it('can generate valid url', () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response (ar)', () => {
|
it('can parse response (ar)', () => {
|
||||||
const result = parser({ date, channel: channelAR, content })
|
const result = parser({ date, channel: channelAR, content }).map(a => {
|
||||||
.map(a => {
|
a.start = a.start.toJSON()
|
||||||
a.start = a.start.toJSON()
|
a.stop = a.stop.toJSON()
|
||||||
a.stop = a.stop.toJSON()
|
return a
|
||||||
return a
|
})
|
||||||
})
|
|
||||||
expect(result.length).toBe(29)
|
expect(result.length).toBe(29)
|
||||||
expect(result[1]).toMatchObject({
|
expect(result[1]).toMatchObject({
|
||||||
start: '2024-11-26T20:50:00.000Z',
|
start: '2024-11-26T20:50:00.000Z',
|
||||||
stop: '2024-11-26T21:45:00.000Z',
|
stop: '2024-11-26T21:45:00.000Z',
|
||||||
title: 'بيت الحلويات: الحلقة 3',
|
title: 'بيت الحلويات: الحلقة 3'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can parse response (en)', () => {
|
it('can parse response (en)', () => {
|
||||||
const result = parser({ date, channel: channelEN, content })
|
const result = parser({ date, channel: channelEN, content }).map(a => {
|
||||||
.map(a => {
|
a.start = a.start.toJSON()
|
||||||
a.start = a.start.toJSON()
|
a.stop = a.stop.toJSON()
|
||||||
a.stop = a.stop.toJSON()
|
return a
|
||||||
return a
|
})
|
||||||
})
|
|
||||||
expect(result.length).toBe(29)
|
expect(result.length).toBe(29)
|
||||||
expect(result[1]).toMatchObject({
|
expect(result[1]).toMatchObject({
|
||||||
start: '2024-11-26T20:50:00.000Z',
|
start: '2024-11-26T20:50:00.000Z',
|
||||||
stop: '2024-11-26T21:45:00.000Z',
|
stop: '2024-11-26T21:45:00.000Z',
|
||||||
title: 'House Of Desserts: Episode 3',
|
title: 'House Of Desserts: Episode 3'
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,7 @@ function parseItems(content, date) {
|
||||||
let data
|
let data
|
||||||
try {
|
try {
|
||||||
data = JSON.parse(json)
|
data = JSON.parse(json)
|
||||||
} catch (error) {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,174 +1,172 @@
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
let apiVersion
|
let apiVersion
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'pickx.be',
|
site: 'pickx.be',
|
||||||
days: 2,
|
days: 2,
|
||||||
setApiVersion: function (version) {
|
setApiVersion: function (version) {
|
||||||
apiVersion = version
|
apiVersion = version
|
||||||
},
|
},
|
||||||
getApiVersion: function () {
|
getApiVersion: function () {
|
||||||
return apiVersion
|
return apiVersion
|
||||||
},
|
},
|
||||||
fetchApiVersion: fetchApiVersion,
|
fetchApiVersion: fetchApiVersion,
|
||||||
url: async function ({ channel, date }) {
|
url: async function ({ channel, date }) {
|
||||||
if (!apiVersion) {
|
if (!apiVersion) {
|
||||||
await fetchApiVersion()
|
await fetchApiVersion()
|
||||||
}
|
}
|
||||||
return `https://px-epg.azureedge.net/airings/${apiVersion}/${date.format(
|
return `https://px-epg.azureedge.net/airings/${apiVersion}/${date.format(
|
||||||
'YYYY-MM-DD'
|
'YYYY-MM-DD'
|
||||||
)}/channel/${channel.site_id}?timezone=Europe%2FBrussels`
|
)}/channel/${channel.site_id}?timezone=Europe%2FBrussels`
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
headers: {
|
headers: {
|
||||||
Origin: 'https://www.pickx.be',
|
Origin: 'https://www.pickx.be',
|
||||||
Referer: 'https://www.pickx.be/'
|
Referer: 'https://www.pickx.be/'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
parser({ channel, content }) {
|
parser({ channel, content }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
if (content) {
|
if (content) {
|
||||||
const items = JSON.parse(content)
|
const items = JSON.parse(content)
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.program.title,
|
title: item.program.title,
|
||||||
sub_title: item.program.episodeTitle,
|
sub_title: item.program.episodeTitle,
|
||||||
description: item.program.description,
|
description: item.program.description,
|
||||||
category: item.program.translatedCategory?.[channel.lang]
|
category: item.program.translatedCategory?.[channel.lang]
|
||||||
? item.program.translatedCategory[channel.lang]
|
? item.program.translatedCategory[channel.lang]
|
||||||
: item.program.category.split('.')[1],
|
: item.program.category.split('.')[1],
|
||||||
image: item.program.posterFileName
|
image: item.program.posterFileName
|
||||||
? `https://experience-cache.proximustv.be/posterserver/poster/EPG/w-166_h-110/${item.program.posterFileName}`
|
? `https://experience-cache.proximustv.be/posterserver/poster/EPG/w-166_h-110/${item.program.posterFileName}`
|
||||||
: null,
|
: null,
|
||||||
season: item.program.seasonNumber,
|
season: item.program.seasonNumber,
|
||||||
episode: item.program.episodeNumber,
|
episode: item.program.episodeNumber,
|
||||||
actors: item.program.actors,
|
actors: item.program.actors,
|
||||||
director: item.program.director ? [item.program.director] : null,
|
director: item.program.director ? [item.program.director] : null,
|
||||||
start: dayjs.utc(item.programScheduleStart),
|
start: dayjs.utc(item.programScheduleStart),
|
||||||
stop: dayjs.utc(item.programScheduleEnd)
|
stop: dayjs.utc(item.programScheduleEnd)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels({ lang = '' }) {
|
async channels({ lang = '' }) {
|
||||||
const query = {
|
const query = {
|
||||||
operationName: 'getChannels',
|
operationName: 'getChannels',
|
||||||
variables: {
|
variables: {
|
||||||
language: lang,
|
language: lang,
|
||||||
queryParams: {},
|
queryParams: {},
|
||||||
id: '0',
|
id: '0',
|
||||||
params: {
|
params: {
|
||||||
shouldReadFromCache: true
|
shouldReadFromCache: true
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
query: `query getChannels($language: String!, $queryParams: ChannelQueryParams, $id: String, $params: ChannelParams) {
|
query: `query getChannels($language: String!, $queryParams: ChannelQueryParams, $id: String, $params: ChannelParams) {
|
||||||
channels(language: $language, queryParams: $queryParams, id: $id, params: $params) {
|
channels(language: $language, queryParams: $queryParams, id: $id, params: $params) {
|
||||||
id
|
id
|
||||||
channelReferenceNumber
|
channelReferenceNumber
|
||||||
name
|
name
|
||||||
callLetter
|
callLetter
|
||||||
number
|
number
|
||||||
logo {
|
logo {
|
||||||
key
|
key
|
||||||
url
|
url
|
||||||
__typename
|
__typename
|
||||||
}
|
}
|
||||||
language
|
language
|
||||||
hd
|
hd
|
||||||
radio
|
radio
|
||||||
replayable
|
replayable
|
||||||
ottReplayable
|
ottReplayable
|
||||||
playable
|
playable
|
||||||
ottPlayable
|
ottPlayable
|
||||||
recordable
|
recordable
|
||||||
subscribed
|
subscribed
|
||||||
cloudRecordable
|
cloudRecordable
|
||||||
catchUpWindowInHours
|
catchUpWindowInHours
|
||||||
isOttNPVREnabled
|
isOttNPVREnabled
|
||||||
ottNPVRStart
|
ottNPVRStart
|
||||||
subscription {
|
subscription {
|
||||||
channelRef
|
channelRef
|
||||||
subscribed
|
subscribed
|
||||||
upselling {
|
upselling {
|
||||||
upsellable
|
upsellable
|
||||||
packages
|
packages
|
||||||
__typename
|
__typename
|
||||||
}
|
}
|
||||||
__typename
|
__typename
|
||||||
}
|
}
|
||||||
packages
|
packages
|
||||||
__typename
|
__typename
|
||||||
}
|
}
|
||||||
}`
|
}`
|
||||||
}
|
}
|
||||||
const result = await axios
|
const result = await axios
|
||||||
.post('https://api.proximusmwc.be/tiams/v3/graphql', query)
|
.post('https://api.proximusmwc.be/tiams/v3/graphql', query)
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
result?.data?.channels
|
result?.data?.channels
|
||||||
.filter(
|
.filter(
|
||||||
channel =>
|
channel =>
|
||||||
!channel.radio && (!lang || channel.language === (lang === 'de' ? 'ger' : lang))
|
!channel.radio && (!lang || channel.language === (lang === 'de' ? 'ger' : lang))
|
||||||
)
|
)
|
||||||
.map(channel => {
|
.map(channel => {
|
||||||
return {
|
return {
|
||||||
lang: channel.language === 'ger' ? 'de' : channel.language,
|
lang: channel.language === 'ger' ? 'de' : channel.language,
|
||||||
site_id: channel.id,
|
site_id: channel.id,
|
||||||
name: channel.name
|
name: channel.name
|
||||||
}
|
}
|
||||||
}) || []
|
}) || []
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
function fetchApiVersion() {
|
async function fetchApiVersion() {
|
||||||
return new Promise(async (resolve, reject) => {
|
// you'll never find what happened here :)
|
||||||
try {
|
// load the pickx page and get the hash from the MWC configuration.
|
||||||
// you'll never find what happened here :)
|
// it's not the best way to get the version but it's the only way to get it.
|
||||||
// load the pickx page and get the hash from the MWC configuration.
|
const hashUrl = 'https://www.pickx.be/nl/televisie/tv-gids'
|
||||||
// it's not the best way to get the version but it's the only way to get it.
|
const hashData = await axios
|
||||||
|
.get(hashUrl)
|
||||||
const hashUrl = 'https://www.pickx.be/nl/televisie/tv-gids';
|
.then(r => {
|
||||||
|
const re = /"hashes":\["(.*)"\]/
|
||||||
const hashData = await axios.get(hashUrl)
|
const match = r.data.match(re)
|
||||||
.then(r => {
|
if (match && match[1]) {
|
||||||
const re = /"hashes":\["(.*)"\]/
|
return match[1]
|
||||||
const match = r.data.match(re)
|
} else {
|
||||||
if (match && match[1]) {
|
throw new Error('React app version hash not found')
|
||||||
return match[1]
|
}
|
||||||
} else {
|
})
|
||||||
throw new Error('React app version hash not found')
|
.catch(console.error)
|
||||||
}
|
|
||||||
})
|
const versionUrl = `https://www.pickx.be/api/s-${hashData}`
|
||||||
.catch(console.error);
|
const response = await axios.get(versionUrl, {
|
||||||
|
headers: {
|
||||||
const versionUrl = `https://www.pickx.be/api/s-${hashData}`
|
Origin: 'https://www.pickx.be',
|
||||||
|
Referer: 'https://www.pickx.be/'
|
||||||
const response = await axios.get(versionUrl, {
|
}
|
||||||
headers: {
|
})
|
||||||
Origin: 'https://www.pickx.be',
|
|
||||||
Referer: 'https://www.pickx.be/'
|
return new Promise((resolve, reject) => {
|
||||||
}
|
try {
|
||||||
})
|
if (response.status === 200) {
|
||||||
|
apiVersion = response.data.version
|
||||||
if (response.status === 200) {
|
resolve()
|
||||||
apiVersion = response.data.version
|
} else {
|
||||||
resolve()
|
console.error(`Failed to fetch API version. Status: ${response.status}`)
|
||||||
} else {
|
reject(`Failed to fetch API version. Status: ${response.status}`)
|
||||||
console.error(`Failed to fetch API version. Status: ${response.status}`)
|
}
|
||||||
reject(`Failed to fetch API version. Status: ${response.status}`)
|
} catch (error) {
|
||||||
}
|
console.error('Error during fetchApiVersion:', error)
|
||||||
} catch (error) {
|
reject(error)
|
||||||
console.error('Error during fetchApiVersion:', error)
|
}
|
||||||
reject(error)
|
})
|
||||||
}
|
}
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,76 +1,69 @@
|
||||||
jest.mock('./pickx.be.config.js', () => {
|
jest.mock('./pickx.be.config.js', () => {
|
||||||
const originalModule = jest.requireActual('./pickx.be.config.js')
|
const originalModule = jest.requireActual('./pickx.be.config.js')
|
||||||
return {
|
return {
|
||||||
...originalModule,
|
...originalModule,
|
||||||
fetchApiVersion: jest.fn(() => Promise.resolve())
|
fetchApiVersion: jest.fn(() => Promise.resolve())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const {
|
const { parser, url, request, setApiVersion } = require('./pickx.be.config.js')
|
||||||
parser,
|
|
||||||
url,
|
const fs = require('fs')
|
||||||
request,
|
const path = require('path')
|
||||||
fetchApiVersion,
|
const dayjs = require('dayjs')
|
||||||
setApiVersion,
|
const utc = require('dayjs/plugin/utc')
|
||||||
getApiVersion
|
|
||||||
} = require('./pickx.be.config.js')
|
dayjs.extend(utc)
|
||||||
|
|
||||||
const fs = require('fs')
|
const date = dayjs.utc('2023-12-13').startOf('d')
|
||||||
const path = require('path')
|
const channel = {
|
||||||
const dayjs = require('dayjs')
|
lang: 'fr',
|
||||||
const utc = require('dayjs/plugin/utc')
|
site_id: 'UID0118',
|
||||||
|
xmltv_id: 'Vedia.be'
|
||||||
dayjs.extend(utc)
|
}
|
||||||
|
|
||||||
const date = dayjs.utc('2023-12-13').startOf('d')
|
beforeEach(() => {
|
||||||
const channel = {
|
setApiVersion('mockedApiVersion')
|
||||||
lang: 'fr',
|
})
|
||||||
site_id: 'UID0118',
|
|
||||||
xmltv_id: 'Vedia.be'
|
it('can generate valid url', async () => {
|
||||||
}
|
const generatedUrl = await url({ channel, date })
|
||||||
|
expect(generatedUrl).toBe(
|
||||||
beforeEach(() => {
|
'https://px-epg.azureedge.net/airings/mockedApiVersion/2023-12-13/channel/UID0118?timezone=Europe%2FBrussels'
|
||||||
setApiVersion('mockedApiVersion')
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid url', async () => {
|
it('can generate valid request headers', () => {
|
||||||
const generatedUrl = await url({ channel, date })
|
expect(request.headers).toMatchObject({
|
||||||
expect(generatedUrl).toBe(
|
Origin: 'https://www.pickx.be',
|
||||||
`https://px-epg.azureedge.net/airings/mockedApiVersion/2023-12-13/channel/UID0118?timezone=Europe%2FBrussels`
|
Referer: 'https://www.pickx.be/'
|
||||||
)
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('can generate valid request headers', () => {
|
it('can parse response', () => {
|
||||||
expect(request.headers).toMatchObject({
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json'))
|
||||||
Origin: 'https://www.pickx.be',
|
const result = parser({ content, channel, date }).map(p => {
|
||||||
Referer: 'https://www.pickx.be/'
|
p.start = p.start.toJSON()
|
||||||
})
|
p.stop = p.stop.toJSON()
|
||||||
})
|
return p
|
||||||
|
})
|
||||||
it('can parse response', () => {
|
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json'))
|
expect(result[0]).toMatchObject({
|
||||||
const result = parser({ content, channel, date }).map(p => {
|
start: '2023-12-12T23:55:00.000Z',
|
||||||
p.start = p.start.toJSON()
|
stop: '2023-12-13T00:15:00.000Z',
|
||||||
p.stop = p.stop.toJSON()
|
title: 'Le 22h30',
|
||||||
return p
|
description: 'Le journal de vivre ici.',
|
||||||
})
|
category: 'Info',
|
||||||
|
image:
|
||||||
expect(result[0]).toMatchObject({
|
'https://experience-cache.proximustv.be/posterserver/poster/EPG/w-166_h-110/250_250_4B990CC58066A7B2A660AFA0BDDE5C41.jpg'
|
||||||
start: '2023-12-12T23:55:00.000Z',
|
})
|
||||||
stop: '2023-12-13T00:15:00.000Z',
|
})
|
||||||
title: 'Le 22h30',
|
|
||||||
description: 'Le journal de vivre ici.',
|
it('can handle empty guide', () => {
|
||||||
category: 'Info',
|
const result = parser({
|
||||||
image:
|
date,
|
||||||
'https://experience-cache.proximustv.be/posterserver/poster/EPG/w-166_h-110/250_250_4B990CC58066A7B2A660AFA0BDDE5C41.jpg'
|
channel,
|
||||||
})
|
content: ''
|
||||||
})
|
})
|
||||||
|
expect(result).toMatchObject([])
|
||||||
it('can handle empty guide', () => {
|
})
|
||||||
const result = parser({
|
|
||||||
date,
|
|
||||||
channel,
|
|
||||||
content: ''
|
|
||||||
})
|
|
||||||
expect(result).toMatchObject([])
|
|
||||||
})
|
|
||||||
|
|
|
@ -1,102 +1,104 @@
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
site: 'player.ee.co.uk',
|
site: 'player.ee.co.uk',
|
||||||
days: 2,
|
days: 2,
|
||||||
url({ date, channel, hour = 0 }) {
|
url({ date, channel, hour = 0 }) {
|
||||||
return `https://api.youview.tv/metadata/linear/v2/schedule/by-servicelocator?serviceLocator=${
|
return `https://api.youview.tv/metadata/linear/v2/schedule/by-servicelocator?serviceLocator=${encodeURIComponent(
|
||||||
encodeURIComponent(channel.site_id)
|
channel.site_id
|
||||||
}&interval=${date.format('YYYY-MM-DD')}T${hour.toString().padStart(2,'0')}Z/PT12H`
|
)}&interval=${date.format('YYYY-MM-DD')}T${hour.toString().padStart(2, '0')}Z/PT12H`
|
||||||
},
|
},
|
||||||
request: {
|
request: {
|
||||||
headers: {
|
headers: {
|
||||||
Referer: 'https://player.ee.co.uk/'
|
Referer: 'https://player.ee.co.uk/'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async parser({ content, channel, date }) {
|
async parser({ content, channel, date }) {
|
||||||
const programs = []
|
const programs = []
|
||||||
if (content) {
|
if (content) {
|
||||||
const schedule = JSON.parse(content)
|
const schedule = JSON.parse(content)
|
||||||
// fetch next 12 hours schedule
|
// fetch next 12 hours schedule
|
||||||
const { url, request } = module.exports
|
const { url, request } = module.exports
|
||||||
const nextSchedule = await axios
|
const nextSchedule = await axios
|
||||||
.get(url({ channel, date, hour: 12 }), { headers: request.headers })
|
.get(url({ channel, date, hour: 12 }), { headers: request.headers })
|
||||||
.then(response => response.data)
|
.then(response => response.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
if (schedule?.items) {
|
if (schedule?.items) {
|
||||||
// merge schedules
|
// merge schedules
|
||||||
if (nextSchedule?.items) {
|
if (nextSchedule?.items) {
|
||||||
schedule.items.push(...nextSchedule.items)
|
schedule.items.push(...nextSchedule.items)
|
||||||
}
|
}
|
||||||
schedule.items.forEach(item => {
|
schedule.items.forEach(item => {
|
||||||
let season, episode
|
let season, episode
|
||||||
const start = dayjs.utc(item.publishedStartTime)
|
const start = dayjs.utc(item.publishedStartTime)
|
||||||
const stop = start.add(item.publishedDuration, 's')
|
const stop = start.add(item.publishedDuration, 's')
|
||||||
const description = item.synopsis
|
const description = item.synopsis
|
||||||
if (description) {
|
if (description) {
|
||||||
const matches = description.trim().match(/\(?S(\d+)[\/\s]Ep(\d+)\)?/)
|
const matches = description.trim().match(/\(?S(\d+)[/\s]Ep(\d+)\)?/)
|
||||||
if (matches) {
|
if (matches) {
|
||||||
if (matches[1]) {
|
if (matches[1]) {
|
||||||
season = parseInt(matches[1])
|
season = parseInt(matches[1])
|
||||||
}
|
}
|
||||||
if (matches[2]) {
|
if (matches[2]) {
|
||||||
episode = parseInt(matches[2])
|
episode = parseInt(matches[2])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
programs.push({
|
programs.push({
|
||||||
title: item.title,
|
title: item.title,
|
||||||
description,
|
description,
|
||||||
season,
|
season,
|
||||||
episode,
|
episode,
|
||||||
start,
|
start,
|
||||||
stop
|
stop
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels() {
|
async channels() {
|
||||||
const token =
|
const token =
|
||||||
'eyJkaXNjb3ZlcnlVc2VyR3JvdXBzIjpbIkFMTFVTRVJTIiwiYWxsIiwiaHR0cDovL3JlZmRhd' +
|
'eyJkaXNjb3ZlcnlVc2VyR3JvdXBzIjpbIkFMTFVTRVJTIiwiYWxsIiwiaHR0cDovL3JlZmRhd' +
|
||||||
'GEueW91dmlldy5jb20vbXBlZzdjcy9Zb3VWaWV3QXBwbGljYXRpb25QbGF5ZXJDUy8yMDIxLT' +
|
'GEueW91dmlldy5jb20vbXBlZzdjcy9Zb3VWaWV3QXBwbGljYXRpb25QbGF5ZXJDUy8yMDIxLT' +
|
||||||
'A5LTEwI2FuZHJvaWRfcnVudGltZS1wcm9maWxlMSIsInRhZzpidC5jb20sMjAxOC0wNy0xMTp' +
|
'A5LTEwI2FuZHJvaWRfcnVudGltZS1wcm9maWxlMSIsInRhZzpidC5jb20sMjAxOC0wNy0xMTp' +
|
||||||
'1c2VyZ3JvdXAjR0JSLWJ0X25vd1RWX211bHRpY2FzdCIsInRhZzpidC5jb20sMjAyMS0xMC0y' +
|
'1c2VyZ3JvdXAjR0JSLWJ0X25vd1RWX211bHRpY2FzdCIsInRhZzpidC5jb20sMjAyMS0xMC0y' +
|
||||||
'NTp1c2VyZ3JvdXAjR0JSLWJ0X2V1cm9zcG9ydCJdLCJyZWdpb25zIjpbIkFMTFJFR0lPTlMiL' +
|
'NTp1c2VyZ3JvdXAjR0JSLWJ0X2V1cm9zcG9ydCJdLCJyZWdpb25zIjpbIkFMTFJFR0lPTlMiL' +
|
||||||
'CJHQlIiLCJHQlItRU5HIiwiR0JSLUVORy1sb25kb24iLCJhbGwiXSwic3Vic2V0IjoiMy41Lj' +
|
'CJHQlIiLCJHQlItRU5HIiwiR0JSLUVORy1sb25kb24iLCJhbGwiXSwic3Vic2V0IjoiMy41Lj' +
|
||||||
'EvYW5kcm9pZF9ydW50aW1lLXByb2ZpbGUxL0JST0FEQ0FTVF9JUC9HQlItYnRfYnJvYWRiYW5' +
|
'EvYW5kcm9pZF9ydW50aW1lLXByb2ZpbGUxL0JST0FEQ0FTVF9JUC9HQlItYnRfYnJvYWRiYW5' +
|
||||||
'kIiwic3Vic2V0cyI6WyIvLy8iLCIvL0JST0FEQ0FTVF9JUC8iLCIzLjUvLy8iXX0='
|
'kIiwic3Vic2V0cyI6WyIvLy8iLCIvL0JST0FEQ0FTVF9JUC8iLCIzLjUvLy8iXX0='
|
||||||
const extensions = [
|
const extensions = [
|
||||||
'LinearCategoriesExtension',
|
'LinearCategoriesExtension',
|
||||||
'LogicalChannelNumberExtension',
|
'LogicalChannelNumberExtension',
|
||||||
'BTSubscriptionCodesExtension'
|
'BTSubscriptionCodesExtension'
|
||||||
]
|
]
|
||||||
const result = await axios
|
const result = await axios
|
||||||
.get(`https://api.youview.tv/metadata/linear/v2/linear-services`, {
|
.get('https://api.youview.tv/metadata/linear/v2/linear-services', {
|
||||||
params: {
|
params: {
|
||||||
contentTargetingToken: token,
|
contentTargetingToken: token,
|
||||||
extensions: extensions.join(',')
|
extensions: extensions.join(',')
|
||||||
},
|
},
|
||||||
headers: module.exports.request.headers
|
headers: module.exports.request.headers
|
||||||
})
|
})
|
||||||
.then(response => response.data)
|
.then(response => response.data)
|
||||||
.catch(console.error)
|
.catch(console.error)
|
||||||
|
|
||||||
return result?.items
|
return (
|
||||||
.filter(channel => channel.contentTypes.indexOf('tv') >= 0)
|
result?.items
|
||||||
.map(channel => {
|
.filter(channel => channel.contentTypes.indexOf('tv') >= 0)
|
||||||
return {
|
.map(channel => {
|
||||||
lang: 'en',
|
return {
|
||||||
site_id: channel.serviceLocator,
|
lang: 'en',
|
||||||
name: channel.fullName
|
site_id: channel.serviceLocator,
|
||||||
}
|
name: channel.fullName
|
||||||
}) || []
|
}
|
||||||
}
|
}) || []
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,72 +1,74 @@
|
||||||
const { parser, url } = require('./player.ee.co.uk.config.js')
|
const { parser, url } = require('./player.ee.co.uk.config.js')
|
||||||
const fs = require('fs')
|
const fs = require('fs')
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
|
|
||||||
dayjs.extend(utc)
|
dayjs.extend(utc)
|
||||||
|
|
||||||
jest.mock('axios')
|
jest.mock('axios')
|
||||||
|
|
||||||
const date = dayjs.utc('2023-12-13').startOf('d')
|
const date = dayjs.utc('2023-12-13').startOf('d')
|
||||||
const channel = {
|
const channel = {
|
||||||
site_id: 'dvb://233a..6d60',
|
site_id: 'dvb://233a..6d60',
|
||||||
xmltv_id: 'HGTV.uk'
|
xmltv_id: 'HGTV.uk'
|
||||||
}
|
}
|
||||||
|
|
||||||
axios.get.mockImplementation((url, opts) => {
|
axios.get.mockImplementation(url => {
|
||||||
if (url === 'https://api.youview.tv/metadata/linear/v2/schedule/by-servicelocator?serviceLocator=dvb%3A%2F%2F233a..6d60&interval=2023-12-13T12Z/PT12H') {
|
if (
|
||||||
return Promise.resolve({
|
url ===
|
||||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/data1.json')))
|
'https://api.youview.tv/metadata/linear/v2/schedule/by-servicelocator?serviceLocator=dvb%3A%2F%2F233a..6d60&interval=2023-12-13T12Z/PT12H'
|
||||||
})
|
) {
|
||||||
}
|
return Promise.resolve({
|
||||||
|
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/data1.json')))
|
||||||
return Promise.resolve({ data: '' })
|
})
|
||||||
})
|
}
|
||||||
|
|
||||||
it('can generate valid url', () => {
|
return Promise.resolve({ data: '' })
|
||||||
expect(url({ date, channel })).toBe(
|
})
|
||||||
'https://api.youview.tv/metadata/linear/v2/schedule/by-servicelocator?serviceLocator=dvb%3A%2F%2F233a..6d60&interval=2023-12-13T00Z/PT12H'
|
|
||||||
)
|
it('can generate valid url', () => {
|
||||||
})
|
expect(url({ date, channel })).toBe(
|
||||||
|
'https://api.youview.tv/metadata/linear/v2/schedule/by-servicelocator?serviceLocator=dvb%3A%2F%2F233a..6d60&interval=2023-12-13T00Z/PT12H'
|
||||||
it('can parse response', async () => {
|
)
|
||||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json'))
|
})
|
||||||
const result = (await parser({ content, channel, date }))
|
|
||||||
.map(p => {
|
it('can parse response', async () => {
|
||||||
p.start = p.start.toJSON()
|
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json'))
|
||||||
p.stop = p.stop.toJSON()
|
const result = (await parser({ content, channel, date })).map(p => {
|
||||||
return p
|
p.start = p.start.toJSON()
|
||||||
})
|
p.stop = p.stop.toJSON()
|
||||||
|
return p
|
||||||
expect(result).toMatchObject([
|
})
|
||||||
{
|
|
||||||
title: 'Bargain Mansions',
|
expect(result).toMatchObject([
|
||||||
description:
|
{
|
||||||
'Tamara and her dad help a recent widow who loves to cook for her family design her dream kitchen, perfect for entertaining and large gatherings. S4/Ep1',
|
title: 'Bargain Mansions',
|
||||||
season: 4,
|
description:
|
||||||
episode: 1,
|
'Tamara and her dad help a recent widow who loves to cook for her family design her dream kitchen, perfect for entertaining and large gatherings. S4/Ep1',
|
||||||
start: '2023-12-13T13:00:00.000Z',
|
season: 4,
|
||||||
stop: '2023-12-13T14:00:00.000Z'
|
episode: 1,
|
||||||
},
|
start: '2023-12-13T13:00:00.000Z',
|
||||||
{
|
stop: '2023-12-13T14:00:00.000Z'
|
||||||
title: 'Flip Or Flop',
|
},
|
||||||
description:
|
{
|
||||||
'Tarek and Christina are contacted by a cash strapped flipper who needs to unload a project house. S2/Ep2',
|
title: 'Flip Or Flop',
|
||||||
season: 2,
|
description:
|
||||||
episode: 2,
|
'Tarek and Christina are contacted by a cash strapped flipper who needs to unload a project house. S2/Ep2',
|
||||||
start: '2023-12-13T14:00:00.000Z',
|
season: 2,
|
||||||
stop: '2023-12-13T14:30:00.000Z'
|
episode: 2,
|
||||||
}
|
start: '2023-12-13T14:00:00.000Z',
|
||||||
])
|
stop: '2023-12-13T14:30:00.000Z'
|
||||||
})
|
}
|
||||||
|
])
|
||||||
it('can handle empty guide', async () => {
|
})
|
||||||
const result = await parser({
|
|
||||||
channel,
|
it('can handle empty guide', async () => {
|
||||||
date,
|
const result = await parser({
|
||||||
content: ''
|
channel,
|
||||||
})
|
date,
|
||||||
expect(result).toMatchObject([])
|
content: ''
|
||||||
})
|
})
|
||||||
|
expect(result).toMatchObject([])
|
||||||
|
})
|
||||||
|
|
|
@ -43,7 +43,7 @@ module.exports = {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.post(
|
.post(
|
||||||
`https://playtv.unifi.com.my:7053/VSP/V3/QueryAllChannel`,
|
'https://playtv.unifi.com.my:7053/VSP/V3/QueryAllChannel',
|
||||||
{ isReturnAllMedia: '0' },
|
{ isReturnAllMedia: '0' },
|
||||||
{
|
{
|
||||||
params: {
|
params: {
|
||||||
|
@ -74,7 +74,7 @@ function parseItems(content, channel) {
|
||||||
|
|
||||||
const channelData = data.find(i => i.id == channel.site_id)
|
const channelData = data.find(i => i.id == channel.site_id)
|
||||||
return channelData.items && Array.isArray(channelData.items) ? channelData.items : []
|
return channelData.items && Array.isArray(channelData.items) ? channelData.items : []
|
||||||
} catch (err) {
|
} catch {
|
||||||
return []
|
return []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,50 +1,49 @@
|
||||||
const dayjs = require('dayjs')
|
const dayjs = require('dayjs')
|
||||||
const utc = require('dayjs/plugin/utc')
|
const utc = require('dayjs/plugin/utc')
|
||||||
const timezone = require('dayjs/plugin/timezone')
|
const timezone = require('dayjs/plugin/timezone')
|
||||||
const axios = require('axios')
|
|
||||||
|
dayjs.extend(utc)
|
||||||
dayjs.extend(utc)
|
dayjs.extend(timezone)
|
||||||
dayjs.extend(timezone)
|
|
||||||
|
module.exports = {
|
||||||
module.exports = {
|
site: 'pluto.tv',
|
||||||
site: 'pluto.tv',
|
days: 3,
|
||||||
days: 3,
|
|
||||||
|
url: function ({ date, channel }) {
|
||||||
url: function ({ date, channel }) {
|
const channelId = channel.site_id
|
||||||
const channelId = channel.site_id
|
|
||||||
|
const localTimezone = dayjs.tz.guess()
|
||||||
const localTimezone = dayjs.tz.guess()
|
|
||||||
|
const startTime = dayjs(date).tz(localTimezone).startOf('day').toISOString()
|
||||||
const startTime = dayjs(date).tz(localTimezone).startOf('day').toISOString()
|
const endTime = dayjs(date).tz(localTimezone).add(this.days, 'day').endOf('day').toISOString()
|
||||||
const endTime = dayjs(date).tz(localTimezone).add(this.days, 'day').endOf('day').toISOString()
|
|
||||||
|
const generatedUrl = `https://api.pluto.tv/v2/channels/${channelId}?start=${startTime}&stop=${endTime}`
|
||||||
const generatedUrl = `https://api.pluto.tv/v2/channels/${channelId}?start=${startTime}&stop=${endTime}`
|
return generatedUrl
|
||||||
return generatedUrl
|
},
|
||||||
},
|
|
||||||
|
parser: function ({ content }) {
|
||||||
parser: function ({ content }) {
|
const data = JSON.parse(content)
|
||||||
const data = JSON.parse(content)
|
const programs = []
|
||||||
const programs = []
|
|
||||||
|
if (data.timelines) {
|
||||||
if (data.timelines) {
|
data.timelines.forEach(item => {
|
||||||
data.timelines.forEach(item => {
|
programs.push({
|
||||||
programs.push({
|
title: item.title,
|
||||||
title: item.title,
|
subTitle: item.episode?.name || '',
|
||||||
subTitle: item.episode?.name || '',
|
description: item.episode?.description || '',
|
||||||
description: item.episode?.description || '',
|
episode: item.episode?.number || '',
|
||||||
episode: item.episode?.number || '',
|
season: item.episode?.season || '',
|
||||||
season: item.episode?.season || '',
|
actors: item.episode?.clip?.actors || [],
|
||||||
actors: item.episode?.clip?.actors || [],
|
categories: [item.episode?.genre, item.episode?.subGenre].filter(Boolean),
|
||||||
categories: [item.episode?.genre, item.episode?.subGenre].filter(Boolean),
|
rating: item.episode?.rating || '',
|
||||||
rating: item.episode?.rating || '',
|
date: item.episode?.clip?.originalReleaseDate || '',
|
||||||
date: item.episode?.clip?.originalReleaseDate || '',
|
icon: item.episode?.series?.tile?.path || '',
|
||||||
icon: item.episode?.series?.tile?.path || '',
|
start: item.start,
|
||||||
start: item.start,
|
stop: item.stop
|
||||||
stop: item.stop
|
})
|
||||||
})
|
})
|
||||||
})
|
}
|
||||||
}
|
|
||||||
|
return programs
|
||||||
return programs
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
|
@ -33,14 +33,17 @@ it('can parse response', () => {
|
||||||
start: '2024-12-28T00:21:00.000Z',
|
start: '2024-12-28T00:21:00.000Z',
|
||||||
stop: '2024-12-28T00:48:00.000Z',
|
stop: '2024-12-28T00:48:00.000Z',
|
||||||
title: 'Naruto: El Tercer Hokage, Eternamente',
|
title: 'Naruto: El Tercer Hokage, Eternamente',
|
||||||
description: 'Gaara y Naruto continúan combatiendo con todas sus fuerzas. Decidido a proteger a Sakura, Naruto ataca a Gaara una y otra vez.',
|
description:
|
||||||
|
'Gaara y Naruto continúan combatiendo con todas sus fuerzas. Decidido a proteger a Sakura, Naruto ataca a Gaara una y otra vez.',
|
||||||
subTitle: 'El Tercer Hokage, Eternamente',
|
subTitle: 'El Tercer Hokage, Eternamente',
|
||||||
episode: 80,
|
episode: 80,
|
||||||
season: 2,
|
season: 2,
|
||||||
actors: ["Isabel Martion (Naruto Uzumaki)",
|
actors: [
|
||||||
"Christine Byrd (Sakura Haruno)",
|
'Isabel Martion (Naruto Uzumaki)',
|
||||||
"Victor Ugarte (Sasuke Uchiha)",
|
'Christine Byrd (Sakura Haruno)',
|
||||||
"Alfonso Obreg (Kakashi Hatake)"],
|
'Victor Ugarte (Sasuke Uchiha)',
|
||||||
|
'Alfonso Obreg (Kakashi Hatake)'
|
||||||
|
],
|
||||||
categories: ['Anime', 'Anime Action & Adventure'],
|
categories: ['Anime', 'Anime Action & Adventure'],
|
||||||
rating: 'TV-14',
|
rating: 'TV-14',
|
||||||
date: '2004-04-21T00:00:00.000Z',
|
date: '2004-04-21T00:00:00.000Z',
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<channels>
|
<channels>
|
||||||
<channel site="pluto.tv" lang="en" xmltv_id="PlutoTVAdventCalendar.ca" site_id="6712256349c4060008e9e0f0">Pluto TV Advent Calendar</channel>
|
<channel site="pluto.tv" lang="en" xmltv_id="PlutoTVAdventCalendar.ca" site_id="6712256349c4060008e9e0f0">Pluto TV Advent Calendar</channel>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
|
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<channels>
|
<channels>
|
||||||
<channel site="pluto.tv" lang="en" xmltv_id="Diane,femmeflic.uk" site_id="6671b26836a2f90008d9333c">Diane, femme flic</channel>
|
<channel site="pluto.tv" lang="en" xmltv_id="Diane,femmeflic.uk" site_id="6671b26836a2f90008d9333c">Diane, femme flic</channel>
|
||||||
|
|
|
@ -46,10 +46,10 @@ module.exports = {
|
||||||
|
|
||||||
return programs
|
return programs
|
||||||
},
|
},
|
||||||
async channels({ country, lang }) {
|
async channels() {
|
||||||
const axios = require('axios')
|
const axios = require('axios')
|
||||||
const data = await axios
|
const data = await axios
|
||||||
.get(`https://www.programetv.ro/api/station/index/`)
|
.get('https://www.programetv.ro/api/station/index/')
|
||||||
.then(r => r.data)
|
.then(r => r.data)
|
||||||
.catch(console.log)
|
.catch(console.log)
|
||||||
|
|
||||||
|
|
|
@ -62,7 +62,7 @@ module.exports = {
|
||||||
$('.channelList-listItemsLink').each((i, el) => {
|
$('.channelList-listItemsLink').each((i, el) => {
|
||||||
const name = $(el).attr('title')
|
const name = $(el).attr('title')
|
||||||
const url = $(el).attr('href')
|
const url = $(el).attr('href')
|
||||||
const [, site_id] = url.match(/\/programme\-(.*)\.html$/i)
|
const [, site_id] = url.match(/\/programme-(.*)\.html$/i)
|
||||||
|
|
||||||
channels.push({
|
channels.push({
|
||||||
lang: 'fr',
|
lang: 'fr',
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue