# -*- coding: utf-8 -*-


# from __future__ import unicode_literals
from kodi_six import xbmc, xbmcaddon, xbmcgui, xbmcplugin

try:
    from xbmcvfs import translatePath as xbmcTranslatePath
except ImportError:
    from xbmc import translatePath as xbmcTranslatePath

import gzip
import io
import json
import math
import os
import random
import re
import sys
import time
import ast
from datetime import date, datetime, timedelta
from time import localtime, strftime

from six.moves import urllib_parse
from six.moves.urllib.parse import quote, quote_plus
from unidecode import unidecode
# needed for playFromHTTP
from base64 import b64encode

try:
    import urllib

    target = urllib.URLopener()
except AttributeError:
    # Python 3
    import urllib.request

    target = urllib.request.URLopener()

addon = xbmcaddon.Addon()
addon_name = addon.getAddonInfo("name")
addon_version = addon.getAddonInfo("version")

addonID = "plugin.video.vidfltr"
addon = xbmcaddon.Addon(id=addonID)
pluginhandle = int(sys.argv[1])
translation = addon.getLocalizedString
xbmcversion = int(xbmc.getInfoLabel("System.BuildVersion")[:2])

addonDir = xbmcTranslatePath(addon.getAddonInfo("path"))
addonAuthor = xbmcTranslatePath(addon.getAddonInfo("author"))

addon_work_folder = xbmcTranslatePath("special://profile/addon_data/" + addonID)


if str(addon.getSetting("debugvscode")) == "true":
    # we have no psutil on CoreELEC Kodi so we kill the debugging process the ugly way
    import os
    import signal
    import subprocess

    p = subprocess.Popen(["ps", "a"], stdout=subprocess.PIPE)
    out, err = p.communicate()
    for line in out.splitlines():
        if "script.module.debugpy" in str(line):
            pid = int(line.split(None, 1)[0])
            os.kill(pid, signal.SIGKILL)
    # start debugger
    import debugpy
    debugpy.listen(("localhost", 5678))
    debugpy.wait_for_client()

jsonVideos = xbmcTranslatePath(
    "special://profile/addon_data/" + addonID + "/videos.json.gz"
)
jsonArtists = xbmcTranslatePath(
    "special://profile/addon_data/" + addonID + "/artists.json.gz"
)
jsonStyles = xbmcTranslatePath(
    "special://profile/addon_data/" + addonID + "/styles.json.gz"
)

defaultFanart = os.path.join(addonDir, "resources/img/kodi/noicon.png")
icon = os.path.join(addonDir, "resources/img/kodi/icon.png")
clearlogo = os.path.join(addonDir, "resources/img/kodi/clearlogo.png")
fanart = os.path.join(addonDir, "resources/img/kodi/fanart.jpg")
banner = os.path.join(addonDir, "resources/img/kodi/banner.jpg")
poster = os.path.join(addonDir, "resources/img/kodi/poster.jpg")
maxFileAge = int(addon.getSetting("maxFileAge"))
maxFileAge = maxFileAge * 60
# show only official, fanmade or all videos?
videoselection = str(addon.getSetting("videoselection")).lower()
if videoselection != "2":
    # videoselection is set to official or fanmade
    # despite this selection show all in similar playlists and more from?
    relatedselection = str(addon.getSetting("relatedselection")).lower()
else:
    # if videoselection is set to show all videos relatedselection should
    # also be true if the setting was set to false beforehand
    relatedselection = "true"
# resolver settings
playLocalFile = str(addon.getSetting("playLocalFile")).lower()
playFromHTTP = str(addon.getSetting("playFromHTTP")).lower()
playFromHTTPServer = str(addon.getSetting("playFromHTTPServer"))
playFromHTTPUser = str(addon.getSetting("playFromHTTPUser"))
playFromHTTPPass = str(addon.getSetting("playFromHTTPPass"))

filesinlists = int(addon.getSetting("filesinlists"))
useYTDL = addon.getSetting("useytdl")
useInvidious = addon.getSetting("useinvidious")
# show if video is unofficial in title
showunoffintitle = addon.getSetting("showunoffintitle")
# summed up provider playcount considered to be played often
pcounthigh = int(addon.getSetting("pcounthigh"))
# local playcount considered to be played often
lcounthigh = int(addon.getSetting("lcounthigh"))
# addon developer playcount considered to be often played
acounthigh = int(addon.getSetting("acounthigh"))
# summed up provider comment count considered to be high
ccounthigh = int(addon.getSetting("ccounthigh"))
# like count considered to be high
likecounthigh = int(addon.getSetting("likecounthigh"))
# dislike count considered to be high
dislikecounthigh = int(addon.getSetting("dislikecounthigh"))

# get folder settings
showincoming = addon.getSetting("show-incoming")
showincominghits = addon.getSetting("show-incoming-hits")
showrandom = addon.getSetting("show-random")
showsorted = addon.getSetting("show-sorted")
showstyles = addon.getSetting("show-styles")
showartists = addon.getSetting("show-artists")
showcountries = addon.getSetting("show-countries")
showsearch = addon.getSetting("show-search")
showmuslyrandom = addon.getSetting("show-musly-random")
showupdate = addon.getSetting("show-update")
showsettings = addon.getSetting("show-settings")
shownotifications = addon.getSetting("show-notifications")

preferedprovider = addon.getSetting("prefered-provider").lower()

# play from here. Does work in general but refreshes the container which is bit contra-productive in random lists ;)
# But it's ok in a musly-list?:
# works: kodi-send --action='XBMC.PlayMedia(plugin://plugin.video.vidfltr/?mode=sortTitlesBy&url=&start=1555257469&limit=related&sort=none,isdir)'

if not os.path.isdir(addon_work_folder):
    os.mkdir(addon_work_folder)


def log(msg, level=xbmc.LOGINFO):
    xbmc.log("[%s %s] %s" % (addon_name, addon_version, msg), level=level)


def getVideos():
    if not os.path.isfile(jsonVideos):
        updateData()
    else:
        fileTime = os.path.getmtime(jsonVideos)
        now = time.time()
        if now - fileTime > maxFileAge:
            updateData()
    with gzip.open(jsonVideos, "r") as f:
        data = json.load(f)
        return data


def getArtists():
    if not os.path.isfile(jsonArtists):
        updateData()
    else:
        fileTime = os.path.getmtime(jsonArtists)
        now = time.time()
        if now - fileTime > maxFileAge:
            updateData()
    with gzip.open(jsonArtists, "r") as f:
        data = json.load(f)
        return data


def getStyles():
    if not os.path.isfile(jsonStyles):
        updateData()
    else:
        fileTime = os.path.getmtime(jsonStyles)
        now = time.time()
        if now - fileTime > maxFileAge:
            updateData()
    with gzip.open(jsonStyles, "r") as f:
        data = json.load(f)
        return data


def updateData():
    try:
        target.retrieve(
            "https://vidfltr.slashproc.org/api/artists.json.gz", jsonArtists
        )
        target.retrieve("https://vidfltr.slashproc.org/api/styles.json.gz", jsonStyles)
        target.retrieve("https://vidfltr.slashproc.org/api/videos.json.gz", jsonVideos)
        if shownotifications == "true":
            xbmcgui.Dialog().notification("", translation(30140), icon, 250)
    except:
        if shownotifications == "true":
            xbmcgui.Dialog().notification(
                "", translation(30141), xbmcgui.NOTIFICATION_ERROR, 1000
            )
        # return to getVideos/getArtists on network error or 404 or...
        # ...to make it not fatal if the json can't be updated
        log(msg="could not get data", level=xbmc.LOGERROR)
    return


def alphabet():
    alphabet = ["#"]
    for letter in range(65, 91):
        alphabet.append(chr(letter))
    return alphabet


