Files
Mainline/sideline/preset_packs/pack_format.py
David Gwilliam e4b143ff36 feat: Implement Sideline plugin system with consistent terminology
This commit implements the Sideline/Mainline split with a clean plugin architecture:

## Core Changes

### Sideline Framework (New Directory)
- Created  directory containing the reusable pipeline framework
- Moved pipeline core, controllers, adapters, and registry to
- Moved display system to
- Moved effects system to
- Created plugin system with security and compatibility management in
- Created preset pack system with ASCII art encoding in
- Added default font (Corptic) to
- Added terminal ANSI constants to

### Mainline Application (Updated)
- Created  for Mainline stage component registration
- Updated  to register Mainline stages at startup
- Updated  as a compatibility shim re-exporting from sideline

### Terminology Consistency
- : Base class for all pipeline components (sources, effects, displays, cameras)
- : Base class for distributable plugin packages (was )
- : Base class for visual effects (was )
- Backward compatibility aliases maintained for existing code

## Key Features
- Plugin discovery via entry points and explicit registration
- Security permissions system for plugins
- Compatibility management with semantic version constraints
- Preset pack system for distributable configurations
- Default font bundled with Sideline (Corptic.otf)

## Testing
- Updated tests to register Mainline stages before discovery
- All StageRegistry tests passing

Note: This is a major refactoring that separates the framework (Sideline) from the application (Mainline), enabling Sideline to be used by other applications.
2026-03-30 19:41:04 -07:00

128 lines
3.7 KiB
Python

"""
Preset pack format definition.
Defines the structure of preset packs and their TOML-based configuration.
"""
from dataclasses import dataclass, field
from typing import Dict, List, Optional
@dataclass
class PresetPackMetadata:
"""Metadata for a preset pack."""
name: str
version: str
author: str
description: str
sideline_version: str # Compatible Sideline version
created: Optional[str] = None # ISO 8601 timestamp
tags: List[str] = field(default_factory=list)
def to_dict(self) -> Dict:
"""Convert to dictionary for TOML serialization."""
return {
"name": self.name,
"version": self.version,
"author": self.author,
"description": self.description,
"sideline_version": self.sideline_version,
"created": self.created,
"tags": self.tags,
}
@classmethod
def from_dict(cls, data: Dict) -> "PresetPackMetadata":
"""Create from dictionary."""
return cls(
name=data["name"],
version=data["version"],
author=data["author"],
description=data["description"],
sideline_version=data["sideline_version"],
created=data.get("created"),
tags=data.get("tags", []),
)
@dataclass
class PluginEntry:
"""Entry for a plugin in the preset pack."""
name: str
category: str # source, effect, display, camera
encoded_code: str # ASCII art encoded plugin code
permissions: List[str] = field(default_factory=list)
capabilities: List[str] = field(default_factory=list)
def to_dict(self) -> Dict:
"""Convert to dictionary for TOML serialization."""
return {
"name": self.name,
"category": self.category,
"code": self.encoded_code,
"permissions": self.permissions,
"capabilities": self.capabilities,
}
@classmethod
def from_dict(cls, data: Dict) -> "PluginEntry":
"""Create from dictionary."""
return cls(
name=data["name"],
category=data["category"],
encoded_code=data["code"],
permissions=data.get("permissions", []),
capabilities=data.get("capabilities", []),
)
@dataclass
class PresetEntry:
"""Entry for a preset in the preset pack."""
name: str
config: Dict # Preset configuration (TOML-compatible)
def to_dict(self) -> Dict:
"""Convert to dictionary for TOML serialization."""
return {
"name": self.name,
"config": self.config,
}
@classmethod
def from_dict(cls, data: Dict) -> "PresetEntry":
"""Create from dictionary."""
return cls(
name=data["name"],
config=data["config"],
)
@dataclass
class PresetPack:
"""Complete preset pack with metadata, plugins, and presets."""
metadata: PresetPackMetadata
plugins: List[PluginEntry] = field(default_factory=list)
presets: List[PresetEntry] = field(default_factory=list)
def to_dict(self) -> Dict:
"""Convert to dictionary for TOML serialization."""
return {
"pack": self.metadata.to_dict(),
"plugins": [p.to_dict() for p in self.plugins],
"presets": [p.to_dict() for p in self.presets],
}
@classmethod
def from_dict(cls, data: Dict) -> "PresetPack":
"""Create from dictionary."""
return cls(
metadata=PresetPackMetadata.from_dict(data["pack"]),
plugins=[PluginEntry.from_dict(p) for p in data.get("plugins", [])],
presets=[PresetEntry.from_dict(p) for p in data.get("presets", [])],
)