feat(upload): add bulk folder upload preserving subfolder structure - #6166
feat(upload): add bulk folder upload preserving subfolder structure#6166shadoskill wants to merge 2 commits into
Conversation
Recurse a local folder into inventory, mirroring the directory hierarchy. Regular files and GLTF assets upload per subfolder. New inventory folder options in viewer and inventory menus.
There was a problem hiding this comment.
Pull request overview
Adds a new “Bulk > Folder…” upload flow that preserves local subfolder structure when uploading into inventory, including special handling for GLTF/GLB material uploads so generated Materials/Textures folders are created alongside the model rather than using system folders.
Changes:
- Replaces the single “Bulk…” upload menu item with a “Bulk” submenu containing “Files…” and new “Folder…” actions.
- Implements recursive folder scanning + inventory folder creation, and routes folder uploads through the existing bulk upload confirmation floater.
- Extends material upload plumbing to support a separate texture destination folder for GLTF material uploads.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| indra/newview/skins/default/xui/en/menu_viewer.xml | Adds “Bulk” submenu and new “Folder…” entry in the main Upload menu. |
| indra/newview/skins/default/xui/en/menu_inventory.xml | Adds “Bulk” submenu and new “Folder…” entry for inventory-context upload location. |
| indra/newview/llviewermenufile.h | Extends bulk upload API and declares new folder-upload entry points. |
| indra/newview/llviewermenufile.cpp | Implements folder scan + recursive upload, and GLTF upload-to-subfolders behavior. |
| indra/newview/llmaterialeditor.h | Extends upload API and adds a texture-destination member. |
| indra/newview/llmaterialeditor.cpp | Wires texture destination through material uploads so textures can land outside system folders. |
| indra/newview/llinventoryfunctions.cpp | Adds inventory action handler for “upload_bulk_folder”. |
| indra/newview/llfloaterbulkupload.h | Stores local folder path for folder-upload confirmation flow. |
| indra/newview/llfloaterbulkupload.cpp | Starts recursive folder upload when the bulk floater represents a folder upload. |
| indra/newview/lldirpicker.cpp | Implements modeless directory picking on Darwin and enables modeless picker thread path. |
Suppressed comments (1)
indra/newview/llviewermenufile.cpp:1554
- upload_folder_recursive_impl() also calls is_regular_file(it->status()) / is_directory(it->status()) without using an error_code overload. This can throw on filesystem errors (permissions, broken links), crashing during the actual upload traversal. Use error_code-based status/symlink_status and handle failures by skipping entries.
for (bfs::directory_iterator it(local_dir, ec), end; !ec && it != end; ++it)
{
if (bfs::is_regular_file(it->status()))
{
std::string path_str = it->path().string();
std::string ext = gDirUtilp->getExtension(path_str);
if (ext == "gltf" || ext == "glb")
gltf_files.push_back(path_str);
else
regular_files.push_back(path_str);
}
else if (bfs::is_directory(it->status()))
subdirs.emplace_back(it->path().string(), it->path().filename().string());
}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| struct Bridge { | ||
| void (*cb)(bool, std::string&, void*); | ||
| void* ud; | ||
| }; | ||
| Bridge* bridge = new Bridge{callback, userdata}; | ||
| auto vectorCb = [](bool success, std::vector<std::string>& files, void* data) { | ||
| Bridge* b = static_cast<Bridge*>(data); | ||
| std::string path = (!files.empty()) ? files[0] : std::string(); | ||
| b->cb(success && !path.empty(), path, b->ud); | ||
| delete b; | ||
| }; | ||
| return mFilePicker->getOpenFileModeless(LLFilePicker::FFLOAD_DIRECTORY, vectorCb, bridge); |
| for (bfs::directory_iterator it(local_dir, ec), end; !ec && it != end; ++it) | ||
| { | ||
| if (bfs::is_regular_file(it->status())) | ||
| files.push_back(it->path().string()); | ||
| else if (bfs::is_directory(it->status())) | ||
| collect_files_recursive(it->path().string(), files); | ||
| } |
On macOS, if the directory picker fails to open, the Bridge adapter object was allocated but never freed. Now deleted on failure. In folder scanning, calling status() on a directory entry without an error code can throw if a file is unreadable. Both recursive scan functions now use the error_code overload and skip problem entries instead of crashing.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Suppressed comments (2)
indra/newview/skins/default/xui/en/menu_viewer.xml:1801
- The Upload.CalculateCosts parameter still targets menu name "Bulk Upload" (used by LLUploadCostCalculator -> gMenuHolder->childSetLabelArg), but this menu item is now named "Bulk Upload Files". Update the parameter (and/or the item's name) so the cost calculator isn't looking up a non-existent child control.
<menu_item_call.on_visible
function="Upload.CalculateCosts"
parameter="Bulk Upload,texture" />
<menu_item_call.on_click
function="File.UploadBulk"
indra/newview/skins/default/xui/en/menu_inventory.xml:1065
- The Upload.CalculateCosts parameter is still "Bulk Upload,texture", but the relevant menu item is now named "Bulk Upload Files". LLUploadCostCalculator uses this string for gMenuHolder->childSetLabelArg lookup, so the name/parameter mismatch should be resolved.
<menu_item_call.on_visible
function="Upload.CalculateCosts"
parameter="Bulk Upload,texture" />
</menu_item_call>
| <menu | ||
| label="Bulk" | ||
| layout="topleft" | ||
| name="Bulk Upload"> | ||
| name="Bulk Upload >"> |
| <menu | ||
| label="Bulk" | ||
| layout="topleft" | ||
| name="Bulk Upload"> | ||
| <menu_item_call.on_click | ||
| function="Inventory.FileUploadLocation" | ||
| parameter="upload_bulk" /> | ||
| <menu_item_call.on_visible | ||
| function="Upload.CalculateCosts" | ||
| parameter="Bulk Upload,texture" /> | ||
| </menu_item_call> | ||
| name="Bulk Upload >"> |
Description
This adds a new option to bulk upload a folder structure and allows a user to upload to a specific inventory folder without needing to change the default upload location every time.
Bulk folder upload from the Build > Upload menu will place the folder into the root "Inventory" folder.
Bulk folder uploading a GLTF material will instead of placing the Textures and Material into the system folders create new Material and Texture folders in the same folder as the GLTF/GLB file its self.
Checklist
Please ensure the following before requesting review:
secondlife-bin_sewmrkqwU5_10mb.mp4