From bfe630a302d993c0cd2389b37c7e89732e7e9414 Mon Sep 17 00:00:00 2001 From: duncte123 Date: Sun, 9 Jul 2023 11:12:25 +0200 Subject: [PATCH] Potentially fix tiktok playback --- build.gradle.kts | 2 +- .../dunctebot/sourcemanagers/Mp3Track.java | 4 ++ .../com/dunctebot/sourcemanagers/Utils.java | 23 +++++-- .../tiktok/TikTokAudioTrack.java | 67 +++---------------- .../tiktok/TikTokAudioTrackHttpManager.java | 29 ++------ 5 files changed, 38 insertions(+), 87 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 3df518f..ad4125e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -25,7 +25,7 @@ plugins { } project.group = "com.dunctebot" -project.version = "1.8.3" +project.version = "1.8.4" val archivesBaseName = "sourcemanagers" repositories { diff --git a/src/main/java/com/dunctebot/sourcemanagers/Mp3Track.java b/src/main/java/com/dunctebot/sourcemanagers/Mp3Track.java index 705a832..65c4636 100644 --- a/src/main/java/com/dunctebot/sourcemanagers/Mp3Track.java +++ b/src/main/java/com/dunctebot/sourcemanagers/Mp3Track.java @@ -63,6 +63,10 @@ protected InternalAudioTrack createAudioTrack(AudioTrackInfo trackInfo, Seekable return new Mp3AudioTrack(trackInfo, stream); } + /** + * A special helper to determine the length of the file in milliseconds. + * @return The clip length in milliseconds, for some sources this needs to be set to unknown for them to properly work. + */ protected long getTrackDuration() { return this.trackInfo.length; } diff --git a/src/main/java/com/dunctebot/sourcemanagers/Utils.java b/src/main/java/com/dunctebot/sourcemanagers/Utils.java index 1e3c0d9..496ca23 100644 --- a/src/main/java/com/dunctebot/sourcemanagers/Utils.java +++ b/src/main/java/com/dunctebot/sourcemanagers/Utils.java @@ -23,7 +23,7 @@ import java.nio.charset.Charset; public class Utils { - public static final String USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36"; + public static final String USER_AGENT = "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/114.0"; public static String urlDecode(String in) { return URLDecoder.decode(in, Charset.defaultCharset()); @@ -57,18 +57,27 @@ public static boolean isURL(String url) { } public static void fakeChrome(HttpRequest request) { + fakeChrome(request, false); + } + + public static void fakeChrome(HttpRequest request, boolean isVideo) { request.setHeader("Connection", "keep-alive"); request.setHeader("DNT", "1"); request.setHeader("Upgrade-Insecure-Requests", "1"); - request.setHeader("Accept", "*/*"); + request.setHeader("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,video/mp4,image/avif,image/webp,*/*;q=0.8"); // request.setHeader("Accept-Encoding", "gzip, deflate, br"); request.setHeader("Accept-Encoding", "none"); + request.setHeader("TE", "trailers"); request.setHeader("Accept-Language", "en-US,en;q=0.9"); - request.setHeader("Sec-Ch-Ua", "\" Not;A Brand\";v=\"99\", \"Google Chrome\";v=\"97\", \"Chromium\";v=\"97\""); - request.setHeader("Sec-Ch-Ua-Mobile", "?0"); - request.setHeader("Sec-Fetch-Dest", "document"); - request.setHeader("Sec-Fetch-Mode", "no-cors"); - request.setHeader("Sec-Fetch-Site", "cross-site"); + + if (isVideo) { + request.setHeader("Sec-Fetch-Dest", "empty"); + } else { + request.setHeader("Sec-Fetch-Dest", "document"); + } + + request.setHeader("Sec-Fetch-Mode", "cors"); + request.setHeader("Sec-Fetch-Site", "same-site"); request.setHeader("User-Agent", USER_AGENT); } diff --git a/src/main/java/com/dunctebot/sourcemanagers/tiktok/TikTokAudioTrack.java b/src/main/java/com/dunctebot/sourcemanagers/tiktok/TikTokAudioTrack.java index 41b54a9..1ecd854 100644 --- a/src/main/java/com/dunctebot/sourcemanagers/tiktok/TikTokAudioTrack.java +++ b/src/main/java/com/dunctebot/sourcemanagers/tiktok/TikTokAudioTrack.java @@ -21,27 +21,14 @@ import com.dunctebot.sourcemanagers.Pair; import com.sedmelluq.discord.lavaplayer.container.mp3.Mp3AudioTrack; import com.sedmelluq.discord.lavaplayer.tools.FriendlyException; -import com.sedmelluq.discord.lavaplayer.tools.JsonBrowser; import com.sedmelluq.discord.lavaplayer.tools.Units; import com.sedmelluq.discord.lavaplayer.tools.io.HttpInterface; -import com.sedmelluq.discord.lavaplayer.tools.io.PersistentHttpStream; import com.sedmelluq.discord.lavaplayer.tools.io.SeekableInputStream; import com.sedmelluq.discord.lavaplayer.track.AudioTrack; import com.sedmelluq.discord.lavaplayer.track.AudioTrackInfo; import com.sedmelluq.discord.lavaplayer.track.InternalAudioTrack; import com.sedmelluq.discord.lavaplayer.track.playback.LocalAudioTrackExecutor; -import org.apache.commons.io.IOUtils; -import org.apache.http.client.methods.CloseableHttpResponse; -import org.apache.http.client.methods.HttpGet; - -import java.io.File; -import java.io.IOException; -import java.net.URI; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.StandardCopyOption; - -import static com.dunctebot.sourcemanagers.Utils.fakeChrome; + import static com.sedmelluq.discord.lavaplayer.tools.FriendlyException.Severity.SUSPICIOUS; public class TikTokAudioTrack extends MpegTrack { @@ -80,7 +67,7 @@ public void process(LocalAudioTrackExecutor executor) throws Exception { } } - /*@Override + @Override protected void loadStream(LocalAudioTrackExecutor localExecutor, HttpInterface httpInterface) throws Exception { try { super.loadStream(localExecutor, httpInterface); @@ -92,10 +79,9 @@ protected void loadStream(LocalAudioTrackExecutor localExecutor, HttpInterface h this.failedOnce = true; super.loadStream(localExecutor, httpInterface); } - }*/ + } - // TODO: load the track url THROUGH the HTTP interface, see if that works - @Override + /*@Override protected void loadStream(LocalAudioTrackExecutor localExecutor, HttpInterface httpInterface) throws Exception { final String trackUrl = getPlaybackUrl(); log.debug("Starting {} track from URL: {}", getSourceManager().getSourceName(), trackUrl); @@ -110,10 +96,9 @@ protected void loadStream(LocalAudioTrackExecutor localExecutor, HttpInterface h processDelegate(createAudioTrack(this.trackInfo, stream), localExecutor); } - } + }*/ protected Pair loadPlaybackUrl() throws Exception { - final TikTokAudioSourceManager.MetaData metdata = this.getSourceManager().extractData( this.trackInfo.author, this.trackInfo.identifier @@ -125,43 +110,6 @@ protected Pair loadPlaybackUrl() throws Exception { ); } - /*protected MetaData extractFromJson(String url) throws IOException { - final Matcher matcher = VIDEO_REGEX.matcher(url); - - if (!matcher.matches()) { - throw new IOException("Url does not match tiktok anymore? wtf"); - } - - final String user = matcher.group("user"); - final String video = matcher.group("video"); - - final HttpGet httpGet = new HttpGet( - "https://www.tiktok.com/node/share/video/@" + user + '/' + video - ); - - fakeChrome(httpGet); - - try (final HttpInterface httpInterface = this.httpManager.getHttpInterface()) { - try (final CloseableHttpResponse response = httpInterface.execute(httpGet)) { - final int statusCode = response.getStatusLine().getStatusCode(); - - if (statusCode != 200) { - if (statusCode == 302) { // most likely a 404 - return null; - } - - throw new IOException("Unexpected status code for video page response: " + statusCode); - } - - final String string = IOUtils.toString(response.getEntity().getContent(), StandardCharsets.UTF_8); - final JsonBrowser json = JsonBrowser.parse(string); - final JsonBrowser base = json.get("itemInfo").get("itemStruct"); - - return getMetaData(url, base); - } - } - }*/ - @Override protected InternalAudioTrack createAudioTrack(AudioTrackInfo trackInfo, SeekableInputStream stream) { if (this.failedOnce && this.urlCache.getRight().contains(".mp3")) { @@ -171,6 +119,11 @@ protected InternalAudioTrack createAudioTrack(AudioTrackInfo trackInfo, Seekable return super.createAudioTrack(trackInfo, stream); } + @Override + protected long getTrackDuration() { + return Units.CONTENT_LENGTH_UNKNOWN; + } + @Override protected HttpInterface getHttpInterface() { return this.getSourceManager().getHttpInterface(); diff --git a/src/main/java/com/dunctebot/sourcemanagers/tiktok/TikTokAudioTrackHttpManager.java b/src/main/java/com/dunctebot/sourcemanagers/tiktok/TikTokAudioTrackHttpManager.java index 7404d10..601d654 100644 --- a/src/main/java/com/dunctebot/sourcemanagers/tiktok/TikTokAudioTrackHttpManager.java +++ b/src/main/java/com/dunctebot/sourcemanagers/tiktok/TikTokAudioTrackHttpManager.java @@ -20,33 +20,21 @@ 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 org.apache.http.Header; import org.apache.http.HttpResponse; import org.apache.http.client.CookieStore; -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.cookie.Cookie; -import org.apache.http.cookie.CookieOrigin; -import org.apache.http.cookie.CookieSpec; -import org.apache.http.cookie.MalformedCookieException; import org.apache.http.impl.client.BasicCookieStore; -import org.apache.http.impl.cookie.BrowserCompatSpec; -import org.apache.http.impl.cookie.DefaultCookieSpec; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; import java.util.stream.Collectors; import static com.dunctebot.sourcemanagers.Utils.fakeChrome; public class TikTokAudioTrackHttpManager implements AutoCloseable { - private String cookie = null; +// private String cookie = null; protected final HttpInterfaceManager httpInterfaceManager; - private final CookieSpec cookieSpec = new BrowserCompatSpec(); // DefaultCookieSpec does not parse (was BrowserCompatSpec) +// private final CookieSpec cookieSpec = new BrowserCompatSpec(); // DefaultCookieSpec does not parse (was BrowserCompatSpec) private final CookieStore cookieStore = new BasicCookieStore(); public TikTokAudioTrackHttpManager() { @@ -63,7 +51,7 @@ public HttpInterface getHttpInterface() { return httpInterfaceManager.getInterface(); } - protected void loadCookies() throws IOException, MalformedCookieException { + /*protected void loadCookies() throws IOException, MalformedCookieException { try (final HttpInterface httpInterface = httpInterfaceManager.getInterface()) { final HttpGet httpGet = new HttpGet("https://www.tiktok.com/"); @@ -81,7 +69,7 @@ protected void loadCookies() throws IOException, MalformedCookieException { .collect(Collectors.joining("; ")); } } - } + }*/ @Override public void close() throws Exception { @@ -102,19 +90,16 @@ public void onContextClose(HttpClientContext context) { @Override public void onRequest(HttpClientContext context, HttpUriRequest request, boolean isRepetition) { // set standard headers - fakeChrome(request); + final boolean isVideo = request.getURI().getPath().contains("video"); + fakeChrome(request, isVideo); - // TODO: set these cookies instead? final String testCookie = context.getCookieStore() .getCookies() .stream() .map((c) -> c.getName() + '=' + c.getValue()) .collect(Collectors.joining("; ")); - if (cookie != null) { - request.setHeader("cookie", cookie); - } - + request.setHeader("Cookie", testCookie); request.setHeader("Referer", "https://www.tiktok.com/"); }