Files
afilmory/apps/docs/contents/builder/plugins.mdx
Innei d5a2ea4db2 feat: enhance documentation and add new features for Afilmory
- Introduced a comprehensive `DEVELOPMENT.md` guide for contributors and self-hosters, detailing workspace layout and common commands.
- Updated `README.md` to include links to the new development guide and improved deployment instructions.
- Added new documentation files covering architecture, builder pipeline, configuration, and deployment strategies.
- Implemented new storage provider documentation for Backblaze B2, Eagle, GitHub, and local storage options.
- Enhanced the UI components with new features, including a navigation context and improved theme handling.
- Removed outdated GitHub Action deployment documentation.

Signed-off-by: Innei <tukon479@gmail.com>
2025-11-23 19:40:51 +08:00

172 lines
5.7 KiB
Plaintext

---
title: Plugins
description: Lifecycle hooks, authoring a custom plugin, and built-in plugins.
createdAt: 2025-11-23T19:00:00+08:00
lastModified: 2025-11-23T19:40:52+08:00
order: 5
---
# Plugins
Plugins extend the builder without forking its core. They are plain objects with lifecycle hooks, optional `onInit`, and can be registered inline, as factories, or via async ESM importers.
## Lifecycle surface
Hook names map directly to `packages/builder/src/plugins/types.ts`:
- `onInit` — once per process, after config is resolved.
- `beforeBuild` / `afterBuild` — whole run.
- `afterManifestLoad` — existing manifest is available.
- `afterAllFilesListed` → `afterLivePhotoDetection` → `afterImagesListed` → `afterTasksPrepared` — storage discovery.
- `beforeProcessTasks` / `afterProcessTasks` — batch level.
- `beforePhotoProcess` / `afterPhotoProcess` / `photoProcessError` — per photo.
- `beforeAddManifestItem` — right before an item is pushed.
- `beforeSaveManifest` / `afterSaveManifest` — final write.
- `afterCleanup` — after orphaned thumbnail cleanup.
- `onError` — uncaught errors.
All hooks receive `{ builder, config, logger, options, pluginName, pluginOptions, runShared, payload }`.
## Writing your first plugin
Minimal inline plugin (skips GIFs):
```typescript
// builder.config.ts
import { defineBuilderConfig } from '@afilmory/builder'
const skipGifsPlugin = {
name: 'skip-gifs',
hooks: {
afterTasksPrepared: async ({ payload }) => {
payload.tasks = payload.tasks.filter((t) => !t.key.toLowerCase().endsWith('.gif'))
},
},
}
export default defineBuilderConfig(() => ({
storage: { provider: 'local', basePath: './photos', baseUrl: '/photos' },
plugins: [skipGifsPlugin],
}))
```
Factory with options:
```typescript
function watermarkPlugin(options: { text: string }) {
return {
name: 'watermark',
hooks: {
afterPhotoProcess: async ({ payload }) => {
if (!payload.result.item) return
payload.result.item.description += ` | ${options.text}`
},
},
}
}
export default defineBuilderConfig(() => ({
storage: { provider: 's3', bucket: process.env.S3_BUCKET_NAME! },
plugins: [watermarkPlugin({ text: '© Afilmory' })],
}))
```
Async importer (keeps bundles slim):
```typescript
export default defineBuilderConfig(() => ({
storage: { provider: 's3', bucket: process.env.S3_BUCKET_NAME! },
plugins: [() => import('./plugins/geo-tag-plugin.js')],
}))
```
## Patterns
- **Shared state**: use `runShared` (a `Map`) to cache data across hooks within one run.
- **Storage extensions**: `registerStorageProvider` lets a plugin add a custom storage provider before discovery.
- **Manifest mutations**: prefer `beforeAddManifestItem` or `afterPhotoProcess` for per-item edits; `beforeSaveManifest` for global edits.
- **I/O**: keep uploads/downloads async and respect `options` flags; avoid blocking the event loop in hooks.
## Built-in plugins
### GitHub repo sync (`githubRepoSyncPlugin`)
Purpose: cache thumbnails and `photos-manifest.json` in a Git repo to speed up CI/CD and enable sharing of built assets.
What it does
- `beforeBuild`: clones or pulls the repo into `assets-git`, ensures branch exists, creates `thumbnails/` and `photos-manifest.json` if missing, then symlinks them into `apps/web/public/thumbnails` and `apps/web/src/data/photos-manifest.json`.
- `afterBuild`: if `autoPush` and there are changes, commits and pushes to the configured branch (requires token).
Config example:
```typescript
import { defineBuilderConfig, githubRepoSyncPlugin } from '@afilmory/builder'
export default defineBuilderConfig(() => ({
storage: { provider: 's3', bucket: process.env.S3_BUCKET_NAME! },
plugins: [
githubRepoSyncPlugin({
enable: true,
autoPush: true,
repo: {
url: 'https://github.com/you/gallery-cache',
token: process.env.GIT_TOKEN,
branch: 'main',
},
}),
],
}))
```
Notes
- Requires Git in PATH.
- If token is absent, sync still reads but skips push.
- Safe to run repeatedly; it reuses `assets-git` and handles re-clone on pull failure.
### Thumbnail storage (`@afilmory/builder/plugins/thumbnail-storage`)
Purpose: upload generated thumbnails to your storage (default or a secondary storage config) and rewrite `thumbnailUrl` to point to the remote location.
What it does
- `beforeBuild`: resolves the target storage manager; can reuse default storage or a dedicated one you pass in `storageConfig`.
- `afterPhotoProcess`: uploads the thumbnail buffer once per run per file, caches remote URL, and mutates `payload.result.item.thumbnailUrl` to the remote URL.
Config example (reuse default storage):
```typescript
import thumbnailStoragePlugin from '@afilmory/builder/plugins/thumbnail-storage'
export default defineBuilderConfig(() => ({
storage: { provider: 's3', bucket: process.env.S3_BUCKET_NAME! },
plugins: [thumbnailStoragePlugin()],
}))
```
Config example (separate storage for thumbnails):
```typescript
import thumbnailStoragePlugin from '@afilmory/builder/plugins/thumbnail-storage'
export default defineBuilderConfig(() => ({
storage: { provider: 's3', bucket: process.env.S3_BUCKET_NAME! },
plugins: [
thumbnailStoragePlugin({
storageConfig: {
provider: 'b2',
bucketId: process.env.B2_BUCKET_ID!,
applicationKeyId: process.env.B2_KEY_ID!,
applicationKey: process.env.B2_KEY!,
},
prefix: 'thumbnails/',
contentType: 'image/jpeg',
}),
],
}))
```
Notes
- Deduplicates uploads per run via an in-memory set; safe for concurrent workers.
- If upload fails, it logs the error and leaves the local URL intact rather than failing the whole build.
Use these built-ins as references for structure, error handling, and hook usage when authoring your own plugins.