Wrapper-Site Auflösung für meinecloud.click implementiert
- _resolve_wrapper_site() erkennt meinecloud.click und parst data-link Attribute - Rekursive Auflösung: Wrapper -> Hoster -> Streaming-API - Streaming-API 404 Fallback: URL direkt zurückgeben wenn API nicht existiert - HTML-Erkennung bei Downloads bleibt aktiv für Hosts die trotzdem HTML zurückgeben - STATUS.md aktualisiert
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# xStream Downloader – Projektstatus
|
# xStream Downloader – Projektstatus
|
||||||
|
|
||||||
Stand: 7.7.2026, 16:56
|
Stand: 7.7.2026, 20:17
|
||||||
|
|
||||||
## Ziel
|
## Ziel
|
||||||
Web-Wrapper für das xstream Kodi-Addon, mit dem man Filme suchen, finden
|
Web-Wrapper für das xstream Kodi-Addon, mit dem man Filme suchen, finden
|
||||||
@@ -53,6 +53,8 @@ xstreamDownloader/
|
|||||||
12. **HLS/m3u8 Download** via ffmpeg
|
12. **HLS/m3u8 Download** via ffmpeg
|
||||||
13. **HTML-Erkennung** – verhindert 16KB HTML-Files als "fertige Downloads"
|
13. **HTML-Erkennung** – verhindert 16KB HTML-Files als "fertige Downloads"
|
||||||
14. **Streaming-Site Auflösung** – vidara.to etc. via API (filecode → m3u8)
|
14. **Streaming-Site Auflösung** – vidara.to etc. via API (filecode → m3u8)
|
||||||
|
15. **Wrapper-Site Auflösung** – meinecloud.click etc. via HTML-Parsing (data-link Attribute)
|
||||||
|
16. **Rekursive Auflösung** – Wrapper → Hoster → Streaming-API (z.B. meinecloud → dr0pstream → m3u8)
|
||||||
|
|
||||||
## Navigation (Menü → Inhalte)
|
## Navigation (Menü → Inhalte)
|
||||||
|
|
||||||
@@ -82,6 +84,7 @@ Beim Klick auf einen Menüpunkt:
|
|||||||
| 14 | Port 8769 belegt | Geändert auf Port 8770 | 6.7.2026 |
|
| 14 | Port 8769 belegt | Geändert auf Port 8770 | 6.7.2026 |
|
||||||
| 15 | Download 16KB HTML statt Video (VOE etc.) | HTML-Erkennung in _run_direct() | 7.7.2026 |
|
| 15 | Download 16KB HTML statt Video (VOE etc.) | HTML-Erkennung in _run_direct() | 7.7.2026 |
|
||||||
| 16 | Streaming-Sites liefern HTML statt m3u8 | _resolve_streaming_api() für vidara.to etc. | 7.7.2026 |
|
| 16 | Streaming-Sites liefern HTML statt m3u8 | _resolve_streaming_api() für vidara.to etc. | 7.7.2026 |
|
||||||
|
| 17 | Wrapper-Site meinecloud.click gibt HTML zurück | _resolve_wrapper_site() parst data-link Attribute | 7.7.2026 |
|
||||||
|
|
||||||
## Verbleibende Aufgaben
|
## Verbleibende Aufgaben
|
||||||
|
|
||||||
|
|||||||
@@ -542,13 +542,88 @@ def _resolve_streaming_api(url):
|
|||||||
m3u8_url = data.get('streaming_url')
|
m3u8_url = data.get('streaming_url')
|
||||||
if m3u8_url:
|
if m3u8_url:
|
||||||
return (m3u8_url, None)
|
return (m3u8_url, None)
|
||||||
return (None, 'streaming_url nicht in API-Antwort')
|
# API responded but no streaming_url - might be direct embed
|
||||||
|
# Return URL for direct handling instead of failing
|
||||||
|
return (url, None)
|
||||||
|
except requests.exceptions.HTTPError as e:
|
||||||
|
if e.response.status_code == 404:
|
||||||
|
# API endpoint doesn't exist - this is likely a direct embed URL
|
||||||
|
# Return the URL for direct handling
|
||||||
|
return (url, None)
|
||||||
|
return (None, f'Streaming-API Fehler: {e}')
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return (None, f'Streaming-API Fehler: {e}')
|
return (None, f'Streaming-API Fehler: {e}')
|
||||||
|
|
||||||
|
|
||||||
|
def _resolve_wrapper_site(url):
|
||||||
|
"""Löst Wrapper-Sites (meinecloud.click etc.) auf: Fetches HTML und parsed data-link Attribute.
|
||||||
|
|
||||||
|
Wrapper-Sites wie meinecloud.click geben keine direkten Video-URLs zurück, sondern
|
||||||
|
HTML-Seiten mit eingebetteten Hoster-URLs in data-link Attributen.
|
||||||
|
"""
|
||||||
|
import re
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
# Prüfe ob es eine bekannte Wrapper-Site ist
|
||||||
|
parsed = urlparse(url)
|
||||||
|
host = parsed.netloc.lower()
|
||||||
|
|
||||||
|
# Wrapper-Site Patterns
|
||||||
|
WRAPPER_PATTERNS = [
|
||||||
|
r'meinecloud\.click',
|
||||||
|
r'meinedownload\.',
|
||||||
|
r'mycloud\.',
|
||||||
|
]
|
||||||
|
|
||||||
|
is_wrapper = any(re.search(p, host) for p in WRAPPER_PATTERNS)
|
||||||
|
if not is_wrapper:
|
||||||
|
return (None, None) # Keine Wrapper-Site
|
||||||
|
|
||||||
|
# Futtern Sie die HTML-Seite und parsen data-link Attribute
|
||||||
|
try:
|
||||||
|
import requests
|
||||||
|
headers = {
|
||||||
|
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/149.0.0.0 Safari/537.36',
|
||||||
|
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
|
||||||
|
'Accept-Language': 'de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7',
|
||||||
|
}
|
||||||
|
resp = requests.get(url, headers=headers, timeout=15)
|
||||||
|
resp.raise_for_status()
|
||||||
|
html = resp.text
|
||||||
|
|
||||||
|
# Parse data-link Attribute
|
||||||
|
# Pattern: data-link="https://host.com/e/filecode" oder data-link="//host.com/e/filecode"
|
||||||
|
links = re.findall(r'data-link="([^"]+)"', html)
|
||||||
|
|
||||||
|
if not links:
|
||||||
|
return (None, f'Keine data-link URLs in Wrapper-HTML gefunden')
|
||||||
|
|
||||||
|
# Filtere leere und interne Links
|
||||||
|
valid_links = []
|
||||||
|
for link in links:
|
||||||
|
if not link or 'meinecloud' in link.lower():
|
||||||
|
continue
|
||||||
|
# Protokoll-relativ beheben
|
||||||
|
if link.startswith('//'):
|
||||||
|
link = 'https:' + link
|
||||||
|
valid_links.append(link)
|
||||||
|
|
||||||
|
if not valid_links:
|
||||||
|
return (None, f'Keine validen Hoster-URLs in Wrapper gefunden')
|
||||||
|
|
||||||
|
# Gebe die erste brauchbare URL zurück (bevorzugt VOE wenn verfügbar)
|
||||||
|
for link in valid_links:
|
||||||
|
if 'voe' in link.lower():
|
||||||
|
return (link, None)
|
||||||
|
|
||||||
|
return (valid_links[0], None)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
return (None, f'Wrapper-Aufloesung Fehler: {e}')
|
||||||
|
|
||||||
|
|
||||||
def _resolve_url(url):
|
def _resolve_url(url):
|
||||||
# Erst prüfen ob es eine bekannte Streaming-Site ist
|
# 1. Prüfe ob es eine bekannte Streaming-Site ist (/e/<filecode> API Pattern)
|
||||||
m3u8_url, err = _resolve_streaming_api(url)
|
m3u8_url, err = _resolve_streaming_api(url)
|
||||||
if m3u8_url:
|
if m3u8_url:
|
||||||
return (m3u8_url, None)
|
return (m3u8_url, None)
|
||||||
@@ -556,7 +631,15 @@ def _resolve_url(url):
|
|||||||
# Streaming-Site erkannt aber Fehler
|
# Streaming-Site erkannt aber Fehler
|
||||||
return (None, err)
|
return (None, err)
|
||||||
|
|
||||||
# Falls resolveurl nicht verfügbar, URL direkt zurückgeben
|
# 2. Prüfe ob es eine Wrapper-Site ist (meinecloud.click etc.)
|
||||||
|
wrapper_url, wrapper_err = _resolve_wrapper_site(url)
|
||||||
|
if wrapper_url:
|
||||||
|
return _resolve_url(wrapper_url) # Rekursiv auflösen (könnte wieder Streaming-Site sein)
|
||||||
|
if wrapper_err:
|
||||||
|
# Wrapper erkannt aber Fehler
|
||||||
|
return (None, wrapper_err)
|
||||||
|
|
||||||
|
# 3. Falls resolveurl nicht verfügbar, URL direkt zurückgeben
|
||||||
# (viele URLs sind bereits direkte Stream-URLs)
|
# (viele URLs sind bereits direkte Stream-URLs)
|
||||||
try:
|
try:
|
||||||
import resolveurl
|
import resolveurl
|
||||||
|
|||||||
+6120
-819
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user