2024最新YT-DLP使用demo网页端渲染

2024最新YT-DLP使用demo网页端渲染

  • 前提摘要
  • 1.使用python的fastapi库和jinjia2库进行前端渲染
  • 2.代码实现
    • 1)目录结构
    • 2)代码
      • style.css
      • index.html
      • result.html
      • main.py
      • run.py
    • 3)运行测试
      • 命令端运行
  • 3.项目下载地址

前提摘要

2024最新python使用yt-dlp
在这里插入图片描述

1.使用python的fastapi库和jinjia2库进行前端渲染

需要下载下面对应的python第三方库
pip install fastapi uvicorn python-multipart jinja2 yt-dlp

功能如下
使用YT-DLP,缓存文件到本地,预览文件信息

在这里插入图片描述
在这里插入图片描述

2.代码实现

1)目录结构

在这里插入图片描述

2)代码

style.css

body {
    font-family: Arial, sans-serif;
    margin: 0;
    padding: 20px;
    background-color: #f0f0f0;
}

.container {
    max-width: 800px;
    margin: 0 auto;
    background-color: white;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}

/* 添加 logo 相关样式 */
.logo {
    display: flex;
    justify-content: center;
    gap: 8px;
    margin-bottom: 20px;
}

.youtube-icon, .download-icon {
    width: 32px;  /* 调整图标大小 */
    height: 32px; /* 调整图标大小 */
}

.header {
    text-align: center;
    margin-bottom: 30px;
}

h1 {
    font-size: 24px;
    margin: 10px 0;
}

.subtitle {
    color: #666;
    margin-bottom: 20px;
}

form {
    margin: 20px 0;
}

.input-group {
    display: flex;
    gap: 10px;
}

input[type="text"] {
    flex: 1;
    padding: 10px;
    border: 1px solid #ddd;
    border-radius: 4px;
}

button {
    background-color: #007bff;
    color: white;
    padding: 10px 20px;
    border: none;
    border-radius: 4px;
    cursor: pointer;
}

button:hover {
    background-color: #0056b3;
}

.disclaimer {
    text-align: center;
    color: #666;
    font-size: 14px;
    margin: 20px 0;
}

.info-icon {
    color: #007bff;
    cursor: help;
}

.footer {
    text-align: center;
    margin-top: 30px;
    color: #666;
    font-size: 14px;
}

index.html

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>YouTube 视频下载器</title>
    <link rel="stylesheet" href="{{ url_for('static', path='style.css') }}">
</head>
<body>
    <div class="container">
        <div class="header">
            <div class="logo">
                <svg class="youtube-icon" viewBox="0 0 24 24">
                    <path fill="#FF0000" d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"/>
                </svg>
                <svg class="download-icon" viewBox="0 0 24 24">
                    <path fill="#4285f4" d="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
                </svg>
            </div>
            <h1>YT-DLP-DEMO</h1>
            <p class="subtitle">YTDownload</p>
        </div>

        <form action="/download" method="POST">
            <div class="input-group">
                <input type="text" 
                       name="url" 
                       placeholder="https://www.youtube.com/watch?v=..." 
                       required>
                <button type="submit">下载</button>
            </div>
        </form>

        <div class="footer">
            <p>由 FastAPI 和 yt-dlp 提供技术支持</p>
        </div>
    </div>
</body>
</html> 

result.html

<!DOCTYPE html>
<html>
<head>
    <title>Download Result</title>
    <link rel="stylesheet" href="{{ url_for('static', path='style.css') }}">
</head>
<body>
    <div class="container">
        {% if success %}
            <div class="result-info">
                <div class="video-header">
                    <div class="video-details">
                        <h2>{{ video_info.title }}</h2>
                        <div class="meta-info">
                            <p><strong>作者:</strong> {{ video_info.author }}</p>
                            <p><strong>时长:</strong> {{ video_info.length }}</p>
                            <p><strong>观看次数:</strong> {{ '{:,}'.format(video_info.views) }}</p>
                        </div>
                    </div>
                </div>
                
                <div class="download-sections">
                    <div class="preview-section">
                        <h3>视频预览</h3>
                        <div class="preview-container">
                            <video controls>
                                <source src="{{ video_path }}" type="video/mp4">
                                您的浏览器不支持视频标签。
                            </video>
                        </div>
                        <a href="{{ video_path }}" class="download-button" download>
                            <span class="icon"></span> 下载视频
                        </a>
                    </div>
                    
                    <div class="preview-section">
                        <h3>音频预览</h3>
                        <div class="preview-container">
                            <audio controls>
                                <source src="{{ audio_path }}" type="audio/mp4">
                                您的浏览器不支持音频标签。
                            </audio>
                        </div>
                        <a href="{{ audio_path }}" class="download-button" download>
                            <span class="icon"></span> 下载音频
                        </a>
                    </div>
                </div>
            </div>
        {% else %}
            <div class="error-container">
                <h2>下载失败</h2>
                <p class="error-message">{{ error }}</p>
            </div>
        {% endif %}
        
        <a href="/" class="back-button">返回首页</a>
    </div>
