publishing

package
v0.2.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 16, 2026 License: MIT Imports: 29 Imported by: 0

Documentation

Overview

Package publishing owns Album import, curation, access and publication.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func RegisterAccessRoutes

func RegisterAccessRoutes(e *echo.Echo, module AccessUseCases, requireCurator echo.MiddlewareFunc)

RegisterAccessRoutes keeps every access mutation behind the Curator guard.

func RegisterPublicationRoutes

func RegisterPublicationRoutes(e *echo.Echo, module PublicationUseCases, requireCurator echo.MiddlewareFunc)

func RegisterRoutes

func RegisterRoutes(e *echo.Echo, module *Module, requireCurator echo.MiddlewareFunc)

func RegisterViewerRoutes

func RegisterViewerRoutes(e *echo.Echo, module ViewerUseCases, requirePerson, requireCurator echo.MiddlewareFunc)

RegisterViewerRoutes keeps selected-Person preview behind the Curator guard.

func SourceAssetThumbnailURL

func SourceAssetThumbnailURL(sourceID string) string

SourceAssetThumbnailURL is the Curator-only preview of an asset that is not yet, or no longer, part of any Album.

Types

type AccessConflict

type AccessConflict struct {
	PersonID    string   `json:"person_id"`
	DisplayName string   `json:"display_name"`
	Source      Decision `json:"source"`
	Target      Decision `json:"target"`
}

type AccessPerson

type AccessPerson struct {
	PersonID          string   `json:"person_id"`
	DisplayName       string   `json:"display_name"`
	AvatarURL         string   `json:"avatar_url"`
	Decision          Decision `json:"decision"`
	Detected          bool     `json:"detected"`
	Suggested         bool     `json:"suggested"`
	SupportingEntries int      `json:"supporting_entries"`
	MomentsDetected   int      `json:"moments_detected"`
	Inherited         bool     `json:"inherited"`
	Effective         bool     `json:"effective"`
	AccessibleCount   int      `json:"accessible_count"`
	Exceptions        int      `json:"exceptions"`
	// Deactivated people appear at Album scope only while they still hold
	// rules here, so a Curator can see and remove frozen access.
	Deactivated bool `json:"deactivated"`
}

AccessPerson summarizes one Person at one scope. At Album scope Decision is the Album allow, Exceptions counts narrower rules, and MomentsDetected says how many Moments recognized them; at Moment scope Inherited reports an Album allow and Decision the Moment rule.

type AccessResolution

type AccessResolution struct {
	PersonID string   `json:"person_id" validate:"required,uuid"`
	Decision Decision `json:"decision" validate:"required,oneof=allow deny inherit"`
}

type AccessUseCases

AccessUseCases is the HTTP seam for complete Curator access operations.

type Album

type Album struct {
	ID          string `json:"id"`
	SourceID    string `json:"source_id"`
	Title       string `json:"title"`
	Description string `json:"description"`
	Published   bool   `json:"published"`
	Ready       bool   `json:"ready"`
	Status      string `json:"status"`
	Message     string `json:"message"`
	Processed   int    `json:"processed"`
	Total       int    `json:"total"`
	PhotoCount  int    `json:"photo_count"`
	VideoCount  int    `json:"video_count"`
	StartDate   string `json:"start_date"`
	EndDate     string `json:"end_date"`
	CoverURL    string `json:"cover_url"`
}

Album is the Curator's view of one import. Status is queued, processing, interrupted, failed, or complete. Ready is true for a complete, unpublished Album that at least one Person could see once published.

type AlbumAccessChoice

type AlbumAccessChoice struct {
	PersonID string `json:"person_id" validate:"required,uuid"`
	Allowed  bool   `json:"allowed"`
}

type AlbumAccessPreview

type AlbumAccessPreview struct {
	Changes []AudienceChange `json:"changes"`
}

type AlbumDetail

type AlbumDetail struct {
	Album    `tstype:",extends"`
	Moments  []Moment        `json:"moments"`
	Access   []AccessPerson  `json:"access"`
	Excluded []ExcludedEntry `json:"excluded"`
}

type AudienceChange

type AudienceChange struct {
	PersonID       string   `json:"person_id"`
	DisplayName    string   `json:"display_name"`
	GainedEntryIDs []string `json:"gained_entry_ids"`
	LostEntryIDs   []string `json:"lost_entry_ids"`
}

AudienceChange describes the Album Entries whose effective access changes.

type Chapter

type Chapter struct {
	Title string  `json:"title"`
	Start float64 `json:"start"`
	End   float64 `json:"end"`
}

Chapter is one read-only navigation segment, in seconds from the start.

type ChapterFailure

type ChapterFailure struct {
	AlbumID    string `json:"album_id"`
	AlbumTitle string `json:"album_title"`
	MomentID   string `json:"moment_id"`
	EntryID    string `json:"entry_id"`
	Title      string `json:"title"`
	Message    string `json:"message"`
}

ChapterFailure is one video whose chapter extraction failed, addressed through an Album and Moment so a Curator can open it and retry.

type ChapterService

type ChapterService interface {
	RequestChapters(ctx context.Context, tx bun.Tx, mediaItemID models.UUID, checksum string) error
	RetryChapters(ctx context.Context, mediaItemID string) error
}

ChapterService is Media's chapter extraction seam. Requests join the import transaction so a committed video always has its committed task.

type Decision

type Decision string

Decision is an explicit access choice. Missing decisions inherit.

const (
	DecisionAllow   Decision = "allow"
	DecisionDeny    Decision = "deny"
	DecisionInherit Decision = "inherit"
)

type DeleteAlbumRequest

type DeleteAlbumRequest struct {
	Title string `json:"title" validate:"required"`
}

func (DeleteAlbumRequest) ValidationMessage

func (DeleteAlbumRequest) ValidationMessage(field, _ string) string

type EnqueueImport

type EnqueueImport func(context.Context, bun.Tx, string) error

EnqueueImport participates in the caller's transaction. It must not execute work inline.

type Entry

type Entry struct {
	ID           string `json:"id"`
	MediaID      string `json:"media_id"`
	Filename     string `json:"filename"`
	Kind         string `json:"kind"`
	CapturedAt   string `json:"captured_at"`
	Available    bool   `json:"available"`
	ThumbnailURL string `json:"thumbnail_url"`
	// Decisions holds only this entry's explicit rules by Person ID.
	Decisions map[string]Decision `json:"decisions"`
	// Title is the global video title override, empty when the filename shows.
	Title string `json:"title"`
	// PlaybackURL streams an available video to the Curator; photos have none.
	PlaybackURL string `json:"playback_url"`
	// Chapter fields describe the Media Item's extraction: ChapterStatus is
	// pending, complete, or failed, and ChapterMessage explains a failure.
	Chapters       []Chapter `json:"chapters"`
	ChapterStatus  string    `json:"chapter_status"`
	ChapterMessage string    `json:"chapter_message"`
}

type EntryPageRequest

type EntryPageRequest struct {
	Cursor string
	From   string
	To     string
}

EntryPageRequest selects one page of a gallery. From and To are local capture days (YYYY-MM-DD) that bound the page, To exclusive, so a browser can load every part of a large Album at once; Cursor continues within those bounds.

type ExcludeEntriesRequest

type ExcludeEntriesRequest struct {
	EntryIDs    []string `json:"entry_ids" validate:"required,min=1,dive,uuid"`
	ReviewToken string   `json:"review_token"`
}

ExcludeEntriesRequest keeps selected media of one Moment out of the Album.

type ExcludedEntry

type ExcludedEntry struct {
	ID           string `json:"id"`
	Filename     string `json:"filename"`
	Kind         string `json:"kind"`
	CapturedAt   string `json:"captured_at"`
	ThumbnailURL string `json:"thumbnail_url"`
	Available    bool   `json:"available"`
	ExcludedAt   string `json:"excluded_at"`
}

ExcludedEntry is media the Curator keeps out of this Album while it stays in the Immich album. It keeps its Album Entry identity for Add back.

type FaceRecord

type FaceRecord struct {
	SourceID     string `json:"source_id"`
	SourceName   string `json:"source_name"`
	ThumbnailURL string `json:"thumbnail_url"`
	ImmichURL    string `json:"immich_url"`
	PersonID     string `json:"person_id"`
	PersonName   string `json:"person_name"`
	Ignored      bool   `json:"ignored"`
	Occurrences  int    `json:"occurrences"`
}

type ImportRequest

type ImportRequest struct {
	SourceID string `json:"source_id" validate:"required,max=1024"`
}

type IncludeEntryRequest

type IncludeEntryRequest struct {
	MomentID    string `json:"moment_id" validate:"required,max=64"`
	ReviewToken string `json:"review_token"`
}

IncludeEntryRequest returns excluded media to an existing Moment or to a new Moment for its capture day, keyed "new:YYYY-MM-DD".

type MergeMomentsRequest

type MergeMomentsRequest struct {
	TargetMomentID string             `json:"target_moment_id" validate:"required,uuid"`
	Title          string             `json:"title" mod:"trim" validate:"max=200"`
	CoverEntryID   string             `json:"cover_entry_id" validate:"required,uuid"`
	Resolutions    []AccessResolution `json:"resolutions" validate:"dive"`
	ReviewToken    string             `json:"review_token"`
}

func (MergeMomentsRequest) ValidationMessage

func (MergeMomentsRequest) ValidationMessage(field, rule string) string

type Module

type Module struct {

	// ImmichURL is the browser-reachable Immich origin used for "Open in
	// Immich" links. Empty hides those links.
	ImmichURL string
	// Chapters queues extraction for imported videos. Nil, as in tests that
	// never look at chapters, imports videos without probing them.
	Chapters ChapterService
	// contains filtered or unexported fields
}

func New

func New(db *bun.DB, source immich.Library, enqueue EnqueueImport) *Module

func (*Module) ApplySync

func (m *Module) ApplySync(ctx context.Context, albumID string, request SyncRequest) (AlbumDetail, error)

ApplySync recomputes the review inside one transaction and commits the approved changes together. Any difference from the reviewed diff, in Immich or in this Album, refuses the apply so nothing unreviewed is committed.

func (*Module) AuthorizeViewerEntry

func (m *Module) AuthorizeViewerEntry(ctx context.Context, actorID, previewPersonID, entryID string) error

AuthorizeViewerEntry is the Media boundary. Every uncached thumbnail request checks current membership and access before resolving an Immich variant.

func (*Module) ChapterFailures

func (m *Module) ChapterFailures(ctx context.Context) ([]ChapterFailure, error)

ChapterFailures lists every failed chapter extraction that a Curator can still reach through a complete Album. Chapter status belongs to Media; this only addresses each failure by its Album Entry.

func (*Module) CheckSync

func (m *Module) CheckSync(ctx context.Context, albumID string, request SyncRequest) (SyncReview, error)

CheckSync compares the Album with its Immich album and returns a temporary review. The opening check, the one without decisions, also refreshes cached faces; otherwise it changes nothing. Cancelling the review discards it and a later check recomputes the source state.

func (*Module) DeleteAlbum

func (m *Module) DeleteAlbum(ctx context.Context, albumID string, request DeleteAlbumRequest) error

DeleteAlbum permanently removes this Album's curation, never Immich media. Shared Media Items remain intact, including metadata owned by later features.

func (*Module) ExcludeEntries

func (m *Module) ExcludeEntries(ctx context.Context, albumID, momentID string, request ExcludeEntriesRequest) (AlbumDetail, error)

ExcludeEntries keeps the reviewed media out of the Album. A later check never offers it as new, and Add back returns it with its identity.

func (*Module) ExecuteImport

func (m *Module) ExecuteImport(ctx context.Context, id string, finalAttempt ...bool) (returnErr error)

ExecuteImport reads outside transactions, then commits a complete reviewed Album atomically. Repeating a completed import never overwrites Curator edits.

func (*Module) GetAlbum

func (m *Module) GetAlbum(ctx context.Context, id string) (AlbumDetail, error)

func (*Module) IgnoreSource added in v0.2.0

func (m *Module) IgnoreSource(ctx context.Context, sourceID string) error

IgnoreSource keeps an Immich album off the import list until it is restored. Ignoring an album twice is the same decision, so it is not an error.

func (*Module) IncludeEntry

func (m *Module) IncludeEntry(ctx context.Context, albumID, entryID string, request IncludeEntryRequest) (AlbumDetail, error)

IncludeEntry returns excluded media to the Album in the reviewed Moment. Media Immich reports as missing or trashed waits for a check first. A video that was kept out before it was ever probed gets its extraction now.

func (*Module) ListAlbums

func (m *Module) ListAlbums(ctx context.Context, search string) ([]Album, error)

func (*Module) ListSources

func (m *Module) ListSources(ctx context.Context, search string, page int, ignored bool) (SourcePage, error)

ListSources pages locally because Immich's album-list endpoint has no paging or partial-name search. Existing imports remain visible regardless of the gate and are never treated as ignored. Ignored selects the albums a Curator keeps off the import list instead of the ones offered for import.

func (*Module) MergeMoments

func (m *Module) MergeMoments(ctx context.Context, albumID, sourceMomentID string, request MergeMomentsRequest) (AlbumDetail, error)

func (*Module) MoveEntries

func (m *Module) MoveEntries(ctx context.Context, albumID, sourceMomentID string, request MoveEntriesRequest) (AlbumDetail, error)

func (*Module) PreviewAlbumAccess

func (m *Module) PreviewAlbumAccess(ctx context.Context, albumID string, request SaveAlbumAccessRequest) (AlbumAccessPreview, error)

func (*Module) PreviewExclude

func (m *Module) PreviewExclude(ctx context.Context, albumID, momentID string, request ExcludeEntriesRequest) (StructurePreview, error)

func (*Module) PreviewInclude

func (m *Module) PreviewInclude(ctx context.Context, albumID, entryID string, request IncludeEntryRequest) (StructurePreview, error)

func (*Module) PreviewMerge

func (m *Module) PreviewMerge(ctx context.Context, albumID, sourceMomentID string, request MergeMomentsRequest) (StructurePreview, error)

func (*Module) PreviewMove

func (m *Module) PreviewMove(ctx context.Context, albumID, sourceMomentID string, request MoveEntriesRequest) (StructurePreview, error)

func (*Module) PreviewRemoveAccess

func (m *Module) PreviewRemoveAccess(ctx context.Context, albumID string, request RemoveAccessPreviewRequest) (RemoveAccessPreview, error)

func (*Module) PreviewSplit

func (m *Module) PreviewSplit(ctx context.Context, albumID, sourceMomentID string, request SplitMomentRequest) (StructurePreview, error)

func (*Module) PublishAlbum

func (m *Module) PublishAlbum(ctx context.Context, albumID string, request PublishRequest) (AlbumDetail, error)

PublishAlbum confirms the reviewed audience and commits publication alone.

func (*Module) RefreshMomentFaces

func (m *Module) RefreshMomentFaces(ctx context.Context, albumID, momentID string) (AlbumDetail, error)

RefreshMomentFaces reads Immich before opening a transaction. Cached face associations are replaced only after every source read succeeds, so a temporary Immich failure cannot erase the last usable recommendations.

func (*Module) RemoveAccess

func (m *Module) RemoveAccess(ctx context.Context, albumID string, request RemoveAccessRequest) (AlbumDetail, error)

func (*Module) RestoreSource added in v0.2.0

func (m *Module) RestoreSource(ctx context.Context, sourceID string) error

RestoreSource offers an ignored Immich album for import again.

func (*Module) RetryChapters

func (m *Module) RetryChapters(ctx context.Context, albumID, entryID string) (AlbumDetail, error)

RetryChapters requeues extraction for one video after a failure. Playback and publication never wait for it.

func (*Module) RetryImport

func (m *Module) RetryImport(ctx context.Context, id string) (AlbumDetail, error)

func (*Module) ReviewPublication

func (m *Module) ReviewPublication(ctx context.Context, albumID string) (PublicationReview, error)

ReviewPublication reports blockers and the current ordinary audience. It does not publish, enqueue work, or record a notification baseline.

func (*Module) SaveAlbumAccess

func (m *Module) SaveAlbumAccess(ctx context.Context, albumID string, request SaveAlbumAccessRequest) (AlbumDetail, error)

SaveAlbumAccess replaces only the Album-wide allows named in the request. Unchecking removes the Album allow and leaves narrower decisions alone.

func (*Module) SaveCoverOrder

func (m *Module) SaveCoverOrder(ctx context.Context, albumID string, request SaveCoverOrderRequest) (AlbumDetail, error)

SaveCoverOrder replaces the Album's Cover Order. Every listed Moment must belong to the Album and appear once; Moments left out fall back to capture order. Positions are cleared first so the unique place constraint never trips while the order is rewritten.

func (*Module) SaveEntryRules

func (m *Module) SaveEntryRules(ctx context.Context, albumID, entryID string, request SaveRulesRequest) (AlbumDetail, error)

func (*Module) SaveMomentRules

func (m *Module) SaveMomentRules(ctx context.Context, albumID, momentID string, request SaveRulesRequest) (AlbumDetail, error)

func (*Module) SetMomentCover

func (m *Module) SetMomentCover(ctx context.Context, albumID, momentID string, request SetMomentCoverRequest) (AlbumDetail, error)

func (*Module) SplitMoment

func (m *Module) SplitMoment(ctx context.Context, albumID, sourceMomentID string, request SplitMomentRequest) (AlbumDetail, error)

func (*Module) StartImport

func (m *Module) StartImport(ctx context.Context, sourceID string) (AlbumDetail, error)

StartImport commits source identity and durable enqueueing together. Existing Albums can be reopened even when Immich is unavailable or its version is unsupported.

func (*Module) UnpublishAlbum

func (m *Module) UnpublishAlbum(ctx context.Context, albumID string) (AlbumDetail, error)

UnpublishAlbum retains all curation and history while hiding ordinary access.

func (*Module) UpdateAlbum

func (m *Module) UpdateAlbum(ctx context.Context, id string, request UpdateAlbumRequest) (AlbumDetail, error)

func (*Module) UpdateMoment

func (m *Module) UpdateMoment(ctx context.Context, albumID, momentID string, request UpdateMomentRequest) (AlbumDetail, error)

func (*Module) UpdateVideo

func (m *Module) UpdateVideo(ctx context.Context, albumID, entryID string, request UpdateVideoRequest) (AlbumDetail, error)

UpdateVideo sets or clears the Media Item's global video title. The title belongs to the Media Item, so every Album showing that video changes.

