Flickr JSON to EXIF

Flickr
Purpose:

This program takes Flickr JSON files and the Flickr photos’ directory and appends a photo's tags to the end of its filename and any overflow tags to that photo's description. It can also update files with the upload date recorded in the Flickr JSON data.

Dependencies

Python (with os and json modules), ExifTool

# NOTES
# Comment out lines 38-44 and 47-54 for the first run (those files will not be written until the first run).
# There should be a lot of blue "Empytying Description" printed into the terminal at first, as it has to clear each photo's
# description (usually over-stating the camera name)
# Neon Pink is when the program adds overflow tags
# Red notates when an error occurs

# REQUIRES EXIFTOOL INSTALLED
import json
import os

# These Varables could likely change - if you want to redate the photos with the Flickr upload date, change redate_photos to True.
directory = "#DIRECTORY_OF_ALL_JSON_FILES#"
photo_directory = "#DIRECTORY_OF_ALL_PHOTOS#"
redate_photos = False

# Initialized Variables
global num_photos                   # Keeps track of the number of photos that enter the editing phase
num_photos = 0
global description_count            # Keeps track of the number of descriptions that are added
description_count = 0
tag_sheet = "All_Tags.csv"          # Stores all of the the different tag strings
error_sheet = "errors.csv"          # Keeps track of errors
tag_string = "tag_string.txt"       # Keeps the tag_string stored outside of the program in case it is run more than once
tag_count = "tag_count.txt"         # Keeps the tag_count stored outside of the program in case it is run more than once
date_sheet = "changed_dates.csv"    # Keeps track of changed dates



# -----PREP-----
# Opens error and tag_sheet csvs to append the photos edited and errors encountered.
f = open(tag_sheet, "w")
e = open(error_sheet, "w")
d = open(date_sheet, "w")


# Opens the tag_string and saves its value to a varible, then closes and reopens it for writing at the end.
t = open(tag_string, "r")
lines = str(t.readlines())
lines = lines[2:-2]
global all_tags
all_tags = lines
t.close()
t = open(tag_string, "w")

# Opens the tag_count and saves its value to a varible, then closes and reopens it for writing at the end.
g = open(tag_count, "r")
lines = str(g.readlines())
lines = lines[2:-2]
if (lines == ""):   # in case the program is quit unexpectedly
   all_tags_num = 0
else:
   all_tags_num = int(lines)
g.close()
g = open(tag_count, "w")



# Adds tags to the end of the file name of a given json file's corresponding photo and any overflow tags to that
# photo's description. It also clears the photo's description regardless of whether it has overflow tags.
def write(tags, overflow_tags, file):
   # Extracts the identifier from the json file name
   file_string = str(file)
   file_start = file_string.find("<DirEntry \'photo_") + len("<DirEntry \'photo_")
   temp_file = file_string[file_start:]
   file_end = temp_file.find(".json\'>")
   file_final = temp_file[:file_end]

   # Gets the path of of target photo (based on the identifier/file_final) in photo_directory and saves it to file_path
   file_path = find_file(file_final)

   # If find_file found a file, it adds one to num_photos and starts the editing process
   if (file_path != "NA" and "#" not in file_path):
       global num_photos
       num_photos += 1
       # Tries to edit the photo, and adds the photo name to the error sheet if it fails along the way
       try:
           # Adds the tags to the end of the photo's file name as long as it is a jpg or png.
           # Only saves it to a variable at this point.

           if ".jpg" in str(file_path):
               jpg_end = file_path.find(".jpg")
               file_path_wo_jpg = file_path[:jpg_end]
               new_file_path = file_path_wo_jpg + "_" + tags + ".jpg"
           if ".png" in str(file_path):
               png_end = file_path.find(".png")
               file_path_wo_png = file_path[:png_end]
               new_file_path = file_path_wo_png + "_" + tags + ".png"


           # If overflow_tags does NOT contain nothing, it adds one to the description_count and writes the description to
           # all of the image's description types.
           if (overflow_tags != ""):
               global description_count
               description_count += 1
               overflow_tags_string = "Overflow Tags: " + str(overflow_tags)
               bashCommand = "Exiftool -IPTC:Caption-Abstract=\'" + overflow_tags_string + "\' -imagedescription=\'" + overflow_tags_string + "\' -xmp:description=\'" + overflow_tags_string + "\' -overwrite_original " + file_path
               print('\33[45m' + "     ADDING OVERFLOW TAGS     " + '\x1b[0m')
               os.system(bashCommand)

           if (redate_photos == True):
               # Prints what it is doing to terminal
               print("Changing "+ str(identifier) + "'s date to", date)
               # Adds one to "date_count"
               global date_count
               date_count += 1
               # Calls terminal to execute Exiftool to change the photo's original date, create date, modified date, and
               # file modified date to the date given to the function.
               bashCommand = "Exiftool \"-datetimeoriginal=" + date + "\" \"-CreateDate=" + date + "\" \"-ModifyDate=" + date + "\" \"-filemodifydate=" + date + "\" -overwrite_original " + file_path
               print('\x1b[6;30;42m' + "CORRECTING DATE" + '\x1b[0m')
               os.system(bashCommand)
               # Writes to the changed_dates csv to keep track of the edited photos
               d.write(identifier + ", " + date + " \n")

           # Renames the photo to the new_file_path including all of the tags
           os.path.join(os.path.dirname(file_path), new_file_path)
           os.rename(file_path, new_file_path)
       except:
           # Makes sure you know there's been an error
           print('\33[101m' + "*****ERROR*****" + '\x1b[0m')
           e.write(file_final + "\n")

