ebook_renamer 0.0.7 → 0.0.8

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: b0e4104daed6e556eb3241169fba5746f4feabcc
4
- data.tar.gz: 9fa908281368c84281e7d71e66ba7e7bf8586f73
3
+ metadata.gz: 5a2139b4a762320fd57c0f4fa7bc7a596fa9fe7e
4
+ data.tar.gz: 9e398f2a27d81053475b3fae18fe92b7de07d8d3
5
5
  SHA512:
6
- metadata.gz: a2733518e944cb0a833715fe645556a807b7f36a26340eb1a6f8df0be6d053e0412a81becd752c8dbdba8eb4c0049f06bbc6272545726f0bbb21ccbcfb5faff2
7
- data.tar.gz: 7c5694af153aead28f0a82857a4fda073d2b6a4f87a8a18b2328aff47830a0175f81199c826399a80949e6b7641c2a187d53d8623c940979c0b8b59f84e3c11b
6
+ metadata.gz: 48751c782e8240b4a539a04bbaed42235c0e620c016fa3c3f298874c7de6f7c4d4eae4cfd08afa56a0d45a6769a2dae7f127c369536d39ace8db3b3c3157c4da
7
+ data.tar.gz: c88f3706a48f84c46c4cb102e89502d6fe943d05fabe6e39baa1b16b1da909f665ec3193350b3195b6fb4cd16bb73284d15ac120cec61aae79fdfc477b7e3553
data/README.md CHANGED
@@ -5,15 +5,12 @@ the metadata within the ebook itself (if available).
5
5
 
6
6
  ### Why do I wrote this gem
7
7
 
8
- Almost always when I purchase an ebook (or download a file from the internet) it always came with the
9
- bad name. I really like the meaningful name to the file that I have and really hate to rename them manually.
8
+ Almost always, when I get an ebook it does not have a good file name.
9
+ This is very annoying as it does not work well when you want to search for it later.
10
10
 
11
- I wrote this gem just to make it easy to rename the files in bulk and recursively.
12
-
13
- This gem will rename any ebook files (pdf, epub, mobi) using the available
14
- meta-data that embedded within them.
15
-
16
- So if you don't like to spend time renaming them manually then this gem is for you.
11
+ I wrote this gem just to make it possible to rename multiple ebook files at once using the
12
+ available meta-data from each file. I like to be able to use this on the single folder or recursively from
13
+ a given folder.
17
14
 
18
15
  ### How the file is renamed
19
16
 
@@ -22,10 +19,12 @@ The file will be renamed using the following format `<title>.by.<author(s)>`.`<e
22
19
  Also the final file name will be sanitized e.g. any multiple occurence of special characters will be
23
20
  replace by one dot `.`.
24
21
 
25
- For example if the meta-data of the ebook contain the title `Start with Why: How Grate Leader Inspire Everyone to Take Action`
26
- and the author is `Simon Sinek` then the output will be `Start.with.Why.How.Great.Leader.Inspire.Everyone.to.Take.Action.by.Simon.Sinek.pdf`
22
+ For example if the ebook contain the title `Start with Why: How Grate Leader Inspire Everyone to Take Action`
23
+ and the author is `Simon Sinek` then the default output will be `Start.with.Why.How.Great.Leader.Inspire.Everyone.to.Take.Action.by.Simon.Sinek.pdf`
27
24
  Note that the `:` and one space before the word `How` is replaced with just one dot string.
28
25
 
26
+ if the `--sep-string` is used then the output will be `Start_with_Why_How_Great_Leader_Inspire_Everyone_to_Take_Action_by_Simon_Sinek.pdf` for `--sep-string _` argument.
27
+
29
28
  ### What you will need
30
29
 
31
30
  * You will need to install the [Calibre](http://www.calibre-ebook.com/) and
@@ -33,12 +32,6 @@ Note that the `:` and one space before the word `How` is replaced with just one
33
32
 
34
33
  * Linux or Mac OSX operating system
35
34
 
36
- ### Problems/Issues
37
-
38
- - Error may be raised if the combined meta-data is too long.
39
- e.g. if your files is stored in NTFS drive then you may get the error indicate the file is too long
40
- or something similar. I will try to handle this error in the next few release.
41
-
42
35
  ### Installation and Usage:
43
36
 
44
37
  ```sh
@@ -52,20 +45,20 @@ Run the following command from the directory that contain the file(s) that
52
45
  you want to rename.
53
46
 
54
47
  ```sh
55
- # cd to the directory containing the file you like to rename
56
- cd ~/Dropbox/ebooks/
48
+ # Cd to the directory containing the file(s) you like to rename
49
+ $cd ~/Dropbox/ebooks/
57
50
 
58
- # or specify the directory as an option
59
- ebook_renamer --base-dir ~/Dropbox/ebooks/samples
51
+ # Or specify the directory as an option from any where you have access to the gem
52
+ $ebook_renamer rename --base-dir ~/Dropbox/ebooks/samples
60
53
 
61
- # If you like to see the usage try
62
- ebook_renamer --help
54
+ # For help on how to use the gem just type without any options.
55
+ ebook_renamer
63
56
 
64
- # Run the command without making any changes to the files (dry-run)
65
- ebook_rename --recursive
57
+ # Run the command without making any changes to the files (dry-run) in the current directory
58
+ $ebook_renamer --base-dir . --recursive
66
59
 
67
- # Once you are happy with what you see, then
68
- ebook_renamer --recusive --commit
60
+ # Once you are happy with what result then
61
+ $ebook_renamer --base-dir . --recursive --commit
69
62
  ```
70
63
 
71
64
  ### Example Outputs
@@ -78,115 +71,80 @@ test/fixtures/
78
71
  ├── demo1.pdf
79
72
  ├── demo2.epub
80
73
  └── subdir
81
- ├── demo3.pdf
82
- └── demo4.epub
74
+ ├── demo1.pdf
75
+ └── demo2.epub
83
76
  2 directories, 4 files
84
77
  ```
85
78
 
86
79
  Run the command without making any changes
87
80
 
88
81
  ```sh
89
- ./bin/ebook_renamer --base-dir ./test/fixtures/ebooks --recursive
82
+ ./bin/ebook_renamer rename --base-dir ./test/fixtures/ebooks --recursive
90
83
  ```
91
84
 
92
85
  You should see the result like the following
93
86
 
94
87
  ```
95
- Your options: {:base_dir=>"test/fixtures/ebooks/", :meta_binary=>"ebook-meta", :recursive=>true, :commit=>false}
96
- Input :test/fixtures/ebooks/demo1.pdf
97
- Output:test/fixtures/ebooks/Fearless.Refactoring.by.Andrzej.Krzywda.pdf
98
- Input :test/fixtures/ebooks/demo2.epub
99
- Output:test/fixtures/ebooks/EPUB.3.0.Specification.by.EPUB.3.Working.Group.epub
100
- Input :test/fixtures/ebooks/subdir/demo3.pdf
101
- Output:test/fixtures/ebooks/subdir/Reliably.Deploying.Rails.Applications.by.Ben.Dixon.pdf
102
- Input :test/fixtures/ebooks/subdir/demo4.epub
103
- Output:test/fixtures/ebooks/subdir/EPUB.3.0.Specification.by.EPUB.3.Working.Group.epub
88
+ Your argument: {:base_dir=>".", :exts=>["pdf", "epub", "mobi"], :sep_string=>".", :commit=>false, :recursive=>true, :version=>false}
89
+ Changes will not be applied without the --commit option.
90
+ [./test/fixtures/ebooks/demo1.pdf] -> [./test/fixtures/ebooks/Fearless.Refactoring.by.Andrzej.Krzywda.pdf]
91
+ [./test/fixtures/ebooks/demo2.epub] -> [./test/fixtures/ebooks/EPUB.3.0.Specification.by.EPUB.3.Working.Group.epub]
92
+ [./test/fixtures/ebooks/subdir/demo1.pdf] -> [./test/fixtures/ebooks/subdir/Reliably.Deploying.Rails.Applications.by.Ben.Dixon.pdf]
93
+ [./test/fixtures/ebooks/subdir/demo2.epub] -> [./test/fixtures/ebooks/subdir/EPUB.3.0.Specification.by.EPUB.3.Working.Group.epub]
104
94
  ```
105
95
 
106
- To actually rename the files using the following command
96
+ To actually make the actual rename just pass in the `--commit` option like
107
97
 
108
98
  ```sh
109
- ./bin/ebook_renamer --base-dir ./test/fixtures/ebooks --recursive --commit
110
- # or short version
111
- ./bin/ebook_renamer -b ./test/fixtures/ebooks -r -c
99
+ ./bin/ebook_renamer rename --base-dir ./test/fixtures/ebooks --recursive --commit
100
+ ./bin/ebook_renamer rename -b ./test/fixtures/ebooks -r -c
112
101
  ```
113
102
 
114
- Note:
115
-
116
- The file will be renamed using the following format `<title>.by.<author(s)>`.`<extension>`
117
- Also the final file name will be sanitized e.g. any special characters will be
118
- converted to one dot `.`.
119
-
120
- For example if the title is `Start with Why: How Grate Leader Inspire Everyone to Take Action` and the author is `Simon Sinek` then
121
- the output will be something like `Start.with.Why.How.Great.Leader.Inspire.Everyone.to.Take.Action.by.Simon.Sinek.pdf` etc.
122
-
123
- ### Sample Usage (from `ebook_renamer --help`)
103
+ The final output should be like
124
104
 
125
105
  ```
126
- Usage: ebook_renamer [options]
127
-
128
- Examples:
129
-
130
- 1) $ebook_renamer
131
-
132
- 2) $ebook_renamer --base-dir ~/Dropbox/ebooks
133
-
134
- 3) $ebook_renamer --base-dir ~/Dropbox/ebooks
135
- --recursive
136
-
137
- 4) $ebook_renamer --base-dir ~/Dropbox/ebooks
138
- --recursive
139
-
140
- 5) $ebook_renamer --base-dir ~/Dropbox/ebooks
141
- --recursive
142
- --commit
143
-
144
- 6) $ebook_renamer --base-dir ~/Dropbox/ebooks
145
- --recursive
146
- --commit
106
+ test/fixtures/
107
+ └── ebooks
108
+ ├── Fearless.Refactoring.by.Andrzej.Krzywda.pdf
109
+ ├── EPUB.3.0.Specification.by.EPUB.3.Working.Group.epub
110
+ └── subdir
111
+ ├── Fearless.Refactoring.by.Andrzej.Krzywda.pdf
112
+ └── EPUB.3.0.Specification.by.EPUB.3.Working.Group.epub
113
+ 2 directories, 4 files
114
+ ```
147
115
 
148
- 7) $ebook_renamer --base-dir ~/Dropbox/ebooks
149
- --recursive
150
- --sep-string '_'
151
- --commit
152
- Options:
116
+ ### Sample Usage
153
117
 
154
- -b, --base-dir directory Starting directory [default - current directory]
155
- -s, --sep-string string Separator string [default - '.']
156
- -r, --recursive Process the files recursively [default - false]
157
- -c, --commit Perform the actual rename [default - false]
158
- -v, --version Display version number
159
- -h, --help Display this screen
118
+ ```
119
+ Usage:
120
+ ebook_renamer rename
121
+
122
+ Options:
123
+ -b, [--base-dir=BASE_DIR] # Base directory
124
+ # Default: /home/bchoomnuan/Dropbox/spikes/ebooks-renamer
125
+ -e, [--exts=one two three] # List of extensions to search for
126
+ # Default: ["pdf", "epub", "mobi"]
127
+ -s, [--sep-string=SEP_STRING] # Separator string between words in filename
128
+ # Default: .
129
+ -c, [--commit], [--no-commit] # Make change permanent
130
+ -r, [--recursive], [--no-recursive] # Search for files recursively
131
+ -v, [--version], [--no-version] # Display version information
132
+
133
+ Rename ebook files (pdf,epub,mobi)
160
134
  ```
161
135
 
162
136
  ### Changelog
163
137
 
164
- #### 0.0.7
165
-
166
- - More improvement and code cleanup
167
-
168
- #### 0.0.6
138
+ #### 0.0.8
169
139
 
170
- - Fix and update README.md
140
+ - Use Thor instead of OptionParser for parsing of options
171
141
 
172
- #### 0.0.5
142
+ #### 0.0.2 - 0.0.7
173
143
 
174
- - Update the README.md to include usage example
175
-
176
- #### 0.0.4
177
-
178
- - fix the stupid bug with the `which` method which raise invalid error
179
- - update README.md to reflect the change
180
-
181
- #### 0.0.3
182
-
183
- - make it possible to change the path to the binary
184
-
185
- #### 0.0.2
186
-
187
- - fix the silly refactoring bug
144
+ - Improvement of code and fix the few bugs a long the way
188
145
 
189
146
  #### 0.0.1
147
+
190
148
  - Initial release
191
149
 
192
150
  ### Contributing
data/Rakefile CHANGED
@@ -9,7 +9,6 @@ end
9
9
 
10
10
  task :default => :test
11
11
 
12
- ## see: http://erniemiller.org/2014/02/05/7-lines-every-gems-rakefile-should-have/
13
12
  task :irb do
14
13
  require 'irb'
15
14
  require 'awesome_print'
@@ -20,7 +19,6 @@ task :irb do
20
19
  IRB.start
21
20
  end
22
21
 
23
- ## see: http://lucapette.com/pry/pry-everywhere/
24
22
  task :pry do
25
23
  require 'pry'
26
24
  require 'awesome_print'
@@ -1,32 +1,8 @@
1
1
  #!/usr/bin/env ruby
2
- begin
3
- require 'ebook_renamer'
4
- rescue LoadError
5
- $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
6
- require 'ebook_renamer'
7
- end
8
-
2
+ require 'thor'
3
+ require 'pry'
4
+ require_relative '../lib/ebook_renamer'
5
+ require_relative '../lib/ebook_renamer/helper'
6
+ require_relative '../lib/ebook_renamer/version'
9
7
  include EbookRenamer
10
- include EbookRenamer::Options
11
- require 'ostruct'
12
-
13
- begin
14
- options = parse_options()
15
- EbookRenamer.logger.debug "Your options: #{options}"
16
-
17
- #meta_binary = options.delete(:meta_binary)
18
- sep_string = options.delete(:sep_string)
19
-
20
- EbookRenamer.configure do |config|
21
- #config.meta_binary = meta_binary if meta_binary
22
- config.sep_string = sep_string if sep_string
23
- end
24
-
25
- cli = EbookRenamer::CLI.new(EbookRenamer.configuration)
26
- cli.rename(options[:base_dir], options)
27
- exit 0
28
- rescue ArgumentError => e
29
- puts e
30
- exit 1
31
- end
32
- # vim: ft=ruby
8
+ EbookRenamer::Command.start(ARGV)
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
19
19
 
20
20
  spec.add_development_dependency "bundler", "~> 1.3"
21
21
  spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "thor"
22
23
 
23
24
  spec.add_development_dependency "minitest-spec-context", "~> 0.0.3"
24
25
  spec.add_development_dependency "guard-minitest", "~> 2.2"
@@ -1,6 +1,128 @@
1
- require 'ebook_renamer/version'
2
- require 'ebook_renamer/logger'
3
- require 'ebook_renamer/helpers'
4
- require 'ebook_renamer/configuration'
5
- require 'ebook_renamer/options'
6
- require 'ebook_renamer/cli'
1
+ require 'thor'
2
+ require 'ap'
3
+ module EbookRenamer
4
+ class Command < Thor
5
+ desc "rename", "Rename ebook files (pdf,epub,mobi)"
6
+
7
+ method_option :base_dir,
8
+ aliases: "-b",
9
+ desc: "Base directory",
10
+ default: Dir.pwd
11
+
12
+ method_option :exts,
13
+ aliases: "-e",
14
+ desc: "List of extensions to search for",
15
+ type: :array,
16
+ default: %w(pdf epub mobi)
17
+
18
+ method_option :sep_string,
19
+ aliases: "-s",
20
+ desc: "Separator string between words in filename",
21
+ default: "."
22
+
23
+ method_option :commit,
24
+ aliases: "-c",
25
+ desc: "Make change permanent",
26
+ type: :boolean,
27
+ default: false
28
+
29
+ method_option :recursive,
30
+ aliases: "-r",
31
+ desc: "Search for files recursively",
32
+ type: :boolean,
33
+ default: false
34
+
35
+ method_option :version,
36
+ aliases: "-v",
37
+ desc: "Display version information",
38
+ type: :boolean,
39
+ default: false
40
+
41
+ def rename
42
+ args = options.symbolize_keys
43
+ puts "Your argument: #{args}"
44
+
45
+ if options[:version]
46
+ puts EbookRenamer::VERSION
47
+ exit
48
+ end
49
+
50
+ execute(args)
51
+ end
52
+
53
+ desc "help", "Display help screen"
54
+ def help
55
+ puts <<-EOS
56
+ Usage:
57
+ ebook_renamer rename
58
+
59
+ Options:
60
+ -b, [--base-dir=BASE_DIR] # Base directory
61
+ # Default: /home/bchoomnuan/Dropbox/spikes/ebooks-renamer
62
+ -e, [--exts=one two three] # List of extensions to search for
63
+ # Default: ["pdf", "epub", "mobi"]
64
+ -s, [--sep-string=SEP_STRING] # Separator string between words in filename
65
+ # Default: .
66
+ -c, [--commit], [--no-commit] # Make change permanent
67
+ -r, [--recursive], [--no-recursive] # Search for files recursively
68
+ -v, [--version], [--no-version] # Display version information
69
+
70
+ Rename ebook files (pdf,epub,mobi)
71
+ EOS
72
+ end
73
+
74
+ default_task :help
75
+
76
+ private
77
+
78
+ # Rename the file from the given directory
79
+ # Using the with the argurment options as follow
80
+ #
81
+ # :base_dir - the base directory
82
+ # :recursive - perform the rename recursively (true|false)
83
+ # :commit - make the rename permanent (true|false)
84
+ # :exts - list of extensions to be processed default to ['epub,mobi,pdf']
85
+ #
86
+ # @param args [Hash<Symbol, Object>] options argument
87
+ def execute(args = {})
88
+ meta_binary = "ebook-meta"
89
+ options = default_options.merge(args)
90
+
91
+ input_files = files(options[:base_dir], options).sort
92
+
93
+ if input_files.empty?
94
+ puts "No file found for your option:", options
95
+ ap options
96
+ return
97
+ end
98
+
99
+ unless options[:commit]
100
+ puts "Changes will not be applied without the --commit option."
101
+ end
102
+
103
+ input_files.each do |file|
104
+ extension = File.extname(file)
105
+ begin
106
+ hash = meta_to_hash(meta(file, meta_binary))
107
+ full_name = formatted_name(hash, sep_char: " by ")
108
+ new_name = "#{File.dirname(file)}/#{sanitize_filename(full_name, options[:sep_string])}#{extension}"
109
+ puts "[#{file}] -> [#{new_name}]"
110
+ FileUtils.mv(file,new_name) if options[:commit] && file != new_name
111
+ rescue RuntimeError => e
112
+ puts e.backtrace
113
+ next
114
+ end
115
+ end
116
+ end
117
+
118
+ def default_options
119
+ options = {
120
+ base_dir: Dir.pwd,
121
+ recursive: false,
122
+ commit: false,
123
+ exts: %w(epub mobi pdf),
124
+ sep_string: '.'
125
+ }
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,36 @@
1
+ class Hash
2
+
3
+ # File activesupport/lib/active_support/core_ext/hash/keys.rb
4
+ #
5
+ # hash = { name: 'Rob', age: '28' }
6
+ # hash.transform_keys{ |key| key.to_s.upcase }
7
+ # => { "NAME" => "Rob", "AGE" => "28" }
8
+ def transform_keys
9
+ result = {}
10
+ each_key do |key|
11
+ result[yield(key)] = self[key]
12
+ end
13
+ result
14
+ end
15
+
16
+ def transform_keys!(&block)
17
+ keys.each do |key|
18
+ value = delete(key)
19
+ self[yield(key)] = value.is_a?(Hash) ? value.transform_keys!(&block) : value
20
+ end
21
+ self
22
+ end
23
+
24
+ # hash = { 'name' => 'Rob', 'age' => '28' }
25
+ # hash.symbolize_keys
26
+ # => { name: "Rob", age: "28" }
27
+ def symbolize_keys
28
+ transform_keys{ |key| key.to_sym rescue key }
29
+ end
30
+
31
+ # File activesupport/lib/active_support/core_ext/hash/keys.rb, line 135
32
+ def symbolize_keys!
33
+ transform_keys!{ |key| key.to_sym rescue key }
34
+ end
35
+
36
+ end
@@ -0,0 +1,168 @@
1
+ require 'open3'
2
+ require 'fileutils'
3
+ require 'shellwords'
4
+ require_relative "./core_ext/hash"
5
+ module EbookRenamer
6
+
7
+ EbookMetaNotInstall = Class.new(StandardError)
8
+
9
+ # Extract meta data from the input file using the ebook-meta tool
10
+ #
11
+ # @param [String] filename the input file name
12
+ # @param [String] binary the executable for use to extract the metadata
13
+ # @return [String] result of the output from running the command
14
+ def meta(filename, binary = 'ebook-meta')
15
+ raise EbookMetaNotInstall, "Need to install ebook-meta to use this gem" if which(binary).nil?
16
+ command = [
17
+ binary,
18
+ Shellwords.escape(filename)
19
+ ]
20
+
21
+ stdout_str, stderr_str, status = Open3.capture3(command.join(" "))
22
+ raise "Problem processing #{filename}" unless status.success?
23
+ stdout_str
24
+ end
25
+
26
+ # Convert the output string to hash
27
+ #
28
+ # @param [String] text output string from the 'ebook-meta' command
29
+ # @return [Hash<String,String>] hash pair for the input string
30
+ def meta_to_hash(text)
31
+ hash = {}
32
+ return hash if text.nil?
33
+ result_list = []
34
+
35
+ text.split(/\n/).each do |meta|
36
+ # split by the first ':' string
37
+ list = meta.split /^(.*?):/
38
+
39
+ # ignore the empty string element
40
+ list.delete_at(0)
41
+
42
+ unless list.empty?
43
+ list.map(&:strip!)
44
+ # downcase the first item to make it easy
45
+ result_list << [list[0].downcase, list[1]]
46
+ hash = Hash[*result_list.flatten]
47
+ end
48
+ end
49
+ hash
50
+ end
51
+
52
+ # Clean the filename to remove the special characters
53
+ #
54
+ # @param [String] filename input file
55
+ # @param [String] sep_char separator character to use
56
+ #
57
+ # @return [String] the new file name with special characters replaced or removed.
58
+ def sanitize_filename(filename, sep_char = nil)
59
+ dot = "."
60
+
61
+ # Note exclude the '.' (dot)
62
+ filename.gsub!(/[^0-9A-Za-z\-_ ]/, dot)
63
+
64
+ # replace multiple occurrences of a given char with a dot
65
+ ['-','_',' '].each do |c|
66
+ filename.gsub!(/#{Regexp.quote(c)}+/, dot)
67
+ end
68
+
69
+ # replace multiple occurrence of dot with one dot
70
+ filename.gsub!(/#{Regexp.quote(dot)}+/, dot)
71
+
72
+ if sep_char
73
+ # File.basename("demo.txt", ".*") #=> "demo"
74
+ name_only = File.basename(filename, ".*")
75
+ # File.extname("demo.txt") #=> ".txt"
76
+ ext_only = File.extname(filename)
77
+ name_only.gsub!(/#{Regexp.quote(dot)}+/, sep_char)
78
+ return "#{name_only}#{ext_only}"
79
+ end
80
+
81
+ filename.strip
82
+ end
83
+
84
+ # Cross-platform way of finding an executable in the $PATH.
85
+ #
86
+ # @param command [String] the command to look up
87
+ # @return [String, NilClass] full path to the executable file or nil if the
88
+ # executable is not valid or available.
89
+ # Example:
90
+ # which('ruby') #=> /usr/bin/ruby
91
+ # which('bad-executable') #=> nil
92
+ def which(command)
93
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
94
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
95
+ exts.each { |ext|
96
+ exe = File.join(path, "#{command}#{ext}")
97
+ return exe if File.executable? exe
98
+ }
99
+ end
100
+ return nil
101
+ end
102
+
103
+ # Return formatted file name using the metadata values
104
+ #
105
+ # @param [Hash<Symbol,String>] meta_hash output from the program 'ebook-meta' or 'exiftoo'
106
+ # @param [Hash<Symbol,String>] fields list of fields that will be used to set the name
107
+ def formatted_name(meta_hash = {}, fields = {})
108
+ if hash.nil? || fields.nil?
109
+ raise ArgumentError.new("Argument must not be nil")
110
+ end
111
+
112
+ # The keys that we get from the 'mdls' or 'exiftool'
113
+ args = {
114
+ keys: ['title',
115
+ 'author(s)'],
116
+ sep_char: ' '
117
+ }.merge(fields)
118
+
119
+ keys = args[:keys]
120
+ sep_char = args[:sep_char]
121
+
122
+ # Note: only show if we have the value for title
123
+ result = []
124
+ if meta_hash.fetch('title', nil)
125
+ keys.each do |k|
126
+ value = meta_hash.fetch(k, nil)
127
+ # Note: don't add 'Author(s)' => 'Unknown' to keep the result clean
128
+ if value && value.downcase != 'unknown'
129
+ result << meta_hash[k]
130
+ end
131
+ end
132
+ return result.join(sep_char)
133
+ end
134
+ # Note: if no title we choose to return empty value for result
135
+ return ""
136
+ end
137
+
138
+ # Ensure that the values in hash are sanitized
139
+ #
140
+ # @param [Hash<Symbol,String>] hash input hash to be sanitized
141
+ # @return [Hash<Symbol,String>] original hash with values sanitized
142
+ # @see #sanitize_filename
143
+ def sanitize_values(hash = {})
144
+ hash.each do |key, value|
145
+ hash[key] = sanitize_filename(value, " ")
146
+ end
147
+ hash
148
+ end
149
+
150
+ # List files base on given options
151
+ # options:
152
+ # :recursive - process the directory recursively (default false)
153
+ # :exts - list of extensions to be search (default ['epub','mobi','pdf'])
154
+ #
155
+ # @param base_dir [String] the starting directory
156
+ # @param options [Hash<Symbol,Object>] the options to be used
157
+ # @return [List<String>] list of matching files or empty list if none are found
158
+ def files(base_dir = Dir.pwd, options = {})
159
+ args = {
160
+ recursive: false,
161
+ exts: %w(epub mobi pdf)
162
+ }.merge(options)
163
+
164
+ wildcard = args[:recursive] ? '**' : ''
165
+ patterns = File.join(base_dir, wildcard, "*.{#{(args[:exts]).join(',')}}")
166
+ Dir.glob(patterns) || []
167
+ end
168
+ end
@@ -1,3 +1,3 @@
1
1
  module EbookRenamer
2
- VERSION = "0.0.7"
2
+ VERSION = "0.0.8"
3
3
  end
@@ -1,15 +1,15 @@
1
1
  require_relative '../../test_helper'
2
2
  describe EbookRenamer do
3
3
 
4
- subject { EbookRenamer::Helpers }
4
+ include EbookRenamer
5
5
 
6
6
  before do
7
- @sample = EbookRenamer::Helpers.meta("./test/fixtures/ebooks/demo1.pdf")
7
+ @sample = meta("./test/fixtures/ebooks/demo1.pdf")
8
8
  end
9
9
 
10
10
  context "#meta" do
11
11
  it "raises error on invalid input" do
12
- ->{ subject.meta("invalid-filename") }.must_raise RuntimeError
12
+ ->{ meta("invalid-filename") }.must_raise RuntimeError
13
13
  end
14
14
 
15
15
  it "returns valid valid input" do
@@ -20,16 +20,16 @@ describe EbookRenamer do
20
20
 
21
21
  context "#meta_to_hash" do
22
22
  it 'returns empty hash' do
23
- subject.meta_to_hash(nil).must_be_empty
23
+ meta_to_hash(nil).must_be_empty
24
24
  end
25
25
  it 'returns non-empty hash' do
26
- hash = EbookRenamer::Helpers.meta_to_hash(@sample)
26
+ hash = meta_to_hash(@sample)
27
27
  hash.wont_be_empty
28
28
  end
29
29
 
30
30
  describe 'invalid format' do
31
31
  it 'return empty hash' do
32
- subject.meta_to_hash('aa bb').must_equal({})
32
+ meta_to_hash('aa bb').must_equal({})
33
33
  end
34
34
  end
35
35
 
@@ -44,7 +44,7 @@ describe EbookRenamer do
44
44
  END
45
45
  }
46
46
 
47
- let(:result) { subject.meta_to_hash(sample) }
47
+ let(:result) { meta_to_hash(sample) }
48
48
 
49
49
  it "returns proper type for result" do
50
50
  result.must_be_instance_of Hash
@@ -62,31 +62,31 @@ describe EbookRenamer do
62
62
 
63
63
  context "#sanitize_filename" do
64
64
  it "must be defined" do
65
- subject.must_respond_to :sanitize_filename
65
+ must_respond_to :sanitize_filename
66
66
  end
67
67
 
68
68
  it "replaces multiple valid chars with one" do
69
- subject.sanitize_filename('Valid- -fil3_name......___ .txt').must_equal('Valid.fil3.name.txt')
69
+ sanitize_filename('Valid- -fil3_name......___ .txt').must_equal('Valid.fil3.name.txt')
70
70
  end
71
71
 
72
72
  it "replaces multiple valid chars with one" do
73
- subject.sanitize_filename('valid filename_.txt').must_equal('valid.filename.txt')
73
+ sanitize_filename('valid filename_.txt').must_equal('valid.filename.txt')
74
74
  end
75
75
 
76
76
  it "uses sepc_char correctly" do
77
- subject.sanitize_filename('valid.file name.txt','_').must_equal('valid_file_name.txt')
77
+ sanitize_filename('valid.file name.txt','_').must_equal('valid_file_name.txt')
78
78
  end
79
79
  end
80
80
 
81
81
  context '#which' do
82
82
  describe 'valid executable' do
83
83
  it 'works with valid executable' do
84
- subject.which('ruby').wont_be_nil
84
+ which('ruby').wont_be_nil
85
85
  end
86
86
  end
87
87
  describe 'invalid executable' do
88
88
  it 'works with invalid executable' do
89
- subject.which('@not-a-valid-executable!').must_be_nil
89
+ which('@not-a-valid-executable!').must_be_nil
90
90
  end
91
91
  end
92
92
  end
@@ -94,29 +94,29 @@ describe EbookRenamer do
94
94
  context '#formatted_name' do
95
95
  describe 'invalid parameters' do
96
96
  it 'raises exception on nil arguments' do
97
- -> { subject.formatted_name({}, nil)}.must_raise ArgumentError
97
+ -> { formatted_name({}, nil)}.must_raise ArgumentError
98
98
  end
99
99
  it 'returns nil on empty hash' do
100
- subject.formatted_name({}, {}).must_be_empty
100
+ formatted_name({}, {}).must_be_empty
101
101
  end
102
102
  end
103
103
 
104
104
  describe 'valid parameters' do
105
105
  it 'returns result based on single key' do
106
- subject.formatted_name({'title' => 'The Firm',
107
- 'author' => 'John Grisham',
108
- 'page count' => 399 },
109
- keys: ['title']).must_equal 'The Firm'
106
+ formatted_name({'title' => 'The Firm',
107
+ 'author' => 'John Grisham',
108
+ 'page count' => 399 },
109
+ keys: ['title']).must_equal 'The Firm'
110
110
  end
111
111
 
112
112
  it 'returns result based for multiple keys' do
113
- subject.formatted_name({'title' => 'The Firm',
114
- 'author' => 'John Grisham',
115
- 'page count' => '399' },
116
- sep_char: ':',
117
- keys: ['title',
118
- 'author',
119
- 'page count']).must_equal 'The Firm:John Grisham:399'
113
+ formatted_name({'title' => 'The Firm',
114
+ 'author' => 'John Grisham',
115
+ 'page count' => '399' },
116
+ sep_char: ':',
117
+ keys: ['title',
118
+ 'author',
119
+ 'page count']).must_equal 'The Firm:John Grisham:399'
120
120
  end
121
121
  end
122
122
  end
@@ -3,4 +3,4 @@ require 'minitest/pride'
3
3
  require 'minitest-spec-context'
4
4
  require 'awesome_print'
5
5
 
6
- require_relative '../lib/ebook_renamer'
6
+ require_relative '../lib/ebook_renamer/helper'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ebook_renamer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Burin Choomnuan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-04-06 00:00:00.000000000 Z
11
+ date: 2014-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: thor
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: minitest-spec-context
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -170,19 +184,15 @@ files:
170
184
  - bin/ebook_renamer
171
185
  - ebook_renamer.gemspec
172
186
  - lib/ebook_renamer.rb
173
- - lib/ebook_renamer/cli.rb
174
- - lib/ebook_renamer/configuration.rb
175
- - lib/ebook_renamer/helpers.rb
187
+ - lib/ebook_renamer/core_ext/hash.rb
188
+ - lib/ebook_renamer/helper.rb
176
189
  - lib/ebook_renamer/logger.rb
177
- - lib/ebook_renamer/options.rb
178
190
  - lib/ebook_renamer/version.rb
179
191
  - test/fixtures/ebooks/demo1.pdf
180
192
  - test/fixtures/ebooks/demo2.epub
181
193
  - test/fixtures/ebooks/subdir/demo3.pdf
182
194
  - test/fixtures/ebooks/subdir/demo4.epub
183
- - test/lib/ebook_renamer/configuration_test.rb
184
195
  - test/lib/ebook_renamer/helpers_test.rb
185
- - test/lib/ebook_renamer/version_test.rb
186
196
  - test/test_helper.rb
187
197
  homepage: https://github.com/agilecreativity/ebook_renamer
188
198
  licenses:
@@ -214,8 +224,6 @@ test_files:
214
224
  - test/fixtures/ebooks/demo2.epub
215
225
  - test/fixtures/ebooks/subdir/demo3.pdf
216
226
  - test/fixtures/ebooks/subdir/demo4.epub
217
- - test/lib/ebook_renamer/configuration_test.rb
218
227
  - test/lib/ebook_renamer/helpers_test.rb
219
- - test/lib/ebook_renamer/version_test.rb
220
228
  - test/test_helper.rb
221
229
  has_rdoc:
@@ -1,60 +0,0 @@
1
- module EbookRenamer
2
- class CLI
3
-
4
- attr_accessor :config
5
-
6
- # Constructor for the class
7
- #
8
- # @param [Configuration] config the configuration class
9
- def initialize(config = Configuration.new)
10
- @config = config
11
- end
12
-
13
- # Rename the file from the given directory
14
- # Using the with the argurment options as follow
15
- # :recursive - perform the rename recursively (true|false)
16
- # :commit - make the rename permanent (true|false)
17
- # :exts - list of extensions to be processed default to ['epub,mobi,pdf']
18
- #
19
- # @param base_dir [String] base directory default to current directory
20
- # @param args [Hash<Symbol, Object>] options argument
21
- def rename(base_dir = Dir.pwd, args = {})
22
- options = {
23
- recursive: false,
24
- commit: false,
25
- exts: %w(epub mobi pdf).join(",")
26
- }.merge(args)
27
-
28
- input_files = Helpers.files(base_dir, options).sort
29
-
30
- unless options[:commit]
31
- puts "Changes will not be applied without the --commit option."
32
- end
33
-
34
- input_files.each do |file|
35
- extension = File.extname(file)
36
- begin
37
- hash = Helpers.meta_to_hash(Helpers.meta(file, config.meta_binary))
38
- formatted_name = Helpers.formatted_name(hash, sep_char: " by ")
39
- formatted_name = "#{formatted_name}#{extension}"
40
- new_name = "#{File.dirname(file)}/#{Helpers.sanitize_filename(formatted_name, config.sep_string)}"
41
- puts "[#{file}] -> [#{new_name}]"
42
-
43
- # TODO: handle this properly!
44
- # skip if the filename is too long '228'
45
- # see: https://github.com/rails/rails/commit/ad95a61b62e70b839567c2e91e127fc2a1acb113
46
- max_allowed_file_size = 220
47
- if new_name && new_name.size > max_allowed_file_size
48
- puts "FYI: skip file name too long [#{new_name.size}] : #{new_name}"
49
- next
50
- end
51
-
52
- FileUtils.mv(file,new_name) if options[:commit] && file != new_name && new_name.size < max_allowed_file_size
53
- rescue RuntimeError => e
54
- puts e.backtrace
55
- next
56
- end
57
- end
58
- end
59
- end
60
- end
@@ -1,30 +0,0 @@
1
- module EbookRenamer
2
- class Configuration
3
- # the separator char for each word in the file name
4
- attr_accessor :meta_binary
5
-
6
- # the separator char for each word in the file name
7
- attr_accessor :sep_string
8
-
9
- def initialize
10
- @meta_binary = 'ebook-meta'
11
- @sep_string = '.'
12
- end
13
- end
14
-
15
- class << self
16
- attr_writer :configuration
17
-
18
- def configuration
19
- @configuration ||= Configuration.new
20
- end
21
-
22
- def reset
23
- @configuration = Configuration.new
24
- end
25
-
26
- def configure
27
- yield(configuration)
28
- end
29
- end
30
- end
@@ -1,175 +0,0 @@
1
- require 'open3'
2
- require 'fileutils'
3
- require 'shellwords'
4
-
5
- module EbookRenamer
6
- class Helpers
7
-
8
- EbookMetaNotInstall = Class.new(StandardError)
9
-
10
- class << self
11
-
12
- # Extract meta data from the input file using the ebook-meta tool
13
- #
14
- # @param [String] filename the input file name
15
- # @param [String] binary the executable for use to extract the metadata
16
- # @return [String] result of the output from running the command
17
- def meta(filename, binary = 'ebook-meta')
18
- raise EbookMetaNotInstall, "Need to install ebook-meta to use this gem" if which(binary).nil?
19
- command = [
20
- binary,
21
- Shellwords.escape(filename)
22
- ]
23
-
24
- stdout_str, stderr_str, status = Open3.capture3(command.join(" "))
25
- raise "Problem processing #{filename}" unless status.success?
26
- stdout_str
27
- end
28
-
29
- # Convert the output string to hash
30
- #
31
- # @param [String] text output string from the 'ebook-meta' command
32
- # @return [Hash<String,String>] hash pair for the input string
33
- def meta_to_hash(text)
34
- hash = {}
35
- return hash if text.nil?
36
- result_list = []
37
-
38
- text.split(/\n/).each do |meta|
39
- # split by the first ':' string
40
- list = meta.split /^(.*?):/
41
-
42
- # ignore the empty string element
43
- list.delete_at(0)
44
-
45
- unless list.empty?
46
- list.map(&:strip!)
47
- # downcase the first item to make it easy
48
- result_list << [list[0].downcase, list[1]]
49
- hash = Hash[*result_list.flatten]
50
- end
51
- end
52
- hash
53
- end
54
-
55
- # Clean the filename to remove the special characters
56
- #
57
- # @param [String] filename input file
58
- # @param [String] sep_char separator character to use
59
- #
60
- # @return [String] the new file name with special characters replaced or removed.
61
- def sanitize_filename(filename, sep_char = nil)
62
- dot = "."
63
-
64
- # Note exclude the '.' (dot)
65
- filename.gsub!(/[^0-9A-Za-z\-_ ]/, dot)
66
-
67
- # replace multiple occurrences of a given char with a dot
68
- ['-','_',' '].each do |c|
69
- filename.gsub!(/#{Regexp.quote(c)}+/, dot)
70
- end
71
-
72
- # replace multiple occurrence of dot with one dot
73
- filename.gsub!(/#{Regexp.quote(dot)}+/, dot)
74
-
75
- if sep_char
76
- # File.basename("demo.txt", ".*") #=> "demo"
77
- name_only = File.basename(filename, ".*")
78
- # File.extname("demo.txt") #=> ".txt"
79
- ext_only = File.extname(filename)
80
- name_only.gsub!(/#{Regexp.quote(dot)}+/, sep_char)
81
- return "#{name_only}#{ext_only}"
82
- end
83
-
84
- filename.strip
85
- end
86
-
87
- # Cross-platform way of finding an executable in the $PATH.
88
- #
89
- # @param command [String] the command to look up
90
- # @return [String, NilClass] full path to the executable file or nil if the
91
- # executable is not valid or available.
92
- # Example:
93
- # which('ruby') #=> /usr/bin/ruby
94
- # which('bad-executable') #=> nil
95
- def which(command)
96
- exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
97
- ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
98
- exts.each { |ext|
99
- exe = File.join(path, "#{command}#{ext}")
100
- return exe if File.executable? exe
101
- }
102
- end
103
- return nil
104
- end
105
-
106
- # Return formatted file name using the metadata values
107
- #
108
- # @param [Hash<Symbol,String>] meta_hash output from the program 'ebook-meta' or 'exiftoo'
109
- # @param [Hash<Symbol,String>] fields list of fields that will be used to set the name
110
- def formatted_name(meta_hash = {}, fields = {})
111
- if hash.nil? || fields.nil?
112
- raise ArgumentError.new("Argument must not be nil")
113
- end
114
-
115
- # The keys that we get from the 'mdls' or 'exiftool'
116
- args = {
117
- keys: ['title',
118
- 'author(s)'],
119
- sep_char: ' '
120
- }.merge(fields)
121
-
122
- keys = args[:keys]
123
- sep_char = args[:sep_char]
124
-
125
- # Note: only show if we have the value for title
126
- result = []
127
- if meta_hash.fetch('title', nil)
128
- keys.each do |k|
129
- value = meta_hash.fetch(k, nil)
130
- # Note: don't add 'Author(s)' => 'Unknown' to keep the result clean
131
- if value && value.downcase != 'unknown'
132
- result << meta_hash[k]
133
- end
134
- end
135
- return result.join(sep_char)
136
- end
137
- # Note: if no title we choose to return empty value for result
138
- return ""
139
- end
140
-
141
- # Ensure that the values in hash are sanitized
142
- #
143
- # @param [Hash<Symbol,String>] hash input hash to be sanitized
144
- # @return [Hash<Symbol,String>] original hash with values sanitized
145
- # @see #sanitize_filename
146
- def sanitize_values(hash = {})
147
- hash.each do |key, value|
148
- hash[key] = sanitize_filename(value, " ")
149
- end
150
- hash
151
- end
152
-
153
- # List files base on given options
154
- # options:
155
- # :recursive - process the directory recursively (default false)
156
- # :exts - list of extensions to be search (default ['epub','mobi','pdf'])
157
- #
158
- # @param base_dir [String] the starting directory
159
- # @param options [Hash<Symbol,Object>] the options to be used
160
- # @return [List<String>] list of matching files or empty list if none are found
161
- def files(base_dir = Dir.pwd, options = {})
162
- args = {
163
- recursive: false,
164
- exts: %w(epub mobi pdf).join(',')
165
- }.merge(options)
166
-
167
- raise ArgumentError.new("Invalid directory #{base_dir}") unless File.directory?(base_dir)
168
-
169
- wildcard = args[:recursive] ? '**' : ''
170
- patterns = File.join(base_dir, wildcard, "*.{#{args[:exts]}}")
171
- Dir.glob(patterns) || []
172
- end
173
- end
174
- end
175
- end
@@ -1,71 +0,0 @@
1
- require 'optparse'
2
-
3
- module EbookRenamer::Options
4
-
5
- def parse_options()
6
- options = {}
7
-
8
- option_parser = OptionParser.new do |opts|
9
- opts.banner = <<-END.gsub(/^\s+\|/, '')
10
- |
11
- | Usage: ebook_renamer [options]
12
- |
13
- | Examples:
14
- |
15
- | 1) $ebook_renamer
16
- |
17
- | 2) $ebook_renamer --base-dir ~/Dropbox/ebooks
18
- |
19
- | 3) $ebook_renamer --base-dir ~/Dropbox/ebooks
20
- | --recursive
21
- |
22
- | 4) $ebook_renamer --base-dir ~/Dropbox/ebooks
23
- | --recursive
24
- |
25
- | 5) $ebook_renamer --base-dir ~/Dropbox/ebooks
26
- | --recursive
27
- | --commit
28
- |
29
- | 6) $ebook_renamer --base-dir ~/Dropbox/ebooks
30
- | --recursive
31
- | --commit
32
- |
33
- | Options:
34
- |
35
- END
36
-
37
- options[:base_dir] ||= Dir.pwd
38
- opts.on('-b', '--base-dir directory', 'Starting directory [default - current directory]') do |base_dir|
39
- options[:base_dir] = base_dir
40
- end
41
-
42
- options[:sep_string] ||= '.'
43
- opts.on('-s', '--sep-string string', "Separator string [default - '.']") do |sep_string|
44
- options[:sep_string] = sep_string
45
- end
46
-
47
- options[:recursive] = false
48
- opts.on('-r', '--recursive', 'Process the files recursively [default - false]') do
49
- options[:recursive] = true
50
- end
51
-
52
- options[:commit] = false
53
- opts.on('-c', '--commit', 'Perform the actual rename [default - false]') do
54
- options[:commit] = true
55
- end
56
-
57
- opts.on('-v', '--version', 'Display version number') do
58
- puts EbookRenamer::VERSION
59
- exit 0
60
- end
61
-
62
- opts.on('-h', '--help', 'Display this screen') do
63
- puts opts
64
- exit 0
65
- end
66
- end
67
-
68
- option_parser.parse!
69
- options
70
- end
71
- end
@@ -1,22 +0,0 @@
1
- require_relative '../../test_helper'
2
- describe EbookRenamer do
3
-
4
- before :each do
5
- EbookRenamer.configure do |config|
6
- config.meta_binary = 'ebook-meta'
7
- end
8
- end
9
-
10
- after :each do
11
- EbookRenamer.reset
12
- end
13
-
14
- it "uses the updated configuration" do
15
- EbookRenamer.configuration.meta_binary.must_equal 'ebook-meta'
16
- config = EbookRenamer.configure do |config|
17
- config.meta_binary = "new-ebook-meta"
18
- end
19
- EbookRenamer.configuration.meta_binary.must_equal 'new-ebook-meta'
20
- end
21
-
22
- end
@@ -1,6 +0,0 @@
1
- require_relative '../../test_helper'
2
- describe EbookRenamer do
3
- it "must be defined" do
4
- EbookRenamer::VERSION.wont_be_nil
5
- end
6
- end