shin 0.0.1

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.
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: []