bartzon-osdb 0.0.6
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/Manifest +17 -0
- data/README.md +31 -0
- data/Rakefile +25 -0
- data/bartzon-osdb.gemspec +32 -0
- data/bin/getsub +9 -0
- data/lib/osdb.rb +37 -0
- data/lib/osdb/language.rb +84 -0
- data/lib/osdb/movie.rb +81 -0
- data/lib/osdb/options.rb +51 -0
- data/lib/osdb/server.rb +53 -0
- data/lib/osdb/sub.rb +91 -0
- data/osdb.gemspec +32 -0
- data/spec/fixtures/somemovie.avi +0 -0
- data/spec/osdb/language_spec.rb +57 -0
- data/spec/osdb/movie_spec.rb +11 -0
- data/spec/osdb/server_spec.rb +79 -0
- data/spec/osdb/sub_spec.rb +33 -0
- data/spec/spec_helper.rb +7 -0
- metadata +97 -0
data/Manifest
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
Manifest
|
2
|
+
README.md
|
3
|
+
Rakefile
|
4
|
+
bin/getsub
|
5
|
+
lib/osdb.rb
|
6
|
+
lib/osdb/language.rb
|
7
|
+
lib/osdb/movie.rb
|
8
|
+
lib/osdb/options.rb
|
9
|
+
lib/osdb/server.rb
|
10
|
+
lib/osdb/sub.rb
|
11
|
+
osdb.gemspec
|
12
|
+
spec/fixtures/somemovie.avi
|
13
|
+
spec/osdb/language_spec.rb
|
14
|
+
spec/osdb/movie_spec.rb
|
15
|
+
spec/osdb/server_spec.rb
|
16
|
+
spec/osdb/sub_spec.rb
|
17
|
+
spec/spec_helper.rb
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# OSDb
|
2
|
+
|
3
|
+
Client library for the [OSDb protocol](http://trac.opensubtitles.org/projects/opensubtitles/wiki/XMLRPC).
|
4
|
+
Currently the implentation is limited to movie identification and subtitles search
|
5
|
+
|
6
|
+
## Examples
|
7
|
+
|
8
|
+
Just read the source of `bin/getsub` it is a typical example of OSDb's capacities.
|
9
|
+
|
10
|
+
## getsub
|
11
|
+
|
12
|
+
The osdb gem provide a simple script to find and download the best subtitle on
|
13
|
+
[opensubtitles.org](http://www.opensubtitles.org/) for your video file.
|
14
|
+
|
15
|
+
### Installation
|
16
|
+
|
17
|
+
$ gem install bartzon-osdb
|
18
|
+
|
19
|
+
### Usage
|
20
|
+
|
21
|
+
You just have to execute `getsub` with some video files in arguments:
|
22
|
+
|
23
|
+
$ getsub somemovie.avi othermovie.mkv
|
24
|
+
|
25
|
+
Or specify a directory to search recursively:
|
26
|
+
|
27
|
+
$ getsub -d ~/Movies
|
28
|
+
|
29
|
+
For options details just run:
|
30
|
+
|
31
|
+
$ getsub --help
|
data/Rakefile
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'spec/rake/spectask'
|
4
|
+
|
5
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
6
|
+
spec.libs << 'lib' << 'spec'
|
7
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
8
|
+
end
|
9
|
+
|
10
|
+
begin
|
11
|
+
require 'echoe'
|
12
|
+
|
13
|
+
Echoe.new('bartzon-osdb', '0.0.6') do |p|
|
14
|
+
p.description = "Ruby library to access OSDb services like OpenSubtitles.org"
|
15
|
+
p.url = "http://github.com/bartzon/ruby-osdb"
|
16
|
+
p.author = "Jean Boussier, Bart Zonneveld"
|
17
|
+
p.email = "jean.boussier @nospam@ gmail.com, loop@superinfinite.com"
|
18
|
+
end
|
19
|
+
|
20
|
+
rescue LoadError => e
|
21
|
+
puts "Failed to load Echoe"
|
22
|
+
puts e.message
|
23
|
+
end
|
24
|
+
|
25
|
+
task :default => :spec
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{bartzon-osdb}
|
5
|
+
s.version = "0.0.6"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Jean Boussier, Bart Zonneveld"]
|
9
|
+
s.date = %q{2010-12-09}
|
10
|
+
s.default_executable = %q{getsub}
|
11
|
+
s.description = %q{Ruby library to access OSDb services like OpenSubtitles.org}
|
12
|
+
s.email = %q{jean.boussier @nospam@ gmail.com, loop@superinfinite.com}
|
13
|
+
s.executables = ["getsub"]
|
14
|
+
s.extra_rdoc_files = ["README.md", "bin/getsub", "lib/osdb.rb", "lib/osdb/language.rb", "lib/osdb/movie.rb", "lib/osdb/options.rb", "lib/osdb/server.rb", "lib/osdb/sub.rb"]
|
15
|
+
s.files = ["Manifest", "README.md", "Rakefile", "bin/getsub", "lib/osdb.rb", "lib/osdb/language.rb", "lib/osdb/movie.rb", "lib/osdb/options.rb", "lib/osdb/server.rb", "lib/osdb/sub.rb", "osdb.gemspec", "spec/fixtures/somemovie.avi", "spec/osdb/language_spec.rb", "spec/osdb/movie_spec.rb", "spec/osdb/server_spec.rb", "spec/osdb/sub_spec.rb", "spec/spec_helper.rb", "bartzon-osdb.gemspec"]
|
16
|
+
s.homepage = %q{http://github.com/bartzon/ruby-osdb}
|
17
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Bartzon-osdb", "--main", "README.md"]
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
s.rubyforge_project = %q{bartzon-osdb}
|
20
|
+
s.rubygems_version = %q{1.3.7}
|
21
|
+
s.summary = %q{Ruby library to access OSDb services like OpenSubtitles.org}
|
22
|
+
|
23
|
+
if s.respond_to? :specification_version then
|
24
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
25
|
+
s.specification_version = 3
|
26
|
+
|
27
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
28
|
+
else
|
29
|
+
end
|
30
|
+
else
|
31
|
+
end
|
32
|
+
end
|
data/bin/getsub
ADDED
data/lib/osdb.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'xmlrpc/client'
|
2
|
+
|
3
|
+
module OSDb
|
4
|
+
base_path = File.expand_path(File.dirname(__FILE__) + '/osdb')
|
5
|
+
autoload :Language, "#{base_path}/language"
|
6
|
+
autoload :Movie, "#{base_path}/movie"
|
7
|
+
autoload :Server, "#{base_path}/server"
|
8
|
+
autoload :Sub, "#{base_path}/sub"
|
9
|
+
autoload :Options, "#{base_path}/options"
|
10
|
+
|
11
|
+
def self.log(str='')
|
12
|
+
puts str unless options[:silent]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.options
|
16
|
+
@@options ||= OSDb::Options.parse_options
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.curl_available?
|
20
|
+
%x{ curl --version 2> /dev/null > /dev/null }
|
21
|
+
$?.success?
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.wget_available?
|
25
|
+
%x{ wget --version 2> /dev/null > /dev/null }
|
26
|
+
$?.success?
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.server
|
30
|
+
@@server ||= OSDb::Server.new(
|
31
|
+
:host => 'api.opensubtitles.org',
|
32
|
+
:path => '/xml-rpc',
|
33
|
+
:timeout => 90,
|
34
|
+
:useragent => "SubDownloader 2.0.10" # register useragent ? WTF ? too boring.
|
35
|
+
)
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module OSDb
|
2
|
+
|
3
|
+
LANGUAGES = [
|
4
|
+
{:iso639_1 => 'sq', :iso639_2b => 'alb', :locale => 'sq', :name => 'Albanian'},
|
5
|
+
{:iso639_1 => 'ar', :iso639_2b => 'ara', :locale => 'ar', :name => 'Arabic'},
|
6
|
+
{:iso639_1 => 'hy', :iso639_2b => 'arm', :locale => 'hy', :name => 'Armenian'},
|
7
|
+
{:iso639_1 => 'ms', :iso639_2b => 'may', :locale => 'ms', :name => 'Malay'},
|
8
|
+
{:iso639_1 => 'bs', :iso639_2b => 'bos', :locale => 'bs', :name => 'Bosnian'},
|
9
|
+
{:iso639_1 => 'bg', :iso639_2b => 'bul', :locale => 'bg', :name => 'Bulgarian'},
|
10
|
+
{:iso639_1 => 'ca', :iso639_2b => 'cat', :locale => 'ca', :name => 'Catalan'},
|
11
|
+
{:iso639_1 => 'eu', :iso639_2b => 'eus', :locale => 'eu', :name => 'Basque'},
|
12
|
+
{:iso639_1 => 'zh', :iso639_2b => 'chi', :locale => 'zh_CN', :name => 'Chinese (China)'},
|
13
|
+
{:iso639_1 => 'hr', :iso639_2b => 'hrv', :locale => 'hr', :name => 'Croatian'},
|
14
|
+
{:iso639_1 => 'cs', :iso639_2b => 'cze', :locale => 'cs', :name => 'Czech'},
|
15
|
+
{:iso639_1 => 'da', :iso639_2b => 'dan', :locale => 'da', :name => 'Danish'},
|
16
|
+
{:iso639_1 => 'nl', :iso639_2b => 'dut', :locale => 'nl', :name => 'Dutch'},
|
17
|
+
{:iso639_1 => 'en', :iso639_2b => 'eng', :locale => 'en', :name => 'English (US)'},
|
18
|
+
{:iso639_1 => 'en', :iso639_2b => 'bre', :locale => 'en_GB', :name => 'English (UK)'},
|
19
|
+
{:iso639_1 => 'eo', :iso639_2b => 'epo', :locale => 'eo', :name => 'Esperanto'},
|
20
|
+
{:iso639_1 => 'et', :iso639_2b => 'est', :locale => 'et', :name => 'Estonian'},
|
21
|
+
{:iso639_1 => 'fi', :iso639_2b => 'fin', :locale => 'fi', :name => 'Finnish'},
|
22
|
+
{:iso639_1 => 'fr', :iso639_2b => 'fre', :locale => 'fr', :name => 'French'},
|
23
|
+
{:iso639_1 => 'gl', :iso639_2b => 'glg', :locale => 'gl', :name => 'Galician'},
|
24
|
+
{:iso639_1 => 'ka', :iso639_2b => 'geo', :locale => 'ka', :name => 'Georgian'},
|
25
|
+
{:iso639_1 => 'de', :iso639_2b => 'ger', :locale => 'de', :name => 'German'},
|
26
|
+
{:iso639_1 => 'el', :iso639_2b => 'ell', :locale => 'el', :name => 'Greek'},
|
27
|
+
{:iso639_1 => 'he', :iso639_2b => 'heb', :locale => 'he', :name => 'Hebrew'},
|
28
|
+
{:iso639_1 => 'hu', :iso639_2b => 'hun', :locale => 'hu', :name => 'Hungarian'},
|
29
|
+
{:iso639_1 => 'id', :iso639_2b => 'ind', :locale => 'id', :name => 'Indonesian'},
|
30
|
+
{:iso639_1 => 'it', :iso639_2b => 'ita', :locale => 'it', :name => 'Italian'},
|
31
|
+
{:iso639_1 => 'ja', :iso639_2b => 'jpn', :locale => 'ja', :name => 'Japanese'},
|
32
|
+
{:iso639_1 => 'kk', :iso639_2b => 'kaz', :locale => 'kk', :name => 'Kazakh'},
|
33
|
+
{:iso639_1 => 'ko', :iso639_2b => 'kor', :locale => 'ko', :name => 'Korean'},
|
34
|
+
{:iso639_1 => 'lv', :iso639_2b => 'lav', :locale => 'lv', :name => 'Latvian'},
|
35
|
+
{:iso639_1 => 'lt', :iso639_2b => 'lit', :locale => 'lt', :name => 'Lithuanian'},
|
36
|
+
{:iso639_1 => 'lb', :iso639_2b => 'ltz', :locale => 'lb', :name => 'Luxembourgish'},
|
37
|
+
{:iso639_1 => 'mk', :iso639_2b => 'mac', :locale => 'mk', :name => 'Macedonian'},
|
38
|
+
{:iso639_1 => 'no', :iso639_2b => 'nor', :locale => 'no', :name => 'Norwegian'},
|
39
|
+
{:iso639_1 => 'fa', :iso639_2b => 'per', :locale => 'fa', :name => 'Persian'},
|
40
|
+
{:iso639_1 => 'pl', :iso639_2b => 'pol', :locale => 'pl', :name => 'Polish'},
|
41
|
+
{:iso639_1 => 'pt', :iso639_2b => 'por', :locale => 'pt_PT', :name => 'Portuguese (Portugal)'},
|
42
|
+
{:iso639_1 => 'pb', :iso639_2b => 'pob', :locale => 'pt_BR', :name => 'Portuguese (Brazil)'},
|
43
|
+
{:iso639_1 => 'ro', :iso639_2b => 'rum', :locale => 'ro', :name => 'Romanian'},
|
44
|
+
{:iso639_1 => 'ru', :iso639_2b => 'rus', :locale => 'ru', :name => 'Russian'},
|
45
|
+
{:iso639_1 => 'sr', :iso639_2b => 'scc', :locale => 'sr', :name => 'Serbian'},
|
46
|
+
{:iso639_1 => 'sk', :iso639_2b => 'slo', :locale => 'sk', :name => 'Slovak'},
|
47
|
+
{:iso639_1 => 'sl', :iso639_2b => 'slv', :locale => 'sl', :name => 'Slovenian'},
|
48
|
+
{:iso639_1 => 'es', :iso639_2b => 'spa', :locale => 'es_ES', :name => 'Spanish (Spain)'},
|
49
|
+
{:iso639_1 => 'sv', :iso639_2b => 'swe', :locale => 'sv', :name => 'Swedish'},
|
50
|
+
{:iso639_1 => 'th', :iso639_2b => 'tha', :locale => 'th', :name => 'Thai'},
|
51
|
+
{:iso639_1 => 'tr', :iso639_2b => 'tur', :locale => 'tr', :name => 'Turkish'},
|
52
|
+
{:iso639_1 => 'uk', :iso639_2b => 'ukr', :locale => 'uk', :name => 'Ukrainian'},
|
53
|
+
{:iso639_1 => 'vi', :iso639_2b => 'vie', :locale => 'vi', :name => 'Vietnamese'}
|
54
|
+
]
|
55
|
+
|
56
|
+
class Language
|
57
|
+
|
58
|
+
class << self
|
59
|
+
|
60
|
+
def from_locale(locale)
|
61
|
+
locale = locale.split('.').first
|
62
|
+
lang = LANGUAGES.find{ |lang| lang[:locale] == locale }
|
63
|
+
return from_locale(locale.split('_').first) if !lang && locale.include?('_')
|
64
|
+
new(lang)
|
65
|
+
end
|
66
|
+
|
67
|
+
def from_iso639_2b(code)
|
68
|
+
new(LANGUAGES.find{ |lang| lang[:iso639_2b] == code })
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
attr_reader :name, :to_iso639_1, :to_iso639_2b, :to_locale
|
74
|
+
|
75
|
+
def initialize(hash)
|
76
|
+
@name = hash[:name]
|
77
|
+
@to_iso639_1 = hash[:iso639_1]
|
78
|
+
@to_iso639_2b = hash[:iso639_2b]
|
79
|
+
@to_locale = hash[:locale]
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
data/lib/osdb/movie.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
module OSDb
|
2
|
+
class Movie
|
3
|
+
|
4
|
+
attr_reader :path
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def get_movie_list
|
8
|
+
|
9
|
+
if dir = OSDb.options[:dir]
|
10
|
+
path = Dir.glob(File.join(dir, '**', "*.{#{OSDb.options[:movie_exts]}}"))
|
11
|
+
movies = path.map { |path| new(path) }
|
12
|
+
else
|
13
|
+
movies = ARGV.map{ |path| OSDb::Movie.new(path) }
|
14
|
+
end
|
15
|
+
movies.reject!(&:has_sub?) unless OSDb.options[:force]
|
16
|
+
|
17
|
+
if movies.empty?
|
18
|
+
OSDb.log "All movies in #{dir} already have subtitles."
|
19
|
+
exit 1
|
20
|
+
end
|
21
|
+
|
22
|
+
movies
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(path)
|
27
|
+
@path = path
|
28
|
+
end
|
29
|
+
|
30
|
+
def has_sub?
|
31
|
+
exist = false
|
32
|
+
%w(.srt .sub .smi).each{ |ext| exist ||= File.exist?(path.gsub(File.extname(path), ext)) }
|
33
|
+
exist
|
34
|
+
end
|
35
|
+
|
36
|
+
def sub_path(format)
|
37
|
+
path.gsub(File.extname(path), ".#{format}")
|
38
|
+
end
|
39
|
+
|
40
|
+
def hash
|
41
|
+
@hash ||= self.class.compute_hash(path)
|
42
|
+
end
|
43
|
+
|
44
|
+
def size
|
45
|
+
@size ||= File.size(path)
|
46
|
+
end
|
47
|
+
|
48
|
+
CHUNK_SIZE = 64 * 1024 # in bytes
|
49
|
+
|
50
|
+
# from http://trac.opensubtitles.org/projects/opensubtitles/wiki/HashSourceCodes
|
51
|
+
def self.compute_hash(path)
|
52
|
+
begin
|
53
|
+
filesize = File.size(path)
|
54
|
+
hash = filesize
|
55
|
+
|
56
|
+
# Read 64 kbytes, divide up into 64 bits and add each
|
57
|
+
# to hash. Do for beginning and end of file.
|
58
|
+
File.open(path, 'rb') do |f|
|
59
|
+
# Q = unsigned long long = 64 bit
|
60
|
+
f.read(CHUNK_SIZE).unpack("Q*").each do |n|
|
61
|
+
hash = hash + n & 0xffffffffffffffff # to remain as 64 bit number
|
62
|
+
end
|
63
|
+
|
64
|
+
f.seek([0, filesize - CHUNK_SIZE].max, IO::SEEK_SET)
|
65
|
+
|
66
|
+
# And again for the end of the file
|
67
|
+
f.read(CHUNK_SIZE).unpack("Q*").each do |n|
|
68
|
+
hash = hash + n & 0xffffffffffffffff
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
sprintf("%016x", hash)
|
73
|
+
rescue Errno::EPERM
|
74
|
+
OSDb.log("* could not read #{path}")
|
75
|
+
rescue RuntimeError
|
76
|
+
OSDb.log("* could not read #{path}")
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
data/lib/osdb/options.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
module OSDb
|
2
|
+
class Options
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def env_lang
|
6
|
+
OSDb::Language.from_locale(ENV['LANG'])
|
7
|
+
end
|
8
|
+
|
9
|
+
def parse_options
|
10
|
+
@@options = {:languages => [env_lang.to_iso639_2b], :force => false, :dir => nil, :silent => false, :movie_exts => %w{avi,mpg,m4v,mkv,mov,ogv,divx} }
|
11
|
+
|
12
|
+
@@parser ||= OptionParser.new do |opts|
|
13
|
+
opts.banner = "Automatically download subs for your video files using opensubtitles.org"
|
14
|
+
opts.separator ""
|
15
|
+
opts.separator "Usage: getsub [options] DIRECTORY | VIDEO_FILE [VIDEO_FILE ...]"
|
16
|
+
opts.separator ""
|
17
|
+
opts.separator "Main options:"
|
18
|
+
|
19
|
+
opts.on("-e", "--extensions EXTENSIONS", "Movie extensions to search for when using --directory. Default: #{@@options[:movie_exts].join(',')}") do |exts|
|
20
|
+
@@options[:movie_exts] = exts.split(',')
|
21
|
+
end
|
22
|
+
|
23
|
+
opts.on("-l", "--languages LANGUAGES", "Sub language ISO 963 code separated by comma's like fre or eng. Default: env $LANG (#{env_lang.to_iso639_2b})") do |languages|
|
24
|
+
@@options[:languages] = languages.split(',')
|
25
|
+
end
|
26
|
+
|
27
|
+
opts.on("-d", "--directory DIRECTORY", "Specify a directory to search recursively for movies") do |dir|
|
28
|
+
@@options[:dir] = dir
|
29
|
+
end
|
30
|
+
|
31
|
+
opts.on("-f", "--force", "Download sub even if video already has one") { @@options[:force] = true }
|
32
|
+
|
33
|
+
opts.on("-s", "--silent", "Don't output anything") { options[:silent] = true }
|
34
|
+
end
|
35
|
+
@@parser.parse!
|
36
|
+
|
37
|
+
if !@@options[:dir] && ARGV.empty?
|
38
|
+
help
|
39
|
+
exit 1
|
40
|
+
end
|
41
|
+
|
42
|
+
@@options
|
43
|
+
end
|
44
|
+
|
45
|
+
def help
|
46
|
+
puts @@parser.help
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
data/lib/osdb/server.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
module OSDb
|
2
|
+
|
3
|
+
class LoginFailed < ::Exception
|
4
|
+
end
|
5
|
+
|
6
|
+
class Server
|
7
|
+
|
8
|
+
attr_reader :username, :password, :language, :useragent, :client
|
9
|
+
|
10
|
+
def initialize(options={})
|
11
|
+
@username = options[:username] || ''
|
12
|
+
@password = options[:password] || ''
|
13
|
+
@language = options[:language] || 'eng'
|
14
|
+
@useragent = options[:useragent] || 'ruby-osdb v0.1'
|
15
|
+
@client = ::XMLRPC::Client.new(*options.values_at(:host, :path, :port, :proxy_host, :proxy_port, :http_user, :http_password, :use_ssl, :timeout))
|
16
|
+
end
|
17
|
+
|
18
|
+
def token
|
19
|
+
@token ||= login
|
20
|
+
end
|
21
|
+
|
22
|
+
def login
|
23
|
+
response = client.call('LogIn', username, password, language, useragent)
|
24
|
+
if response['status'] != '200 OK'
|
25
|
+
raise LoginFailed.new("Failed to login with #{username} : #{password}. Server return code: #{response['status']}")
|
26
|
+
end
|
27
|
+
response['token']
|
28
|
+
end
|
29
|
+
|
30
|
+
def logout
|
31
|
+
client.call('LogOut', token)
|
32
|
+
@token = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def check_movie_hash(*hashes)
|
36
|
+
client.call('CheckMovieHash', token, hashes)
|
37
|
+
end
|
38
|
+
|
39
|
+
def search_subtitles(*queries)
|
40
|
+
subs = client.call('SearchSubtitles', token, queries)['data']
|
41
|
+
subs ? subs.map{ |s| Sub.new(s) } : []
|
42
|
+
rescue EOFError
|
43
|
+
[]
|
44
|
+
rescue RuntimeError
|
45
|
+
[]
|
46
|
+
end
|
47
|
+
|
48
|
+
def info
|
49
|
+
client.call('ServerInfo')
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
data/lib/osdb/sub.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module OSDb
|
4
|
+
|
5
|
+
class Sub
|
6
|
+
|
7
|
+
attr_reader :url, :format, :language, :rating, :movie_name, :raw_data
|
8
|
+
|
9
|
+
class << self
|
10
|
+
def fetch_all
|
11
|
+
movies = Movie.get_movie_list
|
12
|
+
|
13
|
+
movies.each do |movie|
|
14
|
+
begin
|
15
|
+
OSDb.log "* search subs for: #{movie.path}"
|
16
|
+
subs = OSDb.server.search_subtitles(:moviehash => movie.hash, :moviebytesize => movie.size, :sublanguageid => OSDb.options[:languages].join(','))
|
17
|
+
if subs.any?
|
18
|
+
if sub = select_sub(subs)
|
19
|
+
sub_path = movie.sub_path(sub.format)
|
20
|
+
Sub.download!(sub.url, sub_path)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
OSDb.log "* no sub found"
|
24
|
+
end
|
25
|
+
OSDb.log
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def download!(url, local_path)
|
31
|
+
OSDb.log "* download #{url} to #{local_path}"
|
32
|
+
if OSDb.curl_available?
|
33
|
+
%x{ curl -s '#{url}' | gunzip > "#{local_path}" }
|
34
|
+
elsif OSDb.wget_available?
|
35
|
+
%x{ wget -O -quiet - '#{url}' | gunzip > "#{local_path}"}
|
36
|
+
else
|
37
|
+
OSDb.log "Can't found any curl or wget please install one of them or manualy download your sub"
|
38
|
+
OSDb.log url
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def select_sub(subs)
|
44
|
+
return subs.first if subs.length == 1
|
45
|
+
movies = group_by_movie_name(subs)
|
46
|
+
return movies.values.first.max if movies.length == 1
|
47
|
+
if selected_movie_subs = ask_user_to_identify_movie(movies)
|
48
|
+
selected_movie_subs.max
|
49
|
+
else
|
50
|
+
nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def group_by_movie_name(subs)
|
55
|
+
subs.inject({}) do |hash, sub|
|
56
|
+
hash[sub.movie_name] ||= []
|
57
|
+
hash[sub.movie_name] << sub
|
58
|
+
hash
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def ask_user_to_identify_movie(movies)
|
63
|
+
puts "D'oh! You stumbled upon a hash conflict, please resolve it:"
|
64
|
+
puts
|
65
|
+
puts " 0 - none of the below"
|
66
|
+
movies.keys.each_with_index do |name, index|
|
67
|
+
puts " #{index+1} - #{name}"
|
68
|
+
end
|
69
|
+
puts
|
70
|
+
print 'id: '
|
71
|
+
str = STDIN.gets.to_i
|
72
|
+
str == 0 ? false : movies[movies.keys[str.to_i]]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def initialize(data)
|
77
|
+
@url = URI.parse(data['SubDownloadLink'])
|
78
|
+
@format = data['SubFormat']
|
79
|
+
@language = Language.from_iso639_2b(data['SubLanguageID'])
|
80
|
+
@rating = data['SubRating'].to_f
|
81
|
+
@movie_name = data['MovieName']
|
82
|
+
@raw_data = data
|
83
|
+
end
|
84
|
+
|
85
|
+
def <=>(other)
|
86
|
+
rating <=> other.rating
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
data/osdb.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{osdb}
|
5
|
+
s.version = "0.0.5"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Jean Boussier, Bart Zonneveld"]
|
9
|
+
s.date = %q{2010-12-09}
|
10
|
+
s.default_executable = %q{getsub}
|
11
|
+
s.description = %q{Ruby library to access OSDb services like OpenSubtitles.org}
|
12
|
+
s.email = %q{jean.boussier @nospam@ gmail.com, loop@superinfinite.com}
|
13
|
+
s.executables = ["getsub"]
|
14
|
+
s.extra_rdoc_files = ["README.md", "bin/getsub", "lib/osdb.rb", "lib/osdb/language.rb", "lib/osdb/movie.rb", "lib/osdb/server.rb", "lib/osdb/sub.rb", "lib/osdb/options.rb"]
|
15
|
+
s.files = FileList['lib/**/*.rb', 'bin/*', '[A-Z]*', 'spec/**/*'].to_a
|
16
|
+
s.homepage = %q{http://github.com/bartzon/ruby-osdb}
|
17
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Osdb", "--main", "README.md"]
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
s.rubyforge_project = %q{osdb}
|
20
|
+
s.rubygems_version = %q{1.3.7}
|
21
|
+
s.summary = %q{Ruby library to access OSDb services like OpenSubtitles.org}
|
22
|
+
|
23
|
+
if s.respond_to? :specification_version then
|
24
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
25
|
+
s.specification_version = 3
|
26
|
+
|
27
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
28
|
+
else
|
29
|
+
end
|
30
|
+
else
|
31
|
+
end
|
32
|
+
end
|
Binary file
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe OSDb::Language do
|
4
|
+
|
5
|
+
describe '.from_locale' do
|
6
|
+
|
7
|
+
describe "simple locale" do
|
8
|
+
|
9
|
+
subject{ OSDb::Language.from_locale('fr') }
|
10
|
+
|
11
|
+
its(:name) { should == 'French' }
|
12
|
+
|
13
|
+
its(:to_iso639_1) { should == 'fr' }
|
14
|
+
|
15
|
+
its(:to_iso639_2b) { should == 'fre' }
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "locale with territory" do
|
20
|
+
|
21
|
+
subject{ OSDb::Language.from_locale('en_US') }
|
22
|
+
|
23
|
+
its(:name) { should == 'English (US)' }
|
24
|
+
|
25
|
+
its(:to_iso639_1) { should == 'en' }
|
26
|
+
|
27
|
+
its(:to_iso639_2b) { should == 'eng' }
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "locale with territory and encoding" do
|
32
|
+
|
33
|
+
subject{ OSDb::Language.from_locale('es_ES.UTF8') }
|
34
|
+
|
35
|
+
its(:name) { should == 'Spanish (Spain)' }
|
36
|
+
|
37
|
+
its(:to_iso639_1) { should == 'es' }
|
38
|
+
|
39
|
+
its(:to_iso639_2b) { should == 'spa' }
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
describe ".from_iso_639_2b" do
|
46
|
+
|
47
|
+
subject{ OSDb::Language.from_iso639_2b('bre') }
|
48
|
+
|
49
|
+
its(:name) { should == 'English (UK)' }
|
50
|
+
|
51
|
+
its(:to_iso639_1) { should == 'en' }
|
52
|
+
|
53
|
+
its(:to_locale) { should == 'en_GB' }
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe OSDb::Server do
|
4
|
+
|
5
|
+
before :all do
|
6
|
+
@server = OSDb::Server.new(
|
7
|
+
:host => 'api.opensubtitles.org',
|
8
|
+
:path => '/xml-rpc',
|
9
|
+
:timeout => 60, # OS.org is very very slow ....
|
10
|
+
:useragent => 'OS Test User Agent'
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
subject { @server }
|
15
|
+
|
16
|
+
it 'should respond to #info' do
|
17
|
+
hash_including(
|
18
|
+
"seconds" => instance_of(Float),
|
19
|
+
"last_update_strings" => instance_of(Hash),
|
20
|
+
"website_url"=>"http://www.opensubtitles.org",
|
21
|
+
"contact"=>"admin@opensubtitles.org",
|
22
|
+
"application"=>"OpenSuber v0.2",
|
23
|
+
"xmlrpc_version"=>"0.1",
|
24
|
+
"xmlrpc_url"=>"http://api.opensubtitles.net/xml-rpc"
|
25
|
+
).should == subject.info
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'should automatically call #login when token is needed' do
|
29
|
+
subject.instance_variable_get('@token').should be_nil
|
30
|
+
subject.token.should match(/[a-z0-9]{26}/)
|
31
|
+
subject.instance_variable_get('@token').should == subject.token
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'should clear @login after #logout' do
|
35
|
+
expect{
|
36
|
+
subject.logout
|
37
|
+
}.to change{ subject.instance_variable_get('@token') }.from(instance_of(String)).to(nil)
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#check_movie_hash" do
|
41
|
+
|
42
|
+
it 'should identify movie' do
|
43
|
+
subject.check_movie_hash('37d0c7d0cfcbe280')['data'].should == {
|
44
|
+
"37d0c7d0cfcbe280" => {
|
45
|
+
"MovieYear" => "1996",
|
46
|
+
"MovieImdbID" => "0117500",
|
47
|
+
"MovieName" => "The Rock",
|
48
|
+
"MovieHash" => "37d0c7d0cfcbe280"
|
49
|
+
}
|
50
|
+
}
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#search_subtitles' do
|
56
|
+
|
57
|
+
it 'can search by hash and size' do
|
58
|
+
subs = subject.search_subtitles(:moviehash => 'bd71526264fd8bd9', :moviebytesize => '183406990', :sublanguageid => 'fre')
|
59
|
+
subs.should be_a(Array)
|
60
|
+
subs.length.should >= 2
|
61
|
+
subs.each do |sub|
|
62
|
+
sub.language.name.should == 'French'
|
63
|
+
sub.raw_data['MovieName'].should == '"How I Met Your Mother"'
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'can search by imdbid' do
|
68
|
+
subs = subject.search_subtitles(:imdbid => "0117500", :sublanguageid => 'fre')
|
69
|
+
subs.should be_a(Array)
|
70
|
+
subs.length.should >= 1
|
71
|
+
subs.each do |sub|
|
72
|
+
sub.language.name.should == 'French'
|
73
|
+
sub.raw_data['MovieName'].should == 'The Rock'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
2
|
+
|
3
|
+
describe OSDb::Sub do
|
4
|
+
|
5
|
+
before :each do
|
6
|
+
@response = {
|
7
|
+
'SubFormat' => 'srt',
|
8
|
+
'SubDownloadLink' => 'http://example.com/foo.srt.gz',
|
9
|
+
'SubRating' => '7.89',
|
10
|
+
'SubLanguageID' => 'dut',
|
11
|
+
'MovieName' => 'Lock, Stock and Two Smoking Barrels'
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
subject do
|
16
|
+
OSDb::Sub.new @response
|
17
|
+
end
|
18
|
+
|
19
|
+
its(:format) { should == 'srt' }
|
20
|
+
|
21
|
+
its(:url) { should be_an(URI::HTTP) }
|
22
|
+
|
23
|
+
its(:language) { should be_a(OSDb::Language) }
|
24
|
+
|
25
|
+
its(:rating) { should == 7.89 }
|
26
|
+
|
27
|
+
its(:movie_name) { should == 'Lock, Stock and Two Smoking Barrels' }
|
28
|
+
|
29
|
+
it 'should be comparable' do
|
30
|
+
[subject, OSDb::Sub.new(@response.merge('SubRating' => '2.45'))].max.should == subject
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: bartzon-osdb
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 19
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 0
|
9
|
+
- 6
|
10
|
+
version: 0.0.6
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Jean Boussier, Bart Zonneveld
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-12-09 00:00:00 +01:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Ruby library to access OSDb services like OpenSubtitles.org
|
23
|
+
email: jean.boussier @nospam@ gmail.com, loop@superinfinite.com
|
24
|
+
executables:
|
25
|
+
- getsub
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- README.md
|
30
|
+
- bin/getsub
|
31
|
+
- lib/osdb.rb
|
32
|
+
- lib/osdb/language.rb
|
33
|
+
- lib/osdb/movie.rb
|
34
|
+
- lib/osdb/options.rb
|
35
|
+
- lib/osdb/server.rb
|
36
|
+
- lib/osdb/sub.rb
|
37
|
+
files:
|
38
|
+
- Manifest
|
39
|
+
- README.md
|
40
|
+
- Rakefile
|
41
|
+
- bin/getsub
|
42
|
+
- lib/osdb.rb
|
43
|
+
- lib/osdb/language.rb
|
44
|
+
- lib/osdb/movie.rb
|
45
|
+
- lib/osdb/options.rb
|
46
|
+
- lib/osdb/server.rb
|
47
|
+
- lib/osdb/sub.rb
|
48
|
+
- osdb.gemspec
|
49
|
+
- spec/fixtures/somemovie.avi
|
50
|
+
- spec/osdb/language_spec.rb
|
51
|
+
- spec/osdb/movie_spec.rb
|
52
|
+
- spec/osdb/server_spec.rb
|
53
|
+
- spec/osdb/sub_spec.rb
|
54
|
+
- spec/spec_helper.rb
|
55
|
+
- bartzon-osdb.gemspec
|
56
|
+
has_rdoc: true
|
57
|
+
homepage: http://github.com/bartzon/ruby-osdb
|
58
|
+
licenses: []
|
59
|
+
|
60
|
+
post_install_message:
|
61
|
+
rdoc_options:
|
62
|
+
- --line-numbers
|
63
|
+
- --inline-source
|
64
|
+
- --title
|
65
|
+
- Bartzon-osdb
|
66
|
+
- --main
|
67
|
+
- README.md
|
68
|
+
require_paths:
|
69
|
+
- lib
|
70
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
71
|
+
none: false
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
hash: 3
|
76
|
+
segments:
|
77
|
+
- 0
|
78
|
+
version: "0"
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
none: false
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
hash: 11
|
85
|
+
segments:
|
86
|
+
- 1
|
87
|
+
- 2
|
88
|
+
version: "1.2"
|
89
|
+
requirements: []
|
90
|
+
|
91
|
+
rubyforge_project: bartzon-osdb
|
92
|
+
rubygems_version: 1.3.7
|
93
|
+
signing_key:
|
94
|
+
specification_version: 3
|
95
|
+
summary: Ruby library to access OSDb services like OpenSubtitles.org
|
96
|
+
test_files: []
|
97
|
+
|