Photostax 0.6.1

dotnet add package Photostax --version 0.6.1
                    
NuGet\Install-Package Photostax -Version 0.6.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Photostax" Version="0.6.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Photostax" Version="0.6.1" />
                    
Directory.Packages.props
<PackageReference Include="Photostax" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Photostax --version 0.6.1
                    
#r "nuget: Photostax, 0.6.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Photostax@0.6.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Photostax&version=0.6.1
                    
Install as a Cake Addin
#tool nuget:?package=Photostax&version=0.6.1
                    
Install as a Cake Tool

Photostax .NET Binding

A .NET wrapper for the photostax library — access Epson FastFoto photo repositories from C#.

NuGet License

Overview

This package provides idiomatic C# access to Epson FastFoto photo repositories. It groups scanner output files (original, enhanced, back scans) into PhotoStack objects with lazy, cached accessors for image data and metadata.

Architecture

PhotostaxRepository is a managed wrapper around the Rust SessionManager (formerly StackManager) — the unified cache and query engine that powers photostax. Each PhotostaxRepository instance creates a SessionManager internally with a single repository, giving you:

  • O(1) stack lookups by opaque ID
  • PhotoStack-centric I/Ostack.Original.Read(), stack.Metadata.Read(), etc.
  • Lazy, cached accessors — image data and metadata loaded on demand
  • ScanSnapshot queriesQuery() returns a QueryResult for page-based navigation

Multi-repo support: Use the StackManager class to manage multiple repositories through a single cache.

Multi-repo with StackManager

using var mgr = new StackManager();
mgr.AddRepo("/photos/2024", recursive: true);
mgr.AddRepo("/photos/2023", recursive: true);
Console.WriteLine($"Managing {mgr.RepoCount} repos");

// Query across all repos — returns a QueryResult (auto-scans on first call)
var result = mgr.Query(new SearchQuery().WithText("birthday"), pageSize: 20);
foreach (var stack in result.CurrentPage)
{
    Console.WriteLine($"{stack.Name} ({stack.Id})");

    // Per-stack image I/O (no manager methods needed)
    if (stack.Original.IsPresent)
    {
        using var stream = stack.Original.Read();
        // ... process image ...
    }
}

Custom Repository Providers

Register a custom backend (e.g., cloud storage) by implementing IRepositoryProvider:

using Photostax;

public class OneDriveProvider : IRepositoryProvider
{
    public string Location => "onedrive://user/photos";

    public IReadOnlyList<FileEntry> ListEntries(string prefix, bool recursive)
    {
        return new List<FileEntry>
        {
            new("IMG_001.jpg", "vacation", "onedrive://user/photos/vacation/IMG_001.jpg", 2048576),
            new("IMG_001_a.jpg", "vacation", "onedrive://user/photos/vacation/IMG_001_a.jpg", 2148576),
        };
    }

    public Stream OpenRead(string path) => DownloadFromOneDrive(path);
    public Stream OpenWrite(string path) => CreateUploadStream(path);
}

using var mgr = new StackManager();
mgr.AddRepo(new OneDriveProvider(), recursive: true);
// Query() auto-scans on first call

The host providesI/O primitives while Rust handles all scanning, file grouping, naming convention parsing, and metadata operations.

Installation

dotnet add package Photostax

Quick Start

using Photostax;

// Open a repository
using var repo = new PhotostaxRepository("/path/to/photos");

// Query all stacks — Query() is the sole entry point for retrieving stacks
var result = repo.Query(pageSize: 20);

// Iterate the first page
foreach (var stack in result.CurrentPage)
{
    Console.WriteLine($"{stack.Name}: {stack.Original.IsPresent}");
    Console.WriteLine($"  Folder: {stack.Folder ?? "(root)"}");

    // Read images via ImageRef (lazy, cached)
    if (stack.Original.IsPresent)
    {
        using var stream = stack.Original.Read();
        Console.WriteLine($"  Original: {stack.Original.Size} bytes");
        Console.WriteLine($"  Hash: {stack.Original.Hash()}");
    }

    // Read metadata via MetadataRef (lazy-loaded)
    var meta = stack.Metadata.Read();
    Console.WriteLine($"  Camera: {meta.ExifTags.GetValueOrDefault("Make", "unknown")}");
}

// Navigate remaining pages
while (result.NextPage() is { } page)
{
    foreach (var stack in page)
    {
        Console.WriteLine($"{stack.Name} ({stack.Id})");
    }
}

// Filtered query — pass a SearchQuery to filter instead of scan
var filtered = repo.Query(
    new SearchQuery().WithText("birthday").WithHasBack(true),
    pageSize: 20
);
Console.WriteLine($"Page has {filtered.CurrentPage.Count} of {filtered.TotalCount} total");

API Overview

PhotostaxRepository

The main entry point for working with photo repositories.

Method Description
Query(query?, pageSize, onProgress?) Returns a QueryResult for page-based navigation. Auto-scans on first call.

StackManager

Multi-repository manager for unified access across directories and custom backends.

Method Description
new StackManager() Create an empty manager
AddRepo(path, ...) Register a local directory
AddRepo(IRepositoryProvider, ...) Register a custom repository provider
RepoCount Number of registered repositories
Query(query?, pageSize, onProgress?) Search across all repos, returns QueryResult. Auto-scans on first call.

PhotoStack (v0.4.0)

All I/O operations are accessed directly on the stack object:

Property Type Description
Id string Opaque 16-char hex hash (SHA-256). Use for lookups.
Name string Human-readable display name for the stack.
Folder string? Subfolder within the repository, or null if at root.
Original ImageRef Original front scan accessor — Read(), Hash(), Dimensions(), Rotate()
Enhanced ImageRef Enhanced scan accessor
Back ImageRef Back scan accessor
Metadata MetadataRef Metadata accessor — Read(), Write()

ImageRef

Lazy, cached accessor for a single image variant:

Method Description
IsPresent Whether this image variant exists
Read() Read image bytes as a Stream
Hash() SHA-256 content hash (cached after first call)
Dimensions() Image width and height (cached)
Size File size in bytes
Rotate(degrees) Rotate image in place

MetadataRef

Lazy accessor for stack metadata:

Method Description
Read() Load EXIF, XMP, and custom tags (lazy-loaded)
Write(metadata) Write metadata back

IRepositoryProvider

Interface for custom repository backends:

public interface IRepositoryProvider
{
    string Location { get; }
    IReadOnlyList<FileEntry> ListEntries(string prefix, bool recursive);
    Stream OpenRead(string path);
    Stream OpenWrite(string path);
}

public record FileEntry(string Name, string Folder, string Path, long Size);

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
    .WithRepoId("a1b2c3d4");       // Filter by repository (v0.4.0)

QueryResult

Page-based query result with navigation:

Property/Method Type Description
CurrentPage IReadOnlyList<PhotoStack> Stacks on the current page
TotalCount int Total matching stacks across all pages
PageCount int Total number of pages
CurrentPageIndex int Zero-based index of current page
PageSize int Items per page
NextPage() IReadOnlyList<PhotoStack>? Advance to next page, returns it (or null)
PreviousPage() IReadOnlyList<PhotoStack>? Go to previous page (or null)
SetPage(index) IReadOnlyList<PhotoStack>? Jump to specific page (or null)
Query(query) QueryResult Sub-query on current result set

Building from Source

Prerequisites

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.


← Back to main README | FFI Documentation

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • 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 92 3/23/2026
0.5.0 82 3/22/2026
0.4.3 87 3/21/2026
0.4.2 82 3/21/2026
0.4.1 79 3/21/2026
0.4.0 84 3/20/2026
0.3.0 84 3/20/2026
0.2.2 82 3/20/2026
0.2.1 81 3/19/2026
0.2.0 86 3/19/2026
0.1.13 86 3/17/2026
0.1.12 83 3/17/2026
0.1.11 109 3/17/2026
0.1.10 143 3/16/2026
0.1.9 131 3/16/2026
0.1.8 131 3/16/2026
0.1.7 142 3/14/2026
0.1.6 190 3/14/2026
0.1.5 157 3/8/2026
0.1.4 100 3/8/2026
Loading failed