diff --git a/bulk-import-results-2025-07-24T17-20-21-039Z.json b/bulk-import-results-2025-07-24T17-20-21-039Z.json new file mode 100644 index 0000000..05e0924 --- /dev/null +++ b/bulk-import-results-2025-07-24T17-20-21-039Z.json @@ -0,0 +1,1301 @@ +{ + "mtg": { + "total": 103, + "successful": 101, + "failed": 2, + "sets": [ + { + "code": "neo", + "name": "Kamigawa: Neon Dynasty", + "success": true, + "importedCount": 0 + }, + { + "code": "vow", + "name": "Innistrad: Crimson Vow", + "success": true, + "importedCount": 0 + }, + { + "code": "mid", + "name": "Innistrad: Midnight Hunt", + "success": false, + "error": "Import failed" + }, + { + "code": "afr", + "name": "Adventures in the Forgotten Realms", + "success": true, + "importedCount": 0 + }, + { + "code": "stx", + "name": "Strixhaven: School of Mages", + "success": true, + "importedCount": 0 + }, + { + "code": "khm", + "name": "Kaldheim", + "success": true, + "importedCount": 0 + }, + { + "code": "znr", + "name": "Zendikar Rising", + "success": true, + "importedCount": 0 + }, + { + "code": "iko", + "name": "Ikoria: Lair of Behemoths", + "success": true, + "importedCount": 0 + }, + { + "code": "thb", + "name": "Theros Beyond Death", + "success": true, + "importedCount": 0 + }, + { + "code": "eld", + "name": "Throne of Eldraine", + "success": true, + "importedCount": 0 + }, + { + "code": "m20", + "name": "Core Set 2020", + "success": true, + "importedCount": 0 + }, + { + "code": "war", + "name": "War of the Spark", + "success": true, + "importedCount": 0 + }, + { + "code": "rna", + "name": "Ravnica Allegiance", + "success": true, + "importedCount": 0 + }, + { + "code": "grn", + "name": "Guilds of Ravnica", + "success": true, + "importedCount": 0 + }, + { + "code": "m19", + "name": "Core Set 2019", + "success": true, + "importedCount": 0 + }, + { + "code": "dom", + "name": "Dominaria", + "success": true, + "importedCount": 0 + }, + { + "code": "rix", + "name": "Rivals of Ixalan", + "success": true, + "importedCount": 0 + }, + { + "code": "xln", + "name": "Ixalan", + "success": true, + "importedCount": 0 + }, + { + "code": "hou", + "name": "Hour of Devastation", + "success": true, + "importedCount": 0 + }, + { + "code": "akh", + "name": "Amonkhet", + "success": true, + "importedCount": 0 + }, + { + "code": "aer", + "name": "Aether Revolt", + "success": true, + "importedCount": 0 + }, + { + "code": "kld", + "name": "Kaladesh", + "success": true, + "importedCount": 0 + }, + { + "code": "emn", + "name": "Eldritch Moon", + "success": true, + "importedCount": 0 + }, + { + "code": "soi", + "name": "Shadows over Innistrad", + "success": true, + "importedCount": 0 + }, + { + "code": "ogw", + "name": "Oath of the Gatewatch", + "success": true, + "importedCount": 0 + }, + { + "code": "bfz", + "name": "Battle for Zendikar", + "success": true, + "importedCount": 0 + }, + { + "code": "ori", + "name": "Magic Origins", + "success": true, + "importedCount": 0 + }, + { + "code": "dtk", + "name": "Dragons of Tarkir", + "success": true, + "importedCount": 0 + }, + { + "code": "frf", + "name": "Fate Reforged", + "success": true, + "importedCount": 0 + }, + { + "code": "ktk", + "name": "Khans of Tarkir", + "success": true, + "importedCount": 0 + }, + { + "code": "m15", + "name": "Magic 2015", + "success": true, + "importedCount": 0 + }, + { + "code": "jou", + "name": "Journey into Nyx", + "success": true, + "importedCount": 0 + }, + { + "code": "bng", + "name": "Born of the Gods", + "success": true, + "importedCount": 0 + }, + { + "code": "ths", + "name": "Theros", + "success": true, + "importedCount": 0 + }, + { + "code": "m14", + "name": "Magic 2014", + "success": true, + "importedCount": 0 + }, + { + "code": "dgm", + "name": "Dragon's Maze", + "success": true, + "importedCount": 0 + }, + { + "code": "gtc", + "name": "Gatecrash", + "success": true, + "importedCount": 0 + }, + { + "code": "rtr", + "name": "Return to Ravnica", + "success": true, + "importedCount": 0 + }, + { + "code": "m13", + "name": "Magic 2013", + "success": true, + "importedCount": 0 + }, + { + "code": "avr", + "name": "Avacyn Restored", + "success": true, + "importedCount": 0 + }, + { + "code": "dka", + "name": "Dark Ascension", + "success": true, + "importedCount": 0 + }, + { + "code": "isd", + "name": "Innistrad", + "success": true, + "importedCount": 0 + }, + { + "code": "m12", + "name": "Magic 2012", + "success": true, + "importedCount": 0 + }, + { + "code": "nph", + "name": "New Phyrexia", + "success": true, + "importedCount": 0 + }, + { + "code": "mbs", + "name": "Mirrodin Besieged", + "success": true, + "importedCount": 0 + }, + { + "code": "som", + "name": "Scars of Mirrodin", + "success": true, + "importedCount": 0 + }, + { + "code": "m11", + "name": "Magic 2011", + "success": true, + "importedCount": 0 + }, + { + "code": "roe", + "name": "Rise of the Eldrazi", + "success": true, + "importedCount": 0 + }, + { + "code": "wwk", + "name": "Worldwake", + "success": true, + "importedCount": 0 + }, + { + "code": "zen", + "name": "Zendikar", + "success": true, + "importedCount": 0 + }, + { + "code": "m10", + "name": "Magic 2010", + "success": true, + "importedCount": 0 + }, + { + "code": "arb", + "name": "Alara Reborn", + "success": true, + "importedCount": 0 + }, + { + "code": "con", + "name": "Conflux", + "success": true, + "importedCount": 0 + }, + { + "code": "ala", + "name": "Shards of Alara", + "success": true, + "importedCount": 0 + }, + { + "code": "eve", + "name": "Eventide", + "success": true, + "importedCount": 0 + }, + { + "code": "shm", + "name": "Shadowmoor", + "success": true, + "importedCount": 0 + }, + { + "code": "mor", + "name": "Morningtide", + "success": true, + "importedCount": 0 + }, + { + "code": "lrw", + "name": "Lorwyn", + "success": true, + "importedCount": 0 + }, + { + "code": "fut", + "name": "Future Sight", + "success": true, + "importedCount": 0 + }, + { + "code": "plc", + "name": "Planar Chaos", + "success": true, + "importedCount": 0 + }, + { + "code": "tsp", + "name": "Time Spiral", + "success": true, + "importedCount": 0 + }, + { + "code": "csp", + "name": "Coldsnap", + "success": true, + "importedCount": 0 + }, + { + "code": "dis", + "name": "Dissension", + "success": true, + "importedCount": 0 + }, + { + "code": "gpt", + "name": "Guildpact", + "success": true, + "importedCount": 0 + }, + { + "code": "rav", + "name": "Ravnica: City of Guilds", + "success": true, + "importedCount": 0 + }, + { + "code": "9ed", + "name": "Ninth Edition", + "success": true, + "importedCount": 0 + }, + { + "code": "sok", + "name": "Saviors of Kamigawa", + "success": true, + "importedCount": 0 + }, + { + "code": "bet", + "name": "Betrayers of Kamigawa", + "success": false, + "error": "Import failed" + }, + { + "code": "chk", + "name": "Champions of Kamigawa", + "success": true, + "importedCount": 0 + }, + { + "code": "5dn", + "name": "Fifth Dawn", + "success": true, + "importedCount": 0 + }, + { + "code": "dst", + "name": "Darksteel", + "success": true, + "importedCount": 0 + }, + { + "code": "mrd", + "name": "Mirrodin", + "success": true, + "importedCount": 0 + }, + { + "code": "8ed", + "name": "Eighth Edition", + "success": true, + "importedCount": 0 + }, + { + "code": "scg", + "name": "Scourge", + "success": true, + "importedCount": 0 + }, + { + "code": "lgn", + "name": "Legions", + "success": true, + "importedCount": 0 + }, + { + "code": "ons", + "name": "Onslaught", + "success": true, + "importedCount": 0 + }, + { + "code": "7ed", + "name": "Seventh Edition", + "success": true, + "importedCount": 0 + }, + { + "code": "apc", + "name": "Apocalypse", + "success": true, + "importedCount": 0 + }, + { + "code": "pls", + "name": "Planeshift", + "success": true, + "importedCount": 0 + }, + { + "code": "inv", + "name": "Invasion", + "success": true, + "importedCount": 0 + }, + { + "code": "6ed", + "name": "Classic Sixth Edition", + "success": true, + "importedCount": 0 + }, + { + "code": "pcy", + "name": "Prophecy", + "success": true, + "importedCount": 0 + }, + { + "code": "nem", + "name": "Nemesis", + "success": true, + "importedCount": 0 + }, + { + "code": "mmq", + "name": "Mercadian Masques", + "success": true, + "importedCount": 0 + }, + { + "code": "uds", + "name": "Urza's Destiny", + "success": true, + "importedCount": 0 + }, + { + "code": "ulg", + "name": "Urza's Legacy", + "success": true, + "importedCount": 0 + }, + { + "code": "usg", + "name": "Urza's Saga", + "success": true, + "importedCount": 0 + }, + { + "code": "5ed", + "name": "Fifth Edition", + "success": true, + "importedCount": 0 + }, + { + "code": "exo", + "name": "Exodus", + "success": true, + "importedCount": 0 + }, + { + "code": "sth", + "name": "Stronghold", + "success": true, + "importedCount": 0 + }, + { + "code": "tmp", + "name": "Tempest", + "success": true, + "importedCount": 0 + }, + { + "code": "4ed", + "name": "Fourth Edition", + "success": true, + "importedCount": 0 + }, + { + "code": "vis", + "name": "Visions", + "success": true, + "importedCount": 0 + }, + { + "code": "all", + "name": "Alliances", + "success": true, + "importedCount": 0 + }, + { + "code": "ice", + "name": "Ice Age", + "success": true, + "importedCount": 0 + }, + { + "code": "3ed", + "name": "Revised Edition", + "success": true, + "importedCount": 0 + }, + { + "code": "fem", + "name": "Fallen Empires", + "success": true, + "importedCount": 0 + }, + { + "code": "drk", + "name": "The Dark", + "success": true, + "importedCount": 0 + }, + { + "code": "leg", + "name": "Legends", + "success": true, + "importedCount": 0 + }, + { + "code": "atq", + "name": "Antiquities", + "success": true, + "importedCount": 0 + }, + { + "code": "arn", + "name": "Arabian Nights", + "success": true, + "importedCount": 0 + }, + { + "code": "leb", + "name": "Limited Edition Beta", + "success": true, + "importedCount": 0 + }, + { + "code": "lea", + "name": "Limited Edition Alpha", + "success": true, + "importedCount": 0 + } + ] + }, + "pokemon": { + "total": 107, + "successful": 94, + "failed": 13, + "sets": [ + { + "code": "base1", + "name": "Base Set", + "success": true, + "importedCount": 102 + }, + { + "code": "base2", + "name": "Base Set 2", + "success": false, + "error": "Import failed" + }, + { + "code": "base3", + "name": "Base Set 3", + "success": true, + "importedCount": 0 + }, + { + "code": "gym1", + "name": "Gym Heroes", + "success": false, + "error": "Import failed" + }, + { + "code": "gym2", + "name": "Gym Challenge", + "success": true, + "importedCount": 132 + }, + { + "code": "neo1", + "name": "Neo Genesis", + "success": true, + "importedCount": 111 + }, + { + "code": "neo2", + "name": "Neo Discovery", + "success": true, + "importedCount": 75 + }, + { + "code": "neo3", + "name": "Neo Revelation", + "success": true, + "importedCount": 66 + }, + { + "code": "neo4", + "name": "Neo Destiny", + "success": true, + "importedCount": 113 + }, + { + "code": "ecard1", + "name": "Expedition Base Set", + "success": true, + "importedCount": 165 + }, + { + "code": "ecard2", + "name": "Aquapolis", + "success": true, + "importedCount": 182 + }, + { + "code": "ecard3", + "name": "Skyridge", + "success": true, + "importedCount": 182 + }, + { + "code": "ex1", + "name": "Ruby & Sapphire", + "success": true, + "importedCount": 109 + }, + { + "code": "ex2", + "name": "Sandstorm", + "success": true, + "importedCount": 100 + }, + { + "code": "ex3", + "name": "Dragon", + "success": true, + "importedCount": 0 + }, + { + "code": "ex4", + "name": "Team Magma vs Team Aqua", + "success": true, + "importedCount": 0 + }, + { + "code": "ex5", + "name": "Hidden Legends", + "success": true, + "importedCount": 0 + }, + { + "code": "ex6", + "name": "FireRed & LeafGreen", + "success": true, + "importedCount": 116 + }, + { + "code": "ex7", + "name": "Team Rocket Returns", + "success": true, + "importedCount": 0 + }, + { + "code": "ex8", + "name": "Deoxys", + "success": true, + "importedCount": 0 + }, + { + "code": "ex9", + "name": "Emerald", + "success": true, + "importedCount": 0 + }, + { + "code": "ex10", + "name": "Unseen Forces", + "success": true, + "importedCount": 0 + }, + { + "code": "ex11", + "name": "Delta Species", + "success": true, + "importedCount": 0 + }, + { + "code": "ex12", + "name": "Legend Maker", + "success": true, + "importedCount": 0 + }, + { + "code": "ex13", + "name": "Holon Phantoms", + "success": true, + "importedCount": 0 + }, + { + "code": "ex14", + "name": "Crystal Guardians", + "success": true, + "importedCount": 0 + }, + { + "code": "ex15", + "name": "Dragon Frontiers", + "success": true, + "importedCount": 101 + }, + { + "code": "ex16", + "name": "Power Keepers", + "success": true, + "importedCount": 0 + }, + { + "code": "dpp1", + "name": "Diamond & Pearl", + "success": true, + "importedCount": 0 + }, + { + "code": "dpp2", + "name": "Mysterious Treasures", + "success": true, + "importedCount": 0 + }, + { + "code": "dpp3", + "name": "Secret Wonders", + "success": true, + "importedCount": 0 + }, + { + "code": "dpp4", + "name": "Great Encounters", + "success": true, + "importedCount": 0 + }, + { + "code": "dpp5", + "name": "Majestic Dawn", + "success": true, + "importedCount": 0 + }, + { + "code": "dpp6", + "name": "Legends Awakened", + "success": true, + "importedCount": 0 + }, + { + "code": "dpp7", + "name": "Stormfront", + "success": true, + "importedCount": 0 + }, + { + "code": "dpp8", + "name": "Platinum", + "success": true, + "importedCount": 0 + }, + { + "code": "dpp9", + "name": "Rising Rivals", + "success": true, + "importedCount": 0 + }, + { + "code": "dpp10", + "name": "Supreme Victors", + "success": true, + "importedCount": 0 + }, + { + "code": "dpp11", + "name": "Arceus", + "success": false, + "error": "Import failed" + }, + { + "code": "hgss1", + "name": "HeartGold & SoulSilver", + "success": false, + "error": "Import failed" + }, + { + "code": "hgss2", + "name": "Unleashed", + "success": true, + "importedCount": 96 + }, + { + "code": "hgss3", + "name": "Undaunted", + "success": true, + "importedCount": 91 + }, + { + "code": "hgss4", + "name": "Triumphant", + "success": true, + "importedCount": 0 + }, + { + "code": "col1", + "name": "Call of Legends", + "success": true, + "importedCount": 0 + }, + { + "code": "bw1", + "name": "Black & White", + "success": true, + "importedCount": 0 + }, + { + "code": "bw2", + "name": "Emerging Powers", + "success": true, + "importedCount": 98 + }, + { + "code": "bw3", + "name": "Noble Victories", + "success": true, + "importedCount": 0 + }, + { + "code": "bw4", + "name": "Next Destinies", + "success": true, + "importedCount": 0 + }, + { + "code": "bw5", + "name": "Dark Explorers", + "success": true, + "importedCount": 111 + }, + { + "code": "bw6", + "name": "Dragons Exalted", + "success": true, + "importedCount": 128 + }, + { + "code": "bw7", + "name": "Boundaries Crossed", + "success": true, + "importedCount": 153 + }, + { + "code": "bw8", + "name": "Plasma Storm", + "success": false, + "error": "Import failed" + }, + { + "code": "bw9", + "name": "Plasma Freeze", + "success": false, + "error": "Import failed" + }, + { + "code": "bw10", + "name": "Plasma Blast", + "success": false, + "error": "Import failed" + }, + { + "code": "bw11", + "name": "Legendary Treasures", + "success": true, + "importedCount": 0 + }, + { + "code": "xy1", + "name": "XY", + "success": true, + "importedCount": 0 + }, + { + "code": "xy2", + "name": "Flashfire", + "success": false, + "error": "Import failed" + }, + { + "code": "xy3", + "name": "Furious Fists", + "success": false, + "error": "Import failed" + }, + { + "code": "xy4", + "name": "Phantom Forces", + "success": false, + "error": "Import failed" + }, + { + "code": "xy5", + "name": "Primal Clash", + "success": true, + "importedCount": 164 + }, + { + "code": "xy6", + "name": "Double Crisis", + "success": true, + "importedCount": 112 + }, + { + "code": "xy7", + "name": "Roaring Skies", + "success": false, + "error": "Import failed" + }, + { + "code": "xy8", + "name": "Ancient Origins", + "success": false, + "error": "Import failed" + }, + { + "code": "xy9", + "name": "BREAKthrough", + "success": true, + "importedCount": 0 + }, + { + "code": "xy10", + "name": "BREAKpoint", + "success": false, + "error": "Import failed" + }, + { + "code": "xy11", + "name": "Fates Collide", + "success": true, + "importedCount": 0 + }, + { + "code": "xy12", + "name": "Steam Siege", + "success": true, + "importedCount": 0 + }, + { + "code": "xy12", + "name": "Evolutions", + "success": true, + "importedCount": 0 + }, + { + "code": "sm1", + "name": "Sun & Moon", + "success": true, + "importedCount": 173 + }, + { + "code": "sm2", + "name": "Guardians Rising", + "success": true, + "importedCount": 180 + }, + { + "code": "sm3", + "name": "Burning Shadows", + "success": true, + "importedCount": 0 + }, + { + "code": "sm4", + "name": "Crimson Invasion", + "success": true, + "importedCount": 0 + }, + { + "code": "sm5", + "name": "Ultra Prism", + "success": true, + "importedCount": 178 + }, + { + "code": "sm6", + "name": "Forbidden Light", + "success": true, + "importedCount": 150 + }, + { + "code": "sm7", + "name": "Celestial Storm", + "success": true, + "importedCount": 0 + }, + { + "code": "sm8", + "name": "Dragon Majesty", + "success": true, + "importedCount": 0 + }, + { + "code": "sm9", + "name": "Lost Thunder", + "success": true, + "importedCount": 0 + }, + { + "code": "sm10", + "name": "Team Up", + "success": true, + "importedCount": 0 + }, + { + "code": "sm11", + "name": "Detective Pikachu", + "success": true, + "importedCount": 250 + }, + { + "code": "sm12", + "name": "Unbroken Bonds", + "success": true, + "importedCount": 250 + }, + { + "code": "sm115", + "name": "Unified Minds", + "success": true, + "importedCount": 69 + }, + { + "code": "sm116", + "name": "Hidden Fates", + "success": true, + "importedCount": 0 + }, + { + "code": "sm117", + "name": "Cosmic Eclipse", + "success": true, + "importedCount": 0 + }, + { + "code": "swsh1", + "name": "Sword & Shield", + "success": true, + "importedCount": 216 + }, + { + "code": "swsh2", + "name": "Rebel Clash", + "success": true, + "importedCount": 0 + }, + { + "code": "swsh3", + "name": "Darkness Ablaze", + "success": true, + "importedCount": 0 + }, + { + "code": "swsh4", + "name": "Champions Path", + "success": true, + "importedCount": 0 + }, + { + "code": "swsh5", + "name": "Vivid Voltage", + "success": true, + "importedCount": 183 + }, + { + "code": "swsh6", + "name": "Shining Fates", + "success": true, + "importedCount": 0 + }, + { + "code": "swsh7", + "name": "Battle Styles", + "success": true, + "importedCount": 0 + }, + { + "code": "swsh8", + "name": "Chilling Reign", + "success": true, + "importedCount": 0 + }, + { + "code": "swsh9", + "name": "Evolving Skies", + "success": true, + "importedCount": 0 + }, + { + "code": "swsh10", + "name": "Celebrations", + "success": true, + "importedCount": 216 + }, + { + "code": "swsh11", + "name": "Fusion Strike", + "success": true, + "importedCount": 217 + }, + { + "code": "swsh12", + "name": "Brilliant Stars", + "success": true, + "importedCount": 0 + }, + { + "code": "swsh12", + "name": "Astral Radiance", + "success": true, + "importedCount": 0 + }, + { + "code": "swsh12", + "name": "Pokemon GO", + "success": true, + "importedCount": 0 + }, + { + "code": "swsh12", + "name": "Lost Origin", + "success": true, + "importedCount": 0 + }, + { + "code": "swsh12", + "name": "Silver Tempest", + "success": true, + "importedCount": 0 + }, + { + "code": "swsh12", + "name": "Crown Zenith", + "success": true, + "importedCount": 0 + }, + { + "code": "sv1", + "name": "Scarlet & Violet", + "success": true, + "importedCount": 250 + }, + { + "code": "sv2", + "name": "Paldea Evolved", + "success": true, + "importedCount": 250 + }, + { + "code": "sv3", + "name": "Obsidian Flames", + "success": true, + "importedCount": 230 + }, + { + "code": "sv4", + "name": "151", + "success": true, + "importedCount": 250 + }, + { + "code": "sv5", + "name": "Paradox Rift", + "success": true, + "importedCount": 218 + }, + { + "code": "sv6", + "name": "Paldean Fates", + "success": true, + "importedCount": 226 + }, + { + "code": "sv7", + "name": "Temporal Forces", + "success": true, + "importedCount": 0 + } + ] + }, + "lorcana": { + "total": 3, + "successful": 3, + "failed": 0, + "sets": [ + { + "code": "tfc", + "name": "The First Chapter", + "success": true, + "importedCount": 1 + }, + { + "code": "rotf", + "name": "Rise of the Floodborn", + "success": true, + "importedCount": 1 + }, + { + "code": "ink", + "name": "Into the Inklands", + "success": true, + "importedCount": 1 + } + ] + } +} \ No newline at end of file diff --git a/pages/api/cards/import-lorcana.js b/pages/api/cards/import-lorcana.js index 8960310..84a5fcb 100644 --- a/pages/api/cards/import-lorcana.js +++ b/pages/api/cards/import-lorcana.js @@ -56,28 +56,40 @@ export default async function handler(req, res) { console.log(`Starting import for Lorcana set: ${setCode}`); - // For now, we'll use a placeholder approach since Lorcana doesn't have a public API - // You can replace this with actual Lorcana API calls when available - const mockCards = [ - { - id: `lorcana-${setCode}-001`, - name: 'Sample Lorcana Card', - set: { name: setCode, id: setCode }, - number: '001', - rarity: 'Common', - supertype: 'Character', - types: ['Character'], - flavorText: 'A sample Lorcana card', - images: { small: null, large: null } - } - ]; + // Map set codes to Lorcast set codes + const setCodeMap = { + 'tfc': '1', // The First Chapter + 'rotf': '2', // Rise of the Floodborn + 'ink': '3' // Into the Inklands + }; - console.log(`Using mock data for set ${setCode} (${mockCards.length} cards)`); + const lorcastSetCode = setCodeMap[setCode]; + if (!lorcastSetCode) { + return res.status(400).json({ error: `Unknown set code: ${setCode}` }); + } + + // Fetch cards from Lorcast API + const response = await fetchWithRetry(`https://api.lorcast.com/v0/cards/search?q=set:${lorcastSetCode}&unique=prints`); + + const data = await response.json(); + const cards = data.results || []; + + if (cards.length === 0) { + return res.status(200).json({ + success: true, + message: `No cards found for set ${setCode}`, + imported: 0, + skipped: 0, + total: 0 + }); + } + + console.log(`Found ${cards.length} cards for set ${setCode}`); let importedCount = 0; let skippedCount = 0; - for (const card of mockCards) { + for (const card of cards) { try { // Check if card already exists const existingCard = await sql` @@ -89,16 +101,30 @@ export default async function handler(req, res) { continue; } + // Extract price data + let currentPrice = null; + if (card.prices?.usd) { + currentPrice = parseFloat(card.prices.usd); + } else if (card.prices?.usd_foil) { + currentPrice = parseFloat(card.prices.usd_foil); + } + // Determine rarity let rarity = card.rarity || 'Common'; - if (rarity.includes('Enchanted')) { - rarity = 'Enchanted'; - } else if (rarity.includes('Legendary')) { - rarity = 'Legendary'; - } else if (rarity.includes('Rare')) { - rarity = 'Rare'; + if (rarity === 'Super_rare') { + rarity = 'Super Rare'; } + // Extract card type and text + const cardType = card.type?.[0] || 'Character'; + const cardText = card.text || null; + const flavorText = card.flavor_text || null; + + // Extract stats + const strength = card.strength || null; + const willpower = card.willpower || null; + const lore = card.lore || null; + // Insert card into database await sql` INSERT INTO cards ( @@ -107,12 +133,12 @@ export default async function handler(req, res) { power, toughness, image_url, stock_image_url, current_price, market_price, scryfall_id, verified ) VALUES ( - ${card.name}, ${card.set.name}, ${card.set.id}, ${card.number}, - ${rarity}, 'Lorcana', null, null, ${card.supertype || 'Character'}, - ${JSON.stringify(card.types || [])}, ${card.flavorText || null}, - null, null, - ${card.images?.small || null}, ${card.images?.large || null}, - null, null, ${card.id}, true + ${card.name}, ${card.set.name}, ${card.set.code}, ${card.collector_number}, + ${rarity}, 'Lorcana', ${card.cost}, ${card.cost}, ${cardType}, + ${JSON.stringify(card.ink ? [card.ink] : [])}, ${cardText || flavorText}, + ${strength}, ${willpower || lore}, + ${card.image_uris?.digital?.small || null}, ${card.image_uris?.digital?.large || null}, + ${currentPrice}, null, ${card.id}, true ) `; @@ -130,7 +156,7 @@ export default async function handler(req, res) { message: `Import completed for set ${setCode}`, imported: importedCount, skipped: skippedCount, - total: mockCards.length + total: cards.length }); } catch (error) { diff --git a/scripts/import-lorcana-simple.js b/scripts/import-lorcana-simple.js new file mode 100644 index 0000000..c119b86 --- /dev/null +++ b/scripts/import-lorcana-simple.js @@ -0,0 +1,98 @@ +import fetch from 'node-fetch'; +import fs from 'fs'; + +// Lorcana sets to import +const LORCANA_SETS = [ + { code: 'tfc', name: 'The First Chapter' }, + { code: 'rotf', name: 'Rise of the Floodborn' }, + { code: 'ink', name: 'Into the Inklands' } +]; + +async function importLorcanaSet(setCode, setName) { + try { + console.log(`Importing Lorcana set: ${setName} (${setCode})`); + + const response = await fetch(`http://localhost:3000/api/cards/import-lorcana`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + setCode: setCode, + setName: setName + }) + }); + + const result = await response.json(); + + if (result.success) { + console.log(`āœ… Successfully imported ${result.imported || 0} cards from ${setName}`); + return { success: true, importedCount: result.imported || 0 }; + } else { + console.log(`āŒ Failed to import ${setName}: ${result.error}`); + return { success: false, error: result.error }; + } + } catch (error) { + console.log(`āŒ Error importing ${setName}: ${error.message}`); + return { success: false, error: error.message }; + } +} + +async function importAllLorcana() { + console.log('šŸŽØ Starting Lorcana card import...\n'); + + const results = { + total: 0, + successful: 0, + failed: 0, + sets: [] + }; + + for (const set of LORCANA_SETS) { + const result = await importLorcanaSet(set.code, set.name); + results.sets.push({ ...set, ...result }); + + if (result.success) { + results.successful++; + results.total += result.importedCount || 0; + } else { + results.failed++; + } + + // Add a delay between sets + await new Promise(resolve => setTimeout(resolve, 2000)); + } + + // Print summary + console.log('\nšŸ“Š Lorcana Import Summary:'); + console.log('========================'); + console.log(`Total sets: ${LORCANA_SETS.length}`); + console.log(`Successful: ${results.successful}`); + console.log(`Failed: ${results.failed}`); + console.log(`Total cards imported: ${results.total}`); + console.log(`Success rate: ${((results.successful / LORCANA_SETS.length) * 100).toFixed(1)}%`); + + // Save detailed results + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + const filename = `lorcana-import-results-${timestamp}.json`; + + fs.writeFileSync(filename, JSON.stringify(results, null, 2)); + console.log(`\nšŸ“„ Detailed results saved to: ${filename}`); + + return results; +} + +// Run the import if this script is executed directly +if (import.meta.url === `file://${process.argv[1]}`) { + importAllLorcana() + .then(() => { + console.log('\nšŸŽ‰ Lorcana import completed!'); + process.exit(0); + }) + .catch((error) => { + console.error('āŒ Import failed:', error); + process.exit(1); + }); +} + +export { importAllLorcana, importLorcanaSet }; \ No newline at end of file diff --git a/scripts/import-lorcana.js b/scripts/import-lorcana.js new file mode 100644 index 0000000..7109794 --- /dev/null +++ b/scripts/import-lorcana.js @@ -0,0 +1,186 @@ +import fetch from 'node-fetch'; +import fs from 'fs'; + +// Lorcana sets to import +const LORCANA_SETS = [ + { code: 'tfc', name: 'The First Chapter', lorcastCode: '1' }, + { code: 'rotf', name: 'Rise of the Floodborn', lorcastCode: '2' }, + { code: 'ink', name: 'Into the Inklands', lorcastCode: '3' } +]; + +// Helper function to delay execution +const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); + +// Helper function to fetch with retry logic +async function fetchWithRetry(url, maxRetries = 3, delayMs = 2000) { + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + const response = await fetch(url, { + headers: { + 'User-Agent': 'TCG-Vault/1.0', + 'Accept': 'application/json' + }, + timeout: 30000 // 30 second timeout + }); + + if (response.ok) { + return response; + } + + // If it's a 404, don't retry + if (response.status === 404) { + throw new Error(`Set not found: ${response.status}`); + } + + // If it's a 504 or 503, wait longer before retry + if (response.status === 504 || response.status === 503) { + console.log(`Attempt ${attempt}: Got ${response.status}, waiting ${delayMs * attempt}ms before retry...`); + await delay(delayMs * attempt); + continue; + } + + throw new Error(`Lorcana API error: ${response.status}`); + } catch (error) { + if (attempt === maxRetries) { + throw error; + } + console.log(`Attempt ${attempt} failed:`, error.message); + await delay(delayMs * attempt); + } + } +} + +async function importLorcanaSet(setCode, setName, lorcastCode) { + try { + console.log(`\nšŸŽØ Importing Lorcana set: ${setName} (${setCode})`); + + // Fetch cards directly from Lorcast API + const response = await fetchWithRetry(`https://api.lorcast.com/v0/cards/search?q=set:${lorcastCode}&unique=prints`); + const data = await response.json(); + const cards = data.results || []; + + if (cards.length === 0) { + console.log(`āŒ No cards found for set ${setName}`); + return { success: false, error: 'No cards found', importedCount: 0 }; + } + + console.log(`šŸ“Š Found ${cards.length} cards for set ${setName}`); + + let importedCount = 0; + let skippedCount = 0; + + for (const card of cards) { + try { + // Check if card already exists in database + const checkResponse = await fetch('http://localhost:3000/api/cards/search', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + query: card.name, + game: 'Lorcana', + set: card.set.code + }) + }); + + const checkData = await checkResponse.json(); + const existingCard = checkData.cards?.find(c => c.scryfall_id === card.id); + + if (existingCard) { + skippedCount++; + continue; + } + + // Import card via our API + const importResponse = await fetch('http://localhost:3000/api/cards/import-lorcana', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + setCode: setCode, + cardData: card + }) + }); + + const importResult = await importResponse.json(); + + if (importResult.success) { + importedCount++; + } else { + skippedCount++; + } + } catch (error) { + console.error(`Error processing card ${card.name}:`, error.message); + skippedCount++; + } + } + + console.log(`āœ… Import completed for ${setName}: ${importedCount} imported, ${skippedCount} skipped`); + return { success: true, importedCount, skippedCount, total: cards.length }; + + } catch (error) { + console.log(`āŒ Error importing ${setName}: ${error.message}`); + return { success: false, error: error.message }; + } +} + +async function importAllLorcana() { + console.log('šŸŽØ Starting Lorcana card import...\n'); + + const results = { + total: 0, + successful: 0, + failed: 0, + sets: [] + }; + + for (const set of LORCANA_SETS) { + const result = await importLorcanaSet(set.code, set.name, set.lorcastCode); + results.sets.push({ ...set, ...result }); + + if (result.success) { + results.successful++; + results.total += result.importedCount || 0; + } else { + results.failed++; + } + + // Add a delay between sets + await delay(2000); + } + + // Print summary + console.log('\nšŸ“Š Lorcana Import Summary:'); + console.log('========================'); + console.log(`Total sets: ${LORCANA_SETS.length}`); + console.log(`Successful: ${results.successful}`); + console.log(`Failed: ${results.failed}`); + console.log(`Total cards imported: ${results.total}`); + console.log(`Success rate: ${((results.successful / LORCANA_SETS.length) * 100).toFixed(1)}%`); + + // Save detailed results + const timestamp = new Date().toISOString().replace(/[:.]/g, '-'); + const filename = `lorcana-import-results-${timestamp}.json`; + + fs.writeFileSync(filename, JSON.stringify(results, null, 2)); + console.log(`\nšŸ“„ Detailed results saved to: ${filename}`); + + return results; +} + +// Run the import if this script is executed directly +if (import.meta.url === `file://${process.argv[1]}`) { + importAllLorcana() + .then(() => { + console.log('\nšŸŽ‰ Lorcana import completed!'); + process.exit(0); + }) + .catch((error) => { + console.error('āŒ Import failed:', error); + process.exit(1); + }); +} + +export { importAllLorcana, importLorcanaSet }; \ No newline at end of file