Add scryfall DB for links and images
This commit is contained in:
parent
e7e7426da1
commit
1727882fb5
6 changed files with 222 additions and 20 deletions
|
@ -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`
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
110
lib/types.ts
110
lib/types.ts
|
@ -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[];
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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>
|
Loading…
Reference in a new issue