mirror of
https://github.com/TeamPiped/Piped-Backend.git
synced 2024-08-14 23:51:41 +00:00
Merge pull request #400 from TeamPiped/channel-tabs
Implement support for channel tabs.
This commit is contained in:
commit
8c1a80f8be
20 changed files with 249 additions and 214 deletions
|
@ -135,6 +135,16 @@ public class ServerLauncher extends MultithreadedHttpServerLauncher {
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
return getErrorResponse(e, request.getPath());
|
return getErrorResponse(e, request.getPath());
|
||||||
}
|
}
|
||||||
|
})).map(GET, "/channels/tabs", AsyncServlet.ofBlocking(executor, request -> {
|
||||||
|
try {
|
||||||
|
String nextpage = request.getQueryParameter("nextpage");
|
||||||
|
if (StringUtils.isEmpty(nextpage))
|
||||||
|
return getJsonResponse(ResponseHelper.channelTabResponse(request.getQueryParameter("data")), "public, max-age=3600", true);
|
||||||
|
else
|
||||||
|
return getJsonResponse(ResponseHelper.channelTabPageResponse(request.getQueryParameter("data"), nextpage), "public, max-age=3600", true);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return getErrorResponse(e, request.getPath());
|
||||||
|
}
|
||||||
})).map(GET, "/playlists/:playlistId", AsyncServlet.ofBlocking(executor, request -> {
|
})).map(GET, "/playlists/:playlistId", AsyncServlet.ofBlocking(executor, request -> {
|
||||||
try {
|
try {
|
||||||
var playlistId = request.getPathParameter("playlistId");
|
var playlistId = request.getPathParameter("playlistId");
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package me.kavin.piped.consts;
|
package me.kavin.piped.consts;
|
||||||
|
|
||||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||||
|
import com.fasterxml.jackson.databind.json.JsonMapper;
|
||||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||||
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
|
||||||
import me.kavin.piped.utils.PageMixin;
|
import me.kavin.piped.utils.PageMixin;
|
||||||
|
@ -69,7 +70,9 @@ public class Constants {
|
||||||
|
|
||||||
public static final String VERSION;
|
public static final String VERSION;
|
||||||
|
|
||||||
public static final ObjectMapper mapper = new ObjectMapper().addMixIn(Page.class, PageMixin.class);
|
public static final ObjectMapper mapper = JsonMapper.builder()
|
||||||
|
.addMixIn(Page.class, PageMixin.class)
|
||||||
|
.build();
|
||||||
|
|
||||||
public static final Object2ObjectOpenHashMap<String, String> hibernateProperties = new Object2ObjectOpenHashMap<>();
|
public static final Object2ObjectOpenHashMap<String, String> hibernateProperties = new Object2ObjectOpenHashMap<>();
|
||||||
|
|
||||||
|
|
|
@ -19,8 +19,6 @@ import me.kavin.piped.utils.obj.Channel;
|
||||||
import me.kavin.piped.utils.obj.Playlist;
|
import me.kavin.piped.utils.obj.Playlist;
|
||||||
import me.kavin.piped.utils.obj.*;
|
import me.kavin.piped.utils.obj.*;
|
||||||
import me.kavin.piped.utils.obj.db.*;
|
import me.kavin.piped.utils.obj.db.*;
|
||||||
import me.kavin.piped.utils.obj.search.SearchChannel;
|
|
||||||
import me.kavin.piped.utils.obj.search.SearchPlaylist;
|
|
||||||
import me.kavin.piped.utils.resp.*;
|
import me.kavin.piped.utils.resp.*;
|
||||||
import okhttp3.FormBody;
|
import okhttp3.FormBody;
|
||||||
import okhttp3.Request;
|
import okhttp3.Request;
|
||||||
|
@ -34,6 +32,7 @@ import org.schabi.newpipe.extractor.ListExtractor.InfoItemsPage;
|
||||||
import org.schabi.newpipe.extractor.Page;
|
import org.schabi.newpipe.extractor.Page;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
import org.schabi.newpipe.extractor.channel.ChannelInfo;
|
||||||
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
import org.schabi.newpipe.extractor.channel.ChannelInfoItem;
|
||||||
|
import org.schabi.newpipe.extractor.channel.ChannelTabInfo;
|
||||||
import org.schabi.newpipe.extractor.comments.CommentsInfo;
|
import org.schabi.newpipe.extractor.comments.CommentsInfo;
|
||||||
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
import org.schabi.newpipe.extractor.comments.CommentsInfoItem;
|
||||||
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
import org.schabi.newpipe.extractor.exceptions.ExtractionException;
|
||||||
|
@ -45,6 +44,7 @@ import org.schabi.newpipe.extractor.localization.ContentCountry;
|
||||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
import org.schabi.newpipe.extractor.playlist.PlaylistInfo;
|
||||||
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
import org.schabi.newpipe.extractor.playlist.PlaylistInfoItem;
|
||||||
import org.schabi.newpipe.extractor.search.SearchInfo;
|
import org.schabi.newpipe.extractor.search.SearchInfo;
|
||||||
|
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YouTubeChannelTabHandler;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
import org.schabi.newpipe.extractor.stream.StreamInfo;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
import org.schabi.newpipe.extractor.stream.StreamInfoItem;
|
||||||
import org.schabi.newpipe.extractor.stream.StreamType;
|
import org.schabi.newpipe.extractor.stream.StreamType;
|
||||||
|
@ -158,12 +158,7 @@ public class ResponseHelper {
|
||||||
stream.getInitEnd(), stream.getIndexStart(), stream.getIndexEnd(), stream.getCodec())));
|
stream.getInitEnd(), stream.getIndexStart(), stream.getIndexEnd(), stream.getCodec())));
|
||||||
}
|
}
|
||||||
|
|
||||||
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
|
final List<ContentItem> relatedStreams = collectRelatedItems(info.getRelatedItems());
|
||||||
|
|
||||||
info.getRelatedItems().forEach(o -> {
|
|
||||||
if (o instanceof StreamInfoItem)
|
|
||||||
relatedStreams.add(collectRelatedStream(o));
|
|
||||||
});
|
|
||||||
|
|
||||||
long time = info.getUploadDate() != null ? info.getUploadDate().offsetDateTime().toInstant().toEpochMilli()
|
long time = info.getUploadDate() != null ? info.getUploadDate().offsetDateTime().toInstant().toEpochMilli()
|
||||||
: System.currentTimeMillis();
|
: System.currentTimeMillis();
|
||||||
|
@ -224,9 +219,7 @@ public class ResponseHelper {
|
||||||
|
|
||||||
final ChannelInfo info = ChannelInfo.getInfo("https://youtube.com/" + channelPath);
|
final ChannelInfo info = ChannelInfo.getInfo("https://youtube.com/" + channelPath);
|
||||||
|
|
||||||
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
|
final List<ContentItem> relatedStreams = collectRelatedItems(info.getRelatedItems());
|
||||||
|
|
||||||
info.getRelatedItems().forEach(o -> relatedStreams.add(collectRelatedStream(o)));
|
|
||||||
|
|
||||||
Multithreading.runAsync(() -> {
|
Multithreading.runAsync(() -> {
|
||||||
|
|
||||||
|
@ -292,9 +285,19 @@ public class ResponseHelper {
|
||||||
nextpage = mapper.writeValueAsString(page);
|
nextpage = mapper.writeValueAsString(page);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<ChannelTab> tabs = info.getTabs()
|
||||||
|
.stream()
|
||||||
|
.map(tab -> {
|
||||||
|
try {
|
||||||
|
return new ChannelTab(tab.getTab().name(), mapper.writeValueAsString(tab));
|
||||||
|
} catch (JsonProcessingException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
}).toList();
|
||||||
|
|
||||||
final Channel channel = new Channel(info.getId(), info.getName(), rewriteURL(info.getAvatarUrl()),
|
final Channel channel = new Channel(info.getId(), info.getName(), rewriteURL(info.getAvatarUrl()),
|
||||||
rewriteURL(info.getBannerUrl()), info.getDescription(), info.getSubscriberCount(), info.isVerified(),
|
rewriteURL(info.getBannerUrl()), info.getDescription(), info.getSubscriberCount(), info.isVerified(),
|
||||||
nextpage, relatedStreams);
|
nextpage, relatedStreams, tabs);
|
||||||
|
|
||||||
IPFS.publishData(channel);
|
IPFS.publishData(channel);
|
||||||
|
|
||||||
|
@ -313,9 +316,7 @@ public class ResponseHelper {
|
||||||
InfoItemsPage<StreamInfoItem> info = ChannelInfo.getMoreItems(YOUTUBE_SERVICE,
|
InfoItemsPage<StreamInfoItem> info = ChannelInfo.getMoreItems(YOUTUBE_SERVICE,
|
||||||
"https://youtube.com/channel/" + channelId, prevpage);
|
"https://youtube.com/channel/" + channelId, prevpage);
|
||||||
|
|
||||||
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
|
final List<ContentItem> relatedStreams = collectRelatedItems(info.getItems());
|
||||||
|
|
||||||
info.getItems().forEach(o -> relatedStreams.add(collectRelatedStream(o)));
|
|
||||||
|
|
||||||
String nextpage = null;
|
String nextpage = null;
|
||||||
if (info.hasNextPage()) {
|
if (info.hasNextPage()) {
|
||||||
|
@ -329,13 +330,49 @@ public class ResponseHelper {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] channelTabResponse(String data)
|
||||||
|
throws IOException, ExtractionException {
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(data))
|
||||||
|
return mapper.writeValueAsBytes(new InvalidRequestResponse());
|
||||||
|
|
||||||
|
YouTubeChannelTabHandler tabHandler = mapper.readValue(data, YouTubeChannelTabHandlerMixin.class);
|
||||||
|
|
||||||
|
var info = ChannelTabInfo.getInfo(YOUTUBE_SERVICE, tabHandler);
|
||||||
|
|
||||||
|
List<ContentItem> items = collectRelatedItems(info.getRelatedItems());
|
||||||
|
|
||||||
|
String nextpage = null;
|
||||||
|
if (info.hasNextPage()) {
|
||||||
|
Page page = info.getNextPage();
|
||||||
|
nextpage = mapper.writeValueAsString(page);
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapper.writeValueAsBytes(new ChannelTabData(nextpage, items));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] channelTabPageResponse(String data, String nextpage) throws Exception {
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(data))
|
||||||
|
return mapper.writeValueAsBytes(new InvalidRequestResponse());
|
||||||
|
|
||||||
|
YouTubeChannelTabHandler tabHandler = mapper.readValue(data, YouTubeChannelTabHandlerMixin.class);
|
||||||
|
|
||||||
|
Page nextPage = mapper.readValue(nextpage, Page.class);
|
||||||
|
|
||||||
|
var info = ChannelTabInfo.getMoreItems(YOUTUBE_SERVICE, tabHandler, nextPage);
|
||||||
|
|
||||||
|
List<ContentItem> items = collectRelatedItems(info.getItems());
|
||||||
|
|
||||||
|
return mapper.writeValueAsBytes(new ChannelTabData(null, items));
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] trendingResponse(String region)
|
public static byte[] trendingResponse(String region)
|
||||||
throws ExtractionException, IOException {
|
throws ExtractionException, IOException {
|
||||||
|
|
||||||
if (region == null)
|
if (region == null)
|
||||||
return mapper.writeValueAsBytes(new InvalidRequestResponse());
|
return mapper.writeValueAsBytes(new InvalidRequestResponse());
|
||||||
|
|
||||||
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
|
|
||||||
|
|
||||||
KioskList kioskList = YOUTUBE_SERVICE.getKioskList();
|
KioskList kioskList = YOUTUBE_SERVICE.getKioskList();
|
||||||
kioskList.forceContentCountry(new ContentCountry(region));
|
kioskList.forceContentCountry(new ContentCountry(region));
|
||||||
|
@ -343,7 +380,7 @@ public class ResponseHelper {
|
||||||
extractor.fetchPage();
|
extractor.fetchPage();
|
||||||
KioskInfo info = KioskInfo.getInfo(extractor);
|
KioskInfo info = KioskInfo.getInfo(extractor);
|
||||||
|
|
||||||
info.getRelatedItems().forEach(o -> relatedStreams.add(collectRelatedStream(o)));
|
final List<ContentItem> relatedStreams = collectRelatedItems(info.getRelatedItems());
|
||||||
|
|
||||||
return mapper.writeValueAsBytes(relatedStreams);
|
return mapper.writeValueAsBytes(relatedStreams);
|
||||||
}
|
}
|
||||||
|
@ -376,7 +413,7 @@ public class ResponseHelper {
|
||||||
return mapper.writeValueAsBytes(mapper.createObjectNode()
|
return mapper.writeValueAsBytes(mapper.createObjectNode()
|
||||||
.put("error", "Playlist not found"));
|
.put("error", "Playlist not found"));
|
||||||
|
|
||||||
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
|
final List<ContentItem> relatedStreams = new ObjectArrayList<>();
|
||||||
|
|
||||||
var videos = pl.getVideos();
|
var videos = pl.getVideos();
|
||||||
|
|
||||||
|
@ -399,9 +436,7 @@ public class ResponseHelper {
|
||||||
|
|
||||||
final PlaylistInfo info = PlaylistInfo.getInfo("https://www.youtube.com/playlist?list=" + playlistId);
|
final PlaylistInfo info = PlaylistInfo.getInfo("https://www.youtube.com/playlist?list=" + playlistId);
|
||||||
|
|
||||||
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
|
final List<ContentItem> relatedStreams = collectRelatedItems(info.getRelatedItems());
|
||||||
|
|
||||||
info.getRelatedItems().forEach(o -> relatedStreams.add(collectRelatedStream(o)));
|
|
||||||
|
|
||||||
String nextpage = null;
|
String nextpage = null;
|
||||||
if (info.hasNextPage()) {
|
if (info.hasNextPage()) {
|
||||||
|
@ -430,9 +465,7 @@ public class ResponseHelper {
|
||||||
InfoItemsPage<StreamInfoItem> info = PlaylistInfo.getMoreItems(YOUTUBE_SERVICE,
|
InfoItemsPage<StreamInfoItem> info = PlaylistInfo.getMoreItems(YOUTUBE_SERVICE,
|
||||||
"https://www.youtube.com/playlist?list=" + playlistId, prevpage);
|
"https://www.youtube.com/playlist?list=" + playlistId, prevpage);
|
||||||
|
|
||||||
final List<StreamItem> relatedStreams = new ObjectArrayList<>();
|
final List<ContentItem> relatedStreams = collectRelatedItems(info.getItems());
|
||||||
|
|
||||||
info.getItems().forEach(o -> relatedStreams.add(collectRelatedStream(o)));
|
|
||||||
|
|
||||||
String nextpage = null;
|
String nextpage = null;
|
||||||
if (info.hasNextPage()) {
|
if (info.hasNextPage()) {
|
||||||
|
@ -556,24 +589,7 @@ public class ResponseHelper {
|
||||||
final SearchInfo info = SearchInfo.getInfo(YOUTUBE_SERVICE,
|
final SearchInfo info = SearchInfo.getInfo(YOUTUBE_SERVICE,
|
||||||
YOUTUBE_SERVICE.getSearchQHFactory().fromQuery(q, Collections.singletonList(filter), null));
|
YOUTUBE_SERVICE.getSearchQHFactory().fromQuery(q, Collections.singletonList(filter), null));
|
||||||
|
|
||||||
ObjectArrayList<Object> items = new ObjectArrayList<>();
|
List<ContentItem> items = collectRelatedItems(info.getRelatedItems());
|
||||||
|
|
||||||
info.getRelatedItems().forEach(item -> {
|
|
||||||
switch (item.getInfoType()) {
|
|
||||||
case STREAM -> items.add(collectRelatedStream(item));
|
|
||||||
case CHANNEL -> {
|
|
||||||
ChannelInfoItem channel = (ChannelInfoItem) item;
|
|
||||||
items.add(new SearchChannel(item.getName(), rewriteURL(item.getThumbnailUrl()),
|
|
||||||
substringYouTube(item.getUrl()), channel.getDescription(), channel.getSubscriberCount(),
|
|
||||||
channel.getStreamCount(), channel.isVerified()));
|
|
||||||
}
|
|
||||||
case PLAYLIST -> {
|
|
||||||
PlaylistInfoItem playlist = (PlaylistInfoItem) item;
|
|
||||||
items.add(new SearchPlaylist(item.getName(), rewriteURL(item.getThumbnailUrl()),
|
|
||||||
substringYouTube(item.getUrl()), playlist.getUploaderName(), playlist.getStreamCount()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Page nextpage = info.getNextPage();
|
Page nextpage = info.getNextPage();
|
||||||
|
|
||||||
|
@ -593,24 +609,7 @@ public class ResponseHelper {
|
||||||
InfoItemsPage<InfoItem> pages = SearchInfo.getMoreItems(YOUTUBE_SERVICE,
|
InfoItemsPage<InfoItem> pages = SearchInfo.getMoreItems(YOUTUBE_SERVICE,
|
||||||
YOUTUBE_SERVICE.getSearchQHFactory().fromQuery(q, Collections.singletonList(filter), null), prevpage);
|
YOUTUBE_SERVICE.getSearchQHFactory().fromQuery(q, Collections.singletonList(filter), null), prevpage);
|
||||||
|
|
||||||
ObjectArrayList<Object> items = new ObjectArrayList<>();
|
List<ContentItem> items = collectRelatedItems(pages.getItems());
|
||||||
|
|
||||||
pages.getItems().forEach(item -> {
|
|
||||||
switch (item.getInfoType()) {
|
|
||||||
case STREAM -> items.add(collectRelatedStream(item));
|
|
||||||
case CHANNEL -> {
|
|
||||||
ChannelInfoItem channel = (ChannelInfoItem) item;
|
|
||||||
items.add(new SearchChannel(item.getName(), rewriteURL(item.getThumbnailUrl()),
|
|
||||||
substringYouTube(item.getUrl()), channel.getDescription(), channel.getSubscriberCount(),
|
|
||||||
channel.getStreamCount(), channel.isVerified()));
|
|
||||||
}
|
|
||||||
case PLAYLIST -> {
|
|
||||||
PlaylistInfoItem playlist = (PlaylistInfoItem) item;
|
|
||||||
items.add(new SearchPlaylist(item.getName(), rewriteURL(item.getThumbnailUrl()),
|
|
||||||
substringYouTube(item.getUrl()), playlist.getUploaderName(), playlist.getStreamCount()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Page nextpage = pages.getNextPage();
|
Page nextpage = pages.getNextPage();
|
||||||
|
|
||||||
|
@ -1754,31 +1753,47 @@ public class ResponseHelper {
|
||||||
formBuilder.add("hub.mode", "subscribe");
|
formBuilder.add("hub.mode", "subscribe");
|
||||||
formBuilder.add("hub.lease_seconds", "432000");
|
formBuilder.add("hub.lease_seconds", "432000");
|
||||||
|
|
||||||
var resp = Constants.h2client
|
try (var resp = Constants.h2client
|
||||||
.newCall(builder.post(formBuilder.build())
|
.newCall(builder.post(formBuilder.build())
|
||||||
.build()).execute();
|
.build()).execute()) {
|
||||||
|
|
||||||
if (resp.code() == 202) {
|
if (resp.code() == 202) {
|
||||||
try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) {
|
try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) {
|
||||||
var tr = s.beginTransaction();
|
var tr = s.beginTransaction();
|
||||||
if (pubsub == null) {
|
if (pubsub == null) {
|
||||||
pubsub = new PubSub(channelId, System.currentTimeMillis());
|
pubsub = new PubSub(channelId, System.currentTimeMillis());
|
||||||
s.insert(pubsub);
|
s.insert(pubsub);
|
||||||
} else {
|
} else {
|
||||||
pubsub.setSubbedAt(System.currentTimeMillis());
|
pubsub.setSubbedAt(System.currentTimeMillis());
|
||||||
s.update(pubsub);
|
s.update(pubsub);
|
||||||
|
}
|
||||||
|
tr.commit();
|
||||||
}
|
}
|
||||||
tr.commit();
|
|
||||||
}
|
|
||||||
|
|
||||||
} else
|
} else
|
||||||
System.out.println("Failed to subscribe: " + resp.code() + "\n" + Objects.requireNonNull(resp.body()).string());
|
System.out.println("Failed to subscribe: " + resp.code() + "\n" + Objects.requireNonNull(resp.body()).string());
|
||||||
|
|
||||||
resp.close();
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static List<ContentItem> collectRelatedItems(List<? extends InfoItem> items) {
|
||||||
|
return items
|
||||||
|
.stream()
|
||||||
|
.parallel()
|
||||||
|
.map(item -> {
|
||||||
|
if (item instanceof StreamInfoItem)
|
||||||
|
return collectRelatedStream(item);
|
||||||
|
else if (item instanceof PlaylistInfoItem)
|
||||||
|
return collectRelatedPlaylist(item);
|
||||||
|
else if (item instanceof ChannelInfoItem)
|
||||||
|
return collectRelatedChannel(item);
|
||||||
|
else
|
||||||
|
throw new RuntimeException("Unknown item type: " + item.getClass().getName());
|
||||||
|
}).toList();
|
||||||
|
}
|
||||||
|
|
||||||
private static StreamItem collectRelatedStream(Object o) {
|
private static StreamItem collectRelatedStream(Object o) {
|
||||||
|
|
||||||
StreamInfoItem item = (StreamInfoItem) o;
|
StreamInfoItem item = (StreamInfoItem) o;
|
||||||
|
@ -1788,4 +1803,20 @@ public class ResponseHelper {
|
||||||
rewriteURL(item.getUploaderAvatarUrl()), item.getTextualUploadDate(), item.getShortDescription(), item.getDuration(),
|
rewriteURL(item.getUploaderAvatarUrl()), item.getTextualUploadDate(), item.getShortDescription(), item.getDuration(),
|
||||||
item.getViewCount(), item.getUploadDate() != null ? item.getUploadDate().offsetDateTime().toInstant().toEpochMilli() : -1, item.isUploaderVerified(), item.isShortFormContent());
|
item.getViewCount(), item.getUploadDate() != null ? item.getUploadDate().offsetDateTime().toInstant().toEpochMilli() : -1, item.isUploaderVerified(), item.isShortFormContent());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static PlaylistItem collectRelatedPlaylist(Object o) {
|
||||||
|
|
||||||
|
PlaylistInfoItem item = (PlaylistInfoItem) o;
|
||||||
|
|
||||||
|
return new PlaylistItem(substringYouTube(item.getUrl()), item.getName(), rewriteURL(item.getThumbnailUrl()),
|
||||||
|
item.getUploaderName(), item.getPlaylistType().name(), item.getStreamCount());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static ChannelItem collectRelatedChannel(Object o) {
|
||||||
|
|
||||||
|
ChannelInfoItem item = (ChannelInfoItem) o;
|
||||||
|
|
||||||
|
return new ChannelItem(substringYouTube(item.getUrl()), item.getName(), rewriteURL(item.getThumbnailUrl()),
|
||||||
|
item.getDescription(), item.getSubscriberCount(), item.getStreamCount(), item.isVerified());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
package me.kavin.piped.utils;
|
||||||
|
|
||||||
|
import com.fasterxml.jackson.annotation.JsonCreator;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
|
||||||
|
import com.fasterxml.jackson.annotation.JsonProperty;
|
||||||
|
import org.schabi.newpipe.extractor.linkhandler.ChannelTabHandler;
|
||||||
|
import org.schabi.newpipe.extractor.linkhandler.ListLinkHandler;
|
||||||
|
import org.schabi.newpipe.extractor.services.youtube.linkHandler.YouTubeChannelTabHandler;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public class YouTubeChannelTabHandlerMixin extends YouTubeChannelTabHandler {
|
||||||
|
|
||||||
|
@JsonCreator
|
||||||
|
@JsonIgnoreProperties(ignoreUnknown = true)
|
||||||
|
public YouTubeChannelTabHandlerMixin(@JsonProperty("originalUrl") String originalUrl, @JsonProperty("url") String url,
|
||||||
|
@JsonProperty("id") String id, @JsonProperty("contentFilters") List<String> contentFilters,
|
||||||
|
@JsonProperty("sortFilter") String sortFilter, @JsonProperty("tab") ChannelTabHandler.Tab tab,
|
||||||
|
@JsonProperty("visitorData") String visitorData) {
|
||||||
|
super(new ListLinkHandler(originalUrl, url, id, contentFilters, sortFilter), tab, visitorData);
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,10 +7,12 @@ public class Channel {
|
||||||
public String id, name, avatarUrl, bannerUrl, description, nextpage;
|
public String id, name, avatarUrl, bannerUrl, description, nextpage;
|
||||||
public long subscriberCount;
|
public long subscriberCount;
|
||||||
public boolean verified;
|
public boolean verified;
|
||||||
public List<StreamItem> relatedStreams;
|
public List<ContentItem> relatedStreams;
|
||||||
|
|
||||||
|
public List<ChannelTab> tabs;
|
||||||
|
|
||||||
public Channel(String id, String name, String avatarUrl, String bannerUrl, String description, long subscriberCount,
|
public Channel(String id, String name, String avatarUrl, String bannerUrl, String description, long subscriberCount,
|
||||||
boolean verified, String nextpage, List<StreamItem> relatedStreams) {
|
boolean verified, String nextpage, List<ContentItem> relatedStreams, List<ChannelTab> tabs) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.avatarUrl = avatarUrl;
|
this.avatarUrl = avatarUrl;
|
||||||
|
@ -20,5 +22,6 @@ public class Channel {
|
||||||
this.verified = verified;
|
this.verified = verified;
|
||||||
this.nextpage = nextpage;
|
this.nextpage = nextpage;
|
||||||
this.relatedStreams = relatedStreams;
|
this.relatedStreams = relatedStreams;
|
||||||
|
this.tabs = tabs;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
23
src/main/java/me/kavin/piped/utils/obj/ChannelItem.java
Normal file
23
src/main/java/me/kavin/piped/utils/obj/ChannelItem.java
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package me.kavin.piped.utils.obj;
|
||||||
|
|
||||||
|
public class ChannelItem extends ContentItem {
|
||||||
|
|
||||||
|
public final String type = "channel";
|
||||||
|
|
||||||
|
public String name;
|
||||||
|
public String thumbnail;
|
||||||
|
public String description;
|
||||||
|
public long subscribers, videos;
|
||||||
|
public boolean verified;
|
||||||
|
|
||||||
|
public ChannelItem(String url, String name, String thumbnail, String description, long subscribers, long videos,
|
||||||
|
boolean verified) {
|
||||||
|
super(url);
|
||||||
|
this.name = name;
|
||||||
|
this.thumbnail = thumbnail;
|
||||||
|
this.description = description;
|
||||||
|
this.subscribers = subscribers;
|
||||||
|
this.videos = videos;
|
||||||
|
this.verified = verified;
|
||||||
|
}
|
||||||
|
}
|
12
src/main/java/me/kavin/piped/utils/obj/ChannelTab.java
Normal file
12
src/main/java/me/kavin/piped/utils/obj/ChannelTab.java
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
package me.kavin.piped.utils.obj;
|
||||||
|
|
||||||
|
public class ChannelTab {
|
||||||
|
|
||||||
|
public String name;
|
||||||
|
public String data;
|
||||||
|
|
||||||
|
public ChannelTab(String name, String data) {
|
||||||
|
this.name = name;
|
||||||
|
this.data = data;
|
||||||
|
}
|
||||||
|
}
|
14
src/main/java/me/kavin/piped/utils/obj/ChannelTabData.java
Normal file
14
src/main/java/me/kavin/piped/utils/obj/ChannelTabData.java
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package me.kavin.piped.utils.obj;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class ChannelTabData {
|
||||||
|
|
||||||
|
public String nextpage;
|
||||||
|
public List<ContentItem> content;
|
||||||
|
|
||||||
|
public ChannelTabData(String nextpage, List<ContentItem> content) {
|
||||||
|
this.nextpage = nextpage;
|
||||||
|
this.content = content;
|
||||||
|
}
|
||||||
|
}
|
10
src/main/java/me/kavin/piped/utils/obj/ContentItem.java
Normal file
10
src/main/java/me/kavin/piped/utils/obj/ContentItem.java
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
package me.kavin.piped.utils.obj;
|
||||||
|
|
||||||
|
public class ContentItem {
|
||||||
|
|
||||||
|
public String url;
|
||||||
|
|
||||||
|
public ContentItem(String url) {
|
||||||
|
this.url = url;
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,10 +6,10 @@ public class Playlist {
|
||||||
|
|
||||||
public String name, thumbnailUrl, bannerUrl, nextpage, uploader, uploaderUrl, uploaderAvatar;
|
public String name, thumbnailUrl, bannerUrl, nextpage, uploader, uploaderUrl, uploaderAvatar;
|
||||||
public int videos;
|
public int videos;
|
||||||
public List<StreamItem> relatedStreams;
|
public List<ContentItem> relatedStreams;
|
||||||
|
|
||||||
public Playlist(String name, String thumbnailUrl, String bannerUrl, String nextpage, String uploader,
|
public Playlist(String name, String thumbnailUrl, String bannerUrl, String nextpage, String uploader,
|
||||||
String uploaderUrl, String uploaderAvatar, int videos, List<StreamItem> relatedStreams) {
|
String uploaderUrl, String uploaderAvatar, int videos, List<ContentItem> relatedStreams) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.thumbnailUrl = thumbnailUrl;
|
this.thumbnailUrl = thumbnailUrl;
|
||||||
this.bannerUrl = bannerUrl;
|
this.bannerUrl = bannerUrl;
|
||||||
|
|
23
src/main/java/me/kavin/piped/utils/obj/PlaylistItem.java
Normal file
23
src/main/java/me/kavin/piped/utils/obj/PlaylistItem.java
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
package me.kavin.piped.utils.obj;
|
||||||
|
|
||||||
|
public class PlaylistItem extends ContentItem {
|
||||||
|
|
||||||
|
public final String type = "playlist";
|
||||||
|
|
||||||
|
public String name;
|
||||||
|
public String thumbnail;
|
||||||
|
public String uploaderName;
|
||||||
|
public String playlistType;
|
||||||
|
|
||||||
|
public long videos;
|
||||||
|
|
||||||
|
|
||||||
|
public PlaylistItem(String url, String name, String thumbnail, String uploaderName, String playlistType, long videos) {
|
||||||
|
super(url);
|
||||||
|
this.name = name;
|
||||||
|
this.thumbnail = thumbnail;
|
||||||
|
this.uploaderName = uploaderName;
|
||||||
|
this.playlistType = playlistType;
|
||||||
|
this.videos = videos;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,19 +1,19 @@
|
||||||
package me.kavin.piped.utils.obj;
|
package me.kavin.piped.utils.obj;
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
|
import java.util.List;
|
||||||
|
|
||||||
public class SearchResults {
|
public class SearchResults {
|
||||||
|
|
||||||
public ObjectArrayList<Object> items;
|
public List<ContentItem> items;
|
||||||
public String nextpage, suggestion;
|
public String nextpage, suggestion;
|
||||||
public boolean corrected;
|
public boolean corrected;
|
||||||
|
|
||||||
public SearchResults(ObjectArrayList<Object> items, String nextpage) {
|
public SearchResults(List<ContentItem> items, String nextpage) {
|
||||||
this.nextpage = nextpage;
|
this.nextpage = nextpage;
|
||||||
this.items = items;
|
this.items = items;
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchResults(ObjectArrayList<Object> items, String nextpage, String suggestion, boolean corrected) {
|
public SearchResults(List<ContentItem> items, String nextpage, String suggestion, boolean corrected) {
|
||||||
this.items = items;
|
this.items = items;
|
||||||
this.nextpage = nextpage;
|
this.nextpage = nextpage;
|
||||||
this.suggestion = suggestion;
|
this.suggestion = suggestion;
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
package me.kavin.piped.utils.obj;
|
package me.kavin.piped.utils.obj;
|
||||||
|
|
||||||
public class StreamItem {
|
public class StreamItem extends ContentItem {
|
||||||
|
|
||||||
private final String type = "video";
|
public final String type = "stream";
|
||||||
|
|
||||||
public String url, title, thumbnail, uploaderName, uploaderUrl, uploaderAvatar, uploadedDate, shortDescription;
|
public String title, thumbnail, uploaderName, uploaderUrl, uploaderAvatar, uploadedDate, shortDescription;
|
||||||
public long duration, views, uploaded;
|
public long duration, views, uploaded;
|
||||||
public boolean uploaderVerified, isShort;
|
public boolean uploaderVerified, isShort;
|
||||||
|
|
||||||
public StreamItem(String url, String title, String thumbnail, String uploaderName, String uploaderUrl,
|
public StreamItem(String url, String title, String thumbnail, String uploaderName, String uploaderUrl,
|
||||||
String uploaderAvatar, String uploadedDate, String shortDescription, long duration, long views, long uploaded, boolean uploaderVerified, boolean isShort) {
|
String uploaderAvatar, String uploadedDate, String shortDescription, long duration, long views, long uploaded, boolean uploaderVerified, boolean isShort) {
|
||||||
this.url = url;
|
super(url);
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.thumbnail = thumbnail;
|
this.thumbnail = thumbnail;
|
||||||
this.uploaderName = uploaderName;
|
this.uploaderName = uploaderName;
|
||||||
|
|
|
@ -15,7 +15,7 @@ public class Streams {
|
||||||
|
|
||||||
public List<PipedStream> audioStreams, videoStreams;
|
public List<PipedStream> audioStreams, videoStreams;
|
||||||
|
|
||||||
public List<StreamItem> relatedStreams;
|
public List<ContentItem> relatedStreams;
|
||||||
|
|
||||||
public List<Subtitle> subtitles;
|
public List<Subtitle> subtitles;
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@ public class Streams {
|
||||||
public Streams(String title, String description, String uploadDate, String uploader, String uploaderUrl,
|
public Streams(String title, String description, String uploadDate, String uploader, String uploaderUrl,
|
||||||
String uploaderAvatar, String thumbnailUrl, long duration, long views, long likes, long dislikes, long uploaderSubscriberCount,
|
String uploaderAvatar, String thumbnailUrl, long duration, long views, long likes, long dislikes, long uploaderSubscriberCount,
|
||||||
boolean uploaderVerified, List<PipedStream> audioStreams, List<PipedStream> videoStreams,
|
boolean uploaderVerified, List<PipedStream> audioStreams, List<PipedStream> videoStreams,
|
||||||
List<StreamItem> relatedStreams, List<Subtitle> subtitles, boolean livestream, String hls, String dash,
|
List<ContentItem> relatedStreams, List<Subtitle> subtitles, boolean livestream, String hls, String dash,
|
||||||
String lbryId, List<ChapterSegment> chapters) {
|
String lbryId, List<ChapterSegment> chapters) {
|
||||||
this.title = title;
|
this.title = title;
|
||||||
this.description = description;
|
this.description = description;
|
||||||
|
|
|
@ -5,9 +5,9 @@ import java.util.List;
|
||||||
public class StreamsPage {
|
public class StreamsPage {
|
||||||
|
|
||||||
public String nextpage;
|
public String nextpage;
|
||||||
public List<StreamItem> relatedStreams;
|
public List<ContentItem> relatedStreams;
|
||||||
|
|
||||||
public StreamsPage(String nextpage, List<StreamItem> relatedStreams) {
|
public StreamsPage(String nextpage, List<ContentItem> relatedStreams) {
|
||||||
this.nextpage = nextpage;
|
this.nextpage = nextpage;
|
||||||
this.relatedStreams = relatedStreams;
|
this.relatedStreams = relatedStreams;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,33 +0,0 @@
|
||||||
package me.kavin.piped.utils.obj.search;
|
|
||||||
|
|
||||||
public class SearchChannel extends SearchItem {
|
|
||||||
|
|
||||||
private String description;
|
|
||||||
private long subscribers, videos;
|
|
||||||
private boolean verified;
|
|
||||||
|
|
||||||
public SearchChannel(String name, String thumbnail, String url, String description, long subscribers, long videos,
|
|
||||||
boolean verified) {
|
|
||||||
super(name, thumbnail, url);
|
|
||||||
this.description = description;
|
|
||||||
this.subscribers = subscribers;
|
|
||||||
this.videos = videos;
|
|
||||||
this.verified = verified;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getDescription() {
|
|
||||||
return description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getSubscribers() {
|
|
||||||
return subscribers;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getVideos() {
|
|
||||||
return videos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isVerified() {
|
|
||||||
return verified;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package me.kavin.piped.utils.obj.search;
|
|
||||||
|
|
||||||
public class SearchItem {
|
|
||||||
|
|
||||||
private String name, thumbnail, url;
|
|
||||||
|
|
||||||
public SearchItem(String name, String thumbnail, String url) {
|
|
||||||
this.name = name;
|
|
||||||
this.thumbnail = thumbnail;
|
|
||||||
this.url = url;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getThumbnail() {
|
|
||||||
return thumbnail;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUrl() {
|
|
||||||
return url;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,21 +0,0 @@
|
||||||
package me.kavin.piped.utils.obj.search;
|
|
||||||
|
|
||||||
public class SearchPlaylist extends SearchItem {
|
|
||||||
|
|
||||||
private String uploaderName;
|
|
||||||
private long videos;
|
|
||||||
|
|
||||||
public SearchPlaylist(String name, String thumbnail, String url, String uploaderName, long videos) {
|
|
||||||
super(name, thumbnail, url);
|
|
||||||
this.uploaderName = uploaderName;
|
|
||||||
this.videos = videos;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUploaderName() {
|
|
||||||
return uploaderName;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getVideos() {
|
|
||||||
return videos;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,43 +0,0 @@
|
||||||
package me.kavin.piped.utils.obj.search;
|
|
||||||
|
|
||||||
public class SearchStream extends SearchItem {
|
|
||||||
|
|
||||||
private String uploadDate, uploader, uploaderUrl;
|
|
||||||
private long views, duration;
|
|
||||||
private boolean uploaderVerified;
|
|
||||||
|
|
||||||
public SearchStream(String name, String thumbnail, String url, String uploadDate, String uploader,
|
|
||||||
String uploaderUrl, long views, long duration, boolean uploaderVerified) {
|
|
||||||
super(name, thumbnail, url);
|
|
||||||
this.uploadDate = uploadDate;
|
|
||||||
this.uploader = uploader;
|
|
||||||
this.uploaderUrl = uploaderUrl;
|
|
||||||
this.views = views;
|
|
||||||
this.duration = duration;
|
|
||||||
this.uploaderVerified = uploaderVerified;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUploadDate() {
|
|
||||||
return uploadDate;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUploader() {
|
|
||||||
return uploader;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getUploaderUrl() {
|
|
||||||
return uploaderUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getViews() {
|
|
||||||
return views;
|
|
||||||
}
|
|
||||||
|
|
||||||
public long getDuration() {
|
|
||||||
return duration;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isUploaderVerified() {
|
|
||||||
return uploaderVerified;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -21,6 +21,10 @@ curl ${CURLOPTS[@]} $HOST/user/Kurzgesagt || exit 1
|
||||||
CHANNEL_NEXTPAGE=$(curl -s -o - -f $HOST/channel/UCsXVk37bltHxD1rDPwtNM8Q | jq -r .nextpage)
|
CHANNEL_NEXTPAGE=$(curl -s -o - -f $HOST/channel/UCsXVk37bltHxD1rDPwtNM8Q | jq -r .nextpage)
|
||||||
curl ${CURLOPTS[@]} $HOST/nextpage/channel/UCsXVk37bltHxD1rDPwtNM8Q -G --data-urlencode "nextpage=$CHANNEL_NEXTPAGE" || exit 1
|
curl ${CURLOPTS[@]} $HOST/nextpage/channel/UCsXVk37bltHxD1rDPwtNM8Q -G --data-urlencode "nextpage=$CHANNEL_NEXTPAGE" || exit 1
|
||||||
|
|
||||||
|
# Channel Tab
|
||||||
|
CHANNEL_TAB_DATA=$(curl -s -o - -f $HOST/channel/UCsXVk37bltHxD1rDPwtNM8Q | jq -r .tabs[0].data)
|
||||||
|
curl ${CURLOPTS[@]} $HOST/channels/tabs -G --data-urlencode "data=$CHANNEL_TAB_DATA" || exit 1
|
||||||
|
|
||||||
# Playlist
|
# Playlist
|
||||||
curl ${CURLOPTS[@]} $HOST/playlists/PLQSoWXSpjA3-egtFq45DcUydZ885W7MTT || exit 1
|
curl ${CURLOPTS[@]} $HOST/playlists/PLQSoWXSpjA3-egtFq45DcUydZ885W7MTT || exit 1
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue