A 4 rokov pred
rodič
commit
2f198c161e

+ 0 - 0
Neues Textdokument.txt


+ 15 - 4
addon.xml

@@ -1,15 +1,26 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="plugin.video.cryflix_beta" name="[COLOR red]Cry[/COLOR]flix_beta" version="2.2.4">
+<addon id="plugin.video.cryflix_beta" name="[COLOR red]Plu[/COLOR]gin_beta" version="3.0.3">
     <requires>
-        <import addon="xbmc.python" version="2.1.0"/>
-        <import addon="script.module.simplejson" version="2.0.10"/>
-        <import addon="script.module.six" version="1.11.0"/>
         <import addon="script.module.requests" version="2.12.4"/>
+        <import addon="script.module.simplejson" optional="true"/>
+        <import addon="plugin.video.youtube" optional="true"/>
+        <import addon="inputstream.adaptive" optional="true"/>
     </requires>
     <extension point="xbmc.python.pluginsource" library="startup.py">
         <provides>video</provides>
     </extension>
     <extension point="xbmc.addon.metadata">
         <platform>all</platform>
+        <description lang="en">Version: 3.0.3
+Erstellt: 22.10.2020 - 18:17:47
+Release: beta</description>
+        <assets>
+            <icon>icon.png</icon>
+            <banner>icon.png</banner>
+            <clearlogo>icon.png</clearlogo>
+            <landscape>icon.png</landscape>
+            <thumb>icon.png</thumb>
+            <fanart>fanart.jpg</fanart>
+        </assets>
     </extension>
 </addon>

+ 13 - 4
addon_template.xml

@@ -1,15 +1,24 @@
 <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
-<addon id="plugin.video.cryflix%RELEASE%" name="[COLOR red]Cry[/COLOR]flix%RELEASE%" version="2.2.4">
+<addon id="plugin.video.cryflix%RELEASE%" name="[COLOR red]Plu[/COLOR]gin%RELEASE%" version="%VERSION%">
     <requires>
-        <import addon="xbmc.python" version="2.1.0"/>
-        <import addon="script.module.simplejson" version="2.0.10"/>
-        <import addon="script.module.six" version="1.11.0"/>
         <import addon="script.module.requests" version="2.12.4"/>
+        <import addon="script.module.simplejson" optional="true"/>
+        <import addon="plugin.video.youtube" optional="true"/>
+        <import addon="inputstream.adaptive" optional="true"/>
     </requires>
     <extension point="xbmc.python.pluginsource" library="startup.py">
         <provides>video</provides>
     </extension>
     <extension point="xbmc.addon.metadata">
         <platform>all</platform>
+        <description lang="en">Version: %VERSION%%NEWLINE%Erstellt: %DATE%%NEWLINE%Release: %CHANNEL%</description>
+        <assets>
+            <icon>icon.png</icon>
+            <banner>icon.png</banner>
+            <clearlogo>icon.png</clearlogo>
+            <landscape>icon.png</landscape>
+            <thumb>icon.png</thumb>
+            <fanart>fanart.jpg</fanart>
+        </assets>
     </extension>
 </addon>

+ 20 - 10
framework/__init__.py

@@ -7,12 +7,21 @@ import xbmcplugin
 import xbmcaddon
 import xbmcgui
 
-from common import enum
-from common import clean_dict
-from request import Request
-from listitem import ListItem
-from urls import UrlRule, NotFoundException, AmbiguousUrlException
-from storage import TimedStorage
+from .request import Request
+from .listitem import ListItem
+from .urls import UrlRule, NotFoundException, AmbiguousUrlException
+from .storage import TimedStorage
+
+if sys.version_info[0] >= 3:
+    unicode = str
+    basestring = (bytes, str)
+else:
+    long = int
+
+try:
+    from xbmcvfs import translatePath
+except ImportError:
+    from xbmc import translatePath
 
 
 class Plugin(object):
@@ -34,7 +43,7 @@ class Plugin(object):
         self._end_of_directory = False
         self._force_update = False
         self._rule = None
-        self._addondata_path = xbmc.translatePath('special://profile/addon_data/%s/' % self._addon_id)
+        self._addondata_path = translatePath('special://profile/addon_data/%s/' % self._addon_id)
         self._storage_path = os.path.join(self._addondata_path, ".storage/")
         if not os.path.isdir(self._storage_path):
             os.makedirs(self._storage_path)
@@ -241,6 +250,7 @@ class Plugin(object):
                               view_mode=view_mode,
                               goto_top=goto_top)
             return f
+
         return decorator
 
     def add_url_rule(self, url_rule, view_func, name,
@@ -329,7 +339,7 @@ class Plugin(object):
                     xbmc.sleep(self._delay - 75)
 
                 if self._goto_top and (not self._rule or self._rule.goto_top):
-                    xbmc.executebuiltin("XBMC.Action(firstpage)")
+                    xbmc.executebuiltin("Action(firstpage)")
 
                 if self._delay > 0:
                     xbmc.sleep(75)
@@ -390,13 +400,13 @@ class Plugin(object):
                     listitems = self.finish(listitems, cache_to_disc=rule.cache, update_listing=True if self._force_update else rule.update)
 
             return listitems, rule
-        raise NotFoundException, 'No matching view found for %s' % path
+        raise NotFoundException('No matching view found for %s' % path)
 
     def notify(self, msg='', title=None, delay=5000, image=''):
         if title is None:
             title = self._name
 
-        xbmc.executebuiltin('XBMC.Notification("%s", "%s", "%s", "%s")' % (msg, title, delay, image))
+        xbmc.executebuiltin(u'Notification("%s", "%s", "%s", "%s")' % (msg, title, delay, image))
 
     def dialog(self, msg="", title=None):
         if title is None:

+ 11 - 7
framework/common.py

@@ -7,8 +7,15 @@
     :copyright: (c) 2012 by Jonathan Beluch
     :license: GPLv3, see LICENSE for more details.
 '''
-import urllib
-import urllib2
+
+try:
+    import urllib.request as urllib_request  #for python 3
+    import urllib.parse as urllib_parse  #for python 3
+except ImportError:
+    import urllib2 as urllib_request  # for python 2
+    import urllib as urllib_parse
+
+
 try:
     import cPickle as pickle
 except ImportError:
@@ -20,7 +27,7 @@ def xbmc_url(url, **options):
     HTTP headers to XBMC to be used when fetching a media resource, e.g.
     cookies.
     '''
-    optionstring = urllib.urlencode(options)
+    optionstring = urllib_parse.urlencode(options)
     if optionstring:
         return url + '|' + optionstring
     return url
@@ -108,10 +115,7 @@ def unpickle_dict(items):
 def download_page(url, data=None):
     '''Returns the response for the given url. The optional data argument is
     passed directly to urlopen.'''
-    conn = urllib2.urlopen(url, data)
-    resp = conn.read()
-    conn.close()
-    return resp
+    return None
 
 
 _hextochr = dict(('%02x' % i, chr(i)) for i in range(256))

+ 1 - 1
framework/listitem.py

@@ -15,7 +15,7 @@ class ListItem(object):
             'label': label,
             'label2': label2,
             'iconImage': icon,
-            'thumbnailImage': thumbnail,
+            #'thumbnailImage': thumbnail,
             'path': path,
         }
         # kwargs = dict((key, val) for key, val in locals().items() if val is

+ 7 - 9
framework/request.py

@@ -1,13 +1,12 @@
-from common import unpickle_args
-import urlparse
+from .common import unpickle_args
+
 try:
-    from urlparse import parse_qs
+    import urllib.parse as urllib_parse  #for python 3
 except ImportError:
-    from cgi import parse_qs
+    import urlparse as urllib_parse
 
 
 class Request(object):
-
     def __init__(self, url, handle):
         #: The entire request url.
         self.url = url
@@ -18,7 +17,6 @@ class Request(object):
         # urlparse doesn't like the 'plugin' scheme, so pass a protocol
         # relative url, e.g. //plugin.video.helloxbmc/path
         self.scheme, remainder = url.split(':', 1)
-        parts = urlparse.urlparse(remainder)
-        self.netloc, self.path, self.query_string = (
-            parts[1], parts[2], parts[4])
-        self.args = unpickle_args(parse_qs(self.query_string))
+        parts = urllib_parse.urlparse(remainder)
+        self.netloc, self.path, self.query_string = (parts[1], parts[2], parts[4])
+        self.args = unpickle_args(urllib_parse.parse_qs(self.query_string))

+ 13 - 6
framework/storage.py

@@ -2,6 +2,7 @@ import os
 import csv
 import json
 import time
+
 try:
     import cPickle as pickle
 except ImportError:
@@ -14,9 +15,9 @@ from datetime import datetime
 class _PersistentDictMixin(object):
 
     def __init__(self, filename, flag='c', mode=None, file_format='pickle'):
-        self.flag = flag                    # r=readonly, c=create, or n=new
-        self.mode = mode                    # None or an octal triple like 0644
-        self.file_format = file_format      # 'csv', 'json', or 'pickle'
+        self.flag = flag  # r=readonly, c=create, or n=new
+        self.mode = mode  # None or an octal triple like 0644
+        self.file_format = file_format  # 'csv', 'json', or 'pickle'
         self.filename = filename
         if flag != 'n' and os.access(filename, os.R_OK):
             fileobj = open(filename, 'rb' if file_format == 'pickle' else 'r')
@@ -37,7 +38,15 @@ class _PersistentDictMixin(object):
             raise
         finally:
             fileobj.close()
-        shutil.move(tempname, self.filename)    # atomic commit
+        try:
+            os.remove(self.filename)
+        except:
+            pass
+        try:
+            shutil.copyfile(tempname, self.filename)  # atomic commit
+        except:
+            raise
+
         if self.mode is not None:
             os.chmod(self.filename, self.mode)
 
@@ -80,7 +89,6 @@ class _PersistentDictMixin(object):
 
 
 class _Storage(collections.MutableMapping, _PersistentDictMixin):
-
     '''Storage that acts like a dict but also can persist to disk.
 
     :param filename: An absolute filepath to reprsent the storage on disk. The
@@ -127,7 +135,6 @@ class _Storage(collections.MutableMapping, _PersistentDictMixin):
 
 
 class TimedStorage(_Storage):
-
     '''A dict with the ability to persist to disk and TTL for items.'''
 
     def __init__(self, filename, file_format='pickle', TTL=None):

+ 22 - 11
framework/urls.py

@@ -1,6 +1,19 @@
+from __future__ import unicode_literals
 import re
-from urllib import urlencode, unquote_plus, quote_plus
-from common import pickle_dict, unpickle_dict
+
+try:
+    from urllib.parse import urlencode, unquote_plus, quote_plus
+except ImportError:
+    from urllib import urlencode, unquote_plus, quote_plus
+from .common import pickle_dict, unpickle_dict
+
+import sys
+
+if sys.version_info[0] >= 3:
+    unicode = str
+    long = int
+    basestring = (str, bytes)
+
 
 # TODO: Use regular Exceptions
 
@@ -14,7 +27,6 @@ class NotFoundException(Exception):
 
 
 class UrlRule(object):
-
     '''This object stores the various properties related to a routing URL rule.
     It also provides a few methods to create URLs from the rule or to match a
     given URL against a rule.
@@ -59,15 +71,15 @@ class UrlRule(object):
 
         try:
             self._regex = re.compile('^' + p + '$')
-        except re.error, e:
-            raise ValueError, ('There was a problem creating this URL rule. '
-                               'Ensure you do not have any unpaired angle '
-                               'brackets: "<" or ">"')
+        except re.error as e:
+            raise ValueError('There was a problem creating this URL rule. '
+                             'Ensure you do not have any unpaired angle '
+                             'brackets: "<" or ">"')
 
     def __eq__(self, other):
         return (
-            (self._name, self._url_rule, self._view_func, self._options) ==
-            (other._name, other._url_rule, other._view_func, other._options)
+                (self._name, self._url_rule, self._view_func, self._options) ==
+                (other._name, other._url_rule, other._view_func, other._options)
         )
 
     def __ne__(self, other):
@@ -105,8 +117,7 @@ class UrlRule(object):
         '''
         for key, val in items.items():
             if not isinstance(val, basestring):
-                raise content_typeError, ('Value "%s" for key "%s" must be an instance'
-                                          ' of basestring' % (val, key))
+                raise Exception('Value "%s" for key "%s" must be an instance of basestring but is %s' % (val, key, str(type(val))))
             items[key] = quote_plus(val)
 
         try:

BIN
icon_beta.png


+ 14 - 0
languageconvert.py

@@ -0,0 +1,14 @@
+import re
+
+if __name__ == "__main__":
+    content = ""
+    with open("resources/language/english/strings.xml", "r") as f:
+        content = f.read()
+
+    strings = open("resources/language/english/strings.po", "w")
+    strings.write("msgid \"\"\nmsgstr \"\"\n\n")
+
+    for matchNum, match in enumerate(re.finditer(r'<string id="([^"]+)">(.+?)<\/string>', content, re.IGNORECASE), start=1):
+        strings.write("# %s\nmsgctxt \"#%s\"\nmsgid \"%s\"\nmsgstr \"\"\n\n" % (match.group(2), match.group(1), match.group(2)))
+
+    strings.close()

+ 29 - 20
make.bat

@@ -6,39 +6,48 @@ del "output/startup.py" >NUL 2>NUL
 echo do copy...
 cd "src"
 rem copy /b /Y playerwatcher.py+viewids.py+consts.py+compressedcookielib.py+utils.py+portal.py+api.py+plugin.py "..\out.py" >NUL 2>NUL
-copy /b /Y viewids.py+watchdog.py+utils.py+consts.py+compressedcookielib.py+utils.py+api.py+plugin.py "..\out.py" >NUL 2>NUL
+copy /b /Y viewids.py+utils.py+consts.py+playserver.py+api.py+plugin.py "..\out.py" >NUL 2>NUL
 cd "..\"
-copy /b /Y utf8.txt+out.py "tmp.py"
-copy /b /Y tmp.py "out.py"
 timeout /t 1 >NUL 2>NUL
 echo do replace...
 python "replace.py" "out.py" "stable"
 echo do minify...
-copy /Y "out.py" "output/plugin.py" >NUL 2>NUL
-copy /Y "startup.py" "output/startup.py" >NUL 2>NUL
-rem D:\dev\Python27\Scripts\pyminifier --obfuscate-variables --obfuscate-classes --obfuscate-import-methods --prepend=utf8.txt -o "output/plugin_pure.py" out.py
+copy /Y /b out.py "tmp.py" >NUL 2>NUL
+copy /Y /b "startup.py" "output/startup.py" >NUL 2>NUL
+python pypreprocessor.py "tmp.py"
+python languageconvert.py
+copy /b /Y "tmp_out.py" "out.py"
+copy /b /Y "out.py" "tmp.py"
+C:\Python27\Scripts\pyminifier --obfuscate-variables --obfuscate-classes --obfuscate-functions --gzip_modified --prepend=utf8.txt -o tmp.py out.py
+rem C:\Python27\Scripts\pyminifier --obfuscate-variables --obfuscate-classes --obfuscate-functions --prepend=utf8_stable.txt -o tmp.py out.py
+rem C:\Python27\Scripts\pyminifier --obfuscate-variables --obfuscate-classes --obfuscate-functions --prepend=utf8_stable.txt -o tmp.py out.py
 rem D:\dev\Python27\Scripts\pyminifier --obfuscate-variables --obfuscate-classes --obfuscate-import-methods --gzip_modified --prepend=utf8.txt -o "output/plugin.py" out.py
-rem copy /b /Y "utf8.txt"+"src\startup.py" "output\startup.py" >NUL 2>NUL
 rem D:\dev\Python27\Scripts\pyminifier --obfuscate-variables --obfuscate-classes --obfuscate-import-methods --gzip_modified --prepend=utf8.txt -o "output/startup.py" "src/startup.py"
+copy /b /Y "tmp.py" "output/plugin.py"
 echo do cleanup...
 
 DEL /Q /F /S "*.pyc" >NUL 2>NUL
+del tmp.py >NUL 2>NUL
 del out.py >NUL 2>NUL
 
-rd /s /q "../Repo/source/stable/resources" >NUL 2>NUL
-rd /s /q "../Repo/source/stable/framework" >NUL 2>NUL
+rd /s /q "C:\Users\SevenX\Dropbox\Crynet\Repo/source/stable/resources" >NUL 2>NUL
+rd /s /q "C:\Users\SevenX\Dropbox\Crynet\Repo/source/stable/framework" >NUL 2>NUL
 
-echo V | xcopy "resources" /f /y /s "../Repo/source/stable/resources" >NUL 2>NUL
-echo V | xcopy "framework" /f /y /s "..//Repo/source/stable/framework" >NUL 2>NUL
-echo D | xcopy "output/startup.py" /f /y "../Repo/source/stable/startup.py" >NUL 2>NUL
-echo D | xcopy "output/plugin.py" /f /y "../Repo/source/stable/plugin.py" >NUL 2>NUL
-echo D | xcopy "addon.xml" /f /y "../Repo/source/stable/addon.xml" >NUL 2>NUL
-echo D | xcopy "fanart.jpg" /f /y "../Repo/source/stable/fanart.jpg" >NUL 2>NUL
-echo D | xcopy "icon.png" /f /y "../Repo/source/stable/icon.png" >NUL 2>NUL
-echo D | xcopy "changelog.txt" /f /y "../Repo/source/stable/changelog.txt" >NUL 2>NUL
+md "resources/language/resource.language.en_us" >NUL 2>NUL
+echo V | xcopy "resources/language/english/strings.po" /f /y /s "resources/language/resource.language.en_us" >NUL 2>NUL
+echo V | xcopy "resources/language/resource.language.en_us" /f /y /s "resources/language/resource.language.en_gb" >NUL 2>NUL
 
+echo V | xcopy "resources" /f /y /s "C:\Users\SevenX\Dropbox\Crynet\Repo/source/stable/resources" >NUL 2>NUL
+echo V | xcopy "framework" /f /y /s "C:\Users\SevenX\Dropbox\Crynet\Repo/source/stable/framework" >NUL 2>NUL
+echo D | xcopy "output/startup.py" /f /y "C:\Users\SevenX\Dropbox\Crynet\Repo/source/stable/startup.py" >NUL 2>NUL
+echo D | xcopy "output/plugin.py" /f /y "C:\Users\SevenX\Dropbox\Crynet\Repo/source/stable/plugin.py" >NUL 2>NUL
+echo D | xcopy "addon.xml" /f /y "C:\Users\SevenX\Dropbox\Crynet\Repo/source/stable/addon.xml" >NUL 2>NUL
+echo D | xcopy "fanart.jpg" /f /y "C:\Users\SevenX\Dropbox\Crynet\Repo/source/stable/fanart.jpg" >NUL 2>NUL
+echo D | xcopy "icon.png" /f /y "C:\Users\SevenX\Dropbox\Crynet\Repo/source/stable/icon.png" >NUL 2>NUL
+echo D | xcopy "changelog.txt" /f /y "C:\Users\SevenX\Dropbox\Crynet\Repo/source/stable/changelog.txt" >NUL 2>NUL
+
+echo V | xcopy "resources" /f /y /s "C:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix/resources" >NUL 2>NUL
 echo D | xcopy "addon.xml" /f /y "C:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix/addon.xml" >NUL 2>NUL
 echo D | xcopy "output/plugin.py" /f /y "C:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix/plugin.py" >NUL 2>NUL
-echo D | xcopy "output/startup.py" /f /y "DC:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix/startup.py" >NUL 2>NUL
-
-rem pause
+echo D | xcopy "output/startup.py" /f /y "C:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix/startup.py" >NUL 2>NUL
+echo D | xcopy "resources/settings.xml" /f /y "C:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix/resources/settings.xml" >NUL 2>NUL

+ 0 - 42
make.sh

@@ -1,42 +0,0 @@
-#!/bin/sh
-mkdir -p output 2>&1 1>/dev/null
-rm -f out.py 2>&1 1>/dev/null
-rm -f "output/plugin.py" 2>&1 1>/dev/null
-rm -f "output/startup.py" 2>&1 1>/dev/null
-echo "do copy..."
-cd src
-cat uuid.py playerwatcher2.py viewids.py consts.py compressedcookielib.py utils.py portal.py api.py plugin.py >> "../out.py"
-cd ../
-echo "do replace..."
-#./replace 2>&1 > /dev/null
-python "replace.py" "out.py"
-echo "do minify..."
-cp -f "out.py" "output/plugin.py"
-cp -f "src/startup.py" "output/startup.py"
-cat utf8.txt "out.py" > "output/plugin.py"
-#/usr/local/bin/pyminifier --obfuscate-variables --obfuscate-classes --obfuscate-functions --prepend=utf8.txt -o "output/plugin_pure.py" "out.py" 2>&1 >/dev/null
-#/usr/local/bin/pyminifier --obfuscate-variables --obfuscate-classes --obfuscate-functions --prepend=utf8.txt -o "output/plugin.py" "out.py" 2>&1 >/dev/null
-#/usr/local/bin/pyminifier --obfuscate-variables --obfuscate-classes --obfuscate-functions --gzip_modified --prepend=utf8.txt -o "output/plugin.py" "out.py" 2>&1 >/dev/null
-cat utf8.txt "src/startup.py" > "output/startup.py"
-#/usr/local/bin/pyminifier --obfuscate-variables --obfuscate-classes --obfuscate-import-methods --gzip_modified --prepend=utf8.txt -o "output/startup.py" "src/startup.py" 2>&1 >/dev/null
-echo "do cleanup..."
-rm -f out.py
-
-rm -rf "../Repo/Source/resources"
-rm -rf "../Repo/Source/framework"
-rm -rf "../Repo/Source/mechanize"
-
-cp -fr "resources" "../Repo/Source/resources"
-cp -fr "framework" "../Repo/Source/framework"
-cp -fr "mechanize" "../Repo/Source/mechanize"
-cp -f "output/startup.py" "../Repo/Source/startup.py"
-cp -f "output/plugin.py" "../Repo/Source/plugin.py"
-cp -f "addon.xml" "../Repo/Source/addon.xml"
-cp -f "fanart.jpg" "../Repo/Source/fanart.jpg" 2>&1 1>/dev/null
-cp -f "icon.png" "../Repo/Source/icon.png" 2>&1 1>/dev/null 2>&1 1>/dev/null
-cp -f "changelog.txt" "../Repo/Source/changelog.txt" 2>&1 1>/dev/null
-
-cp -fR "resources" "/Users/benjamin/Library/Application Support/Kodi/addons/plugin.video.cryflix/"
-cp -f "addon.xml" "/Users/benjamin/Library/Application Support/Kodi/addons/plugin.video.cryflix/addon.xml"
-cp -f "output/plugin.py" "/Users/benjamin/Library/Application Support/Kodi/addons/plugin.video.cryflix/plugin.py"
-cp -f "output/startup.py" "/Users/benjamin/Library/Application Support/Kodi/addons/plugin.video.cryflix/startup.py"

+ 10 - 3
make_beta.bat

@@ -6,7 +6,7 @@ del "output/startup.py" >NUL 2>NUL
 echo do copy...
 cd "src"
 rem copy /b /Y playerwatcher.py+viewids.py+consts.py+compressedcookielib.py+utils.py+portal.py+api.py+plugin.py "..\out.py" >NUL 2>NUL
-copy /b /Y viewids.py+utils.py+consts.py+playserver.py+compressedcookielib.py+api.py+plugin.py "..\out.py" >NUL 2>NUL
+copy /b /Y viewids.py+utils.py+consts.py+playserver.py+api.py+plugin.py "..\out.py" >NUL 2>NUL
 cd "..\"
 timeout /t 1 >NUL 2>NUL
 echo do replace...
@@ -14,7 +14,8 @@ python "replace.py" "out.py" "beta"
 echo do minify...
 copy /Y /b out.py "tmp.py" >NUL 2>NUL
 copy /Y /b "startup.py" "output/startup.py" >NUL 2>NUL
-python pypreprocessor.py "tmp.py"
+C:\Python27\python pypreprocessor.py "tmp.py"
+C:\Python27\python languageconvert.py
 copy /b /Y "utf8_beta.txt"+"tmp_out.py" "out.py"
 copy /b /Y "out.py" "tmp.py"
 C:\Python27\Scripts\pyminifier --obfuscate-variables --obfuscate-classes --obfuscate-functions --gzip_modified --prepend=utf8_beta.txt -o tmp.py out.py
@@ -32,18 +33,24 @@ del out.py >NUL 2>NUL
 rd /s /q "C:\Users\SevenX\Dropbox\Crynet\Repo/source/beta/resources" >NUL 2>NUL
 rd /s /q "C:\Users\SevenX\Dropbox\Crynet\Repo/source/beta/framework" >NUL 2>NUL
 
+md "resources/language/resource.language.en_us" >NUL 2>NUL
+echo V | xcopy "resources/language/english/strings.po" /f /y /s "resources/language/resource.language.en_us" >NUL 2>NUL
+echo V | xcopy "resources/language/resource.language.en_us" /f /y /s "resources/language/resource.language.en_gb" >NUL 2>NUL
+
 echo V | xcopy "resources" /f /y /s "C:\Users\SevenX\Dropbox\Crynet\Repo/source/beta/resources" >NUL 2>NUL
 echo V | xcopy "framework" /f /y /s "C:\Users\SevenX\Dropbox\Crynet\Repo/source/beta/framework" >NUL 2>NUL
 echo D | xcopy "output/startup.py" /f /y "C:\Users\SevenX\Dropbox\Crynet\Repo/source/beta/startup.py" >NUL 2>NUL
 echo D | xcopy "output/plugin.py" /f /y "C:\Users\SevenX\Dropbox\Crynet\Repo/source/beta/plugin.py" >NUL 2>NUL
 echo D | xcopy "addon.xml" /f /y "C:\Users\SevenX\Dropbox\Crynet\Repo/source/beta/addon.xml" >NUL 2>NUL
 echo D | xcopy "fanart.jpg" /f /y "C:\Users\SevenX\Dropbox\Crynet\Repo/source/beta/fanart.jpg" >NUL 2>NUL
-echo D | xcopy "icon.png" /f /y "C:\Users\SevenX\Dropbox\Crynet\Repo/source/beta/icon.png" >NUL 2>NUL
+echo D | xcopy "icon_beta.png" /f /y "C:\Users\SevenX\Dropbox\Crynet\Repo/source/beta/icon.png" >NUL 2>NUL
 echo D | xcopy "changelog.txt" /f /y "C:\Users\SevenX\Dropbox\Crynet\Repo/source/beta/changelog.txt" >NUL 2>NUL
 
+echo V | xcopy "resources" /f /y /s "C:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix_beta/resources" >NUL 2>NUL
 echo D | xcopy "addon.xml" /f /y "C:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix_beta/addon.xml" >NUL 2>NUL
 echo D | xcopy "output/plugin.py" /f /y "C:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix_beta/plugin.py" >NUL 2>NUL
 echo D | xcopy "output/startup.py" /f /y "C:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix_beta/startup.py" >NUL 2>NUL
 echo D | xcopy "resources/settings.xml" /f /y "C:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix_beta/resources/settings.xml" >NUL 2>NUL
+echo D | xcopy "icon_beta.png" /f /y "C:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix_beta/icon.png" >NUL 2>NUL
 
 rem pause

+ 44 - 0
make_old.bat

@@ -0,0 +1,44 @@
+@echo off
+md output >NUL 2>NUL
+del out.py >NUL 2>NUL
+del "output/plugin.py" >NUL 2>NUL
+del "output/startup.py" >NUL 2>NUL
+echo do copy...
+cd "src"
+rem copy /b /Y playerwatcher.py+viewids.py+consts.py+compressedcookielib.py+utils.py+portal.py+api.py+plugin.py "..\out.py" >NUL 2>NUL
+copy /b /Y viewids.py+watchdog.py+utils.py+consts.py+compressedcookielib.py+utils.py+api.py+plugin.py "..\out.py" >NUL 2>NUL
+cd "..\"
+copy /b /Y utf8.txt+out.py "tmp.py"
+copy /b /Y tmp.py "out.py"
+timeout /t 1 >NUL 2>NUL
+echo do replace...
+python "replace.py" "out.py" "stable"
+echo do minify...
+copy /Y "out.py" "output/plugin.py" >NUL 2>NUL
+copy /Y "startup.py" "output/startup.py" >NUL 2>NUL
+rem D:\dev\Python27\Scripts\pyminifier --obfuscate-variables --obfuscate-classes --obfuscate-import-methods --prepend=utf8.txt -o "output/plugin_pure.py" out.py
+rem D:\dev\Python27\Scripts\pyminifier --obfuscate-variables --obfuscate-classes --obfuscate-import-methods --gzip_modified --prepend=utf8.txt -o "output/plugin.py" out.py
+rem copy /b /Y "utf8.txt"+"src\startup.py" "output\startup.py" >NUL 2>NUL
+rem D:\dev\Python27\Scripts\pyminifier --obfuscate-variables --obfuscate-classes --obfuscate-import-methods --gzip_modified --prepend=utf8.txt -o "output/startup.py" "src/startup.py"
+echo do cleanup...
+
+DEL /Q /F /S "*.pyc" >NUL 2>NUL
+del out.py >NUL 2>NUL
+
+rd /s /q "../Repo/source/stable/resources" >NUL 2>NUL
+rd /s /q "../Repo/source/stable/framework" >NUL 2>NUL
+
+echo V | xcopy "resources" /f /y /s "../Repo/source/stable/resources" >NUL 2>NUL
+echo V | xcopy "framework" /f /y /s "..//Repo/source/stable/framework" >NUL 2>NUL
+echo D | xcopy "output/startup.py" /f /y "../Repo/source/stable/startup.py" >NUL 2>NUL
+echo D | xcopy "output/plugin.py" /f /y "../Repo/source/stable/plugin.py" >NUL 2>NUL
+echo D | xcopy "addon.xml" /f /y "../Repo/source/stable/addon.xml" >NUL 2>NUL
+echo D | xcopy "fanart.jpg" /f /y "../Repo/source/stable/fanart.jpg" >NUL 2>NUL
+echo D | xcopy "icon.png" /f /y "../Repo/source/stable/icon.png" >NUL 2>NUL
+echo D | xcopy "changelog.txt" /f /y "../Repo/source/stable/changelog.txt" >NUL 2>NUL
+
+echo D | xcopy "addon.xml" /f /y "C:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix/addon.xml" >NUL 2>NUL
+echo D | xcopy "output/plugin.py" /f /y "C:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix/plugin.py" >NUL 2>NUL
+echo D | xcopy "output/startup.py" /f /y "DC:\Users\SevenX\AppData\Roaming\Kodi\addons\plugin.video.cryflix/startup.py" >NUL 2>NUL
+
+rem pause

+ 12 - 0
pyminifier-script.py

@@ -0,0 +1,12 @@
+#!C:\Python27\python.exe
+# EASY-INSTALL-ENTRY-SCRIPT: 'pyminifier==2.5','console_scripts','pyminifier'
+__requires__ = 'pyminifier==2.5'
+import re
+import sys
+from pkg_resources import load_entry_point
+
+if __name__ == '__main__':
+    sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0])
+    sys.exit(
+        load_entry_point('pyminifier==2.5', 'console_scripts', 'pyminifier')()
+    )

+ 27 - 2
replace.py

@@ -1,17 +1,37 @@
 import sys
 from pprint import pprint
 import re
+import datetime
+
+VERSION_PATTERN = re.compile(r'^PLUGIN_VERSION\s+=\s+"([^"]+)"$', re.MULTILINE)
 
 if __name__ == "__main__":
     channel = sys.argv[2] if len(sys.argv) == 3 else "stable"
 
+    now = datetime.datetime.now()
+    date = now.strftime("%d.%m.%Y - %H:%M:%S")
+
     # Addon
+    with open("src/consts.py", "r") as f:
+        version_match = VERSION_PATTERN.search(f.read())
+        if version_match:
+            VERSION = version_match.group(1)
+        else:
+            VERSION = "0.0.0"
+
     content = ""
     with open("addon_template.xml", "r") as f:
         content = f.read()
 
     with open("addon.xml", "w") as f:
-        f.write(content.replace("%RELEASE%", "" if channel == "stable" else "_beta"))
+        f.write(
+            content
+                .replace("%RELEASE%", "" if channel == "stable" else "_beta")
+                .replace("%CHANNEL%", channel)
+                .replace("%NEWLINE%", "\n")
+                .replace("%VERSION%", VERSION)
+                .replace("%DATE%", date)
+        )
 
     # Settings
     content = ""
@@ -19,7 +39,12 @@ if __name__ == "__main__":
         content = f.read()
 
     with open("resources/settings.xml", "w") as f:
-        f.write(content.replace("%RELEASE%", "" if channel == "stable" else "_beta"))
+        f.write(content
+                .replace("%RELEASE%", "" if channel == "stable" else "_beta")
+                .replace("%CHANNEL%", channel)
+                .replace("%NEWLINE%", "\n")
+                .replace("%VERSION%", VERSION)
+                .replace("%DATE%", date))
 
     content = ""
     with open(sys.argv[1], "r") as f:

+ 78 - 0
resources/language/english/strings.po

@@ -0,0 +1,78 @@
+msgid ""
+msgstr ""
+
+# Liste
+msgctxt "#32001"
+msgid "Liste"
+msgstr ""
+
+# Thumbnail
+msgctxt "#32002"
+msgid "Thumbnail"
+msgstr ""
+
+# Informationen
+msgctxt "#32003"
+msgid "Informationen"
+msgstr ""
+
+# Manuell
+msgctxt "#32004"
+msgid "Manuell"
+msgstr ""
+
+# Eingetragen
+msgctxt "#32005"
+msgid "Eingetragen"
+msgstr ""
+
+# Jahr
+msgctxt "#32006"
+msgid "Jahr"
+msgstr ""
+
+# IMDB Wertung
+msgctxt "#32007"
+msgid "IMDB Wertung"
+msgstr ""
+
+# Länge
+msgctxt "#32008"
+msgid "Länge"
+msgstr ""
+
+# Abwärts
+msgctxt "#32009"
+msgid "Abwärts"
+msgstr ""
+
+# Aufwärts
+msgctxt "#32010"
+msgid "Aufwärts"
+msgstr ""
+
+# 1
+msgctxt "#32018"
+msgid "1"
+msgstr ""
+
+# 2
+msgctxt "#32019"
+msgid "2"
+msgstr ""
+
+# 3
+msgctxt "#32020"
+msgid "3"
+msgstr ""
+
+# 4
+msgctxt "#32021"
+msgid "4"
+msgstr ""
+
+# 5
+msgctxt "#32022"
+msgid "5"
+msgstr ""
+

+ 0 - 8
resources/language/english/strings.xml

@@ -13,14 +13,6 @@
 	<string id="32009">Abwärts</string>
 	<string id="32010">Aufwärts</string>
 
-	<string id="32011">Alle</string>
-	<string id="32012">HD &amp; Serien</string>
-	<string id="32013">HD &amp; 3D</string>
-	<string id="32014">Serien &amp; 3D</string>
-	<string id="32015">Nur HD</string>
-	<string id="32016">Nur Serien</string>
-	<string id="32017">Nur 3D</string>
-
 	<string id="32018">1</string>
 	<string id="32019">2</string>
 	<string id="32020">3</string>

+ 78 - 0
resources/language/resource.language.en_gb/strings.po

@@ -0,0 +1,78 @@
+msgid ""
+msgstr ""
+
+# Liste
+msgctxt "#32001"
+msgid "Liste"
+msgstr ""
+
+# Thumbnail
+msgctxt "#32002"
+msgid "Thumbnail"
+msgstr ""
+
+# Informationen
+msgctxt "#32003"
+msgid "Informationen"
+msgstr ""
+
+# Manuell
+msgctxt "#32004"
+msgid "Manuell"
+msgstr ""
+
+# Eingetragen
+msgctxt "#32005"
+msgid "Eingetragen"
+msgstr ""
+
+# Jahr
+msgctxt "#32006"
+msgid "Jahr"
+msgstr ""
+
+# IMDB Wertung
+msgctxt "#32007"
+msgid "IMDB Wertung"
+msgstr ""
+
+# Länge
+msgctxt "#32008"
+msgid "Länge"
+msgstr ""
+
+# Abwärts
+msgctxt "#32009"
+msgid "Abwärts"
+msgstr ""
+
+# Aufwärts
+msgctxt "#32010"
+msgid "Aufwärts"
+msgstr ""
+
+# 1
+msgctxt "#32018"
+msgid "1"
+msgstr ""
+
+# 2
+msgctxt "#32019"
+msgid "2"
+msgstr ""
+
+# 3
+msgctxt "#32020"
+msgid "3"
+msgstr ""
+
+# 4
+msgctxt "#32021"
+msgid "4"
+msgstr ""
+
+# 5
+msgctxt "#32022"
+msgid "5"
+msgstr ""
+

+ 78 - 0
resources/language/resource.language.en_us/strings.po

@@ -0,0 +1,78 @@
+msgid ""
+msgstr ""
+
+# Liste
+msgctxt "#32001"
+msgid "Liste"
+msgstr ""
+
+# Thumbnail
+msgctxt "#32002"
+msgid "Thumbnail"
+msgstr ""
+
+# Informationen
+msgctxt "#32003"
+msgid "Informationen"
+msgstr ""
+
+# Manuell
+msgctxt "#32004"
+msgid "Manuell"
+msgstr ""
+
+# Eingetragen
+msgctxt "#32005"
+msgid "Eingetragen"
+msgstr ""
+
+# Jahr
+msgctxt "#32006"
+msgid "Jahr"
+msgstr ""
+
+# IMDB Wertung
+msgctxt "#32007"
+msgid "IMDB Wertung"
+msgstr ""
+
+# Länge
+msgctxt "#32008"
+msgid "Länge"
+msgstr ""
+
+# Abwärts
+msgctxt "#32009"
+msgid "Abwärts"
+msgstr ""
+
+# Aufwärts
+msgctxt "#32010"
+msgid "Aufwärts"
+msgstr ""
+
+# 1
+msgctxt "#32018"
+msgid "1"
+msgstr ""
+
+# 2
+msgctxt "#32019"
+msgid "2"
+msgstr ""
+
+# 3
+msgctxt "#32020"
+msgid "3"
+msgstr ""
+
+# 4
+msgctxt "#32021"
+msgid "4"
+msgstr ""
+
+# 5
+msgctxt "#32022"
+msgid "5"
+msgstr ""
+

+ 13 - 13
resources/settings.xml

@@ -11,20 +11,20 @@
         <setting id="deviceidentifier" label="Gerätekennung ([B]mind. 2 Zeichen[/B])" type="text"/>
 
         <setting label=" " type="lsep"/>
-        <setting label="Verbliebene Laufzeit" type="action" action='RunPlugin("plugin://plugin.video.cryflix_beta/account_status/")' visible="false"/>
+        <setting label="Verbliebene Laufzeit" type="action" action='RunPlugin("plugin://plugin.video.cryflix_beta/account_status/")' />
     </category>
     <category label="Plugin">
-        <setting id="viewlist" default="0" type="select" label="Listen View" lvalues="32001|32002|32003|32004" value="0|1|2|3"/>
-        <setting id="viewlist_man" type="number" label="Listen View ID" default="57" visible="eq(-1,Manuell)" subsetting="true"/>
+        <setting id="viewlist" default="0" type="select" label="Listen View" lvalues="32002|32002|32003|32004" value="0|1|2|3"/>
+        <setting id="viewlist_man" type="number" label="Listen View ID" default="57" subsetting="true"/>
 
         <setting id="viewthumbnail" default="1" type="select" label="Filme View" lvalues="32001|32002|32003|32004" value="0|1|2|3"/>
-        <setting id="viewthumbnail_man" type="number" label="Filme View ID" default="500" visible="eq(-1,Manuell)" subsetting="true"/>
+        <setting id="viewthumbnail_man" type="number" label="Filme View ID" default="500" subsetting="true"/>
 
         <setting id="viewepisode" default="2" type="select" label="Episoden View" lvalues="32001|32002|32003|32004" value="0|1|2|3"/>
-        <setting id="viewepisode_man" type="number" label="Episoden View ID" default="503" visible="eq(-1,Manuell)" subsetting="true"/>
+        <setting id="viewepisode_man" type="number" label="Episoden View ID" default="503" subsetting="true"/>
 
         <setting id="viewwatchlist" default="1" type="select" label="Watchlist View" lvalues="32001|32002|32003|32004" value="0|1|2|3"/>
-        <setting id="viewwatchlist_man" type="number" label="Watchlist View ID" default="500" visible="eq(-1,Manuell)" subsetting="true"/>
+        <setting id="viewwatchlist_man" type="number" label="Watchlist View ID" default="500" subsetting="true"/>
 
         <setting label=" " type="lsep"/>
         <setting id="orderby_field" default="0" type="select" label="Sortierung" lvalues="32005|32006|32007|32008" value="0|1|2|3"/>
@@ -36,7 +36,7 @@
         <setting id="watchedlistid" default="0" type="select" label="Watchedlist Nummer" lvalues="32018|32019|32020|32021" value="0|1|2|3" visible="false"/>
 
         <setting label=" " type="lsep"/>
-        <setting id="shownewcomer" type="bool" label="Menüpunkt '[B]Neueinsteiger[/B]' anzeigen" default="false"/>
+        <setting id="shownewcomer" type="bool" label="Menüpunkt '[B]Neueinsteiger[/B]' anzeigen" default="true"/>
         <setting id="moviesperpage" type="number" label="Filme pro Seite ([B]1-150[/B])" default="50"/>
         <setting id="usetagline" type="bool" label="Tagline für Informationen nutzen" default="true"/>
         <setting id="switchtagline" type="bool" label="Titel und Tagline vertauschen" default="false"/>
@@ -44,9 +44,8 @@
         <setting label=" " type="lsep"/>
         <setting id="colorline" type="text" label="Farbe für Cinelines" default="red"/>
         <setting id="coloruhd" type="text" label="Farbe für UHD" default="yellow"/>
-        <setting id="askbeforeplay" type="bool" label="Fragen ob der Film gestartet werden soll" default="true"/>
+        <setting id="askbeforeplay" type="bool" label="Fragen ob der Film gestartet werden soll" default="false"/>
         <setting id="askfornextepisode" type="bool" label="Fragen ob die nächste Episode gestartet werden soll" default="true"/>
-        <setting id="viewtypes" default="0" type="select" label="Einträge Filtern nach" lvalues="32011|32012|32013|32014|32015|32016|32017" value="0|1|2|3|4|5|6"/>
     </category>
     <!--
     <category label="Jugendschutz">
@@ -75,10 +74,11 @@
 
         <setting label=" " type="lsep" visible="eq(-3,true)"/>
         <setting label="Storage leeren" type="action" action='RunPlugin("plugin://plugin.video.cryflix_beta/clear_storage/")' visible="eq(-4,true)"/>
+        <setting label="Playserver beenden" type="action" action='RunPlugin("plugin://plugin.video.cryflix_beta/stop_playserver/")' visible="eq(-5,true)"/>
 
-        <setting label=" " type="lsep" visible="eq(-5,true)"/>
-        <setting id="usesimplejson" type="bool" label="SimpleJSON anstelle von JSON nutzen" default="false" visible="eq(-6,true)"/>
-        <setting id="gototop" type="bool" label="Automatisch an den Anfang springen" default="false" visible="eq(-7,true)"/>
-        <setting id="directorydelay" type="number" label="Verzögerung Aufbau ([B]Millisekunden[/B])" default="350" visible="eq(-8,true)"/>
+        <setting label=" " type="lsep" visible="eq(-6,true)"/>
+        <setting id="usesimplejson" type="bool" label="SimpleJSON anstelle von JSON nutzen" default="false" visible="eq(-7,true)"/>
+        <setting id="gototop" type="bool" label="Automatisch an den Anfang springen" default="false" visible="eq(-8,true)"/>
+        <setting id="directorydelay" type="number" label="Verzögerung Aufbau ([B]Millisekunden[/B])" default="350" visible="eq(-9,true)"/>
     </category>
 </settings>

+ 6 - 7
settings_template.xml

@@ -11,20 +11,20 @@
         <setting id="deviceidentifier" label="Gerätekennung ([B]mind. 2 Zeichen[/B])" type="text"/>
 
         <setting label=" " type="lsep"/>
-        <setting label="Verbliebene Laufzeit" type="action" action='RunPlugin("plugin://plugin.video.cryflix%RELEASE%/account_status/")' visible="false"/>
+        <setting label="Verbliebene Laufzeit" type="action" action='RunPlugin("plugin://plugin.video.cryflix%RELEASE%/account_status/")' />
     </category>
     <category label="Plugin">
-        <setting id="viewlist" default="0" type="select" label="Listen View" lvalues="32001|32002|32003|32004" value="0|1|2|3"/>
-        <setting id="viewlist_man" type="number" label="Listen View ID" default="57" visible="eq(-1,Manuell)" subsetting="true"/>
+        <setting id="viewlist" default="0" type="select" label="Listen View" lvalues="32002|32002|32003|32004" value="0|1|2|3"/>
+        <setting id="viewlist_man" type="number" label="Listen View ID" default="57" subsetting="true"/>
 
         <setting id="viewthumbnail" default="1" type="select" label="Filme View" lvalues="32001|32002|32003|32004" value="0|1|2|3"/>
-        <setting id="viewthumbnail_man" type="number" label="Filme View ID" default="500" visible="eq(-1,Manuell)" subsetting="true"/>
+        <setting id="viewthumbnail_man" type="number" label="Filme View ID" default="500" subsetting="true"/>
 
         <setting id="viewepisode" default="2" type="select" label="Episoden View" lvalues="32001|32002|32003|32004" value="0|1|2|3"/>
-        <setting id="viewepisode_man" type="number" label="Episoden View ID" default="503" visible="eq(-1,Manuell)" subsetting="true"/>
+        <setting id="viewepisode_man" type="number" label="Episoden View ID" default="503" subsetting="true"/>
 
         <setting id="viewwatchlist" default="1" type="select" label="Watchlist View" lvalues="32001|32002|32003|32004" value="0|1|2|3"/>
-        <setting id="viewwatchlist_man" type="number" label="Watchlist View ID" default="500" visible="eq(-1,Manuell)" subsetting="true"/>
+        <setting id="viewwatchlist_man" type="number" label="Watchlist View ID" default="500" subsetting="true"/>
 
         <setting label=" " type="lsep"/>
         <setting id="orderby_field" default="0" type="select" label="Sortierung" lvalues="32005|32006|32007|32008" value="0|1|2|3"/>
@@ -46,7 +46,6 @@
         <setting id="coloruhd" type="text" label="Farbe für UHD" default="yellow"/>
         <setting id="askbeforeplay" type="bool" label="Fragen ob der Film gestartet werden soll" default="false"/>
         <setting id="askfornextepisode" type="bool" label="Fragen ob die nächste Episode gestartet werden soll" default="true"/>
-        <setting id="viewtypes" default="0" type="select" label="Einträge Filtern nach" lvalues="32011|32012|32013|32014|32015|32016|32017" value="0|1|2|3|4|5|6"/>
     </category>
     <!--
     <category label="Jugendschutz">

+ 24 - 11
src/api.py

@@ -3,7 +3,6 @@
 
 from consts import *
 from utils import *
-import pprint
 
 
 class __api__(object):
@@ -17,14 +16,14 @@ class __api__(object):
 
             elif __json__["errno"] in (-1, -2, -3, -8):
                 hide_busy_dialog()
-                PLUGIN.notify(u"Version [B]%s[/B] ist nicht gültig!" % PLUGIN_VERSION)
-                output("[API] Version not valid")
+                output("[API] Version not valid, err code %s" % __json__["errno"])
                 clear_session()
 
-                xbmc.executebuiltin('Dialog.Close(busydialog)')
-                xbmc.executebuiltin('XBMC.Container.Update("library://video/addons.xml/",replace)')
-                xbmc.executebuiltin("XBMC.ActivateWindow(Home)")
-                sys.exit(-1)
+                hide_busy_dialog()
+                xbmc.executebuiltin('Container.Update("library://video/addons.xml/",replace)')
+                #xbmc.executebuiltin("ActivateWindow(Home)")
+                PLUGIN.dialog(u"Leider ist die Version [B]%s[/B] ist nicht gültig!" % PLUGIN_VERSION)
+                sys.exit(1)
                 return False
 
             else:
@@ -53,7 +52,7 @@ class __api__(object):
 
         try:
             response = request.__execute__()
-        except urllib2.HTTPError as e:
+        except urllib_request.HTTPError as e:
             response = e.read()
         except Exception as e:
             output("[API] Login Error: %s " % repr(e))
@@ -67,18 +66,21 @@ class __api__(object):
         try:
             json = string_to_json(response)
         except Exception as e:
-            output("[API] Error: %s "% repr(e))
+            output("[API] Error: %s " % repr(e))
             return False
 
         if json["error"]:
+            xbmc.log("GOT ERROR! %s" % str(json))
             if not __api__.__check_hash__(json) and __auto_login__:
                 return __api__.__login__(False)
 
             output("[API] Error: %s" % json["error"])
             return False
 
+        #ifdef DEBUG
         if DEBUG:
             PLUGIN.notify("Erneut eingelogt...")
+        #endif
 
         PORTAL_USERID = int(json["data"]["userid"])
         PORTAL_SESSION = json["data"]["sessionhash"]
@@ -96,6 +98,9 @@ class __api__(object):
         @param __first__:
         @return:
         """
+        global PORTAL_USERID, PORTAL_SESSION
+
+        PORTAL_USERID, PORTAL_SESSION = load_session()
 
         if __data__ is None:
             __data__ = {}
@@ -105,12 +110,15 @@ class __api__(object):
                 return False
 
         output("[API] UserID: %s" % PORTAL_USERID)
+        #ifdef DEBUG
+        output("[API] Session: %s" % PORTAL_SESSION)
+        #endif
 
         request = __ApiRequest__(__endpoint__, __data__=__data__)
         response = None
         try:
             response = request.__execute__()
-        except urllib2.HTTPError as e:
+        except urllib_request.HTTPError as e:
             response = e.read()
         except:
             return False
@@ -118,7 +126,12 @@ class __api__(object):
         if not response:
             return False
 
-        json = string_to_json(response)
+        json = None
+        try:
+            json = string_to_json(response)
+        except Exception as e:
+            output("[API] Error: %s " % repr(e))
+            return False
 
         if json["error"]:
             if not __api__.__check_hash__(json) and __first__:

+ 103 - 73
src/consts.py

@@ -1,23 +1,33 @@
 # -*- coding: utf-8 -*-
 
+PLUGIN_VERSION = "3.0.3"
+PLUGIN_TOKEN = "71ab1d4f65676994fbfb45a880311bbb"
 
 ##define DEBUG
 ##define DEBUG_SERVER
-
-import framework
+import random
 import xbmc
-import cookielib
-import os
+import traceback
+import framework
 import xbmcgui
-import urllib2
-import platform, sys
-import random
 import time
-import traceback
+from viewids import *
+from utils import *
+
 #ifdef DEBUG_SERVER
 import socket
+
 #endif
-from utils import *
+
+try:
+    #for python 3
+    import urllib.request as urllib_request
+    import urllib.parse as urllib_parse
+    from urllib.request import pathname2url
+except ImportError:
+    import urllib2 as urllib_request  # for python 2
+    import urllib as urllib_parse
+    from urllib import pathname2url
 
 #ifdef DEBUG
 DEBUG = True
@@ -25,11 +35,10 @@ DEBUG = True
 DEBUG = False
 #endif
 BASEPATH = "vnext.to"
-APPTITLE = "Cryflix"
+APPTITLE = "Plugin"
 DELETE_PASSWORD = False
-
-CJ = cookielib.LWPCookieJar()
-
+KODI_VERSION = int(xbmc.getInfoLabel("System.BuildVersion").split(".")[0])
+xbmc.log("[KODI_VERSION] %s" % str(KODI_VERSION))
 MASTERLOCK = False, None
 
 #ifdef DEBUG_SERVER
@@ -42,13 +51,14 @@ def output(message, to_log_file=True):
     """
 
     @param message:
+    @param to_log_file:
     """
 
     #ifdef DEBUG_SERVER
     global sock
     try:
         if not sock:
-            sock = socket.create_connection(("192.168.1.116", 12345), timeout=1)
+            sock = socket.create_connection(("192.168.1.170", 12345), timeout=1)
 
         if sock:
             sock.send("%s\n" % message)
@@ -115,7 +125,7 @@ PORTAL_SESSION = ""
 API_URL = ""
 PORTAL_URL = ""
 
-IMAGEPATH = "http://image.tmdb.org/t/p/"
+IMAGEPATH = "https://image.tmdb.org/t/p/"
 IMAGESIZE_COVER = "w185"
 IMAGESIZE_SCENE = "w500"
 IMAGESIZE_POSTER = "w1280"
@@ -127,8 +137,6 @@ if win.getWidth() >= 1600:
     # IMAGESIZE_POSTER = "original"
 
 PLUGIN_NAME = "kodi"
-PLUGIN_VERSION = PLUGIN.info("version")
-PLUGIN_TOKEN = "19d718fa70115439b9a1d994e300c1f0"
 PLUGIN_HASH = md5_string("%s%s%s" % (PLUGIN_NAME, PLUGIN_VERSION, PLUGIN_TOKEN))
 
 DEVICE_IDENTIFER = ""
@@ -150,7 +158,7 @@ SWITCH_TAGLINE = True
 HIDE_SETTINGS = True
 HIDE_LOGOUT = True
 ASK_FOR_NEXT_EPISODE = True
-VIEW_TYPES = ""
+VIEW_TYPES = "0"
 
 COLOR_LINE = "red"
 COLOR_UHD = "yellow"
@@ -159,7 +167,11 @@ SOCKET_TIMEOUT = 10
 
 USER_AGENT = ""
 MOVIES_PER_PAGE = 50
-YOUTUBE_SEARCH_URL = "https://www.googleapis.com/youtube/v3/search?key=AIzaSyDA0A-a085kg2wB7b-0F2XLOLCpypZYYgg&part=snippet&order=relevance&q=%s&regionCode=de&type=video&relevanceLanguage=de&maxResults=50&safeSearch=none"
+YOUTUBE_KEYS = [
+    #"AIzaSyAl1Xq9DwdE_KD4AtPaE4EJl3WZe2zCqg4",
+    "AIzaSyDA0A-a085kg2wB7b-0F2XLOLCpypZYYgg"
+]
+YOUTUBE_SEARCH_URL = "https://www.googleapis.com/youtube/v3/search?key=%s&part=snippet&order=relevance&q=%s&regionCode=de&type=video&relevanceLanguage=de&maxResults=50&safeSearch=none"
 
 API_HEADERS = [
     ("Authorization", "Bearer %s" % PLUGIN_TOKEN),
@@ -182,15 +194,15 @@ def load_session():
 def clear_session():
     global PORTAL_USERID, PORTAL_SESSION
 
-    internal_storage = PLUGIN.get_storage("session")
-    if not ("user_id" in internal_storage):
+    session_storage = PLUGIN.get_storage("session")
+    if not ("user_id" in session_storage):
         return None, None
 
     output("[SESSION] Clearing!!!!!")
 
-    del internal_storage["user_id"]
-    del internal_storage["session_hash"]
-    internal_storage.sync()
+    del session_storage["user_id"]
+    del session_storage["session_hash"]
+    session_storage.sync()
 
     PORTAL_USERID = 0
     PORTAL_SESSION = None
@@ -231,8 +243,6 @@ def read_settings():
         PORTAL_URL = "http://%s" % PORTAL_URL
 
     DEVICE_IDENTIFER = PLUGIN.get_setting("deviceidentifier")
-    if not DEVICE_IDENTIFER:
-        DEVICE_IDENTIFER = "Unknown"
 
     WATCHLIST_ID = PLUGIN.get_setting("watchlistid")
     WATCHEDLIST_ID = PLUGIN.get_setting("watchedlistid")
@@ -255,19 +265,24 @@ def read_settings():
     HIDE_SETTINGS = PLUGIN.get_setting("hidesettings") == "true"
     HIDE_LOGOUT = PLUGIN.get_setting("hidelogout") == "true"
     ASK_FOR_NEXT_EPISODE = PLUGIN.get_setting("askfornextepisode") == "true"
-    VIEW_TYPES = PLUGIN.get_setting("viewtypes")
-    if VIEW_TYPES == "1":
-        VIEW_TYPES = "1,2"
-    elif VIEW_TYPES == "2":
-        VIEW_TYPES = "1,3"
-    elif VIEW_TYPES == "3":
-        VIEW_TYPES = "2,3"
-    elif VIEW_TYPES == "4":
-        VIEW_TYPES = "1"
-    elif VIEW_TYPES == "5":
-        VIEW_TYPES = "2"
-    elif VIEW_TYPES == "6":
-        VIEW_TYPES = "3"
+
+    tmp = []
+    if not HIDE_UHD:
+        tmp.append("4")
+        tmp.append("5")
+    if not HIDE_HD:
+        tmp.append("1")
+    if not HIDE_SERIES:
+        tmp.append("2")
+    if not HIDE_3D:
+        tmp.append("3")
+
+    if not len(tmp):
+        VIEW_TYPES = "0"
+    else:
+        VIEW_TYPES = ",".join(tmp)
+
+    output("[VIEW_TYPES] %s" % VIEW_TYPES)
 
     MOVIES_PER_PAGE = PLUGIN.get_setting("moviesperpage")
     COLOR_LINE = PLUGIN.get_setting("colorline").replace("#", "")
@@ -318,8 +333,10 @@ def ping():
         internal_storage["ping"] = time.time()
         internal_storage.sync()
 
+    #ifdef DEBUG
     if DEBUG:
         PLUGIN.notify("Erneut gepingt...")
+    #endif
 
     return pinged
 
@@ -327,6 +344,8 @@ def ping():
 def save_session(user_id, session_hash):
     internal_storage = get_storage("session")
 
+    xbmc.log("storage: %s" % str(internal_storage))
+
     internal_storage["user_id"] = user_id
     internal_storage["session_hash"] = session_hash
     internal_storage.sync()
@@ -341,21 +360,29 @@ def save_session(user_id, session_hash):
 def save_watchtime(episode_id, time=0, seen=False):
     episode_id = str(episode_id)
 
+    internal_storage = get_storage("watched")
     if seen:
-        internal_storage = get_storage("watched")
         internal_storage[episode_id] = 1
         internal_storage.sync()
 
         time = 0
+    else:
+        if episode_id in internal_storage:
+            del internal_storage[episode_id]
+            internal_storage.sync()
 
-        try:
-            __api__.__get__("set/watchedlist", __data__={
-                "id": episode_id,
-                "type": "episode"
-            })
+    success = True
 
-        except:
-            pass
+    try:
+        __api__.__get__("set/watchedlist", __data__={
+            "id": episode_id,
+            "value": int(seen),
+            "type": "episode"
+        })
+
+    except:
+        success = False
+        pass
 
     if time < 3 * 60:
         time = 0
@@ -364,6 +391,8 @@ def save_watchtime(episode_id, time=0, seen=False):
     internal_storage[episode_id] = time
     internal_storage.sync()
 
+    return success
+
 
 def read_watchtime(episode_id):
     episode_id = str(episode_id)
@@ -395,15 +424,15 @@ def check_settings():
     if len(DEVICE_IDENTIFER) < 2 or (len(DEVICE_IDENTIFER) == 16 and is_hex(DEVICE_IDENTIFER)):
         # DEVICE_IDENTIFER = md5_string(str(randint(0, 99999)) + "" + str(int(time.time())))[:16]
         try:
-            DEVICE_IDENTIFER = platform.system() + " " + platform.node() + " #" + str(random.randint(0, 99999))
+            DEVICE_IDENTIFER = platform.system() + " " + platform.node() + " " + str(random.randint(0, 99999))
         except:
-            DEVICE_IDENTIFER = "Maybe iOS " + sys.platform + " #" + str(random.randint(0, 99999))
+            DEVICE_IDENTIFER = "Maybe iOS " + sys.platform + " " + str(random.randint(0, 99999))
 
         PLUGIN.set_setting("deviceidentifier", DEVICE_IDENTIFER)
 
     internal_storage = get_storage("internal")
     # Version
-    if "version" in internal_storage and internal_storage["version"] != PLUGIN.info("version"):
+    if "version" in internal_storage and internal_storage["version"] != PLUGIN_VERSION:
         if os.path.isfile(COOKIE_PATH):
             try:
                 os.remove(COOKIE_PATH)
@@ -411,13 +440,14 @@ def check_settings():
                 pass
 
         PLUGIN.notify("Es wurde eine neue Version installiert.")
+
         clear_session()
 
         if DELETE_PASSWORD:
             PLUGIN.set_setting("password", "")
             PLUGIN.dialog(u"Eine neue Version wurde installiert\nAus Sicherheitsgründen musst du dein Passwort neu eingeben.", "Information")
 
-        internal_storage["version"] = PLUGIN.info("version")
+        internal_storage["version"] = PLUGIN_VERSION
         internal_storage.sync()
 
     PORTAL_USERNAME = PLUGIN.get_setting("username")
@@ -466,7 +496,7 @@ def check_settings():
         pass
 
     internal_storage["hash"] = settings_hash_now
-    internal_storage["version"] = PLUGIN.info("version")
+    internal_storage["version"] = PLUGIN_VERSION
     internal_storage.sync()
 
     return True
@@ -494,33 +524,34 @@ def empty():
 EMPTY_PATH = PLUGIN.url_for(endpoint="empty")
 
 YOUTUBE_LIB_SEARCHED = False
-HAS_YOUTUBE_LIB = False
+HAS_YOUTUBE_LIB = xbmc.getCondVisibility("System.HasAddon(plugin.video.youtube)")
+HAS_DRM_LIB = xbmc.getCondVisibility("System.HasAddon(inputstream.adaptive)")
 
 
 def parse_youtube(trailer_id):
-    global YOUTUBE_LIB_SEARCHED, HAS_YOUTUBE_LIB
+    global HAS_YOUTUBE_LIB
+
+    if HAS_YOUTUBE_LIB:
+        output("YouTube library found")
+    else:
+        output("YouTube library not found!")
+
+    if not HAS_YOUTUBE_LIB:
+        return False
+
+    global YOUTUBE_LIB_SEARCHED
 
     if not YOUTUBE_LIB_SEARCHED:
         YOUTUBE_LIB_SEARCHED = True
-        sys.path.append(os.path.realpath(os.path.join(PLUGIN_PATH, "..", "plugin.video.youtube", "resources")))
+        sys.path.append(os.path.realpath(os.path.join(PLUGIN_PATH, "..", "plugin.video.youtube", "resources", "lib")))
 
         try:
-            import lib.youtube_resolver
-
-            HAS_YOUTUBE_LIB = True
+            import youtube_resolver
         except:
             HAS_YOUTUBE_LIB = False
 
-        if HAS_YOUTUBE_LIB:
-            output("YouTube library found")
-        else:
-            output("YouTube library not found!")
-
-    if not HAS_YOUTUBE_LIB:
-        return False
-
     output("Search for %s" % trailer_id)
-    return lib.youtube_resolver.resolve(trailer_id)
+    return youtube_resolver.resolve(trailer_id)
 
 
 def master_lock_access(age):
@@ -560,7 +591,6 @@ def get_view(view_type="list"):
     return VIEW_IDS["skin.confluence"][0]
 
 
-import urllib
 import collections
 
 OPENER = None
@@ -573,7 +603,7 @@ class __ApiRequest__(object):
         self._request = None
 
         if OPENER is None:
-            OPENER = urllib2.build_opener(urllib2.HTTPCookieProcessor(CJ))
+            OPENER = urllib_request.build_opener(urllib_request.HTTPCookieProcessor())
             OPENER.addheaders = API_HEADERS
 
         original_url = __url
@@ -590,17 +620,17 @@ class __ApiRequest__(object):
             if original_url != "auth":
                 __data__["x"] = "%sFE%s" % (PORTAL_USERID, PORTAL_SESSION)
                 __data__["z"] = int(time.time())
-                __data__["hash"] = md5_string("DEADBEEF%s?%s" % (original_url, urllib.urlencode(__data__)))
+                __data__["hash"] = md5_string("DEADBEEF%s?%s" % (original_url, urllib_parse.urlencode(__data__)))
 
-            __data__ = urllib.urlencode(__data__)
+            __data__ = urllib_parse.urlencode(__data__)
 
         if isinstance(__data__, str):
             __url = "%s?%s" % (__url, __data__)
 
         __data__ = None
 
-        output("[API] Request: %s" % str(__url))
-        self._request = urllib2.Request(__url, __data__, __headers, __origin_req_host, __unverifiable)
+        output("---------------------------------------------------------------\n[API] Request: %s\n---------------------------------------------------------------" % str(__url))
+        self._request = urllib_request.Request(__url, __data__, __headers, __origin_req_host, __unverifiable)
 
     def __execute__(self):
         try:

+ 29 - 16
src/playserver.py

@@ -5,6 +5,12 @@ import time
 import xbmc
 import traceback
 
+if sys.version_info[0] >= 3:
+    xrange = range
+
+else:
+    bytes = str
+
 
 def parse_episode(episode_id="0", first="0"):
     first = 0
@@ -40,7 +46,7 @@ class __PlayServer__(object):
         self._started = False
         self._last_progress = None
         self._exit_requested = False
-        self._running_since = 0
+        self._last_file = 0
         self._times = {}
         self._player = xbmc.Player()
 
@@ -94,7 +100,7 @@ class __PlayServer__(object):
             if not request:
                 return False
 
-            matches = re.search(r"^([^\s]+) /(.*(?=/))/[^\s]* HTTP", request, re.IGNORECASE | re.MULTILINE)
+            matches = re.search(r"^([^\s]+) /(.*(?=/))/[^\s]* HTTP", convert_to_str(request), re.IGNORECASE | re.MULTILINE)
             if matches:
                 method = matches.group(1)
                 params = matches.group(2).split("/")
@@ -105,14 +111,14 @@ class __PlayServer__(object):
 
                 if method == "HEAD":
                     if command == "play":
-                        __client_socket__.send("HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nConnection: close\r\n\r\n")
+                        __client_socket__.send(convert_to_bytes("HTTP/1.1 200 OK\r\nContent-Type: application/octet-stream\r\nConnection: close\r\n\r\n"))
 
                     elif command == "playlist":
-                        __client_socket__.send("HTTP/1.1 200 OK\r\nContent-Type: audio/x-mpegurl; charset=utf-8\r\nConnection: close\r\n\r\n")
+                        __client_socket__.send(convert_to_bytes("HTTP/1.1 200 OK\r\nContent-Type: audio/x-mpegurl; charset=utf-8\r\nConnection: close\r\n\r\n"))
 
                 else:
                     if command == "exit":
-                        __client_socket__.send("HTTP/1.1 200 OK\r\nContent-Length: 2\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nOK")
+                        __client_socket__.send(convert_to_bytes("HTTP/1.1 200 OK\r\nContent-Length: 2\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nOK"))
                         self._exit_requested = True
                         output("[SERVER-" + str(self._port) + "] Playserver will be closed")
                         self._running = False
@@ -146,11 +152,11 @@ class __PlayServer__(object):
                             tagline = unicode("%s" % (episode["title"] if season["title"] == "Default" else ("%s. %s" % (episode["number"], episode["title"]))))
                             title = "%s" % (unicode(tagline).replace(u"ä", "ae").replace(u"Ä", "Ae").replace(u"ö", "oe").replace(u"Ö", "Oe").replace(u"ü", "ue").replace(",", " "))
                             playlist_content += "#EXTINF:-1,%s\n" % title
-                            path = "http://127.0.0.1:%s/play/%s/%s.mkv" % (self._port, episode["id"], urllib.quote(title))
+                            path = "http://127.0.0.1:%s/play/%s/%s.mkv" % (self._port, episode["id"], urllib_parse.quote(title))
                             playlist_content += "%s\n" % path
 
                         #__client_socket__.send("HTTP/1.1 200 OK\r\nContent-Type: application/x-mpegURL; charset=utf-8\r\nContent-Length: %s\r\nConnection: close\r\n\r\n%s" % (len(playlist_content), playlist_content))
-                        __client_socket__.send("HTTP/1.1 200 OK\r\nContent-Type: audio/x-mpegurl; charset=utf-8\r\nContent-Length: %s\r\nConnection: close\r\n\r\n%s" % (len(playlist_content), playlist_content))
+                        __client_socket__.send(convert_to_bytes("HTTP/1.1 200 OK\r\nContent-Type: audio/x-mpegurl; charset=utf-8\r\nContent-Length: %s\r\nConnection: close\r\n\r\n%s" % (len(playlist_content), playlist_content)))
 
                     elif command == "play":
                         read_settings()
@@ -168,15 +174,15 @@ class __PlayServer__(object):
                                 raise Exception("Got no episode url")
 
                             self._episode_id = episode_id
-                            __client_socket__.send("HTTP/1.1 301 Moved Permanently\r\nLocation: %s\r\nConnection: close\r\n\r\n" % episode_url)
+                            __client_socket__.send(convert_to_bytes("HTTP/1.1 301 Moved Permanently\r\nLocation: %s\r\nConnection: close\r\n\r\n" % episode_url))
 
-                        except Exception as e:
+                        except:
                             self._running = False
-                            output("[SERVER-" + str(self._port) + "] Client Play Error: %s" % traceback.format_exc())
+                            xbmc.log("[SERVER-" + str(self._port) + "] Client Play Error: %s" % traceback.format_exc(), level=xbmc.LOGERROR)
 
                             PLUGIN.notify("Fehler beim Abspielen!")
 
-                            raise e
+                            raise
 
                         finally:
                             hide_busy_dialog()
@@ -192,10 +198,11 @@ class __PlayServer__(object):
             return True
 
         except:
-            __client_socket__.send("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n")
+            __client_socket__.send(convert_to_bytes("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n"))
 
             self._running = False
-            output("[SERVER-" + str(self._port) + "] Client Error: %s" % traceback.format_exc())
+            xbmc.log("[SERVER-" + str(self._port) + "] Client Error: %s" % traceback.format_exc(), level=xbmc.LOGERROR)
+            PLUGIN.notify("Fehler beim Abspielen!")
 
             return False
 
@@ -281,7 +288,7 @@ class __PlayServer__(object):
         except:
             pass
 
-        if port:
+        if port and port is not None:
             try:
                 # Try to reach current running Server
                 if socket.create_connection(("127.0.0.1", port), timeout=5):
@@ -330,6 +337,8 @@ class __PlayServer__(object):
 
             output("[SERVER-" + str(self._port) + "] Started")
 
+            self._last_file = time.time()
+
             failed = 0
             monitor = xbmc.Monitor()
             while True:
@@ -373,9 +382,13 @@ class __PlayServer__(object):
                 is_playing = self._player.isPlayingVideo()
                 if is_playing:
                     current_file = self._player.getPlayingFile()
+                    if current_file is not None and (":%s" % self._port) in current_file:
+                        self._last_file = time.time()
+                    else:
+                        current_file = None
 
-                aborted = monitor.abortRequested() > 0 or not self._running or self._exit_requested
-                output("[SERVER-" + str(self._port) + "] Idle abort: %s / file: %s" % (aborted, current_file))
+                aborted = monitor.abortRequested() > 0 or not self._running or self._exit_requested or (current_file is None and time.time() - self._last_file > 5 * 60)
+                output("[SERVER-" + str(self._port) + "] Idle abort: %s / File: %s / Last: %s" % (aborted, current_file, time.time() - self._last_file))
 
                 self.__track__(aborted)
                 if aborted:

+ 399 - 78
src/plugin.py

@@ -1,11 +1,15 @@
 # -*- coding: utf-8 -*-
+import base64
+import copy
 
-from api import *
 from consts import *
+from utils import *
+from api import *
+
 import datetime
-import pprint
-import copy
-import urllib
+
+
+# xbmc.log(str(xbmc.getCondVisibility("System.GetBool(debug.showloginfo)")))
 
 
 @PLUGIN.route("/empty/", name="empty", update=False, cache=True)
@@ -38,12 +42,12 @@ def play_trailer(trailer_id="", age=""):
         xbmc.log("[YOUTUBE] Error: %s" % traceback.format_exc())
 
         hide_busy_dialog()
-        PLUGIN.dialog("Es ist ein Fehler passiert bei YT:\n%s" % repr(e))
+        PLUGIN.dialog("Es ist ein Fehler passiert bei YT:\n%s" % str(e))
         return empty()
 
     if streams is False:
         hide_busy_dialog()
-        PLUGIN.dialog("Das Addon [B]plugin.video.youtube[/B] muss installiert sein")
+        PLUGIN.dialog("Das Addon [B]plugin.video.youtube[/B] muss installiert sein.")
         return empty()
 
     if not streams or len(streams) <= 0:
@@ -59,17 +63,31 @@ def play_trailer(trailer_id="", age=""):
         "is_playable": True
     }
 
+    li = PLUGIN._listitemify(item).as_xbmc_listitem()
+    if stream["container"] == "mpd":
+        if not HAS_DRM_LIB:
+            hide_busy_dialog()
+            PLUGIN.dialog("Das Addon [B]inputstream.adaptive[/B] muss für diesen Trailer installiert sein.")
+            return empty()
+
+        if KODI_VERSION >= 19:
+            li.setProperty("inputstream", "inputstream.adaptive")
+        else:
+            li.setProperty("inputstreamaddon", "inputstream.adaptive")
+        li.setProperty("inputstream.adaptive.manifest_type", "mpd")
+        li.setMimeType("application/dash+xml")
+        li.setContentLookup(False)
+
+    elif stream["container"] in ["ism", "hls", "rtmp"]:
+        hide_busy_dialog()
+        PLUGIN.dialog("Container [B]%s[/B] wird nicht abspielbar!" % stream["container"])
+        return empty()
+
     playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
     playlist.clear()
-
-    li = PLUGIN._listitemify(item).as_xbmc_listitem()
     playlist.add(item["path"], li)
 
     player = xbmc.Player()
-
-    if False:
-        PLUGIN.notify("Trailer wird versucht abzuspielen!")
-
     player.play(playlist)
     del player
     player = None
@@ -84,6 +102,8 @@ def view_actors(movie_id=0):
 
     items = []
 
+    show_busy_dialog()
+
     movie_result = __api__.__get__("get/movie", __data__={
         "id": movie_id,
         "withActors": 1
@@ -101,11 +121,11 @@ def view_actors(movie_id=0):
         return empty()
 
     items = [
-        {
+        set_thumbnail({
             "label": unicode(entry["name"]),
             "path": PLUGIN.url_for(endpoint="view_actor",
                                    actor_id=entry['id'],
-                                   page=0),
+                                   page=1),
             "thumbnail": get_poster_url(entry["profile"]),
             "info": {
                 "count": entry["id"],
@@ -114,17 +134,21 @@ def view_actors(movie_id=0):
             "is_playable": False,
             "context_menu": misc_menu(),
             "replace_context_menu": True
-        } for idx, entry in enumerate(entries)
+        }) for idx, entry in enumerate(entries)
     ]
 
+    hide_busy_dialog()
+
     return items
 
 
 @PLUGIN.route("/view_actor/<actor_id>/<page>/", name="view_actor", cache=True, content_type="movies", view_mode=get_view("thumbnail"))
-def view_actor(actor_id=0, page=0):
+def view_actor(actor_id=0, page=1):
     if not actor_id or actor_id == 0:
         return empty()
 
+    show_busy_dialog()
+
     items = []
 
     actor_result = __api__.__get__("get/actor", __data__={
@@ -133,17 +157,26 @@ def view_actor(actor_id=0, page=0):
     })
 
     if not actor_result:
+        hide_busy_dialog()
         return empty()
 
     items.extend(prepare_movies_to_list(actor_result["data"]["movies"]))
 
+    hide_busy_dialog()
+
     return items
 
 
 @PLUGIN.route("/search_trailer/<title>/<date>/<fanart>/<age>/", name="search_trailer", cache=False, content_type="episodes", view_mode=get_view("episode"))
 def search_trailer(title="", date="", fanart="", age=""):
+    if not HAS_YOUTUBE_LIB:
+        PLUGIN.dialog("Das Addon [B]plugin.video.youtube[/B] muss installiert sein.")
+        return empty()
+
     show_busy_dialog()
+
     if not title or not master_lock_access(age):
+        hide_busy_dialog()
         return empty()
 
     year = re.search("([0-9]{4})-.*", date)
@@ -152,26 +185,40 @@ def search_trailer(title="", date="", fanart="", age=""):
     else:
         year = ""
 
-    title = urllib.pathname2url("%s trailer german deutsch" % (title.replace(":", "")))
+    title = pathname2url("%s trailer german deutsch" % (title.replace(":", "")))
 
-    if DEBUG:
-        output("[YOUTUBE]: %s" % (YOUTUBE_SEARCH_URL % title))
+    search_url = YOUTUBE_SEARCH_URL % (random.choice(YOUTUBE_KEYS), title)
 
+    #ifdef DEBUG
+    if DEBUG:
+        output("[YOUTUBE]: %s" % search_url)
+    #endif
     data = None
     try:
-        data = urllib2.urlopen(YOUTUBE_SEARCH_URL % title).read()
-    except Exception as e:
-        output(repr(e))
+        data = urllib_request.urlopen(search_url).read()
+    except urllib_request.HTTPError as e:
+        data = e.read()
+    except:
+        output("[YOUTUBE] Exception: %s" % traceback.format_exc())
 
     if not data:
         hide_busy_dialog()
         PLUGIN.dialog("Fehler beim verarbeiten der Antwort.")
         return empty()
 
-    obj = string_to_json(data)
-    if not obj or not ('items' in obj):
+    try:
+        obj = string_to_json(data)
+    except:
+        obj = None
+
+    if not obj or 'items' not in obj:
         hide_busy_dialog()
-        PLUGIN.dialog("Es wurden keine Trailer gefunden")
+        if "error" in obj and len(obj["error"]["errors"]):
+            error = obj["error"]["errors"][0]
+            PLUGIN.dialog("Es ist ein Fehler passiert bei YT:\n%s" % error["reason"])
+        else:
+            PLUGIN.dialog("Es wurden keine Trailer gefunden")
+
         return empty()
 
     entries = obj['items']
@@ -181,14 +228,14 @@ def search_trailer(title="", date="", fanart="", age=""):
         return empty()
 
     items = [
-        {
+        set_thumbnail({
             "label": unicode(entry['snippet']["title"]),
             "path": PLUGIN.url_for(endpoint="play_trailer",
                                    trailer_id=str(entry['id']['videoId']),
                                    age="0"),
             "thumbnail": entry['snippet']['thumbnails']['high']['url'].replace("https://", "http://"),
             "properties": {
-                "fanart_image": urllib.unquote(fanart) if fanart and fanart != "None" else "",
+                "fanart_image": urllib_parse.unquote(fanart) if fanart and fanart != "None" else "",
             },
             "info": {
                 "count": entry["id"]["videoId"],
@@ -197,7 +244,7 @@ def search_trailer(title="", date="", fanart="", age=""):
                 "premiered": entry['snippet']["publishedAt"][:10] if entry['snippet']["publishedAt"] else ""
             },
             "is_playable": False,
-        } for idx, entry in enumerate(entries)
+        }) for idx, entry in enumerate(entries)
     ]
 
     hide_busy_dialog()
@@ -221,7 +268,7 @@ def watchlist_action(movie_id="0", action="add", refresh=0):
 
     message = None
     if action == "add":
-        message = u"Eintrag wurde der Liste hinzugefügt"
+        message = "Eintrag wurde der Liste hinzugefuegt"
 
     elif action == "remove":
         message = "Eintrag wurde aus der Liste entfernt"
@@ -235,11 +282,26 @@ def watchlist_action(movie_id="0", action="add", refresh=0):
     return empty()
 
 
-@PLUGIN.route("/view_list/<list_type>/<page>/<view_types>/<genre>/<min_year>/<max_year>/<query>/", name="view_list", update=False, cache=True, content_type="movies", view_mode=get_view("thumbnail"))
+class UrlBasedCache():
+    def __trunc__(self):
+        try:
+            request = PLUGIN.request
+        except:
+            return True
+
+        do_cache = not ("/view_list/watched/" in request.url)
+        if not do_cache:
+            output("[PLUGIN] Cache will be ignored for %s" % request.url)
+        return do_cache
+
+
+@PLUGIN.route("/view_list/<list_type>/<page>/<view_types>/<genre>/<min_year>/<max_year>/<query>/", name="view_list", update=False, cache=UrlBasedCache(), content_type="movies", view_mode=get_view("thumbnail"))
 def view_list(list_type="new", view_types="0", page=1, genre="all", min_year=0, max_year=0, query=0):
     if str(view_types) == "2":
         PLUGIN.set_goto_top(False)
 
+    show_busy_dialog()
+
     items = []
 
     params = {
@@ -251,18 +313,23 @@ def view_list(list_type="new", view_types="0", page=1, genre="all", min_year=0,
     }
 
     if query != "0":
-        output("Search: %s" % query)
-        params["title"] = query
+        if sys.version_info.major < 3:
+            params["title"] = unicode(base64.b64decode(query), "utf-8").encode("utf-8")
+        else:
+            params["title"] = base64.b64decode(query)
 
     params["orderby"] = "updated_at|DESC"
     # params["orderby"] = "popularity|DESC"
 
     if view_types != "0":
-        params["types"] = view_types
+        params["types"] = view_types.replace("%2C", ",")
 
     if int(min_year) > 0 or int(max_year) > 0:
         params["year"] = "%s-%s" % (min_year, max_year)
 
+    if genre != "all":
+        params["genres"] = genre
+
     result = __api__.__get__("get/list", __data__=params)
     if not result:
         hide_busy_dialog()
@@ -303,6 +370,8 @@ def view_list(list_type="new", view_types="0", page=1, genre="all", min_year=0,
 
         items.extend(pages)
 
+    hide_busy_dialog()
+
     return items
 
 
@@ -311,6 +380,8 @@ def search_similars(movie_id=0, page=1, type=0):
     if not movie_id:
         return empty()
 
+    show_busy_dialog()
+
     result = __api__.__get__("get/similars", __data__={
         "id": movie_id
     })
@@ -328,6 +399,8 @@ def search_similars(movie_id=0, page=1, type=0):
 
     items.extend(prepare_movies_to_list(result["data"]["results"]))
 
+    hide_busy_dialog()
+
     return items
 
 
@@ -440,8 +513,8 @@ def play_episode(episode_id=0, ask_playlist=0):
             else:
                 li["info"]["tagline"] = tagline
 
-        tit = unicode("%s" % (episode_result["data"]["season"]["title"] if episode_result["data"]["season"]["title"] == "Default" else ("%s. %s" % (episode["number"], episode["title"]))))
-        li["path"] = path = "http://127.0.0.1:%s/play/%s/%s.mkv" % (play_port, episode["id"], urllib.quote(tit))
+        tit = "File"  #unicode("%s" % (episode_result["data"]["season"]["title"] if episode_result["data"]["season"]["title"] == "Default" else ("%s. %s" % (episode["number"], episode["title"]))))
+        li["path"] = path = "http://127.0.0.1:%s/play/%s/%s.mkv" % (play_port, episode["id"], urllib_parse.quote(tit))
         li = PLUGIN._listitemify(li).as_xbmc_listitem()
         playlist.add(path, li)
 
@@ -466,14 +539,15 @@ def play_episode(episode_id=0, ask_playlist=0):
                     else:
                         li["info"]["tagline"] = tagline
 
-                tit = unicode("%s" % (episode_result["data"]["season"]["title"] if episode_result["data"]["season"]["title"] == "Default" else ("%s. %s" % (episode["number"], episode["title"]))))
-                li["path"] = path = "http://127.0.0.1:%s/play/%s/%s.mkv" % (play_port, episode["id"], urllib.quote(tit))
+                tit = "File"  #unicode("%s" % (episode_result["data"]["season"]["title"] if episode_result["data"]["season"]["title"] == "Default" else ("%s. %s" % (episode["number"], episode["title"]))))
+                li["path"] = path = "http://127.0.0.1:%s/play/%s/%s.mkv" % (play_port, episode["id"], urllib_parse.quote(tit))
                 li = PLUGIN._listitemify(li).as_xbmc_listitem()
                 playlist.add(path, li)
 
         #ifdef DEBUG
         for idx in range(playlist.size()):
-            output("[PLAY] %s - Item %s" % (idx, playlist[idx].getPath()))
+            path = playlist[idx].getPath() if hasattr(playlist[idx], "getPath") else playlist[idx].getfilename()
+            output("[PLAY] %s - Item %s" % (idx + 1, path))
         #endif
 
         first = playlist[0]
@@ -493,9 +567,10 @@ def play_episode(episode_id=0, ask_playlist=0):
     player.play(playlist)
     del player
     player = None
-
+    show_busy_dialog()
     xbmc.sleep(250)
-    xbmc.executebuiltin("XBMC.Action(FullScreen)")
+    xbmc.executebuiltin("Action(FullScreen)")
+    show_busy_dialog()
 
     return empty()
 
@@ -504,6 +579,7 @@ def play_episode(episode_id=0, ask_playlist=0):
 def seasons(movie_id=0):
     show_busy_dialog()
     if movie_id == 0:
+        hide_busy_dialog()
         return empty()
 
     movie_result = __api__.__get__("get/movie", __data__={
@@ -528,7 +604,7 @@ def seasons(movie_id=0):
         return empty()
 
     items = [
-        {
+        set_thumbnail({
             "label": unicode(season["title"]),
             "path": PLUGIN.url_for(endpoint="episodes", season_id=season['id']),
             "thumbnail": get_poster_url(season["posterurl"] if season["posterurl"] else season_result["data"]["posterurl"]),
@@ -546,7 +622,7 @@ def seasons(movie_id=0):
             },
             "context_menu": misc_menu(),
             "replace_context_menu": False
-        } for season in season_result["data"]["seasons"]
+        }) for season in season_result["data"]["seasons"]
     ]
 
     hide_busy_dialog()
@@ -558,6 +634,7 @@ def seasons(movie_id=0):
 def episodes(season_id=0, dialog=True):
     show_busy_dialog()
     if season_id == 0:
+        hide_busy_dialog()
         return empty()
 
     episode_results = __api__.__get__("get/season", __data__={
@@ -572,7 +649,7 @@ def episodes(season_id=0, dialog=True):
         if not episode["id"]:
             continue
 
-        items.append({
+        items.append(set_thumbnail({
             "label": "%s. %s" % (episode["number"], unicode(episode["title"])),
             "path": PLUGIN.url_for(endpoint="play_episode",
                                    episode_id=episode['id'],
@@ -580,7 +657,7 @@ def episodes(season_id=0, dialog=True):
             "is_playable": False,
             "thumbnail": get_scene_url(episode["posterurl"] if episode["posterurl"] else None),
             "properties": {
-                # "fanart_image": get_backdrop_url(obj["data"]["movie"]['backdropurl']),
+                #"fanart_image": get_backdrop_url(episode_results["data"]['backdropurl']),
             },
             "info": {
                 "plot": unicode(episode["description"]) if episode["description"] else (unicode(episode_results["data"]["description"]) if episode_results["data"]["description"] else None),
@@ -593,31 +670,57 @@ def episodes(season_id=0, dialog=True):
             },
             "context_menu": misc_menu(episode_id=episode["id"]),
             "replace_context_menu": False
-        })
+        }))
 
     hide_busy_dialog()
 
     return items
 
 
-@PLUGIN.route("/mark_as_seen/<episodeid>/", name="mark_as_seen")
-def mark_as_seen(episodeid="0"):
+@PLUGIN.route("/mark_as_seen/<episodeid>/<seen>/", name="mark_as_seen")
+def mark_as_seen(episodeid="0", seen="0"):
     if not episodeid or episodeid == "0":
-        return None
+        return empty()
+
+    show_busy_dialog()
+
+    if not save_watchtime(episodeid, time=0, seen=int(seen)):
+        hide_busy_dialog()
+        PLUGIN.notify("Fehler beim ausfuehren der Aktion!")
+    else:
+        hide_busy_dialog()
+        PLUGIN.notify("Eintrag wurde als %sgesehen markiert." % ("un" if not int(seen) else ""))
 
     return None
 
 
 @PLUGIN.route("/account_status/", name="account_status")
 def account_status():
-    return empty()
+    userinfo_result = __api__.__get__("get/userinfo")
+
+    if not userinfo_result:
+        hide_busy_dialog()
+        PLUGIN.dialog("Fehler beim verarbeiten der Antwort.")
+        return empty()
+
+    data = userinfo_result["data"]
+    text = ""
+    if data and "abonnement" in data:
+        text += "[B]Laufzeit:[/B]\n\n"
+        text += "%s - %s" % (data["abonnement"]["start"]["date"], data["abonnement"]["end"]["date"])
+    else:
+        text = u"Keine Informationen bezüglich Laufzeit."
+
+    hide_busy_dialog()
+    PLUGIN.dialog(text)
+    return None
 
 
 @PLUGIN.route("/clear_password/", name="clear_password")
 def clear_password():
     clear_session()
     PLUGIN.set_setting("password", "")
-    return empty()
+    return None
 
 
 @PLUGIN.route("/clear_storage/", name="clear_storage")
@@ -627,29 +730,106 @@ def clear_storage():
     clear_session()
 
     PLUGIN.notify("Storage wurde geleert.")
-    return empty()
+    return None
 
 
-@PLUGIN.route("/search/<is_actor>/", name="search", cache=False)
-def search(is_actor="0"):
-    is_actor = 0
+@PLUGIN.route("/fuckinghell/<is_actor>/", name="fuckinghell", cache=False)
+def fuckinghell(is_actor="0"):
+    is_actor = str(is_actor)
 
     title = "Schauspieler" if is_actor == "1" else "Film oder Serie"
     query = PLUGIN.keyboard('', "Nach %s Suchen" % title)
 
     if query:
-        return PLUGIN.redirect(PLUGIN.url_for(endpoint="view_list",
-                                              list_type="all",
-                                              view_types=VIEW_TYPES,
-                                              genre="all",
-                                              min_year=0,
-                                              max_year=0,
-                                              query=query,
-                                              page=1))
+        query = base64.b64encode(convert_to_bytes(query))
+        if is_actor == "1":
+            return PLUGIN.redirect(PLUGIN.url_for(endpoint="search_actors",
+                                                  query=query,
+                                                  page=1))
+
+        else:
+            return PLUGIN.redirect(PLUGIN.url_for(endpoint="view_list",
+                                                  list_type="all",
+                                                  view_types=VIEW_TYPES,
+                                                  genre="all",
+                                                  min_year=0,
+                                                  max_year=0,
+                                                  query=query,
+                                                  page=1))
 
     return empty()
 
 
+@PLUGIN.route("/search_actors/<query>/<page>/", name="search_actors", cache=True, content_type="movies", view_mode=get_view("thumbnail"), goto_top=True)
+def search_actors(query="", page=1):
+    if not query:
+        return empty()
+
+    show_busy_dialog()
+
+    if sys.version_info.major < 3:
+        encoded_query = unicode(base64.b64decode(query), "utf-8").encode("utf-8")
+    else:
+        encoded_query = base64.b64decode(query)
+
+    result = __api__.__get__("get/actors", __data__={
+        "query": encoded_query,
+        "page": page,
+        "limit": MOVIES_PER_PAGE,
+        "extended": 0
+    })
+
+    if not result:
+        hide_busy_dialog()
+        PLUGIN.notify("Fehler beim verarbeiten der Anfrage.")
+        return empty()
+
+    if result["data"]["total_results"] <= 0:
+        hide_busy_dialog()
+        PLUGIN.dialog(u"Leider wurden keine Einträge gefunden!")
+        return empty()
+
+    items = []
+
+    actors = result["data"]["results"]
+
+    for actor in actors:
+        items.append(set_thumbnail({
+            "label": actor["name"].strip(),
+            "thumbnail": get_poster_url(actor["picture"]) if actor["picture"] else "",
+            "path": PLUGIN.url_for(endpoint="view_actor",
+                                   actor_id=actor["id"],
+                                   page=1),
+            "context_menu": misc_menu(),
+            "replace_context_menu": False
+        }))
+
+    total = result["data"]["total_pages"]
+    if total > 1:
+        pages = []
+        for pagenum in range(1, total + 1):
+            pages.append(
+                {
+                    "label": ("[COLOR red][B][I]Seite %s von %s[/I][/B][/COLOR]" % (pagenum, total)) if int(pagenum) == int(page) else "Seite %s von %s" % (pagenum, total),
+                    "is_playable": False,
+                    "path": EMPTY_PATH if int(pagenum) == int(page) else PLUGIN.url_for(endpoint="search_actors",
+                                                                                        page=pagenum,
+                                                                                        query=query),
+                    "info": {
+                        "playcount": 1 if int(pagenum) == int(page) else 0,
+                        "tagline": "[B]%s[/B] bis [B]%s[/B]" % (unicode(pagenum * int(result["data"]["limit"])), unicode(min(pagenum * int(result["data"]["limit"]), total)))
+                    },
+                    "context_menu": misc_menu(),
+                    "replace_context_menu": False
+                }
+            )
+
+        items.extend(pages)
+
+    hide_busy_dialog()
+    return items
+
+
 @PLUGIN.route("/open_settings/", name="open_settings", cache=True)
 def open_settings():
     PLUGIN.open_settings()
@@ -673,8 +853,8 @@ def account_logout():
 @PLUGIN.route("/stop_playserver/", name="stop_playserver", cache=False)
 def stop_playserver():
     server = get_storage("server")
-    if server is None or not ("port" in server) or server["port"] == 0:
-        PLUGIN.notify(u"Playserver läuft nicht!")
+    if server is None or "port" not in server or server["port"] == 0:
+        PLUGIN.notify("Playserver laeuft nicht!")
 
     else:
         try:
@@ -695,7 +875,141 @@ def stop_playserver():
     return empty()
 
 
-# Main
+@PLUGIN.route("/view_filters/", name="view_filters", cache=True, content_type="files", view_mode=get_view("list"))
+def view_filters():
+    items = []
+
+    items.append({
+        "label": "Nach Genre",
+        "path": PLUGIN.url_for(endpoint="filter_genre"),
+    })
+    items.append({
+        "label": "Nach Jahr",
+        "path": PLUGIN.url_for(endpoint="filter_years"),
+    })
+
+    return items
+
+
+@PLUGIN.route("/filter_genre/", name="filter_genre", cache=True, content_type="files", view_mode=get_view("list"))
+def filter_genre():
+    show_busy_dialog()
+    items = []
+
+    result = __api__.__get__("get/genres", __data__={
+        "page": 1,
+        "limit": 9999999
+    })
+
+    if not result:
+        hide_busy_dialog()
+        PLUGIN.notify("Fehler beim verarbeiten der Anfrage.")
+        return empty()
+
+    if len(result["data"]) <= 0:
+        hide_busy_dialog()
+        PLUGIN.dialog(u"Leider wurden keine Genres gefunden!")
+        return empty()
+
+    for genre in result["data"]:
+        items.append({
+            "label": "%s (%s)" % (genre["name"], genre["movies"]),
+            "path": PLUGIN.url_for(endpoint="view_list",
+                                   genre=genre["name"],
+                                   list_type="all",
+                                   view_types=VIEW_TYPES,
+                                   page=1,
+                                   query=0,
+                                   min_year=0,
+                                   max_year=0),
+        })
+
+    hide_busy_dialog()
+    return items
+
+
+@PLUGIN.route("/filter_years_manually/", name="filter_years_manually", cache=False)
+def search():
+    query = PLUGIN.keyboard('', "Jahr eingeben")
+
+    if query:
+        min_year = query
+        max_year = query
+        if "-" in query:
+            chunks = query.split("-")
+            min_year = chunks[0].strip()
+            max_year = chunks[1].strip()
+
+        if not min_year.isdigit() or not max_year.isdigit():
+            PLUGIN.dialog(u"Jahr nicht gültig!\nBeispiele:\n- 1950\n- 1900-1950")
+            return empty()
+
+        return PLUGIN.redirect(PLUGIN.url_for(endpoint="view_list",
+                                              genre="all",
+                                              list_type="all",
+                                              view_types=VIEW_TYPES,
+                                              page=1,
+                                              query=0,
+                                              min_year=min_year,
+                                              max_year=max_year))
+
+    return empty()
+
+
+@PLUGIN.route("/filter_years/", name="filter_years", cache=True, content_type="files", view_mode=get_view("list"), goto_top=False)
+def filter_years():
+    year = datetime.datetime.now().year
+
+    current_year = year
+    year_range = 0
+
+    items = []
+    while year - year_range >= 1949:
+        diff = current_year - year
+        if diff < 5:
+            year_range = 1
+        elif diff <= 10:
+            year_range = 5
+        else:
+            year_range = 10
+
+        min_year = (year - year_range + 1) if year_range != 1 else year
+        max_year = year
+
+        items.append({
+            "label": ("%s - %s" % (year - year_range + 1, year)) if year_range != 1 else str(year),
+            "path": PLUGIN.url_for(endpoint="view_list",
+                                   genre="all",
+                                   list_type="all",
+                                   view_types=VIEW_TYPES,
+                                   page=1,
+                                   query=0,
+                                   min_year=min_year,
+                                   max_year=max_year)
+        })
+
+        year -= year_range
+
+    if year >= 1950:
+        diff = year - 1950
+        items.append({
+            "label": ("1950 - %s" % year) if diff > 1 else "1950",
+            "path": PLUGIN.url_for(endpoint="view_list",
+                                   genre="all",
+                                   list_type="all",
+                                   view_types=VIEW_TYPES,
+                                   page=1,
+                                   query=0,
+                                   min_year=1950,
+                                   max_year=year if diff > 1 else 1950)
+        })
+
+    items.append({
+        "label": "Jahr manuell eingeben",
+        "path": PLUGIN.url_for(endpoint="filter_years_manually"),
+    })
+
+    return items
 
 
 @PLUGIN.route("/updated_needed/", name="updated_needed", cache=True)
@@ -704,6 +1018,7 @@ def updated_needed():
     return items
 
 
+# Main
 @PLUGIN.route("/", name="main", update=True, cache=False, content_type="files", view_mode=get_view("list"), goto_top=False)
 def main():
     items = []
@@ -738,19 +1053,18 @@ def main():
         if True:
             items.append({
                 "label": "Suche Film oder Serie",
-                "path": PLUGIN.url_for(endpoint="search",
-                                       is_actor=0),
+                "path": PLUGIN.url_for(endpoint="fuckinghell",
+                                       is_actor="0"),
                 "context_menu": [
                     ("", "")
                 ],
                 "replace_context_menu": True,
             })
 
-        if False:
             items.append({
                 "label": "Suche Schauspieler",
-                "path": PLUGIN.url_for(endpoint="search",
-                                       is_actor=1),
+                "path": PLUGIN.url_for(endpoint="fuckinghell",
+                                       is_actor="1"),
                 "context_menu": [
                     ("", "")
                 ],
@@ -761,8 +1075,6 @@ def main():
             if WATCHLIST_TOGETHER:
                 items.append({
                     "label": "Meine Liste",
-                    # "path": PLUGIN.url_for(endpoint="watchlist", page=1, type="0")
-
                     "path": PLUGIN.url_for(endpoint="view_list",
                                            list_type="watchlist",
                                            view_types=VIEW_TYPES,
@@ -776,7 +1088,6 @@ def main():
             else:
                 items.append({
                     "label": "Meine Filme",
-                    # "path": PLUGIN.url_for(endpoint="watchlist", page=1, type="1|3|4")
                     "path": PLUGIN.url_for(endpoint="view_list",
                                            list_type="watchlist-movies",
                                            view_types=VIEW_TYPES,
@@ -788,7 +1099,6 @@ def main():
                 })
                 items.append({
                     "label": "Meine Serien",
-                    # "path": PLUGIN.url_for(endpoint="watchlist", page=1, type="2|5")
                     "path": PLUGIN.url_for(endpoint="view_list",
                                            list_type="watchlist-shows",
                                            view_types=VIEW_TYPES,
@@ -850,7 +1160,7 @@ def main():
                                        max_year=0)
             })
 
-            if False:
+            if not HIDE_SERIES:
                 items.append({
                     "label": "UHD Serien",
                     "path": PLUGIN.url_for(endpoint="view_list",
@@ -858,8 +1168,8 @@ def main():
                                            list_type="uhd-shows",
                                            view_types="5",
                                            page=1,
-                                           min_year=0,
                                            query=0,
+                                           min_year=0,
                                            max_year=0)
                 })
 
@@ -878,7 +1188,7 @@ def main():
 
         if not HIDE_SERIES:
             items.append({
-                "label": "Serien",
+                "label": "HD Serien",
                 "path": PLUGIN.url_for(endpoint="view_list",
                                        genre="all",
                                        list_type="new-episodes",
@@ -914,6 +1224,11 @@ def main():
                                    max_year=0)
         })
 
+        items.append({
+            "label": "Filter",
+            "path": PLUGIN.url_for(endpoint="view_filters"),
+        })
+
         if not HIDE_SETTINGS:
             items.append({
                 "label": "Einstellungen",
@@ -939,6 +1254,9 @@ def main():
 
 # --------------------------------
 
+__version__ = PLUGIN_VERSION
+__all__ = ["__run", "__output", "__version__"]
+
 
 def __run():
     output("==================================== START ====================================")
@@ -954,7 +1272,7 @@ def __run():
 
         try:
             os.chdir(os.path.dirname(os.path.abspath(__file__)))
-            if str(__file__).endswith(".py"):
+            if str(__file__).endswith(".py") and os.path.exists("%s.pyo" % __file__):
                 xbmc.sleep(100)
                 import glob
                 for file in glob.glob("framework/*.py"):
@@ -962,5 +1280,8 @@ def __run():
                 os.remove(__file__)
 
         except Exception as e:
+            #ifdef DEBUG
             if DEBUG:
                 output("Exception: %s" % repr(e))
+            #endif
+            pass

+ 107 - 48
src/utils.py

@@ -3,12 +3,23 @@ import hashlib
 import re
 import os
 import sys
+import pprint
+import xbmc
 import platform
+from consts import *
+
+if sys.version_info[0] >= 3:
+    unicode = str
+    long = int
+
+
+def string_str(s):
+    return s
 
 
 def md5_string(i):
     m = hashlib.md5()
-    m.update(i)
+    m.update(i.encode("utf-8"))
     return m.hexdigest()
 
 
@@ -16,19 +27,22 @@ def md5_file(fname):
     hash_md5 = hashlib.md5()
     with open(fname, "rb") as f:
         for chunk in iter(lambda: f.read(4096), b""):
-            hash_md5.update(chunk)
+            hash_md5.update(chunk.encode("utf-8"))
     return hash_md5.hexdigest()
 
 
 def string_to_json(i):
-    return json.loads(i)
+    try:
+        return json.loads(i)
+    except:
+        return None
 
 
 def is_hex(val):
     try:
         int(val, 16)
         return True
-    except ValueError, e:
+    except ValueError:
         return False
 
 
@@ -104,7 +118,7 @@ def format_seconds_to_hhmmss(seconds):
     return "%02i:%02i:%02i" % (hours, minutes, seconds)
 
 
-def misc_menu(data=None, episode_id=None):
+def misc_menu(menu=None, data=None, episode_id=None):
     """
 
     @param data:
@@ -112,36 +126,36 @@ def misc_menu(data=None, episode_id=None):
     @return:
     """
 
-    if not data:
-        data = []
+    if not menu:
+        menu = []
 
-    if False and episode_id:
-        data.append(
+    if True and episode_id and data:
+        menu.append(
             (
-                "Als gesehen markieren",
-                'RunPlugin("%s")' % PLUGIN.url_for(endpoint="mark_as_seen", episodeid=episode_id)
+                "Als %sgesehen markieren" % ("un" if data["in_watchedlist"] else ""),
+                'RunPlugin("%s")' % PLUGIN.url_for(endpoint="mark_as_seen", episodeid=episode_id, seen=int(not data["in_watchedlist"]))
             )
         )
 
-    data.append(
+    menu.append(
         (
             "Zum Listenanfang",
-            "XBMC.Action(firstpage)"
+            "Action(firstpage)"
         )
     )
-    data.append(
+    menu.append(
         (
             u"Zum Hauptmenü",
-            'XBMC.Container.Update("%s",replace)' % PLUGIN.url_for(endpoint="main")
+            'Container.Update("%s",replace)' % PLUGIN.url_for(endpoint="main")
         )
     )
-    data.append(
+    menu.append(
         (
             u"Ebene zurück",
-            'XBMC.Action(Back)'
+            'Action(Back)'
         )
     )
-    return data
+    return menu
 
 
 def has_played(movie_id=0):
@@ -182,7 +196,47 @@ def get_user_agent():
     return user_agent
 
 
-import pprint
+def convert_to_bytes(text, encoding="utf-8"):
+    if sys.version_info.major < 3:
+        return text
+    else:
+        try:
+            return text.encode(encoding, "ignore")
+        except Exception:
+            return text
+
+
+def convert_to_str(text, encoding="utf-8"):
+    if sys.version_info.major < 3:
+        return text
+    else:
+        try:
+            return text.decode(encoding, "ignore")
+        except Exception:
+            return text
+
+
+def set_thumbnail(item):
+    if "art" not in item or not isinstance(item["art"], dict):
+        item["art"] = {}
+
+    thumbnail = item.get("thumbnail")
+    fanart = item.get("properties", {}).get("thumbnail")
+
+    if fanart is not None:
+        item["art"]["fanart"] = fanart
+
+    if thumbnail is not None:
+        item["art"]["thumb"] = thumbnail
+        item["art"]["landscape"] = item["art"]["thumb"]
+        item["art"]["icon"] = item["art"]["thumb"]
+        item["art"]["clearlogo"] = item["art"]["thumb"]
+        item["art"]["banner"] = item["art"]["thumb"]
+        """
+        item["art"]["poster"] = item["art"]["thumb"]
+        """
+
+    return item
 
 
 def prepare_entry(entry, isfirst=False, prefixed=True):
@@ -192,15 +246,13 @@ def prepare_entry(entry, isfirst=False, prefixed=True):
     trailer_id = None
     if entry["trailer"]:
         # trailer_id = re.match(r'^[^v]+v[/=](.{3,11}).*', entry["trailer"])
-        trailer_id = re.search(r"(?:v=|//[^/]+\.be/)(.{3,11})", entry["trailer"].encode('ascii', 'ignore'))
+        trailer_id = re.search(r"(?:v=|//[^/]+\.be/)(.{3,11})", entry["trailer"])
         if trailer_id:
             trailer_id = trailer_id.group(1)
 
     fanart_url = get_backdrop_url(entry["backdropurl"])
 
-    entry["title"] = u''.join((unicode(entry["title"]), u"")).encode("utf-8").strip()
-
-    title = entry['title'].decode("utf-8")
+    title = unicode(entry['title'])
     if prefixed:
         if entry["type"] == 1:
             title = "HD: %s" % title
@@ -229,7 +281,7 @@ def prepare_entry(entry, isfirst=False, prefixed=True):
     menu = [
         (
             "Informationen",
-            "XBMC.Action(Info)"
+            "Action(Info)"
         )
     ]
 
@@ -250,19 +302,19 @@ def prepare_entry(entry, isfirst=False, prefixed=True):
         menu.append(
             (
                 "Schauspieler anzeigen",
-                'XBMC.Container.Update("%s",True)' % PLUGIN.url_for(endpoint="view_actors",
-                                                                    movie_id=entry['id'])
+                'Container.Update("%s",True)' % PLUGIN.url_for(endpoint="view_actors",
+                                                               movie_id=entry['id'])
             )
         )
 
     menu.append(
         (
             "%sTrailer suchen" % ("weitere " if trailer_id else ""),
-            'XBMC.Container.Update("%s",True)' % PLUGIN.url_for(endpoint="search_trailer",
-                                                                title=entry['title'],
-                                                                date="%s" % entry['release_date'],
-                                                                fanart=fanart_url if fanart_url else "None",
-                                                                age=entry["age"] if "age" in entry and entry["age"] else "0")
+            'Container.Update("%s",True)' % PLUGIN.url_for(endpoint="search_trailer",
+                                                           title=entry['title'] if isinstance(entry["title"], int) else entry['title'].encode("utf-8"),
+                                                           date="%s" % entry['release_date'],
+                                                           fanart=fanart_url if fanart_url else "None",
+                                                           age=entry["age"] if "age" in entry and entry["age"] else "0")
         )
     )
 
@@ -290,7 +342,18 @@ def prepare_entry(entry, isfirst=False, prefixed=True):
             )
         )
 
-    watched = (entry["in_watchedlist"] * 1) if "in_watchedlist" in entry else (has_played(entry['episode_id']) if entry["has_episodes"] == False else 0)
+    if False and entry["collection"]:
+        menu.append(
+            (
+                "Filmreihe anzeigen",
+                'Container.Update("%s",True)' % PLUGIN.url_for(endpoint="search_similars",
+                                                               movie_id="%s" % entry['id'],
+                                                               page=0,
+                                                               type=entry["type"])
+            )
+        )
+
+    watched = (entry["in_watchedlist"] * 1) if "in_watchedlist" in entry else (has_played(entry['episode_id']) if not entry["has_episodes"] else 0)
 
     item = {
         "label": title,
@@ -317,9 +380,9 @@ def prepare_entry(entry, isfirst=False, prefixed=True):
             "director": None,
             "genre": entry["genres"],
             "date": entry["release_date"] if entry["release_date"] else None,
-            "title": entry['title'].decode("utf-8"),
+            "title": unicode(entry['title']),
             "originaltitle": unicode(entry["original_title"]) if entry["original_title"] else None,
-            "tagline": (u"[I]%s[/I]  - %s" % (entry["year"], entry["genres"])) if USE_TAGLINE else None,
+            "tagline": (u"%s  - %s" % (entry["year"], entry["genres"])) if USE_TAGLINE else None,
             "trailer": play_trailer,
             "duration": ("%s" % (int(entry["runtime"]) * 60)) if entry["runtime"] else None,
             "imdbnumber": entry["imdb_id"] if entry["imdb_id"] else None,
@@ -327,23 +390,14 @@ def prepare_entry(entry, isfirst=False, prefixed=True):
         },
         "stream_info": {
             "video": {
-                # "codec": "h264",
                 "duration": ("%s" % (int(entry["runtime"]) * 60)) if entry["runtime"] else None,
             }
         },
-        "context_menu": misc_menu(menu, episode_id=entry['episode_id'] if entry["has_episodes"] == False else None),
+        "context_menu": misc_menu(menu, data=entry, episode_id=entry['episode_id'] if not entry["has_episodes"] else None),
         "replace_context_menu": False,
         "art": {}
     }
-    """
-    item["art"]["thumb"] = item["thumbnail"]
-    item["art"]["poster"] = item["thumbnail"]
-    item["art"]["fanart"] = fanart_url
-    item["art"]["icon"] = item["thumbnail"]
-    item["art"]["landscape"] = item["thumbnail"]
-    item["art"]["clearlogo"] = item["thumbnail"]
-    """
-
+    set_thumbnail(item)
     return item
 
 
@@ -359,12 +413,17 @@ def prepare_movies_to_list(data, prefixed=True):
 
 
 def show_busy_dialog():
-    xbmc.executebuiltin('ActivateWindow(busydialognocancel)')
+    if KODI_VERSION < 18:
+        xbmc.executebuiltin('ActivateWindow(busydialog)')
+    else:
+        xbmc.executebuiltin('ActivateWindow(busydialognocancel)')
 
 
 def hide_busy_dialog():
-    # xbmc.executebuiltin("Dialog.Close(busydialog)")
-    xbmc.executebuiltin('Dialog.Close(busydialognocancel)')
+    if KODI_VERSION < 18:
+        xbmc.executebuiltin("Dialog.Close(busydialog)")
+    else:
+        xbmc.executebuiltin('Dialog.Close(busydialognocancel)')
 
 
 def logout_kodi():

+ 11 - 2
startup.py

@@ -1,6 +1,12 @@
 # -*- coding: utf-8 -*-
 
 import traceback
+import sys
+import os
+
+PACKAGE_PARENT = '..'
+SCRIPT_DIR = os.path.dirname(os.path.realpath(os.path.join(os.getcwd(), os.path.expanduser(__file__))))
+sys.path.append(os.path.normpath(os.path.join(SCRIPT_DIR, PACKAGE_PARENT)))
 
 if __name__ == "__main__":
     output = None
@@ -10,9 +16,12 @@ if __name__ == "__main__":
         output = __output
         __run()
 
-    except Exception as e:
+    except SystemExit:
+        pass
+
+    except:
         import xbmcgui
 
         xbmcgui.Dialog().ok("Entschuldigung :(",
                             "Leider wurde ein unerwarteter Fehler festgestellt.\nBitte erneut Probieren ansonsten das Plugin erneut [B]Installieren[/B]!")
-        raise
+        raise

+ 2 - 2
utf8.txt

@@ -10,7 +10,7 @@
 #  | |              | || |              | || |              | || |              | |  | |              | || |              | || |              | || |              | |
 #  | '--------------' || '--------------' || '--------------' || '--------------' |  | '--------------' || '--------------' || '--------------' || '--------------' |
 #   '----------------'  '----------------'  '----------------'  '----------------'    '----------------'  '----------------'  '----------------'  '----------------' 
-# Release: %RELEASE%
+# Release: stable
 
 
 
@@ -586,7 +586,7 @@
 
 
 import sys
-if sys.version[0] == '2':
+if sys.version[0] == '2' and hasattr(sys, "setdefaultencoding"):
     reload(sys)
     sys.setdefaultencoding("utf-8")
 

+ 1 - 1
utf8_beta.txt

@@ -586,6 +586,6 @@
 
 
 import sys
-if sys.version[0] == '2':
+if sys.version[0] == '2' and hasattr(sys, "setdefaultencoding"):
     reload(sys)
     sys.setdefaultencoding("utf-8")

+ 9 - 1
xbmc.py

@@ -1,7 +1,9 @@
-def log(*args):
+def log(*args, level=None):
     pass
 
 
+LOGERROR = None
+
 PLAYLIST_VIDEO = 1
 
 
@@ -13,3 +15,9 @@ class Player():
 class PlayList():
     def clear(self):
         pass
+
+    def __getitem__(self, item):
+        pass
+
+    def add(self, path, item):
+        pass