diff --git a/plugins/setSceneCoverFromFile/log.py b/plugins/setSceneCoverFromFile/log.py deleted file mode 100644 index e3d6aae..0000000 --- a/plugins/setSceneCoverFromFile/log.py +++ /dev/null @@ -1,52 +0,0 @@ -import sys - - -# Log messages sent from a plugin instance are transmitted via stderr and are -# encoded with a prefix consisting of special character SOH, then the log -# level (one of t, d, i, w, e, or p - corresponding to trace, debug, info, -# warning, error and progress levels respectively), then special character -# STX. -# -# The LogTrace, LogDebug, LogInfo, LogWarning, and LogError methods, and their equivalent -# formatted methods are intended for use by plugin instances to transmit log -# messages. The LogProgress method is also intended for sending progress data. -# - -def __prefix(level_char): - start_level_char = b'\x01' - end_level_char = b'\x02' - - ret = start_level_char + level_char + end_level_char - return ret.decode() - - -def __log(level_char, s): - if level_char == "": - return - - print(__prefix(level_char) + s + "\n", file=sys.stderr, flush=True) - - -def trace(s): - __log(b't', s) - - -def debug(s): - __log(b'd', s) - - -def info(s): - __log(b'i', s) - - -def warning(s): - __log(b'w', s) - - -def error(s): - __log(b'e', s) - - -def progress(p): - progress = min(max(0, p), 1) - __log(b'p', str(progress)) diff --git a/plugins/setSceneCoverFromFile/set_cover.py b/plugins/setSceneCoverFromFile/set_cover.py index 321b55b..ddb7be2 100644 --- a/plugins/setSceneCoverFromFile/set_cover.py +++ b/plugins/setSceneCoverFromFile/set_cover.py @@ -4,8 +4,13 @@ import sys import json import base64 -import log -from stash_interface import StashInterface +try: + import stashapi.log as log + from stashapi.tools import file_to_base64 + from stashapi.stashapp import StashInterface +except ModuleNotFoundError: + print("You need to install the stashapi module. (pip install stashapp-tools)", + file=sys.stderr) MANUAL_ROOT = None # /some/other/path to override scanning all stashes cover_pattern = r'(?:thumb|poster|cover)\.(?:jpg|png)' @@ -21,7 +26,7 @@ def main(): if MANUAL_ROOT: scan(MANUAL_ROOT, handle_cover) else: - for stash_path in stash.get_root_paths(): + for stash_path in get_stash_paths(): scan(stash_path, handle_cover) except Exception as e: log.error(e) @@ -34,30 +39,32 @@ def handle_cover(path, file): filepath = os.path.join(path, file) - with open(filepath, "rb") as img: - b64img_bytes = base64.b64encode(img.read()) - - if not b64img_bytes: + b64img = file_to_base64(filepath) + if not b64img: + log.warning(f"Could not parse {filepath} to b64image") return - - b64img = f"data:image/jpeg;base64,{b64img_bytes.decode('utf-8')}" - scene_ids = stash.get_scenes_id(filter={ + scenes = stash.find_scenes(f={ "path": { "modifier": "INCLUDES", "value": f"{path}\"" } - }) + }, fragment="id") - log.info(f'Found Cover: {[int(s) for s in scene_ids]}|{filepath}') + log.info(f'Found Cover: {[int(s["id"]) for s in scenes]}|{filepath}') if mode_arg == "set_cover": - for scene_id in scene_ids: + for scene in scenes: stash.update_scene({ - "id": scene_id, + "id": scene["id"], "cover_image": b64img }) - log.info(f'Applied cover Scenes') + log.info(f'Applied cover to {len(scenes)} scenes') + +def get_stash_paths(): + config = stash.get_configuration("general { stashes { path excludeVideo } }") + stashes = config["configuration"]["general"]["stashes"] + return [s["path"] for s in stashes if not s["excludeVideo"]] def scan(ROOT_PATH, _callback): log.info(f'Scanning {ROOT_PATH}') @@ -66,4 +73,5 @@ def scan(ROOT_PATH, _callback): if re.match(cover_pattern, file, re.IGNORECASE): _callback(root, file) -main() \ No newline at end of file +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/plugins/setSceneCoverFromFile/set_scene_cover.yml b/plugins/setSceneCoverFromFile/set_scene_cover.yml index d11ae41..f622f35 100644 --- a/plugins/setSceneCoverFromFile/set_scene_cover.yml +++ b/plugins/setSceneCoverFromFile/set_scene_cover.yml @@ -1,6 +1,6 @@ name: Set Scene Cover -description: Searchs Stash for Scenes with a cover image in the same folder and sets the cover image in stash to that image -version: 0.3 +description: searches Stash for Scenes with a cover image in the same folder and sets the cover image in stash to that image +version: 0.4 url: https://github.com/stg-annon/CommunityScripts/tree/main/plugins/setSceneCoverFromFile exec: - python @@ -8,7 +8,7 @@ exec: interface: raw tasks: - name: Scan - description: searchs stash dirs for cover images and logs results + description: searches stash dirs for cover images and logs results defaultArgs: mode: scan - name: Set Cover diff --git a/plugins/setSceneCoverFromFile/stash_interface.py b/plugins/setSceneCoverFromFile/stash_interface.py deleted file mode 100644 index 1223f40..0000000 --- a/plugins/setSceneCoverFromFile/stash_interface.py +++ /dev/null @@ -1,137 +0,0 @@ -import requests -import sys -import re -import log - - -class StashInterface: - port = "" - url = "" - headers = { - "Accept-Encoding": "gzip, deflate, br", - "Content-Type": "application/json", - "Accept": "application/json", - "Connection": "keep-alive", - "DNT": "1" - } - cookies = {} - - def __init__(self, conn, fragments={}): - self.port = conn['Port'] - scheme = conn['Scheme'] - - # Session cookie for authentication - self.cookies = { - 'session': conn.get('SessionCookie').get('Value') - } - - domain = conn.get('Domain') if conn.get('Domain') else 'localhost' - - # Stash GraphQL endpoint - self.url = scheme + "://" + domain + ":" + str(self.port) + "/graphql" - log.debug(f"Using stash GraphQl endpoint at {self.url}") - - self.fragments = fragments - self.fragments.update(stash_gql_fragments) - - def __resolveFragments(self, query): - - fragmentRefrences = list(set(re.findall(r'(?<=\.\.\.)\w+', query))) - fragments = [] - for ref in fragmentRefrences: - fragments.append({ - "fragment": ref, - "defined": bool(re.search("fragment {}".format(ref), query)) - }) - - if all([f["defined"] for f in fragments]): - return query - else: - for fragment in [f["fragment"] for f in fragments if not f["defined"]]: - if fragment not in self.fragments: - raise Exception(f'GraphQL error: fragment "{fragment}" not defined') - query += self.fragments[fragment] - return self.__resolveFragments(query) - - def __callGraphQL(self, query, variables=None): - - query = self.__resolveFragments(query) - - json = {'query': query} - if variables is not None: - json['variables'] = variables - - response = requests.post(self.url, json=json, headers=self.headers, cookies=self.cookies) - - if response.status_code == 200: - result = response.json() - if result.get("error", None): - for error in result["error"]["errors"]: - raise Exception("GraphQL error: {}".format(error)) - if result.get("data", None): - return result.get("data") - elif response.status_code == 401: - sys.exit("HTTP Error 401, Unauthorised. Cookie authentication most likely failed") - else: - raise ConnectionError( - "GraphQL query failed:{} - {}. Query: {}. Variables: {}".format( - response.status_code, response.content, query, variables) - ) - - - - def get_scenes_id(self, filter={}): - query = """ - query FindScenes($filter: FindFilterType, $scene_filter: SceneFilterType, $scene_ids: [Int!]) { - findScenes(filter: $filter, scene_filter: $scene_filter, scene_ids: $scene_ids) { - count - scenes { - id - } - } - } - """ - variables = { - "filter": { "per_page": -1 }, - "scene_filter": filter - } - - result = self.__callGraphQL(query, variables) - scene_ids = [s["id"] for s in result.get('findScenes').get('scenes')] - - return scene_ids - - def update_scene(self, scene_data): - query = """ - mutation SceneUpdate($input:SceneUpdateInput!) { - sceneUpdate(input: $input) { - id - } - } - """ - variables = {'input': scene_data} - - result = self.__callGraphQL(query, variables) - return result["sceneUpdate"]["id"] - - def get_root_paths(self): - query = """ - query Configuration { - configuration { - general{ - stashes{ - path - excludeVideo - } - } - } - } - """ - result = self.__callGraphQL(query) - - stashes = result["configuration"]["general"]["stashes"] - paths = [s["path"] for s in stashes if not s["excludeVideo"]] - - return paths - -stash_gql_fragments = {} \ No newline at end of file