kindle-highlights 1.0.2 → 2.0.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 8965c4c6a5c1a03d4995b880216b34bd7eaa7689
4
- data.tar.gz: 2b8d217f0bef63038fb22aea5a58b4aaf6cfa9f1
3
+ metadata.gz: e4ba7bab3c883c076439b271b56f106f8c03335d
4
+ data.tar.gz: f6f0cc0fbf202a3771a0f2ccc22fc9375ab1cc72
5
5
  SHA512:
6
- metadata.gz: 3071f361fb4c0d03daefbad09800cabae12638776ef1725df0dea8b7aac8d7ecffd33f8d43a8f3643a96051bd0a2bbe75210f3f91a11015528172b58e9c01277
7
- data.tar.gz: 71988265df7a3cb976dcd1dec09144bad05c4b3b3cc906c919bd39376763be02e9df262a92015d62ff510ecbdf4e2ae96f5e649969952910f0970431c73d52fe
6
+ metadata.gz: 72200505594865df64726113c90263a8033591126b5c7e74099e88b1576513522fa83a28dbd277b29de7789d3f12c21e34770e45e96638e49cdc8cca15835ba0
7
+ data.tar.gz: dbb58e9d60ff449202d95561eda889f9b283ac64248b42800f2d7373d5d6e20a168a45b07f93ffeaa175be89a613fddeff6e0b33b798e1efe6567fb58d11df52
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 [name of plugin creator]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,56 @@
1
+ module KindleHighlights
2
+ class Book
3
+ attr_accessor :asin, :author, :title
4
+
5
+ def self.from_html_elements(html_element:, mechanize_agent:)
6
+ new(
7
+ mechanize_agent: mechanize_agent,
8
+ asin: html_element.attributes["id"].value.squish,
9
+ title: html_element.children.search("h2").first.text.squish,
10
+ author: html_element.children.search("p").first.text.split(":").last.strip.squish
11
+ )
12
+ end
13
+
14
+ def initialize(asin:, author:, title:, mechanize_agent: nil)
15
+ @asin = asin
16
+ @author = author
17
+ @title = title
18
+ @mechanize_agent = mechanize_agent
19
+ end
20
+
21
+ def to_s
22
+ "#{title} by #{author}"
23
+ end
24
+
25
+ def inspect
26
+ "<#{self.class}: #{inspectable_vars}>"
27
+ end
28
+
29
+ def highlights_from_amazon
30
+ return [] unless mechanize_agent.present?
31
+
32
+ @highlights ||= fetch_highlights_from_amazon
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :mechanize_agent
38
+
39
+ def fetch_highlights_from_amazon
40
+ mechanize_agent
41
+ .get("https://read.amazon.com/kp/notebook?captcha_verified=1&asin=#{asin}&contentLimitState=&")
42
+ .search("div#kp-notebook-annotations")
43
+ .children
44
+ .select { |child| child.name == "div" }
45
+ .select { |child| child.children.search("div.kp-notebook-highlight").first.present? }
46
+ .map { |html_elements| Highlight.from_html_elements(book: self, html_elements: html_elements) }
47
+ end
48
+
49
+ def inspectable_vars
50
+ instance_variables
51
+ .select { |ivar| ivar != :@mechanize_agent }
52
+ .map { |ivar| "#{ivar}=#{instance_variable_get(ivar).inspect}" }
53
+ .join(", ")
54
+ end
55
+ end
56
+ end
@@ -2,14 +2,20 @@ module KindleHighlights
2
2
  class Client
3
3
  class CaptchaError < StandardError; end
4
4
  class AuthenticationError < StandardError; end
5
+ class AsinNotFoundError < StandardError; end
5
6
 
6
- attr_writer :mechanize_agent
7
- attr_accessor :kindle_logged_in_page
7
+ KINDLE_LOGIN_PAGE = "https://read.amazon.com/notebook"
8
+ SIGNIN_FORM_IDENTIFIER = "signIn"
9
+ MAX_AUTH_RETRIES = 2
10
+
11
+ attr_writer :mechanize_agent, :kindle_logged_in_page
8
12
 
9
13
  def initialize(email_address:, password:, mechanize_options: {})
