kopia lustrzana https://github.com/torrinworx/Blend_My_NFTs
Reformatting and preparing for debug mode
Created Helpers.py file and combined get_combinations.py, loading_animation.py, Constants.py, and Checks.py.pull/142/head
rodzic
db4e4c21c1
commit
cabefc04d1
26
__init__.py
26
__init__.py
|
@ -36,13 +36,11 @@ sys.path.append(os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
|
||||||
# Local file imports:
|
# Local file imports:
|
||||||
from main import \
|
from main import \
|
||||||
Checks, \
|
Helpers, \
|
||||||
DNA_Generator, \
|
DNA_Generator, \
|
||||||
Exporter, \
|
Exporter, \
|
||||||
get_combinations, \
|
|
||||||
HeadlessUtil, \
|
HeadlessUtil, \
|
||||||
Intermediate, \
|
Intermediate, \
|
||||||
loading_animation, \
|
|
||||||
Logic, \
|
Logic, \
|
||||||
Material_Generator, \
|
Material_Generator, \
|
||||||
Metadata, \
|
Metadata, \
|
||||||
|
@ -55,12 +53,10 @@ from UILists import \
|
||||||
|
|
||||||
if "bpy" in locals():
|
if "bpy" in locals():
|
||||||
modules = {
|
modules = {
|
||||||
"Checks": Checks,
|
"Helpers": Helpers,
|
||||||
"DNA_Generator": DNA_Generator,
|
"DNA_Generator": DNA_Generator,
|
||||||
"Exporter": Exporter,
|
"Exporter": Exporter,
|
||||||
"get_combinations": get_combinations,
|
|
||||||
"HeadlessUtil": HeadlessUtil,
|
"HeadlessUtil": HeadlessUtil,
|
||||||
"loading_animation": loading_animation,
|
|
||||||
"Intermediate": Intermediate,
|
"Intermediate": Intermediate,
|
||||||
"Logic": Logic,
|
"Logic": Logic,
|
||||||
"Material_Generator": Material_Generator,
|
"Material_Generator": Material_Generator,
|
||||||
|
@ -75,9 +71,9 @@ if "bpy" in locals():
|
||||||
if i in locals():
|
if i in locals():
|
||||||
importlib.reload(modules[i])
|
importlib.reload(modules[i])
|
||||||
|
|
||||||
# ======== Persistant UI Refresh ======== #
|
# ======== Persistent UI Refresh ======== #
|
||||||
|
|
||||||
# Used for updating text and buttons in UI panels
|
# Used for updating text and buttons in UI panels
|
||||||
|
|
||||||
combinations: int = 0
|
combinations: int = 0
|
||||||
recommended_limit: int = 0
|
recommended_limit: int = 0
|
||||||
dt = datetime.now(timezone.utc).astimezone() # Date Time in UTC local
|
dt = datetime.now(timezone.utc).astimezone() # Date Time in UTC local
|
||||||
|
@ -92,7 +88,7 @@ def Refresh_UI(dummy1, dummy2):
|
||||||
global combinations
|
global combinations
|
||||||
global recommended_limit
|
global recommended_limit
|
||||||
|
|
||||||
combinations = (get_combinations.get_combinations())
|
combinations = (Helpers.get_combinations())
|
||||||
recommended_limit = int(round(combinations / 2))
|
recommended_limit = int(round(combinations / 2))
|
||||||
|
|
||||||
# Add panel classes that require refresh to this refresh_panels tuple:
|
# Add panel classes that require refresh to this refresh_panels tuple:
|
||||||
|
@ -165,6 +161,7 @@ class BMNFTData:
|
||||||
sender_from: str
|
sender_from: str
|
||||||
email_password: str
|
email_password: str
|
||||||
receiver_to: str
|
receiver_to: str
|
||||||
|
enable_debug: bool
|
||||||
|
|
||||||
custom_Fields: dict = None
|
custom_Fields: dict = None
|
||||||
fail_state: Any = False
|
fail_state: Any = False
|
||||||
|
@ -229,6 +226,7 @@ def getBMNFTData():
|
||||||
sender_from=bpy.context.scene.input_tool.sender_from,
|
sender_from=bpy.context.scene.input_tool.sender_from,
|
||||||
email_password=bpy.context.scene.input_tool.email_password,
|
email_password=bpy.context.scene.input_tool.email_password,
|
||||||
receiver_to=bpy.context.scene.input_tool.receiver_to,
|
receiver_to=bpy.context.scene.input_tool.receiver_to,
|
||||||
|
enable_debug=bpy.context.scene.input_tool.enable_debug
|
||||||
)
|
)
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
@ -464,7 +462,6 @@ class BMNFTS_PGT_Input_Properties(bpy.types.PropertyGroup):
|
||||||
enableAutoSave: bpy.props.BoolProperty(name="Auto Save Before Generation",
|
enableAutoSave: bpy.props.BoolProperty(name="Auto Save Before Generation",
|
||||||
description="Automatically saves your Blender file when 'Generate NFTs & Create Metadata' button is clicked")
|
description="Automatically saves your Blender file when 'Generate NFTs & Create Metadata' button is clicked")
|
||||||
|
|
||||||
# Auto Shutdown:
|
|
||||||
enableAutoShutdown: bpy.props.BoolProperty(name="Auto Shutdown",
|
enableAutoShutdown: bpy.props.BoolProperty(name="Auto Shutdown",
|
||||||
description="Automatically shuts down your computer after a Batch is finished Generating")
|
description="Automatically shuts down your computer after a Batch is finished Generating")
|
||||||
|
|
||||||
|
@ -473,15 +470,16 @@ class BMNFTS_PGT_Input_Properties(bpy.types.PropertyGroup):
|
||||||
hours: bpy.props.IntProperty(default=0, min=0)
|
hours: bpy.props.IntProperty(default=0, min=0)
|
||||||
minutes: bpy.props.IntProperty(default=0, min=0)
|
minutes: bpy.props.IntProperty(default=0, min=0)
|
||||||
|
|
||||||
# Send Batch Complete Email:
|
|
||||||
emailNotificationBool: bpy.props.BoolProperty(name="Email Notifications",
|
emailNotificationBool: bpy.props.BoolProperty(name="Email Notifications",
|
||||||
description="Receive Email Notifications from Blender once a batch is finished generating")
|
description="Receive Email Notifications from Blender once a batch is finished generating")
|
||||||
sender_from: bpy.props.StringProperty(name="From", default="from@example.com")
|
sender_from: bpy.props.StringProperty(name="From", default="from@example.com")
|
||||||
email_password: bpy.props.StringProperty(name="Password", subtype='PASSWORD')
|
email_password: bpy.props.StringProperty(name="Password", subtype='PASSWORD')
|
||||||
receiver_to: bpy.props.StringProperty(name="To", default="to@example.com")
|
receiver_to: bpy.props.StringProperty(name="To", default="to@example.com")
|
||||||
|
|
||||||
|
enable_debug: bpy.props.BoolProperty(name="Enable Debug Mode", description="Allows you to run Blend_My_NFTs without generating any content files and includes more console information.")
|
||||||
|
|
||||||
# API Panel properties:
|
# API Panel properties:
|
||||||
apiKey: bpy.props.StringProperty(name="API Key", subtype='PASSWORD') # Test code for future faetures
|
apiKey: bpy.props.StringProperty(name="API Key", subtype='PASSWORD') # Test code for future features
|
||||||
|
|
||||||
|
|
||||||
# ======== Main Operators ======== #
|
# ======== Main Operators ======== #
|
||||||
|
@ -602,6 +600,7 @@ class resume_failed_batch(bpy.types.Operator):
|
||||||
sender_from=render_settings["sender_from"],
|
sender_from=render_settings["sender_from"],
|
||||||
email_password=render_settings["email_password"],
|
email_password=render_settings["email_password"],
|
||||||
receiver_to=render_settings["receiver_to"],
|
receiver_to=render_settings["receiver_to"],
|
||||||
|
enable_debug=render_settings["enable_debug"],
|
||||||
|
|
||||||
fail_state=_fail_state,
|
fail_state=_fail_state,
|
||||||
failed_batch=_failed_batch,
|
failed_batch=_failed_batch,
|
||||||
|
@ -1000,6 +999,9 @@ class BMNFTS_PT_Other(bpy.types.Panel):
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
layout.label(text=f"**Set a Save Path in Create NFT Data to Export Settings")
|
layout.label(text=f"**Set a Save Path in Create NFT Data to Export Settings")
|
||||||
|
|
||||||
|
row = layout.row()
|
||||||
|
row.prop(input_tool_scene, "enable_debug")
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
|
|
||||||
row = layout.row()
|
row = layout.row()
|
||||||
|
|
|
@ -1,51 +0,0 @@
|
||||||
# Purpose:
|
|
||||||
# This file is for storing or updating constant values that may need to be changes depending on system requirements and
|
|
||||||
# different usecases.
|
|
||||||
import os
|
|
||||||
import json
|
|
||||||
import platform
|
|
||||||
|
|
||||||
removeList = [".gitignore", ".DS_Store", "desktop.ini", ".ini"]
|
|
||||||
|
|
||||||
def remove_file_by_extension(dirlist):
|
|
||||||
"""
|
|
||||||
Checks if a given directory list contains any of the files or file extensions listed above, if so, remove them from
|
|
||||||
list and return a clean dir list. These files interfer with BMNFTs operations and should be removed whenever dealing
|
|
||||||
with directories.
|
|
||||||
"""
|
|
||||||
|
|
||||||
if str(type(dirlist)) == "<class 'list'>":
|
|
||||||
dirlist = list(dirlist) # converts single string path to list if dir pasted as string
|
|
||||||
|
|
||||||
return_dirs = []
|
|
||||||
for directory in dirlist:
|
|
||||||
if not str(os.path.split(directory)[1]) in removeList:
|
|
||||||
return_dirs.append(directory)
|
|
||||||
|
|
||||||
return return_dirs
|
|
||||||
|
|
||||||
|
|
||||||
class bcolors:
|
|
||||||
"""
|
|
||||||
The colour of console messages.
|
|
||||||
"""
|
|
||||||
|
|
||||||
OK = '\033[92m' # GREEN
|
|
||||||
WARNING = '\033[93m' # YELLOW
|
|
||||||
ERROR = '\033[91m' # RED
|
|
||||||
RESET = '\033[0m' # RESET COLOR
|
|
||||||
|
|
||||||
def save_result(result):
|
|
||||||
"""
|
|
||||||
Saves json result to json file at the specified path.
|
|
||||||
"""
|
|
||||||
file_name = "log.json"
|
|
||||||
if platform.system() == "Linux" or platform.system() == "Darwin":
|
|
||||||
path = os.path.join(os.path.join(os.path.expanduser('~')), 'Desktop', file_name)
|
|
||||||
|
|
||||||
if platform.system() == "Windows":
|
|
||||||
path = os.path.join(os.environ["HOMEPATH"], "Desktop", file_name)
|
|
||||||
|
|
||||||
data = json.dumps(result, indent=1, ensure_ascii=True)
|
|
||||||
with open(path, 'w') as outfile:
|
|
||||||
outfile.write(data + '\n')
|
|
|
@ -3,15 +3,13 @@
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
import copy
|
import copy
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from .loading_animation import Loader
|
from . import Rarity, Logic, Material_Generator, Helpers
|
||||||
from . import Rarity, Logic, Checks, Material_Generator
|
from .Helpers import bcolors, Loader
|
||||||
from .Constants import bcolors, removeList, remove_file_by_extension
|
|
||||||
|
|
||||||
|
|
||||||
def get_hierarchy():
|
def get_hierarchy():
|
||||||
|
@ -124,10 +122,10 @@ def get_hierarchy():
|
||||||
return hierarchy
|
return hierarchy
|
||||||
|
|
||||||
|
|
||||||
def generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enableMaterials, materialsFile):
|
def generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enableMaterials, materialsFile, enable_debug):
|
||||||
|
"""
|
||||||
|
Returns batchDataDictionary containing the number of NFT combinations, hierarchy, and the DNAList.
|
||||||
"""
|
"""
|
||||||
Returns batchDataDictionary containing the number of NFT combinations, hierarchy, and the DNAList.
|
|
||||||
"""
|
|
||||||
|
|
||||||
hierarchy = get_hierarchy()
|
hierarchy = get_hierarchy()
|
||||||
|
|
||||||
|
@ -164,7 +162,6 @@ def generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enable
|
||||||
"""
|
"""
|
||||||
|
|
||||||
singleDNA = ""
|
singleDNA = ""
|
||||||
# Comments for debugging random, rarity, logic, and materials.
|
|
||||||
if not enableRarity:
|
if not enableRarity:
|
||||||
singleDNA = createDNArandom(hierarchy)
|
singleDNA = createDNArandom(hierarchy)
|
||||||
# print("============")
|
# print("============")
|
||||||
|
@ -214,7 +211,7 @@ def generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enable
|
||||||
|
|
||||||
# Messages:
|
# Messages:
|
||||||
|
|
||||||
Checks.raise_Warning_collectionSize(DNAList, collectionSize)
|
Helpers.raise_Warning_collectionSize(DNAList, collectionSize)
|
||||||
|
|
||||||
# Data stored in batchDataDictionary:
|
# Data stored in batchDataDictionary:
|
||||||
DataDictionary["numNFTsGenerated"] = len(DNAList)
|
DataDictionary["numNFTsGenerated"] = len(DNAList)
|
||||||
|
@ -281,7 +278,7 @@ def makeBatches(collectionSize, nftsPerBatch, save_path, batch_json_save_path):
|
||||||
|
|
||||||
|
|
||||||
def send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, enableLogic, logicFile, enableMaterials,
|
def send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, enableLogic, logicFile, enableMaterials,
|
||||||
materialsFile, Blend_My_NFTs_Output, batch_json_save_path):
|
materialsFile, Blend_My_NFTs_Output, batch_json_save_path, enable_debug):
|
||||||
"""
|
"""
|
||||||
Creates NFTRecord.json file and sends "batchDataDictionary" to it. NFTRecord.json is a permanent record of all DNA
|
Creates NFTRecord.json file and sends "batchDataDictionary" to it. NFTRecord.json is a permanent record of all DNA
|
||||||
you've generated with all attribute variants. If you add new variants or attributes to your .blend file, other scripts
|
you've generated with all attribute variants. If you add new variants or attributes to your .blend file, other scripts
|
||||||
|
@ -290,7 +287,7 @@ def send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, e
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Checking Scene is compatible with BMNFTs:
|
# Checking Scene is compatible with BMNFTs:
|
||||||
Checks.check_Scene()
|
Helpers.check_Scene()
|
||||||
|
|
||||||
# Messages:
|
# Messages:
|
||||||
print(
|
print(
|
||||||
|
@ -313,18 +310,18 @@ def send_To_Record_JSON(collectionSize, nftsPerBatch, save_path, enableRarity, e
|
||||||
def create_nft_data():
|
def create_nft_data():
|
||||||
try:
|
try:
|
||||||
DataDictionary = generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enableMaterials,
|
DataDictionary = generateNFT_DNA(collectionSize, enableRarity, enableLogic, logicFile, enableMaterials,
|
||||||
materialsFile)
|
materialsFile, enable_debug)
|
||||||
NFTRecord_save_path = os.path.join(Blend_My_NFTs_Output, "NFTRecord.json")
|
NFTRecord_save_path = os.path.join(Blend_My_NFTs_Output, "NFTRecord.json")
|
||||||
|
|
||||||
# Checks:
|
# Checks:
|
||||||
|
|
||||||
Checks.raise_Warning_maxNFTs(nftsPerBatch, collectionSize)
|
Helpers.raise_Warning_maxNFTs(nftsPerBatch, collectionSize)
|
||||||
Checks.check_Duplicates(DataDictionary["DNAList"])
|
Helpers.check_Duplicates(DataDictionary["DNAList"])
|
||||||
Checks.raise_Error_ZeroCombinations()
|
Helpers.raise_Error_ZeroCombinations()
|
||||||
|
|
||||||
if enableRarity:
|
if enableRarity:
|
||||||
Checks.check_Rarity(DataDictionary["hierarchy"], DataDictionary["DNAList"],
|
Helpers.check_Rarity(DataDictionary["hierarchy"], DataDictionary["DNAList"],
|
||||||
os.path.join(save_path, "Blend_My_NFTs Output/NFT_Data"))
|
os.path.join(save_path, "Blend_My_NFTs Output/NFT_Data"))
|
||||||
|
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(
|
||||||
|
|
|
@ -10,8 +10,8 @@ import json
|
||||||
import smtplib
|
import smtplib
|
||||||
import datetime
|
import datetime
|
||||||
import platform
|
import platform
|
||||||
from .loading_animation import Loader
|
|
||||||
from .Constants import bcolors, removeList, remove_file_by_extension
|
from .Helpers import bcolors, Loader
|
||||||
from .Metadata import createCardanoMetadata, createSolanaMetaData, createErc721MetaData
|
from .Metadata import createCardanoMetadata, createSolanaMetaData, createErc721MetaData
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,21 +1,122 @@
|
||||||
# Purpose:
|
|
||||||
# The purpose of this file is to check the NFTRecord.json for duplicate NFT DNA and returns any found in the console.
|
|
||||||
# It also checks the percentage each variant is chosen in the NFTRecord, then compares it with its rarity percentage
|
|
||||||
# set in the .blend file.
|
|
||||||
|
|
||||||
# This file is provided for transparency. The accuracy of the rarity values you set in your .blend file as outlined in
|
|
||||||
# the README.md file are dependent on the maxNFTs, and the maximum number of combinations of your NFT collection.
|
|
||||||
|
|
||||||
import bpy
|
import bpy
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
|
import platform
|
||||||
|
from time import sleep
|
||||||
|
from itertools import cycle
|
||||||
|
from threading import Thread
|
||||||
|
from shutil import get_terminal_size
|
||||||
from collections import Counter, defaultdict
|
from collections import Counter, defaultdict
|
||||||
|
|
||||||
from . import DNA_Generator, get_combinations
|
from . import DNA_Generator
|
||||||
from .Constants import bcolors, removeList, remove_file_by_extension
|
|
||||||
|
|
||||||
|
|
||||||
# Checks:
|
# ======== CONSTANTS ======== #
|
||||||
|
|
||||||
|
# This section is used for debugging, coding, or general testing purposes.
|
||||||
|
|
||||||
|
|
||||||
|
def enable_debug(enable_debug_bool):
|
||||||
|
if enable_debug_bool:
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logging.basicConfig(
|
||||||
|
filename="./log.txt",
|
||||||
|
level=logging.DEBUG,
|
||||||
|
format='[%(levelname)s][%(asctime)s]\n%(message)s\n',
|
||||||
|
datefmt='%Y-%m-%d %H:%M:%S'
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ======== CONSTANTS ======== #
|
||||||
|
|
||||||
|
# Constants are used for storing or updating constant values that may need to be changes depending on system
|
||||||
|
# requirements and different use-cases.
|
||||||
|
|
||||||
|
removeList = [".gitignore", ".DS_Store", "desktop.ini", ".ini"]
|
||||||
|
|
||||||
|
|
||||||
|
def remove_file_by_extension(dirlist):
|
||||||
|
"""
|
||||||
|
Checks if a given directory list contains any of the files or file extensions listed above, if so, remove them from
|
||||||
|
list and return a clean dir list. These files interfer with BMNFTs operations and should be removed whenever dealing
|
||||||
|
with directories.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if str(type(dirlist)) == "<class 'list'>":
|
||||||
|
dirlist = list(dirlist) # converts single string path to list if dir pasted as string
|
||||||
|
|
||||||
|
return_dirs = []
|
||||||
|
for directory in dirlist:
|
||||||
|
if not str(os.path.split(directory)[1]) in removeList:
|
||||||
|
return_dirs.append(directory)
|
||||||
|
|
||||||
|
return return_dirs
|
||||||
|
|
||||||
|
|
||||||
|
class bcolors:
|
||||||
|
"""
|
||||||
|
The colour of console messages.
|
||||||
|
"""
|
||||||
|
|
||||||
|
OK = '\033[92m' # GREEN
|
||||||
|
WARNING = '\033[93m' # YELLOW
|
||||||
|
ERROR = '\033[91m' # RED
|
||||||
|
RESET = '\033[0m' # RESET COLOR
|
||||||
|
|
||||||
|
|
||||||
|
def save_result(result):
|
||||||
|
"""
|
||||||
|
Saves json result to json file at the specified path.
|
||||||
|
"""
|
||||||
|
file_name = "log.json"
|
||||||
|
if platform.system() == "Linux" or platform.system() == "Darwin":
|
||||||
|
path = os.path.join(os.path.join(os.path.expanduser('~')), 'Desktop', file_name)
|
||||||
|
|
||||||
|
if platform.system() == "Windows":
|
||||||
|
path = os.path.join(os.environ["HOMEPATH"], "Desktop", file_name)
|
||||||
|
|
||||||
|
data = json.dumps(result, indent=1, ensure_ascii=True)
|
||||||
|
with open(path, 'w') as outfile:
|
||||||
|
outfile.write(data + '\n')
|
||||||
|
|
||||||
|
|
||||||
|
# ======== GET COMBINATIONS ======== #
|
||||||
|
|
||||||
|
# This section is used to get the number of combinations for checks and the UI display
|
||||||
|
|
||||||
|
def get_combinations():
|
||||||
|
"""
|
||||||
|
Returns "combinations", the number of all possible NFT DNA for a given Blender scene formatted to BMNFTs conventions
|
||||||
|
combinations.
|
||||||
|
"""
|
||||||
|
|
||||||
|
hierarchy = DNA_Generator.get_hierarchy()
|
||||||
|
hierarchyByNum = []
|
||||||
|
|
||||||
|
for i in hierarchy:
|
||||||
|
# Ignore Collections with nothing in them
|
||||||
|
if len(hierarchy[i]) != 0:
|
||||||
|
hierarchyByNum.append(len(hierarchy[i]))
|
||||||
|
else:
|
||||||
|
print(f"The following collection has been identified as empty: {i}")
|
||||||
|
|
||||||
|
combinations = 1
|
||||||
|
for i in hierarchyByNum:
|
||||||
|
combinations = combinations * i
|
||||||
|
|
||||||
|
return combinations
|
||||||
|
|
||||||
|
|
||||||
|
# ======== CHECKS ======== #
|
||||||
|
|
||||||
|
# This section is used to check the NFTRecord.json for duplicate NFT DNA and returns any found in the console.
|
||||||
|
# It also checks the percentage each variant is chosen in the NFTRecord, then compares it with its rarity percentage
|
||||||
|
# set in the .blend file.
|
||||||
|
|
||||||
|
# This section is provided for transparency. The accuracy of the rarity values you set in your .blend file as outlined
|
||||||
|
# in the README.md file are dependent on the maxNFTs, and the maximum number of combinations of your NFT collection.
|
||||||
|
|
||||||
def check_Scene(): # Not complete
|
def check_Scene(): # Not complete
|
||||||
"""
|
"""
|
||||||
Checks if Blender file Scene follows the Blend_My_NFTs conventions. If not, raises error with all instances of
|
Checks if Blender file Scene follows the Blend_My_NFTs conventions. If not, raises error with all instances of
|
||||||
|
@ -44,6 +145,7 @@ def check_Scene(): # Not complete
|
||||||
|
|
||||||
# attribute_naming_conventions
|
# attribute_naming_conventions
|
||||||
|
|
||||||
|
|
||||||
def check_Rarity(hierarchy, DNAListFormatted, save_path):
|
def check_Rarity(hierarchy, DNAListFormatted, save_path):
|
||||||
"""Checks rarity percentage of each Variant, then sends it to RarityData.json in NFT_Data folder."""
|
"""Checks rarity percentage of each Variant, then sends it to RarityData.json in NFT_Data folder."""
|
||||||
|
|
||||||
|
@ -51,7 +153,6 @@ def check_Rarity(hierarchy, DNAListFormatted, save_path):
|
||||||
for i in DNAListFormatted:
|
for i in DNAListFormatted:
|
||||||
DNAList.append(list(i.keys())[0])
|
DNAList.append(list(i.keys())[0])
|
||||||
|
|
||||||
|
|
||||||
numNFTsGenerated = len(DNAList)
|
numNFTsGenerated = len(DNAList)
|
||||||
|
|
||||||
numDict = defaultdict(list)
|
numDict = defaultdict(list)
|
||||||
|
@ -90,7 +191,7 @@ def check_Rarity(hierarchy, DNAListFormatted, save_path):
|
||||||
if l == k:
|
if l == k:
|
||||||
name = fullNumName[i][k]
|
name = fullNumName[i][k]
|
||||||
num = numDict[j][l]
|
num = numDict[j][l]
|
||||||
x[name] = [(str(round(((num/numNFTsGenerated)*100), 2)) + "%"), str(num)]
|
x[name] = [(str(round(((num / numNFTsGenerated) * 100), 2)) + "%"), str(num)]
|
||||||
|
|
||||||
completeData[i] = x
|
completeData[i] = x
|
||||||
|
|
||||||
|
@ -112,13 +213,13 @@ def check_Rarity(hierarchy, DNAListFormatted, save_path):
|
||||||
path = os.path.join(save_path, "RarityData.json")
|
path = os.path.join(save_path, "RarityData.json")
|
||||||
print(bcolors.OK + f"Rarity Data has been saved to {path}." + bcolors.RESET)
|
print(bcolors.OK + f"Rarity Data has been saved to {path}." + bcolors.RESET)
|
||||||
|
|
||||||
|
|
||||||
def check_Duplicates(DNAListFormatted):
|
def check_Duplicates(DNAListFormatted):
|
||||||
"""Checks if there are duplicates in DNAList before NFTRecord.json is sent to JSON file."""
|
"""Checks if there are duplicates in DNAList before NFTRecord.json is sent to JSON file."""
|
||||||
DNAList = []
|
DNAList = []
|
||||||
for i in DNAListFormatted:
|
for i in DNAListFormatted:
|
||||||
DNAList.append(list(i.keys())[0])
|
DNAList.append(list(i.keys())[0])
|
||||||
|
|
||||||
|
|
||||||
duplicates = 0
|
duplicates = 0
|
||||||
seen = set()
|
seen = set()
|
||||||
|
|
||||||
|
@ -130,6 +231,7 @@ def check_Duplicates(DNAListFormatted):
|
||||||
|
|
||||||
print(f"\nNFTRecord.json contains {duplicates} duplicate NFT DNA.")
|
print(f"\nNFTRecord.json contains {duplicates} duplicate NFT DNA.")
|
||||||
|
|
||||||
|
|
||||||
def check_FailedBatches(batch_json_save_path):
|
def check_FailedBatches(batch_json_save_path):
|
||||||
fail_state = False
|
fail_state = False
|
||||||
failed_batch = None
|
failed_batch = None
|
||||||
|
@ -151,6 +253,7 @@ def check_FailedBatches(batch_json_save_path):
|
||||||
|
|
||||||
return fail_state, failed_batch, failed_dna, failed_dna_index
|
return fail_state, failed_batch, failed_dna, failed_dna_index
|
||||||
|
|
||||||
|
|
||||||
# Raise Errors:
|
# Raise Errors:
|
||||||
def raise_Error_numBatches(maxNFTs, nftsPerBatch):
|
def raise_Error_numBatches(maxNFTs, nftsPerBatch):
|
||||||
"""Checks if number of Batches is less than maxNFTs, if not raises error."""
|
"""Checks if number of Batches is less than maxNFTs, if not raises error."""
|
||||||
|
@ -168,9 +271,10 @@ def raise_Error_numBatches(maxNFTs, nftsPerBatch):
|
||||||
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n{bcolors.RESET}"
|
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n{bcolors.RESET}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def raise_Error_ZeroCombinations():
|
def raise_Error_ZeroCombinations():
|
||||||
"""Checks if combinations is greater than 0, if so, raises error."""
|
"""Checks if combinations is greater than 0, if so, raises error."""
|
||||||
if get_combinations.get_combinations() == 0:
|
if get_combinations() == 0:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n"
|
f"\n{bcolors.ERROR}Blend_My_NFTs Error:\n"
|
||||||
f"The number of all possible combinations is ZERO. Please review your Blender scene and ensure it follows "
|
f"The number of all possible combinations is ZERO. Please review your Blender scene and ensure it follows "
|
||||||
|
@ -179,6 +283,7 @@ def raise_Error_ZeroCombinations():
|
||||||
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n{bcolors.RESET}"
|
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n{bcolors.RESET}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def raise_Error_numBatchesGreaterThan(numBatches):
|
def raise_Error_numBatchesGreaterThan(numBatches):
|
||||||
if numBatches < 1:
|
if numBatches < 1:
|
||||||
raise ValueError(
|
raise ValueError(
|
||||||
|
@ -189,8 +294,8 @@ def raise_Error_numBatchesGreaterThan(numBatches):
|
||||||
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n{bcolors.RESET}"
|
f"https://github.com/torrinworx/Blend_My_NFTs#blender-file-organization-and-structure\n{bcolors.RESET}"
|
||||||
)
|
)
|
||||||
|
|
||||||
# Raise Warnings:
|
|
||||||
|
|
||||||
|
# Raise Warnings:
|
||||||
def raise_Warning_maxNFTs(nftsPerBatch, collectionSize):
|
def raise_Warning_maxNFTs(nftsPerBatch, collectionSize):
|
||||||
"""
|
"""
|
||||||
Prints warning if nftsPerBatch is greater than collectionSize.
|
Prints warning if nftsPerBatch is greater than collectionSize.
|
||||||
|
@ -202,6 +307,7 @@ def raise_Warning_maxNFTs(nftsPerBatch, collectionSize):
|
||||||
f"The number of NFTs Per Batch you set is smaller than the NFT Collection Size you set.\n{bcolors.RESET}"
|
f"The number of NFTs Per Batch you set is smaller than the NFT Collection Size you set.\n{bcolors.RESET}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def raise_Warning_collectionSize(DNAList, collectionSize):
|
def raise_Warning_collectionSize(DNAList, collectionSize):
|
||||||
"""
|
"""
|
||||||
Prints warning if BMNFTs cannot generate requested number of NFTs from a given collectionSize.
|
Prints warning if BMNFTs cannot generate requested number of NFTs from a given collectionSize.
|
||||||
|
@ -211,9 +317,67 @@ def raise_Warning_collectionSize(DNAList, collectionSize):
|
||||||
print(f"\n{bcolors.WARNING} \nWARNING: \n"
|
print(f"\n{bcolors.WARNING} \nWARNING: \n"
|
||||||
f"Blend_My_NFTs cannot generate {collectionSize} NFTs."
|
f"Blend_My_NFTs cannot generate {collectionSize} NFTs."
|
||||||
f" Only {len(DNAList)} NFT DNA were generated."
|
f" Only {len(DNAList)} NFT DNA were generated."
|
||||||
|
|
||||||
f"\nThis might be for a number of reasons:"
|
f"\nThis might be for a number of reasons:"
|
||||||
f"\n a) Rarity is preventing combinations from being generated (See https://github.com/torrinworx/Blend_My_NFTs#notes-on-rarity-and-weighted-variants).\n"
|
f"\n a) Rarity is preventing combinations from being generated (See https://github.com/torrinworx/Blend_My_NFTs#notes-on-rarity-and-weighted-variants).\n"
|
||||||
f"\n b) Logic is preventing combinations from being generated (See https://github.com/torrinworx/Blend_My_NFTs#logic).\n"
|
f"\n b) Logic is preventing combinations from being generated (See https://github.com/torrinworx/Blend_My_NFTs#logic).\n"
|
||||||
f"\n c) The number of possible combinations of your NFT collection is too low. Add more Variants or Attributes to increase the recommended collection size.\n"
|
f"\n c) The number of possible combinations of your NFT collection is too low. Add more Variants or Attributes to increase the recommended collection size.\n"
|
||||||
f"\n{bcolors.RESET}")
|
f"\n{bcolors.RESET}")
|
||||||
|
|
||||||
|
|
||||||
|
# ======== LOADING ANIMATION ======== #
|
||||||
|
|
||||||
|
# This section is used for the loading animation used in the system console.
|
||||||
|
|
||||||
|
class Loader:
|
||||||
|
def __init__(self, desc="Loading...", end="Done!", timeout=0.1):
|
||||||
|
"""
|
||||||
|
A loader-like context manager
|
||||||
|
|
||||||
|
Args:
|
||||||
|
desc (str, optional): The loader's description. Defaults to "Loading...".
|
||||||
|
end (str, optional): Final print. Defaults to "Done!".
|
||||||
|
timeout (float, optional): Sleep time between prints. Defaults to 0.1.
|
||||||
|
"""
|
||||||
|
self.desc = desc
|
||||||
|
self.end = end
|
||||||
|
self.timeout = timeout
|
||||||
|
|
||||||
|
self._thread = Thread(target=self._animate, daemon=True)
|
||||||
|
self.steps = [
|
||||||
|
" [== ]",
|
||||||
|
" [ == ]",
|
||||||
|
" [ == ]",
|
||||||
|
" [ == ]",
|
||||||
|
" [ == ]",
|
||||||
|
" [ ==]",
|
||||||
|
" [ == ]",
|
||||||
|
" [ == ]",
|
||||||
|
" [ == ]",
|
||||||
|
" [ == ]",
|
||||||
|
]
|
||||||
|
self.done = False
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
self._thread.start()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def _animate(self):
|
||||||
|
for c in cycle(self.steps):
|
||||||
|
if self.done:
|
||||||
|
break
|
||||||
|
print(f"\r{self.desc} {c}", flush=True, end="")
|
||||||
|
sleep(self.timeout)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
self.start()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
self.done = True
|
||||||
|
cols = get_terminal_size((80, 20)).columns
|
||||||
|
print("\r" + " " * cols, end="", flush=True)
|
||||||
|
print(f"\r{self.end}", flush=True)
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, tb):
|
||||||
|
# handle exceptions with those variables ^
|
||||||
|
self.stop()
|
|
@ -53,7 +53,8 @@ def send_To_Record_JSON(input, reverse_order=False):
|
||||||
input.enableMaterials,
|
input.enableMaterials,
|
||||||
input.materialsFile,
|
input.materialsFile,
|
||||||
input.Blend_My_NFTs_Output,
|
input.Blend_My_NFTs_Output,
|
||||||
input.batch_json_save_path
|
input.batch_json_save_path,
|
||||||
|
input.enable_debug,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ import bpy
|
||||||
import random
|
import random
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
from .Constants import bcolors, removeList, remove_file_by_extension, save_result
|
from .Helpers import bcolors, removeList, remove_file_by_extension, save_result
|
||||||
|
|
||||||
|
|
||||||
def reconstructDNA(deconstructedDNA):
|
def reconstructDNA(deconstructedDNA):
|
||||||
|
|
|
@ -7,7 +7,7 @@ import bpy
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
from .Constants import bcolors, removeList, remove_file_by_extension, save_result
|
from .Helpers import bcolors, removeList, remove_file_by_extension, save_result
|
||||||
|
|
||||||
|
|
||||||
def select_material(materialList, variant, enableRarity):
|
def select_material(materialList, variant, enableRarity):
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
import bpy
|
import bpy
|
||||||
import random
|
import random
|
||||||
|
|
||||||
from .Constants import bcolors, removeList, remove_file_by_extension
|
from .Helpers import bcolors, removeList, remove_file_by_extension
|
||||||
|
|
||||||
|
|
||||||
def createDNArarity(hierarchy):
|
def createDNArarity(hierarchy):
|
||||||
|
|
|
@ -6,7 +6,7 @@ import os
|
||||||
import json
|
import json
|
||||||
import shutil
|
import shutil
|
||||||
|
|
||||||
from .Constants import bcolors, removeList, remove_file_by_extension
|
from .Helpers import bcolors, removeList, remove_file_by_extension
|
||||||
|
|
||||||
|
|
||||||
def reformatNFTCollection(refactor_panel_input):
|
def reformatNFTCollection(refactor_panel_input):
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
import bpy
|
|
||||||
|
|
||||||
from . import DNA_Generator
|
|
||||||
|
|
||||||
|
|
||||||
def get_combinations():
|
|
||||||
"""
|
|
||||||
Returns "combinations", the number of all possible NFT DNA for a given Blender scene formatted to BMNFTs conventions
|
|
||||||
combinations.
|
|
||||||
"""
|
|
||||||
|
|
||||||
hierarchy = DNA_Generator.get_hierarchy()
|
|
||||||
hierarchyByNum = []
|
|
||||||
|
|
||||||
for i in hierarchy:
|
|
||||||
# Ignore Collections with nothing in them
|
|
||||||
if len(hierarchy[i]) != 0:
|
|
||||||
hierarchyByNum.append(len(hierarchy[i]))
|
|
||||||
else:
|
|
||||||
print(f"The following collection has been identified as empty: {i}")
|
|
||||||
|
|
||||||
combinations = 1
|
|
||||||
for i in hierarchyByNum:
|
|
||||||
combinations = combinations*i
|
|
||||||
|
|
||||||
return combinations
|
|
|
@ -1,69 +0,0 @@
|
||||||
from itertools import cycle
|
|
||||||
from shutil import get_terminal_size
|
|
||||||
from threading import Thread
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
|
|
||||||
class Loader:
|
|
||||||
def __init__(self, desc="Loading...", end="Done!", timeout=0.1):
|
|
||||||
"""
|
|
||||||
A loader-like context manager
|
|
||||||
|
|
||||||
Args:
|
|
||||||
desc (str, optional): The loader's description. Defaults to "Loading...".
|
|
||||||
end (str, optional): Final print. Defaults to "Done!".
|
|
||||||
timeout (float, optional): Sleep time between prints. Defaults to 0.1.
|
|
||||||
"""
|
|
||||||
self.desc = desc
|
|
||||||
self.end = end
|
|
||||||
self.timeout = timeout
|
|
||||||
|
|
||||||
self._thread = Thread(target=self._animate, daemon=True)
|
|
||||||
self.steps = [
|
|
||||||
" [== ]",
|
|
||||||
" [ == ]",
|
|
||||||
" [ == ]",
|
|
||||||
" [ == ]",
|
|
||||||
" [ == ]",
|
|
||||||
" [ ==]",
|
|
||||||
" [ == ]",
|
|
||||||
" [ == ]",
|
|
||||||
" [ == ]",
|
|
||||||
" [ == ]",
|
|
||||||
]
|
|
||||||
self.done = False
|
|
||||||
|
|
||||||
def start(self):
|
|
||||||
self._thread.start()
|
|
||||||
return self
|
|
||||||
|
|
||||||
def _animate(self):
|
|
||||||
for c in cycle(self.steps):
|
|
||||||
if self.done:
|
|
||||||
break
|
|
||||||
print(f"\r{self.desc} {c}", flush=True, end="")
|
|
||||||
sleep(self.timeout)
|
|
||||||
|
|
||||||
def __enter__(self):
|
|
||||||
self.start()
|
|
||||||
|
|
||||||
def stop(self):
|
|
||||||
self.done = True
|
|
||||||
cols = get_terminal_size((80, 20)).columns
|
|
||||||
print("\r" + " " * cols, end="", flush=True)
|
|
||||||
print(f"\r{self.end}", flush=True)
|
|
||||||
|
|
||||||
def __exit__(self, exc_type, exc_value, tb):
|
|
||||||
# handle exceptions with those variables ^
|
|
||||||
self.stop()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
with Loader("Loading with context manager..."):
|
|
||||||
for i in range(10):
|
|
||||||
sleep(0.25)
|
|
||||||
|
|
||||||
loader = Loader("Loading with object...", "That was fast!", 0.05).start()
|
|
||||||
for i in range(10):
|
|
||||||
sleep(0.25)
|
|
||||||
loader.stop()
|
|
Ładowanie…
Reference in New Issue