Skip to content

Commit

Permalink
Add retry system for expired Youtube ciphers
Browse files Browse the repository at this point in the history
  • Loading branch information
Demeng7215 authored and Demeng7215 committed May 28, 2021
1 parent 8b65f55 commit 4947594
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 39 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser;
import com.sedmelluq.discord.lavaplayer.tools.io.HttpClientTools;
import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface;
import java.io.IOException;
import java.net.URLEncoder;
import org.apache.http.NameValuePair;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
Expand All @@ -18,6 +16,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URLEncoder;

import static com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeTrackJsonData.fromEmbedParts;
import static com.sedmelluq.discord.lavaplayer.tools.ExceptionTools.throwWithDebugInfo;
import static com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity.COMMON;
Expand Down Expand Up @@ -297,13 +298,25 @@ protected YoutubeTrackJsonData augmentWithPlayerScript(
}
}

protected static class CachedPlayerScript {
public final String playerScriptUrl;
public final long timestamp;
public CachedPlayerScript getCachedPlayerScript() {
return cachedPlayerScript;
}

public void clearCache() {
cachedPlayerScript = null;
}

public static class CachedPlayerScript {
private final String playerScriptUrl;
private final long timestamp;

public CachedPlayerScript(String playerScriptUrl, long timestamp) {
this.playerScriptUrl = playerScriptUrl;
this.timestamp = timestamp;
}

public String getPlayerScriptUrl() {
return playerScriptUrl;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

import com.sedmelluq.discord.lavaplayer.tools.io.HttpClientTools;
import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
Expand All @@ -18,12 +25,6 @@
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.io.IOUtils;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.utils.URIBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* Handles parsing and caching of signature ciphers
Expand Down Expand Up @@ -235,6 +236,10 @@ private YoutubeSignatureCipher extractTokensFromScript(String script, String sou
return cipherKey;
}

public void clearCache(String cipherScriptUrl) {
cipherCache.remove(cipherScriptUrl);
}

private static String extractDollarEscapedFirstGroup(Pattern pattern, String text) {
Matcher matcher = pattern.matcher(text);
return matcher.find() ? matcher.group(1).replace("$", "\\$") : null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@
import com.sedmelluq.discord.lavaplayer.format.AudioDataFormat;
import com.sedmelluq.discord.lavaplayer.player.AudioConfiguration;
import com.sedmelluq.discord.lavaplayer.player.AudioPlayerOptions;
import com.sedmelluq.discord.lavaplayer.source.youtube.DefaultYoutubeTrackDetailsLoader;
import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeAudioSourceManager;
import com.sedmelluq.discord.lavaplayer.source.youtube.YoutubeSignatureCipherManager;
import com.sedmelluq.discord.lavaplayer.tools.ExceptionTools;
import com.sedmelluq.discord.lavaplayer.tools.FriendlyException;
import com.sedmelluq.discord.lavaplayer.track.AudioTrackState;
Expand Down Expand Up @@ -30,6 +33,7 @@
*/
public class LocalAudioTrackExecutor implements AudioTrackExecutor {
private static final Logger log = LoggerFactory.getLogger(LocalAudioTrackExecutor.class);
private static final long RETRY_COOLDOWN = 5000L;

private final InternalAudioTrack audioTrack;
private final AudioProcessingContext processingContext;
Expand All @@ -45,6 +49,7 @@ public class LocalAudioTrackExecutor implements AudioTrackExecutor {
private long externalSeekPosition = -1;
private boolean interruptibleForSeek = false;
private volatile Throwable trackException;
private volatile long lastRetry = -1;

/**
* @param audioTrack The audio track that this executor executes
Expand Down Expand Up @@ -89,7 +94,6 @@ public AudioFrameBuffer getAudioBuffer() {

@Override
public void execute(TrackStateListener listener) {
InterruptedException interrupt = null;

if (Thread.interrupted()) {
log.debug("Cleared a stray interrupt.");
Expand All @@ -99,44 +103,75 @@ public void execute(TrackStateListener listener) {
log.debug("Starting to play track {} locally with listener {}", audioTrack.getInfo().identifier, listener);

state.set(AudioTrackState.LOADING);
attemptProcess(listener);

try {
audioTrack.process(this);
} else {
log.warn("Tried to start an already playing track {}", audioTrack.getIdentifier());
}
}

log.debug("Playing track {} finished or was stopped.", audioTrack.getIdentifier());
} catch (Throwable e) {
// Temporarily clear the interrupted status so it would not disrupt listener methods.
interrupt = findInterrupt(e);
private void attemptProcess(TrackStateListener listener) {

if (interrupt != null && checkStopped()) {
log.debug("Track {} was interrupted outside of execution loop.", audioTrack.getIdentifier());
} else {
frameBuffer.setTerminateOnEmpty();
InterruptedException interrupt = null;

FriendlyException exception = ExceptionTools.wrapUnfriendlyExceptions("Something broke when playing the track.", FAULT, e);
ExceptionTools.log(log, exception, "playback of " + audioTrack.getIdentifier());
try {
audioTrack.process(this);

trackException = exception;
listener.onTrackException(audioTrack, exception);
log.debug("Playing track {} finished or was stopped.", audioTrack.getIdentifier());
} catch (Throwable e) {

ExceptionTools.rethrowErrors(e);
}
} finally {
synchronized (actionSynchronizer) {
interrupt = interrupt != null ? interrupt : findInterrupt(null);
// Check for 403, attempt to clear cipher cache and retry if no retries in the past 5 seconds.
if (e.getMessage().contains("Not success status code: 403")
&& (lastRetry == -1 || lastRetry + RETRY_COOLDOWN <= System.currentTimeMillis())
&& audioTrack.getSourceManager() instanceof YoutubeAudioSourceManager) {
lastRetry = System.currentTimeMillis();

playingThread.compareAndSet(Thread.currentThread(), null);
log.debug("Detected 403, clearing cipher cache and retrying.");

markerTracker.trigger(ENDED);
state.set(AudioTrackState.FINISHED);
}
YoutubeAudioSourceManager sourceManager = (YoutubeAudioSourceManager) audioTrack.getSourceManager();
DefaultYoutubeTrackDetailsLoader trackDetailsLoader = (DefaultYoutubeTrackDetailsLoader) sourceManager.getTrackDetailsLoader();
DefaultYoutubeTrackDetailsLoader.CachedPlayerScript cachedScript = trackDetailsLoader.getCachedPlayerScript();

if (interrupt != null) {
Thread.currentThread().interrupt();
// Clear cached scripts and ciphers.
if (cachedScript != null) {
((YoutubeSignatureCipherManager) sourceManager.getSignatureResolver()).clearCache(cachedScript.getPlayerScriptUrl());
trackDetailsLoader.clearCache();
}

// Attempt to process again.
attemptProcess(listener);
return;
}

// Temporarily clear the interrupted status so it would not disrupt listener methods.
interrupt = findInterrupt(e);

if (interrupt != null && checkStopped()) {
log.debug("Track {} was interrupted outside of execution loop.", audioTrack.getIdentifier());
} else {
frameBuffer.setTerminateOnEmpty();

FriendlyException exception = ExceptionTools.wrapUnfriendlyExceptions("Something broke when playing the track.", FAULT, e);
ExceptionTools.log(log, exception, "playback of " + audioTrack.getIdentifier());

trackException = exception;
listener.onTrackException(audioTrack, exception);

ExceptionTools.rethrowErrors(e);
}
} finally {
synchronized (actionSynchronizer) {
interrupt = interrupt != null ? interrupt : findInterrupt(null);

playingThread.compareAndSet(Thread.currentThread(), null);

markerTracker.trigger(ENDED);
state.set(AudioTrackState.FINISHED);
}

if (interrupt != null) {
Thread.currentThread().interrupt();
}
} else {
log.warn("Tried to start an already playing track {}", audioTrack.getIdentifier());
}
}

Expand Down

0 comments on commit 4947594

Please sign in to comment.