10
- @email_address = email_address
11
- @password = password
14
+ @email_address = email_address
15
+ @password = password
12
16
  @mechanize_options = mechanize_options
17
+ @retries = 0
18
+ @kindle_logged_in_page = nil
13
19
  end
14
20
 
15
21
  def books
@@ -17,35 +23,46 @@ module KindleHighlights
17
23
  end
18
24
 
19
25
  def highlights_for(asin)
20
- conditionally_sign_in_to_amazon
26
+ if book = books.detect { |book| book.asin == asin }
27
+ book.highlights_from_amazon
28
+ else
29
+ raise AsinNotFoundError, "Book with ASIN #{asin} not found."
30
+ end
31
+ end
21
32
 
22
- cursor = 0
23
- highlights = []
33
+ private
24
34
 
25
- loop do
26
- # This endpoint includes a `hasMore` field. Unfortunately at the time of this writing is always `false`.
27
- page = mechanize_agent.get("https://kindle.amazon.com/kcw/highlights?asin=#{asin}&cursor=#{cursor}&count=#{BATCH_SIZE}")
28
- items = JSON.parse(page.body).fetch("items", [])
35
+ attr_accessor :email_address, :password, :mechanize_options
36
+ attr_reader :kindle_logged_in_page
29
37
 
30
- break unless items.any?
38
+ def mechanize_agent
39
+ @mechanize_agent ||= initialize_mechanize_agent
40
+ end
31
41
 
32
- highlights.concat(items)
33
- cursor += BATCH_SIZE
42
+ def initialize_mechanize_agent
43
+ mechanize_agent = Mechanize.new
44
+ mechanize_agent.user_agent_alias = Mechanize::AGENT_ALIASES.keys.grep(/\A(Linux|Mac|Windows)/).sample
45
+ mechanize_agent.agent.http.verify_mode = OpenSSL::SSL::VERIFY_NONE
46
+
47
+ mechanize_options.each do |mech_attr, value|
48
+ mechanize_agent.send("#{mech_attr}=", value)
34
49
  end
35
- highlights
50
+ mechanize_agent
36
51
  end
37
52
 
38
- private
53
+ def load_books_from_kindle_account
54
+ conditionally_sign_in_to_amazon
39
55
 
40
- attr_accessor :email_address, :password, :mechanize_options
56
+ kindle_library.map do |book|
57
+ unless book.attributes["id"].blank?
58
+ Book.from_html_elements(html_element: book, mechanize_agent: mechanize_agent)
59
+ end
60
+ end.compact
61
+ end
41
62
 
42
63
  def conditionally_sign_in_to_amazon
43
- if @kindle_logged_in_page.nil?
44
- signin_page = mechanize_agent.get(KINDLE_LOGIN_PAGE)
45
- signin_form = signin_page.form(SIGNIN_FORM_IDENTIFIER)
46
- signin_form.email = email_address
47
- signin_form.password = password
48
- post_signin_page = mechanize_agent.submit(signin_form)
64
+ if login?
65
+ post_signin_page = login_via_mechanize
49
66
 
50
67
  if post_signin_page.search("#ap_captcha_img").any?
51
68
  resolution_url = post_signin_page.link_with(text: /See a new challenge/).resolved_uri.to_s
@@ -57,41 +74,32 @@ module KindleHighlights
57
74
  @kindle_logged_in_page = post_signin_page
58
75
  end
59
76
  end
77
+ rescue AuthenticationError
78
+ retry unless too_many_retries?
60
79
  end
61
80
 
62
- def load_books_from_kindle_account
63
- conditionally_sign_in_to_amazon
64
-
65
- books = {}
66
- highlights_page = mechanize_agent.click(kindle_logged_in_page.link_with(text: /Your Books/))
67
-
68
- loop do
69
- highlights_page.search(".//td[@class='titleAndAuthor']").each do |book|
70
- asin_and_title_element = book.search("a").first
71
- asin = asin_and_title_element.attributes.fetch("href").value.split("/").last
72
- title = asin_and_title_element.inner_html
73
- books[asin] = title
74
- end
81
+ def kindle_library
82
+ @kindle_library ||= @kindle_logged_in_page.search("div#kp-notebook-library").children
83
+ end
75
84
 
