feat(metadata): surface calibre custom columns from EPUB metadata (#4939)

Parse calibre's embedded user metadata (custom columns) from the OPF
in foliate-js, store it on BookMetadata.calibreColumns, render the
columns in the book details view, and match column names and values
in the library search so a value like a recommends tag can be found
by typing it.

Closes #4811
This commit is contained in:
Huang Xin
2026-07-06 01:08:36 +09:00
committed by GitHub
parent 0b180da6a6
commit ec45a080fc
8 changed files with 345 additions and 4 deletions
+26 -1
View File
@@ -1,4 +1,4 @@
import { BookMetadata, EXTS } from '@/libs/document';
import { BookMetadata, CalibreCustomColumn, EXTS } from '@/libs/document';
import {
Book,
BOOK_CONFIG_SCHEMA_VERSION,
@@ -225,6 +225,31 @@ export const formatDate = (date: string | number | Date | null | undefined, isUT
}
};
export const formatCalibreColumnValue = (column: CalibreCustomColumn): string => {
const { datatype, value, extra } = column;
if (Array.isArray(value)) return value.join(', ');
switch (datatype) {
case 'rating': {
// 0-10 in half stars, like calibre's own rendering
const rating = typeof value === 'number' ? value : 0;
return '★'.repeat(Math.floor(rating / 2)) + (rating % 2 ? '½' : '');
}
case 'series':
return extra != null ? `${value} [${extra}]` : String(value);
case 'datetime':
return formatDate(String(value), true) || '';
case 'comments':
return String(value)
.replace(/<[^>]*>/g, ' ')
.replace(/\s+/g, ' ')
.trim();
case 'bool':
return value ? '✓' : '✗';
default:
return String(value);
}
};
export const formatLocaleDateTime = (date: number | Date) => {
const userLang = getLocale();
return new Date(date).toLocaleString(userLang);