Add Node.js to backend Dockerfile and enhance downloader

Added Node.js installation to the backend Dockerfile to support yt-dlp's JavaScript runtime. Updated downloader API to bypass SSL verification in Docker, improved error reporting, and convert video thumbnails to data URLs to avoid mixed content issues. In the frontend, improved Dockerfile.prod install process and added new service routes for drone and web services in App.tsx.
This commit is contained in:
2025-12-23 13:37:24 +01:00
parent 1cec6be6d7
commit cf615c5279
4 changed files with 50 additions and 6 deletions

View File

@@ -2,6 +2,7 @@ FROM python:3.12-slim
WORKDIR /app WORKDIR /app
# Install system dependencies including Node.js for yt-dlp JavaScript runtime
RUN apt update && apt install -y \ RUN apt update && apt install -y \
weasyprint \ weasyprint \
libcairo2 \ libcairo2 \
@@ -9,7 +10,13 @@ RUN apt update && apt install -y \
libpango-1.0-0 \ libpango-1.0-0 \
libgobject-2.0-0 \ libgobject-2.0-0 \
ffmpeg \ ffmpeg \
ca-certificates ca-certificates \
curl \
&& curl -fsSL https://deb.nodesource.com/setup_22.x | bash - \
&& apt install -y nodejs \
&& update-ca-certificates \
&& apt clean \
&& rm -rf /var/lib/apt/lists/*
COPY requirements.txt . COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt RUN pip install --no-cache-dir -r requirements.txt

View File

@@ -6,6 +6,8 @@ import tempfile
import os import os
import shutil import shutil
import mimetypes import mimetypes
import base64
import urllib.request
from rest_framework import serializers from rest_framework import serializers
from rest_framework.views import APIView from rest_framework.views import APIView
@@ -95,12 +97,13 @@ class Downloader(APIView):
ydl_options = { ydl_options = {
"quiet": True, "quiet": True,
"no_check_certificates": True, # Bypass SSL verification in Docker
} }
try: try:
with yt_dlp.YoutubeDL(ydl_options) as ydl: with yt_dlp.YoutubeDL(ydl_options) as ydl:
info = ydl.extract_info(url, download=False) info = ydl.extract_info(url, download=False)
except Exception: except Exception as e:
return Response({"error": "Failed to retrieve video info"}, status=400) return Response({"error": f"Failed to retrieve video info: {str(e)}"}, status=400)
formats = info.get("formats", []) or [] formats = info.get("formats", []) or []
@@ -128,11 +131,24 @@ class Downloader(APIView):
audio_resolutions = [f"{b}kbps" for b in sorted(bitrates, reverse=True)] audio_resolutions = [f"{b}kbps" for b in sorted(bitrates, reverse=True)]
# Convert thumbnail to data URL to avoid mixed content issues (HTTPS thumbnail on HTTP site)
thumbnail_url = info.get("thumbnail")
thumbnail_data_url = None
if thumbnail_url:
try:
with urllib.request.urlopen(thumbnail_url, timeout=10) as response:
image_data = response.read()
content_type = response.headers.get('Content-Type', 'image/jpeg')
thumbnail_data_url = f"data:{content_type};base64,{base64.b64encode(image_data).decode('utf-8')}"
except Exception:
# If thumbnail fetch fails, just use the original URL
thumbnail_data_url = thumbnail_url
return Response( return Response(
{ {
"title": info.get("title"), "title": info.get("title"),
"duration": info.get("duration"), "duration": info.get("duration"),
"thumbnail": info.get("thumbnail"), "thumbnail": thumbnail_data_url,
"video_resolutions": video_resolutions, "video_resolutions": video_resolutions,
"audio_resolutions": audio_resolutions, "audio_resolutions": audio_resolutions,
}, },
@@ -305,6 +321,7 @@ class Downloader(APIView):
"merge_output_format": ext, # container "merge_output_format": ext, # container
"outtmpl": outtmpl, # temp dir "outtmpl": outtmpl, # temp dir
"quiet": True, "quiet": True,
"no_check_certificates": True, # Bypass SSL verification in Docker
"max_filesize": settings.DOWNLOADER_MAX_SIZE_BYTES, "max_filesize": settings.DOWNLOADER_MAX_SIZE_BYTES,
"socket_timeout": settings.DOWNLOADER_TIMEOUT, "socket_timeout": settings.DOWNLOADER_TIMEOUT,
"postprocessors": [], "postprocessors": [],

View File

@@ -1,13 +1,26 @@
# Step 1: Build React (Vite) app # Step 1: Build React (Vite) app
FROM node:22-alpine AS build FROM node:22-alpine AS build
WORKDIR /app WORKDIR /app
# Copy package files
COPY package*.json ./ COPY package*.json ./
# If package-lock.json exists, npm ci is faster and reproducible
RUN npm ci || npm install # Clean install with force flag to bypass cache issues
#RUN rm -rf node_modules package-lock.json && \
# npm cache clean --force && \
# npm install --legacy-peer-deps
# install
RUN npm install --legacy-peer-deps
# Copy source files
COPY . . COPY . .
ENV NODE_ENV=production ENV NODE_ENV=production
# Skip Orval - use pre-generated files committed to git # Skip Orval - use pre-generated files committed to git
ENV SKIP_ORVAL=true ENV SKIP_ORVAL=true
# Build the app
RUN npm run build RUN npm run build
# Step 2: Nginx runtime # Step 2: Nginx runtime

View File

@@ -3,6 +3,8 @@ import Home from "./pages/home/home";
import HomeLayout from "./layouts/HomeLayout"; import HomeLayout from "./layouts/HomeLayout";
import Downloader from "./pages/downloader/Downloader"; import Downloader from "./pages/downloader/Downloader";
import PrivateRoute from "./routes/PrivateRoute"; import PrivateRoute from "./routes/PrivateRoute";
import DroneServisSection from "./pages/home/components/Services/droneServis";
//import { UserContextProvider } from "./context/UserContext"; //import { UserContextProvider } from "./context/UserContext";
// Pages // Pages
@@ -25,6 +27,11 @@ export default function App() {
{/* APPS */} {/* APPS */}
<Route path="apps/downloader" element={<Downloader />} /> <Route path="apps/downloader" element={<Downloader />} />
{/* SERVICES */}
<Route path="services/drone" element={< DroneServisSection />} />
<Route path="services/web" element={<Downloader />} />
</Route> </Route>
{/* Example protected route group (kept for future use) */} {/* Example protected route group (kept for future use) */}