# -*- coding: utf-8 -*-
# Module: default
# Created on: 10.5.2020
# License: AGPL v.3 https://www.gnu.org/licenses/agpl-3.0.html

import io
import os
import sys
import xbmc
import xbmcgui
import xbmcplugin
import xbmcaddon
import xbmcvfs
import requests.cookies
from xml.etree import ElementTree as ET
import hashlib
from md5crypt import md5crypt
import traceback
import json
import unidecode
import re
import zipfile
import uuid

try:
    from urllib import urlencode
    from urlparse import parse_qsl, urlparse
except ImportError:
    from urllib.parse import urlencode
    from urllib.parse import parse_qsl, urlparse

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

BASE = 'https://webshare.cz'
API = BASE + '/api/'
UA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36"
HEADERS = {'User-Agent': UA, 'Referer':BASE}
REALM = ':Webshare:'
CATEGORIES = ['','video','images','audio','archives','docs','adult']
SORTS = ['','recent','rating','largest','smallest']
SEARCH_HISTORY = 'search_history'
NONE_WHAT = '%#NONE#%'
BACKUP_DB = 'D1iIcURxlR'

_url = sys.argv[0]
_handle = int(sys.argv[1])
_addon = xbmcaddon.Addon()
_session = requests.Session()
_session.headers.update(HEADERS)
_profile = translatePath( _addon.getAddonInfo('profile'))
try:
    _profile = _profile.decode("utf-8")
except:
    pass

def get_url(**kwargs):
    return '{0}?{1}'.format(_url, urlencode(kwargs, 'utf-8'))

def api(fnct, data):
    response = _session.post(API + fnct + "/", data=data)
    return response

def is_ok(xml):
    status = xml.find('status').text
    return status == 'OK'

def popinfo(message, heading=_addon.getAddonInfo('name'), icon=xbmcgui.NOTIFICATION_INFO, time=3000, sound=False): #NOTIFICATION_WARNING NOTIFICATION_ERROR
    xbmcgui.Dialog().notification(heading, message, icon, time, sound=sound)

def login():
    username = _addon.getSetting('wsuser')
    password = _addon.getSetting('wspass')
    if username == '' or password == '':
        popinfo(_addon.getLocalizedString(30101), sound=True)
        _addon.openSettings()
        return
    response = api('salt', {'username_or_email': username})
    xml = ET.fromstring(response.content)
    if is_ok(xml):
        salt = xml.find('salt').text
        try:
            encrypted_pass = hashlib.sha1(md5crypt(password.encode('utf-8'), salt.encode('utf-8'))).hexdigest()
            pass_digest = hashlib.md5(username.encode('utf-8') + REALM + encrypted_pass.encode('utf-8')).hexdigest()
        except TypeError:
            encrypted_pass = hashlib.sha1(md5crypt(password.encode('utf-8'), salt.encode('utf-8')).encode('utf-8')).hexdigest()
            pass_digest = hashlib.md5(username.encode('utf-8') + REALM.encode('utf-8') + encrypted_pass.encode('utf-8')).hexdigest()
        response = api('login', {'username_or_email': username, 'password': encrypted_pass, 'digest': pass_digest, 'keep_logged_in': 1})
        xml = ET.fromstring(response.content)
        if is_ok(xml):
            token = xml.find('token').text
            _addon.setSetting('token', token)
            return token
        else:
            popinfo(_addon.getLocalizedString(30102), icon=xbmcgui.NOTIFICATION_ERROR, sound=True)
            _addon.openSettings()
    else:
        popinfo(_addon.getLocalizedString(30102), icon=xbmcgui.NOTIFICATION_ERROR, sound=True)
        _addon.openSettings()

def revalidate():
    token = _addon.getSetting('token')
    if len(token) == 0:
        if login():
            return revalidate()
    else:
        response = api('user_data', { 'wst': token })
        xml = ET.fromstring(response.content)
        status = xml.find('status').text
        if is_ok(xml):
            vip = xml.find('vip').text
            if vip != '1':
                popinfo(_addon.getLocalizedString(30103), icon=xbmcgui.NOTIFICATION_WARNING)
            return token
        else:
            if login():
                return revalidate()

def todict(xml, skip=[]):
    result = {}
    for e in xml:
        if e.tag not in skip:
            value = e.text if len(list(e)) == 0 else todict(e,skip)
            if e.tag in result:
                if isinstance(result[e.tag], list):
                    result[e.tag].append(value)
                else:
                    result[e.tag] = [result[e.tag],value]
            else:
                result[e.tag] = value
    #result = {e.tag:(e.text if len(list(e)) == 0 else todict(e,skip)) for e in xml if e.tag not in skip}
    return result
            
def sizelize(txtsize, units=['B','KB','MB','GB']):
    if txtsize:
        size = float(txtsize)
        if size < 1024:
            size = str(size) + units[0]
        else:
            size = size / 1024
            if size < 1024:
                size = str(int(round(size))) + units[1]
            else:
                size = size / 1024
                if size < 1024:
                    size = str(round(size,2)) + units[2]
                else:
                    size = size / 1024
                    size = str(round(size,2)) + units[3]
        return size
    return str(txtsize)
    
def labelize(file):
    if 'size' in file:
        size = sizelize(file['size'])
    elif 'sizelized' in file:
        size = file['sizelized']
    else:
        size = '?'
    label = file['name'] + ' (' + size + ')'
    return label
    
