Python İle Klasör İzleme ve Görsel Formatları Arasında Dönüştürme İşlemi

Watchdog Events, JPG-PNG ve WEBP Dönüşürme İşlemleri

Python ve FTP Dosya İşlemleri başlıklı bir önceki yazıda Python ile dosya ve FTP işlemlerine değinmiştim. İlgili yazının devamı olarak, bu yazıda yine Python aracılığı ile gerçekleştirilebilecek bir görsel format dönüştürme işlemine değinmeye çalışacağım.

AA

Grav CMS ile ilgili yazılarımı takip ediyorsanız veya hali hazırda bu içeri yönetimi kullanmaktaysanız, bildiğiniz üzere bir içeriği yayınlamak için öncelikle posts ya da ilgili diğer bir dizin içerisinde bir markdown dosyası (örn. post.tr.md) oluşturmam gerekir1. Bu aşamada, eğer yazı içerisinde görsel yer alacaksa ilgili görselin de ya içeriğe ait ya da diğer bir dizinde yer alması gerekir2. Ben isim benzerlikleri ve versiyon farklılıklarından dolayı oluşabilecek sorunları önlemek adına genelde her görseli ilişkili olduğu dizin içerisinde tutuyor ve her görseli oluşturduğum bir shortcode ile çağırıyorum.

[resimage src="gorsel-adi" alt="ALT" caption="CAPION"]

src içerisinde belirtilen ad shortcode tarafından png, jpg ve webp formatlarında çağırılıyor ve render ardından şu biçime kavuşuyor.

<figure class="figure mt-0 mb-3 mx-0 img-responsive">
  <picture>
    <source data-srcset="gorsel-adi.jpg" type="image/jpeg">
    <source data-srcset="gorsel-adi.png" type="image/png">
    <source data-srcset="gorsel-adi.webp" type="image/webp">
    <img src="gorsel-adi.jpg" data-src="gorsel-adi.jpg" alt="ALT" class="lazy img-responsive img-fit-cover" title="ALT">
  </picture>
  <figcaption class="figure-caption text-tiny text-gray">CAPTION</figcaption>
</figure>

Elbette bu görsellerin oluşturulması sürecinde sıkıştırma ve format dönüştürme işlemleri söz konusu. Sıkıştırma işlemleri için ImageOptim3 uygulamasından ve aşağıdaki komutlardan faydalanıyorum. Bu konuda dair daha önce yayınladığım jpegoptim ve optipng İle Görselleri Optimize Etmek başlıklı yazıma göz atabilirsiniz.

jpegoptim -q 80 /Users/user/Desktop/gorsel-adi.png
optipng -o7 -preserve /Users/user/Desktop/gorsel-adi.png

Görsellerin webp fırmatına dönüştürülmesi işleminde ise cwebp komutunu kullanıyorum. Bu işlem ile ilgili daha detaylı bilgi için WebP Nedir? Nasıl Kullanılır? başlıklı yazımdan faydalanabilirsiniz.

cwebp -q 90 /Users/user/Desktop/gorsel-adi.png -o /Users/user/Desktop/gorsel-adi.webp

Görüldüğü üzere bir dizi komut içeren birkaç adımın sonucunda yazı yayınlanmaya hazır hale geliyor. Peki, tekrarlanır bu işlemleri nasıl daha pratik bir şekilde ele alabilirim?

Python Watchdog ve Pillow kullanımı
Python Watchdog ve Pillow kullanımı

Python Watchdog Kütüphanesi

Python programlama dili, dosya sistemi olaylarını (oluşturma, güncelleme, silme vb.) izlemek için Watchdog adında müthiş bir Python API kütüphanesine sahip4. Aynı zamanda komut satırı arayüzü aracılığı ile de bu kütüphaneden faydalanmak mümkün.

Bu kütüphane ile ilgili ilerleyen zaman içerisinde, daha fazla bilgi edindikçe yeni örnekler eklemeye devam edeceğim. Şimdilik, görsellerin dönüştürülme sürecini nasıl daha pratik hale getirebileceğimize bakalım.

Öncelikle, bir klasör oluşturmak ve bu klasör içeriğini izlemek isteyelim. Bu durumda Watchdog bize FileSystemEventHandler sınıfı aracılığı ile on_created, on_deleted, on_modified ve on_moved etkinlikleri ile ilişki kurma imkanı sunmakta. Ayrıca, on_any_event ile gerçekleştirilen ve sınıf aracılığı yakalanabilen tüm etkinlikleri izleyebilirsiniz.

