class KoboNotesBinder::Runner
Attributes
debug_mode[RW]
kobo_device_path[RW]
local_kobo_db_path[RW]
local_original_epub_path[RW]
tmp_dir[RW]
volume_url[RW]
Public Class Methods
new(opts = {}, kobo_device_path:)
click to toggle source
# File lib/kobo_notes_binder/runner.rb, line 21 def initialize(opts = {}, kobo_device_path:) @kobo_device_path = kobo_device_path @local_kobo_db_path = opts[:local_kobo_db_path] @local_original_epub_path = opts[:local_original_epub_path] @volume_url = opts[:volume_url] @tmp_dir = opts[:tmp_dir] @debug_mode = opts[:debug_mode] require "sqlite3" require "tty-prompt" require 'nokogiri' require 'fileutils' begin require 'pry-byebug' rescue LoadError end if tmp_dir.nil? puts 'Creating temporary folder' @tmp_dir = Dir.mktmpdir puts ' ' + tmp_dir at_exit do puts 'Cleaning up' FileUtils.remove_entry(tmp_dir) end end end
Public Instance Methods
compile(volume_url:, tmp_dir:, raw_epub_folder:)
click to toggle source
# File lib/kobo_notes_binder/runner.rb, line 252 def compile(volume_url:, tmp_dir:, raw_epub_folder:) epub_file = tmp_dir + '/' + File.basename(volume_url).sub('.', '.highlights.') files = *Dir[raw_epub_folder + '/*'] # move mimetype to be first mime_path = files.find { |path| File.basename(path) == 'mimetype' } files.delete mime_path files.unshift mime_path files = files.map { |path| Pathname.new(path).relative_path_from(raw_epub_folder).to_s } FileUtils.cd raw_epub_folder do system 'zip', '-rX', epub_file, *files end epub_file end
copy_book(book, dir:, kobo_device_path:)
click to toggle source
# File lib/kobo_notes_binder/runner.rb, line 127 def copy_book(book, dir:, kobo_device_path:) volume_url = book.ContentID file_path = dir + "/" + File.basename(volume_url) FileUtils.cp(kobo_device_path + volume_url.sub('file:///mnt/onboard', ''), file_path) file_path end
copy_db(kobo_device_path, dir:)
click to toggle source
# File lib/kobo_notes_binder/runner.rb, line 242 def copy_db(kobo_device_path, dir:) kobo_db_path = kobo_device_path + '/.kobo/KoboReader.sqlite' db_path = dir + '/KoboReader.sqlite' FileUtils.rm_rf db_path puts " Coping from " + kobo_db_path puts " Coping to " + db_path FileUtils.cp(kobo_db_path, db_path) db_path end
debug() { || ... }
click to toggle source
# File lib/kobo_notes_binder/runner.rb, line 271 def debug if debug_mode result = yield if result === String puts result end end end
execute()
click to toggle source
# File lib/kobo_notes_binder/runner.rb, line 50 def execute puts 'Device path ' + kobo_device_path if local_kobo_db_path.nil? puts 'Copying kobo database' local_kobo_db_path = copy_db kobo_device_path, dir: tmp_dir end puts 'Connecting to database' ActiveRecord::Base.establish_connection( adapter: 'sqlite3', database: local_kobo_db_path, ) if local_original_epub_path.nil? book = pick_book puts 'Copying selected book' local_original_epub_path = copy_book(book, dir: tmp_dir, kobo_device_path: kobo_device_path) end puts 'Extracting book content' raw_epub_folder = extract_epub(local_original_epub_path, dir: tmp_dir) volume_url ||= book.ContentID puts "Notes volume id: #{volume_url}" puts 'Searching for book notes' notes = Bookmark.where(VolumeID: volume_url) puts " Found #{notes.size} notes" if debug_mode puts 'Cloning raw folder' clone_raw_epub_folder = File.dirname(raw_epub_folder) + '/raw_original' FileUtils.rm_rf clone_raw_epub_folder FileUtils.cp_r raw_epub_folder, clone_raw_epub_folder end puts 'Embedding notes' notes.each do |note| process_note note, raw_epub_folder: raw_epub_folder end puts 'Binding book' new_epub_path = compile(volume_url: volume_url, tmp_dir: tmp_dir, raw_epub_folder: raw_epub_folder) puts 'Book is bound' new_epub_path end
extract_epub(path, dir:)
click to toggle source
# File lib/kobo_notes_binder/runner.rb, line 134 def extract_epub(path, dir:) raw_epub_folder = dir + '/raw' FileUtils.rm_rf raw_epub_folder system 'unzip', path, '-d', raw_epub_folder raw_epub_folder end
file_path(note)
click to toggle source
# File lib/kobo_notes_binder/runner.rb, line 99 def file_path(note) volume_url = note.VolumeID # /mnt/onboard/kepub/Title.kepub.epub!OEBPS!17_Chapter.xhtml volume_path = volume_url.sub('file://', '') content_id = note.ContentID path = content_id.sub volume_path, '' path.gsub('!', '/').split('#', 2)[0] end
highlight_node(node)
click to toggle source
# File lib/kobo_notes_binder/runner.rb, line 141 def highlight_node(node) # wrap only when has content if node.content[/\S/] node.wrap("<span style='background-color: #FDE383;' class='kobo-notes-binder-highlight'></span>") node.parent else node end end
pick_book()
click to toggle source
# File lib/kobo_notes_binder/runner.rb, line 111 def pick_book book_list = Content.joins(:bookmarks).group(:Title).order('Max(Bookmark.DateCreated) DESC') prompt = TTY::Prompt.new prompt.on(:keyescape) { exit } book = prompt.select( "What book would you like to export?", per_page: 7, filter: true ) do |menu| book_list.each do |book| menu.choice book.Title, book end end book end
process_note(note, raw_epub_folder:)
click to toggle source
# File lib/kobo_notes_binder/runner.rb, line 151 def process_note(note, raw_epub_folder:) debug do puts puts 'processing note: ' pp note nil end # Looks like `note.StartContainerChildIndex != -99` is for page bookmarks # We can skip them for now if note.StartContainerChildIndex != -99 || note.EndContainerChildIndex != -99 debug { ' Skipping note' } return end xml_path = raw_epub_folder + file_path(note) xml_string = File.read(xml_path) doc = Nokogiri::XML(xml_string) start_node = doc.at_css note.StartContainerPath end_node = doc.at_css note.EndContainerPath # Find parent that includes both start and end parent = start_node loop do match = parent.to_enum(:traverse).find do |node| node == end_node end break if match parent = parent.parent end nodes = parent.to_enum(:traverse).to_a selected_nodes = nodes[nodes.index(start_node)...nodes.index(end_node)] # Append start text node, as it will be skipped selected_nodes = start_node.to_enum(:traverse).to_a + selected_nodes selected_nodes.select(&:text?).each do |node| # special case when the note is duplicate and was already changed next if node.previous_sibling && node.previous_sibling['class'] == 'kobo-notes-binder-highlight' at_start = node.parent == start_node at_end = node.parent == end_node start_offset = note.StartOffset end_offset = note.EndOffset content = node.content if at_start && at_end text_before = content[0...start_offset] text_highlight = content[start_offset...end_offset] text_after = content[end_offset..-1] elsif at_start text_before = content[0...start_offset] text_highlight = content[start_offset..-1] text_after = '' elsif at_end text_before = '' text_highlight = content[0...end_offset] text_after = content[end_offset..-1] else text_before = '' text_highlight = content text_after = '' end if note.BookmarkID == 'ee31725b-d579-4a69-972c-f0bdb740bcb1' # binding.pry end node.content = text_highlight if text_highlight.size > 0 # need to be wrapped first, so sibling Text nodes won't be merged node = highlight_node(node) end if text_before.size > 0 node.add_previous_sibling doc.create_text_node(text_before) end if text_after.size > 0 node.add_next_sibling doc.create_text_node(text_after) end end File.write xml_path, doc.to_xhtml end