Compare commits

...

16 Commits

Author SHA1 Message Date
Stypox 7e793c11ae
Empty commit to make JitPack happy
The build for e920ab3f5f succeeded, but for some reason it is considered as failed...
2023-02-08 22:35:26 +01:00
Stypox e920ab3f5f
Merge pull request #1021 from TeamNewPipe/fix/yt-comment-text-hashtag
[YouTube] Fix getting the comment text if the comment contains a hashtag
2023-01-29 21:47:59 +01:00
TobiGr 3f7df9536e [YouTube] Fix getting the comment text if the comment contains a hashtag 2023-01-29 20:33:51 +01:00
Stypox 999fb7f812
Merge pull request #1024 from AudricV/snd_fix-tracks-like-count
[SoundCloud] Fix extraction of tracks like count
2023-01-29 10:52:54 +01:00
Stypox 3519d4c367
Merge pull request #1015 from AudricV/yt_fix-channel-id-rss-feeds
[YouTube] Fix channel ID extraction of YouTube channels RSS feeds
2023-01-29 10:41:38 +01:00
Stypox 9aca710e86
Merge pull request #1013 from Stypox/fix-music-mixes
[YouTube] Now music mixes can be treated as normal mixes
2023-01-29 09:48:51 +01:00
Stypox 76eeabac45
Merge pull request #1020 from TeamNewPipe/fix/yt-subscriber-count
[YouTube] Fix NPE in search when getting channel items without subscriber count
2023-01-29 09:44:22 +01:00
AudricV 676622f6df
[SoundCloud] Fix expectedLikeCountAtLeast tests of SoundcloudStreamExtractorTest test classes
As like count is now returned by the extractor, we need to assert a positive
minimum like count, which is close to the actual value, in order to avoid test
failures due to lower like counts than the ones excepted.
2023-01-29 01:08:02 +01:00
AudricV 2a24d407d5
[SoundCloud] Fix extraction of tracks like count
SoundCloud is using likes_count to return the like count of a track, like it
was the case before they switched to favoritings_count.
2023-01-29 01:00:49 +01:00
Tobi 0e4e6a9bac
Merge pull request #1022 from AudricV/yt_support-live-urls
[YouTube] Support live URLs
2023-01-28 21:52:26 +01:00
AudricV ba24976e41
[YouTube] Add live URLs test and do minor improvements to YoutubeStreamLinkHandlerFactoryTest
- Remove unused imports;
- Replace wildcard imports by single class imports;
- Suppress "HTTP links are not secured" warnings from IDEA IDEs;
- Replace removed video jZViOEv90dI by an existing video, 9Dpqou5cI08 (the
corresponding test has been of course renamed).
2023-01-28 19:36:21 +01:00
AudricV 57f850bc2d
[YouTube] Support live URLs and do minor improvements to YoutubeStreamLinkHandlerFactory
- Move license header at the top;
- Use an unmodifiable set for the subpaths instead of a modifiable list;
- Add missing Nonnull and Nullable annotations;
- Improve exception messages.
2023-01-28 19:36:20 +01:00
AudricV 1f4ed9dce9
[YouTube] Fix channel ID extraction of YouTube channel RSS feeds
The yt:channelId element doesn't provide the channel ID anymore and is empty,
like the id element, so we need now to extract it from the channel URL provided
in two elements: author -> uri and feed -> link.

Also avoid a NullPointerException in getUrl and getName methods.
2023-01-28 11:53:33 +01:00
TobiGr 72573932cf [YouTube] Fix NPE in search when getting channel items without subscriber count 2023-01-24 23:03:45 +01:00
Stypox 5945057227
[YouTube] Add music mix test 2023-01-15 23:30:30 +01:00
Stypox 7293991832
[YouTube] Now music mixes can be treated as normal mixes
Using a playlist extractor on them would result in "Unviewable playlist" errors
2023-01-15 23:28:59 +01:00
16 changed files with 2397 additions and 93 deletions

View File

