saber 1.1.1 → 1.2.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/.gitignore +1 -0
- data/CHANGELOG.md +8 -1
- data/Gemfile +6 -1
- data/Gemfile.lock +58 -14
- data/README.md +40 -125
- data/bin/saber +4 -2
- data/bin/saber.bb +3 -0
- data/bin/saber.stp +3 -0
- data/lib/saber.rb +5 -0
- data/lib/saber/autofetcher/server.rb +1 -1
- data/lib/saber/book.rb +36 -0
- data/lib/saber/cli.rb +45 -19
- data/lib/saber/core_ext.rb +68 -0
- data/lib/saber/fetcher.rb +2 -2
- data/lib/saber/rc.rb +6 -0
- data/lib/saber/task.rb +3 -0
- data/lib/saber/task/base.rb +6 -1
- data/lib/saber/task/chd.rb +10 -2
- data/lib/saber/task/clean.rb +2 -2
- data/lib/saber/task/find_uploads.rb +53 -0
- data/lib/saber/task/generate.rb +37 -20
- data/lib/saber/task/make.rb +20 -8
- data/lib/saber/task/send.rb +1 -1
- data/lib/saber/task/upload.rb +29 -19
- data/lib/saber/tracker.rb +8 -4
- data/lib/saber/tracker/base.rb +36 -15
- data/lib/saber/tracker/bb.rb +4 -10
- data/lib/saber/tracker/bib.rb +27 -11
- data/lib/saber/tracker/chd.rb +4 -4
- data/lib/saber/tracker/gazelle.rb +7 -0
- data/lib/saber/tracker/ptp.rb +2 -2
- data/lib/saber/tracker/stp.rb +53 -0
- data/lib/saber/tracker/what.rb +2 -2
- data/lib/saber/tracker2.rb +31 -0
- data/lib/saber/tracker2/base.rb +126 -0
- data/lib/saber/tracker2/bb.rb +211 -0
- data/lib/saber/tracker2/bib.rb +162 -0
- data/lib/saber/tracker2/gazelle.rb +52 -0
- data/lib/saber/tracker2/stp.rb +136 -0
- data/lib/saber/tracker2/what.rb +51 -0
- data/lib/saber/version.rb +1 -1
- data/lib/saber/watir_ext.rb +95 -0
- data/saber.gemspec +6 -1
- data/spec/saber/autofetcher/server_spec.rb +1 -1
- data/spec/saber/task_spec.rb +1 -1
- data/systemd/saber-chd@.service +12 -0
- data/systemd/saber-client@.service +12 -0
- data/systemd/saber-server@.service +12 -0
- data/templates/_saberrc +29 -8
- data/templates/article.yml +18 -0
- data/templates/bib/application.yml +8 -8
- data/templates/bib/audiobook.yml +12 -14
- data/templates/comic.yml +25 -0
- data/templates/ebook.yml +35 -0
- data/templates/journal.yml +19 -0
- data/templates/magazine.yml +41 -0
- data/templates/manual.yml +6 -0
- data/templates/newspaper.yml +6 -0
- metadata +109 -13
- data/templates/bb/comic.yml +0 -7
- data/templates/bb/ebook.yml +0 -8
- data/templates/bb/magazine.yml +0 -6
- data/templates/bib/article.yml +0 -18
- data/templates/bib/comic.yml +0 -22
- data/templates/bib/ebook.yml +0 -20
- data/templates/bib/journal.yml +0 -18
- data/templates/bib/magazine.yml +0 -18
- data/templates/what/ebook.yml +0 -6
data/bin/saber.bb
ADDED
data/bin/saber.stp
ADDED
data/lib/saber.rb
CHANGED
@@ -3,6 +3,8 @@ require "pa"
|
|
3
3
|
require "optimism"
|
4
4
|
require "active_support/core_ext/module/attribute_accessors"
|
5
5
|
require "active_support/concern"
|
6
|
+
require "active_support/core_ext/numeric/bytes"
|
7
|
+
require "saber/core_ext"
|
6
8
|
|
7
9
|
module Saber
|
8
10
|
autoload :VERSION, "saber/version"
|
@@ -12,11 +14,14 @@ module Saber
|
|
12
14
|
autoload :Fetcher, "saber/fetcher"
|
13
15
|
autoload :AutoFetcher, "saber/autofetcher"
|
14
16
|
autoload :Tracker, "saber/tracker"
|
17
|
+
autoload :Tracker2, "saber/tracker2"
|
18
|
+
autoload :Book, "saber/book"
|
15
19
|
|
16
20
|
Error = Class.new Exception
|
17
21
|
FatalError = Class.new Exception
|
18
22
|
Rc = Optimism.require "saber/rc", "~/.saberrc"
|
19
23
|
|
24
|
+
|
20
25
|
class << self
|
21
26
|
attr_accessor :ui
|
22
27
|
|
@@ -65,7 +65,7 @@ module Saber
|
|
65
65
|
|
66
66
|
# ["filea", "foo/filea", "foo/fileb"]
|
67
67
|
def build_files(*names)
|
68
|
-
Pa.ls2_r(*names, :base_dir => Rc.
|
68
|
+
Pa.ls2_r(*names, :base_dir => Rc.fetch.remote_dir, :file => true, :include => true) { |p,abs| not Pa.directory?(abs) }
|
69
69
|
end
|
70
70
|
end
|
71
71
|
end
|
data/lib/saber/book.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require "faraday"
|
2
|
+
require "faraday_middleware"
|
3
|
+
|
4
|
+
module Saber
|
5
|
+
class Book
|
6
|
+
# delegate to #populate
|
7
|
+
def self.populate(*args)
|
8
|
+
new.populate(*args)
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :client
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@client = Faraday.new(url: Rc.api_url) {|c|
|
15
|
+
c.response :follow_redirects
|
16
|
+
c.response :json, :content_type => /\bjson$/
|
17
|
+
|
18
|
+
c.adapter Faraday.default_adapter
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
# @return [Hash] data
|
23
|
+
def populate(isbn, filename)
|
24
|
+
params = {}
|
25
|
+
rep = client.get("/books/#{isbn}", params)
|
26
|
+
data = rep.body
|
27
|
+
|
28
|
+
if data["status"] == 0
|
29
|
+
data["tracker"]
|
30
|
+
else
|
31
|
+
Saber.ui.error "Can't populate book -- #{isbn} #{filename}."
|
32
|
+
{}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/saber/cli.rb
CHANGED
@@ -7,28 +7,29 @@ module Saber
|
|
7
7
|
class_option "no-color", banner: "Disable colorization in output", type: :boolean
|
8
8
|
class_option "verbose", aliases: "-V", banner: "Enable verbose output mode", type: :boolean
|
9
9
|
class_option "log", banner: "Log file", type: :string
|
10
|
-
class_option "force",
|
10
|
+
class_option "force", banner: "Fore writing even if file exists", type: :boolean
|
11
11
|
class_option "tracker", aliases: "-t", banner: "tracker name", type: :string
|
12
|
-
|
13
|
-
attr_reader :o
|
12
|
+
class_option "dry-run", aliases: "-n", banner: "dry run", type: :boolean
|
14
13
|
|
15
14
|
def initialize(*)
|
16
15
|
super
|
17
|
-
|
16
|
+
self.options = self.options.dup
|
18
17
|
|
19
|
-
Saber.ui = if
|
18
|
+
Saber.ui = if options["log"] then
|
20
19
|
require "logger"
|
21
|
-
UI::Logger.new(::Logger.new(
|
20
|
+
UI::Logger.new(::Logger.new(options["log"]))
|
22
21
|
else
|
23
22
|
the_shell = (options["no-color"] ? Thor::Shell::Basic.new : shell)
|
24
23
|
UI::Shell.new(the_shell)
|
25
24
|
end
|
26
25
|
|
27
|
-
Saber.ui.debug! if
|
26
|
+
Saber.ui.debug! if options["verbose"]
|
28
27
|
|
29
28
|
# Initialize environment in first time
|
30
29
|
unless Rc.p.home.exists?
|
31
|
-
Pa.mkdir Rc.p.home
|
30
|
+
Pa.mkdir Rc.p.home
|
31
|
+
Pa.mkdir "#{Rc.p.home}/templates"
|
32
|
+
Pa.mkdir "#{Rc.p.home}/database"
|
32
33
|
end
|
33
34
|
end
|
34
35
|
|
@@ -36,7 +37,7 @@ module Saber
|
|
36
37
|
def clean
|
37
38
|
require "saber/task/clean"
|
38
39
|
|
39
|
-
Task["clean"].invoke(:clean, [],
|
40
|
+
Task["clean"].invoke(:clean, [], options)
|
40
41
|
end
|
41
42
|
|
42
43
|
desc "server", "start saber-server daemon"
|
@@ -62,18 +63,25 @@ module Saber
|
|
62
63
|
AutoFetcher::DRbClient.new.add(*names)
|
63
64
|
end
|
64
65
|
|
65
|
-
desc "upload [options] <torrent_file/file ...>", "[make a torrent file and] upoad a torrent file to the site"
|
66
|
-
|
66
|
+
desc "upload [options] <format> <torrent_file/file ...>", "[make a torrent file and] upoad a torrent file to the site"
|
67
|
+
method_option "add", aliases: "-a", desc: "upload via add format.", type: :boolean
|
68
|
+
def upload(format0, *files)
|
67
69
|
require "saber/task/upload"
|
70
|
+
format = format0.downcase
|
71
|
+
Saber.ui.error! "Don't support this format -- #{format0}" unless Rc.book_exts.include?(".#{format}")
|
68
72
|
|
69
|
-
|
73
|
+
files = files.map{|v| v.dup} # unfrozen string.
|
74
|
+
Task["upload"].invoke(:upload, [options["tracker"] || ENV["SABER_TRACKER"], format, *wrap_file(*files)], options)
|
70
75
|
end
|
71
76
|
|
72
|
-
desc "generate [options] <type>
|
73
|
-
|
77
|
+
desc "generate [options] <type> [filename:isbn ...]", %~generate a meta data file (alias: "g")~
|
78
|
+
method_option "file", aliases: "-f", desc: "read files from file list", type: :string
|
79
|
+
def generate(type, *files)
|
74
80
|
require "saber/task/generate"
|
75
81
|
|
76
|
-
|
82
|
+
files = File.read(options["file"]).split(/\n+/).map{|v| v.strip} if options["file"]
|
83
|
+
files = files.map{|v| name, isbn = v.split(":"); [*wrap_file(name), isbn]}
|
84
|
+
Task["generate"].invoke(:generate, [type, *files], options)
|
77
85
|
end
|
78
86
|
map "g" => "generate"
|
79
87
|
|
@@ -86,21 +94,39 @@ module Saber
|
|
86
94
|
def send1(*names)
|
87
95
|
require "saber/task/send"
|
88
96
|
|
89
|
-
Task["send"].invoke(:send1, names,
|
97
|
+
Task["send"].invoke(:send1, names, options)
|
90
98
|
end
|
91
99
|
|
92
100
|
desc "make [options] <file ..>", "make a torent file and send it to local and/or remote watch directory"
|
101
|
+
method_option "option", aliases: "-o", desc: "extra options passed to mktorrent", type: :string
|
102
|
+
method_option "file", aliases: "-f", desc: "read files from file list", type: :string
|
93
103
|
def make(*files)
|
94
104
|
require "saber/task/make"
|
95
105
|
|
96
|
-
|
106
|
+
files = File.read(options["file"]).split(/\n+/).map{|v| v.strip.split(":")[0] } if options["file"]
|
107
|
+
Task["make"].invoke(:make, [options["tracker"] || ENV["SABER_TRACKER"], *files], options)
|
97
108
|
end
|
98
109
|
|
99
|
-
desc "chd", "
|
110
|
+
desc "chd", "NOT WORKING"
|
100
111
|
def chd
|
101
112
|
require "saber/task/chd"
|
102
113
|
|
103
|
-
Task["chd"].invoke(:chd,
|
114
|
+
Task["chd"].invoke(:chd, [], options)
|
115
|
+
end
|
116
|
+
|
117
|
+
desc "find_uploads", "NOT WORKING"
|
118
|
+
def find_uploads(page)
|
119
|
+
require "saber/task/find_uploads"
|
120
|
+
|
121
|
+
Task["find_uploads"].invoke(:find_uploads, [page], options)
|
122
|
+
end
|
123
|
+
|
124
|
+
private
|
125
|
+
|
126
|
+
# a.yml -> a
|
127
|
+
# a.epub.torrent -> a
|
128
|
+
def wrap_file(*names)
|
129
|
+
names.map{|v| Pa.delete_ext(v, *%w[.torrent .yml]).delete_ext2(*Rc.book_exts)}
|
104
130
|
end
|
105
131
|
end
|
106
132
|
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class Hash
|
2
|
+
# From Rails 4.0
|
3
|
+
|
4
|
+
# Return a new hash with all keys converted by the block operation.
|
5
|
+
# This includes the keys from the root hash and from all
|
6
|
+
# nested hashes.
|
7
|
+
#
|
8
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
9
|
+
#
|
10
|
+
# hash.deep_transform_keys{ |key| key.to_s.upcase }
|
11
|
+
# # => { "PERSON" => { "NAME" => "Rob", "AGE" => "28" } }
|
12
|
+
def deep_transform_keys(&block)
|
13
|
+
result = {}
|
14
|
+
each do |key, value|
|
15
|
+
result[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys(&block) : value
|
16
|
+
end
|
17
|
+
result
|
18
|
+
end
|
19
|
+
|
20
|
+
# Destructively convert all keys by using the block operation.
|
21
|
+
# This includes the keys from the root hash and from all
|
22
|
+
# nested hashes.
|
23
|
+
def deep_transform_keys!(&block)
|
24
|
+
keys.each do |key|
|
25
|
+
value = delete(key)
|
26
|
+
self[yield(key)] = value.is_a?(Hash) ? value.deep_transform_keys!(&block) : value
|
27
|
+
end
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
# Return a new hash with all keys converted to strings.
|
32
|
+
# This includes the keys from the root hash and from all
|
33
|
+
# nested hashes.
|
34
|
+
#
|
35
|
+
# hash = { person: { name: 'Rob', age: '28' } }
|
36
|
+
#
|
37
|
+
# hash.deep_stringify_keys
|
38
|
+
# # => { "person" => { "name" => "Rob", "age" => "28" } }
|
39
|
+
def deep_stringify_keys
|
40
|
+
deep_transform_keys{ |key| key.to_s }
|
41
|
+
end
|
42
|
+
|
43
|
+
# Destructively convert all keys to strings.
|
44
|
+
# This includes the keys from the root hash and from all
|
45
|
+
# nested hashes.
|
46
|
+
def deep_stringify_keys!
|
47
|
+
deep_transform_keys!{ |key| key.to_s }
|
48
|
+
end
|
49
|
+
|
50
|
+
# Return a new hash with all keys converted to symbols, as long as
|
51
|
+
# they respond to +to_sym+. This includes the keys from the root hash
|
52
|
+
# and from all nested hashes.
|
53
|
+
#
|
54
|
+
# hash = { 'person' => { 'name' => 'Rob', 'age' => '28' } }
|
55
|
+
#
|
56
|
+
# hash.deep_symbolize_keys
|
57
|
+
# # => { person: { name: "Rob", age: "28" } }
|
58
|
+
def deep_symbolize_keys
|
59
|
+
deep_transform_keys{ |key| key.to_sym rescue key }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Destructively convert all keys to symbols, as long as they respond
|
63
|
+
# to +to_sym+. This includes the keys from the root hash and from all
|
64
|
+
# nested hashes.
|
65
|
+
def deep_symbolize_keys!
|
66
|
+
deep_transform_keys!{ |key| key.to_sym rescue key }
|
67
|
+
end
|
68
|
+
end
|
data/lib/saber/fetcher.rb
CHANGED
@@ -17,7 +17,7 @@ module Saber
|
|
17
17
|
def add(*files)
|
18
18
|
files.each { |file|
|
19
19
|
uri = "#{Rc.server.ftp}/#{file}"
|
20
|
-
gid = aria2_add([uri], :dir => Pa.dir2("#{Rc.
|
20
|
+
gid = aria2_add([uri], :dir => Pa.dir2("#{Rc.fetch.dir}/#{file}"))
|
21
21
|
Saber.ui.debug "DOWNLOAD #{gid} #{uri}"
|
22
22
|
}
|
23
23
|
end
|
@@ -28,7 +28,7 @@ module Saber
|
|
28
28
|
files = []
|
29
29
|
Net::SSH.start(Rc.server.host, Rc.server.user) do |s|
|
30
30
|
name = "'#{names.join("' '")}'"
|
31
|
-
cmd = "cd #{Rc.
|
31
|
+
cmd = "cd #{Rc.fetch.remote_dir} && find #{name} -type f"
|
32
32
|
|
33
33
|
rst = s.exec!(cmd)
|
34
34
|
if rst =~ /^find: `|^cd:cd:/
|
data/lib/saber/rc.rb
CHANGED
@@ -1,9 +1,15 @@
|
|
1
|
+
api_url = "http://saberapi.heroku.com"
|
1
2
|
scgi_server = "http://localhost/RPC2"
|
3
|
+
browser = [:firefox]
|
4
|
+
book_exts = %w[.epub .mobi .pdf .txt .html .djvu .chm .cbr .cbz .azw3]
|
5
|
+
book_formats = %w[epub mobi pdf txt html djvu chm cbr cbz azw3]
|
2
6
|
|
3
7
|
p:
|
4
8
|
root = Pa.expand("../../..", __FILE__)
|
5
9
|
home = Pa("~/.saber")
|
6
10
|
homerc = Pa("~/.saberrc")
|
11
|
+
database = Pa("~/.saber/database")
|
12
|
+
template = Pa("~/.saber/templates")
|
7
13
|
|
8
14
|
aria2:
|
9
15
|
rpc = "http://localhost:6800/rpc"
|
data/lib/saber/task.rb
CHANGED
@@ -18,6 +18,7 @@ module Saber
|
|
18
18
|
autoload :Clean, "saber/task/clean"
|
19
19
|
autoload :Make, "saber/task/make"
|
20
20
|
autoload :Upload, "saber/task/upload"
|
21
|
+
autoload :FindUploads, "saber/task/find_uploads"
|
21
22
|
|
22
23
|
@@tasks = {}
|
23
24
|
|
@@ -26,6 +27,8 @@ module Saber
|
|
26
27
|
|
27
28
|
class << self
|
28
29
|
def [](name)
|
30
|
+
require "saber/task/#{name}"
|
31
|
+
|
29
32
|
tasks[name]
|
30
33
|
end
|
31
34
|
end
|
data/lib/saber/task/base.rb
CHANGED
@@ -8,11 +8,16 @@ module Saber
|
|
8
8
|
Task.tasks[child.name.demodulize.underscore] = child
|
9
9
|
end
|
10
10
|
|
11
|
-
#
|
11
|
+
# delegate to #invoke
|
12
12
|
def invoke(*args)
|
13
13
|
new.invoke(*args)
|
14
14
|
end
|
15
15
|
end
|
16
|
+
|
17
|
+
def initialize(*)
|
18
|
+
super
|
19
|
+
self.options = self.options.dup
|
20
|
+
end
|
16
21
|
end
|
17
22
|
end
|
18
23
|
end
|
data/lib/saber/task/chd.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
+
require "sys/filesystem"
|
2
|
+
|
1
3
|
module Saber
|
2
4
|
module Task
|
3
5
|
class CHD < Base
|
4
6
|
|
5
7
|
desc "chd", "chd"
|
6
8
|
def chd(o={})
|
7
|
-
require "saber/tracker/chd"
|
8
9
|
t = Tracker["chd"].new
|
9
10
|
|
10
11
|
begin
|
@@ -14,7 +15,14 @@ module Saber
|
|
14
15
|
t.update_cache(true)
|
15
16
|
|
16
17
|
while true
|
17
|
-
|
18
|
+
# check free diskspace
|
19
|
+
s = Sys::Filesystem.stat(Rc.chd.diskspace_dir.p)
|
20
|
+
if s.block_size * s.blocks_free < Rc.chd.diskspace_limit
|
21
|
+
Saber.ui.say "::SKIP:: Reach low diskspace."
|
22
|
+
else
|
23
|
+
t.add_torrents
|
24
|
+
end
|
25
|
+
|
18
26
|
sleep Rc.chd.update_interval
|
19
27
|
end
|
20
28
|
rescue Errno::ETIMEDOUT, Mechanize::ResponseCodeError, SocketError
|
data/lib/saber/task/clean.rb
CHANGED
@@ -6,8 +6,8 @@ module Saber
|
|
6
6
|
class Clean < Base
|
7
7
|
desc "clean", "clean"
|
8
8
|
def clean
|
9
|
-
disk_files = Pa.ls2(Rc.
|
10
|
-
bt_files = Retort::Torrent.all.map{|t| Retort::Torrent.action("name", t.info_hash) }.map{|n| Pa.join2(Rc.
|
9
|
+
disk_files = Pa.ls2(Rc.clean.dir, absolute: true)
|
10
|
+
bt_files = Retort::Torrent.all.map{|t| Retort::Torrent.action("name", t.info_hash) }.map{|n| Pa.join2(Rc.clean.dir, n)}
|
11
11
|
|
12
12
|
(disk_files - bt_files).each { |file|
|
13
13
|
Pa.rm_r file, :verbose => true
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "tagen/core/io"
|
2
|
+
require "isbn"
|
3
|
+
|
4
|
+
module Saber
|
5
|
+
module Task
|
6
|
+
class FindUploads < Base
|
7
|
+
desc "find_uploads", "find_uploads"
|
8
|
+
def find_uploads(page="1")
|
9
|
+
bib = Tracker["bib"].new(options)
|
10
|
+
stp = Tracker["stp"].new(options)
|
11
|
+
dir = Rc._fetch("find_uploads.dir", ".")
|
12
|
+
bib.agent.pluggable_parser["application/x-bittorrent"] = Mechanize::DirectorySaver.save_to(dir.to_s)
|
13
|
+
bib.login
|
14
|
+
stp.login
|
15
|
+
|
16
|
+
bib.browse(page) {|torrent|
|
17
|
+
title, isbn, download_link, filenames, tags = torrent[:title], torrent[:isbn],
|
18
|
+
torrent[:download_link], torrent[:filenames], torrent[:tags]
|
19
|
+
|
20
|
+
generic_tags = convert_bibtags(tags)
|
21
|
+
|
22
|
+
begin
|
23
|
+
isbn = ISBN.thirteen(torrent[:isbn])
|
24
|
+
rescue ISBN::Invalid13DigitISBN # empty
|
25
|
+
next
|
26
|
+
end
|
27
|
+
|
28
|
+
if not stp.exists?(isbn: isbn)
|
29
|
+
bib.get(download_link)
|
30
|
+
|
31
|
+
Saber.ui.say "#{isbn} #{title}\n #{filenames.join("\n ")}"
|
32
|
+
File.append("list", "#{filenames[0]}:#{isbn}\n")
|
33
|
+
|
34
|
+
# local data
|
35
|
+
local_data = Pa.exists?("#{Rc.p.database}/#{isbn}.yml") ? YAML.load_file("#{Rc.p.database}/#{isbn}.yml") : {}
|
36
|
+
local_data.merge!({"tags" => generic_tags.join(", "), "bib.tags" => tags.join(", ")})
|
37
|
+
File.write "#{Rc.p.database}/#{isbn}.yml", YAML.dump(local_data)
|
38
|
+
end
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def convert_bibtags(tags)
|
45
|
+
tags.map{|v|
|
46
|
+
v.gsub(/ \(programming\)/i, '')
|
47
|
+
}.sort_by {|v|
|
48
|
+
%w[fiction nonfiction].include?(v) ? -1 : tags.index(v)
|
49
|
+
}
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
data/lib/saber/task/generate.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "isbn"
|
2
|
+
|
1
3
|
module Saber
|
2
4
|
module Task
|
3
5
|
# Generate meta data file.
|
@@ -5,34 +7,49 @@ module Saber
|
|
5
7
|
# Usage
|
6
8
|
# -----
|
7
9
|
#
|
8
|
-
# Task["generate"].invoke(:generate, ["
|
10
|
+
# Task["generate"].invoke(:generate, ["ebook", ["harry_potter", isbn], ...])
|
9
11
|
# > generate Hello.epub.yml data file.
|
10
12
|
#
|
11
13
|
class Generate < Base
|
12
14
|
include Thor::Actions
|
13
15
|
|
16
|
+
def self.source_paths
|
17
|
+
["#{Rc.p.home}/templates"]
|
18
|
+
end
|
19
|
+
|
14
20
|
source_root "#{Rc.p.root}/templates"
|
15
21
|
|
16
22
|
desc "generate", "generate"
|
17
|
-
def generate(
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
23
|
+
def generate(type, *filenames)
|
24
|
+
template_file = find_in_source_paths("#{type}.yml")
|
25
|
+
|
26
|
+
filenames.each {|filename, isbn|
|
27
|
+
isbn = ISBN.thirteen(isbn) rescue nil
|
28
|
+
Saber.ui.say "Populating {#{isbn}} #{filename} ..."
|
29
|
+
dest = "#{filename}.yml"
|
30
|
+
|
31
|
+
if isbn
|
32
|
+
require "yaml"
|
33
|
+
populate = {}
|
34
|
+
data = YAML.load_file(template_file)
|
35
|
+
data.merge! Book.populate(isbn, filename)
|
36
|
+
data.merge! YAML.load_file("#{Rc.p.database}/#{isbn}.yml") if Pa.exists?("#{Rc.p.database}/#{isbn}.yml")
|
37
|
+
|
38
|
+
# tags
|
39
|
+
if data["tags"]
|
40
|
+
Tracker.trackers.each {|name, tracker_class|
|
41
|
+
if data["#{name}.tags"].nil? and tracker_class.method_defined?(:convert_tags)
|
42
|
+
tracker = tracker_class.new(options)
|
43
|
+
data["#{name}.tags"] = tracker.convert_tags(*data["tags"].split(/, */)).join(", ")
|
44
|
+
end
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
create_file dest, YAML.dump(data)
|
49
|
+
else
|
50
|
+
copy_file template_file, dest
|
51
|
+
end
|
52
|
+
}
|
36
53
|
end
|
37
54
|
end
|
38
55
|
end
|