tankobon 0.5.0
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/bin/tankobon +33 -0
- data/lib/tankobon.rb +21 -0
- data/lib/tankobon/application.rb +65 -0
- data/lib/tankobon/archive.rb +10 -0
- data/lib/tankobon/cli.rb +25 -0
- data/lib/tankobon/converter.rb +53 -0
- data/lib/tankobon/directory.rb +9 -0
- data/lib/tankobon/dsl.rb +9 -0
- data/lib/tankobon/ext/dir.rb +19 -0
- data/lib/tankobon/ext/file.rb +42 -0
- data/lib/tankobon/ext/string.rb +9 -0
- data/lib/tankobon/stageable.rb +28 -0
- data/lib/tankobon/stageddirectory.rb +71 -0
- data/lib/tankobon/transform.rb +48 -0
- data/lib/tankobon/version.rb +3 -0
- metadata +92 -0
data/bin/tankobon
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$:.unshift File.join(File.dirname(File.dirname(__FILE__)), "lib")
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "tankobon"
|
6
|
+
require "trollop"
|
7
|
+
|
8
|
+
opts = Trollop::options do
|
9
|
+
version "tankobon #{Tankobon::VERSION} (c) 2010 Stefano Pigozzi"
|
10
|
+
banner <<-EOS
|
11
|
+
This program lets you batch rename, resize and change colorspace for manga scans
|
12
|
+
in order to make them optimal for consumption on ebook readers.
|
13
|
+
|
14
|
+
Usage:
|
15
|
+
tankobon [options] [<filename>]
|
16
|
+
where [<filename>] is the recipe file to make your Tankobons and [options] are:
|
17
|
+
EOS
|
18
|
+
end
|
19
|
+
|
20
|
+
begin
|
21
|
+
if ARGV.length.eql? 1 then
|
22
|
+
recipe do
|
23
|
+
eval(File.new(File.expand_path(ARGV[0])).read,
|
24
|
+
binding, __FILE__, __LINE__)
|
25
|
+
end
|
26
|
+
exit
|
27
|
+
else
|
28
|
+
Tankobon::CLI.message "Please provide a recipe file."
|
29
|
+
end
|
30
|
+
rescue Interrupt => e
|
31
|
+
Tankobon::CLI.message "\nRecived an interrupt. Quitting."
|
32
|
+
exit
|
33
|
+
end
|
data/lib/tankobon.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
require 'tankobon/ext/string'
|
5
|
+
require 'tankobon/ext/dir'
|
6
|
+
require 'tankobon/ext/file'
|
7
|
+
require 'tankobon/transform'
|
8
|
+
require 'tankobon/converter'
|
9
|
+
require 'tankobon/version'
|
10
|
+
|
11
|
+
module Tankobon
|
12
|
+
autoload :Application, 'tankobon/application'
|
13
|
+
autoload :Archive, 'tankobon/archive'
|
14
|
+
autoload :CLI, 'tankobon/cli'
|
15
|
+
autoload :Directory, 'tankobon/directory'
|
16
|
+
autoload :Stageable, 'tankobon/stageable'
|
17
|
+
autoload :StagedDirectory, 'tankobon/stageddirectory'
|
18
|
+
end
|
19
|
+
|
20
|
+
require 'tankobon/dsl'
|
21
|
+
include Tankobon::DSL
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Tankobon
|
2
|
+
class Application
|
3
|
+
|
4
|
+
def initialize
|
5
|
+
no_batch
|
6
|
+
end
|
7
|
+
|
8
|
+
def batch(name)
|
9
|
+
if name then
|
10
|
+
@batch = name
|
11
|
+
@archive = batched_class(Archive)
|
12
|
+
@directory = batched_class(Directory)
|
13
|
+
else
|
14
|
+
no_batch
|
15
|
+
end
|
16
|
+
self
|
17
|
+
end
|
18
|
+
|
19
|
+
def with_base(base, ary)
|
20
|
+
with(ary.map{|x| File.join(base, x)})
|
21
|
+
end
|
22
|
+
|
23
|
+
def with(ary)
|
24
|
+
@staged_ary = ary.map do |elem|
|
25
|
+
obj = File.archive?(elem) ? @archive : @directory
|
26
|
+
obj.new(elem).to_stage
|
27
|
+
end
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def sanitize(transforms = [SanitizeTransform, SequenceTransform])
|
32
|
+
staged_elems.each do |sd|
|
33
|
+
transforms.each {|tx| sd.rename_images(&tx.new)}
|
34
|
+
sd.mv_images_to_root
|
35
|
+
sd.clean
|
36
|
+
end
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def convert(converters = [KindleDXConverter])
|
41
|
+
staged_elems.each do |sd|
|
42
|
+
converters.each {|tx| sd.convert_images(&tx.new)}
|
43
|
+
end
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
def batched_class(klass)
|
49
|
+
Class.new(klass) do
|
50
|
+
def stage_root; super.stage_root + @batch; end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def no_batch
|
55
|
+
@batch = nil
|
56
|
+
@archive = Archive
|
57
|
+
@directory = Directory
|
58
|
+
end
|
59
|
+
|
60
|
+
def staged_elems
|
61
|
+
raise "You called sanitize or convert before with." unless @staged_ary
|
62
|
+
@batch ? File.dirname(@staged_ary[0]) : @staged_ary
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
data/lib/tankobon/cli.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
module Tankobon
|
2
|
+
class CLI
|
3
|
+
def self.message(string)
|
4
|
+
puts string
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.progress(progress)
|
8
|
+
cr = "\r"
|
9
|
+
clear = "\e[0K"
|
10
|
+
reset = cr + clear
|
11
|
+
print "#{reset}"
|
12
|
+
print "#{progress}"
|
13
|
+
$stdout.flush
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.done()
|
17
|
+
cr = "\r"
|
18
|
+
clear = "\e[0K"
|
19
|
+
reset = cr + clear
|
20
|
+
|
21
|
+
print " Done.\n"
|
22
|
+
$stdout.flush
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Tankobon
|
2
|
+
class Converter
|
3
|
+
def initialize
|
4
|
+
utils = [:identify, :convert]
|
5
|
+
utils.each do |util|
|
6
|
+
raise "Fix your ImageMagick installation, #{util} not in your path." if
|
7
|
+
%x[which #{util}].strip == ""
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_proc
|
12
|
+
method(:convert).to_proc
|
13
|
+
end
|
14
|
+
|
15
|
+
def size
|
16
|
+
"#{width}x#{height}"
|
17
|
+
end
|
18
|
+
|
19
|
+
def will_rotate?(file)
|
20
|
+
w, h = %x[identify -format "%wx%h" "#{file}"]
|
21
|
+
.split('x').map{|x| x.to_i}
|
22
|
+
w > h
|
23
|
+
end
|
24
|
+
|
25
|
+
def rotation(file)
|
26
|
+
will_rotate?(file) ? "-rotate 90" : ""
|
27
|
+
end
|
28
|
+
|
29
|
+
def convert(file)
|
30
|
+
%x[convert "#{file}" -format #{format} #{rotation(file)} \
|
31
|
+
-colorspace #{colorspace} -resize #{size} -background white \
|
32
|
+
-gravity center -extent #{size} "#{converted_name(file)}"
|
33
|
+
]
|
34
|
+
clean(file)
|
35
|
+
end
|
36
|
+
|
37
|
+
protected
|
38
|
+
def clean(file)
|
39
|
+
File.delete(file) unless file == converted_name(file)
|
40
|
+
end
|
41
|
+
|
42
|
+
def converted_name(file)
|
43
|
+
File.join(File.dirname(file), "#{File.splitbase(file)[0]}.#{format}")
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class KindleDXConverter < Converter
|
48
|
+
def width; 824; end
|
49
|
+
def height; 1200; end
|
50
|
+
def format; "jpg"; end
|
51
|
+
def colorspace; "Gray"; end
|
52
|
+
end
|
53
|
+
end
|
data/lib/tankobon/dsl.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
class Dir
|
2
|
+
def self.xplore(dir, &block)
|
3
|
+
block = Proc.new {|f| puts "got: #{f}"} if block.nil?
|
4
|
+
Dir.foreach(dir) do |f|
|
5
|
+
next if f[0] == '.'
|
6
|
+
if File.directory? File.join(dir, f)
|
7
|
+
Dir.xplore(File.join(dir, f), &block)
|
8
|
+
block.call(File.join(dir, f))
|
9
|
+
else
|
10
|
+
block.call(File.join(dir, f))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.empty?(dir)
|
16
|
+
entries = Dir.entries(dir)
|
17
|
+
entries.length < 3 and entries.include? '.' and entries.include? '..'
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class File
|
2
|
+
def self.xform(file_name, &block)
|
3
|
+
base_name, _ = File.splitbase(file_name)
|
4
|
+
block = Proc.new {|name| name} if not block_given?
|
5
|
+
rnbase(file_name, block.call(base_name))
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.bubble_mv(root_dir, file)
|
9
|
+
return if root_dir.eql? File.dirname(file)
|
10
|
+
new_name = "#{File.basename(File.dirname(file))}-#{File.basename(file)}"
|
11
|
+
new_full_name = File.join(
|
12
|
+
File.dirname(File.dirname(file)),
|
13
|
+
new_name)
|
14
|
+
File.rename(file, new_full_name)
|
15
|
+
self.bubble_mv(root_dir, new_full_name)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.rnbase(file, new_name)
|
19
|
+
File.rename(file,
|
20
|
+
File.join(File.dirname(file), "#{new_name}#{File.extname(file)}"))
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.splitbase(file)
|
24
|
+
[File.basename(file, ".*"), File.extname(file)]
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.image?(file)
|
28
|
+
image_extensions.include? File.extname(file).tail
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.image_extensions
|
32
|
+
['jpg', 'jpeg', 'png', 'gif']
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.archive?(file)
|
36
|
+
archive_extensions.include? File.extname(file).tail
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.archive_extensions
|
40
|
+
['zip', 'rar', 'cbz', 'cbr']
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
|
3
|
+
module Tankobon
|
4
|
+
class Stageable
|
5
|
+
def initialize(stageable)
|
6
|
+
@stageable = Pathname.new(stageable)
|
7
|
+
end
|
8
|
+
|
9
|
+
def stage_root
|
10
|
+
Pathname.new("~/tankobon2").expand_path
|
11
|
+
end
|
12
|
+
|
13
|
+
def stage
|
14
|
+
stage_root + @stageable.basename('.*')
|
15
|
+
end
|
16
|
+
|
17
|
+
def stageable
|
18
|
+
@stageable.expand_path
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_stage(&block)
|
22
|
+
FileUtils.mkdir_p(stage.dirname) unless stage.dirname.exist?
|
23
|
+
FileUtils.rm_r(stage) if stage.exist?
|
24
|
+
yield if block_given?
|
25
|
+
StagedDirectory.new(stage)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module Tankobon
|
2
|
+
class StagedDirectory
|
3
|
+
attr_reader :stage
|
4
|
+
|
5
|
+
def initialize(stage)
|
6
|
+
@stage = Pathname.new(stage)
|
7
|
+
end
|
8
|
+
|
9
|
+
def rename(what, &block)
|
10
|
+
send(what).each do |file|
|
11
|
+
File.xform(file, &block)
|
12
|
+
end
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def mv_images_to(directory)
|
17
|
+
images.each do |file|
|
18
|
+
FileUtils.mv(file, Pathname.new(directory) + File.basename(file))
|
19
|
+
end
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
def mv_images_to_root
|
24
|
+
mv_images_to stage
|
25
|
+
self
|
26
|
+
end
|
27
|
+
|
28
|
+
def clean
|
29
|
+
all_in_root.each do |file|
|
30
|
+
FileUtils.rm_r(file) unless File.image?(file)
|
31
|
+
end
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def all
|
36
|
+
join_stage stage_glob(File.join("**", "*")).sort.reverse
|
37
|
+
end
|
38
|
+
|
39
|
+
def images
|
40
|
+
images_wildcard = "*.{#{File.image_extensions.join(",")}}"
|
41
|
+
join_stage stage_glob(File.join("**", images_wildcard)).sort
|
42
|
+
end
|
43
|
+
|
44
|
+
def all_in_root
|
45
|
+
join_stage stage_glob("*").sort
|
46
|
+
end
|
47
|
+
|
48
|
+
def convert_images(&conversion)
|
49
|
+
images.each do |file|
|
50
|
+
yield(file) if block_given?
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def method_missing(name, *args, &block)
|
55
|
+
if name =~ /^rename_(\w+)$/
|
56
|
+
rename($1.to_sym, &block)
|
57
|
+
elsif
|
58
|
+
super
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
def stage_glob(pattern)
|
64
|
+
Dir.chdir(stage){ Dir.glob(pattern) }
|
65
|
+
end
|
66
|
+
|
67
|
+
def join_stage(ary)
|
68
|
+
ary.map{|x| stage + x}.map(&:to_s)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Tankobon
|
2
|
+
class Transform
|
3
|
+
def to_proc
|
4
|
+
method(:transform).to_proc
|
5
|
+
end
|
6
|
+
|
7
|
+
def transform(input)
|
8
|
+
input
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class SequenceTransform < Transform
|
13
|
+
def initialize(seq=-1, padding=5)
|
14
|
+
@seq = seq
|
15
|
+
@padding = padding
|
16
|
+
end
|
17
|
+
|
18
|
+
def pad(num)
|
19
|
+
"%0#{@padding}d" % num
|
20
|
+
end
|
21
|
+
|
22
|
+
def transform(input)
|
23
|
+
pad(@seq += 1)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class SanitizeTransform < Transform
|
28
|
+
def initialize(padding=5)
|
29
|
+
@padding = padding
|
30
|
+
end
|
31
|
+
|
32
|
+
def pad(num)
|
33
|
+
"%0#{@padding}d-" % num
|
34
|
+
end
|
35
|
+
|
36
|
+
def transform(input)
|
37
|
+
if not input =~ /[0-9]+/ then
|
38
|
+
"#{pad(0)}#{input}"
|
39
|
+
else
|
40
|
+
input.gsub(/([0-9]+)/){pad($1.to_i)}
|
41
|
+
.gsub(/([^0-9\-]+)/){""}
|
42
|
+
.gsub(/(\-+)/){"-"}
|
43
|
+
.gsub(/(\-$)/){""}
|
44
|
+
.gsub(/(^\-)/){""}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tankobon
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 5
|
8
|
+
- 0
|
9
|
+
version: 0.5.0
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- Stefano Pigozzi
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-05-07 00:00:00 +02:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: trollop
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 1
|
30
|
+
- 16
|
31
|
+
- 0
|
32
|
+
version: 1.16.0
|
33
|
+
type: :runtime
|
34
|
+
version_requirements: *id001
|
35
|
+
description: Script for resizing manga scans for the e-book readers
|
36
|
+
email: stefano.pigozzi@gmail.com
|
37
|
+
executables:
|
38
|
+
- tankobon
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files: []
|
42
|
+
|
43
|
+
files:
|
44
|
+
- bin/tankobon
|
45
|
+
- lib/tankobon/application.rb
|
46
|
+
- lib/tankobon/archive.rb
|
47
|
+
- lib/tankobon/cli.rb
|
48
|
+
- lib/tankobon/converter.rb
|
49
|
+
- lib/tankobon/directory.rb
|
50
|
+
- lib/tankobon/dsl.rb
|
51
|
+
- lib/tankobon/ext/dir.rb
|
52
|
+
- lib/tankobon/ext/file.rb
|
53
|
+
- lib/tankobon/ext/string.rb
|
54
|
+
- lib/tankobon/stageable.rb
|
55
|
+
- lib/tankobon/stageddirectory.rb
|
56
|
+
- lib/tankobon/transform.rb
|
57
|
+
- lib/tankobon/version.rb
|
58
|
+
- lib/tankobon.rb
|
59
|
+
has_rdoc: true
|
60
|
+
homepage: https://github.com/pigoz/tankobon
|
61
|
+
licenses: []
|
62
|
+
|
63
|
+
post_install_message:
|
64
|
+
rdoc_options: []
|
65
|
+
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
segments:
|
74
|
+
- 0
|
75
|
+
version: "0"
|
76
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
77
|
+
none: false
|
78
|
+
requirements:
|
79
|
+
- - ">="
|
80
|
+
- !ruby/object:Gem::Version
|
81
|
+
segments:
|
82
|
+
- 0
|
83
|
+
version: "0"
|
84
|
+
requirements: []
|
85
|
+
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 1.3.7
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: Converts manga scans
|
91
|
+
test_files: []
|
92
|
+
|