Spaces:
Building
Building
Add yt-audio gradio app
Browse files- .gitignore +1 -0
- Dockerfile +23 -0
- app.py +82 -0
- requirements.txt +54 -0
- sample.py +72 -0
.gitignore
ADDED
@@ -0,0 +1 @@
|
|
|
|
|
1 |
+
venv
|
Dockerfile
ADDED
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Use Python 3.10 base image
|
2 |
+
FROM python:3.10-slim
|
3 |
+
|
4 |
+
# Create a non-root user for security
|
5 |
+
RUN useradd -m -u 1000 user
|
6 |
+
USER user
|
7 |
+
ENV PATH="/home/user/.local/bin:${PATH}"
|
8 |
+
|
9 |
+
# Set working directory
|
10 |
+
WORKDIR /app
|
11 |
+
|
12 |
+
# Install Python dependencies first for layer caching
|
13 |
+
COPY --chown=user requirements.txt .
|
14 |
+
RUN pip install --no-cache-dir --upgrade -r requirements.txt
|
15 |
+
|
16 |
+
# Copy application code
|
17 |
+
COPY --chown=user . .
|
18 |
+
|
19 |
+
# Expose Gradio's default port
|
20 |
+
EXPOSE 7860
|
21 |
+
|
22 |
+
# Launch the application with Spaces-compatible settings
|
23 |
+
CMD ["python", "app.py"]
|
app.py
ADDED
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
from pytubefix import YouTube
|
3 |
+
import tempfile
|
4 |
+
import base64
|
5 |
+
from io import BytesIO
|
6 |
+
|
7 |
+
def download_audio(url):
|
8 |
+
try:
|
9 |
+
# Initialize YouTube object
|
10 |
+
yt = YouTube(url, 'WEB')
|
11 |
+
title = yt.title
|
12 |
+
|
13 |
+
# Get best audio stream
|
14 |
+
audio_stream = yt.streams.filter(only_audio=True).order_by('abr').desc().first()
|
15 |
+
if not audio_stream:
|
16 |
+
return ["Error: No audio stream found", None, None]
|
17 |
+
|
18 |
+
# Download to in-memory buffer
|
19 |
+
buffer = BytesIO()
|
20 |
+
audio_stream.stream_to_buffer(buffer)
|
21 |
+
buffer.seek(0)
|
22 |
+
|
23 |
+
# Create temporary file
|
24 |
+
with tempfile.NamedTemporaryFile(suffix=".mp3", delete=False) as tmp_file:
|
25 |
+
tmp_file.write(buffer.read())
|
26 |
+
tmp_path = tmp_file.name
|
27 |
+
|
28 |
+
# Create HTML audio player with base64 encoding
|
29 |
+
with open(tmp_path, "rb") as f:
|
30 |
+
audio_bytes = f.read()
|
31 |
+
audio_base64 = base64.b64encode(audio_bytes).decode("utf-8")
|
32 |
+
audio_html = f"""
|
33 |
+
<audio controls autoplay style="width: 100%">
|
34 |
+
<source src="data:audio/mp3;base64,{audio_base64}" type="audio/mp3">
|
35 |
+
Your browser does not support the audio element.
|
36 |
+
</audio>
|
37 |
+
"""
|
38 |
+
|
39 |
+
return [
|
40 |
+
f"Success: {title}",
|
41 |
+
tmp_path,
|
42 |
+
audio_html
|
43 |
+
]
|
44 |
+
|
45 |
+
except Exception as e:
|
46 |
+
return [f"Error: {str(e)}", None, None]
|
47 |
+
|
48 |
+
with gr.Blocks(title="YouTube Audio Downloader") as demo:
|
49 |
+
gr.Markdown("# YouTube Audio Downloader")
|
50 |
+
|
51 |
+
with gr.Row():
|
52 |
+
url_input = gr.Textbox(label="YouTube URL", placeholder="Enter YouTube URL here...")
|
53 |
+
|
54 |
+
with gr.Column(scale=0.3):
|
55 |
+
clear_btn = gr.Button("Clear")
|
56 |
+
submit_btn = gr.Button("Submit")
|
57 |
+
|
58 |
+
status_output = gr.Textbox(label="Status", interactive=False)
|
59 |
+
file_download = gr.File(label="Audio File")
|
60 |
+
audio_player = gr.HTML(label="Audio Player")
|
61 |
+
|
62 |
+
def clear():
|
63 |
+
return [None, None, None, None]
|
64 |
+
|
65 |
+
clear_btn.click(
|
66 |
+
fn=clear,
|
67 |
+
outputs=[url_input, status_output, file_download, audio_player]
|
68 |
+
)
|
69 |
+
|
70 |
+
submit_btn.click(
|
71 |
+
fn=download_audio,
|
72 |
+
inputs=url_input,
|
73 |
+
outputs=[status_output, file_download, audio_player]
|
74 |
+
)
|
75 |
+
|
76 |
+
if __name__ == "__main__":
|
77 |
+
demo.launch(
|
78 |
+
server_name="0.0.0.0",
|
79 |
+
server_port=7860,
|
80 |
+
show_error=True,
|
81 |
+
allowed_paths=["*"]
|
82 |
+
)
|
requirements.txt
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
aiofiles==23.2.1
|
2 |
+
annotated-types==0.7.0
|
3 |
+
anyio==4.8.0
|
4 |
+
certifi==2024.12.14
|
5 |
+
charset-normalizer==3.4.1
|
6 |
+
click==8.1.8
|
7 |
+
colorama==0.4.6
|
8 |
+
exceptiongroup==1.2.2
|
9 |
+
fastapi==0.115.6
|
10 |
+
ffmpy==0.5.0
|
11 |
+
filelock==3.17.0
|
12 |
+
fsspec==2024.12.0
|
13 |
+
gradio==5.12.0
|
14 |
+
gradio-client==1.5.4
|
15 |
+
h11==0.14.0
|
16 |
+
httpcore==1.0.7
|
17 |
+
httpx==0.28.1
|
18 |
+
huggingface-hub==0.27.1
|
19 |
+
idna==3.10
|
20 |
+
jinja2==3.1.5
|
21 |
+
markdown-it-py==3.0.0
|
22 |
+
markupsafe==2.1.5
|
23 |
+
mdurl==0.1.2
|
24 |
+
numpy==2.2.2
|
25 |
+
orjson==3.10.15
|
26 |
+
packaging==24.2
|
27 |
+
pandas==2.2.3
|
28 |
+
pillow==11.1.0
|
29 |
+
pydantic==2.10.5
|
30 |
+
pydantic-core==2.27.2
|
31 |
+
pydub==0.25.1
|
32 |
+
pygments==2.19.1
|
33 |
+
python-dateutil==2.9.0.post0
|
34 |
+
python-multipart==0.0.20
|
35 |
+
pytubefix==8.12.0
|
36 |
+
pytz==2024.2
|
37 |
+
pyyaml==6.0.2
|
38 |
+
requests==2.32.3
|
39 |
+
rich==13.9.4
|
40 |
+
ruff==0.9.2
|
41 |
+
safehttpx==0.1.6
|
42 |
+
semantic-version==2.10.0
|
43 |
+
shellingham==1.5.4
|
44 |
+
six==1.17.0
|
45 |
+
sniffio==1.3.1
|
46 |
+
starlette==0.41.3
|
47 |
+
tomlkit==0.13.2
|
48 |
+
tqdm==4.67.1
|
49 |
+
typer==0.15.1
|
50 |
+
typing-extensions==4.12.2
|
51 |
+
tzdata==2025.1
|
52 |
+
urllib3==2.3.0
|
53 |
+
uvicorn==0.34.0
|
54 |
+
websockets==14.2
|
sample.py
ADDED
@@ -0,0 +1,72 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import yt_dlp
|
3 |
+
import os
|
4 |
+
import browser_cookie3
|
5 |
+
from huggingface_hub import hf_hub_download, login
|
6 |
+
|
7 |
+
|
8 |
+
|
9 |
+
if "cookies.txt" not in os.listdir("."):
|
10 |
+
hf_hub_download("not-lain/secrets","cookies.txt",local_dir=".",repo_type="dataset")
|
11 |
+
|
12 |
+
|
13 |
+
def get_cookies():
|
14 |
+
try:
|
15 |
+
# Try to get cookies from multiple browsers
|
16 |
+
cookies = []
|
17 |
+
for get_cookies in [
|
18 |
+
browser_cookie3.firefox,
|
19 |
+
browser_cookie3.chrome,
|
20 |
+
browser_cookie3.brave,
|
21 |
+
]:
|
22 |
+
try:
|
23 |
+
cookies.extend([c for c in get_cookies(domain_name=".youtube.com")])
|
24 |
+
except:
|
25 |
+
continue
|
26 |
+
return cookies
|
27 |
+
except:
|
28 |
+
return None
|
29 |
+
|
30 |
+
|
31 |
+
def download_video(url):
|
32 |
+
try:
|
33 |
+
downloads_dir = "/downloads"
|
34 |
+
if not os.path.exists(downloads_dir):
|
35 |
+
os.makedirs(downloads_dir, exist_ok=True)
|
36 |
+
|
37 |
+
# Use a pre-provided cookies file
|
38 |
+
cookie_file = "cookies.txt"
|
39 |
+
cookie_args = {}
|
40 |
+
if os.path.exists(cookie_file):
|
41 |
+
cookie_args["cookiefile"] = cookie_file
|
42 |
+
|
43 |
+
ydl_opts = {
|
44 |
+
"outtmpl": os.path.join(downloads_dir, "%(title)s.%(ext)s"),
|
45 |
+
"format": "mp4/bestvideo+bestaudio/best",
|
46 |
+
"merge_output_format": "mp4",
|
47 |
+
**cookie_args,
|
48 |
+
"paths": {"home": "/downloads", "temp": "/tmp"},
|
49 |
+
}
|
50 |
+
|
51 |
+
with yt_dlp.YoutubeDL(ydl_opts) as ydl:
|
52 |
+
info = ydl.extract_info(url, download=True)
|
53 |
+
video_path = ydl.prepare_filename(info)
|
54 |
+
if os.path.exists(video_path):
|
55 |
+
return f"Successfully downloaded: {info['title']}", video_path
|
56 |
+
else:
|
57 |
+
return f"Download completed but file not found: {info['title']}", None
|
58 |
+
except Exception as e:
|
59 |
+
return f"Error: {str(e)}", None
|
60 |
+
|
61 |
+
|
62 |
+
iface = gr.Interface(
|
63 |
+
fn=download_video,
|
64 |
+
inputs=gr.Textbox(label="YouTube URL"),
|
65 |
+
outputs=[gr.Textbox(label="Status"), gr.Video(label="Downloaded Video")],
|
66 |
+
title="YouTube Video Downloader",
|
67 |
+
description="Cookies created using the following extension : https://chromewebstore.google.com/detail/get-cookiestxt-clean/ahmnmhfbokciafffnknlekllgcnafnie?hl=en \n\nEnter a YouTube URL to download and view the video",
|
68 |
+
examples=["https://www.youtube.com/watch?v=UiyDmqO59QE"],
|
69 |
+
)
|
70 |
+
|
71 |
+
if __name__ == "__main__":
|
72 |
+
iface.launch(server_name="0.0.0.0", allowed_paths=["/"], server_port=7860)
|