Python w Praktyce: Od prostego skryptu do profesjonalnej aplikacji YouTube Downloader (Windows & Linux)

YouTube Downloader 4

Czy zdarzyło Ci się szukać w Google „youtube to mp3”, tylko po to, by trafić na stronę pełną wyskakujących reklam, fałszywych przycisków „Download” i podejrzanych przekierowań? Jako programista i audiofil stwierdziłem: dość.

Mając do dyspozycji Pythona, bibliotekę yt-dlp i framework PyQt6, postanowiłem zbudować własne, desktopowe narzędzie. Bez reklam, bez śledzenia, działające natywnie na Ubuntu i Windowsie.

Początkowo miał to być prosty skrypt, ale apetyt rośnie w miarę jedzenia. Projekt ewoluował w pełnoprawną aplikację z obsługą dwóch języków, tagowaniem plików i profesjonalnym instalatorem. Oto techniczna historia tego projektu.

YouTube Downloader 1

Fundamenty: Dlaczego yt-dlp i PyQt6?

Sercem projektu jest yt-dlp – potężne narzędzie CLI, będące aktywnie rozwijanym forkiem starego youtube-dl. Radzi sobie z większością zabezpieczeń YouTube’a i oferuje świetną jakość, bijąc na głowę serwisy webowe.

Jako „opakowanie” graficzne wybrałem PyQt6. Dlaczego nie prostszy Tkinter?

  1. Wygląd: Styl „Fusion” wygląda nowocześnie i profesjonalnie zarówno na Linuxie, jak i na Windows 11.
  2. Sygnały i Sloty: Mechanizm idealny do komunikacji między logiką a interfejsem.
  3. Wielowątkowość (QThread): To kluczowa sprawa. Bez tego aplikacja „zamrażałaby się” (brak odpowiedzi UI) podczas pobierania dużego pliku.

Architektura: Oddzielamy UI od Logiki

W pierwszej wersji kodu wszystko było w jednym pliku. Szybko jednak podzieliłem projekt na moduły, co ułatwiło późniejszą rozbudowę:

  • downloader_logic.py (Backend): Tu żyją wątki (QThread), które rozmawiają z yt-dlp. Tutaj dzieje się magia pobierania, konwersji FFmpeg i wstrzykiwania metadanych.
  • main_window.py (Frontend): Definicja przycisków, pól tekstowych i pasków postępu. Ten plik nie wie jak pobierać, wie tylko kogo poprosić o pobieranie.
  • main.py: Punkt startowy spinający całość i ładujący zasoby (ikony).

Etap 1: Rozwiązywanie problemów wieku dziecięcego

Wyzwanie: „Piekło Playlist”

Podczas testów natrafiłem na irytujący błąd. Wklejając link do utworu będącego częścią „Mixa YouTube” (parametr &list=RD...), program próbował pobrać… całą playlistę, czyli setki utworów.

Rozwiązanie:

Zaimplementowałem funkcję czyszczącą URL oraz wymusiłem flagę noplaylist w konfiguracji:

def clean_url(url):
    if "&list" in url:
        return url.split("&list")[0] # Odetnij playlistę
    return url

# Konfiguracja yt-dlp
ydl_opts = {
    'noplaylist': True, # Klucz do sukcesu
    # ...
}

Etap 2: Wchodzimy na poziom PRO (v2.0)

Kiedy podstawy działały, brakowało mi funkcji, które odróżniają „skrypt” od „aplikacji”.

1. Globalny Interfejs (i18n)

Pierwsza wersja była „sztywno” polska. Chciałem, aby aplikacja obsługiwała też angielski z przełączaniem w locie. Zamiast skomplikowanego gettext, użyłem słownika Pythonowego:

TRANSLATIONS = {
    "PL": { "title": "Pobieracz YouTube", "btn_download": "POBIERZ", ... },
    "EN": { "title": "YouTube Downloader", "btn_download": "DOWNLOAD", ... }
}

Metoda retranslate_ui() podmienia wszystkie teksty w interfejsie natychmiast po kliknięciu flagi 🇵🇱 lub 🇬🇧.

2. Porządek w Bibliotece: Metadane i Tagi

