saber 1.1.1 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
@@ -0,0 +1,52 @@
|
|
1
|
+
module Saber
|
2
|
+
module Tracker2
|
3
|
+
class Gazelle < Base
|
4
|
+
def add_format(info)
|
5
|
+
unless info[:groupid]
|
6
|
+
Saber.ui.error "You must provide groupid."
|
7
|
+
return false
|
8
|
+
end
|
9
|
+
|
10
|
+
agent.goto "#{self.class::BASE_URL}/upload.php?groupid=#{info[:groupid]}"
|
11
|
+
check_login %r~/upload\.php~
|
12
|
+
|
13
|
+
form = agent.form(action: "")
|
14
|
+
fill_add_form(form, info)
|
15
|
+
form.submit() unless options["dry-run"]
|
16
|
+
|
17
|
+
if agent.url =~ %r~/upload\.php~
|
18
|
+
begin
|
19
|
+
error = agent.element(xpath: "//*[@id='content']/div[2]/p[2]").html
|
20
|
+
rescue Watir::Exception::UnknownObjectException # unable to locate element
|
21
|
+
end
|
22
|
+
error = ReverseMarkdown.parse(error) if error
|
23
|
+
Saber.ui.error "ERROR: #{error.to_s.strip}\n"
|
24
|
+
return false
|
25
|
+
else
|
26
|
+
return true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def new_upload(info)
|
31
|
+
agent.goto "#{self.class::BASE_URL}/upload.php"
|
32
|
+
check_login %r~/upload\.php~
|
33
|
+
|
34
|
+
form = agent.form(action: "")
|
35
|
+
fill_form(form, info)
|
36
|
+
form.submit() unless options["dry-run"]
|
37
|
+
|
38
|
+
if agent.url =~ %r~/upload\.php~
|
39
|
+
begin
|
40
|
+
error = agent.element(xpath: "//*[@id='content']/div[2]/p[2]").html
|
41
|
+
rescue Watir::Exception::UnknownObjectException # unable to locate element
|
42
|
+
end
|
43
|
+
error = ReverseMarkdown.parse(error) if error
|
44
|
+
Saber.ui.error "ERROR: #{error.to_s.strip}\n"
|
45
|
+
return false
|
46
|
+
else
|
47
|
+
return true
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
module Saber
|
2
|
+
module Tracker2
|
3
|
+
class STP < Gazelle
|
4
|
+
BASE_URL = "https://stopthepress.es"
|
5
|
+
|
6
|
+
TYPES = {
|
7
|
+
"magazine" => "Magazines",
|
8
|
+
"newspaper" => "Newspapers",
|
9
|
+
"manual" => "Manuals",
|
10
|
+
"journal" => "Journals",
|
11
|
+
"ebook" => "E-Books"
|
12
|
+
}
|
13
|
+
|
14
|
+
FIELDS = {
|
15
|
+
"magazine" => {
|
16
|
+
torrent_file: "//input[@name='file_input']",
|
17
|
+
name: "//input[@name='artists[]']",
|
18
|
+
title: "//input[@name='title']",
|
19
|
+
type: "//select[@name='releasetype']",
|
20
|
+
edition: "//input[@name='remaster_title']",
|
21
|
+
scene: "//input[@name='scene']",
|
22
|
+
format: "//select[@name='format']",
|
23
|
+
source: "//select[@name='media']",
|
24
|
+
retail: "//input[@name='flac_log']",
|
25
|
+
tags: "//input[@name='tags']",
|
26
|
+
image: "//input[@name='image']",
|
27
|
+
description: "//textarea[@name='album_desc']",
|
28
|
+
release_description: "//textarea[@name='release_desc']"
|
29
|
+
},
|
30
|
+
|
31
|
+
"newspaper" => {
|
32
|
+
torrent_file: "//input[@name='file_input']",
|
33
|
+
title: "//input[@name='title']",
|
34
|
+
tags: "//input[@name='tags']",
|
35
|
+
image: "//input[@name='image']",
|
36
|
+
description: "//textarea[@name='desc']"
|
37
|
+
},
|
38
|
+
|
39
|
+
"manual" => {
|
40
|
+
torrent_file: "//input[@name='file_input']",
|
41
|
+
title: "//input[@name='title']",
|
42
|
+
tags: "//input[@name='tags']",
|
43
|
+
image: "//input[@name='image']",
|
44
|
+
description: "//textarea[@name='desc']"
|
45
|
+
},
|
46
|
+
|
47
|
+
"journal" => {
|
48
|
+
torrent_file: "//input[@name='file_input']",
|
49
|
+
title: "//input[@name='title']",
|
50
|
+
tags: "//input[@name='tags']",
|
51
|
+
image: "//input[@name='image']",
|
52
|
+
description: "//textarea[@name='desc']"
|
53
|
+
},
|
54
|
+
|
55
|
+
"ebook" => {
|
56
|
+
torrent_file: "//input[@name='file_input']",
|
57
|
+
title: "//input[@name='title']",
|
58
|
+
publisher: "//input[@name='record_label']",
|
59
|
+
isbn: "//input[@name='catalogue_number']",
|
60
|
+
year: "//input[@name='year']",
|
61
|
+
#edition: "//input[@name='remaster_title']",
|
62
|
+
#edition_year: "//input[@name='remaster_year']",
|
63
|
+
#edition_publisher: "//input[@name='remaster_record_label']",
|
64
|
+
#edition_isbn: "//input[@name='remaster_catalogue_number']",
|
65
|
+
scene: "//input[@name='scene']",
|
66
|
+
format: "//select[@name='format']",
|
67
|
+
source: "//select[@name='media']",
|
68
|
+
retail: "//input[@name='flac_log']",
|
69
|
+
tags: "//input[@name='tags']",
|
70
|
+
image: "//input[@name='image']",
|
71
|
+
description: "//textarea[@name='album_desc']",
|
72
|
+
release_description: "//textarea[@name='release_desc']"
|
73
|
+
},
|
74
|
+
}
|
75
|
+
|
76
|
+
ADD_FIELDS = {
|
77
|
+
"ebook" => {
|
78
|
+
torrent_file: "//input[@name='file_input']",
|
79
|
+
edition: "//input[@name='remaster_title']",
|
80
|
+
year: "//input[@name='remaster_year']",
|
81
|
+
publisher: "//input[@name='remaster_record_label']",
|
82
|
+
isbn: "//input[@name='remaster_catalogue_number']",
|
83
|
+
scene: "//input[@name='scene']",
|
84
|
+
format: "//select[@name='format']",
|
85
|
+
source: "//select[@name='media']",
|
86
|
+
retail: "//input[@name='flac_log']",
|
87
|
+
release_description: "//textarea[@name='release_desc']"
|
88
|
+
},
|
89
|
+
}
|
90
|
+
|
91
|
+
def fill_add_form(form, info)
|
92
|
+
# ebook: edition
|
93
|
+
if %w[ebook].include? info[:upload_type]
|
94
|
+
form.checkbox(name: "remaster").set(true)
|
95
|
+
end
|
96
|
+
|
97
|
+
ADD_FIELDS[info[:upload_type]].each {|key, selector|
|
98
|
+
form.quick_set(selector, check_value!(info[key]))
|
99
|
+
}
|
100
|
+
end
|
101
|
+
|
102
|
+
def fill_form(form, info)
|
103
|
+
form.select(name: "type").select info[:upload_type2]
|
104
|
+
form.input(name: "artists[]").wait_while_present unless info[:upload_type] == "magazine"
|
105
|
+
sleep 0.1
|
106
|
+
|
107
|
+
# magazine. edition
|
108
|
+
if %[magazine].include? info[:upload_type]
|
109
|
+
form.checkbox(name: "remaster").set(true)
|
110
|
+
end
|
111
|
+
|
112
|
+
# edition, authors
|
113
|
+
if %w[ebook].include? info[:upload_type]
|
114
|
+
#form.checkbox(name: "remaster").set(true)
|
115
|
+
|
116
|
+
(info[:authors].split(",").length - 1).times {
|
117
|
+
form.a(text: '+').click
|
118
|
+
}
|
119
|
+
end
|
120
|
+
|
121
|
+
FIELDS[info[:upload_type]].each {|key, selector|
|
122
|
+
form.quick_set(selector, info[key])
|
123
|
+
}
|
124
|
+
|
125
|
+
# authors
|
126
|
+
if %w[ebook].include? info[:upload_type]
|
127
|
+
info[:authors].split(",").each.with_index { |author, i|
|
128
|
+
form.text_fields(name: "authors[]")[i].set author
|
129
|
+
}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# vim: fdn=4
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Saber
|
2
|
+
module Tracker2
|
3
|
+
class What < Base
|
4
|
+
BASE_URL = "https://what.cd"
|
5
|
+
|
6
|
+
TYPES = {
|
7
|
+
"music" => "Musics",
|
8
|
+
"application" => "Applications",
|
9
|
+
"ebook" => "E-Books",
|
10
|
+
"audiobook" => "Audiobooks",
|
11
|
+
"elearning-video" => "E-Learning Videos",
|
12
|
+
"comedy" => "Comedy",
|
13
|
+
"comic" => "Comics"
|
14
|
+
}
|
15
|
+
|
16
|
+
FIELDS = {
|
17
|
+
"ebook" => {
|
18
|
+
torrent_file: "//input[@name='file_input']",
|
19
|
+
title: "//input[@name='title']",
|
20
|
+
tags: "//input[@name='tags']",
|
21
|
+
image: "//input[@name='image']",
|
22
|
+
description: "//textarea[@name='desc']"
|
23
|
+
}
|
24
|
+
}
|
25
|
+
|
26
|
+
def do_upload(file, info)
|
27
|
+
#path = info["group_id"] ? "/upload.php?group_id=#{info['group_id']}" : "/upload.php"
|
28
|
+
|
29
|
+
agent.goto "#{BASE_URL}/upload.php"
|
30
|
+
check_login %r~/upload\.php~
|
31
|
+
|
32
|
+
form = agent.form(action: "")
|
33
|
+
form.select(name: "type").select info[:type2]
|
34
|
+
form.input(value: "Find Info").wait_while_present unless info[:type] == "music"
|
35
|
+
sleep 0.1
|
36
|
+
|
37
|
+
FIELDS[info[:type]].each {|key, selector|
|
38
|
+
form.quick_set(selector, info[key])
|
39
|
+
}
|
40
|
+
|
41
|
+
form.submit()
|
42
|
+
|
43
|
+
if agent.url =~ %r~/upload\.php~
|
44
|
+
return false
|
45
|
+
else
|
46
|
+
return true
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/saber/version.rb
CHANGED
@@ -0,0 +1,95 @@
|
|
1
|
+
# Cookies
|
2
|
+
module Watir
|
3
|
+
class Browser
|
4
|
+
# Read cookies from Mozilla cookies.txt-style IO stream
|
5
|
+
#
|
6
|
+
# @param file [IO,String]
|
7
|
+
#
|
8
|
+
def load_cookies(file)
|
9
|
+
now = ::Time.now
|
10
|
+
|
11
|
+
io = case file
|
12
|
+
when String
|
13
|
+
open(file)
|
14
|
+
else
|
15
|
+
file
|
16
|
+
end
|
17
|
+
|
18
|
+
io.each_line do |line|
|
19
|
+
line.chomp!
|
20
|
+
line.gsub!(/#.+/, '')
|
21
|
+
fields = line.split("\t")
|
22
|
+
|
23
|
+
next if fields.length != 7
|
24
|
+
|
25
|
+
name, value, domain, for_domain, path, secure, version = fields[5], fields[6],
|
26
|
+
fields[0], (fields[1] == "TRUE"), fields[2], (fields[3] == "TRUE"), 0
|
27
|
+
|
28
|
+
expires_seconds = fields[4].to_i
|
29
|
+
expires = (expires_seconds == 0) ? nil : ::Time.at(expires_seconds)
|
30
|
+
next if expires and (expires < now)
|
31
|
+
|
32
|
+
cookies.add(name, value, domain: domain, path: path, expires: expires, secure: secure)
|
33
|
+
end
|
34
|
+
|
35
|
+
io.close if String === file
|
36
|
+
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
# Write cookies to Mozilla cookies.txt-style IO stream
|
41
|
+
#
|
42
|
+
# @param file [IO,String]
|
43
|
+
def dump_cookies(file)
|
44
|
+
io = case file
|
45
|
+
when String
|
46
|
+
open(file, "w")
|
47
|
+
else
|
48
|
+
file
|
49
|
+
end
|
50
|
+
|
51
|
+
cookies.to_a.each do |cookie|
|
52
|
+
io.puts([
|
53
|
+
cookie[:domain],
|
54
|
+
"FALSE", # for_domain
|
55
|
+
cookie[:path],
|
56
|
+
cookie[:secure] ? "TRUE" : "FALSE",
|
57
|
+
cookie[:expires].to_i.to_s,
|
58
|
+
cookie[:name],
|
59
|
+
cookie[:value]
|
60
|
+
].join("\t"))
|
61
|
+
end
|
62
|
+
|
63
|
+
io.close if String === file
|
64
|
+
|
65
|
+
self
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# Element
|
71
|
+
module Watir
|
72
|
+
class Element
|
73
|
+
# quick set value.
|
74
|
+
#
|
75
|
+
# @example
|
76
|
+
#
|
77
|
+
# form = browser.form
|
78
|
+
# form.quick_set("//input[@name='value']", "hello")
|
79
|
+
# form.quick_set("//input[@name='check']", true)
|
80
|
+
# form.quick_set("//select[@name='foo']", "Bar")
|
81
|
+
# form.quick_set("//textarea[@name='foo']", "bar")
|
82
|
+
#
|
83
|
+
def quick_set(selector, value)
|
84
|
+
elem = element(xpath: selector).to_subtype
|
85
|
+
case elem.tag_name
|
86
|
+
when "input"
|
87
|
+
elem.set value
|
88
|
+
when "select"
|
89
|
+
elem.select value
|
90
|
+
when "textarea"
|
91
|
+
elem.set value
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
data/saber.gemspec
CHANGED
@@ -20,7 +20,7 @@ A complete solution for PT users.
|
|
20
20
|
s.add_dependency "pd", ">= 0"
|
21
21
|
s.add_dependency "tagen", "~> 2.0.1"
|
22
22
|
s.add_dependency "optimism", "~> 3.3.1"
|
23
|
-
s.add_dependency "pa", "~> 1.3.
|
23
|
+
s.add_dependency "pa", "~> 1.3.3"
|
24
24
|
s.add_dependency "retort", "~> 0.0.6"
|
25
25
|
s.add_dependency "thor", "~> 0.16.0"
|
26
26
|
s.add_dependency "net-ssh", "~> 2.5.2"
|
@@ -28,4 +28,9 @@ A complete solution for PT users.
|
|
28
28
|
s.add_dependency "mechanize", "~> 2.5.1"
|
29
29
|
s.add_dependency "highline", "~> 1.6.14"
|
30
30
|
s.add_dependency "reverse_markdown", "~> 0.3.0"
|
31
|
+
s.add_dependency "watir", "~> 4.0.1"
|
32
|
+
s.add_dependency "faraday", "~> 0.8.4"
|
33
|
+
s.add_dependency "faraday_middleware", "~> 0.8.8"
|
34
|
+
s.add_dependency "isbn", "~> 2.0.7"
|
35
|
+
s.add_dependency "sys-filesystem", "~> 1.0.0"
|
31
36
|
end
|
data/spec/saber/task_spec.rb
CHANGED
data/templates/_saberrc
CHANGED
@@ -1,9 +1,23 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
#browser = [:firefox, {profile: "jobs"}]
|
2
|
+
|
3
|
+
make:
|
4
|
+
#dir = Pa(".") # move to directory.
|
5
|
+
#watch = Pa("~/bt/watch") # copy to local watch directory.
|
6
|
+
#remote_watch = Pa("bt/watch") # ssh copy to remote watch directory.
|
7
|
+
|
8
|
+
find_uploads:
|
9
|
+
#dir = Pa("~/bt/watch")
|
10
|
+
|
11
|
+
upload:
|
12
|
+
#move_yml = Pa("done")
|
13
|
+
#move_torrent = Pa("done")
|
14
|
+
|
15
|
+
fetch:
|
16
|
+
dir = Pa("~/downloads") # aria2 download directory.
|
17
|
+
remote_dir = Pa("~/bt") # remote bt download directory.
|
18
|
+
|
19
|
+
clean:
|
20
|
+
dir = Pa("~/bt") # local bt download directory.
|
7
21
|
|
8
22
|
server:
|
9
23
|
user = "x" # seedbox login name
|
@@ -13,8 +27,15 @@ server:
|
|
13
27
|
username = "foo" # default username for login into site.
|
14
28
|
|
15
29
|
bib:
|
16
|
-
username = "bar"
|
17
|
-
announce_url = "x"
|
30
|
+
#username = "bar" # per-site username. use default username if not set.
|
31
|
+
announce_url = "x"
|
32
|
+
|
33
|
+
chd:
|
34
|
+
passkey = "x"
|
35
|
+
dir = Pa("~/bt/watch")
|
36
|
+
update_interval = 15*60
|
37
|
+
diskspace_dir = Pa("/")
|
38
|
+
diskspace_limit = 20.gigabytes
|
18
39
|
|
19
40
|
# [saber-fetch]
|
20
41
|
server:
|