mirror of
https://github.com/derrod/legendary.git
synced 2024-12-22 17:55:27 +00:00
[cli/core/utils/lfs] Add automatic alias generation
This commit is contained in:
parent
d70d5a6521
commit
0d8b74a9e0
|
@ -51,9 +51,15 @@ class LegendaryCLI:
|
|||
ql.start()
|
||||
return ql
|
||||
|
||||
def _resolve_aliases(self, app_name):
|
||||
def _resolve_aliases(self, name):
|
||||
# make sure aliases exist if not yet created
|
||||
self.core.update_aliases(force=False)
|
||||
name = name.strip()
|
||||
# resolve alias (if any) to real app name
|
||||
return self.core.lgd.config.get('Legendary.aliases', app_name, fallback=app_name)
|
||||
return self.core.lgd.config.get(
|
||||
section='Legendary.aliases', option=name,
|
||||
fallback=self.core.lgd.aliases.get(name.lower(), name)
|
||||
)
|
||||
|
||||
def auth(self, args):
|
||||
if args.auth_delete:
|
||||
|
|
|
@ -273,6 +273,11 @@ class LegendaryCore:
|
|||
# return data if available
|
||||
return sdl_data
|
||||
|
||||
def update_aliases(self, force=False):
|
||||
_aliases_enabled = not self.lgd.config.getboolean('Legendary', 'disable_auto_aliasing', fallback=False)
|
||||
if _aliases_enabled and (force or not self.lgd.aliases):
|
||||
self.lgd.generate_aliases()
|
||||
|
||||
def get_assets(self, update_assets=False, platform_override=None) -> List[GameAsset]:
|
||||
# do not save and always fetch list when platform is overridden
|
||||
if platform_override:
|
||||
|
@ -311,6 +316,7 @@ class LegendaryCore:
|
|||
force_refresh=False, skip_ue=True) -> (List[Game], Dict[str, List[Game]]):
|
||||
_ret = []
|
||||
_dlc = defaultdict(list)
|
||||
meta_updated = False
|
||||
|
||||
for ga in self.get_assets(update_assets=update_assets,
|
||||
platform_override=platform_override):
|
||||
|
@ -328,6 +334,7 @@ class LegendaryCore:
|
|||
app_title=eg_meta['title'], asset_info=ga, metadata=eg_meta)
|
||||
|
||||
if not platform_override:
|
||||
meta_updated = True
|
||||
self.lgd.set_game_meta(game.app_name, game)
|
||||
|
||||
# replace asset info with the platform specific one if override is used
|
||||
|
@ -340,6 +347,9 @@ class LegendaryCore:
|
|||
elif not any(i['path'] == 'mods' for i in game.metadata.get('categories', [])):
|
||||
_ret.append(game)
|
||||
|
||||
if not platform_override:
|
||||
self.update_aliases(force=meta_updated)
|
||||
|
||||
return _ret, _dlc
|
||||
|
||||
def get_non_asset_library_items(self, force_refresh=False,
|
||||
|
|
|
@ -4,10 +4,12 @@ import json
|
|||
import os
|
||||
import logging
|
||||
|
||||
from collections import defaultdict
|
||||
from pathlib import Path
|
||||
from time import time
|
||||
|
||||
from legendary.models.game import *
|
||||
from legendary.utils.aliasing import generate_aliases
|
||||
from legendary.utils.config import LGDConf
|
||||
from legendary.utils.env import is_windows_or_pyi
|
||||
from legendary.utils.lfs import clean_filename
|
||||
|
@ -112,6 +114,17 @@ class LGDLFS:
|
|||
except Exception as e:
|
||||
self.log.debug(f'Loading game meta file "{gm_file}" failed: {e!r}')
|
||||
|
||||
# load auto-aliases if enabled
|
||||
self.aliases = dict()
|
||||
if not self.config.getboolean('Legendary', 'disable_auto_aliasing', fallback=False):
|
||||
try:
|
||||
_j = json.load(open(os.path.join(self.path, 'aliases.json')))
|
||||
for app_name, aliases in _j.items():
|
||||
for alias in aliases:
|
||||
self.aliases[alias] = app_name
|
||||
except Exception as e:
|
||||
self.log.debug(f'Loading aliases failed with {e!r}')
|
||||
|
||||
@property
|
||||
def userdata(self):
|
||||
if self._user_data is not None:
|
||||
|
@ -339,3 +352,38 @@ class LGDLFS:
|
|||
json.dump(dict(version=sdl_version, data=sdl_data),
|
||||
open(os.path.join(self.path, 'tmp', f'{app_name}.json'), 'w'),
|
||||
indent=2, sort_keys=True)
|
||||
|
||||
def generate_aliases(self):
|
||||
self.log.debug('Generating list of aliases...')
|
||||
|
||||
aliases = set()
|
||||
collisions = set()
|
||||
alias_map = defaultdict(set)
|
||||
|
||||
for app_name in self._game_metadata.keys():
|
||||
game = self.get_game_meta(app_name)
|
||||
if game.is_dlc:
|
||||
continue
|
||||
game_folder = game.metadata.get('customAttributes', {}).get('FolderName', {}).get('value', None)
|
||||
_aliases = generate_aliases(game.app_title, game_folder)
|
||||
for alias in _aliases:
|
||||
if alias not in aliases:
|
||||
aliases.add(alias)
|
||||
alias_map[game.app_name].add(alias)
|
||||
else:
|
||||
collisions.add(alias)
|
||||
|
||||
# remove colliding aliases from map and add aliases to lookup table
|
||||
for app_name, aliases in alias_map.items():
|
||||
alias_map[app_name] -= collisions
|
||||
for alias in alias_map[app_name]:
|
||||
self.aliases[alias] = app_name
|
||||
|
||||
def serialise_sets(obj):
|
||||
"""Turn sets into sorted lists for storage"""
|
||||
if isinstance(obj, set):
|
||||
return sorted(obj)
|
||||
return obj
|
||||
|
||||
json.dump(alias_map, open(os.path.join(self.path, 'aliases.json'), 'w', newline='\n'),
|
||||
indent=2, sort_keys=True, default=serialise_sets)
|
||||
|
|
89
legendary/utils/aliasing.py
Normal file
89
legendary/utils/aliasing.py
Normal file
|
@ -0,0 +1,89 @@
|
|||
from string import ascii_lowercase, digits
|
||||
|
||||
# Aliases generated:
|
||||
# - name lowercase (without TM etc.)
|
||||
# - same, but without spaces
|
||||
# - same, but roman numerals are replaced
|
||||
# if name has >= 3 parts:
|
||||
# - initials
|
||||
# - initials, but roman numerals are intact
|
||||
# - initials, but roman numerals are replaced with number
|
||||
# if ':' in name:
|
||||
# - run previous recursively with everything before ":"
|
||||
# if single 'f' in long word:
|
||||
# - split word (this is mainly for cases like Battlfront -> BF)
|
||||
|
||||
allowed_characters = ascii_lowercase+digits
|
||||
roman = {
|
||||
# 'i': '1',
|
||||
'ii': '2',
|
||||
'iii': '3',
|
||||
'iv': '4',
|
||||
'v': '5',
|
||||
'vi': '6',
|
||||
'vii': '7',
|
||||
'viii': '8',
|
||||
'ix': '9',
|
||||
'x': '10',
|
||||
'xi': '11',
|
||||
'xii': '12',
|
||||
'xiii': '13',
|
||||
'xiv': '14',
|
||||
'xv': '15',
|
||||
'xvi': '16',
|
||||
'xvii': '17',
|
||||
'xviii': '18',
|
||||
'xix': '19',
|
||||
'xx': '20'
|
||||
}
|
||||
|
||||
|
||||
def _filter(input):
|
||||
return ''.join(l for l in input if l in allowed_characters)
|
||||
|
||||
|
||||
def generate_aliases(game_name, game_folder=None, split_words=True):
|
||||
# normalise and split name, then filter for legal characters
|
||||
game_parts = [_filter(p) for p in game_name.lower().split()]
|
||||
# filter out empty parts
|
||||
game_parts = [p for p in game_parts if p]
|
||||
|
||||
_aliases = [
|
||||
' '.join(game_parts),
|
||||
''.join(game_parts),
|
||||
''.join(roman.get(p, p) for p in game_parts),
|
||||
]
|
||||
|
||||
# single word abbreviation
|
||||
first_word = next(i for i in game_parts if i not in ('for', 'the'))
|
||||
if len(first_word) > 1:
|
||||
_aliases.append(first_word)
|
||||
# remove subtitle from game
|
||||
if ':' in game_name:
|
||||
_aliases.extend(generate_aliases(game_name.partition(':')[0]))
|
||||
# include folder name for alternative short forms
|
||||
if game_folder:
|
||||
_aliases.extend(generate_aliases(game_folder, split_words=False))
|
||||
# include initialisms
|
||||
if len(game_parts) > 2:
|
||||
_aliases.append(''.join(p[0] for p in game_parts))
|
||||
_aliases.append(''.join(p[0] if p not in roman else p for p in game_parts))
|
||||
_aliases.append(''.join(roman.get(p, p[0]) for p in game_parts))
|
||||
# Attempt to address cases like "Battlefront" being shortened to "BF"
|
||||
if split_words:
|
||||
new_game_parts = []
|
||||
for word in game_parts:
|
||||
if len(word) >= 8 and word[3:-3].count('f') == 1:
|
||||
word_middle = word[3:-3]
|
||||
word_split = ' f'.join(word_middle.split('f'))
|
||||
word = word[0:3] + word_split + word[-3:]
|
||||
new_game_parts.extend(word.split())
|
||||
else:
|
||||
new_game_parts.append(word)
|
||||
|
||||
_aliases.append(''.join(p[0] for p in new_game_parts))
|
||||
_aliases.append(''.join(p[0] if p not in roman else p for p in new_game_parts))
|
||||
_aliases.append(''.join(roman.get(p, p[0]) for p in new_game_parts))
|
||||
|
||||
# return sorted unqiues
|
||||
return sorted(set(_aliases))
|
Loading…
Reference in a new issue