import os import re import urllib.parse def version_sort_key(entry): """ Create a sort key for version-aware sorting of Proton/Wine entries. Sorts by prefix alphabetically, then by version number (descending - newer first). """ if isinstance(entry, dict): name = entry.get('name', '') url = entry.get('url', '') if url and not name: parsed_url = urllib.parse.urlparse(url) name = os.path.basename(parsed_url.path) else: name = entry # Remove extensions for ext in ['.tar.gz', '.tar.xz', '.zip']: if name.lower().endswith(ext): name = name[:-len(ext)] break # Normalize the name name_lower = name.lower().strip() # Replace underscores and spaces with hyphens for consistency normalized = name_lower.replace('_', '-').replace(' ', '-') # Extract all numeric sequences and text parts tokens = re.findall(r'\d+|\D+', normalized) # Find where the version numbers start # Usually after the first text token(s) prefix_parts = [] version_parts = [] first_number_index = -1 # Find the first number token for i, token in enumerate(tokens): if token.isdigit(): first_number_index = i break # If we found a number, everything before it is prefix if first_number_index > 0: for i in range(first_number_index): prefix_parts.append(tokens[i].strip('-')) # Everything from first number onwards is version for i in range(first_number_index, len(tokens)): token = tokens[i] if token.isdigit(): # Negative for descending order (higher versions first) version_parts.append((0, -int(token))) else: # Part of version string (like "rc", "staging", etc.) cleaned = token.strip('-') if cleaned: version_parts.append((1, cleaned)) else: # No numbers found, treat entire thing as prefix prefix_parts = [t.strip('-') for t in tokens if t.strip('-')] # Clean up prefix prefix = '-'.join(p for p in prefix_parts if p).strip('-') # If no prefix found, use first token if not prefix: prefix = tokens[0] if tokens else normalized # If no version parts found, add a default if not version_parts: version_parts = [(0, 0)] # Return sort key: (prefix for grouping, version parts for version sorting, normalized name) # Use normalized name for tie-breaking to avoid issues with spaces vs underscores return (prefix, version_parts, normalized)