← All ideas

GET YOUR SHIT TOGETHER. As in 'Gather Resources'. (collect videos etc)

"Get your shit together" ... it's what your boss says when you hand them a blend file with pink where videos should be.

Most video editors offer a feature to "collect files"—organizing media (clips, images, audio) into a single directory to avoid broken links. In Blender, to avoid confusion with 'Collections', let’s call this 'GATHERING' files.

Blender’s VSE lacks a simple way to gather media in one place. The "Pack Resources" feature only applies to 3D assets, not video clips, often leaving editors with missing files and pink placeholders.

Even small projects can become tedious without file gathering. Editors need a quick, reliable way to gather everything for smooth collaboration and hand-offs.

The Solution: New Option: "Gather Resources" File > External Data > Gather Resources

Tool-tip: Gathers all project-related media (clips, audio, images, textures) into a directory next to the .blend file.

Why it Works:

  • Relative paths keep everything linked across devices.
  • Simple organization: All files in one place, easy for collaboration and backup.
  • Non-intrusive: Keeps .blend files light without embedding media, ensuring portability.

FAQ:

  • "Wait, why not Pack Resources again?"

"Pack Resources" doesn’t cover video clips, leading to missing media errors.

  • "Why not use image sequences?"

Slows playback and hampers portability due to larger file sizes.

  • "Extend Pack Resources to include clips, etc?"

Blender decided against storing clips inside .blend files for portability. Instead, this new feature handles the gap more effectively.

  • "Get your shit together… sounds familiar..."

Yes, from justinmcclure.com/gyst . Check out their project structure tips for production workflows. I'm not officially associated, but I like their approach to organisation, and I sure as shit love a good excuse to swear and joke at the same time.

  • *"Videos are good, but what about '(Insert file format)'?

Yep, why not all kinds asset dependencies indeed? If you have a file that the test addon cannot correctly gather; tell me down below, and I will endeaver to include the functionality.

Not convinced? Try the Addon!

github.com/SimonHeggie/Blender-GatherResources

Feel free to test and report bugs! Gather Resources.png

100%

Development Status: Open

Upvote 17 Downvote 0
simon.p.heggie
15 Oct 2024

5 Comments

lee2key

Also .abc file are not collecting into the blend file. Maybe an idea for another topic

1
simon.p.heggie Author

Actually I think it's worth including to hit as many birds as possible with the one stone.

According the the devs; Blender does not allow packing Alembic (.abc) files into .blend files mainly due to performance and stability concerns. Alembic files are often very large, making it impractical to store them inside .blend files without risking slowdowns or crashes, especially with complex simulations​.

projects.blender.org/blender/blender/issues/118839 projects.blender.org/blender/blender/issues/78857 ​ Instead, Alembic files are designed to function as external caches, referenced via relative paths to maintain performance.

Some decent insight provided here too:

github.com/cgmb/alembic-example

But yeah as you see .ABC files align with the workflow philosophy similar to video files: keeping them external to avoid bloated .blend files. A solution like “Collect Resources (& Clips)” SHOULD in theory also collect .abc files. I honestly don't see why not. We can just adjust he script to include .ABC files... in theory it should work.

Here's a new version of the script. Needs testing still but in theory it could work.

Remember to de-indent it (Shift-tab)

import bpy
import shutil
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed

# Define supported file extensions for gathering
CLIP_FILES = {'.mp4', '.mov', '.avi', '.mkv', '.flv', '.webm', '.ogg', '.mpeg'}
ABC_FILES = {'.abc'}  # Alembic files for animations and geometry caches
IMAGE_FILES = {'.jpg', '.jpeg', '.png', '.tga', '.exr', '.dpx', '.cin', 
            '.hdr', '.sgi', '.tif', '.tiff'}
3D_FILES = {'.fbx', '.obj', '.dae', '.stl', '.ply', '.x', '.lwo', 
            '.bvh', '.3ds', '.vrml', '.wrl', '.x3d', '.usd'}

# Cache files include both Alembic (.abc) and USD (.usd) formats
CACHE_FILES = ABC_FILES | {'.usd'}

def copy_file(src, dest):
    """Helper function to copy files with error handling."""
    try:
        if not dest.exists() or src.stat().st_mtime > dest.stat().st_mtime:
            shutil.copy2(src, dest)
            print(f"Collected: {src} -> {dest}")
        return src.name, True
    except PermissionError as e:
        print(f"Error copying {src}: {e}")
        return src.name, False