Pobieranie to jedno, ale bałagan w bibliotece muzycznej to drugie. Pliki z YouTube często mają nazwę Video Title.mp3 i puste pola ID3.

Rozwiązanie: Rozbudowałem UI o pola Artysta, Album i Rok, a dane te przekazuję bezpośrednio do ffmpeg przez yt-dlp:

ff_metadata_args = []
if self.artist:
    ff_metadata_args.extend(['-metadata', f'artist={self.artist}'])
if self.year:
    ff_metadata_args.extend(['-metadata', f'date={self.year}'])

ydl_opts = {
    # ...
    'postprocessor_args': {'ffmpeg': ff_metadata_args}
}

Dzięki temu plik MP3 w telefonie wyświetla się z poprawnym wykonawcą i rokiem wydania.

3. Branding i Linki

Dodałem w stopce klikalny link do mojej strony, używając HTML w QLabel:

self.lbl_footer.setText("Odwiedź nas: <a href='[https://creativeart.club](https://creativeart.club)'>creativeart.club</a>")
self.lbl_footer.setOpenExternalLinks(True)
YouTube Downloader 2

Deployment: Jak to wysłać w świat?

Napisanie kodu to połowa sukcesu. Drugą jest sprawienie, by uruchamiał się jak każda inna aplikacja – kliknięciem w ikonę.

Problem „Znikającej Ikony” w EXE

To klasyczny problem PyInstallera. Plik działa w IDE, bo logo.ico leży obok. Po skompilowaniu do jednego pliku .exe, program nie widzi ikony.

Rozwiązaniem jest funkcja resource_path, która szuka plików w folderze tymczasowym sys._MEIPASS (tam PyInstaller rozpakowuje zasoby podczas uruchamiania):

def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS
    except Exception:
        base_path = os.path.abspath(".")
    return os.path.join(base_path, relative_path)

Automatyzacja Budowania (Windows & Linux)

Stworzyłem inteligentne skrypty, które automatyzują proces.

Specyfika Windows (build_app.bat):

Skrypt pakuje ikonę do środka pliku .exe flagą –add-data.

pyinstaller --noconsole --onefile --icon=logo.ico --add-data "logo.ico;." main.py

Ważne: Na Windowsie użytkownik musi mieć plik ffmpeg.exe w tym samym folderze co aplikacja, aby konwersja do MP3 działała.

Specyfika Linux (build_linux.sh):

Tutaj używamy separatora :. Dodatkowo stworzyłem skrypt create_shortcut.py, który generuje plik .desktop w ~/.local/share/applications/. Dzięki temu program na Ubuntu ma systemową ikonę i można go przypiąć do paska „Ulubione”.

YouTube Downloader 3

Podsumowanie

Budowa własnych narzędzi daje ogromną satysfakcję i pełną kontrolę. Zamiast ryzykować wirusy z sieci, mamy czysty kod w Pythonie, który robi dokładnie to, co chcemy.

Co zyskałem dzięki wersji 2.0?

  1. Wygodę: Mogę pobierać całe albumy, od razu je tagując.
  2. Estetykę: Aplikacja wygląda jak natywny program.
  3. Wiedzę: Nauczyłem się zaawansowanej obsługi FFmpeg i „zamrażania” aplikacji w Pythonie.
Andre Selfie
Andrzej Majewski

Moja fascynacja technologią zaczęła się podczas studiów informatycznych na Uniwersytecie Zielonogórskim. Od czasu przeprowadzki do Wielkiej Brytanii w 2015 roku i osiedlenia się na stałe w Bournemouth, przekułem tę pasję w karierę zawodową poświęconą infrastrukturze o wysokiej wydajności.W głębi duszy jestem entuzjastą Linuxa – to zaangażowanie wykracza poza moją pracę zawodową w SolutionsInc i obejmuje również mój rozbudowany, prywatny homelab. Niezależnie od tego, czy zarządzam złożonymi architekturami serwerowymi przez ISPConfig, buduję systemy VoIP w ramach Phones Rescue, czy tworzę narzędzia do automatyzacji w Pythonie, najlepiej czuję się, podejmując wyzwania związane z projektowaniem wydajnych rozwiązań open-source

Komentarze

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Wymagane pola są oznaczone *