shin 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f2ef70e3ad1839ae50cc03c83431bd39b8cfa8a1
4
+ data.tar.gz: d524891b2e7af3b26a0d615b87066e0b0e4d715b
5
+ SHA512:
6
+ metadata.gz: 644597661ad4e7af9ced7c8e1cad51517ad853add7b3be3e98a273384c20006cf1c55f404d7434a7eaf211bd022396abc7aa6eabebcd4b29ed59269aaf88f8fd
7
+ data.tar.gz: 5e3b98bb07b9c513c350db0451633ac6ea5f30e4102247ca6f5b9d9672bbc520bbcf20a1dd482ca8ca5ffa434b0dee6f3107aeccec7f289936a2a80b0837bc7f
data/README.textile ADDED
@@ -0,0 +1,23 @@
1
+ h1. Shin Hye
2
+
3
+ h2. What?
4
+
5
+ Shin Hye is a Ruby library to talk to several different websites and data providers. The objective of the library is mostly just for the use of EPG.io.
6
+
7
+ h2. Why?
8
+
9
+ We use the data from several providers in EPG.io and we needed a library to provide this rather than using a file in lib/.
10
+
11
+ h2. How?
12
+
13
+ Set up the client
14
+ <code>shin = Shin.new</code>
15
+
16
+ Sometimes we need to provide an API for different cases such as review sites.
17
+ To do this use:
18
+ <code>shin = Shin.new moviezine: "a3ba5fd96ab238d8788c53683e7b72aa"</code>
19
+
20
+ Then you can do:
21
+ <code>shin.reviews.moviezine.find imdb: 'tt4201628'</code>
22
+
23
+ And it will return the review for "Pinocchio (2014 TV Series)":http://www.imdb.com/title/tt4201628/:
data/lib/shin/base.rb ADDED
@@ -0,0 +1,16 @@
1
+ module Shin
2
+ class Base
3
+ include HTTParty
4
+ include HTTParty::Icebox
5
+
6
+ def new
7
+ self
8
+ end
9
+
10
+ def get(url)
11
+ response = self.class.get(url).parsed_response
12
+ return [] unless response
13
+ response
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,259 @@
1
+ # = Icebox : Caching for HTTParty
2
+ #
3
+ # Cache responses in HTTParty models [http://github.com/jnunemaker/httparty]
4
+ #
5
+ # === Usage
6
+ #
7
+ # class Foo
8
+ # include HTTParty
9
+ # include HTTParty::Icebox
10
+ # cache :store => 'file', :timeout => 600, :location => MY_APP_ROOT.join('tmp', 'cache')
11
+ # end
12
+ #
13
+ # Modeled after Martyn Loughran's APICache [http://github.com/newbamboo/api_cache]
14
+ # and Ruby On Rails's caching [http://api.rubyonrails.org/classes/ActiveSupport/Cache.html]
15
+ #
16
+ # Author: Karel Minarik [www.karmi.cz]
17
+ #
18
+ # === Notes
19
+ #
20
+ # Thanks to Amit Chakradeo to point out objects have to be stored marhalled on FS
21
+ # Thanks to Marlin Forbes to point out query parameters have to be include in the cache key
22
+ #
23
+ #
24
+
25
+ require 'logger'
26
+ require 'fileutils'
27
+ require 'tmpdir'
28
+ require 'pathname'
29
+ require 'digest/md5'
30
+
31
+ module HTTParty #:nodoc:
32
+ module Icebox
33
+
34
+ module ClassMethods
35
+
36
+ # Enable caching and set cache options
37
+ # Returns memoized cache object
38
+ #
39
+ # Following options are available, default values are in []:
40
+ #
41
+ # +store+:: Storage mechanism for cached data (memory, filesystem, your own) [memory]
42
+ # +timeout+:: Cache expiration in seconds [60]
43
+ # +logger+:: Path to logfile or logger instance [STDOUT]
44
+ #
45
+ # Any additional options are passed to the Cache constructor
46
+ #
47
+ # Usage:
48
+ #
49
+ # # Enable caching in HTTParty, in memory, for 1 minute
50
+ # cache # Use default values
51
+ #
52
+ # # Enable caching in HTTParty, on filesystem (/tmp), for 10 minutes
53
+ # cache :store => 'file', :timeout => 600, :location => '/tmp/'
54
+ #
55
+ # # Use your own cache store (see AbstractStore class below)
56
+ # cache :store => 'memcached', :timeout => 600, :server => '192.168.1.1:1001'
57
+ #
58
+ def cache(options={})
59
+ options[:store] ||= 'memory'
60
+ options[:timeout] ||= 60
61
+ logger = options[:logger]
62
+ @cache ||= Cache.new( options.delete(:store), options )
63
+ end
64
+
65
+ end
66
+
67
+ # When included, extend class with +cache+ method
68
+ # and redefine +get+ method to use cache
69
+ #
70
+ def self.included(receiver) #:nodoc:
71
+ receiver.extend ClassMethods
72
+ receiver.class_eval do
73
+
74
+ # Get reponse from network
75
+ # TODO: Why alias :new :old is not working here? Returns NoMethodError
76
+ #
77
+ def self.get_without_caching(path, options={})
78
+ perform_request Net::HTTP::Get, path, options
79
+ end
80
+
81
+ # Get response from cache, if available
82
+ #
83
+ def self.get_with_caching(path, options={})
84
+ key = path.clone
85
+ key << options[:query].to_s if defined? options[:query]
86
+
87
+ if cache.exists?(key) and not cache.stale?(key)
88
+ Cache.logger.debug "CACHE -- GET #{path}#{options[:query]}"
89
+ return cache.get(key)
90
+ else
91
+ Cache.logger.debug "/!\\ NETWORK -- GET #{path}#{options[:query]}"
92
+ response = get_without_caching(path, options)
93
+ cache.set(key, response) if response.code == 200
94
+ return response
95
+ end
96
+ end
97
+
98
+ # Redefine original HTTParty +get+ method to use cache
99
+ #
100
+ def self.get(path, options={})
101
+ self.get_with_caching(path, options)
102
+ end
103
+
104
+ end
105
+ end
106
+
107
+ # === Cache container
108
+ #
109
+ # Pass a store name ('memory', etc) to initializer
110
+ #
111
+ class Cache
112
+ attr_accessor :store
113
+
114
+ def initialize(store, options={})
115
+ self.class.logger = options[:logger]
116
+ @store = self.class.lookup_store(store).new(options)
117
+ end
118
+
119
+ def get(key); @store.get encode(key) unless stale?(key); end
120
+ def set(key, value); @store.set encode(key), value; end
121
+ def exists?(key); @store.exists? encode(key); end
122
+ def stale?(key); @store.stale? encode(key); end
123
+
124
+ def self.logger; @logger || default_logger; end
125
+ def self.default_logger; logger = ::Logger.new(STDERR); end
126
+
127
+ # Pass a filename (String), IO object, Logger instance or +nil+ to silence the logger
128
+ def self.logger=(device); @logger = device.kind_of?(::Logger) ? device : ::Logger.new(device); end
129
+
130
+ private
131
+
132
+ # Return store class based on passed name
133
+ def self.lookup_store(name)
134
+ store_name = "#{name.capitalize}Store"
135
+ return Store::const_get(store_name)
136
+ rescue NameError => e
137
+ raise Store::StoreNotFound, "The cache store '#{store_name}' was not found. Did you loaded any such class?"
138
+ end
139
+
140
+ def encode(key); Digest::MD5.hexdigest(key); end
141
+ end
142
+
143
+
144
+ # === Cache stores
145
+ #
146
+ module Store
147
+
148
+ class StoreNotFound < StandardError; end #:nodoc:
149
+
150
+ # ==== Abstract Store
151
+ # Inherit your store from this class
152
+ # *IMPORTANT*: Do not forget to call +super+ in your +initialize+ method!
153
+ #
154
+ class AbstractStore
155
+ def initialize(options={})
156
+ raise ArgumentError, "You need to set the :timeout parameter" unless options[:timeout]
157
+ @timeout = options[:timeout]
158
+ message = "Cache: Using #{self.class.to_s.split('::').last}"
159
+ message << " in location: #{options[:location]}" if options[:location]
160
+ message << " with timeout #{options[:timeout]} sec"
161
+ Cache.logger.info message unless options[:logger].nil?
162
+ return self
163
+ end
164
+ %w{set get exists? stale?}.each do |method_name|
165
+ define_method(method_name) { raise NoMethodError, "Please implement method set in your store class" }
166
+ end
167
+ end
168
+
169
+ # ===== Store objects in memory
170
+ #
171
+ Struct.new("TvdbResponse", :code, :body, :headers) { def to_s; self.body; end }
172
+ class MemoryStore < AbstractStore
173
+ def initialize(options={})
174
+ super; @store = {}; self
175
+ end
176
+ def set(key, value)
177
+ Cache.logger.info("Cache: set (#{key})")
178
+ @store[key] = [Time.now, value]; true
179
+ end
180
+ def get(key)
181
+ data = @store[key][1]
182
+ Cache.logger.info("Cache: #{data.nil? ? "miss" : "hit"} (#{key})")
183
+ data
184
+ end
185
+ def exists?(key)
186
+ !@store[key].nil?
187
+ end
188
+ def stale?(key)
189
+ return true unless exists?(key)
190
+ Time.now - created(key) > @timeout
191
+ end
192
+ private
193
+ def created(key)
194
+ @store[key][0]
195
+ end
196
+ end
197
+
198
+ # ===== Store objects on the filesystem
199
+ #
200
+ class FileStore < AbstractStore
201
+ def initialize(options={})
202
+ super
203
+ options[:location] ||= Dir::tmpdir
204
+ @path = Pathname.new( options[:location] )
205
+ FileUtils.mkdir_p( @path )
206
+ self
207
+ end
208
+ def set(key, value)
209
+ Cache.logger.info("Cache: set (#{key})")
210
+ File.open( @path.join(key), 'w' ) { |file| Marshal.dump(value, file) }
211
+ true
212
+ end
213
+ def get(key)
214
+ data = Marshal.load(File.new(@path.join(key)))
215
+ Cache.logger.info("Cache: #{data.nil? ? "miss" : "hit"} (#{key})")
216
+ data
217
+ end
218
+ def exists?(key)
219
+ File.exists?( @path.join(key) )
220
+ end
221
+ def stale?(key)
222
+ return true unless exists?(key)
223
+ Time.now - created(key) > @timeout
224
+ end
225
+ private
226
+ def created(key)
227
+ File.mtime( @path.join(key) )
228
+ end
229
+ end
230
+ end
231
+
232
+ end
233
+ end
234
+
235
+
236
+ # Major parts of this code are based on architecture of ApiCache.
237
+ # Copyright (c) 2008 Martyn Loughran
238
+ #
239
+ # Other parts are inspired by the ActiveSupport::Cache in Ruby On Rails.
240
+ # Copyright (c) 2005-2009 David Heinemeier Hansson
241
+ #
242
+ # Permission is hereby granted, free of charge, to any person obtaining
243
+ # a copy of this software and associated documentation files (the
244
+ # "Software"), to deal in the Software without restriction, including
245
+ # without limitation the rights to use, copy, modify, merge, publish,
246
+ # distribute, sublicense, and/or sell copies of the Software, and to
247
+ # permit persons to whom the Software is furnished to do so, subject to
248
+ # the following conditions:
249
+ #
250
+ # The above copyright notice and this permission notice shall be
251
+ # included in all copies or substantial portions of the Software.
252
+ #
253
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
254
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
255
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
256
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
257
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
258
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
259
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,152 @@
1
+ ## SVT Play doesn't have an open API so let's parse their HTML
2
+ require 'date'
3
+ require 'time'
4
+
5
+ module Shin
6
+ module Play
7
+ class Svtplay
8
+
9
+ def new
10
+ self
11
+ end
12
+
13
+ # Get episodes for a slug
14
+ def episodes(params={})
15
+ # Response
16
+ response = Base.get('http://www.svtplay.se/' + params[:slug].to_s + '/hela-program?' + URI.encode_www_form(params))
17
+ raise HTTPError, "The response didn't have a 200 HTTP Code. It had #{response.code}." unless response.code == 200
18
+
19
+ # Nokogiri parse
20
+ @main_noko = Nokogiri::HTML response.body rescue nil
21
+
22
+ # Can't be nil
23
+ if @main_noko != nil
24
+ # Title
25
+ @title = @main_noko.css('div.play_gridpage__header-wrapper > h1 > a.play_link.play_link--discreet').text.strip rescue nil
26
+ @next_page = @main_noko.css('div.play_gridpage__pagination').css('a')[0]['href'][/\?sida\=(\d+)/, 1].to_i rescue nil
27
+
28
+ # Data
29
+ @array = {next_page: @next_page, results: []}
30
+
31
+ # Multiple episodes
32
+ @main_noko.css('div#gridpage-content > article').map do |e|
33
+ @video_id = e.css('a')[0]['href'][/\/video\/(\d+)\//, 1].to_i rescue nil
34
+ @url = "http://www.svtplay.se" + e.css('a')[0]['href'] rescue nil
35
+ @desc = e.css('a')[0]['title'] rescue nil
36
+ @season = e['data-season'].to_i rescue nil
37
+ @episode = e['data-title'][/Avsnitt\s+(\d+)/, 1].to_i rescue nil
38
+ @image = e.css('a img')[0]['src'].gsub("ALTERNATES/small", "ALTERNATES/extralarge") rescue nil
39
+
40
+ # Parse published_to
41
+ if pto = e['data-available']
42
+ # Days
43
+ if dayz = pto[/(\d+)\s+dag/, 1].to_i
44
+ @published_to = Time.parse("23:59", Date.today + dayz) rescue nil
45
+ else
46
+ @published_to = nil
47
+ end
48
+ end
49
+
50
+
51
+ # subtitle
52
+ if @episode > 0
53
+ @subtitle = e['data-title'].gsub(@title + " - ", '').gsub("Avsnitt " + @episode.to_s + ':', '').gsub("Avsnitt " + @episode.to_s, '').strip
54
+ else
55
+ @subtitle = e['data-title'].gsub(@title + " - ", '').strip
56
+ end
57
+
58
+ @array[:results] << {id: @video_id, image: @image, season: @season, episode: @episode, subtitle: @subtitle, url: @url, description: @desc, published_to: @published_to}
59
+ end
60
+ else
61
+ raise NotValid, "Nokogiri failed to parse the HTML."
62
+ end
63
+
64
+ @array.to_hashugar
65
+ end
66
+
67
+ # Programs
68
+ def programs
69
+ # Response
70
+ response = Base.get('http://www.svtplay.se/program')
71
+ raise HTTPError, "The response didn't have a 200 HTTP Code. It had #{response.code}." unless response.code == 200
72
+
73
+ # Nokogiri parse
74
+ @main_noko = Nokogiri::HTML response.body rescue nil
75
+
76
+ # Foreach programs
77
+ @array = []
78
+
79
+ # Cant be nil
80
+ if @main_noko != nil
81
+ @main_noko.css('ul.play_alphabetic-list > li > ul > li').map do |p|
82
+ sluge = p.css('a')[0]['href'].strip.gsub("/", '')
83
+ titlee = p.css('a').text
84
+ @array << {slug: sluge, title: titlee}
85
+ end
86
+
87
+ else
88
+ raise NotValid, "Nokogiri failed to parse the HTML."
89
+ end
90
+
91
+ @array.to_hashugar
92
+ end
93
+
94
+ # Video
95
+ def video(params={})
96
+ # Response
97
+ response = Base.get('http://www.svtplay.se/video/' + params[:id].to_s)
98
+ raise HTTPError, "The response didn't have a 200 HTTP Code. It had #{response.code}." unless response.code == 200
99
+
100
+ # Nokogiri parse
101
+ @main_noko = Nokogiri::HTML response.body rescue nil
102
+
103
+ # Cant be nil
104
+ if @main_noko != nil
105
+ # Title
106
+ @title = @main_noko.css("h1.play_video-area-aside__title")[0].text.strip
107
+
108
+ # Subtitle data
109
+ submeta = @main_noko.css("h2.play_video-area-aside__sub-title")[0].text.strip.gsub("\n", ' ').squeeze(' ') rescue nil
110
+ if !submeta.nil?
111
+ @season = submeta[/S.song\s+(\d+)/, 1].to_i rescue nil
112
+ @episode = submeta[/Avsnitt\s+(\d+)/, 1].to_i rescue nil
113
+ end
114
+
115
+ # Desc
116
+ @desc = @main_noko.css('p.play_video-area-aside__info-text')[0].text.strip rescue nil
117
+
118
+ # Player data
119
+ playerdata = @main_noko.css("a.play_js-svtplayer")[0]
120
+ @published_on = Time.at(playerdata['data-popularity-publish-date'].to_i/1000) rescue nil
121
+ if playerdata['data-expires-timestamp'] != nil
122
+ @published_to = Time.at(playerdata['data-expires-timestamp'].to_i/1000) rescue nil
123
+ else
124
+ @published_to = nil
125
+ end
126
+ @url = playerdata['data-popularity-url'] rescue nil
127
+ @length = (playerdata['data-length'].to_i)/60 rescue nil
128
+ @image = @main_noko.css('meta[property="og:image"]')[0]['content'].gsub("ALTERNATES/medium", "ALTERNATES/extralarge") rescue nil
129
+
130
+ # Add subtitle from playerdata
131
+ if submeta != nil and @episode > 0
132
+ @subtitle = playerdata['data-title'].gsub(@title + " - ", '').gsub("Avsnitt " + @episode.to_s + ':', '').gsub("Avsnitt " + @episode.to_s, '').strip
133
+
134
+ @subtitle = nil if @subtitle == ""
135
+ else
136
+ @subtitle = playerdata['data-title'].gsub(@title + " - ", '').strip
137
+ @subtitle = nil if @subtitle == ""
138
+ end
139
+
140
+ { id: params[:id].to_i, title: @title, image: @image, season: @season, episode: @episode, subtitle: @subtitle, length: @length, published_on: @published_on, published_to: @published_to, url: @url, description: @desc }.to_hashugar
141
+ else
142
+ raise NotValid, "Nokogiri failed to parse the HTML."
143
+ end
144
+ end
145
+
146
+
147
+ # Errors
148
+ class NotValid < StandardError; end
149
+ class HTTPError < StandardError; end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,104 @@
1
+ ## Viki provides an open API with embeds urls etc
2
+ ## - Required options is app (id)
3
+
4
+ require 'oj'
5
+
6
+ module Shin
7
+ module Play
8
+ class Viki
9
+
10
+ def new
11
+ self
12
+ end
13
+
14
+ # Fix these before running
15
+ def before(params={})
16
+ raise MissingArgument, "You are missing the argument 'viki_app_id' which is required to use this source." unless Shin.get[:viki_app_id] != nil
17
+
18
+ # Timestamp
19
+ params[:t] = Time.now.to_i
20
+
21
+ "?app=" + Shin.get[:viki_app_id] + "&" + URI.encode_www_form(params)
22
+ end
23
+
24
+ # All <category>, params can be page=num, per_page=num etc.
25
+ def all(category, params={})
26
+ query = before(params)
27
+ raise NotValid, "Not a valid category. Please check again." unless ["films", "series", "news", "artists"].include?(category)
28
+
29
+ # Response
30
+ response = Base.get('http://api.viki.io/v5/' + category + '.json' + query)
31
+ data = Oj.load(response.body) rescue nil
32
+ ret = {more: data['more'], response: []}
33
+
34
+ # Multiple
35
+ if data != nil
36
+ data['response'].each do |r|
37
+ ret[:results] << r
38
+ end
39
+ end
40
+
41
+
42
+ ret.to_hashugar
43
+ end
44
+
45
+ # Search (params can be everything on the docs)
46
+ def search(params={})
47
+ query = before(params)
48
+
49
+ # Response
50
+ response = Base.get('http://api.viki.io/v5/search.json' + query)
51
+ data = Oj.load(response.body) rescue nil
52
+ ret = {more: data['more'], response: []}
53
+
54
+ # Multiple
55
+ if !data.empty? and data != nil
56
+ data['response'].each do |r|
57
+ ret[:results] << r
58
+ end
59
+ end
60
+
61
+
62
+ ret.to_hashugar
63
+ end
64
+
65
+ # Info (can be series id, movie id, video etc)
66
+ def info(params={})
67
+ id = params[:id]
68
+ query = before(params)
69
+
70
+ # Response
71
+ response = Base.get('http://api.viki.io/v5/containers/' + id.to_s + '.json' + query)
72
+ data = Oj.load(response.body) rescue nil
73
+
74
+ data.to_hashugar
75
+ end
76
+
77
+ # Episodes
78
+ def episodes(params={})
79
+ id = params[:id]
80
+ query = before(params)
81
+
82
+ # Response
83
+ response = Base.get('http://api.viki.io/v5/containers/' + id.to_s + '/episodes.json' + query)
84
+ data = Oj.load(response.body) rescue nil
85
+ ret = {more: data['more'], response: []}
86
+
87
+ # Multiple
88
+ if !data.empty? and data != nil
89
+ data['response'].each do |r|
90
+ ret[:results] << r
91
+ end
92
+ end
93
+
94
+ ret.to_hashugar
95
+ end
96
+
97
+
98
+ # Errors
99
+ class NotValid < StandardError; end
100
+ class MissingArgument < StandardError; end
101
+ class HTTPError < StandardError; end
102
+ end
103
+ end
104
+ end
data/lib/shin/play.rb ADDED
@@ -0,0 +1,29 @@
1
+ require_relative 'play/svtplay'
2
+ require_relative 'play/viki'
3
+ #require_relative 'play/tv4play'
4
+ #require_relative 'play/urplay'
5
+ #require_relative 'play/viaplay'
6
+ #require_relative 'play/netflix'
7
+ #require_relative 'play/viasat'
8
+ #require_relative 'play/headweb'
9
+ #require_relative 'play/film2home'
10
+ #require_relative 'play/plejmo'
11
+
12
+ module Shin
13
+ module Play
14
+ class << self
15
+ # I don't know why I need this
16
+ def new
17
+ self
18
+ end
19
+
20
+ def svtplay
21
+ @svtplay ||= Svtplay.new
22
+ end
23
+
24
+ def viki
25
+ @viki ||= Viki.new
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,33 @@
1
+ ## Kritiker provides a private API for people they allow.
2
+ ## - Needed argument is IMDB ID
3
+ ## - Required options is kritiker_token
4
+
5
+ require 'oj'
6
+
7
+ module Shin
8
+ module Reviews
9
+ class Kritiker
10
+
11
+ def new
12
+ self
13
+ end
14
+
15
+ def find(h = {})
16
+ raise MissingArgument, "You are missing the argument 'imdb' or 'kritiker_token' which is required to use this source." unless h[:imdb] != "" and Shin.get[:kritiker_token] == ""
17
+
18
+ # We got the needed things
19
+ response = Base.get('http://api.kritiker.se/film/?imdb='+h[:imdb]+'&token='+Shin.get[:kritiker_token])
20
+
21
+ data = Oj.load(response.body) rescue nil
22
+ raise NotJSON, "Returned data isn't JSON. Couldn't parse it." if data == nil
23
+
24
+ year = data['datum'][/^(\d\d\d\d)/, 1].to_i unless data['datum'][/^(\d\d\d\d)/, 1].to_i == 0
25
+ {name: data['titel'], year: year, title: nil, rating: data['medelbetyg'].gsub(",", ".").to_f, url: data['kritiker'], votes: data['antalrecensioner'].to_i}.to_hashugar
26
+ end
27
+
28
+ class NotJSON < StandardError; end
29
+ class MissingArgument < StandardError; end
30
+ class HTTPError < StandardError; end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ ## This Call is using the Film2home site's API. So only use this if you got approved by MZ to use the data.
2
+ ## As they are very strict about it. I will move this to their API url when they get back to me sometime.
3
+ ## - Needed agruments are imdb_id (only int, but it auto removes "tt")
4
+
5
+ module Shin
6
+ module Reviews
7
+ class Moviezine
8
+
9
+ def new
10
+ self
11
+ end
12
+
13
+ # Currently IMDB ID is the only way
14
+ def find(h = {})
15
+ raise MissingArgument, "You are missing the argument 'imdb' which is required to use this source." unless h[:imdb] != ""
16
+
17
+ # We got the needed things
18
+ imdb_id_int = h[:imdb].gsub(/^tt/, "")
19
+ response = Base.get('https://www.film2home.se/Services/MovieZine.svc/GetReview?imdbId='+imdb_id_int.to_s)
20
+
21
+ # Raise error if it didn't have a correct http code.
22
+ raise HTTPError, "The response didn't have a 200 HTTP Code. It had #{response.code}." unless response.code == 200
23
+
24
+ data = response.parsed_response
25
+ {name: nil, year: nil, title: data['Title'], rating: data['Rating'].to_i, url: data['Url'], votes: nil}.to_hashugar
26
+ end
27
+
28
+ class MissingArgument < StandardError; end
29
+ class HTTPError < StandardError; end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,46 @@
1
+ ## Russin.nu provides this API for non-commercial usage and only if you link it back.
2
+ ## - Needed argument is title and optional is year.
3
+
4
+ module Shin
5
+ module Reviews
6
+ class Russin
7
+
8
+ def new
9
+ self
10
+ end
11
+
12
+ def find(h = {})
13
+ # Search can either have title and year or only title
14
+ if h[:year] != ""
15
+ response = Base.get('http://www.russin.nu/api.php?soktitel='+URI.encode(h[:title])+"&year="+h[:year].to_s)
16
+ elsif h[:title] != ""
17
+ response = Base.get('http://www.russin.nu/api.php?soktitel='+URI.encode(h[:title]))
18
+ else
19
+ raise MissingArgument, "You are missing the argument 'title' which is required to use this source."
20
+ end
21
+
22
+ # Raise error if it didn't have a correct http code.
23
+ raise HTTPError, "The response didn't have a 200 HTTP Code. It had #{response.code}." unless response.code == 200
24
+
25
+ # Data, it can be multiple reviews for a single movie from different reviewers
26
+ doc = Nokogiri::XML(response.body)
27
+
28
+ doc.remove_namespaces!
29
+ data = []
30
+ doc.xpath("//data/russinrecension").each do |review|
31
+ movie_title, movie_year = review.xpath('./filmtitel').text.split(", ")
32
+ title = review.xpath('./rubrik').text
33
+ rating = review.xpath('./betyg').text
34
+ url = review.xpath('./url').text
35
+
36
+ data << {name: movie_title, year: movie_year.to_i, title: title, rating: rating.to_i, url: url, votes: nil}
37
+ end
38
+
39
+ data.to_hashugar
40
+ end
41
+
42
+ class MissingArgument < StandardError; end
43
+ class HTTPError < StandardError; end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,26 @@
1
+ require_relative 'reviews/russin'
2
+ require_relative 'reviews/moviezine'
3
+ require_relative 'reviews/kritiker'
4
+
5
+ module Shin
6
+ module Reviews
7
+ class << self
8
+ # I don't know why I need this
9
+ def new
10
+ self
11
+ end
12
+
13
+ def russin
14
+ @russin ||= Russin.new
15
+ end
16
+
17
+ def moviezine
18
+ @moviezine ||= Moviezine.new
19
+ end
20
+
21
+ def kritiker
22
+ @kritiker ||= Kritiker.new
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,3 @@
1
+ module Shin
2
+ VERSION = "0.0.1"
3
+ end
data/lib/shin.rb ADDED
@@ -0,0 +1,43 @@
1
+ require_relative 'shin/version'
2
+ require 'json'
3
+ require 'nokogiri'
4
+ require 'httparty'
5
+ require 'hashugar'
6
+
7
+ require_relative 'shin/httparty_icebox'
8
+ require_relative 'shin/base'
9
+ require_relative 'shin/reviews'
10
+ require_relative 'shin/play'
11
+
12
+ module Shin
13
+ class Error < RuntimeError
14
+ end
15
+ end
16
+
17
+ module Shin
18
+ def self.new(*a)
19
+ Shin.new(*a)
20
+ end
21
+ class Shin
22
+ #attr_accessor :options
23
+ def initialize(args={})
24
+ @@options = args
25
+ end
26
+
27
+ def self.get
28
+ @@options ||= {}
29
+ end
30
+
31
+ def base
32
+ @base ||= Base.new
33
+ end
34
+
35
+ def play
36
+ @play ||= Play.new
37
+ end
38
+
39
+ def reviews
40
+ @reviews ||= Reviews.new
41
+ end
42
+ end
43
+ end
data/shin.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ require "shin/version"
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "shin"
9
+ s.version = Shin::VERSION
10
+ s.platform = Gem::Platform::RUBY
11
+ s.licenses = ['GPL 2.0']
12
+
13
+ s.authors = ["Joakim Nylen"]
14
+ s.date = %q{2015-02-23}
15
+ s.email = %q{me@jnylen.nu}
16
+ s.files = `git ls-files`.split($/)
17
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
+ s.homepage = %q{https://github.com/jnylen/shin}
19
+ s.require_paths = ["lib"]
20
+ s.summary = "Shin provides a way to fetch data from various places."
21
+ s.description = "Shin provides a way to fetch data for various places such as SVTPlay, Viasat Image, DFI, SFI etc."
22
+
23
+ # We need these for all
24
+ s.add_dependency(%q<nokogiri>, ["~> 1.5"])
25
+ s.add_runtime_dependency 'oj', '~> 2.11', '>= 2.11.1'
26
+ s.add_runtime_dependency 'httparty', '~> 0.6', '>= 0.6.1'
27
+ s.add_runtime_dependency 'hashugar', '~> 1.0', '>= 1.0.0'
28
+ end
29
+
metadata ADDED
@@ -0,0 +1,131 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shin
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Joakim Nylen
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-02-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: oj
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '2.11'
34
+ - - ">="
35
+ - !ruby/object:Gem::Version
36
+ version: 2.11.1
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - "~>"
42
+ - !ruby/object:Gem::Version
43
+ version: '2.11'
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: 2.11.1
47
+ - !ruby/object:Gem::Dependency
48
+ name: httparty
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '0.6'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 0.6.1
57
+ type: :runtime
58
+ prerelease: false
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - "~>"
62
+ - !ruby/object:Gem::Version
63
+ version: '0.6'
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 0.6.1
67
+ - !ruby/object:Gem::Dependency
68
+ name: hashugar
69
+ requirement: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - "~>"
72
+ - !ruby/object:Gem::Version
73
+ version: '1.0'
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: 1.0.0
77
+ type: :runtime
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - "~>"
82
+ - !ruby/object:Gem::Version
83
+ version: '1.0'
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 1.0.0
87
+ description: Shin provides a way to fetch data for various places such as SVTPlay,
88
+ Viasat Image, DFI, SFI etc.
89
+ email: me@jnylen.nu
90
+ executables: []
91
+ extensions: []
92
+ extra_rdoc_files: []
93
+ files:
94
+ - README.textile
95
+ - lib/shin.rb
96
+ - lib/shin/base.rb
97
+ - lib/shin/httparty_icebox.rb
98
+ - lib/shin/play.rb
99
+ - lib/shin/play/svtplay.rb
100
+ - lib/shin/play/viki.rb
101
+ - lib/shin/reviews.rb
102
+ - lib/shin/reviews/kritiker.rb
103
+ - lib/shin/reviews/moviezine.rb
104
+ - lib/shin/reviews/russin.rb
105
+ - lib/shin/version.rb
106
+ - shin.gemspec
107
+ homepage: https://github.com/jnylen/shin
108
+ licenses:
109
+ - GPL 2.0
110
+ metadata: {}
111
+ post_install_message:
112
+ rdoc_options: []
113
+ require_paths:
114
+ - lib
115
+ required_ruby_version: !ruby/object:Gem::Requirement
116
+ requirements:
117
+ - - ">="
118
+ - !ruby/object:Gem::Version
119
+ version: '0'
120
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ requirements: []
126
+ rubyforge_project:
127
+ rubygems_version: 2.2.2
128
+ signing_key:
129
+ specification_version: 4
130
+ summary: Shin provides a way to fetch data from various places.
131
+ test_files: []