fyodor 0.2.8 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9df8b69482e73f4b9111105c9c9e02b5d36ada90536b393d322000e99e9bc7e
4
- data.tar.gz: 0070bfc112a33cea32020dfda7e7e033292f4083bc60e3487b854d66f857e206
3
+ metadata.gz: eeecee4acc8d86b2d4e21dc8071d5c1fa29ef5c7a7873f1c8c9cf0a693f2a9fb
4
+ data.tar.gz: ebd27239fbb6da6fb2fc601ed4138212ded181f135c42c99a4aff45046dcc802
5
5
  SHA512:
6
- metadata.gz: d35a3946e8c78f57c1a742a9e6ab35c5fa0948cccc88e479147b83f91956844b31afe7475c2a7a53a5856a90eab26c04a8c5a197f085f88f19bde774aa934407
7
- data.tar.gz: 4cbba9cd18529bc8e3a5d17a6236fa3ad30965655aca6387df759a11926bd578f373920bfb046751be757028989e39d330b19ab61d3909735dbf969a1693b633
6
+ metadata.gz: 41bd104a94086a9d42d99b85b99b17105013b38f2fa15f8623af86e64f7b648503748770edbec5d22681700b48f56b577c73c555a2b845f59f0d4b5d0b4590db
7
+ data.tar.gz: 06575a116669bbd2f1758ef772a5fbb091bfb22381f7cc44315c2f9973a7384174434c8a82443f1dc0ddef934a5f5fc2f49f53cb2caf5f9033897c0449843695
data/README.md CHANGED
@@ -8,7 +8,7 @@ This application parses `My Clippings.txt` from your Kindle and generates a mark
8
8
 
9
9
  [For samples of the output, click here.](docs/output_demo)
10
10
 
11
- To read more about the motivation and what problem it tries to solve, [check this blog post](https://rafaelc.org/tech/p/export-all-your-kindle-highlights-and-notes/).
11
+ To read more about the motivation and what problem it tries to solve, [check this blog post](https://rafaelc.org/posts/export-all-your-kindle-highlights-and-notes/).
12
12
 
13
13
  ## Features
14
14
 
@@ -18,10 +18,18 @@ To read more about the motivation and what problem it tries to solve, [check thi
18
18
  - Easily configurable for your language, allowing you to get all features and beautiful output.
19
19
  - This software goes some length to be locale agnostic: basic parsing should work without configuration for any language. It should also work even if your clippings file has multiple locales.
20
20
  - Bookmarks are printed together and notes are formatted differently, for better visualization.
21
- - Output in a format that is clean and easy to edit/fiddle around: markdown.
21
+ - By default, output in a format that is clean and easy to edit/fiddle around: markdown.
22
+ - Entirely customizable output, through templates, in the format you prefer.
22
23
 
23
24
  This program is based on the clippings file generated by Kindle 2019, but should work with other models.
24
25
 
26
+ ## Limitations
27
+
28
+ We are limited by the data Kindle makes available through `My Clippings.txt`. This means:
29
+
30
+ - We don't have chapter information.
31
+ - We can’t guess all entries that were deleted.
32
+
25
33
  ## Installation
26
34
 
27
35
  Install Ruby and run:
@@ -46,10 +54,10 @@ Fyodor has an optional configuration file, which is used for the following.
46
54
 
47
55
  If your Kindle is not in English, you should tell Fyodor how some things are called by your `My Clippings.txt` (e.g. highlights, pages, etc). _Fyodor should still work without configuration, but you won't take advantage of many features, resulting in a dirtier output._
48
56
 
49
- 1. Download the sample config to `~/.config/fyodor.toml` or `$XDG_CONFIG_HOME/fyodor.toml`:
57
+ 1. Download the sample config to `~/.config/fyodor/fyodor.toml` or `$XDG_CONFIG_HOME/fyodor/fyodor.toml`:
50
58
 
51
- ```
52
- $ wget https://raw.githubusercontent.com/rccavalcanti/fyodor/master/docs/fyodor.toml.sample -O ~/.config/fyodor.toml
59
+ ```sh
60
+ $ curl https://raw.githubusercontent.com/rc2dev/fyodor/master/docs/fyodor.toml.sample --create-dirs -o ~/.config/fyodor/fyodor.toml
53
61
  ```
54
62
 
55
63
  2. Open both the configuration and your `My Clippings.txt` in your preferred editor. Change the values in the `[parser]` section to mirror what you get in `My Clippings.txt`.
@@ -71,6 +79,23 @@ time = "Adicionado:"
71
79
 
72
80
  In the configuration file you can also set whether to print the time of each entry. On `[output]`, set `time` to `true` or `false`.
73
81
 
82
+ ### Templating
83
+
84
+ You may change the structure of the files output by Fyodor by providing your own template.
85
+
86
+ Simply place a ERB template at `~/.config/fyodor/template.erb` or `$XDG_CONFIG_HOME/fyodor/template.erb` and Fyodor will use it automatically.
87
+
88
+ The default template can be found [here](share/template.erb). You can use any method or attribute available [on this class](lib/fyodor/output_generator.rb).
89
+
90
+ ### Extension
91
+
92
+ If you want to change the extension of the output files - typically after changing the template -, set `extension` on `fyodor.toml`. For example, to change it to HTML:
93
+
94
+ ```toml
95
+ [output]
96
+ extension = "html"
97
+ ```
98
+
74
99
  ## Usage
75
100
 
76
101
  ```
@@ -98,4 +123,4 @@ If you like Fyodor, you can show your support here:
98
123
 
99
124
  Licensed under [GPLv3](LICENSE)
100
125
 
101
- Copyright (C) 2019-2020 [Rafael Cavalcanti](https://rafaelc.org/)
126
+ Copyright (C) 2019-2022 [Rafael Cavalcanti](https://rafaelc.org/dev)
data/lib/fyodor/book.rb CHANGED
@@ -26,8 +26,8 @@ module Fyodor
26
26
  end
27
27
 
28
28
  def basename
29
- base = @author.to_s.empty? ? @title : "#{@author} - #{@title}"
30
- base.strip.gsub(/[?*:|\/"<>]/,"_")
29
+ base = @author.to_s.empty? ? "Author N/A - #{@title}" : "#{@author} - #{@title}"
30
+ base.strip
31
31
  end
32
32
 
33
33
  def count_types
@@ -4,7 +4,11 @@ require "toml"
4
4
 
5
5
  module Fyodor
6
6
  class ConfigGetter
7
- DEFAULT = {
7
+ CONFIG_NAME = "fyodor.toml"
8
+ TEMPLATE_NAME = "template.erb"
9
+ DEFAULT_TEMPLATE_PATH = File.dirname(__FILE__) + "/../../share/template.erb"
10
+
11
+ DEFAULTS = {
8
12
  "parser" => {
9
13
  "highlight" => "Your Highlight",
10
14
  "note" => "Your Note",
@@ -15,7 +19,8 @@ module Fyodor
15
19
  "time" => "Added on"
16
20
  },
17
21
  "output" => {
18
- "time" => false
22
+ "time" => false,
23
+ "extension" => "md"
19
24
  }
20
25
  }
21
26
 
@@ -28,30 +33,52 @@ module Fyodor
28
33
 
29
34
  def get_config
30
35
  Hash.include CoreExtensions::Hash::Merging
31
- print_path
32
36
 
33
- user_config = path.nil? ? {} : TOML.load_file(path)
34
- DEFAULT.deep_merge(user_config)
37
+ @config_path = get_path(CONFIG_NAME)
38
+ print_config_path
39
+ user_config = @config_path.nil? ? {} : TOML.load_file(@config_path)
40
+
41
+ config = DEFAULTS.deep_merge(user_config)
42
+ config["output"]["template"] = template
43
+
44
+ config
35
45
  end
36
46
 
37
- def path
38
- @path ||= paths.find { |path| path.exist? }
47
+ def get_path(name)
48
+ possible_dirs.each do |d|
49
+ path = d + name
50
+ return path if path.exist?
51
+ end
52
+
53
+ return nil
39
54
  end
40
55
 
41
- def paths
42
- return @paths unless @paths.nil?
56
+ def possible_dirs
57
+ return @possible_dirs unless @possible_dirs.nil?
43
58
 
44
- @paths = []
45
- @paths << Pathname.new(ENV["XDG_CONFIG_HOME"]) + "fyodor.toml" unless ENV["XDG_CONFIG_HOME"].nil?
46
- @paths << Pathname.new("~/.config/fyodor.toml").expand_path
59
+ @possible_dirs = []
60
+ @possible_dirs << Pathname.new(ENV["XDG_CONFIG_HOME"]) + "fyodor" unless ENV["XDG_CONFIG_HOME"].nil?
61
+ @possible_dirs << Pathname.new("~/.config/fyodor").expand_path
47
62
  end
48
63
 
49
- def print_path
50
- if path.nil?
51
- puts "No config found: using defaults.\n\n"
64
+ def print_config_path
65
+ if @config_path.nil?
66
+ puts "No config found: using defaults.\n"
52
67
  else
53
- puts "Using config at #{path}\n\n"
68
+ puts "Using config at #{@config_path}.\n"
54
69
  end
55
70
  end
71
+
72
+ def template
73
+ template_path = get_path(TEMPLATE_NAME) || DEFAULT_TEMPLATE_PATH
74
+
75
+ if template_path == DEFAULT_TEMPLATE_PATH
76
+ puts "No template found: using default.\n\n"
77
+ else
78
+ puts "Using template at #{template_path}.\n\n"
79
+ end
80
+
81
+ File.read(template_path)
82
+ end
56
83
  end
57
84
  end
@@ -0,0 +1,72 @@
1
+ require "fyodor/strings"
2
+ require "erb"
3
+
4
+ module Fyodor
5
+ class OutputGenerator
6
+ include Strings
7
+
8
+ def initialize(book, config)
9
+ @book = book
10
+ @config = config
11
+ end
12
+
13
+ def content
14
+ ERB.new(@config["template"], nil, '-').result(binding)
15
+ end
16
+
17
+
18
+ private
19
+
20
+ def regular_entries
21
+ @book.reject { |entry| entry.type == Entry::TYPE[:bookmark] }
22
+ end
23
+
24
+ def bookmarks
25
+ @book.select { |entry| entry.type == Entry::TYPE[:bookmark] }
26
+ end
27
+
28
+ def render_entries(entries)
29
+ output = ""
30
+ entries.each do |entry|
31
+ output += "- #{item_text(entry)}\n\n"
32
+ output += " #{item_desc(entry)}\n\n" unless item_desc(entry).empty?
33
+ end
34
+ output.strip
35
+ end
36
+
37
+ def item_text(entry)
38
+ case entry.type
39
+ when Entry::TYPE[:bookmark]
40
+ "#{page(entry)}"
41
+ when Entry::TYPE[:note]
42
+ "_Note:_ #{entry.text.strip}"
43
+ else
44
+ "#{entry.text.strip}"
45
+ end
46
+ end
47
+
48
+ def item_desc(entry)
49
+ return entry.desc unless entry.desc_parsed?
50
+
51
+ case entry.type
52
+ when Entry::TYPE[:bookmark]
53
+ time(entry)
54
+ else
55
+ (type(entry) + " @ " + page(entry) + " " + time(entry)).strip
56
+ end
57
+ end
58
+
59
+ def page(entry)
60
+ ((entry.page.nil? ? "" : "page #{entry.page}, ") +
61
+ (entry.loc.nil? ? "" : "loc. #{entry.loc}")).delete_suffix(", ")
62
+ end
63
+
64
+ def time(entry)
65
+ @config["time"] ? "[#{entry.time}]" : ""
66
+ end
67
+
68
+ def type(entry)
69
+ SINGULAR[entry.type]
70
+ end
71
+ end
72
+ end
@@ -1,4 +1,4 @@
1
- require "fyodor/md_generator"
1
+ require "fyodor/output_generator"
2
2
 
3
3
  module Fyodor
4
4
  class OutputWriter
@@ -12,7 +12,7 @@ module Fyodor
12
12
  def write_all
13
13
  puts "\nWriting to #{@output_dir}..." unless @library.empty?
14
14
  @library.each do |book|
15
- content = MdGenerator.new(book, @config).content
15
+ content = OutputGenerator.new(book, @config).content
16
16
  File.open(path(book), "w") { |f| f.puts(content) }
17
17
  end
18
18
  end
@@ -21,11 +21,13 @@ module Fyodor
21
21
  private
22
22
 
23
23
  def path(book)
24
- path = @output_dir + "#{book.basename}.md"
24
+ basename = book.basename.gsub(/[?*:|\/"<>]/,"_")
25
+ extension = @config["extension"]
26
+ path = @output_dir + "#{basename}.#{extension}"
25
27
 
26
28
  i = 2
27
29
  while(path.exist?)
28
- path = @output_dir + "#{book.basename} - #{i}.md"
30
+ path = @output_dir + "${basename - #{i}.#{extension}"
29
31
  i += 1
30
32
  end
31
33
 
@@ -1,3 +1,3 @@
1
1
  module Fyodor
2
- VERSION = "0.2.8".freeze
2
+ VERSION = "0.3.1".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fyodor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rafael Cavalcanti
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-22 00:00:00.000000000 Z
11
+ date: 2022-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: toml
@@ -39,7 +39,7 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.0'
41
41
  description: Parse Kindle clippings into markdown files
42
- email: code@rafaelc.org
42
+ email: dev@rafaelc.org
43
43
  executables:
44
44
  - fyodor
45
45
  extensions: []
@@ -56,16 +56,16 @@ files:
56
56
  - lib/fyodor/entry.rb
57
57
  - lib/fyodor/entry_parser.rb
58
58
  - lib/fyodor/library.rb
59
- - lib/fyodor/md_generator.rb
59
+ - lib/fyodor/output_generator.rb
60
60
  - lib/fyodor/output_writer.rb
61
61
  - lib/fyodor/stats_printer.rb
62
62
  - lib/fyodor/strings.rb
63
63
  - lib/fyodor/version.rb
64
- homepage: https://github.com/rccavalcanti/fyodor
64
+ homepage: https://rafaelc.org/fyodor
65
65
  licenses:
66
66
  - GPL-3.0-only
67
67
  metadata:
68
- source_code_uri: https://github.com/rccavalcanti/fyodor
68
+ source_code_uri: https://rafaelc.org/fyodor
69
69
  post_install_message:
70
70
  rdoc_options: []
71
71
  require_paths:
@@ -1,96 +0,0 @@
1
- require "fyodor/strings"
2
-
3
- module Fyodor
4
- class MdGenerator
5
- include Strings
6
-
7
- def initialize(book, config)
8
- @book = book
9
- @config = config
10
- end
11
-
12
- def content
13
- header + body + bookmarks
14
- end
15
-
16
-
17
- private
18
-
19
- def header
20
- return <<~EOF
21
- # #{@book.title}
22
- #{"by #{@book.author}" unless @book.author.to_s.empty?}
23
-
24
- #{header_counts}
25
-
26
- EOF
27
- end
28
-
29
- def header_counts
30
- output = ""
31
- @book.count_types.each do |type, n|
32
- output += "#{n} #{pluralize(type, n)}, " if n > 0
33
- end
34
- output.delete_suffix(", ")
35
- end
36
-
37
- def pluralize(type, n)
38
- n == 1 ? SINGULAR[type] : PLURAL[type]
39
- end
40
-
41
- def body
42
- entries = @book.reject { |entry| entry.type == Entry::TYPE[:bookmark] }
43
- entries.size == 0 ? "" : entries_render(entries)
44
- end
45
-
46
- def bookmarks
47
- bookmarks = @book.select { |entry| entry.type == Entry::TYPE[:bookmark] }
48
- bookmarks.size == 0 ? "" : entries_render(bookmarks, "Bookmarks")
49
- end
50
-
51
- def entries_render(entries, title=nil)
52
- output = "---\n\n"
53
- output += "## #{title}\n\n" unless title.nil?
54
- entries.each do |entry|
55
- output += "#{item_text(entry)}\n\n"
56
- output += "<p style=\"text-align: right;\"><sup>#{item_desc(entry)}</sup></p>\n\n"
57
- end
58
- output
59
- end
60
-
61
- def item_text(entry)
62
- case entry.type
63
- when Entry::TYPE[:bookmark]
64
- "* #{page(entry)}"
65
- when Entry::TYPE[:note]
66
- "* _Note:_\n#{entry.text.strip}"
67
- else
68
- "* #{entry.text.strip}"
69
- end
70
- end
71
-
72
- def item_desc(entry)
73
- return entry.desc unless entry.desc_parsed?
74
-
75
- case entry.type
76
- when Entry::TYPE[:bookmark]
77
- time(entry)
78
- else
79
- (type(entry) + " @ " + page(entry) + " " + time(entry)).strip
80
- end
81
- end
82
-
83
- def page(entry)
84
- ((entry.page.nil? ? "" : "page #{entry.page}, ") +
85
- (entry.loc.nil? ? "" : "loc. #{entry.loc}")).delete_suffix(", ")
86
- end
87
-
88
- def time(entry)
89
- @config["time"] ? "[#{entry.time}]" : ""
90
- end
91
-
92
- def type(entry)
93
- SINGULAR[entry.type]
94
- end
95
- end
96
- end