2025-12-09

This commit is contained in:
2026-03-17 15:03:35 -06:00
parent 4b82b57113
commit aae574f8dc
137 changed files with 17355 additions and 4067 deletions
@@ -0,0 +1,59 @@
# BasedPlayblast
**Easily create playblasts from Blender**
BasedPlayblast is a Blender addon that streamlines the process of creating video playblasts for animation review. It provides optimized render settings for fast preview generation while maintaining visual quality suitable for review purposes.
## Features
- **Fast Playblast Creation**: Optimized render settings for different preview modes (Solid, Material, Rendered)
- **Multiple Display Modes**: Support for Wireframe, Solid, Material Preview, and Rendered modes
- **Flexible Resolution**: Scene, preset, or custom resolution options
- **Video Format Support**: MP4, MOV, AVI, MKV with various codecs (H.264, H.265, AV1, etc.)
- **Metadata Integration**: Automatic inclusion of frame numbers, camera info, and custom notes
- **Settings Management**: Apply and restore render settings without losing your project configuration
- **Flamenco Support**: Custom Flamenco Job Script with a simple, non-destructive workflow
## Installation
### Via BlenderKit's Extension Repository (Recommended)
1. Open Blender (5.0+)
2. Install BlenderKit via https://www.blenderkit.com/get-blenderkit/
3. Open Preferences (Ctrl + ,)
4. Go to **Edit > Preferences > Get Extensions**
5. Search for "BasedPlayblast"
6. Click **Install**
7. Enjoy automatic updating!
### Manual Installation
1. Download the latest release, or the release that supports your intended Blender version
2. In Blender, go to **Edit > Preferences > Add-ons**
3. Click **Install from Disk** and select the downloaded file
4. Enable the addon in the list
## Usage
1. **Locate the Panel**: Go to **Properties > Output > BasedPlayblast**
2. **Configure Settings**: Set your output path, resolution, and display mode
3. **Create Playblast**: Click the **PLAYBLAST** button
4. **View Result**: Click **VIEW** to open the generated video
- **Apply Blast Settings**: Use this button to apply optimized render settings without rendering
- Intended particularly for Flamenco. Apply, check the resultant render settings to ensure they're correct, then send to Flamenco using the BasedPlayblast custom Job type.
- **Restore Original Settings**: Return to your original render configuration
- **Display Modes**:
- **Wireframe/Solid**
- Fast workbench viewport rendering. Recommended for short and/or locally-blasted projects.
- **Material**
- **Rendered**
## Requirements
- Blender 5.0.0 or higher
- Python 3.x (included with Blender)
## Support
- **Documentation**: [GitHub Repository](https://github.com/RaincloudTheDragon/BasedPlayblast)
- **Issues**: Report bugs or request features on GitHub
- **License**: GPL-3.0-or-later
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,28 @@
schema_version = "1.0.0"
id = "basedplayblast"
name = "BasedPlayblast"
tagline = "Easily create playblasts from Blender and Flamenco"
version = "2.4.0"
type = "add-on"
maintainer = "RaincloudTheDragon <raincloudthedragon@gmail.com>"
license = ["GPL-3.0-or-later"]
blender_version_min = "4.2.0"
website = "https://github.com/RaincloudTheDragon/BasedPlayblast"
tags = ["Animation", "Render", "Workflow", "Video"]
[permissions]
files = "Import/export files and data"
[build]
paths_exclude_pattern = [
"__pycache__/",
"*.pyc",
".git/",
".github/",
"addon_updater*",
"basedplayblast_updater/"
]
@@ -0,0 +1,164 @@
import bpy # type: ignore
RAINYS_EXTENSIONS_REPO_NAME = "Rainy's Extensions"
RAINYS_EXTENSIONS_REPO_URL = (
"https://raw.githubusercontent.com/RaincloudTheDragon/rainys-blender-extensions/refs/heads/main/index.json"
)
_BOOTSTRAP_DONE = False
def _log(message: str) -> None:
print(f"RainysExtensionsCheck: {message}")
def ensure_rainys_extensions_repo(_deferred: bool = False) -> None:
"""
Ensure the Rainy's Extensions repository is registered in Blender.
Safe to import and call from multiple add-ons; the helper guards against doing the
work more than once per Blender session.
"""
global _BOOTSTRAP_DONE
if _BOOTSTRAP_DONE:
return
_log("starting repository verification")
context_class_name = type(bpy.context).__name__
if context_class_name == "_RestrictContext":
if _deferred:
_log("context still restricted after deferral; aborting repo check")
return
_log("context restricted; scheduling repo check retry")
def _retry():
ensure_rainys_extensions_repo(_deferred=True)
return None
bpy.app.timers.register(_retry, first_interval=0.5)
return
prefs = getattr(bpy.context, "preferences", None)
if prefs is None:
_log("no preferences available on context; skipping")
return
preferences_changed = False
addon_prefs = None
addon_entry = None
if hasattr(getattr(prefs, "addons", None), "get"):
addon_entry = prefs.addons.get(__name__)
elif hasattr(prefs, "addons"):
try:
addon_entry = prefs.addons[__name__]
except Exception:
addon_entry = None
if addon_entry:
addon_prefs = getattr(addon_entry, "preferences", None)
addon_repo_initialized = bool(
addon_prefs and getattr(addon_prefs, "repo_initialized", False)
)
experimental = getattr(prefs, "experimental", None)
if experimental and hasattr(experimental, "use_extension_platform"):
if not experimental.use_extension_platform:
experimental.use_extension_platform = True
preferences_changed = True
_log("enabled experimental extension platform")
repositories = None
extensions_obj = getattr(prefs, "extensions", None)
if extensions_obj:
if hasattr(extensions_obj, "repos"):
repositories = extensions_obj.repos
elif hasattr(extensions_obj, "repositories"):
repositories = extensions_obj.repositories
if repositories is None:
filepaths = getattr(prefs, "filepaths", None)
repositories = getattr(filepaths, "extension_repos", None) if filepaths else None
if repositories is None:
_log("extension repositories collection missing; skipping")
return
def _repo_matches(repo) -> bool:
return getattr(repo, "remote_url", "") == RAINYS_EXTENSIONS_REPO_URL or getattr(
repo, "url", ""
) == RAINYS_EXTENSIONS_REPO_URL
matching_indices = [idx for idx, repo in enumerate(repositories) if _repo_matches(repo)]
target_repo = None
if matching_indices:
target_repo = repositories[matching_indices[0]]
if len(matching_indices) > 1 and hasattr(repositories, "remove"):
for dup_idx in reversed(matching_indices[1:]):
try:
repositories.remove(dup_idx)
_log(f"removed duplicate repository entry at index {dup_idx}")
except Exception as exc:
_log(f"could not remove duplicate repository at index {dup_idx}: {exc}")
else:
target_repo = next(
(
repo
for repo in repositories
if getattr(repo, "name", "") == RAINYS_EXTENSIONS_REPO_NAME
),
None,
)
if target_repo is None:
_log("repo missing; creating new entry")
if hasattr(repositories, "new"):
target_repo = repositories.new()
elif hasattr(repositories, "add"):
target_repo = repositories.add()
else:
_log("repository collection does not support creation; aborting")
return
else:
_log("repo entry already present; validating fields")
changed = preferences_changed
def _ensure_attr(obj, attr, value):
if hasattr(obj, attr) and getattr(obj, attr) != value:
setattr(obj, attr, value)
return True
if not hasattr(obj, attr):
_log(f"repository entry missing attribute '{attr}', skipping field")
return False
changed |= _ensure_attr(target_repo, "name", RAINYS_EXTENSIONS_REPO_NAME)
changed |= _ensure_attr(target_repo, "module", "rainys_extensions")
changed |= _ensure_attr(target_repo, "use_remote_url", True)
changed |= _ensure_attr(target_repo, "remote_url", RAINYS_EXTENSIONS_REPO_URL)
changed |= _ensure_attr(target_repo, "use_sync_on_startup", True)
changed |= _ensure_attr(target_repo, "use_cache", True)
changed |= _ensure_attr(target_repo, "use_access_token", False)
if addon_prefs and hasattr(addon_prefs, "repo_initialized") and not addon_prefs.repo_initialized:
addon_prefs.repo_initialized = True
changed = True
if not changed:
_log("repository already configured; skipping preference save")
_BOOTSTRAP_DONE = True
return
if hasattr(bpy.ops, "wm") and hasattr(bpy.ops.wm, "save_userpref"):
try:
bpy.ops.wm.save_userpref()
_log("preferences updated and saved")
except Exception as exc: # pragma: no cover
print(f"RainysExtensionsCheck: could not save preferences after repo update -> {exc}")
else:
_log("preferences API unavailable; changes not persisted")
_BOOTSTRAP_DONE = True