def tolistitem(file, addcommands=[]):
    label = labelize(file)
    listitem = xbmcgui.ListItem(label=label)
    if 'img' in file:
        listitem.setArt({'thumb': file['img']})
    listitem.setInfo('video', {'title': label})
    listitem.setProperty('IsPlayable', 'true')
    commands = []
    commands.append(( _addon.getLocalizedString(30211), 'RunPlugin(' + get_url(action='info',ident=file['ident']) + ')'))
    commands.append(( _addon.getLocalizedString(30212), 'RunPlugin(' + get_url(action='download',ident=file['ident']) + ')'))
    if addcommands:
        commands = commands + addcommands
    listitem.addContextMenuItems(commands)
    return listitem

def ask(what):
    if what is None:
        what = ''
    kb = xbmc.Keyboard(what, _addon.getLocalizedString(30007))
    kb.doModal() # Onscreen keyboard appears
    if kb.isConfirmed():
        return kb.getText() # User input
    return None
    
def loadsearch():
    history = []
    try:
        if not os.path.exists(_profile):
            os.makedirs(_profile)
    except Exception as e:
        traceback.print_exc()
    
    try:
        with io.open(os.path.join(_profile, SEARCH_HISTORY), 'r', encoding='utf8') as file:
            fdata = file.read()
            file.close()
            try:
                history = json.loads(fdata, "utf-8")
            except TypeError:
                history = json.loads(fdata)
    except Exception as e:
        traceback.print_exc()

    return history
    
def storesearch(what):
    if what:
        size = int(_addon.getSetting('shistory'))

        history = loadsearch()

        if what in history:
            history.remove(what)

        history = [what] + history
        
        if len(history)>size:
            history = history[:size]

        try:
            with io.open(os.path.join(_profile, SEARCH_HISTORY), 'w', encoding='utf8') as file:
                try:
                    data = json.dumps(history).decode('utf8')
                except AttributeError:
                    data = json.dumps(history)
                file.write(data)
                file.close()
        except Exception as e:
            traceback.print_exc()

def removesearch(what):
    if what:
        history = loadsearch()
        if what in history:
            history.remove(what)
            try:
                with io.open(os.path.join(_profile, SEARCH_HISTORY), 'w', encoding='utf8') as file:
                    try:
                        data = json.dumps(history).decode('utf8')
                    except AttributeError:
                        data = json.dumps(history)
                    file.write(data)
                    file.close()
            except Exception as e:
                traceback.print_exc()

def dosearch(token, what, category, sort, limit, offset, action):
    response = api('search',{'what':'' if what == NONE_WHAT else what, 'category':category, 'sort':sort, 'limit': limit, 'offset': offset, 'wst':token, 'maybe_removed':'true'})
    xml = ET.fromstring(response.content)
    if is_ok(xml):
        
        if offset > 0: #prev page
            listitem = xbmcgui.ListItem(label=_addon.getLocalizedString(30206))
            listitem.setArt({'icon': 'DefaultAddonsSearch.png'})
            xbmcplugin.addDirectoryItem(_handle, get_url(action=action, what=what, category=category, sort=sort, limit=limit, offset=offset - limit if offset > limit else 0), listitem, True)
            
        for file in xml.iter('file'):
            item = todict(file)
            commands = []
            commands.append(( _addon.getLocalizedString(30214), 'Container.Update(' + get_url(action='search',toqueue=item['ident'], what=what, offset=offset) + ')'))
            listitem = tolistitem(item,commands)
            xbmcplugin.addDirectoryItem(_handle, get_url(action='play',ident=item['ident'],name=item['name']), listitem, False)
        
        try:
            total = int(xml.find('total').text)
        except:
            total = 0
            
        if offset + limit < total: #next page
            listitem = xbmcgui.ListItem(label=_addon.getLocalizedString(30207))
            listitem.setArt({'icon': 'DefaultAddonsSearch.png'})
            xbmcplugin.addDirectoryItem(_handle, get_url(action=action, what=what, category=category, sort=sort, limit=limit, offset=offset+limit), listitem, True)
    else:
        popinfo(_addon.getLocalizedString(30107), icon=xbmcgui.NOTIFICATION_WARNING)

