Commit Graph
54 Commits
Author SHA1 Message Date
mmacierzanka b9152c41be Rename schemas to requests
Backend / Lint i formatowanie (pull_request) Failing after 4s
Backend / Testowy build obrazu (pull_request) Canceled after 0s
Backend / Testy i kontrakt API (pull_request) Canceled after 3m28s
2026-08-21 13:33:04 +02:00
mmacierzanka e4776d1348 Rename web to api to reflect, that it's not serving the frontend, split FastAPI creation 2026-08-21 13:31:11 +02:00
mmacierzanka d31747e80b refactor: split music cog by responsibility 2026-08-21 13:16:19 +02:00
mmacierzanka d706317dc8 Move web and bot packages to a consolidated ui package 2026-08-21 13:04:23 +02:00
mmacierzanka e21ccb4bb8 Adjust version and main bot class name 2026-08-21 12:55:39 +02:00
lgooseandClaude Bot fd8d71a80d ci: pipeline backendu dla Gitea Actions
Backend / Lint i formatowanie (pull_request) Successful in 20s
Backend / Testy i kontrakt API (pull_request) Successful in 4m58s
Backend / Testowy build obrazu (pull_request) Successful in 1m34s
Workflow .gitea/workflows/backend.yaml z trzema jobami:

- lint   ruff check oraz ruff format --check --diff
- test   testy jednostkowe i kontrakt API, plus compileall całego pakietu
- image  testowy build obrazu; job czeka na lint i test

Job image nie poprzestaje na zbudowaniu obrazu - uruchamia kontener bez
prawdziwego tokenu Discorda i czeka, aż /api/status odpowie. Panel łączy
się z backendem po sieci, więc sam start procesu niczego nie dowodzi.
Zapytanie leci z wnętrza kontenera, dzięki czemu krok nie zależy od tego,
czy runner ma zainstalowanego curla albo wgeta.

Filtry paths ograniczają uruchomienia do zmian w kodzie Pythona,
zależnościach, konfiguracji lintera, Dockerfile i samym workflow.

Zależności instalują się z gotowych kółek, więc obraz CI nie potrzebuje
kompilatora. ffmpeg i libopus są wymagane dopiero przy odtwarzaniu,
a testy nie dotykają warstwy audio.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:25:40 +02:00
lgooseandClaude Bot 9ae22704ff test: przypięcie powierzchni API
Panel jest osobnym klientem HTTP, więc zmiana ścieżki albo metody psuje go
po cichu - w kodzie Pythona nic nie przestaje się kompilować. Test buduje
aplikację, generuje specyfikację OpenAPI i porównuje ją z listą 22
endpointów w obie strony:

- brak oczekiwanego endpointu to błąd
- nowy endpoint spoza listy również, żeby rozszerzenie API było świadomą
  decyzją, a nie skutkiem ubocznym

Sprawdzany jest też sam dokument OpenAPI, co wyłapuje błędy w deklaracjach
tras i modelach żądań.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:25:40 +02:00
lgooseandClaude Bot 2a961e1a8a chore: konfiguracja ruff i poprawki wskazane przez lintera
Ruff jako linter i formatter, konfiguracja w pyproject.toml:

- reguły E, W, F, I, N, UP, B, C4, SIM, TID, BLE, RUF
- extend-immutable-calls dla fastapi.Depends, File i Query - wstrzykiwanie
  zależności opiera się na wywołaniach w wartościach domyślnych argumentów,
  więc bugbear B008 zgłaszał tam 66 fałszywych trafień
- zakaz importów względnych, aby ścieżki w pakiecie czytało się jednoznacznie
- katalog frontend wyłączony z analizy

Poprawki, które wskazał linter:

- Awaitable, Callable i AsyncIterator importowane z collections.abc zamiast
  z typing
- zbędny argument trybu w open(path, "r")
- contextlib.suppress zamiast try/except/pass przy anulowaniu zadania
- zdjęte dwie niepotrzebne dyrektywy noqa
- pięć linii przekraczających 100 znaków zawiniętych

Całość przepuszczona przez ruff format. Do .gitignore dochodzi .ruff_cache/.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:25:40 +02:00
lgooseandClaude Bot ccfb0d2e34 docs: README z opisem struktury pakietu
Opisane uruchamianie, zmienne środowiskowe, testy oraz mapa pakietu
z klasą przypisaną do pliku. Spisane też trzy zasady, które trzymają
strukturę w ryzach: MusicPlayer jest jedynym właścicielem stanu, kolejka
jest nieusuwalna i śledzona indeksem, a zależności idą przez konstruktory
zamiast globalnych singletonów.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:11:37 +02:00
lgooseandClaude Bot 0879120c85 test: regresja arytmetyki kolejki wskaźnikowej
Kolejka jest nieusuwalna, a pozycję śledzi indeks, więc przeskoki,
usuwanie i zmiana kolejności muszą ten indeks poprawnie przesuwać. To
najbardziej podatna na błędy część przeniesionego kodu, więc dostaje
testy przypinające zachowanie:

- start odtwarzania przy dodaniu do pustej kolejki
- limit odtworzeń usuwający utwór i cofający indeks
- przechodzenie dalej oraz oba tryby zapętlania
- koniec kolejki czyszczący aktualny utwór
- przeskok, usunięcie utworu przed indeksem i usunięcie odtwarzanego
- zmiana kolejności zachowująca indeks na odtwarzanym utworze
- stosowanie głośności serwera i zatrzymanie przy niedziałających utworach

16 przypadków na atrapach Discorda, bez sieci i ffmpega. Uruchamiane
przez python -m unittest discover -s tests -t . - bez nowych zależności.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:11:37 +02:00
lgooseandClaude Bot c8a188647b fix(music): zatrzymanie pomijania, gdy nie da się odtworzyć żadnego utworu
Utwór, dla którego nie udało się zbudować świeżego źródła, był pomijany
przez rekurencyjne wywołanie play_next(advance=True). Przy włączonym
zapętlaniu kolejki - a przy zapętlaniu jednego utworu tym bardziej -
kolejny indeks wskazywał znowu na utwór niedziałający, więc pomijanie
kręciło się w kółko, zasypując kanał powiadomieniami.

Licznik nieudanych prób wędruje teraz przez play_next. Po liczbie
niepowodzeń równej długości kolejki odtwarzanie zatrzymuje się z jednym
komunikatem zamiast pętli.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:11:22 +02:00
lgooseandClaude Bot ab1d0d4a91 fix(music): stosowanie głośności serwera do nowo uruchamianych utworów
YTDLSource powstaje z domyślną głośnością 0.5, a ustawienie głośności
zmieniało tylko źródło aktualnie odtwarzane. Nowy utwór startował więc
na 50%, podczas gdy panel i /api/status raportowały stan serwera - domyślnie
100%. Zmiana głośności "znikała" po przejściu do kolejnego utworu.

Teraz play_next ustawia na źródle głośność ze stanu serwera, zanim
przekaże je do odtworzenia.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:11:10 +02:00
lgooseandClaude Bot 526e9ab644 refactor: korzeń kompozycji i usunięcie starych modułów
- Application składa wszystkie zależności w jednym miejscu i prowadzi bota
  równolegle z serwerem web. Panel przeżywa awarię logowania do Discorda,
  tak jak dotąd, ale komunikat idzie przez logging zamiast print.
- zyrarbot/__main__.py pozwala uruchomić pakiet przez python -m zyrarbot
  i konfiguruje logowanie; main.py zostaje cienkim punktem wejścia.
- Usunięte bot.py, web.py i ytdl.py - ich zawartość rozeszła się po
  pakiecie. Zniknął przy tym globalny obiekt bota tworzony przy imporcie
  modułu, który wymuszał kolejność importów między bot.py a web.py.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:59 +02:00
lgooseandClaude Bot 3fb1e26fb4 refactor(web): routery tematyczne i wstrzykiwanie zależności
22 endpointy z jednego pliku rozdzielone na routery według obszaru:
status, playback, queue, voice, uploads, settings i websocket.

- Powtarzany w każdym endpoincie blok "pobierz cog, sprawdź czy istnieje,
  ustal aktywny serwer, w razie czego rzuć 400" zastąpiony zależnościami
  z dependencies.py. Zniknęło 22-krotne bot.get_cog("MusicBot").
- Zależności wędrują przez app.state zamiast importu globalnego obiektu
  bota, więc warstwa web nie ma już zależności cyklicznej z modułem bota.
