sidecar

package
v0.0.49 Latest Latest
Warning

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

Go to latest
Published: Jun 25, 2026 License: MIT Imports: 7 Imported by: 0

Documentation

Index

Constants

View Source
const CurrentVersion = 1

CurrentVersion is the current version of the sidecar file format. Increment this when making breaking changes to the schema.

View Source
const SidecarSuffix = ".metadata.json"

Variables

View Source
var ErrEmptySidecarPath = errors.New("sidecar: path is empty")

ErrEmptySidecarPath is returned by Write* helpers when the caller supplies an empty book or file path. An empty path would otherwise resolve to the current working directory via filepath.Clean("") → ".", which historically caused stray "..metadata.json" files to be written next to whatever process was running (including Go test binaries).

Functions

func BookSidecarExists

func BookSidecarExists(bookPath string) bool

BookSidecarExists checks if a book sidecar file exists.

func BookSidecarPath

func BookSidecarPath(bookPath string) string

BookSidecarPath returns the sidecar file path for a book. For directory-based books: {bookdir}/{dirname}.metadata.json. For root-level books: {dir}/{filename_without_ext}.metadata.json. Returns "" when bookPath is empty — an empty bookPath has no meaningful sidecar location and must not be coerced into a CWD-relative path.

func ChaptersToModels

func ChaptersToModels(chapters []ChapterMetadata) []*models.Chapter

ChaptersToModels converts ChapterMetadata slice to model chapters. Note: This creates chapter models without IDs - they should be inserted fresh.

func FileSidecarExists

func FileSidecarExists(filePath string) bool

FileSidecarExists checks if a file sidecar exists.

func FileSidecarPath

func FileSidecarPath(filePath string) string

FileSidecarPath returns the sidecar file path for a media file. Returns {filepath}.metadata.json, or "" when filePath is empty.

func WriteBookSidecar

func WriteBookSidecar(bookPath string, s *BookSidecar) error

WriteBookSidecar writes a book sidecar file. Note: The caller is responsible for ensuring the parent directory exists. For root-level files with OrganizeFileStructure enabled, the directory should be created before calling this function. Returns ErrEmptySidecarPath if bookPath is empty.

func WriteBookSidecarFromModel

func WriteBookSidecarFromModel(book *models.Book) error

WriteBookSidecarFromModel writes a book sidecar from a Book model.

For root-level books in libraries with OrganizeFileStructure disabled, the scanner writes a synthetic organized-folder path into book.Filepath that never exists on disk. Writing a sidecar to that path would fail, so if book.Filepath doesn't resolve to an existing directory we fall back to anchoring the sidecar next to a file in the book (preferring a main file). The cover always lives next to the file in that case, so co-locating the book sidecar matches how the rest of the system resolves paths.

func WriteFileSidecar

func WriteFileSidecar(filePath string, s *FileSidecar) error

WriteFileSidecar writes a file sidecar. Returns ErrEmptySidecarPath if filePath is empty.

func WriteFileSidecarFromModel

func WriteFileSidecarFromModel(file *models.File) error

WriteFileSidecarFromModel writes a file sidecar from a File model.

func WriteFileSidecarWithChapters

func WriteFileSidecarWithChapters(file *models.File, chapters []*models.Chapter) error

WriteFileSidecarWithChapters writes a file sidecar from a File model and chapters.

Types

type AuthorMetadata

type AuthorMetadata struct {
	Name      string  `json:"name"`
	SortName  string  `json:"sort_name,omitempty"`
	SortOrder int     `json:"sort_order,omitempty"`
	Role      *string `json:"role,omitempty"` // CBZ creator role: writer, penciller, inker, etc.
}

AuthorMetadata represents an author in the sidecar file.

type BookSidecar

type BookSidecar struct {
	Version     int              `json:"version"`
	Title       string           `json:"title,omitempty"`
	SortTitle   string           `json:"sort_title,omitempty"`
	Subtitle    *string          `json:"subtitle,omitempty"`
	Description *string          `json:"description,omitempty"`
	Authors     []AuthorMetadata `json:"authors,omitempty"`
	Series      []SeriesMetadata `json:"series,omitempty"`
	Genres      []string         `json:"genres,omitempty"`
	Tags        []string         `json:"tags,omitempty"`
}

