subdb 0.1.5 → 0.1.6

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.
@@ -1,13 +1,13 @@
1
- h1. Ruby SubDB
1
+ h1. Ruby SubDB "!http://travis-ci.org/wilkerlucio/subdb.png!":http://travis-ci.org/wilkerlucio/subdb
2
2
 
3
- This project aims to provide a simple API for accessing "SubBD":http://thesubdb.com/
3
+ This project aims to provide a simple API for accessing "SubDB":http://thesubdb.com/
4
4
 
5
5
  h2. GUI client
6
6
 
7
7
  We are proud to annouce now we have a GUI client to make it easy for anyone to use SubDB, you can download client with following links:
8
8
 
9
- "Mac Version":https://github.com/downloads/wilkerlucio/subdb/subdb-0.1.4.dmg
10
- "Windows / Linux version (requires Java)":https://github.com/downloads/wilkerlucio/subdb/subdb-0.1.4.jar
9
+ "Mac Version":https://github.com/downloads/wilkerlucio/subdb/subdb-0.1.6.dmg
10
+ "Windows / Linux version (requires Java)":https://github.com/downloads/wilkerlucio/subdb/subdb-0.1.6.jar
11
11
 
12
12
  Just download, open, and drag your files on program window. It will work in same way as Command Line tool.
13
13
 
@@ -29,9 +29,21 @@ If you plan to use on your project, SubDB gem also provides a simple interface f
29
29
 
30
30
  bc.. require 'subdb'
31
31
 
32
- file = Subdb.new("path_to_your_movie.mp4")
32
+ file = Subdb::Video.new("path_to_your_movie.mp4")
33
33
  file.search # will retrieve a string with available languages (ex: "pt,en") or nil if don't have anyone
34
34
  file.download(["en", "pt"]) # will download the subtitle for given language, it tries in order of array
35
35
  file.upload("path_to_subtitle.str") # will upload a subtitle for this movie
36
36
 
37
37
  p. It's only this :)
38
+
39
+ h2. Development
40
+
41
+ If you are looking to help with project code, please checkout the @develop@ branch, the edge stuff is there.
42
+
43
+ h2. Changelog
44
+
45
+ h3. 0.1.6
46
+
47
+ * Main class @Subdb@ was moved to @Subdb::Video@
48
+ * Ability to drop files direct on Dock Icon (Mac Only)
49
+ * Display a list of downloaded subtitles at end of sync process
@@ -19,139 +19,7 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
- require 'net/http'
23
- require 'uri'
24
- require 'cgi'
25
- require 'digest/md5'
26
- require 'net/http/post/multipart'
27
-
28
22
  require 'subdb/version'
23
+ require 'subdb/video'
29
24
  require 'subdb/client_utils'
30
25
  require 'subdb/upload_cache'
31
-
32
- class Subdb
33
- API = "http://api.thesubdb.com/"
34
- SANDBOX = "http://sandbox.thesubdb.com/"
35
-
36
- class << self
37
- attr_accessor :test_mode
38
-
39
- def api_url
40
- test_mode ? SANDBOX : API
41
- end
42
- end
43
-
44
- self.test_mode = false
45
-
46
- attr_reader :hash, :path
47
-
48
- def initialize(path)
49
- fail "#{path} is not a file" unless File.exists?(path)
50
-
51
- @path = path
52
- @hash = build_hash
53
- end
54
-
55
- def search
56
- res = request("search")
57
- check_get(res)
58
- end
59
-
60
- def download(languages = ["en"])
61
- res = request("download", :language => languages.join(","))
62
- check_get(res)
63
- end
64
-
65
- def upload(path)
66
- fail "Invalid subtitle file #{path}" unless File.exists?(path)
67
-
68
- params = {:action => "upload", :hash => @hash}
69
-
70
- url = URI.parse(self.class.api_url)
71
-
72
- begin
73
- file = File.open(path, "rb")
74
-
75
- io = UploadIO.new(file, "application/octet-stream", File.basename(path))
76
-
77
- req = Net::HTTP::Post::Multipart.new(url.path + stringify_params(params), {"file" => io, "hash" => @hash})
78
- req["User-Agent"] = user_agent
79
-
80
- res = Net::HTTP.start(url.host, url.port) do |http|
81
- http.request(req)
82
- end
83
-
84
- case res.code.to_s
85
- when "201" then true
86
- when "403" then false
87
- when "400" then fail "Malformed request"
88
- when "415" then fail "Invalid subtitle type"
89
- end
90
- ensure
91
- file.close
92
- end
93
- end
94
-
95
- def pathbase
96
- File.basename(path)
97
- end
98
-
99
- protected
100
-
101
- def build_hash
102
- chunk_size = 64 * 1024
103
-
104
- size = File.size(@path)
105
- file = File.open(@path, "rb")
106
- data = file.read(chunk_size)
107
- file.seek(size - chunk_size)
108
- data += file.read(chunk_size)
109
-
110
- file.close
111
-
112
- Digest::MD5.hexdigest(data)
113
- end
114
-
115
- def user_agent
116
- "SubDB/1.0 (RubySubDB/#{VERSION}; http://github.com/wilkerlucio/subdb)"
117
- end
118
-
119
- def request(action, params = {}, body = nil)
120
- params = {:action => action, :hash => @hash}.merge(params)
121
-
122
- url = URI.parse(self.class.api_url)
123
-
124
- req = Net::HTTP::Get.new(url.path + stringify_params(params))
125
- req["User-Agent"] = user_agent
126
- req.set_form_data(body) if body
127
-
128
- Net::HTTP.start(url.host, url.port) do |http|
129
- http.request(req)
130
- end
131
- end
132
-
133
- def check_get(res)
134
- case res.code.to_s
135
- when "200" then res.body
136
- when "400" then fail "Malformed request"
137
- when "404" then nil
138
- else
139
- fail "Unexpected response code - #{res.code}"
140
- end
141
- end
142
-
143
- def stringify_params(params)
144
- params_string = []
145
-
146
- params.each do |key, value|
147
- next unless value
148
-
149
- key = CGI.escape(key.to_s)
150
- value = CGI.escape(value.to_s)
151
-
152
- params_string << "#{key}=#{value}"
153
- end
154
-
155
- params_string.length.zero? ? "" : "?" + params_string.join("&")
156
- end
157
- end
@@ -18,115 +18,117 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- module Subdb::ClientUtils
22
- VIDEO_EXTENSIONS = ['.avi', '.mkv', '.mp4', '.mov', '.mpg', '.wmv', '.rm', '.rmvb', '.divx']
23
- SUB_EXTESNIONS = ['.srt', '.sub']
21
+ module Subdb
22
+ module ClientUtils
23
+ VIDEO_EXTENSIONS = ['.avi', '.mkv', '.mp4', '.mov', '.mpg', '.wmv', '.rm', '.rmvb', '.divx']
24
+ SUB_EXTESNIONS = ['.srt', '.sub']
24
25
 
25
- class << self
26
- def scan_paths(paths)
27
- video_ext = VIDEO_EXTENSIONS.join(",")
26
+ class << self
27
+ def scan_paths(paths)
28
+ video_ext = VIDEO_EXTENSIONS.join(",")
28
29
 
29
- files = []
30
+ files = []
30
31
 
31
- for path in paths
32
- if File.directory?(path)
33
- path = path.chomp(File::SEPARATOR)
34
- globpath = "#{path.gsub("\\", "/")}/**/*{#{video_ext}}"
32
+ for path in paths
33
+ if File.directory?(path)
34
+ path = path.chomp(File::SEPARATOR)
35
+ globpath = "#{path.gsub("\\", "/")}/**/*{#{video_ext}}"
35
36
 
36
- yield globpath if block_given?
37
+ yield globpath if block_given?
37
38
 
38
- files = files.concat(Dir.glob(globpath))
39
- else
40
- files << path if VIDEO_EXTENSIONS.include?(File.extname(path))
39
+ files = files.concat(Dir.glob(globpath))
40
+ else
41
+ files << path if VIDEO_EXTENSIONS.include?(File.extname(path))
42
+ end
41
43
  end
42
- end
43
44
 
44
- files.sort
45
- end
45
+ files.sort
46
+ end
46
47
 
47
- def sync(paths, languages = ["en"])
48
- yield :loading_cache
49
- cache = Subdb::UploadCache.new(cache_file_path)
48
+ def sync(paths, languages = ["en"])
49
+ yield :loading_cache
50
+ cache = Subdb::UploadCache.new(cache_file_path)
50
51
 
51
- results = {:download => [], :upload => []}
52
- i = 0
52
+ results = {:download => [], :upload => []}
53
+ i = 0
53
54
 
54
- for path in paths
55
- base = File.dirname(path) + File::SEPARATOR + File.basename(path, File.extname(path))
56
- sub = find_subtitle(path)
55
+ for path in paths
56
+ base = File.dirname(path) + File::SEPARATOR + File.basename(path, File.extname(path))
57
+ sub = find_subtitle(path)
57
58
 
58
- yield :scan, [path, i]
59
+ yield :scan, [path, i]
59
60
 
60
- begin
61
- subdb = Subdb.new(path)
61
+ begin
62
+ subdb = Video.new(path)
62
63
 
63
- yield :scanned, subdb
64
+ yield :scanned, subdb
64
65
 
65
- if sub and !cache.uploaded?(subdb.hash, sub)
66
- yield :uploading, subdb
66
+ if sub and !cache.uploaded?(subdb.hash, sub)
67
+ yield :uploading, subdb
67
68
 
68
- begin
69
- subdb.upload(sub)
70
- cache.push(subdb.hash, sub)
71
- results[:upload].push(sub)
72
- yield :upload_ok, subdb
73
- rescue
74
- yield :upload_failed, [subdb, $!]
69
+ begin
70
+ subdb.upload(sub)
71
+ cache.push(subdb.hash, sub)
72
+ results[:upload].push(sub)
73
+ yield :upload_ok, subdb
74
+ rescue
75
+ yield :upload_failed, [subdb, $!]
76
+ end
75
77
  end
76
- end
77
78
 
78
- if !sub
79
- yield :downloading, subdb
79
+ if !sub
80
+ yield :downloading, subdb
80
81
 
81
- begin
82
- downloaded = subdb.download(languages)
82
+ begin
83
+ downloaded = subdb.download(languages)
83
84
 
84
- if downloaded
85
- sub = base + ".srt"
85
+ if downloaded
86
+ sub = base + ".srt"
86
87
 
87
- File.open(sub, "wb") do |f|
88
- f << downloaded
89
- end
88
+ File.open(sub, "wb") do |f|
89
+ f << downloaded
90
+ end
90
91
 
91
- cache.push(subdb.hash, sub)
92
- results[:download].push(sub)
93
- yield :download_ok, subdb
94
- else
95
- yield :download_not_found, subdb
92
+ cache.push(subdb.hash, sub)
93
+ results[:download].push(sub)
94
+ yield :download_ok, [subdb, sub]
95
+ else
96
+ yield :download_not_found, subdb
97
+ end
98
+ rescue
99
+ yield :download_failed, [subdb, $!]
96
100
  end
97
- rescue
98
- yield :download_failed, [subdb, $!]
99
101
  end
102
+ rescue
103
+ yield :scan_failed, path, $!
100
104
  end
101
- rescue
102
- yield :scan_failed, path, $!
105
+
106
+ i += 1
107
+
108
+ yield :file_done, [subdb, i]
103
109
  end
104
110
 
105
- i += 1
111
+ yield :storing_cache
112
+ cache.store!
106
113
 
107
- yield :file_done, [subdb, i]
114
+ results
108
115
  end
109
116
 
110
- yield :storing_cache
111
- cache.store!
112
-
113
- results
114
- end
117
+ def find_subtitle(path)
118
+ base = File.dirname(path) + File::SEPARATOR + File.basename(path, File.extname(path))
115
119
 
116
- def find_subtitle(path)
117
- base = File.dirname(path) + File::SEPARATOR + File.basename(path, File.extname(path))
120
+ for subext in SUB_EXTESNIONS
121
+ subpath = base + subext
118
122
 
119
- for subext in SUB_EXTESNIONS
120
- subpath = base + subext
123
+ return subpath if File.exists?(subpath)
124
+ end
121
125
 
