osdb 0.0.10 → 0.1.0

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