Universal Downloader v3 – Preuzimajte video zapise sa Interneta lako i brzo

Glavne kategorije podržanih sajtova:

  1. Video platforme:
    • YouTube, Vimeo, Dailymotion, Twitch, Bilibili
    • TikTok, Instagram, Facebook, Twitter/X
    • Youku, Coub, RuTube, VK
  2. Streaming servisi / TV kanali:
    • Netflix, HBO (sa lokalnim pretplatničkim pristupom, ograničeno)
    • CBC, BBC iPlayer, PBS, NBC, ABC
    • ARD, ZDF, Rai, Mediaset
  3. Muzika i audio:
    • SoundCloud, Bandcamp, Mixcloud, Audiomack
    • Jamendo, Archive.org audio, Spotify (ne direktno streaming, ali metapodaci + linkovi)
  4. Edukacija / kursevi:
    • Coursera, edX, Khan Academy
    • Neko je omogućio i preuzimanje sa Udemy (za lične kurseve)
  5. Dokumenta / prezentacije / fajlovi:
    • Slideshare, Archive.org, Internet Archive, Scribd
    • Podcast feedovi (RSS)
  6. Ostali mediji:
    • Reddit (video, gif, slike), Imgur
    • Gfycat, Streamable, Pornhub, Xvideos

U savremenom digitalnom svetu video sadržaj je svuda – od YouTube-a, TikToka i Facebook-a, do Vimeo-a i drugih platformi. Međutim, preuzimanje i organizovanje ovih video zapisa može biti zamorno, pogotovo ako radite sa većim brojem fajlova. Upravo zbog toga predstavljamo Universal Downloader v3, moćan i jednostavan PyQt6 alat za preuzimanje video i audio sadržaja sa gotovo svih popularnih sajtova.


🔹 Glavne funkcionalnosti

  1. Batch preuzimanje URL-ova
    • Dodajte listu linkova ili batch fajl i preuzmite više video zapisa odjednom.
  2. Custom file name template
    • Direktno u GUI birajte kako će se fajlovi imenovati:
      • Po naslovu videa
      • Po uploader-u i datumu
      • Po ID-u videa
      • Kombinacije svih ovih elemenata
    • Osigurajte da su vaši fajlovi organizovani i jedinstveni, bez konflikta imena.
  3. Podrška za više formata
    • Video: MP4, WebM, najbolja dostupna rezolucija (best)
    • Audio: MP3, Bestaudio
  4. Kontrola preuzimanja u realnom vremenu
    • Pauza, nastavak i stop dugmad
    • Napredak preuzimanja prikazan putem progress bar-a
    • Log u realnom vremenu sa detaljima preuzimanja
  5. Jednostavno biranje lokacije
    • Preuzete fajlove možete sačuvati u folder po izboru
  6. Napredni QThread sistem
    • Stabilno preuzimanje, bez zamrzavanja GUI-ja
    • Svaki video se preuzima u posebnom thread-u, što omogućava fluidan rad programa

🔹 Tehničke informacije

  • GUI: PyQt6
  • Preuzimanje video/audio: yt-dlp
  • Video konverzija / obrada: FFmpeg (neophodan za mnoge formate)

Instalacija zavisnosti:

python3 -m pip install pyqt6 yt-dlp
sudo apt install ffmpeg

🔹 Kako koristiti Universal Downloader v3

  1. Dodavanje URL-ova
    • Nalepite URL u polje i kliknite “Dodaj URL”, ili učitajte batch fajl sa više linkova.
  2. Izbor formata
    • Odaberite željeni format iz dropdown menija: video ili audio.
  3. Podešavanje imena fajla
    • Izaberite template iz dropdown menija ili koristite unapred definisane kombinacije.
  4. Start preuzimanja
    • Kliknite “Preuzmi sve” i pratite napredak kroz progress bar i log.
    • Pauzirajte ili zaustavite preuzimanje kada je potrebno.
  5. Organizacija fajlova
    • Sve fajlove možete snimiti u željeni folder i sa odabranim imenima, što olakšava arhiviranje i kasniju upotrebu.

🔹 Prednosti

  • Jednostavan za korišćenje – GUI dizajniran za brz rad i preglednost
  • Fleksibilno imenovanje fajlova – idealno za arhiviranje i batch obradu
  • Podrška za veliki broj platformi – YouTube, TikTok, Facebook, Vimeo i mnoge druge
  • Stabilno i brzo – koristi QThread kako bi GUI ostao responzivan tokom preuzimanja

