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 +7 -0
- data/.gitignore +2 -0
- data/CHANGELOG.md +8 -1
- data/LICENSE +1 -1
- data/README.md +95 -72
- data/lib/rapgenius.rb +40 -3
- data/lib/rapgenius/artist.rb +54 -0
- data/lib/rapgenius/client.rb +57 -0
- data/lib/rapgenius/exceptions.rb +1 -1
- data/lib/rapgenius/line.rb +59 -0
- data/lib/rapgenius/media.rb +11 -0
- data/lib/rapgenius/song.rb +105 -33
- data/lib/rapgenius/version.rb +1 -1
- data/rapgenius.gemspec +5 -4
- data/spec/rapgenius/artist_spec.rb +33 -0
- data/spec/rapgenius/client_spec.rb +42 -0
- data/spec/rapgenius/line_spec.rb +23 -0
- data/spec/rapgenius/media_spec.rb +19 -0
- data/spec/rapgenius/song_spec.rb +46 -44
- metadata +27 -46
- data/lib/rapgenius/annotation.rb +0 -37
- data/lib/rapgenius/scraper.rb +0 -82
- data/spec/rapgenius/annotation_spec.rb +0 -41
- data/spec/rapgenius/scraper_spec.rb +0 -54
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
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
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
|
-
|
11
|
-
|
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", "~>
|
19
|
+
`gem "rapgenius", "~> 1.0.0"`
|
19
20
|
|
20
21
|
## Usage
|
21
22
|
|
22
|
-
|
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
|
-
|
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
|
30
|
-
|
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
|
-
# => "
|
35
|
-
|
36
|
-
song.artist
|
37
|
-
# => "Big Sean"
|
62
|
+
# => "Versace"
|
38
63
|
|
39
|
-
song.
|
40
|
-
# => "
|
64
|
+
song.url
|
65
|
+
# => "http://rapgenius.com/Migos-versace-lyrics""
|
41
66
|
|
42
|
-
song.
|
43
|
-
# =>
|
67
|
+
song.pyongs
|
68
|
+
# => 22
|
44
69
|
|
45
70
|
song.description
|
46
|
-
# => "
|
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
|
-
|
54
|
-
|
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.
|
58
|
-
# => [<RapGenius::Annotation>, <RapGenius::Annotation>...]
|
81
|
+
line = song.lines[1]
|
59
82
|
|
60
|
-
|
83
|
+
line.lyric
|
84
|
+
# => Versace, Versace, Medusa head on me like I'm 'luminati
|
61
85
|
|
62
|
-
|
63
|
-
# => "
|
86
|
+
line.annotations
|
87
|
+
# => ["Read about how this collaboration came to pass here..."]
|
64
88
|
|
65
|
-
|
66
|
-
# =>
|
89
|
+
line.song
|
90
|
+
# => #<RapGenius::Song:0x007fccdba50a50 @id=176872...
|
91
|
+
```
|
67
92
|
|
68
|
-
|
69
|
-
# => true
|
93
|
+
### Media
|
70
94
|
|
71
|
-
|
72
|
-
|
95
|
+
Rap Genius provides great access to media like MP3s on Soundcloud or videos
|
96
|
+
on YouTube direct from songs.
|
73
97
|
|
74
|
-
|
98
|
+
```ruby
|
99
|
+
media = song.media.first
|
75
100
|
|
76
|
-
|
77
|
-
# =>
|
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
|
-
|
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
|
-
|
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
|
-
|
119
|
+
artist.name
|
120
|
+
# => "Migos"
|
121
|
+
|
122
|
+
artist.type
|
123
|
+
# => :primary
|
113
124
|
|
114
|
-
|
125
|
+
artist.url
|
126
|
+
# => "http://rapgenius.com/artists/Migos"
|
115
127
|
|
116
|
-
|
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
|
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/
|
3
|
-
require 'rapgenius/
|
2
|
+
require 'rapgenius/client'
|
3
|
+
require 'rapgenius/line'
|
4
4
|
require 'rapgenius/song'
|
5
|
-
require 'rapgenius/
|
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
|
data/lib/rapgenius/exceptions.rb
CHANGED
@@ -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
|