mirror of
https://github.com/Theodor-Springmann-Stiftung/kgpz_web.git
synced 2025-10-29 09:05:30 +00:00
BUGFIX: gitignore pics
This commit is contained in:
279
providers/pictures/pictures.go
Normal file
279
providers/pictures/pictures.go
Normal file
@@ -0,0 +1,279 @@
|
||||
package pictures
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// ImageFile represents a single image file with its metadata
|
||||
type ImageFile struct {
|
||||
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)
|
||||
}
|
||||
|
||||
// imageRegistry holds all image files organized by different keys for fast lookup
|
||||
type imageRegistry struct {
|
||||
Files []ImageFile
|
||||
ByYearIssue map[string][]ImageFile // "year-issue" -> files
|
||||
ByYearPage map[string]ImageFile // "year-page" -> file (only for non-beilage)
|
||||
}
|
||||
|
||||
// PicturesProvider manages all newspaper picture images with thread-safe access
|
||||
type PicturesProvider struct {
|
||||
mu sync.RWMutex
|
||||
registry *imageRegistry
|
||||
}
|
||||
|
||||
// NewPicturesProvider creates a new PicturesProvider
|
||||
func NewPicturesProvider() *PicturesProvider {
|
||||
return &PicturesProvider{
|
||||
registry: nil,
|
||||
}
|
||||
}
|
||||
|
||||
// Scan scans the pictures directory and builds the image registry
|
||||
func (p *PicturesProvider) Scan(path string) error {
|
||||
p.mu.Lock()
|
||||
defer p.mu.Unlock()
|
||||
|
||||
registry := &imageRegistry{
|
||||
Files: make([]ImageFile, 0),
|
||||
ByYearIssue: make(map[string][]ImageFile),
|
||||
ByYearPage: make(map[string]ImageFile),
|
||||
}
|
||||
|
||||
// Temporary map to collect all files by their base name (year-issue-page)
|
||||
tempFiles := make(map[string]*ImageFile)
|
||||
|
||||
err := filepath.Walk(path, func(filePath string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return nil
|
||||
}
|
||||
|
||||
filename := info.Name()
|
||||
filenamelower := strings.ToLower(filename)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
parts := strings.Split(nameWithoutExt, "-")
|
||||
|
||||
// Need at least 3 parts: year-issue-page
|
||||
if len(parts) != 3 {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse year
|
||||
year, err := strconv.Atoi(strings.TrimSpace(parts[0]))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Check if second part ends with 'b' (beilage)
|
||||
issueStr := strings.TrimSpace(parts[1])
|
||||
isBeilage := strings.HasSuffix(issueStr, "b")
|
||||
|
||||
if isBeilage {
|
||||
issueStr = strings.TrimSuffix(issueStr, "b")
|
||||
}
|
||||
|
||||
// Parse issue number
|
||||
issue, err := strconv.Atoi(issueStr)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Parse page number
|
||||
page, err := strconv.Atoi(strings.TrimSpace(parts[2]))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Set paths based on file type
|
||||
currentPath := fmt.Sprintf("/static/pictures/%s", filePath[len(path)+1:]) // Remove path 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(path, 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 {
|
||||
registry.Files = append(registry.Files, *imageFile)
|
||||
|
||||
yearIssueKey := fmt.Sprintf("%d-%d", imageFile.Year, imageFile.Issue)
|
||||
registry.ByYearIssue[yearIssueKey] = append(registry.ByYearIssue[yearIssueKey], *imageFile)
|
||||
|
||||
if !imageFile.IsBeilage {
|
||||
yearPageKey := fmt.Sprintf("%d-%d", imageFile.Year, imageFile.Page)
|
||||
registry.ByYearPage[yearPageKey] = *imageFile
|
||||
}
|
||||
}
|
||||
|
||||
p.registry = registry
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetByYearIssuePage returns an image file for a specific year, issue, and page
|
||||
// For beilage pages, isBeilage should be true
|
||||
func (p *PicturesProvider) GetByYearIssuePage(year, issue, page int, isBeilage bool) (*ImageFile, bool) {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
if p.registry == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// For regular pages, use year-page lookup
|
||||
if !isBeilage {
|
||||
key := fmt.Sprintf("%d-%d", year, page)
|
||||
if imageFile, exists := p.registry.ByYearPage[key]; exists {
|
||||
return &imageFile, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// For beilage pages, search through all files for this year-issue
|
||||
yearIssueKey := fmt.Sprintf("%d-%d", year, issue)
|
||||
if issueFiles, exists := p.registry.ByYearIssue[yearIssueKey]; exists {
|
||||
for _, file := range issueFiles {
|
||||
if file.IsBeilage && file.Page == page {
|
||||
return &file, true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// GetByYearIssue returns all image files for a specific year and issue
|
||||
func (p *PicturesProvider) GetByYearIssue(year, issue int) []ImageFile {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
if p.registry == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
yearIssueKey := fmt.Sprintf("%d-%d", year, issue)
|
||||
if files, exists := p.registry.ByYearIssue[yearIssueKey]; exists {
|
||||
// Return a copy to prevent external modification
|
||||
result := make([]ImageFile, len(files))
|
||||
copy(result, files)
|
||||
return result
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetByYearPage returns an image file for a specific year and page (non-beilage only)
|
||||
func (p *PicturesProvider) GetByYearPage(year, page int) (*ImageFile, bool) {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
if p.registry == nil {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
key := fmt.Sprintf("%d-%d", year, page)
|
||||
if imageFile, exists := p.registry.ByYearPage[key]; exists {
|
||||
return &imageFile, true
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// HasImages returns true if the registry has been initialized and contains images
|
||||
func (p *PicturesProvider) HasImages() bool {
|
||||
p.mu.RLock()
|
||||
defer p.mu.RUnlock()
|
||||
|
||||
return p.registry != nil && len(p.registry.Files) > 0
|
||||
}
|
||||
Reference in New Issue
Block a user