ebooks_renamer 0.0.1
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.
- data/.gitignore +21 -0
- data/.rubocop.yml +3 -0
- data/Gemfile +4 -0
- data/LICENSE +22 -0
- data/README.md +119 -0
- data/Rakefile +29 -0
- data/bin/ebooks_renamer +4 -0
- data/ebooks_renamer.gemspec +42 -0
- data/lib/ebooks_renamer.rb +7 -0
- data/lib/ebooks_renamer/cli.rb +70 -0
- data/lib/ebooks_renamer/core_ext/hash/keys.rb +55 -0
- data/lib/ebooks_renamer/core_ext/object/blank.rb +105 -0
- data/lib/ebooks_renamer/ebooks_renamer.rb +80 -0
- data/lib/ebooks_renamer/epub_parser.rb +19 -0
- data/lib/ebooks_renamer/logger.rb +10 -0
- data/lib/ebooks_renamer/mobi_parser.rb +12 -0
- data/lib/ebooks_renamer/pdf_parser.rb +17 -0
- data/lib/ebooks_renamer/version.rb +3 -0
- data/rubocop-todo.yml +92 -0
- metadata +341 -0
data/.gitignore
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
# Custom generated files
|
19
|
+
sample.pdf
|
20
|
+
sample.epub
|
21
|
+
sample.mobi
|
data/.rubocop.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Burin Choomnuan
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,119 @@
|
|
1
|
+
## EbooksRenamer
|
2
|
+
|
3
|
+
[](http://badge.fury.io/rb/ebooks_renamer)
|
4
|
+
[](https://gemnasium.com/agilecreativity/ebooks_renamer)
|
5
|
+
[](https://codeclimate.com/github/agilecreativity/ebooks_renamer)
|
6
|
+
|
7
|
+
Rename multiple ebook files using the power of ruby.
|
8
|
+
This is the alternate version of my [ebook_renamer][] gem that implemented in
|
9
|
+
pure ruby using the power of other gems for metadata extraction.
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
- Rename multiple ebooks at once currently `epub`, `mobi` and `pdf` are supported.
|
14
|
+
|
15
|
+
- Use of the following information as part of the file
|
16
|
+
|
17
|
+
* title - common for (epub, pdf, mobi)
|
18
|
+
* author - common for (epub, pdf, mobi)
|
19
|
+
* total pages - currently only pdf file will have this metadata information
|
20
|
+
* publisher - common for (mobi, epub)
|
21
|
+
|
22
|
+
The output file will be something like (if all of the metadata are available)
|
23
|
+
|
24
|
+
```
|
25
|
+
# if '_' is the `--sep-string` is '_'
|
26
|
+
<title>_by_<author>_<publisher>_<total_pages>_pages.extension
|
27
|
+
|
28
|
+
# The minimal possible final file name will be
|
29
|
+
<title>.extension
|
30
|
+
```
|
31
|
+
|
32
|
+
If the title is not available then no action will be performed.
|
33
|
+
|
34
|
+
- Use feature of other ruby gems to extract metadata
|
35
|
+
* [pdf-reader][] gem to extract pdf metadata
|
36
|
+
* [mobi][] gem to extract mobi metadata
|
37
|
+
* [epubinfo][] gem to extract epub metadat
|
38
|
+
|
39
|
+
- Sanitized file name if the metadata contain invalid/special characters.
|
40
|
+
|
41
|
+
- If the metadata is not available then the rename will not be performed
|
42
|
+
|
43
|
+
- If the result name is the same as the original name then no rename is performed.
|
44
|
+
|
45
|
+
### Installation
|
46
|
+
|
47
|
+
Or install it yourself as:
|
48
|
+
|
49
|
+
$ gem install ebooks_renamer
|
50
|
+
|
51
|
+
### Usage
|
52
|
+
- For help and usage just type `ebooks_renamer` without any options
|
53
|
+
|
54
|
+
```
|
55
|
+
Usage:
|
56
|
+
ebooks_renamer rename
|
57
|
+
|
58
|
+
Options:
|
59
|
+
-b, [--base-dir=BASE_DIR] # Base directory
|
60
|
+
# Default: . (current directory)
|
61
|
+
-n, [--inc-words=one two three] # List of words to be included in the result if any
|
62
|
+
-x, [--exc-words=one two three] # List of words to be excluded from the result if any
|
63
|
+
-i, [--ignore-case], [--no-ignore-case] # Match case insensitively
|
64
|
+
# Default: true
|
65
|
+
-r, [--recursive], [--no-recursive] # Search for files recursively
|
66
|
+
# Default: true
|
67
|
+
-v, [--version], [--no-version] # Display version information
|
68
|
+
-s, [--sep-string=SEP_STRING] # Separator string between words in filename
|
69
|
+
# Default: .
|
70
|
+
-c, [--commit], [--no-commit] # Make change permanent
|
71
|
+
|
72
|
+
Rename ebooks based on given criteria
|
73
|
+
```
|
74
|
+
|
75
|
+
### Usage Example:
|
76
|
+
|
77
|
+
Here is your typical usage of the gem
|
78
|
+
|
79
|
+
```shell
|
80
|
+
# change to the directory that contain your ebook files
|
81
|
+
cd ~/Dropbox/ebooks
|
82
|
+
|
83
|
+
# set version of your ruby
|
84
|
+
rbenv local 2.1.1 # if you are using rbenv as your ruby manager
|
85
|
+
|
86
|
+
# install the gem
|
87
|
+
gem install ebooks_renamer
|
88
|
+
|
89
|
+
# run the command without making any changes
|
90
|
+
ebooks_renamer rename --base-dir . --recursive
|
91
|
+
|
92
|
+
# If you are happy with what will be changed then you can make your change permanent
|
93
|
+
|
94
|
+
ebooks_renamer rename --base-dir . --recursive --commit
|
95
|
+
|
96
|
+
# To change the default separator string `sep_string` (default to '.' - dot string)
|
97
|
+
# e.g. this will use the '_' (underscore) to separate each word in the output
|
98
|
+
ebooks_renamer rename --base-dir . --sep-string '_' --recursive --commit
|
99
|
+
|
100
|
+
# To only include/exclude certain files in the result you can use the
|
101
|
+
# `--exc-words` or `--exc-words` etc
|
102
|
+
# e.g. The following will perform the rename only on the files that contain the word 'android' or 'iphone'
|
103
|
+
# in the title.
|
104
|
+
|
105
|
+
ebooks_renamer rename --base-dir . --sep-string '-' --recursive --inc-words android iphone --commit
|
106
|
+
```
|
107
|
+
|
108
|
+
### Contributing
|
109
|
+
|
110
|
+
1. Fork it
|
111
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
112
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
113
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
114
|
+
5. Create new Pull Request
|
115
|
+
|
116
|
+
[ebook_renamer]: https://github.com/agilecreativity/filename_cleaner
|
117
|
+
[mobi]: https://rubygems.org/gems/mobi
|
118
|
+
[pdf-reader]: https://rubygems.org/gems/pdf-reader
|
119
|
+
[epubinfo]: https://rubygems.org/gems/epubinfo
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
3
|
+
project_name = 'ebooks_renamer'
|
4
|
+
|
5
|
+
Rake::TestTask.new do |t|
|
6
|
+
t.libs << "lib/#{project_name}"
|
7
|
+
t.test_files = FileList["test/lib/#{project_name}/test_*.rb"]
|
8
|
+
t.verbose = true
|
9
|
+
end
|
10
|
+
|
11
|
+
task default: [:test, :rubocop]
|
12
|
+
task :pry do
|
13
|
+
require 'pry'
|
14
|
+
require 'awesome_print'
|
15
|
+
require_relative 'lib/ebook_meta'
|
16
|
+
include EbooksRenamer
|
17
|
+
ARGV.clear
|
18
|
+
Pry.start
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rubocop/rake_task'
|
22
|
+
desc 'Run RuboCop on the lib directory'
|
23
|
+
Rubocop::RakeTask.new(:rubocop) do |task|
|
24
|
+
task.patterns = ['lib/**/*.rb']
|
25
|
+
# only show the files with failures
|
26
|
+
task.formatters = ['files']
|
27
|
+
# don't abort rake on failure
|
28
|
+
task.fail_on_error = false
|
29
|
+
end
|
data/bin/ebooks_renamer
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'ebooks_renamer/version'
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'ebooks_renamer'
|
7
|
+
spec.version = EbooksRenamer::VERSION
|
8
|
+
spec.authors = ['Burin Choomnuan']
|
9
|
+
spec.email = ['agilecreativity@gmail.com']
|
10
|
+
spec.summary = %q{Rename multiple ebook files (pdf, epub, mobi) using embedded metadata when available}
|
11
|
+
spec.description = %q{Rename multiple ebook files (pdf, epub, mobi) using embedded metadata if available}
|
12
|
+
spec.homepage = 'https://github.com/agilecreativity/ebooks_renamer'
|
13
|
+
spec.license = 'MIT'
|
14
|
+
spec.files = Dir.glob("{bin,lib,templates}/**/*") + %w(Gemfile
|
15
|
+
Rakefile
|
16
|
+
ebooks_renamer.gemspec
|
17
|
+
README.md
|
18
|
+
LICENSE
|
19
|
+
.rubocop.yml
|
20
|
+
.gitignore
|
21
|
+
rubocop-todo.yml)
|
22
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
23
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
24
|
+
spec.require_paths = ['lib']
|
25
|
+
spec.add_runtime_dependency 'thor', '~> 0.19'
|
26
|
+
spec.add_runtime_dependency 'pdf-reader', '~> 1.3.3'
|
27
|
+
spec.add_runtime_dependency 'mobi', '~> 1.0.0'
|
28
|
+
spec.add_runtime_dependency 'epubinfo', '~> 0.4.3'
|
29
|
+
spec.add_runtime_dependency 'filename_cleaner', '~> 0.0.3'
|
30
|
+
spec.add_runtime_dependency 'code_lister', '~> 0.0.8'
|
31
|
+
spec.add_development_dependency 'awesome_print', '~> 1.2'
|
32
|
+
spec.add_development_dependency 'bundler', '~> 1.5'
|
33
|
+
spec.add_development_dependency 'gem-ctags', '~> 1.0'
|
34
|
+
spec.add_development_dependency 'guard', '~> 2.6'
|
35
|
+
spec.add_development_dependency 'guard-minitest', '~> 2.2'
|
36
|
+
spec.add_development_dependency 'minitest', '~> 5.3'
|
37
|
+
spec.add_development_dependency 'minitest-spec-context', '~> 0.0.3'
|
38
|
+
spec.add_development_dependency 'pry', '~> 0.9'
|
39
|
+
spec.add_development_dependency 'rake', '~> 10.1'
|
40
|
+
spec.add_development_dependency 'rubocop', '~> 0.20'
|
41
|
+
spec.add_development_dependency 'yard', '~> 0.8'
|
42
|
+
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
require_relative 'ebooks_renamer/version'
|
2
|
+
require_relative 'ebooks_renamer/logger'
|
3
|
+
require_relative 'ebooks_renamer/cli'
|
4
|
+
require_relative 'ebooks_renamer/core_ext/hash/keys'
|
5
|
+
require_relative 'ebooks_renamer/core_ext/object/blank'
|
6
|
+
require_relative 'ebooks_renamer/ebooks_renamer'
|
7
|
+
include EbooksRenamer
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'agile_utils'
|
3
|
+
require_relative 'ebooks_renamer'
|
4
|
+
module EbooksRenamer
|
5
|
+
class CLI < Thor
|
6
|
+
desc 'rename', 'Rename ebooks based on given criteria'
|
7
|
+
method_option *AgileUtils::Options::BASE_DIR
|
8
|
+
method_option *AgileUtils::Options::INC_WORDS
|
9
|
+
method_option *AgileUtils::Options::EXC_WORDS
|
10
|
+
method_option *AgileUtils::Options::IGNORE_CASE
|
11
|
+
method_option *AgileUtils::Options::RECURSIVE
|
12
|
+
method_option *AgileUtils::Options::VERSION
|
13
|
+
|
14
|
+
method_option :sep_string,
|
15
|
+
aliases: "-s",
|
16
|
+
desc: "Separator string between words in filename",
|
17
|
+
default: "."
|
18
|
+
|
19
|
+
method_option :commit,
|
20
|
+
aliases: "-c",
|
21
|
+
desc: "Make change permanent",
|
22
|
+
type: :boolean,
|
23
|
+
default: false
|
24
|
+
|
25
|
+
def rename
|
26
|
+
opts = options.symbolize_keys
|
27
|
+
if opts[:version]
|
28
|
+
puts "You are using EbooksRenamer version #{EbooksRenamer::VERSION}"
|
29
|
+
exit
|
30
|
+
end
|
31
|
+
# Add the default supported extensions
|
32
|
+
opts.merge!(exts: %w(pdf epub mobi))
|
33
|
+
puts "Your options #{opts}"
|
34
|
+
EbooksRenamer.rename(opts)
|
35
|
+
end
|
36
|
+
|
37
|
+
desc 'usage', 'Display help screen'
|
38
|
+
def usage
|
39
|
+
puts <<-EOS
|
40
|
+
|
41
|
+
Usage:
|
42
|
+
ebooks_renamer rename
|
43
|
+
|
44
|
+
Options:
|
45
|
+
-b, [--base-dir=BASE_DIR] # Base directory
|
46
|
+
# Default: . (current directory)
|
47
|
+
-n, [--inc-words=one two three] # List of words to be included in the result if any
|
48
|
+
-x, [--exc-words=one two three] # List of words to be excluded from the result if any
|
49
|
+
-i, [--ignore-case], [--no-ignore-case] # Match case insensitively
|
50
|
+
# Default: true
|
51
|
+
-r, [--recursive], [--no-recursive] # Search for files recursively
|
52
|
+
# Default: true
|
53
|
+
-v, [--version], [--no-version] # Display version information
|
54
|
+
-s, [--sep-string=SEP_STRING] # Separator string between words in filename
|
55
|
+
# Default: .
|
56
|
+
-c, [--commit], [--no-commit] # Make change permanent
|
57
|
+
|
58
|
+
Rename ebooks based on given criteria
|
59
|
+
EOS
|
60
|
+
end
|
61
|
+
|
62
|
+
default_task :usage
|
63
|
+
|
64
|
+
# private
|
65
|
+
#
|
66
|
+
# def process(opts = {})
|
67
|
+
# puts "Your options #{opts}"
|
68
|
+
# end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,55 @@
|
|
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
|
+
# Merges the caller into +other_hash+. For example,
|
37
|
+
#
|
38
|
+
# options = options.reverse_merge(size: 25, velocity: 10)
|
39
|
+
#
|
40
|
+
# is equivalent to
|
41
|
+
#
|
42
|
+
# options = { size: 25, velocity: 10 }.merge(options)
|
43
|
+
#
|
44
|
+
# This is particularly useful for initializing an options hash
|
45
|
+
# with default values.
|
46
|
+
def reverse_merge(other_hash)
|
47
|
+
other_hash.merge(self)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Destructive +reverse_merge+.
|
51
|
+
def reverse_merge!(other_hash)
|
52
|
+
# right wins if there is no left
|
53
|
+
merge!(other_hash) { |key,left,right| left }
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
# from: active_support/core_ext/object/blank.rb
|
3
|
+
class Object
|
4
|
+
# An object is blank if it's false, empty, or a whitespace string.
|
5
|
+
# For example, '', ' ', +nil+, [], and {} are all blank.
|
6
|
+
#
|
7
|
+
# This simplifies:
|
8
|
+
#
|
9
|
+
# if address.nil? || address.empty?
|
10
|
+
#
|
11
|
+
# ...to:
|
12
|
+
#
|
13
|
+
# if address.blank?
|
14
|
+
def blank?
|
15
|
+
respond_to?(:empty?) ? empty? : !self
|
16
|
+
end
|
17
|
+
|
18
|
+
# An object is present if it's not <tt>blank?</tt>.
|
19
|
+
def present?
|
20
|
+
!blank?
|
21
|
+
end
|
22
|
+
|
23
|
+
# Returns object if it's <tt>present?</tt> otherwise returns +nil+.
|
24
|
+
# <tt>object.presence</tt> is equivalent to <tt>object.present? ? object : nil</tt>.
|
25
|
+
#
|
26
|
+
# This is handy for any representation of objects where blank is the same
|
27
|
+
# as not present at all. For example, this simplifies a common check for
|
28
|
+
# HTTP POST/query parameters:
|
29
|
+
#
|
30
|
+
# state = params[:state] if params[:state].present?
|
31
|
+
# country = params[:country] if params[:country].present?
|
32
|
+
# region = state || country || 'US'
|
33
|
+
#
|
34
|
+
# ...becomes:
|
35
|
+
#
|
36
|
+
# region = params[:state].presence || params[:country].presence || 'US'
|
37
|
+
def presence
|
38
|
+
self if present?
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class NilClass
|
43
|
+
# +nil+ is blank:
|
44
|
+
#
|
45
|
+
# nil.blank? # => true
|
46
|
+
def blank?
|
47
|
+
true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class FalseClass
|
52
|
+
# +false+ is blank:
|
53
|
+
#
|
54
|
+
# false.blank? # => true
|
55
|
+
def blank?
|
56
|
+
true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class TrueClass
|
61
|
+
# +true+ is not blank:
|
62
|
+
#
|
63
|
+
# true.blank? # => false
|
64
|
+
def blank?
|
65
|
+
false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class Array
|
70
|
+
# An array is blank if it's empty:
|
71
|
+
#
|
72
|
+
# [].blank? # => true
|
73
|
+
# [1,2,3].blank? # => false
|
74
|
+
alias_method :blank?, :empty?
|
75
|
+
end
|
76
|
+
|
77
|
+
class Hash
|
78
|
+
# A hash is blank if it's empty:
|
79
|
+
#
|
80
|
+
# {}.blank? # => true
|
81
|
+
# { key: 'value' }.blank? # => false
|
82
|
+
alias_method :blank?, :empty?
|
83
|
+
end
|
84
|
+
|
85
|
+
class String
|
86
|
+
# A string is blank if it's empty or contains whitespaces only:
|
87
|
+
#
|
88
|
+
# ''.blank? # => true
|
89
|
+
# ' '.blank? # => true
|
90
|
+
# ' '.blank? # => true
|
91
|
+
# ' something here '.blank? # => false
|
92
|
+
def blank?
|
93
|
+
self !~ /[^[:space:]]/
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class Numeric #:nodoc:
|
98
|
+
# No number is blank:
|
99
|
+
#
|
100
|
+
# 1.blank? # => false
|
101
|
+
# 0.blank? # => false
|
102
|
+
def blank?
|
103
|
+
false
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'mobi'
|
2
|
+
require 'epubinfo'
|
3
|
+
require 'pdf-reader'
|
4
|
+
require 'ostruct'
|
5
|
+
require 'fileutils'
|
6
|
+
require 'filename_cleaner'
|
7
|
+
require 'code_lister'
|
8
|
+
require_relative 'pdf_parser'
|
9
|
+
require_relative 'epub_parser'
|
10
|
+
require_relative 'mobi_parser'
|
11
|
+
module EbooksRenamer
|
12
|
+
CustomError = Class.new(StandardError)
|
13
|
+
class << self
|
14
|
+
def rename(options = {})
|
15
|
+
files = CodeLister.files(options)
|
16
|
+
files.each_with_index do |file, index|
|
17
|
+
puts "Process file #{index + 1} of #{files.length} : #{file}"
|
18
|
+
new_name = formatted_name(file, options[:sep_string])
|
19
|
+
if file != new_name
|
20
|
+
puts "Old name: '#{file}'"
|
21
|
+
puts "New name: '#{new_name}'"
|
22
|
+
if options[:commit]
|
23
|
+
FileUtils.mv(file, new_name)
|
24
|
+
end
|
25
|
+
else
|
26
|
+
puts "File #{file} is identical so no action taken."
|
27
|
+
end
|
28
|
+
end
|
29
|
+
unless options[:commit]
|
30
|
+
puts '------------------------------------------------------------------'
|
31
|
+
puts 'This is a dry run only, to actually rename please specify --commit'
|
32
|
+
puts '------------------------------------------------------------------'
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def formatted_name(file, sep_string)
|
39
|
+
meta = parse(file)
|
40
|
+
if meta && !meta.title.blank?
|
41
|
+
name = meta.title
|
42
|
+
name += " by #{meta.author}" unless meta.author.blank?
|
43
|
+
name += " #{meta.publisher}" unless meta.publisher.blank?
|
44
|
+
name += " #{meta.pages} pages" unless meta.pages.blank?
|
45
|
+
# return the sanitized file name with full path
|
46
|
+
[File.dirname(file),
|
47
|
+
File::SEPARATOR,
|
48
|
+
FilenameCleaner.sanitize_filename(name, sep_string),
|
49
|
+
File.extname(file)].join('')
|
50
|
+
else
|
51
|
+
# return the full path of the original file
|
52
|
+
File.expand_path(file)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Parser
|
57
|
+
attr_reader :parser
|
58
|
+
def initialize(parser)
|
59
|
+
@parser = parser
|
60
|
+
end
|
61
|
+
|
62
|
+
def parse(filename)
|
63
|
+
@parser.parse(filename)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def parse(filename)
|
68
|
+
case File.extname(filename)
|
69
|
+
when '.epub'
|
70
|
+
Parser.new(EpubParser.parse(filename)).parser
|
71
|
+
when '.pdf'
|
72
|
+
Parser.new(PdfParser.parse(filename)).parser
|
73
|
+
when '.mobi'
|
74
|
+
Parser.new(MobiParser.parse(filename)).parser
|
75
|
+
else
|
76
|
+
fail "File type #{File.extname(file)} is not supported"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module EbooksRenamer
|
2
|
+
class EpubParser
|
3
|
+
class << self
|
4
|
+
def parse(filename)
|
5
|
+
info = EPUBInfo.get(filename)
|
6
|
+
if info.present?
|
7
|
+
title = info.titles.first if info.titles
|
8
|
+
author = info.creators.map(&:name).first if info.creators
|
9
|
+
publisher = info.publisher
|
10
|
+
if title.present?
|
11
|
+
OpenStruct.new title: title,
|
12
|
+
author: author,
|
13
|
+
publisher: publisher
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'pry'
|
2
|
+
module EbooksRenamer
|
3
|
+
class PdfParser
|
4
|
+
class << self
|
5
|
+
def parse(filename)
|
6
|
+
File.open(filename, 'rb') do |io|
|
7
|
+
reader = PDF::Reader.new(io)
|
8
|
+
if reader && reader.info && reader.info[:Title].present?
|
9
|
+
OpenStruct.new title: reader.info[:Title],
|
10
|
+
author: reader.info[:Author],
|
11
|
+
pages: reader.page_count
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
data/rubocop-todo.yml
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
# This configuration was generated by `rubocop --auto-gen-config`
|
2
|
+
# on 2014-04-23 16:41:01 +1000 using RuboCop version 0.19.1.
|
3
|
+
# The point is for the user to remove these configuration records
|
4
|
+
# one by one as the offenses are removed from the code base.
|
5
|
+
# Note that changes in the inspected code, or installation of new
|
6
|
+
# versions of RuboCop, may require this file to be generated again.
|
7
|
+
|
8
|
+
# Offense count: 2
|
9
|
+
# Cop supports --auto-correct.
|
10
|
+
DeprecatedClassMethods:
|
11
|
+
Enabled: false
|
12
|
+
|
13
|
+
# Offense count: 9
|
14
|
+
Documentation:
|
15
|
+
Enabled: false
|
16
|
+
|
17
|
+
# Offense count: 6
|
18
|
+
# Cop supports --auto-correct.
|
19
|
+
EmptyLinesAroundBody:
|
20
|
+
Enabled: false
|
21
|
+
|
22
|
+
# Offense count: 1
|
23
|
+
Eval:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
# Offense count: 2
|
27
|
+
# Configuration parameters: Exclude.
|
28
|
+
FileName:
|
29
|
+
Enabled: false
|
30
|
+
|
31
|
+
# Offense count: 1
|
32
|
+
# Cop supports --auto-correct.
|
33
|
+
IndentationConsistency:
|
34
|
+
Enabled: true
|
35
|
+
|
36
|
+
# Offense count: 2
|
37
|
+
# Cop supports --auto-correct.
|
38
|
+
LeadingCommentSpace:
|
39
|
+
Enabled: false
|
40
|
+
|
41
|
+
# Offense count: 5
|
42
|
+
LineLength:
|
43
|
+
Max: 89
|
44
|
+
|
45
|
+
# Offense count: 1
|
46
|
+
# Configuration parameters: CountComments.
|
47
|
+
MethodLength:
|
48
|
+
Max: 12
|
49
|
+
|
50
|
+
# Offense count: 2
|
51
|
+
# Configuration parameters: NamePrefixBlacklist.
|
52
|
+
PredicateName:
|
53
|
+
Enabled: false
|
54
|
+
|
55
|
+
# Offense count: 1
|
56
|
+
RedundantBegin:
|
57
|
+
Enabled: true
|
58
|
+
|
59
|
+
# Offense count: 1
|
60
|
+
RescueException:
|
61
|
+
Enabled: false
|
62
|
+
|
63
|
+
# Offense count: 2
|
64
|
+
RescueModifier:
|
65
|
+
Enabled: false
|
66
|
+
|
67
|
+
# Offense count: 2
|
68
|
+
# Cop supports --auto-correct.
|
69
|
+
SpaceAfterComma:
|
70
|
+
Enabled: false
|
71
|
+
|
72
|
+
# Offense count: 2
|
73
|
+
# Cop supports --auto-correct.
|
74
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
75
|
+
SpaceBeforeBlockBraces:
|
76
|
+
Enabled: false
|
77
|
+
|
78
|
+
# Offense count: 1
|
79
|
+
# Cop supports --auto-correct.
|
80
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SpaceBeforeBlockParameters.
|
81
|
+
SpaceInsideBlockBraces:
|
82
|
+
Enabled: true
|
83
|
+
|
84
|
+
# Offense count: 16
|
85
|
+
# Cop supports --auto-correct.
|
86
|
+
# Configuration parameters: EnforcedStyle, SupportedStyles.
|
87
|
+
StringLiterals:
|
88
|
+
Enabled: false
|
89
|
+
|
90
|
+
# Offense count: 2
|
91
|
+
UselessAssignment:
|
92
|
+
Enabled: true
|
metadata
ADDED
@@ -0,0 +1,341 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ebooks_renamer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Burin Choomnuan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2014-05-04 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: thor
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0.19'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '0.19'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: pdf-reader
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ~>
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 1.3.3
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ~>
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: 1.3.3
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: mobi
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ~>
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.0.0
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.0.0
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: epubinfo
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ~>
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: 0.4.3
|
70
|
+
type: :runtime
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ~>
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: 0.4.3
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: filename_cleaner
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ~>
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 0.0.3
|
86
|
+
type: :runtime
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ~>
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 0.0.3
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: code_lister
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ~>
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: 0.0.8
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ~>
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: 0.0.8
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: awesome_print
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '1.2'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '1.2'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: bundler
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ~>
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '1.5'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ~>
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '1.5'
|
142
|
+
- !ruby/object:Gem::Dependency
|
143
|
+
name: gem-ctags
|
144
|
+
requirement: !ruby/object:Gem::Requirement
|
145
|
+
none: false
|
146
|
+
requirements:
|
147
|
+
- - ~>
|
148
|
+
- !ruby/object:Gem::Version
|
149
|
+
version: '1.0'
|
150
|
+
type: :development
|
151
|
+
prerelease: false
|
152
|
+
version_requirements: !ruby/object:Gem::Requirement
|
153
|
+
none: false
|
154
|
+
requirements:
|
155
|
+
- - ~>
|
156
|
+
- !ruby/object:Gem::Version
|
157
|
+
version: '1.0'
|
158
|
+
- !ruby/object:Gem::Dependency
|
159
|
+
name: guard
|
160
|
+
requirement: !ruby/object:Gem::Requirement
|
161
|
+
none: false
|
162
|
+
requirements:
|
163
|
+
- - ~>
|
164
|
+
- !ruby/object:Gem::Version
|
165
|
+
version: '2.6'
|
166
|
+
type: :development
|
167
|
+
prerelease: false
|
168
|
+
version_requirements: !ruby/object:Gem::Requirement
|
169
|
+
none: false
|
170
|
+
requirements:
|
171
|
+
- - ~>
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '2.6'
|
174
|
+
- !ruby/object:Gem::Dependency
|
175
|
+
name: guard-minitest
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
177
|
+
none: false
|
178
|
+
requirements:
|
179
|
+
- - ~>
|
180
|
+
- !ruby/object:Gem::Version
|
181
|
+
version: '2.2'
|
182
|
+
type: :development
|
183
|
+
prerelease: false
|
184
|
+
version_requirements: !ruby/object:Gem::Requirement
|
185
|
+
none: false
|
186
|
+
requirements:
|
187
|
+
- - ~>
|
188
|
+
- !ruby/object:Gem::Version
|
189
|
+
version: '2.2'
|
190
|
+
- !ruby/object:Gem::Dependency
|
191
|
+
name: minitest
|
192
|
+
requirement: !ruby/object:Gem::Requirement
|
193
|
+
none: false
|
194
|
+
requirements:
|
195
|
+
- - ~>
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '5.3'
|
198
|
+
type: :development
|
199
|
+
prerelease: false
|
200
|
+
version_requirements: !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
202
|
+
requirements:
|
203
|
+
- - ~>
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: '5.3'
|
206
|
+
- !ruby/object:Gem::Dependency
|
207
|
+
name: minitest-spec-context
|
208
|
+
requirement: !ruby/object:Gem::Requirement
|
209
|
+
none: false
|
210
|
+
requirements:
|
211
|
+
- - ~>
|
212
|
+
- !ruby/object:Gem::Version
|
213
|
+
version: 0.0.3
|
214
|
+
type: :development
|
215
|
+
prerelease: false
|
216
|
+
version_requirements: !ruby/object:Gem::Requirement
|
217
|
+
none: false
|
218
|
+
requirements:
|
219
|
+
- - ~>
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: 0.0.3
|
222
|
+
- !ruby/object:Gem::Dependency
|
223
|
+
name: pry
|
224
|
+
requirement: !ruby/object:Gem::Requirement
|
225
|
+
none: false
|
226
|
+
requirements:
|
227
|
+
- - ~>
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '0.9'
|
230
|
+
type: :development
|
231
|
+
prerelease: false
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
none: false
|
234
|
+
requirements:
|
235
|
+
- - ~>
|
236
|
+
- !ruby/object:Gem::Version
|
237
|
+
version: '0.9'
|
238
|
+
- !ruby/object:Gem::Dependency
|
239
|
+
name: rake
|
240
|
+
requirement: !ruby/object:Gem::Requirement
|
241
|
+
none: false
|
242
|
+
requirements:
|
243
|
+
- - ~>
|
244
|
+
- !ruby/object:Gem::Version
|
245
|
+
version: '10.1'
|
246
|
+
type: :development
|
247
|
+
prerelease: false
|
248
|
+
version_requirements: !ruby/object:Gem::Requirement
|
249
|
+
none: false
|
250
|
+
requirements:
|
251
|
+
- - ~>
|
252
|
+
- !ruby/object:Gem::Version
|
253
|
+
version: '10.1'
|
254
|
+
- !ruby/object:Gem::Dependency
|
255
|
+
name: rubocop
|
256
|
+
requirement: !ruby/object:Gem::Requirement
|
257
|
+
none: false
|
258
|
+
requirements:
|
259
|
+
- - ~>
|
260
|
+
- !ruby/object:Gem::Version
|
261
|
+
version: '0.20'
|
262
|
+
type: :development
|
263
|
+
prerelease: false
|
264
|
+
version_requirements: !ruby/object:Gem::Requirement
|
265
|
+
none: false
|
266
|
+
requirements:
|
267
|
+
- - ~>
|
268
|
+
- !ruby/object:Gem::Version
|
269
|
+
version: '0.20'
|
270
|
+
- !ruby/object:Gem::Dependency
|
271
|
+
name: yard
|
272
|
+
requirement: !ruby/object:Gem::Requirement
|
273
|
+
none: false
|
274
|
+
requirements:
|
275
|
+
- - ~>
|
276
|
+
- !ruby/object:Gem::Version
|
277
|
+
version: '0.8'
|
278
|
+
type: :development
|
279
|
+
prerelease: false
|
280
|
+
version_requirements: !ruby/object:Gem::Requirement
|
281
|
+
none: false
|
282
|
+
requirements:
|
283
|
+
- - ~>
|
284
|
+
- !ruby/object:Gem::Version
|
285
|
+
version: '0.8'
|
286
|
+
description: Rename multiple ebook files (pdf, epub, mobi) using embedded metadata
|
287
|
+
if available
|
288
|
+
email:
|
289
|
+
- agilecreativity@gmail.com
|
290
|
+
executables:
|
291
|
+
- ebooks_renamer
|
292
|
+
extensions: []
|
293
|
+
extra_rdoc_files: []
|
294
|
+
files:
|
295
|
+
- bin/ebooks_renamer
|
296
|
+
- lib/ebooks_renamer/cli.rb
|
297
|
+
- lib/ebooks_renamer/core_ext/hash/keys.rb
|
298
|
+
- lib/ebooks_renamer/core_ext/object/blank.rb
|
299
|
+
- lib/ebooks_renamer/ebooks_renamer.rb
|
300
|
+
- lib/ebooks_renamer/epub_parser.rb
|
301
|
+
- lib/ebooks_renamer/logger.rb
|
302
|
+
- lib/ebooks_renamer/mobi_parser.rb
|
303
|
+
- lib/ebooks_renamer/pdf_parser.rb
|
304
|
+
- lib/ebooks_renamer/version.rb
|
305
|
+
- lib/ebooks_renamer.rb
|
306
|
+
- Gemfile
|
307
|
+
- Rakefile
|
308
|
+
- ebooks_renamer.gemspec
|
309
|
+
- README.md
|
310
|
+
- LICENSE
|
311
|
+
- .rubocop.yml
|
312
|
+
- .gitignore
|
313
|
+
- rubocop-todo.yml
|
314
|
+
homepage: https://github.com/agilecreativity/ebooks_renamer
|
315
|
+
licenses:
|
316
|
+
- MIT
|
317
|
+
post_install_message:
|
318
|
+
rdoc_options: []
|
319
|
+
require_paths:
|
320
|
+
- lib
|
321
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
322
|
+
none: false
|
323
|
+
requirements:
|
324
|
+
- - ! '>='
|
325
|
+
- !ruby/object:Gem::Version
|
326
|
+
version: '0'
|
327
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
328
|
+
none: false
|
329
|
+
requirements:
|
330
|
+
- - ! '>='
|
331
|
+
- !ruby/object:Gem::Version
|
332
|
+
version: '0'
|
333
|
+
requirements: []
|
334
|
+
rubyforge_project:
|
335
|
+
rubygems_version: 1.8.23
|
336
|
+
signing_key:
|
337
|
+
specification_version: 3
|
338
|
+
summary: Rename multiple ebook files (pdf, epub, mobi) using embedded metadata when
|
339
|
+
available
|
340
|
+
test_files: []
|
341
|
+
has_rdoc:
|