122
- return subpath if File.exists?(subpath)
126
+ nil
123
127
  end
124
128
 
125
- nil
126
- end
127
-
128
- def cache_file_path
129
- File.join((ENV["HOME"] || ENV["USERPROFILE"]), ".subdb_cache")
129
+ def cache_file_path
130
+ File.join((ENV["HOME"] || ENV["USERPROFILE"]), ".subdb_cache")
131
+ end
130
132
  end
131
133
  end
132
134
  end
@@ -37,135 +37,186 @@ import javax.swing.JTextArea
37
37
  import javax.swing.SwingConstants
38
38
  import javax.swing.UIManager
39
39
 
40
- include_class java.lang.System
41
- include_class Java::FileDrop
40
+ begin
41
+ import com.apple.eawt.Application
42
+ import com.apple.eawt.AppEventListener
43
+ import com.apple.eawt.OpenFilesHandler
44
+ rescue
45
+ # just fake classes
46
+ class Application
47
+ def self.application
48
+ self.new
49
+ end
42
50
 
43
- class SubdbGUI < JFrame
44
- include FileDrop::Listener
51
+ def add_app_event_listener(listener)
52
+ end
45
53
 
46
- def initialize
47
- super "SubDB Sync"
54
+ def open_file_handler=(handle)
55
+ end
56
+ end
48
57
 
49
- @uploading = false
58
+ module AppEventListener
50
59
 
51
- self.init_ui
52
60
  end
53
61
 
54
- def init_ui
55
- begin
56
- icon = ImageIO.read(getClass.getResource("images/subdb128.png"))
57
- set_icon_image icon
58
- rescue
59
- end
62
+ module OpenFilesHandler
60
63
 
61
- border_size = 1
64
+ end
65
+ end
62
66
 
63
- @dropper = JPanel.new
64
- @dropper.set_border BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(border_size, border_size, border_size, border_size), BorderFactory.createLineBorder(Color.black))
67
+ include_class java.lang.System
68
+ include_class Java::FileDrop
65
69
 
66
- @progress = JProgressBar.new(0, 100)
70
+ module Subdb
71
+ module UI
72
+ class Swing < JFrame
73
+ include FileDrop::Listener
74
+ include AppEventListener
75
+ include OpenFilesHandler
67
76
 
68
- hint = JLabel.new("Arraste suas pastas ou arquivos com videos aqui.", SwingConstants::CENTER)
69
- hint.set_preferred_size Dimension.new(500, 60)
77
+ def initialize
78
+ super "SubDB Sync"
70
79
 
71
- @dropper.add(hint, BorderLayout::CENTER)
80
+ @uploading = false
72
81
 
73
- @log = JTextArea.new
74
- @log.set_editable false
82
+ app = Application.application
83
+ app.add_app_event_listener(self)
84
+ app.open_file_handler = self
75
85
 
76
- @scroller = JScrollPane.new(@log)
86
+ self.init_ui
87
+ end
77
88
 
78
- content_pane.add(@dropper, BorderLayout::NORTH)
79
- content_pane.add(@scroller, BorderLayout::CENTER)
80
- content_pane.add(@progress, BorderLayout::SOUTH)
89
+ def init_ui
90
+ begin
91
+ icon = ImageIO.read(get_class.get_resource("/images/subdb128.png"))
92
+ self.icon_image = icon
93
+ rescue
94
+ puts "Can't set window icon: #{$!}"
95
+ end
81
96
 
82
- FileDrop.new(nil, @dropper, self)
97
+ border_size = 1
83
98
 
84
- set_size 800, 300
85
- set_resizable true
86
- set_default_close_operation JFrame::EXIT_ON_CLOSE
87
- set_location_relative_to nil
88
- set_visible true
99
+ @dropper = JPanel.new
100
+ @dropper.border = BorderFactory.create_compound_border(BorderFactory.create_empty_border(border_size, border_size, border_size, border_size), BorderFactory.create_line_border(Color.black))
89
101
 
90
- @progress.set_string_painted true
91
- end
102
+ @progress = JProgressBar.new(0, 100)
103
+ @progress.string_painted = true
92
104
 
93
- def filesDropped(files)
94
- return if @uploading
105
+ hint = JLabel.new("Arraste suas pastas ou arquivos com videos aqui.", SwingConstants::CENTER)
106
+ hint.preferred_size = Dimension.new(500, 60)
95
107
 
96
- files = files.map { |f| f.to_s }
108
+ @dropper.add(hint, BorderLayout::CENTER)
97
109
 
98
- @uploading = true
110
+ @log = JTextArea.new
111
+ @log.editable = false
99
112
 
100
- Thread.new do
101
- @progress.set_indeterminate true
113
+ @scroller = JScrollPane.new(@log)
102
114
 
103
- log "Generating file list..."
115
+ content_pane.add(@dropper, BorderLayout::NORTH)
116
+ content_pane.add(@scroller, BorderLayout::CENTER)
117
+ content_pane.add(@progress, BorderLayout::SOUTH)
104
118
 
105
- files = Subdb::ClientUtils.scan_paths(files) do |path|
106
- log "Scanning #{path}..."
119
+ FileDrop.new(nil, @dropper, self)
120
+
121
+ set_size 800, 300
122
+ set_resizable true
123
+ set_default_close_operation JFrame::EXIT_ON_CLOSE
124
+ set_location_relative_to nil
125
+ set_visible true
107
126
  end
108
127
 
109
- log "Generation done, #{files.length} files to scan"
110
- log_separator
111
-
112
- @progress.set_indeterminate false
113
- @progress.set_maximum files.length
114
-
115
- results = Subdb::ClientUtils.sync files, ["pt", "en"] do |action, arg|
116
- case action
117
- when :loading_cache then log "Carregando cache de legendas enviadas..."
118
- when :scan then log "Abrindo #{arg[0]}..."
119
- when :scanned then log "Verificando #{arg.pathbase} [#{arg.hash}]..."
120
- when :uploading then log "Enviando legenda local para o servidor..."
121
- when :upload_failed then error "Erro ao enviar legenda #{arg[0]}: #{arg[1]}"
122
- when :downloading then log "Procurando legenda no servidor..."
123
- when :download_not_found then log "Nenhuma legenda encontrada no seu indioma"
124
- when :download_failed then error "Erro ao tentar baixar #{arg[0].path}: #{arg[1]}"
125
- when :scan_failed then error "Erro ao abrir arquivo #{arg[0]}: #{arg[1]}"
126
- when :storing_cache then log "Salvando cache de legendas enviadas..."
127
- when :file_done
128
- log "Concluido #{arg[0].path}"
129
- log_separator
130
- @progress.set_value arg[1]
131
- end
128
+ def openFiles(e)
129
+ sleep(0.5) until visible?
130
+ filesDropped(e.files)
132
131
  end
133
132
 
134
- log "Concluido"
135
- log "#{results[:download].length} legendas baixadas"
136
- log "#{results[:upload].length} legendas enviadas"
137
- log_separator
133
+ def filesDropped(files)
134
+ return if @uploading
138
135
 
139
- @uploading = false
140
- end
141
- end
136
+ files = files.map { |f| f.to_s }
142
137
 
143
- def log(msg)
144
- @log.append msg + "\n"
138
+ @uploading = true
145
139
 
146
- sleep 0.1
140
+ Thread.new do
141
+ @progress.indeterminate = true
147
142
 
148
- scroll = @scroller.vertical_scroll_bar
149
- scroll.value = scroll.maximum if scroll.value > scroll.maximum - (@scroller.height + 30)
143
+ log "Gerando lista de arquivos..."
150
144
 
151
- puts msg
152
- end
145
+ files = Subdb::ClientUtils.scan_paths(files) do |path|
146
+ log "Escaneando #{path}..."
147
+ end
153
148
 
