Repository: icloud-photos-downloader/icloud_photos_downloader

Issue:
When multiple sizes and --force-size are specified, some sizes can be skipped even if exist.
## Overview
When both `--size medium` and  `--size thumb` are specified with `--force-size`, but only the thumb-size asset exists for a photo, both sizes will be skipped.


## Steps to Reproduce
1. Upload a small image to iCloud Photos, such that the medium-size asset exists but thumb-size does not. (e.g. a 200*250 heic file)
2. Execute `icloudpd -d syncdir --size medium --size thumb --force-size`.


## Expected Behavior
The medium-size asset should be skipped and the thumb-size asset should be downloaded.


## Actual Behavior
Both medium-size and thumb-size assets are skipped.


## Context
Changing this to `continue` should be a simple fix.
https://github.com/icloud-photos-downloader/icloud_photos_downloader/blob/6e9c6c69996a39d0e7f77c9fd4f9b160b4b8bcdd/src/icloudpd/base.py#L896
I would be glad to issue a PR if you consider this appropriate.



Discussion:
Thanks for reporting the issue. Yes, you can submit PR. Please include tests. 

I am curious how are you using medium & thumb sized assets?
I have created a PR with tests.

I am developing an application that utilizes the assets to preview the photos.

It would be helpful to me if `--live-photo-size` also supports multiple sizes. I can work on that if you think it's a good idea.
> It would be helpful to me if `--live-photo-size` also supports multiple sizes. I can work on that if you think it's a good idea.

I did not see requests for live photo and main assets sizes, so I suspect that with the increase of the internet speed, ppl are using original size. From that point of view, I do not see much value in implementing multiple size support for live photos.





Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4c276602d..309703f46 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+- fix: force_size should not skip subsequent sizes [ref](https://github.com/icloud-photos-downloader/icloud_photos_downloader/issues/955)
+
 ## 1.23.4 (2024-09-02)
 
 - fix: support plain text encoding for filename in addition to base64 [ref](https://github.com/boredazfcuk/docker-icloudpd/issues/641)
diff --git a/src/icloudpd/base.py b/src/icloudpd/base.py
index 30f9f71ac..96620fc74 100644
--- a/src/icloudpd/base.py
+++ b/src/icloudpd/base.py
@@ -893,7 +893,7 @@ def download_photo_(counter: Counter, photo: PhotoAsset) -> bool:
                             download_size.value,
                             photo.filename,
                         )
-                        return False
+                        continue
                     if AssetVersionSize.ORIGINAL in size:
                         continue  # that should avoid double download for original
                     download_size = AssetVersionSize.ORIGINAL


Test patch:
diff --git a/tests/test_download_photos.py b/tests/test_download_photos.py
index 44851d345..ded3cf4b3 100644
--- a/tests/test_download_photos.py
+++ b/tests/test_download_photos.py
@@ -825,6 +825,70 @@ def test_force_size(self) -> None:
 
                 assert result.exit_code == 0
 
+    def test_download_two_sizes_with_force_size(self) -> None:
+        base_dir = os.path.join(self.fixtures_path, inspect.stack()[0][3])
+        with mock.patch("icloudpd.download.download_media") as dp_patched:
+            dp_patched.return_value = True
+
+            with mock.patch("icloudpd.download.os.utime") as ut_patched:
+                ut_patched.return_value = None
+
+                with mock.patch.object(PhotoAsset, "versions", new_callable=PropertyMock) as pa:
+                    pa.return_value = {
+                        AssetVersionSize.ORIGINAL: AssetVersion("IMG_7409.JPG", 1, "http", "jpeg"),
+                        AssetVersionSize.THUMB: AssetVersion("IMG_7409.JPG", 1, "http", "jpeg"),
+                    }
+
+                    data_dir, result = run_icloudpd_test(
+                        self.assertEqual,
+                        self.vcr_path,
+                        base_dir,
+                        "listing_photos.yml",
+                        [],
+                        [],
+                        [
+                            "--username",
+                            "jdoe@gmail.com",
+                            "--password",
+                            "password1",
+                            "--recent",
+                            "1",
+                            "--size",
+                            "medium",
+                            "--size",
+                            "thumb",
+                            "--force-size",
+                            "--no-progress-bar",
+                            "--threads-num",
+                            "1",
+                        ],
+                    )
+
+                    self.assertIn(
+                        "DEBUG    Looking up all photos and videos from album All Photos...",
+                        self._caplog.text,
+                    )
+                    self.assertIn(
+                        f"INFO     Downloading the first medium,thumb photo or video to {data_dir} ...",
+                        self._caplog.text,
+                    )
+                    self.assertIn(
+                        "ERROR    medium size does not exist for IMG_7409.JPG. Skipping...",
+                        self._caplog.text,
+                    )
+                    self.assertIn("INFO     All photos have been downloaded", self._caplog.text)
+                    dp_patched.assert_called_once_with(
+                        ANY,
+                        False,
+                        ANY,
+                        ANY,
+                        f"{os.path.join(data_dir, os.path.normpath('2018/07/31/IMG_7409.JPG'))}",
+                        ANY,
+                        AssetVersionSize.THUMB,
+                    )
+
+                    assert result.exit_code == 0
+
     def test_invalid_creation_date(self) -> None:
         base_dir = os.path.join(self.fixtures_path, inspect.stack()[0][3])