|
32 | 32 | from difflib import SequenceMatcher |
33 | 33 | from functools import cache |
34 | 34 | from itertools import chain |
35 | | -from typing import Any, Callable, Literal |
| 35 | +from typing import ( |
| 36 | + Any, |
| 37 | + Callable, |
| 38 | + Generator, |
| 39 | + Iterable, |
| 40 | + Literal, |
| 41 | + Sequence, |
| 42 | + TypeVar, |
| 43 | +) |
36 | 44 |
|
37 | 45 | import confuse |
| 46 | +import enlighten |
38 | 47 |
|
39 | 48 | from beets import config, library, logging, plugins, util |
40 | 49 | from beets.dbcore import db |
41 | 50 | from beets.dbcore import query as db_query |
42 | 51 | from beets.util import as_string |
43 | 52 | from beets.util.functemplate import template |
44 | 53 |
|
| 54 | +is_windows = sys.platform == "win32" |
| 55 | + |
45 | 56 | # On Windows platforms, use colorama to support "ANSI" terminal colors. |
46 | | -if sys.platform == "win32": |
| 57 | +if is_windows: |
47 | 58 | try: |
48 | 59 | import colorama |
49 | 60 | except ImportError: |
@@ -1325,6 +1336,60 @@ def add_all_common_options(self): |
1325 | 1336 | self.add_format_option() |
1326 | 1337 |
|
1327 | 1338 |
|
| 1339 | +M = library.Album | library.Item | Any |
| 1340 | +def iprogress_bar(sequence: Sequence[M], **kwargs) -> Generator[M, None, None]: |
| 1341 | + """Construct and manage an `enlighten.Counter` progress bar while iterating. |
| 1342 | +
|
| 1343 | + Example usage: |
| 1344 | + ``` |
| 1345 | + for album in ui.iprogress_bar( |
| 1346 | + lib.albums(), desc="Updating albums", unit="albums"): |
| 1347 | + do_something_to(album) |
| 1348 | + ``` |
| 1349 | +
|
| 1350 | + If the progress bar is iterating over an Album or an Item, then it will detect |
| 1351 | + whether or not the item has been modified, and will color-code the progress bar |
| 1352 | + with white and blue to indicate total progress and the portion of items that have |
| 1353 | + been modified. |
| 1354 | +
|
| 1355 | + Args: |
| 1356 | + sequence: An `Iterable` sequence to iterate over. If provided, and the |
| 1357 | + sequence can return its length, then the length will be used as the |
| 1358 | + total for the counter. The counter will be updated for each item |
| 1359 | + in the sequence. |
| 1360 | + kwargs: Additional keyword arguments to pass to the `enlighten.Counter` |
| 1361 | + constructor. |
| 1362 | +
|
| 1363 | + Yields: |
| 1364 | + The items from the sequence. |
| 1365 | + """ |
| 1366 | + if sequence is None: |
| 1367 | + log.error("sequence must not be None") |
| 1368 | + return |
| 1369 | + |
| 1370 | + # If sequence is not None, and can return its length, then use that as the total. |
| 1371 | + if "total" not in kwargs and hasattr(sequence, "__len__"): |
| 1372 | + kwargs["total"] = len(sequence) |
| 1373 | + |
| 1374 | + # Disabled in windows environments. See above for details |
| 1375 | + with enlighten.Manager(enabled=not is_windows) as manager: |
| 1376 | + with manager.counter(**kwargs) as counter: |
| 1377 | + change_counter = counter.add_subcounter("blue") |
| 1378 | + |
| 1379 | + for item in sequence: |
| 1380 | + revision = None |
| 1381 | + if hasattr(item, '_revision'): |
| 1382 | + revision = item._revision |
| 1383 | + |
| 1384 | + # Yield the item, allowing it to be modified, or not. |
| 1385 | + yield item |
| 1386 | + |
| 1387 | + if revision and item._revision != revision: |
| 1388 | + change_counter.update() |
| 1389 | + else: |
| 1390 | + counter.update() |
| 1391 | + |
| 1392 | + |
1328 | 1393 | # Subcommand parsing infrastructure. |
1329 | 1394 | # |
1330 | 1395 | # This is a fairly generic subcommand parser for optparse. It is |
|
0 commit comments