mirror of
https://github.com/TeamNewPipe/NewPipeExtractor.git
synced 2025-04-27 15:30:34 +05:30
Merge pull request #1277 from litetex/fix-tests
Fix tests and cleanup other stuff
This commit is contained in:
commit
b1ecb6882f
@ -29,8 +29,6 @@ dependencies {
|
||||
implementation 'org.jsoup:jsoup:1.17.2'
|
||||
implementation "com.google.code.findbugs:jsr305:$jsr305Version"
|
||||
|
||||
// do not upgrade to 1.7.14, since in 1.7.14 Rhino uses the `SourceVersion` class, which is not
|
||||
// available on Android (even when using desugaring), and `NoClassDefFoundError` is thrown
|
||||
implementation 'org.mozilla:rhino:1.7.15'
|
||||
|
||||
checkstyle "com.puppycrawl.tools:checkstyle:$checkstyleVersion"
|
||||
@ -40,6 +38,6 @@ dependencies {
|
||||
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-params'
|
||||
|
||||
testImplementation "com.squareup.okhttp3:okhttp:3.12.13"
|
||||
testImplementation 'com.google.code.gson:gson:2.11.0'
|
||||
testImplementation "com.squareup.okhttp3:okhttp:4.12.0"
|
||||
testImplementation 'com.google.code.gson:gson:2.12.1'
|
||||
}
|
||||
|
@ -13,11 +13,12 @@ import org.schabi.newpipe.extractor.stream.Description;
|
||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||
import org.schabi.newpipe.extractor.utils.ExtractorHelper;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public final class PlaylistInfo extends ListInfo<StreamInfoItem> {
|
||||
|
||||
/**
|
||||
@ -45,7 +46,11 @@ public final class PlaylistInfo extends ListInfo<StreamInfoItem> {
|
||||
/**
|
||||
* A mix made only of streams from (or related to) the same channel, for example YouTube
|
||||
* channel mixes
|
||||
*
|
||||
* @deprecated There is currently no service that implements this.
|
||||
* YouTube removed all playlists with this type around 2024-06
|
||||
*/
|
||||
@Deprecated
|
||||
MIX_CHANNEL,
|
||||
|
||||
/**
|
||||
|
@ -48,6 +48,7 @@ public class BandcampSearchExtractor extends SearchExtractor {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfoItemsPage<InfoItem> getPage(final Page page)
|
||||
throws IOException, ExtractionException {
|
||||
final MultiInfoItemsCollector collector = new MultiInfoItemsCollector(getServiceId());
|
||||
@ -97,16 +98,12 @@ public class BandcampSearchExtractor extends SearchExtractor {
|
||||
}
|
||||
}
|
||||
|
||||
// Search results appear to be capped at six pages
|
||||
assert pages.size() < 10;
|
||||
|
||||
String nextUrl = null;
|
||||
if (currentPage < pages.size()) {
|
||||
nextUrl = page.getUrl().substring(0, page.getUrl().length() - 1) + (currentPage + 1);
|
||||
}
|
||||
|
||||
return new InfoItemsPage<>(collector, new Page(nextUrl));
|
||||
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@ -7,22 +7,24 @@ import static org.schabi.newpipe.extractor.services.bandcamp.extractors.Bandcamp
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import com.grack.nanojson.JsonParser;
|
||||
import com.grack.nanojson.JsonParserException;
|
||||
import com.grack.nanojson.JsonWriter;
|
||||
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.downloader.Downloader;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
import org.schabi.newpipe.extractor.suggestion.SuggestionExtractor;
|
||||
import org.schabi.newpipe.extractor.utils.Utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class BandcampSuggestionExtractor extends SuggestionExtractor {
|
||||
|
||||
private static final String AUTOCOMPLETE_URL = BASE_API_URL + "/fuzzysearch/1/autocomplete?q=";
|
||||
private static final String AUTOCOMPLETE_URL = BASE_API_URL
|
||||
+ "/bcsearch_public_api/1/autocomplete_elastic";
|
||||
public BandcampSuggestionExtractor(final StreamingService service) {
|
||||
super(service);
|
||||
}
|
||||
@ -33,7 +35,18 @@ public class BandcampSuggestionExtractor extends SuggestionExtractor {
|
||||
|
||||
try {
|
||||
final JsonObject fuzzyResults = JsonParser.object().from(downloader
|
||||
.get(AUTOCOMPLETE_URL + Utils.encodeUrlUtf8(query)).responseBody());
|
||||
.postWithContentTypeJson(
|
||||
AUTOCOMPLETE_URL,
|
||||
Collections.emptyMap(),
|
||||
JsonWriter.string()
|
||||
.object()
|
||||
.value("fan_id", (String) null)
|
||||
.value("full_page", false)
|
||||
.value("search_filter", "")
|
||||
.value("search_text", query)
|
||||
.end()
|
||||
.done()
|
||||
.getBytes(StandardCharsets.UTF_8)).responseBody());
|
||||
|
||||
return fuzzyResults.getObject("auto").getArray("results").stream()
|
||||
.filter(JsonObject.class::isInstance)
|
||||
|
@ -60,7 +60,6 @@ import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||
import org.schabi.newpipe.extractor.stream.AudioTrackType;
|
||||
import org.schabi.newpipe.extractor.utils.JsonUtils;
|
||||
import org.schabi.newpipe.extractor.utils.Parser;
|
||||
import org.schabi.newpipe.extractor.utils.ProtoBuilder;
|
||||
import org.schabi.newpipe.extractor.utils.RandomStringFromAlphabetGenerator;
|
||||
import org.schabi.newpipe.extractor.utils.Utils;
|
||||
|
||||
@ -235,23 +234,6 @@ public final class YoutubeParsingHelper {
|
||||
return url.getHost().equalsIgnoreCase("y2u.be");
|
||||
}
|
||||
|
||||
public static String randomVisitorData(final ContentCountry country) {
|
||||
final ProtoBuilder pbE2 = new ProtoBuilder();
|
||||
pbE2.string(2, "");
|
||||
pbE2.varint(4, numberGenerator.nextInt(255) + 1);
|
||||
|
||||
final ProtoBuilder pbE = new ProtoBuilder();
|
||||
pbE.string(1, country.getCountryCode());
|
||||
pbE.bytes(2, pbE2.toBytes());
|
||||
|
||||
final ProtoBuilder pb = new ProtoBuilder();
|
||||
pb.string(1, RandomStringFromAlphabetGenerator.generate(
|
||||
CONTENT_PLAYBACK_NONCE_ALPHABET, 11, numberGenerator));
|
||||
pb.varint(5, System.currentTimeMillis() / 1000 - numberGenerator.nextInt(600000));
|
||||
pb.bytes(6, pbE.toBytes());
|
||||
return pb.toUrlencodedBase64();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the duration string of the video expecting ":" or "." as separators
|
||||
*
|
||||
@ -359,16 +341,6 @@ public final class YoutubeParsingHelper {
|
||||
return playlistId.startsWith("RDAMVM") || playlistId.startsWith("RDCLAK");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given playlist id is a YouTube Channel Mix (auto-generated playlist)
|
||||
* Ids from a YouTube channel Mix start with "RDCM"
|
||||
*
|
||||
* @return Whether given id belongs to a YouTube Channel Mix
|
||||
*/
|
||||
public static boolean isYoutubeChannelMixId(@Nonnull final String playlistId) {
|
||||
return playlistId.startsWith("RDCM");
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given playlist id is a YouTube Genre Mix (auto-generated playlist)
|
||||
* Ids from a YouTube Genre Mix start with "RDGMEM"
|
||||
@ -399,11 +371,6 @@ public final class YoutubeParsingHelper {
|
||||
} else if (isYoutubeMusicMixId(playlistId)) {
|
||||
return playlistId.substring(6);
|
||||
|
||||
} else if (isYoutubeChannelMixId(playlistId)) {
|
||||
// Channel mixes are of the form RMCM{channelId}, so videoId can't be determined
|
||||
throw new ParsingException("Video id could not be determined from channel mix id: "
|
||||
+ playlistId);
|
||||
|
||||
} else if (isYoutubeGenreMixId(playlistId)) {
|
||||
// Genre mixes are of the form RDGMEM{garbage}, so videoId can't be determined
|
||||
throw new ParsingException("Video id could not be determined from genre mix id: "
|
||||
@ -438,8 +405,6 @@ public final class YoutubeParsingHelper {
|
||||
throw new ParsingException("Could not extract playlist type from empty playlist id");
|
||||
} else if (isYoutubeMusicMixId(playlistId)) {
|
||||
return PlaylistInfo.PlaylistType.MIX_MUSIC;
|
||||
} else if (isYoutubeChannelMixId(playlistId)) {
|
||||
return PlaylistInfo.PlaylistType.MIX_CHANNEL;
|
||||
} else if (isYoutubeGenreMixId(playlistId)) {
|
||||
return PlaylistInfo.PlaylistType.MIX_GENRE;
|
||||
} else if (isYoutubeMixId(playlistId)) { // normal mix
|
||||
|
@ -1,5 +1,6 @@
|
||||
package org.schabi.newpipe.extractor.services.youtube.extractors;
|
||||
|
||||
import com.grack.nanojson.JsonArray;
|
||||
import com.grack.nanojson.JsonObject;
|
||||
import org.schabi.newpipe.extractor.Image;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItemExtractor;
|
||||
@ -61,10 +62,16 @@ public class YoutubeMusicArtistInfoItemExtractor implements ChannelInfoItemExtra
|
||||
|
||||
@Override
|
||||
public long getSubscriberCount() throws ParsingException {
|
||||
final String subscriberCount = getTextFromObject(artistInfoItem.getArray("flexColumns")
|
||||
.getObject(2)
|
||||
final JsonArray flexColumns = artistInfoItem.getArray("flexColumns");
|
||||
final JsonArray runs = flexColumns
|
||||
.getObject(flexColumns.size() - 1)
|
||||
.getObject("musicResponsiveListItemFlexColumnRenderer")
|
||||
.getObject("text"));
|
||||
.getObject("text")
|
||||
.getArray("runs");
|
||||
// NOTE: YoutubeParsingHelper#getTextFromObject would use all entries from the run array,
|
||||
// which is not wanted as only the last entry contains the actual subscriberCount
|
||||
final String subscriberCount = runs.getObject(runs.size() - 1)
|
||||
.getString("text");
|
||||
if (!isNullOrEmpty(subscriberCount)) {
|
||||
try {
|
||||
return Utils.mixedNumberWordToLong(subscriberCount);
|
||||
|
@ -1,6 +1,5 @@
|
||||
package org.schabi.newpipe.extractor.services.youtube.linkHandler;
|
||||
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
|
||||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
|
||||
@ -57,14 +56,6 @@ public final class YoutubePlaylistLinkHandlerFactory extends ListLinkHandlerFact
|
||||
"the list-ID given in the URL does not match the list pattern");
|
||||
}
|
||||
|
||||
if (YoutubeParsingHelper.isYoutubeChannelMixId(listID)
|
||||
&& Utils.getQueryValue(urlObj, "v") == null) {
|
||||
// Video id can't be determined from the channel mix id.
|
||||
// See YoutubeParsingHelper#extractVideoIdFromMixId
|
||||
throw new ContentNotSupportedException(
|
||||
"Channel Mix without a video id are not supported");
|
||||
}
|
||||
|
||||
return listID;
|
||||
} catch (final Exception exception) {
|
||||
throw new ParsingException("Error could not parse URL: " + exception.getMessage(),
|
||||
|
@ -114,7 +114,7 @@ public final class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
|
||||
try {
|
||||
url = Utils.stringToURL(urlString);
|
||||
} catch (final MalformedURLException e) {
|
||||
throw new IllegalArgumentException("The given URL is not valid");
|
||||
throw new ParsingException("The given URL is not valid", e);
|
||||
}
|
||||
|
||||
final String host = url.getHost();
|
||||
|
@ -1,73 +0,0 @@
|
||||
package org.schabi.newpipe.extractor.utils;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Base64;
|
||||
|
||||
public class ProtoBuilder {
|
||||
ByteArrayOutputStream byteBuffer;
|
||||
|
||||
public ProtoBuilder() {
|
||||
this.byteBuffer = new ByteArrayOutputStream();
|
||||
}
|
||||
|
||||
public byte[] toBytes() {
|
||||
return byteBuffer.toByteArray();
|
||||
}
|
||||
|
||||
public String toUrlencodedBase64() {
|
||||
final String b64 = Base64.getUrlEncoder().encodeToString(toBytes());
|
||||
return URLEncoder.encode(b64, StandardCharsets.UTF_8);
|
||||
}
|
||||
|
||||
private void writeVarint(final long val) {
|
||||
try {
|
||||
if (val == 0) {
|
||||
byteBuffer.write(new byte[]{(byte) 0});
|
||||
} else {
|
||||
long v = val;
|
||||
while (v != 0) {
|
||||
byte b = (byte) (v & 0x7f);
|
||||
v >>= 7;
|
||||
|
||||
if (v != 0) {
|
||||
b |= (byte) 0x80;
|
||||
}
|
||||
byteBuffer.write(new byte[]{b});
|
||||
}
|
||||
}
|
||||
} catch (final IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void field(final int field, final byte wire) {
|
||||
final long fbits = ((long) field) << 3;
|
||||
final long wbits = ((long) wire) & 0x07;
|
||||
final long val = fbits | wbits;
|
||||
writeVarint(val);
|
||||
}
|
||||
|
||||
public void varint(final int field, final long val) {
|
||||
field(field, (byte) 0);
|
||||
writeVarint(val);
|
||||
}
|
||||
|
||||
public void string(final int field, final String string) {
|
||||
final byte[] strBts = string.getBytes(StandardCharsets.UTF_8);
|
||||
bytes(field, strBts);
|
||||
}
|
||||
|
||||
public void bytes(final int field, final byte[] bytes) {
|
||||
field(field, (byte) 2);
|
||||
writeVarint(bytes.length);
|
||||
try {
|
||||
byteBuffer.write(bytes);
|
||||
} catch (final IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
}
|
@ -36,7 +36,7 @@ public class DownloaderFactory {
|
||||
* @param path The path to the folder where mocks are saved/retrieved.
|
||||
* Preferably starting with {@link DownloaderFactory#RESOURCE_PATH}
|
||||
*/
|
||||
public static Downloader getDownloader(final String path) throws IOException {
|
||||
public static Downloader getDownloader(final String path) {
|
||||
final DownloaderType type = getDownloaderType();
|
||||
switch (type) {
|
||||
case REAL:
|
||||
|
@ -1,11 +1,13 @@
|
||||
package org.schabi.newpipe.downloader;
|
||||
|
||||
import org.schabi.newpipe.downloader.ratelimiting.RateLimitedClientWrapper;
|
||||
import org.schabi.newpipe.extractor.downloader.Downloader;
|
||||
import org.schabi.newpipe.extractor.downloader.Request;
|
||||
import org.schabi.newpipe.extractor.downloader.Response;
|
||||
import org.schabi.newpipe.extractor.exceptions.ReCaptchaException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@ -13,6 +15,7 @@ import java.util.concurrent.TimeUnit;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import okhttp3.ConnectionSpec;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.ResponseBody;
|
||||
@ -21,13 +24,19 @@ public final class DownloaderTestImpl extends Downloader {
|
||||
/**
|
||||
* Should be the latest Firefox ESR version.
|
||||
*/
|
||||
private static final String USER_AGENT
|
||||
= "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0";
|
||||
private static final String USER_AGENT =
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:128.0) Gecko/20100101 Firefox/128.0";
|
||||
private static DownloaderTestImpl instance;
|
||||
private final OkHttpClient client;
|
||||
private final RateLimitedClientWrapper clientWrapper;
|
||||
|
||||
private DownloaderTestImpl(final OkHttpClient.Builder builder) {
|
||||
this.client = builder.readTimeout(30, TimeUnit.SECONDS).build();
|
||||
this.clientWrapper = new RateLimitedClientWrapper(builder
|
||||
.readTimeout(30, TimeUnit.SECONDS)
|
||||
// Required for certain services
|
||||
// For example Bandcamp otherwise fails on Windows with Java 17+
|
||||
// as their Fastly-CDN returns 403
|
||||
.connectionSpecs(Arrays.asList(ConnectionSpec.RESTRICTED_TLS))
|
||||
.build());
|
||||
}
|
||||
|
||||
/**
|
||||
@ -59,45 +68,40 @@ public final class DownloaderTestImpl extends Downloader {
|
||||
|
||||
RequestBody requestBody = null;
|
||||
if (dataToSend != null) {
|
||||
requestBody = RequestBody.create(null, dataToSend);
|
||||
requestBody = RequestBody.create(dataToSend);
|
||||
}
|
||||
|
||||
final okhttp3.Request.Builder requestBuilder = new okhttp3.Request.Builder()
|
||||
.method(httpMethod, requestBody).url(url)
|
||||
.addHeader("User-Agent", USER_AGENT);
|
||||
.method(httpMethod, requestBody)
|
||||
.url(url)
|
||||
.addHeader("User-Agent", USER_AGENT);
|
||||
|
||||
for (Map.Entry<String, List<String>> pair : headers.entrySet()) {
|
||||
final String headerName = pair.getKey();
|
||||
final List<String> headerValueList = pair.getValue();
|
||||
headers.forEach((headerName, headerValueList) -> {
|
||||
requestBuilder.removeHeader(headerName);
|
||||
headerValueList.forEach(headerValue ->
|
||||
requestBuilder.addHeader(headerName, headerValue));
|
||||
});
|
||||
|
||||
if (headerValueList.size() > 1) {
|
||||
requestBuilder.removeHeader(headerName);
|
||||
for (String headerValue : headerValueList) {
|
||||
requestBuilder.addHeader(headerName, headerValue);
|
||||
}
|
||||
} else if (headerValueList.size() == 1) {
|
||||
requestBuilder.header(headerName, headerValueList.get(0));
|
||||
try (okhttp3.Response response =
|
||||
clientWrapper.executeRequestWithLimit(requestBuilder.build())
|
||||
) {
|
||||
if (response.code() == 429) {
|
||||
throw new ReCaptchaException("reCaptcha Challenge requested", url);
|
||||
}
|
||||
|
||||
String responseBodyToReturn = null;
|
||||
try (ResponseBody body = response.body()) {
|
||||
if (body != null) {
|
||||
responseBodyToReturn = body.string();
|
||||
}
|
||||
}
|
||||
|
||||
return new Response(
|
||||
response.code(),
|
||||
response.message(),
|
||||
response.headers().toMultimap(),
|
||||
responseBodyToReturn,
|
||||
response.request().url().toString());
|
||||
}
|
||||
|
||||
final okhttp3.Response response = client.newCall(requestBuilder.build()).execute();
|
||||
|
||||
if (response.code() == 429) {
|
||||
response.close();
|
||||
|
||||
throw new ReCaptchaException("reCaptcha Challenge requested", url);
|
||||
}
|
||||
|
||||
final ResponseBody body = response.body();
|
||||
String responseBodyToReturn = null;
|
||||
|
||||
if (body != null) {
|
||||
responseBodyToReturn = body.string();
|
||||
}
|
||||
|
||||
final String latestUrl = response.request().url().toString();
|
||||
return new Response(response.code(), response.message(), response.headers().toMultimap(),
|
||||
responseBodyToReturn, latestUrl);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
@ -26,19 +27,22 @@ class MockDownloader extends Downloader {
|
||||
private final String path;
|
||||
private final Map<Request, Response> mocks;
|
||||
|
||||
public MockDownloader(@Nonnull final String path) throws IOException {
|
||||
public MockDownloader(@Nonnull final String path) {
|
||||
this.path = path;
|
||||
this.mocks = new HashMap<>();
|
||||
final File[] files = new File(path).listFiles();
|
||||
if (files != null) {
|
||||
for (final File file : files) {
|
||||
if (file.getName().startsWith(RecordingDownloader.FILE_NAME_PREFIX)) {
|
||||
final InputStreamReader reader = new InputStreamReader(new FileInputStream(
|
||||
file), StandardCharsets.UTF_8);
|
||||
final TestRequestResponse response = new GsonBuilder()
|
||||
.create()
|
||||
.fromJson(reader, TestRequestResponse.class);
|
||||
reader.close();
|
||||
final TestRequestResponse response;
|
||||
try(final InputStreamReader reader = new InputStreamReader(
|
||||
new FileInputStream(file), StandardCharsets.UTF_8)) {
|
||||
response = new GsonBuilder()
|
||||
.create()
|
||||
.fromJson(reader, TestRequestResponse.class);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
mocks.put(response.getRequest(), response.getResponse());
|
||||
}
|
||||
}
|
||||
|
@ -11,11 +11,11 @@ import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.Random;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
@ -47,26 +47,12 @@ class RecordingDownloader extends Downloader {
|
||||
private int index = 0;
|
||||
private final String path;
|
||||
|
||||
// try to prevent ReCaptchaExceptions / rate limits by tracking and throttling the requests
|
||||
/**
|
||||
* The maximum number of requests per 20 seconds which are executed
|
||||
* by the {@link RecordingDownloader}.
|
||||
* 20 seconds is used as upper bound because the rate limit can be triggered within 30 seconds
|
||||
* and hitting the rate limit should be prevented because it comes with a bigger delay.
|
||||
* The values can be adjusted when executing the downloader and running into problems.
|
||||
* <p>TODO: Allow adjusting the value by setting a param in the gradle command</p>
|
||||
*/
|
||||
private static final int MAX_REQUESTS_PER_20_SECONDS = 30;
|
||||
private static final long[] requestTimes = new long[MAX_REQUESTS_PER_20_SECONDS];
|
||||
private static int requestTimesCursor = -1;
|
||||
private static final Random throttleRandom = new Random();
|
||||
|
||||
/**
|
||||
* Creates the folder described by {@code stringPath} if it does not exist.
|
||||
* Deletes existing files starting with {@link RecordingDownloader#FILE_NAME_PREFIX}.
|
||||
* @param stringPath Path to the folder where the json files will be saved to.
|
||||
*/
|
||||
public RecordingDownloader(final String stringPath) throws IOException {
|
||||
public RecordingDownloader(final String stringPath) {
|
||||
this.path = stringPath;
|
||||
final Path path = Paths.get(stringPath);
|
||||
final File folder = path.toFile();
|
||||
@ -77,78 +63,42 @@ class RecordingDownloader extends Downloader {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Files.createDirectories(path);
|
||||
try {
|
||||
Files.createDirectories(path);
|
||||
} catch (IOException e) {
|
||||
throw new UncheckedIOException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Response execute(@Nonnull final Request request) throws IOException,
|
||||
ReCaptchaException {
|
||||
|
||||
// Delay the execution if the max number of requests per minute is reached
|
||||
final long currentTime = System.currentTimeMillis();
|
||||
// the cursor points to the latest request time and the next position is the oldest one
|
||||
final int oldestRequestTimeCursor = (requestTimesCursor + 1) % requestTimes.length;
|
||||
final long oldestRequestTime = requestTimes[oldestRequestTimeCursor];
|
||||
if (oldestRequestTime + 20_000 >= currentTime) {
|
||||
try {
|
||||
// sleep at least until the oldest request is 20s old, but not more than 20s
|
||||
final int minSleepTime = (int) (currentTime - oldestRequestTime);
|
||||
Thread.sleep(minSleepTime + throttleRandom.nextInt(20_000 - minSleepTime));
|
||||
} catch (InterruptedException e) {
|
||||
// handle the exception gracefully because it's not critical for the test
|
||||
System.err.println("Error while throttling the RecordingDownloader.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
requestTimesCursor = oldestRequestTimeCursor; // the oldest value needs to be overridden
|
||||
requestTimes[requestTimesCursor] = System.currentTimeMillis();
|
||||
|
||||
// Handle ReCaptchaExceptions by retrying the request once after a while
|
||||
try {
|
||||
return executeRequest(request);
|
||||
} catch (ReCaptchaException e) {
|
||||
try {
|
||||
// sleep for 35-60 seconds to circumvent the rate limit
|
||||
System.out.println("Throttling the RecordingDownloader to handle a ReCaptcha."
|
||||
+ " Sleeping for 35-60 seconds.");
|
||||
Thread.sleep(35_000 + throttleRandom.nextInt(25_000));
|
||||
} catch (InterruptedException ie) {
|
||||
// handle the exception gracefully because it's not critical for the test
|
||||
System.err.println("Error while throttling the RecordingDownloader.");
|
||||
ie.printStackTrace();
|
||||
e.printStackTrace();
|
||||
}
|
||||
return executeRequest(request);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private Response executeRequest(@Nonnull final Request request) throws IOException,
|
||||
ReCaptchaException {
|
||||
final Downloader downloader = DownloaderTestImpl.getInstance();
|
||||
Response response = downloader.execute(request);
|
||||
String cleanedResponseBody = response.responseBody().replaceAll(IP_V4_PATTERN, "127.0.0.1");
|
||||
response = new Response(
|
||||
response.responseCode(),
|
||||
response.responseMessage(),
|
||||
response.responseHeaders(),
|
||||
cleanedResponseBody,
|
||||
response.latestUrl()
|
||||
response.responseCode(),
|
||||
response.responseMessage(),
|
||||
response.responseHeaders(),
|
||||
response.responseBody().replaceAll(IP_V4_PATTERN, "127.0.0.1"),
|
||||
response.latestUrl()
|
||||
);
|
||||
|
||||
final File outputFile = new File(path + File.separator + FILE_NAME_PREFIX + index
|
||||
+ ".json");
|
||||
final File outputFile = new File(
|
||||
path + File.separator + FILE_NAME_PREFIX + index + ".json");
|
||||
index++;
|
||||
outputFile.createNewFile();
|
||||
final OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(outputFile),
|
||||
StandardCharsets.UTF_8);
|
||||
new GsonBuilder()
|
||||
|
||||
try (final OutputStreamWriter writer = new OutputStreamWriter(
|
||||
new FileOutputStream(outputFile), StandardCharsets.UTF_8)) {
|
||||
|
||||
new GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
.toJson(new TestRequestResponse(request, response), writer);
|
||||
writer.flush();
|
||||
writer.close();
|
||||
|
||||
writer.flush();
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
@ -0,0 +1,84 @@
|
||||
package org.schabi.newpipe.downloader.ratelimiting;
|
||||
|
||||
import org.schabi.newpipe.downloader.ratelimiting.limiter.RateLimiter;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.ProtocolException;
|
||||
import java.time.Duration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.Response;
|
||||
|
||||
public class RateLimitedClientWrapper {
|
||||
private static final boolean DEBUG_PRINT =
|
||||
"1".equals(System.getProperty("rateLimitClientDebugPrint",
|
||||
System.getenv("RATE_LIMIT_CLIENT_DEBUG_PRINT")));
|
||||
|
||||
private static final int REQUEST_RATE_LIMITED_WAIT_MS = 5_000;
|
||||
private static final Map<Predicate<String>, RateLimiter> FORCED_RATE_LIMITERS = Map.ofEntries(
|
||||
Map.entry(host -> host.endsWith("youtube.com"),
|
||||
RateLimiter.create(1.6, Duration.ofSeconds(1))),
|
||||
Map.entry(host -> host.endsWith("bandcamp.com"),
|
||||
RateLimiter.create(2.5, Duration.ofSeconds(1)))
|
||||
);
|
||||
|
||||
private final OkHttpClient client;
|
||||
private final Map<String, RateLimiter> hostRateLimiters = new LinkedHashMap<>();
|
||||
|
||||
public RateLimitedClientWrapper(final OkHttpClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
protected RateLimiter getRateLimiterFor(final Request request) {
|
||||
return hostRateLimiters.computeIfAbsent(request.url().host(), host ->
|
||||
FORCED_RATE_LIMITERS.entrySet()
|
||||
.stream()
|
||||
.filter(e -> e.getKey().test(host))
|
||||
.findFirst()
|
||||
.map(Map.Entry::getValue)
|
||||
.orElseGet(() ->
|
||||
// Default rate limiter per domain
|
||||
RateLimiter.create(5, Duration.ofSeconds(3))));
|
||||
}
|
||||
|
||||
public Response executeRequestWithLimit(final Request request) throws IOException {
|
||||
Exception cause = null;
|
||||
for (int tries = 1; tries <= 3; tries++) {
|
||||
try {
|
||||
final double rateLimitedSec = getRateLimiterFor(request).acquire();
|
||||
if (DEBUG_PRINT) {
|
||||
System.out.println(
|
||||
"[RATE-LIMIT] Waited " + rateLimitedSec + "s for " + request.url());
|
||||
}
|
||||
|
||||
final Response response = client.newCall(request).execute();
|
||||
if(response.code() != 429) { // 429 = Too many requests
|
||||
return response;
|
||||
}
|
||||
cause = new IllegalStateException("HTTP 429 - Too many requests");
|
||||
} catch (final ProtocolException pre) {
|
||||
if (!pre.getMessage().startsWith("Too many follow-up")) { // -> Too many requests
|
||||
throw pre;
|
||||
}
|
||||
cause = pre;
|
||||
}
|
||||
|
||||
final int waitMs = REQUEST_RATE_LIMITED_WAIT_MS * tries;
|
||||
if (DEBUG_PRINT) {
|
||||
System.out.println(
|
||||
"[TOO-MANY-REQUESTS] Waiting " + waitMs + "ms for " + request.url());
|
||||
}
|
||||
try {
|
||||
Thread.sleep(waitMs);
|
||||
} catch (final InterruptedException iex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
throw new IllegalStateException(
|
||||
"Retrying/Rate-limiting for " + request.url() + " failed", cause);
|
||||
}
|
||||
}
|
@ -0,0 +1,497 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Guava Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package org.schabi.newpipe.downloader.ratelimiting.limiter;
|
||||
|
||||
import static java.lang.Math.max;
|
||||
import static java.util.concurrent.TimeUnit.MICROSECONDS;
|
||||
import static java.util.concurrent.TimeUnit.NANOSECONDS;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
|
||||
import org.schabi.newpipe.downloader.ratelimiting.limiter.SmoothRateLimiter.SmoothWarmingUp;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* A rate limiter. Conceptually, a rate limiter distributes permits at a configurable rate. Each
|
||||
* {@link #acquire()} blocks if necessary until a permit is available, and then takes it. Once
|
||||
* acquired, permits need not be released.
|
||||
*
|
||||
* <p>{@code RateLimiter} is safe for concurrent use: It will restrict the total rate of calls from
|
||||
* all threads. Note, however, that it does not guarantee fairness.
|
||||
*
|
||||
* <p>Rate limiters are often used to restrict the rate at which some physical or logical resource
|
||||
* is accessed. This is in contrast to {@link java.util.concurrent.Semaphore} which restricts the
|
||||
* number of concurrent accesses instead of the rate (note though that concurrency and rate are
|
||||
* closely related, e.g. see <a href="http://en.wikipedia.org/wiki/Little%27s_law">Little's
|
||||
* Law</a>).
|
||||
*
|
||||
* <p>A {@code RateLimiter} is defined primarily by the rate at which permits are issued. Absent
|
||||
* additional configuration, permits will be distributed at a fixed rate, defined in terms of
|
||||
* permits per second. Permits will be distributed smoothly, with the delay between individual
|
||||
* permits being adjusted to ensure that the configured rate is maintained.
|
||||
*
|
||||
* <p>It is possible to configure a {@code RateLimiter} to have a warmup period during which time
|
||||
* the permits issued each second steadily increases until it hits the stable rate.
|
||||
*
|
||||
* <p>As an example, imagine that we have a list of tasks to execute, but we don't want to submit
|
||||
* more than 2 per second:
|
||||
*
|
||||
* <pre>{@code
|
||||
* final RateLimiter rateLimiter = RateLimiter.create(2.0); // rate is "2 permits per second"
|
||||
* void submitTasks(List<Runnable> tasks, Executor executor) {
|
||||
* for (Runnable task : tasks) {
|
||||
* rateLimiter.acquire(); // may wait
|
||||
* executor.execute(task);
|
||||
* }
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>As another example, imagine that we produce a stream of data, and we want to cap it at 5kb per
|
||||
* second. This could be accomplished by requiring a permit per byte, and specifying a rate of 5000
|
||||
* permits per second:
|
||||
*
|
||||
* <pre>{@code
|
||||
* final RateLimiter rateLimiter = RateLimiter.create(5000.0); // rate = 5000 permits per second
|
||||
* void submitPacket(byte[] packet) {
|
||||
* rateLimiter.acquire(packet.length);
|
||||
* networkService.send(packet);
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* <p>It is important to note that the number of permits requested <i>never</i> affects the
|
||||
* throttling of the request itself (an invocation to {@code acquire(1)} and an invocation to {@code
|
||||
* acquire(1000)} will result in exactly the same throttling, if any), but it affects the throttling
|
||||
* of the <i>next</i> request. I.e., if an expensive task arrives at an idle RateLimiter, it will be
|
||||
* granted immediately, but it is the <i>next</i> request that will experience extra throttling,
|
||||
* thus paying for the cost of the expensive task.
|
||||
*
|
||||
* @author Dimitris Andreou
|
||||
* @since 13.0
|
||||
*/
|
||||
public abstract class RateLimiter {
|
||||
public static final double DEFAULT_COLD_FACTOR = 3.0;
|
||||
|
||||
/**
|
||||
* Creates a {@code RateLimiter} with the specified stable throughput, given as "permits per
|
||||
* second" (commonly referred to as <i>QPS</i>, queries per second), and a <i>warmup period</i>,
|
||||
* during which the {@code RateLimiter} smoothly ramps up its rate, until it reaches its maximum
|
||||
* rate at the end of the period (as long as there are enough requests to saturate it). Similarly,
|
||||
* if the {@code RateLimiter} is left <i>unused</i> for a duration of {@code warmupPeriod}, it
|
||||
* will gradually return to its "cold" state, i.e. it will go through the same warming up process
|
||||
* as when it was first created.
|
||||
*
|
||||
* <p>The returned {@code RateLimiter} is intended for cases where the resource that actually
|
||||
* fulfills the requests (e.g., a remote server) needs "warmup" time, rather than being
|
||||
* immediately accessed at the stable (maximum) rate.
|
||||
*
|
||||
* <p>The returned {@code RateLimiter} starts in a "cold" state (i.e. the warmup period will
|
||||
* follow), and if it is left unused for long enough, it will return to that state.
|
||||
*
|
||||
* @param permitsPerSecond the rate of the returned {@code RateLimiter}, measured in how many
|
||||
* permits become available per second
|
||||
* @param warmupPeriod the duration of the period where the {@code RateLimiter} ramps up its rate,
|
||||
* before reaching its stable (maximum) rate
|
||||
* @throws IllegalArgumentException if {@code permitsPerSecond} is negative or zero or {@code
|
||||
* warmupPeriod} is negative
|
||||
* @since 28.0 (but only since 33.4.0 in the Android flavor)
|
||||
*/
|
||||
public static RateLimiter create(
|
||||
final double permitsPerSecond,
|
||||
final Duration warmupPeriod
|
||||
) {
|
||||
return create(permitsPerSecond, warmupPeriod, DEFAULT_COLD_FACTOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code RateLimiter} with the specified stable throughput, given as "permits per
|
||||
* second" (commonly referred to as <i>QPS</i>, queries per second), and a <i>warmup period</i>,
|
||||
* during which the {@code RateLimiter} smoothly ramps up its rate, until it reaches its maximum
|
||||
* rate at the end of the period (as long as there are enough requests to saturate it). Similarly,
|
||||
* if the {@code RateLimiter} is left <i>unused</i> for a duration of {@code warmupPeriod}, it
|
||||
* will gradually return to its "cold" state, i.e. it will go through the same warming up process
|
||||
* as when it was first created.
|
||||
*
|
||||
* <p>The returned {@code RateLimiter} is intended for cases where the resource that actually
|
||||
* fulfills the requests (e.g., a remote server) needs "warmup" time, rather than being
|
||||
* immediately accessed at the stable (maximum) rate.
|
||||
*
|
||||
* <p>The returned {@code RateLimiter} starts in a "cold" state (i.e. the warmup period will
|
||||
* follow), and if it is left unused for long enough, it will return to that state.
|
||||
*
|
||||
* @param permitsPerSecond the rate of the returned {@code RateLimiter}, measured in how many
|
||||
* permits become available per second
|
||||
* @param warmupPeriod the duration of the period where the {@code RateLimiter} ramps up its rate,
|
||||
* before reaching its stable (maximum) rate
|
||||
* @param coldFactor see {@link SmoothWarmingUp}
|
||||
* @throws IllegalArgumentException if {@code permitsPerSecond} is negative or zero or {@code
|
||||
* warmupPeriod} is negative
|
||||
* @since 28.0 (but only since 33.4.0 in the Android flavor)
|
||||
*/
|
||||
public static RateLimiter create(
|
||||
final double permitsPerSecond,
|
||||
final Duration warmupPeriod,
|
||||
final double coldFactor
|
||||
) {
|
||||
return create(permitsPerSecond, warmupPeriod.toNanos(), TimeUnit.NANOSECONDS, coldFactor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@code RateLimiter} with the specified stable throughput, given as "permits per
|
||||
* second" (commonly referred to as <i>QPS</i>, queries per second), and a <i>warmup period</i>,
|
||||
* during which the {@code RateLimiter} smoothly ramps up its rate, until it reaches its maximum
|
||||
* rate at the end of the period (as long as there are enough requests to saturate it). Similarly,
|
||||
* if the {@code RateLimiter} is left <i>unused</i> for a duration of {@code warmupPeriod}, it
|
||||
* will gradually return to its "cold" state, i.e. it will go through the same warming up process
|
||||
* as when it was first created.
|
||||
*
|
||||
* <p>The returned {@code RateLimiter} is intended for cases where the resource that actually
|
||||
* fulfills the requests (e.g., a remote server) needs "warmup" time, rather than being
|
||||
* immediately accessed at the stable (maximum) rate.
|
||||
*
|
||||
* <p>The returned {@code RateLimiter} starts in a "cold" state (i.e. the warmup period will
|
||||
* follow), and if it is left unused for long enough, it will return to that state.
|
||||
*
|
||||
* @param permitsPerSecond the rate of the returned {@code RateLimiter}, measured in how many
|
||||
* permits become available per second
|
||||
* @param warmupPeriod the duration of the period where the {@code RateLimiter} ramps up its rate,
|
||||
* before reaching its stable (maximum) rate
|
||||
* @param unit the time unit of the warmupPeriod argument
|
||||
* @param coldFactor see {@link SmoothWarmingUp}
|
||||
* @throws IllegalArgumentException if {@code permitsPerSecond} is negative or zero or {@code
|
||||
* warmupPeriod} is negative
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // should accept a java.time.Duration
|
||||
public static RateLimiter create(
|
||||
final double permitsPerSecond,
|
||||
final long warmupPeriod,
|
||||
final TimeUnit unit,
|
||||
final double coldFactor
|
||||
) {
|
||||
if(warmupPeriod < 0) {
|
||||
throw new IllegalArgumentException(
|
||||
"warmupPeriod must not be negative: " + warmupPeriod);
|
||||
}
|
||||
return create(
|
||||
permitsPerSecond, warmupPeriod, unit, coldFactor, SleepingStopwatch.createFromSystemTimer());
|
||||
}
|
||||
|
||||
static RateLimiter create(
|
||||
final double permitsPerSecond,
|
||||
final long warmupPeriod,
|
||||
final TimeUnit unit,
|
||||
final double coldFactor,
|
||||
final SleepingStopwatch stopwatch) {
|
||||
final RateLimiter rateLimiter = new SmoothWarmingUp(stopwatch, warmupPeriod, unit, coldFactor);
|
||||
rateLimiter.setRate(permitsPerSecond);
|
||||
return rateLimiter;
|
||||
}
|
||||
|
||||
/**
|
||||
* The underlying timer; used both to measure elapsed time and sleep as necessary. A separate
|
||||
* object to facilitate testing.
|
||||
*/
|
||||
private final SleepingStopwatch stopwatch;
|
||||
|
||||
// Can't be initialized in the constructor because mocks don't call the constructor.
|
||||
private volatile Object mutexDoNotUseDirectly;
|
||||
|
||||
private Object mutex() {
|
||||
Object mutex = mutexDoNotUseDirectly;
|
||||
if (mutex == null) {
|
||||
synchronized (this) {
|
||||
mutex = mutexDoNotUseDirectly;
|
||||
if (mutex == null) {
|
||||
mutexDoNotUseDirectly = mutex = new Object();
|
||||
}
|
||||
}
|
||||
}
|
||||
return mutex;
|
||||
}
|
||||
|
||||
RateLimiter(final SleepingStopwatch stopwatch) {
|
||||
this.stopwatch = Objects.requireNonNull(stopwatch);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the stable rate of this {@code RateLimiter}, that is, the {@code permitsPerSecond}
|
||||
* argument provided in the factory method that constructed the {@code RateLimiter}. Currently
|
||||
* throttled threads will <b>not</b> be awakened as a result of this invocation, thus they do not
|
||||
* observe the new rate; only subsequent requests will.
|
||||
*
|
||||
* <p>Note though that, since each request repays (by waiting, if necessary) the cost of the
|
||||
* <i>previous</i> request, this means that the very next request after an invocation to {@code
|
||||
* setRate} will not be affected by the new rate; it will pay the cost of the previous request,
|
||||
* which is in terms of the previous rate.
|
||||
*
|
||||
* <p>The behavior of the {@code RateLimiter} is not modified in any other way, e.g. if the {@code
|
||||
* RateLimiter} was configured with a warmup period of 20 seconds, it still has a warmup period of
|
||||
* 20 seconds after this method invocation.
|
||||
*
|
||||
* @param permitsPerSecond the new stable rate of this {@code RateLimiter}
|
||||
* @throws IllegalArgumentException if {@code permitsPerSecond} is negative or zero
|
||||
*/
|
||||
public final void setRate(final double permitsPerSecond) {
|
||||
if(permitsPerSecond <= 0.0) {
|
||||
throw new IllegalArgumentException("rate must be positive");
|
||||
}
|
||||
synchronized (mutex()) {
|
||||
doSetRate(permitsPerSecond, stopwatch.readMicros());
|
||||
}
|
||||
}
|
||||
|
||||
abstract void doSetRate(double permitsPerSecond, long nowMicros);
|
||||
|
||||
/**
|
||||
* Returns the stable rate (as {@code permits per seconds}) with which this {@code RateLimiter} is
|
||||
* configured with. The initial value of this is the same as the {@code permitsPerSecond} argument
|
||||
* passed in the factory method that produced this {@code RateLimiter}, and it is only updated
|
||||
* after invocations to {@linkplain #setRate}.
|
||||
*/
|
||||
public final double getRate() {
|
||||
synchronized (mutex()) {
|
||||
return doGetRate();
|
||||
}
|
||||
}
|
||||
|
||||
abstract double doGetRate();
|
||||
|
||||
/**
|
||||
* Acquires a single permit from this {@code RateLimiter}, blocking until the request can be
|
||||
* granted. Tells the amount of time slept, if any.
|
||||
*
|
||||
* <p>This method is equivalent to {@code acquire(1)}.
|
||||
*
|
||||
* @return time spent sleeping to enforce rate, in seconds; 0.0 if not rate-limited
|
||||
* @since 16.0 (present in 13.0 with {@code void} return type})
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public double acquire() {
|
||||
return acquire(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires the given number of permits from this {@code RateLimiter}, blocking until the request
|
||||
* can be granted. Tells the amount of time slept, if any.
|
||||
*
|
||||
* @param permits the number of permits to acquire
|
||||
* @return time spent sleeping to enforce rate, in seconds; 0.0 if not rate-limited
|
||||
* @throws IllegalArgumentException if the requested number of permits is negative or zero
|
||||
* @since 16.0 (present in 13.0 with {@code void} return type})
|
||||
*/
|
||||
@CanIgnoreReturnValue
|
||||
public double acquire(final int permits) {
|
||||
final long microsToWait = reserve(permits);
|
||||
stopwatch.sleepMicrosUninterruptibly(microsToWait);
|
||||
return 1.0 * microsToWait / SECONDS.toMicros(1L);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserves the given number of permits from this {@code RateLimiter} for future use, returning
|
||||
* the number of microseconds until the reservation can be consumed.
|
||||
*
|
||||
* @return time in microseconds to wait until the resource can be acquired, never negative
|
||||
*/
|
||||
final long reserve(final int permits) {
|
||||
checkPermits(permits);
|
||||
synchronized (mutex()) {
|
||||
return reserveAndGetWaitLength(permits, stopwatch.readMicros());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires a permit from this {@code RateLimiter} if it can be obtained without exceeding the
|
||||
* specified {@code timeout}, or returns {@code false} immediately (without waiting) if the permit
|
||||
* would not have been granted before the timeout expired.
|
||||
*
|
||||
* <p>This method is equivalent to {@code tryAcquire(1, timeout)}.
|
||||
*
|
||||
* @param timeout the maximum time to wait for the permit. Negative values are treated as zero.
|
||||
* @return {@code true} if the permit was acquired, {@code false} otherwise
|
||||
* @throws IllegalArgumentException if the requested number of permits is negative or zero
|
||||
* @since 28.0 (but only since 33.4.0 in the Android flavor)
|
||||
*/
|
||||
public boolean tryAcquire(final Duration timeout) {
|
||||
return tryAcquire(1, timeout.toNanos(), TimeUnit.NANOSECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires a permit from this {@code RateLimiter} if it can be obtained without exceeding the
|
||||
* specified {@code timeout}, or returns {@code false} immediately (without waiting) if the permit
|
||||
* would not have been granted before the timeout expired.
|
||||
*
|
||||
* <p>This method is equivalent to {@code tryAcquire(1, timeout, unit)}.
|
||||
*
|
||||
* @param timeout the maximum time to wait for the permit. Negative values are treated as zero.
|
||||
* @param unit the time unit of the timeout argument
|
||||
* @return {@code true} if the permit was acquired, {@code false} otherwise
|
||||
* @throws IllegalArgumentException if the requested number of permits is negative or zero
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // should accept a java.time.Duration
|
||||
public boolean tryAcquire(final long timeout, final TimeUnit unit) {
|
||||
return tryAcquire(1, timeout, unit);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires permits from this {@link RateLimiter} if it can be acquired immediately without delay.
|
||||
*
|
||||
* <p>This method is equivalent to {@code tryAcquire(permits, 0, anyUnit)}.
|
||||
*
|
||||
* @param permits the number of permits to acquire
|
||||
* @return {@code true} if the permits were acquired, {@code false} otherwise
|
||||
* @throws IllegalArgumentException if the requested number of permits is negative or zero
|
||||
* @since 14.0
|
||||
*/
|
||||
public boolean tryAcquire(final int permits) {
|
||||
return tryAcquire(permits, 0, MICROSECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires a permit from this {@link RateLimiter} if it can be acquired immediately without
|
||||
* delay.
|
||||
*
|
||||
* <p>This method is equivalent to {@code tryAcquire(1)}.
|
||||
*
|
||||
* @return {@code true} if the permit was acquired, {@code false} otherwise
|
||||
* @since 14.0
|
||||
*/
|
||||
public boolean tryAcquire() {
|
||||
return tryAcquire(1, 0, MICROSECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires the given number of permits from this {@code RateLimiter} if it can be obtained
|
||||
* without exceeding the specified {@code timeout}, or returns {@code false} immediately (without
|
||||
* waiting) if the permits would not have been granted before the timeout expired.
|
||||
*
|
||||
* @param permits the number of permits to acquire
|
||||
* @param timeout the maximum time to wait for the permits. Negative values are treated as zero.
|
||||
* @return {@code true} if the permits were acquired, {@code false} otherwise
|
||||
* @throws IllegalArgumentException if the requested number of permits is negative or zero
|
||||
* @since 28.0 (but only since 33.4.0 in the Android flavor)
|
||||
*/
|
||||
public boolean tryAcquire(final int permits, final Duration timeout) {
|
||||
return tryAcquire(permits, timeout.toNanos(), TimeUnit.NANOSECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquires the given number of permits from this {@code RateLimiter} if it can be obtained
|
||||
* without exceeding the specified {@code timeout}, or returns {@code false} immediately (without
|
||||
* waiting) if the permits would not have been granted before the timeout expired.
|
||||
*
|
||||
* @param permits the number of permits to acquire
|
||||
* @param timeout the maximum time to wait for the permits. Negative values are treated as zero.
|
||||
* @param unit the time unit of the timeout argument
|
||||
* @return {@code true} if the permits were acquired, {@code false} otherwise
|
||||
* @throws IllegalArgumentException if the requested number of permits is negative or zero
|
||||
*/
|
||||
@SuppressWarnings("GoodTime") // should accept a java.time.Duration
|
||||
public boolean tryAcquire(final int permits, final long timeout, final TimeUnit unit) {
|
||||
final long timeoutMicros = max(unit.toMicros(timeout), 0);
|
||||
checkPermits(permits);
|
||||
final long microsToWait;
|
||||
synchronized (mutex()) {
|
||||
final long nowMicros = stopwatch.readMicros();
|
||||
if (!canAcquire(nowMicros, timeoutMicros)) {
|
||||
return false;
|
||||
} else {
|
||||
microsToWait = reserveAndGetWaitLength(permits, nowMicros);
|
||||
}
|
||||
}
|
||||
stopwatch.sleepMicrosUninterruptibly(microsToWait);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean canAcquire(final long nowMicros, final long timeoutMicros) {
|
||||
return queryEarliestAvailable(nowMicros) - timeoutMicros <= nowMicros;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reserves next ticket and returns the wait time that the caller must wait for.
|
||||
*
|
||||
* @return the required wait time, never negative
|
||||
*/
|
||||
final long reserveAndGetWaitLength(final int permits, final long nowMicros) {
|
||||
final long momentAvailable = reserveEarliestAvailable(permits, nowMicros);
|
||||
return max(momentAvailable - nowMicros, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the earliest time that permits are available (with one caveat).
|
||||
*
|
||||
* @return the time that permits are available, or, if permits are available immediately, an
|
||||
* arbitrary past or present time
|
||||
*/
|
||||
abstract long queryEarliestAvailable(long nowMicros);
|
||||
|
||||
/**
|
||||
* Reserves the requested number of permits and returns the time that those permits can be used
|
||||
* (with one caveat).
|
||||
*
|
||||
* @return the time that the permits may be used, or, if the permits may be used immediately, an
|
||||
* arbitrary past or present time
|
||||
*/
|
||||
abstract long reserveEarliestAvailable(int permits, long nowMicros);
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.format(Locale.ROOT, "RateLimiter[stableRate=%3.1fqps]", getRate());
|
||||
}
|
||||
|
||||
abstract static class SleepingStopwatch {
|
||||
/** Constructor for use by subclasses. */
|
||||
protected SleepingStopwatch() {}
|
||||
|
||||
protected abstract long readMicros();
|
||||
|
||||
protected abstract void sleepMicrosUninterruptibly(long micros);
|
||||
|
||||
public static SleepingStopwatch createFromSystemTimer() {
|
||||
return new SleepingStopwatch() {
|
||||
final long startTimeNano = System.nanoTime();
|
||||
|
||||
@Override
|
||||
protected long readMicros() {
|
||||
return MICROSECONDS.convert(System.nanoTime() - startTimeNano, NANOSECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sleepMicrosUninterruptibly(final long micros) {
|
||||
if (micros > 0) {
|
||||
final long sleepMs = MICROSECONDS.toMillis(micros);
|
||||
if (sleepMs > 0) {
|
||||
try {
|
||||
Thread.sleep(sleepMs);
|
||||
} catch (final InterruptedException iex) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkPermits(final int permits) {
|
||||
if(permits <= 0.0) {
|
||||
throw new IllegalArgumentException(
|
||||
"Requested permits (" + permits + ") must be positive");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,368 @@
|
||||
/*
|
||||
* Copyright (C) 2012 The Guava Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
||||
* in compliance with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software distributed under the License
|
||||
* is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
|
||||
* or implied. See the License for the specific language governing permissions and limitations under
|
||||
* the License.
|
||||
*/
|
||||
|
||||
package org.schabi.newpipe.downloader.ratelimiting.limiter;
|
||||
|
||||
import static java.lang.Math.min;
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
abstract class SmoothRateLimiter extends RateLimiter {
|
||||
/*
|
||||
* How is the RateLimiter designed, and why?
|
||||
*
|
||||
* The primary feature of a RateLimiter is its "stable rate", the maximum rate that it should
|
||||
* allow in normal conditions. This is enforced by "throttling" incoming requests as needed. For
|
||||
* example, we could compute the appropriate throttle time for an incoming request, and make the
|
||||
* calling thread wait for that time.
|
||||
*
|
||||
* The simplest way to maintain a rate of QPS is to keep the timestamp of the last granted
|
||||
* request, and ensure that (1/QPS) seconds have elapsed since then. For example, for a rate of
|
||||
* QPS=5 (5 tokens per second), if we ensure that a request isn't granted earlier than 200ms after
|
||||
* the last one, then we achieve the intended rate. If a request comes and the last request was
|
||||
* granted only 100ms ago, then we wait for another 100ms. At this rate, serving 15 fresh permits
|
||||
* (i.e. for an acquire(15) request) naturally takes 3 seconds.
|
||||
*
|
||||
* It is important to realize that such a RateLimiter has a very superficial memory of the past:
|
||||
* it only remembers the last request. What if the RateLimiter was unused for a long period of
|
||||
* time, then a request arrived and was immediately granted? This RateLimiter would immediately
|
||||
* forget about that past underutilization. This may result in either underutilization or
|
||||
* overflow, depending on the real world consequences of not using the expected rate.
|
||||
*
|
||||
* Past underutilization could mean that excess resources are available. Then, the RateLimiter
|
||||
* should speed up for a while, to take advantage of these resources. This is important when the
|
||||
* rate is applied to networking (limiting bandwidth), where past underutilization typically
|
||||
* translates to "almost empty buffers", which can be filled immediately.
|
||||
*
|
||||
* On the other hand, past underutilization could mean that "the server responsible for handling
|
||||
* the request has become less ready for future requests", i.e. its caches become stale, and
|
||||
* requests become more likely to trigger expensive operations (a more extreme case of this
|
||||
* example is when a server has just booted, and it is mostly busy with getting itself up to
|
||||
* speed).
|
||||
*
|
||||
* To deal with such scenarios, we add an extra dimension, that of "past underutilization",
|
||||
* modeled by "storedPermits" variable. This variable is zero when there is no underutilization,
|
||||
* and it can grow up to maxStoredPermits, for sufficiently large underutilization. So, the
|
||||
* requested permits, by an invocation acquire(permits), are served from:
|
||||
*
|
||||
* - stored permits (if available)
|
||||
*
|
||||
* - fresh permits (for any remaining permits)
|
||||
*
|
||||
* How this works is best explained with an example:
|
||||
*
|
||||
* For a RateLimiter that produces 1 token per second, every second that goes by with the
|
||||
* RateLimiter being unused, we increase storedPermits by 1. Say we leave the RateLimiter unused
|
||||
* for 10 seconds (i.e., we expected a request at time X, but we are at time X + 10 seconds before
|
||||
* a request actually arrives; this is also related to the point made in the last paragraph), thus
|
||||
* storedPermits becomes 10.0 (assuming maxStoredPermits >= 10.0). At that point, a request of
|
||||
* acquire(3) arrives. We serve this request out of storedPermits, and reduce that to 7.0 (how
|
||||
* this is translated to throttling time is discussed later). Immediately after, assume that an
|
||||
* acquire(10) request arriving. We serve the request partly from storedPermits, using all the
|
||||
* remaining 7.0 permits, and the remaining 3.0, we serve them by fresh permits produced by the
|
||||
* rate limiter.
|
||||
*
|
||||
* We already know how much time it takes to serve 3 fresh permits: if the rate is
|
||||
* "1 token per second", then this will take 3 seconds. But what does it mean to serve 7 stored
|
||||
* permits? As explained above, there is no unique answer. If we are primarily interested to deal
|
||||
* with underutilization, then we want stored permits to be given out /faster/ than fresh ones,
|
||||
* because underutilization = free resources for the taking. If we are primarily interested to
|
||||
* deal with overflow, then stored permits could be given out /slower/ than fresh ones. Thus, we
|
||||
* require a (different in each case) function that translates storedPermits to throttling time.
|
||||
*
|
||||
* This role is played by storedPermitsToWaitTime(double storedPermits, double permitsToTake). The
|
||||
* underlying model is a continuous function mapping storedPermits (from 0.0 to maxStoredPermits)
|
||||
* onto the 1/rate (i.e. intervals) that is effective at the given storedPermits. "storedPermits"
|
||||
* essentially measure unused time; we spend unused time buying/storing permits. Rate is
|
||||
* "permits / time", thus "1 / rate = time / permits". Thus, "1/rate" (time / permits) times
|
||||
* "permits" gives time, i.e., integrals on this function (which is what storedPermitsToWaitTime()
|
||||
* computes) correspond to minimum intervals between subsequent requests, for the specified number
|
||||
* of requested permits.
|
||||
*
|
||||
* Here is an example of storedPermitsToWaitTime: If storedPermits == 10.0, and we want 3 permits,
|
||||
* we take them from storedPermits, reducing them to 7.0, and compute the throttling for these as
|
||||
* a call to storedPermitsToWaitTime(storedPermits = 10.0, permitsToTake = 3.0), which will
|
||||
* evaluate the integral of the function from 7.0 to 10.0.
|
||||
*
|
||||
* Using integrals guarantees that the effect of a single acquire(3) is equivalent to {
|
||||
* acquire(1); acquire(1); acquire(1); }, or { acquire(2); acquire(1); }, etc, since the integral
|
||||
* of the function in [7.0, 10.0] is equivalent to the sum of the integrals of [7.0, 8.0], [8.0,
|
||||
* 9.0], [9.0, 10.0] (and so on), no matter what the function is. This guarantees that we handle
|
||||
* correctly requests of varying weight (permits), /no matter/ what the actual function is - so we
|
||||
* can tweak the latter freely. (The only requirement, obviously, is that we can compute its
|
||||
* integrals).
|
||||
*
|
||||
* Note well that if, for this function, we chose a horizontal line, at height of exactly (1/QPS),
|
||||
* then the effect of the function is non-existent: we serve storedPermits at exactly the same
|
||||
* cost as fresh ones (1/QPS is the cost for each). We use this trick later.
|
||||
*
|
||||
* If we pick a function that goes /below/ that horizontal line, it means that we reduce the area
|
||||
* of the function, thus time. Thus, the RateLimiter becomes /faster/ after a period of
|
||||
* underutilization. If, on the other hand, we pick a function that goes /above/ that horizontal
|
||||
* line, then it means that the area (time) is increased, thus storedPermits are more costly than
|
||||
* fresh permits, thus the RateLimiter becomes /slower/ after a period of underutilization.
|
||||
*
|
||||
* Last, but not least: consider a RateLimiter with rate of 1 permit per second, currently
|
||||
* completely unused, and an expensive acquire(100) request comes. It would be nonsensical to just
|
||||
* wait for 100 seconds, and /then/ start the actual task. Why wait without doing anything? A much
|
||||
* better approach is to /allow/ the request right away (as if it was an acquire(1) request
|
||||
* instead), and postpone /subsequent/ requests as needed. In this version, we allow starting the
|
||||
* task immediately, and postpone by 100 seconds future requests, thus we allow for work to get
|
||||
* done in the meantime instead of waiting idly.
|
||||
*
|
||||
* This has important consequences: it means that the RateLimiter doesn't remember the time of the
|
||||
* _last_ request, but it remembers the (expected) time of the _next_ request. This also enables
|
||||
* us to tell immediately (see tryAcquire(timeout)) whether a particular timeout is enough to get
|
||||
* us to the point of the next scheduling time, since we always maintain that. And what we mean by
|
||||
* "an unused RateLimiter" is also defined by that notion: when we observe that the
|
||||
* "expected arrival time of the next request" is actually in the past, then the difference (now -
|
||||
* past) is the amount of time that the RateLimiter was formally unused, and it is that amount of
|
||||
* time which we translate to storedPermits. (We increase storedPermits with the amount of permits
|
||||
* that would have been produced in that idle time). So, if rate == 1 permit per second, and
|
||||
* arrivals come exactly one second after the previous, then storedPermits is _never_ increased --
|
||||
* we would only increase it for arrivals _later_ than the expected one second.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This implements the following function where coldInterval = coldFactor * stableInterval.
|
||||
*
|
||||
* <pre>
|
||||
* ^ throttling
|
||||
* |
|
||||
* cold + /
|
||||
* interval | /.
|
||||
* | / .
|
||||
* | / . ← "warmup period" is the area of the trapezoid between
|
||||
* | / . thresholdPermits and maxPermits
|
||||
* | / .
|
||||
* | / .
|
||||
* | / .
|
||||
* stable +----------/ WARM .
|
||||
* interval | . UP .
|
||||
* | . PERIOD.
|
||||
* | . .
|
||||
* 0 +----------+-------+--------------→ storedPermits
|
||||
* 0 thresholdPermits maxPermits
|
||||
* </pre>
|
||||
*
|
||||
* Before going into the details of this particular function, let's keep in mind the basics:
|
||||
*
|
||||
* <ol>
|
||||
* <li>The state of the RateLimiter (storedPermits) is a vertical line in this figure.
|
||||
* <li>When the RateLimiter is not used, this goes right (up to maxPermits)
|
||||
* <li>When the RateLimiter is used, this goes left (down to zero), since if we have
|
||||
* storedPermits, we serve from those first
|
||||
* <li>When _unused_, we go right at a constant rate! The rate at which we move to the right is
|
||||
* chosen as maxPermits / warmupPeriod. This ensures that the time it takes to go from 0 to
|
||||
* maxPermits is equal to warmupPeriod.
|
||||
* <li>When _used_, the time it takes, as explained in the introductory class note, is equal to
|
||||
* the integral of our function, between X permits and X-K permits, assuming we want to
|
||||
* spend K saved permits.
|
||||
* </ol>
|
||||
*
|
||||
* <p>In summary, the time it takes to move to the left (spend K permits), is equal to the area of
|
||||
* the function of width == K.
|
||||
*
|
||||
* <p>Assuming we have saturated demand, the time to go from maxPermits to thresholdPermits is
|
||||
* equal to warmupPeriod. And the time to go from thresholdPermits to 0 is warmupPeriod/2. (The
|
||||
* reason that this is warmupPeriod/2 is to maintain the behavior of the original implementation
|
||||
* where coldFactor was hard coded as 3.)
|
||||
*
|
||||
* <p>It remains to calculate thresholdsPermits and maxPermits.
|
||||
*
|
||||
* <ul>
|
||||
* <li>The time to go from thresholdPermits to 0 is equal to the integral of the function
|
||||
* between 0 and thresholdPermits. This is thresholdPermits * stableIntervals. By (5) it is
|
||||
* also equal to warmupPeriod/2. Therefore
|
||||
* <blockquote>
|
||||
* thresholdPermits = 0.5 * warmupPeriod / stableInterval
|
||||
* </blockquote>
|
||||
* <li>The time to go from maxPermits to thresholdPermits is equal to the integral of the
|
||||
* function between thresholdPermits and maxPermits. This is the area of the pictured
|
||||
* trapezoid, and it is equal to 0.5 * (stableInterval + coldInterval) * (maxPermits -
|
||||
* thresholdPermits). It is also equal to warmupPeriod, so
|
||||
* <blockquote>
|
||||
* maxPermits = thresholdPermits + 2 * warmupPeriod / (stableInterval + coldInterval)
|
||||
* </blockquote>
|
||||
* </ul>
|
||||
*/
|
||||
static final class SmoothWarmingUp extends SmoothRateLimiter {
|
||||
private final long warmupPeriodMicros;
|
||||
|
||||
/**
|
||||
* The slope of the line from the stable interval (when permits == 0), to the cold interval
|
||||
* (when permits == maxPermits)
|
||||
*/
|
||||
private double slope;
|
||||
|
||||
private double thresholdPermits;
|
||||
private final double coldFactor;
|
||||
|
||||
SmoothWarmingUp(
|
||||
final SleepingStopwatch stopwatch,
|
||||
final long warmupPeriod,
|
||||
final TimeUnit timeUnit,
|
||||
final double coldFactor) {
|
||||
super(stopwatch);
|
||||
this.warmupPeriodMicros = timeUnit.toMicros(warmupPeriod);
|
||||
this.coldFactor = coldFactor;
|
||||
}
|
||||
|
||||
@Override
|
||||
void doSetRate(final double permitsPerSecond, final double stableIntervalMicros) {
|
||||
final double oldMaxPermits = maxPermits;
|
||||
final double coldIntervalMicros = stableIntervalMicros * coldFactor;
|
||||
thresholdPermits = 0.5 * warmupPeriodMicros / stableIntervalMicros;
|
||||
maxPermits =
|
||||
thresholdPermits + 2.0 * warmupPeriodMicros / (stableIntervalMicros + coldIntervalMicros);
|
||||
slope = (coldIntervalMicros - stableIntervalMicros) / (maxPermits - thresholdPermits);
|
||||
if (oldMaxPermits == Double.POSITIVE_INFINITY) {
|
||||
// if we don't special-case this, we would get storedPermits == NaN, below
|
||||
storedPermits = 0.0;
|
||||
} else {
|
||||
storedPermits =
|
||||
(oldMaxPermits == 0.0)
|
||||
? maxPermits // initial state is cold
|
||||
: storedPermits * maxPermits / oldMaxPermits;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
long storedPermitsToWaitTime(final double storedPermits, double permitsToTake) {
|
||||
final double availablePermitsAboveThreshold = storedPermits - thresholdPermits;
|
||||
long micros = 0;
|
||||
// measuring the integral on the right part of the function (the climbing line)
|
||||
if (availablePermitsAboveThreshold > 0.0) {
|
||||
final double permitsAboveThresholdToTake = min(availablePermitsAboveThreshold, permitsToTake);
|
||||
final double length =
|
||||
permitsToTime(availablePermitsAboveThreshold)
|
||||
+ permitsToTime(availablePermitsAboveThreshold - permitsAboveThresholdToTake);
|
||||
micros = (long) (permitsAboveThresholdToTake * length / 2.0);
|
||||
permitsToTake -= permitsAboveThresholdToTake;
|
||||
}
|
||||
// measuring the integral on the left part of the function (the horizontal line)
|
||||
micros += (long) (stableIntervalMicros * permitsToTake);
|
||||
return micros;
|
||||
}
|
||||
|
||||
private double permitsToTime(final double permits) {
|
||||
return stableIntervalMicros + permits * slope;
|
||||
}
|
||||
|
||||
@Override
|
||||
double coolDownIntervalMicros() {
|
||||
return warmupPeriodMicros / maxPermits;
|
||||
}
|
||||
}
|
||||
|
||||
/** The currently stored permits. */
|
||||
double storedPermits;
|
||||
|
||||
/** The maximum number of stored permits. */
|
||||
double maxPermits;
|
||||
|
||||
/**
|
||||
* The interval between two unit requests, at our stable rate. E.g., a stable rate of 5 permits
|
||||
* per second has a stable interval of 200ms.
|
||||
*/
|
||||
double stableIntervalMicros;
|
||||
|
||||
/**
|
||||
* The time when the next request (no matter its size) will be granted. After granting a request,
|
||||
* this is pushed further in the future. Large requests push this further than small requests.
|
||||
*/
|
||||
private long nextFreeTicketMicros = 0L; // could be either in the past or future
|
||||
|
||||
private SmoothRateLimiter(final SleepingStopwatch stopwatch) {
|
||||
super(stopwatch);
|
||||
}
|
||||
|
||||
@Override
|
||||
final void doSetRate(final double permitsPerSecond, final long nowMicros) {
|
||||
resync(nowMicros);
|
||||
final double stableIntervalMicros = SECONDS.toMicros(1L) / permitsPerSecond;
|
||||
this.stableIntervalMicros = stableIntervalMicros;
|
||||
doSetRate(permitsPerSecond, stableIntervalMicros);
|
||||
}
|
||||
|
||||
abstract void doSetRate(double permitsPerSecond, double stableIntervalMicros);
|
||||
|
||||
@Override
|
||||
final double doGetRate() {
|
||||
return SECONDS.toMicros(1L) / stableIntervalMicros;
|
||||
}
|
||||
|
||||
@Override
|
||||
final long queryEarliestAvailable(final long nowMicros) {
|
||||
return nextFreeTicketMicros;
|
||||
}
|
||||
|
||||
@Override
|
||||
final long reserveEarliestAvailable(final int requiredPermits, final long nowMicros) {
|
||||
resync(nowMicros);
|
||||
final long returnValue = nextFreeTicketMicros;
|
||||
final double storedPermitsToSpend = min(requiredPermits, this.storedPermits);
|
||||
final double freshPermits = requiredPermits - storedPermitsToSpend;
|
||||
final long waitMicros =
|
||||
storedPermitsToWaitTime(this.storedPermits, storedPermitsToSpend)
|
||||
+ (long) (freshPermits * stableIntervalMicros);
|
||||
|
||||
this.nextFreeTicketMicros = saturatedAdd(nextFreeTicketMicros, waitMicros);
|
||||
this.storedPermits -= storedPermitsToSpend;
|
||||
return returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the sum of {@code a} and {@code b} unless it would overflow or underflow in which case
|
||||
* {@code Long.MAX_VALUE} or {@code Long.MIN_VALUE} is returned, respectively.
|
||||
*
|
||||
* @since 20.0
|
||||
*/
|
||||
@SuppressWarnings("ShortCircuitBoolean")
|
||||
static long saturatedAdd(final long a, final long b) {
|
||||
final long naiveSum = a + b;
|
||||
if ((a ^ b) < 0 || (a ^ naiveSum) >= 0) {
|
||||
// If a and b have different signs or a has the same sign as the result then there was no
|
||||
// overflow, return.
|
||||
return naiveSum;
|
||||
}
|
||||
// we did over/under flow, if the sign is negative we should return MAX otherwise MIN
|
||||
return Long.MAX_VALUE + ((naiveSum >>> (Long.SIZE - 1)) ^ 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Translates a specified portion of our currently stored permits which we want to spend/acquire,
|
||||
* into a throttling time. Conceptually, this evaluates the integral of the underlying function we
|
||||
* use, for the range of [(storedPermits - permitsToTake), storedPermits].
|
||||
*
|
||||
* <p>This always holds: {@code 0 <= permitsToTake <= storedPermits}
|
||||
*/
|
||||
abstract long storedPermitsToWaitTime(double storedPermits, double permitsToTake);
|
||||
|
||||
/**
|
||||
* Returns the number of microseconds during cool down that we have to wait to get a new permit.
|
||||
*/
|
||||
abstract double coolDownIntervalMicros();
|
||||
|
||||
/** Updates {@code storedPermits} and {@code nextFreeTicketMicros} based on the current time. */
|
||||
void resync(final long nowMicros) {
|
||||
// if nextFreeTicket is in the past, resync to now
|
||||
if (nowMicros > nextFreeTicketMicros) {
|
||||
final double newPermits = (nowMicros - nextFreeTicketMicros) / coolDownIntervalMicros();
|
||||
storedPermits = min(maxPermits, storedPermits + newPermits);
|
||||
nextFreeTicketMicros = nowMicros;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Contains classes for Rate limiting.
|
||||
* <br/>
|
||||
* This code is based on
|
||||
* <a href="https://github.com/google/guava/">google/guava</a>
|
||||
* (Apache 2.0 license) but was modified/refactored for our use.
|
||||
*
|
||||
* @author litetex
|
||||
*/
|
||||
package org.schabi.newpipe.downloader.ratelimiting.limiter;
|
@ -133,7 +133,7 @@ public class PeertubeCommentsExtractorTest {
|
||||
public static void setUp() throws Exception {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
extractor = (PeertubeCommentsExtractor) PeerTube
|
||||
.getCommentsExtractor("https://share.tube/w/vxu4uTstUBAUromWwXGHrq");
|
||||
.getCommentsExtractor("https://framatube.org/w/kkGMgK9ZtnKfYAgnEtQxbv");
|
||||
comments = extractor.getInitialPage();
|
||||
}
|
||||
|
||||
@ -141,16 +141,16 @@ public class PeertubeCommentsExtractorTest {
|
||||
void testGetComments() throws IOException, ExtractionException {
|
||||
assertFalse(comments.getItems().isEmpty());
|
||||
final Optional<CommentsInfoItem> nestedCommentHeadOpt =
|
||||
findCommentWithId("9770", comments.getItems());
|
||||
findCommentWithId("34293", comments.getItems());
|
||||
assertTrue(nestedCommentHeadOpt.isPresent());
|
||||
assertTrue(findNestedCommentWithId("9773", nestedCommentHeadOpt.get()), "The nested comment replies were not found");
|
||||
assertTrue(findNestedCommentWithId("34294", nestedCommentHeadOpt.get()), "The nested " +
|
||||
"comment replies were not found");
|
||||
}
|
||||
|
||||
@Test
|
||||
void testHasCreatorReply() {
|
||||
assertCreatorReply("9770", true);
|
||||
assertCreatorReply("9852", false);
|
||||
assertCreatorReply("11239", false);
|
||||
// NOTE: There is currently no creator replied comment
|
||||
assertCreatorReply("34293", false);
|
||||
}
|
||||
|
||||
private static void assertCreatorReply(final String id, final boolean expected) {
|
||||
|
@ -1,5 +1,8 @@
|
||||
package org.schabi.newpipe.extractor.services.peertube;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.PeerTube;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
@ -19,9 +22,6 @@ import java.util.Locale;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.PeerTube;
|
||||
|
||||
public abstract class PeertubeStreamExtractorTest extends DefaultStreamExtractorTest {
|
||||
private static final String BASE_URL = "/videos/watch/";
|
||||
|
||||
@ -149,7 +149,7 @@ public abstract class PeertubeStreamExtractorTest extends DefaultStreamExtractor
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws Exception {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());;
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
// setting instance might break test when running in parallel (!)
|
||||
PeerTube.setInstance(new PeertubeInstance(INSTANCE));
|
||||
extractor = PeerTube.getStreamExtractor(URL);
|
||||
@ -238,21 +238,21 @@ public abstract class PeertubeStreamExtractorTest extends DefaultStreamExtractor
|
||||
|
||||
@Test
|
||||
public void testGetEmptyDescription() throws Exception {
|
||||
StreamExtractor extractorEmpty = PeerTube.getStreamExtractor("https://framatube.org/api/v1/videos/d5907aad-2252-4207-89ec-a4b687b9337d");
|
||||
final StreamExtractor extractorEmpty = PeerTube.getStreamExtractor("https://framatube.org/api/v1/videos/d5907aad-2252-4207-89ec-a4b687b9337d");
|
||||
extractorEmpty.fetchPage();
|
||||
assertEquals("", extractorEmpty.getDescription().getContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSmallDescription() throws Exception {
|
||||
StreamExtractor extractorSmall = PeerTube.getStreamExtractor("https://peertube.cpy.re/videos/watch/d2a5ec78-5f85-4090-8ec5-dc1102e022ea");
|
||||
final StreamExtractor extractorSmall = PeerTube.getStreamExtractor("https://peertube.cpy.re/videos/watch/d2a5ec78-5f85-4090-8ec5-dc1102e022ea");
|
||||
extractorSmall.fetchPage();
|
||||
assertEquals("https://www.kickstarter.com/projects/1587081065/nothing-to-hide-the-documentary", extractorSmall.getDescription().getContent());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSupportInformation() throws ExtractionException, IOException {
|
||||
StreamExtractor supportInfoExtractor = PeerTube.getStreamExtractor("https://framatube.org/videos/watch/ee408ec8-07cd-4e35-b884-fb681a4b9d37");
|
||||
final StreamExtractor supportInfoExtractor = PeerTube.getStreamExtractor("https://framatube.org/videos/watch/ee408ec8-07cd-4e35-b884-fb681a4b9d37");
|
||||
supportInfoExtractor.fetchPage();
|
||||
assertEquals("https://utip.io/chatsceptique", supportInfoExtractor.getSupportInfo());
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
package org.schabi.newpipe.extractor.services.peertube.search;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
@ -70,8 +69,7 @@ public class PeertubeSearchExtractorTest {
|
||||
|
||||
public static class PagingTest {
|
||||
@Test
|
||||
@Disabled("Exception in CI: javax.net.ssl.SSLHandshakeException: PKIX path validation failed: java.security.cert.CertPathValidatorException: validity check failed")
|
||||
public void duplicatedItemsCheck() throws Exception {
|
||||
void duplicatedItemsCheck() throws Exception {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
final SearchExtractor extractor = PeerTube.getSearchExtractor("internet", singletonList(VIDEOS), "");
|
||||
extractor.fetchPage();
|
||||
|
@ -16,7 +16,6 @@ import static org.schabi.newpipe.extractor.utils.Utils.isNullOrEmpty;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderFactory;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.ExtractorAsserts;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.channel.ChannelExtractor;
|
||||
@ -25,7 +24,6 @@ import org.schabi.newpipe.extractor.channel.tabs.ChannelTabs;
|
||||
import org.schabi.newpipe.extractor.exceptions.AccountTerminatedException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
|
||||
import org.schabi.newpipe.extractor.linkhandler.ReadyChannelTabListLinkHandler;
|
||||
import org.schabi.newpipe.extractor.services.BaseChannelExtractorTest;
|
||||
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeChannelExtractor;
|
||||
@ -43,13 +41,13 @@ public class YoutubeChannelExtractorTest {
|
||||
|
||||
public static class NotAvailable {
|
||||
@BeforeAll
|
||||
public static void setUp() throws IOException {
|
||||
public static void setUp() {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "notAvailable"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deletedFetch() throws Exception {
|
||||
void deletedFetch() throws Exception {
|
||||
final ChannelExtractor extractor =
|
||||
YouTube.getChannelExtractor("https://www.youtube.com/channel/UCAUc4iz6edWerIjlnL8OSSw");
|
||||
|
||||
@ -57,7 +55,7 @@ public class YoutubeChannelExtractorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonExistentFetch() throws Exception {
|
||||
void nonExistentFetch() throws Exception {
|
||||
final ChannelExtractor extractor =
|
||||
YouTube.getChannelExtractor("https://www.youtube.com/channel/DOESNT-EXIST");
|
||||
|
||||
@ -65,72 +63,72 @@ public class YoutubeChannelExtractorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accountTerminatedTOSFetch() throws Exception {
|
||||
void accountTerminatedTOSFetch() throws Exception {
|
||||
// "This account has been terminated for a violation of YouTube's Terms of Service."
|
||||
final ChannelExtractor extractor =
|
||||
YouTube.getChannelExtractor("https://www.youtube.com/channel/UCTGjY2I-ZUGnwVoWAGRd7XQ");
|
||||
|
||||
AccountTerminatedException ex =
|
||||
final AccountTerminatedException ex =
|
||||
assertThrows(AccountTerminatedException.class, extractor::fetchPage);
|
||||
assertEquals(AccountTerminatedException.Reason.VIOLATION, ex.getReason());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accountTerminatedCommunityFetch() throws Exception {
|
||||
void accountTerminatedCommunityFetch() throws Exception {
|
||||
// "This account has been terminated for violating YouTube's Community Guidelines."
|
||||
final ChannelExtractor extractor =
|
||||
YouTube.getChannelExtractor("https://www.youtube.com/channel/UC0AuOxCr9TZ0TtEgL1zpIgA");
|
||||
|
||||
AccountTerminatedException ex =
|
||||
final AccountTerminatedException ex =
|
||||
assertThrows(AccountTerminatedException.class, extractor::fetchPage);
|
||||
assertEquals(AccountTerminatedException.Reason.VIOLATION, ex.getReason());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accountTerminatedHateFetch() throws Exception {
|
||||
void accountTerminatedHateFetch() throws Exception {
|
||||
// "This account has been terminated due to multiple or severe violations
|
||||
// of YouTube's policy prohibiting hate speech."
|
||||
final ChannelExtractor extractor =
|
||||
YouTube.getChannelExtractor("https://www.youtube.com/channel/UCPWXIOPK-9myzek6jHR5yrg");
|
||||
|
||||
AccountTerminatedException ex =
|
||||
final AccountTerminatedException ex =
|
||||
assertThrows(AccountTerminatedException.class, extractor::fetchPage);
|
||||
assertEquals(AccountTerminatedException.Reason.VIOLATION, ex.getReason());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accountTerminatedBullyFetch() throws Exception {
|
||||
void accountTerminatedBullyFetch() throws Exception {
|
||||
// "This account has been terminated due to multiple or severe violations
|
||||
// of YouTube's policy prohibiting content designed to harass, bully or threaten."
|
||||
final ChannelExtractor extractor =
|
||||
YouTube.getChannelExtractor("https://youtube.com/channel/UCB1o7_gbFp2PLsamWxFenBg");
|
||||
|
||||
AccountTerminatedException ex =
|
||||
final AccountTerminatedException ex =
|
||||
assertThrows(AccountTerminatedException.class, extractor::fetchPage);
|
||||
assertEquals(AccountTerminatedException.Reason.VIOLATION, ex.getReason());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accountTerminatedSpamFetch() throws Exception {
|
||||
void accountTerminatedSpamFetch() throws Exception {
|
||||
// "This account has been terminated due to multiple or severe violations
|
||||
// of YouTube's policy against spam, deceptive practices and misleading content
|
||||
// or other Terms of Service violations."
|
||||
final ChannelExtractor extractor =
|
||||
YouTube.getChannelExtractor("https://www.youtube.com/channel/UCoaO4U_p7G7AwalqSbGCZOA");
|
||||
|
||||
AccountTerminatedException ex =
|
||||
final AccountTerminatedException ex =
|
||||
assertThrows(AccountTerminatedException.class, extractor::fetchPage);
|
||||
assertEquals(AccountTerminatedException.Reason.VIOLATION, ex.getReason());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void accountTerminatedCopyrightFetch() throws Exception {
|
||||
void accountTerminatedCopyrightFetch() throws Exception {
|
||||
// "This account has been terminated because we received multiple third-party claims
|
||||
// of copyright infringement regarding material that the user posted."
|
||||
final ChannelExtractor extractor =
|
||||
YouTube.getChannelExtractor("https://www.youtube.com/channel/UCI4i4RgFT5ilfMpna4Z_Y8w");
|
||||
|
||||
AccountTerminatedException ex =
|
||||
final AccountTerminatedException ex =
|
||||
assertThrows(AccountTerminatedException.class, extractor::fetchPage);
|
||||
assertEquals(AccountTerminatedException.Reason.VIOLATION, ex.getReason());
|
||||
}
|
||||
@ -139,7 +137,7 @@ public class YoutubeChannelExtractorTest {
|
||||
|
||||
static class SystemTopic {
|
||||
@BeforeAll
|
||||
static void setUp() throws IOException {
|
||||
static void setUp() {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "systemTopic"));
|
||||
}
|
||||
@ -169,26 +167,31 @@ public class YoutubeChannelExtractorTest {
|
||||
// Extractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testServiceId() {
|
||||
assertEquals(YouTube.getServiceId(), extractor.getServiceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testName() throws Exception {
|
||||
assertEquals("Gronkh", extractor.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testId() throws Exception {
|
||||
assertEquals("UCYJ61XIK64sp6ZFFS8sctxw", extractor.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/channel/UCYJ61XIK64sp6ZFFS8sctxw", extractor.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testOriginalUrl() throws ParsingException {
|
||||
assertEquals("http://www.youtube.com/@Gronkh", extractor.getOriginalUrl());
|
||||
@ -198,26 +201,31 @@ public class YoutubeChannelExtractorTest {
|
||||
// ChannelExtractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testDescription() throws Exception {
|
||||
assertContains("Ungebremster Spieltrieb seit 1896.", extractor.getDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testAvatars() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getAvatars());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testBanners() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getBanners());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testFeedUrl() throws Exception {
|
||||
assertEquals("https://www.youtube.com/feeds/videos.xml?channel_id=UCYJ61XIK64sp6ZFFS8sctxw", extractor.getFeedUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testSubscriberCount() throws Exception {
|
||||
ExtractorAsserts.assertGreaterOrEqual(4_900_000, extractor.getSubscriberCount());
|
||||
@ -262,26 +270,31 @@ public class YoutubeChannelExtractorTest {
|
||||
// Extractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testServiceId() {
|
||||
assertEquals(YouTube.getServiceId(), extractor.getServiceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testName() throws Exception {
|
||||
assertEquals("Vsauce", extractor.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testId() throws Exception {
|
||||
assertEquals("UC6nSFpj9HTCZ5t-N3Rm3-HA", extractor.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/channel/UC6nSFpj9HTCZ5t-N3Rm3-HA", extractor.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testOriginalUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/user/Vsauce", extractor.getOriginalUrl());
|
||||
@ -291,31 +304,37 @@ public class YoutubeChannelExtractorTest {
|
||||
// ChannelExtractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testDescription() throws Exception {
|
||||
assertContains("Our World is Amazing", extractor.getDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testAvatars() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getAvatars());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testBanners() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getBanners());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testFeedUrl() throws Exception {
|
||||
assertEquals("https://www.youtube.com/feeds/videos.xml?channel_id=UC6nSFpj9HTCZ5t-N3Rm3-HA", extractor.getFeedUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testSubscriberCount() throws Exception {
|
||||
ExtractorAsserts.assertGreaterOrEqual(17_000_000, extractor.getSubscriberCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testVerified() throws Exception {
|
||||
assertTrue(extractor.isVerified());
|
||||
@ -355,26 +374,31 @@ public class YoutubeChannelExtractorTest {
|
||||
// Extractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testServiceId() {
|
||||
assertEquals(YouTube.getServiceId(), extractor.getServiceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testName() throws Exception {
|
||||
assertTrue(extractor.getName().startsWith("Kurzgesagt"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testId() throws Exception {
|
||||
assertEquals("UCsXVk37bltHxD1rDPwtNM8Q", extractor.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/channel/UCsXVk37bltHxD1rDPwtNM8Q", extractor.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testOriginalUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/channel/UCsXVk37bltHxD1rDPwtNM8Q", extractor.getOriginalUrl());
|
||||
@ -384,6 +408,7 @@ public class YoutubeChannelExtractorTest {
|
||||
// ChannelExtractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testDescription() throws Exception {
|
||||
ExtractorAsserts.assertContains("science", extractor.getDescription());
|
||||
@ -392,26 +417,31 @@ public class YoutubeChannelExtractorTest {
|
||||
//assertTrue(description, description.contains("Currently we make one animation video per month"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testAvatars() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getAvatars());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testBanners() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getBanners());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testFeedUrl() throws Exception {
|
||||
assertEquals("https://www.youtube.com/feeds/videos.xml?channel_id=UCsXVk37bltHxD1rDPwtNM8Q", extractor.getFeedUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testSubscriberCount() throws Exception {
|
||||
ExtractorAsserts.assertGreaterOrEqual(17_000_000, extractor.getSubscriberCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testVerified() throws Exception {
|
||||
assertTrue(extractor.isVerified());
|
||||
@ -442,8 +472,8 @@ public class YoutubeChannelExtractorTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws Exception {
|
||||
// Test is not deterministic, mocks can't be used
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "kurzgesagtAdditional1"));
|
||||
extractor = (YoutubeChannelExtractor) YouTube.getChannelExtractor(
|
||||
"https://www.youtube.com/channel/UCsXVk37bltHxD1rDPwtNM8Q");
|
||||
extractor.fetchPage();
|
||||
@ -453,7 +483,10 @@ public class YoutubeChannelExtractorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetPageInNewExtractor() throws Exception {
|
||||
void testGetPageInNewExtractor() throws Exception {
|
||||
// Init downloader again for mock as otherwise request confusion occurs when using Mock
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "kurzgesagtAdditional2"));
|
||||
|
||||
final ChannelExtractor newExtractor = YouTube.getChannelExtractor(extractor.getUrl());
|
||||
newExtractor.fetchPage();
|
||||
final ChannelTabExtractor newTabExtractor = YouTube.getChannelTabExtractor(
|
||||
@ -478,26 +511,31 @@ public class YoutubeChannelExtractorTest {
|
||||
// Extractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testServiceId() {
|
||||
assertEquals(YouTube.getServiceId(), extractor.getServiceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testName() throws Exception {
|
||||
assertEquals("Captain Disillusion", extractor.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testId() throws Exception {
|
||||
assertEquals("UCEOXxzW2vU0P-0THehuIIeg", extractor.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/channel/UCEOXxzW2vU0P-0THehuIIeg", extractor.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testOriginalUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/user/CaptainDisillusion/videos", extractor.getOriginalUrl());
|
||||
@ -507,31 +545,37 @@ public class YoutubeChannelExtractorTest {
|
||||
// ChannelExtractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testDescription() throws Exception {
|
||||
assertContains("In a world where", extractor.getDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testAvatars() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getAvatars());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testBanners() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getBanners());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testFeedUrl() throws Exception {
|
||||
assertEquals("https://www.youtube.com/feeds/videos.xml?channel_id=UCEOXxzW2vU0P-0THehuIIeg", extractor.getFeedUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testSubscriberCount() throws Exception {
|
||||
ExtractorAsserts.assertGreaterOrEqual(2_000_000, extractor.getSubscriberCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testVerified() throws Exception {
|
||||
assertTrue(extractor.isVerified());
|
||||
@ -571,26 +615,31 @@ public class YoutubeChannelExtractorTest {
|
||||
// Extractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testServiceId() {
|
||||
assertEquals(YouTube.getServiceId(), extractor.getServiceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testName() throws Exception {
|
||||
assertEquals("random channel", extractor.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testId() throws Exception {
|
||||
assertEquals("UCUaQMQS9lY5lit3vurpXQ6w", extractor.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/channel/UCUaQMQS9lY5lit3vurpXQ6w", extractor.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testOriginalUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/channel/UCUaQMQS9lY5lit3vurpXQ6w", extractor.getOriginalUrl());
|
||||
@ -600,31 +649,37 @@ public class YoutubeChannelExtractorTest {
|
||||
// ChannelExtractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testDescription() throws Exception {
|
||||
assertContains("Hey there iu will upoload a load of pranks onto this channel", extractor.getDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testAvatars() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getAvatars());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testBanners() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getBanners());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testFeedUrl() throws Exception {
|
||||
assertEquals("https://www.youtube.com/feeds/videos.xml?channel_id=UCUaQMQS9lY5lit3vurpXQ6w", extractor.getFeedUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testSubscriberCount() throws Exception {
|
||||
ExtractorAsserts.assertGreaterOrEqual(50, extractor.getSubscriberCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testVerified() throws Exception {
|
||||
assertFalse(extractor.isVerified());
|
||||
@ -662,26 +717,31 @@ public class YoutubeChannelExtractorTest {
|
||||
// Extractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testServiceId() {
|
||||
assertEquals(YouTube.getServiceId(), extractor.getServiceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testName() throws Exception {
|
||||
assertEquals("Sports", extractor.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testId() throws Exception {
|
||||
assertEquals("UCEgdi0XIXXZ-qJOFPf4JSKw", extractor.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/channel/UCEgdi0XIXXZ-qJOFPf4JSKw", extractor.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testOriginalUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/channel/UCEgdi0XIXXZ-qJOFPf4JSKw", extractor.getOriginalUrl());
|
||||
@ -697,27 +757,32 @@ public class YoutubeChannelExtractorTest {
|
||||
assertNull(extractor.getDescription());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testAvatars() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getAvatars());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testBanners() {
|
||||
// A CarouselHeaderRenderer doesn't contain a banner
|
||||
assertEmpty(extractor.getBanners());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testFeedUrl() throws Exception {
|
||||
assertEquals("https://www.youtube.com/feeds/videos.xml?channel_id=UCEgdi0XIXXZ-qJOFPf4JSKw", extractor.getFeedUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testSubscriberCount() throws Exception {
|
||||
ExtractorAsserts.assertGreaterOrEqual(70_000_000, extractor.getSubscriberCount());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testVerified() throws Exception {
|
||||
assertTrue(extractor.isVerified());
|
||||
|
@ -1,31 +1,5 @@
|
||||
package org.schabi.newpipe.extractor.services.youtube;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.CreationException;
|
||||
import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeOtfDashManifestCreator;
|
||||
import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeProgressiveDashManifestCreator;
|
||||
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.DeliveryMethod;
|
||||
import org.schabi.newpipe.extractor.stream.Stream;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Random;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertAll;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
@ -50,6 +24,33 @@ import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators
|
||||
import static org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeDashManifestCreatorsUtils.SEGMENT_TIMELINE;
|
||||
import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderFactory;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.CreationException;
|
||||
import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeOtfDashManifestCreator;
|
||||
import org.schabi.newpipe.extractor.services.youtube.dashmanifestcreators.YoutubeProgressiveDashManifestCreator;
|
||||
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeStreamExtractor;
|
||||
import org.schabi.newpipe.extractor.stream.DeliveryMethod;
|
||||
import org.schabi.newpipe.extractor.stream.Stream;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
/**
|
||||
* Test for YouTube DASH manifest creators.
|
||||
*
|
||||
@ -80,19 +81,22 @@ import static org.schabi.newpipe.extractor.utils.Utils.isBlank;
|
||||
* </p>
|
||||
*/
|
||||
class YoutubeDashManifestCreatorsTest {
|
||||
private static final String RESOURCE_PATH =
|
||||
DownloaderFactory.RESOURCE_PATH + "services/youtube/extractor/dashManifest";
|
||||
|
||||
// Setting a higher number may let Google video servers return 403s
|
||||
private static final int MAX_STREAMS_TO_TEST_PER_METHOD = 3;
|
||||
private static final String url = "https://www.youtube.com/watch?v=DJ8GQUNUXGM";
|
||||
private static final String URL = "https://www.youtube.com/watch?v=DJ8GQUNUXGM";
|
||||
private static YoutubeStreamExtractor extractor;
|
||||
private static long videoLength;
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws Exception {
|
||||
YoutubeParsingHelper.resetClientVersion();
|
||||
YoutubeParsingHelper.setNumberGenerator(new Random(1));
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH));
|
||||
|
||||
extractor = (YoutubeStreamExtractor) YouTube.getStreamExtractor(url);
|
||||
extractor = (YoutubeStreamExtractor) YouTube.getStreamExtractor(URL);
|
||||
extractor.fetchPage();
|
||||
videoLength = extractor.getLength();
|
||||
}
|
||||
|
@ -81,7 +81,8 @@ public class YoutubeFeedExtractorTest {
|
||||
public static class NotAvailable {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws IOException {
|
||||
public static void setUp() {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "notAvailable/"));
|
||||
}
|
||||
|
||||
|
@ -4,40 +4,48 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.downloader.DownloaderFactory;
|
||||
import org.schabi.newpipe.extractor.ExtractorAsserts;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class YoutubeJavaScriptExtractorTest {
|
||||
class YoutubeJavaScriptExtractorTest {
|
||||
private static final String RESOURCE_PATH =
|
||||
DownloaderFactory.RESOURCE_PATH + "services/youtube/extractor/jsExtractor/";
|
||||
|
||||
@BeforeEach
|
||||
public void setup() throws IOException {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
public void setup() {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractJavaScriptUrlIframe() throws ParsingException {
|
||||
void testExtractJavaScriptUrlIframe() throws ParsingException {
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "urlWithIframeResource"));
|
||||
|
||||
assertTrue(YoutubeJavaScriptExtractor.extractJavaScriptUrlWithIframeResource()
|
||||
.endsWith("base.js"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractJavaScriptUrlEmbed() throws ParsingException {
|
||||
void testExtractJavaScriptUrlEmbed() throws ParsingException {
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "embedWatchPage"));
|
||||
|
||||
assertTrue(YoutubeJavaScriptExtractor.extractJavaScriptUrlWithEmbedWatchPage("d4IGg5dqeO8")
|
||||
.endsWith("base.js"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractJavaScript__success() throws ParsingException {
|
||||
String playerJsCode = YoutubeJavaScriptExtractor.extractJavaScriptPlayerCode("d4IGg5dqeO8");
|
||||
void testExtractJavaScript__success() throws ParsingException {
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "playerCode"));
|
||||
|
||||
final String playerJsCode = YoutubeJavaScriptExtractor.extractJavaScriptPlayerCode("d4IGg5dqeO8");
|
||||
assertPlayerJsCode(playerJsCode);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExtractJavaScript__invalidVideoId__success() throws ParsingException {
|
||||
void testExtractJavaScript__invalidVideoId__success() throws ParsingException {
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "playerCodeInvalidVideoId"));
|
||||
|
||||
String playerJsCode = YoutubeJavaScriptExtractor.extractJavaScriptPlayerCode("not_a_video_id");
|
||||
assertPlayerJsCode(playerJsCode);
|
||||
|
||||
|
@ -310,7 +310,7 @@ public class YoutubeMixPlaylistExtractorTest {
|
||||
private static final String VIDEO_ID = "QMVCAPd5cwBcg";
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws IOException {
|
||||
public static void setUp() {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
YoutubeParsingHelper.setConsentAccepted(true);
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "invalid"));
|
||||
@ -336,72 +336,6 @@ public class YoutubeMixPlaylistExtractorTest {
|
||||
}
|
||||
}
|
||||
|
||||
public static class ChannelMix {
|
||||
private static final String CHANNEL_ID = "UCXuqSBlHAE6Xw-yeJA0Tunw";
|
||||
private static final String VIDEO_ID_OF_CHANNEL = "mnk6gnOBYIo";
|
||||
private static final String CHANNEL_TITLE = "Linus Tech Tips";
|
||||
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws Exception {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
YoutubeParsingHelper.setConsentAccepted(true);
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "channelMix"));
|
||||
extractor = (YoutubeMixPlaylistExtractor) YouTube
|
||||
.getPlaylistExtractor("https://www.youtube.com/watch?v=" + VIDEO_ID_OF_CHANNEL
|
||||
+ "&list=RDCM" + CHANNEL_ID);
|
||||
extractor.fetchPage();
|
||||
}
|
||||
|
||||
@Test
|
||||
void getName() throws Exception {
|
||||
final String name = extractor.getName();
|
||||
ExtractorAsserts.assertContains("Mix", name);
|
||||
ExtractorAsserts.assertContains(CHANNEL_TITLE, name);
|
||||
}
|
||||
|
||||
@Test
|
||||
void getThumbnails() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getThumbnails());
|
||||
extractor.getThumbnails().forEach(thumbnail ->
|
||||
ExtractorAsserts.assertContains(VIDEO_ID_OF_CHANNEL, thumbnail.getUrl()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void getInitialPage() throws Exception {
|
||||
final InfoItemsPage<StreamInfoItem> streams = extractor.getInitialPage();
|
||||
assertFalse(streams.getItems().isEmpty());
|
||||
assertTrue(streams.hasNextPage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPage() throws Exception {
|
||||
final byte[] body = JsonWriter.string(prepareDesktopJsonBuilder(
|
||||
NewPipe.getPreferredLocalization(), NewPipe.getPreferredContentCountry())
|
||||
.value("videoId", VIDEO_ID_OF_CHANNEL)
|
||||
.value("playlistId", "RDCM" + CHANNEL_ID)
|
||||
.value("params", "OAE%3D")
|
||||
.done())
|
||||
.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
final InfoItemsPage<StreamInfoItem> streams = extractor.getPage(new Page(
|
||||
YOUTUBEI_V1_URL + "next?" + DISABLE_PRETTY_PRINT_PARAMETER,
|
||||
null, null, dummyCookie, body));
|
||||
assertFalse(streams.getItems().isEmpty());
|
||||
assertTrue(streams.hasNextPage());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getStreamCount() {
|
||||
assertEquals(ListExtractor.ITEM_COUNT_INFINITE, extractor.getStreamCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
void getPlaylistType() throws ParsingException {
|
||||
assertEquals(PlaylistInfo.PlaylistType.MIX_CHANNEL, extractor.getPlaylistType());
|
||||
}
|
||||
}
|
||||
|
||||
public static class GenreMix {
|
||||
private static final String VIDEO_ID = "kINJeTNFbpg";
|
||||
private static final String MIX_TITLE = "Mix – Electronic music";
|
||||
|
@ -19,7 +19,7 @@ public class YoutubeParsingHelperTest {
|
||||
private static final String RESOURCE_PATH = DownloaderFactory.RESOURCE_PATH + "services/youtube/";
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws IOException {
|
||||
public static void setUp() {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "youtubeParsingHelper"));
|
||||
}
|
||||
|
@ -40,7 +40,7 @@ public class YoutubePlaylistExtractorTest {
|
||||
|
||||
public static class NotAvailable {
|
||||
@BeforeAll
|
||||
public static void setUp() throws IOException {
|
||||
public static void setUp() {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "notAvailable"));
|
||||
}
|
||||
@ -76,26 +76,31 @@ public class YoutubePlaylistExtractorTest {
|
||||
// Extractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testServiceId() {
|
||||
assertEquals(YouTube.getServiceId(), extractor.getServiceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testName() throws Exception {
|
||||
assertTrue(extractor.getName().startsWith("Pop Music Playlist"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testId() throws Exception {
|
||||
assertEquals("PLMC9KNkIncKtPzgY-5rmhvj7fax8fdxoj", extractor.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/playlist?list=PLMC9KNkIncKtPzgY-5rmhvj7fax8fdxoj", extractor.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testOriginalUrl() throws ParsingException {
|
||||
assertEquals("http://www.youtube.com/watch?v=lp-EO5I60KA&list=PLMC9KNkIncKtPzgY-5rmhvj7fax8fdxoj", extractor.getOriginalUrl());
|
||||
@ -105,11 +110,13 @@ public class YoutubePlaylistExtractorTest {
|
||||
// ListExtractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testRelatedItems() throws Exception {
|
||||
defaultTestRelatedItems(extractor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testMoreRelatedItems() throws Exception {
|
||||
defaultTestMoreItems(extractor);
|
||||
@ -119,11 +126,13 @@ public class YoutubePlaylistExtractorTest {
|
||||
// PlaylistExtractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testThumbnails() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getThumbnails());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testBanners() throws ParsingException {
|
||||
YoutubeTestsUtils.testImages(extractor.getBanners());
|
||||
@ -134,17 +143,20 @@ public class YoutubePlaylistExtractorTest {
|
||||
assertEquals("https://www.youtube.com/channel/UCs72iRpTEuwV3y6pdWYLgiw", extractor.getUploaderUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUploaderName() throws Exception {
|
||||
final String uploaderName = extractor.getUploaderName();
|
||||
ExtractorAsserts.assertContains("Just Hits", uploaderName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUploaderAvatars() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getUploaderAvatars());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testStreamCount() throws Exception {
|
||||
ExtractorAsserts.assertGreater(100, extractor.getStreamCount());
|
||||
@ -194,27 +206,32 @@ public class YoutubePlaylistExtractorTest {
|
||||
// Extractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testServiceId() {
|
||||
assertEquals(YouTube.getServiceId(), extractor.getServiceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testName() throws Exception {
|
||||
final String name = extractor.getName();
|
||||
assertEquals("I Wanna Rock Super Gigantic Playlist 1: Hardrock, AOR, Metal and more !!! 5000 music videos !!!", name);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testId() throws Exception {
|
||||
assertEquals("PLWwAypAcFRgKAIIFqBr9oy-ZYZnixa_Fj", extractor.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/playlist?list=PLWwAypAcFRgKAIIFqBr9oy-ZYZnixa_Fj", extractor.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testOriginalUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/watch?v=8SbUC-UaAxE&list=PLWwAypAcFRgKAIIFqBr9oy-ZYZnixa_Fj", extractor.getOriginalUrl());
|
||||
@ -224,11 +241,13 @@ public class YoutubePlaylistExtractorTest {
|
||||
// ListExtractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testRelatedItems() throws Exception {
|
||||
defaultTestRelatedItems(extractor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testMoreRelatedItems() throws Exception {
|
||||
ListExtractor.InfoItemsPage<StreamInfoItem> currentPage = defaultTestMoreItems(extractor);
|
||||
@ -244,11 +263,13 @@ public class YoutubePlaylistExtractorTest {
|
||||
// PlaylistExtractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testThumbnails() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getThumbnails());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testBanners() throws ParsingException {
|
||||
YoutubeTestsUtils.testImages(extractor.getBanners());
|
||||
@ -259,16 +280,19 @@ public class YoutubePlaylistExtractorTest {
|
||||
assertEquals("https://www.youtube.com/channel/UCHSPWoY1J5fbDVbcnyeqwdw", extractor.getUploaderUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUploaderName() throws Exception {
|
||||
assertEquals("Tomas Nilsson TOMPA571", extractor.getUploaderName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUploaderAvatars() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getUploaderAvatars());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testStreamCount() throws Exception {
|
||||
ExtractorAsserts.assertGreater(100, extractor.getStreamCount());
|
||||
@ -308,26 +332,31 @@ public class YoutubePlaylistExtractorTest {
|
||||
// Extractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testServiceId() {
|
||||
assertEquals(YouTube.getServiceId(), extractor.getServiceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testName() throws Exception {
|
||||
assertTrue(extractor.getName().startsWith("Anatomy & Physiology"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testId() throws Exception {
|
||||
assertEquals("PL8dPuuaLjXtOAKed_MxxWBNaPno5h3Zs8", extractor.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/playlist?list=PL8dPuuaLjXtOAKed_MxxWBNaPno5h3Zs8", extractor.getUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testOriginalUrl() throws ParsingException {
|
||||
assertEquals("https://www.youtube.com/playlist?list=PL8dPuuaLjXtOAKed_MxxWBNaPno5h3Zs8", extractor.getOriginalUrl());
|
||||
@ -337,11 +366,13 @@ public class YoutubePlaylistExtractorTest {
|
||||
// ListExtractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testRelatedItems() throws Exception {
|
||||
defaultTestRelatedItems(extractor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testMoreRelatedItems() throws Exception {
|
||||
assertFalse(extractor.getInitialPage().hasNextPage());
|
||||
@ -351,11 +382,13 @@ public class YoutubePlaylistExtractorTest {
|
||||
// PlaylistExtractor
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testThumbnails() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getThumbnails());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testBanners() throws ParsingException {
|
||||
YoutubeTestsUtils.testImages(extractor.getBanners());
|
||||
@ -366,17 +399,20 @@ public class YoutubePlaylistExtractorTest {
|
||||
assertEquals("https://www.youtube.com/channel/UCX6b17PVsYBQ0ip5gyeme-Q", extractor.getUploaderUrl());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUploaderName() throws Exception {
|
||||
final String uploaderName = extractor.getUploaderName();
|
||||
ExtractorAsserts.assertContains("CrashCourse", uploaderName);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUploaderAvatars() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getUploaderAvatars());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testStreamCount() throws Exception {
|
||||
ExtractorAsserts.assertGreater(40, extractor.getStreamCount());
|
||||
@ -445,6 +481,7 @@ public class YoutubePlaylistExtractorTest {
|
||||
extractor.getOriginalUrl());
|
||||
}
|
||||
|
||||
@Disabled("Known problem, see https://github.com/TeamNewPipe/NewPipeExtractor/issues/1273")
|
||||
@Test
|
||||
@Override
|
||||
public void testRelatedItems() throws Exception {
|
||||
@ -479,6 +516,7 @@ public class YoutubePlaylistExtractorTest {
|
||||
assertEquals("YouTube", extractor.getUploaderName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testUploaderAvatars() throws Exception {
|
||||
YoutubeTestsUtils.testImages(extractor.getUploaderAvatars());
|
||||
@ -511,7 +549,7 @@ public class YoutubePlaylistExtractorTest {
|
||||
public static class ContinuationsTests {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws IOException {
|
||||
public static void setUp() {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "continuations"));
|
||||
}
|
||||
|
@ -1,24 +1,27 @@
|
||||
package org.schabi.newpipe.extractor.services.youtube;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.CsvSource;
|
||||
import org.junit.jupiter.params.provider.ValueSource;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.downloader.DownloaderFactory;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.IOException;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
class YoutubeSignaturesTest {
|
||||
private static final String RESOURCE_PATH =
|
||||
DownloaderFactory.RESOURCE_PATH + "services/youtube/extractor/signatures";
|
||||
|
||||
@BeforeEach
|
||||
void setUp() throws IOException {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
void setUp() {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH));
|
||||
}
|
||||
|
||||
@ValueSource(strings = {
|
||||
|
@ -6,18 +6,20 @@ import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.downloader.DownloaderFactory;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.exceptions.ParsingException;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
class YoutubeThrottlingParameterDeobfuscationTest {
|
||||
private static final String RESOURCE_PATH =
|
||||
DownloaderFactory.RESOURCE_PATH + "services/youtube/extractor/parameterDeobf";
|
||||
|
||||
@BeforeEach
|
||||
void setup() throws IOException {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -28,14 +28,13 @@ import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderFactory;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.kiosk.KioskInfo;
|
||||
import org.schabi.newpipe.extractor.linkhandler.LinkHandlerFactory;
|
||||
|
||||
/**
|
||||
* Test for {@link KioskInfo}
|
||||
*/
|
||||
public class YoutubeTrendingKioskInfoTest {
|
||||
class YoutubeTrendingKioskInfoTest {
|
||||
|
||||
private static final String RESOURCE_PATH = DownloaderFactory.RESOURCE_PATH + "kiosk";
|
||||
|
||||
@ -46,24 +45,24 @@ public class YoutubeTrendingKioskInfoTest {
|
||||
throws Exception {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH));
|
||||
LinkHandlerFactory LinkHandlerFactory = ((StreamingService) YouTube).getKioskList().getListLinkHandlerFactoryByType("Trending");
|
||||
LinkHandlerFactory linkHandlerFactory = YouTube.getKioskList().getListLinkHandlerFactoryByType("Trending");
|
||||
|
||||
kioskInfo = KioskInfo.getInfo(YouTube, LinkHandlerFactory.fromId("Trending").getUrl());
|
||||
kioskInfo = KioskInfo.getInfo(YouTube, linkHandlerFactory.fromId("Trending").getUrl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getStreams() {
|
||||
void getStreams() {
|
||||
assertFalse(kioskInfo.getRelatedItems().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getId() {
|
||||
void getId() {
|
||||
assertTrue(kioskInfo.getId().equals("Trending")
|
||||
|| kioskInfo.getId().equals("Trends"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getName() {
|
||||
void getName() {
|
||||
assertFalse(kioskInfo.getName().isEmpty());
|
||||
}
|
||||
}
|
||||
|
@ -4,28 +4,34 @@ import static org.schabi.newpipe.extractor.ServiceList.YouTube;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.schabi.newpipe.downloader.DownloaderTestImpl;
|
||||
import org.schabi.newpipe.downloader.DownloaderFactory;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
import org.schabi.newpipe.extractor.search.SearchExtractor;
|
||||
import org.schabi.newpipe.extractor.services.DefaultSearchExtractorTest;
|
||||
import org.schabi.newpipe.extractor.services.youtube.YoutubeTestsUtils;
|
||||
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeSearchQueryHandlerFactory;
|
||||
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
// Doesn't work with mocks. Makes request with different `dataToSend` I think
|
||||
public class YoutubeMusicSearchExtractorTest {
|
||||
private static final String RESOURCE_PATH = DownloaderFactory.RESOURCE_PATH + "services/youtube/extractor/musicSearch/";
|
||||
private static final String BASE_SEARCH_URL = "music.youtube.com/search?q=";
|
||||
|
||||
public static class MusicSongs extends DefaultSearchExtractorTest {
|
||||
private static SearchExtractor extractor;
|
||||
private static final String QUERY = "mocromaniac";
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws Exception {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "musicSongs"));
|
||||
|
||||
extractor = YouTube.getSearchExtractor(QUERY, singletonList(YoutubeSearchQueryHandlerFactory.MUSIC_SONGS), "");
|
||||
extractor.fetchPage();
|
||||
}
|
||||
@ -34,8 +40,8 @@ public class YoutubeMusicSearchExtractorTest {
|
||||
@Override public StreamingService expectedService() { return YouTube; }
|
||||
@Override public String expectedName() { return QUERY; }
|
||||
@Override public String expectedId() { return QUERY; }
|
||||
@Override public String expectedUrlContains() { return "music.youtube.com/search?q=" + QUERY; }
|
||||
@Override public String expectedOriginalUrlContains() { return "music.youtube.com/search?q=" + QUERY; }
|
||||
@Override public String expectedUrlContains() { return BASE_SEARCH_URL + QUERY; }
|
||||
@Override public String expectedOriginalUrlContains() { return BASE_SEARCH_URL + QUERY; }
|
||||
@Override public String expectedSearchString() { return QUERY; }
|
||||
@Nullable @Override public String expectedSearchSuggestion() { return null; }
|
||||
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; }
|
||||
@ -47,7 +53,9 @@ public class YoutubeMusicSearchExtractorTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws Exception {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "musicVideos"));
|
||||
|
||||
extractor = YouTube.getSearchExtractor(QUERY, singletonList(YoutubeSearchQueryHandlerFactory.MUSIC_VIDEOS), "");
|
||||
extractor.fetchPage();
|
||||
}
|
||||
@ -56,8 +64,8 @@ public class YoutubeMusicSearchExtractorTest {
|
||||
@Override public StreamingService expectedService() { return YouTube; }
|
||||
@Override public String expectedName() { return QUERY; }
|
||||
@Override public String expectedId() { return QUERY; }
|
||||
@Override public String expectedUrlContains() { return "music.youtube.com/search?q=" + QUERY; }
|
||||
@Override public String expectedOriginalUrlContains() { return "music.youtube.com/search?q=" + QUERY; }
|
||||
@Override public String expectedUrlContains() { return BASE_SEARCH_URL + QUERY; }
|
||||
@Override public String expectedOriginalUrlContains() { return BASE_SEARCH_URL + QUERY; }
|
||||
@Override public String expectedSearchString() { return QUERY; }
|
||||
@Nullable @Override public String expectedSearchSuggestion() { return null; }
|
||||
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; }
|
||||
@ -69,7 +77,9 @@ public class YoutubeMusicSearchExtractorTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws Exception {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "musicAlbums"));
|
||||
|
||||
extractor = YouTube.getSearchExtractor(QUERY, singletonList(YoutubeSearchQueryHandlerFactory.MUSIC_ALBUMS), "");
|
||||
extractor.fetchPage();
|
||||
}
|
||||
@ -78,8 +88,8 @@ public class YoutubeMusicSearchExtractorTest {
|
||||
@Override public StreamingService expectedService() { return YouTube; }
|
||||
@Override public String expectedName() { return QUERY; }
|
||||
@Override public String expectedId() { return QUERY; }
|
||||
@Override public String expectedUrlContains() { return "music.youtube.com/search?q=" + URLEncoder.encode(QUERY); }
|
||||
@Override public String expectedOriginalUrlContains() { return "music.youtube.com/search?q=" + URLEncoder.encode(QUERY); }
|
||||
@Override public String expectedUrlContains() { return BASE_SEARCH_URL + URLEncoder.encode(QUERY, StandardCharsets.UTF_8); }
|
||||
@Override public String expectedOriginalUrlContains() { return BASE_SEARCH_URL + URLEncoder.encode(QUERY, StandardCharsets.UTF_8); }
|
||||
@Override public String expectedSearchString() { return QUERY; }
|
||||
@Nullable @Override public String expectedSearchSuggestion() { return null; }
|
||||
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.PLAYLIST; }
|
||||
@ -91,7 +101,9 @@ public class YoutubeMusicSearchExtractorTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws Exception {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "musicPlaylists"));
|
||||
|
||||
extractor = YouTube.getSearchExtractor(QUERY, singletonList(YoutubeSearchQueryHandlerFactory.MUSIC_PLAYLISTS), "");
|
||||
extractor.fetchPage();
|
||||
}
|
||||
@ -100,21 +112,22 @@ public class YoutubeMusicSearchExtractorTest {
|
||||
@Override public StreamingService expectedService() { return YouTube; }
|
||||
@Override public String expectedName() { return QUERY; }
|
||||
@Override public String expectedId() { return QUERY; }
|
||||
@Override public String expectedUrlContains() { return "music.youtube.com/search?q=" + QUERY; }
|
||||
@Override public String expectedOriginalUrlContains() { return "music.youtube.com/search?q=" + QUERY; }
|
||||
@Override public String expectedUrlContains() { return BASE_SEARCH_URL + QUERY; }
|
||||
@Override public String expectedOriginalUrlContains() { return BASE_SEARCH_URL + QUERY; }
|
||||
@Override public String expectedSearchString() { return QUERY; }
|
||||
@Nullable @Override public String expectedSearchSuggestion() { return null; }
|
||||
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.PLAYLIST; }
|
||||
}
|
||||
|
||||
@Disabled
|
||||
public static class MusicArtists extends DefaultSearchExtractorTest {
|
||||
private static SearchExtractor extractor;
|
||||
private static final String QUERY = "kevin";
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws Exception {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "musicArtists"));
|
||||
|
||||
extractor = YouTube.getSearchExtractor(QUERY, singletonList(YoutubeSearchQueryHandlerFactory.MUSIC_ARTISTS), "");
|
||||
extractor.fetchPage();
|
||||
}
|
||||
@ -123,22 +136,22 @@ public class YoutubeMusicSearchExtractorTest {
|
||||
@Override public StreamingService expectedService() { return YouTube; }
|
||||
@Override public String expectedName() { return QUERY; }
|
||||
@Override public String expectedId() { return QUERY; }
|
||||
@Override public String expectedUrlContains() { return "music.youtube.com/search?q=" + QUERY; }
|
||||
@Override public String expectedOriginalUrlContains() { return "music.youtube.com/search?q=" + QUERY; }
|
||||
@Override public String expectedUrlContains() { return BASE_SEARCH_URL + QUERY; }
|
||||
@Override public String expectedOriginalUrlContains() { return BASE_SEARCH_URL + QUERY; }
|
||||
@Override public String expectedSearchString() { return QUERY; }
|
||||
@Nullable @Override public String expectedSearchSuggestion() { return null; }
|
||||
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.CHANNEL; }
|
||||
}
|
||||
|
||||
@Disabled("Currently constantly switching between \"Did you mean\" and \"Showing results for ...\" occurs")
|
||||
public static class Suggestion extends DefaultSearchExtractorTest {
|
||||
private static SearchExtractor extractor;
|
||||
private static final String QUERY = "megaman x3";
|
||||
private static final boolean CORRECTED = true;
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws Exception {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "suggestion"));
|
||||
|
||||
extractor = YouTube.getSearchExtractor(QUERY, singletonList(YoutubeSearchQueryHandlerFactory.MUSIC_SONGS), "");
|
||||
extractor.fetchPage();
|
||||
}
|
||||
@ -147,12 +160,11 @@ public class YoutubeMusicSearchExtractorTest {
|
||||
@Override public StreamingService expectedService() { return YouTube; }
|
||||
@Override public String expectedName() { return QUERY; }
|
||||
@Override public String expectedId() { return QUERY; }
|
||||
@Override public String expectedUrlContains() { return "music.youtube.com/search?q=" + URLEncoder.encode(QUERY); }
|
||||
@Override public String expectedOriginalUrlContains() { return "music.youtube.com/search?q=" + URLEncoder.encode(QUERY); }
|
||||
@Override public String expectedUrlContains() { return BASE_SEARCH_URL + URLEncoder.encode(QUERY, StandardCharsets.UTF_8); }
|
||||
@Override public String expectedOriginalUrlContains() { return BASE_SEARCH_URL + URLEncoder.encode(QUERY, StandardCharsets.UTF_8); }
|
||||
@Override public String expectedSearchString() { return QUERY; }
|
||||
@Nullable @Override public String expectedSearchSuggestion() { return "mega man x3"; }
|
||||
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; }
|
||||
@Override public boolean isCorrectedSearch() { return CORRECTED; }
|
||||
}
|
||||
|
||||
public static class CorrectedSearch extends DefaultSearchExtractorTest {
|
||||
@ -162,7 +174,9 @@ public class YoutubeMusicSearchExtractorTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUp() throws Exception {
|
||||
NewPipe.init(DownloaderTestImpl.getInstance());
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "corrected"));
|
||||
|
||||
extractor = YouTube.getSearchExtractor(QUERY, singletonList(YoutubeSearchQueryHandlerFactory.MUSIC_SONGS), "");
|
||||
extractor.fetchPage();
|
||||
}
|
||||
@ -171,8 +185,8 @@ public class YoutubeMusicSearchExtractorTest {
|
||||
@Override public StreamingService expectedService() { return YouTube; }
|
||||
@Override public String expectedName() { return QUERY; }
|
||||
@Override public String expectedId() { return QUERY; }
|
||||
@Override public String expectedUrlContains() { return "music.youtube.com/search?q=" + URLEncoder.encode(QUERY); }
|
||||
@Override public String expectedOriginalUrlContains() { return "music.youtube.com/search?q=" + URLEncoder.encode(QUERY); }
|
||||
@Override public String expectedUrlContains() { return BASE_SEARCH_URL + URLEncoder.encode(QUERY, StandardCharsets.UTF_8); }
|
||||
@Override public String expectedOriginalUrlContains() { return BASE_SEARCH_URL + URLEncoder.encode(QUERY, StandardCharsets.UTF_8); }
|
||||
@Override public String expectedSearchString() { return QUERY; }
|
||||
@Nullable @Override public String expectedSearchSuggestion() { return EXPECTED_SUGGESTION; }
|
||||
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; }
|
||||
|
@ -13,6 +13,7 @@ import static org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeS
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.schabi.newpipe.downloader.DownloaderFactory;
|
||||
import org.schabi.newpipe.extractor.InfoItem;
|
||||
@ -219,6 +220,7 @@ public class YoutubeSearchExtractorTest {
|
||||
// Test Overrides
|
||||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
@Override
|
||||
@Test
|
||||
public void testMoreRelatedItems() throws Exception {
|
||||
final ListExtractor.InfoItemsPage<InfoItem> initialPage = extractor().getInitialPage();
|
||||
@ -247,6 +249,7 @@ public class YoutubeSearchExtractorTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Disabled("Known problem, see https://github.com/TeamNewPipe/NewPipeExtractor/issues/1274")
|
||||
public static class MetaInfoTest extends DefaultSearchExtractorTest {
|
||||
private static SearchExtractor extractor;
|
||||
private static final String QUERY = "Covid";
|
||||
@ -308,7 +311,7 @@ public class YoutubeSearchExtractorTest {
|
||||
void testAtLeastOneVerified() throws IOException, ExtractionException {
|
||||
final List<InfoItem> items = extractor.getInitialPage().getItems();
|
||||
boolean verified = false;
|
||||
for (InfoItem item : items) {
|
||||
for (final InfoItem item : items) {
|
||||
if (((ChannelInfoItem) item).isVerified()) {
|
||||
verified = true;
|
||||
break;
|
||||
@ -380,6 +383,12 @@ public class YoutubeSearchExtractorTest {
|
||||
final List<InfoItem> items = extractor.getInitialPage().getItems();
|
||||
assertNotNull(((StreamInfoItem) items.get(0)).getShortDescription());
|
||||
}
|
||||
|
||||
@Disabled("Irrelevant - sometimes suggestions show up, sometimes not")
|
||||
@Override
|
||||
public void testSearchSuggestion() throws Exception {
|
||||
super.testSearchSuggestion();
|
||||
}
|
||||
}
|
||||
|
||||
public static class ShortFormContent extends DefaultSearchExtractorTest {
|
||||
|
@ -3,6 +3,7 @@ package org.schabi.newpipe.extractor.services.youtube.stream;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.schabi.newpipe.downloader.DownloaderFactory;
|
||||
import org.schabi.newpipe.extractor.NewPipe;
|
||||
import org.schabi.newpipe.extractor.StreamingService;
|
||||
@ -16,6 +17,7 @@ import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@Disabled("There is currently no way to extract age-restricted videos")
|
||||
public class YoutubeStreamExtractorAgeRestrictedTest extends DefaultStreamExtractorTest {
|
||||
private static final String RESOURCE_PATH = DownloaderFactory.RESOURCE_PATH + "services/youtube/extractor/stream/";
|
||||
private static final String ID = "rwcfPqbAx-0";
|
||||
|
@ -43,7 +43,7 @@ public class YoutubeStreamExtractorControversialTest extends DefaultStreamExtrac
|
||||
@Override public String expectedOriginalUrlContains() { return URL; }
|
||||
|
||||
@Override public StreamType expectedStreamType() { return StreamType.VIDEO_STREAM; }
|
||||
@Override public String expectedUploaderName() { return "Amazing Atheist"; }
|
||||
@Override public String expectedUploaderName() { return "INTO THE FRAY"; }
|
||||
@Override public String expectedUploaderUrl() { return "https://www.youtube.com/channel/UCjNxszyFPasDdRoD9J6X-sw"; }
|
||||
@Override public long expectedUploaderSubscriberCountAtLeast() { return 900_000; }
|
||||
@Override public List<String> expectedDescriptionContains() {
|
||||
|
@ -68,14 +68,14 @@ public class YoutubeStreamExtractorDefaultTest {
|
||||
public static final String YOUTUBE_LICENCE = "YouTube licence";
|
||||
|
||||
public static class NotAvailable {
|
||||
@BeforeAll
|
||||
public static void setUp() throws IOException {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "notAvailable"));
|
||||
}
|
||||
private static final String RESOURCE_PATH =
|
||||
YoutubeStreamExtractorDefaultTest.RESOURCE_PATH + "notAvailable/";
|
||||
|
||||
@Test
|
||||
void geoRestrictedContent() throws Exception {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "restricted"));
|
||||
|
||||
final StreamExtractor extractor =
|
||||
YouTube.getStreamExtractor(BASE_URL + "_PL2HJKxnOM");
|
||||
assertThrows(GeographicRestrictionException.class, extractor::fetchPage);
|
||||
@ -83,6 +83,9 @@ public class YoutubeStreamExtractorDefaultTest {
|
||||
|
||||
@Test
|
||||
void nonExistentFetch() throws Exception {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "nonExistent"));
|
||||
|
||||
final StreamExtractor extractor =
|
||||
YouTube.getStreamExtractor(BASE_URL + "don-t-exist");
|
||||
assertThrows(ContentNotAvailableException.class, extractor::fetchPage);
|
||||
@ -90,6 +93,9 @@ public class YoutubeStreamExtractorDefaultTest {
|
||||
|
||||
@Test
|
||||
void invalidId() throws Exception {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "invalidId"));
|
||||
|
||||
final StreamExtractor extractor =
|
||||
YouTube.getStreamExtractor(BASE_URL + "INVALID_ID_INVALID_ID");
|
||||
assertThrows(ParsingException.class, extractor::fetchPage);
|
||||
@ -97,6 +103,9 @@ public class YoutubeStreamExtractorDefaultTest {
|
||||
|
||||
@Test
|
||||
void paidContent() throws Exception {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "paidContent"));
|
||||
|
||||
final StreamExtractor extractor =
|
||||
YouTube.getStreamExtractor(BASE_URL + "ayI2iBwGdxw");
|
||||
assertThrows(PaidContentException.class, extractor::fetchPage);
|
||||
@ -104,6 +113,9 @@ public class YoutubeStreamExtractorDefaultTest {
|
||||
|
||||
@Test
|
||||
void privateContent() throws Exception {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "privateContent"));
|
||||
|
||||
final StreamExtractor extractor =
|
||||
YouTube.getStreamExtractor(BASE_URL + "8VajtrESJzA");
|
||||
assertThrows(PrivateContentException.class, extractor::fetchPage);
|
||||
@ -111,6 +123,9 @@ public class YoutubeStreamExtractorDefaultTest {
|
||||
|
||||
@Test
|
||||
void youtubeMusicPremiumContent() throws Exception {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "musicPremiumContent"));
|
||||
|
||||
final StreamExtractor extractor =
|
||||
YouTube.getStreamExtractor(BASE_URL + "sMJ8bRN2dak");
|
||||
assertThrows(YoutubeMusicPremiumContentException.class, extractor::fetchPage);
|
||||
|
@ -1,18 +0,0 @@
|
||||
package org.schabi.newpipe.extractor.utils;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
public class ProtoBuilderTest {
|
||||
@Test
|
||||
public void testProtoBuilder() {
|
||||
final ProtoBuilder pb = new ProtoBuilder();
|
||||
pb.varint(1, 128);
|
||||
pb.varint(2, 1234567890);
|
||||
pb.varint(3, 1234567890123456789L);
|
||||
pb.string(4, "Hello");
|
||||
pb.bytes(5, new byte[]{1, 2, 3});
|
||||
assertEquals("CIABENKF2MwEGJWCpu_HnoSRESIFSGVsbG8qAwECAw%3D%3D", pb.toUrlencodedBase64());
|
||||
}
|
||||
}
|
@ -44,10 +44,10 @@
|
||||
"same-origin; report-to\u003d\"youtube_main\""
|
||||
],
|
||||
"date": [
|
||||
"Sun, 10 Nov 2024 17:58:42 GMT"
|
||||
"Wed, 12 Feb 2025 20:54:00 GMT"
|
||||
],
|
||||
"expires": [
|
||||
"Sun, 10 Nov 2024 17:58:42 GMT"
|
||||
"Wed, 12 Feb 2025 20:54:00 GMT"
|
||||
],
|
||||
"origin-trial": [
|
||||
"AmhMBR6zCLzDDxpW+HfpP67BqwIknWnyMOXOQGfzYswFmJe+fgaI6XZgAzcxOrzNtP7hEDsOo1jdjFnVr2IdxQ4AAAB4eyJvcmlnaW4iOiJodHRwczovL3lvdXR1YmUuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJWaWV3WFJlcXVlc3RlZFdpdGhEZXByZWNhdGlvbiIsImV4cGlyeSI6MTc1ODA2NzE5OSwiaXNTdWJkb21haW4iOnRydWV9"
|
||||
@ -65,8 +65,8 @@
|
||||
"ESF"
|
||||
],
|
||||
"set-cookie": [
|
||||
"YSC\u003d8brHwG9jSy4; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003d; Domain\u003d.youtube.com; Expires\u003dMon, 14-Feb-2022 17:58:42 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone"
|
||||
"YSC\u003dmDWZXj-Vobk; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003d; Domain\u003d.youtube.com; Expires\u003dThu, 19-May-2022 20:54:00 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone"
|
||||
],
|
||||
"strict-transport-security": [
|
||||
"max-age\u003d31536000"
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,87 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "GET",
|
||||
"url": "https://www.youtube.com/sw.js",
|
||||
"headers": {
|
||||
"Referer": [
|
||||
"https://www.youtube.com"
|
||||
],
|
||||
"Origin": [
|
||||
"https://www.youtube.com"
|
||||
],
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"access-control-allow-credentials": [
|
||||
"true"
|
||||
],
|
||||
"access-control-allow-origin": [
|
||||
"https://www.youtube.com"
|
||||
],
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"cache-control": [
|
||||
"private, max-age\u003d0"
|
||||
],
|
||||
"content-security-policy": [
|
||||
"require-trusted-types-for \u0027script\u0027"
|
||||
],
|
||||
"content-type": [
|
||||
"text/javascript; charset\u003dutf-8"
|
||||
],
|
||||
"cross-origin-opener-policy": [
|
||||
"same-origin; report-to\u003d\"youtube_main\""
|
||||
],
|
||||
"date": [
|
||||
"Tue, 11 Feb 2025 21:26:02 GMT"
|
||||
],
|
||||
"expires": [
|
||||
"Tue, 11 Feb 2025 21:26:02 GMT"
|
||||
],
|
||||
"origin-trial": [
|
||||
"AmhMBR6zCLzDDxpW+HfpP67BqwIknWnyMOXOQGfzYswFmJe+fgaI6XZgAzcxOrzNtP7hEDsOo1jdjFnVr2IdxQ4AAAB4eyJvcmlnaW4iOiJodHRwczovL3lvdXR1YmUuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJWaWV3WFJlcXVlc3RlZFdpdGhEZXByZWNhdGlvbiIsImV4cGlyeSI6MTc1ODA2NzE5OSwiaXNTdWJkb21haW4iOnRydWV9"
|
||||
],
|
||||
"p3p": [
|
||||
"CP\u003d\"This is not a P3P policy! See http://support.google.com/accounts/answer/151657?hl\u003den-GB for more info.\""
|
||||
],
|
||||
"permissions-policy": [
|
||||
"ch-ua-arch\u003d*, ch-ua-bitness\u003d*, ch-ua-full-version\u003d*, ch-ua-full-version-list\u003d*, ch-ua-model\u003d*, ch-ua-wow64\u003d*, ch-ua-form-factors\u003d*, ch-ua-platform\u003d*, ch-ua-platform-version\u003d*"
|
||||
],
|
||||
"report-to": [
|
||||
"{\"group\":\"youtube_main\",\"max_age\":2592000,\"endpoints\":[{\"url\":\"https://csp.withgoogle.com/csp/report-to/youtube_main\"}]}"
|
||||
],
|
||||
"server": [
|
||||
"ESF"
|
||||
],
|
||||
"set-cookie": [
|
||||
"YSC\u003d_I6wGtLOmJs; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003d; Domain\u003d.youtube.com; Expires\u003dWed, 18-May-2022 21:26:02 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone"
|
||||
],
|
||||
"strict-transport-security": [
|
||||
"max-age\u003d31536000"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "\n self.addEventListener(\u0027install\u0027, event \u003d\u003e {\n event.waitUntil(self.skipWaiting());\n });\n self.addEventListener(\u0027activate\u0027, event \u003d\u003e {\n event.waitUntil(\n self.clients.claim().then(() \u003d\u003e self.registration.unregister()));\n });\n ",
|
||||
"latestUrl": "https://www.youtube.com/sw.js"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,324 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "POST",
|
||||
"url": "https://www.youtube.com/youtubei/v1/visitor_id?prettyPrint\u003dfalse",
|
||||
"headers": {
|
||||
"Referer": [
|
||||
"https://www.youtube.com"
|
||||
],
|
||||
"Origin": [
|
||||
"https://www.youtube.com"
|
||||
],
|
||||
"Cookie": [
|
||||
"SOCS\u003dCAE\u003d"
|
||||
],
|
||||
"X-YouTube-Client-Version": [
|
||||
"2.20250210.02.00"
|
||||
],
|
||||
"X-YouTube-Client-Name": [
|
||||
"1"
|
||||
],
|
||||
"Content-Type": [
|
||||
"application/json"
|
||||
],
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"dataToSend": [
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
111,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
120,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
114,
|
||||
101,
|
||||
113,
|
||||
117,
|
||||
101,
|
||||
115,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
114,
|
||||
110,
|
||||
97,
|
||||
108,
|
||||
69,
|
||||
120,
|
||||
112,
|
||||
101,
|
||||
114,
|
||||
105,
|
||||
109,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
70,
|
||||
108,
|
||||
97,
|
||||
103,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
91,
|
||||
93,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
83,
|
||||
115,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
116,
|
||||
114,
|
||||
117,
|
||||
101,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
117,
|
||||
116,
|
||||
99,
|
||||
79,
|
||||
102,
|
||||
102,
|
||||
115,
|
||||
101,
|
||||
116,
|
||||
77,
|
||||
105,
|
||||
110,
|
||||
117,
|
||||
116,
|
||||
101,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
48,
|
||||
44,
|
||||
34,
|
||||
104,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
101,
|
||||
110,
|
||||
45,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
78,
|
||||
97,
|
||||
109,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
87,
|
||||
69,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
103,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
83,
|
||||
99,
|
||||
114,
|
||||
101,
|
||||
101,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
87,
|
||||
65,
|
||||
84,
|
||||
67,
|
||||
72,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
86,
|
||||
101,
|
||||
114,
|
||||
115,
|
||||
105,
|
||||
111,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
50,
|
||||
46,
|
||||
50,
|
||||
48,
|
||||
50,
|
||||
53,
|
||||
48,
|
||||
50,
|
||||
49,
|
||||
48,
|
||||
46,
|
||||
48,
|
||||
50,
|
||||
46,
|
||||
48,
|
||||
48,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
112,
|
||||
108,
|
||||
97,
|
||||
116,
|
||||
102,
|
||||
111,
|
||||
114,
|
||||
109,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
68,
|
||||
69,
|
||||
83,
|
||||
75,
|
||||
84,
|
||||
79,
|
||||
80,
|
||||
34,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
114,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
108,
|
||||
111,
|
||||
99,
|
||||
107,
|
||||
101,
|
||||
100,
|
||||
83,
|
||||
97,
|
||||
102,
|
||||
101,
|
||||
116,
|
||||
121,
|
||||
77,
|
||||
111,
|
||||
100,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
102,
|
||||
97,
|
||||
108,
|
||||
115,
|
||||
101,
|
||||
125,
|
||||
125,
|
||||
125
|
||||
],
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"content-type": [
|
||||
"application/json; charset\u003dUTF-8"
|
||||
],
|
||||
"date": [
|
||||
"Tue, 11 Feb 2025 21:26:03 GMT"
|
||||
],
|
||||
"server": [
|
||||
"scaffolding on HTTPServer2"
|
||||
],
|
||||
"vary": [
|
||||
"Origin",
|
||||
"X-Origin",
|
||||
"Referer"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "{\"responseContext\":{\"visitorData\":\"CgtBVVlNdGNaUXVvMCjrgK-9BjIKCgJERRIEEgAgHQ%3D%3D\",\"serviceTrackingParams\":[{\"service\":\"CSI\",\"params\":[{\"key\":\"c\",\"value\":\"WEB\"},{\"key\":\"cver\",\"value\":\"2.20250210.02.00\"},{\"key\":\"yt_li\",\"value\":\"0\"},{\"key\":\"GetVisitorId_rid\",\"value\":\"0xaa026f1cc36208bd\"}]},{\"service\":\"GFEEDBACK\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"},{\"key\":\"visitor_data\",\"value\":\"CgtBVVlNdGNaUXVvMCjrgK-9BjIKCgJERRIEEgAgHQ%3D%3D\"}]},{\"service\":\"GUIDED_HELP\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"}]},{\"service\":\"ECATCHER\",\"params\":[{\"key\":\"client.version\",\"value\":\"2.20250210\"},{\"key\":\"client.name\",\"value\":\"WEB\"}]}],\"mainAppWebResponseContext\":{\"loggedOut\":true},\"webResponseContextExtensionData\":{\"hasDecorated\":true}}}",
|
||||
"latestUrl": "https://www.youtube.com/youtubei/v1/visitor_id?prettyPrint\u003dfalse"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,80 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "GET",
|
||||
"url": "https://www.youtube.com/iframe_api",
|
||||
"headers": {
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"cache-control": [
|
||||
"private, max-age\u003d0"
|
||||
],
|
||||
"content-security-policy": [
|
||||
"require-trusted-types-for \u0027script\u0027"
|
||||
],
|
||||
"content-type": [
|
||||
"text/javascript; charset\u003dutf-8"
|
||||
],
|
||||
"cross-origin-opener-policy-report-only": [
|
||||
"same-origin; report-to\u003d\"youtube_main\""
|
||||
],
|
||||
"cross-origin-resource-policy": [
|
||||
"cross-origin"
|
||||
],
|
||||
"date": [
|
||||
"Tue, 11 Feb 2025 21:26:05 GMT"
|
||||
],
|
||||
"expires": [
|
||||
"Tue, 11 Feb 2025 21:26:05 GMT"
|
||||
],
|
||||
"origin-trial": [
|
||||
"AmhMBR6zCLzDDxpW+HfpP67BqwIknWnyMOXOQGfzYswFmJe+fgaI6XZgAzcxOrzNtP7hEDsOo1jdjFnVr2IdxQ4AAAB4eyJvcmlnaW4iOiJodHRwczovL3lvdXR1YmUuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJWaWV3WFJlcXVlc3RlZFdpdGhEZXByZWNhdGlvbiIsImV4cGlyeSI6MTc1ODA2NzE5OSwiaXNTdWJkb21haW4iOnRydWV9"
|
||||
],
|
||||
"p3p": [
|
||||
"CP\u003d\"This is not a P3P policy! See http://support.google.com/accounts/answer/151657?hl\u003den-GB for more info.\""
|
||||
],
|
||||
"permissions-policy": [
|
||||
"ch-ua-arch\u003d*, ch-ua-bitness\u003d*, ch-ua-full-version\u003d*, ch-ua-full-version-list\u003d*, ch-ua-model\u003d*, ch-ua-wow64\u003d*, ch-ua-form-factors\u003d*, ch-ua-platform\u003d*, ch-ua-platform-version\u003d*"
|
||||
],
|
||||
"report-to": [
|
||||
"{\"group\":\"youtube_main\",\"max_age\":2592000,\"endpoints\":[{\"url\":\"https://csp.withgoogle.com/csp/report-to/youtube_main\"}]}"
|
||||
],
|
||||
"server": [
|
||||
"ESF"
|
||||
],
|
||||
"set-cookie": [
|
||||
"YSC\u003daF72zm975Ks; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003d-F9WHpkngmo; Domain\u003d.youtube.com; Expires\u003dSun, 10-Aug-2025 21:26:05 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_PRIVACY_METADATA\u003dCgJERRIEEgAgOg%3D%3D; Domain\u003d.youtube.com; Expires\u003dSun, 10-Aug-2025 21:26:05 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"__Secure-ROLLOUT_TOKEN\u003dCKrs3vud77f8PhDk38bHx7yLAxjk38bHx7yLAw%3D%3D; Domain\u003dyoutube.com; Expires\u003dSun, 10-Aug-2025 21:26:05 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone; Partitioned"
|
||||
],
|
||||
"strict-transport-security": [
|
||||
"max-age\u003d31536000"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "var scriptUrl \u003d \u0027https:\\/\\/www.youtube.com\\/s\\/player\\/af7f576f\\/www-widgetapi.vflset\\/www-widgetapi.js\u0027;window[\u0027yt_embedsEnableAutoplayAndVisibilitySignals\u0027] \u003d true ;try{var ttPolicy\u003dwindow.trustedTypes.createPolicy(\"youtube-widget-api\",{createScriptURL:function(x){return x}});scriptUrl\u003dttPolicy.createScriptURL(scriptUrl)}catch(e){}var YT;if(!window[\"YT\"])YT\u003d{loading:0,loaded:0};var YTConfig;if(!window[\"YTConfig\"])YTConfig\u003d{\"host\":\"https://www.youtube.com\"};\nif(!YT.loading){YT.loading\u003d1;(function(){var l\u003d[];YT.ready\u003dfunction(f){if(YT.loaded)f();else l.push(f)};window.onYTReady\u003dfunction(){YT.loaded\u003d1;var i\u003d0;for(;i\u003cl.length;i++)try{l[i]()}catch(e){}};YT.setConfig\u003dfunction(c){var k;for(k in c)if(c.hasOwnProperty(k))YTConfig[k]\u003dc[k]};var a\u003ddocument.createElement(\"script\");a.type\u003d\"text/javascript\";a.id\u003d\"www-widgetapi-script\";a.src\u003dscriptUrl;a.async\u003dtrue;var c\u003ddocument.currentScript;if(c){var n\u003dc.nonce||c.getAttribute(\"nonce\");if(n)a.setAttribute(\"nonce\",\nn)}var b\u003ddocument.getElementsByTagName(\"script\")[0];b.parentNode.insertBefore(a,b)})()};\n",
|
||||
"latestUrl": "https://www.youtube.com/iframe_api"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,369 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "POST",
|
||||
"url": "https://youtubei.googleapis.com/youtubei/v1/visitor_id?prettyPrint\u003dfalse",
|
||||
"headers": {
|
||||
"User-Agent": [
|
||||
"com.google.android.youtube/19.28.35 (Linux; U; Android 15; GB) gzip"
|
||||
],
|
||||
"X-Goog-Api-Format-Version": [
|
||||
"2"
|
||||
],
|
||||
"Content-Type": [
|
||||
"application/json"
|
||||
],
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"dataToSend": [
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
111,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
120,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
114,
|
||||
101,
|
||||
113,
|
||||
117,
|
||||
101,
|
||||
115,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
114,
|
||||
110,
|
||||
97,
|
||||
108,
|
||||
69,
|
||||
120,
|
||||
112,
|
||||
101,
|
||||
114,
|
||||
105,
|
||||
109,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
70,
|
||||
108,
|
||||
97,
|
||||
103,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
91,
|
||||
93,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
83,
|
||||
115,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
116,
|
||||
114,
|
||||
117,
|
||||
101,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
97,
|
||||
110,
|
||||
100,
|
||||
114,
|
||||
111,
|
||||
105,
|
||||
100,
|
||||
83,
|
||||
100,
|
||||
107,
|
||||
86,
|
||||
101,
|
||||
114,
|
||||
115,
|
||||
105,
|
||||
111,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
51,
|
||||
53,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
116,
|
||||
99,
|
||||
79,
|
||||
102,
|
||||
102,
|
||||
115,
|
||||
101,
|
||||
116,
|
||||
77,
|
||||
105,
|
||||
110,
|
||||
117,
|
||||
116,
|
||||
101,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
48,
|
||||
44,
|
||||
34,
|
||||
111,
|
||||
115,
|
||||
86,
|
||||
101,
|
||||
114,
|
||||
115,
|
||||
105,
|
||||
111,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
49,
|
||||
53,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
104,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
101,
|
||||
110,
|
||||
45,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
78,
|
||||
97,
|
||||
109,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
65,
|
||||
78,
|
||||
68,
|
||||
82,
|
||||
79,
|
||||
73,
|
||||
68,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
103,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
83,
|
||||
99,
|
||||
114,
|
||||
101,
|
||||
101,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
87,
|
||||
65,
|
||||
84,
|
||||
67,
|
||||
72,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
86,
|
||||
101,
|
||||
114,
|
||||
115,
|
||||
105,
|
||||
111,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
49,
|
||||
57,
|
||||
46,
|
||||
50,
|
||||
56,
|
||||
46,
|
||||
51,
|
||||
53,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
111,
|
||||
115,
|
||||
78,
|
||||
97,
|
||||
109,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
65,
|
||||
110,
|
||||
100,
|
||||
114,
|
||||
111,
|
||||
105,
|
||||
100,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
112,
|
||||
108,
|
||||
97,
|
||||
116,
|
||||
102,
|
||||
111,
|
||||
114,
|
||||
109,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
77,
|
||||
79,
|
||||
66,
|
||||
73,
|
||||
76,
|
||||
69,
|
||||
34,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
114,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
108,
|
||||
111,
|
||||
99,
|
||||
107,
|
||||
101,
|
||||
100,
|
||||
83,
|
||||
97,
|
||||
102,
|
||||
101,
|
||||
116,
|
||||
121,
|
||||
77,
|
||||
111,
|
||||
100,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
102,
|
||||
97,
|
||||
108,
|
||||
115,
|
||||
101,
|
||||
125,
|
||||
125,
|
||||
125
|
||||
],
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"content-type": [
|
||||
"application/json; charset\u003dUTF-8"
|
||||
],
|
||||
"date": [
|
||||
"Tue, 11 Feb 2025 21:26:08 GMT"
|
||||
],
|
||||
"server": [
|
||||
"scaffolding on HTTPServer2"
|
||||
],
|
||||
"vary": [
|
||||
"Origin",
|
||||
"X-Origin",
|
||||
"Referer"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "{\"responseContext\":{\"visitorData\":\"Cgt6RmJ4SGNjMkp4cyjwgK-9BjIKCgJERRIEEgAgGToMCAEgwp71rYOO8NVn\",\"serviceTrackingParams\":[{\"service\":\"CSI\",\"params\":[{\"key\":\"c\",\"value\":\"ANDROID\"},{\"key\":\"cver\",\"value\":\"19.28.35\"},{\"key\":\"yt_li\",\"value\":\"0\"},{\"key\":\"GetVisitorId_rid\",\"value\":\"0xbeaa9019cf98b3bf\"}]},{\"service\":\"GFEEDBACK\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"},{\"key\":\"visitor_data\",\"value\":\"Cgt6RmJ4SGNjMkp4cyjwgK-9BjIKCgJERRIEEgAgGToMCAEgwp71rYOO8NVn\"}]},{\"service\":\"GUIDED_HELP\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"}]},{\"service\":\"ECATCHER\",\"params\":[{\"key\":\"client.version\",\"value\":\"19.28\"},{\"key\":\"client.name\",\"value\":\"ANDROID\"}]}]}}",
|
||||
"latestUrl": "https://youtubei.googleapis.com/youtubei/v1/visitor_id?prettyPrint\u003dfalse"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,80 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "GET",
|
||||
"url": "https://www.youtube.com/iframe_api",
|
||||
"headers": {
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"cache-control": [
|
||||
"private, max-age\u003d0"
|
||||
],
|
||||
"content-security-policy": [
|
||||
"require-trusted-types-for \u0027script\u0027"
|
||||
],
|
||||
"content-type": [
|
||||
"text/javascript; charset\u003dutf-8"
|
||||
],
|
||||
"cross-origin-opener-policy-report-only": [
|
||||
"same-origin; report-to\u003d\"youtube_main\""
|
||||
],
|
||||
"cross-origin-resource-policy": [
|
||||
"cross-origin"
|
||||
],
|
||||
"date": [
|
||||
"Wed, 12 Feb 2025 20:47:12 GMT"
|
||||
],
|
||||
"expires": [
|
||||
"Wed, 12 Feb 2025 20:47:12 GMT"
|
||||
],
|
||||
"origin-trial": [
|
||||
"AmhMBR6zCLzDDxpW+HfpP67BqwIknWnyMOXOQGfzYswFmJe+fgaI6XZgAzcxOrzNtP7hEDsOo1jdjFnVr2IdxQ4AAAB4eyJvcmlnaW4iOiJodHRwczovL3lvdXR1YmUuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJWaWV3WFJlcXVlc3RlZFdpdGhEZXByZWNhdGlvbiIsImV4cGlyeSI6MTc1ODA2NzE5OSwiaXNTdWJkb21haW4iOnRydWV9"
|
||||
],
|
||||
"p3p": [
|
||||
"CP\u003d\"This is not a P3P policy! See http://support.google.com/accounts/answer/151657?hl\u003den-GB for more info.\""
|
||||
],
|
||||
"permissions-policy": [
|
||||
"ch-ua-arch\u003d*, ch-ua-bitness\u003d*, ch-ua-full-version\u003d*, ch-ua-full-version-list\u003d*, ch-ua-model\u003d*, ch-ua-wow64\u003d*, ch-ua-form-factors\u003d*, ch-ua-platform\u003d*, ch-ua-platform-version\u003d*"
|
||||
],
|
||||
"report-to": [
|
||||
"{\"group\":\"youtube_main\",\"max_age\":2592000,\"endpoints\":[{\"url\":\"https://csp.withgoogle.com/csp/report-to/youtube_main\"}]}"
|
||||
],
|
||||
"server": [
|
||||
"ESF"
|
||||
],
|
||||
"set-cookie": [
|
||||
"YSC\u003dLSDP0xuw9oM; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003dYUDo12GY-Ns; Domain\u003d.youtube.com; Expires\u003dMon, 11-Aug-2025 20:47:12 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_PRIVACY_METADATA\u003dCgJERRIEEgAgVQ%3D%3D; Domain\u003d.youtube.com; Expires\u003dMon, 11-Aug-2025 20:47:12 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"__Secure-ROLLOUT_TOKEN\u003dCOLR6u6oxIKUYRD8rIXegL-LAxj8rIXegL-LAw%3D%3D; Domain\u003dyoutube.com; Expires\u003dMon, 11-Aug-2025 20:47:12 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone; Partitioned"
|
||||
],
|
||||
"strict-transport-security": [
|
||||
"max-age\u003d31536000"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "var scriptUrl \u003d \u0027https:\\/\\/www.youtube.com\\/s\\/player\\/e7567ecf\\/www-widgetapi.vflset\\/www-widgetapi.js\u0027;try{var ttPolicy\u003dwindow.trustedTypes.createPolicy(\"youtube-widget-api\",{createScriptURL:function(x){return x}});scriptUrl\u003dttPolicy.createScriptURL(scriptUrl)}catch(e){}var YT;if(!window[\"YT\"])YT\u003d{loading:0,loaded:0};var YTConfig;if(!window[\"YTConfig\"])YTConfig\u003d{\"host\":\"https://www.youtube.com\"};\nif(!YT.loading){YT.loading\u003d1;(function(){var l\u003d[];YT.ready\u003dfunction(f){if(YT.loaded)f();else l.push(f)};window.onYTReady\u003dfunction(){YT.loaded\u003d1;var i\u003d0;for(;i\u003cl.length;i++)try{l[i]()}catch(e){}};YT.setConfig\u003dfunction(c){var k;for(k in c)if(c.hasOwnProperty(k))YTConfig[k]\u003dc[k]};var a\u003ddocument.createElement(\"script\");a.type\u003d\"text/javascript\";a.id\u003d\"www-widgetapi-script\";a.src\u003dscriptUrl;a.async\u003dtrue;var c\u003ddocument.currentScript;if(c){var n\u003dc.nonce||c.getAttribute(\"nonce\");if(n)a.setAttribute(\"nonce\",\nn)}var b\u003ddocument.getElementsByTagName(\"script\")[0];b.parentNode.insertBefore(a,b)})()};\n",
|
||||
"latestUrl": "https://www.youtube.com/iframe_api"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,80 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "GET",
|
||||
"url": "https://www.youtube.com/iframe_api",
|
||||
"headers": {
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"cache-control": [
|
||||
"private, max-age\u003d0"
|
||||
],
|
||||
"content-security-policy": [
|
||||
"require-trusted-types-for \u0027script\u0027"
|
||||
],
|
||||
"content-type": [
|
||||
"text/javascript; charset\u003dutf-8"
|
||||
],
|
||||
"cross-origin-opener-policy-report-only": [
|
||||
"same-origin; report-to\u003d\"youtube_main\""
|
||||
],
|
||||
"cross-origin-resource-policy": [
|
||||
"cross-origin"
|
||||
],
|
||||
"date": [
|
||||
"Wed, 12 Feb 2025 20:47:13 GMT"
|
||||
],
|
||||
"expires": [
|
||||
"Wed, 12 Feb 2025 20:47:13 GMT"
|
||||
],
|
||||
"origin-trial": [
|
||||
"AmhMBR6zCLzDDxpW+HfpP67BqwIknWnyMOXOQGfzYswFmJe+fgaI6XZgAzcxOrzNtP7hEDsOo1jdjFnVr2IdxQ4AAAB4eyJvcmlnaW4iOiJodHRwczovL3lvdXR1YmUuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJWaWV3WFJlcXVlc3RlZFdpdGhEZXByZWNhdGlvbiIsImV4cGlyeSI6MTc1ODA2NzE5OSwiaXNTdWJkb21haW4iOnRydWV9"
|
||||
],
|
||||
"p3p": [
|
||||
"CP\u003d\"This is not a P3P policy! See http://support.google.com/accounts/answer/151657?hl\u003den-GB for more info.\""
|
||||
],
|
||||
"permissions-policy": [
|
||||
"ch-ua-arch\u003d*, ch-ua-bitness\u003d*, ch-ua-full-version\u003d*, ch-ua-full-version-list\u003d*, ch-ua-model\u003d*, ch-ua-wow64\u003d*, ch-ua-form-factors\u003d*, ch-ua-platform\u003d*, ch-ua-platform-version\u003d*"
|
||||
],
|
||||
"report-to": [
|
||||
"{\"group\":\"youtube_main\",\"max_age\":2592000,\"endpoints\":[{\"url\":\"https://csp.withgoogle.com/csp/report-to/youtube_main\"}]}"
|
||||
],
|
||||
"server": [
|
||||
"ESF"
|
||||
],
|
||||
"set-cookie": [
|
||||
"YSC\u003dtzEnsk-eXCk; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"__Secure-ROLLOUT_TOKEN\u003dCPPNh_3Pir_-bBDG_szegL-LAxjG_szegL-LAw%3D%3D; Domain\u003dyoutube.com; Expires\u003dMon, 11-Aug-2025 20:47:13 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone; Partitioned",
|
||||
"VISITOR_INFO1_LIVE\u003dsfuOn2eMIeg; Domain\u003d.youtube.com; Expires\u003dMon, 11-Aug-2025 20:47:13 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_PRIVACY_METADATA\u003dCgJERRIEEgAgVQ%3D%3D; Domain\u003d.youtube.com; Expires\u003dMon, 11-Aug-2025 20:47:13 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone"
|
||||
],
|
||||
"strict-transport-security": [
|
||||
"max-age\u003d31536000"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "var scriptUrl \u003d \u0027https:\\/\\/www.youtube.com\\/s\\/player\\/074a8365\\/www-widgetapi.vflset\\/www-widgetapi.js\u0027;try{var ttPolicy\u003dwindow.trustedTypes.createPolicy(\"youtube-widget-api\",{createScriptURL:function(x){return x}});scriptUrl\u003dttPolicy.createScriptURL(scriptUrl)}catch(e){}var YT;if(!window[\"YT\"])YT\u003d{loading:0,loaded:0};var YTConfig;if(!window[\"YTConfig\"])YTConfig\u003d{\"host\":\"https://www.youtube.com\"};\nif(!YT.loading){YT.loading\u003d1;(function(){var l\u003d[];YT.ready\u003dfunction(f){if(YT.loaded)f();else l.push(f)};window.onYTReady\u003dfunction(){YT.loaded\u003d1;var i\u003d0;for(;i\u003cl.length;i++)try{l[i]()}catch(e){}};YT.setConfig\u003dfunction(c){var k;for(k in c)if(c.hasOwnProperty(k))YTConfig[k]\u003dc[k]};var a\u003ddocument.createElement(\"script\");a.type\u003d\"text/javascript\";a.id\u003d\"www-widgetapi-script\";a.src\u003dscriptUrl;a.async\u003dtrue;var c\u003ddocument.currentScript;if(c){var n\u003dc.nonce||c.getAttribute(\"nonce\");if(n)a.setAttribute(\"nonce\",\nn)}var b\u003ddocument.getElementsByTagName(\"script\")[0];b.parentNode.insertBefore(a,b)})()};\n",
|
||||
"latestUrl": "https://www.youtube.com/iframe_api"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,80 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "GET",
|
||||
"url": "https://www.youtube.com/iframe_api",
|
||||
"headers": {
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"cache-control": [
|
||||
"private, max-age\u003d0"
|
||||
],
|
||||
"content-security-policy": [
|
||||
"require-trusted-types-for \u0027script\u0027"
|
||||
],
|
||||
"content-type": [
|
||||
"text/javascript; charset\u003dutf-8"
|
||||
],
|
||||
"cross-origin-opener-policy-report-only": [
|
||||
"same-origin; report-to\u003d\"youtube_main\""
|
||||
],
|
||||
"cross-origin-resource-policy": [
|
||||
"cross-origin"
|
||||
],
|
||||
"date": [
|
||||
"Wed, 12 Feb 2025 20:47:15 GMT"
|
||||
],
|
||||
"expires": [
|
||||
"Wed, 12 Feb 2025 20:47:15 GMT"
|
||||
],
|
||||
"origin-trial": [
|
||||
"AmhMBR6zCLzDDxpW+HfpP67BqwIknWnyMOXOQGfzYswFmJe+fgaI6XZgAzcxOrzNtP7hEDsOo1jdjFnVr2IdxQ4AAAB4eyJvcmlnaW4iOiJodHRwczovL3lvdXR1YmUuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJWaWV3WFJlcXVlc3RlZFdpdGhEZXByZWNhdGlvbiIsImV4cGlyeSI6MTc1ODA2NzE5OSwiaXNTdWJkb21haW4iOnRydWV9"
|
||||
],
|
||||
"p3p": [
|
||||
"CP\u003d\"This is not a P3P policy! See http://support.google.com/accounts/answer/151657?hl\u003den-GB for more info.\""
|
||||
],
|
||||
"permissions-policy": [
|
||||
"ch-ua-arch\u003d*, ch-ua-bitness\u003d*, ch-ua-full-version\u003d*, ch-ua-full-version-list\u003d*, ch-ua-model\u003d*, ch-ua-wow64\u003d*, ch-ua-form-factors\u003d*, ch-ua-platform\u003d*, ch-ua-platform-version\u003d*"
|
||||
],
|
||||
"report-to": [
|
||||
"{\"group\":\"youtube_main\",\"max_age\":2592000,\"endpoints\":[{\"url\":\"https://csp.withgoogle.com/csp/report-to/youtube_main\"}]}"
|
||||
],
|
||||
"server": [
|
||||
"ESF"
|
||||
],
|
||||
"set-cookie": [
|
||||
"YSC\u003dJm83U0hjoJA; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"__Secure-ROLLOUT_TOKEN\u003dCLa4gJSBq9TnYBC4oJnfgL-LAxi4oJnfgL-LAw%3D%3D; Domain\u003dyoutube.com; Expires\u003dMon, 11-Aug-2025 20:47:15 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone; Partitioned",
|
||||
"VISITOR_INFO1_LIVE\u003dMdPCxAs6k6s; Domain\u003d.youtube.com; Expires\u003dMon, 11-Aug-2025 20:47:15 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_PRIVACY_METADATA\u003dCgJERRIEEgAgPQ%3D%3D; Domain\u003d.youtube.com; Expires\u003dMon, 11-Aug-2025 20:47:15 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone"
|
||||
],
|
||||
"strict-transport-security": [
|
||||
"max-age\u003d31536000"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "var scriptUrl \u003d \u0027https:\\/\\/www.youtube.com\\/s\\/player\\/074a8365\\/www-widgetapi.vflset\\/www-widgetapi.js\u0027;try{var ttPolicy\u003dwindow.trustedTypes.createPolicy(\"youtube-widget-api\",{createScriptURL:function(x){return x}});scriptUrl\u003dttPolicy.createScriptURL(scriptUrl)}catch(e){}var YT;if(!window[\"YT\"])YT\u003d{loading:0,loaded:0};var YTConfig;if(!window[\"YTConfig\"])YTConfig\u003d{\"host\":\"https://www.youtube.com\"};\nif(!YT.loading){YT.loading\u003d1;(function(){var l\u003d[];YT.ready\u003dfunction(f){if(YT.loaded)f();else l.push(f)};window.onYTReady\u003dfunction(){YT.loaded\u003d1;var i\u003d0;for(;i\u003cl.length;i++)try{l[i]()}catch(e){}};YT.setConfig\u003dfunction(c){var k;for(k in c)if(c.hasOwnProperty(k))YTConfig[k]\u003dc[k]};var a\u003ddocument.createElement(\"script\");a.type\u003d\"text/javascript\";a.id\u003d\"www-widgetapi-script\";a.src\u003dscriptUrl;a.async\u003dtrue;var c\u003ddocument.currentScript;if(c){var n\u003dc.nonce||c.getAttribute(\"nonce\");if(n)a.setAttribute(\"nonce\",\nn)}var b\u003ddocument.getElementsByTagName(\"script\")[0];b.parentNode.insertBefore(a,b)})()};\n",
|
||||
"latestUrl": "https://www.youtube.com/iframe_api"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,80 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "GET",
|
||||
"url": "https://www.youtube.com/iframe_api",
|
||||
"headers": {
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"cache-control": [
|
||||
"private, max-age\u003d0"
|
||||
],
|
||||
"content-security-policy": [
|
||||
"require-trusted-types-for \u0027script\u0027;report-uri /cspreport"
|
||||
],
|
||||
"content-type": [
|
||||
"text/javascript; charset\u003dutf-8"
|
||||
],
|
||||
"cross-origin-opener-policy-report-only": [
|
||||
"same-origin; report-to\u003d\"youtube_main\""
|
||||
],
|
||||
"cross-origin-resource-policy": [
|
||||
"cross-origin"
|
||||
],
|
||||
"date": [
|
||||
"Wed, 12 Feb 2025 20:47:16 GMT"
|
||||
],
|
||||
"expires": [
|
||||
"Wed, 12 Feb 2025 20:47:16 GMT"
|
||||
],
|
||||
"origin-trial": [
|
||||
"AmhMBR6zCLzDDxpW+HfpP67BqwIknWnyMOXOQGfzYswFmJe+fgaI6XZgAzcxOrzNtP7hEDsOo1jdjFnVr2IdxQ4AAAB4eyJvcmlnaW4iOiJodHRwczovL3lvdXR1YmUuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJWaWV3WFJlcXVlc3RlZFdpdGhEZXByZWNhdGlvbiIsImV4cGlyeSI6MTc1ODA2NzE5OSwiaXNTdWJkb21haW4iOnRydWV9"
|
||||
],
|
||||
"p3p": [
|
||||
"CP\u003d\"This is not a P3P policy! See http://support.google.com/accounts/answer/151657?hl\u003den-GB for more info.\""
|
||||
],
|
||||
"permissions-policy": [
|
||||
"ch-ua-arch\u003d*, ch-ua-bitness\u003d*, ch-ua-full-version\u003d*, ch-ua-full-version-list\u003d*, ch-ua-model\u003d*, ch-ua-wow64\u003d*, ch-ua-form-factors\u003d*, ch-ua-platform\u003d*, ch-ua-platform-version\u003d*"
|
||||
],
|
||||
"report-to": [
|
||||
"{\"group\":\"youtube_main\",\"max_age\":2592000,\"endpoints\":[{\"url\":\"https://csp.withgoogle.com/csp/report-to/youtube_main\"}]}"
|
||||
],
|
||||
"server": [
|
||||
"ESF"
|
||||
],
|
||||
"set-cookie": [
|
||||
"YSC\u003dkeWCa58A8nU; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003d-PZ3HazfI44; Domain\u003d.youtube.com; Expires\u003dMon, 11-Aug-2025 20:47:16 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_PRIVACY_METADATA\u003dCgJERRIEEgAgHw%3D%3D; Domain\u003d.youtube.com; Expires\u003dMon, 11-Aug-2025 20:47:16 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"__Secure-ROLLOUT_TOKEN\u003dCLeNmcyo2bTTqQEQiszl34C_iwMYiszl34C_iwM%3D; Domain\u003dyoutube.com; Expires\u003dMon, 11-Aug-2025 20:47:16 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone; Partitioned"
|
||||
],
|
||||
"strict-transport-security": [
|
||||
"max-age\u003d31536000"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "var scriptUrl \u003d \u0027https:\\/\\/www.youtube.com\\/s\\/player\\/074a8365\\/www-widgetapi.vflset\\/www-widgetapi.js\u0027;try{var ttPolicy\u003dwindow.trustedTypes.createPolicy(\"youtube-widget-api\",{createScriptURL:function(x){return x}});scriptUrl\u003dttPolicy.createScriptURL(scriptUrl)}catch(e){}var YT;if(!window[\"YT\"])YT\u003d{loading:0,loaded:0};var YTConfig;if(!window[\"YTConfig\"])YTConfig\u003d{\"host\":\"https://www.youtube.com\"};\nif(!YT.loading){YT.loading\u003d1;(function(){var l\u003d[];YT.ready\u003dfunction(f){if(YT.loaded)f();else l.push(f)};window.onYTReady\u003dfunction(){YT.loaded\u003d1;var i\u003d0;for(;i\u003cl.length;i++)try{l[i]()}catch(e){}};YT.setConfig\u003dfunction(c){var k;for(k in c)if(c.hasOwnProperty(k))YTConfig[k]\u003dc[k]};var a\u003ddocument.createElement(\"script\");a.type\u003d\"text/javascript\";a.id\u003d\"www-widgetapi-script\";a.src\u003dscriptUrl;a.async\u003dtrue;var c\u003ddocument.currentScript;if(c){var n\u003dc.nonce||c.getAttribute(\"nonce\");if(n)a.setAttribute(\"nonce\",\nn)}var b\u003ddocument.getElementsByTagName(\"script\")[0];b.parentNode.insertBefore(a,b)})()};\n",
|
||||
"latestUrl": "https://www.youtube.com/iframe_api"
|
||||
}
|
||||
}
|
@ -0,0 +1,315 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "POST",
|
||||
"url": "https://music.youtube.com/youtubei/v1/music/get_search_suggestions?prettyPrint\u003dfalse",
|
||||
"headers": {
|
||||
"Origin": [
|
||||
"https://music.youtube.com"
|
||||
],
|
||||
"Referer": [
|
||||
"https://music.youtube.com"
|
||||
],
|
||||
"X-YouTube-Client-Version": [
|
||||
"2.20250122.04.00"
|
||||
],
|
||||
"X-YouTube-Client-Name": [
|
||||
"67"
|
||||
],
|
||||
"Content-Type": [
|
||||
"application/json"
|
||||
],
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"dataToSend": [
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
111,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
120,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
78,
|
||||
97,
|
||||
109,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
87,
|
||||
69,
|
||||
66,
|
||||
95,
|
||||
82,
|
||||
69,
|
||||
77,
|
||||
73,
|
||||
88,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
86,
|
||||
101,
|
||||
114,
|
||||
115,
|
||||
105,
|
||||
111,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
49,
|
||||
46,
|
||||
50,
|
||||
48,
|
||||
50,
|
||||
53,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
50,
|
||||
46,
|
||||
48,
|
||||
49,
|
||||
46,
|
||||
48,
|
||||
48,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
104,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
101,
|
||||
110,
|
||||
45,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
103,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
112,
|
||||
108,
|
||||
97,
|
||||
116,
|
||||
102,
|
||||
111,
|
||||
114,
|
||||
109,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
68,
|
||||
69,
|
||||
83,
|
||||
75,
|
||||
84,
|
||||
79,
|
||||
80,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
116,
|
||||
99,
|
||||
79,
|
||||
102,
|
||||
102,
|
||||
115,
|
||||
101,
|
||||
116,
|
||||
77,
|
||||
105,
|
||||
110,
|
||||
117,
|
||||
116,
|
||||
101,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
48,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
114,
|
||||
101,
|
||||
113,
|
||||
117,
|
||||
101,
|
||||
115,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
114,
|
||||
110,
|
||||
97,
|
||||
108,
|
||||
69,
|
||||
120,
|
||||
112,
|
||||
101,
|
||||
114,
|
||||
105,
|
||||
109,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
70,
|
||||
108,
|
||||
97,
|
||||
103,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
91,
|
||||
93,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
83,
|
||||
115,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
116,
|
||||
114,
|
||||
117,
|
||||
101,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
114,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
108,
|
||||
111,
|
||||
99,
|
||||
107,
|
||||
101,
|
||||
100,
|
||||
83,
|
||||
97,
|
||||
102,
|
||||
101,
|
||||
116,
|
||||
121,
|
||||
77,
|
||||
111,
|
||||
100,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
102,
|
||||
97,
|
||||
108,
|
||||
115,
|
||||
101,
|
||||
125,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
112,
|
||||
117,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
34,
|
||||
125
|
||||
],
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"content-type": [
|
||||
"application/json; charset\u003dUTF-8"
|
||||
],
|
||||
"date": [
|
||||
"Tue, 11 Feb 2025 21:26:39 GMT"
|
||||
],
|
||||
"server": [
|
||||
"scaffolding on HTTPServer2"
|
||||
],
|
||||
"vary": [
|
||||
"Origin",
|
||||
"X-Origin",
|
||||
"Referer"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "{\"responseContext\":{\"visitorData\":\"CgtkdjdXeTVtWWhnVSiOga-9BjIKCgJERRIEEgAgCw%3D%3D\",\"serviceTrackingParams\":[{\"service\":\"CSI\",\"params\":[{\"key\":\"c\",\"value\":\"WEB_REMIX\"},{\"key\":\"cver\",\"value\":\"1.20250122.01.00\"},{\"key\":\"yt_li\",\"value\":\"0\"},{\"key\":\"GetMusicSearchSuggestions_rid\",\"value\":\"0x0072d572126ab403\"}]},{\"service\":\"GFEEDBACK\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"},{\"key\":\"e\",\"value\":\"9405960,23804281,24004644,24077241,24181174,24241378,24290153,24439361,24548629,24566687,39328397,51010235,51020570,51025415,51050361,51053689,51063643,51065188,51089007,51098299,51101169,51111738,51115184,51124104,51152050,51157411,51176511,51183909,51204329,51217504,51222973,51224491,51225393,51227037,51228850,51237842,51242448,51248734,51255676,51256074,51256084,51274583,51276557,51276565,51281227,51285717,51287196,51292055,51296439,51298020,51299710,51299724,51303667,51303669,51303789,51305839,51310742,51313109,51313767,51313802,51318845,51322669,51326932,51333103,51335366,51335392,51340662,51341975,51342753,51342857,51349880,51349913,51351446,51353231,51353393,51354114,51355679,51357477,51361727,51362455,51362643,51362857,51362912,51365459,51365462,51366423,51366864,51367993,51370095,51371521,51372971,51374017,51374438,51375168,51375205,51375519,51375719,51376330,51376601,51379274,51380892,51381276,51381857,51383376,51384888,51385023,51385277,51386141,51386500,51386566,51387927,51388643,51388999,51389290,51389313,51389629,51389867,51391255,51391339,51395384,51395558,51396626,51398682,51399239,51399885,51400654,51400810,51404699,51406269\"}]},{\"service\":\"ECATCHER\",\"params\":[{\"key\":\"client.version\",\"value\":\"1.20000101\"},{\"key\":\"client.name\",\"value\":\"WEB_REMIX\"}]}]},\"trackingParams\":\"CAAQi24iEwiW7tfXx7yLAxXDgHwGHUDYJys\u003d\"}",
|
||||
"latestUrl": "https://music.youtube.com/youtubei/v1/music/get_search_suggestions?prettyPrint\u003dfalse"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,315 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "POST",
|
||||
"url": "https://music.youtube.com/youtubei/v1/music/get_search_suggestions?prettyPrint\u003dfalse",
|
||||
"headers": {
|
||||
"Origin": [
|
||||
"https://music.youtube.com"
|
||||
],
|
||||
"Referer": [
|
||||
"https://music.youtube.com"
|
||||
],
|
||||
"X-YouTube-Client-Version": [
|
||||
"2.20250122.04.00"
|
||||
],
|
||||
"X-YouTube-Client-Name": [
|
||||
"67"
|
||||
],
|
||||
"Content-Type": [
|
||||
"application/json"
|
||||
],
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"dataToSend": [
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
111,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
120,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
78,
|
||||
97,
|
||||
109,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
87,
|
||||
69,
|
||||
66,
|
||||
95,
|
||||
82,
|
||||
69,
|
||||
77,
|
||||
73,
|
||||
88,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
86,
|
||||
101,
|
||||
114,
|
||||
115,
|
||||
105,
|
||||
111,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
49,
|
||||
46,
|
||||
50,
|
||||
48,
|
||||
50,
|
||||
53,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
50,
|
||||
46,
|
||||
48,
|
||||
49,
|
||||
46,
|
||||
48,
|
||||
48,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
104,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
101,
|
||||
110,
|
||||
45,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
103,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
112,
|
||||
108,
|
||||
97,
|
||||
116,
|
||||
102,
|
||||
111,
|
||||
114,
|
||||
109,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
68,
|
||||
69,
|
||||
83,
|
||||
75,
|
||||
84,
|
||||
79,
|
||||
80,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
116,
|
||||
99,
|
||||
79,
|
||||
102,
|
||||
102,
|
||||
115,
|
||||
101,
|
||||
116,
|
||||
77,
|
||||
105,
|
||||
110,
|
||||
117,
|
||||
116,
|
||||
101,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
48,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
114,
|
||||
101,
|
||||
113,
|
||||
117,
|
||||
101,
|
||||
115,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
114,
|
||||
110,
|
||||
97,
|
||||
108,
|
||||
69,
|
||||
120,
|
||||
112,
|
||||
101,
|
||||
114,
|
||||
105,
|
||||
109,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
70,
|
||||
108,
|
||||
97,
|
||||
103,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
91,
|
||||
93,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
83,
|
||||
115,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
116,
|
||||
114,
|
||||
117,
|
||||
101,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
114,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
108,
|
||||
111,
|
||||
99,
|
||||
107,
|
||||
101,
|
||||
100,
|
||||
83,
|
||||
97,
|
||||
102,
|
||||
101,
|
||||
116,
|
||||
121,
|
||||
77,
|
||||
111,
|
||||
100,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
102,
|
||||
97,
|
||||
108,
|
||||
115,
|
||||
101,
|
||||
125,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
112,
|
||||
117,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
34,
|
||||
125
|
||||
],
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"content-type": [
|
||||
"application/json; charset\u003dUTF-8"
|
||||
],
|
||||
"date": [
|
||||
"Tue, 11 Feb 2025 21:26:47 GMT"
|
||||
],
|
||||
"server": [
|
||||
"scaffolding on HTTPServer2"
|
||||
],
|
||||
"vary": [
|
||||
"Origin",
|
||||
"X-Origin",
|
||||
"Referer"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "{\"responseContext\":{\"visitorData\":\"CgtKM21wZDJPQXdwdyiXga-9BjIKCgJERRIEEgAgJQ%3D%3D\",\"serviceTrackingParams\":[{\"service\":\"CSI\",\"params\":[{\"key\":\"c\",\"value\":\"WEB_REMIX\"},{\"key\":\"cver\",\"value\":\"1.20250122.01.00\"},{\"key\":\"yt_li\",\"value\":\"0\"},{\"key\":\"GetMusicSearchSuggestions_rid\",\"value\":\"0xd605de9624bc897e\"}]},{\"service\":\"GFEEDBACK\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"},{\"key\":\"e\",\"value\":\"23804281,24004644,24077241,24181174,24186126,24241378,24290153,24428948,24439361,24499533,24548629,24566687,39328397,51010235,51020570,51025415,51043774,51050361,51053689,51063643,51065188,51089007,51098299,51111738,51115184,51124104,51152050,51157411,51176421,51176511,51183910,51204329,51217504,51222973,51224489,51225393,51227037,51228850,51237842,51242448,51248734,51255676,51256074,51256084,51274583,51276557,51276565,51281227,51285717,51287196,51292055,51296439,51298020,51299710,51299724,51303667,51303669,51303789,51305839,51310742,51313109,51313767,51313802,51318845,51322669,51326932,51335366,51335392,51340662,51341975,51342753,51342857,51342877,51349880,51349914,51351446,51353231,51353393,51354114,51355679,51357475,51357477,51361727,51362455,51362643,51362857,51362911,51365459,51365462,51366423,51366864,51367993,51370095,51371521,51372971,51374016,51374438,51375168,51375205,51375518,51375646,51375719,51376330,51376601,51379274,51380892,51381276,51381857,51383376,51384888,51385023,51385277,51385397,51386141,51386361,51386500,51386541,51386565,51387927,51388642,51388999,51389313,51389629,51389867,51391338,51395383,51395558,51396626,51398682,51399239,51399861,51399885,51400654,51400717,51400810,51404699,51406269\"}]},{\"service\":\"ECATCHER\",\"params\":[{\"key\":\"client.version\",\"value\":\"1.20000101\"},{\"key\":\"client.name\",\"value\":\"WEB_REMIX\"}]}]},\"trackingParams\":\"CAAQi24iEwi9-c_bx7yLAxWqIAYAHXs5GWY\u003d\"}",
|
||||
"latestUrl": "https://music.youtube.com/youtubei/v1/music/get_search_suggestions?prettyPrint\u003dfalse"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,315 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "POST",
|
||||
"url": "https://music.youtube.com/youtubei/v1/music/get_search_suggestions?prettyPrint\u003dfalse",
|
||||
"headers": {
|
||||
"Referer": [
|
||||
"https://music.youtube.com"
|
||||
],
|
||||
"Origin": [
|
||||
"https://music.youtube.com"
|
||||
],
|
||||
"X-YouTube-Client-Version": [
|
||||
"2.20250122.04.00"
|
||||
],
|
||||
"X-YouTube-Client-Name": [
|
||||
"67"
|
||||
],
|
||||
"Content-Type": [
|
||||
"application/json"
|
||||
],
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"dataToSend": [
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
111,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
120,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
78,
|
||||
97,
|
||||
109,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
87,
|
||||
69,
|
||||
66,
|
||||
95,
|
||||
82,
|
||||
69,
|
||||
77,
|
||||
73,
|
||||
88,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
86,
|
||||
101,
|
||||
114,
|
||||
115,
|
||||
105,
|
||||
111,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
49,
|
||||
46,
|
||||
50,
|
||||
48,
|
||||
50,
|
||||
53,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
50,
|
||||
46,
|
||||
48,
|
||||
49,
|
||||
46,
|
||||
48,
|
||||
48,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
104,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
101,
|
||||
110,
|
||||
45,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
103,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
112,
|
||||
108,
|
||||
97,
|
||||
116,
|
||||
102,
|
||||
111,
|
||||
114,
|
||||
109,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
68,
|
||||
69,
|
||||
83,
|
||||
75,
|
||||
84,
|
||||
79,
|
||||
80,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
116,
|
||||
99,
|
||||
79,
|
||||
102,
|
||||
102,
|
||||
115,
|
||||
101,
|
||||
116,
|
||||
77,
|
||||
105,
|
||||
110,
|
||||
117,
|
||||
116,
|
||||
101,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
48,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
114,
|
||||
101,
|
||||
113,
|
||||
117,
|
||||
101,
|
||||
115,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
114,
|
||||
110,
|
||||
97,
|
||||
108,
|
||||
69,
|
||||
120,
|
||||
112,
|
||||
101,
|
||||
114,
|
||||
105,
|
||||
109,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
70,
|
||||
108,
|
||||
97,
|
||||
103,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
91,
|
||||
93,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
83,
|
||||
115,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
116,
|
||||
114,
|
||||
117,
|
||||
101,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
114,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
108,
|
||||
111,
|
||||
99,
|
||||
107,
|
||||
101,
|
||||
100,
|
||||
83,
|
||||
97,
|
||||
102,
|
||||
101,
|
||||
116,
|
||||
121,
|
||||
77,
|
||||
111,
|
||||
100,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
102,
|
||||
97,
|
||||
108,
|
||||
115,
|
||||
101,
|
||||
125,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
112,
|
||||
117,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
34,
|
||||
125
|
||||
],
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"content-type": [
|
||||
"application/json; charset\u003dUTF-8"
|
||||
],
|
||||
"date": [
|
||||
"Tue, 11 Feb 2025 21:26:57 GMT"
|
||||
],
|
||||
"server": [
|
||||
"scaffolding on HTTPServer2"
|
||||
],
|
||||
"vary": [
|
||||
"Origin",
|
||||
"X-Origin",
|
||||
"Referer"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "{\"responseContext\":{\"visitorData\":\"CgtoOGhCeHl0dHE0NCigga-9BjIKCgJERRIEEgAgJw%3D%3D\",\"serviceTrackingParams\":[{\"service\":\"CSI\",\"params\":[{\"key\":\"c\",\"value\":\"WEB_REMIX\"},{\"key\":\"cver\",\"value\":\"1.20250122.01.00\"},{\"key\":\"yt_li\",\"value\":\"0\"},{\"key\":\"GetMusicSearchSuggestions_rid\",\"value\":\"0xc2468fe509a23aa9\"}]},{\"service\":\"GFEEDBACK\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"},{\"key\":\"e\",\"value\":\"23804281,24004644,24077241,24181174,24186126,24241378,24290153,24439361,24548629,24566687,39328397,51010235,51020570,51025415,51037344,51037353,51050361,51053689,51063643,51065188,51089007,51098299,51101169,51111738,51115184,51124104,51152050,51157411,51176511,51178310,51178329,51178344,51178353,51183909,51204329,51217504,51222973,51225393,51227037,51227408,51228850,51237842,51242448,51248734,51255676,51256074,51256084,51257940,51260454,51274583,51276557,51276565,51281227,51285717,51287196,51288520,51292055,51296439,51298020,51299710,51299724,51303667,51303669,51303789,51305494,51305839,51311029,51311034,51313109,51313767,51313802,51316746,51318845,51322669,51323298,51326932,51327142,51327169,51327186,51329144,51331481,51331502,51331516,51331535,51331538,51331549,51331554,51331559,51333543,51335364,51335392,51335644,51335973,51340613,51340662,51341226,51341975,51342753,51342857,51349440,51349880,51349914,51351446,51353231,51353393,51354114,51354569,51355262,51355271,51355289,51355303,51355316,51355337,51355344,51355679,51355802,51357436,51357477,51360110,51360121,51360134,51361727,51361828,51362073,51362455,51362643,51362857,51362912,51363727,51363738,51363743,51363756,51363765,51363770,51364289,51365459,51365462,51366423,51366864,51367487,51367993,51369889,51370095,51370997,51371006,51371521,51372971,51374017,51374438,51375168,51375205,51375519,51375719,51376330,51376396,51376601,51379274,51380370,51380385,51380400,51380759,51380770,51380781,51380796,51380805,51380818,51380821,51380894,51381276,51381857,51383376,51384305,51384837,51384888,51385023,51385277,51385569,51386141,51386500,51386565,51386625,51387927,51388642,51388999,51389313,51389629,51389867,51390511,51391267,51391338,51391451,51393208,51394774,51394783,51395384,51395558,51395650,51395957,51396626,51397087,51398682,51398887,51399240,51399469,51399886,51400654,51400717,51400810,51401646,51402010,51402825,51403029,51404699,51406269\"}]},{\"service\":\"ECATCHER\",\"params\":[{\"key\":\"client.version\",\"value\":\"1.20000101\"},{\"key\":\"client.name\",\"value\":\"WEB_REMIX\"}]}]},\"trackingParams\":\"CAAQi24iEwjQ8aHgx7yLAxV1CQYAHewBO1E\u003d\"}",
|
||||
"latestUrl": "https://music.youtube.com/youtubei/v1/music/get_search_suggestions?prettyPrint\u003dfalse"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,315 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "POST",
|
||||
"url": "https://music.youtube.com/youtubei/v1/music/get_search_suggestions?prettyPrint\u003dfalse",
|
||||
"headers": {
|
||||
"Referer": [
|
||||
"https://music.youtube.com"
|
||||
],
|
||||
"Origin": [
|
||||
"https://music.youtube.com"
|
||||
],
|
||||
"X-YouTube-Client-Version": [
|
||||
"2.20250122.04.00"
|
||||
],
|
||||
"X-YouTube-Client-Name": [
|
||||
"67"
|
||||
],
|
||||
"Content-Type": [
|
||||
"application/json"
|
||||
],
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"dataToSend": [
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
111,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
120,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
78,
|
||||
97,
|
||||
109,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
87,
|
||||
69,
|
||||
66,
|
||||
95,
|
||||
82,
|
||||
69,
|
||||
77,
|
||||
73,
|
||||
88,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
86,
|
||||
101,
|
||||
114,
|
||||
115,
|
||||
105,
|
||||
111,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
49,
|
||||
46,
|
||||
50,
|
||||
48,
|
||||
50,
|
||||
53,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
50,
|
||||
46,
|
||||
48,
|
||||
49,
|
||||
46,
|
||||
48,
|
||||
48,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
104,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
101,
|
||||
110,
|
||||
45,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
103,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
112,
|
||||
108,
|
||||
97,
|
||||
116,
|
||||
102,
|
||||
111,
|
||||
114,
|
||||
109,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
68,
|
||||
69,
|
||||
83,
|
||||
75,
|
||||
84,
|
||||
79,
|
||||
80,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
116,
|
||||
99,
|
||||
79,
|
||||
102,
|
||||
102,
|
||||
115,
|
||||
101,
|
||||
116,
|
||||
77,
|
||||
105,
|
||||
110,
|
||||
117,
|
||||
116,
|
||||
101,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
48,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
114,
|
||||
101,
|
||||
113,
|
||||
117,
|
||||
101,
|
||||
115,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
114,
|
||||
110,
|
||||
97,
|
||||
108,
|
||||
69,
|
||||
120,
|
||||
112,
|
||||
101,
|
||||
114,
|
||||
105,
|
||||
109,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
70,
|
||||
108,
|
||||
97,
|
||||
103,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
91,
|
||||
93,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
83,
|
||||
115,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
116,
|
||||
114,
|
||||
117,
|
||||
101,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
114,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
108,
|
||||
111,
|
||||
99,
|
||||
107,
|
||||
101,
|
||||
100,
|
||||
83,
|
||||
97,
|
||||
102,
|
||||
101,
|
||||
116,
|
||||
121,
|
||||
77,
|
||||
111,
|
||||
100,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
102,
|
||||
97,
|
||||
108,
|
||||
115,
|
||||
101,
|
||||
125,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
112,
|
||||
117,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
34,
|
||||
125
|
||||
],
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"content-type": [
|
||||
"application/json; charset\u003dUTF-8"
|
||||
],
|
||||
"date": [
|
||||
"Tue, 11 Feb 2025 21:27:06 GMT"
|
||||
],
|
||||
"server": [
|
||||
"scaffolding on HTTPServer2"
|
||||
],
|
||||
"vary": [
|
||||
"Origin",
|
||||
"X-Origin",
|
||||
"Referer"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "{\"responseContext\":{\"visitorData\":\"CgtiRTZSbnJhNVFqYyiqga-9BjIKCgJERRIEEgAgSw%3D%3D\",\"serviceTrackingParams\":[{\"service\":\"CSI\",\"params\":[{\"key\":\"c\",\"value\":\"WEB_REMIX\"},{\"key\":\"cver\",\"value\":\"1.20250122.01.00\"},{\"key\":\"yt_li\",\"value\":\"0\"},{\"key\":\"GetMusicSearchSuggestions_rid\",\"value\":\"0xac22c047248d13a0\"}]},{\"service\":\"GFEEDBACK\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"},{\"key\":\"e\",\"value\":\"9405960,23804281,23813628,24004644,24077241,24181174,24186125,24241378,24290153,24439361,24548629,24566687,39328397,51010235,51020570,51025415,51037344,51037353,51050361,51053689,51063643,51065188,51089007,51098299,51101169,51111738,51115184,51124104,51152050,51153492,51157411,51176511,51178316,51178337,51178340,51178351,51183910,51204329,51217504,51222973,51225393,51227037,51228850,51237842,51242448,51248734,51255676,51256074,51256084,51260454,51274583,51276557,51276565,51281227,51285717,51287196,51292055,51296439,51298020,51299710,51299724,51303667,51303669,51303789,51304729,51305031,51305494,51305839,51308708,51310741,51311025,51311040,51313109,51313767,51313802,51318845,51322669,51326932,51327144,51327167,51327178,51331483,51331504,51331516,51331529,51331540,51331545,51331554,51331561,51335366,51335392,51335644,51339142,51340611,51340662,51341226,51341975,51342753,51342857,51349880,51349913,51351446,51353231,51353393,51354114,51354567,51355268,51355271,51355289,51355303,51355316,51355339,51355342,51355679,51357477,51359177,51360106,51360125,51360138,51361727,51361828,51361846,51362040,51362073,51362455,51362643,51362857,51362911,51363725,51363736,51363741,51363754,51363763,51363772,51364291,51365459,51365462,51366423,51366864,51367489,51367993,51369905,51370095,51371003,51371006,51371521,51372971,51374016,51374438,51375074,51375168,51375205,51375518,51375719,51376330,51376601,51379211,51379274,51380372,51380383,51380400,51380763,51380772,51380785,51380788,51380807,51380816,51380827,51380894,51381276,51381857,51383376,51383439,51384306,51384888,51385023,51386141,51386500,51386540,51386548,51386566,51387927,51388643,51388999,51389313,51389629,51389867,51390075,51390512,51391127,51391339,51393143,51394776,51394783,51395383,51395558,51395649,51396626,51397089,51397104,51397282,51398392,51398608,51398682,51399240,51399885,51400655,51400810,51402011,51403173,51404699,51406269\"}]},{\"service\":\"ECATCHER\",\"params\":[{\"key\":\"client.version\",\"value\":\"1.20000101\"},{\"key\":\"client.name\",\"value\":\"WEB_REMIX\"}]}]},\"trackingParams\":\"CAAQi24iEwi-t93kx7yLAxV9OQYAHSqgMoI\u003d\"}",
|
||||
"latestUrl": "https://music.youtube.com/youtubei/v1/music/get_search_suggestions?prettyPrint\u003dfalse"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,315 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "POST",
|
||||
"url": "https://music.youtube.com/youtubei/v1/music/get_search_suggestions?prettyPrint\u003dfalse",
|
||||
"headers": {
|
||||
"Referer": [
|
||||
"https://music.youtube.com"
|
||||
],
|
||||
"Origin": [
|
||||
"https://music.youtube.com"
|
||||
],
|
||||
"X-YouTube-Client-Version": [
|
||||
"2.20250122.04.00"
|
||||
],
|
||||
"X-YouTube-Client-Name": [
|
||||
"67"
|
||||
],
|
||||
"Content-Type": [
|
||||
"application/json"
|
||||
],
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"dataToSend": [
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
111,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
120,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
78,
|
||||
97,
|
||||
109,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
87,
|
||||
69,
|
||||
66,
|
||||
95,
|
||||
82,
|
||||
69,
|
||||
77,
|
||||
73,
|
||||
88,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
86,
|
||||
101,
|
||||
114,
|
||||
115,
|
||||
105,
|
||||
111,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
49,
|
||||
46,
|
||||
50,
|
||||
48,
|
||||
50,
|
||||
53,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
50,
|
||||
46,
|
||||
48,
|
||||
49,
|
||||
46,
|
||||
48,
|
||||
48,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
104,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
101,
|
||||
110,
|
||||
45,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
103,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
112,
|
||||
108,
|
||||
97,
|
||||
116,
|
||||
102,
|
||||
111,
|
||||
114,
|
||||
109,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
68,
|
||||
69,
|
||||
83,
|
||||
75,
|
||||
84,
|
||||
79,
|
||||
80,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
116,
|
||||
99,
|
||||
79,
|
||||
102,
|
||||
102,
|
||||
115,
|
||||
101,
|
||||
116,
|
||||
77,
|
||||
105,
|
||||
110,
|
||||
117,
|
||||
116,
|
||||
101,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
48,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
114,
|
||||
101,
|
||||
113,
|
||||
117,
|
||||
101,
|
||||
115,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
114,
|
||||
110,
|
||||
97,
|
||||
108,
|
||||
69,
|
||||
120,
|
||||
112,
|
||||
101,
|
||||
114,
|
||||
105,
|
||||
109,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
70,
|
||||
108,
|
||||
97,
|
||||
103,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
91,
|
||||
93,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
83,
|
||||
115,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
116,
|
||||
114,
|
||||
117,
|
||||
101,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
114,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
108,
|
||||
111,
|
||||
99,
|
||||
107,
|
||||
101,
|
||||
100,
|
||||
83,
|
||||
97,
|
||||
102,
|
||||
101,
|
||||
116,
|
||||
121,
|
||||
77,
|
||||
111,
|
||||
100,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
102,
|
||||
97,
|
||||
108,
|
||||
115,
|
||||
101,
|
||||
125,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
112,
|
||||
117,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
34,
|
||||
125
|
||||
],
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"content-type": [
|
||||
"application/json; charset\u003dUTF-8"
|
||||
],
|
||||
"date": [
|
||||
"Tue, 11 Feb 2025 21:27:14 GMT"
|
||||
],
|
||||
"server": [
|
||||
"scaffolding on HTTPServer2"
|
||||
],
|
||||
"vary": [
|
||||
"Origin",
|
||||
"X-Origin",
|
||||
"Referer"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "{\"responseContext\":{\"visitorData\":\"Cgtab3c3RGJCZnZ2TSiyga-9BjIKCgJERRIEEgAgRQ%3D%3D\",\"serviceTrackingParams\":[{\"service\":\"CSI\",\"params\":[{\"key\":\"c\",\"value\":\"WEB_REMIX\"},{\"key\":\"cver\",\"value\":\"1.20250122.01.00\"},{\"key\":\"yt_li\",\"value\":\"0\"},{\"key\":\"GetMusicSearchSuggestions_rid\",\"value\":\"0x6fa27aded42d1916\"}]},{\"service\":\"GFEEDBACK\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"},{\"key\":\"e\",\"value\":\"23804281,24004644,24077241,24181174,24241378,24290153,24439361,24548629,24566687,39328397,51010235,51020570,51025415,51050361,51053689,51063643,51065188,51089007,51098299,51111738,51115184,51124104,51134507,51152050,51157411,51176511,51183910,51204329,51217504,51222973,51224492,51225393,51227037,51228850,51237842,51242448,51248734,51255676,51256074,51256084,51274583,51276557,51276565,51281227,51285717,51287196,51292055,51296439,51298020,51299710,51299724,51303667,51303669,51303789,51305839,51310741,51313109,51313767,51313802,51318845,51322669,51326932,51334295,51335365,51335392,51340662,51341975,51342753,51342857,51349880,51349913,51351446,51353231,51353393,51354114,51355679,51357477,51361727,51362455,51362643,51362857,51362911,51365459,51365462,51366423,51366864,51367993,51370095,51371521,51372971,51374017,51374438,51375168,51375205,51375518,51375646,51375719,51376050,51376330,51376601,51379055,51379274,51380894,51381276,51381857,51383376,51384888,51385023,51385277,51385398,51386141,51386500,51386566,51387927,51388642,51388999,51389313,51389629,51389867,51391267,51391339,51395383,51395558,51396626,51398682,51399240,51399886,51400153,51400655,51400810,51403122,51404700,51405825,51406269\"}]},{\"service\":\"ECATCHER\",\"params\":[{\"key\":\"client.version\",\"value\":\"1.20000101\"},{\"key\":\"client.name\",\"value\":\"WEB_REMIX\"}]}]},\"trackingParams\":\"CAAQi24iEwja8NHox7yLAxXcBAYAHVSMAsw\u003d\"}",
|
||||
"latestUrl": "https://music.youtube.com/youtubei/v1/music/get_search_suggestions?prettyPrint\u003dfalse"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,315 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "POST",
|
||||
"url": "https://music.youtube.com/youtubei/v1/music/get_search_suggestions?prettyPrint\u003dfalse",
|
||||
"headers": {
|
||||
"Origin": [
|
||||
"https://music.youtube.com"
|
||||
],
|
||||
"Referer": [
|
||||
"https://music.youtube.com"
|
||||
],
|
||||
"X-YouTube-Client-Version": [
|
||||
"2.20250122.04.00"
|
||||
],
|
||||
"X-YouTube-Client-Name": [
|
||||
"67"
|
||||
],
|
||||
"Content-Type": [
|
||||
"application/json"
|
||||
],
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"dataToSend": [
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
111,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
120,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
78,
|
||||
97,
|
||||
109,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
87,
|
||||
69,
|
||||
66,
|
||||
95,
|
||||
82,
|
||||
69,
|
||||
77,
|
||||
73,
|
||||
88,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
86,
|
||||
101,
|
||||
114,
|
||||
115,
|
||||
105,
|
||||
111,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
49,
|
||||
46,
|
||||
50,
|
||||
48,
|
||||
50,
|
||||
53,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
50,
|
||||
46,
|
||||
48,
|
||||
49,
|
||||
46,
|
||||
48,
|
||||
48,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
104,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
101,
|
||||
110,
|
||||
45,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
103,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
112,
|
||||
108,
|
||||
97,
|
||||
116,
|
||||
102,
|
||||
111,
|
||||
114,
|
||||
109,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
68,
|
||||
69,
|
||||
83,
|
||||
75,
|
||||
84,
|
||||
79,
|
||||
80,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
116,
|
||||
99,
|
||||
79,
|
||||
102,
|
||||
102,
|
||||
115,
|
||||
101,
|
||||
116,
|
||||
77,
|
||||
105,
|
||||
110,
|
||||
117,
|
||||
116,
|
||||
101,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
48,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
114,
|
||||
101,
|
||||
113,
|
||||
117,
|
||||
101,
|
||||
115,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
114,
|
||||
110,
|
||||
97,
|
||||
108,
|
||||
69,
|
||||
120,
|
||||
112,
|
||||
101,
|
||||
114,
|
||||
105,
|
||||
109,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
70,
|
||||
108,
|
||||
97,
|
||||
103,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
91,
|
||||
93,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
83,
|
||||
115,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
116,
|
||||
114,
|
||||
117,
|
||||
101,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
114,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
108,
|
||||
111,
|
||||
99,
|
||||
107,
|
||||
101,
|
||||
100,
|
||||
83,
|
||||
97,
|
||||
102,
|
||||
101,
|
||||
116,
|
||||
121,
|
||||
77,
|
||||
111,
|
||||
100,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
102,
|
||||
97,
|
||||
108,
|
||||
115,
|
||||
101,
|
||||
125,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
112,
|
||||
117,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
34,
|
||||
125
|
||||
],
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"content-type": [
|
||||
"application/json; charset\u003dUTF-8"
|
||||
],
|
||||
"date": [
|
||||
"Tue, 11 Feb 2025 21:27:22 GMT"
|
||||
],
|
||||
"server": [
|
||||
"scaffolding on HTTPServer2"
|
||||
],
|
||||
"vary": [
|
||||
"Origin",
|
||||
"X-Origin",
|
||||
"Referer"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "{\"responseContext\":{\"visitorData\":\"CgtkNHo3aXhMd3dENCi6ga-9BjIKCgJERRIEEgAgWw%3D%3D\",\"serviceTrackingParams\":[{\"service\":\"CSI\",\"params\":[{\"key\":\"c\",\"value\":\"WEB_REMIX\"},{\"key\":\"cver\",\"value\":\"1.20250122.01.00\"},{\"key\":\"yt_li\",\"value\":\"0\"},{\"key\":\"GetMusicSearchSuggestions_rid\",\"value\":\"0xa304b3b374db0b76\"}]},{\"service\":\"GFEEDBACK\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"},{\"key\":\"e\",\"value\":\"23804281,24004644,24077241,24181174,24241378,24290153,24407444,24439361,24459435,24548629,24566687,39328397,51010235,51020570,51025415,51037344,51037349,51050361,51053689,51063643,51065188,51089007,51098299,51111738,51115184,51124104,51146014,51152050,51157411,51157841,51176511,51178320,51178333,51178348,51178351,51183909,51203015,51204329,51217504,51222973,51225393,51227037,51228850,51237842,51242448,51248734,51255676,51256074,51256084,51260454,51274583,51276557,51276565,51277311,51281227,51285717,51287196,51292055,51296439,51298020,51299710,51299724,51303667,51303669,51303789,51305839,51308709,51310742,51311025,51311038,51313109,51313767,51313802,51316749,51316844,51318845,51322669,51326932,51327142,51327169,51327182,51331485,51331504,51331520,51331533,51331542,51331549,51331552,51331561,51333541,51335364,51335392,51335644,51337456,51340611,51340662,51341228,51341975,51342753,51342857,51349913,51351446,51353231,51353393,51354114,51354567,51355264,51355277,51355289,51355305,51355312,51355335,51355346,51355679,51357477,51360095,51360102,51360119,51360134,51361727,51361828,51362073,51362455,51362643,51362857,51362911,51363727,51363732,51363741,51363754,51363765,51363772,51364291,51365459,51365462,51366423,51366864,51367487,51367993,51369398,51370095,51370997,51371010,51371521,51372971,51374017,51374438,51375076,51375168,51375205,51375518,51375719,51376330,51376601,51379054,51379211,51379274,51380372,51380389,51380396,51380745,51380755,51380770,51380785,51380788,51380805,51380814,51380827,51380894,51381276,51381857,51383376,51384306,51384888,51385023,51385278,51386141,51386361,51386500,51386566,51387195,51387927,51388250,51388642,51388999,51389313,51389583,51389629,51389867,51390948,51391088,51391339,51393068,51394772,51394779,51395384,51395551,51395558,51395564,51395650,51395904,51395957,51396626,51397102,51397282,51398391,51398682,51399239,51399885,51400654,51400810,51401648,51402011,51403173,51404410,51404699,51405825,51406269\"}]},{\"service\":\"ECATCHER\",\"params\":[{\"key\":\"client.version\",\"value\":\"1.20000101\"},{\"key\":\"client.name\",\"value\":\"WEB_REMIX\"}]}]},\"trackingParams\":\"CAAQi24iEwicpKrsx7yLAxU5ekECHVanKoc\u003d\"}",
|
||||
"latestUrl": "https://music.youtube.com/youtubei/v1/music/get_search_suggestions?prettyPrint\u003dfalse"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,315 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "POST",
|
||||
"url": "https://music.youtube.com/youtubei/v1/music/get_search_suggestions?prettyPrint\u003dfalse",
|
||||
"headers": {
|
||||
"Origin": [
|
||||
"https://music.youtube.com"
|
||||
],
|
||||
"Referer": [
|
||||
"https://music.youtube.com"
|
||||
],
|
||||
"X-YouTube-Client-Version": [
|
||||
"2.20250122.04.00"
|
||||
],
|
||||
"X-YouTube-Client-Name": [
|
||||
"67"
|
||||
],
|
||||
"Content-Type": [
|
||||
"application/json"
|
||||
],
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"dataToSend": [
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
111,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
120,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
78,
|
||||
97,
|
||||
109,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
87,
|
||||
69,
|
||||
66,
|
||||
95,
|
||||
82,
|
||||
69,
|
||||
77,
|
||||
73,
|
||||
88,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
86,
|
||||
101,
|
||||
114,
|
||||
115,
|
||||
105,
|
||||
111,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
49,
|
||||
46,
|
||||
50,
|
||||
48,
|
||||
50,
|
||||
53,
|
||||
48,
|
||||
49,
|
||||
50,
|
||||
50,
|
||||
46,
|
||||
48,
|
||||
49,
|
||||
46,
|
||||
48,
|
||||
48,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
104,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
101,
|
||||
110,
|
||||
45,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
103,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
112,
|
||||
108,
|
||||
97,
|
||||
116,
|
||||
102,
|
||||
111,
|
||||
114,
|
||||
109,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
68,
|
||||
69,
|
||||
83,
|
||||
75,
|
||||
84,
|
||||
79,
|
||||
80,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
116,
|
||||
99,
|
||||
79,
|
||||
102,
|
||||
102,
|
||||
115,
|
||||
101,
|
||||
116,
|
||||
77,
|
||||
105,
|
||||
110,
|
||||
117,
|
||||
116,
|
||||
101,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
48,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
114,
|
||||
101,
|
||||
113,
|
||||
117,
|
||||
101,
|
||||
115,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
114,
|
||||
110,
|
||||
97,
|
||||
108,
|
||||
69,
|
||||
120,
|
||||
112,
|
||||
101,
|
||||
114,
|
||||
105,
|
||||
109,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
70,
|
||||
108,
|
||||
97,
|
||||
103,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
91,
|
||||
93,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
83,
|
||||
115,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
116,
|
||||
114,
|
||||
117,
|
||||
101,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
114,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
108,
|
||||
111,
|
||||
99,
|
||||
107,
|
||||
101,
|
||||
100,
|
||||
83,
|
||||
97,
|
||||
102,
|
||||
101,
|
||||
116,
|
||||
121,
|
||||
77,
|
||||
111,
|
||||
100,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
102,
|
||||
97,
|
||||
108,
|
||||
115,
|
||||
101,
|
||||
125,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
112,
|
||||
117,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
34,
|
||||
125
|
||||
],
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"content-type": [
|
||||
"application/json; charset\u003dUTF-8"
|
||||
],
|
||||
"date": [
|
||||
"Tue, 11 Feb 2025 21:27:30 GMT"
|
||||
],
|
||||
"server": [
|
||||
"scaffolding on HTTPServer2"
|
||||
],
|
||||
"vary": [
|
||||
"Origin",
|
||||
"X-Origin",
|
||||
"Referer"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "{\"responseContext\":{\"visitorData\":\"Cgt5RzRaNjZHWVVkNCjCga-9BjIKCgJERRIEEgAgYw%3D%3D\",\"serviceTrackingParams\":[{\"service\":\"CSI\",\"params\":[{\"key\":\"c\",\"value\":\"WEB_REMIX\"},{\"key\":\"cver\",\"value\":\"1.20250122.01.00\"},{\"key\":\"yt_li\",\"value\":\"0\"},{\"key\":\"GetMusicSearchSuggestions_rid\",\"value\":\"0xd8e0611239b12c0b\"}]},{\"service\":\"GFEEDBACK\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"},{\"key\":\"e\",\"value\":\"23804281,24004644,24077241,24181174,24241378,24290153,24439361,24548629,24566687,39328397,51010235,51020570,51025415,51050361,51053689,51063643,51065188,51089007,51098299,51111738,51115184,51124104,51152050,51157411,51176511,51183910,51204329,51217504,51222973,51224492,51225393,51227037,51228850,51237842,51242448,51248734,51255676,51256074,51256084,51274583,51276557,51276565,51281227,51285717,51287196,51292055,51296439,51298020,51299710,51299724,51303667,51303669,51303789,51305839,51308708,51310742,51313109,51313767,51313802,51318845,51322669,51326932,51335364,51335392,51340662,51341975,51342753,51342857,51349880,51349914,51351446,51353231,51353393,51354114,51355679,51357474,51357477,51361727,51362455,51362643,51362857,51362912,51365459,51365462,51366423,51366864,51367993,51370095,51371521,51372971,51374016,51374438,51375168,51375205,51375519,51375719,51376330,51376601,51379054,51379274,51380894,51381276,51381857,51383376,51384888,51385023,51385398,51386141,51386500,51386566,51387927,51388642,51388999,51389313,51389629,51389867,51391256,51391339,51395383,51395558,51395903,51396626,51398391,51398682,51399240,51399886,51400152,51400655,51400716,51400765,51400810,51403122,51403173,51404699,51406269\"}]},{\"service\":\"ECATCHER\",\"params\":[{\"key\":\"client.version\",\"value\":\"1.20000101\"},{\"key\":\"client.name\",\"value\":\"WEB_REMIX\"}]}]},\"trackingParams\":\"CAAQi24iEwity4Xwx7yLAxVIQkECHU69JCw\u003d\"}",
|
||||
"latestUrl": "https://music.youtube.com/youtubei/v1/music/get_search_suggestions?prettyPrint\u003dfalse"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,80 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "GET",
|
||||
"url": "https://www.youtube.com/iframe_api",
|
||||
"headers": {
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"cache-control": [
|
||||
"private, max-age\u003d0"
|
||||
],
|
||||
"content-security-policy": [
|
||||
"require-trusted-types-for \u0027script\u0027"
|
||||
],
|
||||
"content-type": [
|
||||
"text/javascript; charset\u003dutf-8"
|
||||
],
|
||||
"cross-origin-opener-policy-report-only": [
|
||||
"same-origin; report-to\u003d\"youtube_main\""
|
||||
],
|
||||
"cross-origin-resource-policy": [
|
||||
"cross-origin"
|
||||
],
|
||||
"date": [
|
||||
"Tue, 11 Feb 2025 21:20:44 GMT"
|
||||
],
|
||||
"expires": [
|
||||
"Tue, 11 Feb 2025 21:20:44 GMT"
|
||||
],
|
||||
"origin-trial": [
|
||||
"AmhMBR6zCLzDDxpW+HfpP67BqwIknWnyMOXOQGfzYswFmJe+fgaI6XZgAzcxOrzNtP7hEDsOo1jdjFnVr2IdxQ4AAAB4eyJvcmlnaW4iOiJodHRwczovL3lvdXR1YmUuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJWaWV3WFJlcXVlc3RlZFdpdGhEZXByZWNhdGlvbiIsImV4cGlyeSI6MTc1ODA2NzE5OSwiaXNTdWJkb21haW4iOnRydWV9"
|
||||
],
|
||||
"p3p": [
|
||||
"CP\u003d\"This is not a P3P policy! See http://support.google.com/accounts/answer/151657?hl\u003den-GB for more info.\""
|
||||
],
|
||||
"permissions-policy": [
|
||||
"ch-ua-arch\u003d*, ch-ua-bitness\u003d*, ch-ua-full-version\u003d*, ch-ua-full-version-list\u003d*, ch-ua-model\u003d*, ch-ua-wow64\u003d*, ch-ua-form-factors\u003d*, ch-ua-platform\u003d*, ch-ua-platform-version\u003d*"
|
||||
],
|
||||
"report-to": [
|
||||
"{\"group\":\"youtube_main\",\"max_age\":2592000,\"endpoints\":[{\"url\":\"https://csp.withgoogle.com/csp/report-to/youtube_main\"}]}"
|
||||
],
|
||||
"server": [
|
||||
"ESF"
|
||||
],
|
||||
"set-cookie": [
|
||||
"YSC\u003d-HZn8W-65YI; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"__Secure-ROLLOUT_TOKEN\u003dCNz89uH5v5DP1AEQ5bPQrsa8iwMY5bPQrsa8iwM%3D; Domain\u003dyoutube.com; Expires\u003dSun, 10-Aug-2025 21:20:44 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone; Partitioned",
|
||||
"VISITOR_INFO1_LIVE\u003drMJck_iy7Ks; Domain\u003d.youtube.com; Expires\u003dSun, 10-Aug-2025 21:20:44 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_PRIVACY_METADATA\u003dCgJERRIEEgAgEA%3D%3D; Domain\u003d.youtube.com; Expires\u003dSun, 10-Aug-2025 21:20:44 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone"
|
||||
],
|
||||
"strict-transport-security": [
|
||||
"max-age\u003d31536000"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "var scriptUrl \u003d \u0027https:\\/\\/www.youtube.com\\/s\\/player\\/af7f576f\\/www-widgetapi.vflset\\/www-widgetapi.js\u0027;try{var ttPolicy\u003dwindow.trustedTypes.createPolicy(\"youtube-widget-api\",{createScriptURL:function(x){return x}});scriptUrl\u003dttPolicy.createScriptURL(scriptUrl)}catch(e){}var YT;if(!window[\"YT\"])YT\u003d{loading:0,loaded:0};var YTConfig;if(!window[\"YTConfig\"])YTConfig\u003d{\"host\":\"https://www.youtube.com\"};\nif(!YT.loading){YT.loading\u003d1;(function(){var l\u003d[];YT.ready\u003dfunction(f){if(YT.loaded)f();else l.push(f)};window.onYTReady\u003dfunction(){YT.loaded\u003d1;var i\u003d0;for(;i\u003cl.length;i++)try{l[i]()}catch(e){}};YT.setConfig\u003dfunction(c){var k;for(k in c)if(c.hasOwnProperty(k))YTConfig[k]\u003dc[k]};var a\u003ddocument.createElement(\"script\");a.type\u003d\"text/javascript\";a.id\u003d\"www-widgetapi-script\";a.src\u003dscriptUrl;a.async\u003dtrue;var c\u003ddocument.currentScript;if(c){var n\u003dc.nonce||c.getAttribute(\"nonce\");if(n)a.setAttribute(\"nonce\",\nn)}var b\u003ddocument.getElementsByTagName(\"script\")[0];b.parentNode.insertBefore(a,b)})()};\n",
|
||||
"latestUrl": "https://www.youtube.com/iframe_api"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,80 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "GET",
|
||||
"url": "https://www.youtube.com/iframe_api",
|
||||
"headers": {
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"cache-control": [
|
||||
"private, max-age\u003d0"
|
||||
],
|
||||
"content-security-policy": [
|
||||
"require-trusted-types-for \u0027script\u0027"
|
||||
],
|
||||
"content-type": [
|
||||
"text/javascript; charset\u003dutf-8"
|
||||
],
|
||||
"cross-origin-opener-policy-report-only": [
|
||||
"same-origin; report-to\u003d\"youtube_main\""
|
||||
],
|
||||
"cross-origin-resource-policy": [
|
||||
"cross-origin"
|
||||
],
|
||||
"date": [
|
||||
"Tue, 11 Feb 2025 21:26:31 GMT"
|
||||
],
|
||||
"expires": [
|
||||
"Tue, 11 Feb 2025 21:26:31 GMT"
|
||||
],
|
||||
"origin-trial": [
|
||||
"AmhMBR6zCLzDDxpW+HfpP67BqwIknWnyMOXOQGfzYswFmJe+fgaI6XZgAzcxOrzNtP7hEDsOo1jdjFnVr2IdxQ4AAAB4eyJvcmlnaW4iOiJodHRwczovL3lvdXR1YmUuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJWaWV3WFJlcXVlc3RlZFdpdGhEZXByZWNhdGlvbiIsImV4cGlyeSI6MTc1ODA2NzE5OSwiaXNTdWJkb21haW4iOnRydWV9"
|
||||
],
|
||||
"p3p": [
|
||||
"CP\u003d\"This is not a P3P policy! See http://support.google.com/accounts/answer/151657?hl\u003den-GB for more info.\""
|
||||
],
|
||||
"permissions-policy": [
|
||||
"ch-ua-arch\u003d*, ch-ua-bitness\u003d*, ch-ua-full-version\u003d*, ch-ua-full-version-list\u003d*, ch-ua-model\u003d*, ch-ua-wow64\u003d*, ch-ua-form-factors\u003d*, ch-ua-platform\u003d*, ch-ua-platform-version\u003d*"
|
||||
],
|
||||
"report-to": [
|
||||
"{\"group\":\"youtube_main\",\"max_age\":2592000,\"endpoints\":[{\"url\":\"https://csp.withgoogle.com/csp/report-to/youtube_main\"}]}"
|
||||
],
|
||||
"server": [
|
||||
"ESF"
|
||||
],
|
||||
"set-cookie": [
|
||||
"YSC\u003d_RnQFljRZ7g; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003dFTq2gAjABPk; Domain\u003d.youtube.com; Expires\u003dSun, 10-Aug-2025 21:26:31 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_PRIVACY_METADATA\u003dCgJERRIEEgAgMg%3D%3D; Domain\u003d.youtube.com; Expires\u003dSun, 10-Aug-2025 21:26:31 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"__Secure-ROLLOUT_TOKEN\u003dCKf_-fLDg_2hpwEQz7H508e8iwMYz7H508e8iwM%3D; Domain\u003dyoutube.com; Expires\u003dSun, 10-Aug-2025 21:26:31 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone; Partitioned"
|
||||
],
|
||||
"strict-transport-security": [
|
||||
"max-age\u003d31536000"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "var scriptUrl \u003d \u0027https:\\/\\/www.youtube.com\\/s\\/player\\/af7f576f\\/www-widgetapi.vflset\\/www-widgetapi.js\u0027;try{var ttPolicy\u003dwindow.trustedTypes.createPolicy(\"youtube-widget-api\",{createScriptURL:function(x){return x}});scriptUrl\u003dttPolicy.createScriptURL(scriptUrl)}catch(e){}var YT;if(!window[\"YT\"])YT\u003d{loading:0,loaded:0};var YTConfig;if(!window[\"YTConfig\"])YTConfig\u003d{\"host\":\"https://www.youtube.com\"};\nif(!YT.loading){YT.loading\u003d1;(function(){var l\u003d[];YT.ready\u003dfunction(f){if(YT.loaded)f();else l.push(f)};window.onYTReady\u003dfunction(){YT.loaded\u003d1;var i\u003d0;for(;i\u003cl.length;i++)try{l[i]()}catch(e){}};YT.setConfig\u003dfunction(c){var k;for(k in c)if(c.hasOwnProperty(k))YTConfig[k]\u003dc[k]};var a\u003ddocument.createElement(\"script\");a.type\u003d\"text/javascript\";a.id\u003d\"www-widgetapi-script\";a.src\u003dscriptUrl;a.async\u003dtrue;var c\u003ddocument.currentScript;if(c){var n\u003dc.nonce||c.getAttribute(\"nonce\");if(n)a.setAttribute(\"nonce\",\nn)}var b\u003ddocument.getElementsByTagName(\"script\")[0];b.parentNode.insertBefore(a,b)})()};\n",
|
||||
"latestUrl": "https://www.youtube.com/iframe_api"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -35,7 +35,7 @@
|
||||
"private, max-age\u003d0"
|
||||
],
|
||||
"content-security-policy": [
|
||||
"require-trusted-types-for \u0027script\u0027"
|
||||
"require-trusted-types-for \u0027script\u0027;report-uri /cspreport"
|
||||
],
|
||||
"content-type": [
|
||||
"text/javascript; charset\u003dutf-8"
|
||||
@ -44,10 +44,10 @@
|
||||
"same-origin; report-to\u003d\"youtube_main\""
|
||||
],
|
||||
"date": [
|
||||
"Sun, 10 Nov 2024 17:57:16 GMT"
|
||||
"Tue, 11 Feb 2025 21:27:51 GMT"
|
||||
],
|
||||
"expires": [
|
||||
"Sun, 10 Nov 2024 17:57:16 GMT"
|
||||
"Tue, 11 Feb 2025 21:27:51 GMT"
|
||||
],
|
||||
"origin-trial": [
|
||||
"AmhMBR6zCLzDDxpW+HfpP67BqwIknWnyMOXOQGfzYswFmJe+fgaI6XZgAzcxOrzNtP7hEDsOo1jdjFnVr2IdxQ4AAAB4eyJvcmlnaW4iOiJodHRwczovL3lvdXR1YmUuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJWaWV3WFJlcXVlc3RlZFdpdGhEZXByZWNhdGlvbiIsImV4cGlyeSI6MTc1ODA2NzE5OSwiaXNTdWJkb21haW4iOnRydWV9"
|
||||
@ -65,8 +65,8 @@
|
||||
"ESF"
|
||||
],
|
||||
"set-cookie": [
|
||||
"YSC\u003dB6Ez5rWmaxs; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003d; Domain\u003d.youtube.com; Expires\u003dMon, 14-Feb-2022 17:57:16 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone"
|
||||
"YSC\u003dYHvWgExoeiY; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003d; Domain\u003d.youtube.com; Expires\u003dWed, 18-May-2022 21:27:51 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone"
|
||||
],
|
||||
"strict-transport-security": [
|
||||
"max-age\u003d31536000"
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -0,0 +1,369 @@
|
||||
{
|
||||
"request": {
|
||||
"httpMethod": "POST",
|
||||
"url": "https://youtubei.googleapis.com/youtubei/v1/visitor_id?prettyPrint\u003dfalse",
|
||||
"headers": {
|
||||
"User-Agent": [
|
||||
"com.google.android.youtube/19.28.35 (Linux; U; Android 15; GB) gzip"
|
||||
],
|
||||
"X-Goog-Api-Format-Version": [
|
||||
"2"
|
||||
],
|
||||
"Content-Type": [
|
||||
"application/json"
|
||||
],
|
||||
"Accept-Language": [
|
||||
"en-GB, en;q\u003d0.9"
|
||||
]
|
||||
},
|
||||
"dataToSend": [
|
||||
123,
|
||||
34,
|
||||
99,
|
||||
111,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
120,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
114,
|
||||
101,
|
||||
113,
|
||||
117,
|
||||
101,
|
||||
115,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
105,
|
||||
110,
|
||||
116,
|
||||
101,
|
||||
114,
|
||||
110,
|
||||
97,
|
||||
108,
|
||||
69,
|
||||
120,
|
||||
112,
|
||||
101,
|
||||
114,
|
||||
105,
|
||||
109,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
70,
|
||||
108,
|
||||
97,
|
||||
103,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
91,
|
||||
93,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
83,
|
||||
115,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
116,
|
||||
114,
|
||||
117,
|
||||
101,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
97,
|
||||
110,
|
||||
100,
|
||||
114,
|
||||
111,
|
||||
105,
|
||||
100,
|
||||
83,
|
||||
100,
|
||||
107,
|
||||
86,
|
||||
101,
|
||||
114,
|
||||
115,
|
||||
105,
|
||||
111,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
51,
|
||||
53,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
116,
|
||||
99,
|
||||
79,
|
||||
102,
|
||||
102,
|
||||
115,
|
||||
101,
|
||||
116,
|
||||
77,
|
||||
105,
|
||||
110,
|
||||
117,
|
||||
116,
|
||||
101,
|
||||
115,
|
||||
34,
|
||||
58,
|
||||
48,
|
||||
44,
|
||||
34,
|
||||
111,
|
||||
115,
|
||||
86,
|
||||
101,
|
||||
114,
|
||||
115,
|
||||
105,
|
||||
111,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
49,
|
||||
53,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
104,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
101,
|
||||
110,
|
||||
45,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
78,
|
||||
97,
|
||||
109,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
65,
|
||||
78,
|
||||
68,
|
||||
82,
|
||||
79,
|
||||
73,
|
||||
68,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
103,
|
||||
108,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
71,
|
||||
66,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
83,
|
||||
99,
|
||||
114,
|
||||
101,
|
||||
101,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
87,
|
||||
65,
|
||||
84,
|
||||
67,
|
||||
72,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
99,
|
||||
108,
|
||||
105,
|
||||
101,
|
||||
110,
|
||||
116,
|
||||
86,
|
||||
101,
|
||||
114,
|
||||
115,
|
||||
105,
|
||||
111,
|
||||
110,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
49,
|
||||
57,
|
||||
46,
|
||||
50,
|
||||
56,
|
||||
46,
|
||||
51,
|
||||
53,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
111,
|
||||
115,
|
||||
78,
|
||||
97,
|
||||
109,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
65,
|
||||
110,
|
||||
100,
|
||||
114,
|
||||
111,
|
||||
105,
|
||||
100,
|
||||
34,
|
||||
44,
|
||||
34,
|
||||
112,
|
||||
108,
|
||||
97,
|
||||
116,
|
||||
102,
|
||||
111,
|
||||
114,
|
||||
109,
|
||||
34,
|
||||
58,
|
||||
34,
|
||||
77,
|
||||
79,
|
||||
66,
|
||||
73,
|
||||
76,
|
||||
69,
|
||||
34,
|
||||
125,
|
||||
44,
|
||||
34,
|
||||
117,
|
||||
115,
|
||||
101,
|
||||
114,
|
||||
34,
|
||||
58,
|
||||
123,
|
||||
34,
|
||||
108,
|
||||
111,
|
||||
99,
|
||||
107,
|
||||
101,
|
||||
100,
|
||||
83,
|
||||
97,
|
||||
102,
|
||||
101,
|
||||
116,
|
||||
121,
|
||||
77,
|
||||
111,
|
||||
100,
|
||||
101,
|
||||
34,
|
||||
58,
|
||||
102,
|
||||
97,
|
||||
108,
|
||||
115,
|
||||
101,
|
||||
125,
|
||||
125,
|
||||
125
|
||||
],
|
||||
"localization": {
|
||||
"languageCode": "en",
|
||||
"countryCode": "GB"
|
||||
}
|
||||
},
|
||||
"response": {
|
||||
"responseCode": 200,
|
||||
"responseMessage": "",
|
||||
"responseHeaders": {
|
||||
"alt-svc": [
|
||||
"h3\u003d\":443\"; ma\u003d2592000,h3-29\u003d\":443\"; ma\u003d2592000"
|
||||
],
|
||||
"content-type": [
|
||||
"application/json; charset\u003dUTF-8"
|
||||
],
|
||||
"date": [
|
||||
"Tue, 11 Feb 2025 21:27:56 GMT"
|
||||
],
|
||||
"server": [
|
||||
"scaffolding on HTTPServer2"
|
||||
],
|
||||
"vary": [
|
||||
"Origin",
|
||||
"X-Origin",
|
||||
"Referer"
|
||||
],
|
||||
"x-content-type-options": [
|
||||
"nosniff"
|
||||
],
|
||||
"x-frame-options": [
|
||||
"SAMEORIGIN"
|
||||
],
|
||||
"x-xss-protection": [
|
||||
"0"
|
||||
]
|
||||
},
|
||||
"responseBody": "{\"responseContext\":{\"visitorData\":\"CgtqdG01NHlwS3c0TSjcga-9BjIKCgJERRIEEgAgWToMCAEgwazilcKb8NVn\",\"serviceTrackingParams\":[{\"service\":\"CSI\",\"params\":[{\"key\":\"c\",\"value\":\"ANDROID\"},{\"key\":\"cver\",\"value\":\"19.28.35\"},{\"key\":\"yt_li\",\"value\":\"0\"},{\"key\":\"GetVisitorId_rid\",\"value\":\"0x3a567781fcbf0a17\"}]},{\"service\":\"GFEEDBACK\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"},{\"key\":\"visitor_data\",\"value\":\"CgtqdG01NHlwS3c0TSjcga-9BjIKCgJERRIEEgAgWToMCAEgwazilcKb8NVn\"}]},{\"service\":\"GUIDED_HELP\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"}]},{\"service\":\"ECATCHER\",\"params\":[{\"key\":\"client.version\",\"value\":\"19.28\"},{\"key\":\"client.name\",\"value\":\"ANDROID\"}]}]}}",
|
||||
"latestUrl": "https://youtubei.googleapis.com/youtubei/v1/visitor_id?prettyPrint\u003dfalse"
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
@ -44,10 +44,10 @@
|
||||
"same-origin; report-to\u003d\"youtube_main\""
|
||||
],
|
||||
"date": [
|
||||
"Sun, 10 Nov 2024 17:57:17 GMT"
|
||||
"Tue, 11 Feb 2025 21:28:02 GMT"
|
||||
],
|
||||
"expires": [
|
||||
"Sun, 10 Nov 2024 17:57:17 GMT"
|
||||
"Tue, 11 Feb 2025 21:28:02 GMT"
|
||||
],
|
||||
"origin-trial": [
|
||||
"AmhMBR6zCLzDDxpW+HfpP67BqwIknWnyMOXOQGfzYswFmJe+fgaI6XZgAzcxOrzNtP7hEDsOo1jdjFnVr2IdxQ4AAAB4eyJvcmlnaW4iOiJodHRwczovL3lvdXR1YmUuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJWaWV3WFJlcXVlc3RlZFdpdGhEZXByZWNhdGlvbiIsImV4cGlyeSI6MTc1ODA2NzE5OSwiaXNTdWJkb21haW4iOnRydWV9"
|
||||
@ -65,8 +65,8 @@
|
||||
"ESF"
|
||||
],
|
||||
"set-cookie": [
|
||||
"YSC\u003dxeWWSmImBKY; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003d; Domain\u003d.youtube.com; Expires\u003dMon, 14-Feb-2022 17:57:17 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone"
|
||||
"YSC\u003dPp3KL5JdAzE; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003d; Domain\u003d.youtube.com; Expires\u003dWed, 18-May-2022 21:28:02 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone"
|
||||
],
|
||||
"strict-transport-security": [
|
||||
"max-age\u003d31536000"
|
||||
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user