/ src / modules / utils / youtube_tool.py
youtube_tool.py
  1  '''
  2  	QuickSave allows you to download videos from YotuTube
  3  	Copyright (C) 2025  Andrés Chaparro
  4  
  5  	This program is free software: you can redistribute it and/or modify
  6  	it under the terms of the GNU General Public License as published by
  7  	the Free Software Foundation, either version 3 of the License, or
  8  	(at your option) any later version.
  9  
 10  	This program is distributed in the hope that it will be useful,
 11  	but WITHOUT ANY WARRANTY; without even the implied warranty of
 12  	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 13  	GNU General Public License for more details.
 14  
 15  	You should have received a copy of the GNU General Public License
 16  	along with this program.  If not, see <https://www.gnu.org/licenses/>.
 17  '''
 18  
 19  import sys
 20  from kivy.clock import Clock
 21  
 22  # To displays messages
 23  from modules.utils.show_snackbar import ShowSnackbar
 24  
 25  '''
 26  	To run the task of downloading 
 27  	videos asynchronously
 28  '''
 29  from threading import Thread
 30  
 31  # To check the platform
 32  from kivy import platform
 33  
 34  '''
 35  	This is the most important
 36  	dependency that allows you
 37  	to download the videos
 38  '''
 39  import yt_dlp
 40  
 41  '''
 42  	To vibrate when the
 43  	download is complete
 44  '''
 45  from modules.utils.vibrator_tool import VibratorTool
 46  
 47  '''
 48  	This class handles errors that
 49  	may occur in yt_dlp, if this is
 50  	not done an excepcion wiil be
 51  	throen
 52  '''
 53  class CustomLogger:
 54  	def debug(self, msg):
 55  		pass
 56  	def warning(self, msg):
 57  		pass
 58  	def error(self, msg):
 59  		print(msg, file=sys.stderr)
 60  
 61  class YoutubeTool():
 62  	def __init__(self, url):
 63  		'''
 64  			Removes spaces at the
 65  			beginning and end of
 66  			the URL
 67  		'''
 68  		self.url = str(url.replace(" ", ""))
 69  		print(f"url: {self.url}")
 70  	
 71  	def download(self, format_id, output_path="/storage/emulated/0/Download/"):
 72  		'''
 73  			This method is responsible for
 74  			downloading the video in the
 75  			selected path and id
 76  		'''
 77  		if not platform == 'android':
 78  			output_path = "."  # output manager to the screen
 79  		'''
 80  			yt_dlp settings
 81  		'''
 82  		ydl_opts = {
 83  			'external_downloader': 'aria2c',
 84  			'format': format_id,          # Usar el ID del formato
 85  			'outtmpl': f"{output_path}/%(title)s.%(ext)s",
 86  			'quiet': False,              # Mostrar progreso
 87  			'logger': CustomLogger(),
 88  		}
 89  		
 90  		'''
 91  			Create a thread to download the video
 92  		'''
 93  		
 94  		thread = object
 95  		with yt_dlp.YoutubeDL(ydl_opts) as ydl:
 96  			thread = Thread(target=ydl.download, args=([self.url],))
 97  			thread.daemon = True
 98  			thread.start()
 99  		
100  		ShowSnackbar.show("¡La descarga ha iniciado!")
101  		
102  		'''
103  			Create a thread to check
104  			if the download thread has finished
105  		'''
106  		
107  		thread_find_out = Thread(target=self.get_state, args=([thread]))
108  		thread_find_out.daemon = True
109  		thread_find_out.start()
110  		
111  	def get_state(self, thread):
112  		'''
113  			This method checks if
114  			the download thread has already finished
115  			and if so, it displays a message and vibrates
116  		'''
117  		band = True
118  		while(band):
119  			if not thread.is_alive():
120  				'''
121  					Note: Other threads cannot update
122  					the kivy interface; you must use
123  					kivy's clock to schedule method
124  					execution on the main kivy thread.
125  				'''
126  				Clock.schedule_once(self.show_message)
127  				band = False
128  	
129  	def show_message(self, *args):
130  		'''
131  			Displays a message and vibrates
132  		'''
133  		ShowSnackbar.show("La descarga ha terminado")
134  		VibratorTool.vibrate_in_time(1)
135  
136  	def get_metadata(self):
137  		'''
138  			This method is responsible for
139  			obtaining the video data
140  			without downloading it
141  		'''
142  		ydl_opts = {
143  			'external_downloader': 'aria2c',
144  			'quiet': False,           # Evita logs innecesarios
145  			'no_warnings': False,    # Omite advertencias
146  			'logger': CustomLogger(),
147  		}
148  
149  		with yt_dlp.YoutubeDL(ydl_opts) as ydl:
150  			print(f"url: {self.url}")
151  			# Extract metadata without downloading the video
152  			info = object
153  			try:
154  				info = ydl.extract_info(self.url, download=False)
155  			except Exception as e:
156  				print(f"Error extracting data: {e}")
157  			
158  			try:
159  				# basic data
160  				metadata = {
161  					'title': info.get('title', 'Sin título'),
162  					'duration': info.get('duration', 'Desconocido'),
163  					'uploader': info.get('uploader', 'Desconocido'),
164  					'view_count': info.get('view_count', 0),
165  					'formats': [],
166  				}
167  			
168  				# Processes every available format
169  				for fmt in info.get('formats', []):
170  					format_info = {
171  						'format_id': fmt.get('format_id'),
172  						'ext': fmt.get('ext'),
173  						'resolution': fmt.get('resolution', 'audio'),
174  						'filesize': fmt.get('filesize', 'Desconocido'),  # En bytes
175  						'quality': fmt.get('quality'),
176  					}
177  					metadata['formats'].append(format_info)
178  				
179  				'''
180  					Returns the metadata as a list of dictionaries
181  				'''
182  				return metadata
183  
184  			except Exception as e:
185  				print(f"Error getting metadata: {e}")
186  				return None
187  
188  
189  
190  
191  
192  
193  
194