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.
@@ -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 and none on server, uploading..."
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..."
@@ -27,6 +27,7 @@ require 'net/http/post/multipart'
27
27
 
28
28
  require 'subdb/version'
29
29
  require 'subdb/client_utils'
30
+ require 'subdb/upload_cache'
30
31
 
31
32
  class Subdb
32
33
  API = "http://api.thesubdb.com/"
@@ -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
- puts "Scanning #{globpath}"
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
- remote = subdb.search
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 and remote
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
- File.open(base + ".srt", "wb") do |f|
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(10, 10, 10, 10), BorderFactory.createLineBorder(Color.black))
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
- outer_panel = JPanel.new
76
+ @scroller = JScrollPane.new(@log)
74
77
 
75
- content_pane.add(@dropper, BorderLayout::CENTER)
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 false
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
- puts "Generating file list..."
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
- puts "Generation done"
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 :scan then @progress.set_string "Verificando #{arg[0]}"
112
- when :scanned then @progress.set_string "Verificando #{arg.pathbase} [#{arg.hash}]"
113
- when :uploading then @progress.set_string "Encontrada legenda local, subindo para o servidor..."
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 @progress.set_string "Baixando a legenda..."
116
- when :download_not_found then error "Nenhuma legenda encontrada no seu indioma para #{arg[0].pathbase}"
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 :file_done then @progress.set_value 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]
120
131
  end
121
132
  end
122
133
 
123
- @progress.set_string "Concluido"
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 error(msg)
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
@@ -20,5 +20,5 @@
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
22
  class Subdb
23
- VERSION = "0.1.4"
23
+ VERSION = "0.1.5"
24
24
  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: 19
4
+ hash: 17
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 4
10
- version: 0.1.4
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-20 00:00:00 -03:00
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:
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,3 +0,0 @@
1
- module MultipartPost
2
- VERSION = "1.1.0"
3
- 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