pod_ident 1.0.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.
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PodIdent
4
+ class DetectionResult
5
+ attr_reader :app, :user_agent, :rule
6
+ attr_accessor :platform
7
+
8
+ def initialize(rule, user_agent)
9
+ @user_agent = user_agent
10
+ return unless rule
11
+
12
+ @rule = rule
13
+ @app = rule[:app]
14
+ end
15
+
16
+ def positive?
17
+ !@app.nil?
18
+ end
19
+
20
+ def platform_rule
21
+ @rule[:platform]
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,3 @@
1
+ # DO NOT EDIT THIS FILE - it gets automatically generated by running "bin/parse-rules"
2
+
3
+ RULES = [{:app=>"Apple Podcasts", :match=>{"startsWith"=>"AppleCoreMedia"}, :platform=>{"regex"=>"\\((\\w+\\s*\\w*)", "replacements"=>[{"name"=>"Macintosh", "replaceWith"=>"Mac"}]}}, {:app=>"Apple Podcasts", :match=>{"startsWith"=>"itunesstored"}, :platform=>{"regex"=>"(iPad|iPod|iPhone)"}}, {:app=>"iTunes", :match=>{"startsWith"=>"iTunes", "excluding"=>{"regex"=>"Downcast|iCatcher|SqueezeCenter|SqueezeNetwork|MusicServer"}}, :platform=>{"regex"=>"\\((\\w+\\s*\\w*)", "replacements"=>[{"name"=>"Macintosh", "replaceWith"=>"Mac"}]}}, {:app=>"Apple Podcasts", :match=>{"includes"=>"watchOS"}, :platform=>{"text"=>"watchOS"}}, {:app=>"CastBox", :match=>{"startsWith"=>["CastBox", "Castbox"]}, :platform=>{"regexes"=>["(Android|iOS|OS\\sVersion)", "(CastBox)"], "replacements"=>[{"name"=>"OS Version", "replaceWith"=>"iOS"}, {"name"=>"CastBox", "replaceWith"=>"Android"}]}}, {:app=>"Amazon Alexa", :match=>{"startsWith"=>"Alexa"}, :platform=>{"text"=>"Alexa-capable device"}}, {:app=>"Amazon Alexa", :match=>{"startsWith"=>"Echo"}, :platform=>{"text"=>"Amazon Echo", "userAgents"=>[{"userAgent"=>"Echo/1.0(APNG)", "platform"=>"Amazon Echo"}]}}, {:app=>"Deezer", :match=>{"startsWith"=>"Deezer"}, :platform=>{"regexes"=>["(Android|Darwin)", "\\((\\w+\\s*\\w*)"], "replacements"=>[{"name"=>"osx", "replaceWith"=>"Mac"}, {"name"=>"Darwin", "replaceWith"=>"Apple Device"}]}}, {:app=>"Overcast", :match=>{"startsWith"=>"Overcast"}, :platform=>{"text"=>"iOS"}}, {:app=>"Radio.net", :match=>{"startsWith"=>["radio.net", "radio.de", "radio.at", "radio.fr", "radio.dk", "radio.es", "radio.it", "radio.pt", "radio.pl"]}, :platform=>{"regex"=>"(Android|Darwin)", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"iOS"}]}}, {:app=>"PocketCasts", :match=>{"startsWith"=>["PocketCasts", "Pocket Casts", "Shifty Jelly Pocket Casts"]}, :platform=>{"regex"=>"(Android)"}}, {:app=>"Himalaya", :match=>{"startsWith"=>"Himalaya"}, :platform=>{"regex"=>"(Darwin|Android)", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"iOS"}]}}, {:app=>"ExoPlayer", :match=>{"startsWith"=>["ExoPlayer", "yourApplicationName", "null", "md5d42223d6ee7473da82e8136ffb794439.App"]}, :platform=>{"text"=>"Android"}}, {:app=>"Download Manager", :match=>{"startsWith"=>"AndroidDownloadManager"}, :platform=>{"text"=>"Android"}}, {:app=>"Castamatic", :match=>{"startsWith"=>"Castamatic"}, :platform=>{"text"=>"iOS"}}, {:app=>"The Podcast App (podcast.app)", :match=>{"includes"=>"The Podcast App"}, :platform=>{"text"=>"iOS"}}, {:app=>"CastMix", :match=>{"startsWith"=>"CastMix"}, :platform=>{"text"=>"Android"}}, {:app=>"Unknown App", :match=>{"startsWith"=>"okhttp"}, :platform=>{"text"=>"Android"}}, {:app=>"Stagefright Media Playback Engine", :match=>{"includes"=>"stagefright", "excluding"=>{"text"=>"stagefright alternative"}}, :platform=>{"regex"=>"(Fire OS|Android)"}}, {:app=>"LG Player", :match=>{"startsWith"=>"Player/LG Player", "includes"=>["LG Player", "LG-Player"]}, :platform=>{"text"=>"Android"}}, {:app=>"Android Browser", :match=>{"startsWith"=>"Dalvik"}, :platform=>{"text"=>"Android"}}, {:app=>"Acast", :match=>{"startsWith"=>"Acast"}, :platform=>{"regex"=>"(Darwin|Android|Windows)", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"iOS"}]}}, {:app=>"Castro", :match=>{"startsWith"=>"Castro"}, :platform=>{"text"=>"iOS"}}, {:app=>"Breaker", :match=>{"startsWith"=>"Breaker"}, :platform=>{"regex"=>"(Darwin|Android)", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"iOS"}]}}, {:app=>"Podcast Addict", :match=>{"startsWith"=>["PodcastAddict", "Podcast Addict"]}, :platform=>{"text"=>"Android"}}, {:app=>"Podbean", :match=>{"startsWith"=>"Podbean"}, :platform=>{"regex"=>"(iOS|Android)"}}, {:app=>"Google Podcasts", :match=>{"includes"=>["GSA"], "excluding"=>{"regex"=>"iPhone|iPad"}}, :platform=>{"text"=>"Android"}}, {:app=>"Google Search App", :match=>{"includes"=>["GSA"], "excluding"=>{"regex"=>"Android"}}, :platform=>{"regexes"=>["(iPhone|iPad)"]}}, {:app=>"Google Podcasts", :match=>{"includes"=>"GoogleChirp"}, :platform=>{"text"=>"Google Smart Speaker"}}, {:app=>"Stitcher", :match=>{"startsWith"=>"Stitcher"}, :platform=>{"regex"=>"(iOS|Android)"}}, {:app=>"TuneIn", :match=>{"startsWith"=>"TuneIn"}, :platform=>{"regex"=>"(Darwin|Android)", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"iOS"}]}}, {:app=>"PodCruncher", :match=>{"startsWith"=>"PodCruncher"}, :platform=>{"text"=>"iOS"}}, {:app=>"iCatcher!", :match=>{"startsWith"=>"iCatcher!", "includes"=>"iCatcher!"}, :platform=>{"regex"=>"\\((iPhone|iPad|iPod touch)", "fallback"=>"iOS"}}, {:app=>"Castaway", :match=>{"startsWith"=>"Castaway"}, :platform=>{"text"=>"iOS"}}, {:app=>"Instacast", :match=>{"startsWith"=>"Instacast"}, :platform=>{"text"=>"Apple Device"}}, {:app=>"VLC", :match=>{"startsWith"=>["VLC", "LibVLC"], "includes"=>"VLC"}, :platform=>{"regex"=>"(Android|iPhone)"}}, {:app=>"Podcast Republic", :match=>{"startsWith"=>"PodcastRepublic"}, :platform=>{"text"=>"Android"}}, {:app=>"DoggCatcher", :match=>{"includes"=>"DoggCatcher"}, :platform=>{"text"=>"Android"}}, {:app=>"Player FM", :match=>{"startsWith"=>["Player FM", "Player%20FM"]}, :platform=>{"regex"=>"(Darwin)", "fallback"=>"Android", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"iOS"}]}}, {:app=>"Podkicker", :match=>{"startsWith"=>"Podkicker"}, :platform=>{"text"=>"Android"}}, {:app=>"AntennaPod", :match=>{"startsWith"=>"AntennaPod"}, :platform=>{"text"=>"Android"}}, {:app=>"Downcast", :match=>{"startsWith"=>"Downcast", "includes"=>"Downcast"}, :platform=>{"regex"=>"\\((iPhone|iPad|iPod touch|Mac)"}}, {:app=>"gPodder", :match=>{"startsWith"=>"gPodder"}, :platform=>{"regex"=>"(Linux|Windows)"}}, {:app=>"Podcatcher Deluxe", :match=>{"includes"=>"Podcatcher Deluxe"}, :platform=>{"text"=>"Android"}}, {:app=>"Procast", :match=>{"startsWith"=>["Procast", "ProCast"]}, :platform=>{"text"=>"iOS"}}, {:app=>"RSSRadio", :match=>{"startsWith"=>"RSSRadio"}, :platform=>{"regex"=>"(iPhone|iPad|iPod touch|Darwin)", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"iOS"}], "fallback"=>"iOS"}}, {:app=>"Podcat", :match=>{"startsWith"=>"Podcat", "excluding"=>{"regex"=>"Podcatcher"}}, :platform=>{"text"=>"iOS"}}, {:app=>"Audio Now", :match=>{"startsWith"=>"AudioNow", "includes"=>"audionow"}, :platform=>{"regex"=>"(iOS|Android)"}}, {:app=>"DIE ZEIT App", :match=>{"includes"=>"ZONApp"}, :platform=>{"regex"=>"(iPhone|iPad|iPod touch|Android)"}}, {:app=>"F.A.Z Der Tag App", :match=>{"includes"=>"FAZDERTAG"}, :platform=>{"regexes"=>["(Android)", "\\((iPhone|iPad|iPod touch)"]}}, {:app=>"ANTENNE BAYERN App", :match=>{"includes"=>"AntenneBayern"}, :platform=>{"regexes"=>["(Android)", "\\((iPhone|iPad|iPod touch)"]}}, {:app=>"BuzzFeed App", :match=>{"includes"=>"buzzfeed"}, :platform=>{"regexes"=>["(Android)", "\\((iPhone|iPad|iPod touch)"]}}, {:app=>"Facebook in-app browser", :match=>{"includes"=>["FBAN", "FBAV"]}, :platform=>{"regexes"=>["\\((iPhone|iPad|iPod touch)", "(Android)"]}}, {:app=>"Instagram in-app browser", :match=>{"includes"=>"Instagram"}, :platform=>{"regexes"=>["(iPad)", "(iPhone|Android)"]}}, {:app=>"Twitter in-app browser", :match=>{"includes"=>"Twitter"}, :platform=>{"regex"=>"(iPhone|iPad|Darwin|Android)", "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"Apple device"}]}}, {:app=>"Pinterest in-app browser", :match=>{"includes"=>"Pinterest"}, :platform=>{"regexes"=>["(Android)", "\\((iPhone|iPad|iPod touch)"]}}, {:app=>"Windows Media Player", :match=>{"startsWith"=>["NSPlayer", "WMPlayer"]}, :platform=>{"text"=>"Windows"}}, {:app=>"Sonos", :match=>{"includes"=>"Sonos"}, :platform=>{"text"=>"Sonos"}}, {:app=>"Internet Explorer", :match=>{"includes"=>"Trident"}, :platform=>{"regex"=>"(Windows Phone)", "fallback"=>"Windows"}}, {:app=>"Kodi Media Center", :match=>{"startsWith"=>"Kodi", "includes"=>"Kodi"}, :platform=>{"regex"=>"(X11|Android|Windows)", "replacements"=>[{"name"=>"X11", "replaceWith"=>"Linux"}]}}, {:app=>"HermesPod", :match=>{"startsWith"=>"+hermespod.com"}, :platform=>{"text"=>"Windows"}}, {:app=>"ViennaRSS", :match=>{"includes"=>"Vienna"}, :platform=>{"text"=>"Mac"}}, {:app=>"Unknown client", :match=>{"startsWith"=>"(null)"}, :platform=>{"regex"=>"(iPhone|iPad|iPod touch)"}}, {:app=>"Clementine Music Player", :match=>{"startsWith"=>"Clementine"}, :platform=>{"text"=>"Unknown"}}, {:app=>"Flipboard", :match=>{"includes"=>"Flipboard"}, :platform=>{"regexes"=>["(Android)", "\\((iPhone|iPad|iPod touch|Macintosh)"], "replacements"=>[{"name"=>"Macintosh", "replaceWith"=>"Mac"}]}}, {:app=>"iVoox", :match=>{"startsWith"=>["ivoox", "iVoox"]}, :platform=>{"regexes"=>["(Android)", "(Darwin)\\/", "\\((iPhone|iPad|iPod touch|Macintosh)"], "replacements"=>[{"name"=>"Darwin", "replaceWith"=>"Apple device"}, {"name"=>"Macintosh", "replaceWith"=>"Mac"}]}}, {:app=>"FYEO", :match=>{"startsWith"=>"FYEO"}, :platform=>{"regex"=>"(iOS|Android)"}}].freeze
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'yaml'
4
+ require 'pry'
5
+
6
+ module PodIdent
7
+ class RuleParser
8
+ RULES_YAML = File.expand_path('../detection_rules.yml', __dir__)
9
+ RULES_RUBY = File.expand_path('detection_rules.rb', __dir__)
10
+ RULES_SPEC_RUBY = File.expand_path('../../spec/detection_rules.rb', __dir__)
11
+ DO_NOT_EDIT_TEXT = <<~HEREDOC
12
+ # DO NOT EDIT THIS FILE - it gets automatically generated by running \"bin/parse-rules\"\n
13
+ HEREDOC
14
+
15
+ attr_accessor :rules
16
+
17
+ def call
18
+ parse_yaml
19
+ write_rules_rb
20
+ write_rules_spec_rb
21
+ end
22
+
23
+ private
24
+
25
+ def write_rules_rb
26
+ cleaned_rules = rules.dup.map do |rule|
27
+ {
28
+ app: rule['app'],
29
+ match: rule['match'],
30
+ platform: rule['platform']
31
+ }
32
+ end
33
+
34
+ File.open(RULES_RUBY, 'w') do |file|
35
+ file.write(DO_NOT_EDIT_TEXT)
36
+ file.write("RULES = #{cleaned_rules}.freeze")
37
+ end
38
+ end
39
+
40
+ def write_rules_spec_rb
41
+ all_rules = rules.dup.map do |rule|
42
+ # symbolize keys
43
+ Hash[rule.map { |(k, v)| [k.to_sym, v] }]
44
+ end
45
+
46
+ File.open(RULES_SPEC_RUBY, 'w') do |file|
47
+ file.write(DO_NOT_EDIT_TEXT)
48
+ file.write("RULES = #{all_rules}.freeze")
49
+ end
50
+ end
51
+
52
+ def parse_yaml
53
+ @rules = YAML.safe_load(rules_yaml_file_content)
54
+ end
55
+
56
+ def rules_yaml_file_content
57
+ File.read(RULES_YAML).gsub('\\\\', '\\')
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module PodIdent
4
+ VERSION = '1.0.1'
5
+ end
data/lib/pod_ident.rb ADDED
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'pod_ident/version'
4
+ require 'pod_ident/detection_rules'
5
+ require 'pod_ident/detection_result'
6
+
7
+ module PodIdent
8
+ class Detector
9
+ attr_reader :user_agent_string
10
+ attr_accessor :result
11
+
12
+ def self.detect(user_agent_string)
13
+ new(user_agent_string).detect
14
+ end
15
+
16
+ def initialize(user_agent_string)
17
+ @user_agent_string = user_agent_string
18
+ end
19
+
20
+ def detect
21
+ # !~ /[^[:space:]]/ is what Active Support does to detect blank strings
22
+ return nil if user_agent_string !~ /[^[:space:]]/
23
+
24
+ self.result = DetectionResult.new(find_rule, user_agent_string)
25
+ identify_platform if result.positive?
26
+
27
+ result
28
+ end
29
+
30
+ private
31
+
32
+ def find_rule
33
+ RULES.detect do |rule|
34
+ found = false
35
+
36
+ match = rule.fetch(:match)
37
+
38
+ found = apply_starts_with(match['startsWith'], found)
39
+ found ||= apply_includes(match['includes'], found)
40
+
41
+ excluding = match['excluding']
42
+ if found && excluding
43
+ found = false if excluding['text'] && user_agent_string.include?(excluding['text'])
44
+
45
+ if excluding['regex'] && Regexp.new(excluding['regex']).match(user_agent_string)
46
+ found = false
47
+ end
48
+ end
49
+
50
+ found
51
+ end
52
+ end
53
+
54
+ def apply_starts_with(starts_with, found)
55
+ return found unless starts_with
56
+
57
+ Array(starts_with).detect do |string|
58
+ found = user_agent_string.start_with?(string)
59
+ end
60
+
61
+ found
62
+ end
63
+
64
+ def apply_includes(includes, found)
65
+ return found unless includes
66
+
67
+ Array(includes).detect do |string|
68
+ found = user_agent_string.include?(string)
69
+ end
70
+ found
71
+ end
72
+
73
+ def identify_platform
74
+ result.platform = nil
75
+
76
+ regexes = if result.platform_rule['regex']
77
+ Array(result.platform_rule['regex'])
78
+ else
79
+ result.platform_rule['regexes']
80
+ end
81
+
82
+ result.platform = result.platform_rule['text'] if result.platform_rule['text']
83
+
84
+ regexes&.detect do |regex|
85
+ match = Regexp.new(regex).match(user_agent_string)
86
+ next if match.nil?
87
+
88
+ result.platform = match[1]
89
+ if result.platform_rule['replacements']
90
+ result.platform = replace_name(result.platform, result.platform_rule['replacements'])
91
+ end
92
+ true
93
+ end
94
+
95
+ result.platform = result.platform_rule['fallback'] if !result.platform && result.platform_rule['fallback']
96
+ end
97
+
98
+ def replace_name(target, replacements)
99
+ replacement = replacements.detect { |repl| repl['name'] == target }
100
+
101
+ if replacement
102
+ replacement['replaceWith']
103
+ else
104
+ target
105
+ end
106
+ end
107
+ end
108
+ end
data/pod_ident.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'pod_ident/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'pod_ident'
9
+ spec.version = PodIdent::VERSION
10
+ spec.authors = ['Podigee GmbH']
11
+ spec.email = ['hello@podigee.com']
12
+
13
+ spec.summary = 'Identifies podcast client user agents'
14
+ spec.description = <<~HEREDOC
15
+ Library to identify podcast client user agents and translate them into human readable information.'
16
+ HEREDOC
17
+ spec.homepage = 'https://www.podigee.com'
18
+
19
+ # Specify which files should be added to the gem when it is released.
20
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
21
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
23
+ end
24
+ spec.bindir = 'exe'
25
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
26
+ spec.require_paths = ['lib']
27
+
28
+ spec.add_development_dependency 'bundler', '~> 1.16'
29
+ spec.add_development_dependency 'pry'
30
+ spec.add_development_dependency 'rake', '~> 13.0'
31
+ spec.add_development_dependency 'rspec', '~> 3.0'
32
+ spec.add_development_dependency 'rspec_junit_formatter'
33
+ end
metadata ADDED
@@ -0,0 +1,134 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pod_ident
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Podigee GmbH
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2020-03-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec_junit_formatter
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description: 'Library to identify podcast client user agents and translate them into
84
+ human readable information.''
85
+
86
+ '
87
+ email:
88
+ - hello@podigee.com
89
+ executables: []
90
+ extensions: []
91
+ extra_rdoc_files: []
92
+ files:
93
+ - ".circleci/config.yml"
94
+ - ".gitignore"
95
+ - ".rspec"
96
+ - ".rubocop.yml"
97
+ - ".ruby-version"
98
+ - Gemfile
99
+ - Gemfile.lock
100
+ - README.md
101
+ - Rakefile
102
+ - bin/console
103
+ - bin/parse-rules
104
+ - bin/setup
105
+ - lib/detection_rules.yml
106
+ - lib/pod_ident.rb
107
+ - lib/pod_ident/detection_result.rb
108
+ - lib/pod_ident/detection_rules.rb
109
+ - lib/pod_ident/rule_parser.rb
110
+ - lib/pod_ident/version.rb
111
+ - pod_ident.gemspec
112
+ homepage: https://www.podigee.com
113
+ licenses: []
114
+ metadata: {}
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubygems_version: 3.0.3
131
+ signing_key:
132
+ specification_version: 4
133
+ summary: Identifies podcast client user agents
134
+ test_files: []