artbase 0.2.1 → 0.3.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.
- checksums.yaml +4 -4
- data/Manifest.txt +0 -10
- data/Rakefile +4 -3
- data/lib/artbase/tool.rb +74 -24
- data/lib/artbase/version.rb +2 -2
- data/lib/artbase.rb +12 -78
- metadata +11 -21
- data/lib/artbase/attributes.rb +0 -83
- data/lib/artbase/collection/base.rb +0 -306
- data/lib/artbase/collection/image.rb +0 -39
- data/lib/artbase/collection/opensea.rb +0 -297
- data/lib/artbase/collection/token.rb +0 -372
- data/lib/artbase/collection.rb +0 -12
- data/lib/artbase/helper.rb +0 -169
- data/lib/artbase/image/sample.rb +0 -31
- data/lib/artbase/image.rb +0 -31
- data/lib/artbase/retry.rb +0 -41
@@ -1,306 +0,0 @@
|
|
1
|
-
|
2
|
-
module Artbase
|
3
|
-
class Base ## "abstract" Base collection - check -use a different name - why? why not?
|
4
|
-
|
5
|
-
|
6
|
-
def convert_images( overwrite: )
|
7
|
-
image_dir = "./#{slug}/token-i"
|
8
|
-
Image.convert( image_dir, from: 'jpg', to: 'png', overwrite: overwrite )
|
9
|
-
Image.convert( image_dir, from: 'gif', to: 'png', overwrite: overwrite )
|
10
|
-
Image.convert( image_dir, from: 'svg', to: 'png', overwrite: overwrite )
|
11
|
-
end
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
def make_strip
|
16
|
-
composite_count = @count - @excludes.size
|
17
|
-
|
18
|
-
composite = ImageComposite.new( 9, 1,
|
19
|
-
width: @width,
|
20
|
-
height: @height )
|
21
|
-
|
22
|
-
i = 0
|
23
|
-
each_image do |img, id|
|
24
|
-
puts "==> [#{i+1}/9] #{id}"
|
25
|
-
composite << img
|
26
|
-
|
27
|
-
i += 1
|
28
|
-
break if i >= 9
|
29
|
-
end
|
30
|
-
|
31
|
-
|
32
|
-
composite.save( "./#{@slug}/tmp/#{@slug}-strip.png" )
|
33
|
-
end
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
def make_composite
|
38
|
-
### use well-known / pre-defined (default) grids
|
39
|
-
## (cols x rows) for now - why? why not?
|
40
|
-
|
41
|
-
composite_count = @count - @excludes.size
|
42
|
-
cols, rows = case composite_count
|
43
|
-
when 99 then [10, 10]
|
44
|
-
when 100 then [10, 10]
|
45
|
-
when 150 then [15, 10]
|
46
|
-
when 314 then [15, 21]
|
47
|
-
when 500 then [25, 20]
|
48
|
-
when 1000 then [25, 40]
|
49
|
-
when 2200 then [50, 44]
|
50
|
-
when 2222 then [50, 45]
|
51
|
-
when 2469 then [50, 50]
|
52
|
-
when 3000 then [100, 30] ## or use 50*60 - why? why not?
|
53
|
-
when 3500 then [100, 35] ## or use 50*x ??
|
54
|
-
when 3979 then [100, 40]
|
55
|
-
when 4000 then [100, 40] ## or use 50x80 - why? why not?
|
56
|
-
when 4444 then [100, 45] ## or use 50x??
|
57
|
-
when 5000 then [100, 50] ## or use 50x100 - why? why not?
|
58
|
-
when 5555 then [100, 56] # 5600 (45 left empty)
|
59
|
-
when 6666 then [100, 67] # 6700 (34 left empty)
|
60
|
-
when 6688 then [100, 67] # 6700 (12 left empty)
|
61
|
-
when 6969 then [100, 70] # 7000 (31 left empty)
|
62
|
-
when 8888 then [100, 89]
|
63
|
-
when 9969 then [100,100]
|
64
|
-
when 10000 then [100,100]
|
65
|
-
else
|
66
|
-
raise ArgumentError, "sorry - unknown composite count #{composite_count}/#{@count} for now"
|
67
|
-
end
|
68
|
-
|
69
|
-
composite = ImageComposite.new( cols, rows,
|
70
|
-
width: @width,
|
71
|
-
height: @height )
|
72
|
-
|
73
|
-
each_image do |img, id|
|
74
|
-
puts "==> #{id}"
|
75
|
-
composite << img
|
76
|
-
end
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
composite.save( "./#{@slug}/tmp/#{@slug}-#{@width}x#{@height}.png" )
|
81
|
-
|
82
|
-
if composite_count < 1000
|
83
|
-
composite.zoom(2).save( "./#{@slug}/tmp/#{@slug}-#{@width}x#{@height}@2x.png" )
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
def calc_attribute_counters ## todo/check: use a different name _counts/_stats etc - why? why not?
|
91
|
-
|
92
|
-
attributes_by_count = { count: 0,
|
93
|
-
by_count: Hash.new(0)
|
94
|
-
}
|
95
|
-
counter = {}
|
96
|
-
|
97
|
-
|
98
|
-
each_meta do |meta, id| ## todo/fix: change id to index
|
99
|
-
traits = meta.traits
|
100
|
-
# print "#{traits.size} - "
|
101
|
-
# pp traits
|
102
|
-
|
103
|
-
print "#{id}.." if id % 100 == 0 ## print progress report
|
104
|
-
|
105
|
-
attributes_by_count[ :count ] +=1
|
106
|
-
attributes_by_count[ :by_count ][ traits.size ] += 1
|
107
|
-
|
108
|
-
traits.each do |trait_type, trait_value|
|
109
|
-
trait_type = _normalize_trait_type( trait_type )
|
110
|
-
trait_value = _normalize_trait_value( trait_value )
|
111
|
-
|
112
|
-
|
113
|
-
rec = counter[ trait_type ] ||= { count: 0,
|
114
|
-
by_type: Hash.new(0)
|
115
|
-
}
|
116
|
-
rec[ :count ] +=1
|
117
|
-
rec[ :by_type ][ trait_value ] += 1
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
print "\n"
|
122
|
-
puts
|
123
|
-
|
124
|
-
## return all-in-one hash
|
125
|
-
{
|
126
|
-
total: attributes_by_count,
|
127
|
-
traits: counter,
|
128
|
-
}
|
129
|
-
end
|
130
|
-
|
131
|
-
|
132
|
-
def dump_attributes
|
133
|
-
stats = calc_attribute_counters
|
134
|
-
|
135
|
-
total = stats[:total]
|
136
|
-
counter = stats[:traits]
|
137
|
-
|
138
|
-
puts
|
139
|
-
puts "attribute usage / counts:"
|
140
|
-
pp total
|
141
|
-
puts
|
142
|
-
|
143
|
-
puts "#{counter.size} attribute(s):"
|
144
|
-
counter.each do |trait_name, trait_rec|
|
145
|
-
puts " #{trait_name} #{trait_rec[:count]} (#{trait_rec[:by_type].size} uniques)"
|
146
|
-
end
|
147
|
-
|
148
|
-
puts
|
149
|
-
pp counter
|
150
|
-
end
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
## order - allow "custom" attribute order export
|
156
|
-
## renames - allow renames of attributes
|
157
|
-
def export_attributes(
|
158
|
-
order: [],
|
159
|
-
renames: {}
|
160
|
-
)
|
161
|
-
|
162
|
-
## step 1: get counters
|
163
|
-
stats = calc_attribute_counters
|
164
|
-
|
165
|
-
total = stats[:total]
|
166
|
-
counter = stats[:traits]
|
167
|
-
|
168
|
-
puts
|
169
|
-
puts "attribute usage / counts:"
|
170
|
-
pp total
|
171
|
-
puts
|
172
|
-
|
173
|
-
puts "#{counter.size} attribute(s):"
|
174
|
-
counter.each do |trait_name, trait_rec|
|
175
|
-
puts " #{trait_name} #{trait_rec[:count]} (#{trait_rec[:by_type].size} uniques)"
|
176
|
-
end
|
177
|
-
|
178
|
-
|
179
|
-
trait_names = []
|
180
|
-
trait_names += order ## get attributes if any in pre-defined order
|
181
|
-
counter.each do |trait_name, _|
|
182
|
-
if trait_names.include?( trait_name )
|
183
|
-
next ## skip already included
|
184
|
-
else
|
185
|
-
trait_names << trait_name
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
|
-
|
190
|
-
recs = []
|
191
|
-
|
192
|
-
|
193
|
-
## step 2: get tabular data
|
194
|
-
each_meta do |meta, id| ## todo/fix: change id to index
|
195
|
-
|
196
|
-
traits = meta.traits
|
197
|
-
# print "#{traits.size} - "
|
198
|
-
# pp traits
|
199
|
-
|
200
|
-
print "#{id}.." if id % 100 == 0 ## print progress report
|
201
|
-
|
202
|
-
## setup empty hash table (with all attributes)
|
203
|
-
rec = {}
|
204
|
-
|
205
|
-
## note: use __Slug__& __Name__
|
206
|
-
## to avoid conflict with attribute names
|
207
|
-
## e.g. attribute with "Name" will overwrite built-in and so on
|
208
|
-
|
209
|
-
rec['__Slug__'] = if respond_to?( :_meta_slugify )
|
210
|
-
_meta_slugify( meta, id )
|
211
|
-
else
|
212
|
-
## default to id (six digits) as string with leading zeros
|
213
|
-
## for easy sorting using strings
|
214
|
-
## e.g. 1 => '000001'
|
215
|
-
## 2 => '000002'
|
216
|
-
'%06d' % id
|
217
|
-
end
|
218
|
-
|
219
|
-
rec['__Name__'] = meta.name
|
220
|
-
|
221
|
-
## add all attributes/traits names/keys
|
222
|
-
trait_names.reduce( rec ) { |h,value| h[value] = []; h }
|
223
|
-
## pp rec
|
224
|
-
|
225
|
-
## note: use an array (to allow multiple values for attributes)
|
226
|
-
traits.each do |trait_type, trait_value|
|
227
|
-
trait_type = _normalize_trait_type( trait_type )
|
228
|
-
trait_value = _normalize_trait_value( trait_value )
|
229
|
-
|
230
|
-
values = rec[ trait_type ]
|
231
|
-
values << trait_value
|
232
|
-
end
|
233
|
-
recs << rec
|
234
|
-
end
|
235
|
-
print "\n"
|
236
|
-
|
237
|
-
## pp recs
|
238
|
-
|
239
|
-
## flatten recs
|
240
|
-
data = []
|
241
|
-
recs.each do |rec|
|
242
|
-
row = rec.values.map do |value|
|
243
|
-
if value.is_a?( Array )
|
244
|
-
value.join( ' / ' )
|
245
|
-
else
|
246
|
-
value
|
247
|
-
end
|
248
|
-
end
|
249
|
-
data << row
|
250
|
-
end
|
251
|
-
|
252
|
-
|
253
|
-
## sort by slug
|
254
|
-
data = data.sort {|l,r| l[0] <=> r[0] }
|
255
|
-
pp data
|
256
|
-
|
257
|
-
### save dataset
|
258
|
-
## note: change first colum Slug to ID - only used for "internal" sort etc.
|
259
|
-
headers = ['ID', 'Name']
|
260
|
-
headers += trait_names.map do |trait_name| ## check for renames
|
261
|
-
renames[trait_name] || trait_name
|
262
|
-
end
|
263
|
-
|
264
|
-
|
265
|
-
path = "./#{@slug}/tmp/#{@slug}.csv"
|
266
|
-
dirname = File.dirname( path )
|
267
|
-
FileUtils.mkdir_p( dirname ) unless Dir.exist?( dirname )
|
268
|
-
|
269
|
-
File.open( path, 'w:utf-8' ) do |f|
|
270
|
-
f.write( headers.join( ', ' ))
|
271
|
-
f.write( "\n" )
|
272
|
-
## note: replace ID with our own internal running (zero-based) counter
|
273
|
-
data.each_with_index do |row,i|
|
274
|
-
f.write( ([i]+row[1..-1]).join( ', '))
|
275
|
-
f.write( "\n" )
|
276
|
-
end
|
277
|
-
end
|
278
|
-
end
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
#############
|
284
|
-
# "private" helpers
|
285
|
-
|
286
|
-
def _normalize_trait_type( trait_type )
|
287
|
-
if @patch && @patch[:trait_types]
|
288
|
-
@patch[:trait_types][ trait_type ] || trait_type
|
289
|
-
else
|
290
|
-
trait_type
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
|
-
def _normalize_trait_value( trait_value )
|
295
|
-
if @patch && @patch[:trait_values]
|
296
|
-
@patch[:trait_values][ trait_value ] || trait_value
|
297
|
-
else
|
298
|
-
trait_value
|
299
|
-
end
|
300
|
-
end
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
end # class Base
|
306
|
-
end # module Artbase
|
@@ -1,39 +0,0 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
class ImageCollection
|
4
|
-
|
5
|
-
attr_reader :slug, :count
|
6
|
-
|
7
|
-
def initialize( slug, count,
|
8
|
-
image_base: ) # check: rename count to items or such - why? why not?
|
9
|
-
@slug = slug
|
10
|
-
@count = count
|
11
|
-
@image_base = image_base
|
12
|
-
end
|
13
|
-
|
14
|
-
def download_images( range=(0...@count) )
|
15
|
-
start = Time.now
|
16
|
-
delay_in_s = 0.3
|
17
|
-
|
18
|
-
range.each do |offset|
|
19
|
-
image_src = @image_base.sub( '{id}', offset.to_s )
|
20
|
-
|
21
|
-
puts "==> #{offset} - #{@slug}..."
|
22
|
-
|
23
|
-
## note: will auto-add format file extension (e.g. .png, .jpg)
|
24
|
-
## depending on http content type!!!!!
|
25
|
-
copy_image( image_src, "./#{@slug}/image-i/#{offset}" )
|
26
|
-
|
27
|
-
stop = Time.now
|
28
|
-
diff = stop - start
|
29
|
-
|
30
|
-
mins = diff / 60 ## todo - use floor or such?
|
31
|
-
secs = diff % 60
|
32
|
-
puts "up #{mins} mins #{secs} secs (total #{diff} secs)"
|
33
|
-
|
34
|
-
puts "sleeping #{delay_in_s}s..."
|
35
|
-
sleep( delay_in_s )
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end # class ImageCollection
|
39
|
-
|
@@ -1,297 +0,0 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
class Collection ## todo/check - change to OpenseaCollection or such - why? why not?
|
4
|
-
|
5
|
-
attr_reader :slug, :count
|
6
|
-
|
7
|
-
# check: rename count to items or such - why? why not?
|
8
|
-
# default format to '24x24' - why? why not?
|
9
|
-
def initialize( slug, count,
|
10
|
-
meta_slugify: nil,
|
11
|
-
image_pixelate: nil,
|
12
|
-
patch: nil,
|
13
|
-
exclude: [],
|
14
|
-
format:,
|
15
|
-
source: )
|
16
|
-
@slug = slug
|
17
|
-
@count = count
|
18
|
-
|
19
|
-
@meta_slugify = meta_slugify
|
20
|
-
@image_pixelate = image_pixelate
|
21
|
-
|
22
|
-
@patch = patch
|
23
|
-
|
24
|
-
@exclude = exclude
|
25
|
-
|
26
|
-
@width, @height = _parse_dimension( format )
|
27
|
-
|
28
|
-
|
29
|
-
## note: allow multiple source formats / dimensions
|
30
|
-
### e.g. convert 512x512 into [ [512,512] ]
|
31
|
-
##
|
32
|
-
source = [source] unless source.is_a?( Array )
|
33
|
-
@sources = source.map { |dimension| _parse_dimension( dimension ) }
|
34
|
-
end
|
35
|
-
|
36
|
-
## e.g. convert dimension (width x height) "24x24" or "24 x 24" to [24,24]
|
37
|
-
def _parse_dimension( str )
|
38
|
-
str.split( /x/i ).map { |str| str.strip.to_i }
|
39
|
-
end
|
40
|
-
|
41
|
-
|
42
|
-
def _image_pixelate( img )
|
43
|
-
if @image_pixelate
|
44
|
-
@image_pixelate.call( img )
|
45
|
-
else
|
46
|
-
@sources.each do |source_width, source_height|
|
47
|
-
if img.width == source_width && img.height == source_height
|
48
|
-
from = "#{source_width}x#{source_height}"
|
49
|
-
to = "#{@width}x#{@height}"
|
50
|
-
steps = (Image::DOwNSAMPLING_STEPS[ to ] || {})[ from ]
|
51
|
-
if steps.nil?
|
52
|
-
puts "!! ERROR - no sampling steps defined for #{from} to #{to}; sorry"
|
53
|
-
exit 1
|
54
|
-
end
|
55
|
-
|
56
|
-
return img.pixelate( steps )
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
puts "!! ERROR - unknown image dimension #{img.width}x#{img.height}; sorry"
|
61
|
-
puts " supported source dimensions include: #{@sources.inspect}"
|
62
|
-
exit 1
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
def download_meta( range=(0...@count) )
|
70
|
-
self.class.download_meta( range, @slug )
|
71
|
-
end
|
72
|
-
|
73
|
-
def download_images( range=(0...@count) )
|
74
|
-
self.class.download_images( range, @slug )
|
75
|
-
end
|
76
|
-
|
77
|
-
def download( range=(0...@count) )
|
78
|
-
download_meta( range )
|
79
|
-
download_images( range )
|
80
|
-
end
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
def _meta_slugify_match( regex, meta, index )
|
87
|
-
if m=regex.match( meta.name )
|
88
|
-
captures = m.named_captures ## get named captures in match data as hash (keys as strings)
|
89
|
-
# e.g.
|
90
|
-
#=> {"num"=>"3"}
|
91
|
-
#=> {"num"=>"498", "name"=>"Doge"}
|
92
|
-
pp captures
|
93
|
-
|
94
|
-
num = captures['num'] ? captures['num'].to_i( 10 ) : nil ## note: add base 10 (e.g. 015=>15)
|
95
|
-
name = captures['name'] ? captures['name'].strip : nil
|
96
|
-
|
97
|
-
slug = ''
|
98
|
-
if num
|
99
|
-
slug << "%06d" % num ## todo/check: always fill/zero-pad with six 000000's - why? why not?
|
100
|
-
end
|
101
|
-
|
102
|
-
if name
|
103
|
-
slug << "-" if num ## add separator
|
104
|
-
slug << slugify( name )
|
105
|
-
end
|
106
|
-
slug
|
107
|
-
else
|
108
|
-
nil ## note: return nil if no match / slug
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
|
-
def _do_meta_slugify( meta_slugify, meta, index )
|
113
|
-
if meta_slugify.is_a?( Regexp )
|
114
|
-
_meta_slugify_match( meta_slugify, meta, index )
|
115
|
-
elsif meta_slugify.is_a?( Proc )
|
116
|
-
meta_slugify.call( meta, index )
|
117
|
-
else
|
118
|
-
raise ArgumentError, "meta_slugify - unsupported type: #{meta_slugify.class.name}"
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
|
123
|
-
def _meta_slugify( meta, index )
|
124
|
-
slug = nil
|
125
|
-
|
126
|
-
if @meta_slugify.is_a?( Array )
|
127
|
-
@meta_slugify.each do |meta_slugify|
|
128
|
-
slug = _do_meta_slugify( meta_slugify, meta, index )
|
129
|
-
return slug if slug ## note: short-circuit on first match
|
130
|
-
## use break instead of return - why? why not?
|
131
|
-
end
|
132
|
-
else ## assume object e.g. Regexp, Proc, etc.
|
133
|
-
slug = _do_meta_slugify( @meta_slugify, meta, index )
|
134
|
-
end
|
135
|
-
|
136
|
-
## do nothing
|
137
|
-
if slug.nil?
|
138
|
-
puts "!! ERROR - cannot find id in >#{meta.name}<:"
|
139
|
-
pp meta
|
140
|
-
exit 1
|
141
|
-
end
|
142
|
-
|
143
|
-
slug
|
144
|
-
end
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
def each_meta( range=(0...@count),
|
149
|
-
exclude: true, &blk )
|
150
|
-
range.each do |id| ## todo/fix: change id to index
|
151
|
-
meta = OpenSea::Meta.read( "./#{@slug}/meta/#{id}.json" )
|
152
|
-
|
153
|
-
####
|
154
|
-
# filter out/skip
|
155
|
-
if exclude && @exclude.include?( meta.name )
|
156
|
-
puts " skipping / exclude #{id} >#{meta.name}<..."
|
157
|
-
next
|
158
|
-
end
|
159
|
-
|
160
|
-
blk.call( meta, id )
|
161
|
-
end
|
162
|
-
end
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
def pixelate( range=(0...@count) )
|
168
|
-
|
169
|
-
meta_slugs = Hash.new( 0 ) ## deduplicate (auto-add counter if duplicate)
|
170
|
-
|
171
|
-
### todo/fix: must read slugs starting at 0
|
172
|
-
### to work for deduplicate!!!!!!
|
173
|
-
|
174
|
-
|
175
|
-
range.each do |id|
|
176
|
-
meta = OpenSea::Meta.read( "./#{@slug}/meta/#{id}.json" )
|
177
|
-
|
178
|
-
####
|
179
|
-
# filter out/skip
|
180
|
-
if @exclude.include?( meta.name )
|
181
|
-
puts " skipping / exclude #{id} >#{meta.name}<..."
|
182
|
-
next
|
183
|
-
end
|
184
|
-
|
185
|
-
puts meta.name
|
186
|
-
|
187
|
-
|
188
|
-
meta_slug = _meta_slugify( meta, id )
|
189
|
-
count = meta_slugs[ meta_slug ] += 1
|
190
|
-
|
191
|
-
meta_slug = "#{meta_slug}_(#{count})" if count > 1
|
192
|
-
|
193
|
-
|
194
|
-
img = Image.read( "./#{@slug}/i/#{id}.png" )
|
195
|
-
|
196
|
-
pix = _image_pixelate( img )
|
197
|
-
|
198
|
-
path = "./#{@slug}/ii/#{meta_slug}.png"
|
199
|
-
puts " saving to >#{path}<..."
|
200
|
-
pix.save( path )
|
201
|
-
end
|
202
|
-
end
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
################################
|
207
|
-
# private (static) helpers
|
208
|
-
#
|
209
|
-
|
210
|
-
def self.download_images( range, collection,
|
211
|
-
original: false )
|
212
|
-
start = Time.now
|
213
|
-
delay_in_s = 0.3
|
214
|
-
|
215
|
-
range.each do |offset|
|
216
|
-
meta = OpenSea::Meta.read( "./#{collection}/meta/#{offset}.json" )
|
217
|
-
|
218
|
-
puts "==> #{offset}.json - #{meta.name}"
|
219
|
-
|
220
|
-
image_src = if original
|
221
|
-
meta.image_original_url
|
222
|
-
else
|
223
|
-
meta.image_url
|
224
|
-
end
|
225
|
-
|
226
|
-
puts " >#{image_src}<"
|
227
|
-
if image_src.nil?
|
228
|
-
puts "!! ERROR - no image url found (use original: #{original}):"
|
229
|
-
pp meta
|
230
|
-
exit 1
|
231
|
-
end
|
232
|
-
|
233
|
-
## note: use a different directory to avoid size confusion!!!
|
234
|
-
img_slug = if original
|
235
|
-
'i_org'
|
236
|
-
else
|
237
|
-
'i'
|
238
|
-
end
|
239
|
-
|
240
|
-
## note: will auto-add format file extension (e.g. .png, .jpg)
|
241
|
-
## depending on http content type!!!!!
|
242
|
-
copy_image( image_src, "./#{collection}/#{img_slug}/#{offset}" )
|
243
|
-
|
244
|
-
stop = Time.now
|
245
|
-
diff = stop - start
|
246
|
-
|
247
|
-
mins = diff / 60 ## todo - use floor or such?
|
248
|
-
secs = diff % 60
|
249
|
-
puts "up #{mins} mins #{secs} secs (total #{diff} secs)"
|
250
|
-
|
251
|
-
puts "sleeping #{delay_in_s}s..."
|
252
|
-
sleep( delay_in_s )
|
253
|
-
end
|
254
|
-
end
|
255
|
-
|
256
|
-
|
257
|
-
def self.download_meta( range, collection )
|
258
|
-
start = Time.now
|
259
|
-
delay_in_s = 0.3
|
260
|
-
|
261
|
-
range.each do |offset|
|
262
|
-
|
263
|
-
dest = "./#{collection}/meta/#{offset}.json"
|
264
|
-
meta = nil
|
265
|
-
|
266
|
-
puts "==> #{offset} / #{collection} (#{dest})..."
|
267
|
-
|
268
|
-
data = OpenSea.assets( collection: collection,
|
269
|
-
offset: offset )
|
270
|
-
meta = OpenSea::Meta.new( data )
|
271
|
-
puts " name: >#{meta.name}<"
|
272
|
-
puts " image_url: >#{meta.image_url}<"
|
273
|
-
|
274
|
-
|
275
|
-
## make sure path exists
|
276
|
-
dirname = File.dirname( dest )
|
277
|
-
FileUtils.mkdir_p( dirname ) unless Dir.exist?( dirname )
|
278
|
-
|
279
|
-
File.open( dest, "w:utf-8" ) do |f|
|
280
|
-
f.write( JSON.pretty_generate( data ) )
|
281
|
-
end
|
282
|
-
|
283
|
-
|
284
|
-
stop = Time.now
|
285
|
-
diff = stop - start
|
286
|
-
|
287
|
-
mins = diff / 60 ## todo - use floor or such?
|
288
|
-
secs = diff % 60
|
289
|
-
puts "up #{mins} mins #{secs} secs (total #{diff} secs)"
|
290
|
-
|
291
|
-
puts " sleeping #{delay_in_s}s..."
|
292
|
-
sleep( delay_in_s )
|
293
|
-
end
|
294
|
-
end
|
295
|
-
|
296
|
-
|
297
|
-
end # class Collection
|