Universal Downloader v3 predstavlja praktično rešenje za sve koji žele da brzo, lako i organizovano preuzmu video i audio sadržaj sa Interneta, bez komplikacija i dugog čekanja.


Programski kod xuvd.py

#xuvd.py
#instalacija potrebnih zavisnosti: 
#python3 -m pip install pyqt6 yt-dlp
#sudo apt install ffmpeg

import sys
import os
import subprocess
import datetime
from PyQt6.QtCore import Qt, QThread, pyqtSignal
from PyQt6.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout, QPushButton,
    QLineEdit, QListWidget, QFileDialog, QTextEdit, QLabel, QMessageBox,
    QProgressBar, QComboBox
)

class DownloadThread(QThread):
    log_signal = pyqtSignal(str)
    progress_signal = pyqtSignal(int)
    finished = pyqtSignal()

    def __init__(self, url, output_dir, format_code="best", output_template="%(title)s.%(ext)s"):
        super().__init__()
        self.url = url
        self.output_dir = output_dir
        self.format_code = format_code
        self.output_template = output_template
        self._stop = False

    def run(self):
        try:
            output_file = os.path.join(self.output_dir, self.output_template)
            cmd = [
                "yt-dlp",
                "-f", self.format_code,
                "-o", output_file,
                "--no-warnings",
                "--newline",
                self.url
            ]

            process = subprocess.Popen(
                cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True
            )

            for line in process.stdout:
                if self._stop:
                    process.terminate()
                    self.log_signal.emit("⏹ Preuzimanje zaustavljeno!")
                    break
                self.log_signal.emit(line.strip())
                if "%" in line:
                    try:
                        percent = int(line.split("%")[0].split()[-1])
                        self.progress_signal.emit(percent)
                    except:
                        pass

            process.wait()
        except Exception as e:
            self.log_signal.emit(f"❌ Greška: {e}")
        finally:
            self.finished.emit()

    def stop(self):
        self._stop = True