</body>
</html> 

main.py

from fastapi import FastAPI, Request, Form
from fastapi.templating import Jinja2Templates
from fastapi.staticfiles import StaticFiles
from fastapi.responses import FileResponse
import yt_dlp
from pathlib import Path
import os

app = FastAPI()

# 配置静态文件和模板
app.mount("/static", StaticFiles(directory="static"), name="static")
templates = Jinja2Templates(directory="templates")

# 配置下载目录
VIDEO_DIR = Path("downloads/video")
AUDIO_DIR = Path("downloads/audio")
VIDEO_DIR.mkdir(parents=True, exist_ok=True)
AUDIO_DIR.mkdir(parents=True, exist_ok=True)

def download_youtube_video(url):
    try:
        # yt-dlp 基础配置
        common_opts = {
            'cookiefile': r'www.youtube.com_cookies.txt',
            'quiet': False,
            'no_warnings': False,
            'verbose': True,
            'proxy': 'http://127.0.0.1:10809',
            'socket_timeout': 30,
            'retries': 3,
            'nocheckcertificate': True,
            'prefer_insecure': True
        }

        # 视频下载选项
        video_opts = {
            **common_opts,
            'format': 'best[ext=mp4][height<=720]/best[height<=720]/best',
            'outtmpl': str(VIDEO_DIR / '%(title)s.%(ext)s'),
        }

        # 音频下载选项
        audio_opts = {
            **common_opts,
            'format': 'bestaudio[ext=m4a]/bestaudio',
            'outtmpl': str(AUDIO_DIR / '%(title)s.%(ext)s'),
        }

        # 获取视频信息
        with yt_dlp.YoutubeDL(common_opts) as ydl:
            info = ydl.extract_info(url, download=False)
            title = info['title']
            duration = info['duration']
            thumbnail = info['thumbnail']
            author = info.get('uploader', 'Unknown')
            views = info.get('view_count', 0)

        # 下载视频
        with yt_dlp.YoutubeDL(video_opts) as ydl:
            ydl.download([url])

        # 下载音频
        with yt_dlp.YoutubeDL(audio_opts) as ydl:
            ydl.download([url])

        # 获取下载后的文件路径
        video_file = next(VIDEO_DIR.glob(f"{title}.*"))
        audio_file = next(AUDIO_DIR.glob(f"{title}.*"))

        return {
            "status": "success",
            "title": title,
            "author": author,
            "duration": f"{duration // 60}:{duration % 60:02d}",
            "views": views,
            "thumbnail": thumbnail,
            "video_path": str(video_file.name),
            "audio_path": str(audio_file.name)
        }
    except Exception as e:
        return {"status": "error", "message": str(e)}

@app.get("/")
async def home(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

@app.post("/download")
async def download_video_route(request: Request, url: str = Form(...)):
    try:
        result = download_youtube_video(url)
        
        if result["status"] == "success":
            video_info = {
                "title": result["title"],
                "author": result["author"],
                "length": result["duration"],
                "views": result["views"],
                "thumbnail": result["thumbnail"]
            }
            
            return templates.TemplateResponse("result.html", {
                "request": request,
                "video_info": video_info,
                "video_path": f"/downloads/video/{result['video_path']}",
                "audio_path": f"/downloads/audio/{result['audio_path']}",
                "success": True
            })
        else:
            raise Exception(result["message"])
            
    except Exception as e:
        return templates.TemplateResponse("result.html", {
            "request": request,
            "error": str(e),
            "success": False
        })

# 配置下载目录的静态文件服务
app.mount("/downloads", StaticFiles(directory="downloads"), name="downloads") 

run.py

import uvicorn
import os

def check_directories():
    """确保必要的目录存在"""
    directories = [
        'static',
        'templates',
        'downloads',
        'downloads/video',
        'downloads/audio'
    ]
    for directory in directories:
        if not os.path.exists(directory):
            os.makedirs(directory)
            print(f"Created directory: {directory}")

if __name__ == "__main__":
    # 检查并创建必要的目录
    check_directories()
    
    # 配置并启动服务器
    uvicorn.run(
        "main:app", 
        host="127.0.0.1", 
        port=8000, 
        reload=True,
        reload_dirs=["templates", "static"],
        log_level="info"
    ) 

3)运行测试

