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
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
|
||||
# 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 <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 = {}
|
||||
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(' <actor>{}</actor>\n'.format(c['name']))
|
||||
res.append(' </credits>\n');
|
||||
res.append('</programme>\n')
|
||||
if self.add_extra_channels:
|
||||
res.extend(kodimov.extra_channels_xmltv(self.net.session))
|
||||
res.append('</tv>\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
|
||||
|
||||
@@ -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']:
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
-->
|
||||
|
||||
<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 type="sep"/>
|
||||
<setting label="30312" type="action" id="export_epg_now" action="RunPlugin(plugin://plugin.video.kodimovplus/?action=export_epg_now)" option="close"/>
|
||||
|
||||
Reference in new issue
Block a user