diff --git a/__init__.py b/__init__.py index b395340..c0550c0 100644 --- a/__init__.py +++ b/__init__.py @@ -26,13 +26,15 @@ importlib.reload(Exporter) importlib.reload(Refactorer) importlib.reload(get_combinations) + importlib.reload(Uploader) else: from .main import \ DNA_Generator, \ Batch_Sorter, \ Exporter, \ Refactorer, \ - get_combinations + get_combinations, \ + Uploader # User input Property Group: class BMNFTS_PGT_MyProperties(bpy.types.PropertyGroup): @@ -96,6 +98,7 @@ class BMNFTS_PGT_MyProperties(bpy.types.PropertyGroup): cardanoMetaDataBool: bpy.props.BoolProperty(name="Cardano Cip") solanaMetaDataBool: bpy.props.BoolProperty(name="Solana Metaplex") erc721MetaData: bpy.props.BoolProperty(name="ERC721") + openSeaMetaData: bpy.props.BoolProperty(name="OpenSea") # API Panel properties: apiKey: bpy.props.StringProperty(name="API Key", subtype='PASSWORD') @@ -129,6 +132,14 @@ class BMNFTS_PGT_MyProperties(bpy.types.PropertyGroup): # ERC721 Custom Metadata Fields erc721_description: bpy.props.StringProperty(name="ERC721 description") + + openSea_description: bpy.props.StringProperty(name="Description") + + nftport_api_key: bpy.props.StringProperty(name="Nft Port API Key") + + wallet_address: bpy.props.StringProperty(name="Wallet address") + + contract_address: bpy.props.StringProperty(name="Contract address") def make_directories(save_path): @@ -246,10 +257,12 @@ class refactor_panel_input: cardanoMetaDataBool = bpy.context.scene.my_tool.cardanoMetaDataBool solanaMetaDataBool = bpy.context.scene.my_tool.solanaMetaDataBool erc721MetaData = bpy.context.scene.my_tool.erc721MetaData + openSeaMetaData = bpy.context.scene.my_tool.openSeaMetaData cardano_description = bpy.context.scene.my_tool.cardano_description solana_description = bpy.context.scene.my_tool.solana_description erc721_description = bpy.context.scene.my_tool.erc721_description + openSea_description = bpy.context.scene.my_tool.openSea_description Blend_My_NFTs_Output, batch_json_save_path, nftBatch_save_path = make_directories(save_path) @@ -261,6 +274,26 @@ class refactor_panel_input: def invoke(self, context, event): return context.window_manager.invoke_confirm(self, event) +class uploadNFTs(bpy.types.Operator): + bl_idname = 'uploader.nfts' + bl_label = 'Upload NFTs' + bl_description = 'Upload NFTs.' + bl_options = {"REGISTER", "UNDO"} + + def execute(self, context): + + class uploader_panel_input: + save_path = bpy.path.abspath(bpy.context.scene.my_tool.save_path) + nftport_api_key = bpy.context.scene.my_tool.nftport_api_key.strip() + contract_address = bpy.context.scene.my_tool.contract_address.strip() + wallet_address = bpy.context.scene.my_tool.wallet_address.strip() + + Uploader.uploadNFTCollection(uploader_panel_input) + + self.report({'INFO'}, "NFTs Uploaded") + + return {"FINISHED"} + # Create Data Panel: class BMNFTS_PT_CreateData(bpy.types.Panel): bl_label = "Create NFT Data" @@ -386,6 +419,12 @@ def draw(self, context): row.operator("wm.url_open", text="ERC721 Metadata Documentation", icon='URL').url = "https://docs.opensea.io/docs/metadata-standards" + row = layout.row() + row.prop(mytool, "openSeaMetaData") + if bpy.context.scene.my_tool.openSeaMetaData: + row = layout.row() + row.prop(mytool, "openSea_description") + row = layout.row() row.prop(mytool, "enableCustomFields") if bpy.context.scene.my_tool.enableCustomFields: @@ -412,6 +451,33 @@ def draw(self, context): row.operator("wm.url_open", text="Documentation", icon='URL').url = "https://github.com/torrinworx/Blend_My_NFTs" +class BMNFTS_PT_NFTPORT_Uploader(bpy.types.Panel): + bl_label = "NFTPort Uploader" + bl_idname = "BMNFTS_PT_NFTPORT_Uploader" + bl_space_type = 'VIEW_3D' + bl_region_type = 'UI' + bl_category = 'Blend_My_NFTs' + + def draw(self, context): + layout = self.layout + scene = context.scene + mytool = scene.my_tool + + row = layout.row() + row.prop(mytool, "nftport_api_key") + + row = layout.row() + row.prop(mytool, "contract_address") + + row = layout.row() + row.prop(mytool, "wallet_address") + + row = layout.row() + self.layout.operator("uploader.nfts", icon='DISCLOSURE_TRI_RIGHT', text="Upload To IPFS & Mint") + row = layout.row() + row.operator("wm.url_open", text="Documentation", + icon='URL').url = "https://github.com/aizwellenstan/Blend_My_NFTs_Doc" + # # Materials Panel: # # class BMNFTS_PT_MATERIALS_Panel(bpy.types.Panel): @@ -457,6 +523,7 @@ def redraw_panel(): BMNFTS_PT_GenerateNFTs, BMNFTS_PT_Refactor, BMNFTS_PT_Documentation, + BMNFTS_PT_NFTPORT_Uploader, # Other panels: # BMNFTS_PT_MATERIALS_Panel, @@ -465,6 +532,7 @@ def redraw_panel(): createData, exportNFTs, refactor_Batches, + uploadNFTs ) def register(): diff --git a/main/Metadata.py b/main/Metadata.py index 3f33a77..c2cf96b 100644 --- a/main/Metadata.py +++ b/main/Metadata.py @@ -104,8 +104,45 @@ def returnErc721MetaData(name, NFT_DNA, NFT_Variants, custom_Fields_File, enable return metaDataDictErc721 +def returnOpenSeaMetaData(name, NFT_DNA, NFT_Variants, custom_Fields_File, enableCustomFields, erc721_description): + dna = NFT_DNA.replace("-", "") + metaDataDictErc721 = { + "name": name+"_"+dna, + "description": erc721_description, + "image": "", + "attributes": None, + } + + metaDataDictErc721['custom_fields'] = { + 'edition': int(dna) + } + + attributes = [] + + for i in NFT_Variants: + dictionary = { + "trait_type": i, + "value": NFT_Variants[i] + } + + attributes.append(dictionary) + + # Custom Fields: + if enableCustomFields: + custom_Fields = json.load(open(custom_Fields_File)) + for i in custom_Fields: + dictionary = { + "trait_type": i, + "value": custom_Fields[i] + } + attributes.append(dictionary) + + metaDataDictErc721["attributes"] = attributes + + return metaDataDictErc721 if __name__ == '__main__': returnSolanaMetaData() returnCardanoMetaData() returnErc721MetaData() + returnOpenSeaMetaData() diff --git a/main/Refactorer.py b/main/Refactorer.py index c1c97ee..be919bb 100644 --- a/main/Refactorer.py +++ b/main/Refactorer.py @@ -64,6 +64,7 @@ def renameMetaData(rename_MetaData_Variables): cardanoMetaDataPath = os.path.join(rename_MetaData_Variables.completeCollPath, "Cardano_metaData") solanaMetaDataPath = os.path.join(rename_MetaData_Variables.completeCollPath, "Solana_metaData") erc721MetaDataPath = os.path.join(rename_MetaData_Variables.completeCollPath, "Erc721_metaData") + openSeaMetaDataPath = os.path.join(rename_MetaData_Variables.completeCollPath, "OpenSea_metaData") for i in metaDataListOld: name, NFT_DNA, NFT_Variants = getMetaDataDirty(rename_MetaData_Variables.completeMetaDataPath, i) @@ -103,6 +104,19 @@ def renameMetaData(rename_MetaData_Variables): metaDataDictErc721 = Metadata.returnErc721MetaData(erc721NewName, NFT_DNA, NFT_Variants, rename_MetaData_Variables.custom_Fields_File, rename_MetaData_Variables.enableCustomFields, rename_MetaData_Variables.erc721_description) sendMetaDataToJson(metaDataDictErc721, erc721MetaDataPath, erc721JsonNew) + + if rename_MetaData_Variables.openSeaMetaData: + if not os.path.exists(openSeaMetaDataPath): + os.mkdir(openSeaMetaDataPath) + + openSeaFileName =name.split("_")[0] + "_" + str(file_num)+".json" + openSeaNewName = name.split("_")[0] + if len(rename_MetaData_Variables.openSea_description) < 1: + rename_MetaData_Variables.openSea_description = openSeaNewName + + metaDataDictOpenSea = Metadata.returnOpenSeaMetaData(openSeaNewName, NFT_DNA, NFT_Variants, rename_MetaData_Variables.custom_Fields_File, rename_MetaData_Variables.enableCustomFields, rename_MetaData_Variables.openSea_description) + + sendMetaDataToJson(metaDataDictOpenSea, openSeaMetaDataPath, openSeaFileName) return def reformatNFTCollection(refactor_panel_input): @@ -220,6 +234,7 @@ class rename_MetaData_Variables: cardanoMetaDataBool = refactor_panel_input.cardanoMetaDataBool solanaMetaDataBool = refactor_panel_input.solanaMetaDataBool erc721MetaData = refactor_panel_input.erc721MetaData + openSeaMetaData = refactor_panel_input.openSeaMetaData custom_Fields_File = refactor_panel_input.custom_Fields_File enableCustomFields = refactor_panel_input.enableCustomFields @@ -228,6 +243,7 @@ class rename_MetaData_Variables: cardano_description = refactor_panel_input.cardano_description solana_description = refactor_panel_input.solana_description erc721_description = refactor_panel_input.erc721_description + openSea_description = refactor_panel_input.openSea_description renameMetaData(rename_MetaData_Variables) diff --git a/main/Uploader.py b/main/Uploader.py new file mode 100644 index 0000000..8143893 --- /dev/null +++ b/main/Uploader.py @@ -0,0 +1,105 @@ +""" +NftGenerator Using NFTPort +@author https://github.com/aizwellenstan +""" + +import os +import json +import requests +from os import listdir +from os.path import isfile, join +import time + +def nftPortFileUploader(apikey, file): + response = requests.post( + "https://api.nftport.xyz/v0/files", + headers={"Authorization": apikey}, + files={"file": file} + ) + + return json.loads(response.text) + +def nftPortMetaUploader(apikey, metaData): + response = requests.post( + "https://api.nftport.xyz/v0/metadata", + headers={"Authorization": apikey, "Content-Type": "appication/json"}, + data=json.dumps(metaData, indent=4) + ) + + return json.loads(response.text) + +def minter(apikey, meta, CONTRACT_ADDRESS, MINT_TO_ADDRESS): + mintInfo = { + 'chain': 'polygon', + 'contract_address': CONTRACT_ADDRESS, + 'metadata_uri': meta['metadata_uri'], + 'mint_to_address': MINT_TO_ADDRESS, + 'token_id': meta['custom_fields']['edition'] + } + response = requests.post( + "https://api.nftport.xyz/v0/mints/customizable", + headers={"Authorization": apikey, "Content-Type": "appication/json"}, + data=json.dumps(mintInfo, indent=4) + ) + + return json.loads(response.text) + +def uploadToNFTPort(nftport_panel_input): + glbFile = f"{nftport_panel_input.modelPath}\{nftport_panel_input.fileName}.glb" + with open(glbFile, 'rb') as modelFile: + res = nftPortFileUploader(nftport_panel_input.apikey, modelFile) + resModelUrl = f"{res['ipfs_url']}?fileName={nftport_panel_input.fileName}.glb" + + pngFile = f"{nftport_panel_input.imagePath}\{nftport_panel_input.fileName}.png" + with open(pngFile, 'rb') as imageFile: + res = nftPortFileUploader(nftport_panel_input.apikey, imageFile) + resImageUrl = f"{res['ipfs_url']}" + + jsonFile = f"{nftport_panel_input.metaDataPath}\{nftport_panel_input.fileName}.json" + with open(jsonFile, 'r') as inputFile: + metaData = json.load(inputFile) + metaData['animation_url'] = resModelUrl + metaData['file_url'] = resImageUrl + + res = nftPortMetaUploader(nftport_panel_input.apikey, metaData) + metaData['metadata_uri'] = res['metadata_uri'] + with open(jsonFile, 'w') as outputFile: + json.dump(metaData, outputFile, ensure_ascii=False, indent=4) + + return metaData + +def uploadNFTCollection(uploader_panel_input): + completeCollPath = os.path.join(uploader_panel_input.save_path, "Blend_My_NFTs Output", "Complete_Collection") + completeImagePath = os.path.join(completeCollPath, "Images") + completeAnimationsPath = os.path.join(completeCollPath, "Animations") + completeModelsPath = os.path.join(completeCollPath, "Models") + openSeaMetaDataPath = os.path.join(completeCollPath, "OpenSea_metaData") + + class nftport_panel_input: + apikey = uploader_panel_input.nftport_api_key + modelPath = completeModelsPath + imagePath = completeImagePath + metaDataPath = openSeaMetaDataPath + fileName = "" + + onlyfiles = [f for f in listdir(completeModelsPath) if isfile(join(completeModelsPath, f))] + + print(onlyfiles) + allMetadata = [] + for file in onlyfiles: + fileName = file.split(".")[0] + nftport_panel_input.fileName = fileName + metaData = uploadToNFTPort(nftport_panel_input) + allMetadata.append(metaData) + print(allMetadata) + for meta in allMetadata: + minted = minter(nftport_panel_input.apikey, meta, uploader_panel_input.contract_address, uploader_panel_input.wallet_address) + print(minted) + file = f"{nftport_panel_input.metaDataPath}\{meta['custom_fields']['edition']}_minted.json" + with open(file, 'w') as outputFile: + json.dump(minted, outputFile, ensure_ascii=False, indent=4) + print("All Minted") + + +if __name__ == '__main__': + uploadNFTCollection()