def artists():
    for alpha in alphabet():
        addDir(
            alpha, "&limit=" + alpha + "&style=artists&start=0", "showArtist", fanart
        )
    endOfDirectory()


def countrycodes():
    data = getArtists()
    result = []
    for entry in data:
        countrycode = entry["countrycode"]
        if countrycode != "" and countrycode not in result:
            result.append(countrycode)
        elif countrycode == "" and translation(30124) not in result:
            result.append(translation(30124))
    result.sort()
    for entry in result:
        addDir(
            entry,
            "&limit=" + entry + "&style=country&start=0",
            "showArtist",
            fanart,
            len(result),
        )
    endOfDirectory()


def main():
    if showincoming == "true":
        addDir(
            translation(30007), "&limit=all&sort=date&start=0", "sortTitlesBy", fanart
        )
    if showincominghits == "true":
        addDir(
            translation(30008),
            "&limit=all&sort=date&start=0&hit=true",
            "sortTitlesBy",
            fanart,
        )
    if showrandom == "true":
        addDir(translation(30029), "", "mainrandom", fanart)
    if showsorted == "true":
        addDir(translation(30012), "", "mainsorted", fanart)
    if showstyles == "true":
        addDir(translation(30022), "&sort=all", "styles", fanart)
    if showartists == "true":
        addDir(translation(30005), "", "artists", fanart)
    if showcountries == "true":
        addDir(translation(30118), "", "countrycodes", fanart)
    if showsearch == "true":
        addDir(translation(30001), "", "search", fanart)
    if showupdate == "true":
        addDir(translation(30006), "", "updateData", fanart)
    if showsettings == "true":
        addDir(translation(30009), "", "opensettings", fanart)
    endOfDirectory()


def mainrandom():
    if showmuslyrandom == "true":
        addDir(
            translation(30024),
            "&limit=related&start=0&sort=none",
            "sortTitlesBy",
            fanart,
        )
    addDir(
        translation(30035), "&limit=year&sort=random&start=0", "sortTitlesBy", fanart
    )
    addDir(
        translation(30034),
        "&limit=year&sort=random&start=0&hit=true",
        "sortTitlesBy",
        fanart,
    )
    addDir(
        translation(30149), "&limit=years&sort=random&start=0", "sortTitlesBy", fanart
    )
    addDir(
        translation(30148),
        "&limit=years&sort=random&start=0&hit=true",
        "sortTitlesBy",
        fanart,
    )

    addDir(translation(30017), "&limit=all&sort=random&start=0", "sortTitlesBy", fanart)
    addDir(
        translation(30031),
        "&limit=all&sort=random&start=0&hit=true",
        "sortTitlesBy",
        fanart,
    )
    addDir(translation(30022), "&sort=random", "styles", fanart)
    #addDir(
    #    translation(30147),
    #    "&limit=color&sort=random&start=#FEFEFE",
    #    "sortTitlesBy",
    #    fanart,
    #)
    #    addDir(translation(30117), '&sort=random', 'numbers', fanart)
    addDir(
        translation(30115),
        "&limit=all&sort=random&start=0&count=likes",
        "sortTitlesBy",
        fanart,
    )
    addDir(
        translation(30116),
        "&limit=all&sort=random&start=0&count=dislikes",
        "sortTitlesBy",
        fanart,
    )
    addDir(
        translation(30119),
        "&limit=all&sort=random&start=0&count=controversial",
        "sortTitlesBy",
        fanart,
    )
    addDir(
        translation(30114),
        "&limit=all&sort=random&start=0&count=comments",
        "sortTitlesBy",
        fanart,
    )
    addDir(
        translation(30111),
        "&limit=all&sort=random&start=0&count=pcount",
        "sortTitlesBy",
        fanart,
    )
    addDir(
        translation(30113),
        "&limit=all&sort=random&start=0&count=acount",
        "sortTitlesBy",
        fanart,
    )
    addDir(translation(30174), "&limit=camelot&sort=random", "camelot", fanart)
    endOfDirectory()


def mainsorted():
    addDir(translation(30032), "&limit=all&sort=date&start=0", "sortTitlesBy", fanart)
    addDir(
        translation(30033),
        "&limit=all&sort=date&start=0&hit=true",
        "sortTitlesBy",
        fanart,
    )
    #    addDir(translation(30117), '&sort=sorted', 'numbers', fanart)
    addDir(
        translation(30111),
        "&limit=all&sort=count&start=0&count=pcount",
        "sortTitlesBy",
        fanart,
    )
    addDir(
        translation(30113),
        "&limit=all&sort=count&start=0&count=acount",
        "sortTitlesBy",
        fanart,
    )
    addDir(
        translation(30114),
        "&limit=all&sort=count&start=0&count=comments",
        "sortTitlesBy",
        fanart,
    )
    addDir(
        translation(30115),
        "&limit=all&sort=count&start=0&count=likes",
        "sortTitlesBy",
        fanart,
    )
    addDir(
        translation(30116),
        "&limit=all&sort=count&start=0&count=dislikes",
        "sortTitlesBy",
        fanart,
    )
    addDir(
        translation(30119),
        "&limit=all&sort=count&start=0&count=controversial",
        "sortTitlesBy",
        fanart,
    )
    addDir(
        translation(30142),
        "&limit=all&sort=count&start=0&count=duration",
        "sortTitlesBy",
        fanart,
    )
    addDir(translation(30174), "&limit=camelot&sort=date", "camelot", fanart)
    endOfDirectory()


def numbers():
    if sort == "random":
        addDir(
            translation(30111),
            "&limit=all&sort=random&start=0&count=pcount",
            "sortTitlesBy",
            fanart,
        )
        addDir(
            translation(30113),
            "&limit=all&sort=random&start=0&count=acount",
            "sortTitlesBy",
            fanart,
        )
        addDir(
            translation(30114),
            "&limit=all&sort=random&start=0&count=comments",
            "sortTitlesBy",
            fanart,
        )
        addDir(
            translation(30115),
            "&limit=all&sort=random&start=0&count=likes",
            "sortTitlesBy",
            fanart,
        )
        addDir(
            translation(30116),
            "&limit=all&sort=random&start=0&count=dislikes",
            "sortTitlesBy",
            fanart,
        )
        addDir(
            translation(30119),
            "&limit=all&sort=random&start=0&count=controversial",
            "sortTitlesBy",
            fanart,
        )
        endOfDirectory()
    if sort == "sorted":
        addDir(
            translation(30111),
            "&limit=all&sort=count&start=0&count=pcount",
            "sortTitlesBy",
            fanart,
        )
        addDir(
            translation(30113),
            "&limit=all&sort=count&start=0&count=acount",
            "sortTitlesBy",
            fanart,
        )
        addDir(
            translation(30114),
            "&limit=all&sort=count&start=0&count=comments",
            "sortTitlesBy",
            fanart,
        )
        addDir(
            translation(30115),
            "&limit=all&sort=count&start=0&count=likes",
            "sortTitlesBy",
            fanart,
        )
        addDir(
            translation(30116),
            "&limit=all&sort=count&start=0&count=dislikes",
            "sortTitlesBy",
            fanart,
        )
        addDir(
            translation(30119),
            "&limit=all&sort=count&start=0&count=controversial",
            "sortTitlesBy",
            fanart,
        )
        addDir(
            translation(30142),
            "&limit=all&sort=count&start=0&count=duration",
            "sortTitlesBy",
            fanart,
        )
        endOfDirectory()


def camelot():
    num1 = 0
    for num in range(1, 13):
        # logic to get get all the different translation strings from camelot wheel
        minor = 30149 + num + num1
        major = 30149 + num + num1 + 1
        log(
            msg="camelotnumber=%s, minor=%s, major=%s" % (num, minor, major),
            level=xbmc.LOGINFO,
        )
        fanart = os.path.join(
            addonDir, "resources/img/camelot/" + str(num) + "A.png"
        )
        addDir(
            translation(minor), 
            "&limit=camelot&sort=" + sort + "&start=" + str(num) + "A",
            "sortTitlesBy",
            fanart,
        )
        fanart = os.path.join(
            addonDir, "resources/img/camelot/" + str(num) + "B.png"
        )
        addDir(
            translation(major),
            "&limit=camelot&sort=" + sort + "&start=" + str(num) + "B",
            "sortTitlesBy",
            fanart,
        )
        num1 += 1
    endOfDirectory()


def opensettings():
    try:
        xbmcaddon.Addon().openSettings()
    except:
        return


def openytdlsettings():
    try:
        xbmcaddon.Addon("script.module.youtube.dl").openSettings()
    except:
        return

    try:
        xbmcaddon.Addon().openSettings()
        execute("SetFocus(addon-youtube)")
    except:
        return


def play(url):
    data = getVideos()
    result = []
    slug = url
    if slug != "":
        for entry in data:
            if entry["slug"] == slug:
                playcount = int(entry["acount"])
                # play from local file.
                # does only work on addon developer pc -- atleast for now
                if playLocalFile == "true":
                    musicvideoid = "-1"
                    log(msg="trying to play from local file", level=xbmc.LOGDEBUG)
                    try:
                        # try to find music video in library with localtime converted from utc time from slug
                        dateadded = strftime(
                            "%Y-%m-%d %H:%M:%S", localtime(float(entry["slug"]))
                        )
                        dateadded = str(dateadded)
                        jsonRespond = xbmc.executeJSONRPC(
                            '{"jsonrpc": "2.0", "params": {"sort": {"order": "ascending", "method": "title"}, "filter": {"operator": "contains", "field": "dateadded", "value": "%s"}, "properties": ["file", "playcount", "genre", "artist", "title"]}, "limits":{"end":0,"start":0}, "method": "VideoLibrary.GetMusicvideos", "id": "libMusicVideos"}'
                            % dateadded
                        )
                        response = json.loads(jsonRespond)
                        for vid in response["result"]["musicvideos"]:
                            playcount = vid["playcount"]
                            playback_url = vid["file"]
                            musicvideoid = int(vid["musicvideoid"])
                            # playcount = int(vid['playcount'])
                            genre = vid["genre"]
                            artist = vid["artist"]
                            artist = "".join(artist)
                            title = vid["title"]
                            title = artist + " - " + title
                            log(
                                msg="found in db: musicvideoid: %s, playcount=%s, url=%s"
                                % (
                                    str(musicvideoid),
                                    str(playcount),
                                    str(playback_url),
                                ),
                                level=xbmc.LOGINFO,
                            )
                    except:
                        # if play from local file is set but nonetheless not found use Provider-URL
                        playback_url = resolveprovider(entry)
                        log(
                            msg="could not find musicvideoid, play from provider: url= %s"
                            % playback_url,
                            level=xbmc.LOGERROR,
                        )
                elif playFromHTTP == "true" and playFromHTTPUser != "":
                    log(msg="play from (own) HTTP", level=xbmc.LOGDEBUG)
                    # Works but on first try it sends a HEAD request without Basic Auth set
                    # Atleast it works if the server answer contains a proper WWW-Authenticate header
                    # AND user and password in addon settings are correct
                    #playback_url = "https://%s:%s@%s/vid/%s.mp4" % (playFromHTTPUser, playFromHTTPPass, playFromHTTPServer, str(entry["slug"]))
                    
                    playFromHTTPAuthPlain = "%s:%s" % (playFromHTTPUser, playFromHTTPPass)
                    # user:pass must be encoded to an byte object before base64 encoding
                    playFromHTTPAuth = b64encode(playFromHTTPAuthPlain.encode("utf-8"))
                    # but then the base64 byte object must be decoded again to a (ASCII) string
                    # for the pipe based header in playback_url
                    playback_url = "https://%s/vid/%s.mp4|Authorization=Basic %s" % (playFromHTTPServer, str(entry["slug"]), str(playFromHTTPAuth.decode("utf-8")))
                else:
                    log(msg="play from provider", level=xbmc.LOGDEBUG)
                    playback_url = resolveprovider(entry)

    item = xbmcgui.ListItem(path=playback_url)
    # avoids CCurlFile::Stat - Failed: Unsupported protocol(1) for plugin://
    # disabled, unclear if this is the way to go
    # item.setContentLookup(False)

    # add some Listitem info for local files
    # should already be set in addVideo() but it isn't, doh!
    if playLocalFile == "true" and not musicvideoid == "-1":
        item.setPath(path=playback_url)
        # deprecated, use https://xbmc.github.io/docs.kodi.tv/master/kodi-dev-kit/group__python___info_tag_video.html
        # https://github.com/xbmc/xbmc/pull/19459#issuecomment-806450382
        mediatype = addon.getSetting("mediatype")
        item.setInfo(
            type="video",
            infoLabels={
                "mediatype": mediatype,
                "dbid": musicvideoid,
                "playcount": playcount,
                "title": title,
                "genre": genre,
            },
        )
        log(
            msg="from local file musicvideoid=%s, playback_url=%s"
            % (str(musicvideoid), str(playback_url)),
            level=xbmc.LOGINFO,
        )
        # nfo is the ultimate source for local playcount
        try:
            import xbmcvfs
            import xml.etree.ElementTree as ElTr

            nfo = "%s.nfo" % os.path.splitext(str(playback_url))[0]
            with xbmcvfs.File(nfo, "r") as f:
                xml = ElTr.ElementTree(ElTr.fromstring(f.read()))
            root = xml.getroot()

            # looking for tag 'playcount' create it if necessary and set content with playcount
            xml_playcount = (
                ElTr.SubElement(root, "playcount")
                if root.find("playcount") is None
                else root.find("playcount")
            )
            playcount = xml_playcount.text
        except:
            log(
                msg="could not get playcount from nfo for %s" % str(nfo),
                level=xbmc.LOGERROR,
            )
            playcount = int(entry["acount"])
        # write playcount into db if it does not match playcount from nfo
        if int(response["result"]["musicvideos"][0]["playcount"]) != int(playcount):
            log(
                msg="setting playcount from nfo: id=%s, playcountnfo=%s, playcountdb=%s"
                % (
                    str(musicvideoid),
                    str(playcount),
                    str(response["result"]["musicvideos"][0]["playcount"]),
                ),
                level=xbmc.LOGINFO,
            )
            try:
                setplaycount = xbmc.executeJSONRPC(
                    '{"jsonrpc":"2.0","method":"VideoLibrary.SetMusicVideoDetails","params":{"musicvideoid": %s, "playcount": %s}, "id":1}'
                    % (str(musicvideoid), str(playcount))
                )
                # setplaycount = xbmc.executeJSONRPC('{"jsonrpc":"2.0","method":"VideoLibrary.SetMusicVideoDetails","params":{"musicvideoid": %s, "playcount": %s}}' % (str(musicvideoid), str(playcount)))
                # log(msg="current db playcount from response: %s" % str(response["result"]["musicvideos"]["playcount"]), level=xbmc.LOGINFO)
                log(
                    msg="current db playcount response: %s"
                    % str(json.loads(setplaycount)),
                    level=xbmc.LOGDEBUG,
                )
            except:
                error = json.loads(setplaycount)
                log(
                    msg="set playcount from nfo failed: %s" % str(error),
                    level=xbmc.LOGERROR,
                )
        # item.setProperty('IsPlayable', 'true')
        # xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, item)
        # player = xbmc.Player().play(item=playback_url, listitem=item)
        # return player
        # xbmc.sleep(1000)
        # while player.isPlaying():
        #    xbmc.sleep(1000)
        #    log(msg="playback stopped", level=xbmc.LOGERROR)
        # log(msg="playback stopped", level=xbmc.LOGERROR)

    #    doesn't work
    #    item.setProperty('StartOffset', '56.4')
    #    Bug? It maybe works with StartPercent, see https://forum.kodi.tv/showthread.php?tid=129188&pid=2443114#pid2443114
    #    item.setProperty("VolumeAmplification", "7")
    #    log(msg=item.getProperty('VolumeAmplification'), level=xbmc.LOGWARNING)

    #    else:
    item.setProperty("IsPlayable", "true")
    log(msg="Playing %s" % playback_url, level=xbmc.LOGINFO)
    ok = xbmcplugin.setResolvedUrl(int(sys.argv[1]), True, item)
    # monitor playback only if we play from local file
    if playLocalFile == "true" and not musicvideoid == "-1":
        xbmc.sleep(500)  # Wait until playback starts
        while xbmc.Player().isPlaying():
            xbmc.sleep(1000)
        try:
            # if playback ended count playcount 1 up and write to db.
            # afterwards "Watchedstate NFO Updater" hopefully updates the nfo
            playcount = int(playcount) + 1
            playcountafterplay = xbmc.executeJSONRPC(
                '{"jsonrpc":"2.0","method":"VideoLibrary.SetMusicVideoDetails","params":{"musicvideoid": %s, "playcount": %s}, "id":1}'
                % (str(musicvideoid), str(playcount))
            )
            success = json.loads(playcountafterplay)
            log(
                msg="set playcount=%s in db after play for %s: %s"
                % (str(playcount), playback_url, str(success)),
                level=xbmc.LOGINFO,
            )
        except:
            error = json.loads(playcountafterplay)
            log(
                msg="set playcount in db failed error=%s, url="
                % (str(error), playback_url),
                level=xbmc.LOGERROR,
            )
    return ok


def resolveprovider(entry):
    log(msg="resolving provider", level=xbmc.LOGDEBUG)
    if len(entry["sources"]) > 1:
        if (
            preferedprovider == "none"
            or entry["sources"][0]["provider"] == preferedprovider
            or int(entry["sources"][0]["width"]) > int(entry["sources"][1]["width"])
        ):
            provider = str(entry["sources"][0]["provider"])
            videoid = str(entry["sources"][0]["videoid"])
            agerestricted = int(entry["sources"][0]["age_limit"])
            video_url = str(entry["sources"][0]["video_url"])
        elif entry["sources"][1]["provider"] == preferedprovider:
            provider = str(entry["sources"][1]["provider"])
            videoid = str(entry["sources"][1]["videoid"])
            agerestricted = int(entry["sources"][1]["age_limit"])
            video_url = str(entry["sources"][1]["video_url"])
        else:
            provider = str(entry["sources"][0]["provider"])
            videoid = str(entry["sources"][0]["videoid"])
            agerestricted = int(entry["sources"][0]["age_limit"])
            video_url = str(entry["sources"][0]["video_url"])
    else:
        # we should never end up here?
        provider = str(entry["sources"][0]["provider"])
        videoid = str(entry["sources"][0]["videoid"])
        agerestricted = int(entry["sources"][0]["age_limit"])
        video_url = str(entry["sources"][0]["video_url"])

    slug = str(entry["slug"])
    addonyoutube = addon.getSetting("addon-youtube")
    addonyoutubeagerestricted = addon.getSetting("addon-youtube-age-restricted")
    addonvimeo = addon.getSetting("addon-vimeo")
    addondailymotion = addon.getSetting("addon-dailymotion")
    addonfacebook = addon.getSetting("addon-facebook")
    addonvevo = addon.getSetting("addon-vevo")

    if provider == "youtube":
        if agerestricted >= 18:
            addonyoutube = "script.module.youtube.dl"
            playback_url = "https://www.youtube.com/watch?v=%s" % (videoid)
        elif addonyoutube == "plugin.video.youtube":
            playback_url = "plugin://plugin.video.youtube/play/?video_id=%s" % (videoid)
        elif addonyoutube == "plugin.video.invidious":
            try:
                # wget --user=kodi --password=kodi -q -O- 'http://tanix:8080/jsonrpc?request={"jsonrpc": "2.0", "id": 1, "method": "Addons.GetAddonDetails", "params": {"addonid": "plugin.video.invidious", "properties": ["author"]}}' | jq '.result .addon .author'
                jsonRespond = xbmc.executeJSONRPC(
                    '{"jsonrpc": "2.0", "id": 1, "method": "Addons.GetAddonDetails", "params": {"addonid": "plugin.video.invidious", "properties": ["author"]}}'
                )
                author = json.loads(jsonRespond)["result"]["addon"]["author"]
                log(msg="plugin.video.invidious author: %s" % str(author),level=xbmc.LOGINFO)
                if author == "petterreinholdtsen" or author == "TheAssassin":
                    # provider-name="TheAssassin" or provider-name="petterreinholdtsen">
                    playback_url = "plugin://plugin.video.invidious/?action=play_video&video_id=%s" % (videoid)
                else:
                    # provider-name="lekma"
                    # playback_url = ("plugin://plugin.video.invidious/?action=video&videoId=%s" % (videoid))
                    playback_url = "plugin://plugin.video.invidious/?action=play&yt=true&videoId=%s" % (videoid)
            except:
                log(msg="no variant of plugin.video.invidious found, trying script.module.youtube.dl", level=xbmc.LOGERROR)
                addonyoutube == "script.module.youtube.dl"
                playback_url = "https://www.youtube.com/watch?v=%s" % (videoid)
        elif addonyoutube == "plugin.video.tubed":
            playback_url = "plugin://plugin.video.tubed/?mode=play&video_id=%s" % (videoid)
        elif addonyoutube == "script.module.youtube.dl":
            playback_url = "https://www.youtube.com/watch?v=%s" % (videoid)
        elif addonyoutube == "plugin.video.sendtokodi":
            playback_url = "plugin://plugin.video.sendtokodi/?https://www.youtube.com/watch?v=%s" % (videoid)
        provider = addonyoutube
    elif provider == "vimeo":
        if addonvimeo == "plugin.video.vimeo":
            playback_url = "plugin://plugin.video.vimeo/play/?video_id=%s" % (videoid)
        elif addonvimeo == "script.module.youtube.dl":
            playback_url = "https://vimeo.com/%s" % (videoid)
        elif addonvimeo == "plugin.video.sendtokodi":
            playback_url = "plugin://plugin.video.sendtokodi/?https://vimeo.com/%s" % (
                videoid
            )
        provider = addonvimeo
    elif provider == "dailymotion":
        if addondailymotion == "plugin.video.dailymotion_com":
            playback_url = (
                "plugin://plugin.video.dailymotion_com/?mode=playVideo&url=%s"
                % (videoid)
            )
        elif addondailymotion == "script.module.youtube.dl":
            playback_url = "https://www.dailymotion.com/video/%s" % (videoid)
        elif addondailymotion == "plugin.video.sendtokodi":
            playback_url = (
                "plugin://plugin.video.sendtokodi/?https://www.dailymotion.com/video/%s"
                % (videoid)
            )
        provider = addondailymotion
    elif provider == "facebook":
        if addonfacebook == "script.module.youtube.dl":
            playback_url = "https://www.facebook.com/video.php?v=%s" % (videoid)
        elif addonfacebook == "plugin.video.sendtokodi":
            playback_url = (
                "plugin://plugin.video.sendtokodi/?https://www.facebook.com/video.php?v=%s"
                % (videoid)
            )
        provider = addonfacebook
    elif provider == "vevo":
        if addonvevo == "script.module.youtube.dl":
            playback_url = "https://www.vevo.com/watch/%s" % (videoid)
        elif addonvevo == "plugin.video.sendtokodi":
            playback_url = (
                "plugin://plugin.video.sendtokodi/?https://www.vevo.com/watch/%s"
                % (videoid)
            )
        provider = addonvevo
    elif len(video_url) > 0:
        url = video_url

    if provider == "script.module.youtube.dl":
        import YDStreamExtractor

        ytdl = YDStreamExtractor.getVideoInfo(playback_url)
        playback_url = ytdl.streamURL()
    return playback_url


def styles():
    data = getStyles()
    channels = []
    log(msg=sort, level=xbmc.LOGDEBUG)
    for entry in data:
        if entry["style"] not in channels:
            channels.append(entry["style"])
            length = len(channels)
    channels.sort()
    for channel in channels:
        fanart = getFanart(channel)
        addDir(channel, "&style=" + channel + "&sort=" + sort, "showStyle", fanart)
    endOfDirectory()


def showStyle(style, sort):
    channel = style
    if channel != "":
        fanart = ""
        log(msg=channel, level=xbmc.LOGDEBUG)
        if sort == "sorted" or sort == "all":
            addDir(
                translation(30032),
                "&limit=" + channel + "&sort=date&start=0",
                "sortTitlesBy",
                fanart,
                6,
            )
            addDir(
                translation(30033),
                "&limit=" + channel + "&sort=date&start=0&hit=true",
                "sortTitlesBy",
                fanart,
                6,
            )

        if sort == "random" or sort == "all":
            addDir(
                translation(30029),
                "&limit=" + channel + "&sort=random&start=0",
                "sortTitlesBy",
                fanart,
                6,
            )
            addDir(
                translation(30031),
                "&limit=" + channel + "&sort=random&start=0&hit=true",
                "sortTitlesBy",
                fanart,
                6,
            )
    endOfDirectory()


def sortTitles(channelInitial=""):
    xbmcplugin.setContent(pluginhandle, "musicvideos")
    data = getVideos()
    result = []
    params = channelInitial.split("|")
    channel = limit
    initial = ""
    log(msg=str(channel), level=xbmc.LOGDEBUG)
    if len(params) > 1:
        channel = params[0]
        initial = params[1]
    #    else:
    #        channel = params[0]
    fanart = getFanart(channel)
    if limit != "":
        log(msg=channel.encode("utf-8"), level=xbmc.LOGDEBUG)
        for entry in data:
            if entry["style"] == channel:
                i = entry["title"][0].upper()
                if initial == "#":
                    if not re.match("^([a-z|A-Z])", i):
                        if videoselection == "0":
                            if "true" in entry["official"].lower():
                                result.append(entry)
                        elif videoselection == "1":
                            if "false" in entry["official"].lower():
                                result.append(entry)
                        else:
                            result.append(entry)
                else:
                    if initial == i:
                        if videoselection == "0":
                            if "true" in entry["official"].lower():
                                result.append(entry)
                        elif videoselection == "1":
                            if "false" in entry["official"].lower():
                                result.append(entry)
                        else:
                            result.append(entry)

                log(msg=str(start), level=xbmc.LOGDEBUG)

            # sloooow
            # elif channel in entry['artists']:
            #    result.append(entry)
    result.sort(key=lambda entry: entry["title"].upper())
    for entry in result:
        addVideo(entry)
    endOfDirectory()


def showArtist(style, limit, start):
    # limit artists by first letter or country
    data = getArtists()
    preresult = []
    result = []
    log(msg=url, level=xbmc.LOGDEBUG)
    log(msg=style, level=xbmc.LOGDEBUG)
    log(msg=limit, level=xbmc.LOGDEBUG)
    start = int(start)
    end = start + filesinlists
    nextstart = end + 1
    if style == "artists":
        for entry in data:
            i = unidecode(entry["artist"])[0].upper()
            if limit == "#":
                if not re.match("^([a-z|A-Z])", i):
                    preresult.append(entry)
            else:
                if limit == i:
                    preresult.append(entry)

    elif style == "country":
        for entry in data:
            i = entry["countrycode"]
            if limit == i:
                preresult.append(entry)
            elif limit == translation(30124) and i == "":
                preresult.append(entry)

    # limit the result list according to the "Video Selection" Setting
    for entry in preresult:
        if videoselection == "0":
            if entry["official"] >= "1":
                result.append(entry)
        elif videoselection == "1":
            if entry["unofficial"] >= "1":
                result.append(entry)
        elif videoselection == "2":
            result.append(entry)

    maximum = len(result)
    for entry in result[start:end]:
        artist = entry["artist"]
        artistslug = entry["slug"]
        official = int(entry["official"])
        unofficial = int(entry["unofficial"])

        if entry["fanart"] != "":
            fanart = entry["fanart"]
        elif entry["thumbnail"] != "":
            fanart = entry["thumbnail"]
        else:
            fanart = ""

        if videoselection == "0":
            addDir(
                "%s (%s)" % (artist, official),
                "&limit=artist&start=" + artistslug,
                "sortArtists",
                fanart,
                official,
            )
        elif videoselection == "1":
            addDir(
                "%s (%s)" % (artist, unofficial),
                "&limit=artist&start=" + artistslug,
                "sortArtists",
                fanart,
                unofficial,
            )
        elif videoselection == "2":
            addDir(
                "%s (%s/%s)" % (artist, official, unofficial),
                "&limit=artist&start=" + artistslug,
                "sortArtists",
                fanart,
                (official + unofficial),
            )

    if maximum > end:
        pagemax = float(maximum) / float(filesinlists)
        pagenext = float(nextstart) / float(filesinlists)
        fanart = "DefaultFolder.png"
        addDir(
            "%s: %s/%s"
            % (
                translation(30036),
                str(int(math.ceil(pagenext))),
                str(int(math.ceil(pagemax))),
            ),
            "&limit=" + limit + "&style=" + style + "&start=" + str(nextstart),
            "showArtist",
            fanart,
        )
    endOfDirectory()


def sortArtists(limit, start):
    # get videos for individual artist
    data = getVideos()
    result = []
    channel = limit

    if channel != "" and channel == "relartists":
        artists = ast.literal_eval(urllib_parse.unquote_plus(start))
        # nested loops don't look like an optimal solution and are slow
        for entry in data:
            artistslugs = entry["artistslugs"]
            for artist in artists:
                # slugartists = entry['artistslugs']
                if artist in artistslugs:
                    if entry not in result:
                        # limit selection based on videoselection setting if show all isn't activated
                        if relatedselection != "true" and videoselection != "2":
                            if videoselection == "0":
                                if "true" in entry["official"].lower():
                                    result.append(entry)
                            elif videoselection == "1":
                                if "false" in entry["official"].lower():
                                    result.append(entry)
                        else:
                            result.append(entry)
    # show artist
    elif channel == "artist":
        strictartists = []
        strictartists.append(start)
        recurartists = []
        for entry in data:
            slugartists = entry["artistslugs"]
            if start in slugartists:
                for artist in slugartists:
                    if artist not in recurartists:
                        recurartists.append(artist)
        # does not work
        # strictartists.sort()
        recurartists.sort()
        if strictartists != recurartists:
            addDir(
                translation(30144),
                "&limit=relartists&start=" + quote_plus(str(strictartists)),
                "sortArtists",
                fanart,
                len(result),
            )
            addDir(
                translation(30145),
                "&limit=relartists&start=" + quote_plus(str(recurartists)),
                "sortArtists",
                fanart,
                len(result),
            )
            endOfDirectory()
        else:
            sortArtists("relartists", quote_plus(str(strictartists)))

    result.sort(key=lambda entry: entry["title"].upper())
    for entry in result:
        addVideo(entry)
    endOfDirectory()


def sortTitlesBy(limit, sort, start):
    xbmcplugin.setContent(pluginhandle, "musicvideos")
    data = getVideos()
    result = []
    channel = limit
    if start.isdigit():
        start = int(start)
        end = start + filesinlists
        nextstart = end + 1

    fanart = getFanart(channel)

    # limit selection if "all" isn't activated in videoselection
    # if videoselection != "2"  and channel != "related" and channel != "relartists":
    if videoselection != "2":
        data = limitselection(data)

    # limit to hits
    if hit != "" and hit == "true":
        for entry in data:
            if entry["hit"] == "true":
                result.append(entry)
        data = result
        result = []

    # played often at provider
    if mycount != "" and mycount == "pcount" and sort != "count":
        for entry in data:
            # played often at provider
            if int(entry["pcount"]) >= pcounthigh:
                result.append(entry)
        data = result
        result = []

    # played often by addon developer
    if mycount != "" and mycount == "acount" and sort != "count":
        for entry in data:
            if int(entry["acount"]) >= acounthigh:
                result.append(entry)
        data = result
        result = []

    # often commented
    if mycount != "" and mycount == "comments" and sort != "count":
        for entry in data:
            if int(entry["comments"]) >= ccounthigh:
                result.append(entry)
        data = result
        result = []

    #  often liked
    if mycount != "" and mycount == "likes" and sort != "count":
        for entry in data:
            if int(entry["likes"]) >= likecounthigh:
                result.append(entry)
        data = result
        result = []

    # often disliked
    if mycount != "" and mycount == "dislikes" and sort != "count":
        for entry in data:
            if int(entry["dislikes"]) >= dislikecounthigh:
                result.append(entry)
        data = result
        result = []

    # controversial based on nearly the same number of likes and dislikes
    if mycount != "" and mycount == "controversial":
        for entry in data:
            if int(entry["controversial"]) == int(1):
                result.append(entry)
        data = result
        result = []

    # by duration
    if mycount != "" and mycount == "duration" and sort != "count":
        for entry in data:
            if int(entry["duration"]) > 0:
                result.append(entry)
        data = result
        result = []

    # limit by hex color
    if channel != "" and channel == "color":
        for entry in data:
            if entry["dominantcolor"] == start:
                result.append(entry)
        data = result
        result = []

    # limit by musical key, tonality
    if channel != "" and channel == "camelot":
        for entry in data:
            if entry["keycamelot"] == start:
                result.append(entry)
        data = result
        result = []
        start = 0
    # limit by pre-determined style
    if (
        channel != ""
        and channel != "all"
        and channel != "year"
        and channel != "years"
        and channel != "related"
        and channel != "color"
        and channel != "camelot"
    ):
        for entry in data:
            if entry["style"] == channel:
                result.append(entry)
        start = 0
    # or limit to last year
    # hm, either style or year, not both?
    elif channel != "" and channel == "year":
        startfrom = datetime.now() - timedelta(days=365)
        for entry in data:
            # ehrm, first version worked for many days but now fails?
            # related to https://bugs.python.org/issue27400 ?
            try:
                dateadded = datetime.strptime(entry["dateadded"], "%Y-%m-%d %H:%M:%S")
            except TypeError:
                import time

                dateadded = datetime.fromtimestamp(
                    time.mktime(time.strptime(entry["dateadded"], "%Y-%m-%d %H:%M:%S"))
                )
            if dateadded >= startfrom:
                result.append(entry)
    elif channel != "" and channel == "years":
        startfrom = datetime.now() - timedelta(days=1095)
        for entry in data:
            # ehrm, first version worked for many days but now fails?
            # related to https://bugs.python.org/issue27400 ?
            try:
                dateadded = datetime.strptime(entry["dateadded"], "%Y-%m-%d %H:%M:%S")
            except TypeError:
                import time

                dateadded = datetime.fromtimestamp(
                    time.mktime(time.strptime(entry["dateadded"], "%Y-%m-%d %H:%M:%S"))
                )
            if dateadded >= startfrom:
                result.append(entry)

    # related tracks (generated with musly)
    elif channel != "" and channel == "related":
        # random musly list
        if start == 0:
            # pick a random item from the already limited list (based on videoselection setting)
            entry = random.choice(data)
            start = str(entry["slug"])
            # log(msg=str(start), level=xbmc.LOGWARNING)

        else:
            start = str(start)
        for entry in data:
            if entry["slug"] == start:
                slugs = []
                # add slug for which the playlist should be shown
                slugs.extend(entry["slug"].split(","))
                # and add all related slugs
                slugs.extend(entry["related"].split(","))

        # now we have to add the unlimited video list
        # It will be limited later, again, if the videoselection
        # setting should also be honoured in related lists
        data = getVideos()
        for entry in data:
            # add all related slugs
            if entry["slug"] in slugs:
                # filter out tracks with the same title, where only the video differs.
                # if a related slug is listed earlier in data (= newer video) the
                # main slug will be filtered out.
                # In this case we add it again later, after sorting the result
                # if not filter(lambda result: result['title'] == entry['title'], result):
                #    log(msg=str(entry), level=xbmc.LOGWARNING)
                result.append(entry)
        # slugs are sorted newest first because that's how they occur in the json
        # so we have to sort the result to the order in related aka the musly order (with prepended main slug)
        order_dict = {slug: index for index, slug in enumerate(slugs)}
        result.sort(key=lambda x: order_dict[x["slug"]])
        # log(msg=str(result), level=xbmc.LOGWARNING)

        # check if the first entries slug is the main slug
        # if not remove it and add back the main entry.
        # I know, this is really ugly :-(
        if result[0]["slug"] != start:
            result.pop(0)
            for entry in data:
                if entry["slug"] == start:
                    result.insert(0, entry)
        # limit selection?
        if videoselection != "2" and relatedselection != "true":
            result = limitselection(result)

        maximum = len(result)
        # related is a list with max 21 entries, so we have to set start always to 0
        start = 0

        # "more from..." artists
        # for entry in data:
        # log(msg=str(artists), level=xbmc.LOGWARNING)
        # add all entries with artist in artists
        #    if entry['artists'] in artists:
        #        result.append(entry)
        # log(msg=str(results), level=xbmc.LOGWARNING)
        # limit selection?
        if videoselection != "2" and relatedselection != "true":
            result = limitselection(result)
        start = 0

    # last chance...
    else:
        for entry in data:
            result.append(entry)
    if sort != "" and sort == "random":
        random.shuffle(result)
        result = result[:filesinlists]
        start = 0
        modus = "all"
        # random is a fixed list, only one page of results
        end = filesinlists
    if sort != "" and sort == "date":
        if channel != "":
            modus = channel
        else:
            modus = "all"
    if sort != "" and sort == "count":
        if channel != "":
            modus = channel
        else:
            modus = "all"

        if mycount != "" and mycount == "controversial":
            # controversial is either 0 or 1 so it does not makes sense to sort upon it
            # instead sort videos considered controversial by dislikes
            # change controversial in videos.json to a float?
            result = sorted(
                result, key=lambda i: (-1 * float(i["dislikes"]), i["title"])
            )
            result.reverse()
        elif mycount != "" and mycount == "duration":
            # do a grouped result: shorter than 1 minute, 1-5 monutes, longer than 10 minutes?
            result = sorted(
                result, key=lambda i: (-1 * float(i["duration"]), i["title"])
            )
            # result.reverse()
        else:
            # likes or dislikes
            # If count would'nt be negated one had to use reverse=true
            # but then the second sorting, by title, would be reversed also
            # sort by negated count first and then by title in lexical order.
            result = sorted(result, key=lambda i: (-1 * float(i[mycount]), i["title"]))
            # itemgetter, should be faster (hm, but does'nt work: ValueError: could not convert string to float: pcount)
            # importing "operator" for implementing itemgetter
            # from operator import itemgetter
            # result = sorted(result, key=itemgetter((float(mycount), 'title'),reverse = True))

    #   play all (TODO)
    #    addDir(translation(30109), '', '', fanart)
    #    back to VIDFLTR (only useful if the back history would be cleared)
    #    if start > 0:
    #        addDir(translation(30108), '', 'main', fanart)
    #   hm, do I really want to replace the playlist on entering a folder?
    #    playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)
    #    playlist.clear()
    start = int(start)
    end = start + filesinlists
    nextstart = end + 1

    for entry in result[start:end]:
        addVideo(entry, mycount)
    maximum = len(result)
    if maximum > end and sort != "random" and channel != "year":
        pagemax = float(maximum) / float(filesinlists)
        pagenext = float(nextstart) / float(filesinlists)
        # next page link
        if sort != "" and sort == "count":
            addDir(
                "%s: %s/%s"
                % (
                    translation(30036),
                    str(int(math.ceil(pagenext))),
                    str(int(math.ceil(pagemax))),
                ),
                "&limit="
                + modus
                + "&sort=count&start="
                + str(nextstart)
                + "&count="
                + mycount,
                "sortTitlesBy",
                fanart,
            )
        elif hit != "" and hit == "true":
            addDir(
                "%s: %s/%s"
                % (
                    translation(30036),
                    str(int(math.ceil(pagenext))),
                    str(int(math.ceil(pagemax))),
                ),
                "&limit=" + modus + "&sort=date&start=" + str(nextstart) + "&hit=true",
                "sortTitlesBy",
                fanart,
            )
        else:
            addDir(
                "%s: %s/%s"
                % (
                    translation(30036),
                    str(int(math.ceil(pagenext))),
                    str(int(math.ceil(pagemax))),
                ),
                "&limit=" + modus + "&sort=date&start=" + str(nextstart),
                "sortTitlesBy",
                fanart,
            )
    endOfDirectory()


def endOfDirectory():
    # xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_NONE, label2Mask="%G")
    # label2Mask="%G" -> https://github.com/xbmc/xbmc/blob/master/xbmc/filesystem/PluginDirectory.cpp
    xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_NONE, label2Mask="%D")
    # xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_PLAYLIST_ORDER, label2Mask="%V")
    # xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_LABEL)
    # xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_DATEADDED)
    # xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_PROGRAM_COUNT)
    # xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_VIDEO_RUNTIME)
    # xbmcplugin.addSortMethod(pluginhandle, xbmcplugin.SORT_METHOD_GENRE)
    # xbmcplugin.endOfDirectory(pluginhandle)
    xbmcplugin.endOfDirectory(
        pluginhandle, succeeded=True, updateListing=False, cacheToDisc=True
    )


def search(channel=""):
    xbmcplugin.setContent(pluginhandle, "musicvideos")
    result = []
    keyboard = xbmc.Keyboard("", translation(30002))
    keyboard.doModal()
    if keyboard.isConfirmed() and keyboard.getText():
        # search_string = keyboard.getText().encode('utf8').lower()
        search_string = keyboard.getText().lower()
        if len(search_string) > 1:
            data = getVideos()
            for entry in data:
                cEntry = entry
                if search_string in cEntry["title"].lower():
                    if channel != "":
                        if cEntry["style"] == channel:
                            # cEntry['title'] = cEntry['style']+':
                            # '+cEntry['title']
                            if videoselection == "0":
                                if "true" in entry["official"].lower():
                                    result.append(cEntry)
                            else:
                                result.append(cEntry)
                    else:
                        # cEntry['title'] = cEntry['style']+':
                        # '+cEntry['title']
                        if videoselection == "0":
                            if "true" in entry["official"].lower():
                                result.append(cEntry)
                        else:
                            result.append(cEntry)
                elif search_string in cEntry["artist"].lower():
                    if channel != "":
                        if cEntry["style"] == channel:
                            # cEntry['title'] = cEntry['style']+':
                            # '+cEntry['title']
                            if videoselection == "0":
                                if "true" in entry["official"].lower():
                                    result.append(cEntry)
                            else:
                                result.append(cEntry)
                    else:
                        # cEntry['title'] = cEntry['style']+':
                        # '+cEntry['title']
                        if videoselection == "0":
                            if "true" in entry["official"].lower():
                                result.append(cEntry)
                        else:
                            result.append(cEntry)

            result.sort(key=lambda entry: entry["title"].lower())
            for entry in result:
                addVideo(entry)
        endOfDirectory()


# not used (TODO?)
def searchDate(channelDate=""):
    xbmcplugin.setContent(pluginhandle, "musicvideos")
    channel = ""
    date = ""
    params = channelDate.split("|")
    channel = params[0]
    if len(params) > 1:
        date = params[1]
    result = []
    if date == "":
        dialog = xbmcgui.Dialog()
        date = dialog.numeric(1, translation(30007))
        date = re.sub("[^0-9|^\/]", "0", date)
        date = date.replace("/", ".")
    if (channel != "") and (len(date) == 10):
        data = getVideos()
        for entry in data:
            cEntry = entry
            if (entry["style"] == channel) and (entry["date"] == date):
                cEntry[1] = cEntry[2] + ": " + cEntry[1]
                if videoselection == "0":
                    if "true" in entry["official"].lower():
                        result.append(cEntry)
                else:
                    result.append(cEntry)
            result.sort(key=lambda entry: entry["title"].lower())
        for entry in result:
            addVideo(entry)
    endOfDirectory()


# not used (TODO?)
def downloadFile(video_url):
    # get best qualiy url
    bq_url = getBestQuality(video_url)
    # get filname from video_url
    filename = video_url.split("/")[-1]
    filetype = filename.split(".")[-1]
    # open browser dialog to choose destination
    dialog = xbmcgui.Dialog()
    download_dir = dialog.browse(3, translation(30102), "files")
    target = urllib.URLopener()
    fullPath = xbmcTranslatePath(download_dir + filename)
    target.retrieve(video_url, fullPath)
    dialog.ok(addonID, translation(30101), str(fullPath))


# only used in style directories
def getFanart(channel):
    fanart = os.path.join(addonDir, "resources/images/fanart_" + channel + ".jpg")
    if not os.path.isfile(fanart.encode("utf-8")):
        fanart = icon
    return fanart


def limitselection(data):
    log(msg="limit by videoselection", level=xbmc.LOGDEBUG)
    result = []
    for entry in data:
        if entry not in result:
            if videoselection == "0":
                if "true" in entry["official"]:
                    result.append(entry)
            elif videoselection == "1":
                if "false" in entry["official"]:
                    result.append(entry)
            elif videoselection == "2":
                result.append(entry)
        return result


def addDir(name, url, mode, iconimage, total=0):
    #    u = sys.argv[0] + "?url=" + str(url) + "&mode=" + str(mode)
    u = sys.argv[0] + "?mode=" + str(mode) + str(url)

    log(msg=str(u), level=xbmc.LOGDEBUG)
    ok = True
    liz = xbmcgui.ListItem(name)
    liz.setArt({"icon": iconimage})
    description = ""
    if mode == "showStyle":
        data = getStyles()
        for style in data:
            if style["style"] == name:
                description = style["description"]

    liz.setInfo(
        type="Video",
        infoLabels={
            "Title": name,
            "PlotOutline": description,
            "Plot": description,
            "Tagline": description,
        },
    )
    if iconimage:
        liz.setProperty("clearart", iconimage)
    else:
        liz.setProperty("clearart", fanart)

    if iconimage:
        liz.setArt({"clearart": iconimage})
    else:
        liz.setArt({"clearart": fanart})
    ok = xbmcplugin.addDirectoryItem(
        handle=int(sys.argv[1]), url=u, listitem=liz, totalItems=total, isFolder=True
    )
    return ok


