searchlink 2.3.74 → 2.3.76

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 (48) hide show
  1. checksums.yaml +4 -4
  2. data/lib/searchlink/config.rb +23 -23
  3. data/lib/searchlink/curl/html.rb +38 -38
  4. data/lib/searchlink/curl/json.rb +19 -17
  5. data/lib/searchlink/curl.rb +2 -2
  6. data/lib/searchlink/exceptions.rb +2 -2
  7. data/lib/searchlink/help.rb +13 -13
  8. data/lib/searchlink/output.rb +21 -21
  9. data/lib/searchlink/parse.rb +113 -108
  10. data/lib/searchlink/plist.rb +11 -11
  11. data/lib/searchlink/script_plugin.rb +10 -10
  12. data/lib/searchlink/search.rb +6 -6
  13. data/lib/searchlink/searches/amazon.rb +4 -4
  14. data/lib/searchlink/searches/applemusic.rb +28 -28
  15. data/lib/searchlink/searches/bitly.rb +11 -11
  16. data/lib/searchlink/searches/definition.rb +7 -7
  17. data/lib/searchlink/searches/duckduckgo.rb +31 -27
  18. data/lib/searchlink/searches/github.rb +48 -48
  19. data/lib/searchlink/searches/google.rb +16 -16
  20. data/lib/searchlink/searches/helpers/chromium.rb +46 -46
  21. data/lib/searchlink/searches/helpers/firefox.rb +20 -20
  22. data/lib/searchlink/searches/helpers/safari.rb +14 -14
  23. data/lib/searchlink/searches/history.rb +78 -78
  24. data/lib/searchlink/searches/hook.rb +5 -5
  25. data/lib/searchlink/searches/itunes.rb +37 -37
  26. data/lib/searchlink/searches/lastfm.rb +13 -13
  27. data/lib/searchlink/searches/linkding.rb +14 -14
  28. data/lib/searchlink/searches/lyrics.rb +11 -11
  29. data/lib/searchlink/searches/pinboard.rb +35 -35
  30. data/lib/searchlink/searches/social.rb +45 -56
  31. data/lib/searchlink/searches/software.rb +4 -4
  32. data/lib/searchlink/searches/spelling.rb +10 -10
  33. data/lib/searchlink/searches/spotlight.rb +4 -4
  34. data/lib/searchlink/searches/stackoverflow.rb +5 -5
  35. data/lib/searchlink/searches/tmdb.rb +17 -17
  36. data/lib/searchlink/searches/twitter.rb +8 -8
  37. data/lib/searchlink/searches/wikipedia.rb +4 -4
  38. data/lib/searchlink/searches/youtube.rb +7 -7
  39. data/lib/searchlink/searches.rb +16 -16
  40. data/lib/searchlink/semver.rb +4 -4
  41. data/lib/searchlink/string.rb +55 -55
  42. data/lib/searchlink/url.rb +30 -32
  43. data/lib/searchlink/util.rb +3 -3
  44. data/lib/searchlink/version.rb +19 -21
  45. data/lib/searchlink/which.rb +5 -5
  46. data/lib/searchlink.rb +31 -31
  47. metadata +31 -18
  48. data/lib/tokens.rb +0 -3
@@ -6,17 +6,17 @@ module SL
6
6
  # Script Search
7
7
  class ScriptSearch
8
8
  def initialize(config)
9
- @filename = config['filename']
10
- @path = config['path']
9
+ @filename = config["filename"]
10
+ @path = config["path"]
11
11
 
12
12
  %w[trigger searches name script].each do |key|
13
13
  raise PluginError.new(%(configuration missing key "#{key}"), plugin: @filename) unless config.key?(key)
14
14
  end
15
15
 
16
- @trigger = config['trigger']
17
- @searches = config['searches']
18
- @name = config['name']
19
- @script = find_script(config['script'])
16
+ @trigger = config["trigger"]
17
+ @searches = config["searches"]
18
+ @name = config["name"]
19
+ @script = find_script(config["script"])
20
20
 
21
21
  unless File.executable?(@script)
