From 9f54cbfc764c6443e9fa2540dd399a5d90be3918 Mon Sep 17 00:00:00 2001 From: Andrey Nikiforov Date: Sun, 3 Nov 2024 09:44:53 -0800 Subject: [PATCH] fix crash when itemtype is not present in Apple response (#988) --- CHANGELOG.md | 2 + src/icloudpd/base.py | 2 +- src/pyicloud_ipd/services/photos.py | 10 +- tests/test_download_photos.py | 92 +++++++++--------- tests/test_download_photos_id.py | 96 +++++++++---------- ...l => listing_photos_missing_item_type.yml} | 0 6 files changed, 101 insertions(+), 101 deletions(-) rename tests/vcr_cassettes/{listing_photos_bad_item_type.yml => listing_photos_missing_item_type.yml} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3ed1546ce..d3a37e7c4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ## Unreleased +- fix: crashes when no imagetype sent by Apple [ref](https://github.com/boredazfcuk/docker-icloudpd/issues/680) + ## 1.24.2 (2024-11-02) - fix: errors for accounts with salt started with zero byte [#975](https://github.com/icloud-photos-downloader/icloud_photos_downloader/issues/975) diff --git a/src/icloudpd/base.py b/src/icloudpd/base.py index 319dffa58..47cb41579 100644 --- a/src/icloudpd/base.py +++ b/src/icloudpd/base.py @@ -819,7 +819,7 @@ def state_(icloud: PyiCloudService) -> Callable[[Counter, PhotoAsset], bool]: def download_photo_(counter: Counter, photo: PhotoAsset) -> bool: """internal function for actually downloading the photos""" - if skip_videos and photo.item_type != AssetItemType.IMAGE: + if skip_videos and photo.item_type == AssetItemType.MOVIE: logger.debug( "Skipping %s, only downloading photos." + "(Item type was: %s)", photo.filename, diff --git a/src/pyicloud_ipd/services/photos.py b/src/pyicloud_ipd/services/photos.py index 53defd0a9..dc17a1485 100644 --- a/src/pyicloud_ipd/services/photos.py +++ b/src/pyicloud_ipd/services/photos.py @@ -704,13 +704,15 @@ def dimensions(self) -> Tuple[int, int]: self._master_record['fields']['resOriginalHeight']['value']) @property - def item_type(self) -> AssetItemType: + def item_type(self) -> Optional[AssetItemType]: fields = self._master_record['fields'] if 'itemType' not in fields: - raise ValueError(f"Cannot find itemType in {fields!r}") + # raise ValueError(f"Cannot find itemType in {fields!r}") + return None item_type_field = fields['itemType'] if 'value' not in item_type_field: - raise ValueError(f"Cannot find value in itemType {item_type_field!r}") + # raise ValueError(f"Cannot find value in itemType {item_type_field!r}") + return None item_type = item_type_field['value'] if item_type in self.ITEM_TYPES: return self.ITEM_TYPES[item_type] @@ -777,7 +779,7 @@ def versions(self) -> Dict[VersionSize, AssetVersion]: # version['type'] = None # Change live photo movie file extension to .MOV - if (self.item_type == AssetItemType.IMAGE and + if ((self.item_type or AssetItemType.IMAGE) == AssetItemType.IMAGE and version['type'] == "com.apple.quicktime-movie"): version['filename'] = self._service.lp_filename_generator(self.filename) # without size else: diff --git a/tests/test_download_photos.py b/tests/test_download_photos.py index cb38b1bd5..2106abce8 100644 --- a/tests/test_download_photos.py +++ b/tests/test_download_photos.py @@ -1003,62 +1003,58 @@ def astimezone(self, _tz: (Optional[Any]) = None) -> NoReturn: def test_missing_item_type(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 - - data_dir, result = run_icloudpd_test( - self.assertEqual, - self.root_path, - base_dir, - "listing_photos_bad_item_type.yml", - [], - [], - [ - "--username", - "jdoe@gmail.com", - "--password", - "password1", - "--recent", - "1", - "--no-progress-bar", - "--threads-num", - "1", - ], - ) + files_to_download = [ + ("2018/07/31", "IMG_7409.JPG"), + ] - dp_patched.assert_not_called() + _, result = run_icloudpd_test( + self.assertEqual, + self.root_path, + base_dir, + "listing_photos_missing_item_type.yml", + [], + files_to_download, + [ + "--username", + "jdoe@gmail.com", + "--password", + "password1", + "--recent", + "1", + "--no-progress-bar", + "--skip-live-photos", + ], + ) - self.assertIsInstance(result.exception, ValueError) + assert result.exit_code == 0 def test_missing_item_type_value(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 - - data_dir, result = run_icloudpd_test( - self.assertEqual, - self.root_path, - base_dir, - "listing_photos_missing_item_type_value.yml", - [], - [], - [ - "--username", - "jdoe@gmail.com", - "--password", - "password1", - "--recent", - "1", - "--no-progress-bar", - "--threads-num", - "1", - ], - ) + files_to_download = [ + ("2018/07/31", "IMG_7409.JPG"), + ] - dp_patched.assert_not_called() + _, result = run_icloudpd_test( + self.assertEqual, + self.root_path, + base_dir, + "listing_photos_missing_item_type_value.yml", + [], + files_to_download, + [ + "--username", + "jdoe@gmail.com", + "--password", + "password1", + "--recent", + "1", + "--no-progress-bar", + "--skip-live-photos", + ], + ) - self.assertIsInstance(result.exception, ValueError) + assert result.exit_code == 0 def test_download_and_dedupe_existing_photos(self) -> None: base_dir = os.path.join(self.fixtures_path, inspect.stack()[0][3]) diff --git a/tests/test_download_photos_id.py b/tests/test_download_photos_id.py index ec54d2184..ffccfd19a 100644 --- a/tests/test_download_photos_id.py +++ b/tests/test_download_photos_id.py @@ -952,62 +952,62 @@ def astimezone(self, _tz: (Optional[Any]) = None) -> NoReturn: def test_missing_item_type_name_id7(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 - - data_dir, result = run_icloudpd_test( - self.assertEqual, - self.root_path, - base_dir, - "listing_photos_bad_item_type.yml", - [], - [], - [ - "--username", - "jdoe@gmail.com", - "--password", - "password1", - "--recent", - "1", - "--no-progress-bar", - "--file-match-policy", - "name-id7", - ], - ) + files_to_download = [ + ("2018/07/31", "IMG_7409_QVk2Yyt.JPG"), + ] - dp_patched.assert_not_called() + _, result = run_icloudpd_test( + self.assertEqual, + self.root_path, + base_dir, + "listing_photos_missing_item_type.yml", + [], + files_to_download, + [ + "--username", + "jdoe@gmail.com", + "--password", + "password1", + "--recent", + "1", + "--no-progress-bar", + "--file-match-policy", + "name-id7", + "--skip-live-photos", + ], + ) - self.assertIsInstance(result.exception, ValueError) + assert result.exit_code == 0 def test_missing_item_type_value_name_id7(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 - - data_dir, result = run_icloudpd_test( - self.assertEqual, - self.root_path, - base_dir, - "listing_photos_missing_item_type_value.yml", - [], - [], - [ - "--username", - "jdoe@gmail.com", - "--password", - "password1", - "--recent", - "1", - "--no-progress-bar", - "--file-match-policy", - "name-id7", - ], - ) + files_to_download = [ + ("2018/07/31", "IMG_7409_QVk2Yyt.JPG"), + ] - dp_patched.assert_not_called() + _, result = run_icloudpd_test( + self.assertEqual, + self.root_path, + base_dir, + "listing_photos_missing_item_type_value.yml", + [], + files_to_download, + [ + "--username", + "jdoe@gmail.com", + "--password", + "password1", + "--recent", + "1", + "--no-progress-bar", + "--file-match-policy", + "name-id7", + "--skip-live-photos", + ], + ) - self.assertIsInstance(result.exception, ValueError) + assert result.exit_code == 0 def test_download_and_dedupe_existing_photos_name_id7(self) -> None: base_dir = os.path.join(self.fixtures_path, inspect.stack()[0][3]) diff --git a/tests/vcr_cassettes/listing_photos_bad_item_type.yml b/tests/vcr_cassettes/listing_photos_missing_item_type.yml similarity index 100% rename from tests/vcr_cassettes/listing_photos_bad_item_type.yml rename to tests/vcr_cassettes/listing_photos_missing_item_type.yml