Django_YouTube動画のURLを入力してmp3にコンバートしてダウンロードするアプリ #296日目
【一言日記】
今日は熟練営業マンの凄さを痛感しました。池袋久しぶりに行ったなー。
PythonでYouTubeから動画をダウンロードする方法の一つである「yt-dlp」を用いて、Djangoでmp3コンバーターを作成してます。
yt-dlp自体に違法性はないと判断されているらしいですが、動画は著作権やYouTube側の権利など色々あると思うので、以下の内容を扱われる際はくれぐれも著作者への配慮をもって、自己責任で行ってください。
今回はDjangoの実装なので、Pythonでyt-dlpを動作させる部分はこちらをご参照ください。
ダウンロードまでの大まかな流れは以下です。
①FormViewからURLの入力を受け取ってバリデーションチェック
②URLをyt-dlpに渡す(mp3に変換、保存先はDjangoのMEDIAファイル)
④ViewからGETリクエストを送信してファイルダウンロード
↓こんなアプリが出来上がります(見た目は無視してください)
MEDIAファイルへのパスを有効にする
MEDIAファイルはユーザーの入力によって動的にファイルを作り、それを保存しておきたい時に使うものです。
基本設定は以下です。これで「media_root」という名前のフォルダが作られます。
[config/settings.py]
MEDIA_URL = '/media_root/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media_root')
加えてアプリ側のurlに設定する必要があります。
一番下のところです。
[app/urls.py]
urlpatterns = [
path('', YoutubeConverter.as_view(), name='converter'),
path('download/<str:file_name>/', FileDownload, name='download')
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
これでmedia_rootにアクセスできるようになりました。
本来ならば、後はmodelの中でFileFieldやImageFieldを使ってファイルを管理することができます。
ただ、今回厄介なのは、ファイルを生成するのはユーザーではなくyt-dlpである点です。フロント側からの入力がないので、うまくmodelに入れて管理していくことができません。
一先ず進めます。
formを定義する
URLを入力するためのformを定義します。URL形式になっているか、空欄になっていないか等をバリデーションチェックします。最終的にURLのリストを返しています。
[forms.py]
from django import forms
from django.core.exceptions import ValidationError
import re
class EnterURL(forms.Form):
urls = forms.CharField(widget=forms.Textarea(attrs={'placeholder': '複数のURLは改行して入力'}))
def clean(self):
data = super().clean()
if not data:
raise ValidationError("URLを入力してください")
urls = data['urls'].split()
for url in urls:
if not re.match("https?://[\w/:%#\$&\?\(\)~\.=\+\-]+", url):
raise ValidationError("URLではない入力値が存在しています")
if len(urls) > 10:
raise ValidationError("同時に処理できるURLは10個までです")
return urls
Viewを定義する
formを扱うためのViewを定義します。コンバーターとダウンロード機能の2つが定義されています。yt-dlpの保存先パスにはMEDIA_ROOTを用いています。
[views.py]
from yt_dlp import YoutubeDL
import os, glob
from django.http import HttpResponse
from config.settings import MEDIA_ROOT
from django.views.generic.edit import FormView
from django.http import FileResponse
from .forms import EnterURL
class YoutubeConverter(FormView):
form_class = EnterURL
template_name = 'youtube-converter.html'
def form_valid(self, form) -> HttpResponse:
urls = form.cleaned_data
ydl_opts = {
'format': 'bestaudio/best',
'outtmpl': MEDIA_ROOT + '/%(title)s.%(ext)s',
'postprocessors': [
{'key': 'FFmpegExtractAudio', 'preferredcodec': 'mp3', 'preferredquality': '192'},
{'key': 'FFmpegMetadata'},
],
}
with YoutubeDL(ydl_opts) as ydl:
ydl.download(urls)
file_names = os.listdir(MEDIA_ROOT)
ctxt = self.get_context_data(form=form, file_names=file_names)
return self.render_to_response(ctxt)
def FileDownload(request, file_name):
file_path = f'media_root/{file_name}'
return FileResponse(open(file_path, "rb"), as_attachment=True)
FileDownload()は別ファイルで定義してもいいかもしれません。
urls.pyは冒頭のものをご参照ください。
テンプレートを定義する
formに特別なことはしていません。ダウンロード部分は2パターンを試しています。formタグでhttpリクエストを出すパターンと、aタグでリンクを貼ってリクエストを出すパターンです。どちらでもダウンロードできます。
[youtube-converter.html]
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
</head>
<body>
<h1>Youtube Converter</h1>
<div>
<h3>YouTube動画のURLを入力</h3>
<form method="post">
{% csrf_token %}
{% for field in form %}
<p>
{{ field }}
</p>
{% endfor %}
<input type="submit" value="mp3へ変換" />
</form>
</div>
<div>
<h3>↓(変換後)クリックしてダウンロード</h3>
{% for file_name in file_names %}
{{ file_name }}
<form action="{% url 'download' file_name %}" method="get">
<input type="submit" value="ダウンロード">
</form>
<br>
<a href="{% url 'download' file_name %}">{{ file_name }}</a>
<br>
{% endfor %}
</div>
<div>
{% for error in form.non_field_errors %}
{{ error }}
{% endfor %}
</div>
</body>
これで冒頭のようなアプリが完成します。
ここまでお読みいただきありがとうございました!
参考
この記事が気に入ったらサポートをしてみませんか?