subdb 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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