subdb 0.1.4 → 0.1.5
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/README.textile +9 -0
- data/bin/subdb +1 -1
- data/lib/subdb.rb +1 -0
- data/lib/subdb/client_utils.rb +23 -9
- data/{bin/subdb-gui → lib/subdb/ui/swing.rb} +59 -22
- data/lib/subdb/upload_cache.rb +64 -0
- data/lib/subdb/version.rb +1 -1
- metadata +23 -14
- data/javalib/filedrop.jar +0 -0
- data/vendor/multipart-post/composite_io.rb +0 -93
- data/vendor/multipart-post/multipart_post.rb +0 -3
- data/vendor/multipart-post/multipartable.rb +0 -13
- data/vendor/multipart-post/net/http/post/multipart.rb +0 -27
- data/vendor/multipart-post/parts.rb +0 -66
data/README.textile
CHANGED
@@ -2,6 +2,15 @@ h1. Ruby SubDB
|
|
2
2
|
|
3
3
|
This project aims to provide a simple API for accessing "SubBD":http://thesubdb.com/
|
4
4
|
|
5
|
+
h2. GUI client
|
6
|
+
|
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
|
+
|
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
|
11
|
+
|
12
|
+
Just download, open, and drag your files on program window. It will work in same way as Command Line tool.
|
13
|
+
|
5
14
|
h2. Installation
|
6
15
|
|
7
16
|
bc. gem install subdb
|
data/bin/subdb
CHANGED
@@ -31,7 +31,7 @@ Subdb::ClientUtils.sync files, ["pt", "en"] do |action, arg|
|
|
31
31
|
when :scan then puts "Scanning #{arg[0]} [#{arg[1] + 1}/#{files.length}]"
|
32
32
|
when :scanned then puts "Hash: #{arg.hash}"
|
33
33
|
when :remote then puts arg ? "Found with languages: #{arg}" : "No subtitle found"
|
34
|
-
when :uploading then puts "Local subtitle found
|
34
|
+
when :uploading then puts "Local uncached subtitle found, uploading..."
|
35
35
|
when :upload_ok then puts "Upload completed"
|
36
36
|
when :upload_failed then puts "Error on upload: #{arg[1]}"
|
37
37
|
when :downloading then puts "Download from remote..."
|
data/lib/subdb.rb
CHANGED
data/lib/subdb/client_utils.rb
CHANGED
@@ -25,7 +25,6 @@ module Subdb::ClientUtils
|
|
25
25
|
class << self
|
26
26
|
def scan_paths(paths)
|
27
27
|
video_ext = VIDEO_EXTENSIONS.join(",")
|
28
|
-
p paths
|
29
28
|
|
30
29
|
files = []
|
31
30
|
|
@@ -34,7 +33,7 @@ module Subdb::ClientUtils
|
|
34
33
|
path = path.chomp(File::SEPARATOR)
|
35
34
|
globpath = "#{path.gsub("\\", "/")}/**/*{#{video_ext}}"
|
36
35
|
|
37
|
-
|
36
|
+
yield globpath if block_given?
|
38
37
|
|
39
38
|
files = files.concat(Dir.glob(globpath))
|
40
39
|
else
|
@@ -46,6 +45,10 @@ module Subdb::ClientUtils
|
|
46
45
|
end
|
47
46
|
|
48
47
|
def sync(paths, languages = ["en"])
|
48
|
+
yield :loading_cache
|
49
|
+
cache = Subdb::UploadCache.new(cache_file_path)
|
50
|
+
|
51
|
+
results = {:download => [], :upload => []}
|
49
52
|
i = 0
|
50
53
|
|
51
54
|
for path in paths
|
@@ -59,32 +62,34 @@ module Subdb::ClientUtils
|
|
59
62
|
|
60
63
|
yield :scanned, subdb
|
61
64
|
|
62
|
-
|
63
|
-
|
64
|
-
yield :remote, remote
|
65
|
-
|
66
|
-
if sub and !remote
|
65
|
+
if sub and !cache.uploaded?(subdb.hash, sub)
|
67
66
|
yield :uploading, subdb
|
68
67
|
|
69
68
|
begin
|
70
69
|
subdb.upload(sub)
|
70
|
+
cache.push(subdb.hash, sub)
|
71
|
+
results[:upload].push(sub)
|
71
72
|
yield :upload_ok, subdb
|
72
73
|
rescue
|
73
74
|
yield :upload_failed, [subdb, $!]
|
74
75
|
end
|
75
76
|
end
|
76
77
|
|
77
|
-
if !sub
|
78
|
+
if !sub
|
78
79
|
yield :downloading, subdb
|
79
80
|
|
80
81
|
begin
|
81
82
|
downloaded = subdb.download(languages)
|
82
83
|
|
83
84
|
if downloaded
|
84
|
-
|
85
|
+
sub = base + ".srt"
|
86
|
+
|
87
|
+
File.open(sub, "wb") do |f|
|
85
88
|
f << downloaded
|
86
89
|
end
|
87
90
|
|
91
|
+
cache.push(subdb.hash, sub)
|
92
|
+
results[:download].push(sub)
|
88
93
|
yield :download_ok, subdb
|
89
94
|
else
|
90
95
|
yield :download_not_found, subdb
|
@@ -101,6 +106,11 @@ module Subdb::ClientUtils
|
|
101
106
|
|
102
107
|
yield :file_done, [subdb, i]
|
103
108
|
end
|
109
|
+
|
110
|
+
yield :storing_cache
|
111
|
+
cache.store!
|
112
|
+
|
113
|
+
results
|
104
114
|
end
|
105
115
|
|
106
116
|
def find_subtitle(path)
|
@@ -114,5 +124,9 @@ module Subdb::ClientUtils
|
|
114
124
|
|
115
125
|
nil
|
116
126
|
end
|
127
|
+
|
128
|
+
def cache_file_path
|
129
|
+
File.join((ENV["HOME"] || ENV["USERPROFILE"]), ".subdb_cache")
|
130
|
+
end
|
117
131
|
end
|
118
132
|
end
|
@@ -18,16 +18,13 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
-
$: << File.expand_path("../../lib", __FILE__)
|
22
|
-
$: << File.expand_path("../../vendor/multipart-post", __FILE__)
|
23
|
-
$: << File.expand_path("../..", __FILE__)
|
24
|
-
|
25
21
|
require 'subdb'
|
26
22
|
require 'java'
|
27
23
|
require 'javalib/filedrop.jar'
|
28
24
|
|
29
25
|
import java.awt.BorderLayout
|
30
26
|
import java.awt.Color
|
27
|
+
import java.awt.Dimension
|
31
28
|
import java.awt.image.BufferedImage
|
32
29
|
import javax.imageio.ImageIO
|
33
30
|
import javax.swing.BorderFactory
|
@@ -37,6 +34,8 @@ import javax.swing.JPanel
|
|
37
34
|
import javax.swing.JProgressBar
|
38
35
|
import javax.swing.JScrollPane
|
39
36
|
import javax.swing.JTextArea
|
37
|
+
import javax.swing.SwingConstants
|
38
|
+
import javax.swing.UIManager
|
40
39
|
|
41
40
|
include_class java.lang.System
|
42
41
|
include_class Java::FileDrop
|
@@ -59,26 +58,31 @@ class SubdbGUI < JFrame
|
|
59
58
|
rescue
|
60
59
|
end
|
61
60
|
|
61
|
+
border_size = 1
|
62
|
+
|
62
63
|
@dropper = JPanel.new
|
63
|
-
@dropper.set_border BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(
|
64
|
+
@dropper.set_border BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(border_size, border_size, border_size, border_size), BorderFactory.createLineBorder(Color.black))
|
64
65
|
|
65
66
|
@progress = JProgressBar.new(0, 100)
|
66
67
|
|
67
|
-
hint = JLabel.new("Arraste suas pastas ou arquivos com videos aqui.")
|
68
|
+
hint = JLabel.new("Arraste suas pastas ou arquivos com videos aqui.", SwingConstants::CENTER)
|
69
|
+
hint.set_preferred_size Dimension.new(500, 60)
|
68
70
|
|
69
71
|
@dropper.add(hint, BorderLayout::CENTER)
|
70
72
|
|
71
73
|
@log = JTextArea.new
|
74
|
+
@log.set_editable false
|
72
75
|
|
73
|
-
|
76
|
+
@scroller = JScrollPane.new(@log)
|
74
77
|
|
75
|
-
content_pane.add(@dropper, BorderLayout::
|
78
|
+
content_pane.add(@dropper, BorderLayout::NORTH)
|
79
|
+
content_pane.add(@scroller, BorderLayout::CENTER)
|
76
80
|
content_pane.add(@progress, BorderLayout::SOUTH)
|
77
81
|
|
78
82
|
FileDrop.new(nil, @dropper, self)
|
79
83
|
|
80
84
|
set_size 800, 300
|
81
|
-
set_resizable
|
85
|
+
set_resizable true
|
82
86
|
set_default_close_operation JFrame::EXIT_ON_CLOSE
|
83
87
|
set_location_relative_to nil
|
84
88
|
set_visible true
|
@@ -95,40 +99,73 @@ class SubdbGUI < JFrame
|
|
95
99
|
|
96
100
|
Thread.new do
|
97
101
|
@progress.set_indeterminate true
|
98
|
-
@progress.set_string "Gerando lista de arquivos..."
|
99
102
|
|
100
|
-
|
103
|
+
log "Generating file list..."
|
101
104
|
|
102
|
-
files = Subdb::ClientUtils.scan_paths(files)
|
105
|
+
files = Subdb::ClientUtils.scan_paths(files) do |path|
|
106
|
+
log "Scanning #{path}..."
|
107
|
+
end
|
103
108
|
|
104
|
-
|
109
|
+
log "Generation done, #{files.length} files to scan"
|
110
|
+
log_separator
|
105
111
|
|
106
112
|
@progress.set_indeterminate false
|
107
113
|
@progress.set_maximum files.length
|
108
114
|
|
109
|
-
Subdb::ClientUtils.sync files, ["pt", "en"] do |action, arg|
|
115
|
+
results = Subdb::ClientUtils.sync files, ["pt", "en"] do |action, arg|
|
110
116
|
case action
|
111
|
-
when :
|
112
|
-
when :
|
113
|
-
when :
|
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..."
|
114
121
|
when :upload_failed then error "Erro ao enviar legenda #{arg[0]}: #{arg[1]}"
|
115
|
-
when :downloading then
|
116
|
-
when :download_not_found then
|
122
|
+
when :downloading then log "Procurando legenda no servidor..."
|
123
|
+
when :download_not_found then log "Nenhuma legenda encontrada no seu indioma"
|
117
124
|
when :download_failed then error "Erro ao tentar baixar #{arg[0].path}: #{arg[1]}"
|
118
125
|
when :scan_failed then error "Erro ao abrir arquivo #{arg[0]}: #{arg[1]}"
|
119
|
-
when :
|
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]
|
120
131
|
end
|
121
132
|
end
|
122
133
|
|
123
|
-
|
134
|
+
log "Concluido"
|
135
|
+
log "#{results[:download].length} legendas baixadas"
|
136
|
+
log "#{results[:upload].length} legendas enviadas"
|
137
|
+
log_separator
|
124
138
|
|
125
139
|
@uploading = false
|
126
140
|
end
|
127
141
|
end
|
128
142
|
|
129
|
-
def
|
143
|
+
def log(msg)
|
144
|
+
@log.append msg + "\n"
|
145
|
+
|
146
|
+
sleep 0.1
|
147
|
+
|
148
|
+
scroll = @scroller.vertical_scroll_bar
|
149
|
+
scroll.value = scroll.maximum if scroll.value > scroll.maximum - (@scroller.height + 30)
|
150
|
+
|
130
151
|
puts msg
|
131
152
|
end
|
153
|
+
|
154
|
+
def log_separator
|
155
|
+
log "------------------------------"
|
156
|
+
end
|
157
|
+
|
158
|
+
def error(msg)
|
159
|
+
log msg
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
# try use system look and feel
|
164
|
+
begin
|
165
|
+
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
|
166
|
+
rescue
|
167
|
+
# no lucky...
|
132
168
|
end
|
133
169
|
|
170
|
+
# start application
|
134
171
|
SubdbGUI.new
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# Copyright (c) 2011 Wilker Lúcio
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'digest/sha1'
|
22
|
+
|
23
|
+
class Subdb::UploadCache
|
24
|
+
attr_reader :hash, :path
|
25
|
+
|
26
|
+
def self.subtitle_hash(path)
|
27
|
+
data = open(path, "rb") { |io| io.read }
|
28
|
+
Digest::SHA1.hexdigest(data)
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(path)
|
32
|
+
if File.exists? path
|
33
|
+
data = open(path, "rb") { |io| io.read }
|
34
|
+
|
35
|
+
begin
|
36
|
+
@hash = Marshal.load(data)
|
37
|
+
rescue TypeError => e
|
38
|
+
@hash = {}
|
39
|
+
end
|
40
|
+
else
|
41
|
+
@hash = {}
|
42
|
+
end
|
43
|
+
|
44
|
+
@path = path
|
45
|
+
end
|
46
|
+
|
47
|
+
def uploaded?(hash, path)
|
48
|
+
list = @hash[hash] || []
|
49
|
+
list.include? self.class.subtitle_hash(path)
|
50
|
+
end
|
51
|
+
|
52
|
+
def push(hash, path)
|
53
|
+
return if uploaded?(hash, path)
|
54
|
+
|
55
|
+
@hash[hash] ||= []
|
56
|
+
@hash[hash].push(self.class.subtitle_hash(path))
|
57
|
+
end
|
58
|
+
|
59
|
+
def store!
|
60
|
+
File.open(path, "wb") do |file|
|
61
|
+
file << Marshal.dump(@hash)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
data/lib/subdb/version.rb
CHANGED
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:
|
4
|
+
hash: 17
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 1
|
9
|
-
-
|
10
|
-
version: 0.1.
|
9
|
+
- 5
|
10
|
+
version: 0.1.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Wilker Lucio
|
@@ -15,10 +15,25 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2011-04-
|
18
|
+
date: 2011-04-22 00:00:00 -03:00
|
19
19
|
default_executable:
|
20
|
-
dependencies:
|
21
|
-
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
22
|
+
name: multipart-post
|
23
|
+
prerelease: false
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 19
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 1
|
33
|
+
- 0
|
34
|
+
version: 1.1.0
|
35
|
+
type: :runtime
|
36
|
+
version_requirements: *id001
|
22
37
|
description: API for SubDB
|
23
38
|
email:
|
24
39
|
- wilkerlucio@gmail.com
|
@@ -30,16 +45,11 @@ extra_rdoc_files: []
|
|
30
45
|
|
31
46
|
files:
|
32
47
|
- bin/subdb
|
33
|
-
- bin/subdb-gui
|
34
48
|
- lib/subdb/client_utils.rb
|
49
|
+
- lib/subdb/ui/swing.rb
|
50
|
+
- lib/subdb/upload_cache.rb
|
35
51
|
- lib/subdb/version.rb
|
36
52
|
- lib/subdb.rb
|
37
|
-
- javalib/filedrop.jar
|
38
|
-
- vendor/multipart-post/composite_io.rb
|
39
|
-
- vendor/multipart-post/multipart_post.rb
|
40
|
-
- vendor/multipart-post/multipartable.rb
|
41
|
-
- vendor/multipart-post/net/http/post/multipart.rb
|
42
|
-
- vendor/multipart-post/parts.rb
|
43
53
|
- LICENSE
|
44
54
|
- README.textile
|
45
55
|
has_rdoc: true
|
@@ -51,7 +61,6 @@ rdoc_options: []
|
|
51
61
|
|
52
62
|
require_paths:
|
53
63
|
- - lib
|
54
|
-
- vendor
|
55
64
|
required_ruby_version: !ruby/object:Gem::Requirement
|
56
65
|
none: false
|
57
66
|
requirements:
|
data/javalib/filedrop.jar
DELETED
Binary file
|
@@ -1,93 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# (c) Copyright 2007-2011 Nick Sieger.
|
3
|
-
# See the file README.txt included with the distribution for
|
4
|
-
# software license details.
|
5
|
-
#++
|
6
|
-
|
7
|
-
# Concatenate together multiple IO objects into a single, composite IO object
|
8
|
-
# for purposes of reading as a single stream.
|
9
|
-
#
|
10
|
-
# Usage:
|
11
|
-
#
|
12
|
-
# crio = CompositeReadIO.new(StringIO.new('one'), StringIO.new('two'), StringIO.new('three'))
|
13
|
-
# puts crio.read # => "onetwothree"
|
14
|
-
#
|
15
|
-
class CompositeReadIO
|
16
|
-
# Create a new composite-read IO from the arguments, all of which should
|
17
|
-
# respond to #read in a manner consistent with IO.
|
18
|
-
def initialize(*ios)
|
19
|
-
@ios = ios.flatten
|
20
|
-
end
|
21
|
-
|
22
|
-
# Read from the IO object, overlapping across underlying streams as necessary.
|
23
|
-
def read(amount = nil, buf = nil)
|
24
|
-
buffer = buf || ''
|
25
|
-
done = if amount; nil; else ''; end
|
26
|
-
partial_amount = amount
|
27
|
-
|
28
|
-
loop do
|
29
|
-
result = done
|
30
|
-
|
31
|
-
while !@ios.empty? && (result = @ios.first.read(partial_amount)) == done
|
32
|
-
@ios.shift
|
33
|
-
end
|
34
|
-
|
35
|
-
result.force_encoding("BINARY") if result.respond_to?(:force_encoding)
|
36
|
-
buffer << result if result
|
37
|
-
partial_amount -= result.length if partial_amount && result != done
|
38
|
-
|
39
|
-
break if partial_amount && partial_amount <= 0
|
40
|
-
break if result == done
|
41
|
-
end
|
42
|
-
|
43
|
-
if buffer.length > 0
|
44
|
-
buffer
|
45
|
-
else
|
46
|
-
done
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
# Convenience methods for dealing with files and IO that are to be uploaded.
|
52
|
-
class UploadIO
|
53
|
-
# Create an upload IO suitable for including in the params hash of a
|
54
|
-
# Net::HTTP::Post::Multipart.
|
55
|
-
#
|
56
|
-
# Can take two forms. The first accepts a filename and content type, and
|
57
|
-
# opens the file for reading (to be closed by finalizer). The second accepts
|
58
|
-
# an already-open IO, but also requires a third argument, the filename from
|
59
|
-
# which it was opened.
|
60
|
-
#
|
61
|
-
# UploadIO.new("file.txt", "text/plain")
|
62
|
-
# UploadIO.new(file_io, "text/plain", "file.txt")
|
63
|
-
attr_reader :content_type, :original_filename, :local_path, :io
|
64
|
-
|
65
|
-
def initialize(filename_or_io, content_type, filename = nil)
|
66
|
-
io = filename_or_io
|
67
|
-
local_path = ""
|
68
|
-
if io.respond_to? :read
|
69
|
-
local_path = filename_or_io.path
|
70
|
-
else
|
71
|
-
io = File.open(filename_or_io)
|
72
|
-
local_path = filename_or_io
|
73
|
-
end
|
74
|
-
filename ||= local_path
|
75
|
-
|
76
|
-
@content_type = content_type
|
77
|
-
@original_filename = File.basename(filename)
|
78
|
-
@local_path = local_path
|
79
|
-
@io = io
|
80
|
-
end
|
81
|
-
|
82
|
-
def self.convert!(io, content_type, original_filename, local_path)
|
83
|
-
raise ArgumentError, "convert! has been removed. You must now wrap IOs using:\nUploadIO.new(filename_or_io, content_type, filename=nil)\nPlease update your code."
|
84
|
-
end
|
85
|
-
|
86
|
-
def method_missing(*args)
|
87
|
-
@io.send(*args)
|
88
|
-
end
|
89
|
-
|
90
|
-
def respond_to?(meth)
|
91
|
-
@io.respond_to?(meth) || super(meth)
|
92
|
-
end
|
93
|
-
end
|
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'parts'
|
2
|
-
module Multipartable
|
3
|
-
DEFAULT_BOUNDARY = "-----------RubyMultipartPost"
|
4
|
-
def initialize(path, params, headers={}, boundary = DEFAULT_BOUNDARY)
|
5
|
-
super(path, headers)
|
6
|
-
parts = params.map {|k,v| Parts::Part.new(boundary, k, v)}
|
7
|
-
parts << Parts::EpiloguePart.new(boundary)
|
8
|
-
ios = parts.map{|p| p.to_io }
|
9
|
-
self.set_content_type("multipart/form-data", { "boundary" => boundary })
|
10
|
-
self.content_length = parts.inject(0) {|sum,i| sum + i.length }
|
11
|
-
self.body_stream = CompositeReadIO.new(*ios)
|
12
|
-
end
|
13
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
#--
|
2
|
-
# (c) Copyright 2007-2008 Nick Sieger.
|
3
|
-
# See the file README.txt included with the distribution for
|
4
|
-
# software license details.
|
5
|
-
#++
|
6
|
-
|
7
|
-
require 'net/http'
|
8
|
-
require 'stringio'
|
9
|
-
require 'cgi'
|
10
|
-
require 'composite_io'
|
11
|
-
require 'multipartable'
|
12
|
-
require 'parts'
|
13
|
-
|
14
|
-
module Net #:nodoc:
|
15
|
-
class HTTP #:nodoc:
|
16
|
-
class Put
|
17
|
-
class Multipart < Put
|
18
|
-
include Multipartable
|
19
|
-
end
|
20
|
-
end
|
21
|
-
class Post #:nodoc:
|
22
|
-
class Multipart < Post
|
23
|
-
include Multipartable
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
@@ -1,66 +0,0 @@
|
|
1
|
-
module Parts
|
2
|
-
module Part #:nodoc:
|
3
|
-
def self.new(boundary, name, value)
|
4
|
-
if value.respond_to? :content_type
|
5
|
-
FilePart.new(boundary, name, value)
|
6
|
-
else
|
7
|
-
ParamPart.new(boundary, name, value)
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
def length
|
12
|
-
@part.length
|
13
|
-
end
|
14
|
-
|
15
|
-
def to_io
|
16
|
-
@io
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
class ParamPart
|
21
|
-
include Part
|
22
|
-
def initialize(boundary, name, value)
|
23
|
-
@part = build_part(boundary, name, value)
|
24
|
-
@io = StringIO.new(@part)
|
25
|
-
end
|
26
|
-
|
27
|
-
def build_part(boundary, name, value)
|
28
|
-
part = ''
|
29
|
-
part << "--#{boundary}\r\n"
|
30
|
-
part << "Content-Disposition: form-data; name=\"#{name.to_s}\"\r\n"
|
31
|
-
part << "\r\n"
|
32
|
-
part << "#{value}\r\n"
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
# Represents a part to be filled from file IO.
|
37
|
-
class FilePart
|
38
|
-
include Part
|
39
|
-
attr_reader :length
|
40
|
-
def initialize(boundary, name, io)
|
41
|
-
file_length = io.respond_to?(:length) ? io.length : File.size(io.local_path)
|
42
|
-
@head = build_head(boundary, name, io.original_filename, io.content_type, file_length)
|
43
|
-
@length = @head.length + file_length
|
44
|
-
@io = CompositeReadIO.new(StringIO.new(@head), io, StringIO.new("\r\n"))
|
45
|
-
end
|
46
|
-
|
47
|
-
def build_head(boundary, name, filename, type, content_len)
|
48
|
-
part = ''
|
49
|
-
part << "--#{boundary}\r\n"
|
50
|
-
part << "Content-Disposition: form-data; name=\"#{name.to_s}\"; filename=\"#{filename}\"\r\n"
|
51
|
-
part << "Content-Length: #{content_len}\r\n"
|
52
|
-
part << "Content-Type: #{type}\r\n"
|
53
|
-
part << "Content-Transfer-Encoding: binary\r\n"
|
54
|
-
part << "\r\n"
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# Represents the epilogue or closing boundary.
|
59
|
-
class EpiloguePart
|
60
|
-
include Part
|
61
|
-
def initialize(boundary)
|
62
|
-
@part = "--#{boundary}--\r\n"
|
63
|
-
@io = StringIO.new(@part)
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|