154
- def log_separator
155
- log "------------------------------"
156
- end
149
+ log "Finalizado, #{files.length} arquivos para escanear."
150
+ log_separator
151
+
152
+ @progress.indeterminate = false
153
+ @progress.maximum = files.length
154
+
155
+ results = Subdb::ClientUtils.sync files, ["pt", "en"] do |action, arg|
156
+ case action
157
+ when :loading_cache then log "Carregando cache de legendas enviadas..."
158
+ when :scan then log "Abrindo #{arg[0]}..."
159
+ when :scanned then log "Verificando #{arg.pathbase} [#{arg.hash}]..."
160
+ when :uploading then log "Enviando legenda local para o servidor..."
161
+ when :upload_failed then error "Erro ao enviar legenda #{arg[0]}: #{arg[1]}"
162
+ when :downloading then log "Procurando legenda no servidor..."
163
+ when :download_ok then log "Legenda baixada com sucesso: #{arg[1]}"
164
+ when :download_not_found then log "Nenhuma legenda encontrada no seu idioma"
165
+ when :download_failed then error "Erro ao tentar baixar #{arg[0].path}: #{arg[1]}"
166
+ when :scan_failed then error "Erro ao abrir arquivo #{arg[0]}: #{arg[1]}"
167
+ when :storing_cache then log "Salvando cache de legendas enviadas..."
168
+ when :file_done
169
+ log "Concluido #{arg[0].path}"
170
+ log_separator
171
+ @progress.value = arg[1]
172
+ end
173
+ end
174
+
175
+ log "Concluido"
176
+ log_separator
177
+
178
+ if results[:download].length > 0
179
+ log "Legendas baixadas:"
180
+ results[:download].each { |s| log s }
181
+ log_separator
182
+ end
183
+
184
+ log "#{results[:download].length} legendas baixadas"
185
+ log "#{results[:upload].length} legendas enviadas"
186
+ log_separator
187
+
188
+ @uploading = false
189
+ end
190
+ end
191
+
192
+ def log(msg)
193
+ @log.append msg + "\n"
194
+
195
+ sleep 0.1
196
+
197
+ scroll = @scroller.vertical_scroll_bar
198
+ scroll.value = scroll.maximum if scroll.value > scroll.maximum - (@scroller.height + 30)
199
+
200
+ puts msg
201
+ end
202
+
203
+ def log_separator
204
+ log "------------------------------"
205
+ end
157
206
 
158
- def error(msg)
159
- log msg
207
+ def error(msg)
208
+ log msg
209
+ end
210
+ end
160
211
  end
161
212
  end
162
213
 
163
214
  # try use system look and feel
164
215
  begin
165
- UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
216
+ UIManager.look_and_feel = UIManager.system_look_and_feel_class_name
166
217
  rescue
167
218
  # no lucky...
168
219
  end
169
220
 
170
221
  # start application
171
- SubdbGUI.new
222
+ Subdb::UI::Swing.new
@@ -56,6 +56,10 @@ class Subdb::UploadCache
56
56
  @hash[hash].push(self.class.subtitle_hash(path))
57
57
  end
58
58
 
59
+ def versions(hash)
60
+ (@hash[hash] || []).length
61
+ end
62
+
59
63
  def store!
60
64
  File.open(path, "wb") do |file|
61
65
  file << Marshal.dump(@hash)
@@ -19,6 +19,6 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
- class Subdb
23
- VERSION = "0.1.5"
22
+ module Subdb
23
+ VERSION = "0.1.6"
24
24
  end
