From 119002fbbf17d712d2a3a1ddef04a614b4dbbd3d Mon Sep 17 00:00:00 2001 From: xocko Date: Sat, 7 Nov 2026 11:01:37 +0100 Subject: [PATCH] 1.0.0+kodimov.2: programme guide for every channel, TeleVigo on 15 - The amazon.tv guide that upstream 1.0.0 switched to is a flat list of programmes; group it by channel (the guide export crashed with KeyError) - Add TVG2/TeleVigo listings from the EPG_dobleM guide to epg.xml - Export channels + programme guide every 6 hours, by default, without on-screen notifications - TeleVigo moves to 15, near the generalist channels Co-Authored-By: Claude Opus 5.5 --- README.md | 5 ++- __pycache__/service.cpython-315.pyc | Bin 3219 -> 3223 bytes addon.xml | 9 +++-- .../lib/__pycache__/kodimov.cpython-315.pyc | Bin 11185 -> 13742 bytes .../lib/__pycache__/movistar.cpython-315.pyc | Bin 96675 -> 97119 bytes .../lib/__pycache__/plugin.cpython-315.pyc | Bin 56480 -> 56631 bytes resources/lib/kodimov.py | 37 +++++++++++++++++- resources/lib/movistar.py | 14 ++++++- resources/lib/plugin.py | 15 ++++--- resources/settings.xml | 2 +- service.py | 14 +++---- 11 files changed, 70 insertions(+), 26 deletions(-) diff --git a/README.md b/README.md index 46238dd..6b4c979 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,9 @@ What it adds: - **Login from the Movistar home network**, no password. The home-network login gives a token for Movistar's new servers, which is traded once for a classic token; the classic servers renew it every 10 days. It needs the account number (taken from an existing login, or set in the settings) and an already registered device. - **Takes over the login of plugin.video.movistarplus** on first run (same registered device, so no new device is added to the account). - **Hides channels it can't play**: Movistar marks football and premium sports with `QMWatermark`; LALIGA and Liga de Campeones refuse their manifest, while M+ Deportes, DAZN and DAZN Baloncesto play and are kept (`resources/lib/kodimov.py`). -- **Extra free channels** in the TV guide list: TVG2 (10) and TeleVigo (155). -- **The TV guide list stays current**: re-exported every 6 hours, numbered with Movistar's own dial numbers. +- **Extra free channels** in the TV guide list: TVG2 (10) and TeleVigo (15). +- **The TV guide stays current**: channel list and programme guide re-exported every 6 hours, numbered with Movistar's own dial numbers. Listings come from Movistar (with catch-up links) for its channels and from the public EPG_dobleM guide for the extras. +- Fixes the programme guide export of upstream 1.0.0 (the amazon.tv guide is a flat list). - Its own proxy port (57031), so it can run next to the original. --- diff --git a/__pycache__/service.cpython-315.pyc b/__pycache__/service.cpython-315.pyc index 8ac43a4478a3328af0b94cd5bd0b7f93e62d8f64..f4f5e96bc04f46098f25eadfff6fbbd0d3561305 100644 GIT binary patch delta 351 zcmbO%IbD)Bh>w?x0SF4dyvzDEkyn!O+(h*>Mx%*+t&;;8_0$DpSo~E$@~A+b!3Zi) zEF2^3uQ~Z0lhot`jGUYc85pD(fFjzHFEMH}c1#XnRA7n`oUFj4FUYHfWJa-IjM(H0 zj3SdY7=>9xftn^CWReo(GGc_Nk!A4eWMF_OkqYF739CX)NMi~%|T2hjkm+q9GpEFsXTbq%8ax`};(+7^pceztFXXISx)V{>2eS?GNI*0Tn t4(SUrnb$b7Zg2}+=a#?7Eq_75@H)5A6>g)+(L9oznhY#bMdCn*0sxIRQ6T^T delta 382 zcmbO(Ia!i7h>w?x0SH7syvypF$ScYCZK8S_qy5CbR%Ste)yXPMs=S;G3{nh|3^CmD z3`Pu-xfsn^g=1L!H6}k`5#cie=>>vf(HK#G-N_$VrG!)=A`EH$Abmh#{mD}pwHeDM zA7E7E6E@q!z>vlalJRn5m@LSss1eAc1=RsINH_*)FOmS18OSxck4c=poPhx(4{{d= z#FPmPlYN<_c)5)jAu44Vyp~Kp$D}17#~{lf&EWmil3AJ|gHe-r^Dm~YY@7dZvNBG# z;})C5Z96%HTbog3aszj&>;qP=>#UL&StV!aTxV6k#H#**iJ6uACYwM@&Ig{!&$&|r zFUaX%=QOy&X|O}|x~1<$OWzYY*Da&3SVrIA;JMBreThT*f=t>qj`Rmi?Cn~OT5UQX h4H;OZetZyO1ewG2tC$DqyhA+ft|kmeO_*FQm;q|`SY!YI diff --git a/addon.xml b/addon.xml index dcc4869..2083ba7 100644 --- a/addon.xml +++ b/addon.xml @@ -1,7 +1,7 @@ @@ -19,9 +19,10 @@ Movistar+ for Kodi, with automatic login from the Movistar home network Movistar+ para Kodi, con inicio de sesión automático desde la red de Movistar - Fork of Paco8's Movistar+ add-on (plugin.video.movistarplus). Logs in by itself from the Movistar home network, keeps the channel list and numbers up to date, hides the channels that need stronger DRM than Kodi has, and adds free Galician channels (TVG2, TeleVigo) to the TV guide. Requires a Movistar+ subscription. - Fork del addon Movistar+ de Paco8 (plugin.video.movistarplus). Inicia sesión solo desde la red de Movistar, mantiene la lista y los números de los canales al día, oculta los canales que necesitan una protección que Kodi no tiene y añade canales gallegos gratuitos (TVG2, TeleVigo) a la guía. Se necesita estar abonado a Movistar+. - 1.0.0+kodimov.1: first KodiMov+ release, based on Movistar+ 1.0.0. + Fork of Paco8's Movistar+ add-on (plugin.video.movistarplus). Logs in by itself from the Movistar home network, keeps the channel list and numbers up to date, hides the channels that need stronger DRM than Kodi has, adds free Galician channels (TVG2, TeleVigo) and writes a programme guide for every channel. Requires a Movistar+ subscription. + Fork del addon Movistar+ de Paco8 (plugin.video.movistarplus). Inicia sesión solo desde la red de Movistar, mantiene la lista y los números de los canales al día, oculta los canales que necesitan una protección que Kodi no tiene añade canales gallegos gratuitos (TVG2, TeleVigo) y genera la guía de programación de todos los canales. Se necesita estar abonado a Movistar+. + 1.0.0+kodimov.2: programme guide for every channel (Movistar + TVG2/TeleVigo), TeleVigo on 15, silent background refresh. +1.0.0+kodimov.1: first KodiMov+ release, based on Movistar+ 1.0.0. This addon is not officially commissioned/supported by Movistar+. All product names, logos, and trademarks mentioned in this project are property of their respective owners. Este addon no ha sido realizado ni apoyado por Movistar+. Todos los nombres de productos, logotipos y marcas registradas mencionados en este proyecto son propiedad de sus respectivos dueños. all diff --git a/resources/lib/__pycache__/kodimov.cpython-315.pyc b/resources/lib/__pycache__/kodimov.cpython-315.pyc index 303f641370e57998c4edfad2d5b22c9c4635a5ad..475e7de6f024d96c1fba0bd2b9c2882754da3a6c 100644 GIT binary patch delta 3398 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 F_&4rkDbN4_ diff --git a/resources/lib/__pycache__/movistar.cpython-315.pyc b/resources/lib/__pycache__/movistar.cpython-315.pyc index d6c3a0e020aac9cd47a84f148c00bb78384f433f..12217c932259058d601d4577829d7c5f33071fb0 100644 GIT binary patch delta 5608 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 M(ek4X|0=ov2W&WUYXATM delta 5262 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 Rqv}Uc+QzOy1O6(#e*#yAC5He2 diff --git a/resources/lib/__pycache__/plugin.cpython-315.pyc b/resources/lib/__pycache__/plugin.cpython-315.pyc index 7ee219e9cfc1ef4d3155534fe2264c9424f4492c..c785eb5c2ae1393601888e618a1525e410bef155 100644 GIT binary patch delta 2416 zcma)8eN0nV6o0p`E%XCnwxCizhYD3eKwA}XI5WlR{3s4}VGgk?riv}TT8$rz8kHgG z6g-iknFEK8pbT(N!*ost3XEZNsG*~}pz)6_OSY)EWXnF>J@-|ujz4yjciw&H{O&ow z`*F^FwdeTO^Zbb15g}@hwvtD`uJs)RvQMYP&!O_ui4Iv%vLz{;TSvrdS;}700Nw9r zl2YKyKABodI8InbfkFWlK8;{o@7!c7D6$vZguQ5sLR%!-l*P_9PWMKkTpTEK@k$R` zJ&@~}mBXAYMPMEkMx{`RHb8LzMFkbUAeY-}wYgkAQs6>rajY@dLKUK_!~xG4o_Jt? z#U645bd`zZn3!3aD5F%ZFrL(l<*=HN0eG&PAAt2~lxJrq>G9C7jmR12b7 z#1E@G?j>k(2$h5aW~B0C`hMLFsop{@x|9yO2_=sH?O zTE&$gcgRT<#MMQTR>-XTKI}_GFGs%jP*g#}@ifvd<{j7a)Iq|jL&_f1_rk?f6{HsyHx>}D*xa~7Mmk|~vzy$86V28c6!Ns;`t$S|N~Pfl<52ClTshpR1F zI(a(vg3T$g9Y2c(;4HHi5B9^5;0Y-CUxaXh;R_UR^tby|? zojgyTFNeNul%A=Rw%^Sg0|pXD*+gFP1(+=(4&-Q_%0bxIsi`UiMx9ow2%XfUz=Gv-klGv( zBn9A3QGKYx~9e~U7 z<7N(nOA5ff;>TqVgPRzDn93Eq=#|c`q1D2{A*NS)>R1Gx>c+a{N0rV zeH5b&z~uO2&l@JT{=c{duMdNpAAnn6F`MYg!WPZtTcsYyEm2$Ec(O;)EZ>a_A?lmE z(^=hS7Z4V9;Wi3Zyjjg>*F!7nVyP#oFta*c4yK;{MlV{fqrhzNku)Qb@?rqdSeEUF zu=0!WLr=BtbNaniQ)sn1-HweZC5|=1c_{m~H0A$NXvaY1kkz{{@>$@gk%Txjcfw%r z+v>k)Hx=`1g*fNRW?olquE{u@QM=}bA*Cgu&tUQ@&0^cNOl4>t1uj}74AB>*J_M@% zX`|RW96=2){6k-Yq=4o97wGM*_l_e&kl{6x$FRYhJq1G<<-$ieVRR!6nM8XoEXZ-R zN#&9k+zvrY3+Q3-p7&2euEOAeN85%&>^N8+dQ@%Y*mmNi}>QNOmNnE=ezD=+bhBt%7>yj*Aglp@PvqqZkdVUkyb}0 XJe;H@Qy#_?5i<5sE+0=`VukWwZZK?9 delta 1953 zcma)7eN0nV6o2=cT z86B~a2m&>-6dJ|MfU?L@=qN9vRr1|Rr=;A5=r+i%o)NnVr}Hsz0W%gQ-HDD`G~3ba zfD6?or3Q%9EHcbGD(9x)s;ZV z5m;Jht6{w`CL>JMm5R%>f05g53%`4 zLI)U!{ikx=7B~(>k2{VbTbQx`jLrsp3`0|ez9;Bwgfj=CNfUG*s3Yg){DbWZBc5wj zfGK|US_>VEMbKl40nK^HINBfFhSP#ysb~6J>7o*^QgOhZ_{L{IlPnJ)w6GvnUAb8KqSS6&#SXdn`m%yac8*I#}OqfV^EQC|;w5Q_abem@LBXW@}Ig zi{>Lo0X1z#7)n#adpj((X=)M)pPto6#wIgP95F(qkn?=_`)$v{VbvOgBr%q?nizP{ zri#cW$%<#0FXU-=VqUy*?xR>Av> z_uL1Jls6s?#hDGwN>(8Yp%RvqU4j`N${9f#R$XU2d)y#^wl+L^VQ<*1&gGujdG6VH zeTsZ25K|3NK z@!X7rJEDHqERSJ=+c4pa1-*upmW7_wEO%WhO!fTgXa zncdU5~Pdr!zTN%Nms)BxYB%FQ#e{jiw~BiHiDoBXuutMPE}szImmi+Ib&JB#xgUPR#F zHG^)ppFfjj6nBM(yz{JXF#_-Ncfr;5{S>ZBTs%>>D0>?|Y2u2;#qT`&xb|>`;et~I zXTI4U)rSaguG{Q=P?Qh53x(`Uuyxn#&$8!LLHPv*j`EVS#L|*N+6uuvn-l-bt~#9j86x$q;cdwdQNY>=^YI0o~w-+5EqS+D=Ex%TLagpI4UGn3tCymCG-$DSr(kg~dHk wT#;^suLdL3dNyS)!D*H64^Ah>VNEcZONPxFk}^DI2_fU2%#&hC2$!6{0V~fgZ~y=R diff --git a/resources/lib/kodimov.py b/resources/lib/kodimov.py index a82391d..997db50 100644 --- a/resources/lib/kodimov.py +++ b/resources/lib/kodimov.py @@ -33,14 +33,17 @@ WATERMARKED_BUT_PLAYABLE = { # Free channels that Movistar+ doesn't carry. 'chno' must not clash with a # Movistar dial number. EXTRA_CHANNELS = [ - {'id': 'TVG2', 'name': 'TVG2', 'chno': 10, 'group': 'Galicia', + {'id': 'TVG2', 'name': 'TVG2', 'chno': 10, 'group': 'Galicia', 'epg_id': 'TVG2', 'logo': 'https://i.imgur.com/yFy6gxg.png', 'url': 'https://crtvg-tvg2.flumotion.cloud/playlist.m3u8'}, - {'id': 'TeleVigo', 'name': 'TeleVigo', 'chno': 155, 'group': 'Galicia', + {'id': 'TeleVigo', 'name': 'TeleVigo', 'chno': 15, 'group': 'Galicia', 'epg_id': 'TELEVIGO', 'logo': 'https://i.imgur.com/4iVmlFa.png', 'url': 'https://stream-us-east-1.getpublica.com/playlist.m3u8?network_id=12687'}, ] +# Programme listings for the extra channels (Movistar's guide covers the rest). +EXTRA_EPG_URL = 'https://raw.githubusercontent.com/davidmuma/EPG_dobleM/master/guiafanart_color.xml.gz' + AUTOLOGIN_URL = 'https://soter-pf.svp.video.telefonicaservices.com/service/login/autologin?deviceId={device}&deviceType=webplayer&_={ts}' SOTER_INITDATA_URL = 'https://soter-pf.sve.video.telefonicaservices.com/service/login/initData?legacy=true' @@ -167,3 +170,33 @@ def write_if_changed(filename, content): f.write(content) os.replace(tmp, filename) if hasattr(os, 'replace') else shutil.move(tmp, filename) return True + + +def extra_channels_xmltv(session): + """Returns the and elements for the extra channels, + taken from a public XMLTV guide and renamed to their tvg-id.""" + import gzip + import xml.etree.ElementTree as ET + wanted = dict((c['epg_id'], c) for c in EXTRA_CHANNELS if c.get('epg_id')) + out = [] + for c in EXTRA_CHANNELS: + out.append('\n {name}\n \n\n'.format(**c)) + try: + r = session.get(EXTRA_EPG_URL, timeout=(10, 120)) + r.raise_for_status() + found = 0 + for _, el in ET.iterparse(gzip.GzipFile(fileobj=io.BytesIO(r.content))): + if el.tag == 'programme': + c = wanted.get(el.get('channel')) + if c: + el.set('channel', c['id']) + for icon in el.findall('icon'): el.remove(icon) # large fan art + out.append(ET.tostring(el, encoding='unicode') + '\n') + found += 1 + el.clear() + elif el.tag == 'channel': + el.clear() + LOG('extra_channels_xmltv: {} programmes'.format(found)) + except Exception as e: + LOG('extra_channels_xmltv: failed: {}'.format(e)) + return out diff --git a/resources/lib/movistar.py b/resources/lib/movistar.py index a5e9e2d..595c5ee 100644 --- a/resources/lib/movistar.py +++ b/resources/lib/movistar.py @@ -540,7 +540,16 @@ class Movistar(object): epg = {} if 'error' in data: return epg + # The amazon.tv guide is one flat list of programmes; webplayer's is + # grouped by channel. Group the flat one the same way. + if data and isinstance(data[0], dict): + groups = {} + for p in data: + groups.setdefault(p['Canal']['CodCadenaTv'], []).append(p) + data = list(groups.values()) + for ch in data: + if not ch: continue id = ch[0]['Canal']['CodCadenaTv'] if not id in epg: epg[id] = [] for p in ch: @@ -1537,9 +1546,10 @@ class Movistar(object): res.append(' {}\n'.format(c['name'])) res.append(' \n'); res.append('\n') + if self.add_extra_channels: + res.extend(kodimov.extra_channels_xmltv(self.net.session)) res.append('\n') - with io.open(filename, 'w', encoding='utf-8', newline='') as handle: - handle.write(''.join(res)) + kodimov.write_if_changed(filename, ''.join(res)) def save_credentials(self, username, password): from .b64 import encode_base64 diff --git a/resources/lib/plugin.py b/resources/lib/plugin.py index c7691a9..6bd0b26 100644 --- a/resources/lib/plugin.py +++ b/resources/lib/plugin.py @@ -776,23 +776,26 @@ def export_channels_now(): if m.export_channels_to_m3u8(channels_filename, addon.getSettingBool('only_subscribed')): LOG('export_channels_now: channel list changed') -def export_epg_now(): +def export_epg_now(quiet=False): + # quiet: background refresh from the service, no pop-ups while watching TV + def notify(message): + if not quiet: show_notification(message, xbmcgui.NOTIFICATION_INFO) def report_progress(message): - show_notification(addon.getLocalizedString(30314).format(message), xbmcgui.NOTIFICATION_INFO) + notify(addon.getLocalizedString(30314).format(message)) if not m.logged: return only_subscribed = addon.getSettingBool('only_subscribed') channels_filename = os.path.join(profile_dir, 'channels.m3u8') epg_filename = os.path.join(profile_dir, 'epg.xml') - show_notification(addon.getLocalizedString(30310), xbmcgui.NOTIFICATION_INFO) + notify(addon.getLocalizedString(30310)) m.export_channels_to_m3u8(channels_filename, only_subscribed) if True: #not addon.getSettingBool('use_external_epg'): - show_notification(addon.getLocalizedString(30311), xbmcgui.NOTIFICATION_INFO) + notify(addon.getLocalizedString(30311)) m.export_epg_to_xml(epg_filename, addon.getSettingInt('export_days'), report_progress, only_subscribed) - show_notification(addon.getLocalizedString(30313), xbmcgui.NOTIFICATION_INFO) + notify(addon.getLocalizedString(30313)) def to_wishlist(params): @@ -886,7 +889,7 @@ def router(paramstring): elif params['action'] == 'create_iptv_settings': create_iptv_settings() elif params['action'] == 'export_epg_now': - export_epg_now() + export_epg_now(quiet=params.get('quiet') == '1') elif params['action'] == 'to_wishlist': to_wishlist(params) elif 'iptv' in params['action']: diff --git a/resources/settings.xml b/resources/settings.xml index 44293c7..9b76087 100644 --- a/resources/settings.xml +++ b/resources/settings.xml @@ -62,7 +62,7 @@ --> - + diff --git a/service.py b/service.py index 9e29564..958c9b6 100644 --- a/service.py +++ b/service.py @@ -38,14 +38,8 @@ if __name__ == '__main__': proxy.start() LOG('proxy_address: {}'.format(proxy.proxy_address)) - if addon.getSettingBool('export_epg'): - try: - export_epg() - except: - pass - - # Refreshes the TV guide channel list (new channels, new numbers) every - # 6 hours. Opening the add-on also renews the 10-day Movistar login. + # Refreshes the TV guide channel list (new channels, new numbers) and the + # programme guide a minute after Kodi starts and then every 6 hours. Opening the add-on also renews the 10-day Movistar login. next_export = time.time() + 60 monitor = xbmc.Monitor() while not monitor.abortRequested(): @@ -53,7 +47,9 @@ if __name__ == '__main__': break if time.time() >= next_export: next_export = time.time() + 6 * 3600 - if addon.getSettingBool('export_channels') and not addon.getSettingBool('export_epg'): + if addon.getSettingBool('export_epg'): # channels + programme guide + xbmc.executebuiltin('RunPlugin(plugin://plugin.video.kodimovplus/?action=export_epg_now&quiet=1)') + elif addon.getSettingBool('export_channels'): xbmc.executebuiltin('RunPlugin(plugin://plugin.video.kodimovplus/?action=export_channels_now)') proxy.stop()