[PeerTube] playlist support & refactoring

This commit is contained in:
bopol 2020-04-09 17:51:51 +02:00
parent f3913e241e
commit 5bab9d9fc0
12 changed files with 189 additions and 198 deletions

View File

@ -4,6 +4,7 @@ import com.grack.nanojson.JsonObject;
import org.jsoup.helper.StringUtil;
import org.schabi.newpipe.extractor.exceptions.ContentNotAvailableException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.utils.Parser;
import java.text.ParseException;
import java.text.SimpleDateFormat;
@ -13,6 +14,11 @@ import java.util.TimeZone;
public class PeertubeParsingHelper {
public static final String START_KEY = "start";
public static final String COUNT_KEY = "count";
public static final int ITEMS_PER_PAGE = 12;
public static final String START_PATTERN = "start=(\\d*)";
private PeertubeParsingHelper() {
}
@ -38,4 +44,26 @@ public class PeertubeParsingHelper {
return uploadDate;
}
public static String getNextPageUrl(String prevPageUrl, long total) {
String prevStart;
try {
prevStart = Parser.matchGroup1(START_PATTERN, prevPageUrl);
} catch (Parser.RegexException e) {
return "";
}
if (StringUtil.isBlank(prevStart)) return "";
long nextStart = 0;
try {
nextStart = Long.parseLong(prevStart) + ITEMS_PER_PAGE;
} catch (NumberFormatException e) {
return "";
}
if (nextStart >= total) {
return "";
} else {
return prevPageUrl.replace(START_KEY + "=" + prevStart, START_KEY + "=" + nextStart);
}
}
}

View File

@ -44,8 +44,7 @@ public class PeertubeService extends StreamingService {
@Override
public ListLinkHandlerFactory getPlaylistLHFactory() {
// TODO Auto-generated method stub
return null;
return PeertubePlaylistLinkHandlerFactory.getInstance();
}
@Override
@ -70,7 +69,6 @@ public class PeertubeService extends StreamingService {
@Override
public SubscriptionExtractor getSubscriptionExtractor() {
// TODO Auto-generated method stub
return null;
}
@ -88,8 +86,7 @@ public class PeertubeService extends StreamingService {
@Override
public PlaylistExtractor getPlaylistExtractor(ListLinkHandler linkHandler)
throws ExtractionException {
// TODO Auto-generated method stub
return null;
return new PeertubePlaylistExtractor(this, linkHandler);
}
@Override

View File

@ -16,17 +16,12 @@ import org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Parser.RegexException;
import java.io.IOException;
public class PeertubeAccountExtractor extends ChannelExtractor {
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
private static final String START_KEY = "start";
private static final String COUNT_KEY = "count";
private static final int ITEMS_PER_PAGE = 12;
private static final String START_PATTERN = "start=(\\d*)";
public class PeertubeAccountExtractor extends ChannelExtractor {
private InfoItemsPage<StreamInfoItem> initPage;
private long total;
@ -135,36 +130,12 @@ public class PeertubeAccountExtractor extends ChannelExtractor {
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
if (json != null) {
PeertubeParsingHelper.validate(json);
Number number = JsonUtils.getNumber(json, "total");
if (number != null) this.total = number.longValue();
total = JsonUtils.getNumber(json, "total").longValue();
collectStreamsFrom(collector, json, pageUrl);
} else {
throw new ExtractionException("Unable to get PeerTube kiosk info");
}
return new InfoItemsPage<>(collector, getNextPageUrl(pageUrl));
}
private String getNextPageUrl(String prevPageUrl) {
String prevStart;
try {
prevStart = Parser.matchGroup1(START_PATTERN, prevPageUrl);
} catch (RegexException e) {
return "";
}
if (StringUtil.isBlank(prevStart)) return "";
long nextStart = 0;
try {
nextStart = Long.valueOf(prevStart) + ITEMS_PER_PAGE;
} catch (NumberFormatException e) {
return "";
}
if (nextStart >= total) {
return "";
} else {
return prevPageUrl.replace(START_KEY + "=" + prevStart, START_KEY + "=" + String.valueOf(nextStart));
}
return new InfoItemsPage<>(collector, PeertubeParsingHelper.getNextPageUrl(pageUrl, total));
}
@Override

View File

@ -21,12 +21,9 @@ import org.schabi.newpipe.extractor.utils.Parser.RegexException;
import java.io.IOException;
public class PeertubeChannelExtractor extends ChannelExtractor {
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
private static final String START_KEY = "start";
private static final String COUNT_KEY = "count";
private static final int ITEMS_PER_PAGE = 12;
private static final String START_PATTERN = "start=(\\d*)";
public class PeertubeChannelExtractor extends ChannelExtractor {
private InfoItemsPage<StreamInfoItem> initPage;
private long total;
@ -141,36 +138,12 @@ public class PeertubeChannelExtractor extends ChannelExtractor {
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
if (json != null) {
PeertubeParsingHelper.validate(json);
Number number = JsonUtils.getNumber(json, "total");
if (number != null) this.total = number.longValue();
this.total = JsonUtils.getNumber(json, "total").longValue();
collectStreamsFrom(collector, json, pageUrl);
} else {
throw new ExtractionException("Unable to get PeerTube kiosk info");
}
return new InfoItemsPage<>(collector, getNextPageUrl(pageUrl));
}
private String getNextPageUrl(String prevPageUrl) {
String prevStart;
try {
prevStart = Parser.matchGroup1(START_PATTERN, prevPageUrl);
} catch (RegexException e) {
return "";
}
if (StringUtil.isBlank(prevStart)) return "";
long nextStart = 0;
try {
nextStart = Long.valueOf(prevStart) + ITEMS_PER_PAGE;
} catch (NumberFormatException e) {
return "";
}
if (nextStart >= total) {
return "";
} else {
return prevPageUrl.replace(START_KEY + "=" + prevStart, START_KEY + "=" + String.valueOf(nextStart));
}
return new InfoItemsPage<>(collector, PeertubeParsingHelper.getNextPageUrl(pageUrl, total));
}
@Override
@ -182,8 +155,7 @@ public class PeertubeChannelExtractor extends ChannelExtractor {
throw new ExtractionException("Unable to extract PeerTube channel data");
}
String pageUrl = getUrl() + "/videos?" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE;
this.initPage = getPage(pageUrl);
this.initPage = getPage(getUrl() + "/videos?" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE);
}
private void setInitialData(String responseBody) throws ExtractionException {

View File

@ -13,18 +13,16 @@ import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper;
import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Parser.RegexException;
import java.io.IOException;
public class PeertubeCommentsExtractor extends CommentsExtractor {
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
private static final String START_KEY = "start";
private static final String COUNT_KEY = "count";
private static final int ITEMS_PER_PAGE = 12;
private static final String START_PATTERN = "start=(\\d*)";
public class PeertubeCommentsExtractor extends CommentsExtractor {
private InfoItemsPage<CommentsInfoItem> initPage;
private long total;
@ -83,35 +81,12 @@ public class PeertubeCommentsExtractor extends CommentsExtractor {
} else {
throw new ExtractionException("Unable to get peertube comments info");
}
return new InfoItemsPage<>(collector, getNextPageUrl(pageUrl));
return new InfoItemsPage<>(collector, PeertubeParsingHelper.getNextPageUrl(pageUrl, total));
}
@Override
public void onFetchPage(Downloader downloader) throws IOException, ExtractionException {
String pageUrl = getUrl() + "?" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE;
this.initPage = getPage(pageUrl);
}
private String getNextPageUrl(String prevPageUrl) {
String prevStart;
try {
prevStart = Parser.matchGroup1(START_PATTERN, prevPageUrl);
} catch (RegexException e) {
return "";
}
if (StringUtil.isBlank(prevStart)) return "";
long nextStart = 0;
try {
nextStart = Long.valueOf(prevStart) + ITEMS_PER_PAGE;
} catch (NumberFormatException e) {
return "";
}
if (nextStart >= total) {
return "";
} else {
return prevPageUrl.replace(START_KEY + "=" + prevStart, START_KEY + "=" + String.valueOf(nextStart));
}
this.initPage = getPage(getUrl() + "?" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE);
}
}

View File

@ -1,85 +1,117 @@
package org.schabi.newpipe.extractor.services.peertube.extractors;
import com.grack.nanojson.JsonArray;
import com.grack.nanojson.JsonObject;
import com.grack.nanojson.JsonParser;
import com.grack.nanojson.JsonParserException;
import org.schabi.newpipe.extractor.StreamingService;
import org.schabi.newpipe.extractor.downloader.Downloader;
import org.schabi.newpipe.extractor.downloader.Response;
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.playlist.PlaylistExtractor;
import org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.utils.JsonUtils;
import javax.annotation.Nonnull;
import java.io.IOException;
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
public class PeertubePlaylistExtractor extends PlaylistExtractor {
private JsonObject playlistInfo;
private JsonObject playlistVideos;
private String initialPageUrl;
private long total;
public PeertubePlaylistExtractor(StreamingService service, ListLinkHandler linkHandler) {
super(service, linkHandler);
// TODO Auto-generated constructor stub
}
@Override
public String getThumbnailUrl() throws ParsingException {
// TODO Auto-generated method stub
return null;
return getBaseUrl() + playlistInfo.getString("thumbnailPath");
}
@Override
public String getBannerUrl() throws ParsingException {
// TODO Auto-generated method stub
return null;
}
@Override
public String getUploaderUrl() throws ParsingException {
// TODO Auto-generated method stub
return null;
return playlistInfo.getObject("ownerAccount").getString("url");
}
@Override
public String getUploaderName() throws ParsingException {
// TODO Auto-generated method stub
return null;
return playlistInfo.getObject("ownerAccount").getString("displayName");
}
@Override
public String getUploaderAvatarUrl() throws ParsingException {
// TODO Auto-generated method stub
return null;
return getBaseUrl() + playlistInfo.getObject("ownerAccount").getObject("avatar").getString("path");
}
@Override
public long getStreamCount() throws ParsingException {
// TODO Auto-generated method stub
return 0;
return playlistInfo.getNumber("videosLength").longValue();
}
@Nonnull
@Override
public InfoItemsPage<StreamInfoItem> getInitialPage() throws IOException, ExtractionException {
// TODO Auto-generated method stub
return null;
return getPage(initialPageUrl);
}
@Override
public String getNextPageUrl() throws IOException, ExtractionException {
// TODO Auto-generated method stub
return null;
return PeertubeParsingHelper.getNextPageUrl(initialPageUrl, total);
}
@Override
public InfoItemsPage<StreamInfoItem> getPage(String pageUrl) throws IOException, ExtractionException {
// TODO Auto-generated method stub
return null;
Response response = getDownloader().get(pageUrl);
try {
playlistVideos = JsonParser.object().from(response.responseBody());
} catch (JsonParserException jpe) {
throw new ExtractionException("Could not parse json", jpe);
}
PeertubeParsingHelper.validate(playlistVideos);
this.total = JsonUtils.getNumber(playlistVideos, "total").longValue();
StreamInfoItemsCollector collector = new StreamInfoItemsCollector(getServiceId());
JsonArray videos = playlistVideos.getArray("data");
for (Object o : videos) {
JsonObject video = ((JsonObject) o).getObject("video");
collector.commit(new PeertubeStreamInfoItemExtractor(video, getBaseUrl()));
}
return new InfoItemsPage<>(collector, PeertubeParsingHelper.getNextPageUrl(pageUrl, total));
}
@Override
public void onFetchPage(Downloader downloader) throws IOException, ExtractionException {
public void onFetchPage(@Nonnull Downloader downloader) throws IOException, ExtractionException {
Response response = downloader.get(getUrl());
try {
playlistInfo = JsonParser.object().from(response.responseBody());
} catch (JsonParserException jpe) {
throw new ExtractionException("Could not parse json", jpe);
}
PeertubeParsingHelper.validate(playlistInfo);
initialPageUrl = getUrl() + "/videos?" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE;
}
@Nonnull
@Override
public String getName() throws ParsingException {
// TODO Auto-generated method stub
return null;
return playlistInfo.getString("displayName");
}
}

View File

@ -15,18 +15,16 @@ import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.linkhandler.SearchQueryHandler;
import org.schabi.newpipe.extractor.search.InfoItemsSearchCollector;
import org.schabi.newpipe.extractor.search.SearchExtractor;
import org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper;
import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Parser.RegexException;
import java.io.IOException;
public class PeertubeSearchExtractor extends SearchExtractor {
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
private static final String START_KEY = "start";
private static final String COUNT_KEY = "count";
private static final int ITEMS_PER_PAGE = 12;
private static final String START_PATTERN = "start=(\\d*)";
public class PeertubeSearchExtractor extends SearchExtractor {
private InfoItemsPage<InfoItem> initPage;
private long total;
@ -88,9 +86,8 @@ public class PeertubeSearchExtractor extends SearchExtractor {
}
if (json != null) {
Number number = JsonUtils.getNumber(json, "total");
if (number != null) this.total = number.longValue();
return new InfoItemsPage<>(collectStreamsFrom(json), getNextPageUrl(pageUrl));
total = JsonUtils.getNumber(json, "total").longValue();
return new InfoItemsPage<>(collectStreamsFrom(json), PeertubeParsingHelper.getNextPageUrl(pageUrl, total));
} else {
throw new ExtractionException("Unable to get peertube search info");
}
@ -98,31 +95,7 @@ public class PeertubeSearchExtractor extends SearchExtractor {
@Override
public void onFetchPage(Downloader downloader) throws IOException, ExtractionException {
String pageUrl = getUrl() + "&" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE;
this.initPage = getPage(pageUrl);
initPage = getPage(getUrl() + "&" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE);
}
private String getNextPageUrl(String prevPageUrl) {
String prevStart;
try {
prevStart = Parser.matchGroup1(START_PATTERN, prevPageUrl);
} catch (RegexException e) {
return "";
}
if (StringUtil.isBlank(prevStart)) return "";
long nextStart = 0;
try {
nextStart = Long.valueOf(prevStart) + ITEMS_PER_PAGE;
} catch (NumberFormatException e) {
return "";
}
if (nextStart >= total) {
return "";
} else {
return prevPageUrl.replace(START_KEY + "=" + prevStart, START_KEY + "=" + String.valueOf(nextStart));
}
}
}

View File

@ -11,20 +11,16 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.kiosk.KioskExtractor;
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
import org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper;
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
import org.schabi.newpipe.extractor.stream.StreamInfoItemsCollector;
import org.schabi.newpipe.extractor.utils.JsonUtils;
import org.schabi.newpipe.extractor.utils.Parser;
import org.schabi.newpipe.extractor.utils.Parser.RegexException;
import java.io.IOException;
public class PeertubeTrendingExtractor extends KioskExtractor<StreamInfoItem> {
import static org.schabi.newpipe.extractor.services.peertube.PeertubeParsingHelper.*;
private static final String START_KEY = "start";
private static final String COUNT_KEY = "count";
private static final int ITEMS_PER_PAGE = 12;
private static final String START_PATTERN = "start=(\\d*)";
public class PeertubeTrendingExtractor extends KioskExtractor<StreamInfoItem> {
private InfoItemsPage<StreamInfoItem> initPage;
private long total;
@ -89,35 +85,12 @@ public class PeertubeTrendingExtractor extends KioskExtractor<StreamInfoItem> {
} else {
throw new ExtractionException("Unable to get peertube kiosk info");
}
return new InfoItemsPage<>(collector, getNextPageUrl(pageUrl));
return new InfoItemsPage<>(collector, PeertubeParsingHelper.getNextPageUrl(pageUrl, total));
}
@Override
public void onFetchPage(Downloader downloader) throws IOException, ExtractionException {
String pageUrl = getUrl() + "&" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE;
this.initPage = getPage(pageUrl);
}
private String getNextPageUrl(String prevPageUrl) {
String prevStart;
try {
prevStart = Parser.matchGroup1(START_PATTERN, prevPageUrl);
} catch (RegexException e) {
return "";
}
if (StringUtil.isBlank(prevStart)) return "";
long nextStart = 0;
try {
nextStart = Long.valueOf(prevStart) + ITEMS_PER_PAGE;
} catch (NumberFormatException e) {
return "";
}
if (nextStart >= total) {
return "";
} else {
return prevPageUrl.replace(START_KEY + "=" + prevStart, START_KEY + "=" + String.valueOf(nextStart));
}
this.initPage = getPage(getUrl() + "&" + START_KEY + "=0&" + COUNT_KEY + "=" + ITEMS_PER_PAGE);
}
}

View File

@ -11,8 +11,7 @@ import java.util.List;
public class PeertubePlaylistLinkHandlerFactory extends ListLinkHandlerFactory {
private static final PeertubePlaylistLinkHandlerFactory instance = new PeertubePlaylistLinkHandlerFactory();
private static final String ID_PATTERN = "/video-channels/([^/?&#]*)";
private static final String VIDEO_CHANNELS_ENDPOINT = "/api/v1/video-channels/";
private static final String ID_PATTERN = "/videos/watch/playlist/([^/?&#]*)";
public static PeertubePlaylistLinkHandlerFactory getInstance() {
return instance;
@ -26,7 +25,7 @@ public class PeertubePlaylistLinkHandlerFactory extends ListLinkHandlerFactory {
@Override
public String getUrl(String id, List<String> contentFilters, String sortFilter, String baseUrl) {
return baseUrl + VIDEO_CHANNELS_ENDPOINT + id;
return baseUrl + "/api/v1/video-playlists/" + id;
}
@Override
@ -34,9 +33,13 @@ public class PeertubePlaylistLinkHandlerFactory extends ListLinkHandlerFactory {
return Parser.matchGroup1(ID_PATTERN, url);
}
@Override
public boolean onAcceptUrl(final String url) {
return url.contains("/video-channels/");
try {
getId(url);
return true;
} catch (ParsingException e) {
return false;
}
}
}

View File

@ -37,6 +37,12 @@ public class PeertubeStreamLinkHandlerFactory extends LinkHandlerFactory {
@Override
public boolean onAcceptUrl(final String url) throws FoundAdException {
return url.contains("/videos/");
if (url.contains("playlist")) return false;
try {
getId(url);
return true;
} catch (ParsingException e) {
return false;
}
}
}

View File

@ -0,0 +1,56 @@
package org.schabi.newpipe.extractor.services.peertube;
import org.junit.BeforeClass;
import org.junit.Test;
import org.schabi.newpipe.DownloaderTestImpl;
import org.schabi.newpipe.extractor.NewPipe;
import org.schabi.newpipe.extractor.exceptions.ParsingException;
import org.schabi.newpipe.extractor.services.peertube.extractors.PeertubePlaylistExtractor;
import static org.junit.Assert.assertEquals;
import static org.schabi.newpipe.extractor.ServiceList.PeerTube;
public class PeertubePlaylistExtractorTest {
public static class Shocking {
private static PeertubePlaylistExtractor extractor;
@BeforeClass
public static void setUp() throws Exception {
NewPipe.init(DownloaderTestImpl.getInstance());
extractor = (PeertubePlaylistExtractor) PeerTube
.getPlaylistExtractor("https://framatube.org/videos/watch/playlist/96b0ee2b-a5a7-4794-8769-58d8ccb79ab7");
extractor.fetchPage();
}
@Test
public void testGetName() throws ParsingException {
assertEquals("Shocking !", extractor.getName());
}
@Test
public void testGetThumbnailUrl() throws ParsingException {
assertEquals("https://framatube.org/static/thumbnails/playlist-96b0ee2b-a5a7-4794-8769-58d8ccb79ab7.jpg", extractor.getThumbnailUrl());
}
@Test
public void testGetUploaderUrl() throws ParsingException {
assertEquals("https://skeptikon.fr/accounts/metadechoc", extractor.getUploaderUrl());
}
@Test
public void testGetUploaderAvatarUrl() throws ParsingException {
assertEquals("https://framatube.org/lazy-static/avatars/cd0f781d-0287-4be2-94f1-24cd732337b2.jpg", extractor.getUploaderAvatarUrl());
}
@Test
public void testGetUploaderName() throws ParsingException {
assertEquals("Méta de Choc", extractor.getUploaderName());
}
@Test
public void testGetStreamCount() throws ParsingException {
assertEquals(35, extractor.getStreamCount());
}
}
}

View File

@ -25,12 +25,17 @@ public class PeertubePlaylistLinkHandlerFactoryTest {
@Test
public void acceptUrlTest() throws ParsingException {
assertTrue(linkHandler.acceptUrl("https://peertube.mastodon.host/video-channels/b45e84fb-c47f-475b-94f2-718126154d33/videos"));
assertTrue(linkHandler.acceptUrl("https://framatube.org/videos/watch/playlist/d8ca79f9-e4c7-4269-8183-d78ed269c909"));
assertTrue(linkHandler.acceptUrl("https://framatube.org/videos/watch/playlist/d8ca79f9-e4c7-4269-8183-d78ed269c909/videos"));
assertTrue(linkHandler.acceptUrl("https://framatube.org/videos/watch/playlist/dacdc4ef-5160-4846-9b70-a655880da667"));
assertTrue(linkHandler.acceptUrl("https://framatube.org/videos/watch/playlist/96b0ee2b-a5a7-4794-8769-58d8ccb79ab7"));
}
@Test
public void getIdFromUrl() throws ParsingException {
assertEquals("b45e84fb-c47f-475b-94f2-718126154d33", linkHandler.fromUrl("https://peertube.mastodon.host/video-channels/b45e84fb-c47f-475b-94f2-718126154d33").getId());
assertEquals("b45e84fb-c47f-475b-94f2-718126154d33", linkHandler.fromUrl("https://peertube.mastodon.host/video-channels/b45e84fb-c47f-475b-94f2-718126154d33/videos").getId());
assertEquals("d8ca79f9-e4c7-4269-8183-d78ed269c909", linkHandler.getId("https://framatube.org/videos/watch/playlist/d8ca79f9-e4c7-4269-8183-d78ed269c909"));
assertEquals("dacdc4ef-5160-4846-9b70-a655880da667", linkHandler.getId("https://framatube.org/videos/watch/playlist/dacdc4ef-5160-4846-9b70-a655880da667"));
assertEquals("bfc145f5-1be7-48a6-9b9e-4f1967199dad", linkHandler.getId("https://framatube.org/videos/watch/playlist/bfc145f5-1be7-48a6-9b9e-4f1967199dad"));
assertEquals("96b0ee2b-a5a7-4794-8769-58d8ccb79ab7", linkHandler.getId("https://framatube.org/videos/watch/playlist/96b0ee2b-a5a7-4794-8769-58d8ccb79ab7"));
}
}