From 162500d20f9cd7dc8e1c66ef53613066c64a1a61 Mon Sep 17 00:00:00 2001 From: Vashiel <8843265+Vashiel@users.noreply.github.com> Date: Mon, 30 Sep 2024 21:59:05 +0200 Subject: [PATCH] v 0.9.81-beta8 v 0.9.81-beta8 30.09.2024 - fixed Heavy_r (didn't work with Android) - experimenting a lot --- addons.xml | 12 +- addons.xml.md5 | 2 +- plugin.video.adulthideout/addon.xml | 12 +- plugin.video.adulthideout/changelog.txt | 4 + plugin.video.adulthideout/resources/search.py | 4 +- .../resources/websites/heavy_r.py | 260 +++++++++++++++--- ...1-beta7.txt => changelog-0.9.81-beta8.txt} | 4 + zips/plugin.video.adulthideout/changelog.txt | 4 + ...lugin.video.adulthideout-0.9.81-beta8.zip} | Bin 1114358 -> 1116145 bytes 9 files changed, 243 insertions(+), 59 deletions(-) rename zips/plugin.video.adulthideout/{changelog-0.9.81-beta7.txt => changelog-0.9.81-beta8.txt} (99%) rename zips/plugin.video.adulthideout/{plugin.video.adulthideout-0.9.81-beta7.zip => plugin.video.adulthideout-0.9.81-beta8.zip} (98%) diff --git a/addons.xml b/addons.xml index 0da5bb6..ec6489b 100644 --- a/addons.xml +++ b/addons.xml @@ -1,6 +1,6 @@ - + @@ -13,13 +13,9 @@ -v 0.9.81-beta7 26.09.2024 -- fixed Xhamster -- fixed Uflash -- fixed Luxeretv -- fixed Heavy_R -- fixed Redtube -- added (hypnotube, punishbang, realcuckoldsex) Thx rocket-dave +v 0.9.81-beta8 30.09.2024 +- fixed Heavy_r (didn't work with Android) +- experimenting a lot [COLOR red]XXX[/COLOR] Porn videos diff --git a/addons.xml.md5 b/addons.xml.md5 index 44d085e..9a5fe7e 100644 --- a/addons.xml.md5 +++ b/addons.xml.md5 @@ -1 +1 @@ -83a90d21a3efe7182fd9bf53c1ccb3fd \ No newline at end of file +7a8c24a52708d69f29e6fb2bd2f6f6b4 \ No newline at end of file diff --git a/plugin.video.adulthideout/addon.xml b/plugin.video.adulthideout/addon.xml index 03e2ad3..5ddf818 100644 --- a/plugin.video.adulthideout/addon.xml +++ b/plugin.video.adulthideout/addon.xml @@ -1,5 +1,5 @@ - + @@ -12,13 +12,9 @@ -v 0.9.81-beta7 26.09.2024 -- fixed Xhamster -- fixed Uflash -- fixed Luxeretv -- fixed Heavy_R -- fixed Redtube -- added (hypnotube, punishbang, realcuckoldsex) Thx rocket-dave +v 0.9.81-beta8 30.09.2024 +- fixed Heavy_r (didn't work with Android) +- experimenting a lot [COLOR red]XXX[/COLOR] Porn videos diff --git a/plugin.video.adulthideout/changelog.txt b/plugin.video.adulthideout/changelog.txt index 037b9f2..26cfee7 100644 --- a/plugin.video.adulthideout/changelog.txt +++ b/plugin.video.adulthideout/changelog.txt @@ -1,3 +1,7 @@ +v 0.9.81-beta8 30.09.2024 +- fixed Heavy_r (didn't work with Android) +- experimenting a lot + v 0.9.81-beta7 26.09.2024 - fixed Xhamster - fixed Uflash diff --git a/plugin.video.adulthideout/resources/search.py b/plugin.video.adulthideout/resources/search.py index 1ee60e4..a3cad01 100644 --- a/plugin.video.adulthideout/resources/search.py +++ b/plugin.video.adulthideout/resources/search.py @@ -3,8 +3,6 @@ import sys # Hinzufügen des Imports für sys import xbmcaddon from kodi_six import xbmc, xbmcgui, xbmcvfs -from .queries import get_all_queries -import importlib addon = xbmcaddon.Addon() @@ -63,7 +61,7 @@ def edit_query(): queries[selected_query] = new_query save_queries(queries) xbmcgui.Dialog().notification("Query Edited", "Search query edited successfully.", xbmcgui.NOTIFICATION_INFO, 3000) - xbmc.executebuiltin('Container.Refresh') # Aktuelle Ansicht aktualisieren + xbmc.executebuiltin('Container.Update({})'.format(sys.argv[0])) # Zurück zur Seite mit den Suchoptionen def save_queries(queries): file_path = get_queries_path() diff --git a/plugin.video.adulthideout/resources/websites/heavy_r.py b/plugin.video.adulthideout/resources/websites/heavy_r.py index 9173f8f..eaa3f07 100644 --- a/plugin.video.adulthideout/resources/websites/heavy_r.py +++ b/plugin.video.adulthideout/resources/websites/heavy_r.py @@ -1,20 +1,39 @@ import re +import sys +import threading +import os +import hashlib +import urllib.parse as urllib_parse +from urllib.parse import urlparse, urljoin +import urllib.request as urllib_request import xbmc import xbmcvfs -from ..functions import add_dir, add_link, make_request, fanart, logos import xbmcgui import xbmcplugin import xbmcaddon -import urllib.parse as urllib_parse -import logging -from urllib.parse import urlparse, urljoin -import subprocess -import shlex -import os -import xbmcvfs +from http.server import SimpleHTTPRequestHandler +import socketserver from kodi_six import xbmc, xbmcaddon +from ..functions import add_dir, add_link, make_request, fanart, logos + addon = xbmcaddon.Addon() +handle = int(sys.argv[1]) + +httpd = None + +def delete_downloaded_videos(): + folder_path = xbmcvfs.translatePath('special://home/addons/plugin.video.adulthideout/temp/') + + if xbmcvfs.exists(folder_path): + for filename in os.listdir(folder_path): + if filename.endswith(".mp4"): + file_path = os.path.join(folder_path, filename) + xbmcvfs.delete(file_path) + xbmc.log(f"Deleted file: {file_path}", xbmc.LOGINFO) + +delete_downloaded_videos() + def process_heavy_r_content(url, mode=None): if "search" not in url and "newest" not in url: @@ -28,13 +47,13 @@ def process_heavy_r_content(url, mode=None): match = re.compile('.+?Next').findall(content) - add_dir('[COLOR blue]Next Page >>>>[/COLOR]', base_url + match[0], 2, logos + 'heavy-r.png', fanart) + add_dir('[COLOR blue]Next Page >>>>[/COLOR]', urljoin(base_url, match[0]), 2, logos + 'heavy-r.png', fanart) except: pass @@ -45,36 +64,199 @@ def process_heavy_r_categories(url): categories = re.compile('.+?([^', re.DOTALL).findall(content) for video_url, thumb, name in categories: full_url = urljoin(base_url, video_url) - add_dir(name, full_url, 2, base_url + thumb, fanart) - + add_dir(name, full_url, 2, urljoin(base_url, thumb), fanart) def play_heavy_r_video(url): + xbmc.log(f"play_heavy_r_video aufgerufen mit URL: {url}", xbmc.LOGINFO) content = make_request(url) - media_url = re.compile('').findall(content)[0] - - curl_command = [ - "curl", media_url, - "-H", "sec-ch-ua: \"Not/A)Brand\";v=\"8\", \"Chromium\";v=\"126\", \"Google Chrome\";v=\"126\"", - "-H", "Referer: https://www.heavy-r.com/", - "-H", "sec-ch-ua-mobile: ?0", - "-H", "User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36", - "-H", "Range: bytes=0-", - "-H", "sec-ch-ua-platform: \"Windows\"", - "--output", "video.mp4" - ] - - profile = xbmcvfs.translatePath(addon.getAddonInfo('profile')) - curl_path = os.path.join(profile, 'curl') - if not os.path.exists(curl_path): - os.makedirs(curl_path) - - video_path = os.path.join(curl_path, 'video.mp4') - curl_command[-1] = video_path - - process = subprocess.run(curl_command, capture_output=True, text=True, shell=True) - - if process.returncode == 0: - return video_path + + media_url_match = re.compile('').findall(content) + if not media_url_match: + xbmc.log("Fehler: Keine Medien-URL auf der Seite gefunden.", xbmc.LOGERROR) + return + media_url = media_url_match[0] + + headers = { + "Referer": "https://www.heavy-r.com/", + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, wie Gecko)" + } + + video_path = download_video_with_urllib(media_url, headers) + if video_path: + server_thread, port = start_local_http_server(video_path, headers) + if port: + local_url = f'http://127.0.0.1:{port}/{os.path.basename(video_path)}' + play_video(local_url) + + monitor = xbmc.Monitor() + while not xbmc.Player().isPlaying(): + if monitor.waitForAbort(1): + break + + while xbmc.Player().isPlaying(): + if monitor.waitForAbort(1): + break + + stop_local_http_server() + else: + xbmc.log("Fehler: Konnte lokalen HTTP-Server nicht starten.", xbmc.LOGERROR) else: - xbmc.log(f"Error downloading video: {process.stderr}", level=xbmc.LOGERROR) + xbmc.log(f"Fehler: Konnte Video von {media_url} nicht herunterladen.", xbmc.LOGERROR) + +def download_video_with_urllib(media_url, headers): + addon_data_dir = xbmcvfs.translatePath(addon.getAddonInfo('path')) + folder_path = os.path.join(addon_data_dir, 'temp') + + if not xbmcvfs.exists(folder_path): + xbmcvfs.mkdirs(folder_path) + + filename_hash = hashlib.md5(media_url.encode('utf-8')).hexdigest() + video_filename = f"video_{filename_hash}.mp4" + video_path = os.path.join(folder_path, video_filename) + + if xbmcvfs.exists(video_path): + xbmc.log(f"Video bereits heruntergeladen: {video_path}", xbmc.LOGINFO) + return video_path + + request = urllib_request.Request(media_url, headers=headers) + + try: + with urllib_request.urlopen(request) as response: + with xbmcvfs.File(video_path, 'wb') as out_file: + while True: + data = response.read(1024 * 1024) # Lese in 1MB-Blöcken + if not data: + break + out_file.write(data) + xbmc.log(f"Video heruntergeladen: {video_path}", xbmc.LOGINFO) + return video_path + except Exception as e: + xbmc.log(f"Fehler beim Herunterladen des Videos: {e}", xbmc.LOGERROR) return None + +def start_local_http_server(file_path, headers): + import shutil + global httpd + + class ThreadingHTTPServer(socketserver.ThreadingMixIn, socketserver.TCPServer): + daemon_threads = True + allow_reuse_address = True + + class RangeRequestHandler(SimpleHTTPRequestHandler): + def __init__(self, *args, **kwargs): + self.directory = os.path.dirname(file_path) + self.headers_to_add = headers + super().__init__(*args, directory=self.directory, **kwargs) + + def end_headers(self): + for key, value in self.headers_to_add.items(): + self.send_header(key, value) + super().end_headers() + + def send_head(self): + path = self.translate_path(self.path) + if not os.path.exists(path): + self.send_error(404, "Datei nicht gefunden") + return None + + file_size = os.path.getsize(path) + ctype = self.guess_type(path) + range_header = self.headers.get('Range', None) + if range_header: + byte1, byte2 = 0, None + match = re.search(r'bytes=(\d+)-(\d*)', range_header) + if match: + byte1 = int(match.group(1)) + if match.group(2): + byte2 = int(match.group(2)) + else: + self.send_error(400, "Ungültiger Range-Header") + return None + + if byte2 is None or byte2 >= file_size: + byte2 = file_size - 1 + self.send_response(206) + self.send_header("Content-Type", ctype) + self.send_header("Content-Range", f"bytes {byte1}-{byte2}/{file_size}") + self.send_header("Content-Length", str(byte2 - byte1 + 1)) + self.send_header("Accept-Ranges", "bytes") + self.end_headers() + return open(path, 'rb'), byte1, byte2 + else: + self.send_response(200) + self.send_header("Content-Type", ctype) + self.send_header("Content-Length", str(file_size)) + self.send_header("Accept-Ranges", "bytes") + self.end_headers() + return open(path, 'rb'), None, None + + def do_HEAD(self): + result = self.send_head() + if result: + f, _, _ = result + f.close() + + def do_GET(self): + result = self.send_head() + if result: + f, start, end = result + try: + if start is not None and end is not None: + f.seek(start) + chunk_size = 1024 * 1024 # 1MB + bytes_to_send = end - start + 1 + while bytes_to_send > 0: + chunk = f.read(min(chunk_size, bytes_to_send)) + if not chunk: + break + self.wfile.write(chunk) + bytes_to_send -= len(chunk) + else: + shutil.copyfileobj(f, self.wfile) + finally: + f.close() + + def log_message(self, format, *args): + pass + + server_address = ('127.0.0.1', 0) + httpd = ThreadingHTTPServer(server_address, RangeRequestHandler) + port = httpd.server_address[1] + + def run_server(): + try: + httpd.serve_forever() + except Exception as e: + xbmc.log(f"Lokaler HTTP-Server fehlgeschlagen: {e}", xbmc.LOGERROR) + + server_thread = threading.Thread(target=run_server) + server_thread.start() + return server_thread, port + +def stop_local_http_server(): + global httpd + if httpd: + httpd.shutdown() + httpd = None + +def play_video(video_url): + listitem = xbmcgui.ListItem(path=video_url) + listitem.setProperty('IsPlayable', 'true') + xbmcplugin.setResolvedUrl(handle, True, listitem) + +if __name__ == '__main__': + delete_temp_folder() + params = dict(urllib_parse.parse_qsl(sys.argv[2][1:])) + url = params.get('url') + mode = params.get('mode') + + if mode is None: + process_heavy_r_content('https://www.heavy-r.com/videos/') + elif mode == '2': + process_heavy_r_content(url) + elif mode == '4': + play_heavy_r_video(url) + elif mode == '5': + pass + else: + process_heavy_r_content('https://www.heavy-r.com/videos/') diff --git a/zips/plugin.video.adulthideout/changelog-0.9.81-beta7.txt b/zips/plugin.video.adulthideout/changelog-0.9.81-beta8.txt similarity index 99% rename from zips/plugin.video.adulthideout/changelog-0.9.81-beta7.txt rename to zips/plugin.video.adulthideout/changelog-0.9.81-beta8.txt index 037b9f2..26cfee7 100644 --- a/zips/plugin.video.adulthideout/changelog-0.9.81-beta7.txt +++ b/zips/plugin.video.adulthideout/changelog-0.9.81-beta8.txt @@ -1,3 +1,7 @@ +v 0.9.81-beta8 30.09.2024 +- fixed Heavy_r (didn't work with Android) +- experimenting a lot + v 0.9.81-beta7 26.09.2024 - fixed Xhamster - fixed Uflash diff --git a/zips/plugin.video.adulthideout/changelog.txt b/zips/plugin.video.adulthideout/changelog.txt index 037b9f2..26cfee7 100644 --- a/zips/plugin.video.adulthideout/changelog.txt +++ b/zips/plugin.video.adulthideout/changelog.txt @@ -1,3 +1,7 @@ +v 0.9.81-beta8 30.09.2024 +- fixed Heavy_r (didn't work with Android) +- experimenting a lot + v 0.9.81-beta7 26.09.2024 - fixed Xhamster - fixed Uflash diff --git a/zips/plugin.video.adulthideout/plugin.video.adulthideout-0.9.81-beta7.zip b/zips/plugin.video.adulthideout/plugin.video.adulthideout-0.9.81-beta8.zip similarity index 98% rename from zips/plugin.video.adulthideout/plugin.video.adulthideout-0.9.81-beta7.zip rename to zips/plugin.video.adulthideout/plugin.video.adulthideout-0.9.81-beta8.zip index 504d2929134818536fe76f8ea405bb0e871040eb..6abcfdf3f0ea1f50f1b3016d5acc41c2f6f94779 100644 GIT binary patch delta 8809 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 delta 7018 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