diff --git a/.pylintrc b/.pylintrc
index 6d571a9..fd30fdd 100644
--- a/.pylintrc
+++ b/.pylintrc
@@ -130,7 +130,7 @@ disable=print-statement,
R0201, R0205,
C0103, C0114, C0326, C0330,
- W0122, W0123, W0201, W0311, W0312, W0603, W0703, W1505,
+ W0122, W0123, W0201, W0212, W0221, W0311, W0312, W0603, W0614, W0703, W1113, W1505,
E0401, E0611, E1101
# Enable the message, report, category or checker with the given id(s). You can
diff --git a/CONTROL/control b/CONTROL/control
index ac5ab05..0e234ea 100644
--- a/CONTROL/control
+++ b/CONTROL/control
@@ -1,6 +1,6 @@
Description: MediathekCockpit
Maintainer: dream-alpha
Package: enigma2-plugin-extensions-mediathekcockpit
-Version: 0.1.2
+Version: 0.2.5
Architecture: all
Depends: python-requests, enigma2-plugin-skincomponents-extmultilistselection
diff --git a/Components/Converter/MvVideoInfo.py b/Components/Converter/MTCVideoInfo.py
similarity index 97%
rename from Components/Converter/MvVideoInfo.py
rename to Components/Converter/MTCVideoInfo.py
index 2bff50d..31dbaaf 100644
--- a/Components/Converter/MvVideoInfo.py
+++ b/Components/Converter/MTCVideoInfo.py
@@ -3,7 +3,7 @@
from enigma import iServiceInformation, iPlayableService
-class MvVideoInfo(Converter, object):
+class MTCVideoInfo(Converter, object):
def __init__(self, atype):
Converter.__init__(self, atype)
diff --git a/po/de.po b/po/de.po
index 4d5a856..8997567 100644
--- a/po/de.po
+++ b/po/de.po
@@ -1,8 +1,8 @@
msgid ""
msgstr ""
"Project-Id-Version: \n"
-"POT-Creation-Date: 2024-04-16 16:18+0200\n"
-"PO-Revision-Date: 2024-04-16 16:26+0200\n"
+"POT-Creation-Date: 2024-04-22 13:47+0200\n"
+"PO-Revision-Date: 2024-04-22 13:47+0200\n"
"Last-Translator: dream-alpha\n"
"Language-Team: \n"
"Language: de_DE\n"
@@ -14,269 +14,158 @@ msgstr ""
"X-Poedit-Basepath: ../src\n"
"X-Poedit-SearchPath-0: .\n"
-msgid "Error during download."
-msgstr "Fehler beim Download"
-
-msgid "Clock"
-msgstr "Uhr"
-
-msgid "Live channels"
-msgstr "Live-Kanäle"
-
-msgid "Channel selection"
+msgid "Channel Selection"
msgstr "Kanalauswahl"
-msgid "All movies"
-msgstr "Alle Filme"
-
-msgid "Search"
-msgstr "Suche nach:"
-
-msgid "Favorites"
-msgstr "Favoriten"
-
-msgid "MediathekCockpit"
-msgstr "MediathekCockpit"
-
-msgid "Download started..."
-msgstr "Download wurde gestartet..."
-
-msgid "Low"
-msgstr "Niedrig"
-
-msgid "Medium"
-msgstr "Mittel"
-
-msgid "Maximum"
-msgstr "Maximum"
-
-msgid "Video"
-msgstr "Film"
-
-msgid "Resolution"
-msgstr "Auflösung"
-
-msgid "Option Selector"
-msgstr "Auswahl"
+msgid "Live"
+msgstr "Live"
msgid "Movies"
msgstr "Filme"
-msgid "No favorites available"
-msgstr "Keine Favoriten verfügbar"
-
-msgid "Search list"
-msgstr "Suchliste"
-
-msgid "No search list available"
-msgstr "Keine Suchliste verfügbar"
-
-msgid "Delete"
-msgstr "Löschen"
-
-msgid "Enter search text"
-msgstr "Eingabe des Suchtexts"
-
-msgid "Easy selection"
-msgstr "Einfach-Auswahl"
-
-msgid "All"
-msgstr "Alle"
-
-msgid "Manual search"
-msgstr "Manuelle Suche"
+msgid "Search"
+msgstr "Suche"
-msgid "Search for all entries in the search list"
-msgstr "Suche nach allen Einträgen in der Suchliste"
+msgid "All channels"
+msgstr "Alle Kanäle"
-msgid "Edit selected entry"
-msgstr "Ausgewählten Eintrag editieren"
+msgid "Download failed"
+msgstr "Download fehlgeschlagen"
-msgid "Remove entry"
-msgstr "Auswahl entfernen"
+msgid "High"
+msgstr "Hoch"
-msgid "Add entry"
-msgstr "Eintrag hinzufügen"
+msgid "Medium"
+msgstr "Mittel"
-msgid "Search for '%s'"
-msgstr "Suche nach %s:"
+msgid "Low"
+msgstr "Niedrig"
-msgid "Manual Search"
-msgstr "Manuelle Suche"
+msgid "Do nothing"
+msgstr "Nichts tun"
-msgid "Select"
-msgstr "Auswählen"
+msgid "Ask user"
+msgstr "Benutzer fragen"
-msgid "Added '%s'"
-msgstr "'%s' hinzugefügt"
+msgid "Close"
+msgstr "Schliessen"
-msgid "Entry already exists"
-msgstr "Eintrag existiert bereits"
+msgid "Save"
+msgstr "Speichern"
-msgid "Added:"
-msgstr "Hinzugefügt:"
+msgid "Abort current download"
+msgstr "Laufenden Download abbrechen"
-msgid "Directory %s does not exist."
-msgstr "Verzeichnis %s existiert nicht."
+msgid "Abort all downloads"
+msgstr "Alle Downloads abbrechen"
-msgid "Name"
-msgstr "Name"
+msgid "Abort all pending downloads"
+msgstr "Alle wartenden Downloads abbrechen"
-msgid "Fields:"
-msgstr "Felder:"
+msgid "Abort"
+msgstr "Abbrechen"
-msgid "Channel"
-msgstr "Kanal"
+msgid "Channels"
+msgstr "Kanäle"
-msgid "Add favorite"
-msgstr "Zu Favoriten hinzufügen"
+msgid "Default"
+msgstr "Standard"
-msgid "Remove favorite"
-msgstr "Aus Favoriten entfernen"
+msgid "options"
+msgstr "Optionen"
-msgid "movie"
-msgstr "Film"
+msgid "Live channels"
+msgstr "Live-Kanäle"
-msgid "Save"
-msgstr "Speichern"
+msgid "Download Manager"
+msgstr "Download Manager"
-msgid "Setup"
+msgid "Settings"
msgstr "Einstellungen"
-msgid "Download manager"
-msgstr "Download Manager"
-
-msgid "Default"
-msgstr "Standard"
+msgid "About"
+msgstr "Über"
-msgid "options"
-msgstr "Optionen"
+msgid "Functions"
+msgstr "Funktionen"
-msgid "Move selected item up"
-msgstr "Ausgewählter Eintrag nach oben"
+msgid "Menu"
+msgstr "Menü"
-msgid "Move selected item down"
-msgstr "Ausgewähler Eintrag nach unten"
-
-msgid "Reset"
-msgstr "Zurücksetzen"
+msgid "Quit"
+msgstr "Beenden"
-msgid "remove entry"
-msgstr "Eintrag entfernen"
+msgid "Restart from the beginning"
+msgstr "Neustart von Anfang an"
-msgid "Please wait... Loading list..."
-msgstr "Liste wird geladen..."
+msgid "None"
+msgstr "Nichts"
-msgid "Page:"
-msgstr "Seite:"
+msgid "Action"
+msgstr "Aktion"
-msgid "Online"
-msgstr "Online"
+msgid "Search results"
+msgstr "Suchergebnisse"
-msgid "Please wait..."
-msgstr "Bitten warten..."
+msgid "Download"
+msgstr "Download"
msgid "Next page"
msgstr "Nächste Seite"
-msgid "Entries"
-msgstr "Einträge"
-
-msgid "Time"
-msgstr "Zeit"
-
-msgid "From"
-msgstr "Von"
+msgid "Select"
+msgstr "Auswählen"
-msgid "Date"
-msgstr "Datum"
+msgid "Broadcast time"
+msgstr "Sendezeit"
msgid "Duration"
msgstr "Dauer"
-msgid "Download failed"
-msgstr "Download fehlgeschlagen"
-
-msgid "Close"
-msgstr "Schliessen"
-
-msgid "Current"
-msgstr "Aktuellen"
-
-msgid "Abort"
-msgstr "Abbrechen"
-
-msgid "All open"
-msgstr "Alle offene"
-
-msgid "Search entry"
-msgstr "Suche"
+msgid "Quality"
+msgstr "Auflösung"
-msgid "Cancel"
-msgstr "Abbrechen"
+msgid "Entries"
+msgstr "Einträge"
-msgid "Topic"
-msgstr "Thema"
+msgid "Load time"
+msgstr "Ladedauer"
-msgid "Title"
-msgstr "Titel"
+msgid "From"
+msgstr "Ladezeit"
-msgid "Description"
-msgstr "Beschreibung"
+msgid "Download added"
+msgstr "Download hinzugefügt"
-msgid "Search for:"
-msgstr "Suche nach:"
+msgid "Video resolution"
+msgstr "Filmauflösung"
-msgid "Channels:"
-msgstr "Kanäle:"
+msgid "Movie directory does not exist"
+msgstr "Filmverzeichnis existiert nicht"
-msgid "Enter text to search for"
+msgid "Enter search text"
msgstr "Eingabe des Suchtexts"
-msgid "No, do nothing."
-msgstr "Nichts tun"
+msgid "MediathekCockpit"
+msgstr "MediathekCockpit"
-msgid "Ask user"
-msgstr "Benutzer fragen"
+msgid "Setup"
+msgstr "Einstellungen"
+
+msgid "Cancel"
+msgstr "Abbrechen"
msgid "Entries per page"
msgstr "Einträge pro Seite"
-msgid "Future"
-msgstr "Filme aus der Zukunft anzeigen"
-
-msgid "Yellow button mapping"
-msgstr "Belegung der gelben Taste"
-
-msgid "Minimum movie"
-msgstr "Minimale Film"
-
-msgid "minutes"
-msgstr "Minuten"
-
-msgid "disabled"
-msgstr "deaktiviert"
-
-msgid "Maximum movie"
-msgstr "Maximale Film"
+msgid "Show future movies"
+msgstr "Zeige zukünftige Filme"
-msgid "Behavior when a movie is stopped"
+msgid "Behavior when movie stopped"
msgstr "Verhalten beim Stoppen des Films"
-msgid "Select movie path"
-msgstr "Auswahl des Filmpfads"
-
-msgid "Quit"
-msgstr "Beenden"
-
-msgid "Restart from the beginning"
-msgstr "Neustart von Anfang an"
-
-msgid "None"
-msgstr "Nichts"
-
-msgid "Action"
-msgstr "Aktion"
+msgid "Movie path"
+msgstr "Filmpfad"
msgid "Browse Mediathek libraries"
msgstr "Mediatheken durchsuchen, Filme abspielen und herunterladen"
@@ -286,3 +175,15 @@ msgstr "Mediathek Downloads ..."
msgid "MediathekCockpit Search"
msgstr "MediathekCockpit Suche"
+
+msgid "Plugin"
+msgstr "Plugin"
+
+msgid "Version"
+msgstr "Version"
+
+msgid "Copyright"
+msgstr "Copyright"
+
+msgid "License"
+msgstr "Lizenz"
diff --git a/src/About.py b/src/About.py
new file mode 100644
index 0000000..32a751d
--- /dev/null
+++ b/src/About.py
@@ -0,0 +1,35 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Screens.MessageBox import MessageBox
+from .Version import PLUGIN, VERSION, COPYRIGHT, LICENSE
+from .__init__ import _
+
+
+def about(session):
+ session.open(
+ MessageBox,
+ _("Plugin") + ": " + PLUGIN + "\n\n"
+ + _("Version") + ": " + VERSION + "\n\n"
+ + _("Copyright") + ": " + COPYRIGHT + "\n\n"
+ + _("License") + ": " + LICENSE,
+ MessageBox.TYPE_INFO
+ )
diff --git a/src/Channels.py b/src/Channels.py
new file mode 100644
index 0000000..3a80679
--- /dev/null
+++ b/src/Channels.py
@@ -0,0 +1,163 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+import json
+from Screens.Screen import Screen
+from Screens.MessageBox import MessageBox
+from Components.ActionMap import ActionMap
+from Components.Label import Label
+from Components.Sources.StaticText import StaticText
+from Components.Sources.List import List
+from .Debug import logger
+from .LoadPixmap import loadPixmap
+from .Downloader import MyGetPage, headersplain
+from .MyDeferredSemaphore import MyDeferredSemaphore
+from .__init__ import _
+from .Version import PLUGIN
+from .Menu import Menu
+from .Constants import plugindir, CHAN_CHANNEL
+
+
+class Channels(Screen):
+ def attrgetter(self, default=None):
+ attr = self
+
+ def get_any(_self):
+ return getattr(Channels, attr, default)
+ return get_any
+
+ def attrsetter(self):
+ attr = self
+
+ def set_any(_self, value):
+ setattr(Channels, attr, value)
+ return set_any
+
+ curr_index = property(attrgetter('_curr_index', 0), attrsetter('_curr_index'))
+
+ def __init__(self, session, postdata):
+ logger.info("curr_index: %s", self.curr_index)
+ self.postdata = postdata
+ Screen.__init__(self, session)
+ self.skinName = 'MediathekCockpit'
+ self.title = PLUGIN + ' - ' + _('Channel Selection')
+
+ self['key_red'] = StaticText(_('Live'))
+ self['key_green'] = StaticText()
+ self['key_yellow'] = StaticText(_('Movies'))
+ self['key_blue'] = StaticText(_('Search'))
+
+ self['description'] = Label('')
+ self['date'] = Label('')
+ self['sresult'] = Label('')
+
+ self['list'] = List()
+
+ self['actions'] = ActionMap(
+ ['MTC_Actions'],
+ {
+ 'ok': self.pressOk,
+ 'cancel': self.pressClose,
+ 'red': self.pressRed,
+ # 'green': self.pressGreen,
+ 'yellow': self.pressYellow,
+ 'blue': self.pressBlue,
+ 'menu': self.pressMenu,
+ # 'info': self.pressInfo
+ }
+ )
+ self['list'].onSelectionChanged.append(self.__SelectionChanged)
+ self.onLayoutFinish.append(self.__onLayoutFinish)
+ self.download = MyDeferredSemaphore(tokens=1)
+
+ def __onLayoutFinish(self):
+ logger.info("...")
+ url = 'https://mediathekviewweb.de/api/channels'
+ self.download.start(
+ MyGetPage,
+ url=url,
+ headers=headersplain
+ ).addCallback(self.downloadSucceeded).addErrback(self.downloadFailed)
+
+ def downloadSucceeded(self, result):
+ logger.info("...")
+ self['list'].style = 'default'
+ self.list = []
+ self.curr_index2 = self.curr_index
+ if result:
+ self.list.append((_("All channels"), "", None))
+ data = json.loads(result)
+ # logger.info("data: %s", data)
+ if not data.get('error', True):
+ channels = data['channels']
+ for channel in channels:
+ name = channel.encode('utf-8')
+ ptr = loadPixmap('%slogos/%s.png' % (plugindir, name.replace(' ', '').upper()))
+ self.list.append((name, '', ptr))
+ # logger.info("list: %s", self.list)
+ self["list"].setList(self.list)
+ self["list"].setIndex(self.curr_index2)
+
+ def downloadFailed(self, failure, *args):
+ logger.info("...")
+ error_message = str(failure.getErrorMessage())
+ if not error_message:
+ error_message = str(failure)
+ logger.error("%s, %s", error_message, str(args))
+ if 'CancelledError' not in error_message:
+ self.session.open(MessageBox, error_message, MessageBox.TYPE_INFO, timeout=10, title=_('Download failed'))
+
+ def __SelectionChanged(self):
+ logger.info("...")
+ self.curr = self['list'].getCurrent()
+ self.curr_index = self['list'].getIndex()
+ if self.curr[CHAN_CHANNEL] == _("All channels"):
+ if "queries" in self.postdata:
+ del self.postdata['queries']
+ else:
+ self.postdata['queries'] = []
+ self.postdata['queries'].append({'fields': ['channel'], 'query': self.curr[CHAN_CHANNEL].encode('utf-8')})
+ self.postdata['offset'] = 0
+
+ def pressOk(self):
+ self.close("movies")
+
+ def pressRed(self):
+ self.close("streams")
+
+ def pressGreen(self):
+ pass
+
+ def pressYellow(self):
+ self.close("movies")
+
+ def pressBlue(self):
+ self.close("search")
+
+ def pressMenu(self):
+ Menu(self.session, self.postdata)
+
+ def pressInfo(self):
+ pass
+
+ def pressClose(self):
+ logger.info("...")
+ self.close("exit")
diff --git a/src/ConfigInit.py b/src/ConfigInit.py
index 3264a77..f600513 100644
--- a/src/ConfigInit.py
+++ b/src/ConfigInit.py
@@ -19,8 +19,15 @@
# .
-from Components.config import config, ConfigSelection, ConfigSubsection
+from Components.config import config, ConfigSelection, ConfigSelectionNumber, ConfigYesNo, ConfigDirectory, ConfigSubsection
from .Debug import logger, log_levels
+from .__init__ import _
+
+from .Constants import LIST_URL_VIDEO_LOW, LIST_URL_VIDEO, LIST_URL_VIDEO_HD
+
+
+VIDEO_RESOLUTIONS = [LIST_URL_VIDEO_HD, LIST_URL_VIDEO, LIST_URL_VIDEO_LOW]
+VIDEO_RESOLUTIONS_DICT = {LIST_URL_VIDEO_HD: _("High"), LIST_URL_VIDEO: _("Medium"), LIST_URL_VIDEO_LOW: _("Low")}
class ConfigInit():
@@ -29,3 +36,8 @@ def __init__(self):
logger.info("...")
config.plugins.mediathekcockpit = ConfigSubsection()
config.plugins.mediathekcockpit.debug_log_level = ConfigSelection(default="DEBUG", choices=list(log_levels.keys()))
+ config.plugins.mediathekcockpit.size = ConfigSelectionNumber(min=50, max=1500, stepwidth=50, default=500)
+ config.plugins.mediathekcockpit.future = ConfigYesNo(default=False)
+ config.plugins.mediathekcockpit.askstopmovie = ConfigSelection(default='quit', choices=[('quit', _('Do nothing')), ('ask', _('Ask user'))])
+ config.plugins.mediathekcockpit.movie_resolution = ConfigSelection(default=str(LIST_URL_VIDEO_HD), choices=[(str(LIST_URL_VIDEO_LOW), _('Low')), (str(LIST_URL_VIDEO), _('Medium')), (str(LIST_URL_VIDEO_HD), _('High'))])
+ config.plugins.mediathekcockpit.moviesavedir = ConfigDirectory(default='/media/hdd/movie/')
diff --git a/src/ConfigUtils.py b/src/ConfigUtils.py
deleted file mode 100644
index 2d5c64b..0000000
--- a/src/ConfigUtils.py
+++ /dev/null
@@ -1,71 +0,0 @@
-import os
-import json
-from .Debug import logger
-from .Constants import enigma2configdir, listfields
-
-
-configfile = enigma2configdir + 'mv_config.json'
-
-
-def read_mvconfig():
- logger.info("...")
- mvconfig = {}
- mvconfig['offset'] = 0
- mvconfig['size'] = 500
- mvconfig['future'] = False
- mvconfig['askstopmovie'] = 'quit'
- mvconfig['channels'] = ['ARD', 'ZDF', 'NDR', 'BR', 'SR', 'SWR', 'SRF', '3Sat', 'HR', 'WDR', 'MDR', 'RBB', 'ARTE.DE', 'ARTE.FR', 'PHOENIX', 'ORF', 'KiKA', 'DW', 'ZDF-tivi', 'SRF.Podcast']
- mvconfig['channelslist'] = []
- mvconfig['livechannels'] = []
- mvconfig['favorites'] = []
- mvconfig['searchquerys'] = []
- mvconfig['searchquerysfields'] = listfields['topic']
- mvconfig['searchstart'] = 'easyselection'
- mvconfig['playmovieselect'] = 'url_video_hd'
- mvconfig['moviesavedir'] = '/media/hdd/movie/'
- mvconfig['postdata'] = {'sortOrder': 'desc', 'sortBy': 'timestamp'}
- if os.path.exists(configfile):
- with open(configfile) as (data_file):
- data = json.load(data_file)
- mvconfig['size'] = data.get('size', 500)
- mvconfig['future'] = data.get('future', False)
- mvconfig['askstopmovie'] = data.get('askstopmovie', 'quit')
- mvconfig['searchquerysfields'] = data.get('searchquerysfields', listfields['topic'])
- mvconfig['searchstart'] = data.get('searchstart', 'easyselection')
- mvconfig['playmovieselect'] = data.get('playmovieselect', 'url_video_hd')
- mvconfig['moviesavedir'] = data.get('moviesavedir', '/media/hdd/movie/')
- if data.get('duration_min', 0) > 0:
- mvconfig['duration_min'] = data['duration_min']
- mvconfig['postdata']['duration_min'] = mvconfig['duration_min'] * 60
- if data.get('duration_max', 0) > 0:
- mvconfig['duration_max'] = data['duration_max']
- mvconfig['postdata']['duration_max'] = mvconfig['duration_max'] * 60
- mvconfig['postdata']['size'] = mvconfig['size']
- mvconfig['postdata']['offset'] = 0
- mvconfig['postdata']['future'] = mvconfig['future']
- return mvconfig
-
-
-def write_mvconfig(mvconfig):
- logger.info("...")
- tmp = {}
- if mvconfig['size'] != 500:
- tmp['size'] = mvconfig['size']
- if mvconfig['future']:
- tmp['future'] = mvconfig['future']
- if mvconfig['askstopmovie'] != 'quit':
- tmp['askstopmovie'] = mvconfig['askstopmovie']
- if mvconfig['searchquerysfields'] != listfields['topic']:
- tmp['searchquerysfields'] = mvconfig['searchquerysfields']
- if mvconfig['searchstart'] != 'easyselection':
- tmp['searchstart'] = mvconfig['searchstart']
- if mvconfig['playmovieselect'] != 'url_video_hd':
- tmp['playmovieselect'] = mvconfig['playmovieselect']
- if mvconfig['moviesavedir'] != '/media/hdd/movie/':
- tmp['moviesavedir'] = mvconfig['moviesavedir']
- if mvconfig.get('duration_min', 0) > 0:
- tmp['duration_min'] = mvconfig['duration_min']
- if mvconfig.get('duration_max', 0) > 0:
- tmp['duration_max'] = mvconfig['duration_max']
- with open(configfile, 'w') as (fp):
- json.dump(tmp, fp, indent=2, sort_keys=True)
diff --git a/src/Constants.py b/src/Constants.py
index d5530dd..3739975 100644
--- a/src/Constants.py
+++ b/src/Constants.py
@@ -1,59 +1,62 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
plugindir = "/usr/lib/enigma2/python/Plugins/Extensions/MediathekCockpit/"
-enigma2configdir = "/etc/enigma2/"
-listindex = {}
-listindex['channel'] = 0
-listindex['topic'] = 1
-listindex['title'] = 2
-listindex['description'] = 3
-listindex['time'] = 4
-listindex['date'] = 5
-listindex['duration'] = 6
-listindex['size'] = 7
-listindex['channelpixmap'] = 8
-listindex['timetext'] = 9
-listindex['id'] = 10
-listindex['url_video_low'] = 11
-listindex['url_video'] = 12
-listindex['url_video_hd'] = 13
-listindex['url_website'] = 14
-listindex['listend'] = 15
-listempty = [''] * listindex['listend']
-listempty[listindex['size']] = 0
-listempty[listindex['channelpixmap']] = None
-listsearchquerys = {}
-listsearchquerys['name'] = 0
-listsearchquerys['fields'] = 1
-listsearchquerys['channel'] = 2
-listsearchquerys['queries'] = 3
-listsearchquerys['listend'] = 4
-listchannel = {}
-listchannel['channel'] = 0
-listchannel['dummy'] = 1
-listchannel['channelpixmap'] = 2
-listchannel['listend'] = 3
-liveindex = {}
-liveindex['name'] = 0
-liveindex['for_lcd'] = 1
-liveindex['channelpixmap'] = 2
-liveindex['master_url'] = 3
-liveindex['index_url'] = 4
-liveindex['audio_url'] = 5
-liveindex['service'] = 6
-liveindex['options'] = 7
-liveindex['eservice'] = 8
-liveindex['default'] = 9
-liveindex['logo_name'] = 10
-liveindex['listend'] = 11
-liveempty = [''] * liveindex['listend']
-liveempty[liveindex['channelpixmap']] = None
-listfields = {}
-listfields['channel'] = ['channel']
-listfields['topic'] = ['topic']
-listfields['title'] = ['title']
-listfields['description'] = ['description']
-listfields['topictitle'] = ['topic', 'title']
-listfields['topictitledescription'] = ['topic', 'title', 'description']
-mainindex = {}
-mainindex['livestream'] = 2
-mainindex['moviliste'] = listindex['listend']
+LIST_CHANNEL = 0
+LIST_TOPIC = 1
+LIST_TITLE = 2
+LIST_DESCRIPTION = 3
+LIST_TIME = 4
+LIST_DATE = 5
+LIST_DURATION = 6
+LIST_SIZE = 7
+LIST_CHANNELPIXMAP = 8
+LIST_ID = 9
+LIST_URL_VIDEO_LOW = 10
+LIST_URL_VIDEO = 11
+LIST_URL_VIDEO_HD = 12
+LIST_URL_WEBSITE = 13
+LIST_END = 14
+
+listempty = [''] * LIST_END
+listempty[LIST_SIZE] = 0
+listempty[LIST_CHANNELPIXMAP] = None
+
+CHAN_CHANNEL = 0
+CHAN_CHANNELPIXMAP = 1
+CHAN_END = 2
+
+LIVE_NAME = 0
+LIVE_FOR_LCD = 1
+LIVE_CHANNELPIXMAP = 2
+LIVE_MASTER_URL = 3
+LIVE_INDEX_URL = 4
+LIVE_AUDIO_URL = 5
+LIVE_SERVICE = 6
+LIVE_OPTIONS = 7
+LIVE_ESERVICE = 8
+LIVE_DEFAULT = 9
+LIVE_LOGO_NAME = 10
+LIVE_END = 11
+
+liveempty = [''] * LIVE_END
+liveempty[LIVE_CHANNELPIXMAP] = None
diff --git a/src/MediathekCockpitDirBrowser.py b/src/DirBrowser.py
similarity index 62%
rename from src/MediathekCockpitDirBrowser.py
rename to src/DirBrowser.py
index b3b9e99..728fedd 100644
--- a/src/MediathekCockpitDirBrowser.py
+++ b/src/DirBrowser.py
@@ -1,3 +1,24 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
from Screens.Screen import Screen
from Screens.SimpleSummary import SimpleSummary
from Components.ActionMap import ActionMap
@@ -7,11 +28,11 @@
from .Debug import logger
-class MediathekCockpitDirBrowser(Screen):
+class DirBrowser(Screen):
def __init__(self, session, directory=''):
logger.info("...")
- self.skinName = 'MediathekCockpitDirBrowser'
+ self.skinName = 'DirBrowser'
Screen.__init__(self, session)
if directory == '':
directory = '/'
diff --git a/src/Downloader.py b/src/Downloader.py
index e47b9ef..bb5d747 100644
--- a/src/Downloader.py
+++ b/src/Downloader.py
@@ -1,3 +1,24 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
from cookielib import CookieJar
from twisted.web.http_headers import Headers
from twisted.web.client import Agent, RedirectAgent, _GzipProtocol, CookieAgent, PartialDownloadError, BrowserLikePolicyForHTTPS, HTTPConnectionPool, _requireSSL
@@ -61,9 +82,9 @@ def _identityVerifyingInfoCallback(self, connection, where, _ret):
try:
verifyHostname(connection, self._hostnameASCII)
except (CertificateError, VerificationError) as e:
- print('Remote certificate is not valid for hostname "%s"; %s' % (self._hostnameASCII, e))
+ logger.error('Remote certificate is not valid for hostname "%s"; %s', self._hostnameASCII, e)
except ValueError as e:
- print('Ignoring error while verifying certificate from host "%s" (exception: %s)' % (self._hostnameASCII, repr(e)))
+ logger.error('Ignoring error while verifying certificate from host "%s" (exception: %s)', self._hostnameASCII, repr(e))
try:
@@ -104,8 +125,7 @@ def getContext(self, hostname=None, port=None):
@_requireSSL
def creatorForNetloc(self, hostname, port):
- logger.info("...")
- print('creatorForNetloc', hostname)
+ logger.info("hostname: %s", hostname)
return MyClientTLSOptions(hostname.decode('ascii'), self.getContext(hostname, port))
@@ -130,7 +150,6 @@ def stop(self):
def addProgress(self, progress_callback):
logger.info("...")
- print('[addProgress]')
self.factory.progress_callback = progress_callback
@@ -139,8 +158,12 @@ def MydownloadPage(url, file=None, method='GET', headers=_headers, postdata=None
return getPagenew(url, file, method, headers, postdata, contextFactory, *args, **kwargs).deferred
-def MygetPage(url, fileOrName=None, method='GET', headers=_headers, postdata=None, contextFactory=ClientContextFactory(), *args, **kwargs):
+def MyGetPage(url, fileOrName=None, method='GET', headers=_headers, postdata=None, contextFactory=ClientContextFactory(), *args, **kwargs):
logger.info("...")
+ logger.debug(
+ "url: %s, fileOrName: %s, method: %s, headers: %s, postdata: %s",
+ url, fileOrName, method, headers, postdata
+ )
return getPagenew(url, fileOrName, method, headers, postdata, contextFactory, *args, **kwargs).deferred
@@ -248,7 +271,7 @@ def connectionLost(self, reason=None):
self.finished.callback(None)
elif reason.check(PotentialDataLoss):
self.finished.errback(Failure())
- print('PotentialDataLoss')
+ logger.error('PotentialDataLoss')
else:
self.finished.errback(reason)
@@ -282,7 +305,7 @@ def _cancel(_):
try:
response._transport._producer.abortConnection()
except Exception as e:
- print('####################################', e)
+ logger.error('exception: %s', e)
finished = Deferred(canceller=_cancel)
if 'CallbackResponse' in kwargs:
@@ -313,9 +336,9 @@ def http_failed(failure_instance=None, *args, **kwargs):
if not error_message:
error_message = str(failure_instance)
text = kwargs.get('title', 'Error') + '\n'
- text += _('Error during download.') + '\n\n' + error_message + '\n'
- logger.debug(text, failure_instance, args, kwargs)
- logger.debug(failure_instance.printDetailedTraceback())
+ text += _('Download failed') + '\n\n' + error_message + '\n'
+ logger.debug("%s, %s, %s, %s", text, failure_instance, args, kwargs)
+ logger.error("traceback: %s", failure_instance.printDetailedTraceback())
try:
if mysession:
mysession.toastManager.showToast(text, int(kwargs.get('duration', 20)))
diff --git a/src/MediathekCockpitDownloadView.py b/src/Downloads.py
old mode 100644
new mode 100755
similarity index 61%
rename from src/MediathekCockpitDownloadView.py
rename to src/Downloads.py
index 8d51ce0..26567f0
--- a/src/MediathekCockpitDownloadView.py
+++ b/src/Downloads.py
@@ -1,3 +1,24 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
from Screens.Screen import Screen
from Components.ActionMap import ActionMap
from Components.Sources.StaticText import StaticText
@@ -5,32 +26,33 @@
from MyJobManager import downloadManager
from enigma import eTimer
from .__init__ import _
+from .FileUtils import deleteFile
from .Debug import logger
-class MediathekCockpitDownloadView(Screen):
+class Downloads(Screen):
IS_DIALOG = True
def __init__(self, session):
logger.info("...")
Screen.__init__(self, session)
- self.skinName = 'MediathekCockpitDownloadView'
+ self.skinName = 'Downloads'
self['actions'] = ActionMap(
- ['MVD_Actions'],
+ ['MTC_Actions'],
{
'ok': self.close,
'cancel': self.close,
- 'red': self.currabort,
+ 'red': self.key_red,
'green': self.key_green,
'yellow': self.key_yellow,
'blue': self._createSetup
}
)
- self['key_red'] = StaticText(_('Current') + ' ' + _('Abort'))
- self['key_green'] = StaticText(_('All') + ' ' + _('Abort'))
- self['key_yellow'] = StaticText(_('All open') + ' ' + _('Abort'))
+ self['key_red'] = StaticText(_('Abort current download'))
+ self['key_green'] = StaticText(_('Abort all downloads'))
+ self['key_yellow'] = StaticText(_('Abort all pending downloads'))
self['key_blue'] = StaticText('')
- self['liste'] = List([])
+ self['list'] = List([])
self.currindex = 0
self.__dlRefreshTimer = eTimer()
self.__dlRefreshTimer_conn = self.__dlRefreshTimer.timeout.connect(self._createSetup)
@@ -39,7 +61,7 @@ def __init__(self, session):
def _createSetup(self):
logger.info("...")
self.__dlRefreshTimer.stop()
- self.currindex = self['liste'].index
+ self.currindex = self['list'].index
result = []
for job in downloadManager.getPendingJobs():
if job.status == job.IN_PROGRESS:
@@ -51,8 +73,8 @@ def _createSetup(self):
else:
result.append([job.name, job.getStatustext(), -1, '', job])
- self['liste'].setList(result)
- self['liste'].index = self.currindex
+ self['list'].setList(result)
+ self['list'].index = self.currindex
if len(result) > 0 and not self.__dlRefreshTimer.isActive():
self.__dlRefreshTimer.startLongTimer(2)
@@ -61,7 +83,7 @@ def key_yellow(self):
self.__dlRefreshTimer.stop()
downloadManager.active_jobs = []
for job in downloadManager.active_jobs:
- print(job.name)
+ logger.debug("job.name: %s", job.name)
job.abort()
self._createSetup()
@@ -75,14 +97,16 @@ def key_green(self):
currjob.cancel()
self._createSetup()
- def currabort(self):
+ def key_red(self):
logger.info("...")
- curr = self['liste'].getCurrent()
+ curr = self['list'].getCurrent()
if curr and len(curr) == 5:
self.__dlRefreshTimer.stop()
currjob = curr[4]
+ logger.debug("currjob: %s", currjob.file_name)
if currjob.status == currjob.NOT_STARTED:
downloadManager.active_jobs.remove(currjob)
elif currjob.status == currjob.IN_PROGRESS:
currjob.cancel()
+ deleteFile(currjob.file_name)
self._createSetup()
diff --git a/src/EventView.py b/src/EventView.py
new file mode 100644
index 0000000..e0e735b
--- /dev/null
+++ b/src/EventView.py
@@ -0,0 +1,54 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Screens.Screen import Screen
+from Components.ActionMap import ActionMap
+from Components.Label import Label
+from Components.ScrollLabel import ScrollLabel
+from Components.Sources.StaticText import StaticText
+from .Debug import logger
+from .__init__ import _
+from .Constants import LIST_TOPIC, LIST_TITLE, LIST_DESCRIPTION, LIST_DATE, LIST_DURATION, LIST_CHANNEL, LIST_TIME
+
+
+class EventView(Screen):
+
+ def __init__(self, session, curr):
+ logger.info("...")
+ Screen.__init__(self, session, windowTitle=curr[LIST_TOPIC])
+ self.skinName = 'MTCEventView'
+ self['actions'] = ActionMap(
+ ['MTC_Actions', 'OkCancelActions', 'ChannelSelectEPGActions'],
+ {
+ 'red': self.close,
+ 'ok': self.close,
+ 'cancel': self.close,
+ 'showEPGList': self.close
+ }
+ )
+ self['key_red'] = StaticText(_("Abort"))
+ self['key_green'] = StaticText()
+ self['key_yellow'] = StaticText()
+ self['key_blue'] = StaticText()
+ self['epg_description'] = ScrollLabel(curr[LIST_TITLE] + '\n\n\n' + curr[LIST_DESCRIPTION])
+ self['datetime'] = Label(curr[LIST_DATE] + ' ' + curr[LIST_TIME])
+ self['duration'] = Label(curr[LIST_DURATION])
+ self['channel'] = Label(curr[LIST_CHANNEL])
diff --git a/src/FavoriteUtils.py b/src/FavoriteUtils.py
deleted file mode 100644
index b52ad82..0000000
--- a/src/FavoriteUtils.py
+++ /dev/null
@@ -1,91 +0,0 @@
-import os
-import json
-from .Debug import logger
-from .Constants import plugindir, enigma2configdir, listindex, listempty
-from .LoadPixmap import Load_My_Pixmap
-from .__init__ import _
-
-
-favoritesfile = enigma2configdir + 'mv_favorites.json'
-
-
-def convert_favorites(value, tmplist):
- logger.info("...")
- tmp = listempty[:]
- if 'channel' in value:
- name = value['channel'].encode('utf-8')
- ptr = Load_My_Pixmap('%slogos/%s.png' % (plugindir, name.upper()))
- tmp[listindex['channel']] = name
- tmp[listindex['channelpixmap']] = ptr
- tmp[listindex['timetext']] = _('Clock')
- if 'topic' in value:
- tmp[listindex['topic']] = value['topic'].encode('utf-8')
- if 'topic' in value:
- tmp[listindex['topic']] = value['topic'].encode('utf-8')
- if 'title' in value:
- tmp[listindex['title']] = value['title'].encode('utf-8')
- if 'description' in value:
- tmp[listindex['description']] = value['description'].encode('utf-8')
- if 'time' in value:
- tmp[listindex['time']] = value['time'].encode('utf-8')
- if 'date' in value:
- tmp[listindex['date']] = value['date'].encode('utf-8')
- if 'duration' in value:
- tmp[listindex['duration']] = value['duration'].encode('utf-8')
- if 'size' in value:
- tmp[listindex['size']] = value['size']
- if 'id' in value:
- tmplist['favodict'][value['id']] = True
- tmp[listindex['id']] = value['id']
- if 'url_video_low' in value:
- tmp[listindex['url_video_low']] = value['url_video_low']
- if 'url_video' in value:
- tmp[listindex['url_video']] = value['url_video']
- if 'url_video_hd' in value:
- tmp[listindex['url_video_hd']] = value['url_video_hd']
- if 'url_website' in value:
- tmp[listindex['url_website']] = value['url_website']
- return tmp, tmplist
-
-
-def read_favorites(tmplist):
- logger.info("...")
- favorites = []
- changed = False
- if os.path.exists(favoritesfile):
- with open(favoritesfile) as (data_file):
- data = json.load(data_file)
- for value in data:
- if isinstance(value, list):
- changed = True
- tmp = {}
- for xres in value:
- tmp.update(xres)
-
- value = tmp
- favorites.append(convert_favorites(value, tmplist))
- return favorites, tmplist, changed
-
-
-def write_favorites(favorites):
- logger.info("...")
- with open(favoritesfile, 'w') as (fp):
- result = []
- for value in favorites:
- tmp = {}
- tmp['channel'] = value[listindex['channel']]
- tmp['topic'] = value[listindex['topic']]
- tmp['title'] = value[listindex['title']]
- tmp['description'] = value[listindex['description']]
- tmp['time'] = value[listindex['time']]
- tmp['date'] = value[listindex['date']]
- tmp['duration'] = value[listindex['duration']]
- tmp['size'] = value[listindex['size']]
- tmp['id'] = value[listindex['id']]
- tmp['url_video_low'] = value[listindex['url_video_low']]
- tmp['url_video'] = value[listindex['url_video']]
- tmp['url_video_hd'] = value[listindex['url_video_hd']]
- tmp['url_website'] = value[listindex['url_website']]
- result.append(tmp)
-
- json.dump(result, fp, encoding='utf-8', indent=2, sort_keys=True)
diff --git a/src/Live.py b/src/Live.py
new file mode 100644
index 0000000..e36b312
--- /dev/null
+++ b/src/Live.py
@@ -0,0 +1,216 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from uuid import uuid4
+from enigma import eServiceReference
+from Screens.Screen import Screen
+from Screens.MessageBox import MessageBox
+from Components.ActionMap import ActionMap
+from Components.Label import Label
+from Components.Sources.StaticText import StaticText
+from Components.Sources.List import List
+from .Debug import logger
+from .__init__ import _
+from .Version import PLUGIN
+from .LiveUtils import readLiveChannels, writeLiveChannels
+from .MoviePlayer import StreamPlayer
+from .M3U8Parser import M3U8Parser
+from .Downloader import MyGetPage, _headers
+from .MyDeferredSemaphore import MyDeferredSemaphore
+from .Menu import Menu
+from .Constants import LIVE_MASTER_URL, LIVE_INDEX_URL, LIVE_AUDIO_URL, LIVE_DEFAULT, LIVE_OPTIONS, LIVE_SERVICE, LIVE_NAME, LIVE_ESERVICE
+
+
+class Live(Screen):
+ sessionid = str(uuid4())
+ plutosessionid = str(uuid4())
+
+ def __init__(self, session, postdata):
+ self.postdata = postdata
+ Screen.__init__(self, session)
+ self.skinName = 'MediathekCockpit'
+ self.title = PLUGIN + ' - ' + _('Live')
+
+ self['key_red'] = StaticText()
+ self['key_green'] = StaticText(_('Channels'))
+ self['key_yellow'] = StaticText(_('Movies'))
+ self['key_blue'] = StaticText(_('Search'))
+
+ self['description'] = Label('')
+ self['date'] = Label('')
+ self['sresult'] = Label('')
+
+ self['list'] = List()
+
+ self.changed = False
+
+ self['actions'] = ActionMap(
+ ['MTC_Actions'],
+ {
+ 'ok': self.pressOk,
+ 'cancel': self.pressClose,
+ # 'red': self.pressRed,
+ 'green': self.pressGreen,
+ 'yellow': self.pressYellow,
+ 'blue': self.pressBlue,
+ 'menu': self.pressMenu,
+ # 'info': self.pressInfo
+ }
+ )
+ self.download = MyDeferredSemaphore(tokens=1)
+ self['list'].onSelectionChanged.append(self.__SelectionChanged)
+ self.onLayoutFinish.append(self.__onLayoutFinish)
+
+ def __onLayoutFinish(self):
+ logger.info("...")
+ self.loadLiveChannels()
+
+ def __SelectionChanged(self):
+ logger.info("...")
+ self['description'].setText('')
+ self['date'].setText('')
+
+ self.curr = self['list'].getCurrent()
+ if self.curr:
+ txt = ('master_url\n{0}').format(self.curr[LIVE_MASTER_URL])
+ if self.curr[LIVE_INDEX_URL]:
+ txt += ('\n\nindex_url\n{0}').format(self.curr[LIVE_INDEX_URL])
+ if self.curr[LIVE_AUDIO_URL]:
+ txt += ('\n\naudio_url\n{0}').format(self.curr[LIVE_AUDIO_URL])
+ dtxt = ('{0} ({1})\n{2} ({3})').format(_('Default'), self.curr[LIVE_DEFAULT], _('options'), (', ').join(self.curr[LIVE_OPTIONS]))
+ self['date'].setText(dtxt)
+ self['description'].setText(txt)
+
+ def loadLiveChannels(self):
+ logger.info("...")
+ self['list'].style = 'default'
+ self.list = readLiveChannels()
+ self['list'].setList(self.list)
+
+ def playlive(self, url, suburi=None):
+ logger.info("...")
+ mservice = str(self.curr[LIVE_SERVICE])
+ if mservice and mservice.count(':') == 10:
+ sref = eServiceReference(mservice)
+ sref.setPath(url.encode('utf-8'))
+ else:
+ sref = eServiceReference(eServiceReference.idGST, 256, url.encode('utf-8'))
+ sref.setName(self.curr[LIVE_NAME].encode('utf-8'))
+ if suburi:
+ sref.setSuburi(suburi.encode('utf-8'))
+ currindex = self["list"].getIndex()
+ if self.curr[LIVE_DEFAULT] == 'verifyandupdate':
+ if 'playindex' in self.curr[LIVE_OPTIONS]:
+ default = 'playindex'
+ elif 'playmaster' in self.curr[LIVE_OPTIONS]:
+ default = 'playmaster'
+ else:
+ default = 'verify'
+ self.curr[LIVE_ESERVICE] = sref
+ self.curr[LIVE_INDEX_URL] = url
+ self.curr[LIVE_AUDIO_URL] = suburi or ''
+ self.curr[LIVE_DEFAULT] = default
+ self["list"].modifyEntry(currindex, self.curr)
+ self.changed = True
+ self.__SelectionChanged()
+ elif 'cached' in self.curr[LIVE_OPTIONS]:
+ self.curr[LIVE_ESERVICE] = sref
+ self["list"].modifyEntry(currindex, self.curr)
+ self.session.open(StreamPlayer, service=sref)
+
+ def downloadFailed(self, failure, *args):
+ logger.info("...")
+ error_message = str(failure.getErrorMessage())
+ if not error_message:
+ error_message = str(failure)
+ logger.error("%s, %s", error_message, str(args))
+ if 'CancelledError' not in error_message:
+ self.session.open(MessageBox, error_message, MessageBox.TYPE_INFO, timeout=10, title=_('Download failed'))
+
+ def pressOk(self):
+ logger.info("...")
+ if self.curr and self.curr[LIVE_MASTER_URL]:
+ if 'dash' in self.curr[LIVE_OPTIONS]:
+ mservice = str(self.curr[LIVE_SERVICE])
+ sref = eServiceReference(mservice)
+ sref.setPath(str(self.curr[LIVE_MASTER_URL]))
+ sref.setName('%s' % self.curr[0].encode('utf-8'))
+ self.session.open(StreamPlayer, sref)
+ elif 'm3u8' in self.curr[LIVE_OPTIONS]:
+ if self.curr[LIVE_DEFAULT] in ('verify', 'verifyandupdate', 'cached'):
+ if 'cached' in self.curr[LIVE_OPTIONS] and isinstance(self.curr[LIVE_ESERVICE], eServiceReference):
+ self.session.open(StreamPlayer, service=self.curr[LIVE_ESERVICE])
+ else:
+ urlval = self.curr[LIVE_MASTER_URL].encode('utf-8')
+ if 'pluto' in self.curr[LIVE_OPTIONS] and 'deviceId' not in self.curr[LIVE_MASTER_URL]:
+ urlval = ('{0}&sid={1}&deviceId={2}').format(self.curr[LIVE_MASTER_URL], self.plutosessionid, self.sessionid)
+ self.download.start(MyGetPage, url=urlval, headers=_headers, location=True).addCallback(self.m3u8checkback, self.playlive).addErrback(self.downloadFailed)
+ elif self.curr[LIVE_DEFAULT] in ('playindex', 'playmaster', 'cached') and self.curr[LIVE_MASTER_URL]:
+ if 'cached' in self.curr[LIVE_OPTIONS] and isinstance(self.curr[LIVE_ESERVICE], eServiceReference):
+ self.session.open(StreamPlayer, service=self.curr[LIVE_ESERVICE])
+ elif self.curr[LIVE_DEFAULT] == 'playindex' and not self.curr[LIVE_INDEX_URL] and self.curr[LIVE_MASTER_URL]:
+ currindex = self['list'].getIndex()
+ self.curr[LIVE_DEFAULT] = 'verify'
+ urlval = self.curr[LIVE_MASTER_URL].encode('utf-8')
+ self['list'].modifyEntry(currindex, self.curr)
+ self.changed = True
+ self.__SelectionChanged()
+ self.download.start(MyGetPage, url=urlval, headers=_headers, location=True).addCallback(self.m3u8checkback, self.playlive).addErrback(self.downloadFailed)
+ elif self.curr[LIVE_DEFAULT] == 'playmaster' and self.curr[LIVE_MASTER_URL]:
+ urlval = self.curr[LIVE_MASTER_URL].encode('utf-8')
+ self.playlive(urlval)
+
+ def m3u8checkback(self, result, play):
+ logger.info("...")
+ m3u8 = result[0]
+ url = result[1]
+ if '#EXTM3U' in m3u8:
+ m3u8res = M3U8Parser(m3u8, url)
+ if m3u8res.extxmedia_play():
+ play(url)
+ elif m3u8res.extxmedia_parse():
+ urlval, suburi = m3u8res.parser_all()
+ if urlval:
+ play(urlval, suburi)
+
+ def pressRed(self):
+ pass
+
+ def pressGreen(self):
+ self.close("channels")
+
+ def pressYellow(self):
+ self.close("movies")
+
+ def pressBlue(self):
+ self.close("search")
+
+ def pressMenu(self):
+ Menu(self.session, self.postdata)
+
+ def pressInfo(self):
+ pass
+
+ def pressClose(self):
+ logger.info("...")
+ if self.changed:
+ writeLiveChannels(self['list'].list)
+ self.close("exit")
diff --git a/src/LiveChannelUtils.py b/src/LiveChannelUtils.py
deleted file mode 100644
index 6596bda..0000000
--- a/src/LiveChannelUtils.py
+++ /dev/null
@@ -1,88 +0,0 @@
-import os
-import json
-from enigma import eServiceReference
-from .Debug import logger
-from .__init__ import _
-from .LoadPixmap import Load_My_Pixmap
-from .Constants import plugindir, liveindex, liveempty
-
-
-livechannelsfile = plugindir + 'livechannels.json'
-
-
-def read_livechannels():
- logger.info("...")
- livechannels = []
- if os.path.exists(livechannelsfile):
- with open(livechannelsfile) as (data_file):
- data = json.load(data_file)
- for value in data:
- tmp = liveempty[:]
- tmp[liveindex['for_lcd']] = _('Live channels')
- if 'name' in value:
- tmp[liveindex['name']] = value['name'].encode('utf-8')
- if 'logo_name' in value:
- tmp[liveindex['logo_name']] = value['logo_name']
- tmp[liveindex['channelpixmap']] = Load_My_Pixmap('%slogos/%s.png' % (plugindir, value['logo_name'].upper().encode('utf-8')))
- if 'master_url' in value:
- tmp[liveindex['master_url']] = value['master_url'].encode('utf-8')
- if 'index_url' in value:
- tmp[liveindex['index_url']] = value['index_url'].encode('utf-8')
- if 'audio_url' in value:
- tmp[liveindex['audio_url']] = value['audio_url'].encode('utf-8')
- if 'service' in value:
- tmp[liveindex['service']] = value['service'].encode('utf-8')
- if 'options' in value:
- tmp[liveindex['options']] = value['options']
- if 'cached' in value['options']:
- ref = convert_service(value)
- if ref:
- tmp[liveindex['eservice']] = ref
- if 'default' in value:
- tmp[liveindex['default']] = value['default']
- livechannels.append(tmp)
- return livechannels
-
-
-def write_livechannels(livechannels):
- logger.info("...")
- with open(livechannelsfile, 'w') as (fp):
- result = []
- for value in livechannels:
- tmp = {}
- tmp['logo_name'] = value[liveindex['logo_name']]
- tmp['name'] = value[liveindex['name']]
- tmp['service'] = value[liveindex['service']]
- tmp['index_url'] = value[liveindex['index_url']]
- tmp['default'] = value[liveindex['default']]
- tmp['master_url'] = value[liveindex['master_url']]
- tmp['audio_url'] = value[liveindex['audio_url']]
- tmp['options'] = value[liveindex['options']]
- tmp['name'] = value[liveindex['name']]
- tmp['name'] = value[liveindex['name']]
- tmp['name'] = value[liveindex['name']]
- result.append(tmp)
-
- json.dump(result, fp, encoding='utf-8', indent=2, sort_keys=False)
-
-
-def convert_service(value):
- logger.info("...")
- if ('cached' in value['options'] and value['master_url'] or value['index_url']) and 'service' in value:
- if 'playmaster' in value['options'] and not value['index_url']:
- murl = value['master_url'].encode('utf-8')
- else:
- if value['index_url']:
- murl = value['index_url'].encode('utf-8')
- else:
- return None
- if value['service'] and value['service'].count(':') == 10:
- sref = eServiceReference(str(value['service']))
- sref.setPath(murl)
- else:
- sref = eServiceReference(eServiceReference.idGST, 256, murl)
- sref.setName(value['name'].encode('utf-8'))
- if 'audio_url' in value and value['audio_url']:
- sref.setSuburi(value['audio_url'].encode('utf-8'))
- return sref
- return None
diff --git a/src/LiveUtils.py b/src/LiveUtils.py
new file mode 100644
index 0000000..4c03409
--- /dev/null
+++ b/src/LiveUtils.py
@@ -0,0 +1,109 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+import os
+import json
+from enigma import eServiceReference
+from .Debug import logger
+from .__init__ import _
+from .LoadPixmap import loadPixmap
+from .Constants import plugindir, liveempty, LIVE_ESERVICE, LIVE_FOR_LCD, LIVE_NAME, LIVE_LOGO_NAME, LIVE_CHANNELPIXMAP, LIVE_MASTER_URL, LIVE_INDEX_URL, LIVE_AUDIO_URL, LIVE_SERVICE, LIVE_OPTIONS, LIVE_DEFAULT
+
+
+livechannelsfile = plugindir + 'livechannels.json'
+
+
+def readLiveChannels():
+ logger.info("...")
+ livechannels = []
+ if os.path.exists(livechannelsfile):
+ with open(livechannelsfile) as (data_file):
+ data = json.load(data_file)
+ for value in data:
+ tmp = liveempty[:]
+ tmp[LIVE_FOR_LCD] = _('Live channels')
+ if 'name' in value:
+ tmp[LIVE_NAME] = value['name'].encode('utf-8')
+ if 'logo_name' in value:
+ tmp[LIVE_LOGO_NAME] = value['logo_name']
+ tmp[LIVE_CHANNELPIXMAP] = loadPixmap('%slogos/%s.png' % (plugindir, value['logo_name'].upper().encode('utf-8')))
+ if 'master_url' in value:
+ tmp[LIVE_MASTER_URL] = value['master_url'].encode('utf-8')
+ if 'index_url' in value:
+ tmp[LIVE_INDEX_URL] = value['index_url'].encode('utf-8')
+ if 'audio_url' in value:
+ tmp[LIVE_AUDIO_URL] = value['audio_url'].encode('utf-8')
+ if 'service' in value:
+ tmp[LIVE_SERVICE] = value['service'].encode('utf-8')
+ if 'options' in value:
+ tmp[LIVE_OPTIONS] = value['options']
+ if 'cached' in value['options']:
+ ref = convertService(value)
+ if ref:
+ tmp[LIVE_ESERVICE] = ref
+ if 'default' in value:
+ tmp[LIVE_DEFAULT] = value['default']
+ livechannels.append(tmp)
+ return livechannels
+
+
+def writeLiveChannels(livechannels):
+ logger.info("...")
+ with open(livechannelsfile, 'w') as (fp):
+ result = []
+ for value in livechannels:
+ tmp = {}
+ tmp['logo_name'] = value[LIVE_LOGO_NAME]
+ tmp['name'] = value[LIVE_NAME]
+ tmp['service'] = value[LIVE_SERVICE]
+ tmp['index_url'] = value[LIVE_INDEX_URL]
+ tmp['default'] = value[LIVE_DEFAULT]
+ tmp['master_url'] = value[LIVE_MASTER_URL]
+ tmp['audio_url'] = value[LIVE_AUDIO_URL]
+ tmp['options'] = value[LIVE_OPTIONS]
+ tmp['name'] = value[LIVE_NAME]
+ tmp['name'] = value[LIVE_NAME]
+ tmp['name'] = value[LIVE_NAME]
+ result.append(tmp)
+
+ json.dump(result, fp, encoding='utf-8', indent=2, sort_keys=False)
+
+
+def convertService(value):
+ logger.info("...")
+ if ('cached' in value['options'] and value['master_url'] or value['index_url']) and 'service' in value:
+ if 'playmaster' in value['options'] and not value['index_url']:
+ murl = value['master_url'].encode('utf-8')
+ else:
+ if value['index_url']:
+ murl = value['index_url'].encode('utf-8')
+ else:
+ return None
+ if value['service'] and value['service'].count(':') == 10:
+ sref = eServiceReference(str(value['service']))
+ sref.setPath(murl)
+ else:
+ sref = eServiceReference(eServiceReference.idGST, 256, murl)
+ sref.setName(value['name'].encode('utf-8'))
+ if 'audio_url' in value and value['audio_url']:
+ sref.setSuburi(value['audio_url'].encode('utf-8'))
+ return sref
+ return None
diff --git a/src/LoadPixmap.py b/src/LoadPixmap.py
index 9f100c9..587f978 100644
--- a/src/LoadPixmap.py
+++ b/src/LoadPixmap.py
@@ -1,3 +1,24 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
import os
from Tools.LoadPixmap import LoadPixmap
from .Debug import logger
@@ -6,7 +27,7 @@
_pixmap_cache = {}
-def Load_My_Pixmap(path):
+def loadPixmap(path):
logger.info("...")
if path:
svgpath = path[:-3] + 'svg'
diff --git a/src/M3U8Parser.py b/src/M3U8Parser.py
index 2ce4903..3ed2c60 100644
--- a/src/M3U8Parser.py
+++ b/src/M3U8Parser.py
@@ -1,16 +1,36 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
import re
from io import BytesIO
from .Debug import logger
-m3u8parserlist = {}
-m3u8parserlist['url'] = 0
-m3u8parserlist['audio_url'] = 1
-m3u8parserlist['audio_name'] = 2
-m3u8parserlist['sort1'] = 3
-m3u8parserlist['sort2'] = 4
-m3u8parserlist['listend'] = 5
-m3u8parserval = [''] * m3u8parserlist['listend']
+PARSER_URL = 0
+PARSER_AUDIO_URL = 1
+PARSER_AUDIO_NAME = 2
+PARSER_SORT1 = 3
+PARSER_SORT2 = 4
+PARSER_END = 5
+m3u8parserval = [''] * PARSER_END
_codecs = re.compile('CODECS="(.+?)"', re.I | re.X)
_resolution = re.compile('RESOLUTION=(.+?)[,|\\n]', re.I | re.X)
_bandwidth = re.compile('BANDWIDTH=(.+?)[,|\\n]', re.I | re.X)
@@ -23,7 +43,7 @@
_language = re.compile('LANGUAGE="(.+?)"', re.I | re.X)
-class m3u8parser(object):
+class M3U8Parser(object):
def __init__(self, result=None, murl=None):
logger.info("...")
@@ -31,7 +51,7 @@ def __init__(self, result=None, murl=None):
self.murl = murl
self.vurl = None
self.aurl = None
- self.resdict = {'urlliste': [], 'audioliste': []}
+ self.resdict = {'url_list': [], 'audio_list': []}
def findvaluesplit(self, pattern, line):
logger.info("...")
@@ -56,33 +76,33 @@ def parser_all(self, result=None, murl=None):
self.result = result
if murl:
self.murl = murl
- urlliste = m3u8parserval[:]
+ url_list = m3u8parserval[:]
for line in BytesIO(self.result):
if 'EXT-X-MEDIA:TYPE=AUDIO' in line and 'URI' in line:
- self.resdict['audioliste'].append([self.findvalue(_groupid, line), self.urlfix(self.findvalue(_aurlval, line))])
+ self.resdict['audio_list'].append([self.findvalue(_groupid, line), self.urlfix(self.findvalue(_aurlval, line))])
elif 'EXT-X-STREAM-INF' in line:
- urlliste[m3u8parserlist['audio_name']] = self.findvalue(_audio, line)
- urlliste[m3u8parserlist['sort1']] = int((self.findvalue(_resolution, line) or '0').replace('x', ''))
- urlliste[m3u8parserlist['sort2']] = int(self.findvalue(_bandwidth, line) or '0')
+ url_list[PARSER_AUDIO_NAME] = self.findvalue(_audio, line)
+ url_list[PARSER_SORT1] = int((self.findvalue(_resolution, line) or '0').replace('x', ''))
+ url_list[PARSER_SORT2] = int(self.findvalue(_bandwidth, line) or '0')
elif line[0] != '#' and '.' in line:
- urlliste[m3u8parserlist['url']] = self.urlfix(line.strip())
- self.resdict['urlliste'].append(urlliste[:])
- urlliste = m3u8parserval[:]
-
- self.resdict['urlliste'].sort(key=lambda x: x[m3u8parserlist['sort1']] or x[m3u8parserlist['sort2']], reverse=True)
- for x in self.resdict['urlliste']:
- if x[m3u8parserlist['url']] and x[m3u8parserlist['url']].startswith('http'):
- self.vurl = x[m3u8parserlist['url']]
- if self.resdict['audioliste']:
- for y in self.resdict['audioliste']:
- if x[m3u8parserlist['audio_name']] == y[0]:
+ url_list[PARSER_URL] = self.urlfix(line.strip())
+ self.resdict['url_list'].append(url_list[:])
+ url_list = m3u8parserval[:]
+
+ self.resdict['url_list'].sort(key=lambda x: x[PARSER_SORT1] or x[PARSER_SORT2], reverse=True)
+ for x in self.resdict['url_list']:
+ if x[PARSER_URL] and x[PARSER_URL].startswith('http'):
+ self.vurl = x[PARSER_URL]
+ if self.resdict['audio_list']:
+ for y in self.resdict['audio_list']:
+ if x[PARSER_AUDIO_NAME] == y[0]:
self.aurl = y[1]
if self.vurl and self.aurl and self.aurl.startswith('http'):
- print('play', self.vurl, self.aurl)
+ logger.debug('vurl: %s, aurl: %s', self.vurl, self.aurl)
return (self.vurl, self.aurl)
else:
- print('play', self.vurl, self.aurl)
+ logger.debug('vurl: %s, aurl: %s', self.vurl, self.aurl)
return (self.vurl, self.aurl)
return (self.vurl, self.aurl)
@@ -102,35 +122,35 @@ def parser_all_ok(self, result=None, murl=None):
_subtitles = re.compile('SUBTITLES="(.+?)"', re.I | re.X)
_aurlval = re.compile('URI="(.+?)"', re.I | re.X)
_nameval = re.compile('NAME="(.+?)"', re.I | re.X)
- urlliste = []
+ url_list = []
for line in BytesIO(self.result):
if 'EXT-X-MEDIA:TYPE=AUDIO' in line:
audig = self.findvalue(_groupid, line)
- if audig and audig not in self.resdict['audioliste']:
- self.resdict['audioliste'][audig] = []
+ if audig and audig not in self.resdict['audio_list']:
+ self.resdict['audio_list'][audig] = []
if audig:
aurlval = self.findvalue(_aurlval, line)
if aurlval:
nameval = self.findvalue(_nameval, line)
- self.resdict['audioliste'][audig].append([self.urlfix(aurlval), nameval])
+ self.resdict['audio_list'][audig].append([self.urlfix(aurlval), nameval])
elif 'EXT-X-STREAM-INF' in line:
audi = self.findvalue(_audio, line)
audina = ''
audisp = None
- if audi and audi in self.resdict['audioliste']:
- for audispres, audinares in self.resdict['audioliste'][audi]:
+ if audi and audi in self.resdict['audio_list']:
+ for audispres, audinares in self.resdict['audio_list'][audi]:
audisp = audispres
audina = audinares
break
- urlliste = ['', audisp, self.findvaluesplit(_resolution, line) or '0', self.findvaluesplit(_bandwidth, line) or '0', audina]
- elif line[0] != '#' and urlliste:
- urlliste[0] = self.urlfix(line.strip())
- self.resdict['urlliste'].append(urlliste[:])
- urlliste = []
+ url_list = ['', audisp, self.findvaluesplit(_resolution, line) or '0', self.findvaluesplit(_bandwidth, line) or '0', audina]
+ elif line[0] != '#' and url_list:
+ url_list[0] = self.urlfix(line.strip())
+ self.resdict['url_list'].append(url_list[:])
+ url_list = []
- self.resdict['urlliste'].sort(key=lambda x: int(x[3]) or int(x[2].replace('x', '')), reverse=True)
- for x in self.resdict['urlliste']:
+ self.resdict['url_list'].sort(key=lambda x: int(x[3]) or int(x[2].replace('x', '')), reverse=True)
+ for x in self.resdict['url_list']:
if x[0] and x[0].startswith('http'):
self.vurl = x[0]
self.aurl = x[1]
@@ -159,36 +179,27 @@ def extxmedia_play(self, result=None):
logger.info("...")
if result:
self.result = result
- if '#EXT-X-MEDIA-SEQUENCE' in self.result and ('#EXT-X-MEDIA:TYPE' or '#EXT-X-STREAM-INF') not in self.result:
- return True
- return False
+ return '#EXT-X-MEDIA-SEQUENCE' in self.result and ('#EXT-X-MEDIA:TYPE' or '#EXT-X-STREAM-INF') not in self.result
def extxmedia_parse(self, result=None):
logger.info("...")
if result:
self.result = result
- if '#EXT-X-STREAM-INF' in self.result and '#EXT-X-MEDIA-SEQUENCE' not in self.result:
- return True
- return False
+ return '#EXT-X-STREAM-INF' in self.result and '#EXT-X-MEDIA-SEQUENCE' not in self.result
def extxmedia_hls(self, result=None):
logger.info("...")
if result:
self.result = result
- if '#EXT-X-MEDIA:TYPE=AUDIO' in self.result and '#EXT-X-STREAM-INF' in self.result and '#EXT-X-MEDIA-SEQUENCE' not in self.result:
- return True
- return False
+ return '#EXT-X-MEDIA:TYPE=AUDIO' in self.result and '#EXT-X-STREAM-INF' in self.result and '#EXT-X-MEDIA-SEQUENCE' not in self.result
def extxmedia_chunk(self, result=None):
logger.info("...")
if result:
self.result = result
- if '#EXT-X-STREAM-INF' in self.result and 'chunk' in self.result and 'm3u8' in self.result and '#EXT-X-MEDIA-SEQUENCE' not in self.result:
- return True
- return False
+ return '#EXT-X-STREAM-INF' in self.result and 'chunk' in self.result and 'm3u8' in self.result and '#EXT-X-MEDIA-SEQUENCE' not in self.result
def __del__(self):
logger.info("...")
- print('__del__ m3u8parser')
self.resdict.clear()
self.vurl = self.aurl = self.result = self.murl = None
diff --git a/src/MediathekCockpit.py b/src/MediathekCockpit.py
index 92eb2a4..f962521 100644
--- a/src/MediathekCockpit.py
+++ b/src/MediathekCockpit.py
@@ -1,1032 +1,70 @@
-import re
-import json
-from time import time
-from uuid import uuid4
-from datetime import datetime, timedelta
-from Screens.MessageBox import MessageBox
-from Screens.Screen import Screen
-from Screens.ChoiceBox import ChoiceBox
-from Components.ActionMap import ActionMap
-from Components.Label import Label
-from Components.Sources.StaticText import StaticText
-from Components.Sources.List import List
-from enigma import eServiceReference
-from Tools.Directories import pathExists, getRecordingFilename
-from Plugins.SystemPlugins.Toolkit.NTIVirtualKeyBoard import NTIVirtualKeyBoard
-try:
- from Plugins.Extensions.MediaInfo.plugin import MediaInfo
-except Exception:
- MediaInfo = False
-from MyJobManager import downloadManager, DownloadJob
-from MoviePlayer import StreamPlayer, MoviePlayer
-from .Downloader import MygetPage, _headers, headers_ts, headersplain
-from .MyDeferredSemaphore import MyDeferredSemaphore
-from .MediathekCockpitEventView import MediathekCockpitEventView
-from .MediathekCockpitDownloadView import MediathekCockpitDownloadView
-from .MediathekCockpitSetup import MediathekCockpitSetup
-from .MediathekCockpitSearchEntry import MediathekCockpitSearchEntry
-from .LoadPixmap import Load_My_Pixmap
-from .SearchUtils import read_searchquerys, write_searchquerys
-from .ConfigUtils import read_mvconfig, write_mvconfig
-from .FavoriteUtils import read_favorites, write_favorites
-from .LiveChannelUtils import read_livechannels, write_livechannels
-from .M3U8Parser import m3u8parser
-from .__init__ import _
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Components.config import config
from .Debug import logger
-from .Constants import plugindir, listindex, listempty, liveindex, listfields, listsearchquerys, listchannel
+from .Channels import Channels
+from .Live import Live
+from .Movies import Movies
+from .Search import Search
+from .Settings import Settings
-class MediathekCockpitSummary(Screen):
-
- def __init__(self, session, parent):
- logger.info("...")
- Screen.__init__(self, session, parent=parent)
- self.skinName = ['MediathekCockpitSummary']
- self.skinName.append('SimpleSummary')
-
-
-class MediathekCockpit(Screen):
- sessionid = str(uuid4())
- plutosessionid = str(uuid4())
-
+class MediathekCockpit(Search):
def __init__(self, session, query=""):
- logger.info("...")
- self.query = query
- Screen.__init__(self, session)
+ logger.info("query: %s", query)
self.session = session
- self.skinName = 'MediathekCockpit'
- self['actions'] = ActionMap(
- ['MVD_Actions'],
- {
- 'ok': self.key_ok,
- 'cancel': self.close,
- 'red': self.key_red,
- 'green': self.key_green,
- 'yellow': self.key_yellow,
- 'blue': self.key_blue,
- 'next': self.livestreammoveDown,
- 'back': self.livestreammoveUp,
- 'menu': self.showMenu,
- 'info': self.key_info
- }
- )
- self.changed = {'config': False, 'favo': False, 'querys': False, 'live': False}
- self.tmplist = {'favodict': {}}
- self.mvconfig = read_mvconfig()
- self['key_red'] = StaticText(_('Channel selection'))
- self['key_green'] = StaticText(_('All movies'))
- self['key_yellow'] = StaticText(_('Search'))
- self['key_blue'] = StaticText(_('Favorites'))
- self['description'] = Label('')
- self['date'] = Label('')
- self['sresult'] = Label('')
- self['message'] = Label()
- self['message'].hide()
- self['liste'] = List([])
- self['summaryliste'] = List([])
- self['liste'].onSelectionChanged.append(self.__SelectionChanged)
- self.onClose.append(self.__onClose)
- self.onFirstExecBegin.append(self.__onFirstExecBegin)
- self.currindex = 0
- self.channelscurrindex = 0
- self.searchentriesindex = 0
- self.titeltext = _('MediathekCockpit')
- self.lastservice = self.session.nav.getCurrentlyPlayingServiceReference()
- self.download = MyDeferredSemaphore(tokens=1)
-
- def __onFirstExecBegin(self):
- logger.info("...")
- self.key_yellow_text()
- summaryempty = [''] * listindex['id']
- summaryempty[listindex['channel']] = self.getTitle()
- summaryempty[listindex['topic']] = self.getTitle()
- summaryempty[listindex['channelpixmap']] = Load_My_Pixmap(('{0}plugin.png').format(plugindir))
- self['summaryliste'].setList(summaryempty)
- if self.query:
- self.manualsearch(self.query)
-
- def key_ok(self):
-
- def playm3u8(url, suburi=None):
- logger.info("...")
- sref = eServiceReference(eServiceReference.idGST, 0, url.encode('utf-8'))
- if curr[listindex['channel']].upper() not in self.name.upper():
- sref.setName('%s: %s' % (curr[listindex['channel']].encode('utf-8'), self.name.encode('utf-8')))
- else:
- sref.setName('%s' % self.name.encode('utf-8'))
- if suburi:
- sref.setSuburi(suburi.encode('utf-8'))
- if MediaInfo:
- menuval = (MediaInfo,)
- else:
- menuval = None
- self.session.open(MoviePlayer, sref, menuval=menuval, infoval=(MediathekCockpitEventView, curr), myconfig=self.mvconfig)
-
- def playlive(url, suburi=None):
- logger.info("...")
- mservice = str(curr[liveindex['service']])
- if mservice and mservice.count(':') == 10:
- sref = eServiceReference(mservice)
- sref.setPath(url.encode('utf-8'))
- else:
- sref = eServiceReference(eServiceReference.idGST, 256, url.encode('utf-8'))
- sref.setName(curr[liveindex['name']].encode('utf-8'))
- if suburi:
- sref.setSuburi(suburi.encode('utf-8'))
- currindex = self['liste'].getIndex()
- if curr[liveindex['default']] == 'verifyandupdate':
- if 'playindex' in curr[liveindex['options']]:
- default = 'playindex'
- elif 'playmaster' in curr[liveindex['options']]:
- default = 'playmaster'
- else:
- default = 'verify'
- curr[liveindex['eservice']] = sref
- curr[liveindex['index_url']] = url
- curr[liveindex['audio_url']] = suburi or ''
- curr[liveindex['default']] = default
- self['liste'].modifyEntry(currindex, curr)
- self.mvconfig['livechannels'][currindex][liveindex['index_url']] = url
- self.mvconfig['livechannels'][currindex][liveindex['audio_url']] = suburi or ''
- self.mvconfig['livechannels'][currindex][liveindex['default']] = default
- self.changed['live'] = True
- self.__SelectionChanged()
- elif 'cached' in curr[liveindex['options']]:
- curr[liveindex['eservice']] = sref
- self['liste'].modifyEntry(currindex, curr)
- self.session.open(StreamPlayer, service=sref)
-
- def m3u8checkback(result, play):
- logger.info("...")
- self['message'].hide()
- m3u8 = result[0]
- url = result[1]
- if '#EXTM3U' in m3u8:
- m3u8res = m3u8parser(m3u8, url)
- if m3u8res.extxmedia_play():
- play(url)
- elif m3u8res.extxmedia_parse():
- urlval, suburi = m3u8res.parser_all()
- if urlval:
- play(urlval, suburi)
-
- def ChoiceBoxCallback(answer):
- logger.info("...")
- if answer and answer[1]:
- self.url = answer[1].encode('utf-8')
- if curr[listindex['topic']].replace('&', 'und').upper() not in curr[listindex['title']].replace('&', 'und').upper():
- self.name = curr[listindex['topic']] + ' ' + curr[listindex['title']]
- else:
- self.name = curr[listindex['title']]
-
- if '.m3u8' in self.url:
- self['message'].text = _('Download started...')
- self['message'].show()
- self.download.start(MygetPage, url=self.url, headers=_headers, location=True).addCallback(m3u8checkback, playm3u8).addErrback(self.http_failed)
- elif self.url.endswith(('mp4', 'flv')):
- playm3u8(self.url)
-
- def checkvideourl(was):
- logger.info("...")
- if was in listindex and curr[listindex[was]] and curr[listindex[was]].startswith('http'):
- return True
- return False
-
- def getquali(was):
- logger.info("...")
- restext = ''
- pos = curr[listindex[was]].rfind('.')
- if pos != -1:
- restext = curr[listindex[was]][pos + 1:].encode('utf-8') + ' / '
- return restext
-
- logger.info("...")
- curr = self['liste'].getCurrent()
- if curr and len(curr) == listindex['listend']:
- if curr and curr and curr[listindex['channel']] == '>>>':
- self.currindex = self['liste'].index
- self.mvconfig['postdata']['offset'] += self.mvconfig.get('size', 500)
- self['liste'].list.remove(curr)
- self.download_page()
- elif self['liste'].style == 'movi_liste':
- val = self.mvconfig.get('playmovieselect', 'url_video_hd')
- if val == 'playmovieselect':
- options = []
- if checkvideourl('url_video_low'):
- options.append((getquali('url_video_low') + _('Low'), curr[listindex['url_video_low']]))
- if checkvideourl('url_video'):
- options.append((getquali('url_video') + _('Medium'), curr[listindex['url_video']]))
- if checkvideourl('url_video_hd'):
- options.append((getquali('url_video_hd') + _('Maximum'), curr[listindex['url_video_hd']]))
- if options:
- self.session.openWithCallback(ChoiceBoxCallback, ChoiceBox, title=_('Video') + ' ' + _('Resolution'), list=options, selection=len(options), titlebartext=_('Option Selector'))
- elif val in listindex:
- if checkvideourl(val):
- ChoiceBoxCallback((val, curr[listindex[val]]))
- elif checkvideourl('url_video_hd'):
- ChoiceBoxCallback(('url_video_hd', curr[listindex['url_video_hd']]))
- elif checkvideourl('url_video'):
- ChoiceBoxCallback(('url_video', curr[listindex['url_video']]))
- elif checkvideourl('url_video_low'):
- ChoiceBoxCallback(('url_video_low', curr[listindex['url_video_low']]))
- elif curr and len(curr) == listchannel['listend'] and curr[listchannel['channel']]:
- self.mvconfig['postdata']['queries'] = []
- self.mvconfig['postdata']['queries'].append({'fields': ['channel'], 'query': curr[listchannel['channel']].encode('utf-8')})
- self.channelscurrindex = self['liste'].index
- self['liste'].setList([])
- self['sresult'].setText('')
- self.mvconfig['postdata']['offset'] = 0
- self.download_page()
- elif curr and len(curr) == listsearchquerys['listend'] and curr[listsearchquerys['queries']]:
- self.searchentriesindex = self['liste'].index
- self.mvconfig['postdata']['queries'] = curr[listsearchquerys['queries']]
- self['liste'].setList([])
- self['sresult'].setText('')
- self.currindex = 0
- self.mvconfig['postdata']['offset'] = 0
- self.download_page()
- elif curr and len(curr) == liveindex['listend'] and curr[liveindex['master_url']]:
- if 'dash' in curr[liveindex['options']]:
- mservice = str(curr[liveindex['service']])
- sref = eServiceReference(mservice)
- sref.setPath(str(curr[liveindex['master_url']]))
- sref.setName('%s' % curr[0].encode('utf-8'))
- self.session.open(StreamPlayer, sref)
- elif 'm3u8' in curr[liveindex['options']]:
- if curr[liveindex['default']] in ('verify', 'verifyandupdate', 'cached'):
- if 'cached' in curr[liveindex['options']] and isinstance(curr[liveindex['eservice']], eServiceReference):
- self.session.open(StreamPlayer, service=curr[liveindex['eservice']])
- else:
- urlval = curr[liveindex['master_url']].encode('utf-8')
- if 'pluto' in curr[liveindex['options']] and 'deviceId' not in curr[liveindex['master_url']]:
- urlval = ('{0}&sid={1}&deviceId={2}').format(curr[liveindex['master_url']], self.plutosessionid, self.sessionid)
- self['message'].text = _('Download started...')
- self['message'].show()
- self.download.start(MygetPage, url=urlval, headers=_headers, location=True).addCallback(m3u8checkback, playlive).addErrback(self.http_failed)
- elif curr[liveindex['default']] in ('playindex', 'playmaster', 'cached') and curr[liveindex['master_url']]:
- if 'cached' in curr[liveindex['options']] and isinstance(curr[liveindex['eservice']], eServiceReference):
- self.session.open(StreamPlayer, service=curr[liveindex['eservice']])
- elif curr[liveindex['default']] == 'playindex' and not curr[liveindex['index_url']] and curr[liveindex['master_url']]:
- currindex = self['liste'].getIndex()
- curr[liveindex['default']] = 'verify'
- self.mvconfig['livechannels'][currindex][liveindex['default']] = 'verify'
- urlval = curr[liveindex['master_url']].encode('utf-8')
- self.changed['live'] = True
- self['liste'].modifyEntry(currindex, curr)
- self.__SelectionChanged()
- self.download.start(MygetPage, url=urlval, headers=_headers, location=True).addCallback(m3u8checkback, playlive).addErrback(self.http_failed)
- elif curr[liveindex['default']] == 'playmaster' and curr[liveindex['master_url']]:
- urlval = curr[liveindex['master_url']].encode('utf-8')
- playlive(urlval)
-
- def key_red(self):
- logger.info("...")
- self.setTitle(self.titeltext + ' ' + self['key_red'].text)
- self['liste'].style = 'default'
- self.currindex = 0
- if self.mvconfig['channelslist']:
- self['liste'].setList(self.mvconfig['channelslist'][:])
- self['liste'].index = self.channelscurrindex
- self.set_sresult_entries()
- else:
- self['liste'].setList([])
- self['message'].text = _('Channel selection') + '...'
- self['message'].show()
- url = 'https://mediathekviewweb.de/api/channels'
- self.download.start(MygetPage, url=url, headers=headersplain).addCallback(self.result_back_channels).addErrback(self.http_failed)
-
- def key_green(self):
- logger.info("...")
- self.setTitle(self.titeltext + ' ' + self['key_green'].text)
- self['sresult'].setText('')
- self['liste'].setList([])
- self['liste'].style = 'movi_liste'
- self.currindex = 0
- if 'queries' in self.mvconfig['postdata']:
- del self.mvconfig['postdata']['queries']
- self.mvconfig['postdata']['offset'] = 0
- self.download_page()
-
- def key_blue(self):
- logger.info("...")
- self.setTitle(self.titeltext + ' ' + self['key_blue'].text + ' ' + _('Movies'))
- if not self.mvconfig['favorites']:
- self.mvconfig['favorites'], self.tmplist, changed = read_favorites(self.tmplist)
- if changed:
- self.changed['favo'] = True
- if self.mvconfig['favorites']:
- self['liste'].style = 'movi_liste'
- self['liste'].setList(self.mvconfig['favorites'][:])
- self.set_sresult_entries()
- else:
- self.session.open(MessageBox, _('No favorites available'), MessageBox.TYPE_INFO, timeout=3)
-
- def searchentries(self, res=False):
- logger.info("...")
- if res:
- self.setTitle(self.titeltext + ' ' + _('Search list'))
- self['liste'].style = 'search_querys'
- self['liste'].setList(self.mvconfig['searchquerys'][:])
- self.currindex = 0
- self['liste'].index = self.searchentriesindex
- self.set_sresult_entries()
-
- def searchallentries(self, res=False):
- logger.info("...")
- if res:
- if self.mvconfig['searchquerys']:
- self['liste'].style = 'movi_liste'
- self['liste'].setList([])
- self['sresult'].setText('')
- self.currindex = 0
- self.mvconfig['postdata']['offset'] = 0
- self.mvconfig['postdata']['queries'] = list()
- _have = {}
- for searchquerys in self.mvconfig['searchquerys']:
- for x in searchquerys[listsearchquerys['queries']]:
- if x.get('fields') != listfields['channel']:
- if x['query'] not in _have:
- _have[x['query']] = True
- self.mvconfig['postdata']['queries'].append({'fields': self.mvconfig['searchquerysfields'], 'query': x['query'].encode('utf-8')})
-
- self.download_page()
- else:
- self.session.open(MessageBox, _('No search list available'), MessageBox.TYPE_INFO, timeout=3)
-
- def searchfor(self, name='', fields=None):
- if fields is None:
- fields = ['topic', 'title']
- logger.info("name: %s, fields: %s", name, fields)
- if name:
- self['liste'].setList([])
- self['sresult'].setText('')
- self['liste'].style = 'movi_liste'
- self.currindex = 0
- self.mvconfig['postdata']['offset'] = 0
- self.mvconfig['postdata']['queries'] = list()
- self.mvconfig['postdata']['queries'].append({'fields': fields, 'query': name.encode('utf-8')})
- self.download_page()
-
- def addsearchentries(self, querys=None):
- if querys is None:
- querys = {}
- logger.info("...")
- curr = self['liste'].getCurrent()
- if not querys and curr and len(curr) >= listindex['url_video_hd'] and curr[listindex['channel']] != '>>>':
- querys = {}
- querys['queries'] = []
- querys['queries'].append({'fields': ['topic'], 'query': curr[listindex['topic']].encode('utf-8')})
- querys['queries'].append({'fields': ['channel'], 'query': curr[listindex['channel']]})
- elif not querys:
- querys = {}
- querys['queries'] = []
- querys['queries'].append({'fields': ['topic'], 'query': 'text'})
- self.session.openWithCallback(self.searchEntryCallback, MediathekCockpitSearchEntry, self.mvconfig, currquery=querys)
-
- def searchEntryCallback(self, res):
- logger.info("res: %s", res)
- if res:
- self.changed['querys'] = True
- self.mvconfig = res
-
- def searchentriesremove(self):
- logger.info("...")
- curr = self['liste'].getCurrent()
- if curr:
-
- def del_curr(answer=None):
- logger.info("...")
- if answer:
- self.searchentriesindex = self['liste'].index
- del self.mvconfig['searchquerys'][self.searchentriesindex]
- self.searchentries(True)
- self.changed['querys'] = True
+ Search.__init__(self, session, self.keyboardCallback)
+ self.last_service = self.session.nav.getCurrentlyPlayingServiceReference()
- text = '%s\n%s\n\n%s' % (curr[0], curr[1], _('Delete'))
- self.session.openWithCallback(del_curr, MessageBox, text)
+ self.postdata = {}
+ self.postdata = {'sortOrder': 'desc', 'sortBy': 'timestamp'}
+ self.postdata['size'] = config.plugins.mediathekcockpit.size.value
+ self.postdata['offset'] = 0
+ self.postdata['future'] = config.plugins.mediathekcockpit.future.value
- def manualsearch(self, query=""):
- logger.info("...")
- text = query if query else ""
- curr = self['liste'].getCurrent()
- if curr and len(curr) == listindex['listend'] and curr[listindex['channel']] != '>>>':
- if curr[listindex['topic']]:
- text = curr[listindex['topic']]
- self.session.openWithCallback(self.searchfor, NTIVirtualKeyBoard, title=_('Enter search text'), text=text)
-
- def key_yellow_text(self):
- logger.info("...")
- if self.mvconfig['searchstart'] == 'easyselection':
- text = _('Easy selection')
- elif self.mvconfig['searchstart'] == 'searchentries':
- text = _('Search list')
- elif self.mvconfig['searchstart'] == 'searchallentries':
- text = _('All')
- elif self.mvconfig['searchstart'] == 'manualsearch':
- text = _('Manual search')
- elif self.mvconfig['searchstart'] == 'livechannels':
- text = _('Live channels')
+ if query:
+ self.openKeyboard(query, self.postdata)
else:
- text = _('Search')
- self['key_yellow'].text = text
- self['sresult'].setText('')
-
- def key_yellow(self):
- logger.info("...")
- curr = self['liste'].getCurrent()
- if not self.mvconfig['searchquerys']:
- self.mvconfig['searchquerys'] = read_searchquerys()
-
- def ChoiceBoxCallback(answer):
- logger.info("...")
- answer = answer and answer[1]
- if answer == 'searchentries':
- self.searchentries(True)
- elif answer == 'searchallentries':
- self.searchallentries(True)
- elif answer == 'searchfor':
- self.searchfor(curr[listindex['topic']])
- elif answer == 'searchfortitle':
- self.searchfor(curr[listindex['title']], ['title'])
- elif answer == 'addsearchentries':
- self.addsearchentries()
- elif answer == 'manualsearch':
- self.manualsearch()
- elif answer == 'livechannels':
- self.load_livechannels()
- elif answer == 'searchentriesedit':
- self.searchentriesindex = self['liste'].index
- self.session.openWithCallback(self.searchentries, MediathekCockpitSearchEntry, currquery={'queries': curr[listsearchquerys['queries']], 'queryindex': self['liste'].index})
- elif answer == 'searchentriesremove':
- self.searchentriesremove()
- elif answer == 'moveUp':
- self.moveUp()
- elif answer == 'moveDown':
- self.moveDown()
-
- if self.mvconfig['searchstart'] == 'easyselection':
- options = []
- # havesearchquerys = False
- if len(self.mvconfig['searchquerys']) > 0:
- options.append((_('Search list'), 'searchentries'))
- options.append((_('Search for all entries in the search list'), 'searchallentries'))
- if curr and len(curr) == listsearchquerys['listend'] and curr[listsearchquerys['queries']] and self['liste'].style == 'search_querys':
- # title = _('Name') + ': ' + curr[0] + '\n' + _('fields') + ': ' + curr[1] + '\n' + _('Channel') + ': ' + curr[2]
- options.append((_('Search list') + ' ' + _('Edit selected entry'), 'searchentriesedit'))
- options.append((_('Search list') + ' ' + _('Remove entry'), 'searchentriesremove'))
- options.append((_('Search list') + ' ' + _('Add entry'), 'addsearchentries'))
- if curr and len(curr) == listindex['listend'] and curr[listindex['channel']] != '>>>':
- if curr[listindex['topic']]:
- options.append(('--', '--'))
- options.append((_("Search for '%s'") % curr[listindex['topic']], 'searchfor'))
- if curr[listindex['title']]:
- options.append((_("Search for '%s'") % curr[listindex['title']], 'searchfortitle'))
- options.append(('--', '--'))
- options.append((_('Manual Search'), 'manualsearch'))
- options.append((_('Live channels'), 'livechannels'))
- if options:
- self.session.openWithCallback(ChoiceBoxCallback, ChoiceBox, title=_('Easy selection'), list=options, titlebartext='MediathekCockpit')
+ self.showScreen(Channels, self.postdata)
+
+ def showScreen(self, screen, *args):
+ logger.info("screen: %s", screen)
+ self.session.openWithCallback(self.showScreenCallback, screen, *args)
+
+ def showScreenCallback(self, return_screen):
+ logger.info("return_screen: %s", return_screen)
+ if return_screen == "channels":
+ self.showScreen(Channels, self.postdata)
+ elif return_screen == "streams":
+ self.showScreen(Live, self.postdata)
+ elif return_screen == "movies":
+ self.showScreen(Movies, self.postdata)
+ elif return_screen == "search":
+ self.openKeyboard("", self.postdata)
+ elif return_screen == "settings":
+ self.showScreen(Settings, self.postdata)
else:
- ChoiceBoxCallback((self.mvconfig['searchstart'], self.mvconfig['searchstart']))
-
- def showMenu(self):
- logger.info("...")
- curr = self['liste'].getCurrent()
- options = []
- title = _('Select')
- titlebartext = 'MediathekCockpit'
- if not self.mvconfig['favorites']:
- self.mvconfig['favorites'], self.tmplist, changed = read_favorites(self.tmplist)
- if changed:
- self.changed['favo'] = True
- if not self.mvconfig['searchquerys']:
- self.mvconfig['searchquerys'] = read_searchquerys()
-
- def ChoiceBoxCallback(answer):
- logger.info("...")
- answer = answer and answer[1]
- if answer == 'setupview':
-
- def setupback(res=None):
- logger.info("...")
- if res:
- self.mvconfig = res
- self.changed['config'] = True
- self['liste'].setList([])
- self.currindex = 0
- self.mvconfig['postdata']['offset'] = 0
- self.key_yellow_text()
-
- self.session.openWithCallback(setupback, MediathekCockpitSetup, self.mvconfig)
- elif answer == 'searchentries':
- self.searchentries(True)
- elif answer == 'searchallentries':
- self.searchallentries(True)
- elif answer == 'searchentriesedit':
- self.searchentriesindex = self['liste'].index
- self.session.openWithCallback(self.searchentries, MediathekCockpitSearchEntry, currquery={'queries': curr[3], 'queryindex': self['liste'].index})
- elif answer == 'searchentriesremove':
- self.searchentriesremove()
- elif answer == 'searchfor':
- self.searchfor(curr[listindex['topic']])
- elif answer == 'searchfortitle':
- self.searchfor(curr[listindex['title']], ['title'])
- elif answer == 'addsearchentries':
- self.addsearchentries()
- elif answer == 'manualsearch':
- self.manualsearch()
- elif answer == 'moveUp':
- self.moveUp()
- elif answer == 'moveDown':
- self.moveDown()
- elif answer == 'livestreammoveUp':
- self.livestreammoveUp()
- elif answer == 'livestreammoveDown':
- self.livestreammoveDown()
- elif answer == 'addtofavorites':
- if curr[listindex['id']] not in self.tmplist['favodict']:
- self.mvconfig['favorites'].insert(0, curr)
- self.tmplist['favodict'][curr[listindex['id']]] = True
- self.changed['favo'] = True
- tmpname = ''
- tmpname += curr[listindex['channel']]
- tmpname += '\n' + curr[listindex['topic']]
- tmpname += '\n' + curr[listindex['title']]
- self.session.open(MessageBox, _("Added '%s'") % tmpname, type=MessageBox.TYPE_INFO, timeout=3)
- else:
- self.session.open(MessageBox, _('Entry already exists'), type=MessageBox.TYPE_INFO, timeout=3)
- elif answer == 'delfavorites':
-
- def del_curr(answer=None):
- logger.info("...")
- if answer:
- self.mvconfig['favorites'].pop(self['liste'].getIndex())
- self['liste'].updateList(self.mvconfig['favorites'][:])
- self.set_sresult_entries()
- self.__SelectionChanged()
- self.changed['favo'] = True
- if curr[listindex['id']] in self.tmplist['favodict']:
- del self.tmplist['favodict'][curr[listindex['id']]]
-
- text = '%s\n%s\n\n%s' % (curr[0], curr[1], _('Delete'))
- self.session.openWithCallback(del_curr, MessageBox, text)
- elif answer in ('moviesave', 'movierecord'):
-
- def moviesavecallback(answerres):
- logger.info("...")
- answerres = answerres and answerres[1]
- if answerres:
-
- def SaveStreamToDisk(path):
- logger.info("...")
- if path and pathExists(path):
- if curr[listindex['topic']].replace('&', 'und').upper() not in curr[listindex['title']].replace('&', 'und').upper():
- name = curr[listindex['topic']].encode('utf-8') + ' ' + curr[listindex['title']].encode('utf-8')
- else:
- name = curr[listindex['title']].encode('utf-8')
- description = curr[listindex['description']].encode('utf-8')
- # datestring = datetime.now().strftime('%Y%m%d - ')
- p = re.compile('[.:,!/]')
- disname = name
- name = p.sub('', name)
- url = answerres.encode('utf-8')
- formatval = '.mp4'
- pos = url.rfind('.')
- if pos != -1:
- formatval = url[pos:]
- recordfilename = getRecordingFilename(name, path.encode('utf-8'))
- if answer == 'moviesave':
- downloadManager.AddJob(DownloadJob(url, recordfilename + formatval, disname, description, headers_ts))
- self.session.open(MessageBox, _('Added:') + '\n\n' + disname, type=MessageBox.TYPE_INFO, timeout=4)
- elif answer == 'movierecord':
-
- def downloadsegment2(m3u8list, _urlval):
- logger.info("...")
- file1 = open(recordfilename + '.ts', 'wb')
- file1.close()
- filemeta = open(recordfilename + '.ts.meta', 'wb')
- filemeta.write('0:0:0:0:0:0:C00000:0:0:0:' + '\n')
- filemeta.write(disname + '\n')
- filemeta.write(curr[listindex['channel']].encode('utf-8') + '\n')
- filemeta.write(str(int(time())) + '\n')
- filemeta.close()
- maxlen = len(m3u8list)
- for x, m3u8 in enumerate(m3u8list):
- if m3u8:
- file = open(recordfilename + '.ts', 'ab')
- downloadManager.active_jobs.append(DownloadJob(m3u8, file, '%s (%d/%d) %s' % ('Segment', x + 1, maxlen, disname), description, headers_ts))
-
- if maxlen:
- downloadManager.kick()
- self.session.open(MessageBox, _('Added:') + '\n\n' + disname, type=MessageBox.TYPE_INFO, timeout=4)
-
- def backm3u8result(result):
- logger.info("...")
- m3u8 = result[0]
- url = result[1]
- if '#EXTM3U' in m3u8:
- m3u8res = m3u8parser(m3u8, url)
- if m3u8res.extxmedia_play():
- segment = m3u8res.parser_segment()
- if segment:
- downloadsegment2(segment, url)
- elif m3u8res.extxmedia_hls():
- print('hls')
- elif m3u8res.extxmedia_parse():
- print('download')
- chunkurl, _suburi = m3u8res.parser_all()
- if chunkurl:
- self.download.start(MygetPage, url=chunkurl, headers=headers_ts, location=True).addCallback(backm3u8result).addErrback(self.http_failed)
-
- self.download.start(MygetPage, url=url, headers=headers_ts, location=True).addCallback(backm3u8result).addErrback(self.http_failed)
- elif path:
- self.session.open(MessageBox, _('Directory %s does not exist.') % path, type=MessageBox.TYPE_ERROR, timeout=5)
-
- SaveStreamToDisk(self.mvconfig.get('moviesavedir', '/media/hdd/movie/').encode('utf-8'))
-
- def checkvideourl(was):
- logger.info("...")
- if was in listindex and curr[listindex[was]] and curr[listindex[was]].startswith('http'):
- return True
- return False
-
- def getquali(was):
- logger.info("...")
- restext = ''
- pos = curr[listindex[was]].rfind('.')
- if pos != -1:
- restext = curr[listindex[was]][pos + 1:].encode('utf-8') + ' / '
- return restext
-
- val = self.mvconfig.get('playmovieselect', 'url_video_hd')
- if val == 'playmovieselect':
- options = []
- if checkvideourl('url_video_low'):
- options.append((getquali('url_video_low') + _('Low'), curr[listindex['url_video_low']]))
- if checkvideourl('url_video'):
- options.append((getquali('url_video') + _('Medium'), curr[listindex['url_video']]))
- if checkvideourl('url_video_hd'):
- options.append((getquali('url_video_hd') + _('Maximum'), curr[listindex['url_video_hd']]))
- if options:
- self.session.openWithCallback(moviesavecallback, ChoiceBox, title=_('Video') + ' ' + _('Resolution'), list=options, selection=len(options), titlebartext=_('Option Selector'))
- elif val in listindex:
- if checkvideourl(val):
- moviesavecallback((val, curr[listindex[val]]))
- elif checkvideourl('url_video_hd'):
- moviesavecallback(('url_video_hd', curr[listindex['url_video_hd']]))
- elif checkvideourl('url_video'):
- moviesavecallback(('url_video', curr[listindex['url_video']]))
- elif checkvideourl('url_video_low'):
- moviesavecallback(('url_video_low', curr[listindex['url_video_low']]))
- elif answer == 'downloadview':
- self.session.open(MediathekCockpitDownloadView)
- elif answer == 'livechannels':
- self.load_livechannels()
-
- if len(self.mvconfig['searchquerys']) > 0:
- options.append((_('Search list'), 'searchentries'))
- options.append((_('Search for all entries in the search list'), 'searchallentries'))
- if curr and len(curr) == listsearchquerys['listend'] and curr[listsearchquerys['queries']] and self['liste'].style == 'search_querys':
- title = _('Name') + ': ' + curr[0] + '\n' + _('Fields:') + curr[1] + '\n' + _('Channel') + ': ' + curr[2]
- options.append((_('Search list') + ' ' + _('Edit selected entry'), 'searchentriesedit'))
- options.append((_('Search list') + ' ' + _('Remove entry'), 'searchentriesremove'))
- options.append((_('Search list') + ' ' + _('Add entry'), 'addsearchentries'))
- cantofavo = False
- if curr and len(curr) == listindex['listend'] and curr[listindex['channel']] != '>>>':
- cantofavo = True
- if curr[listindex['topic']]:
- titlebartext = curr[listindex['topic']]
- options.append(('--', '--'))
- options.append((_("Search for '%s'") % curr[listindex['topic']][:65], 'searchfor'))
- if curr[listindex['title']]:
- title = curr[listindex['title']]
- options.append((_("Search for '%s'") % curr[listindex['title']][:65], 'searchfortitle'))
- options.append(('--', '--'))
- options.append((_('Manual Search'), 'manualsearch'))
- if cantofavo and not self['key_blue'].text in self.title:
- options.append(('--', '--'))
- options.append((_('Add favorite'), 'addtofavorites'))
- elif len(self.mvconfig['favorites']) > 0 and self['key_blue'].text in self.title:
- options.append(('--', '--'))
- options.append((_('Remove favorite'), 'delfavorites'))
- if DownloadJob and curr and len(curr) >= listindex['url_video_hd'] and curr[listindex['channel']] != '>>>':
- if curr[listindex['url_video_low']].endswith(('mp4', 'flv')) or curr[listindex['url_video']].endswith(('mp4', 'flv')) or curr[listindex['url_video_hd']].endswith(('mp4', 'flv')):
- options.append((_('movie') + ' ' + _('Save'), 'moviesave'))
- elif curr[listindex['url_video_low']].endswith('m3u8') or curr[listindex['url_video']].endswith('m3u8') or curr[listindex['url_video_hd']].endswith('m3u8'):
- if curr[listindex['duration']]:
- options.append((_('movie') + ' ' + _('Save'), 'movierecord'))
- options.append(('--', '--'))
- options.append((_('Live channels'), 'livechannels'))
- options.append((_('Setup'), 'setupview'))
- options.append((_('Download manager'), 'downloadview'))
- if options:
- self.session.openWithCallback(ChoiceBoxCallback, ChoiceBox, title=title, list=options, skin_name='ChoiceBoxMvMenu', titlebartext=titlebartext)
-
- def key_info(self):
- logger.info("...")
- curr = self['liste'].getCurrent()
- options = []
- title = _('Select')
- titlebartext = 'MediathekCockpit'
-
- def ChoiceBoxCallback(answer):
- logger.info("...")
- answer = answer and answer[1]
- if answer == 'moveUp':
- self.moveUp()
- elif answer == 'moveDown':
- self.moveDown()
- elif answer == 'livestreammoveUp':
- self.livestreammoveUp()
- elif answer == 'livestreammoveDown':
- self.livestreammoveDown()
- elif answer == 'livestreamdel':
-
- def del_curr(answer1=None):
- logger.info("...")
- if answer1:
- self.changed['live'] = True
- self.mvconfig['livechannels'].pop(self['liste'].getIndex())
- self['liste'].updateList(self.mvconfig['livechannels'][:])
- self.__SelectionChanged()
- self.set_sresult_entries()
-
- text = '%s\n\n%s' % (curr[0], _('Delete'))
- self.session.openWithCallback(del_curr, MessageBox, text)
- elif answer in ('verifyandupdate', 'cached', 'verify', 'playmaster'):
- self.changed['live'] = True
- currindex = self['liste'].getIndex()
- curr[liveindex['default']] = answer
- self.mvconfig['livechannels'][currindex][liveindex['default']] = answer
- if answer in ('verifyandupdate', 'verify', 'playmaster'):
- curr[liveindex['index_url']] = ''
- curr[liveindex['audio_url']] = ''
- curr[liveindex['eservice']] = ''
- self.mvconfig['livechannels'][currindex][liveindex['index_url']] = ''
- self.mvconfig['livechannels'][currindex][liveindex['audio_url']] = ''
- self['liste'].modifyEntry(currindex, curr)
- self.__SelectionChanged()
-
- if curr and len(curr) == listindex['listend'] and curr[listindex['channel']] != '>>>':
- self.session.open(MediathekCockpitEventView, curr)
- elif curr and len(curr) == liveindex['listend']:
- title = ('{0} ({1})\n{2} ({3})').format(_('Default'), curr[liveindex['default']], _('options'), (', ').join(curr[liveindex['options']]))
- titlebartext = curr[liveindex['name']]
- options.append((_('Move selected item up'), 'livestreammoveUp'))
- options.append((_('Move selected item down'), 'livestreammoveDown'))
- if 'verifyandupdate' in curr[liveindex['options']] and curr[liveindex['default']] != 'verifyandupdate':
- options.append((_('Reset') + ' (verifyandupdate)', 'verifyandupdate'))
- if 'verify' in curr[liveindex['options']] and curr[liveindex['default']] != 'verify':
- options.append((_('Reset') + ' (verify)', 'verify'))
- if 'playmaster' in curr[liveindex['options']] and curr[liveindex['default']] != 'playmaster':
- options.append((_('Reset') + ' (playmaster)', 'playmaster'))
- if options:
- options.append(('--', '--'))
- options.append((_('remove entry'), 'livestreamdel'))
- elif curr and len(curr) == 4 and curr[3] and self['liste'].style == 'search_querys':
- title = _('Name') + ': ' + curr[0] + '\n' + _('Fields:') + curr[1] + '\n' + _('Channel') + ': ' + curr[2]
- currindex = self['liste'].getIndex()
- if currindex != 0:
- options.append((_('Move selected item up'), 'moveUp'))
- if currindex != self['liste'].count() - 1:
- options.append((_('Move selected item down'), 'moveDown'))
- if options:
- self.session.openWithCallback(ChoiceBoxCallback, ChoiceBox, title=title, list=options, skin_name='ChoiceBoxMvMenu', titlebartext=titlebartext)
-
- def moveUp(self):
- logger.info("...")
- self.changed['querys'] = True
- currindex = self['liste'].getIndex()
- self['liste'].moveSelection('moveUp')
- self['liste'].list.insert(self['liste'].getIndex(), self['liste'].list.pop(currindex))
- self.mvconfig['searchquerys'].insert(self['liste'].getIndex(), self.mvconfig['searchquerys'].pop(currindex))
-
- def moveDown(self):
- logger.info("...")
- self.changed['querys'] = True
- currindex = self['liste'].getIndex()
- self['liste'].moveSelection('moveDown')
- self['liste'].list.insert(self['liste'].getIndex(), self['liste'].list.pop(currindex))
- self.mvconfig['searchquerys'].insert(self['liste'].getIndex(), self.mvconfig['searchquerys'].pop(currindex))
-
- def livestreammoveUp(self):
- logger.info("...")
- curr = self['liste'].getCurrent()
- if curr and len(curr) == liveindex['listend']:
- print('livestreammoveUp')
- self.changed['live'] = True
- currindex = self['liste'].getIndex()
- self['liste'].moveSelection('moveUp')
- self['liste'].list.insert(self['liste'].getIndex(), self['liste'].list.pop(currindex))
- self.mvconfig['livechannels'].insert(self['liste'].getIndex(), self.mvconfig['livechannels'].pop(currindex))
-
- def livestreammoveDown(self):
- logger.info("...")
- curr = self['liste'].getCurrent()
- if curr and len(curr) == liveindex['listend']:
- self.changed['live'] = True
- currindex = self['liste'].getIndex()
- self['liste'].moveSelection('moveDown')
- self['liste'].list.insert(self['liste'].getIndex(), self['liste'].list.pop(currindex))
- self.mvconfig['livechannels'].insert(self['liste'].getIndex(), self.mvconfig['livechannels'].pop(currindex))
-
- def download_page(self):
- logger.info("...")
- page = self.mvconfig['postdata']['offset'] / self.mvconfig['size'] + 1
- self['message'].text = _('Please wait... Loading list...') + ' ' + _('Page:') + ' ' + str(page)
- self['message'].show()
- url = 'https://mediathekviewweb.de/api/query'
- self.download.start(MygetPage, url=url, method='POST', postdata=json.dumps(self.mvconfig['postdata']), headers=headersplain).addCallback(self.result_back).addErrback(self.http_failed)
-
- def result_back(self, result):
- logger.info("...")
- if result:
- alist = []
- self.setTitle(self.titeltext + ' ' + _('Online') + ' ' + _('Movies'))
- self['message'].text = _('Please wait...') + ' Parse'
- self['message'].show()
- self['liste'].style = 'movi_liste'
- data = json.loads(result)
- if data.get('error'):
- return
- del result
- logger.debug("data: %s", data)
- queryInfo = data['result']['queryInfo']
- self.mvconfig['resultCount'] = queryInfo['resultCount']
- self.mvconfig['totalResults'] = queryInfo['totalResults']
- self.mvconfig['searchEngineTime'] = queryInfo['searchEngineTime']
- self.mvconfig['filmlisteTimestamp'] = datetime.fromtimestamp(int(queryInfo['filmlisteTimestamp'])).strftime('%d.%m.%Y %H:%M:%S')
- queryInfo.clear()
- results = data['result']['results']
- data.clear()
- for x in results:
- tmp = listempty[:]
- if 'timestamp' in x:
- dtmp = datetime.fromtimestamp(int(x['timestamp'])).strftime('%d.%m.%Y %H:%M:%S')
- tmp[listindex['date']] = dtmp[0:10]
- tmp[listindex['time']] = dtmp[11:16]
- if 'duration' in x:
- tmp[listindex['duration']] = str(timedelta(seconds=x['duration'] or 0))
- if 'channel' in x:
- name = x['channel'].encode('utf-8')
- ptr = Load_My_Pixmap('%slogos/%s.png' % (plugindir, name.replace(' ', '').upper()))
- tmp[listindex['channel']] = name
- tmp[listindex['channelpixmap']] = ptr
- tmp[listindex['timetext']] = _('Clock')
- if 'topic' in x:
- tmp[listindex['topic']] = x['topic'].encode('utf-8')
- if 'title' in x:
- tmp[listindex['title']] = x['title'].encode('utf-8')
- if 'description' in x:
- tmp[listindex['description']] = x['description'].encode('utf-8')
- if 'size' in x:
- tmp[listindex['size']] = int(x['size'] or 0) / 1048576
- if 'id' in x:
- tmp[listindex['id']] = x['id']
- if 'url_video_low' in x:
- tmp[listindex['url_video_low']] = x['url_video_low']
- if 'url_video' in x:
- tmp[listindex['url_video']] = x['url_video']
- if 'url_video_hd' in x:
- tmp[listindex['url_video_hd']] = x['url_video_hd']
- if 'url_website' in x:
- tmp[listindex['url_website']] = x['url_website']
- alist.append(tmp)
-
- math = min(self.mvconfig['postdata']['offset'] + self.mvconfig['resultCount'], self.mvconfig['totalResults'])
- if self.mvconfig['totalResults'] != math:
- addtmp = listempty[:]
- addtmp[listindex['channel']] = '>>>'
- addtmp[listindex['topic']] = _('Next page')
- addtmp[listindex['title']] = _('Select')
- addtmp[listindex['date']] = '>>>'
- alist.append(addtmp)
- self['liste'].setList(alist)
- if self.currindex:
- self['liste'].setIndex(self.currindex)
- self.set_sresult()
- return
-
- def result_back_channels(self, result):
- logger.info("...")
- if result:
- self['liste'].style = 'default'
- data = json.loads(result)
- if data.get('error', True):
- return
- channels = data['channels']
- self.mvconfig['channelslist'] = []
- self.mvconfig['channels'] = []
- for val in channels:
- name = val.encode('utf-8')
- ptr = Load_My_Pixmap('%slogos/%s.png' % (plugindir, name.replace(' ', '').upper()))
- self['liste'].list.append((name, '', ptr))
- self.mvconfig['channelslist'].append((name, '', ptr))
- self.mvconfig['channels'].append(name)
-
- self['liste'].setList(self.mvconfig['channelslist'][:])
- self.set_sresult_entries()
- self['message'].hide()
- return
-
- def load_livechannels(self):
- logger.info("...")
- if not self.mvconfig['livechannels']:
- self.mvconfig['livechannels'] = read_livechannels()
- self.setTitle(self.titeltext + ' ' + _('Live channels'))
- self['liste'].style = 'default'
- if self.mvconfig['livechannels']:
- self['liste'].setList(self.mvconfig['livechannels'][:])
- self.set_sresult_entries()
-
- def set_sresult(self):
- logger.info("...")
- self['message'].hide()
- math = min(self.mvconfig['postdata']['offset'] + self.mvconfig['resultCount'], self.mvconfig['totalResults'])
- self['sresult'].setText('%s: (%s/%s)\n%s: %s ms\n%s: %s' % (
- _('Entries'), math, str(self.mvconfig['totalResults']), _('Time'), str(self.mvconfig['searchEngineTime']), _('From'), str(self.mvconfig['filmlisteTimestamp'])))
-
- def set_sresult_entries(self):
- logger.info("...")
- self['sresult'].setText(('{0}:{1}').format(_('Entries'), self['liste'].count()))
-
- def __SelectionChanged(self):
- logger.info("...")
-
- def clearall():
- logger.info("...")
- self['description'].setText('')
- self['date'].setText('')
-
- curr = self['liste'].getCurrent()
- if curr:
- summaryempty = [''] * listindex['id']
- currlen = len(curr)
- if currlen == listindex['listend'] and curr[listindex['channel']] != '>>>':
- self['description'].setText(curr[listindex['description']])
- size = curr[listindex['size']]
- sizetext = ''
- if size:
- sizetext = ' %d MB' % size
- self['date'].setText('%s: %s\n%s: %s\n%s: %s%s' % (_('Date'), curr[listindex['date']], _('Time'), curr[listindex['time']], _('Duration'), curr[listindex['duration']], sizetext))
- summaryempty = curr[:listindex['id']]
- summaryempty[listindex['size']] = sizetext
- elif currlen == listindex['listend'] and curr[listindex['channel']] == '>>>':
- summaryempty[listindex['channel']] = curr[listindex['topic']]
- summaryempty[listindex['title']] = curr[listindex['title']]
- elif currlen == liveindex['listend']:
- txt = ('master_url\n{0}').format(curr[liveindex['master_url']])
- if curr[liveindex['index_url']]:
- txt += ('\n\nindex_url\n{0}').format(curr[liveindex['index_url']])
- if curr[liveindex['audio_url']]:
- txt += ('\n\naudio_url\n{0}').format(curr[liveindex['audio_url']])
- dtxt = ('{0} ({1})\n{2} ({3})').format(_('Default'), curr[liveindex['default']], _('options'), (', ').join(curr[liveindex['options']]))
- self['date'].setText(dtxt)
- self['description'].setText(txt)
- summaryempty[listindex['channel']] = curr[liveindex['name']]
- summaryempty[listindex['channelpixmap']] = curr[liveindex['channelpixmap']]
- elif currlen == listchannel['listend']:
- clearall()
- summaryempty[listindex['channel']] = curr[listchannel['channel']]
- summaryempty[listindex['channelpixmap']] = curr[listchannel['channelpixmap']]
- elif currlen == listsearchquerys['listend']:
- summaryempty[listindex['channel']] = curr[listsearchquerys['name']]
- summaryempty[listindex['title']] = _('Fields:') + ' ' + curr[listsearchquerys['fields']]
- clearall()
- else:
- clearall()
- self['summaryliste'].setList(summaryempty)
- else:
- clearall()
-
- def http_failed(self, failure_instance, *args):
- logger.info("...")
- error_message = str(failure_instance.getErrorMessage())
- if not error_message:
- error_message = str(failure_instance)
- print('#' * 50)
- print(error_message, str(args))
- if 'CancelledError' not in error_message:
- print('#' * 50)
- self.session.open(MessageBox, error_message, MessageBox.TYPE_INFO, timeout=10, title=_('Download failed'))
- self['message'].hide()
-
- def createSummary(self):
- logger.info("...")
- return MediathekCockpitSummary
+ # exit
+ self.session.nav.playService(self.last_service)
- def __onClose(self):
- logger.info("...")
- if self.changed['config']:
- write_mvconfig(self.mvconfig)
- if self.changed['favo']:
- write_favorites(self.mvconfig['favorites'])
- if self.changed['querys']:
- write_searchquerys(self.mvconfig['searchquerys'])
- if self.changed['live']:
- write_livechannels(self.mvconfig['livechannels'])
- # _pixmap_cache.clear()
- self.mvconfig.clear()
- self.tmplist.clear()
- self.changed.clear()
- self.session.nav.playService(self.lastservice)
+ def keyboardCallback(self, postdata):
+ self.showScreen(Movies, postdata)
diff --git a/src/MediathekCockpitEventView.py b/src/MediathekCockpitEventView.py
deleted file mode 100644
index ce5f059..0000000
--- a/src/MediathekCockpitEventView.py
+++ /dev/null
@@ -1,31 +0,0 @@
-from Screens.Screen import Screen
-from Components.ActionMap import ActionMap
-from Components.Label import Label
-from Components.ScrollLabel import ScrollLabel
-from Components.Button import Button
-from .Debug import logger
-from .Constants import listindex
-
-
-class MediathekCockpitEventView(Screen):
-
- def __init__(self, session, curr):
- logger.info("...")
- self.skinName = 'MediathekCockpitEventView'
- Screen.__init__(self, session, windowTitle=curr[listindex['topic']])
- self['actions'] = ActionMap(
- ['OkCancelActions', 'ChannelSelectEPGActions'],
- {
- 'ok': self.close,
- 'cancel': self.close,
- 'showEPGList': self.close
- }
- )
- self['key_red'] = Button('')
- self['key_green'] = Button('')
- self['key_yellow'] = Button('')
- self['key_blue'] = Button('')
- self['epg_description'] = ScrollLabel(curr[listindex['title']] + '\n\n\n' + curr[listindex['description']])
- self['datetime'] = Label(curr[listindex['date']])
- self['channel'] = Label(curr[listindex['duration']] + ' ' + curr[listindex['channel']])
- self['duration'] = Label(curr[listindex['time']])
diff --git a/src/MediathekCockpitSearchEntry.py b/src/MediathekCockpitSearchEntry.py
deleted file mode 100644
index f0c48d2..0000000
--- a/src/MediathekCockpitSearchEntry.py
+++ /dev/null
@@ -1,172 +0,0 @@
-from enigma import eTimer
-from Screens.Screen import Screen
-from Components.ActionMap import ActionMap
-from Components.Sources.List import List
-from Components.Sources.StaticText import StaticText
-from Components.ConfigList import ConfigListScreen
-from Components.config import ConfigSubsection, getConfigListEntry, ConfigSubList
-from Components.config import ConfigSelection, ConfigYesNo, NoSave, ConfigDirectory
-from Components.config import KEY_OK
-from Plugins.SystemPlugins.Toolkit.NTIVirtualKeyBoard import NTIVirtualKeyBoard
-from MyJobManager import downloadManager
-from .__init__ import _
-from .Debug import logger
-from .Constants import listfields
-from .SearchUtils import convert_searchquerys
-
-
-class MediathekCockpitSearchEntry(Screen, ConfigListScreen):
- IS_DIALOG = True
-
- def __init__(self, session, mvconfig, windowTitle='MediathekCockpit ' + _('Search entry'), currquery=None):
- logger.info("...")
- self.mvconfig = mvconfig
- if currquery is None:
- currquery = {'queries': [{'fields': ['topic'], 'query': 'Tatort'}]}
- Screen.__init__(self, session, windowTitle=windowTitle)
- ConfigListScreen.__init__(self, [], session=session)
- self.skinName = 'Setup'
- self.queryindex = currquery.get('queryindex', None)
- self.default = 'Tatort'
- self.defaultfields = listfields['topic']
- self.defaultchannel = []
- for queries in currquery['queries']:
- if queries.get('fields') == listfields['channel']:
- self.defaultchannel.append(queries.get('query', None))
- elif queries.get('fields') != listfields['channel']:
- self.default = queries.get('query', None)
- self.defaultfields = queries.get('fields', ['topic'])
-
- self['key_red'] = StaticText(_('Cancel'))
- self['key_green'] = StaticText(_('Save'))
- self['actions'] = ActionMap(
- ['SetupActions', 'ColorActions'],
- {
- 'cancel': self.keyCancel,
- 'save': self.keySave
- }
- )
- self.settings = ConfigSubsection()
- self.settings.query = NoSave(ConfigDirectory(default=self.default))
- choices = []
- choices.append((listfields['topic'], _('Topic')))
- choices.append((listfields['title'], _('Title')))
- choices.append((listfields['description'], _('Description')))
- choices.append((listfields['topictitle'], _('Topic') + ' / ' + _('Title')))
- choices.append((listfields['topictitledescription'], _('Topic') + ' / ' + _('Title') + ' / ' + _('Description')))
- self.settings.fields = NoSave(ConfigSelection(default=self.defaultfields, choices=choices))
- self.channels = ConfigSubList()
- self['liste'] = List([])
- self.currindex = 0
- self.__dlRefreshTimer = eTimer()
- self.__dlRefreshTimer_conn = self.__dlRefreshTimer.timeout.connect(self._createSetup)
- self._createSetup()
-
- def _createSetup(self):
- logger.info("...")
- entries = []
- entries.append(getConfigListEntry(_('Search for:'), self.settings.query))
- entries.append(getConfigListEntry(_('Fields:'), self.settings.fields))
- entries.append(getConfigListEntry(_('Channels:')))
- for channel in self.mvconfig['channels']:
- if self.defaultchannel:
- ischannel = channel in self.defaultchannel
- else:
- ischannel = True
- tmp = NoSave(ConfigYesNo(default=ischannel))
- self.channels.append(tmp)
- entries.append(getConfigListEntry(channel, tmp))
-
- self['config'].list = entries
-
- def keyOK(self):
- logger.info("...")
- curr = self['config'].getCurrent()
- if curr and curr[1] == self.settings.query:
-
- def resulttext(res=None):
- logger.info("...")
- if res:
- self.settings.query.value = res
- self._createSetup()
-
- self.session.openWithCallback(resulttext, NTIVirtualKeyBoard, title=_('Enter text to search for'), text=self.settings.query.value)
- else:
- self['config'].handleKey(KEY_OK)
-
- def keyCancel(self):
- logger.info("...")
- self.close(None)
-
- def keySave(self):
- logger.info("...")
- searchquerys = {}
- searchquerys['queries'] = []
- searchquerys['queries'].append({'fields': self.settings.fields.value, 'query': self.settings.query.value})
- tmp = []
- for channel in range(len(self.mvconfig['channels'])):
- if self.channels[channel].value:
- tmp.append({'fields': ['channel'], 'query': self.mvconfig['channels'][channel]})
-
- if len(self.mvconfig['channels']) != len(tmp):
- searchquerys['queries'].extend(tmp)
- if self.queryindex is None:
- querys2 = convert_searchquerys(searchquerys['queries'])
- if querys2 not in self.mvconfig['searchquerys']:
- self.mvconfig['searchquerys'].insert(0, querys2)
- elif self['config'].isChanged():
- querys2 = convert_searchquerys(searchquerys['queries'])
- if self.mvconfig['searchquerys'][self.queryindex] != querys2:
- self.mvconfig['searchquerys'][self.queryindex] = querys2
- self.close(self.mvconfig)
-
- def _createSetup(self):
- logger.info("...")
- self.__dlRefreshTimer.stop()
- self.currindex = self['liste'].index
- result = []
- for job in downloadManager.getPendingJobs():
- if job.status == job.IN_PROGRESS:
- if job.recvbytes >= 5242880:
- mbinfo = '%d%% (%d/%d) Mb' % (job.progress, job.recvbytes / 1024 / 1024, job.totalbytes / 1024 / 1024)
- else:
- mbinfo = '%d%% (%d/%d) Byte' % (job.progress, job.recvbytes / 1024, job.totalbytes / 1024)
- result.append([job.name, job.getStatustext(), job.progress, str(mbinfo), job])
- else:
- result.append([job.name, job.getStatustext(), -1, '', job])
-
- self['liste'].setList(result)
- self['liste'].index = self.currindex
- if len(result) > 0 and not self.__dlRefreshTimer.isActive():
- self.__dlRefreshTimer.startLongTimer(2)
-
- def key_yellow(self):
- logger.info("...")
- self.__dlRefreshTimer.stop()
- downloadManager.active_jobs = []
- for job in downloadManager.active_jobs:
- print(job.name)
- job.abort()
-
- self._createSetup()
-
- def key_green(self):
- logger.info("...")
- self.__dlRefreshTimer.stop()
- del downloadManager.active_jobs[:]
- currjob = downloadManager.active_job
- if currjob and currjob.status == currjob.IN_PROGRESS:
- currjob.cancel()
- self._createSetup()
-
- def currabort(self):
- logger.info("...")
- curr = self['liste'].getCurrent()
- if curr and len(curr) == 5:
- self.__dlRefreshTimer.stop()
- currjob = curr[4]
- if currjob.status == currjob.NOT_STARTED:
- downloadManager.active_jobs.remove(currjob)
- elif currjob.status == currjob.IN_PROGRESS:
- currjob.cancel()
- self._createSetup()
diff --git a/src/MediathekCockpitSetup.py b/src/MediathekCockpitSetup.py
deleted file mode 100644
index 85480d5..0000000
--- a/src/MediathekCockpitSetup.py
+++ /dev/null
@@ -1,125 +0,0 @@
-from Screens.Screen import Screen
-from Components.ActionMap import ActionMap
-from Components.Sources.StaticText import StaticText
-from Components.ConfigList import ConfigListScreen
-from Components.config import ConfigSubsection, getConfigListEntry
-from Components.config import ConfigSelection, ConfigYesNo, ConfigSelectionNumber, NoSave, ConfigDirectory
-from Components.config import KEY_OK
-from Tools.Directories import pathExists
-from .MediathekCockpitDirBrowser import MediathekCockpitDirBrowser
-from .__init__ import _
-from .Debug import logger
-from .Constants import listfields
-
-
-class MediathekCockpitSetup(Screen, ConfigListScreen):
- IS_DIALOG = True
-
- def __init__(self, session, mvconfig, windowTitle=_('MediathekCockpit') + ' ' + _('Setup')):
- logger.info("...")
- self.mvconfig = mvconfig
- Screen.__init__(self, session, windowTitle=windowTitle)
- ConfigListScreen.__init__(self, [], session=session)
- self.skinName = 'Setup'
- self['key_red'] = StaticText(_('Cancel'))
- self['key_green'] = StaticText(_('Save'))
- self['actions'] = ActionMap(
- ['SetupActions', 'ColorActions'],
- {
- 'cancel': self.keyCancel,
- 'save': self.keySave
- }
- )
- self.settings = ConfigSubsection()
- self.settings.size = NoSave(ConfigSelectionNumber(min=50, max=1500, stepwidth=50, default=self.mvconfig.get('size', 500), wraparound=True))
- choices = []
- choices.append((listfields['topic'], _('Topic')))
- choices.append((listfields['title'], _('Title')))
- choices.append((listfields['description'], _('Description')))
- choices.append((listfields['topictitle'], _('Topic') + ' ' + _('Title')))
- choices.append((listfields['topictitledescription'], _('Topic') + ' ' + _('Title') + ' ' + _('Description')))
- self.settings.searchquerysfields = NoSave(ConfigSelection(default=self.mvconfig.get('searchquerysfields', listfields['topic']), choices=choices))
- self.settings.future = NoSave(ConfigYesNo(default=self.mvconfig.get('future', False)))
- choices = []
- choices.append(('easyselection', _('Easy selection')))
- choices.append(('searchentries', _('Search list')))
- choices.append(('searchallentries', _('Search for all entries in the search list')))
- choices.append(('manualsearch', _('Manual Search')))
- choices.append(('livechannels', _('Live channels')))
- self.settings.searchstart = NoSave(ConfigSelection(default=self.mvconfig.get('searchstart', 'easyselection'), choices=choices))
- choices = []
- choices.append(('quit', _('No, do nothing.')))
- choices.append(('ask', _('Ask user')))
- self.settings.askstopmovie = NoSave(ConfigSelection(default=self.mvconfig.get('askstopmovie', 'quit'), choices=choices))
- choices = []
- choices.append(('playmovieselect', _('Ask user')))
- choices.append(('url_video_low', _('Low')))
- choices.append(('url_video', _('Medium')))
- choices.append(('url_video_hd', _('Maximum')))
- self.settings.playmovieselect = NoSave(ConfigSelection(default=self.mvconfig.get('playmovieselect', 'url_video_hd'), choices=choices))
- self.settings.moviesavedir = NoSave(ConfigDirectory(default=self.mvconfig.get('moviesavedir', '/media/hdd/movie/')))
- self.settings.duration_min = NoSave(ConfigSelectionNumber(min=0, max=240, stepwidth=1, default=self.mvconfig.get('duration_min', 0), wraparound=True))
- self.settings.duration_max = NoSave(ConfigSelectionNumber(min=0, max=240, stepwidth=1, default=self.mvconfig.get('duration_max', 0), wraparound=True))
- self._createSetup()
-
- def _createSetup(self):
- logger.info("...")
- entries = []
- entries.append(getConfigListEntry(_('Entries per page'), self.settings.size))
- entries.append(getConfigListEntry(_('Fields:') + ' ' + _('Search for all entries in the search list'), self.settings.searchquerysfields))
- entries.append(getConfigListEntry(_('Future'), self.settings.future))
- entries.append(getConfigListEntry(_('Yellow button mapping'), self.settings.searchstart))
- entries.append(getConfigListEntry(''))
- entries.append(getConfigListEntry(_('Minimum movie') + ' ' + _('Duration') + ' (' + _('minutes') + ') (0=' + _('disabled') + ')', self.settings.duration_min))
- entries.append(getConfigListEntry(_('Maximum movie') + ' ' + _('Duration') + ' (' + _('minutes') + ') (0=' + _('disabled') + ')', self.settings.duration_max))
- entries.append(getConfigListEntry(_('Video') + ' ' + _('Resolution'), self.settings.playmovieselect))
- entries.append(getConfigListEntry(_('Behavior when a movie is stopped'), self.settings.askstopmovie))
- entries.append(getConfigListEntry(_('Select movie path'), self.settings.moviesavedir))
- self['config'].list = entries
-
- def LocationBoxback(self, res):
- logger.info("res: %s", res)
- if res:
- if pathExists(res):
- self.settings.moviesavedir.value = res
-
- def keyOK(self):
- logger.info("...")
- curr = self['config'].getCurrent()
- if curr and curr[1] == self.settings.moviesavedir:
- self.session.openWithCallback(self.LocationBoxback, MediathekCockpitDirBrowser, self.settings.moviesavedir.value)
- else:
- self['config'].handleKey(KEY_OK)
-
- def keySave(self):
- logger.info("...")
- if self['config'].isChanged():
- self.mvconfig['size'] = int(self.settings.size.value)
- self.mvconfig['postdata']['size'] = self.mvconfig['size']
- self.mvconfig['searchquerysfields'] = self.settings.searchquerysfields.value
- self.mvconfig['future'] = self.settings.future.value
- self.mvconfig['askstopmovie'] = self.settings.askstopmovie.value
- self.mvconfig['postdata']['Show future movies'] = self.mvconfig['future']
- self.mvconfig['searchstart'] = self.settings.searchstart.value
- self.mvconfig['playmovieselect'] = self.settings.playmovieselect.value
- self.mvconfig['postdata']['offset'] = 0
- self.mvconfig['moviesavedir'] = self.settings.moviesavedir.value
- if int(self.settings.duration_min.value) > 0:
- self.mvconfig['duration_min'] = int(self.settings.duration_min.value)
- self.mvconfig['postdata']['duration_min'] = self.mvconfig['duration_min'] * 60
- else:
- if 'duration_min' in self.mvconfig:
- del self.mvconfig['duration_min']
- if 'duration_min' in self.mvconfig['postdata']:
- del self.mvconfig['postdata']['duration_min']
- if int(self.settings.duration_max.value) > 0:
- self.mvconfig['duration_max'] = int(self.settings.duration_max.value)
- self.mvconfig['postdata']['duration_max'] = self.mvconfig['duration_max'] * 60
- else:
- if 'duration_max' in self.mvconfig:
- del self.mvconfig['duration_max']
- if 'duration_max' in self.mvconfig['postdata']:
- del self.mvconfig['postdata']['duration_max']
- self.close(self.mvconfig)
- else:
- self.close(False)
diff --git a/src/Menu.py b/src/Menu.py
new file mode 100644
index 0000000..aa89fd8
--- /dev/null
+++ b/src/Menu.py
@@ -0,0 +1,55 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Screens.ChoiceBox import ChoiceBox
+from .Downloads import Downloads
+from .Settings import Settings
+from .About import about
+from .Debug import logger
+from .__init__ import _
+
+
+class Menu():
+ def __init__(self, session, postdata):
+ self.session = session
+ self.postdata = postdata
+ options = []
+ options.append((_('Download Manager'), self.showDownloadManager))
+ options.append((_('Settings'), self.showSettings))
+ options.append((_('About'), self.showAbout))
+ self.session.openWithCallback(self.ChoiceBoxCallback, ChoiceBox, title=_('Functions'), list=options, titlebartext='MediathekCockpit' + " " + _("Menu"))
+
+ def ChoiceBoxCallback(self, selection):
+ logger.info("...")
+ if selection:
+ selection[1]()
+
+ def showDownloadManager(self):
+ logger.info("...")
+ self.session.open(Downloads)
+
+ def showSettings(self):
+ logger.info("...")
+ self.session.open(Settings, self.postdata)
+
+ def showAbout(self):
+ logger.info("...")
+ about(self.session)
diff --git a/src/MoviePlayer.py b/src/MoviePlayer.py
index 4fde353..274bb0b 100644
--- a/src/MoviePlayer.py
+++ b/src/MoviePlayer.py
@@ -1,9 +1,31 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Components.config import config
+from Components.ActionMap import ActionMap
+from Components.ServiceEventTracker import InfoBarBase
from Screens.Screen import Screen
from Screens.SimpleSummary import SimpleSummary
from Screens.InfoBarGenerics import PlayerBase, InfoBarNotifications, InfoBarSeek, InfoBarShowHide, InfoBarAudioSelection, InfoBarSubtitleSupport, InfoBarServiceErrorPopupSupport, InfoBarGstreamerErrorPopupSupport, InfoBarServiceNotifications, InfoBarPVRState
from Screens.ChoiceBox import ChoiceBox
-from Components.ActionMap import ActionMap
-from Components.ServiceEventTracker import InfoBarBase
from enigma import iServiceInformation, iPlayableService
from .__init__ import _
from .Debug import logger
@@ -17,7 +39,7 @@ class BasePlayer(InfoBarBase, InfoBarShowHide, InfoBarAudioSelection, InfoBarSub
ENABLE_RESUME_SUPPORT = False
ALLOW_SUSPEND = True
- def __init__(self, session, service, infoval=None, menuval=None, myconfig=None, lastservice=None):
+ def __init__(self, session, service, infoval=None, menuval=None, lastservice=None):
logger.info("...")
for x in [
InfoBarBase, InfoBarShowHide, InfoBarAudioSelection,
@@ -26,14 +48,11 @@ def __init__(self, session, service, infoval=None, menuval=None, myconfig=None,
]:
x.__init__(self)
- if myconfig is None:
- myconfig = {'askstopmovie': 'quit'}
self.session = session
self.service = service
self.lastservice = lastservice
self.infoval = infoval
self.menuval = menuval
- self.myconfig = myconfig
self['ShowHideActions'] = ActionMap(
['InfobarShowHideActions', 'MoviePlayerActions', 'MenuActions', 'ChannelSelectEPGActions'],
{
@@ -81,7 +100,6 @@ def __evVideoSizeChanged(self):
if service:
info = service.info()
if info:
- print('**********')
_xres = info.getInfo(iServiceInformation.sVideoWidth)
_yres = info.getInfo(iServiceInformation.sVideoHeight)
frame_rate = info.getInfo(iServiceInformation.sFrameRate)
@@ -90,11 +108,10 @@ def __evVideoSizeChanged(self):
frame_rate *= 2
frame_rate = (frame_rate + 500) / 1000
_p = 'p' if progressive else 'i'
- print(iPlayableService.evVideoTypeReady)
- print(iPlayableService.evVideoSizeChanged)
- print(iPlayableService.evVideoFramerateChanged)
- print(iPlayableService.evVideoProgressiveChanged)
- print('**********')
+ logger.debug("evVideoTypeReady: %s", iPlayableService.evVideoTypeReady)
+ logger.debug("evVideoSizeChanged: %s", iPlayableService.evVideoSizeChanged)
+ logger.debug("evVideoFramerateChanged: %s", iPlayableService.evVideoFramerateChanged)
+ logger.debug("evVideoProgressiveChanged: %s", iPlayableService.evVideoProgressiveChanged)
def createSummary(self):
logger.info("...")
@@ -106,7 +123,7 @@ class StreamPlayer(Screen, PlayerBase, BasePlayer):
def __init__(self, session, service, *args, **kwargs):
logger.info("...")
Screen.__init__(self, session)
- self.skinName = ['MvStreamPlayer', 'MvMoviePlayer', 'MoviePlayer']
+ self.skinName = ['MTCStreamPlayer', 'MTCMoviePlayer', 'MoviePlayer']
PlayerBase.__init__(self)
BasePlayer.__init__(self, session, service, *args, **kwargs)
@@ -116,7 +133,7 @@ class MoviePlayer(Screen, BasePlayer, InfoBarSeek, InfoBarServiceNotifications,
def __init__(self, session, service, *args, **kwargs):
logger.info("...")
Screen.__init__(self, session)
- self.skinName = ['MvMoviePlayer', 'MoviePlayer']
+ self.skinName = ['MTCMoviePlayer', 'MoviePlayer']
InfoBarSeek.__init__(self)
BasePlayer.__init__(self, session, service, *args, **kwargs)
InfoBarServiceNotifications.__init__(self)
@@ -128,11 +145,11 @@ def doEofInternal(self, playing):
return
if not playing:
return
- self.handleLeave(self.myconfig.get('askstopmovie', 'quit'))
+ self.handleLeave(config.plugins.mediathekcockpit.askstopmovie.value)
def leavePlayer(self):
logger.info("...")
- self.handleLeave(self.myconfig.get('askstopmovie', 'quit'))
+ self.handleLeave(config.plugins.mediathekcockpit.askstopmovie.value)
def handleLeave(self, how):
logger.info("...")
diff --git a/src/Movies.py b/src/Movies.py
new file mode 100644
index 0000000..413bb56
--- /dev/null
+++ b/src/Movies.py
@@ -0,0 +1,324 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+import os
+import re
+import json
+from datetime import datetime, timedelta
+from enigma import eServiceReference
+from Screens.Screen import Screen
+from Screens.MessageBox import MessageBox
+from Components.ActionMap import ActionMap
+from Components.config import config
+from Components.Label import Label
+from Components.Sources.StaticText import StaticText
+from Components.Sources.List import List
+from Tools.Directories import getRecordingFilename
+from .Debug import logger
+from .LoadPixmap import loadPixmap
+from .Downloader import MyGetPage, _headers, headers_ts, headersplain
+from .MyDeferredSemaphore import MyDeferredSemaphore
+from .__init__ import _
+from .Version import PLUGIN
+from .MoviePlayer import MoviePlayer
+from .EventView import EventView
+from .M3U8Parser import M3U8Parser
+from .MyJobManager import downloadManager, DownloadJob
+from .Menu import Menu
+from .ConfigInit import VIDEO_RESOLUTIONS, VIDEO_RESOLUTIONS_DICT
+from .Constants import plugindir, listempty, LIST_DATE, LIST_TIME, LIST_DURATION, LIST_CHANNEL, LIST_CHANNELPIXMAP, LIST_TOPIC, LIST_TITLE, LIST_DESCRIPTION, LIST_SIZE, LIST_ID, LIST_URL_VIDEO_LOW, LIST_URL_VIDEO, LIST_URL_VIDEO_HD, LIST_URL_WEBSITE
+
+
+class Movies(Screen):
+ def __init__(self, session, postdata):
+ self.postdata = postdata
+ Screen.__init__(self, session)
+ self.skinName = 'MediathekCockpit'
+ self.title = PLUGIN + ' - ' + _('Movies')
+ if "queries" in postdata:
+ self.title += " - " + _('Search results')
+
+ self['key_red'] = StaticText(_('Live'))
+ self['key_green'] = StaticText(_('Channels'))
+ self['key_yellow'] = StaticText(_('Download'))
+ self['key_blue'] = StaticText(_('Search'))
+
+ self['description'] = Label('')
+ self['date'] = Label('')
+ self['sresult'] = Label('')
+
+ self['list'] = List()
+ self['list'].style = 'movie_list'
+ self.list = []
+ self.currindex = 0
+ self.queryInfo = {}
+
+ self['actions'] = ActionMap(
+ ['MTC_Actions'],
+ {
+ 'ok': self.pressOk,
+ 'cancel': self.pressClose,
+ 'red': self.pressRed,
+ 'green': self.pressGreen,
+ 'yellow': self.pressYellow,
+ 'blue': self.pressBlue,
+ 'menu': self.pressMenu,
+ 'info': self.pressInfo
+ }
+ )
+ self['list'].onSelectionChanged.append(self.__SelectionChanged)
+ self.onLayoutFinish.append(self.__onLayoutFinish)
+ self.download = MyDeferredSemaphore(tokens=1)
+
+ def __onLayoutFinish(self):
+ logger.info("...")
+ self.postdata['offset'] = 0
+ self.downloadPage()
+
+ def downloadPage(self):
+ logger.info("...")
+ url = 'https://mediathekviewweb.de/api/query'
+ self.download.start(MyGetPage, url=url, method='POST', postdata=json.dumps(self.postdata), headers=headersplain).addCallback(self.downloadSucceeded).addErrback(self.downloadFailed)
+
+ def downloadSucceeded(self, result):
+ logger.info("...")
+ if result:
+ data = json.loads(result)
+ logger.debug("data: %s", data)
+ if not data.get('error'):
+ logger.debug("data available")
+ self.queryInfo = data['result']['queryInfo']
+ self.queryInfo['filmlisteTimestamp'] = datetime.fromtimestamp(int(self.queryInfo['filmlisteTimestamp'])).strftime('%d.%m.%Y %H:%M:%S')
+ logger.debug("queryInfo: %s", self.queryInfo)
+ results = data['result']['results']
+ logger.debug("results: %s", results)
+ for x in results:
+ logger.debug("x: %s")
+ tmp = listempty[:]
+ if 'timestamp' in x:
+ dtmp = datetime.fromtimestamp(int(x['timestamp'])).strftime('%d.%m.%Y %H:%M:%S')
+ tmp[LIST_DATE] = dtmp[0:10]
+ tmp[LIST_TIME] = dtmp[11:16]
+ if 'duration' in x:
+ tmp[LIST_DURATION] = str(timedelta(seconds=x['duration'] or 0))
+ if 'channel' in x:
+ name = x['channel'].encode('utf-8')
+ ptr = loadPixmap('%slogos/%s.png' % (plugindir, name.replace(' ', '').upper()))
+ tmp[LIST_CHANNEL] = name
+ tmp[LIST_CHANNELPIXMAP] = ptr
+ if 'topic' in x:
+ tmp[LIST_TOPIC] = x['topic'].encode('utf-8')
+ if 'title' in x:
+ tmp[LIST_TITLE] = x['title'].encode('utf-8')
+ if 'description' in x:
+ tmp[LIST_DESCRIPTION] = x['description'].encode('utf-8')
+ if 'size' in x:
+ tmp[LIST_SIZE] = int(x['size'] or 0) / (1024 * 1024)
+ if 'id' in x:
+ tmp[LIST_ID] = x['id']
+ if 'url_video_low' in x:
+ tmp[LIST_URL_VIDEO_LOW] = x['url_video_low']
+ if 'url_video' in x:
+ tmp[LIST_URL_VIDEO] = x['url_video']
+ if 'url_video_hd' in x:
+ tmp[LIST_URL_VIDEO_HD] = x['url_video_hd']
+ if 'url_website' in x:
+ tmp[LIST_URL_WEBSITE] = x['url_website']
+ logger.debug("tmp: %s", tmp)
+ self.list.append(tmp)
+
+ math = min(self.postdata['offset'] + self.queryInfo['resultCount'], self.queryInfo['totalResults'])
+ if self.queryInfo['totalResults'] != math:
+ addtmp = listempty[:]
+ addtmp[LIST_CHANNEL] = '>>>'
+ addtmp[LIST_TOPIC] = _('Next page')
+ addtmp[LIST_TITLE] = _('Select')
+ addtmp[LIST_DATE] = '>>>'
+ logger.debug("addtmp: %s", addtmp)
+ self.list.append(addtmp)
+ # logger.info("list: %s", self.list)
+ self["list"].setList(self.list)
+ self['list'].changed((self['list'].CHANGED_ALL,))
+ logger.debug("setIndex: %s", self.currindex)
+ self['list'].setIndex(self.currindex)
+ self.set_sresult()
+
+ def downloadFailed(self, failure, *args):
+ logger.info("...")
+ error_message = str(failure.getErrorMessage())
+ if not error_message:
+ error_message = str(failure)
+ logger.error("%s, %s", error_message, str(args))
+ if 'CancelledError' not in error_message:
+ self.session.open(MessageBox, error_message, MessageBox.TYPE_INFO, timeout=10, title=_('Download failed'))
+
+ def __SelectionChanged(self):
+ logger.info("...")
+ self['description'].setText('')
+ self['date'].setText('')
+ self.curr = self['list'].getCurrent()
+ if self.curr and self.curr[LIST_CHANNEL] != '>>>':
+ self['description'].setText(self.curr[LIST_DESCRIPTION])
+ # size = self.curr[LIST_SIZE]
+ # size_text = '%d MB' % size if size else ''
+ resolutions = self.getVideoResolutions()
+ self['date'].setText(
+ '%s: %s %s\n%s: %s\n%s: %s' % (
+ _("Broadcast time"), self.curr[LIST_DATE], self.curr[LIST_TIME],
+ _('Duration'), self.curr[LIST_DURATION],
+ _("Quality"), ", ".join(resolutions)
+ )
+ )
+
+ def pressOk(self):
+ if self.curr:
+ if self.curr[LIST_CHANNEL] == '>>>':
+ self.currindex = self["list"].getIndex()
+ logger.debug("getIndex: %s", self.currindex)
+ self.postdata['offset'] += config.plugins.mediathekcockpit.size.value
+ self["list"].list.remove(self.curr)
+ del self.list[len(self.list) - 1]
+ self.downloadPage()
+ else:
+ val = int(config.plugins.mediathekcockpit.movie_resolution.value)
+ url, _resolution = self.getVideoUrl(self.curr, val)
+ if url:
+ url = url.encode('utf-8')
+ if self.curr[LIST_TOPIC].replace('&', 'und').upper() not in self.curr[LIST_TITLE].replace('&', 'und').upper():
+ self.name = self.curr[LIST_TOPIC] + ' ' + self.curr[LIST_TITLE]
+ else:
+ self.name = self.curr[LIST_TITLE]
+
+ if '.m3u8' in url:
+ self.download.start(MyGetPage, url=url, headers=_headers, location=True).addCallback(self.m3u8checkback, self.playm3u8).addErrback(self.downloadFailed)
+ elif url.endswith(('mp4', 'flv')):
+ self.playm3u8(url)
+
+ def playm3u8(self, url, suburi=None):
+ logger.info("...")
+ sref = eServiceReference(eServiceReference.idGST, 0, url.encode('utf-8'))
+ if self.curr[LIST_CHANNEL].upper() not in self.name.upper():
+ sref.setName('%s: %s' % (self.curr[LIST_CHANNEL].encode('utf-8'), self.name.encode('utf-8')))
+ else:
+ sref.setName('%s' % self.name.encode('utf-8'))
+ if suburi:
+ sref.setSuburi(suburi.encode('utf-8'))
+ self.session.open(MoviePlayer, sref, menuval=None, infoval=(EventView, self.curr))
+
+ def m3u8checkback(self, result, play):
+ logger.info("...")
+ m3u8 = result[0]
+ url = result[1]
+ if '#EXTM3U' in m3u8:
+ m3u8res = M3U8Parser(m3u8, url)
+ if m3u8res.extxmedia_play():
+ play(url)
+ elif m3u8res.extxmedia_parse():
+ urlval, suburi = m3u8res.parser_all()
+ if urlval:
+ play(urlval, suburi)
+
+ def checkVideoUrl(self, was):
+ return self.curr[was] and self.curr[was].startswith('http')
+
+ def getVideoResolutions(self):
+ resolutions = []
+ if self.checkVideoUrl(LIST_URL_VIDEO_LOW):
+ resolutions.append(_('Low'))
+ if self.checkVideoUrl(LIST_URL_VIDEO):
+ resolutions.append(_('Medium'))
+ if self.checkVideoUrl(LIST_URL_VIDEO_HD):
+ resolutions.append(_('High'))
+ return resolutions
+
+ def getVideoUrl(self, curr, val):
+ url = ""
+ resolution_text = ""
+ resolutions = [val] + VIDEO_RESOLUTIONS
+ for resolution in resolutions:
+ if curr[resolution] and curr[resolution].startswith('http'):
+ url = curr[resolution]
+ resolution_text = VIDEO_RESOLUTIONS_DICT[resolution]
+ break
+ return url, resolution_text
+
+ def set_sresult(self):
+ logger.info("...")
+ if self.queryInfo:
+ math = min(self.postdata['offset'] + self.queryInfo['resultCount'], self.queryInfo['totalResults'])
+ self['sresult'].setText('%s: %s/%s\n%s: %s ms\n%s: %s' % (
+ _('Entries'), math, str(self.queryInfo['totalResults']),
+ _('Load time'), str(self.queryInfo['searchEngineTime']),
+ _('From'), str(self.queryInfo['filmlisteTimestamp']))
+ )
+
+ def pressRed(self):
+ self.close("streams")
+
+ def pressGreen(self):
+ self.close("channels")
+
+ def pressYellow(self):
+ logger.info("...")
+ path = config.plugins.mediathekcockpit.moviesavedir.value
+ if os.path.exists(path):
+ url, resolution = self.getVideoUrl(self.curr, int(config.plugins.mediathekcockpit.movie_resolution.value))
+ if self.curr[LIST_TOPIC].replace('&', 'und').upper() not in self.curr[LIST_TITLE].replace('&', 'und').upper():
+ name = self.curr[LIST_TOPIC].encode('utf-8') + ' ' + self.curr[LIST_TITLE].encode('utf-8')
+ else:
+ name = self.curr[LIST_TITLE].encode('utf-8')
+ description = self.curr[LIST_DESCRIPTION].encode('utf-8')
+ # datestring = datetime.now().strftime('%Y%m%d - ')
+ p = re.compile('[.:,!/]')
+ disname = name
+ name = p.sub('', name)
+ url = url.encode('utf-8')
+ formatval = '.mp4'
+ pos = url.rfind('.')
+ if pos != -1:
+ formatval = url[pos:]
+ recordfilename = getRecordingFilename(name, path.encode('utf-8'))
+ downloadManager.AddJob(DownloadJob(url, recordfilename + formatval, disname, description, headers_ts))
+ self.session.open(
+ MessageBox, '%s:\n\n%s\n\n%s: %s' % (
+ _('Download added'),
+ disname,
+ _('Video resolution'), resolution,
+ ),
+ type=MessageBox.TYPE_INFO,
+ timeout=4
+ )
+ else:
+ self.session.open(MessageBox, _("Movie directory does not exist") + ":\n\n" + path, type=MessageBox.TYPE_ERROR)
+
+ def pressBlue(self):
+ self.close("search")
+
+ def pressMenu(self):
+ Menu(self.session, self.postdata)
+
+ def pressInfo(self):
+ if self.curr and self.curr[LIST_CHANNEL] != '>>>':
+ self.session.open(EventView, self.curr)
+
+ def pressClose(self):
+ logger.info("...")
+ self.close("exit")
diff --git a/src/MyDeferredSemaphore.py b/src/MyDeferredSemaphore.py
index 88ce78a..90fcdc9 100644
--- a/src/MyDeferredSemaphore.py
+++ b/src/MyDeferredSemaphore.py
@@ -1,3 +1,24 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
from twisted.internet import reactor
from twisted.internet.defer import Deferred, DeferredSemaphore
from .Debug import logger
@@ -12,7 +33,7 @@ def MyCallLater(acallable, *args, **kwargs):
return reactor.callLater(0, acallable, *args, **kwargs)
-def MydeferLater(acallable, *args, **kw):
+def MyDeferLater(acallable, *args, **kw):
logger.info("...")
def deferLaterCancel(_deferred):
@@ -49,7 +70,7 @@ def run(self, *args, **kwargs):
def rundeferLater(self, _ltime, *args, **kwargs):
logger.info("...")
- return MydeferLater(self._ds.run, *args, **kwargs)
+ return MyDeferLater(self._ds.run, *args, **kwargs)
def start(self, *args, **kwargs):
logger.info("...")
diff --git a/src/MyJobManager.py b/src/MyJobManager.py
old mode 100644
new mode 100755
index 2540ee1..9670e0f
--- a/src/MyJobManager.py
+++ b/src/MyJobManager.py
@@ -1,3 +1,24 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
import os
from Components.Task import Task, Job, JobManager
from Downloader import mydownloadWithProgress
@@ -7,16 +28,17 @@
from Plugins.SystemPlugins.CacheCockpit.FileManager import FileManager
cachecockpit = True
except Exception:
- logger.Error("import error")
+ logger.error("CacheCockpit import error")
cachecockpit = False
class DownloadJob(Job):
- def __init__(self, url, file, title, description, headers):
+ def __init__(self, url, file_name, title, description, headers):
logger.info("...")
Job.__init__(self, title)
- DownloadTask(self, url, file, description, headers)
+ self.file_name = file_name
+ DownloadTask(self, url, file_name, description, headers)
def gettotalbytes(self):
logger.info("...")
@@ -36,14 +58,14 @@ def getrecvbytes(self):
class DownloadTask(Task):
totalbytes = recvbytes = 0
- def __init__(self, job, url, fileName, description, headers):
- logger.info("job: %s, url: %s, fileName: %s, description: %s", job, url, fileName, description)
+ def __init__(self, job, url, file_name, description, headers):
+ logger.info("job: %s, url: %s, file_name: %s, description: %s", job, url, file_name, description)
self.description = description
Task.__init__(self, job, 'download task')
self.headers = headers
self.end = 100
self.url = url
- self.local = fileName
+ self.local = file_name
self.download = None
def prepare(self):
@@ -68,8 +90,7 @@ def http_progress(self, recvbytes, totalbytes):
self.recvbytes, self.totalbytes = recvbytes, totalbytes
def response_headers(self, response_headers):
- logger.info("...")
- print(response_headers)
+ logger.info("response_headers: %s", response_headers)
def http_finished(self, _string):
logger.info("...")
diff --git a/src/Search.py b/src/Search.py
new file mode 100644
index 0000000..b0df10b
--- /dev/null
+++ b/src/Search.py
@@ -0,0 +1,50 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Screens.VirtualKeyBoard import VirtualKeyBoard
+from .Debug import logger
+from .__init__ import _
+
+
+class Search():
+ def __init__(self, session, callback):
+ self.session = session
+ self.callback = callback
+
+ def openKeyboard(self, query, postdata):
+ logger.info("query: %s", query)
+ self.postdata = postdata
+ self.session.openWithCallback(self.resulttext, VirtualKeyBoard, title=_('Enter search text'), text=query)
+
+ def resulttext(self, result=None):
+ logger.info("result: %s", result)
+ if result:
+ self.search(result)
+
+ def search(self, name='', fields=None):
+ if fields is None:
+ fields = ['topic', 'title']
+ logger.info("name: %s, fields: %s", name, fields)
+ if name:
+ self.postdata['offset'] = 0
+ self.postdata['queries'] = list()
+ self.postdata['queries'].append({'fields': fields, 'query': name.encode('utf-8')})
+ self.callback(self.postdata)
diff --git a/src/SearchUtils.py b/src/SearchUtils.py
deleted file mode 100644
index 6667b51..0000000
--- a/src/SearchUtils.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import json
-from Tools.Directories import pathExists
-from .Debug import logger
-from .Constants import enigma2configdir, listfields, listsearchquerys
-from .__init__ import _
-
-
-searchquerysfile = enigma2configdir + 'mv_searchquerys.json'
-
-
-def convert_searchquerys(data):
- logger.info("...")
- name = ''
- channel = []
- fields = ''
- for queriesres in data:
- if queriesres.get('fields') == listfields['channel']:
- channel.append(queriesres['query'].encode('utf-8'))
- elif queriesres.get('fields') != listfields['channel']:
- name = queriesres['query'].encode('utf-8')
- for x in queriesres['fields']:
- fields += _(x.encode('utf-8')) + ' '
-
- if not channel:
- channel = [_('All')]
- return (name, fields, (', ').join(channel), data)
-
-
-def read_searchquerys():
- logger.info("...")
- searchquerys = []
- if pathExists(searchquerysfile):
- with open(searchquerysfile) as (data_file):
- data = json.load(data_file)
- for query in data:
- searchquerys.append(convert_searchquerys(query))
- return searchquerys
-
-
-def write_searchquerys(searchquerys):
- logger.info("...")
- with open(searchquerysfile, 'w') as (fp):
- result = []
- for value in searchquerys:
- result.append(value[listsearchquerys['queries']])
- json.dump(result, fp, encoding='utf-8', indent=2, sort_keys=True)
diff --git a/src/Settings.py b/src/Settings.py
new file mode 100644
index 0000000..533cb8d
--- /dev/null
+++ b/src/Settings.py
@@ -0,0 +1,88 @@
+#!/usr/bin/python
+# coding=utf-8
+#
+# Copyright (C) 2018-2024 by dream-alpha
+#
+# In case of reuse of this source code please do not remove this copyright.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# For more information on the GNU General Public License see:
+# .
+
+
+from Screens.Screen import Screen
+from Components.ActionMap import ActionMap
+from Components.Sources.StaticText import StaticText
+from Components.ConfigList import ConfigListScreen
+from Components.config import config, configfile, getConfigListEntry, KEY_OK
+from Tools.Directories import pathExists
+from .DirBrowser import DirBrowser
+from .__init__ import _
+from .Debug import logger
+
+
+class Settings(Screen, ConfigListScreen):
+
+ def __init__(self, session, postdata):
+ logger.info("...")
+ self.postdata = postdata
+ Screen.__init__(self, session, windowTitle=_('MediathekCockpit') + ' - ' + _('Setup'))
+ ConfigListScreen.__init__(self, [], session=session)
+ self.skinName = 'MTCSetup'
+ self['key_red'] = StaticText(_('Cancel'))
+ self['key_green'] = StaticText(_('Save'))
+ self['actions'] = ActionMap(
+ ['SetupActions', 'ColorActions'],
+ {
+ 'cancel': self.keyCancel,
+ 'save': self.keySave
+ }
+ )
+ self._createSetup()
+
+ def _createSetup(self):
+ logger.info("...")
+ self['config'].setList(
+ [
+ getConfigListEntry(_('Entries per page'), config.plugins.mediathekcockpit.size),
+ getConfigListEntry(_('Show future movies'), config.plugins.mediathekcockpit.future),
+ getConfigListEntry(_('Video resolution'), config.plugins.mediathekcockpit.movie_resolution),
+ getConfigListEntry(_('Behavior when movie stopped'), config.plugins.mediathekcockpit.askstopmovie),
+ getConfigListEntry(_('Movie path'), config.plugins.mediathekcockpit.moviesavedir)
+ ]
+ )
+
+ def keyOK(self):
+ logger.info("...")
+ curr = self['config'].getCurrent()
+ if curr and curr[1] == config.plugins.mediathekcockpit.moviesavedir:
+ self.session.openWithCallback(self.LocationBoxCallback, DirBrowser, config.plugins.mediathekcockpit.moviesavedir.value)
+ else:
+ self['config'].handleKey(KEY_OK)
+
+ def LocationBoxCallback(self, res):
+ logger.info("res: %s", res)
+ if res:
+ if pathExists(res):
+ config.plugins.mediathekcockpit.moviesavedir.value = res
+
+ def keySave(self):
+ logger.info("...")
+ if self['config'].isChanged():
+ config.plugins.mediathekcockpit.save()
+ configfile.save()
+ self.postdata['size'] = int(config.plugins.mediathekcockpit.size.value)
+ self.postdata['future'] = config.plugins.mediathekcockpit.future.value
+ self.postdata['offset'] = 0
+ self.close(self.postdata)
+ else:
+ self.close(False)
diff --git a/src/Version.py b/src/Version.py
index 5ed3e52..c0e0ce5 100644
--- a/src/Version.py
+++ b/src/Version.py
@@ -21,6 +21,6 @@
PLUGIN = "MediathekCockpit"
ID = "MTC"
-VERSION = "0.1.2"
+VERSION = "0.2.5"
COPYRIGHT = "2018-2024 by dream-alpha"
LICENSE = "This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version."
diff --git a/src/keymap.xml b/src/keymap.xml
index 6e9b9e2..dc2e03a 100644
--- a/src/keymap.xml
+++ b/src/keymap.xml
@@ -1,5 +1,5 @@
-