├── .gitignore
├── GeniusLyricsAPI.iml
├── README.md
├── pom.xml
└── src
└── main
└── java
├── core
├── GLA.java
├── HttpManager.java
├── ProjectInfo.java
└── Statics.java
└── genius
├── Lyrics.java
├── LyricsParser.java
└── SongSearch.java
/.gitignore:
--------------------------------------------------------------------------------
1 | # Project related
2 | builds/
3 | data/
4 | resources/
5 |
6 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
7 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
8 |
9 | # Gradle and Maven with auto-import
10 | # When using Gradle or Maven with auto-import, you should exclude module files,
11 | # since they will be recreated, and may cause churn. Uncomment if using
12 | # auto-import.
13 | # .idea/modules.xml
14 | # .idea/*.iml
15 | # .idea/modules
16 |
17 | # CMake
18 | cmake-build-*/
19 |
20 | # Mongo Explorer plugin
21 | .idea/**/mongoSettings.xml
22 |
23 | # File-based project format
24 | *.iws
25 |
26 | # IntelliJ
27 | \.idea/
28 | out/
29 | target/
30 |
31 | # mpeltonen/sbt-idea plugin
32 | .idea_modules/
33 |
34 | # JIRA plugin
35 | atlassian-ide-plugin.xml
36 |
37 | # Crashlytics plugin (for Android Studio and IntelliJ)
38 | com_crashlytics_export_strings.xml
39 | crashlytics.properties
40 | crashlytics-build.properties
41 | fabric.properties
42 |
--------------------------------------------------------------------------------
/GeniusLyricsAPI.iml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://jitpack.io/#LowLevelSubmarine/GeniusLyricsAPI)
2 |
3 | # Genius Lyrics API (not-official)
4 | This API is made to search and parse lyrics from the web. The Genius library should provide all the songs your users will ever search for. It uses genius's embed page to load lyrics without getting rate-limited, and caches every parsed lyrics and search results (runtime).
5 |
6 | ## Easy to use!
7 | Just create a new *GLA* object and start with the *search(String)* method ;)
8 |
9 | ## Download
10 |
11 | **Gradle**
12 | ```gradle
13 | dependencies {
14 | implementation 'com.github.LowLevelSubmarine:GeniusLyricsAPI:1.0.3'
15 | }
16 |
17 | repositories {
18 | maven { url 'https://jitpack.io' }
19 | }
20 | ```
21 |
22 | **Maven**
23 | ```xml
24 |
25 | com.github.LowLevelSubmarine
26 | GeniusLyricsAPI
27 | 1.0.3
28 |
29 | ```
30 | ```xml
31 |
32 |
33 | jitpack.io
34 | https://jitpack.io
35 |
36 |
37 | ```
38 |
39 | # Disclaimer
40 | This repository is for research purposes only, the use of this code is your responsibility.
41 |
42 | I take NO responsibility and/or liability for how you choose to use any of the source code available here. By using any of the files available in this repository, you understand that you are AGREEING TO USE AT YOUR OWN RISK. Once again, ALL files available here are for EDUCATION and/or RESEARCH purposes ONLY.
43 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 4.0.0
4 |
5 | com.lowlevelsubmarine
6 | GeniusLyricsAPI
7 | 1.0.0
8 |
9 |
10 |
11 | org.apache.maven.plugins
12 | maven-compiler-plugin
13 |
14 | 7
15 | 7
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | com.github.scribejava
24 | scribejava-apis
25 | 5.5.0
26 |
27 |
28 | org.json
29 | json
30 | 20180813
31 |
32 |
33 |
34 |
--------------------------------------------------------------------------------
/src/main/java/core/GLA.java:
--------------------------------------------------------------------------------
1 | package core;
2 |
3 | import genius.SongSearch;
4 |
5 | import java.io.IOException;
6 |
7 | public class GLA {
8 |
9 | private HttpManager httpManager = new HttpManager();
10 |
11 | public SongSearch search(String query) throws IOException {
12 | return new SongSearch(this, query);
13 | }
14 |
15 | public HttpManager getHttpManager() {
16 | return this.httpManager;
17 | }
18 |
19 | public static void main(String[] args) throws Exception {
20 | GLA gla = new GLA();
21 | System.out.println("Searching...");
22 | long startMs = System.currentTimeMillis();
23 | System.out.println(gla.search("Kygo").getHits().get(0).fetchLyrics());
24 | System.out.println(System.currentTimeMillis() - startMs + "ms");
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/core/HttpManager.java:
--------------------------------------------------------------------------------
1 | package core;
2 |
3 | import javax.net.ssl.HttpsURLConnection;
4 | import java.io.*;
5 | import java.net.HttpURLConnection;
6 | import java.net.URL;
7 |
8 | public class HttpManager {
9 |
10 | public HttpURLConnection getConnection(URL url) throws IOException {
11 | HttpURLConnection con = (HttpURLConnection) url.openConnection();
12 | con.setRequestProperty("User-Agent", Statics.USER_AGENT);
13 | con.setRequestProperty("accept-language", "en-US");
14 | con.setRequestProperty("Content-Type","application/json");
15 | con.setRequestProperty("Accept","application/json");
16 | return con;
17 | }
18 |
19 | public String executeGet(HttpURLConnection con) {
20 | try {
21 | con.setRequestMethod("GET");
22 | BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream()));
23 | String inputLine;
24 | StringBuilder response = new StringBuilder();
25 | while ((inputLine = in.readLine()) != null) {
26 | response.append(inputLine);
27 | }
28 | in.close();
29 | return response.toString();
30 | } catch (Exception e) {
31 | e.printStackTrace();
32 | return null;
33 | }
34 | }
35 |
36 | public String executePost(HttpURLConnection con, String content) {
37 | try {
38 | con.setRequestMethod("POST");
39 | con.setDoOutput(true);
40 | DataOutputStream wr = new DataOutputStream(con.getOutputStream());
41 | byte[] postData = content.getBytes();
42 | wr.write(postData);
43 | //wr.flush();
44 | //wr.close();
45 | con.connect();
46 | int responseCode = con.getResponseCode();
47 | if(responseCode == HttpsURLConnection.HTTP_OK){
48 | return readStream(con.getInputStream());
49 | }else{
50 | return readStream(con.getErrorStream());
51 | }
52 | } catch (Exception e) {
53 | e.printStackTrace();
54 | return null;
55 | }
56 |
57 | }
58 |
59 | private String readStream(InputStream in) {
60 | BufferedReader reader = null;
61 | StringBuffer response = new StringBuffer();
62 | try {
63 | reader = new BufferedReader(new InputStreamReader(in));
64 | String line = "";
65 | while ((line = reader.readLine()) != null) {
66 | response.append(line);
67 | }
68 | } catch (IOException e) {
69 | e.printStackTrace();
70 | } finally {
71 | if (reader != null) {
72 | try {
73 | reader.close();
74 | } catch (IOException e) {
75 | e.printStackTrace();
76 | }
77 | }
78 | }
79 | return response.toString();
80 | }
81 |
82 | }
83 |
--------------------------------------------------------------------------------
/src/main/java/core/ProjectInfo.java:
--------------------------------------------------------------------------------
1 | package core;
2 |
3 | public class ProjectInfo {
4 |
5 | //Project
6 | public static final String NAME = "GLA";
7 |
8 | //Version
9 | public static final String VERSION_NUMBER = "1.1.2";
10 | public static final String VERSION_TITLE = "BETA";
11 | public static final String VERSION = VERSION_NUMBER + " - " + VERSION_TITLE;
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/src/main/java/core/Statics.java:
--------------------------------------------------------------------------------
1 | package core;
2 |
3 | public class Statics {
4 |
5 | public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.100 Safari/537.36";
6 | public static final String HTTP_HEADER_REFERER = "referer";
7 |
8 | }
9 |
--------------------------------------------------------------------------------
/src/main/java/genius/Lyrics.java:
--------------------------------------------------------------------------------
1 | package genius;
2 |
3 | import core.GLA;
4 |
5 | public class Lyrics {
6 |
7 | private final GLA gla;
8 | private final String path;
9 | private final String id;
10 |
11 | public Lyrics(GLA gla, String id, String path) {
12 | this.path = path;
13 | this.gla = gla;
14 | this.id = id;
15 | }
16 |
17 | public String getText() {
18 | return new LyricsParser(this.gla).get(this.id);
19 | }
20 |
21 | @Override
22 | public String toString() {
23 | return this.path;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/genius/LyricsParser.java:
--------------------------------------------------------------------------------
1 | package genius;
2 |
3 | import core.GLA;
4 | import core.ProjectInfo;
5 |
6 | import java.io.IOException;
7 | import java.net.URL;
8 | import java.net.URLConnection;
9 | import java.util.Scanner;
10 |
11 | public class LyricsParser {
12 |
13 | private static final String GENIUS_EMBED_URL_HEAD = "https://genius.com/songs/";
14 | private static final String GENIUS_EMBED_URL_TAIL = "/embed.js";
15 |
16 | private GLA gla;
17 |
18 | public LyricsParser(GLA gla) {
19 |
20 | //Construct crawlCache
21 | this.gla = gla;
22 |
23 | }
24 |
25 | public String get(String id) {
26 | return parseLyrics(id);
27 | }
28 |
29 | private String parseLyrics(String id) {
30 | try {
31 | URLConnection connection = new URL(GENIUS_EMBED_URL_HEAD + id + GENIUS_EMBED_URL_TAIL).openConnection();
32 | connection.setRequestProperty("User-Agent", ProjectInfo.VERSION);
33 | Scanner scanner = new Scanner(connection.getInputStream());
34 | scanner.useDelimiter("\\A");
35 | String raw = "";
36 | while (scanner.hasNext()) {
37 | raw += scanner.next();
38 | }
39 | if (raw.equals("")) {
40 | return null;
41 | }
42 | return getReadable(raw);
43 | } catch (IOException e) {
44 | return null;
45 | }
46 | }
47 |
48 | private String getReadable(String rawLyrics) {
49 | //Remove start
50 | rawLyrics = rawLyrics.replaceAll("[\\S\\s]*
[ (\\\\\\\\n)]*", "");
51 | //Remove end
52 | rawLyrics = rawLyrics.replaceAll("[ (\\\\\\\\n)]*<\\\\/div>[\\S\\s]*", "");
53 | //Remove tags between
54 | rawLyrics = rawLyrics.replaceAll("<[^<>]*>", "");
55 | //Unescape spaces
56 | rawLyrics = rawLyrics.replaceAll("\\\\\\\\n","\n");
57 | //Unescape '
58 | rawLyrics = rawLyrics.replaceAll("\\\\'", "'");
59 | //Unescape "
60 | rawLyrics = rawLyrics.replaceAll("\\\\\\\\\\\\\"", "\"");
61 | return rawLyrics;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/genius/SongSearch.java:
--------------------------------------------------------------------------------
1 | package genius;
2 |
3 | import com.sun.jndi.toolkit.url.Uri;
4 | import core.GLA;
5 | import jdk.nashorn.internal.runtime.regexp.joni.exception.SyntaxException;
6 | import org.json.JSONArray;
7 | import org.json.JSONObject;
8 |
9 | import java.io.IOException;
10 | import java.net.*;
11 | import java.nio.charset.StandardCharsets;
12 | import java.util.LinkedList;
13 |
14 | public class SongSearch {
15 |
16 | private final GLA gla;
17 | private int status;
18 | private int nextPage;
19 | private LinkedList hits = new LinkedList<>();
20 |
21 | public SongSearch(GLA gla, String query) throws IOException {
22 | this.gla = gla;
23 | query = URLEncoder.encode(query, "UTF-8" /* As suggested by User DimitrisStaratzis, for consistent encoding on all devices*/);
24 | try {
25 | URI uri = new URI("https://genius.com/api/search/song?page=1&q=" + query);
26 | request(uri);
27 | } catch (URISyntaxException e) {
28 | e.printStackTrace();
29 | }
30 | }
31 |
32 | public SongSearch(GLA gla, String query, int page) throws IOException {
33 | this.gla = gla;
34 | query = URLEncoder.encode(query, "UTF-8");
35 | try {
36 | URI uri = new URI("https://genius.com/api/search/song?page=" + page + "&q=" + query);
37 | request(uri);
38 | } catch (URISyntaxException e) {
39 | e.printStackTrace();
40 | }
41 | }
42 |
43 | private void request(URI uri) throws IOException {
44 | try {
45 | HttpURLConnection con = this.gla.getHttpManager().getConnection(uri.toURL());
46 | String result = this.gla.getHttpManager().executeGet(con);
47 | parse(new JSONObject(result));
48 | } catch (MalformedURLException e) {
49 | throw new InternalError();
50 | }
51 | }
52 |
53 | private void parse(JSONObject jRoot) {
54 | this.status = jRoot.getJSONObject("meta").getInt("status");
55 | JSONObject response = jRoot.getJSONObject("response");
56 | if (!response.isNull("next_page")) {
57 | this.nextPage = response.getInt("next_page");
58 | }
59 | JSONObject section = response.getJSONArray("sections").getJSONObject(0);
60 | JSONArray hits = section.getJSONArray("hits");
61 | for (int i = 0; i < hits.length(); i++) {
62 | JSONObject hitRoot = hits.getJSONObject(i).getJSONObject("result");
63 | this.hits.add(new Hit(hitRoot));
64 | }
65 | }
66 |
67 | public GLA getGla() {
68 | return gla;
69 | }
70 |
71 | public int getStatus() {
72 | return status;
73 | }
74 |
75 | public int getNextPage() {
76 | return nextPage;
77 | }
78 |
79 | public LinkedList getHits() {
80 | return hits;
81 | }
82 |
83 | public class Hit {
84 |
85 | private long id;
86 | private String title;
87 | private String titleWithFeatured;
88 | private String url;
89 | private String imageUrl;
90 | private String thumbnailUrl;
91 | private Artist artist;
92 |
93 | public Hit(JSONObject jRoot) {
94 | this.id = jRoot.getLong("id");
95 | this.title = jRoot.getString("title");
96 | this.titleWithFeatured = jRoot.getString("title_with_featured");
97 | this.url = jRoot.getString("url");
98 | this.imageUrl = jRoot.getString("header_image_url");
99 | this.thumbnailUrl = jRoot.getString("song_art_image_thumbnail_url");
100 | this.artist = new Artist(jRoot.getJSONObject("primary_artist"));
101 | }
102 |
103 | public long getId() {
104 | return id;
105 | }
106 |
107 | public String getTitle() {
108 | return title;
109 | }
110 |
111 | public String getTitleWithFeatured() {
112 | return titleWithFeatured;
113 | }
114 |
115 | public String getUrl() {
116 | return url;
117 | }
118 |
119 | public String getImageUrl() {
120 | return imageUrl;
121 | }
122 |
123 | public String getThumbnailUrl() {
124 | return thumbnailUrl;
125 | }
126 |
127 | public Artist getArtist() {
128 | return this.artist;
129 | }
130 |
131 | public String fetchLyrics() {
132 | return new LyricsParser(SongSearch.this.gla).get(this.id + "");
133 | }
134 |
135 | }
136 |
137 | public class Artist {
138 |
139 | private long id;
140 | private String imageUrl;
141 | private String name;
142 | private String slug;
143 | private String url;
144 |
145 | public Artist(JSONObject jRoot) {
146 | this.id = jRoot.getLong("id");
147 | this.imageUrl = jRoot.getString("image_url");
148 | this.name = jRoot.getString("name");
149 | this.slug = jRoot.getString("slug");
150 | this.url = jRoot.getString("url");
151 | }
152 |
153 | public long getId() {
154 | return id;
155 | }
156 |
157 | public String getImageUrl() {
158 | return imageUrl;
159 | }
160 |
161 | public String getName() {
162 | return name;
163 | }
164 |
165 | public String getSlug() {
166 | return slug;
167 | }
168 |
169 | public String getUrl() {
170 | return url;
171 | }
172 |
173 | }
174 |
175 | }
176 |
--------------------------------------------------------------------------------