Sản phẩm sau khi làm

http://coffeetube.herokuapp.com/

Yêu cầu

Nói chung thì giờ download nhạc từ youtube thì nó có vô số cách rồi. Nhưng tự mình làm một trang web của riêng mình để download thì cảm giác nó vẫn ... cool ngầu hơn nhỉ?

Mục tiêu của mình là xây dựng một trang download nhạc từ youtube có khả năng như sau:

  • Có khả năng tìm kiếm.
  • Trả về một list các clip sau khi tìm kiếm - phải có ảnh thumbnail.
  • Lười nên mình còn muốn nó có gợi ý để mình không cần phải gõ hết chữ ra mới tìm được.

Thư viện cần dùng

flask
requests
youtube_dl
bs4

Hosting

Heroku

Đây là một trang hosting cho nhiều loại ngôn ngữ, mình nghĩ có khá nhiều bài viết về trang này rồi. 

Hành động

Tạo trang chủ

Đầu tiên mình tạo ra một trang html đơn giản gồm một form tìm kiếm (phần trang trí là mình lượm nhặt khá ngu).

Để form tìm kiếm có thể suggestion được, mình dùng 1 free api key trên google và sử dụng script này:


$(document).ready(function(){
            $("#youtube").autocomplete({
    source: function(request, response){
        /* Google Developer ID (optional) */
        var apiKey = 'AI39si7ZLU83bKtKd4MrdzqcjTVI3DK9FvwJR6a4kB_SW_Dbuskit-mEYqskkSsFLxN5DiG1OBzdHzYfW0zXWjxirQKyxJfdkg';
        /* Search keyword */
        var query = request.term;
        /* youtube sorgusu */
        $.ajax({
            url: "http://suggestqueries.google.com/complete/search?hl=en&ds=yt&client=youtube&hjson=t&cp=1&q="+query+"&key="+apiKey+"&format=5&alt=json&callback=?",  
            dataType: 'jsonp',
            success: function(data, textStatus, request) { 
               response( $.map( data[1], function(item) {
                    return {
                        label: item[0],
                        value: item[0]
                    }
                }));
            }
        });
    },
    /* You can use transaction is selected here to */
    select: function( event, ui ) {
        $.youtubeAPI(ui.item.label);
    }
    });

            $('button#submit').click(function(){
    var value = $('input#youtube').val();
        $.youtubeAPI(value);
    });


            $.youtubeAPI = function(kelime){
    var sonuc = $('#sonuc');
    sonuc.html('Arama gerçeklestiriliyor...');
    $.ajax({
        type: 'GET',
        url: 'http://gdata.youtube.com/feeds/api/videos?q=' + kelime + '&max-results=15&v=2&alt=jsonc',
        dataType: 'jsonp',
        success: function( veri ){
            if( veri.data.items ){
                sonuc.empty();
                $.each( veri.data.items, function(i, data) {
                    sonuc.append('<div class="youtube">\

                        <img src="' + data.thumbnail.sqDefault + '" alt="" />\

                        <h3><a href="javascript:void(0)" onclick="$.youtubePlay(\'' + data.id + '\', \'' + data.content[5] + '\')">' + data.title + '</a></h3>\

                        <p>' + data.description + '</p>\
                    </div>\

                    <div class="youtubeOynat" id="' + data.id + '"></div>');
                });
            }
            else {
                sonuc.html('<div class="hata"><strong>' + kelime + '</strong> ile ilgili hiç video bulunamadi!</div>');
            }
        }
    });
    }
            $.youtubePlay = function(yid, frame){
    $('.youtubeOynat').slideUp().empty();
    $('#'+yid).slideDown().html('<iframe src="'+ frame +'&autoplay=1" style="width: 100%; box-sizing: border-box; height: 300px" />');
    }
        });

Trong route index ở file app.py, mình return về template index.html.

@app.route('/', methods=['GET'])
def main():
return render_template('index.html',year = year)

Xử lí dữ liệu đã search

@app.route('/result', methods=['POST','GET'])
def result():
search = request.form['youtube']
try:
query_string = urllib.parse.urlencode({"search_query": search})
page = requests.get("http://www.youtube.com/results?" + query_string)
soup = BeautifulSoup(page.content, 'html.parser')
display = {}
for vid in soup.find_all(class_="yt-lockup-content"):
print(vid)
#print(vid.find("a")["href"])
#print(vid.find("a")["title"])
print(vid.find(class_="yt-lockup-description yt-ui-ellipsis yt-ui-ellipsis-2"))
if vid.find(class_="yt-lockup-description yt-ui-ellipsis yt-ui-ellipsis-2") is not None:
des = vid.find(class_="yt-lockup-description yt-ui-ellipsis yt-ui-ellipsis-2").get_text()
else:
des = " "
link = "https://youtube.com"+vid.find("a")["href"]
thumbnail = "http://img.youtube.com/vi/%s/0.jpg" % vid.find("a")["href"][9:]
display[vid.find("a")["title"]] = [thumbnail,des,link]
except EnvironmentError:
print('NO')
return render_template('result.html', display = display,year = year)

Sau khi nhận được đoạn text từ form search của trang chủ. Mình dán đoạn text đó vào sau youtube.com/results? và sử dụng Beautifulsoup (Một thư viện của python dùng để scrape dữ liệu) để scrape dữ liệu của trang đó.

Sau đó mình lưu lại thumbnail, link, description và tittle của clip đó.

Trang result

Nếu bạn vào file result.html. Bạn sẽ thấy mình dùng một vòng lặp for để tải những dữ liệu mà mình đã lưu từ def result lên.

Trên thực tế, toàn trang đó cũng lại là một cái form rất lớn. Mỗi thumbnail và link là một cái button chứa value là đường link của clip đó. Khi mà người dùng bấm vào ảnh hoặc đường link. Nó sẽ gửi một POST request đến route download.

Xử lí dữ liệu được gửi từ result

@app.route('/download', methods=['POST','GET'])
def download():
url = request.form["song"]
options = {
'format': 'bestaudio/best', # choice of quality
'outtmpl': '%(id)s', # name the file the ID of the video
} # only download single song, not playlist

ydl = youtube_dl.YoutubeDL(options)
song_name = ydl.extract_info(url, download=False)
savepath = make_savepath(song_name['title'].replace(" ", ""))
savepath = re.sub('[^A-Za-z0-9]+', '', savepath)
savepath = make_savepath((savepath))
with ydl:
    result = ydl.extract_info(url, download=True)
    os.rename(result['id'], savepath)
    print("Downloaded and converted %s successfully!" % savepath)
    try:
        return send_file(savepath,savepath, as_attachment=True)
    except Exception as e:
        return str(e)

Nhận được link clip từ trang result. Mình sử dụng thư viện youtubedl để tải mp3 về. dùng Regex để xóa ký tự đặc biệt. Sau đó send_file ngược lại cho người dùng là xong.

Cuối cùng, mình chạy web flask bằng cách:

if __name__ == '__main__':
port = int(os.getenv('PORT', 8000))
print("Starting app on port %d" % port)
app.run(debug=False, port=port, host='0.0.0.0')

Push lên heroku

Mình tạo 2 file Procfile và requirements.

File Procfile bao gồm cách để bạn chạy chương trình. Ở đây mình ghi: web: python app.py

Còn requirements là những thư viện mình cần phải dùng.

Bạn đăng kí một tài khoản heroku. Sau đó link nó với github repository mà bạn đã sử dụng. Để chế độ auto deploy, vậy là xong!

Github

https://github.com/PhiHuyHoang/youtubedownload

Chúc các bạn thành công.

 

Nguồn: https://kipalog.com/posts/Xay-dung-mot-trang-web-tai-nhac-tu-youtube-bang-Python-Flask-va-BeautifulSoup