class UniversalDownloader(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("🌐 Universal Downloader v3 - Custom File Names")
        self.setGeometry(200, 200, 850, 650)

        self.urls = []
        self.current_index = 0
        self.thread = None
        self.output_dir = os.getcwd()
        self.current_format = "best"
        self.output_template = "%(title)s.%(ext)s"

        layout = QVBoxLayout()

        # URL unos
        url_layout = QHBoxLayout()
        self.url_input = QLineEdit()
        self.url_input.setPlaceholderText("Unesi ili nalepi URL...")
        self.add_btn = QPushButton("Dodaj URL")
        self.add_btn.clicked.connect(self.add_url)
        url_layout.addWidget(self.url_input)
        url_layout.addWidget(self.add_btn)
        layout.addLayout(url_layout)

        # Lista URL-ova
        self.list_widget = QListWidget()
        layout.addWidget(self.list_widget)

        # Dugmad batch i preuzimanje
        btn_layout = QHBoxLayout()
        self.load_batch_btn = QPushButton("📂 Učitaj batch fajl")
        self.load_batch_btn.clicked.connect(self.load_batch)
        self.start_btn = QPushButton("▶️ Preuzmi sve")
        self.start_btn.clicked.connect(self.start_batch_download)
        self.pause_btn = QPushButton("⏸ Pauza")
        self.pause_btn.clicked.connect(self.pause_download)
        self.stop_btn = QPushButton("⏹ Stop")
        self.stop_btn.clicked.connect(self.stop_download)
        self.clear_btn = QPushButton("🧹 Očisti listu")
        self.clear_btn.clicked.connect(self.clear_list)
        btn_layout.addWidget(self.load_batch_btn)
        btn_layout.addWidget(self.start_btn)
        btn_layout.addWidget(self.pause_btn)
        btn_layout.addWidget(self.stop_btn)
        btn_layout.addWidget(self.clear_btn)
        layout.addLayout(btn_layout)

        # Folder za snimanje
        folder_layout = QHBoxLayout()
        self.folder_label = QLabel(f"📁 Lokacija: {self.output_dir}")
        self.choose_folder_btn = QPushButton("Promeni...")
        self.choose_folder_btn.clicked.connect(self.choose_folder)
        folder_layout.addWidget(self.folder_label)
        folder_layout.addWidget(self.choose_folder_btn)
        layout.addLayout(folder_layout)

        # Format dropdown
        format_layout = QHBoxLayout()
        self.format_label = QLabel("Izaberi format:")
        self.format_combo = QComboBox()
        self.format_combo.addItems(["best", "mp4", "webm", "bestaudio", "mp3"])
        self.format_combo.currentTextChanged.connect(self.change_format)
        format_layout.addWidget(self.format_label)
        format_layout.addWidget(self.format_combo)
        layout.addLayout(format_layout)

        # Template dropdown i custom polje
        template_layout = QHBoxLayout()
        self.template_label = QLabel("Izaberi ime fajla:")
        self.template_combo = QComboBox()
        self.template_combo.addItems([
            "%(title)s.%(ext)s",
            "%(uploader)s/%(upload_date)s - %(title)s.%(ext)s",
            "%(id)s.%(ext)s",
            "%(upload_date)s - %(title)s - %(id)s.%(ext)s"
        ])
        self.template_combo.currentTextChanged.connect(self.change_template)
        template_layout.addWidget(self.template_label)
        template_layout.addWidget(self.template_combo)
        layout.addLayout(template_layout)

        # Progress bar
        self.progress_bar = QProgressBar()
        self.progress_bar.setValue(0)
        layout.addWidget(self.progress_bar)

        # Log
        self.log_output = QTextEdit()
        self.log_output.setReadOnly(True)
        layout.addWidget(self.log_output)

        self.setLayout(layout)

    def add_url(self):
        url = self.url_input.text().strip()
        if url:
            self.urls.append(url)
            self.list_widget.addItem(url)
            self.url_input.clear()

    def load_batch(self):
        path, _ = QFileDialog.getOpenFileName(self, "Odaberi batch fajl", "", "Text fajlovi (*.txt)")
        if path:
            with open(path, "r") as f:
                lines = [line.strip() for line in f if line.strip()]
                self.urls.extend(lines)
                self.list_widget.addItems(lines)

    def choose_folder(self):
        folder = QFileDialog.getExistingDirectory(self, "Odaberi folder", self.output_dir)
        if folder:
            self.output_dir = folder
            self.folder_label.setText(f"📁 Lokacija: {folder}")

    def clear_list(self):
        self.urls.clear()
        self.list_widget.clear()

    def change_format(self, text):
        self.current_format = text

    def change_template(self, text):
        self.output_template = text

    def start_batch_download(self):
        if not self.urls:
            QMessageBox.warning(self, "Upozorenje", "Nema URL-ova za preuzimanje!")
            return
        self.current_index = 0
        self.progress_bar.setValue(0)
        self.start_next_download()

    def start_next_download(self):
        if self.current_index >= len(self.urls):
            QMessageBox.information(self, "Završeno", "Svi video zapisi su preuzeti!")
            self.progress_bar.setValue(100)
            return

        url = self.urls[self.current_index]
        self.log_output.append(f"⬇️ Počinje preuzimanje: {url}")

        self.thread = DownloadThread(url, self.output_dir, self.current_format, self.output_template)
        self.thread.log_signal.connect(self.log_output.append)
        self.thread.progress_signal.connect(self.progress_bar.setValue)
        self.thread.finished.connect(self.on_download_finished)
        self.thread.start()

    def pause_download(self):
        if self.thread:
            self.thread.stop()
            self.log_output.append("⏸ Pauza aktivirana. Možete ponovo kliknuti Start za nastavak.")
            self.progress_bar.setValue(0)

    def stop_download(self):
        if self.thread:
            self.thread.stop()
            self.log_output.append("⏹ Preuzimanje zaustavljeno!")
            self.progress_bar.setValue(0)

    def on_download_finished(self):
        self.thread.wait()
        self.thread = None
        self.progress_bar.setValue(0)
        self.current_index += 1
        self.start_next_download()

if __name__ == "__main__":
    app = QApplication(sys.argv)
    win = UniversalDownloader()
    win.show()
    sys.exit(app.exec())

#MIT_License.txt
MIT License

Copyright (c) [2025] [Aleksandar Maričić]

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. 

By Abel

Leave a Reply

Your email address will not be published. Required fields are marked *