@ -133,7 +133,7 @@ public class SoundcloudStreamExtractor extends StreamExtractor {
@Override
public long getLikeCount() {
return track.getLong("favoritings_count", -1);
return track.getLong("likes_count", -1);
}
@Nonnull

View File

@ -388,8 +388,7 @@ public final class YoutubeParsingHelper {
* @return Whether given id belongs to a YouTube Mix
*/
public static boolean isYoutubeMixId(@Nonnull final String playlistId) {
return playlistId.startsWith("RD")
&& !isYoutubeMusicMixId(playlistId);
return playlistId.startsWith("RD");
}
/**
@ -846,6 +845,14 @@ public final class YoutubeParsingHelper {
@Nullable
public static String getUrlFromNavigationEndpoint(@Nonnull final JsonObject navigationEndpoint)
throws ParsingException {
if (navigationEndpoint.has("webCommandMetadata")) {
// this case needs to be handled before the browseEndpoint,
// e.g. for hashtags in comments
final JsonObject metadata = navigationEndpoint.getObject("webCommandMetadata");
if (metadata.has("url")) {
return "https://www.youtube.com" + metadata.getString("url");
}
}
if (navigationEndpoint.has("urlEndpoint")) {
String internUrl = navigationEndpoint.getObject("urlEndpoint").getString("url");
if (internUrl.startsWith("https://www.youtube.com/redirect?")) {

View File

@ -110,8 +110,7 @@ public class YoutubeService extends StreamingService {
@Override
public PlaylistExtractor getPlaylistExtractor(final ListLinkHandler linkHandler) {
if (YoutubeParsingHelper.isYoutubeMixId(linkHandler.getId())
&& !YoutubeParsingHelper.isYoutubeMusicMixId(linkHandler.getId())) {
if (YoutubeParsingHelper.isYoutubeMixId(linkHandler.getId())) {
return new YoutubeMixPlaylistExtractor(this, linkHandler);
} else {
return new YoutubePlaylistExtractor(this, linkHandler);

View File

@ -96,8 +96,12 @@ public class YoutubeChannelInfoItemExtractor implements ChannelInfoItemExtractor
}
if (withHandle) {
return Utils.mixedNumberWordToLong(getTextFromObject(
channelInfoItem.getObject("videoCountText")));
if (channelInfoItem.has("videoCountText")) {
return Utils.mixedNumberWordToLong(getTextFromObject(
channelInfoItem.getObject("videoCountText")));
} else {
return -1;
}
}
return Utils.mixedNumberWordToLong(getTextFromObject(

View File

@ -22,6 +22,8 @@ import java.io.IOException;
import javax.annotation.Nonnull;
public class YoutubeFeedExtractor extends FeedExtractor {
private static final String WEBSITE_CHANNEL_BASE_URL = "https://www.youtube.com/channel/";
public YoutubeFeedExtractor(final StreamingService service, final ListLinkHandler linkHandler) {
super(service, linkHandler);
}
@ -57,19 +59,40 @@ public class YoutubeFeedExtractor extends FeedExtractor {
@Nonnull
@Override
public String getId() {
return document.getElementsByTag("yt:channelId").first().text();
return getUrl().replace(WEBSITE_CHANNEL_BASE_URL, "");
}
@Nonnull
@Override
public String getUrl() {
return document.select("feed > author > uri").first().text();
final Element authorUriElement = document.select("feed > author > uri")
.first();
if (authorUriElement != null) {
final String authorUriElementText = authorUriElement.text();
if (!authorUriElementText.equals("")) {
return authorUriElementText;
}
}
final Element linkElement = document.select("feed > link[rel*=alternate]")
.first();
if (linkElement != null) {
return linkElement.attr("href");
}
return "";
}
@Nonnull
@Override
public String getName() {
return document.select("feed > author > name").first().text();
final Element nameElement = document.select("feed > author > name")
.first();
if (nameElement == null) {
return "";
}
return nameElement.text();
}
@Override

View File

@ -1,3 +1,23 @@
/*
* Created by Christian Schabesberger on 02.02.16.
*
* Copyright (C) Christian Schabesberger 2018 <chris.schabesberger@mailbox.org>
* YoutubeStreamLinkHandlerFactory.java is part of NewPipe Extractor.
*
* NewPipe Extractor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe Extractor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe Extractor. If not, see <https://www.gnu.org/licenses/>.
*/
package org.schabi.newpipe.extractor.services.youtube.linkHandler;
import static org.schabi.newpipe.extractor.services.youtube.YoutubeParsingHelper.isHooktubeURL;
@ -15,33 +35,13 @@ import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/*
* Created by Christian Schabesberger on 02.02.16.
*
* Copyright (C) Christian Schabesberger 2018 <chris.schabesberger@mailbox.org>
* YoutubeStreamLinkHandlerFactory.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
public final class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
private static final Pattern YOUTUBE_VIDEO_ID_REGEX_PATTERN
@ -49,7 +49,7 @@ public final class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
private static final YoutubeStreamLinkHandlerFactory INSTANCE
= new YoutubeStreamLinkHandlerFactory();
private static final List<String> SUBPATHS
= Arrays.asList("embed/", "shorts/", "watch/", "v/", "w/");
= List.of("embed/", "live/", "shorts/", "watch/", "v/", "w/");
private YoutubeStreamLinkHandlerFactory() {
}
@ -67,21 +67,24 @@ public final class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
return null;
}
@Nonnull
private static String assertIsId(@Nullable final String id) throws ParsingException {
final String extractedId = extractId(id);
if (extractedId != null) {
return extractedId;
} else {
throw new ParsingException("The given string is not a Youtube-Video-ID");
throw new ParsingException("The given string is not a YouTube video ID");
}
}
@Nonnull
@Override
public String getUrl(final String id) {
return "https://www.youtube.com/watch?v=" + id;
}
@SuppressWarnings("AvoidNestedBlocks")
@Nonnull
@Override
public String getId(final String theUrlString)
throws ParsingException, IllegalArgumentException {
@ -124,14 +127,14 @@ public final class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
if (!Utils.isHTTP(url) || !(isYoutubeURL(url) || isYoutubeServiceURL(url)
|| isHooktubeURL(url) || isInvidiousURL(url) || isY2ubeURL(url))) {
if (host.equalsIgnoreCase("googleads.g.doubleclick.net")) {
throw new FoundAdException("Error found ad: " + urlString);
throw new FoundAdException("Error: found ad: " + urlString);
}
throw new ParsingException("The url is not a Youtube-URL");
throw new ParsingException("The URL is not a YouTube URL");
}
if (YoutubePlaylistLinkHandlerFactory.getInstance().acceptUrl(urlString)) {
throw new ParsingException("Error no suitable url: " + urlString);
throw new ParsingException("Error: no suitable URL: " + urlString);
}
// Using uppercase instead of lowercase, because toLowercase replaces some unicode
@ -154,9 +157,9 @@ public final class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
final URL decodedURL;
try {
decodedURL = Utils.stringToURL("http://www.youtube.com" + uQueryValue);
decodedURL = Utils.stringToURL("https://www.youtube.com" + uQueryValue);
} catch (final MalformedURLException e) {
throw new ParsingException("Error no suitable url: " + urlString);
throw new ParsingException("Error: no suitable URL: " + urlString);
}
final String viewQueryValue = Utils.getQueryValue(decodedURL, "v");
@ -231,7 +234,7 @@ public final class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
}
}
throw new ParsingException("Error no suitable url: " + urlString);
throw new ParsingException("Error: no suitable URL: " + urlString);
}
@Override
@ -246,7 +249,8 @@ public final class YoutubeStreamLinkHandlerFactory extends LinkHandlerFactory {
}
}
private String getIdFromSubpathsInPath(final String path) throws ParsingException {
@Nullable
private String getIdFromSubpathsInPath(@Nonnull final String path) throws ParsingException {
for (final String subpath : SUBPATHS) {
if (path.startsWith(subpath)) {
final String id = path.substring(subpath.length());

View File

@ -22,7 +22,8 @@ import java.util.List;
import javax.annotation.Nullable;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.schabi.newpipe.extractor.ServiceList.SoundCloud;
public class SoundcloudStreamExtractorTest {
@ -64,7 +65,7 @@ public class SoundcloudStreamExtractorTest {
@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 expectedLikeCountAtLeast() { return 600; }
@Override public long expectedDislikeCountAtLeast() { return -1; }
@Override public boolean expectedHasAudioStreams() { return false; }
@Override public boolean expectedHasVideoStreams() { return false; }
@ -127,7 +128,7 @@ public class SoundcloudStreamExtractorTest {
@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 expectedLikeCountAtLeast() { return 7350; }
@Override public long expectedDislikeCountAtLeast() { return -1; }
@Override public boolean expectedHasAudioStreams() { return false; }
@Override public boolean expectedHasVideoStreams() { return false; }
@ -170,7 +171,7 @@ public class SoundcloudStreamExtractorTest {
@Override public long expectedViewCountAtLeast() { return 27000; }
@Nullable @Override public String expectedUploadDate() { return "2019-03-28 13:36:18.000"; }
@Nullable @Override public String expectedTextualUploadDate() { return "2019-03-28 13:36:18"; }
@Override public long expectedLikeCountAtLeast() { return -1; }
@Override public long expectedLikeCountAtLeast() { return 25; }
@Override public long expectedDislikeCountAtLeast() { return -1; }
@Override public boolean expectedHasVideoStreams() { return false; }
@Override public boolean expectedHasSubtitles() { return false; }

View File

@ -23,22 +23,22 @@ import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.Page;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
import org.schabi.newpipe.extractor.services.youtube.extractors.YoutubeMixPlaylistExtractor;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
@SuppressWarnings({"MismatchedQueryAndUpdateOfCollection", "NewClassNamingConvention"})
public class YoutubeMixPlaylistExtractorTest {
private static final String RESOURCE_PATH = DownloaderFactory.RESOURCE_PATH + "services/youtube/extractor/mix/";
private static final Map<String, String> dummyCookie = new HashMap<>();
private static final Map<String, String> dummyCookie = Map.of(YoutubeMixPlaylistExtractor.COOKIE_NAME, "whatever");
private static YoutubeMixPlaylistExtractor extractor;
public static class Mix {
@ -50,7 +50,6 @@ public class YoutubeMixPlaylistExtractorTest {
YoutubeTestsUtils.ensureStateless();
YoutubeParsingHelper.setConsentAccepted(true);
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "mix"));
dummyCookie.put(YoutubeMixPlaylistExtractor.COOKIE_NAME, "whatever");
extractor = (YoutubeMixPlaylistExtractor) YouTube
.getPlaylistExtractor("https://www.youtube.com/watch?v=" + VIDEO_ID
+ "&list=RD" + VIDEO_ID);
@ -135,7 +134,6 @@ public class YoutubeMixPlaylistExtractorTest {
}
public static class MixWithIndex {
private static final String VIDEO_ID = "FAqYW76GLPA";
private static final String VIDEO_TITLE = "Mix ";
private static final int INDEX = 7; // YT starts the index with 1...
@ -146,7 +144,6 @@ public class YoutubeMixPlaylistExtractorTest {
YoutubeTestsUtils.ensureStateless();
YoutubeParsingHelper.setConsentAccepted(true);
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "mixWithIndex"));
dummyCookie.put(YoutubeMixPlaylistExtractor.COOKIE_NAME, "whatever");
extractor = (YoutubeMixPlaylistExtractor) YouTube
.getPlaylistExtractor("https://www.youtube.com/watch?v=" + VIDEO_ID_AT_INDEX
+ "&list=RD" + VIDEO_ID + "&index=" + INDEX);
@ -233,7 +230,6 @@ public class YoutubeMixPlaylistExtractorTest {
YoutubeTestsUtils.ensureStateless();
YoutubeParsingHelper.setConsentAccepted(true);
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "myMix"));
dummyCookie.put(YoutubeMixPlaylistExtractor.COOKIE_NAME, "whatever");
extractor = (YoutubeMixPlaylistExtractor) YouTube
.getPlaylistExtractor("https://www.youtube.com/watch?v=" + VIDEO_ID
+ "&list=RDMM" + VIDEO_ID);
@ -316,7 +312,6 @@ public class YoutubeMixPlaylistExtractorTest {
}
public static class Invalid {
private static final String VIDEO_ID = "QMVCAPd5cwBcg";
@BeforeAll
@ -324,7 +319,6 @@ public class YoutubeMixPlaylistExtractorTest {
YoutubeTestsUtils.ensureStateless();
YoutubeParsingHelper.setConsentAccepted(true);
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "invalid"));
dummyCookie.put(YoutubeMixPlaylistExtractor.COOKIE_NAME, "whatever");
}
@Test
@ -348,7 +342,6 @@ public class YoutubeMixPlaylistExtractorTest {
}
public static class ChannelMix {
private static final String CHANNEL_ID = "UCXuqSBlHAE6Xw-yeJA0Tunw";
private static final String VIDEO_ID_OF_CHANNEL = "mnk6gnOBYIo";
private static final String CHANNEL_TITLE = "Linus Tech Tips";
@ -359,7 +352,6 @@ public class YoutubeMixPlaylistExtractorTest {
YoutubeTestsUtils.ensureStateless();
YoutubeParsingHelper.setConsentAccepted(true);
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "channelMix"));
dummyCookie.put(YoutubeMixPlaylistExtractor.COOKIE_NAME, "whatever");
extractor = (YoutubeMixPlaylistExtractor) YouTube
.getPlaylistExtractor("https://www.youtube.com/watch?v=" + VIDEO_ID_OF_CHANNEL
+ "&list=RDCM" + CHANNEL_ID);
@ -424,7 +416,6 @@ public class YoutubeMixPlaylistExtractorTest {
YoutubeTestsUtils.ensureStateless();
YoutubeParsingHelper.setConsentAccepted(true);
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "genreMix"));
dummyCookie.put(YoutubeMixPlaylistExtractor.COOKIE_NAME, "whatever");
extractor = (YoutubeMixPlaylistExtractor) YouTube
.getPlaylistExtractor("https://www.youtube.com/watch?v=" + VIDEO_ID
+ "&list=RDGMEMYH9CUrFO7CfLJpaD7UR85w");
@ -504,4 +495,93 @@ public class YoutubeMixPlaylistExtractorTest {
assertEquals(PlaylistInfo.PlaylistType.MIX_GENRE, extractor.getPlaylistType());
}
}
public static class Music {
private static final String VIDEO_ID = "dQw4w9WgXcQ";
private static final String MIX_TITLE = "Mix Rick Astley - Never Gonna Give You Up (Official Music Video)";
@BeforeAll
public static void setUp() throws Exception {
YoutubeTestsUtils.ensureStateless();
YoutubeParsingHelper.setConsentAccepted(true);
NewPipe.init(DownloaderFactory.getDownloader(RESOURCE_PATH + "musicMix"));
extractor = (YoutubeMixPlaylistExtractor)
YouTube.getPlaylistExtractor("https://m.youtube.com/watch?v=" + VIDEO_ID
+ "&list=RDAMVM" + VIDEO_ID);
extractor.fetchPage();
}
@Test
void getServiceId() {
assertEquals(YouTube.getServiceId(), extractor.getServiceId());
}
@Test
void getName() throws Exception {
assertEquals(MIX_TITLE, extractor.getName());
}
@Test
void getThumbnailUrl() throws Exception {
final String thumbnailUrl = extractor.getThumbnailUrl();
assertIsSecureUrl(thumbnailUrl);
ExtractorAsserts.assertContains("yt", thumbnailUrl);
ExtractorAsserts.assertContains(VIDEO_ID, thumbnailUrl);
}
@Test
void getInitialPage() throws Exception {
final InfoItemsPage<StreamInfoItem> streams = extractor.getInitialPage();
assertFalse(streams.getItems().isEmpty());
assertTrue(streams.hasNextPage());
}
@Test
void getPage() throws Exception {
final byte[] body = JsonWriter.string(prepareDesktopJsonBuilder(
NewPipe.getPreferredLocalization(), NewPipe.getPreferredContentCountry())
.value("videoId", VIDEO_ID)
.value("playlistId", "RD" + VIDEO_ID)
.value("params", "OAE%3D")
.done())
.getBytes(StandardCharsets.UTF_8);
final InfoItemsPage<StreamInfoItem> streams = extractor.getPage(new Page(
YOUTUBEI_V1_URL + "next?key=" + getKey(), null, null, dummyCookie, body));
assertFalse(streams.getItems().isEmpty());
assertTrue(streams.hasNextPage());
}
@Test
void getContinuations() throws Exception {
InfoItemsPage<StreamInfoItem> streams = extractor.getInitialPage();
final Set<String> urls = new HashSet<>();
// Should work infinitely, but for testing purposes only 3 times
for (int i = 0; i < 3; i++) {
assertTrue(streams.hasNextPage());
assertFalse(streams.getItems().isEmpty());
for (final StreamInfoItem item : streams.getItems()) {
// TODO Duplicates are appearing
// assertFalse(urls.contains(item.getUrl()));
urls.add(item.getUrl());
}
streams = extractor.getPage(streams.getNextPage());
}
assertTrue(streams.hasNextPage());
assertFalse(streams.getItems().isEmpty());
}
@Test
void getStreamCount() throws ParsingException {
assertEquals(ListExtractor.ITEM_COUNT_INFINITE, extractor.getStreamCount());
}
@Test
void getPlaylistType() throws ParsingException {
assertEquals(PlaylistInfo.PlaylistType.MIX_MUSIC, extractor.getPlaylistType());
}
}
}

View File

@ -11,14 +11,14 @@ import org.schabi.newpipe.extractor.exceptions.FoundAdException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YoutubeStreamLinkHandlerFactory;
import java.util.ArrayList;
import java.util.List;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* Test for {@link YoutubeStreamLinkHandlerFactory}
*/
@SuppressWarnings("HttpUrlsUsage")
public class YoutubeStreamLinkHandlerFactoryTest {
private static YoutubeStreamLinkHandlerFactory linkHandler;
@ -53,25 +53,25 @@ public class YoutubeStreamLinkHandlerFactoryTest {
@ParameterizedTest
@ValueSource(strings = {
"https://www.youtube.com/watch?v=jZViOEv90dI",
"https://www.youtube.com/watch?v=jZViOEv90dI&t=100",
"https://WWW.YouTube.com/watch?v=jZViOEv90dI&t=100",
"HTTPS://www.youtube.com/watch?v=jZViOEv90dI&t=100",
"https://youtu.be/jZViOEv90dI?t=9s",
"HTTPS://Youtu.be/jZViOEv90dI?t=9s",
"https://www.youtube.com/embed/jZViOEv90dI",
"https://www.youtube-nocookie.com/embed/jZViOEv90dI",
"http://www.youtube.com/watch?v=jZViOEv90dI",
"http://youtube.com/watch?v=jZViOEv90dI",
"http://youtu.be/jZViOEv90dI?t=9s",
"http://www.youtube.com/embed/jZViOEv90dI",
"http://www.Youtube.com/embed/jZViOEv90dI",
"http://www.youtube-nocookie.com/embed/jZViOEv90dI",
"vnd.youtube://www.youtube.com/watch?v=jZViOEv90dI",
"vnd.youtube:jZViOEv90dI"
"https://www.youtube.com/watch?v=9Dpqou5cI08",
"https://www.youtube.com/watch?v=9Dpqou5cI08&t=100",
"https://WWW.YouTube.com/watch?v=9Dpqou5cI08&t=100",
"HTTPS://www.youtube.com/watch?v=9Dpqou5cI08&t=100",
"https://youtu.be/9Dpqou5cI08?t=9s",
"HTTPS://Youtu.be/9Dpqou5cI08?t=9s",
"https://www.youtube.com/embed/9Dpqou5cI08",
"https://www.youtube-nocookie.com/embed/9Dpqou5cI08",
"http://www.youtube.com/watch?v=9Dpqou5cI08",
"http://youtube.com/watch?v=9Dpqou5cI08",
"http://youtu.be/9Dpqou5cI08?t=9s",
"http://www.youtube.com/embed/9Dpqou5cI08",
"http://www.Youtube.com/embed/9Dpqou5cI08",
"http://www.youtube-nocookie.com/embed/9Dpqou5cI08",
"vnd.youtube://www.youtube.com/watch?v=9Dpqou5cI08",
"vnd.youtube:9Dpqou5cI08"
})
void getId_jZViOEv90dI_fromYt(final String url) throws Exception {
assertEquals("jZViOEv90dI", linkHandler.fromUrl(url).getId());
void getId_9Dpqou5cI08_fromYt(final String url) throws Exception {
assertEquals("9Dpqou5cI08", linkHandler.fromUrl(url).getId());
}
@ParameterizedTest
@ -117,27 +117,28 @@ public class YoutubeStreamLinkHandlerFactoryTest {
@ParameterizedTest
@ValueSource(strings = {
"https://www.youtube.com/watch?v=jZViOEv90dI",
"https://www.youtube.com/watch?v=jZViOEv90dI&t=100",
"https://WWW.YouTube.com/watch?v=jZViOEv90dI&t=100",
"HTTPS://www.youtube.com/watch?v=jZViOEv90dI&t=100",
"https://youtu.be/jZViOEv90dI?t=9s",
"https://www.youtube.com/embed/jZViOEv90dI",
"https://www.youtube-nocookie.com/embed/jZViOEv90dI",
"http://www.youtube.com/watch?v=jZViOEv90dI",
"http://youtu.be/jZViOEv90dI?t=9s",
"http://www.youtube.com/embed/jZViOEv90dI",
"http://www.youtube-nocookie.com/embed/jZViOEv90dI",
"https://www.youtube.com/watch?v=9Dpqou5cI08",
"https://www.youtube.com/watch?v=9Dpqou5cI08&t=100",
"https://WWW.YouTube.com/watch?v=9Dpqou5cI08&t=100",
"HTTPS://www.youtube.com/watch?v=9Dpqou5cI08&t=100",
"https://youtu.be/9Dpqou5cI08?t=9s",
"https://www.youtube.com/embed/9Dpqou5cI08",
"https://www.youtube-nocookie.com/embed/9Dpqou5cI08",
"http://www.youtube.com/watch?v=9Dpqou5cI08",
"http://youtu.be/9Dpqou5cI08?t=9s",
"http://www.youtube.com/embed/9Dpqou5cI08",
"http://www.youtube-nocookie.com/embed/9Dpqou5cI08",
"http://www.youtube.com/attribution_link?a=JdfC0C9V6ZI&u=%2Fwatch%3Fv%3DEhxJLojIE_o%26feature%3Dshare",
"vnd.youtube://www.youtube.com/watch?v=jZViOEv90dI",
"vnd.youtube:jZViOEv90dI",
"vnd.youtube.launch:jZViOEv90dI",
"vnd.youtube://www.youtube.com/watch?v=9Dpqou5cI08",
"vnd.youtube:9Dpqou5cI08",
"vnd.youtube.launch:9Dpqou5cI08",
"https://music.youtube.com/watch?v=O0EDx9WAelc",
"https://www.youtube.com/shorts/IOS2fqxwYbA",
"http://www.youtube.com/shorts/IOS2fqxwYbA",
"http://www.youtube.com/v/IOS2fqxwYbA",
"https://www.youtube.com/w/IOS2fqxwYbA",
"https://www.youtube.com/watch/IOS2fqxwYbA"
"https://www.youtube.com/watch/IOS2fqxwYbA",
"https://www.youtube.com/live/rUxyKA_-grg"
})
void acceptYtUrl(final String url) throws ParsingException {
assertTrue(linkHandler.acceptUrl(url));

View File

@ -0,0 +1,82 @@
{
"request": {
"httpMethod": "GET",
"url": "https://www.youtube.com/sw.js",
"headers": {
"Origin": [
"https://www.youtube.com"
],
"Referer": [
"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,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": [
"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, 15 Jan 2023 22:30:06 GMT"
],
"expires": [
"Sun, 15 Jan 2023 22:30:06 GMT"
],
"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-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\u003d8TYEubKDjFA; Domain\u003d.youtube.com; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
"VISITOR_INFO1_LIVE\u003d; Domain\u003d.youtube.com; Expires\u003dMon, 20-Apr-2020 22:30:06 GMT; Path\u003d/; Secure; HttpOnly; SameSite\u003dnone",
"CONSENT\u003dPENDING+835; expires\u003dTue, 14-Jan-2025 22:30:06 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"
}
}