mirror of
https://github.com/stashapp/CommunityScripts.git
synced 2026-04-20 11:06:18 -05:00
update Set Scene Cover to use stashapp-tools
issue 110 addressed within stashapp-tools flag
This commit is contained in:
@@ -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))
|
||||
@@ -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()
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -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
|
||||
|
||||
@@ -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 = {}
|
||||
Reference in New Issue
Block a user