Photostax 0.2.2
See the version list below for details.
dotnet add package Photostax --version 0.2.2
NuGet\Install-Package Photostax -Version 0.2.2
<PackageReference Include="Photostax" Version="0.2.2" />
<PackageVersion Include="Photostax" Version="0.2.2" />
<PackageReference Include="Photostax" />
paket add Photostax --version 0.2.2
#r "nuget: Photostax, 0.2.2"
#:package Photostax@0.2.2
#addin nuget:?package=Photostax&version=0.2.2
#tool nuget:?package=Photostax&version=0.2.2
Photostax .NET Binding
A .NET wrapper for the photostax library — access Epson FastFoto photo repositories from C#.
Overview
This package provides idiomatic C# access to Epson FastFoto photo repositories. It groups scanner output files (original, enhanced, back scans) into PhotoStack objects and provides metadata reading and writing.
Architecture
PhotostaxRepository is a managed wrapper around the Rust StackManager — the unified cache and query engine that powers photostax. Each PhotostaxRepository instance creates a StackManager internally with a single repository, giving you:
- O(1) stack lookups by opaque ID
- Unified
Query()API for search + pagination in one call - Lazy metadata loading — scan first, load EXIF/XMP on demand
- Filesystem watching and reactive cache updates (Rust-only for now)
Multi-repo support: The Rust core supports managing multiple repositories through a single
StackManager(viaadd_repo()). This capability is now also available in the .NET binding via theStackManagerclass.
Multi-repo with StackManager
using var mgr = new StackManager();
mgr.AddRepo("/photos/2024", recursive: true);
mgr.AddRepo("/photos/2023", recursive: true);
mgr.Scan();
Console.WriteLine($"Managing {mgr.RepoCount} repos");
// Query across all repos
var page = mgr.Query(new SearchQuery().WithText("birthday"), offset: 0, limit: 20);
foreach (var stack in page.Items)
Console.WriteLine($"{stack.Name} ({stack.Id})");
Installation
dotnet add package Photostax
Quick Start
using Photostax;
// Open a repository
using var repo = new PhotostaxRepository("/path/to/photos");
// Query all stacks (no filter, no pagination)
var all = repo.Query();
foreach (var stack in all.Items)
{
// stack.Id is a 16-char hex hash (e.g. "a1b2c3d4e5f67890")
// stack.Name is the human-readable display name
Console.WriteLine($"{stack.Name} ({stack.Id})");
Console.WriteLine($" Folder: {stack.Folder ?? "(root)"}");
Console.WriteLine($" Original: {stack.OriginalPath}");
Console.WriteLine($" Enhanced: {stack.EnhancedPath}");
Console.WriteLine($" Back: {stack.BackPath}");
Console.WriteLine($" Format: {stack.Format}");
}
// Search with filter and pagination using Query()
var page = repo.Query(
new SearchQuery().WithText("birthday").WithHasBack(true),
offset: 0,
limit: 20
);
Console.WriteLine($"Page has {page.Items.Count} of {page.TotalCount} total");
Console.WriteLine($"Has more: {page.HasMore}");
// Iterate pages
foreach (var stack in page.Items)
Console.WriteLine($"{stack.Name} ({stack.Id})");
if (page.HasMore)
{
var nextPage = repo.Query(
new SearchQuery().WithText("birthday"),
offset: 20, limit: 20);
}
// Read image bytes
var imageData = repo.ReadImage(all.Items[0].OriginalPath!);
// Write metadata (use stack.Id for lookups)
var metadata = new Metadata().WithCustomTag("album", "Family Photos");
repo.WriteMetadata(all.Items[0].Id, metadata);
Note — Stack IDs changed in v0.2.x: Stack IDs are now opaque 16-character hex strings (truncated SHA-256 hashes) rather than human-readable stems. Use
stack.Namewhen displaying a stack to users andstack.Idfor programmatic lookups such asGetStack()orWriteMetadata().
API Overview
PhotostaxRepository
The main entry point for working with photo repositories.
| Method | Description |
|---|---|
Query() |
(v0.2.x) Search and paginate in one call. Accepts an optional SearchQuery, offset, and limit. Returns a PagedResult<PhotoStack>. |
Scan() |
Discover all photo stacks in the repository |
GetStack(id) |
Get a specific stack by its opaque hash ID |
ReadImage(path) |
Read raw image bytes |
WriteMetadata(id, metadata) |
Write metadata to a stack |
Search(query) |
Find stacks matching a query (convenience wrapper around Query()) |
ScanPaginated(offset, limit) |
Scan with pagination (convenience wrapper around Query()) |
SearchPaginated(query, offset, limit) |
Search with pagination (convenience wrapper around Query()) |
Query() — Preferred Search & Pagination API
Query() is the recommended way to search and paginate as of v0.2.x.
The older Search(), ScanPaginated(), and SearchPaginated() methods still
work but are now convenience wrappers around Query().
// All stacks (no filter, no pagination)
var all = repo.Query();
// With filter and pagination
var page = repo.Query(
new SearchQuery().WithText("birthday").WithHasBack(true),
offset: 0,
limit: 20
);
// Iterate pages
foreach (var stack in page.Items)
Console.WriteLine($"{stack.Name} ({stack.Id})");
if (page.HasMore)
{
var nextPage = repo.Query(
new SearchQuery().WithText("birthday"),
offset: 20, limit: 20);
}
PhotoStack
Represents a photo stack with its associated images and metadata.
| Property | Type | Description |
|---|---|---|
Id |
string |
Opaque 16-char hex hash (SHA-256). Use for lookups. |
Name |
string |
(v0.2.x) Human-readable display name for the stack. |
Folder |
string? |
(v0.2.x) Subfolder within the repository, or null if at root. |
OriginalPath |
string? |
Path to original scan |
EnhancedPath |
string? |
Path to enhanced scan |
BackPath |
string? |
Path to back scan |
Metadata |
Metadata |
EXIF, XMP, and custom tags |
Format |
ImageFormat? |
JPEG, TIFF, or Unknown |
SearchQuery
Builder for constructing search queries.
var query = new SearchQuery()
.WithText("vacation") // Free-text search
.WithExifFilter("Make", "EPSON") // EXIF tag filter
.WithCustomFilter("album", "2020") // Custom tag filter
.WithHasBack(true) // Has back scan
.WithHasEnhanced(true); // Has enhanced scan
Building from Source
Prerequisites
- .NET SDK 8.0+
- Rust toolchain (for building native library)
Build Steps
# 1. Build native library
cd <repo_root>
cargo build --release -p photostax-ffi
# 2. Build .NET library
cd bindings/dotnet
dotnet build
# 3. Run tests
dotnet test
Native Library Location
The native library must be in your application's runtime directory:
| Platform | File |
|---|---|
| Windows | photostax_ffi.dll |
| macOS | libphotostax_ffi.dylib |
| Linux | libphotostax_ffi.so |
Running Tests
cd bindings/dotnet
dotnet test
# Skip integration tests (no native library required)
dotnet test --filter "Category!=Integration"
License
Licensed under either of Apache License, Version 2.0 or MIT License at your option.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.6.1 | 100 | 3/23/2026 |
| 0.5.0 | 88 | 3/22/2026 |
| 0.4.3 | 94 | 3/21/2026 |
| 0.4.2 | 87 | 3/21/2026 |
| 0.4.1 | 84 | 3/21/2026 |
| 0.4.0 | 94 | 3/20/2026 |
| 0.3.0 | 88 | 3/20/2026 |
| 0.2.2 | 87 | 3/20/2026 |
| 0.2.1 | 86 | 3/19/2026 |
| 0.2.0 | 90 | 3/19/2026 |
| 0.1.13 | 90 | 3/17/2026 |
| 0.1.12 | 88 | 3/17/2026 |
| 0.1.11 | 114 | 3/17/2026 |
| 0.1.10 | 148 | 3/16/2026 |
| 0.1.9 | 136 | 3/16/2026 |
| 0.1.8 | 136 | 3/16/2026 |
| 0.1.7 | 147 | 3/14/2026 |
| 0.1.6 | 212 | 3/14/2026 |
| 0.1.5 | 160 | 3/8/2026 |
| 0.1.4 | 102 | 3/8/2026 |