Skip to content

Commit 6e6705f

Browse files
authored
Merge pull request #836 from TeamPiped/bg-helper
Implement pooling PoTokens support
2 parents 07785f1 + 26d60af commit 6e6705f

File tree

6 files changed

+113
-5
lines changed

6 files changed

+113
-5
lines changed

build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ dependencies {
1616
implementation 'it.unimi.dsi:fastutil-core:8.5.13'
1717
implementation 'commons-codec:commons-codec:1.17.0'
1818
implementation 'org.bouncycastle:bcprov-jdk18on:1.78.1'
19-
implementation 'com.github.FireMasterK.NewPipeExtractor:NewPipeExtractor:a64e202bb498032e817a702145263590829f3c1d'
19+
implementation 'com.github.FireMasterK.NewPipeExtractor:NewPipeExtractor:e45fa4d37f809a07dd5bdf6f920adabe0077704f'
2020
implementation 'com.github.FireMasterK:nanojson:9f4af3b739cc13f3d0d9d4b758bbe2b2ae7119d7'
2121
implementation 'com.fasterxml.jackson.core:jackson-core:2.17.2'
2222
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.2'

config.properties

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ MATRIX_SERVER:https://matrix-client.matrix.org
7373
# Geo Restriction Checker for federated bypassing of Geo Restrictions
7474
#GEO_RESTRICTION_CHECKER_URL:INSERT_HERE
7575

76+
# BG Helper URL for supplying PoTokens
77+
#BG_HELPER_URL:INSERT_HERE
78+
7679
# S3 Configuration Data (compatible with any provider that offers an S3 compatible API)
7780
#S3_ENDPOINT:INSERT_HERE
7881
#S3_ACCESS_KEY:INSERT_HERE

src/main/java/me/kavin/piped/Main.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import me.kavin.piped.utils.obj.db.PubSub;
1414
import me.kavin.piped.utils.obj.db.Video;
1515
import okhttp3.OkHttpClient;
16+
import org.apache.commons.lang3.StringUtils;
1617
import org.bouncycastle.jce.provider.BouncyCastleProvider;
1718
import org.hibernate.Session;
1819
import org.hibernate.StatelessSession;
@@ -21,6 +22,7 @@
2122
import org.schabi.newpipe.extractor.localization.Localization;
2223
import org.schabi.newpipe.extractor.services.youtube.YoutubeJavaScriptPlayerManager;
2324
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
25+
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
2426
import org.schabi.newpipe.extractor.stream.StreamInfo;
2527
import rocks.kavin.reqwest4j.ReqwestUtils;
2628

@@ -44,6 +46,8 @@ public static void main(String[] args) throws Exception {
4446
ReqwestUtils.init(REQWEST_PROXY, REQWEST_PROXY_USER, REQWEST_PROXY_PASS);
4547

4648
NewPipe.init(new DownloaderImpl(), new Localization("en", "US"), ContentCountry.DEFAULT);
49+
if (!StringUtils.isEmpty(Constants.BG_HELPER_URL))
50+
YoutubeStreamExtractor.setPoTokenProvider(new BgPoTokenProvider(Constants.BG_HELPER_URL));
4751
YoutubeParsingHelper.setConsentAccepted(CONSENT_COOKIE);
4852

4953
// Warm up the extractor
@@ -82,7 +86,7 @@ public static void main(String[] args) throws Exception {
8286
System.exit(1);
8387
}
8488

85-
Multithreading.runAsync(() -> Thread.ofVirtual().start(new SyncRunner(
89+
Multithreading.runAsync(() -> Thread.ofVirtual().start(new SyncRunner(
8690
new OkHttpClient.Builder().readTimeout(60, TimeUnit.SECONDS).build(),
8791
MATRIX_SERVER,
8892
MatrixHelper.MATRIX_TOKEN)

src/main/java/me/kavin/piped/consts/Constants.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828

2929
public class Constants {
3030

31-
public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; rv:102.0) Gecko/20100101 Firefox/102.0";
31+
public static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; rv:128.0) Gecko/20100101 Firefox/128.0";
3232

3333
public static final int PORT;
3434
public static final String HTTP_WORKERS;
@@ -100,6 +100,8 @@ public class Constants {
100100

101101
public static final String GEO_RESTRICTION_CHECKER_URL;
102102

103+
public static final String BG_HELPER_URL;
104+
103105
public static String YOUTUBE_COUNTRY;
104106

105107
public static final String VERSION;
@@ -170,6 +172,7 @@ public class Constants {
170172
MATRIX_SERVER = getProperty(prop, "MATRIX_SERVER", "https://matrix-client.matrix.org");
171173
MATRIX_TOKEN = getProperty(prop, "MATRIX_TOKEN");
172174
GEO_RESTRICTION_CHECKER_URL = getProperty(prop, "GEO_RESTRICTION_CHECKER_URL");
175+
BG_HELPER_URL = getProperty(prop, "BG_HELPER_URL");
173176
prop.forEach((_key, _value) -> {
174177
String key = String.valueOf(_key), value = String.valueOf(_value);
175178
if (key.startsWith("hibernate"))
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package me.kavin.piped.utils;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.jetbrains.annotations.Nullable;
5+
import org.schabi.newpipe.extractor.services.youtube.PoTokenProvider;
6+
import org.schabi.newpipe.extractor.services.youtube.PoTokenResult;
7+
import rocks.kavin.reqwest4j.ReqwestUtils;
8+
9+
import java.util.Map;
10+
import java.util.Queue;
11+
import java.util.concurrent.*;
12+
import java.util.regex.Pattern;
13+
14+
import static me.kavin.piped.consts.Constants.mapper;
15+
16+
@RequiredArgsConstructor
17+
public class BgPoTokenProvider implements PoTokenProvider {
18+
19+
private final String bgHelperUrl;
20+
21+
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
22+
23+
private String getWebVisitorData() throws Exception {
24+
var html = RequestUtils.sendGet("https://www.youtube.com").get();
25+
var matcher = Pattern.compile("visitorData\":\"([\\w%-]+)\"").matcher(html);
26+
27+
if (matcher.find()) {
28+
return matcher.group(1);
29+
}
30+
31+
throw new RuntimeException("Failed to get visitor data");
32+
}
33+
34+
private final Queue<PoTokenResult> validPoTokens = new ConcurrentLinkedQueue<>();
35+
36+
private PoTokenResult getPoTokenPooled() throws Exception {
37+
PoTokenResult poToken = validPoTokens.poll();
38+
39+
if (poToken == null) {
40+
poToken = createWebClientPoToken();
41+
}
42+
43+
// if still null, return null
44+
if (poToken == null) {
45+
return null;
46+
}
47+
48+
// timer to insert back into queue after 10 + random seconds
49+
int delay = 10_000 + ThreadLocalRandom.current().nextInt(5000);
50+
PoTokenResult finalPoToken = poToken;
51+
scheduler.schedule(() -> validPoTokens.offer(finalPoToken), delay, TimeUnit.MILLISECONDS);
52+
53+
return poToken;
54+
}
55+
56+
private PoTokenResult createWebClientPoToken() throws Exception {
57+
String visitorDate = getWebVisitorData();
58+
59+
String poToken = ReqwestUtils.fetch(bgHelperUrl + "/generate", "POST", mapper.writeValueAsBytes(mapper.createObjectNode().put(
60+
"visitorData", visitorDate
61+
)), Map.of(
62+
"Content-Type", "application/json"
63+
)).thenApply(response -> {
64+
try {
65+
return mapper.readTree(response.body()).get("poToken").asText();
66+
} catch (Exception e) {
67+
return null;
68+
}
69+
}).join();
70+
71+
if (poToken != null) {
72+
return new PoTokenResult(visitorDate, poToken);
73+
}
74+
75+
return null;
76+
}
77+
78+
@Override
79+
public @Nullable PoTokenResult getWebClientPoToken() {
80+
try {
81+
return getPoTokenPooled();
82+
} catch (Exception e) {
83+
e.printStackTrace();
84+
}
85+
return null;
86+
}
87+
88+
@Override
89+
public @Nullable PoTokenResult getAndroidClientPoToken() {
90+
// TODO: allow setting from config maybe?
91+
return null;
92+
}
93+
}

src/main/java/me/kavin/piped/utils/RequestUtils.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package me.kavin.piped.utils;
22

33
import com.fasterxml.jackson.databind.JsonNode;
4+
import me.kavin.piped.consts.Constants;
45
import okhttp3.OkHttpClient;
56
import okhttp3.Request;
67
import rocks.kavin.reqwest4j.ReqwestUtils;
@@ -15,11 +16,15 @@
1516
public class RequestUtils {
1617

1718
public static CompletableFuture<Response> sendGetRaw(String url) throws Exception {
18-
return ReqwestUtils.fetch(url, "GET", null, Map.of());
19+
return ReqwestUtils.fetch(url, "GET", null, Map.of(
20+
"User-Agent", Constants.USER_AGENT
21+
));
1922
}
2023

2124
public static CompletableFuture<String> sendGet(String url) throws Exception {
22-
return ReqwestUtils.fetch(url, "GET", null, Map.of())
25+
return ReqwestUtils.fetch(url, "GET", null, Map.of(
26+
"User-Agent", Constants.USER_AGENT
27+
))
2328
.thenApply(Response::body)
2429
.thenApplyAsync(String::new);
2530
}

0 commit comments

Comments
 (0)