func (*Module) ViewAlbum

func (m *Module) ViewAlbum(ctx context.Context, actorID, previewPersonID, albumID string) (ViewerAlbum, error)

ViewAlbum uses one snapshot for identity, effective counts and configured cover.

func (*Module) ViewAlbums

func (m *Module) ViewAlbums(ctx context.Context, actorID string) ([]ViewerAlbum, error)

func (*Module) ViewEntries

func (m *Module) ViewEntries(ctx context.Context, actorID, previewPersonID, albumID, kind string, page EntryPageRequest) (ViewerPage, error)

ViewEntries returns separate cursor-paginated galleries without Curator metadata.

func (*Module) ViewLibrary

func (m *Module) ViewLibrary(ctx context.Context, actorID string) (ViewerLibrary, error)

ViewLibrary summarizes accessible media across Albums in newest-first order.

func (*Module) ViewLibraryEntries

func (m *Module) ViewLibraryEntries(ctx context.Context, actorID, kind string, page EntryPageRequest) (ViewerPage, error)

ViewLibraryEntries returns newest-first media with one accessible Entry per item.

func (*Module) ViewingGroups

func (m *Module) ViewingGroups(ctx context.Context, albumID string) (ViewingGroups, error)

ViewingGroups derives who will see which cover from the same saved rules the viewer uses. Publication state is ignored: the Curator is preparing the Album, and the groups describe the view after publication.

func (*Module) VisibleEntries

func (m *Module) VisibleEntries(ctx context.Context, db bun.IDB, personID string) ([]notifications.VisibleEntry, error)

VisibleEntries lists every Album Entry the Person can view as an ordinary viewer: published Albums and allowing Access Decisions only. A Curator's administrative bypass is deliberately excluded so announcement baselines never treat unpublished or otherwise viewer-ineligible content as announced.

type Moment

type Moment struct {
	ID           string `json:"id"`
	Title        string `json:"title"`
	Label        string `json:"label"`
	Date         string `json:"date"`
	EndDate      string `json:"end_date"`
	CoverEntryID string `json:"cover_entry_id"`
	// CoverPosition is the Moment's place in the Album's Cover Order, 0 when
	// it only competes in capture order.
	CoverPosition int          `json:"cover_position"`
	Entries       []Entry      `json:"entries"`
	Access        MomentAccess `json:"access"`
}

type MomentAccess

type MomentAccess struct {
	People      []AccessPerson `json:"people"`
	Faces       []FaceRecord   `json:"faces"`
	RefreshedAt *time.Time     `json:"refreshed_at"`
}

type MoveEntriesRequest

type MoveEntriesRequest struct {
	EntryIDs            []string `json:"entry_ids" validate:"required,min=1,dive,uuid"`
	DestinationMomentID string   `json:"destination_moment_id" validate:"required,uuid"`
	ReviewToken         string   `json:"review_token"`
}

Moves and splits pick covers themselves: a Moment that loses its cover, or a Moment created by a split, starts with its earliest item. Curators change covers afterwards from the Moment itself.

func (MoveEntriesRequest) ValidationMessage

func (MoveEntriesRequest) ValidationMessage(field, _ string) string

type PublicationAudience

type PublicationAudience struct {
	PersonID        string `json:"person_id"`
	DisplayName     string `json:"display_name"`
	AvatarURL       string `json:"avatar_url"`
	AccessibleCount int    `json:"accessible_count"`
}

type PublicationReview

type PublicationReview struct {
	Title       string                `json:"title"`
	PhotoCount  int                   `json:"photo_count"`
	VideoCount  int                   `json:"video_count"`
	MomentCount int                   `json:"moment_count"`
	Audience    []PublicationAudience `json:"audience"`
	Blockers    []string              `json:"blockers"`
	Warnings    []string              `json:"warnings"`
	ReviewToken string                `json:"review_token"`
}

PublicationReview lists what publication would expose and why it may wait. Warnings never block; blockers do.

type PublicationUseCases

type PublicationUseCases interface {
	ReviewPublication(context.Context, string) (PublicationReview, error)
	PublishAlbum(context.Context, string, PublishRequest) (AlbumDetail, error)
	UnpublishAlbum(context.Context, string) (AlbumDetail, error)
	DeleteAlbum(context.Context, string, DeleteAlbumRequest) error
}

PublicationUseCases is the HTTP seam for publication and permanent removal.

type PublishRequest

type PublishRequest struct {
	ReviewToken string `json:"review_token" validate:"required"`
}

func (PublishRequest) ValidationMessage

func (PublishRequest) ValidationMessage(field, _ string) string

type RemoveAccessPreview