- create_app składa aplikację i montuje routery.
- Wycofane @app.on_event("startup") zastąpione lifespanem, który dodatkowo
  sprząta po sobie: odpina hook i zatrzymuje pętlę rozgłaszania.

Kontrakt HTTP pozostaje bez zmian - te same ścieżki, metody, kody błędów
i kształt odpowiedzi.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:59 +02:00
lgooseandClaude Bot c2f6d265c1 refactor(web): rozgłaszanie stanu po WebSockecie
- ConnectionManager - rejestr aktywnych połączeń (bez zmian poza typami).
- StatusService - budowanie obrazu stanu bota. Endpoint get_status miał
  126 linii i składał odpowiedź w dwóch wariantach; teraz jest to zestaw
  metod budujących poszczególne fragmenty payloadu.
- StatusBroadcaster - rozgłaszanie na żądanie oraz pętla cykliczna, wraz
  z zatrzymywaniem zadania przy wyłączaniu aplikacji. Wcześniej zadanie
  było tworzone w on_event("startup") i nigdy nie anulowane.

Wariant odpowiedzi dla bota bez serwerów zwraca teraz komplet kluczy
(dochodzi queue_index: -1), więc panel dostaje zawsze ten sam kształt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:42 +02:00
lgooseandClaude Bot 6e61892c04 refactor(web): modele żądań w osobnych plikach
Jedenaście klas pydantic zadeklarowanych jednym blokiem na górze web.py
rozdzielone na pliki zgodnie z zasadą jedna klasa = jeden plik. Każdy
model dostaje docstring mówiący, do czego służy - z samych nazw pól
(np. channel_id akceptujące pusty string jako reset) nie wynikało to
w oczywisty sposób.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:42 +02:00
lgooseandClaude Bot b2651bb957 refactor(bot): klient Discorda i cienki cog komend
- ZyrarBot buduje intencje, rejestruje cog i synchronizuje drzewo komend.
- MusicCog zawiera już wyłącznie obsługę komend ukośnikowych: walidację
  argumentów, komunikaty dla użytkownika i wywołania MusicPlayer. Cała
  praca ze stanem zniknęła z tego pliku.
- GuildResolver ustala kontekst serwera dla panelu webowego, który nie ma
  interakcji Discorda - wcześniej robiła to funkcja get_active_guild
  w web.py, sięgająca po globalny obiekt bota.
- Powtarzana w czterech komendach odpowiedź "dodano do kolejki" albo
  "teraz odtwarzane" zebrana w jednym helperze.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:28 +02:00
lgooseandClaude Bot a143cb21d3 refactor(music): MusicPlayer jako jedyny właściciel stanu odtwarzania
Logika odtwarzania wyprowadzona z coga Discorda do osobnej klasy. Dotąd
kolejkę modyfikowały dwie niezależne ścieżki: metody MusicBot oraz
endpointy w web.py sięgające po music_cog.queues[guild_id] i ustawiające
music_cog.jump_flag wprost. Teraz obie warstwy wołają MusicPlayer.

Przeniesione bez zmiany zachowania: enqueue, play_next wraz z arytmetyką
indeksu i limitem odtworzeń, remove, reorder, jump, sterowanie głośnością
oraz hook on_state_change rozgłaszający stan po WebSockecie.

Wybór kolejnego indeksu wydzielony do _next_index, dzięki czemu reguły
trybów zapętlania i przeskoku dają się czytać bez śledzenia całej metody.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:28 +02:00
lgooseandClaude Bot 4cc00618d1 refactor(music): stan serwera, tryb zapętlania i budowanie powiadomień
- GuildMusicState zbiera stan jednego serwera w jedną strukturę. Dotąd
  było to dziewięć równoległych słowników indeksowanych guild_id
  (queues, currents, volumes, loop_currents, loop_playlists, last_channels,
  history, queue_indices, jump_flag), co wymagało pamiętania, że przy
  każdej zmianie trzeba ruszyć kilka z nich naraz.
- LoopMode zastępuje parę booleanów loop_current/loop_playlist, których
  kombinacja "oba włączone" nie miała sensu, a mimo to była osiągalna.
  Wartości enuma (off/playlist/single) odpowiadają kontraktowi API panelu.
