[파이썬] 유튜브 영상 다운로드하고 mp4, mp3로 변환하기(pytube, ffmpeg)

<코드>

from pytube import YouTube  #유튜브영상을 다운로드하기 위한 모듈
import os.path              #경로를 설정하기 위한 모듈
import ffmpeg               #미디어를 변환하기 위한 모듈
from getpass import getuser #기본 경로를 다운로드 폴더로 지정하기 위한 모듈

class Download:
    '''
    파일을 변환하기 위해선 ffmpeg란 프로그램을 별도로 설치해 컴퓨터 환경변수 설정을 마쳐야 함.
    '''
    def __init__(self, link):
        #link 인자는 gui에서 입력된 값을 받을 때 사용
        #컴퓨터 이용자명을 받아서 다운로드폴더를 기본폴더로 지정
        self.parent_dir = f"C:\\Users\\{getuser()}\\Downloads"
        self.yt = YouTube(link)

    def getVideoName(self):
        '''(GUI버전) 비디오 이름을 내보내는 함수'''
        name = self.yt.title
        return name

    def downloadMp3(self):
        '''mp3 파일로 다운로드하는 함수'''
        #mp4형태지만 영상 없이 소리만 있는 파일 다운로드
        stream = self.yt.streams.filter(only_audio=True).first()
        stream.download(self.parent_dir)

        src = stream.default_filename   #mp4로 다운받은 영상제목(파일명과 같음)
        dst = stream.default_filename[0:-3] + 'mp3' #mp3로 변환된 파일명

        #mp4에서 mp3로 변환
        ffmpeg.input(os.path.join(self.parent_dir, src)).output(os.path.join(self.parent_dir, dst)).run(overwrite_output=True)

        #변환되기 전 mp4파일 삭제
        os.remove(os.path.join(self.parent_dir, src))

        return dst #저장한 파일명 리턴

    def downloadMp4(self):
        '''mp4 파일로 다운로드하는 함수'''
        audio = self.downloadMp3()   #mp3파일 다운로드
        video = self.yt.streams.filter(adaptive=True, file_extension='mp4', fps = 30).first()
        print(video)
        video.download(self.parent_dir)  #mp4파일 다운로드

        #mp4로 해상도 높은 파일을 받으면 vcodec만 존재
        #->비디오에 소리를 입히려면 acodec 있는 파일 받아 FFmpeg로 병합
        #->downloadMp3로 mp3파일을 받고 오디오 소스로 사용
        inputAudio = ffmpeg.input(os.path.join(self.parent_dir, audio))
        inputVideo = ffmpeg.input(os.path.join(self.parent_dir, video.default_filename))

        #영상에 소리 입혀 "new.mp4"파일로 내보내기
        ffmpeg.output(inputAudio, inputVideo, os.path.join(self.parent_dir, "new.mp4"), vcodec='copy', acodec='aac').run(overwrite_output=True)

        #변환이 끝나 더 이상 필요 없는 mp3, mp4 파일 지우기
        os.remove(os.path.join(self.parent_dir, video.default_filename))
        os.remove(os.path.join(self.parent_dir, audio))

        #"new.mp4"를 영상 제목으로 바꾸기
        os.rename(os.path.join(self.parent_dir, "new.mp4"), os.path.join(self.parent_dir, video.default_filename))

        return video.default_filename   #저장한 파일명 리턴

 

 

 

<pyqt5를 이용한 GUI>

import sys
from PyQt5.QtWidgets import QApplication, QTextBrowser, QWidget, QPushButton, QLineEdit, QLabel, QHBoxLayout, QVBoxLayout, QFileDialog
from YtDl import Download

