Feature timewarp parse date 867 (#951)
* Working on tests for timewarp --parse-date * Test working for --parse-date * Refactored date utils out of timewarp.py * Added timezone to --parse-date, updated tests * Added cog to README
This commit is contained in:
@@ -1,14 +1,13 @@
|
||||
""" Fix time / date / timezone for photos in Apple Photos """
|
||||
|
||||
import datetime
|
||||
import os
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from functools import partial
|
||||
from textwrap import dedent
|
||||
from typing import Callable, Optional
|
||||
|
||||
import click
|
||||
from photoscript import Photo, PhotosLibrary
|
||||
from photoscript import PhotosLibrary
|
||||
from rich.console import Console
|
||||
|
||||
from osxphotos._constants import APP_NAME
|
||||
@@ -16,10 +15,14 @@ from osxphotos.compare_exif import PhotoCompare
|
||||
from osxphotos.datetime_utils import datetime_naive_to_local, datetime_to_new_tz
|
||||
from osxphotos.exif_datetime_updater import ExifDateTimeUpdater
|
||||
from osxphotos.exiftool import get_exiftool_path
|
||||
from osxphotos.photodates import (
|
||||
set_photo_date_from_filename,
|
||||
update_photo_date_time,
|
||||
update_photo_from_function,
|
||||
update_photo_time_for_new_timezone,
|
||||
)
|
||||
from osxphotos.photosalbum import PhotosAlbumPhotoScript
|
||||
from osxphotos.phototz import PhotoTimeZone, PhotoTimeZoneUpdater
|
||||
from osxphotos.timeutils import update_datetime
|
||||
from osxphotos.timezones import Timezone
|
||||
from osxphotos.utils import noop, pluralize
|
||||
|
||||
from .click_rich_echo import (
|
||||
@@ -38,6 +41,7 @@ from .param_types import (
|
||||
DateOffset,
|
||||
DateTimeISO8601,
|
||||
FunctionCall,
|
||||
StrpDateTimePattern,
|
||||
TimeOffset,
|
||||
TimeString,
|
||||
UTCOffset,
|
||||
@@ -49,110 +53,6 @@ from .verbose import get_verbose_console, verbose_print
|
||||
DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S%z"
|
||||
|
||||
|
||||
def update_photo_date_time(
|
||||
photo: Photo,
|
||||
date,
|
||||
time,
|
||||
date_delta,
|
||||
time_delta,
|
||||
verbose_print: Callable,
|
||||
):
|
||||
"""Update date, time in photo"""
|
||||
photo_date = photo.date
|
||||
new_photo_date = update_datetime(
|
||||
photo_date, date=date, time=time, date_delta=date_delta, time_delta=time_delta
|
||||
)
|
||||
filename = photo.filename
|
||||
uuid = photo.uuid
|
||||
if new_photo_date != photo_date:
|
||||
photo.date = new_photo_date
|
||||
verbose_print(
|
||||
f"Updated date/time for photo [filename]{filename}[/filename] "
|
||||
f"([uuid]{uuid}[/uuid]) from: [time]{photo_date}[/time] to [time]{new_photo_date}[/time]"
|
||||
)
|
||||
else:
|
||||
verbose_print(
|
||||
f"Skipped date/time update for photo [filename]{filename}[/filename] "
|
||||
f"([uuid]{uuid}[/uuid]): nothing to do"
|
||||
)
|
||||
|
||||
|
||||
def update_photo_time_for_new_timezone(
|
||||
library_path: str,
|
||||
photo: Photo,
|
||||
new_timezone: Timezone,
|
||||
verbose_print: Callable,
|
||||
):
|
||||
"""Update time in photo to keep it the same time but in a new timezone
|
||||
|
||||
For example, photo time is 12:00+0100 and new timezone is +0200,
|
||||
so adjust photo time by 1 hour so it will now be 12:00+0200 instead of
|
||||
13:00+0200 as it would be with no adjustment to the time"""
|
||||
old_timezone = PhotoTimeZone(library_path=library_path).get_timezone(photo)[0]
|
||||
# need to move time in opposite direction of timezone offset so that
|
||||
# photo time is the same time but in the new timezone
|
||||
delta = old_timezone - new_timezone.offset
|
||||
photo_date = photo.date
|
||||
new_photo_date = update_datetime(
|
||||
dt=photo_date, time_delta=datetime.timedelta(seconds=delta)
|
||||
)
|
||||
filename = photo.filename
|
||||
uuid = photo.uuid
|
||||
if photo_date != new_photo_date:
|
||||
photo.date = new_photo_date
|
||||
verbose_print(
|
||||
f"Adjusted date/time for photo [filename]{filename}[/filename] ([uuid]{uuid}[/uuid]) to match "
|
||||
f"previous time [time]{photo_date}[time] but in new timezone [tz]{new_timezone}[/tz]."
|
||||
)
|
||||
else:
|
||||
verbose_print(
|
||||
f"Skipping date/time update for photo [filename]{filename}[/filename] ([uuid]{photo.uuid}[/uuid]), "
|
||||
f"already matches new timezone [tz]{new_timezone}[/tz]"
|
||||
)
|
||||
|
||||
|
||||
def update_photo_from_function(
|
||||
library_path: str,
|
||||
function: Callable,
|
||||
verbose_print: Callable,
|
||||
photo: Photo,
|
||||
path: Optional[str],
|
||||
):
|
||||
"""Update photo from function call"""
|
||||
photo_tz_sec, _, photo_tz_name = PhotoTimeZone(
|
||||
library_path=library_path
|
||||
).get_timezone(photo)
|
||||
dt_new, tz_new = function(
|
||||
photo=photo,
|
||||
path=path,
|
||||
tz_sec=photo_tz_sec,
|
||||
tz_name=photo_tz_name,
|
||||
verbose=verbose_print,
|
||||
)
|
||||
if dt_new != photo.date:
|
||||
old_date = photo.date
|
||||
photo.date = dt_new
|
||||
verbose_print(
|
||||
f"Updated date/time for photo [filename]{photo.filename}[/filename] "
|
||||
f"([uuid]{photo.uuid}[/uuid]) from: [time]{old_date}[/time] to [time]{dt_new}[/time]"
|
||||
)
|
||||
else:
|
||||
verbose_print(
|
||||
f"Skipped date/time update for photo [filename]{photo.filename}[/filename] "
|
||||
f"([uuid]{photo.uuid}[/uuid]): nothing to do"
|
||||
)
|
||||
if tz_new != photo_tz_sec:
|
||||
tz_updater = PhotoTimeZoneUpdater(
|
||||
timezone=Timezone(tz_new), verbose=verbose_print, library_path=library_path
|
||||
)
|
||||
tz_updater.update_photo(photo)
|
||||
else:
|
||||
verbose_print(
|
||||
f"Skipped timezone update for photo [filename]{photo.filename}[/filename] "
|
||||
f"([uuid]{photo.uuid}[/uuid]): nothing to do"
|
||||
)
|
||||
|
||||
|
||||
class TimeWarpCommand(click.Command):
|
||||
"""Custom click.Command that overrides get_help() to show additional help info for export"""
|
||||
|
||||
@@ -223,6 +123,40 @@ if the EXIF data is missing, use the file modification date/time; show verbose o
|
||||
|
||||
`osxphotos timewarp --pull-exif --use-file-time --verbose`
|
||||
|
||||
## Parsing Dates/Times from Filenames
|
||||
|
||||
The --parse-date option allows you to parse dates/times from the original filename of the photo.
|
||||
This is useful if you files with dates/times embedded in the filename but not in the metadata.
|
||||
|
||||
|
||||
The argument to `--parse-date` is a pattern string that is used to parse the date/time
|
||||
from the filename. The pattern string is a superset of the python `strftime/strptime`
|
||||
format with the following additions:
|
||||
|
||||
- *: Match any number of characters
|
||||
- ^: Match the beginning of the string
|
||||
- $: Match the end of the string
|
||||
- {n}: Match exactly n characters
|
||||
- {n,}: Match at least n characters
|
||||
- {n,m}: Match at least n characters and at most m characters
|
||||
- In addition to `%%` for a literal `%`, the following format codes are supported:
|
||||
`%^`, `%$`, `%*`, `%|`, `%{`, `%}` for `^`, `$`, `*`, `|`, `{`, `}` respectively
|
||||
- |: join multiple format codes; each code is tried in order until one matches
|
||||
- Unlike the standard library, the leading zero is not optional for
|
||||
%d, %m, %H, %I, %M, %S, %j, %U, %W, and %V
|
||||
- For optional leading zero, use %-d, %-m, %-H, %-I, %-M, %-S, %-j, %-U, %-W, and %-V
|
||||
|
||||
For more information on strptime format codes, see:
|
||||
https://docs.python.org/3/library/datetime.html?highlight=strptime#strftime-and-strptime-format-codes
|
||||
|
||||
**Note**: The time zone of the parsed date/time is assumed to be the local time zone.
|
||||
If the parse pattern includes a time zone, the photo's time will be converted from
|
||||
the specified time zone to the local time zone. osxphotos import does not
|
||||
currently support setting the time zone of imported photos.
|
||||
See also `osxphotos help timewarp` for more information on the timewarp
|
||||
command which can be used to change the time zone of photos after import.
|
||||
|
||||
|
||||
"""
|
||||
),
|
||||
width=formatter.width,
|
||||
@@ -322,6 +256,19 @@ if the EXIF data is missing, use the file modification date/time; show verbose o
|
||||
"Requires the third-party exiftool utility be installed (see https://exiftool.org/). "
|
||||
"See also --push-exif.",
|
||||
)
|
||||
@click.option(
|
||||
"--parse-date",
|
||||
"-M",
|
||||
metavar="DATE_PATTERN",
|
||||
type=StrpDateTimePattern(),
|
||||
help="Parse date from filename using DATE_PATTERN and set photo's date to match. "
|
||||
"If file does not match DATE_PATTERN, the date will not be changed. "
|
||||
"DATE_PATTERN is a strptime-compatible pattern with extensions as pattern described below. "
|
||||
"If DATE_PATTERN matches time zone information, the photo's timezone will be set to match. "
|
||||
"For example, if your photos are named 'IMG_1234_2022_11_23_12_34_56.jpg' where the date/time is "
|
||||
"'2022-11-23 12:34:56', you could use the pattern '%Y_%m_%d_%H_%M_%S' or "
|
||||
"'IMG_*_%Y_%m_%d_%H_%M_%S' to further narrow the pattern to only match files with 'IMG_xxxx_' in the name.",
|
||||
)
|
||||
@click.option(
|
||||
"--function",
|
||||
"-F",
|
||||
@@ -363,7 +310,7 @@ if the EXIF data is missing, use the file modification date/time; show verbose o
|
||||
help="When used with --compare-exif, adds any photos with date/time/timezone differences "
|
||||
"between Photos/EXIF to album ALBUM. If ALBUM does not exist, it will be created.",
|
||||
)
|
||||
@click.option("--verbose", "-V", "verbose", is_flag=True, help="Show verbose output.")
|
||||
@click.option("--verbose", "-V", "verbose_", is_flag=True, help="Show verbose output.")
|
||||
@click.option(
|
||||
"--library",
|
||||
"-L",
|
||||
@@ -419,9 +366,10 @@ def timewarp(
|
||||
use_file_time,
|
||||
add_to_album,
|
||||
exiftool_path,
|
||||
verbose,
|
||||
verbose_,
|
||||
library,
|
||||
theme,
|
||||
parse_date,
|
||||
plain,
|
||||
output_file,
|
||||
terminal_width,
|
||||
@@ -446,6 +394,7 @@ def timewarp(
|
||||
timezone,
|
||||
inspect,
|
||||
compare_exif,
|
||||
parse_date,
|
||||
push_exif,
|
||||
pull_exif,
|
||||
function,
|
||||
@@ -453,7 +402,8 @@ def timewarp(
|
||||
):
|
||||
raise click.UsageError(
|
||||
"At least one of --date, --date-delta, --time, --time-delta, "
|
||||
"--timezone, --inspect, --compare-exif, --push-exif, --pull-exif, --function "
|
||||
"--timezone, --inspect, --compare-exif, --push-exif, --pull-exif, "
|
||||
"--parse-date, --function "
|
||||
"must be specified."
|
||||
)
|
||||
|
||||
@@ -472,8 +422,8 @@ def timewarp(
|
||||
# configure colored rich output
|
||||
# TODO: this is all a little hacky, find a better way to do this
|
||||
color_theme = get_theme(theme)
|
||||
verbose_ = verbose_print(
|
||||
verbose,
|
||||
verbose = verbose_print(
|
||||
verbose_,
|
||||
timestamp,
|
||||
rich=True,
|
||||
theme=color_theme,
|
||||
@@ -499,7 +449,7 @@ def timewarp(
|
||||
|
||||
if any([compare_exif, push_exif, pull_exif]):
|
||||
exiftool_path = exiftool_path or get_exiftool_path()
|
||||
verbose_(f"exiftool path: [filename]{exiftool_path}[/filename]")
|
||||
verbose(f"exiftool path: [filename]{exiftool_path}[/filename]")
|
||||
|
||||
try:
|
||||
photos = PhotosLibrary().selection
|
||||
@@ -533,6 +483,7 @@ def timewarp(
|
||||
push_exif,
|
||||
pull_exif,
|
||||
function,
|
||||
parse_date,
|
||||
]
|
||||
)
|
||||
and not force
|
||||
@@ -556,13 +507,19 @@ def timewarp(
|
||||
time=time,
|
||||
date_delta=date_delta,
|
||||
time_delta=time_delta,
|
||||
verbose_print=verbose_,
|
||||
verbose=verbose,
|
||||
)
|
||||
|
||||
update_photo_time_for_new_timezone_ = partial(
|
||||
update_photo_time_for_new_timezone,
|
||||
library_path=library,
|
||||
verbose_print=verbose_,
|
||||
verbose=verbose,
|
||||
)
|
||||
|
||||
set_photo_date_from_filename_ = partial(
|
||||
set_photo_date_from_filename,
|
||||
library_path=library,
|
||||
verbose=verbose,
|
||||
)
|
||||
|
||||
if function:
|
||||
@@ -570,7 +527,7 @@ def timewarp(
|
||||
update_photo_from_function,
|
||||
library_path=library,
|
||||
function=function[0],
|
||||
verbose_print=verbose_,
|
||||
verbose=verbose,
|
||||
)
|
||||
else:
|
||||
update_photo_from_function_ = noop
|
||||
@@ -579,14 +536,20 @@ def timewarp(
|
||||
tzinfo = PhotoTimeZone(library_path=library)
|
||||
if photos:
|
||||
rich_echo(
|
||||
"[filename]filename[/filename], [uuid]uuid[/uuid], [time]photo time (local)[/time], [time]photo time[/time], [tz]timezone offset[/tz], [tz]timezone name[/tz]"
|
||||
"[filename]filename[/filename], [uuid]uuid[/uuid], "
|
||||
"[time]photo time (local)[/time], "
|
||||
"[time]photo time[/time], "
|
||||
"[tz]timezone offset[/tz], [tz]timezone name[/tz]"
|
||||
)
|
||||
for photo in photos:
|
||||
tz_seconds, tz_str, tz_name = tzinfo.get_timezone(photo)
|
||||
photo_date_local = datetime_naive_to_local(photo.date)
|
||||
photo_date_tz = datetime_to_new_tz(photo_date_local, tz_seconds)
|
||||
rich_echo(
|
||||
f"[filename]{photo.filename}[/filename], [uuid]{photo.uuid}[/uuid], [time]{photo_date_local.strftime(DATETIME_FORMAT)}[/time], [time]{photo_date_tz.strftime(DATETIME_FORMAT)}[/time], [tz]{tz_str}[/tz], [tz]{tz_name}[/tz]"
|
||||
f"[filename]{photo.filename}[/filename], [uuid]{photo.uuid}[/uuid], "
|
||||
f"[time]{photo_date_local.strftime(DATETIME_FORMAT)}[/time], "
|
||||
f"[time]{photo_date_tz.strftime(DATETIME_FORMAT)}[/time], "
|
||||
f"[tz]{tz_str}[/tz], [tz]{tz_name}[/tz]"
|
||||
)
|
||||
sys.exit(0)
|
||||
|
||||
@@ -596,7 +559,7 @@ def timewarp(
|
||||
if photos:
|
||||
photocomp = PhotoCompare(
|
||||
library_path=library,
|
||||
verbose=verbose_,
|
||||
verbose=verbose,
|
||||
exiftool_path=exiftool_path,
|
||||
)
|
||||
if not album:
|
||||
@@ -622,12 +585,12 @@ def timewarp(
|
||||
if album:
|
||||
if diff_results.diff:
|
||||
different_photos += 1
|
||||
verbose_(
|
||||
verbose(
|
||||
f"Photo {filename} ({uuid}) has different date/time/timezone, adding to album '{album.name}'"
|
||||
)
|
||||
album.add(photo)
|
||||
else:
|
||||
verbose_(f"Photo {filename} ({uuid}) has same date/time/timezone")
|
||||
verbose(f"Photo {filename} ({uuid}) has same date/time/timezone")
|
||||
else:
|
||||
rich_echo(
|
||||
f"{filename}, {uuid}, "
|
||||
@@ -644,14 +607,14 @@ def timewarp(
|
||||
|
||||
if timezone:
|
||||
tz_updater = PhotoTimeZoneUpdater(
|
||||
timezone, verbose=verbose_, library_path=library
|
||||
timezone, verbose=verbose, library_path=library
|
||||
)
|
||||
|
||||
if any([push_exif, pull_exif, function]):
|
||||
# ExifDateTimeUpdater used to get photo path for --function
|
||||
exif_updater = ExifDateTimeUpdater(
|
||||
library_path=library,
|
||||
verbose=verbose_,
|
||||
verbose=verbose,
|
||||
exiftool_path=exiftool_path,
|
||||
plain=plain,
|
||||
)
|
||||
@@ -663,6 +626,8 @@ def timewarp(
|
||||
total=num_photos,
|
||||
)
|
||||
for p in photos:
|
||||
if parse_date:
|
||||
set_photo_date_from_filename_(p, p.filename, parse_date)
|
||||
if pull_exif:
|
||||
exif_updater.update_photos_from_exif(
|
||||
p, use_file_modify_date=use_file_time
|
||||
@@ -676,7 +641,7 @@ def timewarp(
|
||||
if timezone:
|
||||
tz_updater.update_photo(p)
|
||||
if function:
|
||||
verbose_(f"Calling function [bold]{function[1]}")
|
||||
verbose(f"Calling function [bold]{function[1]}")
|
||||
photo_path = exif_updater.get_photo_path(p)
|
||||
update_photo_from_function_(photo=p, path=photo_path)
|
||||
if push_exif:
|
||||
|
||||
181
osxphotos/photodates.py
Normal file
181
osxphotos/photodates.py
Normal file
@@ -0,0 +1,181 @@
|
||||
"""Utilities for working with Photo dates in Apple Photos; used by osxphotos timewarp command"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import pathlib
|
||||
from typing import Callable
|
||||
|
||||
import photoscript
|
||||
from strpdatetime import strpdatetime
|
||||
|
||||
from .datetime_utils import (
|
||||
datetime_has_tz,
|
||||
datetime_remove_tz,
|
||||
datetime_tz_to_utc,
|
||||
datetime_utc_to_local,
|
||||
utc_offset_seconds,
|
||||
)
|
||||
from .phototz import PhotoTimeZone, PhotoTimeZoneUpdater
|
||||
from .timeutils import update_datetime
|
||||
from .timezones import Timezone
|
||||
|
||||
|
||||
def update_photo_date_time(
|
||||
photo: photoscript.Photo,
|
||||
date,
|
||||
time,
|
||||
date_delta,
|
||||
time_delta,
|
||||
verbose: Callable,
|
||||
):
|
||||
"""Update date, time in photo"""
|
||||
photo_date = photo.date
|
||||
new_photo_date = update_datetime(
|
||||
photo_date, date=date, time=time, date_delta=date_delta, time_delta=time_delta
|
||||
)
|
||||
filename = photo.filename
|
||||
uuid = photo.uuid
|
||||
if new_photo_date != photo_date:
|
||||
photo.date = new_photo_date
|
||||
verbose(
|
||||
f"Updated date/time for photo [filename]{filename}[/filename] "
|
||||
f"([uuid]{uuid}[/uuid]) from: [time]{photo_date}[/time] to [time]{new_photo_date}[/time]"
|
||||
)
|
||||
else:
|
||||
verbose(
|
||||
f"Skipped date/time update for photo [filename]{filename}[/filename] "
|
||||
f"([uuid]{uuid}[/uuid]): nothing to do"
|
||||
)
|
||||
|
||||
|
||||
def update_photo_from_function(
|
||||
library_path: str,
|
||||
function: Callable,
|
||||
verbose: Callable[..., None],
|
||||
photo: photoscript.Photo,
|
||||
path: str | None,
|
||||
):
|
||||
"""Update photo from function call"""
|
||||
photo_tz_sec, _, photo_tz_name = PhotoTimeZone(
|
||||
library_path=library_path
|
||||
).get_timezone(photo)
|
||||
dt_new, tz_new = function(
|
||||
photo=photo,
|
||||
path=path,
|
||||
tz_sec=photo_tz_sec,
|
||||
tz_name=photo_tz_name,
|
||||
verbose=verbose,
|
||||
)
|
||||
if dt_new != photo.date:
|
||||
old_date = photo.date
|
||||
photo.date = dt_new
|
||||
verbose(
|
||||
f"Updated date/time for photo [filename]{photo.filename}[/filename] "
|
||||
f"([uuid]{photo.uuid}[/uuid]) from: [time]{old_date}[/time] to [time]{dt_new}[/time]"
|
||||
)
|
||||
else:
|
||||
verbose(
|
||||
f"Skipped date/time update for photo [filename]{photo.filename}[/filename] "
|
||||
f"([uuid]{photo.uuid}[/uuid]): nothing to do"
|
||||
)
|
||||
if tz_new != photo_tz_sec:
|
||||
tz_updater = PhotoTimeZoneUpdater(
|
||||
timezone=Timezone(tz_new), verbose=verbose, library_path=library_path
|
||||
)
|
||||
tz_updater.update_photo(photo)
|
||||
else:
|
||||
verbose(
|
||||
f"Skipped timezone update for photo [filename]{photo.filename}[/filename] "
|
||||
f"([uuid]{photo.uuid}[/uuid]): nothing to do"
|
||||
)
|
||||
|
||||
|
||||
def update_photo_time_for_new_timezone(
|
||||
library_path: str,
|
||||
photo: photoscript.Photo,
|
||||
new_timezone: Timezone,
|
||||
verbose: Callable[..., None],
|
||||
):
|
||||
"""Update time in photo to keep it the same time but in a new timezone
|
||||
|
||||
For example, photo time is 12:00+0100 and new timezone is +0200,
|
||||
so adjust photo time by 1 hour so it will now be 12:00+0200 instead of
|
||||
13:00+0200 as it would be with no adjustment to the time"""
|
||||
old_timezone = PhotoTimeZone(library_path=library_path).get_timezone(photo)[0]
|
||||
# need to move time in opposite direction of timezone offset so that
|
||||
# photo time is the same time but in the new timezone
|
||||
delta = old_timezone - new_timezone.offset
|
||||
photo_date = photo.date
|
||||
new_photo_date = update_datetime(
|
||||
dt=photo_date, time_delta=datetime.timedelta(seconds=delta)
|
||||
)
|
||||
filename = photo.filename
|
||||
uuid = photo.uuid
|
||||
if photo_date != new_photo_date:
|
||||
photo.date = new_photo_date
|
||||
verbose(
|
||||
f"Adjusted date/time for photo [filename]{filename}[/] ([uuid]{uuid}[/]) to [time]{new_photo_date}[/] "
|
||||
f"to match previous time [time]{photo_date}[/] but in new timezone [tz]{new_timezone}[/]."
|
||||
)
|
||||
else:
|
||||
verbose(
|
||||
f"Skipping date/time update for photo [filename]{filename}[/] ([uuid]{photo.uuid}[/]), "
|
||||
f"already matches new timezone [tz]{new_timezone}[/]"
|
||||
)
|
||||
|
||||
|
||||
def set_photo_date_from_filename(
|
||||
photo: photoscript.Photo,
|
||||
filepath: pathlib.Path | str,
|
||||
parse_date: str,
|
||||
verbose: Callable[..., None],
|
||||
library_path: str | None = None,
|
||||
) -> datetime.datetime | None:
|
||||
"""Set date of photo from filename
|
||||
|
||||
Args:
|
||||
photo: Photo to set date
|
||||
filepath: Path to photo's original file
|
||||
parse_date: strptime format string to parse date from filename
|
||||
verbose: verbose function to use for logging
|
||||
library_path: Path to Photos library; if not provided, will attempt to determine automatically
|
||||
|
||||
Returns:
|
||||
datetime.datetime: date set on photo or None if date could not be parsed
|
||||
"""
|
||||
|
||||
if not isinstance(filepath, pathlib.Path):
|
||||
filepath = pathlib.Path(filepath)
|
||||
|
||||
try:
|
||||
date = strpdatetime(filepath.name, parse_date)
|
||||
except ValueError:
|
||||
verbose(
|
||||
f"[warning]Could not parse date from filename [filename]{filepath.name}[/][/]"
|
||||
)
|
||||
return None
|
||||
|
||||
# first, set date on photo without timezone (Photos will assume local timezone)
|
||||
date_no_tz = datetime_remove_tz(date) if datetime_has_tz(date) else date
|
||||
verbose(
|
||||
f"Setting date of photo [filename]{filepath.name}[/] to [time]{date_no_tz.strftime('%Y-%m-%d %H:%M:%S')}[/]"
|
||||
)
|
||||
photo.date = date_no_tz
|
||||
if datetime_has_tz(date):
|
||||
# if timezone, need to update timezone and also the date/time to match
|
||||
photo_tz_sec, _, photo_tz_name = PhotoTimeZone(
|
||||
library_path=library_path
|
||||
).get_timezone(photo)
|
||||
tz_new_secs = int(utc_offset_seconds(date))
|
||||
if photo_tz_sec != tz_new_secs:
|
||||
tz_new = Timezone(tz_new_secs)
|
||||
update_photo_time_for_new_timezone(library_path, photo, tz_new, verbose)
|
||||
tz_updater = PhotoTimeZoneUpdater(
|
||||
timezone=tz_new,
|
||||
verbose=verbose,
|
||||
library_path=library_path,
|
||||
)
|
||||
tz_updater.update_photo(photo)
|
||||
|
||||
return date
|
||||
Reference in New Issue
Block a user