- TrackEmbedFactory zbiera budowanie osadzeń Discorda, powielone dotąd
  w enqueue_track i play_next.
- NotificationChannelResolver opisuje kolejność szukania kanału logów:
  zapamiętany -> z konfiguracji -> pierwszy zapisywalny na serwerze.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:11 +02:00
lgooseandClaude Bot 53ffae1663 refactor(audio): wydzielenie warstwy pozyskiwania dźwięku
ytdl.py rozbity na cztery pliki o rozłącznych rolach:

- YtdlOptions   opcje yt-dlp i FFmpeg, wcześniej moduły-globalne
- MediaProbe    odczyt długości pliku przez ffprobe
- YTDLSource    samo źródło audio - dane utworu i zliczanie odtworzonego
                czasu; dochodzą właściwości is_local, filepath, display_url
                i has_web_link, które zastępują powtarzane w kilku miejscach
                data.get("webpage_url") or url
- TrackFactory  budowanie źródeł z zapytania, adresu lub pliku

Rozdzielenie źródła od sposobu jego tworzenia usuwa globalną instancję
YoutubeDL i pozwala podmienić fabrykę w testach. Metoda recreate zbiera
w jednym miejscu odtwarzanie utworu od nowa z zachowaniem jego tożsamości
i pozostałej liczby odtworzeń.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:10:11 +02:00
lgooseandClaude Bot e59b4ad9f1 refactor(storage): repozytoria konfiguracji, historii i plików
Dostęp do danych trwałych schodzi za trzy repozytoria, zamiast być
rozsmarowany po metodach coga i endpointach:

- GuildConfigRepository - kanały powiadomień per serwer i domyślna liczba
  odtworzeń.
- HistoryRepository - historia per serwer z buforem w pamięci i limitem
  wpisów. Zasada odsiewania duplikatów (zgodny tytuł albo adres) siedzi
  teraz w HistoryEntry.is_same_track_as zamiast w warunku wbudowanym
  w listę składaną.
- UploadsRepository - katalog uploadów. Każda nazwa przechodzi przez
  os.path.basename w jednym miejscu, co zamyka temat ścieżek względnych
  przychodzących z API.

Wpisy historii i pliki opisują struktury HistoryEntry oraz UploadFileInfo
zamiast luźnych słowników z kluczami sprawdzanymi przez .get().

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:09:52 +02:00
lgooseandClaude Bot e194a8611c refactor(config): Settings i JsonFileStore jako podstawa pakietu
Początek pakietu zyrarbot, budowanego wedle zasady jedna klasa = jeden plik.

- Settings zbiera konfigurację ze zmiennych środowiskowych oraz ścieżki
  i limity, które dotąd były literałami rozsianymi po bot.py i web.py
  ("config/config.json", "uploads", zakres 1-999, domyślne 5 odtworzeń).
- JsonFileStore zastępuje sześć niemal identycznych bloków open/json.load
  z ręcznym try/except. Brakujący lub uszkodzony plik daje pusty słownik
  zamiast wyjątku, a zapis sam tworzy katalog.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-21 01:09:52 +02:00
