Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

## Unreleased

- fix: disambiguate whole photo collection from the album with `All Photos` name [#1077](https://github.com/icloud-photos-downloader/icloud_photos_downloader/issues/1077)

## 1.27.0 (2025-02-22)

- feature: list and download from shared libraries for invitee [#947](https://github.com/icloud-photos-downloader/icloud_photos_downloader/issues/947)
Expand Down
2 changes: 1 addition & 1 deletion docs/reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ This is a list of all options available for command line interface (CLI) of the

: Specifies what Album to download.

Default: "All Photos" - Special album where all assets are automatically added.
When not specified, the whole asset collection is considered.

```{note}
Only one album can be downloaded by `icloudpd`
Expand Down
14 changes: 6 additions & 8 deletions src/icloudpd/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,8 @@ def report_version(ctx: click.Context, _param: click.Parameter, value: bool) ->
@click.option(
"-a",
"--album",
help="Album to download (default: All Photos)",
help="Album to download or whole collection if not specified",
metavar="<album>",
default="All Photos",
)
@click.option(
"-l",
Expand Down Expand Up @@ -589,7 +588,7 @@ def main(
live_photo_size: LivePhotoVersionSize,
recent: Optional[int],
until_found: Optional[int],
album: str,
album: Optional[str],
list_albums: bool,
library: str,
list_libraries: bool,
Expand Down Expand Up @@ -1168,7 +1167,7 @@ def core(
primary_sizes: Sequence[AssetVersionSize],
recent: Optional[int],
until_found: Optional[int],
album: str,
album: Optional[str],
list_albums: bool,
library: str,
list_libraries: bool,
Expand Down Expand Up @@ -1258,8 +1257,6 @@ def core(

else:
while True:
# Default album is "All Photos", so this is the same as
# calling `icloud.photos.all`.
# After 6 or 7 runs within 1h Apple blocks the API for some time. In that
# case exit.
try:
Expand All @@ -1271,7 +1268,7 @@ def core(
else:
logger.error("Unknown library: %s", library)
return 1
photos = library_object.albums[album]
photos = library_object.albums[album] if album else library_object.all
except PyiCloudAPIResponseException as err:
# For later: come up with a nicer message to the user. For now take the
# exception text
Expand All @@ -1290,7 +1287,8 @@ def core(
directory = os.path.normpath(cast(str, directory))

videos_phrase = "" if skip_videos else " and videos"
logger.debug("Looking up all photos%s from album %s...", videos_phrase, album)
album_phrase = f" from album {album}" if album else ""
logger.debug(f"Looking up all photos{videos_phrase}{album_phrase}...")

session_exception_handler = session_error_handle_builder(logger, icloud)
internal_error_handler = internal_error_handle_builder(logger)
Expand Down
2 changes: 1 addition & 1 deletion src/icloudpd/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def __init__(
live_photo_size: LivePhotoVersionSize,
recent: Optional[int],
until_found: Optional[int],
album: str,
album: Optional[str],
list_albums: bool,
library: str,
list_libraries: bool,
Expand Down
13 changes: 9 additions & 4 deletions src/pyicloud_ipd/services/photos.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ class PhotoLibrary(object):

This provides access to all the albums as well as the photos.
"""
SMART_FOLDERS = {
"All Photos": {
WHOLE_COLLECTION = {
"obj_type": "CPLAssetByAssetDateWithoutHiddenOrDeleted",
"list_type": "CPLAssetAndMasterByAssetDateWithoutHiddenOrDeleted",
"direction": "ASCENDING",
"query_filter": None
},
}
SMART_FOLDERS = {
"Time-lapse": {
"obj_type": "CPLAssetInSmartAlbumByAssetDate:Timelapse",
"list_type": "CPLAssetAndMasterInSmartAlbumByAssetDate",
Expand Down Expand Up @@ -161,6 +161,7 @@ def __init__(self, service: "PhotosService", zone_id: Dict[str, Any], library_ty
self.service_endpoint = self.service.get_service_endpoint(library_type)

self._albums: Optional[Dict[str, PhotoAlbum]] = None
self._all: Optional[PhotoAlbum] = None

url = ('%s/records/query?%s' %
(self.service_endpoint, urlencode(self.service.params)))
Expand Down Expand Up @@ -240,7 +241,11 @@ def _fetch_folders(self) -> Sequence[Dict[str, Any]]:

@property
def all(self) -> "PhotoAlbum":
return self.albums['All Photos']
if not self._all:
self._all = PhotoAlbum(self.service, self.service_endpoint, "", zone_id=self.zone_id, **self.WHOLE_COLLECTION) # type: ignore[arg-type] # dynamically builing params
# need to pull all albums because existing recorded tests expect all albums to be pulled...
self.albums
return self._all


class PhotosService(PhotoLibrary):
Expand Down
36 changes: 13 additions & 23 deletions tests/test_autodelete_photos.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def astimezone(self, _tz: (Optional[Any]) = None) -> NoReturn:
print_result_exception(result)

self.assertIn(
"DEBUG Looking up all photos and videos from album All Photos...",
"DEBUG Looking up all photos and videos...",
self._caplog.text,
)
self.assertIn(
Expand Down Expand Up @@ -122,7 +122,7 @@ def astimezone(self, _tz: (Optional[Any]) = None) -> NoReturn:
print_result_exception(result)

self.assertIn(
"DEBUG Looking up all photos and videos from album All Photos...",
"DEBUG Looking up all photos and videos...",
self._caplog.text,
)
self.assertIn(
Expand Down Expand Up @@ -182,7 +182,7 @@ def test_download_autodelete_photos(self) -> None:
print_result_exception(result)

self.assertIn(
"DEBUG Looking up all photos and videos from album All Photos...",
"DEBUG Looking up all photos and videos...",
self._caplog.text,
)
self.assertIn(
Expand Down Expand Up @@ -225,7 +225,7 @@ def test_download_autodelete_photos(self) -> None:
print_result_exception(result)

self.assertIn(
"DEBUG Looking up all photos and videos from album All Photos...",
"DEBUG Looking up all photos and videos...",
self._caplog.text,
)
self.assertIn(
Expand Down Expand Up @@ -311,9 +311,7 @@ def test_autodelete_photos(self) -> None:
cookie_dir,
],
)
self.assertIn(
"DEBUG Looking up all photos from album All Photos...", self._caplog.text
)
self.assertIn("DEBUG Looking up all photos...", self._caplog.text)
self.assertIn(
f"INFO Downloading 0 original photos to {data_dir} ...",
self._caplog.text,
Expand Down Expand Up @@ -424,7 +422,7 @@ def mocked_authenticate(self: PyiCloudService) -> None:
print_result_exception(result)

self.assertIn(
"DEBUG Looking up all photos and videos from album All Photos...",
"DEBUG Looking up all photos and videos...",
self._caplog.text,
)
self.assertIn(
Expand Down Expand Up @@ -519,7 +517,7 @@ def mocked_authenticate(self: PyiCloudService) -> None:
print_result_exception(result)

self.assertIn(
"DEBUG Looking up all photos and videos from album All Photos...",
"DEBUG Looking up all photos and videos...",
self._caplog.text,
)
self.assertIn(
Expand Down Expand Up @@ -606,7 +604,7 @@ def mock_raise_response_error(
print_result_exception(result)

self.assertIn(
"DEBUG Looking up all photos and videos from album All Photos...",
"DEBUG Looking up all photos and videos...",
self._caplog.text,
)
self.assertIn(
Expand Down Expand Up @@ -687,7 +685,7 @@ def mock_raise_response_error(
print_result_exception(result)

self.assertIn(
"DEBUG Looking up all photos and videos from album All Photos...",
"DEBUG Looking up all photos and videos...",
self._caplog.text,
)
self.assertIn(
Expand Down Expand Up @@ -791,9 +789,7 @@ def test_autodelete_photos_dry_run(self) -> None:
)
print_result_exception(result)

self.assertIn(
"DEBUG Looking up all photos from album All Photos...", self._caplog.text
)
self.assertIn("DEBUG Looking up all photos...", self._caplog.text)
self.assertIn(
f"INFO Downloading 0 original photos to {data_dir} ...",
self._caplog.text,
Expand Down Expand Up @@ -891,9 +887,7 @@ def test_autodelete_photos_folder_none(self) -> None:
cookie_dir,
],
)
self.assertIn(
"DEBUG Looking up all photos from album All Photos...", self._caplog.text
)
self.assertIn("DEBUG Looking up all photos...", self._caplog.text)
self.assertIn(
f"INFO Downloading 0 original photos to {data_dir} ...",
self._caplog.text,
Expand Down Expand Up @@ -988,9 +982,7 @@ def test_autodelete_photos_lp(self) -> None:
cookie_dir,
],
)
self.assertIn(
"DEBUG Looking up all photos from album All Photos...", self._caplog.text
)
self.assertIn("DEBUG Looking up all photos...", self._caplog.text)
self.assertIn(
f"INFO Downloading 0 original photos to {data_dir} ...",
self._caplog.text,
Expand Down Expand Up @@ -1085,9 +1077,7 @@ def test_autodelete_photos_lp_heic(self) -> None:
cookie_dir,
],
)
self.assertIn(
"DEBUG Looking up all photos from album All Photos...", self._caplog.text
)
self.assertIn("DEBUG Looking up all photos...", self._caplog.text)
self.assertIn(
f"INFO Downloading 0 original photos to {data_dir} ...",
self._caplog.text,
Expand Down
4 changes: 1 addition & 3 deletions tests/test_download_live_photos.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,7 @@ def test_skip_existing_live_photodownloads(self) -> None:
],
)

self.assertIn(
"DEBUG Looking up all photos and videos from album All Photos...", self._caplog.text
)
self.assertIn("DEBUG Looking up all photos and videos...", self._caplog.text)
self.assertIn(
f"INFO Downloading 3 original photos and videos to {data_dir} ...",
self._caplog.text,
Expand Down
4 changes: 1 addition & 3 deletions tests/test_download_live_photos_id.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,7 @@ def test_skip_existing_live_photodownloads_name_id7(self) -> None:
],
)

self.assertIn(
"DEBUG Looking up all photos and videos from album All Photos...", self._caplog.text
)
self.assertIn("DEBUG Looking up all photos and videos...", self._caplog.text)
self.assertIn(
f"INFO Downloading 3 original photos and videos to {data_dir} ...",
self._caplog.text,
Expand Down
Loading
Loading