def search(params):
    xbmcplugin.setPluginCategory(_handle, _addon.getAddonInfo('name') + " \\ " + _addon.getLocalizedString(30201))
    token = revalidate()
    
    updateListing=False
    
    if 'remove' in params:
        removesearch(params['remove'])
        updateListing=True
        
    if 'toqueue' in params:
        toqueue(params['toqueue'],token)
        updateListing=True
    
    what = None
    
    if 'what' in params:
        what = params['what']
    
    if 'ask' in params:
        slast = _addon.getSetting('slast')
        if slast != what:
            what = ask(what)
            if what is not None:
                storesearch(what)
            else:
                updateListing=True

    if what is not None:
        if 'offset' not in params:
            _addon.setSetting('slast',what)
        else:
            _addon.setSetting('slast',NONE_WHAT)
            updateListing=True
        
        category = params['category'] if 'category' in params else CATEGORIES[int(_addon.getSetting('scategory'))]
        sort = params['sort'] if 'sort' in params else SORTS[int(_addon.getSetting('ssort'))]
        limit = int(params['limit']) if 'limit' in params else int(_addon.getSetting('slimit'))
        offset = int(params['offset']) if 'offset' in params else 0
        dosearch(token, what, category, sort, limit, offset, 'search')
    else:
        _addon.setSetting('slast',NONE_WHAT)
        history = loadsearch()
        listitem = xbmcgui.ListItem(label=_addon.getLocalizedString(30205))
        listitem.setArt({'icon': 'DefaultSource.png'})
        xbmcplugin.addDirectoryItem(_handle, get_url(action='search',ask=1), listitem, True)
        
        #newest
        listitem = xbmcgui.ListItem(label=_addon.getLocalizedString(30208))
        listitem.setArt({'icon': 'DefaultAddonsRecentlyUpdated.png'})
        xbmcplugin.addDirectoryItem(_handle, get_url(action='search',what=NONE_WHAT,sort=SORTS[1]), listitem, True)
        
        #biggest
        listitem = xbmcgui.ListItem(label=_addon.getLocalizedString(30209))
        listitem.setArt({'icon': 'DefaultHardDisk.png'})
        xbmcplugin.addDirectoryItem(_handle, get_url(action='search',what=NONE_WHAT,sort=SORTS[3]), listitem, True)
        
        for search in history:
            listitem = xbmcgui.ListItem(label=search)
            listitem.setArt({'icon': 'DefaultAddonsSearch.png'})
            commands = []
            commands.append(( _addon.getLocalizedString(30213), 'Container.Update(' + get_url(action='search',remove=search) + ')'))
            listitem.addContextMenuItems(commands)
            xbmcplugin.addDirectoryItem(_handle, get_url(action='search',what=search,ask=1), listitem, True)
    xbmcplugin.endOfDirectory(_handle, updateListing=updateListing)

def queue(params):
    xbmcplugin.setPluginCategory(_handle, _addon.getAddonInfo('name') + " \\ " + _addon.getLocalizedString(30202))
    token = revalidate()
    updateListing=False
    
    if 'dequeue' in params:
        response = api('dequeue_file',{'ident':params['dequeue'],'wst':token})
        xml = ET.fromstring(response.content)
        if is_ok(xml):
            popinfo(_addon.getLocalizedString(30106))
        else:
            popinfo(_addon.getLocalizedString(30107), icon=xbmcgui.NOTIFICATION_WARNING)
        updateListing=True
    
    response = api('queue',{'wst':token})
    xml = ET.fromstring(response.content)
    if is_ok(xml):
        for file in xml.iter('file'):
            item = todict(file)
            commands = []
            commands.append(( _addon.getLocalizedString(30215), 'Container.Update(' + get_url(action='queue',dequeue=item['ident']) + ')'))
            listitem = tolistitem(item,commands)
            xbmcplugin.addDirectoryItem(_handle, get_url(action='play',ident=item['ident'],name=item['name']), listitem, False)
    else:
        popinfo(_addon.getLocalizedString(30107), icon=xbmcgui.NOTIFICATION_WARNING)
    xbmcplugin.endOfDirectory(_handle,updateListing=updateListing)

def toqueue(ident,token):
    response = api('queue_file',{'ident':ident,'wst':token})
    xml = ET.fromstring(response.content)
    if is_ok(xml):
        popinfo(_addon.getLocalizedString(30105))
    else:
        popinfo(_addon.getLocalizedString(30107), icon=xbmcgui.NOTIFICATION_WARNING)

def history(params):
    xbmcplugin.setPluginCategory(_handle, _addon.getAddonInfo('name') + " \\ " + _addon.getLocalizedString(30203))
    token = revalidate()
    updateListing=False
    
    if 'remove' in params:
        remove = params['remove']
        updateListing=True
        response = api('history',{'wst':token})
        xml = ET.fromstring(response.content)
        ids = []
        if is_ok(xml):
            for file in xml.iter('file'):
                if remove == file.find('ident').text:
                    ids.append(file.find('download_id').text)
        else:
            popinfo(_addon.getLocalizedString(30107), icon=xbmcgui.NOTIFICATION_WARNING)
        if ids:
            rr = api('clear_history',{'ids[]':ids,'wst':token})
            xml = ET.fromstring(rr.content)
            if is_ok(xml):
                popinfo(_addon.getLocalizedString(30104))
            else:
                popinfo(_addon.getLocalizedString(30107), icon=xbmcgui.NOTIFICATION_WARNING)
    
    if 'toqueue' in params:
        toqueue(params['toqueue'],token)
        updateListing=True
    
    response = api('history',{'wst':token})
    xml = ET.fromstring(response.content)
    files = []
    if is_ok(xml):
        for file in xml.iter('file'):
            item = todict(file, ['ended_at', 'download_id', 'started_at'])
            if item not in files:
                files.append(item)
        for file in files:
            commands = []
            commands.append(( _addon.getLocalizedString(30213), 'Container.Update(' + get_url(action='history',remove=file['ident']) + ')'))
            commands.append(( _addon.getLocalizedString(30214), 'Container.Update(' + get_url(action='history',toqueue=file['ident']) + ')'))
            listitem = tolistitem(file, commands)
            xbmcplugin.addDirectoryItem(_handle, get_url(action='play',ident=file['ident'],name=file['name']), listitem, False)
    else:
        popinfo(_addon.getLocalizedString(30107), icon=xbmcgui.NOTIFICATION_WARNING)
    xbmcplugin.endOfDirectory(_handle,updateListing=updateListing)
    
def settings(params):
    _addon.openSettings()
    xbmcplugin.setResolvedUrl(_handle, False, xbmcgui.ListItem())

def infonize(data,key,process=str,showkey=True,prefix='',suffix='\n'):
    if key in data:
        return prefix + (key.capitalize() + ': ' if showkey else '') + process(data[key]) + suffix
    return ''

