hot fixes for moving package into main()

This commit is contained in:
2026-02-08 12:47:26 +09:00
parent d771da77e1
commit 34f5ce14bc

View File

@@ -20,6 +20,7 @@ import re
import sqlite3
from shutil import copyfile, get_terminal_size
from math import ceil, radians, sin, cos, atan2, sqrt
from typing import Any
import requests
# Note XMPFiles does not work with sidecar files, need to read via XMPMeta
from libxmp import XMPMeta, consts
@@ -30,7 +31,9 @@ from libxmp import XMPMeta, consts
# this is for looking up if string is non latin letters
# this is used by isLatin and onlyLatinChars
cache_latin_letters = {}
cache_latin_letters: dict[str, Any] = {}
page_no = 1
# ARGPARSE HELPERS
@@ -54,7 +57,9 @@ class writable_dir_folder(argparse.Action):
# and write that list back to the self.dest in the namespace
setattr(namespace, self.dest, out)
else:
raise argparse.ArgumentTypeError("writable_dir_folder: {0} is not a writable dir".format(prospective_dir))
raise argparse.ArgumentTypeError(
"writable_dir_folder: {0} is not a writable dir".format(prospective_dir)
)
# call: readable_dir
@@ -90,7 +95,7 @@ class distance_values(argparse.Action):
# PARAMS: latitude, longitude, map search target (google or openstreetmap)
# RETURN: dict with all data (see below)
# DESC : wrapper to call to either the google or openstreetmap
def reverseGeolocate(longitude, latitude, map_type):
def reverseGeolocate(args, longitude, latitude, map_type):
# clean up long/lat
# they are stored with N/S/E/W if they come from an XMP
# format: Deg,Min.Sec[NSEW]
@@ -99,9 +104,13 @@ def reverseGeolocate(longitude, latitude, map_type):
lat_long = longLatReg(longitude=longitude, latitude=latitude)
# which service to use
if map_type == 'google':
return reverseGeolocateGoogle(lat_long['longitude'], lat_long['latitude'])
return reverseGeolocateGoogle(
args, lat_long['longitude'], lat_long['latitude']
)
elif map_type == 'openstreetmap':
return reverseGeolocateOpenStreetMap(lat_long['longitude'], lat_long['latitude'])
return reverseGeolocateOpenStreetMap(
args, lat_long['longitude'], lat_long['latitude']
)
else:
return {
'Country': '',
@@ -141,7 +150,7 @@ def reverseGeolocateInit(longitude, latitude):
# dict with locaiton, city, state, country, country code
# if not fillable, entry is empty
# SAMPLE: https://nominatim.openstreetmap.org/reverse.php?format=jsonv2&lat=<latitude>&lon=<longitude>&zoom=21&accept-languge=en-US,en&
def reverseGeolocateOpenStreetMap(longitude, latitude):
def reverseGeolocateOpenStreetMap(args, longitude, latitude):
# init
geolocation = reverseGeolocateInit(longitude, latitude)
if geolocation['status'] == 'ERROR':
@@ -201,7 +210,7 @@ def reverseGeolocateOpenStreetMap(longitude, latitude):
# dict with location, city, state, country, country code
# if not fillable, entry is empty
# SAMPLE: http://maps.googleapis.com/maps/api/geocode/json?latlng=<latitude>,<longitude>&language=<lang>&sensor=false&key=<api key>
def reverseGeolocateGoogle(longitude, latitude): # noqa: C901
def reverseGeolocateGoogle(args, longitude, latitude): # noqa: C901
# init
geolocation = reverseGeolocateInit(longitude, latitude)
temp_geolocation = geolocation.copy()
@@ -226,7 +235,7 @@ def reverseGeolocateGoogle(longitude, latitude): # noqa: C901
payload['key'] = args.google_api_key
# build the full url and send it to google
url = "{protocol}{base}".format(protocol=protocol, base=base)
response = requests.get(url, params=payload)
response = requests.get(url, params=payload, timeout=10)
# debug output
if args.debug:
print("Google search for Lat: {}, Long: {} with {}".format(longitude, latitude, response.url))
@@ -385,7 +394,7 @@ def getDistance(from_longitude, from_latitude, to_longitude, to_latitude):
# 2) data is set or not and field_control: overwrite only set
# 3) data for key is not set, but only for key matches field_control
# 4) data for key is set or not, but only for key matches field_control and overwrite is set
def checkOverwrite(data, key, field_controls):
def checkOverwrite(args, data, key, field_controls):
status = False
# init field controls for empty
if not field_controls:
@@ -528,7 +537,7 @@ def fileSortNumber(file):
# PARAMS: none
# RETURN: format_length dictionary
# DESC : adjusts the size for the format length for the list output
def outputListWidthAdjust():
def outputListWidthAdjust(args):
# various string lengths
format_length = {
'filename': 35,
@@ -545,7 +554,10 @@ def outputListWidthAdjust():
reduce_percent = 40
# all formats are reduced to a mininum, we cut % off
for format_key in ['filename', 'latitude', 'longitude', 'country', 'state', 'city', 'location', 'path']:
format_length[format_key] = ceil(format_length[format_key] - ((format_length[format_key] / 100) * reduce_percent))
format_length[format_key] = ceil(
format_length[format_key] -
((format_length[format_key] / 100) * reduce_percent)
)
else:
# minimum resize size for a column
resize_width_min = 4
@@ -583,10 +595,17 @@ def outputListWidthAdjust():
resize_width *= -1
resize_width = ceil(format_length[format_key] + resize_width)
# in case too small, keep old one
format_length[format_key] = resize_width if resize_width > resize_width_min else format_length[format_key]
format_length[format_key] = (
resize_width
if resize_width > resize_width_min
else format_length[format_key]
)
# calc new width for check if we can abort
current_columns = sum(format_length.values()) + ((len(format_length) - 1) * 3) + 2
if (resize == 1 and current_columns >= get_terminal_size().columns) or (resize == -1 and current_columns < get_terminal_size().columns):
if (
(resize == 1 and current_columns >= get_terminal_size().columns) or
(resize == -1 and current_columns < get_terminal_size().columns)
):
# check that we are not OVER but one under
width_up = get_terminal_size().columns - current_columns - 1
if (resize == 1 and width_up < 0) or (resize == -1 and width_up != 0):
@@ -605,7 +624,7 @@ def outputListWidthAdjust():
# PARAMS: file name
# RETURN: next counter to be used for backup
# DESC :
def getBackupFileCounter(xmp_file):
def getBackupFileCounter(args, xmp_file):
# set to 1 for if we have no backups yet
bk_file_counter = 1
# get PATH from file and look for .BK. data in this folder matching, output is sorted per BK counter key
@@ -635,9 +654,9 @@ def getBackupFileCounter(xmp_file):
# ARGUMENT PARSNING
##############################################################
def main():
"""main"""
parser = argparse.ArgumentParser(
description='Reverse Geoencoding based on set Latitude/Longitude data in XMP files',
# formatter_class=argparse.RawDescriptionHelpFormatter,
@@ -700,10 +719,12 @@ def main():
choices=['overwrite', 'location', 'city', 'state', 'country', 'countrycode'],
dest='field_controls',
metavar='<overwrite, location, city, state, country, countrycode>',
help='On default only set fields that are not set yet. Options are: '\
'Overwrite (write all new), Location, City, State, Country, CountryCode. '\
'Multiple can be given for combination overwrite certain fields only or set only certain fields. '\
help=(
'On default only set fields that are not set yet. Options are: '
'Overwrite (write all new), Location, City, State, Country, CountryCode. '
'Multiple can be given for combination overwrite certain fields only or set only certain fields. '
'If with overwrite the field will be overwritten if already set, else it will be always skipped.'
)
)
parser.add_argument(
@@ -715,9 +736,11 @@ def main():
const='10m', # default is 10m
dest='fuzzy_distance',
metavar='FUZZY DISTANCE',
help='Allow fuzzy distance cache lookup. Optional distance can be given, '\
'if not set default of 10m is used. '\
help=(
'Allow fuzzy distance cache lookup. Optional distance can be given, '
'if not set default of 10m is used. '
'Allowed argument is in the format of 12m or 12km'
)
)
# Google Maps API key to overcome restrictions
@@ -842,25 +865,30 @@ def main():
args.unset_only = 0
if args.debug:
print("### ARGUMENT VARS: I: {incl}, X: {excl}, L: {lr}, F: {fc}, D: {fdist}, M: {osm}, G: {gp}, E: {em}, R: {read}, U: {us}, A: {adj}, C: {cmp}, N: {nbk}, W: {wrc}, V: {v}, D: {d}, T: {t}".format(
incl=args.xmp_sources,
excl=args.exclude_sources,
lr=args.lightroom_folder,
fc=args.field_controls,
fdist=args.fuzzy_distance,
osm=args.use_openstreetmap,
gp=args.google_api_key,
em=args.email,
read=args.read_only,
us=args.unset_only,
adj=args.no_autoadjust,
cmp=args.compact_view,
nbk=args.no_xmp_backup,
wrc=args.config_write,
v=args.verbose,
d=args.debug,
t=args.test
))
print(
"### ARGUMENT VARS: I: {incl}, X: {excl}, L: {lr}, F: {fc}, "
"D: {fdist}, M: {osm}, G: {gp}, E: {em}, R: {read}, U: {us}, "
"A: {adj}, C: {cmp}, N: {nbk}, W: {wrc}, V: {v}, D: {d}, T: {t}"
.format(
incl=args.xmp_sources,
excl=args.exclude_sources,
lr=args.lightroom_folder,
fc=args.field_controls,
fdist=args.fuzzy_distance,
osm=args.use_openstreetmap,
gp=args.google_api_key,
em=args.email,
read=args.read_only,
us=args.unset_only,
adj=args.no_autoadjust,
cmp=args.compact_view,
nbk=args.no_xmp_backup,
wrc=args.config_write,
v=args.verbose,
d=args.debug,
t=args.test
)
)
# error flag
error = False
@@ -1033,6 +1061,9 @@ def main():
# init the XML meta for handling
xmp = XMPMeta()
# if xmp_sources is None, set to empty list
if args.xmp_sources is None:
args.xmp_sources = []
# loop through the xmp_sources (folder or files) and read in the XMP data for LAT/LONG, other data
for xmp_file_source in args.xmp_sources:
# if folder, open and loop
@@ -1064,14 +1095,14 @@ def main():
# if we have read only we print list format style
if args.read_only:
# adjust the output width for the list view
format_length = outputListWidthAdjust()
format_length = outputListWidthAdjust(args)
# after how many lines do we reprint the header
header_repeat = 50
# how many pages will we have
page_all = ceil(len(work_files) / header_repeat)
# current page number
page_no = 1
# page_no = 1
# the formatted line for the output
# 4 {} => final replace: data (2 pre replaces)
# 1 {} => length replace here
@@ -1239,7 +1270,7 @@ def main():
failed = False
from_cache = False
for loc in data_set_loc:
if checkOverwrite(data_set[loc], loc, args.field_controls):
if checkOverwrite(args, data_set[loc], loc, args.field_controls):
has_unset = True
if has_unset:
# check if lat/long is in cache
@@ -1273,7 +1304,7 @@ def main():
print("### ***= FUZZY CACHE: YES => Best match: {}".format(best_match_latlong))
if not has_fuzzy_cache:
# get location from maps (google or openstreetmap)
maps_location = reverseGeolocate(latitude=data_set['GPSLatitude'], longitude=data_set['GPSLongitude'], map_type=map_type)
maps_location = reverseGeolocate(args, latitude=data_set['GPSLatitude'], longitude=data_set['GPSLongitude'], map_type=map_type)
# cache data with Lat/Long
data_cache[cache_key] = maps_location
from_cache = False
@@ -1297,7 +1328,7 @@ def main():
if maps_location['Country']:
for loc in data_set_loc:
# only write to XMP if overwrite check passes
if checkOverwrite(data_set_original[loc], loc, args.field_controls):
if checkOverwrite(args, data_set_original[loc], loc, args.field_controls):
data_set[loc] = maps_location[loc]
xmp.set_property(xmp_fields[loc], loc, maps_location[loc])
write_file = True
@@ -1314,7 +1345,7 @@ def main():
if use_lightroom and lightroom_data_ok:
for key in data_set:
# if not the same (to original data) and passes overwrite check
if data_set[key] != data_set_original[key] and checkOverwrite(data_set_original[key], key, args.field_controls):
if data_set[key] != data_set_original[key] and checkOverwrite(args, data_set_original[key], key, args.field_controls):
xmp.set_property(xmp_fields[key], key, data_set[key])
write_file = True
if write_file:
@@ -1325,7 +1356,7 @@ def main():
# use copyfile to create a backup copy
if not args.no_xmp_backup:
# check if there is another file with .BK. already there, if yes, get the max number and +1 it, if not set to 1
bk_file_counter = getBackupFileCounter(xmp_file)
bk_file_counter = getBackupFileCounter(args, xmp_file)
# copy to new backup file
copyfile(xmp_file, "{}.BK.{}{}".format(os.path.splitext(xmp_file)[0], bk_file_counter, os.path.splitext(xmp_file)[1]))
# write back to riginal file