# Finds and returns a file in photo_directory given an identifier. If it finds nothing, it returns "NA"
def find_file(identifier):
   for fname in os.listdir(photo_directory):
       if identifier in str(fname):
           full_path = os.path.join(photo_directory, fname)
           if os.path.isfile(full_path):
               return(full_path)
           break
   return("NA")



# -----START-----
# Finds all files in the json directory
for entry in os.scandir(directory):
   # Checks to make sure the json is about a photo
   if "photo_" in str(entry):
       # If so, it opens the json file initializes/clears some variables
       j = open(entry)
       data = json.load(j)
       tag_count = 0
       tags = ""
       overflow_tags = ""
       overflow_count = 0
       # Iterates though each tag in the json data
       for i in data['tags']:
           # Adds one to the tag_count for future reference
           tag_count += 1
           # Grabs tag text and puts it into a string "tag"
           string = str(i)
           if "\'tag\'" in string:
               tag_start = string.find("\'tag\': \'") + len("\'tag\': \'")
               temp_tag = string[tag_start:]
               tag_end = temp_tag.find("\', \'user\':")
               tag = (temp_tag[:tag_end])
               # Removes spaces for insterting into file name
               tag = ''.join(tag.split())
               # Adds tag to a string until the total string length is 220, whereafter it adds it into a sepatate
               # list for overflow tags. (purpose - title names can only be 250 chacters, so this allows a 30-chracter
               # tolerance to ensure it does not later try and write too many characters into the file name.)
               if tag_count == 1:
                   tags += "#" + tag
               elif len(tags)+len(tag)+2 < 200:
                   tags += "_#" + tag
               else:
                   overflow_count += 1
                   if overflow_count == 1:
                       overflow_tags += "#" + tag
                   else:
                       overflow_tags += "_#" + tag
               # Writes all new tag names to a csv for later manipulation and use
               if tag not in all_tags:
                   all_tags_num += 1
                   if (all_tags != ""):
                       all_tags += ", #" + tag
                       f.write(str(all_tags_num) + "," + tag.replace(",", "|") + "\n")
                   else:
                       all_tags += "#" + tag
                       f.write(str(all_tags_num) + "," + tag.replace(",", "|") + "\n")
       # Prints if any overflow tags were found
       if (overflow_tags != ""):
           print("Overflow Tags: " + str(overflow_tags))
       # Calls the write funtion to write tags to the proper photo's file name and any overflow_tags to the photo's description
       write(tags, overflow_tags, entry)
       # Closes the json file
       j.close()
   # Skips json files that do not realte to a photo
   else:
       continue

# -----END-----
# Writes the all_tags and all_tags_num to two text files so they can be kept track of in future runs of this program
t.write(all_tags)
g.write(str(all_tags_num))

# Closes all of the files opened at the begining
f.close()
e.close()
t.close()

# Prints what was completed at the end, as recorded by the vaious counts.
print('\33[100m' + "----- " + str(num_photos) + " edited,", description_count, "descriptions added" + '\x1b[0m')