From eb09dbd72b8b13c640d59c572f492fa081cdca76 Mon Sep 17 00:00:00 2001 From: tooliload Date: Mon, 24 Jun 2024 20:47:10 +0200 Subject: [PATCH] Add plugin Adulttime Interactive Downloader (#240) * Create requirements.txt * Add files via upload * Create empty * Yarned Files modified: plugins/AdulttimeInteractiveDL/AdulttimeInteractiveDL.py modified: plugins/AdulttimeInteractiveDL/AdulttimeInteractiveDL.yml deleted: plugins/AdulttimeInteractiveDL/cache/empty modified: plugins/AdulttimeInteractiveDL/requirements.txt * - Added Return on Error with stashapi modified: plugins/AdulttimeInteractiveDL/AdulttimeInteractiveDL.py - Add new Plugin "PythonToolsInstaller" for Docker ENV, to create VENV with stashtools to use with other Plugins new file: plugins/PythonToolsInstaller/PythonToolsInstaller.py new file: plugins/PythonToolsInstaller/PythonToolsInstaller.yml new file: plugins/PythonToolsInstaller/packages/stashtools.txt Files yarned. --- .../AdulttimeInteractiveDL.py | 278 ++++++++++++++++++ .../AdulttimeInteractiveDL.yml | 22 ++ .../AdulttimeInteractiveDL/requirements.txt | 1 + .../PythonToolsInstaller.py | 70 +++++ .../PythonToolsInstaller.yml | 13 + .../packages/stashtools.txt | 1 + 6 files changed, 385 insertions(+) create mode 100644 plugins/AdulttimeInteractiveDL/AdulttimeInteractiveDL.py create mode 100644 plugins/AdulttimeInteractiveDL/AdulttimeInteractiveDL.yml create mode 100644 plugins/AdulttimeInteractiveDL/requirements.txt create mode 100644 plugins/PythonToolsInstaller/PythonToolsInstaller.py create mode 100644 plugins/PythonToolsInstaller/PythonToolsInstaller.yml create mode 100644 plugins/PythonToolsInstaller/packages/stashtools.txt diff --git a/plugins/AdulttimeInteractiveDL/AdulttimeInteractiveDL.py b/plugins/AdulttimeInteractiveDL/AdulttimeInteractiveDL.py new file mode 100644 index 0000000..aebae84 --- /dev/null +++ b/plugins/AdulttimeInteractiveDL/AdulttimeInteractiveDL.py @@ -0,0 +1,278 @@ +import requests, os, shutil +import re, sys, json +import datetime as dt +import pathlib +import time +from inspect import getmembers, isfunction + +CurRunDir = pathlib.Path(__file__).parent.resolve() +vENVDir = f"{CurRunDir}/../PythonToolsInstaller/venv/lib/python3.11/site-packages" + +try: + if os.path.isdir(vENVDir): + print(f"VENV Dir {vENVDir} used", file=sys.stderr) + sys.path.insert(0, vENVDir) + else: + print(f"VENV Dir {vENVDir} not used", file=sys.stderr) + +except Exception as e: + # Ignore + print("Hey there...") + +try: + import stashapi.log as log + from stashapi.tools import human_bytes, human_bits + from stashapi.stash_types import PhashDistance + from stashapi.stashapp import StashInterface + +except ModuleNotFoundError: + print( + "You need to install the stashapi module. (pip install stashapp-tools)", + file=sys.stderr, + ) + return + +FAKTORCONV = 6.25 +FRAGMENT = json.loads(sys.stdin.read()) +# MODE = FRAGMENT['args']['mode'] +MODE = FRAGMENT["args"].get("mode") +PLUGIN_DIR = FRAGMENT["server_connection"]["PluginDir"] +stash = StashInterface(FRAGMENT["server_connection"]) + +SLIM_SCENE_FRAGMENT = """ +id +title +url +urls +details +date +tags { id } +studio{ + name + stash_ids{ + endpoint + stash_id + } +} +files { + size + path + width + height + bit_rate + mod_time + duration + frame_rate + video_codec +} +""" + + +def main(): + + log.info(f"Plugin Dir {PLUGIN_DIR} ") + cachepath = os.path.join(PLUGIN_DIR, "cache") + + try: + os.makedirs(cachepath, exist_ok=True) + print("Directory '%s' created successfully" % cachepath) + except OSError as error: + print("Directory '%s' can not be created" % cachepath) + + if MODE: + if MODE == "download": + get_download() + if MODE == "disable": + return true + else: + FRAGMENT_HOOK_TYPE = FRAGMENT["args"]["hookContext"]["type"] + FRAGMENT_SCENE_ID = FRAGMENT["args"]["hookContext"]["id"] + try: + get_download() # ToDo use single Scene + except Exception as err: + log.LogError(f"main function error: {err}") + traceback.print_exc() + + log.exit("Plugin exited normally.") + + +def parse_timestamp(ts, format="%Y-%m-%dT%H:%M:%S%z"): + ts = re.sub(r"\.\d+", "", ts) # remove fractional seconds + return dt.datetime.strptime(ts, format) + + +def get_download(): + + cachepath = os.path.join(PLUGIN_DIR, "cache") + log.info(f"Plugin Cachepath {cachepath} ") + + # adulttime.com + # jerkbuddies.com + # adulttime.studio + # oopsie.tube + # adulttimepilots.com + # kissmefuckme.net + # youngerloverofmine.com + # dareweshare.net + # milfoverload.net + # getupclose.com + # https://switch.com/en/video/switch/Switch-Awakening/234167 + # https://howwomenorgasm.com/en/video/howwomenorgasm/How-Women-Orgasm---Lumi-Ray/230015 + + scene_count, scenes = stash.find_scenes( + # f={"url": {"modifier": "INCLUDES", "value": "adulttime.com"}}, + f={ + "url": { + "modifier": "MATCHES_REGEX", + "value": "howwomenorgasm\\.com|switch\\.com|getupclose\\.com|milfoverload\\.net|dareweshare\\.net|jerkbuddies\\.com|adulttime\\.studio|adulttime\\.com|oopsie\\.tube|adulttimepilots\\.com|kissmefuckme\\.net|youngerloverofmine\\.com", + } + }, + fragment=SLIM_SCENE_FRAGMENT, + get_count=True, + ) + + log.info(f"Plugin found {scene_count} Scenes from Adulttime ") + + i = 0 + for i, scene in enumerate(scenes): + title = re.sub(r"\[PDT: .+?\]\s+", "", scene["title"]) + url = scene["url"] + urls = scene["urls"] + + for u in urls: + # if re.search(r"members\.adulttime\.com", u): + if re.search(r"\.adulttime\.com", u): + aid = re.search(r"\/([0-9]+)", u) + aid = aid.group(1) + fpw = f"{cachepath}/{aid}.json" + fppatw = f"{cachepath}/{aid}.pat" + fpfunw = f"{cachepath}/{aid}.funscript" + log.debug(f"Found Adulttime URL {u} width Provider ID {aid}") + + # Try to DL or open from Cache + if os.path.isfile(fpw) == False: + dlurl = f"https://coll.lovense.com/coll-log/video-websites/get/pattern?videoId={aid}&pf=Adulttime" + r = requests.get(dlurl, allow_redirects=True) + log.debug(r.content) + dlapires = json.loads(r.content) + open(fpw, "w+").write(r.content.decode("utf-8")) + else: + + with open(fpw, "r") as f: + dlapires = json.load(f) + + try: + + if dlapires["code"] == 0: + log.info(f"Try Interactive for this ID") + + if os.path.isfile(fppatw) == False: + dlpaturl = dlapires["data"]["pattern"] + rpat = requests.get(dlpaturl, allow_redirects=True) + open(fppatw, "w+").write( + rpat.content.decode("utf-8") + ) + + if os.path.isfile(fpfunw) == False: + convert_lovense_to_funscript( + scene, fppatw, fpfunw + ) + + map_file_with_funscript(scene, fpfunw) + + else: + log.debug(f"No Interactive for this ID") + + except KeyError as error: + log.error( + "File '%s' can not be read, invailed format" % fpw + ) + fullfile = json.dumps(dlapires) + if re.search("Too many requests", fullfile) or re.search( + "security", fullfile + ): + os.remove(fpw) + log.error("Too many requests. Wait a moment...") + time.sleep(60) + + log.progress(i / scene_count) + i = i + 1 + + +def map_file_with_funscript(sceneinfo, funscriptfile): + + scenefiles = sceneinfo["files"] + for u in scenefiles: + filepath = os.path.dirname(os.path.abspath(u["path"])) + filename = os.path.basename(u["path"]) + filenamewithoutext = filename.rsplit(".", maxsplit=1)[0] + funscriptnewname = f"{filenamewithoutext}.funscript" + funscriptnewlocaton = os.path.join(filepath, funscriptnewname) + + shutil.copy2(funscriptfile, funscriptnewlocaton) + + log.info(f"Copy {funscriptfile} to {funscriptnewlocaton}") + # log.info(filename) + # log.info(filenamewithoutext) + + +def convert_lovense_to_funscript(sceneinfo, patternfile, funscriptfile): + + # Sceneninfo + title = re.sub(r"\[PDT: .+?\]\s+", "", sceneinfo["title"]) + duration = int(sceneinfo["files"][0]["duration"] + 0.5) * 1000 + + # Lovensescript + with open(patternfile, "r") as losc: + lovensactions = json.load(losc) + + # Funscript-Output + data = {} + data["version"] = "1.0" + data["range"] = 100 + data["inverted"] = False + data["metadata"] = {} + data["metadata"]["bookmarks"] = {} + data["metadata"]["chapters"] = {} + data["metadata"]["performers"] = {} + data["metadata"]["tags"] = {} + data["metadata"]["title"] = title + data["metadata"]["creator"] = "Adulttime Interactive Downloader for Stash" + data["metadata"]["description"] = "" + data["metadata"]["duration"] = duration + data["metadata"]["license"] = "Open" + data["metadata"]["script_url"] = "" + data["metadata"]["type"] = "basic" + data["metadata"]["video_url"] = "" + data["metadata"]["notes"] = "Convert from Lovense to Funscript" + data["actions"] = [] + + marker_at = 0 + marker_pos = 0 + for la in lovensactions: + + # 0 nicht konvertieren + if la["v"] == 0: + marker_at = 0 + else: + marker_at = la["v"] * FAKTORCONV + + # Division durch 0 nicht moeglich + if la["t"] == 0: + print("Skip Junk with Value '%s' " % la["t"]) + else: + # marker_pos = (la["t"] / 1000)*1 + marker_pos = (la["t"]) * 1 + data["actions"].extend( + [{"pos": int(marker_at + 0.5), "at": int(marker_pos + 0.5)}] + ) + + # json_data = json.dumps(data) + # log.debug(json_data) + + # Funscript schreiben + open(funscriptfile, "w+").write(json.dumps(data)) + + +if __name__ == "__main__": + main() diff --git a/plugins/AdulttimeInteractiveDL/AdulttimeInteractiveDL.yml b/plugins/AdulttimeInteractiveDL/AdulttimeInteractiveDL.yml new file mode 100644 index 0000000..f738d61 --- /dev/null +++ b/plugins/AdulttimeInteractiveDL/AdulttimeInteractiveDL.yml @@ -0,0 +1,22 @@ +name: "Adulttime Interactive Downloader" +description: Download Interactive Files for Adulttime Scenes +version: 0.1.1 +url: https://github.com/tooliload/StashAppCommunityScripts/tree/main/plugins/AdulttimeInteractiveDL +exec: + - python + - "{pluginDir}/AdulttimeInteractiveDL.py" +interface: raw +tasks: + - name: "Download" + description: "Download Interactive Files from Adulttime Scenes" + defaultArgs: + mode: download + - name: "Clear Cache" + description: "Clean cached JSON Answers and Interactive Cache" + defaultArgs: + mode: cacheclean +hooks: + - name: download + description: Try download if Scene updated + triggeredBy: + - Scene.Update.Post diff --git a/plugins/AdulttimeInteractiveDL/requirements.txt b/plugins/AdulttimeInteractiveDL/requirements.txt new file mode 100644 index 0000000..cfcc685 --- /dev/null +++ b/plugins/AdulttimeInteractiveDL/requirements.txt @@ -0,0 +1 @@ +stashapp-tools>=0.2.33 \ No newline at end of file diff --git a/plugins/PythonToolsInstaller/PythonToolsInstaller.py b/plugins/PythonToolsInstaller/PythonToolsInstaller.py new file mode 100644 index 0000000..a49b5dc --- /dev/null +++ b/plugins/PythonToolsInstaller/PythonToolsInstaller.py @@ -0,0 +1,70 @@ +import requests, os, shutil +import re, sys, json, time, sysconfig +from inspect import getmembers, isfunction +from venv import create +from os.path import join, expanduser, abspath +import subprocess +import shutil + +def main(): + input = None + + if len(sys.argv) < 2: + input = readJSONInput() + + output = {} + run(input, output) + + out = json.dumps(output) + print(out + "\n") + +def readJSONInput(): + input = sys.stdin.read() + return json.loads(input) + + +def run(input, output): + + if input == "None" or input == "": + return + + + PLUGIN_DIR = input["server_connection"]["PluginDir"] + modeArg = input['args']["mode"] + + try: + if modeArg == "" or modeArg == "add": + return + + elif modeArg == "process_py_stashapi_tools": + get_download_py_stashapp_tools(PLUGIN_DIR) + + except Exception as e: + raise + output["error"] = str(e) + return + + output["output"] = "ok" + +def get_download_py_stashapp_tools(PLUGIN_DIR): + + org_packagedir = sysconfig.get_paths()["purelib"] # /usr/lib/python3.11/site-packages/usr/lib/python3.11/site-packages + + used_dir = f"{PLUGIN_DIR}" + create(f"{used_dir}/venv/", with_pip=True) + + # where requirements.txt is in same dir as this script + subprocess.run([f"{used_dir}/venv/bin/pip", "install", "-r", abspath(f"{used_dir}/packages/stashtools.txt")],stdout=None) + + # venv/lib/python3.11/site-packages/stashapp_tools- + + src = f"{used_dir}/venv/lib/python3.11/site-packages" + destination = shutil.copytree(src, org_packagedir,ignore_func,None,shutil.copy2,False,True) + fp = open(f'{used_dir}/copydo.txt', 'w+') + fp.write("%s\n" % print(destination)) + +def ignore_func(src, names): + return ['env'] if 'env' in names else [] + + +main() diff --git a/plugins/PythonToolsInstaller/PythonToolsInstaller.yml b/plugins/PythonToolsInstaller/PythonToolsInstaller.yml new file mode 100644 index 0000000..1e1b658 --- /dev/null +++ b/plugins/PythonToolsInstaller/PythonToolsInstaller.yml @@ -0,0 +1,13 @@ +name: "Python Tools Installer" +description: Download stashapp-tools for DockerEnv +version: 0.1. +url: https://github.com/tooliload/StashAppCommunityScripts/tree/main/plugins/PythonToolsInstaller +exec: + - python + - "{pluginDir}/PythonToolsInstaller.py" +interface: raw +tasks: + - name: "Install" + description: Install Python Module stashapi-tools + defaultArgs: + mode: process_py_stashapi_tools diff --git a/plugins/PythonToolsInstaller/packages/stashtools.txt b/plugins/PythonToolsInstaller/packages/stashtools.txt new file mode 100644 index 0000000..e0fcf02 --- /dev/null +++ b/plugins/PythonToolsInstaller/packages/stashtools.txt @@ -0,0 +1 @@ +stashapp-tools