You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask集成youtube-dlp:点击音视频链接直接下载而非跳转播放页问题

问题

我使用Flask编写后端服务器,通过youtube-dlp按需为用户提供YouTube音视频格式的直接下载链接。当前代码已成功生成并在页面展示所有下载链接,但存在问题:用户点击链接时会跳转到音视频播放页面,需手动操作才能下载。我期望点击链接后直接触发音视频下载,无需跳转至播放页面。

相关代码如下:

app.py

from flask import Flask, jsonify, request, render_template
import yt_dlp

app = Flask(__name__)

def get_download_links(video_url):
    ydl_opts = {
        'format': 'best',
    }

    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        info = ydl.extract_info(video_url, download=False)

        video_formats = []
        audio_formats = []
        for format in info['formats']:
            if 'url' in format:
                if 'acodec' in format and format['acodec'] != 'none':
                    audio_formats.append({
                        'format_id': format['format_id'],
                        'url': format['url'],
                    })
                else:
                    video_formats.append({
                        'format_id': format['format_id'],
                        'url': format['url'],
                    })

    return video_formats, audio_formats, info['title']

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/get_download_links', methods=['GET'])
def get_download_links_api():
    video_url = request.args.get('video_url')

    if not video_url:
        return jsonify({"error": "Il parametro 'video_url' è richiesto."}), 400

    video_formats, audio_formats, video_title = get_download_links(video_url)
    return jsonify({
        'video_formats': video_formats,
        'audio_formats': audio_formats,
        'video_title': video_title
    })

if __name__ == '__main__':
    app.run(debug=True)

index.html

<!DOCTYPE html>
<html>
<head>
    <title>Download Links</title>
</head>
<body>
    <h1>Download Links</h1>
    <form id="video-form">
        <label for="video-url">Inserisci il link del video di YouTube:</label>
        <input type="text" id="video-url" required>
        <button type="submit">Genera link di download</button>
    </form>
    <div id="video-links"></div>
    <div id="audio-links"></div>

    <script src="{{ url_for('static', filename='js/main.js') }}"></script>
</body>
</html>

main.js

document.addEventListener('DOMContentLoaded', function() {
    function createDownloadLink(url, filename) {
        var link = document.createElement('a');
        link.href = url;
        link.download = filename;
        link.textContent = 'Download';
        link.style.display = 'block'; // Nasconde i link di download precedenti
        link.style.marginTop = '10px'; // Aggiunge uno spazio tra i link

        return link;
    }

    function displayVideoLinks(videoFormats) {
        var videoLinksDiv = document.getElementById('video-links');
        videoLinksDiv.innerHTML = '<h2>Video Download Links</h2>';

        videoFormats.forEach(function(format) {
            var link = createDownloadLink(format.url, format.format_id + '.mp4');
            videoLinksDiv.appendChild(link);
        });
    }

    function displayAudioLinks(audioFormats) {
        var audioLinksDiv = document.getElementById('audio-links');
        audioLinksDiv.innerHTML = '<h2>Audio Download Links</h2>';

        audioFormats.forEach(function(format) {
            var link = createDownloadLink(format.url, format.format_id + '.mp3');
            audioLinksDiv.appendChild(link);
        });
    }

    function getDownloadLinks(videoUrl) {
        fetch('/get_download_links?video_url=' + encodeURIComponent(videoUrl))
            .then(response => response.json())
            .then(data => {
                displayVideoLinks(data.video_formats);
                displayAudioLinks(data.audio_formats);
            })
            .catch(error => console.error('Error:', error));
    }

    var form = document.getElementById('video-form');
    form.addEventListener('submit', function(event) {
        event.preventDefault();
        var videoUrlInput = document.getElementById('video-url');
        var videoUrl = videoUrlInput.value.trim();
        if (videoUrl !== '') {
            getDownloadLinks(videoUrl);
        }
    });
});

相关界面截图:
流程
截图1
截图2

解决方案

问题根源在于YouTube返回的音视频直链未设置Content-Disposition: attachment响应头,浏览器识别为可直接播放的媒体资源,而非需要下载的文件。通过后端中转请求,手动添加该响应头即可触发直接下载。

步骤1:修改后端代码(app.py)

新增一个下载中转路由,同时安装requests依赖(用于转发请求):

pip install requests

修改后的app.py:

from flask import Flask, jsonify, request, render_template
import yt_dlp
import requests

