Skip to content

Commit 40acdcc

Browse files
authored
fix ignored photos with --delete-after-download or --keep-icloud-recent-days (#1096)
1 parent e616155 commit 40acdcc

File tree

5 files changed

+21
-17
lines changed

5 files changed

+21
-17
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
## Unreleased
44

5+
- fix: fix ignored photos with --delete-after-download or --keep-icloud-recent-days [#616](https://github.com/icloud-photos-downloader/icloud_photos_downloader/issues/616)
56
- fix: timeout set to 30 seconds for HTTP requests [#793](https://github.com/icloud-photos-downloader/icloud_photos_downloader/issues/793)
67

78
## 1.27.4 (2025-04-15)

src/icloudpd/base.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1403,6 +1403,8 @@ def should_break(counter: Counter) -> bool:
14031403
)
14041404

14051405
retrier(delete_local, error_handler)
1406+
if photos.direction != "DESCENDING":
1407+
photos.increment_offset(-1)
14061408

14071409
photos_counter += 1
14081410
status_exchange.get_progress().photos_counter = photos_counter

src/pyicloud_ipd/services/photos.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,10 @@ def __init__(self, service:PhotosService, service_endpoint: str, name: str, list
349349
self.list_type = list_type
350350
self.obj_type = obj_type
351351
self.direction = direction
352+
if self.direction == "DESCENDING":
353+
self.offset = len(self) - 1
354+
else:
355+
self.offset = 0
352356
self.query_filter = query_filter
353357
self.page_size = page_size
354358
self.exception_handler: Optional[Callable[[Exception, int], None]] = None
@@ -386,30 +390,25 @@ def __len__(self) -> int:
386390

387391
# Perform the request in a separate method so that we
388392
# can mock it to test session errors.
389-
def photos_request(self, offset: int) -> Response:
393+
def photos_request(self) -> Response:
390394
url = ('%s/records/query?' % self.service_endpoint) + \
391395
urlencode(self.service.params)
392396
return self.service.session.post(
393397
url,
394398
data=json.dumps(self._list_query_gen(
395-
offset, self.list_type, self.direction,
399+
self.offset, self.list_type, self.direction,
396400
self.query_filter)),
397401
headers={'Content-type': 'text/plain'}
398402
)
399403

400404

401405
@property
402406
def photos(self) -> Generator["PhotoAsset", Any, None]:
403-
if self.direction == "DESCENDING":
404-
offset = len(self) - 1
405-
else:
406-
offset = 0
407-
408407
exception_retries = 0
409408

410409
while(True):
411410
try:
412-
request = self.photos_request(offset)
411+
request = self.photos_request()
413412
except PyiCloudAPIResponseException as ex:
414413
if self.exception_handler:
415414
exception_retries += 1
@@ -446,18 +445,20 @@ def photos(self) -> Generator["PhotoAsset", Any, None]:
446445

447446
master_records_len = len(master_records)
448447
if master_records_len:
449-
if self.direction == "DESCENDING":
450-
offset = offset - master_records_len
451-
else:
452-
offset = offset + master_records_len
453-
454448
for master_record in master_records:
455449
record_name = master_record['recordName']
456450
yield PhotoAsset(self.service, master_record,
457451
asset_records[record_name])
452+
self.increment_offset(1)
458453
else:
459454
break
460455

456+
def increment_offset(self, value: int) -> None:
457+
if self.direction == "DESCENDING":
458+
self.offset -= value
459+
else:
460+
self.offset += value
461+
461462
def _count_query_gen(self, obj_type: str) -> Dict[str, Any]:
462463
query = {
463464
u'batch': [{

tests/test_download_photos.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ def mocked_authenticate(self: PyiCloudService) -> None:
491491
def test_handle_session_error_during_photo_iteration(self) -> None:
492492
base_dir = os.path.join(self.fixtures_path, inspect.stack()[0][3])
493493

494-
def mock_raise_response_error(_offset: int) -> NoReturn:
494+
def mock_raise_response_error() -> NoReturn:
495495
raise PyiCloudAPIResponseException("Invalid global session", "100")
496496

497497
with mock.patch("time.sleep") as sleep_mock: # noqa: SIM117
@@ -1760,7 +1760,7 @@ def mock_raise_response_error(_arg: Any) -> NoReturn:
17601760
def test_handle_internal_error_during_photo_iteration(self) -> None:
17611761
base_dir = os.path.join(self.fixtures_path, inspect.stack()[0][3])
17621762

1763-
def mock_raise_response_error(_offset: int) -> NoReturn:
1763+
def mock_raise_response_error() -> NoReturn:
17641764
raise PyiCloudAPIResponseException("INTERNAL_ERROR", "INTERNAL_ERROR")
17651765

17661766
with mock.patch("time.sleep") as sleep_mock: # noqa: SIM117

tests/test_download_photos_id.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -491,7 +491,7 @@ def mocked_authenticate(self: PyiCloudService) -> None:
491491
def test_handle_session_error_during_photo_iteration_name_id7(self) -> None:
492492
base_dir = os.path.join(self.fixtures_path, inspect.stack()[0][3])
493493

494-
def mock_raise_response_error(_offset: int) -> NoReturn:
494+
def mock_raise_response_error() -> NoReturn:
495495
raise PyiCloudAPIResponseException("Invalid global session", "100")
496496

497497
with mock.patch("time.sleep") as sleep_mock: # noqa: SIM117
@@ -1744,7 +1744,7 @@ def mock_raise_response_error(_arg: Any) -> NoReturn:
17441744
def test_handle_internal_error_during_photo_iteration_name_id7(self) -> None:
17451745
base_dir = os.path.join(self.fixtures_path, inspect.stack()[0][3])
17461746

1747-
def mock_raise_response_error(_offset: int) -> NoReturn:
1747+
def mock_raise_response_error() -> NoReturn:
17481748
raise PyiCloudAPIResponseException("INTERNAL_ERROR", "INTERNAL_ERROR")
17491749

17501750
with mock.patch("time.sleep") as sleep_mock: # noqa: SIM117

0 commit comments

Comments
 (0)