# -*- 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) popinfo("Debug: mojedb funkce spuštěna") # 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) popinfo(f"Debug: Načítám seriály z: {series_dir}") # 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) popinfo(f"Debug: Úspěšně načteno {len(files)} souborů") 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) popinfo(f"Debug: Složka neexistuje (os.path): {series_dir}") except Exception as e: xbmc.log(f"Debug: Chyba při načítání složky {series_dir}: {str(e)}", xbmc.LOGDEBUG) popinfo(f"Debug: Chyba při načítání složky: {str(e)}") xbmc.log(f"Debug: Nalezeno {len(series_files)} JSON souborů seriálů", xbmc.LOGDEBUG) popinfo(f"Debug: Nalezeno {len(series_files)} JSON souborů seriálů") 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) popinfo(f"Debug: Zobrazuji seriály, nalezeno {len(series_files)} souborů") 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] popinfo(f"Debug: Zpracovávám seriál: {series_file}") # 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) popinfo(f"Debug: Přidáno {len(serie_data.get('seasons', []))} sezón") 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) popinfo("Debug: Zpracovávám seznam epizod") 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) popinfo("Debug: Zpracovávám epizodu") 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) popinfo(f"Debug: Epizoda: {episode['title']}") 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) popinfo(f"Debug: Nalezen ident: {episode['ident']}") if verify_ident(episode['ident'], token): xbmc.log("Debug: Ident je platný, vytvářím přehrávatelnou položku", xbmc.LOGDEBUG) popinfo("Debug: Ident je platný, vytvářím přehrávatelnou položku") # 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) popinfo("Debug: Ukončuji mojedb funkci") xbmcplugin.endOfDirectory(_handle, updateListing=updateListing) return else: xbmc.log("Debug: Ident není platný, zobrazuji výsledky vyhledávání", xbmc.LOGDEBUG) popinfo("Debug: Ident není platný, zobrazuji výsledky vyhledávání") # 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: popinfo("Debug: Není ident, zobrazuji výsledky 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') # 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) popinfo("Debug: Ukončuji mojedb funkci") xbmcplugin.endOfDirectory(_handle, updateListing=updateListing) return else: popinfo("Debug: Ident filmu není platný, zobrazuji výsledky vyhledávání") # 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: popinfo("Debug: Film nemá ident, zobrazuji výsledky 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') xbmcplugin.addSortMethod(_handle, xbmcplugin.SORT_METHOD_LABEL) popinfo("Debug: Ukončuji mojedb funkci") 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) popinfo(f"Debug: Router - action: {action}") 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) popinfo(f"Debug: Router - volám mojedb") 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()