From 66d95e0fb40ce2ff7165acbb6a1c8957fe3cb481 Mon Sep 17 00:00:00 2001 From: KV-Tube Deployer Date: Sun, 22 Feb 2026 19:52:51 +0700 Subject: [PATCH] fix: use NEXT_PUBLIC_API_URL and add yt-dlp to fix backend info fetching --- backend/Dockerfile | 4 ++- docker-compose.yml | 2 ++ fix_urls.js | 33 ++++++++++++++++++++++++ frontend/app/api/download/route.ts | 8 +++--- frontend/app/api/stream/route.ts | 12 ++++----- frontend/app/channel/[id]/page.tsx | 4 +-- frontend/app/feed/library/page.tsx | 4 +-- frontend/app/feed/subscriptions/page.tsx | 10 +++---- frontend/app/search/page.tsx | 2 +- 9 files changed, 58 insertions(+), 21 deletions(-) create mode 100644 fix_urls.js diff --git a/backend/Dockerfile b/backend/Dockerfile index 120cf39..861a92c 100755 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -12,7 +12,9 @@ RUN CGO_ENABLED=1 GOOS=linux go build -o kv-tube . FROM alpine:latest -RUN apk add --no-cache ca-certificates ffmpeg curl +RUN apk add --no-cache ca-certificates ffmpeg curl python3 py3-pip && \ + curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp -o /usr/local/bin/yt-dlp && \ + chmod a+rx /usr/local/bin/yt-dlp WORKDIR /app diff --git a/docker-compose.yml b/docker-compose.yml index beffa9b..ff93e80 100755 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -28,6 +28,8 @@ services: restart: unless-stopped ports: - "5011:3000" + environment: + - NEXT_PUBLIC_API_URL=http://kv-tube-backend:8080 depends_on: - kv-tube-backend labels: diff --git a/fix_urls.js b/fix_urls.js new file mode 100644 index 0000000..e2df077 --- /dev/null +++ b/fix_urls.js @@ -0,0 +1,33 @@ +const fs = require('fs'); +const path = require('path'); +const glob = require('glob'); + +// Use simple find approach for ts/tsx files +const execSync = require('child_process').execSync; +const files = execSync('find frontend -type f -name "*.ts" -o -name "*.tsx"').toString().trim().split('\n'); + +files.forEach(file => { + let content = fs.readFileSync(file, 'utf8'); + // Revert the bad perl replacement + content = content.replace(/\$\{process\.env\.NEXT_PUBLIC_API_URL \|\| 'http:\/\/127\.0\.0\.1:8080'\}/g, 'http://127.0.0.1:8080'); + + // Apply proper replacement: + // const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8080'; + // fetch(`http://127.0.0.1:8080...`) -> fetch(`${process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8080'}...`) + + // Actually, only fetch calls need the environment var, but we can just replace 'http://127.0.0.1:8080' + // with API_BASE if we import or define API_BASE. Since it's easier, let's just replace the raw literal string. + + content = content.replace(/'http:\/\/127\.0\.0\.1:8080/g, '`${process.env.NEXT_PUBLIC_API_URL || \'http://127.0.0.1:8080\'}'); + content = content.replace(/"http:\/\/127\.0\.0\.1:8080/g, '`${process.env.NEXT_PUBLIC_API_URL || \'http://127.0.0.1:8080\'}'); + content = content.replace(/`http:\/\/127\.0\.0\.1:8080/g, '`${process.env.NEXT_PUBLIC_API_URL || \'http://127.0.0.1:8080\'}'); + + // We have to be careful not to double replace. The above will turn 'http://127.0.0.1:8080/api...' into + // `${process.env...}/api...` + + // Let's actually just revert the breaking perl replace first: + // It looks like: process.env.NEXT_PUBLIC_API_URL || '${process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8080'}' + + fs.writeFileSync(file, content); +}); +console.log("Done"); diff --git a/frontend/app/api/download/route.ts b/frontend/app/api/download/route.ts index d24195e..663142b 100755 --- a/frontend/app/api/download/route.ts +++ b/frontend/app/api/download/route.ts @@ -5,7 +5,7 @@ const API_BASE = process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8080'; export async function GET(request: NextRequest) { const videoId = request.nextUrl.searchParams.get('v'); const formatId = request.nextUrl.searchParams.get('f'); - + if (!videoId) { return NextResponse.json({ error: 'No video ID' }, { status: 400 }); } @@ -15,13 +15,13 @@ export async function GET(request: NextRequest) { const res = await fetch(url, { cache: 'no-store', }); - + const data = await res.json(); - + if (!res.ok) { return NextResponse.json({ error: data.error || 'Download failed' }, { status: 500 }); } - + return NextResponse.json(data); } catch (error) { return NextResponse.json({ error: 'Failed to get download link' }, { status: 500 }); diff --git a/frontend/app/api/stream/route.ts b/frontend/app/api/stream/route.ts index 2e1052a..f6c3296 100755 --- a/frontend/app/api/stream/route.ts +++ b/frontend/app/api/stream/route.ts @@ -2,25 +2,25 @@ import { NextRequest, NextResponse } from 'next/server'; export async function GET(request: NextRequest) { const videoId = request.nextUrl.searchParams.get('v'); - + if (!videoId) { return NextResponse.json({ error: 'No video ID' }, { status: 400 }); } try { - const res = await fetch(`http://127.0.0.1:8080/api/get_stream_info?v=${videoId}`, { + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8080'}/api/get_stream_info?v=${videoId}`, { cache: 'no-store', }); - + if (!res.ok) { return NextResponse.json({ error: 'Failed to fetch' }, { status: 500 }); } - + const data = await res.json(); const streamUrl = data.original_url || data.stream_url; const proxyUrl = streamUrl ? `/api/proxy-stream?url=${encodeURIComponent(streamUrl)}` : null; - - return NextResponse.json({ + + return NextResponse.json({ streamUrl: proxyUrl, title: data.title, thumbnail: data.thumbnail diff --git a/frontend/app/channel/[id]/page.tsx b/frontend/app/channel/[id]/page.tsx index 79cc8db..3683f9b 100755 --- a/frontend/app/channel/[id]/page.tsx +++ b/frontend/app/channel/[id]/page.tsx @@ -29,7 +29,7 @@ function formatSubscribers(count: number): string { async function getChannelInfo(id: string) { try { - const res = await fetch(`http://127.0.0.1:8080/api/channel/info?id=${id}`, { cache: 'no-store' }); + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8080'}/api/channel/info?id=${id}`, { cache: 'no-store' }); if (!res.ok) return null; return res.json() as Promise; } catch (e) { @@ -40,7 +40,7 @@ async function getChannelInfo(id: string) { async function getChannelVideos(id: string) { try { - const res = await fetch(`http://127.0.0.1:8080/api/channel/videos?id=${id}&limit=30`, { cache: 'no-store' }); + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8080'}/api/channel/videos?id=${id}&limit=30`, { cache: 'no-store' }); if (!res.ok) return []; return res.json() as Promise; } catch (e) { diff --git a/frontend/app/feed/library/page.tsx b/frontend/app/feed/library/page.tsx index 070a647..a26cfde 100755 --- a/frontend/app/feed/library/page.tsx +++ b/frontend/app/feed/library/page.tsx @@ -18,7 +18,7 @@ interface Subscription { async function getHistory() { try { - const res = await fetch('http://127.0.0.1:8080/api/history?limit=20', { cache: 'no-store' }); + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8080'}/api/history?limit=20`, { cache: 'no-store' }); if (!res.ok) return []; return res.json() as Promise; } catch { @@ -28,7 +28,7 @@ async function getHistory() { async function getSubscriptions() { try { - const res = await fetch('http://127.0.0.1:8080/api/subscriptions', { cache: 'no-store' }); + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8080'}/api/subscriptions`, { cache: 'no-store' }); if (!res.ok) return []; return res.json() as Promise; } catch { diff --git a/frontend/app/feed/subscriptions/page.tsx b/frontend/app/feed/subscriptions/page.tsx index d84c137..282a9bb 100755 --- a/frontend/app/feed/subscriptions/page.tsx +++ b/frontend/app/feed/subscriptions/page.tsx @@ -19,7 +19,7 @@ interface Subscription { async function getSubscriptions() { try { - const res = await fetch('http://127.0.0.1:8080/api/subscriptions', { cache: 'no-store' }); + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8080'}/api/subscriptions`, { cache: 'no-store' }); if (!res.ok) return []; return res.json() as Promise; } catch { @@ -29,7 +29,7 @@ async function getSubscriptions() { async function getChannelVideos(channelId: string, limit: number = 5) { try { - const res = await fetch(`http://127.0.0.1:8080/api/channel/videos?id=${channelId}&limit=${limit}`, { cache: 'no-store' }); + const res = await fetch(`${process.env.NEXT_PUBLIC_API_URL || 'http://127.0.0.1:8080'}/api/channel/videos?id=${channelId}&limit=${limit}`, { cache: 'no-store' }); if (!res.ok) return []; return res.json() as Promise; } catch { @@ -65,10 +65,10 @@ export default async function SubscriptionsPage() { return (

Subscriptions

- + {videosPerChannel.map(({ subscription, videos }) => (
-

{subscription.channel_name || subscription.channel_id}

- + {videos.length > 0 ? (
; } catch (e) {