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