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:
Post a Comment