Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added LavaSrc fields for yandex music tracks. Fixed getting links in … #195

Merged
merged 4 commits into from
May 14, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -7,8 +7,8 @@

public class YandexMusicAudioPlaylist extends ExtendedAudioPlaylist {

public YandexMusicAudioPlaylist(String name, List<AudioTrack> tracks, ExtendedAudioPlaylist.Type type, String identifier, String artworkURL, String author) {
super(name, tracks, type, identifier, artworkURL, author, null);
public YandexMusicAudioPlaylist(String name, List<AudioTrack> tracks, ExtendedAudioPlaylist.Type type, String url, String artworkURL, String author, Integer totalTracks) {
super(name, tracks, type, url, artworkURL, author, totalTracks);
}

}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.github.topi314.lavasrc.yandexmusic;

import com.github.topi314.lavasrc.ExtendedAudioTrack;
import com.sedmelluq.discord.lavaplayer.container.mp3.Mp3AudioTrack;
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManager;
import com.sedmelluq.discord.lavaplayer.tools.io.PersistentHttpStream;
import com.sedmelluq.discord.lavaplayer.track.AudioTrack;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo;
import com.sedmelluq.discord.lavaplayer.track.DelegatedAudioTrack;
import com.sedmelluq.discord.lavaplayer.track.playback.LocalAudioTrackExecutor;
import org.jsoup.Jsoup;
import org.jsoup.parser.Parser;
@@ -16,12 +16,17 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class YandexMusicAudioTrack extends DelegatedAudioTrack {
public class YandexMusicAudioTrack extends ExtendedAudioTrack {

private final YandexMusicSourceManager sourceManager;

public YandexMusicAudioTrack(AudioTrackInfo trackInfo, YandexMusicSourceManager sourceManager) {
super(trackInfo);
super(trackInfo, null, null, null, null, null, false);
this.sourceManager = sourceManager;
}

public YandexMusicAudioTrack(AudioTrackInfo trackInfo, String albumName, String albumUrl, String artistUrl, String artistArtworkUrl, YandexMusicSourceManager sourceManager) {
super(trackInfo, albumName, albumUrl, artistUrl, artistArtworkUrl, null, false);
this.sourceManager = sourceManager;
}

Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
package com.github.topi314.lavasrc.yandexmusic;

import com.github.topi314.lavasrc.ExtendedAudioPlaylist;
import com.github.topi314.lavasrc.ExtendedAudioSourceManager;
import com.github.topi314.lavasrc.LavaSrcTools;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerManager;
import com.sedmelluq.discord.lavaplayer.source.AudioSourceManager;
import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser;
import com.sedmelluq.discord.lavaplayer.tools.io.HttpClientTools;
import com.sedmelluq.discord.lavaplayer.tools.io.HttpConfigurable;
@@ -17,7 +17,6 @@
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;
@@ -28,7 +27,7 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;

public class YandexMusicSourceManager implements AudioSourceManager, HttpConfigurable {
public class YandexMusicSourceManager extends ExtendedAudioSourceManager implements HttpConfigurable {
public static final Pattern URL_PATTERN = Pattern.compile("(https?://)?music\\.yandex\\.(ru|com)/(?<type1>artist|album|track)/(?<identifier>[0-9]+)(/(?<type2>track)/(?<identifier2>[0-9]+))?/?");
public static final Pattern URL_PLAYLIST_PATTERN = Pattern.compile("(https?://)?music\\.yandex\\.(ru|com)/users/(?<identifier>[0-9A-Za-z@.-]+)/playlists/(?<identifier2>[0-9]+)/?");
public static final String SEARCH_PREFIX = "ymsearch:";
@@ -122,7 +121,15 @@ private AudioItem getAlbum(String id) throws IOException {
}
var coverUri = json.get("result").get("coverUri").text();
var author = json.get("result").get("artists").values().get(0).get("name").text();
return new YandexMusicAudioPlaylist(json.get("result").get("title").text(), tracks, ExtendedAudioPlaylist.Type.ALBUM, json.get("result").get("url").text(), this.formatCoverUri(coverUri), author);
return new YandexMusicAudioPlaylist(
json.get("result").get("title").text(),
tracks,
ExtendedAudioPlaylist.Type.ALBUM,
"https://music.yandex.ru/album/" + id,
this.formatCoverUri(coverUri),
author,
tracks.size()
);
}

private AudioItem getTrack(String id) throws IOException {
@@ -144,10 +151,27 @@ private AudioItem getArtist(String id) throws IOException {
return AudioReference.NO_TRACK;
}

var artistJson = this.getJson(PUBLIC_API_BASE + "/artists/" + id);
var coverUri = json.get("result").get("coverUri").text();
var author = artistJson.get("result").get("artist").get("name").text();
return new YandexMusicAudioPlaylist(author + "'s Top Tracks", tracks, ExtendedAudioPlaylist.Type.ARTIST, json.get("result").get("url").text(), this.formatCoverUri(coverUri), author);
var artistJsonResponse = this.getJson(PUBLIC_API_BASE + "/artists/" + id);
var artistJson = artistJsonResponse.get("result").get("artist");
var author = artistJson.get("name").text();

String coverUri = null;

if (!artistJson.get("ogImage").isNull()) {
coverUri = this.formatCoverUri(artistJson.get("ogImage").text());
} else if (!artistJson.get("cover").isNull()) {
coverUri = this.formatCoverUri(artistJson.get("cover").get("uri").text());
}

return new YandexMusicAudioPlaylist(
author + "'s Top Tracks",
tracks,
ExtendedAudioPlaylist.Type.ARTIST,
"https://music.yandex.ru/artist/" + id,
coverUri,
author,
tracks.size()
);
}

private AudioItem getPlaylist(String userString, String id) throws IOException {
@@ -169,10 +193,25 @@ private AudioItem getPlaylist(String userString, String id) throws IOException {
if (tracks.isEmpty()) {
return AudioReference.NO_TRACK;
}
var playlistTitle = json.get("result").get("kind").text().equals("3") ? "Liked songs" : json.get("result").get("title").text();
String playlistTitle;
if (json.get("result").get("kind").text().equals("3")) {
var ownerJson = json.get("result").get("owner");
var ownerName = ownerJson.get("name").isNull() ? ownerJson.get("login").text() : ownerJson.get("name").text();
playlistTitle = ownerName + "'s liked songs";
} else {
playlistTitle = json.get("result").get("title").text();
}
var coverUri = json.get("result").get("cover").get("uri").text();
var author = json.get("result").get("owner").get("name").text();
return new YandexMusicAudioPlaylist(playlistTitle, tracks, ExtendedAudioPlaylist.Type.PLAYLIST, json.get("result").get("url").text(), this.formatCoverUri(coverUri), author);
return new YandexMusicAudioPlaylist(
playlistTitle,
tracks,
ExtendedAudioPlaylist.Type.PLAYLIST,
"https://music.yandex.ru/users/" + userString + "/playlists/" + id,
this.formatCoverUri(coverUri),
author,
tracks.size()
);
}

private List<JsonBrowser> getTracks(String trackIds) throws IOException {
@@ -181,8 +220,8 @@ private List<JsonBrowser> getTracks(String trackIds) throws IOException {

private String getTrackIds(List<JsonBrowser> tracksToParse) {
return tracksToParse.stream()
.map(node -> node.get("id").text())
.collect(Collectors.joining(","));
.map(node -> node.get("id").text())
.collect(Collectors.joining(","));
}

public JsonBrowser getJson(String uri) throws IOException {
@@ -217,18 +256,40 @@ private AudioTrack parseTrack(JsonBrowser json) {
var id = json.get("id").text();
var artist = parseArtist(json);
var coverUri = json.get("coverUri").text();

String albumName = null;
String albumUrl = null;
if (!json.get("albums").values().isEmpty()) {
var album = json.get("albums").values().get(0);
albumName = album.get("title").text();
albumUrl = "https://music.yandex.ru/album/" + album.get("id").text();
}

String artistUrl = null;
String artistArtworkUrl = null;
if (!json.get("artists").values().isEmpty()) {
var firstArtist = json.get("artists").values().get(0);
artistUrl = "https://music.yandex.ru/artist/" + firstArtist.get("id").text();
if (!firstArtist.get("cover").isNull()) {
artistArtworkUrl = this.formatCoverUri(firstArtist.get("cover").get("uri").text());
}
}
return new YandexMusicAudioTrack(
new AudioTrackInfo(
json.get("title").text(),
artist,
json.get("durationMs").as(Long.class),
id,
false,
"https://music.yandex.ru/track/" + id,
this.formatCoverUri(coverUri),
null
),
this
new AudioTrackInfo(
json.get("title").text(),
artist,
json.get("durationMs").as(Long.class),
id,
false,
"https://music.yandex.ru/track/" + id,
this.formatCoverUri(coverUri),
null
),
albumName,
albumUrl,
artistUrl,
artistArtworkUrl,
this
);
}

@@ -250,26 +311,24 @@ private String parseArtist(JsonBrowser json) {

private String extractArtists(JsonBrowser artistNode) {
return artistNode.values().stream()
.map(node -> node.get("name").text())
.collect(Collectors.joining(", "));
.map(node -> node.get("name").text())
.collect(Collectors.joining(", "));
}

private String formatCoverUri(String coverUri) {
return coverUri != null ? "https://" + coverUri.replace("%%", "400x400") : null;
}

@Override
public boolean isTrackEncodable(AudioTrack track) {
return true;
}

@Override
public void encodeTrack(AudioTrack track, DataOutput output) {
}

@Override
public AudioTrack decodeTrack(AudioTrackInfo trackInfo, DataInput input) {
return new YandexMusicAudioTrack(trackInfo, this);
public AudioTrack decodeTrack(AudioTrackInfo trackInfo, DataInput input) throws IOException {
var extendedAudioTrackInfo = super.decodeTrack(input);
return new YandexMusicAudioTrack(trackInfo,
extendedAudioTrackInfo.albumName,
extendedAudioTrackInfo.albumUrl,
extendedAudioTrackInfo.artistUrl,
extendedAudioTrackInfo.artistArtworkUrl,
this
);
}

@Override