# most of this copied from https://github.com/niemands/StashPlugins import requests import sys import log class IdentifierStashInterface: port = "" url = "" headers = { "Accept-Encoding": "gzip, deflate, br", "Content-Type": "application/json", "Accept": "application/json", "Connection": "keep-alive", "DNT": "1" } cookies = {} # # Init # def __init__(self, conn): self.port = conn['Port'] scheme = conn['Scheme'] # Session cookie for authentication self.cookies = { 'session': conn.get('SessionCookie').get('Value') } try: # If stash does not accept connections from all interfaces use the host specified in the config host = conn.get('Host') if '0.0.0.0' not in conn.get('Host') or '' else 'localhost' except TypeError: # Pre stable 0.8 host = 'localhost' # Stash GraphQL endpoint self.url = scheme + "://" + host + ":" + str(self.port) + "/graphql" log.LogDebug(f"Using stash GraphQl endpoint at {self.url}") def __callGraphQL(self, query, variables=None): 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) ) # # Queries # # Performers def getPerformerImages(self, performer_filter=None): return self.__getPerformerImages(performer_filter) def __getPerformerImages(self, performer_filter=None, page=1): per_page = 1000 query = """ query($per_page: Int, $page: Int, $performer_filter: PerformerFilterType) { findPerformers( performer_filter: $performer_filter filter: { per_page: $per_page, page: $page } ) { count performers { id name image_path } } } """ variables = { 'per_page': per_page, 'page': page } if performer_filter: variables['performer_filter'] = performer_filter result = self.__callGraphQL(query, variables) performers = result.get('findPerformers').get('performers') if len(performers) == per_page: next_page = self.__getPerformerImages(performer_filter, page + 1) for performer in next_page: performers.append(performer) return performers # Tags def findTagIdWithName(self, name): query = """ query($name: String!) { findTags( tag_filter: { name: {value: $name, modifier: EQUALS} } ){ tags{ id name } } } """ variables = { 'name': name, } result = self.__callGraphQL(query, variables) if result.get('findTags') is not None and result.get('findTags').get('tags') != []: return result.get('findTags').get('tags')[0].get('id') return None # Images def findImages(self, image_filter=None): return self.__findImages(image_filter) def __findImages(self, image_filter=None, page=1): per_page = 1000 query = """ query($per_page: Int, $page: Int, $image_filter: ImageFilterType) { findImages( image_filter: $image_filter, filter: { per_page: $per_page, page: $page } ) { count images { id path performers { id } } } } """ variables = { 'per_page': per_page, 'page': page } if image_filter: variables['image_filter'] = image_filter result = self.__callGraphQL(query, variables) images = result.get('findImages').get('images') if len(images) == per_page: next_page = self.__findImages(image_filter, page + 1) for image in next_page: images.append(image) return images # Scenes def getScenePaths(self, scene_filter=None): return self.__getScenePaths(scene_filter) def __getScenePaths(self, scene_filter=None, page=1): per_page = 1000 query = """ query($per_page: Int, $page: Int, $scene_filter: SceneFilterType) { findScenes( scene_filter: $scene_filter, filter: { per_page: $per_page, page: $page } ) { count scenes { id paths { screenshot stream } } } } """ variables = { 'per_page': per_page, 'page': page } if scene_filter: variables['scene_filter'] = scene_filter result = self.__callGraphQL(query, variables) scenes = result.get('findScenes').get('scenes') if len(scenes) == 1000: next_page = self.__getScenePaths(scene_filter, page + 1) for scene in next_page: scenes.append(scene) return scenes # # Mutations # def createTagWithName(self, name): query = """ mutation tagCreate($input:TagCreateInput!) { tagCreate(input: $input){ id } } """ variables = {'input': { 'name': name }} result = self.__callGraphQL(query, variables) if result.get('tagCreate'): log.LogDebug(f"Created tag: {name}") return result.get('tagCreate').get("id") else: log.LogError(f"Could not create tag: {name}") return None def updateImage(self, image_data): query = """ mutation($input: ImageUpdateInput!) { imageUpdate(input: $input) { id } } """ variables = {'input': image_data} self.__callGraphQL(query, variables) def addPerformersToImage(self, image_id, performer_ids): self.updateImage({ 'id': image_id, 'performer_ids': performer_ids }) def bulkPerformerAddTags(self, performer_ids, tag_ids): query = """ mutation($ids: [ID!], $tag_ids: BulkUpdateIds) { bulkPerformerUpdate(input: { ids: $ids, tag_ids: $tag_ids }) { id } } """ variables = { "ids": performer_ids, "tag_ids": { "ids": tag_ids, "mode": 'ADD' } } self.__callGraphQL(query, variables) def addPerformersToScene(self, scene_id, performer_ids): query = """ mutation BulkSceneUpdate($ids: [ID!], $performer_ids: BulkUpdateIds) { bulkSceneUpdate(input: { ids: $ids, performer_ids: $performer_ids}) { id } } """ variables = { "ids": [scene_id], "performer_ids": { "ids": performer_ids, "mode": "ADD" } } self.__callGraphQL(query, variables)