rapgenius 0.1.0 → 1.0.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.
- 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
|

|
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
|