diff --git a/README.md b/README.md index 84efbfc7a..80dda8ecf 100644 --- a/README.md +++ b/README.md @@ -214,9 +214,9 @@ sublayerOptions: { # Metadata Name and Number -Name + Number prefix and reset for configuration sets +Name + Number prefix, suffix, and counter reset for configuration sets -If you are using the generator with multiple `layerConfiguration` objects to generate different species/genders/types, it is possible to add a name prefix and a reset counter for the name, so the token names start at `1` for each type. +If you are using the generator with multiple `layerConfiguration` objects to generate different species/genders/types, it is possible to add a name prefix and a suffix, as well as a reset counter for the names, so the token names start at `1` for each type. for example, if you are creating multiple animals, and each animal should start with `Animal #1`, but the token numbers should increment as normal, you can use the following `namePrefix` and `resetNameIndex` properties to acheive this. @@ -224,6 +224,9 @@ for example, if you are creating multiple animals, and each animal should start growEditionSizeTo: 10, namePrefix: "Lion", resetNameIndex: true, // this will start the Lion count at #1 instead of #6 + nameSuffix: "Set-A", // add a suffix after the number. if resetNameIndex is on too, put the reseted counter after the suffix + descriptionOverwrite: "Unique Description For Layer Set-A which {name} is a member of.", // LayerConfig spesific descriptions. Use {name} to embed asset names. + layersOrder: [ { name: "Background" }, { name: "Eyeball" }, @@ -234,6 +237,14 @@ for example, if you are creating multiple animals, and each animal should start You may choose to omit the `resetNameIndex` or set it to false if you would instead like each layer set to use the token (\_edition) number in the name–it does this by default. + +If you want each config set to have a sub group name other than the prefix, you can add it with `nameSuffix` property. And form a name like `Animal #5 Monkey`. +If you use `resetNameIndex: true` propert together with `nameSuffix` property, you the reset counter will be added after the suffix name, and form a name like `Animal #5 - Monkey #3`. + +You can also set different descriptions for different layer configuration sets by using `descriptionOverwrite` property. Write a string that will overwrite the default description text only for the current layer set. + +If you want you can embed the generated asset names into the description texts by writing `{name}` in the string and make each description unique. This method works for the default description text too. You can form unique descriptions such as; `Unique Description For Layer Set-A which "Animal #5 - Monkey #3" is a member of.` + # Chance of "NONE" or skipping a trait If you would like any given layer or sublayer to have a chance at _rolling_ `NONE`, you can do this by adding a blank PNG to the folder, and giving it the name of `NONE` + the weight you would like to use for it's chance of being chosen–identical to any other layer. @@ -406,6 +417,21 @@ If you would like to print additional logging, use the `-d` flag node utils/removeTrait.js "Background" -d ``` +# Remove Metadata Content + +If you need to remove an unwanted top-level data from json files such as `dna` or `external_url` you can use the `removeMetadataContent` util command. +Note that, this utility is a bit different than `removeTrait` util, as you can delete a necessary line such as `name` or `image` too. So, use this with caution. + +Use this util by running a command like the following; +``` +node utils/removeMetadataContent.js "external_url" +``` + +If you would like to print additional logging, use the `-d` flag +``` +node utils/removeMetadataContent.js "external_url" -d +``` + # Randomly Insert Rare items - Replace Util If you would like to manually add 'hand drawn' or unique versions into the pool of generated items, this utility takes a source folder (of your new artwork) and inserts it into the `build` directory, assigning them to random id's. diff --git a/package-lock.json b/package-lock.json index 4ba89076f..21bc8c80c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hashlips_art_engine", - "version": "1.0.5", + "version": "1.0.6", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -38,6 +38,14 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" }, + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, "aproba": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", @@ -57,6 +65,16 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bn.js": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.0.tgz", + "integrity": "sha512-D7iWRBvnZE8ecXiLj/9wbxH7Tk79fAh8IHaTNq1RWRixsS02W+5qS+iE9yq6RYl0asXx5tw0bLhmT5pIfbSquw==" + }, "brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -66,6 +84,15 @@ "concat-map": "0.0.1" } }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "canvas": { "version": "2.8.0", "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.8.0.tgz", @@ -76,10 +103,14 @@ "simple-get": "^3.0.3" } }, - "charenc": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", - "integrity": "sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=" + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } }, "chownr": { "version": "2.0.0", @@ -91,6 +122,24 @@ "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=" }, + "color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "requires": { + "color-name": "~1.1.4" + } + }, + "color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==" + }, "concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -106,11 +155,6 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, - "crypt": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", - "integrity": "sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=" - }, "debug": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", @@ -137,6 +181,48 @@ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=" }, + "eslint-plugin-es": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-es/-/eslint-plugin-es-3.0.1.tgz", + "integrity": "sha512-GUmAsJaN4Fc7Gbtl8uOBlayo2DqhwWvEzykMHSCZHU3XdJ+NSzzZcVhXh3VxX5icqQ+oQdIEawXX8xkR3mIFmQ==", + "requires": { + "eslint-utils": "^2.0.0", + "regexpp": "^3.0.0" + } + }, + "eslint-plugin-node": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-node/-/eslint-plugin-node-11.1.0.tgz", + "integrity": "sha512-oUwtPJ1W0SKD0Tr+wqu92c5xuCeQqB3hSCHasn/ZgjFdA9iDGNkNf2Zi9ztY7X+hNuMib23LNGRm6+uN+KLE3g==", + "requires": { + "eslint-plugin-es": "^3.0.0", + "eslint-utils": "^2.0.0", + "ignore": "^5.1.1", + "minimatch": "^3.0.4", + "resolve": "^1.10.1", + "semver": "^6.1.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==" + } + } + }, + "eslint-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", + "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "requires": { + "eslint-visitor-keys": "^1.1.0" + } + }, + "eslint-visitor-keys": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", + "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==" + }, "fs-minipass": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", @@ -150,6 +236,11 @@ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, "gauge": { "version": "2.7.4", "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", @@ -178,6 +269,19 @@ "path-is-absolute": "^1.0.0" } }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, "has-unicode": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", @@ -192,6 +296,16 @@ "debug": "4" } }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "ignore": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", + "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + }, "inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -206,6 +320,14 @@ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" }, + "is-core-module": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", + "requires": { + "has": "^1.0.3" + } + }, "is-fullwidth-code-point": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", @@ -219,6 +341,38 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" }, + "keccak": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.2.tgz", + "integrity": "sha512-PyKKjkH53wDMLGrvmRGSNWgmSxZOUqbnXwKL9tmgbFYA1iAYqW21kfR7mZXV0MlESiefxQQE9X9fTa3X+2MPDQ==", + "requires": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "dependencies": { + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + } + } + }, + "keccak256": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/keccak256/-/keccak256-1.0.6.tgz", + "integrity": "sha512-8GLiM01PkdJVGUhR1e6M/AvWnSqYS0HaERI+K/QtStGDGlSTx2B1zTqZk4Zlqu5TxHJNTxWAdP9Y+WI50OApUw==", + "requires": { + "bn.js": "^5.2.0", + "buffer": "^6.0.3", + "keccak": "^3.0.2" + } + }, "lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", @@ -287,11 +441,21 @@ "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==" }, + "node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, "node-fetch": { "version": "2.6.2", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.2.tgz", "integrity": "sha512-aLoxToI6RfZ+0NOjmWAgn9+LEd30YCkJKFSyWacNZdEKTit/ZMcKjGkTRo8uWEsnIb/hfKecNPEbln02PdWbcA==" }, + "node-gyp-build": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.3.0.tgz", + "integrity": "sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q==" + }, "nopt": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", @@ -334,6 +498,11 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" }, + "path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -353,6 +522,20 @@ "util-deprecate": "~1.0.1" } }, + "regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==" + }, + "resolve": { + "version": "1.20.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.20.0.tgz", + "integrity": "sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A==", + "requires": { + "is-core-module": "^2.2.0", + "path-parse": "^1.0.6" + } + }, "rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", @@ -379,15 +562,6 @@ "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=" }, - "sha1": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", - "integrity": "sha1-rdqnqTFo85PxnrKxUJFhjicA+Eg=", - "requires": { - "charenc": ">= 0.0.1", - "crypt": ">= 0.0.1" - } - }, "signal-exit": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.4.tgz", @@ -434,6 +608,14 @@ "ansi-regex": "^2.0.0" } }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "requires": { + "has-flag": "^4.0.0" + } + }, "tar": { "version": "6.1.11", "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.11.tgz", diff --git a/package.json b/package.json index fc0952f3b..1a7e7bb3c 100644 --- a/package.json +++ b/package.json @@ -18,7 +18,8 @@ "build": "node index.js", "generate:solana": "node index.js && node utils/metaplex.js", "generate:cardano": "node index.js && node utils/cardano.js", - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "preview": "node utils/createPreviewCollage.js" }, "author": "Daniel Eugene Botha (HashLips), NFTChef", "license": "MIT", @@ -29,4 +30,4 @@ "eslint-plugin-node": "^11.1.0", "keccak256": "^1.0.3" } -} \ No newline at end of file +} diff --git a/src/config.js b/src/config.js index 6fa754115..f2cc24eea 100644 --- a/src/config.js +++ b/src/config.js @@ -12,18 +12,47 @@ const description = "This is the description of your NFT project, remember to replace this"; const baseUri = "ipfs://NewUriToReplace"; +const baseExternalUrl = "https://test.com/collection?asset="; //Base url for extelnal link under the image on OpenSea. The edition number will be appended. Leave empty "" to remove. - BB + const outputJPEG = false; // if false, the generator outputs png's // if you use an empty/transparent file, set the name here. const emptyLayerName = "NONE"; -//IF you need a provenance hash, turn this on +//IF you need a provenance hash, turn this on const hashImages = true; const layerConfigurations = [ + { - growEditionSizeTo: 11, - // namePrefix: "Monkey", Use to add a name to Metadata `name:` + growEditionSizeTo: 5, + namePrefix: "Monkey", + resetNameIndex: true, // this will start the count at #1 for this layerconfig + nameSuffix: "Set A", // add a suffix after the number. if resetNameIndex is on too, put the reseted counter after the suffix - BB + descriptionOverwrite: "{name} with Unique Description For Layer Set A", // LayerConfig spesific descriptions. Use {name} to embed asset names. + layersOrder: [ + { + name: "Back Accessory", + options: { + bypassDNA: true, + }, + }, + { name: "Head" }, + { name: "Clothes" }, + { name: "Eyes" }, + { name: "Hair" }, + { name: "Head Accessory" }, + { name: "Shirt Accessories" }, + ], + }, + + { + growEditionSizeTo: 10, + namePrefix: "Monkey", + resetNameIndex: true, // this will start the count at #1 for this layerconfig + nameSuffix: "Set B", // add a suffix after the number. if resetNameIndex is on too, put the reseted counter after the suffix - BB + descriptionOverwrite: "Description for {name} from Layer Set B", // LayerConfig spesific descriptions. Use {name} to embed asset names. + layersOrder: [ { name: "Background" }, { @@ -40,19 +69,10 @@ const layerConfigurations = [ { name: "Shirt Accessories" }, ], }, - // { - // growEditionSizeTo: 10, - // namePrefix: "Lion", - // resetNameIndex: true, // this will start the Lion count at #1 instead of #6 - // layersOrder: [ - // { name: "Background" }, - // { name: "Hats" }, - // { name: "Male Hair" }, - // ], - // }, ]; -/** + +/** * Incompatible items can be added to this object by a files cleanName * This works in layer order, meaning, you need to define the layer that comes * first as the Key, and the incompatible items that _may_ come after. @@ -65,7 +85,8 @@ const incompatible = { // White: ["rare-Pink-Pompadour"], }; -/** + +/** * Require combinations of files when constructing DNA, this bypasses the * randomization and weights. * @@ -74,6 +95,7 @@ const incompatible = { * the items in the array are "required" items that should be pulled from folders * further in the stack */ + const forcedCombinations = { // floral: ["MetallicShades", "Golden Sakura"], }; @@ -102,7 +124,10 @@ const background = { brightness: "80%", }; -const extraMetadata = {}; +const extraMetadata = { + // You can add extra info for your collection here. Such as the artist name. + "Artist": "A person", +}; const extraAttributes = () => [ // Optionally, if you need to overwrite one of your layers attributes. @@ -152,6 +177,7 @@ module.exports = { layersDir, format, baseUri, + baseExternalUrl, description, background, uniqueDnaTorrance, diff --git a/src/main.js b/src/main.js index fd9f9b471..b8e1e1901 100644 --- a/src/main.js +++ b/src/main.js @@ -18,6 +18,7 @@ const { layersDir, format, baseUri, + baseExternalUrl, description, background, uniqueDnaTorrance, @@ -256,8 +257,12 @@ const drawBackground = (canvasContext) => { const addMetadata = (_dna, _edition, _prefixData) => { let dateTime = Date.now(); - const { _prefix, _offset, _imageHash } = _prefixData; - + const { + _name, + _description, + _imageHash, + } = _prefixData; + const combinedAttrs = [...attributesList, ...extraAttributes()]; const cleanedAttrs = combinedAttrs.reduce((acc, current) => { const x = acc.find((item) => item.trait_type === current.trait_type); @@ -270,9 +275,16 @@ const addMetadata = (_dna, _edition, _prefixData) => { let tempMetadata = { dna: hash(_dna), - name: `${_prefix ? _prefix + " " : ""}#${_edition - _offset}`, - description: description, + + name: _name, + + description: _description, + + // Adds external_url if the baseExternalUrl in config is not empty, combines it with edition numbers. - BB + ...(baseExternalUrl !== "" && { external_url: `${baseExternalUrl}${_edition}` }), + image: `${baseUri}/${_edition}${outputJPEG ? ".jpg" : ".png"}`, + ...(hashImages === true && { imageHash: _imageHash }), edition: _edition, date: dateTime, @@ -685,11 +697,27 @@ const postProcessMetadata = (layerData) => { return acc; }, 0); } + + // if there's a suffix for the current configIndex, then + // add it after the counter number + // if resetNameIndex is on too, the resetted counter will be added after the suffix + const _suffix = layerConfigurations[layerConfigIndex].nameSuffix + ? layerConfigurations[layerConfigIndex].nameSuffix + : null; + + // New name builder. It can form names like; "PREFIX #10 - SUFFIX #2". + const _name = `${_prefix ? `${_prefix} ` : ``}#${_suffix ? abstractedIndexes[0] : abstractedIndexes[0] - _offset}${_suffix ? ` ${_suffix}${layerConfigurations[layerConfigIndex].resetNameIndex ? ` #${abstractedIndexes[0] - _offset}` : ``}` : ``}`; + // New description builder, it can embed the asset name, AND overwrite the description for different layerConfigs. + // Can form unique descriptions like; "Item #10 is an art piece from Collection X". + const _description = (layerConfigurations[layerConfigIndex].descriptionOverwrite + ? layerConfigurations[layerConfigIndex].descriptionOverwrite + : description).replace(/{name}/g, _name); + return { _imageHash, - _prefix, - _offset, + _name, + _description, }; }; @@ -698,11 +726,11 @@ const outputFiles = (abstractedIndexes, layerData) => { // Save the canvas buffer to file saveImage(abstractedIndexes[0]); - const { _imageHash, _prefix, _offset } = postProcessMetadata(layerData); + const { _imageHash, _name, _description } = postProcessMetadata(layerData); addMetadata(newDna, abstractedIndexes[0], { - _prefix, - _offset, + _name, + _description, _imageHash, }); diff --git a/utils/createPreviewCollage.js b/utils/createPreviewCollage.js index 724cda9a2..931f49e5e 100644 --- a/utils/createPreviewCollage.js +++ b/utils/createPreviewCollage.js @@ -8,7 +8,7 @@ const { createCanvas, loadImage } = require("canvas"); const buildDir = `${basePath}/build`; console.log(path.join(basePath, "/src/config.js")); -const { preview } = require(path.join(basePath, "/src/config.js")); +const { preview, outputJPEG } = require(path.join(basePath, "/src/config.js")); // read json data const rawdata = fs.readFileSync(`${basePath}/build/json/_metadata.json`); @@ -37,7 +37,7 @@ const saveProjectPreviewImage = async (_data) => { // Don't want to rely on "edition" for assuming index for (let index = 0; index < _data.length; index++) { const nft = _data[index]; - await loadImage(`${buildDir}/images/${nft.edition}.png`).then((image) => { + await loadImage(`${buildDir}/images/${nft.edition}.${ outputJPEG ? `jpg` : `png` }`).then((image) => { previewCtx.drawImage( image, thumbWidth * (index % thumbPerRow), diff --git a/utils/removeMetadataContent.js b/utils/removeMetadataContent.js new file mode 100644 index 000000000..ee3617214 --- /dev/null +++ b/utils/removeMetadataContent.js @@ -0,0 +1,80 @@ +/* +--------------- +This utility can be used for removing unwanted data from json files such as "dna" or "external_url". +Created by modifying removeTrait.js utility. + +Use by running the following command; +``` +node utils/removeMetadataContent.js "Background" +``` + +or for additional logging, use with the `-d` flag +``` +node utils/removeMetadataContent.js "Background" -d +``` + +- BB +--------------- +*/ + + +"use strict"; + +const isLocal = typeof process.pkg === "undefined"; +const basePath = isLocal ? process.cwd() : path.dirname(process.execPath); +const fs = require("fs"); +const path = require("path"); +const { Command } = require("commander"); +const program = new Command(); + +const chalk = require("chalk"); +const jsonDir = `${basePath}/build/json`; +const metadataFilePath = `${basePath}/build/json/_metadata.json`; + +const getIndividualJsonFiles = () => { + return fs + .readdirSync(jsonDir) + .filter((item) => /^[0-9]{1,6}.json/g.test(item)); +}; + +program + .argument("") + .option("-d, --debug", "display some debugging") + .action((target, options, command) => { + const jsonFiles = getIndividualJsonFiles(); + options.debug + ? console.log( + `Found ${jsonFiles.length} json files in "${jsonDir}" to process` + ) + : null; + + console.log(chalk.greenBright.inverse(`Removing ${target}`)); + jsonFiles.forEach((filename) => { + // read the contents + options.debug ? console.log(`removing ${target} from ${filename}`) : null; + const contents = JSON.parse(fs.readFileSync(`${jsonDir}/${filename}`)); + + const hasTarget = contents.hasOwnProperty(target); + + if (!hasTarget) { + console.log(chalk.yellow(`"${target}" not found in ${filename}`)); + } + // remove the target from attributes + + delete contents[target] + + // write the file + fs.writeFileSync( + `${jsonDir}/${filename}`, + JSON.stringify(contents, null, 2) + ); + + options.debug + ? console.log( + hasTarget ? chalk.greenBright("Removed \n") : "…skipped \n" + ) + : null; + }); + }); + +program.parse(); diff --git a/utils/updateBaseUri.js b/utils/updateBaseUri.js index 5d37318be..747617e44 100644 --- a/utils/updateBaseUri.js +++ b/utils/updateBaseUri.js @@ -6,14 +6,14 @@ const basePath = isLocal ? process.cwd() : path.dirname(process.execPath); const fs = require("fs"); console.log(path.join(basePath, "/src/config.js")); -const { baseUri } = require(path.join(basePath, "/src/config.js")); +const { baseUri, outputJPEG } = require(path.join(basePath, "/src/config.js")); // read json data let rawdata = fs.readFileSync(`${basePath}/build/json/_metadata.json`); let data = JSON.parse(rawdata); data.forEach((item) => { - item.image = `${baseUri}/${item.edition}.png`; + item.image = `${baseUri}/${item.edition}.${ outputJPEG ? `jpg` : `png` }`; fs.writeFileSync( `${basePath}/build/json/${item.edition}.json`, JSON.stringify(item, null, 2)