Coder Social home page Coder Social logo

loopygen's Introduction

sk33z3r doesn't maintain this, reach out to Montspy instead

LooPyGen

Release Checks Issues

This is the unofficial Loopring Python Image Generator and Minter on Layer 2.

⚠️ NEVER SHARE YOUR PRIVATE KEYS OR PASSPHRASES WITH ANYONE ⚠️

Please report issues in the issues section

LooPyGen GUI

The LooPyGen GUI makes it easier to create generative collections for those that want to focus on the art rather than the tech.

Please see our wiki for more details on how to use the app.

LooPyGen CLI

The headless CLI component is meant for use in automating dApps when it comes to minting or transferring. The headless component can also be used to take advantage of server resources to generate high quality GIF or MP4 collections that require more than the average workstation.

Below is the command reference for the headless component. For more details, see our wiki

Notes:

  • You can clean up old images with: docker image prune
  • Your config files are encrypted and stored in a Docker volume that is persistent on updates. Destroy the volume with: docker volume prune
  • If you desire to access the shell of the container for any reason, do so with: ./loopygen-cli.sh bash
  • If you pass your config passphrase on the command line, it should be base64 encoded first. All transfer and mint commands accept this additional argument:
    --configpass $(echo -n "passphrase" | basenc --base64)

Download the script from GitHub with to your local working directory

# using wGET
$ wget -O ./loopygen-cli.sh https://github.com/sk33z3r/LooPyGen/blob/main/cli.sh && chmod +x loopygen-cli.sh
# using cURL
$ curl https://github.com/sk33z3r/LooPyGen/blob/main/cli.sh -o loopygen-cli.sh && chmod +x loopygen-cli.sh

Pull the latest image

$ ./loopygen-cli.sh update

Run a command

$ ./loopygen-cli.sh {command}

Commands

Replace {command} with one of the below commands.

Show Config Commands

Encrypt your mint configuration

encrypt --mint

Encrypt your transfer configuration

encrypt --transfer
Show Image Generator Commands

Basic run:

generate --count XXX

Delete previously generated images before generating a new set:

generate --empty --count XXX

Start generating from a specific ID number:

generate --count XXX --id YY

If you have a beefy computer, you can try to generate images simultaneously to speed up the process:

generate --count XXX --threaded
Show Metadata Generator Commands

Basic run, after generating images:

metadata

Delete previously generated metadata before generating a new set:

metadata --empty
Show Minting Commands

Batch mint a collection:

mint --name <my_nft_collection> --amount 1

Mint a specific set of IDs:

mint --name <my_nft_collection> --start <startID> --end <endID> --amount 1

Mint a single CID:

mint --cid Qmau1Sx2hLTkLmXsu2dD28yMZtL3Pzs2uKqP2MeHZPm93V --amount 100

Test run a mint (shows only what the script would do, but doesn't actually do it):

mint --name <my_nft_collection> --testmint --amount 100
Show CID Calculator Commands

To scan CID files, input a path relative to your working directory.

The file must be at your current level or lower, and not outside of the directory. For instance, ../other-dir/somefile would not work.

CIDv0:

cid ./relative/path/to/file

CIDv1:

cid --cid-version=1 ./relative/path/to/file

loopygen's People

Contributors

montspy avatar sk33z3r avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

loopygen's Issues

/loopygen should be writeable by PHP

image

Current directory /loopygen doesn't allow writes from users other than root
PHP's www-data user calls python which tries to create a temp folder in /loopygen and fails

I can also move the temp folders to /tmp

Add option to update metadata JSON CIDs and final CIDs

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.

[Python] Add readable errors for all disk IO tasks

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.

Implement CID pre-calc

We need to pre-calc the CIDs in a couple ways and pass them along the whole process

  1. After images are generated, make a list of image CIDs
  2. Generate metadata with image CIDs
  3. After metadata is generated, make a list of json CIDs
  4. Load the list of json into batch script and pass to LoopMintPy

I will need to bring in the LoopMintPy repo as a submodule and add the node container to docker-compose.

[python] Add configurable video format for final export

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

Empty flag should only erase related files

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

php: `max_file_uploads` setting not applied

A user hit this error while uploading files for a modestly sized collection (~40 assets)
image

It appears that the max_file_uploads setting among others is not applied for images coming from Docker Hub

[python] GIF generation fails on audio channel missing

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}...

Add batch CID script for users that only want to mint and don't use the generator

@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.

Use a passphrase to encrypt user's minting config

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:

  1. ask the user for a passphrase in the minting config setup alongside the private key.
  2. use bcrypt to encode the private key using the passphrase
  3. store the bcrypt output in json

In Python:

  1. ask the user to enter their passphrase whenever the minting or transfer commands are triggered
  2. decode the bcrypt from json to get private key
  3. if any issues with establishing authentication, let the user know they may have entered their passphrase wrong

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.

[PHP] Ability to edit JSON after generation

Need a few things for this, but I'm no longer requiring it for the UI PR:

  1. Add new options to homepage for editing or creating new config/traits
  2. Get a list of available traits.json/config.json files and present a choice to user
  3. Take them to the respective edit page with the correct collection_lower parameter

Layer ordering might be confusing

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.

Checking `weights` count fails after `layer03`

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
        ]
    }

Docker Compose

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:

  • A basis for the end-user facing repo
  • A build setup for pushing images to Docker Hub
  • A build system for users who want to go all local/offline

[Docker] Cannot pass quoted strings as CLI arguments through docker.sh

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']

Generating batches beyond the first does not check previous batch for uniqueness

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.

[python] Filter out "*Zone.Identifier" files

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

image

[UI] Going back during collection setup can generate a broken traits.json

A user hit the following error after generating a new collection:
image

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?

[python] package generated collections into CAR files, and upload to nft.storage while storing returned CIDv0 vals

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.

[UI] Button to nuke sensitive information

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

[PHP] Get full filename path

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.

`generated` folder is owned by root and not by the user

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

[python] User's GIF generation fails with: 'build_and_save_image' was never awaited

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

Generation fails due to missing function in traits.py

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.

[python] Generate GIF animations

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.

[python] Add option to export "thumbnails" during generation

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

Implement batch script

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

[PHP] Generate traits.json using only file/folder names

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.

Generator fails if there is no Background color set

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.

Metadata field "royalty_percentage" is exported as a string, should be an integer

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,

[UI] Convert traits.json into HTML table

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:

  • HTML table
  • CSV

[UI] Collection setup fails due to incorrect permissions

@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

image

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

[python] Fail on generated image not unique

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.

[WIKI] Documentation

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.

Update traits.json structure

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
        }
      ]
    }
  }
}

[python] Properly merge audio when compositing animated images

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

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.