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