@@ -0,0 +1,162 @@
1
+ # Copyright (c) 2011 Wilker Lucio da Silva
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'net/http'
23
+ require 'uri'
24
+ require 'cgi'
25
+ require 'digest/md5'
26
+ require 'net/http/post/multipart'
27
+
28
+ require 'subdb/version'
29
+ require 'subdb/client_utils'
30
+ require 'subdb/upload_cache'
31
+
32
+ module Subdb
33
+ class Video
34
+ API = "http://api.thesubdb.com/"
35
+ SANDBOX = "http://sandbox.thesubdb.com/"
36
+
37
+ class << self
38
+ attr_accessor :test_mode
39
+
40
+ def api_url
41
+ test_mode ? SANDBOX : API
42
+ end
43
+ end
44
+
45
+ self.test_mode = false
46
+
47
+ attr_reader :hash, :path
48
+
49
+ def initialize(path)
50
+ fail "#{path} is not a file" unless File.exists?(path)
51
+
52
+ @path = path
53
+ @hash = build_hash
54
+ end
55
+
56
+ def search
57
+ res = request("search")
58
+ check_get(res)
59
+ end
60
+
61
+ def download(languages = ["en"])
62
+ res = request("download", :language => languages.join(","))
63
+ check_get(res)
64
+ end
65
+
66
+ def upload(path)
67
+ fail "Invalid subtitle file #{path}" unless File.exists?(path)
68
+
69
+ params = {:action => "upload", :hash => @hash}
70
+
71
+ url = URI.parse(self.class.api_url)
72
+
73
+ begin
74
+ file = File.open(path, "rb")
75
+
76
+ io = UploadIO.new(file, "application/octet-stream", File.basename(path))
77
+
78
+ req = Net::HTTP::Post::Multipart.new(url.path + stringify_params(params), {"file" => io, "hash" => @hash})
79
+ req["User-Agent"] = user_agent
80
+
81
+ res = Net::HTTP.start(url.host, url.port) do |http|
82
+ http.request(req)
83
+ end
84
+
85
+ case res.code.to_s
86
+ when "201" then true
87
+ when "403" then false
88
+ when "400" then fail "Malformed request"
89
+ when "415" then fail "Invalid subtitle type"
90
+ end
91
+ ensure
92
+ file.close
93
+ end
94
+ end
95
+
96
+ def pathbase
97
+ File.basename(path)
98
+ end
99
+
100
+ protected
101
+
102
+ def build_hash
103
+ chunk_size = 64 * 1024
104
+
105
+ size = File.size(@path)
106
+
107
+ raise ArgumentError.new("The video file need to have at least 128kb") if size < chunk_size * 2
108
+
109
+ file = File.open(@path, "rb")
110
+ data = file.read(chunk_size)
111
+ file.seek(size - chunk_size)
112
+ data += file.read(chunk_size)
113
+
114
+ file.close
115
+
116
+ Digest::MD5.hexdigest(data)
117
+ end
118
+
119
+ def user_agent
120
+ "SubDB/1.0 (RubySubDB/#{VERSION}; http://github.com/wilkerlucio/subdb)"
121
+ end
122
+
123
+ def request(action, params = {}, body = nil)
124
+ params = {:action => action, :hash => @hash}.merge(params)
125
+
126
+ url = URI.parse(self.class.api_url)
127
+
128
+ req = Net::HTTP::Get.new(url.path + stringify_params(params))
129
+ req["User-Agent"] = user_agent
130
+ req.set_form_data(body) if body
131
+
132
+ Net::HTTP.start(url.host, url.port) do |http|
133
+ http.request(req)
134
+ end
135
+ end
136
+
137
+ def check_get(res)
138
+ case res.code.to_s
139
+ when "200" then res.body
140
+ when "400" then fail "Malformed request"
141
+ when "404" then nil
142
+ else
143
+ fail "Unexpected response code - #{res.code}"
144
+ end
145
+ end
146
+
147
+ def stringify_params(params)
148
+ params_string = []
149
+
150
+ params.each do |key, value|
151
+ next unless value
152
+
153
+ key = CGI.escape(key.to_s)
154
+ value = CGI.escape(value.to_s)
155
+
156
+ params_string << "#{key}=#{value}"
157
+ end
158
+
159
+ params_string.length.zero? ? "" : "?" + params_string.join("&")
160
+ end
161
+ end
162
+ end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: subdb
3
3
  version: !ruby/object:Gem::Version
4
- hash: 17
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 5
10
- version: 0.1.5
9
+ - 6
10
+ version: 0.1.6
11
11
  platform: ruby
12
12
  authors:
13
13
  - Wilker Lucio
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-04-22 00:00:00 -03:00
18
+ date: 2011-04-28 00:00:00 -03:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -49,6 +49,7 @@ files:
49
49
  - lib/subdb/ui/swing.rb
50
50
  - lib/subdb/upload_cache.rb
51
51
  - lib/subdb/version.rb
52
+ - lib/subdb/video.rb
52
53
  - lib/subdb.rb
53
54
  - LICENSE
54
55
  - README.textile