# Update path structure for renders (2025-11-06 17:28Z)
_**User (2025-11-06 17:28Z)**_
@TalkingHeads Custom Render.js @TalkingHeads cycles_optix_gpu.js we're updating the path structure: no more dailies; sequences should just go straight to their Renders\blendname folder, or their submodule if checked (Renders\submodule\blendname)
I’m working through updating the render output paths to remove the daily folder concept entirely, restructuring sequences to go directly under `Renders\<blendname>` or `Renders\<submodule>\<blendname>`. This means removing all daily path references and adjusting settings, eval expressions, and functions accordingly, while ensuring the blend name drives folder naming instead of job name or daily paths. There's complexity balancing backward compatibility and clarity, but I’m focusing on simplifying by basing paths on the blend file stem and cleaning out obsolete daily-related code.
**Detailing patch preparation for daily removal**
I’m refining the plan to remove all daily path handling, carefully adjusting path parts buildup to exclude daily while allowing empty strings in path joins without breaking structure. I’m systematically identifying code blocks to remove or update, including settings entries, print statements, and obsolete functions like `formatDailyYYMMDD`. Next, I’ll create a precise patch with multiple changes to ensure all references to daily paths get cleared consistently across relevant scripts.</details></think>
eval: "__import__('os').path.normpath(__import__('os').path.join(((__import__('re').search(r'^(.*?)[\\/][Bb]lends[\\/]', bpy.data.filepath.replace('\\\\','/')) and __import__('re').search(r'^(.*?)[\\/][Bb]lends[\\/]', bpy.data.filepath.replace('\\\\','/')).group(1)) or __import__('os').path.dirname(bpy.data.filepath)), 'Renders'))",
evalInfo: {
showLinkButton: true,
description: "Auto-detect the project's Renders folder"
},
description: "Base path where renders are stored, typically the project's Renders folder. If empty, derived automatically."
- description: "Auto-fill with today's daily folder name"
- },
- description: "Daily folder name under the render root, e.g. 'daily_250813'. If empty, auto-fills to today's date."
- },
{
key: "use_submodule",
label: "Use Submodule",
type: "bool",
required: false,
@@
{
key: "render_output_path",
type: "string",
subtype: "file_path",
editable: false,
- eval: "str(Path(abspath(settings.render_output_root or '//'), ((str(settings.submodule or '').strip()) if (settings.use_submodule and str(settings.submodule or '').strip()) else ((__import__('os').path.basename(__import__('os').path.dirname(bpy.data.filepath))) if settings.use_submodule else '')), (settings.daily_path or __import__('datetime').datetime.now().strftime('daily_%y%m%d')), jobname, jobname + '_######'))",
+ eval: "((lambda blendname: str(Path(abspath(settings.render_output_root or '//'), ((str(settings.submodule or '').strip()) if (settings.use_submodule and str(settings.submodule or '').strip()) else ((__import__('os').path.basename(__import__('os').path.dirname(bpy.data.filepath))) if settings.use_submodule else '')), blendname, blendname + '_######')))(__import__('os').path.splitext(__import__('os').path.basename(bpy.data.filepath))[0]))",
description: "Final file path of where render output will be saved"
},
@@
- if (!settings.daily_path || String(settings.daily_path).trim() === '') {
- const createdDate = job && job.created ? new Date(job.created) : new Date();
"__import__('os').path.normpath(__import__('os').path.join(((__import__('re').search(r'^(.*?)[\\/][Bb]lends[\\/]', bpy.data.filepath.replace('\\\\','/')) and __import__('re').search(r'^(.*?)[\\/][Bb]lends[\\/]', bpy.data.filepath.replace('\\\\','/')).group(1)) or __import__('os').path.dirname(bpy.data.filepath)), 'Renders'))",
- "Daily folder name under the render root, e.g. 'daily_250813'. If empty, auto-fills to today's date.",
- },
{
key: 'use_submodule',
label: 'Use Submodule',
type: 'bool',
required: false,
@@
{
key: 'render_output_path',
type: 'string',
subtype: 'file_path',
editable: false,
- eval: "str(Path(abspath(settings.render_output_root or '//'), ((str(settings.submodule or '').strip()) if (settings.use_submodule and str(settings.submodule or '').strip()) else ((__import__('os').path.basename(__import__('os').path.dirname(bpy.data.filepath))) if settings.use_submodule else '')), (settings.daily_path or __import__('datetime').datetime.now().strftime('daily_%y%m%d')), jobname, jobname + '_######'))",
+ eval:
+ "((lambda blendname: str(Path(abspath(settings.render_output_root or '//'), ((str(settings.submodule or '').strip()) if (settings.use_submodule and str(settings.submodule or '').strip()) else ((__import__('os').path.basename(__import__('os').path.dirname(bpy.data.filepath))) if settings.use_submodule else '')), blendname, blendname + '_######')))(__import__('os').path.splitext(__import__('os').path.basename(bpy.data.filepath))[0]))",
description: 'Final file path of where render output will be saved',
},
@@
- if (!settings.daily_path || String(settings.daily_path).trim() === '') {
- const createdDate = job && job.created ? new Date(job.created) : new Date();
I’m gathering precise code snippets with line numbers to clearly show modifications, confirming no leftover 'daily' references, and planning to explain changes, paths, and verification steps in the final summary.</details></think>
**Summarizing configuration changes and message structure**
I'll clearly explain the removal of `daily_path` and the new default for `render_output_path`, then prepare a concise final message with sections for summary, code references—showing precise code snippets with line numbers—and next steps like triggering a test render, ensuring clarity and completeness.</details></think>
---
## Summary
- Updated both TalkingHeads job types to drop `daily_path` usage; auto paths now go straight to `Renders/<blendname>` or `Renders/<submodule>/<blendname>`.
- Simplified compile-time autofill logic to rely on the blend name, keeping optional submodule support untouched.
## Code References
- Render output defaults now skip the daily folder and target the blend-name directory.
```71:76:scripts/TalkingHeads Custom Render.js
{
key: "render_output_path",
type: "string",
subtype: "file_path",
editable: false,
eval: "((lambda blendname: str(Path(abspath(settings.render_output_root or '//'), ((str(settings.submodule or '').strip()) if (settings.use_submodule and str(settings.submodule or '').strip()) else ((__import__('os').path.basename(__import__('os').path.dirname(bpy.data.filepath))) if settings.use_submodule else '')), blendname, blendname + '_######')))(__import__('os').path.splitext(__import__('os').path.basename(bpy.data.filepath))[0]))",
description: "Final file path of where render output will be saved"
```
```60:65:scripts/TalkingHeads cycles_optix_gpu.js
{
key: 'render_output_path',
type: 'string',
subtype: 'file_path',
editable: false,
eval:
"((lambda blendname: str(Path(abspath(settings.render_output_root or '//'), ((str(settings.submodule or '').strip()) if (settings.use_submodule and str(settings.submodule or '').strip()) else ((__import__('os').path.basename(__import__('os').path.dirname(bpy.data.filepath))) if settings.use_submodule else '')), blendname, blendname + '_######')))(__import__('os').path.splitext(__import__('os').path.basename(bpy.data.filepath))[0]))",
```
- Auto-path assembly now appends only the blend name (plus frame pattern), preserving optional submodule handling.
```187:218:scripts/TalkingHeads Custom Render.js
// Derive project root and submodule from the blendfile path.
- eval: "((lambda blendname: str(Path(abspath(settings.render_output_root or '//'), ((str(settings.submodule or '').strip()) if (settings.use_submodule and str(settings.submodule or '').strip()) else ((__import__('os').path.basename(__import__('os').path.dirname(bpy.data.filepath))) if settings.use_submodule else '')), blendname, blendname + '_######')))(__import__('os').path.splitext(__import__('os').path.basename(bpy.data.filepath))[0]))",
- description: "Final file path of where render output will be saved"
- },
+ {
+ key: "render_output_path",
+ type: "string",
+ subtype: "file_path",
+ editable: false,
+ eval: "((lambda Path, blend: str(Path(abspath(settings.render_output_root or '//')) / (((str(settings.submodule or '').strip()) if (settings.use_submodule and str(settings.submodule or '').strip()) else ((__import__('os').path.basename(__import__('os').path.dirname(bpy.data.filepath))) if settings.use_submodule else ''))) / blend / (blend + '_######')))(__import__('pathlib').Path, __import__('os').path.splitext(__import__('os').path.basename(bpy.data.filepath))[0]))",
+ description: "Final file path of where render output will be saved"
- "((lambda blendname: str(Path(abspath(settings.render_output_root or '//'), ((str(settings.submodule or '').strip()) if (settings.use_submodule and str(settings.submodule or '').strip()) else ((__import__('os').path.basename(__import__('os').path.dirname(bpy.data.filepath))) if settings.use_submodule else '')), blendname, blendname + '_######')))(__import__('os').path.splitext(__import__('os').path.basename(bpy.data.filepath))[0]))",
- description: 'Final file path of where render output will be saved',
- },
+ {
+ key: 'render_output_path',
+ type: 'string',
+ subtype: 'file_path',
+ editable: false,
+ eval:
+ "((lambda Path, blend: str(Path(abspath(settings.render_output_root or '//')) / (((str(settings.submodule or '').strip()) if (settings.use_submodule and str(settings.submodule or '').strip()) else ((__import__('os').path.basename(__import__('os').path.dirname(bpy.data.filepath))) if settings.use_submodule else ''))) / blend / (blend + '_######')))(__import__('pathlib').Path, __import__('os').path.splitext(__import__('os').path.basename(bpy.data.filepath))[0]))",
+ description: 'Final file path of where render output will be saved',
**Preparing response with fix explanation**</details></think>
---
Updated both TalkingHeads job types so the `render_output_path` eval now inlines `Path` via `__import__('pathlib').Path`, eliminating the NameError while keeping the new render-folder layout. Let me know if Blender still complains or if we should adjust the join logic further.
---
_**User (2025-11-06 17:28Z)**_
Python: Traceback (most recent call last):
File "C:\Program Files (x86)\Steam\steamapps\common\Blender\portable\scripts\addons\flamenco\job_types_propgroup.py", line 120, in eval_setting
value = eval(setting_eval, {}, eval_locals)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "<string>", line 1, in <module>
File "<string>", line 1, in <lambda>
NameError: name 'abspath' is not defined
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "C:\Program Files (x86)\Steam\steamapps\common\Blender\portable\scripts\addons\flamenco\job_types_propgroup.py", line 122, in eval_setting
raise SettingEvalError(setting_key, setting_eval, eval_locals, ex) from ex
flamenco.job_types_propgroup.SettingEvalError: Evaluation error of setting 'render_output_path': name 'abspath' is not defined
- "((lambda Path, blend: str(Path(abspath(settings.render_output_root or '//')) / (((str(settings.submodule or '').strip()) if (settings.use_submodule and str(settings.submodule or '').strip()) else ((__import__('os').path.basename(__import__('os').path.dirname(bpy.data.filepath))) if settings.use_submodule else ''))) / blend / (blend + '_######')))(__import__('pathlib').Path, __import__('os').path.splitext(__import__('os').path.basename(bpy.data.filepath))[0]))",
- description: 'Final file path of where render output will be saved',
- },
+ {
+ key: 'render_output_path',
+ type: 'string',
+ subtype: 'file_path',
+ editable: false,
+ eval:
+ "((lambda Path, abspath, os_path, blend: str(Path(abspath(settings.render_output_root or '//')) / (((str(settings.submodule or '').strip()) if (settings.use_submodule and str(settings.submodule or '').strip()) else ((os_path.basename(os_path.dirname(bpy.data.filepath))) if settings.use_submodule else ''))) / blend / (blend + '_######')))(__import__('pathlib').Path, __import__('os').path.abspath, __import__('os').path, __import__('os').path.splitext(__import__('os').path.basename(bpy.data.filepath))[0]))",
+ description: 'Final file path of where render output will be saved',
I'm figuring out why the new lambda-based eval code can't see the `settings` variable, while the old string eval could. It looks like Python lambdas capture free variables differently, needing `settings` passed explicitly as a parameter, so I'll modify the lambda to accept `settings` and update the calls accordingly to fix the NameError problem.</details></think>
<summary>Tool use: **read_lints** • Read lints for 2 files</summary>
Lint paths:
-`scripts/TalkingHeads Custom Render.js`
-`scripts/TalkingHeads cycles_optix_gpu.js`
Lint results
**No lint errors found**
</details>
</tool-use>
---
Wrapped the eval in both job types so the lambda now receives `settings` explicitly. Blender should no longer raise “name 'settings' is not defined” when resolving `render_output_path`. Let me know if any other eval errors pop up.