diff --git a/main/src/main/java/com/github/appujet/jiosaavn/ExtendedAudioPlaylist.java b/main/src/main/java/com/github/appujet/jiosaavn/ExtendedAudioPlaylist.java index 112c723..60f69a7 100644 --- a/main/src/main/java/com/github/appujet/jiosaavn/ExtendedAudioPlaylist.java +++ b/main/src/main/java/com/github/appujet/jiosaavn/ExtendedAudioPlaylist.java @@ -3,7 +3,7 @@ import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.BasicAudioPlaylist; import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; + import java.util.List; @@ -11,14 +11,10 @@ public class ExtendedAudioPlaylist extends BasicAudioPlaylist { @NotNull protected final Type type; - @Nullable - protected final String url; - @Nullable - protected final String artworkURL; - @Nullable - protected final String author; - @Nullable - protected final Integer totalTracks; + protected final @NotNull String url; + protected final @NotNull String artworkURL; + protected final @NotNull String author; + protected final @NotNull Integer totalTracks; public ExtendedAudioPlaylist(String name, List tracks, @NotNull Type type, @NotNull String url, @NotNull String artworkURL, @NotNull String author, @NotNull Integer totalTracks) { super(name, tracks, null, false); @@ -34,23 +30,19 @@ public Type getType() { return type; } - @Nullable - public String getUrl() { + public @NotNull String getUrl() { return this.url; } - @Nullable - public String getArtworkURL() { + public @NotNull String getArtworkURL() { return this.artworkURL; } - @Nullable - public String getAuthor() { + public @NotNull String getAuthor() { return this.author; } - @Nullable - public Integer getTotalTracks() { + public @NotNull Integer getTotalTracks() { return this.totalTracks; } diff --git a/main/src/main/java/com/github/appujet/jiosaavn/ExtendedAudioSourceManager.java b/main/src/main/java/com/github/appujet/jiosaavn/ExtendedAudioSourceManager.java index ab3ffc4..ca0c322 100644 --- a/main/src/main/java/com/github/appujet/jiosaavn/ExtendedAudioSourceManager.java +++ b/main/src/main/java/com/github/appujet/jiosaavn/ExtendedAudioSourceManager.java @@ -2,26 +2,40 @@ import com.sedmelluq.discord.lavaplayer.source.AudioSourceManager; import com.sedmelluq.discord.lavaplayer.tools.ExceptionTools; +import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser; import com.sedmelluq.discord.lavaplayer.tools.http.HttpContextFilter; import com.sedmelluq.discord.lavaplayer.tools.io.HttpClientTools; import com.sedmelluq.discord.lavaplayer.tools.io.HttpConfigurable; import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterfaceManager; + +import org.apache.commons.io.IOUtils; import org.apache.http.HttpResponse; import org.apache.http.client.CookieStore; import org.apache.http.client.config.RequestConfig; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpUriRequest; import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.client.utils.URIBuilder; import org.apache.http.impl.client.BasicCookieStore; import org.apache.http.impl.client.HttpClientBuilder; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import java.util.function.Consumer; import java.util.function.Function; public abstract class ExtendedAudioSourceManager implements AudioSourceManager, HttpConfigurable { + public static final Logger log = LoggerFactory.getLogger(ExtendedAudioSourceManager.class); protected final HttpInterfaceManager httpInterfaceManager; - + public static String BASE_API = null; + private static final String BASE_API_URL = "https://www.jiosaavn.com/api.php?"; public ExtendedAudioSourceManager () { this(true); } @@ -42,6 +56,48 @@ public HttpInterface getHttpInterface() { return httpInterfaceManager.getInterface(); } + public JsonBrowser fetchJson(String pageURl) { + final HttpGet httpGet = new HttpGet(BASE_API + pageURl); + try (final CloseableHttpResponse response = this.getHttpInterface().execute(httpGet)) { + final String content = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + return JsonBrowser.parse(content); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public JsonBrowser fetchJson(String endpoint, String[] params, String context) { + try { + URI uri = getUri(endpoint, params, context); + final HttpGet httpGet = new HttpGet(uri); + try (final CloseableHttpResponse response = this.getHttpInterface().execute(httpGet)) { + final String content = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); + //log.info("Response from API: {}", content); + return JsonBrowser.parse(content); + } + } catch (Exception e) { + throw new RuntimeException("Error fetching JSON from JioSaavn API", e); + } + } + + private static URI getUri(String endpoint, String[] params, String context) throws URISyntaxException { + URIBuilder uriBuilder = new URIBuilder(BASE_API_URL); + uriBuilder.addParameter("__call", endpoint); + uriBuilder.addParameter("_format", "json"); + uriBuilder.addParameter("_marker", "0"); + uriBuilder.addParameter("api_version", "4"); + uriBuilder.addParameter("ctx", context != null ? context : "web6dot0"); + + if (params != null) { + for (int i = 0; i < params.length; i += 2) { + if (i + 1 < params.length) { + uriBuilder.addParameter(params[i], params[i + 1]); + } + } + } + return uriBuilder.build(); + } + @Override public void shutdown() { ExceptionTools.closeWithWarnings(httpInterfaceManager); diff --git a/main/src/main/java/com/github/appujet/jiosaavn/ExtendedAudioTrack.java b/main/src/main/java/com/github/appujet/jiosaavn/ExtendedAudioTrack.java index 72b86cf..c9c4334 100644 --- a/main/src/main/java/com/github/appujet/jiosaavn/ExtendedAudioTrack.java +++ b/main/src/main/java/com/github/appujet/jiosaavn/ExtendedAudioTrack.java @@ -37,14 +37,19 @@ public void process(LocalAudioTrackExecutor executor) throws Exception { protected void loadStream(LocalAudioTrackExecutor localExecutor, HttpInterface httpInterface) throws Exception { final String trackUrl = getPlaybackUrl(); - try (PersistentHttpStream stream = new PersistentHttpStream(httpInterface, new URI(trackUrl), this.getTrackDuration())) { + try (final var stream = this + .wrapStream(new PersistentHttpStream(httpInterface, new URI(trackUrl), this.getTrackDuration()))) { processDelegate(createAudioTrack(this.trackInfo, stream), localExecutor); } catch (Exception e) { log.error("Failed to load track from URL: {}", trackUrl, e); throw e; } } - + + protected SeekableInputStream wrapStream(SeekableInputStream stream) { + return stream; + } + protected InternalAudioTrack createAudioTrack(AudioTrackInfo trackInfo, SeekableInputStream stream) { return new MpegAudioTrack(trackInfo, stream); } diff --git a/main/src/main/java/com/github/appujet/jiosaavn/Utils.java b/main/src/main/java/com/github/appujet/jiosaavn/Utils.java index 8b868ef..f0f2631 100644 --- a/main/src/main/java/com/github/appujet/jiosaavn/Utils.java +++ b/main/src/main/java/com/github/appujet/jiosaavn/Utils.java @@ -5,11 +5,85 @@ import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; +import javax.crypto.Cipher; +import javax.crypto.spec.SecretKeySpec; import java.io.IOException; import java.nio.charset.StandardCharsets; +import java.security.Key; +import java.util.Base64; +import java.util.regex.Matcher; +import java.util.regex.Pattern; public class Utils { + private static final String KEY = "38346591"; + private static final String ALGORITHM = "DES/ECB/NoPadding"; + private static final String[] QUALITIES = { "_320", "_160", "_96", "_48", "_12" }; + + // Regular expressions for extracting tokens + private static final Pattern SONG_PATTERN = Pattern.compile("jiosaavn\\.com/song/[^/]+/([^/]+)$"); + private static final Pattern ARTIST_PATTERN = Pattern.compile("jiosaavn\\.com/artist/[^/]+/([^/]+)$"); + private static final Pattern ALBUM_PATTERN = Pattern.compile("jiosaavn\\.com/album/[^/]+/([^/]+)$"); + private static final Pattern PLAYLIST_PATTERN = Pattern.compile("jiosaavn\\.com/(?:featured|s/playlist)/[^/]+/([^/]+)$"); + + /** + * Extracts a song token from a JioSaavn song URL. + * + * @param url The song URL. + * @return The song token or null if not found. + */ + public static String extractSongToken(String url) { + return extractToken(url, SONG_PATTERN); + } + + /** + * Extracts an artist token from a JioSaavn artist URL. + * + * @param url The artist URL. + * @return The artist token or null if not found. + */ + public static String extractArtistToken(String url) { + return extractToken(url, ARTIST_PATTERN); + } + + /** + * Extracts an album token from a JioSaavn album URL. + * + * @param url The album URL. + * @return The album token or null if not found. + */ + public static String extractAlbumToken(String url) { + return extractToken(url, ALBUM_PATTERN); + } + + /** + * Extracts a playlist token from a JioSaavn playlist URL. + * + * @param url The playlist URL. + * @return The playlist token or null if not found. + */ + public static String extractPlaylistToken(String url) { + return extractToken(url, PLAYLIST_PATTERN); + } + + /** + * Extracts a token using the provided pattern. + * + * @param url The URL to extract the token from. + * @param pattern The regex pattern to match the token. + * @return The token if found, or null if not. + */ + private static String extractToken(String url, Pattern pattern) { + if (url == null || url.isEmpty()) { + return null; + } + + Matcher matcher = pattern.matcher(url); + if (matcher.find()) { + return matcher.group(1); // Return the first captured group + } + return null; + } public static JsonBrowser fetchJson(String pageURl, ExtendedAudioSourceManager sourceManager) { final HttpGet httpGet = new HttpGet(pageURl); try (final CloseableHttpResponse response = sourceManager.getHttpInterface().execute(httpGet)) { @@ -19,4 +93,39 @@ public static JsonBrowser fetchJson(String pageURl, ExtendedAudioSourceManager s throw new RuntimeException(e); } } -} \ No newline at end of file + /** + * Generates a download link by decrypting the encrypted media URL. + * + * @param encryptedMediaUrl The encrypted media URL. + * @return The download link or null if not valid. + */ + public static String getDownloadLink(String encryptedMediaUrl) { + if (encryptedMediaUrl == null || encryptedMediaUrl.isEmpty()) + return null; + + try { + byte[] encryptedBytes = Base64.getDecoder().decode(encryptedMediaUrl); + Key key = new SecretKeySpec(KEY.getBytes(), "DES"); + Cipher cipher = Cipher.getInstance(ALGORITHM); + cipher.init(Cipher.DECRYPT_MODE, key); + + byte[] decryptedBytes = cipher.doFinal(encryptedBytes); + String decryptedLink = new String(decryptedBytes).trim(); + System.out.println("Decrypted Link: " + decryptedLink); + for (String quality : QUALITIES) { + String downloadUrl = decryptedLink.replace("_96", quality); + if (isValidUrl(downloadUrl)) { + return downloadUrl; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + // Helper method to check if a URL is valid + private static boolean isValidUrl(String url) { + return url.startsWith("https://aac.saavncdn.com/") && url.endsWith(".mp4"); + } +} diff --git a/main/src/main/java/com/github/appujet/jiosaavn/source/JioSaavnAudioSourceManager.java b/main/src/main/java/com/github/appujet/jiosaavn/source/JioSaavnAudioSourceManager.java index 79aac82..f80dcec 100644 --- a/main/src/main/java/com/github/appujet/jiosaavn/source/JioSaavnAudioSourceManager.java +++ b/main/src/main/java/com/github/appujet/jiosaavn/source/JioSaavnAudioSourceManager.java @@ -2,40 +2,28 @@ import com.github.appujet.jiosaavn.ExtendedAudioPlaylist; import com.github.appujet.jiosaavn.ExtendedAudioSourceManager; +import com.github.appujet.jiosaavn.Utils; import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; -import com.sedmelluq.discord.lavaplayer.tools.DataFormatTools; import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser; import com.sedmelluq.discord.lavaplayer.track.*; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import java.io.DataInput; import java.io.DataOutput; import java.io.IOException; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.regex.Pattern; import java.util.stream.Collectors; public class JioSaavnAudioSourceManager extends ExtendedAudioSourceManager { - private static final Pattern JIOSAAVN_REGEX = Pattern.compile("(https?://)(www\\.)?jiosaavn\\.com/(song|album|featured|artist|s/playlist)/([a-zA-Z0-9-_]+)(/([a-zA-Z0-9-_]+))?"); - private static final Logger log = LoggerFactory.getLogger(JioSaavnAudioSourceManager.class); - public static String BASE_API = null; + private static final Pattern JIOSAAVN_REGEX = Pattern.compile( + "(https?://)(www\\.)?jiosaavn\\.com/(song|album|featured|artist|s/playlist)/([a-zA-Z0-9-_]+)(/([a-zA-Z0-9-_]+))?"); private final int playlistTrackLimit; private final int recommendationsTrackLimit; public static final String SEARCH_PREFIX = "jssearch:"; public static final String RECOMMENDATIONS_PREFIX = "jsrec:"; - public JioSaavnAudioSourceManager(String apiURL, int playlistTrackLimit, int recommendationsTrackLimit) { - if (apiURL == null || apiURL.isEmpty()) { - throw new IllegalArgumentException("API URL must be provided"); - } - BASE_API = apiURL; + public JioSaavnAudioSourceManager(int playlistTrackLimit, int recommendationsTrackLimit) { this.playlistTrackLimit = Math.abs(playlistTrackLimit); this.recommendationsTrackLimit = Math.abs(recommendationsTrackLimit); @@ -95,18 +83,13 @@ public AudioTrack decodeTrack(AudioTrackInfo audioTrackInfo, DataInput dataInput } private AudioItem getSearchResult(String query) throws IOException { - final JsonBrowser json = this.fetchJson("/search/songs?query=" + URLEncoder.encode(query, StandardCharsets.UTF_8) + "&limit=50"); - - if (json.isNull() || json.get("data").isNull()) { - return AudioReference.NO_TRACK; - } - final JsonBrowser data = json.get("data"); + final JsonBrowser json = this.fetchJson("search.getResults", + new String[] { "q", query, "cc", "in", "includeMetaTags", "1", "n", "100" }, null); - if (data.get("results").isNull()) { + if (json.isNull()) { return AudioReference.NO_TRACK; } - - final JsonBrowser songs = data.get("results"); + final JsonBrowser songs = json.get("results"); if (songs.isNull() || !songs.isList()) { return AudioReference.NO_TRACK; @@ -120,85 +103,96 @@ private AudioItem getSearchResult(String query) throws IOException { "Search results for: " + query, tracks, null, - true - ); + true); } - private AudioItem getTrack(String identifier) throws IOException { - final JsonBrowser json = this.fetchJson("/songs?link=" + identifier); - if (json.isNull() || json.get("data").isNull()) { + private AudioItem getTrack(String url) throws IOException { + String token = Utils.extractSongToken(url); + final JsonBrowser json = this.fetchJson("webapi.get", new String[] { "token", token, "type", "song" }, null); + if (json.isNull() || json.get("songs").isNull() || json.get("songs").values().isEmpty()) { return AudioReference.NO_TRACK; } - final JsonBrowser data = json.get("data").index(0); + final JsonBrowser data = json.get("songs").index(0); return this.buildTrack(data); } - public AudioItem getAlbum(String identifier) { + public AudioItem getAlbum(String url) { + String token = Utils.extractAlbumToken(url); + final JsonBrowser json = this.fetchJson("webapi.get", new String[] {"type", "album", "token", token}, null); - final JsonBrowser json = this.fetchJson("/albums?link=" + identifier); - - if (json.isNull() || json.get("data").isNull()) { + if (json.isNull() || json.get("list").isNull()) { return AudioReference.NO_TRACK; } - final JsonBrowser data = json.get("data"); - if (data.get("songs").isNull()) { + final JsonBrowser data = json.get("list"); + if (data.isNull() || !data.isList()) { return AudioReference.NO_TRACK; } - return new JioSaavnAudioPlaylist( - data.get("name").text(), - this.buildTracks(data.get("songs")), - ExtendedAudioPlaylist.Type.ALBUM, - data.get("url").text(), - this.parseImage(data.get("image")), - this.parseArtist(data), - (int) data.get("songCount").asLong(0) - ); + final String albumTitle = json.get("title").text(); + final List tracks = data.values().stream() + .map(this::buildTrack) + .collect(Collectors.toList()); + final String albumUrl = json.get("perma_url").text(); + final String artwork = this.parseImage(json.get("image")); + final String artist = parseArtist(json); + final int trackCount = (int) json.get("list_count").asLong(0); + return new JioSaavnAudioPlaylist(albumTitle, tracks, ExtendedAudioPlaylist.Type.ALBUM, albumUrl, artwork, artist, trackCount); } - public AudioItem getPlaylist(String identifier) { - final JsonBrowser json = this.fetchJson("/playlists?link=" + identifier + "&limit=" + playlistTrackLimit); - if (json.isNull() || json.get("data").isNull()) { + private AudioItem getArtist(String url) { + String token = Utils.extractArtistToken(url); + final JsonBrowser json = this.fetchJson("webapi.get", new String[] {"type", "artist", "token", token}, null); + if (json.isNull() || json.get("topSongs").isNull()) { return AudioReference.NO_TRACK; } - final JsonBrowser data = json.get("data"); - if (data.get("songs").isNull()) { + final JsonBrowser data = json.get("topSongs"); + if (data.isNull() || !data.isList()) { return AudioReference.NO_TRACK; } + final String artistName = json.get("name").text(); + final List tracks = data.values().stream() + .map(this::buildTrack) + .collect(Collectors.toList()); + + final String artwork = this.parseImage(json.get("image")); + final String artist = parseArtist(json); + return new JioSaavnAudioPlaylist( - data.get("name").text(), - this.buildTracks(data.get("songs")), - ExtendedAudioPlaylist.Type.PLAYLIST, - data.get("url").text(), - this.parseImage(data.get("image")), - this.parseArtist(data), - (int) data.get("songCount").asLong(0) - ); + artistName, + tracks, + ExtendedAudioPlaylist.Type.ARTIST, + url, + artwork, + artist, + null); + } - private AudioItem getArtist(String identifier) { - final JsonBrowser json = this.fetchJson("/artists?link=" + identifier + "&songCount=50"); - if (json.isNull() || json.get("data").isNull()) { + public AudioItem getPlaylist(String identifier) { + String token = Utils.extractPlaylistToken(identifier); + final JsonBrowser json = this.fetchJson("webapi.get", new String[] {"type", "playlist", "token", token, "n", String.valueOf(playlistTrackLimit)}, null); + if (json.isNull() || json.get("list").isNull()) { return AudioReference.NO_TRACK; } - final JsonBrowser data = json.get("data"); - if (data.get("topSongs").isNull()) { + final JsonBrowser data = json.get("list"); + if (data.isNull() || !data.isList()) { return AudioReference.NO_TRACK; } - return new JioSaavnAudioPlaylist( - data.get("name").text(), - this.buildTracks(data.get("topSongs")), - ExtendedAudioPlaylist.Type.ARTIST, - data.get("url").text(), - this.parseImage(data.get("image")), - data.get("name").text(), - null - ); + final String playlistTitle = json.get("title").text(); + final List tracks = data.values().stream() + .map(this::buildTrack) + .collect(Collectors.toList()); + final String playlistUrl = json.get("perma_url").text(); + final String artwork = this.parseImage(json.get("image")); + final String artist = parseArtist(json); + final int trackCount = (int) json.get("list_count").asLong(0); + + return new JioSaavnAudioPlaylist(playlistTitle, tracks, ExtendedAudioPlaylist.Type.PLAYLIST, playlistUrl, artwork, artist, trackCount); } public AudioItem getRecommendations(String identifier) { - final JsonBrowser json = this.fetchJson("/songs/" + identifier + "/suggestions?limit=" + recommendationsTrackLimit); + final JsonBrowser json = this.fetchJson("webradio.getSong", new String[] {"stationid", identifier, "k", String.valueOf(recommendationsTrackLimit)}, "android"); if (json.isNull() || json.get("data").isNull()) { return AudioReference.NO_TRACK; } @@ -217,18 +211,7 @@ public AudioItem getRecommendations(String identifier) { null, null, null, - null - ); - } - - public JsonBrowser fetchJson(String pageURl) { - final HttpGet httpGet = new HttpGet(BASE_API + pageURl); - try (final CloseableHttpResponse response = this.getHttpInterface().execute(httpGet)) { - final String content = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); - return JsonBrowser.parse(content); - } catch (IOException e) { - throw new RuntimeException(e); - } + null); } private List buildTracks(JsonBrowser json) { @@ -246,12 +229,14 @@ private AudioTrack buildTrack(JsonBrowser data) { if (data.isNull()) { return null; } - final String title = cleanString(data.get("name").text()); + + final String title = data.get("title").text(); final String id = data.get("id").text(); final String artwork = this.parseImage(data.get("image")); - final long duration = data.get("duration").asLong(1) * 1000; - final String url = data.get("url").text(); - var artist = cleanString(this.parseArtist(data)); + final long duration = data.get("more_info").get("duration").asLong(1) * 1000; + final String url = data.get("perma_url").text(); + final String artist = parseArtist(data); + return new JioSaavnAudioTrack( new AudioTrackInfo( title, @@ -261,49 +246,38 @@ private AudioTrack buildTrack(JsonBrowser data) { false, url, artwork, - null - ), - this - ); + null), + this); } private String parseArtist(JsonBrowser json) { if (json.isNull()) { return null; } - var artists = json.get("artists").get("primary"); - if (artists.isNull()) { + JsonBrowser artistMap = json.get("more_info").get("artistMap"); + if (artistMap.isNull()) { return "Unknown"; } - return artists.values().stream() - .map(name -> name.get("name").text()) - .findFirst() - .orElse("Unknown"); - } - - private String cleanString(String text) { - if (text == null) { - return null; + JsonBrowser primaryArtists = artistMap.get("primary_artists"); + if (primaryArtists.isNull()) { + return "Unknown"; } - return text.replace("\"", "") - .replace(""", "") - .replace("&", ""); + if (primaryArtists.isList()) { + if (!primaryArtists.values().isEmpty()) { + return primaryArtists.values().iterator().next().get("name").text(); + } + } + return "Unknown"; } private String parseImage(JsonBrowser json) { if (json.isNull()) { return null; } - var image = json.index(2); - if (image.isNull()) { - image = json.index(1); - } - if (image.isNull()) { - image = json.index(0); - } - if (image.isNull()) { - return null; + String imageUrl = json.text(); + if (imageUrl.contains("150x150")) { + imageUrl = imageUrl.replace("150x150", "500x500"); } - return image.get("url").text(); + return imageUrl; } } diff --git a/main/src/main/java/com/github/appujet/jiosaavn/source/JioSaavnAudioTrack.java b/main/src/main/java/com/github/appujet/jiosaavn/source/JioSaavnAudioTrack.java index 145676b..b9786cf 100644 --- a/main/src/main/java/com/github/appujet/jiosaavn/source/JioSaavnAudioTrack.java +++ b/main/src/main/java/com/github/appujet/jiosaavn/source/JioSaavnAudioTrack.java @@ -1,15 +1,12 @@ package com.github.appujet.jiosaavn.source; - import com.github.appujet.jiosaavn.ExtendedAudioSourceManager; - import com.github.appujet.jiosaavn.ExtendedAudioTrack; import com.github.appujet.jiosaavn.Utils; import com.sedmelluq.discord.lavaplayer.tools.Units; import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; - public class JioSaavnAudioTrack extends ExtendedAudioTrack { private final ExtendedAudioSourceManager manager; @@ -20,56 +17,25 @@ public JioSaavnAudioTrack(AudioTrackInfo trackInfo, ExtendedAudioSourceManager m @Override public String getPlaybackUrl() { - return getDownloadURL(this.trackInfo.identifier); - } + var json = manager.fetchJson("song.getDetails", new String[] { "pids", this.trackInfo.identifier }, null); - private String getDownloadURL(String identifier) { - // Fetch JSON data from the API - var json = Utils.fetchJson(JioSaavnAudioSourceManager.BASE_API + "/songs?ids=" + identifier, manager); - - // Extract the download URL information - var downloadInfoLink = json.get("data").index(0).get("downloadUrl"); - - if (downloadInfoLink.isNull()) { + if (json.isNull() || json.get("songs").values().isEmpty()) { + log.debug("Invalid JSON response or no data found for identifier: {}", this.trackInfo.identifier); return null; } - - // Retrieve all download URLs - var downloadUrls = downloadInfoLink.values(); - if (downloadUrls.isEmpty()) { + var data = json.get("songs").index(0); + var encryptedMediaUrl = data.get("more_info").get("encrypted_media_url").text(); + if (encryptedMediaUrl == null) { + log.debug("Encrypted media URL not found for identifier: {}", this.trackInfo.identifier); return null; } + var url = Utils.getDownloadLink(encryptedMediaUrl); - String downloadUrl = null; - - // Check for the desired quality in descending order - for (var url : downloadUrls) { - if (url.get("quality").text().equals("320kbps")) { - downloadUrl = url.get("url").text(); - break; - } - } - if (downloadUrl == null) { - for (var url : downloadUrls) { - if (url.get("quality").text().equals("160kbps")) { - downloadUrl = url.get("url").text(); - break; - } - } - } - if (downloadUrl == null) { - for (var url : downloadUrls) { - if (url.get("quality").text().equals("96kbps")) { - downloadUrl = url.get("url").text(); - break; - } - } - } - if (downloadUrl == null && !downloadUrls.isEmpty()) { - downloadUrl = downloadUrls.get(0).get("url").text(); + if (url == null) { + log.debug("Failed to decrypt media URL for identifier: {}", this.trackInfo.identifier); + return null; } - - return downloadUrl; + return url; } @Override diff --git a/plugin/src/main/java/com/github/appujet/plugin/JioSaavnConfig.java b/plugin/src/main/java/com/github/appujet/plugin/JioSaavnConfig.java index 9e6bb5c..6581167 100644 --- a/plugin/src/main/java/com/github/appujet/plugin/JioSaavnConfig.java +++ b/plugin/src/main/java/com/github/appujet/plugin/JioSaavnConfig.java @@ -6,16 +6,8 @@ @Component @ConfigurationProperties(prefix = "plugins.jiosaavn") public class JioSaavnConfig { - private String apiURL = null; private int playlistTrackLimit = 50; // default value for playlist track limit private int recommendationsTrackLimit = 20; // default value for recommendations - public String getApiURL() { - return apiURL; - } - - public void setApiURL(String apiURL) { - this.apiURL = apiURL; - } public int getPlaylistTrackLimit() { return playlistTrackLimit; diff --git a/plugin/src/main/java/com/github/appujet/plugin/JiosaavnPlugin.java b/plugin/src/main/java/com/github/appujet/plugin/JiosaavnPlugin.java index fc0d440..2da9698 100644 --- a/plugin/src/main/java/com/github/appujet/plugin/JiosaavnPlugin.java +++ b/plugin/src/main/java/com/github/appujet/plugin/JiosaavnPlugin.java @@ -1,6 +1,5 @@ package com.github.appujet.plugin; - import com.github.appujet.jiosaavn.source.JioSaavnAudioSourceManager; import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager; import dev.arbjerg.lavalink.api.AudioPlayerManagerConfiguration; @@ -14,28 +13,23 @@ public class JiosaavnPlugin implements AudioPlayerManagerConfiguration { private static final Logger log = LoggerFactory.getLogger(JiosaavnPlugin.class); private final JioSaavnConfig sourcesConfig; - private JioSaavnAudioSourceManager jioSaavn; + private final JioSaavnAudioSourceManager jioSaavn; public JiosaavnPlugin(JioSaavnConfig sourcesConfig) { log.info("Loaded JioSaavn plugin..."); this.sourcesConfig = sourcesConfig; - if (this.sourcesConfig.getApiURL() != null) { - this.jioSaavn = new JioSaavnAudioSourceManager( - this.sourcesConfig.getApiURL(), - this.sourcesConfig.getPlaylistTrackLimit(), + + this.jioSaavn = new JioSaavnAudioSourceManager( + this.sourcesConfig.getPlaylistTrackLimit(), this.sourcesConfig.getRecommendationsTrackLimit()); - } + } @NotNull @Override public AudioPlayerManager configure(@NotNull AudioPlayerManager manager) { - if (this.sourcesConfig.getApiURL() != null) { - log.info("Registering JioSaavn audio source manager..."); - manager.registerSourceManager(this.jioSaavn); - } else { - log.warn("JioSaavn audio source manager not registered, no API URL provided"); - } + log.info("Registering JioSaavn audio source manager..."); + manager.registerSourceManager(this.jioSaavn); return manager; } } diff --git a/plugin/src/main/java/com/github/appujet/plugin/PluginInfoModifier.java b/plugin/src/main/java/com/github/appujet/plugin/PluginInfoModifier.java index 017d340..61e40ce 100644 --- a/plugin/src/main/java/com/github/appujet/plugin/PluginInfoModifier.java +++ b/plugin/src/main/java/com/github/appujet/plugin/PluginInfoModifier.java @@ -35,7 +35,7 @@ public JsonObject modifyAudioPlaylistPluginInfo(@NotNull AudioPlaylist playlist) public JsonObject modifyAudioTrackPluginInfo(@NotNull AudioTrack track) { final String uri; - if (track instanceof ExtendedAudioTrack extendedTrack) { + if (track instanceof ExtendedAudioTrack extendedTrack) { uri = track.getIdentifier(); } else { uri = track.getInfo().uri;