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
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'magicshelf/exception'
|
2
|
+
|
3
|
+
module MagicShelf
|
4
|
+
class FileCleanerError < Error; end
|
5
|
+
|
6
|
+
class FileCleaner
|
7
|
+
attr_accessor :file
|
8
|
+
|
9
|
+
def enter()
|
10
|
+
#raise MagicShelf::FileCleanerError.new("workdir is not set") if @workdir == nil
|
11
|
+
yield
|
12
|
+
end
|
13
|
+
|
14
|
+
def process()
|
15
|
+
FileUtils.remove_file(@file)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'filemagic'
|
2
|
+
require 'zip'
|
3
|
+
require 'open3'
|
4
|
+
require 'shellwords'
|
5
|
+
require 'magicshelf/exception'
|
6
|
+
|
7
|
+
module MagicShelf
|
8
|
+
class FileExtractorError < Error; end
|
9
|
+
|
10
|
+
class FileExtractor
|
11
|
+
attr_accessor :inputfile, :destdir
|
12
|
+
|
13
|
+
def enter()
|
14
|
+
raise MagicShelf::FileExtractorError.new("inputfile is not set") if @inputfile == nil
|
15
|
+
mimetype = FileMagic.new(FileMagic::MAGIC_MIME_TYPE).file(@inputfile)
|
16
|
+
raise MagicShelf::FileExtractorError.new("unsupported filetype: #{mimetype}") if not %w{application/x-rar application/zip}.include?(mimetype)
|
17
|
+
@mimetype = mimetype
|
18
|
+
|
19
|
+
yield
|
20
|
+
end
|
21
|
+
|
22
|
+
def process()
|
23
|
+
case @mimetype
|
24
|
+
when "application/zip"
|
25
|
+
Zip::File.open(@inputfile) do |zip_file|
|
26
|
+
zip_file.each do |entry|
|
27
|
+
MagicShelf.logger.info("Extracting #{entry.name} ...")
|
28
|
+
entry.extract(File.join(@destdir,entry.name))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
when "application/x-rar"
|
32
|
+
out, err, status = Open3.capture3("which unrar")
|
33
|
+
if status.exitstatus != 0
|
34
|
+
raise MagicShelf::FileExtractorError.new("cannot execute unrar, is it on your PATH?")
|
35
|
+
end
|
36
|
+
|
37
|
+
out, err, status = Open3.capture3("unrar x #{Shellwords.escape(@inputfile)} #{Shellwords.escape(@destdir)}")
|
38
|
+
if status.exitstatus != 0
|
39
|
+
raise MagicShelf::FileExtractorError.new("unrar exits with status #{status.exitstatus}: \n #{out} \n #{err}")
|
40
|
+
end
|
41
|
+
else
|
42
|
+
raise MagicShelf::FileExtractorError.new("no way to extract file for the file with filetype: #{@mimetype}")
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'magicshelf/exception'
|
2
|
+
require 'gepub'
|
3
|
+
require 'shellwords'
|
4
|
+
|
5
|
+
module MagicShelf
|
6
|
+
class FileMoverError < Error; end
|
7
|
+
|
8
|
+
class FileMover
|
9
|
+
attr_accessor :inputfile, :outputfile
|
10
|
+
|
11
|
+
def enter()
|
12
|
+
MagicShelf.logger.debug('enter FileMover')
|
13
|
+
raise MagicShelf::FileMoverError.new("inputfile is not set") if @inputfile == nil
|
14
|
+
raise MagicShelf::FileMoverError.new("outputfile is not set") if @outputfile == nil
|
15
|
+
|
16
|
+
yield
|
17
|
+
end
|
18
|
+
|
19
|
+
def process()
|
20
|
+
FileUtils.mv(@inputfile, @outputfile)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'magicshelf/exception'
|
2
|
+
|
3
|
+
module MagicShelf
|
4
|
+
class FileNameValidatorError < Error; end
|
5
|
+
|
6
|
+
class FileNameValidator
|
7
|
+
attr_accessor :workdir
|
8
|
+
def initialize()
|
9
|
+
@erase_original = true
|
10
|
+
end
|
11
|
+
|
12
|
+
def enter()
|
13
|
+
yield
|
14
|
+
end
|
15
|
+
|
16
|
+
def process()
|
17
|
+
@workdir ||= Dir.pwd
|
18
|
+
Dir.glob(File.join(@workdir,'**/*')).select{|f|File.file?(f)}.each do |f|
|
19
|
+
dirname = File.dirname(f)
|
20
|
+
basename = File.basename(f,'.*')
|
21
|
+
extname = File.extname(f)
|
22
|
+
newbasename = basename.gsub(/#/, '_').gsub(/\+/, '_')
|
23
|
+
if not (basename == newbasename)
|
24
|
+
newfilename = File.join(dirname, newbasename + extname)
|
25
|
+
FileUtils.mv(f, newfilename)
|
26
|
+
MagicShelf.logger.info("move #{f} to #{newfilename}.")
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'sinatra/config_file'
|
3
|
+
require 'sinatra/reloader'
|
4
|
+
require 'tilt/erb'
|
5
|
+
require 'redis'
|
6
|
+
require 'magicshelf/mobitask'
|
7
|
+
|
8
|
+
module MagicShelf
|
9
|
+
class FileServer < Sinatra::Application
|
10
|
+
register Sinatra::ConfigFile
|
11
|
+
configure :development do
|
12
|
+
register Sinatra::Reloader
|
13
|
+
end
|
14
|
+
|
15
|
+
set :root, File.join(File.dirname(__FILE__), '../..') #set :views, File.join(File.dirname(__FILE__), '../..', 'views')
|
16
|
+
set :bind, '0.0.0.0'
|
17
|
+
config_file 'server_config.yml'
|
18
|
+
|
19
|
+
get '/' do
|
20
|
+
redirect "/files/", 303
|
21
|
+
end
|
22
|
+
|
23
|
+
get '/files/*' do |path|
|
24
|
+
sort_type = params['sort_by'] || "title"
|
25
|
+
files = []
|
26
|
+
Dir.chdir(settings.library_directory) {
|
27
|
+
files = Dir.glob(File.join('.',path, '*'))
|
28
|
+
case sort_type
|
29
|
+
when 'title'
|
30
|
+
files = files.sort
|
31
|
+
when 'title_reverse'
|
32
|
+
files = files.sort.reverse
|
33
|
+
when 'date'
|
34
|
+
files = files.sort_by{ |f| File.mtime(f) }
|
35
|
+
when 'date_reverse'
|
36
|
+
files = files.sort_by{ |f| File.mtime(f) }.reverse
|
37
|
+
else
|
38
|
+
end
|
39
|
+
files_withmtime = files.map do |f|
|
40
|
+
fname = (f.start_with?('./') ? f[2..-1] : f)
|
41
|
+
[fname, File.mtime(f).strftime("%Y/%m/%d %H:%M:%S")]
|
42
|
+
end
|
43
|
+
upperpath = nil
|
44
|
+
upperpath = path.split('/')[0...-1].join('/') if path != ""
|
45
|
+
erb :index, :locals => {:page_title => settings.page_title, :files_withmtime => files_withmtime, :path => path, :upperpath => upperpath, :sort_type => sort_type}
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
get '/get_file/*' do |path|
|
50
|
+
pass unless path # pass to a subsequent route
|
51
|
+
send_file(File.join(settings.library_directory, path))
|
52
|
+
end
|
53
|
+
|
54
|
+
get '/get_file*' do
|
55
|
+
'you come to this page without specifying the path to file. go back to the previous page!'
|
56
|
+
end
|
57
|
+
|
58
|
+
get '/generate_mobi/*' do |path|
|
59
|
+
#path = params['splat'].first
|
60
|
+
pass unless path # pass to a subsequent route
|
61
|
+
|
62
|
+
erb :generate_mobi, :locals => {:page_title => settings.page_title, :library_directory => settings.library_directory, :path => path}
|
63
|
+
end
|
64
|
+
|
65
|
+
post '/generate_mobi/*' do |path|
|
66
|
+
title = params['title']
|
67
|
+
author = params['author']
|
68
|
+
booktype = params['booktype']
|
69
|
+
outputfile = params['outputfile']
|
70
|
+
outputfile = title + ".mobi" if outputfile.empty?
|
71
|
+
|
72
|
+
taskparams = {}
|
73
|
+
#taskparams.update(params)
|
74
|
+
taskparams['title'] = params['title']
|
75
|
+
taskparams['author'] = params['author']
|
76
|
+
taskparams['booktype'] = params['booktype']
|
77
|
+
taskparams['inputfile'] = File.join(settings.library_directory,path)
|
78
|
+
taskparams['outputfile'] = File.join(settings.library_directory,File.dirname(path),outputfile)
|
79
|
+
|
80
|
+
Resque.enqueue(MobiTask, taskparams)
|
81
|
+
|
82
|
+
<<-EOF
|
83
|
+
<!DOCTYPE html>
|
84
|
+
<html lang="en">
|
85
|
+
<head>
|
86
|
+
<meta charset="UTF-8">
|
87
|
+
<title></title>
|
88
|
+
</head>
|
89
|
+
<body>
|
90
|
+
Now generating mobi file. wait for a while. <br><a href="/">Back to Top</a>
|
91
|
+
</body>
|
92
|
+
</html>
|
93
|
+
EOF
|
94
|
+
end
|
95
|
+
|
96
|
+
get '/generate_mobi*' do |f|
|
97
|
+
'you come to this page without specifying the path to file. go back to the previous page!'
|
98
|
+
end
|
99
|
+
|
100
|
+
run! if app_file == $0
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'magicshelf/exception'
|
2
|
+
require 'open3'
|
3
|
+
|
4
|
+
module MagicShelf
|
5
|
+
class KindleGenWrapperError < Error; end
|
6
|
+
|
7
|
+
class KindleGenWrapper
|
8
|
+
attr_accessor :inputfile, :outputfile
|
9
|
+
|
10
|
+
def enter()
|
11
|
+
MagicShelf.logger.debug('enter KindleGenWrapper')
|
12
|
+
|
13
|
+
# check parameters
|
14
|
+
raise MagicShelf::KindleGenWrapperError.new("inputfile is not set") if @inputfile == nil
|
15
|
+
raise MagicShelf::KindleGenWrapperError.new("outputfile is not set") if @outputfile == nil
|
16
|
+
out, err, status = Open3.capture3("which kindlegen")
|
17
|
+
if status.exitstatus != 0
|
18
|
+
raise MagicShelf::KindleGenWrapperError.new("cannot execute kindlegen, is it on your PATH?")
|
19
|
+
end
|
20
|
+
|
21
|
+
yield
|
22
|
+
end
|
23
|
+
|
24
|
+
def process()
|
25
|
+
out, err, status = Open3.capture3("kindlegen #{@inputfile} -o #{@outputfile}")
|
26
|
+
if status.exitstatus != 0
|
27
|
+
raise MagicShelf::KindleGenWrapperError.new("kindlegen exited with #{status.exitstatus}: \n" + out + "\n" + err)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
@@ -0,0 +1,250 @@
|
|
1
|
+
#! ruby
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
#
|
4
|
+
# It was translated into Ruby script by whiteleaf.
|
5
|
+
# ( https://github.com/whiteleaf7/narou/blob/master/lib/kindlestrip.rb )
|
6
|
+
#
|
7
|
+
# original source code:
|
8
|
+
# kindlestrip.py v.1.35 http://www.mobileread.com/forums/showthread.php?t=96903
|
9
|
+
#
|
10
|
+
# This script strips the penultimate record from a Mobipocket file.
|
11
|
+
# This is useful because the current KindleGen add a compressed copy
|
12
|
+
# of the source files used in this record, making the ebook produced
|
13
|
+
# about twice as big as it needs to be.
|
14
|
+
#
|
15
|
+
#
|
16
|
+
# This is free and unencumbered software released into the public domain.
|
17
|
+
#
|
18
|
+
# Anyone is free to copy, modify, publish, use, compile, sell, or
|
19
|
+
# distribute this software, either in source code form or as a compiled
|
20
|
+
# binary, for any purpose, commercial or non-commercial, and by any
|
21
|
+
# means.
|
22
|
+
#
|
23
|
+
# In jurisdictions that recognize copyright laws, the author or authors
|
24
|
+
# of this software dedicate any and all copyright interest in the
|
25
|
+
# software to the public domain. We make this dedication for the benefit
|
26
|
+
# of the public at large and to the detriment of our heirs and
|
27
|
+
# successors. We intend this dedication to be an overt act of
|
28
|
+
# relinquishment in perpetuity of all present and future rights to this
|
29
|
+
# software under copyright law.
|
30
|
+
#
|
31
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
32
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
33
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
34
|
+
# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
35
|
+
# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
36
|
+
# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
37
|
+
# OTHER DEALINGS IN THE SOFTWARE.
|
38
|
+
#
|
39
|
+
# For more information, please refer to <http://unlicense.org/>
|
40
|
+
#
|
41
|
+
# Written by Paul Durrant, 2010-2011, paul@durrant.co.uk, pdurrant on mobileread.com
|
42
|
+
# With enhancements by Kevin Hendricks, KevinH on mobileread.com
|
43
|
+
#
|
44
|
+
# Changelog
|
45
|
+
# 1.00 - Initial version
|
46
|
+
# 1.10 - Added an option to output the stripped data
|
47
|
+
# 1.20 - Added check for source files section (thanks Piquan)
|
48
|
+
# 1.30 - Added prelim Support for K8 style mobis
|
49
|
+
# 1.31 - removed the SRCS section but kept a 0 size entry for it
|
50
|
+
# 1.32 - removes the SRCS section and its entry, now updates metadata 121 if needed
|
51
|
+
# 1.33 - now uses and modifies mobiheader SRCS and CNT
|
52
|
+
# 1.34 - added credit for Kevin Hendricks
|
53
|
+
# 1.35 - fixed bug when more than one compilation (SRCS/CMET) records
|
54
|
+
|
55
|
+
KINDLESTRIP_VERSION = '1.35'
|
56
|
+
|
57
|
+
class StripException < StandardError; end
|
58
|
+
|
59
|
+
class SectionStripper
|
60
|
+
def load_section(section)
|
61
|
+
if section + 1 == @num_sections
|
62
|
+
endoff = @data_file.length
|
63
|
+
else
|
64
|
+
endoff = @sections[section + 1][0]
|
65
|
+
end
|
66
|
+
off = @sections[section][0]
|
67
|
+
@data_file[off...endoff]
|
68
|
+
end
|
69
|
+
|
70
|
+
def patch(off, _new)
|
71
|
+
@data_file = @data_file[0, off] + new + @data_file[off + _new.length .. -1]
|
72
|
+
end
|
73
|
+
|
74
|
+
def strip(off, len)
|
75
|
+
@data_file = @data_file[0, off] + @data_file[off + len .. -1]
|
76
|
+
end
|
77
|
+
|
78
|
+
def patch_section(section, _new, in_off = 0)
|
79
|
+
if section + 1 == @num_sections
|
80
|
+
endoff = @data_file.length
|
81
|
+
else
|
82
|
+
endoff = @sections[section + 1][0]
|
83
|
+
end
|
84
|
+
raise unless off + in_off + _new.length <= endoff
|
85
|
+
patch(off + in_off, _new)
|
86
|
+
end
|
87
|
+
|
88
|
+
def updateEXTH121(srcs_secnum, srcs_cnt, mobiheader)
|
89
|
+
mobi_length, = mobiheader[0x14...0x18].unpack("N")
|
90
|
+
exth_flag, = mobiheader[0x80...0x84].unpack("N")
|
91
|
+
exth = "NONE"
|
92
|
+
begin
|
93
|
+
if exth_flag & 0x40 != 0
|
94
|
+
exth = mobiheader[16 + mobi_length .. -1]
|
95
|
+
if exth.length >= 4 && exth[0, 4] == "EXTH"
|
96
|
+
nitems, = exth[8...12].unpack("N")
|
97
|
+
pos = 12
|
98
|
+
nitems.times do
|
99
|
+
type, size = exth[pos ... pos + 8].unpack("NN")
|
100
|
+
#puts "#{type}, #{size}"
|
101
|
+
if type == 121
|
102
|
+
boundaryptr, = exth[pos + 8 ... pos + size].unpack("N")
|
103
|
+
if srcs_secnum <= boundaryptr
|
104
|
+
boundaryptr -= srcs_cnt
|
105
|
+
prefix = mobiheader[0, 16 + mobi_length + pos + 8]
|
106
|
+
suffix = mobiheader[16 + mobi_length + pos + 8 + 4 .. -1]
|
107
|
+
nval = [boundaryptr].pack("N")
|
108
|
+
mobiheader = prefix + nval + suffix
|
109
|
+
end
|
110
|
+
end
|
111
|
+
pos += size
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
rescue
|
116
|
+
end
|
117
|
+
mobiheader
|
118
|
+
end
|
119
|
+
|
120
|
+
def initialize(datain, verbose = true)
|
121
|
+
@verbose = verbose
|
122
|
+
if datain[0x3C...0x3C+8] != "BOOKMOBI"
|
123
|
+
raise StripException, "invalid file format"
|
124
|
+
end
|
125
|
+
@num_sections, = datain[76...78].unpack("n")
|
126
|
+
|
127
|
+
# get mobiheader and check SRCS section number and count
|
128
|
+
offset0, = datain.unpack("@78N")
|
129
|
+
offset1, = datain.unpack("@86N")
|
130
|
+
mobiheader = datain[offset0 ... offset1]
|
131
|
+
srcs_secnum, srcs_cnt = mobiheader.unpack("@224NN")
|
132
|
+
if srcs_secnum == 0xffffffff || srcs_cnt == 0
|
133
|
+
raise StripException, "File doesn't contain the sources section."
|
134
|
+
end
|
135
|
+
|
136
|
+
puts "Found SRCS section number %d, and count %d" % [srcs_secnum, srcs_cnt] if @verbose
|
137
|
+
# find its offset and length
|
138
|
+
_next = srcs_secnum + srcs_cnt
|
139
|
+
srcs_offset, = datain.unpack("@#{78+srcs_secnum*8}NN")
|
140
|
+
next_offset, = datain.unpack("@#{78+_next*8}NN")
|
141
|
+
srcs_length = next_offset - srcs_offset
|
142
|
+
if datain[srcs_offset ... srcs_offset+4] != "SRCS"
|
143
|
+
raise StripException, "SRCS section num does not point to SRCS."
|
144
|
+
end
|
145
|
+
puts " beginning at offset %0x and ending at offset %0x" % [srcs_offset, srcs_length] if @verbose
|
146
|
+
|
147
|
+
# it appears bytes 68-71 always contain (2*num_sections) + 1
|
148
|
+
# this is not documented anyplace at all but it appears to be some sort of next
|
149
|
+
# available unique_id used to identify specific sections in the palm db
|
150
|
+
@data_file = datain[0, 68] + [(@num_sections - srcs_cnt) * 2 + 1].pack("N")
|
151
|
+
@data_file += datain[72...76]
|
152
|
+
|
153
|
+
# write out the number of sections reduced by srtcs_cnt
|
154
|
+
@data_file = @data_file + [@num_sections - srcs_cnt].pack("n")
|
155
|
+
|
156
|
+
# we are going to remove srcs_cnt SRCS sections so the offset of every entry in the table
|
157
|
+
# up to the srcs secnum must begin 8 bytes earlier per section removed (each table entry is 8 )
|
158
|
+
delta = -8 * srcs_cnt
|
159
|
+
srcs_secnum.times do |i|
|
160
|
+
offset, flgval = datain.unpack("@#{78+i*8}NN")
|
161
|
+
offset += delta
|
162
|
+
@data_file += [offset].pack("N") + [flgval].pack("N")
|
163
|
+
end
|
164
|
+
|
165
|
+
# for every record after the srcs_cnt SRCS records we must start it
|
166
|
+
# earlier by 8*srcs_cnt + the length of the srcs sections themselves)
|
167
|
+
delta = delta - srcs_length
|
168
|
+
(srcs_secnum + srcs_cnt ... @num_sections).each do |i|
|
169
|
+
offset, = datain.unpack("@#{78+i*8}NN")
|
170
|
+
offset += delta
|
171
|
+
flgval = 2 * (i - srcs_cnt)
|
172
|
+
@data_file += [offset].pack("N") + [flgval].pack("N")
|
173
|
+
end
|
174
|
+
|
175
|
+
# now pad it out to begin right at the first offset
|
176
|
+
# typically this is 2 bytes of nulls
|
177
|
+
first_offset, = @data_file.unpack("@78NN")
|
178
|
+
@data_file += "\0" * (first_offset - @data_file.length)
|
179
|
+
|
180
|
+
# now finally add on every thing up to the original src_offset
|
181
|
+
@data_file += datain[offset0...srcs_offset]
|
182
|
+
|
183
|
+
# and everything afterwards
|
184
|
+
@data_file += datain[srcs_offset + srcs_length .. -1]
|
185
|
+
|
186
|
+
#store away the SRCS section in case the user wants it output
|
187
|
+
@stripped_data_header = datain[srcs_offset ... srcs_offset + 16]
|
188
|
+
@stripped_data = datain[srcs_offset + 16 ... srcs_offset + srcs_length]
|
189
|
+
|
190
|
+
# update the number of sections count
|
191
|
+
@num_section = @num_sections - srcs_cnt
|
192
|
+
|
193
|
+
# update the srcs_secnum and srcs_cnt in the mobiheader
|
194
|
+
offset0, = @data_file.unpack("@78NN")
|
195
|
+
offset1, = @data_file.unpack("@86NN")
|
196
|
+
mobiheader = @data_file[offset0 ... offset1]
|
197
|
+
mobiheader = mobiheader[0, 0xe0] + [-1].pack("N") + [0].pack("N") + mobiheader[0xe8 .. -1]
|
198
|
+
|
199
|
+
# if K8 mobi, handle metadata 121 in old mobiheader
|
200
|
+
mobiheader = updateEXTH121(srcs_secnum, srcs_cnt, mobiheader)
|
201
|
+
@data_file = @data_file[0, offset0] + mobiheader + @data_file[offset1 .. -1]
|
202
|
+
puts "done" if @verbose
|
203
|
+
end
|
204
|
+
|
205
|
+
def get_result
|
206
|
+
@data_file
|
207
|
+
end
|
208
|
+
|
209
|
+
def get_stripped_data
|
210
|
+
@stripped_data
|
211
|
+
end
|
212
|
+
|
213
|
+
def get_header
|
214
|
+
@stripped_data_header
|
215
|
+
end
|
216
|
+
|
217
|
+
def self.strip(infile, outfile = nil, verbose = true)
|
218
|
+
outfile = infile unless outfile
|
219
|
+
data_file = File.binread(infile)
|
220
|
+
stripped_file = new(data_file, verbose)
|
221
|
+
File.binwrite(outfile, stripped_file.get_result)
|
222
|
+
stripped_file
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
if __FILE__ == $0
|
227
|
+
puts "KndleStrip v#{KINDLESTRIP_VERSION}. " +
|
228
|
+
"Written 2010-2012 by Paul Durrant and Kevin Hendricks."
|
229
|
+
if ARGV.length < 2 || ARGV.length > 3
|
230
|
+
puts "Strips the Sources record from Mobipocket ebooks"
|
231
|
+
puts "For ebooks generated using KindleGen 1.1 and later that add the source"
|
232
|
+
puts "Usage:"
|
233
|
+
puts " %s <infile> <outfile> <strippeddatafile>" % File.basename(__FILE__)
|
234
|
+
puts "<strippeddatafile> is optional."
|
235
|
+
exit Narou::EXIT_ERROR_CODE
|
236
|
+
else
|
237
|
+
infile = ARGV[0]
|
238
|
+
outfile = ARGV[1]
|
239
|
+
begin
|
240
|
+
stripped_file = SectionStripper.strip(infile, outfile)
|
241
|
+
#print "Header Bytes: " + binascii.b2a_hex(strippedFile.getHeader())
|
242
|
+
if ARGV.length == 3
|
243
|
+
File.binwrite(ARGV[2], stripped_file.get_stripped_data)
|
244
|
+
end
|
245
|
+
rescue StripException => e
|
246
|
+
warn "Error: #{e.message}"
|
247
|
+
exit Narou::EXIT_ERROR_CODE
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|