def gather_resources():
    # Set destination directory
    destination = Path(bpy.path.abspath("//textures/"))
    destination.mkdir(parents=True, exist_ok=True)

    collected = {"MOVIE": 0, "IMAGE": 0, "SOUND": 0, "ALEMBIC": 0, "3D": 0, "CACHE": 0}
    tasks = []

    # Gather all media tasks using multi-threading
    with ThreadPoolExecutor() as executor:
        for scene in bpy.data.scenes:
            if scene.sequence_editor:
                for strip in scene.sequence_editor.sequences_all:
                    if strip.type in {'MOVIE', 'IMAGE', 'SOUND'}:
                        src = Path(bpy.path.abspath(strip.filepath))
                        dest = destination / src.name
                        tasks.append(executor.submit(copy_file, src, dest))

            # Handle Alembic and other cache files used in modifiers
            for obj in scene.objects:
                if obj.modifiers:
                    for mod in obj.modifiers:
                        if mod.type == 'MESH_SEQUENCE_CACHE' and mod.cache_file:
                            cache_path = Path(bpy.path.abspath(mod.cache_file.filepath))
                            cache_dest = destination / cache_path.name
                            tasks.append(executor.submit(copy_file, cache_path, cache_dest))

    # Process results and update stats
    for future in as_completed(tasks):
        filename, success = future.result()
        if success:
            strip_path = destination / filename

            # Update collection stats based on file type
            if any(filename.endswith(ext) for ext in CLIP_FILES):
                collected['MOVIE'] += 1
            elif filename.endswith(tuple(ABC_FILES)):
                collected['ALEMBIC'] += 1
            elif filename.endswith(tuple(IMAGE_FILES)):
                collected['IMAGE'] += 1
            elif filename.endswith(tuple(3D_FILES)):
                collected['3D'] += 1
            elif filename.endswith(tuple(CACHE_FILES)):
                collected['CACHE'] += 1

    # Display summary of collected files
    print(f"Gathering Complete: {collected['MOVIE']} videos, "
        f"{collected['IMAGE']} images, {collected['SOUND']} audio files, "
        f"{collected['ALEMBIC']} Alembic files, {collected['3D']} 3D models, "
        f"{collected['CACHE']} cache files.")

# Call the function
gather_resources()
simon.p.heggie Author

I have created a github with my first humble attempt to script this myself.

Bit of a carrot on the stick for someone more talented at python coding.

github.com/SimonHeggie/Blender-GatherResources/tree/main

simon.p.heggie Author

Because I am an artist and not a programmer, I got started on the algorithm by directing some AI to fabricate a python script for me.

import bpy
import shutil
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor, as_completed

# Define a list of video file extensions supported by Blender VSE
VIDEO_EXTENSIONS = {'.mp4', '.mov', '.avi', '.mkv', '.flv', '.webm', '.ogg'}

def copy_file(src, dest):
    """Helper function to copy files with error handling."""
    try:
        if not dest.exists() or src.stat().st_mtime > dest.stat().st_mtime:
            shutil.copy2(src, dest)
            print(f"Collected: {src} -> {dest}")
        return src.name, True
    except PermissionError as e:
        print(f"Error copying {src}: {e}")
        return src.name, False

def collect_resources_and_clips():
    # Set destination directory
    destination = Path(bpy.path.abspath("//textures/"))
    destination.mkdir(parents=True, exist_ok=True)

    collected = {"MOVIE": 0, "IMAGE": 0, "SOUND": 0}
    tasks = []

    # Gather all media file tasks
    with ThreadPoolExecutor() as executor:
        for scene in bpy.data.scenes:
            if scene.sequence_editor:
                for strip in scene.sequence_editor.sequences_all:
                    if strip.type in {'MOVIE', 'IMAGE', 'SOUND'}:
                        src = Path(bpy.path.abspath(strip.filepath))
                        dest = destination / src.name

                        # Schedule the copy task
                        tasks.append(executor.submit(copy_file, src, dest))

        # Process results as tasks complete
        for future in as_completed(tasks):
            filename, success = future.result()
            if success:
                # Update path to new relative location
                strip_path = destination / filename
                strip.filepath = bpy.path.relpath(str(strip_path))

                # Update collection stats based on file type or extension
                if strip.type == 'MOVIE' and any(filename.endswith(ext) for ext in VIDEO_EXTENSIONS):
                    collected['MOVIE'] += 1
                elif strip.type == 'IMAGE':
                    collected['IMAGE'] += 1
                elif strip.type == 'SOUND':
                    collected['SOUND'] += 1

    # Display summary of collected files
    print(f"Collection Complete: {collected['MOVIE']} videos, "
        f"{collected['IMAGE']} images, {collected['SOUND']} audio files.")

# Call the function
collect_resources_and_clips()

(Above python is tabbed to make the formatting here clearer)

Next Steps: UI Integration: Hook the script to File > External Data > Collect Resources (& Clips). Testing: Validate the performance on different platforms (Windows, macOS, Linux). Documentation: Provide detailed user instructions and examples.'

Any programmers looking at this, if you can spot any problems off the bat let us know to make it more inviting for someone to take it to completion. If you would like to test the script and report on any errors logged, be my guest.

1
simon.p.heggie Author

Hey guys my script works if you want to try it here:

github.com/SimonHeggie/Blender-GatherResources/tree/main

Main Todos:

-Set it up as an addon -Set up UI

Yes it should work with ALL formats associated with Blender, even .abc files.