from watchdog.events import FileSystemEventHandler
import time
from watchdog.observers import Observer

class convImage(FileSystemEventHandler):
    def on_any_event(self, event):
        print("EVENT")
        print(event.event_type)
        print(event.src_path)
        print()

flToTrack = '/Users/user/Desktop/Old'

evHandler = convImage()
observer = Observer()
observer.schedule(evHandler, evHandler.flToTrack, recursive=True)
observer.start()

try:
  while True:
    time.sleep(10)
except KeyboardInterrupt:
  observer.stop()

observer.join()

Bu kod parçacığı ile birlikte flToTrack ile belirtilen klasörü izlemeye başlayabiliriz. Herhangi bir dosyayı ilgili klasör içerisine attığınızda on_any_event size şu bilgileri iletecektir;

EVENT
created
/Users/user/Desktop/Old/gorsel-adi.png

EVENT
modified
/Users/user/Desktop/Old

EVENT
modified
/Users/user/Desktop/Old/gorsel-adi.png

Şimdi, bu bilgileri kullanarak bir klasörü izleyelim ve eğer içeriğine png, jpg uzantılı bir dosya atılmış ise bu dosyaları farklı bir dizine birbirlerine ve webp formatına dönüştürülmüş olarak atalım.

Python Pillow Kütüphanesi

Pillow kütüphanesi esasında bir PIL forku5. Pillow aracılığı ile ebatlandırma, renk dönüştürme, koordinat belirleme, kesme-birleştirme ve daha pek çok işlemi gerçekleştirmek mümkün6.

Image.open('gorsel-adi.png').convert('RGB').save('gorsel-adi.jpg', 'jpeg')
Image.open('gorsel-adi.png').convert('RGB').save('gorsel-adi.webp', 'webp')

Görsel düzeleme işlemlerinin yanı sıra yine bu aşamada sıkıştırma ve yeniden adlandırma gibi adımlar da izlenebilir. Elbette Pillow bir zorunluluk değil. Görsellerle ilgili işlemler os.system() aracılığı ile shell komutları kullanılarak da ele alınabilir.

import os
if os.path.isfile(event.src_path):
  os.system(f'cwebp -q 90 {event.src_path} -o {event.src_path}.webp')

Şimdi, yukarıda bahsi geçe tüm bu süreci toparlayalım ve bir bütün haline getirelim.

#!/usr/bin/env python3

import os
from watchdog.events import FileSystemEventHandler
import time
from watchdog.observers import Observer
from PIL import Image

class convImage(FileSystemEventHandler):

    def __init__(self):
        self.flToTrack = '/Users/user/Desktop/Old'
        self.flDestination = '/Users/user/Desktop/New'

    def on_modified(self, event):

        for flFullName in os.listdir(self.flToTrack):

            fName, fExt = os.path.splitext(flFullName)

            if fExt == '.png':
                try:
                    Image.open(flFullName).convert('RGB').save(
                        f'{self.flDestination}/{fName}.jpg', 'jpeg')
                except Exception as err:
                    print(err)
            elif fExt in ['.jpg', '.jpeg']:
                try:
                    Image.open(flFullName).convert('RGB').save(
                        f'{self.flDestination}/{fName}.png', 'png')
                except Exception as err:
                    print(err)

            try:
                Image.open(flFullName).convert('RGB').save(
                    f'{self.flDestination}/{fName}.webp', 'webp')
            except Exception as err:
                print(err)

            src = f'{self.flToTrack}/{flFullName}'

            try:
                os.rename(src, f'{self.flDestination}/{flFullName}')
            except Exception as err:
                print(err)

if __name__ == "__main__":

    evHandler = convImage()
    observer = Observer()
    observer.schedule(evHandler, evHandler.flToTrack, recursive=True)
    observer.start()

    try:
        while True:
            time.sleep(10)
    except KeyboardInterrupt:
        observer.stop()

    observer.join()

İlgili kod parçacığını alias haline getirerek bir komut aracılığı ile işleme alınmasını sağlayabilirsiniz. Bu konuya kısaca Python ve FTP Dosya İşlemleri başlıklı yazımda değinmiştim. Ek olarak, yukarıdaki kod parçacığının güncel haline convImage.py adresi üzerinden ulaşabilir, önerilerinizi yorum olarak paylaşabilirsiniz.