summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLeo Moll <leo.moll@dtms.de>2018-01-22 01:01:34 +0100
committerMartijn Kaijser <martijn@xbmc.org>2018-01-22 16:58:17 +0100
commita55a6475b943a804fd76b5181b7d82968c2ea096 (patch)
treec763016250990d9699a589972e5234b54745845a
parenta266c01d2aa6e4e54ccfd37e79170fbb7ebc2a0a (diff)
[plugin.video.mediathekview] 0.4.2
-rw-r--r--plugin.video.mediathekview/README.md35
-rw-r--r--plugin.video.mediathekview/addon.py25
-rw-r--r--plugin.video.mediathekview/addon.xml43
-rwxr-xr-xplugin.video.mediathekview/mvupdate2
-rw-r--r--plugin.video.mediathekview/resources/icons/3sat-m.pngbin0 -> 9180 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/ard-m.pngbin0 -> 8273 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/arte.de-m.pngbin0 -> 4795 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/arte.fr-m.pngbin0 -> 4795 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/br-m.pngbin0 -> 12519 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/default-m.pngbin0 -> 6741 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/dw-m.pngbin0 -> 12946 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/hr-m.pngbin0 -> 8422 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/kika-m.pngbin0 -> 9958 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/mdr-m.pngbin0 -> 5710 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/ndr-m.pngbin0 -> 7541 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/orf-m.pngbin0 -> 4865 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/phoenix-m.pngbin0 -> 3998 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/rbb-m.pngbin0 -> 4620 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/sr-m.pngbin0 -> 9989 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/srf-m.pngbin0 -> 5658 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/srf.podcast-m.pngbin0 -> 5658 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/swr-m.pngbin0 -> 10006 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/wdr-m.pngbin0 -> 8160 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/zdf-m.pngbin0 -> 7184 bytes
-rw-r--r--plugin.video.mediathekview/resources/icons/zdf-tivi-m.pngbin0 -> 7184 bytes
-rw-r--r--plugin.video.mediathekview/resources/language/resource.language.de_de/strings.po22
-rw-r--r--plugin.video.mediathekview/resources/language/resource.language.en_gb/strings.po16
-rw-r--r--plugin.video.mediathekview/resources/language/resource.language.it_it/strings.po20
-rw-r--r--plugin.video.mediathekview/resources/lib/base/Logger.py2
-rw-r--r--plugin.video.mediathekview/resources/lib/channelui.py22
-rw-r--r--plugin.video.mediathekview/resources/lib/exceptions.py3
-rw-r--r--plugin.video.mediathekview/resources/lib/filmui.py13
-rw-r--r--plugin.video.mediathekview/resources/lib/initialui.py18
-rw-r--r--plugin.video.mediathekview/resources/lib/kodi/KodiAddon.py52
-rw-r--r--plugin.video.mediathekview/resources/lib/kodi/KodiUI.py40
-rw-r--r--plugin.video.mediathekview/resources/lib/mvupdate.py6
-rw-r--r--plugin.video.mediathekview/resources/lib/mvutils.py56
-rw-r--r--plugin.video.mediathekview/resources/lib/notifier.py15
-rw-r--r--plugin.video.mediathekview/resources/lib/settings.py2
-rw-r--r--plugin.video.mediathekview/resources/lib/showui.py39
-rw-r--r--plugin.video.mediathekview/resources/lib/storemysql.py136
-rw-r--r--plugin.video.mediathekview/resources/lib/storesqlite.py129
-rw-r--r--plugin.video.mediathekview/resources/lib/ttml2srt.py6
-rw-r--r--plugin.video.mediathekview/resources/lib/updater.py48
-rw-r--r--plugin.video.mediathekview/resources/settings.xml7
-rw-r--r--plugin.video.mediathekview/service.py28
46 files changed, 528 insertions, 257 deletions
diff --git a/plugin.video.mediathekview/README.md b/plugin.video.mediathekview/README.md
index 485d456..ab8fee5 100644
--- a/plugin.video.mediathekview/README.md
+++ b/plugin.video.mediathekview/README.md
@@ -80,9 +80,9 @@ Alternativ-Konfigurationen
--------------------------
Ist das Kodi-System zu langsam um eine eigene Datenbank zu verwalten
-(z.B. Raspberry PI mit sehr langsamer SD-Karte) oder fehlt das Programm
-'xz', so besteht die Möglichkeit das Addon auch mit einer externen
-Datenbank (MySQL) zu nutzen.
+(z.B. Raspberry PI mit sehr langsamer SD-Karte) oder soll die Datenbank mit
+mehreren Kodi Systemen geteilt werden, so besteht die Möglichkeit das Addon
+auch mit einem externen Datenbank-Server (MySQL oder MariaDB) zu nutzen.
Da viele Kodi-Nutzer über ein eigenes NAS-System verfügen um ihre Medien
dem Media-Center zur Verfügung zu stellen, eignet sich dieses in der Regel
@@ -113,12 +113,14 @@ folgende zwei Bibliotheken zur Verfügung stehen, sowie das Entpackprogramm
'xz' (optional):
* ijson
+* defusedxml
* mysql-connector
Die Installation dieser Bibliotheken erfolgt durch Eingabe folgender Befehle:
````
pip install ijson
+pip install defusedxml
pip install mysql-connector==2.1.4
````
@@ -130,7 +132,7 @@ Dies kann entweder durch Herunterladen und Entpacken der Addon-ZIP-Datei
erfolgen oder durch Klonen des Addon-Quellcode-Repositories mittels `git`
````
-git clone git@github.com:mediathekview/plugin.video.mediathekview.git
+git clone https://github.com/mediathekview/plugin.video.mediathekview.git
````
Durch Angabe des Parameters `-h` bzw. `-h` hinter dem Datenbanktyp, gibt
@@ -224,8 +226,9 @@ Alternate Configurations
------------------------
If the Kodi system is too slow to manage its own database (e.g. Raspberry PI
-with a very slow SD card) or if the program 'xz' is missing, it is also
-possible to use the addon with an external database (MySQL).
+with a very slow SD card) or you want to share the database across multiple
+Kodi instances, it is also possible to use the addon with an external database
+(MySQL or MariaDB).
Since many Kodi users have their own NAS system to make their media available
to the media center, this is usually also suitable as a MySQL/MariaDB database
@@ -236,9 +239,6 @@ created automatically if it does not yet exist on the database server. However,
the specified database user must also have sufficient user rights in order to
do this.
-When you have a running MySQL server avaible, you have only to create the
-database by running the SQL script `resources/sql/filmliste-mysql-v1.sql`.
-
The connection to the database can be configured in the addon settings in
the "Database Settings" section.
@@ -255,12 +255,14 @@ target system in order to execute the commandline update process. Additionally
the following python libraries are required:
* ijson
+* defusedxml
* mysql-connector
The required libraries can be installed via pip:
````
pip install ijson
+pip install defusedxml
pip install mysql-connector==2.1.4
````
@@ -272,7 +274,7 @@ This can be either done by downloading and unpacking the addon archive or
by cloning the source repository with `git`
````
-git clone git@github.com:mediathekview/plugin.video.mediathekview.git
+git clone https://github.com/mediathekview/plugin.video.mediathekview.git
````
By specifying the option `-h` itself or after the requested database type,
@@ -355,9 +357,10 @@ possibile fare una dichiarazione finale di compatibilità.
Configurazioni alternative
--------------------------
-Se il sistema Kodi è troppo lento per gestire il proprio database (ad es.
-Raspberry PI con una scheda SD molto lenta) o se manca il programma 'xz',
-è anche possibile utilizzare l'addon con un database esterno (MySQL).
+Se il sistema Kodi è troppo lento per gestire il database interno (ad es.
+Raspberry PI con una scheda SD molto lenta) o se si desidera condividere il
+database con altri sistemi Kodi, è anche possibile utilizzare l'addon con un
+server database esterno (MySQL o MariaDB).
Dal momento che molti utenti Kodi hanno il proprio sistema NAS per rendere i
loro contenuti mediali disponibili al media center, questo è di solito anche
@@ -367,7 +370,7 @@ sistemi operativi NAS offrono l'installazione di un tale database.
Se l' addon è configurato per utilizzare un database MySQL/MariaDB, il database
verrà creato automaticamente se non esiste ancora sul database server.
Tuttavia, anche l'utente del database specificato deve avere i diritti
-necessari.
+necessari alla creazione di un database.
Il collegamento al database può essere effettuato nelle impostazioni
dell'addon nella sezione "Impostazioni Banca Dati".
@@ -387,12 +390,14 @@ interprete python2, il programma di decompressione 'xz' e le seguenti
librerie python:
* ijson
+* defusedxml
* mysql-connector
QUeste potranno essere istallate mediante il programma pip:
````
pip install ijson
+pip install defusedxml
pip install mysql-connector==2.1.4
````
@@ -404,7 +409,7 @@ Questo sarà possibile sia scaricando l'archivio dell'addon che dovrà essere
spacchettato in loco o mediante clonaggio dai sorgenti mediante `git`
````
-git clone git@github.com:mediathekview/plugin.video.mediathekview.git
+git clone https://github.com/mediathekview/plugin.video.mediathekview.git
````
Specificando l'opzione `-h` a se stante o a tergo del tipo di database da
diff --git a/plugin.video.mediathekview/addon.py b/plugin.video.mediathekview/addon.py
index 64e925d..d4f483f 100644
--- a/plugin.video.mediathekview/addon.py
+++ b/plugin.video.mediathekview/addon.py
@@ -30,6 +30,8 @@ from __future__ import unicode_literals # ,absolute_import, division
import os,re,sys,urlparse,datetime
import xbmcplugin,xbmcgui,xbmcvfs
+import resources.lib.mvutils as mvutils
+
from contextlib import closing
from resources.lib.kodi.KodiAddon import KodiPlugin
@@ -190,7 +192,7 @@ class MediathekView( KodiPlugin ):
dirname = self.settings.downloadpath + showname + '/'
episode = 1
if xbmcvfs.exists( dirname ):
- ( dirs, epfiles, ) = xbmcvfs.listdir( dirname )
+ ( _, epfiles, ) = xbmcvfs.listdir( dirname )
for epfile in epfiles:
match = re.search( '^.* [eE][pP]([0-9]*)\.[^/]*$', epfile )
if match and len( match.groups() ) > 0:
@@ -211,22 +213,21 @@ class MediathekView( KodiPlugin ):
bgd.Create( self.language( 30974 ), fileepi + extension )
try:
bgd.Update( 0 )
- result = mvutils.url_retrieve_vfs( videourl, movname, bgd.UrlRetrieveHook )
+ mvutils.url_retrieve_vfs( videourl, movname, bgd.UrlRetrieveHook )
bgd.Close()
- if result is not None:
- self.notifier.ShowNotification( self.language( 30960 ), self.language( 30976 ).format( videourl ) )
+ self.notifier.ShowNotification( 30960, self.language( 30976 ).format( videourl ) )
except Exception as err:
bgd.Close()
self.error( 'Failure downloading {}: {}', videourl, err )
- self.notifier.ShowError( self.language( 30952 ), self.language( 30975 ).format( videourl, err ) )
+ self.notifier.ShowError( 30952, self.language( 30975 ).format( videourl, err ) )
# download subtitles
if film.url_sub:
bgd = KodiBGDialog()
- bgd.Create( self.language( 30978 ), fileepi + u'.ttml' )
+ bgd.Create( 30978, fileepi + u'.ttml' )
try:
bgd.Update( 0 )
- result = mvutils.url_retrieve_vfs( film.url_sub, ttmname, bgd.UrlRetrieveHook )
+ mvutils.url_retrieve_vfs( film.url_sub, ttmname, bgd.UrlRetrieveHook )
try:
ttml2srt( xbmcvfs.File( ttmname, 'r' ), xbmcvfs.File( srtname, 'w' ) )
except Exception as err:
@@ -239,7 +240,7 @@ class MediathekView( KodiPlugin ):
# create NFO Files
self._make_nfo_files( film, episode, dirname, nfoname, videourl )
else:
- self.notifier.ShowError( self.language( 30952 ), self.language( 30958 ) )
+ self.notifier.ShowError( 30952, 30958 )
def doEnqueueFilm( self, filmid ):
self.info( 'Enqueue {}', filmid )
@@ -296,18 +297,18 @@ class MediathekView( KodiPlugin ):
channel = self.args.get( 'channel', [0] )
self.db.GetRecents( channel[0], FilmUI( self ) )
elif mode[0] == 'recentchannels':
- self.db.GetRecentChannels( ChannelUI( self.addon_handle, nextdir = 'recent' ) )
+ self.db.GetRecentChannels( ChannelUI( self, nextdir = 'recent' ) )
elif mode[0] == 'channels':
- self.db.GetChannels( ChannelUI( self.addon_handle ) )
+ self.db.GetChannels( ChannelUI( self, nextdir = 'shows' ) )
elif mode[0] == 'action-dbinfo':
self.showDbInfo()
elif mode[0] == 'initial':
channel = self.args.get( 'channel', [0] )
- self.db.GetInitials( channel[0], InitialUI( self.addon_handle ) )
+ self.db.GetInitials( channel[0], InitialUI( self ) )
elif mode[0] == 'shows':
channel = self.args.get( 'channel', [0] )
initial = self.args.get( 'initial', [None] )
- self.db.GetShows( channel[0], initial[0], ShowUI( self.addon_handle ) )
+ self.db.GetShows( channel[0], initial[0], ShowUI( self ) )
elif mode[0] == 'films':
show = self.args.get( 'show', [0] )
self.db.GetFilms( show[0], FilmUI( self ) )
diff --git a/plugin.video.mediathekview/addon.xml b/plugin.video.mediathekview/addon.xml
index 596fe4b..dabb8ca 100644
--- a/plugin.video.mediathekview/addon.xml
+++ b/plugin.video.mediathekview/addon.xml
@@ -1,13 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="plugin.video.mediathekview"
name="MediathekView"
- version="0.4.0"
+ version="0.4.2"
provider-name="MediathekView.de, Leo Moll">
<requires>
<import addon="xbmc.python" version="2.25.0"/>
<import addon="script.module.requests" version="2.12.4"/>
<import addon="script.module.myconnpy" version="1.1.7"/>
<import addon="script.module.ijson" version="2.3"/>
+ <import addon="script.module.defusedxml" version="0.5.0"/>
</requires>
<extension
point="xbmc.python.pluginsource"
@@ -25,32 +26,28 @@
<description lang="de_DE">Ermöglicht den Zugriff auf fast alle deutschen Mediatheken der öffentlich Rechtlichen basierend auf der Datenbank von MediathekView.de</description>
<description lang="en_GB">Gives access to most video-platforms from German public service broadcasters using the database of MediathekView.de</description>
<description lang="it_IT">Fornisce l'accesso a gran parte delle piattaforme video operate dalle emittenti pubbliche tedesche usando la banca dati di MediathekView.de</description>
- <news>v0.4.0 (2018-01-14):
+ <news>v0.4.2 (2018-01-20):
+- Senderlogos werden nun in verschiedenen Situationen angezeigt
+- Fehler bei Auflisten der Filme in "Alle Sendungen" aus 0.4.1 behoben
+- Fehler bei Datenbankaktualisierung behoben
+- Herunterladen von zufälligem Server
+v0.4.1 (2018-01-20):
+- Alphabetische Anzeige in Listen ist nun nicht mehr Groß-Klein-Sensitiv
+- Download von Inhalten funktioniert wieder
+- "Suchen nach Titel und Beschreibung" mit MySQL funktioniert wieder
+- Freitextsuche kommt nun auch mit " und ' klar
+- In seltenen Fällen konnte es bei der Installation oder das Update des Addons zu einem Fehler kommen, der die Datenbank korrumpiert hat.
+- Das Programm mvupdate hat nicht funktioniert
+- In "Alle Sendungen nach Sender" kommt man nun direkt nach der Auswahl des Senders in die Filmliste
+- Die Anzahl Tage die ein Film als "Vor kurzem hinzugefügt" gilt, ist nun einstellbar
+- Es ist einstellbar, ob "Vor kurzem hinzugefügt" das Sendedatum oder das Datum des Hinzufügens in der Datenbank berücksichtigt
+- Anzahl der neuen Filme in "Vor Kurzem hinzugefügt nach Sender" stimmt nun
+- Übersetzungsfehler korrigiert
+v0.4.0 (2018-01-14):
- Android und Windows Kompatibilität da kein externer Entpacker mehr benötigt wird
- Der Datenbanktreiber für MySQL kann nun die Datenbank selbsttätig anlegen
- Im Datenbanktreiber für MySQL kann nun auch die Portnummer angegeben werden
- Konfigurierbare Begrenzung der Suchergebnisse
-v0.3.5 (2018-01-11):
-- Anpassungen an die Kodi-Addon Regeln
-v0.3.4 (2018-01-11):
-- Die Suche sucht nun auch im Sendungs-Name
-- "Vor Kurzem hinzugefügt" nach Sendern durchsuchen
-- Deutsche und Italienische Übersetzung funktionieren nun
-v0.3.3 (2018-01-09):
-- Auflösung kann nun beim Download ausgewählt werden
-- Kommandozeilenaktualisierer wurde implementiert
-- Fehler in der Generierung von tvshow.nfo behoben
-- Die README Datei enthält nun alle Sprachen
-v0.3.2 (2018-01-08):
-- Dateien mit Umlauten im Namen konnten nicht heruntergeladen werden
-- Herunterladen bei nicht lokalem Download-Verzeichnis erfolgt nun ohne Zwischenspeicherung
-- NFO Dateien enthalten mehr Daten
-v0.3.1 (2018-01-07):
-- Herunterladen ist nun auch in nicht lokalen (VFS) Download-Verzeichnissen möglich
-- Behebung eines sehr seltenen Fehlers der zu einem ENdlos-Update führen konnte
-v0.3.0 (2018-01-07):
-- Neue Funktion zum Herunterladen von Videos mit Untertiteln und Metadaten (NFO-Dateien)
-- Behebung eines Fehlers der das Abspeichern der Untertitel URL in der Datenbank verhinderte
</news>
<platform>all</platform>
<language>de fr</language>
diff --git a/plugin.video.mediathekview/mvupdate b/plugin.video.mediathekview/mvupdate
index 56aedc0..ab0657b 100755
--- a/plugin.video.mediathekview/mvupdate
+++ b/plugin.video.mediathekview/mvupdate
@@ -25,7 +25,7 @@
# -- Imports ------------------------------------------------
from __future__ import unicode_literals
-from classes.mvupdate import UpdateApp
+from resources.lib.mvupdate import UpdateApp
# -- Main Code ----------------------------------------------
if __name__ == '__main__':
diff --git a/plugin.video.mediathekview/resources/icons/3sat-m.png b/plugin.video.mediathekview/resources/icons/3sat-m.png
new file mode 100644
index 0000000..3c34822
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/3sat-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/ard-m.png b/plugin.video.mediathekview/resources/icons/ard-m.png
new file mode 100644
index 0000000..9053e76
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/ard-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/arte.de-m.png b/plugin.video.mediathekview/resources/icons/arte.de-m.png
new file mode 100644
index 0000000..2d558e7
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/arte.de-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/arte.fr-m.png b/plugin.video.mediathekview/resources/icons/arte.fr-m.png
new file mode 100644
index 0000000..2d558e7
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/arte.fr-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/br-m.png b/plugin.video.mediathekview/resources/icons/br-m.png
new file mode 100644
index 0000000..d8123a8
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/br-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/default-m.png b/plugin.video.mediathekview/resources/icons/default-m.png
new file mode 100644
index 0000000..390d91a
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/default-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/dw-m.png b/plugin.video.mediathekview/resources/icons/dw-m.png
new file mode 100644
index 0000000..bd2d979
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/dw-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/hr-m.png b/plugin.video.mediathekview/resources/icons/hr-m.png
new file mode 100644
index 0000000..20ef6aa
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/hr-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/kika-m.png b/plugin.video.mediathekview/resources/icons/kika-m.png
new file mode 100644
index 0000000..02c538b
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/kika-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/mdr-m.png b/plugin.video.mediathekview/resources/icons/mdr-m.png
new file mode 100644
index 0000000..62c9d2d
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/mdr-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/ndr-m.png b/plugin.video.mediathekview/resources/icons/ndr-m.png
new file mode 100644
index 0000000..aef0d47
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/ndr-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/orf-m.png b/plugin.video.mediathekview/resources/icons/orf-m.png
new file mode 100644
index 0000000..7d9645d
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/orf-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/phoenix-m.png b/plugin.video.mediathekview/resources/icons/phoenix-m.png
new file mode 100644
index 0000000..4bd530a
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/phoenix-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/rbb-m.png b/plugin.video.mediathekview/resources/icons/rbb-m.png
new file mode 100644
index 0000000..138e6b6
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/rbb-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/sr-m.png b/plugin.video.mediathekview/resources/icons/sr-m.png
new file mode 100644
index 0000000..f46dcb4
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/sr-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/srf-m.png b/plugin.video.mediathekview/resources/icons/srf-m.png
new file mode 100644
index 0000000..d1049bf
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/srf-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/srf.podcast-m.png b/plugin.video.mediathekview/resources/icons/srf.podcast-m.png
new file mode 100644
index 0000000..d1049bf
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/srf.podcast-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/swr-m.png b/plugin.video.mediathekview/resources/icons/swr-m.png
new file mode 100644
index 0000000..c631f5a
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/swr-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/wdr-m.png b/plugin.video.mediathekview/resources/icons/wdr-m.png
new file mode 100644
index 0000000..da6c936
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/wdr-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/zdf-m.png b/plugin.video.mediathekview/resources/icons/zdf-m.png
new file mode 100644
index 0000000..dc10c64
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/zdf-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/icons/zdf-tivi-m.png b/plugin.video.mediathekview/resources/icons/zdf-tivi-m.png
new file mode 100644
index 0000000..dc10c64
--- /dev/null
+++ b/plugin.video.mediathekview/resources/icons/zdf-tivi-m.png
Binary files differ
diff --git a/plugin.video.mediathekview/resources/language/resource.language.de_de/strings.po b/plugin.video.mediathekview/resources/language/resource.language.de_de/strings.po
index d3832b3..fe15710 100644
--- a/plugin.video.mediathekview/resources/language/resource.language.de_de/strings.po
+++ b/plugin.video.mediathekview/resources/language/resource.language.de_de/strings.po
@@ -32,24 +32,40 @@ msgstr "HD Streams bevorzugen"
msgctxt "#30111"
msgid "No future videos (usually trailers)"
-msgstr "Keine Videos aus der Zukunft (üblicherweise Trailer)"
+msgstr "Keine Filme aus der Zukunft (üblicherweise Trailer)"
msgctxt "#30112"
msgid "Minimum duration in minutes"
-msgstr "Minimlae Länge in Minuten"
+msgstr "Minimale Länge in Minuten"
msgctxt "#30113"
msgid "Group shows of different channel"
msgstr "Sendungen verschiedener Sender zusammenfassen"
msgctxt "#30114"
-msgid "Download directory"
+msgid "Limit search results"
msgstr "Suchergebnisse eingrenzen"
msgctxt "#30115"
+msgid "Max age of recents in days"
+msgstr "Maximales Alter der Neuigkeiten in Tagen"
+
+msgctxt "#30116"
+msgid "Recent calculated by"
+msgstr "Neuigkeit berechnet ab"
+
+msgctxt "#30118"
msgid "Download directory"
msgstr "Download-Verzeichnis"
+msgctxt "#30161"
+msgid "Aired Date"
+msgstr "Sendedatum"
+
+msgctxt "#30162"
+msgid "Added to Database"
+msgstr "Hinzugefügt zur Datenbank"
+
# Settings Page 2
msgctxt "#30210"
msgid "Database type"
diff --git a/plugin.video.mediathekview/resources/language/resource.language.en_gb/strings.po b/plugin.video.mediathekview/resources/language/resource.language.en_gb/strings.po
index 5c121e8..2d3f7bb 100644
--- a/plugin.video.mediathekview/resources/language/resource.language.en_gb/strings.po
+++ b/plugin.video.mediathekview/resources/language/resource.language.en_gb/strings.po
@@ -47,9 +47,25 @@ msgid "Limit search results"
msgstr "Limit search results"
msgctxt "#30115"
+msgid "Max age of recents in days"
+msgstr "Max age of recents in days"
+
+msgctxt "#30116"
+msgid "Recent calculated by"
+msgstr "Recent calculated by"
+
+msgctxt "#30118"
msgid "Download directory"
msgstr "Download directory"
+msgctxt "#30161"
+msgid "Aired Date"
+msgstr "Aired Date"
+
+msgctxt "#30162"
+msgid "Added to Database"
+msgstr "Added to Database"
+
# Settings Page 2
msgctxt "#30210"
msgid "Database type"
diff --git a/plugin.video.mediathekview/resources/language/resource.language.it_it/strings.po b/plugin.video.mediathekview/resources/language/resource.language.it_it/strings.po
index f0d9027..9cc1bca 100644
--- a/plugin.video.mediathekview/resources/language/resource.language.it_it/strings.po
+++ b/plugin.video.mediathekview/resources/language/resource.language.it_it/strings.po
@@ -47,9 +47,25 @@ msgid "Limit search results"
msgstr "Limita risultati ricerche"
msgctxt "#30115"
+msgid "Max age of recents"
+msgstr "Età massima delle novità in giorni"
+
+msgctxt "#30116"
+msgid "Recent calculated by"
+msgstr "Novità calcolate con"
+
+msgctxt "#30118"
msgid "Download directory"
msgstr "Directory di download"
+msgctxt "#30161"
+msgid "Aired Date"
+msgstr "Data di Trasmissione"
+
+msgctxt "#30162"
+msgid "Added to Database"
+msgstr "Entrata nel database"
+
# Settings Page 2
msgctxt "#30210"
msgid "Database type"
@@ -106,11 +122,11 @@ msgstr "Livestreams"
msgctxt "#30904"
msgid "Recently Added"
-msgstr "Recenti"
+msgstr "Novità"
msgctxt "#30905"
msgid "Recently Added by Channel"
-msgstr "Recenti secondo emittenti"
+msgstr "Novità secondo emittenti"
msgctxt "#30906"
msgid "Browse by Show in all Channels"
diff --git a/plugin.video.mediathekview/resources/lib/base/Logger.py b/plugin.video.mediathekview/resources/lib/base/Logger.py
index 3d74680..850ff44 100644
--- a/plugin.video.mediathekview/resources/lib/base/Logger.py
+++ b/plugin.video.mediathekview/resources/lib/base/Logger.py
@@ -28,6 +28,6 @@ class Logger( object ):
def warn( self, message, *args ):
pass
-
+
def error( self, message, *args ):
pass
diff --git a/plugin.video.mediathekview/resources/lib/channelui.py b/plugin.video.mediathekview/resources/lib/channelui.py
index 36743e8..fc9d826 100644
--- a/plugin.video.mediathekview/resources/lib/channelui.py
+++ b/plugin.video.mediathekview/resources/lib/channelui.py
@@ -3,17 +3,19 @@
#
# -- Imports ------------------------------------------------
-import sys, urllib
-import xbmcplugin, xbmcgui
+import xbmcgui
+import xbmcplugin
+
+import resources.lib.mvutils as mvutils
from resources.lib.channel import Channel
# -- Classes ------------------------------------------------
class ChannelUI( Channel ):
- def __init__( self, handle, sortmethods = None, nextdir = 'initial' ):
- self.base_url = sys.argv[0]
+ def __init__( self, plugin, sortmethods = None, nextdir = 'initial' ):
+ self.plugin = plugin
+ self.handle = plugin.addon_handle
self.nextdir = nextdir
- self.handle = handle
self.sortmethods = sortmethods if sortmethods is not None else [ xbmcplugin.SORT_METHOD_TITLE ]
self.count = 0
@@ -24,9 +26,14 @@ class ChannelUI( Channel ):
def Add( self, altname = None ):
resultingname = self.channel if self.count == 0 else '%s (%d)' % ( self.channel, self.count, )
li = xbmcgui.ListItem( label = resultingname if altname is None else altname )
+ icon = 'special://home/addons/' + self.plugin.addon_id + '/resources/icons/' + self.channel.lower() + '-m.png'
+ li.setArt( {
+ 'thumb': icon,
+ 'icon': icon
+ } )
xbmcplugin.addDirectoryItem(
handle = self.handle,
- url = self.build_url( {
+ url = mvutils.build_url( {
'mode': self.nextdir,
'channel': self.id
} ),
@@ -36,6 +43,3 @@ class ChannelUI( Channel ):
def End( self ):
xbmcplugin.endOfDirectory( self.handle )
-
- def build_url( self, query ):
- return self.base_url + '?' + urllib.urlencode( query )
diff --git a/plugin.video.mediathekview/resources/lib/exceptions.py b/plugin.video.mediathekview/resources/lib/exceptions.py
index 53d7f14..b66fb4f 100644
--- a/plugin.video.mediathekview/resources/lib/exceptions.py
+++ b/plugin.video.mediathekview/resources/lib/exceptions.py
@@ -7,3 +7,6 @@ class DatabaseCorrupted( RuntimeError ):
class DatabaseLost( RuntimeError ):
"""This exception is raised when the connection to the database is lost during update"""
+
+class ExitRequested( Exception ):
+ """This exception is thrown if the addon is shut down by Kodi or by another same addon"""
diff --git a/plugin.video.mediathekview/resources/lib/filmui.py b/plugin.video.mediathekview/resources/lib/filmui.py
index af04798..23eaf98 100644
--- a/plugin.video.mediathekview/resources/lib/filmui.py
+++ b/plugin.video.mediathekview/resources/lib/filmui.py
@@ -3,7 +3,8 @@
#
# -- Imports ------------------------------------------------
-import xbmcplugin, xbmcgui
+import xbmcgui
+import xbmcplugin
from resources.lib.film import Film
from resources.lib.settings import Settings
@@ -45,7 +46,7 @@ class FilmUI( Film ):
infoLabels = {
'title' : resultingtitle + videohds,
- 'sorttitle' : resultingtitle,
+ 'sorttitle' : resultingtitle.lower(),
'tvshowtitle' : self.show,
'plot' : self.description
}
@@ -63,9 +64,15 @@ class FilmUI( Film ):
infoLabels['aired'] = airedstring
infoLabels['dateadded'] = airedstring
- li = xbmcgui.ListItem( resultingtitle, self.description )
+ icon = 'special://home/addons/' + self.plugin.addon_id + '/resources/icons/' + self.channel.lower() + '-m.png'
+
+ li = xbmcgui.ListItem( resultingtitle )
li.setInfo( type = 'video', infoLabels = infoLabels )
li.setProperty( 'IsPlayable', 'true' )
+ li.setArt( {
+ 'thumb': icon,
+ 'icon': icon
+ } )
# create context menu
contextmenu = []
diff --git a/plugin.video.mediathekview/resources/lib/initialui.py b/plugin.video.mediathekview/resources/lib/initialui.py
index 71d9aef..b59d409 100644
--- a/plugin.video.mediathekview/resources/lib/initialui.py
+++ b/plugin.video.mediathekview/resources/lib/initialui.py
@@ -3,13 +3,16 @@
#
# -- Imports ------------------------------------------------
-import sys, urllib
-import xbmcplugin, xbmcgui
+import xbmcgui
+import xbmcplugin
+
+import resources.lib.mvutils as mvutils
# -- Classes ------------------------------------------------
class InitialUI( object ):
- def __init__( self, handle, sortmethods = None ):
- self.handle = handle
+ def __init__( self, plugin, sortmethods = None ):
+ self.plugin = plugin
+ self.handle = plugin.addon_handle
self.sortmethods = sortmethods if sortmethods is not None else [ xbmcplugin.SORT_METHOD_TITLE ]
self.channelid = 0
self.initial = ''
@@ -28,7 +31,7 @@ class InitialUI( object ):
li = xbmcgui.ListItem( label = resultingname )
xbmcplugin.addDirectoryItem(
handle = self.handle,
- url = _build_url( {
+ url = mvutils.build_url( {
'mode': "shows",
'channel': self.channelid,
'initial': self.initial,
@@ -40,8 +43,3 @@ class InitialUI( object ):
def End( self ):
xbmcplugin.endOfDirectory( self.handle )
-
-# -- Functions ----------------------------------------------
-
-def _build_url( query ):
- return sys.argv[0] + '?' + urllib.urlencode( query )
diff --git a/plugin.video.mediathekview/resources/lib/kodi/KodiAddon.py b/plugin.video.mediathekview/resources/lib/kodi/KodiAddon.py
index f9c0cd7..4081044 100644
--- a/plugin.video.mediathekview/resources/lib/kodi/KodiAddon.py
+++ b/plugin.video.mediathekview/resources/lib/kodi/KodiAddon.py
@@ -3,14 +3,19 @@
#
# -- Imports ------------------------------------------------
-import sys, urllib
-import xbmc, xbmcgui, xbmcaddon, xbmcplugin
+import os
+import sys
+import urllib
+
+import xbmc
+import xbmcgui
+import xbmcaddon
+import xbmcplugin
from resources.lib.kodi.KodiLogger import KodiLogger
# -- Classes ------------------------------------------------
class KodiAddon( KodiLogger ):
-
def __init__( self ):
self.addon = xbmcaddon.Addon()
self.addon_id = self.addon.getAddonInfo( 'id' )
@@ -29,6 +34,7 @@ class KodiAddon( KodiLogger ):
return self.addon.setSetting( setting_id, value )
def doAction( self, action ):
+ self.debug( 'Triggered action {}', action )
xbmc.executebuiltin( 'Action({})'.format( action ) )
class KodiService( KodiAddon ):
@@ -66,3 +72,43 @@ class KodiPlugin( KodiAddon ):
def endOfDirectory( self, succeeded = True, updateListing = False, cacheToDisc = True ):
xbmcplugin.endOfDirectory( self.addon_handle, succeeded, updateListing, cacheToDisc )
+
+
+class KodiInterlockedMonitor( xbmc.Monitor ):
+ def __init__( self, service, setting_id ):
+ super( KodiInterlockedMonitor, self ).__init__()
+ self.instance_id = ''.join( format( x, '02x' ) for x in bytearray( os.urandom( 16 ) ) )
+ self.setting_id = setting_id
+ self.service = service
+
+ def RegisterInstance( self, waittime = 1 ):
+ if self.BadInstance():
+ self.service.info( 'Found other instance with id {}', self.instance_id )
+ self.service.info( 'Startup delayed by {} second(s) waiting the other instance to shut down', waittime )
+ self.service.setSetting( self.setting_id, self.instance_id )
+ xbmc.Monitor.waitForAbort( self, waittime )
+ else:
+ self.service.setSetting( self.setting_id, self.instance_id )
+
+ def UnregisterInstance( self ):
+ self.service.setSetting( self.setting_id, '' )
+
+ def BadInstance( self ):
+ instance_id = self.service.getSetting( self.setting_id )
+ return len( instance_id ) > 0 and self.instance_id != instance_id
+
+ def abortRequested( self ):
+ return self.BadInstance() or xbmc.Monitor.abortRequested( self )
+
+ def waitForAbort( self, timeout = None ):
+ if timeout is None:
+ # infinite wait
+ while not self.abortRequested():
+ if xbmc.Monitor.waitForAbort( self, 1 ):
+ return True
+ return True
+ else:
+ for _ in range( timeout ):
+ if self.BadInstance() or xbmc.Monitor.waitForAbort( self, 1 ):
+ return True
+ return self.BadInstance()
diff --git a/plugin.video.mediathekview/resources/lib/kodi/KodiUI.py b/plugin.video.mediathekview/resources/lib/kodi/KodiUI.py
index dee8dbb..138af50 100644
--- a/plugin.video.mediathekview/resources/lib/kodi/KodiUI.py
+++ b/plugin.video.mediathekview/resources/lib/kodi/KodiUI.py
@@ -3,15 +3,21 @@
#
# -- Imports ------------------------------------------------
-import xbmc, xbmcgui
+import xbmc
+import xbmcgui
+import xbmcaddon
# -- Classes ------------------------------------------------
class KodiUI( object ):
def __init__( self ):
- self.bgdialog = None
+ self.addon = xbmcaddon.Addon()
+ self.language = self.addon.getLocalizedString
+ self.bgdialog = KodiBGDialog()
- def GetEnteredText( self, deftext = '', heading = '', hidden = False ):
+ def GetEnteredText( self, deftext = None, heading = None, hidden = False ):
+ heading = self.language( heading ) if isinstance( heading, int ) else heading if heading is not None else ''
+ deftext = self.language( deftext ) if isinstance( deftext, int ) else deftext if deftext is not None else ''
keyboard = xbmc.Keyboard( deftext, heading, 1 if hidden else 0 )
keyboard.doModal()
if keyboard.isConfirmed():
@@ -19,39 +25,39 @@ class KodiUI( object ):
return deftext
def ShowNotification( self, heading, message, icon = xbmcgui.NOTIFICATION_INFO, time = 5000, sound = True ):
+ heading = self.language( heading ) if isinstance( heading, int ) else heading
+ message = self.language( message ) if isinstance( message, int ) else message
xbmcgui.Dialog().notification( heading, message, icon, time, sound )
def ShowWarning( self, heading, message, time = 5000, sound = True ):
- xbmcgui.Dialog().notification( heading, message, xbmcgui.NOTIFICATION_WARNING, time, sound )
+ self.ShowNotification( heading, message, xbmcgui.NOTIFICATION_WARNING, time, sound )
def ShowError( self, heading, message, time = 5000, sound = True ):
- xbmcgui.Dialog().notification( heading, message, xbmcgui.NOTIFICATION_ERROR, time, sound )
+ self.ShowNotification( heading, message, xbmcgui.NOTIFICATION_ERROR, time, sound )
def ShowBGDialog( self, heading = None, message = None ):
- if self.bgdialog is None:
- self.bgdialog = xbmcgui.DialogProgressBG()
- self.bgdialog.create( heading, message )
- else:
- self.bgdialog.update( 0, heading, message )
+ self.bgdialog.Create( heading, message )
def UpdateBGDialog( self, percent, heading = None, message = None ):
- if self.bgdialog is not None:
- self.bgdialog.update( percent, heading, message )
+ self.bgdialog.Update( percent, heading, message )
+
+ def HookBGDialog( self, blockcount, blocksize, totalsize ):
+ self.bgdialog.UrlRetrieveHook( blockcount, blocksize, totalsize )
def CloseBGDialog( self ):
- if self.bgdialog is not None:
- self.bgdialog.close()
- del self.bgdialog
- self.bgdialog = None
+ self.bgdialog.Close()
class KodiBGDialog( object ):
def __init__( self ):
+ self.language = xbmcaddon.Addon().getLocalizedString
self.bgdialog= None
def __del__( self ):
self.Close()
def Create( self, heading = None, message = None ):
+ heading = self.language( heading ) if isinstance( heading, int ) else heading
+ message = self.language( message ) if isinstance( message, int ) else message
if self.bgdialog is None:
self.bgdialog = xbmcgui.DialogProgressBG()
self.bgdialog.create( heading, message )
@@ -60,6 +66,8 @@ class KodiBGDialog( object ):
def Update( self, percent, heading = None, message = None ):
if self.bgdialog is not None:
+ heading = self.language( heading ) if isinstance( heading, int ) else heading
+ message = self.language( message ) if isinstance( message, int ) else message
self.bgdialog.update( percent, heading, message )
def UrlRetrieveHook( self, blockcount, blocksize, totalsize ):
diff --git a/plugin.video.mediathekview/resources/lib/mvupdate.py b/plugin.video.mediathekview/resources/lib/mvupdate.py
index 7595417..477139e 100644
--- a/plugin.video.mediathekview/resources/lib/mvupdate.py
+++ b/plugin.video.mediathekview/resources/lib/mvupdate.py
@@ -6,7 +6,7 @@ import os
import sys
import argparse
import datetime
-import xml.etree.ElementTree as ET
+import defusedxml.ElementTree as ET
from resources.lib.base.Logger import Logger
from resources.lib.updater import MediathekViewUpdater
@@ -24,6 +24,8 @@ class Settings( object ):
self.database = args.database
self.nofuture = True
self.minlength = 0
+ self.maxage = 86400
+ self.recentmode = 0
self.groupshows = False
self.updenabled = True
self.updinterval = 3600
@@ -104,6 +106,8 @@ class Notifier( object ):
pass
def UpdateUpdateProgress( self, percent, count, channels, shows, movies ):
pass
+ def HookDownloadProgress( self, blockcount, blocksize, totalsize ):
+ pass
def CloseUpdateProgress( self ):
pass
diff --git a/plugin.video.mediathekview/resources/lib/mvutils.py b/plugin.video.mediathekview/resources/lib/mvutils.py
index d4c7663..7ccaf3a 100644
--- a/plugin.video.mediathekview/resources/lib/mvutils.py
+++ b/plugin.video.mediathekview/resources/lib/mvutils.py
@@ -3,12 +3,21 @@
# -- Imports ------------------------------------------------
import os
+import sys
import stat
import string
+import urllib
import urllib2
-import xbmcvfs
from contextlib import closing
+from resources.lib.exceptions import ExitRequested
+
+# -- Kodi Specific Imports ----------------------------------
+try:
+ import xbmcvfs
+ is_kodi = True
+except ImportError:
+ is_kodi = False
# -- Functions ----------------------------------------------
def dir_exists( name ):
@@ -58,30 +67,29 @@ def cleanup_filename( val ):
search = ''.join( [ c for c in val if c in cset ] )
return search.strip()
-def url_retrieve( url, filename, reporthook, chunk_size = 8192 ):
+def url_retrieve( url, filename, reporthook, chunk_size = 8192, aborthook = None ):
with closing( urllib2.urlopen( url ) ) as u, closing( open( filename, 'wb' ) ) as f:
- total_size = int( u.info().getheader( 'Content-Length' ).strip() ) if u.info() and u.info().getheader( 'Content-Length' ) else 0
- total_chunks = 0
+ _chunked_url_copier( u, f, reporthook, chunk_size, aborthook )
+
+def url_retrieve_vfs( url, filename, reporthook, chunk_size = 8192, aborthook = None ):
+ with closing( urllib2.urlopen( url ) ) as u, closing( xbmcvfs.File( filename, 'wb' ) ) as f:
+ _chunked_url_copier( u, f, reporthook, chunk_size, aborthook )
- while True:
- reporthook( total_chunks, chunk_size, total_size )
- chunk = u.read( chunk_size )
- if not chunk:
- break
- f.write( chunk )
- total_chunks += 1
- return ( filename, [], )
+def build_url( query ):
+ return sys.argv[0] + '?' + urllib.urlencode( query )
-def url_retrieve_vfs( videourl, filename, reporthook, chunk_size = 8192 ):
- with closing( urllib2.urlopen( videourl ) ) as u, closing( xbmcvfs.File( filename, 'wb' ) ) as f:
- total_size = int( u.info().getheader( 'Content-Length' ).strip() ) if u.info() and u.info().getheader( 'Content-Length' ) else 0
- total_chunks = 0
+def _chunked_url_copier( u, f, reporthook, chunk_size, aborthook ):
+ aborthook = aborthook if aborthook is not None else lambda: False
+ total_size = int( u.info().getheader( 'Content-Length' ).strip() ) if u.info() and u.info().getheader( 'Content-Length' ) else 0
+ total_chunks = 0
- while True:
- reporthook( total_chunks, chunk_size, total_size )
- chunk = u.read( chunk_size )
- if not chunk:
- break
- f.write( chunk )
- total_chunks += 1
- return ( filename, [], )
+ while not aborthook():
+ reporthook( total_chunks, chunk_size, total_size )
+ chunk = u.read( chunk_size )
+ if not chunk:
+ # operation has finished
+ return
+ f.write( chunk )
+ total_chunks += 1
+ # abort requested
+ raise ExitRequested( 'Reception interrupted.' )
diff --git a/plugin.video.mediathekview/resources/lib/notifier.py b/plugin.video.mediathekview/resources/lib/notifier.py
index d257a52..517da9e 100644
--- a/plugin.video.mediathekview/resources/lib/notifier.py
+++ b/plugin.video.mediathekview/resources/lib/notifier.py
@@ -14,28 +14,31 @@ class Notifier( KodiUI ):
self.language = xbmcaddon.Addon().getLocalizedString
def ShowDatabaseError( self, err ):
- self.ShowError( self.language( 30951 ), '{}'.format( err ) )
+ self.ShowError( 30951, '{}'.format( err ) )
def ShowDownloadError( self, name, err ):
- self.ShowError( self.language( 30952 ), self.language( 30953 ).format( name, err ) )
+ self.ShowError( 30952, self.language( 30953 ).format( name, err ) )
def ShowMissingExtractorError( self ):
- self.ShowError( self.language( 30952 ), self.language( 30954 ), time = 10000 )
+ self.ShowError( 30952, 30954, time = 10000 )
def ShowLimitResults( self, maxresults ):
- self.ShowNotification( self.language( 30980 ), self.language( 30981 ).format( maxresults ) )
+ self.ShowNotification( 30980, self.language( 30981 ).format( maxresults ) )
def ShowDownloadProgress( self ):
- self.ShowBGDialog( self.language( 30955 ) )
+ self.ShowBGDialog( 30955 )
def UpdateDownloadProgress( self, percent, message = None ):
self.UpdateBGDialog( percent, message = message )
+ def HookDownloadProgress( self, blockcount, blocksize, totalsize ):
+ self.HookBGDialog( blockcount, blocksize, totalsize )
+
def CloseDownloadProgress( self ):
self.CloseBGDialog()
def ShowUpdateProgress( self ):
- self.ShowBGDialog( self.language( 30956 ) )
+ self.ShowBGDialog( 30956 )
def UpdateUpdateProgress( self, percent, count, channels, shows, movies ):
message = self.language( 30957 ) % ( count, channels, shows, movies )
diff --git a/plugin.video.mediathekview/resources/lib/settings.py b/plugin.video.mediathekview/resources/lib/settings.py
index 32bd95a..13d86e0 100644
--- a/plugin.video.mediathekview/resources/lib/settings.py
+++ b/plugin.video.mediathekview/resources/lib/settings.py
@@ -19,6 +19,8 @@ class Settings( object ):
self.minlength = int( float( self.addon.getSetting( 'minlength' ) ) ) * 60
self.groupshows = self.addon.getSetting( 'groupshows' ) == 'true'
self.maxresults = int( self.addon.getSetting( 'maxresults' ) )
+ self.maxage = int( self.addon.getSetting( 'maxage' ) ) * 86400
+ self.recentmode = int( self.addon.getSetting( 'recentmode' ) )
self.downloadpath = self.addon.getSetting( 'downloadpath' )
self.type = self.addon.getSetting( 'dbtype' )
self.host = self.addon.getSetting( 'dbhost' )
diff --git a/plugin.video.mediathekview/resources/lib/showui.py b/plugin.video.mediathekview/resources/lib/showui.py
index b0bf7f9..64aff20 100644
--- a/plugin.video.mediathekview/resources/lib/showui.py
+++ b/plugin.video.mediathekview/resources/lib/showui.py
@@ -3,35 +3,55 @@
#
# -- Imports ------------------------------------------------
-import sys, urllib
-import xbmcplugin, xbmcgui
+import xbmcgui
+import xbmcplugin
+
+import resources.lib.mvutils as mvutils
from resources.lib.show import Show
# -- Classes ------------------------------------------------
class ShowUI( Show ):
- def __init__( self, handle, sortmethods = None ):
- self.base_url = sys.argv[0]
- self.handle = handle
+ def __init__( self, plugin, sortmethods = None ):
+ self.plugin = plugin
+ self.handle = plugin.addon_handle
self.sortmethods = sortmethods if sortmethods is not None else [ xbmcplugin.SORT_METHOD_TITLE ]
self.querychannelid = 0
def Begin( self, channelid ):
- self.querychannelid = channelid
+ self.querychannelid = int( channelid )
for method in self.sortmethods:
xbmcplugin.addSortMethod( self.handle, method )
def Add( self, altname = None ):
if altname is not None:
resultingname = altname
- elif self.querychannelid == '0':
+ elif self.querychannelid == 0:
resultingname = self.show + ' [' + self.channel + ']'
else:
resultingname = self.show
+
+ infoLabels = {
+ 'title' : resultingname,
+ 'sorttitle' : resultingname.lower()
+ }
+
+
+ if self.channel.find( ',' ) == -1:
+ icon = 'special://home/addons/' + self.plugin.addon_id + '/resources/icons/' + self.channel.lower() + '-m.png'
+ else:
+ icon = 'special://home/addons/' + self.plugin.addon_id + '/resources/icons/default-m.png'
+
li = xbmcgui.ListItem( label = resultingname )
+ li.setInfo( type = 'video', infoLabels = infoLabels )
+ li.setArt( {
+ 'thumb': icon,
+ 'icon': icon
+ } )
+
xbmcplugin.addDirectoryItem(
handle = self.handle,
- url = self.build_url( {
+ url = mvutils.build_url( {
'mode': "films",
'show': self.id
} ),
@@ -41,6 +61,3 @@ class ShowUI( Show ):
def End( self ):
xbmcplugin.endOfDirectory( self.handle )
-
- def build_url( self, query ):
- return self.base_url + '?' + urllib.urlencode( query )
diff --git a/plugin.video.mediathekview/resources/lib/storemysql.py b/plugin.video.mediathekview/resources/lib/storemysql.py
index e2419cc..9268eec 100644
--- a/plugin.video.mediathekview/resources/lib/storemysql.py
+++ b/plugin.video.mediathekview/resources/lib/storemysql.py
@@ -20,7 +20,7 @@ class StoreMySQL( object ):
# useful query fragments
self.sql_query_films = "SELECT film.id,`title`,`show`,`channel`,`description`,TIME_TO_SEC(`duration`) AS `seconds`,`size`,`aired`,`url_sub`,`url_video`,`url_video_sd`,`url_video_hd` FROM `film` LEFT JOIN `show` ON show.id=film.showid LEFT JOIN `channel` ON channel.id=film.channelid"
self.sql_query_filmcnt = "SELECT COUNT(*) FROM `film` LEFT JOIN `show` ON show.id=film.showid LEFT JOIN `channel` ON channel.id=film.channelid"
- self.sql_cond_recent = "( TIMESTAMPDIFF(HOUR,`aired`,CURRENT_TIMESTAMP()) < 24 )"
+ self.sql_cond_recent = "( TIMESTAMPDIFF(SECOND,{},CURRENT_TIMESTAMP()) <= {} )".format( "aired" if settings.recentmode == 0 else "film.dtCreated", settings.maxage )
self.sql_cond_nofuture = " AND ( ( `aired` IS NULL ) OR ( TIMESTAMPDIFF(HOUR,`aired`,CURRENT_TIMESTAMP()) > 0 ) )" if settings.nofuture else ""
self.sql_cond_minlength = " AND ( ( `duration` IS NULL ) OR ( TIME_TO_SEC(`duration`) >= %d ) )" % settings.minlength if settings.minlength > 0 else ""
@@ -48,17 +48,21 @@ class StoreMySQL( object ):
self.conn.close()
def Search( self, search, filmui ):
- self._Search_Condition( '( ( `title` LIKE "%%%s%%" ) OR ( `show` LIKE "%%%s%%" ) )' % ( search, search, ), filmui, True, True, self.settings.maxresults )
+ searchmask = '%' + search.decode('utf-8') + '%'
+ self._Search_Condition( '( ( `title` LIKE %s ) OR ( `show` LIKE %s ) )', ( searchmask, searchmask, ), filmui, True, True, self.settings.maxresults )
def SearchFull( self, search, filmui ):
- self._Search_Condition( '( ( `title` LIKE "%%%s%%" ) OR ( `show` LIKE "%%%s%%" ) ) OR ( `description` LIKE "%%%s%%") )' % ( search, search, search ), filmui, True, True, self.settings.maxresults )
+ searchmask = '%' + search.decode('utf-8') + '%'
+ self._Search_Condition( '( ( `title` LIKE %s ) OR ( `show` LIKE %s ) OR ( `description` LIKE %s ) )', ( searchmask, searchmask, searchmask ), filmui, True, True, self.settings.maxresults )
def GetRecents( self, channelid, filmui ):
- sql_cond_channel = ' AND ( film.channelid=' + str( channelid ) + ' ) ' if channelid != '0' else ''
- self._Search_Condition( self.sql_cond_recent + sql_cond_channel, filmui, True, False, 10000 )
+ if channelid != '0':
+ self._Search_Condition( self.sql_cond_recent + ' AND ( film.channelid=%s )', ( int( channelid ), ), filmui, True, False, 10000 )
+ else:
+ self._Search_Condition( self.sql_cond_recent, (), filmui, True, False, 10000 )
def GetLiveStreams( self, filmui ):
- self._Search_Condition( '( show.search="LIVESTREAM" )', filmui, False, False, 10000 )
+ self._Search_Condition( '( show.search="LIVESTREAM" )', (), filmui, False, False, 10000 )
def GetChannels( self, channelui ):
self._Channels_Condition( None, channelui )
@@ -70,18 +74,30 @@ class StoreMySQL( object ):
if self.conn is None:
return
try:
- condition = 'WHERE ( `channelid`=' + str( channelid ) + ' ) ' if channelid != '0' else ''
- self.logger.info( 'MySQL Query: {}',
- 'SELECT LEFT(`search`,1) AS letter,COUNT(*) AS `count` FROM `show` ' +
- condition +
- 'GROUP BY LEFT(search,1)'
- )
+ channelid = int( channelid )
cursor = self.conn.cursor()
- cursor.execute(
- 'SELECT LEFT(`search`,1) AS letter,COUNT(*) AS `count` FROM `show` ' +
- condition +
- 'GROUP BY LEFT(`search`,1)'
- )
+ if channelid != 0:
+ self.logger.info(
+ 'MySQL Query: SELECT LEFT(`search`,1) AS letter,COUNT(*) AS `count` FROM `show` WHERE ( `channelid`={} ) GROUP BY LEFT(search,1)',
+ channelid
+ )
+ cursor.execute( """
+ SELECT LEFT(`search`,1) AS `letter`,
+ COUNT(*) AS `count`
+ FROM `show`
+ WHERE ( `channelid`=%s )
+ GROUP BY LEFT(`search`,1)
+ """, ( channelid, ) )
+ else:
+ self.logger.info(
+ 'MySQL Query: SELECT LEFT(`search`,1) AS letter,COUNT(*) AS `count` FROM `show` GROUP BY LEFT(search,1)'
+ )
+ cursor.execute( """
+ SELECT LEFT(`search`,1) AS `letter`,
+ COUNT(*) AS `count`
+ FROM `show`
+ GROUP BY LEFT(`search`,1)
+ """ )
initialui.Begin( channelid )
for ( initialui.initial, initialui.count ) in cursor:
initialui.Add()
@@ -95,15 +111,57 @@ class StoreMySQL( object ):
if self.conn is None:
return
try:
- if channelid == '0' and self.settings.groupshows:
- query = 'SELECT GROUP_CONCAT(show.id),GROUP_CONCAT(`channelid`),`show`,GROUP_CONCAT(`channel`) FROM `show` LEFT JOIN `channel` ON channel.id=show.channelid WHERE ( `show` LIKE "%s%%" ) GROUP BY `show`' % initial
- elif channelid == '0':
- query = 'SELECT show.id,show.channelid,show.show,channel.channel FROM `show` LEFT JOIN channel ON channel.id=show.channelid WHERE ( `show` LIKE "%s%%" )' % initial
- else:
- query = 'SELECT show.id,show.channelid,show.show,channel.channel FROM `show` LEFT JOIN channel ON channel.id=show.channelid WHERE ( `channelid`=%s ) AND ( `show` LIKE "%s%%" )' % ( channelid, initial )
- self.logger.info( 'MySQL Query: {}', query )
+ channelid = int( channelid )
cursor = self.conn.cursor()
- cursor.execute( query )
+ if channelid == 0 and self.settings.groupshows:
+ cursor.execute( """
+ SELECT GROUP_CONCAT(show.id),
+ GROUP_CONCAT(`channelid`),
+ `show`,
+ GROUP_CONCAT(`channel`)
+ FROM `show`
+ LEFT JOIN `channel`
+ ON ( channel.id = show.channelid )
+ WHERE ( `show` LIKE %s )
+ GROUP BY `show`
+ """, ( initial + '%', ) )
+ elif channelid == 0:
+ cursor.execute( """
+ SELECT show.id,
+ show.channelid,
+ show.show,
+ channel.channel
+ FROM `show`
+ LEFT JOIN `channel`
+ ON ( channel.id = show.channelid )
+ WHERE ( `show` LIKE %s )
+ """, ( initial + '%', ) )
+ elif initial:
+ cursor.execute( """
+ SELECT show.id,
+ show.channelid,
+ show.show,
+ channel.channel
+ FROM `show`
+ LEFT JOIN `channel`
+ ON ( channel.id = show.channelid )
+ WHERE (
+ ( `channelid` = %s )
+ AND
+ ( `show` LIKE %s )
+ )
+ """, ( channelid, initial + '%', ) )
+ else:
+ cursor.execute( """
+ SELECT show.id,
+ show.channelid,
+ show.show,
+ channel.channel
+ FROM `show`
+ LEFT JOIN `channel`
+ ON ( channel.id = show.channelid )
+ WHERE ( `channelid` = %s )
+ """, ( channelid, ) )
showui.Begin( channelid )
for ( showui.id, showui.channelid, showui.show, showui.channel ) in cursor:
showui.Add()
@@ -118,13 +176,10 @@ class StoreMySQL( object ):
return
if showid.find( ',' ) == -1:
# only one channel id
- condition = '( `showid`=%s )' % showid
- showchannels = False
+ self._Search_Condition( '( `showid` = %s )', ( int( showid ), ), filmui, False, False, 10000 )
else:
# multiple channel ids
- condition = '( `showid` IN ( %s ) )' % showid
- showchannels = True
- self._Search_Condition( condition, filmui, False, showchannels, 10000 )
+ self._Search_Condition( '( `showid` IN ( {} ) )'.format( showid ), (), filmui, False, True, 10000 )
def _Channels_Condition( self, condition, channelui):
if self.conn is None:
@@ -132,11 +187,14 @@ class StoreMySQL( object ):
try:
if condition is None:
query = 'SELECT `id`,`channel`,0 AS `count` FROM `channel`'
+ qtail = ''
else:
- query = 'SELECT channel.id AS `id`,`channel`,COUNT(*) AS `count` FROM `film` LEFT JOIN `channel` ON channel.id=film.channelid WHERE ' + condition + ' GROUP BY channel.id'
- self.logger.info( 'MySQL Query: {}', query )
+ query = 'SELECT channel.id AS `id`,`channel`,COUNT(*) AS `count` FROM `film` LEFT JOIN `channel` ON channel.id=film.channelid'
+ qtail = ' WHERE ' + condition + self.sql_cond_nofuture + self.sql_cond_minlength + ' GROUP BY channel.id'
+ self.logger.info( 'MySQL Query: {}', query + qtail )
+
cursor = self.conn.cursor()
- cursor.execute( query )
+ cursor.execute( query + qtail )
channelui.Begin()
for ( channelui.id, channelui.channel, channelui.count ) in cursor:
channelui.Add()
@@ -146,7 +204,7 @@ class StoreMySQL( object ):
self.logger.error( 'Database error: {}', err )
self.notifier.ShowDatabaseError( err )
- def _Search_Condition( self, condition, filmui, showshows, showchannels, maxresults ):
+ def _Search_Condition( self, condition, params, filmui, showshows, showchannels, maxresults ):
if self.conn is None:
return
try:
@@ -164,7 +222,8 @@ class StoreMySQL( object ):
condition +
self.sql_cond_nofuture +
self.sql_cond_minlength +
- ' LIMIT {}'.format( maxresults + 1 ) if maxresults else ''
+ ' LIMIT {}'.format( maxresults + 1 ) if maxresults else '',
+ params
)
( results, ) = cursor.fetchone()
if maxresults and results > maxresults:
@@ -175,7 +234,8 @@ class StoreMySQL( object ):
condition +
self.sql_cond_nofuture +
self.sql_cond_minlength +
- ' LIMIT {}'.format( maxresults + 1 ) if maxresults else ''
+ ' LIMIT {}'.format( maxresults + 1 ) if maxresults else '',
+ params
)
filmui.Begin( showshows, showchannels )
for ( filmui.id, filmui.title, filmui.show, filmui.channel, filmui.description, filmui.seconds, filmui.size, filmui.aired, filmui.url_sub, filmui.url_video, filmui.url_video_sd, filmui.url_video_hd ) in cursor:
@@ -518,10 +578,10 @@ class StoreMySQL( object ):
cursor = self.conn.cursor()
cursor.callproc( 'ftInsertChannel', ( channel, ) )
for result in cursor.stored_results():
- for ( id, added ) in result:
+ for ( idd, added ) in result:
cursor.close()
self.conn.commit()
- return ( id, added )
+ return ( idd, added )
# should never happen
cursor.close()
self.conn.commit()
diff --git a/plugin.video.mediathekview/resources/lib/storesqlite.py b/plugin.video.mediathekview/resources/lib/storesqlite.py
index 601db67..90d10b1 100644
--- a/plugin.video.mediathekview/resources/lib/storesqlite.py
+++ b/plugin.video.mediathekview/resources/lib/storesqlite.py
@@ -23,7 +23,7 @@ class StoreSQLite( object ):
# useful query fragments
self.sql_query_films = "SELECT film.id,title,show,channel,description,duration,size,datetime(aired, 'unixepoch', 'localtime'),url_sub,url_video,url_video_sd,url_video_hd FROM film LEFT JOIN show ON show.id=film.showid LEFT JOIN channel ON channel.id=film.channelid"
self.sql_query_filmcnt = "SELECT COUNT(*) FROM film LEFT JOIN show ON show.id=film.showid LEFT JOIN channel ON channel.id=film.channelid"
- self.sql_cond_recent = "( ( UNIX_TIMESTAMP() - aired ) <= 86400 )"
+ self.sql_cond_recent = "( ( UNIX_TIMESTAMP() - {} ) <= {} )".format( "aired" if settings.recentmode == 0 else "film.dtCreated", settings.maxage )
self.sql_cond_nofuture = " AND ( ( aired IS NULL ) OR ( ( UNIX_TIMESTAMP() - aired ) > 0 ) )" if settings.nofuture else ""
self.sql_cond_minlength = " AND ( ( duration IS NULL ) OR ( duration >= %d ) )" % settings.minlength if settings.minlength > 0 else ""
@@ -55,17 +55,21 @@ class StoreSQLite( object ):
self.conn = None
def Search( self, search, filmui ):
- self._Search_Condition( '( ( title LIKE "%%%s%%" ) OR ( show LIKE "%%%s%%" ) )' % ( search, search ), filmui, True, True, self.settings.maxresults )
+ searchmask = '%' + search.decode('utf-8') + '%'
+ self._Search_Condition( '( ( title LIKE ? ) OR ( show LIKE ? ) )', ( searchmask, searchmask, ), filmui, True, True, self.settings.maxresults )
def SearchFull( self, search, filmui ):
- self._Search_Condition( '( ( title LIKE "%%%s%%" ) OR ( show LIKE "%%%s%%" ) OR ( description LIKE "%%%s%%") )' % ( search, search, search ), filmui, True, True, self.settings.maxresults )
+ searchmask = '%' + search.decode('utf-8') + '%'
+ self._Search_Condition( '( ( title LIKE ? ) OR ( show LIKE ? ) OR ( description LIKE ? ) )', ( searchmask, searchmask, searchmask ), filmui, True, True, self.settings.maxresults )
def GetRecents( self, channelid, filmui ):
- sql_cond_channel = ' AND ( film.channelid=' + str( channelid ) + ' ) ' if channelid != '0' else ''
- self._Search_Condition( self.sql_cond_recent + sql_cond_channel, filmui, True, False, 10000 )
+ if channelid != '0':
+ self._Search_Condition( self.sql_cond_recent + ' AND ( film.channelid=? )', ( int( channelid ), ), filmui, True, False, 10000 )
+ else:
+ self._Search_Condition( self.sql_cond_recent, (), filmui, True, False, 10000 )
def GetLiveStreams( self, filmui ):
- self._Search_Condition( '( show.search="LIVESTREAM" )', filmui, False, False, 10000 )
+ self._Search_Condition( '( show.search="LIVESTREAM" )', (), filmui, False, False, 10000 )
def GetChannels( self, channelui ):
self._Channels_Condition( None, channelui )
@@ -77,18 +81,28 @@ class StoreSQLite( object ):
if self.conn is None:
return
try:
- condition = 'WHERE ( channelid=' + str( channelid ) + ' ) ' if channelid != '0' else ''
- self.logger.info( 'SQlite Query: {}',
- 'SELECT SUBSTR(search,1,1),COUNT(*) FROM show ' +
- condition +
- 'GROUP BY LEFT(search,1)'
- )
+ channelid = int( channelid )
cursor = self.conn.cursor()
- cursor.execute(
- 'SELECT SUBSTR(search,1,1),COUNT(*) FROM show ' +
- condition +
- 'GROUP BY SUBSTR(search,1,1)'
- )
+ if channelid != 0:
+ self.logger.info(
+ 'SQlite Query: SELECT SUBSTR(search,1,1),COUNT(*) FROM show WHERE ( channelid={} ) GROUP BY LEFT(search,1)',
+ channelid
+ )
+ cursor.execute( """
+ SELECT SUBSTR(search,1,1),COUNT(*)
+ FROM show
+ WHERE ( channelid=? )
+ GROUP BY SUBSTR(search,1,1)
+ """, ( channelid, ) )
+ else:
+ self.logger.info(
+ 'SQlite Query: SELECT SUBSTR(search,1,1),COUNT(*) FROM show GROUP BY LEFT(search,1)'
+ )
+ cursor.execute( """
+ SELECT SUBSTR(search,1,1),COUNT(*)
+ FROM show
+ GROUP BY SUBSTR(search,1,1)
+ """ )
initialui.Begin( channelid )
for ( initialui.initial, initialui.count ) in cursor:
initialui.Add()
@@ -102,15 +116,57 @@ class StoreSQLite( object ):
if self.conn is None:
return
try:
- if channelid == '0' and self.settings.groupshows:
- query = 'SELECT GROUP_CONCAT(show.id),GROUP_CONCAT(channelid),show,GROUP_CONCAT(channel) FROM show LEFT JOIN channel ON channel.id=show.channelid WHERE ( show LIKE "%s%%" ) GROUP BY show' % initial
- elif channelid == '0':
- query = 'SELECT show.id,show.channelid,show.show,channel.channel FROM show LEFT JOIN channel ON channel.id=show.channelid WHERE ( show LIKE "%s%%" )' % initial
- else:
- query = 'SELECT show.id,show.channelid,show.show,channel.channel FROM show LEFT JOIN channel ON channel.id=show.channelid WHERE ( channelid=%s ) AND ( show LIKE "%s%%" )' % ( channelid, initial )
- self.logger.info( 'SQLite Query: {}', query )
+ channelid = int( channelid )
cursor = self.conn.cursor()
- cursor.execute( query )
+ if channelid == 0 and self.settings.groupshows:
+ cursor.execute( """
+ SELECT GROUP_CONCAT(show.id),
+ GROUP_CONCAT(channelid),
+ show,
+ GROUP_CONCAT(channel)
+ FROM show
+ LEFT JOIN channel
+ ON ( channel.id = show.channelid )
+ WHERE ( show LIKE ? )
+ GROUP BY show
+ """, ( initial + '%', ) )
+ elif channelid == 0:
+ cursor.execute( """
+ SELECT show.id,
+ show.channelid,
+ show.show,
+ channel.channel
+ FROM show
+ LEFT JOIN channel
+ ON ( channel.id = show.channelid )
+ WHERE ( show LIKE ? )
+ """, ( initial + '%', ) )
+ elif initial:
+ cursor.execute( """
+ SELECT show.id,
+ show.channelid,
+ show.show,
+ channel.channel
+ FROM show
+ LEFT JOIN channel
+ ON ( channel.id = show.channelid )
+ WHERE (
+ ( channelid=? )
+ AND
+ ( show LIKE ? )
+ )
+ """, ( channelid, initial + '%', ) )
+ else:
+ cursor.execute( """
+ SELECT show.id,
+ show.channelid,
+ show.show,
+ channel.channel
+ FROM show
+ LEFT JOIN channel
+ ON ( channel.id = show.channelid )
+ WHERE ( channelid=? )
+ """, ( channelid, ) )
showui.Begin( channelid )
for ( showui.id, showui.channelid, showui.show, showui.channel ) in cursor:
showui.Add()
@@ -125,13 +181,10 @@ class StoreSQLite( object ):
return
if showid.find( ',' ) == -1:
# only one channel id
- condition = '( showid=%s )' % showid
- showchannels = False
+ self._Search_Condition( '( showid=? )', ( int( showid ), ), filmui, False, False, 10000 )
else:
# multiple channel ids
- condition = '( showid IN ( %s ) )' % showid
- showchannels = True
- self._Search_Condition( condition, filmui, False, showchannels, 10000 )
+ self._Search_Condition( '( showid IN ( {} ) )'.format( showid ), (), filmui, False, True, 10000 )
def _Channels_Condition( self, condition, channelui ):
if self.conn is None:
@@ -139,11 +192,13 @@ class StoreSQLite( object ):
try:
if condition is None:
query = 'SELECT id,channel,0 AS `count` FROM channel'
+ qtail = ''
else:
- query = 'SELECT channel.id AS `id`,channel,COUNT(*) AS `count` FROM film LEFT JOIN channel ON channel.id=film.channelid WHERE ' + condition + ' GROUP BY channel'
- self.logger.info( 'SQLite Query: {}', query )
+ query = 'SELECT channel.id AS `id`,channel,COUNT(*) AS `count` FROM film LEFT JOIN channel ON channel.id=film.channelid'
+ qtail = ' WHERE ' + condition + ' GROUP BY channel'
+ self.logger.info( 'SQLite Query: {}', query + qtail )
cursor = self.conn.cursor()
- cursor.execute( query )
+ cursor.execute( query + qtail )
channelui.Begin()
for ( channelui.id, channelui.channel, channelui.count ) in cursor:
channelui.Add()
@@ -153,7 +208,7 @@ class StoreSQLite( object ):
self.logger.error( 'Database error: {}', err )
self.notifier.ShowDatabaseError( err )
- def _Search_Condition( self, condition, filmui, showshows, showchannels, maxresults ):
+ def _Search_Condition( self, condition, params, filmui, showshows, showchannels, maxresults ):
if self.conn is None:
return
try:
@@ -172,7 +227,8 @@ class StoreSQLite( object ):
condition +
self.sql_cond_nofuture +
self.sql_cond_minlength +
- ' LIMIT {}'.format( maxresults + 1 ) if maxresults else ''
+ ' LIMIT {}'.format( maxresults + 1 ) if maxresults else '',
+ params
)
( results, ) = cursor.fetchone()
if maxresults and results > maxresults:
@@ -183,7 +239,8 @@ class StoreSQLite( object ):
condition +
self.sql_cond_nofuture +
self.sql_cond_minlength +
- ' LIMIT {}'.format( maxresults ) if maxresults else ''
+ ' LIMIT {}'.format( maxresults ) if maxresults else '',
+ params
)
filmui.Begin( showshows, showchannels )
for ( filmui.id, filmui.title, filmui.show, filmui.channel, filmui.description, filmui.seconds, filmui.size, filmui.aired, filmui.url_sub, filmui.url_video, filmui.url_video_sd, filmui.url_video_hd ) in cursor:
diff --git a/plugin.video.mediathekview/resources/lib/ttml2srt.py b/plugin.video.mediathekview/resources/lib/ttml2srt.py
index 7bfdc47..5c2c4e3 100644
--- a/plugin.video.mediathekview/resources/lib/ttml2srt.py
+++ b/plugin.video.mediathekview/resources/lib/ttml2srt.py
@@ -27,7 +27,7 @@
import re
import io
from datetime import timedelta
-from xml.etree import ElementTree as ET
+from defusedxml import ElementTree as ET
def ttml2srt( infile, outfile ):
tree = ET.parse( infile )
@@ -59,7 +59,7 @@ def ttml2srt( infile, outfile ):
def parse_time_expression(expression, default_offset=timedelta(0)):
offset_time = re.match(r'^([0-9]+(\.[0-9]+)?)(h|m|s|ms|f|t)$', expression)
if offset_time:
- time_value, fraction, metric = offset_time.groups()
+ time_value, _, metric = offset_time.groups()
time_value = float(time_value)
if metric == 'h':
return default_offset + timedelta(hours=time_value)
@@ -79,7 +79,7 @@ def ttml2srt( infile, outfile ):
clock_time = re.match(
r'^([0-9]{2,}):([0-9]{2,}):([0-9]{2,}(\.[0-9]+)?)$', expression)
if clock_time:
- hours, minutes, seconds, fraction = clock_time.groups()
+ hours, minutes, seconds, _ = clock_time.groups()
return timedelta(hours=int(hours), minutes=int(minutes), seconds=float(seconds))
clock_time_frames = re.match(
diff --git a/plugin.video.mediathekview/resources/lib/updater.py b/plugin.video.mediathekview/resources/lib/updater.py
index afdf840..95c99d0 100644
--- a/plugin.video.mediathekview/resources/lib/updater.py
+++ b/plugin.video.mediathekview/resources/lib/updater.py
@@ -2,9 +2,15 @@
# Copyright (c) 2017-2018, Leo Moll
# -- Imports ------------------------------------------------
-import os, urllib2, subprocess, ijson, datetime, time
-import xml.etree.ElementTree as etree
-
+import os
+import time
+import ijson
+import random
+import urllib2
+import datetime
+import subprocess
+
+import defusedxml.ElementTree as etree
import resources.lib.mvutils as mvutils
from operator import itemgetter
@@ -12,6 +18,7 @@ from operator import itemgetter
from resources.lib.store import Store
from resources.lib.exceptions import DatabaseCorrupted
from resources.lib.exceptions import DatabaseLost
+from resources.lib.exceptions import ExitRequested
# -- Unpacker support ---------------------------------------
upd_can_bz2 = False
@@ -106,7 +113,7 @@ class MediathekViewUpdater( object ):
self.Import( full )
def Import( self, full ):
- ( _, compfile, destfile, avgrecsize ) = self._get_update_info( full )
+ ( _, _, destfile, avgrecsize ) = self._get_update_info( full )
if not mvutils.file_exists( destfile ):
self.logger.error( 'File {} does not exists', destfile )
return False
@@ -178,14 +185,14 @@ class MediathekViewUpdater( object ):
except DatabaseLost as err:
self.logger.error( '{}', err )
self.notifier.CloseUpdateProgress()
- except IOError as err:
+ except Exception as err:
self.logger.error( 'Error {} wile processing {}', err, destfile )
self._update_end( full, 'ABORTED' )
self.notifier.CloseUpdateProgress()
return False
def GetNewestList( self, full ):
- ( url, compfile, destfile, avgrecsize ) = self._get_update_info( full )
+ ( url, compfile, destfile, _ ) = self._get_update_info( full )
if url is None:
self.logger.error( 'No suitable archive extractor available for this system' )
self.notifier.ShowMissingExtractorError()
@@ -199,20 +206,18 @@ class MediathekViewUpdater( object ):
self.logger.error( 'Failure opening {}', url )
self.notifier.ShowDownloadError( url, err )
return False
-
root = etree.fromstring ( data )
urls = []
for server in root.findall( 'Server' ):
try:
URL = server.find( 'URL' ).text
Prio = server.find( 'Prio' ).text
- urls.append( ( self._get_update_url( URL ), Prio ) )
+ urls.append( ( self._get_update_url( URL ), float( Prio ) + random.random() * 1.2 ) )
self.logger.info( 'Found mirror {} (Priority {})', URL, Prio )
except AttributeError:
pass
urls = sorted( urls, key = itemgetter( 1 ) )
urls = [ url[0] for url in urls ]
- result = None
# cleanup downloads
self.logger.info( 'Cleaning up old downloads...' )
@@ -227,17 +232,23 @@ class MediathekViewUpdater( object ):
lasturl = url
self.logger.info( 'Trying to download {} from {}...', os.path.basename( compfile ), url )
self.notifier.UpdateDownloadProgress( 0, url )
- result = mvutils.url_retrieve( url, filename = compfile, reporthook = self._reporthook )
+ mvutils.url_retrieve( url, filename = compfile, reporthook = self.notifier.HookDownloadProgress, aborthook = self.monitor.abortRequested )
break
except urllib2.URLError as err:
self.logger.error( 'Failure downloading {}', url )
+ self.notifier.CloseDownloadProgress()
+ self.notifier.ShowDownloadError( lasturl, err )
+ return False
+ except ExitRequested as err:
+ self.logger.error( 'Immediate exit requested. Aborting download of {}', url )
+ self.notifier.CloseDownloadProgress()
+ self.notifier.ShowDownloadError( lasturl, err )
+ return False
except Exception as err:
self.logger.error( 'Failure writng {}', url )
- if result is None:
- self.logger.info( 'No file downloaded' )
- self.notifier.CloseDownloadProgress()
- self.notifier.ShowDownloadError( lasturl, err )
- return False
+ self.notifier.CloseDownloadProgress()
+ self.notifier.ShowDownloadError( lasturl, err )
+ return False
# decompress filmliste
if self.use_xz is True:
@@ -304,13 +315,6 @@ class MediathekViewUpdater( object ):
self.logger.error( 'Failed to remove {}: error {}', name, err )
return False
- def _reporthook( self, blockcount, blocksize, totalsize ):
- downloaded = blockcount * blocksize
- if totalsize > 0:
- percent = int( (downloaded * 100) / totalsize )
- self.notifier.UpdateDownloadProgress( percent )
- self.logger.debug( 'Downloading blockcount={}, blocksize={}, totalsize={}', blockcount, blocksize, totalsize )
-
def _update_start( self, full ):
self.logger.info( 'Initializing update...' )
self.add_chn = 0
diff --git a/plugin.video.mediathekview/resources/settings.xml b/plugin.video.mediathekview/resources/settings.xml
index a1ef62b..b61a0cc 100644
--- a/plugin.video.mediathekview/resources/settings.xml
+++ b/plugin.video.mediathekview/resources/settings.xml
@@ -2,12 +2,16 @@
<settings>
<category label="30001">
<setting id="firstrun" type="bool" default="true" visible="false" />
+ <setting id="instanceid" type="text" default="" visible="false" />
<setting id="quality" type="bool" label="30110" default="true" />
<setting id="nofuture" type="bool" label="30111" default="true" />
<setting id="minlength" type="slider" label="30112" default="0" range="0,30" />
<setting id="groupshows" type="bool" label="30113" default="true" />
<setting id="maxresults" type="slider" label="30114" default="1000" range="100,100,3000" option="int" />
- <setting id="downloadpath" type="folder" label="30115" source="auto" option="writeable" />
+ <setting id="maxage" type="slider" label="30115" default="2" range="1,30" option="int" />
+ <setting id="recentmode" type="enum" label="30116" default="0" lvalues="30161|30162" />
+
+ <setting id="downloadpath" type="folder" label="30118" source="auto" option="writeable" />
</category>
<category label="30002">
<setting id="dbtype" type="enum" label="30210" default="0" lvalues="30221|30222" />
@@ -18,7 +22,6 @@
<setting id="dbdata" type="text" label="30215" default="mediathekview" visible="eq(-5,1)" />
<setting id="updenabled" type="bool" label="30231" default="true" />
<setting id="updinterval" type="slider" label="30232" default="2" range="1,24" visible="eq(-1,true)" />
- <setting id="updxzbin" type="file" label="30233" default="" visible="eq(-2,true)" />
<!-- setting type="action" label="30238" action="RunScript(plugin.video.mediathekview, downloadreport)"/ -->
</category>
diff --git a/plugin.video.mediathekview/service.py b/plugin.video.mediathekview/service.py
index 933ce55..536b5cb 100644
--- a/plugin.video.mediathekview/service.py
+++ b/plugin.video.mediathekview/service.py
@@ -23,26 +23,20 @@
# SOFTWARE.
# -- Imports ------------------------------------------------
-from __future__ import unicode_literals # ,absolute_import, division
-
-import xbmc
+from __future__ import unicode_literals
from resources.lib.kodi.KodiAddon import KodiService
+from resources.lib.kodi.KodiAddon import KodiInterlockedMonitor
from resources.lib.notifier import Notifier
from resources.lib.settings import Settings
from resources.lib.updater import MediathekViewUpdater
# -- Classes ------------------------------------------------
-class MediathekViewMonitor( xbmc.Monitor ):
- def __init__( self, service ):
- super( MediathekViewMonitor, self ).__init__()
- self.service = service
+class MediathekViewMonitor( KodiInterlockedMonitor ):
+ def __init__( self, service, setting_id ):
+ super( MediathekViewMonitor, self ).__init__( service, setting_id )
self.logger = service.getNewLogger( 'Monitor' )
- self.logger.info( 'Startup' )
-
- def __del__( self ):
- self.logger.info( 'Shutdown' )
def onSettingsChanged( self ):
self.service.ReloadSettings()
@@ -53,15 +47,16 @@ class MediathekViewService( KodiService ):
self.setTopic( 'Service' )
self.settings = Settings()
self.notifier = Notifier()
- self.monitor = MediathekViewMonitor( self )
+ self.monitor = MediathekViewMonitor( self, 'instanceid' )
self.updater = MediathekViewUpdater( self.getNewLogger( 'Updater' ), self.notifier, self.settings, self.monitor )
def Init( self ):
- self.info( 'Init' )
+ self.info( 'Init (instance id: {})', self.monitor.instance_id )
+ self.monitor.RegisterInstance()
self.updater.Init()
def Run( self ):
- self.info( 'Starting up...' )
+ self.info( 'Starting up... (instance id: {})', self.monitor.instance_id )
while not self.monitor.abortRequested():
updateop = self.updater.GetCurrentUpdateOperation()
if updateop == 1:
@@ -76,11 +71,12 @@ class MediathekViewService( KodiService ):
if self.monitor.waitForAbort( 60 ):
# Abort was requested while waiting. We should exit
break
- self.info( 'Shutting down...' )
+ self.info( 'Shutting down... (instance id: {})', self.monitor.instance_id )
def Exit( self ):
- self.info( 'Exit' )
+ self.info( 'Exit (instance id: {})', self.monitor.instance_id )
self.updater.Exit()
+ self.monitor.UnregisterInstance()
def ReloadSettings( self ):
# TODO: support online reconfiguration