Version 0.5.20

This commit is contained in:
Paco8 committed 2023-02-24 15:28:06 +01:00
1 parent dd176ff328
commit 2e4639a886
13 files changed
+295 -16

No files matched your search

+3 -1
View File
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.movistarplus"
name="Movistar+"
version="0.5.14"
version="0.5.20"
provider-name="Paco8">
<requires>
<!--- <import addon="xbmc.python" version="2.25.0"/> -->
@@ -33,6 +33,8 @@
<screenshot>resources/screen1.jpg</screenshot>
<screenshot>resources/screen2.jpg</screenshot>
<screenshot>resources/screen3.jpg</screenshot>
<screenshot>resources/screen4.jpg</screenshot>
<screenshot>resources/screen5.jpg</screenshot>
</assets>
</extension>
</addon>
@@ -272,3 +272,19 @@ msgstr ""
msgctxt "#30204"
msgid "Video not available"
msgstr ""
msgctxt "#30300"
msgid "IPTV Manager"
msgstr ""
msgctxt "#30301"
msgid "Install IPTV Manager add-on"
msgstr ""
msgctxt "#30302"
msgid "Enable IPTV Manager integration"
msgstr ""
msgctxt "#30303"
msgid "IPTV Manager settings"
msgstr ""
@@ -272,3 +272,19 @@ msgstr "Formato no soportado"
msgctxt "#30204"
msgid "Video not available"
msgstr "Vídeo no disponible"
msgctxt "#30300"
msgid "IPTV Manager"
msgstr "IPTV Manager"
msgctxt "#30301"
msgid "Install IPTV Manager add-on"
msgstr "Instalar IPTV Manager"
msgctxt "#30302"
msgid "Enable IPTV Manager integration"
msgstr "Habilitar integración con IPTV Manager"
msgctxt "#30303"
msgid "IPTV Manager settings"
msgstr "Opciones de IPTV Manager"
+13
View File
@@ -48,3 +48,16 @@ class Cache(object):
files.extend(glob.glob(self.config_directory + ext))
for filename in files:
os.remove(filename)
def clear_cache(self, days=10):
from datetime import datetime
deleted = 0
files = glob.glob(os.path.join(self.config_directory + 'cache', '*.json'))
now = datetime.now()
for filename in files:
modified_time = datetime.fromtimestamp(os.path.getmtime(filename))
time_diff = now - modified_time
if time_diff.days > days:
os.remove(filename)
deleted += 1
return deleted
+37
View File
@@ -0,0 +1,37 @@
# -*- coding: utf-8 -*-
"""IPTV Manager Integration module"""
import json
import socket
from .movistar import Movistar
class IPTVManager:
"""Interface to IPTV Manager"""
def __init__(self, port):
"""Initialize IPTV Manager object"""
self.port = port
def via_socket(func):
"""Send the output of the wrapped function to socket"""
def send(self, m):
"""Decorator to send over a socket"""
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(('127.0.0.1', self.port))
try:
sock.sendall(json.dumps(func(self, m)).encode())
finally:
sock.close()
return send
@via_socket
def send_channels(self, m):
"""Return JSON-STREAMS formatted python datastructure to IPTV Manager"""
return dict(version=1, streams=m.export_channels())
@via_socket
def send_epg(self, m):
"""Return JSON-EPG formatted python data structure to IPTV Manager"""
return dict(version=1, epg=m.export_epg())
+170 -12
View File
@@ -11,6 +11,7 @@ import io
import os
import time
import re
from datetime import datetime
from .log import LOG, print_json
from .network import Network
@@ -48,6 +49,8 @@ class Movistar(object):
# Cache
self.cache = Cache(config_directory)
if not os.path.exists(config_directory + 'cache'):
os.makedirs(config_directory + 'cache')
# Endpoints
self.endpoints = self.get_endpoints()
@@ -297,9 +300,10 @@ class Movistar(object):
data = self.net.load_data(url, headers)
self.cache.save_file('devices.json', json.dumps(data, ensure_ascii=False))
#print_json(data)
if not isinstance(data, list): return []
res = []
for d in data:
if d['Id'] != '-':
if d.get('Id') != '-':
dev = {}
dev['id'] = d['Id']
dev['name'] = d['Name']
@@ -436,7 +440,8 @@ class Movistar(object):
if 'TituloHorLinea2' in p:
pr['desc2'] = p['TituloHorLinea2']
pr['show_id'] = p['ShowId']
if 'links' in p: pr['links'] = p['links']
pr['id'] = p['Id']
#if 'links' in p: pr['links'] = p['links']
epg[id].append(pr)
return epg
@@ -497,8 +502,9 @@ class Movistar(object):
ch['desc2'] = desc2
plot += "[B]{}[/B] {} {}\n".format(programs[i]['start_str'], desc1, desc2)
if self.add_extra_info:
ch['art'] = {'poster': None, 'fanart': None}
self.add_video_extra_info(programs[i]['show_id'], ch, catalog='events')
ch['art']['poster'] = ch['art']['fanart'] = None
ch['show_id'] = programs[i]['show_id']
self.add_video_extra_info(ch)
if 'plot' in ch['info']:
plot = plot + ch['info']['plot']
break
@@ -541,6 +547,7 @@ class Movistar(object):
t['stream_type'] = 'tv'
t['info']['mediatype'] = 'movie'
t['info']['title'] = str(c['Dial']) +'. ' + c['Nombre']
t['channel_name'] = c['Nombre']
t['id'] = c['CodCadenaTv']
if add_epg_info:
t['desc1'] = c['Nombre']
@@ -650,8 +657,9 @@ class Movistar(object):
elif video['AssetType'] == 'NPVR':
t['stream_type'] = 'rec'
t['session_request'] = '{"contentID":' + str(t['id']) + ', "streamType":"NPVR"}'
if 'ShowId' in video: t['show_id'] = video['ShowId']
if self.add_extra_info:
self.add_video_extra_info(t['id'], t)
self.add_video_extra_info(t)
if 'Recording' in data:
t['stream_type'] = 'rec'
t['rec'] = {'id': data['Recording']['id'],
@@ -680,11 +688,49 @@ class Movistar(object):
res.append(t)
return res
def add_video_extra_info(self, id, t, catalog=''):
def add_video_extra_info(self, t):
#LOG('add_video_extra_info: t: {}'.format(t))
try:
url = self.endpoints['ficha'].format(deviceType='webplayer', id=id, profile=self.account['platform'], mediatype='FOTOV', version='7.1', mode='GLOBAL', catalog=catalog, channels='', state='', mdrm='true', demarcation=self.account['demarcation'], legacyBoxOffice='')
#print(url)
data = self.net.load_data(url)
if 'show_id' in t:
catalog = 'events'
id = t['show_id']
elif 'id' in t:
catalog = ''
id = t['id']
else:
return
#LOG('id: {} catalog: {}'.format(id, catalog))
prefix = 'event' if catalog=='events' else 'info'
cache_filename = 'cache/{}_{}.json'.format(prefix, id)
content = self.cache.load(cache_filename, 30*24*60)
if content:
data = json.loads(content)
else:
url = self.endpoints['ficha'].format(deviceType='webplayer', id=id, profile=self.account['platform'], mediatype='FOTOV', version='7.1', mode='GLOBAL', catalog=catalog, channels='', state='', mdrm='true', demarcation=self.account['demarcation'], legacyBoxOffice='')
#print(url)
data = self.net.load_data(url)
self.cache.save_file(cache_filename, json.dumps(data, ensure_ascii=False))
if not 'info' in t: t['info'] = {}
if not 'art' in t: t['art'] = {}
if not t['info'].get('title'):
if 'TituloEpisodio' in data:
t['info']['title'] = data['TituloEpisodio']
else:
t['info']['title'] = data['Titulo']
if 'Serie' in data:
if not t['info'].get('episode'): t['info']['episode'] = data['NumeroEpisodio']
if not t['info'].get('tvshowtitle'): t['info']['tvshowtitle'] = data['Serie']['TituloSerie']
if not t['info'].get('season') and data['Serie'].get('Temporada'):
m = re.search(r'T(\d+)', data['Serie']['Temporada'])
if m: t['info']['season'] = m.group(1)
if not 'duration' in t['info']:
t['info']['duration'] = data.get('Duracion', 0) * 60
if data.get('Sinopsis'):
t['info']['plot'] = data['Sinopsis']
if data.get('Actores'):
@@ -774,7 +820,7 @@ class Movistar(object):
t['stream_type'] = 'u7d'
t['session_request'] = '{"contentID":' + str(t['id']) + ', "streamType":"CUTV"}'
if self.add_extra_info:
self.add_video_extra_info(t['id'], t)
self.add_video_extra_info(t)
res.append(t)
return res
@@ -829,19 +875,23 @@ class Movistar(object):
t['stream_type'] = 'u7d'
t['info']['mediatype'] = 'movie'
t['url'] = ''
t['id'] = p['show_id']
t['id'] = p['id']
t['show_id'] = p['show_id']
t['session_request'] = '{"contentID":' + str(t['id']) +',"streamType":"CUTV"}'
t['info']['playcount'] = 1 # Set as watched
t['start'] = p['start']
t['end'] = p['end']
t['aired'] = (p['end'] <= (time.time() * 1000))
t['subscribed'] = True # Fix me
"""
if 'links' in p:
for link in p['links']:
if link['rel'] == 'start-over':
t['url'] = link['href']
"""
t['url'] = 'https://grmovistar.imagenio.telefonica.net/asfe/rest/tvMediaURLs?tvProgram.id='+ str(t['show_id']) +'&svc=startover'
if self.add_extra_info:
self.add_video_extra_info(t['id'], t, catalog='events')
self.add_video_extra_info(t)
res.append(t)
return res
@@ -935,3 +985,111 @@ class Movistar(object):
if link['sizes'] == '512x512':
return link['href']
return None
def export_channels(self):
if sys.version_info[0] >= 3:
from urllib.parse import urlencode
else:
from urllib import urlencode
channels = self.get_channels(False)
res = []
for c in channels:
if not c['subscribed']: continue
t = {}
t['name'] = c['channel_name']
t['id'] = c['id']
t['logo'] = c['art']['icon']
t['preset'] = c['dial']
args = urlencode({'action': 'play', 'stype': 'tv', 'id': c['id'], 'url': c['url'], 'session_request': c['session_request']})
t['stream'] = 'plugin://plugin.video.movistarplus/?' + args
res.append(t)
return res
def export_channels_to_m3u8(self, filename):
channels = self.export_channels()
items = []
for t in channels:
item = '#EXTINF:-1 tvg-name="{name}" tvg-id="{id}" tvg-logo="{logo}" tvg-chno="{preset}" group-title="Movistar+" catchup="vod",{name}\n{stream}\n\n'.format(
name=t['name'], id=t['id'], logo=t['logo'], preset=t['preset'], stream=t['stream'])
items.append(item)
res = '#EXTM3U\n## Movistar+\n{}'.format(''.join(items))
with io.open(filename, 'w', encoding='utf-8', newline='') as handle:
handle.write(res)
def export_epg(self):
if sys.version_info[0] >= 3:
from urllib.parse import urlencode
else:
from urllib import urlencode
now = time.time()*1000
res = {}
epg = self.get_epg()
channels = self.get_channels(False)
for channel in channels:
if not channel['subscribed']: continue
id = channel['id']
res[id] = []
for e in epg[id]:
t = {}
t['title'] = e['desc1']
t['subtitle'] = e['desc2']
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')
aired = (e['end'] <= now)
if aired:
program_id = str(e['show_id'])
url = 'https://grmovistar.imagenio.telefonica.net/asfe/rest/tvMediaURLs?tvProgram.id='+ program_id +'&svc=startover'
session_request = '{"contentID":' + program_id +',"streamType":"CUTV"}'
args = urlencode({'action': 'play', 'stype': 'u7d', 'id': program_id, 'url': url, 'session_request': session_request})
t['stream'] = 'plugin://plugin.video.movistarplus/?' + args
"""
if self.add_extra_info and id in ['HOLLYW', 'TCM', 'AMC', 'MV3', 'MV2', 'CPSER', 'FOXGE', 'TNT']:
i = {'id': e['id'], 'show_id': e['show_id']}
self.add_video_extra_info(i)
#print(i)
t['description'] = i['info'].get('plot')
t['image'] = i['art'].get('poster')
t['credits'] = []
for text in i['info'].get('director', []):
t['credits'].append({'type': 'director', 'name': text})
for text in i['info'].get('cast', []):
t['credits'].append({'type': 'actor', 'name': text})
"""
res[id].append(t)
return res
def export_epg_to_xml(self, filename):
channels = self.export_channels()
res = []
res.append('<?xml version="1.0" encoding="UTF-8"?>\n' +
'<!DOCTYPE tv SYSTEM "xmltv.dtd">\n' +
'<tv>\n')
for t in channels:
res.append('<channel id="{}">\n'.format(t['id']) +
' <display-name>{}</display-name>\n'.format(t['name']) +
' <icon src="{}"/>\n'.format(t['logo']) +
'</channel>\n')
epg = self.export_epg()
for ch in channels:
for e in epg[ch['id']]:
start = datetime.strptime(e['start'], "%Y-%m-%dT%H:%M:%SZ").strftime("%Y%m%d%H%M%S +0000")
stop = datetime.strptime(e['stop'], "%Y-%m-%dT%H:%M:%SZ").strftime("%Y%m%d%H%M%S +0000")
url = e.get('stream', None)
if url:
url = url.replace('&', '&amp;')
res.append('<programme start="{}" stop="{}" channel="{}"'.format(start, stop, ch['id']) +
(' catchup-id="{}"'.format(url) if url else "") +
'>\n' +
' <title>{}</title>\n'.format(e['title']) +
' <sub-title>{}</sub-title>'.format(e['subtitle']) +
'</programme>\n')
res.append('</tv>\n')
with io.open(filename, 'w', encoding='utf-8', newline='') as handle:
handle.write(''.join(res))
+31 -1
View File
@@ -148,6 +148,14 @@ def play(params):
play_item.setMimeType('application/dash+xml')
play_item.setContentLookup(False)
# Add info
if addon.getSettingBool('add_extra_info') and stype in ['vod', 'u7d', 'rec']:
t = {'id': params['id']}
if 'show_id' in params: t['show_id'] = params['show_id']
m.add_video_extra_info(t)
play_item.setInfo('video', t['info'])
play_item.setArt(t['art'])
# Subtitles
if stype == 'vod' and addon.getSettingBool('use_ttml2ssa'):
# Convert subtitles
@@ -238,6 +246,7 @@ def add_videos(category, ctype, videos, ref=None, url_next=None, url_prev=None):
list_item.addContextMenuItems([(addon.getLocalizedString(30173), "RunPlugin(" + action + ")")])
url = get_url(action='play', id=t['id'], url=t['url'], session_request=t['session_request'], stype=t['stream_type'])
if 'show_id' in t: url += '&show_id={}'.format(t['show_id'])
xbmcplugin.addDirectoryItem(_handle, url, list_item, False)
elif t['type'] == 'series':
list_item = xbmcgui.ListItem(label = title_name)
@@ -466,6 +475,19 @@ def list_users():
add_menu_option(addon.getLocalizedString(30150), get_url(action='logout')) # Close session
close_folder()
def iptv(params):
LOG('iptv: params: {}'.format(params))
if m.logged:
try:
from .iptvmanager import IPTVManager
port = int(params['port'])
if params['action'] == 'iptv-channels':
IPTVManager(port).send_channels(m)
elif params['action'] == 'iptv-epg':
IPTVManager(port).send_epg(m)
except:
pass
def router(paramstring):
"""
Router function that calls other functions
@@ -523,6 +545,8 @@ def router(paramstring):
list_vod()
elif params['action'] == 'search':
search(params)
elif 'iptv' in params['action']:
iptv(params)
else:
# Main
open_folder(addon.getLocalizedString(30101)) # Menu
@@ -582,9 +606,15 @@ def run():
if profile_id in ['OTT', 'NODTH']:
m.account['platform'] = addon.getSetting('profile_id')
# Clear cache
LOG('Cleaning cache. {} files removed.'.format(m.cache.clear_cache()))
global player
player = Player()
# Call the router function and pass the plugin call parameters to it.
# We use string slicing to trim the leading '?' from the plugin call paramstring
router(sys.argv[2][1:])
params = sys.argv[2][1:]
if '/iptv/channels' in sys.argv[0]: params += '&action=iptv-channels'
elif '/iptv/epg' in sys.argv[0]: params += '&action=iptv-epg'
router(params)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 150 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

+9 -2
View File
@@ -4,7 +4,7 @@
<setting label="30020" type="bool" id="channels_with_epg" default="true"/>
<setting label="30021" type="bool" id="add_extra_info" default="true"/>
<setting id="use_ttml2ssa" type="bool" label="$ADDON[script.module.ttml2ssa 32202]" default="false"/>
<setting id="ssa_settings" type="action" label="$ADDON[script.module.ttml2ssa 32201]" option="close" action="Addon.OpenSettings(script.module.ttml2ssa)" enable="eq(-1,true)"/>
<setting id="ssa_settings" type="action" label="$ADDON[script.module.ttml2ssa 32201]" option="close" action="Addon.OpenSettings(script.module.ttml2ssa)" enable="eq(-1,true)" subsetting="true"/>
<setting label="30019" type="action" id="is_settings" action="Addon.OpenSettings(inputstream.adaptive)" enable="System.HasAddon(inputstream.adaptive)" option="close"/>
<setting id="reuse_devices" type="bool" label="30023" default="false" visible="true"/>
<setting id="open_session" type="bool" label="30022" default="true" visible="true"/>
@@ -14,12 +14,19 @@
<category label="30002">
<setting label="30011" type="bool" id="manifest_modification" default="true"/>
<setting label="30012" type="bool" id="use_proxy_for_license" default="true"/>
<setting id="reregister" type="bool" label="30024" default="true" enable="eq(-1,true)"/>
<setting id="reregister" type="bool" label="30024" default="true" enable="eq(-1,true)" subsetting="true"/>
</category>
<!--
<category label="30003">
<setting label="30010" type="labelenum" id="drm_type" values="Widevine|Playready"/>
</category>
-->
<category label="30300">
<setting label="30301" type="action" action="InstallAddon(service.iptv.manager)" option="close" visible="!System.HasAddon(service.iptv.manager)"/>
<setting label="30302" type="bool" id="iptv.enabled" default="true" visible="System.HasAddon(service.iptv.manager)" />
<setting label="30303" type="action" action="Addon.OpenSettings(service.iptv.manager)" enable="eq(-1,true)" option="close" visible="System.HasAddon(service.iptv.manager)" subsetting="true"/>
<setting id="iptv.channels_uri" default="plugin://plugin.video.movistarplus/iptv/channels" visible="false"/>
<setting id="iptv.epg_uri" default="plugin://plugin.video.movistarplus/iptv/epg" visible="false"/>
</category>
</settings>