def addVideo(entry, mycount="playcount"):
    # ok = True

    #   initialize the global video playlist. The item will be added later
    #   playlist = xbmc.PlayList(xbmc.PLAYLIST_VIDEO)

    # Altlast. Sinn dahinter?
    # if len(entry) > 7:
    mediatype = addon.getSetting("mediatype")
    dateadded = datetime.utcfromtimestamp(float(entry["slug"]))
    dateadded = str(dateadded)
    slug = entry["slug"]
    url = "%s?mode=play&url=%s" % (sys.argv[0], slug)
    log(msg=str(url), level=xbmc.LOGDEBUG)

    channel = entry["artist"]
    artist = entry["artist"]
    artistslug = entry["artistslug"]
    artistslugs = entry["artistslugs"]
    artists = entry["artists"]
    director = entry["director"]
    title = entry["title"]
    title = title.replace("&quot;", '"')
    if "false" in entry["official"].lower() and "true" in showunoffintitle:
        title = title + " (vid by " + director + ")"
    tracktitle = entry["tracktitle"]
    tracktitle = tracktitle.replace("&quot;", '"')
    style = entry["style"]
    keycamelot = entry["keycamelot"]
    if mycount == "pcount":
        playcount = entry["pcount"]
    elif mycount == "lcount":
        playcount = entry["lcount"]
    else:
        playcount = entry["acount"]
    # slug = entry['slug']
    # if playLocalFile == "true":
    description = "\n"
    if "false" in entry["official"].lower():
        description = "\n\nUnofficial Video by " + director
    description = (
        "Provider: "
        + entry["provider"]
        + "\nProvider playcount: "
        + entry["pcount"]
        + "\nDeveloper playcount: "
        + entry["acount"]
        + "\ncomments: "
        + entry["comments"]
        + "\nlikes: "
        + entry["likes"]
        + "\ndislikes: "
        + entry["dislikes"]
        + "\ncamelot: "
        + entry["keycamelot"]
        + description
    )
    # else:
    #    description = "Provider: " + entry['provider'] + ""
    # link = entry['slug']
    fanart = entry["thumbnail"]
    duration = entry["duration"]
    #        if (xbmcversion < 17):
    li = xbmcgui.ListItem()
    # info_tag = ListItemInfoTag(li, 'video')
    # info_tag.set_Title(title)
    #        else:
    #            li = xbmcgui.ListItem(title, offscreen=True)

    li.setInfo(
        type="video",
        infoLabels={
            "mediatype": mediatype,
            "Title": title,
            "Originaltitle": tracktitle,
            "Genre": style,
            "Director": director,
            "PlotOutline": description,
            "Plot": description,
            "Tagline": "",
            "Artist": entry["artists"],
            "dateadded": dateadded,
            "playcount": playcount,
            "Duration": duration,
        },
    )
    li.setArt({"thumb": fanart})
    li.setProperty("fanart_image", fanart)
    li.setProperty("IsPlayable", "true")

    #        I could add all entries to the current, global playlist but it doesn't look right
    #        Imo at the end it's better if the user just uses "play from here" or the auto play next setting
    #        playlist.add(url=url, listitem=li)

    li.addContextMenuItems(
        [
            #                                (translation(30121),'Container.Update(plugin://'+addonID+'/?mode=sortTitlesBy&url=%26start%3d'+slug+'%26limit%3drelated%26sort%3dnone)',),
            (
                translation(30121),
                "Container.Update(plugin://"
                + addonID
                + "/?mode=sortTitlesBy&limit=related&start="
                + slug
                + "&sort=none)",
            ),
            (
                translation(30122),
                "Container.Update(plugin://"
                + addonID
                + "/?mode=sortArtists&limit=relartists&start="
                + quote_plus(str(artistslugs))
                + ")",
            ),
            (
                translation(30175),
                "Container.Update(plugin://"
                + addonID
                + "/?mode=sortTitlesBy&limit=camelot&start="
                + quote_plus(str(keycamelot))
                + "&sort=random)",
            ),
            (
                translation(30176),
                "Container.Update(plugin://"
                + addonID
                + "/?mode=sortTitlesBy&limit=camelot&start="
                + quote_plus(str(keycamelot))
                + "&sort=date)",
            ),
        ]
    )
    ok = xbmcplugin.addDirectoryItem(handle=pluginhandle, url=url, listitem=li)

    return ok


def parameters_string_to_dict(parameters):
    paramDict = {}
    if parameters:
        paramPairs = parameters[1:].split("&")
        for paramsPair in paramPairs:
            paramSplits = paramsPair.split("=")
            if (len(paramSplits)) == 2:
                paramDict[paramSplits[0]] = paramSplits[1]
    return paramDict


params = parameters_string_to_dict(sys.argv[2])
mode = urllib_parse.unquote_plus(params.get("mode", ""))
url = urllib_parse.unquote_plus(params.get("url", ""))
limit = urllib_parse.unquote_plus(params.get("limit", ""))
sort = urllib_parse.unquote_plus(params.get("sort", ""))
start = urllib_parse.unquote_plus(params.get("start", ""))
style = urllib_parse.unquote_plus(params.get("style", ""))
hit = urllib_parse.unquote_plus(params.get("hit", ""))
mycount = urllib_parse.unquote_plus(params.get("count", ""))
slug = urllib_parse.unquote_plus(params.get("slug", ""))

xbmc.log(msg=str(sys.argv), level=xbmc.LOGDEBUG)
xbmc.log(msg=str(mode), level=xbmc.LOGDEBUG)
xbmc.log(msg=str(url), level=xbmc.LOGDEBUG)
xbmc.log(msg=str(limit), level=xbmc.LOGDEBUG)
xbmc.log(msg=str(sort), level=xbmc.LOGDEBUG)
xbmc.log(msg=str(start), level=xbmc.LOGDEBUG)
xbmc.log(msg=str(style), level=xbmc.LOGDEBUG)

if mode == "updateData":
    updateData()
elif mode == "alphabet":
    alphabet()
elif mode == "showStyle":
    showStyle(style, sort)
elif mode == "search":
    search(url)
elif mode == "sortTitles":
    sortTitles(url)
elif mode == "sortArtists":
    sortArtists(limit, start)
elif mode == "showArtist":
    showArtist(style, limit, start)
elif mode == "sortTitlesBy":
    sortTitlesBy(limit, sort, start)
elif mode == "searchDate":
    searchDate(url)
elif mode == "mainsorted":
    mainsorted()
elif mode == "mainrandom":
    mainrandom()
elif mode == "numbers":
    numbers()
elif mode == "countrycodes":
    countrycodes()
elif mode == "artists":
    artists()
elif mode == "styles":
    styles()
elif mode == "camelot":
    camelot()
elif mode == "play":
    play(url)
elif mode == "opensettings":
    opensettings()
elif mode == "openytdlsettings":
    openytdlsettings()
else:
    main()

# we have no psutil in CoreELEC Kodi so we kill the debugging process the ugly way
if str(addon.getSetting("debugvscode")) == "true":
    import os
    import signal
    import subprocess

    p = subprocess.Popen(["ps", "a"], stdout=subprocess.PIPE)
    out, err = p.communicate()
    for line in out.splitlines():
        if "script.module.debugpy" in str(line):
            pid = int(line.split(None, 1)[0])
            os.kill(pid, signal.SIGKILL)
