Skrypt w fish, kompresujący 130 tysięcy zdjęć w ImageOptim automatycznie (cz. I)
Wczoraj pisałem o instalacji fisha zamiast basha, a był to wstępny krok do małego projektu, który nadal robię. Otóż wszystko rozbija się o to, że zdjęcia i grafiki na serwerze iMagazine, zbierane od wielu lat, zajmują 28 GB przestrzeni na dysku. To nie tyle problem dla nas, ale raczej dla Was wszystkich – im więcej dany materiał waży, tym więcej danych trzeba pobrać i tym dłużej się strona ładuje. Znalazłem w końcu czas na optymalizację.
ImageOptim
Pisałem o tym programie oraz o moich nastawach blisko dwa lata temu – korzystam z niego do dzisiaj. Problem w tym, że program znakomicie spisuje się do kompresji niewielkiej ilości zdjęć, ale przy większych ilościach (1000+), zaczyna wariować.
Kompresja ręczna
Po zgraniu 28 GB zdjęć i grafik z serwera, rozpocząłem ich kompresję, miesiąc po miesiącu (są podzielona na foldery według schematu .../RRRR/MM/
). Na początku nie było to problemem, bo w 2012 roku mieliśmy miesięcznie kilkaset plików do kompresji, przeważnie były to JPG, a ich kompresja trwała kilka sekund. PNG niestety kompresują się prawdopodobnie od 20- do 40-krotnie wolniej. Najważniejsze jednak, że ImageOptim potrafi wykorzystać wszystkie rdzenie i wątki, więc załadowanie wielu plików na raz to spory bonus – kompresowanie pojedynczych plików wydłuża czas mniej więcej czterokrotnie.
Problem zaczął się w późniejszych latach, gdy jeden folder zawierał dwa tysiące lub więcej plików. Większość ImageOptim potrafił przetworzyć, ale na niektórych plikach, bez wyraźnej przyczyny, po prostu się wykrzaczał. Wkurzyłem się i doszedł do tego fakt, że traciłem na tym za dużo czasu. Od początku wiedziałem, że powinienem to zautomatyzować, ale nie miałem kiedy się tym zająć.
Automatyzacja
Wczoraj w końcu usiadłem do napisania skryptu. Początkowo chciałem to zrobić w bashu, ale jak już wspominałem wczoraj, skoro musiałem przypominać sobie syntax, to stwierdziłem, że przesiądę się od razu na fisha.
Zacząłem od następującego skryptu:
#!/usr/local/bin/fish set -e imagefilelist echo Variables cleaned up. set imagefilelist (find /Volumes/Macintosh\ HDD/!\ iMag\ Temp\ !/2014/ -type f) echo File paths loaded. echo Files found: (count $imagefilelist) set imagefilelistcounter (count $imagefilelist) for i in (seq $imagefilelistcounter) /Applications/ImageOptim.app/Contents/MacOS/ImageOptim $imagefilelist[$i] echo Current file: $imagefilelist[$i] echo Files remaining: (math "$imagefilelistcounter-$i") end set -e imagefilelist set -e imagefilelistcounter set -e i
Wytłumaczenie poszczególnych linii:
- Definicja shella, który ma uruchomić skrypt.
- Wyzerowanie zmiennej
imagefilelist
, która zawiera listę wszystkich plików do skompresowania – robi to argument-e
. Nie do końca wiem jeszcze jak fish traktuje zmienne, więc wolałem uniknąć problemów. - Wyświetlenie informacji o wyczyszczeniu zmiennych.
- Ustawiam zmienną
imagefilelist
i wywołuję komendęfind
, której wynik będzie przypisany do zmiennej (dlatego jest w nawiasie). Komendafind
wywołuje się pierwsza – jej wynikiem jest tekstowa lista plików wraz ze ścieżkami do nich – a jej wynik zostaje zapisany doimagefilelist
. Ścieżka do plików jest ustawiona na sztywno – powinienem ją dodać jak argument przy wywoływaniu skryptu, ale nie zrobiłem tego, bo piszę go dla siebie i prawdopodobnie wykorzystam go tylko raz. - Info dla mnie.
- Info dla mnie – wyświetla wynik komendy
count $imagefilelist
, która liczy ile ścieżek do plików zapisało się w zmiennejimagefilelist
. - Deklaruję kolejną zmienną
imagefilelistcounter
, która zawiera liczbę ścieżek do plików zapisanych do zmiennejimagefilelist
. Tak, powinien był zrobić zamienić to miejscami z linią 6, aby wykorzystać zmienną i nie liczyć tego dwa razy. - Tutaj rozpoczyna się właściwe działanie na plikach. Skorzystałem ze zmiennej
for
, która wykona komendę… /Applications/ImageOptim.app/Contents/MacOS/ImageOptim $imagefilelist[$i]
dla każdej ścieżki. W skrócie zrobi tak: otworzy ImageOptim z pierwszym plikiem zapisanym do zmiennejimagefilelist
, skompresuje ten plik, ImageOptim się automatycznie zamknie po skompresowaniu pliku, a pętla zrobi to samo dla drugiego, trzeciego i kolejnych plików, aż dojedzie do końca listy.- Po kompresji każdego pliku, skrypt wyświetli w terminalu ścieżkę kompresowanego pliku…
- … oraz policzy ile plików jeszcze pozostało do skompresowania i również tę wartość wyświetli.
- Kończę tutaj pętlę.
- Na koniec pozostaje wyzerowanie zmiennych. Tutaj też powinienem był przenieść zerowanie zmiennej z linii 2 skryptu.
W praktyce skrypt działa wzorowo. Jest prosty i robi co do niego należy, ale w rzeczywistości jest bardzo nieefektywny – jak wspominałem powyżej, ImageOptim nie wykorzystuje swojego pełnego potencjału na pojedynczych plikach i warto mu zapodać ich więcej za jednym razem.
Zamiast optymalizować ten skrypt, napisałem kolejny… ale o tym napiszę w następnym wpisie z serii.
Komentarze: 9
28GB? Poważnie? Pierwszy rok w JPG/PNG, a późniejsze lata w RAWach 😂?
Bardzo fajna i przejrzysta składnia; poważnie zaczynam rozważać przesiadkę na Fisha 😀.
Co do skryptu: faktycznie mogiła! Bardzo wolno to chodzi. Mam nadzieję, że werjsa 2.0 działa sprawniej. Swoją drogą, jeżeli masz taką bogatą bibliotekę do przemielenia polecam rzucić okiem na Squash. Dla testów wrzuciłem mu ok. 1.100 zdjęć z aparatu i nawet nie stęknął.
Dolicz do każdego JPG i PNG po 6-8 plików generowanych przez Retinę i robi się tego sporo.
Naprawdę napisałeś skrypt z tyloma komentarzami, pętlą i innymi niepotrzebnymi rzeczami zamiast wpisać jedną linie w terminalu?
Mozesz uzyc find -exec lub find |xargs
find . -type f -exec /tam/gdzie/mamy/ImageOptim {} ;
Polecam na początek podstawy linuxa, basha i podstawowe polecenia linuxowe i ich argumenty.
Ta linijka jednak nie rozwiązuje dwóch kwestii których nie doczytałeś:
– nauki syntaxu fisha
– otwierania wielu plików z listy jednocześnie
To był wstęp do drugiego skryptu.
Punkt drugi rozwiązać powinna też jedna linijka tylko z wykorzystaniem xargs
find . -type f -print0 |xargs -0 -P4 -n10 /tamgdziejest/ImageOptim
Uruchomi to 4 procesy jednoczesnie i do kazdego wrzuci 10 plikow.
Wiadomo, że nauka jest wazna, ale po co robić coś co już jest dostepne jako podstawowe narzędzia w systamach linuxowych i działa dobrze.
O i to jest ciekawe rozwiązanie – zobaczę potem jak mogę to zaadaptować do swojego! Drugą wersję skryptu, do której ta prowadziła, będę wrzucał za parę godzin – zerknij sobie.
Sprawdź funkcja time co działa szybciej. Może akurat Twój sposób okaże się bardziej skomplikowany ale wydajniejszy :)
Właśnie robię skomplikowany (w sensie dużo „pierdzielenia się”) test wydajności 5 różnych skryptów. Zaraz będą wyniki. Wpis prawie gotowy.
OK puściłem wpis: https://imagazine.pl/2017/05/26/skrypt-w-fish-kompresujacy-130-tysiecy-zdjec-w-imageoptim-automatycznie-cz-ii/