diff --git a/.gitignore b/.gitignore index 5ec5dbf..6bcc82e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,9 @@ /Google_Images_Search.egg-info/ /build/ /dist/ -/google_images_search/ /tests/__pycache__/ /.DS_Store .venv /tests/test_my.py +/google_images_search/__pycache__/ +/dwnld/ diff --git a/.travis.yml b/.travis.yml index 3b21034..dc6918f 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,9 @@ python: - 3.9 script: - - pip install . + - pip3 install . + - pip3 install pytest + - pip list - pytest -vvv deploy: diff --git a/CHANGELOG.md b/CHANGELOG.md index 2481393..7c35829 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,18 @@ # Changelog +## 1.3.7 + +### Fixed in 1.3.7 + +- Handling CLI exception when api key and cx are not provided +- Handling PIL open and rgb convert exception + +## Added in 1.3.7 + +- Curses terminal progress is now started and ended using context (with statement) +- CLI also uses contextual progress +- Better progress output in CLI overall + ## 1.3.6 ### Fixed in 1.3.6 diff --git a/README.md b/README.md index 5bd2f56..958a8cb 100644 --- a/README.md +++ b/README.md @@ -162,16 +162,28 @@ gis = GoogleImagesSearch('your_dev_api_key', 'your_project_cx', validate_images= ## [Inserting custom progressbar function](#progressbar) +By default, progressbar is not enabled. +Only in CLI progressbar is enabled by default using [Curses library](https://docs.python.org/3/howto/curses.html). +In a programmatic mode it can be enabled in two ways: +- using contextual mode (Curses) +- using your custom progressbar function + ```python from google_images_search import GoogleImagesSearch +# using your custom progressbar function def my_progressbar(url, progress): print(url + ' ' + progress + '%') - gis = GoogleImagesSearch( 'your_dev_api_key', 'your_project_cx', progressbar_fn=my_progressbar ) +_search_params = {...} +gis.search(search_params=_search_params) +# using contextual mode (Curses) +with GoogleImagesSearch('your_dev_api_key', 'your_project_cx') as gis: + _search_params = {...} + gis.search(search_params=_search_params) ... ``` diff --git a/google_images_search/cli.py b/google_images_search/cli.py index 44e605f..c84de72 100644 --- a/google_images_search/cli.py +++ b/google_images_search/cli.py @@ -1,7 +1,8 @@ import click +import googleapiclient -from .fetch_resize_save import FetchResizeSave from .google_api import GoogleBackendException +from .fetch_resize_save import FetchResizeSave, __version__ @click.group() @@ -9,9 +10,13 @@ @click.option('-k', '--developer_key', help='Developer API key') @click.option('-c', '--custom_search_cx', help='Custom Search CX') def cli(ctx, developer_key, custom_search_cx): + click.echo() + click.secho(f'GOOGLE IMAGES SEARCH {__version__}', fg='yellow') + click.echo() + ctx.obj = { 'object': FetchResizeSave( - developer_key, custom_search_cx, progress=True + developer_key, custom_search_cx ) } @@ -65,14 +70,17 @@ def search(ctx, query, num, safe, filetype, imagetype, imagesize, 'imgDominantColor': dominantcolor } - click.clear() - try: - ctx.obj['object'].search(search_params, download_path, - width, height, custom_file_name) + gis = ctx.obj['object'] + + with gis: + gis.search(search_params, download_path, + width, height, custom_file_name) - if ctx.obj['object'].results(): - for image in ctx.obj['object'].results(): + results = ctx.obj['object'].results() + + if results: + for image in results: click.echo(image.url) if image.path: click.secho(image.path, fg='blue') @@ -87,4 +95,7 @@ def search(ctx, query, num, safe, filetype, imagetype, imagesize, except GoogleBackendException: click.secho('Error occurred trying to fetch ' 'images from Google. Please try again.', fg='red') + + except googleapiclient.errors.HttpError as e: + click.secho(f'Google reported an error: {str(e)}', fg='red') return diff --git a/google_images_search/fetch_resize_save.py b/google_images_search/fetch_resize_save.py index c2ac500..e04d6be 100644 --- a/google_images_search/fetch_resize_save.py +++ b/google_images_search/fetch_resize_save.py @@ -2,9 +2,10 @@ import curses import requests import threading -from PIL import Image +from PIL import Image, UnidentifiedImageError from resizeimage import resizeimage, imageexceptions +from .meta import __version__ from .google_api import GoogleCustomSearch @@ -15,7 +16,7 @@ class FetchResizeSave(object): """Class with resizing and downloading logic""" def __init__(self, developer_key, custom_search_cx, - progressbar_fn=None, progress=False, validate_images=True): + progressbar_fn=None, validate_images=True): # initialise google api self._google_custom_search = GoogleCustomSearch( @@ -42,12 +43,41 @@ def __init__(self, developer_key, custom_search_cx, if progressbar_fn: # user inserted progressbar fn self._progress = True - else: - if progress: - # initialise internal progressbar - self._progress = True - self._stdscr = curses.initscr() - self._report_progress = self.__report_progress + + def __enter__(self): + """Emtering a terminal window setup + :return: self + """ + + self._report_progress = self.__report_progress + self._progress = True + + # set terminal screen + self._stdscr = curses.initscr() + self._stdscr.keypad(True) + curses.cbreak() + curses.noecho() + + # show terminal header information + self._stdscr.addstr(0, 0, f'GOOGLE IMAGES SEARCH {__version__}') + + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + """Exiting terminal window and putting all back as it was + :param exc_type: + :param exc_val: + :param exc_tb: + :return: + """ + + self._progress = False + + # reverse all as it was + self._stdscr.keypad(False) + curses.nocbreak() + curses.echo() + curses.endwin() def _set_data(self, search_params=None, path_to_dir=False, width=None, height=None, cache_discovery=True): @@ -259,7 +289,10 @@ def increment_naming(dir_list, name, number=0): for chunk in self.get_raw_data(url): f.write(chunk) - Image.open(path_to_image).convert('RGB').save(path_to_image, 'jpeg') + try: + Image.open(path_to_image).convert('RGB').save(path_to_image, 'jpeg') + except UnidentifiedImageError: + pass return path_to_image @@ -294,7 +327,14 @@ def resize(path_to_image, width, height): fd_img = open(path_to_image, 'rb') img = Image.open(fd_img) - img = resizeimage.resize_cover(img, [int(width), int(height)]) + + try: + img = resizeimage.resize_cover(img, [int(width), int(height)]) + except resizeimage.ImageSizeError: + # error resizing an image + # image is probably too small + pass + img.save(path_to_image, img.format) fd_img.close() @@ -306,10 +346,10 @@ def __report_progress(self, url, progress): """ self._stdscr.addstr( - self._terminal_lines[url], 0, "Downloading file: {0}".format(url) + self._terminal_lines[url] + 2, 0, "Downloading file: {0}".format(url) ) self._stdscr.addstr( - self._terminal_lines[url] + 1, 0, + self._terminal_lines[url] + 3, 0, "Progress: [{1:100}] {0}%".format(progress, "#" * progress) ) self._stdscr.refresh() diff --git a/google_images_search/meta.py b/google_images_search/meta.py new file mode 100644 index 0000000..3aca76e --- /dev/null +++ b/google_images_search/meta.py @@ -0,0 +1 @@ +__version__ = '1.3.7' diff --git a/setup.py b/setup.py index 4fea181..5554508 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,4 @@ +import os from setuptools import setup @@ -6,9 +7,15 @@ def readme(): return f.read() +def version(): + with open(os.path.join('.', 'google_images_search', 'meta.py')) as f: + contents = f.read() + return contents.split('__version__ = ')[1].strip()[1:-1] + + setup( name='Google Images Search', - version="1.3.6", + version=version(), description='Search for image using Google Custom Search ' 'API and resize & crop the image afterwords', diff --git a/tests/test_fetch_resize_save.py b/tests/test_fetch_resize_save.py index 5fb3bb0..291ed06 100644 --- a/tests/test_fetch_resize_save.py +++ b/tests/test_fetch_resize_save.py @@ -50,7 +50,7 @@ def test_init(self): self.assertEqual(self._frs._progress, False) frs = FetchResizeSave(self._api_key, self._api_cx, - progressbar_fn=lambda x, y: None, progress=True) + progressbar_fn=lambda x, y: None) self.assertEqual(frs._chunk_sizes, {}) self.assertEqual(frs._terminal_lines, {})