artbase 0.0.1 → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,68 @@
1
+
2
+
3
+ PIXEL_OFFSETS['24x24'] = {
4
+ ######################
5
+ # 269x269 (24x24)
6
+ # assume 11px per pixel 11x24 = 264 + 5 = 269
7
+ #
8
+ # e.g. 24.times {|i| puts "#{(5+i*11+5.0/24.0*i).to_i} => #{i}," }
9
+ '269x269' => {
10
+ 5 => 0,
11
+ 16 => 1,
12
+ 27 => 2,
13
+ 38 => 3,
14
+ 49 => 4,
15
+ 61 => 5,
16
+ 72 => 6,
17
+ 83 => 7,
18
+ 94 => 8,
19
+ 105 => 9,
20
+ 117 => 10,
21
+ 128 => 11,
22
+ 139 => 12,
23
+ 150 => 13,
24
+ 161 => 14,
25
+ 173 => 15,
26
+ 184 => 16,
27
+ 195 => 17,
28
+ 206 => 18,
29
+ 217 => 19,
30
+ 229 => 20,
31
+ 240 => 21,
32
+ 251 => 22,
33
+ 262 => 23,
34
+ },
35
+
36
+
37
+ ######################
38
+ # 512x512 (24x24)
39
+ # assume 21px per pixel 21x24 = 504 + 8 = 512
40
+ #
41
+ # e.g. 24.times {|i| puts "#{(12+i*21+8.0/24.0*i).to_i} => #{i}," }
42
+ '512x512' => {
43
+ 12 => 0,
44
+ 33 => 1,
45
+ 54 => 2,
46
+ 76 => 3,
47
+ 97 => 4,
48
+ 118 => 5,
49
+ 140 => 6,
50
+ 161 => 7,
51
+ 182 => 8,
52
+ 204 => 9,
53
+ 225 => 10,
54
+ 246 => 11,
55
+ 268 => 12,
56
+ 289 => 13,
57
+ 310 => 14,
58
+ 332 => 15,
59
+ 353 => 16,
60
+ 374 => 17,
61
+ 396 => 18,
62
+ 417 => 19,
63
+ 438 => 20,
64
+ 460 => 21,
65
+ 481 => 22,
66
+ 502 => 23,
67
+ },
68
+ }
@@ -0,0 +1,43 @@
1
+
2
+ PIXEL_OFFSETS['32x32'] = {
3
+ ######################
4
+ # 320x320 (32x32)
5
+ # assume 10px per pixel 10x32 = 320
6
+ #
7
+ # e.g. 32.times {|i| puts "#{5+i*10} => #{i}," }
8
+ '320x320' => {
9
+ 5 => 0,
10
+ 15 => 1,
11
+ 25 => 2,
12
+ 35 => 3,
13
+ 45 => 4,
14
+ 55 => 5,
15
+ 65 => 6,
16
+ 75 => 7,
17
+ 85 => 8,
18
+ 95 => 9,
19
+ 105 => 10,
20
+ 115 => 11,
21
+ 125 => 12,
22
+ 135 => 13,
23
+ 145 => 14,
24
+ 155 => 15,
25
+ 165 => 16,
26
+ 175 => 17,
27
+ 185 => 18,
28
+ 195 => 19,
29
+ 205 => 20,
30
+ 215 => 21,
31
+ 225 => 22,
32
+ 235 => 23,
33
+ 245 => 24,
34
+ 255 => 25,
35
+ 265 => 26,
36
+ 275 => 27,
37
+ 285 => 28,
38
+ 295 => 29,
39
+ 305 => 30,
40
+ 315 => 31,
41
+ },
42
+ }
43
+
@@ -0,0 +1,70 @@
1
+
2
+ PIXEL_OFFSETS['60x60'] = {
3
+ '512x512' => {
4
+ ###########
5
+ # 512x512 (60x60)
6
+ # assume 8px 60*8 = 480 + 32 = 512
7
+ #
8
+ # e.g. 60.times {|i| puts "#{(4+i*8+32.0/60.0*i).to_i} => #{i}," }
9
+ 4 => 0,
10
+ 12 => 1,
11
+ 21 => 2,
12
+ 29 => 3,
13
+ 38 => 4,
14
+ 46 => 5,
15
+ 55 => 6,
16
+ 63 => 7,
17
+ 72 => 8,
18
+ 80 => 9,
19
+ 89 => 10,
20
+ 97 => 11,
21
+ 106 => 12,
22
+ 114 => 13,
23
+ 123 => 14,
24
+ 132 => 15,
25
+ 140 => 16,
26
+ 149 => 17,
27
+ 157 => 18,
28
+ 166 => 19,
29
+ 174 => 20,
30
+ 183 => 21,
31
+ 191 => 22,
32
+ 200 => 23,
33
+ 208 => 24,
34
+ 217 => 25,
35
+ 225 => 26,
36
+ 234 => 27,
37
+ 242 => 28,
38
+ 251 => 29,
39
+ 260 => 30,
40
+ 268 => 31,
41
+ 277 => 32,
42
+ 285 => 33,
43
+ 294 => 34,
44
+ 302 => 35,
45
+ 311 => 36,
46
+ 319 => 37,
47
+ 328 => 38,
48
+ 336 => 39,
49
+ 345 => 40,
50
+ 353 => 41,
51
+ 362 => 42,
52
+ 370 => 43,
53
+ 379 => 44,
54
+ 388 => 45,
55
+ 396 => 46,
56
+ 405 => 47,
57
+ 413 => 48,
58
+ 422 => 49,
59
+ 430 => 50,
60
+ 439 => 51,
61
+ 447 => 52,
62
+ 456 => 53,
63
+ 464 => 54,
64
+ 473 => 55,
65
+ 481 => 56,
66
+ 490 => 57,
67
+ 498 => 58,
68
+ 507 => 59,
69
+ },
70
+ }
@@ -0,0 +1,90 @@
1
+
2
+ PIXEL_OFFSETS['80x80'] = {
3
+ '512x512' => {
4
+ ###########
5
+ # 512x512 (80x80)
6
+ # assume 6px 80*6 = 480 + 32 = 512
7
+ #
8
+ # e.g. 80.times {|i| puts "#{(3+i*6+32.0/80.0*i).to_i} => #{i}," }
9
+ 3 => 0,
10
+ 9 => 1,
11
+ 15 => 2,
12
+ 22 => 3,
13
+ 28 => 4,
14
+ 35 => 5,
15
+ 41 => 6,
16
+ 47 => 7,
17
+ 54 => 8,
18
+ 60 => 9,
19
+ 67 => 10,
20
+ 73 => 11,
21
+ 79 => 12,
22
+ 86 => 13,
23
+ 92 => 14,
24
+ 99 => 15,
25
+ 105 => 16,
26
+ 111 => 17,
27
+ 118 => 18,
28
+ 124 => 19,
29
+ 131 => 20,
30
+ 137 => 21,
31
+ 143 => 22,
32
+ 150 => 23,
33
+ 156 => 24,
34
+ 163 => 25,
35
+ 169 => 26,
36
+ 175 => 27,
37
+ 182 => 28,
38
+ 188 => 29,
39
+ 195 => 30,
40
+ 201 => 31,
41
+ 207 => 32,
42
+ 214 => 33,
43
+ 220 => 34,
44
+ 227 => 35,
45
+ 233 => 36,
46
+ 239 => 37,
47
+ 246 => 38,
48
+ 252 => 39,
49
+ 259 => 40,
50
+ 265 => 41,
51
+ 271 => 42,
52
+ 278 => 43,
53
+ 284 => 44,
54
+ 291 => 45,
55
+ 297 => 46,
56
+ 303 => 47,
57
+ 310 => 48,
58
+ 316 => 49,
59
+ 323 => 50,
60
+ 329 => 51,
61
+ 335 => 52,
62
+ 342 => 53,
63
+ 348 => 54,
64
+ 355 => 55,
65
+ 361 => 56,
66
+ 367 => 57,
67
+ 374 => 58,
68
+ 380 => 59,
69
+ 387 => 60,
70
+ 393 => 61,
71
+ 399 => 62,
72
+ 406 => 63,
73
+ 412 => 64,
74
+ 419 => 65,
75
+ 425 => 66,
76
+ 431 => 67,
77
+ 438 => 68,
78
+ 444 => 69,
79
+ 451 => 70,
80
+ 457 => 71,
81
+ 463 => 72,
82
+ 470 => 73,
83
+ 476 => 74,
84
+ 483 => 75,
85
+ 489 => 76,
86
+ 495 => 77,
87
+ 502 => 78,
88
+ 508 => 79,
89
+ }
90
+ }
@@ -0,0 +1,75 @@
1
+ ######################
2
+ # pixelart image extensions
3
+ # move upstream!!!!!
4
+
5
+
6
+ module Pixelart
7
+
8
+ class Image
9
+ def sample( *args, **kwargs )
10
+ ## note: for now always assume square image (e.g. 24x24, 32x32 and such)
11
+
12
+ offsets = if kwargs[:from] && kwargs[:to]
13
+ PIXEL_OFFSETS[ kwargs[:to] ][ kwargs[ :from ]]
14
+ else
15
+ args[0] ## assume "custom" hash of offsets
16
+ end
17
+
18
+ width = height = offsets.size
19
+
20
+ puts " #{self.width}x#{self.height} => #{width}x#{height}"
21
+
22
+
23
+ dest = Image.new( width, height )
24
+
25
+ offsets.each do |offset_x, x|
26
+ offsets.each do |offset_y, y|
27
+ pixel = self[offset_x,offset_y]
28
+
29
+ dest[x,y] = pixel
30
+ end
31
+ end
32
+
33
+ dest
34
+ end
35
+ alias_method :pixelate, :sample
36
+ end # class Image
37
+
38
+
39
+
40
+ class ImageComposite
41
+ def add_glob( glob )
42
+ files = Dir.glob( glob )
43
+ puts "#{files.size} file(s) found matching >#{glob}<"
44
+
45
+
46
+ files = files.sort
47
+ ## puts files.inspect
48
+
49
+ files.each_with_index do |file,i|
50
+ puts "==> [#{i+1}/#{files.size}] - #{file}"
51
+ img = Image.read( file )
52
+
53
+ self << img ## todo/check: use add alias - why? why not?
54
+ end
55
+ end
56
+ end # class ImageComposite
57
+ end # module Pixelart
58
+
59
+
60
+ ###
61
+ # add common
62
+ # pixel(ate) offsets (for sampling)
63
+ PIXEL_OFFSETS = {}
64
+
65
+ module Pixelart
66
+ class Image
67
+ PIXEL_OFFSETS = ::PIXEL_OFFSETS
68
+ end
69
+ end # module Pixelart
70
+
71
+
72
+ require_relative 'image/24x24'
73
+ require_relative 'image/32x32'
74
+ require_relative 'image/60x60'
75
+ require_relative 'image/80x80'
@@ -0,0 +1,144 @@
1
+
2
+ module OpenSea
3
+
4
+ #############
5
+ # read meta data into struct
6
+
7
+ class Meta
8
+ def self.read( path )
9
+ txt = File.open( path, 'r:utf-8' ) { |f| f.read }
10
+ data = JSON.parse( txt )
11
+ new( data )
12
+ end
13
+
14
+
15
+ def initialize( data )
16
+ @data = data
17
+
18
+ assets = @data['assets']
19
+ if assets.size != 1
20
+ puts "!! error - expected one asset per file only - got #{assets.size} - sorry"
21
+ exit 1
22
+ end
23
+
24
+ @asset = assets[0]
25
+ end
26
+
27
+ def name
28
+ @name ||= _normalize( @asset['name'] )
29
+ end
30
+
31
+ def description
32
+ @description ||= _normalize( @asset['description'] )
33
+ end
34
+
35
+ ## note: auto-convert "" (empty string) to nil
36
+ def image_original_url() _blank( @asset['image_original_url'] ); end
37
+ def image_url() _blank( @asset['image_url'] ); end
38
+
39
+
40
+ def token_id() @asset['token_id']; end ## note: keep id as string as is - why? why not?
41
+
42
+
43
+ def traits
44
+ @traits ||= begin
45
+ traits = []
46
+ ## keep traits as (simple)
47
+ ## ordered array of pairs for now
48
+ ##
49
+ ## in a step two make lookup via hash table
50
+ ## or such easier / "automagic"
51
+
52
+ @asset[ 'traits' ].each do |t|
53
+ trait_type = t['trait_type'].strip
54
+ trait_value = t['value'].strip
55
+ traits << [trait_type, trait_value]
56
+ end
57
+
58
+ traits
59
+ end
60
+ end
61
+
62
+
63
+
64
+ ### "private" convenience / helper methods
65
+ def _normalize( str )
66
+ return if str.nil? ## check: check for nil - why? why not?
67
+
68
+ ## normalize string
69
+ ## remove leading and trailing spaces
70
+ ## collapse two and more spaces into one
71
+ ## change unicode space to ascii
72
+ str = str.gsub( "\u{00a0}", ' ' )
73
+ str = str.strip.gsub( /[ ]{2,}/, ' ' )
74
+ str
75
+ end
76
+
77
+ def _blank( o ) ## auto-convert "" (empty string) into nil
78
+ if o && o.strip.empty?
79
+ nil
80
+ else
81
+ o
82
+ end
83
+ end
84
+ end # class Meta
85
+
86
+
87
+
88
+ BASE = 'https://api.opensea.io/api/v1/assets?collection={collection}&order_direction=asc&offset={offset}&limit=1'
89
+
90
+ ## todo/check: rename to query or search or such - why? why not?
91
+ ##
92
+ ## add self.collect( collection, offset: 0 ) helper or such - why? why not?
93
+ def self.assets( collection:, offset: 0 )
94
+ src = BASE.sub( '{collection}', collection ).
95
+ sub( '{offset}', offset.to_s )
96
+
97
+ call( src )
98
+ end
99
+
100
+
101
+ def self.call( src ) ## get response as (parsed) json (hash table)
102
+ uri = URI.parse( src )
103
+
104
+ http = Net::HTTP.new( uri.host, uri.port )
105
+
106
+ puts "[debug] GET #{uri.request_uri} uri=#{uri}"
107
+
108
+ headers = {
109
+ 'User-Agent' => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.71 Safari/537.36",
110
+ #'User-Agent' => "ruby v#{RUBY_VERSION}",
111
+ }
112
+
113
+
114
+ request = Net::HTTP::Get.new( uri.request_uri, headers )
115
+ if uri.instance_of? URI::HTTPS
116
+ http.use_ssl = true
117
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
118
+ end
119
+
120
+ response = http.request( request )
121
+
122
+ if response.code == '200'
123
+ puts "#{response.code} #{response.message} - content_type: #{response.content_type}, content_length: #{response.content_length}"
124
+
125
+ text = response.body.to_s
126
+ text = text.force_encoding( Encoding::UTF_8 )
127
+
128
+ data = JSON.parse( text )
129
+ data
130
+ else
131
+ puts "!! ERROR:"
132
+ puts "#{response.code} #{response.message}"
133
+ exit 1
134
+ end
135
+ end
136
+ end # module OpenSea
137
+
138
+
139
+
140
+
141
+ #####
142
+ # add convenience alias
143
+ Opensea = OpenSea
144
+
@@ -0,0 +1,159 @@
1
+
2
+
3
+
4
+ module Artbase
5
+ class Tool
6
+
7
+ def self.collection=(value)
8
+ puts " setting collection to:"
9
+ pp value
10
+
11
+ @collection = value
12
+ end
13
+
14
+
15
+ def self.main( args=ARGV )
16
+ puts "==> welcome to collection tool with args:"
17
+ pp args
18
+
19
+ options = { }
20
+ parser = OptionParser.new do |opts|
21
+
22
+ opts.on("--offset NUM", Integer,
23
+ "Start counting at offset (default: 0)") do |num|
24
+ options[ :offset] = num
25
+ end
26
+
27
+ opts.on("--limit NUM", Integer,
28
+ "Limit collection (default: ∞)") do |num|
29
+ options[ :limit] = num
30
+ end
31
+
32
+ ## todo/fix: unsupported argument type: Range
33
+ ## opts.on("--range RANGE", Range,
34
+ ## "Range of collection (default: 0..∞") do |range|
35
+ ## options[ :range] = range
36
+ ## end
37
+
38
+ opts.on("-h", "--help", "Prints this help") do
39
+ puts opts
40
+ exit
41
+ end
42
+ end
43
+
44
+ parser.parse!( args )
45
+ puts "options:"
46
+ pp options
47
+
48
+ puts "args:"
49
+ pp args
50
+
51
+ if args.size < 2
52
+ puts "!! ERROR - no command found - use <collection> <command>..."
53
+ puts ""
54
+ exit
55
+ end
56
+
57
+ name = args[0] ## todo/check - use collection_name/slug or such?
58
+ command = args[1]
59
+ subcommand = args[2]
60
+
61
+
62
+ path = "./#{name}/config.rb"
63
+ puts "==> reading collection config >#{path}<..."
64
+
65
+ ## note: assume for now global const COLLECTION gets set/defined!!!
66
+ ## use/change to a script/dsl loader/eval later!!!
67
+ load( path )
68
+
69
+ ## pp COLLECTION
70
+
71
+ ## configure collection (note: requires self)
72
+ self.collection = COLLECTION
73
+
74
+
75
+ if ['d','dl','down', 'download'].include?( command )
76
+ if subcommand
77
+ if ['m', 'meta'].include?( subcommand )
78
+ download_meta
79
+ elsif ['i', 'img', 'image', 'images'].include?( subcommand )
80
+ download_images
81
+ end
82
+ else
83
+ download_meta
84
+ download_images
85
+ end
86
+ elsif ['p', 'px', 'pix', 'pixel', 'pixelate'].include?( command )
87
+ pixelate( offset: options[ :offset] )
88
+ elsif ['m', 'meta'].include?( command )
89
+ download_meta
90
+ elsif ['i', 'img', 'image', 'images'].include?( command )
91
+ download_images( offset: options[ :offset] )
92
+ elsif ['a', 'attr', 'attributes'].include?( command )
93
+ dump_attributes
94
+ elsif ['x', 'exp', 'export'].include?( command )
95
+ export_attributes
96
+ elsif ['c', 'composite'].include?( command )
97
+ make_composite
98
+ elsif ['t', 'test'].include?( command )
99
+ puts " testing, testing, testing"
100
+ else
101
+ puts "!! ERROR - unknown command >#{command}<, sorry"
102
+ end
103
+
104
+ puts "bye"
105
+ end
106
+
107
+ def self.make_composite
108
+ puts "==> make composite"
109
+ @collection.make_composite
110
+ end
111
+
112
+ def self.dump_attributes
113
+ puts "==> dump attributes"
114
+ @collection.dump_attributes
115
+ end
116
+
117
+ def self.export_attributes
118
+ puts "==> export attributes"
119
+ @collection.export_attributes
120
+ end
121
+
122
+
123
+ def self.download_meta
124
+ puts "==> download meta"
125
+ @collection.download_meta
126
+ end
127
+
128
+ def self.download_images( offset: )
129
+ puts "==> download images"
130
+
131
+ ## note: use three dots (...) to exclude e.g. count 100 => 0 to 99)
132
+ range = if offset
133
+ (offset...@collection.count)
134
+ else
135
+ (0...@collection.count)
136
+ end
137
+
138
+ @collection.download_images( range )
139
+ end
140
+
141
+ def self.pixelate( offset: )
142
+ puts "==> pixelate"
143
+
144
+ ## note: use three dots (...) to exclude e.g. count 100 => 0 to 99)
145
+ range = if offset
146
+ (offset...@collection.count)
147
+ else
148
+ (0...@collection.count)
149
+ end
150
+
151
+ @collection.pixelate( range )
152
+ end
153
+ end # class Tool
154
+
155
+
156
+ end # module Artbase
157
+
158
+
159
+
@@ -1,10 +1,10 @@
1
1
 
