Created
November 11, 2024 16:23
-
-
Save u-haru/afaf3c2b0ec70420e2f94fef090f5c30 to your computer and use it in GitHub Desktop.
WindowsのExplorerのサムネイルを再帰的に取得するpythonスクリプト
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import argparse | |
import os | |
import ctypes | |
import traceback | |
import comtypes | |
from ctypes import wintypes | |
import glob | |
import concurrent.futures | |
import tqdm | |
OLE32 = ctypes.OleDLL('ole32') | |
SHELL32 = ctypes.WinDLL('shell32') | |
IID_IShellItem = comtypes.GUID('{43826D1E-E718-42EE-BC55-A1E261C37BFE}') | |
SIZE = wintypes.SIZE(256, 256) | |
class IShellItemImageFactory(comtypes.IUnknown): | |
_iid_ = comtypes.GUID('{bcc18b79-ba16-442f-80c4-8a59c30c463b}') | |
_methods_ = [ | |
comtypes.STDMETHOD( | |
ctypes.HRESULT, | |
"GetImage", | |
[ | |
wintypes.SIZE, # size | |
wintypes.DWORD, # flags | |
ctypes.POINTER(wintypes.HANDLE) # phbmp | |
] | |
) | |
] | |
def cache_thumbnail(file_path: str): | |
shell_item = ctypes.c_void_p() | |
hr = SHELL32.SHCreateItemFromParsingName( | |
file_path, | |
None, | |
IID_IShellItem, | |
ctypes.byref(shell_item) | |
) | |
if hr != 0: | |
raise ctypes.WinError(hr) | |
shell_item = comtypes.cast(shell_item, comtypes.POINTER(IShellItemImageFactory)) | |
shell_item_image_factory = shell_item.QueryInterface(IShellItemImageFactory) # type: ignore | |
hBitmap = wintypes.HANDLE() | |
hr = shell_item_image_factory.GetImage(SIZE, 0, ctypes.byref(hBitmap)) | |
if hr != 0: | |
raise ctypes.WinError(hr) | |
def cache_thumbnail_com(file_path: str): | |
OLE32.CoInitialize(None) | |
try: | |
cache_thumbnail(file_path) | |
except Exception: | |
tqdm.tqdm.write(traceback.format_exc()) | |
OLE32.CoUninitialize() | |
def generate_thumbnails_in_folder(folder_paths: list[str], recursive=True, threads=4): | |
files = [ | |
os.path.abspath(file_path) | |
for folder_path in folder_paths | |
for file_path in glob.glob(os.path.join(folder_path, '**', '*.*'), recursive=recursive) | |
if os.path.isfile(file_path) | |
] | |
with concurrent.futures.ProcessPoolExecutor(max_workers=threads) as executor: | |
try: | |
for _ in tqdm.tqdm(executor.map(cache_thumbnail_com, files), total=len(files)): | |
pass | |
except KeyboardInterrupt: | |
print("Interrupted") | |
executor.shutdown(wait=True, cancel_futures=True) | |
if __name__ == '__main__': | |
parser = argparse.ArgumentParser(description='Cache thumbnails for files in a directory') | |
parser.add_argument("target_dir", nargs="+", help="Target directory", default=[]) | |
parser.add_argument("-t", "--threads", type=int, default=8, help="Number of threads") | |
args = parser.parse_args() | |
generate_thumbnails_in_folder(args.target_dir, threads=args.threads) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment