Files
immich-folio/lib/structuredData.ts
Ralksta a813433891 release: v0.13.0 (#535)
Twenty-nine commits since v0.12.0. See CHANGELOG.md for the full list
and the upgrade notes.

Ten additions — the SEO package (site URL, robots.txt, sitemap.xml,
JSON-LD), slideshow mode, opt-in original downloads, copy-link,
previous/next album navigation, alt text from the Immich description,
per-album location precision, the update notice, a Help tab, and a
subpage cover grid separate from its photo grid. Two fixes: CJK album
slugs, and the lightbox control bar.

Photo of the Day (#476) was built on `dev` and reverted as not planned;
it never appears in a release.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01DmsHVRL9yQvckUB1ZpVWre

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: ralksta <ralksta@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-08-25 10:08:12 +02:00

61 lines
2.0 KiB
TypeScript

/**
* The JSON-LD payload for an album page (#472).
*
* `ImageGallery` with an `ImageObject` for the cover is the annotation that
* pays off for a photography portfolio: it is what a search result can show,
* and it is where `creator` and `license` belong.
*
* Built as a pure function so the shape can be asserted without rendering, and
* so the "no site URL, no structured data" rule is stated once.
*/
export interface AlbumStructuredDataInput {
siteUrl: string | null;
/** Absolute URL of the album page. */
pageUrl: string | null;
albumName: string;
description?: string;
/** Absolute URL of the cover image, if there is one. */
coverUrl?: string | null;
/** The photographer, for `creator`. */
creator?: string;
/** A licence URL or short identifier, e.g. a Creative Commons link. */
license?: string;
photoCount?: number;
}
export function albumStructuredData(
input: AlbumStructuredDataInput,
): Record<string, unknown> | null {
// Structured data is a set of claims about URLs. Without an absolute site URL
// there is nothing truthful to claim, so nothing is emitted.
if (!input.siteUrl || !input.pageUrl) return null;
if (!input.albumName.trim()) return null;
const creator = input.creator?.trim()
? { '@type': 'Person', name: input.creator.trim() }
: undefined;
const license = input.license?.trim() || undefined;
return {
'@context': 'https://schema.org',
'@type': 'ImageGallery',
name: input.albumName.trim(),
url: input.pageUrl,
...(input.description?.trim() ? { description: input.description.trim() } : {}),
...(typeof input.photoCount === 'number' ? { numberOfItems: input.photoCount } : {}),
...(creator ? { creator } : {}),
...(license ? { license } : {}),
...(input.coverUrl
? {
image: {
'@type': 'ImageObject',
contentUrl: input.coverUrl,
...(creator ? { creator } : {}),
...(license ? { license } : {}),
},
}
: {}),
};
}