udated naming and added interactive
34
cards.csv
@ -2,3 +2,37 @@ Card Name,Set Code,Collector Number,Quantity,Foil
|
|||||||
"Linda, Kandarian Queen",sld,1355,1,True
|
"Linda, Kandarian Queen",sld,1355,1,True
|
||||||
"Vraska, Golgari Queen",sld,1702,1,False
|
"Vraska, Golgari Queen",sld,1702,1,False
|
||||||
"Vraska, Golgari Queen",sld,1702,1,True
|
"Vraska, Golgari Queen",sld,1702,1,True
|
||||||
|
"Huatli, Radiant Champion",sld,1699,1,False
|
||||||
|
"Huatli, Radiant Champion",sld,1699,1,True
|
||||||
|
"Kiora, Behemoth Beckoner",sld,1700,1,False
|
||||||
|
"Kiora, Behemoth Beckoner",sld,1700,1,True
|
||||||
|
"Tezzeret, Master of the Bridge",sld,1701,1,False
|
||||||
|
"Tezzeret, Master of the Bridge",sld,1701,1,True
|
||||||
|
Sorin Markov,sld,1698,1,False
|
||||||
|
Sorin Markov,sld,1698,1,True
|
||||||
|
"Flubs, The Fool",blc,356,1,True
|
||||||
|
"Flubs, The Fool",blc,356,1,False
|
||||||
|
Pollenbright Druid,sld,776,1,True
|
||||||
|
Frilled Mystic,sld,786,1,True
|
||||||
|
"Obeka, Brute Chronolgoist",sld,1579,1,False
|
||||||
|
Dire Undercurrents,sld,1578,1,False
|
||||||
|
Black Market,sld,1577,1,False
|
||||||
|
"Jace, Wielder of Mysteries",sld,1576,1,False
|
||||||
|
Reconnasissance,sld,1575,1,False
|
||||||
|
"Yargle, Glutton of Urborg",sld,1542,1,False
|
||||||
|
"Adrix and Nev, Twincasters",sld,1544,1,False
|
||||||
|
Coveted Jewel,sld,799,1,False
|
||||||
|
Sakashima of a Thousand Faces,sld,1541,1,False
|
||||||
|
"Krark, The Thumbless",sld,1543,1,False
|
||||||
|
Arcance Denial,sld,1545,1,False
|
||||||
|
Nightscape Familiar,sld,1546,1,False
|
||||||
|
Rain of Filth,sld,1547,1,False
|
||||||
|
Simian Spirit Guide,sld,1548,1,False
|
||||||
|
Prince of Thralls,sld,1549,1,False
|
||||||
|
Felidar Guardian,sld,1487,1,False
|
||||||
|
Peregrine Drake,sld,1488,1,False
|
||||||
|
Serpent of Yawning Depths,sld,1489,1,False
|
||||||
|
Scourage of Valkas,sld,1490,1,False
|
||||||
|
Voracious Hydra,sld,1491,1,False
|
||||||
|
The Scorpion God,sld,904,1,True
|
||||||
|
"Vilis, Broker of Blood",sld,1567,1,False
|
||||||
|
|
60
cli_cards.py
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import csv
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
|
||||||
|
# Function to append a card entry to the CSV file
|
||||||
|
def add_card_to_csv(csv_path, card_name, set_code, collector_number, quantity, foil):
|
||||||
|
file_exists = os.path.isfile(csv_path)
|
||||||
|
|
||||||
|
with open(csv_path, mode='a', newline='') as file:
|
||||||
|
writer = csv.writer(file)
|
||||||
|
|
||||||
|
# Write header if the file doesn't exist
|
||||||
|
if not file_exists:
|
||||||
|
writer.writerow(['Card Name', 'Set Code', 'Collector Number', 'Quantity', 'Foil'])
|
||||||
|
|
||||||
|
# Append the card data
|
||||||
|
writer.writerow([card_name, set_code, collector_number, quantity, foil])
|
||||||
|
|
||||||
|
print(f"Added {quantity}x {card_name} from set {set_code} with collector number {collector_number} (Foil: {foil}) to {csv_path}.")
|
||||||
|
|
||||||
|
# Function to parse and add multiple cards
|
||||||
|
def add_multiple_cards(csv_path, card_entries):
|
||||||
|
for entry in card_entries:
|
||||||
|
card_name, set_code, collector_number, quantity, foil = entry
|
||||||
|
add_card_to_csv(csv_path, card_name, set_code, collector_number, quantity, foil)
|
||||||
|
|
||||||
|
# Main function to handle command-line arguments
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Add one or more Magic: The Gathering cards to a CSV file.")
|
||||||
|
|
||||||
|
parser.add_argument('csv_path',
|
||||||
|
help="The path to the CSV file where the card data will be stored. If the file does not exist, it will be created.")
|
||||||
|
|
||||||
|
# Allow multiple --card entries
|
||||||
|
parser.add_argument('--card',
|
||||||
|
action='append',
|
||||||
|
nargs=5,
|
||||||
|
metavar=('Card_Name', 'Set_Code', 'Collector_Number', 'Quantity', 'Foil'),
|
||||||
|
help=("Add a card to the CSV file. "
|
||||||
|
"Each card entry should be specified in the following format: "
|
||||||
|
"Card_Name Set_Code Collector_Number Quantity Foil. "
|
||||||
|
"Foil should be 'True' or 'False' indicating if the card is foil or not. "
|
||||||
|
"This option can be used multiple times to add multiple cards in one command."))
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.card:
|
||||||
|
card_entries = []
|
||||||
|
for card in args.card:
|
||||||
|
card_name, set_code, collector_number, quantity, foil = card
|
||||||
|
quantity = int(quantity)
|
||||||
|
foil = foil.lower() == 'true' # Convert the foil input to a boolean
|
||||||
|
card_entries.append((card_name, set_code, collector_number, quantity, foil))
|
||||||
|
|
||||||
|
add_multiple_cards(args.csv_path, card_entries)
|
||||||
|
else:
|
||||||
|
print("No cards provided. Use the --card option to add cards.")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
BIN
downloaded_images/blc_Flubs, The Fool_Flubs, the Fool.jpg
Normal file
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 108 KiB |
BIN
downloaded_images/sld_Arcance Denial_Arcane Denial.jpg
Normal file
After Width: | Height: | Size: 92 KiB |
BIN
downloaded_images/sld_Black Market_Black Market.jpg
Normal file
After Width: | Height: | Size: 108 KiB |
BIN
downloaded_images/sld_Coveted Jewel_Coveted Jewel.jpg
Normal file
After Width: | Height: | Size: 74 KiB |
BIN
downloaded_images/sld_Dire Undercurrents_Dire Undercurrents.jpg
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
downloaded_images/sld_Felidar Guardian_Felidar Guardian.jpg
Normal file
After Width: | Height: | Size: 113 KiB |
BIN
downloaded_images/sld_Frilled Mystic_Frilled Mystic.jpg
Normal file
After Width: | Height: | Size: 76 KiB |
After Width: | Height: | Size: 92 KiB |
After Width: | Height: | Size: 118 KiB |
After Width: | Height: | Size: 94 KiB |
After Width: | Height: | Size: 98 KiB |
Before Width: | Height: | Size: 93 KiB After Width: | Height: | Size: 93 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 121 KiB |
BIN
downloaded_images/sld_Peregrine Drake_Peregrine Drake.jpg
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
downloaded_images/sld_Pollenbright Druid_Pollenbright Druid.jpg
Normal file
After Width: | Height: | Size: 96 KiB |
BIN
downloaded_images/sld_Prince of Thralls_Prince of Thralls.jpg
Normal file
After Width: | Height: | Size: 93 KiB |
BIN
downloaded_images/sld_Rain of Filth_Rain of Filth.jpg
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
downloaded_images/sld_Reconnasissance_Reconnaissance.jpg
Normal file
After Width: | Height: | Size: 112 KiB |
After Width: | Height: | Size: 103 KiB |
BIN
downloaded_images/sld_Scourage of Valkas_Scourge of Valkas.jpg
Normal file
After Width: | Height: | Size: 119 KiB |
After Width: | Height: | Size: 112 KiB |
After Width: | Height: | Size: 85 KiB |
BIN
downloaded_images/sld_Sorin Markov_Sorin Markov.jpg
Normal file
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 103 KiB |
BIN
downloaded_images/sld_The Scorpion God_The Scorpion God.jpg
Normal file
After Width: | Height: | Size: 120 KiB |
After Width: | Height: | Size: 90 KiB |
BIN
downloaded_images/sld_Voracious Hydra_Voracious Hydra.jpg
Normal file
After Width: | Height: | Size: 74 KiB |
Before Width: | Height: | Size: 102 KiB After Width: | Height: | Size: 102 KiB |
After Width: | Height: | Size: 96 KiB |
81
image_dl.py
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import requests
|
||||||
|
import pandas as pd
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
|
||||||
|
# Function to fetch card details from Scryfall using the set code and collector number
|
||||||
|
def fetch_card_images_by_set_and_number(set_code, collector_number):
|
||||||
|
url = f"https://api.scryfall.com/cards/{set_code}/{collector_number}"
|
||||||
|
response = requests.get(url)
|
||||||
|
|
||||||
|
if response.status_code == 200:
|
||||||
|
card_data = response.json()
|
||||||
|
images = []
|
||||||
|
|
||||||
|
# Handle single-sided or multi-sided cards
|
||||||
|
if 'image_uris' in card_data:
|
||||||
|
images.append((card_data['image_uris']['normal'], card_data['name']))
|
||||||
|
if 'card_faces' in card_data:
|
||||||
|
for face in card_data['card_faces']:
|
||||||
|
if 'image_uris' in face:
|
||||||
|
images.append((face['image_uris']['normal'], face['name']))
|
||||||
|
return images
|
||||||
|
else:
|
||||||
|
print(f"Error fetching card with set code {set_code} and collector number {collector_number}: {response.status_code}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# Function to download and save an image
|
||||||
|
def download_image(image_url, save_path):
|
||||||
|
response = requests.get(image_url)
|
||||||
|
if response.status_code == 200:
|
||||||
|
with open(save_path, 'wb') as file:
|
||||||
|
file.write(response.content)
|
||||||
|
else:
|
||||||
|
print(f"Failed to download image from {image_url}")
|
||||||
|
|
||||||
|
# Function to process the CSV and download images
|
||||||
|
def download_images_from_csv(csv_path, output_folder):
|
||||||
|
# Create output directory if it doesn't exist
|
||||||
|
if not os.path.exists(output_folder):
|
||||||
|
os.makedirs(output_folder)
|
||||||
|
|
||||||
|
# Read the CSV file
|
||||||
|
df = pd.read_csv(csv_path)
|
||||||
|
|
||||||
|
# Assuming the CSV has columns 'Card Name', 'Set Code', and 'Collector Number'
|
||||||
|
for index, row in df.iterrows():
|
||||||
|
card_name = row['Card Name']
|
||||||
|
set_code = row['Set Code']
|
||||||
|
collector_number = row['Collector Number']
|
||||||
|
|
||||||
|
# Naming convention: set_code_card_name_face_name.jpg
|
||||||
|
image_base_name = f"{set_code}_{card_name.replace('/', '-').replace(':', '')}"
|
||||||
|
|
||||||
|
print(f"Processing {card_name} from set {set_code} with collector number {collector_number}...")
|
||||||
|
|
||||||
|
images = fetch_card_images_by_set_and_number(set_code, collector_number)
|
||||||
|
if images:
|
||||||
|
for image_url, face_name in images:
|
||||||
|
# Sanitize file name by removing invalid characters
|
||||||
|
image_file_name = f"{image_base_name}_{face_name.replace('/', '-').replace(':', '')}.jpg"
|
||||||
|
save_path = os.path.join(output_folder, image_file_name)
|
||||||
|
|
||||||
|
# Skip downloading if the image already exists
|
||||||
|
if os.path.exists(save_path):
|
||||||
|
print(f"Image already exists for {card_name} ({face_name}), skipping download.")
|
||||||
|
continue
|
||||||
|
|
||||||
|
download_image(image_url, save_path)
|
||||||
|
time.sleep(0.2) # Sleep to avoid hitting API rate limits
|
||||||
|
else:
|
||||||
|
print(f"No images found for {card_name} from set {set_code} with collector number {collector_number}")
|
||||||
|
|
||||||
|
# Main execution
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# Define the path to the input CSV and output folder
|
||||||
|
csv_path = 'cards.csv' # Replace with your CSV file path
|
||||||
|
output_folder = 'downloaded_images'
|
||||||
|
|
||||||
|
# Start the download process
|
||||||
|
download_images_from_csv(csv_path, output_folder)
|
||||||
|
print("Download completed.")
|
39
interactive_cards.py
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import csv
|
||||||
|
import os
|
||||||
|
|
||||||
|
# Function to append a card entry to the CSV file
|
||||||
|
def add_card_to_csv(csv_path, card_name, set_code, collector_number, quantity, foil):
|
||||||
|
file_exists = os.path.isfile(csv_path)
|
||||||
|
|
||||||
|
with open(csv_path, mode='a', newline='') as file:
|
||||||
|
writer = csv.writer(file)
|
||||||
|
|
||||||
|
# Write header if the file doesn't exist
|
||||||
|
if not file_exists:
|
||||||
|
writer.writerow(['Card Name', 'Set Code', 'Collector Number', 'Quantity', 'Foil'])
|
||||||
|
|
||||||
|
# Append the card data
|
||||||
|
writer.writerow([card_name, set_code, collector_number, quantity, foil])
|
||||||
|
|
||||||
|
print(f"Added {quantity}x {card_name} from set {set_code} with collector number {collector_number} (Foil: {foil}) to {csv_path}.")
|
||||||
|
|
||||||
|
# Function to get user input and add multiple cards interactively
|
||||||
|
def interactive_card_entry(csv_path):
|
||||||
|
while True:
|
||||||
|
# Get the card details from the user
|
||||||
|
card_name = input("Enter the card name (or type 'exit' to quit): ").strip()
|
||||||
|
if card_name.lower() == 'exit':
|
||||||
|
print("Exiting the program.")
|
||||||
|
break
|
||||||
|
|
||||||
|
set_code = input("Enter the set code: ").strip()
|
||||||
|
collector_number = input("Enter the collector number: ").strip()
|
||||||
|
quantity = int(input("Enter the quantity: ").strip())
|
||||||
|
foil = input("Is this card a foil? (yes/no): ").strip().lower() == 'yes'
|
||||||
|
|
||||||
|
# Add the card to the CSV file
|
||||||
|
add_card_to_csv(csv_path, card_name, set_code, collector_number, quantity, foil)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
csv_path = input("Enter the path to the CSV file: ").strip()
|
||||||
|
interactive_card_entry(csv_path)
|