Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: simplify FeedHandlers.java by introducing FeedHelpers.java #781

Merged
merged 1 commit into from
Mar 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 18 additions & 114 deletions src/main/java/me/kavin/piped/server/handlers/auth/FeedHandlers.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
import com.rometools.rome.feed.synd.*;
import com.rometools.rome.io.FeedException;
import com.rometools.rome.io.SyndFeedOutput;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.JoinType;
import me.kavin.piped.consts.Constants;
import me.kavin.piped.utils.*;
import me.kavin.piped.utils.obj.StreamItem;
Expand Down Expand Up @@ -104,24 +101,7 @@ public static byte[] feedResponse(String session) throws IOException {
if (user != null) {
try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) {

CriteriaBuilder cb = s.getCriteriaBuilder();

// Get all videos from subscribed channels, with channel info
CriteriaQuery<Video> criteria = cb.createQuery(Video.class);
var root = criteria.from(Video.class);
root.fetch("channel", JoinType.RIGHT);
var subquery = criteria.subquery(String.class);
var subroot = subquery.from(User.class);
subquery.select(subroot.get("subscribed_ids"))
.where(cb.equal(subroot.get("id"), user.getId()));

criteria.select(root)
.where(
root.get("channel").get("uploader_id").in(subquery)
)
.orderBy(cb.desc(root.get("uploaded")));

List<StreamItem> feedItems = s.createQuery(criteria).setTimeout(20).stream()
List<StreamItem> feedItems = FeedHelpers.generateAuthenticatedFeed(s, user.getId(), Integer.MAX_VALUE)
.parallel().map(video -> {
var channel = video.getChannel();

Expand All @@ -147,41 +127,13 @@ public static byte[] feedResponseRSS(String session) throws FeedException {
User user = DatabaseHelper.getUserFromSession(session);

if (user != null) {

try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) {
SyndFeed feed = FeedHelpers.createRssFeed(user.getUsername());

SyndFeed feed = new SyndFeedImpl();
feed.setFeedType("atom_1.0");
feed.setTitle("Piped - Feed");
feed.setDescription(String.format("Piped's RSS subscription feed for %s.", user.getUsername()));
feed.setUri(Constants.FRONTEND_URL + "/feed");
feed.setPublishedDate(new Date());

CriteriaBuilder cb = s.getCriteriaBuilder();

// Get all videos from subscribed channels, with channel info
CriteriaQuery<Video> criteria = cb.createQuery(Video.class);
var root = criteria.from(Video.class);
root.fetch("channel", JoinType.RIGHT);
var subquery = criteria.subquery(String.class);
var subroot = subquery.from(User.class);
subquery.select(subroot.get("subscribed_ids"))
.where(cb.equal(subroot.get("id"), user.getId()));

criteria.select(root)
.where(
root.get("channel").get("uploader_id").in(subquery)
)
.orderBy(cb.desc(root.get("uploaded")));

final List<SyndEntry> entries = s.createQuery(criteria)
.setTimeout(20)
.setMaxResults(100)
.stream()
final List<SyndEntry> entries = FeedHelpers.generateAuthenticatedFeed(s, user.getId(), 100)
.map(video -> {
var channel = video.getChannel();
SyndEntry entry = ChannelHelpers.createEntry(video, channel);
return entry;
return ChannelHelpers.createEntry(video, channel);
}).toList();

feed.setEntries(entries);
Expand All @@ -196,29 +148,15 @@ public static byte[] feedResponseRSS(String session) throws FeedException {

public static byte[] unauthenticatedFeedResponse(String[] channelIds) throws Exception {

Set<String> filtered = Arrays.stream(channelIds)
Set<String> filteredChannels = Arrays.stream(channelIds)
.filter(ChannelHelpers::isValidId)
.collect(Collectors.toUnmodifiableSet());

if (filtered.isEmpty())
if (filteredChannels.isEmpty())
return mapper.writeValueAsBytes(Collections.EMPTY_LIST);

try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) {

CriteriaBuilder cb = s.getCriteriaBuilder();

// Get all videos from subscribed channels, with channel info
CriteriaQuery<Video> criteria = cb.createQuery(Video.class);
var root = criteria.from(Video.class);
root.fetch("channel", JoinType.RIGHT);

criteria.select(root)
.where(cb.and(
root.get("channel").get("id").in(filtered)
))
.orderBy(cb.desc(root.get("uploaded")));

List<StreamItem> feedItems = s.createQuery(criteria).setTimeout(20).stream()
List<StreamItem> feedItems = FeedHelpers.generateUnauthenticatedFeed(s, filteredChannels, Integer.MAX_VALUE)
.parallel().map(video -> {
var channel = video.getChannel();

Expand All @@ -228,8 +166,8 @@ public static byte[] unauthenticatedFeedResponse(String[] channelIds) throws Exc
video.getUploaded(), channel.isVerified(), video.isShort());
}).toList();

updateSubscribedTime(filtered);
addMissingChannels(filtered);
updateSubscribedTime(filteredChannels);
addMissingChannels(filteredChannels);

return mapper.writeValueAsBytes(feedItems);
}
Expand All @@ -245,39 +183,13 @@ public static byte[] unauthenticatedFeedResponseRSS(String[] channelIds) throws
ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("No valid channel IDs provided"));

try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) {
List<Video> videos = FeedHelpers.generateUnauthenticatedFeed(s, filteredChannels, 100).toList();

CriteriaBuilder cb = s.getCriteriaBuilder();

// Get all videos from subscribed channels, with channel info
CriteriaQuery<Video> criteria = cb.createQuery(Video.class);
var root = criteria.from(Video.class);
root.fetch("channel", JoinType.RIGHT);
List<SyndEntry> entries = videos.stream()
.map(video -> ChannelHelpers.createEntry(video, video.getChannel()))
.toList();

criteria.select(root)
.where(cb.and(
root.get("channel").get("id").in(filteredChannels)
))
.orderBy(cb.desc(root.get("uploaded")));

List<Video> videos = s.createQuery(criteria)
.setTimeout(20)
.setMaxResults(100)
.list();

SyndFeed feed = new SyndFeedImpl();
feed.setFeedType("atom_1.0");
feed.setTitle("Piped - Feed");
feed.setDescription("Piped's RSS unauthenticated subscription feed.");
feed.setUri(Constants.FRONTEND_URL + "/feed");
feed.setPublishedDate(new Date());

final List<SyndEntry> entries = new ObjectArrayList<>();

for (Video video : videos) {
var channel = video.getChannel();
SyndEntry entry = ChannelHelpers.createEntry(video, channel);
entries.add(entry);
}
SyndFeed feed = FeedHelpers.createRssFeed(null);

if (filteredChannels.size() == 1) {
if (!videos.isEmpty()) {
Expand Down Expand Up @@ -440,12 +352,8 @@ public static byte[] subscriptionsResponse(String session)
query.select(root)
.where(root.get("uploader_id").in(subquery));

List<SubscriptionChannel> subscriptionItems = s.createQuery(query)
.stream().parallel()
.filter(channel -> channel.getUploader() != null)
.sorted(Comparator.comparing(Channel::getUploader, String.CASE_INSENSITIVE_ORDER))
.map(channel -> new SubscriptionChannel("/channel/" + channel.getUploaderId(),
channel.getUploader(), rewriteURL(channel.getUploaderAvatar()), channel.isVerified()))
List<SubscriptionChannel> subscriptionItems = FeedHelpers
.generateSubscriptionsList(s.createQuery(query).stream())
.toList();

return mapper.writeValueAsBytes(subscriptionItems);
Expand Down Expand Up @@ -475,12 +383,8 @@ public static byte[] unauthenticatedSubscriptionsResponse(String[] channelIds)
query.select(root);
query.where(root.get("uploader_id").in(filtered));

List<SubscriptionChannel> subscriptionItems = s.createQuery(query)
.stream().parallel()
.filter(channel -> channel.getUploader() != null)
.sorted(Comparator.comparing(Channel::getUploader, String.CASE_INSENSITIVE_ORDER))
.map(channel -> new SubscriptionChannel("/channel/" + channel.getUploaderId(),
channel.getUploader(), rewriteURL(channel.getUploaderAvatar()), channel.isVerified()))
List<SubscriptionChannel> subscriptionItems = FeedHelpers
.generateSubscriptionsList(s.createQuery(query).stream())
.toList();

return mapper.writeValueAsBytes(subscriptionItems);
Expand Down
89 changes: 89 additions & 0 deletions src/main/java/me/kavin/piped/utils/FeedHelpers.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package me.kavin.piped.utils;

import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.feed.synd.SyndFeedImpl;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
import jakarta.persistence.criteria.JoinType;
import me.kavin.piped.consts.Constants;
import me.kavin.piped.utils.obj.SubscriptionChannel;
import me.kavin.piped.utils.obj.db.Channel;
import me.kavin.piped.utils.obj.db.User;
import me.kavin.piped.utils.obj.db.Video;
import org.hibernate.StatelessSession;

import javax.annotation.Nullable;
import java.util.Comparator;
import java.util.Date;
import java.util.Set;
import java.util.stream.Stream;

import static me.kavin.piped.utils.URLUtils.rewriteURL;

public class FeedHelpers {
public static Stream<Video> generateAuthenticatedFeed(StatelessSession s, long userId, int maxResults) {
CriteriaBuilder cb = s.getCriteriaBuilder();

// Get all videos from subscribed channels, with channel info
CriteriaQuery<Video> criteria = cb.createQuery(Video.class);
var root = criteria.from(Video.class);
root.fetch("channel", JoinType.RIGHT);
var subquery = criteria.subquery(String.class);
var subroot = subquery.from(User.class);
subquery.select(subroot.get("subscribed_ids"))
.where(cb.equal(subroot.get("id"), userId));

criteria.select(root)
.where(
root.get("channel").get("uploader_id").in(subquery)
)
.orderBy(cb.desc(root.get("uploaded")));

return s.createQuery(criteria).setTimeout(20).setMaxResults(maxResults).stream();
}

public static Stream<Video> generateUnauthenticatedFeed(StatelessSession s, Set<String> channelIds, int maxResults) {
CriteriaBuilder cb = s.getCriteriaBuilder();

// Get all videos from subscribed channels, with channel info
CriteriaQuery<Video> criteria = cb.createQuery(Video.class);
var root = criteria.from(Video.class);
root.fetch("channel", JoinType.RIGHT);

criteria.select(root)
.where(cb.and(
root.get("channel").get("id").in(channelIds)
))
.orderBy(cb.desc(root.get("uploaded")));

return s.createQuery(criteria)
.setTimeout(20)
.setMaxResults(maxResults)
.stream();
}

public static SyndFeed createRssFeed(@Nullable String username) {
SyndFeed feed = new SyndFeedImpl();
feed.setFeedType("atom_1.0");
feed.setTitle("Piped - Feed");

if (username == null) {
feed.setDescription("Piped's RSS unauthenticated subscription feed.");
} else {
feed.setDescription(String.format("Piped's RSS subscription feed for %s.", username));
}

feed.setUri(Constants.FRONTEND_URL + "/feed");
feed.setPublishedDate(new Date());

return feed;
}

public static Stream<SubscriptionChannel> generateSubscriptionsList(Stream<Channel> channels) {
return channels.parallel()
.filter(channel -> channel.getUploader() != null)
.sorted(Comparator.comparing(Channel::getUploader, String.CASE_INSENSITIVE_ORDER))
.map(channel -> new SubscriptionChannel("/channel/" + channel.getUploaderId(),
channel.getUploader(), rewriteURL(channel.getUploaderAvatar()), channel.isVerified()));
}
}
Loading