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.
This commit is contained in:
tooliload
2024-06-24 20:47:10 +02:00
committed by GitHub
parent b339766c8a
commit eb09dbd72b
6 changed files with 385 additions and 0 deletions

View File

@@ -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()

View File

@@ -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

View File

@@ -0,0 +1 @@
stashapp-tools>=0.2.33

View File

@@ -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()

View File

@@ -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

View File

@@ -0,0 +1 @@
stashapp-tools