diff --git a/internal/api/handlers/discover.go b/internal/api/handlers/discover.go index 68a1470..073791c 100644 --- a/internal/api/handlers/discover.go +++ b/internal/api/handlers/discover.go @@ -103,7 +103,7 @@ func (h *DiscoverHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { result, err := h.scanner.Scan(r.Context(), req, streamWriter) if err != nil { h.logger.Error("discovery failed", err) - streamWriter.SendError(err) + _ = streamWriter.SendError(err) return } @@ -126,5 +126,5 @@ func (h *DiscoverHandler) sendErrorResponse(w http.ResponseWriter, message strin "code": statusCode, } - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) } \ No newline at end of file diff --git a/internal/api/handlers/search.go b/internal/api/handlers/search.go index 100d2bc..fec1288 100644 --- a/internal/api/handlers/search.go +++ b/internal/api/handlers/search.go @@ -95,5 +95,5 @@ func (h *SearchHandler) sendErrorResponse(w http.ResponseWriter, message string, "code": statusCode, } - json.NewEncoder(w).Encode(response) + _ = json.NewEncoder(w).Encode(response) } \ No newline at end of file diff --git a/internal/camera/database/loader.go b/internal/camera/database/loader.go index 0bed389..1a452f1 100644 --- a/internal/camera/database/loader.go +++ b/internal/camera/database/loader.go @@ -52,7 +52,7 @@ func (l *Loader) LoadBrand(brandID string) (*models.Camera, error) { } return nil, fmt.Errorf("failed to open brand file: %w", err) } - defer file.Close() + defer func() { _ = file.Close() }() var camera models.Camera decoder := json.NewDecoder(file) @@ -104,7 +104,7 @@ func (l *Loader) LoadPopularPatterns() ([]models.StreamPattern, error) { if err != nil { return nil, fmt.Errorf("failed to open patterns file: %w", err) } - defer file.Close() + defer func() { _ = file.Close() }() var patterns []models.StreamPattern decoder := json.NewDecoder(file) @@ -133,7 +133,7 @@ func (l *Loader) LoadQueryParameters() ([]string, error) { if err != nil { return nil, fmt.Errorf("failed to open parameters file: %w", err) } - defer file.Close() + defer func() { _ = file.Close() }() var params []string decoder := json.NewDecoder(file) @@ -187,7 +187,7 @@ func (l *Loader) loadCameraFromFile(filePath string) (*models.Camera, error) { if err != nil { return nil, err } - defer file.Close() + defer func() { _ = file.Close() }() var camera models.Camera decoder := json.NewDecoder(file) diff --git a/internal/camera/database/search.go b/internal/camera/database/search.go index 6527852..03a62d9 100644 --- a/internal/camera/database/search.go +++ b/internal/camera/database/search.go @@ -15,7 +15,6 @@ import ( type SearchEngine struct { loader *Loader logger interface{ Debug(string, ...any); Error(string, error, ...any) } - mu sync.RWMutex } // NewSearchEngine creates a new search engine diff --git a/internal/camera/discovery/onvif_simple.go b/internal/camera/discovery/onvif_simple.go index d14a6d7..8e6ec3e 100644 --- a/internal/camera/discovery/onvif_simple.go +++ b/internal/camera/discovery/onvif_simple.go @@ -198,7 +198,7 @@ func (o *ONVIFDiscovery) getProfileStreams(ctx context.Context, dev *onvif.Devic "elapsed", elapsed.String()) return streams } - defer profilesResp.Body.Close() + defer func() { _ = profilesResp.Body.Close() }() o.logger.Debug("✅ GetProfiles call successful", "elapsed", elapsed.String(), @@ -311,7 +311,7 @@ func (o *ONVIFDiscovery) getStreamURI(dev *onvif.Device, profileToken string) st "elapsed", elapsed.String()) return "" } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() o.logger.Debug("✅ GetStreamUri call successful", "profile", profileToken, diff --git a/internal/camera/discovery/scanner.go b/internal/camera/discovery/scanner.go index 0c47da9..1360694 100644 --- a/internal/camera/discovery/scanner.go +++ b/internal/camera/discovery/scanner.go @@ -92,7 +92,7 @@ func (s *Scanner) Scan(ctx context.Context, req models.StreamDiscoveryRequest, s ) // Send initial message - streamWriter.SendJSON("scan_started", map[string]interface{}{ + _ = streamWriter.SendJSON("scan_started", map[string]interface{}{ "target": req.Target, "model": req.Model, "max_streams": req.MaxStreams, @@ -108,7 +108,7 @@ func (s *Scanner) Scan(ctx context.Context, req models.StreamDiscoveryRequest, s ip := s.extractIP(req.Target) if ip == "" { err := fmt.Errorf("invalid target IP: %s", req.Target) - streamWriter.SendError(err) + _ = streamWriter.SendError(err) result.Error = err return result, err } @@ -116,7 +116,7 @@ func (s *Scanner) Scan(ctx context.Context, req models.StreamDiscoveryRequest, s // Collect all streams to test (includes metadata like type) streams, err := s.collectStreams(scanCtx, req, ip) if err != nil { - streamWriter.SendError(err) + _ = streamWriter.SendError(err) result.Error = err return result, err } @@ -124,7 +124,7 @@ func (s *Scanner) Scan(ctx context.Context, req models.StreamDiscoveryRequest, s s.logger.Info("collected streams for testing", "count", len(streams)) // Send progress update - streamWriter.SendJSON("progress", models.ProgressMessage{ + _ = streamWriter.SendJSON("progress", models.ProgressMessage{ Tested: 0, Found: 0, Remaining: len(streams), @@ -137,14 +137,14 @@ func (s *Scanner) Scan(ctx context.Context, req models.StreamDiscoveryRequest, s result.Duration = time.Since(startTime) // Send completion message - streamWriter.SendJSON("complete", models.CompleteMessage{ + _ = streamWriter.SendJSON("complete", models.CompleteMessage{ TotalTested: result.TotalTested, TotalFound: result.TotalFound, Duration: result.Duration.Seconds(), }) // Send final done event to signal proper stream closure - streamWriter.SendJSON("done", map[string]interface{}{ + _ = streamWriter.SendJSON("done", map[string]interface{}{ "message": "Stream discovery finished", }) @@ -196,11 +196,11 @@ func (s *Scanner) scanDirectStream(ctx context.Context, req models.StreamDiscove result.Streams = append(result.Streams, discoveredStream) // Send to SSE - streamWriter.SendJSON("stream_found", map[string]interface{}{ + _ = streamWriter.SendJSON("stream_found", map[string]interface{}{ "stream": discoveredStream, }) } else { - streamWriter.SendJSON("stream_failed", map[string]interface{}{ + _ = streamWriter.SendJSON("stream_failed", map[string]interface{}{ "url": req.Target, "error": testResult.Error, }) @@ -422,7 +422,7 @@ func (s *Scanner) testStreamsConcurrently(ctx context.Context, streams []models. currentTested := atomic.LoadInt32(&tested) // Only send if there's been progress if currentTested != lastTested { - streamWriter.SendJSON("progress", models.ProgressMessage{ + _ = streamWriter.SendJSON("progress", models.ProgressMessage{ Tested: int(currentTested), Found: int(atomic.LoadInt32(&found)), Remaining: len(streams) - int(currentTested), @@ -439,12 +439,12 @@ func (s *Scanner) testStreamsConcurrently(ctx context.Context, streams []models. result.Streams = append(result.Streams, stream) // Send to SSE - streamWriter.SendJSON("stream_found", map[string]interface{}{ + _ = streamWriter.SendJSON("stream_found", map[string]interface{}{ "stream": stream, }) // Send progress (immediate update when stream is found) - streamWriter.SendJSON("progress", models.ProgressMessage{ + _ = streamWriter.SendJSON("progress", models.ProgressMessage{ Tested: int(atomic.LoadInt32(&tested)), Found: int(atomic.LoadInt32(&found)), Remaining: len(streams) - int(atomic.LoadInt32(&tested)), @@ -458,12 +458,13 @@ func (s *Scanner) testStreamsConcurrently(ctx context.Context, streams []models. }() // Test each stream +TestLoop: for _, streamToTest := range streams { // Check if context is done or max streams reached select { case <-ctx.Done(): s.logger.Debug("scan cancelled or timeout") - break + break TestLoop default: } diff --git a/internal/camera/stream/tester.go b/internal/camera/stream/tester.go index f938fda..49347f3 100644 --- a/internal/camera/stream/tester.go +++ b/internal/camera/stream/tester.go @@ -278,7 +278,8 @@ func (t *Tester) testRTSP(ctx context.Context, streamURL string, result *TestRes result.Type = "FFMPEG" for _, stream := range probeResult.Streams { - if stream.CodecType == "video" { + switch stream.CodecType { + case "video": result.Codec = stream.CodecName result.Resolution = fmt.Sprintf("%dx%d", stream.Width, stream.Height) @@ -288,26 +289,26 @@ func (t *Tester) testRTSP(ctx context.Context, streamURL string, result *TestRes if len(parts) == 2 { // Calculate FPS from fraction var num, den int - fmt.Sscanf(parts[0], "%d", &num) - fmt.Sscanf(parts[1], "%d", &den) - if den > 0 { - result.FPS = num / den + if n, _ := fmt.Sscanf(parts[0], "%d", &num); n == 1 { + if n, _ := fmt.Sscanf(parts[1], "%d", &den); n == 1 && den > 0 { + result.FPS = num / den + } } } } // Parse bitrate if stream.BitRate != "" { - fmt.Sscanf(stream.BitRate, "%d", &result.Bitrate) + _, _ = fmt.Sscanf(stream.BitRate, "%d", &result.Bitrate) } - } else if stream.CodecType == "audio" { + case "audio": result.HasAudio = true } } // Use format bitrate if stream bitrate not available if result.Bitrate == 0 && probeResult.Format.BitRate != "" { - fmt.Sscanf(probeResult.Format.BitRate, "%d", &result.Bitrate) + _, _ = fmt.Sscanf(probeResult.Format.BitRate, "%d", &result.Bitrate) } if !result.Working { @@ -348,7 +349,7 @@ func (t *Tester) testHTTP(ctx context.Context, streamURL string, result *TestRes result.Error = fmt.Sprintf("HTTP request failed: %v", err) return } - defer resp.Body.Close() + defer func() { _ = resp.Body.Close() }() // Check status code if resp.StatusCode != http.StatusOK {