Initial SE Palette add-on
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""Run with: blender --background --factory-startup --python tests/blender_smoke.py"""
|
||||
|
||||
import importlib.util
|
||||
import sys
|
||||
import types
|
||||
from pathlib import Path
|
||||
|
||||
import bpy
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
PACKAGE_NAME = "blender_se_palette"
|
||||
|
||||
spec = importlib.util.spec_from_file_location(
|
||||
PACKAGE_NAME,
|
||||
ROOT / "__init__.py",
|
||||
submodule_search_locations=[str(ROOT)],
|
||||
)
|
||||
addon = importlib.util.module_from_spec(spec)
|
||||
sys.modules[PACKAGE_NAME] = addon
|
||||
spec.loader.exec_module(addon)
|
||||
|
||||
addon.register()
|
||||
try:
|
||||
assert hasattr(addon.panels, "SEPALETTE_PT_main")
|
||||
scene = bpy.context.scene
|
||||
props = scene.se_palette
|
||||
props.folder = "/home/hare/Assets/SE/"
|
||||
count = addon.operators.populate_items(props)
|
||||
assert count == 42, f"Expected 42 sounds, got {count}"
|
||||
|
||||
valid_icons = bpy.types.UILayout.bl_rna.functions["operator"].parameters["icon"].enum_items.keys()
|
||||
for icon, *_rest in addon.operators.ICON_ITEMS:
|
||||
assert icon in valid_icons, f"Unknown Blender icon: {icon}"
|
||||
|
||||
# Headless Blender has no OpenAL output device, but the same decoder used by
|
||||
# the preview operator must still be able to load the selected file.
|
||||
import aud
|
||||
|
||||
aud.Sound(props.items[0].filepath)
|
||||
|
||||
# Repeated previews must reuse a single output device instead of leaking
|
||||
# one OpenAL device per button press.
|
||||
real_aud = sys.modules["aud"]
|
||||
created_devices = []
|
||||
|
||||
class FakeHandle:
|
||||
status = 1
|
||||
|
||||
def stop(self):
|
||||
self.status = 3
|
||||
|
||||
class FakeDevice:
|
||||
def __init__(self):
|
||||
created_devices.append(self)
|
||||
|
||||
def play(self, sound):
|
||||
return FakeHandle()
|
||||
|
||||
def stopAll(self):
|
||||
pass
|
||||
|
||||
sys.modules["aud"] = types.SimpleNamespace(
|
||||
Device=FakeDevice,
|
||||
Sound=lambda filepath: filepath,
|
||||
STATUS_PLAYING=1,
|
||||
)
|
||||
try:
|
||||
addon.operators.stop_preview(release_device=True)
|
||||
addon.operators._play_preview(props.items[0].filepath)
|
||||
first_device = addon.operators._preview_device
|
||||
addon.operators.stop_preview()
|
||||
addon.operators._play_preview(props.items[1].filepath)
|
||||
assert addon.operators._preview_device is first_device
|
||||
assert len(created_devices) == 1
|
||||
finally:
|
||||
addon.operators.stop_preview(release_device=True)
|
||||
sys.modules["aud"] = real_aud
|
||||
|
||||
result = bpy.ops.se_palette.insert(filepath=props.items[0].filepath)
|
||||
assert result == {"FINISHED"}, result
|
||||
assert len(scene.sequence_editor.strips_all) == 1
|
||||
strip = scene.sequence_editor.active_strip
|
||||
assert strip.type == "SOUND"
|
||||
assert strip.channel == props.channel
|
||||
assert Path(strip.sound.filepath).name == props.items[0].filename
|
||||
finally:
|
||||
addon.unregister()
|
||||
|
||||
print("SE_PALETTE_BLENDER_SMOKE_OK")
|
||||
@@ -0,0 +1,35 @@
|
||||
import json
|
||||
import tempfile
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
from library import default_label, load_config, save_config, scan_audio_files
|
||||
|
||||
|
||||
class LibraryTests(unittest.TestCase):
|
||||
def test_scan_filters_and_sorts_supported_audio(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
for filename in ("02_b.WAV", "01_a.mp3", "memo.txt"):
|
||||
(root / filename).touch()
|
||||
self.assertEqual(
|
||||
[path.name for path in scan_audio_files(directory)],
|
||||
["01_a.mp3", "02_b.WAV"],
|
||||
)
|
||||
|
||||
def test_default_label_removes_number_prefix(self):
|
||||
self.assertEqual(default_label(Path("01_和太鼓でドン.mp3")), "和太鼓でドン")
|
||||
|
||||
def test_config_round_trip(self):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
path = save_config(
|
||||
directory,
|
||||
[{"filename": "se.mp3", "label": "SE", "icon": "INFO", "icon_path": ""}],
|
||||
)
|
||||
self.assertTrue(path.is_file())
|
||||
self.assertEqual(load_config(directory)["items"]["se.mp3"]["label"], "SE")
|
||||
json.loads(path.read_text(encoding="utf-8"))
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user