Skip to content

Instantly share code, notes, and snippets.

@C0ntroller
Last active July 7, 2023 16:31
Show Gist options
  • Select an option

  • Save C0ntroller/d716837c440f4807c83e6970d461a4a4 to your computer and use it in GitHub Desktop.

Select an option

Save C0ntroller/d716837c440f4807c83e6970d461a4a4 to your computer and use it in GitHub Desktop.
Download and EXIF-tag Snpachat memories
# 1. Request Snapchat data archive here: https://accounts.snapchat.com/accounts/downloadmydata
# - DO NOT enable "Include your Memories and other saved Media"!
# 2. Wait for your archive to be ready and download it
# 3. Search for "memories_history.json" in the "json" directory in the archive
# 4. Put it right next to this script
# 5. Run the script
# - Be sure to have all requirements installed. See below for the pip command you need to run.
# 6. Output is stored in "memories" directory next to the script
# Because of how exif works, we can't just save a timezone.
# So we have to use the local timezone of the machine this script is running on.
# This is not the best solution, but it works for me and usually img are at most 1 hour of because of sommer time.
# Images from vacations etc. can be wrong multiple hours...
# pip install requests exif python-dateutil
import requests
from exif import Image, DATETIME_STR_FORMAT
from dateutil.parser import parse as parse_datetime
from dateutil.tz import tzlocal
from json import load as json_load
from typing import Tuple, Union
from os import mkdir, path
from re import search as regex_search
def setup_paths(base: str):
"""
Creates the memories folder if it doesnt exist
"""
memories_path = path.join(base, "memories")
if not path.exists(memories_path):
mkdir(memories_path)
def get_memories(path: str) -> dict:
"""
Loads the JSON file into a dict
"""
with open(path) as f:
return json_load(f)
def grad_dec_to_minute_second(grad_dec: float) -> Tuple[int, int, float]:
"""
Converts a decimal grad value to a tuple of (grad, minute, second)
"""
grad = int(grad_dec)
minute = int((grad_dec - grad) * 60)
second = ((grad_dec - grad) * 60 - minute) * 60
return (grad, minute, second)
def get_location_triples(location: Union[None, str]) -> Union[None, Tuple[Tuple[int, int, float], Tuple[int, int, float]]]:
"""
Converts a location string to a tuple of (grad, minute, second) for long- and latitude
"""
if location is None:
return None
# format: "Latitude, Longitude: %grad_decimal%, %grad_decimal%"
splitted = location.split(": ")[1].split(", ")
return [tuple(grad_dec_to_minute_second(float(x))) for x in splitted]
def main():
# Get script path
this_path = path.dirname(path.realpath(__file__))
# Create the memories folder
setup_paths(this_path)
# Load JSON
memories = get_memories(f"{this_path}/memories_history.json")
for x in memories["Saved Media"]:
# UTC date of the memory
date = parse_datetime(x["Date"]).astimezone(tzlocal())
# Location of the memory as (Grad, Minute, Second) for long- and latitude
location = get_location_triples(x.get("Location", None))
# Download URL
url = x["Download Link"]
# Splitting the URL because we are gonna POST the data, not GET it
split_url = url.split("?")
r = requests.post(
split_url[0],
headers={"Content-type": "application/x-www-form-urlencoded"},
data=split_url[1],
)
if r.status_code != 200:
print("Snapchat ", r.status_code)
continue
# We get an download URL from AWS back
aws_url = r.text
# filename is set to yyyy-mm-dd hh-mm-ss.ext
# original uuid filename: re.search("[a-f0-9\-]*\.(jpe?g|mp4)", aws_url).group(0)
filename = date.strftime("%Y-%m-%d %H-%M-%S")
filename += "." + regex_search("[a-f0-9\-]*\.(jpe?g|mp4)", aws_url).group(1)
# Finally doanload the file
aws_resp = requests.get(aws_url)
if aws_resp.status_code != 200:
print("AWS ", r.status_code)
continue
# For images we add exif data
if x["Media Type"].lower() == "image":
img = Image(aws_resp.content)
img.datetime_original = date.strftime(DATETIME_STR_FORMAT)
img.datetime_scanned = date.strftime(DATETIME_STR_FORMAT)
img.datetime_digitized = date.strftime(DATETIME_STR_FORMAT)
if location != None:
img.gps_latitude = location[0]
img.gps_latitude_ref = "N"
img.gps_longitude = location[1]
img.gps_longitude_ref = "E"
# Save the image
with open(f"{this_path}/memories/{filename}", "wb") as f:
f.write(img.get_file())
# For videos we cant just embed exif data, so we just save the file
else:
with open(f"{this_path}/memories/{filename}", "wb") as f:
f.write(aws_resp.content)
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment