컴퓨터

압축파일에 있는 이미지 파일들을 webp로 변환하는 소스코드와 프로그램

파이썬 코드임

다른 사람이 뿌린 오픈소스에 오류 안생기게 조금 수정했음
오류는 없고 오류가 뜬다면 이미지 파일 자체가 손상돼서 변환이 되지않는 경우라서

원래 그것도 변환되게 할 수 있는데 안하는게 나아서 패스함

 

# -*- coding:utf-8 -*-
from PIL import Image
import zipfile
from zipfile import ZipFile
import glob
import os
import shutil
import multiprocessing
import sys
import chardet # 인코딩 확인
from ctypes import windll, wintypes, byref

THUMBNAIL_NAME = '_thumbnail.png'

def unzip(source_file, dest_path):
    with zipfile.ZipFile(source_file, 'r') as zf:
        zipInfo = zf.infolist()
        for member in zipInfo:
            try:
                #print(chardet.detect(member.filename.encode()))
                #member.filename = member.filename.encode('cp437', errors='replace').decode('cp949', 'ignore')
                member.filename = member.filename.encode('cp437').decode('euc-kr', 'ignore')
                zf.extract(member, dest_path) #압축해제할 파일명, 압축해제할 경로
            except:
                try:
                    zf.extract(member, dest_path) #한글로 변환이 안 될시 그대로 변환
                except:
                    raise Exception(source_file + ' 파일명과 ' + dest_path + ' 경로가 잘못되었습니다.')
                #zf.extract(member, dest_path)
                #print(source_file)
                #raise Exception('what?!')

def modify_ctime(path, ctime):
    #https://stackoverflow.com/questions/4996405/how-do-i-change-the-file-creation-date-of-a-windows-file
    # Arbitrary example of a file and a date
    filepath = path
    epoch = ctime

    # Convert Unix timestamp to Windows FileTime using some magic numbers
    # See documentation: https://support.microsoft.com/en-us/help/167296
    timestamp = int((epoch * 10000000) + 116444736000000000)
    ctime = wintypes.FILETIME(timestamp & 0xFFFFFFFF, timestamp >> 32)

    # Call Win32 API to modify the file creation date
    handle = windll.kernel32.CreateFileW(filepath, 256, 0, None, 3, 128, None)
    windll.kernel32.SetFileTime(handle, byref(ctime), None, None)
    windll.kernel32.CloseHandle(handle)

def get_thread_number():
    number_of_thread = multiprocessing.cpu_count() - 1
    if number_of_thread == 0:
        return 1
    else:
        return number_of_thread

def split_to_chunks(image_paths, n):
    return [image_paths[i::n] for i in range(n)]

def convert_images(image_paths, options, shared_queue):
    for image_path in image_paths:
        image = Image.open(image_path)
        image.save(os.path.splitext(image_path)[0] + '.webp', method=6, lossless=options['lossless'], quality=options['quality'])

    for image_path in image_paths:
        os.remove(image_path)

    shared_queue.put(True)
    return

def generate_thumbnail(folder_path):
    MAX_SIZE = 640
    file_paths = []
    image_path = get_file_paths_by_extensions(folder_path, ['jpg', 'png', 'webp', 'gif'], file_paths)[0]

    # Generate thumbnail only if images exist
    if not image_path:
        return

    image = Image.open(image_path)
    width, height = image.size
    resize_ratio = min(MAX_SIZE/width, MAX_SIZE/height)
    resized_resolution = (int(width*resize_ratio), int(height*resize_ratio))
    thumbnail = image.resize(resized_resolution)
    thumbnail.save(os.path.join(folder_path, THUMBNAIL_NAME))

def get_file_paths_by_extensions(folder_path, extensions, file_paths):
    sub_folder_paths = os.listdir(folder_path)
    
    for sub_folder_path in sub_folder_paths:
        full_folder_path = os.path.join(folder_path, sub_folder_path)
        if os.path.isdir(full_folder_path):
            get_file_paths_by_extensions(full_folder_path, extensions, file_paths)
        elif os.path.isfile(full_folder_path):
            full_folder_name, full_folder_path_extension = os.path.splitext(full_folder_path)
            for extension in extensions:
                if full_folder_path_extension == "." + extension:
                    if not os.path.basename(full_folder_path) == THUMBNAIL_NAME: # 썸네일 파일 제외
                        file_paths.append(full_folder_path)
                        
    return file_paths

def recover_zip(folder_path, backup_path, zip_file_path):
    os.rename(backup_path, zip_file_path)
    shutil.rmtree(folder_path)
    raise NameError('Error during processing images')

def slimify(zip_file_path, options):
    print('Processing', zip_file_path)

    # Save file time info
    file_time = (os.stat(zip_file_path).st_atime, os.stat(zip_file_path).st_mtime)
    ctime = os.stat(zip_file_path).st_ctime

    # Unzip and make backup
    folder_path, extension = os.path.splitext(zip_file_path)
    #zip_path, zip_name = os.path.split(os.path.realpath(zip_file_path))
    #zip_file_path = os.path.join(zip_path, folder_path)
    #shutil.unpack_archive(zip_file_path, folder_path, 'zip') # 압축 파일의 전체 경로, 압축 해체할 폴더의 경로, 압축파일 형식
    unzip(zip_file_path, folder_path) # 압축 해제할 파일명, 압축 해제할 경로
    if not os.path.exists(folder_path):
        raise NameError('File name cannot be folder name')
    backup_path = zip_file_path + '.back'
    os.rename(zip_file_path, backup_path)

    # Find jpg or png
    file_paths = []
    image_paths = get_file_paths_by_extensions(folder_path, ['jpg', 'png', 'jpeg'], file_paths)
    image_path_chunks = split_to_chunks(image_paths, get_thread_number())

    # Generate thumbnail if needed
    if options['thumbnail']:
        generate_thumbnail(folder_path)

    # Convert images
    processes = []
    shared_queue = multiprocessing.Queue()
    for i in range(get_thread_number()):
        p = multiprocessing.Process(target=convert_images, args=(image_path_chunks[i], options, shared_queue))
        processes.append(p)
        p.start()
    for process in processes:
        process.join()

    # Check if error occured in multiprocessing
    if shared_queue.qsize() != get_thread_number():
        recover_zip(folder_path, backup_path, zip_file_path)
        return
    
    # Rezip the folder
    converted_zip_path = shutil.make_archive(folder_path, 'zip', folder_path)

    # Recover file time info
    os.utime(converted_zip_path, file_time)
    modify_ctime(converted_zip_path, ctime)


    # Remove old zip file and folder
    shutil.rmtree(folder_path)
    os.remove(backup_path)

def get_lossless_option():
    print('Select lossy or lossless (Default = lossy)')
    print('1. Lossy')
    print('2. Lossless')

    answer = input()

    if answer == '2':
        return True
    else:
        return False

def get_quality_option():
    DEFAULT = 80
    print('Select quality parameter 1-100 (Default = 80)')
    answer = input()

    if answer.isdecimal() == False:
        return DEFAULT
    
    answer_number = int(answer)

    if 0 < answer_number <= 100:
        return answer_number
    else:
        return DEFAULT

def get_thumbnail_option():
    print('Generate thumbnail? (for CBXShell) (Default = No)')
    print('1. Yes')
    print('2. No')

    answer = input()

    if answer == '1':
        return True
    else:
        return False

if __name__ == '__main__':
    # Windows exe support
    multiprocessing.freeze_support()

    options = {
        'lossless': get_lossless_option(),
        'quality': get_quality_option(),
        'thumbnail': get_thumbnail_option()
    }

    # In lossless mode, quality means speed
    if options['lossless']:
        options['quality'] = 100

    # Find zip files
    zip_file_paths = glob.glob('**/*.zip', recursive=True)
    print(zip_file_paths)

    # Process zip files
    for index, zip_file_path in enumerate(zip_file_paths):
        print('Processing', index, '/', len(zip_file_paths))
        try:
            slimify(zip_file_path, options)
        except Exception as err:
            print(f'Error during {zip_file_path}', err)
            with open('errorlog.txt', 'a+') as f:
                f.write(f'Error during {zip_file_path} {err}\n')

    print('Process finished')
    print('Press Enter to exit')
    input()

방주작업할 때 좆같이 느린 쓰레기 변환 프로그램에 고통 받으신 분들 이거 쓰세요

아래 이미지 파일 다운 받아서 jpg를 zip로 확장자 변경한 후 풀어서 압축파일과 실행파일 같이 넣어서 실행하세요

무분별한 사용은 차단될 수 있습니다.
번호 제목 글쓴이 추천 수 날짜 조회 수
180564 [잡담] 태블릿 직구했는데 배송조회에 '사고'래.... 2 ✔_✔ 0 1 시간 전 104
180563 [모바일] ㅠㅠ a90 아직 현역인가? 2 슈퍼커브 0 2 시간 전 60
180562 [정보] 개드립 mp4 재생 안되는 사람 보시오 3 쪼렙고양이 1 7 시간 전 69
180561 [컴퓨터] 메인보드 어떤거 써야되죠? 4 함박눈 0 9 시간 전 107
180560 [프로그래밍] 안드로이드 책 추천좀 집에가게해줘 0 9 시간 전 78
180559 [잡담] 갤럭시 앱 아이콘 숫자? 안뜸ㅠ 4 1q2w3es 0 11 시간 전 88
180558 [컴퓨터] 이런 도킹스테이션 어떰? 6 쿠엥쿠엥 0 11 시간 전 190
180557 [잡담] 허먼밀러 뉴 에어론B 하루체감 5 창원토박이 0 11 시간 전 197
180556 [컴퓨터] 노트북 가지고다니기 vs 미니컴퓨터 가지고다니기 15 황제건 0 12 시간 전 172
180555 [잡담] 아이패드 프로 얼마에 나올거 같음? 5 노보케인 0 13 시간 전 156
180554 [정보] 알리 ssd 2테라 9.6만 7 년차html개발자 0 15 시간 전 344
180553 [잡담] 거북선 -> 레이니로 바꿀까 3 fhana 0 15 시간 전 130
180552 [잡담] 집안에 돌아다니는 물건들 싹 다 파는 중 1 연골어류 0 17 시간 전 203
180551 [모바일] 현시점 아이폰 중고 가성비 뭐가 나음? 12 상한가 1 22 시간 전 388
180550 [프로그래밍] 폰 스크리닝 해 본 사람 있어? 3 무지개빛푸딩 0 23 시간 전 317
180549 [컴퓨터] rx7600 이 가격이면 살 만하죠? 5 죽업 0 23 시간 전 231
180548 [잡담] 사무실에서 사운드바 주워옴 2 빠빠양 0 1 일 전 247
180547 [견적] 더는 못 짜겠어요.. 1 아랫마을공돌이 0 1 일 전 170
180546 [컴퓨터] 컴붕이의 추천에 따라 독거미 키보드 결제함 ㅎㅎ 1 쿠쿠N취킨 0 1 일 전 156
180545 [견적] 두제품간의 Cpu, 내장 그래픽 둘중 뭐가 나을까요 1 소산스님 0 1 일 전 223