Inicio Apuntes FPApuntes DAMAcceso a Datos Comp-Rueba-URL. Herramienta de Verificación de URLs con GUI

Comp-Rueba-URL. Herramienta de Verificación de URLs con GUI

Creado con PYTHON 3.10

Publicado por entreunosyceros

Una vez más aquí … y ya hacía tiempo. Después de un tiempo ocupado con algunos proyectos, y sin demasiado tiempo libre, por fin tengo un rato para publicar algo que un «am1j0» me ha sugerido, o más bien me ha dejado caer sutilmente. El caso es que este hombre me decía que el adblock ya no le evitaba tener que tragarse los anuncios de Youtube, y que esto le resultaba muy molesto por que no podía ver … lo que sea que vea en Youtube … sin sus buenos cuatro o cinco cortes publicitarios. Pues bien, después de recomendarle que se instalase HeadSet, pensando un poco se me ocurrió modificar otro proyecto que ya tengo empezado (con otros fines más didácticos) y así reproducir en local los vídeos de Youtube, lo cual hace que te puedas evitar tantos anuncios

Y bueno, una vez picado el código, le he añadido también la posibilidad de reproducir también las listas que los usuarios creen en Youtube. Y como extra, pues el programa hace lo mismo con las listas m3u o con las url m3u8. Que como todo el que ha leído alguna vez este blog, sabe que nos permitirá ver IPTV de manera cómoda, sin necesidad de instalar Kodi en nuestros equipos. Para la reproducción vamos a utilizar VLC, que como todo el mundo sabe, es un reproductor muy completo y extendido en todos los S.O. que se precien.

En fin, la idea de este proyecto surgió pensando que hoy en día, la gestión y verificación de URLs, es algo que es algo que se ha vuelto crucial para asegurar la validez y accesibilidad del contenido en línea. Comp-Rueba-URL es una herramienta desarrollada en Python 3.10 que permite verificar la validez delas URL’s de forma rápida, aun que bastante básica (esto todavía tiene mucho que se podría desarrollar). Aun que esto en principio nació como una aplicación para la terminal, al final le he puesto una interfaz gráfica sencilla basada en PyQt5. Esta aplicación está diseñada para ser fácil de usar tanto para principiantes como para profesionales.

Características generales de Comp-Rueba-URL

Listado Youtube ejecutado en VLC
  1. Interfaz gráfica amigable: Comp-Rueba-URL utiliza PyQt5 para ofrecer una interfaz gráfica de usuario (GUI) limpia y fácil de navegar. Esto elimina la necesidad de interactuar con la línea de comandos, haciendo la herramienta accesible para usuarios con diferentes niveles de experiencia técnica.
  2. Verificación de URLs: La principal funcionalidad de la herramienta es la verificación de URLs. Los usuarios pueden cargar una URLs con una lista m3u o de Youtube y Comp-Rueba-URL verificará cada una de ellas, indicando si son accesibles o no. Si lo son, se van a guardar en un archivo llamado listas.txt y abrirá las URL que guarde en ese archivo en VLC. También nos permitirá verificar URL’s simples de Youtube o m3u8, para realizar la comprobación, y después abrirlas en VLC.
  3. Gestión de dependencias automática: El programa incluye unas funciones para verificar e instalar automáticamente todas las dependencias necesarias, como VLC y FFmpeg, asegurando que todos los componentes requeridos estén presentes y actualizados en el sistema.
  4. Soporte multiplataforma: Aunque inicialmente se diseñó para sistemas operativos basados en Unix, la herramienta también se puede ejecutar en Windows, siempre que se cumplan ciertos requisitos previos.
  5. Log de errores: Cuenta con un log que va a ir registrando todo los mensajes que el programa genere. Esto nos puede servir para depurar errores que se produzcan a la hora de validar o reproducir las URL’s.
  6. Desarrollado con Python 3.10: El programa lo he desarrollado con esta versión de Python, que es la que tengo instalada en el equipo que utilice para picar el código. Supongo que con otras versiones de Python funcionará también, pero la verdad es que no lo he probado.

El código de Comp-Robar-URL

URL no disponible

El proyecto está organizado de manera modular, lo que creo que puede facilitar su mantenimiento y escalabilidad. A continuación, se describen los principales componentes del proyecto:

