rapgenius 0.1.0 → 1.0.0

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: cba91ddf9f2be805d1fdf8c884a3474b893ec741
4
+ data.tar.gz: 41f02951bd8769319f5ed6a4f4a28a84e38baac3
5
+ SHA512:
6
+ metadata.gz: 2632e2bac0a238c1d1f7989cbe3e3e72e34b7c8b7e4fb442cfa40473fc26472532220ad46b9007849ba630c1cf6cc94f765bcb967d4ac5d13256ff69f0314950
7
+ data.tar.gz: d4f89f5534f08a6ee69a6abefef724ba659f5994dbe766ab58af6bd64dc2be4bb941231ded1ced2d409ce211252711d5e94b451c4c14562dc3508671660207ac
data/.gitignore CHANGED
@@ -1,2 +1,4 @@
1
1
  Gemfile.lock
2
2
  spec/support/cassettes
3
+ .bundle/
4
+ pkg/
data/CHANGELOG.md CHANGED
@@ -15,4 +15,11 @@ __v0.0.3__ (22nd August 2013, *contributed by [tsigo](https://github.com/tsigo)*
15
15
 
16
16
  __v0.1.0__ (29th August 2013, *contributed by [tsigo](https://github.com/tsigo)*)
17
17
 
18
- * Adds support for searching for songs with `RapGenius::Song.search("Song, artist name or other query")`
18
+ * Adds support for searching for songs with `RapGenius::Song.search("Song, artist name or other query")`
19
+
20
+ __v1.0.0__ (27th January 2014)
21
+
22
+ * Switches to using the private REST API used by the soon to be released
23
+ [Genius iOS app](http://rapgenius.com/static/app).
24
+ * Vastly improves quality of data available on songs and their lyrics
25
+ * Provides access to media items and song artists
data/LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2013 Tim Rogers
1
+ Copyright (c) 2013-2014 Tim Rogers
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -1,128 +1,151 @@
1
- # rapgenius
1
+ # rapgenius.rb
2
2
 
3
3
  ![Rap Genius logo](http://f.cl.ly/items/303W0c1i2r100j2u3Y0y/Screen%20Shot%202013-08-17%20at%2016.01.19.png)
4
4
 
5
5
  ## What does this do?
6
6
 
7
- It's a Ruby gem for accessing lyrics and explanations on
8
- [Rap Genius](http://rapgenius.com).
7
+ It's a Ruby gem for accessing lyrics, artists and explanations on
8
+ [Rap Genius](http://rapgenius.com).
9
9
 
10
- They very sadly [don't have an API](https://twitter.com/RapGenius/status/245057326321655808) so I decided to replicate one for myself
11
- with a nice bit of screen scraping with [Nokogiri](https://github.com/sparklemotion/nokogiri), much like my [amex](https://github.com/timrogers/amex), [ucas](https://github.com/timrogers/ucas) and [lloydstsb](https://github.com/timrogers/lloydstsb) gems.
10
+ In pre-1.0.0 versions, this gem used Nokogiri to scrape Rap Genius's pages,
11
+ but no more. The new [Genius iOS app](http://rapgenius.com/static/app) uses
12
+ a private API, which this gem makes use of.
12
13
 
13
14
  ## Installation
14
15
 
15
16
  Install the gem, and you're ready to go. Simply add the following to your
16
17
  Gemfile:
17
18
 
18
- `gem "rapgenius", "~> 0.1.0"`
19
+ `gem "rapgenius", "~> 1.0.0"`
19
20
 
20
21
  ## Usage
21
22
 
22
- Songs on Rap Genius don't have numeric identifiers as far as I can tell - they're identified by a URL slug featuring the artist and song name, for instance "Big-sean-control-lyrics". We use this to fetch a particular track, like so:
23
+ __The best way to get a decent idea of the attributes available on `Song` and
24
+ the other objects is by looking through the files in `lib/rapgenius`.__
25
+
26
+ ### Searching
27
+
28
+ You can search for songs by various fields. All of these
29
+ methods return an array of `Song` objects...:
30
+
31
+ ```ruby
32
+ RapGenius.search("Versace")
33
+ RapGenius.search_by_title("Versace")
34
+ RapGenius.search_by_artist("Migos")
35
+ RapGenius.search_by_lyrics("medusa")
36
+ ```
37
+
38
+ If more than 20 results are returned, you won't be able to get access to
39
+ records beyond the 20th. There doesn't appear to be any pagination support
40
+ in the API.
41
+
42
+ ### Songs
43
+
44
+ Songs on Rap Genius have unique identifiers. They're not especially
45
+ easy to find, but if you hover over the "pyong" button near the top of the page,
46
+ you'll see the song's ID in the URL. Once you have an ID, you can load a
47
+ song via the API:
23
48
 
24
49
  ```ruby
25
50
  require 'rapgenius'
26
- song = RapGenius::Song.find("Big-sean-control-lyrics")
51
+
52
+ song = RapGenius::Song.find(176872)
53
+ song.title # => "Versace"
54
+ song.artists.map(&:name) # => ["Migos", "Drake", "Zaytoven"]
27
55
  ```
28
56
 
29
- Once you've got the song, you can easily load details about it. This uses
30
- Nokogiri to fetch the song's page and then parse it:
57
+ Once you've found the song you're seeking, there's plenty of other useful
58
+ details you can access:
31
59
 
32
60
  ```ruby
33
61
  song.title
34
- # => "Control"
35
-
36
- song.artist
37
- # => "Big Sean"
62
+ # => "Versace"
38
63
 
39
- song.full_artist
40
- # => "Big Sean (Ft. Jay Electronica & Kendrick Lamar)"
64
+ song.url
65
+ # => "http://rapgenius.com/Migos-versace-lyrics""
41
66
 
42
- song.images
43
- # => ["http://s3.amazonaws.com/rapgenius/1376434983_jay-electronica.jpg", "http://s3.amazonaws.com/rapgenius/1375029260_Big%20Sean.png", "http://s3.amazonaws.com/rapgenius/Kendrick-Lamar-1024x680.jpg"]
67
+ song.pyongs
68
+ # => 22
44
69
 
45
70
  song.description
46
- # => "The non-album cut from Sean that basically blew up the Internet due to a world-beating verse by Kendrick Lamar...
71
+ # => "Released in June 2013, not only did they take the beat from Soulja Boy’s OMG part 2 but they absolutely killed it."
47
72
  ```
48
73
 
49
- The `#annotations` accessor on a Song returns an array of RapGenius::Annotation
50
- objects corresponding to different annotated lines of the song, identified by
51
- their `id`.
52
74
 
53
- You can look these up manually using `RapGenius::Annotation.find("id")`. You
54
- can grab the ID for a lyric from a RapGenius page by right clicking on an annotation, copying the shortcut and then finding the number after "http://rapgenius.com".
75
+ ### Lines
76
+
77
+ Once you've got a song, you can then go through its "lines", which includes
78
+ annotated and unannotated parts of the content.
55
79
 
56
80
  ```ruby
57
- song.annotations
58
- # => [<RapGenius::Annotation>, <RapGenius::Annotation>...]
81
+ line = song.lines[1]
59
82
 
60
- annotation = song.annotations[99]
83
+ line.lyric
84
+ # => Versace, Versace, Medusa head on me like I'm 'luminati
61
85
 
62
- annotation.lyric
63
- # => "And that goes for Jermaine Cole, Big KRIT, Wale\nPusha T, Meek Millz, A$AP Rocky, Drake\nBig Sean, Jay Electron', Tyler, Mac Miller"
86
+ line.annotations
87
+ # => ["Read about how this collaboration came to pass here..."]
64
88
 
65
- annotation.explanation
66
- # => "Kendrick calls out some of the biggest names in present day Hip-hop...""
89
+ line.song
90
+ # => #<RapGenius::Song:0x007fccdba50a50 @id=176872...
91
+ ```
67
92
 
68
- annotation.song == song # You can get back to the song from the annotation...
69
- # => true
93
+ ### Media
70
94
 
71
- annotation.id
72
- # => "2093001"
95
+ Rap Genius provides great access to media like MP3s on Soundcloud or videos
96
+ on YouTube direct from songs.
73
97
 
74
- annotation2 = RapGenius::Annotation.find("2093001") # Fetching directly...
98
+ ```ruby
99
+ media = song.media.first
75
100
 
76
- annotation == annotations2
77
- # => true
101
+ media.type
102
+ # => "audio"
103
+
104
+ media.provider
105
+ # => "soundcloud"
106
+
107
+ media.url
108
+ # => "https://soundcloud.com/mixtapemechaniks/migos-ft-drake-versace-remix"
78
109
  ```
79
110
 
80
- You can search for songs by artist and/or title.
111
+ ### Artist
112
+
113
+ You can navigate from a song to its artist, and then to other songs by that
114
+ artist. Magic, huh?!
81
115
 
82
116
  ```ruby
83
- results = RapGenius::Song.search("Big Sean Control")
84
- # => [#<RapGenius::Song:0x007fbe4b9195e0
85
- # @artist="Big Sean (Ft. Jay Electronica & Kendrick Lamar)",
86
- # @title="Control",
87
- # @url="http://rapgenius.com/Big-sean-control-lyrics">,
88
- # #<RapGenius::Song:0x007fbe4b920f70
89
- # @artist="Big Sean (Ft. Jay Electronica & Kendrick Lamar)",
90
- # @title="Control (French Version)",
91
- # @url="http://rapgenius.com/Big-sean-control-french-version-lyrics">,
92
- # #<RapGenius::Song:0x007fbe4b920958
93
- # @artist="Big Sean (Ft. Crobar, Jay Electronica & Kendrick Lamar)",
94
- # @title="Control (Remix) [Kendrick Diss]",
95
- # @url="http://rapgenius.com/Big-sean-control-remix-kendrick-diss-lyrics">,
96
- # #<RapGenius::Song:0x007fbe4b920250
97
- # @artist=
98
- # "Sa-roc (Ft. Big Sean - No I.D., Jay Electronica, Kendrick Lamar & Sa-roc)",
99
- # @title="CONTROL",
100
- # @url="http://rapgenius.com/Sa-roc-control-lyrics">,
101
- # #<RapGenius::Song:0x007fbe4b91ff30
102
- # @artist="C3",
103
- # @title=
104
- # "Control ( Disses Kendrick Lamar , Jay-Z, Tyler The Creator, Big Sean, Meek Mill & More )",
105
- # @url=
106
- # "http://rapgenius.com/C3-control-disses-kendrick-lamar-jay-z-tyler-the-creator-big-sean-meek-mill-and-more-lyrics">]
107
-
108
- results[0].description
109
- # => "The non-album cut from Sean that basically blew up the Internet due to a world-beating verse by Kendrick Lamar...
110
- ```
117
+ artist = song.artist # or song.artists, song.featured_artists or song.producer_artists
111
118
 
112
- ## Contributing
119
+ artist.name
120
+ # => "Migos"
121
+
122
+ artist.type
123
+ # => :primary
113
124
 
114
- After the last few contributions, there's one core thing I'd like to add to the gem:
125
+ artist.url
126
+ # => "http://rapgenius.com/artists/Migos"
115
127
 
116
- * __Support for *\*Genius*__ - Rap Genius also have other sites on subdomains like [News Genius](http://news.rapgenius.com) and [Poetry Genius](http://poetry.rapgenius.com). These could very easily be supported, since theyre identical in terms of markup.
128
+ artist.description
129
+ # => "Migos are an American hip-hop group from Atlanta, Georgia..."
130
+
131
+ artist.songs
132
+ # => [#<RapGenius::Song:0x007fccdb884398...]
133
+ ```
134
+
135
+ ## Contributing
117
136
 
118
137
  If you'd like to contribute anything else, go ahead or better still, make an issue and we can talk it over and spec it out! A few quick tips:
119
138
 
120
- * Don't update the version numbers before your pull request - I'll sort that part out for you!
139
+ * Don't update the version numbers before your pull request - I'll sort that part out for you.
121
140
  * Make sure you write specs, then run them with `$ bundle exec rake`
122
141
  * Update this README.md file so I, and users, know how your changes work
123
142
 
143
+ ## Copyright
144
+
145
+ Copyright (c) 2013-2014 Tim Rogers. See LICENSE for details.
146
+
124
147
  ## Get in touch
125
148
 
126
149
  [timrogers](https://github.com/timrogers) and [tsigo](https://github.com/tsigo) are the gem's primary contributors.
127
150
 
128
- Any questions, thoughts or comments? Email me at <me+rapgenius@timrogers.co.uk> or create an issue.
151
+ Any questions, thoughts or comments? Email me at <me@timrogers.co.uk> or create an issue.
data/lib/rapgenius.rb CHANGED
@@ -1,5 +1,42 @@
1
1
  require 'rapgenius/version'
2
- require 'rapgenius/scraper'
3
- require 'rapgenius/annotation'
2
+ require 'rapgenius/client'
3
+ require 'rapgenius/line'
4
4
  require 'rapgenius/song'
5
- require 'rapgenius/exceptions'
5
+ require 'rapgenius/artist'
6
+ require 'rapgenius/media'
7
+ require 'rapgenius/exceptions'
8
+
9
+ module RapGenius
10
+ extend RapGenius::Client
11
+
12
+ def self.search(query, options={})
13
+ response = Client::HTTPClient.get("/search", query: {q: query}.merge(options))
14
+
15
+ response["response"]["hits"].map do |song|
16
+ result = song["result"]
17
+
18
+ Song.new(
19
+ id: result["id"],
20
+ name: result["name"],
21
+ artist: Artist.new(
22
+ id: result["primary_artist"]["id"],
23
+ name: result["primary_artist"]["name"],
24
+ type: :primary
25
+ ),
26
+ title: result["title"]
27
+ )
28
+ end
29
+ end
30
+
31
+ def self.search_by_artist(query)
32
+ self.search(query, field: "primary_artist_name")
33
+ end
34
+
35
+ def self.search_by_title(query)
36
+ self.search(query, field: "title")
37
+ end
38
+
39
+ def self.search_by_lyrics(query)
40
+ self.search(query, field: "lyrics")
41
+ end
42
+ end
@@ -0,0 +1,54 @@
1
+ # encoding: utf-8
2
+ module RapGenius
3
+ class Artist
4
+ include RapGenius::Client
5
+
6
+ def self.find(id)
7
+ self.new(id: id).tap { |artist| artist.document }
8
+ end
9
+
10
+ def initialize(kwargs = {})
11
+ @id = kwargs.delete(:id)
12
+ @name = kwargs.delete(:name)
13
+ @type = kwargs.delete(:type)
14
+ self.url = "artists/#{@id}"
15
+ end
16
+
17
+ def response
18
+ document["response"]["artist"]
19
+ end
20
+
21
+ def name
22
+ @name ||= response["name"]
23
+ end
24
+
25
+ def image
26
+ @image ||= response["image_url"]
27
+ end
28
+
29
+ def url
30
+ response["url"]
31
+ end
32
+
33
+ def description
34
+ @description ||= response["description"]["dom"]["children"].map do |node|
35
+ parse_description(node)
36
+ end.flatten.join("")
37
+ end
38
+
39
+ def songs
40
+ @songs ||= fetch("/artists/#{@id}/songs")["response"]["songs"].map do |song|
41
+ Song.new(
42
+ artist: Artist.new(
43
+ name: song["primary_artist"]["name"],
44
+ id: song["primary_artist"]["id"],
45
+ type: :primary
46
+ ),
47
+ title: song["title"],
48
+ id: song["id"]
49
+ )
50
+ end
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,57 @@
1
+ require 'httparty'
2
+
3
+ module RapGenius
4
+ module Client
5
+ # HTTParty client
6
+ #
7
+ # Sets some useful defaults for all of our requests.
8
+ #
9
+ # See Scraper#fetch
10
+ class HTTPClient
11
+ include HTTParty
12
+
13
+ format :json
14
+ base_uri 'https://api.rapgenius.com'
15
+ headers 'User-Agent' => "rapgenius.rb v#{RapGenius::VERSION}"
16
+ end
17
+
18
+ BASE_URL = HTTPClient.base_uri + "/".freeze
19
+
20
+ def url=(url)
21
+ unless url =~ /^https?:\/\//
22
+ @url = BASE_URL + url.gsub(/^\//, '')
23
+ else
24
+ @url = url
25
+ end
26
+ end
27
+
28
+ def document
29
+ @document ||= fetch(@url)
30
+ end
31
+
32
+ def fetch(url)
33
+ response = HTTPClient.get(url)
34
+
35
+ if response.code != 200
36
+ raise RapGenius::Error, "Received a #{response.code} HTTP response"
37
+ end
38
+
39
+ response.parsed_response
40
+ end
41
+
42
+ # Descriptions are formatted in an irritating way, encapsulating the
43
+ # various kinds of HTML tag that can be included. This parses that
44
+ # into text, but some content may be lost.
45
+ def parse_description(node)
46
+ if node.is_a? String
47
+ node
48
+ elsif node.is_a? Array
49
+ node.map { |subnode| parse_description(subnode) }
50
+ elsif node.is_a? Hash
51
+ return unless node.key? "children"
52
+ parse_description(node["children"])
53
+ end
54
+ end
55
+
56
+ end
57
+ end
@@ -1,4 +1,4 @@
1
1
  module RapGenius
2
- class ScraperError < StandardError
2
+ class Error < StandardError
3
3
  end
4
4
  end
@@ -0,0 +1,59 @@
1
+ module RapGenius
2
+ class Line
3
+ include RapGenius::Client
4
+
5
+ attr_reader :id
6
+
7
+ def self.find(id)
8
+ self.new(id: id).tap { |line| line.document }
9
+ end
10
+
11
+ def initialize(kwargs)
12
+ @id = kwargs.delete(:id)
13
+ @song = kwargs.delete(:song)
14
+ @lyric = kwargs.delete(:lyric)
15
+ self.url = "referents/#{@id}" if @id
16
+ end
17
+
18
+ def response
19
+ return nil unless @id
20
+ document["response"]["referent"]
21
+ end
22
+
23
+ def lyric
24
+ if @id
25
+ @lyric ||= response["fragment"]
26
+ else
27
+ @lyric
28
+ end
29
+ end
30
+
31
+ def annotated?
32
+ !!@id
33
+ end
34
+
35
+ alias_method :explained?, :annotated?
36
+
37
+ # A line can have multiple annotations, usually if it has a community one
38
+ # and a verified one. Ideally, these would be encapsulated into an
39
+ # Annotation class, but I don't have time for now.
40
+ def explanations
41
+ return nil unless @id
42
+ @explanation ||= response["annotations"].map do |annotation|
43
+ annotation["body"]["dom"]["children"].map do |node|
44
+ parse_description(node)
45
+ end.join("")
46
+ end.flatten
47
+ end
48
+
49
+ alias_method :annotations, :explanations
50
+
51
+ def song
52
+ if @id
53
+ @song ||= Song.find(response['song_id'])
54
+ else
55
+ @song
56
+ end
57
+ end
58
+ end
59
+ end