76
- break if highlights_page.link_with(text: /Next/).nil?
77
- highlights_page = mechanize_agent.click(highlights_page.link_with(text: /Next/))
78
- end
79
- books
85
+ def login_via_mechanize
86
+ signin_page = mechanize_agent.get(KINDLE_LOGIN_PAGE)
87
+ signin_form = signin_page.form(SIGNIN_FORM_IDENTIFIER)
88
+ signin_form.email = email_address
89
+ signin_form.password = password
90
+ mechanize_agent.submit(signin_form)
80
91
  end
81
92
 
82
- def mechanize_agent
83
- @mechanize_agent ||= initialize_mechanize_agent
93
+ def login?
94
+ @kindle_logged_in_page.blank?
84
95
  end
85
96
 
86
- def initialize_mechanize_agent
87
- mechanize_agent = Mechanize.new
88
- mechanize_agent.user_agent_alias = 'Windows Mozilla'
89
- mechanize_agent.agent.http.verify_mode = OpenSSL::SSL::VERIFY_NONE
97
+ def too_many_retries?
98
+ retry! == MAX_AUTH_RETRIES
99
+ end
90
100
 
91
- mechanize_options.each do |mech_attr, value|
92
- mechanize_agent.send("#{mech_attr}=", value)
93
- end
94
- mechanize_agent
101
+ def retry!
102
+ retries += 1
95
103
  end
96
104
  end
97
105
  end
@@ -0,0 +1,23 @@
1
+ module KindleHighlights
2
+ class Highlight
3
+ attr_accessor :asin, :text, :location
4
+
5
+ def self.from_html_elements(book:, html_elements:)
6
+ new(
7
+ asin: book.asin,
8
+ text: html_elements.children.search("div.kp-notebook-highlight").first.text.squish,
9
+ location: html_elements.children.search("input#kp-annotation-location").first.attributes["value"].value,
10
+ )
11
+ end
12
+
13
+ def initialize(asin:, text:, location:)
14
+ @asin = asin
15
+ @text = text
16
+ @location = location
17
+ end
18
+
19
+ def to_s
20
+ text
21
+ end
22
+ end
23
+ end
@@ -1,10 +1,8 @@
1
1
  require 'rubygems'
2
2
  require 'mechanize'
3
- require 'json'
4
- require 'kindle_highlights/client'
3
+ require 'active_support/core_ext/object/blank'
4
+ require 'active_support/core_ext/string/filters'
5
5
 
6
- module KindleHighlights
7
- KINDLE_LOGIN_PAGE = "http://kindle.amazon.com/login"
8
- SIGNIN_FORM_IDENTIFIER = "signIn"
9
- BATCH_SIZE = 200
10
- end
6
+ require_relative './kindle_highlights/client'
7
+ require_relative './kindle_highlights/book'
8
+ require_relative './kindle_highlights/highlight'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kindle-highlights
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.2
4
+ version: 2.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Farkas
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-01-17 00:00:00.000000000 Z
11
+ date: 2017-11-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mechanize
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: 2.7.2
19
+ version: 2.7.5
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: 2.7.2
26
+ version: 2.7.5
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -66,14 +66,31 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '5.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: activesupport
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'
69
83
  description: Until there is a Kindle API, this will suffice.
70
84
  email: eric@prudentiadigital.com
71
85
  executables: []
72
86
  extensions: []
73
87
  extra_rdoc_files: []
74
88
  files:
89
+ - MIT-LICENSE
75
90
  - lib/kindle_highlights.rb
91
+ - lib/kindle_highlights/book.rb
76
92
  - lib/kindle_highlights/client.rb
93
+ - lib/kindle_highlights/highlight.rb
77
94
  homepage: https://github.com/speric/kindle-highlights
78
95
  licenses:
79
96
  - MIT
@@ -94,9 +111,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
111
  version: '0'
95
112
  requirements: []
96
113
  rubyforge_project:
97
- rubygems_version: 2.2.3
114
+ rubygems_version: 2.4.8
98
115
  signing_key:
99
116
  specification_version: 4
100
117
  summary: Kindle highlights
101
118
  test_files: []
102
- has_rdoc: