osdb 0.0.10 → 0.1.0
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/bin/getsub +122 -160
- data/lib/osdb.rb +10 -5
- data/lib/osdb/finder.rb +8 -0
- data/lib/osdb/finder/first.rb +13 -0
- data/lib/osdb/finder/interactive.rb +21 -0
- data/lib/osdb/finder/score.rb +13 -0
- data/lib/osdb/movie.rb +8 -53
- data/lib/osdb/movie_file.rb +61 -0
- data/lib/osdb/search.rb +9 -0
- data/lib/osdb/search/imdb.rb +24 -0
- data/lib/osdb/search/movie_hash.rb +21 -0
- data/lib/osdb/search/name.rb +28 -0
- data/lib/osdb/search/path.rb +20 -0
- data/lib/osdb/selector.rb +7 -0
- data/lib/osdb/selector/format.rb +17 -0
- data/lib/osdb/selector/movie.rb +31 -0
- data/lib/osdb/server.rb +4 -0
- data/lib/osdb/subtitle_finder.rb +31 -0
- data/lib/osdb/version.rb +1 -1
- data/osdb.gemspec +2 -0
- data/spec/fixtures/http/check_movie_hash.yml +225 -0
- data/spec/fixtures/http/get_imdb_movie_details.yml +342 -0
- data/spec/fixtures/http/log_in.yml +86 -0
- data/spec/fixtures/http/log_out.yml +68 -0
- data/spec/fixtures/http/search_imdb.yml +761 -0
- data/spec/fixtures/http/search_subtitles_for_himym.yml +1189 -0
- data/spec/fixtures/http/search_subtitles_for_the_rock.yml +4124 -0
- data/spec/fixtures/http/server_info.yml +492 -0
- data/spec/osdb/{movie_spec.rb → movie_file_spec.rb} +2 -2
- data/spec/osdb/server_spec.rb +61 -36
- data/spec/spec_helper.rb +9 -1
- metadata +87 -9
@@ -0,0 +1,61 @@
|
|
1
|
+
module OSDb
|
2
|
+
class MovieFile
|
3
|
+
|
4
|
+
EXTENSIONS = %w(avi mpg m4v mkv mov ogv mp4)
|
5
|
+
|
6
|
+
attr_reader :path
|
7
|
+
|
8
|
+
def initialize(path)
|
9
|
+
@path = path
|
10
|
+
end
|
11
|
+
|
12
|
+
def has_sub?
|
13
|
+
exist = false
|
14
|
+
%w(.srt .sub).each{ |ext| exist ||= File.exist?(path.gsub(File.extname(path), ext)) }
|
15
|
+
exist
|
16
|
+
end
|
17
|
+
|
18
|
+
def sub_path(format)
|
19
|
+
path.gsub(File.extname(path), ".#{format}")
|
20
|
+
end
|
21
|
+
|
22
|
+
def hash
|
23
|
+
@hash ||= self.class.compute_hash(path)
|
24
|
+
end
|
25
|
+
|
26
|
+
def size
|
27
|
+
@size ||= File.size(path)
|
28
|
+
end
|
29
|
+
|
30
|
+
def name
|
31
|
+
@name ||= File.basename(path, File.extname(path))
|
32
|
+
end
|
33
|
+
|
34
|
+
CHUNK_SIZE = 64 * 1024 # in bytes
|
35
|
+
|
36
|
+
# from http://trac.opensubtitles.org/projects/opensubtitles/wiki/HashSourceCodes
|
37
|
+
def self.compute_hash(path)
|
38
|
+
filesize = File.size(path)
|
39
|
+
hash = filesize
|
40
|
+
|
41
|
+
# Read 64 kbytes, divide up into 64 bits and add each
|
42
|
+
# to hash. Do for beginning and end of file.
|
43
|
+
File.open(path, 'rb') do |f|
|
44
|
+
# Q = unsigned long long = 64 bit
|
45
|
+
f.read(CHUNK_SIZE).unpack("Q*").each do |n|
|
46
|
+
hash = hash + n & 0xffffffffffffffff # to remain as 64 bit number
|
47
|
+
end
|
48
|
+
|
49
|
+
f.seek([0, filesize - CHUNK_SIZE].max, IO::SEEK_SET)
|
50
|
+
|
51
|
+
# And again for the end of the file
|
52
|
+
f.read(CHUNK_SIZE).unpack("Q*").each do |n|
|
53
|
+
hash = hash + n & 0xffffffffffffffff
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
sprintf("%016x", hash)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
data/lib/osdb/search.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
module OSDb
|
2
|
+
module Search
|
3
|
+
base_path = File.expand_path(File.dirname(__FILE__) + '/search')
|
4
|
+
autoload :IMDB, "#{base_path}/imdb"
|
5
|
+
autoload :MovieHash, "#{base_path}/movie_hash"
|
6
|
+
autoload :Name, "#{base_path}/name"
|
7
|
+
autoload :Path, "#{base_path}/path"
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module OSDb
|
2
|
+
module Search
|
3
|
+
|
4
|
+
class IMDB
|
5
|
+
|
6
|
+
def initialize(server, selector=Finder::First.new)
|
7
|
+
@server = server
|
8
|
+
@selector = selector
|
9
|
+
end
|
10
|
+
|
11
|
+
def search_subs_for(movie, language)
|
12
|
+
imdb_results = @server.search_imdb(:query => movie.name)
|
13
|
+
if imdb_result = @selector.chose(imdb_results)
|
14
|
+
@server.search_subtitles(
|
15
|
+
:sublanguageid => language,
|
16
|
+
:imdbid => imdb_result.imdbid
|
17
|
+
)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module OSDb
|
2
|
+
module Search
|
3
|
+
|
4
|
+
class MovieHash
|
5
|
+
|
6
|
+
def initialize(server)
|
7
|
+
@server = server
|
8
|
+
end
|
9
|
+
|
10
|
+
def search_subs_for(movie, language)
|
11
|
+
@server.search_subtitles(
|
12
|
+
:moviehash => movie.hash,
|
13
|
+
:moviebytesize => movie.size.to_s,
|
14
|
+
:sublanguageid => language
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module OSDb
|
2
|
+
module Search
|
3
|
+
|
4
|
+
class Name
|
5
|
+
|
6
|
+
def initialize(server)
|
7
|
+
@server = server
|
8
|
+
end
|
9
|
+
|
10
|
+
def search_subs_for(movie, language)
|
11
|
+
subs = @server.search_subtitles(:sublanguageid => language, :query => movie.name)
|
12
|
+
normalized_movie_name = normalize_name(movie.name)
|
13
|
+
subs.select! do |sub|
|
14
|
+
normalize_name(sub.filename).index(normalized_movie_name) # MAYBE: Levenshtein ?
|
15
|
+
end
|
16
|
+
subs
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
def normalize_name(name)
|
22
|
+
name.downcase.gsub(/[\s\.\-\_]+/, ' ')
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module OSDb
|
2
|
+
module Search
|
3
|
+
|
4
|
+
class Path
|
5
|
+
|
6
|
+
def initialize(server)
|
7
|
+
@server = server
|
8
|
+
end
|
9
|
+
|
10
|
+
def search_subs_for(movie, language)
|
11
|
+
@server.search_subtitles(
|
12
|
+
:sublanguageid => language,
|
13
|
+
:tag => movie.path
|
14
|
+
)
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module OSDb
|
2
|
+
module Selector
|
3
|
+
|
4
|
+
class Movie
|
5
|
+
|
6
|
+
def initialize(movie_finder=Finder::First.new)
|
7
|
+
@movie_finder = movie_finder
|
8
|
+
end
|
9
|
+
|
10
|
+
def select(subs, movie)
|
11
|
+
subs_by_movie = group_by_movie_name(subs)
|
12
|
+
|
13
|
+
return subs if subs_by_movie.length <= 1
|
14
|
+
|
15
|
+
movie_names = subs_by_movie.keys
|
16
|
+
movie_name = @movie_finder.chose(movie_names)
|
17
|
+
subs_by_movie[movie_name] || []
|
18
|
+
end
|
19
|
+
|
20
|
+
def group_by_movie_name(subs)
|
21
|
+
subs.inject({}) do |hash, sub|
|
22
|
+
hash[sub.movie_name] ||= []
|
23
|
+
hash[sub.movie_name] << sub
|
24
|
+
hash
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
data/lib/osdb/server.rb
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
module OSDb
|
2
|
+
|
3
|
+
class SubtitleFinder
|
4
|
+
|
5
|
+
def initialize(search_engines, finders, selectors=[])
|
6
|
+
@search_engines = search_engines
|
7
|
+
@finders = finders
|
8
|
+
@selectors = selectors
|
9
|
+
end
|
10
|
+
|
11
|
+
def find_sub_for(movie, language)
|
12
|
+
@search_engines.each do |engine|
|
13
|
+
subs = engine.search_subs_for(movie, language)
|
14
|
+
|
15
|
+
subs = @selectors.inject(subs) do |subs, selector|
|
16
|
+
selector.select(subs, movie)
|
17
|
+
end
|
18
|
+
|
19
|
+
@finders.each do |finder|
|
20
|
+
sub = finder.chose(subs)
|
21
|
+
return sub if sub
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
data/lib/osdb/version.rb
CHANGED
data/osdb.gemspec
CHANGED
@@ -0,0 +1,225 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: post
|
5
|
+
uri: http://api.opensubtitles.org/xml-rpc
|
6
|
+
body:
|
7
|
+
string: |
|
8
|
+
<?xml version="1.0" ?><methodCall><methodName>LogIn</methodName><params><param><value><string></string></value></param><param><value><string></string></value></param><param><value><string>eng</string></value></param><param><value><string>OS Test User Agent</string></value></param></params></methodCall>
|
9
|
+
|
10
|
+
headers:
|
11
|
+
Accept:
|
12
|
+
- "*/*"
|
13
|
+
Cookie:
|
14
|
+
- PHPSESSID=qtqkphpa0us78eikqtdtg4jic7
|
15
|
+
Content-Length:
|
16
|
+
- "304"
|
17
|
+
User-Agent:
|
18
|
+
- XMLRPC::Client (Ruby 1.8.7)
|
19
|
+
Content-Type:
|
20
|
+
- text/xml; charset=utf-8
|
21
|
+
Connection:
|
22
|
+
- keep-alive
|
23
|
+
response:
|
24
|
+
status:
|
25
|
+
code: 200
|
26
|
+
message: OK
|
27
|
+
headers:
|
28
|
+
X-Cache:
|
29
|
+
- MISS
|
30
|
+
Age:
|
31
|
+
- "0"
|
32
|
+
X-Cache-Backend:
|
33
|
+
- www
|
34
|
+
Cache-Control:
|
35
|
+
- no-store, no-cache, must-revalidate, post-check=0, pre-check=0
|
36
|
+
Pragma:
|
37
|
+
- no-cache
|
38
|
+
Expires:
|
39
|
+
- Thu, 19 Nov 1981 08:52:00 GMT
|
40
|
+
Content-Length:
|
41
|
+
- "504"
|
42
|
+
Content-Type:
|
43
|
+
- text/xml
|
44
|
+
Connection:
|
45
|
+
- keep-alive
|
46
|
+
Accept-Ranges:
|
47
|
+
- bytes
|
48
|
+
Date:
|
49
|
+
- Sun, 04 Nov 2012 17:11:36 GMT
|
50
|
+
body:
|
51
|
+
string: |
|
52
|
+
<?xml version="1.0" encoding="utf-8"?>
|
53
|
+
<methodResponse>
|
54
|
+
<params>
|
55
|
+
<param>
|
56
|
+
<value>
|
57
|
+
<struct>
|
58
|
+
<member>
|
59
|
+
<name>token</name>
|
60
|
+
<value>
|
61
|
+
<string>qtqkphpa0us78eikqtdtg4jic7</string>
|
62
|
+
</value>
|
63
|
+
</member>
|
64
|
+
<member>
|
65
|
+
<name>status</name>
|
66
|
+
<value>
|
67
|
+
<string>200 OK</string>
|
68
|
+
</value>
|
69
|
+
</member>
|
70
|
+
<member>
|
71
|
+
<name>seconds</name>
|
72
|
+
<value>
|
73
|
+
<double>0.009</double>
|
74
|
+
</value>
|
75
|
+
</member>
|
76
|
+
</struct>
|
77
|
+
</value>
|
78
|
+
</param>
|
79
|
+
</params>
|
80
|
+
</methodResponse>
|
81
|
+
|
82
|
+
http_version:
|
83
|
+
recorded_at: Sun, 04 Nov 2012 17:11:36 GMT
|
84
|
+
- request:
|
85
|
+
method: post
|
86
|
+
uri: http://api.opensubtitles.org/xml-rpc
|
87
|
+
body:
|
88
|
+
string: |
|
89
|
+
<?xml version="1.0" ?><methodCall><methodName>CheckMovieHash</methodName><params><param><value><string>qtqkphpa0us78eikqtdtg4jic7</string></value></param><param><value><array><data><value><string>37d0c7d0cfcbe280</string></value></data></array></value></param></params></methodCall>
|
90
|
+
|
91
|
+
headers:
|
92
|
+
Accept:
|
93
|
+
- "*/*"
|
94
|
+
Cookie:
|
95
|
+
- PHPSESSID=qtqkphpa0us78eikqtdtg4jic7
|
96
|
+
Content-Length:
|
97
|
+
- "283"
|
98
|
+
User-Agent:
|
99
|
+
- XMLRPC::Client (Ruby 1.8.7)
|
100
|
+
Content-Type:
|
101
|
+
- text/xml; charset=utf-8
|
102
|
+
Connection:
|
103
|
+
- keep-alive
|
104
|
+
response:
|
105
|
+
status:
|
106
|
+
code: 200
|
107
|
+
message: OK
|
108
|
+
headers:
|
109
|
+
X-Cache:
|
110
|
+
- MISS
|
111
|
+
Age:
|
112
|
+
- "0"
|
113
|
+
X-Cache-Backend:
|
114
|
+
- www
|
115
|
+
Cache-Control:
|
116
|
+
- no-store, no-cache, must-revalidate, post-check=0, pre-check=0
|
117
|
+
Set-Cookie:
|
118
|
+
- PHPSESSID=qtqkphpa0us78eikqtdtg4jic7; path=/
|
119
|
+
Pragma:
|
120
|
+
- no-cache
|
121
|
+
Expires:
|
122
|
+
- Thu, 19 Nov 1981 08:52:00 GMT
|
123
|
+
Content-Length:
|
124
|
+
- "1800"
|
125
|
+
Content-Type:
|
126
|
+
- text/xml
|
127
|
+
Connection:
|
128
|
+
- keep-alive
|
129
|
+
Accept-Ranges:
|
130
|
+
- bytes
|
131
|
+
Date:
|
132
|
+
- Sun, 04 Nov 2012 17:11:36 GMT
|
133
|
+
body:
|
134
|
+
string: |
|
135
|
+
<?xml version="1.0" encoding="utf-8"?>
|
136
|
+
<methodResponse>
|
137
|
+
<params>
|
138
|
+
<param>
|
139
|
+
<value>
|
140
|
+
<struct>
|
141
|
+
<member>
|
142
|
+
<name>status</name>
|
143
|
+
<value>
|
144
|
+
<string>200 OK</string>
|
145
|
+
</value>
|
146
|
+
</member>
|
147
|
+
<member>
|
148
|
+
<name>data</name>
|
149
|
+
<value>
|
150
|
+
<struct>
|
151
|
+
<member>
|
152
|
+
<name>37d0c7d0cfcbe280</name>
|
153
|
+
<value>
|
154
|
+
<struct>
|
155
|
+
<member>
|
156
|
+
<name>MovieHash</name>
|
157
|
+
<value>
|
158
|
+
<string>37d0c7d0cfcbe280</string>
|
159
|
+
</value>
|
160
|
+
</member>
|
161
|
+
<member>
|
162
|
+
<name>MovieImdbID</name>
|
163
|
+
<value>
|
164
|
+
<string>0117500</string>
|
165
|
+
</value>
|
166
|
+
</member>
|
167
|
+
<member>
|
168
|
+
<name>MovieName</name>
|
169
|
+
<value>
|
170
|
+
<string>The Rock</string>
|
171
|
+
</value>
|
172
|
+
</member>
|
173
|
+
<member>
|
174
|
+
<name>MovieYear</name>
|
175
|
+
<value>
|
176
|
+
<string>1996</string>
|
177
|
+
</value>
|
178
|
+
</member>
|
179
|
+
<member>
|
180
|
+
<name>MovieKind</name>
|
181
|
+
<value>
|
182
|
+
<string>movie</string>
|
183
|
+
</value>
|
184
|
+
</member>
|
185
|
+
<member>
|
186
|
+
<name>SeriesSeason</name>
|
187
|
+
<value>
|
188
|
+
<string>0</string>
|
189
|
+
</value>
|
190
|
+
</member>
|
191
|
+
<member>
|
192
|
+
<name>SeriesEpisode</name>
|
193
|
+
<value>
|
194
|
+
<string>0</string>
|
195
|
+
</value>
|
196
|
+
</member>
|
197
|
+
</struct>
|
198
|
+
</value>
|
199
|
+
</member>
|
200
|
+
</struct>
|
201
|
+
</value>
|
202
|
+
</member>
|
203
|
+
<member>
|
204
|
+
<name>not_processed</name>
|
205
|
+
<value>
|
206
|
+
<array>
|
207
|
+
<data/>
|
208
|
+
</array>
|
209
|
+
</value>
|
210
|
+
</member>
|
211
|
+
<member>
|
212
|
+
<name>seconds</name>
|
213
|
+
<value>
|
214
|
+
<double>0.016</double>
|
215
|
+
</value>
|
216
|
+
</member>
|
217
|
+
</struct>
|
218
|
+
</value>
|
219
|
+
</param>
|
220
|
+
</params>
|
221
|
+
</methodResponse>
|
222
|
+
|
223
|
+
http_version:
|
224
|
+
recorded_at: Sun, 04 Nov 2012 17:11:36 GMT
|
225
|
+
recorded_with: VCR 2.3.0
|