tiegz-ruby-mtv 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.
Files changed (7) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README +52 -0
  3. data/Rakefile +5 -0
  4. data/TODO +4 -0
  5. data/ruby-mtv.gemspec +21 -0
  6. data/ruby-mtv.rb +283 -0
  7. metadata +67 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Tieg Zaharia
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,52 @@
1
+ ruby-mtv
2
+ by Tieg Zaharia
3
+ http://github.com/tiegz/ruby-mtv
4
+
5
+ == DESCRIPTION:
6
+
7
+ This is a Ruby library for the MTV api. Right now it has classes for Artists and Videos, although
8
+ there is much to be done, spec'd and/or tested!
9
+
10
+ == REQUIREMENTS:
11
+
12
+ * git (http://git.or.cz) tested with 1.5.3.4
13
+
14
+ == INSTALL:
15
+
16
+ $ gem sources -a http://gems.github.com/ (you only need to do this once)
17
+ $ gem install tiegz-ruby-mtv
18
+
19
+ == USAGE:
20
+
21
+ Require the gem...
22
+
23
+ require 'tiegz-ruby-mtv'
24
+
25
+ To find an artist,
26
+
27
+ artist = MTV::Artist.search('dinosaur jr').first
28
+ # => #<MTV::Artist name: "Dinosaur Jr.", uid: "dinosaur_jr", uri: "http://api.mtvnservices.com/1/artist/dinosaur_jr/">
29
+
30
+ To find related artists,
31
+
32
+ artist.related
33
+ # => [#<MTV::Artist name: "Afghan Whigs", uid: "afghan_whigs", uri: "http://api.mtvnservices.com/1/artist/afghan_whigs/">,
34
+ #<MTV::Artist name: "The Breeders", uid: "breeders", uri: "http://api.mtvnservices.com/1/artist/breeders/">, ... ]
35
+
36
+ To find a video,
37
+
38
+ video = MTV::Video.search('dinosaur jr').first
39
+ # => #<MTV::Video title: "Been There All the Time", uid: "hznHivqrbHHDAXBAZ", artist_name: "Dinosaur Jr.",
40
+ uri: "http://api.mtvnservices.com/1/video/hznHivqrbHHDAXB...",
41
+ artist_uri: "http://api.mtvnservices.com/1/artist/dinosaur_jr/">
42
+ v.artist
43
+ # => #<MTV::Artist name: "Dinosaur Jr.", uid: "dinosaur_jr", uri: "http://api.mtvnservices.com/1/artist/dinosaur_jr/">
44
+
45
+ To find all videos for an artist,
46
+
47
+ artist.videos
48
+ # => [#<MTV::Video title: ["Been There All the Time"], uid: "hznHivqrbHHDAXBAZ", artist_name: ["Dinosaur Jr."] ...>, ... ]
49
+
50
+ == RELEVANT LINKS
51
+
52
+ MTVN api docs: http://developer.mtvnservices.com/docs
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/gempackagetask'
4
+
5
+
data/TODO ADDED
@@ -0,0 +1,4 @@
1
+ - Fix edge cases where no string is passed to a #find, #browse, #search, etc. method
2
+ - Add request cacheing
3
+ - Add specs (with mock requests)
4
+ - Better rdocs
data/ruby-mtv.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = "ruby-mtv"
3
+ s.version = "1.0"
4
+ s.date = "2008-09-16"
5
+ s.summary = "Ruby wrapper library for MTV api"
6
+ s.email = "tiegzaharia@gmail.com"
7
+ s.homepage = "http://github.com/tiegz/ruby-mtv"
8
+ s.description = "ruby-mtv is a Ruby library for accessing Artist and Video objects through the MTVN api."
9
+ s.has_rdoc = true
10
+ s.authors = ["Tieg Zaharia"]
11
+ s.files = ["MIT-LICENSE",
12
+ "Rakefile",
13
+ "README",
14
+ "ruby-mtv.gemspec",
15
+ "ruby-mtv.rb",
16
+ "TODO"]
17
+ s.test_files = []
18
+ s.rdoc_options = ["--main", "README"]
19
+ s.extra_rdoc_files = ["README"]
20
+ s.add_dependency("active-support", ["> 2.0.2"])
21
+ end
data/ruby-mtv.rb ADDED
@@ -0,0 +1,283 @@
1
+ require 'rubygems'
2
+ require 'activesupport'
3
+ require 'open-uri'
4
+ require 'benchmark'
5
+
6
+ module MTV
7
+ class Error < StandardError; end
8
+
9
+ class << self
10
+ attr_accessor :base_url, :host, :protocol, :debug
11
+
12
+ end
13
+
14
+ self.host = "api.mtvnservices.com/1"
15
+ self.protocol = "http"
16
+ self.base_url = "#{protocol}://#{host}"
17
+ self.debug = true if ENV['DEBUG']
18
+
19
+ class Base
20
+ def initialize(values={})
21
+ values.each { |k, v| send "#{k}=", v }
22
+ end
23
+
24
+ class << self
25
+ def request path
26
+ url = URI.escape "#{MTV.base_url}/#{path}"
27
+ response = nil
28
+ seconds = Benchmark.realtime { response = open url }
29
+ puts " \e[4;36;1mREQUEST (#{sprintf("%f", seconds)})\e[0m \e[0;1m#{url}\e[0m" if MTV.debug
30
+ response.is_a?(String) ? response : response.read
31
+ rescue OpenURI::HTTPError => e
32
+ puts " \e[4;36;1mREQUEST (404)\e[0m \e[0;1m#{url}\e[0m" if MTV.debug
33
+ nil
34
+ end
35
+
36
+ protected
37
+ # assuming that the uid for the artist is downcase and no spaces, this tries to match better
38
+ def string_to_uid(str)
39
+ str.downcase.underscore.gsub(" ", "_")
40
+ end
41
+ end
42
+
43
+ # Copied from ActiveRecord::Base
44
+ def attribute_for_inspect(attr_name)
45
+ value = send(attr_name)
46
+
47
+ if value.is_a?(String) && value.length > 50
48
+ "#{value[0..50]}...".inspect
49
+ elsif value.is_a?(Date) || value.is_a?(Time)
50
+ %("#{value.to_s(:db)}")
51
+ else
52
+ value.inspect
53
+ end
54
+ end
55
+ end
56
+
57
+ # This class represents a musical artist in MTV's database.
58
+ class Artist < Base
59
+ attr_accessor :name, :uid, :uri, :links, :updated
60
+
61
+ def initialize(values={})
62
+ super(values)
63
+ self.uid = uri.gsub(MTV.base_url + "/artist", '').delete '/' unless @uid
64
+ end
65
+
66
+ def inspect
67
+ attrs = [:name, :uid, :uri].map { |name| "#{name}: #{attribute_for_inspect(name)}" }.compact.join(", ")
68
+ "#<#{self.class} #{attrs}>"
69
+ end
70
+
71
+ # artist = MTV::Artist.search('In Flames')[2]
72
+ # MTV::Artist.browse => I Am Ghost, IB3, INXS, Ice Cube, ...
73
+ def browse
74
+ @browse ||= Artist.browse(uid.first)
75
+ end
76
+
77
+ # artist = MTV::Artist.search 'Frank Zappa'
78
+ # MTV::Artist.browse => Amon Amarth, Amorphis, Arch Enemy, Arcturus, Borknagar, ...
79
+ def related
80
+ @related ||= Artist.related(self)
81
+ end
82
+
83
+ # artist = MTV::Artist.find('beck')
84
+ # MTV::Artist.videos => ...
85
+ def videos
86
+ @videos ||= Video.from_artist(self)
87
+ end
88
+
89
+ class << self
90
+ # MTV::Artist.browse 'x' => X, X-Clan, The X-Ecutioners, Xiren, Xscape, Xtreme, Xzibit
91
+ def browse(letter='a')
92
+ response = request "artist/browse/#{letter.to_s.first}/"
93
+ raise Error, "That artist not found!" if response.nil? || response.empty?
94
+ parse_many response
95
+ end
96
+
97
+ # MTV::Artist.find 'beck'
98
+ # MTV::Artist.find 'meGADeath'
99
+ def find(name, options={})
100
+ return name if name.is_a? Artist
101
+
102
+ options.symbolize_keys!
103
+ name = string_to_uid(options[:name] || name)
104
+
105
+ response = request "artist/#{name}/"
106
+ raise Error, "That artist not found!" if response.nil? || response.empty?
107
+ parse_one response
108
+ end
109
+
110
+ # MTV::Artist.related 'qtip'
111
+ # MTV::Artist.related MTV::Artist.find('qtip')
112
+ def related(artist)
113
+ artist = find(artist)
114
+ response = request "artist/#{artist.uid}/related/"
115
+ raise Error, "That artist not found!" if response.nil? || response.empty?
116
+ parse_many response
117
+ end
118
+
119
+ # MTV::Artist.search 'beck', :max_results => 1, :start_index => 1
120
+ def search(term=nil, options={})
121
+ options.symbolize_keys!
122
+ params = {}
123
+ params[:'max-results'] = options[:max_results] || 1
124
+ params[:'start-index'] = options[:start_index]
125
+ term = options[:term] || term
126
+ params.reject! { |k,v| !v }
127
+ response = request "artist/search?#{term.to_query('term')}&#{params.to_param}"
128
+ raise Error, "That artist not found!" if response.nil? || response.empty?
129
+ parse_many response
130
+ end
131
+
132
+ def videos(artist)
133
+ MTV::Video.from_artist(artist)
134
+ end
135
+
136
+ protected
137
+ def parse_many(body)
138
+ entries = XmlSimple.xml_in(body)['entry']
139
+ raise Error, "That artist not found!" if entries.nil?
140
+ entries = [entries] unless entries.is_a? Array
141
+ entries.map { |entry|
142
+ instantiate entry
143
+ }.reject { |artist| MTV::Artist.name.nil? || MTV::Artist.name.empty? }
144
+ end
145
+
146
+ def parse_one(body)
147
+ entry = XmlSimple.xml_in(body)
148
+ raise Error, "That artist not found!" if entry['author'].first['name'].nil? || entry['author'].first['name'].empty?
149
+ instantiate entry
150
+ end
151
+
152
+ def instantiate(entry={})
153
+ Artist.new(:name => entry['author'].first['name'].to_s,
154
+ :uri => entry['author'].first['uri'].to_s,
155
+ :links => entry['link'].map { |link| link['href'].to_s },
156
+ :updated => entry['updated'].to_s.to_datetime)
157
+ end
158
+ end
159
+ end
160
+
161
+ # This class represents a music video in MTV's database.
162
+ class Video < Base
163
+ attr_accessor :artist, :artist_name, :artist_uri, :category, :content, :credit,
164
+ :media_player_url, :media_thumbnails, :media_title, :media_description,
165
+ :media_keywords, :media_duration, :media_expression, :media_url, :media_type,
166
+ :media_credits, :media_category, :media_medium,
167
+ :title, :published, :uid, :updated, :uri, :vid
168
+
169
+ def initialize(values={})
170
+ super(values)
171
+ self.uid = uri.gsub(MTV.base_url + "/video", '').delete '/' unless @uid
172
+ self.vid = media_url.split(':').last
173
+ end
174
+
175
+ def artist
176
+ @artist ||= Artist.find(artist_uri.gsub(MTV.base_url + "/artist", '').delete('/'))
177
+ end
178
+
179
+ def inspect
180
+ attrs = [:title, :uid, :artist_name, :uri, :artist_uri].map { |name| "#{name}: #{attribute_for_inspect(name)}" }.compact.join(", ")
181
+ "#<#{self.class} #{attrs}>"
182
+ end
183
+
184
+ def thumbnails=(val)
185
+ @thumbnails = (val.is_a?(Array) ? val : [val]).map { |t| Thumbnail.new(t['url'], t['width'], t['height']) unless t.nil? }
186
+ end
187
+
188
+ class << self
189
+ # MTV::Video.find '1234abcd'
190
+ def find(uid, options={})
191
+ return uid if uid.is_a? Artist
192
+
193
+ options.symbolize_keys!
194
+ uid = options[:uid] || uid
195
+
196
+ response = request "video/#{uid}/"
197
+ raise Error, "That video not found!" if response.nil? || response.empty?
198
+ parse_one response
199
+ end
200
+
201
+ # MTV::Video.from_artist(Artist.find('beck'))
202
+ # MTV::Video.from_artist('beck')
203
+ def from_artist(artist)
204
+ artist_uid = artist.is_a?(Artist) ? artist.uid : string_to_uid(artist)
205
+
206
+ response = request "artist/#{artist_uid}/videos"
207
+ raise Error, "No videos found!" if response.nil? || response.empty?
208
+ parse_many response
209
+ end
210
+
211
+ # http://api.mtvnservices.com/1/video/search/[parameters]
212
+ # MTV::Video.search 'the golden age', :max_results => 1, :start_index => 1
213
+ def search(term=nil, options={})
214
+ options.symbolize_keys!
215
+ params = { :'max-results' => (options[:max_results] || 1),
216
+ :'start-index' => options[:start_index] }.reject { |k,v| v.nil? || v.to_s.empty? }.to_param
217
+ params = "&#{params}" unless params.empty?
218
+ term = (options[:term] || term).to_query('term')
219
+
220
+ response = request "video/search?#{term}#{params}"
221
+ raise Error, "No videos not found!" if response.nil? || response.empty?
222
+ parse_many response
223
+ end
224
+
225
+ protected
226
+ def parse_many(body)
227
+ body = hack_media_enclosures(body)
228
+ entries = XmlSimple.xml_in(body)['entry']
229
+ entries = [entries] unless entries.is_a? Array
230
+ entries.reject! { |entry| entry.nil? }
231
+ entries.map { |entry|
232
+ instantiate entry
233
+ }
234
+ end
235
+
236
+ def parse_one(body)
237
+ body = hack_media_enclosures(body)
238
+ entry = XmlSimple.xml_in(body)
239
+ raise Error, "That artist not found!" if entry['author']['name'].nil? || entry['author']['name'].empty?
240
+ instantiate entry
241
+ end
242
+
243
+ # HACK because XmlSimple doesn't seem to handle enclosures, we'll replace colons with hyphens
244
+ # TODO write a better regexp for that hack :\
245
+ def hack_media_enclosures(body)
246
+ body.gsub("<media:", "<media_").gsub("</media:", "</media_")
247
+ end
248
+
249
+ def instantiate(entry={})
250
+ Video.new(:title => entry['title'],
251
+ :artist_uri => entry['author'].first['uri'],
252
+ :artist_name => entry['author'].first['name'],
253
+ :published => entry['published'],
254
+ :content => entry['content'], # Artist Name, Video Title, and Record Label
255
+ :uri => entry['link'].select { |link| link['rel'] == 'self' }.first['href'],
256
+ :updated => (entry['updated'].to_datetime rescue nil),
257
+ :media_category => entry['media_category'], # governing agreement
258
+ :media_credits => entry['media_credit'].map { |credit| "#{credit['content'] || 'N/A'} (#{credit['role']})}" }.join(', '),
259
+ :media_description => entry['media_description'],
260
+ :media_duration => entry['media_content'].first['duration'],
261
+ :media_expression => entry['media_content'].first['expression'],
262
+ :media_keywords => entry['media_keywords'],
263
+ :media_medium => entry['media_content'].first['medium'],
264
+ :media_player_url => entry['media_player'].first['url'],
265
+ :media_thumbnails => entry['media_thumbnail'],
266
+ :media_title => entry['media_title'],
267
+ :media_type => entry['media_content'].first['type'],
268
+ :media_url => entry['media_content'].first['url'])
269
+ end
270
+ end
271
+ end
272
+
273
+ class Thumbnail
274
+ attr_accessor :url, :width, :height
275
+ def initialize(url, width, height)
276
+ self.url = url
277
+ self.width = width
278
+ self.height = height
279
+ end
280
+ end
281
+
282
+ end
283
+
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tiegz-ruby-mtv
3
+ version: !ruby/object:Gem::Version
4
+ version: "1.0"
5
+ platform: ruby
6
+ authors:
7
+ - Tieg Zaharia
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-09-16 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: active-support
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">"
21
+ - !ruby/object:Gem::Version
22
+ version: 2.0.2
23
+ version:
24
+ description: ruby-mtv is a Ruby library for accessing Artist and Video objects through the MTVN api.
25
+ email: tiegzaharia@gmail.com
26
+ executables: []
27
+
28
+ extensions: []
29
+
30
+ extra_rdoc_files:
31
+ - README
32
+ files:
33
+ - MIT-LICENSE
34
+ - Rakefile
35
+ - README
36
+ - ruby-mtv.gemspec
37
+ - ruby-mtv.rb
38
+ - TODO
39
+ has_rdoc: true
40
+ homepage: http://github.com/tiegz/ruby-mtv
41
+ post_install_message:
42
+ rdoc_options:
43
+ - --main
44
+ - README
45
+ require_paths:
46
+ - lib
47
+ required_ruby_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ version:
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ requirements: []
60
+
61
+ rubyforge_project:
62
+ rubygems_version: 1.2.0
63
+ signing_key:
64
+ specification_version: 2
65
+ summary: Ruby wrapper library for MTV api
66
+ test_files: []
67
+