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 <noreply@anthropic.com>
This commit is contained in:
1 parent
235f7a6cab
commit
119002fbbf
11 files changed
+70
-26
No files matched your search
@@ -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.
|
- **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).
|
- **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`).
|
- **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).
|
- **Extra free channels** in the TV guide list: TVG2 (10) and TeleVigo (15).
|
||||||
- **The TV guide list stays current**: re-exported every 6 hours, numbered with Movistar's own dial numbers.
|
- **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.
|
- Its own proxy port (57031), so it can run next to the original.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|||||||
Binary file not shown.
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||||
<addon id="plugin.video.kodimovplus"
|
<addon id="plugin.video.kodimovplus"
|
||||||
name="KodiMov+"
|
name="KodiMov+"
|
||||||
version="1.0.0+kodimov.1"
|
version="1.0.0+kodimov.2"
|
||||||
provider-name="Paco8, xocko">
|
provider-name="Paco8, xocko">
|
||||||
<requires>
|
<requires>
|
||||||
<!--- <import addon="xbmc.python" version="2.25.0"/> -->
|
<!--- <import addon="xbmc.python" version="2.25.0"/> -->
|
||||||
@@ -19,9 +19,10 @@
|
|||||||
<extension point="xbmc.addon.metadata">
|
<extension point="xbmc.addon.metadata">
|
||||||
<summary lang="en_GB">Movistar+ for Kodi, with automatic login from the Movistar home network</summary>
|
<summary lang="en_GB">Movistar+ for Kodi, with automatic login from the Movistar home network</summary>
|
||||||
<summary lang="es_ES">Movistar+ para Kodi, con inicio de sesión automático desde la red de Movistar</summary>
|
<summary lang="es_ES">Movistar+ para Kodi, con inicio de sesión automático desde la red de Movistar</summary>
|
||||||
<description lang="en_GB">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.</description>
|
<description lang="en_GB">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.</description>
|
||||||
<description lang="es_ES">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+.</description>
|
<description lang="es_ES">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+.</description>
|
||||||
<news>1.0.0+kodimov.1: first KodiMov+ release, based on Movistar+ 1.0.0.</news>
|
<news>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.</news>
|
||||||
<disclaimer lang="en_GB">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.</disclaimer>
|
<disclaimer lang="en_GB">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.</disclaimer>
|
||||||
<disclaimer lang="es_ES">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.</disclaimer>
|
<disclaimer lang="es_ES">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.</disclaimer>
|
||||||
<platform>all</platform>
|
<platform>all</platform>
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -33,14 +33,17 @@ WATERMARKED_BUT_PLAYABLE = {
|
|||||||
# Free channels that Movistar+ doesn't carry. 'chno' must not clash with a
|
# Free channels that Movistar+ doesn't carry. 'chno' must not clash with a
|
||||||
# Movistar dial number.
|
# Movistar dial number.
|
||||||
EXTRA_CHANNELS = [
|
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',
|
'logo': 'https://i.imgur.com/yFy6gxg.png',
|
||||||
'url': 'https://crtvg-tvg2.flumotion.cloud/playlist.m3u8'},
|
'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',
|
'logo': 'https://i.imgur.com/4iVmlFa.png',
|
||||||
'url': 'https://stream-us-east-1.getpublica.com/playlist.m3u8?network_id=12687'},
|
'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}'
|
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'
|
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)
|
f.write(content)
|
||||||
os.replace(tmp, filename) if hasattr(os, 'replace') else shutil.move(tmp, filename)
|
os.replace(tmp, filename) if hasattr(os, 'replace') else shutil.move(tmp, filename)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def extra_channels_xmltv(session):
|
||||||
|
"""Returns the <channel> and <programme> 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('<channel id="{id}">\n <display-name>{name}</display-name>\n <icon src="{logo}"/>\n</channel>\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
|
||||||
@@ -540,7 +540,16 @@ class Movistar(object):
|
|||||||
epg = {}
|
epg = {}
|
||||||
if 'error' in data: return 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:
|
for ch in data:
|
||||||
|
if not ch: continue
|
||||||
id = ch[0]['Canal']['CodCadenaTv']
|
id = ch[0]['Canal']['CodCadenaTv']
|
||||||
if not id in epg: epg[id] = []
|
if not id in epg: epg[id] = []
|
||||||
for p in ch:
|
for p in ch:
|
||||||
@@ -1537,9 +1546,10 @@ class Movistar(object):
|
|||||||
res.append(' <actor>{}</actor>\n'.format(c['name']))
|
res.append(' <actor>{}</actor>\n'.format(c['name']))
|
||||||
res.append(' </credits>\n');
|
res.append(' </credits>\n');
|
||||||
res.append('</programme>\n')
|
res.append('</programme>\n')
|
||||||
|
if self.add_extra_channels:
|
||||||
|
res.extend(kodimov.extra_channels_xmltv(self.net.session))
|
||||||
res.append('</tv>\n')
|
res.append('</tv>\n')
|
||||||
with io.open(filename, 'w', encoding='utf-8', newline='') as handle:
|
kodimov.write_if_changed(filename, ''.join(res))
|
||||||
handle.write(''.join(res))
|
|
||||||
|
|
||||||
def save_credentials(self, username, password):
|
def save_credentials(self, username, password):
|
||||||
from .b64 import encode_base64
|
from .b64 import encode_base64
|
||||||
|
|||||||
@@ -776,23 +776,26 @@ def export_channels_now():
|
|||||||
if m.export_channels_to_m3u8(channels_filename, addon.getSettingBool('only_subscribed')):
|
if m.export_channels_to_m3u8(channels_filename, addon.getSettingBool('only_subscribed')):
|
||||||
LOG('export_channels_now: channel list changed')
|
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):
|
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
|
if not m.logged: return
|
||||||
|
|
||||||
only_subscribed = addon.getSettingBool('only_subscribed')
|
only_subscribed = addon.getSettingBool('only_subscribed')
|
||||||
channels_filename = os.path.join(profile_dir, 'channels.m3u8')
|
channels_filename = os.path.join(profile_dir, 'channels.m3u8')
|
||||||
epg_filename = os.path.join(profile_dir, 'epg.xml')
|
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)
|
m.export_channels_to_m3u8(channels_filename, only_subscribed)
|
||||||
|
|
||||||
if True: #not addon.getSettingBool('use_external_epg'):
|
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)
|
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):
|
def to_wishlist(params):
|
||||||
@@ -886,7 +889,7 @@ def router(paramstring):
|
|||||||
elif params['action'] == 'create_iptv_settings':
|
elif params['action'] == 'create_iptv_settings':
|
||||||
create_iptv_settings()
|
create_iptv_settings()
|
||||||
elif params['action'] == 'export_epg_now':
|
elif params['action'] == 'export_epg_now':
|
||||||
export_epg_now()
|
export_epg_now(quiet=params.get('quiet') == '1')
|
||||||
elif params['action'] == 'to_wishlist':
|
elif params['action'] == 'to_wishlist':
|
||||||
to_wishlist(params)
|
to_wishlist(params)
|
||||||
elif 'iptv' in params['action']:
|
elif 'iptv' in params['action']:
|
||||||
|
|||||||
@@ -62,7 +62,7 @@
|
|||||||
-->
|
-->
|
||||||
|
|
||||||
<setting label="30508" type="bool" id="export_channels" default="true"/>
|
<setting label="30508" type="bool" id="export_channels" default="true"/>
|
||||||
<setting label="30305" type="bool" id="export_epg" default="false"/>
|
<setting label="30305" type="bool" id="export_epg" default="true"/>
|
||||||
<setting label="30315" type="number" id="export_days" default="2" enable="eq(-1,true)"/>
|
<setting label="30315" type="number" id="export_days" default="2" enable="eq(-1,true)"/>
|
||||||
<setting type="sep"/>
|
<setting type="sep"/>
|
||||||
<setting label="30312" type="action" id="export_epg_now" action="RunPlugin(plugin://plugin.video.kodimovplus/?action=export_epg_now)" option="close"/>
|
<setting label="30312" type="action" id="export_epg_now" action="RunPlugin(plugin://plugin.video.kodimovplus/?action=export_epg_now)" option="close"/>
|
||||||
|
|||||||
+5
-9
@@ -38,14 +38,8 @@ if __name__ == '__main__':
|
|||||||
proxy.start()
|
proxy.start()
|
||||||
LOG('proxy_address: {}'.format(proxy.proxy_address))
|
LOG('proxy_address: {}'.format(proxy.proxy_address))
|
||||||
|
|
||||||
if addon.getSettingBool('export_epg'):
|
# Refreshes the TV guide channel list (new channels, new numbers) and the
|
||||||
try:
|
# programme guide a minute after Kodi starts and then every 6 hours. Opening the add-on also renews the 10-day Movistar login.
|
||||||
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.
|
|
||||||
next_export = time.time() + 60
|
next_export = time.time() + 60
|
||||||
monitor = xbmc.Monitor()
|
monitor = xbmc.Monitor()
|
||||||
while not monitor.abortRequested():
|
while not monitor.abortRequested():
|
||||||
@@ -53,7 +47,9 @@ if __name__ == '__main__':
|
|||||||
break
|
break
|
||||||
if time.time() >= next_export:
|
if time.time() >= next_export:
|
||||||
next_export = time.time() + 6 * 3600
|
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)')
|
xbmc.executebuiltin('RunPlugin(plugin://plugin.video.kodimovplus/?action=export_channels_now)')
|
||||||
|
|
||||||
proxy.stop()
|
proxy.stop()
|
||||||
Reference in new issue
Block a user