From d947018f421f93ab7dc53eacaff5cd46b6c05a20 Mon Sep 17 00:00:00 2001 From: Kyle Bolen Date: Tue, 28 Jul 2026 08:17:39 +0000 Subject: [PATCH] Fix iOS helper: correct date parsing and pull method Date: AFC st_mtime is nanoseconds since epoch. Fix threshold check: values > 1e18 are nanoseconds, > 1e9 are seconds. Dec 31 bug was caused by mtime being treated as seconds when it was nanoseconds, giving a timestamp near epoch (1970 = Dec 31 local time). Pull: use get_file_contents() which is the correct async AFC method (not pull/afc.open which don't exist in this pymobiledevice3 version). --- .../resources/scripts/photophetch-ios-helper.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/main/resources/scripts/photophetch-ios-helper.py b/src/main/resources/scripts/photophetch-ios-helper.py index 2f502f6..dc0f769 100644 --- a/src/main/resources/scripts/photophetch-ios-helper.py +++ b/src/main/resources/scripts/photophetch-ios-helper.py @@ -90,9 +90,16 @@ async def cmd_list_photos(): try: info = await afc.stat(device_path) size_bytes = int(info.get('st_size', 0)) - mtime_ns = int(info.get('st_mtime', 0)) - # st_mtime may be nanoseconds or seconds depending on version - mtime_s = mtime_ns / 1_000_000_000 if mtime_ns > 1_700_000_000_000 else mtime_ns + mtime_raw = int(info.get('st_mtime', 0)) + # AFC st_mtime is nanoseconds since epoch (not seconds) + # A reasonable photo date in nanoseconds is > 1e18 + # In seconds it would be > 1e9 (year 2001+) + if mtime_raw > 1_000_000_000_000_000_000: + mtime_s = mtime_raw / 1_000_000_000 + elif mtime_raw > 1_000_000_000: + mtime_s = mtime_raw # already seconds + else: + mtime_s = 0 if mtime_s > 0: date_iso = datetime.fromtimestamp(mtime_s).isoformat() except Exception: @@ -120,7 +127,9 @@ async def cmd_pull(device_path: str, local_path: str): lockdown = await get_lockdown() afc = await get_afc(lockdown) os.makedirs(os.path.dirname(os.path.abspath(local_path)), exist_ok=True) - await afc.pull(device_path, local_path) + data = await afc.get_file_contents(device_path) + with open(local_path, 'wb') as f: + f.write(data) print(json.dumps({'ok': True, 'path': local_path})) except Exception as e: print(f'Error: {e}', file=sys.stderr)