Compare commits
1 Commits
main
...
DAP-86-hin
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5fdc576054 |
1
plugin.video.xstream/resources/lib/data/__init__.py
Normal file
1
plugin.video.xstream/resources/lib/data/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
37
plugin.video.xstream/resources/lib/data/api_all_domains.py
Normal file
37
plugin.video.xstream/resources/lib/data/api_all_domains.py
Normal file
@@ -0,0 +1,37 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Python 3
|
||||
|
||||
API_ALL_DOMAINS = (
|
||||
{
|
||||
'label': 'Filmpalast.to',
|
||||
'domain': 'filmpalast.to',
|
||||
'url': 'http://filmpalast.to/',
|
||||
},
|
||||
{
|
||||
'label': 'KinoGer.com',
|
||||
'domain': 'kinoger.com',
|
||||
'url': 'https://kinoger.com/',
|
||||
},
|
||||
{
|
||||
'label': 'KinoX.to',
|
||||
'domain': 'kinox.to',
|
||||
'url': 'https://kinox.to/',
|
||||
},
|
||||
{
|
||||
'label': 'MovieDream.co',
|
||||
'domain': 'moviedream.co',
|
||||
'url': 'https://moviedream.co/',
|
||||
},
|
||||
{
|
||||
'label': 'StreamWorld.to',
|
||||
'domain': 'streamworld.to',
|
||||
'url': 'http://streamworld.to/',
|
||||
},
|
||||
{
|
||||
'label': 'Moflix-Stream',
|
||||
'domain': 'moflix-stream.xyz',
|
||||
'url': 'https://moflix-stream.xyz/',
|
||||
},
|
||||
)
|
||||
|
||||
API_ALL_DEFAULT_DOMAIN = 'kinox.to'
|
||||
@@ -36,6 +36,7 @@ class cPluginHandler:
|
||||
pluginDB = self.__getPluginDB()
|
||||
update = False
|
||||
fileNames = self.__getFileNamesFromFolder(self.defaultFolder)
|
||||
forceSearchPlugins = {'api_all', 'kinoger'}
|
||||
|
||||
for fileName in fileNames:
|
||||
plugin = {'name': '', 'identifier': '', 'icon': '', 'domain': '', 'globalsearch': '', 'modified': 0}
|
||||
@@ -52,6 +53,10 @@ class cPluginHandler:
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
if fileName in forceSearchPlugins and globalSearchStatus != 'true':
|
||||
cConfig().setSetting('global_search_' + fileName, 'true')
|
||||
globalSearchStatus = 'true'
|
||||
|
||||
if fileName not in pluginDB or modTime > plugin['modified'] or globalSearchStatus:
|
||||
pluginData = self.__getPluginData(fileName, self.defaultFolder)
|
||||
if pluginData:
|
||||
|
||||
@@ -314,7 +314,7 @@
|
||||
</setting>
|
||||
<setting id="global_search_api_all" type="boolean" label="30052">
|
||||
<level>0</level>
|
||||
<default>False</default>
|
||||
<default>True</default>
|
||||
<dependencies>
|
||||
<dependency type="enable" operator="!is" setting="plugin_api_all">False</dependency>
|
||||
</dependencies>
|
||||
@@ -322,18 +322,15 @@
|
||||
</setting>
|
||||
<setting id="plugin_api_all.domain" type="string" label="30278" help="">
|
||||
<level>0</level>
|
||||
<default>kinokiste.eu</default>
|
||||
<default>kinox.to</default>
|
||||
<constraints>
|
||||
<options>
|
||||
<option label="HDFilme">hdfilme.me</option>
|
||||
<option label="Kinokiste">kinokiste.eu</option>
|
||||
<option label="Kinoz">kinoz.org</option>
|
||||
<option label="KKiste">kkiste.eu</option>
|
||||
<option label="Movie2k">movie2k.ch</option>
|
||||
<option label="Movie4k">movie4k.sx</option>
|
||||
<option label="Streamcloud">streamcloud.sx</option>
|
||||
<option label="StreamKiste">streamkiste.sx</option>
|
||||
<option label="XCine">xcine.io</option>
|
||||
<option label="Filmpalast.to">filmpalast.to</option>
|
||||
<option label="KinoGer.com">kinoger.com</option>
|
||||
<option label="KinoX.to">kinox.to</option>
|
||||
<option label="MovieDream.co">moviedream.co</option>
|
||||
<option label="StreamWorld.to">streamworld.to</option>
|
||||
<option label="Moflix-Stream">moflix-stream.xyz</option>
|
||||
<allowempty>true</allowempty>
|
||||
</options>
|
||||
</constraints>
|
||||
@@ -532,15 +529,20 @@
|
||||
</setting>
|
||||
<setting id="plugin_hdfilme.domain" type="string" label="30278" help="">
|
||||
<level>3</level>
|
||||
<default/>
|
||||
<default>hdfilme.garden</default>
|
||||
<constraints>
|
||||
<options>
|
||||
<option label="HDFilme.tv">hdfilme.tv</option>
|
||||
<option label="HDFilme.garden">hdfilme.garden</option>
|
||||
<option label="HDFilme.legal">hdfilme.legal</option>
|
||||
</options>
|
||||
<allowempty>true</allowempty>
|
||||
</constraints>
|
||||
<dependencies>
|
||||
<dependency type="enable" operator="!is" setting="plugin_hdfilme">false</dependency>
|
||||
<dependency type="visible" operator="!is" setting="plugin_hdfilme">false</dependency>
|
||||
</dependencies>
|
||||
<control type="edit" format="string">
|
||||
<control type="spinner" format="string">
|
||||
<heading>30278</heading>
|
||||
</control>
|
||||
</setting>
|
||||
@@ -576,15 +578,20 @@
|
||||
</setting>
|
||||
<setting id="plugin_hdfilme_1.domain" type="string" label="30278" help="">
|
||||
<level>3</level>
|
||||
<default/>
|
||||
<default>hdfilme.legal</default>
|
||||
<constraints>
|
||||
<options>
|
||||
<option label="HDFilme.tv">hdfilme.tv</option>
|
||||
<option label="HDFilme.legal">hdfilme.legal</option>
|
||||
<option label="HDFilme.my">hdfilme.my</option>
|
||||
</options>
|
||||
<allowempty>true</allowempty>
|
||||
</constraints>
|
||||
<dependencies>
|
||||
<dependency type="enable" operator="!is" setting="plugin_hdfilme_1">false</dependency>
|
||||
<dependency type="visible" operator="!is" setting="plugin_hdfilme_1">false</dependency>
|
||||
</dependencies>
|
||||
<control type="edit" format="string">
|
||||
<control type="spinner" format="string">
|
||||
<heading>30278</heading>
|
||||
</control>
|
||||
</setting>
|
||||
@@ -648,7 +655,7 @@
|
||||
</setting>
|
||||
<setting id="global_search_kinoger" type="boolean" label="30052">
|
||||
<level>0</level>
|
||||
<default>False</default>
|
||||
<default>True</default>
|
||||
<dependencies>
|
||||
<dependency type="enable" operator="!is" setting="plugin_kinoger">False</dependency>
|
||||
</dependencies>
|
||||
@@ -756,9 +763,11 @@
|
||||
</setting>
|
||||
<setting id="plugin_kinox.domain" type="string" label="30278" help="">
|
||||
<level>3</level>
|
||||
<default>kinoz.to</default><default/>
|
||||
<default>ww22.kinoz.to</default>
|
||||
<constraints>
|
||||
<options>
|
||||
<option label="kinox.to">kinox.to</option>
|
||||
<option label="ww22.kinoz.to">ww22.kinoz.to</option>
|
||||
<option label="kinoz.to">kinoz.to</option>
|
||||
<option label="www12.kinoz.to">www12.kinoz.to</option>
|
||||
<option label="www13.kinoz.to">www13.kinoz.to</option>
|
||||
@@ -816,15 +825,19 @@
|
||||
</setting>
|
||||
<setting id="plugin_kkiste.domain" type="string" label="30278" help="">
|
||||
<level>3</level>
|
||||
<default/>
|
||||
<default>kkiste.eu</default>
|
||||
<constraints>
|
||||
<options>
|
||||
<option label="KKiste.ag">kkiste.ag</option>
|
||||
<option label="KKiste.eu">kkiste.eu</option>
|
||||
</options>
|
||||
<allowempty>true</allowempty>
|
||||
</constraints>
|
||||
<dependencies>
|
||||
<dependency type="enable" operator="!is" setting="plugin_kkiste">false</dependency>
|
||||
<dependency type="visible" operator="!is" setting="plugin_kkiste">false</dependency>
|
||||
</dependencies>
|
||||
<control type="edit" format="string">
|
||||
<control type="spinner" format="string">
|
||||
<heading>30278</heading>
|
||||
</control>
|
||||
</setting>
|
||||
@@ -992,15 +1005,19 @@
|
||||
</setting>
|
||||
<setting id="plugin_movie4k.domain" type="string" label="30278" help="">
|
||||
<level>3</level>
|
||||
<default/>
|
||||
<default>movie4k.sx</default>
|
||||
<constraints>
|
||||
<options>
|
||||
<option label="Movie4k.org">movie4k.org</option>
|
||||
<option label="Movie4k.sx">movie4k.sx</option>
|
||||
</options>
|
||||
<allowempty>true</allowempty>
|
||||
</constraints>
|
||||
<dependencies>
|
||||
<dependency type="enable" operator="!is" setting="plugin_movie4k">false</dependency>
|
||||
<dependency type="visible" operator="!is" setting="plugin_movie4k">false</dependency>
|
||||
</dependencies>
|
||||
<control type="edit" format="string">
|
||||
<control type="spinner" format="string">
|
||||
<heading>30278</heading>
|
||||
</control>
|
||||
</setting>
|
||||
@@ -1055,6 +1072,7 @@
|
||||
<constraints>
|
||||
<options>
|
||||
<option label="S.to">s.to</option>
|
||||
<option label="stream.to">stream.to</option>
|
||||
<option label="SerienStream.to">serienstream.to</option>
|
||||
<option label="SerienStream.stream(VPN)">serienstream.stream</option>
|
||||
<option label="Proxy Server">186.2.175.5</option>
|
||||
@@ -1662,4 +1680,4 @@
|
||||
</group>
|
||||
</category>
|
||||
</section>
|
||||
</settings>
|
||||
</settings>
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
import re
|
||||
import xbmcgui
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from urllib.parse import urlparse
|
||||
|
||||
from resources.lib.handler.ParameterHandler import ParameterHandler
|
||||
from resources.lib.handler.requestHandler import cRequestHandler
|
||||
from resources.lib.tools import logger, cParser, cUtil
|
||||
@@ -16,17 +19,89 @@ from resources.lib.config import cConfig
|
||||
from resources.lib.gui.gui import cGui
|
||||
from json import loads
|
||||
from datetime import datetime
|
||||
from resources.lib.data.api_all_domains import API_ALL_DOMAINS, API_ALL_DEFAULT_DOMAIN
|
||||
|
||||
# Globale Variable für die JSON-Daten
|
||||
apiJson = None
|
||||
|
||||
# Domain Abfrage ###
|
||||
|
||||
def _normalize_domain(value):
|
||||
if not value:
|
||||
return ''
|
||||
|
||||
parsed = urlparse(value)
|
||||
if parsed.hostname:
|
||||
return parsed.hostname.lower()
|
||||
|
||||
return value.strip().lower()
|
||||
|
||||
|
||||
def _get_default_domain():
|
||||
registry = _get_domain_registry()
|
||||
if API_ALL_DEFAULT_DOMAIN in registry:
|
||||
return API_ALL_DEFAULT_DOMAIN
|
||||
if API_ALL_DOMAINS:
|
||||
return API_ALL_DOMAINS[0]['domain']
|
||||
return API_ALL_DEFAULT_DOMAIN
|
||||
|
||||
|
||||
def _get_domain_registry():
|
||||
return {entry['domain']: entry for entry in API_ALL_DOMAINS}
|
||||
|
||||
|
||||
def _get_configured_domain():
|
||||
registry = _get_domain_registry()
|
||||
configured = _normalize_domain(
|
||||
cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', _get_default_domain())
|
||||
)
|
||||
|
||||
if configured in registry:
|
||||
return configured
|
||||
|
||||
return _get_default_domain()
|
||||
|
||||
|
||||
def _get_search_domains():
|
||||
domains = []
|
||||
configured = _get_configured_domain()
|
||||
if configured:
|
||||
domains.append(configured)
|
||||
|
||||
for entry in API_ALL_DOMAINS:
|
||||
domain = entry['domain']
|
||||
if domain not in domains:
|
||||
domains.append(domain)
|
||||
|
||||
return domains
|
||||
|
||||
|
||||
def _fetch_movies_for_domain(domain, sLang):
|
||||
oRequest = cRequestHandler(
|
||||
'https://' + domain + '/data/browse/?lang=%s&order_by=%s&page=%s&limit=0' % (sLang, 'new', '1'),
|
||||
caching=True
|
||||
)
|
||||
oRequest.addHeaderEntry('Referer', 'https://' + domain + '/')
|
||||
oRequest.addHeaderEntry('Origin', 'https://' + domain + '/')
|
||||
oRequest.cacheTime = 60 * 60 * 48
|
||||
sJson = oRequest.request()
|
||||
aJson = loads(sJson)
|
||||
movies = aJson.get('movies', [])
|
||||
return movies if isinstance(movies, list) else []
|
||||
|
||||
|
||||
def _movie_signature(movie):
|
||||
return (
|
||||
str(movie.get('_id', '')).strip(),
|
||||
str(movie.get('title', '')).strip().lower(),
|
||||
str(movie.get('year', '')).strip(),
|
||||
)
|
||||
|
||||
SITE_NAME = 'API Suchmaschine'
|
||||
SITE_ICON = 'api.png'
|
||||
SITE_IDENTIFIER = 'api_all'
|
||||
|
||||
DOMAIN = cConfig().getSetting('plugin_' + SITE_IDENTIFIER + '.domain', 'movie4k.sx')
|
||||
DOMAIN = _get_configured_domain()
|
||||
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 + '/'
|
||||
@@ -367,7 +442,7 @@ def showEntries(entryUrl=False, sGui=False, sSearchText=False):
|
||||
oGuiElement.setLanguage('EN')
|
||||
oGuiElement.setMediaType('tvshow' if isTvshow else 'movie')
|
||||
if 'runtime' in movie:
|
||||
isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], '\d+')
|
||||
isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], r'\d+')
|
||||
if isMatch:
|
||||
oGuiElement.addItemValue('duration', sRuntime)
|
||||
params.setParam('entryUrl', URL_WATCH % str(movie['_id']))
|
||||
@@ -564,7 +639,7 @@ def SSsearch(sGui=False, sSearchText=False):
|
||||
oGuiElement.setLanguage('EN')
|
||||
oGuiElement.setMediaType('tvshow' if isTvshow else 'movie')
|
||||
if 'runtime' in movie:
|
||||
isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], '\d+')
|
||||
isMatch, sRuntime = cParser.parseSingleResult(movie['runtime'], r'\d+')
|
||||
if isMatch:
|
||||
oGuiElement.addItemValue('duration', sRuntime)
|
||||
params.setParam('entryUrl', URL_WATCH % str(movie['_id']))
|
||||
@@ -585,17 +660,34 @@ def loadMoviesData():
|
||||
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')
|
||||
merged_movies = {}
|
||||
domains = _get_search_domains()
|
||||
|
||||
with ThreadPoolExecutor(max_workers=min(8, len(domains) or 1)) as executor:
|
||||
futures = {
|
||||
executor.submit(_fetch_movies_for_domain, domain, sLang): domain
|
||||
for domain in domains
|
||||
}
|
||||
|
||||
for future in as_completed(futures):
|
||||
domain = futures[future]
|
||||
try:
|
||||
movies = future.result()
|
||||
except Exception:
|
||||
logger.info('API-Daten konnten von %s nicht geladen werden' % domain)
|
||||
continue
|
||||
|
||||
for movie in movies:
|
||||
if not isinstance(movie, dict) or '_id' not in movie:
|
||||
continue
|
||||
merged_movies.setdefault(_movie_signature(movie), movie)
|
||||
|
||||
apiJson = {'movies': list(merged_movies.values())}
|
||||
logger.info('API-Daten erfolgreich aus %d Domains geladen' % len(domains))
|
||||
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
|
||||
# loadMoviesData() - entfernt: beschleunigt den Import/Start erheblich
|
||||
|
||||
@@ -270,10 +270,33 @@ def showHosters():
|
||||
sHtmlContent = cRequestHandler(sUrl, ignoreErrors=True, caching=False).request()
|
||||
pattern = r"show[^>]\d,[^>][^>]'([^']+)"
|
||||
isMatch, aResult = cParser.parse(sHtmlContent, pattern)
|
||||
if not isMatch:
|
||||
isMatch, packed = cParser.parseSingleResult(sHtmlContent, r'(eval\s*\(function.*?)</script>')
|
||||
if isMatch:
|
||||
from resources.lib import jsunpacker
|
||||
sHtmlContent = jsunpacker.unpack(packed)
|
||||
isMatchM3u8, aResultM3u8 = cParser.parse(sHtmlContent, r'(https?://[^"\']+\.m3u8[^"\']*)')
|
||||
if isMatchM3u8:
|
||||
isMatch = True
|
||||
if not aResult:
|
||||
aResult = aResultM3u8
|
||||
else:
|
||||
for sM3u8Url in aResultM3u8:
|
||||
if sM3u8Url not in aResult:
|
||||
aResult.append(sM3u8Url)
|
||||
if isMatch:
|
||||
for sUrl in aResult:
|
||||
try:
|
||||
if 'kinoger.ru' in sUrl: continue
|
||||
if '.m3u8' in sUrl:
|
||||
hoster = {
|
||||
'link': sUrl + '|Referer=https%3A%2F%2Fkinoger.com%2F' + headers,
|
||||
'name': 'KinoGer.com [I][HLS][/I]',
|
||||
'quality': '720',
|
||||
'resolved': True
|
||||
}
|
||||
hosters.append(hoster)
|
||||
continue
|
||||
# oRequest = cRequestHandler(sUrl, caching=False, ignoreErrors=True)
|
||||
# oRequest.addHeaderEntry('Referer', 'https://kinoger.com/')
|
||||
# sHtmlContent = oRequest.request() # Durchsucht sHtml Content
|
||||
@@ -429,4 +452,4 @@ def content_decryptor(html_content,passphrase):
|
||||
# Return the decrypted data as a JSON object
|
||||
return json.loads(plain_text.decode())
|
||||
else:
|
||||
return None
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user