package handlers import ( "encoding/json" "fmt" "os" "os/exec" "path/filepath" "github.com/gofiber/fiber/v2" "github.com/savethenurse/ai-media-hub/backend/models" "github.com/savethenurse/ai-media-hub/backend/services" ) // SearchAndFilter handles Zone A logic func SearchAndFilter(c *fiber.Ctx) error { query := c.Query("q") if query == "" { return c.Status(400).JSON(fiber.Map{"error": "Query required"}) } // 1. Send search query to CSE urls, err := services.PerformSearch(query) if err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } if len(urls) == 0 { return c.JSON(fiber.Map{"recommended": []string{}}) } // 2. Filter with Gemini result, err := services.FilterImagesWithGemini(query, urls) if err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } return c.JSON(result) } // UploadMedia handles Zone B logic func UploadMedia(c *fiber.Ctx) error { file, err := c.FormFile("file") if err != nil { return c.Status(400).JSON(fiber.Map{"error": "File upload required"}) } downloadsDir := os.Getenv("DOWNLOADS_DIR") if downloadsDir == "" { downloadsDir = "/app/downloads" } dest := filepath.Join(downloadsDir, file.Filename) if err := c.SaveFile(file, dest); err != nil { return c.Status(500).JSON(fiber.Map{"error": err.Error()}) } // Logging to DB models.DB.Create(&models.MediaHistory{ SourceURL: file.Filename, // Just to log it, though not a URL FilePath: dest, Status: "success", Type: "upload", }) return c.JSON(fiber.Map{"status": "success", "filename": file.Filename}) } // DownloadMedia handles Zone C logic func DownloadMedia(c *fiber.Ctx) error { type Request struct { URL string `json:"url"` Start string `json:"start"` End string `json:"end"` } var req Request if err := c.BodyParser(&req); err != nil { return c.Status(400).JSON(fiber.Map{"error": "Invalid JSON"}) } // Check duplicates var hist models.MediaHistory res := models.DB.Where("source_url = ?", req.URL).First(&hist) if res.RowsAffected > 0 && c.Query("confirm") != "true" { return c.Status(statusConflict).JSON(fiber.Map{"error": "Duplicate", "need_confirm": true}) } downloadsDir := os.Getenv("DOWNLOADS_DIR") if downloadsDir == "" { downloadsDir = "/app/downloads" } // Execute Python worker asynchronously or synchronously // For simplicity, we'll do it synchronously and broadcast progress via WS if we capture it. // But it's easier to just run the command and wait. BroadcastProgress("Downloading: " + req.URL) go func() { args := []string{"./worker/downloader.py", "--url", req.URL, "--outdir", downloadsDir} if req.Start != "" { args = append(args, "--start", req.Start) } if req.End != "" { args = append(args, "--end", req.End) } cmd := exec.Command("python", args...) fmt.Printf("[DEBUG Go Exec] Command: %v\n", cmd.String()) output, err := cmd.CombinedOutput() fmt.Printf("[DEBUG Go Exec] Output:\n%s\n", string(output)) if err != nil { fmt.Printf("[DEBUG Go Exec] Download error: %v\n", string(output)) BroadcastProgress("Error: " + err.Error()) models.DB.Create(&models.MediaHistory{ SourceURL: req.URL, Status: "error", Type: "download", }) return } var result map[string]interface{} json.Unmarshal(output, &result) BroadcastProgress(fmt.Sprintf("Download Success: %v", req.URL)) models.DB.Create(&models.MediaHistory{ SourceURL: req.URL, Status: "success", Type: "download", FilePath: fmt.Sprintf("%v", result["filepath"]), }) }() return c.JSON(fiber.Map{"status": "started", "message": "Download process started"}) } const statusConflict = 409