1.0.0+kodimov.7: synopsis in the programme guide; hourly IPTV Simple reload
Movistar's guide already carries the synopsis (Resena), genre, year, age rating and pictures; write them to epg.xml. IPTV Simple was set to reload the channel list and guide once a day at 4:00, so a new epg.xml didn't reach Kodi until then. Use its hourly reload, which also makes Kodi fetch the guide again. The service switches existing TV guide setups over once and restarts IPTV Simple when nothing is playing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
09d6a68f74
commit
9faaa482fc
6 files changed
+66
-10
No files matched your search
@@ -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.6"
|
version="1.0.0+kodimov.7"
|
||||||
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"/> -->
|
||||||
@@ -21,7 +21,8 @@
|
|||||||
<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, adds free Galician channels (TVG2, TeleVigo) and writes a programme guide for every channel. 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 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>
|
<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.6: the programme guide refreshes on its own again on boxes first set up with kodimov.1, where the automatic guide export had stayed off (the guide ran out after 2 days).
|
<news>1.0.0+kodimov.7: the programme guide has the synopsis, picture, genre, year and age rating of each programme. IPTV Simple now reloads the guide every hour instead of once a day, so a new guide shows up in Kodi by itself (existing TV guide setups are switched over once).
|
||||||
|
1.0.0+kodimov.6: the programme guide refreshes on its own again on boxes first set up with kodimov.1, where the automatic guide export had stayed off (the guide ran out after 2 days).
|
||||||
1.0.0+kodimov.5: TV guide setup writes the programme guide before creating the IPTV Simple settings, so the guide isn't empty on a new box.
|
1.0.0+kodimov.5: TV guide setup writes the programme guide before creating the IPTV Simple settings, so the guide isn't empty on a new box.
|
||||||
1.0.0+kodimov.4: first login on a new box needs no password from the Movistar home network: the account is found and the box registered automatically.
|
1.0.0+kodimov.4: first login on a new box needs no password from the Movistar home network: the account is found and the box registered automatically.
|
||||||
1.0.0+kodimov.3: one-tap TV setup: "Create settings for IPTV Simple" also turns on Movistar channel numbers, daily list reload, and restarts IPTV Simple; fixes invalid XML in that file.
|
1.0.0+kodimov.3: one-tap TV setup: "Create settings for IPTV Simple" also turns on Movistar channel numbers, daily list reload, and restarts IPTV Simple; fixes invalid XML in that file.
|
||||||
|
|||||||
@@ -15,8 +15,8 @@ def save_iptv_settings(filename, name, addon_id, epg_url=None):
|
|||||||
<setting id="m3uCache" default="true">true</setting>
|
<setting id="m3uCache" default="true">true</setting>
|
||||||
<setting id="startNum" default="true">1</setting>
|
<setting id="startNum" default="true">1</setting>
|
||||||
<setting id="numberByOrder" default="true">false</setting>
|
<setting id="numberByOrder" default="true">false</setting>
|
||||||
<setting id="m3uRefreshMode">2</setting>
|
<setting id="m3uRefreshMode">1</setting>
|
||||||
<setting id="m3uRefreshIntervalMins" default="true">60</setting>
|
<setting id="m3uRefreshIntervalMins">60</setting>
|
||||||
<setting id="m3uRefreshHour" default="true">4</setting>
|
<setting id="m3uRefreshHour" default="true">4</setting>
|
||||||
<setting id="connectioncheckinterval" default="true">10</setting>
|
<setting id="connectioncheckinterval" default="true">10</setting>
|
||||||
<setting id="connectionchecktimeout" default="true">20</setting>
|
<setting id="connectionchecktimeout" default="true">20</setting>
|
||||||
|
|||||||
@@ -210,6 +210,37 @@ def write_if_changed(filename, content):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
def use_hourly_iptv_refresh(pvr_dir, addon_id):
|
||||||
|
"""TV guide setups before kodimov.7 made IPTV Simple reload the channel list
|
||||||
|
and programme guide only once a day, at 4:00, so a new epg.xml didn't reach
|
||||||
|
Kodi for up to a day. Switch them to a reload every hour, which also makes
|
||||||
|
Kodi fetch the guide again. Returns True if IPTV Simple must be restarted."""
|
||||||
|
import glob
|
||||||
|
import re
|
||||||
|
changed = False
|
||||||
|
for filename in glob.glob(os.path.join(pvr_dir, 'instance-settings-*.xml')):
|
||||||
|
with io.open(filename, 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read()
|
||||||
|
if 'plugin.video.{}/channels.m3u8'.format(addon_id) not in content: continue
|
||||||
|
new = content
|
||||||
|
for setting, value in (('m3uRefreshMode', '1'), ('m3uRefreshIntervalMins', '60')):
|
||||||
|
new = re.sub(r'<setting id="{}"[^>]*?(/>|>[^<]*</setting>)'.format(setting),
|
||||||
|
'<setting id="{}">{}</setting>'.format(setting, value), new)
|
||||||
|
if write_if_changed(filename, new):
|
||||||
|
LOG('use_hourly_iptv_refresh: updated {}'.format(filename))
|
||||||
|
changed = True
|
||||||
|
return changed
|
||||||
|
|
||||||
|
|
||||||
|
def restart_iptv_simple():
|
||||||
|
"""Restarts IPTV Simple so it loads its instance settings again."""
|
||||||
|
import xbmc
|
||||||
|
for enabled in (False, True):
|
||||||
|
xbmc.executeJSONRPC(json.dumps({'jsonrpc': '2.0', 'id': 1, 'method': 'Addons.SetAddonEnabled',
|
||||||
|
'params': {'addonid': 'pvr.iptvsimple', 'enabled': enabled}}))
|
||||||
|
xbmc.sleep(3000)
|
||||||
|
|
||||||
|
|
||||||
def extra_channels_xmltv(session):
|
def extra_channels_xmltv(session):
|
||||||
"""Returns the <channel> and <programme> elements for the extra channels,
|
"""Returns the <channel> and <programme> elements for the extra channels,
|
||||||
taken from a public XMLTV guide and renamed to their tvg-id."""
|
taken from a public XMLTV guide and renamed to their tvg-id."""
|
||||||
|
|||||||
@@ -564,6 +564,13 @@ class Movistar(object):
|
|||||||
if 'TituloHorLinea2' in p:
|
if 'TituloHorLinea2' in p:
|
||||||
pr['desc2'] = p['TituloHorLinea2']
|
pr['desc2'] = p['TituloHorLinea2']
|
||||||
pr['show_id'] = p['ShowId']
|
pr['show_id'] = p['ShowId']
|
||||||
|
# Synopsis, genre, year, age rating and picture come with the guide
|
||||||
|
pr['plot'] = p.get('Resena') or ''
|
||||||
|
pr['genre'] = p.get('GeneroComAntena') or ''
|
||||||
|
pr['year'] = p.get('Anno') or ''
|
||||||
|
pr['age_rating'] = (p.get('NivelMoral') or {}).get('Id') or ''
|
||||||
|
images = {i.get('id'): i.get('uri') for i in (p.get('Imagenes') or [])}
|
||||||
|
pr['image'] = images.get('horizontal') or images.get('default') or ''
|
||||||
if 'SerialId' in p:
|
if 'SerialId' in p:
|
||||||
pr['serie_id'] = p['SerialId']
|
pr['serie_id'] = p['SerialId']
|
||||||
pr['id'] = p['Id']
|
pr['id'] = p['Id']
|
||||||
@@ -1454,6 +1461,9 @@ class Movistar(object):
|
|||||||
t['subtitle'] = e['desc2']
|
t['subtitle'] = e['desc2']
|
||||||
t['start'] = datetime.utcfromtimestamp(e['start']/1000).strftime('%Y-%m-%dT%H:%M:%SZ')
|
t['start'] = datetime.utcfromtimestamp(e['start']/1000).strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||||
t['stop'] = datetime.utcfromtimestamp(e['end']/1000).strftime('%Y-%m-%dT%H:%M:%SZ')
|
t['stop'] = datetime.utcfromtimestamp(e['end']/1000).strftime('%Y-%m-%dT%H:%M:%SZ')
|
||||||
|
for key, field in (('description', 'plot'), ('image', 'image'), ('genre', 'genre'),
|
||||||
|
('year', 'year'), ('age_rating', 'age_rating')):
|
||||||
|
if e.get(field): t[key] = e[field]
|
||||||
|
|
||||||
if True:
|
if True:
|
||||||
program_id = str(e['show_id'])
|
program_id = str(e['show_id'])
|
||||||
@@ -1533,8 +1543,6 @@ class Movistar(object):
|
|||||||
'>\n' +
|
'>\n' +
|
||||||
' <title>{}</title>\n'.format(html_escape(e['title'])) +
|
' <title>{}</title>\n'.format(html_escape(e['title'])) +
|
||||||
' <sub-title>{}</sub-title>\n'.format(html_escape(e['subtitle'])))
|
' <sub-title>{}</sub-title>\n'.format(html_escape(e['subtitle'])))
|
||||||
if 'image' in e:
|
|
||||||
res.append(' <icon src="{}"/>\n'.format(e['image']))
|
|
||||||
if 'description' in e:
|
if 'description' in e:
|
||||||
res.append(' <desc>{}</desc>\n'.format(html_escape(e['description'])))
|
res.append(' <desc>{}</desc>\n'.format(html_escape(e['description'])))
|
||||||
if 'credits' in e and len(e['credits']) > 0:
|
if 'credits' in e and len(e['credits']) > 0:
|
||||||
@@ -1545,6 +1553,14 @@ class Movistar(object):
|
|||||||
elif c['type'] == 'actor':
|
elif c['type'] == 'actor':
|
||||||
res.append(' <actor>{}</actor>\n'.format(c['name']))
|
res.append(' <actor>{}</actor>\n'.format(c['name']))
|
||||||
res.append(' </credits>\n');
|
res.append(' </credits>\n');
|
||||||
|
if 'year' in e:
|
||||||
|
res.append(' <date>{}</date>\n'.format(html_escape(e['year'])))
|
||||||
|
if 'genre' in e:
|
||||||
|
res.append(' <category>{}</category>\n'.format(html_escape(e['genre'])))
|
||||||
|
if 'image' in e:
|
||||||
|
res.append(' <icon src="{}"/>\n'.format(html_escape(e['image'])))
|
||||||
|
if 'age_rating' in e:
|
||||||
|
res.append(' <rating system="ES"><value>{}</value></rating>\n'.format(html_escape(e['age_rating'])))
|
||||||
res.append('</programme>\n')
|
res.append('</programme>\n')
|
||||||
if self.add_extra_channels:
|
if self.add_extra_channels:
|
||||||
res.extend(kodimov.extra_channels_xmltv(self.net.session))
|
res.extend(kodimov.extra_channels_xmltv(self.net.session))
|
||||||
|
|||||||
@@ -749,6 +749,7 @@ def iptv(params):
|
|||||||
|
|
||||||
def create_iptv_settings():
|
def create_iptv_settings():
|
||||||
from .iptv import save_iptv_settings
|
from .iptv import save_iptv_settings
|
||||||
|
from . import kodimov
|
||||||
show_notification(addon.getLocalizedString(30319), xbmcgui.NOTIFICATION_INFO)
|
show_notification(addon.getLocalizedString(30319), xbmcgui.NOTIFICATION_INFO)
|
||||||
output_file = 'instance-settings-91.xml' if kodi_version > 19 else 'settings.xml'
|
output_file = 'instance-settings-91.xml' if kodi_version > 19 else 'settings.xml'
|
||||||
epg_url = None
|
epg_url = None
|
||||||
@@ -776,10 +777,7 @@ def create_iptv_settings():
|
|||||||
xbmc.executeJSONRPC(json.dumps({'jsonrpc': '2.0', 'id': 1, 'method': 'Settings.SetSettingValue',
|
xbmc.executeJSONRPC(json.dumps({'jsonrpc': '2.0', 'id': 1, 'method': 'Settings.SetSettingValue',
|
||||||
'params': {'setting': 'pvrmanager.usebackendchannelnumbers', 'value': True}}))
|
'params': {'setting': 'pvrmanager.usebackendchannelnumbers', 'value': True}}))
|
||||||
# Restart IPTV Simple so it loads the new instance and channel list.
|
# Restart IPTV Simple so it loads the new instance and channel list.
|
||||||
for enabled in (False, True):
|
kodimov.restart_iptv_simple()
|
||||||
xbmc.executeJSONRPC(json.dumps({'jsonrpc': '2.0', 'id': 1, 'method': 'Addons.SetAddonEnabled',
|
|
||||||
'params': {'addonid': 'pvr.iptvsimple', 'enabled': enabled}}))
|
|
||||||
xbmc.sleep(3000)
|
|
||||||
except:
|
except:
|
||||||
show_notification(addon.getLocalizedString(30324))
|
show_notification(addon.getLocalizedString(30324))
|
||||||
|
|
||||||
|
|||||||
+10
@@ -10,6 +10,7 @@ import xbmcgui
|
|||||||
from resources.lib.proxy import Proxy
|
from resources.lib.proxy import Proxy
|
||||||
from resources.lib.addon import *
|
from resources.lib.addon import *
|
||||||
from resources.lib.log import LOG
|
from resources.lib.log import LOG
|
||||||
|
from resources.lib import kodimov
|
||||||
|
|
||||||
def export_epg():
|
def export_epg():
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
@@ -41,10 +42,19 @@ if __name__ == '__main__':
|
|||||||
# Refreshes the TV guide channel list (new channels, new numbers) and the
|
# 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.
|
# 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
|
next_export = time.time() + 60
|
||||||
|
try:
|
||||||
|
pvr_dir = translatePath(xbmcaddon.Addon('pvr.iptvsimple').getAddonInfo('profile'))
|
||||||
|
restart_iptv = kodimov.use_hourly_iptv_refresh(pvr_dir, 'kodimovplus')
|
||||||
|
except RuntimeError: # IPTV Simple not installed or disabled
|
||||||
|
restart_iptv = False
|
||||||
|
player = xbmc.Player()
|
||||||
monitor = xbmc.Monitor()
|
monitor = xbmc.Monitor()
|
||||||
while not monitor.abortRequested():
|
while not monitor.abortRequested():
|
||||||
if monitor.waitForAbort(1):
|
if monitor.waitForAbort(1):
|
||||||
break
|
break
|
||||||
|
if restart_iptv and not player.isPlaying(): # restarting stops live TV
|
||||||
|
restart_iptv = False
|
||||||
|
kodimov.restart_iptv_simple()
|
||||||
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('auto_export_epg'): # channels + programme guide
|
if addon.getSettingBool('auto_export_epg'): # channels + programme guide
|
||||||
|
|||||||
Reference in new issue
Block a user