libretrofuzz 2.9.2__tar.gz → 2.9.4__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: libretrofuzz
3
- Version: 2.9.2
3
+ Version: 2.9.4
4
4
  Summary: Fuzzy Retroarch thumbnail downloader
5
5
  Home-page: https://github.com/i30817/libretrofuzz
6
6
  License: MIT
@@ -13,13 +13,14 @@ Classifier: Programming Language :: Python :: 3.10
13
13
  Classifier: Programming Language :: Python :: 3.8
14
14
  Classifier: Programming Language :: Python :: 3.9
15
15
  Requires-Dist: beautifulsoup4 (>=4.10.0,<5.0.0)
16
+ Requires-Dist: colorama (>=0.4.6,<0.5.0)
16
17
  Requires-Dist: httpx (>=0.23.0,<0.24.0)
17
18
  Requires-Dist: pillow (>=9.2.0,<10.0.0)
18
19
  Requires-Dist: prompt_toolkit (>=3.0.30,<4.0.0)
19
20
  Requires-Dist: questionary (>=1.10.0,<2.0.0)
20
21
  Requires-Dist: rapidfuzz (>=2.4.2,<3.0.0)
21
22
  Requires-Dist: regex (>=2023.6.3,<2024.0.0)
22
- Requires-Dist: tqdm (>=4.64.0,<5.0.0)
23
+ Requires-Dist: tqdm (>=4.65.0,<5.0.0)
23
24
  Requires-Dist: typer[all] (>=0.5.0,<0.6.0)
24
25
  Project-URL: Bug Tracker, https://github.com/i30817/libretrofuzz/issues
25
26
  Project-URL: Repository, https://github.com/i30817/libretrofuzz
@@ -87,7 +88,7 @@ libretro-fuzzall/libretro-fuzz [OPTIONS] [CFG]
87
88
  --delay FLOAT | Seconds to skip thumbnails download, enter continues.
88
89
  | [1<=x<=30]
89
90
  --filter GLOB Restricts downloads to game labels globs - not paths - in the playlist, can be used multiple times and resets thumbnails, ``--filter '*'`` redownloads all.
90
- --score FUZZ | Min fuzz, 0=no-fail, 100=average, 200equal,default. No-op with ``--no-fail``.
91
+ --min SCORE | 0=any, 100=fuzzy match, 200=equal mininames,default. No-op with ``--no-fail``.
91
92
  | [default: 200; 0<=x<=200]
92
93
  --no-fail Download any score. Equivalent to ``--score 0``.
93
94
  --no-image Don't show images even with chafa installed.
@@ -98,7 +99,8 @@ libretro-fuzzall/libretro-fuzz [OPTIONS] [CFG]
98
99
  --before TEXT Use only the part of the label before TEXT to match. TEXT may not be inside of brackets of any kind, may cause false positives but some labels do not have traditional separators. Forces ignoring metadata.
99
100
  --address URL | URL with libretro-thumbnails server. For local files, git clone/unzip packs, run ``'python3 -m http.server'`` in parent dir, and use ``--address 'http://localhost:8000'``.
100
101
  | [default: https://thumbnails.libretro.com]
101
- --verbose Shows the failures, score and normalized local and server names in output.
102
+ --verbose N | Show mininame, len N list (maxscore, server mininame).
103
+ | [x>=1]
102
104
  --install-completion Install completion for the current shell.
103
105
  --show-completion Show completion for the current shell, to copy it or customize the installation.
104
106
  --help Show this message and exit.
@@ -58,7 +58,7 @@ libretro-fuzzall/libretro-fuzz [OPTIONS] [CFG]
58
58
  --delay FLOAT | Seconds to skip thumbnails download, enter continues.
59
59
  | [1<=x<=30]
60
60
  --filter GLOB Restricts downloads to game labels globs - not paths - in the playlist, can be used multiple times and resets thumbnails, ``--filter '*'`` redownloads all.
61
- --score FUZZ | Min fuzz, 0=no-fail, 100=average, 200equal,default. No-op with ``--no-fail``.
61
+ --min SCORE | 0=any, 100=fuzzy match, 200=equal mininames,default. No-op with ``--no-fail``.
62
62
  | [default: 200; 0<=x<=200]
63
63
  --no-fail Download any score. Equivalent to ``--score 0``.
64
64
  --no-image Don't show images even with chafa installed.
@@ -69,7 +69,8 @@ libretro-fuzzall/libretro-fuzz [OPTIONS] [CFG]
69
69
  --before TEXT Use only the part of the label before TEXT to match. TEXT may not be inside of brackets of any kind, may cause false positives but some labels do not have traditional separators. Forces ignoring metadata.
70
70
  --address URL | URL with libretro-thumbnails server. For local files, git clone/unzip packs, run ``'python3 -m http.server'`` in parent dir, and use ``--address 'http://localhost:8000'``.
71
71
  | [default: https://thumbnails.libretro.com]
72
- --verbose Shows the failures, score and normalized local and server names in output.
72
+ --verbose N | Show mininame, len N list (maxscore, server mininame).
73
+ | [x>=1]
73
74
  --install-completion Install completion for the current shell.
74
75
  --show-completion Show completion for the current shell, to copy it or customize the installation.
75
76
  --help Show this message and exit.
@@ -0,0 +1 @@
1
+ __version__ = '2.9.4'
@@ -102,6 +102,13 @@ class StopProgram(Exception):
102
102
  skip = False
103
103
  escape = False
104
104
  enter = False
105
+
106
+ @contextmanager
107
+ def handleContinueDownload():
108
+ try:
109
+ yield
110
+ except ContinueDownload:
111
+ pass
105
112
  def checkDownload():
106
113
  '''threading.get_native_id() in this and other acesses of these variables
107
114
  confirms all accesses are in synchronous functions on one thread so
@@ -169,6 +176,16 @@ async def lock_keys() -> None:
169
176
  yield done
170
177
 
171
178
  #----------------non contextual str manipulation------------------------
179
+ def link(uri, label=None, parameters=''):
180
+ '''
181
+ Found in github, windows console and many unix consoles trick to embeed hyperlinks/uri with text
182
+ '''
183
+ if label is None:
184
+ label = uri
185
+ # OSC 8 ; params ; URI ST <name> OSC 8 ;; ST
186
+ escape_mask = '\033]8;{};{}\033\\{}\033]8;;\033\\'
187
+ return escape_mask.format(parameters, uri, label)
188
+
172
189
  ppatterns = { '()': regex.compile(r'\([^)(]*\)'), '[]': regex.compile(r'\[[^][]*\]') }
173
190
  def removeparenthesis(s, open_p='(', close_p=')'):
174
191
  nb_rep = 1
@@ -225,29 +242,29 @@ def which(executable):
225
242
  #-----------------------------------------------------------------------------------------------------------------------
226
243
  class TitleScorer(object):
227
244
  def __init__(self):
228
- #rapidfuzz says to use range 0-100, but this doesn't (it's much easier that way), so it uses internal api to prevent a possible early exit at == 100
229
- self._RF_ScorerPy = { 'get_scorer_flags': lambda **kwargs: {'optimal_score': MAX_SCORE, 'worst_score': 0, 'flags': (1 << 6)} }
245
+ #rapidfuzz says to use range 0-100, but this doesn't (it's much easier that way)
246
+ #so it uses internal api to prevent a possible early exit at == 100
247
+ self._RF_ScorerPy = { 'get_scorer_flags': lambda **kwargs: {'optimal_score': MAX_SCORE, 'worst_score': 0, 'flags': (1 << 6)} }
230
248
 
231
249
  def __call__(self, s1, s2, processor=None, score_cutoff=None):
232
- prefix = len(os.path.commonprefix([s1, s2]))
233
- if prefix <= 2 and len(s1) != len(s2):
234
- #ideally this branch wouldn't exist, but since many games do not have
235
- #images, they get caught up on a short title 'score' from token_set_ratio
236
- #without the real title to win the similarity+prefix heuristic
237
- #this removes many false positives and causes few false negatives.
238
- return 0
239
- else:
240
- if s1 == s2:
241
- return MAX_SCORE
242
- #score_cutoff needs to be 0 from a combination of 3 factors that create a bug:
243
- #1. the caller of this, extractOne passes the 'current best score' as score_cutoff
244
- #2. the token_set_ratio function returns 0 if the calculated score < score_cutoff
245
- #3. 'current best score' includes the prefix, which this call can't include in 2.
246
- similarity = fuzz.token_set_ratio(s1,s2,processor=None,score_cutoff=0)
247
- #Combine the scorer with a common prefix heuristic to give priority to longer similar
248
- #names, this helps prevents false positives for shorter strings which token set ratio
249
- #is prone because it sets score to 100 if one string words are completely on the other.
250
- return min(MAX_SCORE-1,similarity + prefix)
250
+ if (min(len(s1),len(s2)) / max(len(s1),len(s2))) < 0.3:
251
+ #ideally this branch wouldn't exist, but since many games do not have
252
+ #images, they get caught up on a short title being completely contained in another
253
+ #token_set_ratio gives that 100. Skip if the smaller name length is less than 30%
254
+ return 0
255
+ #names are whitespace and case normalized
256
+ if s1 == s2:
257
+ return MAX_SCORE
258
+ prefix = len(os.path.commonprefix([s1, s2]))
259
+ #score_cutoff needs to be 0 from a combination of 3 factors that create a bug:
260
+ #1. the caller of this, extract passes the 'current best score' as score_cutoff
261
+ #2. the token_set_ratio function returns 0 if the calculated score < score_cutoff
262
+ #3. 'current best score' includes the prefix, which this call can't include in 2.
263
+ similarity = fuzz.token_set_ratio(s1,s2,processor=None,score_cutoff=0)
264
+ #Combine the scorer with a common prefix heuristic to give priority to longer similar
265
+ #names, this helps prevents false positives for shorter strings which token set ratio
266
+ #is prone because it sets score to 100 if one string words are completely on the other.
267
+ return min(MAX_SCORE-1,similarity + prefix)
251
268
 
252
269
  #-----------------------------------------------------------------------------------------------------------------------------
253
270
  # Normalization functions, part of the functions that change both local labels and remote names to be more similar to compare
@@ -311,9 +328,6 @@ def normalizer(t, nometa, hack):
311
328
  #this makes sure that if a remote name has ' and ' instead of ' _ ' to replace ' & ' it works
312
329
  #': ' doesn't need this because ':' is a forbidden character and both '_' and '-' turn to ''
313
330
  t = t.replace(' and ', '')
314
- #although all names have spaces (now), the local names may have weird spaces,
315
- #so to equalize them after the space dependent checks (this also strips)
316
- t = ''.join(t.split())
317
331
  #Tries to make roman numerals in the range 1-20 equivalent to normal numbers (to handle names that change it).
318
332
  #If both sides are roman numerals there is no harm done if XXIV gets turned into 204 in both sides.
319
333
  t = t.replace('xviii', '18')
@@ -338,7 +352,8 @@ def normalizer(t, nometa, hack):
338
352
  t = t.replace('i', '1')
339
353
  #remove diacritics (does nothing to asian languages diacritics, only for 2 to 1 character combinations)
340
354
  t = u''.join([c for c in unicodedata.normalize('NFKD', t) if not unicodedata.combining(c)])
341
- return t
355
+ #normalize spaces (don't remove them for other later score methods to be able to reorder tokens
356
+ return ' '.join(t.split())
342
357
 
343
358
  def nosubtitle_normalizer(t, nometa, hack):
344
359
  return normalizer(nosubtitle_aux(t), nometa, hack)
@@ -447,6 +462,12 @@ def error(error: str):
447
462
 
448
463
  def test_common_errors(cfg: Path, playlist: str, system: str, address: str):
449
464
  '''returns a tuple with (playlist_dir: Path, thumbnail_dir: Path, PLAYLISTS: [Path], SYSTEMS: [str]) '''
465
+ #stop showing the variables - a library installed this behind my back
466
+ try:
467
+ from rich.traceback import install
468
+ install(show_locals=False)
469
+ except ImportError:
470
+ pass
450
471
  global ADDRESS
451
472
  ADDRESS = address.rstrip('/')
452
473
  global viewer
@@ -494,7 +515,7 @@ def mainfuzzsingle(cfg: Path = typer.Argument(CONFIG, help='Path to the retroarc
494
515
  wait_after: Optional[float] = typer.Option(None, '--delay-after', min=1, max=MAX_WAIT_SECS, clamp=True, metavar='FLOAT', help='Seconds after download to skip replacing thumbnails, enter continues. No-op with --no-image.'),
495
516
  wait_before: Optional[float] = typer.Option(None, '--delay', min=1, max=MAX_WAIT_SECS, clamp=True, metavar='FLOAT', help='Seconds to skip thumbnails download, enter continues.'),
496
517
  filters: Optional[List[str]] = typer.Option(None, '--filter', metavar='GLOB', help='Restricts downloads to game labels globs - not paths - in the playlist, can be used multiple times and resets thumbnails, --filter \'*\' redownloads all.'),
497
- score: int = typer.Option(MAX_SCORE, '--score', min=0, max=MAX_SCORE, metavar='FUZZ', help='Min fuzz, 0=no-fail, 100=average, 200≃equal,default. No-op with --no-fail.'),
518
+ score: int = typer.Option(MAX_SCORE, '--min', min=0, max=MAX_SCORE, metavar='SCORE', help=f'0=any, 100=fuzzy match, {MAX_SCORE}=equal mininames,default. No-op with --no-fail.'),
498
519
  nofail: bool = typer.Option(False, '--no-fail', help='Download any score. Equivalent to --score 0.'),
499
520
  noimage: bool = typer.Option(False, '--no-image', help='Don\'t show images even with chafa installed.'),
500
521
  nomerge: bool = typer.Option(False, '--no-merge', help='Disables missing thumbnails download for a label if there is at least one in cache to avoid mixing thumbnails from different server directories on repeated calls. No-op with --filter.'),
@@ -503,7 +524,7 @@ def mainfuzzsingle(cfg: Path = typer.Argument(CONFIG, help='Path to the retroarc
503
524
  hack: bool = typer.Option(False, '--hack', help='Matches [] delimited metadata and may cause false positives, Best used if the hack has thumbnails. Ignored with --before.'),
504
525
  before: Optional[str] = typer.Option(None, help='Use only the part of the label before TEXT to match. TEXT may not be inside of brackets of any kind, may cause false positives but some labels do not have traditional separators. Forces ignoring metadata.'),
505
526
  address: Optional[str] = typer.Option(ADDRESS, metavar='URL', help='URL with libretro-thumbnails server. For local files, git clone/unzip packs, run \'python3 -m http.server\' in parent dir, and use --address \'http://localhost:8000\'.'),
506
- verbose: bool = typer.Option(False, '--verbose', help='Shows the failures, score and normalized local and server names in output.')
527
+ verbose: Optional[int] = typer.Option(None, '--verbose', min=1, metavar='N', help=f'Show mininame, len N list (maxscore, server mininame).')
507
528
  ):
508
529
  if playlist and not playlist.lower().endswith('.lpl'):
509
530
  playlist = playlist + '.lpl'
@@ -550,7 +571,7 @@ def mainfuzzall(cfg: Path = typer.Argument(CONFIG, help='Path to the retroarch c
550
571
  wait_after: Optional[float] = typer.Option(None, '--delay-after', min=1, max=MAX_WAIT_SECS, clamp=True, metavar='FLOAT', help='Seconds after download to skip replacing thumbnails, enter continues. No-op with --no-image.'),
551
572
  wait_before: Optional[float] = typer.Option(None, '--delay', min=1, max=MAX_WAIT_SECS, clamp=True, metavar='FLOAT', help='Seconds to skip thumbnails download, enter continues.'),
552
573
  filters: Optional[List[str]] = typer.Option(None, '--filter', metavar='GLOB', help='Restricts downloads to game labels globs - not paths - in the playlist, can be used multiple times and resets thumbnails, --filter \'*\' redownloads all.'),
553
- score: int = typer.Option(MAX_SCORE, '--score', min=0, max=MAX_SCORE, metavar='FUZZ', help='Min fuzz, 0=no-fail, 100=average, 200≃equal,default. No-op with --no-fail.'),
574
+ score: int = typer.Option(MAX_SCORE, '--min', min=0, max=MAX_SCORE, metavar='SCORE', help=f'0=any, 100=fuzzy match, {MAX_SCORE}=equal mininames,default. No-op with --no-fail.'),
554
575
  nofail: bool = typer.Option(False, '--no-fail', help='Download any score. Equivalent to --score 0.'),
555
576
  noimage: bool = typer.Option(False, '--no-image', help='Don\'t show images even with chafa installed.'),
556
577
  nomerge: bool = typer.Option(False, '--no-merge', help='Disables missing thumbnails download for a label if there is at least one in cache to avoid mixing thumbnails from different server directories on repeated calls. No-op with --filter.'),
@@ -559,7 +580,7 @@ def mainfuzzall(cfg: Path = typer.Argument(CONFIG, help='Path to the retroarch c
559
580
  hack: bool = typer.Option(False, '--hack', help='Matches [] delimited metadata and may cause false positives, Best used if the hack has thumbnails. Ignored with --before.'),
560
581
  before: Optional[str] = typer.Option(None, help='Use only the part of the label before TEXT to match. TEXT may not be inside of brackets of any kind, may cause false positives but some labels do not have traditional separators. Forces ignoring metadata.'),
561
582
  address: Optional[str] = typer.Option(ADDRESS, metavar='URL', help='URL with libretro-thumbnails server. For local files, git clone/unzip packs, run \'python3 -m http.server\' in parent dir, and use --address \'http://localhost:8000\'.'),
562
- verbose: bool = typer.Option(False, '--verbose', help='Shows the failures, score and normalized local and server names in output.')
583
+ verbose: Optional[int] = typer.Option(None, '--verbose', min=1, metavar='N', help=f'Show mininame, len N list (maxscore, server mininame).')
563
584
  ):
564
585
  playlist_dir, thumbnails_dir, PLAYLISTS, SYSTEMS = test_common_errors(cfg, None, None, address)
565
586
 
@@ -623,7 +644,7 @@ async def downloader(names: [(str,str)],
623
644
  wait_after: Optional[float],
624
645
  filters: Optional[List[str]],
625
646
  score: int,
626
- noimage : bool, nomerge: bool, nofail: bool, nometa: bool, hack: bool, nosubtitle: bool, verbose: bool,
647
+ noimage : bool, nomerge: bool, nofail: bool, nometa: bool, hack: bool, nosubtitle: bool, verbose: Optional[int],
627
648
  before: Optional[str],
628
649
  tmpdir: Path,
629
650
  thumbnails_dir: Path,
@@ -632,9 +653,7 @@ async def downloader(names: [(str,str)],
632
653
  #not a error to pass a empty playlist
633
654
  if len(names) == 0:
634
655
  return
635
-
636
656
  thumbs = Thumbs._make( await downloadgamenames(client, system) )
637
-
638
657
  #before implies that the names of the playlists may be cut, so the hack and meta matching must be disabled
639
658
  if before:
640
659
  hack = False
@@ -692,27 +711,34 @@ async def downloader(names: [(str,str)],
692
711
  #operate on cache (to speed up by not applying normalization every iteration)
693
712
  #the normalization can make it so that the winner has the same score as the runner up(s) so to make sure we catch at least
694
713
  #two thumbnails for cases where that happens, we check both best scores if we can't find a thumb in a server directory
695
- result = process.extract(nameaux, remote_names, scorer=title_scorer,processor=None,limit=2,score_cutoff=None)
696
- norm_thumbnail, i_max, thumbnail = (None, 0, None)
697
- thumbnail2 = None
698
- if len(result) == 1:
699
- norm_thumbnail, i_max, thumbnail = result[0]
700
- elif len(result) == 2:
701
- norm_thumbnail, i_max, thumbnail = result[0]
702
- if result[0][1] == result[1][1]: #equal scores
703
- thumbnail2 = result[1][2]
714
+ result = process.extract(nameaux, remote_names, scorer=title_scorer,processor=None,limit=verbose or 1,score_cutoff=None)
715
+ #build the verbose format and decompose the first result, with a optimization if verbose is not on
716
+ thumb_normal, thumb_score, thumb_name = (None, 0, None)
717
+ verbose_format = []
718
+ if verbose:
719
+ for r in reversed(result):
720
+ thumb_normal, thumb_score, thumb_name = r
721
+ color = typer.colors.RED if thumb_score < score else typer.colors.GREEN
722
+ verbose_format.insert(0, f"{typer.style(f'{int(thumb_score)}', fg=f'{color}', bold=True)} {thumb_normal}")
723
+ verbose_format = '|'.join(verbose_format)
724
+ elif len(result) > 0:
725
+ thumb_normal, thumb_score, thumb_name = result[0]
726
+ #hack, if result 1 and 2 have equal scores use the second thumbnail as a alternative if missing a thumb type
727
+ #this is done because it's relatively common for the server to have a case mistake on thumb types
728
+ thumb_name2 = None
729
+ if len(result) >= 2:
730
+ if result[0][1] == result[1][1]:
731
+ thumb_name2 = result[1][2]
704
732
  #formating legos
705
- zeroth_format = ' 0 ' if verbose else ''
706
- prefix_format = '{:>3} '.format(str(int(i_max))) if verbose else ''
707
- name_format = f'{nameaux} -> {norm_thumbnail}' if verbose else f'{name} -> {thumbnail}'
708
- success_format = f'{prefix_format}{typer.style("Success", fg=typer.colors.GREEN, bold=True)}: {name_format}'
709
- failure_format = f'{prefix_format}{typer.style("Failure", fg=typer.colors.RED, bold=True)}: {name_format}'
710
- missing_format = f'{prefix_format}{typer.style("Missing", fg=typer.colors.RED, bold=True)}:'
711
- cancel_format = f'{prefix_format}{typer.style("Skipped", fg=(135,135,135), bold=True)}: {name_format}'
712
- nomerge_format = f'{zeroth_format}{typer.style("Nomerge", fg=(128,128,128), bold=True)}: {name_format}'
713
- getting_format = f'{prefix_format}{typer.style("Getting", fg=typer.colors.BLUE, bold=True)}: {name_format}'
714
- waiting_format = f'{prefix_format}{typer.style("Waiting", fg=typer.colors.YELLOW, bold=True)}: {name_format}' '{bar:-9b} {remaining_s:2.1f}s: {bar:10u}'
715
- if thumbnail and i_max >= score:
733
+ name_format = f'{nameaux} -> {verbose_format}' if verbose else f'{name} -> {thumb_name}'
734
+ success_format = f'{typer.style("Success", fg=typer.colors.GREEN, bold=True)}: {name_format}'
735
+ failure_format = f'{typer.style("Failure", fg=typer.colors.RED, bold=True)}: {name_format}'
736
+ missing_format = f'{typer.style("Missing", fg=typer.colors.RED, bold=True)}: {name_format}'
737
+ cancel_format = f'{typer.style("Skipped", fg=(135,135,135), bold=True)}: {name_format}'
738
+ nomerge_format = f'{typer.style("Nomerge", fg=(128,128,128), bold=True)}: {name_format}'
739
+ getting_format = f'{typer.style("Getting", fg=typer.colors.BLUE, bold=True)}: {name_format}'
740
+ waiting_format = f'{typer.style("Waiting", fg=typer.colors.YELLOW, bold=True)}: {name_format}' '{bar:-9b} {remaining_s:2.1f}s: {bar:10u}'
741
+ if thumb_name and thumb_score >= score:
716
742
  allow = True
717
743
  #these parent directories were created when reading the playlist, more efficient than doing it a playlist game loop
718
744
  real_thumb_dir = Path(thumbnails_dir,destination)
@@ -725,7 +751,7 @@ async def downloader(names: [(str,str)],
725
751
  real = Path(real_thumb_dir, dirname, name + '.png')
726
752
  if not real.exists():
727
753
  missing_thumbs += 1
728
- if thumbnail in getattr(thumbs, dirname) or thumbnail2 in getattr(thumbs, dirname):
754
+ if thumb_name in getattr(thumbs, dirname) or thumb_name2 in getattr(thumbs, dirname):
729
755
  missing_server_thumbs += 1
730
756
  allow = missing_thumbs == 3
731
757
  #despite the above, print only for when it would download if it was allowed, otherwise it is confusing
@@ -742,7 +768,7 @@ async def downloader(names: [(str,str)],
742
768
  downloaded_dict[dirname] = (real, temp)
743
769
  #something to download
744
770
  thumbmap = getattr(thumbs, dirname)
745
- url = thumbmap[thumbnail] if thumbnail in thumbmap else thumbmap.get(thumbnail2, None)
771
+ url = thumbmap[thumb_name] if thumb_name in thumbmap else thumbmap.get(thumb_name2, None)
746
772
  #with filters/reset you always download, and without only if it doesn't exist already.
747
773
  if url and (filters or not real.exists()):
748
774
  download_format = f'{getting_format}' '{bar:-9b}' f'{dirname[6:-1]}' ' {percentage:3.0f}%: {bar:10u}'
@@ -777,12 +803,10 @@ async def downloader(names: [(str,str)],
777
803
 
778
804
  async def printwait(wait : Optional[float], waiting_format: str):
779
805
  count = int(wait/0.1)
780
- try:
806
+ with handleContinueDownload():
781
807
  for i in trange(count, dynamic_ncols=True, bar_format=waiting_format, colour='YELLOW', leave=False):
782
808
  checkDownload()
783
809
  await asyncio.sleep(0.1)
784
- except ContinueDownload as e:
785
- pass
786
810
 
787
811
  async def download(client, url, destination, download_format, missing_format, waiting_format, first_wait, wait_before, max_retries):
788
812
  '''returns True if downloaded. To download, it must have waited, if first_wait is True. Exceptions may happen instead of returning False, but they
@@ -805,8 +829,9 @@ async def download(client, url, destination, download_format, missing_format, wa
805
829
  await printwait(wait_before, waiting_format)
806
830
  with tqdm.wrapattr(f, 'write', total=length, dynamic_ncols=True, bar_format=download_format, colour='BLUE', leave=False) as w:
807
831
  async for chunk in r.aiter_raw(4096):
832
+ with handleContinueDownload():
808
833
  checkDownload()
809
- w.write(chunk)
834
+ w.write(chunk)
810
835
  return True
811
836
  except (RequestError,HTTPStatusError) as e:
812
837
  if max_retries <= 0:
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "libretrofuzz"
3
- version = "2.9.2"
3
+ version = "2.9.4"
4
4
  description = "Fuzzy Retroarch thumbnail downloader"
5
5
  authors = ["i30817 <i30817@gmail.com>"]
6
6
  license = "MIT"
@@ -14,10 +14,11 @@ questionary = "^1.10.0"
14
14
  typer = {extras = ["all"], version = "^0.5.0"}
15
15
  rapidfuzz = "^2.4.2"
16
16
  httpx = "^0.23.0"
17
- tqdm = "^4.64.0"
17
+ tqdm = "^4.65.0"
18
18
  prompt_toolkit = "^3.0.30"
19
19
  pillow = "^9.2.0"
20
20
  regex = "^2023.6.3"
21
+ colorama = "^0.4.6"
21
22
 
22
23
 
23
24
  [tool.poetry.scripts]
@@ -9,13 +9,14 @@ package_data = \
9
9
 
10
10
  install_requires = \
11
11
  ['beautifulsoup4>=4.10.0,<5.0.0',
12
+ 'colorama>=0.4.6,<0.5.0',
12
13
  'httpx>=0.23.0,<0.24.0',
13
14
  'pillow>=9.2.0,<10.0.0',
14
15
  'prompt_toolkit>=3.0.30,<4.0.0',
15
16
  'questionary>=1.10.0,<2.0.0',
16
17
  'rapidfuzz>=2.4.2,<3.0.0',
17
18
  'regex>=2023.6.3,<2024.0.0',
18
- 'tqdm>=4.64.0,<5.0.0',
19
+ 'tqdm>=4.65.0,<5.0.0',
19
20
  'typer[all]>=0.5.0,<0.6.0']
20
21
 
21
22
  entry_points = \
@@ -24,9 +25,9 @@ entry_points = \
24
25
 
25
26
  setup_kwargs = {
26
27
  'name': 'libretrofuzz',
27
- 'version': '2.9.2',
28
+ 'version': '2.9.4',
28
29
  'description': 'Fuzzy Retroarch thumbnail downloader',
29
- 'long_description': "**Fuzzy Retroarch thumbnail downloader**\n========================================\n\nIn Retroarch, when you use the manual scanner to get nonstandard games or hacks in playlists, thumbnails often fail to download.\n\nThese programs, for each game label on a playlist, download the most similar named image to display in retroarch.\n\nThere are several options to fit unusual labels and increase fuzziness, but you can just run them to get the most restrictive default (with the least fuzz).\n\nIf you use ``libretro-fuzz``, it will download for a single playlist by asking for the playlist and system if they're not provided.\nIf you use ``libretro-fuzzall``, it will download for all playlists with standard libretro names, and will skip custom playlists.\n\nBesides those differences, if no retroarch.cfg is provided, both programs try to use the default retroarch.cfg.\n\nIf `chafa <https://github.com/hpjansson/chafa>`_ is installed, the program will display new thumbnails of a game, with gray border for images already in use and with green border for new images. Chafa works better with a recent release and on a `sixel <https://en.wikipedia.org/wiki/Sixel>`_ or `kitty <https://sw.kovidgoyal.net/kitty/graphics-protocol/>`_ compatible shell.\n\nExample:\n | ``libretro-fuzz --system 'Commodore - Amiga' --before '_'``\n | then\n | ``libretro-fuzz --system 'Commodore - Amiga' --no-subtitle --before '_'``\n \n The Retroplay WHDLoad set has labels like ``MonkeyIsland2_v1.3_0020`` after a manual scan. These labels *often* don't have subtitles (but not always) and all the metadata is not separated from the name by brackets. Select the playlist that contains those whdloads to download from the libretro amiga thumbnails.\n\nNote that the system name you download from doesn't have to be the same as the playlist name.\n\nIf the thumbnail server contains games from multiple releases for the system (like ``ScummVM``), be careful using extra options since it is easy to end up with 'slightly wrong' covers.\n\nExample:\n ``libretro-fuzz --no-meta --no-merge``\n \n After downloading ``ScummVM`` thumbnails (and not before, to minimize false positives), we'd like to try to pickup a few covers from ``DOS`` thumbnails and skip download if there a risk of mixing thumbnails from ``DOS`` and ``ScummVM`` for a single game.\n Choose the ScummVM playlist and DOS system name, and covers would be downloaded with risk of false positives: CD vs floppy covers, USA vs Japan covers, or another platform vs DOS.\n\nBecause of this increased risk of false positives with options, the default is to count everything except hack metadata as part of the match, only select the names that match exactly (after 'safe' heuristics like removing spaces) and the default pre-selected system name to be the same as the playlist name, which is safest.\n\nFalse positives will then be from using ``--score`` (allows inexact matches) or/and one of the commands that do not count metadata (``--before``, ``--no-meta`` and ``--no-subtitle``). A common scenario is the thumbnail server not having a single thumbnail of the game, and the program selecting the best match it can which is still good enough to pass, like a sequel, prequel, or different release, most often regions/languages. It's not recommended to use ``--score`` less than 100 without ``--filter`` to a specific game.\n\nExample:\n ``libretro-fuzz --system 'Commodore - Amiga' --no-subtitle --before '_' --filter '[Ii]shar*'``\n \n The best way to solve these issues is to upload the right cover to the respective libretro-thumbnail subproject with the correct name of the game variant. Then you can redownload just the updated thumbnails with a label, in this example, because of ``--filter``, the Ishar series in the WHDLoad playlist would redownload.\n\nlibretro-fuzzall/libretro-fuzz [OPTIONS] [CFG]\n :CFG: Path to the retroarch cfg file. If not default, asked from the user.\n \n Linux default: ``~/.config/retroarch/retroarch.cfg``\n \n Windows default: ``%APPDATA%/RetroArch/retroarch.cfg``\n \n MacOS default: ``~/Library/Application Support/RetroArch/config/retroarch.cfg``\n \n --playlist <NAME libretro-fuzz only>\n Playlist name with labels used for thumbnail fuzzy matching. If not provided, asked from the user.\n --system <NAME libretro-fuzz only>\n Directory name in the server to download thumbnails. If not provided, asked from the user.\n --delay-after FLOAT | Seconds after download to skip replacing thumbnails, enter continues. No-op with ``--no-image``.\n | [1<=x<=30]\n --delay FLOAT | Seconds to skip thumbnails download, enter continues.\n | [1<=x<=30]\n --filter GLOB Restricts downloads to game labels globs - not paths - in the playlist, can be used multiple times and resets thumbnails, ``--filter '*'`` redownloads all.\n --score FUZZ | Min fuzz, 0=no-fail, 100=average, 200equal,default. No-op with ``--no-fail``.\n | [default: 200; 0<=x<=200]\n --no-fail Download any score. Equivalent to ``--score 0``.\n --no-image Don't show images even with chafa installed.\n --no-merge Disables missing thumbnails download for a label if there is at least one in cache to avoid mixing thumbnails from different server directories on repeated calls. No-op with ``--filter``.\n --no-subtitle Ignores text after last ``' - '`` or ``': '``. ``':'`` can't occur in server names, so if the server has ``'Name_subtitle.png'`` and not ``'Name - subtitle.png'`` (uncommon), this option doesn't help.\n --no-meta Ignores () delimited metadata and may cause false positives. Forced with ``--before``.\n --hack Matches [] delimited metadata and may cause false positives, Best used if the hack has thumbnails. Ignored with ``--before``.\n --before TEXT Use only the part of the label before TEXT to match. TEXT may not be inside of brackets of any kind, may cause false positives but some labels do not have traditional separators. Forces ignoring metadata.\n --address URL | URL with libretro-thumbnails server. For local files, git clone/unzip packs, run ``'python3 -m http.server'`` in parent dir, and use ``--address 'http://localhost:8000'``.\n | [default: https://thumbnails.libretro.com]\n --verbose Shows the failures, score and normalized local and server names in output.\n --install-completion Install completion for the current shell.\n --show-completion Show completion for the current shell, to copy it or customize the installation.\n --help Show this message and exit.\n\n\n\nTo install the program, type on the cmd line\n\n+----------------+---------------------------------------------------------------------------------------------+\n| Latest release | ``pip install --force-reinstall libretrofuzz`` |\n+----------------+---------------------------------------------------------------------------------------------+\n| Current code | ``pip install --force-reinstall https://github.com/i30817/libretrofuzz/archive/master.zip`` |\n+----------------+---------------------------------------------------------------------------------------------+\n\nIn windows, you'll want to check the option to “Add Python to PATH” when installing python, to be able to install and execute the script from any path of the cmd line.\n",
30
+ 'long_description': "**Fuzzy Retroarch thumbnail downloader**\n========================================\n\nIn Retroarch, when you use the manual scanner to get nonstandard games or hacks in playlists, thumbnails often fail to download.\n\nThese programs, for each game label on a playlist, download the most similar named image to display in retroarch.\n\nThere are several options to fit unusual labels and increase fuzziness, but you can just run them to get the most restrictive default (with the least fuzz).\n\nIf you use ``libretro-fuzz``, it will download for a single playlist by asking for the playlist and system if they're not provided.\nIf you use ``libretro-fuzzall``, it will download for all playlists with standard libretro names, and will skip custom playlists.\n\nBesides those differences, if no retroarch.cfg is provided, both programs try to use the default retroarch.cfg.\n\nIf `chafa <https://github.com/hpjansson/chafa>`_ is installed, the program will display new thumbnails of a game, with gray border for images already in use and with green border for new images. Chafa works better with a recent release and on a `sixel <https://en.wikipedia.org/wiki/Sixel>`_ or `kitty <https://sw.kovidgoyal.net/kitty/graphics-protocol/>`_ compatible shell.\n\nExample:\n | ``libretro-fuzz --system 'Commodore - Amiga' --before '_'``\n | then\n | ``libretro-fuzz --system 'Commodore - Amiga' --no-subtitle --before '_'``\n \n The Retroplay WHDLoad set has labels like ``MonkeyIsland2_v1.3_0020`` after a manual scan. These labels *often* don't have subtitles (but not always) and all the metadata is not separated from the name by brackets. Select the playlist that contains those whdloads to download from the libretro amiga thumbnails.\n\nNote that the system name you download from doesn't have to be the same as the playlist name.\n\nIf the thumbnail server contains games from multiple releases for the system (like ``ScummVM``), be careful using extra options since it is easy to end up with 'slightly wrong' covers.\n\nExample:\n ``libretro-fuzz --no-meta --no-merge``\n \n After downloading ``ScummVM`` thumbnails (and not before, to minimize false positives), we'd like to try to pickup a few covers from ``DOS`` thumbnails and skip download if there a risk of mixing thumbnails from ``DOS`` and ``ScummVM`` for a single game.\n Choose the ScummVM playlist and DOS system name, and covers would be downloaded with risk of false positives: CD vs floppy covers, USA vs Japan covers, or another platform vs DOS.\n\nBecause of this increased risk of false positives with options, the default is to count everything except hack metadata as part of the match, only select the names that match exactly (after 'safe' heuristics like removing spaces) and the default pre-selected system name to be the same as the playlist name, which is safest.\n\nFalse positives will then be from using ``--score`` (allows inexact matches) or/and one of the commands that do not count metadata (``--before``, ``--no-meta`` and ``--no-subtitle``). A common scenario is the thumbnail server not having a single thumbnail of the game, and the program selecting the best match it can which is still good enough to pass, like a sequel, prequel, or different release, most often regions/languages. It's not recommended to use ``--score`` less than 100 without ``--filter`` to a specific game.\n\nExample:\n ``libretro-fuzz --system 'Commodore - Amiga' --no-subtitle --before '_' --filter '[Ii]shar*'``\n \n The best way to solve these issues is to upload the right cover to the respective libretro-thumbnail subproject with the correct name of the game variant. Then you can redownload just the updated thumbnails with a label, in this example, because of ``--filter``, the Ishar series in the WHDLoad playlist would redownload.\n\nlibretro-fuzzall/libretro-fuzz [OPTIONS] [CFG]\n :CFG: Path to the retroarch cfg file. If not default, asked from the user.\n \n Linux default: ``~/.config/retroarch/retroarch.cfg``\n \n Windows default: ``%APPDATA%/RetroArch/retroarch.cfg``\n \n MacOS default: ``~/Library/Application Support/RetroArch/config/retroarch.cfg``\n \n --playlist <NAME libretro-fuzz only>\n Playlist name with labels used for thumbnail fuzzy matching. If not provided, asked from the user.\n --system <NAME libretro-fuzz only>\n Directory name in the server to download thumbnails. If not provided, asked from the user.\n --delay-after FLOAT | Seconds after download to skip replacing thumbnails, enter continues. No-op with ``--no-image``.\n | [1<=x<=30]\n --delay FLOAT | Seconds to skip thumbnails download, enter continues.\n | [1<=x<=30]\n --filter GLOB Restricts downloads to game labels globs - not paths - in the playlist, can be used multiple times and resets thumbnails, ``--filter '*'`` redownloads all.\n --min SCORE | 0=any, 100=fuzzy match, 200=equal mininames,default. No-op with ``--no-fail``.\n | [default: 200; 0<=x<=200]\n --no-fail Download any score. Equivalent to ``--score 0``.\n --no-image Don't show images even with chafa installed.\n --no-merge Disables missing thumbnails download for a label if there is at least one in cache to avoid mixing thumbnails from different server directories on repeated calls. No-op with ``--filter``.\n --no-subtitle Ignores text after last ``' - '`` or ``': '``. ``':'`` can't occur in server names, so if the server has ``'Name_subtitle.png'`` and not ``'Name - subtitle.png'`` (uncommon), this option doesn't help.\n --no-meta Ignores () delimited metadata and may cause false positives. Forced with ``--before``.\n --hack Matches [] delimited metadata and may cause false positives, Best used if the hack has thumbnails. Ignored with ``--before``.\n --before TEXT Use only the part of the label before TEXT to match. TEXT may not be inside of brackets of any kind, may cause false positives but some labels do not have traditional separators. Forces ignoring metadata.\n --address URL | URL with libretro-thumbnails server. For local files, git clone/unzip packs, run ``'python3 -m http.server'`` in parent dir, and use ``--address 'http://localhost:8000'``.\n | [default: https://thumbnails.libretro.com]\n --verbose N | Show mininame, len N list (maxscore, server mininame).\n | [x>=1]\n --install-completion Install completion for the current shell.\n --show-completion Show completion for the current shell, to copy it or customize the installation.\n --help Show this message and exit.\n\n\n\nTo install the program, type on the cmd line\n\n+----------------+---------------------------------------------------------------------------------------------+\n| Latest release | ``pip install --force-reinstall libretrofuzz`` |\n+----------------+---------------------------------------------------------------------------------------------+\n| Current code | ``pip install --force-reinstall https://github.com/i30817/libretrofuzz/archive/master.zip`` |\n+----------------+---------------------------------------------------------------------------------------------+\n\nIn windows, you'll want to check the option to “Add Python to PATH” when installing python, to be able to install and execute the script from any path of the cmd line.\n",
30
31
  'author': 'i30817',
31
32
  'author_email': 'i30817@gmail.com',
32
33
  'maintainer': None,
@@ -1 +0,0 @@
1
- __version__ = '2.9.2'
File without changes