def fpsize(fps):
    x = round(float(fps),3)
    if int(x) == x:
       return str(int(x))
    return str(x)
    
def getinfo(ident,wst):
    response = api('file_info',{'ident':ident,'wst': wst})
    xml = ET.fromstring(response.content)
    ok = is_ok(xml)
    if not ok:
        response = api('file_info',{'ident':ident,'wst': wst, 'maybe_removed':'true'})
        xml = ET.fromstring(response.content)
        ok = is_ok(xml)
    if ok:
        return xml
    else:
        popinfo(_addon.getLocalizedString(30107), icon=xbmcgui.NOTIFICATION_WARNING)
        return None

def info(params):
    token = revalidate()
    xml = getinfo(params['ident'],token)
    
    if xml is not None:
        info = todict(xml)
        text = ''
        text += infonize(info, 'name')
        text += infonize(info, 'size', sizelize)
        text += infonize(info, 'type')
        text += infonize(info, 'width')
        text += infonize(info, 'height')
        text += infonize(info, 'format')
        text += infonize(info, 'fps', fpsize)
        text += infonize(info, 'bitrate', lambda x:sizelize(x,['bps','Kbps','Mbps','Gbps']))
        if 'video' in info and 'stream' in info['video']:
            streams = info['video']['stream']
            if isinstance(streams, dict):
                streams = [streams]
            for stream in streams:
                text += 'Video stream: '
                text += infonize(stream, 'width', showkey=False, suffix='')
                text += infonize(stream, 'height', showkey=False, prefix='x', suffix='')
                text += infonize(stream,'format', showkey=False, prefix=', ', suffix='')
                text += infonize(stream,'fps', fpsize, showkey=False, prefix=', ', suffix='')
                text += '\n'
        if 'audio' in info and 'stream' in info['audio']:
            streams = info['audio']['stream']
            if isinstance(streams, dict):
                streams = [streams]
            for stream in streams:
                text += 'Audio stream: '
                text += infonize(stream, 'format', showkey=False, suffix='')
                text += infonize(stream,'channels', prefix=', ', showkey=False, suffix='')
                text += infonize(stream,'bitrate', lambda x:sizelize(x,['bps','Kbps','Mbps','Gbps']), prefix=', ', showkey=False, suffix='')
                text += '\n'
        text += infonize(info, 'removed', lambda x:'Yes' if x=='1' else 'No')
        xbmcgui.Dialog().textviewer(_addon.getAddonInfo('name'), text)

def getlink(ident,wst,dtype='video_stream'):
    #uuid experiment
    duuid = _addon.getSetting('duuid')
    if not duuid:
        duuid = str(uuid.uuid4())
        _addon.setSetting('duuid',duuid)
    data = {'ident':ident,'wst': wst,'download_type':dtype,'device_uuid':duuid}
    #TODO password protect
    #response = api('file_protected',data) #protected
    #xml = ET.fromstring(response.content)
    #if is_ok(xml) and xml.find('protected').text != 0:
    #    pass #ask for password
    response = api('file_link',data)
    xml = ET.fromstring(response.content)
    if is_ok(xml):
        return xml.find('link').text
    else:
        popinfo(_addon.getLocalizedString(30107), icon=xbmcgui.NOTIFICATION_WARNING)
        return None

def play(params):
    token = revalidate()
    link = getlink(params['ident'],token)
    if link is not None:
        #headers experiment
        headers = _session.headers
        if headers:
            headers.update({'Cookie':'wst='+token})
            link = link + '|' + urlencode(headers)
        listitem = xbmcgui.ListItem(label=params['name'],path=link)
        listitem.setProperty('mimetype', 'application/octet-stream')
        xbmcplugin.setResolvedUrl(_handle, True, listitem)
    else:
        popinfo(_addon.getLocalizedString(30107), icon=xbmcgui.NOTIFICATION_WARNING)
        xbmcplugin.setResolvedUrl(_handle, False, xbmcgui.ListItem())

def verify_ident(ident, token):
    """Ověří, zda je ident platný a soubor existuje"""
    response = api('file_info', {'ident': ident, 'wst': token})
    xml = ET.fromstring(response.content)
    if is_ok(xml):
        return True
    else:
        popinfo(f"Neplatný ident: {ident}", icon=xbmcgui.NOTIFICATION_ERROR)
        return False

def join(path, file):
    if path.endswith('/') or path.endswith('\\'):
        return path + file
    else:
        return path + '/' + file

def download(params):
    token = revalidate()
    where = _addon.getSetting('dfolder')
    if not where or not xbmcvfs.exists(where):
        popinfo('set folder!', sound=True)#_addon.getLocalizedString(30101)
        _addon.openSettings()
        return
        
    local = os.path.exists(where)
        
    normalize = 'true' == _addon.getSetting('dnormalize')
    notify = 'true' == _addon.getSetting('dnotify')
    every = _addon.getSetting('dnevery')
    try:
        every = int(re.sub(r'[^\d]+', '', every))
    except:
        every = 10
        
    try:
        link = getlink(params['ident'],token,'file_download')
        info = getinfo(params['ident'],token)
        name = info.find('name').text
        if normalize:
            name = unidecode.unidecode(name)
        bf = io.open(os.path.join(where,name), 'wb') if local else xbmcvfs.File(join(where,name), 'w')
        response = _session.get(link, stream=True)
        total = response.headers.get('content-length')
        if total is None:
            popinfo(_addon.getLocalizedString(30301) + name, icon=xbmcgui.NOTIFICATION_WARNING, sound=True)
            bf.write(response.content)
        elif not notify:
            popinfo(_addon.getLocalizedString(30302) + name)
            bf.write(response.content)
        else:
            popinfo(_addon.getLocalizedString(30302) + name)
            dl = 0
            total = int(total)
            pct = total / 100
            lastpop=0
            for data in response.iter_content(chunk_size=4096):
                dl += len(data)
                bf.write(data)
                done = int(dl / pct)
                if done % every == 0 and lastpop != done:
                    popinfo(str(done) + '% - ' + name)
                    lastpop = done
        bf.close()
        popinfo(_addon.getLocalizedString(30303) + name, sound=True)
    except Exception as e:
        #TODO - remove unfinished file?
        traceback.print_exc()
        popinfo(_addon.getLocalizedString(30304) + name, icon=xbmcgui.NOTIFICATION_ERROR, sound=True)

