ai-phone-leaderboard / src /utils /device_lookup.py
agh123's picture
feat: use android device marketing name
097ec8a
import requests
from urllib.parse import quote
from functools import lru_cache
import os
import json
from functools import lru_cache
apple_device_mapping = {
"Watch1,1": "Apple Watch 38mm case",
"Watch1,2": "Apple Watch 42mm case",
"Watch2,3": "Apple Watch Series 2 38mm case",
"Watch2,4": "Apple Watch Series 2 42mm case",
"Watch2,6": "Apple Watch Series 1 38mm case",
"Watch2,7": "Apple Watch Series 1 42mm case",
"Watch3,1": "Apple Watch Series 3 38mm case",
"Watch3,2": "Apple Watch Series 3 42mm case",
"Watch3,3": "Apple Watch Series 3 38mm case",
"Watch3,4": "Apple Watch Series 3 42mm case",
"Watch4,1": "Apple Watch Series 4 40mm case",
"Watch4,2": "Apple Watch Series 4 44mm case",
"Watch4,3": "Apple Watch Series 4 40mm case",
"Watch4,4": "Apple Watch Series 4 44mm case",
"Watch5,1": "Apple Watch Series 5 40mm case",
"Watch5,10": "Apple Watch SE 44mm case",
"Watch5,11": "Apple Watch SE 40mm case",
"Watch5,12": "Apple Watch SE 44mm case",
"Watch5,2": "Apple Watch Series 5 44mm case",
"Watch5,3": "Apple Watch Series 5 40mm case",
"Watch5,4": "Apple Watch Series 5 44mm case",
"Watch5,9": "Apple Watch SE 40mm case",
"Watch6,1": "Apple Watch Series 6 40mm case",
"Watch6,10": "Apple Watch SE 40mm case",
"Watch6,11": "Apple Watch SE 44mm case",
"Watch6,12": "Apple Watch SE 40mm case",
"Watch6,13": "Apple Watch SE 44mm case",
"Watch6,14": "Apple Watch Series 8 41mm case",
"Watch6,15": "Apple Watch Series 8 45mm case",
"Watch6,16": "Apple Watch Series 8 41mm case",
"Watch6,17": "Apple Watch Series 8 45mm case",
"Watch6,18": "Apple Watch Ultra",
"Watch6,2": "Apple Watch Series 6 44mm case",
"Watch6,3": "Apple Watch Series 6 40mm case",
"Watch6,4": "Apple Watch Series 6 44mm case",
"Watch6,6": "Apple Watch Series 7 41mm case",
"Watch6,7": "Apple Watch Series 7 45mm case",
"Watch6,8": "Apple Watch Series 7 41mm case",
"Watch6,9": "Apple Watch Series 7 45mm case",
"Watch7,1": "Apple Watch Series 9 41mm case",
"Watch7,10": "Apple Watch Series 10 42mm case",
"Watch7,11": "Apple Watch Series 10 46mm",
"Watch7,2": "Apple Watch Series 9 45mm case",
"Watch7,3": "Apple Watch Series 9 41mm case",
"Watch7,4": "Apple Watch Series 9 45mm case",
"Watch7,5": "Apple Watch Ultra 2",
"Watch7,8": "Apple Watch Series 10 42mm case",
"Watch7,9": "Apple Watch Series 10 46mm case (GPS",
"arm64": "iPhone Simulator",
"i386": "iPhone Simulator",
"iPad1,1": "iPad",
"iPad1,2": "iPad 3G",
"iPad11,1": "iPad mini 5th Gen",
"iPad11,2": "iPad mini 5th Gen",
"iPad11,3": "iPad Air 3rd Gen",
"iPad11,4": "iPad Air 3rd Gen",
"iPad11,6": "iPad 8th Gen",
"iPad11,7": "iPad 8th Gen",
"iPad12,1": "iPad 9th Gen",
"iPad12,2": "iPad 9th Gen",
"iPad13,1": "iPad Air 4th Gen",
"iPad13,10": "iPad Pro 12.9 inch 5th Gen",
"iPad13,11": "iPad Pro 12.9 inch 5th Gen",
"iPad13,16": "iPad Air 5th Gen",
"iPad13,17": "iPad Air 5th Gen",
"iPad13,18": "iPad 10th Gen",
"iPad13,19": "iPad 10th Gen",
"iPad13,2": "iPad Air 4th Gen",
"iPad13,4": "iPad Pro 11 inch 5th Gen",
"iPad13,5": "iPad Pro 11 inch 5th Gen",
"iPad13,6": "iPad Pro 11 inch 5th Gen",
"iPad13,7": "iPad Pro 11 inch 5th Gen",
"iPad13,8": "iPad Pro 12.9 inch 5th Gen",
"iPad13,9": "iPad Pro 12.9 inch 5th Gen",
"iPad14,1": "iPad mini 6th Gen",
"iPad14,10": "iPad Air 13 inch 6th Gen",
"iPad14,11": "iPad Air 13 inch 6th Gen",
"iPad14,2": "iPad mini 6th Gen",
"iPad14,3": "iPad Pro 11 inch 4th Gen",
"iPad14,4": "iPad Pro 11 inch 4th Gen",
"iPad14,5": "iPad Pro 12.9 inch 6th Gen",
"iPad14,6": "iPad Pro 12.9 inch 6th Gen",
"iPad14,8": "iPad Air 11 inch 6th Gen",
"iPad14,9": "iPad Air 11 inch 6th Gen",
"iPad15,3": "iPad Air 11-inch 7th Gen",
"iPad15,4": "iPad Air 11-inch 7th Gen",
"iPad15,5": "iPad Air 13-inch 7th Gen",
"iPad15,6": "iPad Air 13-inch 7th Gen",
"iPad15,7": "iPad 11th Gen",
"iPad15,8": "iPad 11th Gen",
"iPad16,1": "iPad mini 7th Gen",
"iPad16,2": "iPad mini 7th Gen",
"iPad16,3": "iPad Pro 11 inch 5th Gen",
"iPad16,4": "iPad Pro 11 inch 5th Gen",
"iPad16,5": "iPad Pro 12.9 inch 7th Gen",
"iPad16,6": "iPad Pro 12.9 inch 7th Gen",
"iPad2,1": "2nd Gen iPad",
"iPad2,2": "2nd Gen iPad",
"iPad2,3": "2nd Gen iPad",
"iPad2,4": "2nd Gen iPad New Revision",
"iPad2,5": "iPad mini",
"iPad2,6": "iPad mini",
"iPad2,7": "iPad mini",
"iPad3,1": "3rd Gen iPad",
"iPad3,2": "3rd Gen iPad",
"iPad3,3": "3rd Gen iPad",
"iPad3,4": "4th Gen iPad",
"iPad3,5": "4th Gen iPad",
"iPad3,6": "4th Gen iPad",
"iPad4,1": "iPad Air",
"iPad4,2": "iPad Air",
"iPad4,3": "1st Gen iPad Air",
"iPad4,4": "iPad mini Retina",
"iPad4,5": "iPad mini Retina",
"iPad4,6": "iPad mini Retina",
"iPad4,7": "iPad mini 3",
"iPad4,8": "iPad mini 3",
"iPad4,9": "iPad Mini 3",
"iPad5,1": "iPad mini 4",
"iPad5,2": "4th Gen iPad mini",
"iPad5,3": "iPad Air 2",
"iPad5,4": "iPad Air 2",
"iPad6,11": "iPad (2017)",
"iPad6,12": "iPad (2017)",
"iPad6,3": "iPad Pro (9.7 inch)",
"iPad6,4": "iPad Pro (9.7 inch)",
"iPad6,7": "iPad Pro (12.9 inch)",
"iPad6,8": "iPad Pro (12.9 inch)",
"iPad7,1": "iPad Pro 2nd Gen",
"iPad7,11": "iPad 7th Gen 10.2-inch",
"iPad7,12": "iPad 7th Gen 10.2-inch",
"iPad7,2": "iPad Pro 2nd Gen",
"iPad7,3": "iPad Pro 10.5-inch 2nd Gen",
"iPad7,4": "iPad Pro 10.5-inch 2nd Gen",
"iPad7,5": "iPad 6th Gen",
"iPad7,6": "iPad 6th Gen",
"iPad8,1": "iPad Pro 11 inch 3rd Gen",
"iPad8,10": "iPad Pro 11 inch 4th Gen",
"iPad8,11": "iPad Pro 12.9 inch 4th Gen",
"iPad8,12": "iPad Pro 12.9 inch 4th Gen",
"iPad8,2": "iPad Pro 11 inch 3rd Gen",
"iPad8,3": "iPad Pro 11 inch 3rd Gen",
"iPad8,4": "iPad Pro 11 inch 3rd Gen",
"iPad8,5": "iPad Pro 12.9 inch 3rd Gen",
"iPad8,6": "iPad Pro 12.9 inch 3rd Gen",
"iPad8,7": "iPad Pro 12.9 inch 3rd Gen",
"iPad8,8": "iPad Pro 12.9 inch 3rd Gen",
"iPad8,9": "iPad Pro 11 inch 4th Gen",
"iPhone1,1": "iPhone",
"iPhone1,2": "iPhone 3G",
"iPhone10,1": "iPhone 8",
"iPhone10,2": "iPhone 8 Plus",
"iPhone10,3": "iPhone X",
"iPhone10,4": "iPhone 8",
"iPhone10,5": "iPhone 8 Plus",
"iPhone10,6": "iPhone X",
"iPhone11,2": "iPhone XS",
"iPhone11,4": "iPhone XS Max",
"iPhone11,6": "iPhone XS Max",
"iPhone11,8": "iPhone XR",
"iPhone12,1": "iPhone 11",
"iPhone12,3": "iPhone 11 Pro",
"iPhone12,5": "iPhone 11 Pro Max",
"iPhone12,8": "iPhone SE 2nd Gen",
"iPhone13,1": "iPhone 12 Mini",
"iPhone13,2": "iPhone 12",
"iPhone13,3": "iPhone 12 Pro",
"iPhone13,4": "iPhone 12 Pro Max",
"iPhone14,2": "iPhone 13 Pro",
"iPhone14,3": "iPhone 13 Pro Max",
"iPhone14,4": "iPhone 13 Mini",
"iPhone14,5": "iPhone 13",
"iPhone14,6": "iPhone SE 3rd Gen",
"iPhone14,7": "iPhone 14",
"iPhone14,8": "iPhone 14 Plus",
"iPhone15,2": "iPhone 14 Pro",
"iPhone15,3": "iPhone 14 Pro Max",
"iPhone15,4": "iPhone 15",
"iPhone15,5": "iPhone 15 Plus",
"iPhone16,1": "iPhone 15 Pro",
"iPhone16,2": "iPhone 15 Pro Max",
"iPhone17,1": "iPhone 16 Pro",
"iPhone17,2": "iPhone 16 Pro Max",
"iPhone17,3": "iPhone 16",
"iPhone17,4": "iPhone 16 Plus",
"iPhone17,5": "iPhone 16e",
"iPhone2,1": "iPhone 3GS",
"iPhone3,1": "iPhone 4",
"iPhone3,2": "iPhone 4",
"iPhone3,3": "iPhone 4",
"iPhone4,1": "iPhone 4S",
"iPhone5,1": "iPhone 5",
"iPhone5,2": "iPhone 5",
"iPhone5,3": "iPhone 5C",
"iPhone5,4": "iPhone 5C",
"iPhone6,1": "iPhone 5S",
"iPhone6,2": "iPhone 5S",
"iPhone7,1": "iPhone 6 Plus",
"iPhone7,2": "iPhone 6",
"iPhone8,1": "iPhone 6s",
"iPhone8,2": "iPhone 6s Plus",
"iPhone8,4": "iPhone SE",
"iPhone9,1": "iPhone 7",
"iPhone9,2": "iPhone 7 Plus",
"iPhone9,3": "iPhone 7",
"iPhone9,4": "iPhone 7 Plus",
"iPod1,1": "1st Gen iPod",
"iPod2,1": "2nd Gen iPod",
"iPod3,1": "3rd Gen iPod",
"iPod4,1": "4th Gen iPod",
"iPod5,1": "5th Gen iPod",
"iPod7,1": "6th Gen iPod",
"iPod9,1": "7th Gen iPod",
"x86_64": "iPhone Simulator",
}
# Cache for Android device names
CACHE_FILE = os.path.join(
os.path.dirname(os.path.abspath(__file__)), "android_device_cache.json"
)
android_device_cache = {}
# Load cache from file if it exists
def load_cache():
global android_device_cache
try:
if os.path.exists(CACHE_FILE):
with open(CACHE_FILE, "r") as f:
android_device_cache = json.load(f)
print(f"Loaded {len(android_device_cache)} Android device names from cache")
except Exception as e:
print(f"Error loading Android device cache: {str(e)}")
android_device_cache = {}
# Save cache to file
def save_cache():
try:
with open(CACHE_FILE, "w") as f:
json.dump(android_device_cache, f, indent=2)
except Exception as e:
print(f"Error saving Android device cache: {str(e)}")
# Load cache on module import
load_cache()
@lru_cache(maxsize=1024)
def get_device_name(manufacturer, device_id, default_device_name):
"""
Get the normalized device name from a device ID like iPad16,1
Args:
device_id (str): The device identifier (e.g., iPad16,1)
Returns:
str: The normalized device name without connectivity info
"""
if manufacturer == "Apple":
return apple_device_mapping.get(device_id, default_device_name)
else:
return get_android_model_name(manufacturer, device_id)
def get_android_model_name(manufacturer, model):
"""
Get a user-friendly Android device name from cache or GitHub repository
Args:
manufacturer (str): Device manufacturer
model (str): Device model
Returns:
str: User-friendly device name, or original model if not found
"""
# https://github.com/openhoangnc/android_model_name
# Create a cache key by combining manufacturer and model
cache_key = f"{manufacturer.lower()}:{model.lower()}"
# Check if we already have this device in our cache
if cache_key in android_device_cache:
# Return the cached result
return android_device_cache[cache_key]
# If not in cache, fetch from GitHub
repo_url = "https://raw.githubusercontent.com/openhoangnc/android_model_name/main"
# Prepare the URL path components
if not manufacturer:
# Return the model if manufacturer is empty
return model
first_letter = manufacturer[0].lower()
safe_manufacturer = quote(manufacturer.lower())
safe_model = quote(model.replace("/", "-").lower()) if model else ""
# Build the query URL
query_url = f"{repo_url}/{first_letter}/{safe_manufacturer}/{safe_model}"
try:
# Fetch the device name from the repository
response = requests.get(query_url, timeout=3)
if response.status_code == 200:
device_name = response.text.strip()
# Cache the result
android_device_cache[cache_key] = device_name
# Save cache periodically (every 10 new entries)
if len(android_device_cache) % 10 == 0:
save_cache()
return device_name
return model
except Exception as e:
print(f"Error fetching Android model name: {str(e)}")
return model
# Make sure to save the cache when the program exits
import atexit
atexit.register(save_cache)