saber 1.1.1 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/.gitignore +1 -0
  2. data/CHANGELOG.md +8 -1
  3. data/Gemfile +6 -1
  4. data/Gemfile.lock +58 -14
  5. data/README.md +40 -125
  6. data/bin/saber +4 -2
  7. data/bin/saber.bb +3 -0
  8. data/bin/saber.stp +3 -0
  9. data/lib/saber.rb +5 -0
  10. data/lib/saber/autofetcher/server.rb +1 -1
  11. data/lib/saber/book.rb +36 -0
  12. data/lib/saber/cli.rb +45 -19
  13. data/lib/saber/core_ext.rb +68 -0
  14. data/lib/saber/fetcher.rb +2 -2
  15. data/lib/saber/rc.rb +6 -0
  16. data/lib/saber/task.rb +3 -0
  17. data/lib/saber/task/base.rb +6 -1
  18. data/lib/saber/task/chd.rb +10 -2
  19. data/lib/saber/task/clean.rb +2 -2
  20. data/lib/saber/task/find_uploads.rb +53 -0
  21. data/lib/saber/task/generate.rb +37 -20
  22. data/lib/saber/task/make.rb +20 -8
  23. data/lib/saber/task/send.rb +1 -1
  24. data/lib/saber/task/upload.rb +29 -19
  25. data/lib/saber/tracker.rb +8 -4
  26. data/lib/saber/tracker/base.rb +36 -15
  27. data/lib/saber/tracker/bb.rb +4 -10
  28. data/lib/saber/tracker/bib.rb +27 -11
  29. data/lib/saber/tracker/chd.rb +4 -4
  30. data/lib/saber/tracker/gazelle.rb +7 -0
  31. data/lib/saber/tracker/ptp.rb +2 -2
  32. data/lib/saber/tracker/stp.rb +53 -0
  33. data/lib/saber/tracker/what.rb +2 -2
  34. data/lib/saber/tracker2.rb +31 -0
  35. data/lib/saber/tracker2/base.rb +126 -0
  36. data/lib/saber/tracker2/bb.rb +211 -0
  37. data/lib/saber/tracker2/bib.rb +162 -0
  38. data/lib/saber/tracker2/gazelle.rb +52 -0
  39. data/lib/saber/tracker2/stp.rb +136 -0
  40. data/lib/saber/tracker2/what.rb +51 -0
  41. data/lib/saber/version.rb +1 -1
  42. data/lib/saber/watir_ext.rb +95 -0
  43. data/saber.gemspec +6 -1
  44. data/spec/saber/autofetcher/server_spec.rb +1 -1
  45. data/spec/saber/task_spec.rb +1 -1
  46. data/systemd/saber-chd@.service +12 -0
  47. data/systemd/saber-client@.service +12 -0
  48. data/systemd/saber-server@.service +12 -0
  49. data/templates/_saberrc +29 -8
  50. data/templates/article.yml +18 -0
  51. data/templates/bib/application.yml +8 -8
  52. data/templates/bib/audiobook.yml +12 -14
  53. data/templates/comic.yml +25 -0
  54. data/templates/ebook.yml +35 -0
  55. data/templates/journal.yml +19 -0
  56. data/templates/magazine.yml +41 -0
  57. data/templates/manual.yml +6 -0
  58. data/templates/newspaper.yml +6 -0
  59. metadata +109 -13
  60. data/templates/bb/comic.yml +0 -7
  61. data/templates/bb/ebook.yml +0 -8
  62. data/templates/bb/magazine.yml +0 -6
  63. data/templates/bib/article.yml +0 -18
  64. data/templates/bib/comic.yml +0 -22
  65. data/templates/bib/ebook.yml +0 -20
  66. data/templates/bib/journal.yml +0 -18
  67. data/templates/bib/magazine.yml +0 -18
  68. data/templates/what/ebook.yml +0 -6
