您是否一直想知道如何创建一个非常简单的 CLI 应用程序,让您可以从 YouTube 下载视频,将它们转换为音频,然后将它们保存到计算机上的指定文件夹中?在这篇文章中,我想解释一种在 Python 中实现这一点的快速简便的方法,而不是太古怪。
在大多数情况下,我们会坚持使用 Python 生态系统中的原生产品。该项目中唯一的外部库将是Pytube库和Typer。
设置
首先,我们需要创建一个文件夹来存放我们的简单项目。之后,我们创建一个虚拟环境并安装我们的需求,在这个例子中就是 Pytube 和 Typer 库,我们就可以开始了。
执行
对于程序设置,我发现以非常简单和可重用的方式构建代码很重要,以便于维护、可转移性和可读性。话虽如此,我们将定义一个配置文件,该文件将包含我们简单的 CLI 工具将具有的基本配置。这将有助于实际定义我们的工具需要的结构配置。
为了实现简单的配置,我们定义了一个配置文件并将下面的代码写入其中。
from dataclasses import dataclass
from typing import TypeVar, Dict, Any, Type
from config.logger import Logger as CustomLogger
from logging import Logger
import os
ConfigClass = TypeVar('ConfigClass', bound='Config')
LOG_FILE = "logfile.log"
#create a new file for every new experiment
if (os.path.exists(LOG_FILE)):
os.remove(LOG_FILE)
setup = {
"VERSION": 1,
"logger": CustomLogger(logger_name="Video-Downloader", log_file=LOG_FILE).get_logger(),
}
@dataclass
class Config:
VERSION: int
logger: Logger
@classmethod
def from_config(cls: Type[ConfigClass], raw_config: Dict[str, Any]) -> ConfigClass:
return cls(**raw_config)
def configure() -> Config:
return Config.from_config(setup)
因此,我们只需定义简单配置的数据类型,在本例中就是您的应用程序和自定义记录器的版本(可以在此处找到)。
接下来,我们定义应用程序的驱动程序的外观。这应该是应用程序的主要部分,它根据一些提供的或预定义的参数进行实际工作。这应该类似于下面的代码片段。
from .settings import Config
from sources import download
def run(config: Config, command: str, link: str, output_path: str, file_type: str, path: str = None):
if command == "youtube":
download(config=config, output_path=output_path,
link=link, file_type=file_type, path=path
)
// Here you can add additional video sources should you have them
else:
config.logger.error("No valid command selected")
pass
因此,我们导入上面定义的配置文件和另一个简单的下载函数,它执行实际的下载。目前,我们只有 YouTube 是我们视频的唯一来源的情况。根据选择,这可以扩展到其他视频源。
上面导入的下载函数如下:
from pytube import YouTube
from config import Config
from pathlib import Path
from utils import convert_video_to_audio_ffmpeg
def download(config: Config, output_path: str, link: str, file_type: str, path: str = None) -> None:
path = f"{Path.home()}/Desktop/downloads"
# print(*Path(Path.home()).iterdir(), sep="\\n")
if file_type == "mp4":
config.logger.info(f"Downloading file in mp4 format ...")
yt = YouTube(f"<http://youtube.com/watch?v={link}>")
yt.streams.filter(progressive=True, file_extension='mp4').order_by('resolution').desc().first().download(output_path=path, filename=f"{output_path}.{file_type}")
config.logger.info(f"Video download completed")
config.logger.info(f"Converting video to audio ...")
convert_video_to_audio_ffmpeg(path, f"{output_path}.{file_type}", output_path)
config.logger.info(f"Audio convert completed!")
elif file_type == "3gpp":
config.logger.info(f"Downloading file in 3gpp format")
YouTube(f'<https://youtu.be/{link}>').streams.first().download(output_path="~/Desktop", filename=f"{output_path}.{file_type}")
else:
config.logger.error(f"Wrong file extension format provided. Provided value: {file_type}")
raise Exception(f"Wrong file extension format provided. Provided value: {file_type}")
现在,我们需要定义 CLI 应用程序的基本命令。我们从定义命令的回调开始,然后继续处理触发回调的事件。
import typer
from dataclasses import dataclass
import re
import random
import string
@dataclass
class Constants:
app = typer.Typer()
stage_tool_tip = typer.style("DEV or PROD", fg=typer.colors.BRIGHT_GREEN, bold=True, italic=True)
provider_tool_tip = typer.style("YOUTUBE or INSTAGRAM OR FACEBOOK", fg=typer.colors.BRIGHT_GREEN, bold=True, italic=True)
file_type_tool_tip = typer.style("3gpp or mp4. Default value is mp4", fg=typer.colors.BRIGHT_GREEN, bold=True, italic=True)
url_pattern = "^https?:\\/\\/(?:www\\.)?[-a-zA-Z0-9@:%._\\+~#=]{1,256}\\.[a-zA-Z0-9()]{1,6}\\b(?:[-a-zA-Z0-9()@:%_\\+.~#?&\\/=]*)$"
def get_random_string(self, length: int = 10):
combination = string.ascii_lowercase + string.ascii_uppercase + string.digits
return ''.join(random.choice(combination) for i in range(length))
def provider_callback(self, value: str) -> str:
if not value:
raise typer.BadParameter(f"A provider was not provided. Value should be either {self.provider_tool_tip}!")
if value not in ["youtube", "facebook", "instagram"]:
raise typer.BadParameter(f"Invalid value for the provider. Value should be either of {self.provider_tool_tip}!")
return value.lower()
def link_callback(self, value: str) -> str:
if not value:
raise typer.BadParameter(f"A link was not provided!")
if not re.match(self.url_pattern, value):
raise typer.BadParameter(f"Invalid url provided")
m = re.search('v=(.+?)&', value)
if not m:
raise typer.BadParameter("The wrong url format was provided")
return m.group(1)
def file_type_callback(self, value: str) -> str:
if not value:
return "mp4"
if value not in ["3gpp", "mp4"]:
raise typer.BadParameter(f"Invalid url provided. Value should be either of {self.file_type_tool_tip}!")
return value
def output_callback(self, value: str) -> str:
if not value:
return self.get_random_string()
return value
@dataclass
class Downloader(Constants):
def get_provider_options(self):
return typer.Option(
None,
"--provider", "-p",
help="The provider or the video source",
callback=self.provider_callback
)
def get_link_options(self):
return typer.Option(
None,
"--link", "-l",
help="The link to the video source",
callback=self.link_callback
)
def get_file_type_options(self):
return typer.Option(
None,
"--ext", "-x",
help="The prefered file extension to be downloaded",
callback=self.file_type_callback
)
def get_output_options(self):
return typer.Option(
None,
"--output", "-o",
help="The output name of the file to be saved",
callback=self.output_callback
)
在回调中,我们处理来自命令的传入事件,照顾
- 提供商,在本例中可以是代码示例中的 youtube、Facebook 或 Instagram
- 视频的链接
- 文件扩展名
- 以及下载文件将另存为的文件名。
就是这样;申请完成。
然后可以这样调用应用程序:
python3 main.py youtube -l <youtube-video-link> -o awesome-song
或者以一种非常简单的方式,
python3 main.py youtube -l <youtube-video-link>
最后一个命令将在保存文件时为文件生成一个随机名称。
结论
我们探索了一种创建 CLI 应用程序的非常简单的方法,该应用程序使用视频 URL 下载、转换 YouTube 视频并将其保存为音频文件。这可以扩展到其他视频源。
我们已经设法以一种非常可扩展的方式构建项目,这样我们就可以轻松地集成其他资源而无需付出太多努力。只需要很少的修改就可以满足您可能有的任何其他需求。
整个项目可以在这里找到。
标签:CLI,str,音频,value,应用程序,file,path,config,self From: https://blog.51cto.com/u_1213352/6054464