diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cadf443 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.DS_Store + +/videos \ No newline at end of file diff --git a/dockerfile b/dockerfile index d858924..cdc3b79 100644 --- a/dockerfile +++ b/dockerfile @@ -5,6 +5,10 @@ ENV PYTHONUNBUFFERED=1 WORKDIR /app +RUN apt-get update && \ + apt-get install -y --no-install-recommends ffmpeg && \ + rm -rf /var/lib/apt/lists/* + COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt diff --git a/main.py b/main.py index e767214..33f36e8 100644 --- a/main.py +++ b/main.py @@ -1,12 +1,17 @@ +import os +import uuid +import re + from typing import Optional from fastapi import FastAPI, HTTPException, Query from youtube_transcript_api import YouTubeTranscriptApi from youtube_transcript_api.formatters import SRTFormatter from youtube_transcript_api._errors import TranscriptsDisabled, NoTranscriptFound from yt_dlp import YoutubeDL +from unidecode import unidecode app = FastAPI( - title="YouTube Transcript & Metadata API", + title="YouTube Transcript, Download and Metadata API", version="1.0.0" ) @@ -31,7 +36,6 @@ def get_transcript( raise HTTPException(status_code=400, detail=str(e)) else: video_id = videoId - try: ytt_api = YouTubeTranscriptApi() @@ -41,12 +45,11 @@ def get_transcript( if not result: raise NoTranscriptFound("Nenhuma transcrição encontrada para este vídeo") except TranscriptsDisabled: - raise HTTPException(status_code=404, error="Transcrição desativada para este vídeo") + raise HTTPException(status_code=404, detail="Transcrição desativada para este vídeo") except NoTranscriptFound: - raise HTTPException(status_code=404, error="Nenhuma transcrição encontrada") + raise HTTPException(status_code=404, detail="Nenhuma transcrição encontrada") except Exception as e: - raise HTTPException(status_code=500, error=f"Erro ao obter transcrição: {e}") - + raise HTTPException(status_code=500, detail=f"Erro ao obter transcrição: {e}") return { "video_id": video_id, @@ -81,3 +84,85 @@ def get_video_metadata( raise HTTPException(status_code=500, detail=f"Erro ao extrair metadata: {e}") return info + +@app.get("/download-video") +def download_video( + url: Optional[str] = Query(None, description="URL completa do vídeo"), + videoId: Optional[str] = Query(None, alias="videoId", description="ID do vídeo no YouTube"), + qualidade: str = Query("high", description="Qualidade do vídeo: low, medium, high") +): + if not url and not videoId: + raise HTTPException(status_code=400, detail="Informe 'url' ou 'videoId'") + if url: + target = url + try: + video_id = extract_video_id(url) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + else: + target = f"https://www.youtube.com/watch?v={videoId}" + video_id = videoId + + quality_map = { + "low": "bestvideo[height<=480]+bestaudio/best[height<=480]", + "medium": "bestvideo[height<=720]+bestaudio/best[height<=720]", + "high": "bestvideo+bestaudio/best" + } + qualidade = qualidade.lower() + if qualidade not in quality_map: + raise HTTPException(status_code=400, detail="Qualidade deve ser: low, medium ou high") + + videos_dir = "/app/videos" + os.makedirs(videos_dir, exist_ok=True) + + unique_id = str(uuid.uuid4()) + output_template = os.path.join(videos_dir, f"{unique_id}.%(ext)s") + + ydl_opts = { + "format": quality_map[qualidade], + "outtmpl": output_template, + "quiet": True, + "noplaylist": True, + "merge_output_format": "mp4", + } + + try: + with YoutubeDL(ydl_opts) as ydl: + base = ydl.extract_info(target, download=False) + title = base.get("title", unique_id) + clean_title = unidecode(title) + clean_title = re.sub(r"[^\w\s-]", "", clean_title) + clean_title = clean_title.replace(" ", "_") + clean_title = re.sub(r"_+", "_", clean_title) + clean_title = clean_title.strip("_") + filename = f"{clean_title}_{qualidade}.mp4" + final_path = os.path.join(videos_dir, filename) + + print('Info ok') + + if os.path.exists(final_path): + return { + "videoId": video_id, + "filename": filename + } + + print('Lets download') + + result = ydl.extract_info(target, download=True) + + if "requested_downloads" in result and len(result["requested_downloads"]) > 0: + real_file_path = result["requested_downloads"][0]["filepath"] + elif "filepath" in result: + real_file_path = result["filepath"] + else: + real_file_path = output_template.replace("%(ext)s", "mp4") + + os.rename(real_file_path, final_path) + + except Exception as e: + raise HTTPException(status_code=500, detail=f"Erro ao baixar vídeo: {e}") + + return { + "videoId": video_id, + "filename": filename + } diff --git a/requirements.txt b/requirements.txt index ced7931..db7a9fe 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ fastapi uvicorn[standard] youtube-transcript-api==1.2.1 yt-dlp +unidecode \ No newline at end of file