From 621b4ed547ebddcc9b458caaec4f65699b4c98ae Mon Sep 17 00:00:00 2001 From: i30817 Date: Sun, 4 Jun 2023 16:56:02 +0100 Subject: [PATCH] add new cmd line argument to switch download location, can work with local files with some preperation change --filter to also delete failed matches so --filter '*' resets everything Update version --- README.rst | 73 +++++++++++++++++----------------------- libretrofuzz/__init__.py | 2 +- libretrofuzz/__main__.py | 39 ++++++++++++--------- poetry.lock | 36 ++++++++++++++------ pyproject.toml | 2 +- 5 files changed, 82 insertions(+), 70 deletions(-) diff --git a/README.rst b/README.rst index 48f2ae2..5e0c78d 100644 --- a/README.rst +++ b/README.rst @@ -1,23 +1,25 @@ **Fuzzy Retroarch thumbnail downloader** ======================================== -In Retroarch, when you use the manual scanner to get non-standard games or hacks in playlists, thumbnails often fail to download. +In Retroarch, when you use the manual scanner to get nonstandard games or hacks in playlists, thumbnails often fail to download. These programs, for each game label on a playlist, download the most similar named image to display in retroarch. There 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). If you use ``libretro-fuzz``, it will download for a single playlist by asking for the playlist and system if they're not provided. -If you use ``libretro-fuzzall``, it will dowload for all playlists with standard libretro names, and will skip custom playlists. +If you use ``libretro-fuzzall``, it will download for all playlists with standard libretro names, and will skip custom playlists. Besides those differences, if no retroarch.cfg is provided, both programs try to use the default retroarch.cfg. -If `chafa `_ is installed, the program will display new thumbnails of a game, with grey border for images already in use and with green border for new images. Chafa works better with a recent release and on a `sixel `_ or `kitty `_ compatible shell. +If `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 `_ or `kitty `_ compatible shell. Example: - ``libretro-fuzz --no-subtitle --before '_'`` + | ``libretro-fuzz --system 'Commodore - Amiga' --before '_'`` + | then + | ``libretro-fuzz --system 'Commodore - Amiga' --no-subtitle --before '_'`` - The Retroplay WHDLoad set has labels like ``MonkeyIsland2_v1.3_0020`` after a manual scan. These labels don't have subtitles and all the metadata is not separated from the name by brackets. Select the playlist that contains those whdloads and the system name ``Commodore - Amiga`` to download from the libretro amiga thumbnails. + 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. Note that the system name you download from doesn't have to be the same as the playlist name. @@ -29,14 +31,14 @@ Example: 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. 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. -Because of this increased risk of false positives with options, the default is to count everything except hack metadata as part of the match, and the default pre-selected system name to be the same as the playlist name, which is safest. +Because 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. -False positives will then mostly be from 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 the similarity test. Common false positives from this are sequels or prequels, or different releases, most often regions/languages. +False 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. Example: - ``libretro-fuzz --no-subtitle --before '_' --filter '[Ii]shar*'`` + ``libretro-fuzz --system 'Commodore - Amiga' --no-subtitle --before '_' --filter '[Ii]shar*'`` - 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, the Ishar series in the WHDLoad playlist. + 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. libretro-fuzzall/libretro-fuzz [OPTIONS] [CFG] :CFG: Path to the retroarch cfg file. If not default, asked from the user. @@ -48,43 +50,28 @@ libretro-fuzzall/libretro-fuzz [OPTIONS] [CFG] MacOS default: ``~/Library/Application Support/RetroArch/config/retroarch.cfg`` --playlist - Playlist name with labels used for thumbnail fuzzy - matching. If not provided, asked from the user. + Playlist name with labels used for thumbnail fuzzy matching. If not provided, asked from the user. --system - Directory name in the server to download thumbnails. - If not provided, asked from the user. - --delay-after FLOAT Seconds after download to skip replacing thumbnails. - No-op with --no-image. [1<=x<=10] - --delay FLOAT Seconds to skip thumbnails download. [1<=x<=10] - --filter GLOB Restricts downloads to game labels globs - not paths - - in the playlist, can be used multiple times and - matches reset thumbnails, --filter '\*' downloads all. - --score FUZZ Download fuzz (less is more fuzz, 100 being average). - No-op with --no-fail. [default: 200; 0<=x<=200] - --no-fail Download any score. Equivalent to --score 0. + Directory name in the server to download thumbnails. If not provided, asked from the user. + --delay-after FLOAT | Seconds after download to skip replacing thumbnails. No-op with ``--no-image``. + | [1<=x<=10] + --delay FLOAT | Seconds to skip thumbnails download. + | [1<=x<=10] + --filter GLOB Restricts downloads to game labels globs - not paths - in the playlist, can be used multiple times and resets thumbnails, ``--filter '*'`` redownloads all. + --score FUZZ | Min fuzz, 0=no-fail, 100=average, 200≃equal,default. No-op with ``--no-fail``. + | [default: 200; 0<=x<=200] + --no-fail Download any score. Equivalent to ``--score 0``. --no-image Don't show images even with chafa installed. - --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. - --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. - --no-meta Ignores () delimited metadata and may cause false - positives. Forced with --before. - --hack Matches [] delimited metadata and may cause false - positives, Best used if the hack has thumbnails. - Ignored with --before. - --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. - --verbose Shows the failures, score and normalized local and - server names in output. + --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``. + --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. + --no-meta Ignores () delimited metadata and may cause false positives. Forced with ``--before``. + --hack Matches [] delimited metadata and may cause false positives, Best used if the hack has thumbnails. Ignored with ``--before``. + --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. + --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'``. + | [default: https://thumbnails.libretro.com] + --verbose Shows the failures, score and normalized local and server names in output. --install-completion Install completion for the current shell. - --show-completion Show completion for the current shell, to copy it or - customize the installation. + --show-completion Show completion for the current shell, to copy it or customize the installation. --help Show this message and exit. diff --git a/libretrofuzz/__init__.py b/libretrofuzz/__init__.py index 7039ccb..387cfac 100644 --- a/libretrofuzz/__init__.py +++ b/libretrofuzz/__init__.py @@ -1 +1 @@ -__version__ = '2.8.6' +__version__ = '2.9.0' diff --git a/libretrofuzz/__main__.py b/libretrofuzz/__main__.py index 1041e05..480b936 100644 --- a/libretrofuzz/__main__.py +++ b/libretrofuzz/__main__.py @@ -45,7 +45,7 @@ ########### SCRIPT SETTINGS ############### ########################################### - +ADDRESS='https://thumbnails.libretro.com' MAX_SCORE = 200 MAX_RETRIES = 3 #00-1f are ascii control codes, rest is 'normal' illegal windows filename chars according to powershell + & @@ -427,8 +427,10 @@ def getPath(cfg: Path, setting: str, default_value: str): def error(error: str): typer.echo(typer.style(error, fg=typer.colors.RED, bold=True)) -def test_common_errors(cfg: Path, playlist: str, system: str): +def test_common_errors(cfg: Path, playlist: str, system: str, address: str): '''returns a tuple with (playlist_dir: Path, thumbnail_dir: Path, PLAYLISTS: [Path], SYSTEMS: [str]) ''' + global ADDRESS + ADDRESS = address.rstrip('/') global viewer viewer = which('chafa') if not viewer: @@ -454,11 +456,11 @@ def test_common_errors(cfg: Path, playlist: str, system: str): try: with Client() as client: - page = client.get('https://thumbnails.libretro.com/', timeout=15) + page = client.get(ADDRESS, timeout=15) soup = BeautifulSoup(page.text, 'html.parser') SYSTEMS = [ unquote(node.get('href')[:-1]) for node in soup.find_all('a') if node.get('href').endswith('/') and not node.get('href').endswith('../') ] except (RequestError,HTTPStatusError) as err: - error(f'Could not get the remote thumbnail system names, exiting') + error(f'Could not get the remote thumbnail system names, exiting: {err}') raise typer.Exit(code=1) if system and system not in SYSTEMS: error(f'The user provided system name {system} does not match any remote thumbnail system names') @@ -474,8 +476,8 @@ def mainfuzzsingle(cfg: Path = typer.Argument(CONFIG, help='Path to the retroarc system: str = typer.Option(None, metavar='NAME', help='Directory name in the server to download thumbnails. If not provided, asked from the user.'), wait_after: Optional[float] = typer.Option(None, '--delay-after', min=1, max=10, clamp=True, metavar='FLOAT', help='Seconds after download to skip replacing thumbnails. No-op with --no-image.'), wait_before: Optional[float] = typer.Option(None, '--delay', min=1, max=10, clamp=True, metavar='FLOAT', help='Seconds to skip thumbnails download.'), - 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 matches reset thumbnails, --filter \'*\' downloads all.'), - score: int = typer.Option(MAX_SCORE, '--score', min=0, max=MAX_SCORE, metavar='FUZZ', help='Download fuzz (less is more fuzz, 100 being average). No-op with --no-fail.'), + 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.'), + 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.'), nofail: bool = typer.Option(False, '--no-fail', help='Download any score. Equivalent to --score 0.'), noimage: bool = typer.Option(False, '--no-image', help='Don\'t show images even with chafa installed.'), 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.'), @@ -483,12 +485,13 @@ def mainfuzzsingle(cfg: Path = typer.Argument(CONFIG, help='Path to the retroarc nometa: bool = typer.Option(False, '--no-meta', help='Ignores () delimited metadata and may cause false positives. Forced with --before.'), 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.'), 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.'), + 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\'.'), verbose: bool = typer.Option(False, '--verbose', help='Shows the failures, score and normalized local and server names in output.') ): if playlist and not playlist.lower().endswith('.lpl'): playlist = playlist + '.lpl' - playlist_dir, thumbnails_dir, PLAYLISTS, SYSTEMS = test_common_errors(cfg, playlist, system) + playlist_dir, thumbnails_dir, PLAYLISTS, SYSTEMS = test_common_errors(cfg, playlist, system, address) custom_style = Style([ ('answer', 'fg:green bold'), @@ -529,8 +532,8 @@ async def runit(): def mainfuzzall(cfg: Path = typer.Argument(CONFIG, help='Path to the retroarch cfg file. If not default, asked from the user.'), wait_after: Optional[float] = typer.Option(None, '--delay-after', min=1, max=10, clamp=True, metavar='FLOAT', help='Seconds after download to skip replacing thumbnails. No-op with --no-image.'), wait_before: Optional[float] = typer.Option(None, '--delay', min=1, max=10, clamp=True, metavar='FLOAT', help='Seconds to skip thumbnails download.'), - 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 matches reset thumbnails, --filter \'*\' downloads all.'), - score: int = typer.Option(MAX_SCORE, '--score', min=0, max=MAX_SCORE, metavar='FUZZ', help='Download fuzz (less is more fuzz, 100 being average). No-op with --no-fail.'), + 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.'), + 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.'), nofail: bool = typer.Option(False, '--no-fail', help='Download any score. Equivalent to --score 0.'), noimage: bool = typer.Option(False, '--no-image', help='Don\'t show images even with chafa installed.'), 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.'), @@ -538,9 +541,10 @@ def mainfuzzall(cfg: Path = typer.Argument(CONFIG, help='Path to the retroarch c nometa: bool = typer.Option(False, '--no-meta', help='Ignores () delimited metadata and may cause false positives. Forced with --before.'), 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.'), 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.'), + 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\'.'), verbose: bool = typer.Option(False, '--verbose', help='Shows the failures, score and normalized local and server names in output.') ): - playlist_dir, thumbnails_dir, PLAYLISTS, SYSTEMS = test_common_errors(cfg, None, None) + playlist_dir, thumbnails_dir, PLAYLISTS, SYSTEMS = test_common_errors(cfg, None, None, address) notInSystems = [ (playlist, os.path.basename(playlist)[:-4]) for playlist in PLAYLISTS if os.path.basename(playlist)[:-4] not in SYSTEMS] for playlist, system in notInSystems: @@ -570,7 +574,7 @@ async def downloadgamenames(client, system): for each of the server directories '/Named_Boxarts/', '/Named_Titles/', '/Named_Snaps/' (potentially some of these dicts may be empty if the server doesn't have the directory) """ - lr_thumbs = 'https://thumbnails.libretro.com/'+quote(system) #then get the thumbnails from the system name + lr_thumbs = ADDRESS+'/'+quote(system) #then get the thumbnails from the system name args = [] try: for tdir in ['/Named_Boxarts/', '/Named_Titles/', '/Named_Snaps/']: @@ -592,7 +596,7 @@ async def downloadgamenames(client, system): l1 = { unquote(Path(node.get('href')).name[:-4]) : lr_thumb+node.get('href') for node in soup.find_all('a') if node.get('href').endswith('.png')} args.append(l1) except (RequestError,HTTPStatusError) as err: - error(f'Could not get the remote thumbnail game names, exiting') + error(f'Could not get the remote thumbnail game names, exiting: {err}') raise typer.Exit(code=1) return args @@ -692,10 +696,10 @@ async def downloader(names: [(str,str)], getting_format = f'{prefix_format}{typer.style("Getting", fg=typer.colors.BLUE, bold=True)}: {name_format}' 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}' if thumbnail and i_max >= score: + allow = True #these parent directories were created when reading the playlist, more efficient than doing it a playlist game loop real_thumb_dir = Path(thumbnails_dir,destination) down_thumb_dir = Path(tmpdir,destination) - allow = True if not filters and nomerge: #to implement no-merge you have to disable downloads on 'at least one' thumbnail (including user added ones) missing_thumbs = 0 @@ -746,8 +750,13 @@ async def downloader(names: [(str,str)], if new.exists(): shutil.move(new, old) typer.echo(success_format) - elif verbose: - typer.echo(failure_format) + else: + if verbose: + typer.echo(failure_format) + if filters: + #nothing to download but we want to remove images that may be there in the case of --filter. + for dirname in Thumbs._fields: + Path(thumbnails_dir,destination, dirname, name + '.png').unlink(missing_ok=True) async def printwait(wait : Optional[float], waiting_format: str): count = int(wait/0.1) diff --git a/poetry.lock b/poetry.lock index 6e0a5b6..fedc628 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,19 +1,20 @@ [[package]] name = "anyio" -version = "3.6.2" +version = "3.7.0" description = "High level compatibility layer for multiple asynchronous event loop implementations" category = "main" optional = false -python-versions = ">=3.6.2" +python-versions = ">=3.7" [package.dependencies] +exceptiongroup = {version = "*", markers = "python_version < \"3.11\""} idna = ">=2.8" sniffio = ">=1.1" [package.extras] -doc = ["packaging", "sphinx-rtd-theme", "sphinx-autodoc-typehints (>=1.2.0)"] -test = ["coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "contextlib2", "uvloop (<0.15)", "mock (>=4)", "uvloop (>=0.15)"] -trio = ["trio (>=0.16,<0.22)"] +doc = ["packaging", "Sphinx (>=6.1.0)", "sphinx-rtd-theme", "sphinxcontrib-jquery", "sphinx-autodoc-typehints (>=1.2.0)"] +test = ["anyio", "coverage[toml] (>=4.5)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)", "mock (>=4)"] +trio = ["trio (<0.22)"] [[package]] name = "atomicwrites" @@ -91,6 +92,17 @@ python-versions = "*" [package.extras] test = ["hypothesis (==3.55.3)", "flake8 (==3.7.8)"] +[[package]] +name = "exceptiongroup" +version = "1.1.1" +description = "Backport of PEP 654 (exception groups)" +category = "main" +optional = false +python-versions = ">=3.7" + +[package.extras] +test = ["pytest (>=6)"] + [[package]] name = "h11" version = "0.14.0" @@ -354,7 +366,7 @@ all = ["rich (>=10.11.0,<13.0.0)", "shellingham (>=1.3.0,<2.0.0)", "colorama (>= [[package]] name = "typing-extensions" -version = "4.6.1" +version = "4.6.3" description = "Backported and Experimental Type Hints for Python 3.7+" category = "main" optional = false @@ -375,8 +387,8 @@ content-hash = "736a61ce1b5d4407e156eb62995604d2411c2d9633403b147fe0c2bdb6fd774b [metadata.files] anyio = [ - {file = "anyio-3.6.2-py3-none-any.whl", hash = "sha256:fbbe32bd270d2a2ef3ed1c5d45041250284e31fc0a4df4a5a6071842051a51e3"}, - {file = "anyio-3.6.2.tar.gz", hash = "sha256:25ea0d673ae30af41a0c442f81cf3b38c7e79fdc7b60335a4c14e05eb0947421"}, + {file = "anyio-3.7.0-py3-none-any.whl", hash = "sha256:eddca883c4175f14df8aedce21054bfca3adb70ffe76a9f607aef9d7fa2ea7f0"}, + {file = "anyio-3.7.0.tar.gz", hash = "sha256:275d9973793619a5374e1c89a4f4ad3f4b0a5510a2b5b939444bee8f4c4d37ce"}, ] atomicwrites = [ {file = "atomicwrites-1.4.1.tar.gz", hash = "sha256:81b2c9071a49367a7f770170e5eec8cb66567cfbbc8c73d20ce5ca4a8d71cf11"}, @@ -405,6 +417,10 @@ commonmark = [ {file = "commonmark-0.9.1-py2.py3-none-any.whl", hash = "sha256:da2f38c92590f83de410ba1a3cbceafbc74fee9def35f9251ba9a971d6d66fd9"}, {file = "commonmark-0.9.1.tar.gz", hash = "sha256:452f9dc859be7f06631ddcb328b6919c67984aca654e5fefb3914d54691aed60"}, ] +exceptiongroup = [ + {file = "exceptiongroup-1.1.1-py3-none-any.whl", hash = "sha256:232c37c63e4f682982c8b6459f33a8981039e5fb8756b2074364e5055c498c9e"}, + {file = "exceptiongroup-1.1.1.tar.gz", hash = "sha256:d484c3090ba2889ae2928419117447a14daf3c1231d5e30d0aae34f354f01785"}, +] h11 = [ {file = "h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"}, {file = "h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d"}, @@ -644,8 +660,8 @@ typer = [ {file = "typer-0.5.0.tar.gz", hash = "sha256:4c285a5585c94d32c305444af934f0078b6a8ba91464f3f85807c91cd499d195"}, ] typing-extensions = [ - {file = "typing_extensions-4.6.1-py3-none-any.whl", hash = "sha256:6bac751f4789b135c43228e72de18637e9a6c29d12777023a703fd1a6858469f"}, - {file = "typing_extensions-4.6.1.tar.gz", hash = "sha256:558bc0c4145f01e6405f4a5fdbd82050bd221b119f4bf72a961a1cfd471349d6"}, + {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, + {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, ] wcwidth = [ {file = "wcwidth-0.2.6-py2.py3-none-any.whl", hash = "sha256:795b138f6875577cd91bba52baf9e445cd5118fd32723b460e30a0af30ea230e"}, diff --git a/pyproject.toml b/pyproject.toml index c5a518d..304e8d6 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "libretrofuzz" -version = "2.8.6" +version = "2.9.0" description = "Fuzzy Retroarch thumbnail downloader" authors = ["i30817 "] license = "MIT"