pasted ChatGPT suggestions

This commit is contained in:
lmluk
2024-09-09 13:37:15 +02:00
parent 282e395ba3
commit ba8982cfe4

293
main.py
View File

@@ -2,203 +2,158 @@ import json
import os import os
from exiftool import ExifToolHelper as et from exiftool import ExifToolHelper as et
from shutil import copy2 as cp from shutil import copy2 as cp
import datetime
from datetime import datetime as dt from datetime import datetime as dt
import argparse import argparse
from PIL import Image, ImageDraw, ImageFont
def init_parser(): def init_parser() -> argparse.Namespace:
""" """
Initializes the argparse module Initializes the argparse module.
""" """
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter) parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter)
parser.add_argument('-t', '--timespan', type=str, help="Exports the given timespan\n"\ parser.add_argument('-t', '--timespan', type=str, help="Exports the given timespan\n"
"Valid format: 'DD.MM.YYYY-DD.MM.YYYY'\n"\ "Valid format: 'DD.MM.YYYY-DD.MM.YYYY'\n"
"Wildcards can be used: 'DD.MM.YYYY-*'") "Wildcards can be used: 'DD.MM.YYYY-*'")
parser.add_argument('-y', '--year', type=int, help="Exports the given year") parser.add_argument('-y', '--year', type=int, help="Exports the given year")
parser.add_argument('-p', '--path', type=str, help="Set a custom output path (default ./out)") parser.add_argument('-p', '--path', type=str, help="Set a custom output path (default ./out)")
parser.add_argument('-v', '--verbose', action='store_true', default=False, help="Explain what is being done") parser.add_argument('-v', '--verbose', action='store_true', default=False, help="Explain what is being done")
parser.add_argument('--no-memories', action='store_false', default=True, dest='memories', help="Don't export the memories") parser.add_argument('--no-memories', action='store_false', default=True, dest='memories', help="Don't export the memories")
parser.add_argument('--no-realmojis', action='store_false', default=True, dest='realmojis', help="Don't export the realmojis") parser.add_argument('--no-realmojis', action='store_false', default=True, dest='realmojis', help="Don't export the realmojis")
args = parser.parse_args() args = parser.parse_args()
if args.year and args.timespan: if args.year and args.timespan:
print("Timespan argument will be prioritized") print("Timespan argument will be prioritized")
return args return args
def init_global_var(args: argparse.Namespace): class BeRealExporter:
""" def __init__(self, args: argparse.Namespace):
Initializes global variables self.time_span = self.init_time_span(args)
""" self.out_path = args.path.strip().removesuffix('/') if args.path else "./out"
global time_span self.verbose = args.verbose
global out_path @staticmethod
global verbose def init_time_span(args: argparse.Namespace) -> tuple:
# Initialize time_span
if args.timespan:
temp_times = args.timespan.strip().split("-")
time_span = (dt.fromtimestamp(0) if temp_times[0] == '*' else dt.strptime(temp_times[0], '%d.%m.%Y'),
dt.now() if temp_times[1] == '*' else dt.strptime(temp_times[1], '%d.%m.%Y'))
elif args.year:
time_span = (dt(args.year, 1, 1), dt(args.year, 12, 31))
else:
time_span = (dt.fromtimestamp(0), dt.now())
# Initialize out_path
if args.path:
out_path = args.path.strip().removesuffix('/')
else:
out_path = "./out"
verbose = args.verbose
def verbose_msg(msg: str):
"""
Prints an explanation what is being done to the terminal
"""
if verbose: print(msg)
def printProgressBar (iteration: int, total: int, prefix: str='', suffix: str='', decimals: str=1, length: int=60, fill: str='', printEnd: str="\r"):
""" """
Call in a loop to create terminal progress bar Initializes time span based on the arguments.
Not my creation: https://stackoverflow.com/questions/3173320/text-progress-bar-in-terminal-with-block-characters """
@params: if args.timespan:
iteration - Required : current iteration (Int) start_str, end_str = args.timespan.strip().split("-")
total - Required : total iterations (Int) start = dt.fromtimestamp(0) if start_str == '*' else dt.strptime(start_str, '%d.%m.%Y')
prefix - Optional : prefix string (Str) end = dt.now() if end_str == '*' else dt.strptime(end_str, '%d.%m.%Y')
suffix - Optional : suffix string (Str) return start, end
decimals - Optional : positive number of decimals in percent complete (Int) elif args.year:
length - Optional : character length of bar (Int) return dt(args.year, 1, 1), dt(args.year, 12, 31)
fill - Optional : bar fill character (Str) else:
printEnd - Optional : end character (e.g. "\r", "\r\n") (Str) return dt.fromtimestamp(0), dt.now()
def verbose_msg(self, msg: str):
"""
Prints an explanation of what is being done to the terminal.
"""
if self.verbose:
print(msg)
@staticmethod
def print_progress_bar(iteration: int, total: int, prefix: str = '', suffix: str = '', decimals: int = 1, length: int = 60, fill: str = '', print_end: str = "\r"):
"""
Call in a loop to create terminal progress bar.
""" """
percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total))) percent = ("{0:." + str(decimals) + "f}").format(100 * (iteration / float(total)))
filledLength = int(length * iteration // total) filled_length = int(length * iteration // total)
bar = fill * filledLength + '-' * (length - filledLength) bar = fill * filled_length + '-' * (length - filled_length)
print(f'\r{prefix} |{bar}| {percent}% {suffix}', end = printEnd) print(f'\r{prefix} |{bar}| {percent}% {suffix}', end=print_end)
# Print New Line on Complete if iteration == total:
if iteration == total:
print() print()
@staticmethod
def get_img_filename(image: dict) -> str:
def get_img_filename(image: json): """
""" Returns the image filename from an image object (frontImage, backImage, primary, secondary).
Returns the image filename from a image object (frontImage, backImage, primary, secondary) """
""" return image['path'].split("/")[-1]
return image['path'].split("/")[-1] @staticmethod
def get_datetime_from_str(time: str) -> dt:
"""
def get_datetime_from_str(time: str): Returns a datetime object from a time key.
""" """
Returns a datetime object form a time key format_string = "%Y-%m-%dT%H:%M:%S.%fZ"
""" return dt.strptime(time, format_string)
format_string = "%Y-%m-%dT%H:%M:%S.%fZ" def export_img(self, old_img_name: str, img_name: str, img_dt: dt, img_location=None):
return dt.strptime(time, format_string) """
Makes a copy of the image and adds EXIF tags to the image.
"""
def export_img(old_img_name: str, img_name: str, img_dt: datetime, img_location=None): self.verbose_msg(f"Export {old_img_name} image to {img_name}")
""" cp(old_img_name, img_name)
Makes a copy of the image and adds exif tags to the image tags = {"DateTimeOriginal": img_dt.strftime("%Y:%m:%d %H:%M:%S")}
""" if img_location:
self.verbose_msg(f"Add metadata to image:\n - DateTimeOriginal={img_dt}\n - GPS=({img_location['latitude']}, {img_location['longitude']})")
# Makes a copy of the images tags.update({
verbose_msg("Export %s image to %s" % (old_img_name, img_name)) "GPSLatitude*": img_location['latitude'],
cp(old_img_name, img_name) "GPSLongitude*": img_location['longitude']
})
# Add metadata to the images with or without location
if img_location:
verbose_msg("Add metadata to image:\n - DateTimeOriginal=%s\n - GPS=(%s, %s)" % (img_dt, img_location['latitude'], img_location['longitude']))
et().set_tags(img_name,
tags={"DateTimeOriginal": img_dt.strftime("%Y:%m:%d %H:%M:%S"),
"GPSLatitude*": img_location['latitude'],
"GPSLongitude*": img_location['longitude']},
params=["-P", "-overwrite_original"])
else:
verbose_msg("Add metadata to image:\n - DateTimeOriginal=%s" % img_dt)
et().set_tags(img_name,
tags={"DateTimeOriginal": img_dt.strftime("%Y:%m:%d %H:%M:%S")},
params=["-P", "-overwrite_original"])
def export_memories(memories: json):
"""
Exports all memories from the Photos/post directory to the corresponding output folder
"""
out_path_memories = out_path + "/memories"
memory_count = len(memories)
if not os.path.exists(out_path_memories):
verbose_msg("Create %s folder for memories output" % out_path_memories)
os.makedirs(out_path_memories)
for i, n in zip(memories, range(memory_count)):
memory_dt = get_datetime_from_str(i['takenTime'])
types = [('frontImage', 'webp'), ('backImage', 'webp')]
if 'btsMedia' in i:
types.append(('btsMedia', 'mp4'))
img_names = ["%s/%s_%s.%s" % (out_path_memories, memory_dt.strftime('%Y-%m-%d_%H-%M-%S'), type[0].removesuffix('Image').removesuffix('Media'), type[1])
for type in types]
# Checks if the memory is in the time span
if time_span[0] <= memory_dt <= time_span[1]:
for img_name, type in zip(img_names, types):
old_img_name = "./Photos/post/" + get_img_filename(i[type[0]])
verbose_msg("\nExport Memory nr %s %s:" % (n, type[0]))
if 'location' in i:
export_img(old_img_name, img_name, memory_dt, i['location'])
else:
export_img(old_img_name, img_name, memory_dt)
if verbose:
printProgressBar(n+1, memory_count, prefix="Exporting Memories", suffix=("- " + memory_dt.strftime("%Y-%m-%d")), printEnd='\n')
else: else:
printProgressBar(n+1, memory_count, prefix="Exporting Memories", suffix=("- " + memory_dt.strftime("%Y-%m-%d"))) self.verbose_msg(f"Add metadata to image:\n - DateTimeOriginal={img_dt}")
et().set_tags(img_name, tags=tags, params=["-P", "-overwrite_original"])
def export_memories(self, memories: list):
def export_realmojis(realmojis: json): """
""" Exports all memories from the Photos/post directory to the corresponding output folder.
Exports all realmojis from the Photos/realmoji directory to the corresponding output folder """
""" out_path_memories = os.path.join(self.out_path, "memories")
realmoji_count = len(realmojis) memory_count = len(memories)
out_path_realmojis = out_path + "/realmojis" if not os.path.exists(out_path_memories):
self.verbose_msg(f"Create {out_path_memories} folder for memories output")
if not os.path.exists(out_path_realmojis): os.makedirs(out_path_memories)
verbose_msg("Create %s folder for memories output" % out_path_realmojis) for n, memory in enumerate(memories):
os.makedirs(out_path_realmojis) memory_dt = self.get_datetime_from_str(memory['takenTime'])
types = [('frontImage', 'webp'), ('backImage', 'webp')]
for i, n in zip(realmojis, range(realmoji_count)): if 'btsMedia' in memory:
realmoji_dt = get_datetime_from_str(i['postedAt']) types.append(('btsMedia', 'mp4'))
img_name = "%s/%s.webp" % (out_path_realmojis, realmoji_dt.strftime('%Y-%m-%d_%H-%M-%S')) img_names = [f"{out_path_memories}/{memory_dt.strftime('%Y-%m-%d_%H-%M-%S')}_{t[0].removesuffix('Image').removesuffix('Media')}.{t[1]}" for t in types]
if self.time_span[0] <= memory_dt <= self.time_span[1]:
# Checks if the realmojis is in the time span for img_name, type in zip(img_names, types):
if time_span[0] <= realmoji_dt <= time_span[1] and i['isInstant']: old_img_name = f"./Photos/post/{self.get_img_filename(memory[type[0]])}"
verbose_msg("\nExport Memory nr %s:" % n) self.verbose_msg(f"\nExport Memory nr {n} {type[0]}:")
export_img("./Photos/realmoji/" + get_img_filename(i['media']), img_name, realmoji_dt) if 'location' in memory:
self.export_img(old_img_name, img_name, memory_dt, memory['location'])
if verbose: else:
printProgressBar(n+1, realmoji_count, prefix="Exporting Realmojis", suffix=("- Current Date: %s" % realmoji_dt.strftime("%Y-%m-%d")), printEnd='\n') self.export_img(old_img_name, img_name, memory_dt)
else: self.print_progress_bar(n + 1, memory_count, prefix="Exporting Memories", suffix=f"- {memory_dt.strftime('%Y-%m-%d')}")
printProgressBar(n+1, realmoji_count, prefix="Exporting Realmojis", suffix=("- Current Date: %s" % realmoji_dt.strftime("%Y-%m-%d"))) def export_realmojis(self, realmojis: list):
"""
Exports all realmojis from the Photos/realmoji directory to the corresponding output folder.
"""
realmoji_count = len(realmojis)
out_path_realmojis = os.path.join(self.out_path, "realmojis")
if not os.path.exists(out_path_realmojis):
self.verbose_msg(f"Create {out_path_realmojis} folder for memories output")
os.makedirs(out_path_realmojis)
for n, realmoji in enumerate(realmojis):
realmoji_dt = self.get_datetime_from_str(realmoji['postedAt'])
img_name = f"{out_path_realmojis}/{realmoji_dt.strftime('%Y-%m-%d_%H-%M-%S')}.webp"
if self.time_span[0] <= realmoji_dt <= self.time_span[1] and realmoji['isInstant']:
self.verbose_msg(f"\nExport Realmoji nr {n}:")
self.export_img(f"./Photos/realmoji/{self.get_img_filename(realmoji['media'])}", img_name, realmoji_dt)
self.print_progress_bar(n + 1, realmoji_count, prefix="Exporting Realmojis", suffix=f"- Current Date: {realmoji_dt.strftime('%Y-%m-%d')}")
if __name__ == '__main__': if __name__ == '__main__':
args = init_parser() args = init_parser()
init_global_var(args) exporter = BeRealExporter(args)
if args.memories: if args.memories:
verbose_msg("Open memories.json file") exporter.verbose_msg("Open memories.json file")
with open('memories.json', encoding='utf-8') as memories: try:
verbose_msg("Start exporting memories") with open('memories.json', encoding='utf-8') as memories_file:
export_memories(json.load(memories)) exporter.verbose_msg("Start exporting memories")
exporter.export_memories(json.load(memories_file))
except FileNotFoundError:
print("memories.json file not found.")
except json.JSONDecodeError:
print("Error decoding memories.json file.")
if args.realmojis: if args.realmojis:
verbose_msg("Open realmojis.json file") exporter.verbose_msg("Open realmojis.json file")
with open('realmojis.json', encoding='utf-8') as realmojis: try:
verbose_msg("Start exporting realmojis") with open('realmojis.json', encoding='utf-8') as realmojis_file:
export_realmojis(json.load(realmojis)) exporter.verbose_msg("Start exporting realmojis")
exporter.export_realmojis(json.load(realmojis_file))
except FileNotFoundError:
print("realmojis.json file not found.")
except json.JSONDecodeError:
print("Error decoding realmojis.json file.")