favicon_get 0.1.0 → 0.1.1
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/lib/favicon_get/version.rb +1 -1
- data/lib/favicon_get.rb +1 -1
- metadata +1 -3
- data/lib/favicon_gem/version.rb +0 -5
- data/lib/favicon_gem.rb +0 -213
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 78bdf48e651f3062901d7c6750ebb960caf7dc2086aa950be3d5e456a8f438aa
|
4
|
+
data.tar.gz: 981d30c87c1fa9e6ae039ceab2d625551f9512c445f45c417284477cb0d6db79
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a0ad54d1e6657dcb345dc1de8da3036b0ce5a5dcdb0b9fa47a08fa871912684d761a6a88d0cb7bcca4e5b74fc3eb7685e1f08eda8df9d1bbafe9508162058dc2
|
7
|
+
data.tar.gz: '09bd2a117bffab266e4a166c9ef19a5e07c71a9c284d1314ff82e8353785c073ac7b52f350dfc2546204c57eec11c1789184aaa6610146380fe676418f28f9a8'
|
data/lib/favicon_get/version.rb
CHANGED
data/lib/favicon_get.rb
CHANGED
@@ -165,7 +165,7 @@ module FaviconGet
|
|
165
165
|
ext = File.extname(URI.parse(url_parsed).path)[1..]&.downcase || ""
|
166
166
|
|
167
167
|
icons.add(Icon.new(url_parsed, width, height, ext))
|
168
|
-
rescue URI::InvalidURIError
|
168
|
+
rescue URI::InvalidURIError
|
169
169
|
# Skip invalid URIs
|
170
170
|
next
|
171
171
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: favicon_get
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ivan Nemytchenko
|
@@ -117,8 +117,6 @@ extensions: []
|
|
117
117
|
extra_rdoc_files: []
|
118
118
|
files:
|
119
119
|
- README.md
|
120
|
-
- lib/favicon_gem.rb
|
121
|
-
- lib/favicon_gem/version.rb
|
122
120
|
- lib/favicon_get.rb
|
123
121
|
- lib/favicon_get/version.rb
|
124
122
|
homepage: https://github.com/inem/favicon
|
data/lib/favicon_gem/version.rb
DELETED
data/lib/favicon_gem.rb
DELETED
@@ -1,213 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "favicon_get/version"
|
4
|
-
require "faraday"
|
5
|
-
require "faraday/follow_redirects"
|
6
|
-
require "nokogiri"
|
7
|
-
require "uri"
|
8
|
-
require "set"
|
9
|
-
|
10
|
-
module FaviconGet
|
11
|
-
class Error < StandardError; end
|
12
|
-
|
13
|
-
# Website icon representation
|
14
|
-
Icon = Struct.new(:url, :width, :height, :format)
|
15
|
-
|
16
|
-
# Gem metadata
|
17
|
-
TITLE = "favicon_get"
|
18
|
-
AUTHOR = "Ported from Python favicon by Scott Werner"
|
19
|
-
LICENSE = "MIT"
|
20
|
-
|
21
|
-
HEADERS = {
|
22
|
-
"User-Agent" => "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_2) " \
|
23
|
-
"AppleWebKit/537.36 (KHTML, like Gecko) " \
|
24
|
-
"Chrome/33.0.1750.152 Safari/537.36"
|
25
|
-
}
|
26
|
-
|
27
|
-
LINK_RELS = [
|
28
|
-
"icon",
|
29
|
-
"shortcut icon",
|
30
|
-
"apple-touch-icon",
|
31
|
-
"apple-touch-icon-precomposed"
|
32
|
-
]
|
33
|
-
|
34
|
-
META_NAMES = ["msapplication-TileImage"] # Removed og:image from metatags as it's usually not a favicon
|
35
|
-
|
36
|
-
# Format priorities (higher = better)
|
37
|
-
FORMAT_PRIORITY = {
|
38
|
-
"ico" => 10,
|
39
|
-
"png" => 9,
|
40
|
-
"jpg" => 8,
|
41
|
-
"jpeg" => 7,
|
42
|
-
"svg" => 6,
|
43
|
-
"gif" => 5,
|
44
|
-
"" => 0 # Unknown format has the lowest priority
|
45
|
-
}
|
46
|
-
|
47
|
-
SIZE_RE = /(?<width>\d{2,4})x(?<height>\d{2,4})/i
|
48
|
-
|
49
|
-
class << self
|
50
|
-
# Get all icons for a URL
|
51
|
-
#
|
52
|
-
# @param url [String] Page URL
|
53
|
-
# @param headers [Hash] Request headers
|
54
|
-
# @return [Array<Icon>] List of found icons, sorted by size
|
55
|
-
def get(url, headers: HEADERS, **request_options)
|
56
|
-
request_options[:headers] ||= headers
|
57
|
-
|
58
|
-
conn = Faraday.new(url: url) do |faraday|
|
59
|
-
faraday.request :url_encoded
|
60
|
-
faraday.headers = request_options[:headers]
|
61
|
-
faraday.options.timeout = request_options[:timeout] if request_options[:timeout]
|
62
|
-
faraday.use Faraday::FollowRedirects::Middleware
|
63
|
-
faraday.adapter Faraday.default_adapter
|
64
|
-
end
|
65
|
-
|
66
|
-
response = conn.get
|
67
|
-
raise Error, "Failed to fetch URL: #{response.status}" unless response.success?
|
68
|
-
|
69
|
-
icons = Set.new
|
70
|
-
|
71
|
-
default_icon = default(response.env.url.to_s, **request_options)
|
72
|
-
icons.add(default_icon) if default_icon
|
73
|
-
|
74
|
-
link_icons = tags(response.env.url.to_s, response.body)
|
75
|
-
icons.merge(link_icons) if link_icons.any?
|
76
|
-
|
77
|
-
# Improve sorting:
|
78
|
-
# 1. By size (larger first)
|
79
|
-
# 2. If sizes are equal, sort by format (ico/png have higher priority)
|
80
|
-
# 3. All icons with zero sizes go to the end
|
81
|
-
icons.to_a.sort_by do |icon|
|
82
|
-
format_priority = FORMAT_PRIORITY[icon.format] || 0
|
83
|
-
size = icon.width + icon.height
|
84
|
-
|
85
|
-
if size > 0
|
86
|
-
[1, size, format_priority] # Icons with non-zero size first
|
87
|
-
else
|
88
|
-
[0, format_priority] # Zero sizes - second priority
|
89
|
-
end
|
90
|
-
end.reverse
|
91
|
-
end
|
92
|
-
|
93
|
-
private
|
94
|
-
|
95
|
-
# Get icon using default filename favicon.ico
|
96
|
-
#
|
97
|
-
# @param url [String] Site URL
|
98
|
-
# @param request_options [Hash] Request parameters
|
99
|
-
# @return [Icon, nil] Icon or nil
|
100
|
-
def default(url, **request_options)
|
101
|
-
uri = URI.parse(url)
|
102
|
-
# Preserve port if explicitly specified
|
103
|
-
port_part = uri.port == uri.default_port ? "" : ":#{uri.port}"
|
104
|
-
favicon_url = "#{uri.scheme}://#{uri.host}#{port_part}/favicon.ico"
|
105
|
-
|
106
|
-
conn = Faraday.new(url: favicon_url) do |faraday|
|
107
|
-
faraday.headers = request_options[:headers] if request_options[:headers]
|
108
|
-
faraday.options.timeout = request_options[:timeout] if request_options[:timeout]
|
109
|
-
faraday.use Faraday::FollowRedirects::Middleware
|
110
|
-
faraday.adapter Faraday.default_adapter
|
111
|
-
end
|
112
|
-
|
113
|
-
response = conn.head
|
114
|
-
return Icon.new(response.env.url.to_s, 0, 0, "ico") if response.success?
|
115
|
-
nil
|
116
|
-
rescue Faraday::Error
|
117
|
-
nil
|
118
|
-
end
|
119
|
-
|
120
|
-
# Get icons from link and meta tags
|
121
|
-
#
|
122
|
-
# @param url [String] Site URL
|
123
|
-
# @param html [String] Page HTML code
|
124
|
-
# @return [Set<Icon>] Found icons
|
125
|
-
def tags(url, html)
|
126
|
-
doc = Nokogiri::HTML(html)
|
127
|
-
icons = Set.new
|
128
|
-
|
129
|
-
# Search in <link> tags
|
130
|
-
link_tags = Set.new
|
131
|
-
LINK_RELS.each do |rel|
|
132
|
-
doc.css("link[rel='#{rel}'][href]").each do |link_tag|
|
133
|
-
link_tags.add(link_tag)
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
# Search in <meta> tags
|
138
|
-
meta_tags = Set.new
|
139
|
-
META_NAMES.each do |name|
|
140
|
-
doc.css("meta[name='#{name}'][content], meta[property='#{name}'][content]").each do |meta_tag|
|
141
|
-
meta_tags.add(meta_tag)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
(link_tags | meta_tags).each do |tag|
|
146
|
-
href = tag["href"] || tag["content"] || ""
|
147
|
-
href = href.strip.gsub(/\s+/, "") # Remove all whitespace, including tabs
|
148
|
-
|
149
|
-
next if href.empty? || href.start_with?("data:image/")
|
150
|
-
|
151
|
-
begin
|
152
|
-
# Fix URLs like '//cdn.network.com/favicon.png'
|
153
|
-
if href.start_with?("//")
|
154
|
-
uri = URI.parse(url)
|
155
|
-
href = "#{uri.scheme}:#{href}"
|
156
|
-
end
|
157
|
-
|
158
|
-
url_parsed = if is_absolute(href)
|
159
|
-
href
|
160
|
-
else
|
161
|
-
URI.join(url, href).to_s
|
162
|
-
end
|
163
|
-
|
164
|
-
width, height = dimensions(tag)
|
165
|
-
ext = File.extname(URI.parse(url_parsed).path)[1..]&.downcase || ""
|
166
|
-
|
167
|
-
icons.add(Icon.new(url_parsed, width, height, ext))
|
168
|
-
rescue URI::InvalidURIError => e
|
169
|
-
# Skip invalid URIs
|
170
|
-
next
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
icons
|
175
|
-
end
|
176
|
-
|
177
|
-
# Check if URL is absolute
|
178
|
-
#
|
179
|
-
# @param url [String] URL
|
180
|
-
# @return [Boolean] true if absolute
|
181
|
-
def is_absolute(url)
|
182
|
-
uri = URI.parse(url)
|
183
|
-
!uri.host.nil?
|
184
|
-
rescue URI::InvalidURIError
|
185
|
-
false
|
186
|
-
end
|
187
|
-
|
188
|
-
# Get icon dimensions from size attribute or filename
|
189
|
-
#
|
190
|
-
# @param tag [Nokogiri::XML::Element] Link or meta tag
|
191
|
-
# @return [Array<Integer>] Width and height, or [0,0]
|
192
|
-
def dimensions(tag)
|
193
|
-
sizes = tag["sizes"]
|
194
|
-
if sizes && sizes != "any"
|
195
|
-
size = sizes.split(" ").max_by { |s| s.scan(/\d+/).map(&:to_i).sum }
|
196
|
-
width, height = size.split(/[x×]/)
|
197
|
-
else
|
198
|
-
filename = tag["href"] || tag["content"] || ""
|
199
|
-
size = SIZE_RE.match(filename)
|
200
|
-
if size
|
201
|
-
width, height = size[:width], size[:height]
|
202
|
-
else
|
203
|
-
width, height = "0", "0"
|
204
|
-
end
|
205
|
-
end
|
206
|
-
|
207
|
-
# Clean non-digit characters
|
208
|
-
width = width.to_s.scan(/\d+/).join
|
209
|
-
height = height.to_s.scan(/\d+/).join
|
210
|
-
[width.to_i, height.to_i]
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|