diff --git a/sites/watch.whaletvplus.com/__data__/content.json b/sites/watch.whaletvplus.com/__data__/content.json new file mode 100644 index 000000000..475b04758 --- /dev/null +++ b/sites/watch.whaletvplus.com/__data__/content.json @@ -0,0 +1,817 @@ +{ + "data": [ + { + "chlId": "972712064533996134", + "chlNm": "123GO!", + "ptList": [ + { + "prgEtm": "1767680730000", + "prgStm": "1767679200000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024814" + }, + { + "prgEtm": "1767682123000", + "prgStm": "1767680730000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024815" + }, + { + "prgEtm": "1767683888000", + "prgStm": "1767682123000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024816" + }, + { + "prgEtm": "1767685585000", + "prgStm": "1767683888000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024817" + }, + { + "prgEtm": "1767687192000", + "prgStm": "1767685585000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024818" + }, + { + "prgEtm": "1767688664000", + "prgStm": "1767687192000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024819" + }, + { + "prgEtm": "1767690132000", + "prgStm": "1767688664000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024820" + }, + { + "prgEtm": "1767691727000", + "prgStm": "1767690132000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024821" + }, + { + "prgEtm": "1767693179000", + "prgStm": "1767691727000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024822" + }, + { + "prgEtm": "1767694741000", + "prgStm": "1767693179000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024823" + }, + { + "prgEtm": "1767696168000", + "prgStm": "1767694741000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024824" + }, + { + "prgEtm": "1767697685000", + "prgStm": "1767696168000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024825" + }, + { + "prgEtm": "1767699105000", + "prgStm": "1767697685000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024826" + }, + { + "prgEtm": "1767700639000", + "prgStm": "1767699105000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024827" + }, + { + "prgEtm": "1767702080000", + "prgStm": "1767700639000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024828" + }, + { + "prgEtm": "1767703536000", + "prgStm": "1767702080000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024829" + }, + { + "prgEtm": "1767704915000", + "prgStm": "1767703536000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024830" + }, + { + "prgEtm": "1767706392000", + "prgStm": "1767704915000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024831" + }, + { + "prgEtm": "1767707772000", + "prgStm": "1767706392000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024832" + }, + { + "prgEtm": "1767709462000", + "prgStm": "1767707772000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024833" + }, + { + "prgEtm": "1767711426000", + "prgStm": "1767709462000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024834" + }, + { + "prgEtm": "1767712932000", + "prgStm": "1767711426000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024835" + }, + { + "prgEtm": "1767714330000", + "prgStm": "1767712932000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024836" + }, + { + "prgEtm": "1767715777000", + "prgStm": "1767714330000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024837" + }, + { + "prgEtm": "1767717501000", + "prgStm": "1767715777000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024838" + }, + { + "prgEtm": "1767719439000", + "prgStm": "1767717501000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024839" + }, + { + "prgEtm": "1767720912000", + "prgStm": "1767719439000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024840" + }, + { + "prgEtm": "1767722448000", + "prgStm": "1767720912000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024841" + }, + { + "prgEtm": "1767723930000", + "prgStm": "1767722448000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024842" + }, + { + "prgEtm": "1767725520000", + "prgStm": "1767723930000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024843" + }, + { + "prgEtm": "1767727050000", + "prgStm": "1767725520000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024844" + }, + { + "prgEtm": "1767728443000", + "prgStm": "1767727050000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024845" + }, + { + "prgEtm": "1767730208000", + "prgStm": "1767728443000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024846" + }, + { + "prgEtm": "1767731905000", + "prgStm": "1767730208000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024847" + }, + { + "prgEtm": "1767733512000", + "prgStm": "1767731905000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024848" + }, + { + "prgEtm": "1767734984000", + "prgStm": "1767733512000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024849" + }, + { + "prgEtm": "1767736452000", + "prgStm": "1767734984000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024850" + }, + { + "prgEtm": "1767738047000", + "prgStm": "1767736452000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024851" + }, + { + "prgEtm": "1767739499000", + "prgStm": "1767738047000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024852" + }, + { + "prgEtm": "1767741061000", + "prgStm": "1767739499000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024853" + }, + { + "prgEtm": "1767742488000", + "prgStm": "1767741061000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024854" + }, + { + "prgEtm": "1767744005000", + "prgStm": "1767742488000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024855" + }, + { + "prgEtm": "1767745425000", + "prgStm": "1767744005000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024856" + }, + { + "prgEtm": "1767746959000", + "prgStm": "1767745425000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024857" + }, + { + "prgEtm": "1767748400000", + "prgStm": "1767746959000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024858" + }, + { + "prgEtm": "1767749856000", + "prgStm": "1767748400000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024859" + }, + { + "prgEtm": "1767751235000", + "prgStm": "1767749856000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024860" + }, + { + "prgEtm": "1767752712000", + "prgStm": "1767751235000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024861" + }, + { + "prgEtm": "1767754092000", + "prgStm": "1767752712000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024862" + }, + { + "prgEtm": "1767755782000", + "prgStm": "1767754092000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024863" + }, + { + "prgEtm": "1767757746000", + "prgStm": "1767755782000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024864" + }, + { + "prgEtm": "1767759132000", + "prgStm": "1767757746000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024865" + }, + { + "prgEtm": "1767760530000", + "prgStm": "1767759132000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024866" + }, + { + "prgEtm": "1767761977000", + "prgStm": "1767760530000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024867" + }, + { + "prgEtm": "1767763701000", + "prgStm": "1767761977000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024868" + }, + { + "prgEtm": "1767765600000", + "prgStm": "1767763701000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024869" + }, + { + "prgEtm": "1767767107000", + "prgStm": "1767765600000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024870" + }, + { + "prgEtm": "1767768860000", + "prgStm": "1767767107000", + "prgTitle": "123GO!", + "prgchId": "1011321495895024871" + } + ] + }, + { + "chlId": "972720861289775954", + "chlNm": "TidPix", + "ptList": [ + { + "prgEtm": "1767675698000", + "prgStm": "1767667211000", + "prgTitle": "Kankan", + "prgchId": "1011997771970447801" + }, + { + "prgEtm": "1767682089000", + "prgStm": "1767675698000", + "prgTitle": "Afrokons", + "prgchId": "1011997771970447802" + }, + { + "prgEtm": "1767689866000", + "prgStm": "1767682089000", + "prgTitle": "Dear Bayo", + "prgchId": "1011997771970447803" + }, + { + "prgEtm": "1767693550000", + "prgStm": "1767689866000", + "prgTitle": "Kukuri", + "prgchId": "1011997771970447804" + }, + { + "prgEtm": "1767697200000", + "prgStm": "1767693550000", + "prgTitle": "Fixed", + "prgchId": "1011997771970447805" + }, + { + "prgEtm": "1767699144000", + "prgStm": "1767697200000", + "prgTitle": "Channel 77", + "prgchId": "1011997771970447806" + }, + { + "prgEtm": "1767701044000", + "prgStm": "1767699144000", + "prgTitle": "Channel 77", + "prgchId": "1011997771970447807" + }, + { + "prgEtm": "1767703052000", + "prgStm": "1767701044000", + "prgTitle": "Channel 77", + "prgchId": "1011997771970447808" + }, + { + "prgEtm": "1767705130000", + "prgStm": "1767703052000", + "prgTitle": "Squatterz", + "prgchId": "1011997771970447809" + }, + { + "prgEtm": "1767707321000", + "prgStm": "1767705130000", + "prgTitle": "Squatterz", + "prgchId": "1011997771970447810" + }, + { + "prgEtm": "1767709669000", + "prgStm": "1767707321000", + "prgTitle": "Squatterz", + "prgchId": "1011997771970447811" + }, + { + "prgEtm": "1767711964000", + "prgStm": "1767709669000", + "prgTitle": "Squatterz", + "prgchId": "1011997771970447812" + }, + { + "prgEtm": "1767719613000", + "prgStm": "1767711964000", + "prgTitle": "Nairobi Half Life", + "prgchId": "1011997771970447813" + }, + { + "prgEtm": "1767728618000", + "prgStm": "1767719613000", + "prgTitle": "Taliya", + "prgchId": "1011997771970447814" + }, + { + "prgEtm": "1767737075000", + "prgStm": "1767728618000", + "prgTitle": "Once Upon a Family", + "prgchId": "1011997771970447815" + }, + { + "prgEtm": "1767740400000", + "prgStm": "1767737075000", + "prgTitle": "Coerced Revenge", + "prgchId": "1011997771970447816" + }, + { + "prgEtm": "1767745888000", + "prgStm": "1767740400000", + "prgTitle": "The Coffin Salesman", + "prgchId": "1011997771970447817" + }, + { + "prgEtm": "1767753665000", + "prgStm": "1767745888000", + "prgTitle": "Dear Bayo", + "prgchId": "1012360208393444142" + }, + { + "prgEtm": "1767760945000", + "prgStm": "1767753665000", + "prgTitle": "Hidden Dreams", + "prgchId": "1012360208393444143" + }, + { + "prgEtm": "1767764600000", + "prgStm": "1767760945000", + "prgTitle": "Poker ", + "prgchId": "1012360208393444144" + }, + { + "prgEtm": "1767773660000", + "prgStm": "1767764600000", + "prgTitle": "The River Goddess (Asuo Subi)", + "prgchId": "1012360208393444145" + } + ] + }, + { + "chlId": "979187723418476852", + "chlNm": "VladTV", + "ptList": [ + { + "prgEtm": "1767677882000", + "prgStm": "1767670807000", + "prgTitle": "Go Yayo - April 2025", + "prgchId": "1011997716962088477" + }, + { + "prgEtm": "1767684540000", + "prgStm": "1767677882000", + "prgTitle": "Skipp Townsend - May 2025", + "prgchId": "1011997716962088478" + }, + { + "prgEtm": "1767686400000", + "prgStm": "1767684540000", + "prgTitle": "Hector Bravo - June 2025", + "prgchId": "1011997716962088479" + }, + { + "prgEtm": "1767698863000", + "prgStm": "1767686400000", + "prgTitle": "Lord Jamar - June 2025", + "prgchId": "1011997716962088480" + }, + { + "prgEtm": "1767708252000", + "prgStm": "1767698863000", + "prgTitle": "Derrick Grace II - April 2025", + "prgchId": "1011997716962088481" + }, + { + "prgEtm": "1767717426000", + "prgStm": "1767708252000", + "prgTitle": "Wack100 - May 2025", + "prgchId": "1011997716962088482" + }, + { + "prgEtm": "1767729194000", + "prgStm": "1767717426000", + "prgTitle": "DJ Akademiks - May 2025", + "prgchId": "1011997716962088483" + }, + { + "prgEtm": "1767740543000", + "prgStm": "1767729194000", + "prgTitle": "Trap Lore Ross - May 2025", + "prgchId": "1011997716962088484" + }, + { + "prgEtm": "1767745895000", + "prgStm": "1767740543000", + "prgTitle": "Freeway Rick - June 2017", + "prgchId": "1011997716962088485" + }, + { + "prgEtm": "1767754872000", + "prgStm": "1767745895000", + "prgTitle": "Wack100 - June 2025", + "prgchId": "1012360104773297334" + }, + { + "prgEtm": "1767761941000", + "prgStm": "1767754872000", + "prgTitle": "Sharay \"Punisher\" Hayes - June 2025", + "prgchId": "1012360104773297335" + }, + { + "prgEtm": "1767771527000", + "prgStm": "1767761941000", + "prgTitle": "Roger Bonds - June 2025", + "prgchId": "1012360104773297336" + } + ] + }, + { + "chlId": "979187723418476854", + "chlNm": "Moonball Sports TV", + "ptList": [ + { + "prgEtm": "1767681000000", + "prgStm": "1767675600000", + "prgTitle": "Ahman Green’s Gamers Lounge", + "prgchId": "1010958633632668647" + }, + { + "prgEtm": "1767686400000", + "prgStm": "1767681000000", + "prgTitle": "Ahman Green’s Gamers Lounge", + "prgchId": "1010958633632668648" + }, + { + "prgEtm": "1767690000000", + "prgStm": "1767686400000", + "prgTitle": "Nicky Breaks 90", + "prgchId": "1010958633632668649" + }, + { + "prgEtm": "1767693600000", + "prgStm": "1767690000000", + "prgTitle": "Diggin' Deep", + "prgchId": "1010958633632668650" + }, + { + "prgEtm": "1767698100000", + "prgStm": "1767693600000", + "prgTitle": "Diggin' Deep", + "prgchId": "1010958633632668651" + }, + { + "prgEtm": "1767700800000", + "prgStm": "1767698100000", + "prgTitle": "Diggin' Deep", + "prgchId": "1010958633632668652" + }, + { + "prgEtm": "1767706200000", + "prgStm": "1767700800000", + "prgTitle": "Athletes Unlimited - Lacrosse", + "prgchId": "1010958633632668653" + }, + { + "prgEtm": "1767711600000", + "prgStm": "1767706200000", + "prgTitle": "Athletes Unlimited - Lacrosse", + "prgchId": "1010958633632668654" + }, + { + "prgEtm": "1767715200000", + "prgStm": "1767711600000", + "prgTitle": "World Chase Tag", + "prgchId": "1010958633632668655" + }, + { + "prgEtm": "1767718800000", + "prgStm": "1767715200000", + "prgTitle": "World Chase Tag", + "prgchId": "1010958633632668656" + }, + { + "prgEtm": "1767722400000", + "prgStm": "1767718800000", + "prgTitle": "World Chase Tag", + "prgchId": "1010958633632668657" + }, + { + "prgEtm": "1767726000000", + "prgStm": "1767722400000", + "prgTitle": "OmegaBall", + "prgchId": "1010958633632668658" + }, + { + "prgEtm": "1767733200000", + "prgStm": "1767726000000", + "prgTitle": "Ultimate Frisbee Association", + "prgchId": "1010958633632668659" + }, + { + "prgEtm": "1767740400000", + "prgStm": "1767733200000", + "prgTitle": "Athletes Unlimited - Women's Basketball", + "prgchId": "1010958633632668660" + }, + { + "prgEtm": "1767744000000", + "prgStm": "1767740400000", + "prgTitle": "Major League Wiffle Ball", + "prgchId": "1010958633632668661" + }, + { + "prgEtm": "1767746700000", + "prgStm": "1767744000000", + "prgTitle": "Major League Wiffle Ball", + "prgchId": "1010958633632668662" + }, + { + "prgEtm": "1767751200000", + "prgStm": "1767746700000", + "prgTitle": "Major League Wiffle Ball", + "prgchId": "1010958633632668663" + }, + { + "prgEtm": "1767755400000", + "prgStm": "1767751200000", + "prgTitle": "Diggin' Deep", + "prgchId": "1010958633632668664" + }, + { + "prgEtm": "1767760200000", + "prgStm": "1767755400000", + "prgTitle": "Diggin' Deep", + "prgchId": "1010958633632668665" + }, + { + "prgEtm": "1767762000000", + "prgStm": "1767760200000", + "prgTitle": "Major League Wiffle Ball", + "prgchId": "1010958633632668666" + }, + { + "prgEtm": "1767767400000", + "prgStm": "1767762000000", + "prgTitle": "Ahman Green’s Gamers Lounge", + "prgchId": "1010958633632668667" + }, + { + "prgEtm": "1767774600000", + "prgStm": "1767767400000", + "prgTitle": "Ahman Green’s Gamers Lounge", + "prgchId": "1010958633632668668" + } + ] + }, + { + "chlId": "979187723418476859", + "chlNm": "a-z Western Grit", + "ptList": [ + { + "prgEtm": "1767677820000", + "prgStm": "1767673500000", + "prgTitle": "Brand Of The Outlaws", + "prgchId": "1011995049988787269" + }, + { + "prgEtm": "1767684480000", + "prgStm": "1767677820000", + "prgTitle": "The Wackiest Wagon Train in the West", + "prgchId": "1011995049988787270" + }, + { + "prgEtm": "1767688980000", + "prgStm": "1767684480000", + "prgTitle": "Loser's End", + "prgchId": "1011995049988787271" + }, + { + "prgEtm": "1767696000000", + "prgStm": "1767688980000", + "prgTitle": "Curse of Demon Mountain", + "prgchId": "1011995049988787272" + }, + { + "prgEtm": "1767700500000", + "prgStm": "1767696000000", + "prgTitle": "Border Phantom", + "prgchId": "1011995049988787273" + }, + { + "prgEtm": "1767705000000", + "prgStm": "1767700500000", + "prgTitle": "The Renegade Ranger", + "prgchId": "1011995049988787274" + }, + { + "prgEtm": "1767711840000", + "prgStm": "1767705000000", + "prgTitle": "This Man Can't Die", + "prgchId": "1011995049988787275" + }, + { + "prgEtm": "1767715740000", + "prgStm": "1767711840000", + "prgTitle": "Texas Terror", + "prgchId": "1011995049988787276" + }, + { + "prgEtm": "1767722820000", + "prgStm": "1767715740000", + "prgTitle": "Pancho Villa", + "prgchId": "1012357431483309932" + }, + { + "prgEtm": "1767730320000", + "prgStm": "1767722820000", + "prgTitle": "War Of The Wildcats", + "prgchId": "1012357431483309933" + }, + { + "prgEtm": "1767735000000", + "prgStm": "1767730320000", + "prgTitle": "Aces And Eights", + "prgchId": "1012357431483309934" + }, + { + "prgEtm": "1767739260000", + "prgStm": "1767735000000", + "prgTitle": "Brothers Of The West", + "prgchId": "1012357431483309935" + }, + { + "prgEtm": "1767743760000", + "prgStm": "1767739260000", + "prgTitle": "Between Fighting Men", + "prgchId": "1012357431483309936" + }, + { + "prgEtm": "1767750660000", + "prgStm": "1767743760000", + "prgTitle": "Savage Guns", + "prgchId": "1012357431483309937" + }, + { + "prgEtm": "1767757500000", + "prgStm": "1767750660000", + "prgTitle": "Quell And Co.", + "prgchId": "1012357431483309938" + }, + { + "prgEtm": "1767763860000", + "prgStm": "1767757500000", + "prgTitle": "Vengeance Valley", + "prgchId": "1012357431483309939" + }, + { + "prgEtm": "1767769800000", + "prgStm": "1767763860000", + "prgTitle": "Dakota 38", + "prgchId": "1012357431483309940" + } + ] + } + ], + "errorCode": "0", + "errorMsg": "ok", + "timestamp": "1767681496847" +} \ No newline at end of file diff --git a/sites/watch.whaletvplus.com/readme.md b/sites/watch.whaletvplus.com/readme.md new file mode 100644 index 000000000..bc084e5e5 --- /dev/null +++ b/sites/watch.whaletvplus.com/readme.md @@ -0,0 +1,31 @@ +# watch.whaletvplus.com + +https://watch.whaletvplus.com + +### Download the guide + +```sh +npm run grab --- --site=watch.whaletvplus.com +``` + +### Update channel list + +```sh +npm run channels:parse --- --config=./sites/watch.whaletvplus.com/watch.whaletvplus.com.config.js --output=./sites/watch.whaletvplus.com/watch.whaletvplus.com.channels.xml +``` + +### Test + +```sh +npm test --- watch.whaletvplus.com +``` + +### Fix `apiToken invalid or expired. Please update config.` + +The `apiToken` rarely changes, but if it does: +1. Go to https://watch.whaletvplus.com +2. Open Developer Tools (press `F12` or right-click and select **Inspect**). +3. Select the **Network** tab. +4. Refresh the page. +5. In the "Filter" box, type `apiToken`. +6. Click on any request found and copy the `apiToken` value from the request URL. \ No newline at end of file diff --git a/sites/watch.whaletvplus.com/watch.whaletvplus.com.channels.xml b/sites/watch.whaletvplus.com/watch.whaletvplus.com.channels.xml new file mode 100644 index 000000000..3c9c7dd2a --- /dev/null +++ b/sites/watch.whaletvplus.com/watch.whaletvplus.com.channels.xml @@ -0,0 +1,365 @@ + + + Alaraby News + France 24 (AR) + TRT Arabi + All Romance + ARTFLIX - Filmklassiker + AUTO BILD TV + AUTO MOTOR UND SPORT + BILD TV + Computer BILD + CrimeStar + Crimify + Curiosity Now + DDR TV-Archiv + Deluxe DeutschPop + Deluxe Lounge HD + euronews deutsch + Fabella + Fantasja + FIFA+ (DE) + Focus TV + GoldStar TV + Grjngo - Westernfilme + Heimatkino + Herzfrequenz + Höhenrausch + Just Cooking + just.fishing + Love The Planet (DE) + Moconomy - Wirtschaft und Finanzen (CH) + Moconomy - Wirtschaft und Finanzen (DE/AT) + More Than Sports TV + Motorvision (DE) + Motorvision Classic + Nachrichten 360 + Naruto + Netzkino + One Terra + Red Bull TV (DE) + Royalworld - Adel & Dynastien + SPIEGEL TV + Spiegel TV Action+Crime + Spiegel TV Konflikte + SPORT BILD TV + Sportdigital Free + Tempora + Terra Mater Wild (DE) + Tierwelt Live + TOP Filme + TOP SciFi + TOP Serien + Trailers (DE) + Travelxp (DE) + Utopja + wedotv Movies (DACH) + wedotv Sports + wedotv True Stories + Xplore Free + Yu-Gi-Oh! (DE) + 123GO! (EN) + 5-Minute Crafts + a-z B-Flix + a-z Best Classic TV + a-z Classic Flix + a-z Western Grit + ACL Cornhole TV + Adventure Earth + africanews english + Afroland African + Afroland Comedy + Afroland Crime + Afroland Docus + Afroland Faith + Afroland Family + Afroland Nollywood + Afroland Romance + Afroland Thriller + AfroLandTV + AllHipHop + Arab Heritage TV + Autentic History + Autentic Travel + AWE Plus + Bark TV + BEONDTV + Beyond The Score + Bigtime - Free Movies + Billiards TV + Bloomberg Originals + Bloomberg TV+ + Bollywood 4U + Brat TV + China Travel + Chrono TV + CNA Originals + Confess by Nosey + Cooking Panda + Craftsy TV + crema.tv + Daystar + DeFiance News + DFB Play TV + Discovering China + DIY Art + Docu Vision + Drive+Speed + Drone TV + Drool + Earth Day 365 + Encore+ + Escape TV + euronews english + FailArmy + FIFA+ (EN) + Filmzie + France 24 (EN) + FUEL TV (AU/NZ) + FUEL TV (BE) + FUEL TV (DACH) + FUEL TV (ES) + FUEL TV (FI) + FUEL TV (FR) + FUEL TV (LATAM) + FUEL TV (NL) + FUEL TV (PT) + FUEL TV (SE) + FUEL TV (UK) + Global Biz + Goal TV + GoUSA TV (EN) + Gusto TV + Homerun TV + Hoop TV + Hunt Fish TV + In Touch + + INFAST + INTER 24/7 (EN) + INTRAVEL + INTROUBLE + INWILD + INWONDER + Judge Nosey + Just For Laughs Gags + Just For Laughs TV + LatiNation + Loupe Art + Love The Planet (EN) + Love Wine (EN) + Mercury+ + Moonball Sports TV + MotoRRacing (EN) + Motorvision (EN) + Motorvision (PT) + Movie Mania (EN) + Movie Mania (US) + Movie Music (CET) + Movie Music (EST) + Movieitaly Channel + NEW KFOOD + Newsmax + NHRA TV + Nolly Africa HD + NOMADLife.tv + Nosey + NTD + OAN Plus + Peekflick + People are Awesome + Pet Collective + + PLL Network + PowerSports World + Qwest TV + RACER International + Red Bull TV (AU) + Red Bull TV (EU/MENA) + Red Bull TV (UK) + Revry Global + Revry Her + Royalworld - Nobility & Dynasties + Scifi World + SHIFT + SKWAD + Smart Healthy Green Living + Space Series + Sport Fishing TV + SportOutdoor.TV (EN) + Stingray Cityscapes + Stingray Cozy Café + Stingray Easy Listening + Stingray Naturescape (CET) + Stingray Naturescape (EST) + Stingray Remember the 80s + Stingray Smooth Jazz + Stingray SPA + Stingray Stargaze + Tasty + Terra Mater Wild (EN) + The Boat Show (EN) + The Red Green Channel + TidPix + time2Rlx + Trace Sport Stars + Trace UK + Trace Urban + Trailers (EN) + Travel & Food TV + Travelxp (NL) + Travelxp (UK/IT) + TRT World + UnchainedTV + Unleashed by DOGTV + VladTV + WatchMojo + WaterBear + Weather Spy + wedotv Big Stories (DACH) + wedotv Big Stories (ES) + wedotv Big Stories (FR) + wedotv Big Stories (IT) + wedotv Big Stories (NL) + wedotv Big Stories (SE) + wedotv Movies (BENELUX) + wedotv Movies (DK) + wedotv Movies (FI) + wedotv Movies (NO) + wedotv Movies (SE) + wedotv Movies (UK/IE) + WeShort + Whoa! That was Wild! + World Billiards TV + World Poker Tour (EN) + Yu-Gi-Oh! (EN) + ZenLife (CET) + ZenLife (EST) + 123GO! (ES) + Actualidad 360 + America Television + AYM Sports + Azteca Internacional + Azteca Uno + Backstage En Español + Box Cinema + Box Gamers + Box Playlist + CINDIE + Cine en Español + Cine Friki + Cine Friki Latino + Cine Western + Clic + Comercio TV + Crimen & Historia + Daystar Español + Delito + Docs & Historias + euronews español + FIFA+ (ES/LATAM) + FIFA+ (ES/MX) + Film&Co + France 24 (ES) + FreeTV Acción + FreeTV Clásico + FreeTV Drama (ES/LATAM) + FreeTV Estelar + FreeTV Sureño + GoUSA TV (ES) + Historia Y Vida + Ideas En 5 Minutos + Love The Planet (ES) + Love Wine (ES) + Motorvision (ES) + Naturaleza Salvaje + NEGOCIOS TV + Nesting TV + Pelimex + Play Ibiza + Red Bull TV (ES) + Revry LatinX + Royalworld - Nobleza y Dinastias + Somos Novelas + Tastemade en Español + Tastemade Hogar + Tastemade Viajes + Todo Novelas + Todo Pasíon + Trace Latina + Trailers (ES) + Viajar TV + Viajes & Sabores + wedotv Amor + wedotv Amor: Piel Salvaje + World Poker Tour (ES) + Yu-Gi-Oh! (ES) + Cap Terre + Ciné Nanar + Ciné Western + Drive TV + Emotion'L + Enquêtes De Choc + euronews français + FIFA+ (FR) + France 24 (FR) + Fréquence Novelas + Grjngo - Films de Western + Homicide + IntoCrime + Love Crime & History + Mayday: Catastrophe Aérienne + MotoRRacing (FR) + Motorvision (FR) + Motus + Trailers (FR) + Travelxp (FR) + Voyages & Saveurs + Y'a que la vérité qui compte + Yu-Gi-Oh! (FR) + Adrenaline Movies + Alberto Sordi & Co + Alta Tensione + Bizzarro Movies + Cinema Italiano + CineWestern + euronews italiano + FIFA+ (IT) + Grandi Nomi + Hip Hop TV + Inazuma Eleven Collection + INTER 24/7 (IT) + InuYasha + Love The Planet (IT) + Mayday: Disastro Aereo + Per Caso TV + Rock TV + Smile + Soap Latino + Soap Turco + SportOutdoor.TV (IT) + Storie Criminali + The Boat Show (IT) + Totò & Co + Trailers (IT) + Velvet + Viaggi & Sapori + wedotv Movies (IT) + Yu-Gi-Oh! (IT) + Food ON + NEW KPOP + Travel ON + AUTO MOTOR OG SPORT + euronews português + Feliz7Play + FIFA+ (PT) + FreeTV Crime + FreeTV Drama (BR) + Red Bull TV (BR) + Sessão Trash + Tastemade Brasil + Tastemade Casa + Tastemade Viagem + Trace Brasil + Ubisoft TV Brasil + World Poker Tour (PT) + 澜湄国际 + diff --git a/sites/watch.whaletvplus.com/watch.whaletvplus.com.config.js b/sites/watch.whaletvplus.com/watch.whaletvplus.com.config.js new file mode 100644 index 000000000..d65292e60 --- /dev/null +++ b/sites/watch.whaletvplus.com/watch.whaletvplus.com.config.js @@ -0,0 +1,188 @@ +const axios = require('axios') +const dayjs = require('dayjs') + +const HEADERS = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:147.0) Gecko/20100101 Firefox/147.0', + 'Referer': 'https://watch.whaletvplus.com/', + 'Origin': 'https://watch.whaletvplus.com' +} +const apiToken = '4ef13b5f3d2744e3b0a569feb8dde298' + +let authTokenPromise = null + +module.exports = { + site: 'watch.whaletvplus.com', + days: 2, + + request: { + cache: { + ttl: 60 * 60 * 1000 // Cache 1 hour + }, + headers: async function() { + const token = await getAuthToken() + return { + ...HEADERS, + 'token': token + } + } + }, + + url: function ({ channel, date }) { + const start = date.valueOf() + const end = date.add(1, 'day').valueOf() + + return `https://rlaxx.zeasn.tv/livetv/api/device/browser/v1/epg?channelIds=${channel.site_id}&startTime=${start}&endTime=${end}` + }, + + parser: async function ({ content }) { + let json + try { + json = JSON.parse(content) + } catch (e) { + console.error('Error parsing JSON:', e) + return [] + } + + if (!json.data || !Array.isArray(json.data) || !json.data[0] || !Array.isArray(json.data[0].ptList)) { + return [] + } + + const programs = json.data[0].ptList + const detailsCache = {} + + return await limit(programs, async (p) => { + const program = { + title: p.prgTitle, + start: dayjs(Number(p.prgStm)), + stop: dayjs(Number(p.prgEtm)) + } + + if (p.prgchId) { + if (!detailsCache[p.prgchId]) { + detailsCache[p.prgchId] = fetchProgramDetail(p.prgchId) + } + const detail = await detailsCache[p.prgchId] + if (detail) { + program.description = detail.prgDesc || null + program.season = detail.seasonNumber || null + program.episode = detail.episodeNumber || null + program.sub_title = detail.prgTitle || detail.seriesTitle || null + + if (program.title === program.sub_title) { + program.sub_title = null + } + + if (detail.images && Array.isArray(detail.images)) { + const bestImg = detail.images.find((i) => i.pimgWidth === '1920') || detail.images[0] + if (bestImg) program.image = bestImg.pimgUrl + } + } + } + return program + }) + }, + + async channels() { + const token = await getAuthToken() + + const countries = [ + 'IN', 'AU', 'NZ', 'ZA', 'US', 'BR', 'MX', 'AR', 'CO', 'CL', 'CA', + 'GB', 'DE', 'FR', 'IT', 'ES', 'PL', 'TR', 'AT', 'CH', 'NL', 'PT', + 'BE', 'SE', 'NO', 'DK', 'FI' + ] + + const requests = countries.map(country => + axios.get('https://rlaxx.zeasn.tv/livetv/api/device/browser/v1/category/channels', { + params: { countryCode: country, langCode: 'en' }, + headers: { ...HEADERS, token } + }).then(r => r.data?.data || []).catch(() => []) + ) + + const results = await Promise.all(requests) + const allChannels = results.flat().flatMap(group => group.channels || []) + + const uniqueChannels = new Map() + for (const ch of allChannels) { + if (!uniqueChannels.has(ch.chlId)) { + uniqueChannels.set(ch.chlId, { + lang: (ch.chlLangCode ? ch.chlLangCode.split('-')[0] : 'en'), + site_id: ch.chlId, + name: ch.chlName.trim(), + short_title: ch.chlShortTitle, + // logo: ch.imageIdentifier ? `https://d3b6luslimvglo.cloudfront.net/images/79/rlaxximages/channels-rescaled/icon-white/${ch.imageIdentifier}_white.png` : null + }) + } + } + + return handleDuplicateNames(Array.from(uniqueChannels.values())) + } +} + +async function limit(items, fn, concurrency = 20) { + const results = [] + for (let i = 0; i < items.length; i += concurrency) { + const batch = items.slice(i, i + concurrency) + results.push(...(await Promise.all(batch.map(fn)))) + } + return results +} + +async function getAuthToken() { + if (authTokenPromise) return authTokenPromise + + authTokenPromise = (async () => { + try { + const response = await axios.get('https://rlaxx.zeasn.tv/livetv/api/v1/auth/access', { + params: { uuid: '1', apiToken, langCode: 'en' }, + headers: HEADERS + }) + + if (response.data && response.data.data && response.data.data.token) { + return response.data.data.token + } + + throw new Error('apiToken invalid or expired. Please update config.') + } catch (error) { + authTokenPromise = null + throw new Error(error.message) + } + })() + + return authTokenPromise +} + +async function fetchProgramDetail(programId) { + const token = await getAuthToken() + try { + const response = await axios.get(`https://rlaxx.zeasn.tv/livetv/api/device/browser/v1/epg/detail/${programId}`, { + headers: { + ...HEADERS, + 'token': token + }, + timeout: 5000 + }) + return response.data && response.data.data ? response.data.data : null + } catch { + return null + } +} + +function handleDuplicateNames(channels) { + const counts = {} + channels.forEach(ch => counts[ch.name] = (counts[ch.name] || 0) + 1) + + channels.forEach(ch => { + if (counts[ch.name] > 1) { + let suffix = ch.short_title && ch.short_title.split('_').slice(1).join('_') + if (suffix) { + if (suffix.startsWith('en-') && suffix.length > 3) suffix = suffix.slice(3) + ch.name += ` (${suffix.replace(/-/g, '/').toUpperCase()})` + } else if (ch.lang) { + ch.name += ` (${ch.lang.toUpperCase()})` + } + } + delete ch.short_title + }) + + return channels +} \ No newline at end of file diff --git a/sites/watch.whaletvplus.com/watch.whaletvplus.com.test.js b/sites/watch.whaletvplus.com/watch.whaletvplus.com.test.js new file mode 100644 index 000000000..5b4625005 --- /dev/null +++ b/sites/watch.whaletvplus.com/watch.whaletvplus.com.test.js @@ -0,0 +1,132 @@ +const { parser, url, channels } = require('./watch.whaletvplus.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') +const axios = require('axios') + +dayjs.extend(customParseFormat) +dayjs.extend(utc) + +jest.mock('axios') + +const date = dayjs.utc('2026-01-08', 'YYYY-MM-DD').startOf('d') + +const content = fs.readFileSync(path.resolve(__dirname, '__data__/content.json'), 'utf8') + +it('can generate valid url', () => { + const channel = { site_id: '878765717599035555' } + + const generatedUrl = url({ channel, date }) + + expect(generatedUrl).toBe( + 'https://rlaxx.zeasn.tv/livetv/api/device/browser/v1/epg?channelIds=878765717599035555&startTime=1767830400000&endTime=1767916800000' + ) +}) + +it('can parse response', async () => { + axios.get.mockImplementation((url) => { + if (url.includes('auth/access')) { + return Promise.resolve({ + data: { data: { token: 'mock_token' } } + }) + } + if (url.includes('epg/detail')) { + return Promise.resolve({ + data: { data: { prgDesc: 'Test Description' } } + }) + } + return Promise.resolve({ data: {} }) + }) + + const json = JSON.parse(content) + const firstChannel = json.data && json.data.length > 0 ? json.data[0] : null + const validSiteId = firstChannel ? firstChannel.chlId : '878765717599035555' + + const channel = { + site_id: validSiteId, + xmltv_id: 'Test.Channel' + } + + const result = await parser({ content, channel }) + + expect(result).toBeInstanceOf(Array) + expect(result.length).toBeGreaterThan(0) + + expect(result[0]).toMatchObject({ + title: expect.any(String), + start: expect.any(Object), + stop: expect.any(Object) + }) +}) + +it('can handle empty guide', async () => { + const result = await parser({ + content: '{"data":[]}', + channel: { site_id: '123' } + }) + expect(result).toMatchObject([]) +}) + +it('can parse channel list', async () => { + axios.get.mockImplementation((reqUrl) => { + if (reqUrl.includes('auth/access')) { + return Promise.resolve({ + data: { data: { token: 'mock_token_123' } } + }) + } + + if (reqUrl.includes('category/channels')) { + return Promise.resolve({ + data: { + data: [ + { + channels: [ + { + chlId: '878765717599035555', + chlName: 'Wedo Movies', + chlLangCode: 'en' + }, + { + chlId: '999420644834214633', + chlName: 'FIFA+', + chlLangCode: 'es' + } + ] + } + ] + } + }) + } + return Promise.resolve({ data: {} }) + }) + + const result = await channels() + + expect(result).toBeInstanceOf(Array) + expect(result.length).toBeGreaterThan(0) + expect(result[0]).toMatchObject({ + name: expect.any(String), + site_id: expect.any(String), + lang: expect.any(String) + }) +}) + +it('can parse token', async () => { + jest.resetModules() + const { request } = require('./watch.whaletvplus.com.config.js') + const axios = require('axios') + + axios.get.mockImplementation((url) => { + if (url.includes('auth/access')) { + return Promise.resolve({ + data: { data: { token: 'test_token' } } + }) + } + return Promise.resolve({ data: {} }) + }) + + const headers = await request.headers() + expect(headers.token).toBe('test_token') +}) \ No newline at end of file