ebook_renamer 0.0.7 → 0.0.8

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
  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