def game(key: str): try: summary = ValorantGameSummary.get(key) except ValorantGameSummary.DoesNotExist: return 'Game does not exist', 404 game, metadata = load_game(summary) dev_info = get_dev_info(summary, game, metadata) if game.won is not None: result = ['LOSS', 'WIN'][game.won] elif game.rounds.has_game_resets: result = 'SCRIM' else: result = 'game' if game.teams.firstperson: title = f'{game.teams.firstperson.name}\'s {result} on {game.map}' else: title = f'{result[0].capitalize() + result[1:]} on {game.map}' imagehash = hashlib.md5( str((game.won, game.game_mode, game.rounds.final_score, summary.agent)).encode()).hexdigest() def is_first_round(round: Round) -> bool: return round.attacking == game.rounds.rounds[0].attacking rounds_first = takewhile(is_first_round, game.rounds.rounds) rounds_second = dropwhile(is_first_round, game.rounds.rounds) return render_template( 'valorant/game/game.html', title=title, meta=Meta(title=title, image_url=url_for( 'valorant.game.game_card_png', key=key, _external=True) + f'?_cachebust={imagehash}', twitter_image_url=url_for( 'valorant.game.game_card_png', key=key, _external=True) + f'?height=190&_cachebust={imagehash}', summary_large_image=True, colour=COLOURS.get(result, 'gray')), summary=summary, game=game, rounds_combined=list(zip_longest(rounds_first, rounds_second)), # show_edit=check_authentication() is None and (summary.user_id == session.user_id or session.superuser), dev_info=dev_info, )
def game(key: str): try: summary = OverwatchGameSummary.get(key) except OverwatchGameSummary.DoesNotExist: return 'Game does not exist', 404 if summary.player_name and summary.result != 'UNKNOWN': title = f'{summary.player_name}\'s {summary.result} on {summary.map}' elif summary.player_name: title = f'{summary.player_name}\'s game on {summary.map}' else: title = f'{key.split("/", 1)[0]}\'s game on {summary.map}' if not summary.game_version or summary.game_version < OLDEST_SUPPORTED_GAME_VERSION or 'legacy' in request.args: return render_template( 'overwatch/game/legacy_game.html', title=title, legacy_base=LEGACY_URL, legacy_scripts=legacy_scripts, legacy_stylesheet=legacy_stylesheet, ) if not summary.viewable and not(check_authentication() is None and session.superuser): return 'Please subscribe to view game details', 403 if summary.game_type == 'custom' and (check_authentication() is not None or (not session.superuser and session.user_id != summary.user_id)): return 'Custom games are restricted to being viewed by their owner', 403 game, metadata = load_game(summary) game.timestamp = summary.time if game.teams.owner and game.result != 'UNKNOWN': title = f'{game.teams.owner.name}\'s {game.result} on {game.map.name}' elif game.teams.owner: title = f'{game.teams.owner.name}\'s game on {game.map.name}' dev_info = get_dev_info(summary, game, metadata) imagehash = hashlib.md5(str((game.result, game.start_sr, game.end_sr)).encode()).hexdigest() try: tfs = game.teamfights stat_totals = { 'eliminations.during_fights': len(tfs.eliminations_during_fights), 'deaths.during_fights': len(tfs.eliminations_during_fights) + len(tfs.suicides_during_fights), 'killfeed_assists.during_fights': len(tfs.killfeed_assists_during_fights), 'first_elims': len(tfs.first_bloods), 'first_deaths': len(tfs.first_bloods), 'eliminations.outside_fights': len(tfs.eliminations_outside_fights), 'deaths.outside_fights': len(tfs.eliminations_outside_fights) + len(tfs.suicides_outside_fights), 'killfeed_assists.outside_fights': len(tfs.killfeed_assists_outside_fights), 'fight_starts_missed': len(tfs.teamfights), 'times_staggered': len(tfs.teamfights), } except AttributeError: stat_totals = {} return render_template( 'overwatch/game/game.html', title=title, meta=Meta( title=title, image_url=url_for('overwatch.game.game_card_png', key=key, _external=True) + f'?_cachebust={imagehash}', twitter_image_url=url_for('overwatch.game.game_card_png', key=key, _external=True) + f'?height=190&_cachebust={imagehash}', summary_large_image=True, colour=COLOURS.get(game.result, 'gray') ), # show_stats=show_stats, stat_totals=stat_totals, # get_top_heroes=get_top_heroes, # get_hero_color=get_hero_color, # get_hero_image=get_hero_image, # process_stat=process_stat, summary=summary, game=game, show_edit=check_authentication() is None and (summary.user_id == session.user_id or session.superuser), dev_info=dev_info, OLDEST_SUPPORTED_GAME_VERSION=OLDEST_SUPPORTED_GAME_VERSION, )
def game(key: str): try: summary = ApexGameSummary.get(key) except ApexGameSummary.DoesNotExist: return 'Game does not exist', 404 logger.info(f'Fetching {summary.url}') try: url = urlparse(summary.url) game_object = s3.get_object(Bucket=url.netloc.split('.')[0], Key=url.path[1:]) game_data = json.loads(game_object['Body'].read()) except: game_object = None logger.exception('Failed to fetch game data from S3 - trying HTTP') r = requests.get(summary.url) r.raise_for_status() game_data = r.json() game_data = compat_game_data(game_data) game = referenced_typedload.load(game_data, ApexGame) # used for link previews og_description = make_game_description(summary, divider='\n') meta = Meta( title= f'{game.squad.player.name} placed #{summary.placed}', # TODO: find another way of getting the name, description=og_description, colour={ 1: '#ffdf00', 2: '#ef20ff', 3: '#d95ff' }.get(summary.placed, '#992e26'), image_url=image_url(game.squad.player.champion)) scrim_details = None if summary.scrims and summary.match_id and game.match_id: champion_name = (game.champion or {}).get('ocr_name') or summary.match_id.split('/')[1] matching_games = [] for match_id in game.match_ids: logger.info( f'Checking for matching scrims with match_id={match_id}') for other_game in ApexGameSummary.match_id_index.query( match_id, (ApexGameSummary.scrims == summary.scrims ) # & (ApexGameSummary.key != summary.key) ): if not any( any(other_game.key == g.key for g in gs) for gs in matching_games): for gamesets in matching_games: if any(g.placed == other_game.placed for g in gamesets): gamesets.append(other_game) break else: matching_games.append([other_game]) # TODO: dedupe # for g in matching_games: # g.player_name = ' / '.join(filter(None, [g.player_name] + list(g.squadmate_names or ()))) scrim_details = ScrimDetails( champion_name, 'Mendo Scrims (Beta)', sorted(matching_games, key=lambda gs: gs[0].placed), ) logger.info(f'Scrim details: {scrim_details}') if logs and check_authentication( ) is None and session.superuser and game_object: try: admin_data = get_admin_data(summary, game_object) except: logger.exception('Failed to get admin data for game') admin_data = None else: admin_data = None return render_template('apex/game/game.html', summary=summary, game=game, is_ranked=summary.rank is not None, scrim_details=scrim_details, meta=meta, admin_data=admin_data)
import os from overtrack_web.data import apex_data, overwatch_data from overtrack_web.data.apex_data import ApexRankSummary, ApexSeason from overtrack_web.lib.opengraph import Meta CDN_URL = os.environ.get('CDN_URL', 'https://cdn.overtrack.gg/static') WELCOME_META = Meta( title='Apex Legends Automatic Match History', description= '''Automatically track your Apex Legends games with computer vision! Tracks your rank, placement, kills, downs, route, weapons, and stats.''', image_url=f'{CDN_URL}/apex_teaser.png', summary_large_image=True, oembed=f'{CDN_URL}/oembed.json')
import os from overtrack_web.data import apex_data, overwatch_data from overtrack_web.data.apex_data import ApexRankSummary, ApexSeason from overtrack_web.lib.opengraph import Meta CDN_URL = os.environ.get('CDN_URL', 'https://cdn.overtrack.gg/static') WELCOME_META = Meta( title='Match History for Overwatch, Valorant, and Apex Legends', description=( 'Automatically track your Overwatch, Valorant, and Apex Legends games with computer vision.\n ' 'Get full match history with details on everything that happened over the course of your games; ' 'kills, deaths, winrate statistics, composition, ults (Overwatch, Valorant), and map route + circles (Apex Legends)' ), image_url=f'{CDN_URL}/images/teaser_ow_val.jpg', summary_large_image=True, oembed=f'{CDN_URL}/oembed.json' ) VALORANT_WELCOME_META = Meta( title='Match History for Valorant, Overwatch, and Apex Legends', description=( 'Automatically track your Valorant games with computer vision.\n ' 'Get full match history with details on everything that happened over the course of your games; ' 'rounds, kills, deaths, ults, agent/map winrates, and more.' ), image_url=f'{CDN_URL}/images/valorant_teaser.png', summary_large_image=True, oembed=f'{CDN_URL}/oembed.json' )
def render_games_list(user: User, public=False, meta_title: Optional[str] = None) -> FlaskResponse: user.refresh() games_it, is_ranked, season = get_games(user, limit=PAGINATION_SIZE) games, next_from = paginate(games_it, username=user.username if public else None) from pprint import pprint pprint([g.season for g in games]) if not len(games): logger.info(f'User {user.username} has no games') if not public and 'season' not in request.args: return render_template('client.html', no_games_alert=True, meta=WELCOME_META) # TODO: don't list ranked/unranked if a player has e.g. no ranked games in a season seasons = [] for sid in user.apex_seasons: if sid in apex_data.seasons: seasons.append(apex_data.seasons[sid]) logger.info( f'User {user.username} has user.apex_seasons={user.apex_seasons} => {seasons}' ) seasons = sorted(seasons, key=lambda s: s.start, reverse=True) t0 = time.time() if len(games) and games[0].url: try: url = urlparse(games[0].url) game_object = s3.get_object(Bucket=url.netloc.split('.')[0], Key=url.path[1:]) latest_game_data = json.loads(game_object['Body'].read()) except: logger.exception('Failed to fetch game data from S3 - trying HTTP') r = requests.get(games[0].url) r.raise_for_status() latest_game_data = r.json() latest_game_data = compat_game_data(latest_game_data) latest_game = referenced_typedload.load(latest_game_data, ApexGame) else: latest_game = None t1 = time.perf_counter() logger.info(f'latest game fetch: {(t1 - t0) * 1000:.2f}ms') is_rank_valid = (is_ranked and latest_game and latest_game.rank and latest_game.rank.rp is not None and latest_game.rank.rp_change is not None) if is_rank_valid: rp = latest_game.rank.rp + latest_game.rank.rp_change derived_rank = None derived_tier = None for rank, (lower, upper) in apex_data.rank_rp.items(): if lower <= rp < upper: derived_rank = rank rank_floor, rank_ceil = apex_data.get_tier_window( rp, lower, (upper - lower) // 4) if rank != 'apex_predator': division = (upper - lower) // 4 tier_ind = (rp - lower) // division derived_tier = ['IV', 'III', 'II', 'I'][tier_ind] else: derived_rank = 'apex predator' derived_tier = '' rank_floor = 1000 rank_ceil = rp break rank_summary = ApexRankSummary(rp, rank_floor, rank_ceil, derived_rank, derived_tier) else: rank_summary = None if is_ranked and len(games): rp_data = [ game.rank.rp for game in reversed(games) if game.rank and game.rank.rp ] if games[0].rank and games[0].rank.rp is not None and games[ 0].rank.rp_change is not None: rp_data.append(games[0].rank.rp + games[0].rank.rp_change) else: rp_data = None if public and latest_game: # description = f'{len(games)} Season {season.index} games\n' description = '' if rank_summary: description += 'Rank: ' + rank_summary.rank.title() if rank_summary.tier: description += ' ' + rank_summary.tier description += '\n' description += f'Last game: {make_game_description(games[0], divider=" / ", include_knockdowns=False)}' summary_meta = Meta( title=(meta_title or user.username) + "'s Games", description=description, colour=rank_summary.color if rank_summary else '#992e26', image_url=url_for('static', filename=f'images/apex/{games[0].rank.rank}.png') if games[0].rank else None) else: summary_meta = WELCOME_META return render_template( 'apex/games_list/games_list.html', title=f"Apex Legends | {user.username}'s Games" if public else "My Games", games=games, next_from=next_from, meta=summary_meta, season=season, seasons=seasons, is_ranked=is_ranked, rank_summary=rank_summary, rp_data=rp_data, latest_game=latest_game, show_sub_request=not public and not session.user.subscription_active, )