From 708f8a0b670fe5c243abc8d1c094bb1e119b33bb Mon Sep 17 00:00:00 2001 From: NotAShelf Date: Tue, 3 Feb 2026 22:29:19 +0300 Subject: [PATCH] examples: add example plugins Signed-off-by: NotAShelf Change-Id: I9eac30c7d4c1c89178f4930b215e523d6a6a6964 --- examples/plugins/README.md | 518 ++++++++++++++++++ examples/plugins/heif-support/README.md | 257 +++++++++ examples/plugins/heif-support/plugin.toml | 37 ++ examples/plugins/markdown-metadata/README.md | 103 ++++ .../plugins/markdown-metadata/plugin.toml | 30 + 5 files changed, 945 insertions(+) create mode 100644 examples/plugins/README.md create mode 100644 examples/plugins/heif-support/README.md create mode 100644 examples/plugins/markdown-metadata/README.md diff --git a/examples/plugins/README.md b/examples/plugins/README.md new file mode 100644 index 0000000..12c5217 --- /dev/null +++ b/examples/plugins/README.md @@ -0,0 +1,518 @@ +# Pinakes Plugin Examples + +This directory contains example plugins demonstrating the Pinakes plugin system. + +## Overview + +Pinakes supports extensibility through a WASM-based plugin system. Plugins can +extend Pinakes functionality by: + +- **Media Type Providers**: Add support for new file formats +- **Metadata Extractors**: Extract metadata from files +- **Thumbnail Generators**: Generate thumbnails for media types +- **Search Backends**: Implement custom search algorithms +- **Event Handlers**: React to system events +- **Theme Providers**: Provide custom UI themes + +## Example Plugins + +### 1. Markdown Metadata Extractor + +**Directory**: `markdown-metadata/` + +Enhances markdown file support with advanced frontmatter parsing. + +**Demonstrates**: + +- Metadata extraction from files +- Plugin configuration via `plugin.toml` +- Minimal capability requirements + +**Plugin Kind**: `metadata_extractor` + +### 2. HEIF/HEIC Support + +**Directory**: `heif-support/` + +Adds support for HEIF and HEIC image formats. + +**Demonstrates**: + +- Media type registration +- Metadata extraction from binary formats +- Thumbnail generation +- Resource limits (memory, CPU time) + +**Plugin Kinds**: `media_type`, `metadata_extractor`, `thumbnail_generator` + +## Plugin Architecture + +### Plugin Structure + +``` +my-plugin/ +├── plugin.toml # Plugin manifest +├── Cargo.toml # Rust project configuration +├── src/ +│ └── lib.rs # Plugin implementation +└── README.md # Plugin documentation +``` + +### Plugin Manifest (plugin.toml) + +```toml +[plugin] +name = "my-plugin" +version = "1.0.0" +api_version = "1.0" +author = "Your Name" +description = "Description of your plugin" +kind = ["metadata_extractor"] + +[plugin.binary] +wasm = "my_plugin.wasm" + +[capabilities] +network = false + +[capabilities.filesystem] +read = ["/path/to/read"] +write = ["/path/to/write"] + +[config] +# Plugin-specific configuration +option1 = "value1" +option2 = 42 +``` + +### Manifest Fields + +#### [plugin] Section + +- `name`: Plugin identifier (must be unique) +- `version`: Semantic version (e.g., "1.0.0") +- `api_version`: Pinakes Plugin API version (currently "1.0") +- `author`: Plugin author (optional) +- `description`: Short description (optional) +- `homepage`: Plugin homepage URL (optional) +- `license`: License identifier (optional) +- `kind`: Array of plugin kinds +- `dependencies`: Array of plugin names this plugin depends on (optional) + +#### [plugin.binary] Section + +- `wasm`: Path to WASM binary (relative to manifest) +- `entrypoint`: Custom entrypoint function name (optional, default: "_start") + +#### [capabilities] Section + +Capabilities define what the plugin can access: + +**Filesystem**: + +```toml +[capabilities.filesystem] +read = ["/tmp/cache", "/var/data"] +write = ["/tmp/output"] +``` + +**Network**: + +```toml +[capabilities] +network = true # or false +``` + +**Environment**: + +```toml +[capabilities] +environment = ["PATH", "HOME"] # or omit for no access +``` + +**Resource Limits**: + +```toml +[capabilities] +max_memory_mb = 128 +max_cpu_time_secs = 10 +``` + +### Plugin Kinds + +#### media_type + +Register new media types with file extensions and MIME types. + +**Trait**: `MediaTypeProvider` + +**Methods**: + +- `supported_media_types()`: Returns list of media type definitions +- `can_handle(path, mime_type)`: Check if plugin can handle a file + +#### metadata_extractor + +Extract metadata from files. + +**Trait**: `MetadataExtractor` + +**Methods**: + +- `extract_metadata(path)`: Extract metadata from file +- `supported_types()`: Returns list of supported media type IDs + +#### thumbnail_generator + +Generate thumbnails for media files. + +**Trait**: `ThumbnailGenerator` + +**Methods**: + +- `generate_thumbnail(path, output_path, options)`: Generate thumbnail +- `supported_types()`: Returns list of supported media type IDs + +#### search_backend + +Implement custom search algorithms. + +**Trait**: `SearchBackend` + +**Methods**: + +- `index_item(item)`: Index a media item +- `remove_item(item_id)`: Remove item from index +- `search(query)`: Perform search +- `get_stats()`: Get index statistics + +#### event_handler + +React to system events. + +**Trait**: `EventHandler` + +**Methods**: + +- `handle_event(event)`: Handle an event +- `interested_events()`: Returns list of event types to receive + +#### theme_provider + +Provide UI themes. + +**Trait**: `ThemeProvider` + +**Methods**: + +- `get_themes()`: List available themes +- `load_theme(theme_id)`: Load theme data + +## Creating a Plugin + +### Step 1: Set Up Project + +```bash +# Create new Rust library project +cargo new --lib my-plugin +cd my-plugin + +# Add dependencies +cat >> Cargo.toml <, +} + +#[async_trait] +impl Plugin for MyPlugin { + fn metadata(&self) -> &PluginMetadata { + // Return plugin metadata + } + + async fn initialize(&mut self, context: PluginContext) -> PluginResult<()> { + self.context = Some(context); + Ok(()) + } + + async fn shutdown(&mut self) -> PluginResult<()> { + Ok(()) + } + + async fn health_check(&self) -> PluginResult { + Ok(HealthStatus { + healthy: true, + message: None, + metrics: Default::default(), + }) + } +} + +#[async_trait] +impl MetadataExtractor for MyPlugin { + async fn extract_metadata(&self, path: &PathBuf) -> PluginResult { + // Extract metadata from file + } + + fn supported_types(&self) -> Vec { + vec!["my_type".to_string()] + } +} +``` + +### Step 3: Build to WASM + +```bash +# Install WASM target +rustup target add wasm32-wasi + +# Build +cargo build --target wasm32-wasi --release + +# Optimize (optional, wasm-tools provides wasm-strip functionality) +cargo install wasm-tools +wasm-tools strip target/wasm32-wasi/release/my_plugin.wasm -o target/wasm32-wasi/release/my_plugin.wasm + +# Copy to plugin directory +cp target/wasm32-wasi/release/my_plugin.wasm . +``` + +### Step 4: Create Manifest + +Create `plugin.toml` with appropriate configuration (see examples above). + +### Step 5: Install Plugin + +```bash +# Copy to plugins directory +cp -r my-plugin ~/.config/pinakes/plugins/ + +# Or use API +curl -X POST http://localhost:3000/api/v1/plugins/install \ + -d '{"source": "/path/to/my-plugin"}' +``` + +## Testing Plugins + +### Unit Tests + +```rust +#[cfg(test)] +mod tests { + use super::*; + + #[tokio::test] + async fn test_metadata_extraction() { + let mut plugin = MyPlugin::default(); + let context = PluginContext { + data_dir: PathBuf::from("/tmp/data"), + cache_dir: PathBuf::from("/tmp/cache"), + config: Default::default(), + capabilities: Default::default(), + }; + + plugin.initialize(context).await.unwrap(); + + let metadata = plugin + .extract_metadata(&PathBuf::from("test.txt")) + .await + .unwrap(); + + assert!(metadata.title.is_some()); + } +} +``` + +### Integration Tests + +```bash +# Load plugin in test Pinakes instance +pinakes --config test-config.toml plugin load /path/to/plugin + +# Verify plugin is loaded +pinakes plugin list + +# Test plugin functionality +pinakes scan /path/to/test/files +``` + +## Security Considerations + +### Capability-Based Security + +Plugins operate in a sandbox with explicit capabilities. Only request the +minimum capabilities needed: + +**Good**: + +```toml +[capabilities.filesystem] +read = ["/tmp/cache"] +``` + +**Bad**: + +```toml +[capabilities.filesystem] +read = ["/", "/home", "/etc"] # Too broad! +``` + +### Resource Limits + +Always set appropriate resource limits: + +```toml +[capabilities] +max_memory_mb = 128 # Reasonable for image processing +max_cpu_time_secs = 10 # Prevent runaway operations +``` + +### Input Validation + +Always validate input in your plugin: + +```rust +async fn extract_metadata(&self, path: &PathBuf) -> PluginResult { + // Check file exists + if !path.exists() { + return Err(PluginError::InvalidInput("File not found".to_string())); + } + + // Check file size + let metadata = std::fs::metadata(path) + .map_err(|e| PluginError::IoError(e.to_string()))?; + if metadata.len() > 10_000_000 { // 10MB limit + return Err(PluginError::InvalidInput("File too large".to_string())); + } + + // Process file... +} +``` + +## Best Practices + +### Error Handling + +- Use descriptive error messages +- Return appropriate `PluginError` variants +- Don't panic - return errors instead + +### Performance + +- Avoid blocking operations in async functions +- Use streaming for large files +- Implement timeouts for external operations +- Cache results when appropriate + +### Configuration + +- Provide sensible defaults +- Document all configuration options +- Validate configuration during initialization + +### Documentation + +- Write clear README with examples +- Document all configuration options +- Include troubleshooting section +- Provide integration examples + +## API Reference + +See the +[pinakes-plugin-api documentation](../../crates/pinakes-plugin-api/README.md) +for detailed API reference. + +## Plugin Distribution + +### Plugin Registry (Future) + +A centralized plugin registry is planned for future releases: + +```bash +# Install from registry +pinakes plugin install markdown-metadata + +# Search plugins +pinakes plugin search heif + +# Update all plugins +pinakes plugin update --all +``` + +### Manual Distribution + +Currently, plugins are distributed as directories containing: + +- `plugin.toml` manifest +- WASM binary +- README and documentation + +## Troubleshooting + +### Plugin Won't Load + +**Check manifest syntax**: + +```bash +# Validate TOML syntax +taplo check plugin.toml +``` + +**Check API version**: Ensure `api_version = "1.0"` in manifest. + +**Check binary path**: Verify WASM binary exists at path specified in +`plugin.binary.wasm`. + +### Plugin Crashes + +**Check resource limits**: Increase `max_memory_mb` or `max_cpu_time_secs` if +operations are timing out. + +**Check capabilities**: Ensure plugin has necessary filesystem/network +capabilities. + +**Check logs**: + +```bash +# View plugin logs +tail -f ~/.local/share/pinakes/logs/plugin.log +``` + +### Permission Denied Errors + +**Filesystem capabilities**: Add required paths to +`capabilities.filesystem.read` or `.write`. + +**Network capabilities**: Set `capabilities.network = true` if plugin needs +network access. + +## Support + +- **Issues**: https://github.com/notashelf/pinakes/issues +- **Discussions**: https://github.com/notashelf/pinakes/discussions +- **Documentation**: https://pinakes.readthedocs.io + +## License + +All example plugins are licensed under MIT. diff --git a/examples/plugins/heif-support/README.md b/examples/plugins/heif-support/README.md new file mode 100644 index 0000000..b64a002 --- /dev/null +++ b/examples/plugins/heif-support/README.md @@ -0,0 +1,257 @@ +# HEIF/HEIC Support Plugin + +This example plugin adds support for HEIF (High Efficiency Image Format) and HEIC (HEIF Container) to Pinakes. + +## Overview + +HEIF is a modern image format that provides better compression than JPEG while maintaining higher quality. This plugin enables Pinakes to: +- Recognize HEIF/HEIC files as a media type +- Extract metadata from HEIF images +- Generate thumbnails from HEIF images + +## Features + +- **Media Type Registration**: Registers `.heif`, `.heic`, `.hif` extensions as image media types +- **EXIF Extraction**: Extracts EXIF metadata including camera info, GPS coordinates, timestamps +- **Thumbnail Generation**: Generates thumbnails in JPEG, PNG, or WebP format +- **Resource Limits**: Configurable memory and CPU limits for safe processing +- **Large Image Support**: Handles images up to 8192x8192 pixels + +## Supported Formats + +- **HEIF**: High Efficiency Image Format (`.heif`, `.hif`) +- **HEIC**: HEIF Container format used by Apple devices (`.heic`) +- **HEIF Sequences**: Multi-image HEIF files +- **HEIF with Alpha**: HEIF images with transparency + +## Implementation + +The plugin implements three traits: + +### MediaTypeProvider + +```rust +#[async_trait] +impl MediaTypeProvider for HeifPlugin { + fn supported_media_types(&self) -> Vec { + vec![MediaTypeDefinition { + id: "heif".to_string(), + name: "HEIF Image".to_string(), + category: "image".to_string(), + extensions: vec!["heif".to_string(), "heic".to_string(), "hif".to_string()], + mime_types: vec!["image/heif".to_string(), "image/heic".to_string()], + icon: Some("image".to_string()), + }] + } + + async fn can_handle(&self, path: &PathBuf, mime_type: Option<&str>) -> PluginResult { + // Check file extension and/or MIME type + } +} +``` + +### MetadataExtractor + +```rust +#[async_trait] +impl MetadataExtractor for HeifPlugin { + async fn extract_metadata(&self, path: &PathBuf) -> PluginResult { + // 1. Parse HEIF file structure + // 2. Extract EXIF metadata + // 3. Get image dimensions + // 4. Return ExtractedMetadata + } + + fn supported_types(&self) -> Vec { + vec!["heif".to_string()] + } +} +``` + +### ThumbnailGenerator + +```rust +#[async_trait] +impl ThumbnailGenerator for HeifPlugin { + async fn generate_thumbnail( + &self, + path: &PathBuf, + output_path: &PathBuf, + options: ThumbnailOptions, + ) -> PluginResult { + // 1. Decode HEIF image + // 2. Resize to thumbnail dimensions + // 3. Encode to output format + // 4. Save to output_path + // 5. Return ThumbnailInfo + } + + fn supported_types(&self) -> Vec { + vec!["heif".to_string()] + } +} +``` + +## Dependencies + +The plugin uses the following Rust crates (compiled to WASM): +- `libheif-rs`: HEIF decoding and encoding +- `image`: Image processing and thumbnail generation +- `kamadak-exif`: EXIF metadata parsing + +## Building + +### Prerequisites + +```bash +# Install WASM target +rustup target add wasm32-wasi + +# Install wasm-tools for optimization (provides strip functionality) +cargo install wasm-tools +``` + +### Build Process + +```bash +# Build the plugin +cargo build --target wasm32-wasi --release + +# Strip debug symbols to reduce size +wasm-tools strip target/wasm32-wasi/release/heif_support.wasm -o target/wasm32-wasi/release/heif_support.wasm + +# Copy to plugin directory +cp target/wasm32-wasi/release/heif_support.wasm . +``` + +### Size Optimization + +```bash +# Use wasm-opt for further optimization +wasm-opt -Oz heif_support.wasm -o heif_support_optimized.wasm +``` + +## Installation + +### Manual Installation + +```bash +# Copy plugin directory to Pinakes plugins directory +cp -r examples/plugins/heif-support ~/.config/pinakes/plugins/ +``` + +### Via API + +```bash +curl -X POST http://localhost:3000/api/v1/plugins/install \ + -H "Content-Type: application/json" \ + -d '{"source": "/path/to/heif-support"}' +``` + +### Via Plugin Manager + +```bash +pinakes plugin install /path/to/heif-support +``` + +## Configuration + +The plugin can be configured through the `config` section in `plugin.toml`: + +### EXIF Extraction +- `extract_exif`: Enable EXIF metadata extraction (default: true) + +### Thumbnail Generation +- `generate_thumbnails`: Enable thumbnail generation (default: true) +- `thumbnail_quality`: JPEG quality for thumbnails, 1-100 (default: 85) +- `thumbnail_format`: Output format - "jpeg", "png", or "webp" (default: "jpeg") + +### Resource Limits +- `max_memory_mb`: Maximum memory the plugin can use in megabytes (default: 256, set in `[capabilities]`) +- `max_width`: Maximum image width to process (default: 8192) +- `max_height`: Maximum image height to process (default: 8192) + +## Security + +### Capabilities + +- **Filesystem Read**: Only files being processed (via Pinakes) +- **Filesystem Write**: Thumbnail directory only +- **Network**: Disabled +- **Environment**: No access + +### Resource Limits + +- **Memory**: 256 MB maximum +- **CPU Time**: 30 seconds maximum per operation + +### Sandboxing + +The plugin runs in a WASM sandbox with: +- No access to host filesystem beyond granted paths +- No network access +- No arbitrary code execution +- Memory and CPU time limits enforced by runtime + +## Performance + +### Typical Performance + +- **Metadata Extraction**: ~50-100ms for typical HEIF files +- **Thumbnail Generation**: ~200-500ms depending on source image size +- **Memory Usage**: 50-150 MB typical, 256 MB maximum + +### Optimization Tips + +1. Keep source images below 8192x8192 for best performance +2. Use JPEG thumbnail format for smaller file sizes +3. Adjust thumbnail quality vs file size tradeoff with `thumbnail_quality` + +## Error Handling + +The plugin handles: +- **Corrupted Files**: Returns descriptive error +- **Unsupported Variants**: Gracefully skips unsupported HEIF features +- **Memory Limits**: Fails safely if image too large +- **Timeout**: Returns error if processing exceeds CPU time limit + +## Testing + +```bash +# Run unit tests +cargo test + +# Test with sample HEIF files +cargo test --test integration -- --nocapture +``` + +## Troubleshooting + +### Plugin Fails to Load + +- Check that `heif_support.wasm` exists in plugin directory +- Verify `plugin.toml` is valid TOML +- Check Pinakes logs for detailed error messages + +### Thumbnails Not Generated + +- Verify `generate_thumbnails = true` in config +- Check filesystem write permissions for thumbnail directory +- Ensure source image is below size limits + +### Out of Memory Errors + +- Reduce `max_width` and `max_height` in config +- Increase `max_memory_mb` if source images are large +- Check that source files aren't corrupted + +## Future Enhancements + +- Support for HEIF image sequences (burst photos) +- HDR metadata extraction +- Live Photo support +- AVIF format support (similar to HEIF) + +## License + +MIT diff --git a/examples/plugins/heif-support/plugin.toml b/examples/plugins/heif-support/plugin.toml index 036001a..b60fecf 100644 --- a/examples/plugins/heif-support/plugin.toml +++ b/examples/plugins/heif-support/plugin.toml @@ -23,3 +23,40 @@ write = ["/tmp/pinakes-output"] extract_exif = { type = "boolean", default = true, description = "Extract EXIF metadata from HEIF images" } generate_thumbnails = { type = "boolean", default = true, description = "Generate thumbnails for HEIF images" } thumbnail_quality = { type = "integer", default = 85, description = "JPEG quality for thumbnails (1-100)" } +[plugin] +name = "heif-support" +version = "1.0.0" +api_version = "1.0" +author = "Pinakes Contributors" +description = "HEIF/HEIC image format support for Pinakes" +homepage = "https://github.com/notashelf/pinakes" +license = "MIT" +kind = ["media_type", "metadata_extractor", "thumbnail_generator"] + +[plugin.binary] +wasm = "heif_support.wasm" + +[capabilities] +network = false +max_memory_mb = 256 +max_cpu_time_secs = 30 + +[capabilities.filesystem] +# Read access for processing images (use specific paths in production) +read = ["/media"] +# Write access for thumbnail generation +write = ["/tmp/pinakes"] + +# Plugin configuration +[config] +# Enable EXIF metadata extraction +extract_exif = true +# Enable thumbnail generation +generate_thumbnails = true +# Thumbnail quality (1-100) +thumbnail_quality = 85 +# Thumbnail format (jpeg, png, webp) +thumbnail_format = "jpeg" +# Maximum image dimensions to process +max_width = 8192 +max_height = 8192 diff --git a/examples/plugins/markdown-metadata/README.md b/examples/plugins/markdown-metadata/README.md new file mode 100644 index 0000000..91f2f4c --- /dev/null +++ b/examples/plugins/markdown-metadata/README.md @@ -0,0 +1,103 @@ +# Markdown Metadata Extractor Plugin + +This example plugin demonstrates how to create a metadata extractor plugin for Pinakes. + +## Overview + +The Markdown Metadata Extractor enhances Pinakes' built-in markdown support by: +- Parsing YAML and TOML frontmatter +- Extracting metadata from frontmatter fields +- Converting frontmatter tags to Pinakes media tags +- Extracting custom fields from frontmatter + +## Features + +- **Frontmatter Parsing**: Supports both YAML (`---`) and TOML (`+++`) frontmatter formats +- **Tag Extraction**: Automatically extracts tags from frontmatter and applies them to media items +- **Custom Fields**: Preserves all frontmatter fields as custom metadata +- **Configuration**: Configurable via `plugin.toml` config section + +## Frontmatter Example + +```markdown +--- +title: "My Document" +author: "John Doe" +date: "2024-01-15" +tags: ["documentation", "example", "markdown"] +category: "tutorials" +draft: false +--- + +# My Document + +Content goes here... +``` + +## Implementation + +The plugin implements the `MetadataExtractor` trait from `pinakes-plugin-api`: + +```rust +#[async_trait] +impl MetadataExtractor for MarkdownMetadataPlugin { + async fn extract_metadata(&self, path: &PathBuf) -> PluginResult { + // 1. Read the file + // 2. Parse frontmatter + // 3. Extract metadata fields + // 4. Return ExtractedMetadata + } + + fn supported_types(&self) -> Vec { + vec!["markdown".to_string()] + } +} +``` + +## Building + +The plugin is compiled to WebAssembly: + +```bash +cargo build --target wasm32-wasi --release +wasm-strip target/wasm32-wasi/release/markdown_metadata.wasm +cp target/wasm32-wasi/release/markdown_metadata.wasm . +``` + +## Installation + +```bash +# Copy plugin directory to Pinakes plugins directory +cp -r examples/plugins/markdown-metadata ~/.config/pinakes/plugins/ + +# Or via API +curl -X POST http://localhost:3000/api/v1/plugins/install \ + -H "Content-Type: application/json" \ + -d '{"source": "/path/to/markdown-metadata"}' +``` + +## Configuration + +The plugin can be configured through the `config` section in `plugin.toml`: + +- `extract_tags`: Extract tags from frontmatter (default: true) +- `parse_yaml`: Parse YAML frontmatter (default: true) +- `parse_toml`: Parse TOML frontmatter (default: true) +- `max_file_size`: Maximum file size to process in bytes (default: 10MB) + +## Security + +This plugin has minimal capabilities: +- **Filesystem**: No write access, read access only to files being processed +- **Network**: Disabled +- **Environment**: No access + +## Testing + +```bash +cargo test +``` + +## License + +MIT diff --git a/examples/plugins/markdown-metadata/plugin.toml b/examples/plugins/markdown-metadata/plugin.toml index 578bb2b..acfc4e8 100644 --- a/examples/plugins/markdown-metadata/plugin.toml +++ b/examples/plugins/markdown-metadata/plugin.toml @@ -19,3 +19,33 @@ wasm = "markdown_metadata.wasm" extract_tags = { type = "boolean", default = true, description = "Extract tags from YAML frontmatter" } parse_yaml = { type = "boolean", default = true, description = "Parse YAML frontmatter" } max_file_size = { type = "integer", default = 10485760, description = "Maximum file size in bytes (10MB)" } +[plugin] +name = "markdown-metadata" +version = "1.0.0" +api_version = "1.0" +author = "Pinakes Contributors" +description = "Enhanced Markdown metadata extractor with frontmatter parsing" +homepage = "https://github.com/notashelf/pinakes" +license = "MIT" +kind = ["metadata_extractor"] + +[plugin.binary] +wasm = "markdown_metadata.wasm" + +[capabilities] +network = false + +[capabilities.filesystem] +read = [] +write = [] + +# Plugin configuration +[config] +# Extract frontmatter tags as media tags +extract_tags = true +# Parse YAML frontmatter +parse_yaml = true +# Parse TOML frontmatter +parse_toml = true +# Maximum file size to process (in bytes) +max_file_size = 10485760 # 10MB