22
22
  raise PluginError.new(%(script "#{File.basename(@script)}" not executable\nrun `chmod a+x #{@script.shorten_path}` to correct),
@@ -36,7 +36,7 @@ module SL
36
36
  terms = Shellwords.escape(search_terms)
37
37
  text = Shellwords.escape(link_text)
38
38
 
39
- stdout = `#{[@script, type, terms, text].join(' ')} 2>&1`
39
+ stdout = `#{[@script, type, terms, text].join(" ")} 2>&1`
40
40
 
41
41
  unless $CHILD_STATUS.success?
42
42
  raise PluginError.new(%("#{File.basename(@script)}" returned error #{$CHILD_STATUS.exitstatus}\n#{stdout}),
@@ -60,7 +60,7 @@ module SL
60
60
  end
61
61
  end
62
62
 
63
- [res['url'], res['title'], res['link_text']]
63
+ [res["url"], res["title"], res["link_text"]]
64
64
  end
65
65
  end
66
66
 
@@ -70,11 +70,11 @@ module SL
70
70
  def find_script(script)
71
71
  return File.expand_path(script) if File.exist?(File.expand_path(script))
72
72
 
73
- base = File.expand_path('~/.config/searchlink/plugins')
73
+ base = File.expand_path("~/.config/searchlink/plugins")
74
74
  first = File.join(base, script)
75
75
  return first if File.exist?(first)
76
76
 
77
- base = File.expand_path('~/.config/searchlink')
77
+ base = File.expand_path("~/.config/searchlink")
78
78
  second = File.join(base, script)
79
79
  return second if File.exist?(second)
80
80
 
@@ -21,9 +21,9 @@ module SL
21
21
  #
22
22
  # @return [Array] [Url, link, text]
23
23
  #
24
- def do_search(search_type, search_terms, link_text = '', search_count = 0)
24
+ def do_search(search_type, search_terms, link_text = "", search_count = 0)
25
25
  if (search_count % 5).zero?
26
- SL.notify('Throttling for 5s')
26
+ SL.notify("Throttling for 5s")
27
27
  sleep 5
28
28
  end
29
29
 
@@ -37,13 +37,13 @@ module SL
37
37
  else
38
38
  case search_type
39
39
  when /^r$/ # simple replacement
40
- if SL.config['validate_links'] && !SL::URL.valid_link?(search_terms)
40
+ if SL.config["validate_links"] && !SL::URL.valid_link?(search_terms)
41
41
  return [false, "Link not valid: #{search_terms}", link_text]
42
42
  end
43
43
 
44
44
  title = SL::URL.title(search_terms) || search_terms
45
45
 
46
- link_text = title if link_text == ''
46
+ link_text = title if link_text == ""
47
47
  return [search_terms, title, link_text]
48
48
  else
49
49
  if search_terms
@@ -56,11 +56,11 @@ module SL
56
56
  end
57
57
  end
58
58
 
59
- if link_text == ''
59
+ if link_text == ""
60
60
  link_text = SL.titleize ? title : search_terms
61
61
  end
62
62
 
63
- if url && SL.config['validate_links'] && !SL::URL.valid_link?(url) && search_type !~ /^sp(ell)?/
63
+ if url && SL.config["validate_links"] && !SL::URL.valid_link?(url) && search_type !~ /^sp(ell)?/
64
64
  [false, "Not found: #{url}", link_text]
65
65
  elsif !url
66
66
  [false, "No results: #{url}", link_text]
@@ -6,22 +6,22 @@ module SL
6
6
  class << self
7
7
  def settings
8
8
  {
9
- trigger: 'a',
9
+ trigger: "a",
10
10
  searches: [
11
- ['a', 'Amazon Search']
11
+ ["a", "Amazon Search"]
12
12
  ]
13
13
  }
14
14
  end
15
15
 
16
16
  def search(_, search_terms, link_text)
17
17
  az_url, = SL.ddg("site:amazon.com #{search_terms}", link_text)
18
- url, title = SL::URL.amazon_affiliatize(az_url, SL.config['amazon_partner'])
18
+ url, title = SL::URL.amazon_affiliatize(az_url, SL.config["amazon_partner"])
19
19
  title ||= search_terms
20
20
 
21
21
  [url, title, link_text]
22
22
  end
23
23
  end
24
24
 
25
- SL::Searches.register 'amazon', :search, self
25
+ SL::Searches.register "amazon", :search, self
26
26
  end
27
27
  end
@@ -8,35 +8,35 @@ module SL
8
8
  class << self
9
9
  def settings
10
10
  {
11
- trigger: 'am(pod|art|alb|song)?e?',
11
+ trigger: "am(pod|art|alb|song)?e?",
12
12
  searches: [
13
- ['am', 'Apple Music'],
14
- ['ampod', 'Apple Music Podcast'],
15
- ['amart', 'Apple Music Artist'],
16
- ['amalb', 'Apple Music Album'],
17
- ['amsong', 'Apple Music Song'],
18
- ['amalbe', 'Apple Music Album Embed'],
19
- ['amsong', 'Apple Music Song Embed']
13
+ ["am", "Apple Music"],
14
+ ["ampod", "Apple Music Podcast"],
15
+ ["amart", "Apple Music Artist"],
16
+ ["amalb", "Apple Music Album"],
17
+ ["amsong", "Apple Music Song"],
18
+ ["amalbe", "Apple Music Album Embed"],
19
+ ["amsong", "Apple Music Song Embed"]
20
20
  ]
21
21
  }
22
22
  end
23
23
 
24
24
  def search(search_type, search_terms, link_text)
25
- stype = search_type.downcase.sub(/^am/, '')
25
+ stype = search_type.downcase.sub(/^am/, "")
26
26
  otype = :link
27
27
  if stype =~ /e$/
28
28
  otype = :embed
29
- stype.sub!(/e$/, '')
29
+ stype.sub!(/e$/, "")
30
30
  end
31
31
  result = case stype
32
32
  when /^pod$/
33
- applemusic(search_terms, 'podcast')
33
+ applemusic(search_terms, "podcast")
34
34
  when /^art$/
35
- applemusic(search_terms, 'music', 'musicArtist')
35
+ applemusic(search_terms, "music", "musicArtist")
36
36
  when /^alb$/
37
- applemusic(search_terms, 'music', 'album')
37
+ applemusic(search_terms, "music", "album")
38
38
  when /^song$/
39
- applemusic(search_terms, 'music', 'musicTrack')
39
+ applemusic(search_terms, "music", "musicTrack")
40
40
  else
41
41
  applemusic(search_terms)
42
42
  end
@@ -45,7 +45,7 @@ module SL
45
45
 
46
46
  # {:type=>,:id=>,:url=>,:title=>}
47
47
  if otype == :embed && result[:type] =~ /(album|song)/
48
- url = 'embed'
48
+ url = "embed"
49
49
  if result[:type] =~ /song/
50
50
  link = %(https://embed.music.apple.com/#{SL.config['country_code'].downcase}/album/#{result[:album]}?i=#{result[:id]}&app=music#{SL.config['itunes_affiliate']})
51
51
  height = 150
@@ -60,7 +60,7 @@ module SL
60
60
  %(style="width:100%;max-width:660px;overflow:hidden;background:transparent;"),
61
61
  %(sandbox="allow-forms allow-popups allow-same-origin),
62
62
  %(allow-scripts allow-top-navigation-by-user-activation"></iframe>)
63
- ].join(' ')
63
+ ].join(" ")
64
64
  else
65
65
  url = result[:url]
66
66
  title = result[:title]
@@ -73,8 +73,8 @@ module SL
73
73
  # media => music, podcast
74
74
  # entity => optional: artist, song, album, podcast
75
75
  # returns {:type=>,:id=>,:url=>,:title}
76
- def applemusic(terms, media = 'music', entity = '')
77
- url = "http://itunes.apple.com/search?term=#{terms.url_encode}&country=#{SL.config['country_code']}&media=#{media}&entity=#{entity}"
76
+ def applemusic(terms, media = "music", entity = "")
77
+ url = "https://itunes.apple.com/search?term=#{terms.url_encode}&country=#{SL.config['country_code']}&media=#{media}&entity=#{entity}"
78
78
  page = Curl::Json.new(url, compressed: true, symbolize_names: true)
79
79
  json = page.json
80
80
  return false unless json[:resultCount]&.positive?
@@ -88,29 +88,29 @@ module SL
88
88
 
89
89
  def process_result(result)
90
90
  output = {}
91
- aff = SL.config['itunes_affiliate']
91
+ aff = SL.config["itunes_affiliate"]
92
92
 
93
93
  case result[:wrapperType]
94
- when 'track'
95
- if result[:kind] == 'podcast'
96
- output[:type] = 'podcast'
94
+ when "track"
95
+ if result[:kind] == "podcast"
96
+ output[:type] = "podcast"
97
97
  output[:id] = result[:collectionId]
98
98
  output[:url] = result[:collectionViewUrl].to_am + aff
99
99
  output[:title] = result[:collectionName]
100
100
  else
101
- output[:type] = 'song'
101
+ output[:type] = "song"
102
102
  output[:album] = result[:collectionId]
103
103
  output[:id] = result[:trackId]
104
104
  output[:url] = result[:trackViewUrl].to_am + aff
105
105
  output[:title] = "#{result[:trackName]} by #{result[:artistName]}"
106
106
  end
107
- when 'collection'
108
- output[:type] = 'album'
107
+ when "collection"
108
+ output[:type] = "album"
109
109
  output[:id] = result[:collectionId]
110
110
  output[:url] = result[:collectionViewUrl].to_am + aff
111
111
  output[:title] = "#{result[:collectionName]} by #{result[:artistName]}"
112
- when 'artist'
113
- output[:type] = 'artist'
112
+ when "artist"
113
+ output[:type] = "artist"
114
114
  output[:id] = result[:artistId]
115
115
  output[:url] = result[:artistLinkUrl].to_am + aff
116
116
  output[:title] = result[:artistName]
@@ -120,6 +120,6 @@ module SL
120
120
  end
121
121
  end
122
122
 
123
- SL::Searches.register 'applemusic', :search, self
123
+ SL::Searches.register "applemusic", :search, self
124
124
  end
125
125
  end
@@ -6,10 +6,10 @@ module SL
6
6
  class << self
7
7
  def settings
8
8
  {
9
- trigger: 'b(l|itly)',
9
+ trigger: "b(l|itly)",
10
10
  searches: [
11
- ['bl', 'bit.ly Shorten'],
12
- ['bitly', 'bit.ly shorten']
11
+ ["bl", "bit.ly Shorten"],
12
+ ["bitly", "bit.ly shorten"]
13
13
  ]
14
14
  }
15
15
  end
@@ -27,26 +27,26 @@ module SL
27
27
  end
28
28
 
29
29
  def bitly_shorten(url, title = nil)
30
- unless SL.config.key?('bitly_access_token') && !SL.config['bitly_access_token'].empty?
31
- SL.add_error('Bit.ly not configured', 'Missing access token')
30
+ unless SL.config.key?("bitly_access_token") && !SL.config["bitly_access_token"].empty?
31
+ SL.add_error("Bit.ly not configured", "Missing access token")
32
32
  return [false, title]
33
33
  end
34
34
 
35
- domain = SL.config.key?('bitly_domain') ? SL.config['bitly_domain'] : 'bit.ly'
35
+ domain = SL.config.key?("bitly_domain") ? SL.config["bitly_domain"] : "bit.ly"
36
36
  long_url = url.dup
37
- curl = TTY::Which.which('curl')
37
+ curl = TTY::Which.which("curl")
38
38
  cmd = [
39
39
  %(#{curl} -SsL -H 'Authorization: Bearer #{SL.config['bitly_access_token']}'),
40
40
  %(-H 'Content-Type: application/json'),
41
- '-X POST', %(-d '{ "long_url": "#{url}", "domain": "#{domain}" }'), 'https://api-ssl.bitly.com/v4/shorten'
41
+ "-X POST", %(-d '{ "long_url": "#{url}", "domain": "#{domain}" }'), "https://api-ssl.bitly.com/v4/shorten"
42
42
  ]
43
- data = JSON.parse(`#{cmd.join(' ')}`.strip)
44
- link = data['link']
43
+ data = JSON.parse(`#{cmd.join(" ")}`.strip)
44
+ link = data["link"]
45
45
  title ||= SL::URL.title(long_url)
46
46
  [link, title]
47
47
  end
48
48
  end
49
49
 
50
- SL::Searches.register 'bitly', :search, self
50
+ SL::Searches.register "bitly", :search, self
51
51
  end
52
52
  end
@@ -10,10 +10,10 @@ module SL
10
10
  #
11
11
  def settings
12
12
  {
13
- trigger: 'def(?:ine)?',
13
+ trigger: "def(?:ine)?",
14
14
  searches: [
15
- ['def', 'Dictionary Definition'],
16
- ['define', nil]
15
+ ["def", "Dictionary Definition"],
16
+ ["define", nil]
17
17
  ]
18
18
  }
19
19
  end
@@ -32,7 +32,7 @@ module SL
32
32
  fix = SL.spell(search_terms)
33
33
 
34
34
  if fix && search_terms.downcase != fix.downcase
35
- SL.add_error('Spelling', "Spelling altered for '#{search_terms}' to '#{fix}'")
35
+ SL.add_error("Spelling", "Spelling altered for '#{search_terms}' to '#{fix}'")
36
36
  search_terms = fix
37
37
  link_text = fix
38
38
  end
@@ -49,12 +49,12 @@ module SL
49
49
  #
50
50
  def define(terms)
51
51
  def_url = "https://www.wordnik.com/words/#{terms.url_encode}"
52
- curl = TTY::Which.which('curl')
52
+ curl = TTY::Which.which("curl")
53
53
  body = `#{curl} -sSL '#{def_url}'`
54
54
  if body =~ /id="define"/
55
55
  first_definition = body.match(%r{(?mi)(?:id="define"[\s\S]*?<li>)([\s\S]*?)</li>})[1]
56
56
  parts = first_definition.match(%r{<abbr title="partOfSpeech">(.*?)</abbr> (.*?)$})
57
- return [def_url, "(#{parts[1]}) #{parts[2]}".gsub(%r{</?.*?>}, '').strip]
57
+ return [def_url, "(#{parts[1]}) #{parts[2]}".gsub(%r{</?.*?>}, "").strip]
58
58
  end
59
59
 
60
60
  false
@@ -64,6 +64,6 @@ module SL
64
64
  end
65
65
 
66
66
  # Registers the search with the SL::Searches module
67
- SL::Searches.register 'definition', :search, self
67
+ SL::Searches.register "definition", :search, self
68
68
  end
69
69
  end
@@ -12,12 +12,12 @@ module SL
12
12
  #
13
13
  def settings
14
14
  {
15
- trigger: '(?:g|ddg|z|ddgimg)',
15
+ trigger: "(?:g|ddg|z|ddgimg)",
16
16
  searches: [
17
- ['g', 'Google/DuckDuckGo Search'],
18
- ['ddg', 'DuckDuckGo Search'],
19
- ['z', 'DDG Zero Click Search'],
20
- ['ddgimg', 'Return the first image from the destination page']
17
+ ["g", "Google/DuckDuckGo Search"],
18
+ ["ddg", "DuckDuckGo Search"],
19
+ ["z", "DDG Zero Click Search"],
20
+ ["ddgimg", "Return the first image from the destination page"]
21
21
  ]
22
22
  }
23
23
  end
@@ -41,13 +41,13 @@ module SL
41
41
  terms = "%5C#{search_terms.url_encode}"
42
42
  page = Curl::Html.new("https://duckduckgo.com/?q=#{terms}", compressed: true)
43
43
 
44
- locs = page.meta['refresh'].match(%r{/l/\?uddg=(.*?)$})
44
+ locs = page.meta["refresh"].match(%r{/l/\?uddg=(.*?)$})
45
45
  locs = page.body.match(%r{/l/\?uddg=(.*?)'}) if locs.nil?
46
46
  locs = page.body.match(/url=(.*?)'/) if locs.nil?
47
47
 
48
48
  return false if locs.nil?
49
49
 
50
- url = locs[1].url_decode.sub(/&rut=\w+/, '')
50
+ url = locs[1].url_decode.sub(/&rut=\w+/, "")
51
51
 
52
52
  result = url.strip.url_decode || false
53
53
  return false unless result
@@ -57,10 +57,10 @@ module SL
57
57
  # output_url = CGI.unescape(result)
58
58
  output_url = result
59
59
 
60
- output_title = if SL.config['include_titles'] || SL.titleize
61
- SL::URL.title(output_url) || ''
60
+ output_title = if SL.config["include_titles"] || SL.titleize
61
+ SL::URL.title(output_url) || ""
62
62
  else
63
- ''
63
+ ""
64
64
  end
65
65
 
66
66
  output_url = SL.first_image(output_url) if search_type =~ /img$/
@@ -83,11 +83,15 @@ module SL
83
83
  #
84
84
  def zero_click(search_terms, link_text, disambiguate: false)
85
85
  search_terms.gsub!(/%22/, '"')
86
- d = disambiguate ? '0' : '1'
87
- url = "http://api.duckduckgo.com/?q=#{search_terms.url_encode}&format=json&no_redirect=1&no_html=1&skip_disambig=#{d}"
88
- result = Curl::Json.new(url, symbolize_names: true).json
89
- return SL.ddg(terms, link_text) unless result
90
-
86
+ d = disambiguate ? "0" : "1"
87
+ url = "https://api.duckduckgo.com/?q=#{search_terms.url_encode}&format=json&no_redirect=1&no_html=1&skip_disambig=#{d}"
88
+ begin
89
+ result = Curl::Json.new(url, symbolize_names: true).json
90
+ return SL.ddg(terms, link_text) unless result
91
+ rescue StandardError => e
92
+ SL.add_error("Invalid response", "Search for #{terms}: (#{e})")
93
+ return false
94
+ end
91
95
  wiki_link = result[:AbstractURL] || result[:Redirect]
92
96
  title = result[:Heading] || false
93
97
 
@@ -107,7 +111,7 @@ module SL
107
111
  # @param type [Symbol] the type of search to
108
112
  # perform
109
113
  # @param klass [Class] the class to register
110
- SL::Searches.register 'duckduckgo', :search, self
114
+ SL::Searches.register "duckduckgo", :search, self
111
115
  end
112
116
  end
113
117
 
@@ -121,13 +125,13 @@ module SL
121
125
  # @param link_text [String] The link text
122
126
  # @param timeout [Integer] The timeout
123
127
  #
124
- def google(search_terms, link_text = nil, timeout: SL.config['timeout'], image: false)
128
+ def google(search_terms, link_text = nil, timeout: SL.config["timeout"], image: false)
125
129
  if SL::GoogleSearch.api_key?
126
- s_class = 'google'
127
- s_type = image ? 'img' : 'gg'
130
+ s_class = "google"
131
+ s_type = image ? "img" : "gg"
128
132
  else
129
- s_class = 'duckduckgo'
130
- s_type = image ? 'ddgimg' : 'g'
133
+ s_class = "duckduckgo"
134
+ s_type = image ? "ddgimg" : "g"
131
135
  end
132
136
  search = proc { SL::Searches.plugins[:search][s_class][:class].search(s_type, search_terms, link_text) }
133
137
  SL::Util.search_with_timeout(search, timeout)
@@ -144,13 +148,13 @@ module SL
144
148
  # @param image [Boolean] Image search
145
149
  # @return [SL::Searches::Result] The search result
146
150
  #
147
- def ddg(search_terms, link_text = nil, timeout: SL.config['timeout'], google: true, image: false)
151
+ def ddg(search_terms, link_text = nil, timeout: SL.config["timeout"], google: true, image: false)
148
152
  if google && SL::GoogleSearch.api_key?
149
- s_class = 'google'
150
- s_type = image ? 'img' : 'gg'
153
+ s_class = "google"
154
+ s_type = image ? "img" : "gg"
151
155
  else
152
- s_class = 'duckduckgo'
153
- s_type = image ? 'ddgimg' : 'g'
156
+ s_class = "duckduckgo"
157
+ s_type = image ? "ddgimg" : "g"
154
158
  end
155
159
 
156
160
  search = proc { SL::Searches.plugins[:search][s_class][:class].search(s_type, search_terms, link_text) }
@@ -170,7 +174,7 @@ module SL
170
174
 
171
175
  def first_image(url)
172
176
  images = Curl::Html.new(url).images
173
- images.filter { |img| img[:type] == 'img' }.first[:src]
177
+ images.filter { |img| img[:type] == "img" }.first[:src]
174
178
  end
175
179
  end
176
180
  end