142 lines
4.3 KiB
Python
142 lines
4.3 KiB
Python
import win32com.client
|
|
import os
|
|
import pathlib
|
|
|
|
# --- CONFIGURATION ---
|
|
pst_path = r'd:\Dropbox\!!!Days\Downloads Z230\PST\tkulhava.pst'
|
|
output_dir = r'd:\Dropbox\!!!Days\Downloads Z230\PST\pictures'
|
|
|
|
# Image extensions to look for (case insensitive)
|
|
IMAGE_EXTENSIONS = {'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tif', '.tiff'}
|
|
|
|
|
|
def fix_encoding(text):
|
|
"""Repairs text wrongly decoded as cp1252 instead of cp1250."""
|
|
if not text: return ""
|
|
try:
|
|
return text.encode('cp1252').decode('cp1250')
|
|
except Exception:
|
|
return text
|
|
|
|
|
|
def get_unique_filepath(directory, filename):
|
|
"""
|
|
Checks if a file exists. If so, adds a counter (_1, _2) to the filename
|
|
until a unique name is found.
|
|
"""
|
|
# Clean filename of illegal characters just in case
|
|
filename = "".join(x for x in filename if x.isalnum() or x in "._- ")
|
|
|
|
path = pathlib.Path(directory) / filename
|
|
if not path.exists():
|
|
return path
|
|
|
|
# Split name and extension
|
|
stem = path.stem
|
|
suffix = path.suffix
|
|
counter = 1
|
|
|
|
while True:
|
|
new_filename = f"{stem}_{counter}{suffix}"
|
|
new_path = pathlib.Path(directory) / new_filename
|
|
if not new_path.exists():
|
|
return new_path
|
|
counter += 1
|
|
|
|
|
|
def process_item_attachments(item, save_folder):
|
|
"""Checks an item for attachments and saves pictures."""
|
|
try:
|
|
# Check if item has attachments
|
|
if item.Attachments.Count > 0:
|
|
for attachment in item.Attachments:
|
|
try:
|
|
# Get filename and extension
|
|
fname = getattr(attachment, 'FileName', '')
|
|
if not fname: continue
|
|
|
|
# Fix encoding on filename if needed (sometimes attachments inherit bad encoding)
|
|
fname = fix_encoding(fname)
|
|
|
|
ext = os.path.splitext(fname)[1].lower()
|
|
|
|
if ext in IMAGE_EXTENSIONS:
|
|
# Determine unique path
|
|
save_path = get_unique_filepath(save_folder, fname)
|
|
|
|
# Save the file
|
|
attachment.SaveAsFile(str(save_path))
|
|
print(f" [SAVED] {save_path.name}")
|
|
except Exception as e:
|
|
print(f" [ERROR saving attachment]: {e}")
|
|
except Exception:
|
|
# Some items (like corrupted notes) fail when accessing .Attachments
|
|
pass
|
|
|
|
|
|
def scan_folder_recursively(folder, save_folder):
|
|
"""Recursively walks folders and processes items."""
|
|
try:
|
|
folder_name = fix_encoding(folder.Name)
|
|
|
|
# Optional: Print folder progress
|
|
if folder.Items.Count > 0:
|
|
print(f"Scanning Folder: {folder_name}...")
|
|
|
|
# Process items in this folder
|
|
for item in folder.Items:
|
|
process_item_attachments(item, save_folder)
|
|
|
|
# Recursion
|
|
for subfolder in folder.Folders:
|
|
scan_folder_recursively(subfolder, save_folder)
|
|
|
|
except Exception as e:
|
|
print(f"Skipping folder '{fix_encoding(folder.Name)}': {e}")
|
|
|
|
|
|
def main():
|
|
# 1. Ensure output directory exists
|
|
if not os.path.exists(output_dir):
|
|
os.makedirs(output_dir)
|
|
print(f"Created directory: {output_dir}")
|
|
|
|
if not os.path.exists(pst_path):
|
|
print(f"Error: PST file not found at {pst_path}")
|
|
return
|
|
|
|
try:
|
|
# 2. Connect to Outlook
|
|
outlook = win32com.client.Dispatch("Outlook.Application").GetNamespace("MAPI")
|
|
|
|
print(f"Mounting PST: {pst_path}...")
|
|
outlook.AddStore(pst_path)
|
|
|
|
# 3. Find the PST folder
|
|
pst_name = "tkulhava" # Usually derived from filename
|
|
root_folder = None
|
|
for folder in outlook.Folders:
|
|
if pst_name.lower() in folder.Name.lower():
|
|
root_folder = folder
|
|
break
|
|
|
|
if not root_folder:
|
|
root_folder = outlook.Folders.GetLast()
|
|
|
|
print(f"Opened: {fix_encoding(root_folder.Name)}")
|
|
print(f"Saving pictures to: {output_dir}")
|
|
print("=" * 50)
|
|
|
|
# 4. Start processing
|
|
scan_folder_recursively(root_folder, output_dir)
|
|
|
|
# 5. Cleanup
|
|
outlook.RemoveStore(root_folder)
|
|
print("\nDone. PST detached.")
|
|
|
|
except Exception as e:
|
|
print(f"Critical Error: {e}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |