iMagazine

Skrypt w fish, kompresujący 130 tysięcy zdjęć w ImageOptim automatycznie (cz. I)

25/05/2017, 10:44 · · · 9

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:

Wytłumaczenie poszczególnych linii:

  1. Definicja shella, który ma uruchomić skrypt.
  2. 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.
  3. Wyświetlenie informacji o wyczyszczeniu zmiennych.
  4. Ustawiam zmienną imagefilelist i wywołuję komendę find, której wynik będzie przypisany do zmiennej (dlatego jest w nawiasie). Komenda find wywołuje się pierwsza – jej wynikiem jest tekstowa lista plików wraz ze ścieżkami do nich – a jej wynik zostaje zapisany do imagefilelist. Ś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.
  5. Info dla mnie.
  6. Info dla mnie – wyświetla wynik komendy count $imagefilelist, która liczy ile ścieżek do plików zapisało się w zmiennej imagefilelist.
  7. Deklaruję kolejną zmienną imagefilelistcounter, która zawiera liczbę ścieżek do plików zapisanych do zmiennej imagefilelist. Tak, powinien był zrobić zamienić to miejscami z linią 6, aby wykorzystać zmienną i nie liczyć tego dwa razy.
  8. Tutaj rozpoczyna się właściwe działanie na plikach. Skorzystałem ze zmiennej for, która wykona komendę…
  9. /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 zmiennej imagefilelist, 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.
  10. Po kompresji każdego pliku, skrypt wyświetli w terminalu ścieżkę kompresowanego pliku…
  11. … oraz policzy ile plików jeszcze pozostało do skompresowania i również tę wartość wyświetli.
  12. Kończę tutaj pętlę.
  13. 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.

9

Wojtek Pietrusiewicz

Wydawca, fotograf, podróżnik, podcaster – niekoniecznie w tej kolejności. Lubię espresso, mechaniczne zegarki i zwinne samochody. Niedawno rozpocząłem prowadzenie kursów Lightrooma i fotografii na Pikselowe.pl – zapraszam.

morid1n

Dodaj komentarz

Buthimild napisał(a):

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ął.

Moridin napisał(a):

Dolicz do każdego JPG i PNG po 6-8 plików generowanych przez Retinę i robi się tego sporo.

Łukasz Motyka napisał(a):

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.

Moridin napisał(a):

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.

Łukasz Motyka napisał(a):

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.

Moridin napisał(a):

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.

Łukasz Motyka napisał(a):

Sprawdź funkcja time co działa szybciej. Może akurat Twój sposób okaże się bardziej skomplikowany ale wydajniejszy :)

Moridin napisał(a):

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.

Jeśli chcesz wesprzeć redakcję iMagazine, podoba Ci się nasza praca, to zapraszamy do iMag Weekly

dołącz

W archiwum iMag Weekly znajdziecie ponad 500 felietonów, artykułów, recenzji, opisów, przepisów oraz relacji z podróży – w sumie do przeczytania jest ponad 500 tysięcy słów. Wykupienie dostępu do niego jest „dożywotnie” (czyli tak długo jak będzie funkcjonował iMagazine) i wystarczy to zrobić raz. Nasz tygodnik był wydawany do dnia 27/01/2017.

Osoby, które miały wykupioną jakąkolwiek subskrypcję otrzymały pełny dostęp do archiwum.