Glavne kategorije podržanih sajtova:
- Video platforme:
- YouTube, Vimeo, Dailymotion, Twitch, Bilibili
- TikTok, Instagram, Facebook, Twitter/X
- Youku, Coub, RuTube, VK
- Streaming servisi / TV kanali:
- Netflix, HBO (sa lokalnim pretplatničkim pristupom, ograničeno)
- CBC, BBC iPlayer, PBS, NBC, ABC
- ARD, ZDF, Rai, Mediaset
- Muzika i audio:
- SoundCloud, Bandcamp, Mixcloud, Audiomack
- Jamendo, Archive.org audio, Spotify (ne direktno streaming, ali metapodaci + linkovi)
- Edukacija / kursevi:
- Coursera, edX, Khan Academy
- Neko je omogućio i preuzimanje sa Udemy (za lične kurseve)
- Dokumenta / prezentacije / fajlovi:
- Slideshare, Archive.org, Internet Archive, Scribd
- Podcast feedovi (RSS)
- 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
- Batch preuzimanje URL-ova
- Dodajte listu linkova ili batch fajl i preuzmite više video zapisa odjednom.
- 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.
- Direktno u GUI birajte kako će se fajlovi imenovati:
- Podrška za više formata
- Video: MP4, WebM, najbolja dostupna rezolucija (
best) - Audio: MP3, Bestaudio
- Video: MP4, WebM, najbolja dostupna rezolucija (
- Kontrola preuzimanja u realnom vremenu
- Pauza, nastavak i stop dugmad
- Napredak preuzimanja prikazan putem progress bar-a
- Log u realnom vremenu sa detaljima preuzimanja
- Jednostavno biranje lokacije
- Preuzete fajlove možete sačuvati u folder po izboru
- 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
- Dodavanje URL-ova
- Nalepite URL u polje i kliknite “Dodaj URL”, ili učitajte batch fajl sa više linkova.
- Izbor formata
- Odaberite željeni format iz dropdown menija: video ili audio.
- Podešavanje imena fajla
- Izaberite template iz dropdown menija ili koristite unapred definisane kombinacije.
- Start preuzimanja
- Kliknite “Preuzmi sve” i pratite napredak kroz progress bar i log.
- Pauzirajte ili zaustavite preuzimanje kada je potrebno.
- 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.
