snapchat-memories-downloader/notebooks/make_df.ipynb

1921 lines
86 KiB
Plaintext

{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "ffcab11d",
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "a4cda272",
"metadata": {},
"outputs": [],
"source": [
"df: pd.DataFrame = pd.read_json(\"../temp/file_info.json\", orient='records')"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "09ee7182",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Date</th>\n",
" <th>Media Type</th>\n",
" <th>Location</th>\n",
" <th>Download Link</th>\n",
" <th>Media Download Url</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2025-11-29 04:50:47+00:00</td>\n",
" <td>Video</td>\n",
" <td>Latitude, Longitude: 44.110836, -94.217804</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2025-11-29 04:49:56+00:00</td>\n",
" <td>Video</td>\n",
" <td>Latitude, Longitude: 44.110767, -94.217705</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2025-11-26 14:23:27+00:00</td>\n",
" <td>Video</td>\n",
" <td>Latitude, Longitude: 44.354908, -93.96698</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2025-11-26 14:18:45+00:00</td>\n",
" <td>Video</td>\n",
" <td>Latitude, Longitude: 44.355003, -93.96696</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2025-11-16 17:58:59+00:00</td>\n",
" <td>Image</td>\n",
" <td>Latitude, Longitude: 46.184643, -94.363495</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>5</th>\n",
" <td>2025-11-12 02:10:34+00:00</td>\n",
" <td>Video</td>\n",
" <td>Latitude, Longitude: 44.354897, -93.96707</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>6</th>\n",
" <td>2025-11-07 04:43:52+00:00</td>\n",
" <td>Video</td>\n",
" <td>Latitude, Longitude: 44.354797, -93.96702</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>7</th>\n",
" <td>2025-10-27 21:50:11+00:00</td>\n",
" <td>Video</td>\n",
" <td>Latitude, Longitude: 44.354794, -93.967026</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>8</th>\n",
" <td>2025-10-16 06:39:29+00:00</td>\n",
" <td>Video</td>\n",
" <td>Latitude, Longitude: 40.4103, -104.70806</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" </tr>\n",
" <tr>\n",
" <th>9</th>\n",
" <td>2025-10-14 20:46:28+00:00</td>\n",
" <td>Video</td>\n",
" <td>Latitude, Longitude: 40.432137, -104.76975</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Date Media Type \\\n",
"0 2025-11-29 04:50:47+00:00 Video \n",
"1 2025-11-29 04:49:56+00:00 Video \n",
"2 2025-11-26 14:23:27+00:00 Video \n",
"3 2025-11-26 14:18:45+00:00 Video \n",
"4 2025-11-16 17:58:59+00:00 Image \n",
"5 2025-11-12 02:10:34+00:00 Video \n",
"6 2025-11-07 04:43:52+00:00 Video \n",
"7 2025-10-27 21:50:11+00:00 Video \n",
"8 2025-10-16 06:39:29+00:00 Video \n",
"9 2025-10-14 20:46:28+00:00 Video \n",
"\n",
" Location \\\n",
"0 Latitude, Longitude: 44.110836, -94.217804 \n",
"1 Latitude, Longitude: 44.110767, -94.217705 \n",
"2 Latitude, Longitude: 44.354908, -93.96698 \n",
"3 Latitude, Longitude: 44.355003, -93.96696 \n",
"4 Latitude, Longitude: 46.184643, -94.363495 \n",
"5 Latitude, Longitude: 44.354897, -93.96707 \n",
"6 Latitude, Longitude: 44.354797, -93.96702 \n",
"7 Latitude, Longitude: 44.354794, -93.967026 \n",
"8 Latitude, Longitude: 40.4103, -104.70806 \n",
"9 Latitude, Longitude: 40.432137, -104.76975 \n",
"\n",
" Download Link \\\n",
"0 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"1 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"2 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"3 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"4 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"5 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"6 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"7 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"8 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"9 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"\n",
" Media Download Url \n",
"0 https://us-east1-aws.api.snapchat.com/dmd/mm?u... \n",
"1 https://us-east1-aws.api.snapchat.com/dmd/mm?u... \n",
"2 https://us-east1-aws.api.snapchat.com/dmd/mm?u... \n",
"3 https://us-east1-aws.api.snapchat.com/dmd/mm?u... \n",
"4 https://us-east1-aws.api.snapchat.com/dmd/mm?u... \n",
"5 https://us-east1-aws.api.snapchat.com/dmd/mm?u... \n",
"6 https://us-east1-aws.api.snapchat.com/dmd/mm?u... \n",
"7 https://us-east1-aws.api.snapchat.com/dmd/mm?u... \n",
"8 https://us-east1-aws.api.snapchat.com/dmd/mm?u... \n",
"9 https://us-east1-aws.api.snapchat.com/dmd/mm?u... "
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df.head(10)"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "d8fb8fd5",
"metadata": {},
"outputs": [],
"source": [
"def get_join_col(row: pd.Series) -> pd.Series:\n",
" row['file_dt'] = f\"{row['Date'].strftime('%Y-%m-%d_%H-%M-%S')}\"\n",
" return row"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "71007845",
"metadata": {},
"outputs": [],
"source": [
"def extract_lat_long(row: pd.Series) -> pd.Series:\n",
" coordinates = row[\"Location\"].split(\": \")[1]\n",
" lat_str, long_str = coordinates.split(\", \")\n",
" row[\"location_latitude\"] = round(float(lat_str), 5)\n",
" row[\"location_longitude\"] = round(float(long_str), 5)\n",
" return row"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "68ed283f",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>Date</th>\n",
" <th>Media Type</th>\n",
" <th>Location</th>\n",
" <th>Download Link</th>\n",
" <th>Media Download Url</th>\n",
" <th>file_dt</th>\n",
" <th>location_latitude</th>\n",
" <th>location_longitude</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>2025-11-29 04:50:47+00:00</td>\n",
" <td>Video</td>\n",
" <td>Latitude, Longitude: 44.110836, -94.217804</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" <td>2025-11-29_04-50-47</td>\n",
" <td>44.11084</td>\n",
" <td>-94.21780</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>2025-11-29 04:49:56+00:00</td>\n",
" <td>Video</td>\n",
" <td>Latitude, Longitude: 44.110767, -94.217705</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" <td>2025-11-29_04-49-56</td>\n",
" <td>44.11077</td>\n",
" <td>-94.21770</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>2025-11-26 14:23:27+00:00</td>\n",
" <td>Video</td>\n",
" <td>Latitude, Longitude: 44.354908, -93.96698</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" <td>2025-11-26_14-23-27</td>\n",
" <td>44.35491</td>\n",
" <td>-93.96698</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>2025-11-26 14:18:45+00:00</td>\n",
" <td>Video</td>\n",
" <td>Latitude, Longitude: 44.355003, -93.96696</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" <td>2025-11-26_14-18-45</td>\n",
" <td>44.35500</td>\n",
" <td>-93.96696</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>2025-11-16 17:58:59+00:00</td>\n",
" <td>Image</td>\n",
" <td>Latitude, Longitude: 46.184643, -94.363495</td>\n",
" <td>https://app.snapchat.com/dmd/memories?uid=098c...</td>\n",
" <td>https://us-east1-aws.api.snapchat.com/dmd/mm?u...</td>\n",
" <td>2025-11-16_17-58-59</td>\n",
" <td>46.18464</td>\n",
" <td>-94.36350</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" Date Media Type \\\n",
"0 2025-11-29 04:50:47+00:00 Video \n",
"1 2025-11-29 04:49:56+00:00 Video \n",
"2 2025-11-26 14:23:27+00:00 Video \n",
"3 2025-11-26 14:18:45+00:00 Video \n",
"4 2025-11-16 17:58:59+00:00 Image \n",
"\n",
" Location \\\n",
"0 Latitude, Longitude: 44.110836, -94.217804 \n",
"1 Latitude, Longitude: 44.110767, -94.217705 \n",
"2 Latitude, Longitude: 44.354908, -93.96698 \n",
"3 Latitude, Longitude: 44.355003, -93.96696 \n",
"4 Latitude, Longitude: 46.184643, -94.363495 \n",
"\n",
" Download Link \\\n",
"0 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"1 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"2 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"3 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"4 https://app.snapchat.com/dmd/memories?uid=098c... \n",
"\n",
" Media Download Url file_dt \\\n",
"0 https://us-east1-aws.api.snapchat.com/dmd/mm?u... 2025-11-29_04-50-47 \n",
"1 https://us-east1-aws.api.snapchat.com/dmd/mm?u... 2025-11-29_04-49-56 \n",
"2 https://us-east1-aws.api.snapchat.com/dmd/mm?u... 2025-11-26_14-23-27 \n",
"3 https://us-east1-aws.api.snapchat.com/dmd/mm?u... 2025-11-26_14-18-45 \n",
"4 https://us-east1-aws.api.snapchat.com/dmd/mm?u... 2025-11-16_17-58-59 \n",
"\n",
" location_latitude location_longitude \n",
"0 44.11084 -94.21780 \n",
"1 44.11077 -94.21770 \n",
"2 44.35491 -93.96698 \n",
"3 44.35500 -93.96696 \n",
"4 46.18464 -94.36350 "
]
},
"execution_count": 6,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"df = df.apply(get_join_col, axis=1)\n",
"df = df.apply(extract_lat_long, axis=1)\n",
"df.head(5)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "713d3202",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>file</th>\n",
" <th>createdate</th>\n",
" <th>gpslatitude</th>\n",
" <th>gpslongitude</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>../downloads/2019-04-05_23-27-17.jpg</td>\n",
" <td>2019:04:05 18:27:17</td>\n",
" <td>40 deg 24' 14.76\" N</td>\n",
" <td>104 deg 44' 21.52\" W</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>../downloads/2024-10-23_00-29-09.jpg</td>\n",
" <td>2024:10:22 19:29:09</td>\n",
" <td>44 deg 6' 15.77\" N</td>\n",
" <td>94 deg 13' 10.52\" W</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>../downloads/2025-02-16_00-09-41.mp4</td>\n",
" <td>2025:02:16 00:09:41</td>\n",
" <td>44 deg 21' 17.71\" N</td>\n",
" <td>93 deg 58' 1.20\" W</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>../downloads/2023-03-10_22-11-18.jpg</td>\n",
" <td>2023:03:10 16:11:18</td>\n",
" <td>40 deg 24' 39.13\" N</td>\n",
" <td>104 deg 41' 33.90\" W</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>../downloads/2024-02-24_22-18-07.jpg</td>\n",
" <td>2024:02:24 16:18:07</td>\n",
" <td>40 deg 25' 17.08\" N</td>\n",
" <td>104 deg 45' 22.75\" W</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" file createdate \\\n",
"0 ../downloads/2019-04-05_23-27-17.jpg 2019:04:05 18:27:17 \n",
"1 ../downloads/2024-10-23_00-29-09.jpg 2024:10:22 19:29:09 \n",
"2 ../downloads/2025-02-16_00-09-41.mp4 2025:02:16 00:09:41 \n",
"3 ../downloads/2023-03-10_22-11-18.jpg 2023:03:10 16:11:18 \n",
"4 ../downloads/2024-02-24_22-18-07.jpg 2024:02:24 16:18:07 \n",
"\n",
" gpslatitude gpslongitude \n",
"0 40 deg 24' 14.76\" N 104 deg 44' 21.52\" W \n",
"1 44 deg 6' 15.77\" N 94 deg 13' 10.52\" W \n",
"2 44 deg 21' 17.71\" N 93 deg 58' 1.20\" W \n",
"3 40 deg 24' 39.13\" N 104 deg 41' 33.90\" W \n",
"4 40 deg 25' 17.08\" N 104 deg 45' 22.75\" W "
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"metadata = pd.read_csv(\"../temp/filemetadata.csv\", sep=\"#\")\n",
"metadata.head(5)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "cb28dcd6",
"metadata": {},
"outputs": [],
"source": [
"import re\n",
"\n",
"def dms_to_decimal(dms_string):\n",
" \"\"\"\n",
" Converts a geographical coordinate string in Degrees, Minutes, Seconds (DMS)\n",
" format (e.g., \"44 deg 6' 15.76\" N\") to a decimal degree coordinate.\n",
"\n",
" Args:\n",
" dms_string (str): The DMS coordinate string.\n",
"\n",
" Returns:\n",
" float: The decimal degree equivalent.\n",
" \n",
" Raises:\n",
" ValueError: If the string format is not recognized.\n",
" \"\"\"\n",
" # Regex to capture Degrees, Minutes, Seconds, and Direction\n",
" # Pattern: (\\d+) deg (\\d+)' ([\\d.]+)\" ([NSEW])\n",
" # The pattern is flexible with leading zeros, extra spaces, and decimal seconds.\n",
" match = re.search(r\"(\\d+)\\s*deg\\s*(\\d+)'\\s*([\\d.]+)\\\"\\s*([NSEW])\", dms_string, re.IGNORECASE)\n",
"\n",
" if not match:\n",
" raise ValueError(f\"Coordinate format not recognized: {dms_string}\")\n",
"\n",
" # Extract the parts, ensuring they are converted to appropriate numerical types\n",
" degrees = float(match.group(1))\n",
" minutes = float(match.group(2))\n",
" seconds = float(match.group(3))\n",
" direction = match.group(4).upper()\n",
"\n",
" # Calculate the decimal degree value\n",
" decimal_degrees = degrees + (minutes / 60) + (seconds / 3600)\n",
"\n",
" # Apply the sign based on the direction (S and W are negative)\n",
" if direction in ['S', 'W']:\n",
" decimal_degrees *= -1\n",
" \n",
" decimal_degrees = round(decimal_degrees, 5)\n",
"\n",
" return decimal_degrees"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "97ee9100",
"metadata": {},
"outputs": [],
"source": [
"def convert_lat_long_to_decimal_degrees(row: pd.Series) -> pd.Series:\n",
" # def convert(value: str) -> float:\n",
" # if pd.isna(value):\n",
" # return None\n",
" # if value == '-':\n",
" # return 0.0\n",
" # parts = value.strip().split(' ')\n",
" # degrees = float(parts[0])\n",
" # minutes = float(parts[1]) / 60\n",
" # seconds = float(parts[2]) / 3600\n",
" # direction = parts[3]\n",
" # decimal_degrees = degrees + minutes + seconds\n",
" # if direction in ['S', 'W']:\n",
" # decimal_degrees = -decimal_degrees\n",
" # return decimal_degrees\n",
" def convert(value: str) -> float:\n",
" if pd.isna(value):\n",
" return 0.0\n",
" if value == '-':\n",
" return 0.0\n",
" try:\n",
" return dms_to_decimal(value)\n",
" except ValueError:\n",
" return None\n",
" \n",
" row[\"Latitude\"] = convert(row[\"gpslatitude\"])\n",
" row[\"Longitude\"] = convert(row[\"gpslongitude\"])\n",
" return row"
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "8684b468",
"metadata": {},
"outputs": [],
"source": [
"def get_file_name(row: pd.Series) -> pd.Series:\n",
" path_parts = row['file'].split('/')\n",
" file = path_parts[-1]\n",
" row['filename'] = file\n",
" \n",
" row['extension'] = file.split('.')[-1].lower()\n",
" row['file_dt'] = file.split('.')[0]\n",
" return row"
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "6a1e641e",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>file</th>\n",
" <th>createdate</th>\n",
" <th>gpslatitude</th>\n",
" <th>gpslongitude</th>\n",
" <th>Latitude</th>\n",
" <th>Longitude</th>\n",
" <th>filename</th>\n",
" <th>extension</th>\n",
" <th>file_dt</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>0</th>\n",
" <td>../downloads/2019-04-05_23-27-17.jpg</td>\n",
" <td>2019:04:05 18:27:17</td>\n",
" <td>40 deg 24' 14.76\" N</td>\n",
" <td>104 deg 44' 21.52\" W</td>\n",
" <td>40.40410</td>\n",
" <td>-104.73931</td>\n",
" <td>2019-04-05_23-27-17.jpg</td>\n",
" <td>jpg</td>\n",
" <td>2019-04-05_23-27-17</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1</th>\n",
" <td>../downloads/2024-10-23_00-29-09.jpg</td>\n",
" <td>2024:10:22 19:29:09</td>\n",
" <td>44 deg 6' 15.77\" N</td>\n",
" <td>94 deg 13' 10.52\" W</td>\n",
" <td>44.10438</td>\n",
" <td>-94.21959</td>\n",
" <td>2024-10-23_00-29-09.jpg</td>\n",
" <td>jpg</td>\n",
" <td>2024-10-23_00-29-09</td>\n",
" </tr>\n",
" <tr>\n",
" <th>2</th>\n",
" <td>../downloads/2025-02-16_00-09-41.mp4</td>\n",
" <td>2025:02:16 00:09:41</td>\n",
" <td>44 deg 21' 17.71\" N</td>\n",
" <td>93 deg 58' 1.20\" W</td>\n",
" <td>44.35492</td>\n",
" <td>-93.96700</td>\n",
" <td>2025-02-16_00-09-41.mp4</td>\n",
" <td>mp4</td>\n",
" <td>2025-02-16_00-09-41</td>\n",
" </tr>\n",
" <tr>\n",
" <th>3</th>\n",
" <td>../downloads/2023-03-10_22-11-18.jpg</td>\n",
" <td>2023:03:10 16:11:18</td>\n",
" <td>40 deg 24' 39.13\" N</td>\n",
" <td>104 deg 41' 33.90\" W</td>\n",
" <td>40.41087</td>\n",
" <td>-104.69275</td>\n",
" <td>2023-03-10_22-11-18.jpg</td>\n",
" <td>jpg</td>\n",
" <td>2023-03-10_22-11-18</td>\n",
" </tr>\n",
" <tr>\n",
" <th>4</th>\n",
" <td>../downloads/2024-02-24_22-18-07.jpg</td>\n",
" <td>2024:02:24 16:18:07</td>\n",
" <td>40 deg 25' 17.08\" N</td>\n",
" <td>104 deg 45' 22.75\" W</td>\n",
" <td>40.42141</td>\n",
" <td>-104.75632</td>\n",
" <td>2024-02-24_22-18-07.jpg</td>\n",
" <td>jpg</td>\n",
" <td>2024-02-24_22-18-07</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" file createdate \\\n",
"0 ../downloads/2019-04-05_23-27-17.jpg 2019:04:05 18:27:17 \n",
"1 ../downloads/2024-10-23_00-29-09.jpg 2024:10:22 19:29:09 \n",
"2 ../downloads/2025-02-16_00-09-41.mp4 2025:02:16 00:09:41 \n",
"3 ../downloads/2023-03-10_22-11-18.jpg 2023:03:10 16:11:18 \n",
"4 ../downloads/2024-02-24_22-18-07.jpg 2024:02:24 16:18:07 \n",
"\n",
" gpslatitude gpslongitude Latitude Longitude \\\n",
"0 40 deg 24' 14.76\" N 104 deg 44' 21.52\" W 40.40410 -104.73931 \n",
"1 44 deg 6' 15.77\" N 94 deg 13' 10.52\" W 44.10438 -94.21959 \n",
"2 44 deg 21' 17.71\" N 93 deg 58' 1.20\" W 44.35492 -93.96700 \n",
"3 40 deg 24' 39.13\" N 104 deg 41' 33.90\" W 40.41087 -104.69275 \n",
"4 40 deg 25' 17.08\" N 104 deg 45' 22.75\" W 40.42141 -104.75632 \n",
"\n",
" filename extension file_dt \n",
"0 2019-04-05_23-27-17.jpg jpg 2019-04-05_23-27-17 \n",
"1 2024-10-23_00-29-09.jpg jpg 2024-10-23_00-29-09 \n",
"2 2025-02-16_00-09-41.mp4 mp4 2025-02-16_00-09-41 \n",
"3 2023-03-10_22-11-18.jpg jpg 2023-03-10_22-11-18 \n",
"4 2024-02-24_22-18-07.jpg jpg 2024-02-24_22-18-07 "
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"meta = metadata.apply(convert_lat_long_to_decimal_degrees, axis=1)\n",
"meta = meta.apply(get_file_name, axis=1)\n",
"meta.head(5)"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "543eecf8",
"metadata": {},
"outputs": [],
"source": [
"import pytz\n",
"def get_datetime_for_df(row: pd.Series) -> pd.Series:\n",
" from datetime import datetime\n",
" try:\n",
" row['createdate_mod'] = None\n",
" if row['createdate'] != '-':\n",
" createdate = datetime.strptime(row['createdate'], '%Y:%m:%d %H:%M:%S')\n",
" createdate_wtz = createdate.astimezone(pytz.timezone('America/Chicago'))\n",
" \n",
" row['createdate_mod'] = createdate_wtz\n",
" \n",
" \n",
" date = datetime.strptime(row['file_dt'], '%Y-%m-%d_%H-%M-%S')\n",
" dt_w_tz = date.astimezone(pytz.UTC)\n",
" # dt_w_tz = date.dtz_localize('UTC')\n",
" # dt_w_tz = date.tz_localize('UTC')\n",
" row['datetime'] = dt_w_tz\n",
" row['dt_join'] = f\"{dt_w_tz}\"\n",
" # row['datetime_str'] = dt_w_tz.strftime('%Y-%m-%d %H:%M:%S %Z')\n",
" # row['datetime'] = datetime.strptime(row['Date'], '%Y-%m-%d %H-%M-%S')\n",
" except ValueError:\n",
" row['createdate_mod'] = None\n",
" row['datetime'] = None\n",
" row['dt_join'] = ''\n",
" return row"
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "a162c993",
"metadata": {},
"outputs": [],
"source": [
"def get_media_type(row: pd.Series) -> pd.Series:\n",
" row['file_type'] = 'Unknown'\n",
" if row['extension'] == 'jpg':\n",
" row['file_type'] = 'Image'\n",
" if row['extension'] == 'mp4':\n",
" row['file_type'] = 'Video'\n",
" return row"
]
},
{
"cell_type": "code",
"execution_count": 15,
"id": "b57f9726",
"metadata": {},
"outputs": [],
"source": [
"data = meta.apply(get_datetime_for_df, axis=1)\n",
"data = data.apply(get_media_type, axis=1)\n",
"# data.head(10)"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "cea52cd0",
"metadata": {},
"outputs": [],
"source": [
"joined = pd.merge(df, data, how='left', left_on='file_dt', right_on='file_dt', suffixes=('_df', '_meta'))\n",
"# joined.head(10)"
]
},
{
"cell_type": "code",
"execution_count": 17,
"id": "3f350445",
"metadata": {},
"outputs": [],
"source": [
"col_mapper = {\n",
" 'Date': 'correct_date_utc',\n",
" 'Media Type': 'correct_media_type',\n",
" # 'Location': 'correct_location',\n",
" 'location_latitude': 'correct_latitude',\n",
" 'location_longitude': 'correct_longitude',\n",
" 'file': 'path',\n",
" 'filename': 'filename',\n",
" 'createdate': 'actual_date_cst',\n",
" 'Latitude': 'actual_latitude',\n",
" 'Longitude': 'actual_longitude',\n",
" # 'extension': 'actual_extension',\n",
" 'file_type': 'actual_media_type'\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 18,
"id": "07ac5dd2",
"metadata": {},
"outputs": [],
"source": [
"df0 = joined[col_mapper.keys()].rename(columns=col_mapper)\n",
"# df0.head(10)"
]
},
{
"cell_type": "code",
"execution_count": 19,
"id": "192561b3",
"metadata": {},
"outputs": [],
"source": [
"def check_for_errors(row: pd.Series) -> pd.Series:\n",
" errors = 0\n",
" if row[\"correct_media_type\"] != row['actual_media_type']:\n",
" errors += 1\n",
" if row[\"actual_latitude\"] == 0.00 and row[\"correct_media_type\"] == \"Image\":\n",
" if row['correct_latitude'] != 0.00:\n",
" errors +=2\n",
" if abs(row[\"actual_longitude\"] - row[\"correct_longitude\"]) > 2.00 :\n",
" errors +=2\n",
" if row[\"actual_date_cst\"] == '-':\n",
" errors +=4\n",
" if pd.isna(row['path']):\n",
" errors +=20\n",
" row[\"errors\"] = errors\n",
" return row"
]
},
{
"cell_type": "code",
"execution_count": 20,
"id": "c89101b9",
"metadata": {},
"outputs": [],
"source": [
"# 1 = Media Type\n",
"# 2 = Latitude\n",
"# 4 = Date\n",
"# 20 = Path\n",
"\n",
"df1 = df0.apply(check_for_errors, axis=1)\n",
"# df1.head(10)"
]
},
{
"cell_type": "code",
"execution_count": 21,
"id": "445c1b9b",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>correct_date_utc</th>\n",
" <th>correct_media_type</th>\n",
" <th>correct_latitude</th>\n",
" <th>correct_longitude</th>\n",
" <th>path</th>\n",
" <th>filename</th>\n",
" <th>actual_date_cst</th>\n",
" <th>actual_latitude</th>\n",
" <th>actual_longitude</th>\n",
" <th>actual_media_type</th>\n",
" <th>errors</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>608</th>\n",
" <td>2024-05-11 05:50:50+00:00</td>\n",
" <td>Video</td>\n",
" <td>44.10428</td>\n",
" <td>-94.21973</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>21</td>\n",
" </tr>\n",
" <tr>\n",
" <th>823</th>\n",
" <td>2023-11-29 17:14:20+00:00</td>\n",
" <td>Image</td>\n",
" <td>40.41069</td>\n",
" <td>-104.69252</td>\n",
" <td>../downloads/2023-11-29_17-14-20.jpg</td>\n",
" <td>2023-11-29_17-14-20.jpg</td>\n",
" <td>-</td>\n",
" <td>0.00000</td>\n",
" <td>0.00000</td>\n",
" <td>Image</td>\n",
" <td>8</td>\n",
" </tr>\n",
" <tr>\n",
" <th>824</th>\n",
" <td>2023-11-29 17:14:20+00:00</td>\n",
" <td>Image</td>\n",
" <td>40.41069</td>\n",
" <td>-104.69252</td>\n",
" <td>../downloads/2023-11-29_17-14-20.jpg</td>\n",
" <td>2023-11-29_17-14-20.jpg</td>\n",
" <td>-</td>\n",
" <td>0.00000</td>\n",
" <td>0.00000</td>\n",
" <td>Image</td>\n",
" <td>8</td>\n",
" </tr>\n",
" <tr>\n",
" <th>879</th>\n",
" <td>2023-11-03 03:13:21+00:00</td>\n",
" <td>Image</td>\n",
" <td>44.10438</td>\n",
" <td>-94.21960</td>\n",
" <td>../downloads/2023-11-03_03-13-21.jpg</td>\n",
" <td>2023-11-03_03-13-21.jpg</td>\n",
" <td>2023:11:02 22:13:21</td>\n",
" <td>40.43225</td>\n",
" <td>-104.76981</td>\n",
" <td>Image</td>\n",
" <td>2</td>\n",
" </tr>\n",
" <tr>\n",
" <th>993</th>\n",
" <td>2023-09-02 16:34:44+00:00</td>\n",
" <td>Image</td>\n",
" <td>44.05208</td>\n",
" <td>-94.40385</td>\n",
" <td>../downloads/2023-09-02_16-34-44.jpg</td>\n",
" <td>2023-09-02_16-34-44.jpg</td>\n",
" <td>2023:09:02 11:34:44</td>\n",
" <td>40.41102</td>\n",
" <td>-104.69190</td>\n",
" <td>Image</td>\n",
" <td>2</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" correct_date_utc correct_media_type correct_latitude \\\n",
"608 2024-05-11 05:50:50+00:00 Video 44.10428 \n",
"823 2023-11-29 17:14:20+00:00 Image 40.41069 \n",
"824 2023-11-29 17:14:20+00:00 Image 40.41069 \n",
"879 2023-11-03 03:13:21+00:00 Image 44.10438 \n",
"993 2023-09-02 16:34:44+00:00 Image 44.05208 \n",
"\n",
" correct_longitude path \\\n",
"608 -94.21973 NaN \n",
"823 -104.69252 ../downloads/2023-11-29_17-14-20.jpg \n",
"824 -104.69252 ../downloads/2023-11-29_17-14-20.jpg \n",
"879 -94.21960 ../downloads/2023-11-03_03-13-21.jpg \n",
"993 -94.40385 ../downloads/2023-09-02_16-34-44.jpg \n",
"\n",
" filename actual_date_cst actual_latitude \\\n",
"608 NaN NaN NaN \n",
"823 2023-11-29_17-14-20.jpg - 0.00000 \n",
"824 2023-11-29_17-14-20.jpg - 0.00000 \n",
"879 2023-11-03_03-13-21.jpg 2023:11:02 22:13:21 40.43225 \n",
"993 2023-09-02_16-34-44.jpg 2023:09:02 11:34:44 40.41102 \n",
"\n",
" actual_longitude actual_media_type errors \n",
"608 NaN NaN 21 \n",
"823 0.00000 Image 8 \n",
"824 0.00000 Image 8 \n",
"879 -104.76981 Image 2 \n",
"993 -104.69190 Image 2 "
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"errors = df1[df1['errors'] > 0]\n",
"errors.head(5)"
]
},
{
"cell_type": "code",
"execution_count": 22,
"id": "de9ed1f5",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(7, 11)"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"errors.shape"
]
},
{
"cell_type": "markdown",
"id": "bd5d80b3",
"metadata": {},
"source": [
"679/3700 (oof)"
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "0b325144",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(4, 11)"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"img_errors = errors[errors['correct_media_type'] == 'Image']\n",
"img_errors.shape"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "3559f965",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"(3, 11)"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"video_errors = errors[errors['correct_media_type'] == 'Video']\n",
"video_errors.shape"
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "49e183fd",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>correct_date_utc</th>\n",
" <th>correct_media_type</th>\n",
" <th>correct_latitude</th>\n",
" <th>correct_longitude</th>\n",
" <th>path</th>\n",
" <th>filename</th>\n",
" <th>actual_date_cst</th>\n",
" <th>actual_latitude</th>\n",
" <th>actual_longitude</th>\n",
" <th>actual_media_type</th>\n",
" <th>errors</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>608</th>\n",
" <td>2024-05-11 05:50:50+00:00</td>\n",
" <td>Video</td>\n",
" <td>44.10428</td>\n",
" <td>-94.21973</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>21</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1633</th>\n",
" <td>2022-05-07 15:25:31+00:00</td>\n",
" <td>Video</td>\n",
" <td>0.00000</td>\n",
" <td>0.00000</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>21</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1763</th>\n",
" <td>2022-01-31 05:59:54+00:00</td>\n",
" <td>Video</td>\n",
" <td>40.43224</td>\n",
" <td>-104.76977</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>NaN</td>\n",
" <td>21</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" correct_date_utc correct_media_type correct_latitude \\\n",
"608 2024-05-11 05:50:50+00:00 Video 44.10428 \n",
"1633 2022-05-07 15:25:31+00:00 Video 0.00000 \n",
"1763 2022-01-31 05:59:54+00:00 Video 40.43224 \n",
"\n",
" correct_longitude path filename actual_date_cst actual_latitude \\\n",
"608 -94.21973 NaN NaN NaN NaN \n",
"1633 0.00000 NaN NaN NaN NaN \n",
"1763 -104.76977 NaN NaN NaN NaN \n",
"\n",
" actual_longitude actual_media_type errors \n",
"608 NaN NaN 21 \n",
"1633 NaN NaN 21 \n",
"1763 NaN NaN 21 "
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"video_errors.head(10)"
]
},
{
"cell_type": "code",
"execution_count": 26,
"id": "9e5569c5",
"metadata": {},
"outputs": [
{
"data": {
"text/html": [
"<div>\n",
"<style scoped>\n",
" .dataframe tbody tr th:only-of-type {\n",
" vertical-align: middle;\n",
" }\n",
"\n",
" .dataframe tbody tr th {\n",
" vertical-align: top;\n",
" }\n",
"\n",
" .dataframe thead th {\n",
" text-align: right;\n",
" }\n",
"</style>\n",
"<table border=\"1\" class=\"dataframe\">\n",
" <thead>\n",
" <tr style=\"text-align: right;\">\n",
" <th></th>\n",
" <th>correct_date_utc</th>\n",
" <th>correct_media_type</th>\n",
" <th>correct_latitude</th>\n",
" <th>correct_longitude</th>\n",
" <th>path</th>\n",
" <th>filename</th>\n",
" <th>actual_date_cst</th>\n",
" <th>actual_latitude</th>\n",
" <th>actual_longitude</th>\n",
" <th>actual_media_type</th>\n",
" <th>errors</th>\n",
" </tr>\n",
" </thead>\n",
" <tbody>\n",
" <tr>\n",
" <th>608</th>\n",
" <td>2024-05-11 05:50:50+00:00</td>\n",
" <td>Video</td>\n",
" <td>44.10428</td>\n",
" <td>-94.21973</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>21</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1633</th>\n",
" <td>2022-05-07 15:25:31+00:00</td>\n",
" <td>Video</td>\n",
" <td>0.00000</td>\n",
" <td>0.00000</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>21</td>\n",
" </tr>\n",
" <tr>\n",
" <th>1763</th>\n",
" <td>2022-01-31 05:59:54+00:00</td>\n",
" <td>Video</td>\n",
" <td>40.43224</td>\n",
" <td>-104.76977</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>-</td>\n",
" <td>21</td>\n",
" </tr>\n",
" </tbody>\n",
"</table>\n",
"</div>"
],
"text/plain": [
" correct_date_utc correct_media_type correct_latitude \\\n",
"608 2024-05-11 05:50:50+00:00 Video 44.10428 \n",
"1633 2022-05-07 15:25:31+00:00 Video 0.00000 \n",
"1763 2022-01-31 05:59:54+00:00 Video 40.43224 \n",
"\n",
" correct_longitude path filename actual_date_cst actual_latitude \\\n",
"608 -94.21973 - - - - \n",
"1633 0.00000 - - - - \n",
"1763 -104.76977 - - - - \n",
"\n",
" actual_longitude actual_media_type errors \n",
"608 - - 21 \n",
"1633 - - 21 \n",
"1763 - - 21 "
]
},
"execution_count": 26,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"wrong_filetype = df1[df1['correct_media_type'] != df1['actual_media_type']]\n",
"wrong_filetype = wrong_filetype.fillna('-')\n",
"wrong_filetype.head(5)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f7891aa1",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"\n",
"def change_filetype(row: pd.Series) -> pd.Series:\n",
" try:\n",
" if row['path'] != '-':\n",
" if row['correct_media_type'] == 'Image':\n",
" os.rename(src=f\"../downloads/{row['filename'][:-4]}.mp4\", dst=f\"../downloads/{row['filename'][:-4]}.jpg\")\n",
" elif row['correct_media_type'] == 'Video':\n",
" os.rename(src=f\"../downloads/{row['filename'][:-4]}.jpg\", dst=f\"../downloads/{row['filename'][:-4]}.mp4\")\n",
" except FileNotFoundError:\n",
" print(f\"File not found: {row['filename']}\")\n",
" return row"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "df5eedbd",
"metadata": {},
"outputs": [],
"source": [
"wrong_filetype.apply(change_filetype, axis=1)"
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "07514caa",
"metadata": {},
"outputs": [],
"source": [
"import pytz\n",
"from timezonefinder import TimezoneFinder\n",
"from datetime import datetime\n",
"import pandas as pd # Assuming you still use pandas\n",
"\n",
"# Initialize the TimezoneFinder outside the loop for efficiency\n",
"tf = TimezoneFinder()\n",
"\n",
"def get_localized_dt_and_offset(utc_dt, latitude, longitude):\n",
" \"\"\"\n",
" Finds the correct local timezone based on coordinates and converts\n",
" the UTC datetime object to that local time, returning the time string\n",
" and the corresponding offset string.\n",
" \"\"\"\n",
" # 1. Find the IANA Timezone Name\n",
" tz_name = tf.timezone_at(lng=longitude, lat=latitude)\n",
" \n",
" # Timezones\n",
" central_timezone = pytz.timezone('America/Chicago')\n",
" mountain_timezone = pytz.timezone('America/Denver')\n",
"\n",
" if not tz_name or latitude == 0 or longitude == 0:\n",
" # Fallback if no timezone is found (e.g., open ocean)\n",
" # print(f\"⚠️ Warning: No timezone found for {latitude}, {longitude}. Defaulting to UTC.\")\n",
" # local_tz = pytz.utc\n",
" print(f\"⚠️ Warning: No timezone found for {latitude}, {longitude}. Defaulting to MDT/MST.\")\n",
" local_tz = mountain_timezone\n",
" else:\n",
" local_tz = pytz.timezone(tz_name)\n",
"\n",
" # 2. Convert from UTC to the determined local timezone\n",
" # The input 'utc_dt' must already be a timezone-aware UTC object (which it is in your current code)\n",
" dt_local = utc_dt.astimezone(local_tz)\n",
"\n",
" # 3. Format the local date string (for DateTimeOriginal, etc.)\n",
" # Must use 'YYYY:MM:DD HH:MM:SS' format\n",
" dt_str = dt_local.strftime(\"%Y:%m:%d %H:%M:%S\")\n",
"\n",
" # 4. Format the time zone offset string (for OffsetTimeOriginal)\n",
" # The '+/-HHMM' string is extracted, and the colon is inserted for EXIF compatibility\n",
" offset_str = dt_local.strftime('%z')\n",
" if len(offset_str) == 5:\n",
" offset_str = offset_str[:3] + \":\" + offset_str[3:] \n",
"\n",
" return dt_str, offset_str"
]
},
{
"cell_type": "code",
"execution_count": 35,
"id": "e7b8202f",
"metadata": {},
"outputs": [],
"source": [
"import pandas as pd\n",
"import subprocess\n",
"import os\n",
"\n",
"# --- 1. Define the Update Function ---\n",
"\n",
"def update_exif_with_exiftool(row):\n",
" \"\"\"\n",
" Constructs and runs the ExifTool command for a single image.\n",
" \"\"\"\n",
" image_path = row['path']\n",
" create_date = row['correct_date_utc']\n",
" latitude = row['correct_latitude']\n",
" longitude = row['correct_longitude']\n",
" file_type = row['correct_media_type']\n",
" abs_latitude = abs(latitude)\n",
" abs_longitude = abs(longitude)\n",
" \n",
" if image_path is None or pd.isna(image_path) or image_path == '':\n",
" print(f\"❌ File not found, skipping: {image_path}\")\n",
" return\n",
" \n",
" utc = pytz.utc\n",
" # Use the 'America/Chicago' zone which handles both CST (-6) and CDT (-5) automatically\n",
" central = pytz.timezone('America/Chicago')\n",
" # local_dt = utc_dt.astimezone(central)\n",
" # dt_utc = utc.localize(create_date)\n",
" dt_utc = create_date\n",
" dt_central = dt_utc.astimezone(central)\n",
" dt_str = dt_central.strftime(\"%Y:%m:%d %H:%M:%S\")\n",
" dt_utc_str = dt_utc.strftime(\"%Y:%m:%d %H:%M:%S\")\n",
" \n",
" lat_ref = 'N' if latitude >= 0 else 'S'\n",
" lon_ref = 'E' if longitude >= 0 else 'W'\n",
" \n",
" # Determine the time zone offset string (e.g., \"-05:00\" or \"-06:00\")\n",
" # This is critical for the viewing software to correctly interpret dt_str.\n",
" offset_str = dt_central.strftime('%z')\n",
" if len(offset_str) == 5:\n",
" offset_str = offset_str[:3] + \":\" + offset_str[3:] # Converts -0500 to -05:00\n",
" \n",
" dt_str, offset_str = get_localized_dt_and_offset(\n",
" utc_dt=dt_utc,\n",
" latitude=row['correct_latitude'],\n",
" longitude=row['correct_longitude']\n",
" )\n",
"\n",
" if not os.path.exists(image_path):\n",
" print(f\"❌ File not found, skipping: {image_path}\")\n",
" return\n",
"\n",
" # ExifTool requires the date format to be adjusted slightly, \n",
" # and it automatically handles the degree/minute/second conversion.\n",
" # The date MUST be in 'YYYY:MM:DD HH:MM:SS' format (same as piexif).\n",
" \n",
" # We use -overwrite_original to avoid creating unnecessary backup files (.jpg_original)\n",
" # The -all= handles potential piexif conflicts by deleting all existing tags \n",
" # before adding new ones, but is commented out for safety.\n",
" # command = [\n",
" # 'exiftool',\n",
" # # '-all=', # <-- Optional: Use this to remove all existing metadata first\n",
" # # # Set all three date tags explicitly using the Central Time value\n",
" # f'-CreateDate={dt_str}'\n",
" # # f'-DateTime={dt_str}'\n",
" # # f'-DateTimeOriginal={dt_str}',\n",
" # # f'-DateTimeDigitized={dt_str}',\n",
" # f'-GPSLatitude={latitude}',\n",
" # f'-GPSLongitude={longitude}',\n",
" # f\"-GPSLatitudeRef='{lat_ref}'\",\n",
" # f\"-GPSLongitudeRef='{lon_ref}'\",\n",
" # '-overwrite_original',\n",
" # image_path\n",
" # ]\n",
" jpg_command = [\n",
" 'exiftool',\n",
" # Local time (Central Time) determined by pytz\n",
" f'-IFD0:DateTime={dt_str}',\n",
" f'-ExifIFD:DateTimeOriginal={dt_str}',\n",
" f'-ExifIFD:DateTimeDigitized={dt_str}',\n",
" \n",
" # Dynamic offset (-05:00 or -06:00) determined by pytz\n",
" f'-OffsetTimeOriginal={offset_str}',\n",
" \n",
" # GPS tags\n",
" f'-GPSLatitude={latitude}',\n",
" f'-GPSLongitude={longitude}',\n",
" f\"-GPSLatitudeRef='{lat_ref}'\",\n",
" f\"-GPSLongitudeRef='{lon_ref}'\",\n",
" \n",
" # Keep overwrite and target file\n",
" '-overwrite_original',\n",
" image_path\n",
" ]\n",
" mp4_command = [\n",
" \"exiftool\",\n",
" \n",
" # --- EXIF Tags (These are often ignored by video players, but good to include) ---\n",
" f'-IFD0:DateTime={dt_str}', # Local time\n",
" f'-ExifIFD:DateTimeOriginal={dt_str}', # Local time\n",
" f'-ExifIFD:DateTimeDigitized={dt_str}', # Local time\n",
" f'-OffsetTimeOriginal={offset_str}', # Dynamic offset\n",
" \n",
" # --- QUICKTIME Tags (The critical part for MP4/MOV) ---\n",
" # QuickTime tags should be set to UTC time string\n",
" f\"-QuickTime:CreationDate={dt_utc_str}\", # <-- *** CHANGE 1: Use UTC Time, Different Tag Name ***\n",
" f\"-QuickTime:TrackCreateDate={dt_utc_str}\", # <-- *** CHANGE 2: Also set Track-level tag to UTC ***\n",
" f\"-QuickTime:MediaCreateDate={dt_utc_str}\", # <-- *** CHANGE 3: Also set Media-level tag to UTC ***\n",
" \n",
" # This is for the display TimeZone of the above tags\n",
" f\"-QuickTime:TimeZone={offset_str}\", # Dynamic offset (e.g., -05:00)\n",
" \n",
" # # Generic Composite Tag (Tries to create necessary atoms)\n",
" # f\"-GPSCoordinates={coord_str}\",\n",
"\n",
" # # EXIF\n",
" # f\"-exif:gpslatitude={lat}\",\n",
" # f\"-exif:gpslongitude={lon}\",\n",
" # f\"-gpslatituderef={'N' if lat >= 0 else 'S'}\",\n",
" # f\"-gpslongituderef={'E' if lon >= 0 else 'W'}\",\n",
" \n",
" # omposite Tag (Highly recommended for QuickTime/MP4 containers)\n",
" f\"-QuickTime:GPSCoordinates={abs_latitude} {lat_ref}, {abs_longitude} {lon_ref}\",\n",
"\n",
" # Highly prioritized XMP Tag (Often read first by Adobe/Google)\n",
" f\"-xmp:gpslatitude={latitude}\",\n",
" f\"-xmp:gpslongitude={longitude}\",\n",
"\n",
" # f\"-gpslatituderef={'N' if lat >= 0 else 'S'}\",\n",
" # f\"-gpslongituderef={'E' if lon >= 0 else 'W'}\",\n",
"\n",
" # QuickTime Location tags (Apple/iOS compatibility)\n",
" f\"-QuickTime:LocationLatitude={latitude}\",\n",
" f\"-QuickTime:LocationLongitude={longitude}\",\n",
"\n",
" # # Generic GPS tags (For broad compatibility)\n",
" f\"-GPSLatitude={latitude}\",\n",
" f\"-GPSLongitude={longitude}\",\n",
" f\"-GPSLatitudeRef={lat_ref}\",\n",
" f\"-GPSLongitudeRef={lon_ref}\",\n",
" \n",
" # Keep overwrite and target file\n",
" '-overwrite_original',\n",
" image_path\n",
" ]\n",
"\n",
" try:\n",
" # Run the command\n",
" if file_type == 'Video':\n",
" result = subprocess.run(mp4_command, check=True, capture_output=True, text=True)\n",
" else:\n",
" result = subprocess.run(jpg_command, check=True, capture_output=True, text=True)\n",
" \n",
" # Check ExifTool output for success messages\n",
" if \"1 image files updated\" in result.stdout:\n",
" print(f\"✅ Updated: {image_path}\")\n",
" else:\n",
" # Print stderr if ExifTool reports an error (e.g., invalid data)\n",
" print(f\"⚠️ Warning updating {image_path}: {result.stderr.strip()}\")\n",
"\n",
" except subprocess.CalledProcessError as e:\n",
" print(f\"🛑 Subprocess Error on {image_path}: {e.stderr.strip()}\")\n",
" except FileNotFoundError:\n",
" print(\"🛑 ERROR: ExifTool command not found. Is ExifTool installed and in your PATH?\")\n"
]
},
{
"cell_type": "code",
"execution_count": 36,
"id": "e2736799",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"✅ Updated: ../downloads/2025-11-29_04-50-47.mp4\n",
"✅ Updated: ../downloads/2025-11-29_04-49-56.mp4\n",
"✅ Updated: ../downloads/2025-11-26_14-23-27.mp4\n",
"✅ Updated: ../downloads/2025-11-26_14-18-45.mp4\n",
"✅ Updated: ../downloads/2025-11-16_17-58-59.jpg\n",
"✅ Updated: ../downloads/2025-11-12_02-10-34.mp4\n",
"✅ Updated: ../downloads/2025-11-07_04-43-52.mp4\n",
"✅ Updated: ../downloads/2025-10-27_21-50-11.mp4\n",
"✅ Updated: ../downloads/2025-10-16_06-39-29.mp4\n",
"✅ Updated: ../downloads/2025-10-14_20-46-28.mp4\n",
"✅ Updated: ../downloads/2025-10-14_03-36-17.mp4\n",
"✅ Updated: ../downloads/2025-10-12_00-28-20.jpg\n",
"✅ Updated: ../downloads/2025-10-05_23-44-16.mp4\n",
"✅ Updated: ../downloads/2025-09-27_06-55-18.mp4\n",
"✅ Updated: ../downloads/2025-09-01_03-54-01.mp4\n",
"✅ Updated: ../downloads/2025-08-31_00-59-00.mp4\n",
"✅ Updated: ../downloads/2025-08-29_17-28-21.jpg\n",
"✅ Updated: ../downloads/2025-08-29_17-27-42.jpg\n",
"✅ Updated: ../downloads/2025-08-24_02-19-46.jpg\n",
"✅ Updated: ../downloads/2025-08-23_22-21-37.mp4\n",
"✅ Updated: ../downloads/2025-08-17_04-10-25.mp4\n",
"✅ Updated: ../downloads/2025-08-12_03-06-36.jpg\n",
"✅ Updated: ../downloads/2025-08-12_03-06-25.mp4\n",
"✅ Updated: ../downloads/2025-08-12_03-06-07.mp4\n",
"✅ Updated: ../downloads/2025-08-12_02-49-32.mp4\n",
"✅ Updated: ../downloads/2025-08-12_02-40-31.mp4\n",
"✅ Updated: ../downloads/2025-08-10_13-33-53.mp4\n",
"✅ Updated: ../downloads/2025-08-10_12-21-16.mp4\n",
"✅ Updated: ../downloads/2025-08-10_03-46-49.mp4\n",
"✅ Updated: ../downloads/2025-08-09_04-23-56.jpg\n",
"✅ Updated: ../downloads/2025-08-02_16-35-00.mp4\n",
"✅ Updated: ../downloads/2025-07-28_02-31-13.mp4\n",
"✅ Updated: ../downloads/2025-07-28_02-30-32.mp4\n",
"✅ Updated: ../downloads/2025-07-28_02-29-46.mp4\n",
"✅ Updated: ../downloads/2025-07-28_02-27-42.mp4\n",
"✅ Updated: ../downloads/2025-07-28_02-26-47.mp4\n",
"✅ Updated: ../downloads/2025-07-26_20-37-00.mp4\n",
"✅ Updated: ../downloads/2025-07-20_20-56-16.mp4\n",
"✅ Updated: ../downloads/2025-07-20_20-51-51.mp4\n",
"✅ Updated: ../downloads/2025-07-20_20-05-21.jpg\n",
"✅ Updated: ../downloads/2025-07-17_17-01-56.jpg\n",
"✅ Updated: ../downloads/2025-07-10_01-51-17.jpg\n",
"✅ Updated: ../downloads/2025-07-02_17-28-47.mp4\n",
"✅ Updated: ../downloads/2025-06-29_01-56-40.mp4\n",
"✅ Updated: ../downloads/2025-06-29_00-52-15.mp4\n",
"✅ Updated: ../downloads/2025-06-29_00-51-07.jpg\n",
"✅ Updated: ../downloads/2025-06-29_00-50-16.jpg\n",
"✅ Updated: ../downloads/2025-06-29_00-49-42.mp4\n",
"✅ Updated: ../downloads/2025-06-28_19-34-51.mp4\n",
"✅ Updated: ../downloads/2025-06-28_02-55-48.mp4\n",
"✅ Updated: ../downloads/2025-06-28_02-55-14.jpg\n",
"✅ Updated: ../downloads/2025-06-28_02-54-42.jpg\n",
"✅ Updated: ../downloads/2025-06-26_17-46-22.mp4\n",
"✅ Updated: ../downloads/2025-06-26_17-09-28.jpg\n",
"✅ Updated: ../downloads/2025-06-25_21-14-52.mp4\n",
"✅ Updated: ../downloads/2025-06-24_17-37-25.mp4\n",
"✅ Updated: ../downloads/2025-06-22_15-44-19.jpg\n",
"✅ Updated: ../downloads/2025-06-19_05-37-13.mp4\n",
"✅ Updated: ../downloads/2025-06-16_23-56-32.mp4\n",
"✅ Updated: ../downloads/2025-06-16_23-51-15.mp4\n",
"✅ Updated: ../downloads/2025-06-07_04-22-44.jpg\n",
"✅ Updated: ../downloads/2025-06-06_21-56-34.mp4\n",
"✅ Updated: ../downloads/2025-06-06_06-48-59.mp4\n",
"✅ Updated: ../downloads/2025-06-04_21-26-33.mp4\n",
"✅ Updated: ../downloads/2025-06-03_01-08-58.jpg\n",
"✅ Updated: ../downloads/2025-06-01_00-47-20.jpg\n",
"✅ Updated: ../downloads/2025-05-30_13-00-27.jpg\n",
"✅ Updated: ../downloads/2025-05-29_23-34-02.mp4\n",
"✅ Updated: ../downloads/2025-05-26_23-20-42.mp4\n",
"✅ Updated: ../downloads/2025-05-25_22-48-34.mp4\n",
"✅ Updated: ../downloads/2025-05-25_20-22-07.mp4\n",
"✅ Updated: ../downloads/2025-05-25_19-41-03.mp4\n",
"✅ Updated: ../downloads/2025-05-25_19-21-09.jpg\n",
"✅ Updated: ../downloads/2025-05-25_15-01-09.mp4\n",
"✅ Updated: ../downloads/2025-05-25_03-48-39.jpg\n",
"✅ Updated: ../downloads/2025-05-25_03-10-33.jpg\n",
"✅ Updated: ../downloads/2025-05-24_23-15-27.jpg\n",
"✅ Updated: ../downloads/2025-05-24_21-44-40.mp4\n",
"✅ Updated: ../downloads/2025-05-24_21-44-03.mp4\n",
"✅ Updated: ../downloads/2025-05-23_17-10-41.mp4\n",
"✅ Updated: ../downloads/2025-05-22_22-38-27.jpg\n",
"✅ Updated: ../downloads/2025-05-22_22-36-17.jpg\n",
"✅ Updated: ../downloads/2025-05-22_20-46-23.jpg\n",
"✅ Updated: ../downloads/2025-05-22_08-13-57.jpg\n",
"✅ Updated: ../downloads/2025-05-20_22-06-05.mp4\n",
"✅ Updated: ../downloads/2025-05-18_18-30-41.jpg\n",
"✅ Updated: ../downloads/2025-05-17_17-48-44.mp4\n",
"✅ Updated: ../downloads/2025-05-16_17-30-06.jpg\n",
"✅ Updated: ../downloads/2025-05-15_22-01-54.mp4\n",
"✅ Updated: ../downloads/2025-05-15_22-00-58.jpg\n",
"✅ Updated: ../downloads/2025-05-15_12-59-50.jpg\n",
"✅ Updated: ../downloads/2025-05-15_12-54-21.mp4\n",
"✅ Updated: ../downloads/2025-05-13_15-03-08.jpg\n",
"✅ Updated: ../downloads/2025-05-12_17-33-31.mp4\n",
"✅ Updated: ../downloads/2025-05-12_01-32-33.jpg\n",
"✅ Updated: ../downloads/2025-05-11_00-25-50.jpg\n",
"✅ Updated: ../downloads/2025-05-10_03-47-27.mp4\n",
"✅ Updated: ../downloads/2025-05-09_13-01-46.mp4\n",
"✅ Updated: ../downloads/2025-05-06_13-12-34.mp4\n",
"✅ Updated: ../downloads/2025-05-06_00-42-06.mp4\n",
"✅ Updated: ../downloads/2025-05-04_21-46-48.jpg\n",
"✅ Updated: ../downloads/2025-05-04_21-33-24.jpg\n",
"✅ Updated: ../downloads/2025-05-03_05-03-28.mp4\n",
"✅ Updated: ../downloads/2025-05-03_03-26-24.mp4\n",
"✅ Updated: ../downloads/2025-05-03_03-06-52.mp4\n",
"✅ Updated: ../downloads/2025-05-03_03-06-16.mp4\n",
"✅ Updated: ../downloads/2025-05-01_22-29-02.mp4\n",
"✅ Updated: ../downloads/2025-05-01_22-15-25.mp4\n",
"✅ Updated: ../downloads/2025-05-01_22-14-00.mp4\n",
"✅ Updated: ../downloads/2025-04-30_23-50-51.mp4\n",
"✅ Updated: ../downloads/2025-04-30_23-30-19.mp4\n",
"✅ Updated: ../downloads/2025-04-29_03-14-13.jpg\n",
"✅ Updated: ../downloads/2025-04-28_21-39-34.mp4\n",
"✅ Updated: ../downloads/2025-04-28_21-29-43.mp4\n",
"✅ Updated: ../downloads/2025-04-28_21-28-42.mp4\n",
"✅ Updated: ../downloads/2025-04-28_21-27-35.mp4\n",
"✅ Updated: ../downloads/2025-04-28_21-27-01.mp4\n",
"✅ Updated: ../downloads/2025-04-28_21-25-57.mp4\n",
"✅ Updated: ../downloads/2025-04-28_20-09-47.mp4\n",
"✅ Updated: ../downloads/2025-04-28_12-34-43.mp4\n",
"✅ Updated: ../downloads/2025-04-27_23-09-13.jpg\n",
"✅ Updated: ../downloads/2025-04-27_19-36-41.jpg\n",
"✅ Updated: ../downloads/2025-04-27_14-36-14.mp4\n",
"✅ Updated: ../downloads/2025-04-26_22-53-20.mp4\n",
"✅ Updated: ../downloads/2025-04-26_18-16-24.jpg\n",
"✅ Updated: ../downloads/2025-04-26_14-56-17.jpg\n",
"✅ Updated: ../downloads/2025-04-24_17-36-13.jpg\n",
"✅ Updated: ../downloads/2025-04-22_23-35-51.mp4\n",
"✅ Updated: ../downloads/2025-04-22_22-45-21.jpg\n",
"✅ Updated: ../downloads/2025-04-22_22-44-30.mp4\n",
"✅ Updated: ../downloads/2025-04-22_21-31-04.mp4\n",
"✅ Updated: ../downloads/2025-04-22_00-16-13.mp4\n",
"✅ Updated: ../downloads/2025-04-22_00-13-39.jpg\n",
"✅ Updated: ../downloads/2025-04-21_23-36-23.jpg\n",
"✅ Updated: ../downloads/2025-04-21_23-33-09.jpg\n",
"✅ Updated: ../downloads/2025-04-21_04-15-06.jpg\n",
"✅ Updated: ../downloads/2025-04-20_02-17-54.mp4\n",
"✅ Updated: ../downloads/2025-04-20_00-41-07.mp4\n",
"✅ Updated: ../downloads/2025-04-20_00-37-27.mp4\n",
"✅ Updated: ../downloads/2025-04-20_00-34-34.jpg\n",
"✅ Updated: ../downloads/2025-04-20_00-01-42.mp4\n",
"✅ Updated: ../downloads/2025-04-19_21-36-45.jpg\n",
"✅ Updated: ../downloads/2025-04-19_21-30-08.jpg\n",
"✅ Updated: ../downloads/2025-04-19_21-23-49.mp4\n",
"✅ Updated: ../downloads/2025-04-18_20-40-08.jpg\n",
"✅ Updated: ../downloads/2025-04-18_19-38-33.mp4\n",
"✅ Updated: ../downloads/2025-04-17_22-43-13.mp4\n",
"✅ Updated: ../downloads/2025-04-17_22-42-17.mp4\n",
"✅ Updated: ../downloads/2025-04-17_21-15-53.mp4\n",
"✅ Updated: ../downloads/2025-04-17_21-13-09.mp4\n",
"✅ Updated: ../downloads/2025-04-16_17-22-51.jpg\n",
"✅ Updated: ../downloads/2025-04-16_01-26-38.jpg\n",
"✅ Updated: ../downloads/2025-04-14_23-23-23.mp4\n",
"✅ Updated: ../downloads/2025-04-13_22-54-57.jpg\n",
"✅ Updated: ../downloads/2025-04-13_19-29-32.mp4\n",
"✅ Updated: ../downloads/2025-04-12_22-13-27.mp4\n",
"✅ Updated: ../downloads/2025-04-12_22-12-59.mp4\n",
"✅ Updated: ../downloads/2025-04-12_05-30-26.jpg\n",
"✅ Updated: ../downloads/2025-04-09_21-19-07.mp4\n",
"✅ Updated: ../downloads/2025-04-09_16-08-58.jpg\n",
"⚠️ Warning: No timezone found for 0.0, 0.0. Defaulting to MDT/MST.\n",
"✅ Updated: ../downloads/2025-04-08_23-49-59.jpg\n",
"⚠️ Warning: No timezone found for 0.0, 0.0. Defaulting to MDT/MST.\n",
"✅ Updated: ../downloads/2025-04-08_23-41-20.jpg\n",
"✅ Updated: ../downloads/2025-04-07_21-45-12.jpg\n",
"✅ Updated: ../downloads/2025-04-07_20-16-50.jpg\n",
"✅ Updated: ../downloads/2025-04-07_19-09-50.mp4\n",
"✅ Updated: ../downloads/2025-04-07_13-36-24.mp4\n",
"✅ Updated: ../downloads/2025-04-07_12-27-41.mp4\n",
"✅ Updated: ../downloads/2025-04-05_20-32-35.mp4\n",
"✅ Updated: ../downloads/2025-04-05_03-15-28.jpg\n",
"✅ Updated: ../downloads/2025-04-01_23-56-36.mp4\n",
"✅ Updated: ../downloads/2025-04-01_23-44-12.mp4\n",
"✅ Updated: ../downloads/2025-04-01_23-40-31.mp4\n",
"✅ Updated: ../downloads/2025-04-01_23-24-43.jpg\n",
"✅ Updated: ../downloads/2025-04-01_23-24-43.jpg\n",
"✅ Updated: ../downloads/2025-04-01_23-22-41.mp4\n",
"✅ Updated: ../downloads/2025-04-01_23-20-56.mp4\n",
"✅ Updated: ../downloads/2025-04-01_22-46-19.jpg\n",
"✅ Updated: ../downloads/2025-04-01_22-25-16.jpg\n",
"✅ Updated: ../downloads/2025-04-01_22-06-11.mp4\n",
"✅ Updated: ../downloads/2025-04-01_22-04-18.mp4\n",
"✅ Updated: ../downloads/2025-04-01_22-01-08.mp4\n",
"✅ Updated: ../downloads/2025-04-01_22-01-08.mp4\n",
"✅ Updated: ../downloads/2025-04-01_21-46-17.mp4\n",
"✅ Updated: ../downloads/2025-04-01_17-36-23.jpg\n",
"✅ Updated: ../downloads/2025-03-31_21-52-18.jpg\n",
"✅ Updated: ../downloads/2025-03-30_17-35-39.mp4\n",
"✅ Updated: ../downloads/2025-03-29_19-00-58.jpg\n",
"✅ Updated: ../downloads/2025-03-28_17-09-38.jpg\n",
"✅ Updated: ../downloads/2025-03-26_00-02-08.jpg\n",
"✅ Updated: ../downloads/2025-03-22_03-24-25.jpg\n",
"✅ Updated: ../downloads/2025-03-20_22-01-42.jpg\n",
"✅ Updated: ../downloads/2025-03-17_14-00-28.jpg\n",
"✅ Updated: ../downloads/2025-03-17_13-32-21.mp4\n",
"✅ Updated: ../downloads/2025-03-17_13-09-26.mp4\n",
"✅ Updated: ../downloads/2025-03-17_13-06-32.mp4\n",
"✅ Updated: ../downloads/2025-03-17_12-55-42.mp4\n",
"✅ Updated: ../downloads/2025-03-17_12-55-05.mp4\n",
"✅ Updated: ../downloads/2025-03-15_17-26-31.mp4\n",
"✅ Updated: ../downloads/2025-03-15_17-10-23.mp4\n",
"✅ Updated: ../downloads/2025-03-15_14-49-27.jpg\n",
"✅ Updated: ../downloads/2025-03-15_01-17-10.mp4\n",
"✅ Updated: ../downloads/2025-03-12_00-50-08.mp4\n",
"✅ Updated: ../downloads/2025-03-12_00-49-19.mp4\n",
"✅ Updated: ../downloads/2025-03-12_00-33-16.mp4\n",
"✅ Updated: ../downloads/2025-03-12_00-30-44.jpg\n",
"✅ Updated: ../downloads/2025-03-12_00-30-10.jpg\n",
"✅ Updated: ../downloads/2025-03-10_00-56-31.mp4\n",
"✅ Updated: ../downloads/2025-03-08_06-20-39.mp4\n",
"✅ Updated: ../downloads/2025-03-08_06-20-39.mp4\n",
"✅ Updated: ../downloads/2025-03-08_06-18-34.mp4\n",
"✅ Updated: ../downloads/2025-03-06_20-27-01.mp4\n",
"✅ Updated: ../downloads/2025-03-06_14-02-07.mp4\n",
"✅ Updated: ../downloads/2025-03-05_18-21-48.jpg\n",
"✅ Updated: ../downloads/2025-03-05_18-20-54.jpg\n",
"✅ Updated: ../downloads/2025-03-05_05-46-33.mp4\n",
"✅ Updated: ../downloads/2025-03-05_05-39-17.mp4\n",
"✅ Updated: ../downloads/2025-03-05_05-26-55.mp4\n",
"✅ Updated: ../downloads/2025-03-05_03-32-33.mp4\n",
"✅ Updated: ../downloads/2025-03-04_19-48-15.mp4\n",
"✅ Updated: ../downloads/2025-03-03_19-59-16.mp4\n",
"✅ Updated: ../downloads/2025-03-02_19-34-00.jpg\n",
"✅ Updated: ../downloads/2025-03-02_19-33-30.jpg\n",
"✅ Updated: ../downloads/2025-02-28_18-26-33.mp4\n",
"✅ Updated: ../downloads/2025-02-28_18-25-46.mp4\n",
"✅ Updated: ../downloads/2025-02-27_05-01-59.mp4\n",
"✅ Updated: ../downloads/2025-02-26_01-35-44.jpg\n",
"✅ Updated: ../downloads/2025-02-25_00-13-39.mp4\n",
"✅ Updated: ../downloads/2025-02-24_03-55-48.jpg\n",
"✅ Updated: ../downloads/2025-02-24_00-13-46.jpg\n",
"✅ Updated: ../downloads/2025-02-23_23-16-19.jpg\n",
"✅ Updated: ../downloads/2025-02-23_19-23-34.jpg\n",
"✅ Updated: ../downloads/2025-02-22_17-41-15.mp4\n",
"✅ Updated: ../downloads/2025-02-22_17-28-12.mp4\n",
"✅ Updated: ../downloads/2025-02-22_17-27-00.mp4\n",
"✅ Updated: ../downloads/2025-02-22_17-21-48.jpg\n",
"✅ Updated: ../downloads/2025-02-22_17-21-48.jpg\n",
"✅ Updated: ../downloads/2025-02-22_17-21-39.jpg\n",
"✅ Updated: ../downloads/2025-02-22_17-11-36.mp4\n",
"✅ Updated: ../downloads/2025-02-22_17-08-10.mp4\n",
"✅ Updated: ../downloads/2025-02-21_22-00-03.jpg\n",
"✅ Updated: ../downloads/2025-02-18_13-43-25.mp4\n",
"✅ Updated: ../downloads/2025-02-18_13-40-28.mp4\n",
"✅ Updated: ../downloads/2025-02-17_14-01-53.mp4\n",
"✅ Updated: ../downloads/2025-02-16_00-14-59.mp4\n",
"✅ Updated: ../downloads/2025-02-16_00-09-41.mp4\n",
"✅ Updated: ../downloads/2025-02-16_00-06-41.mp4\n",
"✅ Updated: ../downloads/2025-02-16_00-06-01.mp4\n",
"✅ Updated: ../downloads/2025-02-15_01-18-42.mp4\n",
"✅ Updated: ../downloads/2025-02-14_20-35-53.mp4\n",
"✅ Updated: ../downloads/2025-02-14_14-32-27.jpg\n",
"✅ Updated: ../downloads/2025-02-13_13-46-28.mp4\n",
"✅ Updated: ../downloads/2025-02-13_04-16-24.jpg\n",
"✅ Updated: ../downloads/2025-02-13_04-16-24.jpg\n",
"✅ Updated: ../downloads/2025-02-12_23-07-00.jpg\n",
"✅ Updated: ../downloads/2025-02-12_22-59-55.jpg\n",
"✅ Updated: ../downloads/2025-02-12_18-35-15.jpg\n",
"✅ Updated: ../downloads/2025-02-11_15-01-59.mp4\n",
"✅ Updated: ../downloads/2025-02-11_00-42-09.jpg\n",
"✅ Updated: ../downloads/2025-02-10_03-35-28.jpg\n",
"✅ Updated: ../downloads/2025-02-10_03-35-17.jpg\n",
"✅ Updated: ../downloads/2025-02-08_22-39-58.jpg\n",
"✅ Updated: ../downloads/2025-02-08_18-28-36.jpg\n",
"✅ Updated: ../downloads/2025-02-08_17-12-11.mp4\n",
"✅ Updated: ../downloads/2025-02-07_23-27-03.mp4\n",
"✅ Updated: ../downloads/2025-02-07_17-13-16.jpg\n",
"✅ Updated: ../downloads/2025-02-07_17-06-06.jpg\n",
"✅ Updated: ../downloads/2025-02-02_00-26-16.jpg\n",
"✅ Updated: ../downloads/2025-02-01_20-44-49.mp4\n",
"✅ Updated: ../downloads/2025-02-01_20-06-54.jpg\n",
"✅ Updated: ../downloads/2025-02-01_19-01-42.mp4\n",
"✅ Updated: ../downloads/2025-01-31_02-17-52.mp4\n",
"✅ Updated: ../downloads/2025-01-31_02-14-42.mp4\n",
"✅ Updated: ../downloads/2025-01-30_22-52-59.jpg\n",
"✅ Updated: ../downloads/2025-01-30_21-32-32.jpg\n",
"⚠️ Warning: No timezone found for 0.0, 0.0. Defaulting to MDT/MST.\n",
"✅ Updated: ../downloads/2025-01-28_01-39-36.jpg\n",
"✅ Updated: ../downloads/2025-01-25_22-22-47.jpg\n",
"✅ Updated: ../downloads/2025-01-25_22-22-37.jpg\n",
"✅ Updated: ../downloads/2025-01-21_15-21-56.jpg\n",
"✅ Updated: ../downloads/2025-01-21_00-26-04.jpg\n",
"✅ Updated: ../downloads/2025-01-21_00-18-03.mp4\n",
"✅ Updated: ../downloads/2025-01-20_22-37-04.jpg\n",
"✅ Updated: ../downloads/2025-01-20_13-53-55.mp4\n",
"✅ Updated: ../downloads/2025-01-20_13-52-49.mp4\n",
"✅ Updated: ../downloads/2025-01-20_13-49-25.mp4\n",
"✅ Updated: ../downloads/2025-01-20_02-47-40.mp4\n"
]
},
{
"ename": "KeyboardInterrupt",
"evalue": "",
"output_type": "error",
"traceback": [
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
"\u001b[31mKeyboardInterrupt\u001b[39m Traceback (most recent call last)",
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[36]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;66;03m# img_errors.apply(update_exif_with_exiftool, axis=1) # type: ignore\u001b[39;00m\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[43mdf1\u001b[49m\u001b[43m.\u001b[49m\u001b[43mapply\u001b[49m\u001b[43m(\u001b[49m\u001b[43mupdate_exif_with_exiftool\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43maxis\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m1\u001b[39;49m\u001b[43m)\u001b[49m \u001b[38;5;66;03m# type: ignore\u001b[39;00m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/repositories/snap_memories/customized/snapchat-memories-downloader/.venv/lib/python3.13/site-packages/pandas/core/frame.py:10401\u001b[39m, in \u001b[36mDataFrame.apply\u001b[39m\u001b[34m(self, func, axis, raw, result_type, args, by_row, engine, engine_kwargs, **kwargs)\u001b[39m\n\u001b[32m 10387\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mpandas\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mcore\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mapply\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m frame_apply\n\u001b[32m 10389\u001b[39m op = frame_apply(\n\u001b[32m 10390\u001b[39m \u001b[38;5;28mself\u001b[39m,\n\u001b[32m 10391\u001b[39m func=func,\n\u001b[32m (...)\u001b[39m\u001b[32m 10399\u001b[39m kwargs=kwargs,\n\u001b[32m 10400\u001b[39m )\n\u001b[32m> \u001b[39m\u001b[32m10401\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mop\u001b[49m\u001b[43m.\u001b[49m\u001b[43mapply\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m.__finalize__(\u001b[38;5;28mself\u001b[39m, method=\u001b[33m\"\u001b[39m\u001b[33mapply\u001b[39m\u001b[33m\"\u001b[39m)\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/repositories/snap_memories/customized/snapchat-memories-downloader/.venv/lib/python3.13/site-packages/pandas/core/apply.py:916\u001b[39m, in \u001b[36mFrameApply.apply\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 913\u001b[39m \u001b[38;5;28;01melif\u001b[39;00m \u001b[38;5;28mself\u001b[39m.raw:\n\u001b[32m 914\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.apply_raw(engine=\u001b[38;5;28mself\u001b[39m.engine, engine_kwargs=\u001b[38;5;28mself\u001b[39m.engine_kwargs)\n\u001b[32m--> \u001b[39m\u001b[32m916\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mapply_standard\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/repositories/snap_memories/customized/snapchat-memories-downloader/.venv/lib/python3.13/site-packages/pandas/core/apply.py:1063\u001b[39m, in \u001b[36mFrameApply.apply_standard\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 1061\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mapply_standard\u001b[39m(\u001b[38;5;28mself\u001b[39m):\n\u001b[32m 1062\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m.engine == \u001b[33m\"\u001b[39m\u001b[33mpython\u001b[39m\u001b[33m\"\u001b[39m:\n\u001b[32m-> \u001b[39m\u001b[32m1063\u001b[39m results, res_index = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mapply_series_generator\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1064\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 1065\u001b[39m results, res_index = \u001b[38;5;28mself\u001b[39m.apply_series_numba()\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/repositories/snap_memories/customized/snapchat-memories-downloader/.venv/lib/python3.13/site-packages/pandas/core/apply.py:1081\u001b[39m, in \u001b[36mFrameApply.apply_series_generator\u001b[39m\u001b[34m(self)\u001b[39m\n\u001b[32m 1078\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m option_context(\u001b[33m\"\u001b[39m\u001b[33mmode.chained_assignment\u001b[39m\u001b[33m\"\u001b[39m, \u001b[38;5;28;01mNone\u001b[39;00m):\n\u001b[32m 1079\u001b[39m \u001b[38;5;28;01mfor\u001b[39;00m i, v \u001b[38;5;129;01min\u001b[39;00m \u001b[38;5;28menumerate\u001b[39m(series_gen):\n\u001b[32m 1080\u001b[39m \u001b[38;5;66;03m# ignore SettingWithCopy here in case the user mutates\u001b[39;00m\n\u001b[32m-> \u001b[39m\u001b[32m1081\u001b[39m results[i] = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43m*\u001b[49m\u001b[43m*\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mkwargs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1082\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(results[i], ABCSeries):\n\u001b[32m 1083\u001b[39m \u001b[38;5;66;03m# If we have a view on v, we need to make a copy because\u001b[39;00m\n\u001b[32m 1084\u001b[39m \u001b[38;5;66;03m# series_generator will swap out the underlying data\u001b[39;00m\n\u001b[32m 1085\u001b[39m results[i] = results[i].copy(deep=\u001b[38;5;28;01mFalse\u001b[39;00m)\n",
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[35]\u001b[39m\u001b[32m, line 149\u001b[39m, in \u001b[36mupdate_exif_with_exiftool\u001b[39m\u001b[34m(row)\u001b[39m\n\u001b[32m 146\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m 147\u001b[39m \u001b[38;5;66;03m# Run the command\u001b[39;00m\n\u001b[32m 148\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m file_type == \u001b[33m'\u001b[39m\u001b[33mVideo\u001b[39m\u001b[33m'\u001b[39m:\n\u001b[32m--> \u001b[39m\u001b[32m149\u001b[39m result = \u001b[43msubprocess\u001b[49m\u001b[43m.\u001b[49m\u001b[43mrun\u001b[49m\u001b[43m(\u001b[49m\u001b[43mmp4_command\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcheck\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcapture_output\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtext\u001b[49m\u001b[43m=\u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m)\u001b[49m\n\u001b[32m 150\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 151\u001b[39m result = subprocess.run(jpg_command, check=\u001b[38;5;28;01mTrue\u001b[39;00m, capture_output=\u001b[38;5;28;01mTrue\u001b[39;00m, text=\u001b[38;5;28;01mTrue\u001b[39;00m)\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/.pyenv/versions/3.13.11/lib/python3.13/subprocess.py:556\u001b[39m, in \u001b[36mrun\u001b[39m\u001b[34m(input, capture_output, timeout, check, *popenargs, **kwargs)\u001b[39m\n\u001b[32m 554\u001b[39m \u001b[38;5;28;01mwith\u001b[39;00m Popen(*popenargs, **kwargs) \u001b[38;5;28;01mas\u001b[39;00m process:\n\u001b[32m 555\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m556\u001b[39m stdout, stderr = \u001b[43mprocess\u001b[49m\u001b[43m.\u001b[49m\u001b[43mcommunicate\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m=\u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 557\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m TimeoutExpired \u001b[38;5;28;01mas\u001b[39;00m exc:\n\u001b[32m 558\u001b[39m process.kill()\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/.pyenv/versions/3.13.11/lib/python3.13/subprocess.py:1222\u001b[39m, in \u001b[36mPopen.communicate\u001b[39m\u001b[34m(self, input, timeout)\u001b[39m\n\u001b[32m 1219\u001b[39m endtime = \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[32m 1221\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m-> \u001b[39m\u001b[32m1222\u001b[39m stdout, stderr = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_communicate\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43minput\u001b[39;49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mendtime\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 1223\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mKeyboardInterrupt\u001b[39;00m:\n\u001b[32m 1224\u001b[39m \u001b[38;5;66;03m# https://bugs.python.org/issue25942\u001b[39;00m\n\u001b[32m 1225\u001b[39m \u001b[38;5;66;03m# See the detailed comment in .wait().\u001b[39;00m\n\u001b[32m 1226\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m timeout \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/.pyenv/versions/3.13.11/lib/python3.13/subprocess.py:2154\u001b[39m, in \u001b[36mPopen._communicate\u001b[39m\u001b[34m(self, input, endtime, orig_timeout)\u001b[39m\n\u001b[32m 2147\u001b[39m \u001b[38;5;28mself\u001b[39m._check_timeout(endtime, orig_timeout,\n\u001b[32m 2148\u001b[39m stdout, stderr,\n\u001b[32m 2149\u001b[39m skip_check_and_raise=\u001b[38;5;28;01mTrue\u001b[39;00m)\n\u001b[32m 2150\u001b[39m \u001b[38;5;28;01mraise\u001b[39;00m \u001b[38;5;167;01mRuntimeError\u001b[39;00m( \u001b[38;5;66;03m# Impossible :)\u001b[39;00m\n\u001b[32m 2151\u001b[39m \u001b[33m'\u001b[39m\u001b[33m_check_timeout(..., skip_check_and_raise=True) \u001b[39m\u001b[33m'\u001b[39m\n\u001b[32m 2152\u001b[39m \u001b[33m'\u001b[39m\u001b[33mfailed to raise TimeoutExpired.\u001b[39m\u001b[33m'\u001b[39m)\n\u001b[32m-> \u001b[39m\u001b[32m2154\u001b[39m ready = \u001b[43mselector\u001b[49m\u001b[43m.\u001b[49m\u001b[43mselect\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 2155\u001b[39m \u001b[38;5;28mself\u001b[39m._check_timeout(endtime, orig_timeout, stdout, stderr)\n\u001b[32m 2157\u001b[39m \u001b[38;5;66;03m# XXX Rewrite these to use non-blocking I/O on the file\u001b[39;00m\n\u001b[32m 2158\u001b[39m \u001b[38;5;66;03m# objects; they are no longer using C stdio!\u001b[39;00m\n",
"\u001b[36mFile \u001b[39m\u001b[32m~/.pyenv/versions/3.13.11/lib/python3.13/selectors.py:398\u001b[39m, in \u001b[36m_PollLikeSelector.select\u001b[39m\u001b[34m(self, timeout)\u001b[39m\n\u001b[32m 396\u001b[39m ready = []\n\u001b[32m 397\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m--> \u001b[39m\u001b[32m398\u001b[39m fd_event_list = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_selector\u001b[49m\u001b[43m.\u001b[49m\u001b[43mpoll\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 399\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mInterruptedError\u001b[39;00m:\n\u001b[32m 400\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m ready\n",
"\u001b[31mKeyboardInterrupt\u001b[39m: "
]
}
],
"source": [
"# img_errors.apply(update_exif_with_exiftool, axis=1) # type: ignore\n",
"df1.apply(update_exif_with_exiftool, axis=1) # type: ignore"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.13.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}