feat: enhance card identification from image URLs and introduce cropped art support for cards.
All checks were successful
Build and Deploy / build (push) Successful in 2m7s
All checks were successful
Build and Deploy / build (push) Successful in 2m7s
This commit is contained in:
@@ -82,7 +82,7 @@ define(['./workbox-5a5d9309'], (function (workbox) { 'use strict';
|
||||
"revision": "3ca0b8505b4bec776b69afdba2768812"
|
||||
}, {
|
||||
"url": "index.html",
|
||||
"revision": "0.sortnjvj4s8"
|
||||
"revision": "0.jtdcrepbpeo"
|
||||
}], {});
|
||||
workbox.cleanupOutdatedCaches();
|
||||
workbox.registerRoute(new workbox.NavigationRoute(workbox.createHandlerBoundToURL("index.html"), {
|
||||
|
||||
@@ -62,8 +62,26 @@ export const CardVisual: React.FC<CardVisualProps> = ({
|
||||
let src = card.imageUrl || card.image;
|
||||
|
||||
// Use top-level properties if available (common in DraftCard / Game Card objects)
|
||||
const setCode = card.setCode || card.set || card.definition?.set;
|
||||
const cardId = card.scryfallId || card.definition?.id;
|
||||
let setCode = card.setCode || card.set || card.definition?.set;
|
||||
let cardId = card.scryfallId || card.definition?.id;
|
||||
|
||||
// Fallback: Attempt to extract from Image URL if IDs are missing (Fix for legacy/active games)
|
||||
if ((!setCode || !cardId) && (card.imageUrl || card.image)) {
|
||||
const url = card.imageUrl || card.image;
|
||||
if (typeof url === 'string' && url.includes('/cards/images/')) {
|
||||
const parts = url.split('/cards/images/')[1].split('/');
|
||||
// Expected formats:
|
||||
// 1. [set]/full/[id].jpg
|
||||
// 2. [set]/crop/[id].jpg
|
||||
if (parts.length >= 2) {
|
||||
if (!setCode) setCode = parts[0];
|
||||
if (!cardId) {
|
||||
const filename = parts[parts.length - 1];
|
||||
cardId = filename.replace(/\.(jpg|png)(\?.*)?$/, ''); // strip extension and query
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (viewMode === 'cutout') {
|
||||
// Priority 1: Local Cache (standard naming convention) - PREFERRED BY USER
|
||||
|
||||
@@ -482,7 +482,7 @@ export const DeckBuilderView: React.FC<DeckBuilderViewProps> = ({ initialPool, i
|
||||
landCard = {
|
||||
id: `basic-source-${type}`,
|
||||
name: type,
|
||||
image_uris: { normal: LAND_URL_MAP[type] },
|
||||
image_uris: { normal: LAND_URL_MAP[type], art_crop: LAND_URL_MAP[type] },
|
||||
typeLine: "Basic Land",
|
||||
scryfallId: `generic-${type}`
|
||||
};
|
||||
@@ -721,6 +721,7 @@ export const DeckBuilderView: React.FC<DeckBuilderViewProps> = ({ initialPool, i
|
||||
name: type,
|
||||
isLandSource: true,
|
||||
image: LAND_URL_MAP[type],
|
||||
imageArtCrop: LAND_URL_MAP[type], // Explicitly add fallback crop
|
||||
typeLine: `Basic Land — ${type}`,
|
||||
rarity: 'common',
|
||||
cmc: 0,
|
||||
|
||||
@@ -56,6 +56,7 @@ export interface CardInstance {
|
||||
png?: string;
|
||||
border_crop?: string;
|
||||
};
|
||||
imageArtCrop?: string;
|
||||
}
|
||||
|
||||
export interface PlayerState {
|
||||
|
||||
@@ -65,6 +65,7 @@ export interface CardObject {
|
||||
setCode?: string;
|
||||
controlledSinceTurn: number; // For Summoning Sickness check
|
||||
definition?: any;
|
||||
imageArtCrop?: string;
|
||||
}
|
||||
|
||||
export interface PlayerState {
|
||||
|
||||
@@ -674,16 +674,38 @@ io.on('connection', (socket) => {
|
||||
const game = gameManager.createGame(room.id, updatedRoom.players);
|
||||
if (decks) {
|
||||
Object.entries(decks).forEach(([pid, deck]: [string, any]) => {
|
||||
|
||||
// @ts-ignore
|
||||
deck.forEach(card => {
|
||||
// Robustly resolve setCode / scryfallId
|
||||
let setCode = card.setCode || card.set || card.definition?.set;
|
||||
let scryfallId = card.scryfallId || card.id || card.definition?.id;
|
||||
|
||||
// Fallback: Extract from Image URL if missing
|
||||
if ((!setCode || !scryfallId) && card.imageUrl && card.imageUrl.includes('/cards/images/')) {
|
||||
const parts = card.imageUrl.split('/cards/images/');
|
||||
if (parts[1]) {
|
||||
const pathParts = parts[1].split('/');
|
||||
// Format: [setCode]/[full|crop]/[id].jpg OR [setCode]/[id].jpg
|
||||
if (!setCode) setCode = pathParts[0];
|
||||
|
||||
if (!scryfallId) {
|
||||
const filename = pathParts[pathParts.length - 1]; // uuid.jpg
|
||||
scryfallId = filename.replace(/\.(jpg|png)$/, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gameManager.addCardToGame(room.id, {
|
||||
ownerId: pid,
|
||||
controllerId: pid,
|
||||
oracleId: card.oracle_id || card.id || card.definition?.oracle_id,
|
||||
scryfallId: card.scryfallId || card.id || card.definition?.id,
|
||||
setCode: card.setCode || card.set || card.definition?.set,
|
||||
scryfallId: scryfallId,
|
||||
setCode: setCode,
|
||||
name: card.name,
|
||||
imageUrl: card.image_uris?.normal || card.image_uris?.large || card.imageUrl || "",
|
||||
// IMPORTANT: If we have setCode+scryfallId, we clear imageUrl so client uses local cache logic
|
||||
imageUrl: (setCode && scryfallId) ? "" : (card.image_uris?.normal || card.image_uris?.large || card.imageUrl || ""),
|
||||
imageArtCrop: card.image_uris?.art_crop || card.image_uris?.crop || card.imageArtCrop || "",
|
||||
zone: 'library',
|
||||
typeLine: card.typeLine || card.type_line || '',
|
||||
oracleText: card.oracleText || card.oracle_text || '',
|
||||
@@ -800,14 +822,33 @@ io.on('connection', (socket) => {
|
||||
[{ p: p1, d: deck1 }, { p: p2, d: deck2 }].forEach(({ p, d }) => {
|
||||
if (d) {
|
||||
d.forEach((card: any) => {
|
||||
// Robustly resolve setCode / scryfallId
|
||||
let setCode = card.setCode || card.set || card.definition?.set;
|
||||
let scryfallId = card.scryfallId || card.id || card.definition?.id;
|
||||
|
||||
// Fallback: Extract from Image URL if missing
|
||||
if ((!setCode || !scryfallId) && card.imageUrl && card.imageUrl.includes('/cards/images/')) {
|
||||
const parts = card.imageUrl.split('/cards/images/');
|
||||
if (parts[1]) {
|
||||
const pathParts = parts[1].split('/');
|
||||
if (!setCode) setCode = pathParts[0];
|
||||
if (!scryfallId) {
|
||||
const filename = pathParts[pathParts.length - 1]; // uuid.jpg
|
||||
scryfallId = filename.replace(/\.(jpg|png)$/, '');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gameManager.addCardToGame(matchId, {
|
||||
ownerId: p.id,
|
||||
controllerId: p.id,
|
||||
oracleId: card.oracle_id || card.id || card.definition?.oracle_id,
|
||||
scryfallId: card.scryfallId || card.id || card.definition?.id,
|
||||
setCode: card.setCode || card.set || card.definition?.set,
|
||||
scryfallId: scryfallId,
|
||||
setCode: setCode,
|
||||
name: card.name,
|
||||
imageUrl: "", // Optimisation: Client hydrates from cache
|
||||
// IMPORTANT: If we have setCode+scryfallId, we clear imageUrl so client uses local cache logic
|
||||
imageUrl: (setCode && scryfallId) ? "" : (card.image_uris?.normal || card.image_uris?.large || card.imageUrl || ""),
|
||||
imageArtCrop: card.image_uris?.art_crop || card.image_uris?.crop || card.imageArtCrop || "",
|
||||
zone: 'library',
|
||||
typeLine: card.typeLine || card.type_line || '',
|
||||
oracleText: card.oracleText || card.oracle_text || '',
|
||||
|
||||
Reference in New Issue
Block a user