# -*- coding: utf-8 -*- from skin import loadSkin from Plugins.Plugin import PluginDescriptor from Screens.Screen import Screen from Screens.InfoBarGenerics import InfoBarMenu, InfoBarSeek, InfoBarNotifications, InfoBarServiceNotifications, InfoBarShowHide, InfoBarSimpleEventView, InfoBarServiceErrorPopupSupport from Screens.MessageBox import MessageBox from Screens.Standby import TryQuitMainloop from Screens.VirtualKeyBoard import VirtualKeyBoard from Screens.Setup import SetupSummary from Components.ActionMap import ActionMap from Components.ServiceEventTracker import InfoBarBase from Components.Sources.List import List from Components.Label import Label from Components.MultiContent import MultiContentEntryText, MultiContentEntryProgress from Components.Pixmap import Pixmap from Components.ConfigList import ConfigListScreen from Components.config import config, getConfigListEntry, ConfigSubsection, ConfigText, ConfigPassword, ConfigInteger, ConfigNothing, ConfigYesNo, ConfigSelection, NoSave from Tools.BoundFunction import boundFunction from Tools.Directories import resolveFilename, SCOPE_PLUGINS from downloader import TelekomSportFileDownloader from enigma import eTimer, eListboxPythonMultiContent, gFont, eEnv, eServiceReference, getDesktop, eConsoleAppContainer import xml.etree.ElementTree as ET import time import urllib import urllib2 import json import base64 import re import random import string import hashlib from os import path from itertools import cycle, izip from datetime import datetime, timedelta from twisted.web.client import Agent, readBody from twisted.internet import reactor from twisted.web.http_headers import Headers if getDesktop(0).size().width() <= 1280: loadSkin(resolveFilename(SCOPE_PLUGINS) + "Extensions/TelekomSport/skin_hd.xml") else: loadSkin(resolveFilename(SCOPE_PLUGINS) + "Extensions/TelekomSport/skin_fhd.xml") try: from enigma import eMediaDatabase telekomsport_isDreamOS = True import ssl try: _create_unverified_https_context = ssl._create_unverified_context except AttributeError: pass else: ssl._create_default_https_context = _create_unverified_https_context except: telekomsport_isDreamOS = False #==== workaround for TLSv1_2 with DreamOS ======= from OpenSSL import SSL from twisted.internet.ssl import ClientContextFactory try: # available since twisted 14.0 from twisted.internet._sslverify import ClientTLSOptions except ImportError: ClientTLSOptions = None #================================================ config.plugins.telekomsport = ConfigSubsection() config.plugins.telekomsport.username1 = ConfigText(default = '', fixed_size = False) config.plugins.telekomsport.password1 = ConfigPassword(default = '', fixed_size = False) config.plugins.telekomsport.token1 = ConfigText(default = '') config.plugins.telekomsport.token1_expiration_time = ConfigInteger(default = 0) config.plugins.telekomsport.username2 = ConfigText(default = '', fixed_size = False) config.plugins.telekomsport.password2 = ConfigPassword(default = '', fixed_size = False) config.plugins.telekomsport.token2 = ConfigText(default = '') config.plugins.telekomsport.token2_expiration_time = ConfigInteger(default = 0) config.plugins.telekomsport.hide_unplayable = ConfigYesNo(default=False) # Use 2 config variables as workaround as empty ConfigSelection is not initialized with the stored value after e2 restart config.plugins.telekomsport.default_section = ConfigText(default = '', fixed_size = False) config.plugins.telekomsport.default_section_chooser = NoSave(ConfigSelection([], default = None)) # Some images like DreamOS need streams with fix quality config.plugins.telekomsport.fix_stream_quality = ConfigYesNo(default = telekomsport_isDreamOS) config.plugins.telekomsport.stream_quality = ConfigSelection(default = "2", choices = [("0", _("sehr gering")), ("1", _("gering")), ("2", _("mittel")), ("3", _("hoch")), ("4", _("sehr hoch"))]) config.plugins.telekomsport.conf_alarm_duration = ConfigSelection(default = "8000", choices = [("4000", "4 Sekunden"), ("6000", "6 Sekunden"), ("8000", "8 Sekunden"), ("10000", "10 Sekunden"), ("12000", "12 Sekunden")]) def encode(x): return base64.encodestring(''.join(chr(ord(c) ^ ord(k)) for c, k in izip(x, cycle('password protection')))).strip() def decode(x): return ''.join(chr(ord(c) ^ ord(k)) for c, k in izip(base64.decodestring(x), cycle('password protection'))) def generateToken(url): api_salt = '55!#r%Rn3%xn?U?PX*k' t = int(((datetime.now() - timedelta(hours=5)).replace(hour=0, minute=0, second=0, microsecond=0) - datetime(1970, 1, 1)).total_seconds()) return hashlib.sha256('{0}{1}{2}'.format(api_salt, t, url)).hexdigest() def generateUrl(urlEnd): return TelekomSportMainScreen.base_url + TelekomSportMainScreen.api_url + urlEnd + '?token=' + generateToken(TelekomSportMainScreen.api_url + urlEnd) def readPasswords(session): try: with open('/etc/enigma2/MagentaSport.cfg', 'rb') as f: p1 = decode(f.readline()) p2 = decode(f.readline()) return p1, p2 except Exception as e: session.open(MessageBox, 'Error reading passwords' + str(e), MessageBox.TYPE_ERROR) print "Error reading MagentaSport.cfg", e return '', '' def savePasswords(session, p1, p2): try: with open('/etc/enigma2/MagentaSport.cfg', 'wb') as f: f.write(encode(p1) + '\n') f.write(encode(p2)) return True except Exception as e: session.open(MessageBox, 'Error writing passwords' + str(e), MessageBox.TYPE_ERROR) print "Error writing MagentaSport.cfg", e return False def loadTelekomSportJsonData(screen, statusField, buildListFunc, data): try: jsonResult = json.loads(data) if statusField: if 'status' not in jsonResult or jsonResult['status'] != 'success': statusField.setText(screen + ': Fehler beim Laden der JSON Daten: "Status ist nicht success"') return buildListFunc(jsonResult) except Exception as e: statusField.setText(screen + ': Fehler beim Laden der JSON Daten "' + str(e) + '"') def handleTelekomSportWebsiteResponse(callback, response): d = readBody(response) d.addCallback(callback) return d def handleTelekomSportDownloadError(screen, statusField, err): if statusField: statusField.setText(screen + ': Fehler beim Download "' + str(err) + '"') def downloadTelekomSportJson(url, callback, errorCallback): if telekomsport_isDreamOS == False: agent = Agent(reactor) else: class WebClientContextFactory(ClientContextFactory): "A SSL context factory which is more permissive against SSL bugs." def __init__(self): self.method = SSL.SSLv23_METHOD def getContext(self, hostname=None, port=None): ctx = ClientContextFactory.getContext(self) # Enable all workarounds to SSL bugs as documented by # http://www.openssl.org/docs/ssl/SSL_CTX_set_options.html ctx.set_options(SSL.OP_ALL) if hostname and ClientTLSOptions is not None: # workaround for TLS SNI ClientTLSOptions(hostname, ctx) return ctx contextFactory = WebClientContextFactory() agent = Agent(reactor, contextFactory) d = agent.request('GET', url, Headers({'user-agent': ['Twisted']})) d.addCallback(boundFunction(handleTelekomSportWebsiteResponse, callback)) d.addErrback(errorCallback) class TelekomSportRedirectHandler(urllib2.HTTPRedirectHandler): screen = None def __init__(self, screen): self.screen = screen def http_error_302(self, req, fp, code, msg, headers): location = headers.getheaders('Location')[0] pos = location.find('code=') + 5 self.screen.auth_code = location[pos: pos + 8] return None class TelekomSportMainScreenSummary(SetupSummary): def __init__(self, session, parent): SetupSummary.__init__(self, session, parent = parent) self.skinName = 'SetupSummary' self.onShow.append(self.addWatcher) self.onHide.append(self.removeWatcher) def addWatcher(self): self.parent['list'].onSelectionChanged.append(self.selectionChanged) self.selectionChanged() def removeWatcher(self): self.parent['list'].onSelectionChanged.remove(self.selectionChanged) class TelekomSportConfigScreen(ConfigListScreen, Screen): ts_font_str = "" if getDesktop(0).size().width() <= 1280: if not telekomsport_isDreamOS: ts_font_str = 'font="Regular;20"' skin = ''' ''' else: if not telekomsport_isDreamOS: ts_font_str = 'font="Regular;32"' skin = ''' ''' def __init__(self, session): Screen.__init__(self, session) self.session = session self.list = [] self.config_account1_text = getConfigListEntry('1. Account', ConfigNothing()) self.list.append(self.config_account1_text) self.config_username1 = getConfigListEntry('Benutzername', config.plugins.telekomsport.username1) self.list.append(self.config_username1) self.config_password1 = getConfigListEntry('Passwort', config.plugins.telekomsport.password1) self.list.append(self.config_password1) self.config_account2_text = getConfigListEntry('2. Account', ConfigNothing()) self.list.append(self.config_account2_text) self.config_username2 = getConfigListEntry('Benutzername', config.plugins.telekomsport.username2) self.list.append(self.config_username2) self.config_password2 = getConfigListEntry('Passwort', config.plugins.telekomsport.password2) self.list.append(self.config_password2) self.config_hide_unplayable = getConfigListEntry('Unspielbares ausblenden', config.plugins.telekomsport.hide_unplayable) self.list.append(self.config_hide_unplayable) self.config_default_section_chooser = getConfigListEntry('Default Abschnitt', config.plugins.telekomsport.default_section_chooser) self.list.append(self.config_default_section_chooser) self.config_fix_stream_quality = getConfigListEntry('Feste Stream Qualität verwenden', config.plugins.telekomsport.fix_stream_quality) self.list.append(self.config_fix_stream_quality) self.config_stream_quality = getConfigListEntry('Stream Qualität', config.plugins.telekomsport.stream_quality) self.list.append(self.config_stream_quality) self.config_conf_alarm_duration = getConfigListEntry('Anzeigedauer Konferenzalarm', config.plugins.telekomsport.conf_alarm_duration) self.list.append(self.config_conf_alarm_duration) ConfigListScreen.__init__(self, self.list, session) self['buttonred'] = Label(_('Cancel')) self['buttongreen'] = Label(_('Ok')) self['buttonblue'] = Label('virt. Tastatur') self['setupActions'] = ActionMap(['SetupActions', 'VirtualKeyboardActions', 'ColorActions'], { 'green': self.save, 'red': self.cancel, 'blue': self.virtualKeyboard, 'save': self.save, 'cancel': self.cancel, 'ok': self.save, 'showVirtualKeyboard': self.virtualKeyboard, }, -2) p1, p2 = readPasswords(session) config.plugins.telekomsport.password1.value = p1 config.plugins.telekomsport.password2.value = p2 config.plugins.telekomsport.default_section_chooser.value = config.plugins.telekomsport.default_section.value self["HelpWindow"] = Pixmap() self["HelpWindow"].hide() self.onLayoutFinish.append(self.setWindowTitle) def setWindowTitle(self): self.setTitle(TelekomSportMainScreen.title + ' Einstellungen') def save(self): savePasswords(self.session, config.plugins.telekomsport.password1.value, config.plugins.telekomsport.password2.value) config.plugins.telekomsport.password1.value = '' config.plugins.telekomsport.token1.value = '' config.plugins.telekomsport.token1_expiration_time.value = 0 config.plugins.telekomsport.password2.value = '' config.plugins.telekomsport.token2.value = '' config.plugins.telekomsport.token2_expiration_time.value = 0 config.plugins.telekomsport.default_section.value = config.plugins.telekomsport.default_section_chooser.value config.plugins.telekomsport.default_section.save() for x in self['config'].list: x[1].save() self.close() def cancel(self): for x in self['config'].list: x[1].cancel() self.close() def virtualKeyboard(self): if self['config'].getCurrent() in (self.config_username1, self.config_username2, self.config_password1, self.config_password2): self.session.openWithCallback(self.virtualKeyBoardCallback, VirtualKeyBoard, title = self['config'].getCurrent()[0], text = self['config'].getCurrent()[1].value) def virtualKeyBoardCallback(self, callback = None): if callback is not None: self['config'].getCurrent()[1].value = callback self['config'].invalidate(self['config'].getCurrent()) # for summary def createSummary(self): from Screens.SimpleSummary import SimpleSummary return SimpleSummary class TelekomSportConferenceAlarm(Screen): def __init__(self, session): Screen.__init__(self, session) self.session = session self.setup_title = TelekomSportMainScreen.title self['list'] = List() self['logo_on'] = Pixmap() self['logo_off'] = Pixmap() self['logo_off'].hide() self.close_timer = eTimer() if telekomsport_isDreamOS: self.close_timer_conn = self.close_timer.timeout.connect(self.hide_screen) else: self.close_timer.callback.append(self.hide_screen) self.onShow.append(self.startTimer) self.onHide.append(self.stopTimer) def startTimer(self): if self.close_timer.isActive(): self.close_timer.stop() self.close_timer.start(int(config.plugins.telekomsport.conf_alarm_duration.value), True) def stopTimer(self): if self.close_timer.isActive(): self.close_timer.stop() def hide_screen(self): self.hide() self.shown = False class TelekomSportMoviePlayer(Screen, InfoBarMenu, InfoBarBase, InfoBarSeek, InfoBarNotifications, InfoBarServiceNotifications, InfoBarShowHide, InfoBarSimpleEventView, InfoBarServiceErrorPopupSupport): conference_alarm_url = 'https://www.magentasport.de/api/v2/player/alert/history' def __init__(self, session, service, standings_url, schedule_url, statistics_url, boxscore_url, conference_alarm_available, league_id, video_id): Screen.__init__(self, session) self.skinName = 'MoviePlayer' self.title = service.getName() InfoBarMenu.__init__(self) InfoBarBase.__init__(self) InfoBarNotifications.__init__(self) InfoBarServiceNotifications.__init__(self) InfoBarShowHide.__init__(self) InfoBarSimpleEventView.__init__(self) InfoBarSeek.__init__(self) InfoBarServiceErrorPopupSupport.__init__(self) # disable 2nd infobar as it calls openEventView if hasattr(config.usage, "show_second_infobar"): self.saved_show_second_infobar_value = config.usage.show_second_infobar.value config.usage.show_second_infobar.value = '0' self.service = service self.lastservice = self.session.nav.getCurrentlyPlayingServiceReference() self.standings_url = standings_url self.schedule_url = schedule_url self.statistics_url = statistics_url self.boxscore_url = boxscore_url self.conference_alarm_available = conference_alarm_available self.league_id = league_id self.video_id = video_id self.conference_alarm_timer = eTimer() if telekomsport_isDreamOS: self.conference_alarm_timer_conn = self.conference_alarm_timer.timeout.connect(self.checkAlarmHistory) else: self.conference_alarm_timer.callback.append(self.checkAlarmHistory) self.conference_complete_alarm_list = [] self.conference_alarm_dialog = self.session.instantiateDialog(TelekomSportConferenceAlarm) self.conference_alarm_dialog.hide_screen() self['actions'] = ActionMap(['MoviePlayerActions', 'ColorActions', 'OkCancelActions', 'SetupActions'], { 'leavePlayer': self.leavePlayer, 'cancel' : self.leavePlayer, 'leavePlayerOnExit': self.leavePlayerOnExit, 'deleteBackward' : self.showLastConfAlarm, 'red' : self.showBoxScore, 'green' : self.showStatistics, 'yellow': self.showSchedule, 'blue' : self.showStandings, }, -2) self.onFirstExecBegin.append(self.playStream) self.onClose.append(self.stopPlayback) self.onLayoutFinish.append(self.onLayoutFinished) def onLayoutFinished(self): if self.conference_alarm_available: self.conference_alarm_dialog.show() def playStream(self): self.session.nav.playService(self.service) def stopPlayback(self): if self.lastservice: self.session.nav.playService(self.lastservice) else: self.session.nav.stopService() def leavePlayer(self): if self.conference_alarm_dialog.shown: self.conference_alarm_dialog.hide_screen() else: self.session.openWithCallback(self.leavePlayerConfirmed, MessageBox, 'Abspielen beenden?') def leavePlayerOnExit(self): self.leavePlayer() def leavePlayerConfirmed(self, answer): if answer: self.session.deleteDialog(self.conference_alarm_dialog) self.conference_alarm_dialog = None # restore old 2nd infobar config value if hasattr(config.usage, "show_second_infobar"): config.usage.show_second_infobar.value = self.saved_show_second_infobar_value self.close() def openEventView(self): if self.conference_alarm_available and not self.conference_alarm_timer.isActive(): self.conference_complete_alarm_list = [] self.conference_alarm_dialog['logo_on'].hide() self.conference_alarm_dialog['logo_off'].hide() self.checkAlarmHistory(True) self.conference_alarm_timer.start(20000, False) elif self.conference_alarm_available and self.conference_alarm_timer.isActive(): self.conference_alarm_timer.stop() self.conference_alarm_dialog['logo_off'].show() self.conference_alarm_dialog['list'].setList([]) self.conference_alarm_dialog.show() def showBoxScore(self): if self.boxscore_url: self.session.open(TelekomSportBoxScoreScreen, self.boxscore_url) def showStatistics(self): if self.statistics_url: self.session.open(TelekomSportStatisticsScreen, self.statistics_url) def showSchedule(self): if self.schedule_url: self.session.open(TelekomSportScheduleScreen, self.schedule_url) def showStandings(self): if self.standings_url: self.session.open(TelekomSportStandingsScreen, self.standings_url) def showMovies(self): pass def checkAlarmHistory(self, showAll = False): downloadTelekomSportJson(self.conference_alarm_url, boundFunction(loadTelekomSportJsonData, 'Player', None, boundFunction(self.checkForNewAlarm, showAll)), self.checkAlarmHistoryError) def checkForNewAlarm(self, showAll, jsonData): try: new_alarms_list = [] complete_list = [] if 'leagues' in jsonData['data']: for league in jsonData['data']['leagues']: if self.league_id == league.encode('utf8'): leagueEvents = jsonData['data']['leagues'][league] for ev in leagueEvents['events']: title = leagueEvents['events'][ev]['title'].encode('utf8') text = leagueEvents['events'][ev]['text'].encode('utf8') if leagueEvents['events'][ev]['imageRightAlt']: match = leagueEvents['events'][ev]['imageLeftAlt'].encode('utf8') + ' : ' + leagueEvents['events'][ev]['imageRightAlt'].encode('utf8') else: match = leagueEvents['events'][ev]['imageLeftAlt'].encode('utf8') eventid = leagueEvents['events'][ev]['eventid'] videoid = leagueEvents['events'][ev]['videoid'] # don't show events from current stream to avoid showing events before they are visible in the stream (playback may be behind live point) if str(videoid) == self.video_id: continue complete_list.append((ev, match, title, text, eventid, videoid)) if (filter(lambda x: x[0] == ev, self.conference_complete_alarm_list) == []) or showAll: # event not found in the current list or conference alarm enabled -> show all events new_alarms_list.append((ev, match, title, text, eventid, videoid)) self.conference_complete_alarm_list = complete_list if len(new_alarms_list) > 0 or showAll: new_alarms_list.sort(key=lambda x: x[0], reverse=True) self.conference_alarm_dialog['list'].setList(new_alarms_list) self.conference_alarm_dialog.show() except Exception as e: print "MagentaSport error checkForNewAlarm", e self.conference_alarm_timer.stop() def checkAlarmHistoryError(self, err): print "MagentaSport checkAlarmHistoryError", err self.conference_alarm_timer.stop() def showLastConfAlarm(self): if self.conference_alarm_available and self.conference_alarm_timer.isActive(): self.conference_alarm_dialog.show() # for summary def createSummary(self): from Screens.SimpleSummary import SimpleSummary return SimpleSummary class TelekomSportBoxScoreScreen(Screen): def __init__(self, session, boxscore_url): Screen.__init__(self, session) self.session = session self.setup_title = TelekomSportMainScreen.title self['status'] = Label('Lade Daten...') self['title'] = Label('Ergebnis') self['match_home'] = Label() self['match_away'] = Label() self['endResult'] = Label() self['version'] = Label(TelekomSportMainScreen.version) self.resultList = [] self['list'] = List(self.resultList) self['actions'] = ActionMap(['OkCancelActions'], { 'ok': self.close, 'cancel': self.close, }) downloadTelekomSportJson(generateUrl(boxscore_url), boundFunction(loadTelekomSportJsonData, 'BoxScore', self['status'], self.loadBoxScore), boundFunction(handleTelekomSportDownloadError, 'BoxScore', self['status'])) def loadBoxScore(self, jsonData): if not jsonData['data'] or not jsonData['data']['data'] or not jsonData['data']['data']['results'] or not jsonData['data']['type'] == 'boxScore': self['status'].setText('Bitte Pluginentwickler informieren:\nTelekomSportBoxScoreScreen BoxScore') return try: home = jsonData['data']['data']['teams']['home']['name'].encode('utf8') away = jsonData['data']['data']['teams']['away']['name'].encode('utf8') result_home = str(jsonData['data']['data']['results']['home']).encode('utf8') result_away = str(jsonData['data']['data']['results']['away']).encode('utf8') self['match_home'].setText(home) self['match_away'].setText(away) self['endResult'].setText(result_home + ' : ' + result_away) for period in jsonData['data']['data']['results']['periods']: self.resultList.append((period['name'].encode('utf8') + ' ' + period['value'].encode('utf8'), '')) except Exception as e: self['status'].setText('Bitte Pluginentwickler informieren:\nTelekomSportBoxScoreScreen BoxScore ' + str(e)) return self['list'].setList(self.resultList) self['status'].hide() # for summary def getCurrentEntry(self): return self['title'].getText() def getCurrentValue(self): return self['endResult'].getText() def createSummary(self): return TelekomSportMainScreenSummary class TelekomSportStatisticsScreen(Screen): def __init__(self, session, statistics_url): Screen.__init__(self, session) self.session = session self.setup_title = TelekomSportMainScreen.title self['status'] = Label('Lade Daten...') self['match'] = Label() self['title'] = Label('Spielstatistiken') self['version'] = Label(TelekomSportMainScreen.version) self.statList = [] self['list'] = List(self.statList) self['actions'] = ActionMap(['OkCancelActions'], { 'ok': self.close, 'cancel': self.close, }) downloadTelekomSportJson(generateUrl(statistics_url), boundFunction(loadTelekomSportJsonData, 'Statistic', self['status'], self.loadStatistics), boundFunction(handleTelekomSportDownloadError, 'Statistics', self['status'])) def loadStatistics(self, jsonData): if not jsonData['data']: return try: for ev in jsonData['data']: self['match'].setText(jsonData['data'][ev]['name'].encode('utf8')) for stat in jsonData['data'][ev]['statistics']: stat_name = stat['name'].encode('utf8') if stat['data']['type'] == 'ratio': home_prop = stat['data']['home']['proportion'] home_total = stat['data']['home']['total'] away_prop = stat['data']['away']['proportion'] away_total = stat['data']['away']['total'] if home_total == 0: home_percent = 0 else: home_percent = float(home_prop) / home_total * 100 if away_total == 0: away_percent = 0 else: away_percent = float(away_prop) / away_total * 100 if home_percent == 0 and away_percent == 0: percent = 50 else: percent = int(home_percent / (home_percent + away_percent) * 100) home_value_str = str(int(home_percent)) + '% (' + str(home_prop) + '/' + str(home_total) + ')' away_value_str = str(int(away_percent)) + '% (' + str(away_prop) + '/' + str(away_total) + ')' elif stat['data']['type'] == 'single': home_value = stat['data']['home']['value'] away_value = stat['data']['away']['value'] if home_value == 0 and away_value == 0: percent = 50 else: percent = int(float(home_value) / (home_value + away_value) * 100) home_value_str = str(home_value).encode('utf8') away_value_str = str(away_value).encode('utf8') else: continue self.statList.append((home_value_str, stat_name, away_value_str, percent)) except Exception as e: self['status'].setText('Bitte Pluginentwickler informieren:\nTelekomSportStatisticsScreen ' + str(e)) return self['list'].setList(self.statList) self['status'].hide() # for summary def getCurrentEntry(self): return self['title'].getText() def getCurrentValue(self): return self['match'].getText() def createSummary(self): return TelekomSportMainScreenSummary class TelekomSportScheduleScreen(Screen): def __init__(self, session, schedule_url): Screen.__init__(self, session) self.session = session self.setup_title = TelekomSportMainScreen.title self['title'] = Label() self['subtitle'] = Label('Spielplan') self['status'] = Label('Lade Daten...') self['version'] = Label(TelekomSportMainScreen.version) self.scheduleList = [] self['list'] = List() self['actions'] = ActionMap(['SetupActions', 'DirectionActions'], { 'cancel': self.close, 'ok': self.close, }) downloadTelekomSportJson(generateUrl(schedule_url), boundFunction(loadTelekomSportJsonData, 'Schedule', self['status'], self.loadSchedule), boundFunction(handleTelekomSportDownloadError, 'Schedule', self['status'])) def loadSchedule(self, jsonData): try: self['title'].setText(jsonData['data']['metadata']['parent_title'].encode('utf8')) for c in jsonData['data']['content']: for g in c['group_elements']: for d in g['data']['days']: for ev in d['events']: if ev['type'] in ('skyConferenceEvent', 'conferenceEvent'): continue description = ev['metadata']['description_bold'].encode('utf8') sub_description = ev['metadata']['description_regular'].encode('utf8') if sub_description: description = description + ' - ' + sub_description original = ev['metadata']['scheduled_start']['original'] starttime = datetime.strptime(original, '%Y-%m-%d %H:%M:%S') starttime_str = starttime.strftime('%d.%m.%Y %H:%M') home_team = ev['metadata']['details']['home']['name_full'].encode('utf8') away_team = ev['metadata']['details']['away']['name_full'].encode('utf8') if 'result' in ev['metadata']['details']['encounter']: home_goals = str(ev['metadata']['details']['encounter']['result']['home']).encode('utf8') away_goals = str(ev['metadata']['details']['encounter']['result']['away']).encode('utf8') else: home_goals = ' ' away_goals = ' ' match = home_team + ' ' + home_goals + ' - ' + away_goals + ' ' + away_team self.scheduleList.append((description, starttime_str, match)) except Exception as e: self['status'].setText('Bitte Pluginentwickler informieren:\nTelekomSportScheduleScreen ' + str(e)) return self['list'].setList(self.scheduleList) self['status'].hide() # for summary def getCurrentEntry(self): return self['title'].getText() def getCurrentValue(self): return self['subtitle'].getText() def createSummary(self): return TelekomSportMainScreenSummary class TelekomSportStandingsScreen(Screen): def __init__(self, session, standings_url): Screen.__init__(self, session) self.session = session self.setup_title = TelekomSportMainScreen.title self['title'] = Label() self['subtitle'] = Label('Tabelle') self['table_header_team'] = Label('Team') self['table_header_matches'] = Label('Spiele') self['table_header_wins'] = Label('S') self['table_header_draws'] = Label('U') self['table_header_losses'] = Label('N') self['table_header_goals'] = Label('Tore') self['table_header_goaldiff'] = Label('Diff') self['table_header_points'] = Label('Punkte') self['status'] = Label('Lade Daten...') self['version'] = Label(TelekomSportMainScreen.version) self.standingsList = [] self.playoffStandingsList = [] self['list'] = List() self.curr = 'standings' self['buttonblue'] = Label('') self['buttonblue'].hide() self['actions'] = ActionMap(['SetupActions', 'DirectionActions', 'ColorActions'], { 'cancel': self.close, 'ok': self.close, 'blue': self.switchList, }) self.toogleStandingsVisibility(False) downloadTelekomSportJson(generateUrl(standings_url), boundFunction(loadTelekomSportJsonData, 'Standings', self['status'], self.loadStandings), boundFunction(handleTelekomSportDownloadError, 'Standings', self['status'])) def toogleStandingsVisibility(self, show): self['table_header_team'].setVisible(show) self['table_header_matches'].setVisible(show) self['table_header_wins'].setVisible(show) self['table_header_draws'].setVisible(show) self['table_header_losses'].setVisible(show) self['table_header_goals'].setVisible(show) self['table_header_goaldiff'].setVisible(show) self['table_header_points'].setVisible(show) def showStandings(self): self['status'].hide() self['subtitle'].setText('Tabelle') if self['status'].getText() == '' or self['status'].getText() == 'Lade Daten...': self.toogleStandingsVisibility(True) self['status'].hide() self['list'].style = 'default' self['list'].setList(self.standingsList) else: self['status'].show() def showPlayoff(self): self['status'].hide() self['subtitle'].setText('Tabelle - Playoffs') if self['status'].getText() == '' or self['status'].getText() == 'Lade Daten...': self.toogleStandingsVisibility(False) self['list'].style = 'playoff' self['list'].setList(self.playoffStandingsList) else: self['status'].show() def switchList(self): if self.curr == 'standings' and self.playoffStandingsList: self.curr = 'playoff' self['buttonblue'].setText('Tabelle') self.showPlayoff() elif self.curr == 'standings' and not self.playoffStandingsList: self.showStandings() elif self.curr == 'playoff' and self.standingsList: self.curr = 'standings' self['buttonblue'].setText('Tabelle - Playoffs') self.showStandings() elif self.curr == 'playoff' and not self.standingsList: self.showPlayoff() def loadNormalStandingsTable(self, standingsList, jsonData, table): for team in jsonData['ranking']: rank = team['rank'] team_title = team['team_title'].encode('utf8') played = str(team['played']) win = str(team['win']) draw = str(team['draw']) loss = str(team['loss']) goals_for = str(team['goals_for']) goals_against = str(team['goals_against']) if 'goals_diff' in team: goal_diff = str(team['goals_diff']) elif 'goal_diff' in team: goal_diff = str(team['goal_diff']) else: goal_diff = '' points = str(team['points']) standingsList.append((str(rank), team_title, played, win, draw, loss, goals_for + ':' + goals_against, goal_diff, points, table, rank)) def loadNormalStandings(self, jsonData): try: if len(jsonData['data']['standing']) == 1: self.loadNormalStandingsTable(self.standingsList, jsonData['data']['standing'][0], 1) else: self.loadNormalStandingsTable(self.standingsList, jsonData['data']['standing'][0], 1) self.loadNormalStandingsTable(self.standingsList, jsonData['data']['standing'][1], 2) # add headers for the 2 icehockey standings self.standingsList.append(('', 'Gruppe 1', '', '', '', '', '', '', '', 1, 0)) self.standingsList.append(('', '', '', '', '', '', '', '', '', 2, -1)) self.standingsList.append(('', '', '', '', '', '', '', '', '', 2, -2)) self.standingsList.append(('', 'Gruppe 2', '', '', '', '', '', '', '', 2, 0)) self.standingsList = sorted(self.standingsList, key = lambda entry: (entry[9], entry[10])) if not self.playoff_standings_url: self.switchList() except Exception as e: self['status'].setText('Aktuell steht die Tabelle nicht zur Verfügung. Bitte versuchen sie es später noch einmal.' + str(e)) return def loadPlayoffStandings(self, jsonData): try: if 'title' in jsonData['data']: title = jsonData['data']['title'].encode('utf8') else: title = '' for round in jsonData['data']['rounds']: subtitle = round['title'].encode('utf8') for enc in round['encounters']: if not enc: continue home_team = enc['home']['title_mini'].encode('utf8') home_wins = str(enc['home']['wins']) away_team = enc['away']['title_mini'].encode('utf8') away_wins = str(enc['away']['wins']) if home_wins == 'None': home_wins = ' ' if away_wins == 'None': away_wins = ' ' encounters = home_team + ' ' + home_wins + ' - ' + away_wins + ' ' + away_team self.playoffStandingsList.append((subtitle, encounters)) self.switchList() except Exception as e: self['status'].setText('Aktuell steht die Playoff Tabelle nicht zur Verfügung. Bitte versuchen sie es später noch einmal.' + str(e)) return def loadStandings(self, jsonData): self.normal_standings_url = '' self.playoff_standings_url = '' try: self['title'].setText(jsonData['data']['metadata']['parent_title'].encode('utf8')) for c in jsonData['data']['content']: for g in c['group_elements']: if g['type'] == 'standings': self.normal_standings_url = g['data']['urls']['standings_url'].encode('utf8') elif g['type'] == 'playoffTree': self.playoff_standings_url = g['data']['url'].encode('utf8') except Exception as e: self['status'].setText('Aktuell steht die Tabelle nicht zur Verfügung. Bitte versuchen sie es später noch einmal.') return if self.normal_standings_url: downloadTelekomSportJson(generateUrl(self.normal_standings_url), boundFunction(loadTelekomSportJsonData, 'NormalStandings', self['status'], self.loadNormalStandings), boundFunction(handleTelekomSportDownloadError, 'NormalStandings', self['status'])) if self.playoff_standings_url: downloadTelekomSportJson(generateUrl(self.playoff_standings_url), boundFunction(loadTelekomSportJsonData, 'PlayoffStandings', self['status'], self.loadPlayoffStandings), boundFunction(handleTelekomSportDownloadError, 'PlayoffStandings', self['status'])) if self.normal_standings_url and self.playoff_standings_url: self['buttonblue'].show() # for summary def getCurrentEntry(self): return self['title'].getText() def getCurrentValue(self): return self['subtitle'].getText() def createSummary(self): return TelekomSportMainScreenSummary class TelekomSportEventScreen(Screen): oauth_url = 'https://accounts.login.idm.telekom.com/oauth2/auth' oauth_factorx_url = 'https://accounts.login.idm.telekom.com/factorx' oauth_token_url = 'https://accounts.login.idm.telekom.com/oauth2/tokens' jwt_url = 'https://www.magentasport.de/service/auth/app/login/jwt' stream_access_url = 'https://www.magentasport.de/service/player/v2/streamAccess' auth_code = '' def __init__(self, session, description, starttime, match, url, standings_url, schedule_url): Screen.__init__(self, session) self.session = session self.starttime = starttime self.standings_url = standings_url self.schedule_url = schedule_url self.match = match self.statistics_url = '' self.boxscore_url = '' self.conference_alarm_available = False self.league_id = '' self.setup_title = TelekomSportMainScreen.title self['match'] = Label(match) self['description'] = Label(description) self['subdescription'] = Label('') self['status'] = Label('Lade Daten...') self['pay'] = Label('* = Abo benötigt') self['confalarm'] = Label('') self['version'] = Label(TelekomSportMainScreen.version) self.videoList = [] self['list'] = List(self.videoList) self['actions'] = ActionMap(['MenuActions', 'SetupActions', 'DirectionActions'], { 'menu': self.closeRecursive, 'cancel': self.close, 'ok': self.ok, }) downloadTelekomSportJson(generateUrl(url), boundFunction(loadTelekomSportJsonData, 'Event', self['status'], self.buildScreen), boundFunction(handleTelekomSportDownloadError, 'Event', self['status'])) def closeRecursive(self): self.close(True) def findXsrfTid(self, html): pos = html.find('name="xsrf') xsrf_name = html[pos + 6: pos + 33] pos = html.find('value=',pos) xsrf_value = html[pos + 7: pos + 29] pos = html.find('name="tid" value="') tid = html[pos + 18: pos + 54] return xsrf_name, xsrf_value, tid def login(self, account, username, password, config_token, config_token_expiration_time): err = '' # check if token is present and valid if config_token.value and config_token_expiration_time.value > int(time.time()): return '' nonce = ''.join(random.sample(string.ascii_letters + string.digits, 20)) code_verifier = ''.join(random.sample(string.ascii_letters + string.digits, 20)) code_challenge = base64.urlsafe_b64encode(hashlib.sha256(code_verifier).digest()).split('=')[0] state = ''.join(random.sample(string.ascii_letters + string.digits, 20)) data = { 'prompt': 'x-no-sso', 'nonce': nonce, 'response_type': 'code', 'scope': 'openid', 'code_challenge': code_challenge, 'code_challenge_method': 'S256', 'redirect_uri': 'sso.magentasport://web_login_callback', 'client_id': '10LIVESAM30000004901MAGENTASPORTIOS00000', 'state': state} try: response = urllib.urlopen(self.oauth_url + '?' + urllib.urlencode(data), '') cookies = response.info().getheaders('Set-Cookie') html = response.read() xsrf_name, xsrf_value, tid = self.findXsrfTid(html) # send username data = { xsrf_name: xsrf_value, 'tid': tid, 'x-show-cancel': 'true', 'bdata': '' , 'pw_usr': username, 'pw_submit': '', 'hidden_pwd' :''} req = urllib2.Request(self.oauth_factorx_url, urllib.urlencode(data)) req.add_header('Cookie', ';'.join(cookies)) response = urllib2.urlopen(req) cookies += response.info().getheaders('Set-Cookie') html = response.read() xsrf_name, xsrf_value, tid = self.findXsrfTid(html) # send password data = { xsrf_name: xsrf_value, 'tid': tid, 'bdata':'' , 'hidden_usr': username, 'pw_submit': '', 'pw_pwd': password } # request is redirected which needs to be prevented opener = urllib2.build_opener(TelekomSportRedirectHandler(self)).open req = urllib2.Request(self.oauth_factorx_url, urllib.urlencode(data)) req.add_header('Cookie', ';'.join(cookies)) try: response = opener(req) except Exception as e: # ignore redirect error we need only auth_code which is set in the handler pass if self.auth_code == '': return 'Fehler beim Login ' + str(account) + '. Account. Kein auth code.' # get auth code token data = { 'code': self.auth_code, 'code_verifier': code_verifier, 'client_id': '10LIVESAM30000004901MAGENTASPORTIOS00000', 'grant_type': 'authorization_code' , 'redirect_uri': 'sso.magentasport://web_login_callback'} response = urllib2.urlopen(urllib2.Request(self.oauth_token_url, urllib.urlencode(data))) jsonData= json.loads(response.read()) # get tsm token data = { 'refresh_token': jsonData['refresh_token'], 'client_id': '10LIVESAM30000004901MAGENTASPORTIOS00000', 'grant_type':'refresh_token', 'redirect_uri': 'sso.magentasport://web_login_callback', 'scope':'tsm'} response = urllib2.urlopen(urllib2.Request(self.oauth_token_url, urllib.urlencode(data))) jsonData= json.loads(response.read()) if 'access_token' not in jsonData: if 'error_description' in jsonData: return jsonData['error_description'].encode('utf8') else: return 'Fehler beim Login ' + str(account) + '. Account. Kein access_token.' response = urllib2.urlopen(urllib2.Request(self.jwt_url, json.dumps({'token': jsonData['access_token']}), {'Content-Type': 'application/json'})).read() jsonResult = json.loads(response) if 'data' not in jsonResult or 'token' not in jsonResult['data']: return 'Fehler beim Login ' + str(account) + '. Account. Kein Token.' config_token.value = jsonResult['data']['token'] config_token.save() config_token_expiration_time.value = jsonResult['data']['expiration_time'] config_token_expiration_time.save() return '' except Exception as e: return 'Fehler beim Login ' + str(account) + '. Account. ' + str(e) def loginAllAccounts(self): if config.plugins.telekomsport.username1.value: p1, p2 = readPasswords(self.session) err = self.login(1, config.plugins.telekomsport.username1.value, p1, config.plugins.telekomsport.token1, config.plugins.telekomsport.token1_expiration_time) if err: return err else: if config.plugins.telekomsport.username2.value: err = self.login(2, config.plugins.telekomsport.username2.value, p2, config.plugins.telekomsport.token2, config.plugins.telekomsport.token2_expiration_time) return err return 'Account bitte in den Einstellungen hinterlegen.' def getStreamUrl(self, videoid, token): try: response = urllib2.urlopen(urllib2.Request(self.stream_access_url, json.dumps({'videoId': videoid}), {'xauthorization': token, 'Content-Type': 'application/json'}, {'label': '2780_hls'})).read() jsonResult = json.loads(response) if 'status' not in jsonResult or jsonResult['status'] != 'success': self['status'].setText('Fehler beim streamAccess') self['status'].show() return '', -1 url = 'https:' + jsonResult['data']['stream-access'][0] response = urllib.urlopen(url).read() xmlroot = ET.ElementTree(ET.fromstring(response)) playlisturl = xmlroot.find('token').get('url') + "?hdnea=" + xmlroot.find('token').get('auth') return playlisturl, 0 except urllib2.HTTPError as e: return '', e.code except urllib2.URLError as e2: if 'CERTIFICATE_VERIFY_FAILED' in str(e2.reason): return '', -2 return '', -1 def readExtXStreamInfLine(self, line, attributeListPattern): line = line.replace('#EXT-X-STREAM-INF:', '') for param in attributeListPattern.split(line)[1::2]: if param.startswith('BANDWIDTH='): return param.strip().split('=')[1] return '' def getFixQualtiyStreamUrl(self, m3u8_url): try: attributeListPattern = re.compile(r'''((?:[^,"']|"[^"]*"|'[^']*')+)''') streams = [] req = urllib2.Request(m3u8_url) req.add_header('User-Agent', 'Enigma2 HbbTV/1.1.1 (PVRRTSPDL;OpenPLi;;;)') response = urllib2.urlopen(req) self.cookies = response.info().getheaders('Set-Cookie') lines = response.readlines() if len(lines) > 0 and lines[0] == '#EXTM3U\n': i = 1 count_lines = len(lines) while i < len(lines) - 1: if lines[i].startswith('#EXT-X-STREAM-INF:'): bandwith = self.readExtXStreamInfLine(lines[i], attributeListPattern) if bandwith and i + 1 < count_lines: if lines[i+1].strip().startswith('https'): stream_url = lines[i+1].strip() else: stream_url = m3u8_url.rsplit('/', 1)[0] + '/' + lines[i+1].strip() streams.append((int(bandwith), stream_url)) i += 1 if streams: streams.sort(key = lambda x: x[0]) if len(streams) <> 5: print 'Warning: %d streams in m3u8. 5 expected' % len(streams) if int(config.plugins.telekomsport.stream_quality.value) < 3: return streams[0][1] else: return streams[len(streams)-1][1] return streams[int(config.plugins.telekomsport.stream_quality.value)][1] return '' except: return '' def playVideo(self, videoid, pay, title): # login if necessary if pay: err = self.loginAllAccounts() if err: self['status'].setText('Bezahlinhalt kann nicht angezeigt werden.\n' + err) self['status'].show() return # for non pay content you pass a random token -> token1 is also a valid token playlisturl, errorCode = self.getStreamUrl(videoid, config.plugins.telekomsport.token1.value) if (errorCode == 403 or not playlisturl) and config.plugins.telekomsport.username2.value: playlisturl, errorCode = self.getStreamUrl(videoid, config.plugins.telekomsport.token2.value) if errorCode == 403: self['status'].setText('Es wird ein Abo benötigt um den Inhalt anzuzeigen!') self['status'].show() return elif errorCode == -2: self['status'].setText('Bitte stellen sie das Datum ein!') self['status'].show() return elif errorCode == -1: self['status'].setText('Es ist ein Fehler aufgetreten. Der Stream kann nicht abgespielt werden!') self['status'].show() return if config.plugins.telekomsport.fix_stream_quality.value: url = self.getFixQualtiyStreamUrl(playlisturl) if url: playlisturl = url + "#User-Agent=Enigma2 HbbTV/1.1.1 (PVRRTSPDL;OpenPLi;;;)&Cookie=" + ";".join(self.cookies) ref = eServiceReference(4097, 0, playlisturl) ref.setName(title) self.session.open(TelekomSportMoviePlayer, ref, self.standings_url, self.schedule_url, self.statistics_url, self.boxscore_url, self.conference_alarm_available, self.league_id, videoid) def buildPreEventScreen(self, jsonData): pay = '' for content in jsonData['data']['content']: if content['group_elements']: for element in content['group_elements']: if element['type'] == 'noVideo': if 'pay' in element['data']['metadata'] and element['data']['metadata']['pay']: pay = ' *' if self.starttime.date() == datetime.today().date(): self['subdescription'].setText('Die Übertragung startet heute um ' + self.starttime.strftime('%H:%M') + pay) else: self['subdescription'].setText('Die Übertragung startet am ' + self.starttime.strftime('%d.%m.%Y') + ' um ' + self.starttime.strftime('%H:%M') + pay) def buildPostEventScreen(self, jsonData): self['subdescription'].setText('Übertragung vom ' + self.starttime.strftime('%d.%m.%Y %H:%M') + '\n\nVideos:') for content in jsonData['data']['content']: if content['group_elements']: for element in content['group_elements']: if element['type'] in ('eventVideos', 'player'): if element['type'] == 'eventVideos': # remove all previous player videolist entries. This is needed for Bayern.TV self.videoList = [] for videos in element['data']: title = videos['title'].encode('utf8') if videos['pay']: title += ' *' self.videoList.append((title, self.match + ' - ' + videos['title'].encode('utf8'), str(videos['videoID']), str(videos['pay']))) elif element['type'] == 'statistics': self.statistics_url = element['data']['url'].encode('utf8') elif element['type'] == 'boxScore': self.boxscore_url = element['data_url'].encode('utf8') def buildLiveEventScreen(self, jsonData): self['subdescription'].setText('Übertragung vom ' + self.starttime.strftime('%d.%m.%Y %H:%M') + '\n\nVideos:') if 'has_alerts' in jsonData['data']['metadata']['event_metadata']: self.conference_alarm_available = jsonData['data']['metadata']['event_metadata']['has_alerts'] == True if self.conference_alarm_available: self['confalarm'].setText('Konferenzalarm ist verfügbar. Bitte Info/EPG Taste drücken um ihn zu aktivieren.') if 'league_id' in jsonData['data']['metadata']['event_metadata']: self.league_id = str(jsonData['data']['metadata']['event_metadata']['league_id']) for content in jsonData['data']['content']: if content['group_elements']: for element in content['group_elements']: if element['type'] == 'player': title = 'Live Stream' if element['data'][0]['pay']: title += ' *' self.videoList.append((title, element['data'][0]['title'].encode('utf8'), str(element['data'][0]['videoID']), str(element['data'][0]['pay']))) elif element['type'] == 'statistics': self.statistics_url = element['data']['url'].encode('utf8') elif element['type'] == 'boxScore': self.boxscore_url = element['data_url'].encode('utf8') def buildScreen(self, jsonData): self.videoList = [] if not jsonData['data'] or not jsonData['data']['content'] or not jsonData['data']['metadata']: self['status'].setText('Bitte Pluginentwickler informieren:\nTelekomSportEventScreen') return try: if jsonData['data']['metadata']['state'] == 'pre': self.buildPreEventScreen(jsonData) elif jsonData['data']['metadata']['state'] == 'post': self.buildPostEventScreen(jsonData) elif jsonData['data']['metadata']['state'] == 'live': self.buildLiveEventScreen(jsonData) except Exception as e: self['status'].setText('Bitte Pluginentwickler informieren:\nTelekomSportEventScreen ' + str(e)) return self['list'].setList(self.videoList) self['status'].hide() def ok(self): if self['list'].getCurrent(): title = self['list'].getCurrent()[1] videoid = self['list'].getCurrent()[2] pay = self['list'].getCurrent()[3] self.playVideo(videoid, pay == 'True', title) # for summary def getCurrentEntry(self): if self['list'].getCurrent(): return self['list'].getCurrent()[1] return self['match'].getText() def getCurrentValue(self): return ' ' def createSummary(self): return TelekomSportMainScreenSummary class TelekomSportEventLaneScreen(Screen): def __init__(self, session, main_title, title, url, epg_url, standings_url, schedule_url): Screen.__init__(self, session) self.session = session self.standings_url = standings_url self.schedule_url = schedule_url self.setup_title = TelekomSportMainScreen.title self['title'] = Label(main_title) self['subtitle'] = Label(title) self['status'] = Label('Lade Daten...') self['version'] = Label(TelekomSportMainScreen.version) self.eventList = [] self['list'] = List(self.eventList) self['actions'] = ActionMap(['MenuActions', 'SetupActions', 'DirectionActions'], { 'menu': self.closeRecursive, 'cancel': self.close, 'ok': self.ok, }) if url != '': downloadTelekomSportJson(generateUrl(url), boundFunction(loadTelekomSportJsonData, 'EventLane', self['status'], self.buildList), boundFunction(handleTelekomSportDownloadError, 'EventLane', self['status'])) elif epg_url != '': downloadTelekomSportJson(generateUrl(epg_url), boundFunction(loadTelekomSportJsonData, 'EventLane', self['status'], self.buildEpgList), boundFunction(handleTelekomSportDownloadError, 'EventLane', self['status'])) def closeRecursive(self): self.close(True) def addEventToList(self, events): if 'target_type' in events and events['target_type'] == 'event' and (events['target_playable'] or not config.plugins.telekomsport.hide_unplayable.value): description = events['metadata']['description_bold'].encode('utf8') subdescription = events['metadata']['description_regular'].encode('utf8') if events['metadata']['scheduled_start']['original'] is None: starttime = datetime.now() starttime_str = 'Jetzt' else: original = events['metadata']['scheduled_start']['original'].encode('utf8') starttime = datetime.strptime(original, '%Y-%m-%d %H:%M:%S') starttime_str = starttime.strftime('%d.%m.%Y %H:%M') urlpart = events['target'].encode('utf8') if subdescription: description = description + ' - ' + subdescription if 'home' in events['metadata']['details'] and 'name_full' in events['metadata']['details']['home'] and events['metadata']['details']['home']['name_full'].encode('utf8') <> '': home = events['metadata']['details']['home']['name_full'].encode('utf8') away = events['metadata']['details']['away']['name_full'].encode('utf8') self.eventList.append((description, starttime_str, home + ' - ' + away, urlpart, starttime)) else: title = events['metadata']['title'].encode('utf8') self.eventList.append((description, starttime_str, title, urlpart, starttime)) def buildList(self, jsonData): try: if 'panels' in jsonData['data']['data']: jsonData = jsonData['data']['data']['panels'] else: jsonData = jsonData['data']['data'] for events in jsonData: if 'event' in events: events = events['event'] self.addEventToList(events) except Exception as e: self['status'].setText('Bitte Pluginentwickler informieren:\nTelekomSportEventLaneScreen ' + str(e)) return self['list'].setList(self.eventList) self['status'].hide() def buildEpgList(self, jsonData): try: for element in jsonData['data']['elements']: for slot in element['slots']: for events in slot['events']: self.addEventToList(events) except Exception as e: self['status'].setText('Bitte Pluginentwickler informieren:\nTelekomSportEventLaneScreen ' + str(e)) return self['list'].setList(self.eventList) self['status'].hide() def ok(self): if self['list'].getCurrent(): description = self['list'].getCurrent()[0] match = self['list'].getCurrent()[2] urlpart = self['list'].getCurrent()[3] starttime = self['list'].getCurrent()[4] self.session.openWithCallback(self.recursiveClose, TelekomSportEventScreen, description, starttime, match, urlpart, self.standings_url, self.schedule_url) def recursiveClose(self, *retVal): if retVal: self.close(True) # for summary def getCurrentEntry(self): if self['list'].getCurrent(): return self['list'].getCurrent()[0] else: return ' ' def getCurrentValue(self): if self['list'].getCurrent(): return self['list'].getCurrent()[2] else: return ' ' def createSummary(self): return TelekomSportMainScreenSummary class TelekomSportSportsTypeScreen(Screen): def __init__(self, session, title, url): Screen.__init__(self, session) self.session = session self.main_title = title self.standings_url = '' self.schedule_url = '' self.setup_title = TelekomSportMainScreen.title # for update self.telekomSportMainScreen = session.current_dialog self['title'] = Label(title) self['subtitle'] = Label('') self['status'] = Label('Lade Daten...') self['version'] = Label(TelekomSportMainScreen.version) self.eventLaneList = [] self['list'] = List(self.eventLaneList) self['buttonblue'] = Label('') self['buttonblue'].hide() self['buttongreen'] = Label('Update') if self.telekomSportMainScreen.update_exist == False: self['buttongreen'].hide() self['actions'] = ActionMap(['MenuActions', 'SetupActions', 'DirectionActions', 'ColorActions'], { 'menu': self.closeRecursive, 'cancel': self.close, 'ok': self.ok, 'blue': self.showTableResults, 'green': self.update, }) downloadTelekomSportJson(generateUrl(url), boundFunction(loadTelekomSportJsonData, 'SportsType', self['status'], self.buildList), boundFunction(handleTelekomSportDownloadError, 'SportsType', self['status'])) def update(self): if self.telekomSportMainScreen.update_exist: self.session.openWithCallback(self.telekomSportMainScreen.updateConfirmed, MessageBox, 'Ein Update ist verfügbar. Wollen sie es installieren?\nInformationen:\n' + self.telekomSportMainScreen.updateText, MessageBox.TYPE_YESNO, default = False) def closeRecursive(self): self.close(True) def buildList(self, jsonData): try: title = '' for content in jsonData['data']['content']: if content['title'] and content['title'] != '': title = content['title'].encode('utf8') for group_element in content['group_elements']: if group_element['type'] == 'eventLane' or group_element['type'] == 'editorialLane' or group_element['type'] == 'teaserGrid': subtitle = group_element['title'].encode('utf8') urlpart = group_element['data_url'].encode('utf8') if content['title'] != '' and subtitle == '': self.eventLaneList.append((title, subtitle, title, urlpart, '')) elif content['title'] != '' and subtitle != '': self.eventLaneList.append((title, '', title, '', '')) self.eventLaneList.append(('', subtitle, title + ' - ' + subtitle, urlpart, '')) elif content['title'] == '' and subtitle != '': self.eventLaneList.append(('', subtitle, subtitle, urlpart, '')) else: self.eventLaneList.append(('Aktuelles', subtitle, 'Aktuelles', urlpart, '')) if group_element['type'] == 'teaserGrid': # read epg data to get all matches content_id = group_element['content_id'] self.eventLaneList.append(('Spielplan', '', 'Spielplan', '', '/epg/content/' + str(content_id))) if 'navigation' in jsonData['data'] and 'header' in jsonData['data']['navigation']: for header in jsonData['data']['navigation']['header']: if header['target_type'] == 'standings': self.standings_url = header['target'].encode('utf8') self['buttonblue'].setText('Tabelle') self['buttonblue'].show() if header['target_type'] == 'schedule': self.schedule_url = header['target'].encode('utf8') except Exception as e: self['status'].setText('Bitte Pluginentwickler informieren:\nTelekomSportSportsTypeScreen ' + str(e)) return self['list'].setList(self.eventLaneList) self['status'].hide() def ok(self): if self['list'].getCurrent(): title = self['list'].getCurrent()[2] urlpart = self['list'].getCurrent()[3] epg_urlpart = self['list'].getCurrent()[4] if urlpart != '' or epg_urlpart != '': self.session.openWithCallback(self.recursiveClose, TelekomSportEventLaneScreen, self.main_title, title, urlpart, epg_urlpart, self.standings_url, self.schedule_url) def recursiveClose(self, *retVal): if retVal: self.close(True) def showTableResults(self): if self.standings_url: self.session.open(TelekomSportStandingsScreen, self.standings_url) # for summary def getCurrentEntry(self): return self['title'].getText() def getCurrentValue(self): if self['list'].getCurrent(): return self['list'].getCurrent()[2] else: return ' ' def createSummary(self): return TelekomSportMainScreenSummary class TelekomSportMainScreen(Screen): version = 'v3.0.0' base_url = 'https://www.magentasport.de' api_url = '/api/v3/mobile' main_page = '/navigation' title = 'Magenta Sport' def __init__(self, session, args = None): Screen.__init__(self, session) self.session = session self.updateUrl = '' self.updateText = '' self.filename = '' self.setup_title = TelekomSportMainScreen.title self['title'] = Label('') self['subtitle'] = Label('') self['status'] = Label('Lade Daten...') self['version'] = Label(self.version) self.sportslist = [] self['list'] = List(self.sportslist) self['buttonblue'] = Label('Einstellungen') self['buttongreen'] = Label('Update') self['buttongreen'].hide() self['actions'] = ActionMap(['SetupActions', 'DirectionActions', 'ColorActions'], { 'cancel': self.close, 'ok': self.ok, 'blue': self.showSetup, 'green': self.update, }) downloadTelekomSportJson(generateUrl(self.main_page), boundFunction(loadTelekomSportJsonData, 'Main', self['status'], self.buildList), boundFunction(handleTelekomSportDownloadError, 'Main', self['status'])) self.onLayoutFinish.append(self.checkForUpdate) self.migrate_timer = eTimer() if telekomsport_isDreamOS: self.migrate_timer_conn = self.migrate_timer.timeout.connect(self.checkNewPasswordFileIsUsed) else: self.migrate_timer.callback.append(self.checkNewPasswordFileIsUsed) self.migrate_timer.start(200, True) def buildList(self, jsonData): default_section_choicelist = [('', 'Default')] self.sportslist.append(('MagentaSport Hauptseite', '', 'MagentaSport Hauptseite', '/page/1')) default_section_choicelist.append(('MagentaSport Hauptseite', 'MagentaSport Hauptseite')) try: for sports in jsonData['data']['league_filter']: title = sports['title'].encode('utf8') if title != "" and 'target' in sports: self.sportslist.append((title, '', title, sports['target'].encode('utf8'))) default_section_choicelist.append((title, title)) except Exception as e: self['status'].setText('Bitte Pluginentwickler informieren:\nTelekomSportMainScreen ' + str(e)) return config.plugins.telekomsport.default_section_chooser.setChoices(default_section_choicelist, '') self['list'].setList(self.sportslist) self['status'].hide() self.selectDefaultSportsType() def ok(self): if self['list'].getCurrent(): title = self['list'].getCurrent()[2] urlpart = self['list'].getCurrent()[3] self.session.openWithCallback(self.recursiveClose, TelekomSportSportsTypeScreen, title, urlpart) def recursiveClose(self, *retVal): if retVal: self.close() def selectDefaultSportsType(self): if config.plugins.telekomsport.default_section.value: items = filter(lambda x: x[0] == config.plugins.telekomsport.default_section.value or x[1] == config.plugins.telekomsport.default_section.value, self.sportslist) if items: self.selectSportsType(items[0]) def selectSportsType(self, item): if item: title = item[2] urlpart = item[3] self.session.openWithCallback(self.recursiveClose, TelekomSportSportsTypeScreen, title, urlpart) def showSetup(self): self.session.open(TelekomSportConfigScreen) # for summary def getCurrentEntry(self): if self['list'].getCurrent(): return self['list'].getCurrent()[2] else: return ' ' def getCurrentValue(self): return ' ' def createSummary(self): return TelekomSportMainScreenSummary # for update def checkForUpdate(self): url = 'https://api.github.com/repos/E2OpenPlugins/e2openplugin-TelekomSport/releases' header = { 'Accept' : 'application/vnd.github.v3+json' } req = urllib2.Request(url, None, header) self.update_exist = False try: response = urllib2.urlopen(req) jsonData = json.loads(response.read()) for rel in jsonData: if rel['target_commitish'] != 'master': continue if self.version < rel['tag_name']: self.updateText = rel['body'].encode('utf8') for asset in rel['assets']: if telekomsport_isDreamOS and asset['name'].endswith('.deb'): self.updateUrl = asset['browser_download_url'].encode('utf8') self.filename = '/tmp/enigma2-plugin-extensions-telekomsport.deb' self['buttongreen'].show() self.update_exist = True break elif (not telekomsport_isDreamOS) and asset['name'].endswith('.ipk'): self.updateUrl = asset['browser_download_url'].encode('utf8') self.filename = '/tmp/enigma2-plugin-extensions-telekomsport.ipk' self['buttongreen'].show() self.update_exist = True break if self.version >= rel['tag_name'] or self.updateUrl != '': break except Exception as e: pass def update(self): if self.updateUrl: self.session.openWithCallback(self.updateConfirmed, MessageBox, 'Ein Update ist verfügbar. Wollen sie es installieren?\nInformationen:\n' + self.updateText, MessageBox.TYPE_YESNO, default = False) def updateConfirmed(self, answer): if answer: downloader = TelekomSportFileDownloader(telekomsport_isDreamOS) downloader.start(self.updateUrl, self.filename, self.downloadFinished, self.updateFailed) def downloadFinished(self): self.container = eConsoleAppContainer() if telekomsport_isDreamOS: self.container.appClosed_conn = self.container.appClosed.connect(self.updateFinished) self.container.execute('dpkg -i ' + self.filename) else: self.container.appClosed.append(self.updateFinished) self.container.execute('opkg update; opkg install ' + self.filename) def updateFailed(self, reason): self.updateFinished(1) def updateFinished(self, retval): self['buttongreen'].hide() self.updateUrl = '' if retval == 0: self.session.openWithCallback(self.restartE2, MessageBox, 'Das Magenta Sport Plugin wurde erfolgreich installiert!\nSoll das E2 GUI neugestartet werden?', MessageBox.TYPE_YESNO, default = False) else: self.session.open(MessageBox, 'Bei der Installation ist ein Problem aufgetreten.', MessageBox.TYPE_ERROR) def restartE2(self, answer): if answer: self.session.open(TryQuitMainloop, 3) def checkNewPasswordFileIsUsed(self): if not path.isfile('/etc/enigma2/MagentaSport.cfg'): print 'Migrating passwords' if savePasswords(self.session, decode(config.plugins.telekomsport.password1.value), decode(config.plugins.telekomsport.password2.value)): config.plugins.telekomsport.password1.value = '' config.plugins.telekomsport.password1.save() config.plugins.telekomsport.password2.value = '' config.plugins.telekomsport.password2.save() def main(session, **kwargs): session.open(TelekomSportMainScreen) def Plugins(**kwargs): return PluginDescriptor(name='Magenta Sport', description=_('Magenta Sport Plugin'), where = PluginDescriptor.WHERE_PLUGINMENU, icon='plugin.png', fnc=main)