2
2
 
3
- class Artbase
3
+ module Artbase
4
4
 
5
5
  MAJOR = 0
6
- MINOR = 0
7
- PATCH = 1
6
+ MINOR = 1
7
+ PATCH = 0
8
8
  VERSION = [MAJOR,MINOR,PATCH].join('.')
9
9
 
10
10
  def self.version
data/lib/artbase.rb CHANGED
@@ -1,9 +1,42 @@
1
1
  require 'pp'
2
+ require 'time'
3
+ require 'date'
4
+
5
+ require 'uri'
6
+ require 'net/http'
7
+ require 'net/https'
8
+ require 'cgi'
9
+ require 'fileutils'
10
+
11
+ require 'json'
12
+
13
+ require 'optparse'
14
+
15
+
16
+
17
+ ## our own gems
18
+ require 'pixelart'
19
+
20
+
21
+
2
22
 
3
23
 
4
24
  ## our own code
5
25
  require 'artbase/version' # note: let version always go first
6
26
 
27
+ require 'artbase/image'
28
+
29
+
30
+ require 'artbase/helper'
31
+ require 'artbase/opensea'
32
+
33
+ require 'artbase/collection'
34
+ require 'artbase/attributes'
35
+
36
+
37
+ require 'artbase/tool'
38
+
39
+
7
40
 
8
- pp Artbase.banner
9
- pp Artbase.root
41
+ puts Artbase.banner
42
+ puts Artbase.root