mmacierzanka edf317d21e feat: dodanie mechanizmu limitu odtworzeń (TTL) w kolejce oraz konfiguracji w UI i na Discordzie 2026-07-05 16:03:58 +02:00
mmacierzanka 5c98f29782 feat: ograniczenie widocznych plików do 10 ostatnio używanych, dodanie okna modalnego z całą bazą plików oraz zmiana ikony na plus przy przycisku Dodaj 3.0 2026-07-04 17:05:18 +02:00
mmacierzanka a900097134 feat: przebudowa systemu kolejki od podstaw z użyciem wskaźnika indeksu (nieusuwalna kolejka z możliwością przeskoku i przewijania) 2026-07-04 16:53:20 +02:00
mmacierzanka ab6f16b57d feat: połączenie przycisków zapętlania w jeden przycisk z cyklem Wyłączone -> Kolejka -> Utwór (styl YT Music/Spotify) 2026-07-04 16:41:17 +02:00
mmacierzanka 2a399364d8 feat: zmiana ikony play/pause w dashboardzie zależnie od stanu odtwarzania 2026-07-04 16:38:12 +02:00
mmacierzanka 55c9b7207f fix: wyświetlanie kanałów głosowych ze wszystkich serwerów na liście do połączenia w dashboardzie 2026-07-04 16:37:19 +02:00
mmacierzanka d05615a079 chore: dodanie katalogu uploads do .gitignore 2026-07-04 16:35:57 +02:00
mmacierzanka c0ec3a5f95 style: dodanie limitów szerokości kolumn i kontenerów zapobiegających rozjeżdżaniu się layoutu przy długich tytułach 2026-07-04 16:17:54 +02:00
mmacierzanka 8f0b398843 feat: deduplikacja wpisów w historii odtwarzania (każde nagranie widoczne tylko raz) 2026-07-04 16:14:40 +02:00
mmacierzanka 11a1834312 style: poprawa kontrastu i widoczności przycisku zamknięcia w panelu ustawień 2026-07-04 16:13:46 +02:00
mmacierzanka d074df571c fix: przywrócenie deklaracji voiceSelect w app.js rozwiązujące brak ładowania kanałów w interfejsie 2026-07-04 16:02:46 +02:00
mmacierzanka ebee5ac9d3 fix: dodanie brakującego importu Optional w bot.py oraz wyłączenie buforowania logów w Dockerze 2026-07-04 15:57:20 +02:00
mmacierzanka f2d19bd708 fix: dodanie python-multipart do requirements.txt dla obsługi przesyłania plików w FastAPI 2026-07-04 15:53:45 +02:00
mmacierzanka dcbd7859d7 refactor: przeniesienie konfiguracji kanału logów do dedykowanego modalu Ustawienia z wybierakiem per-serwer 2026-07-04 15:53:03 +02:00
mmacierzanka 89137f7d1c feat: dodanie trybu zapętlania całej kolejki (playlisty) w Discord (/loopplaylist) i w dashboardzie web 2026-07-04 15:39:32 +02:00
mmacierzanka 4f5ca391fa fix: naprawa działania pętli utworu - wyłączanie pętli pojedynczego utworu przy ręcznym pominięciu (/skip, przycisk w web) 2026-07-04 15:37:02 +02:00
mmacierzanka 65d797a1a6 feat: dodanie historii kolejki z możliwością ponownego odtwarzania w dashboardzie oraz komendy /history i /replay w Discord 2026-07-04 15:35:53 +02:00
mmacierzanka dfd681be04 feat: dodanie możliwości przesyłania własnych plików na serwer (wszystkie formaty ffmpeg), odtwarzanie jednym kliknięciem z web i komenda /playfile w Discord 2026-07-04 15:33:43 +02:00
mmacierzanka 444b797cd0 feat: sanityzacja tytułów w Discordzie oraz linki i embedy dla powiadomień z panelu web 2026-07-04 15:31:45 +02:00
mmacierzanka 867edf302f perf: usunięto redundantne zapytania HTTP statusu po kliknięciach przycisków, techniczne notyfikacje WS 2.0 2026-05-23 15:15:20 +02:00
mmacierzanka a8b2c89735 feat: zmigrowano panel webowy na WebSockets w czasie rzeczywistym 2026-05-23 15:10:19 +02:00
mmacierzanka 3eee27edfd feat: dodano pasek postępu odtwarzania w czasie rzeczywistym w panelu webowym 2026-05-23 15:04:42 +02:00
mmacierzanka c550215070 feat: add multi-guild support and text channel mapping persistence via Docker volume 2026-05-23 14:45:37 +02:00
mmacierzanka 07e32c1a1d chore: default volume to 100%, translate discord logs to Polish, rename project to ŻyrarBOT 2026-05-23 14:37:16 +02:00
mmacierzanka 71800c8fd5 feat: add beautiful web queue dashboard with loop, reordering, and text channel settings persistence 2026-05-23 14:32:43 +02:00
mmacierzanka 6382955b3f Remove yt-dlp version lock 1.0 2026-05-23 00:58:19 +02:00
mmacierzanka e24ca36ccb Remove redundant youtube_dl bug report suppression logic
Caused issues with playback
2025-06-14 02:13:21 +02:00
mmacierzanka bf724efe4d Update yt-dlp version in requirements.txt to 2025.6.9 2025-06-14 02:13:09 +02:00