Skip to content

Instantly share code, notes, and snippets.

@mitcdh
Created March 28, 2025 10:17
Show Gist options
  • Save mitcdh/fb90d26556c65c2ffae02d68e8eed746 to your computer and use it in GitHub Desktop.
Save mitcdh/fb90d26556c65c2ffae02d68e8eed746 to your computer and use it in GitHub Desktop.
Script to delete photos from a specific camera before a specific date (reprocessed raws)
import flickrapi
import json
import time
import os
from datetime import datetime
# Get API credentials from environment variables
API_KEY = os.environ.get('FLICKR_API_KEY')
API_SECRET = os.environ.get('FLICKR_API_SECRET')
# The camera model to search for
TARGET_CAMERA_MODEL = 'Olympus TG-4' # Replace with your target camera
# Date threshold - photos uploaded before this date will be considered for deletion
# Format: 'YYYY-MM-DD'
DATE_THRESHOLD = '2024-03-01'
# Convert the date threshold to a timestamp
date_threshold_timestamp = int(datetime.strptime(DATE_THRESHOLD, '%Y-%m-%d').timestamp())
# List of album IDs to search through
ALBUM_IDS = [
'72177720313836905',
'72177720313869418'
]
def authenticate():
"""Authenticate with Flickr API and return the API object"""
flickr = flickrapi.FlickrAPI(API_KEY, API_SECRET, format='parsed-json')
# Only do this if we need to authenticate
if not flickr.token_valid(perms='delete'):
# Get a request token
flickr.get_request_token(oauth_callback='oob')
# Open a browser at the authentication URL
authorize_url = flickr.auth_url(perms='delete')
print(f"Open this URL in your browser: {authorize_url}")
# Get the verification code from the user
verifier = input('Verification code: ')
# Trade the request token for an access token
flickr.get_access_token(verifier)
return flickr
def get_photos_in_album(flickr, album_id):
"""Get all photos in a specific album"""
try:
photos = []
page = 1
total_pages = 1
while page <= total_pages:
response = flickr.photosets.getPhotos(
photoset_id=album_id,
page=page,
per_page=500 # Maximum allowed by Flickr
)
if page == 1:
total_pages = response['photoset']['pages']
photos.extend(response['photoset']['photo'])
page += 1
return photos
except Exception as e:
print(f"Error getting photos from album {album_id}: {e}")
return []
def get_photo_info(flickr, photo_id):
"""Get detailed information about a specific photo"""
try:
return flickr.photos.getInfo(photo_id=photo_id)
except Exception as e:
print(f"Error getting info for photo {photo_id}: {e}")
return None
def get_photo_exif(flickr, photo_id):
"""Get EXIF data for a specific photo"""
try:
return flickr.photos.getExif(photo_id=photo_id)
except Exception as e:
# Some photos might not have EXIF data
print(f"Error getting EXIF data for photo {photo_id}: {e}")
return None
def get_camera_model(flickr, photo_id):
"""Get the camera model from EXIF data"""
exif_data = get_photo_exif(flickr, photo_id)
if not exif_data or 'photo' not in exif_data:
return None
# First check if camera is directly in the response
if 'camera' in exif_data['photo'] and exif_data['photo']['camera']:
return exif_data['photo']['camera']
# If not, look in the EXIF tags
if 'exif' in exif_data['photo']:
for exif_tag in exif_data['photo']['exif']:
if exif_tag.get('tag') == 'Model':
make = None
# Try to find the Make tag as well
for make_tag in exif_data['photo']['exif']:
if make_tag.get('tag') == 'Make':
make = make_tag.get('raw', {}).get('_content')
break
model = exif_tag.get('raw', {}).get('_content')
# If we have both make and model, combine them
if make and model and not model.startswith(make):
return f"{make} {model}"
return model
return None
def delete_photo(flickr, photo_id):
"""Delete a specific photo"""
try:
result = flickr.photos.delete(photo_id=photo_id)
print(f"Successfully deleted photo {photo_id}")
return True
except Exception as e:
print(f"Error deleting photo {photo_id}: {e}")
return False
def format_date(timestamp):
"""Convert a Unix timestamp to a human-readable date"""
return datetime.fromtimestamp(int(timestamp)).strftime('%Y-%m-%d %H:%M:%S')
def main():
# Authenticate with Flickr
flickr = authenticate()
# Keep track of photos to delete
photos_to_delete = []
# Process each album
for album_id in ALBUM_IDS:
print(f"Processing album {album_id}...")
# Get all photos in the album
photos = get_photos_in_album(flickr, album_id)
print(f"Found {len(photos)} photos in album {album_id}")
# Check each photo for the target camera model and upload date
for photo in photos:
photo_id = photo['id']
photo_info = get_photo_info(flickr, photo_id)
if photo_info and 'photo' in photo_info:
# Get photo title
title = photo_info['photo'].get('title', {}).get('_content', 'Untitled')
# Check upload date
date_match = False
if 'dateuploaded' in photo_info['photo']:
upload_timestamp = int(photo_info['photo']['dateuploaded'])
upload_date = format_date(upload_timestamp)
if upload_timestamp < date_threshold_timestamp:
date_match = True
# Check camera model
camera_model = get_camera_model(flickr, photo_id)
camera_match = False
if camera_model:
# For logging purposes
print(f"Photo '{title}' (ID: {photo_id}) - Camera: {camera_model}")
# Check if it matches our target camera
if camera_model == TARGET_CAMERA_MODEL:
camera_match = True
# If both conditions match, add to deletion list
if camera_match and date_match:
print(f"MATCH: Photo '{title}' (ID: {photo_id}) taken with {camera_model}, uploaded on {upload_date}")
photos_to_delete.append((photo_id, title, upload_date))
# Be nice to Flickr API - don't make too many requests too quickly
time.sleep(0.5)
# Confirm deletion
if photos_to_delete:
print(f"\nFound {len(photos_to_delete)} photos taken with {TARGET_CAMERA_MODEL} uploaded before {DATE_THRESHOLD}")
print("\nPhotos to be deleted:")
for idx, (photo_id, title, upload_date) in enumerate(photos_to_delete, 1):
print(f"{idx}. '{title}' (ID: {photo_id}) - Uploaded: {upload_date}")
confirm = input("\nDo you want to delete these photos? (yes/no): ")
if confirm.lower() == 'yes':
deleted_count = 0
for photo_id, _, _ in photos_to_delete:
if delete_photo(flickr, photo_id):
deleted_count += 1
# Be nice to Flickr API
time.sleep(1)
print(f"Deleted {deleted_count} photos")
else:
print("Deletion cancelled")
else:
print(f"No photos found taken with {TARGET_CAMERA_MODEL} uploaded before {DATE_THRESHOLD}")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment