device_detector 1.0.0 → 1.1.3

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.
Files changed (89) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +58 -4
  3. data/README.md +57 -21
  4. data/lib/device_detector/bot.rb +2 -2
  5. data/lib/device_detector/browser.rb +691 -0
  6. data/lib/device_detector/client.rb +11 -2
  7. data/lib/device_detector/client_hint.rb +249 -0
  8. data/lib/device_detector/device.rb +1954 -23
  9. data/lib/device_detector/memory_cache.rb +26 -19
  10. data/lib/device_detector/metadata_extractor.rb +7 -8
  11. data/lib/device_detector/model_extractor.rb +3 -3
  12. data/lib/device_detector/name_extractor.rb +2 -2
  13. data/lib/device_detector/os.rb +289 -112
  14. data/lib/device_detector/parser.rb +49 -13
  15. data/lib/device_detector/vendor_fragment.rb +25 -0
  16. data/lib/device_detector/version.rb +3 -1
  17. data/lib/device_detector/version_extractor.rb +29 -2
  18. data/lib/device_detector.rb +192 -44
  19. data/regexes/bots.yml +3399 -91
  20. data/regexes/client/browser_engine.yml +28 -4
  21. data/regexes/client/browsers.yml +2697 -408
  22. data/regexes/client/feed_readers.yml +60 -22
  23. data/regexes/client/hints/apps.yml +150 -0
  24. data/regexes/client/hints/browsers.yml +292 -0
  25. data/regexes/client/libraries.yml +598 -4
  26. data/regexes/client/mediaplayers.yml +110 -5
  27. data/regexes/client/mobile_apps.yml +2451 -14
  28. data/regexes/client/pim.yml +128 -3
  29. data/regexes/device/cameras.yml +6 -6
  30. data/regexes/device/car_browsers.yml +39 -3
  31. data/regexes/device/consoles.yml +40 -6
  32. data/regexes/device/mobiles.yml +38844 -2907
  33. data/regexes/device/notebooks.yml +127 -0
  34. data/regexes/device/portable_media_player.yml +75 -12
  35. data/regexes/device/shell_tv.yml +145 -0
  36. data/regexes/device/televisions.yml +981 -40
  37. data/regexes/oss.yml +1560 -311
  38. data/regexes/vendorfragments.yml +6 -2
  39. metadata +31 -105
  40. data/.gitignore +0 -14
  41. data/.travis.yml +0 -18
  42. data/Gemfile +0 -8
  43. data/Rakefile +0 -79
  44. data/device_detector.gemspec +0 -26
  45. data/spec/device_detector/bot_fixtures_spec.rb +0 -30
  46. data/spec/device_detector/client_fixtures_spec.rb +0 -31
  47. data/spec/device_detector/concrete_user_agent_spec.rb +0 -136
  48. data/spec/device_detector/detector_fixtures_spec.rb +0 -60
  49. data/spec/device_detector/device_fixtures_spec.rb +0 -36
  50. data/spec/device_detector/device_spec.rb +0 -151
  51. data/spec/device_detector/memory_cache_spec.rb +0 -116
  52. data/spec/device_detector/model_extractor_spec.rb +0 -63
  53. data/spec/device_detector/os_fixtures_spec.rb +0 -26
  54. data/spec/device_detector/version_extractor_spec.rb +0 -80
  55. data/spec/device_detector_spec.rb +0 -198
  56. data/spec/fixtures/client/browser.yml +0 -1313
  57. data/spec/fixtures/client/feed_reader.yml +0 -187
  58. data/spec/fixtures/client/library.yml +0 -84
  59. data/spec/fixtures/client/mediaplayer.yml +0 -168
  60. data/spec/fixtures/client/mobile_app.yml +0 -30
  61. data/spec/fixtures/client/pim.yml +0 -96
  62. data/spec/fixtures/detector/bots.yml +0 -2418
  63. data/spec/fixtures/detector/camera.yml +0 -115
  64. data/spec/fixtures/detector/car_browser.yml +0 -20
  65. data/spec/fixtures/detector/console.yml +0 -267
  66. data/spec/fixtures/detector/desktop.yml +0 -4828
  67. data/spec/fixtures/detector/feature_phone.yml +0 -782
  68. data/spec/fixtures/detector/feed_reader.yml +0 -486
  69. data/spec/fixtures/detector/mediaplayer.yml +0 -179
  70. data/spec/fixtures/detector/mobile_apps.yml +0 -149
  71. data/spec/fixtures/detector/phablet.yml +0 -2140
  72. data/spec/fixtures/detector/portable_media_player.yml +0 -153
  73. data/spec/fixtures/detector/smart_display.yml +0 -58
  74. data/spec/fixtures/detector/smartphone-1.yml +0 -9469
  75. data/spec/fixtures/detector/smartphone-2.yml +0 -9414
  76. data/spec/fixtures/detector/smartphone-3.yml +0 -9396
  77. data/spec/fixtures/detector/smartphone-4.yml +0 -5742
  78. data/spec/fixtures/detector/smartphone.yml +0 -9411
  79. data/spec/fixtures/detector/tablet-1.yml +0 -9495
  80. data/spec/fixtures/detector/tablet-2.yml +0 -248
  81. data/spec/fixtures/detector/tablet.yml +0 -9484
  82. data/spec/fixtures/detector/tv.yml +0 -2582
  83. data/spec/fixtures/detector/unknown.yml +0 -3196
  84. data/spec/fixtures/device/camera.yml +0 -18
  85. data/spec/fixtures/device/car_browser.yml +0 -6
  86. data/spec/fixtures/device/console.yml +0 -78
  87. data/spec/fixtures/parser/oss.yml +0 -800
  88. data/spec/fixtures/parser/vendorfragments.yml +0 -162
  89. data/spec/spec_helper.rb +0 -9
@@ -1,10 +1,19 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class DeviceDetector
2
4
  class Client < Parser
3
-
4
5
  def known?
5
6
  regex_meta.any?
6
7
  end
7
8
 
9
+ def browser?
10
+ regex_meta[:path] == :"client/browsers.yml"
11
+ end
12
+
13
+ def mobile_only_browser?
14
+ DeviceDetector::Browser.mobile_only_browser?(name)
15
+ end
16
+
8
17
  private
9
18
 
10
19
  def filenames
@@ -14,7 +23,7 @@ class DeviceDetector
14
23
  'client/mediaplayers.yml',
15
24
  'client/pim.yml',
16
25
  'client/browsers.yml',
17
- 'client/libraries.yml',
26
+ 'client/libraries.yml'
18
27
  ]
19
28
  end
20
29
  end
@@ -0,0 +1,249 @@
1
+ # frozen_string_literal: true
2
+
3
+ class DeviceDetector
4
+ class ClientHint
5
+ ROOT = File.expand_path('../..', __dir__)
6
+
7
+ REGEX_CACHE = ::DeviceDetector::MemoryCache.new({})
8
+ private_constant :REGEX_CACHE
9
+
10
+ class HintBrowser < Struct.new(:name, :version)
11
+ end
12
+
13
+ def initialize(headers)
14
+ return if headers.nil?
15
+
16
+ @headers = headers
17
+ @full_version = extract_full_version
18
+ @browser_list = extract_browser_list
19
+ @app_name = extract_app_name
20
+ @platform = extract_platform
21
+ @platform_version = extract_platform_version
22
+ @mobile = extract_mobile
23
+ @model = extract_model
24
+ end
25
+
26
+ attr_reader :app_name, :browser_list, :full_version, :headers, :mobile, :model, :platform,
27
+ :platform_version
28
+
29
+ def browser_name
30
+ return 'Iridium' if iridium?
31
+ return '360 Secure Browser' if secure_browser?
32
+
33
+ browser_name_from_list || app_name
34
+ end
35
+
36
+ def os_version
37
+ return windows_version if platform == 'Windows'
38
+ return lineage_version if lineage_os_app?
39
+ return fire_os_version if fire_os_app?
40
+
41
+ platform_version
42
+ end
43
+
44
+ def os_name
45
+ return 'Android' if android_app?
46
+ return 'Lineage OS' if lineage_os_app?
47
+ return 'Fire OS' if fire_os_app?
48
+ return unless ['Windows', 'Chromium OS'].include?(platform)
49
+
50
+ platform
51
+ end
52
+
53
+ def os_short_name
54
+ return if os_name.nil?
55
+
56
+ DeviceDetector::OS::DOWNCASED_OPERATING_SYSTEMS[os_name.downcase]
57
+ end
58
+
59
+ def os_family
60
+ return if os_short_name.nil?
61
+
62
+ DeviceDetector::OS::FAMILY_TO_OS[os_short_name]
63
+ end
64
+
65
+ private
66
+
67
+ # https://github.com/matomo-org/device-detector/blob/28211c6f411528abf41304e07b886fdf322a49b7/Parser/OperatingSystem.php#L330
68
+ def android_app?
69
+ %w[com.hisense.odinbrowser com.seraphic.openinet.pre
70
+ com.appssppa.idesktoppcbrowser every.browser.inc].include?(app_name_from_headers)
71
+ end
72
+
73
+ # https://github.com/matomo-org/device-detector/blob/67ae11199a5129b42fa8b985d372ea834104fe3a/Parser/OperatingSystem.php#L449-L456
74
+ def fire_os_app?
75
+ app_name_from_headers == 'org.mozilla.tv.firefox'
76
+ end
77
+
78
+ # https://github.com/matomo-org/device-detector/blob/67ae11199a5129b42fa8b985d372ea834104fe3a/Parser/OperatingSystem.php#L439-L447
79
+ def lineage_os_app?
80
+ app_name_from_headers == 'org.lineageos.jelly'
81
+ end
82
+
83
+ # https://github.com/matomo-org/device-detector/blob/75d88bbefb0182f9207c9f48dc39b1bc8c7cc43f/Parser/Client/Browser.php#L1076-L1079
84
+ def browser_name_from_list
85
+ @browser_name_from_list ||= browser_list&.reject do |b|
86
+ ['Chromium', 'Microsoft Edge'].include?(b.name)
87
+ end&.last&.name
88
+ end
89
+
90
+ def available_browsers
91
+ DeviceDetector::Browser::AVAILABLE_BROWSERS.values
92
+ end
93
+
94
+ def available_osses
95
+ DeviceDetector::OS::OPERATING_SYSTEMS.values
96
+ end
97
+
98
+ # https://github.com/matomo-org/device-detector/blob/28211c6f411528abf41304e07b886fdf322a49b7/Parser/OperatingSystem.php#L434
99
+ def windows_version
100
+ return if platform_version.nil?
101
+
102
+ major_version = platform_version.split('.').first.to_i
103
+ return if major_version < 1
104
+
105
+ major_version < 11 ? '10' : '11'
106
+ end
107
+
108
+ def lineage_version
109
+ DeviceDetector::OS
110
+ .mapped_os_version(platform_version, DeviceDetector::OS::LINEAGE_OS_VERSION_MAPPING)
111
+ end
112
+
113
+ def fire_os_version
114
+ DeviceDetector::OS
115
+ .mapped_os_version(platform_version, DeviceDetector::OS::FIRE_OS_VERSION_MAPPING)
116
+ end
117
+
118
+ # https://github.com/matomo-org/device-detector/blob/67ae11199a5129b42fa8b985d372ea834104fe3a/Parser/Client/Browser.php#L923-L929
119
+ # If the version reported from the client hints is YYYY or YYYY.MM (e.g., 2022 or 2022.04),
120
+ # then it is the Iridium browser
121
+ # https://iridiumbrowser.de/news/
122
+ def iridium?
123
+ return if browser_list.nil?
124
+
125
+ !browser_list.find do |browser|
126
+ browser.name == 'Chromium' && browser.version =~ /^202[0-4]/
127
+ end.nil?
128
+ end
129
+
130
+ # https://github.com/matomo-org/device-detector/blob/67ae11199a5129b42fa8b985d372ea834104fe3a/Parser/Client/Browser.php#L931-L937
131
+ # https://bbs.360.cn/thread-16096544-1-1.html
132
+ def secure_browser?
133
+ return if browser_list.nil?
134
+
135
+ !browser_list.find do |browser|
136
+ browser.name == 'Chromium' && browser.version =~ /^15/
137
+ end.nil?
138
+ end
139
+
140
+ def app_name_from_headers
141
+ return if headers.nil?
142
+
143
+ headers['http-x-requested-with'] ||
144
+ headers['X-Requested-With'] ||
145
+ headers['x-requested-with']
146
+ end
147
+
148
+ def extract_app_name
149
+ requested_with = app_name_from_headers
150
+ return if requested_with.nil?
151
+
152
+ hint_app_names[requested_with]
153
+ end
154
+
155
+ def hint_app_names
156
+ DeviceDetector.cache.get_or_set('hint_app_names') do
157
+ load_hint_app_names.flatten.reduce({}, :merge)
158
+ end
159
+ end
160
+
161
+ def hint_filenames
162
+ %w[client/hints/browsers.yml client/hints/apps.yml]
163
+ end
164
+
165
+ def hint_filepaths
166
+ hint_filenames.map do |filename|
167
+ [filename.to_sym, File.join(ROOT, 'regexes', filename)]
168
+ end
169
+ end
170
+
171
+ def load_hint_app_names
172
+ hint_filepaths.map { |_, full_path| YAML.load_file(full_path) }
173
+ end
174
+
175
+ def extract_browser_list
176
+ extract_browser_list_from_full_version_list ||
177
+ extract_browser_list_from_header('Sec-CH-UA') ||
178
+ extract_browser_list_from_header('Sec-CH-UA-Full-Version-List')
179
+ end
180
+
181
+ def extract_browser_list_from_header(header)
182
+ return if headers[header].nil?
183
+
184
+ headers[header].split(', ').map do |component|
185
+ name_and_version = extract_browser_name_and_version(component)
186
+ next if name_and_version[:name].nil?
187
+
188
+ HintBrowser.new(name_and_version[:name], name_and_version[:version])
189
+ end.compact
190
+ end
191
+
192
+ def extract_browser_name_and_version(component)
193
+ component_and_version = component.gsub('"', '').split("\;v=")
194
+ name = name_from_known_browsers(component_and_version.first)
195
+ browser_version = full_version&.gsub('"', '') || component_and_version.last
196
+ { name: name, version: browser_version }
197
+ end
198
+
199
+ def extract_browser_list_from_full_version_list
200
+ return if headers['fullVersionList'].nil? && headers['brands'].nil?
201
+
202
+ (headers['brands'] || headers['fullVersionList']).map do |item|
203
+ name = name_from_known_browsers(item['brand'])
204
+ next if name.nil?
205
+
206
+ HintBrowser.new(name, full_version || item['version'])
207
+ end.compact
208
+ end
209
+
210
+ # https://github.com/matomo-org/device-detector/blob/be1c9ef486c247dc4886668da5ed0b1c49d90ba8/Parser/Client/Browser.php#L865
211
+ def name_from_known_browsers(name)
212
+ DeviceDetector::Browser::KNOWN_BROWSER_TO_NAME.fetch(name) do
213
+ available_browsers.find do |i|
214
+ i == name ||
215
+ i.gsub(' ', '') == name.gsub(' ', '') ||
216
+ i == name.gsub('Browser', '') ||
217
+ i == name.gsub(' Browser', '') ||
218
+ i == "#{name} Browser"
219
+ end
220
+ end
221
+ end
222
+
223
+ def extract_from_header(header)
224
+ return if headers[header].nil? || headers[header] == ''
225
+
226
+ headers[header]
227
+ end
228
+
229
+ def extract_full_version
230
+ extract_from_header('Sec-CH-UA-Full-Version') || extract_from_header('uaFullVersion')
231
+ end
232
+
233
+ def extract_platform
234
+ extract_from_header('Sec-CH-UA-Platform') || extract_from_header('platform')
235
+ end
236
+
237
+ def extract_platform_version
238
+ extract_from_header('Sec-CH-UA-Platform-Version') || extract_from_header('platformVersion')
239
+ end
240
+
241
+ def extract_mobile
242
+ extract_from_header('Sec-CH-UA-Mobile') || extract_from_header('mobile')
243
+ end
244
+
245
+ def extract_model
246
+ extract_from_header('Sec-CH-UA-Model') || extract_from_header('model')
247
+ end
248
+ end
249
+ end