├── .gitignore ├── Python Youtube downloader 950958136b7941818d625833951a1a95 ├── Capture 1.png ├── Capture 2.png ├── Capture 3.png ├── Capture 4.png └── Capture.png ├── README.md ├── conf_management.py ├── config.py ├── main.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | *.py[cod] 3 | *$py.class 4 | 5 | # Distribution / packaging 6 | .Python build/ 7 | develop-eggs/ 8 | dist/ 9 | downloads/ 10 | eggs/ 11 | .eggs/ 12 | lib/ 13 | lib64/ 14 | parts/ 15 | sdist/ 16 | var/ 17 | wheels/ 18 | *.egg-info/ 19 | .installed.cfg 20 | *.egg 21 | *.manifest 22 | *.spec 23 | 24 | # Log files 25 | pip-log.txt 26 | pip-delete-this-directory.txt 27 | *.log 28 | 29 | # Unit test / coverage reports 30 | htmlcov/ 31 | .tox/ 32 | .coverage 33 | .coverage.* 34 | .cache 35 | .pytest_cache/ 36 | nosetests.xml 37 | coverage.xml 38 | *.cover 39 | .hypothesis/ 40 | 41 | # Translations 42 | *.mo 43 | *.pot 44 | 45 | # PyBuilder 46 | target/ 47 | 48 | # Jupyter Notebook 49 | .ipynb_checkpoints 50 | 51 | # IPython 52 | profile_default/ 53 | ipython_config.py 54 | 55 | # pyenv 56 | .python-version 57 | 58 | # pyflow 59 | __pypackages__/ 60 | 61 | # Environment 62 | .env 63 | .venv 64 | env/ 65 | venv/ 66 | ENV/ 67 | 68 | # If you are using PyCharm # 69 | .idea/**/workspace.xml 70 | .idea/**/tasks.xml 71 | .idea/dictionaries 72 | .idea/**/dataSources/ 73 | .idea/**/dataSources.ids 74 | .idea/**/dataSources.xml 75 | .idea/**/dataSources.local.xml 76 | .idea/**/sqlDataSources.xml 77 | .idea/**/dynamic.xml 78 | .idea/**/uiDesigner.xml 79 | .idea/**/gradle.xml 80 | .idea/**/libraries 81 | *.iws /out/ 82 | 83 | # Sublime Text 84 | *.tmlanguage.cache 85 | *.tmPreferences.cache 86 | *.stTheme.cache 87 | *.sublime-workspace 88 | *.sublime-project 89 | 90 | # sftp configuration file 91 | sftp-config.json 92 | 93 | # Package control specific files Package 94 | Control.last-run 95 | Control.ca-list 96 | Control.ca-bundle 97 | Control.system-ca-bundle 98 | GitHub.sublime-settings 99 | 100 | # Visual Studio Code # 101 | .vscode/* 102 | !.vscode/settings.json 103 | !.vscode/tasks.json 104 | !.vscode/launch.json 105 | !.vscode/extensions.json 106 | .history 107 | .vscode 108 | build -------------------------------------------------------------------------------- /Python Youtube downloader 950958136b7941818d625833951a1a95/Capture 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dynam1co/Python_youtube_video_downloader/696711b34dc5468c3650427b45497c64d9a70222/Python Youtube downloader 950958136b7941818d625833951a1a95/Capture 1.png -------------------------------------------------------------------------------- /Python Youtube downloader 950958136b7941818d625833951a1a95/Capture 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dynam1co/Python_youtube_video_downloader/696711b34dc5468c3650427b45497c64d9a70222/Python Youtube downloader 950958136b7941818d625833951a1a95/Capture 2.png -------------------------------------------------------------------------------- /Python Youtube downloader 950958136b7941818d625833951a1a95/Capture 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dynam1co/Python_youtube_video_downloader/696711b34dc5468c3650427b45497c64d9a70222/Python Youtube downloader 950958136b7941818d625833951a1a95/Capture 3.png -------------------------------------------------------------------------------- /Python Youtube downloader 950958136b7941818d625833951a1a95/Capture 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dynam1co/Python_youtube_video_downloader/696711b34dc5468c3650427b45497c64d9a70222/Python Youtube downloader 950958136b7941818d625833951a1a95/Capture 4.png -------------------------------------------------------------------------------- /Python Youtube downloader 950958136b7941818d625833951a1a95/Capture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Dynam1co/Python_youtube_video_downloader/696711b34dc5468c3650427b45497c64d9a70222/Python Youtube downloader 950958136b7941818d625833951a1a95/Capture.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python Youtube downloader 📺 2 | 3 | Created: Sep 25, 2020 6:20 PM 4 | 5 | Descarga de vídeos de Youtube seleccionando la calidad. También se puede descargar solo el audio de un vídeo. 6 | 7 | ## Pre-requisitos 📋 8 | 9 | - Entorno virtual con las dependencias instaladas del fichero [requirements.txt](requirements.txt) 10 | 11 | ## Configuraciones ⚙️ 12 | 13 | Configurar la ruta donde se descargarán los vídeos: 14 | 15 | En el fichero [config.py](python_youtube_downloader/config.py) cambiaremos la ruta que viene por defecto. 16 | 17 | ## Herramientas/Librerías usadas 🛠️ 18 | 19 | Estas son las herramientas usadas durante el desarrollo del proyecto: 20 | 21 | - [NeoVim](https://neovim.io/): Como editor de código Python. 22 | - [Pytube 9.6.0](https://python-pytube.readthedocs.io/en/latest/): Para descarga de vídeos de Youtube. 23 | - [moviepy](https://pypi.org/project/moviepy/): Para montar el audio y el vídeo en un mismo fichero. 24 | 25 | ## Funcionamiento 🔧 26 | 27 | Podemos descargar los vídeos uno a uno usando su link a Youtube o poner las urls en un fichero de texto y que la aplicación los descargue en bucle. 28 | 29 | Al iniciar la aplicación nos pide que seleccionemos una de las dos opciones: 30 | 31 | ![Python%20Youtube%20downloader%20950958136b7941818d625833951a1a95/Capture.png](Python%20Youtube%20downloader%20950958136b7941818d625833951a1a95/Capture.png) 32 | 33 | ### Descarga un solo link 34 | 35 | Nos pedirá la url del vídeo: 36 | 37 | ![Python%20Youtube%20downloader%20950958136b7941818d625833951a1a95/Capture%201.png](Python%20Youtube%20downloader%20950958136b7941818d625833951a1a95/Capture%201.png) 38 | 39 | Ahora tendremos que seleccionar una de estas tres opciones: 40 | 41 | - Descarga rápida de vídeo y audio: descarga el vídeo junto con el audio en baja calidad 42 | - Descargar vídeo seleccionando la calidad: nos mostrará las calidades disponibles en el vídeo para que seleccionemos una. Esta opción es la que más tarda porque tiene que descargar el vídeo por un lado y el audio por otro para mergearlo después. Este proceso es transparente para el usuario y se hace de forma automática. 43 | - Descargar audio: descarga únicamente el audio de la canción. 44 | 45 | ![Python%20Youtube%20downloader%20950958136b7941818d625833951a1a95/Capture%202.png](Python%20Youtube%20downloader%20950958136b7941818d625833951a1a95/Capture%202.png) 46 | 47 | ### Descarga en bucle 48 | 49 | Nos pedirá la ruta del fichero de texto que contiene los enlaces: 50 | 51 | ![Python%20Youtube%20downloader%20950958136b7941818d625833951a1a95/Capture%203.png](Python%20Youtube%20downloader%20950958136b7941818d625833951a1a95/Capture%203.png) 52 | 53 | El fichero tendrá este formato: 54 | 55 | ![Python%20Youtube%20downloader%20950958136b7941818d625833951a1a95/Capture%204.png](Python%20Youtube%20downloader%20950958136b7941818d625833951a1a95/Capture%204.png) 56 | 57 | Cuando el proceso acabe, veremos los vídeos o audios en la ruta configurada. 58 | 59 | ## Expresiones de gratitud 🎁 60 | 61 | - Comenta a otros sobre este proyecto 📢 62 | - Da las gracias públicamente 🤓 63 | - Sígueme en [Twitter](https://twitter.com/AsensiFj) 🐦 -------------------------------------------------------------------------------- /conf_management.py: -------------------------------------------------------------------------------- 1 | import config as cfg 2 | 3 | 4 | def get_ruta_descargas(): 5 | return cfg.RUTA_DESCARGAS -------------------------------------------------------------------------------- /config.py: -------------------------------------------------------------------------------- 1 | RUTA_DESCARGAS = r"/mnt/d/Youtube" 2 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from ast import Str 2 | from pytube import YouTube 3 | import os 4 | from moviepy.editor import * 5 | import conf_management as cfg 6 | 7 | 8 | parent_dir = '' 9 | 10 | # Windows 11 | # def clear(): os.system('cls') 12 | 13 | # Linux 14 | def clear(): os.system('clear') 15 | 16 | 17 | def menu_inicio() -> Str: 18 | clear() 19 | return input('Selecciona una opción:\n1. Descargar enlace YouTube\n2. Descargar fichero\n3. Salir\n\nOpción (1|2|3): ') 20 | 21 | 22 | def seleccionar_accion() -> Str: 23 | return input('¿Qué deseas hacer?\n\n1. Descarga rápida vídeo y audio\n2. Descarga vídeo seleccionando calidad\n3. Descarga audio\n\nOpción (1|2|3): ') 24 | 25 | 26 | def print_proceso_terminado(): 27 | print('Descarga completada') 28 | 29 | print('-------------------------------------------------------') 30 | 31 | 32 | def pide_url() -> Str: 33 | return input('Url vídeo: ') 34 | 35 | 36 | def procesa_fichero(ruta) -> list: 37 | list_enlaces = [] 38 | 39 | file_object = open(ruta, 'r') 40 | 41 | [list_enlaces.append(linea) for linea in file_object] 42 | 43 | file_object.close() 44 | 45 | return list_enlaces 46 | 47 | 48 | def inicia_proceso_descarga(link, job, loop): 49 | contador = 1 50 | 51 | try: 52 | yt = YouTube(link) 53 | except: 54 | print('Error en el link: {0}'.format(link)) 55 | return 56 | 57 | nombre_video = '' 58 | 59 | print('') 60 | print('-------------------------------------------------------') 61 | print('Título: {0}'.format(yt.title)) 62 | print('Autor: {0}'.format(yt.author)) 63 | print('') 64 | 65 | video_y_audio = yt.streams.filter(progressive=True).order_by('resolution').desc().first() 66 | vids = yt.streams.filter(mime_type="video/mp4").order_by('resolution').desc() 67 | 68 | if job == '1': # Vídeo y audio rápido 69 | video_y_audio.download(parent_dir + '/video') 70 | print_proceso_terminado() 71 | elif job == '3': # Solo audio 72 | ruta_fin = yt.streams.get_audio_only().download(parent_dir + '/audio') 73 | audioclip = AudioFileClip(ruta_fin) 74 | audioclip.write_audiofile(audioclip.filename.replace('.mp4', '.mp3')) 75 | 76 | os.remove(audioclip.filename) 77 | 78 | print_proceso_terminado() 79 | elif job == '2': # Descargar vídeo y audio seleccionando calidad 80 | num_video_descargar = 1 81 | 82 | if not loop: 83 | print('') 84 | print('Seleccionar vídeo (1..{0})'.format(len(vids))) 85 | 86 | for video in vids: 87 | print(' ({1}) - {0}'.format(video, contador)) 88 | contador += 1 89 | 90 | print('') 91 | 92 | num_video_descargar = int(input('Nº vídeo: ')) 93 | 94 | num_video_descargar -= 1 95 | 96 | nombre_video = vids[num_video_descargar].default_filename 97 | nombre_video_final = 'f_' + nombre_video 98 | vids[num_video_descargar].download(parent_dir + '/video') 99 | 100 | yt.streams.get_audio_only().download(parent_dir + '/audio') 101 | 102 | audioclip = AudioFileClip(parent_dir + '/audio/' + nombre_video) 103 | 104 | videoclip2 = VideoFileClip(parent_dir + '/video/' + nombre_video) 105 | videoclip2 = videoclip2.set_audio(audioclip) 106 | 107 | videoclip2.write_videofile(parent_dir + '/video/' + nombre_video_final) 108 | 109 | os.remove(videoclip2.filename) 110 | os.remove(audioclip.filename) 111 | 112 | 113 | if __name__ == "__main__": 114 | tarea = 0 115 | job = 0 116 | enlaces = [] 117 | parent_dir = cfg.get_ruta_descargas() # Obtener ruta donde se guardan las descargas 118 | 119 | while tarea != '3': 120 | enlaces.clear() 121 | tarea = menu_inicio() # Mostrar menú inicial 122 | loop = False 123 | 124 | clear() 125 | 126 | # Llenar una lista con los enlaces a descargar 127 | if tarea == '1': # Un solo vídeo 128 | enlaces.append(pide_url()) 129 | elif tarea == '2': # Fichero de texto 130 | loop = True 131 | ruta_fichero = input('Ruta fichero enlaces: ') 132 | enlaces = procesa_fichero(ruta_fichero) 133 | 134 | # Seleccionar qué tipo de descarga hacer 135 | while job not in ['1', '2', '3']: 136 | clear() 137 | job = seleccionar_accion() 138 | 139 | [inicia_proceso_descarga(x, job, loop) for x in enlaces] 140 | 141 | print_proceso_terminado() 142 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2020.12.5 2 | chardet==4.0.0 3 | decorator==4.4.2 4 | idna==2.10 5 | imageio==2.9.0 6 | imageio-ffmpeg==0.4.3 7 | moviepy==1.0.3 8 | numpy==1.20.2 9 | Pillow==8.2.0 10 | proglog==0.1.9 11 | pytube==10.7.1 12 | requests==2.25.1 13 | tqdm==4.60.0 14 | urllib3==1.26.4 15 | --------------------------------------------------------------------------------