montspy / loopygen Goto Github PK
View Code? Open in Web Editor NEWLoopring Python Minter on Layer 2
Loopring Python Minter on Layer 2
Give user the option of:
If there are IDs in all-traits, and the image file already exists, then skip them and only generate new IDs
Nice to have, but goes well with issue #19
Need metadata precalc, but otherwise I just need to load in the metadata CIDs and pass the CID to LoopMintPy.
Need to add the repo as a submodule and add to docker-compose
Windows can create Zone.Identifier files when files are downloaded from the internet, copied to WSL via the windows file explorer.
This confuses the glob
filter that looks for files when creating CIDs and can lead to invalid CID pre-calculation.
The real solution should be Windows to fix their 6 year old bugs.
As a workaround, we should add a filter to exclude those files from the glob
result
Suggested by chriscarter in Discord. I might end up making this a standalone site for the time being, but as a future feature this might be nice for people that want to display their trait output on a page.
Possible output formats:
Although I'm calling docker.sh as a user, the generated
folder and all sub-directories are owned by root
monty@DESKTOP-K3TBKAD:~/LooPyGen$ sudo rm -rf generated
[sudo] password for monty:
monty@DESKTOP-K3TBKAD:~/LooPyGen$ ./docker.sh generate -c 3
Starting at ID: 1
Using randomness seed: AAAAAAAAAAAW3UGObhzOWQ==
Are all images unique? True
How many of each trait exist?
Layer 01: {'BG 01': 1, 'BG 02': 2}
Layer 02: {'Guy 01': 0, 'Guy 02': 2, 'Guy 03': 1}
Layer 03: {'Eyes 01': 3, 'Eyes 02': 0, 'Eyes 03': 0}
Generated ./generated/collection_name/images/collection_name_001.png
Generated ./generated/collection_name/images/collection_name_002.png
Generated ./generated/collection_name/images/collection_name_003.png
Look in ./generated/collection_name/all-traits.json for an overview of all generated IDs and traits.
monty@DESKTOP-K3TBKAD:~/LooPyGen$ ls -al
total 76
drwxr-xr-x 8 monty docker 4096 Mar 17 12:25 .
drwxr-xr-x 11 monty monty 4096 Mar 17 00:09 ..
-rw-r--r-- 1 monty docker 66 Mar 17 00:09 .dockerignore
-rw-r--r-- 1 monty monty 176 Mar 17 10:32 .env
-rw-r--r-- 1 monty docker 97 Mar 17 00:09 .env.example
drwxr-xr-x 9 monty docker 4096 Mar 17 12:12 .git
-rw-r--r-- 1 monty docker 4201 Mar 17 00:09 .gitignore
-rw-r--r-- 1 monty docker 81 Mar 17 00:09 .gitmodules
-rw-r--r-- 1 monty docker 1391 Mar 17 00:09 Dockerfile
-rw-r--r-- 1 monty docker 1099 Mar 17 00:09 README.md
-rw-r--r-- 1 monty docker 493 Mar 17 00:09 docker-compose.yml
-rw-r--r-- 1 monty docker 135 Mar 17 00:09 docker.bat
-rwxr-xr-x 1 monty docker 246 Mar 17 00:09 docker.sh
drwxr-xr-x 2 monty docker 4096 Mar 17 00:09 dockerfiles
drwxr-xr-x 3 root root 4096 Mar 17 12:25 generated
drwxr-xr-x 3 monty docker 4096 Mar 17 00:14 generator
drwxr-xr-x 3 monty docker 4096 Mar 17 00:09 images
drwxr-xr-x 3 monty docker 4096 Mar 17 00:09 minter
monty@DESKTOP-K3TBKAD:~/LooPyGen$ whoami
monty
I'd like to add an easily reachable button in UI to nuke all sensitive info (mainly private keys) from disk
It could simply remove the config.json
file and provide confirmation
Most issues I see on Discord are due to missing or misnamed files but the Python scripts are not checking if the files exist before reading files.
The errors users get are very hard to read.
We should add file existence checks before reading any file.
So I can play with master
on LooPyMinty
The generator should fail gracefully with an explicit message if it failed to generate unique images.
The current behavior is to print a message but continue. Users might miss this and end up minting duplicated images.
Slightly different to #26 , and I think this probably belongs in this repo.
I want to add a function to docker.sh
that allows me to:
I need to get all the repos pulled in as submodules, and get a build system and dev env happening.
This system will be used for a few things:
The user initially just ran the pull commands and updated, but after getting the error they deleted the loopygen project and cloned frmo scratch again. Evidently they had an issue updating previously, and a fresh clone worked. In this instance it was a no-go, even after I had them prune the Docker images and run ./docker.sh reload
Log:
Traceback (most recent call last):
File "/var/www/html/generator/generate.py", line 285, in <module>
main()
File "/var/www/html/generator/generate.py", line 279, in main
composites = asyncio.run(generate(paths, traits, this_batch))
File "/usr/lib/python3.9/asyncio/runners.py", line 44, in run
return loop.run_until_complete(main)
File "/usr/lib/python3.9/asyncio/base_events.py", line 647, in run_until_complete
return future.result()
File "/var/www/html/generator/generate.py", line 167, in generate
result = await task
File "/usr/lib/python3.9/asyncio/tasks.py", line 611, in _wait_for_one
return f.result() # May raise f.exception().
File "/var/www/html/generator/generate.py", line 156, in sem_task
return await task
File "/var/www/html/generator/generate.py", line 130, in build_and_save_image
composite = await img_builder.build()
File "/var/www/html/generator/ImageBuilder.py", line 126, in build
self._make_canvas(self.descriptors[0].fp)
File "/usr/lib/python3.9/functools.py", line 938, in _method
return method.__get__(obj, cls)(*args, **kwargs)
File "/var/www/html/generator/ImageBuilder.py", line 168, in _
self.img = ImageDescriptor(type=ImageType.STATIC, img=Image.new(mode=self.STATIC_MODE, size=self._get_size(fp)))
File "/usr/lib/python3.9/functools.py", line 938, in _method
return method.__get__(obj, cls)(*args, **kwargs)
File "/var/www/html/generator/ImageBuilder.py", line 187, in _
return tuple( (int(x) for x in subprocess.run(cmd.split(), capture_output=True, text=True).stdout.split(sep=',')) )
File "/var/www/html/generator/ImageBuilder.py", line 187, in <genexpr>
return tuple( (int(x) for x in subprocess.run(cmd.split(), capture_output=True, text=True).stdout.split(sep=',')) )
ValueError: invalid literal for int() with base 10: ''
Task exception was never retrieved
future: <Task finished name='Task-15' coro=<generate.<locals>.sem_task() done, defined at /var/www/html/generator/generate.py:154> exception=ValueError("invalid literal for int() with base 10: ''")>
Traceback (most recent call last):
File "/var/www/html/generator/generate.py", line 156, in sem_task
return await task
File "/var/www/html/generator/generate.py", line 130, in build_and_save_image
composite = await img_builder.build()
File "/var/www/html/generator/ImageBuilder.py", line 126, in build
self._make_canvas(self.descriptors[0].fp)
File "/usr/lib/python3.9/functools.py", line 938, in _method
return method.__get__(obj, cls)(*args, **kwargs)
File "/var/www/html/generator/ImageBuilder.py", line 168, in _
self.img = ImageDescriptor(type=ImageType.STATIC, img=Image.new(mode=self.STATIC_MODE, size=self._get_size(fp)))
File "/usr/lib/python3.9/functools.py", line 938, in _method
return method.__get__(obj, cls)(*args, **kwargs)
File "/var/www/html/generator/ImageBuilder.py", line 187, in _
return tuple( (int(x) for x in subprocess.run(cmd.split(), capture_output=True, text=True).stdout.split(sep=',')) )
File "/var/www/html/generator/ImageBuilder.py", line 187, in <genexpr>
return tuple( (int(x) for x in subprocess.run(cmd.split(), capture_output=True, text=True).stdout.split(sep=',')) )
ValueError: invalid literal for int() with base 10: ''
Task exception was never retrieved
future: <Task finished name='Task-18' coro=<generate.<locals>.sem_task() done, defined at /var/www/html/generator/generate.py:154> exception=ValueError("invalid literal for int() with base 10: ''")>
Traceback (most recent call last):
File "/var/www/html/generator/generate.py", line 156, in sem_task
return await task
File "/var/www/html/generator/generate.py", line 130, in build_and_save_image
composite = await img_builder.build()
File "/var/www/html/generator/ImageBuilder.py", line 126, in build
self._make_canvas(self.descriptors[0].fp)
File "/usr/lib/python3.9/functools.py", line 938, in _method
return method.__get__(obj, cls)(*args, **kwargs)
File "/var/www/html/generator/ImageBuilder.py", line 168, in _
self.img = ImageDescriptor(type=ImageType.STATIC, img=Image.new(mode=self.STATIC_MODE, size=self._get_size(fp)))
File "/usr/lib/python3.9/functools.py", line 938, in _method
return method.__get__(obj, cls)(*args, **kwargs)
File "/var/www/html/generator/ImageBuilder.py", line 187, in _
return tuple( (int(x) for x in subprocess.run(cmd.split(), capture_output=True, text=True).stdout.split(sep=',')) )
File "/var/www/html/generator/ImageBuilder.py", line 187, in <genexpr>
return tuple( (int(x) for x in subprocess.run(cmd.split(), capture_output=True, text=True).stdout.split(sep=',')) )
ValueError: invalid literal for int() with base 10: ''
sys:1: RuntimeWarning: coroutine 'build_and_save_image' was never awaited
Current metadata template is missing description
Previous metadata could have been generated with a royalty_percentage
as a string:
"description": "My Guy Collection",
"royalty_percentage": "5",
"tokenId": 3,
That is incorrect as the ""
shouldn't be present (looking at MetaBoy for example. To be determined if that's an issue for marketplace or not. It should really look like so:
"description": "My Guy Collection",
"royalty_percentage": 5,
"tokenId": 3,
Users should be able to generate NFTs with an audio track if they provide webm or mp4 files that contain a stereo audio track.
ffmpeg can do the mixing using this filter complex:
[0:a][1:a]amerge=inputs=2,pan=stereo|c0<c0+c2|c1<c1+c3[a]
[0:a][1:a]amerge=inputs=2
merge the two stereo audio sources 0:a
and 1:a
(from video source 0
and 1
) into a 4 channel audio source
pan=stereo|c0<c0+c2|c1<c1+c3
combine the 4 channels c0
, c1
, c2
, c3
into 2 channels (stereo)
Related to MP4 support in #30
MINT SINGLE ITEM
to MINT CUSTOM ITEM
I'm beginning to see this as a nice-to-have instead of mandatory. Will be a great feature eventually.
Besides PHP to do the work, we'll also need some good documentation so the users know precisely how to setup their folders for this to work properly.
A user hit the following error after generating a new collection:
The user did go back to a previous step while generating the traits.json file. After re-trying without going back during collection setup, the problem went away.
The bugged traits.json
file was lost but the user will try to reproduce and share the file.
I believe that the traits.json
file ended up with "animation": true
but with an invalid value for the key "animation_size"
, causing the above error.
@sk33z3r is there a way to make the collection setup foolproof against going back to previous steps?
For the final export, a default of WebM with VP9 will be good enough for most users but some might prefer MP4 h.264 if transparency is not a requirement.
And of course, we can support GIF export too
For intermediate layering steps, WebM with lossless VP9 could still be used in all cases
Current subprocess call doesn't wrap filenames in double quotes so space in file paths are not supported
Users might want to edit images and/or non-critical metadata after generation.
That means CIDs can become stale and should be updated to the new file contents.
I propose we add some option(s) to metadata.py
to update the image
and animation_url
CIDs as well as the final metadata CIDs.
It would run the normal flow but read JSON, update the CIDs and write back.
Programmers will understand the ordering right now. You input layers starting with the bottom most layer and working your way up. So your background image starts as layer 1, then you add on layers.
However, one tester has reported they saw it the other way, because they looked at the layers visually instead of sequentially. They thought since layer07 was the last layer for them, and at the "bottom of the list" it should be considered the bottom most layer.
I'm not sure how I feel about it.
It looks like somewhere along the chain shell -> docker.sh -> transfer bash script -> python transfer.py
, quoted strings lose their quotes.
I am trying to add a --memo
argument to the transfer.py
script.
python3 generator/transfer.py --memo "This is a memo~!" --other arg # works fine Python gets this as ['--memo', 'This is a memo~!', '--other', 'arg']
./docker.sh transfer --memo "This is a memo~!" --other arg # Python gets this as ['--memo', 'This', 'is', 'a', 'memo~!', '--other', 'arg']
The README and comments are sufficient for engineers, but not for the average photoshop user.
Need docs and tutorials on the various ways you can use this utility.
You can start batch off at a certain ID. This allows you to generate a small amount initially, then more later if the project calls for it.
./docker.sh generate --id 200 100
This command starts at ID 200 and generates 100 images starting from that number.
Fix: I generate all-traits.json
. If it exists, pull it in and set all_images
to the content.
metadata.py -e
flag should only erase JSON files
generate.py -e
flag could only erase image files and the few JSON files it generates, but open to keeping it as erase all files
Some folks might want transparency, some might not.
An option to select one or the other would be great.
We might want to take it a step further by allowing the user to select a set of colors that get randomly picked for a default BG color.
Running generate produces the following error:
Traceback (most recent call last):
File "/loopymint2/generate.py", line 240, in <module>
main()
File "/loopymint2/generate.py", line 122, in main
if total_image > traits.get_variation_cnt():
AttributeError: module 'traits' has no attribute 'get_variation_cnt'
It seems there may have been a function saved into traits.py
, but was not transferred to traits.example.py
, so the repo did not commit the changes.
The workaround for missing audio channels does not behave as expected on some installs.
raise RuntimeError(f'Could not run ffmpeg command "{cmd}":\n\t{stderr.decode()}')
RuntimeError: Could not run ffmpeg command "ffmpeg -hide_banner -loglevel warning -y -f image2 -pattern_type none -loop 0 -i "/var/www/html/tmp4pfhg9gx/tmp0b1px2g1.png" -ignore_loop 1 -i "./images/wagmi_wolves_club_bodies/layer19/sparkle.gif" -f lavfi -i anullsrc -f lavfi -i anullsrc -filter_complex "amerge=inputs=2,pan=stereo|c0<c0+c2|c1<c1+c3[a]" -filter_complex "[0][1]overlay[ov]" -map [ov] -map [a] -c:v libvpx-vp9 -lag-in-frames 0 -lossless 1 -row-mt 1 -pix_fmt yuva420p -shortest "/var/www/html/tmp4pfhg9gx/tmp0801l87t.webm"":
Stream map 'a' matches no streams.
To ignore this, add a trailing '?' to the map.
Confirmed ffmpeg version matches known working setups
./docker.sh ffmpeg
ffmpeg version 4.3.3-0+deb11u1 Copyright (c) 2000-2021 the FFmpeg developers
built with gcc 10 (Debian 10.2.1-6)
configuration: --prefix=/usr --extra-version=0+deb11u1 --toolchain=hardened --libdir=/usr/lib/x86_64-linux-gnu --incdir=/usr/include/x86_64-linux-gnu --arch=amd64 --enable-gpl --disable-stripping --enable-avresample --disable-filter=resample --enable-gnutls --enable-ladspa --enable-libaom --enable-libass --enable-libbluray --enable-libbs2b --enable-libcaca --enable-libcdio --enable-libcodec2 --enable-libdav1d --enable-libflite --enable-libfontconfig --enable-libfreetype --enable-libfribidi --enable-libgme --enable-libgsm --enable-libjack --enable-libmp3lame --enable-libmysofa --enable-libopenjpeg --enable-libopenmpt --enable-libopus --enable-libpulse --enable-librabbitmq --enable-librsvg --enable-librubberband --enable-libshine --enable-libsnappy --enable-libsoxr --enable-libspeex --enable-libsrt --enable-libssh --enable-libtheora --enable-libtwolame --enable-libvidstab --enable-libvorbis --enable-libvpx --enable-libwavpack --enable-libwebp --enable-libx265 --enable-libxml2 --enable-libxvid --enable-libzmq --enable-libzvbi --enable-lv2 --enable-omx
<snip>
--enable-libdc1394 --enable-libdrm --enable-libiec61883 --enable-chromaprint --enable-frei0r --enable-libx264 --enable-shared
libavutil 56. 51.100 / 56. 51.100
libavcodec 58. 91.100 / 58. 91.100
libavformat 58. 45.100 / 58. 45.100
libavdevice 58. 10.100 / 58. 10.100
libavfilter 7. 85.100 / 7. 85.100
libavresample 4. 0. 0 / 4. 0. 0
libswscale 5. 7.100 / 5. 7.100
libswresample 3. 7.100 / 3. 7.100
libpostproc 55. 7.100 / 55. 7.100
Hyper fast Audio and Video encoder
usage: ffmpeg [options] [[infile options] -i infile]... {[outfile options] outfile}...
Need a way to differentiate between a GIF and a STATIC collection.
Most likely need imagemagick's convert, and will have to be very specific on how to organize the folder structure.
We need to pre-calc the CIDs in a couple ways and pass them along the whole process
I will need to bring in the LoopMintPy repo as a submodule and add the node container to docker-compose.
If the traits.py file has no background set, python tries to get the first image from a folder named layer00
which doesn't jive with the way we have things explained in the docs.
Either we have to be specific about the need to keep background color in traits, even if you don't want it; or we add logic that says "if Background Color" doesn't exist, start at layer01
.
The preferable solution is the latter, so people that don't care about background and have one set in images already won't get confused.
I recently implemented a simple auth system in PHP, I wonder if we should adopt the usual db auth strategy for the tool to make it a bit safer and less sketchy to anyone who is concernd about giving their private key. Even though we don't put it anywhere but the local file, it's still plaintext.
My proposal:
In React:
In Python:
If a user ever forgets their password, then it's easy to re-run the mint config again. I think this will do A LOT for security conscious folks, and for those that are not we can at least make sure that in the event of accidentally sharing that file or getting compromised, we did not keep their pk in plaintext.
We may want to reach out to glitchyg specifically if/when we implement to make sure he is aware for his automated system.
@Montspy Do you think we should make this a feature of the LooPyGen
or for LooPyMinty
?
I could see the argument that if someone doesn't want to generate images, they only want to batch mint. Obviously with the way you implemented batch minting the user can just use your repo.
The only argument to make it a feature in this repo is to funnel everyone to a single place for all the needs.
In this scenario, I think the user would be responsible for making their own metadata files and pre-filling them with the right CIDs.
Then our function can simply iterate a folder of metadata, build the CID json, then run a mint.
Need a few things for this, but I'm no longer requiring it for the UI PR:
traits.json
/config.json
files and present a choice to useredit
page with the correct collection_lower
parameter@sk33z3r
I helped a user setup LooPyGen in their home directory ~/LooPyGen
on Windows 10 + WSL2 (Ubuntu 18.04)
It became apparent that they were logged in as root
and cd ~
resolved to cd /root/
Trying to setup a collection with the PHP UI resulted in the below PHP error message
I think this would be caused by the UID/GID automatically populated in the .env
does not have the correct access to /root/*
but have not had time to gather more info
As discussed a while ago, Here's a few example on the update of "traits.json" structure,
v1.0.0 "traits.json" with thumbnail, animation and 3 layers (I removed the background already on this structure)
{
"collection_name": "Lazy Loop Cat",
"collection_lower": "lazyloopcat",
"description": "Lazy Loop Cat description",
"artist_name": "lazyloopcat.loopring.eth",
"thumbnails": true,
"animation": true,
"animation_format": ".gif",
"royalty_percentage": 10,
"royalty_address": "0x0000000000000000000000000000000dead",
"seed": "cats",
"image_layers": [
{
"layer_name": "Head",
"variations": 1,
"filenames": {
"Diamond": "head_diamond.png"
},
"weights": [
1
]
},
{
"layer_name": "Torso",
"variations": 1,
"filenames": {
"Diamond": "torso_diamond.json"
},
"weights": [
2
]
},
{
"layer_name": "Feet",
"variations": 1,
"filenames": {
"Diamond": "feet_diamond.json"
},
"weights": [
3
]
}
],
"thumbnail_size": [
200,
200
]
}
V2.0.0 "traits.json" that corresponds to the one above
{
"collection": {
"name": "Lazy Loop Cat",
"lower": "lazy_loop_cat",
"description": "Lazy Loop Cat Description",
"artists": [
"lazyloopcat.loopring.eth"
],
"royalty": {
"address": "0x00000000000000000000000000000000d3ad",
"percentage": 5
},
"options": {
"seed": "cats",
"thumbnails": {
"width": 200,
"height": 200
},
"animation": {
"format": ".gif"
}
},
"layers": {
"Head": [
{
"name": "Diamond",
"filename": "head_diamond.png",
"weight": 1
}
],
"Torso": [
{
"name": "Diamond",
"filename": "torso_diamond.png",
"weight": 2
}
],
"Feet": [
{
"name": "Diamond",
"filename": "feet_diamond.png",
"weight": 3
}
]
}
}
}
If the v1.0.0 traits.json doesn't have an animation or thumbnail the v2 will just remove it in the "options" properties
{
"collection": {
"name": "Lazy Loop Cat",
"lower": "lazy_loop_cat",
"description": "Lazy Loop Cat Description",
"artists": [
"lazyloopcat.loopring.eth"
],
"royalty": {
"address": "0x00000000000000000000000000000000d3ad",
"percentage": 5
},
"options": {
"seed": "cats"
},
"layers": {
"Head": [
{
"name": "Diamond",
"filename": "head_diamond.png",
"weight": 1
}
],
"Torso": [
{
"name": "Diamond",
"filename": "torso_diamond.png",
"weight": 2
}
],
"Feet": [
{
"name": "Diamond",
"filename": "feet_diamond.png",
"weight": 3
}
]
}
}
}
If the v1.0.0 "traits.json" have more than 1 variants on their layers
{
"collection": {
"name": "Lazy Loop Cat",
"lower": "lazy_loop_cat",
"description": "Lazy Loop Cat Description",
"artists": [
"lazyloopcat.loopring.eth"
],
"royalty": {
"address": "0x00000000000000000000000000000000d3ad",
"percentage": 5
},
"options": {
"seed": "cats"
},
"layers": {
"Head": [
{
"name": "Diamond",
"filename": "head_diamond.png",
"weight": 1
},
{
"name": "Gold",
"filename": "head_gold.png",
"weight": 2
},
{
"name": "Bronze",
"filename": "head_bronze.png",
"weight": 3
}
],
"Torso": [
{
"name": "Diamond",
"filename": "torso_diamond.png",
"weight": 2
}
],
"Feet": [
{
"name": "Diamond",
"filename": "feet_diamond.png",
"weight": 3
}
]
}
}
}
I'm running some test generations for a PG thing, and running into an issue I can't seem to sort out.
Using the new Background setup, I have traits from Background to layer03 setup. Generates without issue all the way through.
However, as soon as I add layer04, I get the following error:
Traceback (most recent call last):
File "/var/www/html/generator/generate.py", line 248, in <module>
main()
File "/var/www/html/generator/generate.py", line 173, in main
this_batch = img_gen.generate_images(starting_id=starting_id, image_cnt=total_image)
File "/var/www/html/generator/generate.py", line 66, in generate_images
unique_image = self.create_new_image(id=starting_id + i)
File "/var/www/html/generator/generate.py", line 53, in create_new_image
new_image[l["layer_name"]] = random.choices(l["names"], l["weights"])[0]
File "/usr/lib/python3.9/random.py", line 499, in choices
raise ValueError('The number of weights does not match the population')
ValueError: The number of weights does not match the population
I've triple checked my new layer, but I am quite sure the syntax is right, and the weights to filenames ratio is 1:1. When I remove this layer from traits, the script generates without issue. Here is the layer that causes the error, maybe I am missing something obvious:
{ # layer04
"layer_name": "Belt",
"filenames": {
"Black Belt": "01-belt.png",
"Purple Belt": "02-belt.png",
"Red Belt": "03-belt.png",
"Green Belt": "04-belt.png",
"Yellow Belt": "05-belt.png",
"Purple Belt": "06-belt.png"
},
"weights": [
1,
2,
4,
3,
2,
2
]
}
Current compositing only support alpha blending an element (layer) on top of others.
Other popular blending modes:
Useful blending modes for masking:
Browsers + HTML only return a filename for security purposes. However, PHP can run shell commands and we already require the user be in bash. I should be able to utilize server side commands try and find the files' full paths.
It would be nice to have a web form where users could enter information in a more streamlined way, then output json for use in python.
Turns out, CIDv1 works with any Pinata gateway. The only issue with using CIDv1 on LRC right now is when it is used for Metadata (because they only convert the NFT ID hex back to CIDv0 in the API). However, putting CIDv1 in the metadata for any field will appear fine in the explorer. As long as the metadata is a CIDv0 that's all that matters.
I'm doing a little research into it, but it's possible that even files uploaded to nft.storage can be accessed by their CIDv0, so we might be able to implement some auto upload feature using nft.storage, as long as we populate the metadata CIDs with v0.
We've seen several NFT collections with different image
and animation_url
fields.
For animated NFTs, usually a GIF is in the image
field and the full blown video (MP4 or WebM) is in the animation_url
field
For static NFTs, a lower resolution image can be in image
and the full resolution in animation_url
I propose we add the option to generate those thumbnails either as a command line argument:
Example: --thumbnail 1280x720
on an animated collection would export GIFs to the collection_name/thumbnails
with a resolution of 1280x720
Example: --thumbnail 1200x1200
on static collections would export PNGs to the collection_name/thumbnails
with a resolution of 1200x1200
Or as a key/value pair in traits.py
/traits.json
:
# No "thumbnails" key/value pair does not generate thumbnails
"thumbnails": [1280, 720] # Generate thumbnails of that resolution
"thumbnails": [] # Generate thumbnails of default resolution
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.