Skip to content

Instantly share code, notes, and snippets.

@CarstenG2
Created February 11, 2026 08:50
Show Gist options
  • Select an option

  • Save CarstenG2/b7f3b58c62cfb91bc74a4c4be9027b23 to your computer and use it in GitHub Desktop.

Select an option

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
# -*- 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
# -*- 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
# -*- 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
# -*- 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('&nbsp;', '')
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)
# -*- 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))
# -*- 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
# -*- 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
# -*- 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 (('\\/', '/'), ('&amp;', '&'), ('\\u00c4', 'Ä'), ('\\u00e4', 'ä'),
('\\u00d6', 'Ö'), ('\\u00f6', 'ö'), ('\\u00dc', 'Ü'), ('\\u00fc', 'ü'),
('\\u00df', 'ß'), ('\\u2013', '-'), ('\\u00b2', '²'), ('\\u00b3', '³'),
('\\u00e9', 'é'), ('\\u2018', '‘'), ('\\u201e', '„'), ('\\u201c', '“'),
('\\u00c9', 'É'), ('\\u2026', '...'), ('\\u202f', 'h'), ('\\u2019', '’'),
('\\u0308', '̈'), ('\\u00e8', 'è'), ('#038;', ''), ('\\u00f8', 'ø'),
('/', '/'), ('\\u00e1', 'á'), ('&#8211;', '-'), ('&#8220;', '“'), ('&#8222;', '„'),
('&#8217;', '’'), ('&#8230;', '…'), ('\\u00bc', '¼'), ('\\u00bd', '½'), ('\\u00be', '¾'),
('\\u2153', '⅓'), ('\\u002A', '*')):
s = s.replace(*t)
# Umlaute HTML konvertieren
for h in (('\\/', '/'), ('&#x26;', '&'), ('&#039;', "'"), ("&#39;", "'"),
('&#xC4;', 'Ä'), ('&#xE4;', 'ä'), ('&#xD6;', 'Ö'), ('&#xF6;', 'ö'),
('&#xDC;', 'Ü'), ('&#xFC;', 'ü'), ('&#xDF;', 'ß') , ('&#xB2;', '²'),
('&#xDC;', '³'), ('&#xBC;', '¼'), ('&#xBD;', '½'), ('&#xBE;', '¾'),
('&#8531;', '⅓'), ('&#8727;', '*')):
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()
# -*- 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="([^"]+)">&gt;</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
# -*- 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
# -*- 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)
# -*- 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()
# -*- 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()
# -*- 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()
# -*- 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
# -*- 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
# -*- 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)
# -*- 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
# -*- 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()
# -*- 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)
# -*- 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
# -*- 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)
# -*- 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="([^"]+)">&gt;</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
# -*- 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()
# -*- 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()
# -*- 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
# -*- 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