Index.py

Este es el punto de entrada de la aplicación. Este código es un script de Python que configura y lanza una aplicación gráfica para verificar URLs utilizando PyQt5. Además, implementa algunas funciones auxiliares para la gestión de dependencias, logging y limpieza de archivos temporales.

# Importar la función de verificación de dependencias
from dependencias import ensure_all_dependencies
import logging

# Verificar e instalar todas las dependencias necesarias
ensure_all_dependencies()
# Configurar logging para registrar errores
logfile_path = 'logfile.log'
logging.basicConfig(filename=logfile_path, level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s')

import atexit
import os
import sys
from PyQt5.QtWidgets import QApplication
from url_checker import URLChecker



# Función para eliminar listas.txt al salir
def cleanup():
    listas_file = 'listas.txt'
    if os.path.exists(listas_file):
        os.remove(listas_file)
        logging.info("Archivo listas.txt eliminado al salir del programa.")

# Registrar la función cleanup para que se ejecute al salir
atexit.register(cleanup)

if __name__ == "__main__":
    try:
        app = QApplication(sys.argv)
        url_checker = URLChecker()
        url_checker.show()
        sys.exit(app.exec_())
    except Exception as e:
        logging.error(f"Error al ejecutar la aplicación: {str(e)}")
        sys.exit(1)

Dependencias.py

Este módulo se encarga de la gestión de dependencias. El código está diseñado para asegurar que todas las dependencias necesarias para la aplicación estén instaladas antes de su ejecución. Esto incluye dependencias de Python, así como aplicaciones externas como VLC y FFmpeg, que son útiles para la manipulación de medios. La instalación de estas dependencias se maneja de manera específica para cada sistema operativo (Gnu/Linux, Windows y macOS). Aun que tengo que decir que la única prueba real que he hecho ha sido en Gnu/Linux.

import platform
import subprocess
import os
import urllib.request

import sys

# Función para instalar dependencias de Python desde un archivo requirements.txt
def install_python_dependencies():
    requirements_path = 'requirements.txt'
    subprocess.run(['pip3', 'install', '--user', '-r', requirements_path], check=True)


# Función para verificar e instalar todas las dependencias necesarias
def ensure_all_dependencies():
    install_python_dependencies()
    ensure_vlc_installed()
    ensure_ffmpeg_installed()

# Función para instalar VLC en Windows
def install_vlc_windows():
    vlc_installer_url = "https://get.videolan.org/vlc/3.0.16/win64/vlc-3.0.16-win64.exe"
    installer_path = "vlc_installer.exe"
    urllib.request.urlretrieve(vlc_installer_url, installer_path)
    subprocess.run([installer_path, '/S'], check=True)
    os.remove(installer_path)

# Función para instalar VLC en macOS
def install_vlc_mac():
    try:
        subprocess.run(['brew', '--version'], check=True)
    except FileNotFoundError:
        subprocess.run(['/bin/bash', '-c', "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"], check=True)
    subprocess.run(['brew', 'install', 'vlc'], check=True)

# Función para instalar VLC en Linux (Debian/Ubuntu)
def install_vlc_linux():
    subprocess.run(['sudo', 'apt', 'update'], check=True)
    subprocess.run(['sudo', 'apt', 'install', '-y', 'vlc'], check=True)

# Función para verificar e instalar VLC en el sistema operativo correspondiente
def ensure_vlc_installed():
    if platform.system() == 'Windows':
        try:
            subprocess.run(['vlc', '--version'], check=True)
        except FileNotFoundError:
            install_vlc_windows()
    elif platform.system() == 'Darwin':
        try:
            subprocess.run(['/Applications/VLC.app/Contents/MacOS/VLC', '--version'], check=True)
        except FileNotFoundError:
            install_vlc_mac()
    elif platform.system() == 'Linux':
        try:
            subprocess.run(['vlc', '--version'], check=True)
        except FileNotFoundError:
            install_vlc_linux()

# Función para mostrar un mensaje al usuario de Windows sobre cómo instalar FFmpeg
def show_ffmpeg_install_message():
    from PyQt5.QtWidgets import QApplication, QMessageBox
    app = QApplication(sys.argv)
    msg = QMessageBox()
    msg.setIcon(QMessageBox.Information)
    msg.setWindowTitle("FFmpeg no encontrado")
    msg.setText("FFmpeg no está instalado en su sistema. Por favor, descárguelo e instálelo desde:\nhttps://ffmpeg.org/download.html")
    msg.setInformativeText("Es importante asegurarse de añadir FFmpeg al PATH del sistema. O pásate a Gnu/Linux!!")
    msg.setStandardButtons(QMessageBox.Ok)
    msg.exec_()

# Función para instalar ffmpeg en Windows (descarga el zip pero no configura el PATH)
def install_ffmpeg_windows():
    show_ffmpeg_install_message()

# Función para instalar ffmpeg en macOS
def install_ffmpeg_mac():
    try:
        subprocess.run(['brew', '--version'], check=True)
    except FileNotFoundError:
        subprocess.run(['/bin/bash', '-c', "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"], check=True)
    subprocess.run(['brew', 'install', 'ffmpeg'], check=True)

# Función para instalar ffmpeg en Linux (Debian/Ubuntu)
def install_ffmpeg_linux():
    subprocess.run(['sudo', 'apt', 'update'], check=True)
    subprocess.run(['sudo', 'apt', 'install', '-y', 'ffmpeg'], check=True)

# Función para verificar e instalar ffmpeg en el sistema operativo correspondiente
def ensure_ffmpeg_installed():
    if platform.system() == 'Windows':
        try:
            subprocess.run(['ffmpeg', '--version'], check=True)
        except FileNotFoundError:
            install_ffmpeg_windows()
    elif platform.system() == 'Darwin':
        try:
            subprocess.run(['ffmpeg', '--version'], check=True)
        except FileNotFoundError:
            install_ffmpeg_mac()
    elif platform.system() == 'Linux':
        try:
            subprocess.run(['ffmpeg', '-version'], check=True)
        except FileNotFoundError:
            install_ffmpeg_linux()



# Llamar a esta función al iniciar el programa para asegurarse de que todas las dependencias estén instaladas
if __name__ == "__main__":
    ensure_all_dependencies()

url_checker.py

Este archivo contiene la lógica principal para la verificación de URLs. Se conecta a la GUI para proporcionar actualizaciones en tiempo real sobre el estado de cada URL verificada. Aquí se define la clase URLChecker, que es una aplicación gráfica (GUI) construida con PyQt5. La aplicación permite a los usuarios verificar si una URL es activa y compatible con flujos de transmisión multimedia (como archivos m3u8). Además, la aplicación puede extraer URLs de listas de reproducción de YouTube y archivos m3u, y abrir estas URLs en VLC Media Player.

from PyQt5.QtWidgets import QWidget, QVBoxLayout, QLineEdit, QPushButton, QLabel, QHBoxLayout, QMenuBar, QAction, QInputDialog, QMessageBox, QProgressDialog, QApplication
from PyQt5.QtCore import Qt, QThread, pyqtSlot, pyqtSignal
import subprocess
import platform
from url_check_worker import URLCheckWorker
from about_dialog import AboutDialog
from url_utilities import getStreamURL, extractYouTubePlaylist, extractM3UUrls

class URLChecker(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()
        self.url_check_thread = QThread()
        self.url_check_worker = URLCheckWorker()
        self.url_check_worker.moveToThread(self.url_check_thread)
        self.url_check_worker.url_checked.connect(self.handleURLChecked)
        self.url_check_thread.start()

    @pyqtSlot(bool)
    def handleURLChecked(self, is_available):
        if is_available:
            self.result_label.setText('URL activa')
            self.result_label.setStyleSheet("font-weight: bold; font-size: 16px; color: green;")
            self.open_vlc_button.setVisible(True)
            self.url_to_open = self.url_input.text().strip()
        else:
            self.result_label.setText('URL no disponible')
            self.result_label.setStyleSheet("font-weight: bold; font-size: 16px; color: red;")
            self.open_vlc_button.setVisible(False)

    def initUI(self):
        self.setWindowTitle('Comp-Rueba-URL')
        self.setGeometry(100, 100, 400, 200)
        self.setFixedSize(400, 200)  # Para que no se pueda redimensionar la ventana
        
        layout = QVBoxLayout()
        menubar = QMenuBar(self)
        
        file_menu = menubar.addMenu('Archivo')
        exit_action = QAction('Salir', self)
        exit_action.setShortcut('Ctrl+Q')
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)
        
        options_menu = menubar.addMenu('Opciones')
        
        about_action = QAction('About', self)
        about_action.triggered.connect(self.showAboutDialog)
        options_menu.addAction(about_action)
        
        playlist_action = QAction('Extraer URLs de lista en YouTube', self)
        playlist_action.triggered.connect(self.extractPlaylist)
        options_menu.addAction(playlist_action)
        
        m3u_action = QAction('Extraer URLs de un archivo .m3u', self)
        m3u_action.triggered.connect(self.extractM3U)
        options_menu.addAction(m3u_action)
        
        layout.setMenuBar(menubar)
        
        self.url_input = QLineEdit()
        layout.addWidget(self.url_input)
        
        button_layout = QHBoxLayout()
        self.check_button = QPushButton('Comprobar URL')
        self.check_button.setToolTip('Comprueba si la URL proporcionada está activa y es un archivo m3u8 o un flujo de transmisión multimedia')
        self.check_button.clicked.connect(self.checkURL)
        button_layout.addWidget(self.check_button)
        
        self.clear_button = QPushButton('Borrar URL')
        self.clear_button.setToolTip('Borra el contenido del campo de entrada de URL')
        self.clear_button.clicked.connect(self.clearURL)
        button_layout.addWidget(self.clear_button)
        
        layout.addLayout(button_layout)
        
        self.result_label = QLabel()
        self.result_label.setAlignment(Qt.AlignCenter)
        self.result_label.setStyleSheet("font-weight: bold; font-size: 16px; color: blue;")
        layout.addWidget(self.result_label)
        
        self.open_vlc_button = QPushButton('Abrir en VLC')
        self.open_vlc_button.setToolTip('Abre la URL en VLC Media Player')
        self.open_vlc_button.clicked.connect(self.openInVLC)
        self.open_vlc_button.setVisible(False)
        layout.addWidget(self.open_vlc_button)
        
        self.setLayout(layout)

    def checkURL(self):
        url = self.url_input.text().strip()
        self.url_input.setText(url)
        self.result_label.setText('Verificando URL...')
        self.result_label.setStyleSheet("font-weight: bold; font-size: 16px; color: blue;")
        QApplication.processEvents()  # Fuerza la actualización de la interfaz de usuario

        # Emite la señal para verificar la URL en el hilo del trabajador
        self.url_check_worker.check_url_signal.emit(url)

    def clearURL(self):
        self.url_input.clear()
        self.result_label.clear()
        self.open_vlc_button.setVisible(False)

    def isVLCInstalled(self):
        try:
            if platform.system() == 'Windows':
                subprocess.run(['vlc', '--version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
            elif platform.system() == 'Darwin':
                subprocess.run(['/Applications/VLC.app/Contents/MacOS/VLC', '--version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
            elif platform.system() == 'Linux':
                subprocess.run(['vlc', '--version'], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, check=True)
            return True
        except subprocess.CalledProcessError:
            return False
        except FileNotFoundError:
            return False

    def openInVLC(self):
        url = self.url_to_open
        stream_url = getStreamURL(url)
        if stream_url is None:
            QMessageBox.critical(self, "Error", "No se pudo obtener la URL de streaming.")
            return
        if not self.isVLCInstalled():
            QMessageBox.critical(self, "Error", "VLC no está instalado.")
            return
        try:
            if platform.system() == 'Windows':
                subprocess.Popen(['vlc', stream_url])
            elif platform.system() == 'Darwin':
                subprocess.Popen(['/Applications/VLC.app/Contents/MacOS/VLC', stream_url])
            elif platform.system() == 'Linux':
                subprocess.Popen(['vlc', stream_url])
        except Exception as e:
            QMessageBox.critical(self, "Error", f"Fallo al abrir VLC: {str(e)}")

    def showAboutDialog(self):
        about_dialog = AboutDialog()
        about_dialog.exec_()

    def extractPlaylist(self):
        playlist_url, ok = QInputDialog.getText(self, 'Extraer URLs de lista de YouTube', 'Ingrese la URL de la lista de YouTube:')
        if ok and playlist_url:
            progress_dialog = QProgressDialog("Extrayendo URLs...", "Cancelar", 0, 100, self)
            progress_dialog.setWindowTitle('Progreso de extracción')
            progress_dialog.setWindowModality(Qt.WindowModal)
            progress_dialog.setMinimumDuration(0)

            self.result_label.setText('Preparando las URLs a reproducir...')
            self.result_label.setStyleSheet("font-weight: bold; font-size: 16px; color: blue;")
            success = extractYouTubePlaylist(playlist_url, progress_dialog)
            if success:
                self.result_label.setText('URLs extraídas y guardadas en listas.txt')
                self.result_label.setStyleSheet("font-weight: bold; font-size: 16px; color: green;")
                QMessageBox.information(self, 'Éxito', 'URLs extraídas y guardadas en el archivo listas.txt')
                self.openPlaylistInVLC()
            else:
                self.result_label.setText('Sin URL en la lista de reproducción')
                self.result_label.setStyleSheet("font-weight: bold; font-size: 16px; color: red;")
                QMessageBox.critical(self, 'Error', 'No se pudieron extraer las URLs de la lista de reproducción o no se encontraron entradas.')

    def extractM3U(self):
        m3u_url, ok = QInputDialog.getText(self, 'Extraer URLs de archivo .m3u', 'Escribe la URL del archivo .m3u:')
        if ok and m3u_url:
            progress_dialog = QProgressDialog("Extrayendo URLs...", "Cancelar", 0, 100, self)
            progress_dialog.setWindowTitle('Progreso de extracción')
            progress_dialog.setWindowModality(Qt.WindowModal)
            progress_dialog.setMinimumDuration(0)

            self.result_label.setText('Preparando las URLs a reproducir...')
            self.result_label.setStyleSheet("font-weight: bold; font-size: 16px; color: blue;")
            success = extractM3UUrls(m3u_url, progress_dialog)
            if success:
                self.result_label.setText('URLs extraídas y guardadas en listas.txt')
                self.result_label.setStyleSheet("font-weight: bold; font-size: 16px; color: green;")
                QMessageBox.information(self, 'Éxito', 'URLs extraídas y guardadas en listas.txt')
                self.openPlaylistInVLC()
            else:
                self.result_label.setText('No se encontraron URLs válidas en el archivo .m3u')
                self.result_label.setStyleSheet("font-weight: bold; font-size: 16px; color: red;")
                QMessageBox.critical(self, 'Error', 'No se pudieron extraer URLs válidas del archivo .m3u.')

    def openPlaylistInVLC(self):
        try:
            with open('listas.txt', 'r') as file:
                urls = file.readlines()
            
            if not urls:
                QMessageBox.critical(self, "Error", "El archivo listas.txt está vacío o no contiene URLs válidas.")
                return

            urls = [url.strip() for url in urls if url.strip()]

            if not self.isVLCInstalled():
                QMessageBox.critical(self, "Error", "VLC no está instalado.")
                return

            vlc_command = ['vlc', '--playlist-enqueue'] + urls
            subprocess.Popen(vlc_command)
            
        except FileNotFoundError:
            QMessageBox.critical(self, "Error", "El archivo listas.txt no fue encontrado.")
        except Exception as e:
            QMessageBox.critical(self, "Error", f"Fallo al abrir VLC: {str(e)}")

url_checker_worker.py

Este código define la clase URLCheckWorker, que es una clase «trabajadora» en PyQt5. Su propósito es verificar si una URL es válida y accesible. La verificación se realiza en un hilo separado del hilo principal de la GUI para mantener la interfaz de usuario funcionando, sin bloquearse (aun que esto todavía es mejorable). La clase soporta la verificación tanto de URLs de YouTube como de URLs generales.

from PyQt5.QtCore import QObject, pyqtSignal
import subprocess
import platform
import tempfile
import yt_dlp
import time

class URLCheckWorker(QObject):
    url_checked = pyqtSignal(bool)
    check_url_signal = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.check_url_signal.connect(self.checkURL)

    def checkURL(self, url, retries=3, delay=5):
        success = False
        for attempt in range(retries):
            if "youtube.com" in url or "youtu.be" in url:
                success = self.checkYouTubeURL(url)
            else:
                success = self.checkGeneralURL(url)
            
            if success:
                self.url_checked.emit(True)
                return
            
            print(f"Intento {attempt + 1} fallido, reintentando en {delay} segundos...")
            time.sleep(delay)
        
        self.url_checked.emit(False)

    def checkYouTubeURL(self, url):
        try:
            ydl_opts = {
                'quiet': True,
                'no_warnings': True,
                'format': 'best',
            }
            with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                info_dict = ydl.extract_info(url, download=False)
                video_url = info_dict.get("url", None)
                if video_url:
                    return True
                else:
                    return False
        except Exception as e:
            print(f"Error al verificar la URL de YouTube: {e}")
            return False

    def checkGeneralURL(self, url):
        try:
            with tempfile.NamedTemporaryFile(delete=False) as temp_file:
                log_file = temp_file.name

            if platform.system() == 'Windows':
                vlc_command = ['vlc', '--intf', 'dummy', '--no-video-title-show', '--no-audio', '--no-video', '--play-and-exit', '--run-time=1', url, '>', log_file, '2>&1']
            elif platform.system() == 'Darwin':
                vlc_command = ['/Applications/VLC.app/Contents/MacOS/VLC', '--intf', 'dummy', '--no-video-title-show', '--no-audio', '--no-video', '--play-and-exit', '--run-time=1', url, '>', log_file, '2>&1']
            elif platform.system() == 'Linux':
                vlc_command = ['cvlc', '--no-video-title-show', '--no-audio', '--no-video', '--play-and-exit', '--run-time=1', url, '>', log_file, '2>&1']

            result = subprocess.run(" ".join(vlc_command), shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
            
            with open(log_file, 'r') as file:
                output = file.read()

            print(output)

            error_messages = [
                "access error", "Your input can't be opened",
                "unable to open", "Could not connect", "Failed to open",
                "Network is unreachable", "Connection refused", "http stream error",
                "access stream error", "main stream error"
            ]

            is_valid = not any(error_message in output for error_message in error_messages)
            return is_valid
        except Exception as e:
            print(f"Error al verificar la URL con VLC: {e}")
            return False

url_utilities.py

Este código contiene varias funciones relacionadas con la verificación y extracción de URLs de transmisión (streaming). Utiliza la biblioteca requests para realizar solicitudes HTTP y yt_dlp para manejar URLs de YouTube. También incluye la integración con QProgressDialog de PyQt5 para mostrar el progreso de las operaciones largas.

import requests
import yt_dlp
from PyQt5.QtWidgets import QProgressDialog

# Función para verificar si una URL está disponible
def isURLAvailable(url):
    try:
        response = requests.get(url, timeout=10)
        return response.status_code == 200
    except Exception as e:
        print(f"Error al conectar a la URL: {str(e)}")
        return False

# Función para obtener la URL de streaming de una URL dada
def getStreamURL(url, retries=3, timeout=30):
    attempt = 0
    while attempt < retries:
        try:
            ydl_opts = {
                'quiet': True,
                'no_warnings': True,
                'format': 'best',
                'socket_timeout': timeout,
            }
            with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                info = ydl.extract_info(url, download=False)
                return info['url'] if 'url' in info else info['entries'][0]['url']
        except yt_dlp.utils.DownloadError as e:
            print(f"Error al obtener la URL de streaming (Intento {attempt + 1}/{retries}): {str(e)}")
            if attempt == retries - 1:
                return None
            attempt += 1

# Función para extraer las URLs de una lista de reproducción de YouTube
def extractYouTubePlaylist(playlist_url, progress_dialog=None):
    try:
        ydl_opts = {
            'quiet': True,
            'no_warnings': True,
            'extract_flat': True,  # Solo obtiene metadatos, no el contenido multimedia
            'force_generic_extractor': True,
        }
        retries = 3
        for attempt in range(retries):
            try:
                with yt_dlp.YoutubeDL(ydl_opts) as ydl:
                    info = ydl.extract_info(playlist_url, download=False)
                    if 'entries' in info:
                        urls = []
                        total_entries = len(info['entries'])
                        for idx, entry in enumerate(info['entries']):
                            if progress_dialog:
                                progress_dialog.setValue(int((idx / total_entries) * 100))
                                if progress_dialog.wasCanceled():
                                    return False
                            video_url = f"https://www.youtube.com/watch?v={entry['id']}"
                            stream_url = getStreamURL(video_url)
                            if stream_url:
                                urls.append(stream_url)
                        if urls:
                            with open('listas.txt', 'w') as f:
                                f.write('\n'.join(urls))
                            if progress_dialog:
                                progress_dialog.setValue(100)
                            return True
                        else:
                            print("No se pudieron obtener las URLs de transmisión.")
                            if progress_dialog:
                                progress_dialog.setValue(100)
                            return False
                    else:
                        print(f"No se encontraron entradas en la lista de reproducción en el intento {attempt + 1}.")
                        if attempt < retries - 1:
                            print("Reintentando...")
                            continue
                        if progress_dialog:
                            progress_dialog.setValue(100)
                        return False
            except Exception as e:
                print(f"Error al extraer la lista de reproducción de YouTube en el intento {attempt + 1}: {str(e)}")
                if attempt < retries - 1:
                    print("Reintentando...")
                    continue
                if progress_dialog:
                    progress_dialog.setValue(100)
                return False
    except Exception as e:
        print(f"Error inesperado al extraer la lista de reproducción de YouTube: {str(e)}")
        if progress_dialog:
            progress_dialog.setValue(100)
        return False

# Función para extraer URLs de un archivo M3U
def extractM3UUrls(m3u_url, progress_dialog=None):
    try:
        response = requests.get(m3u_url)
        response.raise_for_status()
        urls = [line.strip() for line in response.text.splitlines() if line.strip() and not line.startswith("#")]
        
        valid_urls = []
        total_urls = len(urls)
        for idx, url in enumerate(urls):
            if progress_dialog:
                progress_dialog.setValue(int((idx / total_urls) * 100))
                if progress_dialog.wasCanceled():
                    return False
            if isURLAvailable(url):
                valid_urls.append(url)
        
        if valid_urls:
            with open('listas.txt', 'w') as f:
                f.write('\n'.join(valid_urls))
            if progress_dialog:
                progress_dialog.setValue(100)
            return True
        else:
            print("No se pudieron obtener URLs válidas.")
            if progress_dialog:
                progress_dialog.setValue(100)
            return False
    except Exception as e:
        print(f"Error al extraer URLs de {m3u_url}: {str(e)}")
        if progress_dialog:
            progress_dialog.setValue(100)
        return False

about_dialog.py

Este módulo que define la ventana «Acerca de», proporcionando información sobre la versión del software y otros detalles relevantes.

from PyQt5.QtWidgets import QDialog, QVBoxLayout, QLabel, QDialogButtonBox
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap
import os

class AboutDialog(QDialog):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        super().__init__()
        self.setWindowTitle('About')
        self.setFixedSize(400, 200)
        layout = QVBoxLayout()

        # Ruta del directorio actual del script
        current_dir = os.path.dirname(os.path.abspath(__file__))
        logo_path = os.path.join(current_dir, 'logo.png')

        image_label = QLabel()
        pixmap = QPixmap(logo_path)  # La ruta completa del logo
        image_label.setPixmap(pixmap)
        image_label.setAlignment(Qt.AlignCenter)
        layout.addWidget(image_label)

        label = QLabel("Comp-Rueba-URL.\nVersion 0.5\nDistribuido sin garantías de ningún tipo.")
        label.setAlignment(Qt.AlignCenter)
        layout.addWidget(label)

        button_box = QDialogButtonBox(QDialogButtonBox.Ok)
        button_box.accepted.connect(self.accept)
        layout.addWidget(button_box)

        self.setLayout(layout)

Ejecución o instalación del programa

reproduciendo m3u8 en VLC

Instalar y utilizar Comp-Rueba-URL es sencillo. Tengo que aclarar que este programa solo lo he probado en Ubuntu 22.04, por lo que la instalación de dependencias en otros sistemas, solo lo he picado, pero no depurado, por lo que lo más probable es que falle. Antes de proceder con la instalación y ejecución del programa es necesario tener instalado Python 3.10 en nuestro sistema. Una vez solucionado este problema, tan solo será necesario seguir los siguientes pasos:

Ejecución del programa (sin instalación)

  1. Se puede empezar por clonar el repositorio de Github en el que he alojado el proyecto. Si optas por esta opción, será necesario tener git instalado en el sistema. Una vez que tengas git disponible, basta con escribir en la terminal (Ctrl+Alt+T):
clonar repo comp-rueba-url
https://github.com/sapoclay/comp-rueba-url.git && cd comp-rueba-url

Una vez que estemos en el directorio en el que acabamos de colocar el proyecto, ya podemos ejecutarlo escribiendo en la misma terminal:

ejecución desde la terminal
python3 index.py

Al ejecutar el script, este se encargará de comprobar e instalar todas las dependencias necesarias para que esto funcione. Una vez que todas las dependencias estén instaladas, el programa lanzará la interfaz gráfica, como se puede ver en la anterior captura de pantalla.

Instalación del paquete .DEB

Como he dicho, este programa lo he desarrollado principalmente para que funcione en Ubuntu. Por eso he creado un paquete .deb que podemos instalar de forma rápida, sencilla y limpia

El proceso es sencillo. Para empezar tendremos que descargar el paquete .DEB que he publicado en GitHub. Por el momento he publicado el paquete como versión 0.5, por lo que la URL que voy a utilizar para este ejemplo, quizás no funcione si actualizo el programa. Pero bueno, por el momento, para descargar el paquete .deb, basta con abrir una terminal (Ctrl+Alt+T) y ejecutar:

descargar paquete .deb
wget https://github.com/sapoclay/comp-rueba-url/releases/download/0.5/Comp-Rueba-URL.deb

Tras la descarga, ya podemos instalar el programa ejecutando en la misma terminal:

sudo dpkg -i Comp-Rueba-URL.deb

Al finalizar la instalación, ya podemos buscar el lanzador en nuestro equipo.

lanzador comp-rueba-urb

Desinstalar

Para eliminar esta aplicación, no hay más que abrir una terminal y ejecutar:

sudo apt remove comp-rueba-url

Esto va a dejar el archivo de log y alguna otra cosa en nuestro equipo, pero para terminar de eliminar el programa del equipo, basta con escribir este comando en la misma terminal:

sudo rm -rf /usr/share/Comp-Rueba-URL

Funcionamiento de la Aplicación

ventana about comp-rueba-url

Al iniciar Comp-Rueba-URL, el usuario será recibido con una ventana principal que permite pegar todas las URLs a verificar (vídeos de Youtube o URL m3u8). Además, dentro del menú Opciones, encontraremos disponible la posibilidad de cargar listas m3u o listas de Youtube. Tan solo hay que pegar la URL en el campo indicado. La aplicación recorrerá cada URL, realizando solicitudes HTTP para determinar su accesibilidad y mostrando los resultados en la interfaz, las cuales pasará a VLC para reproducirlos.

Como comentaba líneas más arriba, esto surgió por un motivo bastante concreo, pero creo que al final el programa puede ahorrar algo de tiempo al automatizar la verificación de URLs, evitando la necesidad de comprobarlas manualmente una por una. Además, la cosa ha quedado tan sencilla, que su interfaz gráfica facilita el uso de la herramienta, sin requerir conocimientos técnicos avanzados de ningún tipo. Evidentemente, esto todavía se puede pulir mucho, pero en principio hace lo que tiene que hacer.

Comp-Rueba-URL es una herramienta gratuita para todo el que quiera utilizarla o desarrollarla por su cuenta. Su diseño modular y su interfaz gráfica básica la hacen funcional para cualquier tipo de usuario. Evidentemente esto solo nace con la intención de programar algo con Python, nada más.

Antes de terminar este pequeño artículo, quiero aclarar que al código aquí mostrado le falta el archivo requirements.txt, el cual se puede consultar y descargar desde el repositorio de GitHub en el que está alojado este proyecto.

También te puede interesar ...

Deja un comentario

* Al utilizar este formulario, aceptas que este sitio web almacene y maneje tus datos.

Adblock Detectado!!

Ayúdanos deshabilitando la extensión AdBlocker de tu navegador para visitar esta web.
Si no sabes hacerlo en Chrome, consulta el siguiente enlace. Si utilizas Firefox, puedes consultar este otro enlace.
Esto mejorará tu experiencia en este sitio web.