1- import requests
2- import time
1+ import sys
2+ import json
33import random
4+ import time
5+ import requests
46import stashapi .log as log
7+ from stashapi .stashapp import StashInterface
58
6- # GraphQL endpoint URL
7- endpoint_url = "http://localhost:9999/graphql"
8-
9- # GraphQL query to retrieve tags with a marker count greater than 0
10- tags_with_markers_query = """
11- query findTags {
12- findTags(tag_filter: {marker_count: {modifier: GREATER_THAN, value: 0} }, filter: {per_page: -1}) {
13- tags{
14- id
15- name
16- image_path
17- scene_marker_count
18- }
19- }
20- }
21- """
22-
23- # GraphQL query to find scene markers by tag id
24- find_markers_tag_id_query = """
25- query find_Markers_tag_id ($tag_id: ID!) {
26- findSceneMarkers(
27- filter: { per_page: -1 },
28- scene_marker_filter: {
29- tags: {
30- value: [$tag_id],
31- modifier: INCLUDES
32- }
33- }
34- ){
35- scene_markers {
36- id
37- stream
38- title
39- primary_tag { id }
40- }
41- }
42- }
43- """
44-
45- # GraphQL mutation to update tag image
46- tag_update_mutation = """
47- mutation tagUpdate($id: ID!, $image: String!) {
48- tagUpdate(input: { id: $id, image: $image }) {
49- id
50- }
51- }
52- """
53-
54- def fetch_graphql_data (query , variables = None ):
9+ def main ():
10+ # 1. READ CONFIG FROM STASH
5511 try :
56- response = requests .post (endpoint_url , json = {'query' : query , 'variables' : variables })
57- response .raise_for_status () # Raise an exception for non-2xx responses
58- data = response .json ()
59- if "errors" in data :
60- for error in data ["errors" ]:
61- log .error (f"GraphQL Error: { error .get ('message' )} " )
62- return None
63- return data
64- except Exception as e :
65- log .error (f"Error fetching GraphQL data: { e } " )
66- return None
12+ # Read the JSON configuration passed by Stash
13+ input_data = json .loads (sys .stdin .read ())
14+ server_info = input_data .get ("server_connection" , {})
6715
68-
69- def update_tag_image (tag_id , stream ):
70- variables = {"id" : tag_id , "image" : stream }
71- try :
72- response = requests .post (endpoint_url , json = {'query' : tag_update_mutation , 'variables' : variables })
73- response .raise_for_status ()
74- return response .json ()
75- except Exception as e :
76- log .error (f"Error updating tag image: { e } " )
77- return None
16+ # Automatically get the base URL and API Key from Stash itself
17+ base_url = f"{ server_info .get ('Scheme' , 'http' )} ://{ server_info .get ('Host' , 'localhost' )} :{ server_info .get ('Port' , 9999 )} "
18+ api_key = server_info .get ('ApiKey' , '' )
7819
79- def calculate_eta (total_tags , total_markers , current_tag_index , current_marker_index , start_time ):
80- tags_remaining = total_tags - current_tag_index
81- markers_remaining = total_markers - current_marker_index
82- total_remaining = tags_remaining + markers_remaining
83- elapsed_time = time .time () - start_time
84- if total_remaining == 0 :
85- return 0
86- avg_time_per_item = elapsed_time / (total_tags + total_markers )
87- eta = avg_time_per_item * total_remaining
88- return int (eta )
89-
90- def main ():
91- # Fetch tags with marker count greater than 0
92- tags_data = fetch_graphql_data (tags_with_markers_query )
93- if not tags_data :
20+ # Initialize the official Stash Interface
21+ stash = StashInterface (server_info )
22+ except Exception as e :
23+ log .error (f"Failed to initialize plugin connection: { e } " )
9424 return
95-
96- tags = tags_data .get ("data" , {}).get ("findTags" , {}).get ("tags" , [])
97- total_tags = len (tags )
98- total_markers = 0
99-
100- # Determine total number of markers
101- for tag in tags :
102- tag_id = tag .get ("id" )
103- scene_markers_data = fetch_graphql_data (find_markers_tag_id_query , variables = {"tag_id" : tag_id })
104- if scene_markers_data :
105- scene_markers = scene_markers_data .get ("data" , {}).get ("findSceneMarkers" , {}).get ("scene_markers" , [])
106- total_markers += len (scene_markers )
10725
108- # Initialize progress variables
109- processed_tags = 0
110- processed_markers = 0
26+ endpoint_url = f"{ base_url } /graphql"
27+ headers = {'Content-Type' : 'application/json' }
28+ if api_key :
29+ headers ['ApiKey' ] = api_key
30+
31+ # Helper function for GraphQL
32+ def call_gql (query , variables = None ):
33+ try :
34+ response = requests .post (endpoint_url , json = {'query' : query , 'variables' : variables }, headers = headers , timeout = 10 )
35+ return response .json ().get ("data" , {})
36+ except Exception as e :
37+ log .error (f"GQL Error: { e } " )
38+ return {}
39+
40+ # 2. START PROCESSING
41+ start_time = time .time ()
42+
43+ tags_query = """
44+ query { findTags(tag_filter: {marker_count: {modifier: GREATER_THAN, value: 0}}, filter: {per_page: -1}) {
45+ tags { id name }
46+ }}
47+ """
48+ tags_data = call_gql (tags_query )
49+ tags = tags_data .get ("findTags" , {}).get ("tags" , [])
11150
112- # Loop through tags
113- for tag_index , tag in enumerate (tags , 1 ):
114- tag_id = tag .get ("id" )
115- tag_name = tag .get ("name" )
51+ if not tags :
52+ log .info ("No tags with markers found." )
53+ return
54+
55+ total = len (tags )
56+ log .info (f"Processing { total } tags via plugin..." )
57+
58+ for idx , tag in enumerate (tags , 1 ):
59+ tag_id = tag ['id' ]
11660
117- # Search for scene markers by tag id
118- scene_markers_data = fetch_graphql_data (find_markers_tag_id_query , variables = {"tag_id" : tag_id })
61+ # Find markers for this tag
62+ marker_query = """
63+ query($id: ID!) { findSceneMarkers(scene_marker_filter: {tags: {value: [$id], modifier: INCLUDES}}, filter: {per_page: -1}) {
64+ scene_markers { id scene { id } }
65+ }}
66+ """
67+ markers_data = call_gql (marker_query , {"id" : tag_id })
68+ markers = markers_data .get ("findSceneMarkers" , {}).get ("scene_markers" , [])
11969
120- if scene_markers_data :
121- scene_markers = scene_markers_data .get ("data" , {}).get ("findSceneMarkers" , {}).get ("scene_markers" , [])
122-
123- # Find a random scene marker stream URL
124- random_url = None
70+ if markers :
71+ marker = random .choice (markers )
72+ # Use the dynamic base_url
73+ stream_url = f"{ base_url } /scene/{ marker ['scene' ]['id' ]} /scene_marker/{ marker ['id' ]} /stream"
74+
75+ # RESET & UPDATE
76+ clear_mut = "mutation($id: ID!) { tagUpdate(input: { id: $id, image: \" \" }) { id } }"
77+ call_gql (clear_mut , {"id" : tag_id })
12578
126- # Check if there are any scene markers returned:
127- if scene_markers :
128- # Pick one random marker dictionary from the list
129- random_marker = random .choice (scene_markers )
130- # Get stream URL from dictionary
131- random_url = random_marker .get ("stream" )
132-
133- # Update tag image if a random URL was allocated
134- if random_url :
135- update_tag_image (tag_id , random_url )
136- log .info (f"Updated tag '{ tag_name } ' with scene marker video preview." )
137- processed_markers += 1
138- time .sleep (0.5 ) # Add a half-second delay
139- else :
140- log .info (f"No URL was available for tag '{ tag_name } '. Skipping." )
141- else :
142- log .info (f"Could not fetch scene markers for tag_id '{ tag_id } '. Skipping." )
143-
144- processed_tags += 1
145-
146- # Calculate progress as a percentage and log it
147- progress = processed_tags / total_tags
148- log .progress (progress )
79+ update_mut = "mutation($id: ID!, $img: String!) { tagUpdate(input: { id: $id, image: $img }) { id } }"
80+ call_gql (update_mut , {"id" : tag_id , "img" : stream_url })
14981
150- # Calculate and log ETA
151- eta = calculate_eta (total_tags , total_markers , processed_tags , processed_markers , start_time )
152- log .info (f"Progress: { progress * 100 :.2f} %, ETA: { eta } seconds." )
82+ # 3. UPDATE STASH UI PROGRESS BAR
83+ log .progress (idx / total )
15384
85+ if idx % 10 == 0 :
86+ elapsed = time .time () - start_time
87+ eta = int ((elapsed / idx ) * (total - idx ))
88+ log .info (f"Progress: { idx } /{ total } - ETA: { eta } s" )
89+
90+ log .info ("Finished updating tag previews." )
91+
15492if __name__ == "__main__" :
155- log .info ("Starting script..." )
156- start_time = time .time () # Initialize start time
157- main ()
93+ main ()
0 commit comments