magicshelf 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|