Browse Source

logarithmic progress bars woohoo

Nick Sweeting 5 years ago
parent
commit
b8bbb75f9c
2 changed files with 30 additions and 11 deletions
  1. 1 1
      archivebox/config.py
  2. 29 10
      archivebox/logging_util.py

+ 1 - 1
archivebox/config.py

@@ -48,7 +48,7 @@ CONFIG_DEFAULTS: Dict[str, ConfigDefaultDict] = {
     'SHELL_CONFIG': {
     'SHELL_CONFIG': {
         'IS_TTY':                   {'type': bool,  'default': lambda _: sys.stdout.isatty()},
         'IS_TTY':                   {'type': bool,  'default': lambda _: sys.stdout.isatty()},
         'USE_COLOR':                {'type': bool,  'default': lambda c: c['IS_TTY']},
         'USE_COLOR':                {'type': bool,  'default': lambda c: c['IS_TTY']},
-        'SHOW_PROGRESS':            {'type': bool,  'default': lambda c: False if platform.system() == 'Darwin' else c['IS_TTY']},  # TODO: remove this temporary hack once progress bars are fixed on macOS
+        'SHOW_PROGRESS':            {'type': bool,  'default': lambda c: c['IS_TTY']},
         'IN_DOCKER':                {'type': bool,  'default': False},
         'IN_DOCKER':                {'type': bool,  'default': False},
         # TODO: 'SHOW_HINTS':       {'type:  bool,  'default': True},
         # TODO: 'SHOW_HINTS':       {'type:  bool,  'default': True},
     },
     },

+ 29 - 10
archivebox/logging_util.py

@@ -4,7 +4,9 @@ import re
 import os
 import os
 import sys
 import sys
 import time
 import time
+import platform
 import argparse
 import argparse
+from math import log
 from multiprocessing import Process
 from multiprocessing import Process
 from pathlib import Path
 from pathlib import Path
 
 
@@ -99,10 +101,24 @@ class TimedProgress:
         if self.SHOW_PROGRESS:
         if self.SHOW_PROGRESS:
             # terminate if we havent already terminated
             # terminate if we havent already terminated
             try:
             try:
+                # WARNING: HACKY
+                # I've spent over 15 hours trying to get rid of this stupid macOS-only  
+                # intermittent (but harmeless) warning when the progress bars end sometimes
+                #     Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='utf-8'>
+                #     BrokenPipeError: [Errno 32] Broken pipe
+                # In the end, this is the only thing I found that makes it 
+                # happen slightly less often:
+                if platform.system() == 'Darwin':
+                    time.sleep(0.1)
+
+                # kill the progress bar subprocess
                 self.p.terminate()
                 self.p.terminate()
                 self.p.join()
                 self.p.join()
                 self.p.close()
                 self.p.close()
 
 
+                if platform.system() == 'Darwin':
+                    time.sleep(0.1)
+
                 # clear whole terminal line
                 # clear whole terminal line
                 try:
                 try:
                     sys.stdout.write('\r{}{}\r'.format((' ' * TERM_WIDTH()), ANSI['reset']))
                     sys.stdout.write('\r{}{}\r'.format((' ' * TERM_WIDTH()), ANSI['reset']))
@@ -128,17 +144,18 @@ def progress_bar(seconds: int, prefix: str='') -> None:
                 sys.stdout.write('\r\n')
                 sys.stdout.write('\r\n')
                 sys.stdout.flush()
                 sys.stdout.flush()
             chunks = max_width - len(prefix) - 20
             chunks = max_width - len(prefix) - 20
-            progress = s / chunks / seconds * 100
-            bar_width = round(progress/(100/chunks))
+            pct_complete = s / chunks / seconds * 100
+            log_pct = (log(pct_complete or 1, 10) / 2) * 100  # everyone likes faster progress bars ;)
+            bar_width = round(log_pct/(100/chunks))
             last_width = max_width
             last_width = max_width
 
 
             # ████████████████████           0.9% (1/60sec)
             # ████████████████████           0.9% (1/60sec)
             sys.stdout.write('\r{0}{1}{2}{3} {4}% ({5}/{6}sec)'.format(
             sys.stdout.write('\r{0}{1}{2}{3} {4}% ({5}/{6}sec)'.format(
                 prefix,
                 prefix,
-                ANSI['green'],
+                ANSI['green' if pct_complete < 80 else 'lightyellow'],
                 (chunk * bar_width).ljust(chunks),
                 (chunk * bar_width).ljust(chunks),
                 ANSI['reset'],
                 ANSI['reset'],
-                round(progress, 1),
+                round(pct_complete, 1),
                 round(s/chunks),
                 round(s/chunks),
                 seconds,
                 seconds,
             ))
             ))
@@ -146,7 +163,7 @@ def progress_bar(seconds: int, prefix: str='') -> None:
             time.sleep(1 / chunks)
             time.sleep(1 / chunks)
 
 
         # ██████████████████████████████████ 100.0% (60/60sec)
         # ██████████████████████████████████ 100.0% (60/60sec)
-        sys.stdout.write('\r{0}{1}{2}{3} {4}% ({5}/{6}sec)\n'.format(
+        sys.stdout.write('\r{0}{1}{2}{3} {4}% ({5}/{6}sec)'.format(
             prefix,
             prefix,
             ANSI['red'],
             ANSI['red'],
             chunk * chunks,
             chunk * chunks,
@@ -156,6 +173,10 @@ def progress_bar(seconds: int, prefix: str='') -> None:
             seconds,
             seconds,
         ))
         ))
         sys.stdout.flush()
         sys.stdout.flush()
+        # uncomment to have it disappear when it hits 100% instead of staying full red:
+        # time.sleep(0.5)
+        # sys.stdout.write('\r{}{}\r'.format((' ' * TERM_WIDTH()), ANSI['reset']))
+        # sys.stdout.flush()
     except (KeyboardInterrupt, BrokenPipeError):
     except (KeyboardInterrupt, BrokenPipeError):
         print()
         print()
         pass
         pass
@@ -242,7 +263,7 @@ def log_archiving_started(num_links: int, resume: Optional[float]=None):
              **ANSI,
              **ANSI,
         ))
         ))
     else:
     else:
-        print('{green}[▶] [{}] Collecting content for {} Snapshots in archive...{reset}'.format(
+        print('{green}[▶] [{}] Starting archiving of {} snapshots in index...{reset}'.format(
              start_ts.strftime('%Y-%m-%d %H:%M:%S'),
              start_ts.strftime('%Y-%m-%d %H:%M:%S'),
              num_links,
              num_links,
              **ANSI,
              **ANSI,
@@ -287,10 +308,8 @@ def log_archiving_finished(num_links: int):
     print('    - {} links updated'.format(_LAST_RUN_STATS.succeeded + _LAST_RUN_STATS.failed))
     print('    - {} links updated'.format(_LAST_RUN_STATS.succeeded + _LAST_RUN_STATS.failed))
     print('    - {} links had errors'.format(_LAST_RUN_STATS.failed))
     print('    - {} links had errors'.format(_LAST_RUN_STATS.failed))
     print()
     print()
-    print('    {lightred}Hint:{reset} To view your archive index, run:'.format(**ANSI))
-    print('        archivebox server  # then visit http://127.0.0.1:8000')
-    print('    Or run the built-in webserver:')
-    print('        archivebox server')
+    print('    {lightred}Hint:{reset} To manage your archive in a Web UI, run:'.format(**ANSI))
+    print('        archivebox server 0.0.0.0:8000')
 
 
 
 
 def log_link_archiving_started(link: "Link", link_dir: str, is_new: bool):
 def log_link_archiving_started(link: "Link", link_dir: str, is_new: bool):