Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add new thumbnail flag #336

Closed
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
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ Options:
Download live photos)
--force-size Only download the requested size (default:
download original if size is not available)
--thumbnail Only download the first photo as thumbnail
of album as it is shown in the device
(-a, --album option is required)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why is -a required?

--auto-delete Scans the "Recently Deleted" folder and
deletes any files found in there. (If you
restore the photo in iCloud, it will be
Expand Down
14 changes: 12 additions & 2 deletions icloudpd/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,11 @@
type=click.IntRange(1),
default=1,
)
@click.option(
"--thumbnail",
help="Get the first photo of album as thumbnail as is shown in the device",
is_flag=True,
)
@click.version_option()
# pylint: disable-msg=too-many-arguments,too-many-statements
# pylint: disable-msg=too-many-branches,too-many-locals
Expand Down Expand Up @@ -224,6 +229,7 @@ def main(
no_progress_bar,
notification_script,
threads_num, # pylint: disable=W0613
thumbnail,
):
"""Download all iCloud photos to a local directory"""

Expand Down Expand Up @@ -321,6 +327,10 @@ def photos_exception_handler(ex, retries):

photos_count = len(photos)

if thumbnail and album is not None:
photos = itertools.islice(photos, photos.page_size-1, photos.page_size)
photos_count = 1

# Optional: Only download the x most recent photos.
if recent is not None:
photos_count = recent
Expand All @@ -334,9 +344,9 @@ def photos_exception_handler(ex, retries):
# ensure photos iterator doesn't have a known length
photos = (p for p in photos)

plural_suffix = "" if photos_count == 1 else "s"
plural_suffix = "" if photos_count == 1 or thumbnail else "s"
video_suffix = ""
photos_count_str = "the first" if photos_count == 1 else photos_count
photos_count_str = "the first" if photos_count == 1 else "thumbnail" if thumbnail else photos_count
if not skip_videos:
video_suffix = " or video" if photos_count == 1 else " and videos"
logger.info(
Expand Down
48 changes: 48 additions & 0 deletions tests/test_download_photos.py
Original file line number Diff line number Diff line change
Expand Up @@ -1295,3 +1295,51 @@ def test_download_chinese(self):
photo_modified_time.strftime('%Y-%m-%d %H:%M:%S'))

assert result.exit_code == 0

def test_get_thumbnail_photo_of_album(self):
base_dir = os.path.normpath(f"tests/fixtures/Photos/{inspect.stack()[0][3]}")
if os.path.exists(base_dir):
shutil.rmtree(base_dir)
os.makedirs(base_dir)

with vcr.use_cassette("tests/vcr_cassettes/listing_photos.yml"):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Best would be to add a new cassette for this test case. Creating a new cassette is pretty easy. Just use a new filename and run the test again. Make sure to remove personal data from the cassette before you check in.

# Pass fixed client ID via environment variable

def mock_raise_response_error():
raise PyiCloudAPIResponseError("Api Error", 100)

with mock.patch.object(PhotosService, "_fetch_folders") as pa_photos_request:
pa_photos_request.side_effect = mock_raise_response_error

# Let the initial authenticate() call succeed,
# but do nothing on the second try.
orig_authenticate = PyiCloudService.authenticate

def mocked_authenticate(self):
if not hasattr(self, "already_authenticated"):
orig_authenticate(self)
setattr(self, "already_authenticated", True)

with mock.patch("icloudpd.constants.WAIT_SECONDS", 0):
with mock.patch.object(
PyiCloudService, "authenticate", new=mocked_authenticate
):
runner = CliRunner(env={
"CLIENT_ID": "DE309E26-942E-11E8-92F5-14109FE0B321"
})
result = runner.invoke(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are running the script but don't have any assertions/tests. I suggest checking for the pictures you are expecting to be downloaded. Additionally you should do a negative test, p.e. run without your new parameter (so that more pictures are downloaded) and check that the second picture that would come is not been downloaded when run with --thumbnail.

main,
[
"--username",
"[email protected]",
"--password",
"password1",
"--no-progress-bar",
"--thumbnail",
"--threads-num",
1,
"-d",
base_dir,
],
)
print_result_exception(result)