AMI to replace {selected_ids} with record id

Hi will anyone please be a kind neighbor and help me.

I have an AMI issue. The AMI url I am using on Flow won’t replace the {selected_ids} with the record id number. It only works if I hardcode a record id number in the AMI URL. What am I doing wrong? The URL is shotgun://ShotgunAMIEngine.app?selected_ids=${selected_ids}, if I right click on a record it will not replace the selected_ids with the record id to open the folder, but as mentioned, if I hardcode record ids ex. shotgun://ShotgunAMIEngine.app?selected_ids=1339,1340,1341 then right click on any record, it will open the 3 hardcoded folders.

What am I doing wrong? The Python script obviously works to open folder if hardcoded record numbers, so how do I get AMI / Flow to replace {selected_ids} with the checkbox selected records id’s when I right click and choose my AMI ‘open folder’?

Please any help, I am on a huge VFX feature and need help asap! I even messaged Autodesk for help but I’m not getting any response. I appreciate it if someone would help me.

Correct me if I’m misunderstanding you:
You want to use the selected_ids from the post request, but it’s not coming through correctly?
Does it work when only one entity is selected?

I had a similar issue when setting up an AMI - with flask I was using:
post_dict = dict(request.form.lists())
to get my post request.

The post request comes through like this: “selected_ids”: [“123,456,789”] I had to reformat the data so that the ids were separate [“123”,“456”,“789”]

Not sure if this is the issue you’re encountering.

I am using an app AMIEngine.app that I created, it takes the action from the AMI URL and uses the app applescript to run the python script to get the action of opening the desired folder. So here is the applescript in the AMIEngine.app, takes the python script:

PYTHON
#!/usr/bin/env python3
import os
import sys
import subprocess
import urllib.parse
from shotgun_api3 import Shotgun

ShotGrid Configuration

SERVER_URL = “removed for this post”
SCRIPT_NAME = “removed for this post”
API_KEY = “removed for this post”
MEDIA_TREE_BASE = “removed for this post”

def get_shot_code(shot_id):
“”“Get Shot code from ShotGrid using the shot ID.”“”
try:
sg = Shotgun(SERVER_URL, SCRIPT_NAME, API_KEY)
shot_data = sg.find_one(“Shot”, [[“id”, “is”, int(shot_id)]], [“code”])
if shot_data and shot_data.get(“code”):
return shot_data[“code”]
else:
print(f":x: Error: Shot ID {shot_id} not found in ShotGrid.“)
return None
except Exception as e:
print(f":x: ShotGrid API error: {e}”)
return None

def open_shot_folder(shot_code):
“”“Find and open the folder matching the shot code.”“”
for root, dirs, _ in os.walk(MEDIA_TREE_BASE):
if shot_code in dirs:
folder_path = os.path.join(root, shot_code)
subprocess.run([“open”, folder_path])
print(f":white_check_mark: Opened: {folder_path}“)
return True
print(f":x: No folder found for ‘{shot_code}’ in {MEDIA_TREE_BASE}”)
return False

def process_shot_ids(shot_ids):
“”“Process a list of shot IDs: fetch their code and open the folder.”“”
for shot_id in shot_ids:
shot_id = shot_id.strip()
if shot_id:
shot_code = get_shot_code(shot_id)
if shot_code:
open_shot_folder(shot_code)

if name == “main”:
if len(sys.argv) < 2:
print(“:x: Usage: python ami_engine.py <shot_ids_or_url>”)
sys.exit(1)

input_arg = sys.argv[1]
shot_ids = []

# If the argument is a URL (starts with "shotgun://"), parse it:
if input_arg.startswith("shotgun://"):
    parsed_url = urllib.parse.urlparse(input_arg)
    query_params = urllib.parse.parse_qs(parsed_url.query)
    # Expecting the parameter to be named "selected_ids" (e.g. selected_ids=123,456,789)
    ids_param = query_params.get("selected_ids", [])
    if ids_param:
        # ids_param is typically a list with one element like ["123,456,789"]
        shot_ids = ids_param[0].split(",")
    else:
        print("❌ No 'selected_ids' parameter found in the URL.")
        sys.exit(1)
else:
    # If not a URL, then treat the input as shot IDs.
    # If it contains commas, split it; otherwise, use the provided arguments.
    if "," in input_arg:
        shot_ids = input_arg.split(",")
    else:
        shot_ids = sys.argv[1:]

if not shot_ids:
    print("❌ No shot IDs provided.")
    sys.exit(1)

process_shot_ids(shot_ids)

APPLESCRIPT
on open location this_URL

try

– Extract parameters from the URL

set AppleScript’s text item delimiters to “?”

set query to item 2 of (text items of this_URL)

set AppleScript’s text item delimiters to “&”

set params to text items of query

set AppleScript’s text item delimiters to “=”

– Initialize an empty list for shot IDs

set shot_ids to {}

– Loop through parameters to find selected_ids

repeat with param in params

set param_items to text items of param

if item 1 of param_items is “selected_ids” then

set id_list to item 2 of param_items

– Split the id_list by commas

set AppleScript’s text item delimiters to “,”

set id_items to text items of id_list

– Add each ID to the shot_ids list

repeat with id_item in id_items

set end of shot_ids to id_item

end repeat

end if

end repeat

– Ensure we have at least one ID

if (count of shot_ids) is greater than 0 then

– Construct the command to run the Python script with all shot IDs

set shot_id_args to “”

repeat with shot_id in shot_ids

set shot_id_args to shot_id_args & " " & shot_id

end repeat

– Activate the virtual environment and run the Python script

do shell script “removed python script address for this post” & shot_id_args

else

display dialog “Error: No Shot IDs found in URL.” buttons {“OK”} default button “OK”

end if

on error errMsg

display dialog "Error: " & errMsg buttons {“OK”} default button “OK”

end try

end open location

And I’ve tried these URLS: shotgun://ShotgunAMIEngine.app?selected_ids={ids}
shotgun://ShotgunAMIEngine.app?selected_ids=${selected_ids} but as mentioned, it doesn’t open the folders via right click on flow and choosing ‘open folder’, but if I hardcode a record number shotgun://ShotgunAMIEngine.app?selected_ids=1339, it will open the folder of that record (shot) on our server.

The AMI sends your selected_ids as a post request. It doesn’t come through in the url query. You need some way to receive the post request and parse it. Here’s a how-to that uses a flask server to do this. I’m not sure how to do this with applescript. Starting at 8:39 describes how to get the post data.

Thanks, it doesn’t seem to matter what I do, I’ve tried this and even enlisted the help of AI to help find my error, even AI says my script is good, but no matter what I do whether using the AMIEngine app I created, (which script is good and will open folders hardcoded) even trying this Flask, it will open folders via terminal but none of the AMI URL’s I use for either the app or Flask, will open the folders when I right click on a record. It’s so frustrating I could cry! But thank you for trying to help me. I truly appreciate it.

I’m pretty sure you have misunderstood how ami’s work.

Your url should not include the key like this:
shotgun://ShotgunAMIEngine.app?selected_ids=${selected_ids}

It should just be like this:
shotgun://ShotgunAMIEngine.app

Your script app will then receive a POST request which includes a key selected_ids with comma separated integers. You need to process that POST request and use those values as needed.

I tried this and I keep getting error when I use AMI URL

#!/bin/python
from flask import Flask, request, jsonify
import os
import subprocess
from shotgun_api3 import Shotgun

app = Flask(name)

Configuration

SERVER_URL =
SCRIPT_NAME =
API_KEY =
MEDIA_TREE_BASE =

Create a Shotgun instance

sg = Shotgun(SERVER_URL, SCRIPT_NAME, API_KEY)

def get_shot_code(shot_id):
“”“Query ShotGrid to get the shot code from a shot ID.”“”
try:
shot_data = sg.find_one(“Shot”, [[“id”, “is”, int(shot_id)]], [“code”])
if shot_data and shot_data.get(“code”):
return shot_data[“code”]
else:
print(f":x: Error: Shot ID {shot_id} not found in ShotGrid.“)
return None
except Exception as e:
print(f":x: ShotGrid API error: {e}”)
return None

def open_shot_folder(shot_code):
“”“Find and open the folder matching the shot code in MEDIA_TREE_BASE.”“”
for root, dirs, _ in os.walk(MEDIA_TREE_BASE):
if shot_code in dirs:
folder_path = os.path.join(root, shot_code)
subprocess.run([“open”, folder_path])
print(f":white_check_mark: Opened: {folder_path}“)
return folder_path
print(f":x: No folder found for ‘{shot_code}’ in {MEDIA_TREE_BASE}”)
return None

def process_shot_ids(shot_ids):
“”“Process a list of shot IDs and return results.”“”
results =
for shot_id in shot_ids:
shot_code = get_shot_code(shot_id)
if not shot_code:
results.append({“shot_id”: shot_id, “error”: “Shot code not found”})
continue
folder_path = open_shot_folder(shot_code)
if folder_path:
results.append({
“shot_id”: shot_id,
“shot_code”: shot_code,
“folder_path”: folder_path
})
else:
results.append({
“shot_id”: shot_id,
“shot_code”: shot_code,
“error”: “Folder not found”
})
return results

@app.route(‘/open_folder’, methods=[‘GET’, ‘POST’])
def open_folder():
if request.method == ‘POST’:
# Handle POST request
if request.is_json:
data = request.get_json()
else:
data = dict(request.form.lists())

    if not data or "selected_ids" not in data:
        return jsonify({"error": "Missing 'selected_ids' parameter in POST data"}), 400

    selected_ids_str = data["selected_ids"][0] if isinstance(data["selected_ids"], list) else data["selected_ids"]
elif request.method == 'GET':
    # Handle GET request
    selected_ids_str = request.args.get('selected_ids')
    if not selected_ids_str:
        return jsonify({"error": "Missing 'selected_ids' parameter in GET request"}), 400

# Process the selected_ids for both GET and POST
shot_ids = [s.strip() for s in selected_ids_str.split(",") if s.strip()]
results = process_shot_ids(shot_ids)

return jsonify({"message": "Processed shot IDs", "results": results}), 200

if name == ‘main’:
app.run(debug=True, host=‘0.0.0.0’, port=5001)

Thanks, but when I just do the above it gives me this error Error: :x: No ‘selected_ids’ parameter found in the URL as I had it in the script to make sure to error if selected id’s were not passing from shotgrid through AMi. Here is my script, I must be doing it wrong,

If the argument is a URL (starts with “shotgun://”), parse it:

if input_arg.startswith("shotgun://"):
    parsed_url = urllib.parse.urlparse(input_arg)
    query_params = urllib.parse.parse_qs(parsed_url.query)
    # Expecting the parameter to be named "selected_ids" (e.g. selected_ids=123,456,789)
    ids_param = query_params.get("selected_ids", [])
    if ids_param:
        # ids_param is typically a list with one element like ["123,456,789"]
        shot_ids = ids_param[0].split(",")
    else:
        print("❌ No 'selected_ids' parameter found in the URL.")
        sys.exit(1)
else:

You should try printing the Request params to see what you are sent.

Also, when posting scripts and logs, please use the 3 ` notation so it formats like code.

` ` ` (quotes witout space) ` ` `