15分鐘
Flask一個Youtube下載網站
2019/05/26 SIRLA定期聚
by 土豆
成品
git clone https://github.com/SIRLA-FJULIS/FlaTuber
Pytube介紹
pip install pytube
import pytube
url = "https://www.youtube.com/watch?v=elsh3J5lJ6g"
yt = Youtube(url)
video = yt.streams.filter(subtype='mp4').first()
video.download()
查看所有可下載項目
<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">
<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028">
<Stream: itag="248" mime_type="video/webm" res="1080p" fps="30fps" vcodec="vp9">
<Stream: itag="136" mime_type="video/mp4" res="720p" fps="30fps" vcodec="avc1.4d401f">
<Stream: itag="247" mime_type="video/webm" res="720p" fps="30fps" vcodec="vp9">
<Stream: itag="135" mime_type="video/mp4" res="480p" fps="30fps" vcodec="avc1.4d401e">
<Stream: itag="244" mime_type="video/webm" res="480p" fps="30fps" vcodec="vp9">
<Stream: itag="134" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.4d401e">
<Stream: itag="243" mime_type="video/webm" res="360p" fps="30fps" vcodec="vp9">
<Stream: itag="133" mime_type="video/mp4" res="240p" fps="30fps" vcodec="avc1.4d4015">
<Stream: itag="242" mime_type="video/webm" res="240p" fps="30fps" vcodec="vp9">
<Stream: itag="160" mime_type="video/mp4" res="144p" fps="30fps" vcodec="avc1.4d400c">
<Stream: itag="278" mime_type="video/webm" res="144p" fps="30fps" vcodec="vp9">
<Stream: itag="140" mime_type="audio/mp4" abr="128kbps" acodec="mp4a.40.2">
<Stream: itag="171" mime_type="audio/webm" abr="128kbps" acodec="vorbis">
<Stream: itag="249" mime_type="audio/webm" abr="50kbps" acodec="opus">
<Stream: itag="250" mime_type="audio/webm" abr="70kbps" acodec="opus">
<Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus">
yt.streams.all()
Progressive vs. DASH
<Stream: itag="18" mime_type="video/mp4" res="360p" fps="30fps" vcodec="avc1.42001E" acodec="mp4a.40.2">
<Stream: itag="137" mime_type="video/mp4" res="1080p" fps="30fps" vcodec="avc1.640028">
<Stream: itag="251" mime_type="audio/webm" abr="160kbps" acodec="opus">
DASH: Dynamic Adaptive Streaming over HTTP
讓高品質影像播放更加流暢,但聲音與影像會分開
Progressive
分段式,載入較慢,但影音合在一起,最高只支援到720p
filter()
yt.streams.filter(progressive=True).all()
只出現progressive的結果
yt.streams.filter(progressive=True, subtype='mp4').all()
progressive & 格式為mp4
yt.streams.filter(only_audio=True, subtype='mp4').all()
只出現聲音 & 格式為mp4
order_by()
yt.streams.filter(progressive=True, subtype='mp4').order_by('resolution').desc().first()
可以搭配order_by()以及first()來選擇畫質(音質)最好的
yt.streams.filter(only_audio=True, subtype='mp4').order_by('abr').desc().first()
音質最好的mp4
畫質最好的progressive mp4
實作
檔案結構
|--flaTuber
|--static
|--css
|--main.css
|--templates
|--base.html
|--index.html
|--venv
app.py
建立表單class
from pytube import YouTube
from flask import Flask, render_template, flash, redirect, url_for
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired
app = Flask(__name__)
app.config['SECRET_KEY'] = "flaTuber"
class urlForm(FlaskForm):
url = StringField(validators = [DataRequired()])
video_submit = SubmitField('Video')
audio_submit = SubmitField('Audio')
一個輸入欄位,兩顆submit按鈕
建立路由
@app.route('/', methods=['GET', 'POST'])
def index():
form = urlForm()
if form.validate_on_submit():
pass
# 待會補上
flash('Done!')
return redirect(url_for('index'))
return render_template("index.html", form=form)
一個輸入欄位,兩顆submit按鈕
模板
<!DOCTYPE html>
<html lang="zh-TW">
<head>
<link rel="icon" type="image/png" href="../static/../static/images/logo.png" />
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/main.css') }}">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>{% block title %}{% endblock title %}</title>
</head>
<body class="bg-light">
<div id="wrap">
<div id="main" class="container d-flex flex-column">
{% for message in get_flashed_messages() %}
<div class="alert alert-success">
<button type="button" class="close" data-dismiss="alert">×</button>
{{ message }}
</div>
{% endfor %}
{% block content %}
{% endblock content %}
</div>
</div>
<footer class="footer bg-secondary text-light">
<p class="footer_text text-center pt-3">Created by 土豆</a></p>
</footer>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
</body>
</html>
templates/base.html
模板
{% extends "base.html" %}
{% block title %}FlaTuber{% endblock title %}
{% block content %}
<form method="POST">
<h1 class='page-title mt-5 text-primary' style="text-align: center; font-size: 3.5rem">Flatuber</h1>
<div class="mt-5 form-group col-xl-5 col-md-6" style="margin: 15px auto">
{{ form.hidden_tag() }}
{{ form.url(placeholder="請輸入連結", class="form-control") }}
</div>
<div class="text-center">
{{ form.video_submit(class="btn btn-primary mx-1 bg-danger", id="btnVideo") }}
{{ form.audio_submit(class="btn btn-primary mx-1 bg-success", id="btnAudio") }}
</div>
</form>
{% endblock content %}
templates/index.html
模板
/* 避免sticky navbar 覆蓋到內容 */
body {
padding-top: 70px;
}
body,input,button,select,textarea{
font-family: "Helvetica Neue", Helvetica, Arial, "微軟正黑體", "微软雅黑", sans-serif;
}
/* 讓footer可以正常出現在下方 */
html, body {
height: 100%;
}
#wrap {
min-height: 100%;
}
#main {
overflow: auto;
padding-bottom: 50px;
}
.footer {
position: relative;
margin-top: -50px; /* negative value of footer height */
height: 50px;
clear:both;
}
.footer_text{
font-size: 0.9rem;
margin: 0;
}
.sirla_link {
color: inherit;
text-decoration: underline;
}
static/css/main.css
主要邏輯
@app.route('/', methods=['GET', 'POST'])
def index():
form = urlForm()
if form.validate_on_submit():
press_video = form.video_submit.data
press_audio = form.audio_submit.data
url = form.url.data
yt = YouTube(url)
if press_video:
video = yt.streams.filter(subtype='mp4', progressive=True).order_by('resolution').desc().first()
video.download()
elif press_audio:
audio = yt.streams.filter(subtype='mp4', only_audio=True).order_by('abr').desc().first()
audio.download()
flash('Done!')
return redirect(url_for('index'))
return render_template("index.html", form=form)
讀取submit的data值,得知使用者按下哪顆按鈕
謝謝聆聽
15分鐘Falsk一個Youtube下載網站
By Sam Yang
15分鐘Falsk一個Youtube下載網站
- 587