命令端运行

pip install fastapi uvicorn python-multipart jinja2 yt-dlp

在这里插入图片描述

python run.py

在这里插入图片描述

3.项目下载地址

https://github.com/unkownc/python_demo/tree/main
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/922637.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C0034.在Ubuntu中安装的Qt路径

Qt安装路径查询 在终端输入qmake -v如上中/usr/lib/x86_64-linux-gnu就是Qt的安装目录&#xff1b;

【STL】10.set与map的模拟实现

一、源码及框架分析 SGI-STL30版本源代码&#xff0c;map和set的源代码在map/set/stl_map.h/stl_set.h/stl_tree.h等及个头文件中。 map和set的实现结构框架核心部分截取出来如下&#xff1a; // set #ifndef __SGI_STL_INTERNAL_TREE_H #include <stl_tree.h> #endif …

AI模型---安装cuda与cuDNN

1.安装cuda 先打开cmd 输入nvidia-smi 查看显卡支持cuda对应的版本&#xff1a; 然后去英伟达官网下载cuda&#xff08;外网多刷几次&#xff09; https://developer.nvidia.com/cuda-toolkit-archive 注意对应版本 安装过程中如果显示如下图&#xff1a; 请安装visual Stu…

docker pull命令拉取镜像失败的解决方案

docker pull命令拉取镜像失败的解决方案 简介&#xff1a; docker pull命令拉取镜像失败的解决方案 docker pull命令拉取镜像失败的解决方案 一、执行docker pull命令&#xff0c;拉取镜像失败 报错信息&#xff1a;error pulling image configuration: Get https://produc…

Java开发经验——SpringRestTemplate常见错误

摘要 本文分析了在使用Spring框架的RestTemplate发送表单请求时遇到的常见错误。主要问题在于将表单参数错误地以JSON格式提交&#xff0c;导致服务器无法正确解析参数。文章提供了错误案例的分析&#xff0c;并提出了修正方法。 1. 表单参数类型是MultiValueMap RestControl…

《生成式 AI》课程 作业6 大语言模型(LLM)的训练微调 Fine Tuning -- part1

资料来自李宏毅老师《生成式 AI》课程&#xff0c;如有侵权请通知下线 Introduction to Generative AI 2024 Spring 该文档主要介绍了国立台湾大学&#xff08;NTU&#xff09;2024 年春季 “生成式人工智能&#xff08;GenAI&#xff09;” 课程的作业 5&#xff08;GenAI HW…

tcpdump抓包 wireShark

TCPdump抓包工具介绍 TCPdump&#xff0c;全称dump the traffic on anetwork&#xff0c;是一个运行在linux平台可以根据使用者需求对网络上传输的数据包进行捕获的抓包工具。 tcpdump可以支持的功能: 1、在Linux平台将网络中传输的数据包全部捕获过来进行分析 2、支持网络层…

利用Hooka开源的多种功能shellcode加载器实现快速免杀火绒,静态360+360杀毒,微步查杀1,vt查杀7(教程)

免责声明: 本文旨在提供有关特定漏洞的深入信息&#xff0c;帮助用户充分了解潜在的安全风险。发布此信息的目的在于提升网络安全意识和推动技术进步&#xff0c;未经授权访问系统、网络或应用程序&#xff0c;可能会导致法律责任或严重后果。因此&#xff0c;作者不对读者基于…

MyBatis中特殊SQL的执行

目录 1.模糊查询 2.批量删除 3.动态设置表名 4.添加功能获取自增的主键 1.模糊查询 List<User> getUserByLike(Param("username") String username); <select id"getUserByLike" resultType"com.atguigu.mybatis.pojo.User">&…

九、FOC原理详解

1、FOC简介 FOC&#xff08;field-oriented control&#xff09;为磁场定向控制&#xff0c;又称为矢量控制&#xff08;vectorcontrol&#xff09;&#xff0c;是目前无刷直流电机&#xff08;BLDC&#xff09;和永磁同步电机&#xff08;PMSM&#xff09;高效控制的最佳选择…

selinux及防火墙

selinux说明 SELinux 是 Security-Enhanced Linux 的缩写&#xff0c;意思是安全强化的 linux 。 SELinux 主要由美国国家安全局&#xff08; NSA &#xff09;开发&#xff0c;当初开发的目的是为了避免资源的误用。 httpd进程标签&#xff08;/usr/share/nginx/html &#…

Flink学习连载第二篇-使用flink编写WordCount(多种情况演示)

使用Flink编写代码&#xff0c;步骤非常固定&#xff0c;大概分为以下几步&#xff0c;只要牢牢抓住步骤&#xff0c;基本轻松拿下&#xff1a; 1. env-准备环境 2. source-加载数据 3. transformation-数据处理转换 4. sink-数据输出 5. execute-执行 DataStream API开发 //n…

解锁PPTist的全新体验:Windows系统环境下本地部署与远程访问

文章目录 前言1. 本地安装PPTist2. PPTist 使用介绍3. 安装Cpolar内网穿透4. 配置公网地址5. 配置固定公网地址 前言 在Windows系统环境中&#xff0c;如何本地部署开源在线演示文稿应用PPTist&#xff0c;并实现远程访问&#xff1f;本文将为您提供详细的部署和配置指南。 P…

《第十部分》1.STM32之通信接口《精讲》之IIC通信---介绍

经过近一周的USART学习&#xff0c;我深刻体会到通信对单片机的重要性。它就像人类的手脚和大脑&#xff0c;只有掌握了通信技术&#xff0c;单片机才能与外界交互&#xff0c;展现出丰富多彩的功能&#xff0c;变得更加强大和实用。 单片机最基础的“语言”是二进制。可惜&am…

URL在线编码解码- 加菲工具

URL在线编码解码 打开网站 加菲工具 选择“URL编码解码” 输入需要编码/解码的内容&#xff0c;点击“编码”/“解码”按钮 编码&#xff1a; 解码&#xff1a; 复制已经编码/解码后的内容。

【TEST】Apache JMeter + Influxdb + Grafana

介绍 使用Jmeter发起测试&#xff0c;测试结果存入Influxdb&#xff0c;Grafana展示你的测试结果。 环境 windows 10docker desktopJDK17 安装 Apache JMeter 访问官网&#xff08;Apache JMeter - Apache JMeter™&#xff09;下载JMeter&#xff08;目前最新版本5.6.3&a…

Linux笔记---进程:进程切换与O(1)调度算法

1. 补充概念 1.1 并行与并发 竞争性&#xff1a;系统进程数目众多&#xff0c;而CPU资源只有少量&#xff0c;甚至只有1个&#xff0c;所以进程之间是具有竞争属性的。为了高效完成任务&#xff0c;更合理竞争相关资源&#xff0c;便具有了优先级。独立性&#xff1a;多进程运…

C语言:深入理解指针

一.内存和地址 我们知道计算机上CPU&#xff08;中央处理器&#xff09;在处理数据的时候&#xff0c;需要的数据是在内存中读取的&#xff0c;处理后的数据也会放回内存中&#xff0c;那我们买电脑的时候&#xff0c;电脑上内存是 8GB/16GB/32GB 等&#xff0c;那这些内存空间…

mybatis学习(一)

声明&#xff1a;该内容来源于动力节点&#xff0c;本人在学习mybatis过程中参考该内容&#xff0c;并自己做了部分笔记&#xff0c;但个人觉得本人做的笔记不如动力节点做的好&#xff0c;故使用动力节点的笔记作为后续mybatis的复习。 一、MyBatis概述 1.1 框架 在文献中看…

【C++】list模拟实现(详解)

本篇来详细说一下list的模拟实现&#xff0c;list的大体框架实现会比较简单&#xff0c;难的是list的iterator的实现。我们模拟实现的是带哨兵位头结点的list。 1.准备工作 为了不和C库里面的list冲突&#xff0c;我们在实现的时候用命名空间隔开。 //list.h #pragma once #…