magicshelf 0.2.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.
- checksums.yaml +7 -0
- data/.gitignore +3 -0
- data/Gemfile +6 -0
- data/Procfile +4 -0
- data/README.md +38 -0
- data/Rakefile +16 -0
- data/bin/console +14 -0
- data/bin/magicconvert +40 -0
- data/bin/server +8 -0
- data/bin/setup +7 -0
- data/config.ru +6 -0
- data/lib/magicshelf.rb +33 -0
- data/lib/magicshelf/dirchanger.rb +22 -0
- data/lib/magicshelf/dirrenamer.rb +30 -0
- data/lib/magicshelf/dirstructureflattener.rb +25 -0
- data/lib/magicshelf/epubgenerator.rb +86 -0
- data/lib/magicshelf/exception.rb +4 -0
- data/lib/magicshelf/executionpipe.rb +105 -0
- data/lib/magicshelf/filecleaner.rb +19 -0
- data/lib/magicshelf/fileextractor.rb +48 -0
- data/lib/magicshelf/filemover.rb +24 -0
- data/lib/magicshelf/filenamevalidator.rb +31 -0
- data/lib/magicshelf/fileserver.rb +103 -0
- data/lib/magicshelf/kindlegenwrapper.rb +33 -0
- data/lib/magicshelf/kindlestrip.rb +250 -0
- data/lib/magicshelf/kindlestripper.rb +25 -0
- data/lib/magicshelf/makeitvertical.rb +41 -0
- data/lib/magicshelf/mobitask.rb +58 -0
- data/lib/magicshelf/tempdiropener.rb +18 -0
- data/lib/magicshelf/version.rb +3 -0
- data/magicshelf.gemspec +37 -0
- data/public/css/index.css +29 -0
- data/public/css/pure-min.css +11 -0
- data/server_config.yml.sample +2 -0
- data/views/generate_mobi.erb +62 -0
- data/views/index.erb +51 -0
- metadata +290 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1442269b371a6a584ed4a00eede012300e8f48af
|
4
|
+
data.tar.gz: e311a0dd3d245eaa42c8a0c2cfa488e9805234db
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f61e304a272623e583b13f1fdfd1d83ad62316d0323613479e2fc943eae1d4317100e91a0da7ee6a11e7460c6528a060b1f3b91b80c24115df82b7c1ab6a8948
|
7
|
+
data.tar.gz: 04bfca83c4fb273026ba2dfea8484f82f03dac508ea108c8407c986bd2a877fdf2ca866af718a2ff5e4755e00f770e437c3bcc31d8c4bde8dee0f7a991de664c
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Procfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# MagicShelf
|
2
|
+
|
3
|
+
Access and get your e-book from kindle anywhere.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'magicshelf'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install magicshelf
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
### Console app
|
24
|
+
````
|
25
|
+
magicconvert --inputfile comic.rar --outputfile comic.mobi --booktype novel/comic/novelimage --title booktitle
|
26
|
+
````
|
27
|
+
|
28
|
+
|
29
|
+
### File Server
|
30
|
+
````
|
31
|
+
bundle exec foreman start
|
32
|
+
````
|
33
|
+
|
34
|
+
|
35
|
+
## Contributing
|
36
|
+
|
37
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/ompugao/magicshelf.
|
38
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'resque/tasks'
|
4
|
+
|
5
|
+
desc 'Run test_unit based test'
|
6
|
+
Rake::TestTask.new do |t|
|
7
|
+
# To run test for only one file (or file path pattern)
|
8
|
+
# $ bundle exec rake test TEST=test/test_specified_path.rb
|
9
|
+
t.libs << "test"
|
10
|
+
t.test_files = Dir["test/**/test_*.rb"]
|
11
|
+
t.verbose = true
|
12
|
+
end
|
13
|
+
|
14
|
+
task :'resque:setup' do
|
15
|
+
Dir["./lib/magicshelf/mobitask.rb"].each {|file| require file}
|
16
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "magicshelf"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
require "pry"
|
11
|
+
Pry.start
|
12
|
+
|
13
|
+
#require "irb"
|
14
|
+
#IRB.start
|
data/bin/magicconvert
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'magicshelf'
|
4
|
+
require 'magicshelf/executionpipe'
|
5
|
+
require 'optparse'
|
6
|
+
|
7
|
+
optparams = ARGV.getopts("", "inputfile:", "title:", "outputfile:", "booktype:comic")
|
8
|
+
|
9
|
+
MagicShelf::ExecutionPipe.new.enter { |params,&block|
|
10
|
+
Dir.mktmpdir("magicshelf") do |dir|
|
11
|
+
params[:workdir] = dir
|
12
|
+
block.call
|
13
|
+
end
|
14
|
+
}.pipe(MagicShelf::FileExtractor.new,:workdir => [:destdir]) { |this|
|
15
|
+
this.inputfile = File.expand_path(optparams["inputfile"])
|
16
|
+
}.enter(:destdir => [:workdir]) {|params, &block|
|
17
|
+
Dir.chdir(params[:workdir]) do
|
18
|
+
block.call
|
19
|
+
end
|
20
|
+
}.pipe(MagicShelf::DirRenamer.new, :workdir => [:workdir]) { |this|
|
21
|
+
}.pipe(MagicShelf::FileNameValidator.new, :workdir => [:workdir]) { |this|
|
22
|
+
}.pipe(MagicShelf::MakeItVertical.new, :workdir => [:workdir]) { |this|
|
23
|
+
}.pipe(MagicShelf::EpubGenerator.new, :workdir => [:workdir]) { |this|
|
24
|
+
this.title = optparams['title']
|
25
|
+
this.book_type = optparams['booktype']
|
26
|
+
this.outputfile = 'test.epub'
|
27
|
+
}.pipe(MagicShelf::KindleGenWrapper.new, :outputfile => [:inputfile]) { |this|
|
28
|
+
this.outputfile = 'test.mobi'
|
29
|
+
}.process(:inputfile => [:file,:inputfile], :outputfile => [:outputfile]) { |params|
|
30
|
+
FileUtils.remove(params[:file])
|
31
|
+
}.pipe(MagicShelf::KindleStripper.new, :outputfile => [:inputfile]) { |this|
|
32
|
+
this.outputfile = File.expand_path('test_strip.mobi')
|
33
|
+
}.pipe(MagicShelf::FileCleaner.new, :inputfile => [:file], :outputfile => [:outputfile]) { |this|
|
34
|
+
}.pipe(MagicShelf::FileMover.new, :outputfile => [:inputfile]) { |this|
|
35
|
+
this.outputfile = File.expand_path(optparams["outputfile"])
|
36
|
+
}.execute
|
37
|
+
|
38
|
+
# vim: set ft=ruby ts=22 sw=2:
|
39
|
+
|
40
|
+
|
data/bin/server
ADDED
data/bin/setup
ADDED
data/config.ru
ADDED
data/lib/magicshelf.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require "magicshelf/version"
|
3
|
+
|
4
|
+
require "magicshelf/dirchanger"
|
5
|
+
require "magicshelf/dirrenamer"
|
6
|
+
require "magicshelf/dirstructureflattener"
|
7
|
+
require "magicshelf/epubgenerator"
|
8
|
+
require "magicshelf/executionpipe"
|
9
|
+
require "magicshelf/filecleaner"
|
10
|
+
require "magicshelf/fileextractor"
|
11
|
+
require "magicshelf/filemover"
|
12
|
+
require "magicshelf/filenamevalidator"
|
13
|
+
require "magicshelf/fileserver"
|
14
|
+
require "magicshelf/kindlegenwrapper"
|
15
|
+
require "magicshelf/kindlestripper"
|
16
|
+
require "magicshelf/makeitvertical"
|
17
|
+
require "magicshelf/tempdiropener"
|
18
|
+
|
19
|
+
|
20
|
+
Dir.glob('monkeypatches/*') do |f|
|
21
|
+
require f
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'logger'
|
25
|
+
|
26
|
+
module MagicShelf
|
27
|
+
(class << self; self end).module_eval do
|
28
|
+
attr_accessor :logger
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
MagicShelf.logger = Logger.new(STDOUT)
|
33
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'magicshelf/exception'
|
2
|
+
|
3
|
+
module MagicShelf
|
4
|
+
class DirChangerError < Error; end
|
5
|
+
|
6
|
+
class DirChanger
|
7
|
+
attr_accessor :workdir
|
8
|
+
|
9
|
+
def enter()
|
10
|
+
raise MagicShelf::DirChangerError.new("workdir is not set") if @workdir == nil
|
11
|
+
Dir.chdir(@workdir) {|dir|
|
12
|
+
MagicShelf.logger.debug("DirChanger: chdir to #{dir}")
|
13
|
+
yield
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def process()
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'magicshelf/exception'
|
2
|
+
|
3
|
+
module MagicShelf
|
4
|
+
class DirRenamerError < Error; end
|
5
|
+
|
6
|
+
class DirRenamer
|
7
|
+
attr_accessor :workdir
|
8
|
+
def enter()
|
9
|
+
yield
|
10
|
+
end
|
11
|
+
|
12
|
+
def process()
|
13
|
+
@workdir ||= Dir.pwd
|
14
|
+
rename_dir_recursively(@workdir)
|
15
|
+
end
|
16
|
+
|
17
|
+
def rename_dir_recursively(path)
|
18
|
+
Dir.chdir(path) {
|
19
|
+
Dir['./*'].select{|f|File.directory?(f)}.each.with_index { |f,index|
|
20
|
+
if File.directory?(f)
|
21
|
+
rename_dir_recursively(f)
|
22
|
+
FileUtils.mv(f, index.to_s)
|
23
|
+
end
|
24
|
+
}
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'magicshelf/exception'
|
2
|
+
|
3
|
+
module MagicShelf
|
4
|
+
class DirStructureFlattenerError < Error; end
|
5
|
+
|
6
|
+
class DirStructureFlattener
|
7
|
+
attr_accessor :workdir
|
8
|
+
def enter()
|
9
|
+
yield
|
10
|
+
end
|
11
|
+
|
12
|
+
def process()
|
13
|
+
@workdir ||= Dir.pwd
|
14
|
+
Dir.glob(File.join(@workdir,'**/*')).select{|f|File.file?(f)}.each do |f|
|
15
|
+
begin
|
16
|
+
FileUtils.mv f, @workdir
|
17
|
+
rescue => e
|
18
|
+
MagicShelf.logger.warn(e.message)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'magicshelf/exception'
|
2
|
+
require 'gepub'
|
3
|
+
require 'shellwords'
|
4
|
+
require 'naturally'
|
5
|
+
|
6
|
+
module MagicShelf
|
7
|
+
class EpubGeneratorError < Error; end
|
8
|
+
|
9
|
+
# create a epub file with the file under the current directory
|
10
|
+
class EpubGenerator
|
11
|
+
attr_accessor :book_type, :title, :outputfile, :language, :identifier_url, :creator, :creator_en
|
12
|
+
|
13
|
+
def enter()
|
14
|
+
MagicShelf.logger.debug('enter EpubGenerator')
|
15
|
+
# check parameters
|
16
|
+
raise MagicShelf::EpubGeneratorError.new("@title is not set") if @title == nil
|
17
|
+
raise MagicShelf::EpubGeneratorError.new("@outputfile is not set") if @outputfile == nil
|
18
|
+
# default parameters
|
19
|
+
@book_type ||= 'comic'
|
20
|
+
@language ||= 'ja'
|
21
|
+
@identifier_url ||= 'http:/example.jp/bookid_in_url'
|
22
|
+
|
23
|
+
yield
|
24
|
+
end
|
25
|
+
|
26
|
+
def process()
|
27
|
+
epubname = @outputfile
|
28
|
+
|
29
|
+
book = GEPUB::Book.new
|
30
|
+
book.unique_identifier @identifier_url
|
31
|
+
book.identifier @identifier_url
|
32
|
+
book.language = @language
|
33
|
+
|
34
|
+
|
35
|
+
book.add_title(@title, nil, GEPUB::TITLE_TYPE::MAIN) { |title|
|
36
|
+
title.lang = @language
|
37
|
+
title.file_as = Shellwords.escape(@title)
|
38
|
+
title.display_seq = 1
|
39
|
+
}
|
40
|
+
if @creator
|
41
|
+
book.add_creator(@creator) { |creator|
|
42
|
+
creator.display_seq = 1
|
43
|
+
creator.add_alternates('en' => @creator_en) if @creator_en
|
44
|
+
}
|
45
|
+
end
|
46
|
+
|
47
|
+
if @book_type == "comic"
|
48
|
+
book.page_progression_direction = 'rtl'
|
49
|
+
metadata = book.instance_eval{@package}.instance_eval{@metadata}
|
50
|
+
metacomic = metadata.add_metadata('meta', '')
|
51
|
+
metacomic['name'] = 'book-type'
|
52
|
+
metacomic['content'] = 'comic'
|
53
|
+
metafixedlayout = metadata.add_metadata('meta', '')
|
54
|
+
metafixedlayout['name'] = 'fixed-layout'
|
55
|
+
metafixedlayout['content'] = 'true'
|
56
|
+
elsif @book_type == "novelimage"
|
57
|
+
book.page_progression_direction = 'rtl'
|
58
|
+
metadata = book.instance_eval{@package}.instance_eval{@metadata}
|
59
|
+
metafixedlayout = metadata.add_metadata('meta', '')
|
60
|
+
metafixedlayout['name'] = 'fixed-layout'
|
61
|
+
metafixedlayout['content'] = 'true'
|
62
|
+
elsif @book_type == "novel"
|
63
|
+
book.page_progression_direction = 'rtl'
|
64
|
+
#nothing to do
|
65
|
+
elsif @book_type == "ltr"
|
66
|
+
book.page_progression_direction = 'ltr'
|
67
|
+
end
|
68
|
+
|
69
|
+
# within ordered block, add_item will be added to spine.
|
70
|
+
book.ordered {
|
71
|
+
# to add nav file:
|
72
|
+
#navpath = 'nav.xhtml'
|
73
|
+
#book.add_item(navpath).add_content(File.open(navpath)).add_property('nav')
|
74
|
+
Naturally.sort(Dir.glob('**/*.{jpg,png}')).each_with_index do |filepath,index|
|
75
|
+
MagicShelf.logger.info("append image #{filepath}, index: #{index}")
|
76
|
+
item = book.add_item(filepath)
|
77
|
+
item.add_content(File.open(filepath)).toc_text(index.to_s)
|
78
|
+
item.cover_image if index == 0
|
79
|
+
end
|
80
|
+
}
|
81
|
+
|
82
|
+
book.generate_epub(epubname)
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'magicshelf/exception'
|
2
|
+
|
3
|
+
module MagicShelf
|
4
|
+
class ExecutionPipe
|
5
|
+
class Procedure
|
6
|
+
attr_accessor :command_obj, :proc, :proc_withblock, :map_params, :params
|
7
|
+
def initialize
|
8
|
+
@command_obj = nil
|
9
|
+
@proc = nil
|
10
|
+
@proc_withblock = nil
|
11
|
+
@params = nil
|
12
|
+
@map_params = nil #from previous procedure
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
@procedures = []
|
18
|
+
end
|
19
|
+
|
20
|
+
def pipe(command_obj, **map_params, &block)
|
21
|
+
procedure = Procedure.new()
|
22
|
+
block.call(command_obj)
|
23
|
+
procedure.command_obj = command_obj
|
24
|
+
procedure.map_params = map_params
|
25
|
+
@procedures.push(procedure)
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def enter(**map_params, &block)
|
30
|
+
procedure = Procedure.new()
|
31
|
+
procedure.proc_withblock = block
|
32
|
+
procedure.map_params = map_params
|
33
|
+
@procedures.push(procedure)
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def process(**map_params, &block)
|
38
|
+
procedure = Procedure.new()
|
39
|
+
procedure.proc = block
|
40
|
+
procedure.map_params = map_params
|
41
|
+
@procedures.push(procedure)
|
42
|
+
self
|
43
|
+
end
|
44
|
+
|
45
|
+
def execute
|
46
|
+
self.execute_(0)
|
47
|
+
end
|
48
|
+
|
49
|
+
def execute_(i_proc)
|
50
|
+
procedure = @procedures[i_proc]
|
51
|
+
previous_procedure = @procedures[i_proc-1]
|
52
|
+
|
53
|
+
if procedure.nil?
|
54
|
+
return
|
55
|
+
end
|
56
|
+
|
57
|
+
if not procedure.command_obj.nil?
|
58
|
+
if not previous_procedure.nil?
|
59
|
+
if not previous_procedure.command_obj.nil?
|
60
|
+
procedure.map_params.each_pair do |param_key_recv, param_keys|
|
61
|
+
param_keys.each do |param_key|
|
62
|
+
procedure.command_obj.instance_variable_set('@'+param_key.to_s, previous_procedure.command_obj.instance_variable_get('@'+param_key_recv.to_s))
|
63
|
+
end
|
64
|
+
end
|
65
|
+
else
|
66
|
+
procedure.map_params.each_pair do |param_key_recv, param_keys|
|
67
|
+
param_keys.each do |param_key|
|
68
|
+
procedure.command_obj.instance_variable_set('@'+param_key.to_s, previous_procedure.params[param_key_recv])
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
procedure.command_obj.enter {
|
74
|
+
procedure.command_obj.process
|
75
|
+
self.execute_(i_proc+1)
|
76
|
+
}
|
77
|
+
elsif not procedure.proc.nil? or not procedure.proc_withblock.nil?
|
78
|
+
|
79
|
+
procedure.params = {}
|
80
|
+
if not previous_procedure.command_obj.nil?
|
81
|
+
procedure.map_params.each_pair do |param_key_recv, param_keys|
|
82
|
+
param_keys.each do |param_key|
|
83
|
+
procedure.params[param_key] = previous_procedure.command_obj.instance_variable_get('@'+param_key_recv.to_s)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
else
|
87
|
+
procedure.map_params.each_pair do |param_key_recv, param_keys|
|
88
|
+
param_keys.each do |param_key|
|
89
|
+
procedure.params[param_key] = previous_procedure.params[param_key_recv]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
if not procedure.proc.nil?
|
95
|
+
procedure.proc.call(procedure.params)
|
96
|
+
self.execute_(i_proc+1)
|
97
|
+
else
|
98
|
+
procedure.proc_withblock.call(procedure.params) do
|
99
|
+
self.execute_(i_proc+1)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|