type RemoveAccessPreview struct {
	PersonID    string           `json:"person_id"`
	DisplayName string           `json:"display_name"`
	Changes     []AudienceChange `json:"changes"`
	ReviewToken string           `json:"review_token"`
}

type RemoveAccessPreviewRequest

type RemoveAccessPreviewRequest struct {
	PersonID string `json:"person_id" validate:"required,uuid"`
}

type RemoveAccessRequest

type RemoveAccessRequest struct {
	PersonID    string `json:"person_id" validate:"required,uuid"`
	ReviewToken string `json:"review_token" validate:"required"`
}

type SaveAlbumAccessRequest

type SaveAlbumAccessRequest struct {
	People []AlbumAccessChoice `json:"people" validate:"required,dive"`
}

func (SaveAlbumAccessRequest) ValidationMessage

func (SaveAlbumAccessRequest) ValidationMessage(field, _ string) string

type SaveCoverOrderRequest

type SaveCoverOrderRequest struct {
	MomentIDs []string `json:"moment_ids" validate:"dive,uuid"`
}

SaveCoverOrderRequest replaces an Album's Cover Order with these Moments in this order. An empty list clears it.

func (SaveCoverOrderRequest) ValidationMessage

func (SaveCoverOrderRequest) ValidationMessage(field, rule string) string

type SaveRulesRequest

type SaveRulesRequest struct {
	Decisions []AccessResolution `json:"decisions" validate:"required,dive"`
}

func (SaveRulesRequest) ValidationMessage

func (SaveRulesRequest) ValidationMessage(field, _ string) string

type SetMomentCoverRequest

type SetMomentCoverRequest struct {
	EntryID string `json:"entry_id" validate:"required,uuid"`
}

func (SetMomentCoverRequest) ValidationMessage

func (SetMomentCoverRequest) ValidationMessage(field, _ string) string

type SourceAlbum

type SourceAlbum struct {
	ID          string `json:"id"`
	Title       string `json:"title"`
	Description string `json:"description"`
	Count       int    `json:"count"`
	StartDate   string `json:"start_date"`
	EndDate     string `json:"end_date"`
	CoverURL    string `json:"cover_url"`
	AlbumID     string `json:"album_id"`
}

SourceAlbum describes an Immich Album available for import.

type SourcePage

type SourcePage struct {
	Albums  []SourceAlbum `json:"albums"`
	Page    int           `json:"page"`
	Pages   int           `json:"pages"`
	Total   int           `json:"total"`
	Ignored int           `json:"ignored"`
}

SourcePage is one page of Immich albums, either those offered for import or those a Curator ignored. Ignored counts every ignored album Immich still has, whichever side of the list this page shows.

type SourceRequest added in v0.2.0

type SourceRequest struct {
	SourceID string `json:"source_id" validate:"required,max=1024"`
}

SourceRequest names one Immich album to ignore or restore.

type SplitMomentRequest

type SplitMomentRequest struct {
	EntryIDs    []string `json:"entry_ids" validate:"required,min=1,dive,uuid"`
	NewTitle    string   `json:"new_title" mod:"trim" validate:"max=200"`
	ReviewToken string   `json:"review_token"`
}

func (SplitMomentRequest) ValidationMessage

func (SplitMomentRequest) ValidationMessage(field, rule string) string

type StructurePreview

type StructurePreview struct {
	Ready         bool             `json:"ready"`
	ReviewToken   string           `json:"review_token"`
	RemovesMoment bool             `json:"removes_moment"`
	Changes       []AudienceChange `json:"changes"`
	Conflicts     []AccessConflict `json:"conflicts"`
}

type SyncAddition

type SyncAddition struct {
	SourceID          string         `json:"source_id"`
	Filename          string         `json:"filename"`
	Kind              string         `json:"kind"`
	CapturedAt        string         `json:"captured_at"`
	ThumbnailURL      string         `json:"thumbnail_url"`
	Returning         bool           `json:"returning"`
	SuggestedMomentID string         `json:"suggested_moment_id"`
	MomentID          string         `json:"moment_id"`
	Exclude           bool           `json:"exclude"`
	OtherAlbums       []SyncAlbumRef `json:"other_albums"`
}

SyncAddition is a source asset the Album does not currently show. Returning means a removed Album Entry keeps its identity and announcement history. Media the Curator excluded is not listed here; it lives in the Album's Excluded section.

type SyncAlbumRef

type SyncAlbumRef struct {
	ID    string `json:"id"`
	Title string `json:"title"`
}

type SyncChange