def loaddb(dbdir,file):
    try:
        data = {}
        with io.open(os.path.join(dbdir, file), 'r', encoding='utf8') as file:
            fdata = file.read()
            file.close()
            try:
                data = json.loads(fdata, "utf-8")['data']
            except TypeError:
                data = json.loads(fdata)['data']
        return data
    except Exception as e:
        traceback.print_exc()
        return {}

def db(params):
    token = revalidate()
    updateListing=False

    dbdir = os.path.join(_profile,'db')
    if not os.path.exists(dbdir):
        link = getlink(BACKUP_DB,token)
        dbfile = os.path.join(_profile,'db.zip')
        with io.open(dbfile, 'wb') as bf:
            response = _session.get(link, stream=True)
            bf.write(response.content)
            bf.flush()
            bf.close()
        with zipfile.ZipFile(dbfile, 'r') as zf:
            zf.extractall(_profile)
        os.unlink(dbfile)
    
    if 'toqueue' in params:
        toqueue(params['toqueue'],token)
        updateListing=True
    
    if 'file' in params and 'key' in params:
        data = loaddb(dbdir,params['file'])
        item = next((x for x in data if x['id'] == params['key']), None)
        if item is not None:
            for stream in item['streams']:
                commands = []
                commands.append(( _addon.getLocalizedString(30214), 'Container.Update(' + get_url(action='db',file=params['file'],key=params['key'],toqueue=stream['ident']) + ')'))
                listitem = tolistitem({'ident':stream['ident'],'name':stream['quality'] + ' - ' + stream['lang'] + stream['ainfo'],'sizelized':stream['size']},commands)
                xbmcplugin.addDirectoryItem(_handle, get_url(action='play',ident=stream['ident'],name=item['title']), listitem, False)
    elif 'file' in params:
        data = loaddb(dbdir,params['file'])
        for item in data:
            listitem = xbmcgui.ListItem(label=item['title'])
            if 'plot' in item:
                listitem.setInfo('video', {'title': item['title'],'plot': item['plot']})
            xbmcplugin.addDirectoryItem(_handle, get_url(action='db',file=params['file'],key=item['id']), listitem, True)
    else:
        if os.path.exists(dbdir):
            dbfiles = [f for f in os.listdir(dbdir) if os.path.isfile(os.path.join(dbdir, f))]
            for dbfile in dbfiles:
                listitem = xbmcgui.ListItem(label=os.path.splitext(dbfile)[0])
                xbmcplugin.addDirectoryItem(_handle, get_url(action='db',file=dbfile), listitem, True)
    xbmcplugin.addSortMethod(_handle,xbmcplugin.SORT_METHOD_LABEL)
    xbmcplugin.endOfDirectory(_handle, updateListing=updateListing)

