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.
@@ -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
@@ -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,7 @@
1
+ module OSDb
2
+ module Selector
3
+ base_path = File.expand_path(File.dirname(__FILE__) + '/selector')
4
+ autoload :Format, "#{base_path}/format"
5
+ autoload :Movie, "#{base_path}/movie"
6
+ end
7
+ end
@@ -0,0 +1,17 @@
1
+ module OSDb
2
+ module Selector
3
+
4
+ class Format
5
+
6
+ def initialize(formats)
7
+ @formats = formats
8
+ end
9
+
10
+ def select(subs, movie)
11
+ subs.select{ |s| @formats.include?(s.format) }
12
+ end
13
+
14
+ end
15
+
16
+ end
17
+ 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
@@ -53,5 +53,9 @@ module OSDb
53
53
  client.call('ServerInfo')
54
54
  end
55
55
 
56
+ def get_imdb_movie_details(id)
57
+ Movie.new(client.call('GetIMDBMovieDetails', token, id)['data'])
58
+ end
59
+
56
60
  end
57
61
  end
@@ -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
@@ -1,3 +1,3 @@
1
1
  module OSDb
2
- VERSION = '0.0.10'
2
+ VERSION = '0.1.0'
3
3
  end
data/osdb.gemspec CHANGED
@@ -19,4 +19,6 @@ Gem::Specification.new do |s|
19
19
 
20
20
  s.add_development_dependency 'rake'
21
21
  s.add_development_dependency 'rspec'
22
+ s.add_development_dependency 'webmock', '~> 1.8.11'
23
+ s.add_development_dependency 'vcr', '~> 2.3.0'
22
24
  end
@@ -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