app = Flask(__name__)

def get_download_links(video_url):
    ydl_opts = {
        'format': 'best',
    }

    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
        info = ydl.extract_info(video_url, download=False)

        video_formats = []
        audio_formats = []
        for format in info['formats']:
            if 'url' in format:
                if 'acodec' in format and format['acodec'] != 'none':
                    audio_formats.append({
                        'format_id': format['format_id'],
                        'url': format['url'],
                    })
                else:
                    video_formats.append({
                        'format_id': format['format_id'],
                        'url': format['url'],
                    })

    return video_formats, audio_formats, info['title']

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/get_download_links', methods=['GET'])
def get_download_links_api():
    video_url = request.args.get('video_url')

    if not video_url:
        return jsonify({"error": "Il parametro 'video_url' è richiesto."}), 400

    video_formats, audio_formats, video_title = get_download_links(video_url)
    return jsonify({
        'video_formats': video_formats,
        'audio_formats': audio_formats,
        'video_title': video_title
    })

# 新增下载中转路由
@app.route('/download')
def download():
    target_url = request.args.get('url')
    filename = request.args.get('filename')
    
    if not target_url or not filename:
        return jsonify({"error": "缺少必要参数"}), 400
    
    # 流式请求YouTube资源
    response = requests.get(target_url, stream=True)
    # 设置触发下载的响应头
    headers = {
        'Content-Disposition': f'attachment; filename="{filename}"',
        'Content-Type': response.headers.get('Content-Type', 'application/octet-stream')
    }
    
    return response.content, 200, headers

if __name__ == '__main__':
    app.run(debug=True)

步骤2:修改前端代码(main.js)

将原来的YouTube直链替换为后端中转路由:

document.addEventListener('DOMContentLoaded', function() {
    function createDownloadLink(url, filename) {
        var link = document.createElement('a');
        // 替换为后端下载路由,传递目标链接和文件名
        link.href = `/download?url=${encodeURIComponent(url)}&filename=${encodeURIComponent(filename)}`;
        link.textContent = '下载';
        link.style.display = 'block';
        link.style.marginTop = '10px';

        return link;
    }

    function displayVideoLinks(videoFormats) {
        var videoLinksDiv = document.getElementById('video-links');
        videoLinksDiv.innerHTML = '<h2>视频下载链接</h2>';

        videoFormats.forEach(function(format) {
            var link = createDownloadLink(format.url, format.format_id + '.mp4');
            videoLinksDiv.appendChild(link);
        });
    }

    function displayAudioLinks(audioFormats) {
        var audioLinksDiv = document.getElementById('audio-links');
        audioLinksDiv.innerHTML = '<h2>音频下载链接</h2>';

        audioFormats.forEach(function(format) {
            var link = createDownloadLink(format.url, format.format_id + '.mp3');
            audioLinksDiv.appendChild(link);
        });
    }

    function getDownloadLinks(videoUrl) {
        fetch('/get_download_links?video_url=' + encodeURIComponent(videoUrl))
            .then(response => response.json())
            .then(data => {
                displayVideoLinks(data.video_formats);
                displayAudioLinks(data.audio_formats);
            })
            .catch(error => console.error('错误:', error));
    }

    var form = document.getElementById('video-form');
    form.addEventListener('submit', function(event) {
        event.preventDefault();
        var videoUrlInput = document.getElementById('video-url');
        var videoUrl = videoUrlInput.value.trim();
        if (videoUrl !== '') {
            getDownloadLinks(videoUrl);
        }
    });
});

步骤3:修改前端文本(可选)

将index.html中的意大利语文本替换为中文,提升用户体验:

<!DOCTYPE html>
<html>
<head>
    <title>YouTube音视频下载</title>
</head>
<body>
    <h1>YouTube音视频下载</h1>
    <form id="video-form">
        <label for="video-url">输入YouTube视频链接:</label>
        <input type="text" id="video-url" required>
        <button type="submit">生成下载链接</button>
    </form>
    <div id="video-links"></div>
    <div id="audio-links"></div>

    <script src="{{ url_for('static', filename='js/main.js') }}"></script>
</body>
</html>

现在点击下载链接时,后端会转发请求到YouTube资源,并添加强制下载的响应头,浏览器将直接触发文件下载,不再跳转至播放页面。

内容的提问来源于stack exchange,提问作者jfranz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 02:14:55