Merge pull request #509 from TiA4f8R/soundcloud-improvements
Add new exceptions to be able to display different error messages in apps
This commit is contained in:
commit
7e6f464407
36 changed files with 1239 additions and 131 deletions
|
@ -0,0 +1,11 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -1,11 +1,11 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
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;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
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);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,15 @@ import org.schabi.newpipe.extractor.MetaInfo;
|
|||
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.AgeRestrictedContentException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
|
||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||
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.ReCaptchaException;
|
||||
import org.schabi.newpipe.extractor.exceptions.YoutubeMusicPremiumContentException;
|
||||
import org.schabi.newpipe.extractor.linkhandler.LinkHandler;
|
||||
import org.schabi.newpipe.extractor.localization.DateWrapper;
|
||||
import org.schabi.newpipe.extractor.localization.Localization;
|
||||
|
@ -71,7 +76,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
//////////////////////////////////////////////////////////////////////////*/
|
||||
|
||||
public static class DeobfuscateException extends ParsingException {
|
||||
DeobfuscateException(String message, Throwable cause) {
|
||||
DeobfuscateException(final String message, final Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
|
@ -94,7 +99,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
@Nullable
|
||||
private List<SubtitlesStream> subtitles = null;
|
||||
|
||||
public YoutubeStreamExtractor(StreamingService service, LinkHandler linkHandler) {
|
||||
public YoutubeStreamExtractor(final StreamingService service, final LinkHandler linkHandler) {
|
||||
super(service, linkHandler);
|
||||
}
|
||||
|
||||
|
@ -110,7 +115,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
|
||||
try {
|
||||
title = getTextFromObject(getVideoPrimaryInfoRenderer().getObject("title"));
|
||||
} catch (ParsingException ignored) {
|
||||
} catch (final ParsingException ignored) {
|
||||
// age-restricted videos cause a ParsingException here
|
||||
}
|
||||
|
||||
|
@ -153,14 +158,14 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
TimeAgoParser timeAgoParser = TimeAgoPatternsManager.getTimeAgoParserFor(Localization.fromLocalizationCode("en"));
|
||||
OffsetDateTime parsedTime = timeAgoParser.parse(time).offsetDateTime();
|
||||
return DateTimeFormatter.ISO_LOCAL_DATE.format(parsedTime);
|
||||
} catch (Exception ignored) {
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
|
||||
try { // Premiered Feb 21, 2020
|
||||
final LocalDate localDate = LocalDate.parse(time,
|
||||
DateTimeFormatter.ofPattern("MMM dd, yyyy", Locale.ENGLISH));
|
||||
return DateTimeFormatter.ISO_LOCAL_DATE.format(localDate);
|
||||
} catch (Exception ignored) {
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,7 +174,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
LocalDate localDate = LocalDate.parse(getTextFromObject(getVideoPrimaryInfoRenderer().getObject("dateText")),
|
||||
DateTimeFormatter.ofPattern("dd MMM yyyy", Locale.ENGLISH));
|
||||
return DateTimeFormatter.ISO_LOCAL_DATE.format(localDate);
|
||||
} catch (Exception ignored) {
|
||||
} catch (final Exception ignored) {
|
||||
}
|
||||
|
||||
throw new ParsingException("Could not get upload date");
|
||||
|
@ -196,7 +201,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
String url = thumbnails.getObject(thumbnails.size() - 1).getString("url");
|
||||
|
||||
return fixThumbnailUrl(url);
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get thumbnail url");
|
||||
}
|
||||
|
||||
|
@ -210,7 +215,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
try {
|
||||
String description = getTextFromObject(getVideoSecondaryInfoRenderer().getObject("description"), true);
|
||||
if (description != null && !description.isEmpty()) return new Description(description, Description.HTML);
|
||||
} catch (ParsingException ignored) {
|
||||
} catch (final ParsingException ignored) {
|
||||
// age-restricted videos cause a ParsingException here
|
||||
}
|
||||
|
||||
|
@ -260,7 +265,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
.getObject("videoDetails")
|
||||
.getString("lengthSeconds");
|
||||
return Long.parseLong(duration);
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
try {
|
||||
String durationMs = playerResponse
|
||||
.getObject("streamingData")
|
||||
|
@ -268,7 +273,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
.getObject(0)
|
||||
.getString("approxDurationMs");
|
||||
return Math.round(Long.parseLong(durationMs) / 1000f);
|
||||
} catch (Exception ignored) {
|
||||
} catch (final Exception ignored) {
|
||||
throw new ParsingException("Could not get duration", e);
|
||||
}
|
||||
}
|
||||
|
@ -300,7 +305,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
try {
|
||||
views = getTextFromObject(getVideoPrimaryInfoRenderer().getObject("viewCount")
|
||||
.getObject("videoViewCountRenderer").getObject("viewCount"));
|
||||
} catch (ParsingException ignored) {
|
||||
} catch (final ParsingException ignored) {
|
||||
// age-restricted videos cause a ParsingException here
|
||||
}
|
||||
|
||||
|
@ -323,17 +328,17 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
try {
|
||||
likesString = getVideoPrimaryInfoRenderer().getObject("sentimentBar")
|
||||
.getObject("sentimentBarRenderer").getString("tooltip").split("/")[0];
|
||||
} catch (NullPointerException e) {
|
||||
//if this kicks in our button has no content and therefore ratings must be disabled
|
||||
} catch (final NullPointerException e) {
|
||||
// if this kicks in our button has no content and therefore ratings must be disabled
|
||||
if (playerResponse.getObject("videoDetails").getBoolean("allowRatings")) {
|
||||
throw new ParsingException("Ratings are enabled even though the like button is missing", e);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return Integer.parseInt(Utils.removeNonDigitCharacters(likesString));
|
||||
} catch (NumberFormatException nfe) {
|
||||
} catch (final NumberFormatException nfe) {
|
||||
throw new ParsingException("Could not parse \"" + likesString + "\" as an Integer", nfe);
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
if (getAgeLimit() == NO_AGE_LIMIT) {
|
||||
throw new ParsingException("Could not get like count", e);
|
||||
}
|
||||
|
@ -349,17 +354,17 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
try {
|
||||
dislikesString = getVideoPrimaryInfoRenderer().getObject("sentimentBar")
|
||||
.getObject("sentimentBarRenderer").getString("tooltip").split("/")[1];
|
||||
} catch (NullPointerException e) {
|
||||
//if this kicks in our button has no content and therefore ratings must be disabled
|
||||
} catch (final NullPointerException e) {
|
||||
// if this kicks in our button has no content and therefore ratings must be disabled
|
||||
if (playerResponse.getObject("videoDetails").getBoolean("allowRatings")) {
|
||||
throw new ParsingException("Ratings are enabled even though the dislike button is missing", e);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
return Integer.parseInt(Utils.removeNonDigitCharacters(dislikesString));
|
||||
} catch (NumberFormatException nfe) {
|
||||
} catch (final NumberFormatException nfe) {
|
||||
throw new ParsingException("Could not parse \"" + dislikesString + "\" as an Integer", nfe);
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
if (getAgeLimit() == NO_AGE_LIMIT) {
|
||||
throw new ParsingException("Could not get dislike count", e);
|
||||
}
|
||||
|
@ -373,16 +378,16 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
assertPageFetched();
|
||||
|
||||
try {
|
||||
String uploaderUrl = getUrlFromNavigationEndpoint(getVideoSecondaryInfoRenderer()
|
||||
final String uploaderUrl = getUrlFromNavigationEndpoint(getVideoSecondaryInfoRenderer()
|
||||
.getObject("owner").getObject("videoOwnerRenderer").getObject("navigationEndpoint"));
|
||||
if (!isNullOrEmpty(uploaderUrl)) {
|
||||
return uploaderUrl;
|
||||
}
|
||||
} catch (ParsingException ignored) {
|
||||
} catch (final ParsingException ignored) {
|
||||
// age-restricted videos cause a ParsingException here
|
||||
}
|
||||
|
||||
String uploaderId = playerResponse.getObject("videoDetails").getString("channelId");
|
||||
final String uploaderId = playerResponse.getObject("videoDetails").getString("channelId");
|
||||
if (!isNullOrEmpty(uploaderId)) {
|
||||
return YoutubeChannelLinkHandlerFactory.getInstance().getUrl("channel/" + uploaderId);
|
||||
}
|
||||
|
@ -400,7 +405,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
try {
|
||||
uploaderName = getTextFromObject(getVideoSecondaryInfoRenderer().getObject("owner")
|
||||
.getObject("videoOwnerRenderer").getObject("title"));
|
||||
} catch (ParsingException ignored) {
|
||||
} catch (final ParsingException ignored) {
|
||||
}
|
||||
|
||||
if (isNullOrEmpty(uploaderName)) {
|
||||
|
@ -430,7 +435,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
try {
|
||||
url = getVideoSecondaryInfoRenderer().getObject("owner").getObject("videoOwnerRenderer")
|
||||
.getObject("thumbnail").getArray("thumbnails").getObject(0).getString("url");
|
||||
} catch (ParsingException ignored) {
|
||||
} catch (final ParsingException ignored) {
|
||||
// age-restricted videos cause a ParsingException here
|
||||
}
|
||||
|
||||
|
@ -478,14 +483,14 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
|
||||
if (!dashManifestUrl.contains("/signature/")) {
|
||||
String obfuscatedSig = Parser.matchGroup1("/s/([a-fA-F0-9\\.]+)", dashManifestUrl);
|
||||
String deobfuscatedSig;
|
||||
final String deobfuscatedSig;
|
||||
|
||||
deobfuscatedSig = deobfuscateSignature(obfuscatedSig);
|
||||
dashManifestUrl = dashManifestUrl.replace("/s/" + obfuscatedSig, "/signature/" + deobfuscatedSig);
|
||||
}
|
||||
|
||||
return dashManifestUrl;
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get dash manifest url", e);
|
||||
}
|
||||
}
|
||||
|
@ -497,7 +502,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
|
||||
try {
|
||||
return playerResponse.getObject("streamingData").getString("hlsManifestUrl");
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get hls manifest url", e);
|
||||
}
|
||||
}
|
||||
|
@ -505,17 +510,17 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
@Override
|
||||
public List<AudioStream> getAudioStreams() throws ExtractionException {
|
||||
assertPageFetched();
|
||||
List<AudioStream> audioStreams = new ArrayList<>();
|
||||
try {
|
||||
for (Map.Entry<String, ItagItem> entry : getItags(ADAPTIVE_FORMATS, ItagItem.ItagType.AUDIO).entrySet()) {
|
||||
ItagItem itag = entry.getValue();
|
||||
final List<AudioStream> audioStreams = new ArrayList<>();
|
||||
|
||||
AudioStream audioStream = new AudioStream(entry.getKey(), itag);
|
||||
try {
|
||||
for (final Map.Entry<String, ItagItem> entry : getItags(ADAPTIVE_FORMATS, ItagItem.ItagType.AUDIO).entrySet()) {
|
||||
final ItagItem itag = entry.getValue();
|
||||
final AudioStream audioStream = new AudioStream(entry.getKey(), itag);
|
||||
if (!Stream.containSimilarStream(audioStream, audioStreams)) {
|
||||
audioStreams.add(audioStream);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get audio streams", e);
|
||||
}
|
||||
|
||||
|
@ -525,17 +530,17 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
@Override
|
||||
public List<VideoStream> getVideoStreams() throws ExtractionException {
|
||||
assertPageFetched();
|
||||
List<VideoStream> videoStreams = new ArrayList<>();
|
||||
try {
|
||||
for (Map.Entry<String, ItagItem> entry : getItags(FORMATS, ItagItem.ItagType.VIDEO).entrySet()) {
|
||||
ItagItem itag = entry.getValue();
|
||||
final List<VideoStream> videoStreams = new ArrayList<>();
|
||||
|
||||
VideoStream videoStream = new VideoStream(entry.getKey(), false, itag);
|
||||
try {
|
||||
for (final Map.Entry<String, ItagItem> entry : getItags(FORMATS, ItagItem.ItagType.VIDEO).entrySet()) {
|
||||
final ItagItem itag = entry.getValue();
|
||||
final VideoStream videoStream = new VideoStream(entry.getKey(), false, itag);
|
||||
if (!Stream.containSimilarStream(videoStream, videoStreams)) {
|
||||
videoStreams.add(videoStream);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get video streams", e);
|
||||
}
|
||||
|
||||
|
@ -545,17 +550,17 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
@Override
|
||||
public List<VideoStream> getVideoOnlyStreams() throws ExtractionException {
|
||||
assertPageFetched();
|
||||
List<VideoStream> videoOnlyStreams = new ArrayList<>();
|
||||
final List<VideoStream> videoOnlyStreams = new ArrayList<>();
|
||||
try {
|
||||
for (Map.Entry<String, ItagItem> entry : getItags(ADAPTIVE_FORMATS, ItagItem.ItagType.VIDEO_ONLY).entrySet()) {
|
||||
ItagItem itag = entry.getValue();
|
||||
for (final Map.Entry<String, ItagItem> entry : getItags(ADAPTIVE_FORMATS, ItagItem.ItagType.VIDEO_ONLY).entrySet()) {
|
||||
final ItagItem itag = entry.getValue();
|
||||
|
||||
VideoStream videoStream = new VideoStream(entry.getKey(), true, itag);
|
||||
final VideoStream videoStream = new VideoStream(entry.getKey(), true, itag);
|
||||
if (!Stream.containSimilarStream(videoStream, videoOnlyStreams)) {
|
||||
videoOnlyStreams.add(videoStream);
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get video only streams", e);
|
||||
}
|
||||
|
||||
|
@ -572,7 +577,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
@Nonnull
|
||||
public List<SubtitlesStream> getSubtitles(final MediaFormat format) throws ParsingException {
|
||||
assertPageFetched();
|
||||
// If the video is age restricted getPlayerConfig will fail
|
||||
// if the video is age restricted getPlayerConfig will fail
|
||||
if (getAgeLimit() != NO_AGE_LIMIT) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
@ -630,7 +635,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
.getArray("contents").getObject(0).getObject("compactVideoRenderer");
|
||||
|
||||
return new YoutubeStreamInfoItemExtractor(videoInfo, getTimeAgoParser());
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get next video", e);
|
||||
}
|
||||
}
|
||||
|
@ -663,7 +668,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
}
|
||||
}
|
||||
return collector;
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new ParsingException("Could not get related videos", e);
|
||||
}
|
||||
}
|
||||
|
@ -677,7 +682,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
return getTextFromObject(initialAjaxJson.getObject(2).getObject("playerResponse")
|
||||
.getObject("playabilityStatus").getObject("errorScreen")
|
||||
.getObject("playerErrorMessageRenderer").getObject("reason"));
|
||||
} catch (ParsingException | NullPointerException e) {
|
||||
} catch (final ParsingException | NullPointerException e) {
|
||||
return null; // no error message
|
||||
}
|
||||
}
|
||||
|
@ -713,16 +718,74 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
}
|
||||
|
||||
playerResponse = initialAjaxJson.getObject(2).getObject("playerResponse", null);
|
||||
// Save the playerResponse from the youtube.com website,
|
||||
// because there can be restrictions on the embedded player.
|
||||
// E.g. if a video is age-restricted, the embedded player's playabilityStatus says,
|
||||
// that the video cannot be played outside of YouTube,
|
||||
// but does not show the original message.
|
||||
JsonObject youtubePlayerResponse = playerResponse;
|
||||
|
||||
if (playerResponse == null || !playerResponse.has("streamingData")) {
|
||||
// try to get player response by fetching video info page
|
||||
fetchVideoInfoPage();
|
||||
}
|
||||
|
||||
final JsonObject playabilityStatus = playerResponse.getObject("playabilityStatus");
|
||||
final String status = playabilityStatus.getString("status");
|
||||
// If status exist, and is not "OK", throw a ContentNotAvailableException with the reason.
|
||||
if (status != null && !status.toLowerCase().equals("ok")) {
|
||||
if (playerResponse == null && youtubePlayerResponse == null) {
|
||||
throw new ExtractionException("Could not get playerResponse");
|
||||
} else if (youtubePlayerResponse == null) {
|
||||
youtubePlayerResponse = playerResponse;
|
||||
}
|
||||
|
||||
JsonObject playabilityStatus = (playerResponse == null ? youtubePlayerResponse : playerResponse)
|
||||
.getObject("playabilityStatus");
|
||||
String status = playabilityStatus.getString("status");
|
||||
// If status exist, and is not "OK", throw the specific exception based on error message
|
||||
// or a ContentNotAvailableException with the reason text if it's an unknown reason.
|
||||
if (status != null && !status.equalsIgnoreCase("ok")) {
|
||||
playabilityStatus = youtubePlayerResponse.getObject("playabilityStatus");
|
||||
status = playabilityStatus.getString("status");
|
||||
|
||||
final String reason = playabilityStatus.getString("reason");
|
||||
|
||||
if (status.equalsIgnoreCase("login_required")) {
|
||||
if (reason == null) {
|
||||
final String message = playabilityStatus.getArray("messages").getString(0);
|
||||
if (message != null && message.equals("This is a private video. Please sign in to verify that you may see it.")) {
|
||||
throw new PrivateContentException("This video is private.");
|
||||
}
|
||||
} else if (reason.equals("Sign in to confirm your age")) {
|
||||
// No streams can be fetched, therefore thrown an AgeRestrictedContentException explicitly.
|
||||
throw new AgeRestrictedContentException("This age-restricted video cannot be watched.");
|
||||
}
|
||||
}
|
||||
if (status.equalsIgnoreCase("unplayable")) {
|
||||
if (reason != null) {
|
||||
if (reason.equals("This video is only available to Music Premium members")) {
|
||||
throw new YoutubeMusicPremiumContentException();
|
||||
}
|
||||
if (reason.equals("This video requires payment to watch.")) {
|
||||
throw new PaidContentException("This video is a paid video");
|
||||
}
|
||||
if (reason.equals("Join this channel to get access to members-only content like this video, and other exclusive perks.") ||
|
||||
reason.equals("Join this channel to get access to members-only content like this video and other exclusive perks.")) {
|
||||
throw new PaidContentException("This video is only available for members of the channel of this video");
|
||||
}
|
||||
if (reason.equals("Video unavailable")) {
|
||||
final String detailedErrorMessage = playabilityStatus.getObject("errorScreen")
|
||||
.getObject("playerErrorMessageRenderer")
|
||||
.getObject("subreason")
|
||||
.getArray("runs")
|
||||
.getObject(0)
|
||||
.getString("text");
|
||||
if (detailedErrorMessage != null) {
|
||||
if (detailedErrorMessage.equals("The uploader has not made this video available in your country.")) {
|
||||
throw new GeographicRestrictionException("This video is not available in user's country.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new ContentNotAvailableException("Got error: \"" + reason + "\"");
|
||||
}
|
||||
}
|
||||
|
@ -736,7 +799,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
|
||||
try {
|
||||
playerResponse = JsonParser.object().from(videoInfoPage.get("player_response"));
|
||||
} catch (JsonParserException e) {
|
||||
} catch (final JsonParserException e) {
|
||||
throw new ParsingException(
|
||||
"Could not parse YouTube player response from video info page", e);
|
||||
}
|
||||
|
@ -753,12 +816,12 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
final String assetsPattern = "\"assets\":.+?\"js\":\\s*(\"[^\"]+\")";
|
||||
playerJsUrl = Parser.matchGroup1(assetsPattern, embedPageContent)
|
||||
.replace("\\", "").replace("\"", "");
|
||||
} catch (Parser.RegexException ex) {
|
||||
} catch (final Parser.RegexException ex) {
|
||||
// playerJsUrl is still available in the file, just somewhere else TODO
|
||||
// it is ok not to find it, see how that's handled in getDeobfuscationCode()
|
||||
final Document doc = Jsoup.parse(embedPageContent);
|
||||
final Elements elems = doc.select("script").attr("name", "player_ias/base");
|
||||
for (Element elem : elems) {
|
||||
for (final Element elem : elems) {
|
||||
if (elem.attr("src").contains("base.js")) {
|
||||
playerJsUrl = elem.attr("src");
|
||||
break;
|
||||
|
@ -768,19 +831,18 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
|
||||
// Get embed sts
|
||||
return Parser.matchGroup1("\"sts\"\\s*:\\s*(\\d+)", embedPageContent);
|
||||
} catch (Exception i) {
|
||||
} catch (final Exception i) {
|
||||
// if it fails we simply reply with no sts as then it does not seem to be necessary
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private String getDeobfuscationFuncName(final String playerCode) throws DeobfuscateException {
|
||||
Parser.RegexException exception = null;
|
||||
for (final String regex : REGEXES) {
|
||||
try {
|
||||
return Parser.matchGroup1(regex, playerCode);
|
||||
} catch (Parser.RegexException re) {
|
||||
} catch (final Parser.RegexException re) {
|
||||
if (exception == null) {
|
||||
exception = re;
|
||||
}
|
||||
|
@ -812,9 +874,9 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
"function " + DEOBFUSCATION_FUNC_NAME + "(a){return " + deobfuscationFunctionName + "(a);}";
|
||||
|
||||
return helperObject + deobfuscateFunction + callerFunction;
|
||||
} catch (IOException ioe) {
|
||||
} catch (final IOException ioe) {
|
||||
throw new DeobfuscateException("Could not load deobfuscate function", ioe);
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new DeobfuscateException("Could not parse deobfuscate function ", e);
|
||||
}
|
||||
}
|
||||
|
@ -855,7 +917,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
context.evaluateString(scope, deobfuscationCode, "deobfuscationCode", 1, null);
|
||||
final Function deobfuscateFunc = (Function) scope.get(DEOBFUSCATION_FUNC_NAME, scope);
|
||||
result = deobfuscateFunc.call(context, scope, scope, new Object[]{obfuscatedSig});
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new DeobfuscateException("Could not get deobfuscate signature", e);
|
||||
} finally {
|
||||
Context.exit();
|
||||
|
@ -874,7 +936,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
.getObject("results").getObject("results").getArray("contents");
|
||||
JsonObject videoPrimaryInfoRenderer = null;
|
||||
|
||||
for (Object content : contents) {
|
||||
for (final Object content : contents) {
|
||||
if (((JsonObject) content).has("videoPrimaryInfoRenderer")) {
|
||||
videoPrimaryInfoRenderer = ((JsonObject) content).getObject("videoPrimaryInfoRenderer");
|
||||
break;
|
||||
|
@ -896,7 +958,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
.getObject("results").getObject("results").getArray("contents");
|
||||
JsonObject videoSecondaryInfoRenderer = null;
|
||||
|
||||
for (Object content : contents) {
|
||||
for (final Object content : contents) {
|
||||
if (((JsonObject) content).has("videoSecondaryInfoRenderer")) {
|
||||
videoSecondaryInfoRenderer = ((JsonObject) content).getObject("videoSecondaryInfoRenderer");
|
||||
break;
|
||||
|
@ -975,7 +1037,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
|
||||
urlAndItags.put(streamUrl, itagItem);
|
||||
}
|
||||
} catch (UnsupportedEncodingException ignored) {
|
||||
} catch (final UnsupportedEncodingException ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1032,7 +1094,7 @@ public class YoutubeStreamExtractor extends StreamExtractor {
|
|||
}
|
||||
result.trimToSize();
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new ExtractionException(e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,14 +1,20 @@
|
|||
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 {
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
{
|
||||
"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\":[{\"key\":\"c\",\"value\":\"WEB\"},{\"key\":\"cver\",\"value\":\"2.20200214.04.00\"},{\"key\":\"yt_li\",\"value\":\"0\"},{\"key\":\"GetPlayer_rid\",\"value\":\"0xbf3eb825a2665b63\"}]},{\"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\":\"LOGIN_REQUIRED\",\"messages\":[\"This is a private video. Please sign in to verify that you may see it.\"],\"errorScreen\":{\"playerErrorMessageRenderer\":{\"subreason\":{\"simpleText\":\"Sign in if you\u0027ve been granted access to this video\"},\"reason\":{\"simpleText\":\"Private video\"},\"proceedButton\":{\"buttonRenderer\":{\"style\":\"STYLE_OVERLAY\",\"size\":\"SIZE_DEFAULT\",\"isDisabled\":false,\"text\":{\"simpleText\":\"Sign in\"},\"navigationEndpoint\":{\"clickTrackingParams\":\"CAEQ8FsiEwjlysbXs_vuAhUVGPEFHZMpD64\u003d\",\"commandMetadata\":{\"webCommandMetadata\":{\"url\":\"https://accounts.google.com/ServiceLogin?service\u003dyoutube\\u0026uilel\u003d3\\u0026passive\u003dtrue\\u0026continue\u003dhttps%3A%2F%2Fwww.youtube.com%2Fsignin%3Faction_handle_signin%3Dtrue%26app%3Ddesktop%26hl%3Den-GB%26next%3D%252Fwatch%253Fv%253D8VajtrESJzA\\u0026hl\u003den-GB\",\"webPageType\":\"WEB_PAGE_TYPE_UNKNOWN\",\"rootVe\":83769}},\"signInEndpoint\":{\"nextEndpoint\":{\"clickTrackingParams\":\"CAEQ8FsiEwjlysbXs_vuAhUVGPEFHZMpD64\u003d\",\"commandMetadata\":{\"webCommandMetadata\":{\"url\":\"/watch?v\u003d8VajtrESJzA\",\"webPageType\":\"WEB_PAGE_TYPE_UNKNOWN\",\"rootVe\":83769}},\"urlEndpoint\":{\"url\":\"/watch?v\u003d8VajtrESJzA\"}}}},\"trackingParams\":\"CAEQ8FsiEwjlysbXs_vuAhUVGPEFHZMpD64\u003d\"}},\"thumbnail\":{\"thumbnails\":[{\"url\":\"//s.ytimg.com/yts/img/meh7-vflGevej7.png\",\"width\":140,\"height\":100}]},\"icon\":{\"iconType\":\"ERROR_OUTLINE\"}}},\"contextParams\":\"Q0FBU0FnZ0E\u003d\"},\"trackingParams\":\"CAAQu2kiEwjlysbXs_vuAhUVGPEFHZMpD64\u003d\"}},\r\n{\"page\": \"watch\",\"response\": {\"responseContext\":{\"webResponseContextExtensionData\":{\"ytConfigData\":{\"visitorData\":\"CgthLTJmajc4YkFObyiYlsqBBg%3D%3D\",\"rootVisualElementType\":3832}}}},\"xsrf_token\": \"QUFFLUhqbXBCdnJkelFzdnNHaEpnUEZObGNvbk5JSTNJd3xBQ3Jtc0tuSDlkZHJYcExYTkt6TVplTDU1cUQwUUgzUThPOVhPYXVPM2N1bDJmYV9weVNpdDdNVUd4cnNCX1VTU0NXcFRQTGdCWk1aNW5FM25nWVJORGs3S0hUSVdDV3NFcHRpNF91THJpN1N2QWp6VkNqSTdLOA\\u003d\\u003d\",\"url\": \"/watch?v\\u003d8VajtrESJzA\",\"endpoint\": {\"clickTrackingParams\":\"IhMI_6bF17P77gIV1h_xBR134Ae2MghleHRlcm5hbA\u003d\u003d\",\"commandMetadata\":{\"webCommandMetadata\":{\"url\":\"/watch?v\u003d8VajtrESJzA\",\"webPageType\":\"WEB_PAGE_TYPE_WATCH\",\"rootVe\":3832}},\"watchEndpoint\":{\"videoId\":\"8VajtrESJzA\"}}},\r\n{\"page\": \"watch\",\"timing\": {\"info\": {\"st\": 0.0 }}}]\r\n",
|
||||
"latestUrl": "https://www.youtube.com/watch?v\u003d8VajtrESJzA\u0026pbj\u003d1"
|
||||
}
|
||||
}
|
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/watch?v\u003ddon-t-exist\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:25 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:25 GMT; Path\u003d/; Secure; HttpOnly",
|
||||
"YSC\u003dZYtZ6I2oRtc; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"VISITOR_INFO1_LIVE\u003dlGs7Hq6Bd-s; Domain\u003d.youtube.com; Expires\u003dFri, 20-Aug-2021 16:32:25 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
|
||||
"CONSENT\u003dPENDING+377; 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-25ge7nzs.googlevideo.com\\/generate_204\",\"https:\\/\\/r3---sn-25ge7nzs.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\":\"23987364,23891344,23999475,23987908,23884386,23966208,23970529,23983515,23944779,23891347,23890959,23987676,23974595,23994373,9407157,23744176,24002218,23839597,23999565,24000882,23998150,23992808,1714242,23994368,23735348,23918597,23984880,23968386,23986016,23996751,24306355,23934970,23821390,23804281,23882502,23857950,23969934,23946420,23988063,23994474,23971936,23976578,23991736,23996625\"}]},{\"service\":\"CSI\",\"params\":[{\"key\":\"c\",\"value\":\"WEB\"},{\"key\":\"cver\",\"value\":\"2.20200214.04.00\"},{\"key\":\"yt_li\",\"value\":\"0\"},{\"key\":\"GetPlayer_rid\",\"value\":\"0xa53847d070bf2c39\"}]},{\"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\":\"CAAQu2kiEwjdxOPXs_vuAhVbIEwKHbXzD7Y\u003d\"}},\r\n{\"page\": \"watch\",\"response\": {\"responseContext\":{\"webResponseContextExtensionData\":{\"ytConfigData\":{\"visitorData\":\"CgtsR3M3SHE2QmQtcyiZlsqBBg%3D%3D\",\"rootVisualElementType\":3832}}}},\"xsrf_token\": \"QUFFLUhqbTNNcmg1NEltaHZEODVYZmZqNmI4RGpheDVxUXxBQ3Jtc0tuMzBYdHEzM096ZGJ1ZXUtSFpRRWNlTk0xakhJM2xIZHJqSkFNUkpHd2tnRk1WSXVDNFFpeXRiWWJ1NHk3T2xvalFBZzBTQjZETWkxZFotYUpyZkhnNjUtcjIxbVo0aFVxdC1XZmhYblpHRnJKS3VqSQ\\u003d\\u003d\",\"url\": \"/watch?v\\u003ddon-t-exist\",\"endpoint\": {\"clickTrackingParams\":\"IhMIvebi17P77gIVXzTxBR2cKA8lMghleHRlcm5hbA\u003d\u003d\",\"commandMetadata\":{\"webCommandMetadata\":{\"url\":\"/watch?v\u003ddon-t-exist\",\"webPageType\":\"WEB_PAGE_TYPE_WATCH\",\"rootVe\":3832}},\"watchEndpoint\":{\"videoId\":\"don-t-exist\"}}},\r\n{\"page\": \"watch\",\"timing\": {\"info\": {\"st\": 0.0 }}}]\r\n",
|
||||
"latestUrl": "https://www.youtube.com/watch?v\u003ddon-t-exist\u0026pbj\u003d1"
|
||||
}
|
||||
}
|
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
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
Loading…
Reference in a new issue