import re from datetime import datetime, timedelta, timezone import slippi.event as evt import slippi.id as id from slippi.util import * class Metadata(Base): """Miscellaneous data not directly provided by Melee.""" def __init__(self, date, duration, platform, players, console_name=None): self.date = date #: datetime: Game start date & time self.duration = duration #: int: Duration of game, in frames self.platform = platform #: :py:class:`Platform`: Platform the game was played on (console/dolphin) self.players = players #: tuple(:py:class:`Player` | None): Player metadata by port (port 1 is at index 0; empty ports will contain None) self.console_name = console_name #: str | None: Name of the console the game was played on, if any @classmethod def _parse(cls, json): d = json['startAt'].rstrip('\x00') # workaround for Nintendont/Slippi<1.5 bug # timezone & fractional seconds aren't always provided, so parse the date manually (strptime lacks support for optional components) m = [int(g or '0') for g in re.search(r'(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.(\d+))?(?:Z|\+(\d{2})(\d{2}))?$', d).groups()] date = datetime(*m[:7], timezone(timedelta(hours=m[7], minutes=m[8]))) try: duration = 1 + json['lastFrame'] - evt.FIRST_FRAME_INDEX except KeyError: duration = None platform = cls.Platform(json['playedOn']) try: console_name = json['consoleNick'] except KeyError: console_name = None players = [None, None, None, None] for i in PORTS: try: players[i] = cls.Player._parse(json['players'][str(i)]) except KeyError: pass return cls(date=date, duration=duration, platform=platform, players=tuple(players), console_name=console_name) def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented return self.date == other.date and self.duration == other.duration and self.platform == other.platform and self.players == other.players and self.console_name == other.console_name class Player(Base): def __init__(self, characters, netplay_name=None): self.characters = characters #: dict(:py:class:`slippi.id.InGameCharacter`, int): Character(s) used, with usage duration in frames (for Zelda/Sheik) self.netplay_name = netplay_name #: str | None: Netplay name of player (Dolphin-only) @classmethod def _parse(cls, json): characters = {} for char_id, duration in json['characters'].items(): characters[id.InGameCharacter(int(char_id))] = duration try: netplay_name = json['names']['netplay'] except KeyError: netplay_name = None return cls(characters, netplay_name) def __eq__(self, other): if not isinstance(other, self.__class__): return NotImplemented return self.characters == other.characters and self.netplay_name == other.netplay_name class Platform(Enum): CONSOLE = 'console' DOLPHIN = 'dolphin' NETWORK = 'network' NINTENDONT = 'nintendont'