fix(YouTube - Hook download actions, Overlay buttons): Queue manager fails to identify brand account

This commit is contained in:
inotia00 2025-03-30 18:22:29 +09:00
parent edccd61e6b
commit f19dd54026
8 changed files with 165 additions and 81 deletions

View File

@ -279,6 +279,7 @@ object InnerTubeRequestBody {
route: CompiledRoute, route: CompiledRoute,
clientType: YouTubeAppClient.ClientType, clientType: YouTubeAppClient.ClientType,
requestHeader: Map<String, String>? = null, requestHeader: Map<String, String>? = null,
dataSyncId: String? = null,
connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
) = getInnerTubeResponseConnectionFromRoute( ) = getInnerTubeResponseConnectionFromRoute(
@ -288,6 +289,7 @@ object InnerTubeRequestBody {
clientVersion = clientType.clientVersion, clientVersion = clientType.clientVersion,
supportsCookies = clientType.supportsCookies, supportsCookies = clientType.supportsCookies,
requestHeader = requestHeader, requestHeader = requestHeader,
dataSyncId = dataSyncId,
connectTimeout = connectTimeout, connectTimeout = connectTimeout,
readTimeout = readTimeout, readTimeout = readTimeout,
) )
@ -297,6 +299,7 @@ object InnerTubeRequestBody {
route: CompiledRoute, route: CompiledRoute,
clientType: YouTubeWebClient.ClientType, clientType: YouTubeWebClient.ClientType,
requestHeader: Map<String, String>? = null, requestHeader: Map<String, String>? = null,
dataSyncId: String? = null,
connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
) = getInnerTubeResponseConnectionFromRoute( ) = getInnerTubeResponseConnectionFromRoute(
@ -305,6 +308,7 @@ object InnerTubeRequestBody {
clientId = clientType.id.toString(), clientId = clientType.id.toString(),
clientVersion = clientType.clientVersion, clientVersion = clientType.clientVersion,
requestHeader = requestHeader, requestHeader = requestHeader,
dataSyncId = dataSyncId,
connectTimeout = connectTimeout, connectTimeout = connectTimeout,
readTimeout = readTimeout, readTimeout = readTimeout,
) )
@ -317,6 +321,7 @@ object InnerTubeRequestBody {
clientVersion: String, clientVersion: String,
supportsCookies: Boolean = true, supportsCookies: Boolean = true,
requestHeader: Map<String, String>? = null, requestHeader: Map<String, String>? = null,
dataSyncId: String? = null,
connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, connectTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS, readTimeout: Int = CONNECTION_TIMEOUT_MILLISECONDS,
): HttpURLConnection { ): HttpURLConnection {
@ -348,6 +353,11 @@ object InnerTubeRequestBody {
} }
} }
// Used to identify brand accounts
if (dataSyncId != null && dataSyncId.isNotEmpty()) {
connection.setRequestProperty("X-Goog-PageId", dataSyncId)
}
return connection return connection
} }

View File

