├── .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/v/LowLevelSubmarine/GeniusLyricsAPI.svg)](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 | --------------------------------------------------------------------------------