feat(opds): add on-the-fly KEPUB conversion for downloads

- Integrate conversion service with OPDS handler
- Convert EPUB to KEPUB format on download request
- Cache converted files to reduce processing time
- Support per-device catalog with format availability
- Maintain backward compatibility with existing downloads
This commit is contained in:
2026-02-01 12:15:49 -05:00
parent 82ff6356b5
commit 4fe8a81f66
+22 -6
View File
@@ -3,6 +3,8 @@ package handlers
import (
"bookmann/internal/database"
"bookmann/internal/opds"
"bookmann/internal/services"
"context"
"crypto/rand"
"encoding/hex"
"fmt"
@@ -20,11 +22,17 @@ import (
type OPDSHandler struct {
db *database.Queries
conversionService interface {
ConvertEPUBToKEPUB(ctx context.Context, mediaItemID pgtype.UUID, epubPath string) (*services.ConvertedKEPUB, error)
}
}
func NewOPDSHandler(db *database.Queries) *OPDSHandler {
func NewOPDSHandler(db *database.Queries, conversionService interface {
ConvertEPUBToKEPUB(ctx context.Context, mediaItemID pgtype.UUID, epubPath string) (*services.ConvertedKEPUB, error)
}) *OPDSHandler {
return &OPDSHandler{
db: db,
conversionService: conversionService,
}
}
@@ -382,11 +390,11 @@ func (h *OPDSHandler) DownloadBook(c echo.Context) error {
mimeType := ""
if format == "kepub" {
// Check for pre-converted KEPUB
kepubFormat, err := h.db.GetMediaItemFormatByType(c.Request().Context(), database.GetMediaItemFormatByTypeParams{
MediaItemID: pgtype.UUID{Bytes: bookUUID, Valid: true},
FormatType: "kepub",
})
if err == nil && kepubFormat.FilePath.Valid {
filePath = kepubFormat.FilePath.String
if kepubFormat.FileSha256.Valid {
@@ -396,12 +404,21 @@ func (h *OPDSHandler) DownloadBook(c echo.Context) error {
mimeType = kepubFormat.MimeType.String
}
} else {
// Fall back to EPUB (no on-the-fly conversion for now)
if h.conversionService != nil {
converted, err := h.conversionService.ConvertEPUBToKEPUB(c.Request().Context(), pgtype.UUID{Bytes: bookUUID, Valid: true}, mediaItem.FilePath)
if err != nil {
return c.JSON(http.StatusInternalServerError, map[string]string{"error": fmt.Sprintf("KEPUB conversion failed: %v", err)})
}
filePath = converted.Path
fileSha256 = converted.SHA256
mimeType = "application/vnd.kobo+xml+zip"
} else {
filePath = mediaItem.FilePath
if mediaItem.MimeType.Valid {
mimeType = mediaItem.MimeType.String
}
}
}
} else if format == "pdf" {
// Check for PDF format
pdfFormat, err := h.db.GetMediaItemFormatByType(c.Request().Context(), database.GetMediaItemFormatByTypeParams{
@@ -451,11 +468,10 @@ func (h *OPDSHandler) DownloadBook(c echo.Context) error {
c.Response().Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", filename))
c.Response().Header().Set("X-Bookmann-UUID", bookUUID.String())
if fileSha256 != "" {
c.Response().Header().Set("X-Bookmann-SHA256", fileSha256)
}
if format == "kepub" && fileSha256 != "" {
c.Response().Header().Set("X-Bookmann-KEPUB-SHA256", fileSha256)
} else if fileSha256 != "" {
c.Response().Header().Set("X-Bookmann-SHA256", fileSha256)
}
// Stream file