BookSidecar represents the metadata sidecar for a book. This is stored as {bookname}.metadata.json in the book directory.

func BookSidecarFromModel

func BookSidecarFromModel(book *models.Book) *BookSidecar

BookSidecarFromModel creates a BookSidecar from a Book model.

func ReadBookSidecar

func ReadBookSidecar(bookPath string) (*BookSidecar, error)

ReadBookSidecar reads and parses a book sidecar file. Returns nil, nil if the sidecar doesn't exist or bookPath is empty.

func ReadBookSidecarFromModel added in v0.0.37

func ReadBookSidecarFromModel(book *models.Book, fileHint *models.File) (*BookSidecar, error)

ReadBookSidecarFromModel reads the book sidecar for a Book, using the same anchor-resolution logic as WriteBookSidecarFromModel. For root-level books with a synthetic book.Filepath, the sidecar is read from next to a file in the book instead of from the non-existent synthetic directory.

fileHint (may be nil) is consulted when the resolved anchor doesn't exist on disk — typically because book.Files was not loaded and book.Filepath is the synthetic pre-organize path. Pass the current file being scanned so resolution works before the book is reloaded with its files relation.

type ChapterMetadata

type ChapterMetadata struct {
	Title            string            `json:"title"`
	StartPage        *int              `json:"start_page,omitempty"`
	StartTimestampMs *int64            `json:"start_timestamp_ms,omitempty"`
	Href             *string           `json:"href,omitempty"`
	Children         []ChapterMetadata `json:"children,omitempty"`
}

ChapterMetadata represents a chapter in the sidecar file. Position fields are mutually exclusive based on file type: - CBZ uses StartPage (0-indexed). - M4B uses StartTimestampMs. - EPUB uses Href.

func ChaptersFromModels

func ChaptersFromModels(chapters []*models.Chapter) []ChapterMetadata

ChaptersFromModels converts model chapters to ChapterMetadata slice.

type FileSidecar

type FileSidecar struct {
	Version     int                  `json:"version"`
	Narrators   []NarratorMetadata   `json:"narrators,omitempty"`
	URL         *string              `json:"url,omitempty"`
	Publisher   *string              `json:"publisher,omitempty"`
	ReleaseDate *string              `json:"release_date,omitempty"` // ISO 8601 date string (YYYY-MM-DD)
	Identifiers []IdentifierMetadata `json:"identifiers,omitempty"`
	Name        *string              `json:"name,omitempty"`
	Chapters    []ChapterMetadata    `json:"chapters,omitempty"`
	CoverPage   *int                 `json:"cover_page,omitempty"` // 0-indexed page number for page-based formats (CBZ, PDF)
	Language    *string              `json:"language,omitempty"`
	Abridged    *bool                `json:"abridged,omitempty"`
}

FileSidecar represents the metadata sidecar for a media file. This is stored as {filename}.metadata.json alongside the media file.

func FileSidecarFromModel

func FileSidecarFromModel(file *models.File) *FileSidecar

FileSidecarFromModel creates a FileSidecar from a File model.

func ReadFileSidecar

func ReadFileSidecar(filePath string) (*FileSidecar, error)

ReadFileSidecar reads and parses a file sidecar. Returns nil, nil if the sidecar doesn't exist or filePath is empty.

type IdentifierMetadata

type IdentifierMetadata struct {
	Type  string `json:"type"` // isbn_10, isbn_13, asin, uuid, goodreads, google, other
	Value string `json:"value"`
}

IdentifierMetadata represents an identifier in the sidecar file.

type NarratorMetadata

type NarratorMetadata struct {
	Name      string `json:"name"`
	SortName  string `json:"sort_name,omitempty"`
	SortOrder int    `json:"sort_order,omitempty"`
}

NarratorMetadata represents a narrator in the sidecar file.

type SeriesMetadata

type SeriesMetadata struct {
	Name      string   `json:"name"`
	SortName  string   `json:"sort_name,omitempty"`
	Number    *float64 `json:"number,omitempty"`
	Unit      *string  `json:"unit,omitempty"` // models.SeriesNumberUnitVolume or models.SeriesNumberUnitChapter; CBZ only
	SortOrder int      `json:"sort_order,omitempty"`
}

SeriesMetadata represents series information in the sidecar file.

Jump to

Keyboard shortcuts

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