Unverified Commit 7e6f4644 authored by Tobi's avatar Tobi Committed by GitHub
Browse files

Merge pull request #509 from TiA4f8R/soundcloud-improvements

Add new exceptions to be able to display different error messages in apps
parents ac51134a e55284bb
package org.schabi.newpipe.extractor.exceptions;
public class AgeRestrictedContentException extends ContentNotAvailableException {
public AgeRestrictedContentException(final String message) {
super(message);
}
public AgeRestrictedContentException(final String message, final Throwable cause) {
super(message, cause);
}
}
package org.schabi.newpipe.extractor.exceptions;
public class ContentNotAvailableException extends ParsingException {
public ContentNotAvailableException(String message) {
public ContentNotAvailableException(final String message) {
super(message);
}
public ContentNotAvailableException(String message, Throwable cause) {
public ContentNotAvailableException(final String message, final Throwable cause) {
super(message, cause);
}
}
package org.schabi.newpipe.extractor.exceptions;
public class ContentNotSupportedException extends ParsingException {
public ContentNotSupportedException(String message) {
public ContentNotSupportedException(final String message) {
super(message);
}
public ContentNotSupportedException(String message, Throwable cause) {
public ContentNotSupportedException(final String message, final Throwable cause) {
super(message, cause);
}
}
......@@ -21,15 +21,15 @@ package org.schabi.newpipe.extractor.exceptions;
*/
public class ExtractionException extends Exception {
public ExtractionException(String message) {
public ExtractionException(final String message) {
super(message);
}
public ExtractionException(Throwable cause) {
public ExtractionException(final Throwable cause) {
super(cause);
}
public ExtractionException(String message, Throwable cause) {
public ExtractionException(final String message, final Throwable cause) {
super(message, cause);
}
}
\ No newline at end of file
}
......@@ -21,11 +21,11 @@ package org.schabi.newpipe.extractor.exceptions;
*/
public class FoundAdException extends ParsingException {
public FoundAdException(String message) {
public FoundAdException(final String message) {
super(message);
}
public FoundAdException(String message, Throwable cause) {
public FoundAdException(final String message, final Throwable cause) {
super(message, cause);
}
}
package org.schabi.newpipe.extractor.exceptions;
public class GeographicRestrictionException extends ContentNotAvailableException {
public GeographicRestrictionException(final String message) {
super(message);
}
public GeographicRestrictionException(final String message, final Throwable cause) {
super(message, cause);
}
}
package org.schabi.newpipe.extractor.exceptions;
public class PaidContentException extends ContentNotAvailableException {
public PaidContentException(final String message) {
super(message);
}
public PaidContentException(final String message, final Throwable cause) {
super(message, cause);
}
}
......@@ -22,11 +22,11 @@ package org.schabi.newpipe.extractor.exceptions;
public class ParsingException extends ExtractionException {
public ParsingException(String message) {
public ParsingException(final String message) {
super(message);
}
public ParsingException(String message, Throwable cause) {
public ParsingException(final String message, final Throwable cause) {
super(message, cause);
}
}
\ No newline at end of file
}
package org.schabi.newpipe.extractor.exceptions;
public class PrivateContentException extends ContentNotAvailableException {
public PrivateContentException(final String message) {
super(message);
}
public PrivateContentException(final String message, final Throwable cause) {
super(message, cause);
}
}
......@@ -23,7 +23,7 @@ package org.schabi.newpipe.extractor.exceptions;
public class ReCaptchaException extends ExtractionException {
private String url;
public ReCaptchaException(String message, String url) {
public ReCaptchaException(final String message, final String url) {
super(message);
this.url = url;
}
......
package org.schabi.newpipe.extractor.exceptions;
public class SoundCloudGoPlusContentException extends ContentNotAvailableException {
public SoundCloudGoPlusContentException() {
super("This track is a SoundCloud Go+ track");
}
public SoundCloudGoPlusContentException(final Throwable cause) {
super("This track is a SoundCloud Go+ track", cause);
}
}
package org.schabi.newpipe.extractor.exceptions;
public class YoutubeMusicPremiumContentException extends ContentNotAvailableException {
public YoutubeMusicPremiumContentException() {
super("This video is a YouTube Music Premium video");
}
public YoutubeMusicPremiumContentException(final Throwable cause) {
super("This video is a YouTube Music Premium video", cause);
}
}
......@@ -12,7 +12,9 @@ import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException;
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
import org.schabi.newpipe.extractor.localization.DateWrapper;
import org.schabi.newpipe.extractor.services.soundcloud.SoundcloudParsingHelper;
......@@ -43,6 +45,12 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
String policy = track.getString("policy", EMPTY_STRING);
if (!policy.equals("ALLOW") && !policy.equals("MONETIZE")) {
if (policy.equals("SNIP")) {
throw new SoundCloudGoPlusContentException();
}
if (policy.equals("BLOCK")) {
throw new GeographicRestrictionException("This track is not available in user's country");
}
throw new ContentNotAvailableException("Content not available: policy " + policy);
}
}
......
......@@ -48,6 +48,7 @@ public abstract class DefaultStreamExtractorTest extends DefaultExtractorTest<St
public boolean expectedUploaderVerified() { return false; }
public String expectedSubChannelName() { return ""; } // default: there is no subchannel
public String expectedSubChannelUrl() { return ""; } // default: there is no subchannel
public boolean expectedDescriptionIsEmpty() { return false; } // default: description is not empty
public abstract List<String> expectedDescriptionContains(); // e.g. for full links
public abstract long expectedLength();
public long expectedTimestamp() { return 0; } // default: there is no timestamp
......@@ -146,7 +147,12 @@ public abstract class DefaultStreamExtractorTest extends DefaultExtractorTest<St
public void testDescription() throws Exception {
final Description description = extractor().getDescription();
assertNotNull(description);
assertFalse("description is empty", description.getContent().isEmpty());
if (expectedDescriptionIsEmpty()) {
assertTrue("description is not empty", description.getContent().isEmpty());
} else {
assertFalse("description is empty", description.getContent().isEmpty());
}
for (final String s : expectedDescriptionContains()) {
assertThat(description.getContent(), containsString(s));
......@@ -225,7 +231,7 @@ public abstract class DefaultStreamExtractorTest extends DefaultExtractorTest<St
defaultTestListOfItems(extractor().getService(), relatedStreams.getItems(),
relatedStreams.getErrors());
} else {
assertNull(relatedStreams);
assertTrue(relatedStreams == null || relatedStreams.getItems().isEmpty());
}
}
......
package org.schabi.newpipe.extractor.services.soundcloud;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.schabi.newpipe.downloader.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ContentNotSupportedException;
import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException;
import org.schabi.newpipe.extractor.exceptions.SoundCloudGoPlusContentException;
import org.schabi.newpipe.extractor.services.DefaultStreamExtractorTest;
import org.schabi.newpipe.extractor.stream.StreamExtractor;
import org.schabi.newpipe.extractor.stream.StreamType;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
......@@ -16,10 +22,93 @@ import javax.annotation.Nullable;
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
public class SoundcloudStreamExtractorTest {
private static final String SOUNDCLOUD = "https://soundcloud.com/";
@Ignore("Ignore until #526 is merged. Throwing the ContentNotSupportedException is wrong and going to be fixed by that PR.")
public static class SoundcloudGeoRestrictedTrack extends DefaultStreamExtractorTest {
private static final String ID = "one-touch";
private static final String UPLOADER = SOUNDCLOUD + "jessglynne";
private static final int TIMESTAMP = 0;
private static final String URL = UPLOADER + "/" + ID + "#t=" + TIMESTAMP;
private static StreamExtractor extractor;
@Test(expected = GeographicRestrictionException.class)
public void geoRestrictedContent() throws Exception {
NewPipe.init(DownloaderTestImpl.getInstance());
extractor = SoundCloud.getStreamExtractor(URL);
extractor.fetchPage();
}
@Override public StreamExtractor extractor() { return extractor; }
@Override public StreamingService expectedService() { return SoundCloud; }
@Override public String expectedName() { return "Jess Glynne & Jax Jones - One Touch"; }
@Override public String expectedId() { return "621612588"; }
@Override public String expectedUrlContains() { return UPLOADER + "/" + ID; }
@Override public String expectedOriginalUrlContains() { return URL; }
@Override public StreamType expectedStreamType() { return StreamType.AUDIO_STREAM; }
@Override public String expectedUploaderName() { return "Jess Glynne"; }
@Override public String expectedUploaderUrl() { return UPLOADER; }
@Override public boolean expectedUploaderVerified() { return true; }
@Override public boolean expectedDescriptionIsEmpty() { return true; }
@Override public List<String> expectedDescriptionContains() { return Collections.emptyList(); }
@Override public long expectedLength() { return 197; }
@Override public long expectedTimestamp() { return TIMESTAMP; }
@Override public long expectedViewCountAtLeast() { return 43000; }
@Nullable @Override public String expectedUploadDate() { return "2019-05-16 16:28:45.000"; }
@Nullable @Override public String expectedTextualUploadDate() { return "2019-05-16 16:28:45"; }
@Override public long expectedLikeCountAtLeast() { return -1; }
@Override public long expectedDislikeCountAtLeast() { return -1; }
@Override public boolean expectedHasVideoStreams() { return false; }
@Override public boolean expectedHasSubtitles() { return false; }
@Override public boolean expectedHasFrames() { return false; }
@Override public int expectedStreamSegmentsCount() { return 0; }
@Override public boolean expectedHasRelatedStreams() { return false; }
}
public static class SoundcloudGoPlusTrack extends DefaultStreamExtractorTest {
private static final String ID = "places";
private static final String UPLOADER = SOUNDCLOUD + "martinsolveig";
private static final int TIMESTAMP = 0;
private static final String URL = UPLOADER + "/" + ID + "#t=" + TIMESTAMP;
private static StreamExtractor extractor;
@Test(expected = SoundCloudGoPlusContentException.class)
public void goPlusContent() throws Exception {
NewPipe.init(DownloaderTestImpl.getInstance());
extractor = SoundCloud.getStreamExtractor(URL);
extractor.fetchPage();
}
@Override public StreamExtractor extractor() { return extractor; }
@Override public StreamingService expectedService() { return SoundCloud; }
@Override public String expectedName() { return "Places (feat. Ina Wroldsen)"; }
@Override public String expectedId() { return "292479564"; }
@Override public String expectedUrlContains() { return UPLOADER + "/" + ID; }
@Override public String expectedOriginalUrlContains() { return URL; }
@Override public StreamType expectedStreamType() { return StreamType.AUDIO_STREAM; }
@Override public String expectedUploaderName() { return "martinsolveig"; }
@Override public String expectedUploaderUrl() { return UPLOADER; }
@Override public boolean expectedUploaderVerified() { return true; }
@Override public boolean expectedDescriptionIsEmpty() { return true; }
@Override public List<String> expectedDescriptionContains() { return Collections.emptyList(); }
@Override public long expectedLength() { return 30; }
@Override public long expectedTimestamp() { return TIMESTAMP; }
@Override public long expectedViewCountAtLeast() { return 386000; }
@Nullable @Override public String expectedUploadDate() { return "2016-11-11 01:16:37.000"; }
@Nullable @Override public String expectedTextualUploadDate() { return "2016-11-11 01:16:37"; }
@Override public long expectedLikeCountAtLeast() { return -1; }
@Override public long expectedDislikeCountAtLeast() { return -1; }
@Override public boolean expectedHasVideoStreams() { return false; }
@Override public boolean expectedHasSubtitles() { return false; }
@Override public boolean expectedHasFrames() { return false; }
@Override public int expectedStreamSegmentsCount() { return 0; }
}
public static class CreativeCommonsPlaysWellWithOthers extends DefaultStreamExtractorTest {
private static final String ID = "plays-well-with-others-ep-2-what-do-an-army-of-ants-and-an-online-encyclopedia-have-in-common";
private static final String UPLOADER = "https://soundcloud.com/wearecc";
private static final String UPLOADER = SOUNDCLOUD + "wearecc";
private static final int TIMESTAMP = 69;
private static final String URL = UPLOADER + "/" + ID + "#t=" + TIMESTAMP;
private static StreamExtractor extractor;
......
......@@ -8,7 +8,11 @@ import org.schabi.newpipe.extractor.MetaInfo;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.GeographicRestrictionException;
import org.schabi.newpipe.extractor.exceptions.PaidContentException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.exceptions.PrivateContentException;
import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException;
import org.schabi.newpipe.extractor.services.DefaultStreamExtractorTest;
import org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper;
import org.schabi.newpipe.extractor.stream.Description;
......@@ -60,6 +64,13 @@ public class YoutubeStreamExtractorDefaultTest {
NewPipe.init(new DownloaderFactory().getDownloader(RESOURCE_PATH + "notAvailable"));
}
@Test(expected = GeographicRestrictionException.class)
public void geoRestrictedContent() throws Exception {
final StreamExtractor extractor =
YouTube.getStreamExtractor(BASE_URL + "_PL2HJKxnOM");
extractor.fetchPage();
}
@Test(expected = ContentNotAvailableException.class)
public void nonExistentFetch() throws Exception {
final StreamExtractor extractor =
......@@ -73,6 +84,27 @@ public class YoutubeStreamExtractorDefaultTest {
YouTube.getStreamExtractor(BASE_URL + "INVALID_ID_INVALID_ID");
extractor.fetchPage();
}
@Test(expected = PaidContentException.class)
public void paidContent() throws Exception {
final StreamExtractor extractor =
YouTube.getStreamExtractor(BASE_URL + "ayI2iBwGdxw");
extractor.fetchPage();
}
@Test(expected = PrivateContentException.class)
public void privateContent() throws Exception {
final StreamExtractor extractor =
YouTube.getStreamExtractor(BASE_URL + "8VajtrESJzA");
extractor.fetchPage();
}
@Test(expected = YoutubeMusicPremiumContentException.class)
public void youtubeMusicPremiumContent() throws Exception {
final StreamExtractor extractor =
YouTube.getStreamExtractor(BASE_URL + "sMJ8bRN2dak");
extractor.fetchPage();
}
}
public static class DescriptionTestPewdiepie extends DefaultStreamExtractorTest {
......
......@@ -20,7 +20,7 @@
},
"response": {
"responseCode": 200,
"responseMessage": "",
"responseMessage": "OK",
"responseHeaders": {
"alt-svc": [
"h3-29\u003d\":443\"; ma\u003d2592000,h3-T051\u003d\":443\"; ma\u003d2592000,h3-Q050\u003d\":443\"; ma\u003d2592000,h3-Q046\u003d\":443\"; ma\u003d2592000,h3-Q043\u003d\":443\"; ma\u003d2592000,quic\u003d\":443\"; ma\u003d2592000; v\u003d\"46,43\""
......@@ -35,7 +35,7 @@
"application/json; charset\u003dutf-8"
],
"date": [
"Sat, 13 Feb 2021 19:13:27 GMT"
"Sun, 21 Feb 2021 16:32:21 GMT"
],
"expires": [
"Mon, 01 Jan 1990 00:00:00 GMT"
......@@ -50,14 +50,17 @@
"ESF"
],
"set-cookie": [
"GPS\u003d1; Domain\u003d.youtube.com; Expires\u003dSat, 13-Feb-2021 19:43:27 GMT; Path\u003d/; Secure; HttpOnly",
"YSC\u003djEoKPLL534o; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
"VISITOR_INFO1_LIVE\u003d3tVKAqhB50g; Domain\u003d.youtube.com; Expires\u003dThu, 12-Aug-2021 19:13:27 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
"CONSENT\u003dPENDING+694; expires\u003dFri, 01-Jan-2038 00:00:00 GMT; path\u003d/; domain\u003d.youtube.com"
"GPS\u003d1; Domain\u003d.youtube.com; Expires\u003dSun, 21-Feb-2021 17:02:21 GMT; Path\u003d/; Secure; HttpOnly",
"YSC\u003dmqhSM1eHxP0; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
"VISITOR_INFO1_LIVE\u003dtBlN1T2LiYc; Domain\u003d.youtube.com; Expires\u003dFri, 20-Aug-2021 16:32:21 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
"CONSENT\u003dPENDING+871; expires\u003dFri, 01-Jan-2038 00:00:00 GMT; path\u003d/; domain\u003d.youtube.com"
],
"strict-transport-security": [
"max-age\u003d31536000"
],
"transfer-encoding": [
"chunked"
],
"x-content-type-options": [
"nosniff"
],
......@@ -71,7 +74,7 @@
"0"
]
},
"responseBody": "[\r\n{\"page\": \"watch\",\"rootVe\": \"3832\"},\r\n{\"page\": \"watch\",\"preconnect\": [\"https:\\/\\/r5---sn-4g5ednly.googlevideo.com\\/generate_204\",\"https:\\/\\/r5---sn-4g5ednly.googlevideo.com\\/generate_204?conn2\"]},\r\n{\"page\": \"watch\",\"playerResponse\": {\"responseContext\":{\"serviceTrackingParams\":[{\"service\":\"GFEEDBACK\",\"params\":[{\"key\":\"is_viewed_live\",\"value\":\"False\"},{\"key\":\"logged_in\",\"value\":\"0\"},{\"key\":\"e\",\"value\":\"23744176,23987676,23994373,23912909,23890959,23970529,23839597,23934970,23891344,23974595,1714255,23804281,23946420,23918597,23971936,23858057,23891346,23884386,23969934,23996751,23882502,23968386,23986026,23976578,23996375,24000882,23944779\"}]},{\"service\":\"CSI\",\"params\":[{\"key\":\"c\",\"value\":\"WEB\"},{\"key\":\"cver\",\"value\":\"2.20200214.04.00\"},{\"key\":\"yt_li\",\"value\":\"0\"},{\"key\":\"GetPlayer_rid\",\"value\":\"0xedf6baa3a1796f75\"}]},{\"service\":\"GUIDED_HELP\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"}]},{\"service\":\"ECATCHER\",\"params\":[{\"key\":\"client.version\",\"value\":\"2.20210114\"},{\"key\":\"client.name\",\"value\":\"WEB\"}]}],\"mainAppWebResponseContext\":{\"loggedOut\":true},\"webResponseContextExtensionData\":{\"hasDecorated\":true}},\"playabilityStatus\":{\"status\":\"ERROR\",\"reason\":\"Video unavailable\",\"errorScreen\":{\"playerErrorMessageRenderer\":{\"reason\":{\"simpleText\":\"Video unavailable\"},\"thumbnail\":{\"thumbnails\":[{\"url\":\"//s.ytimg.com/yts/img/meh7-vflGevej7.png\",\"width\":140,\"height\":100}]},\"icon\":{\"iconType\":\"ERROR_OUTLINE\"}}},\"contextParams\":\"Q0FBU0FnZ0E\u003d\"},\"trackingParams\":\"CAAQu2kiEwjP37bhyOfuAhUWHeAKHRPGDjM\u003d\"}},\r\n{\"page\": \"watch\",\"response\": {\"responseContext\":{\"webResponseContextExtensionData\":{\"ytConfigData\":{\"visitorData\":\"CgszdFZLQXFoQjUwZyjXyaCBBg%3D%3D\",\"rootVisualElementType\":3832}}}},\"xsrf_token\": \"QUFFLUhqbW5vX0NhQzZZc0JmRkdQRHJLWElKSkZUVDc3UXxBQ3Jtc0tuTnNpZm5haW1ySHkzZjM1THpxYk1OMUpMVWtuWC1Pd0FrbnE5Tm0yTldoYkgzdjdxUXBTVzhNaVRuZ2FFc2d4QngzWFBral8xM3d1UGlmdkxnSXp0ekhMWUJCamtkeEV4eUFlR3lZUWl1b1o5RkpObw\\u003d\\u003d\",\"url\": \"/watch?v\\u003dINVALID_ID_\",\"endpoint\": {\"clickTrackingParams\":\"IhMIn9m14cjn7gIV0ox8Ch2Udg5_MghleHRlcm5hbA\u003d\u003d\",\"commandMetadata\":{\"webCommandMetadata\":{\"url\":\"/watch?v\u003dINVALID_ID_\",\"webPageType\":\"WEB_PAGE_TYPE_WATCH\",\"rootVe\":3832}},\"watchEndpoint\":{\"videoId\":\"INVALID_ID_\"}}},\r\n{\"page\": \"watch\",\"timing\": {\"info\": {\"st\": 0.0 }}}]\r\n",
"responseBody": "[\r\n{\"page\": \"watch\",\"rootVe\": \"3832\"},\r\n{\"page\": \"watch\",\"preconnect\": [\"https:\\/\\/r5---sn-25glen7r.googlevideo.com\\/generate_204\",\"https:\\/\\/r5---sn-25glen7r.googlevideo.com\\/generate_204?conn2\"]},\r\n{\"page\": \"watch\",\"playerResponse\": {\"responseContext\":{\"serviceTrackingParams\":[{\"service\":\"GFEEDBACK\",\"params\":[{\"key\":\"is_viewed_live\",\"value\":\"False\"},{\"key\":\"logged_in\",\"value\":\"0\"},{\"key\":\"e\",\"value\":\"1714259,23981915,23974595,23890959,23744176,23987676,23986020,23974883,23999517,23944779,23992808,23839597,24000882,23996624,23891344,23977071,23994373,23966208,23970529,24006263,23891346,23997047,23741522,23857950,23882502,23991264,23996751,23969934,23884386,9466593,23976578,23848212,23934970,23946420,23973444,23804281,23971936,23877025,23980552,23998149,23994474,23968386,23918597,23999478\"}]},{\"service\":\"CSI\",\"params\":[{\"key\":\"c\",\"value\":\"WEB\"},{\"key\":\"cver\",\"value\":\"2.20200214.04.00\"},{\"key\":\"yt_li\",\"value\":\"0\"},{\"key\":\"GetPlayer_rid\",\"value\":\"0xbe1d80f13a2a71eb\"}]},{\"service\":\"GUIDED_HELP\",\"params\":[{\"key\":\"logged_in\",\"value\":\"0\"}]},{\"service\":\"ECATCHER\",\"params\":[{\"key\":\"client.version\",\"value\":\"2.20210114\"},{\"key\":\"client.name\",\"value\":\"WEB\"}]}],\"mainAppWebResponseContext\":{\"loggedOut\":true},\"webResponseContextExtensionData\":{\"hasDecorated\":true}},\"playabilityStatus\":{\"status\":\"ERROR\",\"reason\":\"Video unavailable\",\"errorScreen\":{\"playerErrorMessageRenderer\":{\"reason\":{\"simpleText\":\"Video unavailable\"},\"thumbnail\":{\"thumbnails\":[{\"url\":\"//s.ytimg.com/yts/img/meh7-vflGevej7.png\",\"width\":140,\"height\":100}]},\"icon\":{\"iconType\":\"ERROR_OUTLINE\"}}},\"contextParams\":\"Q0FBU0FnZ0E\u003d\"},\"trackingParams\":\"CAAQu2kiEwiM2ZfWs_vuAhVMBQYAHSlPDN4\u003d\"}},\r\n{\"page\": \"watch\",\"response\": {\"responseContext\":{\"webResponseContextExtensionData\":{\"ytConfigData\":{\"visitorData\":\"Cgt0QmxOMVQyTGlZYyiVlsqBBg%3D%3D\",\"rootVisualElementType\":3832}}}},\"xsrf_token\": \"QUFFLUhqbFdNMzIzMmFLWGdvTjI3M255R3lOMm1lb2h4d3xBQ3Jtc0ttODlkSUMtS3ZlR1RnRVFTLWVvY3hpVjZ5Z091N1R4aXRxanJkaEQzb1p3SlFUVVNLSi1lZTBzR1VhUkRnTG5WQVk0UkNmTFBqeGFnTG5UN3k5dDA2Zk9kbkgyaXdPbzgtZjVtX190ZE5YdzlWR1lCQQ\\u003d\\u003d\",\"url\": \"/watch?v\\u003dINVALID_ID_\",\"endpoint\": {\"clickTrackingParams\":\"IhMIhvSW1rP77gIVrhvxBR0B-w8mMghleHRlcm5hbA\u003d\u003d\",\"commandMetadata\":{\"webCommandMetadata\":{\"url\":\"/watch?v\u003dINVALID_ID_\",\"webPageType\":\"WEB_PAGE_TYPE_WATCH\",\"rootVe\":3832}},\"watchEndpoint\":{\"videoId\":\"INVALID_ID_\"}}},\r\n{\"page\": \"watch\",\"timing\": {\"info\": {\"st\": 0.0 }}}]\r\n",
"latestUrl": "https://www.youtube.com/watch?v\u003dINVALID_ID_\u0026pbj\u003d1"
}
}
\ No newline at end of file
{
"request": {
"httpMethod": "GET",
"url": "https://www.youtube.com/watch?v\u003d8VajtrESJzA\u0026pbj\u003d1",
"headers": {
"Accept-Language": [
"en-GB, en;q\u003d0.9"
],
"X-YouTube-Client-Name": [
"1"
],
"X-YouTube-Client-Version": [
"2.20200214.04.00"
]
},
"localization": {
"languageCode": "en",
"countryCode": "GB"
}
},
"response": {
"responseCode": 200,
"responseMessage": "OK",
"responseHeaders": {
"alt-svc": [
"h3-29\u003d\":443\"; ma\u003d2592000,h3-T051\u003d\":443\"; ma\u003d2592000,h3-Q050\u003d\":443\"; ma\u003d2592000,h3-Q046\u003d\":443\"; ma\u003d2592000,h3-Q043\u003d\":443\"; ma\u003d2592000,quic\u003d\":443\"; ma\u003d2592000; v\u003d\"46,43\""
],
"cache-control": [
"no-cache, no-store, max-age\u003d0, must-revalidate"
],
"content-disposition": [
"attachment"
],
"content-type": [
"application/json; charset\u003dutf-8"
],
"date": [
"Sun, 21 Feb 2021 16:32:24 GMT"
],
"expires": [
"Mon, 01 Jan 1990 00:00:00 GMT"
],
"p3p": [
"CP\u003d\"This is not a P3P policy! See http://support.google.com/accounts/answer/151657?hl\u003den-GB for more info.\""
],
"pragma": [
"no-cache"
],
"server": [
"ESF"
],
"set-cookie": [
"GPS\u003d1; Domain\u003d.youtube.com; Expires\u003dSun, 21-Feb-2021 17:02:24 GMT; Path\u003d/; Secure; HttpOnly",
"YSC\u003d19P0gGWXP2o; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
"VISITOR_INFO1_LIVE\u003da-2fj78bANo; Domain\u003d.youtube.com; Expires\u003dFri, 20-Aug-2021 16:32:24 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
"CONSENT\u003dPENDING+569; expires\u003dFri, 01-Jan-2038 00:00:00 GMT; path\u003d/; domain\u003d.youtube.com"
],
"strict-transport-security": [
"max-age\u003d31536000"
],
"transfer-encoding": [
"chunked"
],
"x-content-type-options": [
"nosniff"
],
"x-frame-options": [
"SAMEORIGIN"
],
"x-spf-response-type": [
"multipart"
],
"x-xss-protection": [
"0"
]
},
"responseBody": "[\r\n{\"page\": \"watch\",\"rootVe\": \"3832\"},\r\n{\"page\": \"watch\",\"preconnect\": [\"https:\\/\\/r3---sn-25ge7nsl.googlevideo.com\\/generate_204\",\"https:\\/\\/r3---sn-25ge7nsl.googlevideo.com\\/generate_204?conn2\"]},\r\n{\"page\": \"watch\",\"playerResponse\": {\"responseContext\":{\"serviceTrackingParams\":[{\"service\":\"GFEEDBACK\",\"params\":[{\"key\":\"is_viewed_live\",\"value\":\"False\"},{\"key\":\"logged_in\",\"value\":\"0\"},{\"key\":\"e\",\"value\":\"24002342,23918597,23986023,23983514,23999478,23744176,23880830,9466593,23934970,23931562,23978419,23891347,23940248,23996625,23974595,23890959,23995022,23991736,23966208,23992809,23970529,23946420,23891344,23804281,23882502,23996751,23884386,23971936,24000882,23944779,23885487,23974884,23994984,23994373,23976578,23998150,23994475,23969934,23880836,23839597,23857949,23968386,23983297,23993730,23987676,1714259\"}]},{\"service\":\"CSI\",\"params\":[{