From 8ba3c50768d5582aaed6e741ecae66676c1015e3 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 23 Apr 2026 14:32:22 +0000 Subject: [PATCH] feat(import): accept gzipped .nzb.gz files in uploads and scanners Upload endpoints, the directory scanner, and the watcher only matched the bare .nzb extension, so gzipped NZBs were rejected even though the downstream openNzbFile helper already decompresses them transparently. Centralize the extension check in nzbtrim.HasNzbExtension and apply it across the HTTP handlers, scanner, watcher, and frontend upload input. --- frontend/src/components/queue/ImportMethods.tsx | 6 ++++-- internal/api/nzb_stremio_handlers.go | 4 ++-- internal/api/queue_handlers.go | 5 +++-- internal/api/sabnzbd_handlers.go | 9 +++++---- internal/importer/scanner/directory.go | 5 +++-- internal/importer/scanner/watcher.go | 3 ++- internal/importer/utils/nzbtrim/nzb_trim.go | 6 ++++++ 7 files changed, 25 insertions(+), 13 deletions(-) diff --git a/frontend/src/components/queue/ImportMethods.tsx b/frontend/src/components/queue/ImportMethods.tsx index 8c935ce12..225b94ce5 100644 --- a/frontend/src/components/queue/ImportMethods.tsx +++ b/frontend/src/components/queue/ImportMethods.tsx @@ -180,7 +180,9 @@ function EnhancedUploadSection() { const categories = config?.sabnzbd?.categories ?? []; const validateFile = useCallback((file: File): string | null => { - if (!file.name.toLowerCase().endsWith(".nzb")) return "Only .nzb files are allowed"; + const name = file.name.toLowerCase(); + if (!name.endsWith(".nzb") && !name.endsWith(".nzb.gz")) + return "Only .nzb or .nzb.gz files are allowed"; if (file.size > 100 * 1024 * 1024) return "File size must be less than 100MB"; return null; }, []); @@ -482,7 +484,7 @@ function EnhancedUploadSection() { diff --git a/internal/api/nzb_stremio_handlers.go b/internal/api/nzb_stremio_handlers.go index 78f3c4d88..ebfc890b7 100644 --- a/internal/api/nzb_stremio_handlers.go +++ b/internal/api/nzb_stremio_handlers.go @@ -97,8 +97,8 @@ func (s *Server) handleNzbStreams(c *fiber.Ctx) error { return RespondBadRequest(c, "No file provided", "A .nzb file must be uploaded") } - if !strings.HasSuffix(strings.ToLower(file.Filename), ".nzb") { - return RespondValidationError(c, "Invalid file type", "Only .nzb files are allowed") + if !nzbtrim.HasNzbExtension(file.Filename) { + return RespondValidationError(c, "Invalid file type", "Only .nzb or .nzb.gz files are allowed") } const maxUploadSize = 100 * 1024 * 1024 // 100 MB diff --git a/internal/api/queue_handlers.go b/internal/api/queue_handlers.go index a979b5188..8c9562fab 100644 --- a/internal/api/queue_handlers.go +++ b/internal/api/queue_handlers.go @@ -18,6 +18,7 @@ import ( "github.com/gofiber/fiber/v2" "github.com/javi11/altmount/internal/database" internalerrors "github.com/javi11/altmount/internal/errors" + "github.com/javi11/altmount/internal/importer/utils/nzbtrim" "github.com/javi11/altmount/internal/nzblnk" ) @@ -576,8 +577,8 @@ func (s *Server) handleUploadToQueue(c *fiber.Ctx) error { } // Validate file extension - if !strings.HasSuffix(strings.ToLower(file.Filename), ".nzb") { - return RespondValidationError(c, "Invalid file type", "Only .nzb files are allowed") + if !nzbtrim.HasNzbExtension(file.Filename) { + return RespondValidationError(c, "Invalid file type", "Only .nzb or .nzb.gz files are allowed") } // Validate file size (100MB limit) diff --git a/internal/api/sabnzbd_handlers.go b/internal/api/sabnzbd_handlers.go index c92afd1cc..d95329b10 100644 --- a/internal/api/sabnzbd_handlers.go +++ b/internal/api/sabnzbd_handlers.go @@ -22,6 +22,7 @@ import ( "github.com/javi11/altmount/internal/database" "github.com/javi11/altmount/internal/httpclient" "github.com/javi11/altmount/internal/importer/utils" + "github.com/javi11/altmount/internal/importer/utils/nzbtrim" "github.com/javi11/altmount/internal/pathutil" ) @@ -319,8 +320,8 @@ func (s *Server) handleSABnzbdAddFile(c *fiber.Ctx) error { } // Validate file extension - if !strings.HasSuffix(strings.ToLower(file.Filename), ".nzb") { - return s.writeSABnzbdErrorFiber(c, "Invalid file type, must be .nzb") + if !nzbtrim.HasNzbExtension(file.Filename) { + return s.writeSABnzbdErrorFiber(c, "Invalid file type, must be .nzb or .nzb.gz") } // Get and validate category from form first @@ -475,8 +476,8 @@ func (s *Server) handleSABnzbdAddUrl(c *fiber.Ctx) error { filename = "downloaded.nzb" } - // Ensure .nzb extension - if !strings.HasSuffix(strings.ToLower(filename), ".nzb") { + // Ensure .nzb or .nzb.gz extension + if !nzbtrim.HasNzbExtension(filename) { filename += ".nzb" } diff --git a/internal/importer/scanner/directory.go b/internal/importer/scanner/directory.go index 50b5a1e4f..a1cece14c 100644 --- a/internal/importer/scanner/directory.go +++ b/internal/importer/scanner/directory.go @@ -9,6 +9,8 @@ import ( "strings" "sync" "time" + + "github.com/javi11/altmount/internal/importer/utils/nzbtrim" ) // QueueAdder defines the interface for adding items to the queue @@ -176,8 +178,7 @@ func (d *DirectoryScanner) performScan(ctx context.Context, scanPath string) { d.info.FilesFound++ d.mu.Unlock() - ext := strings.ToLower(path) - if !strings.HasSuffix(ext, ".nzb") && !strings.HasSuffix(ext, ".strm") { + if !nzbtrim.HasNzbExtension(path) && !strings.HasSuffix(strings.ToLower(path), ".strm") { return nil } diff --git a/internal/importer/scanner/watcher.go b/internal/importer/scanner/watcher.go index b52c9c427..2397a5939 100644 --- a/internal/importer/scanner/watcher.go +++ b/internal/importer/scanner/watcher.go @@ -11,6 +11,7 @@ import ( "github.com/javi11/altmount/internal/config" "github.com/javi11/altmount/internal/database" + "github.com/javi11/altmount/internal/importer/utils/nzbtrim" ) // WatchQueueAdder interface for adding items to the import queue from directory watcher @@ -113,7 +114,7 @@ func (w *Watcher) scanDirectory(ctx context.Context, watchDir string) { } // Check extension - if !strings.HasSuffix(strings.ToLower(d.Name()), ".nzb") { + if !nzbtrim.HasNzbExtension(d.Name()) { return nil } diff --git a/internal/importer/utils/nzbtrim/nzb_trim.go b/internal/importer/utils/nzbtrim/nzb_trim.go index 24a11c249..891b404d3 100644 --- a/internal/importer/utils/nzbtrim/nzb_trim.go +++ b/internal/importer/utils/nzbtrim/nzb_trim.go @@ -17,3 +17,9 @@ func TrimNzbExtension(filename string) string { // Fallback to standard extension removal if it's not a known NZB extension return strings.TrimSuffix(filename, filepath.Ext(filename)) } + +// HasNzbExtension returns true when filename ends with .nzb or .nzb.gz (case-insensitive). +func HasNzbExtension(filename string) bool { + lower := strings.ToLower(filename) + return strings.HasSuffix(lower, ".nzb") || strings.HasSuffix(lower, ".nzb.gz") +}