From 696d1bef9ff7d0716bad95e94dfe935bcdefbbcf Mon Sep 17 00:00:00 2001 From: Simon Martens Date: Tue, 30 Sep 2025 01:53:19 +0200 Subject: [PATCH] +logging audit +imageprovider --- Dockerfile | 9 ++ app/kgpz.go | 32 +++-- controllers/ausgabe_controller.go | 5 +- controllers/jahr_controller.go | 2 +- controllers/piece_controller.go | 9 +- helpers/logging/logging.go | 14 -- providers/config.go | 6 + viewmodels/issue_view.go | 211 +++--------------------------- viewmodels/piece_view.go | 183 ++++++++------------------ 9 files changed, 115 insertions(+), 356 deletions(-) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..0d56393 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,9 @@ +FROM golang:1.25 +WORKDIR /app + +COPY . . +RUN go build +EXPOSE 8082 + +CMD ["./lenz-web"] + diff --git a/app/kgpz.go b/app/kgpz.go index eb4c297..838eda6 100644 --- a/app/kgpz.go +++ b/app/kgpz.go @@ -12,6 +12,7 @@ import ( "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers" "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/geonames" "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/gnd" + "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/pictures" searchprovider "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/search" "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/xmlprovider" "github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels" @@ -64,6 +65,7 @@ type KGPZ struct { Repo *providers.GitProvider GND *gnd.GNDProvider Geonames *geonames.GeonamesProvider + Pictures *pictures.PicturesProvider Library *xmlmodels.Library Search *searchprovider.SearchProvider @@ -98,14 +100,15 @@ func (k *KGPZ) Pre(srv *fiber.App) error { } // Serve newspaper pictures from pictures directory - if _, err := os.Stat("pictures"); err == nil { - picturesFS := os.DirFS("pictures") + picturesPath := k.Config.Config.PicturesPath + if _, err := os.Stat(picturesPath); err == nil { + picturesFS := os.DirFS(picturesPath) srv.Use(PICTURES_PREFIX, compress.New(compress.Config{ Level: compress.LevelBestSpeed, }), etag.New(), helpers.StaticHandler(&picturesFS)) - logging.Info("Serving newspaper pictures from pictures/ directory.") + logging.Info("Serving newspaper pictures from " + picturesPath + " directory.") } else { - logging.Info("Pictures folder not found. Skipping picture serving.") + logging.Info("Pictures folder not found at " + picturesPath + ". Skipping picture serving.") } return nil @@ -132,6 +135,9 @@ func (k *KGPZ) Init() error { if err := k.initGeonames(); err != nil { logging.Error(err, "Error reading Geonames-Cache. Continuing.") } + if err := k.initPictures(); err != nil { + logging.Error(err, "Error scanning pictures directory. Continuing without pictures.") + } if sp, err := searchprovider.NewSearchProvider(filepath.Join(k.Config.Config.BaseDIR, k.Config.SearchPath)); err != nil { logging.Error(err, "Error initializing SearchProvider. Continuing without Search.") @@ -162,6 +168,11 @@ func (k *KGPZ) initGeonames() error { return k.Geonames.ReadCache(filepath.Join(k.Config.BaseDIR, k.Config.GeoPath)) } +func (k *KGPZ) initPictures() error { + k.Pictures = pictures.NewPicturesProvider() + return k.Pictures.Scan(k.Config.Config.PicturesPath) +} + func (k *KGPZ) Routes(srv *fiber.App) error { srv.Get("/", func(c *fiber.Ctx) error { c.Redirect(INDEX_URL) @@ -174,8 +185,8 @@ func (k *KGPZ) Routes(srv *fiber.App) error { srv.Get(PLACE_OVERVIEW_URL, controllers.GetPlace(k.Library, k.Geonames)) srv.Get(CATEGORY_OVERVIEW_URL, controllers.GetCategory(k.Library)) srv.Get(AGENTS_OVERVIEW_URL, controllers.GetAgents(k.Library)) - srv.Get(PIECE_PAGE_URL, controllers.GetPieceWithPage(k.Library)) - srv.Get(PIECE_URL, controllers.GetPiece(k.Library)) + srv.Get(PIECE_PAGE_URL, controllers.GetPieceWithPage(k.Library, k.Pictures)) + srv.Get(PIECE_URL, controllers.GetPiece(k.Library, k.Pictures)) // Page jump routes for direct navigation srv.Get(PAGE_JUMP_URL, controllers.GetPageJump(k.Library)) @@ -188,8 +199,8 @@ func (k *KGPZ) Routes(srv *fiber.App) error { // This applies to all paths with two or three segments without a static prefix: // Prob better to do /ausgabe/:year/:issue/:page? and /jahrgang/:year? respectively. srv.Get(YEAR_OVERVIEW_URL, controllers.GetYear(k.Library)) - srv.Get(ISSSUE_URL, controllers.GetIssue(k.Library)) - srv.Get(ADDITIONS_URL, controllers.GetIssue(k.Library)) + srv.Get(ISSSUE_URL, controllers.GetIssue(k.Library, k.Pictures)) + srv.Get(ADDITIONS_URL, controllers.GetIssue(k.Library, k.Pictures)) srv.Get(EDITION_URL, controllers.Get(EDITION_URL)) srv.Get(PRIVACY_URL, controllers.Get(PRIVACY_URL)) @@ -621,6 +632,11 @@ func (k *KGPZ) Pull() { k.Serialize() k.EnrichAndRebuildIndex() + // Rescan pictures after pull + if err := k.initPictures(); err != nil { + logging.Error(err, "Error rescanning pictures directory after pull.") + } + // Notify about git data update if k.gitUpdateCallback != nil { k.gitUpdateCallback( diff --git a/controllers/ausgabe_controller.go b/controllers/ausgabe_controller.go index ad74f9b..a6d3e12 100644 --- a/controllers/ausgabe_controller.go +++ b/controllers/ausgabe_controller.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging" + "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/pictures" "github.com/Theodor-Springmann-Stiftung/kgpz_web/viewmodels" "github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels" "github.com/gofiber/fiber/v2" @@ -16,7 +17,7 @@ const ( MAXYEAR = 1779 ) -func GetIssue(kgpz *xmlmodels.Library) fiber.Handler { +func GetIssue(kgpz *xmlmodels.Library, pics *pictures.PicturesProvider) fiber.Handler { return func(c *fiber.Ctx) error { y := c.Params("year") yi, err := strconv.Atoi(y) @@ -67,7 +68,7 @@ func GetIssue(kgpz *xmlmodels.Library) fiber.Handler { } } - issue, err := viewmodels.NewSingleIssueView(yi, di, kgpz) + issue, err := viewmodels.NewSingleIssueView(yi, di, kgpz, pics) if err != nil { logging.Error(err, "Issue could not be found") diff --git a/controllers/jahr_controller.go b/controllers/jahr_controller.go index 47c6a00..e1cac8d 100644 --- a/controllers/jahr_controller.go +++ b/controllers/jahr_controller.go @@ -21,7 +21,7 @@ func GetYear(kgpz *xmlmodels.Library) fiber.Handler { view, err := viewmodels.YearView(yi, kgpz) if err != nil { - logging.ErrorDebug(err, "Keine Ausgaben für das Jahr "+y) + logging.Error(err, "Keine Ausgaben für das Jahr "+y) return c.SendStatus(fiber.StatusNotFound) } diff --git a/controllers/piece_controller.go b/controllers/piece_controller.go index c2824cc..ad88142 100644 --- a/controllers/piece_controller.go +++ b/controllers/piece_controller.go @@ -6,12 +6,13 @@ import ( "strings" "github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging" + "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/pictures" "github.com/Theodor-Springmann-Stiftung/kgpz_web/viewmodels" "github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels" "github.com/gofiber/fiber/v2" ) -func GetPiece(kgpz *xmlmodels.Library) fiber.Handler { +func GetPiece(kgpz *xmlmodels.Library, pics *pictures.PicturesProvider) fiber.Handler { return func(c *fiber.Ctx) error { id := c.Params("id") if id == "" { @@ -25,7 +26,7 @@ func GetPiece(kgpz *xmlmodels.Library) fiber.Handler { return c.SendStatus(fiber.StatusNotFound) } - pieceView, err := viewmodels.NewPieceView(*piece, kgpz) + pieceView, err := viewmodels.NewPieceView(*piece, kgpz, pics) if err != nil { logging.Error(err, "Piece view could not be created") return c.SendStatus(fiber.StatusInternalServerError) @@ -36,7 +37,7 @@ func GetPiece(kgpz *xmlmodels.Library) fiber.Handler { } // GetPieceWithPage handles piece URLs with optional page parameter: /beitrag/:id/:page? -func GetPieceWithPage(kgpz *xmlmodels.Library) fiber.Handler { +func GetPieceWithPage(kgpz *xmlmodels.Library, pics *pictures.PicturesProvider) fiber.Handler { return func(c *fiber.Ctx) error { id := c.Params("id") if id == "" { @@ -86,7 +87,7 @@ func GetPieceWithPage(kgpz *xmlmodels.Library) fiber.Handler { return c.SendStatus(fiber.StatusNotFound) } - pieceView, err := viewmodels.NewPieceView(*piece, kgpz) + pieceView, err := viewmodels.NewPieceView(*piece, kgpz, pics) if err != nil { logging.Error(err, "Piece view could not be created") return c.SendStatus(fiber.StatusInternalServerError) diff --git a/helpers/logging/logging.go b/helpers/logging/logging.go index 79bf957..eb8e977 100644 --- a/helpers/logging/logging.go +++ b/helpers/logging/logging.go @@ -51,20 +51,6 @@ func Error(err error, msg ...string) { slog.Error(err.Error()) } -func ErrorDebug(err error, msg ...string) { - if err == nil { - return - } - - if len(msg) > 0 { - for _, m := range msg { - slog.Debug(m) - } - } - - slog.Debug(err.Error()) -} - func Info(msg ...string) { if len(msg) > 0 { for _, m := range msg { diff --git a/providers/config.go b/providers/config.go index 02c4540..0f1176e 100644 --- a/providers/config.go +++ b/providers/config.go @@ -20,6 +20,7 @@ const ( DEFAULT_GEO_CACHE_DIR = "geo" DEFAULT_SEARCH_CACHE_DIR = "search" DEFAULT_IMG_DIR = "data_bilder" + DEFAULT_PICTURES_DIR = "pictures" DEFAULT_PORT = "8080" DEFAULT_ADDR = "localhost" @@ -43,6 +44,7 @@ type Config struct { GeoPath string `json:"geo_path" envconfig:"GEO_PATH"` SearchPath string `json:"search_path" envconfig:"SEARCH_PATH"` ImgPath string `json:"img_path" envconfig:"IMG_PATH"` + PicturesPath string `json:"pictures_path" envconfig:"PICTURES_PATH"` WebHookEndpoint string `json:"webhook_endpoint" envconfig:"WEBHOOK_ENDPOINT"` WebHookSecret string `json:"webhook_secret" envconfig:"WEBHOOK_SECRET"` Debug bool `json:"debug" envconfig:"DEBUG"` @@ -134,6 +136,10 @@ func readDefaults(cfg *Config) *Config { cfg.SearchPath = DEFAULT_SEARCH_CACHE_DIR } + if strings.TrimSpace(cfg.PicturesPath) == "" { + cfg.PicturesPath = DEFAULT_PICTURES_DIR + } + return cfg } diff --git a/viewmodels/issue_view.go b/viewmodels/issue_view.go index 31e1e98..6e10172 100644 --- a/viewmodels/issue_view.go +++ b/viewmodels/issue_view.go @@ -3,14 +3,13 @@ package viewmodels import ( "fmt" "maps" - "os" - "path/filepath" "slices" "sort" "strconv" - "strings" "github.com/Theodor-Springmann-Stiftung/kgpz_web/functions" + "github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging" + "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/pictures" "github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels" ) @@ -53,32 +52,15 @@ type IssuePage struct { PageIcon string // Icon type: "first", "last", "even", "odd" } +// Deprecated: Use pictures.ImageFile from provider instead +type ImageFile = pictures.ImageFile + type IssueImages struct { MainPages []IssuePage AdditionalPages map[int][]IssuePage // Beilage number -> pages HasImages bool } -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) -} - -type ImageRegistry struct { - Files []ImageFile - ByYearIssue map[string][]ImageFile // "year-issue" -> files - ByYearPage map[string]ImageFile // "year-page" -> file -} - -var imageRegistry *ImageRegistry - // TODO: Next & Prev type IssueVM struct { xmlmodels.Issue @@ -90,7 +72,7 @@ type IssueVM struct { HasBeilageButton bool // Whether to show beilage navigation button } -func NewSingleIssueView(y, no int, lib *xmlmodels.Library) (*IssueVM, error) { +func NewSingleIssueView(y, no int, lib *xmlmodels.Library, pics *pictures.PicturesProvider) (*IssueVM, error) { lib.Issues.Lock() var issue *xmlmodels.Issue = nil var next *xmlmodels.Issue = nil @@ -123,7 +105,7 @@ func NewSingleIssueView(y, no int, lib *xmlmodels.Library) (*IssueVM, error) { slices.Sort(ppi.Pages) slices.Sort(ppa.Pages) - images, err := LoadIssueImages(*issue) + images, err := LoadIssueImages(*issue, pics) if err != nil { return nil, err } @@ -152,7 +134,7 @@ func PiecesForIssue(lib *xmlmodels.Library, issue xmlmodels.Issue) (PiecesByPage if issueRef.Nr == issue.Number.No && issueRef.When.Year == year { // DEBUG: Log piece details for specific issue if year == 1771 && issue.Number.No == 29 { - fmt.Printf("DEBUG PiecesForIssue: Piece ID=%s, Von=%d, Bis=%d, Beilage=%d\n", piece.Identifier.ID, issueRef.Von, issueRef.Bis, issueRef.Beilage) + logging.Debug(fmt.Sprintf("PiecesForIssue: Piece ID=%s, Von=%d, Bis=%d, Beilage=%d", piece.Identifier.ID, issueRef.Von, issueRef.Bis, issueRef.Beilage)) } // Add main entry on starting page @@ -477,12 +459,7 @@ func determinePageIconForLayout(pageNum int) string { return "odd" } -func LoadIssueImages(issue xmlmodels.Issue) (IssueImages, error) { - // Initialize registry if not already done - if err := initImageRegistry(); err != nil { - return IssueImages{}, err - } - +func LoadIssueImages(issue xmlmodels.Issue, pics *pictures.PicturesProvider) (IssueImages, error) { year := issue.Datum.When.Year issueNo := issue.Number.No @@ -493,12 +470,11 @@ func LoadIssueImages(issue xmlmodels.Issue) (IssueImages, error) { } // Get all image files for this year-issue combination - yearIssueKey := fmt.Sprintf("%d-%d", year, issueNo) - issueFiles := imageRegistry.ByYearIssue[yearIssueKey] + issueFiles := pics.GetByYearIssue(year, issueNo) // Separate main pages from beilage pages - var mainFiles []ImageFile - var beilageFiles []ImageFile + var mainFiles []pictures.ImageFile + var beilageFiles []pictures.ImageFile for _, file := range issueFiles { if file.IsBeilage { @@ -510,12 +486,13 @@ func LoadIssueImages(issue xmlmodels.Issue) (IssueImages, error) { // Create main pages - match with issue page range for page := issue.Von; page <= issue.Bis; page++ { - var foundFile *ImageFile + var foundFile *pictures.ImageFile // Look for a file that has this page number for _, file := range mainFiles { if file.Page == page { - foundFile = &file + fileCopy := file + foundFile = &fileCopy break } } @@ -591,161 +568,3 @@ func LoadIssueImages(issue xmlmodels.Issue) (IssueImages, error) { return images, nil } - -func initImageRegistry() error { - if imageRegistry != nil { - return nil - } - - imageRegistry = &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("pictures", func(path 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", 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 -} diff --git a/viewmodels/piece_view.go b/viewmodels/piece_view.go index 730f943..9df9d16 100644 --- a/viewmodels/piece_view.go +++ b/viewmodels/piece_view.go @@ -4,6 +4,8 @@ import ( "fmt" "sort" + "github.com/Theodor-Springmann-Stiftung/kgpz_web/helpers/logging" + "github.com/Theodor-Springmann-Stiftung/kgpz_web/providers/pictures" "github.com/Theodor-Springmann-Stiftung/kgpz_web/xmlmodels" ) @@ -38,7 +40,7 @@ type PieceVM struct { IssueContexts []string // List of issue contexts for display } -func NewPieceView(piece xmlmodels.Piece, lib *xmlmodels.Library) (*PieceVM, error) { +func NewPieceView(piece xmlmodels.Piece, lib *xmlmodels.Library, pics *pictures.PicturesProvider) (*PieceVM, error) { pvm := &PieceVM{ Piece: piece, AllIssueRefs: piece.IssueRefs, @@ -47,10 +49,10 @@ func NewPieceView(piece xmlmodels.Piece, lib *xmlmodels.Library) (*PieceVM, erro } // DEBUG: Log piece details - fmt.Printf("DEBUG PieceView: Creating view for piece ID=%s\n", piece.Identifier.ID) - fmt.Printf("DEBUG PieceView: Piece has %d IssueRefs\n", len(piece.IssueRefs)) + logging.Debug(fmt.Sprintf("PieceView: Creating view for piece ID=%s", piece.Identifier.ID)) + logging.Debug(fmt.Sprintf("PieceView: Piece has %d IssueRefs", len(piece.IssueRefs))) for i, ref := range piece.IssueRefs { - fmt.Printf("DEBUG PieceView: IssueRef[%d]: Year=%d, Nr=%d, Von=%d, Bis=%d, Beilage=%d\n", i, ref.When.Year, ref.Nr, ref.Von, ref.Bis, ref.Beilage) + logging.Debug(fmt.Sprintf("PieceView: IssueRef[%d]: Year=%d, Nr=%d, Von=%d, Bis=%d, Beilage=%d", i, ref.When.Year, ref.Nr, ref.Von, ref.Bis, ref.Beilage)) } // Extract title from piece @@ -98,8 +100,8 @@ func NewPieceView(piece xmlmodels.Piece, lib *xmlmodels.Library) (*PieceVM, erro PartNumber: partIndex + 1, // Part number (1-based) } - // 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) + // Get actual image path, preview path, and JPEG path from provider + pageEntry.ImagePath, pageEntry.PreviewPath, pageEntry.JpegPath = getImagePathsFromProvider(pics, issueRef.When.Year, issueRef.Nr, pageNum, issueRef.Beilage > 0) pvm.AllPages = append(pvm.AllPages, pageEntry) } @@ -108,11 +110,11 @@ func NewPieceView(piece xmlmodels.Piece, lib *xmlmodels.Library) (*PieceVM, erro pvm.TotalPageCount = len(pvm.AllPages) // DEBUG: Log final counts - fmt.Printf("DEBUG PieceView: Final counts - %d issue contexts, %d total pages\n", len(pvm.IssueContexts), pvm.TotalPageCount) - fmt.Printf("DEBUG PieceView: Issue contexts: %v\n", pvm.IssueContexts) + logging.Debug(fmt.Sprintf("PieceView: Final counts - %d issue contexts, %d total pages", len(pvm.IssueContexts), pvm.TotalPageCount)) + logging.Debug(fmt.Sprintf("PieceView: Issue contexts: %v", pvm.IssueContexts)) // Load images and update availability - if err := pvm.loadImages(); err != nil { + if err := pvm.loadImages(pics); err != nil { return nil, fmt.Errorf("failed to load images: %w", err) } @@ -130,12 +132,7 @@ func NewPieceView(piece xmlmodels.Piece, lib *xmlmodels.Library) (*PieceVM, erro } // loadImages loads and validates all page images for the piece -func (pvm *PieceVM) loadImages() error { - // Initialize image registry if needed - if err := initImageRegistry(); err != nil { - return err - } - +func (pvm *PieceVM) loadImages(pics *pictures.PicturesProvider) error { issuePages := []IssuePage{} hasAnyImages := false @@ -150,15 +147,12 @@ func (pvm *PieceVM) loadImages() error { PageIcon: "single", // Simplified icon for piece view } - // Check if image actually exists using the registry - key := fmt.Sprintf("%d-%d", pageEntry.IssueYear, pageEntry.PageNumber) - if imageRegistry != nil { - if _, exists := imageRegistry.ByYearPage[key]; exists { - hasAnyImages = true - } else { - issuePage.Available = false - pvm.AllPages[i].Available = false - } + // Check if image actually exists using the provider + if _, exists := pics.GetByYearPage(pageEntry.IssueYear, pageEntry.PageNumber); exists { + hasAnyImages = true + } else { + issuePage.Available = false + pvm.AllPages[i].Available = false } issuePages = append(issuePages, issuePage) @@ -218,110 +212,37 @@ func (pvm *PieceVM) createContinuousPages(lib *xmlmodels.Library) error { return nil } -// getImagePathFromRegistry gets the actual image path from the image registry -func getImagePathFromRegistry(year, page int) string { - // Initialize registry if needed - if err := initImageRegistry(); err != nil { - return "" - } - - // Look up the image by year and page - key := fmt.Sprintf("%d-%d", year, page) - if imageFile, exists := imageRegistry.ByYearPage[key]; exists { - return imageFile.Path - } - - // Fallback: generate a default path (though this probably won't exist) - 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 { +// getImagePathsFromProvider gets image paths: primary (WebP preferred), preview (compressed), and JPEG, handling both regular and Beilage pages +func getImagePathsFromProvider(pics *pictures.PicturesProvider, year, issue, page int, isBeilage bool) (string, string, string) { + // Get image file from provider + imageFile, exists := pics.GetByYearIssuePage(year, issue, page, isBeilage) + if !exists { + // Fallback for regular pages + if !isBeilage { + fallbackPath := fmt.Sprintf("/static/pictures/%d/seite_%d.jpg", year, page) + return fallbackPath, fallbackPath, fallbackPath + } + // No file found for beilage 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 + // Return paths from found image file + previewPath := imageFile.PreviewPath + if previewPath == "" { + previewPath = imageFile.Path // Fallback to original if no preview } - - // 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 - } - } + jpegPath := imageFile.JpegPath + if jpegPath == "" { + jpegPath = imageFile.Path // Fallback to primary if no separate JPEG } - - // 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 { - return "" - } - - // For regular pages, use the old method - if !isBeilage { - key := fmt.Sprintf("%d-%d", year, page) - if imageFile, exists := imageRegistry.ByYearPage[key]; exists { - return imageFile.Path - } - // Fallback for regular pages - return fmt.Sprintf("/static/pictures/%d/seite_%d.jpg", year, page) - } - - // 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 { - fmt.Printf("DEBUG: Found Beilage image for year=%d, issue=%d, page=%d: %s\n", year, issue, page, file.Path) - return file.Path - } - } - } - - // Fallback for Beilage pages - fmt.Printf("DEBUG: No Beilage image found for year=%d, issue=%d, page=%d\n", year, issue, page) - return fmt.Sprintf("/static/pictures/%d/%db-beilage-seite_%d.jpg", year, issue, page) + return imageFile.Path, previewPath, jpegPath } // populateOtherPieces finds and populates other pieces that appear on the same pages as this piece func (pvm *PieceVM) populateOtherPieces(lib *xmlmodels.Library) error { - fmt.Printf("DEBUG: Starting populateOtherPieces for piece %s\n", pvm.Piece.Identifier.ID) + logging.Debug(fmt.Sprintf("Starting populateOtherPieces for piece %s", pvm.Piece.Identifier.ID)) for i, pageEntry := range pvm.AllPages { - fmt.Printf("DEBUG: Processing page %d from issue %d/%d\n", pageEntry.PageNumber, pageEntry.IssueYear, pageEntry.IssueNumber) + logging.Debug(fmt.Sprintf("Processing page %d from issue %d/%d", pageEntry.PageNumber, pageEntry.IssueYear, pageEntry.IssueNumber)) // Find the issue this page belongs to var issue *xmlmodels.Issue @@ -335,41 +256,41 @@ func (pvm *PieceVM) populateOtherPieces(lib *xmlmodels.Library) error { lib.Issues.Unlock() if issue == nil { - fmt.Printf("DEBUG: Issue not found for %d/%d\n", pageEntry.IssueYear, pageEntry.IssueNumber) + logging.Debug(fmt.Sprintf("Issue not found for %d/%d", pageEntry.IssueYear, pageEntry.IssueNumber)) continue } // Get all pieces for this issue using the same approach as the ausgabe view piecesForIssue, _, err := PiecesForIssue(lib, *issue) if err != nil { - fmt.Printf("DEBUG: Error getting pieces for issue: %v\n", err) + logging.Error(err, fmt.Sprintf("Error getting pieces for issue %d/%d", pageEntry.IssueYear, pageEntry.IssueNumber)) continue } - fmt.Printf("DEBUG: Found %d total pieces for issue %d/%d\n", len(piecesForIssue.Pages), pageEntry.IssueYear, pageEntry.IssueNumber) - fmt.Printf("DEBUG: PiecesForIssue.Pages = %v\n", piecesForIssue.Pages) + logging.Debug(fmt.Sprintf("Found %d total pieces for issue %d/%d", len(piecesForIssue.Pages), pageEntry.IssueYear, pageEntry.IssueNumber)) + logging.Debug(fmt.Sprintf("PiecesForIssue.Pages = %v", piecesForIssue.Pages)) // Create IndividualPiecesByPage using the same function as ausgabe view individualPieces := CreateIndividualPagesWithMetadata(piecesForIssue, lib) - fmt.Printf("DEBUG: CreateIndividualPagesWithMetadata created %d pages with pieces\n", len(individualPieces.Pages)) - fmt.Printf("DEBUG: Pages with pieces: %v\n", individualPieces.Pages) + logging.Debug(fmt.Sprintf("CreateIndividualPagesWithMetadata created %d pages with pieces", len(individualPieces.Pages))) + logging.Debug(fmt.Sprintf("Pages with pieces: %v", individualPieces.Pages)) // DEBUG: Show what pages are available in the map if pageEntry.PageNumber == 113 { - fmt.Printf("DEBUG: Available pages in individualPieces.Items: %v\n", individualPieces.Pages) + logging.Debug(fmt.Sprintf("Available pages in individualPieces.Items: %v", individualPieces.Pages)) for pageNum := range individualPieces.Items { - fmt.Printf("DEBUG: Page %d has %d pieces\n", pageNum, len(individualPieces.Items[pageNum])) + logging.Debug(fmt.Sprintf("Page %d has %d pieces", pageNum, len(individualPieces.Items[pageNum]))) } } // Get pieces that appear on this specific page if individualPiecesOnPage, exists := individualPieces.Items[pageEntry.PageNumber]; exists { - fmt.Printf("DEBUG: Found %d pieces on page %d\n", len(individualPiecesOnPage), pageEntry.PageNumber) + logging.Debug(fmt.Sprintf("Found %d pieces on page %d", len(individualPiecesOnPage), pageEntry.PageNumber)) otherPieces := []IndividualPieceByIssue{} for _, individualPiece := range individualPiecesOnPage { // Skip the current piece itself using comprehensive comparison if IsSamePiece(individualPiece.PieceByIssue.Piece, pvm.Piece) { - fmt.Printf("DEBUG: Skipping current piece (comprehensive field match)\n") + logging.Debug("Skipping current piece (comprehensive field match)") continue } @@ -381,14 +302,14 @@ func (pvm *PieceVM) populateOtherPieces(lib *xmlmodels.Library) error { pieceTitle = individualPiece.PieceByIssue.Piece.Incipit[0] } - fmt.Printf("DEBUG: Adding other piece title='%s'\n", pieceTitle) + logging.Debug(fmt.Sprintf("Adding other piece title='%s'", pieceTitle)) otherPieces = append(otherPieces, individualPiece) } - fmt.Printf("DEBUG: Found %d other pieces on page %d\n", len(otherPieces), pageEntry.PageNumber) + logging.Debug(fmt.Sprintf("Found %d other pieces on page %d", len(otherPieces), pageEntry.PageNumber)) pvm.AllPages[i].OtherPieces = otherPieces } else { - fmt.Printf("DEBUG: No pieces found on page %d\n", pageEntry.PageNumber) + logging.Debug(fmt.Sprintf("No pieces found on page %d", pageEntry.PageNumber)) } }