185 lines
12 KiB
Markdown
185 lines
12 KiB
Markdown
# Drupal 6 – duże obrazy, lokalizacja tematów i bezpieczna optymalizacja
|
||
|
||
Rozbudowana wersja `compare_filesize_drupal6`. Działa **poza Drupalem**, w Pythonie 3.9+; potrzebuje dostępu do MySQL/MariaDB i katalogu plików.
|
||
|
||
## Co robi
|
||
|
||
- Wyszukuje największe pliki i największe rozdzielczości (JPG, PNG, GIF – GIF wyłącznie do analizy).
|
||
- Wskazuje, **w jakim temacie, poście lub komentarzu** pojawia się obraz. Raport pokazuje `nid`, `cid`, tytuł, typ zawartości, URL do wpisu, URL do edycji i źródło powiązania.
|
||
- Szuka połączeń przez `upload.fid` i pola `*_fid` w tabelach CCK/ImageField/FileField; obsługuje aktualne rewizje (`vid`) i opcjonalne tabele załączników komentarzy (`cid` + `fid`).
|
||
- Szuka obrazów w `node_revisions.body` i `node_revisions.teaser`, `comments.comment`, opcjonalnie też w tekstowych polach CCK. Rozróżnia **pewne** dopasowania identyfikatora `fid` lub pełnej ścieżki od **możliwych** trafień po samej nazwie pliku.
|
||
- Generuje link do oryginalnego pliku (jeżeli publiczny) i pomocniczy link do Drupal Search (jeżeli moduł Search jest aktywny).
|
||
- Generuje CSV (polskie kolumny, Excel, UTF-8 BOM) i JSON.
|
||
- Symuluje dokładną optymalizację w **dry-run**, bez zapisu źródłowego pliku i bazy.
|
||
- Przy `--apply` zachowuje oryginał w oddzielnym katalogu, przygotowuje nowy obraz do atomowej podmiany, zapisuje `files.filesize`, a przy błędzie próbuje odtworzyć oryginał i wartości bazy.
|
||
- Wykrywa duplikaty SHA-256 i pliki na dysku, których nie ma w analizowanym zestawieniu z bazy.
|
||
|
||
## Instalacja
|
||
|
||
```bash
|
||
unzip compare_filesize_drupal6-extended.zip
|
||
cd compare_filesize_drupal6
|
||
python3 -m venv .venv
|
||
source .venv/bin/activate
|
||
pip install -r requirements.txt
|
||
cp config.example.py config.py
|
||
```
|
||
|
||
W `config.py` ustaw:
|
||
|
||
```python
|
||
DB_CONFIG = {
|
||
'host': 'localhost',
|
||
'port': 3306,
|
||
'database': 'drupal6_db',
|
||
'user': 'drupal_user',
|
||
'password': 'twoje_haslo',
|
||
'charset': 'utf8',
|
||
}
|
||
FILES_BASE_PATH = '/var/www/drupal/sites/default/files'
|
||
PUBLIC_FILES_PATH = 'sites/default/files'
|
||
DRUPAL_ROOT = '/var/www/drupal'
|
||
DRUPAL_BASE_URL = 'https://forum.example.org'
|
||
DB_TABLE_PREFIX = ''
|
||
```
|
||
|
||
**Ważne:** `FILES_BASE_PATH` to lokalizacja rzeczywistych plików, a `PUBLIC_FILES_PATH` to fragment zapisany zwykle w `files.filepath`. Jeśli pliki są np. w `/var/www/drupal/files`, zmień obie wartości. Można też podać je w CLI: `--public-files-path`, `--drupal-root`. Gdy Drupal pracuje bez clean URLs, dodaj `--no-clean-urls`.
|
||
|
||
## 1. Znajdź tematy i komentarze z największymi obrazami
|
||
|
||
```bash
|
||
python main.py --find-usage --top 50 --report-csv najwieksze-z-linkami.csv
|
||
```
|
||
|
||
Przykładowe wiersze (fikcyjne):
|
||
|
||
```text
|
||
1. 18.42 MB 4200x5900 fid=1234 scans/mapa.jpg
|
||
-> Archiwalne skany (forum, nid=812, pewne)
|
||
LINK: https://forum.example.org/node/812
|
||
EDYCJA: https://forum.example.org/node/812/edit
|
||
SKAD: upload.fid, tresc wpisu (sciezka)
|
||
-> Zdjęcie w komentarzu (forum, nid=920, cid=42, pewne)
|
||
LINK: https://forum.example.org/node/920#comment-42
|
||
```
|
||
|
||
CSV zawiera jedną linię dla każdego znalezionego miejsca. Gdy powiązania brak, w CSV zostaje plik, jego `fid`, rozmiar i propozycja wyszukiwania nazwą w Drupal Search.
|
||
|
||
### Tylko skany lub wybrane pliki
|
||
|
||
```bash
|
||
python main.py --find-usage --top 100 --path-contains skan --report-csv skany.csv
|
||
python main.py --find-usage --fid 1234 --report-csv jeden-obraz.csv
|
||
python main.py --find-usage --min-size-mb 5 --usage-scope all --report-csv wieksze-niz-5mb.csv
|
||
```
|
||
|
||
`--usage-scope top` szuka tylko w `--top N` największych obrazach; `optimizable` – w obrazach przekraczających limity; `all` – w całym filtrowanym zestawieniu. Przy bardzo dużych bazach `all` może oznaczać długi skan treści. `--no-text-scan` wyłącza szukanie w treściach i zostawia szybsze powiązania `fid`. `--scan-cck-text` rozszerza skan na tekstowe pola CCK.
|
||
|
||
## 2. Dokładny dry-run optymalizacji
|
||
|
||
```bash
|
||
python main.py \
|
||
--find-usage --usage-scope optimizable \
|
||
--optimize --dry-run --optimize-top 20 \
|
||
--max-width 2400 --max-height 3200 --max-file-mb 2 \
|
||
--report-csv plan.csv --report-json plan.json
|
||
```
|
||
|
||
Dostaniesz rzeczywisty przewidywany rozmiar po rekompresji (test wykonany na pliku tymczasowym). **Nie zmieni to oryginałów ani SQL.** `--top` dotyczy wyświetlania; `--optimize-top` ogranicza liczbę obrazów poddawanych testowi/zmniejszaniu.
|
||
|
||
### Duze skany: DecompressionBombWarning / DecompressionBombError
|
||
|
||
Pillow standardowo ostrzega powyzej ok. **89,5 mln pikseli (89,5 MP)**, a powyzej ok. **179 mln** przerywa otwieranie z bledem `DecompressionBombError`. To limit liczby pikseli, **nie rozmiaru pliku w MB**. Uczciwe skany dokumentow moga miec 100-200 MP.
|
||
|
||
W tej wersji prog jest konfigurowalny: domyslnie `--max-image-mpixels 220`, dzieki czemu skany o **199 756 800 pikselach** z podanego logu nie wywoluja juz ostrzezen ani automatycznego bledu Pillow. Narzedzie **nie wylacza globalnie ochrony**: otwiera naglowek, sprawdza liczbe pikseli **przed dekodowaniem**, a przekraczajace limit obrazy wypisuje jako `[SKIP-LIMIT]` z przyczyna. Pozostale rodzaje ostrzezen nie sa filtrowane. Zbyt duzy obraz nie zostaje zmieniony na dysku ani w bazie.
|
||
|
||
Uruchom ponownie swoj test:
|
||
|
||
```bash
|
||
python main.py \
|
||
--min-size-mb 5 --optimize --dry-run \
|
||
--max-width 4000 --max-height 6000 --max-file-mb 3 \
|
||
--max-image-mpixels 220 --report-csv plan.csv
|
||
```
|
||
|
||
Jesli serwer ma malo RAM, mozna obnizyc limit, np. do `--max-image-mpixels 120`: obrazy powyzej 120 MP zostana **pominiete**, a nie bezwarunkowo dekodowane. Skany JPEG wymagajace skalowania korzystaja z `draft()` do zmniejszenia pamieci przy dekodowaniu. Bardzo duze PNG moga nadal potrzebowac **ponad 1 GB RAM** na dekodowanie i skalowanie; domyslny limit MP nie jest gwarancja bezpiecznego zuzycia pamieci. Uruchamiaj najpierw `--dry-run`, a `--apply` testuj na pojedynczych plikach z backupem.
|
||
|
||
Nie ustawiaj `Image.MAX_IMAGE_PIXELS = None` ani nie ignoruj wszystkich ostrzezen Pillow: takie ustawienia usuwaja mechanizm ostrzegania przed niebezpiecznymi obrazami.
|
||
|
||
## 3. Zmniejsz np. pierwsze 10 obrazów i aktualizuj bazę
|
||
|
||
**Najpierw zrób pełen backup bazy danych i katalogu plików** oraz przetestuj na kopii. Następnie:
|
||
|
||
```bash
|
||
python main.py \
|
||
--optimize --apply \
|
||
--optimize-top 10 --max-changes 10 \
|
||
--backup-dir /srv/backups/drupal6-images \
|
||
--max-width 2400 --max-height 3200 --max-file-mb 2 \
|
||
--report-csv wykonane.csv --report-json wykonane.json
|
||
```
|
||
|
||
Każda udana zmiana:
|
||
|
||
1. Zapisuje oryginał w osobnym katalogu (automatycznie tworzony podfolder uruchomienia).
|
||
2. Zastępuje obraz tylko jeżeli nowy plik jest mniejszy o co najmniej `--min-gain-percent` (domyślnie 5%).
|
||
3. Aktualizuje `files.filesize` dla wszystkich `fid` wskazujących na tę samą ścieżkę.
|
||
4. W przypadku błędu SQL próbuje odtworzyć oryginalny plik i rozmiar w DB (także przy MyISAM – kompensacja aktualizacji).
|
||
|
||
`--apply` jest wymagane do każdego zapisu. Samo `--optimize` lub `--update-db` nie wykonuje zmian. **Realna optymalizacja zawsze aktualizuje rozmiar w DB**, także bez `--update-db`.
|
||
|
||
Jeżeli chcesz wyłącznie zsynchronizować rozmiary plików w istniejącym `files`:
|
||
|
||
```bash
|
||
python main.py --update-db --dry-run
|
||
python main.py --update-db --apply
|
||
```
|
||
|
||
`--update-db` w połączeniu z `--optimize --apply` dodatkowo koryguje rozmiary tych plików, które *nie zostały* zoptymalizowane.
|
||
|
||
## Zasady jakości i bezpieczeństwa
|
||
|
||
- **JPEG:** zachowuje rozszerzenie i ścieżkę, skaluje proporcjonalnie filtrem LANCZOS, zapisuje z wysoką jakością 92/89/86/85 (`--jpeg-quality`, `--min-jpeg-quality`). Nie schodzi poniżej skonfigurowanej jakości, nawet gdy cel 2 MB nie zostanie osiągnięty. JPEG jest formatem stratnym – każda ponowna kompresja może zmienić piksele.
|
||
- **PNG:** zachowuje format oraz kanał przezroczystości, używa kompresji **bezstratnej**. Nie konwertuje PNG do JPEG i nie zmienia rozszerzeń, bo mogłoby to popsuć stare linki w Drupalu. Duży PNG ze skanem może pozostać duży, jeśli kompresja bezstratna nic nie da. Konwersja do JPEG wymagałaby osobnej, kontrolowanej migracji i aktualizacji odnośników.
|
||
- **Skan tekstowy:** domyślny limit **2400 × 3200 px** zamiast starego 1920 × 1080. Przed decyzją sprawdź czytelność drobnego druku w próbce i w razie potrzeby zwiększ limity lub pomiń plik.
|
||
- **GIF/animacje, nieobsługiwane formaty, niezgodności formatu i rozszerzenia:** pomijane przy zapisie. Symlinki i hardlinki też są pomijane.
|
||
- Kopia oryginału wymagana jest przy `--optimize --apply`, a lokalizacja kopii **musi być poza** `FILES_BASE_PATH` i poza katalogiem WWW Drupala (jeśli ustawiono `DRUPAL_ROOT`).
|
||
- Weryfikacja czasu modyfikacji i inode chroni przed zmianą pliku w trakcie skanowania; zalecane jest wykonywanie pracy w oknie serwisowym.
|
||
- Zachowuje ICC, DPI, EXIF (z prawidłową obsługą orientacji) i tekstowe metadane PNG w obsługiwanych formatach, ale nie gwarantuje zachowania każdego niestandardowego segmentu metadanych.
|
||
- Aktualizowany jest tylko `files.filesize` – nie są zmieniane ścieżki, `fid`, treści postów ani załączniki. Zmiana dużego obrazka może wymagać ręcznego odświeżenia wygenerowanych miniatur przez ImageCache.
|
||
- Kopia wykonana przez samo narzędzie obejmuje **obraz**, nie całą bazę. MyISAM nie zapewnia transakcji; procedura przywracania jest best-effort. Pełny backup DB i plików przed zmianami jest obowiązkowy.
|
||
|
||
## Pozostałe opcje
|
||
|
||
```bash
|
||
python main.py --find-duplicates
|
||
python main.py --show-deleted --exclude-folders imagecache styles
|
||
python main.py --find-usage --no-clean-urls --site-url https://example.org/drupal
|
||
python main.py --help
|
||
```
|
||
|
||
Uwaga do `--show-deleted`: pokazuje pliki na dysku **bez pasującego wpisu `files` w analizowanym zestawieniu**, nie zaś potwierdzenie, że plik naprawdę nie występuje w treści. Przy filtrach (`--path-contains`, `--min-size-mb`, `--fid`) plik odfiltrowany może zostać pokazany jako „spoza zestawienia”. Nic nie jest usuwane.
|
||
|
||
### Ograniczenia wyszukiwania
|
||
|
||
- Powiązania po `fid` są najbardziej jednoznaczne; wyszukiwanie po samej nazwie pliku bywa niejednoznaczne – w raporcie `pewnosc=mozliwe`.
|
||
- Tekst przeszukiwany jest w **aktualnych rewizjach** węzłów i komentarzach. Nie szuka w archiwalnych rewizjach ani w niestandardowych zewnętrznych modułach przechowywania treści bez włączonego skanu CCK.
|
||
- Link do nieopublikowanego wpisu lub komentarza może wymagać konta z prawami administracyjnymi. Raport to zaznacza.
|
||
- **Drupal 6 stronicuje komentarze**: `node/NID#comment-CID` może nie przewinąć do komentarza z dalszej strony. Jeżeli masz zainstalowany moduł [Permalink dla Drupal 6](https://www.drupal.org/project/permalink), dodaj `--comment-permalinks`, aby generować `comment/CID#comment-CID`, które kieruje na właściwą stronę komentarzy. Bez tego modułu nie używaj tej flagi. Niezależnie od typu linku raport pokazuje `nid` i `cid`.
|
||
- Nie ma gwarancji odnalezienia obrazów w niestandardowych relacjach tworzonych przez własne moduły, w CSS i w HTML poza bazą.
|
||
- W przypadku prywatnego systemu plików adres bezpośredni do pliku może nie działać; odnośnik do tematu nadal powinien wskazać miejsce.
|
||
|
||
## Testy
|
||
|
||
```bash
|
||
python -m unittest discover -s tests -v
|
||
```
|
||
|
||
Testy z lokalną, symulowaną bazą weryfikują powiązania Upload/CCK, obecne rewizje, komentarze, brak zmian w dry-run, aktualizację `files.filesize`, backup, przywracanie po błędzie SQL, format PNG i ścieżki katalogów. **Nie zastępują próby na kopii konkretnej instalacji Drupal 6**.
|
||
|
||
### Źródła struktury bazy Drupal 6
|
||
|
||
- [Oficjalny migrator Drupal 6 Upload](https://api.drupal.org/api/drupal/core%21modules%21file%21src%21Plugin%21migrate%21source%21d6%21Upload.php/8.9.x) – `upload.fid`, `upload.nid`, `upload.vid`.
|
||
- [Oficjalny migrator komentarzy D6](https://api.drupal.org/api/drupal/core%21modules%21comment%21src%21Plugin%21migrate%21source%21d6%21Comment.php/class/Comment/main) – tabela `comments`, `cid`, `nid`, status komentarza.
|
||
- [CCK 6](https://api.druphelp.com/api/cck/content.module/6) – `content_type_*`, `content_field_*`.
|