def mojedb(params):
    xbmc.log("Debug: mojedb funkce spuštěna", xbmc.LOGDEBUG)
    
    # Funkce pro stahování JSON souborů z GITu
    def update_json_db():
        try:
            import urllib.request
            import re
            
            def get_json_files_from_folder(folder):
                base_url = "https://git.gald.site/gald/galdistream/src/branch/main/resources/"
                url = base_url + folder
                
                req = urllib.request.Request(url)
                req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36')
                
                with urllib.request.urlopen(req, timeout=10) as response:
                    html_content = response.read().decode('utf-8')
                
                json_pattern = r'href="(/gald/galdistream/src/branch/main/resources/[^"]*\.json)"'
                matches = re.findall(json_pattern, html_content)
                
                files = []
                for match in matches:
                    file_name = match.split("/")[-1]
                    files.append(file_name)
                return files
            
            base_url_raw = "https://git.gald.site/gald/galdistream/raw/branch/main/resources/"
            folders = ["movies", "series"]
            all_files = []
            
            for folder in folders:
                try:
                    files = get_json_files_from_folder(folder)
                    all_files += [f"{folder}/{file}" for file in files]
                except Exception as e:
                    xbmc.log(f"Chyba při získávání souborů ze složky {folder}: {e}", xbmc.LOGERROR)
            
            for file in all_files:
                url = base_url_raw + file
                local_path = os.path.join(_addon.getAddonInfo('path'), 'resources', file)
                try:
                    req = urllib.request.Request(url)
                    req.add_header('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36')
                    
                    with urllib.request.urlopen(req, timeout=10) as response:
                        content = response.read()
                    
                    os.makedirs(os.path.dirname(local_path), exist_ok=True)
                    with open(local_path, "wb") as f:
                        f.write(content)
                    xbmc.log(f"Staženo: {file}", xbmc.LOGDEBUG)
                except Exception as e:
                    xbmc.log(f"Chyba při stahování {file}: {e}", xbmc.LOGERROR)
        except Exception as e:
            xbmc.log(f"Chyba při stahování JSON souborů: {e}", xbmc.LOGERROR)
    
    # Stáhni aktuální JSON soubory z GITu
    update_json_db()
    
    xbmcplugin.setPluginCategory(_handle, _addon.getAddonInfo('name') + " \\ " + _addon.getLocalizedString(30220))
    token = revalidate()
    updateListing=False
    
    try:
        # Načti seznam dostupných seriálů a filmů
        series_dir = xbmcvfs.translatePath(os.path.join(_addon.getAddonInfo('path'), 'resources', 'series'))
        movies_dir = xbmcvfs.translatePath(os.path.join(_addon.getAddonInfo('path'), 'resources', 'movies'))
        
        xbmc.log(f"Debug: Načítám seriály z: {series_dir}", xbmc.LOGDEBUG)
        
        # Načti seznam souborů seriálů
        series_files = []
        xbmc.log(f"Debug: Kontroluji existenci složky: {series_dir}", xbmc.LOGDEBUG)
        
        # Zkusíme načíst soubory pomocí os.path
        try:
            # Použijeme os.path místo xbmcvfs pro načítání souborů
            if os.path.exists(series_dir):
                files = os.listdir(series_dir)
                xbmc.log(f"Debug: Úspěšně načteno {len(files)} souborů z {series_dir}", xbmc.LOGDEBUG)
                for file in files:
                    xbmc.log(f"Debug: Kontroluji soubor: {file}", xbmc.LOGDEBUG)
                    if file.endswith('.json'):
                        series_files.append(file)
                        xbmc.log(f"Debug: Přidán JSON soubor: {file}", xbmc.LOGDEBUG)
            else:
                xbmc.log(f"Debug: Složka neexistuje (os.path): {series_dir}", xbmc.LOGDEBUG)
        except Exception as e:
            xbmc.log(f"Debug: Chyba při načítání složky {series_dir}: {str(e)}", xbmc.LOGDEBUG)
        
        xbmc.log(f"Debug: Nalezeno {len(series_files)} JSON souborů seriálů", xbmc.LOGDEBUG)
        for file in series_files:
            xbmc.log(f"Debug: Seriál soubor: {file}", xbmc.LOGDEBUG)
        
    except Exception as e:
        xbmc.log(f"Chyba při načítání databáze: {str(e)}", xbmc.LOGERROR)
        popinfo(f"Chyba při načítání databáze: {str(e)}", icon=xbmcgui.NOTIFICATION_ERROR, sound=True)
        xbmcplugin.endOfDirectory(_handle, updateListing=updateListing)
        return

    # Hlavní rozcestník: seriály/filmy
    if not params.get('type'):
        # Seriály
        listitem = xbmcgui.ListItem(label=_addon.getLocalizedString(30221) if _addon.getLocalizedString(30221) else 'Seriály')
        listitem.setArt({'icon': 'DefaultTVShows.png'})
        xbmcplugin.addDirectoryItem(_handle, get_url(action='mojedb', type='series'), listitem, True)
        # Filmy
        listitem = xbmcgui.ListItem(label=_addon.getLocalizedString(30222) if _addon.getLocalizedString(30222) else 'Filmy')
        listitem.setArt({'icon': 'DefaultMovies.png'})
        xbmcplugin.addDirectoryItem(_handle, get_url(action='mojedb', type='movies'), listitem, True)

    # Výpis seriálů
    elif params.get('type') == 'series' and not params.get('series_idx'):
        xbmc.log(f"Debug: Zobrazuji seriály, nalezeno {len(series_files)} souborů", xbmc.LOGDEBUG)
        for idx, file in enumerate(series_files):
            try:
                xbmc.log(f"Debug: Načítám seriál: {file}", xbmc.LOGDEBUG)
                # Načti data seriálu z JSON souboru
                series_path = xbmcvfs.translatePath(os.path.join(_addon.getAddonInfo('path'), 'resources', 'series', file))
                xbmc.log(f"Debug: Cesta k souboru: {series_path}", xbmc.LOGDEBUG)
                with xbmcvfs.File(series_path, 'r') as f:
                    content = f.read()
                    xbmc.log(f"Debug: Přečteno {len(content)} znaků", xbmc.LOGDEBUG)
                    serie_data = json.loads(content)
                
                # Vytvoř název seriálu z názvu souboru
                series_name = file.replace('.json', '').replace('-', ' ').title()
                if 'title' in serie_data:
                    series_name = serie_data['title']
                
                xbmc.log(f"Debug: Vytvářím položku pro seriál: {series_name}", xbmc.LOGDEBUG)
                # Použijeme číselný index místo názvu souboru
                url = get_url(action='mojedb', type='series', series_idx=idx)
                xbmc.log(f"Debug: URL pro seriál: {url}", xbmc.LOGDEBUG)
                listitem = xbmcgui.ListItem(label=series_name)
                listitem.setArt({'icon': serie_data.get('icon', 'DefaultTVShows.png')})
                xbmcplugin.addDirectoryItem(_handle, url, listitem, True)
                xbmc.log(f"Debug: Přidána položka pro seriál: {series_name}", xbmc.LOGDEBUG)
            except Exception as e:
                xbmc.log(f"Chyba při načítání seriálu {file}: {str(e)}", xbmc.LOGERROR)
                popinfo(f"Chyba při načítání seriálu {file}: {str(e)}", icon=xbmcgui.NOTIFICATION_ERROR)

    # Výpis sezón daného seriálu
    elif params.get('type') == 'series' and params.get('series_idx') is not None and not params.get('season_idx'):
        try:
            series_idx = int(params['series_idx'])
            series_file = series_files[series_idx]
            
            # Načti data seriálu
            series_path = xbmcvfs.translatePath(os.path.join(_addon.getAddonInfo('path'), 'resources', 'series', series_file))
            with xbmcvfs.File(series_path, 'r') as f:
                content = f.read()
                serie_data = json.loads(content)
            
            for idx, season in enumerate(serie_data.get('seasons', [])):
                label = f"{serie_data['title']} - Sezóna {season['season']}"
                listitem = xbmcgui.ListItem(label=label)
                listitem.setArt({'icon': serie_data.get('icon', 'DefaultTVShows.png')})
                xbmcplugin.addDirectoryItem(_handle, get_url(action='mojedb', type='series', series_idx=series_idx, season_idx=idx), listitem, True)
        except Exception as e:
            popinfo(f"Chyba při zpracování sezón: {str(e)}", icon=xbmcgui.NOTIFICATION_ERROR, sound=True)

    # Výpis epizod dané sezóny
    elif params.get('type') == 'series' and params.get('series_idx') is not None and params.get('season_idx') is not None and not params.get('episode_idx'):
        xbmc.log("Debug: Zpracovávám seznam epizod", xbmc.LOGDEBUG)
        try:
            series_idx = int(params['series_idx'])
            series_file = series_files[series_idx]
            season_idx = int(params['season_idx'])
            
            # Načti data seriálu
            series_path = xbmcvfs.translatePath(os.path.join(_addon.getAddonInfo('path'), 'resources', 'series', series_file))
            with xbmcvfs.File(series_path, 'r') as f:
                content = f.read()
                serie_data = json.loads(content)
            
            season = serie_data['seasons'][season_idx]
            xbmc.log(f"Debug: Sezóna {season['season']}, {len(season.get('episodes', []))} epizod", xbmc.LOGDEBUG)
            popinfo(f"Debug: Sezóna {season['season']}, {len(season.get('episodes', []))} epizod")
            for idx, episode in enumerate(season.get('episodes', [])):
                listitem = xbmcgui.ListItem(label=episode['title'])
                listitem.setArt({'icon': serie_data.get('icon', 'DefaultTVShows.png')})
                xbmcplugin.addDirectoryItem(_handle, get_url(action='mojedb', type='series', series_idx=series_idx, season_idx=params['season_idx'], episode_idx=idx), listitem, True)
            popinfo(f"Debug: Přidáno {len(season.get('episodes', []))} epizod")
        except Exception as e:
            xbmc.log(f"Chyba při zpracování epizod: {str(e)}", xbmc.LOGERROR)
            popinfo(f"Chyba při zpracování epizod: {str(e)}", icon=xbmcgui.NOTIFICATION_ERROR, sound=True)

    # Vyhledání konkrétní epizody
    elif params.get('type') == 'series' and params.get('series_idx') is not None and params.get('season_idx') is not None and params.get('episode_idx') is not None:
        xbmc.log("Debug: Zpracovávám epizodu", xbmc.LOGDEBUG)
        try:
            series_idx = int(params['series_idx'])
            series_file = series_files[series_idx]
            season_idx = int(params['season_idx'])
            episode_idx = int(params['episode_idx'])
            
            # Načti data seriálu
            series_path = xbmcvfs.translatePath(os.path.join(_addon.getAddonInfo('path'), 'resources', 'series', series_file))
            with xbmcvfs.File(series_path, 'r') as f:
                content = f.read()
                serie_data = json.loads(content)
            
            season = serie_data['seasons'][season_idx]
            episode = season['episodes'][episode_idx]
            xbmc.log(f"Debug: Epizoda: {episode['title']}", xbmc.LOGDEBUG)
        except Exception as e:
            xbmc.log(f"Chyba při zpracování epizody: {str(e)}", xbmc.LOGERROR)
            popinfo(f"Chyba při zpracování epizody: {str(e)}", icon=xbmcgui.NOTIFICATION_ERROR, sound=True)
            xbmcplugin.endOfDirectory(_handle, updateListing=updateListing)
            return
        if 'ident' in episode:
            xbmc.log(f"Debug: Nalezen ident: {episode['ident']}", xbmc.LOGDEBUG)
            if verify_ident(episode['ident'], token):
                xbmc.log("Debug: Ident je platný, vytvářím přehrávatelnou položku", xbmc.LOGDEBUG)
                # Vytvoř přehrávatelnou položku
                listitem = xbmcgui.ListItem(label=episode['title'])
                listitem.setArt({'icon': serie_data.get('icon', 'DefaultTVShows.png')})
                listitem.setInfo('video', {'title': episode['title']})
                listitem.setProperty('IsPlayable', 'true')
                xbmcplugin.addDirectoryItem(_handle, get_url(action='play', ident=episode['ident'], name=episode['title']), listitem, False)
                # Ukonči adresář bez volání endOfDirectory na konci
                xbmcplugin.addSortMethod(_handle, xbmcplugin.SORT_METHOD_LABEL)
                xbmcplugin.endOfDirectory(_handle, updateListing=updateListing)
                return
            else:
                xbmc.log("Debug: Ident není platný, zobrazuji výsledky vyhledávání", xbmc.LOGDEBUG)
                # Pokud ident není platný, použij vyhledávání
                what = episode['search']
                category = 'video'
                sort = 'recent'
                limit = 20  # Zobraz více výsledků
                offset = 0
                dosearch(token, what, category, sort, limit, offset, 'search')
        else:
            what = episode['search']
            category = 'video'
            sort = 'recent'
            limit = 20  # Zobraz více výsledků
            offset = 0
            dosearch(token, what, category, sort, limit, offset, 'search')

    # Výpis filmů
    elif params.get('type') == 'movies':
        for idx, movie in enumerate(dbdata.get('movies', [])):
            listitem = xbmcgui.ListItem(label=movie['title'])
            listitem.setArt({'icon': movie.get('icon', 'DefaultMovies.png')})
            xbmcplugin.addDirectoryItem(_handle, get_url(action='mojedb', type='movies', movie_idx=idx), listitem, True)

    # Vyhledání konkrétního filmu
    elif params.get('type') == 'movies' and params.get('movie_idx') is not None:
        movie = dbdata['movies'][int(params['movie_idx'])]
        if 'ident' in movie:
            if verify_ident(movie['ident'], token):
                # Vytvoř přehrávatelnou položku
                listitem = xbmcgui.ListItem(label=movie['title'])
                listitem.setArt({'icon': movie.get('icon', 'DefaultMovies.png')})
                listitem.setInfo('video', {'title': movie['title']})
                listitem.setProperty('IsPlayable', 'true')
                xbmcplugin.addDirectoryItem(_handle, get_url(action='play', ident=movie['ident'], name=movie['title']), listitem, False)
                # Ukonči adresář bez volání endOfDirectory na konci
                xbmcplugin.addSortMethod(_handle, xbmcplugin.SORT_METHOD_LABEL)
                xbmcplugin.endOfDirectory(_handle, updateListing=updateListing)
                return
            else:
                # Pokud ident není platný, použij vyhledávání
                what = movie['search']
                category = 'video'
                sort = 'recent'
                limit = 20  # Zobraz více výsledků
                offset = 0
                dosearch(token, what, category, sort, limit, offset, 'search')
        else:
            what = movie['search']
            category = 'video'
            sort = 'recent'
            limit = 20  # Zobraz více výsledků
            offset = 0
            dosearch(token, what, category, sort, limit, offset, 'search')

    xbmcplugin.addSortMethod(_handle, xbmcplugin.SORT_METHOD_LABEL)
    xbmcplugin.endOfDirectory(_handle, updateListing=updateListing)

