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.
- {libretrofuzz-2.9.2 → libretrofuzz-2.9.4}/PKG-INFO +6 -4
- {libretrofuzz-2.9.2 → libretrofuzz-2.9.4}/README.rst +3 -2
- libretrofuzz-2.9.4/libretrofuzz/__init__.py +1 -0
- {libretrofuzz-2.9.2 → libretrofuzz-2.9.4}/libretrofuzz/__main__.py +83 -58
- {libretrofuzz-2.9.2 → libretrofuzz-2.9.4}/pyproject.toml +3 -2
- {libretrofuzz-2.9.2 → libretrofuzz-2.9.4}/setup.py +4 -3
- libretrofuzz-2.9.2/libretrofuzz/__init__.py +0 -1
- {libretrofuzz-2.9.2 → libretrofuzz-2.9.4}/LICENSE +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: libretrofuzz
|
|
3
|
-
Version: 2.9.
|
|
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.
|
|
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
|
-
--
|
|
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
|
|
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
|
-
--
|
|
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
|
|
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
|
-
|
|
229
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
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
|
-
|
|
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, '--
|
|
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:
|
|
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, '--
|
|
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:
|
|
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:
|
|
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=
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
if
|
|
703
|
-
|
|
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
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
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
|
|
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[
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 --
|
|
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
|