subdb 0.1.4 → 0.1.5

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