[Python] Streamlit App Using yt-dlp for YouTube Video Downloads - Downloads Files to Server,...

Discussão em 'Python' iniciado por Stack, Dezembro 20, 2024.

  1. Stack

    Stack Membro Participativo

    I’m building a Streamlit app that uses yt-dlp (a command-line tool for downloading YouTube videos) to download videos when a user enters a YouTube URL. The app works perfectly on my local machine, downloading the video correctly. However, after deploying the app on Streamlit Cloud, the video file is downloaded to the Streamlit server (not to the local machine) even though the download button is clicked. The browser just loads and shows "Download successfully" but the file doesn’t appear in my local Downloads folder.

    Here's the basic flow:

    The user inputs a YouTube URL. The app runs yt-dlp to download the video on the server. A download button is displayed with the downloaded video file from the server. However, the file does not download to the user's local machine, and it only exists on the Streamlit server.

    What I’ve Tried: Used st.download_button to serve the file for download. Tried using tempfile.mkdtemp() to save the downloaded file in a temporary folder. Checked the file path: The file does exist on the Streamlit server, but it's not being served correctly to the client.

    import streamlit as st
    import subprocess
    import os
    import tempfile

    # Function to download video and save it to a temporary directory
    def download_video(url, is_playlist, quality, subtitles):
    # Create a temporary directory to save the downloaded video
    temp_dir = tempfile.mkdtemp()

    # Base command for yt-dlp
    cmd = ["yt-dlp", "-o", os.path.join(temp_dir, "%(title)s.%(ext)s")]

    # Add format options for video and audio (same as before)
    if quality == "144p":
    cmd += ["-f", "bv*[height<=144][ext=mp4]+ba[ext=m4a]/bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]"]
    elif quality == "240p":
    cmd += ["-f", "bv*[height<=240][ext=mp4]+ba[ext=m4a]/bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]"]
    elif quality == "360p":
    cmd += ["-f", "bv*[height<=360][ext=mp4]+ba[ext=m4a]/bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]"]
    elif quality == "480p":
    cmd += ["-f", "bv*[height<=480][ext=mp4]+ba[ext=m4a]/bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]"]
    elif quality == "720p":
    cmd += ["-f", "bv*[height<=720][ext=mp4]+ba[ext=m4a]/bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]"]
    elif quality == "1080p":
    cmd += ["-f", "bv*[height<=1080][ext=mp4]+ba[ext=m4a]/bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]"]
    elif quality == "1440p":
    cmd += ["-f", "bv*[height<=1440][ext=mp4]+ba[ext=m4a]/bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]"]
    elif quality == "2160p":
    cmd += ["-f", "bv*[height<=2160][ext=mp4]+ba[ext=m4a]/bv*[ext=mp4]+ba[ext=m4a]/b[ext=mp4]"]
    else:
    cmd += ["-f", "bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]"]

    # Set output format to mp4
    cmd += ["--merge-output-format", "mp4"]

    # Subtitles options
    if subtitles:
    cmd += ["--write-sub", "--sub-lang", "en", "--embed-subs"]

    # Playlist or single video
    if is_playlist:
    cmd += ["-o", os.path.join(temp_dir, "%(playlist_index)s. %(title)s.%(ext)s")]
    else:
    cmd += ["-o", os.path.join(temp_dir, "%(title)s.%(ext)s")]

    # Add the URL
    cmd.append(url)

    # Execute the command
    subprocess.run(cmd, check=True)

    # Return the path to the downloaded file
    downloaded_file = [f for f in os.listdir(temp_dir) if f.endswith('.mp4') or f.endswith('.mkv') or f.endswith('.webm')][0]
    file_path = os.path.join(temp_dir, downloaded_file)

    return file_path

    # Streamlit UI
    st.set_page_config(page_title="YouTube Downloader", layout="centered")
    st.title("YouTube Video Downloader")

    # Input fields
    with st.container():
    st.markdown("### Input Video Details")
    url = st.text_input("Enter YouTube URL", key="url_input")
    option = st.selectbox("Download Type", ("Single Video", "Playlist"), key="download_type")
    is_playlist = option == "Playlist"

    # Options for quality and subtitles
    with st.container():
    st.markdown("### Options")
    quality = st.selectbox(
    "Select Video Quality",
    ["144p", "240p", "360p", "480p", "720p", "1080p", "1440p", "2160p", "Best Available"],
    index=4,
    key="quality_select"
    )
    subtitles = st.checkbox("Add Subtitles", key="subtitles_checkbox")

    # Download button
    with st.container():
    download_button = st.button("Download", key="download_button")

    if download_button:
    if not url:
    st.error("Please provide a valid YouTube URL.")
    else:
    try:
    with st.spinner("Downloading..."):
    # Download video and get the file path
    file_path = download_video(url, is_playlist, quality, subtitles)

    # Provide a download link/button for the user to download the video
    with open(file_path, "rb") as file:
    st.download_button(
    label="Download Video",
    data=file,
    file_name=os.path.basename(file_path),
    mime="video/mp4"
    )

    st.success("Download completed successfully!")
    except subprocess.CalledProcessError:
    st.error("An error occurred during the download. Please check the URL and try again.")




    What I’m Trying to Achieve: I want to download the video directly to the user’s local machine, not the server. The file is being downloaded to the Streamlit server, but I want to provide a link/button for users to download it to their local machine. I'm using the st.download_button to provide the download option, but the video file is being downloaded to the Streamlit server instead of the client’s machine. What I’ve Tried: Using yt-dlp to download the video to a temporary directory on the Streamlit server. Using st.download_button to serve the file to the client, but it's still downloading the file from the server, not directly to the local machine. Can anyone help me solve this? How can I ensure the downloaded video file is transferred to the client's local machine? Thank you!

    Continue reading...

Compartilhe esta Página