@@ -0,0 +1,31 @@
1
+ require "watir"
2
+ require "saber/watir_ext"
3
+ require "reverse_markdown"
4
+ require "active_support/core_ext/module/attribute_accessors"
5
+
6
+ module Saber
7
+ # Usage
8
+ #
9
+ # bib = Tracker2["bib"]
10
+ # bib.upload("Hello.epub")
11
+ #
12
+ module Tracker2
13
+ @@trackers = {}
14
+ mattr_reader :trackers
15
+
16
+ class << self
17
+ def [](name)
18
+ require "saber/tracker2/#{name}"
19
+
20
+ trackers[name]
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ require "saber/tracker2/base"
27
+ require "saber/tracker2/gazelle"
28
+ require "saber/tracker2/what"
29
+ require "saber/tracker2/bib"
30
+ require "saber/tracker2/stp"
31
+
@@ -0,0 +1,126 @@
1
+ require "active_support/core_ext/string/inflections"
2
+
3
+ module Saber
4
+ module Tracker2
5
+ class Base
6
+ def self.inherited(child)
7
+ Tracker2.trackers[child.name.demodulize.underscore] = child
8
+ end
9
+
10
+ # Implement
11
+ BASE_URL = ""
12
+ TYPES = {}
13
+
14
+ attr_reader :agent, :name, :options
15
+
16
+ def initialize(options={})
17
+ @name = self.class.name.demodulize.underscore
18
+ @agent = Watir::Browser.new *Rc.browser
19
+ @options = options
20
+ #@agent.load_cookies("#{Rc.p.home}/#{name}.cookies")
21
+
22
+ # share cookies
23
+ launcher = agent.driver.instance_variable_get("@bridge").instance_variable_get("@launcher")
24
+ origin_profile_dir = launcher.instance_variable_get("@profile").instance_variable_get("@model")
25
+ profile_dir = launcher.instance_variable_get("@profile_dir")
26
+
27
+ Pa.symln_f "#{origin_profile_dir}/cookies.sqlite", "#{profile_dir}/cookies.sqlite"
28
+ end
29
+
30
+ def upload(format, filemap, o={})
31
+ failed_counts = 0
32
+
33
+ filemap.each {|file, torrent_file|
34
+ info0 = YAML.load_file("#{file}.yml")
35
+
36
+ # current release only support ebook type.
37
+ Saber.ui.error "Current doesn't support this upload_type -- #{info0['upload_type']}." unless
38
+ %w[ebook magazine journal newspaper manual article comic].include? info0["upload_type"]
39
+
40
+ info = {}
41
+
42
+ # convert {"tracker.tags" => x} to {"trackers" => {"tags" => x}}
43
+ info0.each {|k, v|
44
+ if k.include?(".")
45
+ tracker, key = k.split(".")
46
+ info[tracker] ||= {}
47
+ info[tracker][key] = v
48
+ elsif Hash === info[k] and Hash === v
49
+ info[k].merge!(v)
50
+ else
51
+ info[k] = v
52
+ end
53
+ }
54
+
55
+ info.deep_symbolize_keys!
56
+ info[:format] = format.upcase
57
+ info[:upload_type2] = self.class::TYPES[info[:upload_type]]
58
+ info[:torrent_file] = Pa.absolute2(torrent_file)
59
+ info.merge! info.delete(name.to_sym){ {} }
60
+
61
+ # skip empty description.
62
+ if info[:description].empty?
63
+ Saber.ui.error "SKIP: Empty description -- #{file}.yml"
64
+ next
65
+ end
66
+
67
+ # check exists?
68
+ if Tracker[name].respond_to?(:exists?)
69
+ tracker = Tracker[name].new
70
+ tracker.login
71
+ if tracker.exists?(isbn: info[:isbn])
72
+ Saber.ui.say "SKIP: File existing in #{name} site. -- (#{info[:isbn]}) #{info[:title]}"
73
+ next
74
+ end
75
+ end
76
+
77
+ upload_method = o[:add] ? :add_format : :new_upload
78
+ if send(upload_method, info)
79
+ Saber.ui.say "Upload Complete: #{file}"
80
+
81
+ Pa.mv "#{file}.yml", Rc.upload.move_yml if Rc._has_key?("upload.move_yml")
82
+ Pa.mv torrent_file, Rc.upload.move_torrent if Rc._has_key?("upload.move_torrent")
83
+ else
84
+ failed_counts += 1
85
+ Saber.ui.error "SKIP: Upload failed -- #{file}"
86
+ end
87
+ }
88
+
89
+ agent.quit unless failed_counts > 0
90
+ end
91
+
92
+ protected
93
+
94
+ # check login and save cookies
95
+ def check_login(url_pat)
96
+ #unless agent.url =~ url_pat
97
+ # agent.wait_until { agent.url =~ url_pat }
98
+ # agent.cookies.dump("#{Rc.p.home}/#{name}.cookies")
99
+ #end
100
+
101
+ agent.wait_until { agent.url =~ url_pat }
102
+ end
103
+
104
+ # Implement
105
+ #
106
+ # Upload one torrent file to the site.
107
+ #
108
+ # @param [Optimism] info comes from <file>.yml data file.
109
+ #
110
+ # @return [Boolean] result-code
111
+ def new_upload(info)
112
+ raise NotImplementedError
113
+ end
114
+
115
+ # Implement
116
+ #
117
+ # Upload via add formt
118
+ #
119
+ def add_format(info)
120
+ raise NotImplementedError
121
+ end
122
+ end
123
+ end
124
+ end
125
+
126
+ # vim: fdn=4
@@ -0,0 +1,211 @@
1
+ module Saber
2
+ module Tracker2
3
+ class BB < Gazelle
4
+ BASE_URL = "https://baconbits.org"
5
+
6
+ TYPES = {
7
+ "music" => "Musics",
8
+ "application" => "Applications",
9
+ "ebook" => "E-Books",
10
+ "audiobook" => "Audiobooks",
11
+ "elearning-video" => "E-Learning Videos",
12
+ "magazine" => "Magazines",
13
+ "comic" => "Comics",
14
+ "anime" => "Anime",
15
+ "movie" => "Movies",
16
+ "tv" => "TV",
17
+ "game-pc" => "Game - PC",
18
+ "game-console" => "Game - Console",
19
+ "documentary" => "Documentaries",
20
+ "misc" => "Misc"
21
+ }
22
+
23
+ FIELDS = {
24
+ "music" => {
25
+ torrent_file: "//input[@name='file_input']",
26
+ artist: "//input[@name='artist']",
27
+ title: "//input[@name='title']",
28
+ remaster: "//input[@name='remaster']",
29
+ year: "//input[@name='year']",
30
+ scene: "//input[@name='scene']",
31
+ format: "//select[@name='format']",
32
+ bitrate: "//select[@name='bitrate']",
33
+ media: "//select[@name='media']",
34
+ tags: "//input[@name='tags']",
35
+ image: "//input[@name='image']",
36
+ description: "//input[@name='album_desc']",
37
+ release_description: "//input[@name='release_desc']"
38
+ },
39
+
40
+ "application" => {
41
+ torrent_file: "//input[@name='file_input']",
42
+ title: "//input[@name='title']",
43
+ tags: "//input[@name='tags']",
44
+ description: "//textarea[@name='desc']",
45
+ image: "//input[@name='image']",
46
+ scene: "//input[@name='scene']"
47
+ },
48
+
49
+ "ebook" => {
50
+ torrent_file: "//input[@name='file_input']",
51
+ title: "//input[@name='title']",
52
+ tags: "//input[@name='tags']",
53
+ format: "//select[@name='book_format']",
54
+ isbn: "//input[@name='book_isbn']",
55
+ authors: "//input[@name='book_author']",
56
+ publisher: "//input[@name='book_publisher']",
57
+ language: "//input[@name='book_language']",
58
+ year: "//input[@name='book_year']",
59
+ retail: "//input[@name='book_retail']",
60
+ description: "//textarea[@name='desc']",
61
+ image: "//input[@name='image']",
62
+ scene: "//input[@name='scene']"
63
+ },
64
+
65
+ "audiobook" => {
66
+ torrent_file: "//input[@name='file_input']",
67
+ title: "//input[@name='title']",
68
+ year: "//input[@name='year']",
69
+ format: "//select[@name='format']",
70
+ bitrate: "//select[@name='bitrate']",
71
+ tags: "//input[@name='tags']",
72
+ image: "//input[@name='image']",
73
+ description: "//input[@name='album_desc']",
74
+ release_description: "//input[@name='release_desc']"
75
+ },
76
+
77
+ "elearning-video" => {
78
+ torrent_file: "//input[@name='file_input']",
79
+ title: "//input[@name='title']",
80
+ tags: "//input[@name='tags']",
81
+ isbn: "//input[@name='book_isbn']",
82
+ description: "//textarea[@name='desc']",
83
+ image: "//input[@name='image']",
84
+ scene: "//input[@name='scene']"
85
+ },
86
+
87
+ "magazine" => {
88
+ torrent_file: "//input[@name='file_input']",
89
+ title: "//input[@name='title']",
90
+ tags: "//input[@name='tags']",
91
+ description: "//textarea[@name='desc']",
92
+ image: "//input[@name='image']",
93
+ scene: "//input[@name='scene']"
94
+ },
95
+
96
+ "comic" => {
97
+ torrent_file: "//input[@name='file_input']",
98
+ title: "//input[@name='title']",
99
+ tags: "//input[@name='tags']",
100
+ isbn: "//input[@name='book_isbn']",
101
+ description: "//textarea[@name='desc']",
102
+ image: "//input[@name='image']",
103
+ scene: "//input[@name='scene']"
104
+ },
105
+
106
+ "anime" => {
107
+ torrent_file: "//input[@name='file_input']",
108
+ title: "//input[@name='title']",
109
+ tags: "//input[@name='tags']",
110
+ description: "//textarea[@name='desc']",
111
+ image: "//input[@name='image']",
112
+ scene: "//input[@name='scene']"
113
+ },
114
+
115
+ "movie" => {
116
+ torrent_file: "//input[@name='file_input']",
117
+ title: "//input[@name='title']",
118
+ source: "//select[@name='source']",
119
+ videoformat: "//select[@name='videoformat']",
120
+ audioformat: "//select[@name='audioformat']",
121
+ container: "//select[@name='container']",
122
+ resolution: "//select[@name='resolution']",
123
+ remaster_title: "//input[@name='remaster_title']",
124
+ year: "//input[@name='year']",
125
+ tags: "//input[@name='tags']",
126
+ description: "//textarea[@name='desc']",
127
+ release_info: "//textarea[@name='release_info']",
128
+ screenshot1: "//input[@name='screenshot1']",
129
+ screenshot2: "//input[@name='screenshot2']",
130
+ image: "//input[@name='image']",
131
+ scene: "//input[@name='scene']"
132
+ },
133
+
134
+ "tv" => {
135
+ torrent_file: "//input[@name='file_input']",
136
+ title: "//input[@name='title']",
137
+ tags: "//input[@name='tags']",
138
+ description: "//textarea[@name='desc']",
139
+ image: "//input[@name='image']",
140
+ scene: "//input[@name='scene']"
141
+ },
142
+
143
+ "game-pc" => {
144
+ torrent_file: "//input[@name='file_input']",
145
+ title: "//input[@name='title']",
146
+ tags: "//input[@name='tags']",
147
+ description: "//textarea[@name='desc']",
148
+ image: "//input[@name='image']",
149
+ scene: "//input[@name='scene']"
150
+ },
151
+
152
+ "game-console" => {
153
+ torrent_file: "//input[@name='file_input']",
154
+ title: "//input[@name='title']",
155
+ tags: "//input[@name='tags']",
156
+ description: "//textarea[@name='desc']",
157
+ image: "//input[@name='image']",
158
+ scene: "//input[@name='scene']"
159
+ },
160
+
161
+ "documentary" => {
162
+ torrent_file: "//input[@name='file_input']",
163
+ title: "//input[@name='title']",
164
+ source: "//select[@name='source']",
165
+ videoformat: "//select[@name='videoformat']",
166
+ audioformat: "//select[@name='audioformat']",
167
+ container: "//select[@name='container']",
168
+ resolution: "//select[@name='resolution']",
169
+ remaster_title: "//input[@name='remaster_title']",
170
+ year: "//input[@name='year']",
171
+ tags: "//input[@name='tags']",
172
+ description: "//textarea[@name='desc']",
173
+ release_info: "//textarea[@name='release_info']",
174
+ screenshot1: "//input[@name='screenshot1']",
175
+ screenshot2: "//input[@name='screenshot2']",
176
+ image: "//input[@name='image']",
177
+ scene: "//input[@name='scene']"
178
+ },
179
+
180
+ "misc" => {
181
+ torrent_file: "//input[@name='file_input']",
182
+ title: "//input[@name='title']",
183
+ tags: "//input[@name='tags']",
184
+ description: "//textarea[@name='desc']",
185
+ image: "//input[@name='image']",
186
+ scene: "//input[@name='scene']"
187
+ },
188
+ }
189
+
190
+ def fill_form(form, info)
191
+ form.select(name: "type").select info[:upload_type2]
192
+ sleep 0.1
193
+ form.h2(text: "Getting Form...").wait_while_present unless info[:upload_type] == "music"
194
+ sleep 0.1
195
+
196
+ FIELDS[info[:upload_type]].each {|key, selector|
197
+ form.quick_set(selector, info[key])
198
+ }
199
+
200
+ # comic
201
+ if info[:upload_type] == "comic"
202
+ info[:format].split(",").each { |format|
203
+ form.input(value: format).set true
204
+ }
205
+ end
206
+ end
207
+ end
208
+ end
209
+ end
210
+
211
+ # vim: fdn=4
@@ -0,0 +1,162 @@
1
+ module Saber
2
+ module Tracker2
3
+ class BIB < Base
4
+ BASE_URL = "http://bibliotik.org"
5
+
6
+ TYPES = {
7
+ "application" => "applications",
8
+ "article" => "articles",
9
+ "audiobook" => "audiobooks",
10
+ "comic" => "comics",
11
+ "ebook" => "ebooks",
12
+ "journal" => "journals",
13
+ "magazine" => "magazines"
14
+ }
15
+
16
+ FIELDS = {
17
+ "application" => {
18
+ torrent_file: "//input[@name='TorrentFileField']",
19
+ scene: "//input[@name='SecneField']",
20
+ title: "//input[@name='TitleField']",
21
+ tags: "//input[@name='TagsField']",
22
+ image: "//input[@name='ImageField']",
23
+ description: "//textarea[@name='DescriptionField']",
24
+ anonymous: "//input[@name='AnonymousField']",
25
+ notify: "//input[@name='NotifyField']"
26
+ },
27
+
28
+ "article" => {
29
+ torrent_file: "//input[@name='TorrentFileField']",
30
+ authors: "//input[@name='AuthorsField']",
31
+ title: "//input[@name='TitleField']",
32
+ pages: "//input[@name='PagesField']",
33
+ year: "//input[@name='YearField']",
34
+ yearto: "//input[@name='YearToField']",
35
+ complete: "//input[@name='CompleteField']",
36
+ format: "//select[@name='FormatField']",
37
+ language: "//select[@name='LanguageField']",
38
+ tags: "//input[@name='TagsField']",
39
+ image: "//input[@name='ImageField']",
40
+ description: "//textarea[@name='DescriptionField']",
41
+ anonymous: "//input[@name='AnonymousField']",
42
+ notify: "//input[@name='NotifyField']"
43
+ },
44
+
45
+ "autobook" => {
46
+ torrent_file: "//input[@name='TorrentFileField']",
47
+ authors: "//input[@name='AuthorsField']",
48
+ title: "//input[@name='TitleField']",
49
+ isbn: "//input[@name='IsbnField']",
50
+ publisher: "//input[@name='PublishersField']",
51
+ year: "//input[@name='YearField']",
52
+ format: "//select[@name='FormatField']",
53
+ language: "//select[@name='LanguageField']",
54
+ tags: "//input[@name='TagsField']",
55
+ image: "//input[@name='ImageField']",
56
+ description: "//textarea[@name='DescriptionField']",
57
+ anonymous: "//input[@name='AnonymousField']",
58
+ notify: "//input[@name='NotifyField']"
59
+ },
60
+
61
+ "comic" => {
62
+ torrent_file: "//input[@name='TorrentFileField']",
63
+ scene: "//input[@name='SceneField']",
64
+ authors: "//input[@name='AuthorsField']",
65
+ artists: "//input[@name='ArtistsField']",
66
+ title: "//input[@name='TitleField']",
67
+ publisher: "//input[@name='PublishersField']",
68
+ pages: "//input[@name='PagesField']",
69
+ year: "//input[@name='YearField']",
70
+ yearto: "//input[@name='YearToField']",
71
+ complete: "//input[@name='CompleteField']",
72
+ format: "//select[@name='FormatField']",
73
+ language: "//select[@name='LanguageField']",
74
+ tags: "//input[@name='TagsField']",
75
+ image: "//input[@name='ImageField']",
76
+ description: "//textarea[@name='DescriptionField']",
77
+ anonymous: "//input[@name='AnonymousField']",
78
+ notify: "//input[@name='NotifyField']"
79
+ },
80
+
81
+ "ebook" => {
82
+ torrent_file: "//input[@name='TorrentFileField']",
83
+ scene: "//input[@name='SceneField']",
84
+ authors: "//input[@name='AuthorsField']",
85
+ title: "//input[@name='TitleField']",
86
+ isbn: "//input[@name='IsbnField']",
87
+ publisher: "//input[@name='PublishersField']",
88
+ pages: "//input[@name='PagesField']",
89
+ year: "//input[@name='YearField']",
90
+ format: "//select[@name='FormatField']",
91
+ language: "//select[@name='LanguageField']",
92
+ retail: "//input[@name='RetailField']",
93
+ tags: "//input[@name='TagsField']",
94
+ image: "//input[@name='ImageField']",
95
+ description: "//textarea[@name='DescriptionField']",
96
+ anonymous: "//input[@name='AnonymousField']",
97
+ notify: "//input[@name='NotifyField']"
98
+ },
99
+
100
+ "journal" => {
101
+ torrent_file: "//input[@name='TorrentFileField']",
102
+ scene: "//input[@name='SceneField']",
103
+ title: "//input[@name='TitleField']",
104
+ pages: "//input[@name='PagesField']",
105
+ year: "//input[@name='YearField']",
106
+ yearto: "//input[@name='YearToField']",
107
+ complete: "//input[@name='CompleteField']",
108
+ format: "//select[@name='FormatField']",
109
+ language: "//select[@name='LanguageField']",
110
+ tags: "//input[@name='TagsField']",
111
+ image: "//input[@name='ImageField']",
112
+ description: "//textarea[@name='DescriptionField']",
113
+ anonymous: "//input[@name='AnonymousField']",
114
+ notify: "//input[@name='NotifyField']"
115
+ },
116
+
117
+ "magazine" => {
118
+ torrent_file: "//input[@name='TorrentFileField']",
119
+ scene: "//input[@name='SceneField']",
120
+ title: "//input[@name='TitleField']",
121
+ pages: "//input[@name='PagesField']",
122
+ year: "//input[@name='YearField']",
123
+ yearto: "//input[@name='YearToField']",
124
+ complete: "//input[@name='CompleteField']",
125
+ format: "//select[@name='FormatField']",
126
+ language: "//select[@name='LanguageField']",
127
+ tags: "//input[@name='TagsField']",
128
+ image: "//input[@name='ImageField']",
129
+ description: "//textarea[@name='DescriptionField']",
130
+ anonymous: "//input[@name='AnonymousField']",
131
+ notify: "//input[@name='NotifyField']"
132
+ }
133
+ }
134
+
135
+ def do_upload(file, info)
136
+ info[:complete] = (info[:type] == "Pack")
137
+
138
+ agent.goto "#{BASE_URL}/upload/#{info[:upload_type2]}"
139
+ check_login %r~/upload/#{info[:upload_type2]}~
140
+
141
+ form = agent.form(action: "")
142
+
143
+ FIELDS[info[:upload_type]].each {|key, selector|
144
+ form.quick_set(selector, info[key])
145
+ }
146
+
147
+ form.submit()
148
+
149
+ if agent.url =~ %r~/upload/#{info[:upload_type2]}~
150
+ err = agent.element(xpath: "//*[@id='formerrorlist']")
151
+ msg = err.exists? ? ReverseMarkdown.parse(err) : agent.text
152
+ Saber.ui.error "ERROR: #{msg.to_s.strip}\n"
153
+ return false
154
+ else
155
+ return true
156
+ end
157
+ end
158
+ end
159
+ end
160
+ end
161
+
162
+ # vim: fdn=4