From 8ac9073d0986529a8a27059ab1ff661ee7c7a1b9 Mon Sep 17 00:00:00 2001 From: Jirayu Date: Sat, 30 Nov 2024 21:49:52 +0700 Subject: [PATCH 1/7] Only apply access token to PLAYER_URL (#79) --- .../java/dev/lavalink/youtube/http/YoutubeOauth2Handler.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/common/src/main/java/dev/lavalink/youtube/http/YoutubeOauth2Handler.java b/common/src/main/java/dev/lavalink/youtube/http/YoutubeOauth2Handler.java index 340697a..ace3703 100644 --- a/common/src/main/java/dev/lavalink/youtube/http/YoutubeOauth2Handler.java +++ b/common/src/main/java/dev/lavalink/youtube/http/YoutubeOauth2Handler.java @@ -9,6 +9,7 @@ import com.sedmelluq.discord.lavaplayer.tools.io.HttpClientTools; import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterfaceManager; +import dev.lavalink.youtube.clients.skeleton.Client; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.client.methods.HttpUriRequest; @@ -258,6 +259,10 @@ public void applyToken(HttpUriRequest request) { return; } + if (!Client.PLAYER_URL.equals(request.getURI().toString())) { + return; + } + if (shouldRefreshAccessToken()) { log.debug("Access token has expired, refreshing..."); From 48abb600df9aa515a5c7e90f092e5123fe0b9851 Mon Sep 17 00:00:00 2001 From: Devoxin Date: Mon, 2 Dec 2024 20:16:26 +0000 Subject: [PATCH 2/7] Add MWeb client. --- .../dev/lavalink/youtube/clients/MWeb.java | 63 +++++++++++++++++++ .../youtube/plugin/ClientProviderV3.java | 1 + .../youtube/plugin/ClientProviderV4.java | 1 + .../youtube/clients/MWebWithThumbnail.java | 10 +++ 4 files changed, 75 insertions(+) create mode 100644 common/src/main/java/dev/lavalink/youtube/clients/MWeb.java create mode 100644 v2/src/main/java/dev/lavalink/youtube/clients/MWebWithThumbnail.java diff --git a/common/src/main/java/dev/lavalink/youtube/clients/MWeb.java b/common/src/main/java/dev/lavalink/youtube/clients/MWeb.java new file mode 100644 index 0000000..97896b6 --- /dev/null +++ b/common/src/main/java/dev/lavalink/youtube/clients/MWeb.java @@ -0,0 +1,63 @@ +package dev.lavalink.youtube.clients; + +import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser; +import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; +import org.jetbrains.annotations.NotNull; + +public class MWeb extends Web { + public static ClientConfig BASE_CONFIG = new ClientConfig() + .withApiKey("AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8") + .withClientName("MWEB") + .withClientField("clientVersion", "2.20240726.11.00"); + + public MWeb(@NotNull ClientOptions options) { + super(options); + } + + @Override + @NotNull + public ClientConfig getBaseClientConfig(@NotNull HttpInterface httpInterface) { + return BASE_CONFIG.copy(); + } + + @Override + @NotNull + protected JsonBrowser extractMixPlaylistData(@NotNull JsonBrowser json) { + return json.get("contents") + .get("singleColumnWatchNextResults") + .get("playlist") + .get("playlist"); + } + + @Override + protected String extractPlaylistName(@NotNull JsonBrowser json) { + return json.get("header") + .get("pageHeaderRenderer") + .get("pageTitle") + .text(); + } + + @Override + @NotNull + protected JsonBrowser extractPlaylistVideoList(@NotNull JsonBrowser json) { + return json.get("contents") + .get("singleColumnBrowseResultsRenderer") + .get("tabs") + .index(0) + .get("tabRenderer") + .get("content") + .get("sectionListRenderer") + .get("contents") + .index(0) + .get("itemSectionRenderer") + .get("contents") + .index(0) + .get("playlistVideoListRenderer"); + } + + @Override + @NotNull + public String getIdentifier() { + return BASE_CONFIG.getName(); + } +} diff --git a/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV3.java b/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV3.java index 5ff0712..e9991a7 100644 --- a/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV3.java +++ b/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV3.java @@ -20,6 +20,7 @@ private enum ClientMapping implements ClientReference { TVHTML5EMBEDDED(TvHtml5Embedded::new), WEB(Web::new), WEBEMBEDDED(WebEmbedded::new), + MWEB(MWeb::new), MEDIA_CONNECT(MediaConnect::new); private final ClientWithOptions clientFactory; diff --git a/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV4.java b/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV4.java index 25658f8..040f8dc 100644 --- a/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV4.java +++ b/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV4.java @@ -21,6 +21,7 @@ private enum ClientMapping implements ClientReference { TVHTML5EMBEDDED(TvHtml5EmbeddedWithThumbnail::new), WEB(WebWithThumbnail::new), WEBEMBEDDED(WebEmbeddedWithThumbnail::new), + MWEB(MWebWithThumbnail::new), MEDIA_CONNECT(MediaConnectWithThumbnail::new); private final ClientWithOptions clientFactory; diff --git a/v2/src/main/java/dev/lavalink/youtube/clients/MWebWithThumbnail.java b/v2/src/main/java/dev/lavalink/youtube/clients/MWebWithThumbnail.java new file mode 100644 index 0000000..860329a --- /dev/null +++ b/v2/src/main/java/dev/lavalink/youtube/clients/MWebWithThumbnail.java @@ -0,0 +1,10 @@ +package dev.lavalink.youtube.clients; + +import dev.lavalink.youtube.clients.skeleton.NonMusicClientWithThumbnail; +import org.jetbrains.annotations.NotNull; + +public class MWebWithThumbnail extends MWeb implements NonMusicClientWithThumbnail { + public MWebWithThumbnail(@NotNull ClientOptions options) { + super(options); + } +} From 7aec38a05ab40ef9953b65622fab378b6235f028 Mon Sep 17 00:00:00 2001 From: Jirayu Date: Tue, 3 Dec 2024 03:19:30 +0700 Subject: [PATCH 3/7] Fix Playlist Loading for ANDROID and IOS Clients; Remove Deprecated Clients (#82) * chore: update `CLIENT_VERSION` for client `ANDROID` * chore: remove `ANDROID_LITE`, `ANDROID_TESTSUITE` and `MEDIA_CONNECT` due to its broken * fix: playlist name extraction[`ANDROID`] * fix: playlist name extraction[`ANDROID_VR`] * fix: playlist name extraction[`IOS`] * chore: update `CLIENT_VERSION` and `USER_AGENT` for `IOS` client * chore: update `CLIENT_VERSION` for `ANDROID_VR` client * chore: update `CLIENT_VERSION` for `ANDROID_MUSIC` client * chore: update `CLIENT_VERSION` for `WEB_EMBEDDED_PLAYER` client * chore: update `CLIENT_VERSION` for `WEB_REMIX` client * chore: update default clients --------- Co-authored-by: devoxin <15076404+devoxin@users.noreply.github.com> --- README.md | 6 -- .../youtube/YoutubeAudioSourceManager.java | 2 +- .../dev/lavalink/youtube/clients/Android.java | 24 +++++- .../lavalink/youtube/clients/AndroidLite.java | 67 ----------------- .../youtube/clients/AndroidMusic.java | 2 +- .../youtube/clients/AndroidTestsuite.java | 70 ----------------- .../lavalink/youtube/clients/AndroidVr.java | 9 ++- .../dev/lavalink/youtube/clients/Ios.java | 13 +++- .../youtube/clients/MediaConnect.java | 75 ------------------- .../dev/lavalink/youtube/clients/Music.java | 2 +- .../lavalink/youtube/clients/WebEmbedded.java | 2 +- .../youtube/plugin/ClientProviderV3.java | 5 +- .../youtube/plugin/ClientProviderV4.java | 5 +- .../clients/AndroidLiteWithThumbnail.java | 14 ---- .../AndroidTestsuiteWithThumbnail.java | 14 ---- .../clients/MediaConnectWithThumbnail.java | 14 ---- 16 files changed, 48 insertions(+), 276 deletions(-) delete mode 100644 common/src/main/java/dev/lavalink/youtube/clients/AndroidLite.java delete mode 100644 common/src/main/java/dev/lavalink/youtube/clients/AndroidTestsuite.java delete mode 100644 common/src/main/java/dev/lavalink/youtube/clients/MediaConnect.java delete mode 100644 v2/src/main/java/dev/lavalink/youtube/clients/AndroidLiteWithThumbnail.java delete mode 100644 v2/src/main/java/dev/lavalink/youtube/clients/AndroidTestsuiteWithThumbnail.java delete mode 100644 v2/src/main/java/dev/lavalink/youtube/clients/MediaConnectWithThumbnail.java diff --git a/README.md b/README.md index 3488b19..2a5cf84 100644 --- a/README.md +++ b/README.md @@ -193,17 +193,11 @@ Currently, the following clients are available for use: - ❌ No mix/playlist/search support. - `ANDROID` - ❌ Heavily restricted, frequently dysfunctional. -- `ANDROID_TESTSUITE` - - ✔ Opus formats. - - ❌ No mix/playlist/livestream support. - `ANDROID_MUSIC` - ✔ Opus formats. - ❌ No playlist/livestream support. - `ANDROID_VR` - ✔ Opus formats. -- `MEDIA_CONNECT` - - ❌ No Opus formats (requires transcoding). - - ❌ No mix/playlist/search support. - `IOS` - ❌ No Opus formats (requires transcoding). - `TVHTML5EMBEDDED` diff --git a/common/src/main/java/dev/lavalink/youtube/YoutubeAudioSourceManager.java b/common/src/main/java/dev/lavalink/youtube/YoutubeAudioSourceManager.java index 2648de1..ac27a5d 100644 --- a/common/src/main/java/dev/lavalink/youtube/YoutubeAudioSourceManager.java +++ b/common/src/main/java/dev/lavalink/youtube/YoutubeAudioSourceManager.java @@ -79,7 +79,7 @@ public YoutubeAudioSourceManager(boolean allowSearch) { public YoutubeAudioSourceManager(boolean allowSearch, boolean allowDirectVideoIds, boolean allowDirectPlaylistIds) { // query order: music -> web -> androidtestsuite -> tvhtml5embedded - this(allowSearch, allowDirectVideoIds, allowDirectPlaylistIds, new Music(), new Web(), new AndroidTestsuite(), new TvHtml5Embedded()); + this(allowSearch, allowDirectVideoIds, allowDirectPlaylistIds, new Music(), new AndroidVr(), new Web(), new WebEmbedded()); } /** diff --git a/common/src/main/java/dev/lavalink/youtube/clients/Android.java b/common/src/main/java/dev/lavalink/youtube/clients/Android.java index 738f786..50cac73 100644 --- a/common/src/main/java/dev/lavalink/youtube/clients/Android.java +++ b/common/src/main/java/dev/lavalink/youtube/clients/Android.java @@ -1,5 +1,6 @@ package dev.lavalink.youtube.clients; +import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser; import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; import dev.lavalink.youtube.clients.ClientConfig.AndroidVersion; import dev.lavalink.youtube.clients.skeleton.StreamingNonMusicClient; @@ -10,7 +11,7 @@ public class Android extends StreamingNonMusicClient { private static final Logger log = LoggerFactory.getLogger(Android.class); - public static String CLIENT_VERSION = "19.07.39"; + public static String CLIENT_VERSION = "19.44.38"; public static AndroidVersion ANDROID_VERSION = AndroidVersion.ANDROID_11; public static ClientConfig BASE_CONFIG = new ClientConfig() @@ -62,4 +63,25 @@ public ClientOptions getOptions() { public String getIdentifier() { return BASE_CONFIG.getName(); } + + @Override + @NotNull + protected String extractPlaylistName(@NotNull JsonBrowser json) { + return json.get("header") + .get("pageHeaderRenderer") + .get("content") + .get("elementRenderer") + .get("newElement") + .get("type") + .get("componentType") + .get("model") + .get("youtubeModel") + .get("viewModel") + .get("pageHeaderViewModel") + .get("title") + .get("dynamicTextViewModel") + .get("text") + .get("content") + .text(); + } } diff --git a/common/src/main/java/dev/lavalink/youtube/clients/AndroidLite.java b/common/src/main/java/dev/lavalink/youtube/clients/AndroidLite.java deleted file mode 100644 index 9a9276a..0000000 --- a/common/src/main/java/dev/lavalink/youtube/clients/AndroidLite.java +++ /dev/null @@ -1,67 +0,0 @@ -package dev.lavalink.youtube.clients; - -import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; -import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity; -import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; -import com.sedmelluq.discord.lavaplayer.track.AudioItem; -import dev.lavalink.youtube.YoutubeAudioSourceManager; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class AndroidLite extends Android { - public static ClientConfig BASE_CONFIG = new ClientConfig() - .withApiKey(Android.BASE_CONFIG.getApiKey()) - .withClientName("ANDROID_LITE") - .withClientField("clientVersion", "3.26.1") - .withClientField("androidSdkVersion", ANDROID_VERSION.getSdkVersion()); - - public AndroidLite() { - this(ClientOptions.DEFAULT); - } - - public AndroidLite(@NotNull ClientOptions options) { - super(options, false); - } - - @Override - @NotNull - protected ClientConfig getBaseClientConfig(@NotNull HttpInterface httpInterface) { - return BASE_CONFIG.copy(); - } - - @Override - @NotNull - public String getIdentifier() { - return BASE_CONFIG.getName(); - } - - @Override - public boolean canHandleRequest(@NotNull String identifier) { - // loose check to avoid loading mixes/playlists. - return !identifier.contains("list=") && super.canHandleRequest(identifier); - } - - @Override - public AudioItem loadMix(@NotNull YoutubeAudioSourceManager source, - @NotNull HttpInterface httpInterface, - @NotNull String mixId, - @Nullable String selectedVideoId) { - // Considered returning null but an exception makes it clearer as to why a mix couldn't be loaded, - // assuming someone tries to only register this client with the source manager. - // Also, an exception will halt further loading so other source managers won't be queried. - // N.B. This client genuinely cannot load mixes for whatever reason. You can get the mix metadata - // but there are no videos in the response JSON. Weird. - throw new FriendlyException("This client cannot load mixes", Severity.COMMON, - new RuntimeException("ANDROID_LITE cannot be used to load mixes")); - } - - @Override - public AudioItem loadPlaylist(@NotNull YoutubeAudioSourceManager source, - @NotNull HttpInterface httpInterface, - @NotNull String playlistId, - @Nullable String selectedVideoId) { - // Similar to mixes except server returns status code 500 when trying to load playlists. - throw new FriendlyException("This client cannot load playlists", Severity.COMMON, - new RuntimeException("ANDROID_LITE cannot be used to load playlists")); - } -} diff --git a/common/src/main/java/dev/lavalink/youtube/clients/AndroidMusic.java b/common/src/main/java/dev/lavalink/youtube/clients/AndroidMusic.java index f744c19..643dded 100644 --- a/common/src/main/java/dev/lavalink/youtube/clients/AndroidMusic.java +++ b/common/src/main/java/dev/lavalink/youtube/clients/AndroidMusic.java @@ -19,7 +19,7 @@ public class AndroidMusic extends Android { private static final Logger log = LoggerFactory.getLogger(AndroidMusic.class); - public static String CLIENT_VERSION = "7.11.50"; + public static String CLIENT_VERSION = "7.27.52"; public static ClientConfig BASE_CONFIG = new ClientConfig() .withApiKey(Android.BASE_CONFIG.getApiKey()) diff --git a/common/src/main/java/dev/lavalink/youtube/clients/AndroidTestsuite.java b/common/src/main/java/dev/lavalink/youtube/clients/AndroidTestsuite.java deleted file mode 100644 index 6e4a58a..0000000 --- a/common/src/main/java/dev/lavalink/youtube/clients/AndroidTestsuite.java +++ /dev/null @@ -1,70 +0,0 @@ -package dev.lavalink.youtube.clients; - -import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; -import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity; -import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; -import com.sedmelluq.discord.lavaplayer.track.AudioItem; -import dev.lavalink.youtube.YoutubeAudioSourceManager; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class AndroidTestsuite extends Android { - public static String CLIENT_VERSION = "1.9"; - - public static ClientConfig BASE_CONFIG = new ClientConfig() - .withApiKey(Android.BASE_CONFIG.getApiKey()) - .withUserAgent(String.format("com.google.android.youtube/%s (Linux; U; Android %s) gzip", CLIENT_VERSION, ANDROID_VERSION.getOsVersion())) - .withClientName("ANDROID_TESTSUITE") - .withClientField("clientVersion", CLIENT_VERSION) - .withClientField("androidSdkVersion", ANDROID_VERSION.getSdkVersion()); - - public AndroidTestsuite() { - this(ClientOptions.DEFAULT); - } - - public AndroidTestsuite(@NotNull ClientOptions options) { - super(options, false); - } - - @Override - @NotNull - protected ClientConfig getBaseClientConfig(@NotNull HttpInterface httpInterface) { - return BASE_CONFIG.copy(); - } - - @Override - @NotNull - public String getIdentifier() { - return BASE_CONFIG.getName(); - } - - @Override - public boolean canHandleRequest(@NotNull String identifier) { - // loose check to avoid loading mixes/playlists. - return !identifier.contains("list=") && super.canHandleRequest(identifier); - } - - @Override - public AudioItem loadMix(@NotNull YoutubeAudioSourceManager source, - @NotNull HttpInterface httpInterface, - @NotNull String mixId, - @Nullable String selectedVideoId) { - // Considered returning null but an exception makes it clearer as to why a mix couldn't be loaded, - // assuming someone tries to only register this client with the source manager. - // Also, an exception will halt further loading so other source managers won't be queried. - // N.B. This client genuinely cannot load mixes for whatever reason. You can get the mix metadata - // but there are no videos in the response JSON. Weird. - throw new FriendlyException("This client cannot load mixes", Severity.COMMON, - new RuntimeException("ANDROID_TESTSUITE cannot be used to load mixes")); - } - - @Override - public AudioItem loadPlaylist(@NotNull YoutubeAudioSourceManager source, - @NotNull HttpInterface httpInterface, - @NotNull String playlistId, - @Nullable String selectedVideoId) { - // Similar to mixes except server returns status code 500 when trying to load playlists. - throw new FriendlyException("This client cannot load playlists", Severity.COMMON, - new RuntimeException("ANDROID_TESTSUITE cannot be used to load playlists")); - } -} diff --git a/common/src/main/java/dev/lavalink/youtube/clients/AndroidVr.java b/common/src/main/java/dev/lavalink/youtube/clients/AndroidVr.java index 8e6aaee..7170521 100644 --- a/common/src/main/java/dev/lavalink/youtube/clients/AndroidVr.java +++ b/common/src/main/java/dev/lavalink/youtube/clients/AndroidVr.java @@ -1,11 +1,12 @@ package dev.lavalink.youtube.clients; +import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser; import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; import dev.lavalink.youtube.clients.ClientConfig.AndroidVersion; import org.jetbrains.annotations.NotNull; public class AndroidVr extends Android { - public static String CLIENT_VERSION = "1.60.18"; + public static String CLIENT_VERSION = "1.60.19"; public static AndroidVersion ANDROID_VERSION = AndroidVersion.ANDROID_12L; public static ClientConfig BASE_CONFIG = new ClientConfig() @@ -36,4 +37,10 @@ protected ClientConfig getBaseClientConfig(@NotNull HttpInterface httpInterface) public String getIdentifier() { return BASE_CONFIG.getName(); } + + @Override + @NotNull + protected String extractPlaylistName(@NotNull JsonBrowser json) { + return json.get("header").get("playlistHeaderRenderer").get("title").get("runs").index(0).get("text").text(); + } } diff --git a/common/src/main/java/dev/lavalink/youtube/clients/Ios.java b/common/src/main/java/dev/lavalink/youtube/clients/Ios.java index 6142399..47af8ac 100644 --- a/common/src/main/java/dev/lavalink/youtube/clients/Ios.java +++ b/common/src/main/java/dev/lavalink/youtube/clients/Ios.java @@ -6,11 +6,11 @@ import org.jetbrains.annotations.NotNull; public class Ios extends StreamingNonMusicClient { - public static String CLIENT_VERSION = "19.07.5"; + public static String CLIENT_VERSION = "19.45.4"; public static ClientConfig BASE_CONFIG = new ClientConfig() .withApiKey("AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc") - .withUserAgent(String.format("com.google.ios.youtube/%s (iPhone14,5; U; CPU iOS 15_6 like Mac OS X)", CLIENT_VERSION)) + .withUserAgent(String.format("com.google.ios.youtube/%s (iPhone16,2; U; CPU iOS 18_1_0 like Mac OS X;)", CLIENT_VERSION)) .withClientName("IOS") .withClientField("clientVersion", CLIENT_VERSION) .withUserField("lockedSafetyMode", false); @@ -49,6 +49,15 @@ protected JsonBrowser extractPlaylistVideoList(@NotNull JsonBrowser json) { .get("playlistVideoListRenderer"); } + @Override + @NotNull + protected String extractPlaylistName(@NotNull JsonBrowser json) { + return json.get("header") + .get("pageHeaderRenderer") + .get("pageTitle") + .text(); + } + @Override @NotNull public String getPlayerParams() { diff --git a/common/src/main/java/dev/lavalink/youtube/clients/MediaConnect.java b/common/src/main/java/dev/lavalink/youtube/clients/MediaConnect.java deleted file mode 100644 index f6e99b3..0000000 --- a/common/src/main/java/dev/lavalink/youtube/clients/MediaConnect.java +++ /dev/null @@ -1,75 +0,0 @@ -package dev.lavalink.youtube.clients; - -import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; -import com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity; -import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; -import com.sedmelluq.discord.lavaplayer.track.AudioItem; -import dev.lavalink.youtube.YoutubeAudioSourceManager; -import dev.lavalink.youtube.clients.skeleton.StreamingNonMusicClient; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public class MediaConnect extends StreamingNonMusicClient { - public static ClientConfig BASE_CONFIG = new ClientConfig() - .withClientName("MEDIA_CONNECT_FRONTEND") - .withClientField("clientVersion", "0.1"); - - protected ClientOptions options; - - public MediaConnect() { - this(ClientOptions.DEFAULT); - } - - public MediaConnect(@NotNull ClientOptions options) { - this.options = options; - } - - @Override - @NotNull - protected ClientConfig getBaseClientConfig(@NotNull HttpInterface httpInterface) { - return BASE_CONFIG.copy(); - } - - @Override - @NotNull - public String getPlayerParams() { - return MOBILE_PLAYER_PARAMS; - } - - @Override - @NotNull - public ClientOptions getOptions() { - return this.options; - } - - @Override - public boolean canHandleRequest(@NotNull String identifier) { - // This client appears to be able to load livestreams and videos, but will - // receive 400 bad request when loading playlists. mixes do return JSON, but does not contain mix videos. - return !identifier.startsWith(YoutubeAudioSourceManager.SEARCH_PREFIX) && !identifier.contains("list=") && super.canHandleRequest(identifier); - } - - @Override - @NotNull - public String getIdentifier() { - return BASE_CONFIG.getName(); - } - - @Override - public AudioItem loadSearch(@NotNull YoutubeAudioSourceManager source, @NotNull HttpInterface httpInterface, @NotNull String searchQuery) { - throw new FriendlyException("This client cannot load searches", Severity.COMMON, - new RuntimeException("MEDIA_CONNECT cannot be used to load searches")); - } - - @Override - public AudioItem loadPlaylist(@NotNull YoutubeAudioSourceManager source, @NotNull HttpInterface httpInterface, @NotNull String playlistId, @Nullable String selectedVideoId) { - throw new FriendlyException("This client cannot load playlists", Severity.COMMON, - new RuntimeException("MEDIA_CONNECT cannot be used to load playlists")); - } - - @Override - public AudioItem loadMix(@NotNull YoutubeAudioSourceManager source, @NotNull HttpInterface httpInterface, @NotNull String mixId, @Nullable String selectedVideoId) { - throw new FriendlyException("This client cannot load mixes", Severity.COMMON, - new RuntimeException("MEDIA_CONNECT cannot be used to load mixes")); - } -} diff --git a/common/src/main/java/dev/lavalink/youtube/clients/Music.java b/common/src/main/java/dev/lavalink/youtube/clients/Music.java index 6d8d287..610c537 100644 --- a/common/src/main/java/dev/lavalink/youtube/clients/Music.java +++ b/common/src/main/java/dev/lavalink/youtube/clients/Music.java @@ -8,7 +8,7 @@ public class Music extends MusicClient { public static ClientConfig BASE_CONFIG = new ClientConfig() .withApiKey("AIzaSyC9XL3ZjWddXya6X74dJoCTL-WEYFDNX30") // Requires header (Referer music.youtube.com) .withClientName("WEB_REMIX") - .withClientField("clientVersion", "1.20240401.00.00"); + .withClientField("clientVersion", "1.20240724.00.00"); protected ClientOptions options; diff --git a/common/src/main/java/dev/lavalink/youtube/clients/WebEmbedded.java b/common/src/main/java/dev/lavalink/youtube/clients/WebEmbedded.java index 6ee7d38..413a286 100644 --- a/common/src/main/java/dev/lavalink/youtube/clients/WebEmbedded.java +++ b/common/src/main/java/dev/lavalink/youtube/clients/WebEmbedded.java @@ -20,7 +20,7 @@ public class WebEmbedded extends Web { public static ClientConfig BASE_CONFIG = new ClientConfig() .withApiKey("AIzaSyAO_FJ2SlqU8Q4STEHLGCilw_Y9_11qcW8") .withClientName("WEB_EMBEDDED_PLAYER") - .withClientField("clientVersion", "1.20240902.00.00") + .withClientField("clientVersion", "1.20240723.01.00") .withUserField("lockedSafetyMode", false); public WebEmbedded() { diff --git a/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV3.java b/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV3.java index e9991a7..ffc8a58 100644 --- a/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV3.java +++ b/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV3.java @@ -11,8 +11,6 @@ public Client[] getClients(String[] clients, OptionsProvider optionsProvider) { private enum ClientMapping implements ClientReference { ANDROID(Android::new), - ANDROID_TESTSUITE(AndroidTestsuite::new), - ANDROID_LITE(AndroidLite::new), ANDROID_MUSIC(AndroidMusic::new), ANDROID_VR(AndroidVr::new), IOS(Ios::new), @@ -20,8 +18,7 @@ private enum ClientMapping implements ClientReference { TVHTML5EMBEDDED(TvHtml5Embedded::new), WEB(Web::new), WEBEMBEDDED(WebEmbedded::new), - MWEB(MWeb::new), - MEDIA_CONNECT(MediaConnect::new); + MWEB(MWeb::new); private final ClientWithOptions clientFactory; diff --git a/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV4.java b/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV4.java index 040f8dc..598894e 100644 --- a/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV4.java +++ b/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV4.java @@ -12,8 +12,6 @@ public Client[] getClients(String[] clients, OptionsProvider optionsProvider) { private enum ClientMapping implements ClientReference { ANDROID(AndroidWithThumbnail::new), - ANDROID_TESTSUITE(AndroidTestsuiteWithThumbnail::new), - ANDROID_LITE(AndroidLiteWithThumbnail::new), ANDROID_MUSIC(AndroidMusicWithThumbnail::new), ANDROID_VR(AndroidVrWithThumbnail::new), IOS(IosWithThumbnail::new), @@ -21,8 +19,7 @@ private enum ClientMapping implements ClientReference { TVHTML5EMBEDDED(TvHtml5EmbeddedWithThumbnail::new), WEB(WebWithThumbnail::new), WEBEMBEDDED(WebEmbeddedWithThumbnail::new), - MWEB(MWebWithThumbnail::new), - MEDIA_CONNECT(MediaConnectWithThumbnail::new); + MWEB(MWebWithThumbnail::new); private final ClientWithOptions clientFactory; diff --git a/v2/src/main/java/dev/lavalink/youtube/clients/AndroidLiteWithThumbnail.java b/v2/src/main/java/dev/lavalink/youtube/clients/AndroidLiteWithThumbnail.java deleted file mode 100644 index 828bf9d..0000000 --- a/v2/src/main/java/dev/lavalink/youtube/clients/AndroidLiteWithThumbnail.java +++ /dev/null @@ -1,14 +0,0 @@ -package dev.lavalink.youtube.clients; - -import dev.lavalink.youtube.clients.skeleton.NonMusicClientWithThumbnail; -import org.jetbrains.annotations.NotNull; - -public class AndroidLiteWithThumbnail extends AndroidLite implements NonMusicClientWithThumbnail { - public AndroidLiteWithThumbnail() { - super(); - } - - public AndroidLiteWithThumbnail(@NotNull ClientOptions options) { - super(options); - } -} diff --git a/v2/src/main/java/dev/lavalink/youtube/clients/AndroidTestsuiteWithThumbnail.java b/v2/src/main/java/dev/lavalink/youtube/clients/AndroidTestsuiteWithThumbnail.java deleted file mode 100644 index b0fe176..0000000 --- a/v2/src/main/java/dev/lavalink/youtube/clients/AndroidTestsuiteWithThumbnail.java +++ /dev/null @@ -1,14 +0,0 @@ -package dev.lavalink.youtube.clients; - -import dev.lavalink.youtube.clients.skeleton.NonMusicClientWithThumbnail; -import org.jetbrains.annotations.NotNull; - -public class AndroidTestsuiteWithThumbnail extends AndroidTestsuite implements NonMusicClientWithThumbnail { - public AndroidTestsuiteWithThumbnail() { - super(); - } - - public AndroidTestsuiteWithThumbnail(@NotNull ClientOptions options) { - super(options); - } -} diff --git a/v2/src/main/java/dev/lavalink/youtube/clients/MediaConnectWithThumbnail.java b/v2/src/main/java/dev/lavalink/youtube/clients/MediaConnectWithThumbnail.java deleted file mode 100644 index 37fee8e..0000000 --- a/v2/src/main/java/dev/lavalink/youtube/clients/MediaConnectWithThumbnail.java +++ /dev/null @@ -1,14 +0,0 @@ -package dev.lavalink.youtube.clients; - -import dev.lavalink.youtube.clients.skeleton.NonMusicClientWithThumbnail; -import org.jetbrains.annotations.NotNull; - -public class MediaConnectWithThumbnail extends MediaConnect implements NonMusicClientWithThumbnail { - public MediaConnectWithThumbnail() { - super(); - } - - public MediaConnectWithThumbnail(@NotNull ClientOptions options) { - super(options); - } -} From b3a18a15ec0a5439bef03f754098f4a6dbb26fd3 Mon Sep 17 00:00:00 2001 From: Devoxin Date: Mon, 2 Dec 2024 20:20:21 +0000 Subject: [PATCH 4/7] Add MWeb to README. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2a5cf84..54457ae 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,8 @@ Currently, the following clients are available for use: - ❌ Cannot be used for playback, or playlist/mix/livestream loading. - `WEB` - ✔ Opus formats. +- `MWEB` + - ✔ Opus formats. - `WEBEMBEDDED` - ✔ Opus formats. - ✔ Limited age-restricted video playback. From e92ee8cbae9771b3099e3f214107949a8a33fb5c Mon Sep 17 00:00:00 2001 From: Devoxin Date: Mon, 2 Dec 2024 20:23:02 +0000 Subject: [PATCH 5/7] Fix no-arg constructor in MWeb --- common/src/main/java/dev/lavalink/youtube/clients/MWeb.java | 4 ++++ .../java/dev/lavalink/youtube/plugin/ClientProviderV4.java | 1 - .../java/dev/lavalink/youtube/clients/MWebWithThumbnail.java | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/common/src/main/java/dev/lavalink/youtube/clients/MWeb.java b/common/src/main/java/dev/lavalink/youtube/clients/MWeb.java index 97896b6..42a2ba0 100644 --- a/common/src/main/java/dev/lavalink/youtube/clients/MWeb.java +++ b/common/src/main/java/dev/lavalink/youtube/clients/MWeb.java @@ -10,6 +10,10 @@ public class MWeb extends Web { .withClientName("MWEB") .withClientField("clientVersion", "2.20240726.11.00"); + public MWeb() { + super(); + } + public MWeb(@NotNull ClientOptions options) { super(options); } diff --git a/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV4.java b/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV4.java index 598894e..0bd91d9 100644 --- a/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV4.java +++ b/plugin/src/main/java/dev/lavalink/youtube/plugin/ClientProviderV4.java @@ -2,7 +2,6 @@ import dev.lavalink.youtube.clients.*; import dev.lavalink.youtube.clients.skeleton.Client; -import dev.lavalink.youtube.clients.WebEmbeddedWithThumbnail; public class ClientProviderV4 implements ClientProvider { @Override diff --git a/v2/src/main/java/dev/lavalink/youtube/clients/MWebWithThumbnail.java b/v2/src/main/java/dev/lavalink/youtube/clients/MWebWithThumbnail.java index 860329a..17019f0 100644 --- a/v2/src/main/java/dev/lavalink/youtube/clients/MWebWithThumbnail.java +++ b/v2/src/main/java/dev/lavalink/youtube/clients/MWebWithThumbnail.java @@ -4,6 +4,10 @@ import org.jetbrains.annotations.NotNull; public class MWebWithThumbnail extends MWeb implements NonMusicClientWithThumbnail { + public MWebWithThumbnail() { + super(); + } + public MWebWithThumbnail(@NotNull ClientOptions options) { super(options); } From 667b0775ae021bfb5bea0add67835e51cc6e7d25 Mon Sep 17 00:00:00 2001 From: Devoxin Date: Mon, 2 Dec 2024 20:35:06 +0000 Subject: [PATCH 6/7] Add note about versioning policy. --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 54457ae..ed62492 100644 --- a/README.md +++ b/README.md @@ -386,6 +386,12 @@ In addition, there are a few significant changes to note: the source manager with (e.g. an overridden `YoutubeTrackDetailsLoader`), this **is not** compatible with this source manager. +## Versioning Policy +This project follows [Semantic Versioning](https://semver.org/), except in the case of [client](#available-clients) removal. +Typically, clients are not removed unless there is good reason, such as being deprecated, irreparably broken or removed from YouTube's client lifecycle. +In such scenarios, we anticipate that you have ceased usage of such clients prior to their removal, so do not expect any code breakage, +however we advise that you periodically check and keep your client list up to date due to this. + ## Additional Support If you need additional help with using this source, that's not covered here or in any of the issues, [join our Discord server](https://discord.gg/ZW4s47Ppw4). From 8742c341b0e51d222dc9c2a25a85e24bef189b20 Mon Sep 17 00:00:00 2001 From: Devoxin Date: Mon, 2 Dec 2024 20:56:07 +0000 Subject: [PATCH 7/7] Only apply OAuth token to requests from OAuth-enabled clients. --- .../dev/lavalink/youtube/YoutubeAudioSourceManager.java | 1 + .../dev/lavalink/youtube/clients/TvHtml5Embedded.java | 5 +++++ .../dev/lavalink/youtube/clients/skeleton/Client.java | 9 +++++++++ .../lavalink/youtube/http/YoutubeHttpContextFilter.java | 8 +++++++- .../dev/lavalink/youtube/http/YoutubeOauth2Handler.java | 4 ---- .../dev/lavalink/youtube/track/YoutubeAudioTrack.java | 2 ++ 6 files changed, 24 insertions(+), 5 deletions(-) diff --git a/common/src/main/java/dev/lavalink/youtube/YoutubeAudioSourceManager.java b/common/src/main/java/dev/lavalink/youtube/YoutubeAudioSourceManager.java index ac27a5d..5dc62cb 100644 --- a/common/src/main/java/dev/lavalink/youtube/YoutubeAudioSourceManager.java +++ b/common/src/main/java/dev/lavalink/youtube/YoutubeAudioSourceManager.java @@ -212,6 +212,7 @@ protected AudioItem loadItemOnce(@NotNull AudioReference reference) { } log.debug("Attempting to load {} with client \"{}\"", reference.identifier, client.getIdentifier()); + httpInterface.getContext().setAttribute(Client.OAUTH_CLIENT_ATTRIBUTE, client.supportsOAuth()); try { AudioItem item = router.route(client); diff --git a/common/src/main/java/dev/lavalink/youtube/clients/TvHtml5Embedded.java b/common/src/main/java/dev/lavalink/youtube/clients/TvHtml5Embedded.java index 073687b..3fe8694 100644 --- a/common/src/main/java/dev/lavalink/youtube/clients/TvHtml5Embedded.java +++ b/common/src/main/java/dev/lavalink/youtube/clients/TvHtml5Embedded.java @@ -105,6 +105,11 @@ public boolean canHandleRequest(@NotNull String identifier) { return (!identifier.contains("list=") || identifier.contains("list=RD")) && super.canHandleRequest(identifier); } + @Override + public boolean supportsOAuth() { + return true; + } + @Override @NotNull public String getIdentifier() { diff --git a/common/src/main/java/dev/lavalink/youtube/clients/skeleton/Client.java b/common/src/main/java/dev/lavalink/youtube/clients/skeleton/Client.java index 74cf41f..eebc54f 100644 --- a/common/src/main/java/dev/lavalink/youtube/clients/skeleton/Client.java +++ b/common/src/main/java/dev/lavalink/youtube/clients/skeleton/Client.java @@ -25,6 +25,8 @@ * The interface for a Client. */ public interface Client { + String OAUTH_CLIENT_ATTRIBUTE = "yt-oauth-enabled-client"; + String WATCH_URL = "https://www.youtube.com/watch?v="; String API_BASE_URL = "https://youtubei.googleapis.com/youtubei/v1"; String PLAYER_URL = API_BASE_URL + "/player?prettyPrint=false"; @@ -208,6 +210,13 @@ default boolean isEmbedded() { return false; } + /** + * @return True, if this client supports account linking via OAuth (i.e. TV) + */ + default boolean supportsOAuth() { + return false; + } + void setPlaylistPageCount(int count); /** diff --git a/common/src/main/java/dev/lavalink/youtube/http/YoutubeHttpContextFilter.java b/common/src/main/java/dev/lavalink/youtube/http/YoutubeHttpContextFilter.java index 939c73c..03ba527 100644 --- a/common/src/main/java/dev/lavalink/youtube/http/YoutubeHttpContextFilter.java +++ b/common/src/main/java/dev/lavalink/youtube/http/YoutubeHttpContextFilter.java @@ -3,6 +3,7 @@ import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; import com.sedmelluq.discord.lavaplayer.tools.http.HttpContextRetryCounter; import com.sedmelluq.discord.lavaplayer.tools.io.HttpClientTools; +import dev.lavalink.youtube.clients.skeleton.Client; import org.apache.http.HttpResponse; import org.apache.http.client.CookieStore; import org.apache.http.client.methods.HttpUriRequest; @@ -79,7 +80,12 @@ public void onRequest(HttpClientContext context, context.removeAttribute(ATTRIBUTE_USER_AGENT_SPECIFIED); } - oauth2Handler.applyToken(request); + boolean isRequestFromOauthedClient = context.getAttribute(Client.OAUTH_CLIENT_ATTRIBUTE) == Boolean.TRUE; + + if (isRequestFromOauthedClient && Client.PLAYER_URL.equals(request.getURI().toString())) { + // only apply the token to /player requests. + oauth2Handler.applyToken(request); + } } // try { diff --git a/common/src/main/java/dev/lavalink/youtube/http/YoutubeOauth2Handler.java b/common/src/main/java/dev/lavalink/youtube/http/YoutubeOauth2Handler.java index ace3703..7540333 100644 --- a/common/src/main/java/dev/lavalink/youtube/http/YoutubeOauth2Handler.java +++ b/common/src/main/java/dev/lavalink/youtube/http/YoutubeOauth2Handler.java @@ -259,10 +259,6 @@ public void applyToken(HttpUriRequest request) { return; } - if (!Client.PLAYER_URL.equals(request.getURI().toString())) { - return; - } - if (shouldRefreshAccessToken()) { log.debug("Access token has expired, refreshing..."); diff --git a/common/src/main/java/dev/lavalink/youtube/track/YoutubeAudioTrack.java b/common/src/main/java/dev/lavalink/youtube/track/YoutubeAudioTrack.java index 4ad6259..0fe4c1d 100644 --- a/common/src/main/java/dev/lavalink/youtube/track/YoutubeAudioTrack.java +++ b/common/src/main/java/dev/lavalink/youtube/track/YoutubeAudioTrack.java @@ -68,6 +68,8 @@ public void process(LocalAudioTrackExecutor localExecutor) throws Exception { continue; } + httpInterface.getContext().setAttribute(Client.OAUTH_CLIENT_ATTRIBUTE, client.supportsOAuth()); + try { processWithClient(localExecutor, httpInterface, client, 0); return; // stream played through successfully, short-circuit.