+Compressed IMG files

This commit is contained in:
Simon Martens
2025-09-29 23:18:43 +02:00
parent fdfe95a225
commit 269d635f1d
13 changed files with 840 additions and 97 deletions

View File

@@ -41,14 +41,16 @@ type IndividualPiecesByPage struct {
}
type IssuePage struct {
PageNumber int
ImagePath string
Available bool
GridColumn int // 1 or 2 for left/right positioning
GridRow int // Row number in grid
HasHeader bool // Whether this page has a double-spread header
HeaderText string // Text for double-spread header
PageIcon string // Icon type: "first", "last", "even", "odd"
PageNumber int
ImagePath string // Full-quality image path (prefers WebP over JPEG)
PreviewPath string // Compressed WebP path for layout views
JpegPath string // JPEG path for download button
Available bool
GridColumn int // 1 or 2 for left/right positioning
GridRow int // Row number in grid
HasHeader bool // Whether this page has a double-spread header
HeaderText string // Text for double-spread header
PageIcon string // Icon type: "first", "last", "even", "odd"
}
type IssueImages struct {
@@ -58,13 +60,15 @@ type IssueImages struct {
}
type ImageFile struct {
Year int
Issue int
Page int
IsBeilage bool
BeilageNo int
Filename string
Path string
Year int
Issue int
Page int
IsBeilage bool
BeilageNo int
Filename string
Path string // Primary path (prefers WebP over JPEG)
PreviewPath string // Path to compressed WebP version for layout views
JpegPath string // Path to JPEG version (for download button)
}
type ImageRegistry struct {
@@ -518,16 +522,30 @@ func LoadIssueImages(issue xmlmodels.Issue) (IssueImages, error) {
if foundFile != nil {
images.HasImages = true
// Use preview path if available, otherwise fallback to original
previewPath := foundFile.PreviewPath
if previewPath == "" {
previewPath = foundFile.Path
}
// Use JPEG path if available, otherwise fallback to primary
jpegPath := foundFile.JpegPath
if jpegPath == "" {
jpegPath = foundFile.Path
}
images.MainPages = append(images.MainPages, IssuePage{
PageNumber: page,
ImagePath: foundFile.Path,
Available: true,
PageNumber: page,
ImagePath: foundFile.Path,
PreviewPath: previewPath,
JpegPath: jpegPath,
Available: true,
})
} else {
images.MainPages = append(images.MainPages, IssuePage{
PageNumber: page,
ImagePath: "",
Available: false,
PageNumber: page,
ImagePath: "",
PreviewPath: "",
JpegPath: "",
Available: false,
})
}
}
@@ -539,10 +557,22 @@ func LoadIssueImages(issue xmlmodels.Issue) (IssueImages, error) {
// Add ALL beilage files found for this issue
for _, file := range beilageFiles {
images.HasImages = true
// Use preview path if available, otherwise fallback to original
previewPath := file.PreviewPath
if previewPath == "" {
previewPath = file.Path
}
// Use JPEG path if available, otherwise fallback to primary
jpegPath := file.JpegPath
if jpegPath == "" {
jpegPath = file.Path
}
beilagePages = append(beilagePages, IssuePage{
PageNumber: file.Page,
ImagePath: file.Path,
Available: true,
PageNumber: file.Page,
ImagePath: file.Path,
PreviewPath: previewPath,
JpegPath: jpegPath,
Available: true,
})
}
@@ -573,7 +603,10 @@ func initImageRegistry() error {
ByYearPage: make(map[string]ImageFile),
}
return filepath.Walk("pictures", func(path string, info os.FileInfo, err error) error {
// Temporary map to collect all files by their base name (year-issue-page)
tempFiles := make(map[string]*ImageFile)
err := filepath.Walk("pictures", func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
@@ -583,14 +616,22 @@ func initImageRegistry() error {
}
filename := info.Name()
filenamelower := strings.ToLower(filename)
// Skip non-jpg files
if !strings.HasSuffix(strings.ToLower(filename), ".jpg") {
return nil
// Only process .jpg and .webp files (but skip preview files)
var nameWithoutExt string
var isWebP bool
if strings.HasSuffix(filenamelower, ".jpg") {
nameWithoutExt = strings.TrimSuffix(filename, ".jpg")
isWebP = false
} else if strings.HasSuffix(filenamelower, ".webp") && !strings.HasSuffix(filenamelower, "-preview.webp") {
nameWithoutExt = strings.TrimSuffix(filename, ".webp")
isWebP = true
} else {
return nil // Skip non-image files and preview files
}
// Remove .jpg extension and split by -
nameWithoutExt := strings.TrimSuffix(filename, ".jpg")
parts := strings.Split(nameWithoutExt, "-")
// Need at least 3 parts: year-issue-page
@@ -624,26 +665,87 @@ func initImageRegistry() error {
return nil
}
imageFile := ImageFile{
Year: year,
Issue: issue,
Page: page,
IsBeilage: isBeilage,
BeilageNo: 1, // Default beilage number
Filename: filename,
Path: fmt.Sprintf("/static/pictures/%s", path[9:]), // Remove "pictures/" prefix
// Create unique key for this image (handles both regular and beilage)
var uniqueKey string
if isBeilage {
uniqueKey = fmt.Sprintf("%d-%db-%d", year, issue, page)
} else {
uniqueKey = fmt.Sprintf("%d-%d-%d", year, issue, page)
}
imageRegistry.Files = append(imageRegistry.Files, imageFile)
// Get or create the ImageFile entry
imageFile, exists := tempFiles[uniqueKey]
if !exists {
imageFile = &ImageFile{
Year: year,
Issue: issue,
Page: page,
IsBeilage: isBeilage,
BeilageNo: 1, // Default beilage number
}
tempFiles[uniqueKey] = imageFile
}
yearIssueKey := fmt.Sprintf("%d-%d", year, issue)
imageRegistry.ByYearIssue[yearIssueKey] = append(imageRegistry.ByYearIssue[yearIssueKey], imageFile)
if !isBeilage {
yearPageKey := fmt.Sprintf("%d-%d", year, page)
imageRegistry.ByYearPage[yearPageKey] = imageFile
// Set paths based on file type
currentPath := fmt.Sprintf("/static/pictures/%s", path[9:]) // Remove "pictures/" prefix
if isWebP {
// WebP is the primary path for single page viewer
imageFile.Path = currentPath
imageFile.Filename = filename
} else {
// JPEG is the fallback path for download
imageFile.JpegPath = currentPath
// If no WebP path is set yet, use JPEG as primary
if imageFile.Path == "" {
imageFile.Path = currentPath
imageFile.Filename = filename
}
}
return nil
})
if err != nil {
return err
}
// Second pass: set PreviewPath for each ImageFile by checking for preview files
for _, imageFile := range tempFiles {
// Extract the base name from the filename to preserve original format
baseNameWithExt := imageFile.Filename
var baseName string
// Remove extension to get base name
if strings.HasSuffix(strings.ToLower(baseNameWithExt), ".webp") {
baseName = strings.TrimSuffix(baseNameWithExt, ".webp")
} else if strings.HasSuffix(strings.ToLower(baseNameWithExt), ".jpg") {
baseName = strings.TrimSuffix(baseNameWithExt, ".jpg")
} else {
baseName = baseNameWithExt
}
// Generate preview filename using the original base name format
previewFilename := baseName + "-preview.webp"
// Check if preview file exists
previewFullPath := filepath.Join("pictures", fmt.Sprintf("%d", imageFile.Year), previewFilename)
if _, err := os.Stat(previewFullPath); err == nil {
imageFile.PreviewPath = fmt.Sprintf("/static/pictures/%d/%s", imageFile.Year, previewFilename)
}
}
// Convert temp map to final registry structures
for _, imageFile := range tempFiles {
imageRegistry.Files = append(imageRegistry.Files, *imageFile)
yearIssueKey := fmt.Sprintf("%d-%d", imageFile.Year, imageFile.Issue)
imageRegistry.ByYearIssue[yearIssueKey] = append(imageRegistry.ByYearIssue[yearIssueKey], *imageFile)
if !imageFile.IsBeilage {
yearPageKey := fmt.Sprintf("%d-%d", imageFile.Year, imageFile.Page)
imageRegistry.ByYearPage[yearPageKey] = *imageFile
}
}
return nil
}

View File

@@ -11,7 +11,9 @@ type PiecePageEntry struct {
PageNumber int
IssueYear int
IssueNumber int
ImagePath string
ImagePath string // Full-quality image path (prefers WebP over JPEG)
PreviewPath string // Compressed WebP path for layout views
JpegPath string // JPEG path for download button
IsContinuation bool
IssueContext string // "1764 Nr. 37" for display
Available bool
@@ -96,8 +98,8 @@ func NewPieceView(piece xmlmodels.Piece, lib *xmlmodels.Library) (*PieceVM, erro
PartNumber: partIndex + 1, // Part number (1-based)
}
// Get actual image path from registry
pageEntry.ImagePath = getImagePathFromRegistryWithBeilage(issueRef.When.Year, issueRef.Nr, pageNum, issueRef.Beilage > 0)
// Get actual image path, preview path, and JPEG path from registry
pageEntry.ImagePath, pageEntry.PreviewPath, pageEntry.JpegPath = getImagePathsFromRegistryWithBeilage(issueRef.When.Year, issueRef.Nr, pageNum, issueRef.Beilage > 0)
pvm.AllPages = append(pvm.AllPages, pageEntry)
}
@@ -140,10 +142,12 @@ func (pvm *PieceVM) loadImages() error {
for i, pageEntry := range pvm.AllPages {
// Create IssuePage for template compatibility
issuePage := IssuePage{
PageNumber: pageEntry.PageNumber,
ImagePath: pageEntry.ImagePath,
Available: true, // Assume available for now
PageIcon: "single", // Simplified icon for piece view
PageNumber: pageEntry.PageNumber,
ImagePath: pageEntry.ImagePath,
PreviewPath: pageEntry.PreviewPath,
JpegPath: pageEntry.JpegPath,
Available: true, // Assume available for now
PageIcon: "single", // Simplified icon for piece view
}
// Check if image actually exists using the registry
@@ -231,7 +235,56 @@ func getImagePathFromRegistry(year, page int) string {
return fmt.Sprintf("/static/pictures/%d/seite_%d.jpg", year, page)
}
// getImagePathsFromRegistryWithBeilage gets image paths: primary (WebP preferred), preview (compressed), and JPEG, handling both regular and Beilage pages
func getImagePathsFromRegistryWithBeilage(year, issue, page int, isBeilage bool) (string, string, string) {
// Initialize registry if needed
if err := initImageRegistry(); err != nil {
return "", "", ""
}
// For regular pages, use the year-page lookup
if !isBeilage {
key := fmt.Sprintf("%d-%d", year, page)
if imageFile, exists := imageRegistry.ByYearPage[key]; exists {
previewPath := imageFile.PreviewPath
if previewPath == "" {
previewPath = imageFile.Path // Fallback to original if no preview
}
jpegPath := imageFile.JpegPath
if jpegPath == "" {
jpegPath = imageFile.Path // Fallback to primary if no separate JPEG
}
return imageFile.Path, previewPath, jpegPath
}
// Fallback for regular pages
fallbackPath := fmt.Sprintf("/static/pictures/%d/seite_%d.jpg", year, page)
return fallbackPath, fallbackPath, fallbackPath
}
// For Beilage pages, search through all files for this year-issue
yearIssueKey := fmt.Sprintf("%d-%d", year, issue)
if issueFiles, exists := imageRegistry.ByYearIssue[yearIssueKey]; exists {
for _, file := range issueFiles {
if file.IsBeilage && file.Page == page {
previewPath := file.PreviewPath
if previewPath == "" {
previewPath = file.Path // Fallback to original if no preview
}
jpegPath := file.JpegPath
if jpegPath == "" {
jpegPath = file.Path // Fallback to primary if no separate JPEG
}
return file.Path, previewPath, jpegPath
}
}
}
// No file found
return "", "", ""
}
// getImagePathFromRegistryWithBeilage gets the actual image path, handling both regular and Beilage pages
// Kept for backward compatibility
func getImagePathFromRegistryWithBeilage(year, issue, page int, isBeilage bool) string {
// Initialize registry if needed
if err := initImageRegistry(); err != nil {