Skip to content

Instantly share code, notes, and snippets.

@stutzman
Forked from tobi/kindle.rb
Created January 18, 2021 04:35
Show Gist options
  • Save stutzman/14ae40114684c51aef916e74807f94b0 to your computer and use it in GitHub Desktop.
Save stutzman/14ae40114684c51aef916e74807f94b0 to your computer and use it in GitHub Desktop.
Download your Kindle Highlights to local markdown files. Great for Obsidian.md.
#!/usr/bin/env ruby
# gem install active_support
require 'active_support/inflector'
require 'active_support/core_ext/string'
# gem install webrick (only ruby3)
require 'webrick'
# gem install mechanize
require 'mechanize'
# set this variable to https://read.amazon.ca if you are in Canada (or likewise elsewere)
root_url = ENV['KINDLE_URL'] || "https://read.amazon.com"
login = ENV['KINDLE_USER'].squish || raise("Need KINDLE_USER env var")
pass = ENV['KINDLE_PASS'] || raise("Need KINDLE_PASS env var")
output_dir = if ARGV[0].nil? or !File.directory?(ARGV[0])
STDERR.puts "#{File.basename(__FILE__)} [valid output directory]"
exit(1)
else
File.expand_path(ARGV[0])
end
m = Mechanize.new
m.user_agent_alias = 'Mac Safari'
m.cookie_jar.load("cookies.yaml") if File.exist?("cookies.yaml")
at_exit { m.cookie_jar.save("cookies.yaml", session: true) }
response = m.get("#{root_url}/notebook")
if form = response.form("signIn")
# attempt to login
form = response.form("signIn")
form.email = ENV['KINDLE_USER']
form.password = ENV['KINDLE_PASS']
response = m.submit(form, form.buttons.first)
end
if response.title != "Kindle: Your Notes and Highlights"
STDERR.puts "Unexpected page. Additional security or captcha? Check your phone."
file = File.open('/tmp/amazon.html', "w+") do |f|
f << response.body
end
STDERR.puts "Inspect: #{file.path}"
exit(1)
end
# Get all books
books = response.search("div#kp-notebook-library").children.select { |child| child.attributes["id"].present? }
# Write each out as formatted markdown file to
books.each do |book|
full_title = title = book.children.search("h2").first.text.squish
# If the book title has a colon in it we terminate the title there
title = $1 if title =~ /^([^\:]+)\:/
filename = title.strip.gsub(/[^\w]/, '_')
filename = title.parameterize(preserve_case: true, separator: '_')
puts "* writing #{title} to #{filename}.md"
File.open("#{output_dir}/#{filename}.md", "w+") do |f|
asin = book.attributes["id"].value.squish
author = book.children.search("p").first.text.split(":").last.strip.squish
# Write markdown header
f.puts "# #{full_title}"
f.puts " by [[#{author}]], asin: [#{asin}](kindle://book?action=openasin=#{asin})"
f.puts " #Book"
highlights = m.get("#{root_url}/kp/notebook?captcha_verified=1&asin=#{asin}&contentLimitState=&")
.search("div#kp-notebook-annotations").children
.select { |child| child.name == "div" }
.select { |child| child.children.search("div.kp-notebook-highlight").first.present? }
highlights.each do |highlight|
text = highlight.children.search("div.kp-notebook-highlight").first.text.squish
location = highlight.children.search("input#kp-annotation-location").first.attributes["value"].value
# Write markdown highlights
f.puts
f.puts "---"
f.print "#{text}"
f.puts " - location: [#{location}](kindle://book?action=open&location=#{location}&asin=#{asin})"
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment