Feature date added 998 (#1003)
* Implemented --date-added, #998 * Added --date-added-from-photo * Fixed typehint
This commit is contained in:
parent
0e1613f134
commit
94f484e9ec
@ -18,6 +18,8 @@ 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 (
|
||||
get_photo_date_added,
|
||||
set_photo_date_added,
|
||||
set_photo_date_from_filename,
|
||||
update_photo_date_time,
|
||||
update_photo_from_function,
|
||||
@ -111,6 +113,14 @@ For this to work, you'll need to install the third-party exiftool (https://exift
|
||||
|
||||
*Note on timezones and times*: In Photos, when you change the timezone, Photos assumes the time itself was correct for the previous timezone and adjusts the time accordingly to the new timezone. E.g. if the photo's time is `13:00` and the timezone is `GMT -07:00` and you adjust the timezone one hour east to `GMT -06:00`, Photos will change the time of the photo to `14:00`. osxphotos timewarp follows this behavior. Using `--match-time` allows you to adjust the timezone but keep the same time without adjustment. For example, if your camera clock was correct but lacked timezone information and you took photos in one timezone but imported them to photos in another, Photos will add the timezone of the computer at time of import. You can use osxphotos timewarp to adjust the timezone but keep the time using `--match-time`.
|
||||
|
||||
**Update the date the photos were added to Photos**
|
||||
|
||||
`osxphotos timewarp --date-added 2021-09-10`
|
||||
|
||||
**Update the date the photos were added to Photos to match the date of the photo**
|
||||
|
||||
`osxphotos timewarp --date-added-from-photo`
|
||||
|
||||
**Compare the date/time/timezone of selected photos with the date/time/timezone in the photos' original EXIF metadata**
|
||||
|
||||
`osxphotos timewarp --compare-exif`
|
||||
@ -210,6 +220,25 @@ command which can be used to change the time zone of photos after import.
|
||||
"This is the same behavior exhibited by Photos when manually adjusting timezone in the Get Info window. "
|
||||
"See also --match-time. ",
|
||||
)
|
||||
@click.option(
|
||||
"--date-added",
|
||||
metavar="DATE",
|
||||
type=DateTimeISO8601(),
|
||||
help="Set date/time added for selected photos. "
|
||||
"This changes the date added or imported date in Photos but "
|
||||
"does not change the date/time/timezone of the photo itself. "
|
||||
"This is useful for removing photos from the Recents album, "
|
||||
"for example if you have imported old scanned photos. "
|
||||
"Format is 'YYYY-MM-DD' or 'YYYY-MM-DD HH:MM:SS'. "
|
||||
"If time is not included, midnight is assumed.",
|
||||
)
|
||||
@click.option(
|
||||
"--date-added-from-photo",
|
||||
is_flag=True,
|
||||
help="Set date/time added for selected photos to the date/time the photo was taken. "
|
||||
"This changes the date added or imported date in Photos but "
|
||||
"does not change the date/time/timezone of the photo itself. ",
|
||||
)
|
||||
@click.option(
|
||||
"--inspect",
|
||||
"-i",
|
||||
@ -349,6 +378,8 @@ def timewarp(
|
||||
time,
|
||||
time_delta,
|
||||
timezone,
|
||||
date_added,
|
||||
date_added_from_photo,
|
||||
inspect,
|
||||
compare_exif,
|
||||
push_exif,
|
||||
@ -379,23 +410,25 @@ def timewarp(
|
||||
# check constraints
|
||||
if not any(
|
||||
[
|
||||
date,
|
||||
date_delta,
|
||||
time,
|
||||
time_delta,
|
||||
timezone,
|
||||
inspect,
|
||||
compare_exif,
|
||||
parse_date,
|
||||
push_exif,
|
||||
pull_exif,
|
||||
date_added_from_photo,
|
||||
date_added,
|
||||
date_delta,
|
||||
date,
|
||||
function,
|
||||
inspect,
|
||||
parse_date,
|
||||
pull_exif,
|
||||
push_exif,
|
||||
time_delta,
|
||||
time,
|
||||
timezone,
|
||||
]
|
||||
):
|
||||
raise click.UsageError(
|
||||
"At least one of --date, --date-delta, --time, --time-delta, "
|
||||
"--timezone, --inspect, --compare-exif, --push-exif, --pull-exif, "
|
||||
"--parse-date, --function "
|
||||
"--parse-date, --function, --date-added, or --date-added-from-photo "
|
||||
"must be specified."
|
||||
)
|
||||
|
||||
@ -441,15 +474,17 @@ def timewarp(
|
||||
if (
|
||||
any(
|
||||
[
|
||||
date,
|
||||
date_added_from_photo,
|
||||
date_added,
|
||||
date_delta,
|
||||
time,
|
||||
time_delta,
|
||||
timezone,
|
||||
push_exif,
|
||||
pull_exif,
|
||||
date,
|
||||
function,
|
||||
parse_date,
|
||||
pull_exif,
|
||||
push_exif,
|
||||
time_delta,
|
||||
time,
|
||||
timezone,
|
||||
]
|
||||
)
|
||||
and not force
|
||||
@ -488,6 +523,24 @@ def timewarp(
|
||||
verbose=verbose,
|
||||
)
|
||||
|
||||
set_photo_date_added_ = partial(
|
||||
set_photo_date_added,
|
||||
library_path=library,
|
||||
verbose=verbose,
|
||||
date_added=date_added,
|
||||
)
|
||||
|
||||
set_photo_date_added_from_photo_ = partial(
|
||||
set_photo_date_added,
|
||||
library_path=library,
|
||||
verbose=verbose,
|
||||
)
|
||||
|
||||
get_photo_date_added_ = partial(
|
||||
get_photo_date_added,
|
||||
library_path=library,
|
||||
)
|
||||
|
||||
if function:
|
||||
update_photo_from_function_ = partial(
|
||||
update_photo_from_function,
|
||||
@ -505,18 +558,21 @@ def timewarp(
|
||||
"[filename]filename[/filename], [uuid]uuid[/uuid], "
|
||||
"[time]photo time (local)[/time], "
|
||||
"[time]photo time[/time], "
|
||||
"[tz]timezone offset[/tz], [tz]timezone name[/tz]"
|
||||
"[tz]timezone offset[/tz], [tz]timezone name[/tz], "
|
||||
"[time]date added (local)[/time]"
|
||||
)
|
||||
for photo in photos:
|
||||
set_crash_data("photo", f"{photo.uuid} {photo.filename}")
|
||||
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)
|
||||
date_added = datetime_naive_to_local(get_photo_date_added_(photo))
|
||||
echo(
|
||||
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]"
|
||||
f"[tz]{tz_str}[/tz], [tz]{tz_name}[/tz], "
|
||||
f"[time]{date_added.strftime(DATETIME_FORMAT)}[/time]"
|
||||
)
|
||||
sys.exit(0)
|
||||
|
||||
@ -609,6 +665,10 @@ def timewarp(
|
||||
update_photo_time_for_new_timezone_(photo=photo, new_timezone=timezone)
|
||||
if timezone:
|
||||
tz_updater.update_photo(photo)
|
||||
if date_added:
|
||||
set_photo_date_added_(photo)
|
||||
if date_added_from_photo:
|
||||
set_photo_date_added_from_photo_(photo, date_added=photo.date)
|
||||
if function:
|
||||
verbose(f"Calling function [bold]{function[1]}")
|
||||
photo_path = exif_updater.get_photo_path(photo)
|
||||
|
||||
@ -4,11 +4,14 @@ from __future__ import annotations
|
||||
|
||||
import datetime
|
||||
import pathlib
|
||||
import sqlite3
|
||||
from typing import Callable
|
||||
|
||||
import photoscript
|
||||
from strpdatetime import strpdatetime
|
||||
from tenacity import retry, stop_after_attempt, wait_exponential
|
||||
|
||||
from ._constants import _DB_TABLE_NAMES
|
||||
from .datetime_utils import (
|
||||
datetime_has_tz,
|
||||
datetime_remove_tz,
|
||||
@ -16,9 +19,13 @@ from .datetime_utils import (
|
||||
datetime_utc_to_local,
|
||||
utc_offset_seconds,
|
||||
)
|
||||
from .photosdb.photosdb_utils import get_photos_library_version
|
||||
from .phototz import PhotoTimeZone, PhotoTimeZoneUpdater
|
||||
from .timeutils import update_datetime
|
||||
from .timezones import Timezone
|
||||
from .utils import get_last_library_path, get_system_library_path
|
||||
|
||||
MACOS_TIME_EPOCH = datetime.datetime(2001, 1, 1, 0, 0, 0)
|
||||
|
||||
|
||||
def update_photo_date_time(
|
||||
@ -179,3 +186,94 @@ def set_photo_date_from_filename(
|
||||
tz_updater.update_photo(photo)
|
||||
|
||||
return date
|
||||
|
||||
|
||||
def set_photo_date_added(
|
||||
photo: photoscript.Photo,
|
||||
date_added: datetime.datetime,
|
||||
verbose: Callable[..., None],
|
||||
library_path: str | None = None,
|
||||
) -> datetime.datetime | None:
|
||||
"""Modify the ADDEDDATE of a photo"""
|
||||
|
||||
if not (library_path := _get_photos_library_path(library_path)):
|
||||
raise ValueError("Could not determine Photos library path")
|
||||
verbose(
|
||||
f"Setting date added for photo [filename]{photo.filename}[/] to [time]{date_added}[/]"
|
||||
)
|
||||
_set_date_added(library_path, photo.uuid, date_added)
|
||||
|
||||
photo.date = photo.date + datetime.timedelta(seconds=1)
|
||||
photo.date = photo.date - datetime.timedelta(seconds=1)
|
||||
|
||||
|
||||
@retry(
|
||||
wait=wait_exponential(multiplier=1, min=0.100, max=5),
|
||||
stop=stop_after_attempt(10),
|
||||
)
|
||||
def _set_date_added(library_path: str, uuid: str, date_added: datetime.datetime):
|
||||
"""Set the ADDEDDATE of a photo"""
|
||||
# Use retry decorator to retry if database is locked
|
||||
photos_version = get_photos_library_version(library_path)
|
||||
db_path = str(pathlib.Path(library_path) / "database/Photos.sqlite")
|
||||
asset_table = _DB_TABLE_NAMES[photos_version]["ASSET"]
|
||||
|
||||
timestamp = datetime_to_photos_timestamp(date_added)
|
||||
conn = sqlite3.connect(db_path)
|
||||
c = conn.cursor()
|
||||
c.execute(
|
||||
f"UPDATE {asset_table} SET ZADDEDDATE=? WHERE ZUUID=?",
|
||||
(timestamp, uuid),
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
|
||||
def _get_photos_library_path(library_path: str | None = None) -> str:
|
||||
"""Return path to the Photos library or None if not found"""
|
||||
# get_last_library_path() returns the path to the last Photos library
|
||||
# opened but sometimes (rarely) fails on some systems
|
||||
try:
|
||||
library_path = (
|
||||
library_path or get_last_library_path() or get_system_library_path()
|
||||
)
|
||||
except Exception:
|
||||
library_path = None
|
||||
return library_path
|
||||
|
||||
|
||||
def datetime_to_photos_timestamp(dt: datetime.datetime) -> int:
|
||||
"""Convert datetime to Photos timestamp (seconds since 2001-01-01)"""
|
||||
return int((dt - MACOS_TIME_EPOCH).total_seconds())
|
||||
|
||||
|
||||
def photos_timestamp_to_datetime(ts: int) -> datetime.datetime:
|
||||
"""Convert Photos timestamp (seconds since 2001-01-01) to datetime"""
|
||||
return MACOS_TIME_EPOCH + datetime.timedelta(seconds=ts)
|
||||
|
||||
|
||||
@retry(
|
||||
wait=wait_exponential(multiplier=1, min=0.100, max=5),
|
||||
stop=stop_after_attempt(5),
|
||||
)
|
||||
def get_photo_date_added(
|
||||
photo: photoscript.Photo,
|
||||
library_path: str | None = None,
|
||||
) -> datetime.datetime | None:
|
||||
"""Get the ADDEDDATE of a photo"""
|
||||
|
||||
if not (library_path := _get_photos_library_path(library_path)):
|
||||
raise ValueError("Could not determine Photos library path")
|
||||
|
||||
photos_version = get_photos_library_version(library_path)
|
||||
db_path = str(pathlib.Path(library_path) / "database/Photos.sqlite")
|
||||
asset_table = _DB_TABLE_NAMES[photos_version]["ASSET"]
|
||||
conn = sqlite3.connect(db_path)
|
||||
c = conn.cursor()
|
||||
c.execute(
|
||||
f"SELECT ZADDEDDATE FROM {asset_table} WHERE ZUUID=?",
|
||||
(photo.uuid,),
|
||||
)
|
||||
row = c.fetchone()
|
||||
conn.close()
|
||||
return photos_timestamp_to_datetime(row[0])
|
||||
|
||||
@ -51,8 +51,13 @@ class PhotoTimeZone:
|
||||
self.db_path = db_path
|
||||
self.ASSET_TABLE = _DB_TABLE_NAMES[photos_version]["ASSET"]
|
||||
|
||||
@retry(
|
||||
wait=wait_exponential(multiplier=1, min=0.100, max=5),
|
||||
stop=stop_after_attempt(10),
|
||||
)
|
||||
def get_timezone(self, photo: Photo) -> Tuple[int, str, str]:
|
||||
"""Return (timezone_seconds, timezone_str, timezone_name) of photo"""
|
||||
# Use retry decorator to retry if database is locked
|
||||
uuid = photo.uuid
|
||||
sql = f""" SELECT
|
||||
ZADDITIONALASSETATTRIBUTES.ZTIMEZONEOFFSET,
|
||||
@ -119,6 +124,7 @@ class PhotoTimeZoneUpdater:
|
||||
stop=stop_after_attempt(10),
|
||||
)
|
||||
def _update_photo(self, photo: Photo):
|
||||
# Use retry decorator to retry if database is locked
|
||||
try:
|
||||
uuid = photo.uuid
|
||||
sql = f""" SELECT
|
||||
|
||||
@ -4,7 +4,11 @@ import datetime
|
||||
import pathlib
|
||||
import time
|
||||
|
||||
from tests.parse_timewarp_output import CompareValues, InspectValues
|
||||
from tests.parse_timewarp_output import (
|
||||
CompareValues,
|
||||
InspectValues,
|
||||
InspectValuesDateAdded,
|
||||
)
|
||||
|
||||
TEST_LIBRARY_TIMEWARP = "tests/TestTimeWarp-10.15.7.photoslibrary"
|
||||
|
||||
@ -393,4 +397,55 @@ CATALINA_PHOTOS_5 = {
|
||||
"GMT-0400",
|
||||
),
|
||||
},
|
||||
"date_added": {
|
||||
# 20230120_010203-0400.jpg
|
||||
"uuid": "5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
|
||||
"data": [
|
||||
(
|
||||
"2022-01-01",
|
||||
InspectValuesDateAdded(
|
||||
"20230120_010203-0400.jpg",
|
||||
"5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
|
||||
"2023-01-19 21:02:03-0800"
|
||||
if not is_dst()
|
||||
else "2023-01-19 20:02:03-0700",
|
||||
"2023-01-20 01:02:03-0400",
|
||||
"-0400",
|
||||
"GMT-0400",
|
||||
"2022-01-01 00:00:00-0800"
|
||||
if not is_dst()
|
||||
else "2022-01-01 00:00:00-0700",
|
||||
),
|
||||
),
|
||||
(
|
||||
"2022-01-01 01:02:03",
|
||||
InspectValuesDateAdded(
|
||||
"20230120_010203-0400.jpg",
|
||||
"5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
|
||||
"2023-01-19 21:02:03-0800"
|
||||
if not is_dst()
|
||||
else "2023-01-19 20:02:03-0700",
|
||||
"2023-01-20 01:02:03-0400",
|
||||
"-0400",
|
||||
"GMT-0400",
|
||||
"2022-01-01 01:02:03-0800"
|
||||
if not is_dst()
|
||||
else "2022-01-01 01:02:03-0700",
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
"date_added_from_photo": {
|
||||
# 20230120_010203-0400.jpg
|
||||
"uuid": "5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
|
||||
"expected": InspectValuesDateAdded(
|
||||
"20230120_010203-0400.jpg",
|
||||
"5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
|
||||
"2023-01-19 21:02:03-0800" if not is_dst() else "2023-01-19 20:02:03-0700",
|
||||
"2023-01-20 01:02:03-0400",
|
||||
"-0400",
|
||||
"GMT-0400",
|
||||
"2023-01-19 21:02:03-0800" if not is_dst() else "2023-01-19 20:02:03-0700",
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
@ -7,7 +7,11 @@ import datetime
|
||||
import pathlib
|
||||
import time
|
||||
|
||||
from tests.parse_timewarp_output import CompareValues, InspectValues
|
||||
from tests.parse_timewarp_output import (
|
||||
CompareValues,
|
||||
InspectValues,
|
||||
InspectValuesDateAdded,
|
||||
)
|
||||
|
||||
TEST_LIBRARY_TIMEWARP = "tests/TestTimeWarp-13.1.0.photoslibrary"
|
||||
|
||||
@ -396,4 +400,55 @@ VENTURA_PHOTOS_5 = {
|
||||
"GMT-0400",
|
||||
),
|
||||
},
|
||||
"date_added": {
|
||||
# 20230120_010203-0400.jpg
|
||||
"uuid": "5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
|
||||
"data": [
|
||||
(
|
||||
"2022-01-01",
|
||||
InspectValuesDateAdded(
|
||||
"20230120_010203-0400.jpg",
|
||||
"5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
|
||||
"2023-01-19 21:02:03-0800"
|
||||
if not is_dst()
|
||||
else "2023-01-19 20:02:03-0700",
|
||||
"2023-01-20 01:02:03-0400",
|
||||
"-0400",
|
||||
"GMT-0400",
|
||||
"2022-01-01 00:00:00-0800"
|
||||
if not is_dst()
|
||||
else "2022-01-01 00:00:00-0700",
|
||||
),
|
||||
),
|
||||
(
|
||||
"2022-01-01 01:02:03",
|
||||
InspectValuesDateAdded(
|
||||
"20230120_010203-0400.jpg",
|
||||
"5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
|
||||
"2023-01-19 21:02:03-0800"
|
||||
if not is_dst()
|
||||
else "2023-01-19 20:02:03-0700",
|
||||
"2023-01-20 01:02:03-0400",
|
||||
"-0400",
|
||||
"GMT-0400",
|
||||
"2022-01-01 01:02:03-0800"
|
||||
if not is_dst()
|
||||
else "2022-01-01 01:02:03-0700",
|
||||
),
|
||||
),
|
||||
],
|
||||
},
|
||||
"date_added_from_photo": {
|
||||
# 20230120_010203-0400.jpg
|
||||
"uuid": "5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
|
||||
"expected": InspectValuesDateAdded(
|
||||
"20230120_010203-0400.jpg",
|
||||
"5285C4E2-BB1A-49DF-AEF5-246AA337ACAB",
|
||||
"2023-01-19 21:02:03-0800" if not is_dst() else "2023-01-19 20:02:03-0700",
|
||||
"2023-01-20 01:02:03-0400",
|
||||
"-0400",
|
||||
"GMT-0400",
|
||||
"2023-01-19 21:02:03-0800" if not is_dst() else "2023-01-19 20:02:03-0700",
|
||||
),
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,12 +1,34 @@
|
||||
""" Parse --inspect and --compare-exif output for testing"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from collections import namedtuple
|
||||
from typing import List
|
||||
|
||||
# filename, uuid, photo time (local), photo time, timezone offset, timezone name
|
||||
InspectValues = namedtuple(
|
||||
"InspectValues",
|
||||
["filename", "uuid", "date_local", "date_tz", "tz_offset", "tz_name"],
|
||||
[
|
||||
"filename",
|
||||
"uuid",
|
||||
"date_local",
|
||||
"date_tz",
|
||||
"tz_offset",
|
||||
"tz_name",
|
||||
],
|
||||
)
|
||||
|
||||
InspectValuesDateAdded = namedtuple(
|
||||
"InspectValues",
|
||||
[
|
||||
"filename",
|
||||
"uuid",
|
||||
"date_local",
|
||||
"date_tz",
|
||||
"tz_offset",
|
||||
"tz_name",
|
||||
"date_added",
|
||||
],
|
||||
)
|
||||
|
||||
CompareValues = namedtuple(
|
||||
@ -22,7 +44,9 @@ CompareValues = namedtuple(
|
||||
)
|
||||
|
||||
|
||||
def parse_inspect_output(output: str) -> List[InspectValues]:
|
||||
def parse_inspect_output(
|
||||
output: str, date_added: bool = False
|
||||
) -> List[InspectValues] | List[InspectValuesDateAdded]:
|
||||
"""Parse output of --inspect and return list of InspectValues named tuple"""
|
||||
|
||||
lines = [line for line in output.split("\n") if line.strip()]
|
||||
@ -32,7 +56,12 @@ def parse_inspect_output(output: str) -> List[InspectValues]:
|
||||
for line in lines:
|
||||
parts = line.split(",")
|
||||
parts = [part.strip() for part in parts]
|
||||
values.append(InspectValues(*parts))
|
||||
if not date_added:
|
||||
# remove date added
|
||||
parts.pop()
|
||||
values.append(InspectValues(*parts))
|
||||
else:
|
||||
values.append(InspectValuesDateAdded(*parts))
|
||||
return values
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
""" Tests which require user interaction to run for osxphotos timewarp command """
|
||||
|
||||
import datetime
|
||||
import os
|
||||
import time
|
||||
|
||||
@ -9,7 +10,11 @@ from click.testing import CliRunner
|
||||
from osxphotos import PhotosDB
|
||||
from osxphotos.exiftool import ExifTool
|
||||
from tests.conftest import get_os_version
|
||||
from tests.parse_timewarp_output import parse_compare_exif, parse_inspect_output
|
||||
from tests.parse_timewarp_output import (
|
||||
InspectValuesDateAdded,
|
||||
parse_compare_exif,
|
||||
parse_inspect_output,
|
||||
)
|
||||
|
||||
# set timezone to avoid issues with comparing dates
|
||||
os.environ["TZ"] = "US/Pacific"
|
||||
@ -1055,3 +1060,61 @@ def test_parse_date_tz(photoslib, suspend_capture):
|
||||
assert output_values[0].date_local == expected.date_local
|
||||
assert output_values[0].date_tz == expected.date_tz
|
||||
assert output_values[0].tz_offset == expected.tz_offset
|
||||
|
||||
|
||||
@pytest.mark.timewarp
|
||||
@pytest.mark.parametrize(
|
||||
"date_added,expected",
|
||||
TEST_DATA["date_added"]["data"],
|
||||
)
|
||||
def test_date_added(
|
||||
photoslib, suspend_capture, date_added: str, expected: InspectValuesDateAdded
|
||||
):
|
||||
"""Test --date-added"""
|
||||
from osxphotos.cli.timewarp import timewarp
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
timewarp,
|
||||
[
|
||||
"--date-added",
|
||||
date_added,
|
||||
"--force",
|
||||
],
|
||||
terminal_width=TERMINAL_WIDTH,
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
result = runner.invoke(
|
||||
timewarp,
|
||||
["--inspect", "--plain", "--force"],
|
||||
terminal_width=TERMINAL_WIDTH,
|
||||
)
|
||||
output_values = parse_inspect_output(result.output, date_added=True)
|
||||
assert output_values[0].date_added == expected.date_added
|
||||
|
||||
|
||||
@pytest.mark.timewarp
|
||||
def test_date_added_from_photo(photoslib, suspend_capture):
|
||||
"""Test --date-added-from-photo"""
|
||||
from osxphotos.cli.timewarp import timewarp
|
||||
|
||||
runner = CliRunner()
|
||||
result = runner.invoke(
|
||||
timewarp,
|
||||
[
|
||||
"--date-added-from-photo",
|
||||
"--force",
|
||||
],
|
||||
terminal_width=TERMINAL_WIDTH,
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
result = runner.invoke(
|
||||
timewarp,
|
||||
["--inspect", "--plain", "--force"],
|
||||
terminal_width=TERMINAL_WIDTH,
|
||||
)
|
||||
output_values = parse_inspect_output(result.output, date_added=True)
|
||||
expected = TEST_DATA["date_added_from_photo"]["expected"]
|
||||
assert output_values[0].date_added == expected.date_added
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user