Add scryfall DB for links and images

This commit is contained in:
Hamcha 2019-11-22 15:58:55 +01:00
parent e7e7426da1
commit 1727882fb5
Signed by: hamcha
GPG key ID: 44AD3571EB09A39E
6 changed files with 222 additions and 20 deletions

View file

@ -29,7 +29,8 @@ export ACCESS_SECRET=<Access token secret>
To use the rare finder, you'll need to build the rare db To use the rare finder, you'll need to build the rare db
1. Download this: https://www.mtgjson.com/files/AllPrintings.json 1. Download this: https://www.mtgjson.com/files/AllPrintings.json
2. Run `yarn import-db` 2. Also download this: https://archive.scryfall.com/json/scryfall-default-cards.json
3. Run `yarn build-db`
It will create a file called `mcmCards.json` It will create a file called `mcmCards.json`

View file

@ -1,31 +1,56 @@
import { existsSync } from "fs"; import { existsSync } from "fs";
import { asyncLoadJSON, asyncSaveJSON } from "../lib"; import { asyncLoadJSON, asyncSaveJSON, MCMCard, MCMDB, ScryfallCard, ScryfallDB } from "../lib";
interface JSONCard {
mcmId: number;
set: string;
}
interface JSONSet { interface JSONSet {
cards: JSONCard[]; cards: MCMCard[];
} }
type JSONDB = Record<string, JSONSet>; type JSONDB = Record<string, JSONSet>;
const mtgjsonFile = "AllPrintings.json";
const scryfallJSONFile = "scryfall-default-cards.json";
async function run() { async function run() {
if (!existsSync("AllPrintings.json")) { if (!existsSync(mtgjsonFile)) {
console.error( console.error(
"AllPrintings.json not found. Download from: https://www.mtgjson.com/files/AllPrintings.json" mtgjsonFile +
" not found. Download from: https://www.mtgjson.com/files/AllPrintings.json"
); );
process.exit(1); process.exit(1);
} }
console.info("Loading AllPrintings.json"); if (!existsSync(scryfallJSONFile)) {
const db = await asyncLoadJSON<JSONDB>("AllPrintings.json"); console.error(
let acc = {}; scryfallJSONFile +
for (const set in db) { " not found. Download from: https://archive.scryfall.com/json/scryfall-default-cards.json"
db[set].cards.forEach(c => { );
process.exit(1);
}
console.info("Loading " + mtgjsonFile);
const mtgdb = await asyncLoadJSON<JSONDB>(mtgjsonFile);
console.info("Loading " + scryfallJSONFile);
const scrydb = await asyncLoadJSON<ScryfallDB>(scryfallJSONFile);
let scrycards: Record<string, ScryfallCard> = {};
scrydb.forEach(c => (scrycards[c.id] = c));
let acc: MCMDB = {};
for (const set in mtgdb) {
mtgdb[set].cards.forEach(c => {
c.set = set; c.set = set;
if (c.scryfallId in scrycards) {
c.scryfallUrl = scrycards[c.scryfallId].scryfall_uri;
if ("image_uris" in scrycards[c.scryfallId]) {
c.scryfallImageUrl = scrycards[c.scryfallId].image_uris.normal;
} else if (
"card_faces" in scrycards[c.scryfallId] &&
"image_uris" in scrycards[c.scryfallId].card_faces[0]
) {
c.scryfallImageUrl =
scrycards[c.scryfallId].card_faces[0].image_uris.normal;
} else {
console.log(scrycards[c.scryfallId]);
}
}
// Search for scryfall card
acc[c.mcmId] = c; acc[c.mcmId] = c;
}); });
} }

View file

@ -62,6 +62,8 @@ export interface MCMCard {
scryfallId: string; scryfallId: string;
scryfallIllustrationId: string; scryfallIllustrationId: string;
scryfallOracleId: string; scryfallOracleId: string;
scryfallImageUrl: string;
scryfallUrl: string;
subtypes: string[]; subtypes: string[];
supertypes: any[]; supertypes: any[];
tcgplayerProductId: number; tcgplayerProductId: number;
@ -86,12 +88,15 @@ export interface LeanMCMCard {
name: string; name: string;
rarity: string; rarity: string;
scryfallId: string; scryfallId: string;
scryfallImageUrl: string;
scryfallUrl: string;
subtypes: string[]; subtypes: string[];
supertypes: any[]; supertypes: any[];
text: string; text: string;
type: string; type: string;
types: string[]; types: string[];
set: string; set: string;
number: string;
} }
export interface Language { export interface Language {
@ -133,3 +138,108 @@ export interface LeanArticle {
} }
export type CardItem = LeanMCMCard & LeanArticle; export type CardItem = LeanMCMCard & LeanArticle;
export interface ImageUris {
small: string;
normal: string;
large: string;
png: string;
art_crop: string;
border_crop: string;
}
export interface Legalities {
standard: string;
future: string;
historic: string;
pioneer: string;
modern: string;
legacy: string;
pauper: string;
vintage: string;
penny: string;
commander: string;
brawl: string;
duel: string;
oldschool: string;
}
export interface RelatedUris {
tcgplayer_decks: string;
edhrec: string;
mtgtop8: string;
}
export interface CardFace {
object: string;
name: string;
mana_cost: string;
type_line: string;
oracle_text: string;
colors: string[];
power: string;
toughness: string;
artist: string;
artist_id: string;
illustration_id: string;
image_uris: ImageUris;
color_indicator: string[];
loyalty: string;
}
export interface ScryfallCard {
object: string;
id: string;
oracle_id: string;
multiverse_ids: any[];
tcgplayer_id: number;
name: string;
lang: string;
released_at: string;
uri: string;
scryfall_uri: string;
layout: string;
highres_image: boolean;
image_uris: ImageUris;
card_faces?: CardFace[];
mana_cost: string;
cmc: number;
type_line: string;
oracle_text: string;
colors: any[];
color_identity: string[];
legalities: Legalities;
games: string[];
reserved: boolean;
foil: boolean;
nonfoil: boolean;
oversized: boolean;
promo: boolean;
reprint: boolean;
variation: boolean;
set: string;
set_name: string;
set_type: string;
set_uri: string;
set_search_uri: string;
scryfall_set_uri: string;
rulings_uri: string;
prints_search_uri: string;
collector_number: string;
digital: boolean;
rarity: string;
card_back_id: string;
artist: string;
artist_ids: string[];
illustration_id: string;
border_color: string;
frame: string;
full_art: boolean;
textless: boolean;
booster: boolean;
story_spotlight: boolean;
edhrec_rank: number;
related_uris: RelatedUris;
}
export type ScryfallDB = ScryfallCard[];

View file

@ -36,6 +36,7 @@ export function leanCard(card: MCMCard): LeanMCMCard {
manaCost: card.manaCost, manaCost: card.manaCost,
mcmId: card.mcmId, mcmId: card.mcmId,
name: card.name, name: card.name,
number: card.number,
rarity: card.rarity, rarity: card.rarity,
scryfallId: card.scryfallId, scryfallId: card.scryfallId,
subtypes: card.subtypes, subtypes: card.subtypes,
@ -43,7 +44,9 @@ export function leanCard(card: MCMCard): LeanMCMCard {
text: card.text, text: card.text,
type: card.type, type: card.type,
types: card.types, types: card.types,
set: card.set set: card.set,
scryfallImageUrl: card.scryfallImageUrl,
scryfallUrl: card.scryfallUrl
}; };
} }

View file

@ -7,7 +7,7 @@
"license": "MIT", "license": "MIT",
"scripts": { "scripts": {
"fetch": "ts-node cmd/fetch.ts", "fetch": "ts-node cmd/fetch.ts",
"init-db": "ts-node cmd/convert.ts", "build-db": "ts-node cmd/convert.ts",
"find": "ts-node cmd/find.ts", "find": "ts-node cmd/find.ts",
"cube": "ts-node cmd/cube.ts", "cube": "ts-node cmd/cube.ts",
"cube-watch": "ts-node cmd/cube-dev.ts" "cube-watch": "ts-node cmd/cube-dev.ts"

View file

@ -33,11 +33,11 @@
.color-section { .color-section {
flex: 1; flex: 1;
margin: 5px;
} }
.color-section header { .color-section header {
text-align: center; text-align: center;
padding: 5px;
} }
.color-W header.color-header { .color-W header.color-header {
@ -77,7 +77,7 @@
border: 1px solid #aaa; border: 1px solid #aaa;
border-radius: 5px; border-radius: 5px;
font-size: 10pt; font-size: 10pt;
margin: 5px 0; margin: 10px 5px;
} }
.type-section header.type-header { .type-section header.type-header {
@ -87,6 +87,17 @@
padding: 5px; padding: 5px;
} }
.cards {
position: relative;
}
.cmc-index {
position: absolute;
opacity: 0.1;
right: 5px;
font-size: 15pt;
}
ul { ul {
margin: 0; margin: 0;
padding: 0; padding: 0;
@ -95,12 +106,35 @@
} }
ul li { ul li {
display: flex;
}
ul li a {
padding: 5px; padding: 5px;
flex: 1;
color: inherit;
text-decoration: none;
}
ul li a:hover {
background-color: #eee;
} }
ul:last-child { ul:last-child {
border-bottom: none; border-bottom: none;
} }
.img-overlay {
position: fixed;
top: 0;
left: 0;
pointer-events: none;
}
.img-overlay img {
width: auto;
height: 300px;
}
</style> </style>
</head> </head>
@ -123,9 +157,15 @@
(<%=utils.deepSum(typecards)%>)</header> (<%=utils.deepSum(typecards)%>)</header>
<section class="cards"> <section class="cards">
<% Object.entries(typecards).forEach(([cmc, finalcards]) => { %> <% Object.entries(typecards).forEach(([cmc, finalcards]) => { %>
<div class="cmc-index"><%=cmc%></div>
<ul> <ul>
<% finalcards.forEach(card => { %> <% finalcards.forEach(card => { %>
<li><%= card.name %></li> <li data-set="<%=card.set%>" data-num="<%=card.number%>">
<a target="_blank" data-image="<%=card.scryfallImageUrl%>"
href="<%= card.scryfallUrl %>">
<%= card.name %>
</a>
</li>
<% }); %> <% }); %>
</ul> </ul>
<% }); %> <% }); %>
@ -136,6 +176,29 @@
<% }); %> <% }); %>
</section> </section>
</main> </main>
<script>
const overlay = document.createElement("div");
const img = document.createElement("img");
overlay.appendChild(img);
overlay.classList.add("img-overlay");
document.body.appendChild(overlay);
overlay.style.display = "none";
Array.prototype.slice.call(document.querySelectorAll("li a[data-image]"), 0).forEach(el => {
el.addEventListener("mouseenter", ev => {
overlay.style.display = "block";
img.src = el.dataset.image;
});
el.addEventListener("mouseleave", ev => {
overlay.style.display = "none";
});
el.addEventListener("mousemove", ev => {
overlay.style.left = `${ev.clientX}px`;
overlay.style.top = `${ev.clientY - 320}px`;
console.log(ev);
});
});
</script>
</body> </body>
</html> </html>