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 = {
|
||||
tabWidth: 2,
|
||||
useTabs: false,
|
||||
endOfLine: 'lf',
|
||||
endOfLine: 'crlf',
|
||||
semi: false,
|
||||
singleQuote: true,
|
||||
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
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
#include "./.sites/_table.md"
|
||||
# Sites
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
#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.
|
||||
|
||||
- `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).
|
||||
- `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.
|
||||
|
|
427
SITES.md
427
SITES.md
|
@ -1,212 +1,215 @@
|
|||
# Sites
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<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/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/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>https://github.com/iptv-org/epg/issues/2556</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>https://github.com/iptv-org/epg/issues/2557</td></tr>
|
||||
<tr><td><a href="sites/ontvtonight.com">ontvtonight.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/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></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/tivie.id">tivie.id</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>https://github.com/iptv-org/epg/issues/2555</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>
|
||||
# Sites
|
||||
|
||||
<!-- prettier-ignore -->
|
||||
<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></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/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/cyta.com.cy">cyta.com.cy</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></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>https://github.com/iptv-org/epg/issues/2556</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>https://github.com/iptv-org/epg/issues/2557</td></tr>
|
||||
<tr><td><a href="sites/ontvtonight.com">ontvtonight.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/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/pluto.tv">pluto.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></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/stod2.is">stod2.is</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/tivie.id">tivie.id</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>https://github.com/iptv-org/epg/issues/2570</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>
|
||||
|
|
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",
|
||||
"dependencies": {
|
||||
"@alex_neo/jest-expect-message": "^1.0.5",
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "^9.17.0",
|
||||
"@freearhey/core": "^0.3.1",
|
||||
"@freearhey/search-js": "^0.1.1",
|
||||
"@ntlab/sfetch": "^1.0.0",
|
||||
"@octokit/plugin-paginate-rest": "^11.3.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/fs-extra": "^11.0.2",
|
||||
"@types/inquirer": "^9.0.3",
|
||||
|
@ -39,9 +43,12 @@
|
|||
"form-data": "^4.0.0",
|
||||
"fs-extra": "^10.0.1",
|
||||
"glob": "^7.2.0",
|
||||
"globals": "^15.14.0",
|
||||
"husky": "^9.1.7",
|
||||
"iconv-lite": "^0.4.24",
|
||||
"inquirer": "^8.2.6",
|
||||
"jest": "^29.7.0",
|
||||
"jest-offline": "^1.0.1",
|
||||
"langs": "^2.0.0",
|
||||
"libxmljs2": "^0.35.0",
|
||||
"lodash": "^4.17.21",
|
||||
|
@ -58,12 +65,12 @@
|
|||
"run-script-os": "^1.1.6",
|
||||
"serve": "^14.2.4",
|
||||
"signale": "^1.4.0",
|
||||
"skip-postinstall": "^1.0.0",
|
||||
"srcset": "^4.0.0",
|
||||
"table2array": "^0.0.2",
|
||||
"tabletojson": "^2.0.7",
|
||||
"tough-cookie": "^5.0.0",
|
||||
"transliteration": "^2.2.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"tsx": "^4.19.2",
|
||||
"unzipit": "^1.4.0",
|
||||
"wildcard-match": "^5.1.2"
|
||||
|
@ -639,6 +646,14 @@
|
|||
"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": {
|
||||
"version": "7.23.3",
|
||||
"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": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
|
||||
|
@ -2246,6 +2272,222 @@
|
|||
"@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": {
|
||||
"version": "4.0.6",
|
||||
"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_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": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
|
||||
|
@ -5262,11 +5493,14 @@
|
|||
}
|
||||
},
|
||||
"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==",
|
||||
"version": "15.14.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz",
|
||||
"integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
"node": ">=18"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/got": {
|
||||
|
@ -5417,6 +5651,20 @@
|
|||
"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": {
|
||||
"version": "0.4.24",
|
||||
"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_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": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz",
|
||||
|
@ -6562,6 +6818,11 @@
|
|||
"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": {
|
||||
"version": "6.1.0",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
|
@ -6796,7 +7052,9 @@
|
|||
"node_modules/make-error": {
|
||||
"version": "1.3.6",
|
||||
"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": {
|
||||
"version": "13.0.1",
|
||||
|
@ -7083,6 +7341,25 @@
|
|||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
|
||||
"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": {
|
||||
"version": "1.0.4",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
|
@ -9406,86 +9692,6 @@
|
|||
"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": {
|
||||
"version": "10.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz",
|
||||
|
@ -10484,6 +10690,13 @@
|
|||
"@babel/types": "^7.23.3",
|
||||
"debug": "^4.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": {
|
||||
|
@ -10994,6 +11207,14 @@
|
|||
"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": {
|
||||
"version": "29.7.0",
|
||||
"resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz",
|
||||
|
@ -11591,6 +11812,108 @@
|
|||
"@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": {
|
||||
"version": "4.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
|
||||
|
@ -12372,14 +12695,6 @@
|
|||
"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": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz",
|
||||
|
@ -13782,9 +14097,9 @@
|
|||
}
|
||||
},
|
||||
"globals": {
|
||||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
|
||||
"version": "15.14.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz",
|
||||
"integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig=="
|
||||
},
|
||||
"got": {
|
||||
"version": "11.8.5",
|
||||
|
@ -13888,6 +14203,11 @@
|
|||
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
|
||||
"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": {
|
||||
"version": "0.4.24",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
|
||||
|
@ -14438,6 +14758,14 @@
|
|||
"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": {
|
||||
"version": "1.2.3",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "6.1.0",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "4.6.2",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
|
||||
|
@ -14908,7 +15236,9 @@
|
|||
"make-error": {
|
||||
"version": "1.3.6",
|
||||
"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": {
|
||||
"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": {
|
||||
"version": "1.0.4",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "3.0.0",
|
||||
"resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
|
||||
|
@ -16818,49 +17168,6 @@
|
|||
"integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==",
|
||||
"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": {
|
||||
"version": "10.9.1",
|
||||
"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",
|
||||
"scripts": {
|
||||
"act:check": "act pull_request -W .github/workflows/check.yml",
|
||||
"act:update": "act workflow_dispatch -W .github/workflows/update.yml",
|
||||
"api:load": "npx tsx scripts/commands/api/load.ts",
|
||||
"api:generate": "npx tsx scripts/commands/api/generate.ts",
|
||||
|
@ -10,29 +11,37 @@
|
|||
"channels:validate": "npx tsx scripts/commands/channels/validate.ts",
|
||||
"sites:update": "npx tsx scripts/commands/sites/update.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:win32": "SET \"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,
|
||||
"author": "Arhey",
|
||||
"license": "UNLICENSED",
|
||||
"jest": {
|
||||
"setupFiles": [
|
||||
"<rootDir>/node_modules/jest-offline"
|
||||
],
|
||||
"transform": {
|
||||
"^.+\\.(ts|js)$": "ts-jest"
|
||||
"^.+\\.(ts|js)$": "@swc/jest"
|
||||
},
|
||||
"testRegex": "(tests|sites)/(.*?/)?.*test.(js|ts)$",
|
||||
"testTimeout": 10000
|
||||
},
|
||||
"dependencies": {
|
||||
"@alex_neo/jest-expect-message": "^1.0.5",
|
||||
"@eslint/eslintrc": "^3.2.0",
|
||||
"@eslint/js": "^9.17.0",
|
||||
"@freearhey/core": "^0.3.1",
|
||||
"@freearhey/search-js": "^0.1.1",
|
||||
"@ntlab/sfetch": "^1.0.0",
|
||||
"@octokit/plugin-paginate-rest": "^11.3.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/fs-extra": "^11.0.2",
|
||||
"@types/inquirer": "^9.0.3",
|
||||
|
@ -58,9 +67,12 @@
|
|||
"form-data": "^4.0.0",
|
||||
"fs-extra": "^10.0.1",
|
||||
"glob": "^7.2.0",
|
||||
"globals": "^15.14.0",
|
||||
"husky": "^9.1.7",
|
||||
"iconv-lite": "^0.4.24",
|
||||
"inquirer": "^8.2.6",
|
||||
"jest": "^29.7.0",
|
||||
"jest-offline": "^1.0.1",
|
||||
"langs": "^2.0.0",
|
||||
"libxmljs2": "^0.35.0",
|
||||
"lodash": "^4.17.21",
|
||||
|
@ -77,12 +89,12 @@
|
|||
"run-script-os": "^1.1.6",
|
||||
"serve": "^14.2.4",
|
||||
"signale": "^1.4.0",
|
||||
"skip-postinstall": "^1.0.0",
|
||||
"srcset": "^4.0.0",
|
||||
"table2array": "^0.0.2",
|
||||
"tabletojson": "^2.0.7",
|
||||
"tough-cookie": "^5.0.0",
|
||||
"transliteration": "^2.2.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"tsx": "^4.19.2",
|
||||
"unzipit": "^1.4.0",
|
||||
"wildcard-match": "^5.1.2"
|
||||
|
|
|
@ -1,51 +1,51 @@
|
|||
import { Logger, Storage, Collection } from '@freearhey/core'
|
||||
import { ChannelsParser } from '../../core'
|
||||
import path from 'path'
|
||||
import { SITES_DIR, API_DIR } from '../../constants'
|
||||
import { Channel } from 'epg-grabber'
|
||||
|
||||
type OutputItem = {
|
||||
channel: string | null
|
||||
site: string
|
||||
site_id: string
|
||||
site_name: string
|
||||
lang: string
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const logger = new Logger()
|
||||
|
||||
logger.start('staring...')
|
||||
|
||||
logger.info('loading channels...')
|
||||
const sitesStorage = new Storage(SITES_DIR)
|
||||
const parser = new ChannelsParser({ storage: sitesStorage })
|
||||
|
||||
let files: string[] = []
|
||||
files = await sitesStorage.list('**/*.channels.xml')
|
||||
|
||||
let parsedChannels = new Collection()
|
||||
for (const filepath of files) {
|
||||
parsedChannels = parsedChannels.concat(await parser.parse(filepath))
|
||||
}
|
||||
|
||||
logger.info(` found ${parsedChannels.count()} channel(s)`)
|
||||
|
||||
const output = parsedChannels.map((channel: Channel): OutputItem => {
|
||||
return {
|
||||
channel: channel.xmltv_id || null,
|
||||
site: channel.site || '',
|
||||
site_id: channel.site_id || '',
|
||||
site_name: channel.name,
|
||||
lang: channel.lang || ''
|
||||
}
|
||||
})
|
||||
|
||||
const apiStorage = new Storage(API_DIR)
|
||||
const outputFilename = 'guides.json'
|
||||
await apiStorage.save('guides.json', output.toJSON())
|
||||
|
||||
logger.info(`saved to "${path.join(API_DIR, outputFilename)}"`)
|
||||
}
|
||||
|
||||
main()
|
||||
import { Logger, Storage, Collection } from '@freearhey/core'
|
||||
import { ChannelsParser } from '../../core'
|
||||
import path from 'path'
|
||||
import { SITES_DIR, API_DIR } from '../../constants'
|
||||
import { Channel } from 'epg-grabber'
|
||||
|
||||
type OutputItem = {
|
||||
channel: string | null
|
||||
site: string
|
||||
site_id: string
|
||||
site_name: string
|
||||
lang: string
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const logger = new Logger()
|
||||
|
||||
logger.start('staring...')
|
||||
|
||||
logger.info('loading channels...')
|
||||
const sitesStorage = new Storage(SITES_DIR)
|
||||
const parser = new ChannelsParser({ storage: sitesStorage })
|
||||
|
||||
let files: string[] = []
|
||||
files = await sitesStorage.list('**/*.channels.xml')
|
||||
|
||||
let parsedChannels = new Collection()
|
||||
for (const filepath of files) {
|
||||
parsedChannels = parsedChannels.concat(await parser.parse(filepath))
|
||||
}
|
||||
|
||||
logger.info(` found ${parsedChannels.count()} channel(s)`)
|
||||
|
||||
const output = parsedChannels.map((channel: Channel): OutputItem => {
|
||||
return {
|
||||
channel: channel.xmltv_id || null,
|
||||
site: channel.site || '',
|
||||
site_id: channel.site_id || '',
|
||||
site_name: channel.name,
|
||||
lang: channel.lang || ''
|
||||
}
|
||||
})
|
||||
|
||||
const apiStorage = new Storage(API_DIR)
|
||||
const outputFilename = 'guides.json'
|
||||
await apiStorage.save('guides.json', output.toJSON())
|
||||
|
||||
logger.info(`saved to "${path.join(API_DIR, outputFilename)}"`)
|
||||
}
|
||||
|
||||
main()
|
||||
|
|
|
@ -43,7 +43,7 @@ async function main() {
|
|||
const channelsIndex = sj.createIndex(channelsContent)
|
||||
|
||||
const buffer = new Dictionary()
|
||||
for (let option of options.all()) {
|
||||
for (const option of options.all()) {
|
||||
const channel: Channel = option.channel
|
||||
if (channel.xmltv_id) {
|
||||
if (channel.xmltv_id !== '-') {
|
||||
|
@ -150,7 +150,7 @@ function getOptions(channelsIndex, channel: Channel) {
|
|||
const query = channel.name
|
||||
.replace(/\s(SD|TV|HD|SD\/HD|HDTV)$/i, '')
|
||||
.replace(/(\(|\)|,)/gi, '')
|
||||
.replace(/\-/gi, ' ')
|
||||
.replace(/-/gi, ' ')
|
||||
.replace(/\+/gi, '')
|
||||
const similar = channelsIndex.search(query).map(item => new ApiChannel(item))
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import chalk from 'chalk'
|
||||
import libxml, { ValidationError } from 'libxmljs2'
|
||||
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"?>
|
||||
<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:schema>`
|
||||
|
||||
program
|
||||
.option(
|
||||
'-c, --channels <path>',
|
||||
'Path to channels.xml file to validate',
|
||||
'sites/**/*.channels.xml'
|
||||
)
|
||||
.parse(process.argv)
|
||||
|
||||
const options = program.opts()
|
||||
program.argument('[filepath]', 'Path to *.channels.xml files to validate').parse(process.argv)
|
||||
|
||||
async function main() {
|
||||
const logger = new Logger()
|
||||
const storage = new Storage()
|
||||
|
||||
logger.info('options:')
|
||||
logger.tree(options)
|
||||
|
||||
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) {
|
||||
const file = new File(filepath)
|
||||
if (file.extension() !== 'xml') continue
|
||||
|
@ -51,11 +39,15 @@ async function main() {
|
|||
|
||||
let localErrors: ValidationError[] = []
|
||||
|
||||
const xsdDoc = libxml.parseXml(xsd)
|
||||
const doc = libxml.parseXml(xml)
|
||||
try {
|
||||
const xsdDoc = libxml.parseXml(xsd)
|
||||
const doc = libxml.parseXml(xml)
|
||||
|
||||
if (!doc.validate(xsdDoc)) {
|
||||
localErrors = doc.validationErrors
|
||||
if (!doc.validate(xsdDoc)) {
|
||||
localErrors = doc.validationErrors
|
||||
}
|
||||
} catch (error) {
|
||||
localErrors.push(error)
|
||||
}
|
||||
|
||||
if (localErrors.length) {
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Logger, File, Collection, Storage } from '@freearhey/core'
|
|||
import { ChannelsParser, XML } from '../../core'
|
||||
import { Channel } from 'epg-grabber'
|
||||
import { Command } from 'commander'
|
||||
import path from 'path'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
|
||||
const program = new Command()
|
||||
program
|
||||
|
@ -26,7 +26,7 @@ async function main() {
|
|||
const logger = new Logger()
|
||||
const file = new File(options.config)
|
||||
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`
|
||||
|
||||
let channels = new Collection()
|
||||
|
|
|
@ -47,7 +47,6 @@ async function main() {
|
|||
|
||||
const parsedChannels = await parser.parse(filepath)
|
||||
|
||||
const bufferById = new Dictionary()
|
||||
const bufferBySiteId = new Dictionary()
|
||||
const errors: ValidationError[] = []
|
||||
parsedChannels.forEach((channel: Channel) => {
|
||||
|
|
|
@ -1,58 +1,58 @@
|
|||
import { Logger, Storage, Collection, Dictionary } from '@freearhey/core'
|
||||
import { IssueLoader, HTMLTable, Markdown } from '../../core'
|
||||
import { Issue, Site } from '../../models'
|
||||
import { SITES_DIR, DOT_SITES_DIR } from '../../constants'
|
||||
import path from 'path'
|
||||
|
||||
async function main() {
|
||||
const logger = new Logger({ disabled: true })
|
||||
const loader = new IssueLoader()
|
||||
const storage = new Storage(SITES_DIR)
|
||||
const sites = new Collection()
|
||||
|
||||
logger.info('loading list of sites')
|
||||
const folders = await storage.list('*/')
|
||||
|
||||
logger.info('loading issues...')
|
||||
const issues = await loadIssues(loader)
|
||||
|
||||
logger.info('putting the data together...')
|
||||
folders.forEach((domain: string) => {
|
||||
const filteredIssues = issues.filter((issue: Issue) => domain === issue.data.get('site'))
|
||||
const site = new Site({
|
||||
domain,
|
||||
issues: filteredIssues
|
||||
})
|
||||
|
||||
sites.add(site)
|
||||
})
|
||||
|
||||
logger.info('creating sites table...')
|
||||
let data = new Collection()
|
||||
sites.forEach((site: Site) => {
|
||||
data.add([
|
||||
`<a href="sites/${site.domain}">${site.domain}</a>`,
|
||||
site.getStatus().emoji,
|
||||
site.getIssues().all().join(', ')
|
||||
])
|
||||
})
|
||||
|
||||
const table = new HTMLTable(data.all(), [{ name: 'Site' }, { name: 'Status' }, { name: 'Notes' }])
|
||||
|
||||
const readmeStorage = new Storage(DOT_SITES_DIR)
|
||||
await readmeStorage.save('_table.md', table.toString())
|
||||
|
||||
logger.info('updating sites.md...')
|
||||
const configPath = path.join(DOT_SITES_DIR, 'config.json')
|
||||
const sitesMarkdown = new Markdown(configPath)
|
||||
sitesMarkdown.compile()
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
async function loadIssues(loader: IssueLoader) {
|
||||
const issuesWithStatusWarning = await loader.load({ labels: ['broken guide', 'status:warning'] })
|
||||
const issuesWithStatusDown = await loader.load({ labels: ['broken guide', 'status:down'] })
|
||||
|
||||
return issuesWithStatusWarning.concat(issuesWithStatusDown)
|
||||
}
|
||||
import { Logger, Storage, Collection } from '@freearhey/core'
|
||||
import { IssueLoader, HTMLTable, Markdown } from '../../core'
|
||||
import { Issue, Site } from '../../models'
|
||||
import { SITES_DIR, DOT_SITES_DIR } from '../../constants'
|
||||
import path from 'path'
|
||||
|
||||
async function main() {
|
||||
const logger = new Logger({ disabled: true })
|
||||
const loader = new IssueLoader()
|
||||
const storage = new Storage(SITES_DIR)
|
||||
const sites = new Collection()
|
||||
|
||||
logger.info('loading list of sites')
|
||||
const folders = await storage.list('*/')
|
||||
|
||||
logger.info('loading issues...')
|
||||
const issues = await loadIssues(loader)
|
||||
|
||||
logger.info('putting the data together...')
|
||||
folders.forEach((domain: string) => {
|
||||
const filteredIssues = issues.filter((issue: Issue) => domain === issue.data.get('site'))
|
||||
const site = new Site({
|
||||
domain,
|
||||
issues: filteredIssues
|
||||
})
|
||||
|
||||
sites.add(site)
|
||||
})
|
||||
|
||||
logger.info('creating sites table...')
|
||||
const data = new Collection()
|
||||
sites.forEach((site: Site) => {
|
||||
data.add([
|
||||
`<a href="sites/${site.domain}">${site.domain}</a>`,
|
||||
site.getStatus().emoji,
|
||||
site.getIssues().all().join(', ')
|
||||
])
|
||||
})
|
||||
|
||||
const table = new HTMLTable(data.all(), [{ name: 'Site' }, { name: 'Status' }, { name: 'Notes' }])
|
||||
|
||||
const readmeStorage = new Storage(DOT_SITES_DIR)
|
||||
await readmeStorage.save('_table.md', table.toString())
|
||||
|
||||
logger.info('updating sites.md...')
|
||||
const configPath = path.join(DOT_SITES_DIR, 'config.json')
|
||||
const sitesMarkdown = new Markdown(configPath)
|
||||
sitesMarkdown.compile()
|
||||
}
|
||||
|
||||
main()
|
||||
|
||||
async function loadIssues(loader: IssueLoader) {
|
||||
const issuesWithStatusWarning = await loader.load({ labels: ['broken guide', 'status:warning'] })
|
||||
const issuesWithStatusDown = await loader.load({ labels: ['broken guide', 'status:down'] })
|
||||
|
||||
return issuesWithStatusWarning.concat(issuesWithStatusDown)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
import dayjs from 'dayjs'
|
||||
import utc from 'dayjs/plugin/utc'
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = {}
|
||||
|
@ -10,4 +11,4 @@ date.getUTC = function (d = null) {
|
|||
return dayjs.utc().startOf('d')
|
||||
}
|
||||
|
||||
module.exports = date
|
||||
export default date
|
||||
|
|
|
@ -1,46 +1,46 @@
|
|||
type Column = {
|
||||
name: string
|
||||
nowrap?: boolean
|
||||
align?: string
|
||||
}
|
||||
|
||||
type DataItem = string[]
|
||||
|
||||
export class HTMLTable {
|
||||
data: DataItem[]
|
||||
columns: Column[]
|
||||
|
||||
constructor(data: DataItem[], columns: Column[]) {
|
||||
this.data = data
|
||||
this.columns = columns
|
||||
}
|
||||
|
||||
toString() {
|
||||
let output = '<table>\n'
|
||||
|
||||
output += ' <thead>\n <tr>'
|
||||
for (const column of this.columns) {
|
||||
output += `<th align="left">${column.name}</th>`
|
||||
}
|
||||
output += '</tr>\n </thead>\n'
|
||||
|
||||
output += ' <tbody>\n'
|
||||
for (const item of this.data) {
|
||||
output += ' <tr>'
|
||||
let i = 0
|
||||
for (const prop in item) {
|
||||
const column = this.columns[i]
|
||||
const nowrap = column.nowrap ? ' nowrap' : ''
|
||||
const align = column.align ? ` align="${column.align}"` : ''
|
||||
output += `<td${align}${nowrap}>${item[prop]}</td>`
|
||||
i++
|
||||
}
|
||||
output += '</tr>\n'
|
||||
}
|
||||
output += ' </tbody>\n'
|
||||
|
||||
output += '</table>'
|
||||
|
||||
return output
|
||||
}
|
||||
}
|
||||
type Column = {
|
||||
name: string
|
||||
nowrap?: boolean
|
||||
align?: string
|
||||
}
|
||||
|
||||
type DataItem = string[]
|
||||
|
||||
export class HTMLTable {
|
||||
data: DataItem[]
|
||||
columns: Column[]
|
||||
|
||||
constructor(data: DataItem[], columns: Column[]) {
|
||||
this.data = data
|
||||
this.columns = columns
|
||||
}
|
||||
|
||||
toString() {
|
||||
let output = '<table>\r\n'
|
||||
|
||||
output += ' <thead>\r\n <tr>'
|
||||
for (const column of this.columns) {
|
||||
output += `<th align="left">${column.name}</th>`
|
||||
}
|
||||
output += '</tr>\r\n </thead>\r\n'
|
||||
|
||||
output += ' <tbody>\r\n'
|
||||
for (const item of this.data) {
|
||||
output += ' <tr>'
|
||||
let i = 0
|
||||
for (const prop in item) {
|
||||
const column = this.columns[i]
|
||||
const nowrap = column.nowrap ? ' nowrap' : ''
|
||||
const align = column.align ? ` align="${column.align}"` : ''
|
||||
output += `<td${align}${nowrap}>${item[prop]}</td>`
|
||||
i++
|
||||
}
|
||||
output += '</tr>\r\n'
|
||||
}
|
||||
output += ' </tbody>\r\n'
|
||||
|
||||
output += '</table>'
|
||||
|
||||
return output
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,40 +1,41 @@
|
|||
import { Collection } from '@freearhey/core'
|
||||
import { restEndpointMethods } from '@octokit/plugin-rest-endpoint-methods'
|
||||
import { paginateRest } from '@octokit/plugin-paginate-rest'
|
||||
import { Octokit } from '@octokit/core'
|
||||
import { IssueParser } from './'
|
||||
import { TESTING, OWNER, REPO } from '../constants'
|
||||
|
||||
const CustomOctokit = Octokit.plugin(paginateRest, restEndpointMethods)
|
||||
const octokit = new CustomOctokit()
|
||||
|
||||
export class IssueLoader {
|
||||
async load({ labels }: { labels: string[] | string }) {
|
||||
labels = Array.isArray(labels) ? labels.join(',') : labels
|
||||
let issues: object[] = []
|
||||
if (TESTING) {
|
||||
switch (labels) {
|
||||
case 'broken guide,status:warning':
|
||||
issues = require('../../tests/__data__/input/issues/broken_guide_warning.js')
|
||||
break
|
||||
case 'broken guide,status:down':
|
||||
issues = require('../../tests/__data__/input/issues/broken_guide_down.js')
|
||||
break
|
||||
}
|
||||
} else {
|
||||
issues = await octokit.paginate(octokit.rest.issues.listForRepo, {
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
per_page: 100,
|
||||
labels,
|
||||
headers: {
|
||||
'X-GitHub-Api-Version': '2022-11-28'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const parser = new IssueParser()
|
||||
|
||||
return new Collection(issues).map(parser.parse)
|
||||
}
|
||||
}
|
||||
import { Collection } from '@freearhey/core'
|
||||
import { restEndpointMethods } from '@octokit/plugin-rest-endpoint-methods'
|
||||
import { paginateRest } from '@octokit/plugin-paginate-rest'
|
||||
import { Octokit } from '@octokit/core'
|
||||
import { IssueParser } from './'
|
||||
import { TESTING, OWNER, REPO } from '../constants'
|
||||
|
||||
const CustomOctokit = Octokit.plugin(paginateRest, restEndpointMethods)
|
||||
const octokit = new CustomOctokit()
|
||||
|
||||
export class IssueLoader {
|
||||
async load({ labels }: { labels: string[] | string }) {
|
||||
labels = Array.isArray(labels) ? labels.join(',') : labels
|
||||
let issues: object[] = []
|
||||
if (TESTING) {
|
||||
switch (labels) {
|
||||
case 'broken guide,status:warning':
|
||||
issues = (await import('../../tests/__data__/input/issues/broken_guide_warning.mjs'))
|
||||
.default
|
||||
break
|
||||
case 'broken guide,status:down':
|
||||
issues = (await import('../../tests/__data__/input/issues/broken_guide_down.mjs')).default
|
||||
break
|
||||
}
|
||||
} else {
|
||||
issues = await octokit.paginate(octokit.rest.issues.listForRepo, {
|
||||
owner: OWNER,
|
||||
repo: REPO,
|
||||
per_page: 100,
|
||||
labels,
|
||||
headers: {
|
||||
'X-GitHub-Api-Version': '2022-11-28'
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const parser = new IssueParser()
|
||||
|
||||
return new Collection(issues).map(parser.parse)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,34 +1,34 @@
|
|||
import { Dictionary } from '@freearhey/core'
|
||||
import { Issue } from '../models'
|
||||
|
||||
const FIELDS = new Dictionary({
|
||||
Site: 'site'
|
||||
})
|
||||
|
||||
export class IssueParser {
|
||||
parse(issue: { number: number; body: string; labels: { name: string }[] }): Issue {
|
||||
const fields = issue.body.split('###')
|
||||
|
||||
const data = new Dictionary()
|
||||
fields.forEach((field: string) => {
|
||||
let parsed = field.split(/\r?\n/).filter(Boolean)
|
||||
let _label = parsed.shift()
|
||||
_label = _label ? _label.trim() : ''
|
||||
let _value = parsed.join('\r\n')
|
||||
_value = _value ? _value.trim() : ''
|
||||
|
||||
if (!_label || !_value) return data
|
||||
|
||||
const id: string = FIELDS.get(_label)
|
||||
const value: string = _value === '_No response_' || _value === 'None' ? '' : _value
|
||||
|
||||
if (!id) return
|
||||
|
||||
data.set(id, value)
|
||||
})
|
||||
|
||||
const labels = issue.labels.map(label => label.name)
|
||||
|
||||
return new Issue({ number: issue.number, labels, data })
|
||||
}
|
||||
}
|
||||
import { Dictionary } from '@freearhey/core'
|
||||
import { Issue } from '../models'
|
||||
|
||||
const FIELDS = new Dictionary({
|
||||
Site: 'site'
|
||||
})
|
||||
|
||||
export class IssueParser {
|
||||
parse(issue: { number: number; body: string; labels: { name: string }[] }): Issue {
|
||||
const fields = issue.body.split('###')
|
||||
|
||||
const data = new Dictionary()
|
||||
fields.forEach((field: string) => {
|
||||
const parsed = field.split(/\r?\n/).filter(Boolean)
|
||||
let _label = parsed.shift()
|
||||
_label = _label ? _label.trim() : ''
|
||||
let _value = parsed.join('\r\n')
|
||||
_value = _value ? _value.trim() : ''
|
||||
|
||||
if (!_label || !_value) return data
|
||||
|
||||
const id: string = FIELDS.get(_label)
|
||||
const value: string = _value === '_No response_' || _value === 'None' ? '' : _value
|
||||
|
||||
if (!id) return
|
||||
|
||||
data.set(id, value)
|
||||
})
|
||||
|
||||
const labels = issue.labels.map(label => label.name)
|
||||
|
||||
return new Issue({ number: issue.number, labels, data })
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
import markdownInclude from 'markdown-include'
|
||||
|
||||
export class Markdown {
|
||||
filepath: string
|
||||
|
||||
constructor(filepath: string) {
|
||||
this.filepath = filepath
|
||||
}
|
||||
|
||||
compile() {
|
||||
markdownInclude.compileFiles(this.filepath)
|
||||
}
|
||||
}
|
||||
import markdownInclude from 'markdown-include'
|
||||
|
||||
export class Markdown {
|
||||
filepath: string
|
||||
|
||||
constructor(filepath: string) {
|
||||
this.filepath = filepath
|
||||
}
|
||||
|
||||
compile() {
|
||||
markdownInclude.compileFiles(this.filepath)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { Storage, Collection, DateTime, Logger } from '@freearhey/core'
|
||||
import { ChannelsParser, ConfigLoader, ApiChannel, Queue } from './'
|
||||
import { SITES_DIR, DATA_DIR } from '../constants'
|
||||
import { Channel, SiteConfig } from 'epg-grabber'
|
||||
import { SiteConfig } from 'epg-grabber'
|
||||
import path from 'path'
|
||||
import { GrabOptions } from '../commands/epg/grab'
|
||||
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
export * from './issue'
|
||||
export * from './site'
|
||||
export * from './issue'
|
||||
export * from './site'
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
import { Dictionary } from '@freearhey/core'
|
||||
import { OWNER, REPO } from '../constants'
|
||||
|
||||
type IssueProps = {
|
||||
number: number
|
||||
labels: string[]
|
||||
data: Dictionary
|
||||
}
|
||||
|
||||
export class Issue {
|
||||
number: number
|
||||
labels: string[]
|
||||
data: Dictionary
|
||||
|
||||
constructor({ number, labels, data }: IssueProps) {
|
||||
this.number = number
|
||||
this.labels = labels
|
||||
this.data = data
|
||||
}
|
||||
|
||||
getURL() {
|
||||
return `https://github.com/${OWNER}/${REPO}/issues/${this.number}`
|
||||
}
|
||||
}
|
||||
import { Dictionary } from '@freearhey/core'
|
||||
import { OWNER, REPO } from '../constants'
|
||||
|
||||
type IssueProps = {
|
||||
number: number
|
||||
labels: string[]
|
||||
data: Dictionary
|
||||
}
|
||||
|
||||
export class Issue {
|
||||
number: number
|
||||
labels: string[]
|
||||
data: Dictionary
|
||||
|
||||
constructor({ number, labels, data }: IssueProps) {
|
||||
this.number = number
|
||||
this.labels = labels
|
||||
this.data = data
|
||||
}
|
||||
|
||||
getURL() {
|
||||
return `https://github.com/${OWNER}/${REPO}/issues/${this.number}`
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,57 +1,57 @@
|
|||
import { Collection } from '@freearhey/core'
|
||||
import { Issue } from './'
|
||||
|
||||
enum StatusCode {
|
||||
DOWN = 'down',
|
||||
WARNING = 'warning',
|
||||
OK = 'ok'
|
||||
}
|
||||
|
||||
type Status = {
|
||||
code: StatusCode
|
||||
emoji: string
|
||||
}
|
||||
|
||||
type SiteProps = {
|
||||
domain: string
|
||||
issues: Collection
|
||||
}
|
||||
|
||||
export class Site {
|
||||
domain: string
|
||||
issues: Collection
|
||||
|
||||
constructor({ domain, issues }: SiteProps) {
|
||||
this.domain = domain
|
||||
this.issues = issues
|
||||
}
|
||||
|
||||
getStatus(): Status {
|
||||
const issuesWithStatusDown = this.issues.filter((issue: Issue) =>
|
||||
issue.labels.find(label => label === 'status:down')
|
||||
)
|
||||
if (issuesWithStatusDown.notEmpty())
|
||||
return {
|
||||
code: StatusCode.DOWN,
|
||||
emoji: '🔴'
|
||||
}
|
||||
|
||||
const issuesWithStatusWarning = this.issues.filter((issue: Issue) =>
|
||||
issue.labels.find(label => label === 'status:warning')
|
||||
)
|
||||
if (issuesWithStatusWarning.notEmpty())
|
||||
return {
|
||||
code: StatusCode.WARNING,
|
||||
emoji: '🟡'
|
||||
}
|
||||
|
||||
return {
|
||||
code: StatusCode.OK,
|
||||
emoji: '🟢'
|
||||
}
|
||||
}
|
||||
|
||||
getIssues(): Collection {
|
||||
return this.issues.map((issue: Issue) => issue.getURL())
|
||||
}
|
||||
}
|
||||
import { Collection } from '@freearhey/core'
|
||||
import { Issue } from './'
|
||||
|
||||
enum StatusCode {
|
||||
DOWN = 'down',
|
||||
WARNING = 'warning',
|
||||
OK = 'ok'
|
||||
}
|
||||
|
||||
type Status = {
|
||||
code: StatusCode
|
||||
emoji: string
|
||||
}
|
||||
|
||||
type SiteProps = {
|
||||
domain: string
|
||||
issues: Collection
|
||||
}
|
||||
|
||||
export class Site {
|
||||
domain: string
|
||||
issues: Collection
|
||||
|
||||
constructor({ domain, issues }: SiteProps) {
|
||||
this.domain = domain
|
||||
this.issues = issues
|
||||
}
|
||||
|
||||
getStatus(): Status {
|
||||
const issuesWithStatusDown = this.issues.filter((issue: Issue) =>
|
||||
issue.labels.find(label => label === 'status:down')
|
||||
)
|
||||
if (issuesWithStatusDown.notEmpty())
|
||||
return {
|
||||
code: StatusCode.DOWN,
|
||||
emoji: '🔴'
|
||||
}
|
||||
|
||||
const issuesWithStatusWarning = this.issues.filter((issue: Issue) =>
|
||||
issue.labels.find(label => label === 'status:warning')
|
||||
)
|
||||
if (issuesWithStatusWarning.notEmpty())
|
||||
return {
|
||||
code: StatusCode.WARNING,
|
||||
emoji: '🟡'
|
||||
}
|
||||
|
||||
return {
|
||||
code: StatusCode.OK,
|
||||
emoji: '🟢'
|
||||
}
|
||||
}
|
||||
|
||||
getIssues(): Collection {
|
||||
return this.issues.map((issue: Issue) => issue.getURL())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,7 +92,7 @@ function parseItems(content, channel) {
|
|||
const [, channelId] = channel.site_id.split('#')
|
||||
const channelData = data.schedule.find(i => i.channel == channelId)
|
||||
return channelData.listing && Array.isArray(channelData.listing) ? channelData.listing : []
|
||||
} catch (err) {
|
||||
} catch {
|
||||
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 path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
const axios = require('axios')
|
||||
const cheerio = require('cheerio')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
|
@ -62,7 +61,7 @@ function parseItems(content) {
|
|||
let data
|
||||
try {
|
||||
data = JSON.parse(content)
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
const { parser, url } = require('./beinsports.com.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
|
|
@ -1,54 +1,55 @@
|
|||
const axios = require('axios');
|
||||
const cheerio = require('cheerio');
|
||||
const dayjs = require('dayjs');
|
||||
const utc = require('dayjs/plugin/utc');
|
||||
const timezone = require('dayjs/plugin/timezone');
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat');
|
||||
|
||||
dayjs.extend(utc);
|
||||
dayjs.extend(timezone);
|
||||
dayjs.extend(customParseFormat);
|
||||
|
||||
module.exports = {
|
||||
site: 'chada.ma',
|
||||
channels: 'chada.ma.channels.xml',
|
||||
days: 1,
|
||||
request: {
|
||||
cache: {
|
||||
ttl: 60 * 60 * 1000 // 1 hour
|
||||
}
|
||||
},
|
||||
url() {
|
||||
return 'https://chada.ma/fr/chada-tv/grille-tv/';
|
||||
},
|
||||
parser: function ({ content }) {
|
||||
const $ = cheerio.load(content);
|
||||
const programs = [];
|
||||
|
||||
$('#stopfix .posts-area h2').each((i, element) => {
|
||||
const timeRange = $(element).text().trim();
|
||||
const [start, stop] = timeRange.split(' - ').map(t => parseProgramTime(t.trim()));
|
||||
|
||||
const titleElement = $(element).next('div').next('h3');
|
||||
const title = titleElement.text().trim();
|
||||
|
||||
const description = titleElement.next('div').text().trim() || 'No description available';
|
||||
|
||||
programs.push({
|
||||
title,
|
||||
description,
|
||||
start,
|
||||
stop
|
||||
});
|
||||
});
|
||||
|
||||
return programs;
|
||||
}
|
||||
};
|
||||
|
||||
function parseProgramTime(timeStr) {
|
||||
const timeZone = 'Africa/Casablanca';
|
||||
const currentDate = dayjs().format('YYYY-MM-DD');
|
||||
|
||||
return dayjs.tz(`${currentDate} ${timeStr}`, 'YYYY-MM-DD HH:mm', timeZone).format('YYYY-MM-DDTHH:mm:ssZ');
|
||||
}
|
||||
const cheerio = require('cheerio')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
module.exports = {
|
||||
site: 'chada.ma',
|
||||
channels: 'chada.ma.channels.xml',
|
||||
days: 1,
|
||||
request: {
|
||||
cache: {
|
||||
ttl: 60 * 60 * 1000 // 1 hour
|
||||
}
|
||||
},
|
||||
url() {
|
||||
return 'https://chada.ma/fr/chada-tv/grille-tv/'
|
||||
},
|
||||
parser: function ({ content }) {
|
||||
const $ = cheerio.load(content)
|
||||
const programs = []
|
||||
|
||||
$('#stopfix .posts-area h2').each((i, element) => {
|
||||
const timeRange = $(element).text().trim()
|
||||
const [start, stop] = timeRange.split(' - ').map(t => parseProgramTime(t.trim()))
|
||||
|
||||
const titleElement = $(element).next('div').next('h3')
|
||||
const title = titleElement.text().trim()
|
||||
|
||||
const description = titleElement.next('div').text().trim() || 'No description available'
|
||||
|
||||
programs.push({
|
||||
title,
|
||||
description,
|
||||
start,
|
||||
stop
|
||||
})
|
||||
})
|
||||
|
||||
return programs
|
||||
}
|
||||
}
|
||||
|
||||
function parseProgramTime(timeStr) {
|
||||
const timeZone = 'Africa/Casablanca'
|
||||
const currentDate = dayjs().format('YYYY-MM-DD')
|
||||
|
||||
return dayjs
|
||||
.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 axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const cheerio = require('cheerio')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
jest.mock('axios')
|
||||
|
||||
const mockHtmlContent = `
|
||||
<div class="pm0 col-md-8" id="stopfix">
|
||||
<h2 class="posts-date">Programmes d'Aujourd'hui</h2>
|
||||
<div class="posts-area">
|
||||
<h2> <i class="fas fa-circle"></i>00:00 - 09:00</h2>
|
||||
<div class="relativeme">
|
||||
<a href="https://chada.ma/fr/emissions/bloc-prime-clips/">
|
||||
<img class="programthumb" src="https://chada.ma/wp-content/uploads/2023/11/Autres-slides-clips-la-couverture.jpg">
|
||||
</a>
|
||||
</div>
|
||||
<h3>Bloc Prime + Clips</h3>
|
||||
<div class="authorbox"></div>
|
||||
<div class="ssprogramme row"></div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url()).toBe('https://chada.ma/fr/chada-tv/grille-tv/')
|
||||
});
|
||||
|
||||
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')
|
||||
p.stop = dayjs(p.stop).tz('Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ')
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: "Bloc Prime + Clips",
|
||||
description: "No description available",
|
||||
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({
|
||||
content: '<div class="pm0 col-md-8" id="stopfix"><div class="posts-area"></div></div>'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
const { parser, url } = require('./chada.ma.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
jest.mock('axios')
|
||||
|
||||
const mockHtmlContent = `
|
||||
<div class="pm0 col-md-8" id="stopfix">
|
||||
<h2 class="posts-date">Programmes d'Aujourd'hui</h2>
|
||||
<div class="posts-area">
|
||||
<h2> <i class="fas fa-circle"></i>00:00 - 09:00</h2>
|
||||
<div class="relativeme">
|
||||
<a href="https://chada.ma/fr/emissions/bloc-prime-clips/">
|
||||
<img class="programthumb" src="https://chada.ma/wp-content/uploads/2023/11/Autres-slides-clips-la-couverture.jpg">
|
||||
</a>
|
||||
</div>
|
||||
<h3>Bloc Prime + Clips</h3>
|
||||
<div class="authorbox"></div>
|
||||
<div class="ssprogramme row"></div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url()).toBe('https://chada.ma/fr/chada-tv/grille-tv/')
|
||||
})
|
||||
|
||||
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')
|
||||
p.stop = dayjs(p.stop).tz('Africa/Casablanca').format('YYYY-MM-DDTHH:mm:ssZ')
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: 'Bloc Prime + Clips',
|
||||
description: 'No description available',
|
||||
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({
|
||||
content: '<div class="pm0 col-md-8" id="stopfix"><div class="posts-area"></div></div>'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -16,9 +16,12 @@ module.exports = {
|
|||
const start = parseStart(item)
|
||||
const stop = parseStop(item, start)
|
||||
programs.push({
|
||||
title: item.season?.serie?.title ? item.season.serie.title : item.title,
|
||||
title: item.title,
|
||||
subTitle: item.season?.serie?.title,
|
||||
category: item.genreDetailed,
|
||||
description: item.synopsis,
|
||||
season: parseSeason(item),
|
||||
episode: parseEpisode(item),
|
||||
image: parseImage(item),
|
||||
start: start.toJSON(),
|
||||
stop: stop.toJSON()
|
||||
|
@ -29,7 +32,7 @@ module.exports = {
|
|||
},
|
||||
async channels() {
|
||||
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)
|
||||
.catch(console.log)
|
||||
|
||||
|
@ -61,6 +64,14 @@ function parseStop(item, start) {
|
|||
return start.add(item.duration, 's')
|
||||
}
|
||||
|
||||
function parseSeason(item) {
|
||||
return item.season?.number
|
||||
}
|
||||
|
||||
function parseEpisode(item) {
|
||||
return item.episodeNumber
|
||||
}
|
||||
|
||||
function parseItems(content, channel) {
|
||||
const data = JSON.parse(content)
|
||||
|
||||
|
|
|
@ -27,6 +27,9 @@ it('can parse response', () => {
|
|||
start: '2021-11-07T23:35:00.000Z',
|
||||
stop: '2021-11-08T00:20:00.000Z',
|
||||
title: 'Tête de liste',
|
||||
subTitle: 'Esprits criminels',
|
||||
season: 10,
|
||||
episode: 12,
|
||||
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.",
|
||||
category: 'Série Suspense',
|
||||
|
|
|
@ -40,7 +40,7 @@ module.exports = {
|
|||
},
|
||||
async channels() {
|
||||
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)
|
||||
.catch(console.log)
|
||||
|
||||
|
@ -85,7 +85,7 @@ function parseItems(content, date) {
|
|||
const schedules = data.response.schedule
|
||||
|
||||
return schedules[date.format('YYYY-MM-DD')] || []
|
||||
} catch (e) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,81 +1,85 @@
|
|||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(timezone)
|
||||
|
||||
module.exports = {
|
||||
site: 'cosmotetv.gr',
|
||||
days: 5,
|
||||
request: {
|
||||
cache: {
|
||||
ttl: 60 * 60 * 1000 // 1 hour
|
||||
},
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'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',
|
||||
'Accept': '*/*',
|
||||
'Accept-Language': 'en-US,en;q=0.9',
|
||||
'Accept-Encoding': 'gzip, deflate, br, zstd',
|
||||
'Origin': 'https://www.cosmotetv.gr',
|
||||
'Sec-Ch-Ua': '"Not.A/Brand";v="24", "Chromium";v="131", "Google Chrome";v="131"',
|
||||
'Sec-Ch-Ua-Mobile': '?0',
|
||||
'Sec-Ch-Ua-Platform': '"Windows"',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'cross-site'
|
||||
}
|
||||
},
|
||||
url: function ({date, channel}) {
|
||||
const startOfDay = dayjs(date).startOf('day').utc().unix()
|
||||
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 = []
|
||||
const data = JSON.parse(content)
|
||||
data.channels.forEach(channel => {
|
||||
channel.items.forEach(item => {
|
||||
const start = dayjs(item.startTime).utc().toISOString()
|
||||
const stop = dayjs(item.endTime).utc().toISOString()
|
||||
programs.push({
|
||||
title: item.title,
|
||||
description: item.description || 'No description available',
|
||||
category: item.qoe.genre,
|
||||
image: item.thumbnails.standard,
|
||||
start,
|
||||
stop
|
||||
})
|
||||
})
|
||||
})
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const axios = require('axios')
|
||||
try {
|
||||
const response = await axios.get('https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/channels/all/el', {
|
||||
headers: this.request.headers
|
||||
})
|
||||
const data = response.data
|
||||
|
||||
if (data && data.channels) {
|
||||
return data.channels.map(item => ({
|
||||
lang: 'el',
|
||||
site_id: item.callSign,
|
||||
name: item.title,
|
||||
//logo: item.logos.square
|
||||
}))
|
||||
} else {
|
||||
console.error('Unexpected response structure:', data)
|
||||
return []
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error fetching channel data:', error)
|
||||
return []
|
||||
}
|
||||
}
|
||||
}
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(timezone)
|
||||
|
||||
module.exports = {
|
||||
site: 'cosmotetv.gr',
|
||||
days: 5,
|
||||
request: {
|
||||
cache: {
|
||||
ttl: 60 * 60 * 1000 // 1 hour
|
||||
},
|
||||
method: 'GET',
|
||||
headers: {
|
||||
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',
|
||||
Accept: '*/*',
|
||||
'Accept-Language': 'en-US,en;q=0.9',
|
||||
'Accept-Encoding': 'gzip, deflate, br, zstd',
|
||||
Origin: 'https://www.cosmotetv.gr',
|
||||
'Sec-Ch-Ua': '"Not.A/Brand";v="24", "Chromium";v="131", "Google Chrome";v="131"',
|
||||
'Sec-Ch-Ua-Mobile': '?0',
|
||||
'Sec-Ch-Ua-Platform': '"Windows"',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'cross-site'
|
||||
}
|
||||
},
|
||||
url: function ({ date, channel }) {
|
||||
const startOfDay = dayjs(date).startOf('day').utc().unix()
|
||||
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 ({ content }) {
|
||||
let programs = []
|
||||
const data = JSON.parse(content)
|
||||
data.channels.forEach(channel => {
|
||||
channel.items.forEach(item => {
|
||||
const start = dayjs(item.startTime).utc().toISOString()
|
||||
const stop = dayjs(item.endTime).utc().toISOString()
|
||||
programs.push({
|
||||
title: item.title,
|
||||
description: item.description || 'No description available',
|
||||
category: item.qoe.genre,
|
||||
image: item.thumbnails.standard,
|
||||
start,
|
||||
stop
|
||||
})
|
||||
})
|
||||
})
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const axios = require('axios')
|
||||
try {
|
||||
const response = await axios.get(
|
||||
'https://mwapi-prod.cosmotetvott.gr/api/v3.4/epg/channels/all/el',
|
||||
{
|
||||
headers: this.request.headers
|
||||
}
|
||||
)
|
||||
const data = response.data
|
||||
|
||||
if (data && data.channels) {
|
||||
return data.channels.map(item => ({
|
||||
lang: 'el',
|
||||
site_id: item.callSign,
|
||||
name: item.title
|
||||
//logo: item.logos.square
|
||||
}))
|
||||
} else {
|
||||
console.error('Unexpected response structure:', data)
|
||||
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 dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const axios = require('axios')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(timezone)
|
||||
|
||||
jest.mock('axios')
|
||||
|
||||
const date = dayjs.utc('2024-12-26', 'YYYY-MM-DD').startOf('d')
|
||||
const channel = { site_id: 'vouli', xmltv_id: 'HellenicParliamentTV.gr' }
|
||||
|
||||
const mockChannelData = {
|
||||
"channels": [
|
||||
{
|
||||
"guid": "XTV100000954",
|
||||
"title": "ΒΟΥΛΗ HD",
|
||||
"callSign": "vouli",
|
||||
"logos": {
|
||||
"square": "https://tr.static.cdn.cosmotetvott.gr/ote-prod/channel_logos/vouli1-normal.png",
|
||||
"wide": "https://tr.static.cdn.cosmotetvott.gr/ote-prod/channel_logos/vouli1-wide.png"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const mockEpgData = {
|
||||
"channels": [
|
||||
{
|
||||
"items": [
|
||||
{
|
||||
"startTime": "2024-12-26T23:00:00+00:00",
|
||||
"endTime": "2024-12-27T00:00:00+00:00",
|
||||
"title": "Τι Λέει ο Νόμος",
|
||||
"description": "νημερωτική εκπομπή. Συζήτηση με τους εισηγητές των κομμάτων για το νομοθετικό έργο.",
|
||||
"qoe": {
|
||||
"genre": "Special"
|
||||
},
|
||||
"thumbnails": {
|
||||
"standard": "https://gr-ermou-prod-cache05.static.cdn.cosmotetvott.gr/ote-prod/70/280/040029714812000800_1734415727199.jpg"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
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`)
|
||||
})
|
||||
|
||||
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()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: "Τι Λέει ο Νόμος",
|
||||
description: "νημερωτική εκπομπή. Συζήτηση με τους εισηγητές των κομμάτων για το νομοθετικό έργο.",
|
||||
category: "Special",
|
||||
image: "https://gr-ermou-prod-cache05.static.cdn.cosmotetvott.gr/ote-prod/70/280/040029714812000800_1734415727199.jpg",
|
||||
start: "2024-12-26T23:00:00.000Z",
|
||||
stop: "2024-12-27T00:00:00.000Z"
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({ date, channel, content: '{"date":"2024-12-26","categories":[],"channels":[]}' });
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
const { parser, url } = require('./cosmotetv.gr.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(timezone)
|
||||
|
||||
jest.mock('axios')
|
||||
|
||||
const date = dayjs.utc('2024-12-26', 'YYYY-MM-DD').startOf('d')
|
||||
const channel = { site_id: 'vouli', xmltv_id: 'HellenicParliamentTV.gr' }
|
||||
|
||||
const mockEpgData = {
|
||||
channels: [
|
||||
{
|
||||
items: [
|
||||
{
|
||||
startTime: '2024-12-26T23:00:00+00:00',
|
||||
endTime: '2024-12-27T00:00:00+00:00',
|
||||
title: 'Τι Λέει ο Νόμος',
|
||||
description:
|
||||
'νημερωτική εκπομπή. Συζήτηση με τους εισηγητές των κομμάτων για το νομοθετικό έργο.',
|
||||
qoe: {
|
||||
genre: 'Special'
|
||||
},
|
||||
thumbnails: {
|
||||
standard:
|
||||
'https://gr-ermou-prod-cache05.static.cdn.cosmotetvott.gr/ote-prod/70/280/040029714812000800_1734415727199.jpg'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
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`
|
||||
)
|
||||
})
|
||||
|
||||
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()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: 'Τι Λέει ο Νόμος',
|
||||
description:
|
||||
'νημερωτική εκπομπή. Συζήτηση με τους εισηγητές των κομμάτων για το νομοθετικό έργο.',
|
||||
category: 'Special',
|
||||
image:
|
||||
'https://gr-ermou-prod-cache05.static.cdn.cosmotetvott.gr/ote-prod/70/280/040029714812000800_1734415727199.jpg',
|
||||
start: '2024-12-26T23:00:00.000Z',
|
||||
stop: '2024-12-27T00:00:00.000Z'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
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 timezone = require('dayjs/plugin/timezone')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(utc)
|
||||
|
||||
module.exports = {
|
||||
site: 'cubmu.com',
|
||||
days: 2,
|
||||
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}`
|
||||
},
|
||||
parser({ content, channel }) {
|
||||
const programs = []
|
||||
const items = parseItems(content)
|
||||
items.forEach(item => {
|
||||
programs.push({
|
||||
title: parseTitle(item),
|
||||
description: parseDescription(item, channel.lang),
|
||||
episode: parseEpisode(item),
|
||||
start: parseStart(item).toISOString(),
|
||||
stop: parseStop(item).toISOString()
|
||||
})
|
||||
})
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels({ lang = 'id' }) {
|
||||
const axios = require('axios')
|
||||
const cheerio = require('cheerio')
|
||||
const result = await axios
|
||||
.get('https://cubmu.com/live-tv')
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
const $ = cheerio.load(result)
|
||||
|
||||
// retrieve service api data
|
||||
const config = JSON.parse($('#__NEXT_DATA__').text()).runtimeConfig || {}
|
||||
|
||||
const options = {
|
||||
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)
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
// list channels
|
||||
const subscribedChannels = await axios
|
||||
.post(`https://servicebuss.transvision.co.id/tvs/subscribe_product/list?platformId=${config.platformId}`, options)
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
const channels = []
|
||||
const included = []
|
||||
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,
|
||||
site_id: channel.id,
|
||||
name: channel.name
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return channels
|
||||
}
|
||||
}
|
||||
|
||||
function parseItems(content) {
|
||||
return content ? JSON.parse(content.trim()).result || [] : []
|
||||
}
|
||||
|
||||
function parseTitle(item) {
|
||||
return item.scehedule_title
|
||||
}
|
||||
|
||||
function parseDescription(item, lang = 'id') {
|
||||
return lang === 'id' ? item.schedule_json.primarySynopsis : item.schedule_json.secondarySynopsis
|
||||
}
|
||||
|
||||
function parseEpisode(item) {
|
||||
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')
|
||||
}
|
||||
const dayjs = require('dayjs')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(utc)
|
||||
|
||||
module.exports = {
|
||||
site: 'cubmu.com',
|
||||
days: 2,
|
||||
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}`
|
||||
},
|
||||
parser({ content, channel }) {
|
||||
const programs = []
|
||||
const items = parseItems(content)
|
||||
items.forEach(item => {
|
||||
programs.push({
|
||||
title: parseTitle(item),
|
||||
description: parseDescription(item, channel.lang),
|
||||
episode: parseEpisode(item),
|
||||
start: parseStart(item).toISOString(),
|
||||
stop: parseStop(item).toISOString()
|
||||
})
|
||||
})
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels({ lang = 'id' }) {
|
||||
const axios = require('axios')
|
||||
const cheerio = require('cheerio')
|
||||
const result = await axios
|
||||
.get('https://cubmu.com/live-tv')
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
const $ = cheerio.load(result)
|
||||
|
||||
// retrieve service api data
|
||||
const config = JSON.parse($('#__NEXT_DATA__').text()).runtimeConfig || {}
|
||||
|
||||
const options = {
|
||||
headers: {
|
||||
Origin: 'https://cubmu.com',
|
||||
Referer: 'https://cubmu.com/live-tv'
|
||||
}
|
||||
}
|
||||
// login to service bus
|
||||
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
|
||||
)
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
// list channels
|
||||
const subscribedChannels = await axios
|
||||
.post(
|
||||
`https://servicebuss.transvision.co.id/tvs/subscribe_product/list?platformId=${config.platformId}`,
|
||||
options
|
||||
)
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
const channels = []
|
||||
const included = []
|
||||
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,
|
||||
site_id: channel.id,
|
||||
name: channel.name
|
||||
})
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return channels
|
||||
}
|
||||
}
|
||||
|
||||
function parseItems(content) {
|
||||
return content ? JSON.parse(content.trim()).result || [] : []
|
||||
}
|
||||
|
||||
function parseTitle(item) {
|
||||
return item.scehedule_title
|
||||
}
|
||||
|
||||
function parseDescription(item, lang = 'id') {
|
||||
return lang === 'id' ? item.schedule_json.primarySynopsis : item.schedule_json.secondarySynopsis
|
||||
}
|
||||
|
||||
function parseEpisode(item) {
|
||||
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 dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2023-11-05', 'DD/MM/YYYY').startOf('d')
|
||||
const channel = { site_id: '4028c68574537fcd0174be43042758d8', xmltv_id: 'TransTV.id', lang: 'id' }
|
||||
const channelEn = Object.assign({}, channel, { lang: 'en' })
|
||||
|
||||
it('can generate valid url', () => {
|
||||
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'
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
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"}]}'
|
||||
|
||||
const idResults = parser({ content, channel })
|
||||
expect(idResults).toMatchObject([
|
||||
{
|
||||
start: '2023-11-04T18:30:00.000Z',
|
||||
stop: '2023-11-04T19:00:00.000Z',
|
||||
title: 'CNN Tech News',
|
||||
description:
|
||||
'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 })
|
||||
expect(enResults).toMatchObject([
|
||||
{
|
||||
start: '2023-11-04T18:30:00.000Z',
|
||||
stop: '2023-11-04T19:00:00.000Z',
|
||||
title: 'CNN Tech News',
|
||||
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.'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const results = parser({ content: '' })
|
||||
|
||||
expect(results).toMatchObject([])
|
||||
})
|
||||
const { url, parser } = require('./cubmu.com.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2023-11-05', 'DD/MM/YYYY').startOf('d')
|
||||
const channel = { site_id: '4028c68574537fcd0174be43042758d8', xmltv_id: 'TransTV.id', lang: 'id' }
|
||||
const channelEn = Object.assign({}, channel, { lang: 'en' })
|
||||
|
||||
it('can generate valid url', () => {
|
||||
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'
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
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"}]}'
|
||||
|
||||
const idResults = parser({ content, channel })
|
||||
expect(idResults).toMatchObject([
|
||||
{
|
||||
start: '2023-11-04T18:30:00.000Z',
|
||||
stop: '2023-11-04T19:00:00.000Z',
|
||||
title: 'CNN Tech News',
|
||||
description:
|
||||
'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 })
|
||||
expect(enResults).toMatchObject([
|
||||
{
|
||||
start: '2023-11-04T18:30:00.000Z',
|
||||
stop: '2023-11-04T19:00:00.000Z',
|
||||
title: 'CNN Tech News',
|
||||
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.'
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const results = parser({ content: '' })
|
||||
|
||||
expect(results).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -1,60 +1,59 @@
|
|||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
const cheerio = require('cheerio')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
module.exports = {
|
||||
site: 'cyta.com.cy',
|
||||
days: 7,
|
||||
request: {
|
||||
cache: {
|
||||
ttl: 60 * 60 * 1000 // 1 hour
|
||||
}
|
||||
},
|
||||
url: function ({date, channel}) {
|
||||
// Get the epoch timestamp
|
||||
const todayEpoch = date.startOf('day').utc().valueOf()
|
||||
// Get the epoch timestamp for the next day
|
||||
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}`
|
||||
},
|
||||
parser: function ({content}) {
|
||||
const data = JSON.parse(content)
|
||||
const programs = []
|
||||
|
||||
data.channelEpgs.forEach(channel => {
|
||||
channel.epgPlayables.forEach(epg => {
|
||||
const start = new Date(epg.startTime).toISOString();
|
||||
const stop = new Date(epg.endTime).toISOString();
|
||||
|
||||
programs.push({
|
||||
title: epg.name,
|
||||
start,
|
||||
stop
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const axios = require('axios')
|
||||
const data = await axios
|
||||
.get(`https://epg.cyta.com.cy/api/mediacatalog/fetchChannels?language=1`)
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
return data.channels.map(item => {
|
||||
return {
|
||||
lang: 'el',
|
||||
site_id: item.id,
|
||||
name: item.name
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
module.exports = {
|
||||
site: 'cyta.com.cy',
|
||||
days: 7,
|
||||
request: {
|
||||
cache: {
|
||||
ttl: 60 * 60 * 1000 // 1 hour
|
||||
}
|
||||
},
|
||||
url: function ({ date, channel }) {
|
||||
// Get the epoch timestamp
|
||||
const todayEpoch = date.startOf('day').utc().valueOf()
|
||||
// Get the epoch timestamp for the next day
|
||||
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}`
|
||||
},
|
||||
parser: function ({ content }) {
|
||||
const data = JSON.parse(content)
|
||||
const programs = []
|
||||
|
||||
data.channelEpgs.forEach(channel => {
|
||||
channel.epgPlayables.forEach(epg => {
|
||||
const start = new Date(epg.startTime).toISOString()
|
||||
const stop = new Date(epg.endTime).toISOString()
|
||||
|
||||
programs.push({
|
||||
title: epg.name,
|
||||
start,
|
||||
stop
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const axios = require('axios')
|
||||
const data = await axios
|
||||
.get('https://epg.cyta.com.cy/api/mediacatalog/fetchChannels?language=1')
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
return data.channels.map(item => {
|
||||
return {
|
||||
lang: 'el',
|
||||
site_id: item.id,
|
||||
name: item.name
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,53 +1,49 @@
|
|||
const { url, parser } = require('./cyta.com.cy.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
const date = dayjs.utc('2025-01-03', 'YYYY-MM-DD').startOf('day')
|
||||
const channel = {
|
||||
site_id: '561066',
|
||||
xmltv_id: 'RIK1.cy'
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
const generatedUrl = url({ date, channel })
|
||||
expect(generatedUrl).toBe(
|
||||
'https://epg.cyta.com.cy/api/mediacatalog/fetchEpg?startTimeEpoch=1735862400000&endTimeEpoch=1735948800000&language=1&channelIds=561066'
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = `
|
||||
{
|
||||
"channelEpgs": [
|
||||
{
|
||||
"epgPlayables": [
|
||||
{ "name": "Πρώτη Ενημέρωση", "startTime": 1735879500000, "endTime": 1735889400000 }
|
||||
]
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
const result = parser({ content }).map(p => {
|
||||
p.start = p.start
|
||||
p.stop = p.stop
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
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":[]}'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
const { url, parser } = require('./cyta.com.cy.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
const date = dayjs.utc('2025-01-03', 'YYYY-MM-DD').startOf('day')
|
||||
const channel = {
|
||||
site_id: '561066',
|
||||
xmltv_id: 'RIK1.cy'
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
const generatedUrl = url({ date, channel })
|
||||
expect(generatedUrl).toBe(
|
||||
'https://epg.cyta.com.cy/api/mediacatalog/fetchEpg?startTimeEpoch=1735862400000&endTimeEpoch=1735948800000&language=1&channelIds=561066'
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = `
|
||||
{
|
||||
"channelEpgs": [
|
||||
{
|
||||
"epgPlayables": [
|
||||
{ "name": "Πρώτη Ενημέρωση", "startTime": 1735879500000, "endTime": 1735889400000 }
|
||||
]
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
const result = parser({ content })
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
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":[]}'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -13,9 +13,9 @@ module.exports = {
|
|||
site: 'dens.tv',
|
||||
days: 2,
|
||||
url({ channel, date }) {
|
||||
return `https://www.dens.tv/api/dens3/tv/TvChannels/listEpgByDate?date=${date.format('YYYY-MM-DD')}&id_channel=${
|
||||
channel.site_id
|
||||
}&app_type=10`
|
||||
return `https://www.dens.tv/api/dens3/tv/TvChannels/listEpgByDate?date=${date.format(
|
||||
'YYYY-MM-DD'
|
||||
)}&id_channel=${channel.site_id}&app_type=10`
|
||||
},
|
||||
parser({ content }) {
|
||||
// parsing
|
||||
|
@ -25,8 +25,9 @@ module.exports = {
|
|||
if (Array.isArray(response?.data)) {
|
||||
response.data.forEach(item => {
|
||||
const title = item.title
|
||||
const [, , , season, , , episode] = title.match(/( (Season |Season|S)(\d+))?( (Episode|Ep) (\d+))/) ||
|
||||
[null, null, null, null, null, null, null]
|
||||
const [, , , season, , , episode] = title.match(
|
||||
/( (Season |Season|S)(\d+))?( (Episode|Ep) (\d+))/
|
||||
) || [null, null, null, null, null, null, null]
|
||||
programs.push({
|
||||
title,
|
||||
description: item.description,
|
||||
|
@ -52,7 +53,7 @@ module.exports = {
|
|||
const channels = []
|
||||
for (const id_category of Object.values(categories)) {
|
||||
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 }
|
||||
})
|
||||
.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' }
|
||||
|
||||
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', () => {
|
||||
|
|
|
@ -65,7 +65,7 @@ module.exports = {
|
|||
const cheerio = require('cheerio')
|
||||
|
||||
const data = await axios
|
||||
.get(`https://www.digiturk.com.tr/`, {
|
||||
.get('https://www.digiturk.com.tr/', {
|
||||
headers: {
|
||||
'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'
|
||||
|
|
|
@ -60,7 +60,7 @@ module.exports = {
|
|||
$('.pgrid').each((i, el) => {
|
||||
const onclick = $(el).find('.chnl-logo').attr('onclick')
|
||||
const number = $(el).find('.cnl-fav > a > span').text().trim()
|
||||
const [, name, site_id] = onclick.match(/ShowChannelGuid\('([^']+)','([^']+)'/) || [
|
||||
const [, , site_id] = onclick.match(/ShowChannelGuid\('([^']+)','([^']+)'/) || [
|
||||
null,
|
||||
'',
|
||||
''
|
||||
|
|
|
@ -49,7 +49,7 @@ module.exports = {
|
|||
const channels = []
|
||||
for (let provider of providers) {
|
||||
const data = await axios
|
||||
.post(`https://www.guida.tv/guide/schedule`, null, {
|
||||
.post('https://www.guida.tv/guide/schedule', null, {
|
||||
params: {
|
||||
provider,
|
||||
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 dateString = `${date.format('YYYY-MM-DD')} ${timeString}`
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ module.exports = {
|
|||
const cheerio = require('cheerio')
|
||||
|
||||
const data = await axios
|
||||
.get(`https://guidatv.sky.it/canali`)
|
||||
.get('https://guidatv.sky.it/canali')
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
|
|
|
@ -1,63 +1,63 @@
|
|||
const axios = require('axios')
|
||||
const convert = require('xml-js')
|
||||
const dayjs = require('dayjs')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
|
||||
dayjs.extend(timezone)
|
||||
|
||||
module.exports = {
|
||||
site: 'hoy.tv',
|
||||
days: 2,
|
||||
request: {
|
||||
cache: {
|
||||
ttl: 60 * 60 * 1000 // 1h
|
||||
}
|
||||
},
|
||||
url: function ({ channel, date }) {
|
||||
return `https://epg-file.hoy.tv/hoy/OTT${channel.site_id}${date.format('YYYYMMDD')}.xml`
|
||||
},
|
||||
parser({ content, channel, date }) {
|
||||
const data = convert.xml2js(content, {
|
||||
compact: true,
|
||||
ignoreDeclaration: true,
|
||||
ignoreAttributes: true
|
||||
})
|
||||
|
||||
const programs = []
|
||||
|
||||
for (let item of data.ProgramGuide.Channel.EpgItem) {
|
||||
const start = dayjs.tz(item.EpgStartDateTime._text, 'YYYY-MM-DD HH:mm:ss', 'Asia/Hong_Kong')
|
||||
|
||||
if (! date.isSame(start, 'day')) {
|
||||
continue
|
||||
}
|
||||
|
||||
const epIndex = item.EpisodeInfo.EpisodeIndex._text
|
||||
const subtitle = parseInt(epIndex) > 0 ? `第${epIndex}集` : undefined
|
||||
|
||||
programs.push({
|
||||
title: `${item.ComScore.ns_st_pr._text}${item.EpgOtherInfo?._text || ''}`,
|
||||
sub_title: subtitle,
|
||||
description: item.EpisodeInfo.EpisodeLongDescription._text,
|
||||
start,
|
||||
stop: dayjs.tz(item.EpgEndDateTime._text, 'YYYY-MM-DD HH:mm:ss', 'Asia/Hong_Kong'),
|
||||
})
|
||||
}
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels({ lang }) {
|
||||
const data = await axios
|
||||
.get('https://api2.hoy.tv/api/v2/a/channel')
|
||||
.then(r => r.data)
|
||||
.catch(console.error)
|
||||
|
||||
return data.data.map(c => {
|
||||
return {
|
||||
site_id: c.videos.id,
|
||||
name: c.name.zh_hk,
|
||||
lang: 'zh',
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const axios = require('axios')
|
||||
const convert = require('xml-js')
|
||||
const dayjs = require('dayjs')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
|
||||
dayjs.extend(timezone)
|
||||
|
||||
module.exports = {
|
||||
site: 'hoy.tv',
|
||||
days: 2,
|
||||
request: {
|
||||
cache: {
|
||||
ttl: 60 * 60 * 1000 // 1h
|
||||
}
|
||||
},
|
||||
url: function ({ channel, date }) {
|
||||
return `https://epg-file.hoy.tv/hoy/OTT${channel.site_id}${date.format('YYYYMMDD')}.xml`
|
||||
},
|
||||
parser({ content, date }) {
|
||||
const data = convert.xml2js(content, {
|
||||
compact: true,
|
||||
ignoreDeclaration: true,
|
||||
ignoreAttributes: true
|
||||
})
|
||||
|
||||
const programs = []
|
||||
|
||||
for (let item of data.ProgramGuide.Channel.EpgItem) {
|
||||
const start = dayjs.tz(item.EpgStartDateTime._text, 'YYYY-MM-DD HH:mm:ss', 'Asia/Hong_Kong')
|
||||
|
||||
if (!date.isSame(start, 'day')) {
|
||||
continue
|
||||
}
|
||||
|
||||
const epIndex = item.EpisodeInfo.EpisodeIndex._text
|
||||
const subtitle = parseInt(epIndex) > 0 ? `第${epIndex}集` : undefined
|
||||
|
||||
programs.push({
|
||||
title: `${item.ComScore.ns_st_pr._text}${item.EpgOtherInfo?._text || ''}`,
|
||||
sub_title: subtitle,
|
||||
description: item.EpisodeInfo.EpisodeLongDescription._text,
|
||||
start,
|
||||
stop: dayjs.tz(item.EpgEndDateTime._text, 'YYYY-MM-DD HH:mm:ss', 'Asia/Hong_Kong')
|
||||
})
|
||||
}
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const data = await axios
|
||||
.get('https://api2.hoy.tv/api/v2/a/channel')
|
||||
.then(r => r.data)
|
||||
.catch(console.error)
|
||||
|
||||
return data.data.map(c => {
|
||||
return {
|
||||
site_id: c.videos.id,
|
||||
name: c.name.zh_hk,
|
||||
lang: 'zh'
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,116 +1,115 @@
|
|||
const { parser, url } = require('./hoy.tv.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
jest.mock('axios')
|
||||
|
||||
const date = dayjs.utc('2024-09-13', 'YYYY-MM-DD').startOf('d')
|
||||
const channel = {
|
||||
site_id: '76',
|
||||
xmltv_id: 'HOYIBC.hk',
|
||||
lang: 'zh'
|
||||
}
|
||||
const content = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<ProgramGuide>
|
||||
<Channel id="76">
|
||||
<EpgItem>
|
||||
<EpgStartDateTime>2024-09-13 11:30:00</EpgStartDateTime>
|
||||
<EpgEndDateTime>2024-09-13 12:30:00</EpgEndDateTime>
|
||||
<EpgOtherInfo>[PG]</EpgOtherInfo>
|
||||
<DisableLive>false</DisableLive>
|
||||
<DisableVod>false</DisableVod>
|
||||
<VODLicPeriod>2024-09-27 11:30:00</VODLicPeriod>
|
||||
<ProgramInfo>
|
||||
<ProgramId>0</ProgramId>
|
||||
<ProgramTitle></ProgramTitle>
|
||||
<ProgramPos>0</ProgramPos>
|
||||
<FirstRunDateTime></FirstRunDateTime>
|
||||
<ProgramThumbnailUrl>http://tv.fantv.hk/images/thumbnail_1920_1080_fantv.jpg</ProgramThumbnailUrl>
|
||||
</ProgramInfo>
|
||||
<EpisodeInfo>
|
||||
<EpisodeId>EQ00135</EpisodeId>
|
||||
<EpisodeIndex>46</EpisodeIndex>
|
||||
<EpisodeShortDescription>點講都係一家人</EpisodeShortDescription>
|
||||
<EpisodeLongDescription></EpisodeLongDescription>
|
||||
<EpisodeThumbnailUrl>http://tv.fantv.hk/images/nosuchthumbnail.jpg</EpisodeThumbnailUrl>
|
||||
</EpisodeInfo>
|
||||
<ComScore>
|
||||
<ns_st_stc></ns_st_stc>
|
||||
<ns_st_pr>點講都係一家人</ns_st_pr>
|
||||
<ns_st_tpr>0</ns_st_tpr>
|
||||
<ns_st_tep>EQ00135</ns_st_tep>
|
||||
<ns_st_ep>點講都係一家人 Episode 46</ns_st_ep>
|
||||
<ns_st_li>1</ns_st_li>
|
||||
<ns_st_tdt>20240913</ns_st_tdt>
|
||||
<ns_st_tm>1130</ns_st_tm>
|
||||
<ns_st_ty>0001</ns_st_ty>
|
||||
<ns_st_cl>3704000</ns_st_cl>
|
||||
</ComScore>
|
||||
</EpgItem>
|
||||
<EpgItem>
|
||||
<EpgStartDateTime>2024-09-13 12:30:00</EpgStartDateTime>
|
||||
<EpgEndDateTime>2024-09-13 13:30:00</EpgEndDateTime>
|
||||
<EpgOtherInfo></EpgOtherInfo>
|
||||
<DisableLive>false</DisableLive>
|
||||
<DisableVod>false</DisableVod>
|
||||
<VODLicPeriod>2024-09-27 12:30:00</VODLicPeriod>
|
||||
<ProgramInfo>
|
||||
<ProgramId>0</ProgramId>
|
||||
<ProgramTitle></ProgramTitle>
|
||||
<ProgramPos>0</ProgramPos>
|
||||
<FirstRunDateTime></FirstRunDateTime>
|
||||
<ProgramThumbnailUrl>http://tv.fantv.hk/images/thumbnail_1920_1080_fantv.jpg</ProgramThumbnailUrl>
|
||||
</ProgramInfo>
|
||||
<EpisodeInfo>
|
||||
<EpisodeId>ED00311</EpisodeId>
|
||||
<EpisodeIndex>0</EpisodeIndex>
|
||||
<EpisodeShortDescription>麝香之路</EpisodeShortDescription>
|
||||
<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>
|
||||
</EpisodeInfo>
|
||||
<ComScore>
|
||||
<ns_st_stc></ns_st_stc>
|
||||
<ns_st_pr>麝香之路</ns_st_pr>
|
||||
<ns_st_tpr>0</ns_st_tpr>
|
||||
<ns_st_tep>ED00311</ns_st_tep>
|
||||
<ns_st_ep>麝香之路 2024-09-13</ns_st_ep>
|
||||
<ns_st_li>1</ns_st_li>
|
||||
<ns_st_tdt>20240913</ns_st_tdt>
|
||||
<ns_st_tm>1230</ns_st_tm>
|
||||
<ns_st_ty>0001</ns_st_ty>
|
||||
<ns_st_cl>3704000</ns_st_cl>
|
||||
</ComScore>
|
||||
</EpgItem>
|
||||
</Channel>
|
||||
</ProgramGuide>`
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ channel, date })).toBe(
|
||||
'https://epg-file.hoy.tv/hoy/OTT7620240913.xml'
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const result = parser({ content, channel, date }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
start: '2024-09-13T03:30:00.000Z',
|
||||
stop: '2024-09-13T04:30:00.000Z',
|
||||
title: '點講都係一家人[PG]',
|
||||
sub_title: '第46集',
|
||||
},
|
||||
{
|
||||
start: '2024-09-13T04:30:00.000Z',
|
||||
stop: '2024-09-13T05:30:00.000Z',
|
||||
title: '麝香之路',
|
||||
description: 'Ep. 2 .The Secret of disappeared kingdom.shows the mysterious disappearance of the ancient Tibetan kingdom which gained world',
|
||||
}
|
||||
])
|
||||
})
|
||||
const { parser, url } = require('./hoy.tv.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
jest.mock('axios')
|
||||
|
||||
const date = dayjs.utc('2024-09-13', 'YYYY-MM-DD').startOf('d')
|
||||
const channel = {
|
||||
site_id: '76',
|
||||
xmltv_id: 'HOYIBC.hk',
|
||||
lang: 'zh'
|
||||
}
|
||||
const content = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<ProgramGuide>
|
||||
<Channel id="76">
|
||||
<EpgItem>
|
||||
<EpgStartDateTime>2024-09-13 11:30:00</EpgStartDateTime>
|
||||
<EpgEndDateTime>2024-09-13 12:30:00</EpgEndDateTime>
|
||||
<EpgOtherInfo>[PG]</EpgOtherInfo>
|
||||
<DisableLive>false</DisableLive>
|
||||
<DisableVod>false</DisableVod>
|
||||
<VODLicPeriod>2024-09-27 11:30:00</VODLicPeriod>
|
||||
<ProgramInfo>
|
||||
<ProgramId>0</ProgramId>
|
||||
<ProgramTitle></ProgramTitle>
|
||||
<ProgramPos>0</ProgramPos>
|
||||
<FirstRunDateTime></FirstRunDateTime>
|
||||
<ProgramThumbnailUrl>http://tv.fantv.hk/images/thumbnail_1920_1080_fantv.jpg</ProgramThumbnailUrl>
|
||||
</ProgramInfo>
|
||||
<EpisodeInfo>
|
||||
<EpisodeId>EQ00135</EpisodeId>
|
||||
<EpisodeIndex>46</EpisodeIndex>
|
||||
<EpisodeShortDescription>點講都係一家人</EpisodeShortDescription>
|
||||
<EpisodeLongDescription></EpisodeLongDescription>
|
||||
<EpisodeThumbnailUrl>http://tv.fantv.hk/images/nosuchthumbnail.jpg</EpisodeThumbnailUrl>
|
||||
</EpisodeInfo>
|
||||
<ComScore>
|
||||
<ns_st_stc></ns_st_stc>
|
||||
<ns_st_pr>點講都係一家人</ns_st_pr>
|
||||
<ns_st_tpr>0</ns_st_tpr>
|
||||
<ns_st_tep>EQ00135</ns_st_tep>
|
||||
<ns_st_ep>點講都係一家人 Episode 46</ns_st_ep>
|
||||
<ns_st_li>1</ns_st_li>
|
||||
<ns_st_tdt>20240913</ns_st_tdt>
|
||||
<ns_st_tm>1130</ns_st_tm>
|
||||
<ns_st_ty>0001</ns_st_ty>
|
||||
<ns_st_cl>3704000</ns_st_cl>
|
||||
</ComScore>
|
||||
</EpgItem>
|
||||
<EpgItem>
|
||||
<EpgStartDateTime>2024-09-13 12:30:00</EpgStartDateTime>
|
||||
<EpgEndDateTime>2024-09-13 13:30:00</EpgEndDateTime>
|
||||
<EpgOtherInfo></EpgOtherInfo>
|
||||
<DisableLive>false</DisableLive>
|
||||
<DisableVod>false</DisableVod>
|
||||
<VODLicPeriod>2024-09-27 12:30:00</VODLicPeriod>
|
||||
<ProgramInfo>
|
||||
<ProgramId>0</ProgramId>
|
||||
<ProgramTitle></ProgramTitle>
|
||||
<ProgramPos>0</ProgramPos>
|
||||
<FirstRunDateTime></FirstRunDateTime>
|
||||
<ProgramThumbnailUrl>http://tv.fantv.hk/images/thumbnail_1920_1080_fantv.jpg</ProgramThumbnailUrl>
|
||||
</ProgramInfo>
|
||||
<EpisodeInfo>
|
||||
<EpisodeId>ED00311</EpisodeId>
|
||||
<EpisodeIndex>0</EpisodeIndex>
|
||||
<EpisodeShortDescription>麝香之路</EpisodeShortDescription>
|
||||
<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>
|
||||
</EpisodeInfo>
|
||||
<ComScore>
|
||||
<ns_st_stc></ns_st_stc>
|
||||
<ns_st_pr>麝香之路</ns_st_pr>
|
||||
<ns_st_tpr>0</ns_st_tpr>
|
||||
<ns_st_tep>ED00311</ns_st_tep>
|
||||
<ns_st_ep>麝香之路 2024-09-13</ns_st_ep>
|
||||
<ns_st_li>1</ns_st_li>
|
||||
<ns_st_tdt>20240913</ns_st_tdt>
|
||||
<ns_st_tm>1230</ns_st_tm>
|
||||
<ns_st_ty>0001</ns_st_ty>
|
||||
<ns_st_cl>3704000</ns_st_cl>
|
||||
</ComScore>
|
||||
</EpgItem>
|
||||
</Channel>
|
||||
</ProgramGuide>`
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ channel, date })).toBe('https://epg-file.hoy.tv/hoy/OTT7620240913.xml')
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const result = parser({ content, channel, date }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
start: '2024-09-13T03:30:00.000Z',
|
||||
stop: '2024-09-13T04:30:00.000Z',
|
||||
title: '點講都係一家人[PG]',
|
||||
sub_title: '第46集'
|
||||
},
|
||||
{
|
||||
start: '2024-09-13T04:30:00.000Z',
|
||||
stop: '2024-09-13T05:30:00.000Z',
|
||||
title: '麝香之路',
|
||||
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 []
|
||||
|
||||
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 => {
|
||||
if (Array.isArray(p.date) && p.date.length) {
|
||||
p.date = p.date[0]
|
||||
}
|
||||
return p
|
||||
})
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,73 +1,80 @@
|
|||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
module.exports = {
|
||||
site: 'ipko.tv',
|
||||
timezone: 'Europe/Belgrade',
|
||||
days: 5,
|
||||
url({ date, channel }) { return 'https://stargate.ipko.tv/api/titan.tv.WebEpg/GetWebEpgData' },
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Host': 'stargate.ipko.tv',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'nl,en-US;q=0.7,en;q=0.3',
|
||||
'Content-Type': 'application/json',
|
||||
'X-AppLayout': '1',
|
||||
'x-language': 'sq',
|
||||
'Origin': 'https://ipko.tv',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'cross-site',
|
||||
'Sec-GPC': '1',
|
||||
'Connection': 'keep-alive'
|
||||
},
|
||||
data({ channel, date }) {
|
||||
const todayEpoch = date.startOf('day').unix();
|
||||
const nextDayEpoch = date.add(1, 'day').startOf('day').unix();
|
||||
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 => {
|
||||
const start = dayjs.unix(show.show_start).utc();
|
||||
const stop = dayjs.unix(show.show_end).utc();
|
||||
const programData = {
|
||||
title: show.title,
|
||||
description: show.summary || 'No description available',
|
||||
start: start.toISOString(),
|
||||
stop: stop.toISOString(),
|
||||
thumbnail: show.thumbnail
|
||||
}
|
||||
programs.push(programData)
|
||||
})
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const response = await axios.post('https://stargate.ipko.tv/api/titan.tv.WebEpg/ZapList', JSON.stringify({ includeRadioStations: true }), {
|
||||
headers: this.request.headers
|
||||
});
|
||||
|
||||
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)
|
||||
}))
|
||||
}
|
||||
}
|
||||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
module.exports = {
|
||||
site: 'ipko.tv',
|
||||
timezone: 'Europe/Belgrade',
|
||||
days: 5,
|
||||
url() {
|
||||
return 'https://stargate.ipko.tv/api/titan.tv.WebEpg/GetWebEpgData'
|
||||
},
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Host: 'stargate.ipko.tv',
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'nl,en-US;q=0.7,en;q=0.3',
|
||||
'Content-Type': 'application/json',
|
||||
'X-AppLayout': '1',
|
||||
'x-language': 'sq',
|
||||
Origin: 'https://ipko.tv',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'cross-site',
|
||||
'Sec-GPC': '1',
|
||||
Connection: 'keep-alive'
|
||||
},
|
||||
data({ channel, date }) {
|
||||
const todayEpoch = date.startOf('day').unix()
|
||||
const nextDayEpoch = date.add(1, 'day').startOf('day').unix()
|
||||
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 => {
|
||||
const start = dayjs.unix(show.show_start).utc()
|
||||
const stop = dayjs.unix(show.show_end).utc()
|
||||
const programData = {
|
||||
title: show.title,
|
||||
description: show.summary || 'No description available',
|
||||
start: start.toISOString(),
|
||||
stop: stop.toISOString(),
|
||||
thumbnail: show.thumbnail
|
||||
}
|
||||
programs.push(programData)
|
||||
})
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const response = await axios.post(
|
||||
'https://stargate.ipko.tv/api/titan.tv.WebEpg/ZapList',
|
||||
JSON.stringify({ includeRadioStations: true }),
|
||||
{
|
||||
headers: this.request.headers
|
||||
}
|
||||
)
|
||||
|
||||
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 dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2024-12-24', 'YYYY-MM-DD').startOf('day')
|
||||
const channel = {
|
||||
site_id: 'ipko-promo',
|
||||
xmltv_id: 'IPKOPROMO'
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ date, channel })).toBe('https://stargate.ipko.tv/api/titan.tv.WebEpg/GetWebEpgData')
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = `
|
||||
{
|
||||
"shows": [
|
||||
{
|
||||
"title": "IPKO Promo",
|
||||
"show_start": 1735012800,
|
||||
"show_end": 1735020000,
|
||||
"timestamp": "5:00 - 7:00",
|
||||
"show_id": "EPG_TvProfil_IPKOPROMO_296105567",
|
||||
"thumbnail": "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg",
|
||||
"is_adult": false,
|
||||
"friendly_id": "ipko_promo_4cf3",
|
||||
"pg": "",
|
||||
"genres": [],
|
||||
"year": 0,
|
||||
"summary": "",
|
||||
"categories": "Other",
|
||||
"stb_only": false,
|
||||
"is_live": false,
|
||||
"original_title": "IPKO Promo"
|
||||
},
|
||||
{
|
||||
"title": "IPKO Promo",
|
||||
"show_start": 1735020000,
|
||||
"show_end": 1735027200,
|
||||
"timestamp": "7:00 - 9:00",
|
||||
"show_id": "EPG_TvProfil_IPKOPROMO_296105568",
|
||||
"thumbnail": "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg",
|
||||
"is_adult": false,
|
||||
"friendly_id": "ipko_promo_416b",
|
||||
"pg": "",
|
||||
"genres": [],
|
||||
"year": 0,
|
||||
"summary": "",
|
||||
"categories": "Other",
|
||||
"stb_only": false,
|
||||
"is_live": false,
|
||||
"original_title": "IPKO Promo"
|
||||
},
|
||||
{
|
||||
"title": "IPKO Promo",
|
||||
"show_start": 1735027200,
|
||||
"show_end": 1735034400,
|
||||
"timestamp": "9:00 - 11:00",
|
||||
"show_id": "EPG_TvProfil_IPKOPROMO_296105569",
|
||||
"thumbnail": "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg",
|
||||
"is_adult": false,
|
||||
"friendly_id": "ipko_promo_2e23",
|
||||
"pg": "",
|
||||
"genres": [],
|
||||
"year": 0,
|
||||
"summary": "",
|
||||
"categories": "Other",
|
||||
"stb_only": false,
|
||||
"is_live": false,
|
||||
"original_title": "IPKO Promo"
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
const result = parser({ content, channel }).map(p => {
|
||||
p.start = p.start
|
||||
p.stop = p.stop
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: "IPKO Promo",
|
||||
description: "No description available",
|
||||
start: "2024-12-24T04:00:00.000Z",
|
||||
stop: "2024-12-24T06:00:00.000Z",
|
||||
thumbnail: "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg"
|
||||
},
|
||||
{
|
||||
title: "IPKO Promo",
|
||||
description: "No description available",
|
||||
start: "2024-12-24T06:00:00.000Z",
|
||||
stop: "2024-12-24T08:00:00.000Z",
|
||||
thumbnail: "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg"
|
||||
},
|
||||
{
|
||||
title: "IPKO Promo",
|
||||
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":[]}'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
const { parser, url } = require('./ipko.tv.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2024-12-24', 'YYYY-MM-DD').startOf('day')
|
||||
const channel = {
|
||||
site_id: 'ipko-promo',
|
||||
xmltv_id: 'IPKOPROMO'
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ date, channel })).toBe('https://stargate.ipko.tv/api/titan.tv.WebEpg/GetWebEpgData')
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = `
|
||||
{
|
||||
"shows": [
|
||||
{
|
||||
"title": "IPKO Promo",
|
||||
"show_start": 1735012800,
|
||||
"show_end": 1735020000,
|
||||
"timestamp": "5:00 - 7:00",
|
||||
"show_id": "EPG_TvProfil_IPKOPROMO_296105567",
|
||||
"thumbnail": "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg",
|
||||
"is_adult": false,
|
||||
"friendly_id": "ipko_promo_4cf3",
|
||||
"pg": "",
|
||||
"genres": [],
|
||||
"year": 0,
|
||||
"summary": "",
|
||||
"categories": "Other",
|
||||
"stb_only": false,
|
||||
"is_live": false,
|
||||
"original_title": "IPKO Promo"
|
||||
},
|
||||
{
|
||||
"title": "IPKO Promo",
|
||||
"show_start": 1735020000,
|
||||
"show_end": 1735027200,
|
||||
"timestamp": "7:00 - 9:00",
|
||||
"show_id": "EPG_TvProfil_IPKOPROMO_296105568",
|
||||
"thumbnail": "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg",
|
||||
"is_adult": false,
|
||||
"friendly_id": "ipko_promo_416b",
|
||||
"pg": "",
|
||||
"genres": [],
|
||||
"year": 0,
|
||||
"summary": "",
|
||||
"categories": "Other",
|
||||
"stb_only": false,
|
||||
"is_live": false,
|
||||
"original_title": "IPKO Promo"
|
||||
},
|
||||
{
|
||||
"title": "IPKO Promo",
|
||||
"show_start": 1735027200,
|
||||
"show_end": 1735034400,
|
||||
"timestamp": "9:00 - 11:00",
|
||||
"show_id": "EPG_TvProfil_IPKOPROMO_296105569",
|
||||
"thumbnail": "https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg",
|
||||
"is_adult": false,
|
||||
"friendly_id": "ipko_promo_2e23",
|
||||
"pg": "",
|
||||
"genres": [],
|
||||
"year": 0,
|
||||
"summary": "",
|
||||
"categories": "Other",
|
||||
"stb_only": false,
|
||||
"is_live": false,
|
||||
"original_title": "IPKO Promo"
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
const result = parser({ content, channel })
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: 'IPKO Promo',
|
||||
description: 'No description available',
|
||||
start: '2024-12-24T04:00:00.000Z',
|
||||
stop: '2024-12-24T06:00:00.000Z',
|
||||
thumbnail: 'https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg'
|
||||
},
|
||||
{
|
||||
title: 'IPKO Promo',
|
||||
description: 'No description available',
|
||||
start: '2024-12-24T06:00:00.000Z',
|
||||
stop: '2024-12-24T08:00:00.000Z',
|
||||
thumbnail: 'https://vimg.ipko.tv/mtcms/18/2/1/1821cc68-a9bf-4733-b1af-9a5d80163b78.jpg'
|
||||
},
|
||||
{
|
||||
title: 'IPKO Promo',
|
||||
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":[]}'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -38,7 +38,7 @@ module.exports = {
|
|||
async channels() {
|
||||
const axios = require('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)
|
||||
.catch(console.log)
|
||||
|
||||
|
|
|
@ -77,8 +77,8 @@ function parseItems(content) {
|
|||
let data
|
||||
try {
|
||||
data = JSON.parse(content)
|
||||
} catch (error) {
|
||||
console.log(error.message)
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
if (!data || !Array.isArray(data)) return []
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ module.exports = {
|
|||
async channels() {
|
||||
const axios = require('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)
|
||||
.catch(console.log)
|
||||
|
||||
|
|
|
@ -56,7 +56,7 @@ function parseStop($item) {
|
|||
|
||||
try {
|
||||
return dayjs(timeString, 'YYYY-MM-DD HH:mm:ssZZ')
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,93 +1,97 @@
|
|||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(timezone)
|
||||
|
||||
module.exports = {
|
||||
site: 'mediasetinfinity.mediaset.it',
|
||||
days: 2,
|
||||
url: function ({date, channel}) {
|
||||
// Get the epoch timestamp
|
||||
const todayEpoch = date.startOf('day').utc().valueOf()
|
||||
// Get the epoch timestamp for the next day
|
||||
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}`
|
||||
},
|
||||
parser: function ({content}) {
|
||||
const programs = []
|
||||
const data = JSON.parse(content)
|
||||
|
||||
if (!data.response || !data.response.entries || !data.response.entries[0] || !data.response.entries[0].listings) {
|
||||
// If the structure is not as expected, return an empty array
|
||||
return programs
|
||||
}
|
||||
|
||||
const listings = data.response.entries[0].listings
|
||||
|
||||
listings.forEach((listing) => {
|
||||
const title = listing.mediasetlisting$epgTitle
|
||||
const subTitle = listing.program.title
|
||||
const season = parseSeason(listing)
|
||||
const episode = parseEpisode(listing)
|
||||
|
||||
|
||||
if (listing.program.title && listing.startTime && listing.endTime) {
|
||||
programs.push({
|
||||
title: title || subTitle,
|
||||
sub_title: title && title != subTitle ? subTitle : null,
|
||||
description: listing.program.description || null,
|
||||
category: listing.program.mediasetprogram$skyGenre || null,
|
||||
season: episode && !season ? '0' : season,
|
||||
episode: episode,
|
||||
start: parseTime(listing.startTime),
|
||||
stop: parseTime(listing.endTime),
|
||||
image: getMaxResolutionThumbnails(listing)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return programs
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function parseTime(timestamp) {
|
||||
return dayjs(timestamp).utc().format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
||||
function parseSeason(item) {
|
||||
if (!item.mediasetlisting$shortDescription) return null
|
||||
const season = item.mediasetlisting$shortDescription.match(/S(\d+)\s/)
|
||||
return season ? season[1] : null
|
||||
}
|
||||
|
||||
function parseEpisode(item) {
|
||||
if (!item.mediasetlisting$shortDescription) return null
|
||||
const episode = item.mediasetlisting$shortDescription.match(/Ep(\d+)\s/)
|
||||
return episode ? episode[1] : null
|
||||
}
|
||||
|
||||
function getMaxResolutionThumbnails(item) {
|
||||
const thumbnails = item.program.thumbnails || null
|
||||
const maxResolutionThumbnails = {}
|
||||
|
||||
for (const key in thumbnails) {
|
||||
const type = key.split('-')[0] // Estrarre il tipo di thumbnail
|
||||
const {width, height, url, title} = thumbnails[key]
|
||||
|
||||
if (!maxResolutionThumbnails[type] ||
|
||||
(width * height > maxResolutionThumbnails[type].width * maxResolutionThumbnails[type].height)) {
|
||||
maxResolutionThumbnails[type] = {width, height, url, title}
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(timezone)
|
||||
|
||||
module.exports = {
|
||||
site: 'mediasetinfinity.mediaset.it',
|
||||
days: 2,
|
||||
url: function ({ date, channel }) {
|
||||
// Get the epoch timestamp
|
||||
const todayEpoch = date.startOf('day').utc().valueOf()
|
||||
// Get the epoch timestamp for the next day
|
||||
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}`
|
||||
},
|
||||
parser: function ({ content }) {
|
||||
const programs = []
|
||||
const data = JSON.parse(content)
|
||||
|
||||
if (
|
||||
!data.response ||
|
||||
!data.response.entries ||
|
||||
!data.response.entries[0] ||
|
||||
!data.response.entries[0].listings
|
||||
) {
|
||||
// If the structure is not as expected, return an empty array
|
||||
return programs
|
||||
}
|
||||
|
||||
const listings = data.response.entries[0].listings
|
||||
|
||||
listings.forEach(listing => {
|
||||
const title = listing.mediasetlisting$epgTitle
|
||||
const subTitle = listing.program.title
|
||||
const season = parseSeason(listing)
|
||||
const episode = parseEpisode(listing)
|
||||
|
||||
if (listing.program.title && listing.startTime && listing.endTime) {
|
||||
programs.push({
|
||||
title: title || subTitle,
|
||||
sub_title: title && title != subTitle ? subTitle : null,
|
||||
description: listing.program.description || null,
|
||||
category: listing.program.mediasetprogram$skyGenre || null,
|
||||
season: episode && !season ? '0' : season,
|
||||
episode: episode,
|
||||
start: parseTime(listing.startTime),
|
||||
stop: parseTime(listing.endTime),
|
||||
image: getMaxResolutionThumbnails(listing)
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return programs
|
||||
}
|
||||
}
|
||||
|
||||
function parseTime(timestamp) {
|
||||
return dayjs(timestamp).utc().format('YYYY-MM-DD HH:mm')
|
||||
}
|
||||
|
||||
function parseSeason(item) {
|
||||
if (!item.mediasetlisting$shortDescription) return null
|
||||
const season = item.mediasetlisting$shortDescription.match(/S(\d+)\s/)
|
||||
return season ? season[1] : null
|
||||
}
|
||||
|
||||
function parseEpisode(item) {
|
||||
if (!item.mediasetlisting$shortDescription) return null
|
||||
const episode = item.mediasetlisting$shortDescription.match(/Ep(\d+)\s/)
|
||||
return episode ? episode[1] : null
|
||||
}
|
||||
|
||||
function getMaxResolutionThumbnails(item) {
|
||||
const thumbnails = item.program.thumbnails || null
|
||||
const maxResolutionThumbnails = {}
|
||||
|
||||
for (const key in thumbnails) {
|
||||
const type = key.split('-')[0] // Estrarre il tipo di thumbnail
|
||||
const { width, height, url, title } = thumbnails[key]
|
||||
|
||||
if (
|
||||
!maxResolutionThumbnails[type] ||
|
||||
width * height > maxResolutionThumbnails[type].width * maxResolutionThumbnails[type].height
|
||||
) {
|
||||
maxResolutionThumbnails[type] = { width, height, url, title }
|
||||
}
|
||||
}
|
||||
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 fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2024-01-20', 'YYYY-MM-DD').startOf('d')
|
||||
const channel = {
|
||||
site_id: 'LB', xmltv_id: '20.it'
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({
|
||||
channel,
|
||||
date
|
||||
})).toBe('https://api-ott-prod-fe.mediaset.net/PROD/play/feed/allListingFeedEpg/v2.0?byListingTime=1705708800000~1705795200000&byCallSign=LB')
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
||||
const results = parser({content, date}).map(p => {
|
||||
return p
|
||||
})
|
||||
|
||||
expect(results[3]).toMatchObject({
|
||||
start: '2024-01-20 02:14',
|
||||
stop: '2024-01-20 02:54',
|
||||
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.',
|
||||
category: 'Intrattenimento',
|
||||
season: '7',
|
||||
episode: '22',
|
||||
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'
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({
|
||||
content: '[]'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
const { parser, url } = require('./mediasetinfinity.mediaset.it.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2024-01-20', 'YYYY-MM-DD').startOf('d')
|
||||
const channel = {
|
||||
site_id: 'LB',
|
||||
xmltv_id: '20.it'
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(
|
||||
url({
|
||||
channel,
|
||||
date
|
||||
})
|
||||
).toBe(
|
||||
'https://api-ott-prod-fe.mediaset.net/PROD/play/feed/allListingFeedEpg/v2.0?byListingTime=1705708800000~1705795200000&byCallSign=LB'
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8')
|
||||
const results = parser({ content, date }).map(p => {
|
||||
return p
|
||||
})
|
||||
|
||||
expect(results[3]).toMatchObject({
|
||||
start: '2024-01-20 02:14',
|
||||
stop: '2024-01-20 02:54',
|
||||
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.',
|
||||
category: 'Intrattenimento',
|
||||
season: '7',
|
||||
episode: '22',
|
||||
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'
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({
|
||||
content: '[]'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -40,7 +40,7 @@ module.exports = {
|
|||
async channels() {
|
||||
const axios = require('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: {
|
||||
Origin: 'https://www.meo.pt'
|
||||
}
|
||||
|
|
|
@ -1,101 +1,105 @@
|
|||
const cheerio = require('cheerio')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
module.exports = {
|
||||
site: 'meuguia.tv',
|
||||
days: 2,
|
||||
url({ channel }) {
|
||||
return `https://meuguia.tv/programacao/canal/${channel.site_id}`
|
||||
},
|
||||
parser({ content, date }) {
|
||||
const programs = []
|
||||
parseItems(content, date).forEach(item => {
|
||||
if (dayjs.utc(item.start).isSame(date, 'day')) {
|
||||
programs.push(item)
|
||||
}
|
||||
})
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const channels = []
|
||||
const axios = require('axios')
|
||||
const baseUrl = 'https://meuguia.tv'
|
||||
|
||||
let seq = 0
|
||||
const queues = [baseUrl]
|
||||
while (true) {
|
||||
if (!queues.length) {
|
||||
break
|
||||
}
|
||||
const url = queues.shift()
|
||||
const content = await axios
|
||||
.get(url)
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
if (content) {
|
||||
const [ $, items ] = getItems(content)
|
||||
if (seq === 0) {
|
||||
queues.push(...items.map(category => baseUrl + $(category).attr('href')))
|
||||
} else {
|
||||
items.forEach(item => {
|
||||
const href = $(item).attr('href')
|
||||
channels.push({
|
||||
lang: 'pt',
|
||||
site_id: href.substr(href.lastIndexOf('/') + 1),
|
||||
name: $(item).find('.licontent h2').text().trim()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
seq++
|
||||
}
|
||||
|
||||
return channels
|
||||
}
|
||||
}
|
||||
|
||||
function getItems(content) {
|
||||
const $ = cheerio.load(content)
|
||||
return [$, $('div.mw ul li a').toArray()]
|
||||
}
|
||||
|
||||
function parseItems(content, date) {
|
||||
const result = []
|
||||
const $ = cheerio.load(content)
|
||||
|
||||
let lastDate
|
||||
for (const item of $('ul.mw li').toArray()) {
|
||||
const $item = $(item)
|
||||
if ($item.hasClass('subheader')) {
|
||||
lastDate = `${$item.text().split(', ')[1]}/${date.format('YYYY')}`
|
||||
} else if ($item.hasClass('divider')) {
|
||||
// ignore
|
||||
} else if (lastDate) {
|
||||
const data = { title: $item.find('a').attr('title').trim() }
|
||||
const ep = data.title.match(/T(\d+) EP(\d+)/)
|
||||
if (ep) {
|
||||
data.season = parseInt(ep[1])
|
||||
data.episode = parseInt(ep[2])
|
||||
}
|
||||
data.start = dayjs.tz(`${lastDate} ${$item.find('.time').text()}`, 'DD/MM/YYYY HH:mm', 'America/Sao_Paulo')
|
||||
result.push(data)
|
||||
}
|
||||
}
|
||||
// use stop time from next item
|
||||
if (result.length > 1) {
|
||||
for (let i = 0; i < result.length - 1; i++) {
|
||||
result[i].stop = result[i + 1].start
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
const cheerio = require('cheerio')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
module.exports = {
|
||||
site: 'meuguia.tv',
|
||||
days: 2,
|
||||
url({ channel }) {
|
||||
return `https://meuguia.tv/programacao/canal/${channel.site_id}`
|
||||
},
|
||||
parser({ content, date }) {
|
||||
const programs = []
|
||||
parseItems(content, date).forEach(item => {
|
||||
if (dayjs.utc(item.start).isSame(date, 'day')) {
|
||||
programs.push(item)
|
||||
}
|
||||
})
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const channels = []
|
||||
const axios = require('axios')
|
||||
const baseUrl = 'https://meuguia.tv'
|
||||
|
||||
let seq = 0
|
||||
const queues = [baseUrl]
|
||||
while (true) {
|
||||
if (!queues.length) {
|
||||
break
|
||||
}
|
||||
const url = queues.shift()
|
||||
const content = await axios
|
||||
.get(url)
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
if (content) {
|
||||
const [$, items] = getItems(content)
|
||||
if (seq === 0) {
|
||||
queues.push(...items.map(category => baseUrl + $(category).attr('href')))
|
||||
} else {
|
||||
items.forEach(item => {
|
||||
const href = $(item).attr('href')
|
||||
channels.push({
|
||||
lang: 'pt',
|
||||
site_id: href.substr(href.lastIndexOf('/') + 1),
|
||||
name: $(item).find('.licontent h2').text().trim()
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
seq++
|
||||
}
|
||||
|
||||
return channels
|
||||
}
|
||||
}
|
||||
|
||||
function getItems(content) {
|
||||
const $ = cheerio.load(content)
|
||||
return [$, $('div.mw ul li a').toArray()]
|
||||
}
|
||||
|
||||
function parseItems(content, date) {
|
||||
const result = []
|
||||
const $ = cheerio.load(content)
|
||||
|
||||
let lastDate
|
||||
for (const item of $('ul.mw li').toArray()) {
|
||||
const $item = $(item)
|
||||
if ($item.hasClass('subheader')) {
|
||||
lastDate = `${$item.text().split(', ')[1]}/${date.format('YYYY')}`
|
||||
} else if ($item.hasClass('divider')) {
|
||||
// ignore
|
||||
} else if (lastDate) {
|
||||
const data = { title: $item.find('a').attr('title').trim() }
|
||||
const ep = data.title.match(/T(\d+) EP(\d+)/)
|
||||
if (ep) {
|
||||
data.season = parseInt(ep[1])
|
||||
data.episode = parseInt(ep[2])
|
||||
}
|
||||
data.start = dayjs.tz(
|
||||
`${lastDate} ${$item.find('.time').text()}`,
|
||||
'DD/MM/YYYY HH:mm',
|
||||
'America/Sao_Paulo'
|
||||
)
|
||||
result.push(data)
|
||||
}
|
||||
}
|
||||
// use stop time from next item
|
||||
if (result.length > 1) {
|
||||
for (let i = 0; i < result.length - 1; i++) {
|
||||
result[i].stop = result[i + 1].start
|
||||
}
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
|
|
@ -1,60 +1,60 @@
|
|||
const { parser, url } = require('./meuguia.tv.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2023-11-21').startOf('d')
|
||||
const channel = {
|
||||
site_id: 'AXN',
|
||||
xmltv_id: 'AXN.id'
|
||||
}
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ channel })).toBe('https://meuguia.tv/programacao/canal/AXN')
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||
const result = parser({ content, channel, date }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
if (p.stop) {
|
||||
p.stop = p.stop.toJSON()
|
||||
}
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: 'Hawaii Five-0 : T10 EP4 - Tiny Is the Flower, Yet It Scents the Grasses Around It',
|
||||
start: '2023-11-21T21:20:00.000Z',
|
||||
stop: '2023-11-21T22:15:00.000Z',
|
||||
season: 10,
|
||||
episode: 4
|
||||
},
|
||||
{
|
||||
title:
|
||||
"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',
|
||||
stop: '2023-11-21T23:10:00.000Z',
|
||||
season: 10,
|
||||
episode: 5
|
||||
},
|
||||
{
|
||||
title: 'NCIS : T5 EP15 - In the Zone',
|
||||
start: '2023-11-21T23:10:00.000Z',
|
||||
season: 5,
|
||||
episode: 15
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({
|
||||
date,
|
||||
channel,
|
||||
content: '<!DOCTYPE html><html><head></head><body></body></html>'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
const { parser, url } = require('./meuguia.tv.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2023-11-21').startOf('d')
|
||||
const channel = {
|
||||
site_id: 'AXN',
|
||||
xmltv_id: 'AXN.id'
|
||||
}
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ channel })).toBe('https://meuguia.tv/programacao/canal/AXN')
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||
const result = parser({ content, channel, date }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
if (p.stop) {
|
||||
p.stop = p.stop.toJSON()
|
||||
}
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: 'Hawaii Five-0 : T10 EP4 - Tiny Is the Flower, Yet It Scents the Grasses Around It',
|
||||
start: '2023-11-21T21:20:00.000Z',
|
||||
stop: '2023-11-21T22:15:00.000Z',
|
||||
season: 10,
|
||||
episode: 4
|
||||
},
|
||||
{
|
||||
title:
|
||||
"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',
|
||||
stop: '2023-11-21T23:10:00.000Z',
|
||||
season: 10,
|
||||
episode: 5
|
||||
},
|
||||
{
|
||||
title: 'NCIS : T5 EP15 - In the Zone',
|
||||
start: '2023-11-21T23:10:00.000Z',
|
||||
season: 5,
|
||||
episode: 15
|
||||
}
|
||||
])
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({
|
||||
date,
|
||||
channel,
|
||||
content: '<!DOCTYPE html><html><head></head><body></body></html>'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -42,7 +42,7 @@ module.exports = {
|
|||
const axios = require('axios')
|
||||
const cheerio = require('cheerio')
|
||||
const data = await axios
|
||||
.get(`https://www.mewatch.sg/channel-guide`)
|
||||
.get('https://www.mewatch.sg/channel-guide')
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
|
||||
|
|
|
@ -11,9 +11,7 @@ dayjs.extend(utc)
|
|||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
doFetch
|
||||
.setCheckResult(false)
|
||||
.setDebugger(debug)
|
||||
doFetch.setCheckResult(false).setDebugger(debug)
|
||||
|
||||
const languages = { en: 'english', id: 'indonesia' }
|
||||
const cookies = {}
|
||||
|
@ -125,7 +123,7 @@ async function parseItems(content, date, cookies) {
|
|||
const url = $item.find('a').attr('href')
|
||||
const headers = {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
Cookie: cookies,
|
||||
Cookie: cookies
|
||||
}
|
||||
queues.push({ i: $item, url, params: { headers, timeout } })
|
||||
}
|
||||
|
|
|
@ -48,8 +48,10 @@ function parseItems(context) {
|
|||
schDayPrograms.forEach((program, i) => {
|
||||
const itemDay = {
|
||||
progStart: parseStart($(schDayMonth), $(program)),
|
||||
progStop: parseStop($(schDayMonth), schDayPrograms[i + 1] ?
|
||||
$(schDayPrograms[i + 1]) : null),
|
||||
progStop: parseStop(
|
||||
$(schDayMonth),
|
||||
schDayPrograms[i + 1] ? $(schDayPrograms[i + 1]) : null
|
||||
),
|
||||
progTitle: parseTitle($(program)),
|
||||
progDesc: parseDescription($(program))
|
||||
}
|
||||
|
@ -91,7 +93,9 @@ function parseStop(schDayMonth, itemNext) {
|
|||
)
|
||||
} else {
|
||||
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',
|
||||
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())
|
||||
for (let page of pages) {
|
||||
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') },
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
|
@ -65,7 +65,7 @@ module.exports = {
|
|||
|
||||
async function getTotalPageCount() {
|
||||
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') },
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
|
|
|
@ -43,7 +43,7 @@ module.exports = {
|
|||
const pages = Array.from(Array(totalPages).keys())
|
||||
for (let page of pages) {
|
||||
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') },
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
|
@ -67,7 +67,7 @@ module.exports = {
|
|||
|
||||
async function getTotalPageCount() {
|
||||
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') },
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest'
|
||||
|
@ -84,8 +84,8 @@ function parseContent(content, channel) {
|
|||
let data
|
||||
try {
|
||||
data = JSON.parse(content)
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
if (!data || !data.channels || !data.channels.length) return null
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ module.exports = {
|
|||
|
||||
const data = await axios
|
||||
.post(
|
||||
`https://services.mujtvprogram.cz/tvprogram2services/services/tvchannellist_mobile.php`,
|
||||
'https://services.mujtvprogram.cz/tvprogram2services/services/tvchannellist_mobile.php',
|
||||
params,
|
||||
{
|
||||
headers: {
|
||||
|
@ -86,7 +86,7 @@ function parseItems(content) {
|
|||
if (!data) return []
|
||||
const programmes = data['tv-program-programmes'].programme
|
||||
return programmes && Array.isArray(programmes) ? programmes : []
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ dayjs.extend(customParseFormat)
|
|||
|
||||
const headers = {
|
||||
'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 = {
|
||||
|
|
|
@ -26,12 +26,11 @@ it('can generate valid url for today', () => {
|
|||
|
||||
it('can parse response', () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.html'))
|
||||
const results = parser({ content, date })
|
||||
.map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
const results = parser({ content, date }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(results[0]).toMatchObject({
|
||||
start: '2022-11-19T23:00:00.000Z',
|
||||
|
|
|
@ -23,14 +23,15 @@ module.exports = {
|
|||
channel.site_id
|
||||
}.html?dt=${date.format('YYYY-MM-DD')}`
|
||||
},
|
||||
async parser({ content, date, channel }) {
|
||||
async parser({ content, date }) {
|
||||
const programs = []
|
||||
|
||||
if (content) {
|
||||
const queues = []
|
||||
const $ = cheerio.load(content)
|
||||
|
||||
$('table.table > tbody > tr').toArray()
|
||||
$('table.table > tbody > tr')
|
||||
.toArray()
|
||||
.forEach(el => {
|
||||
const td = $(el).find('td:eq(1)')
|
||||
const title = td.find('h5 a')
|
||||
|
@ -66,12 +67,16 @@ module.exports = {
|
|||
const subTitle = parseText($('.tab-pane > h5 > strong'))
|
||||
const description = parseText($('.tab-pane > .tvbody > p'))
|
||||
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())
|
||||
const casts = $('.single-cast-head:not([id])').toArray()
|
||||
const casts = $('.single-cast-head:not([id])')
|
||||
.toArray()
|
||||
.map(el => {
|
||||
const cast = { name: parseText($(el).find('a')) }
|
||||
const [, role] = $(el).text().match(/\((.*)\)/) || [null, null]
|
||||
const [, role] = $(el)
|
||||
.text()
|
||||
.match(/\((.*)\)/) || [null, null]
|
||||
if (role) {
|
||||
cast.role = role
|
||||
}
|
||||
|
@ -102,7 +107,7 @@ module.exports = {
|
|||
start,
|
||||
stop
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -115,11 +120,17 @@ module.exports = {
|
|||
// process form -> provider
|
||||
if (queue.t === 'p') {
|
||||
const $ = cheerio.load(res)
|
||||
$('#guide_provider option').toArray()
|
||||
$('#guide_provider option')
|
||||
.toArray()
|
||||
.forEach(el => {
|
||||
const opt = $(el)
|
||||
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
|
||||
|
@ -135,26 +146,30 @@ module.exports = {
|
|||
u_time: now.format('HHmm'),
|
||||
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
|
||||
if (queue.t === 's') {
|
||||
const $ = cheerio.load(res)
|
||||
$('.channelname')
|
||||
.each((i, el) => {
|
||||
const name = $(el).find('center > a:eq(1)').text()
|
||||
const url = $(el).find('center > a:eq(1)').attr('href')
|
||||
const [, number, slug] = url.match(/\/(\d+)\/(.*)\.html$/)
|
||||
const site_id = `${number}/${slug}`
|
||||
if (channels[site_id] === undefined) {
|
||||
channels[site_id] = {
|
||||
lang: 'en',
|
||||
site_id,
|
||||
name
|
||||
}
|
||||
$('.channelname').each((i, el) => {
|
||||
const name = $(el).find('center > a:eq(1)').text()
|
||||
const url = $(el).find('center > a:eq(1)').attr('href')
|
||||
const [, number, slug] = url.match(/\/(\d+)\/(.*)\.html$/)
|
||||
const site_id = `${number}/${slug}`
|
||||
if (channels[site_id] === undefined) {
|
||||
channels[site_id] = {
|
||||
lang: 'en',
|
||||
site_id,
|
||||
name
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -178,13 +193,10 @@ function parseTime(date, time) {
|
|||
}
|
||||
|
||||
function parseText($item) {
|
||||
let text = $item.text()
|
||||
.replace(/\t/g, '')
|
||||
.replace(/\n/g, ' ')
|
||||
.trim()
|
||||
let text = $item.text().replace(/\t/g, '').replace(/\n/g, ' ').trim()
|
||||
while (true) {
|
||||
if (text.match(/ /)) {
|
||||
text = text.replace(/ /g, ' ')
|
||||
if (text.match(/\s\s/)) {
|
||||
text = text.replace(/\s\s/g, ' ')
|
||||
continue
|
||||
}
|
||||
break
|
||||
|
|
|
@ -17,16 +17,18 @@ const channel = {
|
|||
xmltv_id: 'BBCOneLondon.uk'
|
||||
}
|
||||
|
||||
axios.get.mockImplementation((url, opts) => {
|
||||
axios.get.mockImplementation(url => {
|
||||
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({
|
||||
data: fs.readFileSync(path.join(__dirname, '__data__', 'programme.html'))
|
||||
})
|
||||
}
|
||||
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({
|
||||
data: fs.readFileSync(path.join(__dirname, '__data__', 'programme2.html'))
|
||||
|
@ -57,7 +59,8 @@ it('can parse response', async () => {
|
|||
title: 'Captain Phillips',
|
||||
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',
|
||||
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']
|
||||
})
|
||||
expect(results[1]).toMatchObject({
|
||||
|
@ -67,7 +70,8 @@ it('can parse response', async () => {
|
|||
subTitle: 'Past and Pressure Season 6, Episode 5',
|
||||
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',
|
||||
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'],
|
||||
season: 6,
|
||||
episode: 5
|
||||
|
|
|
@ -1,73 +1,80 @@
|
|||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
module.exports = {
|
||||
site: 'neo.io',
|
||||
timezone: 'Europe/Ljubljana',
|
||||
days: 5,
|
||||
url({ date, channel }) { return 'https://stargate.telekom.si/api/titan.tv.WebEpg/GetWebEpgData' },
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Host': 'stargate.telekom.si',
|
||||
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
|
||||
'Accept': 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'nl,en-US;q=0.7,en;q=0.3',
|
||||
'Content-Type': 'application/json',
|
||||
'X-AppLayout': '1',
|
||||
'x-language': 'sl',
|
||||
'Origin': 'https://neo.io',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'cross-site',
|
||||
'Sec-GPC': '1',
|
||||
'Connection': 'keep-alive'
|
||||
},
|
||||
data({ channel, date }) {
|
||||
const todayEpoch = date.startOf('day').unix();
|
||||
const nextDayEpoch = date.add(1, 'day').startOf('day').unix();
|
||||
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 => {
|
||||
const start = dayjs.unix(show.show_start).utc();
|
||||
const stop = dayjs.unix(show.show_end).utc();
|
||||
const programData = {
|
||||
title: show.title,
|
||||
description: show.summary || 'No description available',
|
||||
start: start.toISOString(),
|
||||
stop: stop.toISOString(),
|
||||
thumbnail: show.thumbnail
|
||||
}
|
||||
programs.push(programData)
|
||||
})
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const response = await axios.post('https://stargate.telekom.si/api/titan.tv.WebEpg/ZapList', JSON.stringify({ includeRadioStations: true }), {
|
||||
headers: this.request.headers
|
||||
});
|
||||
|
||||
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)
|
||||
}))
|
||||
}
|
||||
}
|
||||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(customParseFormat)
|
||||
|
||||
module.exports = {
|
||||
site: 'neo.io',
|
||||
timezone: 'Europe/Ljubljana',
|
||||
days: 5,
|
||||
url() {
|
||||
return 'https://stargate.telekom.si/api/titan.tv.WebEpg/GetWebEpgData'
|
||||
},
|
||||
request: {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Host: 'stargate.telekom.si',
|
||||
'User-Agent':
|
||||
'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0',
|
||||
Accept: 'application/json, text/plain, */*',
|
||||
'Accept-Language': 'nl,en-US;q=0.7,en;q=0.3',
|
||||
'Content-Type': 'application/json',
|
||||
'X-AppLayout': '1',
|
||||
'x-language': 'sl',
|
||||
Origin: 'https://neo.io',
|
||||
'Sec-Fetch-Dest': 'empty',
|
||||
'Sec-Fetch-Mode': 'cors',
|
||||
'Sec-Fetch-Site': 'cross-site',
|
||||
'Sec-GPC': '1',
|
||||
Connection: 'keep-alive'
|
||||
},
|
||||
data({ channel, date }) {
|
||||
const todayEpoch = date.startOf('day').unix()
|
||||
const nextDayEpoch = date.add(1, 'day').startOf('day').unix()
|
||||
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 => {
|
||||
const start = dayjs.unix(show.show_start).utc()
|
||||
const stop = dayjs.unix(show.show_end).utc()
|
||||
const programData = {
|
||||
title: show.title,
|
||||
description: show.summary || 'No description available',
|
||||
start: start.toISOString(),
|
||||
stop: stop.toISOString(),
|
||||
thumbnail: show.thumbnail
|
||||
}
|
||||
programs.push(programData)
|
||||
})
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const response = await axios.post(
|
||||
'https://stargate.telekom.si/api/titan.tv.WebEpg/ZapList',
|
||||
JSON.stringify({ includeRadioStations: true }),
|
||||
{
|
||||
headers: this.request.headers
|
||||
}
|
||||
)
|
||||
|
||||
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 dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2024-12-26', 'YYYY-MM-DD').startOf('day')
|
||||
const channel = {
|
||||
site_id: 'tv-slo-1',
|
||||
xmltv_id: 'TVSLO1.si'
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ date, channel })).toBe('https://stargate.telekom.si/api/titan.tv.WebEpg/GetWebEpgData')
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = `
|
||||
{
|
||||
"shows": [
|
||||
{
|
||||
"title": "Napovedujemo",
|
||||
"show_start": 1735185900,
|
||||
"show_end": 1735192200,
|
||||
"timestamp": "5:05 - 6:50",
|
||||
"show_id": "CUP_IECOM_SLO1_10004660",
|
||||
"thumbnail": "https://ngimg.siol.tv/sioltv/mtcmsprod/52/0/0/5200d01a-fe5f-487e-835a-274e77227a6b.jpg",
|
||||
"is_adult": false,
|
||||
"friendly_id": "napovedujemo_db48",
|
||||
"pg": "",
|
||||
"genres": [
|
||||
"napovednik"
|
||||
],
|
||||
"year": 0,
|
||||
"summary": "Vabilo k ogledu naših oddaj.",
|
||||
"categories": "Ostalo",
|
||||
"stb_only": false,
|
||||
"is_live": false,
|
||||
"original_title": "Napovedujemo"
|
||||
},
|
||||
{
|
||||
"title": "S0E0 - Hrabri zajčki: Prvi sneg",
|
||||
"show_start": 1735192200,
|
||||
"show_end": 1735192800,
|
||||
"timestamp": "6:50 - 7:00",
|
||||
"show_id": "CUP_IECOM_SLO1_79637910",
|
||||
"thumbnail": "https://ngimg.siol.tv/sioltv/mtcmsprod/d6/4/5/d6456f4a-4f0a-4825-90c1-1749abd59688.jpg",
|
||||
"is_adult": false,
|
||||
"friendly_id": "hrabri_zajcki_prvi_sneg_1619",
|
||||
"pg": "",
|
||||
"genres": [
|
||||
"risanka"
|
||||
],
|
||||
"year": 2020,
|
||||
"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",
|
||||
"stb_only": false,
|
||||
"is_live": false,
|
||||
"original_title": "S0E0 - Brave Bunnies"
|
||||
},
|
||||
{
|
||||
"title": "Dobro jutro",
|
||||
"show_start": 1735192800,
|
||||
"show_end": 1735203900,
|
||||
"timestamp": "7:00 - 10:05",
|
||||
"show_id": "CUP_IECOM_SLO1_79637911",
|
||||
"thumbnail": "https://ngimg.siol.tv/sioltv/mtcmsprod/e1/2/d/e12d8eb4-693a-43d3-89d4-fd96dade9f0f.jpg",
|
||||
"is_adult": false,
|
||||
"friendly_id": "dobro_jutro_2f10",
|
||||
"pg": "",
|
||||
"genres": [
|
||||
"zabavna oddaja"
|
||||
],
|
||||
"year": 2024,
|
||||
"summary": "Oddaja Dobro jutro poleg informativnih in zabavnih vsebin podaja koristne nasvete o najrazličnejših tematikah iz vsakdanjega življenja.",
|
||||
"categories": "Razvedrilni program",
|
||||
"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
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: "Napovedujemo",
|
||||
description: "Vabilo k ogledu naših oddaj.",
|
||||
start: "2024-12-26T04:05:00.000Z",
|
||||
stop: "2024-12-26T05:50:00.000Z",
|
||||
thumbnail: "https://ngimg.siol.tv/sioltv/mtcmsprod/52/0/0/5200d01a-fe5f-487e-835a-274e77227a6b.jpg"
|
||||
},
|
||||
{
|
||||
title: "S0E0 - Hrabri zajčki: Prvi sneg",
|
||||
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.",
|
||||
start: "2024-12-26T05:50: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"
|
||||
},
|
||||
{
|
||||
title: "Dobro jutro",
|
||||
description: "Oddaja Dobro jutro poleg informativnih in zabavnih vsebin podaja koristne nasvete o najrazličnejših tematikah iz vsakdanjega življenja.",
|
||||
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":[]}'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
const { parser, url } = require('./neo.io.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2024-12-26', 'YYYY-MM-DD').startOf('day')
|
||||
const channel = {
|
||||
site_id: 'tv-slo-1',
|
||||
xmltv_id: 'TVSLO1.si'
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ date, channel })).toBe(
|
||||
'https://stargate.telekom.si/api/titan.tv.WebEpg/GetWebEpgData'
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = `
|
||||
{
|
||||
"shows": [
|
||||
{
|
||||
"title": "Napovedujemo",
|
||||
"show_start": 1735185900,
|
||||
"show_end": 1735192200,
|
||||
"timestamp": "5:05 - 6:50",
|
||||
"show_id": "CUP_IECOM_SLO1_10004660",
|
||||
"thumbnail": "https://ngimg.siol.tv/sioltv/mtcmsprod/52/0/0/5200d01a-fe5f-487e-835a-274e77227a6b.jpg",
|
||||
"is_adult": false,
|
||||
"friendly_id": "napovedujemo_db48",
|
||||
"pg": "",
|
||||
"genres": [
|
||||
"napovednik"
|
||||
],
|
||||
"year": 0,
|
||||
"summary": "Vabilo k ogledu naših oddaj.",
|
||||
"categories": "Ostalo",
|
||||
"stb_only": false,
|
||||
"is_live": false,
|
||||
"original_title": "Napovedujemo"
|
||||
},
|
||||
{
|
||||
"title": "S0E0 - Hrabri zajčki: Prvi sneg",
|
||||
"show_start": 1735192200,
|
||||
"show_end": 1735192800,
|
||||
"timestamp": "6:50 - 7:00",
|
||||
"show_id": "CUP_IECOM_SLO1_79637910",
|
||||
"thumbnail": "https://ngimg.siol.tv/sioltv/mtcmsprod/d6/4/5/d6456f4a-4f0a-4825-90c1-1749abd59688.jpg",
|
||||
"is_adult": false,
|
||||
"friendly_id": "hrabri_zajcki_prvi_sneg_1619",
|
||||
"pg": "",
|
||||
"genres": [
|
||||
"risanka"
|
||||
],
|
||||
"year": 2020,
|
||||
"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",
|
||||
"stb_only": false,
|
||||
"is_live": false,
|
||||
"original_title": "S0E0 - Brave Bunnies"
|
||||
},
|
||||
{
|
||||
"title": "Dobro jutro",
|
||||
"show_start": 1735192800,
|
||||
"show_end": 1735203900,
|
||||
"timestamp": "7:00 - 10:05",
|
||||
"show_id": "CUP_IECOM_SLO1_79637911",
|
||||
"thumbnail": "https://ngimg.siol.tv/sioltv/mtcmsprod/e1/2/d/e12d8eb4-693a-43d3-89d4-fd96dade9f0f.jpg",
|
||||
"is_adult": false,
|
||||
"friendly_id": "dobro_jutro_2f10",
|
||||
"pg": "",
|
||||
"genres": [
|
||||
"zabavna oddaja"
|
||||
],
|
||||
"year": 2024,
|
||||
"summary": "Oddaja Dobro jutro poleg informativnih in zabavnih vsebin podaja koristne nasvete o najrazličnejših tematikah iz vsakdanjega življenja.",
|
||||
"categories": "Razvedrilni program",
|
||||
"stb_only": false,
|
||||
"is_live": false,
|
||||
"original_title": "Dobro jutro"
|
||||
}
|
||||
]
|
||||
}`
|
||||
|
||||
const result = parser({ content, channel })
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: 'Napovedujemo',
|
||||
description: 'Vabilo k ogledu naših oddaj.',
|
||||
start: '2024-12-26T04:05:00.000Z',
|
||||
stop: '2024-12-26T05:50:00.000Z',
|
||||
thumbnail:
|
||||
'https://ngimg.siol.tv/sioltv/mtcmsprod/52/0/0/5200d01a-fe5f-487e-835a-274e77227a6b.jpg'
|
||||
},
|
||||
{
|
||||
title: 'S0E0 - Hrabri zajčki: Prvi sneg',
|
||||
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.',
|
||||
start: '2024-12-26T05:50: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'
|
||||
},
|
||||
{
|
||||
title: 'Dobro jutro',
|
||||
description:
|
||||
'Oddaja Dobro jutro poleg informativnih in zabavnih vsebin podaja koristne nasvete o najrazličnejših tematikah iz vsakdanjega življenja.',
|
||||
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":[]}'
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -50,7 +50,7 @@ function parseItems(content, date) {
|
|||
if (!data || !data.item || !Array.isArray(data.item.episodes)) return []
|
||||
|
||||
return data.item.episodes.filter(ep => ep.schedule.startsWith(date.format('YYYY-MM-DD')))
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,45 +1,46 @@
|
|||
const dayjs = require('dayjs')
|
||||
|
||||
module.exports = {
|
||||
site: 'nhl.com',
|
||||
// I'm not sure what `endDate` represents but they only return 1 day of
|
||||
// results, with `endTime`s ocassionally in the following day.
|
||||
days: 1,
|
||||
url: ({ date }) => `https://api-web.nhle.com/v1/network/tv-schedule/${date.toJSON().split("T")[0]}`,
|
||||
parser({ content }) {
|
||||
const programs = []
|
||||
const items = parseItems(content)
|
||||
for (const item of items) {
|
||||
programs.push({
|
||||
title: item.title,
|
||||
description: item.description === item.title ? undefined : item.description,
|
||||
category: "Sports",
|
||||
// image: parseImage(item),
|
||||
start: parseStart(item),
|
||||
stop: parseStop(item)
|
||||
})
|
||||
}
|
||||
|
||||
return programs
|
||||
}
|
||||
}
|
||||
|
||||
// Unfortunately I couldn't determine how these are
|
||||
// supposed to be formatted. Pointers appreciated!
|
||||
// function parseImage(item) {
|
||||
// const uri = item.broadcastImageUrl
|
||||
|
||||
// return uri ? `https://???/${uri}` : null
|
||||
// }
|
||||
|
||||
function parseStart(item) {
|
||||
return dayjs(item.startTime)
|
||||
}
|
||||
|
||||
function parseStop(item) {
|
||||
return dayjs(item.endTime)
|
||||
}
|
||||
|
||||
function parseItems(content) {
|
||||
return JSON.parse(content).broadcasts
|
||||
}
|
||||
const dayjs = require('dayjs')
|
||||
|
||||
module.exports = {
|
||||
site: 'nhl.com',
|
||||
// I'm not sure what `endDate` represents but they only return 1 day of
|
||||
// results, with `endTime`s ocassionally in the following day.
|
||||
days: 1,
|
||||
url: ({ date }) =>
|
||||
`https://api-web.nhle.com/v1/network/tv-schedule/${date.toJSON().split('T')[0]}`,
|
||||
parser({ content }) {
|
||||
const programs = []
|
||||
const items = parseItems(content)
|
||||
for (const item of items) {
|
||||
programs.push({
|
||||
title: item.title,
|
||||
description: item.description === item.title ? undefined : item.description,
|
||||
category: 'Sports',
|
||||
// image: parseImage(item),
|
||||
start: parseStart(item),
|
||||
stop: parseStop(item)
|
||||
})
|
||||
}
|
||||
|
||||
return programs
|
||||
}
|
||||
}
|
||||
|
||||
// Unfortunately I couldn't determine how these are
|
||||
// supposed to be formatted. Pointers appreciated!
|
||||
// function parseImage(item) {
|
||||
// const uri = item.broadcastImageUrl
|
||||
|
||||
// return uri ? `https://???/${uri}` : null
|
||||
// }
|
||||
|
||||
function parseStart(item) {
|
||||
return dayjs(item.startTime)
|
||||
}
|
||||
|
||||
function parseStop(item) {
|
||||
return dayjs(item.endTime)
|
||||
}
|
||||
|
||||
function parseItems(content) {
|
||||
return JSON.parse(content).broadcasts
|
||||
}
|
||||
|
|
|
@ -1,44 +1,44 @@
|
|||
const { parser, url } = require('./nhl.com.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2024-11-21', 'YYYY-MM-DD').startOf('d')
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ date })).toBe(
|
||||
'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'))
|
||||
let results = parser({ content, date })
|
||||
results = results.map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(results[0]).toMatchObject({
|
||||
start: '2024-11-21T12:00:00.000Z',
|
||||
stop: '2024-11-21T13:00:00.000Z',
|
||||
title: 'On The Fly',
|
||||
category: 'Sports',
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const results = parser({ content: JSON.stringify({
|
||||
// extra props not necessary but they form a valid response
|
||||
date: "2024-11-21",
|
||||
startDate: "2024-11-07",
|
||||
endDate: "2024-12-05",
|
||||
broadcasts: [],
|
||||
}) })
|
||||
expect(results).toMatchObject([])
|
||||
})
|
||||
const { parser, url } = require('./nhl.com.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2024-11-21', 'YYYY-MM-DD').startOf('d')
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ date })).toBe('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'))
|
||||
let results = parser({ content, date })
|
||||
results = results.map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(results[0]).toMatchObject({
|
||||
start: '2024-11-21T12:00:00.000Z',
|
||||
stop: '2024-11-21T13:00:00.000Z',
|
||||
title: 'On The Fly',
|
||||
category: 'Sports'
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const results = parser({
|
||||
content: JSON.stringify({
|
||||
// extra props not necessary but they form a valid response
|
||||
date: '2024-11-21',
|
||||
startDate: '2024-11-07',
|
||||
endDate: '2024-12-05',
|
||||
broadcasts: []
|
||||
})
|
||||
})
|
||||
expect(results).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -1,68 +1,68 @@
|
|||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
const headers = {
|
||||
'X-Apikey': 'xe1dgrShwdR1DVOKGmsj8Ut4QLlGyOFI',
|
||||
'X-Core-Appversion': '2.14.0.1',
|
||||
'X-Core-Contentratinglimit': '0',
|
||||
'X-Core-Deviceid': '',
|
||||
'X-Core-Devicetype': 'web',
|
||||
Origin: 'https://nostv.pt',
|
||||
'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'
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
site: 'nostv.pt',
|
||||
days: 2,
|
||||
url({ channel, date }) {
|
||||
return `https://tyr-prod.apigee.net/nostv/ott/schedule/range/contents/guest?channels=${
|
||||
channel.site_id
|
||||
}&minDate=${date.format('YYYY-MM-DD')}T00:00:00Z&maxDate=${date.format(
|
||||
'YYYY-MM-DD'
|
||||
)}T23:59:59Z&isDateInclusive=true&client_id=${headers['X-Apikey']}`
|
||||
},
|
||||
request: { headers },
|
||||
parser({ content }) {
|
||||
const programs = []
|
||||
if (content) {
|
||||
const items = Array.isArray(content) ? content : JSON.parse(content)
|
||||
items.forEach(item => {
|
||||
programs.push({
|
||||
title: item.Metadata?.Title,
|
||||
sub_title: item.Metadata?.SubTitle ? item.Metadata?.SubTitle : null,
|
||||
description: item.Metadata?.Description,
|
||||
season: item.Metadata?.Season,
|
||||
episode: item.Metadata?.Episode,
|
||||
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']}`
|
||||
: null,
|
||||
start: dayjs.utc(item.UtcDateTimeStart),
|
||||
stop: dayjs.utc(item.UtcDateTimeEnd)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const result = await axios
|
||||
.get(
|
||||
`https://tyr-prod.apigee.net/nostv/ott/channels/guest?client_id=${headers['X-Apikey']}`,
|
||||
{ headers }
|
||||
)
|
||||
.then(r => r.data)
|
||||
.catch(console.error)
|
||||
|
||||
return result.map(item => {
|
||||
return {
|
||||
lang: 'pt',
|
||||
site_id: item.ServiceId,
|
||||
name: item.Name
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
const headers = {
|
||||
'X-Apikey': 'xe1dgrShwdR1DVOKGmsj8Ut4QLlGyOFI',
|
||||
'X-Core-Appversion': '2.14.0.1',
|
||||
'X-Core-Contentratinglimit': '0',
|
||||
'X-Core-Deviceid': '',
|
||||
'X-Core-Devicetype': 'web',
|
||||
Origin: 'https://nostv.pt',
|
||||
'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'
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
site: 'nostv.pt',
|
||||
days: 2,
|
||||
url({ channel, date }) {
|
||||
return `https://tyr-prod.apigee.net/nostv/ott/schedule/range/contents/guest?channels=${
|
||||
channel.site_id
|
||||
}&minDate=${date.format('YYYY-MM-DD')}T00:00:00Z&maxDate=${date.format(
|
||||
'YYYY-MM-DD'
|
||||
)}T23:59:59Z&isDateInclusive=true&client_id=${headers['X-Apikey']}`
|
||||
},
|
||||
request: { headers },
|
||||
parser({ content }) {
|
||||
const programs = []
|
||||
if (content) {
|
||||
const items = Array.isArray(content) ? content : JSON.parse(content)
|
||||
items.forEach(item => {
|
||||
programs.push({
|
||||
title: item.Metadata?.Title,
|
||||
sub_title: item.Metadata?.SubTitle ? item.Metadata?.SubTitle : null,
|
||||
description: item.Metadata?.Description,
|
||||
season: item.Metadata?.Season,
|
||||
episode: item.Metadata?.Episode,
|
||||
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']}`
|
||||
: null,
|
||||
start: dayjs.utc(item.UtcDateTimeStart),
|
||||
stop: dayjs.utc(item.UtcDateTimeEnd)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const result = await axios
|
||||
.get(
|
||||
`https://tyr-prod.apigee.net/nostv/ott/channels/guest?client_id=${headers['X-Apikey']}`,
|
||||
{ headers }
|
||||
)
|
||||
.then(r => r.data)
|
||||
.catch(console.error)
|
||||
|
||||
return result.map(item => {
|
||||
return {
|
||||
lang: 'pt',
|
||||
site_id: item.ServiceId,
|
||||
name: item.Name
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,51 +1,51 @@
|
|||
const { parser, url } = require('./nostv.pt.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2023-12-11').startOf('d')
|
||||
const channel = {
|
||||
site_id: '510',
|
||||
xmltv_id: 'SPlus.pt'
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
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'
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json'))
|
||||
const results = parser({ content }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(results[0]).toMatchObject({
|
||||
start: '2023-12-11T16:30:00.000Z',
|
||||
stop: '2023-12-11T17:00:00.000Z',
|
||||
title: 'Village Vets',
|
||||
description:
|
||||
'A história de dois melhores amigos veterinários e o seu extraordinário trabalho na Austrália.',
|
||||
season: 1,
|
||||
episode: 12,
|
||||
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'
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', async () => {
|
||||
const results = await parser({
|
||||
date,
|
||||
content: '[]'
|
||||
})
|
||||
|
||||
expect(results).toMatchObject([])
|
||||
})
|
||||
const { parser, url } = require('./nostv.pt.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2023-12-11').startOf('d')
|
||||
const channel = {
|
||||
site_id: '510',
|
||||
xmltv_id: 'SPlus.pt'
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
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'
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json'))
|
||||
const results = parser({ content }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(results[0]).toMatchObject({
|
||||
start: '2023-12-11T16:30:00.000Z',
|
||||
stop: '2023-12-11T17:00:00.000Z',
|
||||
title: 'Village Vets',
|
||||
description:
|
||||
'A história de dois melhores amigos veterinários e o seu extraordinário trabalho na Austrália.',
|
||||
season: 1,
|
||||
episode: 12,
|
||||
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'
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', async () => {
|
||||
const results = await parser({
|
||||
date,
|
||||
content: '[]'
|
||||
})
|
||||
|
||||
expect(results).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -55,7 +55,7 @@ module.exports = {
|
|||
.map(function () {
|
||||
return {
|
||||
lang: 'es',
|
||||
site_id: $(this).attr('alt').replace(/\&/gi, '&'),
|
||||
site_id: $(this).attr('alt').replace(/&/gi, '&'),
|
||||
name: $(this).attr('alt')
|
||||
}
|
||||
})
|
||||
|
|
|
@ -1,81 +1,81 @@
|
|||
const parser = require('epg-parser')
|
||||
|
||||
module.exports = {
|
||||
site: 'nzxmltv.com',
|
||||
days: 2,
|
||||
request: {
|
||||
cache: {
|
||||
ttl: 3600000 // 1 hour
|
||||
},
|
||||
maxContentLength: 104857600 // 100 MB
|
||||
},
|
||||
url({ channel }) {
|
||||
const [path] = channel.site_id.split('#')
|
||||
|
||||
return `https://nzxmltv.com/${path}.xml`
|
||||
},
|
||||
parser({ content, channel, date }) {
|
||||
const programs = []
|
||||
parseItems(content, channel, date).forEach(item => {
|
||||
const program = {
|
||||
title: item.title?.[0]?.value,
|
||||
description: item.desc?.[0]?.value,
|
||||
icon: item.icon?.[0],
|
||||
start: item.start,
|
||||
stop: item.stop
|
||||
}
|
||||
if (item.episodeNum) {
|
||||
item.episodeNum.forEach(ep => {
|
||||
if (ep.system === 'xmltv_ns') {
|
||||
const [season, episode, _] = ep.value.split('.')
|
||||
program.season = parseInt(season) + 1
|
||||
program.episode = parseInt(episode) + 1
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
programs.push(program)
|
||||
})
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels({ provider }) {
|
||||
const axios = require('axios')
|
||||
const cheerio = require('cheerio')
|
||||
|
||||
const providers = {
|
||||
freeview: 'xmltv/guide',
|
||||
sky: 'sky/guide',
|
||||
redbull: 'iptv/redbull',
|
||||
pluto: 'iptv/plutotv'
|
||||
}
|
||||
|
||||
const channels = []
|
||||
const path = providers[provider]
|
||||
const xml = await axios
|
||||
.get(`https://nzxmltv.com/${path}.xml`)
|
||||
.then(r => r.data)
|
||||
.catch(console.error)
|
||||
|
||||
const $ = cheerio.load(xml)
|
||||
$('tv channel').each((i, el) => {
|
||||
const disp = $(el).find('display-name')
|
||||
const channelId = $(el).attr('id')
|
||||
|
||||
channels.push({
|
||||
lang: disp.attr('lang').substr(0, 2),
|
||||
site_id: `${path}#${channelId}`,
|
||||
name: disp.text().trim()
|
||||
})
|
||||
})
|
||||
|
||||
return channels
|
||||
}
|
||||
}
|
||||
|
||||
function parseItems(content, channel, date) {
|
||||
const { programs } = parser.parse(content)
|
||||
const [, channelId] = channel.site_id.split('#')
|
||||
|
||||
return programs.filter(p => p.channel === channelId && date.isSame(p.start, 'day'))
|
||||
}
|
||||
const parser = require('epg-parser')
|
||||
|
||||
module.exports = {
|
||||
site: 'nzxmltv.com',
|
||||
days: 2,
|
||||
request: {
|
||||
cache: {
|
||||
ttl: 3600000 // 1 hour
|
||||
},
|
||||
maxContentLength: 104857600 // 100 MB
|
||||
},
|
||||
url({ channel }) {
|
||||
const [path] = channel.site_id.split('#')
|
||||
|
||||
return `https://nzxmltv.com/${path}.xml`
|
||||
},
|
||||
parser({ content, channel, date }) {
|
||||
const programs = []
|
||||
parseItems(content, channel, date).forEach(item => {
|
||||
const program = {
|
||||
title: item.title?.[0]?.value,
|
||||
description: item.desc?.[0]?.value,
|
||||
icon: item.icon?.[0],
|
||||
start: item.start,
|
||||
stop: item.stop
|
||||
}
|
||||
if (item.episodeNum) {
|
||||
item.episodeNum.forEach(ep => {
|
||||
if (ep.system === 'xmltv_ns') {
|
||||
const [season, episode] = ep.value.split('.')
|
||||
program.season = parseInt(season) + 1
|
||||
program.episode = parseInt(episode) + 1
|
||||
return true
|
||||
}
|
||||
})
|
||||
}
|
||||
programs.push(program)
|
||||
})
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels({ provider }) {
|
||||
const axios = require('axios')
|
||||
const cheerio = require('cheerio')
|
||||
|
||||
const providers = {
|
||||
freeview: 'xmltv/guide',
|
||||
sky: 'sky/guide',
|
||||
redbull: 'iptv/redbull',
|
||||
pluto: 'iptv/plutotv'
|
||||
}
|
||||
|
||||
const channels = []
|
||||
const path = providers[provider]
|
||||
const xml = await axios
|
||||
.get(`https://nzxmltv.com/${path}.xml`)
|
||||
.then(r => r.data)
|
||||
.catch(console.error)
|
||||
|
||||
const $ = cheerio.load(xml)
|
||||
$('tv channel').each((i, el) => {
|
||||
const disp = $(el).find('display-name')
|
||||
const channelId = $(el).attr('id')
|
||||
|
||||
channels.push({
|
||||
lang: disp.attr('lang').substr(0, 2),
|
||||
site_id: `${path}#${channelId}`,
|
||||
name: disp.text().trim()
|
||||
})
|
||||
})
|
||||
|
||||
return channels
|
||||
}
|
||||
}
|
||||
|
||||
function parseItems(content, channel, date) {
|
||||
const { programs } = parser.parse(content)
|
||||
const [, channelId] = channel.site_id.split('#')
|
||||
|
||||
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 fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2023-11-21').startOf('d')
|
||||
const channel = {
|
||||
site_id: 'xmltv/guide#1',
|
||||
xmltv_id: 'TVNZ1.nz'
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ channel })).toBe('https://nzxmltv.com/xmltv/guide.xml')
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.xml'))
|
||||
const results = parser({ content, channel, date })
|
||||
|
||||
expect(results[0]).toMatchObject({
|
||||
start: '2023-11-21T10:30:00.000Z',
|
||||
stop: '2023-11-21T11:25:00.000Z',
|
||||
title: 'Sunday',
|
||||
description:
|
||||
'On Sunday, an unmissable show with stories about divorce, weight loss, and the incomprehensible devastation of Gaza.',
|
||||
season: 2023,
|
||||
episode: 37,
|
||||
icon: 'https://www.thetvdb.com/banners/posters/5dbebff2986f2.jpg'
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({ content: '', channel, date })
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
const { parser, url } = require('./nzxmltv.com.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2023-11-21').startOf('d')
|
||||
const channel = {
|
||||
site_id: 'xmltv/guide#1',
|
||||
xmltv_id: 'TVNZ1.nz'
|
||||
}
|
||||
|
||||
it('can generate valid url', () => {
|
||||
expect(url({ channel })).toBe('https://nzxmltv.com/xmltv/guide.xml')
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.xml'))
|
||||
const results = parser({ content, channel, date })
|
||||
|
||||
expect(results[0]).toMatchObject({
|
||||
start: '2023-11-21T10:30:00.000Z',
|
||||
stop: '2023-11-21T11:25:00.000Z',
|
||||
title: 'Sunday',
|
||||
description:
|
||||
'On Sunday, an unmissable show with stories about divorce, weight loss, and the incomprehensible devastation of Gaza.',
|
||||
season: 2023,
|
||||
episode: 37,
|
||||
icon: 'https://www.thetvdb.com/banners/posters/5dbebff2986f2.jpg'
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({ content: '', channel, date })
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -132,7 +132,7 @@ module.exports = {
|
|||
const $ = cheerio.load(data)
|
||||
$('.channelname').each((i, el) => {
|
||||
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')
|
||||
if (!url) return
|
||||
const [, number, slug] = url.match(/\/(\d+)\/(.*)\.html$/)
|
||||
|
|
|
@ -1,113 +1,108 @@
|
|||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const axios = require('axios')
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
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_IMAGE_ENDPOINT = 'https://pc.orangetv.orange.es/pc/api/rtv/v1/images'
|
||||
|
||||
module.exports = {
|
||||
site: 'orangetv.orange.es',
|
||||
days: 2,
|
||||
request: {
|
||||
cache: {
|
||||
ttl: 60 * 60 * 1000 // 1 hour
|
||||
}
|
||||
},
|
||||
url({ date }) {
|
||||
return `${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_1.json`
|
||||
},
|
||||
async parser({ content, channel, date }) {
|
||||
let programs = []
|
||||
let items = parseItems(content, channel)
|
||||
if (!items.length) return programs
|
||||
|
||||
const promises = [
|
||||
axios.get(
|
||||
`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_1.json`,
|
||||
),
|
||||
axios.get(
|
||||
`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_2.json`,
|
||||
),
|
||||
axios.get(
|
||||
`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_3.json`,
|
||||
),
|
||||
]
|
||||
|
||||
await Promise.allSettled(promises)
|
||||
.then(results => {
|
||||
results.forEach(r => {
|
||||
if (r.status === 'fulfilled') {
|
||||
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 => {
|
||||
programs.push({
|
||||
title: item.name,
|
||||
description: item.description,
|
||||
category: parseGenres(item),
|
||||
season: item.seriesSeason || null,
|
||||
episode: item.episodeId || null,
|
||||
icon: parseIcon(item),
|
||||
start: dayjs.utc(item.startDate) || null,
|
||||
stop: dayjs.utc(item.endDate) || null,
|
||||
})
|
||||
})
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const axios = require('axios')
|
||||
const data = await axios
|
||||
.get(API_CHANNEL_ENDPOINT)
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
return data.response.map(item => {
|
||||
return {
|
||||
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")
|
||||
|
||||
if(cover)
|
||||
{
|
||||
return `${API_IMAGE_ENDPOINT}${cover.value}`;
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
function parseGenres(item){
|
||||
return item.genres.map(i => i.name);
|
||||
}
|
||||
|
||||
function parseItems(content, channel) {
|
||||
const json = typeof content === 'string' ? JSON.parse(content) : Array.isArray(content) ? content : []
|
||||
|
||||
if (!Array.isArray(json)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const channelData = json.find(i => i.channelExternalId == channel.site_id);
|
||||
|
||||
if(!channelData)
|
||||
return [];
|
||||
|
||||
return channelData.programs;
|
||||
}
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const axios = require('axios')
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
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_IMAGE_ENDPOINT = 'https://pc.orangetv.orange.es/pc/api/rtv/v1/images'
|
||||
|
||||
module.exports = {
|
||||
site: 'orangetv.orange.es',
|
||||
days: 2,
|
||||
request: {
|
||||
cache: {
|
||||
ttl: 60 * 60 * 1000 // 1 hour
|
||||
}
|
||||
},
|
||||
url({ date }) {
|
||||
return `${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_1.json`
|
||||
},
|
||||
async parser({ content, channel, date }) {
|
||||
let programs = []
|
||||
let items = parseItems(content, channel)
|
||||
if (!items.length) return programs
|
||||
|
||||
const promises = [
|
||||
axios.get(`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_1.json`),
|
||||
axios.get(`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_2.json`),
|
||||
axios.get(`${API_PROGRAM_ENDPOINT}/${date.format('YYYYMMDD')}_8h_3.json`)
|
||||
]
|
||||
|
||||
await Promise.allSettled(promises)
|
||||
.then(results => {
|
||||
results.forEach(r => {
|
||||
if (r.status === 'fulfilled') {
|
||||
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 => {
|
||||
programs.push({
|
||||
title: item.name,
|
||||
description: item.description,
|
||||
category: parseGenres(item),
|
||||
season: item.seriesSeason || null,
|
||||
episode: item.episodeId || null,
|
||||
icon: parseIcon(item),
|
||||
start: dayjs.utc(item.startDate) || null,
|
||||
stop: dayjs.utc(item.endDate) || null
|
||||
})
|
||||
})
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const axios = require('axios')
|
||||
const data = await axios
|
||||
.get(API_CHANNEL_ENDPOINT)
|
||||
.then(r => r.data)
|
||||
.catch(console.log)
|
||||
return data.response.map(item => {
|
||||
return {
|
||||
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')
|
||||
|
||||
if (cover) {
|
||||
return `${API_IMAGE_ENDPOINT}${cover.value}`
|
||||
}
|
||||
}
|
||||
|
||||
return ''
|
||||
}
|
||||
|
||||
function parseGenres(item) {
|
||||
return item.genres.map(i => i.name)
|
||||
}
|
||||
|
||||
function parseItems(content, channel) {
|
||||
const json =
|
||||
typeof content === 'string' ? JSON.parse(content) : Array.isArray(content) ? content : []
|
||||
|
||||
if (!Array.isArray(json)) {
|
||||
return []
|
||||
}
|
||||
|
||||
const channelData = json.find(i => i.channelExternalId == channel.site_id)
|
||||
|
||||
if (!channelData) return []
|
||||
|
||||
return channelData.programs
|
||||
}
|
||||
|
|
|
@ -1,49 +1,54 @@
|
|||
const { parser, url } = require('./orangetv.orange.es.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
|
||||
const date = dayjs.utc('2024-12-01', 'YYYY-MM-DD').startOf('d')
|
||||
const channel = {
|
||||
site_id: '1010',
|
||||
xmltv_id: 'La1.es'
|
||||
}
|
||||
|
||||
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`)
|
||||
})
|
||||
|
||||
it('can parse response', async () => {
|
||||
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()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(results.length).toBe(4)
|
||||
|
||||
var sampleResult = results[0];
|
||||
|
||||
expect(sampleResult).toMatchObject({
|
||||
start: '2024-11-30T22:36:51.000Z',
|
||||
stop: '2024-11-30T23:57:25.000Z',
|
||||
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.',
|
||||
title: 'Loco de amor'
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({
|
||||
date,
|
||||
channel,
|
||||
content: '{}'
|
||||
})
|
||||
expect(result).toMatchObject({})
|
||||
})
|
||||
const { parser, url } = require('./orangetv.orange.es.config.js')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const customParseFormat = require('dayjs/plugin/customParseFormat')
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(utc)
|
||||
const path = require('path')
|
||||
const fs = require('fs')
|
||||
|
||||
const date = dayjs.utc('2024-12-01', 'YYYY-MM-DD').startOf('d')
|
||||
const channel = {
|
||||
site_id: '1010',
|
||||
xmltv_id: 'La1.es'
|
||||
}
|
||||
|
||||
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`
|
||||
)
|
||||
})
|
||||
|
||||
it('can parse response', async () => {
|
||||
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()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(results.length).toBe(4)
|
||||
|
||||
var sampleResult = results[0]
|
||||
|
||||
expect(sampleResult).toMatchObject({
|
||||
start: '2024-11-30T22:36:51.000Z',
|
||||
stop: '2024-11-30T23:57:25.000Z',
|
||||
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.',
|
||||
title: 'Loco de amor'
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({
|
||||
date,
|
||||
channel,
|
||||
content: '{}'
|
||||
})
|
||||
expect(result).toMatchObject({})
|
||||
})
|
||||
|
|
|
@ -5,7 +5,12 @@ const timezone = require('dayjs/plugin/timezone')
|
|||
dayjs.extend(utc)
|
||||
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 tz = 'Asia/Dubai'
|
||||
|
||||
|
@ -13,11 +18,9 @@ module.exports = {
|
|||
site: 'osn.com',
|
||||
days: 2,
|
||||
url({ channel, date }) {
|
||||
return `https://www.osn.com/api/TVScheduleWebService.asmx/time?dt=${
|
||||
encodeURIComponent(date.format('MM/DD/YYYY'))
|
||||
}&co=${country}&ch=${
|
||||
channel.site_id
|
||||
}&mo=false&hr=0`
|
||||
return `https://www.osn.com/api/TVScheduleWebService.asmx/time?dt=${encodeURIComponent(
|
||||
date.format('MM/DD/YYYY')
|
||||
)}&co=${country}&ch=${channel.site_id}&mo=false&hr=0`
|
||||
},
|
||||
request: {
|
||||
headers({ channel }) {
|
||||
|
@ -46,7 +49,9 @@ module.exports = {
|
|||
const axios = require('axios')
|
||||
for (const pkg of Object.values(packages)) {
|
||||
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)
|
||||
.catch(console.error)
|
||||
|
||||
|
|
|
@ -28,32 +28,30 @@ it('can generate valid url', () => {
|
|||
})
|
||||
|
||||
it('can parse response (ar)', () => {
|
||||
const result = parser({ date, channel: channelAR, content })
|
||||
.map(a => {
|
||||
a.start = a.start.toJSON()
|
||||
a.stop = a.stop.toJSON()
|
||||
return a
|
||||
})
|
||||
const result = parser({ date, channel: channelAR, content }).map(a => {
|
||||
a.start = a.start.toJSON()
|
||||
a.stop = a.stop.toJSON()
|
||||
return a
|
||||
})
|
||||
expect(result.length).toBe(29)
|
||||
expect(result[1]).toMatchObject({
|
||||
start: '2024-11-26T20:50:00.000Z',
|
||||
stop: '2024-11-26T21:45:00.000Z',
|
||||
title: 'بيت الحلويات: الحلقة 3',
|
||||
title: 'بيت الحلويات: الحلقة 3'
|
||||
})
|
||||
})
|
||||
|
||||
it('can parse response (en)', () => {
|
||||
const result = parser({ date, channel: channelEN, content })
|
||||
.map(a => {
|
||||
a.start = a.start.toJSON()
|
||||
a.stop = a.stop.toJSON()
|
||||
return a
|
||||
})
|
||||
const result = parser({ date, channel: channelEN, content }).map(a => {
|
||||
a.start = a.start.toJSON()
|
||||
a.stop = a.stop.toJSON()
|
||||
return a
|
||||
})
|
||||
expect(result.length).toBe(29)
|
||||
expect(result[1]).toMatchObject({
|
||||
start: '2024-11-26T20:50: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
|
||||
try {
|
||||
data = JSON.parse(json)
|
||||
} catch (error) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
|
||||
|
|
|
@ -1,174 +1,172 @@
|
|||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
let apiVersion
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
module.exports = {
|
||||
site: 'pickx.be',
|
||||
days: 2,
|
||||
setApiVersion: function (version) {
|
||||
apiVersion = version
|
||||
},
|
||||
getApiVersion: function () {
|
||||
return apiVersion
|
||||
},
|
||||
fetchApiVersion: fetchApiVersion,
|
||||
url: async function ({ channel, date }) {
|
||||
if (!apiVersion) {
|
||||
await fetchApiVersion()
|
||||
}
|
||||
return `https://px-epg.azureedge.net/airings/${apiVersion}/${date.format(
|
||||
'YYYY-MM-DD'
|
||||
)}/channel/${channel.site_id}?timezone=Europe%2FBrussels`
|
||||
},
|
||||
request: {
|
||||
headers: {
|
||||
Origin: 'https://www.pickx.be',
|
||||
Referer: 'https://www.pickx.be/'
|
||||
}
|
||||
},
|
||||
parser({ channel, content }) {
|
||||
const programs = []
|
||||
if (content) {
|
||||
const items = JSON.parse(content)
|
||||
items.forEach(item => {
|
||||
programs.push({
|
||||
title: item.program.title,
|
||||
sub_title: item.program.episodeTitle,
|
||||
description: item.program.description,
|
||||
category: item.program.translatedCategory?.[channel.lang]
|
||||
? item.program.translatedCategory[channel.lang]
|
||||
: item.program.category.split('.')[1],
|
||||
image: item.program.posterFileName
|
||||
? `https://experience-cache.proximustv.be/posterserver/poster/EPG/w-166_h-110/${item.program.posterFileName}`
|
||||
: null,
|
||||
season: item.program.seasonNumber,
|
||||
episode: item.program.episodeNumber,
|
||||
actors: item.program.actors,
|
||||
director: item.program.director ? [item.program.director] : null,
|
||||
start: dayjs.utc(item.programScheduleStart),
|
||||
stop: dayjs.utc(item.programScheduleEnd)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels({ lang = '' }) {
|
||||
const query = {
|
||||
operationName: 'getChannels',
|
||||
variables: {
|
||||
language: lang,
|
||||
queryParams: {},
|
||||
id: '0',
|
||||
params: {
|
||||
shouldReadFromCache: true
|
||||
}
|
||||
},
|
||||
query: `query getChannels($language: String!, $queryParams: ChannelQueryParams, $id: String, $params: ChannelParams) {
|
||||
channels(language: $language, queryParams: $queryParams, id: $id, params: $params) {
|
||||
id
|
||||
channelReferenceNumber
|
||||
name
|
||||
callLetter
|
||||
number
|
||||
logo {
|
||||
key
|
||||
url
|
||||
__typename
|
||||
}
|
||||
language
|
||||
hd
|
||||
radio
|
||||
replayable
|
||||
ottReplayable
|
||||
playable
|
||||
ottPlayable
|
||||
recordable
|
||||
subscribed
|
||||
cloudRecordable
|
||||
catchUpWindowInHours
|
||||
isOttNPVREnabled
|
||||
ottNPVRStart
|
||||
subscription {
|
||||
channelRef
|
||||
subscribed
|
||||
upselling {
|
||||
upsellable
|
||||
packages
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
packages
|
||||
__typename
|
||||
}
|
||||
}`
|
||||
}
|
||||
const result = await axios
|
||||
.post('https://api.proximusmwc.be/tiams/v3/graphql', query)
|
||||
.then(r => r.data)
|
||||
.catch(console.error)
|
||||
|
||||
return (
|
||||
result?.data?.channels
|
||||
.filter(
|
||||
channel =>
|
||||
!channel.radio && (!lang || channel.language === (lang === 'de' ? 'ger' : lang))
|
||||
)
|
||||
.map(channel => {
|
||||
return {
|
||||
lang: channel.language === 'ger' ? 'de' : channel.language,
|
||||
site_id: channel.id,
|
||||
name: channel.name
|
||||
}
|
||||
}) || []
|
||||
)
|
||||
}
|
||||
}
|
||||
function fetchApiVersion() {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
try {
|
||||
// you'll never find what happened here :)
|
||||
// load the pickx page and get the hash from the MWC configuration.
|
||||
// it's not the best way to get the version but it's the only way to get it.
|
||||
|
||||
const hashUrl = 'https://www.pickx.be/nl/televisie/tv-gids';
|
||||
|
||||
const hashData = await axios.get(hashUrl)
|
||||
.then(r => {
|
||||
const re = /"hashes":\["(.*)"\]/
|
||||
const match = r.data.match(re)
|
||||
if (match && match[1]) {
|
||||
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}`
|
||||
|
||||
const response = await axios.get(versionUrl, {
|
||||
headers: {
|
||||
Origin: 'https://www.pickx.be',
|
||||
Referer: 'https://www.pickx.be/'
|
||||
}
|
||||
})
|
||||
|
||||
if (response.status === 200) {
|
||||
apiVersion = response.data.version
|
||||
resolve()
|
||||
} else {
|
||||
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)
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
let apiVersion
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
module.exports = {
|
||||
site: 'pickx.be',
|
||||
days: 2,
|
||||
setApiVersion: function (version) {
|
||||
apiVersion = version
|
||||
},
|
||||
getApiVersion: function () {
|
||||
return apiVersion
|
||||
},
|
||||
fetchApiVersion: fetchApiVersion,
|
||||
url: async function ({ channel, date }) {
|
||||
if (!apiVersion) {
|
||||
await fetchApiVersion()
|
||||
}
|
||||
return `https://px-epg.azureedge.net/airings/${apiVersion}/${date.format(
|
||||
'YYYY-MM-DD'
|
||||
)}/channel/${channel.site_id}?timezone=Europe%2FBrussels`
|
||||
},
|
||||
request: {
|
||||
headers: {
|
||||
Origin: 'https://www.pickx.be',
|
||||
Referer: 'https://www.pickx.be/'
|
||||
}
|
||||
},
|
||||
parser({ channel, content }) {
|
||||
const programs = []
|
||||
if (content) {
|
||||
const items = JSON.parse(content)
|
||||
items.forEach(item => {
|
||||
programs.push({
|
||||
title: item.program.title,
|
||||
sub_title: item.program.episodeTitle,
|
||||
description: item.program.description,
|
||||
category: item.program.translatedCategory?.[channel.lang]
|
||||
? item.program.translatedCategory[channel.lang]
|
||||
: item.program.category.split('.')[1],
|
||||
image: item.program.posterFileName
|
||||
? `https://experience-cache.proximustv.be/posterserver/poster/EPG/w-166_h-110/${item.program.posterFileName}`
|
||||
: null,
|
||||
season: item.program.seasonNumber,
|
||||
episode: item.program.episodeNumber,
|
||||
actors: item.program.actors,
|
||||
director: item.program.director ? [item.program.director] : null,
|
||||
start: dayjs.utc(item.programScheduleStart),
|
||||
stop: dayjs.utc(item.programScheduleEnd)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels({ lang = '' }) {
|
||||
const query = {
|
||||
operationName: 'getChannels',
|
||||
variables: {
|
||||
language: lang,
|
||||
queryParams: {},
|
||||
id: '0',
|
||||
params: {
|
||||
shouldReadFromCache: true
|
||||
}
|
||||
},
|
||||
query: `query getChannels($language: String!, $queryParams: ChannelQueryParams, $id: String, $params: ChannelParams) {
|
||||
channels(language: $language, queryParams: $queryParams, id: $id, params: $params) {
|
||||
id
|
||||
channelReferenceNumber
|
||||
name
|
||||
callLetter
|
||||
number
|
||||
logo {
|
||||
key
|
||||
url
|
||||
__typename
|
||||
}
|
||||
language
|
||||
hd
|
||||
radio
|
||||
replayable
|
||||
ottReplayable
|
||||
playable
|
||||
ottPlayable
|
||||
recordable
|
||||
subscribed
|
||||
cloudRecordable
|
||||
catchUpWindowInHours
|
||||
isOttNPVREnabled
|
||||
ottNPVRStart
|
||||
subscription {
|
||||
channelRef
|
||||
subscribed
|
||||
upselling {
|
||||
upsellable
|
||||
packages
|
||||
__typename
|
||||
}
|
||||
__typename
|
||||
}
|
||||
packages
|
||||
__typename
|
||||
}
|
||||
}`
|
||||
}
|
||||
const result = await axios
|
||||
.post('https://api.proximusmwc.be/tiams/v3/graphql', query)
|
||||
.then(r => r.data)
|
||||
.catch(console.error)
|
||||
|
||||
return (
|
||||
result?.data?.channels
|
||||
.filter(
|
||||
channel =>
|
||||
!channel.radio && (!lang || channel.language === (lang === 'de' ? 'ger' : lang))
|
||||
)
|
||||
.map(channel => {
|
||||
return {
|
||||
lang: channel.language === 'ger' ? 'de' : channel.language,
|
||||
site_id: channel.id,
|
||||
name: channel.name
|
||||
}
|
||||
}) || []
|
||||
)
|
||||
}
|
||||
}
|
||||
async function fetchApiVersion() {
|
||||
// you'll never find what happened here :)
|
||||
// load the pickx page and get the hash from the MWC configuration.
|
||||
// it's not the best way to get the version but it's the only way to get it.
|
||||
const hashUrl = 'https://www.pickx.be/nl/televisie/tv-gids'
|
||||
const hashData = await axios
|
||||
.get(hashUrl)
|
||||
.then(r => {
|
||||
const re = /"hashes":\["(.*)"\]/
|
||||
const match = r.data.match(re)
|
||||
if (match && match[1]) {
|
||||
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}`
|
||||
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
|
||||
resolve()
|
||||
} else {
|
||||
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)
|
||||
reject(error)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,76 +1,69 @@
|
|||
jest.mock('./pickx.be.config.js', () => {
|
||||
const originalModule = jest.requireActual('./pickx.be.config.js')
|
||||
return {
|
||||
...originalModule,
|
||||
fetchApiVersion: jest.fn(() => Promise.resolve())
|
||||
}
|
||||
})
|
||||
|
||||
const {
|
||||
parser,
|
||||
url,
|
||||
request,
|
||||
fetchApiVersion,
|
||||
setApiVersion,
|
||||
getApiVersion
|
||||
} = require('./pickx.be.config.js')
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2023-12-13').startOf('d')
|
||||
const channel = {
|
||||
lang: 'fr',
|
||||
site_id: 'UID0118',
|
||||
xmltv_id: 'Vedia.be'
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
setApiVersion('mockedApiVersion')
|
||||
})
|
||||
|
||||
it('can generate valid url', async () => {
|
||||
const generatedUrl = await url({ channel, date })
|
||||
expect(generatedUrl).toBe(
|
||||
`https://px-epg.azureedge.net/airings/mockedApiVersion/2023-12-13/channel/UID0118?timezone=Europe%2FBrussels`
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate valid request headers', () => {
|
||||
expect(request.headers).toMatchObject({
|
||||
Origin: 'https://www.pickx.be',
|
||||
Referer: 'https://www.pickx.be/'
|
||||
})
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json'))
|
||||
const result = parser({ content, channel, date }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result[0]).toMatchObject({
|
||||
start: '2023-12-12T23:55:00.000Z',
|
||||
stop: '2023-12-13T00:15:00.000Z',
|
||||
title: 'Le 22h30',
|
||||
description: 'Le journal de vivre ici.',
|
||||
category: 'Info',
|
||||
image:
|
||||
'https://experience-cache.proximustv.be/posterserver/poster/EPG/w-166_h-110/250_250_4B990CC58066A7B2A660AFA0BDDE5C41.jpg'
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({
|
||||
date,
|
||||
channel,
|
||||
content: ''
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
jest.mock('./pickx.be.config.js', () => {
|
||||
const originalModule = jest.requireActual('./pickx.be.config.js')
|
||||
return {
|
||||
...originalModule,
|
||||
fetchApiVersion: jest.fn(() => Promise.resolve())
|
||||
}
|
||||
})
|
||||
|
||||
const { parser, url, request, setApiVersion } = require('./pickx.be.config.js')
|
||||
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
const date = dayjs.utc('2023-12-13').startOf('d')
|
||||
const channel = {
|
||||
lang: 'fr',
|
||||
site_id: 'UID0118',
|
||||
xmltv_id: 'Vedia.be'
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
setApiVersion('mockedApiVersion')
|
||||
})
|
||||
|
||||
it('can generate valid url', async () => {
|
||||
const generatedUrl = await url({ channel, date })
|
||||
expect(generatedUrl).toBe(
|
||||
'https://px-epg.azureedge.net/airings/mockedApiVersion/2023-12-13/channel/UID0118?timezone=Europe%2FBrussels'
|
||||
)
|
||||
})
|
||||
|
||||
it('can generate valid request headers', () => {
|
||||
expect(request.headers).toMatchObject({
|
||||
Origin: 'https://www.pickx.be',
|
||||
Referer: 'https://www.pickx.be/'
|
||||
})
|
||||
})
|
||||
|
||||
it('can parse response', () => {
|
||||
const content = fs.readFileSync(path.resolve(__dirname, '__data__/data.json'))
|
||||
const result = parser({ content, channel, date }).map(p => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result[0]).toMatchObject({
|
||||
start: '2023-12-12T23:55:00.000Z',
|
||||
stop: '2023-12-13T00:15:00.000Z',
|
||||
title: 'Le 22h30',
|
||||
description: 'Le journal de vivre ici.',
|
||||
category: 'Info',
|
||||
image:
|
||||
'https://experience-cache.proximustv.be/posterserver/poster/EPG/w-166_h-110/250_250_4B990CC58066A7B2A660AFA0BDDE5C41.jpg'
|
||||
})
|
||||
})
|
||||
|
||||
it('can handle empty guide', () => {
|
||||
const result = parser({
|
||||
date,
|
||||
channel,
|
||||
content: ''
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -1,102 +1,104 @@
|
|||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
module.exports = {
|
||||
site: 'player.ee.co.uk',
|
||||
days: 2,
|
||||
url({ date, channel, hour = 0 }) {
|
||||
return `https://api.youview.tv/metadata/linear/v2/schedule/by-servicelocator?serviceLocator=${
|
||||
encodeURIComponent(channel.site_id)
|
||||
}&interval=${date.format('YYYY-MM-DD')}T${hour.toString().padStart(2,'0')}Z/PT12H`
|
||||
},
|
||||
request: {
|
||||
headers: {
|
||||
Referer: 'https://player.ee.co.uk/'
|
||||
}
|
||||
},
|
||||
async parser({ content, channel, date }) {
|
||||
const programs = []
|
||||
if (content) {
|
||||
const schedule = JSON.parse(content)
|
||||
// fetch next 12 hours schedule
|
||||
const { url, request } = module.exports
|
||||
const nextSchedule = await axios
|
||||
.get(url({ channel, date, hour: 12 }), { headers: request.headers })
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
if (schedule?.items) {
|
||||
// merge schedules
|
||||
if (nextSchedule?.items) {
|
||||
schedule.items.push(...nextSchedule.items)
|
||||
}
|
||||
schedule.items.forEach(item => {
|
||||
let season, episode
|
||||
const start = dayjs.utc(item.publishedStartTime)
|
||||
const stop = start.add(item.publishedDuration, 's')
|
||||
const description = item.synopsis
|
||||
if (description) {
|
||||
const matches = description.trim().match(/\(?S(\d+)[\/\s]Ep(\d+)\)?/)
|
||||
if (matches) {
|
||||
if (matches[1]) {
|
||||
season = parseInt(matches[1])
|
||||
}
|
||||
if (matches[2]) {
|
||||
episode = parseInt(matches[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
programs.push({
|
||||
title: item.title,
|
||||
description,
|
||||
season,
|
||||
episode,
|
||||
start,
|
||||
stop
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const token =
|
||||
'eyJkaXNjb3ZlcnlVc2VyR3JvdXBzIjpbIkFMTFVTRVJTIiwiYWxsIiwiaHR0cDovL3JlZmRhd' +
|
||||
'GEueW91dmlldy5jb20vbXBlZzdjcy9Zb3VWaWV3QXBwbGljYXRpb25QbGF5ZXJDUy8yMDIxLT' +
|
||||
'A5LTEwI2FuZHJvaWRfcnVudGltZS1wcm9maWxlMSIsInRhZzpidC5jb20sMjAxOC0wNy0xMTp' +
|
||||
'1c2VyZ3JvdXAjR0JSLWJ0X25vd1RWX211bHRpY2FzdCIsInRhZzpidC5jb20sMjAyMS0xMC0y' +
|
||||
'NTp1c2VyZ3JvdXAjR0JSLWJ0X2V1cm9zcG9ydCJdLCJyZWdpb25zIjpbIkFMTFJFR0lPTlMiL' +
|
||||
'CJHQlIiLCJHQlItRU5HIiwiR0JSLUVORy1sb25kb24iLCJhbGwiXSwic3Vic2V0IjoiMy41Lj' +
|
||||
'EvYW5kcm9pZF9ydW50aW1lLXByb2ZpbGUxL0JST0FEQ0FTVF9JUC9HQlItYnRfYnJvYWRiYW5' +
|
||||
'kIiwic3Vic2V0cyI6WyIvLy8iLCIvL0JST0FEQ0FTVF9JUC8iLCIzLjUvLy8iXX0='
|
||||
const extensions = [
|
||||
'LinearCategoriesExtension',
|
||||
'LogicalChannelNumberExtension',
|
||||
'BTSubscriptionCodesExtension'
|
||||
]
|
||||
const result = await axios
|
||||
.get(`https://api.youview.tv/metadata/linear/v2/linear-services`, {
|
||||
params: {
|
||||
contentTargetingToken: token,
|
||||
extensions: extensions.join(',')
|
||||
},
|
||||
headers: module.exports.request.headers
|
||||
})
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
return result?.items
|
||||
.filter(channel => channel.contentTypes.indexOf('tv') >= 0)
|
||||
.map(channel => {
|
||||
return {
|
||||
lang: 'en',
|
||||
site_id: channel.serviceLocator,
|
||||
name: channel.fullName
|
||||
}
|
||||
}) || []
|
||||
}
|
||||
}
|
||||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
module.exports = {
|
||||
site: 'player.ee.co.uk',
|
||||
days: 2,
|
||||
url({ date, channel, hour = 0 }) {
|
||||
return `https://api.youview.tv/metadata/linear/v2/schedule/by-servicelocator?serviceLocator=${encodeURIComponent(
|
||||
channel.site_id
|
||||
)}&interval=${date.format('YYYY-MM-DD')}T${hour.toString().padStart(2, '0')}Z/PT12H`
|
||||
},
|
||||
request: {
|
||||
headers: {
|
||||
Referer: 'https://player.ee.co.uk/'
|
||||
}
|
||||
},
|
||||
async parser({ content, channel, date }) {
|
||||
const programs = []
|
||||
if (content) {
|
||||
const schedule = JSON.parse(content)
|
||||
// fetch next 12 hours schedule
|
||||
const { url, request } = module.exports
|
||||
const nextSchedule = await axios
|
||||
.get(url({ channel, date, hour: 12 }), { headers: request.headers })
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
if (schedule?.items) {
|
||||
// merge schedules
|
||||
if (nextSchedule?.items) {
|
||||
schedule.items.push(...nextSchedule.items)
|
||||
}
|
||||
schedule.items.forEach(item => {
|
||||
let season, episode
|
||||
const start = dayjs.utc(item.publishedStartTime)
|
||||
const stop = start.add(item.publishedDuration, 's')
|
||||
const description = item.synopsis
|
||||
if (description) {
|
||||
const matches = description.trim().match(/\(?S(\d+)[/\s]Ep(\d+)\)?/)
|
||||
if (matches) {
|
||||
if (matches[1]) {
|
||||
season = parseInt(matches[1])
|
||||
}
|
||||
if (matches[2]) {
|
||||
episode = parseInt(matches[2])
|
||||
}
|
||||
}
|
||||
}
|
||||
programs.push({
|
||||
title: item.title,
|
||||
description,
|
||||
season,
|
||||
episode,
|
||||
start,
|
||||
stop
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return programs
|
||||
},
|
||||
async channels() {
|
||||
const token =
|
||||
'eyJkaXNjb3ZlcnlVc2VyR3JvdXBzIjpbIkFMTFVTRVJTIiwiYWxsIiwiaHR0cDovL3JlZmRhd' +
|
||||
'GEueW91dmlldy5jb20vbXBlZzdjcy9Zb3VWaWV3QXBwbGljYXRpb25QbGF5ZXJDUy8yMDIxLT' +
|
||||
'A5LTEwI2FuZHJvaWRfcnVudGltZS1wcm9maWxlMSIsInRhZzpidC5jb20sMjAxOC0wNy0xMTp' +
|
||||
'1c2VyZ3JvdXAjR0JSLWJ0X25vd1RWX211bHRpY2FzdCIsInRhZzpidC5jb20sMjAyMS0xMC0y' +
|
||||
'NTp1c2VyZ3JvdXAjR0JSLWJ0X2V1cm9zcG9ydCJdLCJyZWdpb25zIjpbIkFMTFJFR0lPTlMiL' +
|
||||
'CJHQlIiLCJHQlItRU5HIiwiR0JSLUVORy1sb25kb24iLCJhbGwiXSwic3Vic2V0IjoiMy41Lj' +
|
||||
'EvYW5kcm9pZF9ydW50aW1lLXByb2ZpbGUxL0JST0FEQ0FTVF9JUC9HQlItYnRfYnJvYWRiYW5' +
|
||||
'kIiwic3Vic2V0cyI6WyIvLy8iLCIvL0JST0FEQ0FTVF9JUC8iLCIzLjUvLy8iXX0='
|
||||
const extensions = [
|
||||
'LinearCategoriesExtension',
|
||||
'LogicalChannelNumberExtension',
|
||||
'BTSubscriptionCodesExtension'
|
||||
]
|
||||
const result = await axios
|
||||
.get('https://api.youview.tv/metadata/linear/v2/linear-services', {
|
||||
params: {
|
||||
contentTargetingToken: token,
|
||||
extensions: extensions.join(',')
|
||||
},
|
||||
headers: module.exports.request.headers
|
||||
})
|
||||
.then(response => response.data)
|
||||
.catch(console.error)
|
||||
|
||||
return (
|
||||
result?.items
|
||||
.filter(channel => channel.contentTypes.indexOf('tv') >= 0)
|
||||
.map(channel => {
|
||||
return {
|
||||
lang: 'en',
|
||||
site_id: channel.serviceLocator,
|
||||
name: channel.fullName
|
||||
}
|
||||
}) || []
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,72 +1,74 @@
|
|||
const { parser, url } = require('./player.ee.co.uk.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
jest.mock('axios')
|
||||
|
||||
const date = dayjs.utc('2023-12-13').startOf('d')
|
||||
const channel = {
|
||||
site_id: 'dvb://233a..6d60',
|
||||
xmltv_id: 'HGTV.uk'
|
||||
}
|
||||
|
||||
axios.get.mockImplementation((url, opts) => {
|
||||
if (url === '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', () => {
|
||||
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 => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: 'Bargain Mansions',
|
||||
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',
|
||||
season: 4,
|
||||
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',
|
||||
season: 2,
|
||||
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,
|
||||
date,
|
||||
content: ''
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
const { parser, url } = require('./player.ee.co.uk.config.js')
|
||||
const fs = require('fs')
|
||||
const path = require('path')
|
||||
const axios = require('axios')
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
|
||||
dayjs.extend(utc)
|
||||
|
||||
jest.mock('axios')
|
||||
|
||||
const date = dayjs.utc('2023-12-13').startOf('d')
|
||||
const channel = {
|
||||
site_id: 'dvb://233a..6d60',
|
||||
xmltv_id: 'HGTV.uk'
|
||||
}
|
||||
|
||||
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'
|
||||
) {
|
||||
return Promise.resolve({
|
||||
data: JSON.parse(fs.readFileSync(path.resolve(__dirname, '__data__/data1.json')))
|
||||
})
|
||||
}
|
||||
|
||||
return Promise.resolve({ data: '' })
|
||||
})
|
||||
|
||||
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 => {
|
||||
p.start = p.start.toJSON()
|
||||
p.stop = p.stop.toJSON()
|
||||
return p
|
||||
})
|
||||
|
||||
expect(result).toMatchObject([
|
||||
{
|
||||
title: 'Bargain Mansions',
|
||||
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',
|
||||
season: 4,
|
||||
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',
|
||||
season: 2,
|
||||
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,
|
||||
date,
|
||||
content: ''
|
||||
})
|
||||
expect(result).toMatchObject([])
|
||||
})
|
||||
|
|
|
@ -43,7 +43,7 @@ module.exports = {
|
|||
const axios = require('axios')
|
||||
const data = await axios
|
||||
.post(
|
||||
`https://playtv.unifi.com.my:7053/VSP/V3/QueryAllChannel`,
|
||||
'https://playtv.unifi.com.my:7053/VSP/V3/QueryAllChannel',
|
||||
{ isReturnAllMedia: '0' },
|
||||
{
|
||||
params: {
|
||||
|
@ -74,7 +74,7 @@ function parseItems(content, channel) {
|
|||
|
||||
const channelData = data.find(i => i.id == channel.site_id)
|
||||
return channelData.items && Array.isArray(channelData.items) ? channelData.items : []
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return []
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,50 +1,49 @@
|
|||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const axios = require('axios')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
|
||||
module.exports = {
|
||||
site: 'pluto.tv',
|
||||
days: 3,
|
||||
|
||||
url: function ({ date, channel }) {
|
||||
const channelId = channel.site_id
|
||||
|
||||
const localTimezone = dayjs.tz.guess()
|
||||
|
||||
const startTime = dayjs(date).tz(localTimezone).startOf('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}`
|
||||
return generatedUrl
|
||||
},
|
||||
|
||||
parser: function ({ content }) {
|
||||
const data = JSON.parse(content)
|
||||
const programs = []
|
||||
|
||||
if (data.timelines) {
|
||||
data.timelines.forEach(item => {
|
||||
programs.push({
|
||||
title: item.title,
|
||||
subTitle: item.episode?.name || '',
|
||||
description: item.episode?.description || '',
|
||||
episode: item.episode?.number || '',
|
||||
season: item.episode?.season || '',
|
||||
actors: item.episode?.clip?.actors || [],
|
||||
categories: [item.episode?.genre, item.episode?.subGenre].filter(Boolean),
|
||||
rating: item.episode?.rating || '',
|
||||
date: item.episode?.clip?.originalReleaseDate || '',
|
||||
icon: item.episode?.series?.tile?.path || '',
|
||||
start: item.start,
|
||||
stop: item.stop
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return programs
|
||||
}
|
||||
}
|
||||
const dayjs = require('dayjs')
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
|
||||
module.exports = {
|
||||
site: 'pluto.tv',
|
||||
days: 3,
|
||||
|
||||
url: function ({ date, channel }) {
|
||||
const channelId = channel.site_id
|
||||
|
||||
const localTimezone = dayjs.tz.guess()
|
||||
|
||||
const startTime = dayjs(date).tz(localTimezone).startOf('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}`
|
||||
return generatedUrl
|
||||
},
|
||||
|
||||
parser: function ({ content }) {
|
||||
const data = JSON.parse(content)
|
||||
const programs = []
|
||||
|
||||
if (data.timelines) {
|
||||
data.timelines.forEach(item => {
|
||||
programs.push({
|
||||
title: item.title,
|
||||
subTitle: item.episode?.name || '',
|
||||
description: item.episode?.description || '',
|
||||
episode: item.episode?.number || '',
|
||||
season: item.episode?.season || '',
|
||||
actors: item.episode?.clip?.actors || [],
|
||||
categories: [item.episode?.genre, item.episode?.subGenre].filter(Boolean),
|
||||
rating: item.episode?.rating || '',
|
||||
date: item.episode?.clip?.originalReleaseDate || '',
|
||||
icon: item.episode?.series?.tile?.path || '',
|
||||
start: item.start,
|
||||
stop: item.stop
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return programs
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,14 +33,17 @@ it('can parse response', () => {
|
|||
start: '2024-12-28T00:21:00.000Z',
|
||||
stop: '2024-12-28T00:48:00.000Z',
|
||||
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',
|
||||
episode: 80,
|
||||
season: 2,
|
||||
actors: ["Isabel Martion (Naruto Uzumaki)",
|
||||
"Christine Byrd (Sakura Haruno)",
|
||||
"Victor Ugarte (Sasuke Uchiha)",
|
||||
"Alfonso Obreg (Kakashi Hatake)"],
|
||||
actors: [
|
||||
'Isabel Martion (Naruto Uzumaki)',
|
||||
'Christine Byrd (Sakura Haruno)',
|
||||
'Victor Ugarte (Sasuke Uchiha)',
|
||||
'Alfonso Obreg (Kakashi Hatake)'
|
||||
],
|
||||
categories: ['Anime', 'Anime Action & Adventure'],
|
||||
rating: 'TV-14',
|
||||
date: '2004-04-21T00:00:00.000Z',
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<channels>
|
||||
<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"?>
|
||||
<channels>
|
||||
<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
|
||||
},
|
||||
async channels({ country, lang }) {
|
||||
async channels() {
|
||||
const axios = require('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)
|
||||
.catch(console.log)
|
||||
|
||||
|
|
|
@ -62,7 +62,7 @@ module.exports = {
|
|||
$('.channelList-listItemsLink').each((i, el) => {
|
||||
const name = $(el).attr('title')
|
||||
const url = $(el).attr('href')
|
||||
const [, site_id] = url.match(/\/programme\-(.*)\.html$/i)
|
||||
const [, site_id] = url.match(/\/programme-(.*)\.html$/i)
|
||||
|
||||
channels.push({
|
||||
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