class MyApp(QWidget):

    def __init__(self):
        super().__init__()
        self.UI()

    def UI(self):
        '''UI 세팅'''
        #위젯 생성
        self.linkInput = QLineEdit(self)
        self.submitBtn = QPushButton('Submit', self)
        self.fileRoute = QTextBrowser(self)
        self.fileBtn = QPushButton('file...', self)
        self.mp3Btn = QPushButton('mp3', self)
        self.mp4Btn = QPushButton('mp4', self)   
        self.name = QTextBrowser(self)
        self.status = QLabel(self)
        #이벤트 연결
        self.linkInput.returnPressed.connect(self.inputURL)
        self.submitBtn.clicked.connect(self.inputURL)
        self.fileBtn.clicked.connect(self.setFileRoute)
        self.mp3Btn.clicked.connect(self.mp3Download)
        self.mp4Btn.clicked.connect(self.mp4Download)
        self.mp4Btn.clicked.connect(self.mp4Download)
        
        #레이아웃 생성, 기본 설정
        self.vbox = QVBoxLayout()
        self.InputBox = QHBoxLayout()
        self.fileRouteBox = QHBoxLayout()
        self.titleBox = QHBoxLayout()
        self.downloadbox = QHBoxLayout()
        self.setLayout(self.vbox)
        self.setWindowTitle('Youtube Download')
        self.setGeometry(300, 300, 400, 200)
        self.fileRoute.setMaximumHeight(60)
        self.name.setMaximumHeight(60)
        self.name.setMaximumWidth(195)
        self.show()

        #1: 주소 입력 박스
        self.InputBox.addWidget(QLabel('동영상 주소 입력:', self))
        self.InputBox.addWidget(self.linkInput)
        self.InputBox.addWidget(self.submitBtn)
        self.vbox.addLayout(self.InputBox)
        #2: 파일 경로 박스
        self.fileRouteBox.addWidget(QLabel('  다운로드 경로:  ', self))
        self.fileRouteBox.addWidget(self.fileRoute)
        self.fileRouteBox.addWidget(self.fileBtn)
        self.vbox.addLayout(self.fileRouteBox)
        #3: 영상 제목 박스
        self.titleBox.addWidget(QLabel('    영상 제목:      ', self))
        self.titleBox.addWidget(self.name)
        self.titleBox.addStretch(1)
        self.vbox.addLayout(self.titleBox)
        #4: 다운로드 버튼 박스
        self.downloadbox.addStretch(1)
        self.downloadbox.addWidget(self.mp3Btn)
        self.downloadbox.addWidget(self.mp4Btn)
        self.downloadbox.addStretch(1)
        self.vbox.addLayout(self.downloadbox)
        self.vbox.addWidget(self.status)
        #다운로드 버튼 비활성화
        self.btnStatusFalse()

    def btnStatusFalse(self):
        '''버튼 비활성화'''
        self.mp3Btn.setEnabled(False)
        self.mp4Btn.setEnabled(False)
        self.fileBtn.setEnabled(False)


    def inputURL(self):
        '''주소 입력 시 다운로드 모듈 실행,
        버튼 활성화'''
        self.btnStatusFalse()
        self.status.setText('')
        self.obj = Download(self.linkInput.text())
        self.name.setText(self.obj.getVideoName())
        self.fileRoute.setText(self.obj.parent_dir)
        self.fileBtn.setEnabled(True)
        self.mp3Btn.setEnabled(True)
        self.mp4Btn.setEnabled(True)

    def mp3Download(self):
        '''mp3 다운로드'''
        self.status.setText('downloading...')
        self.obj.downloadMp3()
        self.status.setText('complete!')
        
    def mp4Download(self):
        '''mp4 다운로드'''
        self.status.setText('downloading...')
        self.obj.downloadMp4()
        self.status.setText('complete!')

    def setFileRoute(self):
        '''파일 다운로드 경로 지정'''
        fname = QFileDialog.getExistingDirectory(self)
        self.obj.parent_dir = fname
        self.fileRoute.setText(self.obj.parent_dir)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = MyApp()
    sys.exit(app.exec_())

 

2.JPG

 

 

이 버전은 위젯 생성부터 모두 파이썬을 통해 실행한다. 그래서 클래스는 Qwidget을 상속받는다. 이 자체가 하나의 메인 위젯이기 때문이다. 

 

위젯을 생성하고 배치하는 것까지 다 생각하며 하려니 상당히 감을 잡기 어려웠는데, 알고 보니 qt designer란 툴이 있었다.

 

1.JPG

 

 

이 툴을 이용해서 원하는 ui파일을 만들면 손쉽게 디자인이 가능하다.

<pyqt5를 이용한 GUI - qtdesigner 활용>

import sys
from PyQt5.QtWidgets import *
from PyQt5.QtCore import QEventLoop, QObject, pyqtSignal
from PyQt5 import uic, QtGui
from YtDl import Download
from os import popen

#UI파일 연결
#단, UI파일은 Python 코드 파일과 같은 디렉토리에 위치해야한다.
mainUi = uic.loadUiType("project\PythonTool.ui")[0]

#화면을 띄우는데 사용되는 Class 선언
class WindowClass(QMainWindow, mainUi) :
    def __init__(self) :
        super().__init__()
        self.setupUi(self)
        self.menu3.clicked.connect(self.runMenu3)

    def runMenu3(self):
        Menu3(self)

class Menu3(QDialog):
    def __init__(self, parent):
        super(Menu3, self).__init__(parent)
        menu3Ui = "project\YtDlDialog.ui"
        uic.loadUi(menu3Ui, self)
        self.show()

        #이벤트 연결
        self.adressLineEdit.returnPressed.connect(self.inputURL)
        self.submitBtn.clicked.connect(self.inputURL)
        self.pathBtn.clicked.connect(self.setFileRoute)
        self.mp3Btn.clicked.connect(self.mp3Download)
        self.mp4Btn.clicked.connect(self.mp4Download)

        self.btnStatusFalse()

    def btnStatusFalse(self):
        '''버튼 비활성화'''
        self.mp3Btn.setEnabled(False)
        self.mp4Btn.setEnabled(False)
        self.pathBtn.setEnabled(False)

    def inputURL(self):
        '''주소 입력 시 다운로드 모듈 실행,
        버튼 활성화'''
        self.btnStatusFalse()
        self.obj = Download(self.adressLineEdit.text())
        self.titleBrowser.setText(self.obj.getVideoName())
        self.pathBrowser.setText(self.obj.parent_dir)
        self.pathBtn.setEnabled(True)
        self.mp3Btn.setEnabled(True)
        self.mp4Btn.setEnabled(True)

    def mp3Download(self):
        '''mp3 다운로드'''
        self.obj.downloadMp3()
        
    def mp4Download(self):
        '''mp4 다운로드'''
        self.obj.downloadMp4()

    def setFileRoute(self):
        '''파일 다운로드 경로 지정'''
        fname = QFileDialog.getExistingDirectory(self)
        self.obj.parent_dir = fname
        self.pathBrowser.setText(self.obj.parent_dir)

if __name__ == "__main__" :
    #QApplication : 프로그램을 실행시켜주는 클래스
    app = QApplication(sys.argv) 
    #WindowClass의 인스턴스 생성
    myWindow = WindowClass()
    #프로그램 화면을 보여주는 코드
    myWindow.show()
    #프로그램을 이벤트루프로 진입시키는(프로그램을 작동시키는) 코드
    app.exec_()

 

팀 프로젝트 과제로 이 기능을 구현했는데, 여러 기능 중 하나였다.

기능을 선택하면 Qdialog를 띄워서 기능이 실행되도록 했던지라 메인 클래스는 qdialog를 상속받는다.

 

 

 

오직 다운로드 기능만 이용하겠다면 

 

def run(self):
        print("유튜브 동영상 링크를 입력하세요>>", end = " ")
        video = input()
        yt = YouTube(video)
        print(yt.title)

        print("mp3로 저장하려면 1, mp4로 저장하려면 2")
        fileFormat = int(input())

        if(fileFormat == 1):    #mp3 다운로드
            print("mp3로 다운로드합니다.")
            fileName = self.downloadMp3(yt)
            print(f"{fileName}을 다운로드했습니다.")
        else:   #mp4 다운로드
            print("mp4로 다운로드합니다.")
            fileName = self.downloadMp4(yt)
            print(f"{fileName}을 다운로드했습니다.")

a = Download()
a.run()

 

이처럼 다운로드 클래스 내부에 run함수를 만들어 기능을 실행하면 된다.