Merge 2fb7a35194
into eac850ca10
This commit is contained in:
commit
2de97c8718
|
@ -8,6 +8,7 @@ import org.schabi.newpipe.extractor.localization.ContentCountry;
|
|||
import org.schabi.newpipe.extractor.localization.Localization;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Optional;
|
||||
|
@ -320,4 +321,46 @@ public final class YoutubeChannelHelper {
|
|||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a {@code channelAgeGateRenderer} object from a channel response if it exists.
|
||||
*
|
||||
* <p>
|
||||
* A {@code channelAgeGateRenderer} is returned when a channel is age-restricted (creator seems
|
||||
* to be able to set this setting), its pages are only accessible to logged-in and age-verified
|
||||
* users. This renderer contains only the following channel metadata: name and avatar.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* This restriction doesn't seem to apply to all countries.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* At most one {@code channelAgeGateRenderer} should be returned per age-restricted channel
|
||||
* response.
|
||||
* </p>
|
||||
*
|
||||
* @param jsonResponse a channel JSON response
|
||||
* @return the first {@code channelAgeGateRenderer} if there is one present or {@code null}
|
||||
*/
|
||||
@Nullable
|
||||
public static JsonObject getChannelAgeGateRenderer(@Nonnull final JsonObject jsonResponse) {
|
||||
return jsonResponse.getObject("contents")
|
||||
.getObject("twoColumnBrowseResultsRenderer")
|
||||
.getArray("tabs")
|
||||
.stream()
|
||||
.filter(JsonObject.class::isInstance)
|
||||
.map(JsonObject.class::cast)
|
||||
.flatMap(tab -> tab.getObject("tabRenderer")
|
||||
.getObject("content")
|
||||
.getObject("sectionListRenderer")
|
||||
.getArray("contents")
|
||||
.stream()
|
||||
.filter(JsonObject.class::isInstance)
|
||||
.map(JsonObject.class::cast))
|
||||
.filter(content -> content.has("channelAgeGateRenderer"))
|
||||
.map(content -> content.getObject("channelAgeGateRenderer"))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -66,15 +66,6 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
|
|||
|
||||
private String channelId;
|
||||
|
||||
/**
|
||||
* If a channel is age-restricted, its pages are only accessible to logged-in and
|
||||
* age-verified users, we get an {@code channelAgeGateRenderer} in this case, containing only
|
||||
* the following metadata: channel name and channel avatar.
|
||||
*
|
||||
* <p>
|
||||
* This restriction doesn't seem to apply to all countries.
|
||||
* </p>
|
||||
*/
|
||||
@Nullable
|
||||
private JsonObject channelAgeGateRenderer;
|
||||
|
||||
|
@ -95,28 +86,7 @@ public class YoutubeChannelExtractor extends ChannelExtractor {
|
|||
jsonResponse = data.jsonResponse;
|
||||
channelHeader = YoutubeChannelHelper.getChannelHeader(jsonResponse);
|
||||
channelId = data.channelId;
|
||||
channelAgeGateRenderer = getChannelAgeGateRenderer();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JsonObject getChannelAgeGateRenderer() {
|
||||
return jsonResponse.getObject("contents")
|
||||
.getObject("twoColumnBrowseResultsRenderer")
|
||||
.getArray("tabs")
|
||||
.stream()
|
||||
.filter(JsonObject.class::isInstance)
|
||||
.map(JsonObject.class::cast)
|
||||
.flatMap(tab -> tab.getObject("tabRenderer")
|
||||
.getObject("content")
|
||||
.getObject("sectionListRenderer")
|
||||
.getArray("contents")
|
||||
.stream()
|
||||
.filter(JsonObject.class::isInstance)
|
||||
.map(JsonObject.class::cast))
|
||||
.filter(content -> content.has("channelAgeGateRenderer"))
|
||||
.map(content -> content.getObject("channelAgeGateRenderer"))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
channelAgeGateRenderer = YoutubeChannelHelper.getChannelAgeGateRenderer(jsonResponse);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
|
|
@ -61,11 +61,15 @@ public class YoutubeChannelTabExtractor extends ChannelTabExtractor {
|
|||
private String channelId;
|
||||
@Nullable
|
||||
private String visitorData;
|
||||
@Nullable
|
||||
private JsonObject channelAgeGateRenderer;
|
||||
@Nullable
|
||||
private YoutubeChannelTabPlaylistExtractor playlistExtractorInstance;
|
||||
|
||||
public YoutubeChannelTabExtractor(final StreamingService service,
|
||||
final ListLinkHandler linkHandler) {
|
||||
super(service, linkHandler);
|
||||
useVisitorData = getName().equals(ChannelTabs.SHORTS);
|
||||
useVisitorData = ChannelTabs.SHORTS.equals(getName());
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@ -101,6 +105,23 @@ public class YoutubeChannelTabExtractor extends ChannelTabExtractor {
|
|||
if (useVisitorData) {
|
||||
visitorData = jsonResponse.getObject("responseContext").getString("visitorData");
|
||||
}
|
||||
channelAgeGateRenderer = YoutubeChannelHelper.getChannelAgeGateRenderer(jsonResponse);
|
||||
if (channelAgeGateRenderer != null) {
|
||||
final String channelTabName = getName();
|
||||
if (ChannelTabs.VIDEOS.equals(channelTabName)
|
||||
|| ChannelTabs.SHORTS.equals(channelTabName)
|
||||
|| ChannelTabs.LIVESTREAMS.equals(channelTabName)) {
|
||||
final ListLinkHandler originalLinkHandler = getLinkHandler();
|
||||
playlistExtractorInstance =
|
||||
new YoutubeChannelTabPlaylistExtractor(getService(),
|
||||
new ListLinkHandler(originalLinkHandler.getOriginalUrl(),
|
||||
originalLinkHandler.getUrl(),
|
||||
getId(),
|
||||
originalLinkHandler.getContentFilters(),
|
||||
originalLinkHandler.getSortFilter()));
|
||||
playlistExtractorInstance.fetchPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@ -117,29 +138,31 @@ public class YoutubeChannelTabExtractor extends ChannelTabExtractor {
|
|||
@Nonnull
|
||||
@Override
|
||||
public String getId() throws ParsingException {
|
||||
final String id = jsonResponse.getObject("header")
|
||||
.getObject("c4TabbedHeaderRenderer")
|
||||
.getString("channelId", "");
|
||||
if (channelAgeGateRenderer == null) {
|
||||
final String id = jsonResponse.getObject("header")
|
||||
.getObject("c4TabbedHeaderRenderer")
|
||||
.getString("channelId", "");
|
||||
|
||||
if (!id.isEmpty()) {
|
||||
return id;
|
||||
}
|
||||
if (!id.isEmpty()) {
|
||||
return id;
|
||||
}
|
||||
|
||||
final Optional<String> carouselHeaderId = jsonResponse.getObject("header")
|
||||
.getObject("carouselHeaderRenderer")
|
||||
.getArray("contents")
|
||||
.stream()
|
||||
.filter(JsonObject.class::isInstance)
|
||||
.map(JsonObject.class::cast)
|
||||
.filter(item -> item.has("topicChannelDetailsRenderer"))
|
||||
.findFirst()
|
||||
.flatMap(item ->
|
||||
Optional.ofNullable(item.getObject("topicChannelDetailsRenderer")
|
||||
.getObject("navigationEndpoint")
|
||||
.getObject("browseEndpoint")
|
||||
.getString("browseId")));
|
||||
if (carouselHeaderId.isPresent()) {
|
||||
return carouselHeaderId.get();
|
||||
final Optional<String> carouselHeaderId = jsonResponse.getObject("header")
|
||||
.getObject("carouselHeaderRenderer")
|
||||
.getArray("contents")
|
||||
.stream()
|
||||
.filter(JsonObject.class::isInstance)
|
||||
.map(JsonObject.class::cast)
|
||||
.filter(item -> item.has("topicChannelDetailsRenderer"))
|
||||
.findFirst()
|
||||
.flatMap(item ->
|
||||
Optional.ofNullable(item.getObject("topicChannelDetailsRenderer")
|
||||
.getObject("navigationEndpoint")
|
||||
.getObject("browseEndpoint")
|
||||
.getString("browseId")));
|
||||
if (carouselHeaderId.isPresent()) {
|
||||
return carouselHeaderId.get();
|
||||
}
|
||||
}
|
||||
|
||||
if (!isNullOrEmpty(channelId)) {
|
||||
|
@ -150,6 +173,10 @@ public class YoutubeChannelTabExtractor extends ChannelTabExtractor {
|
|||
}
|
||||
|
||||
protected String getChannelName() {
|
||||
if (channelAgeGateRenderer != null) {
|
||||
return channelAgeGateRenderer.getString("channelTitle");
|
||||
}
|
||||
|
||||
final String metadataName = jsonResponse.getObject("metadata")
|
||||
.getObject("channelMetadataRenderer")
|
||||
.getString("title");
|
||||
|
@ -176,6 +203,13 @@ public class YoutubeChannelTabExtractor extends ChannelTabExtractor {
|
|||
@Nonnull
|
||||
@Override
|
||||
public InfoItemsPage<InfoItem> getInitialPage() throws IOException, ExtractionException {
|
||||
if (channelAgeGateRenderer != null) {
|
||||
if (playlistExtractorInstance != null) {
|
||||
return playlistExtractorInstance.getInitialPage();
|
||||
}
|
||||
return InfoItemsPage.emptyPage();
|
||||
}
|
||||
|
||||
final MultiInfoItemsCollector collector = new MultiInfoItemsCollector(getServiceId());
|
||||
|
||||
JsonArray items = new JsonArray();
|
||||
|
@ -223,6 +257,13 @@ public class YoutubeChannelTabExtractor extends ChannelTabExtractor {
|
|||
@Override
|
||||
public InfoItemsPage<InfoItem> getPage(final Page page)
|
||||
throws IOException, ExtractionException {
|
||||
if (channelAgeGateRenderer != null) {
|
||||
if (playlistExtractorInstance != null) {
|
||||
return playlistExtractorInstance.getPage(page);
|
||||
}
|
||||
return InfoItemsPage.emptyPage();
|
||||
}
|
||||
|
||||
if (page == null || isNullOrEmpty(page.getUrl())) {
|
||||
throw new IllegalArgumentException("Page doesn't contain an URL");
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.schabi.newpipe.extractor.services.youtube;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.schabi.newpipe.extractor.ServiceList.YouTube;
|
||||
|
||||
|
@ -43,7 +42,6 @@ class YoutubeChannelTabExtractorTest {
|
|||
@Override public String expectedUrlContains() throws Exception { return "https://www.youtube.com/channel/UCTwECeGqMZee77BjdoYtI2Q/videos"; }
|
||||
@Override public String expectedOriginalUrlContains() throws Exception { return "https://www.youtube.com/user/creativecommons/videos"; }
|
||||
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; }
|
||||
@Override public boolean expectedHasMoreItems() { return true; }
|
||||
}
|
||||
|
||||
static class Playlists extends DefaultListExtractorTest<ChannelTabExtractor> {
|
||||
|
@ -65,7 +63,6 @@ class YoutubeChannelTabExtractorTest {
|
|||
@Override public String expectedUrlContains() throws Exception { return "https://www.youtube.com/channel/UC2DjFE7Xf11URZqWBigcVOQ/playlists"; }
|
||||
@Override public String expectedOriginalUrlContains() throws Exception { return "https://www.youtube.com/@EEVblog/playlists"; }
|
||||
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.PLAYLIST; }
|
||||
@Override public boolean expectedHasMoreItems() { return true; }
|
||||
}
|
||||
|
||||
static class Channels extends DefaultListExtractorTest<ChannelTabExtractor> {
|
||||
|
@ -87,7 +84,6 @@ class YoutubeChannelTabExtractorTest {
|
|||
@Override public String expectedUrlContains() throws Exception { return "https://www.youtube.com/channel/UC2DjFE7Xf11URZqWBigcVOQ/channels"; }
|
||||
@Override public String expectedOriginalUrlContains() throws Exception { return "https://www.youtube.com/channel/UC2DjFE7Xf11URZqWBigcVOQ/channels"; }
|
||||
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.CHANNEL; }
|
||||
@Override public boolean expectedHasMoreItems() { return true; }
|
||||
}
|
||||
|
||||
static class Livestreams extends DefaultListExtractorTest<ChannelTabExtractor> {
|
||||
|
@ -109,7 +105,6 @@ class YoutubeChannelTabExtractorTest {
|
|||
@Override public String expectedUrlContains() throws Exception { return "https://www.youtube.com/channel/UCR-DXc1voovS8nhAvccRZhg/streams"; }
|
||||
@Override public String expectedOriginalUrlContains() throws Exception { return "https://www.youtube.com/c/JeffGeerling/streams"; }
|
||||
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; }
|
||||
@Override public boolean expectedHasMoreItems() { return true; }
|
||||
}
|
||||
|
||||
static class Shorts extends DefaultListExtractorTest<ChannelTabExtractor> {
|
||||
|
@ -131,32 +126,12 @@ class YoutubeChannelTabExtractorTest {
|
|||
@Override public String expectedUrlContains() throws Exception { return "https://www.youtube.com/channel/UCh8gHdtzO2tXd593_bjErWg/shorts"; }
|
||||
@Override public String expectedOriginalUrlContains() throws Exception { return "https://www.youtube.com/channel/UCh8gHdtzO2tXd593_bjErWg/shorts"; }
|
||||
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; }
|
||||
@Override public boolean expectedHasMoreItems() { return true; }
|
||||
}
|
||||
|
||||
private static abstract class AgeRestrictedTabsVideosBaseTest
|
||||
extends DefaultListExtractorTest<ChannelTabExtractor> {
|
||||
|
||||
// TESTS FOR TABS OF AGE RESTRICTED CHANNELS
|
||||
// Fetching the tabs individually would use the standard tabs without fallback to
|
||||
// system playlists for stream tabs, we need to fetch the channel extractor to get the
|
||||
// channel playlist tabs
|
||||
// TODO: implement system playlists fallback in YoutubeChannelTabExtractor for stream
|
||||
// tabs
|
||||
|
||||
static class AgeRestrictedTabsVideos extends DefaultListExtractorTest<ChannelTabExtractor> {
|
||||
private static ChannelTabExtractor extractor;
|
||||
|
||||
@BeforeAll
|
||||
static void setUp() throws IOException, ExtractionException {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "ageRestrictedTabsVideos"));
|
||||
final ChannelExtractor channelExtractor = YouTube.getChannelExtractor(
|
||||
"https://www.youtube.com/channel/UCbfnHqxXs_K3kvaH-WlNlig");
|
||||
channelExtractor.fetchPage();
|
||||
|
||||
// the videos tab is the first one
|
||||
extractor = YouTube.getChannelTabExtractor(channelExtractor.getTabs().get(0));
|
||||
extractor.fetchPage();
|
||||
}
|
||||
protected static ChannelTabExtractor extractor;
|
||||
|
||||
@Override public ChannelTabExtractor extractor() throws Exception { return extractor; }
|
||||
@Override public StreamingService expectedService() throws Exception { return YouTube; }
|
||||
|
@ -165,7 +140,18 @@ class YoutubeChannelTabExtractorTest {
|
|||
@Override public String expectedUrlContains() throws Exception { return "https://www.youtube.com/channel/UCbfnHqxXs_K3kvaH-WlNlig/videos"; }
|
||||
@Override public String expectedOriginalUrlContains() throws Exception { return "https://www.youtube.com/channel/UCbfnHqxXs_K3kvaH-WlNlig/videos"; }
|
||||
@Override public InfoItem.InfoType expectedInfoItemType() { return InfoItem.InfoType.STREAM; }
|
||||
@Override public boolean expectedHasMoreItems() { return true; }
|
||||
}
|
||||
|
||||
static class AgeRestrictedTabsVideos extends AgeRestrictedTabsVideosBaseTest {
|
||||
@BeforeAll
|
||||
static void setUp() throws IOException, ExtractionException {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(
|
||||
RESOURCE_PATH + "ageRestrictedTabsVideos"));
|
||||
extractor = YouTube.getChannelTabExtractorFromId(
|
||||
"channel/UCbfnHqxXs_K3kvaH-WlNlig", ChannelTabs.VIDEOS);
|
||||
extractor.fetchPage();
|
||||
}
|
||||
}
|
||||
|
||||
static class AgeRestrictedTabsShorts extends DefaultListExtractorTest<ChannelTabExtractor> {
|
||||
|
@ -174,13 +160,10 @@ class YoutubeChannelTabExtractorTest {
|
|||
@BeforeAll
|
||||
static void setUp() throws IOException, ExtractionException {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "ageRestrictedTabsShorts"));
|
||||
final ChannelExtractor channelExtractor = YouTube.getChannelExtractor(
|
||||
"https://www.youtube.com/channel/UCbfnHqxXs_K3kvaH-WlNlig");
|
||||
channelExtractor.fetchPage();
|
||||
|
||||
// the shorts tab is the second one
|
||||
extractor = YouTube.getChannelTabExtractor(channelExtractor.getTabs().get(1));
|
||||
NewPipe.init(DownloaderFactory.getDownloader(
|
||||
RESOURCE_PATH + "ageRestrictedTabsShorts"));
|
||||
extractor = YouTube.getChannelTabExtractorFromId(
|
||||
"channel/UCbfnHqxXs_K3kvaH-WlNlig", ChannelTabs.SHORTS);
|
||||
extractor.fetchPage();
|
||||
}
|
||||
|
||||
|
@ -195,9 +178,26 @@ class YoutubeChannelTabExtractorTest {
|
|||
@Test
|
||||
@Override
|
||||
public void testRelatedItems() throws Exception {
|
||||
// this channel has no shorts, so an empty page is returned by the playlist extractor
|
||||
// This channel has no shorts, so an empty page should be returned by the playlist
|
||||
// extractor
|
||||
assertTrue(extractor.getInitialPage().getItems().isEmpty());
|
||||
assertTrue(extractor.getInitialPage().getErrors().isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
static class AgeRestrictedTabsVideosFromChannel extends AgeRestrictedTabsVideosBaseTest {
|
||||
@BeforeAll
|
||||
static void setUp() throws IOException, ExtractionException {
|
||||
YoutubeTestsUtils.ensureStateless();
|
||||
NewPipe.init(DownloaderFactory.getDownloader(
|
||||
RESOURCE_PATH + "ageRestrictedTabsVideosFromChannel"));
|
||||
final ChannelExtractor channelExtractor = YouTube.getChannelExtractor(
|
||||
"https://www.youtube.com/channel/UCbfnHqxXs_K3kvaH-WlNlig");
|
||||
channelExtractor.fetchPage();
|
||||
|
||||
// the videos tab is the first one
|
||||
extractor = YouTube.getChannelTabExtractor(channelExtractor.getTabs().get(0));
|
||||
extractor.fetchPage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -34,20 +34,17 @@
|
|||
"cache-control": [
|
||||
"private, max-age\u003d0"
|
||||
],
|
||||
"content-security-policy-report-only": [
|
||||
"require-trusted-types-for \u0027script\u0027;report-uri /cspreport"
|
||||
],
|
||||
"content-type": [
|
||||
"text/javascript; charset\u003dutf-8"
|
||||
],
|
||||
"cross-origin-opener-policy": [
|
||||
"cross-origin-opener-policy-report-only": [
|
||||
"same-origin; report-to\u003d\"youtube_main\""
|
||||
],
|
||||
"date": [
|
||||
"Sun, 06 Aug 2023 11:54:54 GMT"
|
||||
"Sun, 27 Aug 2023 13:58:38 GMT"
|
||||
],
|
||||
"expires": [
|
||||
"Sun, 06 Aug 2023 11:54:54 GMT"
|
||||
"Sun, 27 Aug 2023 13:58:38 GMT"
|
||||
],
|
||||
"origin-trial": [
|
||||
"AvC9UlR6RDk2crliDsFl66RWLnTbHrDbp+DiY6AYz/PNQ4G4tdUTjrHYr2sghbkhGQAVxb7jaPTHpEVBz0uzQwkAAAB4eyJvcmlnaW4iOiJodHRwczovL3lvdXR1YmUuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJWaWV3WFJlcXVlc3RlZFdpdGhEZXByZWNhdGlvbiIsImV4cGlyeSI6MTcxOTUzMjc5OSwiaXNTdWJkb21haW4iOnRydWV9"
|
||||
|
@ -65,9 +62,10 @@
|
|||
"ESF"
|
||||
],
|
||||
"set-cookie": [
|
||||
"YSC\u003dIXSPROQ-CH8; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003d; Domain\u003d.youtube.com; Expires\u003dMon, 09-Nov-2020 11:54:54 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"CONSENT\u003dPENDING+942; expires\u003dTue, 05-Aug-2025 11:54:54 GMT; path\u003d/; domain\u003d.youtube.com; Secure"
|
||||
"YSC\u003dfX4diOWsOao; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003d; Domain\u003d.youtube.com; Expires\u003dMon, 30-Nov-2020 13:58:38 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_PRIVACY_METADATA\u003dCgJGUhIA; Domain\u003d.youtube.com; Expires\u003dFri, 23-Feb-2024 13:58:38 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dlax",
|
||||
"CONSENT\u003dPENDING+200; expires\u003dTue, 26-Aug-2025 13:58:38 GMT; path\u003d/; domain\u003d.youtube.com; Secure"
|
||||
],
|
||||
"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
|
@ -41,10 +41,10 @@
|
|||
"same-origin; report-to\u003d\"youtube_main\""
|
||||
],
|
||||
"date": [
|
||||
"Sun, 06 Aug 2023 11:55:07 GMT"
|
||||
"Sun, 27 Aug 2023 13:58:22 GMT"
|
||||
],
|
||||
"expires": [
|
||||
"Sun, 06 Aug 2023 11:55:07 GMT"
|
||||
"Sun, 27 Aug 2023 13:58:22 GMT"
|
||||
],
|
||||
"origin-trial": [
|
||||
"AvC9UlR6RDk2crliDsFl66RWLnTbHrDbp+DiY6AYz/PNQ4G4tdUTjrHYr2sghbkhGQAVxb7jaPTHpEVBz0uzQwkAAAB4eyJvcmlnaW4iOiJodHRwczovL3lvdXR1YmUuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJWaWV3WFJlcXVlc3RlZFdpdGhEZXByZWNhdGlvbiIsImV4cGlyeSI6MTcxOTUzMjc5OSwiaXNTdWJkb21haW4iOnRydWV9"
|
||||
|
@ -62,9 +62,10 @@
|
|||
"ESF"
|
||||
],
|
||||
"set-cookie": [
|
||||
"YSC\u003dqCdA3bmpLFQ; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003d; Domain\u003d.youtube.com; Expires\u003dMon, 09-Nov-2020 11:55:07 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"CONSENT\u003dPENDING+717; expires\u003dTue, 05-Aug-2025 11:55:07 GMT; path\u003d/; domain\u003d.youtube.com; Secure"
|
||||
"YSC\u003dVBy9_nvvJ1s; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003d; Domain\u003d.youtube.com; Expires\u003dMon, 30-Nov-2020 13:58:22 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_PRIVACY_METADATA\u003dCgJGUhIA; Domain\u003d.youtube.com; Expires\u003dFri, 23-Feb-2024 13:58:22 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dlax",
|
||||
"CONSENT\u003dPENDING+755; expires\u003dTue, 26-Aug-2025 13:58:22 GMT; path\u003d/; domain\u003d.youtube.com; Secure"
|
||||
],
|
||||
"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,86 @@
|
|||
{
|
||||
"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-type": [
|
||||
"text/javascript; charset\u003dutf-8"
|
||||
],
|
||||
"cross-origin-opener-policy-report-only": [
|
||||
"same-origin; report-to\u003d\"youtube_main\""
|
||||
],
|
||||
"date": [
|
||||
"Sun, 27 Aug 2023 13:58:55 GMT"
|
||||
],
|
||||
"expires": [
|
||||
"Sun, 27 Aug 2023 13:58:55 GMT"
|
||||
],
|
||||
"origin-trial": [
|
||||
"AvC9UlR6RDk2crliDsFl66RWLnTbHrDbp+DiY6AYz/PNQ4G4tdUTjrHYr2sghbkhGQAVxb7jaPTHpEVBz0uzQwkAAAB4eyJvcmlnaW4iOiJodHRwczovL3lvdXR1YmUuY29tOjQ0MyIsImZlYXR1cmUiOiJXZWJWaWV3WFJlcXVlc3RlZFdpdGhEZXByZWNhdGlvbiIsImV4cGlyeSI6MTcxOTUzMjc5OSwiaXNTdWJkb21haW4iOnRydWV9"
|
||||
],
|
||||
"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-factor\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\u003dzUzmQXpaKn8; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003d; Domain\u003d.youtube.com; Expires\u003dMon, 30-Nov-2020 13:58:55 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_PRIVACY_METADATA\u003dCgJGUhIA; Domain\u003d.youtube.com; Expires\u003dFri, 23-Feb-2024 13:58:55 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dlax",
|
||||
"CONSENT\u003dPENDING+311; expires\u003dTue, 26-Aug-2025 13:58:55 GMT; path\u003d/; domain\u003d.youtube.com; Secure"
|
||||
],
|
||||
"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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue