diff --git a/.specstory/history/2025-12-19_17-59Z-texture-folder-commonality-script.md b/.specstory/history/2025-12-19_17-59Z-texture-folder-commonality-script.md
index 4c33b15..730c249 100644
--- a/.specstory/history/2025-12-19_17-59Z-texture-folder-commonality-script.md
+++ b/.specstory/history/2025-12-19_17-59Z-texture-folder-commonality-script.md
@@ -3441,3 +3441,12 @@ Got it, no more problems detected.
---
+next issue: let's make sure it scans each folder separately. it goes \\textures\[Blendfile]\[Material]
+
+Intra-blendfile commonalities should go to `\\textures\[blendfile]\common`
+Inter-blendfile commonalities should go to `\\textures\common`
+
+sound good?
+
+---
+
diff --git a/.specstory/history/2025-12-19_18-52Z-texture-commonality-folder-structure.md b/.specstory/history/2025-12-19_18-52Z-texture-commonality-folder-structure.md
new file mode 100644
index 0000000..b29c383
--- /dev/null
+++ b/.specstory/history/2025-12-19_18-52Z-texture-commonality-folder-structure.md
@@ -0,0 +1,5596 @@
+
+
+
+
+# Texture commonality folder structure (2025-12-19 18:52Z)
+
+_**User**_
+
+context:@.specstory/history/2025-12-19_17-59Z-texture-folder-commonality-script.md
+
+next issue: let's make sure it scans each folder separately. it goes \\textures\[Blendfile]\[Material]
+
+Intra-blendfile commonalities should go to `\\textures\[blendfile]\common`
+Inter-blendfile commonalities should go to `\\textures\common`
+
+sound good?
+
+---
+
+_**Agent (model default, mode Plan)**_
+
+
+Reviewing the specstory and current implementation to understand the context.
+
+
+---
+
+
+Tool use: **read_file_v2** • Read file: a:\1 Amazon_Active_Projects\3 ProjectStructure\.specstory\history\2025-12-19_17-59Z-texture-folder-commonality-script.md
+
+
+
+
+---
+
+
+Tool use: **read_file_v2** • Read file: a:\1 Amazon_Active_Projects\3 ProjectStructure\organize_textures.bat
+
+
+
+
+---
+
+
+Tool use: **read_file_v2** • Read file: a:\1 Amazon_Active_Projects\3 ProjectStructure\organize_textures.ps1
+
+
+
+
+---
+
+
+Reading the specstory file in sections to understand the context:
+
+
+---
+
+
+Tool use: **read_file_v2** • Read file: a:\1 Amazon_Active_Projects\3 ProjectStructure\.specstory\history\2025-12-19_17-59Z-texture-folder-commonality-script.md
+
+
+
+
+---
+
+
+Checking the folder structure to understand the layout:
+
+
+---
+
+
+
+Tool use: **semantic_search_full**
+
+
+Parameters:
+
+```json
+{
+ "query": "What is the folder structure for textures with blendfile and material subdirectories?",
+ "topK": 10,
+ "explanation": ""
+}
+```
+Result:
+
+```json
+{
+ "codeResults": [
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\components\\gitignore",
+ "range": {
+ "startPosition": {},
+ "endPosition": {
+ "line": 125,
+ "column": 7
+ }
+ },
+ "contents": "# Blender Studio Git Management - .gitignore\n\n# Blender auto-save files\n*.blend1\n*.blend2\n*.blend3\n*.blend4\n*.blend5\n*.blend6\n*.blend7\n*.blend8\n*.blend9\n*.blend10\n*.blend11\n*.blend12\n*.blend13\n*.blend14\n*.blend15\n*.blend16\n*.blend17\n*.blend18\n*.blend19\n*.blend20\n*.blend21\n*.blend22\n*.blend23\n*.blend24\n*.blend25\n*.blend26\n*.blend27\n*.blend28\n*.blend29\n*.blend30\n*.blend31\n*.blend32\n\n# Blender cache and temporary files\n*.swp\n*.swo\n*~\n*.tmp\n*.temp\n\n# OS generated files\n.DS_Store\n.DS_Store?\n._*\n.Spotlight-V100\n.Trashes\nehthumbs.db\nThumbs.db\ndesktop.ini\n\n# Editor files\n.vscode/\n.idea/\n*.swp\n*.swo\n*~\n\n# Large media files (uncomment if you want to exclude rendered outputs)\n# *.mp4\n# *.avi\n# *.mov\n# *.png\n# *.jpg\n# *.jpeg\n# *.exr\n# *.hdr\n# *.tiff\n# *.tga\n\n# Audio files (uncomment if excluding)\n# *.wav\n# *.mp3\n# *.aac\n# *.ogg\n\n# Log files\n*.log\n\n# Python cache\n__pycache__/\n*.pyc\n*.pyo\n*.pyd\n.Python\n\n# Node modules (if using any web tools)\nnode_modules/\n\n# Environment files\n.env\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# Backup files\n*.bak\n*.backup\n*.old\n\n# Temporary files\ntemp/\ntmp/\ncache/ \n\nRenders/**\n!Renders/_zipped/\n!Renders/_zipped/**\n!Renders/**/\n!Renders/**/*.bat\n!Renders/**/*.log\nseq/\n\n# Premiere\n*.prlock\nAdobe Premiere Pro Video Previews/\nAdobe Premiere Pro Audio Previews/\nAdobe Premiere Pro (Beta) Video Previews/\nAdobe Premiere Pro (Beta) Audio Previews/\nfootage/\nplates/\n\nPr/RnR/",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "# Blender Studio Git Management - .gitignore",
+ "lineNumber": 1
+ },
+ {
+ "lineNumber": 2
+ },
+ {
+ "text": "# Blender auto-save files",
+ "lineNumber": 3
+ },
+ {
+ "text": "*.blend1",
+ "lineNumber": 4
+ },
+ {
+ "text": "*.blend2",
+ "lineNumber": 5
+ },
+ {
+ "text": "*.blend3",
+ "lineNumber": 6
+ },
+ {
+ "text": "*.blend4",
+ "lineNumber": 7
+ },
+ {
+ "text": "*.blend5",
+ "lineNumber": 8
+ },
+ {
+ "text": "*.blend6",
+ "lineNumber": 9
+ },
+ {
+ "text": "*.blend7",
+ "lineNumber": 10
+ },
+ {
+ "text": "*.blend8",
+ "lineNumber": 11
+ },
+ {
+ "text": "*.blend9",
+ "lineNumber": 12
+ },
+ {
+ "text": "*.blend10",
+ "lineNumber": 13
+ },
+ {
+ "text": "*.blend11",
+ "lineNumber": 14
+ },
+ {
+ "text": "*.blend12",
+ "lineNumber": 15
+ },
+ {
+ "text": "*.blend13",
+ "lineNumber": 16
+ },
+ {
+ "text": "*.blend14",
+ "lineNumber": 17
+ },
+ {
+ "text": "*.blend15",
+ "lineNumber": 18
+ },
+ {
+ "text": "*.blend16",
+ "lineNumber": 19
+ },
+ {
+ "text": "*.blend17",
+ "lineNumber": 20
+ },
+ {
+ "text": "*.blend18",
+ "lineNumber": 21
+ },
+ {
+ "text": "*.blend19",
+ "lineNumber": 22
+ },
+ {
+ "text": "*.blend20",
+ "lineNumber": 23
+ },
+ {
+ "text": "*.blend21",
+ "lineNumber": 24
+ },
+ {
+ "text": "*.blend22",
+ "lineNumber": 25
+ },
+ {
+ "text": "*.blend23",
+ "lineNumber": 26
+ },
+ {
+ "text": "*.blend24",
+ "lineNumber": 27
+ },
+ {
+ "text": "*.blend25",
+ "lineNumber": 28
+ },
+ {
+ "text": "*.blend26",
+ "lineNumber": 29
+ },
+ {
+ "text": "*.blend27",
+ "lineNumber": 30
+ },
+ {
+ "text": "*.blend28",
+ "lineNumber": 31
+ },
+ {
+ "text": "*.blend29",
+ "lineNumber": 32
+ },
+ {
+ "text": "*.blend30",
+ "lineNumber": 33
+ },
+ {
+ "text": "*.blend31",
+ "lineNumber": 34
+ },
+ {
+ "text": "*.blend32",
+ "lineNumber": 35
+ },
+ {
+ "lineNumber": 36
+ },
+ {
+ "text": "# Blender cache and temporary files",
+ "lineNumber": 37
+ },
+ {
+ "text": "*.swp",
+ "lineNumber": 38
+ },
+ {
+ "text": "*.swo",
+ "lineNumber": 39
+ },
+ {
+ "text": "*~",
+ "lineNumber": 40
+ },
+ {
+ "text": "*.tmp",
+ "lineNumber": 41
+ },
+ {
+ "text": "*.temp",
+ "lineNumber": 42
+ },
+ {
+ "lineNumber": 43
+ },
+ {
+ "text": "# OS generated files",
+ "lineNumber": 44
+ },
+ {
+ "text": ".DS_Store",
+ "lineNumber": 45
+ },
+ {
+ "text": ".DS_Store?",
+ "lineNumber": 46
+ },
+ {
+ "text": "._*",
+ "lineNumber": 47
+ },
+ {
+ "text": ".Spotlight-V100",
+ "lineNumber": 48
+ },
+ {
+ "text": ".Trashes",
+ "lineNumber": 49
+ },
+ {
+ "text": "ehthumbs.db",
+ "lineNumber": 50
+ },
+ {
+ "text": "Thumbs.db",
+ "lineNumber": 51
+ },
+ {
+ "text": "desktop.ini",
+ "lineNumber": 52
+ },
+ {
+ "lineNumber": 53
+ },
+ {
+ "text": "# Editor files",
+ "lineNumber": 54
+ },
+ {
+ "text": ".vscode/",
+ "lineNumber": 55
+ },
+ {
+ "text": ".idea/",
+ "lineNumber": 56
+ },
+ {
+ "text": "*.swp",
+ "lineNumber": 57
+ },
+ {
+ "text": "*.swo",
+ "lineNumber": 58
+ },
+ {
+ "text": "*~",
+ "lineNumber": 59
+ },
+ {
+ "lineNumber": 60
+ },
+ {
+ "text": "# Large media files (uncomment if you want to exclude rendered outputs)",
+ "lineNumber": 61
+ },
+ {
+ "text": "# *.mp4",
+ "lineNumber": 62
+ },
+ {
+ "text": "# *.avi",
+ "lineNumber": 63
+ },
+ {
+ "text": "# *.mov",
+ "lineNumber": 64
+ },
+ {
+ "text": "# *.png",
+ "lineNumber": 65
+ },
+ {
+ "text": "# *.jpg",
+ "lineNumber": 66
+ },
+ {
+ "text": "# *.jpeg",
+ "lineNumber": 67
+ },
+ {
+ "text": "# *.exr",
+ "lineNumber": 68
+ },
+ {
+ "text": "# *.hdr",
+ "lineNumber": 69
+ },
+ {
+ "text": "# *.tiff",
+ "lineNumber": 70
+ },
+ {
+ "text": "# *.tga",
+ "lineNumber": 71
+ },
+ {
+ "lineNumber": 72
+ },
+ {
+ "text": "# Audio files (uncomment if excluding)",
+ "lineNumber": 73
+ },
+ {
+ "text": "# *.wav",
+ "lineNumber": 74
+ },
+ {
+ "text": "# *.mp3",
+ "lineNumber": 75
+ },
+ {
+ "text": "# *.aac",
+ "lineNumber": 76
+ },
+ {
+ "text": "# *.ogg",
+ "lineNumber": 77
+ },
+ {
+ "lineNumber": 78
+ },
+ {
+ "text": "# Log files",
+ "lineNumber": 79
+ },
+ {
+ "text": "*.log",
+ "lineNumber": 80
+ },
+ {
+ "lineNumber": 81
+ },
+ {
+ "text": "# Python cache",
+ "lineNumber": 82
+ },
+ {
+ "text": "__pycache__/",
+ "lineNumber": 83
+ },
+ {
+ "text": "*.pyc",
+ "lineNumber": 84
+ },
+ {
+ "text": "*.pyo",
+ "lineNumber": 85
+ },
+ {
+ "text": "*.pyd",
+ "lineNumber": 86
+ },
+ {
+ "text": ".Python",
+ "lineNumber": 87
+ },
+ {
+ "lineNumber": 88
+ },
+ {
+ "text": "# Node modules (if using any web tools)",
+ "lineNumber": 89
+ },
+ {
+ "text": "node_modules/",
+ "lineNumber": 90
+ },
+ {
+ "lineNumber": 91
+ },
+ {
+ "text": "# Environment files",
+ "lineNumber": 92
+ },
+ {
+ "text": ".env",
+ "lineNumber": 93
+ },
+ {
+ "text": ".env.local",
+ "lineNumber": 94
+ },
+ {
+ "text": ".env.development.local",
+ "lineNumber": 95
+ },
+ {
+ "text": ".env.test.local",
+ "lineNumber": 96
+ },
+ {
+ "text": ".env.production.local",
+ "lineNumber": 97
+ },
+ {
+ "lineNumber": 98
+ },
+ {
+ "text": "# Backup files",
+ "lineNumber": 99
+ },
+ {
+ "text": "*.bak",
+ "lineNumber": 100
+ },
+ {
+ "text": "*.backup",
+ "lineNumber": 101
+ },
+ {
+ "text": "*.old",
+ "lineNumber": 102
+ },
+ {
+ "lineNumber": 103
+ },
+ {
+ "text": "# Temporary files",
+ "lineNumber": 104
+ },
+ {
+ "text": "temp/",
+ "lineNumber": 105
+ },
+ {
+ "text": "tmp/",
+ "lineNumber": 106
+ },
+ {
+ "text": "cache/ ",
+ "lineNumber": 107
+ },
+ {
+ "lineNumber": 108
+ },
+ {
+ "text": "Renders/**",
+ "lineNumber": 109
+ },
+ {
+ "text": "!Renders/_zipped/",
+ "lineNumber": 110
+ },
+ {
+ "text": "!Renders/_zipped/**",
+ "lineNumber": 111
+ },
+ {
+ "text": "!Renders/**/",
+ "lineNumber": 112
+ },
+ {
+ "text": "!Renders/**/*.bat",
+ "lineNumber": 113
+ },
+ {
+ "text": "!Renders/**/*.log",
+ "lineNumber": 114
+ },
+ {
+ "text": "seq/",
+ "lineNumber": 115
+ },
+ {
+ "lineNumber": 116
+ },
+ {
+ "text": "# Premiere",
+ "lineNumber": 117
+ },
+ {
+ "text": "*.prlock",
+ "lineNumber": 118
+ },
+ {
+ "text": "Adobe Premiere Pro Video Previews/",
+ "lineNumber": 119
+ },
+ {
+ "text": "Adobe Premiere Pro Audio Previews/",
+ "lineNumber": 120
+ },
+ {
+ "text": "Adobe Premiere Pro (Beta) Video Previews/",
+ "lineNumber": 121
+ },
+ {
+ "text": "Adobe Premiere Pro (Beta) Audio Previews/",
+ "lineNumber": 122
+ },
+ {
+ "text": "footage/",
+ "lineNumber": 123
+ },
+ {
+ "text": "plates/",
+ "lineNumber": 124
+ },
+ {
+ "lineNumber": 125
+ },
+ {
+ "text": "Pr/RnR/",
+ "lineNumber": 126
+ }
+ ]
+ },
+ "score": 0.354219913482666
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\NewProject.bat",
+ "range": {
+ "startPosition": {},
+ "endPosition": {
+ "line": 63
+ }
+ },
+ "contents": "@echo off\nsetlocal EnableExtensions\n\nset \"SCRIPT_DIR=%~dp0\"\nset \"CONFIG_LOADER=%SCRIPT_DIR%ConfigLoader.ps1\"\nset \"CONFIG_PATH=%SCRIPT_DIR%config.json\"\n\nif not exist \"%CONFIG_LOADER%\" (\n echo [ERROR] ConfigLoader.ps1 not found next to NewProject.bat.\n exit /b 1\n)\n\nif not exist \"%CONFIG_PATH%\" (\n echo [ERROR] config.json not found next to NewProject.bat.\n exit /b 1\n)\n\nfor /f \"usebackq delims=\" %%I in (`powershell -NoProfile -ExecutionPolicy Bypass -Command ^\n \"Set-StrictMode -Version Latest; $loader = Resolve-Path -LiteralPath '%CONFIG_LOADER%' -ErrorAction Stop; . $loader.Path; Write-Output (Get-StructDirectory)\"`) do set \"STRUCT_DIR=%%I\"\n\nif not defined STRUCT_DIR (\n echo [ERROR] Unable to resolve ProjectStructure directory from config.\n exit /b 1\n)\n\n:: Get current date in YYYY-MM-DD format\nfor /f \"tokens=2-4 delims=/ \" %%a in ('date /t') do (\n set mm=%%a\n set dd=%%b\n set yy=%%c\n)\nset yyyy=20%yy:~-2%\nset yymmdd=%yy:~-2%%mm%%dd%\n\n:: Ask for project name\nset /p projectName=\"Enter project name (press Enter for default 'NewProject'): \"\nif \"%projectName%\"==\"\" set projectName=NewProject\nset projectRoot=%yymmdd%_%projectName%\n\n:: Create main project directory\nmkdir \"%projectRoot%\"\n\n:: Create Assets structure\nmkdir \"%projectRoot%\\Assets\\ElevenLabs\"\nif exist \"%STRUCT_DIR%\\NewDaily.bat\" copy /Y \"%STRUCT_DIR%\\NewDaily.bat\" \"%projectRoot%\\Assets\\ElevenLabs\\NewDaily.bat\" >nul\nmkdir \"%projectRoot%\\Assets\\Blends\"\nmkdir \"%projectRoot%\\Assets\\Mocap\"\nmkdir \"%projectRoot%\\Assets\\VO\"\nif exist \"%~dp0NewDaily.bat\" copy /Y \"%~dp0NewDaily.bat\" \"%projectRoot%\\Assets\\VO\\NewDaily.bat\" >nul\n\n:: Create Blends structure\nmkdir \"%projectRoot%\\Blends\\animations\\\"\nmkdir \"%projectRoot%\\Blends\\stills\\img-BG\"\n\n:: Create Deliverable structure\nmkdir \"%projectRoot%\\Deliverable\\\"\n\n:: Create Pr structure\nmkdir \"%projectRoot%\\Pr\\RnR\\RIFE\"\n\n:: Add project root additions\nif not exist \"%projectRoot%\\Renders\" mkdir \"%projectRoot%\\Renders\"",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "@echo off",
+ "lineNumber": 1
+ },
+ {
+ "text": "setlocal EnableExtensions",
+ "lineNumber": 2
+ },
+ {
+ "lineNumber": 3
+ },
+ {
+ "text": "set \"SCRIPT_DIR=%~dp0\"",
+ "lineNumber": 4
+ },
+ {
+ "text": "set \"CONFIG_LOADER=%SCRIPT_DIR%ConfigLoader.ps1\"",
+ "lineNumber": 5
+ },
+ {
+ "text": "set \"CONFIG_PATH=%SCRIPT_DIR%config.json\"",
+ "lineNumber": 6
+ },
+ {
+ "lineNumber": 7
+ },
+ {
+ "text": "if not exist \"%CONFIG_LOADER%\" (",
+ "lineNumber": 8
+ },
+ {
+ "text": " echo [ERROR] ConfigLoader.ps1 not found next to NewProject.bat.",
+ "lineNumber": 9
+ },
+ {
+ "text": " exit /b 1",
+ "lineNumber": 10
+ },
+ {
+ "text": ")",
+ "lineNumber": 11
+ },
+ {
+ "lineNumber": 12
+ },
+ {
+ "text": "if not exist \"%CONFIG_PATH%\" (",
+ "lineNumber": 13
+ },
+ {
+ "text": " echo [ERROR] config.json not found next to NewProject.bat.",
+ "lineNumber": 14
+ },
+ {
+ "text": " exit /b 1",
+ "lineNumber": 15
+ },
+ {
+ "text": ")",
+ "lineNumber": 16
+ },
+ {
+ "lineNumber": 17
+ },
+ {
+ "text": "for /f \"usebackq delims=\" %%I in (`powershell -NoProfile -ExecutionPolicy Bypass -Command ^",
+ "lineNumber": 18
+ },
+ {
+ "text": " \"Set-StrictMode -Version Latest; $loader = Resolve-Path -LiteralPath '%CONFIG_LOADER%' -ErrorAction Stop; . $loader.Path; Write-Output (Get-StructDirectory)\"`) do set \"STRUCT_DIR=%%I\"",
+ "lineNumber": 19
+ },
+ {
+ "lineNumber": 20
+ },
+ {
+ "text": "if not defined STRUCT_DIR (",
+ "lineNumber": 21
+ },
+ {
+ "text": " echo [ERROR] Unable to resolve ProjectStructure directory from config.",
+ "lineNumber": 22
+ },
+ {
+ "text": " exit /b 1",
+ "lineNumber": 23
+ },
+ {
+ "text": ")",
+ "lineNumber": 24
+ },
+ {
+ "lineNumber": 25
+ },
+ {
+ "text": ":: Get current date in YYYY-MM-DD format",
+ "lineNumber": 26
+ },
+ {
+ "text": "for /f \"tokens=2-4 delims=/ \" %%a in ('date /t') do (",
+ "lineNumber": 27
+ },
+ {
+ "text": " set mm=%%a",
+ "lineNumber": 28
+ },
+ {
+ "text": " set dd=%%b",
+ "lineNumber": 29
+ },
+ {
+ "text": " set yy=%%c",
+ "lineNumber": 30
+ },
+ {
+ "text": ")",
+ "lineNumber": 31
+ },
+ {
+ "text": "set yyyy=20%yy:~-2%",
+ "lineNumber": 32
+ },
+ {
+ "text": "set yymmdd=%yy:~-2%%mm%%dd%",
+ "lineNumber": 33
+ },
+ {
+ "lineNumber": 34
+ },
+ {
+ "text": ":: Ask for project name",
+ "lineNumber": 35
+ },
+ {
+ "text": "set /p projectName=\"Enter project name (press Enter for default 'NewProject'): \"",
+ "lineNumber": 36
+ },
+ {
+ "text": "if \"%projectName%\"==\"\" set projectName=NewProject",
+ "lineNumber": 37
+ },
+ {
+ "text": "set projectRoot=%yymmdd%_%projectName%",
+ "lineNumber": 38
+ },
+ {
+ "lineNumber": 39
+ },
+ {
+ "text": ":: Create main project directory",
+ "lineNumber": 40
+ },
+ {
+ "text": "mkdir \"%projectRoot%\"",
+ "lineNumber": 41
+ },
+ {
+ "lineNumber": 42
+ },
+ {
+ "text": ":: Create Assets structure",
+ "lineNumber": 43
+ },
+ {
+ "text": "mkdir \"%projectRoot%\\Assets\\ElevenLabs\"",
+ "lineNumber": 44
+ },
+ {
+ "text": "if exist \"%STRUCT_DIR%\\NewDaily.bat\" copy /Y \"%STRUCT_DIR%\\NewDaily.bat\" \"%projectRoot%\\Assets\\ElevenLabs\\NewDaily.bat\" >nul",
+ "lineNumber": 45
+ },
+ {
+ "text": "mkdir \"%projectRoot%\\Assets\\Blends\"",
+ "lineNumber": 46
+ },
+ {
+ "text": "mkdir \"%projectRoot%\\Assets\\Mocap\"",
+ "lineNumber": 47
+ },
+ {
+ "text": "mkdir \"%projectRoot%\\Assets\\VO\"",
+ "lineNumber": 48
+ },
+ {
+ "text": "if exist \"%~dp0NewDaily.bat\" copy /Y \"%~dp0NewDaily.bat\" \"%projectRoot%\\Assets\\VO\\NewDaily.bat\" >nul",
+ "lineNumber": 49
+ },
+ {
+ "lineNumber": 50
+ },
+ {
+ "text": ":: Create Blends structure",
+ "lineNumber": 51
+ },
+ {
+ "text": "mkdir \"%projectRoot%\\Blends\\animations\\\"",
+ "lineNumber": 52
+ },
+ {
+ "text": "mkdir \"%projectRoot%\\Blends\\stills\\img-BG\"",
+ "lineNumber": 53
+ },
+ {
+ "lineNumber": 54
+ },
+ {
+ "text": ":: Create Deliverable structure",
+ "lineNumber": 55
+ },
+ {
+ "text": "mkdir \"%projectRoot%\\Deliverable\\\"",
+ "lineNumber": 56
+ },
+ {
+ "lineNumber": 57
+ },
+ {
+ "text": ":: Create Pr structure",
+ "lineNumber": 58
+ },
+ {
+ "text": "mkdir \"%projectRoot%\\Pr\\RnR\\RIFE\"",
+ "lineNumber": 59
+ },
+ {
+ "lineNumber": 60
+ },
+ {
+ "text": ":: Add project root additions",
+ "lineNumber": 61
+ },
+ {
+ "text": "if not exist \"%projectRoot%\\Renders\" mkdir \"%projectRoot%\\Renders\"",
+ "lineNumber": 62
+ }
+ ]
+ },
+ "score": 0.3472631871700287
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\components\\gitattributes",
+ "range": {
+ "startPosition": {},
+ "endPosition": {
+ "line": 53
+ }
+ },
+ "contents": "# Git LFS attributes configuration\n# All listed file types will be tracked by Git LFS\n\n# Video files\n*.avi filter=lfs diff=lfs merge=lfs -text\n*.mkv filter=lfs diff=lfs merge=lfs -text\n*.mov filter=lfs diff=lfs merge=lfs -text\n*.MOV filter=lfs diff=lfs merge=lfs -text\n*.mp4 filter=lfs diff=lfs merge=lfs -text\n*.webm filter=lfs diff=lfs merge=lfs -text\n\n# Image files\n*.bmp filter=lfs diff=lfs merge=lfs -text\n*.exr filter=lfs diff=lfs merge=lfs -text\n*.gif filter=lfs diff=lfs merge=lfs -text\n*.hdr filter=lfs diff=lfs merge=lfs -text\n*.jpg filter=lfs diff=lfs merge=lfs -text\n*.jpeg filter=lfs diff=lfs merge=lfs -text\n*.png filter=lfs diff=lfs merge=lfs -text\n*.tga filter=lfs diff=lfs merge=lfs -text\n*.tif filter=lfs diff=lfs merge=lfs -text\n*.tiff filter=lfs diff=lfs merge=lfs -text\n*.webp filter=lfs diff=lfs merge=lfs -text\n\n# 3D/CG files\n*.3ds filter=lfs diff=lfs merge=lfs -text\n*.abc filter=lfs diff=lfs merge=lfs -text\n*.blend filter=lfs diff=lfs merge=lfs -text\n*.blend1 filter=lfs diff=lfs merge=lfs -text\n*.blend2 filter=lfs diff=lfs merge=lfs -text\n*.bvh filter=lfs diff=lfs merge=lfs -text\n*.bvh.gz filter=lfs diff=lfs merge=lfs -text\n*.c4d filter=lfs diff=lfs merge=lfs -text\n*.dae filter=lfs diff=lfs merge=lfs -text\n*.fbx filter=lfs diff=lfs merge=lfs -text\n*.fbxkey filter=lfs diff=lfs merge=lfs -text\n*.ma filter=lfs diff=lfs merge=lfs -text\n*.max filter=lfs diff=lfs merge=lfs -text\n*.mb filter=lfs diff=lfs merge=lfs -text\n*.obj filter=lfs diff=lfs merge=lfs -text\n*.usd filter=lfs diff=lfs merge=lfs -text\n*.usdz filter=lfs diff=lfs merge=lfs -text\n*.vdb filter=lfs diff=lfs merge=lfs -text\n*.bphys filter=lfs diff=lfs merge=lfs -text\n*.bobj filter=lfs diff=lfs merge=lfs -text\n*.bvel filter=lfs diff=lfs merge=lfs -text\n*.bpointcache filter=lfs diff=lfs merge=lfs -text\n*.blob filter=lfs diff=lfs merge=lfs -text\n*.unitypackage filter=lfs diff=lfs merge=lfs -text\n\n# Houdini files\n*.hiplc filter=lfs diff=lfs merge=lfs -text\n*.bgeo filter=lfs diff=lfs merge=lfs -text",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "# Git LFS attributes configuration",
+ "lineNumber": 1
+ },
+ {
+ "text": "# All listed file types will be tracked by Git LFS",
+ "lineNumber": 2
+ },
+ {
+ "lineNumber": 3
+ },
+ {
+ "text": "# Video files",
+ "lineNumber": 4
+ },
+ {
+ "text": "*.avi filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 5
+ },
+ {
+ "text": "*.mkv filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 6
+ },
+ {
+ "text": "*.mov filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 7
+ },
+ {
+ "text": "*.MOV filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 8
+ },
+ {
+ "text": "*.mp4 filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 9
+ },
+ {
+ "text": "*.webm filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 10
+ },
+ {
+ "lineNumber": 11
+ },
+ {
+ "text": "# Image files",
+ "lineNumber": 12
+ },
+ {
+ "text": "*.bmp filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 13
+ },
+ {
+ "text": "*.exr filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 14
+ },
+ {
+ "text": "*.gif filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 15
+ },
+ {
+ "text": "*.hdr filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 16
+ },
+ {
+ "text": "*.jpg filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 17
+ },
+ {
+ "text": "*.jpeg filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 18
+ },
+ {
+ "text": "*.png filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 19
+ },
+ {
+ "text": "*.tga filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 20
+ },
+ {
+ "text": "*.tif filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 21
+ },
+ {
+ "text": "*.tiff filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 22
+ },
+ {
+ "text": "*.webp filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 23
+ },
+ {
+ "lineNumber": 24
+ },
+ {
+ "text": "# 3D/CG files",
+ "lineNumber": 25
+ },
+ {
+ "text": "*.3ds filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 26
+ },
+ {
+ "text": "*.abc filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 27
+ },
+ {
+ "text": "*.blend filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 28
+ },
+ {
+ "text": "*.blend1 filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 29
+ },
+ {
+ "text": "*.blend2 filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 30
+ },
+ {
+ "text": "*.bvh filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 31
+ },
+ {
+ "text": "*.bvh.gz filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 32
+ },
+ {
+ "text": "*.c4d filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 33
+ },
+ {
+ "text": "*.dae filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 34
+ },
+ {
+ "text": "*.fbx filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 35
+ },
+ {
+ "text": "*.fbxkey filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 36
+ },
+ {
+ "text": "*.ma filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 37
+ },
+ {
+ "text": "*.max filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 38
+ },
+ {
+ "text": "*.mb filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 39
+ },
+ {
+ "text": "*.obj filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 40
+ },
+ {
+ "text": "*.usd filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 41
+ },
+ {
+ "text": "*.usdz filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 42
+ },
+ {
+ "text": "*.vdb filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 43
+ },
+ {
+ "text": "*.bphys filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 44
+ },
+ {
+ "text": "*.bobj filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 45
+ },
+ {
+ "text": "*.bvel filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 46
+ },
+ {
+ "text": "*.bpointcache filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 47
+ },
+ {
+ "text": "*.blob filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 48
+ },
+ {
+ "text": "*.unitypackage filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 49
+ },
+ {
+ "lineNumber": 50
+ },
+ {
+ "text": "# Houdini files",
+ "lineNumber": 51
+ },
+ {
+ "text": "*.hiplc filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 52
+ },
+ {
+ "text": "*.bgeo filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 53
+ }
+ ]
+ },
+ "score": 0.3170698881149292
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\organize_textures.ps1",
+ "range": {
+ "startPosition": {},
+ "endPosition": {
+ "line": 51
+ }
+ },
+ "contents": "# Script to organize texture files by checksum, moving all files to \\common and duplicates to \\common\\duplicates\n# Usage: .\\organize_textures.ps1\n\n# Prompt user for texture folder path\n$textureFolderPath = Read-Host \"Enter texture folder path\"\n\n# Validate the input path\nif ([string]::IsNullOrWhiteSpace($textureFolderPath)) {\n Write-Host \"Error: No path provided.\" -ForegroundColor Red\n exit\n}\n\nif (-not (Test-Path -Path $textureFolderPath -PathType Container)) {\n Write-Host \"Error: Path does not exist or is not a directory: $textureFolderPath\" -ForegroundColor Red\n exit\n}\n\n# Resolve the full path\n$textureFolderPath = (Resolve-Path $textureFolderPath).ProviderPath\nWrite-Host \"Processing texture folder: $textureFolderPath\" -ForegroundColor Cyan\n\n# Get all files recursively, excluding the \\common folder to avoid processing already-moved files\nWrite-Host \"Collecting files...\" -ForegroundColor Yellow\n$allFiles = Get-ChildItem -Path $textureFolderPath -Recurse -File | Where-Object { $_.FullName -notlike \"*\\common\\*\" }\n\nif ($null -eq $allFiles -or $allFiles.Count -eq 0) {\n Write-Host \"No files found to process (excluding \\common folder).\" -ForegroundColor Yellow\n exit\n}\n\nWrite-Host \"Found $($allFiles.Count) files to process.\" -ForegroundColor Green\n\n# Calculate checksums for all files using parallel processing\nWrite-Host \"Calculating checksums using parallel processing (this may take a while)...\" -ForegroundColor Yellow\n$throttleLimit = [Math]::Max(1, [Environment]::ProcessorCount)\n$parallelScriptBlock = {\n try {\n $hash = Get-FileHash -Path $_.FullName -Algorithm SHA256\n [PSCustomObject]@{\n File = $_\n Hash = $hash.Hash\n }\n } catch {\n Write-Warning \"Failed to calculate checksum for: $($_.FullName) - $($_.Exception.Message)\"\n $null\n }\n}\n$filesWithChecksums = $allFiles | ForEach-Object -Parallel $parallelScriptBlock -ThrottleLimit $throttleLimit | Where-Object { $null -ne $_ }\n\nWrite-Host \"Checksum calculation complete.\" -ForegroundColor Green",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "# Script to organize texture files by checksum, moving all files to \\common and duplicates to \\common\\duplicates",
+ "lineNumber": 1
+ },
+ {
+ "text": "# Usage: .\\organize_textures.ps1",
+ "lineNumber": 2
+ },
+ {
+ "lineNumber": 3
+ },
+ {
+ "text": "# Prompt user for texture folder path",
+ "lineNumber": 4
+ },
+ {
+ "text": "$textureFolderPath = Read-Host \"Enter texture folder path\"",
+ "lineNumber": 5
+ },
+ {
+ "lineNumber": 6
+ },
+ {
+ "text": "# Validate the input path",
+ "lineNumber": 7
+ },
+ {
+ "text": "if ([string]::IsNullOrWhiteSpace($textureFolderPath)) {",
+ "lineNumber": 8
+ },
+ {
+ "text": " Write-Host \"Error: No path provided.\" -ForegroundColor Red",
+ "lineNumber": 9
+ },
+ {
+ "text": " exit",
+ "lineNumber": 10
+ },
+ {
+ "text": "}",
+ "lineNumber": 11
+ },
+ {
+ "lineNumber": 12
+ },
+ {
+ "text": "if (-not (Test-Path -Path $textureFolderPath -PathType Container)) {",
+ "lineNumber": 13
+ },
+ {
+ "text": " Write-Host \"Error: Path does not exist or is not a directory: $textureFolderPath\" -ForegroundColor Red",
+ "lineNumber": 14
+ },
+ {
+ "text": " exit",
+ "lineNumber": 15
+ },
+ {
+ "text": "}",
+ "lineNumber": 16
+ },
+ {
+ "lineNumber": 17
+ },
+ {
+ "text": "# Resolve the full path",
+ "lineNumber": 18
+ },
+ {
+ "text": "$textureFolderPath = (Resolve-Path $textureFolderPath).ProviderPath",
+ "lineNumber": 19
+ },
+ {
+ "text": "Write-Host \"Processing texture folder: $textureFolderPath\" -ForegroundColor Cyan",
+ "lineNumber": 20
+ },
+ {
+ "lineNumber": 21
+ },
+ {
+ "text": "# Get all files recursively, excluding the \\common folder to avoid processing already-moved files",
+ "lineNumber": 22
+ },
+ {
+ "text": "Write-Host \"Collecting files...\" -ForegroundColor Yellow",
+ "lineNumber": 23
+ },
+ {
+ "text": "$allFiles = Get-ChildItem -Path $textureFolderPath -Recurse -File | Where-Object { $_.FullName -notlike \"*\\common\\*\" }",
+ "lineNumber": 24
+ },
+ {
+ "lineNumber": 25
+ },
+ {
+ "text": "if ($null -eq $allFiles -or $allFiles.Count -eq 0) {",
+ "lineNumber": 26
+ },
+ {
+ "text": " Write-Host \"No files found to process (excluding \\common folder).\" -ForegroundColor Yellow",
+ "lineNumber": 27
+ },
+ {
+ "text": " exit",
+ "lineNumber": 28
+ },
+ {
+ "text": "}",
+ "lineNumber": 29
+ },
+ {
+ "lineNumber": 30
+ },
+ {
+ "text": "Write-Host \"Found $($allFiles.Count) files to process.\" -ForegroundColor Green",
+ "lineNumber": 31
+ },
+ {
+ "lineNumber": 32
+ },
+ {
+ "text": "# Calculate checksums for all files using parallel processing",
+ "lineNumber": 33
+ },
+ {
+ "text": "Write-Host \"Calculating checksums using parallel processing (this may take a while)...\" -ForegroundColor Yellow",
+ "lineNumber": 34
+ },
+ {
+ "text": "$throttleLimit = [Math]::Max(1, [Environment]::ProcessorCount)",
+ "lineNumber": 35
+ },
+ {
+ "text": "$parallelScriptBlock = {",
+ "lineNumber": 36
+ },
+ {
+ "text": " try {",
+ "lineNumber": 37
+ },
+ {
+ "text": " $hash = Get-FileHash -Path $_.FullName -Algorithm SHA256",
+ "lineNumber": 38
+ },
+ {
+ "text": " [PSCustomObject]@{",
+ "lineNumber": 39
+ },
+ {
+ "text": " File = $_",
+ "lineNumber": 40
+ },
+ {
+ "text": " Hash = $hash.Hash",
+ "lineNumber": 41
+ },
+ {
+ "text": " }",
+ "lineNumber": 42
+ },
+ {
+ "text": " } catch {",
+ "lineNumber": 43
+ },
+ {
+ "text": " Write-Warning \"Failed to calculate checksum for: $($_.FullName) - $($_.Exception.Message)\"",
+ "lineNumber": 44
+ },
+ {
+ "text": " $null",
+ "lineNumber": 45
+ },
+ {
+ "text": " }",
+ "lineNumber": 46
+ },
+ {
+ "text": "}",
+ "lineNumber": 47
+ },
+ {
+ "text": "$filesWithChecksums = $allFiles | ForEach-Object -Parallel $parallelScriptBlock -ThrottleLimit $throttleLimit | Where-Object { $null -ne $_ }",
+ "lineNumber": 48
+ },
+ {
+ "lineNumber": 49
+ },
+ {
+ "text": "Write-Host \"Checksum calculation complete.\" -ForegroundColor Green",
+ "lineNumber": 50
+ }
+ ]
+ },
+ "score": 0.3156466484069824
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\replace_cel_with_bsdf.py",
+ "range": {
+ "startPosition": {},
+ "endPosition": {
+ "line": 106
+ }
+ },
+ "contents": "import bpy\nimport re\nimport os\n\ndef link_bsdf_materials():\n \"\"\"Link all materials from the BSDF library file\"\"\"\n \n library_path = r\"R:\\Creative\\artsy\\maya\\0 ProjectStructure\\1 BlenderAssets\\Amazon\\MATERIALS_BSDF_pallette_v1.0.blend\"\n \n if not os.path.exists(library_path):\n print(f\"Warning: Library file not found at {library_path}\")\n return []\n \n print(f\"Linking materials from: {library_path}\")\n \n # Get list of materials before linking\n materials_before = set(bpy.data.materials.keys())\n \n # Link all materials from the library file\n with bpy.data.libraries.load(library_path, link=True) as (data_from, data_to):\n # Link all materials\n data_to.materials = data_from.materials\n \n # Get list of newly linked materials\n materials_after = set(bpy.data.materials.keys())\n newly_linked = materials_after - materials_before\n \n print(f\"Linked {len(newly_linked)} materials from library\")\n for mat_name in sorted(newly_linked):\n print(f\" - {mat_name}\")\n \n return list(newly_linked)\n\ndef remap_appended_to_linked():\n \"\"\"Remap any appended BSDF materials to their linked counterparts\"\"\"",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "import bpy",
+ "lineNumber": 1
+ },
+ {
+ "text": "import re",
+ "lineNumber": 2
+ },
+ {
+ "text": "import os",
+ "lineNumber": 3
+ },
+ {
+ "lineNumber": 4
+ },
+ {
+ "text": "def link_bsdf_materials():",
+ "lineNumber": 5,
+ "isSignature": true
+ },
+ {
+ "text": " \"\"\"Link all materials from the BSDF library file\"\"\"",
+ "lineNumber": 6
+ },
+ {
+ "text": " ",
+ "lineNumber": 7
+ },
+ {
+ "text": " library_path = r\"R:\\Creative\\artsy\\maya\\0 ProjectStructure\\1 BlenderAssets\\Amazon\\MATERIALS_BSDF_pallette_v1.0.blend\"",
+ "lineNumber": 8
+ },
+ {
+ "text": " ",
+ "lineNumber": 9
+ },
+ {
+ "text": " if not os.path.exists(library_path):",
+ "lineNumber": 10
+ },
+ {
+ "text": " print(f\"Warning: Library file not found at {library_path}\")",
+ "lineNumber": 11
+ },
+ {
+ "text": " return []",
+ "lineNumber": 12
+ },
+ {
+ "text": " ",
+ "lineNumber": 13
+ },
+ {
+ "text": " print(f\"Linking materials from: {library_path}\")",
+ "lineNumber": 14
+ },
+ {
+ "text": " ",
+ "lineNumber": 15
+ },
+ {
+ "text": " # Get list of materials before linking",
+ "lineNumber": 16
+ },
+ {
+ "text": " materials_before = set(bpy.data.materials.keys())",
+ "lineNumber": 17
+ },
+ {
+ "text": " ",
+ "lineNumber": 18
+ },
+ {
+ "text": " # Link all materials from the library file",
+ "lineNumber": 19
+ },
+ {
+ "text": " with bpy.data.libraries.load(library_path, link=True) as (data_from, data_to):",
+ "lineNumber": 20
+ },
+ {
+ "text": " # Link all materials",
+ "lineNumber": 21
+ },
+ {
+ "text": " data_to.materials = data_from.materials",
+ "lineNumber": 22
+ },
+ {
+ "text": " ",
+ "lineNumber": 23
+ },
+ {
+ "text": " # Get list of newly linked materials",
+ "lineNumber": 24
+ },
+ {
+ "text": " materials_after = set(bpy.data.materials.keys())",
+ "lineNumber": 25
+ },
+ {
+ "text": " newly_linked = materials_after - materials_before",
+ "lineNumber": 26
+ },
+ {
+ "text": " ",
+ "lineNumber": 27
+ },
+ {
+ "text": " print(f\"Linked {len(newly_linked)} materials from library\")",
+ "lineNumber": 28
+ },
+ {
+ "text": " for mat_name in sorted(newly_linked):",
+ "lineNumber": 29
+ },
+ {
+ "text": " print(f\" - {mat_name}\")",
+ "lineNumber": 30
+ },
+ {
+ "text": " ",
+ "lineNumber": 31
+ },
+ {
+ "text": " return list(newly_linked)",
+ "lineNumber": 32,
+ "isSignature": true
+ },
+ {
+ "lineNumber": 33
+ },
+ {
+ "text": "def remap_appended_to_linked():",
+ "lineNumber": 34,
+ "isSignature": true
+ },
+ {
+ "text": " \"\"\"Remap any appended BSDF materials to their linked counterparts\"\"\"",
+ "lineNumber": 35
+ }
+ ]
+ },
+ "score": 0.30952730774879456
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\UpgradeToGitProj.bat",
+ "range": {
+ "startPosition": {
+ "line": 121
+ },
+ "endPosition": {
+ "line": 166
+ }
+ },
+ "contents": " )\n )\n )\n\n if \"!foundSubmodules!\"==\"1\" (\n if \"%DRY%\"==\"1\" echo [DRY] Using submodules under Blends\\animations\n for /d %%S in (\"%animDir%\\*\") do (\n set \"name=%%~nS\"\n set \"prefix=!name:~0,6!\"\n if /I not \"!prefix!\"==\"daily_\" (\n set \"submodName=%%~nS\"\n set \"_subdir=%rendersDir%\\!submodName!\"\n rem Ensure submodule dir exists and place helper scripts there\n if \"%DRY%\"==\"1\" (\n if not exist \"!_subdir!\" echo [DRY] mkdir \"!_subdir!\"\n if exist \"%scriptDir%UpdateSequences.bat\" echo [DRY] copy \"%scriptDir%UpdateSequences.bat\" \"!_subdir!\\UpdateSequences.bat\"\n if exist \"%scriptDir%ZipSeqArchv.bat\" echo [DRY] copy \"%scriptDir%ZipSeqArchv.bat\" \"!_subdir!\\ZipSeqArchv.bat\"\n if exist \"%scriptDir%UnzipSeqArchv.bat\" echo [DRY] copy \"%scriptDir%UnzipSeqArchv.bat\" \"!_subdir!\\UnzipSeqArchv.bat\"\n ) else (\n if not exist \"!_subdir!\" mkdir \"!_subdir!\" >nul 2>&1\n if exist \"%scriptDir%UpdateSequences.bat\" copy /Y \"%scriptDir%UpdateSequences.bat\" \"!_subdir!\\UpdateSequences.bat\" >nul\n if exist \"%scriptDir%ZipSeqArchv.bat\" copy /Y \"%scriptDir%ZipSeqArchv.bat\" \"!_subdir!\\ZipSeqArchv.bat\" >nul\n if exist \"%scriptDir%UnzipSeqArchv.bat\" copy /Y \"%scriptDir%UnzipSeqArchv.bat\" \"!_subdir!\\UnzipSeqArchv.bat\" >nul\n )\n for /d %%D in (\"%%S\\daily_*\") do (\n set \"dailyName=%%~nD\"\n set \"_src=%%D\\seq\"\n set \"_dst=%rendersDir%\\!submodName!\\!dailyName!\"\n set /a debugTotal+=1\n if \"%DRY%\"==\"1\" (\n if exist \"!_src!\" (\n echo [DRY] WOULD copy \"!_src!\" -^> \"!_dst!\"\n set /a debugPlanned+=1\n ) else (\n echo [DRY] Skip: missing \"!_src!\"\n set /a debugMissing+=1\n )\n )\n call :CopySeqToRenders \"!_src!\" \"!_dst!\"\n )\n )\n )\n set \"foundAny=1\"\n ) else (\n if \"%DRY%\"==\"1\" echo [DRY] No submodules found; using direct daily_* under animations",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": " )",
+ "lineNumber": 122
+ },
+ {
+ "text": " )",
+ "lineNumber": 123
+ },
+ {
+ "text": " )",
+ "lineNumber": 124
+ },
+ {
+ "lineNumber": 125
+ },
+ {
+ "text": " if \"!foundSubmodules!\"==\"1\" (",
+ "lineNumber": 126
+ },
+ {
+ "text": " if \"%DRY%\"==\"1\" echo [DRY] Using submodules under Blends\\animations",
+ "lineNumber": 127
+ },
+ {
+ "text": " for /d %%S in (\"%animDir%\\*\") do (",
+ "lineNumber": 128
+ },
+ {
+ "text": " set \"name=%%~nS\"",
+ "lineNumber": 129
+ },
+ {
+ "text": " set \"prefix=!name:~0,6!\"",
+ "lineNumber": 130
+ },
+ {
+ "text": " if /I not \"!prefix!\"==\"daily_\" (",
+ "lineNumber": 131
+ },
+ {
+ "text": " set \"submodName=%%~nS\"",
+ "lineNumber": 132
+ },
+ {
+ "text": " set \"_subdir=%rendersDir%\\!submodName!\"",
+ "lineNumber": 133
+ },
+ {
+ "text": " rem Ensure submodule dir exists and place helper scripts there",
+ "lineNumber": 134
+ },
+ {
+ "text": " if \"%DRY%\"==\"1\" (",
+ "lineNumber": 135
+ },
+ {
+ "text": " if not exist \"!_subdir!\" echo [DRY] mkdir \"!_subdir!\"",
+ "lineNumber": 136
+ },
+ {
+ "text": " if exist \"%scriptDir%UpdateSequences.bat\" echo [DRY] copy \"%scriptDir%UpdateSequences.bat\" \"!_subdir!\\UpdateSequences.bat\"",
+ "lineNumber": 137
+ },
+ {
+ "text": " if exist \"%scriptDir%ZipSeqArchv.bat\" echo [DRY] copy \"%scriptDir%ZipSeqArchv.bat\" \"!_subdir!\\ZipSeqArchv.bat\"",
+ "lineNumber": 138
+ },
+ {
+ "text": " if exist \"%scriptDir%UnzipSeqArchv.bat\" echo [DRY] copy \"%scriptDir%UnzipSeqArchv.bat\" \"!_subdir!\\UnzipSeqArchv.bat\"",
+ "lineNumber": 139
+ },
+ {
+ "text": " ) else (",
+ "lineNumber": 140
+ },
+ {
+ "text": " if not exist \"!_subdir!\" mkdir \"!_subdir!\" >nul 2>&1",
+ "lineNumber": 141
+ },
+ {
+ "text": " if exist \"%scriptDir%UpdateSequences.bat\" copy /Y \"%scriptDir%UpdateSequences.bat\" \"!_subdir!\\UpdateSequences.bat\" >nul",
+ "lineNumber": 142
+ },
+ {
+ "text": " if exist \"%scriptDir%ZipSeqArchv.bat\" copy /Y \"%scriptDir%ZipSeqArchv.bat\" \"!_subdir!\\ZipSeqArchv.bat\" >nul",
+ "lineNumber": 143
+ },
+ {
+ "text": " if exist \"%scriptDir%UnzipSeqArchv.bat\" copy /Y \"%scriptDir%UnzipSeqArchv.bat\" \"!_subdir!\\UnzipSeqArchv.bat\" >nul",
+ "lineNumber": 144
+ },
+ {
+ "text": " )",
+ "lineNumber": 145
+ },
+ {
+ "text": " for /d %%D in (\"%%S\\daily_*\") do (",
+ "lineNumber": 146
+ },
+ {
+ "text": " set \"dailyName=%%~nD\"",
+ "lineNumber": 147
+ },
+ {
+ "text": " set \"_src=%%D\\seq\"",
+ "lineNumber": 148
+ },
+ {
+ "text": " set \"_dst=%rendersDir%\\!submodName!\\!dailyName!\"",
+ "lineNumber": 149
+ },
+ {
+ "text": " set /a debugTotal+=1",
+ "lineNumber": 150
+ },
+ {
+ "text": " if \"%DRY%\"==\"1\" (",
+ "lineNumber": 151
+ },
+ {
+ "text": " if exist \"!_src!\" (",
+ "lineNumber": 152
+ },
+ {
+ "text": " echo [DRY] WOULD copy \"!_src!\" -^> \"!_dst!\"",
+ "lineNumber": 153
+ },
+ {
+ "text": " set /a debugPlanned+=1",
+ "lineNumber": 154
+ },
+ {
+ "text": " ) else (",
+ "lineNumber": 155
+ },
+ {
+ "text": " echo [DRY] Skip: missing \"!_src!\"",
+ "lineNumber": 156
+ },
+ {
+ "text": " set /a debugMissing+=1",
+ "lineNumber": 157
+ },
+ {
+ "text": " )",
+ "lineNumber": 158
+ },
+ {
+ "text": " )",
+ "lineNumber": 159
+ },
+ {
+ "text": " call :CopySeqToRenders \"!_src!\" \"!_dst!\"",
+ "lineNumber": 160
+ },
+ {
+ "text": " )",
+ "lineNumber": 161
+ },
+ {
+ "text": " )",
+ "lineNumber": 162
+ },
+ {
+ "text": " )",
+ "lineNumber": 163
+ },
+ {
+ "text": " set \"foundAny=1\"",
+ "lineNumber": 164
+ },
+ {
+ "text": " ) else (",
+ "lineNumber": 165
+ },
+ {
+ "text": " if \"%DRY%\"==\"1\" echo [DRY] No submodules found; using direct daily_* under animations",
+ "lineNumber": 166
+ }
+ ]
+ },
+ "score": 0.2996269464492798
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\UpgradeToGitProj.bat",
+ "range": {
+ "startPosition": {
+ "line": 58
+ },
+ "endPosition": {
+ "line": 126,
+ "column": 4
+ }
+ },
+ "contents": "for %%F in (ConfigLoader.ps1 config.json) do (\n if exist \"%scriptDir%%%F\" (\n if \"%DRY%\"==\"1\" (\n echo [DRY] copy \"%scriptDir%%%F\" \"%rendersDir%\\%%F\"\n ) else (\n copy /Y \"%scriptDir%%%F\" \"%rendersDir%\\%%F\" >nul\n )\n ) else (\n echo [WARN] Missing template: \"%scriptDir%%%F\"\n )\n)\n\n:: -----------------------------\n:: Merge .gitignore and .gitattributes from templates\n:: -----------------------------\nset \"tplGitIgnore=%scriptDir%components\\gitignore\"\nset \"dstGitIgnore=%projectDir%\\.gitignore\"\nset \"tplGitAttr=%scriptDir%components\\gitattributes\"\nset \"dstGitAttr=%projectDir%\\.gitattributes\"\n\ncall :MergeTemplate \"%tplGitIgnore%\" \"%dstGitIgnore%\"\ncall :MergeTemplate \"%tplGitAttr%\" \"%dstGitAttr%\"\n\n:: -----------------------------\n:: Initialize git and Git LFS\n:: -----------------------------\nif not exist \"%projectDir%\\.git\" (\n if \"%DRY%\"==\"1\" (\n echo [DRY] git init\n ) else (\n pushd \"%projectDir%\" >nul\n git init\n popd >nul\n )\n)\n\nif \"%DRY%\"==\"1\" (\n echo [DRY] git lfs install\n) else (\n pushd \"%projectDir%\" >nul\n git lfs install\n popd >nul\n)\n\n:: -----------------------------\n:: Collect seq outputs from daily_* into Renders\n:: -----------------------------\nset \"animDir=%projectDir%\\Blends\\animations\"\nset \"foundAny=0\"\nset \"foundSubmodules=0\"\n\nif exist \"%animDir%\" (\n if \"%DRY%\"==\"1\" echo [DRY] Scanning animations dir: \"%animDir%\"\n :: Detect submodules: first-level folders under animations that contain daily_*\n for /d %%S in (\"%animDir%\\*\") do (\n set \"name=%%~nS\"\n set \"prefix=!name:~0,6!\"\n if /I not \"!prefix!\"==\"daily_\" (\n for /d %%D in (\"%%S\\daily_*\") do (\n set \"foundSubmodules=1\"\n )\n if \"%DRY%\"==\"1\" (\n if exist \"%%S\\daily_*\" echo [DRY] Detected submodule: \"%%~nS\"\n )\n )\n )\n\n if \"!foundSubmodules!\"==\"1\" (\n ",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "for %%F in (ConfigLoader.ps1 config.json) do (",
+ "lineNumber": 59
+ },
+ {
+ "text": " if exist \"%scriptDir%%%F\" (",
+ "lineNumber": 60
+ },
+ {
+ "text": " if \"%DRY%\"==\"1\" (",
+ "lineNumber": 61
+ },
+ {
+ "text": " echo [DRY] copy \"%scriptDir%%%F\" \"%rendersDir%\\%%F\"",
+ "lineNumber": 62
+ },
+ {
+ "text": " ) else (",
+ "lineNumber": 63
+ },
+ {
+ "text": " copy /Y \"%scriptDir%%%F\" \"%rendersDir%\\%%F\" >nul",
+ "lineNumber": 64
+ },
+ {
+ "text": " )",
+ "lineNumber": 65
+ },
+ {
+ "text": " ) else (",
+ "lineNumber": 66
+ },
+ {
+ "text": " echo [WARN] Missing template: \"%scriptDir%%%F\"",
+ "lineNumber": 67
+ },
+ {
+ "text": " )",
+ "lineNumber": 68
+ },
+ {
+ "text": ")",
+ "lineNumber": 69
+ },
+ {
+ "lineNumber": 70
+ },
+ {
+ "text": ":: -----------------------------",
+ "lineNumber": 71
+ },
+ {
+ "text": ":: Merge .gitignore and .gitattributes from templates",
+ "lineNumber": 72
+ },
+ {
+ "text": ":: -----------------------------",
+ "lineNumber": 73
+ },
+ {
+ "text": "set \"tplGitIgnore=%scriptDir%components\\gitignore\"",
+ "lineNumber": 74
+ },
+ {
+ "text": "set \"dstGitIgnore=%projectDir%\\.gitignore\"",
+ "lineNumber": 75
+ },
+ {
+ "text": "set \"tplGitAttr=%scriptDir%components\\gitattributes\"",
+ "lineNumber": 76
+ },
+ {
+ "text": "set \"dstGitAttr=%projectDir%\\.gitattributes\"",
+ "lineNumber": 77
+ },
+ {
+ "lineNumber": 78
+ },
+ {
+ "text": "call :MergeTemplate \"%tplGitIgnore%\" \"%dstGitIgnore%\"",
+ "lineNumber": 79
+ },
+ {
+ "text": "call :MergeTemplate \"%tplGitAttr%\" \"%dstGitAttr%\"",
+ "lineNumber": 80
+ },
+ {
+ "lineNumber": 81
+ },
+ {
+ "text": ":: -----------------------------",
+ "lineNumber": 82
+ },
+ {
+ "text": ":: Initialize git and Git LFS",
+ "lineNumber": 83
+ },
+ {
+ "text": ":: -----------------------------",
+ "lineNumber": 84
+ },
+ {
+ "text": "if not exist \"%projectDir%\\.git\" (",
+ "lineNumber": 85
+ },
+ {
+ "text": " if \"%DRY%\"==\"1\" (",
+ "lineNumber": 86
+ },
+ {
+ "text": " echo [DRY] git init",
+ "lineNumber": 87
+ },
+ {
+ "text": " ) else (",
+ "lineNumber": 88
+ },
+ {
+ "text": " pushd \"%projectDir%\" >nul",
+ "lineNumber": 89
+ },
+ {
+ "text": " git init",
+ "lineNumber": 90
+ },
+ {
+ "text": " popd >nul",
+ "lineNumber": 91
+ },
+ {
+ "text": " )",
+ "lineNumber": 92
+ },
+ {
+ "text": ")",
+ "lineNumber": 93
+ },
+ {
+ "lineNumber": 94
+ },
+ {
+ "text": "if \"%DRY%\"==\"1\" (",
+ "lineNumber": 95
+ },
+ {
+ "text": " echo [DRY] git lfs install",
+ "lineNumber": 96
+ },
+ {
+ "text": ") else (",
+ "lineNumber": 97
+ },
+ {
+ "text": " pushd \"%projectDir%\" >nul",
+ "lineNumber": 98
+ },
+ {
+ "text": " git lfs install",
+ "lineNumber": 99
+ },
+ {
+ "text": " popd >nul",
+ "lineNumber": 100
+ },
+ {
+ "text": ")",
+ "lineNumber": 101
+ },
+ {
+ "lineNumber": 102
+ },
+ {
+ "text": ":: -----------------------------",
+ "lineNumber": 103
+ },
+ {
+ "text": ":: Collect seq outputs from daily_* into Renders",
+ "lineNumber": 104
+ },
+ {
+ "text": ":: -----------------------------",
+ "lineNumber": 105
+ },
+ {
+ "text": "set \"animDir=%projectDir%\\Blends\\animations\"",
+ "lineNumber": 106
+ },
+ {
+ "text": "set \"foundAny=0\"",
+ "lineNumber": 107
+ },
+ {
+ "text": "set \"foundSubmodules=0\"",
+ "lineNumber": 108
+ },
+ {
+ "lineNumber": 109
+ },
+ {
+ "text": "if exist \"%animDir%\" (",
+ "lineNumber": 110
+ },
+ {
+ "text": " if \"%DRY%\"==\"1\" echo [DRY] Scanning animations dir: \"%animDir%\"",
+ "lineNumber": 111
+ },
+ {
+ "text": " :: Detect submodules: first-level folders under animations that contain daily_*",
+ "lineNumber": 112
+ },
+ {
+ "text": " for /d %%S in (\"%animDir%\\*\") do (",
+ "lineNumber": 113
+ },
+ {
+ "text": " set \"name=%%~nS\"",
+ "lineNumber": 114
+ },
+ {
+ "text": " set \"prefix=!name:~0,6!\"",
+ "lineNumber": 115
+ },
+ {
+ "text": " if /I not \"!prefix!\"==\"daily_\" (",
+ "lineNumber": 116
+ },
+ {
+ "text": " for /d %%D in (\"%%S\\daily_*\") do (",
+ "lineNumber": 117
+ },
+ {
+ "text": " set \"foundSubmodules=1\"",
+ "lineNumber": 118
+ },
+ {
+ "text": " )",
+ "lineNumber": 119
+ },
+ {
+ "text": " if \"%DRY%\"==\"1\" (",
+ "lineNumber": 120
+ },
+ {
+ "text": " if exist \"%%S\\daily_*\" echo [DRY] Detected submodule: \"%%~nS\"",
+ "lineNumber": 121
+ },
+ {
+ "text": " )",
+ "lineNumber": 122
+ },
+ {
+ "text": " )",
+ "lineNumber": 123
+ },
+ {
+ "text": " )",
+ "lineNumber": 124
+ },
+ {
+ "lineNumber": 125
+ },
+ {
+ "text": " if \"!foundSubmodules!\"==\"1\" (",
+ "lineNumber": 126
+ },
+ {
+ "text": " ",
+ "lineNumber": 127
+ }
+ ]
+ },
+ "score": 0.29278281331062317
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\replace_cel_with_bsdf.py",
+ "range": {
+ "startPosition": {
+ "line": 186,
+ "column": 26
+ },
+ "endPosition": {
+ "line": 355
+ }
+ },
+ "contents": "def replace_cel_materials():\n \"\"\"Replace all CEL materials with their BSDF counterparts using Blender's user remapping\"\"\"\n \n # First, link BSDF materials from library\n linked_materials = link_bsdf_materials()\n \n # Then, remap any missing datablocks\n missing_remaps = remap_missing_datablocks()\n \n # Then, remap any appended BSDF materials to linked versions\n appended_remaps = remap_appended_to_linked()\n \n print(f\"\\n=== STARTING MATERIAL REPLACEMENT ===\")\n \n # Custom material mappings (source -> target)\n custom_mappings = {\n \"bag BLACK (squid ink)\": \"BSDF_black_SQUID-INK\",\n \"bag WHITE\": \"BSDF_WHITE\",\n \"Wheel-White\": \"BSDF_WHITE\",\n \"Bag Colors\": \"BSDF_Bag Colors\",\n \"cardboard\": \"Package_Cardboard\",\n \"blue (triton)\": \"BSDF_blue-2_TRITON\",\n \"gray (snow)\": \"BSDF_gray-6_SNOW\",\n \"gray (storm)\": \"BSDF_gray-2_STORM\",\n \"gray (summit)\": \"BSDF_gray-5_SUMMIT\",\n \"light blue (prime)\": \"BSDF_blue-4_PRIME\",\n \"yellow (summer)\": \"BSDF_orange-5_SUMMER\",\n \"Accessory_CEL_gray-6_SNOW\": \"BSDF_gray-6_SNOW\",\n \"Accessory_CEL_SquidInk\": \"BSDF_black_SQUID-INK\",\n \"FingerScanner\": \"BSDF_black_SQUID-INK\",\n \"cel BLACK (squid ink)\": \"BSDF_black_SQUID-INK\",\n \"cel WHITE\": \"BSDF_WHITE\",\n \"gray (stone)\": \"BSDF_gray-3_STONE\",\n \"green (oxygen)\": \"BSDF_green-3_OXYGEN\",\n \"orange (smile)\": \"BSDF_orange-3_SMILE\",\n \"orange (blaze)\": \"BSDF_orange-1_BLAZE\"\n }\n \n # Get all materials in the scene\n materials = bpy.data.materials\n \n # Dictionary to store source -> target material mapping\n material_mapping = {}\n \n # Replace all CEL materials with their BSDF counterparts, ignoring numeric suffixes\n cel_pattern = re.compile(r\"^(CEL_.+?)(\\.\\d{3})?$\")\n bsdf_pattern = re.compile(r\"^(BSDF_.+?)(\\.\\d{3})?$\")\n \n # Build a mapping from base BSDF name to BSDF material (without suffix)",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "def replace_cel_materials():",
+ "lineNumber": 189,
+ "isSignature": true
+ },
+ {
+ "text": " \"\"\"Replace all CEL materials with their BSDF counterparts using Blender's user remapping\"\"\"",
+ "lineNumber": 190
+ },
+ {
+ "text": " ",
+ "lineNumber": 191
+ },
+ {
+ "text": " # First, link BSDF materials from library",
+ "lineNumber": 192
+ },
+ {
+ "text": " linked_materials = link_bsdf_materials()",
+ "lineNumber": 193
+ },
+ {
+ "text": " ",
+ "lineNumber": 194
+ },
+ {
+ "text": " # Then, remap any missing datablocks",
+ "lineNumber": 195
+ },
+ {
+ "text": " missing_remaps = remap_missing_datablocks()",
+ "lineNumber": 196
+ },
+ {
+ "text": " ",
+ "lineNumber": 197
+ },
+ {
+ "text": " # Then, remap any appended BSDF materials to linked versions",
+ "lineNumber": 198
+ },
+ {
+ "text": " appended_remaps = remap_appended_to_linked()",
+ "lineNumber": 199
+ },
+ {
+ "text": " ",
+ "lineNumber": 200
+ },
+ {
+ "text": " print(f\"\\n=== STARTING MATERIAL REPLACEMENT ===\")",
+ "lineNumber": 201
+ },
+ {
+ "text": " ",
+ "lineNumber": 202
+ },
+ {
+ "text": " # Custom material mappings (source -> target)",
+ "lineNumber": 203
+ },
+ {
+ "text": " custom_mappings = {",
+ "lineNumber": 204
+ },
+ {
+ "text": " \"bag BLACK (squid ink)\": \"BSDF_black_SQUID-INK\",",
+ "lineNumber": 205
+ },
+ {
+ "text": " \"bag WHITE\": \"BSDF_WHITE\",",
+ "lineNumber": 206
+ },
+ {
+ "text": " \"Wheel-White\": \"BSDF_WHITE\",",
+ "lineNumber": 207
+ },
+ {
+ "text": " \"Bag Colors\": \"BSDF_Bag Colors\",",
+ "lineNumber": 208
+ },
+ {
+ "text": " \"cardboard\": \"Package_Cardboard\",",
+ "lineNumber": 209
+ },
+ {
+ "text": " \"blue (triton)\": \"BSDF_blue-2_TRITON\",",
+ "lineNumber": 210
+ },
+ {
+ "text": " \"gray (snow)\": \"BSDF_gray-6_SNOW\",",
+ "lineNumber": 211
+ },
+ {
+ "text": " \"gray (storm)\": \"BSDF_gray-2_STORM\",",
+ "lineNumber": 212
+ },
+ {
+ "text": " \"gray (summit)\": \"BSDF_gray-5_SUMMIT\",",
+ "lineNumber": 213
+ },
+ {
+ "text": " \"light blue (prime)\": \"BSDF_blue-4_PRIME\",",
+ "lineNumber": 214
+ },
+ {
+ "text": " \"yellow (summer)\": \"BSDF_orange-5_SUMMER\",",
+ "lineNumber": 215
+ },
+ {
+ "text": " \"Accessory_CEL_gray-6_SNOW\": \"BSDF_gray-6_SNOW\",",
+ "lineNumber": 216
+ },
+ {
+ "text": " \"Accessory_CEL_SquidInk\": \"BSDF_black_SQUID-INK\",",
+ "lineNumber": 217
+ },
+ {
+ "text": " \"FingerScanner\": \"BSDF_black_SQUID-INK\",",
+ "lineNumber": 218
+ },
+ {
+ "text": " \"cel BLACK (squid ink)\": \"BSDF_black_SQUID-INK\",",
+ "lineNumber": 219
+ },
+ {
+ "text": " \"cel WHITE\": \"BSDF_WHITE\",",
+ "lineNumber": 220
+ },
+ {
+ "text": " \"gray (stone)\": \"BSDF_gray-3_STONE\",",
+ "lineNumber": 221
+ },
+ {
+ "text": " \"green (oxygen)\": \"BSDF_green-3_OXYGEN\",",
+ "lineNumber": 222
+ },
+ {
+ "text": " \"orange (smile)\": \"BSDF_orange-3_SMILE\",",
+ "lineNumber": 223
+ },
+ {
+ "text": " \"orange (blaze)\": \"BSDF_orange-1_BLAZE\"",
+ "lineNumber": 224
+ },
+ {
+ "text": " }",
+ "lineNumber": 225
+ },
+ {
+ "text": " ",
+ "lineNumber": 226
+ },
+ {
+ "text": " # Get all materials in the scene",
+ "lineNumber": 227
+ },
+ {
+ "text": " materials = bpy.data.materials",
+ "lineNumber": 228
+ },
+ {
+ "text": " ",
+ "lineNumber": 229
+ },
+ {
+ "text": " # Dictionary to store source -> target material mapping",
+ "lineNumber": 230
+ },
+ {
+ "text": " material_mapping = {}",
+ "lineNumber": 231
+ },
+ {
+ "text": " ",
+ "lineNumber": 232
+ },
+ {
+ "text": " # Replace all CEL materials with their BSDF counterparts, ignoring numeric suffixes",
+ "lineNumber": 233
+ },
+ {
+ "text": " cel_pattern = re.compile(r\"^(CEL_.+?)(\\.\\d{3})?$\")",
+ "lineNumber": 234
+ },
+ {
+ "text": " bsdf_pattern = re.compile(r\"^(BSDF_.+?)(\\.\\d{3})?$\")",
+ "lineNumber": 235
+ },
+ {
+ "text": " ",
+ "lineNumber": 236
+ },
+ {
+ "text": " # Build a mapping from base BSDF name to BSDF material (without suffix)",
+ "lineNumber": 237
+ }
+ ]
+ },
+ "score": 0.2842274010181427
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\CONFIG.md",
+ "range": {
+ "startPosition": {},
+ "endPosition": {
+ "line": 24
+ }
+ },
+ "contents": "# ProjectStructure Configuration\n\nThe repository reads user preferences from `config.json` in the ProjectStructure\nroot (copied alongside helper batches when they are deployed to projects).\n\n## Keys\n\n| Key | Type | Meaning |\n| --- | --- | --- |\n| `dailyFormat` | bool | `true` → daily folders named `YYYY-MM-DD`; `false` → `daily_*` style. |\n| `structDir` | string | Absolute or relative path to the canonical ProjectStructure directory. Blank values default to the folder containing `config.json`. |\n| `projectsRoot` | string (optional) | Override for the root directory scanned by `UpgradeSeqBatches.ps1`. Defaults to the parent of `structDir`. |\n| `zipper` | bool | `true` → use 7‑Zip (if available); `false` → use Python’s built-in zipfile module. |\n| `compression` | int | Compression level `0-9` (0 = store only, 9 = max). Applies to both zipfile and 7‑Zip. |\n\n## Notes\n\n- `UpdateProjectBatches.ps1` copies `config.json` and `ConfigLoader.ps1` to the\n target project so the helper `.bat` launchers can resolve script locations.\n- When `zipper` is `true`, the tool searches for `7z`/`7za` on `PATH`. If neither\n is found it logs a warning and falls back to `zipfile`.\n- Leaving `structDir` empty is safe—the scripts fall back to the directory that\n contains `config.json`.",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "# ProjectStructure Configuration",
+ "lineNumber": 1
+ },
+ {
+ "lineNumber": 2
+ },
+ {
+ "text": "The repository reads user preferences from `config.json` in the ProjectStructure",
+ "lineNumber": 3
+ },
+ {
+ "text": "root (copied alongside helper batches when they are deployed to projects).",
+ "lineNumber": 4
+ },
+ {
+ "lineNumber": 5
+ },
+ {
+ "text": "## Keys",
+ "lineNumber": 6
+ },
+ {
+ "lineNumber": 7
+ },
+ {
+ "text": "| Key | Type | Meaning |",
+ "lineNumber": 8
+ },
+ {
+ "text": "| --- | --- | --- |",
+ "lineNumber": 9
+ },
+ {
+ "text": "| `dailyFormat` | bool | `true` → daily folders named `YYYY-MM-DD`; `false` → `daily_*` style. |",
+ "lineNumber": 10
+ },
+ {
+ "text": "| `structDir` | string | Absolute or relative path to the canonical ProjectStructure directory. Blank values default to the folder containing `config.json`. |",
+ "lineNumber": 11
+ },
+ {
+ "text": "| `projectsRoot` | string (optional) | Override for the root directory scanned by `UpgradeSeqBatches.ps1`. Defaults to the parent of `structDir`. |",
+ "lineNumber": 12
+ },
+ {
+ "text": "| `zipper` | bool | `true` → use 7‑Zip (if available); `false` → use Python’s built-in zipfile module. |",
+ "lineNumber": 13
+ },
+ {
+ "text": "| `compression` | int | Compression level `0-9` (0 = store only, 9 = max). Applies to both zipfile and 7‑Zip. |",
+ "lineNumber": 14
+ },
+ {
+ "lineNumber": 15
+ },
+ {
+ "text": "## Notes",
+ "lineNumber": 16
+ },
+ {
+ "lineNumber": 17
+ },
+ {
+ "text": "- `UpdateProjectBatches.ps1` copies `config.json` and `ConfigLoader.ps1` to the",
+ "lineNumber": 18
+ },
+ {
+ "text": " target project so the helper `.bat` launchers can resolve script locations.",
+ "lineNumber": 19
+ },
+ {
+ "text": "- When `zipper` is `true`, the tool searches for `7z`/`7za` on `PATH`. If neither",
+ "lineNumber": 20
+ },
+ {
+ "text": " is found it logs a warning and falls back to `zipfile`.",
+ "lineNumber": 21
+ },
+ {
+ "text": "- Leaving `structDir` empty is safe—the scripts fall back to the directory that",
+ "lineNumber": 22
+ },
+ {
+ "text": " contains `config.json`.",
+ "lineNumber": 23
+ }
+ ]
+ },
+ "score": 0.2787865400314331
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\components\\gitattributes",
+ "range": {
+ "startPosition": {
+ "line": 51
+ },
+ "endPosition": {
+ "line": 111,
+ "column": 40
+ }
+ },
+ "contents": "*.hiplc filter=lfs diff=lfs merge=lfs -text\n*.bgeo filter=lfs diff=lfs merge=lfs -text\n*.bgeo.sc filter=lfs diff=lfs merge=lfs -text\n\n# Flip Fluids cache files\n*.data filter=lfs diff=lfs merge=lfs -text\n*.sqlite3 filter=lfs diff=lfs merge=lfs -text\n*.ffp3 filter=lfs diff=lfs merge=lfs -text\n\n# Substance files\n*.sbs filter=lfs diff=lfs merge=lfs -text\n*.sbsar filter=lfs diff=lfs merge=lfs -text\n*.spp filter=lfs diff=lfs merge=lfs -text\n\n# Audio files\n*.mp3 filter=lfs diff=lfs merge=lfs -text\n*.wav filter=lfs diff=lfs merge=lfs -text\n\n# Archive/Compression files\n*.7z filter=lfs diff=lfs merge=lfs -text\n*.bz2 filter=lfs diff=lfs merge=lfs -text\n*.gz filter=lfs diff=lfs merge=lfs -text\n*.rar filter=lfs diff=lfs merge=lfs -text\n*.tar filter=lfs diff=lfs merge=lfs -text\n*.zip filter=lfs diff=lfs merge=lfs -text\n\n# Documents\n*.docx filter=lfs diff=lfs merge=lfs -text\n*.xlsx filter=lfs diff=lfs merge=lfs -text\n*.eps filter=lfs diff=lfs merge=lfs -text\n*.pdf filter=lfs diff=lfs merge=lfs -text\n\n# Adobe files\n*.aegraphic filter=lfs diff=lfs merge=lfs -text\n*.aep filter=lfs diff=lfs merge=lfs -text\n*.prel filter=lfs diff=lfs merge=lfs -text\n*.prin filter=lfs diff=lfs merge=lfs -text\n*.prmf filter=lfs diff=lfs merge=lfs -text\n*.prproj filter=lfs diff=lfs merge=lfs -text\n*.ai filter=lfs diff=lfs merge=lfs -text\n*.psd filter=lfs diff=lfs merge=lfs -text\n\n# Davinci Resolve files\n*.dpx filter=lfs diff=lfs merge=lfs -text\n*.drp filter=lfs diff=lfs merge=lfs -text\n\n# Data files\n*.h5 filter=lfs diff=lfs merge=lfs -text\n*.hdf5 filter=lfs diff=lfs merge=lfs -text\n\n# Other\n*.uni filter=lfs diff=lfs merge=lfs -text\n*.pdn filter=lfs diff=lfs merge=lfs -text\n*.pur filter=lfs diff=lfs merge=lfs -text\n\n# Python files\n*.npz filter=lfs diff=lfs merge=lfs -text\n\n# Adobe Alternatives\n*.xcf filter=lfs diff=lfs merge=lfs -text\n*.af filter=lfs diff=lfs merge=lfs -text",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "*.hiplc filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 52
+ },
+ {
+ "text": "*.bgeo filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 53
+ },
+ {
+ "text": "*.bgeo.sc filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 54
+ },
+ {
+ "lineNumber": 55
+ },
+ {
+ "text": "# Flip Fluids cache files",
+ "lineNumber": 56
+ },
+ {
+ "text": "*.data filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 57
+ },
+ {
+ "text": "*.sqlite3 filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 58
+ },
+ {
+ "text": "*.ffp3 filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 59
+ },
+ {
+ "lineNumber": 60
+ },
+ {
+ "text": "# Substance files",
+ "lineNumber": 61
+ },
+ {
+ "text": "*.sbs filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 62
+ },
+ {
+ "text": "*.sbsar filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 63
+ },
+ {
+ "text": "*.spp filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 64
+ },
+ {
+ "lineNumber": 65
+ },
+ {
+ "text": "# Audio files",
+ "lineNumber": 66
+ },
+ {
+ "text": "*.mp3 filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 67
+ },
+ {
+ "text": "*.wav filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 68
+ },
+ {
+ "lineNumber": 69
+ },
+ {
+ "text": "# Archive/Compression files",
+ "lineNumber": 70
+ },
+ {
+ "text": "*.7z filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 71
+ },
+ {
+ "text": "*.bz2 filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 72
+ },
+ {
+ "text": "*.gz filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 73
+ },
+ {
+ "text": "*.rar filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 74
+ },
+ {
+ "text": "*.tar filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 75
+ },
+ {
+ "text": "*.zip filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 76
+ },
+ {
+ "lineNumber": 77
+ },
+ {
+ "text": "# Documents",
+ "lineNumber": 78
+ },
+ {
+ "text": "*.docx filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 79
+ },
+ {
+ "text": "*.xlsx filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 80
+ },
+ {
+ "text": "*.eps filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 81
+ },
+ {
+ "text": "*.pdf filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 82
+ },
+ {
+ "lineNumber": 83
+ },
+ {
+ "text": "# Adobe files",
+ "lineNumber": 84
+ },
+ {
+ "text": "*.aegraphic filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 85
+ },
+ {
+ "text": "*.aep filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 86
+ },
+ {
+ "text": "*.prel filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 87
+ },
+ {
+ "text": "*.prin filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 88
+ },
+ {
+ "text": "*.prmf filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 89
+ },
+ {
+ "text": "*.prproj filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 90
+ },
+ {
+ "text": "*.ai filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 91
+ },
+ {
+ "text": "*.psd filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 92
+ },
+ {
+ "lineNumber": 93
+ },
+ {
+ "text": "# Davinci Resolve files",
+ "lineNumber": 94
+ },
+ {
+ "text": "*.dpx filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 95
+ },
+ {
+ "text": "*.drp filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 96
+ },
+ {
+ "lineNumber": 97
+ },
+ {
+ "text": "# Data files",
+ "lineNumber": 98
+ },
+ {
+ "text": "*.h5 filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 99
+ },
+ {
+ "text": "*.hdf5 filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 100
+ },
+ {
+ "lineNumber": 101
+ },
+ {
+ "text": "# Other",
+ "lineNumber": 102
+ },
+ {
+ "text": "*.uni filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 103
+ },
+ {
+ "text": "*.pdn filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 104
+ },
+ {
+ "text": "*.pur filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 105
+ },
+ {
+ "lineNumber": 106
+ },
+ {
+ "text": "# Python files",
+ "lineNumber": 107
+ },
+ {
+ "text": "*.npz filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 108
+ },
+ {
+ "lineNumber": 109
+ },
+ {
+ "text": "# Adobe Alternatives",
+ "lineNumber": 110
+ },
+ {
+ "text": "*.xcf filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 111
+ },
+ {
+ "text": "*.af filter=lfs diff=lfs merge=lfs -text",
+ "lineNumber": 112
+ }
+ ]
+ },
+ "score": 0.27743393182754517
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\compress_blend_files.bat",
+ "range": {
+ "startPosition": {},
+ "endPosition": {
+ "line": 66
+ }
+ },
+ "contents": "@echo off\nsetlocal enabledelayedexpansion\n\necho Starting Blender file compression...\n\nREM Check if files were dragged onto the batch file\nif \"%~1\"==\"\" (\n echo.\n echo Usage: Drag .blend files onto this batch file to compress them.\n echo Output will be saved to the \\output folder.\n echo.\n pause\n exit /b 1\n)\n\nREM Create output directory if it doesn't exist\nif not exist \"output\" mkdir \"output\"\n\nREM Initialize counters and tracking\nset \"processed=0\"\nset \"crashed=0\"\nset \"skipped=0\"\nset \"deleted_similar=0\"\nset \"threshold_percent=10\"\n\nREM Create a temporary Python script for compression\nset \"TEMP_SCRIPT=compress_blend_temp.py\"\necho Creating Python script: !TEMP_SCRIPT!\n\n(\necho import bpy\necho import os\necho import sys\necho.\necho # Enable compression globally\necho bpy.context.preferences.filepaths.save_version = 0\necho bpy.context.preferences.filepaths.use_file_compression = True\necho.\necho # Create output directory if it doesn't exist\necho if not os.path.exists^(\"output\"^):\necho os.makedirs^(\"output\"^)\necho.\necho # Get command line arguments ^(the dragged files^)\necho blend_files = sys.argv[sys.argv.index^(\"--\"^) + 1:] if \"--\" in sys.argv else []\necho.\necho print^(\"Found \" + str^(len^(blend_files^)^) + \" .blend files to compress\"^)\necho.\necho def is_compressed_file^(path^):\necho try:\necho with open^(path, 'rb'^) as f:\necho header = f.read^(4^)\necho if header[:2] == b'\\x1f\\x8b': # gzip\necho return True\necho if header == b'\\x28\\xb5\\x2f\\xfd': # zstd\necho return True\necho return False\necho except Exception:\necho return False\necho.\necho for blend_file in blend_files:\necho if blend_file.lower^(^).endswith^(\".blend\"^):\necho try:\necho # Create output path in output folder\necho filename = os.path.basename^(blend_file^)\necho output_file = os.path.join^(\"output\", filename^)\necho # Check if output file already exists",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "@echo off",
+ "lineNumber": 1
+ },
+ {
+ "text": "setlocal enabledelayedexpansion",
+ "lineNumber": 2
+ },
+ {
+ "lineNumber": 3
+ },
+ {
+ "text": "echo Starting Blender file compression...",
+ "lineNumber": 4
+ },
+ {
+ "lineNumber": 5
+ },
+ {
+ "text": "REM Check if files were dragged onto the batch file",
+ "lineNumber": 6
+ },
+ {
+ "text": "if \"%~1\"==\"\" (",
+ "lineNumber": 7
+ },
+ {
+ "text": " echo.",
+ "lineNumber": 8
+ },
+ {
+ "text": " echo Usage: Drag .blend files onto this batch file to compress them.",
+ "lineNumber": 9
+ },
+ {
+ "text": " echo Output will be saved to the \\output folder.",
+ "lineNumber": 10
+ },
+ {
+ "text": " echo.",
+ "lineNumber": 11
+ },
+ {
+ "text": " pause",
+ "lineNumber": 12
+ },
+ {
+ "text": " exit /b 1",
+ "lineNumber": 13
+ },
+ {
+ "text": ")",
+ "lineNumber": 14
+ },
+ {
+ "lineNumber": 15
+ },
+ {
+ "text": "REM Create output directory if it doesn't exist",
+ "lineNumber": 16
+ },
+ {
+ "text": "if not exist \"output\" mkdir \"output\"",
+ "lineNumber": 17
+ },
+ {
+ "lineNumber": 18
+ },
+ {
+ "text": "REM Initialize counters and tracking",
+ "lineNumber": 19
+ },
+ {
+ "text": "set \"processed=0\"",
+ "lineNumber": 20
+ },
+ {
+ "text": "set \"crashed=0\"",
+ "lineNumber": 21
+ },
+ {
+ "text": "set \"skipped=0\"",
+ "lineNumber": 22
+ },
+ {
+ "text": "set \"deleted_similar=0\"",
+ "lineNumber": 23
+ },
+ {
+ "text": "set \"threshold_percent=10\"",
+ "lineNumber": 24
+ },
+ {
+ "lineNumber": 25
+ },
+ {
+ "text": "REM Create a temporary Python script for compression",
+ "lineNumber": 26
+ },
+ {
+ "text": "set \"TEMP_SCRIPT=compress_blend_temp.py\"",
+ "lineNumber": 27
+ },
+ {
+ "text": "echo Creating Python script: !TEMP_SCRIPT!",
+ "lineNumber": 28
+ },
+ {
+ "lineNumber": 29
+ },
+ {
+ "text": "(",
+ "lineNumber": 30
+ },
+ {
+ "text": "echo import bpy",
+ "lineNumber": 31
+ },
+ {
+ "text": "echo import os",
+ "lineNumber": 32
+ },
+ {
+ "text": "echo import sys",
+ "lineNumber": 33
+ },
+ {
+ "text": "echo.",
+ "lineNumber": 34
+ },
+ {
+ "text": "echo # Enable compression globally",
+ "lineNumber": 35
+ },
+ {
+ "text": "echo bpy.context.preferences.filepaths.save_version = 0",
+ "lineNumber": 36
+ },
+ {
+ "text": "echo bpy.context.preferences.filepaths.use_file_compression = True",
+ "lineNumber": 37
+ },
+ {
+ "text": "echo.",
+ "lineNumber": 38
+ },
+ {
+ "text": "echo # Create output directory if it doesn't exist",
+ "lineNumber": 39
+ },
+ {
+ "text": "echo if not os.path.exists^(\"output\"^):",
+ "lineNumber": 40
+ },
+ {
+ "text": "echo os.makedirs^(\"output\"^)",
+ "lineNumber": 41
+ },
+ {
+ "text": "echo.",
+ "lineNumber": 42
+ },
+ {
+ "text": "echo # Get command line arguments ^(the dragged files^)",
+ "lineNumber": 43
+ },
+ {
+ "text": "echo blend_files = sys.argv[sys.argv.index^(\"--\"^) + 1:] if \"--\" in sys.argv else []",
+ "lineNumber": 44
+ },
+ {
+ "text": "echo.",
+ "lineNumber": 45
+ },
+ {
+ "text": "echo print^(\"Found \" + str^(len^(blend_files^)^) + \" .blend files to compress\"^)",
+ "lineNumber": 46
+ },
+ {
+ "text": "echo.",
+ "lineNumber": 47
+ },
+ {
+ "text": "echo def is_compressed_file^(path^):",
+ "lineNumber": 48
+ },
+ {
+ "text": "echo try:",
+ "lineNumber": 49
+ },
+ {
+ "text": "echo with open^(path, 'rb'^) as f:",
+ "lineNumber": 50
+ },
+ {
+ "text": "echo header = f.read^(4^)",
+ "lineNumber": 51
+ },
+ {
+ "text": "echo if header[:2] == b'\\x1f\\x8b': # gzip",
+ "lineNumber": 52
+ },
+ {
+ "text": "echo return True",
+ "lineNumber": 53
+ },
+ {
+ "text": "echo if header == b'\\x28\\xb5\\x2f\\xfd': # zstd",
+ "lineNumber": 54
+ },
+ {
+ "text": "echo return True",
+ "lineNumber": 55
+ },
+ {
+ "text": "echo return False",
+ "lineNumber": 56
+ },
+ {
+ "text": "echo except Exception:",
+ "lineNumber": 57
+ },
+ {
+ "text": "echo return False",
+ "lineNumber": 58
+ },
+ {
+ "text": "echo.",
+ "lineNumber": 59
+ },
+ {
+ "text": "echo for blend_file in blend_files:",
+ "lineNumber": 60
+ },
+ {
+ "text": "echo if blend_file.lower^(^).endswith^(\".blend\"^):",
+ "lineNumber": 61
+ },
+ {
+ "text": "echo try:",
+ "lineNumber": 62
+ },
+ {
+ "text": "echo # Create output path in output folder",
+ "lineNumber": 63
+ },
+ {
+ "text": "echo filename = os.path.basename^(blend_file^)",
+ "lineNumber": 64
+ },
+ {
+ "text": "echo output_file = os.path.join^(\"output\", filename^)",
+ "lineNumber": 65
+ },
+ {
+ "text": "echo # Check if output file already exists",
+ "lineNumber": 66
+ }
+ ]
+ },
+ "score": 0.27458083629608154
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\organize_textures.ps1",
+ "range": {
+ "startPosition": {
+ "line": 44
+ },
+ "endPosition": {
+ "line": 83,
+ "column": 8
+ }
+ },
+ "contents": " $null\n }\n}\n$filesWithChecksums = $allFiles | ForEach-Object -Parallel $parallelScriptBlock -ThrottleLimit $throttleLimit | Where-Object { $null -ne $_ }\n\nWrite-Host \"Checksum calculation complete.\" -ForegroundColor Green\n\n# Group files by checksum\nWrite-Host \"Grouping files by checksum...\" -ForegroundColor Yellow\n$groupedByChecksum = $filesWithChecksums | Group-Object -Property Hash\n\nWrite-Host \"Found $($groupedByChecksum.Count) unique checksums.\" -ForegroundColor Green\n\n# Create \\common and \\common\\duplicates directories\n$commonPath = Join-Path -Path $textureFolderPath -ChildPath \"common\"\n$duplicatesPath = Join-Path -Path $commonPath -ChildPath \"duplicates\"\n\nif (-not (Test-Path -Path $commonPath -PathType Container)) {\n New-Item -ItemType Directory -Path $commonPath | Out-Null\n Write-Host \"Created directory: $commonPath\" -ForegroundColor Green\n}\n\nif (-not (Test-Path -Path $duplicatesPath -PathType Container)) {\n New-Item -ItemType Directory -Path $duplicatesPath | Out-Null\n Write-Host \"Created directory: $duplicatesPath\" -ForegroundColor Green\n}\n\n# Track filenames already in \\common to handle name conflicts\n$filesInCommon = @{}\n\n# Process each checksum group\nWrite-Host \"Moving files to \\common and \\common\\duplicates...\" -ForegroundColor Yellow\n$movedCount = 0\n$duplicateCount = 0\n\nforeach ($group in $groupedByChecksum) {\n $files = $group.Group\n \n if ($files.Count -eq 1) {\n ",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": " $null",
+ "lineNumber": 45
+ },
+ {
+ "text": " }",
+ "lineNumber": 46
+ },
+ {
+ "text": "}",
+ "lineNumber": 47
+ },
+ {
+ "text": "$filesWithChecksums = $allFiles | ForEach-Object -Parallel $parallelScriptBlock -ThrottleLimit $throttleLimit | Where-Object { $null -ne $_ }",
+ "lineNumber": 48
+ },
+ {
+ "lineNumber": 49
+ },
+ {
+ "text": "Write-Host \"Checksum calculation complete.\" -ForegroundColor Green",
+ "lineNumber": 50
+ },
+ {
+ "lineNumber": 51
+ },
+ {
+ "text": "# Group files by checksum",
+ "lineNumber": 52
+ },
+ {
+ "text": "Write-Host \"Grouping files by checksum...\" -ForegroundColor Yellow",
+ "lineNumber": 53
+ },
+ {
+ "text": "$groupedByChecksum = $filesWithChecksums | Group-Object -Property Hash",
+ "lineNumber": 54
+ },
+ {
+ "lineNumber": 55
+ },
+ {
+ "text": "Write-Host \"Found $($groupedByChecksum.Count) unique checksums.\" -ForegroundColor Green",
+ "lineNumber": 56
+ },
+ {
+ "lineNumber": 57
+ },
+ {
+ "text": "# Create \\common and \\common\\duplicates directories",
+ "lineNumber": 58
+ },
+ {
+ "text": "$commonPath = Join-Path -Path $textureFolderPath -ChildPath \"common\"",
+ "lineNumber": 59
+ },
+ {
+ "text": "$duplicatesPath = Join-Path -Path $commonPath -ChildPath \"duplicates\"",
+ "lineNumber": 60
+ },
+ {
+ "lineNumber": 61
+ },
+ {
+ "text": "if (-not (Test-Path -Path $commonPath -PathType Container)) {",
+ "lineNumber": 62
+ },
+ {
+ "text": " New-Item -ItemType Directory -Path $commonPath | Out-Null",
+ "lineNumber": 63
+ },
+ {
+ "text": " Write-Host \"Created directory: $commonPath\" -ForegroundColor Green",
+ "lineNumber": 64
+ },
+ {
+ "text": "}",
+ "lineNumber": 65
+ },
+ {
+ "lineNumber": 66
+ },
+ {
+ "text": "if (-not (Test-Path -Path $duplicatesPath -PathType Container)) {",
+ "lineNumber": 67
+ },
+ {
+ "text": " New-Item -ItemType Directory -Path $duplicatesPath | Out-Null",
+ "lineNumber": 68
+ },
+ {
+ "text": " Write-Host \"Created directory: $duplicatesPath\" -ForegroundColor Green",
+ "lineNumber": 69
+ },
+ {
+ "text": "}",
+ "lineNumber": 70
+ },
+ {
+ "lineNumber": 71
+ },
+ {
+ "text": "# Track filenames already in \\common to handle name conflicts",
+ "lineNumber": 72
+ },
+ {
+ "text": "$filesInCommon = @{}",
+ "lineNumber": 73
+ },
+ {
+ "lineNumber": 74
+ },
+ {
+ "text": "# Process each checksum group",
+ "lineNumber": 75
+ },
+ {
+ "text": "Write-Host \"Moving files to \\common and \\common\\duplicates...\" -ForegroundColor Yellow",
+ "lineNumber": 76
+ },
+ {
+ "text": "$movedCount = 0",
+ "lineNumber": 77
+ },
+ {
+ "text": "$duplicateCount = 0",
+ "lineNumber": 78
+ },
+ {
+ "lineNumber": 79
+ },
+ {
+ "text": "foreach ($group in $groupedByChecksum) {",
+ "lineNumber": 80
+ },
+ {
+ "text": " $files = $group.Group",
+ "lineNumber": 81
+ },
+ {
+ "text": " ",
+ "lineNumber": 82
+ },
+ {
+ "text": " if ($files.Count -eq 1) {",
+ "lineNumber": 83
+ },
+ {
+ "text": " ",
+ "lineNumber": 84
+ }
+ ]
+ },
+ "score": 0.2703059911727905
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\NewProject.bat",
+ "range": {
+ "startPosition": {
+ "line": 61
+ },
+ "endPosition": {
+ "line": 83
+ }
+ },
+ "contents": "if not exist \"%projectRoot%\\Renders\" mkdir \"%projectRoot%\\Renders\"\n\n:: Place helper scripts into Renders\nset \"templateRoot=%STRUCT_DIR%\"\nfor %%F in (UpdateSequences.bat ZipSeqArchv.bat UnzipSeqArchv.bat ConfigLoader.ps1 config.json) do (\n if exist \"%templateRoot%\\%%F\" copy /Y \"%templateRoot%\\%%F\" \"%projectRoot%\\Renders\\%%F\" >nul\n)\n\n:: Use repo-provided templates for git config files\nif exist \"%~dp0components\\gitignore\" copy /Y \"%~dp0components\\gitignore\" \"%projectRoot%\\.gitignore\" >nul\nif exist \"%~dp0components\\gitattributes\" copy /Y \"%~dp0components\\gitattributes\" \"%projectRoot%\\.gitattributes\" >nul\n\n:: Initialize git and install Git LFS\npushd \"%projectRoot%\" >nul\ngit init\ngit lfs install\ngit add . -v\ngit commit -m \"init\"\npopd >nul\n\necho Project structure created successfully in folder: %projectRoot%\npause ",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "if not exist \"%projectRoot%\\Renders\" mkdir \"%projectRoot%\\Renders\"",
+ "lineNumber": 62
+ },
+ {
+ "lineNumber": 63
+ },
+ {
+ "text": ":: Place helper scripts into Renders",
+ "lineNumber": 64
+ },
+ {
+ "text": "set \"templateRoot=%STRUCT_DIR%\"",
+ "lineNumber": 65
+ },
+ {
+ "text": "for %%F in (UpdateSequences.bat ZipSeqArchv.bat UnzipSeqArchv.bat ConfigLoader.ps1 config.json) do (",
+ "lineNumber": 66
+ },
+ {
+ "text": " if exist \"%templateRoot%\\%%F\" copy /Y \"%templateRoot%\\%%F\" \"%projectRoot%\\Renders\\%%F\" >nul",
+ "lineNumber": 67
+ },
+ {
+ "text": ")",
+ "lineNumber": 68
+ },
+ {
+ "lineNumber": 69
+ },
+ {
+ "text": ":: Use repo-provided templates for git config files",
+ "lineNumber": 70
+ },
+ {
+ "text": "if exist \"%~dp0components\\gitignore\" copy /Y \"%~dp0components\\gitignore\" \"%projectRoot%\\.gitignore\" >nul",
+ "lineNumber": 71
+ },
+ {
+ "text": "if exist \"%~dp0components\\gitattributes\" copy /Y \"%~dp0components\\gitattributes\" \"%projectRoot%\\.gitattributes\" >nul",
+ "lineNumber": 72
+ },
+ {
+ "lineNumber": 73
+ },
+ {
+ "text": ":: Initialize git and install Git LFS",
+ "lineNumber": 74
+ },
+ {
+ "text": "pushd \"%projectRoot%\" >nul",
+ "lineNumber": 75
+ },
+ {
+ "text": "git init",
+ "lineNumber": 76
+ },
+ {
+ "text": "git lfs install",
+ "lineNumber": 77
+ },
+ {
+ "text": "git add . -v",
+ "lineNumber": 78
+ },
+ {
+ "text": "git commit -m \"init\"",
+ "lineNumber": 79
+ },
+ {
+ "text": "popd >nul",
+ "lineNumber": 80
+ },
+ {
+ "lineNumber": 81
+ },
+ {
+ "text": "echo Project structure created successfully in folder: %projectRoot%",
+ "lineNumber": 82
+ },
+ {
+ "text": "pause ",
+ "lineNumber": 83
+ }
+ ]
+ },
+ "score": 0.2641300559043884
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\UpdateSequences.ps1",
+ "range": {
+ "startPosition": {
+ "line": 183
+ },
+ "endPosition": {
+ "line": 223,
+ "column": 8
+ }
+ },
+ "contents": "function Add-SequenceFolder {\n param(\n [Parameter(Mandatory)] [System.IO.DirectoryInfo]$Directory,\n [Parameter(Mandatory)] [hashtable]$Map\n )\n if ($Directory.Name -like '_*') { return }\n $fullPath = $Directory.FullName\n if ($Map.ContainsKey($fullPath)) { return }\n \n # Check if directory contains sequence files directly (not just subdirectories)\n $extensions = @('.png','.jpg','.jpeg','.exr','.tif','.tiff','.bmp','.tga')\n $hasSequenceFiles = Get-ChildItem -LiteralPath $fullPath -File -ErrorAction SilentlyContinue |\n Where-Object { $extensions -contains $_.Extension.ToLower() } |\n Select-Object -First 1\n \n # If it only has subdirectories and no sequence files, skip it\n $hasSubdirs = Get-ChildItem -LiteralPath $fullPath -Directory -ErrorAction SilentlyContinue |\n Select-Object -First 1\n if ($null -eq $hasSequenceFiles -and $null -ne $hasSubdirs) {\n return\n }\n \n $Map[$fullPath] = Resolve-SequenceName -Directory $Directory\n}\n\ntry {\n $root = (Get-Location).ProviderPath\n $logFile = $null\n\n if ($DebugMode) {\n $logFile = Join-Path $root (\"UpdateSequences_{0}.log\" -f (Get-Date -Format 'yyyyMMdd_HHmmss'))\n \"[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] === UpdateSequences started in '$root' ===\" | Out-File -LiteralPath $logFile -Encoding UTF8\n }\n\n $sequenceMap = @{}\n\n $primaryPattern = if ($useIsoDailyFormat) { '????-??-??' } else { 'daily_*' }\n $secondaryPattern = if ($useIsoDailyFormat) { 'daily_*' } else { '????-??-??' }\n\n $primaryDirs = Get-ChildItem -LiteralPath $root -Directory -Filter $primaryPattern -ErrorAction SilentlyContinue |\n ",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "function Add-SequenceFolder {",
+ "lineNumber": 184
+ },
+ {
+ "text": " param(",
+ "lineNumber": 185
+ },
+ {
+ "text": " [Parameter(Mandatory)] [System.IO.DirectoryInfo]$Directory,",
+ "lineNumber": 186
+ },
+ {
+ "text": " [Parameter(Mandatory)] [hashtable]$Map",
+ "lineNumber": 187
+ },
+ {
+ "text": " )",
+ "lineNumber": 188
+ },
+ {
+ "text": " if ($Directory.Name -like '_*') { return }",
+ "lineNumber": 189
+ },
+ {
+ "text": " $fullPath = $Directory.FullName",
+ "lineNumber": 190
+ },
+ {
+ "text": " if ($Map.ContainsKey($fullPath)) { return }",
+ "lineNumber": 191
+ },
+ {
+ "text": " ",
+ "lineNumber": 192
+ },
+ {
+ "text": " # Check if directory contains sequence files directly (not just subdirectories)",
+ "lineNumber": 193
+ },
+ {
+ "text": " $extensions = @('.png','.jpg','.jpeg','.exr','.tif','.tiff','.bmp','.tga')",
+ "lineNumber": 194
+ },
+ {
+ "text": " $hasSequenceFiles = Get-ChildItem -LiteralPath $fullPath -File -ErrorAction SilentlyContinue |",
+ "lineNumber": 195
+ },
+ {
+ "text": " Where-Object { $extensions -contains $_.Extension.ToLower() } |",
+ "lineNumber": 196
+ },
+ {
+ "text": " Select-Object -First 1",
+ "lineNumber": 197
+ },
+ {
+ "text": " ",
+ "lineNumber": 198
+ },
+ {
+ "text": " # If it only has subdirectories and no sequence files, skip it",
+ "lineNumber": 199
+ },
+ {
+ "text": " $hasSubdirs = Get-ChildItem -LiteralPath $fullPath -Directory -ErrorAction SilentlyContinue |",
+ "lineNumber": 200
+ },
+ {
+ "text": " Select-Object -First 1",
+ "lineNumber": 201
+ },
+ {
+ "text": " if ($null -eq $hasSequenceFiles -and $null -ne $hasSubdirs) {",
+ "lineNumber": 202
+ },
+ {
+ "text": " return",
+ "lineNumber": 203
+ },
+ {
+ "text": " }",
+ "lineNumber": 204
+ },
+ {
+ "text": " ",
+ "lineNumber": 205
+ },
+ {
+ "text": " $Map[$fullPath] = Resolve-SequenceName -Directory $Directory",
+ "lineNumber": 206
+ },
+ {
+ "text": "}",
+ "lineNumber": 207
+ },
+ {
+ "lineNumber": 208
+ },
+ {
+ "text": "try {",
+ "lineNumber": 209
+ },
+ {
+ "text": " $root = (Get-Location).ProviderPath",
+ "lineNumber": 210
+ },
+ {
+ "text": " $logFile = $null",
+ "lineNumber": 211
+ },
+ {
+ "lineNumber": 212
+ },
+ {
+ "text": " if ($DebugMode) {",
+ "lineNumber": 213
+ },
+ {
+ "text": " $logFile = Join-Path $root (\"UpdateSequences_{0}.log\" -f (Get-Date -Format 'yyyyMMdd_HHmmss'))",
+ "lineNumber": 214
+ },
+ {
+ "text": " \"[$(Get-Date -Format 'yyyy-MM-dd HH:mm:ss')] === UpdateSequences started in '$root' ===\" | Out-File -LiteralPath $logFile -Encoding UTF8",
+ "lineNumber": 215
+ },
+ {
+ "text": " }",
+ "lineNumber": 216
+ },
+ {
+ "lineNumber": 217
+ },
+ {
+ "text": " $sequenceMap = @{}",
+ "lineNumber": 218
+ },
+ {
+ "lineNumber": 219
+ },
+ {
+ "text": " $primaryPattern = if ($useIsoDailyFormat) { '????-??-??' } else { 'daily_*' }",
+ "lineNumber": 220
+ },
+ {
+ "text": " $secondaryPattern = if ($useIsoDailyFormat) { 'daily_*' } else { '????-??-??' }",
+ "lineNumber": 221
+ },
+ {
+ "lineNumber": 222
+ },
+ {
+ "text": " $primaryDirs = Get-ChildItem -LiteralPath $root -Directory -Filter $primaryPattern -ErrorAction SilentlyContinue |",
+ "lineNumber": 223
+ },
+ {
+ "text": " ",
+ "lineNumber": 224
+ }
+ ]
+ },
+ "score": 0.263225793838501
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\replace_cel_with_bsdf.py",
+ "range": {
+ "startPosition": {
+ "line": 188
+ },
+ "endPosition": {
+ "line": 355
+ }
+ },
+ "contents": "def replace_cel_materials():\n\n \n # Build a mapping from base BSDF name to BSDF material (without suffix)\n bsdf_base_map = {bsdf_pattern.match(mat.name).group(1): mat for mat in materials if bsdf_pattern.match(mat.name)}\n \n # Build a mapping from exact material names to materials\n exact_material_map = {mat.name: mat for mat in materials}\n\n # Helpers to normalize names (case-insensitive, ignore numeric suffixes and library suffix)\n def normalize_base(name):\n base_name = name.split(\".blend\")[0] if \".blend\" in name else name\n match = re.match(r\"^(.*?)(\\.\\d{3})?$\", base_name)\n base_name = match.group(1) if match else base_name\n return base_name.strip().casefold()\n\n # Map normalized base name -> list of materials\n materials_by_base = {}\n for mat in materials:\n base = normalize_base(mat.name)\n materials_by_base.setdefault(base, []).append(mat)\n\n # Normalize BSDF base name map for robust target lookups\n bsdf_base_map_normalized = {normalize_base(base): mat for base, mat in bsdf_base_map.items()}\n \n replacements_made = 0\n missing_targets = []\n \n # Process custom mappings first (case/suffix-insensitive)",
+ "signatures": {
+ "ranges": [
+ {
+ "startPosition": {
+ "line": 189,
+ "column": 1
+ },
+ "endPosition": {
+ "line": 190,
+ "column": 5
+ }
+ }
+ ]
+ },
+ "detailedLines": [
+ {
+ "text": "def replace_cel_materials():",
+ "lineNumber": 189,
+ "isSignature": true
+ },
+ {
+ "lineNumber": 235
+ },
+ {
+ "text": " ",
+ "lineNumber": 236
+ },
+ {
+ "text": " # Build a mapping from base BSDF name to BSDF material (without suffix)",
+ "lineNumber": 237
+ },
+ {
+ "text": " bsdf_base_map = {bsdf_pattern.match(mat.name).group(1): mat for mat in materials if bsdf_pattern.match(mat.name)}",
+ "lineNumber": 238
+ },
+ {
+ "text": " ",
+ "lineNumber": 239
+ },
+ {
+ "text": " # Build a mapping from exact material names to materials",
+ "lineNumber": 240
+ },
+ {
+ "text": " exact_material_map = {mat.name: mat for mat in materials}",
+ "lineNumber": 241
+ },
+ {
+ "lineNumber": 242
+ },
+ {
+ "text": " # Helpers to normalize names (case-insensitive, ignore numeric suffixes and library suffix)",
+ "lineNumber": 243
+ },
+ {
+ "text": " def normalize_base(name):",
+ "lineNumber": 244
+ },
+ {
+ "text": " base_name = name.split(\".blend\")[0] if \".blend\" in name else name",
+ "lineNumber": 245
+ },
+ {
+ "text": " match = re.match(r\"^(.*?)(\\.\\d{3})?$\", base_name)",
+ "lineNumber": 246
+ },
+ {
+ "text": " base_name = match.group(1) if match else base_name",
+ "lineNumber": 247
+ },
+ {
+ "text": " return base_name.strip().casefold()",
+ "lineNumber": 248
+ },
+ {
+ "lineNumber": 249
+ },
+ {
+ "text": " # Map normalized base name -> list of materials",
+ "lineNumber": 250
+ },
+ {
+ "text": " materials_by_base = {}",
+ "lineNumber": 251
+ },
+ {
+ "text": " for mat in materials:",
+ "lineNumber": 252
+ },
+ {
+ "text": " base = normalize_base(mat.name)",
+ "lineNumber": 253
+ },
+ {
+ "text": " materials_by_base.setdefault(base, []).append(mat)",
+ "lineNumber": 254
+ },
+ {
+ "lineNumber": 255
+ },
+ {
+ "text": " # Normalize BSDF base name map for robust target lookups",
+ "lineNumber": 256
+ },
+ {
+ "text": " bsdf_base_map_normalized = {normalize_base(base): mat for base, mat in bsdf_base_map.items()}",
+ "lineNumber": 257
+ },
+ {
+ "text": " ",
+ "lineNumber": 258
+ },
+ {
+ "text": " replacements_made = 0",
+ "lineNumber": 259
+ },
+ {
+ "text": " missing_targets = []",
+ "lineNumber": 260
+ },
+ {
+ "text": " ",
+ "lineNumber": 261
+ },
+ {
+ "text": " # Process custom mappings first (case/suffix-insensitive)",
+ "lineNumber": 262
+ }
+ ]
+ },
+ "score": 0.2553691864013672
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\UpgradeToGitProj.bat",
+ "range": {
+ "startPosition": {},
+ "endPosition": {
+ "line": 61,
+ "column": 6
+ }
+ },
+ "contents": "@echo off\nsetlocal EnableExtensions EnableDelayedExpansion\n\n:: UpgradeToGitProj.bat\n:: Run this inside an existing (pre-git) project root.\n:: - Merges .gitignore and .gitattributes from components/ templates\n:: - Initializes git and installs Git LFS\n:: - Creates Renders, copies helper scripts\n:: - Collects seq/ outputs from daily_* folders under Blends\\animations (with submodule support)\n\n:: -----------------------------\n:: Arguments\n:: -----------------------------\nset \"DRY=0\"\nif /I \"%~1\"==\"/dry-run\" set \"DRY=1\"\nif /I \"%~1\"==\"--dry-run\" set \"DRY=1\"\nif /I \"%~1\"==\"-n\" set \"DRY=1\"\nif /I \"%~1\"==\"/n\" set \"DRY=1\"\n\nset \"projectDir=%CD%\"\nset \"scriptDir=%~dp0\"\nset \"RUN_ID=%RANDOM%_%RANDOM%\"\nset \"DELETE_LIST=%TEMP%\\upg_delete_%RUN_ID%.lst\"\n\nsetlocal EnableDelayedExpansion\nset /a debugTotal=0\nset /a debugPlanned=0\nset /a debugMissing=0\n\necho ==============================================\necho UpgradeToGitProj in \"%projectDir%\"\nif \"%DRY%\"==\"1\" (echo Mode: DRY-RUN) else (echo Mode: APPLY)\necho ==============================================\n\n:: -----------------------------\n:: Ensure Renders exists and copy helper scripts\n:: -----------------------------\nset \"rendersDir=%projectDir%\\Renders\"\nif not exist \"%rendersDir%\" (\n if \"%DRY%\"==\"1\" (\n echo [DRY] mkdir \"%rendersDir%\"\n ) else (\n mkdir \"%rendersDir%\" >nul 2>&1\n )\n)\n\nfor %%F in (UpdateSequences.bat ZipSeqArchv.bat UnzipSeqArchv.bat) do (\n if exist \"%scriptDir%%%F\" (\n if \"%DRY%\"==\"1\" (\n echo [DRY] copy \"%scriptDir%%%F\" \"%rendersDir%\\%%F\"\n ) else (\n copy /Y \"%scriptDir%%%F\" \"%rendersDir%\\%%F\" >nul\n )\n ) else (\n echo [WARN] Missing template: \"%scriptDir%%%F\"\n )\n)\n\nfor %%F in (ConfigLoader.ps1 config.json) do (\n if exist \"%scriptDir%%%F\" (\n if \"%DRY%\"==\"1\" (\n ",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "@echo off",
+ "lineNumber": 1
+ },
+ {
+ "text": "setlocal EnableExtensions EnableDelayedExpansion",
+ "lineNumber": 2
+ },
+ {
+ "lineNumber": 3
+ },
+ {
+ "text": ":: UpgradeToGitProj.bat",
+ "lineNumber": 4
+ },
+ {
+ "text": ":: Run this inside an existing (pre-git) project root.",
+ "lineNumber": 5
+ },
+ {
+ "text": ":: - Merges .gitignore and .gitattributes from components/ templates",
+ "lineNumber": 6
+ },
+ {
+ "text": ":: - Initializes git and installs Git LFS",
+ "lineNumber": 7
+ },
+ {
+ "text": ":: - Creates Renders, copies helper scripts",
+ "lineNumber": 8
+ },
+ {
+ "text": ":: - Collects seq/ outputs from daily_* folders under Blends\\animations (with submodule support)",
+ "lineNumber": 9
+ },
+ {
+ "lineNumber": 10
+ },
+ {
+ "text": ":: -----------------------------",
+ "lineNumber": 11
+ },
+ {
+ "text": ":: Arguments",
+ "lineNumber": 12
+ },
+ {
+ "text": ":: -----------------------------",
+ "lineNumber": 13
+ },
+ {
+ "text": "set \"DRY=0\"",
+ "lineNumber": 14
+ },
+ {
+ "text": "if /I \"%~1\"==\"/dry-run\" set \"DRY=1\"",
+ "lineNumber": 15
+ },
+ {
+ "text": "if /I \"%~1\"==\"--dry-run\" set \"DRY=1\"",
+ "lineNumber": 16
+ },
+ {
+ "text": "if /I \"%~1\"==\"-n\" set \"DRY=1\"",
+ "lineNumber": 17
+ },
+ {
+ "text": "if /I \"%~1\"==\"/n\" set \"DRY=1\"",
+ "lineNumber": 18
+ },
+ {
+ "lineNumber": 19
+ },
+ {
+ "text": "set \"projectDir=%CD%\"",
+ "lineNumber": 20
+ },
+ {
+ "text": "set \"scriptDir=%~dp0\"",
+ "lineNumber": 21
+ },
+ {
+ "text": "set \"RUN_ID=%RANDOM%_%RANDOM%\"",
+ "lineNumber": 22
+ },
+ {
+ "text": "set \"DELETE_LIST=%TEMP%\\upg_delete_%RUN_ID%.lst\"",
+ "lineNumber": 23
+ },
+ {
+ "lineNumber": 24
+ },
+ {
+ "text": "setlocal EnableDelayedExpansion",
+ "lineNumber": 25
+ },
+ {
+ "text": "set /a debugTotal=0",
+ "lineNumber": 26
+ },
+ {
+ "text": "set /a debugPlanned=0",
+ "lineNumber": 27
+ },
+ {
+ "text": "set /a debugMissing=0",
+ "lineNumber": 28
+ },
+ {
+ "lineNumber": 29
+ },
+ {
+ "text": "echo ==============================================",
+ "lineNumber": 30
+ },
+ {
+ "text": "echo UpgradeToGitProj in \"%projectDir%\"",
+ "lineNumber": 31
+ },
+ {
+ "text": "if \"%DRY%\"==\"1\" (echo Mode: DRY-RUN) else (echo Mode: APPLY)",
+ "lineNumber": 32
+ },
+ {
+ "text": "echo ==============================================",
+ "lineNumber": 33
+ },
+ {
+ "lineNumber": 34
+ },
+ {
+ "text": ":: -----------------------------",
+ "lineNumber": 35
+ },
+ {
+ "text": ":: Ensure Renders exists and copy helper scripts",
+ "lineNumber": 36
+ },
+ {
+ "text": ":: -----------------------------",
+ "lineNumber": 37
+ },
+ {
+ "text": "set \"rendersDir=%projectDir%\\Renders\"",
+ "lineNumber": 38
+ },
+ {
+ "text": "if not exist \"%rendersDir%\" (",
+ "lineNumber": 39
+ },
+ {
+ "text": " if \"%DRY%\"==\"1\" (",
+ "lineNumber": 40
+ },
+ {
+ "text": " echo [DRY] mkdir \"%rendersDir%\"",
+ "lineNumber": 41
+ },
+ {
+ "text": " ) else (",
+ "lineNumber": 42
+ },
+ {
+ "text": " mkdir \"%rendersDir%\" >nul 2>&1",
+ "lineNumber": 43
+ },
+ {
+ "text": " )",
+ "lineNumber": 44
+ },
+ {
+ "text": ")",
+ "lineNumber": 45
+ },
+ {
+ "lineNumber": 46
+ },
+ {
+ "text": "for %%F in (UpdateSequences.bat ZipSeqArchv.bat UnzipSeqArchv.bat) do (",
+ "lineNumber": 47
+ },
+ {
+ "text": " if exist \"%scriptDir%%%F\" (",
+ "lineNumber": 48
+ },
+ {
+ "text": " if \"%DRY%\"==\"1\" (",
+ "lineNumber": 49
+ },
+ {
+ "text": " echo [DRY] copy \"%scriptDir%%%F\" \"%rendersDir%\\%%F\"",
+ "lineNumber": 50
+ },
+ {
+ "text": " ) else (",
+ "lineNumber": 51
+ },
+ {
+ "text": " copy /Y \"%scriptDir%%%F\" \"%rendersDir%\\%%F\" >nul",
+ "lineNumber": 52
+ },
+ {
+ "text": " )",
+ "lineNumber": 53
+ },
+ {
+ "text": " ) else (",
+ "lineNumber": 54
+ },
+ {
+ "text": " echo [WARN] Missing template: \"%scriptDir%%%F\"",
+ "lineNumber": 55
+ },
+ {
+ "text": " )",
+ "lineNumber": 56
+ },
+ {
+ "text": ")",
+ "lineNumber": 57
+ },
+ {
+ "lineNumber": 58
+ },
+ {
+ "text": "for %%F in (ConfigLoader.ps1 config.json) do (",
+ "lineNumber": 59
+ },
+ {
+ "text": " if exist \"%scriptDir%%%F\" (",
+ "lineNumber": 60
+ },
+ {
+ "text": " if \"%DRY%\"==\"1\" (",
+ "lineNumber": 61
+ },
+ {
+ "text": " ",
+ "lineNumber": 62
+ }
+ ]
+ },
+ "score": 0.25357121229171753
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\zip_sequences.py",
+ "range": {
+ "startPosition": {},
+ "endPosition": {
+ "line": 102
+ }
+ },
+ "contents": "#!/usr/bin/env python3\n\"\"\"Maintain zipped render sequences for Git hooks.\n\nDefault mode scans `Renders/`, produces ZIP archives under `Renders/_zipped/`,\nand stages any updated archives so commits only track compact files. Switch to\n`--mode expand` to inflate the tracked archives back into the ignored working\ndirectories after checkouts or pulls.\n\"\"\"\n\nfrom __future__ import annotations\n\nimport argparse\nimport json\nimport os\nimport platform\nimport shutil\nimport subprocess\nimport sys\nimport tempfile\nimport time\nimport traceback\nfrom concurrent.futures import ThreadPoolExecutor, as_completed\nfrom pathlib import Path\nfrom typing import Iterator, Sequence\n\n# Try to import psutil for cross-platform RAM detection\ntry:\n import psutil\n HAS_PSUTIL = True\nexcept ImportError:\n HAS_PSUTIL = False\n # For Windows fallback\n if platform.system() == \"Windows\":\n try:\n import ctypes\n HAS_CTYPES = True\n except ImportError:\n HAS_CTYPES = False\n else:\n HAS_CTYPES = False\n\n\nRENDER_ROOT = Path(\"Renders\")\nARCHIVE_ROOT = RENDER_ROOT / \"_zipped\"\nSEQUENCE_EXTENSIONS = {\n \".png\",\n \".jpg\",\n \".jpeg\",\n \".tif\",\n \".tiff\",\n \".exr\",\n}\nSTATE_SUFFIX = \".meta.json\"\nDEFAULT_CONFIG = {\n \"zipper\": \"7z\",\n \"compression\": 9,\n \"compressionMethod\": \"LZMA2\", # Compression method: LZMA2 (multi-threaded), PPMd (single-threaded), BZip2, Deflate\n \"dailyFormat\": \"daily_YYMMDD\",\n \"Max7zInst\": 0, # Maximum concurrent 7z instances (0 = auto-calculate)\n}\n\n\ndef log(mode: str, message: str, *, verbose_only: bool = False, verbose: bool = False) -> None:\n if verbose_only and not verbose:\n return\n print(f\"[{mode}] {message}\", flush=True)\n\n\ndef load_config() -> dict:\n # First try to load from project's .config folder (current working directory)\n # Then fall back to ProjectStructure repo config (next to zip_sequences.py)\n ",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "#!/usr/bin/env python3",
+ "lineNumber": 1
+ },
+ {
+ "text": "\"\"\"Maintain zipped render sequences for Git hooks.",
+ "lineNumber": 2
+ },
+ {
+ "lineNumber": 3
+ },
+ {
+ "text": "Default mode scans `Renders/`, produces ZIP archives under `Renders/_zipped/`,",
+ "lineNumber": 4
+ },
+ {
+ "text": "and stages any updated archives so commits only track compact files. Switch to",
+ "lineNumber": 5
+ },
+ {
+ "text": "`--mode expand` to inflate the tracked archives back into the ignored working",
+ "lineNumber": 6
+ },
+ {
+ "text": "directories after checkouts or pulls.",
+ "lineNumber": 7
+ },
+ {
+ "text": "\"\"\"",
+ "lineNumber": 8
+ },
+ {
+ "lineNumber": 9
+ },
+ {
+ "text": "from __future__ import annotations",
+ "lineNumber": 10
+ },
+ {
+ "lineNumber": 11
+ },
+ {
+ "text": "import argparse",
+ "lineNumber": 12
+ },
+ {
+ "text": "import json",
+ "lineNumber": 13
+ },
+ {
+ "text": "import os",
+ "lineNumber": 14
+ },
+ {
+ "text": "import platform",
+ "lineNumber": 15
+ },
+ {
+ "text": "import shutil",
+ "lineNumber": 16
+ },
+ {
+ "text": "import subprocess",
+ "lineNumber": 17
+ },
+ {
+ "text": "import sys",
+ "lineNumber": 18
+ },
+ {
+ "text": "import tempfile",
+ "lineNumber": 19
+ },
+ {
+ "text": "import time",
+ "lineNumber": 20
+ },
+ {
+ "text": "import traceback",
+ "lineNumber": 21
+ },
+ {
+ "text": "from concurrent.futures import ThreadPoolExecutor, as_completed",
+ "lineNumber": 22
+ },
+ {
+ "text": "from pathlib import Path",
+ "lineNumber": 23
+ },
+ {
+ "text": "from typing import Iterator, Sequence",
+ "lineNumber": 24
+ },
+ {
+ "lineNumber": 25
+ },
+ {
+ "text": "# Try to import psutil for cross-platform RAM detection",
+ "lineNumber": 26
+ },
+ {
+ "text": "try:",
+ "lineNumber": 27
+ },
+ {
+ "text": " import psutil",
+ "lineNumber": 28
+ },
+ {
+ "text": " HAS_PSUTIL = True",
+ "lineNumber": 29
+ },
+ {
+ "text": "except ImportError:",
+ "lineNumber": 30
+ },
+ {
+ "text": " HAS_PSUTIL = False",
+ "lineNumber": 31
+ },
+ {
+ "text": " # For Windows fallback",
+ "lineNumber": 32
+ },
+ {
+ "text": " if platform.system() == \"Windows\":",
+ "lineNumber": 33
+ },
+ {
+ "text": " try:",
+ "lineNumber": 34
+ },
+ {
+ "text": " import ctypes",
+ "lineNumber": 35
+ },
+ {
+ "text": " HAS_CTYPES = True",
+ "lineNumber": 36
+ },
+ {
+ "text": " except ImportError:",
+ "lineNumber": 37
+ },
+ {
+ "text": " HAS_CTYPES = False",
+ "lineNumber": 38
+ },
+ {
+ "text": " else:",
+ "lineNumber": 39
+ },
+ {
+ "text": " HAS_CTYPES = False",
+ "lineNumber": 40
+ },
+ {
+ "lineNumber": 41
+ },
+ {
+ "lineNumber": 42
+ },
+ {
+ "text": "RENDER_ROOT = Path(\"Renders\")",
+ "lineNumber": 43
+ },
+ {
+ "text": "ARCHIVE_ROOT = RENDER_ROOT / \"_zipped\"",
+ "lineNumber": 44
+ },
+ {
+ "text": "SEQUENCE_EXTENSIONS = {",
+ "lineNumber": 45
+ },
+ {
+ "text": " \".png\",",
+ "lineNumber": 46
+ },
+ {
+ "text": " \".jpg\",",
+ "lineNumber": 47
+ },
+ {
+ "text": " \".jpeg\",",
+ "lineNumber": 48
+ },
+ {
+ "text": " \".tif\",",
+ "lineNumber": 49
+ },
+ {
+ "text": " \".tiff\",",
+ "lineNumber": 50
+ },
+ {
+ "text": " \".exr\",",
+ "lineNumber": 51
+ },
+ {
+ "text": "}",
+ "lineNumber": 52
+ },
+ {
+ "text": "STATE_SUFFIX = \".meta.json\"",
+ "lineNumber": 53
+ },
+ {
+ "text": "DEFAULT_CONFIG = {",
+ "lineNumber": 54
+ },
+ {
+ "text": " \"zipper\": \"7z\",",
+ "lineNumber": 55
+ },
+ {
+ "text": " \"compression\": 9,",
+ "lineNumber": 56
+ },
+ {
+ "text": " \"compressionMethod\": \"LZMA2\", # Compression method: LZMA2 (multi-threaded), PPMd (single-threaded), BZip2, Deflate",
+ "lineNumber": 57
+ },
+ {
+ "text": " \"dailyFormat\": \"daily_YYMMDD\",",
+ "lineNumber": 58
+ },
+ {
+ "text": " \"Max7zInst\": 0, # Maximum concurrent 7z instances (0 = auto-calculate)",
+ "lineNumber": 59
+ },
+ {
+ "text": "}",
+ "lineNumber": 60
+ },
+ {
+ "lineNumber": 61
+ },
+ {
+ "lineNumber": 62
+ },
+ {
+ "text": "def log(mode: str, message: str, *, verbose_only: bool = False, verbose: bool = False) -> None:",
+ "lineNumber": 63,
+ "isSignature": true
+ },
+ {
+ "text": " if verbose_only and not verbose:",
+ "lineNumber": 64
+ },
+ {
+ "text": " return",
+ "lineNumber": 65
+ },
+ {
+ "text": " print(f\"[{mode}] {message}\", flush=True)",
+ "lineNumber": 66,
+ "isSignature": true
+ },
+ {
+ "lineNumber": 67
+ },
+ {
+ "lineNumber": 68
+ },
+ {
+ "text": "def load_config() -> dict:",
+ "lineNumber": 69,
+ "isSignature": true
+ },
+ {
+ "text": " # First try to load from project's .config folder (current working directory)",
+ "lineNumber": 70,
+ "isSignature": true
+ },
+ {
+ "text": " # Then fall back to ProjectStructure repo config (next to zip_sequences.py)",
+ "lineNumber": 71,
+ "isSignature": true
+ },
+ {
+ "text": " ",
+ "lineNumber": 72
+ }
+ ]
+ },
+ "score": 0.251534104347229
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\compress_blend_files.bat",
+ "range": {
+ "startPosition": {
+ "line": 64
+ },
+ "endPosition": {
+ "line": 118
+ }
+ },
+ "contents": "echo output_file = os.path.join^(\"output\", filename^)\necho # Check if output file already exists\necho if os.path.exists^(output_file^):\necho print^(\"SKIP_EXISTING:\" + blend_file^)\necho continue\necho # Skip if source is already compressed\necho if is_compressed_file^(blend_file^):\necho print^(\"SKIP_ALREADY_COMPRESSED:\" + blend_file^)\necho continue\necho print^(\"PROCESSING:\" + blend_file^)\necho # Load the blend file\necho bpy.ops.wm.open_mainfile^(filepath=blend_file^)\necho # Save with compression to output folder\necho bpy.ops.wm.save_mainfile^(filepath=output_file, compress=True^)\necho print^(\"SUCCESS:\" + blend_file + \":\" + output_file^)\necho except Exception as e:\necho print^(\"CRASH:\" + blend_file + \":\" + str^(e^)^)\necho else:\necho print^(\"SKIP_NOT_BLEND:\" + blend_file^)\necho.\necho print^(\"COMPRESSION_COMPLETE\"^)\necho bpy.ops.wm.quit_blender^(^)\n) > \"!TEMP_SCRIPT!\"\n\nREM Check if script was created successfully\nif exist \"!TEMP_SCRIPT!\" (\n echo Script created successfully: !TEMP_SCRIPT!\n) else (\n echo ERROR: Failed to create script file!\n pause\n exit /b 1\n)\n\nREM Run Blender to process the dragged files\necho Processing dragged .blend files...\necho Using script: !TEMP_SCRIPT!\ndel /f /q \"blender_output.txt\" 2>nul\nblender --background --factory-startup --python \"!TEMP_SCRIPT!\" -- %* 2^>^&1 | powershell -NoProfile -Command \"$input | Tee-Object -FilePath 'blender_output.txt'\"\n\nREM Clean up temporary script\necho Cleaning up temporary script...\ndel \"!TEMP_SCRIPT!\" 2>nul\n\nREM Parse output and generate report\necho.\necho ========================================\necho COMPRESSION REPORT\necho ========================================\necho.\n\nREM Count processed files and get file info\nfor /f \"tokens=1,2,3 delims=:\" %%a in (blender_output.txt) do (\n if \"%%a\"==\"SUCCESS\" (\n set /a processed+=1",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "echo output_file = os.path.join^(\"output\", filename^)",
+ "lineNumber": 65
+ },
+ {
+ "text": "echo # Check if output file already exists",
+ "lineNumber": 66
+ },
+ {
+ "text": "echo if os.path.exists^(output_file^):",
+ "lineNumber": 67
+ },
+ {
+ "text": "echo print^(\"SKIP_EXISTING:\" + blend_file^)",
+ "lineNumber": 68
+ },
+ {
+ "text": "echo continue",
+ "lineNumber": 69
+ },
+ {
+ "text": "echo # Skip if source is already compressed",
+ "lineNumber": 70
+ },
+ {
+ "text": "echo if is_compressed_file^(blend_file^):",
+ "lineNumber": 71
+ },
+ {
+ "text": "echo print^(\"SKIP_ALREADY_COMPRESSED:\" + blend_file^)",
+ "lineNumber": 72
+ },
+ {
+ "text": "echo continue",
+ "lineNumber": 73
+ },
+ {
+ "text": "echo print^(\"PROCESSING:\" + blend_file^)",
+ "lineNumber": 74
+ },
+ {
+ "text": "echo # Load the blend file",
+ "lineNumber": 75
+ },
+ {
+ "text": "echo bpy.ops.wm.open_mainfile^(filepath=blend_file^)",
+ "lineNumber": 76
+ },
+ {
+ "text": "echo # Save with compression to output folder",
+ "lineNumber": 77
+ },
+ {
+ "text": "echo bpy.ops.wm.save_mainfile^(filepath=output_file, compress=True^)",
+ "lineNumber": 78
+ },
+ {
+ "text": "echo print^(\"SUCCESS:\" + blend_file + \":\" + output_file^)",
+ "lineNumber": 79
+ },
+ {
+ "text": "echo except Exception as e:",
+ "lineNumber": 80
+ },
+ {
+ "text": "echo print^(\"CRASH:\" + blend_file + \":\" + str^(e^)^)",
+ "lineNumber": 81
+ },
+ {
+ "text": "echo else:",
+ "lineNumber": 82
+ },
+ {
+ "text": "echo print^(\"SKIP_NOT_BLEND:\" + blend_file^)",
+ "lineNumber": 83
+ },
+ {
+ "text": "echo.",
+ "lineNumber": 84
+ },
+ {
+ "text": "echo print^(\"COMPRESSION_COMPLETE\"^)",
+ "lineNumber": 85
+ },
+ {
+ "text": "echo bpy.ops.wm.quit_blender^(^)",
+ "lineNumber": 86
+ },
+ {
+ "text": ") > \"!TEMP_SCRIPT!\"",
+ "lineNumber": 87
+ },
+ {
+ "lineNumber": 88
+ },
+ {
+ "text": "REM Check if script was created successfully",
+ "lineNumber": 89
+ },
+ {
+ "text": "if exist \"!TEMP_SCRIPT!\" (",
+ "lineNumber": 90
+ },
+ {
+ "text": " echo Script created successfully: !TEMP_SCRIPT!",
+ "lineNumber": 91
+ },
+ {
+ "text": ") else (",
+ "lineNumber": 92
+ },
+ {
+ "text": " echo ERROR: Failed to create script file!",
+ "lineNumber": 93
+ },
+ {
+ "text": " pause",
+ "lineNumber": 94
+ },
+ {
+ "text": " exit /b 1",
+ "lineNumber": 95
+ },
+ {
+ "text": ")",
+ "lineNumber": 96
+ },
+ {
+ "lineNumber": 97
+ },
+ {
+ "text": "REM Run Blender to process the dragged files",
+ "lineNumber": 98
+ },
+ {
+ "text": "echo Processing dragged .blend files...",
+ "lineNumber": 99
+ },
+ {
+ "text": "echo Using script: !TEMP_SCRIPT!",
+ "lineNumber": 100
+ },
+ {
+ "text": "del /f /q \"blender_output.txt\" 2>nul",
+ "lineNumber": 101
+ },
+ {
+ "text": "blender --background --factory-startup --python \"!TEMP_SCRIPT!\" -- %* 2^>^&1 | powershell -NoProfile -Command \"$input | Tee-Object -FilePath 'blender_output.txt'\"",
+ "lineNumber": 102
+ },
+ {
+ "lineNumber": 103
+ },
+ {
+ "text": "REM Clean up temporary script",
+ "lineNumber": 104
+ },
+ {
+ "text": "echo Cleaning up temporary script...",
+ "lineNumber": 105
+ },
+ {
+ "text": "del \"!TEMP_SCRIPT!\" 2>nul",
+ "lineNumber": 106
+ },
+ {
+ "lineNumber": 107
+ },
+ {
+ "text": "REM Parse output and generate report",
+ "lineNumber": 108
+ },
+ {
+ "text": "echo.",
+ "lineNumber": 109
+ },
+ {
+ "text": "echo ========================================",
+ "lineNumber": 110
+ },
+ {
+ "text": "echo COMPRESSION REPORT",
+ "lineNumber": 111
+ },
+ {
+ "text": "echo ========================================",
+ "lineNumber": 112
+ },
+ {
+ "text": "echo.",
+ "lineNumber": 113
+ },
+ {
+ "lineNumber": 114
+ },
+ {
+ "text": "REM Count processed files and get file info",
+ "lineNumber": 115
+ },
+ {
+ "text": "for /f \"tokens=1,2,3 delims=:\" %%a in (blender_output.txt) do (",
+ "lineNumber": 116
+ },
+ {
+ "text": " if \"%%a\"==\"SUCCESS\" (",
+ "lineNumber": 117
+ },
+ {
+ "text": " set /a processed+=1",
+ "lineNumber": 118
+ }
+ ]
+ },
+ "score": 0.24592049419879913
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\ConfigLoader.ps1",
+ "range": {
+ "startPosition": {
+ "line": 226
+ },
+ "endPosition": {
+ "line": 268
+ }
+ },
+ "contents": " exit 1\n }\n\n if (-not (Test-Path -LiteralPath $resolvedProject -PathType Container)) {\n Write-Error \"Project path is not a directory: $resolvedProject\"\n exit 1\n }\n\n Write-Host \"`nDeploying to: $resolvedProject\" -ForegroundColor Cyan\n Write-Host \"Struct directory: $structDir\" -ForegroundColor Cyan\n\n $specs = @(\n @{ Name = 'UpdateSequences.bat'; Source = Join-Path -Path $structDir -ChildPath 'UpdateSequences.bat' },\n @{ Name = 'UpdateAllSequences.bat'; Source = Join-Path -Path $structDir -ChildPath 'UpdateAllSequences.bat' },\n @{ Name = 'ZipSeqArchv.bat'; Source = Join-Path -Path $structDir -ChildPath 'ZipSeqArchv.bat' },\n @{ Name = 'UnzipSeqArchv.bat'; Source = Join-Path -Path $structDir -ChildPath 'UnzipSeqArchv.bat' },\n @{ Name = 'NewDaily.bat'; Source = Join-Path -Path $structDir -ChildPath 'NewDaily.bat' },\n @{ Name = '.gitattributes'; Source = Join-Path -Path $structDir -ChildPath 'components\\gitattributes' },\n @{ Name = '.gitignore'; Source = Join-Path -Path $structDir -ChildPath 'components\\gitignore' }\n )\n\n # Config files to deploy to projectroot\\.config\\\n $configAssets = @(\n @{ Name = 'config.json'; Source = Join-Path -Path $structDir -ChildPath 'config.json' },\n @{ Name = 'GetStructDir.ps1'; Source = Join-Path -Path $structDir -ChildPath 'GetStructDir.ps1' }\n )\n\n foreach ($spec in $specs) {\n if (-not (Test-Path -LiteralPath $spec.Source -PathType Leaf)) {\n Write-Error \"Source file not found: $($spec.Source)\"\n exit 1\n }\n }\n\n foreach ($asset in $configAssets) {\n if (-not (Test-Path -LiteralPath $asset.Source -PathType Leaf)) {\n Write-Error \"Config asset not found: $($asset.Source)\"\n exit 1\n }\n }\n\n # Ensure .config directory exists in project root and is hidden",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": " exit 1",
+ "lineNumber": 227
+ },
+ {
+ "text": " }",
+ "lineNumber": 228
+ },
+ {
+ "lineNumber": 229
+ },
+ {
+ "text": " if (-not (Test-Path -LiteralPath $resolvedProject -PathType Container)) {",
+ "lineNumber": 230
+ },
+ {
+ "text": " Write-Error \"Project path is not a directory: $resolvedProject\"",
+ "lineNumber": 231
+ },
+ {
+ "text": " exit 1",
+ "lineNumber": 232
+ },
+ {
+ "text": " }",
+ "lineNumber": 233
+ },
+ {
+ "lineNumber": 234
+ },
+ {
+ "text": " Write-Host \"`nDeploying to: $resolvedProject\" -ForegroundColor Cyan",
+ "lineNumber": 235
+ },
+ {
+ "text": " Write-Host \"Struct directory: $structDir\" -ForegroundColor Cyan",
+ "lineNumber": 236
+ },
+ {
+ "lineNumber": 237
+ },
+ {
+ "text": " $specs = @(",
+ "lineNumber": 238
+ },
+ {
+ "text": " @{ Name = 'UpdateSequences.bat'; Source = Join-Path -Path $structDir -ChildPath 'UpdateSequences.bat' },",
+ "lineNumber": 239
+ },
+ {
+ "text": " @{ Name = 'UpdateAllSequences.bat'; Source = Join-Path -Path $structDir -ChildPath 'UpdateAllSequences.bat' },",
+ "lineNumber": 240
+ },
+ {
+ "text": " @{ Name = 'ZipSeqArchv.bat'; Source = Join-Path -Path $structDir -ChildPath 'ZipSeqArchv.bat' },",
+ "lineNumber": 241
+ },
+ {
+ "text": " @{ Name = 'UnzipSeqArchv.bat'; Source = Join-Path -Path $structDir -ChildPath 'UnzipSeqArchv.bat' },",
+ "lineNumber": 242
+ },
+ {
+ "text": " @{ Name = 'NewDaily.bat'; Source = Join-Path -Path $structDir -ChildPath 'NewDaily.bat' },",
+ "lineNumber": 243
+ },
+ {
+ "text": " @{ Name = '.gitattributes'; Source = Join-Path -Path $structDir -ChildPath 'components\\gitattributes' },",
+ "lineNumber": 244
+ },
+ {
+ "text": " @{ Name = '.gitignore'; Source = Join-Path -Path $structDir -ChildPath 'components\\gitignore' }",
+ "lineNumber": 245
+ },
+ {
+ "text": " )",
+ "lineNumber": 246
+ },
+ {
+ "lineNumber": 247
+ },
+ {
+ "text": " # Config files to deploy to projectroot\\.config\\",
+ "lineNumber": 248
+ },
+ {
+ "text": " $configAssets = @(",
+ "lineNumber": 249
+ },
+ {
+ "text": " @{ Name = 'config.json'; Source = Join-Path -Path $structDir -ChildPath 'config.json' },",
+ "lineNumber": 250
+ },
+ {
+ "text": " @{ Name = 'GetStructDir.ps1'; Source = Join-Path -Path $structDir -ChildPath 'GetStructDir.ps1' }",
+ "lineNumber": 251
+ },
+ {
+ "text": " )",
+ "lineNumber": 252
+ },
+ {
+ "lineNumber": 253
+ },
+ {
+ "text": " foreach ($spec in $specs) {",
+ "lineNumber": 254
+ },
+ {
+ "text": " if (-not (Test-Path -LiteralPath $spec.Source -PathType Leaf)) {",
+ "lineNumber": 255
+ },
+ {
+ "text": " Write-Error \"Source file not found: $($spec.Source)\"",
+ "lineNumber": 256
+ },
+ {
+ "text": " exit 1",
+ "lineNumber": 257
+ },
+ {
+ "text": " }",
+ "lineNumber": 258
+ },
+ {
+ "text": " }",
+ "lineNumber": 259
+ },
+ {
+ "lineNumber": 260
+ },
+ {
+ "text": " foreach ($asset in $configAssets) {",
+ "lineNumber": 261
+ },
+ {
+ "text": " if (-not (Test-Path -LiteralPath $asset.Source -PathType Leaf)) {",
+ "lineNumber": 262
+ },
+ {
+ "text": " Write-Error \"Config asset not found: $($asset.Source)\"",
+ "lineNumber": 263
+ },
+ {
+ "text": " exit 1",
+ "lineNumber": 264
+ },
+ {
+ "text": " }",
+ "lineNumber": 265
+ },
+ {
+ "text": " }",
+ "lineNumber": 266
+ },
+ {
+ "lineNumber": 267
+ },
+ {
+ "text": " # Ensure .config directory exists in project root and is hidden",
+ "lineNumber": 268
+ }
+ ]
+ },
+ "score": 0.24059920012950897
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\UpdateSequences.ps1",
+ "range": {
+ "startPosition": {
+ "line": 221
+ },
+ "endPosition": {
+ "line": 266,
+ "column": 8
+ }
+ },
+ "contents": " $primaryDirs = Get-ChildItem -LiteralPath $root -Directory -Filter $primaryPattern -ErrorAction SilentlyContinue |\n Where-Object { $_.Name -notlike '_*' }\n foreach ($d in $primaryDirs) {\n $seqDirs = @(Get-ChildItem -LiteralPath $d.FullName -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -notlike '_*' })\n if ($seqDirs.Count -eq 0) {\n Add-SequenceFolder -Directory $d -Map $sequenceMap\n } else {\n foreach ($s in $seqDirs) {\n Add-SequenceFolder -Directory $s -Map $sequenceMap\n }\n }\n }\n\n $secondaryDirs = Get-ChildItem -LiteralPath $root -Directory -Filter $secondaryPattern -ErrorAction SilentlyContinue |\n Where-Object { $_.Name -notlike '_*' }\n foreach ($d in $secondaryDirs) {\n $seqDirs = @(Get-ChildItem -LiteralPath $d.FullName -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -notlike '_*' })\n if ($seqDirs.Count -eq 0) {\n Add-SequenceFolder -Directory $d -Map $sequenceMap\n } else {\n foreach ($s in $seqDirs) {\n Add-SequenceFolder -Directory $s -Map $sequenceMap\n }\n }\n }\n\n $directSeqs = Get-ChildItem -LiteralPath $root -Directory -ErrorAction SilentlyContinue |\n Where-Object {\n $_.Name -notlike '_*' -and\n $_.Name -notlike 'daily_*' -and\n $_.Name -notmatch '^\\d{4}-\\d{2}-\\d{2}$'\n }\n foreach ($seq in $directSeqs) {\n Add-SequenceFolder -Directory $seq -Map $sequenceMap\n }\n\n $sequenceFolders = $sequenceMap.GetEnumerator() | ForEach-Object {\n [pscustomobject]@{\n Path = $_.Key\n Name = $_.Value\n }\n } | Sort-Object -Property Path\n\n if (-not $sequenceFolders) {\n ",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": " $primaryDirs = Get-ChildItem -LiteralPath $root -Directory -Filter $primaryPattern -ErrorAction SilentlyContinue |",
+ "lineNumber": 223
+ },
+ {
+ "text": " Where-Object { $_.Name -notlike '_*' }",
+ "lineNumber": 224
+ },
+ {
+ "text": " foreach ($d in $primaryDirs) {",
+ "lineNumber": 225
+ },
+ {
+ "text": " $seqDirs = @(Get-ChildItem -LiteralPath $d.FullName -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -notlike '_*' })",
+ "lineNumber": 226
+ },
+ {
+ "text": " if ($seqDirs.Count -eq 0) {",
+ "lineNumber": 227
+ },
+ {
+ "text": " Add-SequenceFolder -Directory $d -Map $sequenceMap",
+ "lineNumber": 228
+ },
+ {
+ "text": " } else {",
+ "lineNumber": 229
+ },
+ {
+ "text": " foreach ($s in $seqDirs) {",
+ "lineNumber": 230
+ },
+ {
+ "text": " Add-SequenceFolder -Directory $s -Map $sequenceMap",
+ "lineNumber": 231
+ },
+ {
+ "text": " }",
+ "lineNumber": 232
+ },
+ {
+ "text": " }",
+ "lineNumber": 233
+ },
+ {
+ "text": " }",
+ "lineNumber": 234
+ },
+ {
+ "lineNumber": 235
+ },
+ {
+ "text": " $secondaryDirs = Get-ChildItem -LiteralPath $root -Directory -Filter $secondaryPattern -ErrorAction SilentlyContinue |",
+ "lineNumber": 236
+ },
+ {
+ "text": " Where-Object { $_.Name -notlike '_*' }",
+ "lineNumber": 237
+ },
+ {
+ "text": " foreach ($d in $secondaryDirs) {",
+ "lineNumber": 238
+ },
+ {
+ "text": " $seqDirs = @(Get-ChildItem -LiteralPath $d.FullName -Directory -ErrorAction SilentlyContinue | Where-Object { $_.Name -notlike '_*' })",
+ "lineNumber": 239
+ },
+ {
+ "text": " if ($seqDirs.Count -eq 0) {",
+ "lineNumber": 240
+ },
+ {
+ "text": " Add-SequenceFolder -Directory $d -Map $sequenceMap",
+ "lineNumber": 241
+ },
+ {
+ "text": " } else {",
+ "lineNumber": 242
+ },
+ {
+ "text": " foreach ($s in $seqDirs) {",
+ "lineNumber": 243
+ },
+ {
+ "text": " Add-SequenceFolder -Directory $s -Map $sequenceMap",
+ "lineNumber": 244
+ },
+ {
+ "text": " }",
+ "lineNumber": 245
+ },
+ {
+ "text": " }",
+ "lineNumber": 246
+ },
+ {
+ "text": " }",
+ "lineNumber": 247
+ },
+ {
+ "lineNumber": 248
+ },
+ {
+ "text": " $directSeqs = Get-ChildItem -LiteralPath $root -Directory -ErrorAction SilentlyContinue |",
+ "lineNumber": 249
+ },
+ {
+ "text": " Where-Object {",
+ "lineNumber": 250
+ },
+ {
+ "text": " $_.Name -notlike '_*' -and",
+ "lineNumber": 251
+ },
+ {
+ "text": " $_.Name -notlike 'daily_*' -and",
+ "lineNumber": 252
+ },
+ {
+ "text": " $_.Name -notmatch '^\\d{4}-\\d{2}-\\d{2}$'",
+ "lineNumber": 253
+ },
+ {
+ "text": " }",
+ "lineNumber": 254
+ },
+ {
+ "text": " foreach ($seq in $directSeqs) {",
+ "lineNumber": 255
+ },
+ {
+ "text": " Add-SequenceFolder -Directory $seq -Map $sequenceMap",
+ "lineNumber": 256
+ },
+ {
+ "text": " }",
+ "lineNumber": 257
+ },
+ {
+ "lineNumber": 258
+ },
+ {
+ "text": " $sequenceFolders = $sequenceMap.GetEnumerator() | ForEach-Object {",
+ "lineNumber": 259
+ },
+ {
+ "text": " [pscustomobject]@{",
+ "lineNumber": 260
+ },
+ {
+ "text": " Path = $_.Key",
+ "lineNumber": 261
+ },
+ {
+ "text": " Name = $_.Value",
+ "lineNumber": 262
+ },
+ {
+ "text": " }",
+ "lineNumber": 263
+ },
+ {
+ "text": " } | Sort-Object -Property Path",
+ "lineNumber": 264
+ },
+ {
+ "lineNumber": 265
+ },
+ {
+ "text": " if (-not $sequenceFolders) {",
+ "lineNumber": 266
+ },
+ {
+ "text": " ",
+ "lineNumber": 267
+ }
+ ]
+ },
+ "score": 0.2402079999446869
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\zip_sequences.py",
+ "range": {
+ "startPosition": {
+ "line": 645,
+ "column": 42
+ },
+ "endPosition": {
+ "line": 661,
+ "column": 63
+ }
+ },
+ "contents": "def sequence_dir_for(zip_path: Path) -> Path:\n rel = zip_path.relative_to(ARCHIVE_ROOT)\n # Remove the archive suffix (.7z or .zip) from the end\n # Handle both .7z and .zip extensions\n rel_str = str(rel)\n if rel_str.endswith(\".7z\"):\n rel_str = rel_str[:-3]\n elif rel_str.endswith(\".zip\"):\n rel_str = rel_str[:-4]\n return RENDER_ROOT / rel_str\n\n\ndef state_path_for(zip_path: Path) -> Path:\n return zip_path.with_suffix(zip_path.suffix + STATE_SUFFIX)",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "def sequence_dir_for(zip_path: Path) -> Path:",
+ "lineNumber": 649,
+ "isSignature": true
+ },
+ {
+ "text": " rel = zip_path.relative_to(ARCHIVE_ROOT)",
+ "lineNumber": 650
+ },
+ {
+ "text": " # Remove the archive suffix (.7z or .zip) from the end",
+ "lineNumber": 651
+ },
+ {
+ "text": " # Handle both .7z and .zip extensions",
+ "lineNumber": 652
+ },
+ {
+ "text": " rel_str = str(rel)",
+ "lineNumber": 653
+ },
+ {
+ "text": " if rel_str.endswith(\".7z\"):",
+ "lineNumber": 654
+ },
+ {
+ "text": " rel_str = rel_str[:-3]",
+ "lineNumber": 655
+ },
+ {
+ "text": " elif rel_str.endswith(\".zip\"):",
+ "lineNumber": 656
+ },
+ {
+ "text": " rel_str = rel_str[:-4]",
+ "lineNumber": 657
+ },
+ {
+ "text": " return RENDER_ROOT / rel_str",
+ "lineNumber": 658,
+ "isSignature": true
+ },
+ {
+ "lineNumber": 659
+ },
+ {
+ "lineNumber": 660
+ },
+ {
+ "text": "def state_path_for(zip_path: Path) -> Path:",
+ "lineNumber": 661,
+ "isSignature": true
+ },
+ {
+ "text": " return zip_path.with_suffix(zip_path.suffix + STATE_SUFFIX)",
+ "lineNumber": 662,
+ "isSignature": true
+ }
+ ]
+ },
+ "score": 0.23667949438095093
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\organize_textures.bat",
+ "range": {
+ "startPosition": {},
+ "endPosition": {
+ "line": 22,
+ "column": 12
+ }
+ },
+ "contents": "@echo off\nsetlocal EnableExtensions\n\nset \"script_dir=%~dp0\"\nset \"ps1_path=%script_dir%organize_textures.ps1\"\n\nif not exist \"%ps1_path%\" (\n echo [ERROR] organize_textures.ps1 not found at %ps1_path%\n exit /b 1\n)\n\necho Running texture organizer...\npwsh -NoProfile -ExecutionPolicy Bypass -File \"%ps1_path%\"\nset \"rc=%errorlevel%\"\n\nif %rc% neq 0 (\n echo.\n echo Script exited with error code %rc%\n)\n\necho.\npause\nexit /b %rc%",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "@echo off",
+ "lineNumber": 1
+ },
+ {
+ "text": "setlocal EnableExtensions",
+ "lineNumber": 2
+ },
+ {
+ "lineNumber": 3
+ },
+ {
+ "text": "set \"script_dir=%~dp0\"",
+ "lineNumber": 4
+ },
+ {
+ "text": "set \"ps1_path=%script_dir%organize_textures.ps1\"",
+ "lineNumber": 5
+ },
+ {
+ "lineNumber": 6
+ },
+ {
+ "text": "if not exist \"%ps1_path%\" (",
+ "lineNumber": 7
+ },
+ {
+ "text": " echo [ERROR] organize_textures.ps1 not found at %ps1_path%",
+ "lineNumber": 8
+ },
+ {
+ "text": " exit /b 1",
+ "lineNumber": 9
+ },
+ {
+ "text": ")",
+ "lineNumber": 10
+ },
+ {
+ "lineNumber": 11
+ },
+ {
+ "text": "echo Running texture organizer...",
+ "lineNumber": 12
+ },
+ {
+ "text": "pwsh -NoProfile -ExecutionPolicy Bypass -File \"%ps1_path%\"",
+ "lineNumber": 13
+ },
+ {
+ "text": "set \"rc=%errorlevel%\"",
+ "lineNumber": 14
+ },
+ {
+ "lineNumber": 15
+ },
+ {
+ "text": "if %rc% neq 0 (",
+ "lineNumber": 16
+ },
+ {
+ "text": " echo.",
+ "lineNumber": 17
+ },
+ {
+ "text": " echo Script exited with error code %rc%",
+ "lineNumber": 18
+ },
+ {
+ "text": ")",
+ "lineNumber": 19
+ },
+ {
+ "lineNumber": 20
+ },
+ {
+ "text": "echo.",
+ "lineNumber": 21
+ },
+ {
+ "text": "pause",
+ "lineNumber": 22
+ },
+ {
+ "text": "exit /b %rc%",
+ "lineNumber": 23
+ }
+ ]
+ },
+ "score": 0.23035340011119843
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\UpdateProjectBatches.ps1",
+ "range": {
+ "startPosition": {
+ "line": 55
+ },
+ "endPosition": {
+ "line": 115
+ }
+ },
+ "contents": ")\n\n# Config files to deploy to projectroot\\.config\\\n$configAssets = @(\n @{ Name = 'config.json'; Source = Join-Path -Path $structDir -ChildPath 'config.json' },\n @{ Name = 'GetStructDir.ps1'; Source = Join-Path -Path $structDir -ChildPath 'GetStructDir.ps1' }\n)\n\nforeach ($spec in $specs) {\n if (-not (Test-Path -LiteralPath $spec.Source -PathType Leaf)) {\n Write-Error \"Source file not found: $($spec.Source)\"\n exit 1\n }\n}\n\nforeach ($asset in $configAssets) {\n if (-not (Test-Path -LiteralPath $asset.Source -PathType Leaf)) {\n Write-Error \"Config asset not found: $($asset.Source)\"\n exit 1\n }\n}\n\n# Ensure .config directory exists in project root\n$projectConfigDir = Join-Path -Path $resolvedProject -ChildPath '.config'\nif (-not (Test-Path -LiteralPath $projectConfigDir -PathType Container)) {\n New-Item -Path $projectConfigDir -ItemType Directory -Force | Out-Null\n Write-Host \"Created .config directory: $projectConfigDir\" -ForegroundColor Cyan\n}\n\n$touchedDirs = @{}\n$summary = @()\n\nforeach ($spec in $specs) {\n Write-Host \"`n=== Updating $($spec.Name) ===\" -ForegroundColor Magenta\n\n $targets = Get-ChildItem -LiteralPath $resolvedProject -Recurse -Filter $spec.Name -File -ErrorAction SilentlyContinue\n $targets = $targets | Where-Object { $_.FullName -ne $spec.Source }\n\n if (-not $targets) {\n Write-Host \"No targets found.\" -ForegroundColor Yellow\n $summary += [pscustomobject]@{\n Name = $spec.Name\n Updated = 0\n Failed = 0\n Skipped = 0\n Total = 0\n }\n continue\n }\n\n $updated = 0\n $failed = 0\n\n foreach ($target in $targets) {\n try {\n Copy-Item -Path $spec.Source -Destination $target.FullName -Force\n Write-Host \"[OK] $($target.FullName)\" -ForegroundColor Green\n $updated++\n\n $targetDir = $target.Directory.FullName",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": ")",
+ "lineNumber": 56
+ },
+ {
+ "lineNumber": 57
+ },
+ {
+ "text": "# Config files to deploy to projectroot\\.config\\",
+ "lineNumber": 58
+ },
+ {
+ "text": "$configAssets = @(",
+ "lineNumber": 59
+ },
+ {
+ "text": " @{ Name = 'config.json'; Source = Join-Path -Path $structDir -ChildPath 'config.json' },",
+ "lineNumber": 60
+ },
+ {
+ "text": " @{ Name = 'GetStructDir.ps1'; Source = Join-Path -Path $structDir -ChildPath 'GetStructDir.ps1' }",
+ "lineNumber": 61
+ },
+ {
+ "text": ")",
+ "lineNumber": 62
+ },
+ {
+ "lineNumber": 63
+ },
+ {
+ "text": "foreach ($spec in $specs) {",
+ "lineNumber": 64
+ },
+ {
+ "text": " if (-not (Test-Path -LiteralPath $spec.Source -PathType Leaf)) {",
+ "lineNumber": 65
+ },
+ {
+ "text": " Write-Error \"Source file not found: $($spec.Source)\"",
+ "lineNumber": 66
+ },
+ {
+ "text": " exit 1",
+ "lineNumber": 67
+ },
+ {
+ "text": " }",
+ "lineNumber": 68
+ },
+ {
+ "text": "}",
+ "lineNumber": 69
+ },
+ {
+ "lineNumber": 70
+ },
+ {
+ "text": "foreach ($asset in $configAssets) {",
+ "lineNumber": 71
+ },
+ {
+ "text": " if (-not (Test-Path -LiteralPath $asset.Source -PathType Leaf)) {",
+ "lineNumber": 72
+ },
+ {
+ "text": " Write-Error \"Config asset not found: $($asset.Source)\"",
+ "lineNumber": 73
+ },
+ {
+ "text": " exit 1",
+ "lineNumber": 74
+ },
+ {
+ "text": " }",
+ "lineNumber": 75
+ },
+ {
+ "text": "}",
+ "lineNumber": 76
+ },
+ {
+ "lineNumber": 77
+ },
+ {
+ "text": "# Ensure .config directory exists in project root",
+ "lineNumber": 78
+ },
+ {
+ "text": "$projectConfigDir = Join-Path -Path $resolvedProject -ChildPath '.config'",
+ "lineNumber": 79
+ },
+ {
+ "text": "if (-not (Test-Path -LiteralPath $projectConfigDir -PathType Container)) {",
+ "lineNumber": 80
+ },
+ {
+ "text": " New-Item -Path $projectConfigDir -ItemType Directory -Force | Out-Null",
+ "lineNumber": 81
+ },
+ {
+ "text": " Write-Host \"Created .config directory: $projectConfigDir\" -ForegroundColor Cyan",
+ "lineNumber": 82
+ },
+ {
+ "text": "}",
+ "lineNumber": 83
+ },
+ {
+ "lineNumber": 84
+ },
+ {
+ "text": "$touchedDirs = @{}",
+ "lineNumber": 85
+ },
+ {
+ "text": "$summary = @()",
+ "lineNumber": 86
+ },
+ {
+ "lineNumber": 87
+ },
+ {
+ "text": "foreach ($spec in $specs) {",
+ "lineNumber": 88
+ },
+ {
+ "text": " Write-Host \"`n=== Updating $($spec.Name) ===\" -ForegroundColor Magenta",
+ "lineNumber": 89
+ },
+ {
+ "lineNumber": 90
+ },
+ {
+ "text": " $targets = Get-ChildItem -LiteralPath $resolvedProject -Recurse -Filter $spec.Name -File -ErrorAction SilentlyContinue",
+ "lineNumber": 91
+ },
+ {
+ "text": " $targets = $targets | Where-Object { $_.FullName -ne $spec.Source }",
+ "lineNumber": 92
+ },
+ {
+ "lineNumber": 93
+ },
+ {
+ "text": " if (-not $targets) {",
+ "lineNumber": 94
+ },
+ {
+ "text": " Write-Host \"No targets found.\" -ForegroundColor Yellow",
+ "lineNumber": 95
+ },
+ {
+ "text": " $summary += [pscustomobject]@{",
+ "lineNumber": 96
+ },
+ {
+ "text": " Name = $spec.Name",
+ "lineNumber": 97
+ },
+ {
+ "text": " Updated = 0",
+ "lineNumber": 98
+ },
+ {
+ "text": " Failed = 0",
+ "lineNumber": 99
+ },
+ {
+ "text": " Skipped = 0",
+ "lineNumber": 100
+ },
+ {
+ "text": " Total = 0",
+ "lineNumber": 101
+ },
+ {
+ "text": " }",
+ "lineNumber": 102
+ },
+ {
+ "text": " continue",
+ "lineNumber": 103
+ },
+ {
+ "text": " }",
+ "lineNumber": 104
+ },
+ {
+ "lineNumber": 105
+ },
+ {
+ "text": " $updated = 0",
+ "lineNumber": 106
+ },
+ {
+ "text": " $failed = 0",
+ "lineNumber": 107
+ },
+ {
+ "lineNumber": 108
+ },
+ {
+ "text": " foreach ($target in $targets) {",
+ "lineNumber": 109
+ },
+ {
+ "text": " try {",
+ "lineNumber": 110
+ },
+ {
+ "text": " Copy-Item -Path $spec.Source -Destination $target.FullName -Force",
+ "lineNumber": 111
+ },
+ {
+ "text": " Write-Host \"[OK] $($target.FullName)\" -ForegroundColor Green",
+ "lineNumber": 112
+ },
+ {
+ "text": " $updated++",
+ "lineNumber": 113
+ },
+ {
+ "lineNumber": 114
+ },
+ {
+ "text": " $targetDir = $target.Directory.FullName",
+ "lineNumber": 115
+ }
+ ]
+ },
+ "score": 0.22555430233478546
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\remove_action_fake_users.bat",
+ "range": {
+ "startPosition": {},
+ "endPosition": {
+ "line": 66
+ }
+ },
+ "contents": "@echo off\nsetlocal enabledelayedexpansion\n\necho Starting fake user removal for actions...\n\nREM Check if files were dragged onto the batch file\nif \"%~1\"==\"\" (\n\techo.\n\techo Usage: Drag .blend files onto this batch file to remove fake users from actions.\n\techo Output will be saved to the \\output folder.\n\techo.\n\tpause\n\texit /b 1\n)\n\nREM Create output directory if it doesn't exist\nif not exist \"output\" mkdir \"output\"\n\nREM Initialize counters and tracking\nset \"processed=0\"\nset \"crashed=0\"\nset \"skipped=0\"\nset \"deleted_similar=0\"\nset \"threshold_percent=10\"\nset \"updated_total=0\"\n\nREM Create a temporary Python script for processing\nset \"TEMP_SCRIPT=remove_action_fake_users_temp.py\"\necho Creating Python script: !TEMP_SCRIPT!\n\n(\necho import bpy\necho import os\necho import sys\necho.\necho # Create output directory if it doesn't exist\necho if not os.path.exists^(\"output\"^):\necho \tos.makedirs^(\"output\"^)\necho.\necho # Get command line arguments ^(the dragged files^)\necho blend_files = sys.argv[sys.argv.index^(\"--\"^) + 1:] if \"--\" in sys.argv else []\necho.\necho print^(\"Found \" + str^(len^(blend_files^)^) + \" .blend files to process\"^)\necho.\necho for blend_file in blend_files:\necho ^\tif blend_file.lower^(^).endswith^(\".blend\"^):\necho ^\t\ttry:\necho ^\t\t\t# Create output path in output folder\necho ^\t\t\tfilename = os.path.basename^(blend_file^)\necho ^\t\t\toutput_file = os.path.join^(\"output\", filename^)\necho ^\t\t\t# Check if output file already exists\necho ^\t\t\tif os.path.exists^(output_file^):\necho ^\t\t\t\tprint^(\"SKIP_EXISTING:\" + blend_file^)\necho ^\t\t\t\tcontinue\necho ^\t\t\tprint^(\"PROCESSING:\" + blend_file^)\necho ^\t\t\t# Load the blend file\necho ^\t\t\tbpy.ops.wm.open_mainfile^(filepath=blend_file^)\necho ^\t\t\t# Remove fake users from all actions\necho ^\t\t\tchanged = 0\necho ^\t\t\tfor action in bpy.data.actions:\necho ^\t\t\t\tif getattr^(action, \"use_fake_user\", False^):\necho ^\t\t\t\t\taction.use_fake_user = False\necho ^\t\t\t\t\tchanged += 1\necho ^\t\t\tprint^(\"UPDATED:\" + blend_file + \":\" + str^(changed^)^)\necho ^\t\t\t# Save to output folder\necho ^\t\t\tbpy.ops.wm.save_mainfile^(filepath=output_file, compress=True^)",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "@echo off",
+ "lineNumber": 1
+ },
+ {
+ "text": "setlocal enabledelayedexpansion",
+ "lineNumber": 2
+ },
+ {
+ "lineNumber": 3
+ },
+ {
+ "text": "echo Starting fake user removal for actions...",
+ "lineNumber": 4
+ },
+ {
+ "lineNumber": 5
+ },
+ {
+ "text": "REM Check if files were dragged onto the batch file",
+ "lineNumber": 6
+ },
+ {
+ "text": "if \"%~1\"==\"\" (",
+ "lineNumber": 7
+ },
+ {
+ "text": "\techo.",
+ "lineNumber": 8
+ },
+ {
+ "text": "\techo Usage: Drag .blend files onto this batch file to remove fake users from actions.",
+ "lineNumber": 9
+ },
+ {
+ "text": "\techo Output will be saved to the \\output folder.",
+ "lineNumber": 10
+ },
+ {
+ "text": "\techo.",
+ "lineNumber": 11
+ },
+ {
+ "text": "\tpause",
+ "lineNumber": 12
+ },
+ {
+ "text": "\texit /b 1",
+ "lineNumber": 13
+ },
+ {
+ "text": ")",
+ "lineNumber": 14
+ },
+ {
+ "lineNumber": 15
+ },
+ {
+ "text": "REM Create output directory if it doesn't exist",
+ "lineNumber": 16
+ },
+ {
+ "text": "if not exist \"output\" mkdir \"output\"",
+ "lineNumber": 17
+ },
+ {
+ "lineNumber": 18
+ },
+ {
+ "text": "REM Initialize counters and tracking",
+ "lineNumber": 19
+ },
+ {
+ "text": "set \"processed=0\"",
+ "lineNumber": 20
+ },
+ {
+ "text": "set \"crashed=0\"",
+ "lineNumber": 21
+ },
+ {
+ "text": "set \"skipped=0\"",
+ "lineNumber": 22
+ },
+ {
+ "text": "set \"deleted_similar=0\"",
+ "lineNumber": 23
+ },
+ {
+ "text": "set \"threshold_percent=10\"",
+ "lineNumber": 24
+ },
+ {
+ "text": "set \"updated_total=0\"",
+ "lineNumber": 25
+ },
+ {
+ "lineNumber": 26
+ },
+ {
+ "text": "REM Create a temporary Python script for processing",
+ "lineNumber": 27
+ },
+ {
+ "text": "set \"TEMP_SCRIPT=remove_action_fake_users_temp.py\"",
+ "lineNumber": 28
+ },
+ {
+ "text": "echo Creating Python script: !TEMP_SCRIPT!",
+ "lineNumber": 29
+ },
+ {
+ "lineNumber": 30
+ },
+ {
+ "text": "(",
+ "lineNumber": 31
+ },
+ {
+ "text": "echo import bpy",
+ "lineNumber": 32
+ },
+ {
+ "text": "echo import os",
+ "lineNumber": 33
+ },
+ {
+ "text": "echo import sys",
+ "lineNumber": 34
+ },
+ {
+ "text": "echo.",
+ "lineNumber": 35
+ },
+ {
+ "text": "echo # Create output directory if it doesn't exist",
+ "lineNumber": 36
+ },
+ {
+ "text": "echo if not os.path.exists^(\"output\"^):",
+ "lineNumber": 37
+ },
+ {
+ "text": "echo \tos.makedirs^(\"output\"^)",
+ "lineNumber": 38
+ },
+ {
+ "text": "echo.",
+ "lineNumber": 39
+ },
+ {
+ "text": "echo # Get command line arguments ^(the dragged files^)",
+ "lineNumber": 40
+ },
+ {
+ "text": "echo blend_files = sys.argv[sys.argv.index^(\"--\"^) + 1:] if \"--\" in sys.argv else []",
+ "lineNumber": 41
+ },
+ {
+ "text": "echo.",
+ "lineNumber": 42
+ },
+ {
+ "text": "echo print^(\"Found \" + str^(len^(blend_files^)^) + \" .blend files to process\"^)",
+ "lineNumber": 43
+ },
+ {
+ "text": "echo.",
+ "lineNumber": 44
+ },
+ {
+ "text": "echo for blend_file in blend_files:",
+ "lineNumber": 45
+ },
+ {
+ "text": "echo ^\tif blend_file.lower^(^).endswith^(\".blend\"^):",
+ "lineNumber": 46
+ },
+ {
+ "text": "echo ^\t\ttry:",
+ "lineNumber": 47
+ },
+ {
+ "text": "echo ^\t\t\t# Create output path in output folder",
+ "lineNumber": 48
+ },
+ {
+ "text": "echo ^\t\t\tfilename = os.path.basename^(blend_file^)",
+ "lineNumber": 49
+ },
+ {
+ "text": "echo ^\t\t\toutput_file = os.path.join^(\"output\", filename^)",
+ "lineNumber": 50
+ },
+ {
+ "text": "echo ^\t\t\t# Check if output file already exists",
+ "lineNumber": 51
+ },
+ {
+ "text": "echo ^\t\t\tif os.path.exists^(output_file^):",
+ "lineNumber": 52
+ },
+ {
+ "text": "echo ^\t\t\t\tprint^(\"SKIP_EXISTING:\" + blend_file^)",
+ "lineNumber": 53
+ },
+ {
+ "text": "echo ^\t\t\t\tcontinue",
+ "lineNumber": 54
+ },
+ {
+ "text": "echo ^\t\t\tprint^(\"PROCESSING:\" + blend_file^)",
+ "lineNumber": 55
+ },
+ {
+ "text": "echo ^\t\t\t# Load the blend file",
+ "lineNumber": 56
+ },
+ {
+ "text": "echo ^\t\t\tbpy.ops.wm.open_mainfile^(filepath=blend_file^)",
+ "lineNumber": 57
+ },
+ {
+ "text": "echo ^\t\t\t# Remove fake users from all actions",
+ "lineNumber": 58
+ },
+ {
+ "text": "echo ^\t\t\tchanged = 0",
+ "lineNumber": 59
+ },
+ {
+ "text": "echo ^\t\t\tfor action in bpy.data.actions:",
+ "lineNumber": 60
+ },
+ {
+ "text": "echo ^\t\t\t\tif getattr^(action, \"use_fake_user\", False^):",
+ "lineNumber": 61
+ },
+ {
+ "text": "echo ^\t\t\t\t\taction.use_fake_user = False",
+ "lineNumber": 62
+ },
+ {
+ "text": "echo ^\t\t\t\t\tchanged += 1",
+ "lineNumber": 63
+ },
+ {
+ "text": "echo ^\t\t\tprint^(\"UPDATED:\" + blend_file + \":\" + str^(changed^)^)",
+ "lineNumber": 64
+ },
+ {
+ "text": "echo ^\t\t\t# Save to output folder",
+ "lineNumber": 65
+ },
+ {
+ "text": "echo ^\t\t\tbpy.ops.wm.save_mainfile^(filepath=output_file, compress=True^)",
+ "lineNumber": 66
+ }
+ ]
+ },
+ "score": 0.2200496941804886
+ },
+ {
+ "codeBlock": {
+ "relativeWorkspacePath": ".\\ConfigLoader.ps1",
+ "range": {
+ "startPosition": {},
+ "endPosition": {
+ "line": 64
+ }
+ },
+ "contents": "Set-StrictMode -Version Latest\n$ErrorActionPreference = 'Stop'\n\n$script:LoaderRoot = Split-Path -Parent $MyInvocation.MyCommand.Path\n$script:ConfigPath = Join-Path -Path $script:LoaderRoot -ChildPath 'config.json'\n$script:ConfigCache = $null\n\nfunction Get-ProjectStructureConfig {\n if ($null -ne $script:ConfigCache) {\n return $script:ConfigCache\n }\n\n if (Test-Path -LiteralPath $script:ConfigPath) {\n try {\n $raw = Get-Content -LiteralPath $script:ConfigPath -Raw -ErrorAction Stop\n if ($raw.Trim().Length -gt 0) {\n $script:ConfigCache = $raw | ConvertFrom-Json\n return $script:ConfigCache\n }\n }\n catch {\n Write-Warning \"Failed to parse config.json: $($_.Exception.Message)\"\n }\n }\n\n $script:ConfigCache = [pscustomobject]@{}\n return $script:ConfigCache\n}\n\nfunction Get-ConfigValue {\n param(\n [Parameter(Mandatory)] [string]$Name,\n $Default = $null\n )\n\n $config = Get-ProjectStructureConfig\n if ($config.PSObject.Properties.Name -contains $Name) {\n $value = $config.$Name\n if ($null -ne $value -and ($value -isnot [string] -or $value.Trim().Length -gt 0)) {\n return $value\n }\n }\n\n return $Default\n}\n\nfunction Get-StructDirectory {\n # When script is run directly (not dot-sourced), always use script's directory for portability\n if ($MyInvocation.InvocationName -ne '.') {\n return $script:LoaderRoot\n }\n\n $value = Get-ConfigValue -Name 'structDir'\n if ($null -eq $value -or [string]::IsNullOrWhiteSpace($value)) {\n return $script:LoaderRoot\n }\n\n if ([System.IO.Path]::IsPathRooted($value)) {\n $resolved = Resolve-Path -LiteralPath $value -ErrorAction SilentlyContinue\n if ($null -ne $resolved) { return $resolved.Path }\n return $value\n }\n\n $candidate = Join-Path -Path $script:LoaderRoot -ChildPath $value",
+ "signatures": {},
+ "detailedLines": [
+ {
+ "text": "Set-StrictMode -Version Latest",
+ "lineNumber": 1
+ },
+ {
+ "text": "$ErrorActionPreference = 'Stop'",
+ "lineNumber": 2
+ },
+ {
+ "lineNumber": 3
+ },
+ {
+ "text": "$script:LoaderRoot = Split-Path -Parent $MyInvocation.MyCommand.Path",
+ "lineNumber": 4
+ },
+ {
+ "text": "$script:ConfigPath = Join-Path -Path $script:LoaderRoot -ChildPath 'config.json'",
+ "lineNumber": 5
+ },
+ {
+ "text": "$script:ConfigCache = $null",
+ "lineNumber": 6
+ },
+ {
+ "lineNumber": 7
+ },
+ {
+ "text": "function Get-ProjectStructureConfig {",
+ "lineNumber": 8
+ },
+ {
+ "text": " if ($null -ne $script:ConfigCache) {",
+ "lineNumber": 9
+ },
+ {
+ "text": " return $script:ConfigCache",
+ "lineNumber": 10
+ },
+ {
+ "text": " }",
+ "lineNumber": 11
+ },
+ {
+ "lineNumber": 12
+ },
+ {
+ "text": " if (Test-Path -LiteralPath $script:ConfigPath) {",
+ "lineNumber": 13
+ },
+ {
+ "text": " try {",
+ "lineNumber": 14
+ },
+ {
+ "text": " $raw = Get-Content -LiteralPath $script:ConfigPath -Raw -ErrorAction Stop",
+ "lineNumber": 15
+ },
+ {
+ "text": " if ($raw.Trim().Length -gt 0) {",
+ "lineNumber": 16
+ },
+ {
+ "text": " $script:ConfigCache = $raw | ConvertFrom-Json",
+ "lineNumber": 17
+ },
+ {
+ "text": " return $script:ConfigCache",
+ "lineNumber": 18
+ },
+ {
+ "text": " }",
+ "lineNumber": 19
+ },
+ {
+ "text": " }",
+ "lineNumber": 20
+ },
+ {
+ "text": " catch {",
+ "lineNumber": 21
+ },
+ {
+ "text": " Write-Warning \"Failed to parse config.json: $($_.Exception.Message)\"",
+ "lineNumber": 22
+ },
+ {
+ "text": " }",
+ "lineNumber": 23
+ },
+ {
+ "text": " }",
+ "lineNumber": 24
+ },
+ {
+ "lineNumber": 25
+ },
+ {
+ "text": " $script:ConfigCache = [pscustomobject]@{}",
+ "lineNumber": 26
+ },
+ {
+ "text": " return $script:ConfigCache",
+ "lineNumber": 27
+ },
+ {
+ "text": "}",
+ "lineNumber": 28
+ },
+ {
+ "lineNumber": 29
+ },
+ {
+ "text": "function Get-ConfigValue {",
+ "lineNumber": 30
+ },
+ {
+ "text": " param(",
+ "lineNumber": 31
+ },
+ {
+ "text": " [Parameter(Mandatory)] [string]$Name,",
+ "lineNumber": 32
+ },
+ {
+ "text": " $Default = $null",
+ "lineNumber": 33
+ },
+ {
+ "text": " )",
+ "lineNumber": 34
+ },
+ {
+ "lineNumber": 35
+ },
+ {
+ "text": " $config = Get-ProjectStructureConfig",
+ "lineNumber": 36
+ },
+ {
+ "text": " if ($config.PSObject.Properties.Name -contains $Name) {",
+ "lineNumber": 37
+ },
+ {
+ "text": " $value = $config.$Name",
+ "lineNumber": 38
+ },
+ {
+ "text": " if ($null -ne $value -and ($value -isnot [string] -or $value.Trim().Length -gt 0)) {",
+ "lineNumber": 39
+ },
+ {
+ "text": " return $value",
+ "lineNumber": 40
+ },
+ {
+ "text": " }",
+ "lineNumber": 41
+ },
+ {
+ "text": " }",
+ "lineNumber": 42
+ },
+ {
+ "lineNumber": 43
+ },
+ {
+ "text": " return $Default",
+ "lineNumber": 44
+ },
+ {
+ "text": "}",
+ "lineNumber": 45
+ },
+ {
+ "lineNumber": 46
+ },
+ {
+ "text": "function Get-StructDirectory {",
+ "lineNumber": 47
+ },
+ {
+ "text": " # When script is run directly (not dot-sourced), always use script's directory for portability",
+ "lineNumber": 48
+ },
+ {
+ "text": " if ($MyInvocation.InvocationName -ne '.') {",
+ "lineNumber": 49
+ },
+ {
+ "text": " return $script:LoaderRoot",
+ "lineNumber": 50
+ },
+ {
+ "text": " }",
+ "lineNumber": 51
+ },
+ {
+ "lineNumber": 52
+ },
+ {
+ "text": " $value = Get-ConfigValue -Name 'structDir'",
+ "lineNumber": 53
+ },
+ {
+ "text": " if ($null -eq $value -or [string]::IsNullOrWhiteSpace($value)) {",
+ "lineNumber": 54
+ },
+ {
+ "text": " return $script:LoaderRoot",
+ "lineNumber": 55
+ },
+ {
+ "text": " }",
+ "lineNumber": 56
+ },
+ {
+ "lineNumber": 57
+ },
+ {
+ "text": " if ([System.IO.Path]::IsPathRooted($value)) {",
+ "lineNumber": 58
+ },
+ {
+ "text": " $resolved = Resolve-Path -LiteralPath $value -ErrorAction SilentlyContinue",
+ "lineNumber": 59
+ },
+ {
+ "text": " if ($null -ne $resolved) { return $resolved.Path }",
+ "lineNumber": 60
+ },
+ {
+ "text": " return $value",
+ "lineNumber": 61
+ },
+ {
+ "text": " }",
+ "lineNumber": 62
+ },
+ {
+ "lineNumber": 63
+ },
+ {
+ "text": " $candidate = Join-Path -Path $script:LoaderRoot -ChildPath $value",
+ "lineNumber": 64
+ }
+ ]
+ },
+ "score": 0.21668344736099243
+ }
+ ]
+}
+```
+Status: **completed**
+
+
+
+
+
+---
+
+
+Creating a plan to update the script for two-level duplicate detection:
+
+
+---
+
+
+
+Tool use: **create_plan**
+
+
+Parameters:
+
+```json
+{
+ "plan": "# Two-Level Texture Organization Script\n\n## Current Behavior\nThe script currently processes all files recursively and moves everything to a single `\\common` folder at the texture root level.\n\n## New Requirements\nThe texture folder structure is: `\\\\textures\\[Blendfile]\\[Material]`\n\nThe script needs to:\n1. **Intra-blendfile commonalities**: Files that appear in multiple materials within the same blendfile → `\\\\textures\\[blendfile]\\common`\n2. **Inter-blendfile commonalities**: Files that appear across multiple blendfiles → `\\\\textures\\common`\n\n## Implementation Plan\n\n### File: `organize_textures.ps1`\n\n**Two-Pass Approach:**\n\n#### Pass 1: Intra-Blendfile Processing\n1. Get all direct subdirectories of the texture folder (these are blendfile folders)\n2. For each blendfile folder:\n - Get all files recursively within that blendfile folder (excluding `\\common` folders)\n - Calculate checksums for all files in that blendfile\n - Group files by checksum\n - For each checksum group:\n - If multiple files (duplicates within blendfile):\n - Move first file to `[blendfile]\\common`\n - Move remaining files to `[blendfile]\\common\\duplicates` with numbered suffixes\n - If single file: leave it in place (will be processed in Pass 2)\n\n#### Pass 2: Inter-Blendfile Processing\n1. After Pass 1 completes, get all remaining files (excluding all `\\common` folders)\n2. Calculate checksums for all remaining files\n3. Group files by checksum\n4. For each checksum group:\n - If multiple files (duplicates across blendfiles):\n - Move first file to `\\textures\\common`\n - Move remaining files to `\\textures\\common\\duplicates` with numbered suffixes\n - If single file: leave it in place (unique file)\n\n**Key Changes:**\n- Identify blendfile folders as direct subdirectories of the texture root\n- Process each blendfile folder independently in Pass 1\n- Exclude `\\common` folders from file collection in both passes\n- Create `[blendfile]\\common` and `[blendfile]\\common\\duplicates` directories as needed\n- Create `\\textures\\common` and `\\textures\\common\\duplicates` directories for Pass 2\n- Handle filename conflicts when moving files\n- Provide progress feedback for each blendfile and overall progress\n\n**Structure Detection:**\n- Assume texture folder structure: `[textureRoot]\\[Blendfile]\\[Material]\\...`\n- Blendfile folders are direct children of the texture root\n- Material folders are children of blendfile folders\n\n**Edge Cases:**\n- Handle blendfile folders that already have `\\common` subdirectories\n- Skip processing if a blendfile folder has no files\n- Handle files directly in blendfile folder (not in material subfolders)",
+ "name": "Two-level texture organization",
+ "todos": [
+ {
+ "content": "Add logic to identify blendfile folders as direct subdirectories of texture root",
+ "status": "pending",
+ "id": "identify_blendfile_folders"
+ },
+ {
+ "content": "Implement Pass 1: Process each blendfile folder separately, finding duplicates within each blendfile and moving to [blendfile]\\common",
+ "status": "pending",
+ "id": "pass1_intra_blendfile"
+ },
+ {
+ "content": "Implement Pass 2: Process remaining files across all blendfiles, finding duplicates across blendfiles and moving to \\textures\\common",
+ "status": "pending",
+ "id": "pass2_inter_blendfile"
+ },
+ {
+ "content": "Update file collection to exclude all \\common folders (both blendfile-level and root-level)",
+ "status": "pending",
+ "id": "update_exclusions"
+ },
+ {
+ "content": "Ensure proper directory creation for both [blendfile]\\common and \\textures\\common paths",
+ "status": "pending",
+ "id": "handle_directory_creation"
+ }
+ ],
+ "overview": "Update organize_textures.ps1 to scan each blendfile folder separately, moving intra-blendfile duplicates to [blendfile]\\common and inter-blendfile duplicates to \\textures\\common"
+}
+```
+Additional data:
+
+```json
+{
+ "reviewData": {
+ "status": "Requested",
+ "selectedOption": "none",
+ "isShowingInput": false
+ },
+ "planUri": "c:\\Users\\Nathan\\.cursor\\plans\\two-level_texture_organization_df41b675.plan.md",
+ "hasOpenedEditor": true
+}
+```
+Result:
+
+```json
+{
+ "rejected": {}
+}
+```
+Status: **completed**
+
+
+
+
+
+---
+
+_**User**_
+
+Implement the plan as specified, it is attached for your reference. Do NOT edit the plan file itself.
+
+To-do's from the plan have already been created. Do not create them again. Mark them as in_progress as you work, starting with the first one. Don't stop until you have completed all the to-dos.
+
+---
+
+_**Agent (model default, mode Agent)**_
+
+
+Implementing the plan. Reviewing the current script, then updating it for two-level duplicate detection.
+
+
+---
+
+
+
+Tool use: **read_file_v2**
+
+Status: **loading**
+
+
+
+
+
+---
+
diff --git a/organize_textures.ps1 b/organize_textures.ps1
index 60be93e..cd707c6 100644
--- a/organize_textures.ps1
+++ b/organize_textures.ps1
@@ -1,4 +1,6 @@
-# Script to organize texture files by checksum, moving all files to \common and duplicates to \common\duplicates
+# Script to organize texture files by checksum with two-level duplicate detection
+# Pass 1: Intra-blendfile duplicates → [blendfile]\common
+# Pass 2: Inter-blendfile duplicates → \textures\common
# Usage: .\organize_textures.ps1
# Prompt user for texture folder path
@@ -19,153 +21,292 @@ if (-not (Test-Path -Path $textureFolderPath -PathType Container)) {
$textureFolderPath = (Resolve-Path $textureFolderPath).ProviderPath
Write-Host "Processing texture folder: $textureFolderPath" -ForegroundColor Cyan
-# Get all files recursively, excluding the \common folder to avoid processing already-moved files
-Write-Host "Collecting files..." -ForegroundColor Yellow
-$allFiles = Get-ChildItem -Path $textureFolderPath -Recurse -File | Where-Object { $_.FullName -notlike "*\common\*" }
-
-if ($null -eq $allFiles -or $allFiles.Count -eq 0) {
- Write-Host "No files found to process (excluding \common folder)." -ForegroundColor Yellow
- exit
-}
-
-Write-Host "Found $($allFiles.Count) files to process." -ForegroundColor Green
-
-# Calculate checksums for all files using parallel processing
-Write-Host "Calculating checksums using parallel processing (this may take a while)..." -ForegroundColor Yellow
-$throttleLimit = [Math]::Max(1, [Environment]::ProcessorCount)
-$parallelScriptBlock = {
- try {
- $hash = Get-FileHash -Path $_.FullName -Algorithm SHA256
- [PSCustomObject]@{
- File = $_
- Hash = $hash.Hash
- }
- } catch {
- Write-Warning "Failed to calculate checksum for: $($_.FullName) - $($_.Exception.Message)"
- $null
- }
-}
-$filesWithChecksums = $allFiles | ForEach-Object -Parallel $parallelScriptBlock -ThrottleLimit $throttleLimit | Where-Object { $null -ne $_ }
-
-Write-Host "Checksum calculation complete." -ForegroundColor Green
-
-# Group files by checksum
-Write-Host "Grouping files by checksum..." -ForegroundColor Yellow
-$groupedByChecksum = $filesWithChecksums | Group-Object -Property Hash
-
-Write-Host "Found $($groupedByChecksum.Count) unique checksums." -ForegroundColor Green
-
-# Create \common and \common\duplicates directories
-$commonPath = Join-Path -Path $textureFolderPath -ChildPath "common"
-$duplicatesPath = Join-Path -Path $commonPath -ChildPath "duplicates"
-
-if (-not (Test-Path -Path $commonPath -PathType Container)) {
- New-Item -ItemType Directory -Path $commonPath | Out-Null
- Write-Host "Created directory: $commonPath" -ForegroundColor Green
-}
-
-if (-not (Test-Path -Path $duplicatesPath -PathType Container)) {
- New-Item -ItemType Directory -Path $duplicatesPath | Out-Null
- Write-Host "Created directory: $duplicatesPath" -ForegroundColor Green
-}
-
-# Track filenames already in \common to handle name conflicts
-$filesInCommon = @{}
-
-# Process each checksum group
-Write-Host "Moving files to \common and \common\duplicates..." -ForegroundColor Yellow
-$movedCount = 0
-$duplicateCount = 0
-
-foreach ($group in $groupedByChecksum) {
- $files = $group.Group
+# Function to calculate checksums for files
+function Get-FilesWithChecksums {
+ param(
+ [array]$Files
+ )
- if ($files.Count -eq 1) {
- # Single file - move to \common
- $fileObj = $files[0].File
+ $throttleLimit = [Math]::Max(1, [Environment]::ProcessorCount)
+ $parallelScriptBlock = {
+ try {
+ $hash = Get-FileHash -Path $_.FullName -Algorithm SHA256
+ [PSCustomObject]@{
+ File = $_
+ Hash = $hash.Hash
+ }
+ } catch {
+ Write-Warning "Failed to calculate checksum for: $($_.FullName) - $($_.Exception.Message)"
+ $null
+ }
+ }
+
+ return $Files | ForEach-Object -Parallel $parallelScriptBlock -ThrottleLimit $throttleLimit | Where-Object { $null -ne $_ }
+}
+
+# Function to move files to common folder
+function Move-FilesToCommon {
+ param(
+ [array]$Files,
+ [string]$CommonPath,
+ [string]$DuplicatesPath,
+ [hashtable]$FilesInCommon
+ )
+
+ $movedCount = 0
+ $duplicateCount = 0
+
+ foreach ($fileObj in $Files) {
$fileName = $fileObj.Name
- $destinationPath = Join-Path -Path $commonPath -ChildPath $fileName
+ $destinationPath = Join-Path -Path $CommonPath -ChildPath $fileName
# Handle name conflicts
- if ($filesInCommon.ContainsKey($fileName)) {
- # File with same name but different checksum already exists
+ if ($FilesInCommon.ContainsKey($fileName)) {
$baseName = [System.IO.Path]::GetFileNameWithoutExtension($fileName)
$extension = [System.IO.Path]::GetExtension($fileName)
$counter = 1
do {
$newFileName = "${baseName}_${counter}${extension}"
- $destinationPath = Join-Path -Path $commonPath -ChildPath $newFileName
+ $destinationPath = Join-Path -Path $CommonPath -ChildPath $newFileName
$counter++
- } while ($filesInCommon.ContainsKey($newFileName) -or (Test-Path -Path $destinationPath))
+ } while ($FilesInCommon.ContainsKey($newFileName) -or (Test-Path -Path $destinationPath))
$fileName = $newFileName
- Write-Host " Name conflict resolved: renamed to '$fileName'" -ForegroundColor Yellow
}
try {
Move-Item -Path $fileObj.FullName -Destination $destinationPath -Force
- $filesInCommon[$fileName] = $true
+ $FilesInCommon[$fileName] = $true
$movedCount++
} catch {
Write-Warning "Failed to move file: $($fileObj.FullName) - $($_.Exception.Message)"
}
- } else {
- # Multiple files with same checksum (duplicates)
- # Move first file to \common
- $firstFile = $files[0].File
- $fileName = $firstFile.Name
- $destinationPath = Join-Path -Path $commonPath -ChildPath $fileName
+ }
+
+ return @{
+ MovedCount = $movedCount
+ DuplicateCount = $duplicateCount
+ }
+}
+
+# Function to process duplicate group
+function Process-DuplicateGroup {
+ param(
+ [array]$Files,
+ [string]$CommonPath,
+ [string]$DuplicatesPath,
+ [hashtable]$FilesInCommon
+ )
+
+ $movedCount = 0
+ $duplicateCount = 0
+
+ if ($Files.Count -eq 1) {
+ # Single file - leave in place (will be processed in Pass 2)
+ return @{
+ MovedCount = 0
+ DuplicateCount = 0
+ }
+ }
+
+ # Multiple files with same checksum (duplicates)
+ # Move first file to \common
+ $firstFile = $Files[0].File
+ $fileName = $firstFile.Name
+ $destinationPath = Join-Path -Path $CommonPath -ChildPath $fileName
+
+ # Handle name conflicts for the first file
+ if ($FilesInCommon.ContainsKey($fileName)) {
+ $baseName = [System.IO.Path]::GetFileNameWithoutExtension($fileName)
+ $extension = [System.IO.Path]::GetExtension($fileName)
+ $counter = 1
+ do {
+ $newFileName = "${baseName}_${counter}${extension}"
+ $destinationPath = Join-Path -Path $CommonPath -ChildPath $newFileName
+ $counter++
+ } while ($FilesInCommon.ContainsKey($newFileName) -or (Test-Path -Path $destinationPath))
- # Handle name conflicts for the first file
- if ($filesInCommon.ContainsKey($fileName)) {
- $baseName = [System.IO.Path]::GetFileNameWithoutExtension($fileName)
- $extension = [System.IO.Path]::GetExtension($fileName)
- $counter = 1
- do {
- $newFileName = "${baseName}_${counter}${extension}"
- $destinationPath = Join-Path -Path $commonPath -ChildPath $newFileName
- $counter++
- } while ($filesInCommon.ContainsKey($newFileName) -or (Test-Path -Path $destinationPath))
-
- $fileName = $newFileName
- Write-Host " Name conflict resolved for duplicate group: renamed to '$fileName'" -ForegroundColor Yellow
+ $fileName = $newFileName
+ }
+
+ try {
+ Move-Item -Path $firstFile.FullName -Destination $destinationPath -Force
+ $FilesInCommon[$fileName] = $true
+ $movedCount++
+ } catch {
+ Write-Warning "Failed to move first duplicate file: $($firstFile.FullName) - $($_.Exception.Message)"
+ }
+
+ # Move remaining files to \common\duplicates with numbered suffixes
+ for ($i = 1; $i -lt $Files.Count; $i++) {
+ $fileObj = $Files[$i].File
+ $baseName = [System.IO.Path]::GetFileNameWithoutExtension($fileObj.Name)
+ $extension = [System.IO.Path]::GetExtension($fileObj.Name)
+ $numberedFileName = "${baseName}.$($i.ToString('000'))${extension}"
+ $duplicateDestinationPath = Join-Path -Path $DuplicatesPath -ChildPath $numberedFileName
+
+ # Handle name conflicts in duplicates folder
+ $conflictCounter = 1
+ while (Test-Path -Path $duplicateDestinationPath) {
+ $numberedFileName = "${baseName}.$($i.ToString('000'))_${conflictCounter}${extension}"
+ $duplicateDestinationPath = Join-Path -Path $DuplicatesPath -ChildPath $numberedFileName
+ $conflictCounter++
}
try {
- Move-Item -Path $firstFile.FullName -Destination $destinationPath -Force
- $filesInCommon[$fileName] = $true
- $movedCount++
+ Move-Item -Path $fileObj.FullName -Destination $duplicateDestinationPath -Force
+ $duplicateCount++
} catch {
- Write-Warning "Failed to move first duplicate file: $($firstFile.FullName) - $($_.Exception.Message)"
- }
-
- # Move remaining files to \common\duplicates with numbered suffixes
- for ($i = 1; $i -lt $files.Count; $i++) {
- $fileObj = $files[$i].File
- $baseName = [System.IO.Path]::GetFileNameWithoutExtension($fileObj.Name)
- $extension = [System.IO.Path]::GetExtension($fileObj.Name)
- $numberedFileName = "${baseName}.$($i.ToString('000'))${extension}"
- $duplicateDestinationPath = Join-Path -Path $duplicatesPath -ChildPath $numberedFileName
-
- # Handle name conflicts in duplicates folder
- $conflictCounter = 1
- while (Test-Path -Path $duplicateDestinationPath) {
- $numberedFileName = "${baseName}.$($i.ToString('000'))_${conflictCounter}${extension}"
- $duplicateDestinationPath = Join-Path -Path $duplicatesPath -ChildPath $numberedFileName
- $conflictCounter++
- }
-
- try {
- Move-Item -Path $fileObj.FullName -Destination $duplicateDestinationPath -Force
- $duplicateCount++
- } catch {
- Write-Warning "Failed to move duplicate file: $($fileObj.FullName) - $($_.Exception.Message)"
- }
+ Write-Warning "Failed to move duplicate file: $($fileObj.FullName) - $($_.Exception.Message)"
}
}
+
+ return @{
+ MovedCount = $movedCount
+ DuplicateCount = $duplicateCount
+ }
+}
+
+# ============================================================================
+# PASS 1: Intra-Blendfile Processing
+# ============================================================================
+Write-Host ""
+Write-Host "=== PASS 1: Intra-Blendfile Processing ===" -ForegroundColor Cyan
+
+# Get all direct subdirectories of texture folder (blendfile folders)
+$blendfileFolders = Get-ChildItem -Path $textureFolderPath -Directory | Where-Object { $_.Name -ne "common" }
+
+if ($null -eq $blendfileFolders -or $blendfileFolders.Count -eq 0) {
+ Write-Host "No blendfile folders found. Skipping Pass 1." -ForegroundColor Yellow
+} else {
+ Write-Host "Found $($blendfileFolders.Count) blendfile folder(s) to process." -ForegroundColor Green
+
+ $totalPass1Moved = 0
+ $totalPass1Duplicates = 0
+
+ foreach ($blendfileFolder in $blendfileFolders) {
+ Write-Host ""
+ Write-Host "Processing blendfile: $($blendfileFolder.Name)" -ForegroundColor Yellow
+
+ # Get all files in this blendfile folder, excluding \common folders
+ $blendfileFiles = Get-ChildItem -Path $blendfileFolder.FullName -Recurse -File | Where-Object { $_.FullName -notlike "*\common\*" }
+
+ if ($null -eq $blendfileFiles -or $blendfileFiles.Count -eq 0) {
+ Write-Host " No files found in this blendfile folder." -ForegroundColor Gray
+ continue
+ }
+
+ Write-Host " Found $($blendfileFiles.Count) files." -ForegroundColor Gray
+
+ # Calculate checksums
+ Write-Host " Calculating checksums..." -ForegroundColor Gray
+ $filesWithChecksums = Get-FilesWithChecksums -Files $blendfileFiles
+
+ # Group by checksum
+ $groupedByChecksum = $filesWithChecksums | Group-Object -Property Hash
+
+ Write-Host " Found $($groupedByChecksum.Count) unique checksums." -ForegroundColor Gray
+
+ # Create [blendfile]\common and [blendfile]\common\duplicates directories
+ $blendfileCommonPath = Join-Path -Path $blendfileFolder.FullName -ChildPath "common"
+ $blendfileDuplicatesPath = Join-Path -Path $blendfileCommonPath -ChildPath "duplicates"
+
+ if (-not (Test-Path -Path $blendfileCommonPath -PathType Container)) {
+ New-Item -ItemType Directory -Path $blendfileCommonPath | Out-Null
+ }
+
+ if (-not (Test-Path -Path $blendfileDuplicatesPath -PathType Container)) {
+ New-Item -ItemType Directory -Path $blendfileDuplicatesPath | Out-Null
+ }
+
+ # Track filenames already in [blendfile]\common
+ $filesInBlendfileCommon = @{}
+
+ # Process each checksum group
+ $blendfileMoved = 0
+ $blendfileDuplicates = 0
+
+ foreach ($group in $groupedByChecksum) {
+ $result = Process-DuplicateGroup -Files $group.Group -CommonPath $blendfileCommonPath -DuplicatesPath $blendfileDuplicatesPath -FilesInCommon $filesInBlendfileCommon
+ $blendfileMoved += $result.MovedCount
+ $blendfileDuplicates += $result.DuplicateCount
+ }
+
+ Write-Host " Moved $blendfileMoved file(s) to \common, $blendfileDuplicates duplicate(s) to \common\duplicates" -ForegroundColor Green
+ $totalPass1Moved += $blendfileMoved
+ $totalPass1Duplicates += $blendfileDuplicates
+ }
+
+ Write-Host ""
+ Write-Host "Pass 1 complete: $totalPass1Moved file(s) moved, $totalPass1Duplicates duplicate(s) moved" -ForegroundColor Green
+}
+
+# ============================================================================
+# PASS 2: Inter-Blendfile Processing
+# ============================================================================
+Write-Host ""
+Write-Host "=== PASS 2: Inter-Blendfile Processing ===" -ForegroundColor Cyan
+
+# Get all remaining files (excluding all \common folders)
+Write-Host "Collecting remaining files..." -ForegroundColor Yellow
+$remainingFiles = Get-ChildItem -Path $textureFolderPath -Recurse -File | Where-Object { $_.FullName -notlike "*\common\*" }
+
+if ($null -eq $remainingFiles -or $remainingFiles.Count -eq 0) {
+ Write-Host "No remaining files found to process." -ForegroundColor Yellow
+} else {
+ Write-Host "Found $($remainingFiles.Count) remaining files to process." -ForegroundColor Green
+
+ # Calculate checksums
+ Write-Host "Calculating checksums using parallel processing (this may take a while)..." -ForegroundColor Yellow
+ $filesWithChecksums = Get-FilesWithChecksums -Files $remainingFiles
+
+ Write-Host "Checksum calculation complete." -ForegroundColor Green
+
+ # Group files by checksum
+ Write-Host "Grouping files by checksum..." -ForegroundColor Yellow
+ $groupedByChecksum = $filesWithChecksums | Group-Object -Property Hash
+
+ Write-Host "Found $($groupedByChecksum.Count) unique checksums." -ForegroundColor Green
+
+ # Create \textures\common and \textures\common\duplicates directories
+ $rootCommonPath = Join-Path -Path $textureFolderPath -ChildPath "common"
+ $rootDuplicatesPath = Join-Path -Path $rootCommonPath -ChildPath "duplicates"
+
+ if (-not (Test-Path -Path $rootCommonPath -PathType Container)) {
+ New-Item -ItemType Directory -Path $rootCommonPath | Out-Null
+ Write-Host "Created directory: $rootCommonPath" -ForegroundColor Green
+ }
+
+ if (-not (Test-Path -Path $rootDuplicatesPath -PathType Container)) {
+ New-Item -ItemType Directory -Path $rootDuplicatesPath | Out-Null
+ Write-Host "Created directory: $rootDuplicatesPath" -ForegroundColor Green
+ }
+
+ # Track filenames already in \textures\common
+ $filesInRootCommon = @{}
+
+ # Process each checksum group
+ Write-Host "Moving files to \common and \common\duplicates..." -ForegroundColor Yellow
+ $pass2Moved = 0
+ $pass2Duplicates = 0
+
+ foreach ($group in $groupedByChecksum) {
+ $files = $group.Group
+
+ if ($files.Count -eq 1) {
+ # Single file - leave in place (unique file)
+ continue
+ }
+
+ # Multiple files with same checksum (duplicates across blendfiles)
+ $result = Process-DuplicateGroup -Files $files -CommonPath $rootCommonPath -DuplicatesPath $rootDuplicatesPath -FilesInCommon $filesInRootCommon
+ $pass2Moved += $result.MovedCount
+ $pass2Duplicates += $result.DuplicateCount
+ }
+
+ Write-Host ""
+ Write-Host "Pass 2 complete: $pass2Moved file(s) moved to \common, $pass2Duplicates duplicate(s) moved to \common\duplicates" -ForegroundColor Green
}
Write-Host ""
Write-Host "File organization complete!" -ForegroundColor Green
-Write-Host " Files moved to \common: $movedCount" -ForegroundColor Cyan
-Write-Host " Duplicate files moved to \common\duplicates: $duplicateCount" -ForegroundColor Cyan