def menu():
    revalidate()
    xbmcplugin.setPluginCategory(_handle, _addon.getAddonInfo('name'))
    listitem = xbmcgui.ListItem(label=_addon.getLocalizedString(30201))
    listitem.setArt({'icon': 'DefaultAddonsSearch.png'})
    xbmcplugin.addDirectoryItem(_handle, get_url(action='search'), listitem, True)
    
    listitem = xbmcgui.ListItem(label=_addon.getLocalizedString(30202))
    listitem.setArt({'icon': 'DefaultPlaylist.png'})
    xbmcplugin.addDirectoryItem(_handle, get_url(action='queue'), listitem, True)
    
    listitem = xbmcgui.ListItem(label=_addon.getLocalizedString(30203))
    listitem.setArt({'icon': 'DefaultAddonsUpdates.png'})
    xbmcplugin.addDirectoryItem(_handle, get_url(action='history'), listitem, True)
    
    listitem = xbmcgui.ListItem(label=_addon.getLocalizedString(30220))
    listitem.setArt({'icon': 'DefaultFavourites.png'})
    xbmcplugin.addDirectoryItem(_handle, get_url(action='mojedb'), listitem, True)
    
    if 'true' == _addon.getSetting('experimental'):
        listitem = xbmcgui.ListItem(label='Backup DB')
        listitem.setArt({'icon': 'DefaultAddonsZip.png'})
        xbmcplugin.addDirectoryItem(_handle, get_url(action='db'), listitem, True)

    listitem = xbmcgui.ListItem(label=_addon.getLocalizedString(30204))
    listitem.setArt({'icon': 'DefaultAddonService.png'})
    xbmcplugin.addDirectoryItem(_handle, get_url(action='settings'), listitem, False)

    xbmcplugin.endOfDirectory(_handle)

def router(paramstring):
    xbmc.log(f"Debug: Router - paramstring: {paramstring}", xbmc.LOGDEBUG)
    params = dict(parse_qsl(paramstring))
    xbmc.log(f"Debug: Router - params: {params}", xbmc.LOGDEBUG)
    if params:
        action = params.get('action', 'none')
        xbmc.log(f"Debug: Router - action: {action}", xbmc.LOGDEBUG)
        
        if action == 'search':
            search(params)
        elif action == 'queue':
            queue(params)
        elif action == 'history':
            history(params)
        elif action == 'settings':
            settings(params)
        elif action == 'info':
            info(params)
        elif action == 'play':
            play(params)
        elif action == 'download':
            download(params)
        elif action == 'db':
            db(params)
        elif action == 'mojedb':
            xbmc.log(f"Debug: Router - volám mojedb s parametry: {params}", xbmc.LOGDEBUG)
            mojedb(params)
        else:
            xbmc.log(f"Debug: Router - neznámá akce: {action}, volám menu", xbmc.LOGDEBUG)
            menu()
    else:
        xbmc.log(f"Debug: Router - žádné parametry, volám menu", xbmc.LOGDEBUG)
        menu()
