Merge pull request #400 from TeamPiped/channel-tabs

Implement support for channel tabs.
This commit is contained in:
Kavin 2022-10-27 12:08:09 +01:00 committed by GitHub
commit 8c1a80f8be
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 249 additions and 214 deletions

View file

@ -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");

View file

@ -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<>();

View file

@ -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());
}
} }

View file

@ -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);
}
}

View file

@ -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;
} }
} }

View 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;
}
}

View 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;
}
}

View 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;
}
}

View file

@ -0,0 +1,10 @@
package me.kavin.piped.utils.obj;
public class ContentItem {
public String url;
public ContentItem(String url) {
this.url = url;
}
}

View file

@ -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;

View 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;
}
}

View file

@ -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;

View file

@ -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;

View file

@ -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;

View file

@ -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;
} }

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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;
}
}

View file

@ -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