pinterest-url-normalizer 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.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +62 -0
- data/exe/pinterest-url-normalizer +6 -0
- data/lib/pinterest_url_normalizer/cli.rb +58 -0
- data/lib/pinterest_url_normalizer/version.rb +5 -0
- data/lib/pinterest_url_normalizer.rb +228 -0
- metadata +80 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 5293ed706c039755df8a8f487ffb434813eaf3a7c26bdd35b18f24b22decb4d9
|
|
4
|
+
data.tar.gz: e058bc0992ee1da9af2470a04fec5b148bf2dbb756f0666fad6db4ff76d64f1d
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 03b33feb90157133ee8dc29fa422d1800694f4a24ed2662642f9034150214f8a83f8f97a7b75a4de00b965a2516ff9cc49572528ad7a2ed9310c64b14539d8bd
|
|
7
|
+
data.tar.gz: a593d71cded361ce53a460ea4b7226c757bf4e7899d35b182404b6380e2d0c0274855aed1947c686206397c7614b6ce1cc114f2019b7b7eeda91097a662ec765
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SavePinner
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# pinterest-url-normalizer
|
|
2
|
+
|
|
3
|
+
Parse, classify, and normalize Pinterest URLs in Ruby without making network requests.
|
|
4
|
+
|
|
5
|
+
[SavePinner](https://savepinner.com/pinterest-downloader/) · [RubyGems](https://rubygems.org/gems/pinterest-url-normalizer) · [RubyDoc.info](https://www.rubydoc.info/gems/pinterest-url-normalizer) · [npm](https://www.npmjs.com/package/pinterest-url-normalizer) · [PyPI](https://pypi.org/project/pinterest-url-normalizer/) · [crates.io](https://crates.io/crates/pinterest-url-normalizer) · [Packagist](https://packagist.org/packages/savepinner/pinterest-url-normalizer) · [Go](https://pkg.go.dev/github.com/jiankn/pinterest-url-normalizer-go)
|
|
6
|
+
|
|
7
|
+
The gem recognizes Pin, `pin.it`, profile, board, and Ideas URLs across Pinterest country domains. It uses an exact host allow list, rejects HTTP URLs and lookalike domains, and removes query parameters and fragments from normalized output.
|
|
8
|
+
|
|
9
|
+
## Install
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
gem install pinterest-url-normalizer
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Library
|
|
16
|
+
|
|
17
|
+
```ruby
|
|
18
|
+
require "pinterest_url_normalizer"
|
|
19
|
+
|
|
20
|
+
parsed = PinterestURLNormalizer.parse(
|
|
21
|
+
"https://de.pinterest.com/pin/987654321/?utm_source=share"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
parsed.pin_id
|
|
25
|
+
# => "987654321"
|
|
26
|
+
|
|
27
|
+
parsed.normalized_url
|
|
28
|
+
# => "https://www.pinterest.com/pin/987654321/"
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## CLI
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
pinterest-url-normalizer \
|
|
35
|
+
"https://de.pinterest.com/pin/987654321/?utm_source=share" \
|
|
36
|
+
"https://pin.it/AbC123"
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Read one URL per line and emit JSON Lines:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
printf '%s\n' 'https://www.pinterest.com/pin/123/' | \
|
|
43
|
+
pinterest-url-normalizer --json
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
The process exits with status `1` when any input is invalid and status `2` for usage errors.
|
|
47
|
+
|
|
48
|
+
## Supported URLs
|
|
49
|
+
|
|
50
|
+
| Kind | Example |
|
|
51
|
+
| --- | --- |
|
|
52
|
+
| Pin | `https://www.pinterest.com/pin/123456789/` |
|
|
53
|
+
| Short | `https://pin.it/AbC123` |
|
|
54
|
+
| Profile | `https://www.pinterest.com/savepinner/` |
|
|
55
|
+
| Board | `https://www.pinterest.com/savepinner/media-tools/` |
|
|
56
|
+
| Ideas | `https://www.pinterest.com/ideas/space-wallpaper/926295399832/` |
|
|
57
|
+
|
|
58
|
+
`pin.it` links are classified and normalized but are not followed. Resolving them requires a network request and belongs in the consuming application.
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "optparse"
|
|
5
|
+
require_relative "../pinterest_url_normalizer"
|
|
6
|
+
|
|
7
|
+
module PinterestURLNormalizer
|
|
8
|
+
# Command-line interface for the gem executable.
|
|
9
|
+
module CLI
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def run(arguments, input: $stdin, output: $stdout, error: $stderr)
|
|
13
|
+
json_output = false
|
|
14
|
+
parser = OptionParser.new do |options|
|
|
15
|
+
options.banner = "Usage: pinterest-url-normalizer [--json] [URL ...]"
|
|
16
|
+
options.on("--json", "Emit JSON Lines") { json_output = true }
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
urls = arguments.dup
|
|
20
|
+
begin
|
|
21
|
+
parser.parse!(urls)
|
|
22
|
+
rescue OptionParser::ParseError => exception
|
|
23
|
+
error.puts exception.message
|
|
24
|
+
error.puts parser
|
|
25
|
+
return 2
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
urls = input.each_line.map(&:strip).reject(&:empty?) if urls.empty?
|
|
29
|
+
if urls.empty?
|
|
30
|
+
error.puts parser
|
|
31
|
+
return 2
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
exit_code = 0
|
|
35
|
+
urls.each do |url|
|
|
36
|
+
begin
|
|
37
|
+
parsed = PinterestURLNormalizer.parse(url)
|
|
38
|
+
if json_output
|
|
39
|
+
output.puts JSON.generate(parsed.to_h.compact)
|
|
40
|
+
else
|
|
41
|
+
output.puts parsed.normalized_url
|
|
42
|
+
end
|
|
43
|
+
rescue Error => exception
|
|
44
|
+
exit_code = 1
|
|
45
|
+
if json_output
|
|
46
|
+
output.puts JSON.generate(
|
|
47
|
+
input: url,
|
|
48
|
+
error: { code: exception.code, message: exception.message }
|
|
49
|
+
)
|
|
50
|
+
else
|
|
51
|
+
error.puts "#{exception.code}: #{exception.message}: #{url}"
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
exit_code
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "uri"
|
|
4
|
+
require_relative "pinterest_url_normalizer/version"
|
|
5
|
+
|
|
6
|
+
# Parses, classifies, and normalizes supported Pinterest URLs without making
|
|
7
|
+
# network requests.
|
|
8
|
+
#
|
|
9
|
+
# Full Pinterest URLs are normalized to www.pinterest.com. Short pin.it links
|
|
10
|
+
# retain their host because resolving them requires a network request.
|
|
11
|
+
#
|
|
12
|
+
# SavePinner: https://savepinner.com/pinterest-downloader/
|
|
13
|
+
module PinterestURLNormalizer
|
|
14
|
+
CANONICAL_HOST = "www.pinterest.com"
|
|
15
|
+
SHORT_HOST = "pin.it"
|
|
16
|
+
MAX_URL_LENGTH = 2048
|
|
17
|
+
|
|
18
|
+
COUNTRY_HOSTS = %w[
|
|
19
|
+
pinterest.at pinterest.be pinterest.ca pinterest.ch pinterest.cl
|
|
20
|
+
pinterest.co pinterest.co.kr pinterest.co.nz pinterest.co.uk
|
|
21
|
+
pinterest.com.au pinterest.com.br pinterest.com.mx pinterest.com.pe
|
|
22
|
+
pinterest.com.tr pinterest.cz pinterest.de pinterest.dk pinterest.es
|
|
23
|
+
pinterest.fi pinterest.fr pinterest.gr pinterest.hu pinterest.id
|
|
24
|
+
pinterest.ie pinterest.it pinterest.jp pinterest.nl pinterest.no
|
|
25
|
+
pinterest.ph pinterest.pl pinterest.pt pinterest.ro pinterest.se
|
|
26
|
+
pinterest.sk
|
|
27
|
+
].freeze
|
|
28
|
+
|
|
29
|
+
REGIONAL_SUBDOMAINS = %w[
|
|
30
|
+
at au be br ca ch cl co cz de dk es fi fr gr hu id ie it jp kr mx nl
|
|
31
|
+
no nz pe ph pl pt ro se sk tr uk
|
|
32
|
+
].freeze
|
|
33
|
+
|
|
34
|
+
RESERVED_FIRST_SEGMENTS = %w[
|
|
35
|
+
business categories explore help ideas login logout oauth pin pin-builder
|
|
36
|
+
resource search settings signup today topics
|
|
37
|
+
].freeze
|
|
38
|
+
|
|
39
|
+
# A supported Pinterest URL and its canonical fields.
|
|
40
|
+
ParsedURL = Struct.new(
|
|
41
|
+
:kind,
|
|
42
|
+
:original_url,
|
|
43
|
+
:normalized_url,
|
|
44
|
+
:host,
|
|
45
|
+
:pin_id,
|
|
46
|
+
:shortcode,
|
|
47
|
+
:username,
|
|
48
|
+
:board_slug,
|
|
49
|
+
:idea_slug,
|
|
50
|
+
:idea_id,
|
|
51
|
+
keyword_init: true
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
# An invalid URL or unsupported Pinterest path.
|
|
55
|
+
class Error < StandardError
|
|
56
|
+
attr_reader :code
|
|
57
|
+
|
|
58
|
+
def initialize(code, message)
|
|
59
|
+
@code = code
|
|
60
|
+
super(message)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
module_function
|
|
65
|
+
|
|
66
|
+
# Parses a supported Pinterest URL and returns its canonical fields.
|
|
67
|
+
def parse(input)
|
|
68
|
+
original = input.to_s.strip
|
|
69
|
+
raise_invalid("URL is empty or too long") if original.empty? || original.bytesize > MAX_URL_LENGTH
|
|
70
|
+
|
|
71
|
+
uri = URI.parse(original)
|
|
72
|
+
raise_invalid("URL could not be parsed") unless uri.host
|
|
73
|
+
raise_invalid("only HTTPS URLs are supported") unless uri.scheme == "https"
|
|
74
|
+
raise_invalid("credentials are not supported") if uri.userinfo
|
|
75
|
+
raise_invalid("non-standard ports are not supported") unless uri.port == 443
|
|
76
|
+
raise_invalid("encoded paths are not supported") if uri.path.include?("%")
|
|
77
|
+
|
|
78
|
+
host = uri.host.downcase
|
|
79
|
+
segments = uri.path.split("/").reject(&:empty?)
|
|
80
|
+
return parse_short(original, segments) if host == SHORT_HOST
|
|
81
|
+
|
|
82
|
+
raise_invalid("host is not an allowed Pinterest domain") unless pinterest_host?(host)
|
|
83
|
+
|
|
84
|
+
parse_pin(original, segments) ||
|
|
85
|
+
parse_ideas(original, segments) ||
|
|
86
|
+
parse_profile_or_board(original, segments)
|
|
87
|
+
rescue URI::InvalidURIError, URI::InvalidComponentError, ArgumentError
|
|
88
|
+
raise_invalid("URL could not be parsed")
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Returns the canonical URL for a supported Pinterest URL.
|
|
92
|
+
def normalize(input)
|
|
93
|
+
parse(input).normalized_url
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Reports whether input is a supported Pinterest URL.
|
|
97
|
+
def pinterest_url?(input)
|
|
98
|
+
parse(input)
|
|
99
|
+
true
|
|
100
|
+
rescue Error
|
|
101
|
+
false
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Reports whether host is an exact supported Pinterest host.
|
|
105
|
+
def pinterest_host?(host)
|
|
106
|
+
host = host.to_s.downcase
|
|
107
|
+
return true if %w[pinterest.com www.pinterest.com m.pinterest.com].include?(host)
|
|
108
|
+
return true if COUNTRY_HOSTS.include?(host)
|
|
109
|
+
return true if host.start_with?("www.") && COUNTRY_HOSTS.include?(host.delete_prefix("www."))
|
|
110
|
+
|
|
111
|
+
region = host.delete_suffix(".pinterest.com")
|
|
112
|
+
region != host && REGIONAL_SUBDOMAINS.include?(region)
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def parse_short(original, segments)
|
|
116
|
+
unless segments.length == 1 && segments.first.length >= 2 && ascii_alphanumeric?(segments.first)
|
|
117
|
+
raise_unsupported("unsupported pin.it path")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
shortcode = segments.first
|
|
121
|
+
ParsedURL.new(
|
|
122
|
+
kind: "short",
|
|
123
|
+
original_url: original,
|
|
124
|
+
normalized_url: "https://#{SHORT_HOST}/#{shortcode}/",
|
|
125
|
+
host: SHORT_HOST,
|
|
126
|
+
shortcode: shortcode
|
|
127
|
+
)
|
|
128
|
+
end
|
|
129
|
+
private_class_method :parse_short
|
|
130
|
+
|
|
131
|
+
def parse_pin(original, segments)
|
|
132
|
+
return unless [2, 3].include?(segments.length) && segments.first == "pin"
|
|
133
|
+
|
|
134
|
+
pin_segment = segments[1]
|
|
135
|
+
pin_id = if numeric_id?(pin_segment)
|
|
136
|
+
pin_segment
|
|
137
|
+
else
|
|
138
|
+
candidate = pin_segment.rpartition("--").last
|
|
139
|
+
candidate if slug?(pin_segment) && numeric_id?(candidate)
|
|
140
|
+
end
|
|
141
|
+
return unless pin_id
|
|
142
|
+
return if segments.length == 3 && !slug?(segments[2])
|
|
143
|
+
|
|
144
|
+
ParsedURL.new(
|
|
145
|
+
kind: "pin",
|
|
146
|
+
original_url: original,
|
|
147
|
+
normalized_url: "https://#{CANONICAL_HOST}/pin/#{pin_id}/",
|
|
148
|
+
host: CANONICAL_HOST,
|
|
149
|
+
pin_id: pin_id
|
|
150
|
+
)
|
|
151
|
+
end
|
|
152
|
+
private_class_method :parse_pin
|
|
153
|
+
|
|
154
|
+
def parse_ideas(original, segments)
|
|
155
|
+
return unless segments.length == 3 && segments.first == "ideas"
|
|
156
|
+
return unless slug?(segments[1]) && numeric_id?(segments[2])
|
|
157
|
+
|
|
158
|
+
ParsedURL.new(
|
|
159
|
+
kind: "ideas",
|
|
160
|
+
original_url: original,
|
|
161
|
+
normalized_url: "https://#{CANONICAL_HOST}/ideas/#{segments[1]}/#{segments[2]}/",
|
|
162
|
+
host: CANONICAL_HOST,
|
|
163
|
+
idea_slug: segments[1],
|
|
164
|
+
idea_id: segments[2]
|
|
165
|
+
)
|
|
166
|
+
end
|
|
167
|
+
private_class_method :parse_ideas
|
|
168
|
+
|
|
169
|
+
def parse_profile_or_board(original, segments)
|
|
170
|
+
username = segments.first
|
|
171
|
+
raise_unsupported("unsupported Pinterest path") unless username
|
|
172
|
+
raise_unsupported("unsupported Pinterest path") if RESERVED_FIRST_SEGMENTS.include?(username.downcase)
|
|
173
|
+
|
|
174
|
+
if segments.length == 1 && username?(username)
|
|
175
|
+
return ParsedURL.new(
|
|
176
|
+
kind: "profile",
|
|
177
|
+
original_url: original,
|
|
178
|
+
normalized_url: "https://#{CANONICAL_HOST}/#{username}/",
|
|
179
|
+
host: CANONICAL_HOST,
|
|
180
|
+
username: username
|
|
181
|
+
)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
if segments.length == 2 && username?(username) && slug?(segments[1])
|
|
185
|
+
return ParsedURL.new(
|
|
186
|
+
kind: "board",
|
|
187
|
+
original_url: original,
|
|
188
|
+
normalized_url: "https://#{CANONICAL_HOST}/#{username}/#{segments[1]}/",
|
|
189
|
+
host: CANONICAL_HOST,
|
|
190
|
+
username: username,
|
|
191
|
+
board_slug: segments[1]
|
|
192
|
+
)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
raise_unsupported("unsupported Pinterest path")
|
|
196
|
+
end
|
|
197
|
+
private_class_method :parse_profile_or_board
|
|
198
|
+
|
|
199
|
+
def numeric_id?(value)
|
|
200
|
+
value.match?(/\A\d{1,20}\z/)
|
|
201
|
+
end
|
|
202
|
+
private_class_method :numeric_id?
|
|
203
|
+
|
|
204
|
+
def ascii_alphanumeric?(value)
|
|
205
|
+
value.match?(/\A[A-Za-z0-9]+\z/)
|
|
206
|
+
end
|
|
207
|
+
private_class_method :ascii_alphanumeric?
|
|
208
|
+
|
|
209
|
+
def slug?(value)
|
|
210
|
+
value.match?(/\A[A-Za-z0-9][A-Za-z0-9_-]*\z/)
|
|
211
|
+
end
|
|
212
|
+
private_class_method :slug?
|
|
213
|
+
|
|
214
|
+
def username?(value)
|
|
215
|
+
value.match?(/\A[A-Za-z0-9_][A-Za-z0-9_.-]*\z/)
|
|
216
|
+
end
|
|
217
|
+
private_class_method :username?
|
|
218
|
+
|
|
219
|
+
def raise_invalid(message)
|
|
220
|
+
raise Error.new("INVALID_URL", message)
|
|
221
|
+
end
|
|
222
|
+
private_class_method :raise_invalid
|
|
223
|
+
|
|
224
|
+
def raise_unsupported(message)
|
|
225
|
+
raise Error.new("UNSUPPORTED_URL", message)
|
|
226
|
+
end
|
|
227
|
+
private_class_method :raise_unsupported
|
|
228
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: pinterest-url-normalizer
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- SavePinner
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: minitest
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '5.0'
|
|
19
|
+
type: :development
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '5.0'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rake
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '13.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '13.0'
|
|
40
|
+
description: A network-free Pinterest URL parser and normalizer with an exact host
|
|
41
|
+
allow list.
|
|
42
|
+
email:
|
|
43
|
+
- jiankn@users.noreply.github.com
|
|
44
|
+
executables:
|
|
45
|
+
- pinterest-url-normalizer
|
|
46
|
+
extensions: []
|
|
47
|
+
extra_rdoc_files: []
|
|
48
|
+
files:
|
|
49
|
+
- LICENSE
|
|
50
|
+
- README.md
|
|
51
|
+
- exe/pinterest-url-normalizer
|
|
52
|
+
- lib/pinterest_url_normalizer.rb
|
|
53
|
+
- lib/pinterest_url_normalizer/cli.rb
|
|
54
|
+
- lib/pinterest_url_normalizer/version.rb
|
|
55
|
+
homepage: https://savepinner.com/pinterest-downloader/
|
|
56
|
+
licenses:
|
|
57
|
+
- MIT
|
|
58
|
+
metadata:
|
|
59
|
+
allowed_push_host: https://rubygems.org
|
|
60
|
+
source_code_uri: https://github.com/jiankn/pinterest-url-normalizer-ruby
|
|
61
|
+
documentation_uri: https://www.rubydoc.info/gems/pinterest-url-normalizer
|
|
62
|
+
homepage_uri: https://savepinner.com/pinterest-downloader/
|
|
63
|
+
rdoc_options: []
|
|
64
|
+
require_paths:
|
|
65
|
+
- lib
|
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
67
|
+
requirements:
|
|
68
|
+
- - ">="
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '3.1'
|
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
requirements: []
|
|
77
|
+
rubygems_version: 4.0.16
|
|
78
|
+
specification_version: 4
|
|
79
|
+
summary: Parse, classify, and normalize Pinterest URLs
|
|
80
|
+
test_files: []
|