Files
ai-media-hub/backend/main.go
T
2026-03-24 16:02:49 +09:00

137 lines
4.0 KiB
Go

package main
import (
"log"
"net/http"
"os"
"path/filepath"
"strconv"
"strings"
"ai-media-hub/backend/handlers"
"ai-media-hub/backend/models"
"ai-media-hub/backend/services"
"github.com/gin-gonic/gin"
)
func main() {
root := envOrDefault("APP_ROOT", "/app")
dbPath := envOrDefault("SQLITE_PATH", filepath.Join(root, "db", "media.db"))
downloadsDir := envOrDefault("DOWNLOADS_DIR", filepath.Join(root, "downloads"))
giphyDownloadDir := envOrDefault("GIPHY_DOWNLOAD_DIR", filepath.Join(downloadsDir, "giphy"))
frontendDir := envOrDefault("FRONTEND_DIR", filepath.Join(root, "frontend"))
workerScript := envOrDefault("WORKER_SCRIPT", filepath.Join(root, "worker", "downloader.py"))
geminiAPIKey := os.Getenv("GEMINI_API_KEY")
geminiModel := envOrDefault("GEMINI_MODEL", "gemini-2.5-flash")
giphyEnabled := envBoolOrDefault("GIPHY_ENABLED", true)
giphyAPIKey := os.Getenv("GIPHY_API_KEY")
giphyMaxResults := envIntOrDefault("GIPHY_MAX_RESULTS", 100)
giphyRating := envOrDefault("GIPHY_RATING", "g")
giphyLang := envOrDefault("GIPHY_LANG", "en")
db, err := models.InitDB(dbPath)
if err != nil {
log.Fatal(err)
}
defer db.Close()
if err := handlers.EnsurePaths(downloadsDir, workerScript); err != nil {
log.Fatal(err)
}
if geminiAPIKey == "" {
log.Printf("warning: GEMINI_API_KEY is not configured; query expansion will use fallback behavior")
}
if giphyEnabled && strings.TrimSpace(giphyAPIKey) == "" {
log.Fatal("GIPHY_ENABLED is true but GIPHY_API_KEY is not configured")
}
if err := os.MkdirAll(giphyDownloadDir, 0o755); err != nil {
log.Fatal(err)
}
geminiService := services.NewGeminiService(geminiAPIKey, geminiModel)
giphyService := services.NewGiphyService(services.GiphyConfig{
Enabled: giphyEnabled,
APIKey: giphyAPIKey,
MaxResults: giphyMaxResults,
Rating: giphyRating,
Lang: giphyLang,
DownloadDir: giphyDownloadDir,
}, geminiService)
app := &handlers.App{
DB: db,
DownloadsDir: downloadsDir,
PreviewCacheDir: filepath.Join(downloadsDir, ".preview-cache"),
WorkerScript: workerScript,
SearchService: services.NewSearchService(
os.Getenv("SEARXNG_BASE_URL"),
os.Getenv("SEARXNG_GOOGLE_VIDEO_ENGINE"),
os.Getenv("SEARXNG_WEB_ENGINE"),
),
GeminiService: geminiService,
GiphyService: giphyService,
Hub: handlers.NewHub(),
}
app.SearchService.Debug = func(message string, data any) {
app.Hub.Broadcast("debug", gin.H{"message": message, "data": data})
}
app.GeminiService.Debug = func(message string, data any) {
app.Hub.Broadcast("debug", gin.H{"message": message, "data": data})
}
app.GiphyService.Debug = func(message string, data any) {
app.Hub.Broadcast("debug", gin.H{"message": message, "data": data})
}
router := gin.Default()
handlers.RegisterRoutes(router, app)
router.StaticFile("/", filepath.Join(frontendDir, "index.html"))
router.StaticFile("/app.js", filepath.Join(frontendDir, "app.js"))
router.StaticFile("/style.css", filepath.Join(frontendDir, "style.css"))
router.NoRoute(func(c *gin.Context) {
c.File(filepath.Join(frontendDir, "index.html"))
})
router.NoMethod(func(c *gin.Context) {
c.JSON(http.StatusMethodNotAllowed, gin.H{"error": "method not allowed"})
})
addr := envOrDefault("APP_ADDR", ":8080")
log.Printf("server listening on %s", addr)
if err := router.Run(addr); err != nil {
log.Fatal(err)
}
}
func envOrDefault(key, fallback string) string {
if value := os.Getenv(key); value != "" {
return value
}
return fallback
}
func envBoolOrDefault(key string, fallback bool) bool {
value := strings.TrimSpace(strings.ToLower(os.Getenv(key)))
switch value {
case "1", "true", "yes", "on":
return true
case "0", "false", "no", "off":
return false
case "":
return fallback
default:
return fallback
}
}
func envIntOrDefault(key string, fallback int) int {
value := strings.TrimSpace(os.Getenv(key))
if value == "" {
return fallback
}
parsed, err := strconv.Atoi(value)
if err != nil {
return fallback
}
return parsed
}