File size: 6,371 Bytes
f0c8ada | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 | export const Platform = {
CHESSCOM: 'chesscom',
LICHESS: 'lichess',
PGN: 'pgn'
}
export class GameLoader {
constructor() {}
static parsePGN(pgn) {
const data = {};
const metadataRegex = /\[(\w+)\s+"([^"]+)"\]/g;
let match;
while ((match = metadataRegex.exec(pgn)) !== null) {
data[match[1].toLowerCase()] = match[2];
}
return data;
}
static async fetchSingleLichessGame(gameId) {
try {
const response = await fetch(`https://lichess.org/game/export/${gameId}`, {
headers: { 'Accept': 'application/x-chess-pgn' }
});
if (!response.ok) {
throw new Error(`Failed to fetch Lichess game: ${response.status}`);
}
const pgn = await response.text();
return {
pgn: pgn.trim(),
url: `https://lichess.org/${gameId}`
};
} catch (error) {
console.error('Error fetching single Lichess game:', error);
return null;
}
}
static async fetchSingleChessComGame(username, gameId) {
try {
const archivesResponse = await fetch(`https://api.chess.com/pub/player/${username}/games/archives`);
if (!archivesResponse.ok) throw new Error(`Failed to fetch archives: ${archivesResponse.status}`);
const { archives = [] } = await archivesResponse.json();
for (const archiveUrl of archives.reverse()) {
try {
const archiveResponse = await fetch(archiveUrl);
if (!archiveResponse.ok) continue;
const archiveData = await archiveResponse.json();
const game = archiveData.games?.find(g => g.url?.includes(gameId));
if (game) {
return {
pgn: game.pgn,
url: game.url,
rated: game.rated
};
}
} catch (error) {}
}
console.error(`Game ${gameId} not found in archives`);
} catch (error) {
console.error(`Error fetching single Chess.com game: ${error}`);
return null;
}
}
static async fetchSingleGame(username, gameId, platform = Platform.CHESSCOM) {
try {
if (platform === Platform.LICHESS) {
return await this.fetchSingleLichessGame(gameId);
} else {
return await this.fetchSingleChessComGame(username, gameId);
}
} catch (error) {
console.error(`Error fetching single game:`, error);
return null;
}
}
static async fetchPlayerAvatar(username, platform = Platform.CHESSCOM) {
try {
if (platform === Platform.LICHESS) {
// Lichess doesn't have profile pictures
return null;
} else {
const response = await fetch(`https://api.chess.com/pub/player/${username}`);
if (response.ok) {
const data = await response.json();
return data.avatar;
}
}
} catch (error) {
console.warn(`Failed to fetch avatar for ${username}:`, error);
}
return null;
}
static async loadGameFromURL() {
const params = new URLSearchParams(document.location.search);
const username = params.get("user");
const gameId = params.get("id");
const platform = params.get("platform") || Platform.CHESSCOM;
if (!username || !gameId) {
return console.info('Missing username or gameId in URL parameters');
}
try {
const game = await this.fetchSingleGame(username, gameId, platform);
if (!game?.pgn?.trim()) {
console.error(`Game not found or has no PGN: ${gameId}`);
return this.loadEmptyGame();
}
console.info(`Successfully loaded game: ${gameId}`);
const pgnData = this.parsePGN(game.pgn);
// Lichess doesn't support profile avatars
const avatarSupport = platform === Platform.CHESSCOM;
const whiteAvatar = avatarSupport ? this.fetchPlayerAvatar(pgnData.white) : undefined;
const blackAvatar = avatarSupport ? this.fetchPlayerAvatar(pgnData.black) : undefined;
return {
username: username,
pgn: game.pgn,
result: pgnData.result || '*',
white: {
name: pgnData.white || 'Unknown',
elo: pgnData.whiteelo || 'Unrated',
avatar: whiteAvatar
},
black: {
name: pgnData.black || 'Unknown',
elo: pgnData.blackelo || 'Unrated',
avatar: blackAvatar
},
}
} catch (error) {
console.error(`Error loading game:`, error);
return null;
}
}
static loadEmptyGame() {
return {
username: 'White',
pgn: '',
result: '*',
white: {
name: 'White',
elo: 'Unrated'
},
black: {
name: 'Black',
elo: 'Unrated'
},
}
}
static loadGameFromPGN(pgn) {
// See if we can pull the username from the pgn
const pgnData = this.parsePGN(pgn);
const white = pgnData.white || 'White';
const black = pgnData.black || 'Black';
const whiteElo = pgnData.whiteelo || 'Unrated';
const blackElo = pgnData.blackelo || 'Unrated';
const username = white || black;
return {
username: username,
pgn: pgn,
result: pgnData.result || '*',
white: {
name: white,
elo: whiteElo
},
black: {
name: black,
elo: blackElo
},
}
}
} |