# -*- 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 series_files = [] movies_collections = [] 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ů 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) xbmc.log(f"Debug: Načítám filmy z: {movies_dir}", xbmc.LOGDEBUG) movies_files = [] try: if os.path.exists(movies_dir): files = os.listdir(movies_dir) xbmc.log(f"Debug: Úspěšně načteno {len(files)} souborů z {movies_dir}", xbmc.LOGDEBUG) for file in files: xbmc.log(f"Debug: Kontroluji filmový soubor: {file}", xbmc.LOGDEBUG) if file.endswith('.json'): movies_files.append(file) xbmc.log(f"Debug: Přidán JSON soubor filmů: {file}", xbmc.LOGDEBUG) else: xbmc.log(f"Debug: Složka neexistuje (os.path): {movies_dir}", xbmc.LOGDEBUG) except Exception as e: xbmc.log(f"Debug: Chyba při načítání složky {movies_dir}: {str(e)}", xbmc.LOGDEBUG) xbmc.log(f"Debug: Nalezeno {len(movies_files)} JSON souborů filmů", xbmc.LOGDEBUG) for file in movies_files: try: movie_path = xbmcvfs.translatePath(os.path.join(_addon.getAddonInfo('path'), 'resources', 'movies', file)) xbmc.log(f"Debug: Načítám filmový katalog: {file}", xbmc.LOGDEBUG) with xbmcvfs.File(movie_path, 'r') as f: content = f.read() movie_data = json.loads(content) movies_collections.append({'file': file, 'data': movie_data}) xbmc.log(f"Debug: Přidán katalog filmů: {movie_data.get('title', file)}", xbmc.LOGDEBUG) except Exception as e: xbmc.log(f"Chyba při načítání filmového souboru {file}: {str(e)}", xbmc.LOGERROR) 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') # Přehled filmových kolekcí elif params.get('type') == 'movies' and params.get('collection_idx') is None and params.get('movie_idx') is None: xbmc.log(f"Debug: Zobrazuji filmové kolekce, nalezeno {len(movies_collections)} souborů", xbmc.LOGDEBUG) for idx, collection in enumerate(movies_collections): data = collection['data'] title = data.get('title') or collection['file'].replace('.json', '').replace('-', ' ').title() xbmc.log(f"Debug: Přidávám kolekci filmů: {title}", xbmc.LOGDEBUG) listitem = xbmcgui.ListItem(label=title) listitem.setArt({'icon': data.get('icon', 'DefaultMovies.png')}) xbmcplugin.addDirectoryItem(_handle, get_url(action='mojedb', type='movies', collection_idx=idx), listitem, True) # Výpis filmů v kolekci elif params.get('type') == 'movies' and params.get('collection_idx') is not None and params.get('movie_idx') is None: try: collection_idx = int(params['collection_idx']) collection = movies_collections[collection_idx] movies = collection['data'].get('movies', []) xbmc.log(f"Debug: Zobrazuji filmy v kolekci {collection['file']} (počet {len(movies)})", xbmc.LOGDEBUG) for idx, movie in enumerate(movies): listitem = xbmcgui.ListItem(label=movie.get('title', f'Film {idx + 1}')) listitem.setArt({'icon': movie.get('icon', 'DefaultMovies.png')}) xbmcplugin.addDirectoryItem( _handle, get_url(action='mojedb', type='movies', collection_idx=collection_idx, movie_idx=idx), listitem, True ) except (ValueError, IndexError) as e: xbmc.log(f"Chyba při zobrazení filmové kolekce: {str(e)}", xbmc.LOGERROR) popinfo(f"Chyba při zobrazení filmové kolekce: {str(e)}", icon=xbmcgui.NOTIFICATION_ERROR, sound=True) # Vyhledání konkrétního filmu elif params.get('type') == 'movies' and params.get('collection_idx') is not None and params.get('movie_idx') is not None: try: collection_idx = int(params['collection_idx']) movie_idx = int(params['movie_idx']) collection = movies_collections[collection_idx] movie_list = collection['data'].get('movies', []) movie = movie_list[movie_idx] except (ValueError, IndexError) as e: xbmc.log(f"Chyba při načítání filmu: {str(e)}", xbmc.LOGERROR) popinfo(f"Chyba při načítání filmu: {str(e)}", icon=xbmcgui.NOTIFICATION_ERROR, sound=True) xbmcplugin.endOfDirectory(_handle, updateListing=updateListing) return collection_icon = collection['data'].get('icon', 'DefaultMovies.png') 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', collection_icon)}) 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()