ordinals 0.0.1 → 1.0.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 +5 -0
- data/README.md +147 -4
- data/Rakefile +3 -2
- data/bin/ordbase +17 -0
- data/lib/ordinals/api.rb +224 -0
- data/lib/ordinals/collection.rb +256 -0
- data/lib/ordinals/stats.rb +142 -0
- data/lib/ordinals/tool.rb +121 -0
- data/lib/ordinals.rb +90 -1
- metadata +27 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23ab1c1b894f3d7d528d90943d890e07c40d878cc56b54206384be335ae47fa6
|
4
|
+
data.tar.gz: f3ca9276b04d13052424d9a4834204d7153a92681507efac9c8511949e6ec362
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a91a05ebbaf2ab66030ef45bf9a8da6744df0f40d3014451364f1d802a0193c0a7450f90647612e95c6469b7c21a6f8c6f04a10e3a354d820cafa88de32ccc6b
|
7
|
+
data.tar.gz: 30ca5d81426d13f3149a37f9eb8a7bc3a703c22d9cf0685c991033273ae81efe4917fce3d00f68aadfaf9742ab0d626bdc6e290a2f52c8b2c967b0453ebaa475
|
data/Manifest.txt
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# ordinals
|
2
2
|
|
3
|
-
ordinals gem - (off-chain) ordinals (pixel art) machinery & helpers for Bitcoin & co.
|
3
|
+
ordinals gem - "right-clicker" (off-chain) ordinals (pixel art) machinery & helpers for Bitcoin, Litcoin, Dogecoin & co.
|
4
4
|
|
5
5
|
|
6
6
|
* home :: [github.com/pixelartexchange/ordinals.sandbox](https://github.com/pixelartexchange/ordinals.sandbox)
|
@@ -9,9 +9,152 @@ ordinals gem - (off-chain) ordinals (pixel art) machinery & helpers for Bitcoin
|
|
9
9
|
* rdoc :: [rubydoc.info/gems/ordinals](http://rubydoc.info/gems/ordinals)
|
10
10
|
|
11
11
|
|
12
|
-
## Usage
|
13
12
|
|
14
|
-
|
13
|
+
## Command-Line Usage
|
14
|
+
|
15
|
+
Let's use the 100 Ordinal Punks collection to try out the
|
16
|
+
`ordbase` command-line tool shipping with the ordinals package.
|
17
|
+
|
18
|
+
Tip: New to Ordinal Punks? For some background see [**Awesome 100 Ordinal Punks (Anno 2023) Notes - 24×24 Pixel Art on the (Bitcoin) Blockchain »**](https://github.com/cryptopunksnotdead/cryptopunks/tree/master/awesome-ordinalpunks)
|
19
|
+
|
20
|
+
|
21
|
+
### Step 0: Prepare A Tabular Dataset (List) Of All Ordinals w/ ID
|
22
|
+
|
23
|
+
For now a manual step - prepare a list of all ordinals with id in the comma-separated values (.csv) tabular dataset format.
|
24
|
+
Example - [ordinalpunks/ordinals.csv](https://github.com/pixelartexchange/ordinals.sandbox/blob/master/ordinalpunks/ordinals.csv):
|
25
|
+
|
26
|
+
``` csv
|
27
|
+
num, id
|
28
|
+
1, 96d87d7e59d75ebc0e6144b09fdd96355fcdaa86fd098d64c46f19a424012bbei0
|
29
|
+
2, acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37i0
|
30
|
+
3, 0406654dffdd01a49794bd8531bf33721986cc7c6546f871962adee921a39a9di0
|
31
|
+
4, 2fe9bb034f60db694701acb23a76c3d7d5aba4328dbd315764f6ee406ba41786i0
|
32
|
+
5, dcfa240f2681d1e4a8948120a3a64567262e3c78d5497cb4e97351bfa836b638i0
|
33
|
+
6, 16df62c86321895df2b93236d103c935015ed77e189485be649ce2c7e6ac8a4ei0
|
34
|
+
7, 81e8d9159b8e9a27c692a5bb3ba18ca037757e94e975b53e175eaaeb2c52f15ai0
|
35
|
+
8, c2e15fe87c4b1fd61de65f2804858e6d1152b6316bcb9c2b39b69c9c21638f5di0
|
36
|
+
9, 3ed569f3a92ade9f1b47031eb2db2045e7dee3e00787954a88c67ed2ad9854bbi0
|
37
|
+
...
|
38
|
+
```
|
39
|
+
|
40
|
+
|
41
|
+
### Step 1: Download All Pixel Art Images Via Ordinals.com
|
42
|
+
|
43
|
+
Use
|
44
|
+
|
45
|
+
```
|
46
|
+
$ ordbase ordinalpunks image # or
|
47
|
+
$ ordbase ordinalpunks img
|
48
|
+
```
|
49
|
+
|
50
|
+
to download all images via the ordinals.com (web) service.
|
51
|
+
All images get stored in the (temporary) `token-i/` directory.
|
52
|
+
Resulting in:
|
53
|
+
|
54
|
+
```
|
55
|
+
/ordinalpunks
|
56
|
+
ordinals.csv
|
57
|
+
/token-i
|
58
|
+
1.png
|
59
|
+
2.png
|
60
|
+
3.png
|
61
|
+
...
|
62
|
+
```
|
63
|
+
|
64
|
+

|
65
|
+

|
66
|
+

|
67
|
+

|
68
|
+

|
69
|
+

|
70
|
+
...
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
|
75
|
+
### Step 2: Downsample ("Pixelate") All Pixel Art Images
|
76
|
+
|
77
|
+
Note: Most pixel art collections upload / inscribe images with a zoom.
|
78
|
+
The ordinal punks, for example, use a 8x zoom factor for the 24×24px originals, thus,
|
79
|
+
resulting in 192×192px.
|
80
|
+
|
81
|
+
|
82
|
+
Add a ["artbase-compatible"](https://github.com/pixelartexchange/artbase) collection configuration file to lists the source format(s)
|
83
|
+
and the minimal true pixel format.
|
84
|
+
Example - [ordinalpunks/collection.yml](https://github.com/pixelartexchange/ordinals.sandbox/blob/master/ordinalpunks/collection.yml):
|
85
|
+
|
86
|
+
``` yaml
|
87
|
+
slug: ordinalpunks
|
88
|
+
count: 100
|
89
|
+
format: 24x24
|
90
|
+
source: 192x192
|
91
|
+
offset: 1
|
92
|
+
```
|
93
|
+
|
94
|
+
Use
|
95
|
+
|
96
|
+
```
|
97
|
+
$ ordbase ordinalpunks pixelate # or
|
98
|
+
$ ordbase ordinalpunks px
|
99
|
+
```
|
100
|
+
|
101
|
+
to downsample ("pixelate") all images
|
102
|
+
in the (temporary) `token-i/` directory.
|
103
|
+
Resulting in a `24x24`/ directory with all images
|
104
|
+
in the "minimal" `24x24` format:
|
105
|
+
|
106
|
+
```
|
107
|
+
/ordinalpunks
|
108
|
+
ordinals.csv
|
109
|
+
collections.yml
|
110
|
+
/24x24
|
111
|
+
1.png
|
112
|
+
2.png
|
113
|
+
3.png
|
114
|
+
...
|
115
|
+
```
|
116
|
+
|
117
|
+

|
118
|
+

|
119
|
+

|
120
|
+

|
121
|
+

|
122
|
+

|
123
|
+
...
|
124
|
+
|
125
|
+
|
126
|
+
|
127
|
+
### Bonus: Step 3: Make An All-In-One Collect'Em All Composite Image
|
128
|
+
|
129
|
+
|
130
|
+
Use
|
131
|
+
|
132
|
+
```
|
133
|
+
$ ordbase ordinalpunks composite # or
|
134
|
+
$ ordbase ordinalpunks comp
|
135
|
+
```
|
136
|
+
|
137
|
+
to make an all-in-one image composite for the complete collection.
|
138
|
+
Resulting in `/tmp/ordinalpunks.png` (~11kb).
|
139
|
+
|
140
|
+
|
141
|
+

|
142
|
+
|
143
|
+
|
144
|
+
That's it for now.
|
145
|
+
|
146
|
+
|
147
|
+
|
148
|
+
|
149
|
+
## Bonus: More Ordinal Pixel Art Collections
|
150
|
+
|
151
|
+
|
152
|
+
See the [**Ordinals (Pixel Art) Sandbox (& Cache)**](https://github.com/pixelartexchange/ordinals.sandbox)
|
153
|
+
for more collections incl. Bitcoin Punks (24×24), Ordinal Mini Doges (24×24),
|
154
|
+
Extra Ordinal Women (32×32), Ordinal Penguins (35×35),
|
155
|
+
Ordinal Birds (42×42), Bitcoin Bears (48×48) and much more.
|
156
|
+
|
157
|
+
Add your sandbox or "right-clicker" ordinal backup / archive / gallery here. Yes, you can.
|
15
158
|
|
16
159
|
|
17
160
|
|
@@ -23,6 +166,6 @@ Use it as you please with no restrictions whatsoever.
|
|
23
166
|
|
24
167
|
## Questions? Comments?
|
25
168
|
|
169
|
+
Post them over at the [Help & Support](https://github.com/geraldb/help) page. Thanks.
|
26
170
|
|
27
|
-
Post them on the [D.I.Y. Punk (Pixel) Art reddit](https://old.reddit.com/r/DIYPunkArt). Thanks.
|
28
171
|
|
data/Rakefile
CHANGED
@@ -10,9 +10,9 @@ end
|
|
10
10
|
|
11
11
|
Hoe.spec 'ordinals' do
|
12
12
|
|
13
|
-
self.version = '0.0
|
13
|
+
self.version = '1.0.0'
|
14
14
|
|
15
|
-
self.summary =
|
15
|
+
self.summary = 'ordinals gem - "right-clicker" (off-chain) ordinals (pixel art) machinery & helpers for Bitcoin, Litecoin, Dogecoin & co.'
|
16
16
|
self.description = summary
|
17
17
|
|
18
18
|
self.urls = { home: 'https://github.com/pixelartexchange/ordinals.sandbox' }
|
@@ -27,6 +27,7 @@ Hoe.spec 'ordinals' do
|
|
27
27
|
self.extra_deps = [
|
28
28
|
['cocos'],
|
29
29
|
['pixelart'],
|
30
|
+
['nokogiri'], ## required / used by api support (html parsing)
|
30
31
|
]
|
31
32
|
|
32
33
|
self.licenses = ['Public Domain']
|
data/bin/ordbase
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
###################
|
4
|
+
# == DEV TIPS:
|
5
|
+
#
|
6
|
+
# For local testing run like:
|
7
|
+
#
|
8
|
+
# ruby -Ilib bin/ordbase
|
9
|
+
#
|
10
|
+
# Set the executable bit in Linux. Example:
|
11
|
+
#
|
12
|
+
# % chmod a+x bin/ordbase
|
13
|
+
#
|
14
|
+
|
15
|
+
require 'ordinals'
|
16
|
+
|
17
|
+
Ordinals::Tool.main
|
data/lib/ordinals/api.rb
ADDED
@@ -0,0 +1,224 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module Ordinals
|
4
|
+
|
5
|
+
|
6
|
+
class Api ## change/rename Api to Client - why? why not?
|
7
|
+
def self.litecoin
|
8
|
+
@litecoin ||= new( 'https://litecoin.earlyordies.com' )
|
9
|
+
@litecoin
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.bitcoin
|
13
|
+
@bitcoin ||= new( 'https://ordinals.com' )
|
14
|
+
@bitcoin
|
15
|
+
end
|
16
|
+
## todo: add ltc and btc alias - why? why not?
|
17
|
+
|
18
|
+
def self.dogecoin
|
19
|
+
## note: "doginals" call inscriptions
|
20
|
+
## shibescriptions
|
21
|
+
@dogecoin ||= new( 'https://doginals.com', inscription: 'shibescription' )
|
22
|
+
@dogecoin
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
def initialize( base, inscription: 'inscription' )
|
27
|
+
@base = base
|
28
|
+
@inscription = inscription
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
###
|
33
|
+
# use a struct-like content class - why? why not?
|
34
|
+
class Content
|
35
|
+
attr_reader :data,
|
36
|
+
:type,
|
37
|
+
:length
|
38
|
+
def initialize( data, type, length )
|
39
|
+
@data = data
|
40
|
+
@type = type
|
41
|
+
@length = length
|
42
|
+
end
|
43
|
+
|
44
|
+
alias_method :blob, :data
|
45
|
+
end ## (nested) class Content
|
46
|
+
|
47
|
+
|
48
|
+
|
49
|
+
def content( id )
|
50
|
+
src = "#{@base}/content/#{id}"
|
51
|
+
res = get( src )
|
52
|
+
|
53
|
+
content_type = res.content_type
|
54
|
+
content_length = res.content_length
|
55
|
+
|
56
|
+
## note - content_length -- returns an integer (number)
|
57
|
+
## puts "content_length:"
|
58
|
+
## print content_length.inspect
|
59
|
+
## print " - #{content_length.class.name}\n"
|
60
|
+
|
61
|
+
content = Content.new(
|
62
|
+
res.blob,
|
63
|
+
content_type,
|
64
|
+
content_length )
|
65
|
+
content
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
=begin
|
70
|
+
<dl>
|
71
|
+
<dt>id</dt>
|
72
|
+
<dd class=monospace>d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2i0</dd>
|
73
|
+
<dt>address</dt>
|
74
|
+
<dd class=monospace>bc1pqapcyswesvccgqsmuncd96ylghs9juthqeshdr8smmh9w7azn8zsghjjar</dd>
|
75
|
+
<dt>output value</dt>
|
76
|
+
<dd>10000</dd>
|
77
|
+
<dt>sat</dt>
|
78
|
+
<dd><a href=/sat/1320953397332258>1320953397332258</a></dd>
|
79
|
+
<dt>preview</dt>
|
80
|
+
<dd><a href=/preview/d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2i0>link</a></dd>
|
81
|
+
<dt>content</dt>
|
82
|
+
<dd><a href=/content/d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2i0>link</a></dd>
|
83
|
+
<dt>content length</dt>
|
84
|
+
<dd>71997 bytes</dd>
|
85
|
+
<dt>content type</dt>
|
86
|
+
<dd>text/plain;charset=utf-8</dd>
|
87
|
+
<dt>timestamp</dt>
|
88
|
+
<dd><time>2023-02-11 21:39:00 UTC</time></dd>
|
89
|
+
<dt>genesis height</dt>
|
90
|
+
<dd><a href=/block/776090>776090</a></dd>
|
91
|
+
<dt>genesis fee</dt>
|
92
|
+
<dd>273630</dd>
|
93
|
+
<dt>genesis transaction</dt>
|
94
|
+
<dd><a class=monospace href=/tx/d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2>d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2</a></dd>
|
95
|
+
<dt>location</dt>
|
96
|
+
<dd class=monospace>d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2:0:0</dd>
|
97
|
+
<dt>output</dt>
|
98
|
+
<dd><a class=monospace href=/output/d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2:0>d026ac5994f698dba475681359b6c29d6d39a895484b95e06b7ae49921d80df2:0</a></dd>
|
99
|
+
<dt>offset</dt>
|
100
|
+
<dd>0</dd>
|
101
|
+
</dl>
|
102
|
+
|
103
|
+
<dl>
|
104
|
+
<dt>id</dt>
|
105
|
+
<dd class=monospace>acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37i0</dd>
|
106
|
+
<dt>address</dt>
|
107
|
+
<dd class=monospace>bc1qx3scwushtwenxtxlnjet4x2w5vg35etdtse5u0</dd>
|
108
|
+
<dt>output value</dt>
|
109
|
+
<dd>8020</dd>
|
110
|
+
<dt>sat</dt>
|
111
|
+
<dd><a href=/sat/1883186433806857>1883186433806857</a></dd>
|
112
|
+
<dt>preview</dt>
|
113
|
+
<dd><a href=/preview/acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37i0>link</a></dd>
|
114
|
+
<dt>content</dt>
|
115
|
+
<dd><a href=/content/acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37i0>link</a></dd>
|
116
|
+
<dt>content length</dt>
|
117
|
+
<dd>529 bytes</dd>
|
118
|
+
<dt>content type</dt>
|
119
|
+
<dd>image/png</dd>
|
120
|
+
<dt>timestamp</dt>
|
121
|
+
<dd><time>2023-01-31 19:34:47 UTC</time></dd>
|
122
|
+
<dt>genesis height</dt>
|
123
|
+
<dd><a href=/block/774489>774489</a></dd>
|
124
|
+
<dt>genesis fee</dt>
|
125
|
+
<dd>5340</dd>
|
126
|
+
<dt>genesis transaction</dt>
|
127
|
+
<dd><a class=monospace href=/tx/acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37>acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37</a></dd>
|
128
|
+
<dt>location</dt>
|
129
|
+
<dd class=monospace>6630ff2153985504b180fc16721d559a4cecdc66f4be6acf33509ec2100c0aa5:0:0</dd>
|
130
|
+
<dt>output</dt>
|
131
|
+
<dd><a class=monospace href=/output/6630ff2153985504b180fc16721d559a4cecdc66f4be6acf33509ec2100c0aa5:0>6630ff2153985504b180fc16721d559a4cecdc66f4be6acf33509ec2100c0aa5:0</a></dd>
|
132
|
+
<dt>offset</dt>
|
133
|
+
<dd>0</dd>
|
134
|
+
</dl>
|
135
|
+
|
136
|
+
|
137
|
+
|
138
|
+
<title>Inscription 407</title>
|
139
|
+
|
140
|
+
id =
|
141
|
+
genesis transaction + offset ???
|
142
|
+
|
143
|
+
genesis transaction: acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37
|
144
|
+
id: acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37i0
|
145
|
+
address: bc1qx3scwushtwenxtxlnjet4x2w5vg35etdtse5u0
|
146
|
+
output value: 8020
|
147
|
+
sat: 1883186433806857
|
148
|
+
content length: 529 bytes
|
149
|
+
content type: image/png
|
150
|
+
timestamp: 2023-01-31 19:34:47 UTC
|
151
|
+
genesis height: 774489
|
152
|
+
genesis fee: 5340
|
153
|
+
genesis transaction: acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37>acda637db995df796b35035fd978cc1a947f1e6fd5215968da88b7e38a7e4b37
|
154
|
+
location: 6630ff2153985504b180fc16721d559a4cecdc66f4be6acf33509ec2100c0aa5:0:0
|
155
|
+
output: 6630ff2153985504b180fc16721d559a4cecdc66f4be6acf33509ec2100c0aa5:0
|
156
|
+
offset: 0
|
157
|
+
|
158
|
+
=end
|
159
|
+
|
160
|
+
def inscription( id )
|
161
|
+
src = "#{@base}/#{@inscription}/#{id}"
|
162
|
+
res = get( src )
|
163
|
+
|
164
|
+
data = _parse_inscription( res.text )
|
165
|
+
data
|
166
|
+
end
|
167
|
+
|
168
|
+
|
169
|
+
def _parse_inscription( html )
|
170
|
+
doc = Nokogiri::HTML( html )
|
171
|
+
|
172
|
+
items = []
|
173
|
+
|
174
|
+
title = doc.css( 'head title' )
|
175
|
+
items << ['title', title.text]
|
176
|
+
|
177
|
+
|
178
|
+
dls = doc.css( 'body dl' )
|
179
|
+
dls[0].css( 'dt,dd' ).each do |el|
|
180
|
+
if el.name == 'dt'
|
181
|
+
items << [el.text]
|
182
|
+
elsif el.name == 'dd'
|
183
|
+
items[-1] << el.text
|
184
|
+
else
|
185
|
+
puts "!! ERROR - unexpected tag; expected dd|dl; got: #{el.name}"
|
186
|
+
exit 1
|
187
|
+
end
|
188
|
+
end
|
189
|
+
items
|
190
|
+
|
191
|
+
## convert to hash
|
192
|
+
## and check for duplicate
|
193
|
+
data = {}
|
194
|
+
items.each do |k,v|
|
195
|
+
k = k.strip
|
196
|
+
v = v.strip
|
197
|
+
if data.has_key?( k )
|
198
|
+
puts "!! ERROR - duplicate key >#{k}< in:"
|
199
|
+
pp items
|
200
|
+
exit 1
|
201
|
+
end
|
202
|
+
data[ k ] = v
|
203
|
+
end
|
204
|
+
data
|
205
|
+
end
|
206
|
+
|
207
|
+
|
208
|
+
def get( src )
|
209
|
+
res = Webclient.get( src )
|
210
|
+
if res.status.ok?
|
211
|
+
res
|
212
|
+
else
|
213
|
+
puts "!! ERROR - HTTP #{res.status.code} #{res.status.message} - failed web request >#{src}<; sorry"
|
214
|
+
exit 1
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end # class Api
|
218
|
+
|
219
|
+
##
|
220
|
+
## add convenience alias
|
221
|
+
API = Api
|
222
|
+
|
223
|
+
end ## module Ordinals
|
224
|
+
|
@@ -0,0 +1,256 @@
|
|
1
|
+
|
2
|
+
module Ordinals
|
3
|
+
|
4
|
+
|
5
|
+
class Collection
|
6
|
+
attr_reader :slug,
|
7
|
+
:width, :height,
|
8
|
+
:sources
|
9
|
+
|
10
|
+
|
11
|
+
def initialize( slug )
|
12
|
+
@slug = slug
|
13
|
+
|
14
|
+
## read config if present
|
15
|
+
config_path = "./#{@slug}/collection.yml"
|
16
|
+
if File.exist?( config_path )
|
17
|
+
config = read_yaml( config_path )
|
18
|
+
pp config
|
19
|
+
|
20
|
+
@width, @height = _parse_dimension( config['format'] )
|
21
|
+
|
22
|
+
## note: allow multiple source formats / dimensions
|
23
|
+
### e.g. convert 512x512 into [ [512,512] ]
|
24
|
+
##
|
25
|
+
source = config['source']
|
26
|
+
source = [source] unless source.is_a?( Array )
|
27
|
+
@sources = source.map { |dimension| _parse_dimension( dimension ) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
def ordinals
|
33
|
+
@ordinals ||= begin
|
34
|
+
recs = read_csv( "./#{@slug}/ordinals.csv" )
|
35
|
+
puts " #{recs.size} record(s)"
|
36
|
+
recs
|
37
|
+
end
|
38
|
+
@ordinals
|
39
|
+
end
|
40
|
+
|
41
|
+
def count() ordinals.size; end
|
42
|
+
alias_method :size, :count
|
43
|
+
|
44
|
+
|
45
|
+
|
46
|
+
def each_ordinal( &block )
|
47
|
+
ordinals.each_with_index do |rec, i| ## pass along hash rec for now - why? why not?
|
48
|
+
block.call( rec, i )
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
## add each_source_image or each_token_image or each_original_image or such - why? why not??
|
53
|
+
|
54
|
+
|
55
|
+
|
56
|
+
def each_image( &block )
|
57
|
+
each_ordinal do |rec, i|
|
58
|
+
id = rec['id']
|
59
|
+
num = rec.has_key?('num') ? rec['num'].to_i(10) : i+1
|
60
|
+
|
61
|
+
path = "./#{@slug}/#{@width}x#{@height}/#{num}.png"
|
62
|
+
img = Image.read( path )
|
63
|
+
|
64
|
+
block.call( img, num )
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
|
70
|
+
def image_dir() "./#{@slug}/token-i"; end
|
71
|
+
|
72
|
+
|
73
|
+
|
74
|
+
## e.g. convert dimension (width x height) "24x24" or "24 x 24" to [24,24]
|
75
|
+
def _parse_dimension( str )
|
76
|
+
str.split( /x/i ).map { |str| str.strip.to_i }
|
77
|
+
end
|
78
|
+
|
79
|
+
|
80
|
+
def pixelate
|
81
|
+
each_ordinal do |rec, i|
|
82
|
+
id = rec['id']
|
83
|
+
num = rec.has_key?('num') ? rec['num'].to_i(10) : i+1
|
84
|
+
|
85
|
+
outpath = "./#{@slug}/#{@width}x#{@height}/#{num}.png"
|
86
|
+
next if File.exist?( outpath )
|
87
|
+
|
88
|
+
path = "#{image_dir}/#{num}.png"
|
89
|
+
puts "==> reading #{path}..."
|
90
|
+
|
91
|
+
img = Image.read( path )
|
92
|
+
puts " #{img.width}x#{img.height}"
|
93
|
+
|
94
|
+
## check for source images
|
95
|
+
if !@sources.include?( [img.width, img.height] )
|
96
|
+
puts " !! ERROR - unexpected image size; sorry - expected:"
|
97
|
+
pp @sources
|
98
|
+
exit 1
|
99
|
+
end
|
100
|
+
|
101
|
+
## check for special case source == format!!
|
102
|
+
if [img.width,img.height] == [@width,@height]
|
103
|
+
puts " note: saving image as is - no downsampling"
|
104
|
+
img.save( outpath )
|
105
|
+
else
|
106
|
+
steps_x = Image.calc_sample_steps( img.width, @width )
|
107
|
+
steps_y = Image.calc_sample_steps( img.height, @height )
|
108
|
+
|
109
|
+
img = img.sample( steps_x, steps_y )
|
110
|
+
img.save( outpath )
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
|
116
|
+
|
117
|
+
|
118
|
+
def make_composite
|
119
|
+
cols, rows = case count
|
120
|
+
when 10 then [5, 2]
|
121
|
+
when 12 then [4, 3]
|
122
|
+
when 15 then [5, 3]
|
123
|
+
when 20 then [5, 4]
|
124
|
+
when 50 then [10, 5]
|
125
|
+
when 69 then [10, 7]
|
126
|
+
when 80 then [10, 8]
|
127
|
+
when 88 then [10, 9]
|
128
|
+
when 98,99 then [10, 10]
|
129
|
+
when 100 then [10, 10]
|
130
|
+
when 101 then [11, 10]
|
131
|
+
when 111 then [11, 11]
|
132
|
+
when 130 then [10, 13]
|
133
|
+
when 512 then [20, 26]
|
134
|
+
else
|
135
|
+
raise ArgumentError, "sorry - unknown composite count #{count} for now"
|
136
|
+
end
|
137
|
+
|
138
|
+
composite = ImageComposite.new( cols, rows,
|
139
|
+
width: @width,
|
140
|
+
height: @height )
|
141
|
+
|
142
|
+
|
143
|
+
each_image do |img, num|
|
144
|
+
puts "==> #{num}"
|
145
|
+
composite << img
|
146
|
+
end
|
147
|
+
|
148
|
+
composite.save( "./#{@slug}/tmp/#{@slug}.png" )
|
149
|
+
end
|
150
|
+
|
151
|
+
|
152
|
+
|
153
|
+
def convert_images
|
154
|
+
Image.convert( image_dir, from: 'jpg',
|
155
|
+
to: 'png' )
|
156
|
+
|
157
|
+
Image.convert( image_dir, from: 'gif',
|
158
|
+
to: 'png' )
|
159
|
+
|
160
|
+
Image.convert( image_dir, from: 'webp',
|
161
|
+
to: 'png' )
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
|
166
|
+
def fix_images ## todo - find a different names for resaving png images?
|
167
|
+
## "repair" png images
|
168
|
+
## starting w/
|
169
|
+
## - why?
|
170
|
+
##
|
171
|
+
## verify_signature! - ChunkyPNG::SignatureMismatch:
|
172
|
+
## PNG signature not found,
|
173
|
+
## found "RIFF\\xFE\\b\\x00\\x00"
|
174
|
+
## instead of "\\x89PNG\\r\\n\\x1A\\n"
|
175
|
+
|
176
|
+
Image.convert( image_dir, from: 'png',
|
177
|
+
to: 'png' )
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
def download_meta ## inscription metadata
|
182
|
+
each_ordinal do |rec, i|
|
183
|
+
id = rec['id']
|
184
|
+
num = rec.has_key?('num') ? rec['num'].to_i(10) : i+1
|
185
|
+
|
186
|
+
chain = Ordinals.config.chain
|
187
|
+
path = "../ordinals.cache/#{chain}/#{id}.json"
|
188
|
+
next if File.exist?( path )
|
189
|
+
|
190
|
+
puts "==> downloading #{chain} inscription meta #{num} w/ id #{id}..."
|
191
|
+
|
192
|
+
data = Ordinals.client.inscription( id )
|
193
|
+
|
194
|
+
write_json( path, data )
|
195
|
+
|
196
|
+
sleep( 1.0 ) ## sleep (delay_in_s)
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def dump_stats
|
201
|
+
stats = InscriptionStats.new
|
202
|
+
|
203
|
+
each_ordinal do |rec, i|
|
204
|
+
id = rec['id']
|
205
|
+
|
206
|
+
chain = Ordinals.config.chain
|
207
|
+
path = "../ordinals.cache/#{chain}/#{id}.json"
|
208
|
+
|
209
|
+
data = read_json( path )
|
210
|
+
stats.update( data )
|
211
|
+
end
|
212
|
+
|
213
|
+
pp stats.data
|
214
|
+
puts
|
215
|
+
puts stats.format
|
216
|
+
end
|
217
|
+
|
218
|
+
|
219
|
+
def download_images
|
220
|
+
each_ordinal do |rec, i|
|
221
|
+
id = rec['id']
|
222
|
+
num = rec.has_key?('num') ? rec['num'].to_i(10) : i+1
|
223
|
+
|
224
|
+
next if File.exist?( "#{image_dir}/#{num}.png" )
|
225
|
+
|
226
|
+
puts "==> downloading image ##{num}..."
|
227
|
+
|
228
|
+
|
229
|
+
content = Ordinals.client.content( id )
|
230
|
+
|
231
|
+
puts " content_type: #{content.type}, content_length: #{content.length}"
|
232
|
+
|
233
|
+
format = if content.type =~ %r{image/jpeg}i
|
234
|
+
'jpg'
|
235
|
+
elsif content.type =~ %r{image/png}i
|
236
|
+
'png'
|
237
|
+
elsif content.type =~ %r{image/gif}i
|
238
|
+
'gif'
|
239
|
+
elsif content.type =~ %r{image/webp}i
|
240
|
+
'webp'
|
241
|
+
else
|
242
|
+
puts "!! ERROR:"
|
243
|
+
puts " unknown image format content type: >#{content.type}<"
|
244
|
+
exit 1
|
245
|
+
end
|
246
|
+
|
247
|
+
## save image - using b(inary) mode
|
248
|
+
write_blob( "#{image_dir}/#{num}.#{format}", content.blob )
|
249
|
+
|
250
|
+
sleep( 1.0 ) ## sleep (delay_in_s)
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
end # class Collection
|
255
|
+
end # module Ordinals
|
256
|
+
|
@@ -0,0 +1,142 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
class InscriptionStats
|
4
|
+
|
5
|
+
TITLE_RX = /^Inscription[ ]+(?<ord>[0-9]+)$/
|
6
|
+
BYTES_RX = /^(?<bytes>[0-9]+)[ ]+bytes$/
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@stats = {
|
10
|
+
count: 0,
|
11
|
+
ord: { '<1000' => 0,
|
12
|
+
'<10000' => 0,
|
13
|
+
'<100000' => 0,
|
14
|
+
'<1000000' => 0,
|
15
|
+
'min' => nil,
|
16
|
+
'max' => nil, },
|
17
|
+
type: Hash.new(0),
|
18
|
+
bytes: { '<100' => 0,
|
19
|
+
'<1000' => 0,
|
20
|
+
'<10000' => 0,
|
21
|
+
'<100000' => 0,
|
22
|
+
'<1000000' => 0,
|
23
|
+
'min' => nil,
|
24
|
+
'max' => nil, },
|
25
|
+
}
|
26
|
+
|
27
|
+
@days = Hash.new(0)
|
28
|
+
end
|
29
|
+
|
30
|
+
def size() @stats[:count]; end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
def update( data )
|
35
|
+
data = JSON.parse( data) if data.is_a?( String )
|
36
|
+
|
37
|
+
@stats[ :count ] += 1
|
38
|
+
|
39
|
+
|
40
|
+
ord = nil
|
41
|
+
if m=TITLE_RX.match( data['title'] )
|
42
|
+
ord = m[:ord].to_i(10)
|
43
|
+
else
|
44
|
+
puts "!! ERROR - cannot find ord in inscription title; sorry"
|
45
|
+
pp data
|
46
|
+
exit 1
|
47
|
+
end
|
48
|
+
|
49
|
+
if ord < 1000 then @stats[:ord]['<1000'] += 1
|
50
|
+
elsif ord < 10000 then @stats[:ord]['<10000'] += 1
|
51
|
+
elsif ord < 100000 then @stats[:ord]['<100000'] += 1
|
52
|
+
elsif ord < 1000000 then @stats[:ord]['<1000000'] += 1
|
53
|
+
else
|
54
|
+
puts "!! ERROR ord out-of-bounds"
|
55
|
+
exit 1
|
56
|
+
end
|
57
|
+
|
58
|
+
## update min/max
|
59
|
+
@stats[:ord]['min'] = ord if ord < (@stats[:ord]['min'] || 9999999)
|
60
|
+
@stats[:ord]['max'] = ord if ord > (@stats[:ord]['max'] || -1)
|
61
|
+
|
62
|
+
|
63
|
+
bytes = nil
|
64
|
+
if m=BYTES_RX.match( data['content length'] )
|
65
|
+
bytes = m[:bytes].to_i(10)
|
66
|
+
else
|
67
|
+
puts "!! ERROR - cannot find bytes in inscription content length; sorry"
|
68
|
+
pp data
|
69
|
+
exit 1
|
70
|
+
end
|
71
|
+
|
72
|
+
if bytes < 100 then @stats[:bytes]['<100'] += 1
|
73
|
+
elsif bytes < 1000 then @stats[:bytes]['<1000'] += 1
|
74
|
+
elsif bytes < 10000 then @stats[:bytes]['<10000'] += 1
|
75
|
+
elsif bytes < 100000 then @stats[:bytes]['<100000'] += 1
|
76
|
+
elsif bytes < 1000000 then @stats[:bytes]['<1000000'] += 1
|
77
|
+
else
|
78
|
+
puts "!! ERROR bytes (content-length) out-of-bounds"
|
79
|
+
exit 1
|
80
|
+
end
|
81
|
+
|
82
|
+
## update min/max
|
83
|
+
@stats[:bytes]['min'] = bytes if bytes < (@stats[:bytes]['min'] || 9999999)
|
84
|
+
@stats[:bytes]['max'] = bytes if bytes > (@stats[:bytes]['max'] || -1)
|
85
|
+
|
86
|
+
|
87
|
+
type = data['content type']
|
88
|
+
|
89
|
+
@stats[:type][ type ] += 1
|
90
|
+
|
91
|
+
## "timestamp"=>"2023-02-05 01:45:22 UTC",
|
92
|
+
ts = Time.strptime( data['timestamp'], '%Y-%m-%d %H:%M:%S' )
|
93
|
+
|
94
|
+
@days[ ts.strftime('%Y-%m-%d') ] +=1
|
95
|
+
end # method update
|
96
|
+
|
97
|
+
|
98
|
+
def data ## rename to model or such - why? why not?
|
99
|
+
## sort days
|
100
|
+
days = @days.sort {|l,r| l[0] <=> r[0] }
|
101
|
+
## add to stats
|
102
|
+
@stats[:days] = {}
|
103
|
+
days.each {|k,v| @stats[:days][k] = v }
|
104
|
+
@stats
|
105
|
+
end
|
106
|
+
|
107
|
+
=begin
|
108
|
+
{:count=>101,
|
109
|
+
:ord=>{"<1000"=>0, "<10000"=>0, "<100000"=>101, "<1000000"=>0, "min"=>14343, "max"=>42901},
|
110
|
+
:type=>{"image/png"=>101},
|
111
|
+
:bytes=>{"<100"=>0, "<1000"=>101, "<10000"=>0, "<100000"=>0, "<1000000"=>0, "min"=>274, "max"=>512},
|
112
|
+
:days=>{"2023-02-22"=>10, "2023-02-25"=>17, "2023-02-26"=>74}}
|
113
|
+
=end
|
114
|
+
|
115
|
+
def format ## rename to pretty_print or such - why? why not?
|
116
|
+
stat = data
|
117
|
+
|
118
|
+
buf = String.new('')
|
119
|
+
buf << "#{stat[:count]} inscription(s)\n"
|
120
|
+
|
121
|
+
buf << "- from ##{stat[:ord]['min']} to ##{stat[:ord]['max']} (min. to max.)\n"
|
122
|
+
buf << " - <1000 => #{stat[:ord]['<1000']}\n" if stat[:ord]["<1000"] > 0
|
123
|
+
buf << " - <10000 => #{stat[:ord]['<10000']}\n" if stat[:ord]["<10000"] > 0
|
124
|
+
buf << " - <100000 => #{stat[:ord]['<100000']}\n" if stat[:ord]["<100000"] > 0
|
125
|
+
buf << " - <1000000 => #{stat[:ord]['<1000000']}\n" if stat[:ord]["<1000000"] > 0
|
126
|
+
|
127
|
+
buf << "- format(s)\n"
|
128
|
+
stat[:type].each do |k,v|
|
129
|
+
buf << " - #{k} => #{v}\n"
|
130
|
+
end
|
131
|
+
|
132
|
+
buf << "- day(s)\n"
|
133
|
+
stat[:days].each do |k,v|
|
134
|
+
buf << " - #{k} => #{v}\n"
|
135
|
+
end
|
136
|
+
|
137
|
+
buf
|
138
|
+
end
|
139
|
+
|
140
|
+
end ## class InscriptionStats
|
141
|
+
|
142
|
+
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Ordinals
|
2
|
+
class Tool
|
3
|
+
|
4
|
+
def self.main( args=ARGV )
|
5
|
+
puts "==> welcome to ordinals/ordbase tool with args:"
|
6
|
+
pp args
|
7
|
+
|
8
|
+
options = {
|
9
|
+
}
|
10
|
+
|
11
|
+
parser = OptionParser.new do |opts|
|
12
|
+
opts.on( "--doge", "--dogecoin", "Use Dogecoin / DOGE") do
|
13
|
+
## switch to doge
|
14
|
+
Ordinals.config.chain = :doge
|
15
|
+
end
|
16
|
+
opts.on( "--ltc", "--litecoin", "Use Litecoin / LTC") do
|
17
|
+
## switch to ltc
|
18
|
+
Ordinals.config.chain = :ltc
|
19
|
+
end
|
20
|
+
opts.on( "--btc", "--bitcoin", "Use Bitcoin / BTC") do
|
21
|
+
## switch to btc (default)
|
22
|
+
Ordinals.config.chain = :btc
|
23
|
+
end
|
24
|
+
|
25
|
+
opts.on("-h", "--help", "Prints this help") do
|
26
|
+
puts opts
|
27
|
+
exit
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
parser.parse!( args )
|
32
|
+
puts "options:"
|
33
|
+
pp options
|
34
|
+
|
35
|
+
puts "args:"
|
36
|
+
pp args
|
37
|
+
|
38
|
+
if args.size < 1
|
39
|
+
puts "!! ERROR - no collection found - use <collection> <command>..."
|
40
|
+
puts ""
|
41
|
+
exit
|
42
|
+
end
|
43
|
+
|
44
|
+
slug = args[0]
|
45
|
+
command = args[1] || 'image'
|
46
|
+
|
47
|
+
if ['m', 'meta'].include?( command )
|
48
|
+
do_download_meta( slug )
|
49
|
+
elsif ['stat', 'stats'].include?( command )
|
50
|
+
do_dump_stats( slug )
|
51
|
+
elsif ['i','img', 'image'].include?( command )
|
52
|
+
do_download_images( slug )
|
53
|
+
elsif ['conv','convert'].include?( command )
|
54
|
+
do_convert_images( slug )
|
55
|
+
elsif ['fix'].include?( command )
|
56
|
+
do_fix_images( slug )
|
57
|
+
elsif ['px','pix', 'pixelate' ].include?( command )
|
58
|
+
do_pixelate( slug )
|
59
|
+
elsif ['comp','composite' ].include?( command )
|
60
|
+
do_make_composite( slug )
|
61
|
+
else
|
62
|
+
puts "!! ERROR - unknown command >#{command}<, sorry"
|
63
|
+
end
|
64
|
+
|
65
|
+
puts "bye"
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def self.do_dump_stats( slug )
|
70
|
+
puts "==> dump inscription stats for collection >#{slug}<..."
|
71
|
+
|
72
|
+
col = Collection.new( slug )
|
73
|
+
col.dump_stats
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.do_download_meta( slug )
|
77
|
+
puts "==> download meta for collection >#{slug}<..."
|
78
|
+
|
79
|
+
col = Collection.new( slug )
|
80
|
+
col.download_meta
|
81
|
+
end
|
82
|
+
|
83
|
+
def self.do_download_images( slug )
|
84
|
+
puts "==> download images for collection >#{slug}<..."
|
85
|
+
|
86
|
+
col = Collection.new( slug )
|
87
|
+
col.download_images
|
88
|
+
end
|
89
|
+
|
90
|
+
|
91
|
+
def self.do_convert_images( slug )
|
92
|
+
puts "==> convert images for collection >#{slug}<..."
|
93
|
+
|
94
|
+
col = Collection.new( slug )
|
95
|
+
col.convert_images
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.do_fix_images( slug )
|
99
|
+
puts "==> fix images for collection >#{slug}<..."
|
100
|
+
|
101
|
+
col = Collection.new( slug )
|
102
|
+
col.fix_images
|
103
|
+
end
|
104
|
+
|
105
|
+
|
106
|
+
def self.do_pixelate( slug )
|
107
|
+
puts "==> downsample / pixelate images for collection >#{slug}<..."
|
108
|
+
|
109
|
+
col = Collection.new( slug )
|
110
|
+
col.pixelate
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.do_make_composite( slug )
|
114
|
+
puts "==> make composite for collection >#{slug}<..."
|
115
|
+
|
116
|
+
col = Collection.new( slug )
|
117
|
+
col.make_composite
|
118
|
+
end
|
119
|
+
|
120
|
+
end # class Tool
|
121
|
+
end # module Ordinals
|
data/lib/ordinals.rb
CHANGED
@@ -1,3 +1,92 @@
|
|
1
1
|
|
2
|
-
|
2
|
+
require 'cocos'
|
3
|
+
require 'pixelart'
|
4
|
+
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
|
8
|
+
## add nokogiri for api (html parsing)
|
9
|
+
require 'nokogiri'
|
10
|
+
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
module Ordinals
|
16
|
+
class Configuration
|
17
|
+
|
18
|
+
#######################
|
19
|
+
## accessors
|
20
|
+
def chain=(value)
|
21
|
+
if value.is_a?( String ) || value.is_a?( Symbol )
|
22
|
+
case value.downcase.to_s
|
23
|
+
when 'btc', 'bitcoin', 'bitcon'
|
24
|
+
@chain = 'btc'
|
25
|
+
@client = Ordinals::Api.bitcoin
|
26
|
+
when 'ltc', 'litecoin', 'litecon'
|
27
|
+
@chain = 'ltc'
|
28
|
+
@client = Ordinals::Api.litecoin
|
29
|
+
when 'doge', 'dogecoin', 'dogecon'
|
30
|
+
@chain = 'doge'
|
31
|
+
@client = Ordinals::Api.dogecoin
|
32
|
+
else
|
33
|
+
raise ArgumentError, "unknown chain - expected btc | ltc | doge; got #{value}"
|
34
|
+
end
|
35
|
+
else
|
36
|
+
raise ArgumentError, "only string or symbol supported for now; sorry - got: #{value.inspect} : #{value.class.name}"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def chain
|
41
|
+
## note - default to btc/bitcon if not set
|
42
|
+
self.chain = 'btc' unless defined?( @chain )
|
43
|
+
@chain
|
44
|
+
end
|
45
|
+
|
46
|
+
## note: read-only for now - why? why not?
|
47
|
+
def client
|
48
|
+
## note - default to btc/bitcon if not set
|
49
|
+
self.chain = 'btc' unless defined?( @client )
|
50
|
+
@client
|
51
|
+
end
|
52
|
+
end # class Configuration
|
53
|
+
|
54
|
+
|
55
|
+
## lets you use
|
56
|
+
## Ordinals.configure do |config|
|
57
|
+
## config.chain = :btc
|
58
|
+
## end
|
59
|
+
def self.configure() yield( config ); end
|
60
|
+
def self.config() @config ||= Configuration.new; end
|
61
|
+
|
62
|
+
## add some convenience shortcut helpers (no config. required) - why? why not?
|
63
|
+
def self.client() config.client; end
|
64
|
+
def self.chain() config.chain; end
|
65
|
+
|
66
|
+
def self.btc?() config.chain == 'btc'; end
|
67
|
+
def self.ltc?() config.chain == 'ltc'; end
|
68
|
+
def self.doge?() config.chain == 'doge'; end
|
69
|
+
class << self
|
70
|
+
alias_method :bitcoin?, :btc?
|
71
|
+
alias_method :bitcon?, :btc?
|
72
|
+
|
73
|
+
alias_method :litecoin?, :ltc?
|
74
|
+
alias_method :litecon?, :ltc?
|
75
|
+
|
76
|
+
alias_method :dogecoin?, :doge?
|
77
|
+
alias_method :dogecon?, :doge?
|
78
|
+
end
|
79
|
+
end # module Ordinals
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
|
84
|
+
## our own code
|
85
|
+
require_relative 'ordinals/api'
|
86
|
+
require_relative 'ordinals/collection'
|
87
|
+
require_relative 'ordinals/stats'
|
88
|
+
|
89
|
+
require_relative 'ordinals/tool'
|
90
|
+
|
91
|
+
|
3
92
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ordinals
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gerald Bauer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-03-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: cocos
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: nokogiri
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rdoc
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -72,10 +86,11 @@ dependencies:
|
|
72
86
|
- - "~>"
|
73
87
|
- !ruby/object:Gem::Version
|
74
88
|
version: '3.23'
|
75
|
-
description: ordinals gem - (off-chain) ordinals (pixel art) machinery
|
76
|
-
Bitcoin & co.
|
89
|
+
description: ordinals gem - "right-clicker" (off-chain) ordinals (pixel art) machinery
|
90
|
+
& helpers for Bitcoin, Litecoin, Dogecoin & co.
|
77
91
|
email: wwwmake@googlegroups.com
|
78
|
-
executables:
|
92
|
+
executables:
|
93
|
+
- ordbase
|
79
94
|
extensions: []
|
80
95
|
extra_rdoc_files:
|
81
96
|
- CHANGELOG.md
|
@@ -86,7 +101,12 @@ files:
|
|
86
101
|
- Manifest.txt
|
87
102
|
- README.md
|
88
103
|
- Rakefile
|
104
|
+
- bin/ordbase
|
89
105
|
- lib/ordinals.rb
|
106
|
+
- lib/ordinals/api.rb
|
107
|
+
- lib/ordinals/collection.rb
|
108
|
+
- lib/ordinals/stats.rb
|
109
|
+
- lib/ordinals/tool.rb
|
90
110
|
homepage: https://github.com/pixelartexchange/ordinals.sandbox
|
91
111
|
licenses:
|
92
112
|
- Public Domain
|
@@ -111,6 +131,6 @@ requirements: []
|
|
111
131
|
rubygems_version: 3.3.7
|
112
132
|
signing_key:
|
113
133
|
specification_version: 4
|
114
|
-
summary: ordinals gem - (off-chain) ordinals (pixel art) machinery
|
115
|
-
& co.
|
134
|
+
summary: ordinals gem - "right-clicker" (off-chain) ordinals (pixel art) machinery
|
135
|
+
& helpers for Bitcoin, Litecoin, Dogecoin & co.
|
116
136
|
test_files: []
|