Initial commit for AI Media Hub
Some checks failed
Build and Push Docker Image / build-and-push (push) Has been cancelled

This commit is contained in:
AI Assistant
2026-03-12 14:13:05 +09:00
parent b9940fa4d2
commit d030e737cb
17 changed files with 1051 additions and 0 deletions

137
backend/handlers/api.go Normal file
View File

@@ -0,0 +1,137 @@
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...)
output, err := cmd.CombinedOutput()
if err != nil {
fmt.Println("Download error:", 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

47
backend/handlers/ws.go Normal file
View File

@@ -0,0 +1,47 @@
package handlers
import (
"log"
"sync"
"github.com/gofiber/websocket/v2"
)
var clients = make(map[*websocket.Conn]bool)
var clientsMutex sync.Mutex
func WsHandler(c *websocket.Conn) {
clientsMutex.Lock()
clients[c] = true
clientsMutex.Unlock()
defer func() {
clientsMutex.Lock()
delete(clients, c)
clientsMutex.Unlock()
c.Close()
}()
for {
// Just keep alive, ignore incoming messages
_, _, err := c.ReadMessage()
if err != nil {
log.Println("ws error:", err)
break
}
}
}
func BroadcastProgress(message string) {
clientsMutex.Lock()
defer clientsMutex.Unlock()
for client := range clients {
err := client.WriteMessage(websocket.TextMessage, []byte(message))
if err != nil {
log.Println("ws broadcast error:", err)
client.Close()
delete(clients, client)
}
}
}