type SyncChange struct {
	EntryID       string         `json:"entry_id"`
	Filename      string         `json:"filename"`
	Kind          string         `json:"kind"`
	ThumbnailURL  string         `json:"thumbnail_url"`
	Excluded      bool           `json:"excluded"`
	Fields        []string       `json:"fields"`
	CapturedAt    string         `json:"captured_at"`
	NewCapturedAt string         `json:"new_captured_at"`
	Available     bool           `json:"available"`
	NewAvailable  bool           `json:"new_available"`
	OtherAlbums   []SyncAlbumRef `json:"other_albums"`
}

SyncChange is a Media Item whose source facts differ. Fields names what changed: checksum, capture_time, availability, filename, or details. Excluded media is refreshed too so Add back never carries stale facts.

type SyncCover

type SyncCover struct {
	MomentID string `json:"moment_id" validate:"required,max=64"`
	EntryID  string `json:"entry_id" validate:"required,max=1100"`
}

SyncCover replaces a Moment cover that the reviewed removals invalidate. EntryID is an Album Entry ID or, for media joining in the same review, the placeholder the review lists, so it is bounded like a source ID.

type SyncCoverChoice

type SyncCoverChoice struct {
	MomentID    string            `json:"moment_id"`
	MomentLabel string            `json:"moment_label"`
	EntryID     string            `json:"entry_id"`
	Options     []SyncCoverOption `json:"options"`
}

SyncCoverChoice is a surviving Moment whose configured cover is being removed. EntryID is the Curator's chosen replacement, empty until chosen.

type SyncCoverOption

type SyncCoverOption struct {
	EntryID      string `json:"entry_id"`
	Filename     string `json:"filename"`
	ThumbnailURL string `json:"thumbnail_url"`
}

type SyncDescription

type SyncDescription struct {
	Before string `json:"before"`
	After  string `json:"after"`
}

type SyncMomentOption

type SyncMomentOption struct {
	ID    string `json:"id"`
	Label string `json:"label"`
	New   bool   `json:"new"`
}

SyncMomentOption is a destination: an existing Moment or one the apply would create, keyed "new:YYYY-MM-DD".

type SyncPlacement

type SyncPlacement struct {
	SourceID string `json:"source_id" validate:"required,max=1024"`
	MomentID string `json:"moment_id" validate:"max=64"`
	Exclude  bool   `json:"exclude"`
}

SyncPlacement chooses where one source asset joins the Album. MomentID is an existing Moment or a proposed Moment key from the review. Exclude keeps the asset out of this Album while it remains in the Immich album.

type SyncRemoval

type SyncRemoval struct {
	EntryID      string `json:"entry_id"`
	Filename     string `json:"filename"`
	Kind         string `json:"kind"`
	CapturedAt   string `json:"captured_at"`
	ThumbnailURL string `json:"thumbnail_url"`
	MomentID     string `json:"moment_id"`
	MomentLabel  string `json:"moment_label"`
	Cover        bool   `json:"cover"`
	Excluded     bool   `json:"excluded"`
	Reason       string `json:"reason"`
}

SyncRemoval is an Album Entry whose asset is no longer shown by the Immich album. Reason is left_album, trashed, or deleted; a trashed asset returns as a returning addition if it is restored from the Immich trash. Excluded means the entry was in the Excluded section, which it leaves; Moment fields are empty for it.

type SyncRequest

type SyncRequest struct {
	Placements  []SyncPlacement `json:"placements" validate:"dive"`
	Covers      []SyncCover     `json:"covers" validate:"dive"`
	ReviewToken string          `json:"review_token"`
}

SyncRequest carries the Curator's review decisions. A check accepts it without a token; apply requires the token from the matching review.

type SyncReview

type SyncReview struct {
	Ready          bool               `json:"ready"`
	UpToDate       bool               `json:"up_to_date"`
	ReviewToken    string             `json:"review_token"`
	Description    *SyncDescription   `json:"description" tstype:"SyncDescription | null"`
	Additions      []SyncAddition     `json:"additions"`
	Removals       []SyncRemoval      `json:"removals"`
	Changes        []SyncChange       `json:"changes"`
	CoverChoices   []SyncCoverChoice  `json:"cover_choices"`
	RemovedMoments []SyncMomentOption `json:"removed_moments"`
	Moments        []SyncMomentOption `json:"moments"`
	Audience       []AudienceChange   `json:"audience"`
	Blockers       []string           `json:"blockers"`
	FacesRefreshed bool               `json:"faces_refreshed"`
	FacesMessage   string             `json:"faces_message"`
}

SyncReview is the temporary diff between the Album and its Immich album. Nothing about it is stored; leaving the review discards it.

type UpdateAlbumRequest

type UpdateAlbumRequest struct {
	Title string `json:"title" mod:"trim" validate:"required,max=200"`
}

type UpdateMomentRequest

type UpdateMomentRequest struct {
	Title string `json:"title" mod:"trim" validate:"max=200"`
}

func (UpdateMomentRequest) ValidationMessage

