removed
This commit is contained in:
@@ -1,41 +0,0 @@
|
||||
name: Build and Push Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
tags:
|
||||
- 'v*'
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: '1.22'
|
||||
|
||||
- name: Log in to Gitea Container Registry
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
registry: git.savethenurse.com
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ github.token }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4
|
||||
with:
|
||||
images: git.savethenurse.com/savethenurse/ai-media-hub
|
||||
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
33
Dockerfile
33
Dockerfile
@@ -1,33 +0,0 @@
|
||||
# Stage 1: Build the Go Application
|
||||
FROM golang:1.22-alpine AS builder
|
||||
WORKDIR /app
|
||||
# Initialize go mod if not exists, download deps
|
||||
COPY . .
|
||||
RUN go mod init ai-media-hub || true
|
||||
RUN go mod tidy
|
||||
RUN CGO_ENABLED=0 GOOS=linux go build -o ai-media-hub main.go
|
||||
|
||||
# Stage 2: Final Image (Python 3.10+ & yt-dlp & Go Binary)
|
||||
FROM python:3.10-slim
|
||||
WORKDIR /app
|
||||
|
||||
# Install dependencies (ffmpeg for media merging/cropping)
|
||||
RUN apt-get update && \
|
||||
apt-get install -y ffmpeg curl && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
|
||||
# Install yt-dlp
|
||||
RUN pip install --no-cache-dir yt-dlp
|
||||
|
||||
# Copy Go binary and frontend files from builder
|
||||
COPY --from=builder /app/ai-media-hub /app/ai-media-hub
|
||||
COPY --from=builder /app/index.html /app/index.html
|
||||
|
||||
# Expose port
|
||||
EXPOSE 8000
|
||||
|
||||
# Directory for NAS mount
|
||||
RUN mkdir -p /data/nas
|
||||
|
||||
# Run the Go server
|
||||
CMD ["./ai-media-hub"]
|
||||
24
go.mod
24
go.mod
@@ -1,24 +0,0 @@
|
||||
module ai-media-hub
|
||||
|
||||
go 1.22.2
|
||||
|
||||
require (
|
||||
github.com/gofiber/fiber/v2 v2.52.12
|
||||
github.com/gofiber/websocket/v2 v2.2.1
|
||||
)
|
||||
|
||||
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/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/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
|
||||
)
|
||||
33
go.sum
33
go.sum
@@ -1,33 +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/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/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=
|
||||
202
index.html
202
index.html
@@ -1,202 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="ko">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>AI Media Hub</title>
|
||||
<!-- Tailwind CSS (CDN for minimal setup) -->
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
darkMode: 'class',
|
||||
theme: {
|
||||
extend: {
|
||||
colors: {
|
||||
dark: '#121212',
|
||||
darker: '#0a0a0a',
|
||||
primary: '#ffffff',
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
body { @apply bg-darker text-primary font-sans antialiased; }
|
||||
.zone-card { @apply bg-dark rounded-xl border border-gray-800 p-6 shadow-2xl; }
|
||||
.input-dark { @apply bg-darker border border-gray-700 rounded-lg px-4 py-3 focus:outline-none focus:border-gray-500 w-full transition-colors; }
|
||||
.btn-primary { @apply bg-white text-black font-semibold rounded-lg px-6 py-3 hover:bg-gray-200 transition-colors; }
|
||||
.thumbnail-card { @apply relative group rounded-lg overflow-hidden border border-gray-800 cursor-pointer; }
|
||||
.thumbnail-img { @apply w-full h-48 object-cover transition-transform duration-300 group-hover:scale-105; }
|
||||
.ai-badge { @apply absolute top-2 right-2 bg-gradient-to-r from-purple-500 to-indigo-500 text-white text-xs font-bold px-2 py-1 rounded-md shadow-lg; }
|
||||
.source-badge { @apply absolute bottom-2 left-2 bg-black/70 backdrop-blur-sm text-white text-xs px-2 py-1 rounded; }
|
||||
.dropzone { @apply border-2 border-dashed border-gray-700 rounded-xl p-12 text-center hover:border-gray-500 transition-colors cursor-pointer bg-dark; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="dark h-screen flex flex-col">
|
||||
<!-- Header -->
|
||||
<header class="border-b border-gray-800 bg-darker/80 backdrop-blur-md sticky top-0 z-50">
|
||||
<div class="container mx-auto px-6 py-4 flex justify-between items-center">
|
||||
<h1 class="text-2xl font-bold tracking-tight">AI Media Hub <span class="text-sm font-normal text-gray-500 ml-2">For Video Editors</span></h1>
|
||||
<div id="status-indicator" class="flex items-center space-x-2 text-sm text-gray-400">
|
||||
<span class="w-2 h-2 rounded-full bg-green-500"></span>
|
||||
<span>System Online</span>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="flex-1 overflow-y-auto p-6">
|
||||
<div class="container mx-auto grid grid-cols-1 lg:grid-cols-2 gap-8">
|
||||
|
||||
<!-- Zone A: AI 디스커버리 -->
|
||||
<section class="lg:col-span-2 zone-card">
|
||||
<h2 class="text-xl font-semibold mb-4 flex items-center">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 21l-6-6m2-5a7 7 0 11-14 0 7 7 0 0114 0z"></path></svg>
|
||||
Zone A: AI Discovery (Gemini 2.5 Flash Vision)
|
||||
</h2>
|
||||
<form id="discovery-form" class="flex space-x-4 mb-6">
|
||||
<input type="text" id="search-query" class="input-dark" placeholder="AI가 썸네일을 분석하여 가장 적합한 영상을 찾아줍니다... (예: 사이버펑크 스타일 도시 야경)" required>
|
||||
<button type="submit" class="btn-primary whitespace-nowrap" id="search-btn">
|
||||
Search
|
||||
</button>
|
||||
</form>
|
||||
|
||||
<div id="loading-spinner" class="hidden py-12 flex-col items-center justify-center text-gray-500">
|
||||
<svg class="animate-spin h-8 w-8 mb-4" viewBox="0 0 24 24" fill="none">
|
||||
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle>
|
||||
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
|
||||
</svg>
|
||||
<p>AI가 썸네일을 분석하고 있습니다...</p>
|
||||
</div>
|
||||
|
||||
<div id="results-grid" class="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 gap-6">
|
||||
<!-- AI 추천 썸네일 렌더링 영역 -->
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- Zone B: 스마트 인제스트 -->
|
||||
<section class="zone-card">
|
||||
<h2 class="text-xl font-semibold mb-4 flex items-center">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"></path></svg>
|
||||
Zone B: Smart Ingest (NAS)
|
||||
</h2>
|
||||
<div id="dropzone" class="dropzone mb-4 flex flex-col items-center justify-center">
|
||||
<svg class="w-12 h-12 text-gray-500 mb-4" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5" d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12"></path></svg>
|
||||
<p class="text-gray-400">파일을 드래그 앤 드롭 하거나 클릭하여 업로드</p>
|
||||
<p class="text-gray-600 text-sm mt-2">NAS에 바로 저장됩니다.</p>
|
||||
</div>
|
||||
<input type="file" id="file-input" class="hidden" multiple>
|
||||
</section>
|
||||
|
||||
<!-- Zone C: 다이렉트 다운로드 (yt-dlp) -->
|
||||
<section class="zone-card">
|
||||
<h2 class="text-xl font-semibold mb-4 flex items-center">
|
||||
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"></path><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path></svg>
|
||||
Zone C: Direct Download (yt-dlp)
|
||||
</h2>
|
||||
<form id="download-form" class="space-y-4">
|
||||
<input type="url" id="dl-url" class="input-dark" placeholder="비디오 URL 입력 (YouTube, TikTok 등)" required>
|
||||
<div class="flex space-x-4">
|
||||
<input type="text" id="dl-start" class="input-dark w-1/2" placeholder="시작 구간 (옵션: 00:00:00)">
|
||||
<input type="text" id="dl-end" class="input-dark w-1/2" placeholder="종료 구간 (옵션: 00:01:00)">
|
||||
</div>
|
||||
<button type="submit" class="btn-primary w-full">Download to NAS</button>
|
||||
<!-- 진행률 표시 -->
|
||||
<div id="progress-container" class="hidden mt-4">
|
||||
<div class="flex justify-between text-sm text-gray-400 mb-1">
|
||||
<span>Downloading...</span>
|
||||
<span id="progress-text">0%</span>
|
||||
</div>
|
||||
<div class="w-full bg-gray-800 rounded-full h-2">
|
||||
<div id="progress-bar" class="bg-white h-2 rounded-full transition-all duration-300" style="width: 0%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script>
|
||||
// WebSocket for Progress updates
|
||||
const ws = new WebSocket('ws://' + window.location.host + '/ws');
|
||||
ws.onmessage = function(event) {
|
||||
const data = JSON.parse(event.data);
|
||||
if(data.type === 'progress') {
|
||||
document.getElementById('progress-container').classList.remove('hidden');
|
||||
document.getElementById('progress-bar').style.width = data.percent + '%';
|
||||
document.getElementById('progress-text').innerText = data.percent + '%';
|
||||
}
|
||||
};
|
||||
|
||||
// Zone A: Search
|
||||
document.getElementById('discovery-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const query = document.getElementById('search-query').value;
|
||||
const btn = document.getElementById('search-btn');
|
||||
const spinner = document.getElementById('loading-spinner');
|
||||
const grid = document.getElementById('results-grid');
|
||||
|
||||
btn.disabled = true;
|
||||
spinner.classList.remove('hidden');
|
||||
grid.innerHTML = '';
|
||||
|
||||
try {
|
||||
const res = await fetch('/api/search', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({ query })
|
||||
});
|
||||
const data = await res.json();
|
||||
|
||||
if (data.results && data.results.length > 0) {
|
||||
data.results.forEach(item => {
|
||||
const card = document.createElement('div');
|
||||
card.className = 'thumbnail-card';
|
||||
card.onclick = () => window.open(item.link, '_blank');
|
||||
card.innerHTML = `
|
||||
<img src="${item.thumbnail}" class="thumbnail-img" alt="Thumbnail">
|
||||
${item.ai_recommended ? '<div class="ai-badge">✨ AI Recommended</div>' : ''}
|
||||
<div class="source-badge">${item.source}</div>
|
||||
<div class="p-3 bg-dark border-t border-gray-800">
|
||||
<h3 class="text-sm font-semibold truncate" title="${item.title}">${item.title}</h3>
|
||||
<p class="text-xs text-gray-400 mt-1 line-clamp-2">${item.ai_reasoning || item.snippet}</p>
|
||||
</div>
|
||||
`;
|
||||
grid.appendChild(card);
|
||||
});
|
||||
} else {
|
||||
grid.innerHTML = '<p class="text-gray-500 col-span-full text-center">결과가 없습니다.</p>';
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert("검색 중 오류가 발생했습니다.");
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
spinner.classList.add('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
// Zone C: Download
|
||||
document.getElementById('download-form').addEventListener('submit', async (e) => {
|
||||
e.preventDefault();
|
||||
const url = document.getElementById('dl-url').value;
|
||||
const start = document.getElementById('dl-start').value;
|
||||
const end = document.getElementById('dl-end').value;
|
||||
|
||||
document.getElementById('progress-container').classList.remove('hidden');
|
||||
document.getElementById('progress-bar').style.width = '0%';
|
||||
|
||||
try {
|
||||
await fetch('/api/download', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({ url, start, end })
|
||||
});
|
||||
// Note: progress will be updated via WebSocket
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
alert("다운로드 요청 실패");
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,259 +0,0 @@
|
||||
2026-03-12T04:55:14.4174500Z 7906ada0cbde(version:v0.3.0) received task 7 of job docker, be triggered by event: push
|
||||
2026-03-12T04:55:14.4287282Z workflow prepared
|
||||
2026-03-12T04:55:14.4288544Z evaluating expression 'success()'
|
||||
2026-03-12T04:55:14.4289524Z expression 'success()' evaluated to 'true'
|
||||
2026-03-12T04:55:14.4289737Z 🚀 Start image=docker.gitea.com/runner-images:ubuntu-latest
|
||||
2026-03-12T04:55:14.6708348Z 🐳 docker pull image=docker.gitea.com/runner-images:ubuntu-latest platform= username= forcePull=false
|
||||
2026-03-12T04:55:14.6708727Z 🐳 docker pull docker.gitea.com/runner-images:ubuntu-latest
|
||||
2026-03-12T04:55:14.6803631Z Image exists? true
|
||||
2026-03-12T04:55:14.6995177Z Cleaning up network for job docker, and network name is: GITEA-ACTIONS-TASK-7_WORKFLOW-Build-and-Push-Docker-Image_JOB-docker-docker-network
|
||||
2026-03-12T04:55:15.2153753Z 🐳 docker create image=docker.gitea.com/runner-images:ubuntu-latest platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="GITEA-ACTIONS-TASK-7_WORKFLOW-Build-and-Push-Docker-Image_JOB-docker-docker-network"
|
||||
2026-03-12T04:55:17.5004001Z Created container name=GITEA-ACTIONS-TASK-7_WORKFLOW-Build-and-Push-Docker-Image_JOB-docker id=5e6a9ffcbc9639d45beb4895eed58e334dfdee51e780012d0645efebe4e32903 from image docker.gitea.com/runner-images:ubuntu-latest (platform: )
|
||||
2026-03-12T04:55:17.5004712Z ENV ==> [RUNNER_TOOL_CACHE=/opt/hostedtoolcache RUNNER_OS=Linux RUNNER_ARCH=X64 RUNNER_TEMP=/tmp LANG=C.UTF-8]
|
||||
2026-03-12T04:55:17.5005118Z 🐳 docker run image=docker.gitea.com/runner-images:ubuntu-latest platform= entrypoint=["/bin/sleep" "10800"] cmd=[] network="GITEA-ACTIONS-TASK-7_WORKFLOW-Build-and-Push-Docker-Image_JOB-docker-docker-network"
|
||||
2026-03-12T04:55:17.5005422Z Starting container: 5e6a9ffcbc9639d45beb4895eed58e334dfdee51e780012d0645efebe4e32903
|
||||
2026-03-12T04:55:20.4770617Z Started container: 5e6a9ffcbc9639d45beb4895eed58e334dfdee51e780012d0645efebe4e32903
|
||||
2026-03-12T04:55:20.8919460Z Writing entry to tarball workflow/event.json len:5154
|
||||
2026-03-12T04:55:20.8920146Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-03-12T04:55:20.8920515Z Extracting content to '/var/run/act/'
|
||||
2026-03-12T04:55:20.9185361Z ☁ git clone 'https://github.com/actions/checkout' # ref=v3
|
||||
2026-03-12T04:55:20.9186046Z cloning https://github.com/actions/checkout to /root/.cache/act/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6
|
||||
2026-03-12T04:55:22.5612074Z Unable to pull refs/heads/v3: worktree contains unstaged changes
|
||||
2026-03-12T04:55:22.5612659Z Cloned https://github.com/actions/checkout to /root/.cache/act/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6
|
||||
2026-03-12T04:55:22.6468778Z Checked out v3
|
||||
2026-03-12T04:55:22.6530493Z ☁ git clone 'https://github.com/actions/setup-go' # ref=v4
|
||||
2026-03-12T04:55:22.6531016Z cloning https://github.com/actions/setup-go to /root/.cache/act/fd7d62239e994546e01f58df7ed12dc03dc4f9370800b8ff8736bf90b80e2db5
|
||||
2026-03-12T04:55:42.8411153Z Cloned https://github.com/actions/setup-go to /root/.cache/act/fd7d62239e994546e01f58df7ed12dc03dc4f9370800b8ff8736bf90b80e2db5
|
||||
2026-03-12T04:55:43.0109620Z Checked out v4
|
||||
2026-03-12T04:55:43.0340443Z ☁ git clone 'https://github.com/docker/login-action' # ref=v2
|
||||
2026-03-12T04:55:43.0341444Z cloning https://github.com/docker/login-action to /root/.cache/act/f88522bd445684e74cb6d7753bd78176885e45ce4b458abaf39d58df5ab4184e
|
||||
2026-03-12T04:55:44.2699899Z Unable to pull refs/heads/v2: worktree contains unstaged changes
|
||||
2026-03-12T04:55:44.2700402Z Cloned https://github.com/docker/login-action to /root/.cache/act/f88522bd445684e74cb6d7753bd78176885e45ce4b458abaf39d58df5ab4184e
|
||||
2026-03-12T04:55:44.3323096Z Checked out v2
|
||||
2026-03-12T04:55:44.3392365Z ☁ git clone 'https://github.com/docker/metadata-action' # ref=v4
|
||||
2026-03-12T04:55:44.3392856Z cloning https://github.com/docker/metadata-action to /root/.cache/act/56cfeb2dfe4931386e5074169fd368b371d8c16549b400f4b28605fa93ff4e61
|
||||
2026-03-12T04:56:09.3269344Z Cloned https://github.com/docker/metadata-action to /root/.cache/act/56cfeb2dfe4931386e5074169fd368b371d8c16549b400f4b28605fa93ff4e61
|
||||
2026-03-12T04:56:09.5398437Z Checked out v4
|
||||
2026-03-12T04:56:09.5493942Z ☁ git clone 'https://github.com/docker/build-push-action' # ref=v4
|
||||
2026-03-12T04:56:09.5494770Z cloning https://github.com/docker/build-push-action to /root/.cache/act/c2a42a35a31cd79bcf65f5ea52f316319ce5c18d677fd116460e0f2d572e581d
|
||||
2026-03-12T04:56:11.0540651Z Cloned https://github.com/docker/build-push-action to /root/.cache/act/c2a42a35a31cd79bcf65f5ea52f316319ce5c18d677fd116460e0f2d572e581d
|
||||
2026-03-12T04:56:11.3329352Z Checked out v4
|
||||
2026-03-12T04:56:11.3561072Z evaluating expression ''
|
||||
2026-03-12T04:56:11.3561681Z expression '' evaluated to 'true'
|
||||
2026-03-12T04:56:11.3561837Z ⭐ Run Main Checkout repository
|
||||
2026-03-12T04:56:11.3562049Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-03-12T04:56:11.3562257Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-03-12T04:56:11.3562394Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-03-12T04:56:11.3562525Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-03-12T04:56:11.3562641Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-03-12T04:56:11.3562776Z Extracting content to '/var/run/act'
|
||||
2026-03-12T04:56:11.3686518Z expression '${{ github.token }}' rewritten to 'format('{0}', github.token)'
|
||||
2026-03-12T04:56:11.3686898Z evaluating expression 'format('{0}', github.token)'
|
||||
2026-03-12T04:56:11.3687327Z expression 'format('{0}', github.token)' evaluated to '%!t(string=***)'
|
||||
2026-03-12T04:56:11.3687990Z expression '${{ github.repository }}' rewritten to 'format('{0}', github.repository)'
|
||||
2026-03-12T04:56:11.3688123Z evaluating expression 'format('{0}', github.repository)'
|
||||
2026-03-12T04:56:11.3688360Z expression 'format('{0}', github.repository)' evaluated to '%!t(string=savethenurse/ai-media-hub)'
|
||||
2026-03-12T04:56:11.3688613Z type=remote-action actionDir=/root/.cache/act/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6 actionPath= workdir=/workspace/savethenurse/ai-media-hub actionCacheDir=/root/.cache/act actionName=656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6 containerActionDir=/var/run/act/actions/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6
|
||||
2026-03-12T04:56:11.3688820Z /var/run/act/actions/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6
|
||||
2026-03-12T04:56:11.3689050Z Removing /root/.cache/act/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6/.gitignore before docker cp
|
||||
2026-03-12T04:56:11.3690405Z 🐳 docker cp src=/root/.cache/act/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6/ dst=/var/run/act/actions/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6/
|
||||
2026-03-12T04:56:11.3691406Z Writing tarball /tmp/act2992138607 from /root/.cache/act/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6/
|
||||
2026-03-12T04:56:11.3691597Z Stripping prefix:/root/.cache/act/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6/ src:/root/.cache/act/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6/
|
||||
2026-03-12T04:56:11.5090748Z Extracting content from '/tmp/act2992138607' to '/var/run/act/actions/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6/'
|
||||
2026-03-12T04:56:11.6685025Z executing remote job container: [node /var/run/act/actions/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6/dist/index.js]
|
||||
2026-03-12T04:56:11.6685853Z 🐳 docker exec cmd=[node /var/run/act/actions/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6/dist/index.js] user= workdir=
|
||||
2026-03-12T04:56:11.6686210Z Exec command '[node /var/run/act/actions/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6/dist/index.js]'
|
||||
2026-03-12T04:56:11.6686946Z Working directory '/workspace/savethenurse/ai-media-hub'
|
||||
2026-03-12T04:56:13.1858559Z (node:19) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
|
||||
2026-03-12T04:56:13.1858822Z (Use `node --trace-deprecation ...` to show where the warning was created)
|
||||
2026-03-12T04:56:13.1871551Z ::add-matcher::/run/act/actions/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6/dist/problem-matcher.json
|
||||
2026-03-12T04:56:13.1876566Z Syncing repository: savethenurse/ai-media-hub
|
||||
2026-03-12T04:56:13.1880377Z ::group::Getting Git version info
|
||||
2026-03-12T04:56:13.1880648Z Working directory is '/workspace/savethenurse/ai-media-hub'
|
||||
2026-03-12T04:56:13.1929523Z [command]/usr/bin/git version
|
||||
2026-03-12T04:56:13.2552429Z git version 2.53.0
|
||||
2026-03-12T04:56:13.2587196Z ::endgroup::
|
||||
2026-03-12T04:56:13.3046233Z Temporarily overriding HOME='/tmp/f3cdd707-f1b8-4d85-b62f-b81323c78f00' before making global git config changes
|
||||
2026-03-12T04:56:13.3046623Z Adding repository directory to the temporary git global config as a safe directory
|
||||
2026-03-12T04:56:13.3051475Z [command]/usr/bin/git config --global --add safe.directory /workspace/savethenurse/ai-media-hub
|
||||
2026-03-12T04:56:13.3109293Z Deleting the contents of '/workspace/savethenurse/ai-media-hub'
|
||||
2026-03-12T04:56:13.3118907Z ::group::Initializing the repository
|
||||
2026-03-12T04:56:13.3124507Z [command]/usr/bin/git init /workspace/savethenurse/ai-media-hub
|
||||
2026-03-12T04:56:13.3264254Z hint: Using 'master' as the name for the initial branch. This default branch name
|
||||
2026-03-12T04:56:13.3264682Z hint: will change to "main" in Git 3.0. To configure the initial branch name
|
||||
2026-03-12T04:56:13.3265042Z hint: to use in all of your new repositories, which will suppress this warning,
|
||||
2026-03-12T04:56:13.3265216Z hint: call:
|
||||
2026-03-12T04:56:13.3265330Z hint:
|
||||
2026-03-12T04:56:13.3265444Z hint: git config --global init.defaultBranch <name>
|
||||
2026-03-12T04:56:13.3265558Z hint:
|
||||
2026-03-12T04:56:13.3265679Z hint: Names commonly chosen instead of 'master' are 'main', 'trunk' and
|
||||
2026-03-12T04:56:13.3265891Z hint: 'development'. The just-created branch can be renamed via this command:
|
||||
2026-03-12T04:56:13.3266032Z hint:
|
||||
2026-03-12T04:56:13.3266234Z hint: git branch -m <name>
|
||||
2026-03-12T04:56:13.3266350Z hint:
|
||||
2026-03-12T04:56:13.3266618Z hint: Disable this message with "git config set advice.defaultBranchName false"
|
||||
2026-03-12T04:56:13.3266951Z Initialized empty Git repository in /workspace/savethenurse/ai-media-hub/.git/
|
||||
2026-03-12T04:56:13.3281364Z [command]/usr/bin/git remote add origin https://git.savethenurse.com/savethenurse/ai-media-hub
|
||||
2026-03-12T04:56:13.3511790Z ::endgroup::
|
||||
2026-03-12T04:56:13.3512307Z ::group::Disabling automatic garbage collection
|
||||
2026-03-12T04:56:13.3516533Z [command]/usr/bin/git config --local gc.auto 0
|
||||
2026-03-12T04:56:13.3565615Z ::endgroup::
|
||||
2026-03-12T04:56:13.3566057Z ::group::Setting up auth
|
||||
2026-03-12T04:56:13.3578984Z [command]/usr/bin/git config --local --name-only --get-regexp core\.sshCommand
|
||||
2026-03-12T04:56:13.3632373Z [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-12T04:56:13.5190386Z [command]/usr/bin/git config --local --name-only --get-regexp http\.https\:\/\/git\.savethenurse\.com\/\.extraheader
|
||||
2026-03-12T04:56:13.5224269Z [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-12T04:56:13.5591573Z [command]/usr/bin/git config --local http.https://git.savethenurse.com/.extraheader AUTHORIZATION: basic ***
|
||||
2026-03-12T04:56:13.5654788Z ::endgroup::
|
||||
2026-03-12T04:56:13.5655116Z ::group::Fetching the repository
|
||||
2026-03-12T04:56:13.5664866Z [command]/usr/bin/git -c protocol.version=2 fetch --no-tags --prune --progress --no-recurse-submodules --depth=1 origin +8562c44897ad1242bf5e58a11da250a04f1db7eb:refs/remotes/origin/main
|
||||
2026-03-12T04:56:14.9322391Z remote: Enumerating objects: 11, done.
|
||||
2026-03-12T04:56:14.9323560Z remote: Counting objects: 9% (1/11)
|
||||
remote: Counting objects: 18% (2/11)
|
||||
remote: Counting objects: 27% (3/11)
|
||||
remote: Counting objects: 36% (4/11)
|
||||
remote: Counting objects: 45% (5/11)
|
||||
remote: Counting objects: 54% (6/11)
|
||||
remote: Counting objects: 63% (7/11)
|
||||
remote: Counting objects: 72% (8/11)
|
||||
remote: Counting objects: 81% (9/11)
|
||||
remote: Counting objects: 90% (10/11)
|
||||
remote: Counting objects: 100% (11/11)
|
||||
remote: Counting objects: 100% (11/11), done.
|
||||
2026-03-12T04:56:14.9324245Z remote: Compressing objects: 11% (1/9)
|
||||
remote: Compressing objects: 22% (2/9)
|
||||
remote: Compressing objects: 33% (3/9)
|
||||
remote: Compressing objects: 44% (4/9)
|
||||
remote: Compressing objects: 55% (5/9)
|
||||
remote: Compressing objects: 66% (6/9)
|
||||
remote: Compressing objects: 77% (7/9)
|
||||
remote: Compressing objects: 88% (8/9)
|
||||
remote: Compressing objects: 100% (9/9)
|
||||
remote: Compressing objects: 100% (9/9), done.
|
||||
2026-03-12T04:56:14.9329339Z remote: Total 11 (delta 1), reused 0 (delta 0), pack-reused 0 (from 0)
|
||||
2026-03-12T04:56:14.9411682Z From https://git.savethenurse.com/savethenurse/ai-media-hub
|
||||
2026-03-12T04:56:14.9412330Z * [new ref] 8562c44897ad1242bf5e58a11da250a04f1db7eb -> origin/main
|
||||
2026-03-12T04:56:14.9454107Z ::endgroup::
|
||||
2026-03-12T04:56:14.9454812Z ::group::Determining the checkout info
|
||||
2026-03-12T04:56:14.9460955Z ::endgroup::
|
||||
2026-03-12T04:56:14.9461523Z ::group::Checking out the ref
|
||||
2026-03-12T04:56:14.9468934Z [command]/usr/bin/git checkout --progress --force -B main refs/remotes/origin/main
|
||||
2026-03-12T04:56:14.9535003Z Switched to a new branch 'main'
|
||||
2026-03-12T04:56:14.9537994Z branch 'main' set up to track 'origin/main'.
|
||||
2026-03-12T04:56:14.9548923Z ::endgroup::
|
||||
2026-03-12T04:56:14.9601096Z [command]/usr/bin/git log -1 --format='%H'
|
||||
2026-03-12T04:56:14.9646452Z '8562c44897ad1242bf5e58a11da250a04f1db7eb'
|
||||
2026-03-12T04:56:14.9669706Z ::remove-matcher owner=checkout-git::
|
||||
2026-03-12T04:56:15.4673087Z Setup go version spec 1.22
|
||||
2026-03-12T04:56:15.4689576Z Attempting to download 1.22...
|
||||
2026-03-12T04:56:15.4828741Z (node:110) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
|
||||
2026-03-12T04:56:15.4829167Z (Use `node --trace-deprecation ...` to show where the warning was created)
|
||||
2026-03-12T04:56:16.1161554Z matching 1.22...
|
||||
2026-03-12T04:56:16.1241537Z Acquiring 1.22.12 from https://github.com/actions/go-versions/releases/download/1.22.12-13149489080/go-1.22.12-linux-x64.tar.gz
|
||||
2026-03-12T04:56:18.6285622Z Extracting Go...
|
||||
2026-03-12T04:56:18.7069441Z [command]/usr/bin/tar xz --warning=no-unknown-keyword --overwrite -C /tmp/1224119c-df3b-4d47-828e-7fcffb79fa65 -f /tmp/608b9f6c-2a0e-46d6-af1d-8f9937f5d61b
|
||||
2026-03-12T04:56:21.8672442Z Successfully extracted go to /tmp/1224119c-df3b-4d47-828e-7fcffb79fa65
|
||||
2026-03-12T04:56:21.8673341Z Adding to the cache ...
|
||||
2026-03-12T04:56:32.3902093Z Successfully cached go to /opt/hostedtoolcache/go/1.22.12/x64
|
||||
2026-03-12T04:56:32.3905807Z Added go to the path
|
||||
2026-03-12T04:56:32.3907647Z Successfully set up Go version 1.22
|
||||
2026-03-12T04:56:32.5359303Z [command]/opt/hostedtoolcache/go/1.22.12/x64/bin/go env GOMODCACHE
|
||||
2026-03-12T04:56:32.5411115Z [command]/opt/hostedtoolcache/go/1.22.12/x64/bin/go env GOCACHE
|
||||
2026-03-12T04:56:32.5563344Z /root/go/pkg/mod
|
||||
2026-03-12T04:56:32.5571140Z /root/.cache/go-build
|
||||
2026-03-12T05:01:10.5150732Z ::warning::Failed to restore: getCacheEntry failed: connect ETIMEDOUT 172.18.0.8:42323
|
||||
2026-03-12T05:01:10.5157159Z Cache is not found
|
||||
2026-03-12T05:01:10.5159265Z ##[add-matcher]/run/act/actions/fd7d62239e994546e01f58df7ed12dc03dc4f9370800b8ff8736bf90b80e2db5/matchers.json
|
||||
2026-03-12T05:01:10.5159535Z go version go1.22.12 linux/amd64
|
||||
2026-03-12T05:01:10.5159831Z
|
||||
2026-03-12T05:01:10.5160567Z ::group::go env
|
||||
2026-03-12T05:01:11.4990400Z GO111MODULE=''
|
||||
2026-03-12T05:01:11.4991068Z GOARCH='amd64'
|
||||
2026-03-12T05:01:11.4991571Z GOBIN=''
|
||||
2026-03-12T05:01:11.4991845Z GOCACHE='/root/.cache/go-build'
|
||||
2026-03-12T05:01:11.4992347Z GOENV='/root/.config/go/env'
|
||||
2026-03-12T05:01:11.4992728Z GOEXE=''
|
||||
2026-03-12T05:01:11.4993325Z GOEXPERIMENT=''
|
||||
2026-03-12T05:01:11.4993841Z GOFLAGS=''
|
||||
2026-03-12T05:01:11.4994162Z GOHOSTARCH='amd64'
|
||||
2026-03-12T05:01:11.4994593Z GOHOSTOS='linux'
|
||||
2026-03-12T05:01:11.4994931Z GOINSECURE=''
|
||||
2026-03-12T05:01:11.4995133Z GOMODCACHE='/root/go/pkg/mod'
|
||||
2026-03-12T05:01:11.4995531Z GONOPROXY=''
|
||||
2026-03-12T05:01:11.4995736Z GONOSUMDB=''
|
||||
2026-03-12T05:01:11.4996235Z GOOS='linux'
|
||||
2026-03-12T05:01:11.4996451Z GOPATH='/root/go'
|
||||
2026-03-12T05:01:11.4996816Z GOPRIVATE=''
|
||||
2026-03-12T05:01:11.4997186Z GOPROXY='https://proxy.golang.org,direct'
|
||||
2026-03-12T05:01:11.4997648Z GOROOT='/opt/hostedtoolcache/go/1.22.12/x64'
|
||||
2026-03-12T05:01:11.4997962Z GOSUMDB='sum.golang.org'
|
||||
2026-03-12T05:01:11.4998385Z GOTMPDIR=''
|
||||
2026-03-12T05:01:11.4998667Z GOTOOLCHAIN='auto'
|
||||
2026-03-12T05:01:11.4998889Z GOTOOLDIR='/opt/hostedtoolcache/go/1.22.12/x64/pkg/tool/linux_amd64'
|
||||
2026-03-12T05:01:11.4999418Z GOVCS=''
|
||||
2026-03-12T05:01:11.4999644Z GOVERSION='go1.22.12'
|
||||
2026-03-12T05:01:11.4999854Z GCCGO='gccgo'
|
||||
2026-03-12T05:01:11.5000352Z GOAMD64='v1'
|
||||
2026-03-12T05:01:11.5000608Z AR='ar'
|
||||
2026-03-12T05:01:11.5000831Z CC='gcc'
|
||||
2026-03-12T05:01:11.5000995Z CXX='g++'
|
||||
2026-03-12T05:01:11.5001175Z CGO_ENABLED='1'
|
||||
2026-03-12T05:01:11.5001415Z GOMOD='/workspace/savethenurse/ai-media-hub/go.mod'
|
||||
2026-03-12T05:01:11.5001577Z GOWORK=''
|
||||
2026-03-12T05:01:11.5001794Z CGO_CFLAGS='-O2 -g'
|
||||
2026-03-12T05:01:11.5001944Z CGO_CPPFLAGS=''
|
||||
2026-03-12T05:01:11.5002148Z CGO_CXXFLAGS='-O2 -g'
|
||||
2026-03-12T05:01:11.5002375Z CGO_FFLAGS='-O2 -g'
|
||||
2026-03-12T05:01:11.5002852Z CGO_LDFLAGS='-O2 -g'
|
||||
2026-03-12T05:01:11.5003402Z PKG_CONFIG='pkg-config'
|
||||
2026-03-12T05:01:11.5003529Z GOGCCFLAGS='-fPIC -m64 -pthread -Wl,--no-gc-sections -fmessage-length=0 -ffile-prefix-map=/tmp/go-build3840878617=/tmp/go-build -gno-record-gcc-switches'
|
||||
2026-03-12T05:01:11.5003733Z
|
||||
2026-03-12T05:01:11.5004090Z ::endgroup::
|
||||
2026-03-12T05:01:12.7614835Z Logging into git.savethenurse.com...
|
||||
2026-03-12T05:01:14.3447631Z ::error::Error response from daemon: Get "https://git.savethenurse.com/v2/": unauthorized
|
||||
2026-03-12T05:01:14.3545808Z ❌ Failure - Main Log in to Gitea Container Registry
|
||||
2026-03-12T05:01:14.3611448Z exitcode '1': failure
|
||||
2026-03-12T05:01:14.4107126Z skipping post step for 'Build and push Docker image'; main step was skipped
|
||||
2026-03-12T05:01:14.4107652Z skipping post step for 'Extract metadata (tags, labels) for Docker'; main step was skipped
|
||||
2026-03-12T05:01:14.4292529Z expression '${{ github.actor }}' rewritten to 'format('{0}', github.actor)'
|
||||
2026-03-12T05:01:14.4293822Z evaluating expression 'format('{0}', github.actor)'
|
||||
2026-03-12T05:01:14.4294436Z expression 'format('{0}', github.actor)' evaluated to '%!t(string=savethenurse)'
|
||||
2026-03-12T05:01:14.4294744Z expression '${{ secrets.GITEA_TOKEN }}' rewritten to 'format('{0}', secrets.GITEA_TOKEN)'
|
||||
2026-03-12T05:01:14.4295036Z evaluating expression 'format('{0}', secrets.GITEA_TOKEN)'
|
||||
2026-03-12T05:01:14.4295356Z expression 'format('{0}', secrets.GITEA_TOKEN)' evaluated to '%!t(string=***)'
|
||||
2026-03-12T05:01:14.4384607Z evaluating expression 'always()'
|
||||
2026-03-12T05:01:14.4385579Z expression 'always()' evaluated to 'true'
|
||||
2026-03-12T05:01:14.4386228Z ⭐ Run Post Log in to Gitea Container Registry
|
||||
2026-03-12T05:01:14.4386626Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-03-12T05:01:14.4387172Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-03-12T05:01:14.4387437Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-03-12T05:01:14.4387900Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-03-12T05:01:14.4388142Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-03-12T05:01:14.4388708Z Extracting content to '/var/run/act'
|
||||
2026-03-12T05:01:14.4420093Z run post step for 'Log in to Gitea Container Registry'
|
||||
2026-03-12T05:01:14.4425571Z executing remote job container: [node /var/run/act/actions/f88522bd445684e74cb6d7753bd78176885e45ce4b458abaf39d58df5ab4184e/dist/index.js]
|
||||
2026-03-12T05:01:14.4426817Z 🐳 docker exec cmd=[node /var/run/act/actions/f88522bd445684e74cb6d7753bd78176885e45ce4b458abaf39d58df5ab4184e/dist/index.js] user= workdir=
|
||||
2026-03-12T05:01:14.4427315Z Exec command '[node /var/run/act/actions/f88522bd445684e74cb6d7753bd78176885e45ce4b458abaf39d58df5ab4184e/dist/index.js]'
|
||||
2026-03-12T05:01:14.4428468Z Working directory '/workspace/savethenurse/ai-media-hub'
|
||||
2026-03-12T05:01:14.6227094Z [command]/usr/bin/docker logout git.savethenurse.com
|
||||
2026-03-12T05:01:14.6482525Z Removing login credentials for git.savethenurse.com
|
||||
2026-03-12T05:01:14.6641219Z ✅ Success - Post Log in to Gitea Container Registry
|
||||
2026-03-12T05:01:14.6948569Z evaluating expression 'success()'
|
||||
2026-03-12T05:01:14.6949763Z expression 'success()' evaluated to 'false'
|
||||
2026-03-12T05:01:14.6950307Z Skipping step 'Setup Go' due to 'success()'
|
||||
2026-03-12T05:01:14.7182343Z evaluating expression 'always()'
|
||||
2026-03-12T05:01:14.7183728Z expression 'always()' evaluated to 'true'
|
||||
2026-03-12T05:01:14.7184058Z ⭐ Run Post Checkout repository
|
||||
2026-03-12T05:01:14.7184657Z Writing entry to tarball workflow/outputcmd.txt len:0
|
||||
2026-03-12T05:01:14.7185204Z Writing entry to tarball workflow/statecmd.txt len:0
|
||||
2026-03-12T05:01:14.7185470Z Writing entry to tarball workflow/pathcmd.txt len:0
|
||||
2026-03-12T05:01:14.7185949Z Writing entry to tarball workflow/envs.txt len:0
|
||||
2026-03-12T05:01:14.7186192Z Writing entry to tarball workflow/SUMMARY.md len:0
|
||||
2026-03-12T05:01:14.7186631Z Extracting content to '/var/run/act'
|
||||
2026-03-12T05:01:14.7214784Z run post step for 'Checkout repository'
|
||||
2026-03-12T05:01:14.7215581Z executing remote job container: [node /var/run/act/actions/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6/dist/index.js]
|
||||
2026-03-12T05:01:14.7225346Z 🐳 docker exec cmd=[node /var/run/act/actions/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6/dist/index.js] user= workdir=
|
||||
2026-03-12T05:01:14.7225587Z Exec command '[node /var/run/act/actions/656c968832d266db0fe5f8f638000eea9e6a5501569cb9a87c7fdaefedb6a0b6/dist/index.js]'
|
||||
2026-03-12T05:01:14.7225978Z Working directory '/workspace/savethenurse/ai-media-hub'
|
||||
2026-03-12T05:01:14.8609991Z (node:232) [DEP0040] DeprecationWarning: The `punycode` module is deprecated. Please use a userland alternative instead.
|
||||
2026-03-12T05:01:14.8610867Z (Use `node --trace-deprecation ...` to show where the warning was created)
|
||||
241
main.go
241
main.go
@@ -1,241 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"strings"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/websocket/v2"
|
||||
)
|
||||
|
||||
// Config - 환경 변수 등
|
||||
const (
|
||||
Port = ":8000"
|
||||
)
|
||||
|
||||
type SearchRequest struct {
|
||||
Query string `json:"query"`
|
||||
}
|
||||
|
||||
type SearchResult struct {
|
||||
Title string `json:"title"`
|
||||
Link string `json:"link"`
|
||||
Thumbnail string `json:"thumbnail"`
|
||||
Snippet string `json:"snippet"`
|
||||
Source string `json:"source"`
|
||||
AiRecommended bool `json:"ai_recommended"`
|
||||
AiReasoning string `json:"ai_reasoning"`
|
||||
}
|
||||
|
||||
type DownloadRequest struct {
|
||||
URL string `json:"url"`
|
||||
Start string `json:"start"`
|
||||
End string `json:"end"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
app := fiber.New()
|
||||
|
||||
// Static Files (Frontend)
|
||||
app.Static("/", "./index.html")
|
||||
|
||||
// Middleware for WebSocket upgrade
|
||||
app.Use("/ws", func(c *fiber.Ctx) error {
|
||||
if websocket.IsWebSocketUpgrade(c) {
|
||||
return c.Next()
|
||||
}
|
||||
return fiber.ErrUpgradeRequired
|
||||
})
|
||||
|
||||
// WebSocket handler (for progress updates)
|
||||
app.Get("/ws", websocket.New(func(c *websocket.Conn) {
|
||||
for {
|
||||
// keep connection alive or handle client messages
|
||||
if _, _, err := c.ReadMessage(); err != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
}))
|
||||
|
||||
app.Post("/api/search", handleSearch)
|
||||
app.Post("/api/download", handleDownload)
|
||||
|
||||
log.Printf("Server starting on %s", Port)
|
||||
if err := app.Listen(Port); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// 1차: Google CSE를 통한 메타데이터/썸네일 수집
|
||||
// 2차: Gemini 2.5 Flash를 통한 비전 분석
|
||||
func handleSearch(c *fiber.Ctx) error {
|
||||
var req SearchRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(400).JSON(fiber.Map{"error": "Invalid request"})
|
||||
}
|
||||
|
||||
apiKey := os.Getenv("GOOGLE_API_KEY")
|
||||
cx := os.Getenv("GOOGLE_CSE_ID")
|
||||
geminiKey := os.Getenv("GEMINI_API_KEY")
|
||||
|
||||
// 1. Google Custom Search API 호출 (Mock logic if keys are missing)
|
||||
var rawResults []SearchResult
|
||||
if apiKey != "" && cx != "" {
|
||||
rawResults = fetchGoogleCSE(req.Query, apiKey, cx)
|
||||
} else {
|
||||
// Mock Data for demonstration
|
||||
rawResults = []SearchResult{
|
||||
{Title: "Sample Video 1", Link: "https://youtube.com", Thumbnail: "https://via.placeholder.com/300x200", Source: "YouTube", Snippet: "Cyberpunk city night"},
|
||||
{Title: "Sample TikTok", Link: "https://tiktok.com", Thumbnail: "https://via.placeholder.com/300x200?text=TikTok", Source: "TikTok", Snippet: "Neon lights"},
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Gemini 2.5 Flash API로 멀티모달 프롬프팅
|
||||
if geminiKey != "" && len(rawResults) > 0 {
|
||||
rawResults = analyzeWithGemini(req.Query, rawResults, geminiKey)
|
||||
} else {
|
||||
// Mock logic: mark first as recommended
|
||||
if len(rawResults) > 0 {
|
||||
rawResults[0].AiRecommended = true
|
||||
rawResults[0].AiReasoning = "입력한 [ " + req.Query + " ] 키워드와 분위기가 가장 흡사해 보입니다."
|
||||
}
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{"results": rawResults})
|
||||
}
|
||||
|
||||
// Google Custom Search API 연동
|
||||
func fetchGoogleCSE(query, apiKey, cx string) []SearchResult {
|
||||
// 실제 CSE API 요청
|
||||
url := fmt.Sprintf("https://www.googleapis.com/customsearch/v1?key=%s&cx=%s&q=%s&searchType=image&num=10", apiKey, cx, query)
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
log.Println("CSE Fetch error:", err)
|
||||
return nil
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var cseResp struct {
|
||||
Items []struct {
|
||||
Title string `json:"title"`
|
||||
Link string `json:"link"` // image url
|
||||
Snippet string `json:"snippet"`
|
||||
Image struct {
|
||||
ContextLink string `json:"contextLink"` // original page
|
||||
} `json:"image"`
|
||||
} `json:"items"`
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&cseResp); err != nil {
|
||||
log.Println("CSE Decode error:", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
var results []SearchResult
|
||||
for _, item := range cseResp.Items {
|
||||
source := "Unknown"
|
||||
if strings.Contains(item.Image.ContextLink, "youtube.com") {
|
||||
source = "YouTube"
|
||||
} else if strings.Contains(item.Image.ContextLink, "tiktok.com") {
|
||||
source = "TikTok"
|
||||
} else if strings.Contains(item.Image.ContextLink, "envato.com") {
|
||||
source = "Envato Elements"
|
||||
} else if strings.Contains(item.Image.ContextLink, "artgrid.io") {
|
||||
source = "Artgrid"
|
||||
}
|
||||
|
||||
results = append(results, SearchResult{
|
||||
Title: item.Title,
|
||||
Link: item.Image.ContextLink,
|
||||
Thumbnail: item.Link,
|
||||
Snippet: item.Snippet,
|
||||
Source: source,
|
||||
})
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
// Gemini 2.5 Flash를 이용한 이미지 기반 추천 로직 (멀티모달)
|
||||
func analyzeWithGemini(query string, items []SearchResult, apiKey string) []SearchResult {
|
||||
// 실제 환경에서는 각 썸네일 이미지를 Base64 인코딩하거나 파일 URI를 첨부하여 전송해야 하나
|
||||
// 구조적인 코드 예시로서 REST API 호출 포맷을 구현합니다.
|
||||
apiURL := fmt.Sprintf("https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent?key=%s", apiKey)
|
||||
|
||||
// 단순화를 위해 텍스트 프롬프트 형태로 썸네일 URL과 함께 전달
|
||||
prompt := fmt.Sprintf("사용자의 검색어: '%s'. 다음은 수집된 미디어 항목들의 썸네일 URL과 정보입니다. 이 중 검색어와 시각적으로 가장 잘 부합하는 것을 1~2개 고르고, 그 이유를 설명해줘. 입력 정보: ", query)
|
||||
for i, it := range items {
|
||||
prompt += fmt.Sprintf("[%d] 제목: %s, URL: %s ", i, it.Title, it.Thumbnail)
|
||||
}
|
||||
|
||||
bodyMap := map[string]interface{}{
|
||||
"contents": []map[string]interface{}{
|
||||
{
|
||||
"parts": []map[string]interface{}{
|
||||
{"text": prompt},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
b, _ := json.Marshal(bodyMap)
|
||||
|
||||
resp, err := http.Post(apiURL, "application/json", bytes.NewBuffer(b))
|
||||
if err != nil {
|
||||
log.Println("Gemini API Error:", err)
|
||||
return items
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, _ := io.ReadAll(resp.Body)
|
||||
respStr := string(respBody)
|
||||
|
||||
// 응답을 분석하여 AI Recommended 태그 부착 (단순 문자열 매칭 방식 예시)
|
||||
for i := range items {
|
||||
if strings.Contains(respStr, items[i].Title) || strings.Contains(respStr, fmt.Sprintf("[%d]", i)) {
|
||||
items[i].AiRecommended = true
|
||||
items[i].AiReasoning = "Gemini 2.5 Flash 분석: 사용자의 요구사항과 시각적으로 매칭도가 높다고 판단됨."
|
||||
}
|
||||
}
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
// Download request - yt-dlp 호출 (Python backend 역할)
|
||||
func handleDownload(c *fiber.Ctx) error {
|
||||
var req DownloadRequest
|
||||
if err := c.BodyParser(&req); err != nil {
|
||||
return c.Status(400).SendString("Invalid Input")
|
||||
}
|
||||
|
||||
// 컨테이너/호스트 내 NAS 마운트 경로 (예: /data/nas)
|
||||
outDir := "/data/nas"
|
||||
os.MkdirAll(outDir, os.ModePerm)
|
||||
|
||||
// yt-dlp 명령어 구성
|
||||
args := []string{"-o", outDir + "/%(title)s.%(ext)s", req.URL}
|
||||
|
||||
// 구간 크롭 옵션
|
||||
if req.Start != "" && req.End != "" {
|
||||
args = append(args, "--download-sections", fmt.Sprintf("*%s-%s", req.Start, req.End))
|
||||
}
|
||||
|
||||
// 비동기 실행
|
||||
go func(args []string) {
|
||||
cmd := exec.Command("yt-dlp", args...)
|
||||
// stdout 진행률 파싱하여 WebSocket으로 전송 가능 (여기서는 실행만 구현)
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
log.Println("Download failed:", err)
|
||||
} else {
|
||||
log.Println("Download complete")
|
||||
}
|
||||
}(args)
|
||||
|
||||
return c.JSON(fiber.Map{"status": "started"})
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
<?xml version="1.0"?>
|
||||
<Container version="2">
|
||||
<Name>AI-Media-Hub</Name>
|
||||
<Repository>git.savethenurse.com/savethenurse/ai-media-hub:latest</Repository>
|
||||
<Registry>https://git.savethenurse.com</Registry>
|
||||
<Network>bridge</Network>
|
||||
<MyIP></MyIP>
|
||||
<Shell>sh</Shell>
|
||||
<Privileged>false</Privileged>
|
||||
<Support></Support>
|
||||
<Project></Project>
|
||||
<Overview>AI 미디어 수집 및 인제스트 웹 대시보드 (Gemini 2.5 Flash 기반)</Overview>
|
||||
<Category>MediaApp: Video:</Category>
|
||||
<WebUI>http://[IP]:[PORT:8000]</WebUI>
|
||||
<TemplateURL></TemplateURL>
|
||||
<Icon>https://raw.githubusercontent.com/linuxserver/docker-templates/master/linuxserver.io/img/youtube-dl-icon.png</Icon>
|
||||
<ExtraParams></ExtraParams>
|
||||
<PostArgs></PostArgs>
|
||||
<CPUset></CPUset>
|
||||
<DateInstalled></DateInstalled>
|
||||
<DonateText></DonateText>
|
||||
<DonateLink></DonateLink>
|
||||
<Description>AI 미디어 수집 및 인제스트 웹 대시보드 (Gemini 2.5 Flash 기반)</Description>
|
||||
|
||||
<Networking>
|
||||
<Mode>bridge</Mode>
|
||||
<Publish>
|
||||
<Port>
|
||||
<HostPort>8282</HostPort>
|
||||
<ContainerPort>8000</ContainerPort>
|
||||
<Protocol>tcp</Protocol>
|
||||
</Port>
|
||||
</Publish>
|
||||
</Networking>
|
||||
<Data>
|
||||
<Volume>
|
||||
<HostDir>/mnt/user/media/ingest/</HostDir>
|
||||
<ContainerDir>/data/nas</ContainerDir>
|
||||
<Mode>rw</Mode>
|
||||
</Volume>
|
||||
</Data>
|
||||
<Environment>
|
||||
<Variable>
|
||||
<Value></Value>
|
||||
<Name>GOOGLE_API_KEY</Name>
|
||||
<Mode/>
|
||||
</Variable>
|
||||
<Variable>
|
||||
<Value></Value>
|
||||
<Name>GOOGLE_CSE_ID</Name>
|
||||
<Mode/>
|
||||
</Variable>
|
||||
<Variable>
|
||||
<Value></Value>
|
||||
<Name>GEMINI_API_KEY</Name>
|
||||
<Mode/>
|
||||
</Variable>
|
||||
</Environment>
|
||||
<Labels/>
|
||||
<Config Name="WebUI Port" Target="8000" Default="8282" Mode="tcp" Description="Host Port for WebUI" Type="Port" Display="always" Required="true" Mask="false">8282</Config>
|
||||
<Config Name="NAS Mount Path" Target="/data/nas" Default="/mnt/user/media/ingest/" Mode="rw" Description="Host path to map to NAS ingest directory" Type="Path" Display="always" Required="true" Mask="false">/mnt/user/media/ingest/</Config>
|
||||
<Config Name="GOOGLE_API_KEY" Target="GOOGLE_API_KEY" Default="" Mode="" Description="Google Custom Search API Key" Type="Variable" Display="always" Required="false" Mask="true"></Config>
|
||||
<Config Name="GOOGLE_CSE_ID" Target="GOOGLE_CSE_ID" Default="" Mode="" Description="Google Custom Search Engine ID" Type="Variable" Display="always" Required="false" Mask="true"></Config>
|
||||
<Config Name="GEMINI_API_KEY" Target="GEMINI_API_KEY" Default="" Mode="" Description="Gemini 2.5 Flash API Key" Type="Variable" Display="always" Required="false" Mask="true"></Config>
|
||||
</Container>
|
||||
Reference in New Issue
Block a user