From 3516b31c06d148f759cb7b5cb10d0565a18d12d8 Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Fri, 10 Apr 2026 12:03:47 -0400 Subject: [PATCH] =?UTF-8?q?v3.4:=20Fix=20hlist.Count=20(.NET)=20=E2=86=92?= =?UTF-8?q?=20len(hlist)=20(Python),=20add=20.get()=20safety=20on=20hierar?= =?UTF-8?q?chy=20response?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- folder-organizer-v3.4.py | 102 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 folder-organizer-v3.4.py diff --git a/folder-organizer-v3.4.py b/folder-organizer-v3.4.py new file mode 100644 index 0000000..87f730f --- /dev/null +++ b/folder-organizer-v3.4.py @@ -0,0 +1,102 @@ +# ================================================================ +# AMPP Framelight X — Nested Prefix Folder Organizer +# Production v3.4 — Delimiter: -- +# +# Examples: +# "BMG--Videos--File.mp4" → BMG / Videos / (asset linked) +# "NEWS--PKG--Clip1.mxf" → NEWS / PKG / (asset linked) +# "IMG--4709.jpg" → IMG / (asset linked) +# "No-Delimiter.mp4" → skipped +# +# v3.3 Fix: Properly URL-encode folder paths including special +# characters (apostrophes, ampersands, etc.) in hierarchy lookups. +# +# v3.4 Fix: Replace .NET-style hlist.Count with Python len(hlist). +# hlist is a Python list (parsed JSON), not a .NET collection. +# Using .Count caused a silent AttributeError (swallowed by bare +# except), making every hierarchy lookup fail and causing duplicate +# folders to be created on every run instead of reusing existing ones. +# Also use .get() on hierarchy response to avoid KeyError when the +# path doesn't exist yet. Also replaced bare except with named +# exception so lookup errors are visible in AMPP job logs. +# ================================================================ +import json +import urllib.parse + +PREFIX_DELIM = "--" +asset_id = str(asset.Id) +asset_name = str(job.AssetName) + +if PREFIX_DELIM not in asset_name: + pass +else: + parts = asset_name.split(PREFIX_DELIM) + if len(parts) < 2: + pass + else: + folder_names = parts[:-1] + parent_id = None + current_path = "" + + for fname in folder_names: + fname = fname.strip() + if not fname: + continue + + if current_path: + current_path = current_path + "/" + fname + else: + current_path = fname + + folder_id = None + try: + # URL-encode the full path, preserving "/" as path separator + encoded = urllib.parse.quote(current_path, safe="/") + resp = httpClient.sendAsync( + "GET", + "api/v1/store/folder/folders/hierarchy?path=" + encoded + ) + # Use .get() in case the key is absent (path not found yet) + # Use len() — hlist is a Python list, not a .NET collection (.Count doesn't exist) + hlist = resp.get("hierarchy:list", []) + if hlist and len(hlist) > 0: + folder_id = str(hlist[-1]["folder:id"]) + except Exception as lookup_ex: + logger.log("Folder hierarchy lookup failed for '" + current_path + "': " + str(lookup_ex)) + folder_id = None + + if not folder_id: + create_body = json.dumps({"name:text": fname}) + if parent_id: + create_body = json.dumps({ + "name:text": fname, + "parentFolders:tags": [parent_id] + }) + try: + fresp = httpClient.sendAsync( + "POST", + "api/v1/store/folder/folders", + create_body + ) + folder_id = str(fresp["folder:id"]) + except Exception as ex: + raise Exception( + "Failed to create folder '" + fname + + "' in '" + current_path + "': " + str(ex) + ) + + parent_id = folder_id + + if parent_id: + link_body = json.dumps( + {"folder:id": parent_id, "asset:id": asset_id}, + separators=(',', ':') + ) + try: + httpClient.sendAsync( + "POST", + "api/v1/store/folder/references", + link_body + ) + except: + pass