@ -1,5 +1,7 @@
package app.revanced.extension.youtube.patches.utils; package app.revanced.extension.youtube.patches.utils;
import static app.revanced.extension.shared.utils.StringRef.str;
import android.content.Context; import android.content.Context;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.widget.LinearLayout; import android.widget.LinearLayout;
@ -47,43 +49,52 @@ public class PlaylistPatch extends VideoUtils {
private static Context mContext; private static Context mContext;
private static volatile String authorization = ""; private static volatile String authorization = "";
private static volatile boolean isIncognito = false; public static volatile String dataSyncId = "";
public static volatile boolean isIncognito = false;
private static volatile Map<String, String> requestHeader; private static volatile Map<String, String> requestHeader;
private static volatile String playlistId = ""; private static volatile String playlistId = "";
private static volatile String videoId = ""; private static volatile String videoId = "";
private static final String checkFailedAuth = private static String checkFailedAuth;
ResourceUtils.getString("revanced_queue_manager_check_failed_auth"); private static String checkFailedPlaylistId;
private static final String checkFailedPlaylistId = private static String checkFailedQueue;
ResourceUtils.getString("revanced_queue_manager_check_failed_playlist_id"); private static String checkFailedVideoId;
private static final String checkFailedQueue = private static String checkFailedGeneric;
ResourceUtils.getString("revanced_queue_manager_check_failed_queue");
private static final String checkFailedVideoId =
ResourceUtils.getString("revanced_queue_manager_check_failed_video_id");
private static final String checkFailedGeneric =
ResourceUtils.getString("revanced_queue_manager_check_failed_generic");
private static final String fetchFailedAdd = private static String fetchFailedAdd;
ResourceUtils.getString("revanced_queue_manager_fetch_failed_add"); private static String fetchFailedCreate;
private static final String fetchFailedCreate = private static String fetchFailedDelete;
ResourceUtils.getString("revanced_queue_manager_fetch_failed_create"); private static String fetchFailedRemove;
private static final String fetchFailedDelete = private static String fetchFailedSave;
ResourceUtils.getString("revanced_queue_manager_fetch_failed_delete");
private static final String fetchFailedRemove =
ResourceUtils.getString("revanced_queue_manager_fetch_failed_remove");
private static final String fetchFailedSave =
ResourceUtils.getString("revanced_queue_manager_fetch_failed_save");
private static final String fetchSucceededAdd = private static String fetchSucceededAdd;
ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_add"); private static String fetchSucceededCreate;
private static final String fetchSucceededCreate = private static String fetchSucceededDelete;
ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_create"); private static String fetchSucceededRemove;
private static final String fetchSucceededDelete = private static String fetchSucceededSave;
ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_delete");
private static final String fetchSucceededRemove = static {
ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_remove"); Context mContext = Utils.getContext();
private static final String fetchSucceededSave = if (mContext != null && mContext.getResources() != null) {
ResourceUtils.getString("revanced_queue_manager_fetch_succeeded_save"); checkFailedAuth = str("revanced_queue_manager_check_failed_auth");
checkFailedPlaylistId = str("revanced_queue_manager_check_failed_playlist_id");
checkFailedQueue = str("revanced_queue_manager_check_failed_queue");
checkFailedVideoId = str("revanced_queue_manager_check_failed_video_id");
checkFailedGeneric = str("revanced_queue_manager_check_failed_generic");
fetchFailedAdd = str("revanced_queue_manager_fetch_failed_add");
fetchFailedCreate = str("revanced_queue_manager_fetch_failed_create");
fetchFailedDelete = str("revanced_queue_manager_fetch_failed_delete");
fetchFailedRemove = str("revanced_queue_manager_fetch_failed_remove");
fetchFailedSave = str("revanced_queue_manager_fetch_failed_save");
fetchSucceededAdd = str("revanced_queue_manager_fetch_succeeded_add");
fetchSucceededCreate = str("revanced_queue_manager_fetch_succeeded_create");
fetchSucceededDelete = str("revanced_queue_manager_fetch_succeeded_delete");
fetchSucceededRemove = str("revanced_queue_manager_fetch_succeeded_remove");
fetchSucceededSave = str("revanced_queue_manager_fetch_succeeded_save");
}
}
@GuardedBy("itself") @GuardedBy("itself")
private static final BidiMap<String, String> lastVideoIds = new DualHashBidiMap<>(); private static final BidiMap<String, String> lastVideoIds = new DualHashBidiMap<>();
@ -118,15 +129,6 @@ public class PlaylistPatch extends VideoUtils {
} }
} }
/**
* Injection point.
*/
public static void setIncognitoStatus(boolean incognito) {
if (QUEUE_MANAGER) {
isIncognito = incognito;
}
}
/** /**
* Injection point. * Injection point.
*/ */
@ -171,7 +173,7 @@ public class PlaylistPatch extends VideoUtils {
* Invoked by extension. * Invoked by extension.
*/ */
public static void prepareDialogBuilder(@NonNull String currentVideoId) { public static void prepareDialogBuilder(@NonNull String currentVideoId) {
if (authorization.isEmpty() || isIncognito) { if (authorization.isEmpty() || (dataSyncId.isEmpty() && isIncognito)) {
handleCheckError(checkFailedAuth); handleCheckError(checkFailedAuth);
return; return;
} }
@ -217,7 +219,7 @@ public class PlaylistPatch extends VideoUtils {
String currentVideoId = videoId; String currentVideoId = videoId;
synchronized (lastVideoIds) { synchronized (lastVideoIds) {
if (currentPlaylistId.isEmpty()) { // Queue is empty, create new playlist. if (currentPlaylistId.isEmpty()) { // Queue is empty, create new playlist.
CreatePlaylistRequest.fetchRequestIfNeeded(currentVideoId, requestHeader); CreatePlaylistRequest.fetchRequestIfNeeded(currentVideoId, requestHeader, dataSyncId);
runOnMainThreadDelayed(() -> { runOnMainThreadDelayed(() -> {
CreatePlaylistRequest request = CreatePlaylistRequest.getRequestForVideoId(currentVideoId); CreatePlaylistRequest request = CreatePlaylistRequest.getRequestForVideoId(currentVideoId);
if (request != null) { if (request != null) {
@ -241,7 +243,7 @@ public class PlaylistPatch extends VideoUtils {
}, 1000); }, 1000);
} else { // Queue is not empty, add or remove video. } else { // Queue is not empty, add or remove video.
String setVideoId = lastVideoIds.get(currentVideoId); String setVideoId = lastVideoIds.get(currentVideoId);
EditPlaylistRequest.fetchRequestIfNeeded(currentVideoId, currentPlaylistId, setVideoId, requestHeader); EditPlaylistRequest.fetchRequestIfNeeded(currentVideoId, currentPlaylistId, setVideoId, requestHeader, dataSyncId);
runOnMainThreadDelayed(() -> { runOnMainThreadDelayed(() -> {
EditPlaylistRequest request = EditPlaylistRequest.getRequestForVideoId(currentVideoId); EditPlaylistRequest request = EditPlaylistRequest.getRequestForVideoId(currentVideoId);
@ -286,7 +288,7 @@ public class PlaylistPatch extends VideoUtils {
return; return;
} }
try { try {
GetPlaylistsRequest.fetchRequestIfNeeded(currentPlaylistId, requestHeader); GetPlaylistsRequest.fetchRequestIfNeeded(currentPlaylistId, requestHeader, dataSyncId);
runOnMainThreadDelayed(() -> { runOnMainThreadDelayed(() -> {
GetPlaylistsRequest request = GetPlaylistsRequest.getRequestForPlaylistId(currentPlaylistId); GetPlaylistsRequest request = GetPlaylistsRequest.getRequestForPlaylistId(currentPlaylistId);
if (request != null) { if (request != null) {
@ -328,7 +330,7 @@ public class PlaylistPatch extends VideoUtils {
handleCheckError(checkFailedPlaylistId); handleCheckError(checkFailedPlaylistId);
return; return;
} }
SavePlaylistRequest.fetchRequestIfNeeded(playlistId, libraryId, requestHeader); SavePlaylistRequest.fetchRequestIfNeeded(playlistId, libraryId, requestHeader, dataSyncId);
runOnMainThreadDelayed(() -> { runOnMainThreadDelayed(() -> {
SavePlaylistRequest request = SavePlaylistRequest.getRequestForLibraryId(libraryId); SavePlaylistRequest request = SavePlaylistRequest.getRequestForLibraryId(libraryId);
@ -354,7 +356,7 @@ public class PlaylistPatch extends VideoUtils {
return; return;
} }
try { try {
DeletePlaylistRequest.fetchRequestIfNeeded(currentPlaylistId, requestHeader); DeletePlaylistRequest.fetchRequestIfNeeded(currentPlaylistId, requestHeader, dataSyncId);
runOnMainThreadDelayed(() -> { runOnMainThreadDelayed(() -> {
DeletePlaylistRequest request = DeletePlaylistRequest.getRequestForPlaylistId(currentPlaylistId); DeletePlaylistRequest request = DeletePlaylistRequest.getRequestForPlaylistId(currentPlaylistId);
if (request != null) { if (request != null) {

View File

@ -24,9 +24,14 @@ import java.util.concurrent.TimeoutException
class CreatePlaylistRequest private constructor( class CreatePlaylistRequest private constructor(
private val videoId: String, private val videoId: String,
private val requestHeader: Map<String, String>, private val requestHeader: Map<String, String>,
private val dataSyncId: String,
) { ) {
private val future: Future<Pair<String, String>> = Utils.submitOnBackgroundThread { private val future: Future<Pair<String, String>> = Utils.submitOnBackgroundThread {
fetch(videoId, requestHeader) fetch(
videoId,
requestHeader,
dataSyncId,
)
} }
val playlistId: Pair<String, String>? val playlistId: Pair<String, String>?
@ -75,11 +80,19 @@ class CreatePlaylistRequest private constructor(
} }
@JvmStatic @JvmStatic
fun fetchRequestIfNeeded(videoId: String, requestHeader: Map<String, String>) { fun fetchRequestIfNeeded(
videoId: String,
requestHeader: Map<String, String>,
dataSyncId: String,
) {
Objects.requireNonNull(videoId) Objects.requireNonNull(videoId)
synchronized(cache) { synchronized(cache) {
if (!cache.containsKey(videoId)) { if (!cache.containsKey(videoId)) {
cache[videoId] = CreatePlaylistRequest(videoId, requestHeader) cache[videoId] = CreatePlaylistRequest(
videoId,
requestHeader,
dataSyncId,
)
} }
} }
} }
@ -97,7 +110,8 @@ class CreatePlaylistRequest private constructor(
private fun sendCreatePlaylistRequest( private fun sendCreatePlaylistRequest(
videoId: String, videoId: String,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
): JSONObject? { ): JSONObject? {
Objects.requireNonNull(videoId) Objects.requireNonNull(videoId)
@ -112,6 +126,7 @@ class CreatePlaylistRequest private constructor(
CREATE_PLAYLIST, CREATE_PLAYLIST,
clientType, clientType,
requestHeader, requestHeader,
dataSyncId,
) )
val requestBody = createPlaylistRequestBody(videoId = videoId) val requestBody = createPlaylistRequestBody(videoId = videoId)
@ -143,7 +158,8 @@ class CreatePlaylistRequest private constructor(
private fun sendSetVideoIdRequest( private fun sendSetVideoIdRequest(
videoId: String, videoId: String,
playlistId: String, playlistId: String,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
): JSONObject? { ): JSONObject? {
Objects.requireNonNull(playlistId) Objects.requireNonNull(playlistId)
@ -157,7 +173,8 @@ class CreatePlaylistRequest private constructor(
val connection = getInnerTubeResponseConnectionFromRoute( val connection = getInnerTubeResponseConnectionFromRoute(
GET_SET_VIDEO_ID, GET_SET_VIDEO_ID,
clientType, clientType,
requestHeader requestHeader,
dataSyncId,
) )
val requestBody = createApplicationRequestBody( val requestBody = createApplicationRequestBody(
@ -232,13 +249,23 @@ class CreatePlaylistRequest private constructor(
private fun fetch( private fun fetch(
videoId: String, videoId: String,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
): Pair<String, String>? { ): Pair<String, String>? {
val createPlaylistJson = sendCreatePlaylistRequest(videoId, requestHeader) val createPlaylistJson = sendCreatePlaylistRequest(
videoId,
requestHeader,
dataSyncId
)
if (createPlaylistJson != null) { if (createPlaylistJson != null) {
val playlistId = parseCreatePlaylistResponse(createPlaylistJson) val playlistId = parseCreatePlaylistResponse(createPlaylistJson)
if (playlistId != null) { if (playlistId != null) {
val setVideoIdJson = sendSetVideoIdRequest(videoId, playlistId, requestHeader) val setVideoIdJson = sendSetVideoIdRequest(
videoId,
playlistId,
requestHeader,
dataSyncId
)
if (setVideoIdJson != null) { if (setVideoIdJson != null) {
val setVideoId = parseSetVideoIdResponse(setVideoIdJson) val setVideoId = parseSetVideoIdResponse(setVideoIdJson)
if (setVideoId != null) { if (setVideoId != null) {

View File

@ -22,11 +22,13 @@ import java.util.concurrent.TimeoutException
class DeletePlaylistRequest private constructor( class DeletePlaylistRequest private constructor(
private val playlistId: String, private val playlistId: String,
private val requestHeader: Map<String, String>, private val requestHeader: Map<String, String>,
private val dataSyncId: String,
) { ) {
private val future: Future<Boolean> = Utils.submitOnBackgroundThread { private val future: Future<Boolean> = Utils.submitOnBackgroundThread {
fetch( fetch(
playlistId, playlistId,
requestHeader, requestHeader,
dataSyncId,
) )
} }
@ -78,14 +80,16 @@ class DeletePlaylistRequest private constructor(
@JvmStatic @JvmStatic
fun fetchRequestIfNeeded( fun fetchRequestIfNeeded(
playlistId: String, playlistId: String,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
) { ) {
Objects.requireNonNull(playlistId) Objects.requireNonNull(playlistId)
synchronized(cache) { synchronized(cache) {
if (!cache.containsKey(playlistId)) { if (!cache.containsKey(playlistId)) {
cache[playlistId] = DeletePlaylistRequest( cache[playlistId] = DeletePlaylistRequest(
playlistId, playlistId,
requestHeader requestHeader,
dataSyncId,
) )
} }
} }
@ -104,7 +108,8 @@ class DeletePlaylistRequest private constructor(
private fun sendRequest( private fun sendRequest(
playlistId: String, playlistId: String,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
): JSONObject? { ): JSONObject? {
Objects.requireNonNull(playlistId) Objects.requireNonNull(playlistId)
@ -118,7 +123,8 @@ class DeletePlaylistRequest private constructor(
val connection = getInnerTubeResponseConnectionFromRoute( val connection = getInnerTubeResponseConnectionFromRoute(
DELETE_PLAYLIST, DELETE_PLAYLIST,
clientType, clientType,
requestHeader requestHeader,
dataSyncId
) )
val requestBody = deletePlaylistRequestBody(playlistId) val requestBody = deletePlaylistRequestBody(playlistId)
@ -163,9 +169,14 @@ class DeletePlaylistRequest private constructor(
private fun fetch( private fun fetch(
playlistId: String, playlistId: String,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
): Boolean? { ): Boolean? {
val json = sendRequest(playlistId, requestHeader) val json = sendRequest(
playlistId,
requestHeader,
dataSyncId,
)
if (json != null) { if (json != null) {
return parseResponse(json) return parseResponse(json)
} }

View File

@ -25,6 +25,7 @@ class EditPlaylistRequest private constructor(
private val playlistId: String, private val playlistId: String,
private val setVideoId: String?, private val setVideoId: String?,
private val requestHeader: Map<String, String>, private val requestHeader: Map<String, String>,
private val dataSyncId: String,
) { ) {
private val future: Future<String> = Utils.submitOnBackgroundThread { private val future: Future<String> = Utils.submitOnBackgroundThread {
fetch( fetch(
@ -32,6 +33,7 @@ class EditPlaylistRequest private constructor(
playlistId, playlistId,
setVideoId, setVideoId,
requestHeader, requestHeader,
dataSyncId,
) )
} }
@ -92,7 +94,8 @@ class EditPlaylistRequest private constructor(
videoId: String, videoId: String,
playlistId: String, playlistId: String,
setVideoId: String?, setVideoId: String?,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
) { ) {
Objects.requireNonNull(videoId) Objects.requireNonNull(videoId)
synchronized(cache) { synchronized(cache) {
@ -101,7 +104,8 @@ class EditPlaylistRequest private constructor(
videoId, videoId,
playlistId, playlistId,
setVideoId, setVideoId,
requestHeader requestHeader,
dataSyncId,
) )
} }
} }
@ -122,7 +126,8 @@ class EditPlaylistRequest private constructor(
videoId: String, videoId: String,
playlistId: String, playlistId: String,
setVideoId: String?, setVideoId: String?,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
): JSONObject? { ): JSONObject? {
Objects.requireNonNull(videoId) Objects.requireNonNull(videoId)
@ -136,7 +141,8 @@ class EditPlaylistRequest private constructor(
val connection = getInnerTubeResponseConnectionFromRoute( val connection = getInnerTubeResponseConnectionFromRoute(
EDIT_PLAYLIST, EDIT_PLAYLIST,
clientType, clientType,
requestHeader requestHeader,
dataSyncId
) )
val requestBody = editPlaylistRequestBody( val requestBody = editPlaylistRequestBody(
@ -199,9 +205,16 @@ class EditPlaylistRequest private constructor(
videoId: String, videoId: String,
playlistId: String, playlistId: String,
setVideoId: String?, setVideoId: String?,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
): String? { ): String? {
val json = sendRequest(videoId, playlistId, setVideoId, requestHeader) val json = sendRequest(
videoId,
playlistId,
setVideoId,
requestHeader,
dataSyncId,
)
if (json != null) { if (json != null) {
return parseResponse(json, StringUtils.isNotEmpty(setVideoId)) return parseResponse(json, StringUtils.isNotEmpty(setVideoId))
} }

View File

@ -22,11 +22,13 @@ import java.util.concurrent.TimeoutException
class GetPlaylistsRequest private constructor( class GetPlaylistsRequest private constructor(
private val playlistId: String, private val playlistId: String,
private val requestHeader: Map<String, String>, private val requestHeader: Map<String, String>,
private val dataSyncId: String,
) { ) {
private val future: Future<Array<Pair<String, String>>> = Utils.submitOnBackgroundThread { private val future: Future<Array<Pair<String, String>>> = Utils.submitOnBackgroundThread {
fetch( fetch(
playlistId, playlistId,
requestHeader, requestHeader,
dataSyncId,
) )
} }
@ -78,14 +80,16 @@ class GetPlaylistsRequest private constructor(
@JvmStatic @JvmStatic
fun fetchRequestIfNeeded( fun fetchRequestIfNeeded(
playlistId: String, playlistId: String,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
) { ) {
Objects.requireNonNull(playlistId) Objects.requireNonNull(playlistId)
synchronized(cache) { synchronized(cache) {
if (!cache.containsKey(playlistId)) { if (!cache.containsKey(playlistId)) {
cache[playlistId] = GetPlaylistsRequest( cache[playlistId] = GetPlaylistsRequest(
playlistId, playlistId,
requestHeader requestHeader,
dataSyncId,
) )
} }
} }
@ -104,7 +108,8 @@ class GetPlaylistsRequest private constructor(
private fun sendRequest( private fun sendRequest(
playlistId: String, playlistId: String,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
): JSONObject? { ): JSONObject? {
Objects.requireNonNull(playlistId) Objects.requireNonNull(playlistId)
@ -118,7 +123,8 @@ class GetPlaylistsRequest private constructor(
val connection = getInnerTubeResponseConnectionFromRoute( val connection = getInnerTubeResponseConnectionFromRoute(
GET_PLAYLISTS, GET_PLAYLISTS,
clientType, clientType,
requestHeader requestHeader,
dataSyncId
) )
val requestBody = getPlaylistsRequestBody(playlistId) val requestBody = getPlaylistsRequestBody(playlistId)
@ -202,9 +208,10 @@ class GetPlaylistsRequest private constructor(
private fun fetch( private fun fetch(
playlistId: String, playlistId: String,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
): Array<Pair<String, String>>? { ): Array<Pair<String, String>>? {
val json = sendRequest(playlistId, requestHeader) val json = sendRequest(playlistId, requestHeader, dataSyncId)
if (json != null) { if (json != null) {
return parseResponse(json) return parseResponse(json)
} }

View File

@ -23,12 +23,14 @@ class SavePlaylistRequest private constructor(
private val playlistId: String, private val playlistId: String,
private val libraryId: String, private val libraryId: String,
private val requestHeader: Map<String, String>, private val requestHeader: Map<String, String>,
private val dataSyncId: String,
) { ) {
private val future: Future<Boolean> = Utils.submitOnBackgroundThread { private val future: Future<Boolean> = Utils.submitOnBackgroundThread {
fetch( fetch(
playlistId, playlistId,
libraryId, libraryId,
requestHeader, requestHeader,
dataSyncId,
) )
} }
@ -81,14 +83,16 @@ class SavePlaylistRequest private constructor(
fun fetchRequestIfNeeded( fun fetchRequestIfNeeded(
playlistId: String, playlistId: String,
libraryId: String, libraryId: String,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
) { ) {
Objects.requireNonNull(playlistId) Objects.requireNonNull(playlistId)
synchronized(cache) { synchronized(cache) {
cache[libraryId] = SavePlaylistRequest( cache[libraryId] = SavePlaylistRequest(
playlistId, playlistId,
libraryId, libraryId,
requestHeader requestHeader,
dataSyncId,
) )
} }
} }
@ -107,7 +111,8 @@ class SavePlaylistRequest private constructor(
private fun sendRequest( private fun sendRequest(
playlistId: String, playlistId: String,
libraryId: String, libraryId: String,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
): JSONObject? { ): JSONObject? {
Objects.requireNonNull(playlistId) Objects.requireNonNull(playlistId)
Objects.requireNonNull(libraryId) Objects.requireNonNull(libraryId)
@ -122,7 +127,8 @@ class SavePlaylistRequest private constructor(
val connection = getInnerTubeResponseConnectionFromRoute( val connection = getInnerTubeResponseConnectionFromRoute(
EDIT_PLAYLIST, EDIT_PLAYLIST,
clientType, clientType,
requestHeader requestHeader,
dataSyncId
) )
val requestBody = savePlaylistRequestBody(libraryId, playlistId) val requestBody = savePlaylistRequestBody(libraryId, playlistId)
@ -168,9 +174,15 @@ class SavePlaylistRequest private constructor(
private fun fetch( private fun fetch(
playlistId: String, playlistId: String,
libraryId: String, libraryId: String,
requestHeader: Map<String, String> requestHeader: Map<String, String>,
dataSyncId: String,
): Boolean? { ): Boolean? {
val json = sendRequest(playlistId, libraryId, requestHeader) val json = sendRequest(
playlistId,
libraryId,
requestHeader,
dataSyncId,
)
if (json != null) { if (json != null) {
return parseResponse(json) return parseResponse(json)
} }

View File

@ -34,8 +34,10 @@ val playlistPatch = bytecodePatch(
execute { execute {
// In Incognito mode, sending a request always seems to fail. // In Incognito mode, sending a request always seems to fail.
accountIdentityFingerprint.methodOrThrow().addInstructions( accountIdentityFingerprint.methodOrThrow().addInstructions(
1, 1, """
"invoke-static/range {p4 .. p4}, $EXTENSION_CLASS_DESCRIPTOR->setIncognitoStatus(Z)V" sput-object p3, $EXTENSION_CLASS_DESCRIPTOR->dataSyncId:Ljava/lang/String;
sput-boolean p4, $EXTENSION_CLASS_DESCRIPTOR->isIncognito:Z
"""
) )
// Get the header to use the auth token. // Get the header to use the auth token.