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.