diff --git a/.gitea/workflows/build-push.yaml b/.gitea/workflows/build-push.yaml
deleted file mode 100644
index 696d092..0000000
--- a/.gitea/workflows/build-push.yaml
+++ /dev/null
@@ -1,44 +0,0 @@
-name: Build and Push Docker Image
-
-on:
- push:
- branches:
- - main
-
-env:
- REGISTRY: git.savethenurse.com
- # CI/CD 파이프라인이 동작하는 Gitea 서버 자체의 레지스트리로 변경합니다.
- IMAGE_NAME: ${{ github.repository }}
-
-jobs:
- build-and-push:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
-
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v3
-
- - name: Log in to the Container registry
- uses: docker/login-action@v3
- with:
- registry: ${{ env.REGISTRY }}
- username: ${{ github.actor }}
- password: 18b8c375c76a07b9a6954bfdda794f5064dc6aa1
-
- - name: Extract metadata (tags, labels) for Docker
- id: meta
- uses: docker/metadata-action@v5
- with:
- images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
-
- - name: Build and push Docker image
- uses: docker/build-push-action@v5
- with:
- context: .
- push: true
- tags: ${{ steps.meta.outputs.tags }}
- labels: ${{ steps.meta.outputs.labels }}
- cache-from: type=gha
- cache-to: type=gha,mode=max
diff --git a/Dockerfile b/Dockerfile
deleted file mode 100644
index 3f43517..0000000
--- a/Dockerfile
+++ /dev/null
@@ -1,50 +0,0 @@
-# 1. Build Go Backend
-FROM golang:1.22-bookworm AS builder
-
-# Debian 기반 이미지는 빌드 도구가 이미 포함되어 있으므로 추가 설치 없이 진행합니다.
-
-WORKDIR /build
-
-# Cache go modules
-COPY backend/go.mod backend/go.sum ./
-RUN go mod download
-
-COPY backend/ ./
-# CGO_ENABLED=1 is required for sqlite3
-RUN CGO_ENABLED=1 go build -ldflags="-s -w" -o main main.go
-
-# 2. Final Minimal Image (Python + Go binary + Frontend)
-FROM python:3.10-slim
-
-# 파이썬 출력 버퍼링을 비활성화하여 도커 로그에 즉각 표시되도록 설정합니다.
-ENV PYTHONUNBUFFERED=1
-
-# Install system dependencies (ffmpeg is required for yt-dlp)
-RUN apt-get update && apt-get install -y \
- ffmpeg \
- ca-certificates \
- && rm -rf /var/lib/apt/lists/*
-
-WORKDIR /app
-
-# Install Python dependencies for worker
-COPY worker/requirements.txt ./worker/
-RUN pip install --no-cache-dir -r worker/requirements.txt
-
-# Copy Go binary from builder
-COPY --from=builder /build/main ./main
-
-# Copy worker script
-COPY worker/ ./worker/
-
-# Copy frontend
-COPY frontend/ ./frontend/
-
-# Ensure directories exist
-RUN mkdir -p db downloads
-
-# Expose Go server port
-EXPOSE 3000
-
-# Run the Go server command (which will also call the yt-dlp python script when needed)
-CMD ["./main"]
diff --git a/TODO.md b/TODO.md
deleted file mode 100644
index 4190ac4..0000000
--- a/TODO.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# 전체 작업 진행 점검표
-
-- [x] 1. 프로젝트 폴더 구조 생성
-- [x] 2. Dockerfile 및 Unraid XML 템플릿 작성
-- [x] 3. Gitea Actions CI/CD 파일 작성 (.gitea/workflows/build-push.yaml)
-- [x] 4. SQLite DB 모델 및 초기화 로직 (backend/models)
-- [x] 5. Python yt-dlp 워커 스크립트 작성 (worker/downloader.py)
-- [x] 6. Go 백엔드 라우팅 및 파일 업로드(Zone B) 구현
-- [x] 7. Go - Python 연동 로직 (Zone C 다운로드 실행)
-- [x] 8. Google CSE 및 Gemini 2.5 Flash 연동 로직 (Zone A)
-- [x] 9. WebSocket 서버 및 진행률 방송 로직
-- [x] 10. 프론트엔드 메인 UI 구성 (Tailwind 3-Zone Layout)
-- [x] 11. 프론트엔드 JS 통신 로직 및 상태 바 렌더링 연동
-- [x] 12. 전체 기능 통합 테스트
-- [x] 13. Git Init 및 자동 Push (Gitea)
diff --git a/backend/go.mod b/backend/go.mod
deleted file mode 100644
index b212384..0000000
--- a/backend/go.mod
+++ /dev/null
@@ -1,29 +0,0 @@
-module github.com/savethenurse/ai-media-hub/backend
-
-go 1.22.2
-
-require (
- github.com/gofiber/fiber/v2 v2.52.12
- github.com/gofiber/websocket/v2 v2.2.1
- gorm.io/driver/sqlite v1.5.5
- gorm.io/gorm v1.25.10
-)
-
-require (
- github.com/andybalholm/brotli v1.1.0 // indirect
- github.com/fasthttp/websocket v1.5.3 // indirect
- github.com/google/uuid v1.6.0 // indirect
- github.com/jinzhu/inflection v1.0.0 // indirect
- github.com/jinzhu/now v1.1.5 // indirect
- github.com/klauspost/compress v1.17.9 // indirect
- github.com/mattn/go-colorable v0.1.13 // indirect
- github.com/mattn/go-isatty v0.0.20 // indirect
- github.com/mattn/go-runewidth v0.0.16 // indirect
- github.com/mattn/go-sqlite3 v1.14.17 // indirect
- github.com/rivo/uniseg v0.2.0 // indirect
- github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect
- github.com/valyala/bytebufferpool v1.0.0 // indirect
- github.com/valyala/fasthttp v1.51.0 // indirect
- github.com/valyala/tcplisten v1.0.0 // indirect
- golang.org/x/sys v0.28.0 // indirect
-)
diff --git a/backend/go.sum b/backend/go.sum
deleted file mode 100644
index a78dc1b..0000000
--- a/backend/go.sum
+++ /dev/null
@@ -1,43 +0,0 @@
-github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
-github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
-github.com/fasthttp/websocket v1.5.3 h1:TPpQuLwJYfd4LJPXvHDYPMFWbLjsT91n3GpWtCQtdek=
-github.com/fasthttp/websocket v1.5.3/go.mod h1:46gg/UBmTU1kUaTcwQXpUxtRwG2PvIZYeA8oL6vF3Fs=
-github.com/gofiber/fiber/v2 v2.52.12 h1:0LdToKclcPOj8PktUdIKo9BUohjjwfnQl42Dhw8/WUw=
-github.com/gofiber/fiber/v2 v2.52.12/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
-github.com/gofiber/websocket/v2 v2.2.1 h1:C9cjxvloojayOp9AovmpQrk8VqvVnT8Oao3+IUygH7w=
-github.com/gofiber/websocket/v2 v2.2.1/go.mod h1:Ao/+nyNnX5u/hIFPuHl28a+NIkrqK7PRimyKaj4JxVU=
-github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
-github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
-github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
-github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
-github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
-github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
-github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
-github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
-github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
-github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
-github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
-github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
-github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
-github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
-github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk=
-github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g=
-github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
-github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA=
-github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
-github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
-github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
-golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-gorm.io/driver/sqlite v1.5.5 h1:7MDMtUZhV065SilG62E0MquljeArQZNfJnjd9i9gx3E=
-gorm.io/driver/sqlite v1.5.5/go.mod h1:6NgQ7sQWAIFsPrJJl1lSNSu2TABh0ZZ/zm5fosATavE=
-gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s=
-gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
diff --git a/backend/handlers/api.go b/backend/handlers/api.go
deleted file mode 100644
index 7f6b06f..0000000
--- a/backend/handlers/api.go
+++ /dev/null
@@ -1,140 +0,0 @@
-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
diff --git a/backend/handlers/ws.go b/backend/handlers/ws.go
deleted file mode 100644
index 77fc08f..0000000
--- a/backend/handlers/ws.go
+++ /dev/null
@@ -1,47 +0,0 @@
-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)
- }
- }
-}
diff --git a/backend/main.go b/backend/main.go
deleted file mode 100644
index 5243c13..0000000
--- a/backend/main.go
+++ /dev/null
@@ -1,59 +0,0 @@
-package main
-
-import (
- "log"
- "os"
-
- "github.com/gofiber/fiber/v2"
- "github.com/gofiber/fiber/v2/middleware/cors"
- "github.com/gofiber/fiber/v2/middleware/logger"
- "github.com/gofiber/websocket/v2"
- "github.com/savethenurse/ai-media-hub/backend/handlers"
- "github.com/savethenurse/ai-media-hub/backend/models"
-)
-
-func main() {
- dbDir := os.Getenv("DB_DIR")
- if dbDir == "" {
- dbDir = "/app/db"
- }
- dbPath := dbDir + "/media_hub.db"
-
- // Ensure DB directory exists
- os.MkdirAll(dbDir, os.ModePerm)
-
- // Initialize Database
- models.InitDB(dbPath)
-
- app := fiber.New()
-
- app.Use(logger.New())
- app.Use(cors.New())
-
- // Static files (Frontend)
- app.Static("/", "./frontend")
-
- // API Routes
- api := app.Group("/api")
- api.Get("/search", handlers.SearchAndFilter)
- api.Post("/upload", handlers.UploadMedia)
- api.Post("/download", handlers.DownloadMedia)
-
- // WebSocket Route
- app.Use("/ws", func(c *fiber.Ctx) error {
- if websocket.IsWebSocketUpgrade(c) {
- c.Locals("allowed", true)
- return c.Next()
- }
- return fiber.ErrUpgradeRequired
- })
- app.Get("/ws", websocket.New(handlers.WsHandler))
-
- port := os.Getenv("PORT")
- if port == "" {
- port = "3000"
- }
-
- log.Printf("Starting Server on port %s", port)
- app.Listen(":" + port)
-}
diff --git a/backend/models/db.go b/backend/models/db.go
deleted file mode 100644
index 5953080..0000000
--- a/backend/models/db.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package models
-
-import (
- "log"
- "os"
-
- "gorm.io/driver/sqlite"
- "gorm.io/gorm"
- "gorm.io/gorm/logger"
-)
-
-var DB *gorm.DB
-
-type MediaHistory struct {
- gorm.Model
- SourceURL string `gorm:"uniqueIndex"`
- FilePath string
- Status string
- Type string // ENUM: "download", "upload"
-}
-
-func InitDB(dbPath string) {
- var err error
-
- // 도커 로그 디버깅을 위해 SQL 쿼리를 디테일하게 출력하는 로거 설정
- newLogger := logger.New(
- log.New(os.Stdout, "\r\n", log.LstdFlags),
- logger.Config{
- LogLevel: logger.Info,
- Colorful: true,
- },
- )
-
- log.Println("Connecting to SQLite at:", dbPath)
- DB, err = gorm.Open(sqlite.Open(dbPath), &gorm.Config{
- Logger: newLogger,
- })
- if err != nil {
- log.Fatal("Failed to connect to database:", err)
- }
-
- // SQLite 동시성 최적화를 위해 WAL(Write-Ahead Logging) 모드를 활성화
- DB.Exec("PRAGMA journal_mode=WAL;")
-
- err = DB.AutoMigrate(&MediaHistory{})
- if err != nil {
- log.Println("Database migration error:", err)
- }
- log.Println("Database initialized and migrated.")
-}
diff --git a/backend/services/ai.go b/backend/services/ai.go
deleted file mode 100644
index cd81789..0000000
--- a/backend/services/ai.go
+++ /dev/null
@@ -1,94 +0,0 @@
-package services
-
-import (
- "bytes"
- "encoding/json"
- "fmt"
- "io/ioutil"
- "net/http"
- "os"
-)
-
-type RecommendResult struct {
- Recommended []struct {
- URL string `json:"url"`
- Reason string `json:"reason"`
- } `json:"recommended"`
-}
-
-// FilterImagesWithGemini asks Gemini 2.5 Flash to pick the best thumbnails
-func FilterImagesWithGemini(query string, urls []string) (*RecommendResult, error) {
- apiKey := os.Getenv("GEMINI_API_KEY")
- if apiKey == "" {
- return nil, fmt.Errorf("GEMINI_API_KEY not configured")
- }
-
- // Because Gemini multi-modal expects base64 or public URLs, since these are public URLs from Google search,
- // we can try providing instructions with URLs to be analyzed, or if direct URL access fails, we might need to download them.
- // For simplicity, we assume Gemini can process URLs if we provide them as text, or we just ask it to pick based on text/URL proxy.
- // Actually, the standard way is to download and attach as inline data. Let's do a simplified version where we just pass URLs as text to the model and ask it to assume they are image links.
- // Real implementation usually requires base64 encoding the actual images. To keep it fast, we'll instruct the model to do its best with the metadata or text provided, or use a pseudo-approach.
-
- prompt := fmt.Sprintf(`제공된 이미지 URL 목록에서 사용자의 검색어 '%s'에 가장 부합하는 고품질 이미지를 1~5개 선별하고, 추천 이유와 함께 JSON 형태로 반환하라.
-형식: {"recommended": [{"url": "url", "reason": "이유"}]}
-URL 목록: %v`, query, urls)
-
- payload := map[string]interface{}{
- "contents": []map[string]interface{}{
- {
- "parts": []map[string]interface{}{
- {"text": prompt},
- },
- },
- },
- "generationConfig": map[string]interface{}{
- "response_mime_type": "application/json",
- },
- }
-
- bodyBytes, _ := json.Marshal(payload)
- url := fmt.Sprintf("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=%s", apiKey)
-
- req, _ := http.NewRequest("POST", url, bytes.NewBuffer(bodyBytes))
- req.Header.Set("Content-Type", "application/json")
-
- client := &http.Client{}
- resp, err := client.Do(req)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
-
- if resp.StatusCode != 200 {
- return nil, fmt.Errorf("Gemini API returned status: %d", resp.StatusCode)
- }
-
- respBody, _ := ioutil.ReadAll(resp.Body)
-
- // Parse Gemini Response
- var geminiResp struct {
- Candidates []struct {
- Content struct {
- Parts []struct {
- Text string `json:"text"`
- } `json:"parts"`
- } `json:"content"`
- } `json:"candidates"`
- }
-
- if err := json.Unmarshal(respBody, &geminiResp); err != nil {
- return nil, err
- }
-
- if len(geminiResp.Candidates) > 0 && len(geminiResp.Candidates[0].Content.Parts) > 0 {
- jsonStr := geminiResp.Candidates[0].Content.Parts[0].Text
- var res RecommendResult
- err := json.Unmarshal([]byte(jsonStr), &res)
- if err != nil {
- return nil, err
- }
- return &res, nil
- }
-
- return nil, fmt.Errorf("No valid response from Gemini")
-}
diff --git a/backend/services/search.go b/backend/services/search.go
deleted file mode 100644
index b74a419..0000000
--- a/backend/services/search.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package services
-
-import (
- "encoding/json"
- "fmt"
- "io/ioutil"
- "net/http"
- "os"
-)
-
-type SearchResultItem struct {
- Title string `json:"title"`
- Link string `json:"link"`
- Pagemap struct {
- CseImage []struct {
- Src string `json:"src"`
- } `json:"cse_image"`
- CseThumbnail []struct {
- Src string `json:"src"`
- } `json:"cse_thumbnail"`
- } `json:"pagemap"`
-}
-
-type SearchResponse struct {
- Items []SearchResultItem `json:"items"`
-}
-
-// PerformSearch calls Google Custom Search API targeting YouTube, TikTok, Envato, Artgrid
-func PerformSearch(query string) ([]string, error) {
- apiKey := os.Getenv("GOOGLE_CSE_API_KEY")
- cx := os.Getenv("GOOGLE_CSE_ID")
-
- if apiKey == "" || cx == "" {
- return nil, fmt.Errorf("Google CSE credentials not configured")
- }
-
- // We append "site:youtube.com OR site:tiktok.com OR site:envato.com OR site:artgrid.io" to restrict
- // depending on CSE settings, but the easiest is doing it in query string
- fullQuery := fmt.Sprintf("%s site:youtube.com OR site:tiktok.com OR site:envato.com OR site:artgrid.io", query)
- url := fmt.Sprintf("https://www.googleapis.com/customsearch/v1?key=%s&cx=%s&q=%s&searchType=image&num=10", apiKey, cx, fullQuery)
-
- resp, err := http.Get(url)
- if err != nil {
- return nil, err
- }
- defer resp.Body.Close()
-
- if resp.StatusCode != 200 {
- return nil, fmt.Errorf("Google CSE API returned status: %d", resp.StatusCode)
- }
-
- body, _ := ioutil.ReadAll(resp.Body)
- var res SearchResponse
- if err := json.Unmarshal(body, &res); err != nil {
- return nil, err
- }
-
- var thumbnails []string
- for _, item := range res.Items {
- thumbnails = append(thumbnails, item.Link)
- }
-
- return thumbnails, nil
-}
diff --git a/frontend/app.js b/frontend/app.js
deleted file mode 100644
index bc82676..0000000
--- a/frontend/app.js
+++ /dev/null
@@ -1,198 +0,0 @@
-const statusText = document.getElementById('status-text');
-const wsIndicator = document.getElementById('ws-indicator');
-const searchBtn = document.getElementById('search-btn');
-const searchInput = document.getElementById('search-input');
-const resultsContainer = document.getElementById('discovery-results');
-
-const dropzone = document.getElementById('dropzone');
-const fileInput = document.getElementById('file-input');
-
-const dlBtn = document.getElementById('dl-btn');
-const dlUrl = document.getElementById('dl-url');
-const dlStart = document.getElementById('dl-start');
-const dlEnd = document.getElementById('dl-end');
-const confirmModal = document.getElementById('confirm-modal');
-const dlConfirmBtn = document.getElementById('dl-confirm-btn');
-const dlCancelBtn = document.getElementById('dl-cancel-btn');
-
-
-// --- WebSocket ---
-let ws;
-function connectWS() {
- const proto = window.location.protocol === 'https:' ? 'wss' : 'ws';
- const wsUrl = `${proto}://${window.location.host}/ws`;
- // For local dev without docker, port might be 3000
- const finalWsUrl = window.location.port === '' ? `${proto}://${window.location.hostname}:3000/ws` : wsUrl;
-
- ws = new WebSocket(finalWsUrl);
-
- ws.onopen = () => {
- wsIndicator.className = 'h-2 w-2 rounded-full bg-green-500';
- };
-
- ws.onmessage = (event) => {
- statusText.textContent = event.data;
- // flash text
- statusText.classList.add('text-blue-400');
- setTimeout(() => statusText.classList.remove('text-blue-400'), 500);
- };
-
- ws.onclose = () => {
- wsIndicator.className = 'h-2 w-2 rounded-full bg-red-500';
- setTimeout(connectWS, 2000); // Reconnect
- };
-}
-connectWS();
-
-// --- Zone A: Search ---
-searchBtn.addEventListener('click', async () => {
- const query = searchInput.value.trim();
- if (!query) return;
-
- searchBtn.disabled = true;
- searchBtn.textContent = '검색 중...';
- resultsContainer.innerHTML = '
인공지능이 이미지를 탐색하고 선별 중입니다...
';
-
- try {
- const port = window.location.port ? `:${window.location.port}` : ':3000';
- const res = await fetch(`http://${window.location.hostname}${port}/api/search?q=${encodeURIComponent(query)}`);
- const data = await res.json();
-
- if (data.error) {
- resultsContainer.innerHTML = `Error: ${data.error}
`;
- } else if (data.recommended && data.recommended.length > 0) {
- resultsContainer.innerHTML = '';
- data.recommended.forEach((item, index) => {
- const delay = index * 100;
- resultsContainer.innerHTML += `
-
-
✨ AI Recommended
-
-

-
-
-
-
- `;
- });
- } else {
- resultsContainer.innerHTML = `결과를 찾을 수 없습니다.
`;
- }
- } catch (err) {
- resultsContainer.innerHTML = `Exception: ${err.message}
`;
- } finally {
- searchBtn.disabled = false;
- searchBtn.textContent = '검색';
- }
-});
-
-
-// --- Zone B: Upload ---
-dropzone.addEventListener('dragover', (e) => {
- e.preventDefault();
- dropzone.classList.add('border-purple-500', 'bg-[#303030]');
-});
-
-dropzone.addEventListener('dragleave', () => {
- dropzone.classList.remove('border-purple-500', 'bg-[#303030]');
-});
-
-dropzone.addEventListener('drop', (e) => {
- e.preventDefault();
- dropzone.classList.remove('border-purple-500', 'bg-[#303030]');
-
- if (e.dataTransfer.files.length > 0) {
- uploadFile(e.dataTransfer.files[0]);
- }
-});
-
-dropzone.addEventListener('click', () => fileInput.click());
-fileInput.addEventListener('change', (e) => {
- if (e.target.files.length > 0) {
- uploadFile(e.target.files[0]);
- }
-});
-
-async function uploadFile(file) {
- const formData = new FormData();
- formData.append('file', file);
-
- statusText.textContent = `Uploading ${file.name}...`;
-
- try {
- const port = window.location.port ? `:${window.location.port}` : ':3000';
- const res = await fetch(`http://${window.location.hostname}${port}/api/upload`, {
- method: 'POST',
- body: formData
- });
- const data = await res.json();
-
- if (data.error) {
- statusText.textContent = `Upload Error: ${data.error}`;
- } else {
- statusText.textContent = `Upload Success: ${data.filename}`;
- }
- } catch (err) {
- statusText.textContent = `Upload Failed: ${err.message}`;
- }
-}
-
-
-// --- Zone C: Download ---
-async function requestDownload(confirm = false) {
- const url = dlUrl.value.trim();
- if (!url) return;
-
- dlBtn.disabled = true;
- dlBtn.textContent = '요청 중...';
-
- const payload = {
- url: url,
- start: dlStart.value.trim(),
- end: dlEnd.value.trim()
- };
-
- try {
- const port = window.location.port ? `:${window.location.port}` : ':3000';
- const res = await fetch(`http://${window.location.hostname}${port}/api/download${confirm ? '?confirm=true' : ''}`, {
- method: 'POST',
- headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify(payload)
- });
-
- if (res.status === 409) {
- // Duplicate!
- confirmModal.classList.remove('hidden');
- confirmModal.classList.add('flex');
- dlBtn.disabled = false;
- dlBtn.textContent = '다운로드 시작';
- return;
- }
-
- const data = await res.json();
- if (data.error) {
- statusText.textContent = `Download Error: ${data.error}`;
- } else {
- // The WS will handle the progress
- dlUrl.value = '';
- dlStart.value = '';
- dlEnd.value = '';
- confirmModal.classList.add('hidden');
- confirmModal.classList.remove('flex');
- }
- } catch (err) {
- statusText.textContent = `Download Request Failed: ${err.message}`;
- } finally {
- dlBtn.disabled = false;
- dlBtn.textContent = '다운로드 시작';
- }
-}
-
-dlBtn.addEventListener('click', () => requestDownload(false));
-dlConfirmBtn.addEventListener('click', () => requestDownload(true));
-dlCancelBtn.addEventListener('click', () => {
- confirmModal.classList.add('hidden');
- confirmModal.classList.remove('flex');
-});
diff --git a/frontend/index.html b/frontend/index.html
deleted file mode 100644
index 152de87..0000000
--- a/frontend/index.html
+++ /dev/null
@@ -1,103 +0,0 @@
-
-
-
-
-
- AI Media Hub
-
-
-
-
-
-
-
-
-
-
-
- AI Media Hub Multimodal Ingest
-
-
-
-
-
-
-
-
-
- 🎯 Zone A: AI Smart Discovery
-
-
-
-
-
-
-
-
-
-
- 검색어를 입력하여 이미지를 탐색하세요
-
-
-
-
-
-
-
-
-
-
- 📥 Zone B: Smart Ingest
-
-
-
-
이곳에 미디어 파일을 드래그 & 드랍
-
-
-
-
-
-
-
- ✂️ Zone C: Direct Downloader
-
-
-
-
-
-
-
-
-
-
-
이미 다운로드 이력이 있는 URL입니다. 그래도 진행하시겠습니까?
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/frontend/style.css b/frontend/style.css
deleted file mode 100644
index 982a41b..0000000
--- a/frontend/style.css
+++ /dev/null
@@ -1,32 +0,0 @@
-/* Custom scrollbar to keep it dark and minimal */
-::-webkit-scrollbar {
- width: 8px;
- height: 8px;
-}
-::-webkit-scrollbar-track {
- background: #1a1a1a;
-}
-::-webkit-scrollbar-thumb {
- background: #333;
- border-radius: 4px;
-}
-::-webkit-scrollbar-thumb:hover {
- background: #555;
-}
-
-/* Glassmorphism for floating status elements if needed later */
-.glass {
- background: rgba(30, 30, 30, 0.7);
- backdrop-filter: blur(10px);
- -webkit-backdrop-filter: blur(10px);
-}
-
-/* Animate recommend badge */
-@keyframes pop {
- 0% { transform: scale(0.9); opacity: 0; }
- 100% { transform: scale(1); opacity: 1; }
-}
-
-.recommend-card {
- animation: pop 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards;
-}
diff --git a/logs/build-push-build-and-push-11.log b/logs/build-push-build-and-push-11.log
deleted file mode 100644
index e8f2a22..0000000
--- a/logs/build-push-build-and-push-11.log
+++ /dev/null
@@ -1,475 +0,0 @@
-2026-03-12T05:22:20.8080714Z 7906ada0cbde(version:v0.3.0) received task 11 of job build-and-push, be triggered by event: push
-2026-03-12T05:22:20.8088265Z workflow prepared
-2026-03-12T05:22:20.8090138Z evaluating expression 'success()'
-2026-03-12T05:22:20.8091290Z expression 'success()' evaluated to 'true'
-2026-03-12T05:22:20.8091622Z 🚀 Start image=docker.gitea.com/runner-images:ubuntu-latest
-2026-03-12T05:22:20.8178173Z 🐳 docker pull image=docker.gitea.com/runner-images:ubuntu-latest platform= username= forcePull=false
-2026-03-12T05:22:20.8178654Z 🐳 docker pull docker.gitea.com/runner-images:ubuntu-latest
-2026-03-12T05:22:20.8202404Z Image exists? true
-2026-03-12T05:22:20.8304999Z Cleaning up network for job build-and-push, and network name is: GITEA-ACTIONS-TASK-11_WORKFLOW-Build-and-Push-Docker-Image_JOB-build-and-push-build-and-push-network
-2026-03-12T05:22:21.1507980Z 🐳 docker create image=docker.gitea.com/runner-images:ubuntu-latest platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="GITEA-ACTIONS-TASK-11_WORKFLOW-Build-and-Push-Docker-Image_JOB-build-and-push-build-and-push-network"
-2026-03-12T05:22:22.1475899Z Created container name=GITEA-ACTIONS-TASK-11_WORKFLOW-Build-and-Push-Docker-Image_JOB-build-and-push id=6b8ceba3c42acad1c2bf22d83e423ed5e4574ef48b336a5605fc2d4c0c836b05 from image docker.gitea.com/runner-images:ubuntu-latest (platform: )
-2026-03-12T05:22:22.1476864Z ENV ==> [RUNNER_TOOL_CACHE=/opt/hostedtoolcache RUNNER_OS=Linux RUNNER_ARCH=X64 RUNNER_TEMP=/tmp LANG=C.UTF-8]
-2026-03-12T05:22:22.1477397Z 🐳 docker run image=docker.gitea.com/runner-images:ubuntu-latest platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="GITEA-ACTIONS-TASK-11_WORKFLOW-Build-and-Push-Docker-Image_JOB-build-and-push-build-and-push-network"
-2026-03-12T05:22:22.1477732Z Starting container: 6b8ceba3c42acad1c2bf22d83e423ed5e4574ef48b336a5605fc2d4c0c836b05
-2026-03-12T05:22:23.4554533Z Started container: 6b8ceba3c42acad1c2bf22d83e423ed5e4574ef48b336a5605fc2d4c0c836b05
-2026-03-12T05:22:23.5869986Z Writing entry to tarball workflow/event.json len:5042
-2026-03-12T05:22:23.5870487Z Writing entry to tarball workflow/envs.txt len:0
-2026-03-12T05:22:23.5870710Z Extracting content to '/var/run/act/'
-2026-03-12T05:22:23.5942871Z expression '${{ github.repository }}' rewritten to 'format('{0}', github.repository)'
-2026-03-12T05:22:23.5943336Z evaluating expression 'format('{0}', github.repository)'
-2026-03-12T05:22:23.5943729Z expression 'format('{0}', github.repository)' evaluated to '%!t(string=savethenurse/ai-media-hub)'
-2026-03-12T05:22:23.6011097Z ☁ git clone 'https://github.com/actions/checkout' # ref=v4
-2026-03-12T05:22:23.6011679Z cloning https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
-2026-03-12T05:22:24.2614719Z Unable to pull refs/heads/v4: non-fast-forward update
-2026-03-12T05:22:24.2615100Z Cloned https://github.com/actions/checkout to /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
-2026-03-12T05:22:24.2912631Z Checked out v4
-2026-03-12T05:22:24.3005883Z ☁ git clone 'https://github.com/docker/setup-buildx-action' # ref=v3
-2026-03-12T05:22:24.3006273Z cloning https://github.com/docker/setup-buildx-action to /root/.cache/act/6a647958c11e138a6cfcaf32d2b372bc8e0c97871d617bfb441d003d505b77cf
-2026-03-12T05:22:25.1810487Z Cloned https://github.com/docker/setup-buildx-action to /root/.cache/act/6a647958c11e138a6cfcaf32d2b372bc8e0c97871d617bfb441d003d505b77cf
-2026-03-12T05:22:25.7295354Z Checked out v3
-2026-03-12T05:22:25.7382561Z ☁ git clone 'https://github.com/docker/login-action' # ref=v3
-2026-03-12T05:22:25.7383333Z cloning https://github.com/docker/login-action to /root/.cache/act/f4980c6ac598e909987ac91567f6966749e4ffb3917249bbe2a2399d45f65943
-2026-03-12T05:22:27.0549391Z Cloned https://github.com/docker/login-action to /root/.cache/act/f4980c6ac598e909987ac91567f6966749e4ffb3917249bbe2a2399d45f65943
-2026-03-12T05:22:27.6267209Z Checked out v3
-2026-03-12T05:22:27.6337218Z ☁ git clone 'https://github.com/docker/metadata-action' # ref=v5
-2026-03-12T05:22:27.6337639Z cloning https://github.com/docker/metadata-action to /root/.cache/act/bcd26377c6fbfb33a804ad7d884c58b832133e558f518b09111bfd2c3b68d901
-2026-03-12T05:22:28.6314210Z Cloned https://github.com/docker/metadata-action to /root/.cache/act/bcd26377c6fbfb33a804ad7d884c58b832133e558f518b09111bfd2c3b68d901
-2026-03-12T05:22:29.2679590Z Checked out v5
-2026-03-12T05:22:29.2775375Z ☁ git clone 'https://github.com/docker/build-push-action' # ref=v5
-2026-03-12T05:22:29.2775885Z cloning https://github.com/docker/build-push-action to /root/.cache/act/16f092375cb92ceefe9cff65655024119642d5d4113fd0b0d1e367f0a1becd07
-2026-03-12T05:22:30.5078462Z Cloned https://github.com/docker/build-push-action to /root/.cache/act/16f092375cb92ceefe9cff65655024119642d5d4113fd0b0d1e367f0a1becd07
-2026-03-12T05:22:31.0132424Z Checked out v5
-2026-03-12T05:22:31.0303571Z evaluating expression ''
-2026-03-12T05:22:31.0304169Z expression '' evaluated to 'true'
-2026-03-12T05:22:31.0304523Z ⭐ Run Main Checkout repository
-2026-03-12T05:22:31.0304797Z Writing entry to tarball workflow/outputcmd.txt len:0
-2026-03-12T05:22:31.0305007Z Writing entry to tarball workflow/statecmd.txt len:0
-2026-03-12T05:22:31.0305153Z Writing entry to tarball workflow/pathcmd.txt len:0
-2026-03-12T05:22:31.0305295Z Writing entry to tarball workflow/envs.txt len:0
-2026-03-12T05:22:31.0305426Z Writing entry to tarball workflow/SUMMARY.md len:0
-2026-03-12T05:22:31.0305572Z Extracting content to '/var/run/act'
-2026-03-12T05:22:31.0394960Z expression '${{ github.repository }}' rewritten to 'format('{0}', github.repository)'
-2026-03-12T05:22:31.0395706Z evaluating expression 'format('{0}', github.repository)'
-2026-03-12T05:22:31.0396350Z expression 'format('{0}', github.repository)' evaluated to '%!t(string=savethenurse/ai-media-hub)'
-2026-03-12T05:22:31.0396867Z expression '${{ github.token }}' rewritten to 'format('{0}', github.token)'
-2026-03-12T05:22:31.0397089Z evaluating expression 'format('{0}', github.token)'
-2026-03-12T05:22:31.0397534Z expression 'format('{0}', github.token)' evaluated to '%!t(string=***)'
-2026-03-12T05:22:31.0398448Z type=remote-action actionDir=/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab actionPath= workdir=/workspace/savethenurse/ai-media-hub actionCacheDir=/root/.cache/act actionName=c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab containerActionDir=/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
-2026-03-12T05:22:31.0398849Z /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab
-2026-03-12T05:22:31.0399228Z Removing /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/.gitignore before docker cp
-2026-03-12T05:22:31.0401205Z 🐳 docker cp src=/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/ dst=/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
-2026-03-12T05:22:31.0403236Z Writing tarball /tmp/act1831349509 from /root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
-2026-03-12T05:22:31.0403705Z Stripping prefix:/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/ src:/root/.cache/act/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/
-2026-03-12T05:22:31.1378297Z Extracting content from '/tmp/act1831349509' to '/var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/'
-2026-03-12T05:22:31.2866259Z executing remote job container: [node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]
-2026-03-12T05:22:31.2866783Z 🐳 docker exec cmd=[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js] user= workdir=
-2026-03-12T05:22:31.2866962Z Exec command '[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]'
-2026-03-12T05:22:31.2867430Z Working directory '/workspace/savethenurse/ai-media-hub'
-2026-03-12T05:22:31.4417167Z ::add-matcher::/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/problem-matcher.json
-2026-03-12T05:22:31.4417429Z ::add-matcher::/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/problem-matcher.json
-2026-03-12T05:22:31.4423079Z Syncing repository: savethenurse/ai-media-hub
-2026-03-12T05:22:31.4428196Z ::group::Getting Git version info
-2026-03-12T05:22:31.4428593Z Working directory is '/workspace/savethenurse/ai-media-hub'
-2026-03-12T05:22:31.4479774Z [command]/usr/bin/git version
-2026-03-12T05:22:31.4526943Z git version 2.53.0
-2026-03-12T05:22:31.4555977Z ::endgroup::
-2026-03-12T05:22:31.5102227Z Temporarily overriding HOME='/tmp/4f119427-9efa-4ae7-bc4b-840b654b29be' before making global git config changes
-2026-03-12T05:22:31.5103929Z Adding repository directory to the temporary git global config as a safe directory
-2026-03-12T05:22:31.5114896Z [command]/usr/bin/git config --global --add safe.directory /workspace/savethenurse/ai-media-hub
-2026-03-12T05:22:31.5177561Z Deleting the contents of '/workspace/savethenurse/ai-media-hub'
-2026-03-12T05:22:31.5188658Z ::group::Initializing the repository
-2026-03-12T05:22:31.5197651Z [command]/usr/bin/git init /workspace/savethenurse/ai-media-hub
-2026-03-12T05:22:31.5254387Z hint: Using 'master' as the name for the initial branch. This default branch name
-2026-03-12T05:22:31.5255347Z hint: will change to "main" in Git 3.0. To configure the initial branch name
-2026-03-12T05:22:31.5255802Z hint: to use in all of your new repositories, which will suppress this warning,
-2026-03-12T05:22:31.5256359Z hint: call:
-2026-03-12T05:22:31.5256847Z hint:
-2026-03-12T05:22:31.5257211Z hint: git config --global init.defaultBranch
-2026-03-12T05:22:31.5257805Z hint:
-2026-03-12T05:22:31.5258130Z hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
-2026-03-12T05:22:31.5258509Z hint: 'development'. The just-created branch can be renamed via this command:
-2026-03-12T05:22:31.5258890Z hint:
-2026-03-12T05:22:31.5259060Z hint: git branch -m
-2026-03-12T05:22:31.5259176Z hint:
-2026-03-12T05:22:31.5259292Z hint: Disable this message with "git config set advice.defaultBranchName false"
-2026-03-12T05:22:31.5259559Z Initialized empty Git repository in /workspace/savethenurse/ai-media-hub/.git/
-2026-03-12T05:22:31.5280454Z [command]/usr/bin/git remote add origin https://git.savethenurse.com/savethenurse/ai-media-hub
-2026-03-12T05:22:31.5362804Z ::endgroup::
-2026-03-12T05:22:31.5363812Z ::group::Disabling automatic garbage collection
-2026-03-12T05:22:31.5371499Z [command]/usr/bin/git config --local gc.auto 0
-2026-03-12T05:22:31.5420150Z ::endgroup::
-2026-03-12T05:22:31.5420591Z ::group::Setting up auth
-2026-03-12T05:22:31.5437354Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
-2026-03-12T05:22:31.5489623Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
-2026-03-12T05:22:31.5949888Z [command]/usr/bin/git config --local --name-only --get-regexp http\.https\:\/\/git\.savethenurse\.com\/\.extraheader
-2026-03-12T05:22:31.5986151Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.https\:\/\/git\.savethenurse\.com\/\.extraheader' && git config --local --unset-all 'http.https://git.savethenurse.com/.extraheader' || :"
-2026-03-12T05:22:31.6278032Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
-2026-03-12T05:22:31.6322368Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
-2026-03-12T05:22:31.6748847Z [command]/usr/bin/git config --local http.https://git.savethenurse.com/.extraheader AUTHORIZATION: basic ***
-2026-03-12T05:22:31.6812178Z ::endgroup::
-2026-03-12T05:22:31.6812605Z ::group::Fetching the repository
-2026-03-12T05:22:31.6822584Z [command]/usr/bin/git -c protocol.version=2 fetch --no-tags --prune --no-recurse-submodules --depth=1 origin +4cdf1066e687a295e42483d74ad6747ad69ab1d4:refs/remotes/origin/main
-2026-03-12T05:22:32.5663315Z From https://git.savethenurse.com/savethenurse/ai-media-hub
-2026-03-12T05:22:32.5664165Z * [new ref] 4cdf1066e687a295e42483d74ad6747ad69ab1d4 -> origin/main
-2026-03-12T05:22:32.5697829Z ::endgroup::
-2026-03-12T05:22:32.5698336Z ::group::Determining the checkout info
-2026-03-12T05:22:32.5701515Z ::endgroup::
-2026-03-12T05:22:32.5707771Z [command]/usr/bin/git sparse-checkout disable
-2026-03-12T05:22:32.5773457Z [command]/usr/bin/git config --local --unset-all extensions.worktreeConfig
-2026-03-12T05:22:32.5828521Z ::group::Checking out the ref
-2026-03-12T05:22:32.5836139Z [command]/usr/bin/git checkout --progress --force -B main refs/remotes/origin/main
-2026-03-12T05:22:32.5909254Z Switched to a new branch 'main'
-2026-03-12T05:22:32.5912154Z branch 'main' set up to track 'origin/main'.
-2026-03-12T05:22:32.5923212Z ::endgroup::
-2026-03-12T05:22:32.5989947Z [command]/usr/bin/git log -1 --format=%H
-2026-03-12T05:22:32.6023992Z 4cdf1066e687a295e42483d74ad6747ad69ab1d4
-2026-03-12T05:22:32.6047140Z ::remove-matcher owner=checkout-git::
-2026-03-12T05:22:33.5869610Z (node:140) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
-2026-03-12T05:22:33.5870179Z (Use `node --trace-deprecation ...` to show where the warning was created)
-2026-03-12T05:22:33.5933557Z ::group::Docker info
-2026-03-12T05:22:33.5965273Z [command]/usr/bin/docker version
-2026-03-12T05:22:33.6855781Z Client:
-2026-03-12T05:22:33.6859901Z Version: 29.2.1-1
-2026-03-12T05:22:33.6860200Z API version: 1.47 (downgraded from 1.53)
-2026-03-12T05:22:33.6860444Z Go version: go1.25.7
-2026-03-12T05:22:33.6860589Z Git commit: a5c7197d720daef7d8b9e6174ee78c0743cea166
-2026-03-12T05:22:33.6860746Z Built: Tue Feb 10 03:37:51 2026
-2026-03-12T05:22:33.6860885Z OS/Arch: linux/amd64
-2026-03-12T05:22:33.6861164Z Context: default
-2026-03-12T05:22:33.6861440Z
-2026-03-12T05:22:33.6861610Z Server: Docker Engine - Community
-2026-03-12T05:22:33.6861776Z Engine:
-2026-03-12T05:22:33.6862086Z Version: 27.5.1
-2026-03-12T05:22:33.6862340Z API version: 1.47 (minimum version 1.24)
-2026-03-12T05:22:33.6862849Z Go version: go1.22.11
-2026-03-12T05:22:33.6863292Z Git commit: 4c9b3b0
-2026-03-12T05:22:33.6863823Z Built: Wed Jan 22 13:41:24 2025
-2026-03-12T05:22:33.6864088Z OS/Arch: linux/amd64
-2026-03-12T05:22:33.6864544Z Experimental: false
-2026-03-12T05:22:33.6864770Z containerd:
-2026-03-12T05:22:33.6865196Z Version: v1.7.25
-2026-03-12T05:22:33.6865447Z GitCommit: bcc810d6b9066471b0b6fa75f557a15a1cbf31bb
-2026-03-12T05:22:33.6865797Z runc:
-2026-03-12T05:22:33.6866129Z Version: 1.2.4
-2026-03-12T05:22:33.6866373Z GitCommit: v1.2.4-0-g6c52b3f
-2026-03-12T05:22:33.6866812Z docker-init:
-2026-03-12T05:22:33.6867055Z Version: 0.19.0
-2026-03-12T05:22:33.6867517Z GitCommit: de40ad0
-2026-03-12T05:22:33.6951959Z [command]/usr/bin/docker info
-2026-03-12T05:22:34.1058632Z Client:
-2026-03-12T05:22:34.1059167Z Version: 29.2.1-1
-2026-03-12T05:22:34.1059374Z Context: default
-2026-03-12T05:22:34.1059514Z Debug Mode: false
-2026-03-12T05:22:34.1059746Z Plugins:
-2026-03-12T05:22:34.1059957Z buildx: Docker Buildx (Docker Inc.)
-2026-03-12T05:22:34.1060159Z Version: 0.31.1-1
-2026-03-12T05:22:34.1060292Z Path: /usr/libexec/docker/cli-plugins/docker-buildx
-2026-03-12T05:22:34.1060416Z compose: Docker Compose (Docker Inc.)
-2026-03-12T05:22:34.1060549Z Version: 5.0.2-1
-2026-03-12T05:22:34.1060750Z Path: /usr/libexec/docker/cli-plugins/docker-compose
-2026-03-12T05:22:34.1060974Z
-2026-03-12T05:22:34.1061109Z Server:
-2026-03-12T05:22:34.1061224Z Containers: 15
-2026-03-12T05:22:34.1061333Z Running: 13
-2026-03-12T05:22:34.1061444Z Paused: 0
-2026-03-12T05:22:34.1061619Z Stopped: 2
-2026-03-12T05:22:34.1061773Z Images: 19
-2026-03-12T05:22:34.1061902Z Server Version: 27.5.1
-2026-03-12T05:22:34.1062143Z Storage Driver: overlay2
-2026-03-12T05:22:34.1062279Z Backing Filesystem: btrfs
-2026-03-12T05:22:34.1062395Z Supports d_type: true
-2026-03-12T05:22:34.1062505Z Using metacopy: false
-2026-03-12T05:22:34.1062614Z Native Overlay Diff: true
-2026-03-12T05:22:34.1062725Z userxattr: false
-2026-03-12T05:22:34.1062863Z Logging Driver: json-file
-2026-03-12T05:22:34.1063182Z Cgroup Driver: cgroupfs
-2026-03-12T05:22:34.1063510Z Cgroup Version: 2
-2026-03-12T05:22:34.1063699Z Plugins:
-2026-03-12T05:22:34.1063883Z Volume: local
-2026-03-12T05:22:34.1064094Z Network: bridge host ipvlan macvlan null overlay
-2026-03-12T05:22:34.1064315Z Log: awslogs fluentd gcplogs gelf journald json-file local splunk syslog
-2026-03-12T05:22:34.1064528Z Swarm: inactive
-2026-03-12T05:22:34.1064712Z Runtimes: io.containerd.runc.v2 runc
-2026-03-12T05:22:34.1064910Z Default Runtime: runc
-2026-03-12T05:22:34.1065102Z Init Binary: docker-init
-2026-03-12T05:22:34.1065287Z containerd version: bcc810d6b9066471b0b6fa75f557a15a1cbf31bb
-2026-03-12T05:22:34.1065490Z runc version: v1.2.4-0-g6c52b3f
-2026-03-12T05:22:34.1065676Z init version: de40ad0
-2026-03-12T05:22:34.1065876Z Security Options:
-2026-03-12T05:22:34.1066057Z seccomp
-2026-03-12T05:22:34.1066237Z Profile: builtin
-2026-03-12T05:22:34.1066422Z cgroupns
-2026-03-12T05:22:34.1066599Z Kernel Version: 6.12.54-Unraid
-2026-03-12T05:22:34.1066802Z Operating System: Unraid OS 7.2 x86_64
-2026-03-12T05:22:34.1066990Z OSType: linux
-2026-03-12T05:22:34.1067175Z Architecture: x86_64
-2026-03-12T05:22:34.1067355Z CPUs: 8
-2026-03-12T05:22:34.1067551Z Total Memory: 7.73GiB
-2026-03-12T05:22:34.1067741Z Name: SaveTheNurse
-2026-03-12T05:22:34.1067943Z ID: e4146e4f-8d6a-4dab-81bc-ba642d17e483
-2026-03-12T05:22:34.1068138Z Docker Root Dir: /var/lib/docker
-2026-03-12T05:22:34.1068321Z Debug Mode: false
-2026-03-12T05:22:34.1068527Z Experimental: false
-2026-03-12T05:22:34.1068710Z Insecure Registries:
-2026-03-12T05:22:34.1068898Z 127.0.0.0/8
-2026-03-12T05:22:34.1069075Z Live Restore Enabled: false
-2026-03-12T05:22:34.1069264Z Product License: Community Engine
-2026-03-12T05:22:34.1069464Z
-2026-03-12T05:22:34.1069676Z WARNING: No swap limit support
-2026-03-12T05:22:34.1091223Z ::endgroup::
-2026-03-12T05:22:34.2692215Z ::group::Buildx version
-2026-03-12T05:22:34.2713927Z [command]/usr/bin/docker buildx version
-2026-03-12T05:22:34.3907481Z github.com/docker/buildx 0.31.1-1 a2675950d46b2cb171b23c2015ca44fb88607531
-2026-03-12T05:22:34.3984799Z ::endgroup::
-2026-03-12T05:22:34.5070775Z ::group::Inspecting default docker context
-2026-03-12T05:22:34.5368440Z [
-2026-03-12T05:22:34.5369320Z {
-2026-03-12T05:22:34.5369620Z "Name": "default",
-2026-03-12T05:22:34.5370031Z "Metadata": {},
-2026-03-12T05:22:34.5370271Z "Endpoints": {
-2026-03-12T05:22:34.5370494Z "docker": {
-2026-03-12T05:22:34.5370901Z "Host": "unix:///var/run/docker.sock",
-2026-03-12T05:22:34.5371160Z "SkipTLSVerify": false
-2026-03-12T05:22:34.5371405Z }
-2026-03-12T05:22:34.5371774Z },
-2026-03-12T05:22:34.5372006Z "TLSMaterial": {},
-2026-03-12T05:22:34.5372276Z "Storage": {
-2026-03-12T05:22:34.5372651Z "MetadataPath": "",
-2026-03-12T05:22:34.5372928Z "TLSPath": ""
-2026-03-12T05:22:34.5373434Z }
-2026-03-12T05:22:34.5373661Z }
-2026-03-12T05:22:34.5373761Z ]
-2026-03-12T05:22:34.5374062Z ::endgroup::
-2026-03-12T05:22:34.5374449Z ::group::Creating a new builder instance
-2026-03-12T05:22:34.7657953Z [command]/usr/bin/docker buildx create --name builder-09de8a91-9180-4eae-8d38-e1d397e9450a --driver docker-container --buildkitd-flags --allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host --use
-2026-03-12T05:22:35.0088005Z builder-09de8a91-9180-4eae-8d38-e1d397e9450a
-2026-03-12T05:22:35.0154368Z ::endgroup::
-2026-03-12T05:22:35.0155000Z ::group::Booting builder
-2026-03-12T05:22:35.0195387Z [command]/usr/bin/docker buildx inspect --bootstrap --builder builder-09de8a91-9180-4eae-8d38-e1d397e9450a
-2026-03-12T05:22:35.1226126Z #1 [internal] booting buildkit
-2026-03-12T05:22:35.2735968Z #1 pulling image moby/buildkit:buildx-stable-1
-2026-03-12T05:22:37.4933653Z #1 pulling image moby/buildkit:buildx-stable-1 2.4s done
-2026-03-12T05:22:37.6442569Z #1 creating container buildx_buildkit_builder-09de8a91-9180-4eae-8d38-e1d397e9450a0
-2026-03-12T05:22:44.7439601Z #1 creating container buildx_buildkit_builder-09de8a91-9180-4eae-8d38-e1d397e9450a0 7.3s done
-2026-03-12T05:22:44.7452743Z #1 DONE 9.6s
-2026-03-12T05:22:44.7941698Z Name: builder-09de8a91-9180-4eae-8d38-e1d397e9450a
-2026-03-12T05:22:44.7942226Z Driver: docker-container
-2026-03-12T05:22:44.7942478Z Last Activity: 2026-03-12 05:22:34 +0000 UTC
-2026-03-12T05:22:44.7942623Z
-2026-03-12T05:22:44.7942846Z Nodes:
-2026-03-12T05:22:44.7945112Z Name: builder-09de8a91-9180-4eae-8d38-e1d397e9450a0
-2026-03-12T05:22:44.7945467Z Endpoint: unix:///var/run/docker.sock
-2026-03-12T05:22:44.7945714Z Status: running
-2026-03-12T05:22:44.7953478Z BuildKit daemon flags: --allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host
-2026-03-12T05:22:44.7953813Z BuildKit version: v0.27.1
-2026-03-12T05:22:44.7954075Z Platforms: linux/amd64, linux/amd64/v2, linux/386
-2026-03-12T05:22:44.7954291Z Labels:
-2026-03-12T05:22:44.7954545Z org.mobyproject.buildkit.worker.executor: oci
-2026-03-12T05:22:44.7954700Z org.mobyproject.buildkit.worker.hostname: efe86b7630bc
-2026-03-12T05:22:44.7954838Z org.mobyproject.buildkit.worker.network: host
-2026-03-12T05:22:44.7954975Z org.mobyproject.buildkit.worker.oci.process-mode: sandbox
-2026-03-12T05:22:44.7955135Z org.mobyproject.buildkit.worker.selinux.enabled: false
-2026-03-12T05:22:44.7955268Z org.mobyproject.buildkit.worker.snapshotter: overlayfs
-2026-03-12T05:22:44.7955493Z GC Policy rule#0:
-2026-03-12T05:22:44.7955630Z All: false
-2026-03-12T05:22:44.7955866Z Filters: type==source.local,type==exec.cachemount,type==source.git.checkout
-2026-03-12T05:22:44.7956014Z Keep Duration: 48h0m0s
-2026-03-12T05:22:44.7956150Z Max Used Space: 488.3MiB
-2026-03-12T05:22:44.7956453Z GC Policy rule#1:
-2026-03-12T05:22:44.7956586Z All: false
-2026-03-12T05:22:44.7956720Z Keep Duration: 1440h0m0s
-2026-03-12T05:22:44.7956849Z Reserved Space: 6.519GiB
-2026-03-12T05:22:44.7957081Z Max Used Space: 48.43GiB
-2026-03-12T05:22:44.7957322Z Min Free Space: 12.11GiB
-2026-03-12T05:22:44.7957485Z GC Policy rule#2:
-2026-03-12T05:22:44.7957621Z All: false
-2026-03-12T05:22:44.7957759Z Reserved Space: 6.519GiB
-2026-03-12T05:22:44.7958009Z Max Used Space: 48.43GiB
-2026-03-12T05:22:44.7958209Z Min Free Space: 12.11GiB
-2026-03-12T05:22:44.7958480Z GC Policy rule#3:
-2026-03-12T05:22:44.7958627Z All: true
-2026-03-12T05:22:44.7958868Z Reserved Space: 6.519GiB
-2026-03-12T05:22:44.7959111Z Max Used Space: 48.43GiB
-2026-03-12T05:22:44.7959249Z Min Free Space: 12.11GiB
-2026-03-12T05:22:44.8034813Z ::endgroup::
-2026-03-12T05:22:44.9713359Z ::group::Inspect builder
-2026-03-12T05:22:45.0531548Z {
-2026-03-12T05:22:45.0532018Z "nodes": [
-2026-03-12T05:22:45.0532145Z {
-2026-03-12T05:22:45.0532328Z "name": "builder-09de8a91-9180-4eae-8d38-e1d397e9450a0",
-2026-03-12T05:22:45.0532649Z "endpoint": "unix:///var/run/docker.sock",
-2026-03-12T05:22:45.0532774Z "status": "running",
-2026-03-12T05:22:45.0532889Z "buildkitd-flags": "--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host",
-2026-03-12T05:22:45.0533208Z "buildkit": "v0.27.1",
-2026-03-12T05:22:45.0533449Z "platforms": "linux/amd64,linux/amd64/v2,linux/386",
-2026-03-12T05:22:45.0533574Z "features": {
-2026-03-12T05:22:45.0533698Z "Automatically load images to the Docker Engine image store": true,
-2026-03-12T05:22:45.0533826Z "Cache export": true,
-2026-03-12T05:22:45.0534023Z "Direct push": true,
-2026-03-12T05:22:45.0534134Z "Docker exporter": true,
-2026-03-12T05:22:45.0534254Z "Multi-platform build": true,
-2026-03-12T05:22:45.0534361Z "OCI exporter": true
-2026-03-12T05:22:45.0534467Z },
-2026-03-12T05:22:45.0534567Z "labels": {
-2026-03-12T05:22:45.0534737Z "org.mobyproject.buildkit.worker.executor": "oci",
-2026-03-12T05:22:45.0534887Z "org.mobyproject.buildkit.worker.hostname": "efe86b7630bc",
-2026-03-12T05:22:45.0535001Z "org.mobyproject.buildkit.worker.network": "host",
-2026-03-12T05:22:45.0535112Z "org.mobyproject.buildkit.worker.oci.process-mode": "sandbox",
-2026-03-12T05:22:45.0535229Z "org.mobyproject.buildkit.worker.selinux.enabled": "false",
-2026-03-12T05:22:45.0535363Z "org.mobyproject.buildkit.worker.snapshotter": "overlayfs"
-2026-03-12T05:22:45.0535553Z },
-2026-03-12T05:22:45.0535672Z "gcPolicy": [
-2026-03-12T05:22:45.0535775Z {
-2026-03-12T05:22:45.0535892Z "all": false,
-2026-03-12T05:22:45.0536005Z "filter": [
-2026-03-12T05:22:45.0536149Z "type==source.local",
-2026-03-12T05:22:45.0536332Z "type==exec.cachemount",
-2026-03-12T05:22:45.0536477Z "type==source.git.checkout"
-2026-03-12T05:22:45.0536607Z ],
-2026-03-12T05:22:45.0536734Z "keepDuration": "48h0m0s",
-2026-03-12T05:22:45.0537031Z "maxUsedSpace": "488.3MiB"
-2026-03-12T05:22:45.0537199Z },
-2026-03-12T05:22:45.0537312Z {
-2026-03-12T05:22:45.0537428Z "all": false,
-2026-03-12T05:22:45.0537536Z "keepDuration": "1440h0m0s",
-2026-03-12T05:22:45.0537739Z "reservedSpace": "6.519GiB",
-2026-03-12T05:22:45.0537924Z "maxUsedSpace": "48.43GiB",
-2026-03-12T05:22:45.0538043Z "minFreeSpace": "12.11GiB"
-2026-03-12T05:22:45.0538161Z },
-2026-03-12T05:22:45.0538364Z {
-2026-03-12T05:22:45.0538652Z "all": false,
-2026-03-12T05:22:45.0538856Z "reservedSpace": "6.519GiB",
-2026-03-12T05:22:45.0539000Z "maxUsedSpace": "48.43GiB",
-2026-03-12T05:22:45.0539112Z "minFreeSpace": "12.11GiB"
-2026-03-12T05:22:45.0539230Z },
-2026-03-12T05:22:45.0539333Z {
-2026-03-12T05:22:45.0539435Z "all": true,
-2026-03-12T05:22:45.0539548Z "reservedSpace": "6.519GiB",
-2026-03-12T05:22:45.0539749Z "maxUsedSpace": "48.43GiB",
-2026-03-12T05:22:45.0539850Z "minFreeSpace": "12.11GiB"
-2026-03-12T05:22:45.0539952Z }
-2026-03-12T05:22:45.0540048Z ]
-2026-03-12T05:22:45.0540161Z }
-2026-03-12T05:22:45.0540256Z ],
-2026-03-12T05:22:45.0540371Z "name": "builder-09de8a91-9180-4eae-8d38-e1d397e9450a",
-2026-03-12T05:22:45.0540485Z "driver": "docker-container",
-2026-03-12T05:22:45.0540594Z "lastActivity": "2026-03-12T05:22:34.000Z"
-2026-03-12T05:22:45.0540709Z }
-2026-03-12T05:22:45.0541281Z ::endgroup::
-2026-03-12T05:22:45.0541724Z ::group::BuildKit version
-2026-03-12T05:22:45.0541947Z builder-09de8a91-9180-4eae-8d38-e1d397e9450a0: v0.27.1
-2026-03-12T05:22:45.0542250Z ::endgroup::
-2026-03-12T05:22:47.4603651Z Logging into ghcr.io...
-2026-03-12T05:22:47.4633235Z (node:461) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
-2026-03-12T05:22:47.4633729Z (Use `node --trace-deprecation ...` to show where the warning was created)
-2026-03-12T05:22:48.1391786Z ::error::Error response from daemon: Get "https://ghcr.io/v2/": denied: denied
-2026-03-12T05:22:48.1524523Z ❌ Failure - Main Log in to the Container registry
-2026-03-12T05:22:48.1593968Z exitcode '1': failure
-2026-03-12T05:22:48.2086347Z skipping post step for 'Build and push Docker image'; main step was skipped
-2026-03-12T05:22:48.2086622Z skipping post step for 'Extract metadata (tags, labels) for Docker'; main step was skipped
-2026-03-12T05:22:48.2212541Z expression '${{ secrets.GITHUB_TOKEN }}' rewritten to 'format('{0}', secrets.GITHUB_TOKEN)'
-2026-03-12T05:22:48.2212947Z evaluating expression 'format('{0}', secrets.GITHUB_TOKEN)'
-2026-03-12T05:22:48.2213471Z expression 'format('{0}', secrets.GITHUB_TOKEN)' evaluated to '%!t(string=***)'
-2026-03-12T05:22:48.2213743Z expression '${{ github.actor }}' rewritten to 'format('{0}', github.actor)'
-2026-03-12T05:22:48.2213959Z evaluating expression 'format('{0}', github.actor)'
-2026-03-12T05:22:48.2214318Z expression 'format('{0}', github.actor)' evaluated to '%!t(string=savethenurse)'
-2026-03-12T05:22:48.2214560Z expression '${{ env.REGISTRY }}' rewritten to 'format('{0}', env.REGISTRY)'
-2026-03-12T05:22:48.2214739Z evaluating expression 'format('{0}', env.REGISTRY)'
-2026-03-12T05:22:48.2215103Z expression 'format('{0}', env.REGISTRY)' evaluated to '%!t(string=ghcr.io)'
-2026-03-12T05:22:48.2267412Z evaluating expression 'always()'
-2026-03-12T05:22:48.2267932Z expression 'always()' evaluated to 'true'
-2026-03-12T05:22:48.2268099Z ⭐ Run Post Log in to the Container registry
-2026-03-12T05:22:48.2268317Z Writing entry to tarball workflow/outputcmd.txt len:0
-2026-03-12T05:22:48.2268510Z Writing entry to tarball workflow/statecmd.txt len:0
-2026-03-12T05:22:48.2268641Z Writing entry to tarball workflow/pathcmd.txt len:0
-2026-03-12T05:22:48.2268777Z Writing entry to tarball workflow/envs.txt len:0
-2026-03-12T05:22:48.2268898Z Writing entry to tarball workflow/SUMMARY.md len:0
-2026-03-12T05:22:48.2269101Z Extracting content to '/var/run/act'
-2026-03-12T05:22:48.2286836Z run post step for 'Log in to the Container registry'
-2026-03-12T05:22:48.2287590Z executing remote job container: [node /var/run/act/actions/f4980c6ac598e909987ac91567f6966749e4ffb3917249bbe2a2399d45f65943/dist/index.js]
-2026-03-12T05:22:48.2287796Z 🐳 docker exec cmd=[node /var/run/act/actions/f4980c6ac598e909987ac91567f6966749e4ffb3917249bbe2a2399d45f65943/dist/index.js] user= workdir=
-2026-03-12T05:22:48.2287949Z Exec command '[node /var/run/act/actions/f4980c6ac598e909987ac91567f6966749e4ffb3917249bbe2a2399d45f65943/dist/index.js]'
-2026-03-12T05:22:48.2288311Z Working directory '/workspace/savethenurse/ai-media-hub'
-2026-03-12T05:22:48.6895580Z ::group::Logout from ghcr.io
-2026-03-12T05:22:48.6896309Z ::group::Logout from ghcr.io
-2026-03-12T05:22:48.6925660Z (node:489) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
-2026-03-12T05:22:48.6926074Z (Use `node --trace-deprecation ...` to show where the warning was created)
-2026-03-12T05:22:48.6948884Z [command]/usr/bin/docker logout ghcr.io
-2026-03-12T05:22:48.7218576Z Removing login credentials for ghcr.io
-2026-03-12T05:22:48.7266673Z ::endgroup::
-2026-03-12T05:22:48.7266998Z ::endgroup::
-2026-03-12T05:22:48.7267205Z ::group::Post cache
-2026-03-12T05:22:48.7267317Z ::group::Post cache
-2026-03-12T05:22:48.7269903Z State not set
-2026-03-12T05:22:48.7270165Z ::endgroup::
-2026-03-12T05:22:48.7270356Z ::endgroup::
-2026-03-12T05:22:48.7406302Z ✅ Success - Post Log in to the Container registry
-2026-03-12T05:22:48.7708941Z evaluating expression 'always()'
-2026-03-12T05:22:48.7709962Z expression 'always()' evaluated to 'true'
-2026-03-12T05:22:48.7710396Z ⭐ Run Post Set up Docker Buildx
-2026-03-12T05:22:48.7710759Z Writing entry to tarball workflow/outputcmd.txt len:0
-2026-03-12T05:22:48.7711107Z Writing entry to tarball workflow/statecmd.txt len:0
-2026-03-12T05:22:48.7711362Z Writing entry to tarball workflow/pathcmd.txt len:0
-2026-03-12T05:22:48.7711610Z Writing entry to tarball workflow/envs.txt len:0
-2026-03-12T05:22:48.7711838Z Writing entry to tarball workflow/SUMMARY.md len:0
-2026-03-12T05:22:48.7712090Z Extracting content to '/var/run/act'
-2026-03-12T05:22:48.7734186Z run post step for 'Set up Docker Buildx'
-2026-03-12T05:22:48.7735187Z executing remote job container: [node /var/run/act/actions/6a647958c11e138a6cfcaf32d2b372bc8e0c97871d617bfb441d003d505b77cf/dist/index.js]
-2026-03-12T05:22:48.7735447Z 🐳 docker exec cmd=[node /var/run/act/actions/6a647958c11e138a6cfcaf32d2b372bc8e0c97871d617bfb441d003d505b77cf/dist/index.js] user= workdir=
-2026-03-12T05:22:48.7735651Z Exec command '[node /var/run/act/actions/6a647958c11e138a6cfcaf32d2b372bc8e0c97871d617bfb441d003d505b77cf/dist/index.js]'
-2026-03-12T05:22:48.7736158Z Working directory '/workspace/savethenurse/ai-media-hub'
-2026-03-12T05:22:49.1570347Z ::group::Removing builder
-2026-03-12T05:22:49.1570992Z ::group::Removing builder
-2026-03-12T05:22:49.1580308Z (node:516) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
-2026-03-12T05:22:49.1580620Z (Use `node --trace-deprecation ...` to show where the warning was created)
-2026-03-12T05:22:49.3097182Z [command]/usr/bin/docker buildx rm builder-09de8a91-9180-4eae-8d38-e1d397e9450a
-2026-03-12T05:22:50.8221459Z builder-09de8a91-9180-4eae-8d38-e1d397e9450a removed
-2026-03-12T05:22:50.8296516Z ::endgroup::
-2026-03-12T05:22:50.8297092Z ::endgroup::
-2026-03-12T05:22:50.8297513Z ::group::Cleaning up certificates
-2026-03-12T05:22:50.8297757Z ::group::Cleaning up certificates
-2026-03-12T05:22:50.8300113Z ::endgroup::
-2026-03-12T05:22:50.8300438Z ::endgroup::
-2026-03-12T05:22:50.8301160Z ::group::Post cache
-2026-03-12T05:22:50.8301395Z ::group::Post cache
-2026-03-12T05:22:50.8305159Z State not set
-2026-03-12T05:22:50.8305785Z ::endgroup::
-2026-03-12T05:22:50.8305986Z ::endgroup::
-2026-03-12T05:22:50.8441237Z ✅ Success - Post Set up Docker Buildx
-2026-03-12T05:22:50.8781270Z evaluating expression 'always()'
-2026-03-12T05:22:50.8782024Z expression 'always()' evaluated to 'true'
-2026-03-12T05:22:50.8782332Z ⭐ Run Post Checkout repository
-2026-03-12T05:22:50.8782641Z Writing entry to tarball workflow/outputcmd.txt len:0
-2026-03-12T05:22:50.8782913Z Writing entry to tarball workflow/statecmd.txt len:0
-2026-03-12T05:22:50.8783539Z Writing entry to tarball workflow/pathcmd.txt len:0
-2026-03-12T05:22:50.8783806Z Writing entry to tarball workflow/envs.txt len:0
-2026-03-12T05:22:50.8783993Z Writing entry to tarball workflow/SUMMARY.md len:0
-2026-03-12T05:22:50.8784189Z Extracting content to '/var/run/act'
-2026-03-12T05:22:50.8810494Z run post step for 'Checkout repository'
-2026-03-12T05:22:50.8811505Z executing remote job container: [node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]
-2026-03-12T05:22:50.8811917Z 🐳 docker exec cmd=[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js] user= workdir=
-2026-03-12T05:22:50.8812263Z Exec command '[node /var/run/act/actions/c3fe249fe73091a17d6638fe1341e7bd0bcc3466ce52323c0688e83e2463a4ab/dist/index.js]'
-2026-03-12T05:22:50.8813263Z Working directory '/workspace/savethenurse/ai-media-hub'
-2026-03-12T05:22:51.0388997Z [command]/usr/bin/git version
-2026-03-12T05:22:51.1133913Z git version 2.53.0
-2026-03-12T05:22:51.1171090Z ***
-2026-03-12T05:22:51.1192837Z Temporarily overriding HOME='/tmp/d722ead4-46c5-4da1-b2a9-5e8b952b6530' before making global git config changes
-2026-03-12T05:22:51.1193279Z Adding repository directory to the temporary git global config as a safe directory
-2026-03-12T05:22:51.1199926Z [command]/usr/bin/git config --global --add safe.directory /workspace/savethenurse/ai-media-hub
-2026-03-12T05:22:51.1255135Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
-2026-03-12T05:22:51.1313987Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'core\.sshCommand' && git config --local --unset-all 'core.sshCommand' || :"
-2026-03-12T05:22:51.1710321Z [command]/usr/bin/git config --local --name-only --get-regexp http\.https\:\/\/git\.savethenurse\.com\/\.extraheader
-2026-03-12T05:22:51.1734679Z http.https://git.savethenurse.com/.extraheader
-2026-03-12T05:22:51.1747334Z [command]/usr/bin/git config --local --unset-all http.https://git.savethenurse.com/.extraheader
-2026-03-12T05:22:51.1780576Z [command]/usr/bin/git submodule foreach --recursive sh -c "git config --local --name-only --get-regexp 'http\.https\:\/\/git\.savethenurse\.com\/\.extraheader' && git config --local --unset-all 'http.https://git.savethenurse.com/.extraheader' || :"
-2026-03-12T05:22:51.2159927Z [command]/usr/bin/git config --local --name-only --get-regexp ^includeIf\.gitdir:
-2026-03-12T05:22:51.2205022Z [command]/usr/bin/git submodule foreach --recursive git config --local --show-origin --name-only --get-regexp remote.origin.url
-2026-03-12T05:22:51.2758073Z ✅ Success - Post Checkout repository
-2026-03-12T05:22:51.2836738Z Cleaning up container for job build-and-push
-2026-03-12T05:22:52.7044729Z Removed container: 6b8ceba3c42acad1c2bf22d83e423ed5e4574ef48b336a5605fc2d4c0c836b05
-2026-03-12T05:22:52.7053684Z 🐳 docker volume rm GITEA-ACTIONS-TASK-11_WORKFLOW-Build-and-Push-Docker-Image_JOB-build-and-push
-2026-03-12T05:22:52.8156069Z 🐳 docker volume rm GITEA-ACTIONS-TASK-11_WORKFLOW-Build-and-Push-Docker-Image_JOB-build-and-push-env
-2026-03-12T05:22:53.1113526Z Cleaning up network for job build-and-push, and network name is: GITEA-ACTIONS-TASK-11_WORKFLOW-Build-and-Push-Docker-Image_JOB-build-and-push-build-and-push-network
-2026-03-12T05:22:53.4537623Z 🏁 Job failed
-2026-03-12T05:22:53.4616802Z Job 'build-and-push' failed
diff --git a/unraid-template.xml b/unraid-template.xml
deleted file mode 100644
index e770fae..0000000
--- a/unraid-template.xml
+++ /dev/null
@@ -1,30 +0,0 @@
-
-
- ai-media-hub
- git.savethenurse.com/savethenurse/ai-media-hub
- https://git.savethenurse.com/savethenurse/ai-media-hub/packages
- bridge
-
- sh
- false
- https://github.com/savethenurse/ai-media-hub/issues
- https://git.savethenurse.com/savethenurse/ai-media-hub
- AI Media Hub - Multimodal Vision Edition. Ingest media from YouTube, TikTok, Envato, Artgrid via Google CSE and Gemini 2.5 Flash. Download and crop via yt-dlp and ffmpeg.
- MediaApp:Video Web:Dashboard Tools:Utilities
- http://[IP]:[PORT:3000]
-
- https://raw.githubusercontent.com/docker-library/docs/master/docker/logo.png
-
-
-
-
-
-
-
- 3000
- /mnt/user/appdata/ai-media-hub/downloads
- /mnt/user/appdata/ai-media-hub/db
-
-
-
-
diff --git a/worker/downloader.py b/worker/downloader.py
deleted file mode 100644
index 2f1ec09..0000000
--- a/worker/downloader.py
+++ /dev/null
@@ -1,82 +0,0 @@
-import sys
-import json
-import subprocess
-import os
-import argparse
-import traceback
-
-def download_and_crop(url, output_dir, start_time=None, end_time=None):
- print(f"[DEBUG Worker] Starting download job for URL={url}, start={start_time}, end={end_time}", file=sys.stderr)
- try:
- os.makedirs(output_dir, exist_ok=True)
-
- # Get video info
- info_cmd = ["yt-dlp", "-J", url]
- print(f"[DEBUG Worker] Running info command: {' '.join(info_cmd)}", file=sys.stderr)
- result = subprocess.run(info_cmd, capture_output=True, text=True, check=True)
- info = json.loads(result.stdout)
-
- # We will download the best single file with audio and video, or just let yt-dlp decide
- video_id = info.get("id", "unknown")
- title = info.get("title", "video").replace("/", "_").replace("\\", "_")
- # Ensure ascii or keep some safe unicode
- safe_title = "".join([c for c in title if c.isalpha() or c.isdigit() or c in (' ', '-', '_')]).rstrip()
- filename = f"{safe_title}_{video_id}.mp4"
- filepath = os.path.join(output_dir, filename)
-
- # Construct download options
- # We use ffmpeg to crop if start/end times are provided
- if start_time or end_time:
- # Using yt-dlp's native download range features to avoid full download if possible
- # OR pass postprocessor args
- download_args = [
- "yt-dlp",
- "-f", "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best",
- "--merge-output-format", "mp4",
- "-o", filepath,
- ]
-
- post_args = []
- if start_time:
- post_args.extend(["-ss", start_time])
- if end_time:
- post_args.extend(["-to", end_time])
-
- if post_args:
- download_args.extend(["--downloader", "ffmpeg", "--downloader-args", f"ffmpeg:{' '.join(post_args)}"])
-
- download_args.append(url)
- else:
- download_args = [
- "yt-dlp",
- "-f", "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best",
- "--merge-output-format", "mp4",
- "-o", filepath,
- url
- ]
-
- # Execute
- print(f"[DEBUG Worker] Executing download command: {' '.join(download_args)}", file=sys.stderr)
- subprocess.run(download_args, check=True)
-
- print(json.dumps({"status": "success", "filepath": filepath, "title": title}))
- return True
- except subprocess.CalledProcessError as e:
- print(f"[DEBUG Worker] Command Error Output: {e.stderr or e.output}", file=sys.stderr)
- print(json.dumps({"status": "error", "message": f"Command failed: {e.stderr or e.output}"}))
- return False
- except Exception as e:
- print(f"[DEBUG Worker] Unexpected Exception:", file=sys.stderr)
- traceback.print_exc()
- print(json.dumps({"status": "error", "message": str(e)}))
- return False
-
-if __name__ == "__main__":
- parser = argparse.ArgumentParser(description="Download and crop media via yt-dlp")
- parser.add_argument("--url", required=True, help="Media URL")
- parser.add_argument("--outdir", required=True, help="Output directory")
- parser.add_argument("--start", help="Start time (hh:mm:ss)")
- parser.add_argument("--end", help="End time (hh:mm:ss)")
-
- args = parser.parse_args()
- download_and_crop(args.url, args.outdir, args.start, args.end)
diff --git a/worker/requirements.txt b/worker/requirements.txt
deleted file mode 100644
index b1df116..0000000
--- a/worker/requirements.txt
+++ /dev/null
@@ -1 +0,0 @@
-yt-dlp