Auto Screenshot Scheduler

import os

import json

import time

import threading

from datetime import datetime

from pathlib import Path


try:

    import pyautogui

    PYAUTOGUI_OK = True

except ImportError:

    PYAUTOGUI_OK = False


try:

    from PIL import Image, ImageDraw, ImageFont

    PILLOW_OK = True

except ImportError:

    PILLOW_OK = False


# ============================================================

# CONFIGURATION

# ============================================================


CONFIG_FILE   = "screenshot_config.json"

DEFAULT_DIR   = "screenshots"

DEFAULT_INTERVAL = 60       # seconds between screenshots

DEFAULT_FORMAT   = "png"    # png or jpg

MAX_SCREENSHOTS  = 0        # 0 = unlimited


# ============================================================

# LOAD & SAVE CONFIG

# ============================================================


def load_config():

    if Path(CONFIG_FILE).exists():

        try:

            with open(CONFIG_FILE, "r") as f:

                return json.load(f)

        except:

            pass

    return {

        "save_dir":    DEFAULT_DIR,

        "interval":    DEFAULT_INTERVAL,

        "format":      DEFAULT_FORMAT,

        "max_count":   MAX_SCREENSHOTS,

        "add_timestamp_overlay": False,

        "prefix":      "screenshot",

        "quality":     95

    }



def save_config(config):

    with open(CONFIG_FILE, "w") as f:

        json.dump(config, f, indent=2)

    print("  Config saved.")



# ============================================================

# GENERATE FILENAME

# ============================================================


def generate_filename(save_dir, prefix, fmt):

    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")

    filename  = f"{prefix}_{timestamp}.{fmt}"

    return Path(save_dir) / filename



# ============================================================

# ADD TIMESTAMP OVERLAY TO IMAGE

# ============================================================


def add_timestamp_overlay(img_path):

    """Burn timestamp text into the bottom-right corner of the image."""

    if not PILLOW_OK:

        return


    try:

        img   = Image.open(img_path).convert("RGBA")

        draw  = ImageDraw.Draw(img)

        ts    = datetime.now().strftime("%d-%m-%Y  %H:%M:%S")

        w, h  = img.size


        # Draw background box

        margin  = 10

        box_h   = 28

        box_w   = len(ts) * 8 + margin * 2

        box_x   = w - box_w - margin

        box_y   = h - box_h - margin


        draw.rectangle(

            [box_x - 4, box_y - 4, box_x + box_w, box_y + box_h],

            fill=(0, 0, 0, 160)

        )

        draw.text(

            (box_x, box_y),

            ts,

            fill=(255, 255, 255, 255)

        )


        img = img.convert("RGB")

        img.save(img_path)

    except Exception as e:

        pass   # overlay is optional — don't crash on failure



# ============================================================

# TAKE A SINGLE SCREENSHOT

# ============================================================


def take_screenshot(config):

    """

    Capture the screen and save it.

    Returns (filepath, success).

    """

    save_dir = config["save_dir"]

    prefix   = config.get("prefix", "screenshot")

    fmt      = config.get("format", "png").lower()

    quality  = config.get("quality", 95)

    overlay  = config.get("add_timestamp_overlay", False)


    Path(save_dir).mkdir(parents=True, exist_ok=True)

    filepath = generate_filename(save_dir, prefix, fmt)


    try:

        if not PYAUTOGUI_OK:

            print("  pyautogui not installed — cannot take screenshot.")

            return None, False


        screenshot = pyautogui.screenshot()


        if fmt == "jpg" or fmt == "jpeg":

            screenshot = screenshot.convert("RGB")

            screenshot.save(str(filepath), "JPEG", quality=quality)

        else:

            screenshot.save(str(filepath), "PNG")


        if overlay and PILLOW_OK:

            add_timestamp_overlay(str(filepath))


        size_str = _format_size(filepath.stat().st_size)

        print(f"  Saved: {filepath.name}  ({size_str})")

        return filepath, True


    except Exception as e:

        print(f"  Screenshot error: {e}")

        return None, False



# ============================================================

# SCHEDULER CLASS

# ============================================================


class ScreenshotScheduler:

    def __init__(self, config):

        self.config    = config

        self.running   = False

        self.count     = 0

        self.log       = []

        self._thread   = None


    def _run(self):

        interval  = self.config.get("interval", DEFAULT_INTERVAL)

        max_count = self.config.get("max_count", 0)


        print(f"\n  Scheduler started.")

        print(f"  Interval : every {interval}s")

        print(f"  Save to  : {self.config['save_dir']}")

        print(f"  Max shots: {'unlimited' if max_count == 0 else max_count}")

        print(f"  Press Ctrl+C or use menu to stop.\n")


        self.running = True


        while self.running:

            filepath, ok = take_screenshot(self.config)


            if ok:

                self.count += 1

                self.log.append({

                    "index":    self.count,

                    "file":     str(filepath),

                    "time":     datetime.now().strftime("%d-%m-%Y %H:%M:%S"),

                    "size":     _format_size(filepath.stat().st_size)

                })


            # Stop if max count reached

            if max_count > 0 and self.count >= max_count:

                print(f"\n  Reached max {max_count} screenshots. Stopping.")

                self.running = False

                break


            # Wait interval (in 1s ticks so we can stop cleanly)

            for _ in range(interval):

                if not self.running:

                    break

                time.sleep(1)


    def start(self):

        if self.running:

            print("\n  Scheduler is already running.")

            return

        self._thread = threading.Thread(target=self._run, daemon=True)

        self._thread.start()


    def stop(self):

        self.running = False

        print("\n  Scheduler stopped.")


    def status(self):

        status_str = "RUNNING" if self.running else "STOPPED"

        print(f"\n  Status   : {status_str}")

        print(f"  Captured : {self.count} screenshot(s)")

        print(f"  Save dir : {self.config['save_dir']}")

        if self.log:

            last = self.log[-1]

            print(f"  Last shot: {last['file']} at {last['time']}")


    def show_log(self):

        if not self.log:

            print("\n  No screenshots taken yet.")

            return


        print("\n" + "="*60)

        print(f"  SCREENSHOT LOG  ({len(self.log)} captured)")

        print("="*60)

        print(f"  {'#':<5} {'TIME':<22} {'SIZE':<10} FILE")

        print("  " + "-"*55)


        for entry in self.log[-20:]:   # show last 20

            fname = Path(entry["file"]).name

            print(f"  {entry['index']:<5} {entry['time']:<22} "

                  f"{entry['size']:<10} {fname}")


        if len(self.log) > 20:

            print(f"  ... and {len(self.log) - 20} more.")

        print("="*60)


    def folder_summary(self):

        save_dir = Path(self.config["save_dir"])

        if not save_dir.exists():

            print("\n  Save folder does not exist yet.")

            return


        fmt    = self.config.get("format", "png")

        files  = list(save_dir.glob(f"*.{fmt}")) + list(save_dir.glob(f"*.jpg"))

        total  = sum(f.stat().st_size for f in files)


        print(f"\n  Folder   : {save_dir}")

        print(f"  Files    : {len(files)}")

        print(f"  Total    : {_format_size(total)}")


        if files:

            oldest = min(files, key=lambda f: f.stat().st_mtime)

            newest = max(files, key=lambda f: f.stat().st_mtime)

            print(f"  Oldest   : {oldest.name}")

            print(f"  Newest   : {newest.name}")



# ============================================================

# SETTINGS EDITOR

# ============================================================


def edit_settings(config):

    print("\n" + "="*50)

    print("  SETTINGS")

    print("="*50)

    print(f"  Current settings:")

    print(f"  1. Save directory   : {config['save_dir']}")

    print(f"  2. Interval (secs)  : {config['interval']}")

    print(f"  3. Format           : {config['format']}")

    print(f"  4. Max screenshots  : {config['max_count']} (0=unlimited)")

    print(f"  5. Filename prefix  : {config['prefix']}")

    print(f"  6. JPEG quality     : {config['quality']} (1-100)")

    print(f"  7. Timestamp overlay: {config['add_timestamp_overlay']}")

    print(f"  8. Save & return")

    print("="*50)


    while True:

        sub = input("\n  Edit setting (1-8): ").strip()


        if sub == "1":

            val = input(f"  Save directory [{config['save_dir']}]: ").strip()

            if val:

                config["save_dir"] = val


        elif sub == "2":

            val = input(f"  Interval in seconds [{config['interval']}]: ").strip()

            if val.isdigit():

                config["interval"] = max(1, int(val))


        elif sub == "3":

            val = input("  Format (png/jpg) [png]: ").strip().lower()

            if val in ["png", "jpg", "jpeg"]:

                config["format"] = val


        elif sub == "4":

            val = input(f"  Max screenshots (0=unlimited) [{config['max_count']}]: ").strip()

            if val.isdigit():

                config["max_count"] = int(val)


        elif sub == "5":

            val = input(f"  Prefix [{config['prefix']}]: ").strip()

            if val:

                config["prefix"] = val


        elif sub == "6":

            val = input(f"  JPEG quality 1-100 [{config['quality']}]: ").strip()

            if val.isdigit():

                config["quality"] = max(1, min(100, int(val)))


        elif sub == "7":

            val = input("  Timestamp overlay (y/n): ").strip().lower()

            config["add_timestamp_overlay"] = val == "y"


        elif sub == "8":

            save_config(config)

            break


        else:

            print("  Invalid option.")


    return config



# ============================================================

# HELPER

# ============================================================


def _format_size(size_bytes):

    for unit in ["B", "KB", "MB", "GB"]:

        if size_bytes < 1024:

            return f"{size_bytes:.1f} {unit}"

        size_bytes /= 1024

    return f"{size_bytes:.1f} GB"



# ============================================================

# DEPENDENCY CHECK

# ============================================================


def check_deps():

    print("\n  Dependency check:")

    print(f"  pyautogui : {'OK' if PYAUTOGUI_OK else 'MISSING  ->  pip install pyautogui'}")

    print(f"  Pillow    : {'OK' if PILLOW_OK    else 'MISSING  ->  pip install Pillow'}")

    if not PYAUTOGUI_OK:

        print("\n  pyautogui is required to take screenshots.")

        return False

    return True



# ============================================================

# MAIN MENU

# ============================================================


def print_menu(scheduler):

    status = "RUNNING" if scheduler.running else "STOPPED"

    count  = scheduler.count

    print("\n" + "-"*48)

    print(f"  AUTO SCREENSHOT SCHEDULER  [{status}] [{count} taken]")

    print("-"*48)

    print("  1. Start scheduler")

    print("  2. Stop scheduler")

    print("  3. Take one screenshot NOW")

    print("  4. View screenshot log")

    print("  5. Folder summary")

    print("  6. Settings")

    print("  7. Check dependencies")

    print("  0. Exit")

    print("-"*48)



def main():

    print("\n" + "="*55)

    print("     AUTO SCREENSHOT SCHEDULER")

    print("="*55)


    config    = load_config()

    scheduler = ScreenshotScheduler(config)


    print(f"\n  Save dir : {config['save_dir']}")

    print(f"  Interval : every {config['interval']}s")

    print(f"  Format   : {config['format'].upper()}")


    while True:

        print_menu(scheduler)

        choice = input("  > ").strip()


        if choice == "1":

            if not PYAUTOGUI_OK:

                print("\n  pyautogui not installed. Run option 7 for install help.")

            else:

                scheduler.config = config

                scheduler.start()


        elif choice == "2":

            scheduler.stop()


        elif choice == "3":

            if not PYAUTOGUI_OK:

                print("\n  pyautogui not installed. Run option 7 for install help.")

            else:

                take_screenshot(config)


        elif choice == "4":

            scheduler.show_log()


        elif choice == "5":

            scheduler.folder_summary()


        elif choice == "6":

            config = edit_settings(config)

            scheduler.config = config


        elif choice == "7":

            check_deps()


        elif choice == "0":

            scheduler.stop()

            scheduler.status()

            print("\n  Goodbye!\n")

            break


        else:

            print("  Invalid choice.")



# ============================================================

# RUN

# ============================================================


if __name__ == "__main__":

    main()

No comments: