diff --git a/setup.py b/setup.py
index 6604fe6..7c44b31 100755
--- a/setup.py
+++ b/setup.py
@@ -9,7 +9,9 @@ PROJECT_NAME = 'friendlypins'
 PROJECT_DEPENDENCIES = [
     'requests',
     'six',
-    'dateutils']
+    'dateutils',
+    'tqdm',
+    'pillow']
 PROJECT_DEV_DEPENDENCIES = [
     'wheel<1.0.0',
     'twine<2.0.0',
diff --git a/src/friendlypins/headers.py b/src/friendlypins/headers.py
index 5df1ea4..b05b9f8 100644
--- a/src/friendlypins/headers.py
+++ b/src/friendlypins/headers.py
@@ -79,5 +79,13 @@ class Headers(object):
         # return time data in current locale for convenience
         return date_with_tz.astimezone(tz.tzlocal())
 
+    @property
+    def bytes(self):
+        """Gets the number of bytes contained in the response data
+
+        :rtype: :class:`int`
+        """
+        return int(self._data['Content-Length'])
+
 if __name__ == "__main__":
     pass
diff --git a/src/friendlypins/utils/console_actions.py b/src/friendlypins/utils/console_actions.py
index 84c63b7..864bfbc 100644
--- a/src/friendlypins/utils/console_actions.py
+++ b/src/friendlypins/utils/console_actions.py
@@ -3,7 +3,9 @@ import logging
 import os
 from six.moves import urllib
 import requests
+from tqdm import tqdm
 from friendlypins.api import API
+from friendlypins.headers import Headers
 
 def _download_pin(pin, folder):
     """Helper method for downloading a thumbnail from a single pin
@@ -15,6 +17,7 @@ def _download_pin(pin, folder):
     :rtype: :class:`int`
     """
     log = logging.getLogger(__name__)
+
     temp_url = urllib.parse.urlparse(pin.thumbnail.url)
     temp_filename = os.path.basename(temp_url.path)
     output_file = os.path.join(folder, temp_filename)
@@ -28,6 +31,9 @@ def _download_pin(pin, folder):
     try:
         response = requests.get(pin.thumbnail.url, stream=True)
         response.raise_for_status()
+        headers = Headers(response.headers)
+        log.debug(headers)
+
         with open(output_file, "wb") as handle:
             for data in response.iter_content():
                 handle.write(data)
@@ -64,16 +70,18 @@ def download_thumbnails(api_token, board_name, output_folder, delete):
         return 1
 
     all_pins = selected_board.all_pins
-    log.info('Downloading %s thumbnails...', len(all_pins))
+    log.info('Downloading thumbnails...')
     if not os.path.exists(output_folder):
         os.makedirs(output_folder)
 
-    for cur_pin in all_pins:
-        retval = _download_pin(cur_pin, output_folder)
-        if retval:
-            return retval
-        if delete:
-            cur_pin.delete()
+    with tqdm(total=selected_board.num_pins, unit='b', ncols=80) as pbar:
+        for cur_pin in all_pins:
+            retval = _download_pin(cur_pin, output_folder)
+            if retval:
+                return retval
+            if delete:
+                cur_pin.delete()
+            pbar.update()
 
     return 0
 

Add progress bar support to fpins console app
To make it easier to track the overall progress of a lengthy download operation, we should add support for showing a progress bar to the fpins console app.
diff --git a/unit_tests/test_console_actions.py b/unit_tests/test_console_actions.py
index d92a3e9..b91cd0d 100644
--- a/unit_tests/test_console_actions.py
+++ b/unit_tests/test_console_actions.py
@@ -28,7 +28,10 @@ def test_download_thumbnails(api_requests, user_requests, board_requests, action
         "data": [{
             "id": "6789",
             "name": expected_board_name,
-            "url": "https://www.pinterest.ca/MyName/MyBoard/"
+            "url": "https://www.pinterest.ca/MyName/MyBoard/",
+            "counts": {
+                "pins": 1
+            }
         }]
     }
 
@@ -110,7 +113,10 @@ def test_download_thumbnails_with_delete(pin_requests, api_requests, user_reques
         "data": [{
             "id": "6789",
             "name": expected_board_name,
-            "url": "https://www.pinterest.ca/MyName/MyBoard/"
+            "url": "https://www.pinterest.ca/MyName/MyBoard/",
+            "counts": {
+                "pins": 1
+            }
         }]
     }
 
@@ -194,7 +200,10 @@ def test_download_thumbnails_error(api_requests, user_requests, board_requests,
         "data": [{
             "id": "6789",
             "name": expected_board_name,
-            "url": "https://www.pinterest.ca/MyName/MyBoard/"
+            "url": "https://www.pinterest.ca/MyName/MyBoard/",
+            "counts": {
+                "pins": 1
+            }
         }]
     }
 
@@ -278,7 +287,10 @@ def test_download_thumbnails_missing_board(api_requests, user_requests, board_re
         "data": [{
             "id": "6789",
             "name": "MyBoard",
-            "url": "https://www.pinterest.ca/MyName/MyBoard/"
+            "url": "https://www.pinterest.ca/MyName/MyBoard/",
+            "counts": {
+                "pins": 1
+            }
         }]
     }
 
@@ -358,7 +370,10 @@ def test_download_thumbnails_exists(api_requests, user_requests, board_requests,
         "data": [{
             "id": "6789",
             "name": expected_board_name,
-            "url": "https://www.pinterest.ca/MyName/MyBoard/"
+            "url": "https://www.pinterest.ca/MyName/MyBoard/",
+            "counts": {
+                "pins": 1
+            }
         }]
     }
 
diff --git a/unit_tests/test_headers.py b/unit_tests/test_headers.py
index e7bf38f..0a5e77e 100644
--- a/unit_tests/test_headers.py
+++ b/unit_tests/test_headers.py
@@ -3,8 +3,9 @@ import mock
 from friendlypins.headers import Headers
 from dateutil import tz
 
-sample_rate_limit = "200"
-sample_rate_max = "150"
+sample_rate_limit = 200
+sample_rate_max = 150
+sample_content_length = 1024
 sample_header = {
       'Access-Control-Allow-Origin': '*',
       'Age': '0',
@@ -14,12 +15,13 @@ sample_header = {
       'Pinterest-Version': 'e3f92ef',
       'X-Content-Type-Options': 'nosniff',
       'X-Pinterest-RID': '12345678',
-      'X-Ratelimit-Limit': sample_rate_limit,
-      'X-Ratelimit-Remaining': sample_rate_max,
+      'X-Ratelimit-Limit': str(sample_rate_limit),
+      'X-Ratelimit-Remaining': str(sample_rate_max),
       'Transfer-Encoding': 'chunked',
       'Date': 'Sat, 31 Mar 2018 10:58:09 GMT',
       'Connection': 'keep-alive',
-      'Pinterest-Generated-By': ''
+      'Pinterest-Generated-By': '',
+      'Content-Length': str(sample_content_length)
     }
 
 
@@ -31,18 +33,22 @@ def test_get_date_locale():
 def test_get_rate_limit():
     obj = Headers(sample_header)
 
-    assert obj.rate_limit == 200
+    assert obj.rate_limit == sample_rate_limit
 
 def test_get_rate_max():
     obj = Headers(sample_header)
 
-    assert obj.rate_remaining == 150
+    assert obj.rate_remaining == sample_rate_max
 
 def test_get_rate_percent():
     obj = Headers(sample_header)
 
     assert obj.percent_rate_remaining == 75
 
+def test_get_num_bytes():
+    obj = Headers(sample_header)
+
+    assert obj.bytes == sample_content_length
 
 if __name__ == "__main__":
     pytest.main([__file__, "-v", "-s"])
\ No newline at end of file

pip install -e .[dev]
parse_log_pytest
pytest
pylint pytest pytest-cov mock
apt-get update apt-get install -y gcc
3.6
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
alabaster==0.7.13
astroid==2.11.7
attrs @ file:///opt/conda/conda-bld/attrs_1642510447205/work
Babel==2.11.0
bleach==4.1.0
cachetools==4.2.4
certifi==2021.5.30
chardet==5.0.0
charset-normalizer==2.0.12
colorama==0.4.5
coverage==6.2
dateutils==0.6.12
dill==0.3.4
distlib==0.3.9
docutils==0.18.1
filelock==3.4.1
-e git+https://github.com/TheFriendlyCoder/friendlypins.git@b52793c458ee2bc4057c22a233d43cc2b1439f8c#egg=friendlypins
idna==3.10
imagesize==1.4.1
importlib-metadata==4.8.3
importlib-resources==5.4.0
iniconfig @ file:///home/linux1/recipes/ci/iniconfig_1610983019677/work
isort==5.10.1
Jinja2==3.0.3
lazy-object-proxy==1.7.1
mando==0.7.1
MarkupSafe==2.0.1
mccabe==0.7.0
mock==5.2.0
more-itertools @ file:///tmp/build/80754af9/more-itertools_1637733554872/work
packaging @ file:///tmp/build/80754af9/packaging_1637314298585/work
pkginfo==1.10.0
platformdirs==2.4.0
pluggy @ file:///tmp/build/80754af9/pluggy_1615976315926/work
py @ file:///opt/conda/conda-bld/py_1644396412707/work
Pygments==2.14.0
pylint==2.13.9
pyparsing @ file:///tmp/build/80754af9/pyparsing_1635766073266/work
pytest==6.2.4
pytest-cov==4.0.0
python-dateutil==2.9.0.post0
pytz==2025.2
radon==6.0.1
readme-renderer==34.0
requests==2.27.1
requests-toolbelt==1.0.0
six==1.17.0
snowballstemmer==2.2.0
Sphinx==5.3.0
sphinxcontrib-applehelp==1.0.2
sphinxcontrib-devhelp==1.0.2
sphinxcontrib-htmlhelp==2.0.0
sphinxcontrib-jsmath==1.0.1
sphinxcontrib-qthelp==1.0.3
sphinxcontrib-serializinghtml==1.1.5
toml @ file:///tmp/build/80754af9/toml_1616166611790/work
tomli==1.2.3
tox==4.0.0a9
tqdm==4.64.1
twine==1.15.0
typed-ast==1.5.5
typing_extensions @ file:///opt/conda/conda-bld/typing_extensions_1647553014482/work
urllib3==1.26.20
virtualenv==20.17.1
webencodings==0.5.1
wrapt==1.16.0
zipp @ file:///tmp/build/80754af9/zipp_1633618647012/work

name: friendlypins
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - attrs=21.4.0=pyhd3eb1b0_0
  - ca-certificates=2025.2.25=h06a4308_0
  - certifi=2021.5.30=py36h06a4308_0
  - importlib_metadata=4.8.1=hd3eb1b0_0
  - iniconfig=1.1.1=pyhd3eb1b0_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - libffi=3.3=he6710b0_2
  - libgcc-ng=11.2.0=h1234567_1
  - libgomp=11.2.0=h1234567_1
  - libstdcxx-ng=11.2.0=h1234567_1
  - more-itertools=8.12.0=pyhd3eb1b0_0
  - ncurses=6.4=h6a678d5_0
  - openssl=1.1.1w=h7f8727e_0
  - packaging=21.3=pyhd3eb1b0_0
  - pip=21.2.2=py36h06a4308_0
  - pluggy=0.13.1=py36h06a4308_0
  - py=1.11.0=pyhd3eb1b0_0
  - pyparsing=3.0.4=pyhd3eb1b0_0
  - pytest=6.2.4=py36h06a4308_2
  - python=3.6.13=h12debd9_1
  - readline=8.2=h5eee18b_0
  - setuptools=58.0.4=py36h06a4308_0
  - sqlite=3.45.3=h5eee18b_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd3eb1b0_0
  - typing_extensions=4.1.1=pyh06a4308_0
  - wheel=0.37.1=pyhd3eb1b0_0
  - xz=5.6.4=h5eee18b_1
  - zipp=3.6.0=pyhd3eb1b0_0
  - zlib=1.2.13=h5eee18b_1
  - pip:
      - alabaster==0.7.13
      - astroid==2.11.7
      - babel==2.11.0
      - bleach==4.1.0
      - cachetools==4.2.4
      - chardet==5.0.0
      - charset-normalizer==2.0.12
      - colorama==0.4.5
      - coverage==6.2
      - dateutils==0.6.12
      - dill==0.3.4
      - distlib==0.3.9
      - docutils==0.18.1
      - filelock==3.4.1
      - idna==3.10
      - imagesize==1.4.1
      - importlib-metadata==4.8.3
      - importlib-resources==5.4.0
      - isort==5.10.1
      - jinja2==3.0.3
      - lazy-object-proxy==1.7.1
      - mando==0.7.1
      - markupsafe==2.0.1
      - mccabe==0.7.0
      - mock==5.2.0
      - pkginfo==1.10.0
      - platformdirs==2.4.0
      - pygments==2.14.0
      - pylint==2.13.9
      - pytest-cov==4.0.0
      - python-dateutil==2.9.0.post0
      - pytz==2025.2
      - radon==6.0.1
      - readme-renderer==34.0
      - requests==2.27.1
      - requests-toolbelt==1.0.0
      - six==1.17.0
      - snowballstemmer==2.2.0
      - sphinx==5.3.0
      - sphinxcontrib-applehelp==1.0.2
      - sphinxcontrib-devhelp==1.0.2
      - sphinxcontrib-htmlhelp==2.0.0
      - sphinxcontrib-jsmath==1.0.1
      - sphinxcontrib-qthelp==1.0.3
      - sphinxcontrib-serializinghtml==1.1.5
      - tomli==1.2.3
      - tox==4.0.0a9
      - tqdm==4.64.1
      - twine==1.15.0
      - typed-ast==1.5.5
      - urllib3==1.26.20
      - virtualenv==20.17.1
      - webencodings==0.5.1
      - wrapt==1.16.0
prefix: /opt/conda/envs/friendlypins

unit_tests/test_headers.py::test_get_num_bytes
unit_tests/test_console_actions.py::test_download_thumbnails unit_tests/test_console_actions.py::test_download_thumbnails_with_delete unit_tests/test_console_actions.py::test_download_thumbnails_error unit_tests/test_console_actions.py::test_download_thumbnails_missing_board unit_tests/test_console_actions.py::test_download_thumbnails_exists unit_tests/test_headers.py::test_get_date_locale unit_tests/test_headers.py::test_get_rate_limit unit_tests/test_headers.py::test_get_rate_max unit_tests/test_headers.py::test_get_rate_percent
setup.py src/friendlypins/utils/console_actions.py src/friendlypins/headers.py
setup.py src/friendlypins/utils/console_actions.py src/friendlypins/headers.py