loader image

Professional Media Downloader

What makes us different from other similar websites? Forums Tech Professional Media Downloader

Viewing 1 post (of 1 total)
  • Author
    Posts
  • #8490
    thumbtak
    Moderator

    I was bored and decided to make an app that can download m3u8 files from any website, and save it as mp4. All you need to do is grab the m3u8 link from the website, by copying the link in the video player, or by using F12 to grab the link.

    Save these two files in a folder and run python3 bootstrap.py. This will download all the files into a temp container.

    Note: This application is in beta, and hasn’t been long term tested. Use at your own risk. 

    app.py

    import sys
    import re
    import yt_dlp
    from PySide6.QtCore import QThread, Signal, Qt
    from PySide6.QtWidgets import (QApplication, QMainWindow, QVBoxLayout,
    QLineEdit, QPushButton, QTableWidget,
    QTableWidgetItem, QWidget, QHBoxLayout,
    QHeaderView, QLabel)
    from PySide6.QtGui import QFont
    
    class DownloadWorker(QThread):
    status_update = Signal(int, str)
    
    def __init__(self, row, url, filename):
    super().__init__()
    self.row = row
    self.url = url
    self.filename = filename
    
    def run(self):
    self.ansi_escape = re.compile(r'\x1b\[[0-9;]*m')
    ydl_opts = {
    'progress_hooks': [self.hook],
    'outtmpl': f'./downloads/{self.filename}.%(ext)s',
    'format': 'best',
    'merge_output_format': 'mp4'
    }
    try:
    with yt_dlp.YoutubeDL(ydl_opts) as ydl:
    ydl.download([self.url])
    self.status_update.emit(self.row, "✓ Completed")
    except Exception:
    self.status_update.emit(self.row, "⚠ Error")
    
    def hook(self, d):
    if d['status'] == 'downloading':
    raw_p = d.get('_percent_str', '0%')
    clean_p = self.ansi_escape.sub('', raw_p).strip()
    self.status_update.emit(self.row, f"Downloading: {clean_p}")
    
    class DownloaderApp(QMainWindow):
    def __init__(self):
    super().__init__()
    self.setWindowTitle("Professional Media Downloader")
    self.setMinimumSize(850, 550)
    
    # Pink Theme Stylesheet
    self.setStyleSheet("""
    QMainWindow { background-color: #1e1e1e; }
    QLineEdit {
    padding: 12px; border: 1px solid #333; border-radius: 6px;
    color: #e0e0e0; background: #2d2d2d; font-size: 14px;
    }
    QPushButton {
    padding: 10px 20px; background-color: #e91e63; color: white;
    border-radius: 6px; font-weight: bold; border: none;
    }
    QPushButton:hover { background-color: #c2185b; }
    QTableWidget {
    background-color: #252526; color: #d4d4d4;
    gridline-color: #333; selection-background-color: #880e4f;
    }
    QHeaderView::section { background-color: #333; color: #fff; padding: 8px; border: none; }
    QLabel#HeaderLabel { color: #e91e63; font-weight: bold; }
    """)
    
    self.workers = []
    
    main_widget = QWidget()
    layout = QVBoxLayout()
    layout.setContentsMargins(20, 20, 20, 20)
    
    # Header
    header = QLabel("Add New Download Task")
    header.setObjectName("HeaderLabel")
    layout.addWidget(header)
    
    # Input Area
    input_layout = QHBoxLayout()
    self.url_input = QLineEdit(placeholderText="Paste URL here...")
    self.name_input = QLineEdit(placeholderText="Filename...")
    self.btn = QPushButton("Start Download")
    input_layout.addWidget(self.url_input, stretch=3)
    input_layout.addWidget(self.name_input, stretch=1)
    input_layout.addWidget(self.btn)
    layout.addLayout(input_layout)
    
    # Table
    self.table = QTableWidget(0, 3)
    self.table.setHorizontalHeaderLabels(["URL", "Filename", "Status"])
    self.table.horizontalHeader().setStretchLastSection(True)
    layout.addWidget(self.table)
    
    # Clickable Pink Footer
    footer = QLabel('<a href="https://taksshack.com/members/thumbtak/" style="color: #e91e63; text-decoration: none;">Created by thumbtak @ https://taksshack.com/members/thumbtak/</a>')
    footer.setAlignment(Qt.AlignCenter)
    footer.setOpenExternalLinks(True)
    footer.setStyleSheet("font-size: 11px; margin-top: 10px;")
    layout.addWidget(footer)
    
    main_widget.setLayout(layout)
    self.setCentralWidget(main_widget)
    
    self.btn.clicked.connect(self.add_task)
    
    def add_task(self):
    row = self.table.rowCount()
    self.table.insertRow(row)
    self.table.setItem(row, 0, QTableWidgetItem(self.url_input.text()))
    self.table.setItem(row, 1, QTableWidgetItem(self.name_input.text()))
    self.table.setItem(row, 2, QTableWidgetItem("Initializing..."))
    
    worker = DownloadWorker(row, self.url_input.text(), self.name_input.text())
    worker.status_update.connect(lambda r, s: self.table.setItem(r, 2, QTableWidgetItem(s)))
    
    self.workers.append(worker)
    worker.start()
    
    self.url_input.clear()
    self.name_input.clear()
    
    def closeEvent(self, event):
    for worker in self.workers:
    if worker.isRunning():
    worker.terminate()
    worker.wait()
    event.accept()
    
    if __name__ == "__main__":
    app = QApplication(sys.argv)
    app.setFont(QFont("Segoe UI", 10))
    window = DownloaderApp()
    window.show()
    sys.exit(app.exec())

    bootstrap.py

    import os
    import subprocess
    import venv
    import shutil
    
    VENV_DIR = ".temp_env"
    
    def setup():
    if not os.path.exists(VENV_DIR):
    print("Creating temporary environment...")
    venv.create(VENV_DIR, with_pip=True)
    
    pip_path = os.path.join(VENV_DIR, 'bin', 'pip')
    python_path = os.path.join(VENV_DIR, 'bin', 'python')
    
    print("Installing dependencies...")
    subprocess.check_call([pip_path, "install", "yt-dlp", "PySide6"])
    
    print("Launching application...")
    try:
    subprocess.call([python_path, "app.py"])
    finally:
    print("Cleaning up environment...")
    if os.path.exists(VENV_DIR):
    shutil.rmtree(VENV_DIR)
    
    if __name__ == "__main__":
    setup()
Viewing 1 post (of 1 total)
  • You must be logged in to reply to this topic.
TAKs Shack