saber 0.0.7 → 1.0.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/.travis.yml +4 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +18 -8
- data/Gemfile.lock +72 -33
- data/Guardfile +4 -0
- data/README.md +192 -52
- data/Rakefile +46 -0
- data/bin/1saber +5 -3
- data/bin/saber +4 -0
- data/bin/saber.bib +3 -0
- data/bin/saber.what +3 -0
- data/extconf.rb +4 -1
- data/lib/saber.rb +7 -13
- data/lib/saber/autofetcher.rb +8 -0
- data/lib/saber/autofetcher/client.rb +66 -0
- data/lib/saber/autofetcher/server.rb +78 -0
- data/lib/saber/cli.rb +65 -26
- data/lib/saber/{downloader.rb → fetcher.rb} +9 -11
- data/lib/saber/mechanize_ext.rb +136 -0
- data/lib/saber/rc.rb +3 -21
- data/lib/saber/task.rb +26 -11
- data/lib/saber/task/base.rb +18 -0
- data/lib/saber/task/clean.rb +18 -0
- data/lib/saber/task/generate.rb +38 -0
- data/lib/saber/task/make.rb +44 -0
- data/lib/saber/task/send.rb +21 -0
- data/lib/saber/task/upload.rb +37 -0
- data/lib/saber/tracker.rb +28 -0
- data/lib/saber/tracker/base.rb +115 -2
- data/lib/saber/tracker/bb.rb +244 -0
- data/lib/saber/tracker/bib.rb +225 -0
- data/lib/saber/tracker/ptp.rb +100 -0
- data/lib/saber/tracker/what.rb +55 -7
- data/lib/saber/ui.rb +65 -22
- data/lib/saber/version.rb +1 -1
- data/rutorrent/init.js +8 -9
- data/rutorrent/plugin.info +1 -1
- data/saber.gemspec +14 -10
- data/spec/data/_saber/.gitkeep +0 -0
- data/spec/saber/downloader_spec.rb +5 -2
- data/spec/saber/task_spec.rb +16 -0
- data/spec/saber/tracker/bib_spec.rb +24 -0
- data/spec/spec_helper.rb +25 -0
- data/templates/_saberrc +36 -0
- data/templates/bb/anime.yml +6 -0
- data/templates/bb/application.yml +7 -0
- data/templates/bb/audiobook.yml +12 -0
- data/templates/bb/comic.yml +7 -0
- data/templates/bb/documentary.yml +26 -0
- data/templates/bb/ebook.yml +8 -0
- data/templates/bb/elearning_video.yml +7 -0
- data/templates/bb/game_console.yml +6 -0
- data/templates/bb/game_pc.yml +6 -0
- data/templates/bb/magazine.yml +6 -0
- data/templates/bb/misc.yml +6 -0
- data/templates/bb/movie.yml +26 -0
- data/templates/bb/music.yml +21 -0
- data/templates/bb/tv.yml +6 -0
- data/templates/bib/application.yml +9 -0
- data/templates/bib/article.yml +18 -0
- data/templates/bib/audiobook.yml +17 -0
- data/templates/bib/comic.yml +22 -0
- data/templates/bib/ebook.yml +20 -0
- data/templates/bib/journal.yml +18 -0
- data/templates/bib/magazine.yml +18 -0
- data/templates/ptp/movie.yml +63 -0
- data/templates/ptp/movie_add.yml +35 -0
- data/templates/what/ebook.yml +6 -0
- data/templates/what/music.yml +2 -0
- data/templates/what/music_add.yml +2 -0
- metadata +182 -26
- data/doc/Development.md +0 -3
- data/lib/saber/client.rb +0 -58
- data/lib/saber/server.rb +0 -70
- data/saber.watchr +0 -23
@@ -0,0 +1,244 @@
|
|
1
|
+
module Saber
|
2
|
+
module Tracker
|
3
|
+
# DOESN'T WORK for mechanize does not support javascript.
|
4
|
+
class BB < Base
|
5
|
+
@@BASE_URL = "https://baconbits.org"
|
6
|
+
|
7
|
+
@@LOGIN_CHECK_PATH = "/inbox.php"
|
8
|
+
|
9
|
+
FIELDS = {
|
10
|
+
"Musics" => {
|
11
|
+
"file_input" => :file_upload,
|
12
|
+
"type" => :select_list,
|
13
|
+
"artist" => :text,
|
14
|
+
"title" => :text,
|
15
|
+
"remaster" => :check_box,
|
16
|
+
"year" => :text,
|
17
|
+
"scene" => :checkbox,
|
18
|
+
"format" => :select_list,
|
19
|
+
"bitrate" => :select_list,
|
20
|
+
"media" => :select_list,
|
21
|
+
"tags" => :text,
|
22
|
+
"image" => :text,
|
23
|
+
"album_desc" => :text,
|
24
|
+
"release_desc" => :text,
|
25
|
+
},
|
26
|
+
|
27
|
+
"Applications" => {
|
28
|
+
"file_input" => :file_upload,
|
29
|
+
"type" => :select_list,
|
30
|
+
"title" => :text,
|
31
|
+
"tags" => :text,
|
32
|
+
"desc" => :text,
|
33
|
+
"image" => :text,
|
34
|
+
"scene" => :checkbox,
|
35
|
+
},
|
36
|
+
|
37
|
+
"E-Books" => {
|
38
|
+
"file_input" => :file_upload,
|
39
|
+
"type" => :select_list,
|
40
|
+
"title" => :text,
|
41
|
+
"tags" => :text,
|
42
|
+
"isbn" => :text,
|
43
|
+
"desc" => :text,
|
44
|
+
"image" => :text,
|
45
|
+
"scene" => :checkbox,
|
46
|
+
},
|
47
|
+
|
48
|
+
"Audiobooks" => {
|
49
|
+
"file_input" => :file_upload,
|
50
|
+
"type" => :select_list,
|
51
|
+
"title" => :text,
|
52
|
+
"year" => :text,
|
53
|
+
"format" => :select_list,
|
54
|
+
"bitrate" => :select_list,
|
55
|
+
"tags" => :text,
|
56
|
+
"image" => :text,
|
57
|
+
"album_desc" => :text,
|
58
|
+
"release_desc" => :text,
|
59
|
+
},
|
60
|
+
|
61
|
+
"E-Learning Videos" => {
|
62
|
+
"type" => :select_list,
|
63
|
+
"title" => :text,
|
64
|
+
"tags" => :text,
|
65
|
+
"isbn" => :text,
|
66
|
+
"desc" => :text,
|
67
|
+
"image" => :text,
|
68
|
+
"scene" => :checkbox,
|
69
|
+
},
|
70
|
+
|
71
|
+
"Magazines" => {
|
72
|
+
"file_input" => :file_upload,
|
73
|
+
"type" => :select_list,
|
74
|
+
"title" => :text,
|
75
|
+
"tags" => :text,
|
76
|
+
"desc" => :text,
|
77
|
+
"image" => :text,
|
78
|
+
"scene" => :checkbox,
|
79
|
+
},
|
80
|
+
|
81
|
+
"Comics" => {
|
82
|
+
"file_input" => :file_upload,
|
83
|
+
"type" => :select_list,
|
84
|
+
"title" => :text,
|
85
|
+
"tags" => :text,
|
86
|
+
"isbn" => :text,
|
87
|
+
"desc" => :text,
|
88
|
+
"image" => :text,
|
89
|
+
"scene" => :checkbox,
|
90
|
+
},
|
91
|
+
|
92
|
+
"Anime" => {
|
93
|
+
"file_input" => :file_upload,
|
94
|
+
"type" => :select_list,
|
95
|
+
"title" => :text,
|
96
|
+
"tags" => :text,
|
97
|
+
"desc" => :text,
|
98
|
+
"image" => :text,
|
99
|
+
"scene" => :checkbox,
|
100
|
+
},
|
101
|
+
|
102
|
+
"Movies" => {
|
103
|
+
"file_input" => :file_upload,
|
104
|
+
"type" => :select_list,
|
105
|
+
"title" => :text,
|
106
|
+
"source" => :select_list,
|
107
|
+
"videoformat" => :select_list,
|
108
|
+
"audioformat" => :select_list,
|
109
|
+
"container" => :select_list,
|
110
|
+
"resolution" => :select_list,
|
111
|
+
"remaster_title" => :text,
|
112
|
+
"year" => :text,
|
113
|
+
"tags" => :text,
|
114
|
+
"desc" => :text,
|
115
|
+
"release_info" => :text,
|
116
|
+
"screenshot1" => :text,
|
117
|
+
"screenshot2" => :text,
|
118
|
+
"image" => :text,
|
119
|
+
"scene" => :checkbox,
|
120
|
+
},
|
121
|
+
|
122
|
+
"TV" => {
|
123
|
+
"file_input" => :file_upload,
|
124
|
+
"type" => :select_list,
|
125
|
+
"title" => :text,
|
126
|
+
"tags" => :text,
|
127
|
+
"desc" => :text,
|
128
|
+
"image" => :text,
|
129
|
+
"scene" => :checkbox,
|
130
|
+
},
|
131
|
+
|
132
|
+
"Games - PC" => {
|
133
|
+
"file_input" => :file_upload,
|
134
|
+
"type" => :select_list,
|
135
|
+
"title" => :text,
|
136
|
+
"tags" => :text,
|
137
|
+
"desc" => :text,
|
138
|
+
"image" => :text,
|
139
|
+
"scene" => :checkbox,
|
140
|
+
},
|
141
|
+
|
142
|
+
"Games - Console" => {
|
143
|
+
"file_input" => :file_upload,
|
144
|
+
"type" => :select_list,
|
145
|
+
"title" => :text,
|
146
|
+
"tags" => :text,
|
147
|
+
"desc" => :text,
|
148
|
+
"image" => :text,
|
149
|
+
"scene" => :checkbox,
|
150
|
+
},
|
151
|
+
|
152
|
+
"Documentaries" => {
|
153
|
+
"file_input" => :file_upload,
|
154
|
+
"type" => :select_list,
|
155
|
+
"title" => :text,
|
156
|
+
"source" => :select_list,
|
157
|
+
"videoformat" => :select_list,
|
158
|
+
"audioformat" => :select_list,
|
159
|
+
"container" => :select_list,
|
160
|
+
"resolution" => :select_list,
|
161
|
+
"remaster_title" => :text,
|
162
|
+
"year" => :text,
|
163
|
+
"tags" => :text,
|
164
|
+
"desc" => :text,
|
165
|
+
"release_info" => :text,
|
166
|
+
"screenshot1" => :text,
|
167
|
+
"screenshot2" => :text,
|
168
|
+
"image" => :text,
|
169
|
+
"scene" => :checkbox,
|
170
|
+
},
|
171
|
+
|
172
|
+
"Misc" => {
|
173
|
+
"file_input" => :file_upload,
|
174
|
+
"type" => :select_list,
|
175
|
+
"title" => :text,
|
176
|
+
"tags" => :text,
|
177
|
+
"desc" => :text,
|
178
|
+
"image" => :text,
|
179
|
+
"scene" => :checkbox,
|
180
|
+
},
|
181
|
+
}
|
182
|
+
|
183
|
+
# We have below attributes:
|
184
|
+
#
|
185
|
+
# * agent: a Mechanize object
|
186
|
+
# * site_name: "bib"
|
187
|
+
|
188
|
+
# Upload one torrent file to the site.
|
189
|
+
#
|
190
|
+
# @param [String] file a filename
|
191
|
+
# @param [Optimism] info comes from <file>.yml data file.
|
192
|
+
#
|
193
|
+
# @return [Boolean] result-code
|
194
|
+
def do_upload(file, info)
|
195
|
+
info["file_input"] = "#{file}.torrent"
|
196
|
+
|
197
|
+
agent.get("/upload.php") {|p|
|
198
|
+
ret = p.form_with(action: "") {|f|
|
199
|
+
FIELDS[info.type].each {|k,t|
|
200
|
+
f.set(t, k, info[k])
|
201
|
+
}
|
202
|
+
}.submit
|
203
|
+
|
204
|
+
if ret.uri.path == "/upload.php"
|
205
|
+
msg = ReverseMarkdown.parse(ret.at("//*[@id='content']/div[2]/p[2]")
|
206
|
+
Saber.ui.error "ERROR:\n#{msg}"
|
207
|
+
return false
|
208
|
+
else
|
209
|
+
return true
|
210
|
+
end
|
211
|
+
}
|
212
|
+
end
|
213
|
+
|
214
|
+
protected
|
215
|
+
|
216
|
+
# Attpened to login the site with username and password. this happens
|
217
|
+
# after login failed with cookie.
|
218
|
+
def do_login_with_username(username)
|
219
|
+
agent.get("/login.php") {|p|
|
220
|
+
ret = p.form_with(action: "login.php" ) {|f|
|
221
|
+
unless f
|
222
|
+
Saber.ui.error! p.at("//body").inner_text
|
223
|
+
end
|
224
|
+
|
225
|
+
f.username = username || ask("Username: ")
|
226
|
+
f.password = ask("Password: "){|q| q.echo = false}
|
227
|
+
f.checkbox(name: "keeplogged").check
|
228
|
+
}.submit
|
229
|
+
|
230
|
+
# error
|
231
|
+
if ret.uri.path == "/login.php"
|
232
|
+
msg = ret.at("//*[@id='loginform']/font[2]").inner_text
|
233
|
+
Saber.ui.error "Failed. You have #{msg} attempts remaining."
|
234
|
+
return false
|
235
|
+
else
|
236
|
+
return true
|
237
|
+
end
|
238
|
+
}
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# vim: fdn=4
|
@@ -0,0 +1,225 @@
|
|
1
|
+
module Saber
|
2
|
+
module Tracker
|
3
|
+
class BIB < Base
|
4
|
+
@@POPULATE_TYPES = %w[ebook audiobook]
|
5
|
+
|
6
|
+
@@BASE_URL = "http://bibliotik.org"
|
7
|
+
|
8
|
+
# used by login-with-cookie to check if it's succeed.
|
9
|
+
@@LOGIN_CHECK_PATH = "/conversations"
|
10
|
+
|
11
|
+
FIELDS = {
|
12
|
+
"applications" => {
|
13
|
+
"TorrentFile" => :file_upload,
|
14
|
+
"Scene" => :checkbox,
|
15
|
+
"Title" => :text,
|
16
|
+
"Tags" => :text,
|
17
|
+
"Image" => :text,
|
18
|
+
"Description" => :text,
|
19
|
+
"Anonymous" => :checkbox,
|
20
|
+
"Notify" => :checkbox,
|
21
|
+
},
|
22
|
+
|
23
|
+
"articles" => {
|
24
|
+
"TorrentFile" => :file_upload,
|
25
|
+
"Authors" => :text,
|
26
|
+
"Title" => :text,
|
27
|
+
"Pages" => :text,
|
28
|
+
"Year" => :text,
|
29
|
+
"YearTo" => :text,
|
30
|
+
"Complete" => :checkbox,
|
31
|
+
"Format" => :text,
|
32
|
+
"Language" => :text,
|
33
|
+
"Tags" => :text,
|
34
|
+
"Image" => :text,
|
35
|
+
"Description" => :text,
|
36
|
+
"Anonymous" => :checkbox,
|
37
|
+
"Notify" => :checkbox,
|
38
|
+
},
|
39
|
+
|
40
|
+
|
41
|
+
"autobooks" => {
|
42
|
+
"TorrentFile" => :file_upload,
|
43
|
+
"Authors" => :text,
|
44
|
+
"Title" => :text,
|
45
|
+
"ISBN" => :text,
|
46
|
+
"Publishers" => :text,
|
47
|
+
"Year" => :text,
|
48
|
+
"Format" => :select_list_text,
|
49
|
+
"Language" => :select_list_text,
|
50
|
+
"Tags" => :text,
|
51
|
+
"Image" => :text,
|
52
|
+
"Description" => :text,
|
53
|
+
"Anonymous" => :checkbox,
|
54
|
+
"Notify" => :checkbox,
|
55
|
+
},
|
56
|
+
|
57
|
+
"comics" => {
|
58
|
+
"TorrentFile" => :file_upload,
|
59
|
+
"Scene" => :checkbox,
|
60
|
+
"Authors" => :text,
|
61
|
+
"Artists" => :text,
|
62
|
+
"Title" => :text,
|
63
|
+
"Publishers" => :text,
|
64
|
+
"Pages" => :text,
|
65
|
+
"Year" => :text,
|
66
|
+
"YearTo" => :text,
|
67
|
+
"Complete" => :checkbox,
|
68
|
+
"Format" => :select_list_text,
|
69
|
+
"Language" => :select_list_text,
|
70
|
+
"Tags" => :text,
|
71
|
+
"Image" => :text,
|
72
|
+
"Description" => :text,
|
73
|
+
"Anonymous" => :checkbox,
|
74
|
+
"Notify" => :checkbox,
|
75
|
+
},
|
76
|
+
|
77
|
+
"ebooks" => {
|
78
|
+
"TorrentFile" => :file_upload,
|
79
|
+
"Scene" => :checkbox,
|
80
|
+
"Authors" => :text,
|
81
|
+
"Title" => :text,
|
82
|
+
"ISBN" => :text,
|
83
|
+
"Publishers" => :text,
|
84
|
+
"Pages" => :text,
|
85
|
+
"Year" => :text,
|
86
|
+
"Format" => :select_list_text,
|
87
|
+
"Language" => :select_list_text,
|
88
|
+
"Retail" => :checkbox,
|
89
|
+
"Tags" => :text,
|
90
|
+
"Image" => :text,
|
91
|
+
"Description" => :text,
|
92
|
+
"Anonymous" => :checkbox,
|
93
|
+
"Notify" => :checkbox,
|
94
|
+
},
|
95
|
+
|
96
|
+
"journals" => {
|
97
|
+
"TorrentFile" => :file_upload,
|
98
|
+
"Scene" => :checkbox,
|
99
|
+
"Title" => :text,
|
100
|
+
"Pages" => :text,
|
101
|
+
"Year" => :text,
|
102
|
+
"YearTo" => :text,
|
103
|
+
"Complete" => :checkbox,
|
104
|
+
"Format" => :select_list_text,
|
105
|
+
"Language" => :select_list_text,
|
106
|
+
"Tags" => :text,
|
107
|
+
"Image" => :text,
|
108
|
+
"Description" => :text,
|
109
|
+
"Anonymous" => :checkbox,
|
110
|
+
"Notify" => :checkbox,
|
111
|
+
},
|
112
|
+
|
113
|
+
"magazines" => {
|
114
|
+
"TorrentFile" => :file_upload,
|
115
|
+
"Scene" => :checkbox,
|
116
|
+
"Title" => :text,
|
117
|
+
"Pages" => :text,
|
118
|
+
"Year" => :text,
|
119
|
+
"YearTo" => :text,
|
120
|
+
"Complete" => :checkbox,
|
121
|
+
"Format" => :select_list_text,
|
122
|
+
"Language" => :select_list_text,
|
123
|
+
"Tags" => :text,
|
124
|
+
"Image" => :text,
|
125
|
+
"Description" => :text,
|
126
|
+
"Anonymous" => :checkbox,
|
127
|
+
"Notify" => :checkbox,
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
# We have below attributes:
|
132
|
+
#
|
133
|
+
# * agent: a Mechanize object
|
134
|
+
# * site_name: "bib"
|
135
|
+
|
136
|
+
# Upload one torrent file to the site.
|
137
|
+
#
|
138
|
+
# @param [String] file a filename
|
139
|
+
# @param [Optimism] info comes from <file>.yml data file.
|
140
|
+
#
|
141
|
+
# @return [Boolean] result-code
|
142
|
+
def do_upload(file, info)
|
143
|
+
info["TorrentFile"] = "#{file}.torrent"
|
144
|
+
|
145
|
+
agent.get("/upload/#{info.type}") {|p|
|
146
|
+
ret = p.form_with(action: "") {|f|
|
147
|
+
FIELDS[info.type].each {|k,t|
|
148
|
+
f.set(t, "#{k}Field", info[k])
|
149
|
+
}
|
150
|
+
}.submit
|
151
|
+
|
152
|
+
# error if return path is "/upload/<type>"
|
153
|
+
if ret.uri.path == "/upload/#{info.type}"
|
154
|
+
msg = nil
|
155
|
+
if (err=ret.at("//*[@id='formerrorlist']"))
|
156
|
+
# convert html to markdown for pretty print.
|
157
|
+
msg = ReverseMarkdown.parse(err)
|
158
|
+
else
|
159
|
+
msg = ret.body
|
160
|
+
end
|
161
|
+
Saber.ui.error "ERROR:\n#{msg}"
|
162
|
+
return false
|
163
|
+
else
|
164
|
+
return true
|
165
|
+
end
|
166
|
+
}
|
167
|
+
end
|
168
|
+
|
169
|
+
protected
|
170
|
+
|
171
|
+
# Attpened to login the site with username and password. this happens
|
172
|
+
# after login failed with cookie.
|
173
|
+
def do_login_with_username(username)
|
174
|
+
agent.get("/login") {|p|
|
175
|
+
ret = p.form_with(action: "login" ) {|f|
|
176
|
+
# error. e.g. temporary disabled for failed logining exceed maxmium count.
|
177
|
+
unless f
|
178
|
+
# print error in red color and exit the program.
|
179
|
+
Saber.ui.error! p.at("//body").inner_text
|
180
|
+
end
|
181
|
+
|
182
|
+
f.username = username || ask("Username: ")
|
183
|
+
f.password = ask("Password: "){|q| q.echo = false}
|
184
|
+
f.checkbox(name: "keeplogged").check
|
185
|
+
}.submit
|
186
|
+
|
187
|
+
# error
|
188
|
+
if ret.uri.path == "/login"
|
189
|
+
msg = ret.at("//body/center").inner_text
|
190
|
+
Saber.ui.error "Failed. #{msg}"
|
191
|
+
return false
|
192
|
+
else
|
193
|
+
return true
|
194
|
+
end
|
195
|
+
}
|
196
|
+
end
|
197
|
+
|
198
|
+
def populate_ebook(isbn)
|
199
|
+
populate_isbn("ebooks", isbn)
|
200
|
+
end
|
201
|
+
|
202
|
+
def populate_audiobook(isbn)
|
203
|
+
populate_isbn("audiobooks", isbn)
|
204
|
+
end
|
205
|
+
|
206
|
+
def populate_isbn(type, isbn)
|
207
|
+
headers = {"X-Requested-With" => "XMLHttpRequest", "Content-Type" => "application/json; charset=utf-8", "Accept" => "application/json, text/javascript, */*"}
|
208
|
+
page = agent.get("/upload/#{type}")
|
209
|
+
authkey = page.at("//input[@name='authkey']")["value"]
|
210
|
+
|
211
|
+
params = {isbn: isbn, authkey: authkey}
|
212
|
+
ret = JSON.parse(agent.get("/isbnlookup", params, nil, headers).body)
|
213
|
+
|
214
|
+
if ret["error"]
|
215
|
+
Saber.ui.error "Populate Failed. #{ret['error']}"
|
216
|
+
{}
|
217
|
+
else
|
218
|
+
Hash[ret.map{|k, v| [(k=="publisher" ? "publishers" : k).capitalize, v]}]
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
# vim: fdn=4
|