func (UpdateMomentRequest) ValidationMessage(field, rule string) string

type UpdateVideoRequest

type UpdateVideoRequest struct {
	Title string `json:"title" mod:"trim" validate:"max=200"`
}

UpdateVideoRequest sets or, when blank, clears a video's global title.

func (UpdateVideoRequest) ValidationMessage

func (UpdateVideoRequest) ValidationMessage(field, rule string) string

type ViewerAlbum

type ViewerAlbum struct {
	ID          string `json:"id"`
	Title       string `json:"title"`
	Description string `json:"description"`
	PhotoCount  int    `json:"photo_count"`
	VideoCount  int    `json:"video_count"`
	StartDate   string `json:"start_date"`
	EndDate     string `json:"end_date"`
	CoverURL    string `json:"cover_url"`
	// CoverPreviewURL is the same cover at Immich's larger variant for the
	// Album header; list cards keep the small thumbnail.
	CoverPreviewURL string      `json:"cover_preview_url"`
	Days            []ViewerDay `json:"days"`
}

ViewerAlbum contains only the selected Person's accessible presentation.

type ViewerDay

type ViewerDay struct {
	Date        string    `json:"date"`
	PhotoCount  int       `json:"photo_count"`
	VideoCount  int       `json:"video_count"`
	PhotoRatios []float64 `json:"photo_ratios" bun:"photo_ratios,array"`
	VideoRatios []float64 `json:"video_ratios" bun:"video_ratios,array"`
}

ViewerDay counts one local capture day. PhotoRatios and VideoRatios list each item's width-to-height ratio in gallery order, truncated to three decimals, so the browser can lay out every day before the media arrives.

type ViewerEntry

type ViewerEntry struct {
	ID           string `json:"id"`
	Kind         string `json:"kind"`
	Title        string `json:"title"`
	CapturedAt   string `json:"captured_at"`
	Available    bool   `json:"available"`
	ThumbnailURL string `json:"thumbnail_url"`
	PreviewURL   string `json:"preview_url"`
	// DownloadURL streams the original photo or video. It is empty in Curator
	// preview and for unavailable media.
	DownloadURL string `json:"download_url"`
	// PlaybackURL is the ranged video stream, also in preview. Photos have none.
	PlaybackURL string `json:"playback_url"`
	Width       int    `json:"width"`
	Height      int    `json:"height"`
	// Chapters are read-only navigation marks. ChapterStatus is pending,
	// complete, or failed; photos leave both empty.
	Chapters      []Chapter `json:"chapters"`
	ChapterStatus string    `json:"chapter_status"`
}

ViewerEntry is one gallery item. Title is the Curator's video title when set, otherwise the original filename without its extension.

type ViewerLibrary

type ViewerLibrary struct {
	PhotoCount int         `json:"photo_count"`
	VideoCount int         `json:"video_count"`
	Days       []ViewerDay `json:"days"`
}

ViewerLibrary counts each accessible media item once across Albums.

type ViewerPage

type ViewerPage struct {
	Entries    []ViewerEntry `json:"entries"`
	NextCursor string        `json:"next_cursor"`
}

type ViewerUseCases

type ViewerUseCases interface {
	ViewLibrary(ctx context.Context, actorID string) (ViewerLibrary, error)
	ViewLibraryEntries(ctx context.Context, actorID, kind string, page EntryPageRequest) (ViewerPage, error)
	ViewAlbum(ctx context.Context, actorID, previewPersonID, albumID string) (ViewerAlbum, error)
	ViewAlbums(ctx context.Context, actorID string) ([]ViewerAlbum, error)
	ViewEntries(ctx context.Context, actorID, previewPersonID, albumID, kind string, page EntryPageRequest) (ViewerPage, error)
}

ViewerUseCases keeps HTTP identity selection separate from viewer projections.

type ViewingGroup

type ViewingGroup struct {
	MomentIDs []string        `json:"moment_ids"`
	People    []ViewingPerson `json:"people"`
}

ViewingGroup is one set of People and the Moments whose covers all of them can see, in Album display order. Which of those covers they get follows from the Cover Order, so the frontend can preview an unsaved order.

type ViewingGroups

type ViewingGroups struct {
	Groups      []ViewingGroup  `json:"groups"`
	Placeholder []ViewingPerson `json:"placeholder"`
}

ViewingGroups previews who will see which Album cover. People who can see exactly the same Moment covers form one Viewing Group; they are derived on every read and never stored. Placeholder lists People with some access but no visible cover, who see the neutral placeholder instead.

type ViewingPerson

type ViewingPerson struct {
	PersonID    string `json:"person_id"`
	DisplayName string `json:"display_name"`
	AvatarURL   string `json:"avatar_url"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL