Created
February 11, 2026 08:50
-
-
Save CarstenG2/b7f3b58c62cfb91bc74a4c4be9027b23 to your computer and use it in GitHub Desktop.
xStream v2026.01.21 - Python 3.12+ fixes + startup optimization + pyaes import fix
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| from resources.lib.tools import cParser, cUtil | |
| from resources.lib.config import cConfig | |
| from xbmc import LOGERROR, log | |
| from os import path | |
| class cGuiElement: | |
| ''' | |
| This class "abstracts" a xbmc listitem. | |
| Kwargs: | |
| sTitle (str): title/label oft the GuiElement/listitem | |
| sSite (str): siteidentifier of the siteplugin, which is called if the GuiElement is selected | |
| sFunction (str): name of the function, which is called if the GuiElement is selected | |
| These arguments are mandatory. If not given on init, they have to be set by their setter-methods, before the GuiElement is added to the Gui. | |
| ''' | |
| DEFAULT_FOLDER_ICON = 'DefaultFolder.png' | |
| DEFAULT_FANART = path.join(cConfig().getAddonInfo('path'), 'fanart.jpg') | |
| MEDIA_TYPES = ['movie', 'tvshow', 'season', 'episode'] | |
| def __init__(self, sTitle: object = '', sSite: object = None, sFunction: object = None) -> None: | |
| self.__sType = 'video' | |
| self.__sMediaUrl = '' | |
| self.__sTitle = cUtil.cleanse_text(sTitle) | |
| self.__sTitleSecond = '' | |
| self.__sDescription = '' | |
| self.__sThumbnail = '' | |
| self.__sIcon = self.DEFAULT_FOLDER_ICON | |
| self.__aItemValues = {} | |
| self.__aProperties = {} | |
| self.__aContextElements = [] | |
| self.__sFanart = self.DEFAULT_FANART | |
| self.__sSiteName = sSite | |
| self.__sFunctionName = sFunction | |
| self._sLanguage = '' | |
| self._sSubLanguage = '' | |
| self._sYear = '' | |
| self._sQuality = '' | |
| self._sInfo = '' | |
| self._mediaType = '' | |
| self._season = '' | |
| self._episode = '' | |
| self._tmdbID = '' | |
| self._rating = '' | |
| self._isMetaSet = False | |
| def setType(self, sType): | |
| self.__sType = sType | |
| def getType(self): | |
| return self.__sType | |
| def setMediaUrl(self, sMediaUrl): | |
| self.__sMediaUrl = sMediaUrl | |
| def getMediaUrl(self): | |
| return self.__sMediaUrl | |
| def setSiteName(self, sSiteName): | |
| self.__sSiteName = sSiteName | |
| def getSiteName(self): | |
| return self.__sSiteName | |
| def setFunction(self, sFunctionName): | |
| self.__sFunctionName = sFunctionName | |
| def getFunction(self): | |
| return self.__sFunctionName | |
| def setTitle(self, sTitle): | |
| self.__sTitle = cUtil.cleanse_text(sTitle) | |
| # Sprachen im sName ins GUI Element übernehmen | |
| def getTitle(self): | |
| if ' (19' in self.__sTitle or ' (20' in self.__sTitle: | |
| isMatch, aYear = cParser.parse(self.__sTitle, r'(.*?)\((\d{4})\)') | |
| if isMatch: | |
| self.__sTitle = aYear[0][0] | |
| self.setYear(aYear[0][1]) | |
| if '*19' in self.__sTitle or '*20' in self.__sTitle: | |
| isMatch, aYear = cParser.parse(self.__sTitle, r'(.*?)\*(\d{4})\*') | |
| if isMatch: | |
| self.__sTitle = aYear[0][0] | |
| self.setYear(aYear[0][1]) | |
| if '*english*' in self.__sTitle.lower(): | |
| isMatch, aLang = cParser.parse(self.__sTitle, r'(.*?)\*(.*?)\*') | |
| if isMatch: | |
| self.__sTitle = aLang[0][0] | |
| self.setLanguage('EN') | |
| if '*deutsch*' in self.__sTitle.lower(): | |
| isMatch, aLang = cParser.parse(self.__sTitle, r'(.*?)\*(.*?)\*') | |
| if isMatch: | |
| self.__sTitle = aLang[0][0] | |
| self.setLanguage('DE') | |
| if 'English:' in self.__sTitle: | |
| self.__sTitle = self.__sTitle.replace('English:', '') | |
| self.setLanguage('EN') | |
| if 'Deutsch:' in self.__sTitle: | |
| self.__sTitle = self.__sTitle.replace('Deutsch:', '') | |
| self.setLanguage('DE') | |
| if '(omu)' in self.__sTitle.lower() or '*OmU*' in self.__sTitle: | |
| self.__sTitle = self.__sTitle.replace('(OmU) ', '') | |
| self.__sTitle = self.__sTitle.replace('(Omu) ', '') | |
| self.setLanguage('OmU') | |
| if self._sYear: self.__sTitle = self.__sTitle.strip() + ' (' + self._sYear + ')' | |
| return self.__sTitle.strip() | |
| def setMediaType(self, mediaType): | |
| ''' | |
| Set mediatype for GuiElement | |
| Args: | |
| mediaType(str): 'movie'/'tvshow'/'season'/'episode' | |
| ''' | |
| mediaType = mediaType.lower() | |
| if mediaType in self.MEDIA_TYPES: | |
| self._mediaType = mediaType | |
| else: | |
| log(cConfig().getLocalizedString(30166) + ' -> [guiElement]: Unknown MediaType given for %s' % self.getTitle(), LOGERROR) | |
| def setSeason(self, season): | |
| self._season = season | |
| self.__aItemValues['season'] = str(season) | |
| def setEpisode(self, episode): | |
| self._episode = episode | |
| self.__aItemValues['episode'] = str(episode) | |
| def setTVShowTitle(self, tvShowTitle): | |
| self.__aItemValues['TVShowTitle'] = str(tvShowTitle) | |
| def setYear(self, year): | |
| try: | |
| year = int(year) | |
| except: | |
| log(cConfig().getLocalizedString(30166) + ' -> [guiElement]: Year given for %s seems not to be a valid number' % self.getTitle(), LOGERROR) | |
| return False | |
| if len(str(year)) != 4: | |
| log(cConfig().getLocalizedString(30166) + ' -> [guiElement]: Year given for %s has %s digits, required 4 digits' % (self.getTitle(), len(str(year))), LOGERROR) | |
| return False | |
| if year > 0: | |
| self._sYear = str(year) | |
| self.__aItemValues['year'] = year | |
| return True | |
| else: | |
| log(cConfig().getLocalizedString(30166) + ' -> [guiElement]: Year given for %s must be greater than 0' % self.getTitle(), LOGERROR) | |
| return False | |
| def setQuality(self, quality): | |
| try: | |
| if '2160' in quality: | |
| self._sQuality = '4K 2160p' | |
| elif '1440' in quality: | |
| self._sQuality = '2K 1440p' | |
| elif '1080' in quality: | |
| self._sQuality = 'HD 1080p' | |
| elif '720' in quality: | |
| self._sQuality = 'HD 720p' | |
| elif '480' in quality: | |
| self._sQuality = 'SD 480p' | |
| elif '360' in quality: | |
| self._sQuality = 'SD 360p' | |
| elif 'HD' in quality: | |
| self._sQuality = 'HD' | |
| elif 'BDRip' in quality: | |
| self._sQuality = 'BD Rip' | |
| elif 'WEBRip' in quality: | |
| self._sQuality = 'WEB Rip' | |
| elif 'TS.MD' in quality: | |
| self._sQuality = 'TS Mic' | |
| elif 'MD.TS' in quality: | |
| self._sQuality = 'TS Mic' | |
| elif 'TS.LD' in quality: | |
| self._sQuality = 'TS Line' | |
| elif 'LD.TS' in quality: | |
| self._sQuality = 'TS Line' | |
| elif 'TS' in quality: | |
| self._sQuality = 'TS' | |
| #self._sQuality = quality | |
| except: | |
| pass | |
| def getQuality(self): | |
| return self._sQuality | |
| def setInfo(self, info): | |
| self._sInfo = info | |
| def getInfo(self): | |
| return self._sInfo | |
| def setTitleSecond(self, sTitleSecond): | |
| self.__sTitleSecond = cUtil.cleanse_text(str(sTitleSecond)) | |
| def getTitleSecond(self): | |
| return self.__sTitleSecond | |
| def setDescription(self, sDescription): | |
| sDescription = cUtil.cleanse_text(sDescription) | |
| self.__sDescription = sDescription | |
| self.__aItemValues['plot'] = sDescription | |
| def getDescription(self): | |
| if 'plot' not in self.__aItemValues: | |
| return self.__sDescription | |
| else: | |
| return self.__aItemValues['plot'] | |
| def setThumbnail(self, sThumbnail): | |
| self.__sThumbnail = sThumbnail | |
| try: | |
| if cConfig().getSetting('replacefanart') == 'true' and sThumbnail.startswith('http'): | |
| self.__sFanart = sThumbnail | |
| except: | |
| pass | |
| def getThumbnail(self): | |
| return self.__sThumbnail | |
| def setIcon(self, sIcon): | |
| self.__sIcon = sIcon | |
| def getIcon(self): | |
| return self.__sIcon | |
| def setFanart(self, sFanart): | |
| self.__sFanart = sFanart | |
| def getFanart(self): | |
| return self.__sFanart | |
| def addItemValue(self, sItemKey, sItemValue): | |
| self.__aItemValues[sItemKey] = sItemValue | |
| def setItemValues(self, aValueList): | |
| self.__aItemValues = aValueList | |
| def getItemValues(self): | |
| self.__aItemValues['title'] = self.getTitle() | |
| if self.getDescription(): | |
| self.__aItemValues['plot'] = self.getDescription() | |
| for sPropertyKey in self.__aProperties.keys(): | |
| self.__aItemValues[sPropertyKey] = self.__aProperties[sPropertyKey] | |
| return self.__aItemValues | |
| # siehe gui.setInfoTagVideo() | |
| def addItemProperties(self, sPropertyKey, sPropertyValue): | |
| self.__aProperties[sPropertyKey] = sPropertyValue | |
| def getItemProperties(self): | |
| for sItemValueKey in self.__aItemValues.keys(): | |
| if not self.__aItemValues[sItemValueKey] == '': | |
| try: | |
| self.__aProperties[sItemValueKey] = str(self.__aItemValues[sItemValueKey]) | |
| except: | |
| pass | |
| return self.__aProperties | |
| def addContextItem(self, oContextElement): | |
| self.__aContextElements.append(oContextElement) | |
| def getContextItems(self): | |
| return self.__aContextElements | |
| def setLanguage(self, sLang): | |
| self._sLanguage = str(sLang) | |
| def setSubLanguage(self, sLang): | |
| self._sSubLanguage = str(sLang) | |
| def getMeta(self, mediaType, tmdbID='', TVShowTitle='', season='', episode='', mode='add'): | |
| ''' | |
| Fetch metainformations for GuiElement. | |
| Args: | |
| mediaType(str): 'movie'/'tvshow'/'season'/'episode' | |
| Kwargs: | |
| tmdbID (str) : | |
| TVShowTitle (str) : | |
| mode (str) : 'add'/'replace' defines if fetched metainformtions should be added to existing informations, or if they should replace them. | |
| ''' | |
| if cConfig().getSetting('TMDBMETA') == 'false': | |
| return False | |
| if not self._mediaType: | |
| self.setMediaType(mediaType) | |
| if mode not in ['add', 'replace']: | |
| log(cConfig().getLocalizedString(30166) + ' -> [guiElement]: Wrong meta set mode', LOGERROR) | |
| if not season and self._season: | |
| season = self._season | |
| if not episode and self._episode: | |
| episode = self._episode | |
| if not self._mediaType: | |
| log(cConfig().getLocalizedString(30166) + ' -> [guiElement]: Could not get MetaInformations for %s, mediaType not defined' % self.getTitle(), LOGERROR) | |
| return False | |
| from resources.lib.tmdb import cTMDB | |
| oMetaget = cTMDB() | |
| if not oMetaget: | |
| return False | |
| if self._mediaType == 'movie': | |
| if self._sYear: | |
| meta = oMetaget.get_meta(self._mediaType, self.getTitle(), year=self._sYear, advanced=cConfig().getSetting('advanced')) | |
| else: | |
| meta = oMetaget.get_meta(self._mediaType, self.getTitle(), advanced=cConfig().getSetting('advanced')) | |
| elif self._mediaType == 'tvshow': | |
| if self._sYear: | |
| meta = oMetaget.get_meta(self._mediaType, self.getTitle(), year=self._sYear, advanced=cConfig().getSetting('advanced')) | |
| else: | |
| meta = oMetaget.get_meta(self._mediaType, self.getTitle(), advanced=cConfig().getSetting('advanced')) | |
| elif self._mediaType == 'season': | |
| meta = {} | |
| elif self._mediaType == 'episode': | |
| meta = oMetaget.get_meta_episodes(self._mediaType, TVShowTitle, tmdbID, str(season), str(episode)) | |
| else: | |
| return False | |
| if not meta: | |
| return False | |
| if self._mediaType == 'season': | |
| meta = meta[0] | |
| if mode == 'replace': | |
| self.setItemValues(meta) | |
| if 'cover_url' in meta: | |
| self.setThumbnail(meta['cover_url']) | |
| if 'backdrop_url' in meta: | |
| self.setFanart(meta['backdrop_url']) | |
| if 'title' in meta and episode: | |
| self.setTitle(str(episode) + '. ' + meta['title']) | |
| else: | |
| meta.update(self.__aItemValues) | |
| meta.update(self.__aProperties) | |
| if 'cover_url' in meta != '' and self.__sThumbnail == '': | |
| self.setThumbnail(meta['cover_url']) | |
| if 'backdrop_url' in meta and self.__sFanart == self.DEFAULT_FANART: | |
| self.setFanart(meta['backdrop_url']) | |
| self.setItemValues(meta) | |
| if 'tmdb_id' in meta: | |
| self._tmdbID = meta['tmdb_id'] | |
| self._isMetaSet = True | |
| return meta |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| import hashlib | |
| import hmac | |
| import json | |
| import time | |
| import base64 | |
| import requests | |
| try: | |
| from resources.lib import pyaes | |
| except ImportError: | |
| try: | |
| import pyaes | |
| except ImportError: | |
| from resolveurl.lib import pyaes | |
| from urllib.parse import quote | |
| class MYJDException(BaseException): | |
| pass | |
| def PAD(s): | |
| BS = 16 | |
| try: | |
| return s + ((BS - len(s) % BS) * chr(BS - len(s) % BS)).encode() | |
| except Exception: | |
| return s + (BS - len(s) % BS) * chr(BS - len(s) % BS) | |
| def UNPAD(s): | |
| try: | |
| return s[0:-s[-1]] | |
| except Exception: | |
| return s[0:-ord(s[-1])] | |
| class System: | |
| def __init__(self, device): | |
| self.device = device | |
| self.url = '/system' | |
| def exit_jd(self): | |
| return self.device.action(self.url + "/exitJD") | |
| def restart_jd(self): | |
| return self.device.action(self.url + "/restartJD") | |
| def hibernate_os(self): | |
| return self.device.action(self.url + "/hibernateOS") | |
| def shutdown_os(self, force): | |
| return self.device.action(self.url + "/shutdownOS", force) | |
| def standby_os(self): | |
| return self.device.action(self.url + "/standbyOS") | |
| class Update: | |
| def __init__(self, device): | |
| self.device = device | |
| self.url = '/update' | |
| def restart_and_update(self): | |
| return self.device.action(self.url + "/restartAndUpdate") | |
| def run_update_check(self): | |
| return self.device.action(self.url + "/runUpdateCheck") | |
| def is_update_available(self): | |
| return self.device.action(self.url + "/isUpdateAvailable") | |
| class DownloadController: | |
| def __init__(self, device): | |
| self.device = device | |
| self.url = '/downloadcontroller' | |
| def start_downloads(self): | |
| return self.device.action(self.url + "/start") | |
| def stop_downloads(self): | |
| return self.device.action(self.url + "/stop") | |
| def pause_downloads(self, value): | |
| params = [value] | |
| return self.device.action(self.url + "/pause", params) | |
| def get_speed_in_bytes(self): | |
| return self.device.action(self.url + "/getSpeedInBps") | |
| def force_download(self, link_ids, package_ids): | |
| params = [link_ids, package_ids] | |
| return self.device.action(self.url + "/forceDownload", params) | |
| def get_current_state(self): | |
| return self.device.action(self.url + "/getCurrentState") | |
| class Linkgrabber: | |
| def __init__(self, device): | |
| self.device = device | |
| self.url = '/linkgrabberv2' | |
| def clear_list(self): | |
| return self.device.action(self.url + "/clearList", http_action="POST") | |
| def move_to_downloadlist(self, links_ids, packages_ids): | |
| params = [links_ids, packages_ids] | |
| return self.device.action(self.url + "/moveToDownloadlist", params) | |
| def query_links(self, params=[ | |
| {"bytesTotal": True, | |
| "comment": True, | |
| "status": True, | |
| "enabled": True, | |
| "maxResults": -1, | |
| "startAt": 0, | |
| "hosts": True, | |
| "url": True, | |
| "availability": True, | |
| "variantIcon": True, | |
| "variantName": True, | |
| "variantID": True, | |
| "variants": True, | |
| "priority": True}]): | |
| return self.device.action(self.url + "/queryLinks", params) | |
| def cleanup(self, action, mode, selection_type, links_ids=[], packages_ids=[]): | |
| params = [links_ids, packages_ids] | |
| params += [action, mode, selection_type] | |
| return self.device.action(self.url + "/cleanup", params) | |
| def add_container(self, type_, content): | |
| params = [type_, content] | |
| return self.device.action(self.url + "/addContainer", params) | |
| def get_download_urls(self, links_ids, packages_ids, url_display_type): | |
| params = [packages_ids, links_ids, url_display_type] | |
| return self.device.action(self.url + "/getDownloadUrls", params) | |
| def set_priority(self, priority, links_ids, packages_ids): | |
| params = [priority, links_ids, packages_ids] | |
| return self.device.action(self.url + "/setPriority", params) | |
| def set_enabled(self, params): | |
| return self.device.action(self.url + "/setEnabled", params) | |
| def get_variants(self, params): | |
| return self.device.action(self.url + "/getVariants", params) | |
| def add_links(self, params=[ | |
| {"autostart": False, | |
| "links": None, | |
| "packageName": None, | |
| "extractPassword": None, | |
| "priority": "DEFAULT", | |
| "downloadPassword": None, | |
| "destinationFolder": None, | |
| "overwritePackagizerRules": False}]): | |
| return self.device.action("/linkgrabberv2/addLinks", params) | |
| def get_childrenchanged(self): | |
| pass | |
| def remove_links(self): | |
| pass | |
| def get_downfolderhistoryselectbase(self): | |
| pass | |
| def help(self): | |
| return self.device.action("/linkgrabberv2/help", http_action="GET") | |
| def rename_link(self): | |
| pass | |
| def move_links(self): | |
| pass | |
| def set_variant(self): | |
| pass | |
| def get_package_count(self): | |
| pass | |
| def rename_package(self): | |
| pass | |
| def query_packages(self): | |
| pass | |
| def move_packages(self): | |
| pass | |
| def add_variant_copy(self): | |
| pass | |
| class Downloads: | |
| def __init__(self, device): | |
| self.device = device | |
| self.url = "/downloadsV2" | |
| def query_links(self, params=[ | |
| {"bytesTotal": True, | |
| "comment": True, | |
| "status": True, | |
| "enabled": True, | |
| "maxResults": -1, | |
| "startAt": 0, | |
| "packageUUIDs": [], | |
| "host": True, | |
| "url": True, | |
| "bytesloaded": True, | |
| "speed": True, | |
| "eta": True, | |
| "finished": True, | |
| "priority": True, | |
| "running": True, | |
| "skipped": True, | |
| "extractionStatus": True}]): | |
| return self.device.action(self.url + "/queryLinks", params) | |
| def query_packages(self, params=[ | |
| {"bytesLoaded": True, | |
| "bytesTotal": True, | |
| "comment": True, | |
| "enabled": True, | |
| "eta": True, | |
| "priority": True, | |
| "finished": True, | |
| "running": True, | |
| "speed": True, | |
| "status": True, | |
| "childCount": True, | |
| "hosts": True, | |
| "saveTo": True, | |
| "maxResults": -1, | |
| "startAt": 0}]): | |
| return self.device.action(self.url + "/queryPackages", params) | |
| def cleanup(self, action, mode, selection_type, links_ids=[], packages_ids=[]): | |
| params = [links_ids, packages_ids] | |
| params += [action, mode, selection_type] | |
| return self.device.action(self.url + "/cleanup", params) | |
| class Jddevice: | |
| def __init__(self, jd, device_dict): | |
| self.name = device_dict["name"] | |
| self.device_id = device_dict["id"] | |
| self.device_type = device_dict["type"] | |
| self.myjd = jd | |
| self.linkgrabber = Linkgrabber(self) | |
| self.downloads = Downloads(self) | |
| self.downloadcontroller = DownloadController(self) | |
| self.update = Update(self) | |
| self.system = System(self) | |
| def action(self, path, params=(), http_action="POST"): | |
| action_url = self.__action_url() | |
| response = self.myjd.request_api(path, http_action, params, action_url) | |
| if response is None: | |
| return False | |
| return response['data'] | |
| def __action_url(self): | |
| return "/t_" + self.myjd.get_session_token() + "_" + self.device_id | |
| class Myjdapi: | |
| def __init__(self): | |
| self.__request_id = int(time.time() * 1000) | |
| self.__api_url = "http://api.jdownloader.org" | |
| self.__app_key = "http://git.io/vmcsk" | |
| self.__api_version = 1 | |
| self.__devices = None | |
| self.__login_secret = None | |
| self.__device_secret = None | |
| self.__session_token = None | |
| self.__regain_token = None | |
| self.__server_encryption_token = None | |
| self.__device_encryption_token = None | |
| self.__connected = False | |
| def get_session_token(self): | |
| return self.__session_token | |
| def is_connected(self): | |
| return self.__connected | |
| def set_app_key(self, app_key): | |
| self.__app_key = app_key | |
| def __secret_create(self, email, password, domain): | |
| secret_hash = hashlib.sha256() | |
| secret_hash.update(email.lower().encode('utf-8') + password.encode('utf-8') + domain.lower().encode('utf-8')) | |
| return secret_hash.digest() | |
| def __update_encryption_tokens(self): | |
| if self.__server_encryption_token is None: | |
| old_token = self.__login_secret | |
| else: | |
| old_token = self.__server_encryption_token | |
| new_token = hashlib.sha256() | |
| new_token.update(old_token + bytearray.fromhex(self.__session_token)) | |
| self.__server_encryption_token = new_token.digest() | |
| new_token = hashlib.sha256() | |
| new_token.update(self.__device_secret + bytearray.fromhex(self.__session_token)) | |
| self.__device_encryption_token = new_token.digest() | |
| def __signature_create(self, key, data): | |
| signature = hmac.new(key, data.encode('utf-8'), hashlib.sha256) | |
| return signature.hexdigest() | |
| def __decrypt(self, secret_token, data): | |
| init_vector = secret_token[:len(secret_token) // 2] | |
| key = secret_token[len(secret_token) // 2:] | |
| decryptor = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(key, init_vector)) | |
| decrypted_data = decryptor.feed(base64.b64decode(data)) | |
| decrypted_data += decryptor.feed() | |
| return decrypted_data | |
| def __encrypt(self, secret_token, data): | |
| init_vector = secret_token[:len(secret_token) // 2] | |
| key = secret_token[len(secret_token) // 2:] | |
| encryptor = pyaes.Encrypter(pyaes.AESModeOfOperationCBC(key, init_vector)) | |
| encrypted_data = encryptor.feed(data) | |
| encrypted_data += encryptor.feed() | |
| encrypted_data = base64.b64encode(encrypted_data) | |
| return encrypted_data.decode('utf-8') | |
| def update_request_id(self): | |
| self.__request_id = int(time.time()) | |
| def connect(self, email, password): | |
| self.__login_secret = self.__secret_create(email, password, "server") | |
| self.__device_secret = self.__secret_create(email, password, "device") | |
| response = self.request_api("/my/connect", "GET", [("email", email), ("appkey", self.__app_key)]) | |
| self.__connected = True | |
| self.update_request_id() | |
| self.__session_token = response["sessiontoken"] | |
| self.__regain_token = response["regaintoken"] | |
| self.__update_encryption_tokens() | |
| self.update_devices() | |
| def reconnect(self): | |
| response = self.request_api("/my/reconnect", "GET", [("sessiontoken", self.__session_token), ("regaintoken", self.__regain_token)]) | |
| self.update_request_id() | |
| self.__session_token = response["sessiontoken"] | |
| self.__regain_token = response["regaintoken"] | |
| self.__update_encryption_tokens() | |
| def disconnect(self): | |
| self.request_api("/my/disconnect", "GET", [("sessiontoken", self.__session_token)]) | |
| self.update_request_id() | |
| self.__login_secret = None | |
| self.__device_secret = None | |
| self.__session_token = None | |
| self.__regain_token = None | |
| self.__server_encryption_token = None | |
| self.__device_encryption_token = None | |
| self.__devices = None | |
| self.__connected = False | |
| def update_devices(self): | |
| response = self.request_api("/my/listdevices", "GET", [("sessiontoken", self.__session_token)]) | |
| self.update_request_id() | |
| self.__devices = response["list"] | |
| def list_devices(self): | |
| return self.__devices | |
| def get_device(self, device_name=None, device_id=None): | |
| if not self.is_connected(): | |
| raise (MYJDException("No connection established\n")) | |
| if device_id is not None: | |
| for device in self.__devices: | |
| if device["id"] == device_id: | |
| return Jddevice(self, device) | |
| elif device_name is not None: | |
| for device in self.__devices: | |
| if device["name"] == device_name: | |
| return Jddevice(self, device) | |
| raise MYJDException("Device not found\n") | |
| def request_api(self, path, http_method="GET", params=None, action=None): | |
| data = None | |
| if not self.is_connected() and path != "/my/connect": | |
| raise (MYJDException("No connection established\n")) | |
| if http_method == "GET": | |
| query = [path + "?"] | |
| for param in params: | |
| if param[0] != "encryptedLoginSecret": | |
| query += ["%s=%s" % (param[0], quote(param[1]))] | |
| else: | |
| query += ["&%s=%s" % (param[0], param[1])] | |
| query += ["rid=" + str(self.__request_id)] | |
| if self.__server_encryption_token is None: | |
| query += ["signature=" + str(self.__signature_create(self.__login_secret, query[0] + "&".join(query[1:])))] | |
| else: | |
| query += ["signature=" + str(self.__signature_create(self.__server_encryption_token, query[0] + "&".join(query[1:])))] | |
| query = query[0] + "&".join(query[1:]) | |
| encrypted_response = requests.get(self.__api_url + query) | |
| else: | |
| params_request = [] | |
| for param in params: | |
| if not isinstance(param, list): | |
| params_request += [json.dumps(param)] | |
| else: | |
| params_request += [param] | |
| params_request = {"apiVer": self.__api_version, "url": path, "params": params_request, "rid": self.__request_id} | |
| data = json.dumps(params_request).replace('"null"', "null").replace("'null'", "null") | |
| encrypted_data = self.__encrypt(self.__device_encryption_token, data) | |
| if action is not None: | |
| request_url = self.__api_url + action + path | |
| else: | |
| request_url = self.__api_url + path | |
| encrypted_response = requests.post(request_url, headers={"Content-Type": "application/aesjson-jd; charset=utf-8"}, data=encrypted_data) | |
| if encrypted_response.status_code != 200: | |
| error_msg = json.loads(encrypted_response.text) | |
| msg = "\n\tSOURCE: " + error_msg["src"] + "\n\tTYPE: " + \ | |
| error_msg["type"] + "\n------\nREQUEST_URL: " + \ | |
| self.__api_url + path | |
| if http_method == "GET": | |
| msg += query | |
| msg += "\n" | |
| if data is not None: | |
| msg += "DATA:\n" + data | |
| raise (MYJDException(msg)) | |
| if action is None: | |
| if not self.__server_encryption_token: | |
| response = self.__decrypt(self.__login_secret, encrypted_response.text) | |
| else: | |
| response = self.__decrypt(self.__server_encryption_token, encrypted_response.text) | |
| else: | |
| if params is not None: | |
| response = self.__decrypt(self.__device_encryption_token, encrypted_response.text) | |
| else: | |
| return {"data": response} | |
| jsondata = json.loads(response.decode('utf-8')) | |
| if jsondata['rid'] != self.__request_id: | |
| self.update_request_id() | |
| return None | |
| self.update_request_id() | |
| return jsondata |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| import sys | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| from xbmc import LOGINFO as LOGNOTICE, log | |
| from urllib.request import Request, urlopen, build_opener | |
| from urllib.error import HTTPError | |
| from urllib.parse import urlencode, quote_plus | |
| class cPyLoadHandler: | |
| def __init__(self): | |
| self.config = cConfig() | |
| def sendToPyLoad(self, sPackage, sUrl): | |
| log(cConfig().getLocalizedString(30166) + ' -> [pyLoadHandler]: PyLoad package: ' + str(sPackage) + ', ' + str(sUrl), LOGNOTICE) | |
| if self.__sendLinkToCore(sPackage, sUrl): | |
| cGui().showInfo(cConfig().getLocalizedString(30257), cConfig().getLocalizedString(30256), 5) | |
| else: | |
| cGui().showInfo(cConfig().getLocalizedString(30257), cConfig().getLocalizedString(30258), 5) | |
| def __sendLinkToCore(self, sPackage, sUrl): | |
| log(cConfig().getLocalizedString(30166) + ' -> [pyLoadHandler]: Sending link...', LOGNOTICE) | |
| try: | |
| py_host = self.config.getSetting('pyload_host') | |
| py_port = self.config.getSetting('pyload_port') | |
| py_user = self.config.getSetting('pyload_user') | |
| py_passwd = self.config.getSetting('pyload_passwd') | |
| mydata = [('username', py_user), ('password', py_passwd)] | |
| mydata = urlencode(mydata) | |
| # check if host has a leading http:// | |
| if py_host.find('http://') != 0: | |
| py_host = 'http://' + py_host | |
| log(cConfig().getLocalizedString(30166) + ' -> [pyLoadHandler]: Attemting to connect to PyLoad at: ' + py_host + ':' + py_port, LOGNOTICE) | |
| req = Request(py_host + ':' + py_port + '/api/login', mydata) | |
| req.add_header("Content-type", "application/x-www-form-urlencoded") | |
| page = urlopen(req).read() | |
| page = page[1:] | |
| session = page[:-1] | |
| opener = build_opener() | |
| opener.addheaders.append(('Cookie', 'beaker.session.id=' + session)) | |
| sPackage = sPackage.translate(str.maketrans('\\/:*?"<>|', '_________')) | |
| py_url = py_host + ':' + py_port + '/api/addPackage?name="' + quote_plus(sPackage) + '"&links=["' + quote_plus(sUrl) + '"]' | |
| log(cConfig().getLocalizedString(30166) + ' -> [pyLoadHandler]: PyLoad API call: ' + py_url, LOGNOTICE) | |
| sock = opener.open(py_url).read() | |
| sock.close() | |
| return True | |
| except HTTPError as e: | |
| log(cConfig().getLocalizedString(30166) + ' -> [pyLoadHandler]: unable to send link: Error= ' + str(sys.exc_info()[0]), LOGNOTICE) | |
| log(e.code, LOGNOTICE) | |
| log(e.read(), LOGNOTICE) | |
| try: | |
| sock.close() | |
| except Exception: | |
| log(cConfig().getLocalizedString(30166) + ' -> [pyLoadHandler]: unable to close socket...', LOGNOTICE) | |
| return False |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| import time | |
| import xbmcgui | |
| import re | |
| import os | |
| import hashlib | |
| import json | |
| import traceback | |
| import ssl | |
| import certifi | |
| import socket | |
| import zlib | |
| import http.client | |
| from resources.lib.config import cConfig | |
| from resources.lib.tools import logger, cCache | |
| from xbmcvfs import translatePath | |
| from urllib.parse import quote, urlencode, urlparse, quote_plus | |
| from urllib.error import HTTPError, URLError | |
| from urllib.request import HTTPHandler, HTTPSHandler, Request, HTTPCookieProcessor, build_opener, urlopen, HTTPRedirectHandler | |
| from http.cookiejar import LWPCookieJar, Cookie | |
| from http.client import HTTPException | |
| from random import choice | |
| class IPHTTPSConnection(http.client.HTTPSConnection): | |
| def __init__(self, host, ip=None, port=None, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, context=None): | |
| self.context = context | |
| # If an IP is provided, connect to it rather than the resolved host. | |
| self.ip = ip | |
| self.actual_host = host # original hostname for SNI and Host header | |
| super().__init__(host if not ip else ip, port, timeout=timeout, context=context) | |
| def connect(self): | |
| # Create a socket connection to the provided IP (if any) | |
| if self.ip: | |
| self.sock = self._create_connection((self.ip, self.port), self.timeout) | |
| #if self._tunnel_host: | |
| # self._tunnel() | |
| # Wrap the socket with our SSL context using the actual host for SNI. | |
| self.sock = self.context.wrap_socket(self.sock, server_hostname=self.actual_host) | |
| else: | |
| super().connect() | |
| class CustomSecureHTTPSHandler(HTTPSHandler): | |
| def __init__(self, ip=None): | |
| # Create an SSL context with certifi's CA bundle. | |
| context = ssl.create_default_context(cafile=certifi.where()) | |
| # If an IP is provided, disable hostname checking (since we'll verify using SNI later). | |
| context.check_hostname = False if ip else True | |
| context.verify_mode = ssl.CERT_REQUIRED | |
| self.ip = ip | |
| self.context = context | |
| super().__init__(context=context) | |
| def https_open(self, req): | |
| # Extract the hostname from the request URL. | |
| parsed = urlparse(req.full_url) | |
| host = parsed.hostname | |
| # Define a connection factory that returns an IPHTTPSConnection | |
| def connection_factory(*args, **kwargs): | |
| return IPHTTPSConnection(host, ip=self.ip, timeout=req.timeout, context=self.context) | |
| return self.do_open(connection_factory, req) | |
| class RedirectFilter(HTTPRedirectHandler): | |
| def redirect_request(self, req, fp, code, msg, hdrs, newurl): | |
| if cConfig().getSetting('bypassDNSlock', 'false') != 'true': | |
| if 'notice.cuii' in newurl: | |
| xbmcgui.Dialog().ok(cConfig().getLocalizedString(30265), cConfig().getLocalizedString(30260) + '\n' + cConfig().getLocalizedString(30261)) | |
| return None | |
| return HTTPRedirectHandler.redirect_request(self, req, fp, code, msg, hdrs, newurl) | |
| class cRequestHandler: | |
| # useful for e.g. tmdb request where multiple requests are made within a loop | |
| persistent_openers = {} | |
| @staticmethod | |
| def RandomUA(): | |
| #Random User Agents aktualisiert 08.06.2025 | |
| FF_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:139.0) Gecko/20100101 Firefox/139.0' | |
| OPERA_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 OPR/119.0.0.0' | |
| ANDROID_USER_AGENT = 'Mozilla/5.0 (Linux; Android 15; SM-S931U Build/AP3A.240905.015.A2; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/132.0.6834.163 Mobile Safari/537.36' | |
| EDGE_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36 Edg/134.0.0.0' | |
| CHROME_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36' | |
| SAFARI_USER_AGENT = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 14_7_6) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.4 Safari/605.1.15' | |
| _User_Agents = [FF_USER_AGENT, OPERA_USER_AGENT, EDGE_USER_AGENT, CHROME_USER_AGENT, SAFARI_USER_AGENT] | |
| return choice(_User_Agents) | |
| def __init__(self, sUrl, caching=True, ignoreErrors=False, method='GET', data=None, compression=True, jspost=False, ssl_verify=False, bypass_dns=False): | |
| self._sUrl = self.__cleanupUrl(sUrl) | |
| self._sRealUrl = '' | |
| self._USER_AGENT = self.RandomUA() | |
| self._aParameters = {} | |
| self._headerEntries = {} | |
| self._profilePath = translatePath(cConfig().getAddonInfo('profile')) | |
| self._cachePath = '' | |
| self._cookiePath = '' | |
| self._Status = '' | |
| self._sResponseHeader = '' | |
| self._ssl_verify = ssl_verify | |
| self._bypass_dns = bypass_dns | |
| self.ignoreDiscard(False) | |
| self.ignoreExpired(False) | |
| self.caching = caching | |
| self.method = method | |
| self.data = data | |
| self.ignoreErrors = ignoreErrors | |
| self.compression = compression | |
| self.jspost = jspost | |
| self.cacheTime = int(cConfig().getSetting('cacheTime', 360)) *60 # 360 Minuten * 60 = 6 Stunden Cachetime | |
| self.requestTimeout = int(cConfig().getSetting('requestTimeout', 10)) | |
| self.bypassDNSlock = (cConfig().getSetting('bypassDNSlock', 'false') == 'true') | |
| self.removeBreakLines(True) | |
| self.removeNewLines(True) | |
| self.__setDefaultHeader() | |
| self.__setCachePath() | |
| self.__setCookiePath() | |
| self.isMemoryCacheActive = (cConfig().getSetting('volatileHtmlCache', 'false') == 'true') | |
| if self.isMemoryCacheActive: | |
| self._memCache = cCache() | |
| socket.setdefaulttimeout(self.requestTimeout) | |
| def getStatus(self): | |
| return self._Status | |
| def removeNewLines(self, bRemoveNewLines): | |
| self.__bRemoveNewLines = bRemoveNewLines | |
| def removeBreakLines(self, bRemoveBreakLines): | |
| self.__bRemoveBreakLines = bRemoveBreakLines | |
| def addHeaderEntry(self, sHeaderKey, sHeaderValue): | |
| self._headerEntries[sHeaderKey] = sHeaderValue | |
| def getHeaderEntry(self, sHeaderKey): | |
| if sHeaderKey in self._headerEntries: | |
| return self._headerEntries[sHeaderKey] | |
| def addParameters(self, key, value, Quote=False): | |
| self._aParameters[key] = value if not Quote else quote(str(value)) | |
| def getResponseHeader(self): | |
| return self._sResponseHeader | |
| def getRealUrl(self): | |
| return self._sRealUrl | |
| def getRequestUri(self): | |
| return self._sUrl + '?' + urlencode(self._aParameters) | |
| def __setDefaultHeader(self): | |
| self.addHeaderEntry('User-Agent', self._USER_AGENT) | |
| self.addHeaderEntry('Accept-Language', 'de,en-US;q=0.7,en;q=0.3') | |
| self.addHeaderEntry('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8') | |
| if self.compression: | |
| self.addHeaderEntry('Accept-Encoding', 'gzip, deflate') | |
| self.addHeaderEntry('Connection', 'keep-alive') | |
| self.addHeaderEntry('Keep-Alive', 'timeout=5') | |
| @staticmethod | |
| def __getDefaultHandler(ssl_verify, ip=None): | |
| if ip: | |
| return [CustomSecureHTTPSHandler(ip=ip)] | |
| elif ssl_verify: | |
| return [CustomSecureHTTPSHandler()] | |
| else: | |
| ssl_context = ssl.create_default_context() | |
| ssl_context.check_hostname = False | |
| ssl_context.verify_mode = ssl.CERT_NONE | |
| return [HTTPSHandler(context=ssl_context)] | |
| @staticmethod | |
| def __cleanupUrl(url): | |
| #p = urlparse(url) | |
| #if p.query: | |
| # query = quote_plus(p.query).replace('%3D', '=').replace('%26', '&') | |
| # p = p._replace(query=p.query.replace(p.query, query)) | |
| #else: | |
| # path = quote_plus(p.path).replace('%2F', '/').replace('%26', '&').replace('%3D', '=') | |
| # p = p._replace(path=p.path.replace(p.path, path)) | |
| #return p.geturl() | |
| return url | |
| def request(self): | |
| if self.caching and self.cacheTime > 0 and self.method == 'GET' and self.data is None: | |
| if self.isMemoryCacheActive: | |
| sContent = self.__readVolatileCache(self.getRequestUri(), self.cacheTime) | |
| else: | |
| sContent = self.__readPersistentCache(self.getRequestUri()) | |
| if sContent: | |
| self._Status = '200' | |
| return sContent | |
| else: | |
| logger.info('-> [requestHandler]: read html for %s' % self.getRequestUri()) | |
| # nur ausführen wenn der übergabeparameter und die konfiguration passen | |
| if self._bypass_dns and self.bypassDNSlock: | |
| ### DNS lock bypass | |
| ip_override = self.__doh_request(self._sUrl) | |
| ### DNS lock bypass | |
| else: | |
| ip_override = None | |
| cookieJar = LWPCookieJar(filename=self._cookiePath) | |
| try: | |
| cookieJar.load(ignore_discard=self.__bIgnoreDiscard, ignore_expires=self.__bIgnoreExpired) | |
| except Exception as e: | |
| logger.debug(e) | |
| domain = urlparse(self._sUrl).netloc | |
| if domain in cRequestHandler.persistent_openers: | |
| opener = cRequestHandler.persistent_openers[domain] | |
| else: | |
| handlers = self.__getDefaultHandler(self._ssl_verify, ip_override) | |
| handlers += [HTTPHandler(), HTTPCookieProcessor(cookiejar=cookieJar), RedirectFilter()] | |
| opener = build_opener(*handlers) | |
| cRequestHandler.persistent_openers[domain] = opener | |
| # Prepare parameters for GET/POST | |
| if self.method == 'POST': | |
| if self.data is not None: | |
| if isinstance(self.data, dict): | |
| # Default: form data | |
| sParameters = urlencode(self.data).encode() | |
| elif isinstance(self.data, str): | |
| sParameters = self.data.encode() | |
| else: | |
| sParameters = self.data | |
| else: | |
| sParameters = None | |
| else: | |
| sParameters = json.dumps(self._aParameters).encode() if self.jspost else urlencode(self._aParameters, True).encode() | |
| if len(sParameters) == 0: | |
| sParameters = None | |
| oRequest = Request(self._sUrl, sParameters if sParameters and len(sParameters) > 0 else None) | |
| for key, value in self._headerEntries.items(): | |
| oRequest.add_header(key, value) | |
| if self.method == 'POST' and 'Content-Type' not in self._headerEntries: | |
| oRequest.add_header('Content-Type', 'application/x-www-form-urlencoded') | |
| elif self.jspost: | |
| oRequest.add_header('Content-Type', 'application/json') | |
| cookieJar.add_cookie_header(oRequest) | |
| try: | |
| oResponse = opener.open(oRequest) | |
| except HTTPError as e: | |
| if e.code >= 400: | |
| self._Status = str(e.code) | |
| data = e.fp.read() | |
| if 'DDOS-GUARD' in str(data): | |
| opener = build_opener(HTTPCookieProcessor(cookieJar)) | |
| opener.addheaders = [('User-agent', self._USER_AGENT), ('Referer', self._sUrl)] | |
| response = opener.open('https://check.ddos-guard.net/check.js') | |
| content = response.read().decode('utf-8', 'replace') | |
| url2 = re.findall("Image.*?'([^']+)'; new", content) | |
| url3 = urlparse(self._sUrl) | |
| url3 = '%s://%s/%s' % (url3.scheme, url3.netloc, url2[0]) | |
| opener = build_opener(HTTPCookieProcessor(cookieJar)) | |
| opener.addheaders = [('User-agent', self._USER_AGENT), ('Referer', self._sUrl)] | |
| opener.open(url3).read() | |
| opener = build_opener(HTTPCookieProcessor(cookieJar)) | |
| opener.addheaders = [('User-agent', self._USER_AGENT), ('Referer', self._sUrl)] | |
| oResponse = opener.open(self._sUrl, sParameters if len(sParameters) > 0 else None) | |
| if not oResponse: | |
| logger.error(' -> [requestHandler]: Failed DDOS-GUARD active: ' + self._sUrl) | |
| return 'DDOS GUARD SCHUTZ' | |
| elif 'cloudflare' in str(e.headers): | |
| if not self.ignoreErrors: | |
| # Angepasste, nutzerfreundliche Cloudflare Meldung | |
| msg = 'Die angeforderte Seite (%s) ist durch Cloudflare geschützt.' % urlparse(self._sUrl).netloc | |
| msg += '\nDer Zugriff ist deshalb blockiert.' | |
| msg += '\n\nBitte versuchen Sie es später erneut oder prüfen Sie die Webseite im Browser.' | |
| xbmcgui.Dialog().ok('Cloudflare Schutz aktiv', msg) | |
| logger.error(' -> [requestHandler]: Failed Cloudflare active: ' + self._sUrl) | |
| return 'CLOUDFLARE-SCHUTZ AKTIV' # Meldung geht als "e.doc" in die exception nach default.py | |
| else: | |
| if not self.ignoreErrors: | |
| xbmcgui.Dialog().ok('xStream', cConfig().getLocalizedString(30259) + ' {0} {1}'.format(self._sUrl, str(e))) | |
| logger.error(' -> [requestHandler]: HTTPError ' + str(e) + ' Url: ' + self._sUrl) | |
| return 'SEITE NICHT ERREICHBAR' | |
| else: | |
| if not self.ignoreErrors: | |
| xbmcgui.Dialog().ok('xStream', cConfig().getLocalizedString(30259) + ' {0} {1}'.format(self._sUrl, str(e))) | |
| logger.error(' -> [requestHandler]: HTTPError ' + str(e) + ' Url: ' + self._sUrl) | |
| return 'SEITE NICHT ERREICHBAR' | |
| except URLError as e: | |
| if not self.ignoreErrors: | |
| xbmcgui.Dialog().ok('xStream', str(e.reason)) | |
| logger.error(' -> [requestHandler]: URLError ' + str(e.reason) + ' Url: ' + self._sUrl) | |
| return 'URL FEHLER' | |
| except HTTPException as e: | |
| if not self.ignoreErrors: | |
| xbmcgui.Dialog().ok('xStream', str(e)) | |
| logger.error(' -> [requestHandler]: HTTPException ' + str(e) + ' Url: ' + self._sUrl) | |
| return 'TIMEOUT' | |
| self._sResponseHeader = oResponse.info() | |
| content_encoding = self._sResponseHeader.get('Content-Encoding', '').lower() | |
| if content_encoding: | |
| raw_content = oResponse.read() | |
| if content_encoding == 'gzip': | |
| decompressed = zlib.decompress(raw_content, wbits=zlib.MAX_WBITS | 16) | |
| elif content_encoding == 'deflate': | |
| decompressed = zlib.decompress(raw_content, wbits=-zlib.MAX_WBITS) | |
| else: | |
| decompressed = raw_content | |
| sContent = decompressed.decode('utf-8', 'replace') | |
| else: | |
| sContent = oResponse.read().decode('utf-8', 'replace') | |
| if 'lazingfast' in sContent: | |
| bf = cBF().resolve(self._sUrl, sContent, cookieJar, self._USER_AGENT, sParameters) | |
| if bf: | |
| sContent = bf | |
| else: | |
| logger.error(' -> [requestHandler]: Failed Blazingfast active: ' + self._sUrl) | |
| try: | |
| cookieJar.save(ignore_discard=self.__bIgnoreDiscard, ignore_expires=self.__bIgnoreExpired) | |
| except Exception as e: | |
| logger.error(' -> [requestHandler]: Failed save cookie: %s' % e) | |
| self._sRealUrl = oResponse.geturl() | |
| self._Status = oResponse.getcode() if self._sUrl == self._sRealUrl else '301' | |
| if self.__bRemoveNewLines: | |
| sContent = sContent.replace('\n', '').replace('\r\t', '') | |
| if self.__bRemoveBreakLines: | |
| sContent = sContent.replace(' ', '') | |
| if self.caching and self.cacheTime > 0 and self.method == 'GET' and self.data is None: | |
| if self.isMemoryCacheActive: | |
| self.__writeVolatileCache(self.getRequestUri(), sContent) | |
| else: | |
| self.__writePersistentCache(self.getRequestUri(), sContent) | |
| return sContent | |
| def __setCookiePath(self): | |
| cookieFile = os.path.join(self._profilePath, 'cookies') | |
| if not os.path.exists(cookieFile): | |
| os.makedirs(cookieFile) | |
| if 'dummy' not in self._sUrl: | |
| cookieFile = os.path.join(cookieFile, urlparse(self._sUrl).netloc.replace('.', '_') + '.txt') | |
| if not os.path.exists(cookieFile): | |
| open(cookieFile, 'w').close() | |
| self._cookiePath = cookieFile | |
| def getCookie(self, sCookieName, sDomain=''): | |
| cookieJar = LWPCookieJar() | |
| try: | |
| cookieJar.load(self._cookiePath, self.__bIgnoreDiscard, self.__bIgnoreExpired) | |
| except Exception as e: | |
| logger.error(e) | |
| for entry in cookieJar: | |
| if entry.name == sCookieName: | |
| if sDomain == '': | |
| return entry | |
| elif entry.domain == sDomain: | |
| return entry | |
| return False | |
| def setCookie(self, oCookie): | |
| cookieJar = LWPCookieJar() | |
| try: | |
| cookieJar.load(self._cookiePath, self.__bIgnoreDiscard, self.__bIgnoreExpired) | |
| cookieJar.set_cookie(oCookie) | |
| cookieJar.save(self._cookiePath, self.__bIgnoreDiscard, self.__bIgnoreExpired) | |
| except Exception as e: | |
| logger.error(e) | |
| def ignoreDiscard(self, bIgnoreDiscard): | |
| self.__bIgnoreDiscard = bIgnoreDiscard | |
| def ignoreExpired(self, bIgnoreExpired): | |
| self.__bIgnoreExpired = bIgnoreExpired | |
| def __doh_request(self, url, doh_server="https://cloudflare-dns.com/dns-query"): | |
| # Parse the URL | |
| parsed_url = urlparse(url) | |
| hostname = parsed_url.hostname | |
| key = 'doh_request' + hostname | |
| if self.isMemoryCacheActive and self.cacheTime > 0: | |
| ip_address = self.__readVolatileCache(key, self.cacheTime) | |
| if ip_address: | |
| return ip_address | |
| params = urlencode({"name": hostname, "type": "A"}) | |
| doh_url = f"{doh_server}?{params}" | |
| req = Request(doh_url) | |
| req.add_header("Accept", "application/dns-json") | |
| try: | |
| response = urlopen(req, timeout=5) | |
| response_text = response.read().decode("utf-8", "replace") | |
| dns_response = json.loads(response_text) | |
| if "Answer" not in dns_response: | |
| raise Exception("Invalid DNS response") | |
| ip_address = dns_response["Answer"][0]["data"] | |
| if self.isMemoryCacheActive and self.cacheTime > 0: | |
| self.__writeVolatileCache(key, ip_address) | |
| return ip_address | |
| except Exception as e: | |
| logger.error(' -> [requestHandler]: DNS query failed: %s' % e) | |
| return None | |
| def __setCachePath(self): | |
| cache = os.path.join(self._profilePath, 'htmlcache') | |
| if not os.path.exists(cache): | |
| os.makedirs(cache) | |
| self._cachePath = cache | |
| def __readPersistentCache(self, url): | |
| h = hashlib.md5(url.encode('utf8')).hexdigest() | |
| cacheFile = os.path.join(self._cachePath, h) | |
| fileAge = self.getFileAge(cacheFile) | |
| if 0 < fileAge < self.cacheTime: | |
| try: | |
| with open(cacheFile, 'rb') as f: | |
| content = f.read().decode('utf8') | |
| except Exception: | |
| logger.error(' -> [requestHandler]: Could not read Cache') | |
| if content: | |
| logger.info(' -> [requestHandler]: read html for %s from cache' % url) | |
| return content | |
| return None | |
| def __writePersistentCache(self, url, content): | |
| try: | |
| h = hashlib.md5(url.encode('utf8')).hexdigest() | |
| with open(os.path.join(self._cachePath, h), 'wb') as f: | |
| f.write(content.encode('utf8')) | |
| except Exception: | |
| logger.error(' -> [requestHandler]: Could not write Cache') | |
| def __writeVolatileCache(self, url, content): | |
| self._memCache.set(hashlib.md5(url.encode('utf8')).hexdigest(), content) | |
| def __readVolatileCache(self, url, cache_time): | |
| entry = self._memCache.get(hashlib.md5(url.encode('utf8')).hexdigest(), cache_time) | |
| if entry: | |
| logger.info('-> [requestHandler]: read html for %s from cache' % url) | |
| return entry | |
| @staticmethod | |
| def getFileAge(cacheFile): | |
| try: | |
| return time.time() - os.stat(cacheFile).st_mtime | |
| except Exception: | |
| return 0 | |
| def clearCache(self): | |
| # clear volatile cache | |
| if self.isMemoryCacheActive: | |
| self._memCache.clear() | |
| cRequestHandler.persistent_openers.clear() | |
| # clear persistent cache | |
| files = os.listdir(self._cachePath) | |
| for file in files: | |
| os.remove(os.path.join(self._cachePath, file)) | |
| xbmcgui.Dialog().notification('xStream', cConfig().getLocalizedString(30405), xbmcgui.NOTIFICATION_INFO, 100, False) | |
| class cBF: | |
| def resolve(self, url, html, cookie_jar, user_agent, sParameters): | |
| page = urlparse(url).scheme + '://' + urlparse(url).netloc | |
| j = re.compile('<script[^>]src="([^"]+)').findall(html) | |
| if j: | |
| opener = build_opener(HTTPCookieProcessor(cookie_jar)) | |
| opener.addheaders = [('User-agent', user_agent), ('Referer', url)] | |
| opener.open(page + j[0]) | |
| a = re.compile(r'xhr\.open\("GET","([^,]+)",').findall(html) | |
| if a: | |
| import random | |
| aespage = page + a[0].replace('" + ww +"', str(random.randint(700, 1500))) | |
| opener = build_opener(HTTPCookieProcessor(cookie_jar)) | |
| opener.addheaders = [('User-agent', user_agent), ('Referer', url)] | |
| html = opener.open(aespage).read().decode('utf-8', 'replace') | |
| cval = self.aes_decode(html) | |
| cdata = re.compile('cookie="([^="]+).*?domain[^>]=([^;]+)').findall(html) | |
| if cval and cdata: | |
| c = Cookie(version=0, name=cdata[0][0], value=cval, port=None, port_specified=False, domain=cdata[0][1], domain_specified=True, domain_initial_dot=False, path="/", path_specified=True, secure=False, expires=time.time() + 21600, discard=False, comment=None, comment_url=None, rest={}) | |
| cookie_jar.set_cookie(c) | |
| opener = build_opener(HTTPCookieProcessor(cookie_jar)) | |
| opener.addheaders = [('User-agent', user_agent), ('Referer', url)] | |
| return opener.open(url, sParameters if len(sParameters) > 0 else None).read().decode('utf-8', 'replace') | |
| @staticmethod | |
| def aes_decode(html): | |
| try: | |
| import pyaes | |
| keys = re.compile(r'toNumbers\("([^"]+)"').findall(html) | |
| if keys: | |
| from binascii import hexlify, unhexlify | |
| msg = unhexlify(keys[2]) | |
| key = unhexlify(keys[0]) | |
| iv = unhexlify(keys[1]) | |
| decrypter = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(key, iv)) | |
| plain_text = decrypter.feed(msg) | |
| plain_text += decrypter.feed() | |
| return hexlify(plain_text).decode() | |
| except Exception as e: | |
| logger.error(e) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| """ | |
| urlresolver XBMC Addon | |
| Copyright (C) 2013 Bstrdsmkr | |
| This program is free software: you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation, either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <http://www.gnu.org/licenses/>. | |
| Adapted for use in xbmc from: | |
| https://github.com/einars/js-beautify/blob/master/python/jsbeautifier/unpackers/packer.py | |
| Unpacker for Dean Edward's p.a.c.k.e.r, a part of javascript beautifier | |
| by Einar Lielmanis <[email protected]> | |
| written by Stefano Sanfilippo <[email protected]> | |
| usage: | |
| if detect(some_string): | |
| unpacked = unpack(some_string) | |
| Unpacker for Dean Edward's p.a.c.k.e.r | |
| """ | |
| import re | |
| import string | |
| def detect(source): | |
| """Detects whether `source` is P.A.C.K.E.R. coded.""" | |
| return source.replace(' ', '').startswith('eval(function(p,a,c,k,e,') | |
| def unpack(source): | |
| """Unpacks P.A.C.K.E.R. packed js code.""" | |
| payload, symtab, radix, count = _filterargs(source) | |
| if count != len(symtab): | |
| raise UnpackingError('Malformed p.a.c.k.e.r. symtab.') | |
| try: | |
| unbase = Unbaser(radix) | |
| except TypeError: | |
| raise UnpackingError('Unknown p.a.c.k.e.r. encoding.') | |
| def lookup(match): | |
| """Look up symbols in the synthetic symtab.""" | |
| word = match.group(0) | |
| return symtab[unbase(word)] or word | |
| source = re.sub(r'\b\w+\b', lookup, payload) | |
| return _replacestrings(source) | |
| def _filterargs(source): | |
| """Juice from a source file the four args needed by decoder.""" | |
| juicers = [ (r"}\('(.*)', *(\d+), *(\d+), *'(.*)'\.split\('\|'\), *(\d+), *(.*)\)\)"), | |
| (r"}\('(.*)', *(\d+), *(\d+), *'(.*)'\.split\('\|'\)"), | |
| ] | |
| for juicer in juicers: | |
| args = re.search(juicer, source, re.DOTALL) | |
| if args: | |
| a = args.groups() | |
| try: | |
| return a[0], a[3].split('|'), int(a[1]), int(a[2]) | |
| except ValueError: | |
| raise UnpackingError('Corrupted p.a.c.k.e.r. data.') | |
| # could not find a satisfying regex | |
| raise UnpackingError('Could not make sense of p.a.c.k.e.r data (unexpected code structure)') | |
| def _replacestrings(source): | |
| """Strip string lookup table (list) and replace values in source.""" | |
| match = re.search(r'var *(_\w+)\=\["(.*?)"\];', source, re.DOTALL) | |
| if match: | |
| varname, strings = match.groups() | |
| startpoint = len(match.group(0)) | |
| lookup = strings.split('","') | |
| variable = '%s[%%d]' % varname | |
| for index, value in enumerate(lookup): | |
| source = source.replace(variable % index, '"%s"' % value) | |
| return source[startpoint:] | |
| return source | |
| class Unbaser(object): | |
| """Functor for a given base. Will efficiently convert | |
| strings to natural numbers.""" | |
| ALPHABET = { | |
| 56: '23456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz', | |
| 59: '0123456789abcdefghijklmnopqrstuvwABCDEFGHIJKLMNOPQRSTUVWXYZ', | |
| 64: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ+/', | |
| 95: (' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ' | |
| r'[\]^_`abcdefghijklmnopqrstuvwxyz{|}~') | |
| } | |
| def __init__(self, base): | |
| self.base = base | |
| # If base can be handled by int() builtin, let it do it for us | |
| if 2 <= base <= 36: | |
| self.unbase = lambda string: int(string, base) | |
| else: | |
| # Build conversion dictionary cache | |
| try: | |
| self.ALPHABET = self.ALPHABET[base] if base in self.ALPHABET else self.ALPHABET[64][0:base] | |
| self.dictionary = dict((cipher, index) for | |
| index, cipher in enumerate(self.ALPHABET)) | |
| except KeyError: | |
| raise TypeError('Unsupported base encoding.') | |
| self.unbase = self._dictunbaser | |
| def __call__(self, string): | |
| return self.unbase(string) | |
| def _dictunbaser(self, string): | |
| """Decodes a value to an integer.""" | |
| ret = 0 | |
| for index, cipher in enumerate(string[::-1]): | |
| ret += (self.base ** index) * self.dictionary[cipher] | |
| return ret | |
| class UnpackingError(Exception): | |
| """Badly packed source or general error. Argument is a | |
| meaningful description.""" | |
| pass | |
| if __name__ == "__main__": | |
| # test = '''eval(function(p,a,c,k,e,d){while(c--)if(k[c])p=p.replace(new RegExp('\\b'+c.toString(a)+'\\b','g'),k[c]);return p}('4(\'30\').2z({2y:\'5://a.8.7/i/z/y/w.2x\',2w:{b:\'2v\',19:\'<p><u><2 d="20" c="#17">2u 19.</2></u><16/><u><2 d="18" c="#15">2t 2s 2r 2q.</2></u></p>\',2p:\'<p><u><2 d="20" c="#17">2o 2n b.</2></u><16/><u><2 d="18" c="#15">2m 2l 2k 2j.</2></u></p>\',},2i:\'2h\',2g:[{14:"11",b:"5://a.8.7/2f/13.12"},{14:"2e",b:"5://a.8.7/2d/13.12"},],2c:"11",2b:[{10:\'2a\',29:\'5://v.8.7/t-m/m.28\'},{10:\'27\'}],26:{\'25-3\':{\'24\':{\'23\':22,\'21\':\'5://a.8.7/i/z/y/\',\'1z\':\'w\',\'1y\':\'1x\'}}},s:\'5://v.8.7/t-m/s/1w.1v\',1u:"1t",1s:"1r",1q:\'1p\',1o:"1n",1m:"1l",1k:\'5\',1j:\'o\',});l e;l k=0;l 6=0;4().1i(9(x){f(6>0)k+=x.r-6;6=x.r;f(q!=0&&k>=q){6=-1;4().1h();4().1g(o);$(\'#1f\').j();$(\'h.g\').j()}});4().1e(9(x){6=-1});4().1d(9(x){n(x)});4().1c(9(){$(\'h.g\').j()});9 n(x){$(\'h.g\').1b();f(e)1a;e=1;}',36,109,'||font||jwplayer|http|p0102895|me|vidto|function|edge3|file|color|size|vvplay|if|video_ad|div||show|tt102895|var|player|doPlay|false||21600|position|skin|test||static|1y7okrqkv4ji||00020|01|type|360p|mp4|video|label|FFFFFF|br|FF0000||deleted|return|hide|onComplete|onPlay|onSeek|play_limit_box|setFullscreen|stop|onTime|dock|provider|391|height|650|width|over|controlbar|5110|duration|uniform|stretching|zip|stormtrooper|213|frequency|prefix||path|true|enabled|preview|timeslidertooltipplugin|plugins|html5|swf|src|flash|modes|hd_default|3bjhohfxpiqwws4phvqtsnolxocychumk274dsnkblz6sfgq6uz6zt77gxia|240p|3bjhohfxpiqwws4phvqtsnolxocychumk274dsnkba36sfgq6uzy3tv2oidq|hd|original|ratio|broken|is|link|Your|such|No|nofile|more|any|availabe|Not|File|OK|previw|jpg|image|setup|flvplayer'.split('|')))''' | |
| # test = '''eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('y.x(A(\'%0%f%b%9%1%d%8%8%o%e%B%c%0%e%d%0%f%w%1%7%3%2%p%d%1%n%2%1%c%0%t%0%f%7%8%8%d%5%6%1%7%e%b%l%7%1%2%e%9%q%c%0%6%1%z%2%0%f%b%1%9%c%0%s%6%6%l%G%4%4%5%5%5%k%b%7%5%8%o%i%2%k%6%i%4%2%3%p%2%n%4%5%7%6%9%s%4%j%q%a%h%a%3%a%E%a%3%D%H%9%K%C%I%m%r%g%h%L%v%g%u%F%r%g%3%J%3%j%3%m%h%4\'));',48,48,'22|72|65|6d|2f|77|74|61|6c|63|4e|73|3d|6f|6e|20|4d|32|76|59|2e|70|51|64|69|62|79|31|68|30|7a|34|66|write|document|75|unescape|67|4f|5a|57|55|3a|44|47|4a|78|49'.split('|'),0,{}))''' | |
| # test = '''eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('x.w(z(\'%1%f%9%b%0%d%7%7%m%e%A%c%1%e%d%1%f%v%0%3%i%2%o%d%0%s%2%0%c%1%q%1%f%3%7%7%d%6%5%0%3%e%9%l%3%0%2%e%b%g%c%1%5%0%y%2%1%f%9%0%b%c%1%r%5%5%l%E%4%4%6%6%6%n%9%3%6%7%m%k%2%n%5%k%4%2%i%o%2%s%4%6%3%5%b%r%4%8%D%h%C%a%F%8%H%B%I%h%i%a%g%8%u%a%q%j%t%j%g%8%t%h%p%j%p%a%G%4\'));',45,45,'72|22|65|61|2f|74|77|6c|5a|73|55|63|3d|6f|6e|20|79|59|6d|4d|76|70|69|2e|62|7a|30|68|64|44|54|66|write|document|75|unescape|67|51|32|6a|3a|35|5f|47|34'.split('|'),0,{}))''' | |
| test = '''eval(function(p,a,c,k,e,d){e=function(c){return(c<a?'':e(parseInt(c/a)))+((c=c%a)>35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--){d[e(c)]=k[c]||e(c)}k=[function(e){return d[e]}];e=function(){return'\\w+'};c=1};while(c--){if(k[c]){p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c])}}return p}('q.r(s(\'%h%t%a%p%u%6%c%n%0%5%l%4%2%4%7%j%0%8%1%o%b%3%7%m%1%8%a%7%b%3%d%6%1%f%0%v%1%5%D%9%0%5%c%g%0%4%A%9%0%f%k%z%2%8%1%C%2%i%d%6%2%3%k%j%2%3%y%e%x%w%g%B%E%F%i%h%e\'));',42,42,'5a|4d|4f|54|6a|44|33|6b|57|7a|56|4e|68|55|3e|47|69|65|6d|32|45|46|31|6f|30|75|document|write|unescape|6e|62|6c|2f|3c|22|79|63|66|78|59|72|61'.split('|'),0,{}))''' | |
| print (unpack(test)) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| import json | |
| import re | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.config import cConfig | |
| from urllib.parse import quote_plus | |
| class cTMDB: | |
| TMDB_GENRES = {12: 'Abenteuer', 14: 'Fantasy', 16: 'Animation', 18: 'Drama', 27: 'Horror', 28: 'Action', 35: 'Komödie', 36: 'Historie', 37: 'Western', 53: 'Thriller', 80: 'Krimi', 99: 'Dokumentarfilm', 878: 'Science Fiction', 9648: 'Mystery', 10402: 'Musik', 10749: 'Liebesfilm', 10751: 'Familie', 10752: 'Kriegsfilm', 10759: 'Action & Adventure', 10762: 'Kids', 10763: 'News', 10764: 'Reality', 10765: 'Sci-Fi & Fantasy', 10766: 'Soap', 10767: 'Talk', 10768: 'War & Politics', 10770: 'TV-Film'} | |
| URL = 'https://api.themoviedb.org/3/' | |
| URL_TRAILER = 'plugin://plugin.video.youtube/play/?video_id=%s' | |
| TMDB_LANGUAGE = cConfig().getSetting('tmdb_lang') | |
| def __init__(self, api_key='', lang=TMDB_LANGUAGE): | |
| self.api_key = '86dd18b04874d9c94afadde7993d94e3' | |
| self.lang = lang | |
| self.poster = 'https://image.tmdb.org/t/p/%s' % cConfig().getSetting('poster_tmdb') | |
| self.fanart = 'https://image.tmdb.org/t/p/%s' % cConfig().getSetting('backdrop_tmdb') | |
| def search_movie_name(self, name, year='', page=1, advanced='false'): | |
| name = re.sub(' +', ' ', name) | |
| if year: | |
| # term = quote_plus(name) + '&year=' + year | |
| name = re.sub(year, ' ', name) #Wenn das Jahr im Namen auftaucht dann das Jahr löschen | |
| #else: | |
| # term = quote_plus(name) | |
| term = quote_plus(name) | |
| meta = self._call('search/movie', 'query=' + term + '&page=' + str(page)) | |
| if 'errors' not in meta and 'status_code' not in meta: | |
| #if 'total_results' in meta and meta['total_results'] == 0 and year: | |
| # meta = self.search_movie_name(name, '', advanced=advanced) | |
| if 'total_results' in meta and meta['total_results'] != 0: | |
| movie = '' | |
| if meta['total_results'] == 1: | |
| movie = meta['results'][0] | |
| else: | |
| for searchMovie in meta['results']: | |
| try: # Exception Handling notwendig da TMDb in seltenen Fällen keine genre_ids mitliefert | |
| if searchMovie['genre_ids'] and 99 not in searchMovie['genre_ids']: | |
| if searchMovie['title'].lower() == name.lower(): | |
| movie = searchMovie | |
| break | |
| except Exception: | |
| break | |
| if not movie: | |
| for searchMovie in meta['results']: | |
| if searchMovie['genre_ids'] and 99 not in searchMovie['genre_ids']: | |
| if year: | |
| if 'release_date' in searchMovie and searchMovie['release_date']: | |
| release_date = searchMovie['release_date'] | |
| yy = release_date[:4] | |
| if int(year) - int(yy) > 1: | |
| continue | |
| movie = searchMovie | |
| break | |
| if not movie: | |
| movie = meta['results'][0] | |
| if advanced == 'true': | |
| tmdb_id = movie['id'] | |
| meta = self.search_movie_id(tmdb_id) | |
| else: | |
| meta = movie | |
| else: | |
| meta = {} | |
| return meta | |
| def search_movie_id(self, movie_id, append_to_response='append_to_response=trailers,credits'): | |
| result = self._call('movie/' + str(movie_id), append_to_response) | |
| result['tmdb_id'] = movie_id | |
| return result | |
| def search_tvshow_name(self, name, year='', page=1, genre='', advanced='false'): | |
| name = name.lower() | |
| if '- staffel' in name: | |
| name = re.sub(r'\s-\s\wtaffel[^>]([1-9\-]+)', '', name) | |
| elif 'staffel' in name: | |
| name = re.sub(r'\s\wtaffel[^>]([1-9\-]+)', '', name) | |
| if year: | |
| # term = quote_plus(name) + '&year=' + year | |
| name = re.sub(year, ' ', name) #Wenn das Jahr im Namen auftaucht dann das Jahr löschen | |
| #else: | |
| # term = quote_plus(name) | |
| term = quote_plus(name) | |
| meta = self._call('search/tv', 'query=' + term + '&page=' + str(page)) | |
| if 'errors' not in meta and 'status_code' not in meta: | |
| #if 'total_results' in meta and meta['total_results'] == 0 and year: | |
| # meta = self.search_tvshow_name(name, '', advanced=advanced) | |
| if 'total_results' in meta and meta['total_results'] != 0: | |
| movie = '' | |
| if meta['total_results'] == 1: | |
| movie = meta['results'][0] | |
| else: | |
| for searchMovie in meta['results']: | |
| if genre == '' or genre in searchMovie['genre_ids']: | |
| movieName = searchMovie['name'] | |
| if movieName.lower() == name.lower(): | |
| movie = searchMovie | |
| break | |
| if not movie: | |
| for searchMovie in meta['results']: | |
| if genre and genre in searchMovie['genre_ids']: | |
| if year: | |
| if 'release_date' in searchMovie and searchMovie['release_date']: | |
| release_date = searchMovie['release_date'] | |
| yy = release_date[:4] | |
| if int(year) - int(yy) > 1: | |
| continue | |
| movie = searchMovie | |
| break | |
| if not movie: | |
| movie = meta['results'][0] | |
| if advanced == 'true': | |
| tmdb_id = movie['id'] | |
| meta = self.search_tvshow_id(tmdb_id) | |
| else: | |
| meta = movie | |
| else: | |
| meta = {} | |
| return meta | |
| def search_tvshow_id(self, show_id, append_to_response='append_to_response=external_ids,videos,credits'): | |
| result = self._call('tv/' + str(show_id), append_to_response) | |
| result['tmdb_id'] = show_id | |
| return result | |
| def get_meta(self, media_type, name, imdb_id='', tmdb_id='', year='', season='', episode='', advanced='false'): | |
| name = re.sub(' +', ' ', name) | |
| meta = {} | |
| if media_type == 'movie': | |
| if tmdb_id: | |
| meta = self.search_movie_id(tmdb_id) | |
| elif name: | |
| meta = self.search_movie_name(name, year, advanced=advanced) | |
| elif media_type == 'tvshow': | |
| if tmdb_id: | |
| meta = self.search_tvshow_id(tmdb_id) | |
| elif name: | |
| meta = self.search_tvshow_name(name, year, advanced=advanced) | |
| if meta and 'id' in meta: | |
| meta = self._format(meta, name) | |
| return meta | |
| def getUrl(self, url, page=1, term=''): | |
| try: | |
| if term: | |
| term = term + '&page=' + str(page) | |
| else: | |
| term = 'page=' + str(page) | |
| result = self._call(url, term) | |
| except Exception: | |
| return False | |
| return result | |
| def _call(self, action, append_to_response=''): | |
| url = '%s%s?language=%s&api_key=%s' % (self.URL, action, self.lang, self.api_key) | |
| if append_to_response: | |
| url += '&%s' % append_to_response | |
| if 'person' in url: | |
| url = url.replace('&page=', '') | |
| oRequestHandler = cRequestHandler(url, ignoreErrors=True) | |
| name = oRequestHandler.request() | |
| try: | |
| data = json.loads(name) | |
| except Exception: | |
| return {} | |
| if 'status_code' in data and data['status_code'] == 34: | |
| return {} | |
| return data | |
| def getGenresFromIDs(self, genresID): | |
| sGenres = [] | |
| for gid in genresID: | |
| genre = self.TMDB_GENRES.get(gid) | |
| if genre: | |
| sGenres.append(genre) | |
| return sGenres | |
| def getLanguage(self, Language): | |
| iso_639 = {'en': 'English', 'de': 'German', 'fr': 'French', 'it': 'Italian', 'nl': 'Nederlands', 'sv': 'Swedish', 'cs': 'Czech', 'da': 'Danish', 'fi': 'Finnish', 'pl': 'Polish', 'es': 'Spanish', 'el': 'Greek', 'tr': 'Turkish', 'uk': 'Ukrainian', 'ru': 'Russian', 'kn': 'Kannada', 'ga': 'Irish', 'hr': 'Croatian', 'hu': 'Hungarian', 'ja': 'Japanese', 'no': 'Norwegian', 'id': 'Indonesian', 'ko': 'Korean', 'pt': 'Portuguese', 'lv': 'Latvian', 'lt': 'Lithuanian', 'ro': 'Romanian', 'sk': 'Slovak', 'sl': 'Slovenian', 'sq': 'Albanian', 'sr': 'Serbian', 'th': 'Thai', 'vi': 'Vietnamese', 'bg': 'Bulgarian', 'fa': 'Persian', 'hy': 'Armenian', 'ka': 'Georgian', 'ar': 'Arabic', 'af': 'Afrikaans', 'bs': 'Bosnian', 'zh': 'Chinese', 'cn': 'Chinese', 'hi': 'Hindi'} | |
| if Language in iso_639: | |
| return iso_639[Language] | |
| else: | |
| return Language | |
| def get_meta_episodes(self, media_type, name, tmdb_id='', season='', episode='', advanced='false'): | |
| meta = {} | |
| if media_type == 'episode' and tmdb_id and season and episode: | |
| url = '%stv/%s/season/%s?api_key=%s&language=de' % (self.URL, tmdb_id, season, self.api_key) | |
| Data = cRequestHandler(url, ignoreErrors=True).request() | |
| if Data: | |
| try: | |
| meta = json.loads(Data) | |
| if 'status_code' in meta and meta['status_code'] == 34: | |
| meta = {} | |
| except Exception: | |
| meta = {} | |
| if 'episodes' in meta: | |
| for e in meta['episodes']: | |
| if 'episode_number': | |
| if e['episode_number'] == int(episode): | |
| return self._format_episodes(e, name) | |
| else: | |
| return {} | |
| def _format_episodes(self, meta, name): | |
| _meta = {} | |
| if 'air_date' in meta and meta['air_date']: | |
| _meta['aired'] = meta['air_date'] | |
| if 'episode_number' in meta and meta['episode_number']: | |
| _meta['episode'] = meta['episode_number'] | |
| if 'name' in meta and meta['name']: | |
| _meta['title'] = meta['name'] | |
| if 'overview' in meta and meta['overview']: | |
| _meta['plot'] = meta['overview'] | |
| if 'production_code' in meta and meta['production_code']: | |
| _meta['code'] = str(meta['production_code']) | |
| if 'season_number' in meta and meta['season_number']: | |
| _meta['season'] = meta['season_number'] | |
| if 'still_path' in meta and meta['still_path']: | |
| _meta['cover_url'] = self.poster + meta['still_path'] | |
| if 'vote_average' in meta and meta['vote_average']: | |
| _meta['rating'] = meta['vote_average'] | |
| if 'vote_count' in meta and meta['vote_count']: | |
| _meta['votes'] = meta['vote_count'] | |
| if 'crew' in meta and meta['crew']: | |
| _meta['writer'] = '' | |
| _meta['director'] = '' | |
| for crew in meta['crew']: | |
| if crew['department'] == 'Directing': | |
| if _meta['director'] != '': | |
| _meta['director'] += ' / ' | |
| _meta['director'] += '%s: %s' % (crew['job'], crew['name']) | |
| elif crew['department'] == 'Writing': | |
| if _meta['writer'] != '': | |
| _meta['writer'] += ' / ' | |
| _meta['writer'] += '%s: %s' % (crew['job'], crew['name']) | |
| if 'guest_stars' in meta and meta['guest_stars']: | |
| licast = [] | |
| for c in meta['guest_stars']: | |
| licast.append((c['name'], c['character'], self.poster + str(c['profile_path']))) | |
| _meta['cast'] = licast | |
| return _meta | |
| def _format(self, meta, name): | |
| _meta = {} | |
| _meta['genre'] = '' | |
| if 'id' in meta: | |
| _meta['tmdb_id'] = meta['id'] | |
| if 'backdrop_path' in meta and meta['backdrop_path']: | |
| _meta['backdrop_url'] = self.fanart + str(meta['backdrop_path']) | |
| if 'original_language' in meta and meta['original_language']: | |
| _meta['country'] = self.getLanguage(meta['original_language']) | |
| if 'original_title' in meta and meta['original_title']: | |
| _meta['originaltitle'] = meta['original_title'] | |
| elif 'original_name' in meta and meta['original_name']: | |
| _meta['originaltitle'] = meta['original_name'] | |
| if 'overview' in meta and meta['overview']: | |
| _meta['plot'] = meta['overview'] | |
| if 'poster_path' in meta and meta['poster_path']: | |
| _meta['cover_url'] = self.poster + str(meta['poster_path']) | |
| if 'release_date' in meta and meta['release_date']: | |
| _meta['premiered'] = meta['release_date'] | |
| elif 'first_air_date' in meta and meta['first_air_date']: | |
| _meta['premiered'] = meta['first_air_date'] | |
| if 'premiered' in _meta and _meta['premiered'] and len(_meta['premiered']) == 10: | |
| _meta['year'] = int(_meta['premiered'][:4]) | |
| if 'budget' in meta and meta['budget']: | |
| _meta['budget'] = '{:,} $'.format(meta['budget']) | |
| if 'revenue' in meta and meta['revenue']: | |
| _meta['revenue'] = '{:,} $'.format(meta['revenue']) | |
| if 'status' in meta and meta['status']: | |
| _meta['status'] = meta['status'] | |
| duration = 0 | |
| if 'runtime' in meta and meta['runtime']: | |
| duration = int(meta['runtime']) | |
| elif 'episode_run_time' in meta and meta['episode_run_time']: | |
| duration = int(meta['episode_run_time'][0]) | |
| if duration > 1: | |
| _meta['duration'] = duration | |
| if 'tagline' in meta and meta['tagline']: | |
| _meta['tagline'] = meta['tagline'] | |
| if 'vote_average' in meta and meta['vote_average']: | |
| _meta['rating'] = meta['vote_average'] | |
| if 'vote_count' in meta and meta['vote_count']: | |
| _meta['votes'] = meta['vote_count'] | |
| if 'genres' in meta and meta['genres']: | |
| for genre in meta['genres']: | |
| if _meta['genre'] == '': | |
| _meta['genre'] += genre['name'] | |
| else: | |
| _meta['genre'] += ' / ' + genre['name'] | |
| elif 'genre_ids' in meta and meta['genre_ids']: | |
| genres = self.getGenresFromIDs(meta['genre_ids']) | |
| for genre in genres: | |
| if _meta['genre'] == '': | |
| _meta['genre'] += genre | |
| else: | |
| _meta['genre'] += ' / ' + genre | |
| if 'production_companies' in meta and meta['production_companies']: | |
| _meta['studio'] = '' | |
| for studio in meta['production_companies']: | |
| if _meta['studio'] == '': | |
| _meta['studio'] += studio['name'] | |
| else: | |
| _meta['studio'] += ' / ' + studio['name'] | |
| if 'credits' in meta and meta['credits']: | |
| strmeta = str(meta['credits']) | |
| listCredits = eval(strmeta) | |
| casts = listCredits['cast'] | |
| crews = [] | |
| if len(casts) > 0: | |
| licast = [] | |
| if 'crew' in listCredits: | |
| crews = listCredits['crew'] | |
| if len(crews) > 0: | |
| _meta['credits'] = "{'cast': " + str(casts) + ", 'crew': " + str(crews) + "}" | |
| for cast in casts: | |
| licast.append((cast['name'], cast['character'], self.poster + str(cast['profile_path']), str(cast['id']))) | |
| _meta['cast'] = licast | |
| else: | |
| _meta['credits'] = "{'cast': " + str(casts) + '}' | |
| if len(crews) > 0: | |
| _meta['writer'] = '' | |
| for crew in crews: | |
| if crew['job'] == 'Director': | |
| _meta['director'] = crew['name'] | |
| elif crew['department'] == 'Writing': | |
| if _meta['writer'] != '': | |
| _meta['writer'] += ' / ' | |
| _meta['writer'] += '%s: %s' % (crew['job'], crew['name']) | |
| elif crew['department'] == 'Production' and 'Producer' in crew['job']: | |
| if _meta['writer'] != '': | |
| _meta['writer'] += ' / ' | |
| _meta['writer'] += '%s: %s' % (crew['job'], crew['name']) | |
| if 'trailers' in meta and meta['trailers']: | |
| if 'youtube' in meta['trailers']: | |
| trailers = '' | |
| for t in meta['trailers']['youtube']: | |
| if t['type'] == 'Trailer': | |
| trailers = self.URL_TRAILER % t['source'] | |
| if trailers: | |
| _meta['trailer'] = trailers | |
| elif 'videos' in meta and meta['videos']: | |
| if 'results' in meta['videos']: | |
| trailers = '' | |
| for t in meta['videos']['results']: | |
| if t['type'] == 'Trailer' and t['site'] == 'YouTube': | |
| trailers = self.URL_TRAILER % t['key'] | |
| if trailers: | |
| _meta['trailer'] = trailers | |
| return _meta |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| import xbmc | |
| import time | |
| import xbmcgui | |
| from resources.lib.config import cConfig | |
| from resources.lib.tmdb import cTMDB | |
| from datetime import date, datetime | |
| from urllib.parse import urlencode | |
| def WindowsBoxes(sTitle, sFileName, metaType, year=''): | |
| try: | |
| meta = cTMDB().get_meta(metaType, sFileName, tmdb_id=xbmc.getInfoLabel('ListItem.Property(TmdbId)'), year=year, advanced='true') | |
| try: | |
| meta['plot'] = str(meta['plot'].encode('latin-1'), 'utf-8') | |
| except Exception: | |
| pass | |
| except Exception: | |
| print("TMDB - error") | |
| pass | |
| if 'tmdb_id' not in meta: | |
| xbmc.executebuiltin("Notification(TMDB, Kein Eintrag gefunden, 1000, '')") | |
| return | |
| if 'premiered' in meta and meta['premiered']: | |
| releaseDate = datetime(*(time.strptime(meta['premiered'], '%Y-%m-%d')[0:6])) | |
| meta['releaseDate'] = releaseDate.strftime('%d/%m/%Y') | |
| else: | |
| meta['releaseDate'] = '-' | |
| if 'duration' in meta and meta['duration']: | |
| duration = meta['duration'] | |
| durationH = duration // 60 | |
| meta['durationH'] = durationH | |
| meta['durationM'] = '{:02d}'.format(int(duration - 60 * durationH)) | |
| else: | |
| meta['durationH'] = 0 | |
| meta['durationM'] = 0 | |
| class XMLDialog(xbmcgui.WindowXMLDialog): | |
| def __init__(self, *args, **kwargs): | |
| xbmcgui.WindowXMLDialog.__init__(self) | |
| pass | |
| def onInit(self): | |
| self.setProperty('color', cConfig().getSetting('Color')) | |
| self.poster = 'https://image.tmdb.org/t/p/%s' % cConfig().getSetting('poster_tmdb') | |
| self.none_poster = 'https://eu.ui-avatars.com/api/?background=000&size=512&name=%s&color=FFF&font-size=0.33' | |
| if 'trailer' in meta: | |
| self.setProperty('isTrailer', 'true') | |
| self.setFocusId(9000) | |
| if 'credits' in meta and meta['credits']: | |
| cast = [] | |
| crew = [] | |
| try: | |
| data = eval(str(meta['credits'].encode('latin-1'), 'utf-8')) | |
| except Exception: | |
| data = eval(str(meta['credits'])) | |
| listitems = [] | |
| if 'cast' in data and data['cast']: | |
| for i in data['cast']: | |
| slabel = i['name'] | |
| slabel2 = i['character'] | |
| if i['profile_path']: | |
| sicon = self.poster + str(i['profile_path']) | |
| else: | |
| sicon = self.none_poster % slabel | |
| sid = i['id'] | |
| listitem_ = xbmcgui.ListItem(label=slabel, label2=slabel2) | |
| listitem_.setProperty('id', str(sid)) | |
| listitem_.setArt({'icon': sicon}) | |
| listitems.append(listitem_) | |
| cast.append(slabel) | |
| self.getControl(50).addItems(listitems) | |
| listitems2 = [] | |
| if 'crew' in data and data['crew']: | |
| for i in data['crew']: | |
| slabel = i['name'] | |
| slabel2 = i['job'] | |
| if i['profile_path']: | |
| sicon = self.poster + str(i['profile_path']) | |
| else: | |
| sicon = self.none_poster % slabel | |
| sid = i['id'] | |
| listitem_ = xbmcgui.ListItem(label=slabel, label2=slabel2) | |
| listitem_.setProperty('id', str(sid)) | |
| listitem_.setArt({'icon': sicon}) | |
| listitems2.append(listitem_) | |
| crew.append(slabel) | |
| self.getControl(5200).addItems(listitems2) | |
| meta['title'] = sTitle | |
| if 'rating' not in meta or meta['rating'] == 0: | |
| meta['rating'] = '-' | |
| if 'votes' not in meta or meta['votes'] == '0': | |
| meta['votes'] = '-' | |
| for prop in meta: | |
| try: | |
| if isinstance(meta[prop], str): | |
| self.setProperty(prop, meta[prop]) | |
| else: | |
| self.setProperty(prop, str(meta[prop])) | |
| except Exception: | |
| if isinstance(meta[prop], str): | |
| self.setProperty(prop, meta[prop]) | |
| else: | |
| self.setProperty(prop, str(meta[prop])) | |
| def credit(self, meta='', control=''): | |
| listitems = [] | |
| if not meta: | |
| meta = {} | |
| for i in meta: | |
| if 'title' in i and i['title']: | |
| sTitle = i['title'] | |
| elif 'name' in i and i['name']: | |
| sTitle = i['name'] | |
| if i['poster_path']: | |
| sThumbnail = self.poster + str(i['poster_path']) | |
| else: | |
| sThumbnail = self.none_poster % sTitle | |
| listitem_ = xbmcgui.ListItem(label=sTitle) | |
| listitem_.setArt({'icon': sThumbnail}) | |
| listitems.append(listitem_) | |
| self.getControl(control).addItems(listitems) | |
| def onClick(self, controlId): | |
| if controlId == 11: | |
| if metaType == 'movie': | |
| sUrl = 'movie/%s/videos' % str(self.getProperty('tmdb_id')) | |
| else: | |
| sUrl = 'tv/%s/videos' % str(self.getProperty('tmdb_id')) | |
| meta = cTMDB().getUrl(sUrl) | |
| name = [] | |
| url = [] | |
| for result in meta['results']: | |
| name.append((result['name'])) | |
| url.append((result['key'])) | |
| index = xbmcgui.Dialog().select('Trailer/Teaser', name) | |
| if index > -1: | |
| self.close() | |
| YT = 'plugin://plugin.video.youtube/play/?video_id=%s' % url[index] | |
| return xbmc.executebuiltin('RunPlugin(%s)' % YT) | |
| elif controlId == 30: | |
| self.close() | |
| return | |
| elif controlId == 50 or controlId == 5200: | |
| item = self.getControl(controlId).getSelectedItem() | |
| sid = item.getProperty('id') | |
| sUrl = 'person/' + str(sid) | |
| try: | |
| meta = cTMDB().getUrl(sUrl, '', "append_to_response=movie_credits,tv_credits") | |
| meta_credits = meta['movie_credits']['cast'] | |
| self.credit(meta_credits, 5215) | |
| sTitle = meta['name'] | |
| if not meta['deathday']: | |
| today = date.today() | |
| try: | |
| birthday = datetime(*(time.strptime(meta['birthday'], '%Y-%m-%d')[0:6])) | |
| age = today.year - birthday.year - ((today.month, today.day) < (birthday.month, birthday.day)) | |
| age = '%s Jahre' % age | |
| except Exception: | |
| age = '' | |
| else: | |
| age = meta['deathday'] | |
| self.setProperty('Person_name', sTitle) | |
| self.setProperty('Person_birthday', meta['birthday']) | |
| self.setProperty('Person_place_of_birth', meta['place_of_birth']) | |
| self.setProperty('Person_deathday', str(age)) | |
| self.setProperty('Person_biography', meta['biography']) | |
| self.setFocusId(9000) | |
| except Exception: | |
| return | |
| self.setProperty('xstream_menu', 'Person') | |
| elif controlId == 9: | |
| sid = self.getProperty('tmdb_id') | |
| if metaType == 'movie': | |
| sUrl_simil = 'movie/%s/similar' % str(sid) | |
| sUrl_recom = 'movie/%s/recommendations' % str(sid) | |
| else: | |
| sUrl_simil = 'tv/%s/similar' % str(sid) | |
| sUrl_recom = 'tv/%s/recommendations' % str(sid) | |
| try: | |
| meta = cTMDB().getUrl(sUrl_simil) | |
| meta = meta['results'] | |
| self.credit(meta, 5205) | |
| except Exception: | |
| pass | |
| try: | |
| meta = cTMDB().getUrl(sUrl_recom) | |
| meta = meta['results'] | |
| self.credit(meta, 5210) | |
| except Exception: | |
| return | |
| elif controlId == 5215 or controlId == 5205 or controlId == 5210: | |
| item = self.getControl(controlId).getSelectedItem() | |
| self.close() | |
| xbmc.executebuiltin("Container.Update(%s?function=searchTMDB&%s)" % ('plugin://plugin.video.xstream/', urlencode({'searchTitle': item.getLabel()}))) | |
| return | |
| def onFocus(self, controlId): | |
| self.controlId = controlId | |
| def _close_dialog(self): | |
| self.close() | |
| def onAction(self, action): | |
| if action.getId() in (104, 105, 1, 2): | |
| return | |
| if action.getId() in (9, 10, 11, 30, 92, 216, 247, 257, 275, 61467, 61448): | |
| self.close() | |
| # kasi | |
| path = 'special://home/addons/%s' % cConfig().getAddonInfo('id') | |
| wd = XMLDialog('info.xml', path, 'default', '720p') | |
| wd.doModal() | |
| del wd |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| import xbmc | |
| import xbmcgui | |
| import hashlib | |
| import re | |
| import os | |
| import time, pyaes | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from xbmcvfs import translatePath | |
| from resources.lib.config import cConfig | |
| from urllib.parse import quote, unquote, quote_plus, unquote_plus, urlparse | |
| from html.entities import name2codepoint | |
| from difflib import SequenceMatcher | |
| from functools import lru_cache | |
| from os import path, chdir | |
| # Aufgeführte Plattformen zum Anzeigen der Systemplattform | |
| def platform(): | |
| if xbmc.getCondVisibility('system.platform.android'): | |
| return 'Android' | |
| elif xbmc.getCondVisibility('system.platform.linux'): | |
| return 'Linux' | |
| elif xbmc.getCondVisibility('system.platform.linux.Raspberrypi'): | |
| return 'Linux/RPi' | |
| elif xbmc.getCondVisibility('system.platform.windows'): | |
| return 'Windows' | |
| elif xbmc.getCondVisibility('system.platform.uwp'): | |
| return 'Windows UWP' | |
| elif xbmc.getCondVisibility('system.platform.osx'): | |
| return 'OSX' | |
| elif xbmc.getCondVisibility('system.platform.atv2'): | |
| return 'ATV2' | |
| elif xbmc.getCondVisibility('system.platform.ios'): | |
| return 'iOS' | |
| elif xbmc.getCondVisibility('system.platform.darwin'): | |
| return 'iOS' | |
| elif xbmc.getCondVisibility('system.platform.xbox'): | |
| return 'XBOX' | |
| elif xbmc.getCondVisibility('System.HasAddon(service.coreelec.settings)'): | |
| return 'CoreElec' | |
| elif xbmc.getCondVisibility('System.HasAddon(service.libreelec.settings)'): | |
| return 'LibreElec' | |
| elif xbmc.getCondVisibility('System.HasAddon(service.osmc.settings)'): | |
| return 'OSMC' | |
| # zeigt nach Update den Changelog als Popup an | |
| def changelog(): | |
| CHANGELOG_PATH = translatePath(os.path.join('special://home/addons/' + cConfig().getAddonInfo('id') + '/', 'changelog.txt')) | |
| version = cConfig().getAddonInfo('version') | |
| if cConfig().getSetting('changelog_version') == version or not os.path.isfile(CHANGELOG_PATH): | |
| return | |
| cConfig().setSetting('changelog_version', version) | |
| heading = cConfig().getLocalizedString(30275) | |
| with open(CHANGELOG_PATH, mode='r', encoding='utf-8') as f: | |
| cl_lines = f.readlines() | |
| announce = '' | |
| for line in cl_lines: | |
| announce += line | |
| textBox(heading, announce) | |
| # zeigt die Entwickler Optionen Warnung als Popup an | |
| def devWarning(): | |
| POPUP_PATH = translatePath(os.path.join('special://home/addons/' + cConfig().getAddonInfo('id') + '/resources/popup', 'devWarning.txt')) | |
| heading = cConfig().getLocalizedString(30322) | |
| with open(POPUP_PATH, mode='r', encoding='utf-8') as f: | |
| cl_lines = f.readlines() | |
| announce = '' | |
| for line in cl_lines: | |
| announce += line | |
| textBox(heading, announce) | |
| # Erstellt eine Textbox | |
| def textBox(heading, announce): | |
| class TextBox(): | |
| def __init__(self, *args, **kwargs): | |
| self.WINDOW = 10147 | |
| self.CONTROL_LABEL = 1 | |
| self.CONTROL_TEXTBOX = 5 | |
| xbmc.executebuiltin("ActivateWindow(%d)" % (self.WINDOW, )) | |
| self.win = xbmcgui.Window(self.WINDOW) | |
| xbmc.sleep(500) | |
| self.setControls() | |
| def setControls(self): | |
| self.win.getControl(self.CONTROL_LABEL).setLabel(heading) | |
| try: | |
| f = open(announce) | |
| text = f.read() | |
| except: | |
| text = announce | |
| self.win.getControl(self.CONTROL_TEXTBOX).setText(str(text)) | |
| return | |
| TextBox() | |
| while xbmc.getCondVisibility('Window.IsVisible(10147)'): | |
| xbmc.sleep(500) | |
| # Info Meldung im Kodi | |
| def infoDialog(message, heading=cConfig().getAddonInfo('name'), icon='', time=5000, sound=False): | |
| if icon == '': icon = cConfig().getAddonInfo('icon') | |
| elif icon == 'INFO': icon = xbmcgui.NOTIFICATION_INFO | |
| elif icon == 'WARNING': icon = xbmcgui.NOTIFICATION_WARNING | |
| elif icon == 'ERROR': icon = xbmcgui.NOTIFICATION_ERROR | |
| xbmcgui.Dialog().notification(heading, message, icon, time, sound=sound) | |
| class cParser: | |
| @staticmethod | |
| def _get_compiled_pattern(pattern, flags=0): | |
| return re.compile(pattern, flags) | |
| @staticmethod | |
| def _replaceSpecialCharacters(s): | |
| try: | |
| # Umlaute Unicode konvertieren | |
| for t in (('\\/', '/'), ('&', '&'), ('\\u00c4', 'Ä'), ('\\u00e4', 'ä'), | |
| ('\\u00d6', 'Ö'), ('\\u00f6', 'ö'), ('\\u00dc', 'Ü'), ('\\u00fc', 'ü'), | |
| ('\\u00df', 'ß'), ('\\u2013', '-'), ('\\u00b2', '²'), ('\\u00b3', '³'), | |
| ('\\u00e9', 'é'), ('\\u2018', '‘'), ('\\u201e', '„'), ('\\u201c', '“'), | |
| ('\\u00c9', 'É'), ('\\u2026', '...'), ('\\u202f', 'h'), ('\\u2019', '’'), | |
| ('\\u0308', '̈'), ('\\u00e8', 'è'), ('#038;', ''), ('\\u00f8', 'ø'), | |
| ('/', '/'), ('\\u00e1', 'á'), ('–', '-'), ('“', '“'), ('„', '„'), | |
| ('’', '’'), ('…', '…'), ('\\u00bc', '¼'), ('\\u00bd', '½'), ('\\u00be', '¾'), | |
| ('\\u2153', '⅓'), ('\\u002A', '*')): | |
| s = s.replace(*t) | |
| # Umlaute HTML konvertieren | |
| for h in (('\\/', '/'), ('&', '&'), (''', "'"), ("'", "'"), | |
| ('Ä', 'Ä'), ('ä', 'ä'), ('Ö', 'Ö'), ('ö', 'ö'), | |
| ('Ü', 'Ü'), ('ü', 'ü'), ('ß', 'ß') , ('²', '²'), | |
| ('Ü', '³'), ('¼', '¼'), ('½', '½'), ('¾', '¾'), | |
| ('⅓', '⅓'), ('∗', '*')): | |
| s = s.replace(*h) | |
| except: | |
| pass | |
| return s | |
| @staticmethod | |
| def parseSingleResult(sHtmlContent, pattern, ignoreCase=False): | |
| if sHtmlContent: | |
| flags = re.S | re.M | |
| if ignoreCase: | |
| flags |= re.I | |
| matches = cParser._get_compiled_pattern(pattern, flags).search(sHtmlContent) | |
| if matches: | |
| # Check if there's at least one capturing group | |
| if matches.lastindex is not None and matches.lastindex >= 1: | |
| return True, cParser._replaceSpecialCharacters(matches.group(1)) | |
| else: | |
| # fallback to the entire match if no group was captured | |
| return True, cParser._replaceSpecialCharacters(matches.group(0)) | |
| return False, None | |
| @staticmethod | |
| def parse(sHtmlContent, pattern, iMinFoundValue=1, ignoreCase=False): | |
| if sHtmlContent: | |
| flags = re.DOTALL | |
| if ignoreCase: | |
| flags |= re.I | |
| aMatches = cParser._get_compiled_pattern(pattern, flags).findall(sHtmlContent) | |
| if len(aMatches) >= iMinFoundValue: | |
| # handle both single strings and tuples of matches | |
| if isinstance(aMatches[0], tuple): | |
| # Process each string in tuple | |
| aMatches = [tuple(cParser._replaceSpecialCharacters(x) if isinstance(x, str) and x is not None else '' for x in match) for match in aMatches] | |
| else: | |
| # Process single strings | |
| aMatches = [cParser._replaceSpecialCharacters(x) if isinstance(x, str) and x is not None else '' for x in aMatches] | |
| return True, aMatches | |
| return False, None | |
| @staticmethod | |
| def replace(pattern, sReplaceString, sValue): | |
| return cParser._get_compiled_pattern(pattern).sub(sReplaceString, sValue) | |
| @staticmethod | |
| def search(pattern, sValue, ignoreCase=True): | |
| flags = 0 | |
| if ignoreCase: | |
| flags = re.IGNORECASE | |
| return cParser._get_compiled_pattern(pattern, flags).search(sValue) | |
| @staticmethod | |
| def escape(sValue): | |
| return re.escape(sValue) | |
| @staticmethod | |
| def getNumberFromString(sValue): | |
| aMatches = re.compile(r'\d+').findall(sValue) | |
| if len(aMatches) > 0: | |
| return int(aMatches[0]) | |
| return 0 | |
| @staticmethod | |
| def urlparse(sUrl): | |
| return urlparse(sUrl.replace('www.', '')).netloc.title() | |
| @staticmethod | |
| def urlDecode(sUrl): | |
| return unquote(sUrl) | |
| @staticmethod | |
| def urlEncode(sUrl, safe=''): | |
| return quote(sUrl, safe) | |
| @staticmethod | |
| def quote(sUrl): | |
| return quote(sUrl) | |
| @staticmethod | |
| def unquotePlus(sUrl): | |
| return unquote_plus(sUrl) | |
| @staticmethod | |
| def quotePlus(sUrl): | |
| return quote_plus(sUrl) | |
| @staticmethod | |
| def B64decode(text): | |
| import base64 | |
| return base64.b64decode(text).decode('utf-8') | |
| # xStream interner Log | |
| class logger: | |
| @staticmethod | |
| def info(sInfo): | |
| logger.__writeLog(sInfo, cLogLevel=xbmc.LOGINFO) | |
| @staticmethod | |
| def debug(sInfo): | |
| logger.__writeLog(sInfo, cLogLevel=xbmc.LOGDEBUG) | |
| @staticmethod | |
| def warning(sInfo): | |
| logger.__writeLog(sInfo, cLogLevel=xbmc.LOGWARNING) | |
| @staticmethod | |
| def error(sInfo): | |
| logger.__writeLog(sInfo, cLogLevel=xbmc.LOGERROR) | |
| @staticmethod | |
| def fatal(sInfo): | |
| logger.__writeLog(sInfo, cLogLevel=xbmc.LOGFATAL) | |
| @staticmethod | |
| def __writeLog(sLog, cLogLevel=xbmc.LOGDEBUG): | |
| params = ParameterHandler() | |
| try: | |
| if params.exist('site'): | |
| site = params.getValue('site') | |
| sLog = "[%s] -> [%s]: %s" % (cConfig().getAddonInfo('name'), site, sLog) | |
| else: | |
| sLog = "[%s] %s" % (cConfig().getAddonInfo('name'), sLog) | |
| xbmc.log(sLog, cLogLevel) | |
| except Exception as e: | |
| xbmc.log('Logging Failure: %s' % e, cLogLevel) | |
| pass | |
| class cUtil: | |
| @staticmethod | |
| def removeHtmlTags(sValue, sReplace=''): | |
| return re.compile(r'<.*?>').sub(sReplace, sValue) | |
| @staticmethod | |
| def unescape(text): | |
| # edit kasi 2024-11-26 so für py2/py3 oder für nur py3 unichr ersetzen durch chr | |
| try: unichr | |
| except NameError: unichr = chr | |
| def fixup(m): | |
| text = m.group(0) | |
| if not text.endswith(';'): text += ';' | |
| if text[:2] == '&#': | |
| try: | |
| if text[:3] == '&#x': | |
| return unichr(int(text[3:-1], 16)) | |
| else: | |
| return unichr(int(text[2:-1])) | |
| except ValueError: | |
| pass | |
| else: | |
| try: | |
| text = unichr(name2codepoint[text[1:-1]]) | |
| except KeyError: | |
| pass | |
| return text | |
| if isinstance(text, str): | |
| try: | |
| text = text.decode('utf-8') | |
| except Exception: | |
| try: | |
| text = text.decode('utf-8', 'ignore') | |
| except Exception: | |
| pass | |
| return re.compile('&(\\w+;|#x?\\d+;?)').sub(fixup, text.strip()) | |
| @staticmethod | |
| def cleanse_text(text): | |
| if text is None: text = '' | |
| text = cUtil.removeHtmlTags(text) | |
| return text | |
| @staticmethod | |
| def evp_decode(cipher_text, passphrase, salt=None): | |
| if not salt: | |
| salt = cipher_text[8:16] | |
| cipher_text = cipher_text[16:] | |
| key, iv = cUtil.evpKDF(passphrase, salt) | |
| decrypter = pyaes.Decrypter(pyaes.AESModeOfOperationCBC(key, iv)) | |
| plain_text = decrypter.feed(cipher_text) | |
| plain_text += decrypter.feed() | |
| return plain_text.decode("utf-8") | |
| @staticmethod | |
| def evpKDF(pwd, salt, key_size=32, iv_size=16): | |
| temp = b'' | |
| fd = temp | |
| while len(fd) < key_size + iv_size: | |
| h = hashlib.md5() | |
| h.update(temp + pwd + salt) | |
| temp = h.digest() | |
| fd += temp | |
| key = fd[0:key_size] | |
| iv = fd[key_size:key_size + iv_size] | |
| return key, iv | |
| @staticmethod | |
| def isSimilar(sSearch, sText, threshold=0.9): | |
| return (SequenceMatcher(None, sSearch, sText).ratio() >= threshold) | |
| @staticmethod | |
| @lru_cache(maxsize=200000) | |
| def get_seq_match_ratio(token1, token2): | |
| return SequenceMatcher(None, token1, token2).ratio() | |
| @staticmethod | |
| def isSimilarByToken(sSearch, sText, threshold=0.9): | |
| tokens_sSearch = sSearch.split() | |
| tokens_sText = sText.split() | |
| if not tokens_sSearch: | |
| return False | |
| # get_ratio = lambda a, b: SequenceMatcher(None, a, b).ratio() | |
| best_ratios = [ | |
| max(cUtil.get_seq_match_ratio(token, token2) for token2 in tokens_sText) | |
| for token in tokens_sSearch | |
| ] | |
| return (sum(best_ratios) / len(best_ratios)) >= threshold | |
| def valid_email(email): #ToDo: Funktion in Settings / Konten aktivieren | |
| # Überprüfen der EMail-Adresse mit dem Muster | |
| if re.compile(r'^[\w\.-]+@[\w\.-]+\.\w+$').match(email): | |
| return True | |
| else: | |
| return False | |
| def getDNS(dns): | |
| status = 'Beschäftigt' | |
| loop = 1 | |
| while status == 'Beschäftigt': | |
| if loop == 20: | |
| break | |
| status = xbmc.getInfoLabel(dns) | |
| xbmc.sleep(20) | |
| loop += 1 | |
| return status | |
| def getRepofromAddonsDB(addonID): | |
| from sqlite3 import dbapi2 as database | |
| from glob import glob | |
| chdir(path.join(translatePath('special://database/'))) | |
| addonsDB = path.join(translatePath('special://database/'), sorted(glob("Addons*.db"), reverse=True)[0]) | |
| dbcon = database.connect(addonsDB) | |
| dbcur = dbcon.cursor() | |
| select = ("SELECT origin FROM installed WHERE addonID = '%s'") % addonID | |
| dbcur.execute(select) | |
| match = dbcur.fetchone() | |
| dbcon.close() | |
| if match and len(match) > 0: | |
| repo = match[0] | |
| else: | |
| repo = '' | |
| return repo | |
| class cCache(object): | |
| _win = None | |
| def __init__(self): | |
| # see https://kodi.wiki/view/Window_IDs | |
| self._win = xbmcgui.Window(10000) | |
| def __del__(self): | |
| del self._win | |
| def get(self, key, cache_time): | |
| cachedata = self._win.getProperty(key) | |
| if cachedata: | |
| cachedata = eval(cachedata) | |
| if time.time() - cachedata[0] < cache_time or cache_time < 0: | |
| return cachedata[1] | |
| else: | |
| self._win.clearProperty(key) | |
| return None | |
| def set(self, key, data): | |
| self._win.setProperty(key, repr((time.time(), data))) | |
| def clear(self): | |
| self._win.clearProperties() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| #Always pay attention to the translations in the menu! | |
| # Sprachauswahl für Hoster enthalten. | |
| # Ajax Suchfunktion enthalten. | |
| # HTML LangzeitCache hinzugefügt | |
| # showValue: 24 Stunden | |
| # showAllSeries: 24 Stunden | |
| # showEpisodes: 4 Stunden | |
| # SSsearch: 24 Stunden | |
| # 2022-12-06 Heptamer - Suchfunktion überarbeitet | |
| import xbmcgui | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser, cUtil | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| SITE_IDENTIFIER = 'aniworld' | |
| SITE_NAME = 'AniWorld' | |
| SITE_ICON = 'aniworld.png' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| # Domain Abfrage | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain') # Domain Auswahl über die xStream Einstellungen möglich | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| URL_MAIN = 'https://' + DOMAIN | |
| # URL_MAIN = 'https://aniworld.to' | |
| URL_SERIES = URL_MAIN + '/animes' | |
| URL_POPULAR = URL_MAIN + '/beliebte-animes' | |
| URL_NEW_EPISODES = URL_MAIN + '/neue-episoden' | |
| URL_LOGIN = URL_MAIN + '/login' | |
| REFERER = 'https://' + DOMAIN | |
| # | |
| def load(): # Menu structure of the site plugin | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| username = cConfig().getSetting('aniworld.user') # Username | |
| password = cConfig().getSetting('aniworld.pass') # Password | |
| if username == '' or password == '': # If no username and password were set, close the plugin! | |
| xbmcgui.Dialog().ok(cConfig().getLocalizedString(30241), cConfig().getLocalizedString(30263)) # Info Dialog! | |
| else: | |
| params.setParam('sUrl', URL_SERIES) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30518), SITE_IDENTIFIER, 'showAllSeries'), params) # All Series | |
| params.setParam('sUrl', URL_NEW_EPISODES) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30516), SITE_IDENTIFIER, 'showNewEpisodes'), params) # New Episodes | |
| params.setParam('sUrl', URL_POPULAR) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30519), SITE_IDENTIFIER, 'showEntries'), params) # Popular Series | |
| params.setParam('sUrl', URL_MAIN) | |
| params.setParam('sCont', 'catalogNav') | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showValue'), params) # From A-Z | |
| params.setParam('sCont', 'homeContentGenresList') | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30506), SITE_IDENTIFIER, 'showValue'), params) # Genre | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params) # Search | |
| cGui().setEndOfDirectory() | |
| def showValue(): | |
| params = ParameterHandler() | |
| sUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 24 # HTML Cache Zeit 1 Tag | |
| sHtmlContent = oRequest.request() | |
| isMatch, sContainer = cParser.parseSingleResult(sHtmlContent, '<ul[^>]*class="%s"[^>]*>(.*?)<\\/ul>' % params.getValue('sCont')) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sContainer, r'<li>\s*<a[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>\s*<\/li>') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| sUrl = sUrl if sUrl.startswith('http') else URL_MAIN + sUrl | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showAllSeries(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False)) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 24 # HTML Cache Zeit 1 Tag | |
| sHtmlContent = oRequest.request() | |
| pattern = '<a[^>]*href="(\\/anime\\/[^"]*)"[^>]*>(.*?)</a>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sUrl, sName in aResult: | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showSeasons') | |
| oGuiElement.setMediaType('tvshow') | |
| params.setParam('sUrl', URL_MAIN + sUrl) | |
| params.setParam('TVShowTitle', sName) | |
| oGui.addFolder(oGuiElement, params, True, total) | |
| if not sGui: | |
| oGui.setView('tvshows') | |
| oGui.setEndOfDirectory() | |
| def showNewEpisodes(entryUrl=False, sGui=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| if not entryUrl: | |
| entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False)) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 4 # HTML Cache Zeit 4 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = r'<div[^>]*class="col-md-[^"]*"[^>]*>\s*<a[^>]*href="([^"]*)"[^>]*>\s*<strong>([^<]+)</strong>\s*<span[^>]*>([^<]+)</span>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sUrl, sName, sInfo in aResult: | |
| sMovieTitle = sName + ' ' + sInfo | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showSeasons') | |
| oGuiElement.setMediaType('tvshow') | |
| oGuiElement.setTitle(sMovieTitle) | |
| params.setParam('sUrl', URL_MAIN + sUrl) | |
| params.setParam('TVShowTitle', sMovieTitle) | |
| oGui.addFolder(oGuiElement, params, True, total) | |
| if not sGui: | |
| oGui.setView('tvshows') | |
| oGui.setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| if not entryUrl: | |
| entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False)) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # HTML Cache Zeit 6 Stunden | |
| sHtmlContent = oRequest.request() | |
| #Aufbau pattern | |
| #'<div[^>]*class="col-md-[^"]*"[^>]*>.*?' # start element | |
| #'<a[^>]*href="([^"]*)"[^>]*>.*?' # url | |
| #'<img[^>]*src="([^"]*)"[^>]*>.*?' # thumbnail | |
| #'<h3>(.*?)<span[^>]*class="paragraph-end">.*?' # title | |
| #'<\\/div>' # end element | |
| pattern = '<div[^>]*class="col-md-[^"]*"[^>]*>.*?<a[^>]*href="([^"]*)"[^>]*>.*?<img[^>]*src="([^"]*)"[^>]*>.*?<h3>(.*?)<span[^>]*class="paragraph-end">.*?</div>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sUrl, sThumbnail, sName in aResult: | |
| if sThumbnail.startswith('/'): | |
| sThumbnail = URL_MAIN + sThumbnail | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showSeasons') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| oGuiElement.setMediaType('tvshow') | |
| params.setParam('sUrl', URL_MAIN + sUrl) | |
| params.setParam('TVShowTitle', sName) | |
| oGui.addFolder(oGuiElement, params, True, total) | |
| if not sGui: | |
| pattern = 'pagination">.*?<a href="([^"]+)">></a>.*?</a></div>' | |
| isMatchNextPage, sNextUrl = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatchNextPage: | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('tvshows') | |
| oGui.setEndOfDirectory() | |
| def showSeasons(): | |
| params = ParameterHandler() | |
| sUrl = params.getValue('sUrl') | |
| sTVShowTitle = params.getValue('TVShowTitle') | |
| oRequest = cRequestHandler(sUrl) | |
| sHtmlContent = oRequest.request() | |
| pattern = '<div[^>]*class="hosterSiteDirectNav"[^>]*>.*?<ul>(.*?)</ul>' | |
| isMatch, sContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| pattern = '<a[^>]*href="([^"]*)"[^>]*title="([^"]*)"[^>]*>(.*?)</a>.*?' | |
| isMatch, aResult = cParser.parse(sContainer, pattern) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| isDesc, sDesc = cParser.parseSingleResult(sHtmlContent, '<p[^>]*data-full-description="(.*?)"[^>]*>') | |
| isThumbnail, sThumbnail = cParser.parseSingleResult(sHtmlContent, '<div[^>]*class="seriesCoverBox"[^>]*>.*?<img[^>]*src="([^"]*)"[^>]*>') | |
| if isThumbnail: | |
| if sThumbnail.startswith('/'): | |
| sThumbnail = URL_MAIN + sThumbnail | |
| total = len(aResult) | |
| for sUrl, sName, sNr in aResult: | |
| isMovie = sUrl.endswith('filme') | |
| if 'Alle Filme' in sName: | |
| sName = 'Filme' | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showEpisodes') | |
| oGuiElement.setMediaType('season' if not isMovie else 'movie') | |
| if isThumbnail: | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if isDesc: | |
| oGuiElement.setDescription(sDesc) | |
| if not isMovie: | |
| oGuiElement.setTVShowTitle(sTVShowTitle) | |
| oGuiElement.setSeason(sNr) | |
| params.setParam('sSeason', sNr) | |
| params.setParam('sThumbnail', sThumbnail) | |
| params.setParam('sUrl', URL_MAIN + sUrl) | |
| cGui().addFolder(oGuiElement, params, True, total) | |
| cGui().setView('seasons') | |
| cGui().setEndOfDirectory() | |
| def showEpisodes(): | |
| params = ParameterHandler() | |
| sUrl = params.getValue('sUrl') | |
| sTVShowTitle = params.getValue('TVShowTitle') | |
| sSeason = params.getValue('sSeason') | |
| sThumbnail = params.getValue('sThumbnail') | |
| if not sSeason: | |
| sSeason = '0' | |
| isMovieList = sUrl.endswith('filme') | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 4 # HTML Cache Zeit 4 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = '<table[^>]*class="seasonEpisodesList"[^>]*>(.*?)</table>' | |
| isMatch, sContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| if isMovieList == True: | |
| pattern = r'<tr[^>]*data-episode-season-id="(\d+).*?<a href="([^"]+)">\s([^<]+).*?<strong>([^<]+)' | |
| isMatch, aResult = cParser.parse(sContainer, pattern) | |
| if not isMatch: | |
| pattern = r'<tr[^>]*data-episode-season-id="(\d+).*?<a href="([^"]+)">\s([^<]+).*?<span>([^<]+)' | |
| isMatch, aResult = cParser.parse(sContainer, pattern) | |
| else: | |
| pattern = r'<tr[^>]*data-episode-season-id="(\d+).*?<a href="([^"]+).*?(?:<strong>(.*?)</strong>.*?)?(?:<span>(.*?)</span>.*?)?<' | |
| isMatch, aResult = cParser.parse(sContainer, pattern) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| isDesc, sDesc = cParser.parseSingleResult(sHtmlContent, '<p[^>]*data-full-description="(.*?)"[^>]*>') | |
| total = len(aResult) | |
| for sID, sUrl2, sNameGer, sNameEng in aResult: | |
| sName = '%d - ' % int(sID) | |
| if isMovieList == True: | |
| sName += sNameGer + '- ' + sNameEng | |
| else: | |
| sName += sNameGer if sNameGer else sNameEng | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showHosters') | |
| oGuiElement.setMediaType('episode' if not isMovieList else 'movie') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if isDesc: | |
| oGuiElement.setDescription(sDesc) | |
| if not isMovieList: | |
| oGuiElement.setSeason(sSeason) | |
| oGuiElement.setEpisode(int(sID)) | |
| oGuiElement.setTVShowTitle(sTVShowTitle) | |
| params.setParam('sUrl', URL_MAIN + sUrl2) | |
| params.setParam('entryUrl', sUrl) | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| cGui().setView('episodes' if not isMovieList else 'movies') | |
| cGui().setEndOfDirectory() | |
| def showHosters(): | |
| hosters = [] | |
| sUrl = ParameterHandler().getValue('sUrl') | |
| sHtmlContent = cRequestHandler(sUrl, caching=False).request() | |
| if cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain') == 'www.aniworld.info': | |
| pattern = r'<li[^>]*episodeLink([^"]+)"\sdata-lang-key="([^"]+).*?data-link-target="([^"]+).*?<h4>([^<]+)<([^>]+)' | |
| pattern2 = r'itemprop="keywords".content=".*?Season...([^"]+).S.*?' # HD Kennzeichen | |
| # data-lang-key="1" Deutsch | |
| # data-lang-key="2" Japanisch mit englischen Untertitel | |
| # data-lang-key="3" Japanisch mit deutschen Untertitel | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| aResult2 = cParser.parse(sHtmlContent, pattern2) # pattern 2 auslesen | |
| if isMatch: | |
| for sID, sLang, sUrl, sName, sQuality in aResult: | |
| # Die Funktion gibt 2 werte zurück! | |
| # element 1 aus array "[0]" True bzw. False | |
| # element 2 aus array "[1]" Name von domain / hoster - wird hier nicht gebraucht! | |
| sUrl = sUrl.replace('/dl/2010', '/redirect/' + sID) | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '1': # Voreingestellte Sprache Deutsch in settings.xml | |
| if '2' in sLang: # data-lang-key="2" Japanisch mit englischen Untertitel | |
| continue | |
| elif '3' in sLang: # data-lang-key="3" Japanisch mit deutschen Untertitel | |
| continue | |
| elif sLang == '1': # data-lang-key="1" Deutsch | |
| sLang = '(DE)' # Anzeige der Sprache Deutsch | |
| if sLanguage == '2': # Voreingestellte Sprache Englisch in settings.xml | |
| cGui().showLanguage() # Kein Eintrag in der ausgewählten Sprache verfügbar | |
| continue | |
| if sLanguage == '3': # Voreingestellte Sprache Japanisch in settings.xml | |
| if '1' in sLang: # data-lang-key="1" Deutsch | |
| continue | |
| elif sLang == '3': # data-lang-key="3" Japanisch mit deutschen Untertitel | |
| sLang = '(JPN) Sub: (DE)' # Anzeige der Sprache Japanisch mit deutschen Untertitel | |
| elif sLang == '2': # data-lang-key="2" Japanisch mit englischen Untertitel | |
| sLang = '(JPN) Sub: (EN)' # Anzeige der Sprache Japanisch mit englischen Untertitel | |
| if sLanguage == '0': # Alle Sprachen | |
| if sLang == '1': # data-lang-key="1" | |
| sLang = '(DE)' # Anzeige der Sprache | |
| elif sLang == '3': # data-lang-key="3" | |
| sLang = '(JPN) Sub: (DE)' # Anzeige der Sprache Japanisch mit deutschen Untertitel | |
| elif sLang == '2': # data-lang-key="2" | |
| sLang = '(JPN) Sub: (EN)' # Anzeige der Sprache Japanisch mit englischen Untertitel | |
| if 'HD' in aResult2[1]: # Prüfen ob tuple aResult2 das Kennzeichen HD enthält, dann übersteuern | |
| sQuality = '720' | |
| else: | |
| sQuality = '480' | |
| # Ab hier wird der sName mit abgefragt z.B: | |
| # aus dem Log [serienstream]: ['/redirect/12286260', 'VOE'] | |
| # hier ist die sUrl = '/redirect/12286260' und der sName 'VOE' | |
| # hoster.py 194 | |
| hoster = {'link': [sUrl, sName], 'name': sName, 'displayedName': '%s [I]%s [%sp][/I]' % (sName, sLang, sQuality), 'quality': sQuality, 'languageCode': sLang} # Language Code für hoster.py Sprache Prio | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| if not hosters: | |
| cGui().showLanguage() | |
| return hosters | |
| else: | |
| pattern = '<li[^>]*data-lang-key="([^"]+).*?data-link-target="([^"]+).*?<h4>([^<]+)<([^>]+)' | |
| pattern2 = 'itemprop="keywords".content=".*?Season...([^"]+).S.*?' # HD Kennzeichen | |
| # data-lang-key="1" Deutsch | |
| # data-lang-key="2" Japanisch mit englischen Untertitel | |
| # data-lang-key="3" Japanisch mit deutschen Untertitel | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| aResult2 = cParser.parse(sHtmlContent, pattern2) # pattern 2 auslesen | |
| if isMatch: | |
| for sLang, sUrl, sName, sQuality in aResult: | |
| # Die Funktion gibt 2 werte zurück! | |
| # element 1 aus array "[0]" True bzw. False | |
| # element 2 aus array "[1]" Name von domain / hoster - wird hier nicht gebraucht! | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '1': # Voreingestellte Sprache Deutsch in settings.xml | |
| if '2' in sLang: # data-lang-key="2" Japanisch mit englischen Untertitel | |
| continue | |
| elif '3' in sLang: # data-lang-key="3" Japanisch mit deutschen Untertitel | |
| continue | |
| elif sLang == '1': # data-lang-key="1" Deutsch | |
| sLang = '(DE)' # Anzeige der Sprache Deutsch | |
| if sLanguage == '2': # Voreingestellte Sprache Englisch in settings.xml | |
| cGui().showLanguage() # Kein Eintrag in der ausgewählten Sprache verfügbar | |
| continue | |
| if sLanguage == '3': # Voreingestellte Sprache Japanisch in settings.xml | |
| if '1' in sLang: # data-lang-key="1" Deutsch | |
| continue | |
| elif sLang == '3': # data-lang-key="3" Japanisch mit deutschen Untertitel | |
| sLang = '(JPN) Sub: (DE)' # Anzeige der Sprache Japanisch mit deutschen Untertitel | |
| elif sLang == '2': # data-lang-key="2" Japanisch mit englischen Untertitel | |
| sLang = '(JPN) Sub: (EN)' # Anzeige der Sprache Japanisch mit englischen Untertitel | |
| if sLanguage == '0': # Alle Sprachen | |
| if sLang == '1': # data-lang-key="1" | |
| sLang = '(DE)' # Anzeige der Sprache | |
| elif sLang == '3': # data-lang-key="3" | |
| sLang = '(JPN) Sub: (DE)' # Anzeige der Sprache Japanisch mit deutschen Untertitel | |
| elif sLang == '2': # data-lang-key="2" | |
| sLang = '(JPN) Sub: (EN)' # Anzeige der Sprache Japanisch mit englischen Untertitel | |
| if 'HD' in aResult2[1]: # Prüfen ob tuple aResult2 das Kennzeichen HD enthält, dann übersteuern | |
| sQuality = '720' | |
| else: | |
| sQuality = '480' | |
| # Ab hier wird der sName mit abgefragt z.B: | |
| # aus dem Log [serienstream]: ['/redirect/12286260', 'VOE'] | |
| # hier ist die sUrl = '/redirect/12286260' und der sName 'VOE' | |
| # hoster.py 194 | |
| hoster = {'link': [sUrl, sName], 'name': sName, 'displayedName': '%s [I]%s [%sp][/I]' % (sName, sLang, sQuality), 'quality': sQuality, 'languageCode': sLang} # Language Code für hoster.py Sprache Prio | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| if not hosters: | |
| cGui().showLanguage() | |
| return hosters | |
| def getHosterUrl(hUrl): | |
| if type(hUrl) == str: hUrl = eval(hUrl) | |
| username = cConfig().getSetting('aniworld.user') | |
| password = cConfig().getSetting('aniworld.pass') | |
| Handler = cRequestHandler(URL_LOGIN, caching=False) | |
| Handler.addHeaderEntry('Upgrade-Insecure-Requests', '1') | |
| Handler.addHeaderEntry('Referer', ParameterHandler().getValue('entryUrl')) | |
| Handler.addParameters('email', username) | |
| Handler.addParameters('password', password) | |
| Handler.request() | |
| Request = cRequestHandler(URL_MAIN + hUrl[0], caching=False) | |
| Request.addHeaderEntry('Referer', ParameterHandler().getValue('entryUrl')) | |
| Request.addHeaderEntry('Upgrade-Insecure-Requests', '1') | |
| Request.request() | |
| sUrl = Request.getRealUrl() | |
| if 'voe' in hUrl[1].lower(): | |
| isBlocked, sDomain = cConfig().isBlockedHoster(sUrl) # Die funktion gibt 2 werte zurück! | |
| if isBlocked: # Voe Pseudo sDomain nicht bekannt in resolveUrl | |
| sUrl = sUrl.replace(sDomain, 'voe.sx') | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| SSsearch(oGui, sSearchText) | |
| def SSsearch(sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| params.getValue('sSearchText') | |
| oRequest = cRequestHandler(URL_SERIES, caching=True, ignoreErrors=(sGui is not False)) | |
| oRequest.addHeaderEntry('X-Requested-With', 'XMLHttpRequest') | |
| oRequest.addHeaderEntry('Referer', REFERER + '/animes') | |
| oRequest.addHeaderEntry('Origin', REFERER) | |
| oRequest.addHeaderEntry('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8') | |
| oRequest.addHeaderEntry('Upgrade-Insecure-Requests', '1') | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 24 # HTML Cache Zeit 1 Tag | |
| sHtmlContent = oRequest.request() | |
| if not sHtmlContent: | |
| return | |
| sst = sSearchText.lower() | |
| pattern = r'<li><a data.+?href="([^"]+)".+?">(.*?)\<\/a><\/l' #link - title | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| if not aResult[0]: | |
| oGui.showInfo() | |
| return | |
| total = len(aResult[1]) | |
| for link, title in aResult[1]: | |
| titleLow = title.lower() | |
| if not sst in titleLow and not cUtil.isSimilarByToken(sst, titleLow): | |
| continue | |
| else: | |
| #get images thumb / descr pro call. (optional) | |
| try: | |
| sThumbnail, sDescription = getMetaInfo(link, title) | |
| oGuiElement = cGuiElement(title, SITE_IDENTIFIER, 'showSeasons') | |
| oGuiElement.setThumbnail(URL_MAIN + sThumbnail) | |
| oGuiElement.setDescription(sDescription) | |
| oGuiElement.setTVShowTitle(title) | |
| oGuiElement.setMediaType('tvshow') | |
| params.setParam('sUrl', URL_MAIN + link) | |
| params.setParam('sName', title) | |
| oGui.addFolder(oGuiElement, params, True, total) | |
| except Exception: | |
| oGuiElement = cGuiElement(title, SITE_IDENTIFIER, 'showSeasons') | |
| oGuiElement.setTVShowTitle(title) | |
| oGuiElement.setMediaType('tvshow') | |
| params.setParam('sUrl', URL_MAIN + link) | |
| params.setParam('sName', title) | |
| oGui.addFolder(oGuiElement, params, True, total) | |
| if not sGui: | |
| oGui.setView('tvshows') | |
| def getMetaInfo(link, title): # Setzen von Metadata in Suche: | |
| oGui = cGui() | |
| oRequest = cRequestHandler(URL_MAIN + link, caching=False) | |
| oRequest.addHeaderEntry('X-Requested-With', 'XMLHttpRequest') | |
| oRequest.addHeaderEntry('Referer', REFERER + '/animes') | |
| oRequest.addHeaderEntry('Origin', REFERER) | |
| #GET CONTENT OF HTML | |
| sHtmlContent = oRequest.request() | |
| if not sHtmlContent: | |
| return | |
| pattern = r'seriesCoverBox">.*?<img src="([^"]+)"\ al.+?data-full-description="([^"]+)"' #img , descr | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| if not aResult[0]: | |
| return | |
| for sImg, sDescr in aResult[1]: | |
| return sImg, sDescr |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Always pay attention to the translations in the menu! | |
| # HTML LangzeitCache hinzugefügt | |
| # showGenre: 48 Stunden | |
| # showEntries: 6 Stunden | |
| # showEpisodes: 4 Stunden | |
| import re | |
| import xbmcgui | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser, cUtil | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| from json import loads | |
| from datetime import datetime | |
| # Globale Variable für die JSON-Daten | |
| apiJson = None | |
| # Domain Abfrage ### | |
| SITE_NAME = 'API Suchmaschine' | |
| SITE_ICON = 'api.png' | |
| SITE_IDENTIFIER = 'api_all' | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'movie4k.sx') | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| ORIGIN = 'https://' + DOMAIN + '/' | |
| REFERER = ORIGIN + '/' | |
| URL_API = 'https://' + DOMAIN | |
| URL_MAIN = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&page=%s' | |
| URL_SEARCH = URL_API + '/data/browse/?lang=%s&order_by=%s&page=%s&limit=0' | |
| URL_THUMBNAIL = 'https://image.tmdb.org/t/p/w300%s' | |
| URL_WATCH = URL_API + '/data/watch/?_id=%s' | |
| URL_GENRE = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&genre=%s&page=%s' | |
| URL_CAST = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&cast=%s&page=%s' | |
| URL_YEAR = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&year=%s&page=%s' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| def load(): | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| # Änderung des Sprachcodes nach voreigestellter Sprache | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| elif sLanguage == '3': # prefLang Japanisch | |
| sLang = cGui().showLanguage() | |
| return | |
| params.setParam('sLanguage', sLang) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502), SITE_IDENTIFIER, 'showMovieMenu'), params) # Movies | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30551), SITE_IDENTIFIER, 'showGenreMMenu'), params) # Movies Genre | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511), SITE_IDENTIFIER, 'showSeriesMenu'), params) # Series | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30529), SITE_IDENTIFIER, 'showGenreSMenu'), params) # Series Genre | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30508), SITE_IDENTIFIER, 'showYearsMenu'), params) # Years | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30553), SITE_IDENTIFIER, 'showSearchActor'), params) # Cast | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params) # Search | |
| cGui().setEndOfDirectory() | |
| def _cleanTitle(sTitle): | |
| sTitle = re.sub("[\xE4]", 'ae', sTitle) | |
| sTitle = re.sub("[\xFC]", 'ue', sTitle) | |
| sTitle = re.sub("[\xF6]", 'oe', sTitle) | |
| sTitle = re.sub("[\xC4]", 'Ae', sTitle) | |
| sTitle = re.sub("[\xDC]", 'Ue', sTitle) | |
| sTitle = re.sub("[\xD6]", 'Oe', sTitle) | |
| sTitle = re.sub("[\x00-\x1F\x80-\xFF]", '', sTitle) | |
| return sTitle | |
| def _getQuality(sQuality): | |
| isMatch, aResult = cParser.parse(sQuality, '(HDCAM|HD|WEB|BLUERAY|BRRIP|DVD|TS|SD|CAM)', 1, True) | |
| if isMatch: | |
| return aResult[0] | |
| else: | |
| return sQuality | |
| def _showGenreMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| sType = params.getValue('sType') | |
| sMenu = params.getValue('sMenu') | |
| if sLanguage == '2' or sLanguage == 'all': | |
| genres = { | |
| 'Action': 'Action', | |
| 'Abenteuer': 'Abenteuer', | |
| 'Animation': 'Animation', | |
| 'Biographie': 'Biographie', | |
| 'Dokumentation': 'Dokumentation', | |
| 'Drama': 'Drama', | |
| 'Familie': 'Familie', | |
| 'Fantasy': 'Fantasy', | |
| 'Geschichte': 'Geschichte', | |
| 'Horror': 'Horror', | |
| 'Komödie': 'Komödie', | |
| 'Krieg': 'Krieg', | |
| 'Krimi': 'Krimi', | |
| 'Musik': 'Musik', | |
| 'Mystery': 'Mystery', | |
| 'Romantik': 'Romantik', | |
| 'Reality-TV': 'Reality-TV', | |
| 'Sci-Fi': 'Sci-Fi', | |
| 'Sports': 'Sport', | |
| 'Thriller': 'Thriller', | |
| 'Western': 'Western' | |
| } | |
| else: | |
| genres = { | |
| 'Action': 'Action', | |
| 'Adventure': 'Abenteuer', | |
| 'Animation': 'Animation', | |
| 'Biography': 'Biographie', | |
| 'Comedy': 'Komödie', | |
| 'Crime': 'Krimi', | |
| 'Documentation': 'Dokumentation', | |
| 'Drama': 'Drama', | |
| 'Family': 'Familie', | |
| 'Fantasy': 'Fantasy', | |
| 'History': 'Geschichte', | |
| 'Horror': 'Horror', | |
| 'Music': 'Musik', | |
| 'Mystery': 'Mystery', | |
| 'Romance': 'Romantik', | |
| 'Reality-TV': 'Reality-TV', | |
| 'Sci-Fi': 'Sci-Fi', | |
| 'Sports': 'Sport', | |
| 'Thriller': 'Thriller', | |
| 'War': 'Krieg', | |
| 'Western': 'Western' | |
| } | |
| for genre, searchGenre in genres.items(): | |
| params.setParam('sUrl', URL_GENRE % (sLanguage, sType, sMenu, searchGenre, '1')) | |
| cGui().addFolder(cGuiElement(genre, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showMovieMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'Trending', '1')) ### Trending Filme trending1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30521), SITE_IDENTIFIER, 'showEntries'), params) ### Trending Filme trending1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'new', '1')) ### neue filme neu1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30541), SITE_IDENTIFIER, 'showEntries'), params) ### neue filme neu1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'views', '1')) ### Views filme views1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30552), SITE_IDENTIFIER, 'showEntries'), params) ### Views filme views1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'rating', '1')) ### Rating Filme rating1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30510), SITE_IDENTIFIER, 'showEntries'), params) ### Rating Filme rating1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'votes', '1')) ### votes filme votes 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30536), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'updates', '1')) ### updates filme updates 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30533), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'name', '1')) ### name filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'featured', '1')) ### featured filme features1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30530), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'requested', '1')) ### requested filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30534), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'releases', '1')) ### releases filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30531), SITE_IDENTIFIER, 'showEntries'), params) ### Filme releases 1 | |
| cGui().setEndOfDirectory() | |
| def showGenreMMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sType', 'movies') | |
| params.setParam('sMenu', 'Trending') | |
| cGui().addFolder(cGuiElement('Genre trending', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Neu') | |
| cGui().addFolder(cGuiElement('Genre new', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Views') | |
| cGui().addFolder(cGuiElement('Genre viewed', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Votes') | |
| cGui().addFolder(cGuiElement('Genre voted', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Updates') | |
| cGui().addFolder(cGuiElement('Genre updated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Rating') | |
| cGui().addFolder(cGuiElement('Genre rated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Name') | |
| cGui().addFolder(cGuiElement('Genre named', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'requested') | |
| cGui().addFolder(cGuiElement('Genre requested', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'featured') | |
| cGui().addFolder(cGuiElement('Genre featured', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'releases') | |
| cGui().addFolder(cGuiElement('Genre released', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| cGui().setEndOfDirectory() | |
| # Serienmenue | |
| def showSeriesMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'neu', '1')) ### serien neu 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30514), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'views', '1')) ### serien views 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30537), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'votes', '1')) ### serien votes 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30519), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'updates', '1')) ### serien updates 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30533), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'name', '1')) ### serien name 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'featured', '1')) ### | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30530), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'requested', '1')) ### serien requested | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30534), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'releases', '1')) ### serien releases 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30531), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'rating', '1')) ### serien rating 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30535), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| #params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'Jahr', '1')) # ## | |
| #cGui().addFolder(cGuiElement('Jahr', SITE_IDENTIFIER, 'showEntries'), params) ## | |
| #params.setParam('sCont', 'Jahr') # | |
| #cGui().addFolder(cGuiElement('Jahr', SITE_IDENTIFIER, 'showValue'), params), params) # | |
| cGui().setEndOfDirectory() | |
| # show genre serien menue | |
| def showGenreSMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sType', 'tvseries') | |
| params.setParam('sMenu', 'Trending') | |
| cGui().addFolder(cGuiElement('Series genre trending', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Neu') | |
| cGui().addFolder(cGuiElement('Series genre new', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Views') | |
| cGui().addFolder(cGuiElement('Series genre viewed', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Votes') | |
| cGui().addFolder(cGuiElement('Series genre voted', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Updates') | |
| cGui().addFolder(cGuiElement('Series genre updated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Rating') | |
| cGui().addFolder(cGuiElement('Series genre rated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Name') | |
| cGui().addFolder(cGuiElement('Series genre named', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'requested') | |
| cGui().addFolder(cGuiElement('Series genre requested', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'featured') | |
| cGui().addFolder(cGuiElement('Series genre featured', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'releases') | |
| cGui().addFolder(cGuiElement('Series genre releases', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| cGui().setEndOfDirectory() | |
| def showYearsMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| # Anfangs- und Endjahr für das menü eintragen | |
| start_jahr = 1931 | |
| end_jahr = datetime.now().year | |
| # show the current year first | |
| for jahr in range(end_jahr, start_jahr - 1, -1): | |
| params.setParam('sUrl', URL_YEAR % (sLanguage, 'movies', 'new', str(jahr), '1')) | |
| cGui().addFolder(cGuiElement(str(jahr), SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| isTvshow = False | |
| sThumbnail = '' | |
| sLanguage = params.getValue('sLanguage') | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| try: | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # HTML Cache Zeit 6 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| aJson = loads(sJson) | |
| except: | |
| if not sGui: oGui.showInfo() | |
| return | |
| if 'movies' not in aJson or not isinstance(aJson.get('movies'), list) or len(aJson['movies']) == 0: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = 0 | |
| # ignore movies which does not contain any streams | |
| for movie in aJson['movies']: | |
| if '_id' in movie: | |
| total += 1 | |
| for movie in aJson['movies']: | |
| if not '_id' in movie: | |
| continue | |
| sTitle = str(movie['title']) | |
| if sSearchText and not cParser.search(sSearchText, sTitle): | |
| continue | |
| if 'Staffel' in sTitle or 'Season' in sTitle: | |
| isTvshow = True | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'showEpisodes' if isTvshow else 'showHosters') | |
| if 'poster_path_season' in movie and movie['poster_path_season']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path_season']) | |
| elif 'poster_path' in movie and movie['poster_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path']) | |
| elif 'backdrop_path' in movie and movie['backdrop_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['backdrop_path']) | |
| if sThumbnail: | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 'storyline' in movie: | |
| oGuiElement.setDescription(str(movie['storyline'])) | |
| elif 'overview' in movie: | |
| oGuiElement.setDescription(str(movie['overview'])) | |
| if 'year' in movie and len(str(movie['year'])) == 4: | |
| oGuiElement.setYear(movie['year']) | |
| if 'quality' in movie: | |
| oGuiElement.setQuality(_getQuality(movie['quality'])) | |
| if 'rating' in movie: | |
| oGuiElement.addItemValue('rating', movie['rating']) | |
| if 'lang' in movie: | |
| if (sLanguage != '1' and movie['lang'] == 2): # Deutsch | |
| oGuiElement.setLanguage('DE') | |
| if (sLanguage != '2' and movie['lang'] == 3): # Englisch | |
| oGuiElement.setLanguage('EN') | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| if 'runtime' in movie: | |
| isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], '\d+') | |
| if isMatch: | |
| oGuiElement.addItemValue('duration', sRuntime) | |
| params.setParam('entryUrl', URL_WATCH % str(movie['_id'])) | |
| params.setParam('sName', sTitle) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui and not sSearchText: | |
| curPage = aJson['pager']['currentPage'] | |
| if curPage < aJson['pager']['totalPages']: | |
| sNextUrl = entryUrl.replace('page=' + str(curPage), 'page=' + str(curPage + 1)) | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('tvshows' if isTvshow else 'movies') | |
| oGui.setEndOfDirectory() | |
| def showEpisodes(): | |
| aEpisodes = [] | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue("sThumbnail") | |
| try: | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 4 # HTML Cache Zeit 4 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| aJson = loads(sJson) | |
| except: | |
| cGui().showInfo() | |
| return | |
| if 'streams' not in aJson or len(aJson['streams']) == 0: | |
| cGui().showInfo() | |
| return | |
| for stream in aJson['streams']: | |
| if 'e' in stream: | |
| aEpisodes.append(int(stream['e'])) | |
| if aEpisodes: | |
| aEpisodesSorted = set(aEpisodes) | |
| total = len(aEpisodesSorted) | |
| for sEpisode in aEpisodesSorted: | |
| oGuiElement = cGuiElement('Episode ' + str(sEpisode), SITE_IDENTIFIER, 'showHosters') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 's' in aJson: | |
| oGuiElement.setSeason(aJson['s']) | |
| oGuiElement.setTVShowTitle('Episode ' + str(sEpisode)) | |
| oGuiElement.setEpisode(sEpisode) | |
| oGuiElement.setMediaType('episode') | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| cGui().setView('episodes') | |
| cGui().setEndOfDirectory() | |
| def showHosters(): | |
| hosters = [] | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sEpisode = params.getValue('episode') | |
| try: | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 8 # HTML Cache Zeit 8 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| except: | |
| return hosters | |
| if sJson: | |
| aJson = loads(sJson) | |
| if 'streams' in aJson: | |
| i = 0 | |
| for stream in aJson['streams']: | |
| if (('e' not in stream) or (str(sEpisode) == str(stream['e']))): | |
| sHoster = str(i) + ':' | |
| isMatch, aName = cParser.parse(stream['stream'], '//([^/]+)/') | |
| if isMatch: | |
| # sName = cParser.urlparse(sUrl) ### angezeigter hostername api | |
| sName = aName[0][:aName[0].rindex('.')] | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| sHoster = sHoster + ' ' + sName | |
| if 'release' in stream and str(stream['release']) != '': | |
| sHoster = sHoster + ' [I][' + _getQuality(stream['release']) + '][/I]' | |
| hoster = {'link': stream['stream'], 'name': sHoster} | |
| hosters.append(hoster) | |
| i += 1 | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearchActor(): | |
| sName = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30280)) | |
| if not sName: return | |
| _searchActor(False, sName) | |
| cGui().setEndOfDirectory() | |
| def _searchActor(oGui, sName): | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| showEntries(URL_CAST % (sLanguage, 'movies', 'new', cParser.quotePlus(sName), '1'), oGui) | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| SSsearch(oGui, sSearchText) | |
| def SSsearch(sGui=False, sSearchText=False): | |
| global apiJson | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| # Falls die Daten noch nicht geladen wurden oder neu geladen werden sollen | |
| if apiJson is None or 'movies' not in apiJson: | |
| loadMoviesData() | |
| if 'movies' not in apiJson or not isinstance(apiJson.get('movies'), list) or len(apiJson['movies']) == 0: | |
| oGui.showInfo() | |
| return | |
| sst = sSearchText.lower() | |
| if not sGui: | |
| dialog = xbmcgui.DialogProgress() | |
| dialog.create(cConfig().getLocalizedString(30122), cConfig().getLocalizedString(30123)) | |
| total = len(apiJson['movies']) | |
| position = 0 | |
| for movie in apiJson['movies']: | |
| position += 1 | |
| if not '_id' in movie: | |
| continue | |
| if not sGui and position % 128 == 0: # Update progress every 128 items | |
| if dialog.iscanceled(): break | |
| dialog.update(position, str(position) + cConfig().getLocalizedString(30128) + str(total)) | |
| sTitle = movie['title'] | |
| if 'Staffel' in sTitle or 'Season' in sTitle: | |
| isTvshow = True | |
| sSearch = sTitle.rsplit('-', 1)[0].replace(' ', '').lower() | |
| else: | |
| isTvshow = False | |
| sSearch = sTitle.lower() | |
| if not sst in sSearch and not cUtil.isSimilarByToken(sst, sSearch): | |
| continue | |
| #logger.info('-> [DEBUG]: %s' % str(movie)) | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'showEpisodes' if isTvshow else 'showHosters') | |
| sThumbnail = '' | |
| if 'poster_path_season' in movie and movie['poster_path_season']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path_season']) | |
| elif 'poster_path' in movie and movie['poster_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path']) | |
| elif 'backdrop_path' in movie and movie['backdrop_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['backdrop_path']) | |
| if sThumbnail: | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 'storyline' in movie: | |
| oGuiElement.setDescription(str(movie['storyline'])) | |
| elif 'overview' in movie: | |
| oGuiElement.setDescription(str(movie['overview'])) | |
| if 'year' in movie and len(str(movie['year'])) == 4: | |
| oGuiElement.setYear(movie['year']) | |
| if 'quality' in movie: | |
| oGuiElement.setQuality(_getQuality(movie['quality'])) | |
| if 'rating' in movie: | |
| oGuiElement.addItemValue('rating', movie['rating']) | |
| if 'lang' in movie: | |
| if (sLanguage != '1' and movie['lang'] == 2): # Deutsch | |
| oGuiElement.setLanguage('DE') | |
| if (sLanguage != '2' and movie['lang'] == 3): # Englisch | |
| oGuiElement.setLanguage('EN') | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| if 'runtime' in movie: | |
| isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], '\d+') | |
| if isMatch: | |
| oGuiElement.addItemValue('duration', sRuntime) | |
| params.setParam('entryUrl', URL_WATCH % str(movie['_id'])) | |
| params.setParam('sName', sTitle) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui: | |
| dialog.close() | |
| def loadMoviesData(): | |
| global apiJson | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| try: | |
| oRequest = cRequestHandler(URL_SEARCH % (sLang, 'new', '1'), caching=True) | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| oRequest.cacheTime = 60 * 60 * 48 # HTML Cache Zeit 2 Tage | |
| sJson = oRequest.request() | |
| apiJson = loads(sJson) | |
| logger.info('API-Daten erfolgreich geladen') | |
| except: | |
| logger.error('Fehler beim Laden der API-Daten') | |
| apiJson = {'movies': []} | |
| # Daten werden lazy beim ersten Zugriff geladen (siehe SSsearch) | |
| # loadMoviesData() - entfernt: beschleunigt den Import/Start erheblich |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| # Always pay attention to the translations in the menu! | |
| # Multi Scraper für Dokumentationen | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| from resources.lib import youtube_fix | |
| import sys, xbmcplugin, xbmc | |
| # | |
| SITE_IDENTIFIER = 'dokus' | |
| SITE_NAME = cConfig().getLocalizedString(30505) | |
| SITE_ICON = 'dokus.png' | |
| SITE_GLOBAL_SEARCH = False | |
| cConfig().setSetting('global_search_' + SITE_IDENTIFIER, 'false') | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| #################### Hauptmenü #################### | |
| def load(): # Menu structure of the site plugin | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| # Abfrage ob Youtube installiert ist | |
| if cConfig().getSetting('plugin_' + SITE_IDENTIFIER) == 'true': | |
| if not xbmc.getCondVisibility('System.HasAddon(%s)' % 'plugin.video.youtube'): | |
| xbmc.executebuiltin('InstallAddon(%s)' % 'plugin.video.youtube') | |
| # Menü für Dokus4.me | |
| logger.info('Load %s' % SITE_NAME_1) | |
| params.setParam('sUrl', URL_MAIN_1) | |
| cGui().addFolder(cGuiElement('[B]' + SITE_NAME_1 + ': [/B]' + cConfig().getLocalizedString(30505), SITE_IDENTIFIER, 'showEntries_1'), params) # Documentations | |
| cGui().addFolder(cGuiElement('[B]' + SITE_NAME_1 + ': [/B]' + cConfig().getLocalizedString(30506), SITE_IDENTIFIER, 'showGenre_1'), params) # Genre | |
| cGui().addFolder(cGuiElement('[B]' + SITE_NAME_1 + ': [/B]' + cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch_1'), params) # Search | |
| # Menü für DokusStreams.de | |
| logger.info('Load %s' % SITE_NAME_2) | |
| params.setParam('sUrl', URL_MAIN_2) | |
| cGui().addFolder(cGuiElement('[B]' + SITE_NAME_2 + ': [/B]' + cConfig().getLocalizedString(30505), SITE_IDENTIFIER, 'showEntries_2'), params) # Documentations | |
| cGui().addFolder(cGuiElement('[B]' + SITE_NAME_2 + ': [/B]' + cConfig().getLocalizedString(30506), SITE_IDENTIFIER, 'showGenre_2'), params) # Genre | |
| cGui().addFolder(cGuiElement('[B]' + SITE_NAME_2 + ': [/B]' + cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch_2'), params) # Search | |
| # Menü für Dokuh.de | |
| logger.info('Load %s' % SITE_NAME_3) | |
| params.setParam('sUrl', URL_MAIN_3) | |
| cGui().addFolder(cGuiElement('[B]' + SITE_NAME_3 + ': [/B]' + cConfig().getLocalizedString(30505), SITE_IDENTIFIER, 'showEntries_3'), params) # Documentations | |
| cGui().addFolder(cGuiElement('[B]' + SITE_NAME_3 + ': [/B]' + cConfig().getLocalizedString(30506), SITE_IDENTIFIER, 'showGenre_3'), params) # Genre | |
| cGui().addFolder(cGuiElement('[B]' + SITE_NAME_3 + ': [/B]' + cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch_3'), params) # Search | |
| # Menü für Videogold.de | |
| logger.info('Load %s' % SITE_NAME_6) | |
| params.setParam('sUrl', URL_MAIN_6) | |
| cGui().addFolder(cGuiElement('[B]' + SITE_NAME_6 + ': [/B]' + cConfig().getLocalizedString(30505), SITE_IDENTIFIER, 'showDoku_6'), params) # Doku | |
| cGui().addFolder(cGuiElement('[B]' + SITE_NAME_6 + ': [/B]' + cConfig().getLocalizedString(30506), SITE_IDENTIFIER, 'showThemen_6'), params) # Themen | |
| cGui().addFolder(cGuiElement('[B]' + SITE_NAME_6 + ': [/B]' + cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch_6'), params) # Search | |
| cGui().addFolder(cGuiElement('[B]YouTube:[/B] Kanäle', SITE_IDENTIFIER, 'showYTChannels'), params) | |
| cGui().addFolder(cGuiElement('[B]YouTube:[/B] Genre', SITE_IDENTIFIER, 'showYTGenre'), params) | |
| cGui().setEndOfDirectory() | |
| #################### Dokus4.me #################### | |
| SITE_NAME_1 = 'Dokus4.me' | |
| SITE_ICON_1 = 'dokus4.png' | |
| URL_MAIN_1= 'http://www.dokus4.me/' | |
| URL_SEARCH_1 = URL_MAIN_1 + '?s=%s' | |
| def showGenre_1(): | |
| params = ParameterHandler() | |
| entryUrl = params.getValue('sUrl') | |
| sHtmlContent = cRequestHandler(entryUrl).request() | |
| pattern = 'cat-item.*?href="([^"]+)">([^<]+)</a>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries_1'), params) | |
| cGui().setEndOfDirectory() | |
| def showEntries_1(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| sHtmlContent = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False)).request() | |
| pattern = 'tbl_titel.*?title="([^"]+).*?href="([^"]+).*?src="([^"]+).*?vid_desc">([^<]+)' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sName, sUrl, sThumbnail, sDesc in aResult: | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showHosters_1') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| oGuiElement.setDescription(sDesc) | |
| params.setParam('entryUrl', sUrl) | |
| oGui.addFolder(oGuiElement, params, False, total) | |
| if not sGui: | |
| isMatchNextPage, sNextUrl = cParser.parseSingleResult(sHtmlContent, 'rel="next" href="([^"]+)') | |
| if isMatchNextPage: | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries_1', params) | |
| oGui.setView('movies') | |
| oGui.setEndOfDirectory() | |
| def showHosters_1(): | |
| hosters = [] | |
| sUrl = ParameterHandler().getValue('entryUrl') | |
| sHtmlContent = cRequestHandler(sUrl, caching=False).request() | |
| isMatch, aResult = cParser.parse(sHtmlContent, 'src="([^"]+)" f') | |
| if isMatch: | |
| for sUrl in aResult: | |
| hoster = {'link': sUrl, 'name': cParser.urlparse(sUrl)} | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl_1') | |
| return hosters | |
| def getHosterUrl_1(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearch_1(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30289)) | |
| if not sSearchText: return | |
| _search_1(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search_1(oGui, sSearchText): | |
| showEntries_1(URL_SEARCH_1 % cParser.quotePlus(sSearchText), oGui, sSearchText) | |
| #################### Dokustreams #################### | |
| SITE_NAME_2 = 'Dokustreams.de' | |
| SITE_ICON_2 = 'Dokustreams.png' | |
| URL_MAIN_2= 'http://dokustreams.de/' | |
| URL_SEARCH_2 = URL_MAIN_2 + '?s=%s' | |
| def showGenre_2(): | |
| params = ParameterHandler() | |
| oRequest = cRequestHandler(URL_MAIN_2) | |
| sHtmlContent = oRequest.request() | |
| pattern = 'Themen.*?<ul class="sub-menu">(.*?)</ul>' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'href="([^"]+).*?>([^<]+)') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| if sUrl.startswith('/'): | |
| sUrl = URL_MAIN_2 + sUrl | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries_2'), params) | |
| cGui().setEndOfDirectory() | |
| def showEntries_2(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| isTvshow = True | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| iPage = int(params.getValue('page')) | |
| oRequest = cRequestHandler(entryUrl + 'page/' + str(iPage) if iPage > 0 else entryUrl, ignoreErrors=(sGui is not False)) | |
| sHtmlContent = oRequest.request() | |
| pattern = '<article id="post-.*?href="([^"]+).*?<img.*?src="([^"]+).*?>([^<]+)</a></h2>.*?<p>([^<]+)' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sUrl, sThumbnail, sName, sDesc in aResult: | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showEpisodes_2') | |
| oGuiElement.setMediaType('tvshow') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| oGuiElement.setDescription(sDesc) | |
| oGuiElement.setMediaType('season') | |
| params.setParam('entryUrl', sUrl) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui and not sSearchText: | |
| sPageNr = int(params.getValue('page')) | |
| if sPageNr == 0: | |
| sPageNr = 2 | |
| else: | |
| sPageNr += 1 | |
| params.setParam('page', int(sPageNr)) | |
| params.setParam('sUrl', entryUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries_2', params) | |
| oGui.setView('tvshows') | |
| oGui.setEndOfDirectory() | |
| def showEpisodes_2(sGui = False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| # Parameter laden | |
| sUrl = params.getValue('entryUrl') | |
| oRequest = cRequestHandler(sUrl) | |
| sHtmlContent = oRequest.request() | |
| pattern = 'yotu-videos.*?<ul>(.*?)</ul>' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'data-videoid="([^"]+).*?data-title="([^"]+).*?src="([^"]+).*?') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| total = len(aResult) | |
| for sId, sName, sThumbnail in aResult: | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showHosters_2') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| oGuiElement.setMediaType('episode') | |
| params.setParam('sId', sId) | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| # Nächste Seite muss noch gebaut werden | |
| if not sGui: | |
| isMatchNextPage, sNextUrl = cParser.parseSingleResult(sHtmlContent, 'rel="next" href="([^"]+)') | |
| if isMatchNextPage: | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEpisodes_2', params) | |
| cGui().setView('episodes') | |
| oGui.setEndOfDirectory() | |
| def showHosters_2(): | |
| hosters = [] | |
| sId = ParameterHandler().getValue('sId') | |
| sUrl = 'https://www.youtube.com/watch?v=' + sId | |
| hoster = {'link': sUrl, 'name': cParser.urlparse(sUrl)} | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl_2') | |
| return hosters | |
| def getHosterUrl_2(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearch_2(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30289)) | |
| if not sSearchText: return | |
| _search_2(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search_2(oGui, sSearchText): | |
| showEntries_2(URL_SEARCH_2 % cParser.quotePlus(sSearchText), oGui, sSearchText) | |
| #################### Dokuh #################### | |
| SITE_NAME_3 = 'Dokuh.de' | |
| SITE_ICON_3 = 'Dokuh.png' | |
| URL_MAIN_3= 'http://www.dokuh.de/' | |
| URL_SEARCH_3 = URL_MAIN_3 + '?s=%s' | |
| def showGenre_3(): | |
| params = ParameterHandler() | |
| oRequest = cRequestHandler(URL_MAIN_3) | |
| sHtmlContent = oRequest.request() | |
| pattern = '>Kategorien(.*?)</a></li></ul></li></ul>' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'href="([^"]+).*?>([^<]+)') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| if sUrl.startswith('/'): | |
| sUrl = URL_MAIN_3 + sUrl | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries_3'), params) | |
| cGui().setEndOfDirectory() | |
| def showEntries_3(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| iPage = int(params.getValue('page')) | |
| oRequest = cRequestHandler(entryUrl + 'page/' + str(iPage) if iPage > 0 else entryUrl, ignoreErrors=(sGui is not False)) | |
| sHtmlContent = oRequest.request() | |
| pattern = 'class="item-thumbnail">.*?href="([^"]+).*?title="([^"]+).*?src="([^"]+).*?' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sUrl, sName, sThumbnail in aResult: | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showHosters_3') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| params.setParam('entryUrl', sUrl) | |
| oGui.addFolder(oGuiElement, params, False, total) | |
| if not sGui and not sSearchText: | |
| sPageNr = int(params.getValue('page')) | |
| if sPageNr == 0: | |
| sPageNr = 2 | |
| else: | |
| sPageNr += 1 | |
| params.setParam('page', int(sPageNr)) | |
| params.setParam('sUrl', entryUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries_3', params) | |
| oGui.setView('movies') | |
| oGui.setEndOfDirectory() | |
| def showHosters_3(): | |
| hosters = [] | |
| sUrl = ParameterHandler().getValue('entryUrl') | |
| sHtmlContent = cRequestHandler(sUrl, caching=False).request() | |
| isMatch, aResult = cParser.parse(sHtmlContent, 'src="([^"]+)" f') | |
| if isMatch: | |
| for sUrl in aResult: | |
| hoster = {'link': sUrl, 'name': cParser.urlparse(sUrl)} | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl_3') | |
| return hosters | |
| def getHosterUrl_3(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearch_3(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30289)) | |
| if not sSearchText: return | |
| _search_3(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search_3(oGui, sSearchText): | |
| showEntries_3(URL_SEARCH_3 % cParser.quotePlus(sSearchText), oGui, sSearchText) | |
| #################### VideoGold #################### | |
| SITE_NAME_6 = 'VideoGold' | |
| SITE_ICON_6 = 'videogold.png' | |
| URL_MAIN_6= 'http://videogold.de/' | |
| URL_SEARCH_6 = URL_MAIN_6 + '?s=%s' | |
| def showDoku_6(): | |
| params = ParameterHandler() | |
| oRequest = cRequestHandler(URL_MAIN_6) | |
| sHtmlContent = oRequest.request() | |
| pattern = 'Formate</a>(.*?)Themen' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'href="([^"]+).*?>([^<]+)') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| if sUrl.startswith('/'): | |
| sUrl = URL_MAIN_6 + sUrl | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries_6'), params) | |
| cGui().setEndOfDirectory() | |
| def showThemen_6(): | |
| params = ParameterHandler() | |
| oRequest = cRequestHandler(URL_MAIN_6) | |
| sHtmlContent = oRequest.request() | |
| pattern = r'Themen</a>(.*?)class="\swp-block-navigation-item\shas-child open-on-hover-click\swp-block-navigation-submenu">' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'href="([^"]+).*?>([^<]+)') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| if sUrl.startswith('/'): | |
| sUrl = URL_MAIN_6 + sUrl | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries_6'), params) | |
| cGui().setEndOfDirectory() | |
| def showEntries_6(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| iPage = int(params.getValue('page')) | |
| oRequest = cRequestHandler(entryUrl + 'seite/' + str(iPage) if iPage > 0 else entryUrl, ignoreErrors=(sGui is not False)) | |
| sHtmlContent = oRequest.request() | |
| pattern = r'<li\sclass="wp-block-post.*?href="([^"]+).*?alt="([^"]+).*?data-src="([^"]+).*?' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sUrl, sName, sThumbnail in aResult: | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showHosters_6') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| oGuiElement.setMediaType('movie') | |
| params.setParam('entryUrl', sUrl) | |
| params.setParam('sName', sName) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, False, total) | |
| if not sGui and not sSearchText: | |
| sPageNr = int(params.getValue('page')) | |
| if sPageNr == 0: | |
| sPageNr = 2 | |
| else: | |
| sPageNr += 1 | |
| params.setParam('page', int(sPageNr)) | |
| params.setParam('sUrl', entryUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries_6', params) | |
| oGui.setView('movies') | |
| oGui.setEndOfDirectory() | |
| def showHosters_6(): | |
| hosters = [] | |
| sUrl = ParameterHandler().getValue('entryUrl') | |
| sHtmlContent = cRequestHandler(sUrl, caching=False).request() | |
| isMatch, aResult = cParser.parse(sHtmlContent, r'<noscript><a\shref="([^"]+)') | |
| if isMatch: | |
| for sUrl in aResult: | |
| hoster = {'link': sUrl, 'name': cParser.urlparse(sUrl)} | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl_6') | |
| return hosters | |
| def getHosterUrl_6(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearch_6(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30289)) | |
| if not sSearchText: return | |
| _search_6(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search_6(oGui, sSearchText): | |
| showEntries_6(URL_SEARCH_6 % cParser.quotePlus(sSearchText), oGui, sSearchText) | |
| #################### Youtube #################### | |
| URL_MAIN = 'http://www.youtube.com' | |
| def showYTChannels(): | |
| youtube_fix.YT() | |
| try: | |
| params = ParameterHandler() | |
| apikey = cConfig('plugin.video.youtube').getSetting('youtube.api.key') | |
| channelslists = { | |
| 'Channel':[ | |
| ("ARTEde", "https://yt3.ggpht.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s240-c-k-c0x00ffffff-no-rj", "/channel/UCLLibJTCy3sXjHLVaDimnpQ"), | |
| ("ARTE․tv Doku", "https://yt3.ggpht.com/FQMc44rW0CmIUKRCDjKcc0BiyyDyWobqb65gsaUbdG-jH3RYQakdYUMmBWusGnyFxlGvKEkK=s240-c-k-c0x00ffffff-no-rj", "/channel/UCr7BKJBRxT66pMGVpFQxzZw"), | |
| ("BR24", "https://yt3.googleusercontent.com/3utKLXf5P7l9j_sTEuj6dqwSx3ff0pvflQNkuBiU9VHAT-XVz3OOw-SFcTctjFtSp5RSQo3aU7Q=s160-c-k-c0x00ffffff-no-rj", "/channel/UCcweJsCV2TUP_kzMX25U-oQ"), | |
| ("De Doku Akte", "https://yt3.ggpht.com/ytc/AIdro_lpEa9xCRCqx5a5t8Bh1RB-Q0wzQ_jNgczmdQj3UgfNaw=s240-c-k-c0x00ffffff-no-rj", "/channel/UCaKgcPc5U_PiTSG-K3RKG3w"), | |
| ("DOKU", "https://yt3.ggpht.com/8x5H67IovFv_P2CFKFSHbAzuAZv-tmtwpuV--29lC3tZMasxIYDUYDjvDt1SKVrrEBE7Xpg7SAY=s240-c-k-c0x00ffffff-no-rj", "/channel/UC4lYbU_FGJY95xSGaUF9PCA"), | |
| ("Dokus und mehr", "https://yt3.ggpht.com/ytc/AIdro_mLaLdZfO84aZuqG1SppQ2eEnYmBysvKBu_IZFdGUo=s240-c-k-c0x00ffffff-no-rj", "/channel/UCue9a_27UYZUpx0azinesIw"), | |
| ("Der Spiegel", "https://yt3.ggpht.com/ytc/AIdro_nQCxKfFs_hJhLzVDhXK_13EJjqFJNbSWM4JImydL02m68=s240-c-k-c0x00ffffff-no-rj", "/channel/UC1w6pNGiiLdZgyNpXUnA4Zw"), | |
| ("DMAX", "https://yt3.googleusercontent.com/iW1EB5wkDE76d08Jy6yRO5JX_06EokRm85rBLovQOQ-jTrhuoCbxD_t7T9DuGvGTnGNhMwJS=s160-c-k-c0x00ffffff-no-rj", "/channel/UCnytvQ-VexQ43pYx6KX-DPw"), | |
| ("Galileo", "https://yt3.ggpht.com/g_I4TuM3r7n7ukBZH-R4Tp4eKabz1OA_F6bOfuacGWDZky-MC8jbyywxLWbQlb3exNaBjE-ICL8=s240-c-k-c0x00ffffff-no-rj", "/channel/UC1XrG1M_hw8103zO2x-oivg"), | |
| ("Mach dich schlau! - Mystery, Doku & Space", "https://yt3.ggpht.com/QybN6XyuPAOGMomZRpRKdfJR7Y_vumGuCHhzr3vodqmvvflO11pIaHd_qlEidRaYbRYjCRA5QQ=s240-c-k-c0x00ffffff-no-rj", "/channel/UCOidlT_g7LMXlLkiGK6aTdw"), | |
| ("MDR DOK", "https://yt3.ggpht.com/ytc/AIdro_lYFPfGkjfEPgUxvMFD5ygFlRRoRX5X7_iS6JAHJgciIw=s240-c-k-c0x00ffffff-no-rj", "/channel/UCFxgCHRLXmaW3YUyDsikoSw"), | |
| ("National Geographic Deutschland", "https://yt3.ggpht.com/ytc/AIdro_kU4mJluUbaOxrPEudLfQVR5jqy-Tg7jfEdXc1qLdnC78c=s240-c-k-c0x00ffffff-no-rj", "/channel/UCLZgflun26j9V9n73zA2T5g"), | |
| ("NDR Doku", "https://yt3.googleusercontent.com/ytc/AIdro_lCawKvOSn5yZPta-q7z-OH0jiOphq0cjhyPMst4dDsAa8=s160-c-k-c0x00ffffff-no-rj", "/channel/UCTPAHk1b-h-WGQn9cfGlw2Q"), | |
| ("ntv Nachrichten", "https://yt3.googleusercontent.com/ytc/AIdro_mRTjZ8IflJpkRty2Q26wo7MA1gctT0jEz-iJqBRwD43bQ=s160-c-k-c0x00ffffff-no-rj", "/channel/UCSeil5V81-mEGB1-VNR7YEA"), | |
| ("phoenix", "https://yt3.ggpht.com/Z8hNO57BolkhiNu-nWUuQ6h_WCwH8k11LBVEfBbjKtIabNMogzbFQ8Jjr0YS3Kr0B-7g6kk-Dw=s240-c-k-c0x00ffffff-no-rj", "/channel/UCwyiPnNlT8UABRmGmU0T9jg"), | |
| ("Real Wild Deutschland", "https://yt3.ggpht.com/KsrwAPzVj9CzRTXg_6F2BwNUjzS8HakOCpAZGxeAP6N1gMfXN3f1n5VbnrahL5JUx9EEIR5bmA=s240-c-k-c0x00ffffff-no-rj", "/channel/UCCf599vX34Bf8vT22RYJe0w"), | |
| ("SRF Dok", "https://yt3.googleusercontent.com/EXf5J9hT7Nm_3hmF6jxfUMW0cFkmfvDxWb_-Sb-2O9_eG1lUsfrYtFBPWjTEmKRd3tfhM8U5Bw=s160-c-k-c0x00ffffff-no-rj", "/channel/UCdFkj0fA6VYJaty-v8_avvg"), | |
| ("SWR Doku", "https://yt3.ggpht.com/k05Upm6oh65EuJnNPvYnBJpaxBrj-RWNNBS0BS70yw5Az0JoPw6_MpfA96_yNUSv1ObQn5hX=s240-c-k-c0x00ffffff-no-rj", "/channel/UCK6jlnWA8t-XgUxwZJJHkQA"), | |
| ("Terra X History", "https://yt3.ggpht.com/UrIj1GSSPUxtrlm2mbJC98ELpgOP0zeNjuYyB3ZAXCp4lScUes4-4PkqWESPSfAMC8eYC6mObA=s240-c-k-c0x00ffffff-no-rj", "/channel/UCA3mpqm67CpJ13YfA8qAnow"), | |
| ("Welt der Wunder", "https://yt3.ggpht.com/ytc/AIdro_kvhxXUtVXav_qwl4bPJlsivUSCX0-oHX32rGUrs9B05Wo=s240-c-k-c0x00ffffff-no-rj", "/channel/UCBk6ZmyoyX1kLl-w17B0V1A"), | |
| ("WDR Doku", "https://yt3.googleusercontent.com/ytc/AIdro_lvqTnZ71kiSPuVGl0MwB94uktdGVECDzVrVNiUj5Yl0g=s160-c-k-c0x00ffffff-no-rj", "/channel/UCUuab1dctZzN5ZmRmQnTzkg"), | |
| ("XL Doku Deutschland", "https://yt3.googleusercontent.com/tCRuC1qvXA39Q7AeVQOK4x6n1dYtsIE_v8NVCkJqNcKkSJfuvsk2dQ0nLlILZT9Gz_x9Fqyw9A=s160-c-k-c0x00ffffff-no-rj", "/channel/UCYaNwUGngT8hljZRSZVTvUg"), | |
| ("XL Geschichte", "https://yt3.googleusercontent.com/A2reg275ar2WKAHCosLxo-WZtUkoqwcr0BAXE5xB0SyU40_GsZYOPBefISvYky24uSJksyp4wPw=s160-c-k-c0x00ffffff-no-rj", "/channel/UCllLzS9TIgsD3E970AoSMnA"), | |
| ("ZDFinfo Dokus & Reportagen", "https://yt3.ggpht.com/ytc/AIdro_nPZhGyR2qcxE745O7Xa0O1jzqTtIxuOyLFQLnZ7R9Dx6c=s240-c-k-c0x00ffffff-no-rj", "/channel/UC7FeuS5wwfSR9IwOPkBV7SQ"), | |
| ],} | |
| for List in channelslists['Channel']: | |
| name = List[0] | |
| id = List[2] | |
| icon = List[1] | |
| if apikey == '' or apikey == None: | |
| sUrl="plugin://plugin.video.youtube" + id + "/?addon_id=plugin.video.xstream" | |
| else: | |
| sUrl="plugin://plugin.video.youtube" + id + "/" | |
| params.setParam('trumb', icon) | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(name,SITE_IDENTIFIER,''),params,bIsFolder=True) | |
| xbmcplugin.endOfDirectory(handle=int(sys.argv[1]), succeeded=True) | |
| except:return | |
| def showYTGenre(): | |
| youtube_fix.YT() | |
| params = ParameterHandler() | |
| channellist = [ | |
| ("Arbeit, Beruf & Leben", "Arbeit", "youtube.png"), | |
| ("Autos, Technik & Wissen", "Autos", "youtube.png"), | |
| ("Ernährung, Kochen & Essen", "Ernährung", "youtube.png"), | |
| ("Geschichte, Kultur & Gesellschaft", "Geschichte", "youtube.png"), | |
| ("Gesundheit, Medizin & Wissen", "Gesundheit", "youtube.png"), | |
| ("Kriminalität, Sucht & Gewalt", "Kriminalität", "youtube.png"), | |
| ("Musik, Klang & Rock", "Musik", "youtube.png"), | |
| ("Natur, Tiere & die Erde", "Natur", "youtube.png"), | |
| ("Wirtschaft, Finanzen & Politik", "Wirtschaft", "youtube.png"), | |
| ("Wissenschaft, Universum & Ufos", "Wissenschaft", "youtube.png"), | |
| ] | |
| for name, id, icon in channellist: | |
| params.setParam('action', id) | |
| params.setParam('icon', icon) | |
| cGui().addFolder(cGuiElement(name, SITE_IDENTIFIER, 'showYTLists'), params) | |
| cGui().setEndOfDirectory() | |
| def showYTLists(): | |
| params = ParameterHandler() | |
| id = params.getValue('action') | |
| apikey = cConfig('plugin.video.youtube').getSetting('youtube.api.key') | |
| sublist = { | |
| 'Arbeit': [ | |
| ("Architektur & Design | ARTE", "playlist/PLhGeNYH-50Kanjb7dddNt0ywYBX39NAm7", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("China | ARTE", "playlist/PLlQWnS27jXh94iIRrSqPzlfDOaWjdSfAU", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Helden der Baustelle | DMAX Motor", "playlist/PLu791Jb5lWoCOzceiS6SDeGW_u1s7x-0h", "https://yt3.googleusercontent.com/Nx3PWjIZtuLMfc5fye7PNIiZYEupPoEWxxjkxgzyPN-2-J-tbwQpr5ztqC9g-s-8435JDRvrUA=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Kinder & Familie | WDR", "playlist/PLeVHoee00PXs9DuUuSohGq2GbnZZcnGJF", "https://yt3.googleusercontent.com/Nx3PWjIZtuLMfc5fye7PNIiZYEupPoEWxxjkxgzyPN-2-J-tbwQpr5ztqC9g-s-8435JDRvrUA=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Anders Reisen & Arbeiten | WDR", "playlist/PLeVHoee00PXuKl5EIcyErW5ku-yyqhkNE", "https://yt3.googleusercontent.com/Nx3PWjIZtuLMfc5fye7PNIiZYEupPoEWxxjkxgzyPN-2-J-tbwQpr5ztqC9g-s-8435JDRvrUA=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Außergewöhnlich leben | WDR", "playlist/PLeVHoee00PXulyH000ptJ21w-BoNEqFVL", "https://yt3.googleusercontent.com/Nx3PWjIZtuLMfc5fye7PNIiZYEupPoEWxxjkxgzyPN-2-J-tbwQpr5ztqC9g-s-8435JDRvrUA=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Dokus und Reportagen | ZDFinfo", "playlist/PLo0xoJDmhYEacg7venaFlOYWsWlXu8_0_", "https://yt3.googleusercontent.com/ytc/AIdro_nPZhGyR2qcxE745O7Xa0O1jzqTtIxuOyLFQLnZ7R9Dx6c=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Deutschland 24/7 - Ohne uns läuft nichts! | DMAX", "playlist/PL-83LnmN43mZJ6ZmgzcvI1-HSmL6avP83", "https://yt3.googleusercontent.com/iW1EB5wkDE76d08Jy6yRO5JX_06EokRm85rBLovQOQ-jTrhuoCbxD_t7T9DuGvGTnGNhMwJS=s160-c-k-c0x00ffffff-no-rj"), | |
| ("THW - Wahre Helden im Einsatz | NDR", "PLMJjvZqoYSrD4QZi6dDiuUj5AA4-aIJ6P", "https://yt3.googleusercontent.com/ytc/AIdro_lCawKvOSn5yZPta-q7z-OH0jiOphq0cjhyPMst4dDsAa8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Unternehmen | SWR", "playlist/PLF4F26D09B2C4FF18", "https://yt3.ggpht.com/k05Upm6oh65EuJnNPvYnBJpaxBrj-RWNNBS0BS70yw5Az0JoPw6_MpfA96_yNUSv1ObQn5hX=s240-c-k-c0x00ffffff-no-rj"), | |
| ("Galileo testet Berufe | ProSieben", "playlist/PLg_KHB2Fiu4eTR4SFQjDdhapk0yj4bD4I", "https://yt3.googleusercontent.com/g_I4TuM3r7n7ukBZH-R4Tp4eKabz1OA_F6bOfuacGWDZky-MC8jbyywxLWbQlb3exNaBjE-ICL8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Schrecklich schöne Bausünden | ARTE", "playlist/PLhGeNYH-50KaS92AY2VQLKg81Q18DKaeM", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ], | |
| 'Autos': [ | |
| ("Auto Motor und Sport", "channel/UCLINPbYQ9sy6qc-TqtBeVnw", "https://yt3.googleusercontent.com/ytc/AIdro_k3FanN1l7rSijwXIXpP-hRkFk2CSYQi84W6xruiu23iCA=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Auto Bild", "channel/UCJrXOOtvmGn4CF7aYaJwuHA", "https://yt3.googleusercontent.com/ytc/AIdro_nQEoz8_Bl0N9F1XX_Kp0ceWTJYzXhmkdYJdDO3HFYP7A=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Auto Zeitung", "channel/UCHwc6w57Q3S8CSHWergMJWg", "https://yt3.googleusercontent.com/ytc/AIdro_mqA44JCYCHj9Klaq8xwV4gwHufq5oT5Jr4r4hBy7Q8ODzJ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("JP Performance GmbH", "channel/UC1-VOKyTJrgLiBeiJqzeIUQ", "https://yt3.googleusercontent.com/VD-_UIQmAWviblV4ju2op5ksCiI405YMkhEqU3GoTYa3GRnE8aYLCz_N73DXDO8f2ochm7oJ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Limora Oldtimer", "channel/UCT-7FHrVJGFSniI8YhiCfIg", "https://yt3.googleusercontent.com/ytc/AIdro_kabpiOxSiXA2xNotYZP-FCYygBliD5NfqzodSaZ5kq_w=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Strassenklassiker", "channel/UCx7LHFyEJzIgQ96jhpPeV5g", "https://yt3.googleusercontent.com/ytc/AIdro_lvqTnZ71kiSPuVGl0MwB94uktdGVECDzVrVNiUj5Yl0g=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Max Carshop | DMAX", "playlist/PL-83LnmN43mbvMjyKvjqGqXiiRzl-llb5", "https://yt3.googleusercontent.com/iW1EB5wkDE76d08Jy6yRO5JX_06EokRm85rBLovQOQ-jTrhuoCbxD_t7T9DuGvGTnGNhMwJS=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Speed Cops | DMAX", "playlist/PL-83LnmN43mZAGslLyLbkhtFPHAzom9Tg", "https://yt3.googleusercontent.com/iW1EB5wkDE76d08Jy6yRO5JX_06EokRm85rBLovQOQ-jTrhuoCbxD_t7T9DuGvGTnGNhMwJS=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Steel Buddies | Staffel 8 | DMAX", "playlist/PL-83LnmN43mZCqnCtGp8RgOv3Iy2ZlPCd", "https://yt3.googleusercontent.com/iW1EB5wkDE76d08Jy6yRO5JX_06EokRm85rBLovQOQ-jTrhuoCbxD_t7T9DuGvGTnGNhMwJS=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Steel Buddies | Staffel 11 | DMAX", "playlist/PL-83LnmN43mYQI7JHNCT3H0pI3MDUcWa3", "https://yt3.googleusercontent.com/iW1EB5wkDE76d08Jy6yRO5JX_06EokRm85rBLovQOQ-jTrhuoCbxD_t7T9DuGvGTnGNhMwJS=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Cash für Chrom | Staffel 3 | DMAX", "playlist/PL-83LnmN43mYN4PyKYSQfwTJ03yVWWAnE", "https://yt3.googleusercontent.com/iW1EB5wkDE76d08Jy6yRO5JX_06EokRm85rBLovQOQ-jTrhuoCbxD_t7T9DuGvGTnGNhMwJS=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Overhaulin - Aufgemotzt und Abgefahrn | DMAX", "playlist/PL-83LnmN43maV9n1M4VsjrbTuKOVyPnPj", "https://yt3.googleusercontent.com/iW1EB5wkDE76d08Jy6yRO5JX_06EokRm85rBLovQOQ-jTrhuoCbxD_t7T9DuGvGTnGNhMwJS=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Fast N' Loud | DMAX", "playlist/PL-83LnmN43maCpqDqNfWQRp4-HkGOe0bG", "https://yt3.googleusercontent.com/iW1EB5wkDE76d08Jy6yRO5JX_06EokRm85rBLovQOQ-jTrhuoCbxD_t7T9DuGvGTnGNhMwJS=s160-c-k-c0x00ffffff-no-rj"), | |
| ], | |
| 'Ernährung': [ | |
| ("Kiki and Koko | @beagleskiko", "channel/UCMPw97JErllU6MQ8tnaMcLg", "https://yt3.googleusercontent.com/vvQyMcFuDLfn3sHdBh43u-YyvuU0421fXszR9ZvzYUyEafnWjcA7zEGtV7l28z9XLhRH5EIj=s160-c-k-c0x00ffffff-no-rj"), | |
| ("WELT Food", "channel/UCITYOgOaytxZWlBVK6q5apw", "https://yt3.googleusercontent.com/YjfuJ48M0oKjPkcc5jPxutzb_NI_Ty9qQMvBM0Q9E_599sDWQR73GFq1Iww2z49_en2l4IwJ3A=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Doku rund um's Essen, Kultur und Armut Weltweit | Best4ever", "playlist/PLrH_VmNS2hgXwe-o7F8amXWzoEOK6JM9U", "https://yt3.googleusercontent.com/ytc/AIdro_mKBLdCXVlFCdK9S52WXllejLSUPQsQfoE6ahZyvU4rJA=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Brot & Stulle | NDR", "playlist/PLMJjvZqoYSrBYyf9kjKVQhyPO01-Gp92w", "https://yt3.googleusercontent.com/ytc/AIdro_lCawKvOSn5yZPta-q7z-OH0jiOphq0cjhyPMst4dDsAa8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Norddeutsche Imbiss-Kultur | NDR", "playlist/PLMJjvZqoYSrCZ1tbtR3GgX1cP-8vd-X7i", "https://yt3.googleusercontent.com/ytc/AIdro_lCawKvOSn5yZPta-q7z-OH0jiOphq0cjhyPMst4dDsAa8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Plätzchen-Werkstatt | SWR", "playlist/PLqcJ1tIeqh4jvH-9SpAkvXGnxZuROZhUr", "https://yt3.ggpht.com/k05Upm6oh65EuJnNPvYnBJpaxBrj-RWNNBS0BS70yw5Az0JoPw6_MpfA96_yNUSv1ObQn5hX=s240-c-k-c0x00ffffff-no-rj"), | |
| ("Koch ein! | SWR", "playlist/PLqcJ1tIeqh4h4qdELDWsuRIXeET6qSsJw", "https://yt3.ggpht.com/k05Upm6oh65EuJnNPvYnBJpaxBrj-RWNNBS0BS70yw5Az0JoPw6_MpfA96_yNUSv1ObQn5hX=s240-c-k-c0x00ffffff-no-rj"), | |
| ("Ernährung | SWR", "playlist/PLqcJ1tIeqh4jJqs305xUwKDDzeX13m7IB", "https://yt3.ggpht.com/k05Upm6oh65EuJnNPvYnBJpaxBrj-RWNNBS0BS70yw5Az0JoPw6_MpfA96_yNUSv1ObQn5hX=s240-c-k-c0x00ffffff-no-rj"), | |
| ("Lifestyle & Kochen | SWR", "playlist/PLqcJ1tIeqh4gldQL3sRGywM_1njJtUmMe", "https://yt3.ggpht.com/k05Upm6oh65EuJnNPvYnBJpaxBrj-RWNNBS0BS70yw5Az0JoPw6_MpfA96_yNUSv1ObQn5hX=s240-c-k-c0x00ffffff-no-rj"), | |
| ("Oma kocht am besten | SWR", "playlist/PLqcJ1tIeqh4hKc4oVoeky-N_-s7_O4lsk", "https://yt3.ggpht.com/k05Upm6oh65EuJnNPvYnBJpaxBrj-RWNNBS0BS70yw5Az0JoPw6_MpfA96_yNUSv1ObQn5hX=s240-c-k-c0x00ffffff-no-rj"), | |
| ("Galileo Burger | ProSieben", "playlist/PLg_KHB2Fiu4dreS90s4c4ZvGdJKDWTkdK", "https://yt3.googleusercontent.com/g_I4TuM3r7n7ukBZH-R4Tp4eKabz1OA_F6bOfuacGWDZky-MC8jbyywxLWbQlb3exNaBjE-ICL8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Galileo FoodScan | ProSieben", "playlist/PLg_KHB2Fiu4ci4ewc2K9LYqhzsahTWIWo", "https://yt3.googleusercontent.com/g_I4TuM3r7n7ukBZH-R4Tp4eKabz1OA_F6bOfuacGWDZky-MC8jbyywxLWbQlb3exNaBjE-ICL8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Galileo FoodScan | ProSieben", "playlist/PLg_KHB2Fiu4ci4ewc2K9LYqhzsahTWIWo", "https://yt3.googleusercontent.com/g_I4TuM3r7n7ukBZH-R4Tp4eKabz1OA_F6bOfuacGWDZky-MC8jbyywxLWbQlb3exNaBjE-ICL8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Kulinarik | ARTE", "playlist/PLhGeNYH-50KbReiEQw8W-aBBRQMn5q0jG", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Fast and Good | ARTE", "playlist/PLhGeNYH-50KYvZj4C4RTX6Zb0SVGb38Ds", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Kochen und Rezepte | HR", "playlist/PLBoP5sAJK3ft5-1Gj9vPhx5Dr1MDspOxI", "https://yt3.googleusercontent.com/AESs0cOwQhi4Zlxmye5TGtsdlR-le5I-VgdvirAiX97--C8wwFOHG-osatCZLfWuybEXeJB0sg=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Kochs anders | HR", "playlist/PLBoP5sAJK3fvTQ5ZIdZBQUxvD2bjlb6l6", "https://yt3.googleusercontent.com/AESs0cOwQhi4Zlxmye5TGtsdlR-le5I-VgdvirAiX97--C8wwFOHG-osatCZLfWuybEXeJB0sg=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Einfach lecker | HR", "playlist/PLBoP5sAJK3fsaqG0C-6ksCio0jokHInpe", "https://yt3.googleusercontent.com/AESs0cOwQhi4Zlxmye5TGtsdlR-le5I-VgdvirAiX97--C8wwFOHG-osatCZLfWuybEXeJB0sg=s160-c-k-c0x00ffffff-no-rj"), | |
| ], | |
| 'Geschichte': [ | |
| ("Kunst | ARTE", "playlist/PLhGeNYH-50KYd7Rw7hw25cvk57gB5RT-S", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Die Mythen der Wikinger | ARTE", "playlist/PLlQWnS27jXh-A2EdHJ8nOpYq6_UdOpy-S", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Eine Geschichte des Antisemitismus | ARTE", "playlist/PLlQWnS27jXh8McGW6dctPIaZixmfa_-sP", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Der Amerikanische Bürgerkrieg | ARTE", "playlist/PLlQWnS27jXh9_Po60UOaxj0Cawlu4gbKY", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Skandale & Politik | WDR", "playlist/PLeVHoee00PXuSbteBjTrg9MTEXRP-Gq58", "https://yt3.googleusercontent.com/ytc/AIdro_lvqTnZ71kiSPuVGl0MwB94uktdGVECDzVrVNiUj5Yl0g=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Historisch und abgründig | ZDFinfo", "playlist/PLo0xoJDmhYEbGMoU6Y5Wy6pS9s7-3Ond3", "https://yt3.googleusercontent.com/ytc/AIdro_nPZhGyR2qcxE745O7Xa0O1jzqTtIxuOyLFQLnZ7R9Dx6c=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Andere Länder, andere Sitten | ZDFinfo", "playlist/PLo0xoJDmhYEaziLbtuOpgChskPjVIYpg-", "https://yt3.googleusercontent.com/ytc/AIdro_nPZhGyR2qcxE745O7Xa0O1jzqTtIxuOyLFQLnZ7R9Dx6c=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Umwelt und Nachhaltigkeit | ZDFinfo", "playlist/PLo0xoJDmhYEYzDcLGYFqK9gPpuMBWQHmi", "https://yt3.googleusercontent.com/ytc/AIdro_nPZhGyR2qcxE745O7Xa0O1jzqTtIxuOyLFQLnZ7R9Dx6c=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Geschichte und Europa | ZDFinfo", "playlist/PLo0xoJDmhYEbcQTzcQ3F9IikTpIHoTniR", "https://yt3.googleusercontent.com/ytc/AIdro_nPZhGyR2qcxE745O7Xa0O1jzqTtIxuOyLFQLnZ7R9Dx6c=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Jugoslawienkriege | ZDFinfo", "playlist/PLo0xoJDmhYEYXd48Cks4zyeaq48GVgiGP", "https://yt3.googleusercontent.com/ytc/AIdro_nPZhGyR2qcxE745O7Xa0O1jzqTtIxuOyLFQLnZ7R9Dx6c=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Unsere Geschichte | NDR", "playlist/PLMJjvZqoYSrBSUNUjlJcqjevsWB3zgCoa", "https://yt3.googleusercontent.com/ytc/AIdro_lCawKvOSn5yZPta-q7z-OH0jiOphq0cjhyPMst4dDsAa8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Griechenland: Von den Gipfeln bis ans Meer | WELT", "playlist/PLslDofkqdKI96RKNyrSlsa3s_xDhIoEWJ", "https://yt3.googleusercontent.com/warJ1-zqcnR1n0LMK6ONepLoYwFcQS9u-noc8bl0-Uk6Lfbd8vIuXPEgDk6bjs34vp8FZpM5yw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Gesellschaft | SWR", "playlist/PLqcJ1tIeqh4g_M3O2KSXOT91owaYyfb34", "https://yt3.ggpht.com/k05Upm6oh65EuJnNPvYnBJpaxBrj-RWNNBS0BS70yw5Az0JoPw6_MpfA96_yNUSv1ObQn5hX=s240-c-k-c0x00ffffff-no-rj"), | |
| ("Hitler privat | SPIEGEL TV", "playlist/PLuiYhcgFTmqCjQ7eYtpbUWf1oJtLujmHq", "https://yt3.googleusercontent.com/ytc/AIdro_nQCxKfFs_hJhLzVDhXK_13EJjqFJNbSWM4JImydL02m68=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Der Zweite Weltkrieg | SPIEGEL TV", "playlist/PLuiYhcgFTmqAi8SWk6p1iC4zESvcO0mDt", "https://yt3.googleusercontent.com/ytc/AIdro_nQCxKfFs_hJhLzVDhXK_13EJjqFJNbSWM4JImydL02m68=s160-c-k-c0x00ffffff-no-rj"), | |
| ("30 Jahre Mauerfall | SPIEGEL TV", "playlist/PLuiYhcgFTmqDdxOuhl4yAJ7KCiXK3jYij", "https://yt3.googleusercontent.com/ytc/AIdro_nQCxKfFs_hJhLzVDhXK_13EJjqFJNbSWM4JImydL02m68=s160-c-k-c0x00ffffff-no-rj"), | |
| ], | |
| 'Gesundheit': [ | |
| ("Krankheit & Gesundheit | WDR", "playlist/PLeVHoee00PXvtH9M66B9qcVOprg9eqPPj", "https://yt3.googleusercontent.com/ytc/AIdro_lvqTnZ71kiSPuVGl0MwB94uktdGVECDzVrVNiUj5Yl0g=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Liebe & Sex | WDR", "PLeVHoee00PXuR8r-W2f2f8j60MixKDlCw", "https://yt3.googleusercontent.com/ytc/AIdro_lvqTnZ71kiSPuVGl0MwB94uktdGVECDzVrVNiUj5Yl0g=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Gesundheit | SWR", "playlist/PLqcJ1tIeqh4g4c-CBS5997U4uFa2aCsZN", "https://yt3.ggpht.com/k05Upm6oh65EuJnNPvYnBJpaxBrj-RWNNBS0BS70yw5Az0JoPw6_MpfA96_yNUSv1ObQn5hX=s240-c-k-c0x00ffffff-no-rj"), | |
| ("Wissen | SWR", "playlist/PLqcJ1tIeqh4j21kT9PTEtdwXr0axLdNEF", "https://yt3.ggpht.com/k05Upm6oh65EuJnNPvYnBJpaxBrj-RWNNBS0BS70yw5Az0JoPw6_MpfA96_yNUSv1ObQn5hX=s240-c-k-c0x00ffffff-no-rj"), | |
| ("Gesundheit | ARTE", "playlist/PLlQWnS27jXh_S3iOhmygd8hqRBNLyrBAW", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Unser Baby | HR", "playlist/PLBoP5sAJK3fthq_2OFmxID4G_cfQ111cr", "https://yt3.googleusercontent.com/AESs0cOwQhi4Zlxmye5TGtsdlR-le5I-VgdvirAiX97--C8wwFOHG-osatCZLfWuybEXeJB0sg=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Notfall Krankenhaus | HR", "playlist/PLBoP5sAJK3fv5fIS8gEjIa_22IBBvTKzu", "https://yt3.googleusercontent.com/AESs0cOwQhi4Zlxmye5TGtsdlR-le5I-VgdvirAiX97--C8wwFOHG-osatCZLfWuybEXeJB0sg=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Kinderarzt Berwald | HR", "playlist/PLBoP5sAJK3ftkNwmI8fOxTZ1jjGsQDTeE", "https://yt3.googleusercontent.com/AESs0cOwQhi4Zlxmye5TGtsdlR-le5I-VgdvirAiX97--C8wwFOHG-osatCZLfWuybEXeJB0sg=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Die Gesundmacher | HR", "playlist/PLBoP5sAJK3fsO8MIQBxCwI8e0a19lRb1n", "https://yt3.googleusercontent.com/AESs0cOwQhi4Zlxmye5TGtsdlR-le5I-VgdvirAiX97--C8wwFOHG-osatCZLfWuybEXeJB0sg=s160-c-k-c0x00ffffff-no-rj"), | |
| ], | |
| 'Kriminalität': [ | |
| ("Süchtig nach Dopamin | ARTE", "playlist/PLhGeNYH-50KaGfPTo2_sl_yxVByNEfLTO", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Welt im Terror | ARTE", "playlist/PLlQWnS27jXh-SJ3lGY0kUb02WduOmw_TQ", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Alles zum Thema Drogen | ARTE", "playlist/PLlQWnS27jXh9McmOSmrMFzshYO4WOWcMG", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Nachtstreife | SWR", "playlist/PLqFRvy_OBSDpCgH03yvVVu9lPCBA3RN9V", "https://yt3.ggpht.com/k05Upm6oh65EuJnNPvYnBJpaxBrj-RWNNBS0BS70yw5Az0JoPw6_MpfA96_yNUSv1ObQn5hX=s240-c-k-c0x00ffffff-no-rj"), | |
| ("Crime & Justiz | WDR", "playlist/PLeVHoee00PXuiVpqioBWVuGpnFjwmK9EU", "https://yt3.googleusercontent.com/ytc/AIdro_lvqTnZ71kiSPuVGl0MwB94uktdGVECDzVrVNiUj5Yl0g=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Hinter Gittern | ZDFinfo", "playlist/PLo0xoJDmhYEZ0TuKyDavuZXwCJf5cLyjz", "https://yt3.googleusercontent.com/ytc/AIdro_nPZhGyR2qcxE745O7Xa0O1jzqTtIxuOyLFQLnZ7R9Dx6c=s160-c-k-c0x00ffffff-no-rj"), | |
| ("True Crime | ZDFinfo", "playlist/PLo0xoJDmhYEZU3al7C0if6-wDVUrLSSdM", "https://yt3.googleusercontent.com/ytc/AIdro_nPZhGyR2qcxE745O7Xa0O1jzqTtIxuOyLFQLnZ7R9Dx6c=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Tatort Internet | ZDFinfo", "playlist/PLo0xoJDmhYEYCjvayqOEQ1faH2tm9dOgr", "https://yt3.googleusercontent.com/ytc/AIdro_nPZhGyR2qcxE745O7Xa0O1jzqTtIxuOyLFQLnZ7R9Dx6c=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Gangs und Clans | ZDFinfo", "playlist/PLo0xoJDmhYEZUCp5UF3iV5J81o99gg0oT", "https://yt3.googleusercontent.com/ytc/AIdro_nPZhGyR2qcxE745O7Xa0O1jzqTtIxuOyLFQLnZ7R9Dx6c=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Polizei im Einsatz | ZDFinfo", "playlist/PLo0xoJDmhYEbQ_-tMI1Jext3BXQUep6sU", "https://yt3.googleusercontent.com/ytc/AIdro_nPZhGyR2qcxE745O7Xa0O1jzqTtIxuOyLFQLnZ7R9Dx6c=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Verbrechen und Mord | ZDFinfo", "playlist/PLo0xoJDmhYEYKrB5-czkUjEiCP-oNlttC", "https://yt3.googleusercontent.com/ytc/AIdro_nPZhGyR2qcxE745O7Xa0O1jzqTtIxuOyLFQLnZ7R9Dx6c=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Border Control: Schwedens Grenzschützer | DMAX", "playlist/PL-83LnmN43maC8SPvHyz5heHhlYsCBi-2", "https://yt3.googleusercontent.com/iW1EB5wkDE76d08Jy6yRO5JX_06EokRm85rBLovQOQ-jTrhuoCbxD_t7T9DuGvGTnGNhMwJS=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Verbrechen & Justiz | NDR", "playlist/PLMJjvZqoYSrBum1QtGLeDwwR1nVIqHS-g", "https://yt3.googleusercontent.com/ytc/AIdro_lCawKvOSn5yZPta-q7z-OH0jiOphq0cjhyPMst4dDsAa8=s160-c-k-c0x00ffffff-no-rj"), | |
| ], | |
| 'Natur': [ | |
| ("Natur & Umwelt | WDR", "playlist/PLeVHoee00PXsPbMnu_R_d2n1AbEbBCyKi", "https://yt3.googleusercontent.com/ytc/AIdro_lvqTnZ71kiSPuVGl0MwB94uktdGVECDzVrVNiUj5Yl0g=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Die Natur hinter den Mythen | ARTE", "playlist/PLlQWnS27jXh81J4EexYTn1AjPQh7cpgNa", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Mächtige Winde | ARTE", "playlist/PLlQWnS27jXh_A3o1EVe9WNmPz1saMcmDP", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Unser Wasser - Faszinierende Wunderwelten | ARTE", "playlist/PLlQWnS27jXh-nOtc4iaDn58AxlWdMcznl", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Home Rescue - Wohnen in der Wildnis | DMAX", "playlist/PL-83LnmN43mZ3_asZCua7i7g9ILnutj6e", "https://yt3.googleusercontent.com/iW1EB5wkDE76d08Jy6yRO5JX_06EokRm85rBLovQOQ-jTrhuoCbxD_t7T9DuGvGTnGNhMwJS=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Geschichten am Wasser | NDR", "playlist/PLMJjvZqoYSrD8HWA0zVEJvTfr0J1STo2u", "https://yt3.googleusercontent.com/ytc/AIdro_lCawKvOSn5yZPta-q7z-OH0jiOphq0cjhyPMst4dDsAa8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Abenteuer Kanu-Tour | NDR", "playlist/PLMJjvZqoYSrD8HWA0zVEJvTfr0J1STo2u", "https://yt3.googleusercontent.com/ytc/AIdro_lCawKvOSn5yZPta-q7z-OH0jiOphq0cjhyPMst4dDsAa8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("#wetterextrem | NDR", "playlist/PLMJjvZqoYSrAPvLvHEZwzEaunnaFGGo6B", "https://yt3.googleusercontent.com/ytc/AIdro_lCawKvOSn5yZPta-q7z-OH0jiOphq0cjhyPMst4dDsAa8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("NaturNah | NDR", "playlist/PLMJjvZqoYSrDDPHqBwAanhHtA1D9BWkgI", "https://yt3.googleusercontent.com/ytc/AIdro_lCawKvOSn5yZPta-q7z-OH0jiOphq0cjhyPMst4dDsAa8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Hanseblick | NDR", "playlist/PLMJjvZqoYSrDqbkkjJkGuWuUmiz2YlYnv", "https://yt3.googleusercontent.com/ytc/AIdro_lCawKvOSn5yZPta-q7z-OH0jiOphq0cjhyPMst4dDsAa8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Nordseereport | NDR", "playlist/PLMJjvZqoYSrCgXhI_S9JT2DD5v7rmyCv3", "https://yt3.googleusercontent.com/ytc/AIdro_lCawKvOSn5yZPta-q7z-OH0jiOphq0cjhyPMst4dDsAa8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Ostseereport | NDR", "playlist/PLMJjvZqoYSrAs3QxoP7kYZ8LPJO2oR2Lm", "https://yt3.googleusercontent.com/ytc/AIdro_lCawKvOSn5yZPta-q7z-OH0jiOphq0cjhyPMst4dDsAa8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("LOST PLACES - Doku Serie | WELT", "playlist/PLslDofkqdKI_520q4xOI31tK63E-KyobL", "https://yt3.googleusercontent.com/warJ1-zqcnR1n0LMK6ONepLoYwFcQS9u-noc8bl0-Uk6Lfbd8vIuXPEgDk6bjs34vp8FZpM5yw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Wetter & Klima | WELT", "playlist/PLslDofkqdKI-8ensvM5eAoBHoeRuEQbVu", "https://yt3.googleusercontent.com/warJ1-zqcnR1n0LMK6ONepLoYwFcQS9u-noc8bl0-Uk6Lfbd8vIuXPEgDk6bjs34vp8FZpM5yw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Die schönsten Reiseziele der Welt I Real Wild Deutschland", "playlist/PLSwXKep2Ci4FuCAEOMCf5skJ-o781_ybh", "https://yt3.googleusercontent.com/KsrwAPzVj9CzRTXg_6F2BwNUjzS8HakOCpAZGxeAP6N1gMfXN3f1n5VbnrahL5JUx9EEIR5bmA=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Tierdokumentationen | Real Wild Deutschland", "playlist/PLSwXKep2Ci4FFqhP83ZBT-Ajlj8iL4XgJ", "https://yt3.googleusercontent.com/KsrwAPzVj9CzRTXg_6F2BwNUjzS8HakOCpAZGxeAP6N1gMfXN3f1n5VbnrahL5JUx9EEIR5bmA=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Geniale Natur: clever und erstaunlich | Real Wild Deutschland", "playlist/PLSwXKep2Ci4EIjsXmudXsxaJqbpofGDm9", "https://yt3.googleusercontent.com/KsrwAPzVj9CzRTXg_6F2BwNUjzS8HakOCpAZGxeAP6N1gMfXN3f1n5VbnrahL5JUx9EEIR5bmA=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Naturschutz | Real Wild Deutschland", "playlist/PLSwXKep2Ci4Fz6i7XTBPmeN0vse6kmkVK", "https://yt3.googleusercontent.com/KsrwAPzVj9CzRTXg_6F2BwNUjzS8HakOCpAZGxeAP6N1gMfXN3f1n5VbnrahL5JUx9EEIR5bmA=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Faszinierende Natur | Real Wild Deutschland", "playlist/PLSwXKep2Ci4Fc18-vym377ywEN-Y-zW54", "https://yt3.googleusercontent.com/KsrwAPzVj9CzRTXg_6F2BwNUjzS8HakOCpAZGxeAP6N1gMfXN3f1n5VbnrahL5JUx9EEIR5bmA=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Tierisch Galileo | ProSieben", "playlist/PLg_KHB2Fiu4cp-ofOvFcdkqkXj8UZhNkb", "https://yt3.googleusercontent.com/g_I4TuM3r7n7ukBZH-R4Tp4eKabz1OA_F6bOfuacGWDZky-MC8jbyywxLWbQlb3exNaBjE-ICL8=s160-c-k-c0x00ffffff-no-rj"), | |
| ], | |
| 'Musik': [ | |
| ("DJPaoloMontiOfficial", "channel/UCHKb9guNj_pRq2OQ1QWgSAg", "https://yt3.googleusercontent.com/umHjglKoKH-POESb1cXGsCCY3kdrsOrpicNn8D5QoY52KI5Lk7EETCtED7Ii_asWvhyMaE_cXrA=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Mix Akták Retro Party", "channel/UCfzTaGgL5NT7BblA7eiyFYg", "https://yt3.googleusercontent.com/1XYO899bDtvgdg27NehA2_1lXFaHmaysiF7wU5XtuzjFIu7kzX_5CGIQs5X0GRz70eGUqzN1eMY=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Mr.Stephen - Hit Maker ", "channel/UC9TUDA88MhhBmc0IS78DBoA", "https://yt3.googleusercontent.com/k_FJkpPO7gIWSqJupHwr1hTiXAgjap2QRrdaJMtuR4oAgRS5_RNWAAKRvDs9Aa1f2l4WG_91Pw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("HBz", "channel/UCj6ljqIWs4Sfk3TBIn1xP6Q", "https://yt3.googleusercontent.com/ytc/AIdro_nj9Ll2_o0VuyJpbI7K3tChWr_SUypT9eH5BuK1ZyrBBV4=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Moreno J Remixes", "channel/UCJqyF-E8VW75fQz61ftchzg", "https://yt3.googleusercontent.com/zKjmrFXhlzaXsL29KBa06TazK2PJZr5180AaKcdk9eSAxM_gJrzU_SV8EdegguTqJ4QztOKbew=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Mashup Mixes", "channel/UC_MRLN4C-I0sApga0TrH7Sw", "https://yt3.googleusercontent.com/ytc/AIdro_kp3Jwpa8TLTMbDEyq4Fbppg3jr18Y3dZ5Wc4FNi0T93g=s160-c-k-c0x00ffffff-no-rj"), | |
| ("N&T Party", "channel/UCC9rwt1T2i4klATksN6prdQ", "https://yt3.googleusercontent.com/ytc/AIdro_nLWadIzMDdiXcTIR0lHk78Tr7UGdQ3gSvL-QRDBQSnYg=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Best Remixes Channel", "channel/UCmpqeOzl0kdzEBpGni1kh1w", "https://yt3.googleusercontent.com/ytc/AIdro_lFw-qBchdHY68UifRatZ2rLYyFnfnwTSgtQna3WZZRAw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("ZILITIK", "channel/UCEMmEHUY8JLErkizCPuUk_w", "https://yt3.googleusercontent.com/w8EYJ2IH_ZqzpLS9wqt47VcuT3ENK_Q6W7d406AlY4d3UYZI3oafzAuNPKr6FEjs8ZuiOXi1bw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("DJ Happy Vibes", "channel/UC5LrfTmOrn-4R6tvltTOIRg", "https://yt3.googleusercontent.com/ytc/AIdro_mXiNbV6MyHIOOUeXbz4aIuR_2NTbUVcqvoB9JNU6UJIAg=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Top Hits 80s - Ohrwürmer der 80er Jahre", "playlist/PLd5xnond3B5Q76AHn0yU7iuCU5j7b4DLi", "https://yt3.googleusercontent.com/AyjYEnhHw62pOnBagaJwDqEjHTSXMqYno6m1bOxnk7t60KaJybbzq8QV8IpCNtKOYNgaP2FHiCg=s160-c-k-c0x00ffffff-no-rj"), | |
| ("90er Party Hits", "playlist/PLILCJF6YwPP-Uk6R539eCrqIUKaJOCyZ-", "https://yt3.googleusercontent.com/ytc/AIdro_m_q1gNRBvL1PCtss-fL81oeFCgx7m4q9iprHw52EhOdqg=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Martin Davis", "channel/UCqcBhsHlUuwQaI-il7xlcYQ", "https://yt3.googleusercontent.com/ytc/AIdro_m_q1gNRBvL1PCtss-fL81oeFCgx7m4q9iprHw52EhOdqg=s160-c-k-c0x00ffffff-no-rj"), | |
| ("2000er Hits Playlist", "playlist/PLJNXa5V1YTQT-Um4F8dVit4Uuu2Ag8NqG", "https://yt3.googleusercontent.com/U0EyubzYWFpfTGMwatogyyO-pOnjMs2AIsWIG7FTF4GlxZx4Q2Pjgcxwb97CMHkmk_easYC5JA=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Trampsta", "channel/UCOlxGyNCuzG8xm5ogYM6OOQ", "https://yt3.googleusercontent.com/ytc/AIdro_kqguRLm3O5_6sPkiqLVWev2L6GLTnnXd6GZeCvyr2a9Rw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Die Prinzen", "channel/UCvkPFtR30EM6wXu5Dy07p-g", "https://yt3.googleusercontent.com/F7c2A2Vo_9IxQrCKYvbRQVS-e9gr00wcE1qU4SXet7owfI8NNtLZhUJlCs8ZU5FgiYRVVJgLgis=s160-c-k-c0x00ffffff-no-rj"), | |
| ("MaxRiven", "channel/UCb7G5XdA9I8sNxC-3E3maFQ", "https://yt3.googleusercontent.com/ytc/AIdro_nQIbgFExwRCDB0NnZl1IX-APNWkz4ugpdLg2C0LTACui8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Musik | ARTE", "playlist/PLhGeNYH-50KYyCOpar7RnUF3ycN0jIi7F", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ], | |
| 'Wirtschaft': [ | |
| ("Industrie und Wirtschaft – Dokus und Reportagen kompakt | ZDFinfo", "playlist/PLo0xoJDmhYEZYhZe1a0LcS_BMOoQZEZcB", "https://yt3.googleusercontent.com/ytc/AIdro_nPZhGyR2qcxE745O7Xa0O1jzqTtIxuOyLFQLnZ7R9Dx6c=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Der Tag: News, Politik, Panorama, Wirtschaft und Sport | WELT", "playlist/PLslDofkqdKI9iy5ns0XnD8f7lvoSnnTot", "https://yt3.googleusercontent.com/warJ1-zqcnR1n0LMK6ONepLoYwFcQS9u-noc8bl0-Uk6Lfbd8vIuXPEgDk6bjs34vp8FZpM5yw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("LTW 2024 Brandenburg | phoenix", "playlist/PLoeytWjTuSuqRGjrqNduz1WuLgCBe7PUg", "https://yt3.googleusercontent.com/Z8hNO57BolkhiNu-nWUuQ6h_WCwH8k11LBVEfBbjKtIabNMogzbFQ8Jjr0YS3Kr0B-7g6kk-Dw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Bundestag 2024 | phoenix", "playlist/PLoeytWjTuSur7r-5DIpm4RE3clHBokEU5", "https://yt3.googleusercontent.com/Z8hNO57BolkhiNu-nWUuQ6h_WCwH8k11LBVEfBbjKtIabNMogzbFQ8Jjr0YS3Kr0B-7g6kk-Dw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Europawahl 2024 | Rechtsruck in der EU? | phoenix", "playlist/PLoeytWjTuSurgOmUfemNWL2pI4O6npoK7", "https://yt3.googleusercontent.com/Z8hNO57BolkhiNu-nWUuQ6h_WCwH8k11LBVEfBbjKtIabNMogzbFQ8Jjr0YS3Kr0B-7g6kk-Dw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("#Gamescom2022 | phoenix", "playlist/PLoeytWjTuSuowLmvkg4azapIw3r4M2icx", "https://yt3.googleusercontent.com/Z8hNO57BolkhiNu-nWUuQ6h_WCwH8k11LBVEfBbjKtIabNMogzbFQ8Jjr0YS3Kr0B-7g6kk-Dw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("ntv Faktenzeichen | N-TV", "playlist/PLwneNHYIBCIQO-vQ_N_xdb9daLVGc5aBh", "https://yt3.googleusercontent.com/ytc/AIdro_mRTjZ8IflJpkRty2Q26wo7MA1gctT0jEz-iJqBRwD43bQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Weltwirtschaftsforum WEF | N-TV", "playlist/PLwneNHYIBCISLENJlZUKMfE4gMQnGO95Q", "https://yt3.googleusercontent.com/ytc/AIdro_mRTjZ8IflJpkRty2Q26wo7MA1gctT0jEz-iJqBRwD43bQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("ntv Wirtschaft | N-TV", "playlist/PLwneNHYIBCIQDX9Tkvy5-xeBOqOpiwR1h", "https://yt3.googleusercontent.com/ytc/AIdro_mRTjZ8IflJpkRty2Q26wo7MA1gctT0jEz-iJqBRwD43bQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("ntv Nachrichten | N-TV", "playlist/PLwneNHYIBCISaAk_rizKfxde9OjT40Xz1", "https://yt3.googleusercontent.com/ytc/AIdro_mRTjZ8IflJpkRty2Q26wo7MA1gctT0jEz-iJqBRwD43bQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Universum | phoenix", "playlist/PL2TzD1hFT-xlhOsrV865Tie5-mbMJ9h1P", "https://yt3.googleusercontent.com/Z8hNO57BolkhiNu-nWUuQ6h_WCwH8k11LBVEfBbjKtIabNMogzbFQ8Jjr0YS3Kr0B-7g6kk-Dw=s160-c-k-c0x00ffffff-no-rj"), | |
| ], | |
| 'Wissenschaft': [ | |
| ("Das Universum entdecken | ARTE", "playlist/PLlQWnS27jXh9zChHrrOkFrpYBYIKLitu8", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("42 - Die Antwort auf fast alles | ARTE", "playlist/PLlQWnS27jXh_ofqIJZH7vdOkQ-p_w9gKw", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Wissenschaftsdokus | ARTE", "playlist/PLlQWnS27jXh9_eNjMwbuhNhlwEmjQRr-q", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Couchwissen | ARTE", "playlist/PLhGeNYH-50Ka-CHu6q-dytZJT4uRLKxY7", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Der Mensch, die Natur, das Abenteuer | ARTE", "playlist/PLlQWnS27jXh_WXZtlemAN3V0li9Gi57Cn", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Europa und das Weltall | ARTE", "playlist/PLlQWnS27jXh9o5zB_sBlecHpKkejRI8SI", "https://yt3.googleusercontent.com/ytc/AIdro_lK_Yng-tiFUXYY2ukKBmdtzqSazAH7iPHda9YgHX3JVZQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("DOKUS: Raumfahrt | WELT", "playlist/PLslDofkqdKI_KNyed3YcuDhKcE9RxtfMt", "https://yt3.googleusercontent.com/warJ1-zqcnR1n0LMK6ONepLoYwFcQS9u-noc8bl0-Uk6Lfbd8vIuXPEgDk6bjs34vp8FZpM5yw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Expedition Sternenhimmel | WELT", "playlist/PLslDofkqdKI_VYfR6q5au7iLpdce84asE", "https://yt3.googleusercontent.com/warJ1-zqcnR1n0LMK6ONepLoYwFcQS9u-noc8bl0-Uk6Lfbd8vIuXPEgDk6bjs34vp8FZpM5yw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Mysterien und Paranormales | WELT", "playlist/PLslDofkqdKI87XYQ68rgbCOMSCWJPGgvs", "https://yt3.googleusercontent.com/warJ1-zqcnR1n0LMK6ONepLoYwFcQS9u-noc8bl0-Uk6Lfbd8vIuXPEgDk6bjs34vp8FZpM5yw=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Aliens, Ufo, Geister, Monster, Horror und vieles mehr | Mach dich schlau!", "playlist/PL2TzD1hFT-xnsecdrlXpNyM3EkvAgUEcs", "https://yt3.googleusercontent.com/QybN6XyuPAOGMomZRpRKdfJR7Y_vumGuCHhzr3vodqmvvflO11pIaHd_qlEidRaYbRYjCRA5QQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Out of Place Artefakte | Mach dich schlau!", "playlist/PL2TzD1hFT-xnkM_8_KC7yrelq1C9_3aKk", "https://yt3.googleusercontent.com/QybN6XyuPAOGMomZRpRKdfJR7Y_vumGuCHhzr3vodqmvvflO11pIaHd_qlEidRaYbRYjCRA5QQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Universum | Mach dich schlau!", "playlist/PL2TzD1hFT-xlhOsrV865Tie5-mbMJ9h1P", "https://yt3.googleusercontent.com/QybN6XyuPAOGMomZRpRKdfJR7Y_vumGuCHhzr3vodqmvvflO11pIaHd_qlEidRaYbRYjCRA5QQ=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Unterhaltung | SPIEGEL TV", "playlist/PL084070CE355CB92A", "https://yt3.googleusercontent.com/ytc/AIdro_nQCxKfFs_hJhLzVDhXK_13EJjqFJNbSWM4JImydL02m68=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Science & Galileo | ProSieben", "playlist/PLg_KHB2Fiu4fc88rrCum8XMaB69WcRNUi", "https://yt3.googleusercontent.com/g_I4TuM3r7n7ukBZH-R4Tp4eKabz1OA_F6bOfuacGWDZky-MC8jbyywxLWbQlb3exNaBjE-ICL8=s160-c-k-c0x00ffffff-no-rj"), | |
| ("Galileo Wissenscountdown | ProSieben", "playlist/PLg_KHB2Fiu4eQGBwmccma9Tua-XVK_wEf", "https://yt3.googleusercontent.com/g_I4TuM3r7n7ukBZH-R4Tp4eKabz1OA_F6bOfuacGWDZky-MC8jbyywxLWbQlb3exNaBjE-ICL8=s160-c-k-c0x00ffffff-no-rj"), | |
| ], | |
| } | |
| for List in sublist[id]: | |
| name = List[0] | |
| id = List[1] | |
| icon = List[2] | |
| if apikey == '' or apikey == None: | |
| sUrl="plugin://plugin.video.youtube/" + id + "/?addon_id=plugin.video.xstream" | |
| else: | |
| sUrl="plugin://plugin.video.youtube/" + id + "/" | |
| params.setParam('trumb', icon) | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(name,SITE_IDENTIFIER,''),params,bIsFolder=True) | |
| xbmcplugin.endOfDirectory(handle=int(sys.argv[1]), succeeded=True) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| # Always pay attention to the translations in the menu! | |
| # Sprachauswahl für Filme | |
| # HTML LangzeitCache hinzugefügt | |
| # showValue: 24 Stunden | |
| # showEntries: 6 Stunden | |
| # showEpisodes: 4 Stunden | |
| import re | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| SITE_IDENTIFIER = 'filmpalast' | |
| SITE_NAME = 'FilmPalast' | |
| SITE_ICON = 'filmpalast.png' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| # Domain Abfrage | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'filmpalast.to') # Domain Auswahl über die xStream Einstellungen möglich | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| URL_MAIN = 'https://' + DOMAIN | |
| # URL_MAIN = 'https://filmpalast.to' | |
| URL_MOVIES = URL_MAIN + '/movies/%s' | |
| URL_ENGLISH = URL_MAIN + '/search/genre/Englisch' | |
| URL_SEARCH = URL_MAIN + '/search/title/%s' | |
| URL_SERIES = URL_MAIN + '/serien/view' | |
| def load(): # Menu structure of the site plugin | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| # Logic moved from showMovieMenu to main load function | |
| if sLanguage == '0' or '1': # Alle Sprachen oder Deutsch | |
| params.setParam('sUrl', URL_MOVIES % 'new') | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502), SITE_IDENTIFIER, 'showEntries'), params) # Filme | |
| params.setParam('sUrl', URL_MOVIES % 'top') | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30509), SITE_IDENTIFIER, 'showEntries'), params) # Top movies | |
| params.setParam('sUrl', URL_MOVIES % 'imdb') | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30510), SITE_IDENTIFIER, 'showEntries'), params) # IMDB rating | |
| if sLanguage == '0': # Nur bei Alle Sprachen | |
| params.setParam('sUrl', URL_ENGLISH) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30104), SITE_IDENTIFIER, 'showEntries'), params) # English | |
| params.setParam('sUrl', URL_MOVIES % 'new') | |
| params.setParam('value', 'genre') | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30506), SITE_IDENTIFIER, 'showValue'), params) # Genre | |
| params.setParam('value', 'movietitle') | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showValue'), params) # From A-Z | |
| if sLanguage == '2': # English | |
| params.setParam('sUrl', URL_ENGLISH) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30104), SITE_IDENTIFIER, 'showEntries'), params) # English | |
| elif sLanguage == '3': # Japanisch | |
| cGui().showLanguage() | |
| # Add Serien entry above search | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511), SITE_IDENTIFIER, 'showSeriesMenu')) # Serien | |
| # Search added at the bottom | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params) # Search | |
| cGui().setEndOfDirectory() | |
| def showValue(): | |
| params = ParameterHandler() | |
| value = params.getValue("value") | |
| oRequest = cRequestHandler(params.getValue('sUrl'), bypass_dns=True) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 24 # HTML Cache Zeit 1 Tag | |
| sHtmlContent = oRequest.request() | |
| pattern = '<section[^>]id="%s">(.*?)</section>' % value # Suche in der Section Einträge | |
| isMatch, sContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sContainer, 'href="([^"]+)">([^<]+)') | |
| aResult = sorted(aResult, key=lambda x: x[1].lower()) # Sort alphabetically by name (case-insensitive) | |
| for sUrl, sName in aResult: | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries'), params) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| cGui().setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| isTvshow = False | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False), bypass_dns=True) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # 6 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = r'<article[^>]*>\s*<a href="([^"]+)" title="([^"]+)">\s*<img src=["\']([^"\']+)["\'][^>]*>(.*?)</article>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| pattern = r'<a[^>]*href="([^"]*)"[^>]*title="([^"]*)"[^>]*>[^<]*<img[^>]*src=["\']([^"\']*)["\'][^>]*>\s*</a>(\s*)</article>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| seen_tv_shows = set() | |
| # SORTING CHANGE: Removed the check for '/movies/new' | |
| # Now everything is sorted alphabetically | |
| aResult = sorted(aResult, key=lambda x: x[1].lower()) # Sort alphabetically by name (case-insensitive) | |
| for sUrl, sName, sThumbnail, sDummy in aResult: | |
| isTvshow, _ = cParser.parse(sName, r'S\d\dE\d\d') | |
| # seriesname should not be crippled here! | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| if isTvshow: | |
| # Clean the name (e.g. "ABCDE S01E02" -> "ABCDE") | |
| cleanNameMatch = re.search(r'(.*?)\s*S\d+E\d+', sName, re.IGNORECASE) | |
| if cleanNameMatch: | |
| cleanName = cleanNameMatch.group(1).strip() | |
| # If we have already seen this show in this loop, skip it | |
| if cleanName in seen_tv_shows: | |
| continue | |
| # Mark as seen and update the display name to the clean title | |
| seen_tv_shows.add(cleanName) | |
| sName = cleanName | |
| if sThumbnail.startswith('/'): | |
| sThumbnail = URL_MAIN + sThumbnail | |
| ### ÄNDERUNG ANFANG ### | |
| isYear, sYear = cParser.parseSingleResult(sDummy, r'Jahr:[^>]([\d]+)') | |
| isDuration, sDuration = cParser.parseSingleResult(sDummy, r'(?:Laufzeit|Spielzeit):[^>]([\d]+)') | |
| isRating, sRating = cParser.parseSingleResult(sDummy, 'Imdb:[^>]([^/]+)') | |
| ### ÄNDERUNG ENDE ### | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showSeasons' if isTvshow else 'showHosters') | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if isYear: | |
| oGuiElement.setYear(sYear) | |
| if isDuration: | |
| oGuiElement.addItemValue('duration', sDuration) | |
| if isRating: | |
| oGuiElement.addItemValue('rating', sRating.replace(',', '.')) | |
| # Parameter übergeben | |
| if sUrl.startswith('//'): | |
| params.setParam('entryUrl', 'https:' + sUrl) | |
| else: | |
| params.setParam('entryUrl', sUrl) | |
| params.setParam('sName', sName) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui and not sSearchText: | |
| pattern = r'<a class="pageing[^"]*"\s*href=([^>]+)>[^\+]+\+</a>\s*</div>' | |
| isMatchNextPage, sNextUrl = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatchNextPage: | |
| sNextUrl = sNextUrl.replace("'", "").replace('"', '') | |
| if sNextUrl.startswith('/'): | |
| sNextUrl = URL_MAIN + sNextUrl | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('tvshows' if isTvshow else 'movies') | |
| oGui.setEndOfDirectory() | |
| def showSeasons(): | |
| params = ParameterHandler() | |
| # Parameter laden | |
| sUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue("sThumbnail") | |
| sName = params.getValue('sName') | |
| oRequest = cRequestHandler(sUrl, bypass_dns=True) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # HTML Cache Zeit 6 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = r'<a[^>]*class="staffTab"[^>]*data-sid="(\d+)"[^>]*>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| isDesc, sDesc = cParser.parseSingleResult(sHtmlContent, '"description">([^<]+)') | |
| total = len(aResult) | |
| for sSeason in aResult: | |
| oGuiElement = cGuiElement('Staffel ' + str(sSeason), SITE_IDENTIFIER, 'showEpisodes') | |
| oGuiElement.setTVShowTitle(sName) | |
| oGuiElement.setSeason(sSeason) | |
| oGuiElement.setMediaType('season') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if isDesc: | |
| oGuiElement.setDescription(sDesc) | |
| cGui().addFolder(oGuiElement, params, True, total) | |
| cGui().setView('seasons') | |
| cGui().setEndOfDirectory() | |
| def showEpisodes(): | |
| params = ParameterHandler() | |
| # Parameter laden | |
| sUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue("sThumbnail") | |
| sSeason = params.getValue('season') | |
| sShowName = params.getValue('TVShowTitle') | |
| oRequest = cRequestHandler(sUrl, bypass_dns=True) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 4 # HTML Cache Zeit 4 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = r'<div[^>]*class="staffelWrapperLoop[^"]*"[^>]*data-sid="%s">(.*?)</ul></div>' % sSeason | |
| isMatch, sContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| pattern = 'href="([^"]+)' | |
| isMatch, aResult = cParser.parse(sContainer, pattern) | |
| isDesc, sDesc = cParser.parseSingleResult(sHtmlContent, '"description">([^<]+)') | |
| total = len(aResult) | |
| for sUrl in aResult: | |
| isMatch, sName = cParser.parseSingleResult(sUrl, r'e(\d+)') | |
| oGuiElement = cGuiElement('Episode ' + str(sName), SITE_IDENTIFIER, 'showHosters') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| oGuiElement.setTVShowTitle(sShowName) | |
| oGuiElement.setSeason(sSeason) | |
| oGuiElement.setEpisode(sName) | |
| oGuiElement.setMediaType('episode') | |
| if sUrl.startswith('//'): | |
| params.setParam('entryUrl', 'https:' + sUrl) | |
| else: | |
| params.setParam('entryUrl', sUrl) | |
| if isDesc: | |
| oGuiElement.setDescription(sDesc) | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| cGui().setView('episodes') | |
| cGui().setEndOfDirectory() | |
| def showHosters(): | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| if '-english' in sUrl: sLang = '(EN)' | |
| else: sLang = '' | |
| sHtmlContent = cRequestHandler(sUrl, caching=False, bypass_dns=True).request() | |
| pattern = 'hostName">([^<]+).*?(http[^"]+)' # Hoster Link | |
| releaseQuality = r'class="rb">.*?(\d\d\d+)p\.' # Release Qualität | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| isQuality, sQuality = cParser.parseSingleResult(sHtmlContent, releaseQuality) # sReleaseQuality auslesen z.B. 1080 | |
| if not isQuality: sQuality = '720' | |
| hosters = [] | |
| if isMatch: | |
| for sName, sUrl in aResult: | |
| sName = sName.split(' HD')[0].strip() | |
| if 'Filemoon' in sName or 'Swiftload' in sName or 'Vidhide' in sName: | |
| sUrl = sUrl + '$$https://filmpalast.to/' # Referer hinzugefügt | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| hoster = {'link': sUrl, 'name': sName, 'displayedName': '%s [I]%s [%sp][/I]' % (sName, sLang, sQuality), 'languageCode': sLang, 'quality': sQuality} # Qualität Anzeige aus Release Eintrag | |
| hosters.append(hoster) | |
| else: | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| hoster = {'link': sUrl, 'name': sName, 'displayedName': '%s [I]%s [%sp][/I]' % (sName, sLang, sQuality), 'languageCode': sLang, 'quality': sQuality} # Qualität Anzeige aus Release Eintrag | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| showEntries(URL_SEARCH % cParser.quotePlus(sSearchText), oGui, sSearchText) | |
| def showSeriesMenu(): # Menu structure of series menu | |
| params = ParameterHandler() | |
| params.setParam('sUrl', URL_SERIES) | |
| cGui().addFolder(cGuiElement('Alle ' + cConfig().getLocalizedString(30511), SITE_IDENTIFIER, 'showEntries'), params) # Alle Serien | |
| params.setParam('value', 'movietitle') | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showValue'), params) # Von A bis Z | |
| cGui().setEndOfDirectory() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| # Always pay attention to the translations in the menu! | |
| # HTML LangzeitCache hinzugefügt | |
| # showGenre: 48 Stunden | |
| # showYears: 48 Stunden | |
| # showEpisodes: 4 Stunden | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| SITE_IDENTIFIER = 'hdfilme' | |
| SITE_NAME = 'HD Filme' | |
| SITE_ICON = 'hdfilme.png' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| # Domain Abfrage | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'hdfilme.garden') # Domain Auswahl über die xStream Einstellungen möglich | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| URL_MAIN = 'https://' + DOMAIN + '/' | |
| # URL_MAIN = 'https://hdfilme.im/' | |
| URL_NEW = URL_MAIN + 'kinofilme-online/' | |
| URL_KINO = URL_MAIN + 'aktuelle-kinofilme-im-kino/' | |
| URL_MOVIES = URL_MAIN + 'kinofilme-online' | |
| URL_SERIES = URL_MAIN + 'serienstream-deutsch/' | |
| URL_SEARCH = URL_MAIN + 'index.php?do=search&subaction=search&story=%s' | |
| # | |
| def load(): # Menu structure of the site plugin | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| params.setParam('sUrl', URL_NEW) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30500), SITE_IDENTIFIER, 'showEntries'), params) # New | |
| params.setParam('sUrl', URL_KINO) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30501), SITE_IDENTIFIER, 'showEntries'), params) # Current films in the cinema | |
| params.setParam('sUrl', URL_MOVIES) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502), SITE_IDENTIFIER, 'showEntries'), params) # Movies | |
| cGui().addFolder(cGuiElement('Jahr', SITE_IDENTIFIER, 'showYearSearch')) # New Year entry | |
| params.setParam('sUrl', URL_SERIES) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511), SITE_IDENTIFIER, 'showEntries'), params) # Series | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params)# Search | |
| cGui().setEndOfDirectory() | |
| def showGenre(entryUrl=False): | |
| params = ParameterHandler() | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 48 # 48 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = 'Genre">Genre.*?</ul>' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'href="([^"]+).*?>([^<]+)') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| if sUrl.startswith('/'): | |
| sUrl = URL_MAIN + sUrl | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showCountry(entryUrl=False): | |
| params = ParameterHandler() | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 48 # 48 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = 'Land?">Land.*?</ul>' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'href="([^"]+).*?>([^<]+)') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| if sUrl.startswith('/'): | |
| sUrl = URL_MAIN + sUrl | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False, sSearchText=False, sSearchPageText = False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| isTvshow = False | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False)) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # 6 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = '<div class="box-product(.*?)<h3.*?href="([^"]+).*?">([^<]+).*?(.*?)</li>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sInfo, sUrl, sName, sDummy in aResult: | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| # Abfrage der voreingestellten Sprache | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if (sLanguage == '1' and 'English*' in sName): # Deutsch | |
| continue | |
| if (sLanguage == '2' and not 'English*' in sName): # English | |
| continue | |
| elif sLanguage == '3': # Japanisch | |
| cGui().showLanguage() | |
| continue | |
| isThumbnail, sThumbnail = cParser.parseSingleResult(sInfo, 'data-src="([^"]+)') # Thumbnail | |
| isYear, sYear = cParser.parseSingleResult(sDummy, r'([\d]+)\s</p>') # Release Jahr | |
| isQuality, sQuality = cParser.parseSingleResult(sDummy, 'quality-product">([^<]+)') # Qualität | |
| isTvshow = True if 'taffel' in sName else False | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showEpisodes' if isTvshow else 'showHosters') | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| if isThumbnail: | |
| sThumbnail = URL_MAIN + sThumbnail | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if isYear: | |
| oGuiElement.setYear(sYear) | |
| if isQuality: | |
| oGuiElement.setQuality(sQuality) | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| params.setParam('entryUrl', sUrl) | |
| params.setParam('sName', sName) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui and not sSearchText and not sSearchPageText: | |
| isMatchNextPage, sNextUrl = cParser.parseSingleResult(sHtmlContent, 'href="([^"]+)">›</a></div>') | |
| # Start Page Function | |
| isMatchSiteSearch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, 'class="pagination(.*?)</div></div>') | |
| if isMatchSiteSearch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, r'<span>([\d]+)</span>.*?nav_ext">.*?">([\d]+)</a>.*?href="([^"]+)') | |
| for sPageActive, sPageLast, sNextPage in aResult: | |
| #sPageName = '[I]Seitensuche starten >>> [/I] Seite ' + str(sPageActive) + ' von ' + str(sPageLast) + ' Seiten [I]<<<[/I]' | |
| sPageName = cConfig().getLocalizedString(30284) + str(sPageActive) + cConfig().getLocalizedString(30285) + str(sPageLast) + cConfig().getLocalizedString(30286) | |
| params.setParam('sNextPage', sNextPage) | |
| params.setParam('sPageLast', sPageLast) | |
| oGui.searchNextPage(sPageName, SITE_IDENTIFIER, 'showSearchPage', params) | |
| # End Page Function | |
| if isMatchNextPage: | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('tvshows' if isTvshow else 'movies') | |
| oGui.setEndOfDirectory() | |
| def showEpisodes(): | |
| params = ParameterHandler() | |
| entryUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue('sThumbnail') | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # 6 Stunden | |
| sHtmlContent = oRequest.request() | |
| isMatch, aResult = cParser.parse(sHtmlContent, '"><a href="#">([^<]+)') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| isDesc, sDesc = cParser.parseSingleResult(sHtmlContent, '"description"[^>]content="([^"]+)') | |
| total = len(aResult) | |
| for sName in aResult: | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showHosters') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if isDesc: | |
| oGuiElement.setDescription(sDesc) | |
| oGuiElement.setMediaType('episode') | |
| params.setParam('entryUrl', entryUrl) | |
| params.setParam('episode', sName) | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| cGui().setView('episodes') | |
| cGui().setEndOfDirectory() | |
| def showHosters(): | |
| hosters = [] | |
| sHtmlContent = cRequestHandler(ParameterHandler().getValue('entryUrl'), caching=False).request() | |
| if ParameterHandler().getValue('episode'): | |
| pass | |
| pattern = '%s<.*?</ul>' % ParameterHandler().getValue('episode') | |
| isMatch, sHtmlContent = cParser.parseSingleResult(sHtmlContent, pattern) | |
| isMatch, aResult = cParser.parse(sHtmlContent, 'link="([^"]+)') | |
| if isMatch: | |
| sQuality = '720' | |
| for sUrl in aResult: | |
| if 'youtube' in sUrl or 'dropload' in sUrl: | |
| continue | |
| elif sUrl.startswith('//'): | |
| sUrl = 'https:' + sUrl | |
| sName = cParser.urlparse(sUrl).split('.')[0].strip() | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| hoster = {'link': sUrl, 'name': sName, 'displayedName': '%s [I][%sp][/I]' % (sName, sQuality), 'quality': sQuality} | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| showEntries(URL_SEARCH % cParser.quotePlus(sSearchText), oGui, sSearchText) | |
| def showSearchPage(): # Suche für die Page Funktion | |
| params = ParameterHandler() | |
| sNextPage = params.getValue('sNextPage') # URL mit nächster Seite | |
| sPageLast = params.getValue('sPageLast') # Anzahl gefundener Seiten | |
| #sHeading = 'Bitte eine Zahl zwischen 1 und ' + str(sPageLast) + ' wählen.' | |
| sHeading = cConfig().getLocalizedString(30282) + str(sPageLast) | |
| sSearchPageText = cGui().showKeyBoard(sHeading=sHeading) | |
| if not sSearchPageText: return | |
| sNextSearchPage = sNextPage.split('page/')[0].strip() + 'page/' + sSearchPageText + '/' | |
| showEntries(sNextSearchPage) | |
| cGui().setEndOfDirectory() | |
| def showYearSearch(): | |
| sYear = cGui().showKeyBoard(sHeading="Jahr eintragen (z.B., 2017)") | |
| if not sYear: return | |
| searchUrl = URL_MAIN + '/xfsearch/' + sYear | |
| showEntries(searchUrl) | |
| cGui().setEndOfDirectory() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| # Always pay attention to the translations in the menu! | |
| # HTML LangzeitCache hinzugefügt | |
| # showValue: 48 Stunden | |
| # showEntries: 6 Stunden | |
| # showEpisodes: 4 Stunden | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| SITE_IDENTIFIER = 'hdfilme_1' | |
| SITE_NAME = 'FHD Filme' | |
| SITE_ICON = 'hdfilme_1.png' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| # Domain Abfrage | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'hdfilme.legal') # Domain Auswahl über die xStream Einstellungen möglich | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| URL_MAIN = 'https://' + DOMAIN | |
| # URL_MAIN = 'https://hdfilme.my' | |
| URL_NEW = URL_MAIN + '/filme1/' | |
| URL_KINO = URL_MAIN + '/kinofilme/' | |
| URL_MOVIES = URL_MAIN | |
| URL_SERIES = URL_MAIN + '/serien/' | |
| URL_SEARCH = URL_MAIN + '/?story=%s&do=search&subaction=search' | |
| # | |
| def load(): # Menu structure of the site plugin | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| params.setParam('sUrl', URL_NEW) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30500), SITE_IDENTIFIER, 'showEntries'), params) # New | |
| params.setParam('sUrl', URL_KINO) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30501), SITE_IDENTIFIER, 'showEntries'), params) # Current films in the cinema | |
| params.setParam('sUrl', URL_MOVIES) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502), SITE_IDENTIFIER, 'showEntries'), params) # Movies | |
| params.setParam('sUrl', URL_SERIES) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511), SITE_IDENTIFIER, 'showEntries'), params) # Series | |
| cGui().addFolder(cGuiElement('Jahr', SITE_IDENTIFIER, 'showYearSearch')) # New Year entry | |
| params.setParam('Value', 'Genre') | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30506), SITE_IDENTIFIER, 'showValue'), params) # Genre | |
| params.setParam('Value', 'Land') | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30538), SITE_IDENTIFIER, 'showValue'), params) # Country | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params) # Search | |
| cGui().setEndOfDirectory() | |
| def showValue(): | |
| params = ParameterHandler() | |
| oRequest = cRequestHandler(URL_MAIN) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 48 # 48 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = '>{0}</(.*?)</a[^<]*</div>'.format(params.getValue('Value')) | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'href="([^"]+).*?>([^<]+)') | |
| for sUrl, sName in aResult: | |
| if sUrl.startswith('/'): | |
| sUrl = URL_MAIN + sUrl | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries'), params) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| cGui().setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False, sSearchText=False, sSearchPageText = False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| isTvshow = False | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False)) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # 6 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = 'class="item relative mt-3">.*?href="([^"]+).*?title="([^"]+).*?data-src="([^"]+)(.*?)</div></div>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sUrl, sName, sThumbnail, sDummy in aResult: | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| isYear, sYear = cParser.parseSingleResult(sDummy, r'mt-1">[^<]*<span>([\d]+)</span>') # Release Jahr | |
| isDuration, sDuration = cParser.parseSingleResult(sDummy, r'<span>([\d]+)\smin</span>') # Laufzeit | |
| if int(sDuration) <= int('70'): # Wenn Laufzeit kleiner oder gleich 70min, dann ist es eine Serie. | |
| isTvshow = True | |
| else: | |
| from resources.lib.tmdb import cTMDB | |
| oMetaget = cTMDB() | |
| if not oMetaget: | |
| isTvshow = False | |
| else: | |
| if isYear: | |
| meta = oMetaget.search_movie_name(sName, year=sYear) | |
| else: | |
| meta = oMetaget.search_movie_name(sName) | |
| if meta and 'id' in meta: | |
| isTvshow = False | |
| else: | |
| isTvshow = True | |
| if 'South Park: The End Of Obesity' in sName: | |
| isTvshow = False | |
| isQuality, sQuality = cParser.parseSingleResult(sDummy, '">([^<]+)</span>') # Qualität | |
| sThumbnail = URL_MAIN + sThumbnail | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showSeasons' if isTvshow else 'showHosters') | |
| if isYear: | |
| oGuiElement.setYear(sYear) | |
| if isDuration: | |
| oGuiElement.addItemValue('duration', sDuration) | |
| if isQuality: | |
| oGuiElement.setQuality(sQuality) | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| params.setParam('entryUrl', sUrl) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui and not sSearchText and not sSearchPageText: | |
| isMatchNextPage, sNextUrl = cParser.parseSingleResult(sHtmlContent, 'nav_ext">.*?next">.*?href="([^"]+)') | |
| # Start Page Function | |
| isMatchSiteSearch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, 'class="pages">(.*?)<svg') | |
| if isMatchSiteSearch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, r'<span>([\d]+)</span>.*?">([\d]+)</a></div>.*?ref="([^"]+)') | |
| for sPageActive, sPageLast, sNextPage in aResult: | |
| #sPageName = '[I]Seitensuche starten >>> [/I] Seite ' + str(sPageActive) + ' von ' + str(sPageLast) + ' Seiten [I]<<<[/I]' | |
| sPageName = cConfig().getLocalizedString(30284) + str(sPageActive) + cConfig().getLocalizedString(30285) + str(sPageLast) + cConfig().getLocalizedString(30286) | |
| params.setParam('sNextPage', sNextPage) | |
| params.setParam('sPageLast', sPageLast) | |
| oGui.searchNextPage(sPageName, SITE_IDENTIFIER, 'showSearchPage', params) | |
| # End Page Function | |
| if isMatchNextPage: | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('tvshows' if isTvshow else 'movies') | |
| oGui.setEndOfDirectory() | |
| def showSeasons(): | |
| params = ParameterHandler() | |
| # Parameter laden | |
| sUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue('sThumbnail') | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # HTML Cache Zeit 6 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = 'class="su-accordion collapse show"(.*?)<br>' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, r'#se-ac-(\d+)') | |
| total = len(aResult) | |
| for sSeason in aResult: | |
| oGuiElement = cGuiElement('Staffel ' + str(sSeason), SITE_IDENTIFIER, 'showEpisodes') | |
| oGuiElement.setSeason(sSeason) | |
| oGuiElement.setMediaType('season') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| cGui().addFolder(oGuiElement, params, True, total) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| cGui().setView('seasons') | |
| cGui().setEndOfDirectory() | |
| def showEpisodes(): | |
| params = ParameterHandler() | |
| # Parameter laden | |
| entryUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue('sThumbnail') | |
| sSeason = params.getValue('season') | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 4 # HTML Cache Zeit 4 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = '#se-ac-%s(.*?)</div></div>' % sSeason | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, r'Episode\s(\d+)') | |
| total = len(aResult) | |
| for sEpisode in aResult: | |
| oGuiElement = cGuiElement('Episode ' + str(sEpisode), SITE_IDENTIFIER, 'showEpisodeHosters') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| oGuiElement.setMediaType('episode') | |
| params.setParam('entryUrl', entryUrl) | |
| params.setParam('season', sSeason) | |
| params.setParam('episode', sEpisode) | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| cGui().setView('episodes') | |
| cGui().setEndOfDirectory() | |
| def showEpisodeHosters(): | |
| hosters = [] | |
| params = ParameterHandler() | |
| # Parameter laden | |
| sUrl = params.getValue('entryUrl') | |
| sSeason = params.getValue('season') | |
| sEpisode = params.getValue('episode') | |
| sHtmlContent = cRequestHandler(sUrl, caching=False).request() | |
| pattern = '#se-ac-%s(.*?)</div></div>' % sSeason | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| pattern = r'x%s\sEpisode(.*?)<br' % sEpisode | |
| isMatch, sHtmlLink = cParser.parseSingleResult(sHtmlContainer, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlLink, 'href="([^"]+)') | |
| if isMatch: | |
| sQuality = '720' | |
| for sUrl in aResult: | |
| if 'youtube' in sUrl: | |
| continue | |
| elif sUrl.startswith('//'): | |
| sUrl = 'https:' + sUrl | |
| sName = cParser.urlparse(sUrl).split('.')[0].strip() | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| hoster = {'link': sUrl, 'name': sName, 'displayedName': '%s [I][%sp][/I]' % (sName, sQuality), 'quality': sQuality} | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def showHosters(): | |
| hosters = [] | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sHtmlContent = cRequestHandler(sUrl, caching=False).request() | |
| pattern = r'<iframe\sw.*?src="([^"]+)' | |
| isMatch, hUrl = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| sHtmlContainer = cRequestHandler(hUrl).request() | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'data-link="([^"]+)') | |
| if isMatch: | |
| sQuality= '720' | |
| for sUrl in aResult: | |
| if 'youtube' in sUrl: | |
| continue | |
| elif sUrl.startswith('//'): | |
| sUrl = 'https:' + sUrl | |
| sName = cParser.urlparse(sUrl).split('.')[0].strip() | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| hoster = {'link': sUrl, 'name': sName, 'displayedName': '%s [I][%sp][/I]' % (sName, sQuality), 'quality': sQuality} | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| showEntries(URL_SEARCH % cParser.quotePlus(sSearchText), oGui, sSearchText) | |
| def showSearchPage(): # Suche für die Page Funktion | |
| params = ParameterHandler() | |
| sNextPage = params.getValue('sNextPage') # URL mit nächster Seite | |
| sPageLast = params.getValue('sPageLast') # Anzahl gefundener Seiten | |
| #sHeading = 'Bitte eine Zahl zwischen 1 und ' + str(sPageLast) + ' wählen.' | |
| sHeading = cConfig().getLocalizedString(30282) + str(sPageLast) | |
| sSearchPageText = cGui().showKeyBoard(sHeading=sHeading) | |
| if not sSearchPageText: return | |
| sNextSearchPage = sNextPage.split('page/')[0].strip() + 'page/' + sSearchPageText + '/' | |
| showEntries(sNextSearchPage) | |
| cGui().setEndOfDirectory() | |
| def showYearSearch(): | |
| sYear = cGui().showKeyBoard(sHeading="Jahr eintragen (z.B., 2017)") | |
| if not sYear: return | |
| searchUrl = URL_MAIN + '/xfsearch/' + sYear | |
| showEntries(searchUrl) | |
| cGui().setEndOfDirectory() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| # Always pay attention to the translations in the menu! | |
| # HTML LangzeitCache hinzugefügt | |
| # showGenre: 48 Stunden | |
| # showEntries: 6 Stunden | |
| # showSeasons: 6 Stunden | |
| # showEpisodes: 4 Stunden | |
| import base64, binascii, hashlib, re, json, pyaes | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| from itertools import zip_longest as ziplist | |
| SITE_IDENTIFIER = 'kinoger' | |
| SITE_NAME = 'KinoGer' | |
| SITE_ICON = 'kinoger.png' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| # Domain Abfrage | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain') # Domain Auswahl über die xStream Einstellungen möglich | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| URL_MAIN = 'https://' + DOMAIN | |
| # URL_MAIN = 'https://kinoger.to' | |
| URL_SERIES = URL_MAIN + '/stream/serie/' | |
| # | |
| def load(): # Menu structure of the site plugin | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| params.setParam('sUrl', URL_MAIN) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30500), SITE_IDENTIFIER, 'showEntries'), params) # New | |
| params.setParam('sUrl', URL_SERIES) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511), SITE_IDENTIFIER, 'showEntries'), params) # Series | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30506), SITE_IDENTIFIER, 'showGenre'), params) # Genre | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params) # Search | |
| cGui().setEndOfDirectory() | |
| def showGenre(): | |
| params = ParameterHandler() | |
| oRequest = cRequestHandler(URL_MAIN) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 48 # 48 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = '<li[^>]class="links"><a href="([^"]+).*?/>([^<]+)</a>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| params.setParam('sUrl', URL_MAIN + sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False, sSearchText=False, sSearchPageText = False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False)) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # 6 Stunden | |
| if sSearchText: | |
| oRequest.addParameters('story', sSearchText) | |
| oRequest.addParameters('do', 'search') | |
| oRequest.addParameters('subaction', 'search') | |
| oRequest.addParameters('x', '0') | |
| oRequest.addParameters('y', '0') | |
| oRequest.addParameters('titleonly', '3') | |
| oRequest.addParameters('submit', 'submit') | |
| else: | |
| oRequest.addParameters('dlenewssortby', 'date') | |
| oRequest.addParameters('dledirection', 'desc') | |
| oRequest.addParameters('set_new_sort', 'dle_sort_main') | |
| oRequest.addParameters('set_direction_sort', 'dle_direction_main') | |
| sHtmlContent = oRequest.request() | |
| pattern = 'class="title".*?href="([^"]+)">([^<]+).*?src="([^"]+)(.*?)</a> </span>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sUrl, sName, sThumbnail, sDummy in aResult: | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| isTvshow = True if 'staffel' in sName.lower() or 'serie' in entryUrl or ';">S0' in sDummy else False | |
| isYear, sYear = cParser.parse(sName, r'(.*?)\s+\((\d+)\)') # Jahr und Name trennen | |
| if isYear: | |
| for name, year in sYear: | |
| sName = name | |
| sYear = year | |
| break | |
| if sThumbnail.startswith('/'): | |
| sThumbnail = URL_MAIN + sThumbnail | |
| isDesc, sDesc = cParser.parseSingleResult(sDummy, '(?:</b></div>|</div></b>|</b>)([^<]+)') # Beschreibung | |
| isDuration, sDuration = cParser.parseSingleResult(sDummy, r'(?:Laufzeit|Spielzeit).*?([\d]+)') # Laufzeit | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showSeasons' if isTvshow else 'showHosters') | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if isYear: | |
| oGuiElement.setYear(sYear) | |
| if isDesc: | |
| oGuiElement.setDescription(sDesc) | |
| if isDuration: | |
| oGuiElement.addItemValue('duration', sDuration) | |
| # Parameter übergeben | |
| params.setParam('sThumbnail', sThumbnail) | |
| params.setParam('TVShowTitle', sName) | |
| params.setParam('entryUrl', sUrl) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui and not sSearchText and not sSearchPageText: | |
| isMatchNextPage, sNextUrl = cParser.parseSingleResult(sHtmlContent, '<a[^>]href="([^"]+)">vorw') | |
| # Start Page Function | |
| isMatchSiteSearch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, 'class="navigation(.*?)</a></div>') | |
| if isMatchSiteSearch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, r'<span>([\d]+)</span>.*?nav_ext">.*?">([\d]+).*?href="([^"]+)') | |
| for sPageActive, sPageLast, sNextPage in aResult: | |
| #sPageName = '[I]Seitensuche starten >>> [/I] Seite ' + str(sPageActive) + ' von ' + str(sPageLast) + ' Seiten [I]<<<[/I]' | |
| sPageName = cConfig().getLocalizedString(30284) + str(sPageActive) + cConfig().getLocalizedString(30285) + str(sPageLast) + cConfig().getLocalizedString(30286) | |
| params.setParam('sNextPage', sNextPage) | |
| params.setParam('sPageLast', sPageLast) | |
| oGui.searchNextPage(sPageName, SITE_IDENTIFIER, 'showSearchPage', params) | |
| # End Page Function | |
| if isMatchNextPage: | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('tvshows' if 'staffel' in sName.lower() else 'movies') | |
| oGui.setEndOfDirectory() | |
| def showSeasons(): | |
| params = ParameterHandler() | |
| # Parameter laden | |
| entryUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue('sThumbnail') | |
| sTVShowTitle = params.getValue('TVShowTitle') | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # HTML Cache Zeit 6 Stunden | |
| sHtmlContent = oRequest.request() | |
| L11 = [] | |
| isMatchsst, sstsContainer = cParser.parseSingleResult(sHtmlContent, 'sst.show.*?</script>') | |
| if isMatchsst: | |
| sstsContainer = sstsContainer.replace('[', '<').replace(']', '>') | |
| isMatchsst, L11 = cParser.parse(sstsContainer, "<'([^>]+)") | |
| if isMatchsst: | |
| total = len(L11) | |
| L22 = [] | |
| isMatchollhd, ollhdsContainer = cParser.parseSingleResult(sHtmlContent, 'ollhd.show.*?</script>') | |
| if isMatchollhd: | |
| ollhdsContainer = ollhdsContainer.replace('[', '<').replace(']', '>') | |
| isMatchollhd, L22 = cParser.parse(ollhdsContainer, "<'([^>]+)") | |
| if isMatchollhd: | |
| total = len(L22) | |
| L33 = [] | |
| isMatchpw, pwsContainer = cParser.parseSingleResult(sHtmlContent, 'pw.show.*?</script>') | |
| if isMatchpw: | |
| pwsContainer = pwsContainer.replace('[', '<').replace(']', '>') | |
| isMatchpw, L33 = cParser.parse(pwsContainer, "<'([^>]+)") | |
| if isMatchpw: | |
| total = len(L33) | |
| L44 = [] | |
| isMatchgo, gosContainer = cParser.parseSingleResult(sHtmlContent, 'go.show.*?</script>') | |
| if isMatchgo: | |
| gosContainer = gosContainer.replace('[', '<').replace(']', '>') | |
| isMatchgo, L44 = cParser.parse(gosContainer, "<'([^>]+)") | |
| if isMatchgo: | |
| total = len(L44) | |
| isDesc, sDesc = cParser.parseSingleResult(sHtmlContent, '</b>([^"]+)<br><br>') | |
| for i in range(0, total): | |
| try: | |
| params.setParam('L11', L11[i]) | |
| except Exception: | |
| pass | |
| try: | |
| params.setParam('L22', L22[i]) | |
| except Exception: | |
| pass | |
| try: | |
| params.setParam('L33', L33[i]) | |
| except Exception: | |
| pass | |
| try: | |
| params.setParam('L44', L44[i]) | |
| except Exception: | |
| pass | |
| i = i + 1 | |
| oGuiElement = cGuiElement('Staffel ' + str(i), SITE_IDENTIFIER, 'showEpisodes') | |
| oGuiElement.setMediaType('season') | |
| oGuiElement.setTVShowTitle(sTVShowTitle) | |
| oGuiElement.setSeason(i) | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if isDesc: | |
| oGuiElement.setDescription(sDesc) | |
| params.setParam('sDesc', sDesc) | |
| params.setParam('sSeasonNr', i) | |
| cGui().addFolder(oGuiElement, params, True, total) | |
| cGui().setView('seasons') | |
| cGui().setEndOfDirectory() | |
| def showEpisodes(): | |
| params = ParameterHandler() | |
| sSeasonNr = params.getValue('sSeasonNr') | |
| sThumbnail = params.getValue('sThumbnail') | |
| sTVShowTitle = params.getValue('TVShowTitle') | |
| sDesc = params.getValue('sDesc') | |
| L11 = [] | |
| if params.exist('L11'): | |
| L11 = params.getValue('L11') | |
| isMatch1, L11 = cParser.parse(L11, "(http[^']+)") | |
| L22 = [] | |
| if params.exist('L22'): | |
| L22 = params.getValue('L22') | |
| isMatch, L22 = cParser.parse(L22, "(http[^']+)") | |
| L33 = [] | |
| if params.exist('L33'): | |
| L33 = params.getValue('L33') | |
| isMatch3, L33 = cParser.parse(L33, "(http[^']+)") | |
| L44 = [] | |
| if params.exist('L44'): | |
| L44 = params.getValue('L44') | |
| isMatch4, L44 = cParser.parse(L44, "(http[^']+)") | |
| liste = ziplist(L11, L22, L33, L44) | |
| i = 0 | |
| for sUrl in liste: | |
| i = i + 1 | |
| oGuiElement = cGuiElement('Episode ' + str(i), SITE_IDENTIFIER, 'showHosters') | |
| oGuiElement.setTVShowTitle(sTVShowTitle) | |
| oGuiElement.setSeason(sSeasonNr) | |
| oGuiElement.setEpisode(i) | |
| oGuiElement.setMediaType('episode') | |
| if sDesc: | |
| oGuiElement.setDescription(sDesc) | |
| if sThumbnail: | |
| oGuiElement.setThumbnail(sThumbnail) | |
| params.setParam('sLinks', sUrl) | |
| cGui().addFolder(oGuiElement, params, False) | |
| cGui().setView('episodes') | |
| cGui().setEndOfDirectory() | |
| def showHosters(): | |
| hosters = [] | |
| headers = '&Accept-Language=de%2Cde-DE%3Bq%3D0.9%2Cen%3Bq%3D0.8%2Cen-GB%3Bq%3D0.7%2Cen-US%3Bq%3D0.6&Accept=%2A%2F%2A&User-Agent=Mozilla%2F5.0+%28Windows+NT+10.0%3B+Win64%3B+x64%3B+rv%3A99.0%29+Gecko%2F20100101+Firefox%2F99.0' | |
| params = ParameterHandler() | |
| if params.exist('sLinks'): | |
| sUrl = params.getValue('sLinks') | |
| isMatch, aResult = cParser.parse(sUrl, "(http[^']+)") | |
| else: | |
| sUrl = params.getValue('entryUrl') | |
| sHtmlContent = cRequestHandler(sUrl, ignoreErrors=True, caching=False).request() | |
| pattern = r"show[^>]\d,[^>][^>]'([^']+)" | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if isMatch: | |
| for sUrl in aResult: | |
| try: | |
| if 'kinoger.ru' in sUrl: continue | |
| # oRequest = cRequestHandler(sUrl, caching=False, ignoreErrors=True) | |
| # oRequest.addHeaderEntry('Referer', 'https://kinoger.com/') | |
| # sHtmlContent = oRequest.request() # Durchsucht sHtml Content | |
| # if isMatch: | |
| # decryptHtmlContent = content_decryptor(sHtmlContent, 'H&5+Tx_nQcdK{U,.') # Decrypt Content | |
| # isMatch, hUrl = cParser.parseSingleResult(decryptHtmlContent, 'sources.*?file.*?(http[^"]+)') | |
| # if isMatch: | |
| # hUrl = hUrl.replace('\\', '') | |
| # oRequest = cRequestHandler(hUrl, caching=False, ignoreErrors=True) | |
| # oRequest.addHeaderEntry('Referer', 'https://kinoger.ru/') | |
| # oRequest.addHeaderEntry('Origin', 'https://kinoger.ru') | |
| # oRequest.removeNewLines(False) | |
| # sHtmlContent = oRequest.request() | |
| # if not 'MEDIA:TYPE=AUDIO' in sHtmlContent: # Wenn keine zusätzlichen Audiostreams vorhanden durchsuche m3u8 und filter Links aus | |
| # pattern = 'RESOLUTION=.*?x(\d+).*?\n([^\s]+)' | |
| # isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| # if isMatch: | |
| # for sQuality, sUrl in aResult: | |
| # sUrl = (hUrl.split('video')[0].strip() + sUrl.strip()) | |
| # sUrl = sUrl + '|verifypeer=false&Referer=https%3A%2F%2Fkinoger.ru%2F&Origin=https%3A%2F%2Fkinoger.ru' + headers | |
| # hoster = {'link': sUrl, 'name': 'KinoGer.ru [I][%sp][/I]' % sQuality, 'quality': sQuality, 'resolveable': True} | |
| # hosters.append(hoster) | |
| # else: # Wenn Audiostreams enthalten nutze video.m3u8 und lese Content daraus | |
| # sUrl = hUrl + '|verifypeer=false&Referer=https%3A%2F%2Fkinoger.ru%2F&Origin=https%3A%2F%2Fkinoger.ru' + headers | |
| # hoster = {'link': sUrl, 'name': 'KinoGer.ru [I][Video/Audio auswählbar][/I]', 'resolveable': True} | |
| # hosters.append(hoster) | |
| elif 'kinoger.be' in sUrl: | |
| oRequest = cRequestHandler(sUrl, caching=False, ignoreErrors=True) | |
| oRequest.addHeaderEntry('Referer', 'https://kinoger.com/') | |
| sHtmlContent = oRequest.request() | |
| # Wenn Content p.a.c.k.e.d ist dann entpacken | |
| isMatch, packed = cParser.parseSingleResult(sHtmlContent, r'(eval\s*\(function.*?)</script>') | |
| if isMatch: | |
| from resources.lib import jsunpacker | |
| sHtmlContent = jsunpacker.unpack(packed) | |
| isMatch, hUrl = cParser.parseSingleResult(sHtmlContent, 'sources.*?file.*?(http[^"]+)') | |
| if isMatch: | |
| hUrl = hUrl.replace('\\', '') | |
| oRequest = cRequestHandler(hUrl, caching=False, ignoreErrors=True) | |
| oRequest.addHeaderEntry('Referer', 'https://kinoger.be/') | |
| oRequest.addHeaderEntry('Origin', 'https://kinoger.be') | |
| oRequest.removeNewLines(False) | |
| sHtmlContent = oRequest.request() | |
| if 'CF-DDOS-GUARD aktiv' in sHtmlContent: # Wenn Request eine 403 zurückgibt dann überspringen | |
| continue | |
| else: | |
| pattern = r'RESOLUTION=.*?x(\d+).*?\n(index[^\n]+)' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if isMatch: | |
| for sQuality, sUrl in aResult: | |
| sUrl = (hUrl.split('video')[0].strip() + sUrl.strip()) | |
| sUrl = sUrl + '|Origin=https%3A%2F%2Fkinoger.be&Referer=https%3A%2F%2Fkinoger.be%2F' + headers | |
| hoster = {'link': sUrl, 'name': 'KinoGer.be [I][%sp][/I]' % sQuality, 'quality': sQuality, 'resolveable': True} | |
| hosters.append(hoster) | |
| elif 'kinoger.pw' in sUrl: # Veev | |
| sQuality = '720' | |
| hoster = {'link': sUrl + 'DIREKT', 'name': 'Veev.to [I][%sp][/I]'% sQuality, 'quality': sQuality} | |
| hosters.append(hoster) | |
| elif 'kinoger.re' in sUrl: | |
| sQuality = '1080' | |
| hoster = {'link': sUrl + 'DIREKT', 'name': 'Kinoger.re [I][%sp][/I]'% sQuality, 'quality': sQuality, 'resolveable': True} | |
| hosters.append(hoster) | |
| else: # Alle anderen Hoster wie z.B. Voe | |
| sQuality = '720' | |
| sName = cParser.urlparse(sUrl) | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| hoster = {'link': sUrl + 'DIREKT', 'name': sName, 'displayedName': '%s [I][%sp][/I]' % (sName, sQuality), 'quality': sQuality} | |
| hosters.append(hoster) | |
| except Exception: | |
| pass | |
| if not isMatch: | |
| return | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| if sUrl.endswith('DIREKT'): | |
| sUrl = sUrl[:-6] | |
| Request = cRequestHandler(sUrl, caching=False) | |
| Request.request() | |
| sUrl = Request.getRealUrl() # hole reale URL von der Umleitung | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| else: | |
| return [{'streamUrl': sUrl, 'resolved': True}] | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| showEntries(URL_MAIN, oGui, sSearchText) | |
| def showSearchPage(): # Suche für die Page Funktion | |
| params = ParameterHandler() | |
| sNextPage = params.getValue('sNextPage') # URL mit nächster Seite | |
| sPageLast = params.getValue('sPageLast') # Anzahl gefundener Seiten | |
| #sHeading = 'Bitte eine Zahl zwischen 1 und ' + str(sPageLast) + ' wählen.' | |
| sHeading = cConfig().getLocalizedString(30282) + str(sPageLast) | |
| sSearchPageText = cGui().showKeyBoard(sHeading=sHeading) | |
| if not sSearchPageText: return | |
| sNextSearchPage = sNextPage.split('page/')[0].strip() + 'page/' + sSearchPageText + '/' | |
| showEntries(sNextSearchPage) | |
| cGui().setEndOfDirectory() | |
| def content_decryptor(html_content,passphrase): | |
| match = re.compile(r'''JScripts = '(.+?)';''', re.DOTALL).search(html_content) | |
| if match: | |
| # Parse the JSON string | |
| json_obj = json.loads(match.group(1)) | |
| # Extract the salt, iv, and ciphertext from the JSON object | |
| salt = binascii.unhexlify(json_obj["s"]) | |
| iv = binascii.unhexlify(json_obj["iv"]) | |
| ct = base64.b64decode(json_obj["ct"]) | |
| # Concatenate the passphrase and the salt | |
| concated_passphrase = passphrase.encode() + salt | |
| # Compute the MD5 hashes | |
| md5 = [hashlib.md5(concated_passphrase).digest()] | |
| result = md5[0] | |
| i = 1 | |
| while len(result) < 32: | |
| md5.append(hashlib.md5(md5[i - 1] + concated_passphrase).digest()) | |
| result += md5[i] | |
| i += 1 | |
| # Extract the key from the result | |
| key = result[:32] | |
| # Decrypt the ciphertext using AES-256-CBC | |
| aes = pyaes.AESModeOfOperationCBC(key, iv) | |
| decrypter = pyaes.Decrypter(aes) | |
| plain_text = decrypter.feed(ct) | |
| plain_text += decrypter.feed() | |
| # Return the decrypted data as a JSON object | |
| return json.loads(plain_text.decode()) | |
| else: | |
| return None |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Always pay attention to the translations in the menu! | |
| # HTML LangzeitCache hinzugefügt | |
| # showGenre: 48 Stunden | |
| # showEntries: 6 Stunden | |
| # showEpisodes: 4 Stunden | |
| import re | |
| import xbmcgui | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser, cUtil | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| from json import loads | |
| from datetime import datetime | |
| # Globale Variable für die JSON-Daten | |
| apiJson = None | |
| # Domain Abfrage ### | |
| SITE_IDENTIFIER = 'kinokiste' | |
| SITE_NAME = 'Kinokiste' | |
| SITE_ICON = 'kinokistetech.png' | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'kinokiste.club') | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| ORIGIN = 'https://' + DOMAIN + '/' | |
| REFERER = ORIGIN + '/' | |
| URL_API = 'https://' + DOMAIN | |
| URL_MAIN = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&page=%s' | |
| URL_SEARCH = URL_API + '/data/browse/?lang=%s&order_by=%s&page=%s&limit=0' | |
| URL_THUMBNAIL = 'https://image.tmdb.org/t/p/w300%s' | |
| URL_WATCH = URL_API + '/data/watch/?_id=%s' | |
| URL_GENRE = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&genre=%s&page=%s' | |
| URL_CAST = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&cast=%s&page=%s' | |
| URL_YEAR = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&year=%s&page=%s' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| def load(): | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| # Änderung des Sprachcodes nach voreigestellter Sprache | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| elif sLanguage == '3': # prefLang Japanisch | |
| sLang = cGui().showLanguage() | |
| return | |
| params.setParam('sLanguage', sLang) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502), SITE_IDENTIFIER, 'showMovieMenu'), params) # Movies | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30551), SITE_IDENTIFIER, 'showGenreMMenu'), params) # Movies Genre | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511), SITE_IDENTIFIER, 'showSeriesMenu'), params) # Series | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30529), SITE_IDENTIFIER, 'showGenreSMenu'), params) # Series Genre | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30508), SITE_IDENTIFIER, 'showYearsMenu'), params) # Years | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30553), SITE_IDENTIFIER, 'showSearchActor'), params) # Cast | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params) # Search | |
| cGui().setEndOfDirectory() | |
| def _cleanTitle(sTitle): | |
| sTitle = re.sub("[\xE4]", 'ae', sTitle) | |
| sTitle = re.sub("[\xFC]", 'ue', sTitle) | |
| sTitle = re.sub("[\xF6]", 'oe', sTitle) | |
| sTitle = re.sub("[\xC4]", 'Ae', sTitle) | |
| sTitle = re.sub("[\xDC]", 'Ue', sTitle) | |
| sTitle = re.sub("[\xD6]", 'Oe', sTitle) | |
| sTitle = re.sub("[\x00-\x1F\x80-\xFF]", '', sTitle) | |
| return sTitle | |
| def _getQuality(sQuality): | |
| isMatch, aResult = cParser.parse(sQuality, '(HDCAM|HD|WEB|BLUERAY|BRRIP|DVD|TS|SD|CAM)', 1, True) | |
| if isMatch: | |
| return aResult[0] | |
| else: | |
| return sQuality | |
| def _showGenreMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| sType = params.getValue('sType') | |
| sMenu = params.getValue('sMenu') | |
| if sLanguage == '2' or sLanguage == 'all': | |
| genres = { | |
| 'Action': 'Action', | |
| 'Abenteuer': 'Abenteuer', | |
| 'Animation': 'Animation', | |
| 'Biographie': 'Biographie', | |
| 'Dokumentation': 'Dokumentation', | |
| 'Drama': 'Drama', | |
| 'Familie': 'Familie', | |
| 'Fantasy': 'Fantasy', | |
| 'Geschichte': 'Geschichte', | |
| 'Horror': 'Horror', | |
| 'Komödie': 'Komödie', | |
| 'Krieg': 'Krieg', | |
| 'Krimi': 'Krimi', | |
| 'Musik': 'Musik', | |
| 'Mystery': 'Mystery', | |
| 'Romantik': 'Romantik', | |
| 'Reality-TV': 'Reality-TV', | |
| 'Sci-Fi': 'Sci-Fi', | |
| 'Sports': 'Sport', | |
| 'Thriller': 'Thriller', | |
| 'Western': 'Western' | |
| } | |
| else: | |
| genres = { | |
| 'Action': 'Action', | |
| 'Adventure': 'Abenteuer', | |
| 'Animation': 'Animation', | |
| 'Biography': 'Biographie', | |
| 'Comedy': 'Komödie', | |
| 'Crime': 'Krimi', | |
| 'Documentation': 'Dokumentation', | |
| 'Drama': 'Drama', | |
| 'Family': 'Familie', | |
| 'Fantasy': 'Fantasy', | |
| 'History': 'Geschichte', | |
| 'Horror': 'Horror', | |
| 'Music': 'Musik', | |
| 'Mystery': 'Mystery', | |
| 'Romance': 'Romantik', | |
| 'Reality-TV': 'Reality-TV', | |
| 'Sci-Fi': 'Sci-Fi', | |
| 'Sports': 'Sport', | |
| 'Thriller': 'Thriller', | |
| 'War': 'Krieg', | |
| 'Western': 'Western' | |
| } | |
| for genre, searchGenre in genres.items(): | |
| params.setParam('sUrl', URL_GENRE % (sLanguage, sType, sMenu, searchGenre, '1')) | |
| cGui().addFolder(cGuiElement(genre, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showMovieMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'Trending', '1')) ### Trending Filme trending1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30521), SITE_IDENTIFIER, 'showEntries'), params) ### Trending Filme trending1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'new', '1')) ### neue filme neu1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30541), SITE_IDENTIFIER, 'showEntries'), params) ### neue filme neu1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'views', '1')) ### Views filme views1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30552), SITE_IDENTIFIER, 'showEntries'), params) ### Views filme views1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'rating', '1')) ### Rating Filme rating1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30510), SITE_IDENTIFIER, 'showEntries'), params) ### Rating Filme rating1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'votes', '1')) ### votes filme votes 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30536), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'updates', '1')) ### updates filme updates 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30533), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'name', '1')) ### name filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'featured', '1')) ### featured filme features1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30530), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'requested', '1')) ### requested filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30534), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'releases', '1')) ### releases filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30531), SITE_IDENTIFIER, 'showEntries'), params) ### Filme releases 1 | |
| cGui().setEndOfDirectory() | |
| def showGenreMMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sType', 'movies') | |
| params.setParam('sMenu', 'Trending') | |
| cGui().addFolder(cGuiElement('Genre trending', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Neu') | |
| cGui().addFolder(cGuiElement('Genre new', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Views') | |
| cGui().addFolder(cGuiElement('Genre viewed', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Votes') | |
| cGui().addFolder(cGuiElement('Genre voted', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Updates') | |
| cGui().addFolder(cGuiElement('Genre updated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Rating') | |
| cGui().addFolder(cGuiElement('Genre rated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Name') | |
| cGui().addFolder(cGuiElement('Genre named', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'requested') | |
| cGui().addFolder(cGuiElement('Genre requested', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'featured') | |
| cGui().addFolder(cGuiElement('Genre featured', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'releases') | |
| cGui().addFolder(cGuiElement('Genre released', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| cGui().setEndOfDirectory() | |
| # Serienmenue | |
| def showSeriesMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'neu', '1')) ### serien neu 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30514), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'views', '1')) ### serien views 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30537), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'votes', '1')) ### serien votes 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30519), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'updates', '1')) ### serien updates 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30533), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'name', '1')) ### serien name 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'featured', '1')) ### | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30530), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'requested', '1')) ### serien requested | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30534), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'releases', '1')) ### serien releases 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30531), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'rating', '1')) ### serien rating 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30535), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| #params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'Jahr', '1')) # ## | |
| #cGui().addFolder(cGuiElement('Jahr', SITE_IDENTIFIER, 'showEntries'), params) ## | |
| #params.setParam('sCont', 'Jahr') # | |
| #cGui().addFolder(cGuiElement('Jahr', SITE_IDENTIFIER, 'showValue'), params), params) # | |
| cGui().setEndOfDirectory() | |
| # show genre serien menue | |
| def showGenreSMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sType', 'tvseries') | |
| params.setParam('sMenu', 'Trending') | |
| cGui().addFolder(cGuiElement('Series genre trending', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Neu') | |
| cGui().addFolder(cGuiElement('Series genre new', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Views') | |
| cGui().addFolder(cGuiElement('Series genre viewed', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Votes') | |
| cGui().addFolder(cGuiElement('Series genre voted', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Updates') | |
| cGui().addFolder(cGuiElement('Series genre updated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Rating') | |
| cGui().addFolder(cGuiElement('Series genre rated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Name') | |
| cGui().addFolder(cGuiElement('Series genre named', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'requested') | |
| cGui().addFolder(cGuiElement('Series genre requested', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'featured') | |
| cGui().addFolder(cGuiElement('Series genre featured', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'releases') | |
| cGui().addFolder(cGuiElement('Series genre releases', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| cGui().setEndOfDirectory() | |
| def showYearsMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| # Anfangs- und Endjahr für das menü eintragen | |
| start_jahr = 1931 | |
| end_jahr = datetime.now().year | |
| # show the current year first | |
| for jahr in range(end_jahr, start_jahr - 1, -1): | |
| params.setParam('sUrl', URL_YEAR % (sLanguage, 'movies', 'new', str(jahr), '1')) | |
| cGui().addFolder(cGuiElement(str(jahr), SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| isTvshow = False | |
| sThumbnail = '' | |
| sLanguage = params.getValue('sLanguage') | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| try: | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # HTML Cache Zeit 6 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| aJson = loads(sJson) | |
| except: | |
| if not sGui: oGui.showInfo() | |
| return | |
| if 'movies' not in aJson or not isinstance(aJson.get('movies'), list) or len(aJson['movies']) == 0: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = 0 | |
| # ignore movies which does not contain any streams | |
| for movie in aJson['movies']: | |
| if '_id' in movie: | |
| total += 1 | |
| for movie in aJson['movies']: | |
| if not '_id' in movie: | |
| continue | |
| sTitle = str(movie['title']) | |
| if sSearchText and not cParser.search(sSearchText, sTitle): | |
| continue | |
| if 'Staffel' in sTitle or 'Season' in sTitle: | |
| isTvshow = True | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'showEpisodes' if isTvshow else 'showHosters') | |
| if 'poster_path_season' in movie and movie['poster_path_season']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path_season']) | |
| elif 'poster_path' in movie and movie['poster_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path']) | |
| elif 'backdrop_path' in movie and movie['backdrop_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['backdrop_path']) | |
| if sThumbnail: | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 'storyline' in movie: | |
| oGuiElement.setDescription(str(movie['storyline'])) | |
| elif 'overview' in movie: | |
| oGuiElement.setDescription(str(movie['overview'])) | |
| if 'year' in movie and len(str(movie['year'])) == 4: | |
| oGuiElement.setYear(movie['year']) | |
| if 'quality' in movie: | |
| oGuiElement.setQuality(_getQuality(movie['quality'])) | |
| if 'rating' in movie: | |
| oGuiElement.addItemValue('rating', movie['rating']) | |
| if 'lang' in movie: | |
| if (sLanguage != '1' and movie['lang'] == 2): # Deutsch | |
| oGuiElement.setLanguage('DE') | |
| if (sLanguage != '2' and movie['lang'] == 3): # Englisch | |
| oGuiElement.setLanguage('EN') | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| if 'runtime' in movie: | |
| isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], '\d+') | |
| if isMatch: | |
| oGuiElement.addItemValue('duration', sRuntime) | |
| params.setParam('entryUrl', URL_WATCH % str(movie['_id'])) | |
| params.setParam('sName', sTitle) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui and not sSearchText: | |
| curPage = aJson['pager']['currentPage'] | |
| if curPage < aJson['pager']['totalPages']: | |
| sNextUrl = entryUrl.replace('page=' + str(curPage), 'page=' + str(curPage + 1)) | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('tvshows' if isTvshow else 'movies') | |
| oGui.setEndOfDirectory() | |
| def showEpisodes(): | |
| aEpisodes = [] | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue("sThumbnail") | |
| try: | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 4 # HTML Cache Zeit 4 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| aJson = loads(sJson) | |
| except: | |
| cGui().showInfo() | |
| return | |
| if 'streams' not in aJson or len(aJson['streams']) == 0: | |
| cGui().showInfo() | |
| return | |
| for stream in aJson['streams']: | |
| if 'e' in stream: | |
| aEpisodes.append(int(stream['e'])) | |
| if aEpisodes: | |
| aEpisodesSorted = set(aEpisodes) | |
| total = len(aEpisodesSorted) | |
| for sEpisode in aEpisodesSorted: | |
| oGuiElement = cGuiElement('Episode ' + str(sEpisode), SITE_IDENTIFIER, 'showHosters') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 's' in aJson: | |
| oGuiElement.setSeason(aJson['s']) | |
| oGuiElement.setTVShowTitle('Episode ' + str(sEpisode)) | |
| oGuiElement.setEpisode(sEpisode) | |
| oGuiElement.setMediaType('episode') | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| cGui().setView('episodes') | |
| cGui().setEndOfDirectory() | |
| def showHosters(): | |
| hosters = [] | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sEpisode = params.getValue('episode') | |
| try: | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 8 # HTML Cache Zeit 8 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| except: | |
| return hosters | |
| if sJson: | |
| aJson = loads(sJson) | |
| if 'streams' in aJson: | |
| i = 0 | |
| for stream in aJson['streams']: | |
| if (('e' not in stream) or (str(sEpisode) == str(stream['e']))): | |
| sHoster = str(i) + ':' | |
| isMatch, aName = cParser.parse(stream['stream'], '//([^/]+)/') | |
| if isMatch: | |
| # sName = cParser.urlparse(sUrl) ### angezeigter hostername api | |
| sName = aName[0][:aName[0].rindex('.')] | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| sHoster = sHoster + ' ' + sName | |
| if 'release' in stream and str(stream['release']) != '': | |
| sHoster = sHoster + ' [I][' + _getQuality(stream['release']) + '][/I]' | |
| hoster = {'link': stream['stream'], 'name': sHoster} | |
| hosters.append(hoster) | |
| i += 1 | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearchActor(): | |
| sName = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30280)) | |
| if not sName: return | |
| _searchActor(False, sName) | |
| cGui().setEndOfDirectory() | |
| def _searchActor(oGui, sName): | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| showEntries(URL_CAST % (sLanguage, 'movies', 'new', cParser.quotePlus(sName), '1'), oGui) | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| SSsearch(oGui, sSearchText) | |
| def SSsearch(sGui=False, sSearchText=False): | |
| global apiJson | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| # Falls die Daten noch nicht geladen wurden oder neu geladen werden sollen | |
| if apiJson is None or 'movies' not in apiJson: | |
| loadMoviesData() | |
| if 'movies' not in apiJson or not isinstance(apiJson.get('movies'), list) or len(apiJson['movies']) == 0: | |
| oGui.showInfo() | |
| return | |
| sst = sSearchText.lower() | |
| if not sGui: | |
| dialog = xbmcgui.DialogProgress() | |
| dialog.create(cConfig().getLocalizedString(30122), cConfig().getLocalizedString(30123)) | |
| total = len(apiJson['movies']) | |
| position = 0 | |
| for movie in apiJson['movies']: | |
| position += 1 | |
| if not '_id' in movie: | |
| continue | |
| if not sGui and position % 128 == 0: # Update progress every 128 items | |
| if dialog.iscanceled(): break | |
| dialog.update(position, str(position) + cConfig().getLocalizedString(30128) + str(total)) | |
| sTitle = movie['title'] | |
| if 'Staffel' in sTitle or 'Season' in sTitle: | |
| isTvshow = True | |
| sSearch = sTitle.rsplit('-', 1)[0].replace(' ', '').lower() | |
| else: | |
| isTvshow = False | |
| sSearch = sTitle.lower() | |
| if not sst in sSearch and not cUtil.isSimilarByToken(sst, sSearch): | |
| continue | |
| #logger.info('-> [DEBUG]: %s' % str(movie)) | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'showEpisodes' if isTvshow else 'showHosters') | |
| sThumbnail = '' | |
| if 'poster_path_season' in movie and movie['poster_path_season']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path_season']) | |
| elif 'poster_path' in movie and movie['poster_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path']) | |
| elif 'backdrop_path' in movie and movie['backdrop_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['backdrop_path']) | |
| if sThumbnail: | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 'storyline' in movie: | |
| oGuiElement.setDescription(str(movie['storyline'])) | |
| elif 'overview' in movie: | |
| oGuiElement.setDescription(str(movie['overview'])) | |
| if 'year' in movie and len(str(movie['year'])) == 4: | |
| oGuiElement.setYear(movie['year']) | |
| if 'quality' in movie: | |
| oGuiElement.setQuality(_getQuality(movie['quality'])) | |
| if 'rating' in movie: | |
| oGuiElement.addItemValue('rating', movie['rating']) | |
| if 'lang' in movie: | |
| if (sLanguage != '1' and movie['lang'] == 2): # Deutsch | |
| oGuiElement.setLanguage('DE') | |
| if (sLanguage != '2' and movie['lang'] == 3): # Englisch | |
| oGuiElement.setLanguage('EN') | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| if 'runtime' in movie: | |
| isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], '\d+') | |
| if isMatch: | |
| oGuiElement.addItemValue('duration', sRuntime) | |
| params.setParam('entryUrl', URL_WATCH % str(movie['_id'])) | |
| params.setParam('sName', sTitle) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui: | |
| dialog.close() | |
| def loadMoviesData(): | |
| global apiJson | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| try: | |
| oRequest = cRequestHandler(URL_SEARCH % (sLang, 'new', '1'), caching=True) | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| oRequest.cacheTime = 60 * 60 * 48 # HTML Cache Zeit 2 Tage | |
| sJson = oRequest.request() | |
| apiJson = loads(sJson) | |
| logger.info('API-Daten erfolgreich geladen') | |
| except: | |
| logger.error('Fehler beim Laden der API-Daten') | |
| apiJson = {'movies': []} | |
| # Daten werden lazy beim ersten Zugriff geladen (siehe SSsearch) | |
| # loadMoviesData() - entfernt: beschleunigt den Import/Start erheblich |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| # Always pay attention to the translations in the menu! | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.gui.gui import cGui | |
| from resources.lib.config import cConfig | |
| from json import loads | |
| #from xbmc import LOGINFO as LOGNOTICE, LOGERROR, log | |
| SITE_IDENTIFIER = 'kinox' | |
| SITE_NAME = 'KinoX' | |
| SITE_ICON = 'kinox.png' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| # Domain Abfrage | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'ww22.kinoz.to') # Domain Auswahl über die xStream Einstellungen möglich | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| # Domain Auswahl über die xStream Einstellungen möglich | |
| URL_MAIN = 'https://' + DOMAIN | |
| # URL_MAIN = 'https://ww19.kinox.to' | |
| URL_NEWS = URL_MAIN + '/index.php' | |
| URL_CINEMA_PAGE = URL_MAIN + '/Kino-Filme.html' | |
| URL_GENRE_PAGE = URL_MAIN + '/Genre.html' | |
| URL_MOVIE_PAGE = URL_MAIN + '/Movies.html' | |
| URL_SERIE_PAGE = URL_MAIN + '/Series.html' | |
| URL_DOCU_PAGE = URL_MAIN + '/Documentations.html' | |
| URL_FAVOURITE_MOVIE_PAGE = URL_MAIN + '/Popular-Movies.html' | |
| URL_FAVOURITE_SERIE_PAGE = URL_MAIN + '/Popular-Series.html' | |
| URL_FAVOURITE_DOCU_PAGE = URL_MAIN + '/Popular-Documentations.html' | |
| URL_LATEST_MOVIE_PAGE = URL_MAIN + '/Latest-Movies.html' | |
| URL_LATEST_SERIE_PAGE = URL_MAIN + '/Latest-Series.html' | |
| URL_LATEST_DOCU_PAGE = URL_MAIN + '/Latest-Documentations.html' | |
| URL_SEARCH = URL_MAIN + '/Search.html?q=%s' | |
| URL_MIRROR = URL_MAIN + '/aGET/Mirror/' | |
| URL_EPISODE_URL = URL_MAIN + '/aGET/MirrorByEpisode/' | |
| URL_AJAX = URL_MAIN + '/aGET/List/' | |
| URL_LANGUAGE = URL_MAIN + '/aSET/PageLang/1' | |
| def load(): # Menu structure of the site plugin | |
| logger.info('Load %s' % SITE_NAME) | |
| parms = ParameterHandler() | |
| oGui = cGui() | |
| parms.setParam('sUrl', URL_NEWS) | |
| parms.setParam('page', 1) | |
| parms.setParam('mediaType', 'news') | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30500), SITE_IDENTIFIER, 'showNews'), parms) # New | |
| parms.setParam('sUrl', URL_MOVIE_PAGE) | |
| parms.setParam('mediaType', 'movie') | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30502), SITE_IDENTIFIER, 'showMovieMenu'), parms) # Movies | |
| parms.setParam('sUrl', URL_SERIE_PAGE) | |
| parms.setParam('mediaType', 'series') | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30511), SITE_IDENTIFIER, 'showSeriesMenu'), parms) # Series | |
| parms.setParam('sUrl', URL_DOCU_PAGE) | |
| parms.setParam('mediaType', 'documentation') | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30505), SITE_IDENTIFIER, 'showDocuMenu'), parms) # Documentations | |
| parms.setParam('sUrl', URL_SEARCH) | |
| parms.setParam('mediaType', '') | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), parms) # Search | |
| oGui.setEndOfDirectory() | |
| def __createMenuEntry(oGui, sFunction, sLabel, dOutputParameter): | |
| parms = ParameterHandler() | |
| try: | |
| for param, value in dOutputParameter.items(): | |
| parms.setParam(param, value) | |
| except Exception as e: | |
| logger.error("Can't add parameter to menu entry with label: %s: %s" % (sLabel, e)) | |
| oGuiElement = cGuiElement() | |
| oGuiElement.setSiteName(SITE_IDENTIFIER) | |
| oGuiElement.setFunction(sFunction) | |
| oGuiElement.setTitle(sLabel) | |
| oGui.addFolder(oGuiElement, parms) | |
| def showMovieMenu(): | |
| oGui = cGui() | |
| parms = ParameterHandler() | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30501), SITE_IDENTIFIER, 'showCinemaMovies'), parms)# Current films in the cinema | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showCharacters'), parms) # From A-Z | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30506), SITE_IDENTIFIER, 'showGenres'), parms) # Genre | |
| parms.setParam('sUrl', URL_FAVOURITE_MOVIE_PAGE) | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30521), SITE_IDENTIFIER, 'showFavItems'), parms) # popular Movies | |
| parms.setParam('sUrl', URL_LATEST_MOVIE_PAGE) | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30541), SITE_IDENTIFIER, 'showFavItems'), parms) # new Movies | |
| oGui.setEndOfDirectory() | |
| def showSeriesMenu(): | |
| oGui = cGui() | |
| parms = ParameterHandler() | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showCharacters'), parms)# From A-Z | |
| parms.setParam('sUrl', URL_FAVOURITE_SERIE_PAGE) | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30519), SITE_IDENTIFIER, 'showFavItems'), parms)# popular Series | |
| parms.setParam('sUrl', URL_LATEST_SERIE_PAGE) | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30514), SITE_IDENTIFIER, 'showFavItems'), parms)# new Series | |
| oGui.setEndOfDirectory() | |
| def showDocuMenu(): | |
| oGui = cGui() | |
| parms = ParameterHandler() | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showCharacters'), parms) # From A-Z | |
| parms.setParam('sUrl', URL_FAVOURITE_DOCU_PAGE) | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30522), SITE_IDENTIFIER, 'showFavItems'), parms)# Popular Documentations | |
| parms.setParam('sUrl', URL_LATEST_DOCU_PAGE) | |
| oGui.addFolder(cGuiElement(cConfig().getLocalizedString(30523), SITE_IDENTIFIER, 'showFavItems'), parms)# New Documentations | |
| oGui.setEndOfDirectory() | |
| def __createLanguage(sLangID): # Sprachenkürzel nach der internationalen Norm ISO-639-1 | |
| return {'1': 'DE', '2': 'EN', '4': 'ZA', '5': 'ES', '6': 'FR', '7': 'TR', | |
| '8': 'JA', '9': 'AR', '11': 'IT', '12': 'HR', '13': 'SR', | |
| '14': 'BS', '15': 'DE / EN', '16': 'NL', '17': 'KO', | |
| '24': 'EL', '25': 'RU', '26': 'HI', }.get(sLangID, sLangID) | |
| def __checkSubLanguage(sTitle): | |
| if ' subbed*' not in sTitle: | |
| return [sTitle, ''] | |
| temp = sTitle.split(' *') | |
| subLang = temp[-1].split('subbed*')[0].strip() | |
| title = ' '.join(temp[0:-1]).strip() | |
| return [title, 'de'] if subLang == 'german' else [title, subLang] | |
| def __getHtmlContent(sUrl=None, ignoreErrors=False): | |
| parms = ParameterHandler() | |
| if sUrl is None and not parms.exist('sUrl'): | |
| logger.error('There is no url we can request.') | |
| return False | |
| elif sUrl is None: | |
| sUrl = parms.getValue('sUrl') | |
| sPrefLang = __getPreferredLanguage() | |
| oRequest = cRequestHandler(sUrl, ignoreErrors=ignoreErrors) | |
| oRequest.addHeaderEntry('Cookie', sPrefLang + 'ListDisplayYears=Always;') | |
| oRequest.addHeaderEntry('Referer', URL_MAIN) | |
| return oRequest.request() | |
| def __getPreferredLanguage(): | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '1': # Voreingestellte Sprache Deutsch | |
| sPrefLang = 'ListNeededLanguage=25%2C24%2C26%2C2%2C5%2C6%2C7%2C8%2C11%2C15%2C16%2C9%2C12%2C13%2C14%2C17%2C4' | |
| elif sLanguage == '2': # Voreingestellte Sprache Englisch | |
| sPrefLang = 'ListNeededLanguage=25%2C24%2C26%2C5%2C6%2C7%2C8%2C11%2C15%2C16%2C9%2C12%2C13%2C14%2C17%2C4%2C1' | |
| else: | |
| sPrefLang = '' | |
| return sPrefLang | |
| def __displayItems(sGui, sHtmlContent): | |
| oGui = sGui if sGui else cGui() | |
| parms = ParameterHandler() | |
| pattern = r'<td class="Icon"><img width="16" height="11" src="/gr/sys/lng/(\d+).png" alt="language"></td>' + \ | |
| r'.*?title="([^\"]+)".*?<td class="Title">.*?<a href="([^\"]+)" onclick="return false;">([^<]+)</a> <span class="Year">([0-9]+)</span>' | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| if not aResult[0]: | |
| logger.error('Could not find an item') | |
| return | |
| total = len(aResult[1]) | |
| for aEntry in aResult[1]: | |
| sTitle = aEntry[3] | |
| sTitle, subLang = __checkSubLanguage(sTitle) | |
| sLang = __createLanguage(aEntry[0]) | |
| sUrl = URL_MAIN + aEntry[2] | |
| if aEntry[1] == 'movie' or aEntry[1] == 'cinema': | |
| mediaType = 'movie' | |
| elif aEntry[1] == 'series': | |
| mediaType = 'series' | |
| else: | |
| mediaType = 'documentation' | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'parseMovieEntrySite') | |
| oGuiElement.setLanguage(sLang) | |
| oGuiElement.setSubLanguage(subLang) | |
| oGuiElement.setYear(aEntry[4]) | |
| parms.setParam('sUrl', sUrl) | |
| parms.setParam('mediaType', mediaType) | |
| if mediaType == 'series': | |
| oGuiElement.setMediaType('tvshow') | |
| oGui.addFolder(oGuiElement, parms, total) | |
| elif mediaType == 'movie': | |
| oGuiElement.setMediaType('movie') | |
| oGui.addFolder(oGuiElement, parms, False, total) | |
| else: | |
| oGui.addFolder(oGuiElement, parms, False, total) | |
| def showFavItems(): | |
| oGui = cGui() | |
| sHtmlContent = __getHtmlContent() | |
| __displayItems(oGui, sHtmlContent) | |
| oGui.setEndOfDirectory() | |
| def showNews(): | |
| parms = ParameterHandler() | |
| sUrl = parms.getValue('sUrl') | |
| pattern = r'<div class="Opt leftOpt Headlne"><h1>([a-zA-Z0-9\s.]+)</h1></div>\s*(?:<div.*?)?<div class="Opt rightOpt Hint">Insgesamt: (.*?)</div>' | |
| sHtmlContent = __getHtmlContent(sUrl) | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| if aResult[0]: | |
| for aEntry in aResult[1]: | |
| sTitle = str(aEntry[0]) + ' (' + str(aEntry[1]) + ')' | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'parseNews') | |
| parms.addParams({'sUrl': URL_NEWS, 'page': 1, 'mediaType': 'news', 'sNewsTitle': aEntry[0]}) | |
| cGui().addFolder(oGuiElement, parms) | |
| cGui().setEndOfDirectory() | |
| def parseNews(): | |
| oGui = cGui() | |
| parms = ParameterHandler() | |
| sUrl = parms.getValue('sUrl') | |
| sNewsTitle = parms.getValue('sNewsTitle') | |
| if 'Serien' in sNewsTitle and 'Filme' not in sNewsTitle: | |
| mediaType = 'series' | |
| else: | |
| mediaType = 'movie' | |
| pattern = '<div class="Opt leftOpt Headlne"><h1>' + sNewsTitle \ | |
| + '</h1></div>(.*?)<div class="ModuleFooter">' | |
| sHtmlContent = __getHtmlContent(sUrl) | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| if not aResult[0]: | |
| logger.info("Can't get any news") | |
| oGui.setEndOfDirectory() | |
| return | |
| pattern = r'<td class="Icon"><img src="/gr/sys/lng/(\d+).png" alt="language" width="16" height="11".*?<td class="Title.*?rel="([^"]+)"><(?:a|span) href="([^\"]+)".*?class="OverlayLabel">([^<]+)(?:<span class="EpisodeDescr">)?([^<]+)' | |
| aResult = cParser.parse(aResult[1][0], pattern) | |
| if not aResult[0]: | |
| logger.info("Can't get any news") | |
| oGui.setEndOfDirectory() | |
| return | |
| total = len(aResult[1]) | |
| for aEntry in aResult[1]: | |
| sLang = __createLanguage(aEntry[0]) | |
| sTitle = aEntry[3] + aEntry[4] | |
| if sTitle.endswith(': '): | |
| sTitle = sTitle[:-2] | |
| sTitle, subLang = __checkSubLanguage(sTitle) | |
| sUrl = aEntry[2] | |
| aUrl = sUrl.split(',') | |
| if len(aUrl) > 0: | |
| sUrl = aUrl[0] | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'parseMovieEntrySite') | |
| oGuiElement.setLanguage(sLang) | |
| oGuiElement.setSubLanguage(subLang) | |
| oGuiElement.setThumbnail(URL_MAIN + str(aEntry[1])) | |
| parms.setParam('sUrl', URL_MAIN + sUrl) | |
| parms.setParam('mediaType', mediaType) | |
| if mediaType == 'series': | |
| oGuiElement.setMediaType('tvshow') | |
| oGui.addFolder(oGuiElement, parms, total) | |
| oGui.setView('tvshows') | |
| else: | |
| oGuiElement.setMediaType('movie') | |
| oGui.addFolder(oGuiElement, parms, False, total) | |
| oGui.setView('movies') | |
| oGui.setEndOfDirectory() | |
| def showCharacters(): | |
| oGui = cGui() | |
| parms = ParameterHandler() | |
| if parms.exist('sUrl') and parms.exist('page') and parms.exist('mediaType'): | |
| siteUrl = parms.getValue('sUrl') | |
| sHtmlContent = __getHtmlContent(siteUrl) | |
| pattern = 'class="LetterMode.*?>([^>]+)</a>' | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| if aResult[0]: | |
| for aEntry in aResult[1]: | |
| oGuiElement = cGuiElement(aEntry, SITE_IDENTIFIER, 'ajaxCall') | |
| parms.setParam('character', aEntry[0]) | |
| if parms.exist('mediaTypePageId'): | |
| sMediaTypePageId = parms.getValue('mediaTypePageId') | |
| parms.setParam('mediaTypePageId', sMediaTypePageId) | |
| oGui.addFolder(oGuiElement, parms) | |
| oGui.setEndOfDirectory() | |
| def showGenres(): | |
| logger.info('load displayGenreSite') | |
| pattern = '<td class="Title"><a.*?href="/Genre/([^"]+)">([^<]+)</a>.*?Tipp-([0-9]+).html">' | |
| sHtmlContent = __getHtmlContent(URL_GENRE_PAGE) | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| oGui = cGui() | |
| if aResult[0]: | |
| for aEntry in aResult[1]: | |
| iGenreId = aEntry[2] | |
| __createMenuEntry(oGui, 'showCharacters', aEntry[1], {'page': 1, 'mediaType': 'fGenre', 'mediaTypePageId': iGenreId, 'sUrl': URL_MOVIE_PAGE}) | |
| oGui.setEndOfDirectory() | |
| def showCinemaMovies(): | |
| logger.info('load displayCinemaSite') | |
| oGui = cGui() | |
| _cinema(oGui) | |
| oGui.setView('movies') | |
| oGui.setEndOfDirectory() | |
| def _cinema(oGui): | |
| pattern = '<div class="Opt leftOpt Headlne"><a title="(.*?)" href="(.*?)">.*?src="(.*?)".*?class="Descriptor">(.*?)</div.*?/lng/([0-9]+).png".*?IMDb:</b> (.*?) /' | |
| parms = ParameterHandler() | |
| sHtmlContent = __getHtmlContent(URL_CINEMA_PAGE) | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| if not aResult[0]: return | |
| total = len(aResult[1]) | |
| for aEntry in aResult[1]: | |
| sMovieTitle = aEntry[0] | |
| lang = __createLanguage(aEntry[4]) | |
| rating = aEntry[5] | |
| oGuiElement = cGuiElement() | |
| oGuiElement.setSiteName(SITE_IDENTIFIER) | |
| oGuiElement.setFunction('parseMovieEntrySite') | |
| oGuiElement.setLanguage(lang) | |
| oGuiElement.setTitle(sMovieTitle) | |
| oGuiElement.setDescription(aEntry[3]) | |
| oGuiElement.setMediaType('movie') | |
| oGuiElement.setThumbnail(URL_MAIN + str(aEntry[2])) | |
| oGuiElement.addItemValue('rating', rating) | |
| parms.setParam('sUrl', URL_MAIN + str(aEntry[1])) | |
| oGui.addFolder(oGuiElement, parms, False, total) | |
| def parseMovieEntrySite(): | |
| parms = ParameterHandler() | |
| if parms.exist('sUrl'): | |
| sUrl = parms.getValue('sUrl') | |
| sHtmlContent = __getHtmlContent(sUrl) | |
| sMovieTitle = __createMovieTitle(sHtmlContent) | |
| result = cParser.parse(sHtmlContent, '<div class="Grahpics">.*?<img src="([^"]+)"') | |
| thumbnail = URL_MAIN + str(result[1][0]) if result[0] else False | |
| bIsSerie = __isSerie(sHtmlContent) | |
| if bIsSerie: | |
| oGui = cGui() | |
| aSeriesItems = parseSerieSite(sHtmlContent) | |
| if not aSeriesItems[0]: return | |
| total = len(aSeriesItems) | |
| for aEntry in aSeriesItems[1]: | |
| seasonNum = str(aEntry) | |
| guiElement = cGuiElement('%s - Staffel %s' % (sMovieTitle, seasonNum), SITE_IDENTIFIER, 'showEpisodes') | |
| guiElement.setMediaType('season') | |
| guiElement.setSeason(seasonNum) | |
| guiElement.setTVShowTitle(sMovieTitle) | |
| parms.setParam('Season', seasonNum) | |
| if thumbnail: | |
| guiElement.setThumbnail(thumbnail) | |
| oGui.addFolder(guiElement, parms, total) | |
| oGui.setView('seasons') | |
| oGui.setEndOfDirectory() | |
| else: | |
| logger.info('Movie') | |
| result = showHosters() | |
| return result | |
| def showEpisodes(): | |
| oGui = cGui() | |
| parms = ParameterHandler() | |
| sUrl = parms.getValue('sUrl') | |
| seasonNum = parms.getValue('Season') | |
| sHtmlContent = __getHtmlContent(sUrl) | |
| sMovieTitle = __createMovieTitle(sHtmlContent) | |
| result = cParser.parse(sHtmlContent, '<div class="Grahpics">.*?<img src="([^"]+)"') | |
| thumbnail = URL_MAIN + str(result[1][0]) if result[0] else False | |
| aSeriesItems = parseSerieEpisodes(sHtmlContent, seasonNum) | |
| if not aSeriesItems[0]: return | |
| for item in aSeriesItems: | |
| oGuiElement = cGuiElement(item['title'], SITE_IDENTIFIER, 'showHosters') | |
| sShowTitle = sMovieTitle.split('(')[0].split('*')[0] | |
| oGuiElement.setThumbnail(thumbnail) | |
| oGuiElement.setMediaType('episode') | |
| oGuiElement.setSeason(item['season']) | |
| oGuiElement.setEpisode(item['episode']) | |
| oGuiElement.setTVShowTitle(sShowTitle) | |
| parms.addParams({'sUrl': item['url'], 'episode': item['episode'], 'season': item['season']}) | |
| oGui.addFolder(oGuiElement, parms, False, len(aSeriesItems)) | |
| oGui.setView('episodes') | |
| oGui.setEndOfDirectory() | |
| def __createMovieTitle(sHtmlContent): | |
| pattern = '<h1><span style="display: inline-block">(.*?)</h1>' | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| if aResult[0]: | |
| return str(aResult[1][0]) | |
| return False | |
| def parseSerieSite(sHtmlContent): | |
| pattern = r'<option[^>]+value="(\d+)"[^>]+>Staffel.+?</option>' | |
| return cParser.parse(sHtmlContent, pattern) | |
| def parseSerieEpisodes(sHtmlContent, seasonNum): | |
| aSeriesItems = [] | |
| pattern = 'id="SeasonSelection" rel="([^"]+)"' | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| if aResult[0]: | |
| aSeriesUrls = aResult[1][0].split("&") | |
| sSeriesUrl = '&' + str(aSeriesUrls[0]) + '&' + str(aSeriesUrls[1]) | |
| pattern = '<option.*?value="%d" rel="([^"]+)".*?>Staffel.*?</option>' % int(seasonNum) | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| logger.info(aResult[1]) | |
| if aResult[0]: | |
| aSeriesIds = aResult[1][0].split(',') | |
| for iSeriesIds in aSeriesIds: | |
| aSeries = {} | |
| iEpisodeNum = iSeriesIds | |
| sTitel = 'Folge ' + str(iEpisodeNum) | |
| sUrl = URL_EPISODE_URL + sSeriesUrl + '&Season=' + str(seasonNum) + '&Episode=' + str(iEpisodeNum) | |
| aSeries['title'] = sTitel | |
| aSeries['url'] = sUrl | |
| aSeries['season'] = seasonNum | |
| aSeries['episode'] = iEpisodeNum | |
| aSeriesItems.append(aSeries) | |
| return aSeriesItems | |
| def __isSerie(sHtmlContent): | |
| pattern = 'id="SeasonSelection" rel="([^"]+)"' | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| return aResult[0] == True | |
| def ajaxCall(): | |
| oGui = cGui() | |
| metaOn = oGui.isMetaOn | |
| parms = ParameterHandler() | |
| if parms.exist('page') and parms.exist('mediaType'): | |
| iPage = parms.getValue('page') | |
| sMediaType = parms.getValue('mediaType') | |
| iMediaTypePageId = False | |
| if parms.exist('mediaTypePageId'): | |
| iMediaTypePageId = parms.getValue('mediaTypePageId') | |
| sCharacter = 'A' | |
| if parms.exist('character'): | |
| sCharacter = parms.getValue('character') | |
| logger.info('MediaType: ' + sMediaType + ' , Page: ' + str(iPage) + ' , iMediaTypePageId: ' + str( | |
| iMediaTypePageId) + ' , sCharacter: ' + str(sCharacter)) | |
| sHtmlContent = __getAjaxContent(sMediaType, iPage, iMediaTypePageId, metaOn, sCharacter) | |
| #log('DEBUG: %s ' % sHtmlContent, LOGNOTICE) | |
| if not sHtmlContent: | |
| return | |
| if metaOn and not sMediaType == 'documentation': | |
| aData = loads(sHtmlContent)['aaData'] | |
| total = len(aData) | |
| for aEntry in aData: | |
| pattern = r'<a href="([^"]+)".*?onclick="return false;">([^<]+)<.*?>([0-9]{4})<' | |
| aResult = cParser.parse(aEntry[2], pattern) | |
| if aResult[0]: | |
| sYear = str(aResult[1][0][2]).strip() | |
| sTitle = aResult[1][0][1] | |
| sLang = aEntry[0] | |
| sUrl = URL_MAIN + str(aResult[1][0][0]) | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'parseMovieEntrySite') | |
| oGuiElement.setYear(sYear) | |
| oGuiElement.setLanguage(sLang) | |
| parms.setParam('sUrl', sUrl) | |
| if sMediaType == 'series': | |
| oGuiElement.setMediaType('tvshow') | |
| oGui.addFolder(oGuiElement, parms, total) | |
| else: | |
| oGuiElement.setMediaType('movie') | |
| oGui.addFolder(oGuiElement, parms, False, total) | |
| pattern = '"iTotalDisplayRecords":"([^"]+)' | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| if aResult[0]: | |
| for aEntry in aResult[1]: | |
| iTotalCount = int(aEntry[0]) | |
| iNextPage = int(iPage) + 1 | |
| iCurrentDisplayStart = __createDisplayStart(iNextPage) | |
| if iCurrentDisplayStart < iTotalCount: | |
| parms = ParameterHandler() | |
| parms.setParam('page', iNextPage) | |
| parms.setParam('character', sCharacter) | |
| parms.setParam('mediaType', sMediaType) | |
| if iMediaTypePageId: | |
| parms.setParam('mediaTypePageId', iMediaTypePageId) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'ajaxCall', parms) | |
| else: | |
| aData = loads(sHtmlContent) | |
| pattern = '<div class="Opt leftOpt Headlne"><a title="(.*?)" href="(.*?)">.*?src="(.*?)".*?class="Descriptor">(.*?)</div.*?lng/(.*?).png' | |
| aResult = cParser.parse(aData['Content'], pattern) | |
| if aResult[0]: | |
| total = len(aResult[1]) | |
| for aEntry in aResult[1]: | |
| sMovieTitle, subLang = __checkSubLanguage(aEntry[0]) | |
| lang = __createLanguage(aEntry[4]) | |
| oGuiElement = cGuiElement(sMovieTitle, SITE_IDENTIFIER, 'parseMovieEntrySite') | |
| oGuiElement.setDescription(aEntry[3]) | |
| oGuiElement.setThumbnail(URL_MAIN + str(aEntry[2])) | |
| oGuiElement.setLanguage(lang) | |
| oGuiElement.setSubLanguage(subLang) | |
| parms.setParam('sUrl', URL_MAIN + str(aEntry[1])) | |
| if sMediaType == 'series': | |
| oGui.addFolder(oGuiElement, parms, total) | |
| else: | |
| oGui.addFolder(oGuiElement, parms, False, total) | |
| iTotalCount = int(aData['Total']) | |
| iNextPage = int(iPage) + 1 | |
| if __createDisplayStart(iNextPage) < iTotalCount: | |
| parms = ParameterHandler() | |
| parms.setParam('page', iNextPage) | |
| if iMediaTypePageId: | |
| parms.setParam('mediaTypePageId', iMediaTypePageId) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'ajaxCall', parms) | |
| if sMediaType == 'series': | |
| oGui.setView('tvshows') | |
| else: | |
| oGui.setView('movies') | |
| oGui.setEndOfDirectory() | |
| def __createDisplayStart(iPage): | |
| return (30 * int(iPage)) - 30 | |
| def __getAjaxContent(sMediaType, iPage, iMediaTypePageId, metaOn, sCharacter=''): | |
| iDisplayStart = __createDisplayStart(iPage) | |
| sPrefLang = __getPreferredLanguage() | |
| oRequest = cRequestHandler(URL_AJAX) | |
| if not iMediaTypePageId: | |
| oRequest.addParameters('additional', '{"fType":"' + str(sMediaType) + '","Length":60,"fLetter":"' + str(sCharacter) + '"}') | |
| else: | |
| oRequest.addParameters('additional', '{"foo":"bar","' + str( | |
| sMediaType) + '":"' + iMediaTypePageId + '","fType":"movie","fLetter":"' + str(sCharacter) + '"}') | |
| oRequest.addParameters('iDisplayLength', '30') | |
| oRequest.addParameters('iDisplayStart', iDisplayStart) | |
| if metaOn and not sMediaType == 'documentation': | |
| oRequest.addParameters('bSortable_0', 'true') | |
| oRequest.addParameters('bSortable_1', 'true') | |
| oRequest.addParameters('bSortable_2', 'true') | |
| oRequest.addParameters('bSortable_3', 'false') | |
| oRequest.addParameters('bSortable_4', 'false') | |
| oRequest.addParameters('bSortable_5', 'false') | |
| oRequest.addParameters('bSortable_6', 'true') | |
| oRequest.addParameters('iColumns', '7') | |
| oRequest.addParameters('iSortCol_0', '2') | |
| oRequest.addParameters('iSortingCols', '1') | |
| oRequest.addParameters('sColumns', '') | |
| oRequest.addParameters('sEcho', iPage) | |
| oRequest.addParameters('sSortDir_0', 'asc') | |
| sUrl = oRequest.getRequestUri() | |
| oRequest = cRequestHandler(sUrl) | |
| else: | |
| oRequest.addParameters('ListMode', 'cover') | |
| oRequest.addParameters('Page', str(iPage)) | |
| oRequest.addParameters('Per_Page', '30') | |
| oRequest.addParameters('per_page', '30') | |
| oRequest.addParameters('dir', 'desc') | |
| oRequest.addParameters('sort', 'title') | |
| sUrl = oRequest.getRequestUri() | |
| oRequest = cRequestHandler(sUrl) | |
| oRequest.addHeaderEntry('Cookie', sPrefLang + 'ListDisplayYears=Always;') | |
| #log('DEBUG: %s ' % oRequest.getRequestUri(), LOGNOTICE) | |
| return oRequest.request() | |
| def showHosters(): | |
| parms = ParameterHandler() | |
| if parms.exist('sUrl'): | |
| sUrl = parms.getValue('sUrl') | |
| sHtmlContent = __getHtmlContent(sUrl) | |
| pattern = 'class="MirBtn.*?rel="([^"]+)".*?class="Named">([^<]+)</div>(.*?)</div>' | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| hosters = [] | |
| if aResult[0]: | |
| for aEntry in aResult[1]: | |
| sHoster = aEntry[1] | |
| if cConfig().isBlockedHoster(sHoster)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| pattern = '<b>Mirror</b>: [0-9]+/([0-9]+)' | |
| aResult = cParser.parse(aEntry[2], pattern) | |
| mirrors = 1 | |
| if aResult[0]: | |
| mirrors = int(aResult[1][0]) | |
| for i in range(1, mirrors + 1): | |
| sUrl = URL_MIRROR + cParser.unquotePlus(aEntry[0]) | |
| mirrorName = '' | |
| if mirrors > 1: | |
| mirrorName = ' Mirror ' + str(i) | |
| sUrl = cParser.replace(r'Mirror=[0-9]+', 'Mirror=' + str(i), sUrl) | |
| hoster = {'name': sHoster, 'link': sUrl, 'displayedName': sHoster + mirrorName} | |
| hosters.append(hoster) | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| sHtmlContent = cRequestHandler(sUrl).request() | |
| oRequest = cRequestHandler(sUrl) | |
| oRequest.addHeaderEntry('Referer', URL_MAIN) | |
| sHtmlContent = oRequest.request() | |
| isMatch, sStreamUrl = cParser.parseSingleResult(sHtmlContent, 'a\\shref=\\\\".*?(https?:.*?)\\\\"') | |
| if not isMatch: | |
| isMatch, sStreamUrl = cParser.parseSingleResult(sHtmlContent, '<iframe src=[^"]*"([^"]+)') | |
| if isMatch: | |
| if sStreamUrl.startswith('//'): | |
| sStreamUrl = 'https:' + sStreamUrl | |
| if 'streamcrypt.net' in sStreamUrl: | |
| oRequest = cRequestHandler(sStreamUrl, caching=False) | |
| oRequest.request() | |
| sStreamUrl = oRequest.getRealUrl() | |
| if 'thevideo' in sStreamUrl: | |
| sStreamUrl = sStreamUrl.replace('embed-', 'stream').replace('html', 'mp4') | |
| sUrl = _redirectHoster(sStreamUrl) | |
| return [{'streamUrl': sUrl, 'resolved': True}] | |
| return [{'streamUrl': sStreamUrl, 'resolved': False}] | |
| def _redirectHoster(url): | |
| try: | |
| from urllib2 import build_opener, HTTPError | |
| except ImportError: | |
| from urllib.error import HTTPError | |
| from urllib.request import build_opener | |
| opener = build_opener() | |
| opener.addheaders = [('Referer', url)] | |
| try: | |
| resp = opener.open(url) | |
| if url != resp.geturl(): | |
| return resp.geturl() | |
| else: | |
| return url | |
| except HTTPError as e: | |
| if e.code == 403: | |
| if url != e.geturl(): | |
| return e.geturl() | |
| raise | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| sHtmlContent = __getHtmlContent(URL_SEARCH % cParser.quotePlus(sSearchText), ignoreErrors=(oGui is not False)) | |
| __displayItems(oGui, sHtmlContent) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Always pay attention to the translations in the menu! | |
| # HTML LangzeitCache hinzugefügt | |
| # showGenre: 48 Stunden | |
| # showEntries: 6 Stunden | |
| # showEpisodes: 4 Stunden | |
| import re | |
| import xbmcgui | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser, cUtil | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| from json import loads | |
| from datetime import datetime | |
| # Globale Variable für die JSON-Daten | |
| apiJson = None | |
| # Domain Abfrage ### | |
| SITE_IDENTIFIER = 'kkiste' | |
| SITE_NAME = 'KKiste' | |
| SITE_ICON = 'kkiste.png' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| # Domain Abfrage | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'kkiste.eu') # Domain Auswahl über die xStream Einstellungen möglich | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| ORIGIN = 'https://' + DOMAIN + '/' | |
| REFERER = ORIGIN + '/' | |
| URL_API = 'https://' + DOMAIN | |
| URL_MAIN = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&page=%s' | |
| URL_SEARCH = URL_API + '/data/browse/?lang=%s&order_by=%s&page=%s&limit=0' | |
| URL_THUMBNAIL = 'https://image.tmdb.org/t/p/w300%s' | |
| URL_WATCH = URL_API + '/data/watch/?_id=%s' | |
| URL_GENRE = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&genre=%s&page=%s' | |
| URL_CAST = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&cast=%s&page=%s' | |
| URL_YEAR = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&year=%s&page=%s' | |
| def load(): | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| # Änderung des Sprachcodes nach voreigestellter Sprache | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| elif sLanguage == '3': # prefLang Japanisch | |
| sLang = cGui().showLanguage() | |
| return | |
| params.setParam('sLanguage', sLang) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502), SITE_IDENTIFIER, 'showMovieMenu'), params) # Movies | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30551), SITE_IDENTIFIER, 'showGenreMMenu'), params) # Movies Genre | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511), SITE_IDENTIFIER, 'showSeriesMenu'), params) # Series | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30529), SITE_IDENTIFIER, 'showGenreSMenu'), params) # Series Genre | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30508), SITE_IDENTIFIER, 'showYearsMenu'), params) # Years | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30553), SITE_IDENTIFIER, 'showSearchActor'), params) # Cast | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params) # Search | |
| cGui().setEndOfDirectory() | |
| def _cleanTitle(sTitle): | |
| sTitle = re.sub("[\xE4]", 'ae', sTitle) | |
| sTitle = re.sub("[\xFC]", 'ue', sTitle) | |
| sTitle = re.sub("[\xF6]", 'oe', sTitle) | |
| sTitle = re.sub("[\xC4]", 'Ae', sTitle) | |
| sTitle = re.sub("[\xDC]", 'Ue', sTitle) | |
| sTitle = re.sub("[\xD6]", 'Oe', sTitle) | |
| sTitle = re.sub("[\x00-\x1F\x80-\xFF]", '', sTitle) | |
| return sTitle | |
| def _getQuality(sQuality): | |
| isMatch, aResult = cParser.parse(sQuality, '(HDCAM|HD|WEB|BLUERAY|BRRIP|DVD|TS|SD|CAM)', 1, True) | |
| if isMatch: | |
| return aResult[0] | |
| else: | |
| return sQuality | |
| def _showGenreMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| sType = params.getValue('sType') | |
| sMenu = params.getValue('sMenu') | |
| if sLanguage == '2' or sLanguage == 'all': | |
| genres = { | |
| 'Action': 'Action', | |
| 'Abenteuer': 'Abenteuer', | |
| 'Animation': 'Animation', | |
| 'Biographie': 'Biographie', | |
| 'Dokumentation': 'Dokumentation', | |
| 'Drama': 'Drama', | |
| 'Familie': 'Familie', | |
| 'Fantasy': 'Fantasy', | |
| 'Geschichte': 'Geschichte', | |
| 'Horror': 'Horror', | |
| 'Komödie': 'Komödie', | |
| 'Krieg': 'Krieg', | |
| 'Krimi': 'Krimi', | |
| 'Musik': 'Musik', | |
| 'Mystery': 'Mystery', | |
| 'Romantik': 'Romantik', | |
| 'Reality-TV': 'Reality-TV', | |
| 'Sci-Fi': 'Sci-Fi', | |
| 'Sports': 'Sport', | |
| 'Thriller': 'Thriller', | |
| 'Western': 'Western' | |
| } | |
| else: | |
| genres = { | |
| 'Action': 'Action', | |
| 'Adventure': 'Abenteuer', | |
| 'Animation': 'Animation', | |
| 'Biography': 'Biographie', | |
| 'Comedy': 'Komödie', | |
| 'Crime': 'Krimi', | |
| 'Documentation': 'Dokumentation', | |
| 'Drama': 'Drama', | |
| 'Family': 'Familie', | |
| 'Fantasy': 'Fantasy', | |
| 'History': 'Geschichte', | |
| 'Horror': 'Horror', | |
| 'Music': 'Musik', | |
| 'Mystery': 'Mystery', | |
| 'Romance': 'Romantik', | |
| 'Reality-TV': 'Reality-TV', | |
| 'Sci-Fi': 'Sci-Fi', | |
| 'Sports': 'Sport', | |
| 'Thriller': 'Thriller', | |
| 'War': 'Krieg', | |
| 'Western': 'Western' | |
| } | |
| for genre, searchGenre in genres.items(): | |
| params.setParam('sUrl', URL_GENRE % (sLanguage, sType, sMenu, searchGenre, '1')) | |
| cGui().addFolder(cGuiElement(genre, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showMovieMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'Trending', '1')) ### Trending Filme trending1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30521), SITE_IDENTIFIER, 'showEntries'), params) ### Trending Filme trending1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'new', '1')) ### neue filme neu1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30541), SITE_IDENTIFIER, 'showEntries'), params) ### neue filme neu1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'views', '1')) ### Views filme views1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30552), SITE_IDENTIFIER, 'showEntries'), params) ### Views filme views1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'rating', '1')) ### Rating Filme rating1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30510), SITE_IDENTIFIER, 'showEntries'), params) ### Rating Filme rating1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'votes', '1')) ### votes filme votes 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30536), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'updates', '1')) ### updates filme updates 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30533), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'name', '1')) ### name filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'featured', '1')) ### featured filme features1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30530), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'requested', '1')) ### requested filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30534), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'releases', '1')) ### releases filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30531), SITE_IDENTIFIER, 'showEntries'), params) ### Filme releases 1 | |
| cGui().setEndOfDirectory() | |
| def showGenreMMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sType', 'movies') | |
| params.setParam('sMenu', 'Trending') | |
| cGui().addFolder(cGuiElement('Genre trending', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Neu') | |
| cGui().addFolder(cGuiElement('Genre new', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Views') | |
| cGui().addFolder(cGuiElement('Genre viewed', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Votes') | |
| cGui().addFolder(cGuiElement('Genre voted', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Updates') | |
| cGui().addFolder(cGuiElement('Genre updated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Rating') | |
| cGui().addFolder(cGuiElement('Genre rated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Name') | |
| cGui().addFolder(cGuiElement('Genre named', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'requested') | |
| cGui().addFolder(cGuiElement('Genre requested', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'featured') | |
| cGui().addFolder(cGuiElement('Genre featured', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'releases') | |
| cGui().addFolder(cGuiElement('Genre released', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| cGui().setEndOfDirectory() | |
| # Serienmenue | |
| def showSeriesMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'neu', '1')) ### serien neu 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30514), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'views', '1')) ### serien views 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30537), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'votes', '1')) ### serien votes 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30519), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'updates', '1')) ### serien updates 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30533), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'name', '1')) ### serien name 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'featured', '1')) ### | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30530), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'requested', '1')) ### serien requested | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30534), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'releases', '1')) ### serien releases 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30531), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'rating', '1')) ### serien rating 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30535), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| #params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'Jahr', '1')) # ## | |
| #cGui().addFolder(cGuiElement('Jahr', SITE_IDENTIFIER, 'showEntries'), params) ## | |
| #params.setParam('sCont', 'Jahr') # | |
| #cGui().addFolder(cGuiElement('Jahr', SITE_IDENTIFIER, 'showValue'), params), params) # | |
| cGui().setEndOfDirectory() | |
| # show genre serien menue | |
| def showGenreSMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sType', 'tvseries') | |
| params.setParam('sMenu', 'Trending') | |
| cGui().addFolder(cGuiElement('Series genre trending', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Neu') | |
| cGui().addFolder(cGuiElement('Series genre new', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Views') | |
| cGui().addFolder(cGuiElement('Series genre viewed', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Votes') | |
| cGui().addFolder(cGuiElement('Series genre voted', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Updates') | |
| cGui().addFolder(cGuiElement('Series genre updated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Rating') | |
| cGui().addFolder(cGuiElement('Series genre rated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Name') | |
| cGui().addFolder(cGuiElement('Series genre named', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'requested') | |
| cGui().addFolder(cGuiElement('Series genre requested', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'featured') | |
| cGui().addFolder(cGuiElement('Series genre featured', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'releases') | |
| cGui().addFolder(cGuiElement('Series genre releases', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| cGui().setEndOfDirectory() | |
| def showYearsMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| # Anfangs- und Endjahr für das menü eintragen | |
| start_jahr = 1931 | |
| end_jahr = datetime.now().year | |
| # show the current year first | |
| for jahr in range(end_jahr, start_jahr - 1, -1): | |
| params.setParam('sUrl', URL_YEAR % (sLanguage, 'movies', 'new', str(jahr), '1')) | |
| cGui().addFolder(cGuiElement(str(jahr), SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| isTvshow = False | |
| sThumbnail = '' | |
| sLanguage = params.getValue('sLanguage') | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| try: | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # HTML Cache Zeit 6 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| aJson = loads(sJson) | |
| except: | |
| if not sGui: oGui.showInfo() | |
| return | |
| if 'movies' not in aJson or not isinstance(aJson.get('movies'), list) or len(aJson['movies']) == 0: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = 0 | |
| # ignore movies which does not contain any streams | |
| for movie in aJson['movies']: | |
| if '_id' in movie: | |
| total += 1 | |
| for movie in aJson['movies']: | |
| if not '_id' in movie: | |
| continue | |
| sTitle = str(movie['title']) | |
| if sSearchText and not cParser.search(sSearchText, sTitle): | |
| continue | |
| if 'Staffel' in sTitle or 'Season' in sTitle: | |
| isTvshow = True | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'showEpisodes' if isTvshow else 'showHosters') | |
| if 'poster_path_season' in movie and movie['poster_path_season']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path_season']) | |
| elif 'poster_path' in movie and movie['poster_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path']) | |
| elif 'backdrop_path' in movie and movie['backdrop_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['backdrop_path']) | |
| if sThumbnail: | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 'storyline' in movie: | |
| oGuiElement.setDescription(str(movie['storyline'])) | |
| elif 'overview' in movie: | |
| oGuiElement.setDescription(str(movie['overview'])) | |
| if 'year' in movie and len(str(movie['year'])) == 4: | |
| oGuiElement.setYear(movie['year']) | |
| if 'quality' in movie: | |
| oGuiElement.setQuality(_getQuality(movie['quality'])) | |
| if 'rating' in movie: | |
| oGuiElement.addItemValue('rating', movie['rating']) | |
| if 'lang' in movie: | |
| if (sLanguage != '1' and movie['lang'] == 2): # Deutsch | |
| oGuiElement.setLanguage('DE') | |
| if (sLanguage != '2' and movie['lang'] == 3): # Englisch | |
| oGuiElement.setLanguage('EN') | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| if 'runtime' in movie: | |
| isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], '\d+') | |
| if isMatch: | |
| oGuiElement.addItemValue('duration', sRuntime) | |
| params.setParam('entryUrl', URL_WATCH % str(movie['_id'])) | |
| params.setParam('sName', sTitle) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui and not sSearchText: | |
| curPage = aJson['pager']['currentPage'] | |
| if curPage < aJson['pager']['totalPages']: | |
| sNextUrl = entryUrl.replace('page=' + str(curPage), 'page=' + str(curPage + 1)) | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('tvshows' if isTvshow else 'movies') | |
| oGui.setEndOfDirectory() | |
| def showEpisodes(): | |
| aEpisodes = [] | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue("sThumbnail") | |
| try: | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 4 # HTML Cache Zeit 4 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| aJson = loads(sJson) | |
| except: | |
| cGui().showInfo() | |
| return | |
| if 'streams' not in aJson or len(aJson['streams']) == 0: | |
| cGui().showInfo() | |
| return | |
| for stream in aJson['streams']: | |
| if 'e' in stream: | |
| aEpisodes.append(int(stream['e'])) | |
| if aEpisodes: | |
| aEpisodesSorted = set(aEpisodes) | |
| total = len(aEpisodesSorted) | |
| for sEpisode in aEpisodesSorted: | |
| oGuiElement = cGuiElement('Episode ' + str(sEpisode), SITE_IDENTIFIER, 'showHosters') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 's' in aJson: | |
| oGuiElement.setSeason(aJson['s']) | |
| oGuiElement.setTVShowTitle('Episode ' + str(sEpisode)) | |
| oGuiElement.setEpisode(sEpisode) | |
| oGuiElement.setMediaType('episode') | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| cGui().setView('episodes') | |
| cGui().setEndOfDirectory() | |
| def showHosters(): | |
| hosters = [] | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sEpisode = params.getValue('episode') | |
| try: | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 8 # HTML Cache Zeit 8 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| except: | |
| return hosters | |
| if sJson: | |
| aJson = loads(sJson) | |
| if 'streams' in aJson: | |
| i = 0 | |
| for stream in aJson['streams']: | |
| if (('e' not in stream) or (str(sEpisode) == str(stream['e']))): | |
| sHoster = str(i) + ':' | |
| isMatch, aName = cParser.parse(stream['stream'], '//([^/]+)/') | |
| if isMatch: | |
| # sName = cParser.urlparse(sUrl) ### angezeigter hostername api | |
| sName = aName[0][:aName[0].rindex('.')] | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| sHoster = sHoster + ' ' + sName | |
| if 'release' in stream and str(stream['release']) != '': | |
| sHoster = sHoster + ' [I][' + _getQuality(stream['release']) + '][/I]' | |
| hoster = {'link': stream['stream'], 'name': sHoster} | |
| hosters.append(hoster) | |
| i += 1 | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearchActor(): | |
| sName = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30280)) | |
| if not sName: return | |
| _searchActor(False, sName) | |
| cGui().setEndOfDirectory() | |
| def _searchActor(oGui, sName): | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| showEntries(URL_CAST % (sLanguage, 'movies', 'new', cParser.quotePlus(sName), '1'), oGui) | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| SSsearch(oGui, sSearchText) | |
| def SSsearch(sGui=False, sSearchText=False): | |
| global apiJson | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| # Falls die Daten noch nicht geladen wurden oder neu geladen werden sollen | |
| if apiJson is None or 'movies' not in apiJson: | |
| loadMoviesData() | |
| if 'movies' not in apiJson or not isinstance(apiJson.get('movies'), list) or len(apiJson['movies']) == 0: | |
| oGui.showInfo() | |
| return | |
| sst = sSearchText.lower() | |
| if not sGui: | |
| dialog = xbmcgui.DialogProgress() | |
| dialog.create(cConfig().getLocalizedString(30122), cConfig().getLocalizedString(30123)) | |
| total = len(apiJson['movies']) | |
| position = 0 | |
| for movie in apiJson['movies']: | |
| position += 1 | |
| if not '_id' in movie: | |
| continue | |
| if not sGui and position % 128 == 0: # Update progress every 128 items | |
| if dialog.iscanceled(): break | |
| dialog.update(position, str(position) + cConfig().getLocalizedString(30128) + str(total)) | |
| sTitle = movie['title'] | |
| if 'Staffel' in sTitle or 'Season' in sTitle: | |
| isTvshow = True | |
| sSearch = sTitle.rsplit('-', 1)[0].replace(' ', '').lower() | |
| else: | |
| isTvshow = False | |
| sSearch = sTitle.lower() | |
| if not sst in sSearch and not cUtil.isSimilarByToken(sst, sSearch): | |
| continue | |
| #logger.info('-> [DEBUG]: %s' % str(movie)) | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'showEpisodes' if isTvshow else 'showHosters') | |
| sThumbnail = '' | |
| if 'poster_path_season' in movie and movie['poster_path_season']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path_season']) | |
| elif 'poster_path' in movie and movie['poster_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path']) | |
| elif 'backdrop_path' in movie and movie['backdrop_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['backdrop_path']) | |
| if sThumbnail: | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 'storyline' in movie: | |
| oGuiElement.setDescription(str(movie['storyline'])) | |
| elif 'overview' in movie: | |
| oGuiElement.setDescription(str(movie['overview'])) | |
| if 'year' in movie and len(str(movie['year'])) == 4: | |
| oGuiElement.setYear(movie['year']) | |
| if 'quality' in movie: | |
| oGuiElement.setQuality(_getQuality(movie['quality'])) | |
| if 'rating' in movie: | |
| oGuiElement.addItemValue('rating', movie['rating']) | |
| if 'lang' in movie: | |
| if (sLanguage != '1' and movie['lang'] == 2): # Deutsch | |
| oGuiElement.setLanguage('DE') | |
| if (sLanguage != '2' and movie['lang'] == 3): # Englisch | |
| oGuiElement.setLanguage('EN') | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| if 'runtime' in movie: | |
| isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], '\d+') | |
| if isMatch: | |
| oGuiElement.addItemValue('duration', sRuntime) | |
| params.setParam('entryUrl', URL_WATCH % str(movie['_id'])) | |
| params.setParam('sName', sTitle) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui: | |
| dialog.close() | |
| def loadMoviesData(): | |
| global apiJson | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| try: | |
| oRequest = cRequestHandler(URL_SEARCH % (sLang, 'new', '1'), caching=True) | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| oRequest.cacheTime = 60 * 60 * 48 # HTML Cache Zeit 2 Tage | |
| sJson = oRequest.request() | |
| apiJson = loads(sJson) | |
| except: | |
| apiJson = {'movies': []} | |
| # Daten werden lazy beim ersten Zugriff geladen (siehe SSsearch) | |
| # loadMoviesData() - entfernt: beschleunigt den Import/Start erheblich |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| # Always pay attention to the translations in the menu! | |
| # HTML LangzeitCache hinzugefügt | |
| # showValue: 48 Stunden | |
| # showEntries: 6 Stunden | |
| # showEpisodes: 4 Stunden | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| import urllib.parse | |
| SITE_IDENTIFIER = 'megakino' | |
| SITE_NAME = 'Megakino' | |
| SITE_ICON = 'megakino.png' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| # Domain Abfrage | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'megakino1.to') | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) | |
| URL_MAIN = 'https://' + DOMAIN + '/' | |
| URL_KINO = URL_MAIN + 'kinofilme/' | |
| URL_MOVIES = URL_MAIN + 'films/' | |
| URL_SERIES = URL_MAIN + 'serials/' | |
| URL_ANIMATION = URL_MAIN + 'multfilm/' | |
| URL_DOKU = URL_MAIN + 'documentary/' | |
| URL_SEARCH = URL_MAIN + '?do=search&subaction=search&story=%s' | |
| def load(): | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| params.setParam('sUrl', URL_MAIN) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30500), SITE_IDENTIFIER, 'showEntries'), params) # New | |
| params.setParam('sUrl', URL_KINO) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30501), SITE_IDENTIFIER, 'showEntries'), params) # Current films in the cinema | |
| params.setParam('sUrl', URL_MOVIES) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502), SITE_IDENTIFIER, 'showEntries'), params) # Movies | |
| params.setParam('sUrl', URL_ANIMATION) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30504), SITE_IDENTIFIER, 'showEntries'), params) # Animated Films | |
| params.setParam('sUrl', URL_SERIES) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511), SITE_IDENTIFIER, 'showEntries'), params) # Series | |
| params.setParam('sUrl', URL_DOKU) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30505), SITE_IDENTIFIER, 'showEntries'), params) # Documentations | |
| params.setParam('sUrl', URL_MAIN) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30543), SITE_IDENTIFIER, 'showCollection'), params) # Collections | |
| params.setParam('sUrl', URL_MAIN) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30506), SITE_IDENTIFIER, 'showGenre'), params) # Genre | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params) # Search | |
| cGui().setEndOfDirectory() | |
| def showGenre(): | |
| params = ParameterHandler() | |
| entryUrl = params.getValue('sUrl') | |
| sHtmlContent = getHtmlContent(entryUrl) | |
| if not sHtmlContent: | |
| cGui().showInfo() | |
| return | |
| pattern = '<div class="side-block__title">Genres</div>.*?<ul class="side-block__content side-block__menu"(.*?)</ul>' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| pattern = 'href="([^"]+)">([^<]+)</a>' | |
| isMatch, aResult = cParser.parse(sHtmlContainer, pattern) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| if sUrl.startswith('/'): | |
| sUrl = URL_MAIN + sUrl[1:] | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showCollection(): | |
| params = ParameterHandler() | |
| entryUrl = params.getValue('sUrl') | |
| sHtmlContent = getHtmlContent(entryUrl) | |
| if not sHtmlContent: | |
| cGui().showInfo() | |
| return | |
| pattern = '<div class="side-block__title">Sammlung</div>.*?<div class="side-block__content collection-scroll">(.*?)</div>' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| pattern = 'href="([^"]+)"[^>]*>.*?<div class="custom-collection-title">([^<]+)</div>' | |
| isMatch, aResult = cParser.parse(sHtmlContainer, pattern) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| if sUrl.startswith('/'): | |
| sUrl = URL_MAIN + sUrl[1:] | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def getHtmlContent(url): | |
| """Hilfsfunktion zum Abrufen von HTML-Inhalten mit Token-Umgehung""" | |
| logger.info(f'[megakino] Getting HTML content for: {url}') | |
| # Erster Versuch: Direkter Aufruf | |
| oRequest = cRequestHandler(url, bypass_dns=True) | |
| oRequest.cacheTime = 0 | |
| oRequest.addHeaderEntry('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36') | |
| oRequest.addHeaderEntry('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8') | |
| oRequest.addHeaderEntry('Accept-Language', 'de-DE,de;q=0.9,en;q=0.8') | |
| oRequest.addHeaderEntry('Referer', URL_MAIN) | |
| sHtmlContent = oRequest.request() | |
| # Prüfen ob Token-Redirect erforderlich ist | |
| if sHtmlContent and 'yg=token' in sHtmlContent: | |
| logger.info('[megakino] Token redirect detected, getting token...') | |
| # Token URL erstellen | |
| token_url = URL_MAIN + 'index.php?yg=token' | |
| # Token abrufen | |
| oTokenRequest = cRequestHandler(token_url, bypass_dns=True) | |
| oTokenRequest.cacheTime = 0 | |
| oTokenRequest.addHeaderEntry('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36') | |
| oTokenRequest.addHeaderEntry('Accept', '*/*') | |
| oTokenRequest.addHeaderEntry('Accept-Language', 'de-DE,de;q=0.9,en;q=0.8') | |
| oTokenRequest.addHeaderEntry('Referer', url) | |
| oTokenRequest.addHeaderEntry('X-Requested-With', 'XMLHttpRequest') | |
| token_response = oTokenRequest.request() | |
| logger.info(f'[megakino] Token response: {token_response}') | |
| # Nach Token-Abruf die ursprüngliche URL erneut aufrufen | |
| oRequest2 = cRequestHandler(url, bypass_dns=True) | |
| oRequest2.cacheTime = 0 | |
| oRequest2.addHeaderEntry('User-Agent', 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36') | |
| oRequest2.addHeaderEntry('Accept', 'text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8') | |
| oRequest2.addHeaderEntry('Accept-Language', 'de-DE,de;q=0.9,en;q=0.8') | |
| oRequest2.addHeaderEntry('Referer', URL_MAIN) | |
| sHtmlContent = oRequest2.request() | |
| if sHtmlContent and len(sHtmlContent) > 1000: | |
| logger.info(f'[megakino] Successfully retrieved HTML content, length: {len(sHtmlContent)}') | |
| return sHtmlContent | |
| else: | |
| logger.error(f'[megakino] Failed to retrieve valid HTML content, length: {len(sHtmlContent) if sHtmlContent else 0}') | |
| return None | |
| def showEntries(entryUrl=False, sGui=False, sSearchText=False, sSearchPageText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| isTvshow = False | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| logger.info(f'[megakino] Loading URL: {entryUrl}') | |
| sHtmlContent = getHtmlContent(entryUrl) | |
| if not sHtmlContent: | |
| logger.error('[megakino] No valid HTML content received') | |
| if not sGui: oGui.showInfo() | |
| return | |
| # Vereinfachtes Pattern für die Einträge | |
| pattern = '<a class="poster grid-item[^>]*href="([^"]+)"[^>]*>.*?<img[^>]*data-src="([^"]+)"[^>]*alt="([^"]+)"[^>]*>.*?<div class="poster__label">([^<]*)</div>.*?<h3 class="poster__title[^>]*>([^<]*)</h3>.*?<div class="poster__text[^>]*>([^<]*)</div>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| logger.error('[megakino] No items found with main pattern') | |
| # Fallback Pattern | |
| pattern = '<a class="poster grid-item[^>]*href="([^"]+)"[^>]*>.*?<img[^>]*data-src="([^"]+)"[^>]*alt="([^"]+)"' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch or not aResult: | |
| logger.error('[megakino] No items found with any pattern') | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| logger.info(f'[megakino] Found {total} items') | |
| for entry in aResult: | |
| if len(entry) >= 6: | |
| # Vollständiges Pattern | |
| sUrl = entry[0] | |
| sThumbnail = entry[1] | |
| sAltName = entry[2] | |
| sQuality = entry[3] | |
| sName = entry[4] | |
| sDesc = entry[5] | |
| else: | |
| # Einfaches Pattern | |
| sUrl = entry[0] | |
| sThumbnail = entry[1] | |
| sName = entry[2] | |
| sQuality = '' | |
| sDesc = '' | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| # Relative URLs korrigieren | |
| if sUrl.startswith('/'): | |
| sUrl = URL_MAIN + sUrl[1:] | |
| if sThumbnail.startswith('/'): | |
| sThumbnail = URL_MAIN + sThumbnail[1:] | |
| elif sThumbnail.startswith('//'): | |
| sThumbnail = 'https:' + sThumbnail | |
| # TV Show Erkennung | |
| isTvshow = 'staffel' in sName.lower() or 'season' in sName.lower() or 'serials' in entryUrl | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showEpisodes' if isTvshow else 'showHosters') | |
| if sQuality: | |
| oGuiElement.setQuality(sQuality) | |
| if sDesc: | |
| oGuiElement.setDescription(sDesc) | |
| oGuiElement.setThumbnail(sThumbnail) | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| params.setParam('entryUrl', sUrl) | |
| params.setParam('sName', sName) | |
| params.setParam('sThumbnail', sThumbnail) | |
| params.setParam('sDesc', sDesc) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| # Nächste Seite suchen | |
| if not sGui and not sSearchText and not sSearchPageText: | |
| pattern = '<div class="pagination__btn-loader[^>]*>.*?<a href="([^"]+)"[^>]*>' | |
| isMatchNextPage, sNextUrl = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatchNextPage and sNextUrl: | |
| if sNextUrl.startswith('/'): | |
| sNextUrl = URL_MAIN + sNextUrl[1:] | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('tvshows' if isTvshow else 'movies') | |
| oGui.setEndOfDirectory() | |
| def showEpisodes(): | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue("sThumbnail") | |
| sName = params.getValue('sName') | |
| sDesc = params.getValue('sDesc') | |
| logger.info(f'[megakino] Loading episodes for: {sName}') | |
| sHtmlContent = getHtmlContent(sUrl) | |
| if not sHtmlContent: | |
| logger.error('[megakino] No valid HTML content for episodes') | |
| cGui().showInfo() | |
| return | |
| # Episoden Patterns | |
| patterns = [ | |
| r'<option\s+value="ep([^"]+)">([^<]+)</option>', | |
| '<option[^>]*value="ep([^"]+)"[^>]*>([^<]+)</option>' | |
| ] | |
| aResult = [] | |
| for pattern in patterns: | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if isMatch and aResult: | |
| break | |
| if not isMatch: | |
| logger.error('[megakino] No episodes found') | |
| cGui().showInfo() | |
| return | |
| total = len(aResult) | |
| for episode, episodeName in aResult: | |
| params.setParam('episodeId', episode) | |
| oGuiElement = cGuiElement(str(episodeName), SITE_IDENTIFIER, 'showEpisodeHosters') | |
| # Thumbnail URL korrigieren | |
| if sThumbnail.startswith('/'): | |
| sThumbnail = URL_MAIN + sThumbnail[1:] | |
| elif sThumbnail.startswith('//'): | |
| sThumbnail = 'https:' + sThumbnail | |
| oGuiElement.setThumbnail(sThumbnail) | |
| oGuiElement.setDescription(sDesc) | |
| oGuiElement.setTVShowTitle(sName) | |
| oGuiElement.setMediaType('episode') | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| cGui().setView('episodes') | |
| cGui().setEndOfDirectory() | |
| def showHosters(): | |
| hosters = [] | |
| sUrl = ParameterHandler().getValue('entryUrl') | |
| sHtmlContent = getHtmlContent(sUrl) | |
| if not sHtmlContent: | |
| return hosters | |
| # Iframe Patterns | |
| patterns = [ | |
| r'<iframe.*?src=([^\s]+)', | |
| '<iframe[^>]*src="([^"]+)"' | |
| ] | |
| aResult = [] | |
| for pattern in patterns: | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if isMatch and aResult: | |
| break | |
| if isMatch: | |
| for sUrl in aResult: | |
| sQuality = '720' | |
| sUrl = sUrl.replace('"', '').replace("'", "").strip() | |
| if not sUrl.startswith('http'): | |
| continue | |
| if 'youtube' in sUrl: continue # Youtube Trailer | |
| sName = cParser.urlparse(sUrl).split('.')[0].replace('https://', '').replace('http://', '') | |
| if 'Watch' in sName: sName = sName.replace('Watch', 'GXPlayer') | |
| if cConfig().isBlockedHoster(sName)[0]: continue | |
| hoster = {'link': sUrl, 'name': sName, 'displayedName': '%s [I][%sp][/I]' % (sName, sQuality), 'quality': sQuality} | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def showEpisodeHosters(): | |
| hosters = [] | |
| sUrl = ParameterHandler().getValue('entryUrl') | |
| episodeId = 'ep' + ParameterHandler().getValue('episodeId') | |
| sHtmlContent = getHtmlContent(sUrl) | |
| if not sHtmlContent: | |
| return hosters | |
| patterns = [ | |
| '<select[^>]*id="%s"[^>]*>(.*?)</select>' % episodeId | |
| ] | |
| sContainer = None | |
| for pattern in patterns: | |
| isMatch, sContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| break | |
| if isMatch: | |
| pattern = 'value="([^"]+)"' | |
| isMatch, aResult = cParser.parse(sContainer, pattern) | |
| if isMatch: | |
| for sUrl in aResult: | |
| sQuality = '720' | |
| if 'youtube' in sUrl: continue | |
| sName = cParser.urlparse(sUrl).split('.')[0].replace('https://', '').replace('http://', '') | |
| if cConfig().isBlockedHoster(sName)[0]: continue | |
| hoster = {'link': sUrl, 'name': sName, 'displayedName': '%s [I][%sp][/I]' % (sName, sQuality), 'quality': sQuality} | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| showEntries(URL_SEARCH % cParser.quotePlus(sSearchText), oGui, sSearchText) | |
| def showSearchPage(): | |
| params = ParameterHandler() | |
| sNextPage = params.getValue('sNextPage') | |
| sPageLast = params.getValue('sPageLast') | |
| sHeading = cConfig().getLocalizedString(30282) + str(sPageLast) | |
| sSearchPageText = cGui().showKeyBoard(sHeading=sHeading) | |
| if not sSearchPageText: return | |
| sNextSearchPage = sNextPage.split('page/')[0].strip() + 'page/' + sSearchPageText + '/' | |
| showEntries(sNextSearchPage) | |
| cGui().setEndOfDirectory() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| # Always pay attention to the translations in the menu! | |
| # HTML LangzeitCache hinzugefügt | |
| # showEntries: 6 Stunden | |
| # showEpisodes: 4 Stunden | |
| import re | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| from json import loads | |
| SITE_IDENTIFIER = 'movie2k' | |
| SITE_NAME = 'Movie2K' | |
| SITE_ICON = 'movie2k.png' | |
| URL_MAIN = 'https://movie2k.ch/data/browse/?lang=%s&type=%s&order_by=%s&page=%s' # lang=%s 2 = deutsch / 3 = englisch / all = Alles | |
| URL_SEARCH = 'https://movie2k.ch/data/browse/?lang=%s&keyword=%s&page=%s&limit=0' | |
| URL_THUMBNAIL = 'https://image.tmdb.org/t/p/w300%s' | |
| URL_WATCH = 'https://movie2k.ch/data/watch/?_id=%s' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| # Domain Abfrage | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'movie2k.ch') # Domain Auswahl über die xStream Einstellungen möglich | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| ORIGIN = 'https://' + DOMAIN + '/' | |
| # ORIGIN = 'https://movie2k.at/' | |
| REFERER = ORIGIN + '/' | |
| # | |
| def load(): | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| # Änderung des Sprachcodes nach voreigestellter Sprache | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| elif sLanguage == '3': # prefLang Japanisch | |
| sLang = cGui().showLanguage() | |
| return | |
| params.setParam('sLanguage', sLang) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502), SITE_IDENTIFIER, 'showMovieMenu'), params) # Movies | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511), SITE_IDENTIFIER, 'showSeriesMenu'), params) # Series | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params) # Search | |
| cGui().setEndOfDirectory() | |
| def _cleanTitle(sTitle): | |
| sTitle = re.sub("[\xE4]", 'ae', sTitle) | |
| sTitle = re.sub("[\xFC]", 'ue', sTitle) | |
| sTitle = re.sub("[\xF6]", 'oe', sTitle) | |
| sTitle = re.sub("[\xC4]", 'Ae', sTitle) | |
| sTitle = re.sub("[\xDC]", 'Ue', sTitle) | |
| sTitle = re.sub("[\xD6]", 'Oe', sTitle) | |
| sTitle = re.sub("[\x00-\x1F\x80-\xFF]", '', sTitle) | |
| return sTitle | |
| def _getQuality(sQuality): | |
| isMatch, aResult = cParser.parse(sQuality, '(HDCAM|HD|WEB|BLUERAY|BRRIP|DVD|TS|SD|CAM)', 1, True) | |
| if isMatch: | |
| return aResult[0] | |
| else: | |
| return sQuality | |
| def showMovieMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'featured', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502) + cConfig().getLocalizedString(30530), SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'releases', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502) + cConfig().getLocalizedString(30531), SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'trending', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502) + cConfig().getLocalizedString(30532), SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'updates', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502) + cConfig().getLocalizedString(30533), SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'requested', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502) + cConfig().getLocalizedString(30534), SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'rating', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502) + cConfig().getLocalizedString(30535), SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'votes', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502) + cConfig().getLocalizedString(30536), SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'views', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502) + cConfig().getLocalizedString(30537), SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showSeriesMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'releases', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511) + cConfig().getLocalizedString(30531), SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'trending', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511) + cConfig().getLocalizedString(30532), SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'updates', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511) + cConfig().getLocalizedString(30533), SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'requested', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511) + cConfig().getLocalizedString(30534), SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'rating', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511) + cConfig().getLocalizedString(30535), SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'votes', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511) + cConfig().getLocalizedString(30536), SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'views', '1')) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511) + cConfig().getLocalizedString(30537), SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| isTvshow = False | |
| sThumbnail = '' | |
| sLanguage = params.getValue('sLanguage') | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| try: | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # HTML Cache Zeit 6 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| aJson = loads(sJson) | |
| except: | |
| if not sGui: oGui.showInfo() | |
| return | |
| if 'movies' not in aJson or not isinstance(aJson.get('movies'), list) or len(aJson['movies']) == 0: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = 0 | |
| # ignore movies which does not contain any streams | |
| for movie in aJson['movies']: | |
| if '_id' in movie: | |
| total += 1 | |
| for movie in aJson['movies']: | |
| if not '_id' in movie: | |
| continue | |
| sTitle = str(movie['title']) | |
| if sSearchText and not cParser.search(sSearchText, sTitle): | |
| continue | |
| if 'Staffel' in sTitle or 'Season' in sTitle: | |
| isTvshow = True | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'showEpisodes' if isTvshow else 'showHosters') | |
| if 'poster_path_season' in movie and movie['poster_path_season']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path_season']) | |
| elif 'poster_path' in movie and movie['poster_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path']) | |
| elif 'backdrop_path' in movie and movie['backdrop_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['backdrop_path']) | |
| if sThumbnail: | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 'storyline' in movie: | |
| oGuiElement.setDescription(str(movie['storyline'])) | |
| elif 'overview' in movie: | |
| oGuiElement.setDescription(str(movie['overview'])) | |
| if 'year' in movie and len(str(movie['year'])) == 4: | |
| oGuiElement.setYear(movie['year']) | |
| if 'quality' in movie: | |
| oGuiElement.setQuality(_getQuality(movie['quality'])) | |
| if 'rating' in movie: | |
| oGuiElement.addItemValue('rating', movie['rating']) | |
| if 'lang' in movie: | |
| if (sLanguage != '1' and movie['lang'] == 2): # Deutsch | |
| oGuiElement.setLanguage('DE') | |
| if (sLanguage != '2' and movie['lang'] == 3): # Englisch | |
| oGuiElement.setLanguage('EN') | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| if 'runtime' in movie: | |
| isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], r'\d+') | |
| if isMatch: | |
| oGuiElement.addItemValue('duration', sRuntime) | |
| params.setParam('entryUrl', URL_WATCH % str(movie['_id'])) | |
| params.setParam('sName', sTitle) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui and not sSearchText: | |
| curPage = aJson['pager']['currentPage'] | |
| if curPage < aJson['pager']['totalPages']: | |
| sNextUrl = entryUrl.replace('page=' + str(curPage), 'page=' + str(curPage + 1)) | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('tvshows' if isTvshow else 'movies') | |
| oGui.setEndOfDirectory() | |
| def showEpisodes(): | |
| aEpisodes = [] | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue("sThumbnail") | |
| try: | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 4 # HTML Cache Zeit 4 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| aJson = loads(sJson) | |
| except: | |
| cGui().showInfo() | |
| return | |
| if 'streams' not in aJson or len(aJson['streams']) == 0: | |
| cGui().showInfo() | |
| return | |
| for stream in aJson['streams']: | |
| if 'e' in stream: | |
| aEpisodes.append(int(stream['e'])) | |
| if aEpisodes: | |
| aEpisodesSorted = set(aEpisodes) | |
| total = len(aEpisodesSorted) | |
| for sEpisode in aEpisodesSorted: | |
| oGuiElement = cGuiElement('Episode ' + str(sEpisode), SITE_IDENTIFIER, 'showHosters') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 's' in aJson: | |
| oGuiElement.setSeason(aJson['s']) | |
| oGuiElement.setTVShowTitle('Episode ' + str(sEpisode)) | |
| oGuiElement.setEpisode(sEpisode) | |
| oGuiElement.setMediaType('episode') | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| cGui().setView('episodes') | |
| cGui().setEndOfDirectory() | |
| def showHosters(): | |
| hosters = [] | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sEpisode = params.getValue('episode') | |
| try: | |
| oRequest = cRequestHandler(sUrl, caching=False) | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| except: | |
| return hosters | |
| if sJson: | |
| aJson = loads(sJson) | |
| if 'streams' in aJson: | |
| i = 0 | |
| for stream in aJson['streams']: | |
| if (('e' not in stream) or (str(sEpisode) == str(stream['e']))): | |
| sHoster = str(i) + ':' | |
| isMatch, aName = cParser.parse(stream['stream'], '//([^/]+)/') | |
| if isMatch: | |
| sName = aName[0][:aName[0].rindex('.')] | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| sHoster = sHoster + ' ' + sName | |
| if 'release' in stream and str(stream['release']) != '': | |
| sHoster = sHoster + ' [I][' + _getQuality(stream['release']) + '][/I]' | |
| hoster = {'link': stream['stream'], 'name': sHoster} | |
| hosters.append(hoster) | |
| i += 1 | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| showEntries(URL_SEARCH % (sLang, cParser.quotePlus(sSearchText), '1'), oGui, sSearchText) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Always pay attention to the translations in the menu! | |
| # HTML LangzeitCache hinzugefügt | |
| # showGenre: 48 Stunden | |
| # showEntries: 6 Stunden | |
| # showEpisodes: 4 Stunden | |
| import re | |
| import xbmcgui | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser, cUtil | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| from json import loads | |
| from datetime import datetime | |
| # Globale Variable für die JSON-Daten | |
| apiJson = None | |
| # Domain Abfrage ### | |
| SITE_IDENTIFIER = 'movie4k' | |
| SITE_NAME = 'Movie4k' | |
| SITE_ICON = 'movie4k.png' | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'movie4k.sx') | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| ORIGIN = 'https://' + DOMAIN + '/' | |
| REFERER = ORIGIN + '/' | |
| URL_API = 'https://' + DOMAIN | |
| URL_MAIN = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&page=%s' | |
| URL_SEARCH = URL_API + '/data/browse/?lang=%s&order_by=%s&page=%s&limit=0' | |
| URL_THUMBNAIL = 'https://image.tmdb.org/t/p/w300%s' | |
| URL_WATCH = URL_API + '/data/watch/?_id=%s' | |
| URL_GENRE = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&genre=%s&page=%s' | |
| URL_CAST = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&cast=%s&page=%s' | |
| URL_YEAR = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&year=%s&page=%s' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| def load(): | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| # Änderung des Sprachcodes nach voreigestellter Sprache | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| elif sLanguage == '3': # prefLang Japanisch | |
| sLang = cGui().showLanguage() | |
| return | |
| params.setParam('sLanguage', sLang) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502), SITE_IDENTIFIER, 'showMovieMenu'), params) # Movies | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30551), SITE_IDENTIFIER, 'showGenreMMenu'), params) # Movies Genre | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511), SITE_IDENTIFIER, 'showSeriesMenu'), params) # Series | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30529), SITE_IDENTIFIER, 'showGenreSMenu'), params) # Series Genre | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30508), SITE_IDENTIFIER, 'showYearsMenu'), params) # Years | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30553), SITE_IDENTIFIER, 'showSearchActor'), params) # Cast | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params) # Search | |
| cGui().setEndOfDirectory() | |
| def _cleanTitle(sTitle): | |
| sTitle = re.sub("[\xE4]", 'ae', sTitle) | |
| sTitle = re.sub("[\xFC]", 'ue', sTitle) | |
| sTitle = re.sub("[\xF6]", 'oe', sTitle) | |
| sTitle = re.sub("[\xC4]", 'Ae', sTitle) | |
| sTitle = re.sub("[\xDC]", 'Ue', sTitle) | |
| sTitle = re.sub("[\xD6]", 'Oe', sTitle) | |
| sTitle = re.sub("[\x00-\x1F\x80-\xFF]", '', sTitle) | |
| return sTitle | |
| def _getQuality(sQuality): | |
| isMatch, aResult = cParser.parse(sQuality, '(HDCAM|HD|WEB|BLUERAY|BRRIP|DVD|TS|SD|CAM)', 1, True) | |
| if isMatch: | |
| return aResult[0] | |
| else: | |
| return sQuality | |
| def _showGenreMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| sType = params.getValue('sType') | |
| sMenu = params.getValue('sMenu') | |
| if sLanguage == '2' or sLanguage == 'all': | |
| genres = { | |
| 'Action': 'Action', | |
| 'Abenteuer': 'Abenteuer', | |
| 'Animation': 'Animation', | |
| 'Biographie': 'Biographie', | |
| 'Dokumentation': 'Dokumentation', | |
| 'Drama': 'Drama', | |
| 'Familie': 'Familie', | |
| 'Fantasy': 'Fantasy', | |
| 'Geschichte': 'Geschichte', | |
| 'Horror': 'Horror', | |
| 'Komödie': 'Komödie', | |
| 'Krieg': 'Krieg', | |
| 'Krimi': 'Krimi', | |
| 'Musik': 'Musik', | |
| 'Mystery': 'Mystery', | |
| 'Romantik': 'Romantik', | |
| 'Reality-TV': 'Reality-TV', | |
| 'Sci-Fi': 'Sci-Fi', | |
| 'Sports': 'Sport', | |
| 'Thriller': 'Thriller', | |
| 'Western': 'Western' | |
| } | |
| else: | |
| genres = { | |
| 'Action': 'Action', | |
| 'Adventure': 'Abenteuer', | |
| 'Animation': 'Animation', | |
| 'Biography': 'Biographie', | |
| 'Comedy': 'Komödie', | |
| 'Crime': 'Krimi', | |
| 'Documentation': 'Dokumentation', | |
| 'Drama': 'Drama', | |
| 'Family': 'Familie', | |
| 'Fantasy': 'Fantasy', | |
| 'History': 'Geschichte', | |
| 'Horror': 'Horror', | |
| 'Music': 'Musik', | |
| 'Mystery': 'Mystery', | |
| 'Romance': 'Romantik', | |
| 'Reality-TV': 'Reality-TV', | |
| 'Sci-Fi': 'Sci-Fi', | |
| 'Sports': 'Sport', | |
| 'Thriller': 'Thriller', | |
| 'War': 'Krieg', | |
| 'Western': 'Western' | |
| } | |
| for genre, searchGenre in genres.items(): | |
| params.setParam('sUrl', URL_GENRE % (sLanguage, sType, sMenu, searchGenre, '1')) | |
| cGui().addFolder(cGuiElement(genre, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showMovieMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'Trending', '1')) ### Trending Filme trending1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30521), SITE_IDENTIFIER, 'showEntries'), params) ### Trending Filme trending1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'new', '1')) ### neue filme neu1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30541), SITE_IDENTIFIER, 'showEntries'), params) ### neue filme neu1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'views', '1')) ### Views filme views1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30552), SITE_IDENTIFIER, 'showEntries'), params) ### Views filme views1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'rating', '1')) ### Rating Filme rating1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30510), SITE_IDENTIFIER, 'showEntries'), params) ### Rating Filme rating1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'votes', '1')) ### votes filme votes 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30536), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'updates', '1')) ### updates filme updates 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30533), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'name', '1')) ### name filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'featured', '1')) ### featured filme features1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30530), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'requested', '1')) ### requested filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30534), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'releases', '1')) ### releases filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30531), SITE_IDENTIFIER, 'showEntries'), params) ### Filme releases 1 | |
| cGui().setEndOfDirectory() | |
| def showGenreMMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sType', 'movies') | |
| params.setParam('sMenu', 'Trending') | |
| cGui().addFolder(cGuiElement('Genre trending', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Neu') | |
| cGui().addFolder(cGuiElement('Genre new', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Views') | |
| cGui().addFolder(cGuiElement('Genre viewed', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Votes') | |
| cGui().addFolder(cGuiElement('Genre voted', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Updates') | |
| cGui().addFolder(cGuiElement('Genre updated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Rating') | |
| cGui().addFolder(cGuiElement('Genre rated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Name') | |
| cGui().addFolder(cGuiElement('Genre named', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'requested') | |
| cGui().addFolder(cGuiElement('Genre requested', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'featured') | |
| cGui().addFolder(cGuiElement('Genre featured', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'releases') | |
| cGui().addFolder(cGuiElement('Genre released', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| cGui().setEndOfDirectory() | |
| # Serienmenue | |
| def showSeriesMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'neu', '1')) ### serien neu 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30514), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'views', '1')) ### serien views 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30537), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'votes', '1')) ### serien votes 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30519), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'updates', '1')) ### serien updates 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30533), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'name', '1')) ### serien name 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'featured', '1')) ### | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30530), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'requested', '1')) ### serien requested | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30534), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'releases', '1')) ### serien releases 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30531), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'rating', '1')) ### serien rating 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30535), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| #params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'Jahr', '1')) # ## | |
| #cGui().addFolder(cGuiElement('Jahr', SITE_IDENTIFIER, 'showEntries'), params) ## | |
| #params.setParam('sCont', 'Jahr') # | |
| #cGui().addFolder(cGuiElement('Jahr', SITE_IDENTIFIER, 'showValue'), params), params) # | |
| cGui().setEndOfDirectory() | |
| # show genre serien menue | |
| def showGenreSMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sType', 'tvseries') | |
| params.setParam('sMenu', 'Trending') | |
| cGui().addFolder(cGuiElement('Series genre trending', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Neu') | |
| cGui().addFolder(cGuiElement('Series genre new', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Views') | |
| cGui().addFolder(cGuiElement('Series genre viewed', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Votes') | |
| cGui().addFolder(cGuiElement('Series genre voted', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Updates') | |
| cGui().addFolder(cGuiElement('Series genre updated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Rating') | |
| cGui().addFolder(cGuiElement('Series genre rated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Name') | |
| cGui().addFolder(cGuiElement('Series genre named', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'requested') | |
| cGui().addFolder(cGuiElement('Series genre requested', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'featured') | |
| cGui().addFolder(cGuiElement('Series genre featured', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'releases') | |
| cGui().addFolder(cGuiElement('Series genre releases', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| cGui().setEndOfDirectory() | |
| def showYearsMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| # Anfangs- und Endjahr für das menü eintragen | |
| start_jahr = 1931 | |
| end_jahr = datetime.now().year | |
| # show the current year first | |
| for jahr in range(end_jahr, start_jahr - 1, -1): | |
| params.setParam('sUrl', URL_YEAR % (sLanguage, 'movies', 'new', str(jahr), '1')) | |
| cGui().addFolder(cGuiElement(str(jahr), SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| isTvshow = False | |
| sThumbnail = '' | |
| sLanguage = params.getValue('sLanguage') | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| try: | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # HTML Cache Zeit 6 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| aJson = loads(sJson) | |
| except: | |
| if not sGui: oGui.showInfo() | |
| return | |
| if 'movies' not in aJson or not isinstance(aJson.get('movies'), list) or len(aJson['movies']) == 0: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = 0 | |
| # ignore movies which does not contain any streams | |
| for movie in aJson['movies']: | |
| if '_id' in movie: | |
| total += 1 | |
| for movie in aJson['movies']: | |
| if not '_id' in movie: | |
| continue | |
| sTitle = str(movie['title']) | |
| if sSearchText and not cParser.search(sSearchText, sTitle): | |
| continue | |
| if 'Staffel' in sTitle or 'Season' in sTitle: | |
| isTvshow = True | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'showEpisodes' if isTvshow else 'showHosters') | |
| if 'poster_path_season' in movie and movie['poster_path_season']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path_season']) | |
| elif 'poster_path' in movie and movie['poster_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path']) | |
| elif 'backdrop_path' in movie and movie['backdrop_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['backdrop_path']) | |
| if sThumbnail: | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 'storyline' in movie: | |
| oGuiElement.setDescription(str(movie['storyline'])) | |
| elif 'overview' in movie: | |
| oGuiElement.setDescription(str(movie['overview'])) | |
| if 'year' in movie and len(str(movie['year'])) == 4: | |
| oGuiElement.setYear(movie['year']) | |
| if 'quality' in movie: | |
| oGuiElement.setQuality(_getQuality(movie['quality'])) | |
| if 'rating' in movie: | |
| oGuiElement.addItemValue('rating', movie['rating']) | |
| if 'lang' in movie: | |
| if (sLanguage != '1' and movie['lang'] == 2): # Deutsch | |
| oGuiElement.setLanguage('DE') | |
| if (sLanguage != '2' and movie['lang'] == 3): # Englisch | |
| oGuiElement.setLanguage('EN') | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| if 'runtime' in movie: | |
| isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], '\d+') | |
| if isMatch: | |
| oGuiElement.addItemValue('duration', sRuntime) | |
| params.setParam('entryUrl', URL_WATCH % str(movie['_id'])) | |
| params.setParam('sName', sTitle) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui and not sSearchText: | |
| curPage = aJson['pager']['currentPage'] | |
| if curPage < aJson['pager']['totalPages']: | |
| sNextUrl = entryUrl.replace('page=' + str(curPage), 'page=' + str(curPage + 1)) | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('tvshows' if isTvshow else 'movies') | |
| oGui.setEndOfDirectory() | |
| def showEpisodes(): | |
| aEpisodes = [] | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue("sThumbnail") | |
| try: | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 4 # HTML Cache Zeit 4 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| aJson = loads(sJson) | |
| except: | |
| cGui().showInfo() | |
| return | |
| if 'streams' not in aJson or len(aJson['streams']) == 0: | |
| cGui().showInfo() | |
| return | |
| for stream in aJson['streams']: | |
| if 'e' in stream: | |
| aEpisodes.append(int(stream['e'])) | |
| if aEpisodes: | |
| aEpisodesSorted = set(aEpisodes) | |
| total = len(aEpisodesSorted) | |
| for sEpisode in aEpisodesSorted: | |
| oGuiElement = cGuiElement('Episode ' + str(sEpisode), SITE_IDENTIFIER, 'showHosters') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 's' in aJson: | |
| oGuiElement.setSeason(aJson['s']) | |
| oGuiElement.setTVShowTitle('Episode ' + str(sEpisode)) | |
| oGuiElement.setEpisode(sEpisode) | |
| oGuiElement.setMediaType('episode') | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| cGui().setView('episodes') | |
| cGui().setEndOfDirectory() | |
| def showHosters(): | |
| hosters = [] | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sEpisode = params.getValue('episode') | |
| try: | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 8 # HTML Cache Zeit 8 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| except: | |
| return hosters | |
| if sJson: | |
| aJson = loads(sJson) | |
| if 'streams' in aJson: | |
| i = 0 | |
| for stream in aJson['streams']: | |
| if (('e' not in stream) or (str(sEpisode) == str(stream['e']))): | |
| sHoster = str(i) + ':' | |
| isMatch, aName = cParser.parse(stream['stream'], '//([^/]+)/') | |
| if isMatch: | |
| # sName = cParser.urlparse(sUrl) ### angezeigter hostername api | |
| sName = aName[0][:aName[0].rindex('.')] | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| sHoster = sHoster + ' ' + sName | |
| if 'release' in stream and str(stream['release']) != '': | |
| sHoster = sHoster + ' [I][' + _getQuality(stream['release']) + '][/I]' | |
| hoster = {'link': stream['stream'], 'name': sHoster} | |
| hosters.append(hoster) | |
| i += 1 | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearchActor(): | |
| sName = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30280)) | |
| if not sName: return | |
| _searchActor(False, sName) | |
| cGui().setEndOfDirectory() | |
| def _searchActor(oGui, sName): | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| showEntries(URL_CAST % (sLanguage, 'movies', 'new', cParser.quotePlus(sName), '1'), oGui) | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| SSsearch(oGui, sSearchText) | |
| def SSsearch(sGui=False, sSearchText=False): | |
| global apiJson | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| # Falls die Daten noch nicht geladen wurden oder neu geladen werden sollen | |
| if apiJson is None or 'movies' not in apiJson: | |
| loadMoviesData() | |
| if 'movies' not in apiJson or not isinstance(apiJson.get('movies'), list) or len(apiJson['movies']) == 0: | |
| oGui.showInfo() | |
| return | |
| sst = sSearchText.lower() | |
| if not sGui: | |
| dialog = xbmcgui.DialogProgress() | |
| dialog.create(cConfig().getLocalizedString(30122), cConfig().getLocalizedString(30123)) | |
| total = len(apiJson['movies']) | |
| position = 0 | |
| for movie in apiJson['movies']: | |
| position += 1 | |
| if not '_id' in movie: | |
| continue | |
| if not sGui and position % 128 == 0: # Update progress every 128 items | |
| if dialog.iscanceled(): break | |
| dialog.update(position, str(position) + cConfig().getLocalizedString(30128) + str(total)) | |
| sTitle = movie['title'] | |
| if 'Staffel' in sTitle or 'Season' in sTitle: | |
| isTvshow = True | |
| sSearch = sTitle.rsplit('-', 1)[0].replace(' ', '').lower() | |
| else: | |
| isTvshow = False | |
| sSearch = sTitle.lower() | |
| if not sst in sSearch and not cUtil.isSimilarByToken(sst, sSearch): | |
| continue | |
| #logger.info('-> [DEBUG]: %s' % str(movie)) | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'showEpisodes' if isTvshow else 'showHosters') | |
| sThumbnail = '' | |
| if 'poster_path_season' in movie and movie['poster_path_season']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path_season']) | |
| elif 'poster_path' in movie and movie['poster_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path']) | |
| elif 'backdrop_path' in movie and movie['backdrop_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['backdrop_path']) | |
| if sThumbnail: | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 'storyline' in movie: | |
| oGuiElement.setDescription(str(movie['storyline'])) | |
| elif 'overview' in movie: | |
| oGuiElement.setDescription(str(movie['overview'])) | |
| if 'year' in movie and len(str(movie['year'])) == 4: | |
| oGuiElement.setYear(movie['year']) | |
| if 'quality' in movie: | |
| oGuiElement.setQuality(_getQuality(movie['quality'])) | |
| if 'rating' in movie: | |
| oGuiElement.addItemValue('rating', movie['rating']) | |
| if 'lang' in movie: | |
| if (sLanguage != '1' and movie['lang'] == 2): # Deutsch | |
| oGuiElement.setLanguage('DE') | |
| if (sLanguage != '2' and movie['lang'] == 3): # Englisch | |
| oGuiElement.setLanguage('EN') | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| if 'runtime' in movie: | |
| isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], '\d+') | |
| if isMatch: | |
| oGuiElement.addItemValue('duration', sRuntime) | |
| params.setParam('entryUrl', URL_WATCH % str(movie['_id'])) | |
| params.setParam('sName', sTitle) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui: | |
| dialog.close() | |
| def loadMoviesData(): | |
| global apiJson | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| try: | |
| oRequest = cRequestHandler(URL_SEARCH % (sLang, 'new', '1'), caching=True) | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| oRequest.cacheTime = 60 * 60 * 48 # HTML Cache Zeit 2 Tage | |
| sJson = oRequest.request() | |
| apiJson = loads(sJson) | |
| logger.info('API-Daten erfolgreich geladen') | |
| except: | |
| logger.error('Fehler beim Laden der API-Daten') | |
| apiJson = {'movies': []} | |
| # Daten werden lazy beim ersten Zugriff geladen (siehe SSsearch) | |
| # loadMoviesData() - entfernt: beschleunigt den Import/Start erheblich |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| # Always pay attention to the translations in the menu! | |
| # HTML LangzeitCache hinzugefügt | |
| # showEntries: 6 Stunden | |
| # showEntriesUnJson:6 Stunden | |
| import json | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| SITE_IDENTIFIER = 'netzkino' | |
| SITE_NAME = 'NetzKino' | |
| SITE_ICON = 'netzkino.png' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| # Domain Abfrage | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'www.netzkino.de') # Domain Auswahl über die xStream Einstellungen möglich | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| URL_MAIN = 'https://api.netzkino.de.simplecache.net/capi-2.0a/categories/%s.json?d=www&l=de-DE' | |
| URL_SEARCH = 'https://api.netzkino.de.simplecache.net/capi-2.0a/search?q=%s&d=www&l=de-DE' | |
| URL_START = 'https://' + DOMAIN + '/category/%s' | |
| # | |
| def load(): # Menu structure of the site plugin | |
| logger.info('Load %s' % SITE_NAME) | |
| oGui = cGui() | |
| params = ParameterHandler() | |
| cGui().addFolder(cGuiElement('Startseite', SITE_IDENTIFIER, 'showStart'), params) # Startseite | |
| oGui.addFolder(cGuiElement('Genres', SITE_IDENTIFIER, 'showGenreMenu')) | |
| params.setParam('sUrl', URL_START % 'themenkino-genre') | |
| oGui.addFolder(cGuiElement('Themenkino', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| oGui.addFolder(cGuiElement('Suche', SITE_IDENTIFIER, 'showSearch'), params) | |
| oGui.setEndOfDirectory() | |
| def showStart(): | |
| params = ParameterHandler() | |
| params.setParam('sUrl', URL_MAIN % 'highlights-frontpage') | |
| cGui().addFolder(cGuiElement('Highlights', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'neu-frontpage') | |
| cGui().addFolder(cGuiElement('Neu bei Netzkino', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'blockbuster-kultfilme-frontpage') | |
| params.setParam('sUrl', URL_START % 'actionfilme_frontpage') | |
| cGui().addFolder(cGuiElement('Actionfilme', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_START % 'top-rated-imdb_frontpage') | |
| cGui().addFolder(cGuiElement('Top Rated IMDb', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_MAIN % 'blockbuster-kultfilme-frontpage') | |
| cGui().addFolder(cGuiElement('Blockbuster & Kultfilme', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_START % 'kriegsfilme-frontpage') | |
| cGui().addFolder(cGuiElement('Beliebte Kriegsfilme', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_MAIN % 'meisgesehene_filme-frontpage') | |
| cGui().addFolder(cGuiElement('Meistgesehene Filme', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_START % 'top-dokumentationen') | |
| cGui().addFolder(cGuiElement('Top Dokumentationen', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_START % 'horrortime_frontpage') | |
| cGui().addFolder(cGuiElement('Horrortime', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_START % 'Thriller-frontpage') | |
| cGui().addFolder(cGuiElement('Thriller', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_MAIN % 'komodien-frontpage') | |
| cGui().addFolder(cGuiElement('Komödien', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_START % 'Zombiefilme-frontpage') | |
| cGui().addFolder(cGuiElement('Zombiefilme', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_START % 'Hollywood-Filme-frontpage') | |
| cGui().addFolder(cGuiElement('Hollywood Filme', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_MAIN % 'beste-bewertung-frontpage') | |
| cGui().addFolder(cGuiElement('Beste Bewertung', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'mockbuster-frontpage') | |
| cGui().addFolder(cGuiElement('Mockbuster', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'frontpage-exklusiv-frontpage') | |
| cGui().addFolder(cGuiElement('Die schönsten Märchen', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'empfehlungen_woche-frontpage') | |
| cGui().addFolder(cGuiElement('Unsere Empfehlungen der Woche', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'filme_mit_auszeichnungen-frontpage') | |
| cGui().addFolder(cGuiElement('Filme mit Auszeichnungen', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'top-20-frontpage') | |
| cGui().addFolder(cGuiElement('Top 20 - Action Classics', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_START % 'science-fiction-fantasy_frontpage') | |
| cGui().addFolder(cGuiElement('Science Fiction & Fantasy', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_START % 'Familienfilme-frontpage') | |
| cGui().addFolder(cGuiElement('Familienfilme', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_START % 'must-see-frontpage') | |
| cGui().addFolder(cGuiElement('Must-See', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_START % 'Deutsche-Filme-frontpage') | |
| cGui().addFolder(cGuiElement('Deutsche Filme', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_START % 'Drama-frontpage') | |
| cGui().addFolder(cGuiElement('Die besten Drama-Filme', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_START % 'western-frontpage') | |
| cGui().addFolder(cGuiElement('Western', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_START % 'Independent-Filme-frontpage') | |
| cGui().addFolder(cGuiElement('Independent-Filme', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_START % 'history-dokus-frontpage') | |
| cGui().addFolder(cGuiElement('History Dokus', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_START % 'Action-Abenteuer-frontpage') | |
| cGui().addFolder(cGuiElement('Action & Abenteuer', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_START % 'Romantic-Comedies-frontpage') | |
| cGui().addFolder(cGuiElement('Romantic Comedies', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| params.setParam('sUrl', URL_START % '90er-Jahre-frontpage') | |
| cGui().addFolder(cGuiElement('Die besten Filme der 90er Jahre', SITE_IDENTIFIER, 'showEntriesUnJson'), params) | |
| cGui().setEndOfDirectory() | |
| def showGenreMenu(): | |
| oGui = cGui() | |
| params = ParameterHandler() | |
| params.setParam('sUrl', URL_MAIN % 'actionkino') | |
| oGui.addFolder(cGuiElement('Actionkino', SITE_IDENTIFIER, 'showEntries'), params) | |
| #params.setParam('sUrl', URL_MAIN % 'animekino') | |
| #oGui.addFolder(cGuiElement('Animekino', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'arthousekino') | |
| oGui.addFolder(cGuiElement('Arthousekino', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'asiakino') | |
| oGui.addFolder(cGuiElement('Asiakino', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'dramakino') | |
| oGui.addFolder(cGuiElement('Dramakino', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'thrillerkino') | |
| oGui.addFolder(cGuiElement('Thrillerkino', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'liebesfilmkino') | |
| oGui.addFolder(cGuiElement('Liebesfilmkino', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'scifikino') | |
| oGui.addFolder(cGuiElement('Scifikino', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'kinderkino') | |
| oGui.addFolder(cGuiElement('Kinderkino', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'spasskino') | |
| oGui.addFolder(cGuiElement('Spasskino', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'horrorkino') | |
| oGui.addFolder(cGuiElement('Horrorkino', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'thrillerkino') | |
| oGui.addFolder(cGuiElement('Thrillerkino', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MAIN % 'kinoab18') | |
| oGui.addFolder(cGuiElement('Kino ab 18', SITE_IDENTIFIER, 'showEntries'), params) | |
| oGui.setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=sGui is not False) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # 6 Stunden | |
| jSearch = json.loads(oRequest.request()) # Lade JSON aus dem Request der URL | |
| if not jSearch: return # # Wenn Suche erfolglos - Abbruch | |
| if 'posts' not in jSearch or len(jSearch['posts']) == 0: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(jSearch['posts']) | |
| for item in jSearch['posts']: | |
| try: | |
| if sSearchText and not cParser.search(sSearchText, item['title']): | |
| continue | |
| oGuiElement = cGuiElement(str(item['title']), SITE_IDENTIFIER, 'showHosters') | |
| oGuiElement.setThumbnail(str(item['thumbnail'])) | |
| oGuiElement.setDescription(str(item['content'])) | |
| oGuiElement.setFanart(str(item['custom_fields']['featured_img_all'][0])) | |
| oGuiElement.setYear(str(item['custom_fields']['Jahr'][0])) | |
| oGuiElement.setQuality(str(item['custom_fields']['Adaptives_Streaming'][0])) | |
| oGuiElement.setMediaType('movie') | |
| if 'Duration' in item['custom_fields'] and item['custom_fields']['Duration'][0]: | |
| oGuiElement.addItemValue('duration', item['custom_fields']['Duration'][0]) | |
| urls = '' | |
| if 'Streaming' in item['custom_fields'] and item['custom_fields']['Streaming'][0]: | |
| urls += 'https://pmd.netzkino-seite.netzkino.de/%s.mp4' % item['custom_fields']['Streaming'][0] | |
| if 'Youtube_Delivery_Id' in item['custom_fields'] and item['custom_fields']['Youtube_Delivery_Id'][0]: | |
| urls += '#' + 'plugin://plugin.video.youtube/play/?video_id=%s' % item['custom_fields']['Youtube_Delivery_Id'][0] | |
| params.setParam('entryUrl', urls) | |
| oGui.addFolder(oGuiElement, params, False, total) | |
| except: | |
| continue | |
| if not sGui: | |
| oGui.setView('movies') | |
| oGui.setEndOfDirectory() | |
| def showEntriesUnJson(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False)) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # 6 Stunden | |
| sHtmlContent = oRequest.request() | |
| #Aufbau pattern | |
| #'item":.*?' # Container Start | |
| #'image.*?(https[^"]+).*?' # Image | |
| #'name":\s.*?([^"]+).*?' # Name | |
| #'url":\s.*?([^"]+).*?' # URL | |
| #'(.*?)}' # Dummy | |
| pattern = r'item":.*?image.*?(https[^"]+).*?name":\s.*?([^"]+).*?url":\s.*?([^"]+).*?(.*?)}' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sThumbnail, sName, sUrl, sDummy in aResult: | |
| try: | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| isDuration, sDurationH = cParser.parseSingleResult(sDummy, r'duration":\s"([\d]+).*?') # Laufzeit Stunden | |
| isDuration, sDurationM = cParser.parseSingleResult(sDummy, r'H([\d]+).*?') # Laufzeit Minuten | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showHostersUnJson') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if isDuration: | |
| oGuiElement.addItemValue('duration', int(sDurationH) *60 + int(sDurationM)) | |
| oGuiElement.setMediaType('movie') | |
| params.setParam('entryUrl', sUrl) | |
| params.setParam('sName', sName) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, False, total) | |
| except: | |
| continue | |
| if not sGui: | |
| oGui.setView('movies') | |
| oGui.setEndOfDirectory() | |
| def showHosters(): | |
| hosters = [] | |
| URL = ParameterHandler().getValue('entryUrl') | |
| for sUrl in URL.split('#'): | |
| hoster = {'link': sUrl, 'name': 'Netzkino' if 'netzkino' in sUrl else 'Youtube', 'resolveable': True} | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def showHostersUnJson(): | |
| hosters = [] | |
| sHtmlContent = cRequestHandler(ParameterHandler().getValue('entryUrl')).request() | |
| isMatch, aResult = cParser.parse(sHtmlContent, 'pmdUrl":"([^"]+)') | |
| if isMatch: | |
| for sUrl in aResult: | |
| sName = 'Netzkino' | |
| sUrl = 'https://pmd.netzkino-seite.netzkino.de/' + sUrl | |
| hoster = {'link': sUrl, 'name': sName, 'resolveable': True} | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': True}] | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30287)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| showEntries(URL_SEARCH % cParser.quotePlus(sSearchText), oGui, sSearchText) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| # Always pay attention to the translations in the menu! | |
| # Sprachauswahl für Hoster enthalten. | |
| # Ajax Suchfunktion enthalten. | |
| # HTML LangzeitCache hinzugefügt | |
| # showValue: 24 Stunden | |
| # showAllSeries: 24 Stunden | |
| # showEpisodes: 4 Stunden | |
| # SSsearch: 24 Stunden | |
| # 2022-12-06 Heptamer - Suchfunktion überarbeitet | |
| import xbmcgui | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser, cUtil | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| SITE_IDENTIFIER = 'serienstream' | |
| SITE_NAME = 'SerienStream' | |
| SITE_ICON = 'serienstream.png' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| # Domain Abfrage | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain') # Domain Auswahl über die xStream Einstellungen möglich | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| # URL_MAIN = 'https://s.to/' | |
| if DOMAIN == '186.2.175.5': # Bei Proxy Änderung nur IP hier in den Settings und in Zeile 53 tauschen. | |
| URL_MAIN = 'http://' + DOMAIN | |
| REFERER = 'http://' + DOMAIN | |
| proxy = 'true' | |
| else: | |
| URL_MAIN = 'https://' + DOMAIN | |
| REFERER = 'https://' + DOMAIN | |
| proxy = 'false' | |
| URL_SERIES = URL_MAIN + '/serien' | |
| URL_NEW_SERIES = URL_MAIN + '/neu' | |
| URL_NEW_EPISODES = URL_MAIN + '/neue-episoden' | |
| URL_POPULAR = URL_MAIN + '/beliebte-serien' | |
| URL_LOGIN = URL_MAIN + '/login' | |
| # Wenn DNS Bypass aktiv nutze Proxy Server | |
| if cConfig().getSetting('bypassDNSlock') == 'true': | |
| cConfig().setSetting('plugin_' + SITE_IDENTIFIER + '.domain', '186.2.175.5') | |
| # | |
| def load(): # Menu structure of the site plugin | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| username = cConfig().getSetting('serienstream.user')# Username | |
| password = cConfig().getSetting('serienstream.pass')# Password | |
| if username == '' or password == '': # If no username and password were set, close the plugin! | |
| xbmcgui.Dialog().ok(cConfig().getLocalizedString(30241), cConfig().getLocalizedString(30264)) # Info Dialog! | |
| else: | |
| params.setParam('sUrl', URL_SERIES) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30518), SITE_IDENTIFIER, 'showAllSeries'), params)# All Series | |
| params.setParam('sUrl', URL_NEW_SERIES) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30514), SITE_IDENTIFIER, 'showEntries'), params) # New Series | |
| params.setParam('sUrl', URL_NEW_EPISODES) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30516), SITE_IDENTIFIER, 'showNewEpisodes'), params) # New Episodes | |
| params.setParam('sUrl', URL_POPULAR) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30519), SITE_IDENTIFIER, 'showEntries'), params) # Popular Series | |
| params.setParam('sUrl', URL_MAIN) | |
| params.setParam('sCont', 'catalogNav') | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showValue'), params) # From A-Z | |
| params.setParam('sCont', 'homeContentGenresList') | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30506), SITE_IDENTIFIER, 'showValue'), params) # Genre | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params) # Search | |
| cGui().setEndOfDirectory() | |
| def showValue(): | |
| params = ParameterHandler() | |
| sUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 24 # HTML Cache Zeit 1 Tag | |
| sHtmlContent = oRequest.request() | |
| isMatch, sContainer = cParser.parseSingleResult(sHtmlContent, r'<ul[^>]*class="%s"[^>]*>(.*?)<\/ul>' % params.getValue('sCont')) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sContainer, r'<li>\s*<a[^>]*href="([^"]*)"[^>]*>(.*?)<\/a>\s*<\/li>') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| sUrl = sUrl if sUrl.startswith('http') else URL_MAIN + sUrl | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showAllSeries(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False)) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 24 # HTML Cache Zeit 1 Tag | |
| sHtmlContent = oRequest.request() | |
| pattern = r'<a[^>]*href="(\/serie\/[^"]*)"[^>]*>(.*?)</a>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sUrl, sName in aResult: | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showSeasons') | |
| oGuiElement.setMediaType('tvshow') | |
| params.setParam('sUrl', URL_MAIN + sUrl) | |
| params.setParam('TVShowTitle', sName) | |
| oGui.addFolder(oGuiElement, params, True, total) | |
| if not sGui: | |
| oGui.setView('tvshows') | |
| oGui.setEndOfDirectory() | |
| def showNewEpisodes(entryUrl=False, sGui=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| if not entryUrl: | |
| entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False)) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 4 # HTML Cache Zeit 4 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = r'<div[^>]*class="col-md-[^"]*"[^>]*>\s*<a[^>]*href="([^"]*)"[^>]*>\s*<strong>([^<]+)</strong>\s*<span[^>]*>([^<]+)</span>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sUrl, sName, sInfo in aResult: | |
| sMovieTitle = sName + ' ' + sInfo | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showSeasons') | |
| oGuiElement.setMediaType('tvshow') | |
| oGuiElement.setTitle(sMovieTitle) | |
| params.setParam('sUrl', URL_MAIN + sUrl) | |
| params.setParam('TVShowTitle', sMovieTitle) | |
| oGui.addFolder(oGuiElement, params, True, total) | |
| if not sGui: | |
| oGui.setView('tvshows') | |
| oGui.setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| if not entryUrl: | |
| entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False)) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # HTML Cache Zeit 6 Stunden | |
| sHtmlContent = oRequest.request() | |
| #Aufbau pattern | |
| #'<div[^>]*class="col-md-[^"]*"[^>]*>.*?' # start element | |
| #'<a[^>]*href="([^"]*)"[^>]*>.*?' # url | |
| #'data-src="([^"]*).*?' # thumbnail | |
| #'<h3>(.*?)<span[^>]*class="paragraph-end">.*?' # title | |
| #'<\\/div>' # end element | |
| pattern = '<div[^>]*class="col-md-[^"]*"[^>]*>.*?<a[^>]*href="([^"]*)"[^>]*>.*?data-src="([^"]*).*?<h3>(.*?)<span[^>]*class="paragraph-end">.*?</div>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sUrl, sThumbnail, sName in aResult: | |
| if sThumbnail.startswith('/'): | |
| sThumbnail = URL_MAIN + sThumbnail | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showSeasons') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| oGuiElement.setMediaType('tvshow') | |
| params.setParam('sUrl', URL_MAIN + sUrl) | |
| params.setParam('TVShowTitle', sName) | |
| oGui.addFolder(oGuiElement, params, True, total) | |
| if not sGui: | |
| pattern = 'pagination">.*?<a href="([^"]+)">></a>.*?</a></div>' | |
| isMatchNextPage, sNextUrl = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatchNextPage: | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('tvshows') | |
| oGui.setEndOfDirectory() | |
| def showSeasons(): | |
| params = ParameterHandler() | |
| sUrl = params.getValue('sUrl') | |
| sTVShowTitle = params.getValue('TVShowTitle') | |
| oRequest = cRequestHandler(sUrl) | |
| sHtmlContent = oRequest.request() | |
| pattern = r'<div[^>]*class="hosterSiteDirectNav"[^>]*>.*?<ul>(.*?)<\/ul>' | |
| isMatch, sContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| pattern = '<a[^>]*href="([^"]*)"[^>]*title="([^"]*)"[^>]*>(.*?)</a>.*?' | |
| isMatch, aResult = cParser.parse(sContainer, pattern) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| isDesc, sDesc = cParser.parseSingleResult(sHtmlContent, '<p[^>]*data-full-description="(.*?)"[^>]*>') | |
| isThumbnail, sThumbnail = cParser.parseSingleResult(sHtmlContent, '<div[^>]*class="seriesCoverBox"[^>]*>.*?data-src="([^"]*)"[^>]*>') | |
| if isThumbnail: | |
| if sThumbnail.startswith('/'): | |
| sThumbnail = URL_MAIN + sThumbnail | |
| total = len(aResult) | |
| for sUrl, sName, sNr in aResult: | |
| isMovie = sUrl.endswith('filme') | |
| if 'Alle Filme' in sName: | |
| sName = 'Filme' | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showEpisodes') | |
| oGuiElement.setMediaType('season' if not isMovie else 'movie') | |
| if isThumbnail: | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if isDesc: | |
| oGuiElement.setDescription(sDesc) | |
| if not isMovie: | |
| oGuiElement.setTVShowTitle(sTVShowTitle) | |
| oGuiElement.setSeason(sNr) | |
| params.setParam('sSeason', sNr) | |
| params.setParam('sThumbnail', sThumbnail) | |
| params.setParam('sUrl', URL_MAIN + sUrl) | |
| cGui().addFolder(oGuiElement, params, True, total) | |
| cGui().setView('seasons') | |
| cGui().setEndOfDirectory() | |
| def showEpisodes(): | |
| params = ParameterHandler() | |
| sUrl = params.getValue('sUrl') | |
| sTVShowTitle = params.getValue('TVShowTitle') | |
| sSeason = params.getValue('sSeason') | |
| sThumbnail = params.getValue('sThumbnail') | |
| if not sSeason: | |
| sSeason = '0' | |
| isMovieList = sUrl.endswith('filme') | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 4 # HTML Cache Zeit 4 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = r'<table[^>]*class="seasonEpisodesList"[^>]*>(.*?)</table>' | |
| isMatch, sContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| if isMovieList == True: | |
| pattern = r'<tr[^>]*data-episode-season-id="(\d+).*?<a href="([^"]+)">\s([^<]+).*?<strong>([^<]+)' | |
| isMatch, aResult = cParser.parse(sContainer, pattern) | |
| if not isMatch: | |
| pattern = r'<tr[^>]*data-episode-season-id="(\d+).*?<a href="([^"]+)">\s([^<]+).*?<span>([^<]+)' | |
| isMatch, aResult = cParser.parse(sContainer, pattern) | |
| else: | |
| pattern = r'<tr[^>]*data-episode-season-id="(\d+).*?<a href="([^"]+).*?(?:<strong>(.*?)</strong>.*?)?(?:<span>(.*?)</span>.*?)?<' | |
| isMatch, aResult = cParser.parse(sContainer, pattern) | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| isDesc, sDesc = cParser.parseSingleResult(sHtmlContent, '<p[^>]*data-full-description="(.*?)"[^>]*>') | |
| total = len(aResult) | |
| for sID, sUrl2, sNameGer, sNameEng in aResult: | |
| sName = '%d - ' % int(sID) | |
| if isMovieList == True: | |
| sName += sNameGer + '- ' + sNameEng | |
| else: | |
| sName += sNameGer if sNameGer else sNameEng | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showHosters') | |
| oGuiElement.setMediaType('episode' if not isMovieList else 'movie') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if isDesc: | |
| oGuiElement.setDescription(sDesc) | |
| if not isMovieList: | |
| oGuiElement.setSeason(sSeason) | |
| oGuiElement.setEpisode(int(sID)) | |
| oGuiElement.setTVShowTitle(sTVShowTitle) | |
| params.setParam('sUrl', URL_MAIN + sUrl2) | |
| params.setParam('entryUrl', sUrl) | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| cGui().setView('episodes' if not isMovieList else 'movies') | |
| cGui().setEndOfDirectory() | |
| def showHosters(): | |
| hosters = [] | |
| sUrl = ParameterHandler().getValue('sUrl') | |
| sHtmlContent = cRequestHandler(sUrl, caching=False).request() | |
| if cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain') == 'serienstream.stream': | |
| pattern = r'<li[^>]*episodeLink([^"]+)"\sdata-lang-key="([^"]+).*?data-link-target=([^"]+).*?<h4>([^<]+)<([^>]+)' | |
| pattern2 = r'itemprop="keywords".content=".*?Season...([^"]+).S.*?' # HD Kennzeichen | |
| # data-lang-key="1" Deutsch | |
| # data-lang-key="2" Englisch | |
| # data-lang-key="3" Englisch mit deutschen Untertitel | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| aResult2 = cParser.parse(sHtmlContent, pattern2) # pattern 2 auslesen | |
| if isMatch: | |
| for sID, sLang, sUrl, sName, sQuality in aResult: | |
| sUrl = sUrl.replace(sUrl, '') | |
| sUrl = sUrl.replace('', '/redirect/' + sID) | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '1': # Voreingestellte Sprache Deutsch in settings.xml | |
| if '2' in sLang: # data-lang-key="2" English | |
| continue | |
| if '3' in sLang: # data-lang-key="3" Englisch mit deutschen Untertitel | |
| continue | |
| if sLang == '1': # data-lang-key="1" Deutsch | |
| sLang = '(DE)' # Anzeige der Sprache Deutsch | |
| if sLanguage == '2': # Voreingestellte Sprache Englisch in settings.xml | |
| if '1' in sLang: # data-lang-key="1" Deutsch | |
| continue | |
| if '3' in sLang: # data-lang-key="3" Englisch mit deutschen Untertitel | |
| continue | |
| if sLang == '2': # data-lang-key="2" English | |
| sLang = '(EN)' # Anzeige der Sprache | |
| if sLanguage == '3': # Voreingestellte Sprache Japanisch in settings.xml | |
| cGui().showLanguage() # Kein Eintrag in der ausgewählten Sprache verfügbar | |
| continue | |
| if sLanguage == '0': # Alle Sprachen | |
| if sLang == '1': # data-lang-key="1" Deutsch | |
| sLang = '(DE)' # Anzeige der Sprache Deutsch | |
| if sLang == '2': # data-lang-key="2" Englisch | |
| sLang = '(EN)' # Anzeige der Sprache Englisch | |
| elif sLang == '3': # data-lang-key="3" Englisch mit deutschen Untertitel | |
| sLang = '(EN) Sub: (DE)' # Anzeige der Sprache Englisch mit deutschen Untertitel | |
| if 'HD' in aResult2[1]: # Prüfen ob tuple aResult2 das Kennzeichen HD enthält, dann übersteuern | |
| sQuality = '720' | |
| else: | |
| sQuality = '480' | |
| # Ab hier wird der sName mit abgefragt z.B: | |
| # aus dem Log [serienstream]: ['/redirect/12286260', 'VOE'] | |
| # hier ist die sUrl = '/redirect/12286260' und der sName 'VOE' | |
| # hoster.py 194 | |
| hoster = {'link': [sUrl, sName], 'name': sName, 'displayedName': '%s [I]%s [%sp][/I]' % (sName, sLang, sQuality), 'quality': sQuality, 'languageCode': sLang} # Language Code für hoster.py Sprache Prio | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| if not hosters: | |
| cGui().showLanguage() | |
| return hosters | |
| else: | |
| pattern = r'<li[^>]*data-lang-key="([^"]+).*?data-link-target="([^"]+).*?<h4>([^<]+)<([^>]+)' | |
| pattern2 = r'itemprop="keywords".content=".*?Season...([^"]+).S.*?' # HD Kennzeichen | |
| # data-lang-key="1" Deutsch | |
| # data-lang-key="2" Englisch | |
| # data-lang-key="3" Englisch mit deutschen Untertitel | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| aResult2 = cParser.parse(sHtmlContent, pattern2) # pattern 2 auslesen | |
| if isMatch: | |
| for sLang, sUrl, sName, sQuality in aResult: | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '1': # Voreingestellte Sprache Deutsch in settings.xml | |
| if '2' in sLang: # data-lang-key="2" | |
| continue | |
| if '3' in sLang: # data-lang-key="3" | |
| continue | |
| if sLang == '1': # data-lang-key="1" | |
| sLang = '(DE)' # Anzeige der Sprache | |
| if sLanguage == '2': # Voreingestellte Sprache Englisch in settings.xml | |
| if '1' in sLang: # data-lang-key="1" | |
| continue | |
| if '3' in sLang: # data-lang-key="3" | |
| continue | |
| if sLang == '2': # data-lang-key="2" | |
| sLang = '(EN)' # Anzeige der Sprache | |
| if sLanguage == '3': # Voreingestellte Sprache Japanisch in settings.xml | |
| cGui().showLanguage() # Kein Eintrag in der ausgewählten Sprache verfügbar | |
| continue | |
| if sLanguage == '0': # Alle Sprachen | |
| if sLang == '1': # data-lang-key="1" | |
| sLang = '(DE)' # Anzeige der Sprache | |
| if sLang == '2': # data-lang-key="2" | |
| sLang = '(EN)' # Anzeige der Sprache | |
| elif sLang == '3': # data-lang-key="3" | |
| sLang = '(EN) Sub: (DE)' # Anzeige der Sprache | |
| if 'HD' in aResult2[1]: # Prüfen ob tuple aResult2 das Kennzeichen HD enthält, dann übersteuern | |
| sQuality = '720' | |
| else: | |
| sQuality = '480' | |
| # Ab hier wird der sName mit abgefragt z.B: | |
| # aus dem Log [serienstream]: ['/redirect/12286260', 'VOE'] | |
| # hier ist die sUrl = '/redirect/12286260' und der sName 'VOE' | |
| # hoster.py 194 | |
| hoster = {'link': [sUrl, sName], 'name': sName, 'displayedName': '%s [I]%s [%sp][/I]' % (sName, sLang, sQuality), 'quality': sQuality, 'languageCode': sLang} # Language Code für hoster.py Sprache Prio | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| if not hosters: | |
| cGui().showLanguage() | |
| return hosters | |
| def getHosterUrl(hUrl): | |
| if type(hUrl) == str: hUrl = eval(hUrl) | |
| username = cConfig().getSetting('serienstream.user') | |
| password = cConfig().getSetting('serienstream.pass') | |
| Handler = cRequestHandler(URL_LOGIN, caching=False) | |
| Handler.addHeaderEntry('Upgrade-Insecure-Requests', '1') | |
| Handler.addHeaderEntry('Referer', ParameterHandler().getValue('entryUrl')) | |
| Handler.addParameters('email', username) | |
| Handler.addParameters('password', password) | |
| Handler.request() | |
| Request = cRequestHandler(URL_MAIN + hUrl[0], caching=False) | |
| Request.addHeaderEntry('Referer', ParameterHandler().getValue('entryUrl')) | |
| Request.addHeaderEntry('Upgrade-Insecure-Requests', '1') | |
| Request.request() | |
| sUrl = Request.getRealUrl() | |
| if 'voe' in hUrl[1].lower(): | |
| isBlocked, sDomain = cConfig().isBlockedHoster(sUrl) # Die funktion gibt 2 werte zurück! | |
| if isBlocked: # Voe Pseudo sDomain nicht bekannt in resolveUrl | |
| sUrl = sUrl.replace(sDomain, 'voe.sx') | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| SSsearch(oGui, sSearchText) | |
| def SSsearch(sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| params.getValue('sSearchText') | |
| oRequest = cRequestHandler(URL_SERIES, caching=True, ignoreErrors=(sGui is not False)) | |
| oRequest.addHeaderEntry('X-Requested-With', 'XMLHttpRequest') | |
| oRequest.addHeaderEntry('Referer', REFERER + '/serien') | |
| oRequest.addHeaderEntry('Origin', REFERER) | |
| oRequest.addHeaderEntry('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8') | |
| oRequest.addHeaderEntry('Upgrade-Insecure-Requests', '1') | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 24 # HTML Cache Zeit 1 Tag | |
| sHtmlContent = oRequest.request() | |
| if not sHtmlContent: | |
| return | |
| sst = sSearchText.lower() | |
| pattern = r'<li><a data.+?href="([^"]+)".+?">(.*?)\<\/a><\/l' #link - title | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| if not aResult[0]: | |
| oGui.showInfo() | |
| return | |
| total = len(aResult[1]) | |
| for link, title in aResult[1]: | |
| titleLow = title.lower() | |
| if not sst in titleLow and not cUtil.isSimilarByToken(sst, titleLow): | |
| continue | |
| else: | |
| #get images thumb / descr pro call. (optional) | |
| try: | |
| sThumbnail, sDescription = getMetaInfo(link, title) | |
| oGuiElement = cGuiElement(title, SITE_IDENTIFIER, 'showSeasons') | |
| oGuiElement.setThumbnail(URL_MAIN + sThumbnail) | |
| oGuiElement.setDescription(sDescription) | |
| oGuiElement.setTVShowTitle(title) | |
| oGuiElement.setMediaType('tvshow') | |
| params.setParam('sUrl', URL_MAIN + link) | |
| params.setParam('sName', title) | |
| oGui.addFolder(oGuiElement, params, True, total) | |
| except Exception: | |
| oGuiElement = cGuiElement(title, SITE_IDENTIFIER, 'showSeasons') | |
| oGuiElement.setTVShowTitle(title) | |
| oGuiElement.setMediaType('tvshow') | |
| params.setParam('sUrl', URL_MAIN + link) | |
| params.setParam('sName', title) | |
| oGui.addFolder(oGuiElement, params, True, total) | |
| if not sGui: | |
| oGui.setView('tvshows') | |
| def getMetaInfo(link, title): # Setzen von Metadata in Suche: | |
| oGui = cGui() | |
| oRequest = cRequestHandler(URL_MAIN + link, caching=False) | |
| oRequest.addHeaderEntry('X-Requested-With', 'XMLHttpRequest') | |
| oRequest.addHeaderEntry('Referer', REFERER + '/serien') | |
| oRequest.addHeaderEntry('Origin', REFERER) | |
| #GET CONTENT OF HTML | |
| sHtmlContent = oRequest.request() | |
| if not sHtmlContent: | |
| return | |
| pattern = 'seriesCoverBox">.*?data-src="([^"]+).*?data-full-description="([^"]+)"' #img , descr | |
| aResult = cParser.parse(sHtmlContent, pattern) | |
| if not aResult[0]: | |
| return | |
| for sImg, sDescr in aResult[1]: | |
| return sImg, sDescr |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| # Always pay attention to the translations in the menu! | |
| # HTML LangzeitCache hinzugefügt | |
| # showGenre: 48 Stunden | |
| # showYears: 48 Stunden | |
| # showEpisodes: 4 Stunden | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| SITE_IDENTIFIER = 'streamcloud' | |
| SITE_NAME = 'Streamcloud' | |
| SITE_ICON = 'streamcloud.png' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| # Domain Abfrage | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'streamcloud.plus') # Domain Auswahl über die xStream Einstellungen möglich | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| URL_MAIN = 'https://' + DOMAIN + '/' | |
| # URL_MAIN = 'https://streamcloud.plus/' | |
| URL_MAINPAGE = URL_MAIN + 'streamcloud/' | |
| URL_MOVIES = URL_MAIN + 'filme-stream/' | |
| URL_KINO = URL_MAIN + 'kinofilme/' | |
| URL_FAVOURITE_MOVIE_PAGE = URL_MAIN + 'beliebte-filme/' | |
| URL_SERIES = URL_MAIN + 'serien/' | |
| URL_NEW = URL_MAIN + 'neue-filme/' | |
| URL_SEARCH = URL_MAIN + 'index.php?story=%s&do=search&subaction=search' | |
| # | |
| #ToDo Serien auch auf reinen Filmseiten, prüfen ob Filterung möglich | |
| def load(): # Menu structure of the site plugin | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| params.setParam('sUrl', URL_KINO) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30501), SITE_IDENTIFIER, 'showEntries'), params) # Current films in the cinema | |
| params.setParam('sUrl', URL_NEW) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30500), SITE_IDENTIFIER, 'showEntries'), params) # New Movies | |
| params.setParam('sUrl', URL_MAINPAGE) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30507), SITE_IDENTIFIER, 'showGenre'), params) # Categories | |
| params.setParam('sUrl', URL_MAINPAGE) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30508), SITE_IDENTIFIER, 'showYears'), params) # Year | |
| params.setParam('sUrl', URL_MAINPAGE) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30538), SITE_IDENTIFIER, 'showCountry'), params) # Country | |
| params.setParam('sUrl', URL_SERIES) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511), SITE_IDENTIFIER, 'showSeries'), params) # Series | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params) # Search | |
| cGui().setEndOfDirectory() | |
| def showGenre(entryUrl=False): | |
| params = ParameterHandler() | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 48 # 48 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = '>Genres<.*?</div></div>' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'href="([^"]+).*?>([^<]+)') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| if sUrl.startswith('/'): | |
| sUrl = URL_MAIN + sUrl | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showYears(entryUrl=False): | |
| params = ParameterHandler() | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 48 # 48 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = '>Ers.*?</div></div>' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'href=\'([^\']+).*?>([^<]+)') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| if sUrl.startswith('/'): | |
| sUrl = URL_MAIN + sUrl | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showCountry(entryUrl=False): #ToDo Sortierung A-Z bei Ländern | |
| params = ParameterHandler() | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 48 # 48 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = '">Land.*?</div></div>' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'href="([^"]+).*?>([^<]+)') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| if sUrl.startswith('/'): | |
| sUrl = URL_MAIN + sUrl | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False, sSearchText=False, sSearchPageText = False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| isTvshow = False | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False)) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # HTML Cache Zeit 6 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = 'class="thumb".*?title="([^"]+).*?href="([^"]+).*?src="([^"]+).*?_year">([^<]+)' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sName, sUrl, sThumbnail, sYear in aResult: | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| if sThumbnail[0] == '/': | |
| sThumbnail = sThumbnail[1:] | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showHosters') | |
| oGuiElement.setThumbnail(URL_MAIN + sThumbnail) | |
| oGuiElement.setMediaType('movie') | |
| #oGuiElement.setYear(sYear) #ToDo sYear erzeugt falschen Suchstring in tmdb.py (re.sub in tmdb.py) | |
| params.setParam('entryUrl', sUrl) | |
| params.setParam('sName', sName) | |
| params.setParam('sThumbnail', sThumbnail) | |
| params.setParam('sYear', sYear) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui and not sSearchText and not sSearchPageText: | |
| isMatchNextPage, sNextUrl = cParser.parseSingleResult(sHtmlContent, 'href="([^"]+)">Next') | |
| # Start Page Function | |
| isMatchSiteSearch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, 'class="wp-pagenavi(.*?)Next') | |
| if isMatchSiteSearch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, r'<span>([\d]+)</span>.*?nav_ext">.*?">([\d]+)</a>.*?href="([^"]+)') | |
| if isMatch: | |
| for sPageActive, sPageLast, sNextPage in aResult: | |
| # sPageName = '[I]Seitensuche starten >>> [/I] Seite ' + str(sPageActive) + ' von ' + str(sPageLast) + ' Seiten [I]<<<[/I]' | |
| sPageName = cConfig().getLocalizedString(30284) + str(sPageActive) + cConfig().getLocalizedString( | |
| 30285) + str(sPageLast) + cConfig().getLocalizedString(30286) | |
| params.setParam('sNextPage', sNextPage) | |
| params.setParam('sPageLast', sPageLast) | |
| oGui.searchNextPage(sPageName, SITE_IDENTIFIER, 'showSearchPage', params) | |
| # End Page Function | |
| if isMatchNextPage: | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('movies') | |
| oGui.setEndOfDirectory() | |
| def showSeries(entryUrl=False, sGui=False, sSearchText=False): # Neu eingebaut da auf der Webseite nicht erkennbar ist was Serien sind und was nicht | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| isTvshow = True | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False)) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # HTML Cache Zeit 6 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = 'class="thumb".*?title="([^"]+).*?href="([^"]+).*?src="([^"]+).*?_year">([^<]+)' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sName, sUrl, sThumbnail, sYear in aResult: | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| if sThumbnail[0] == '/': | |
| sThumbnail = sThumbnail[1:] | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showEpisodes') | |
| oGuiElement.setThumbnail(URL_MAIN + sThumbnail) | |
| oGuiElement.setMediaType('tvshow') | |
| #oGuiElement.setYear(sYear) #ToDo sYear erzeugt falschen Suchstring in tmdb.py (jahr in name) | |
| params.setParam('entryUrl', sUrl) | |
| params.setParam('sName', sName) | |
| params.setParam('sThumbnail', sThumbnail) | |
| params.setParam('sYear', sYear) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui and not sSearchText: | |
| isMatchNextPage, sNextUrl = cParser.parseSingleResult(sHtmlContent, r'"nav_ext.*?>\d[1-9]+<.*?href="([^"]+).*?</div>') | |
| if isMatchNextPage: | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showSeries', params) | |
| oGui.setView('tvshows') | |
| oGui.setEndOfDirectory() | |
| def showEpisodes(): | |
| params = ParameterHandler() | |
| entryUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue('sThumbnail') | |
| sHtmlContent = cRequestHandler(entryUrl).request() | |
| isMatch, aResult = cParser.parse(sHtmlContent, 'data-num="([^"]+)') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| total = len(aResult) | |
| for sName in aResult: | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showHosters') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| oGuiElement.setMediaType('episode') | |
| params.setParam('entryUrl', entryUrl) | |
| params.setParam('episode', sName) | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| cGui().setView('episodes') | |
| cGui().setEndOfDirectory() | |
| def showHosters(): | |
| hosters = [] | |
| sUrl = ParameterHandler().getValue('entryUrl') | |
| sHtmlContent = cRequestHandler(sUrl, caching=False).request() | |
| if ParameterHandler().exist('episode'): #kommt aus showSeries | |
| episode = ParameterHandler().getValue('episode') | |
| pattern = 'data-num="{0}".*?allowfull'.format(episode) | |
| isMatch, sHtmlContent = cParser.parseSingleResult(sHtmlContent, pattern) | |
| else: | |
| pattern = '<iframe.*?allowfull' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'src="([^"]+)') | |
| try: | |
| sUrl = aResult[0] | |
| except: | |
| pass | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| sHtmlContent = cRequestHandler(sUrl).request() | |
| isMatch, aResult = cParser.parse(sHtmlContent, 'data-link="([^"]+)') | |
| if isMatch: | |
| sQuality = '720' | |
| for sUrl in aResult: | |
| if 'youtube' in sUrl: | |
| continue | |
| elif sUrl.startswith('//'): | |
| sUrl = 'https:' + sUrl | |
| sName = cParser.urlparse(sUrl).split('.')[0].strip() | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| hoster = {'link': sUrl, 'name': sName, 'displayedName': '%s [I][%sp][/I]' % (sName, sQuality), 'quality': sQuality} | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30287)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| showEntries(URL_SEARCH % cParser.quotePlus(sSearchText), oGui, sSearchText) | |
| def showSearchPage(): # Suche für die Page Funktion | |
| params = ParameterHandler() | |
| sNextPage = params.getValue('sNextPage') # URL mit nächster Seite | |
| sPageLast = params.getValue('sPageLast') # Anzahl gefundener Seiten | |
| #sHeading = 'Bitte eine Zahl zwischen 1 und ' + str(sPageLast) + ' wählen.' | |
| sHeading = cConfig().getLocalizedString(30282) + str(sPageLast) | |
| sSearchPageText = cGui().showKeyBoard(sHeading=sHeading) | |
| if not sSearchPageText: return | |
| sNextSearchPage = sNextPage.split('page/')[0].strip() + 'page/' + sSearchPageText + '/' | |
| showEntries(sNextSearchPage) | |
| cGui().setEndOfDirectory() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| # Always pay attention to the translations in the menu! | |
| # HTML LangzeitCache hinzugefügt | |
| # showValue: 48 Stunden | |
| # showEntries: 6 Stunden | |
| # showEpisodes: 4 Stunden | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| SITE_IDENTIFIER = 'topstreamfilm' | |
| SITE_NAME = 'Topstreamfilm' | |
| SITE_ICON = 'topstreamfilm.png' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| # Domain Abfrage | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'topstreamfilm.live') # Domain Auswahl über die xStream Einstellungen möglich | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| URL_MAIN = 'https://' + DOMAIN | |
| # URL_MAIN = 'https://www.topstreamfilm.live' | |
| URL_ALL = URL_MAIN + '/filme-online-sehen/' | |
| URL_MOVIES = URL_MAIN + '/beliebte-filme-online/' | |
| URL_KINO = URL_MAIN + '/kinofilme/' | |
| URL_SERIES = URL_MAIN + '/serien/' | |
| URL_SEARCH = URL_MAIN + '/?story=%s&do=search&subaction=search' | |
| # | |
| def load(): # Menu structure of the site plugin | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| params.setParam('sUrl', URL_ALL) | |
| cGui().addFolder(cGuiElement('Alle Filme', SITE_IDENTIFIER, 'showEntries'), params) | |
| params.setParam('sUrl', URL_MOVIES) | |
| cGui().addFolder(cGuiElement('Kürzlich hinzugefügt', SITE_IDENTIFIER, 'showEntries'), params) # Neue Uploads | |
| params.setParam('sUrl', URL_KINO) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30501), SITE_IDENTIFIER, 'showEntries'), params) # Kinofilme | |
| params.setParam('Value', 'KATEGORIEN') | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30506), SITE_IDENTIFIER, 'showValue'), params) # Genre | |
| params.setParam('Value', 'LAND') | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30538), SITE_IDENTIFIER, 'showValue'), params) # Country | |
| cGui().addFolder(cGuiElement('Jahr', SITE_IDENTIFIER, 'showYearSearch')) # New Year entry | |
| params.setParam('sUrl', URL_SERIES) | |
| cGui().addFolder(cGuiElement('Serien', SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params) # Search | |
| cGui().setEndOfDirectory() | |
| def showValue(): | |
| params = ParameterHandler() | |
| oRequest = cRequestHandler(URL_MAIN) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 48 # 48 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = '>{0}</a>(.*?)</ul>'.format(params.getValue('Value')) | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if not isMatch: | |
| pattern = '>{0}</(.*?)</ul>'.format(params.getValue('Value')) | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'href="([^"]+).*?>([^<]+)') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| for sUrl, sName in aResult: | |
| if sUrl.startswith('/'): | |
| sUrl = URL_MAIN + sUrl | |
| params.setParam('sUrl', sUrl) | |
| cGui().addFolder(cGuiElement(sName, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False, sSearchText=False, sSearchPageText = False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| isTvshow = False | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| oRequest = cRequestHandler(entryUrl, ignoreErrors=(sGui is not False)) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # 6 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = 'TPostMv">.*?href="([^"]+).*?data-src="([^"]+).*?Title">([^<]+)(.*?)</li>' | |
| isMatch, aResult = cParser.parse(sHtmlContent, pattern) | |
| if not isMatch: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = len(aResult) | |
| for sUrl, sThumbnail, sName, sDummy in aResult: | |
| if sName: | |
| sName = sName.split('- Der Film')[0].strip() # Name nach dem - abschneiden und Array [0] nutzen | |
| if sSearchText and not cParser.search(sSearchText, sName): | |
| continue | |
| isYear, sYear = cParser.parseSingleResult(sDummy, r'Year">([\d]+)</span>') # Release Jahr | |
| isDuration, sDuration = cParser.parseSingleResult(sDummy, r'time">([\d]+)') # Laufzeit | |
| if int(sDuration) <= int('70'): # Wenn Laufzeit kleiner oder gleich 70min, dann ist es eine Serie. | |
| isTvshow = True | |
| else: | |
| from resources.lib.tmdb import cTMDB | |
| oMetaget = cTMDB() | |
| if not oMetaget: | |
| isTvshow = False | |
| else: | |
| if isYear: | |
| meta = oMetaget.search_movie_name(sName, year=sYear) | |
| else: | |
| meta = oMetaget.search_movie_name(sName) | |
| if meta and 'id' in meta: | |
| isTvshow = False | |
| else: | |
| isTvshow = True | |
| if 'South Park: The End Of Obesity' in sName: | |
| isTvshow = False | |
| isQuality, sQuality = cParser.parseSingleResult(sDummy, 'Qlty">([^<]+)</span>') # Qualität | |
| isDesc, sDesc = cParser.parseSingleResult(sDummy, 'Description"><p>([^<]+)') # Beschreibung | |
| sThumbnail = URL_MAIN + sThumbnail | |
| oGuiElement = cGuiElement(sName, SITE_IDENTIFIER, 'showSeasons' if isTvshow else 'showHosters') | |
| if isYear: | |
| oGuiElement.setYear(sYear) | |
| if isDuration: | |
| oGuiElement.addItemValue('duration', sDuration) | |
| if isQuality: | |
| oGuiElement.setQuality(sQuality) | |
| if isDesc: | |
| oGuiElement.setDescription(sDesc) | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| params.setParam('entryUrl', sUrl) | |
| params.setParam('sThumbnail', sThumbnail) | |
| params.setParam('sDesc', sDesc) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui and not sSearchText and not sSearchPageText: | |
| isMatchNextPage, sNextUrl = cParser.parseSingleResult(sHtmlContent, 'href="([^"]+)">Next') | |
| # Start Page Function | |
| isMatchSiteSearch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, 'class="wp-pagenavi">(.*?)Next') | |
| if isMatchSiteSearch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, r'<span>([\d]+)</span>.*?nav_ext">.*?">([\d]+)</a>.*?href="([^"]+)') | |
| for sPageActive, sPageLast, sNextPage in aResult: | |
| #sPageName = '[I]Seitensuche starten >>> [/I] Seite ' + str(sPageActive) + ' von ' + str(sPageLast) + ' Seiten [I]<<<[/I]' | |
| sPageName = cConfig().getLocalizedString(30284) + str(sPageActive) + cConfig().getLocalizedString(30285) + str(sPageLast) + cConfig().getLocalizedString(30286) | |
| params.setParam('sNextPage', sNextPage) | |
| params.setParam('sPageLast', sPageLast) | |
| oGui.searchNextPage(sPageName, SITE_IDENTIFIER, 'showSearchPage', params) | |
| # End Page Function | |
| if isMatchNextPage: | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('tvshows' if isTvshow else 'movies') | |
| oGui.setEndOfDirectory() | |
| def showSeasons(): | |
| params = ParameterHandler() | |
| # Parameter laden | |
| sUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue('sThumbnail') | |
| isDesc = params.getValue('sDesc') | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # HTML Cache Zeit 6 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = '<div class="tt_season">(.*)</ul>' | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, r'"#season-(\d+)') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| total = len(aResult) | |
| for sSeason in aResult: | |
| oGuiElement = cGuiElement('Staffel ' + str(sSeason), SITE_IDENTIFIER, 'showEpisodes') | |
| oGuiElement.setSeason(sSeason) | |
| oGuiElement.setMediaType('season') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if isDesc: | |
| oGuiElement.setDescription(isDesc) | |
| cGui().addFolder(oGuiElement, params, True, total) | |
| cGui().setView('seasons') | |
| cGui().setEndOfDirectory() | |
| def showEpisodes(): | |
| params = ParameterHandler() | |
| # Parameter laden | |
| entryUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue('sThumbnail') | |
| sSeason = params.getValue('season') | |
| isDesc = params.getValue('sDesc') | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 4 # HTML Cache Zeit 4 Stunden | |
| sHtmlContent = oRequest.request() | |
| pattern = 'id="season-%s(.*?)</ul>' % sSeason | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlContainer, r'data-title="Episode\s(\d+)') | |
| if not isMatch: | |
| cGui().showInfo() | |
| return | |
| total = len(aResult) | |
| for sEpisode in aResult: | |
| oGuiElement = cGuiElement('Episode ' + str(sEpisode), SITE_IDENTIFIER, 'showEpisodeHosters') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if isDesc: | |
| oGuiElement.setDescription(isDesc) | |
| oGuiElement.setMediaType('episode') | |
| params.setParam('entryUrl', entryUrl) | |
| params.setParam('season', sSeason) | |
| params.setParam('episode', sEpisode) | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| cGui().setView('episodes') | |
| cGui().setEndOfDirectory() | |
| def showEpisodeHosters(): | |
| hosters = [] | |
| params = ParameterHandler() | |
| # Parameter laden | |
| sUrl = params.getValue('entryUrl') | |
| sSeason = params.getValue('season') | |
| sEpisode = params.getValue('episode') | |
| sHtmlContent = cRequestHandler(sUrl, caching=False).request() | |
| pattern = 'id="season-%s">(.*?)</ul>' % sSeason | |
| isMatch, sHtmlContainer = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| pattern = '>%s</a>(.*?)</li>' % sEpisode | |
| isMatch, sHtmlLink = cParser.parseSingleResult(sHtmlContainer, pattern) | |
| if isMatch: | |
| isMatch, aResult = cParser.parse(sHtmlLink, 'data-link="([^"]+)') | |
| if isMatch: | |
| sQuality = '720' | |
| for sUrl in aResult: | |
| if 'youtube' in sUrl: | |
| continue | |
| elif sUrl.startswith('//'): | |
| sUrl = 'https:' + sUrl | |
| sName = cParser.urlparse(sUrl).split('.')[0].strip() | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| hoster = {'link': sUrl, 'name': sName, 'displayedName': '%s [I][%sp][/I]' % (sName, sQuality), 'quality': sQuality} | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def showHosters(): | |
| hosters = [] | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sHtmlContent = cRequestHandler(sUrl, caching=False).request() | |
| pattern = '<iframe.*?src="([^"]+)' | |
| isMatch, hUrl = cParser.parseSingleResult(sHtmlContent, pattern) | |
| if isMatch: | |
| sHtmlContainer = cRequestHandler(hUrl).request() | |
| isMatch, aResult = cParser.parse(sHtmlContainer, 'data-link="([^"]+)') | |
| if isMatch: | |
| sQuality = '720' | |
| for sUrl in aResult: | |
| if 'youtube' in sUrl: | |
| continue | |
| elif sUrl.startswith('//'): | |
| sUrl = 'https:' + sUrl | |
| sName = cParser.urlparse(sUrl).split('.')[0].strip() | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| hoster = {'link': sUrl, 'name': sName, 'displayedName': '%s [I][%sp][/I]' % (sName, sQuality), 'quality': sQuality} | |
| hosters.append(hoster) | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| showEntries(URL_SEARCH % cParser.quotePlus(sSearchText), oGui, sSearchText) | |
| def showSearchPage(): # Suche für die Page Funktion | |
| params = ParameterHandler() | |
| sNextPage = params.getValue('sNextPage') # URL mit nächster Seite | |
| sPageLast = params.getValue('sPageLast') # Anzahl gefundener Seiten | |
| #sHeading = 'Bitte eine Zahl zwischen 1 und ' + str(sPageLast) + ' wählen.' | |
| sHeading = cConfig().getLocalizedString(30282) + str(sPageLast) | |
| sSearchPageText = cGui().showKeyBoard(sHeading=sHeading) | |
| if not sSearchPageText: return | |
| sNextSearchPage = sNextPage.split('page/')[0].strip() + 'page/' + sSearchPageText + '/' | |
| showEntries(sNextSearchPage) | |
| cGui().setEndOfDirectory() | |
| def showYearSearch(): | |
| sYear = cGui().showKeyBoard(sHeading="Jahr eintragen (z.B., 2017)") | |
| if not sYear: return | |
| searchUrl = URL_MAIN + '/xfsearch/' + sYear | |
| showEntries(searchUrl) | |
| cGui().setEndOfDirectory() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Always pay attention to the translations in the menu! | |
| # HTML LangzeitCache hinzugefügt | |
| # showGenre: 48 Stunden | |
| # showEntries: 6 Stunden | |
| # showEpisodes: 4 Stunden | |
| import re | |
| import xbmcgui | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.tools import logger, cParser, cUtil | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.config import cConfig | |
| from resources.lib.gui.gui import cGui | |
| from json import loads | |
| from datetime import datetime | |
| # Globale Variable für die JSON-Daten | |
| apiJson = None | |
| # Domain Abfrage ### | |
| SITE_IDENTIFIER = 'xcine' | |
| SITE_NAME = 'xCine' | |
| SITE_ICON = 'xcinetop.png' | |
| DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'xcine.io') | |
| STATUS = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '_status') # Status Code Abfrage der Domain | |
| ACTIVE = cConfig().getSetting('plugin_' + SITE_IDENTIFIER) # Ob Plugin aktiviert ist oder nicht | |
| ORIGIN = 'https://' + DOMAIN + '/' | |
| REFERER = ORIGIN + '/' | |
| URL_API = 'https://' + DOMAIN | |
| URL_MAIN = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&page=%s' | |
| URL_SEARCH = URL_API + '/data/browse/?lang=%s&order_by=%s&page=%s&limit=0' | |
| URL_THUMBNAIL = 'https://image.tmdb.org/t/p/w300%s' | |
| URL_WATCH = URL_API + '/data/watch/?_id=%s' | |
| URL_GENRE = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&genre=%s&page=%s' | |
| URL_CAST = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&cast=%s&page=%s' | |
| URL_YEAR = URL_API + '/data/browse/?lang=%s&type=%s&order_by=%s&year=%s&page=%s' | |
| # Global search function is thus deactivated! | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'false': | |
| SITE_GLOBAL_SEARCH = False | |
| logger.info('-> [SitePlugin]: globalSearch for %s is deactivated.' % SITE_NAME) | |
| def load(): | |
| logger.info('Load %s' % SITE_NAME) | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| # Änderung des Sprachcodes nach voreigestellter Sprache | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| elif sLanguage == '3': # prefLang Japanisch | |
| sLang = cGui().showLanguage() | |
| return | |
| params.setParam('sLanguage', sLang) | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30502), SITE_IDENTIFIER, 'showMovieMenu'), params) # Movies | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30551), SITE_IDENTIFIER, 'showGenreMMenu'), params) # Movies Genre | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30511), SITE_IDENTIFIER, 'showSeriesMenu'), params) # Series | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30529), SITE_IDENTIFIER, 'showGenreSMenu'), params) # Series Genre | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30508), SITE_IDENTIFIER, 'showYearsMenu'), params) # Years | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30553), SITE_IDENTIFIER, 'showSearchActor'), params) # Cast | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30520), SITE_IDENTIFIER, 'showSearch'), params) # Search | |
| cGui().setEndOfDirectory() | |
| def _cleanTitle(sTitle): | |
| sTitle = re.sub("[\xE4]", 'ae', sTitle) | |
| sTitle = re.sub("[\xFC]", 'ue', sTitle) | |
| sTitle = re.sub("[\xF6]", 'oe', sTitle) | |
| sTitle = re.sub("[\xC4]", 'Ae', sTitle) | |
| sTitle = re.sub("[\xDC]", 'Ue', sTitle) | |
| sTitle = re.sub("[\xD6]", 'Oe', sTitle) | |
| sTitle = re.sub("[\x00-\x1F\x80-\xFF]", '', sTitle) | |
| return sTitle | |
| def _getQuality(sQuality): | |
| isMatch, aResult = cParser.parse(sQuality, '(HDCAM|HD|WEB|BLUERAY|BRRIP|DVD|TS|SD|CAM)', 1, True) | |
| if isMatch: | |
| return aResult[0] | |
| else: | |
| return sQuality | |
| def _showGenreMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| sType = params.getValue('sType') | |
| sMenu = params.getValue('sMenu') | |
| if sLanguage == '2' or sLanguage == 'all': | |
| genres = { | |
| 'Action': 'Action', | |
| 'Abenteuer': 'Abenteuer', | |
| 'Animation': 'Animation', | |
| 'Biographie': 'Biographie', | |
| 'Dokumentation': 'Dokumentation', | |
| 'Drama': 'Drama', | |
| 'Familie': 'Familie', | |
| 'Fantasy': 'Fantasy', | |
| 'Geschichte': 'Geschichte', | |
| 'Horror': 'Horror', | |
| 'Komödie': 'Komödie', | |
| 'Krieg': 'Krieg', | |
| 'Krimi': 'Krimi', | |
| 'Musik': 'Musik', | |
| 'Mystery': 'Mystery', | |
| 'Romantik': 'Romantik', | |
| 'Reality-TV': 'Reality-TV', | |
| 'Sci-Fi': 'Sci-Fi', | |
| 'Sports': 'Sport', | |
| 'Thriller': 'Thriller', | |
| 'Western': 'Western' | |
| } | |
| else: | |
| genres = { | |
| 'Action': 'Action', | |
| 'Adventure': 'Abenteuer', | |
| 'Animation': 'Animation', | |
| 'Biography': 'Biographie', | |
| 'Comedy': 'Komödie', | |
| 'Crime': 'Krimi', | |
| 'Documentation': 'Dokumentation', | |
| 'Drama': 'Drama', | |
| 'Family': 'Familie', | |
| 'Fantasy': 'Fantasy', | |
| 'History': 'Geschichte', | |
| 'Horror': 'Horror', | |
| 'Music': 'Musik', | |
| 'Mystery': 'Mystery', | |
| 'Romance': 'Romantik', | |
| 'Reality-TV': 'Reality-TV', | |
| 'Sci-Fi': 'Sci-Fi', | |
| 'Sports': 'Sport', | |
| 'Thriller': 'Thriller', | |
| 'War': 'Krieg', | |
| 'Western': 'Western' | |
| } | |
| for genre, searchGenre in genres.items(): | |
| params.setParam('sUrl', URL_GENRE % (sLanguage, sType, sMenu, searchGenre, '1')) | |
| cGui().addFolder(cGuiElement(genre, SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showMovieMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'Trending', '1')) ### Trending Filme trending1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30521), SITE_IDENTIFIER, 'showEntries'), params) ### Trending Filme trending1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'new', '1')) ### neue filme neu1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30541), SITE_IDENTIFIER, 'showEntries'), params) ### neue filme neu1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'views', '1')) ### Views filme views1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30552), SITE_IDENTIFIER, 'showEntries'), params) ### Views filme views1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'rating', '1')) ### Rating Filme rating1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30510), SITE_IDENTIFIER, 'showEntries'), params) ### Rating Filme rating1 | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'votes', '1')) ### votes filme votes 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30536), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'updates', '1')) ### updates filme updates 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30533), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'name', '1')) ### name filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'featured', '1')) ### featured filme features1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30530), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'requested', '1')) ### requested filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30534), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'movies', 'releases', '1')) ### releases filme | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30531), SITE_IDENTIFIER, 'showEntries'), params) ### Filme releases 1 | |
| cGui().setEndOfDirectory() | |
| def showGenreMMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sType', 'movies') | |
| params.setParam('sMenu', 'Trending') | |
| cGui().addFolder(cGuiElement('Genre trending', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Neu') | |
| cGui().addFolder(cGuiElement('Genre new', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Views') | |
| cGui().addFolder(cGuiElement('Genre viewed', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Votes') | |
| cGui().addFolder(cGuiElement('Genre voted', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Updates') | |
| cGui().addFolder(cGuiElement('Genre updated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Rating') | |
| cGui().addFolder(cGuiElement('Genre rated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Name') | |
| cGui().addFolder(cGuiElement('Genre named', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'requested') | |
| cGui().addFolder(cGuiElement('Genre requested', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'featured') | |
| cGui().addFolder(cGuiElement('Genre featured', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'releases') | |
| cGui().addFolder(cGuiElement('Genre released', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| cGui().setEndOfDirectory() | |
| # Serienmenue | |
| def showSeriesMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'neu', '1')) ### serien neu 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30514), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'views', '1')) ### serien views 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30537), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'votes', '1')) ### serien votes 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30519), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'updates', '1')) ### serien updates 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30533), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'name', '1')) ### serien name 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30517), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'featured', '1')) ### | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30530), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'requested', '1')) ### serien requested | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30534), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'releases', '1')) ### serien releases 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30531), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'rating', '1')) ### serien rating 1 | |
| cGui().addFolder(cGuiElement(cConfig().getLocalizedString(30535), SITE_IDENTIFIER, 'showEntries'), params) ### | |
| #params.setParam('sUrl', URL_MAIN % (sLanguage, 'tvseries', 'Jahr', '1')) # ## | |
| #cGui().addFolder(cGuiElement('Jahr', SITE_IDENTIFIER, 'showEntries'), params) ## | |
| #params.setParam('sCont', 'Jahr') # | |
| #cGui().addFolder(cGuiElement('Jahr', SITE_IDENTIFIER, 'showValue'), params), params) # | |
| cGui().setEndOfDirectory() | |
| # show genre serien menue | |
| def showGenreSMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| params.setParam('sType', 'tvseries') | |
| params.setParam('sMenu', 'Trending') | |
| cGui().addFolder(cGuiElement('Series genre trending', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Neu') | |
| cGui().addFolder(cGuiElement('Series genre new', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Views') | |
| cGui().addFolder(cGuiElement('Series genre viewed', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Votes') | |
| cGui().addFolder(cGuiElement('Series genre voted', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Updates') | |
| cGui().addFolder(cGuiElement('Series genre updated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Rating') | |
| cGui().addFolder(cGuiElement('Series genre rated', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'Name') | |
| cGui().addFolder(cGuiElement('Series genre named', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'requested') | |
| cGui().addFolder(cGuiElement('Series genre requested', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'featured') | |
| cGui().addFolder(cGuiElement('Series genre featured', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| params.setParam('sMenu', 'releases') | |
| cGui().addFolder(cGuiElement('Series genre releases', SITE_IDENTIFIER, '_showGenreMenu'), params) | |
| cGui().setEndOfDirectory() | |
| def showYearsMenu(): | |
| params = ParameterHandler() | |
| sLanguage = params.getValue('sLanguage') | |
| # Anfangs- und Endjahr für das menü eintragen | |
| start_jahr = 1931 | |
| end_jahr = datetime.now().year | |
| # show the current year first | |
| for jahr in range(end_jahr, start_jahr - 1, -1): | |
| params.setParam('sUrl', URL_YEAR % (sLanguage, 'movies', 'new', str(jahr), '1')) | |
| cGui().addFolder(cGuiElement(str(jahr), SITE_IDENTIFIER, 'showEntries'), params) | |
| cGui().setEndOfDirectory() | |
| def showEntries(entryUrl=False, sGui=False, sSearchText=False): | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| isTvshow = False | |
| sThumbnail = '' | |
| sLanguage = params.getValue('sLanguage') | |
| if not entryUrl: entryUrl = params.getValue('sUrl') | |
| try: | |
| oRequest = cRequestHandler(entryUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 6 # HTML Cache Zeit 6 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| aJson = loads(sJson) | |
| except: | |
| if not sGui: oGui.showInfo() | |
| return | |
| if 'movies' not in aJson or not isinstance(aJson.get('movies'), list) or len(aJson['movies']) == 0: | |
| if not sGui: oGui.showInfo() | |
| return | |
| total = 0 | |
| # ignore movies which does not contain any streams | |
| for movie in aJson['movies']: | |
| if '_id' in movie: | |
| total += 1 | |
| for movie in aJson['movies']: | |
| if not '_id' in movie: | |
| continue | |
| sTitle = str(movie['title']) | |
| if sSearchText and not cParser.search(sSearchText, sTitle): | |
| continue | |
| if 'Staffel' in sTitle or 'Season' in sTitle: | |
| isTvshow = True | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'showEpisodes' if isTvshow else 'showHosters') | |
| if 'poster_path_season' in movie and movie['poster_path_season']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path_season']) | |
| elif 'poster_path' in movie and movie['poster_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path']) | |
| elif 'backdrop_path' in movie and movie['backdrop_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['backdrop_path']) | |
| if sThumbnail: | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 'storyline' in movie: | |
| oGuiElement.setDescription(str(movie['storyline'])) | |
| elif 'overview' in movie: | |
| oGuiElement.setDescription(str(movie['overview'])) | |
| if 'year' in movie and len(str(movie['year'])) == 4: | |
| oGuiElement.setYear(movie['year']) | |
| if 'quality' in movie: | |
| oGuiElement.setQuality(_getQuality(movie['quality'])) | |
| if 'rating' in movie: | |
| oGuiElement.addItemValue('rating', movie['rating']) | |
| if 'lang' in movie: | |
| if (sLanguage != '1' and movie['lang'] == 2): # Deutsch | |
| oGuiElement.setLanguage('DE') | |
| if (sLanguage != '2' and movie['lang'] == 3): # Englisch | |
| oGuiElement.setLanguage('EN') | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| if 'runtime' in movie: | |
| isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], '\d+') | |
| if isMatch: | |
| oGuiElement.addItemValue('duration', sRuntime) | |
| params.setParam('entryUrl', URL_WATCH % str(movie['_id'])) | |
| params.setParam('sName', sTitle) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui and not sSearchText: | |
| curPage = aJson['pager']['currentPage'] | |
| if curPage < aJson['pager']['totalPages']: | |
| sNextUrl = entryUrl.replace('page=' + str(curPage), 'page=' + str(curPage + 1)) | |
| params.setParam('sUrl', sNextUrl) | |
| oGui.addNextPage(SITE_IDENTIFIER, 'showEntries', params) | |
| oGui.setView('tvshows' if isTvshow else 'movies') | |
| oGui.setEndOfDirectory() | |
| def showEpisodes(): | |
| aEpisodes = [] | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sThumbnail = params.getValue("sThumbnail") | |
| try: | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 4 # HTML Cache Zeit 4 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| aJson = loads(sJson) | |
| except: | |
| cGui().showInfo() | |
| return | |
| if 'streams' not in aJson or len(aJson['streams']) == 0: | |
| cGui().showInfo() | |
| return | |
| for stream in aJson['streams']: | |
| if 'e' in stream: | |
| aEpisodes.append(int(stream['e'])) | |
| if aEpisodes: | |
| aEpisodesSorted = set(aEpisodes) | |
| total = len(aEpisodesSorted) | |
| for sEpisode in aEpisodesSorted: | |
| oGuiElement = cGuiElement('Episode ' + str(sEpisode), SITE_IDENTIFIER, 'showHosters') | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 's' in aJson: | |
| oGuiElement.setSeason(aJson['s']) | |
| oGuiElement.setTVShowTitle('Episode ' + str(sEpisode)) | |
| oGuiElement.setEpisode(sEpisode) | |
| oGuiElement.setMediaType('episode') | |
| cGui().addFolder(oGuiElement, params, False, total) | |
| cGui().setView('episodes') | |
| cGui().setEndOfDirectory() | |
| def showHosters(): | |
| hosters = [] | |
| params = ParameterHandler() | |
| sUrl = params.getValue('entryUrl') | |
| sEpisode = params.getValue('episode') | |
| try: | |
| oRequest = cRequestHandler(sUrl) | |
| if cConfig().getSetting('global_search_' + SITE_IDENTIFIER) == 'true': | |
| oRequest.cacheTime = 60 * 60 * 8 # HTML Cache Zeit 8 Stunden | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| sJson = oRequest.request() | |
| except: | |
| return hosters | |
| if sJson: | |
| aJson = loads(sJson) | |
| if 'streams' in aJson: | |
| i = 0 | |
| for stream in aJson['streams']: | |
| if (('e' not in stream) or (str(sEpisode) == str(stream['e']))): | |
| sHoster = str(i) + ':' | |
| isMatch, aName = cParser.parse(stream['stream'], '//([^/]+)/') | |
| if isMatch: | |
| # sName = cParser.urlparse(sUrl) ### angezeigter hostername api | |
| sName = aName[0][:aName[0].rindex('.')] | |
| if cConfig().isBlockedHoster(sName)[0]: continue # Hoster aus settings.xml oder deaktivierten Resolver ausschließen | |
| sHoster = sHoster + ' ' + sName | |
| if 'release' in stream and str(stream['release']) != '': | |
| sHoster = sHoster + ' [I][' + _getQuality(stream['release']) + '][/I]' | |
| hoster = {'link': stream['stream'], 'name': sHoster} | |
| hosters.append(hoster) | |
| i += 1 | |
| if hosters: | |
| hosters.append('getHosterUrl') | |
| return hosters | |
| def getHosterUrl(sUrl=False): | |
| return [{'streamUrl': sUrl, 'resolved': False}] | |
| def showSearchActor(): | |
| sName = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30280)) | |
| if not sName: return | |
| _searchActor(False, sName) | |
| cGui().setEndOfDirectory() | |
| def _searchActor(oGui, sName): | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| showEntries(URL_CAST % (sLanguage, 'movies', 'new', cParser.quotePlus(sName), '1'), oGui) | |
| def showSearch(): | |
| sSearchText = cGui().showKeyBoard(sHeading=cConfig().getLocalizedString(30281)) | |
| if not sSearchText: return | |
| _search(False, sSearchText) | |
| cGui().setEndOfDirectory() | |
| def _search(oGui, sSearchText): | |
| SSsearch(oGui, sSearchText) | |
| def SSsearch(sGui=False, sSearchText=False): | |
| global apiJson | |
| oGui = sGui if sGui else cGui() | |
| params = ParameterHandler() | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| # Falls die Daten noch nicht geladen wurden oder neu geladen werden sollen | |
| if apiJson is None or 'movies' not in apiJson: | |
| loadMoviesData() | |
| if 'movies' not in apiJson or not isinstance(apiJson.get('movies'), list) or len(apiJson['movies']) == 0: | |
| oGui.showInfo() | |
| return | |
| sst = sSearchText.lower() | |
| if not sGui: | |
| dialog = xbmcgui.DialogProgress() | |
| dialog.create(cConfig().getLocalizedString(30122), cConfig().getLocalizedString(30123)) | |
| total = len(apiJson['movies']) | |
| position = 0 | |
| for movie in apiJson['movies']: | |
| position += 1 | |
| if not '_id' in movie: | |
| continue | |
| if not sGui and position % 128 == 0: # Update progress every 128 items | |
| if dialog.iscanceled(): break | |
| dialog.update(position, str(position) + cConfig().getLocalizedString(30128) + str(total)) | |
| sTitle = movie['title'] | |
| if 'Staffel' in sTitle or 'Season' in sTitle: | |
| isTvshow = True | |
| sSearch = sTitle.rsplit('-', 1)[0].replace(' ', '').lower() | |
| else: | |
| isTvshow = False | |
| sSearch = sTitle.lower() | |
| if not sst in sSearch and not cUtil.isSimilarByToken(sst, sSearch): | |
| continue | |
| #logger.info('-> [DEBUG]: %s' % str(movie)) | |
| oGuiElement = cGuiElement(sTitle, SITE_IDENTIFIER, 'showEpisodes' if isTvshow else 'showHosters') | |
| sThumbnail = '' | |
| if 'poster_path_season' in movie and movie['poster_path_season']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path_season']) | |
| elif 'poster_path' in movie and movie['poster_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['poster_path']) | |
| elif 'backdrop_path' in movie and movie['backdrop_path']: | |
| sThumbnail = URL_THUMBNAIL % str(movie['backdrop_path']) | |
| if sThumbnail: | |
| oGuiElement.setThumbnail(sThumbnail) | |
| if 'storyline' in movie: | |
| oGuiElement.setDescription(str(movie['storyline'])) | |
| elif 'overview' in movie: | |
| oGuiElement.setDescription(str(movie['overview'])) | |
| if 'year' in movie and len(str(movie['year'])) == 4: | |
| oGuiElement.setYear(movie['year']) | |
| if 'quality' in movie: | |
| oGuiElement.setQuality(_getQuality(movie['quality'])) | |
| if 'rating' in movie: | |
| oGuiElement.addItemValue('rating', movie['rating']) | |
| if 'lang' in movie: | |
| if (sLanguage != '1' and movie['lang'] == 2): # Deutsch | |
| oGuiElement.setLanguage('DE') | |
| if (sLanguage != '2' and movie['lang'] == 3): # Englisch | |
| oGuiElement.setLanguage('EN') | |
| oGuiElement.setMediaType('tvshow' if isTvshow else 'movie') | |
| if 'runtime' in movie: | |
| isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], '\d+') | |
| if isMatch: | |
| oGuiElement.addItemValue('duration', sRuntime) | |
| params.setParam('entryUrl', URL_WATCH % str(movie['_id'])) | |
| params.setParam('sName', sTitle) | |
| params.setParam('sThumbnail', sThumbnail) | |
| oGui.addFolder(oGuiElement, params, isTvshow, total) | |
| if not sGui: | |
| dialog.close() | |
| def loadMoviesData(): | |
| global apiJson | |
| sLanguage = cConfig().getSetting('prefLanguage') | |
| if sLanguage == '0': # prefLang Alle Sprachen | |
| sLang = 'all' | |
| if sLanguage == '1': # prefLang Deutsch | |
| sLang = '2' | |
| if sLanguage == '2': # prefLang Englisch | |
| sLang = '3' | |
| try: | |
| oRequest = cRequestHandler(URL_SEARCH % (sLang, 'new', '1'), caching=True) | |
| oRequest.addHeaderEntry('Referer', REFERER) | |
| oRequest.addHeaderEntry('Origin', ORIGIN) | |
| oRequest.cacheTime = 60 * 60 * 48 # HTML Cache Zeit 2 Tage | |
| sJson = oRequest.request() | |
| apiJson = loads(sJson) | |
| logger.info('API-Daten erfolgreich geladen') | |
| except: | |
| logger.error('Fehler beim Laden der API-Daten') | |
| apiJson = {'movies': []} | |
| # Daten werden lazy beim ersten Zugriff geladen (siehe SSsearch) | |
| # loadMoviesData() - entfernt: beschleunigt den Import/Start erheblich |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # -*- coding: utf-8 -*- | |
| # Python 3 | |
| import sys | |
| import xbmc | |
| import xbmcgui | |
| import os | |
| import time | |
| import concurrent.futures | |
| from resources.lib.handler.ParameterHandler import ParameterHandler | |
| from resources.lib.handler.requestHandler import cRequestHandler | |
| from resources.lib.handler.pluginHandler import cPluginHandler | |
| from xbmc import LOGINFO as LOGNOTICE, LOGERROR, log | |
| from resources.lib.gui.guiElement import cGuiElement | |
| from resources.lib.gui.gui import cGui | |
| from resources.lib.config import cConfig | |
| from resources.lib.tools import logger, cParser, cCache | |
| try: | |
| import resolveurl as resolver | |
| except ImportError: | |
| # Resolver Fehlermeldung (bei defekten oder nicht installierten Resolver) | |
| xbmcgui.Dialog().ok(cConfig().getLocalizedString(30119), cConfig().getLocalizedString(30120)) | |
| def viewInfo(params): | |
| from resources.lib.tmdbinfo import WindowsBoxes | |
| parms = ParameterHandler() | |
| sCleanTitle = params.getValue('searchTitle') | |
| sMeta = parms.getValue('sMeta') | |
| sYear = parms.getValue('sYear') | |
| WindowsBoxes(sCleanTitle, sCleanTitle, sMeta, sYear) | |
| def parseUrl(): | |
| if xbmc.getInfoLabel('Container.PluginName') == 'plugin.video.osmosis': | |
| sys.exit() | |
| params = ParameterHandler() | |
| logger.info(params.getAllParameters()) | |
| # If no function is set, we set it to the default "load" function | |
| if params.exist('function'): | |
| sFunction = params.getValue('function') | |
| if sFunction == 'spacer': | |
| return True | |
| elif sFunction == 'clearCache': | |
| cRequestHandler('dummy').clearCache() | |
| return | |
| elif sFunction == 'viewInfo': | |
| viewInfo(params) | |
| return | |
| elif sFunction == 'searchAlter': | |
| searchAlter(params) | |
| return | |
| elif sFunction == 'searchTMDB': | |
| searchTMDB(params) | |
| return | |
| elif sFunction == 'devUpdates': | |
| from resources.lib import updateManager | |
| updateManager.devUpdates() | |
| return | |
| elif sFunction == 'pluginInfo': | |
| cPluginHandler().pluginInfo() | |
| return | |
| elif sFunction == 'changelog': | |
| from resources.lib import tools | |
| cConfig().setSetting('changelog_version', '') | |
| tools.changelog() | |
| return | |
| elif sFunction == 'devWarning': | |
| from resources.lib import tools | |
| tools.devWarning() | |
| return | |
| elif params.exist('remoteplayurl'): | |
| try: | |
| remotePlayUrl = params.getValue('remoteplayurl') | |
| sLink = resolver.resolve(remotePlayUrl) | |
| if sLink: | |
| xbmc.executebuiltin('PlayMedia(' + sLink + ')') | |
| else: | |
| log(cConfig().getLocalizedString(30166) + ' -> [xstream]: Could not play remote url %s ' % sLink, LOGNOTICE) | |
| except resolver.resolver.ResolverError as e: | |
| log(cConfig().getLocalizedString(30166) + ' -> [xstream]: ResolverError: %s' % e, LOGERROR) | |
| return | |
| else: | |
| sFunction = 'load' | |
| # Test if we should run a function on a special site | |
| if not params.exist('site'): | |
| # As a default if no site was specified, we run the default starting gui with all plugins | |
| showMainMenu(sFunction) | |
| return | |
| sSiteName = params.getValue('site') | |
| if params.exist('playMode'): | |
| from resources.lib.gui.hoster import cHosterGui | |
| url = False | |
| playMode = params.getValue('playMode') | |
| isHoster = params.getValue('isHoster') | |
| url = params.getValue('url') | |
| manual = params.exist('manual') | |
| if cConfig().getSetting('hosterSelect') == 'Auto' and playMode != 'jd' and playMode != 'jd2' and playMode != 'pyload' and not manual: | |
| cHosterGui().streamAuto(playMode, sSiteName, sFunction) | |
| else: | |
| cHosterGui().stream(playMode, sSiteName, sFunction, url) | |
| return | |
| log(cConfig().getLocalizedString(30166) + " -> [xstream]: Call function '%s' from '%s'" % (sFunction, sSiteName), LOGNOTICE) | |
| # If the hoster gui is called, run the function on it and return | |
| if sSiteName == 'cHosterGui': | |
| showHosterGui(sFunction) | |
| # If global search is called | |
| elif sSiteName == 'globalSearch': | |
| searchterm = False | |
| if params.exist('searchterm'): | |
| searchterm = params.getValue('searchterm') | |
| searchGlobal(searchterm) | |
| elif sSiteName == 'xStream': | |
| oGui = cGui() | |
| oGui.openSettings() | |
| # resolves strange errors in the logfile | |
| #oGui.updateDirectory() | |
| oGui.setEndOfDirectory() | |
| xbmc.executebuiltin('Action(ParentDir)') | |
| # Resolver Einstellungen im Hauptmenü | |
| elif sSiteName == 'resolver': | |
| oGui = cGui() | |
| resolver.display_settings() | |
| # resolves strange errors in the logfile | |
| oGui.setEndOfDirectory() | |
| xbmc.executebuiltin('Action(ParentDir)') | |
| # Manuelles Update im Hauptmenü | |
| elif sSiteName == 'devUpdates': | |
| from resources.lib import updateManager | |
| updateManager.devUpdates() | |
| # Plugin Infos | |
| elif sSiteName == 'pluginInfo': | |
| cPluginHandler().pluginInfo() | |
| # Changelog anzeigen | |
| elif sSiteName == 'changelog': | |
| from resources.lib import tools | |
| tools.changelog() | |
| # Dev Warnung anzeigen | |
| elif sSiteName == 'devWarning': | |
| from resources.lib import tools | |
| tools.devWarning() | |
| # Unterordner der Einstellungen | |
| elif sSiteName == 'settings': | |
| oGui = cGui() | |
| for folder in settingsGuiElements(): | |
| oGui.addFolder(folder) | |
| oGui.setEndOfDirectory() | |
| else: | |
| # Else load any other site as plugin and run the function | |
| plugin = __import__(sSiteName, globals(), locals()) | |
| function = getattr(plugin, sFunction) | |
| function() | |
| def showMainMenu(sFunction): | |
| ART = os.path.join(cConfig().getAddonInfo('path'), 'resources', 'art') | |
| addon_id = cConfig().getAddonInfo('id') | |
| start_time = time.time() | |
| # timeout for the startup status check = 60s | |
| while (startupStatus := cCache().get(addon_id + '_main', -1)) != 'finished' and time.time() - start_time <= 60: | |
| time.sleep(0.5) | |
| oGui = cGui() | |
| # Setzte die globale Suche an erste Stelle | |
| if cConfig().getSetting('GlobalSearchPosition') == 'true': | |
| oGui.addFolder(globalSearchGuiElement()) | |
| oPluginHandler = cPluginHandler() | |
| aPlugins = oPluginHandler.getAvailablePlugins() | |
| if not aPlugins: | |
| log(cConfig().getLocalizedString(30166) + ' -> [xstream]: No activated Plugins found', LOGNOTICE) | |
| # Open the settings dialog to choose a plugin that could be enabled | |
| oGui.openSettings() | |
| oGui.updateDirectory() | |
| else: | |
| # Create a gui element for every plugin found | |
| for aPlugin in sorted(aPlugins, key=lambda k: k['id']): | |
| if 'vod_' in aPlugin['id']: | |
| continue | |
| oGuiElement = cGuiElement() | |
| oGuiElement.setTitle(aPlugin['name']) | |
| oGuiElement.setSiteName(aPlugin['id']) | |
| oGuiElement.setFunction(sFunction) | |
| if 'icon' in aPlugin and aPlugin['icon']: | |
| oGuiElement.setThumbnail(aPlugin['icon']) | |
| oGui.addFolder(oGuiElement) | |
| if cConfig().getSetting('GlobalSearchPosition') == 'false': | |
| oGui.addFolder(globalSearchGuiElement()) | |
| if cConfig().getSetting('SettingsFolder') == 'true': | |
| # Einstellung im Menü mit Untereinstellungen | |
| oGuiElement = cGuiElement() | |
| oGuiElement.setTitle(cConfig().getLocalizedString(30041)) | |
| oGuiElement.setSiteName('settings') | |
| oGuiElement.setFunction('showSettingsFolder') | |
| oGuiElement.setThumbnail(os.path.join(ART, 'settings.png')) | |
| oGui.addFolder(oGuiElement) | |
| else: | |
| for folder in settingsGuiElements(): | |
| oGui.addFolder(folder) | |
| oGui.setEndOfDirectory() | |
| def settingsGuiElements(): | |
| ART = os.path.join(cConfig().getAddonInfo('path'), 'resources', 'art') | |
| # GUI Plugin Informationen | |
| oGuiElement = cGuiElement() | |
| oGuiElement.setTitle(cConfig().getLocalizedString(30267)) | |
| oGuiElement.setSiteName('pluginInfo') | |
| oGuiElement.setFunction('pluginInfo') | |
| oGuiElement.setThumbnail(os.path.join(ART, 'plugin_info.png')) | |
| PluginInfo = oGuiElement | |
| # GUI xStream Einstellungen | |
| oGuiElement = cGuiElement() | |
| oGuiElement.setTitle(cConfig().getLocalizedString(30042)) | |
| oGuiElement.setSiteName('xStream') | |
| oGuiElement.setFunction('display_settings') | |
| oGuiElement.setThumbnail(os.path.join(ART, 'xstream_settings.png')) | |
| xStreamSettings = oGuiElement | |
| # GUI Resolver Einstellungen | |
| oGuiElement = cGuiElement() | |
| oGuiElement.setTitle(cConfig().getLocalizedString(30043)) | |
| oGuiElement.setSiteName('resolver') | |
| oGuiElement.setFunction('display_settings') | |
| oGuiElement.setThumbnail(os.path.join(ART, 'resolveurl_settings.png')) | |
| resolveurlSettings = oGuiElement | |
| # GUI Nightly Updatemanager | |
| oGuiElement = cGuiElement() | |
| oGuiElement.setTitle(cConfig().getLocalizedString(30121)) | |
| oGuiElement.setSiteName('devUpdates') | |
| oGuiElement.setFunction('devUpdates') | |
| oGuiElement.setThumbnail(os.path.join(ART, 'manuel_update.png')) | |
| DevUpdateMan = oGuiElement | |
| return PluginInfo, xStreamSettings, resolveurlSettings, DevUpdateMan | |
| def globalSearchGuiElement(): | |
| ART = os.path.join(cConfig().getAddonInfo('path'), 'resources', 'art') | |
| # Create a gui element for global search | |
| oGuiElement = cGuiElement() | |
| oGuiElement.setTitle(cConfig().getLocalizedString(30040)) | |
| oGuiElement.setSiteName('globalSearch') | |
| oGuiElement.setFunction('globalSearch') | |
| oGuiElement.setThumbnail(os.path.join(ART, 'search.png')) | |
| return oGuiElement | |
| def showHosterGui(sFunction): | |
| from resources.lib.gui.hoster import cHosterGui | |
| oHosterGui = cHosterGui() | |
| function = getattr(oHosterGui, sFunction) | |
| function() | |
| return True | |
| def searchGlobal(sSearchText=False): | |
| oGui = cGui() | |
| oGui.globalSearch = True | |
| oGui._collectMode = True | |
| if not sSearchText: | |
| sSearchText = oGui.showKeyBoard(sHeading=cConfig().getLocalizedString(30280)) # Bitte Suchbegriff eingeben | |
| if not sSearchText: | |
| oGui.setEndOfDirectory() | |
| return True | |
| aPlugins = cPluginHandler().getAvailablePlugins() | |
| dialog = xbmcgui.DialogProgress() | |
| dialog.create(cConfig().getLocalizedString(30122), cConfig().getLocalizedString(30123)) | |
| numPlugins = len(aPlugins) | |
| searchablePlugins = [pluginEntry for pluginEntry in aPlugins if pluginEntry['globalsearch'] not in ['false', '']] | |
| def worker(pluginEntry): | |
| log(cConfig().getLocalizedString(30166) + ' -> [xstream]: Searching for %s at %s' % (sSearchText, pluginEntry['id']),LOGNOTICE) | |
| _pluginSearch(pluginEntry, sSearchText, oGui) | |
| return pluginEntry['name'] | |
| with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor: | |
| future_to_plugin = {executor.submit(worker, pluginEntry): pluginEntry for pluginEntry in searchablePlugins} | |
| for count, future in enumerate(concurrent.futures.as_completed(future_to_plugin)): | |
| pluginEntry = future_to_plugin[future] | |
| if dialog.iscanceled(): | |
| oGui.setEndOfDirectory() | |
| return | |
| try: pluginName = future.result() | |
| except Exception as e: | |
| pluginName = pluginEntry['name'] | |
| log(f"Fehler bei Plugin {pluginName}: {str(e)}", LOGERROR) | |
| progress = (count + 1) * 50 // len(searchablePlugins) | |
| dialog.update(progress, pluginName + cConfig().getLocalizedString(30125)) | |
| dialog.close() | |
| # Ergebnisse anzeigen | |
| oGui._collectMode = False | |
| total = len(oGui.searchResults) | |
| dialog = xbmcgui.DialogProgress() | |
| dialog.create(cConfig().getLocalizedString(30126), cConfig().getLocalizedString(30127)) | |
| for count, result in enumerate(sorted(oGui.searchResults, key=lambda k: k['guiElement'].getSiteName()), 1): | |
| if dialog.iscanceled(): | |
| oGui.setEndOfDirectory() | |
| return | |
| oGui.addFolder(result['guiElement'], result['params'], bIsFolder=result['isFolder'], iTotal=total) | |
| dialog.update(count * 100 // total, str(count) + cConfig().getLocalizedString(30128) + str(total) + ': ' + result['guiElement'].getTitle()) | |
| dialog.close() | |
| oGui.setView() | |
| oGui.setEndOfDirectory() | |
| return True | |
| def searchAlter(params): | |
| searchTitle = params.getValue('searchTitle') | |
| searchImdbId = params.getValue('searchImdbID') | |
| searchYear = params.getValue('searchYear') | |
| # Jahr aus dem Titel extrahieren | |
| if ' (19' in searchTitle or ' (20' in searchTitle: | |
| isMatch, aYear = cParser.parse(searchTitle, r'(.*?) \((\d{4})\)') | |
| if isMatch: | |
| searchTitle = aYear[0][0] | |
| if not searchYear: | |
| searchYear = str(aYear[0][1]) | |
| # Staffel oder Episodenkennung abschneiden | |
| for token in [' S0', ' E0', ' - Staffel', ' Staffel']: | |
| if token in searchTitle: | |
| searchTitle = searchTitle.split(token)[0].strip() | |
| break | |
| oGui = cGui() | |
| oGui.globalSearch = True | |
| oGui._collectMode = True | |
| aPlugins = cPluginHandler().getAvailablePlugins() | |
| dialog = xbmcgui.DialogProgress() | |
| dialog.create(cConfig().getLocalizedString(30122), cConfig().getLocalizedString(30123)) | |
| searchablePlugins = [ | |
| pluginEntry for pluginEntry in aPlugins | |
| if pluginEntry['globalsearch'] not in ['false', ''] | |
| ] | |
| def worker(pluginEntry): | |
| log(cConfig().getLocalizedString(30166) + ' -> [xstream]: Searching for ' + searchTitle + pluginEntry['id'], LOGNOTICE) | |
| _pluginSearch(pluginEntry, searchTitle, oGui) | |
| return pluginEntry['name'] | |
| with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor: | |
| future_to_plugin = {executor.submit(worker, plugin): plugin for plugin in searchablePlugins} | |
| for count, future in enumerate(concurrent.futures.as_completed(future_to_plugin)): | |
| plugin = future_to_plugin[future] | |
| if dialog.iscanceled(): | |
| oGui.setEndOfDirectory() | |
| return | |
| try: | |
| name = future.result() | |
| except Exception as e: | |
| name = plugin['name'] | |
| log(f"Fehler bei Plugin {name}: {str(e)}", LOGERROR) | |
| dialog.update((count + 1) * 50 // len(searchablePlugins) + 50, name + cConfig().getLocalizedString(30125)) | |
| dialog.close() | |
| # Ergebnisse filtern | |
| filteredResults = [] | |
| for result in oGui.searchResults: | |
| guiElement = result['guiElement'] | |
| log(cConfig().getLocalizedString(30166) + ' -> [xstream]: Site: %s Titel: %s' % (guiElement.getSiteName(), guiElement.getTitle()), LOGNOTICE) | |
| if searchTitle not in guiElement.getTitle(): | |
| continue | |
| if guiElement._sYear and searchYear and guiElement._sYear != searchYear: | |
| continue | |
| if searchImdbId and guiElement.getItemProperties().get('imdbID') != searchImdbId: | |
| continue | |
| filteredResults.append(result) | |
| oGui._collectMode = False | |
| total = len(filteredResults) | |
| for result in sorted(filteredResults, key=lambda k: k['guiElement'].getSiteName()): | |
| oGui.addFolder(result['guiElement'], result['params'], bIsFolder=result['isFolder'], iTotal=total) | |
| oGui.setView() | |
| oGui.setEndOfDirectory() | |
| xbmc.executebuiltin('Container.Update') | |
| return True | |
| def searchTMDB(params): | |
| sSearchText = params.getValue('searchTitle') | |
| oGui = cGui() | |
| oGui.globalSearch = True | |
| oGui._collectMode = True | |
| if not sSearchText: | |
| oGui.setEndOfDirectory() | |
| return True | |
| aPlugins = cPluginHandler().getAvailablePlugins() | |
| dialog = xbmcgui.DialogProgress() | |
| dialog.create(cConfig().getLocalizedString(30122), cConfig().getLocalizedString(30123)) | |
| searchablePlugins = [ | |
| pluginEntry for pluginEntry in aPlugins | |
| if pluginEntry['globalsearch'] != 'false' | |
| ] | |
| def worker(pluginEntry): | |
| log(cConfig().getLocalizedString(30166) + ' -> [xstream]: Searching for %s at %s' % (sSearchText, pluginEntry['id']), LOGNOTICE) | |
| _pluginSearch(pluginEntry, sSearchText, oGui) | |
| return pluginEntry['name'] | |
| with concurrent.futures.ThreadPoolExecutor(max_workers=10) as executor: | |
| future_to_plugin = {executor.submit(worker, plugin): plugin for plugin in searchablePlugins} | |
| for count, future in enumerate(concurrent.futures.as_completed(future_to_plugin)): | |
| plugin = future_to_plugin[future] | |
| if dialog.iscanceled(): | |
| oGui.setEndOfDirectory() | |
| return | |
| try: | |
| name = future.result() | |
| except Exception as e: | |
| name = plugin['name'] | |
| log(f"Fehler bei Plugin {name}: {str(e)}", LOGERROR) | |
| dialog.update((count + 1) * 50 // len(searchablePlugins) + 50, name + cConfig().getLocalizedString(30125)) | |
| dialog.close() | |
| oGui._collectMode = False | |
| total = len(oGui.searchResults) | |
| dialog = xbmcgui.DialogProgress() | |
| dialog.create(cConfig().getLocalizedString(30126), cConfig().getLocalizedString(30127)) | |
| for count, result in enumerate(sorted(oGui.searchResults, key=lambda k: k['guiElement'].getSiteName()), 1): | |
| if dialog.iscanceled(): | |
| oGui.setEndOfDirectory() | |
| return | |
| oGui.addFolder(result['guiElement'], result['params'], bIsFolder=result['isFolder'], iTotal=total) | |
| dialog.update(count * 100 // total, str(count) + cConfig().getLocalizedString(30128) + str(total) + ': ' + result['guiElement'].getTitle()) | |
| dialog.close() | |
| oGui.setView() | |
| oGui.setEndOfDirectory() | |
| return True | |
| def _pluginSearch(pluginEntry, sSearchText, oGui): | |
| try: | |
| plugin = __import__(pluginEntry['id'], globals(), locals()) | |
| function = getattr(plugin, '_search') | |
| function(oGui, sSearchText) | |
| except Exception: | |
| log(cConfig().getLocalizedString(30166) + ' -> [xstream]: ' + pluginEntry['name'] + ': search failed', LOGERROR) | |
| import traceback | |
| log(traceback.format_exc()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment