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
|
||||
|
||||
Stand: 7.7.2026, 16:56
|
||||
Stand: 7.7.2026, 20:17
|
||||
|
||||
## Ziel
|
||||
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
|
||||
13. **HTML-Erkennung** – verhindert 16KB HTML-Files als "fertige Downloads"
|
||||
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)
|
||||
|
||||
@@ -82,6 +84,7 @@ Beim Klick auf einen Menüpunkt:
|
||||
| 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 |
|
||||
| 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
|
||||
|
||||
|
||||
@@ -542,13 +542,88 @@ def _resolve_streaming_api(url):
|
||||
m3u8_url = data.get('streaming_url')
|
||||
if m3u8_url:
|
||||
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:
|
||||
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):
|
||||
# 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)
|
||||
if m3u8_url:
|
||||
return (m3u8_url, None)
|
||||
@@ -556,7 +631,15 @@ def _resolve_url(url):
|
||||
# Streaming-Site erkannt aber Fehler
|
||||
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)
|
||||
try:
|
||||
import resolveurl
|
||||
|
||||
+6120
-819
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user