Speed up playlists and fix adding videos on empty playlists.

This commit is contained in:
Kavin 2022-11-18 17:27:42 +00:00
parent 99fcaf8733
commit adfc0850ed
No known key found for this signature in database
GPG key ID: 49451E4482CC5BCD
3 changed files with 47 additions and 30 deletions

View file

@ -34,7 +34,7 @@ import static me.kavin.piped.utils.URLUtils.rewriteURL;
import static me.kavin.piped.utils.URLUtils.substringYouTube; import static me.kavin.piped.utils.URLUtils.substringYouTube;
public class PlaylistHandlers { public class PlaylistHandlers {
public static byte[] playlistResponse(String playlistId) throws ExtractionException, IOException { public static byte[] playlistResponse(String playlistId) throws Exception {
if (StringUtils.isBlank(playlistId)) if (StringUtils.isBlank(playlistId))
ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is a required parameter")); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is a required parameter"));
@ -98,7 +98,7 @@ public class PlaylistHandlers {
} }
public static byte[] playlistRSSResponse(String playlistId) throws ExtractionException, IOException, FeedException { public static byte[] playlistRSSResponse(String playlistId) throws Exception {
if (StringUtils.isBlank(playlistId)) if (StringUtils.isBlank(playlistId))
ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is a required parameter")); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is a required parameter"));

View file

@ -5,16 +5,12 @@ import com.rometools.rome.feed.synd.SyndEntry;
import com.rometools.rome.feed.synd.SyndEntryImpl; import com.rometools.rome.feed.synd.SyndEntryImpl;
import com.rometools.rome.feed.synd.SyndFeed; import com.rometools.rome.feed.synd.SyndFeed;
import com.rometools.rome.feed.synd.SyndFeedImpl; import com.rometools.rome.feed.synd.SyndFeedImpl;
import com.rometools.rome.io.FeedException;
import com.rometools.rome.io.SyndFeedOutput; import com.rometools.rome.io.SyndFeedOutput;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList; import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import jakarta.persistence.criteria.JoinType; import jakarta.persistence.criteria.JoinType;
import me.kavin.piped.consts.Constants; import me.kavin.piped.consts.Constants;
import me.kavin.piped.utils.DatabaseHelper; import me.kavin.piped.utils.*;
import me.kavin.piped.utils.DatabaseSessionFactory;
import me.kavin.piped.utils.ExceptionHandler;
import me.kavin.piped.utils.URLUtils;
import me.kavin.piped.utils.obj.ContentItem; import me.kavin.piped.utils.obj.ContentItem;
import me.kavin.piped.utils.obj.Playlist; import me.kavin.piped.utils.obj.Playlist;
import me.kavin.piped.utils.obj.StreamItem; import me.kavin.piped.utils.obj.StreamItem;
@ -43,21 +39,17 @@ import static me.kavin.piped.utils.URLUtils.rewriteURL;
import static me.kavin.piped.utils.URLUtils.substringYouTube; import static me.kavin.piped.utils.URLUtils.substringYouTube;
public class AuthPlaylistHandlers { public class AuthPlaylistHandlers {
public static byte[] playlistPipedResponse(String playlistId) throws IOException { public static byte[] playlistPipedResponse(String playlistId) throws Exception {
if (StringUtils.isBlank(playlistId)) if (StringUtils.isBlank(playlistId))
ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is a required parameter")); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is a required parameter"));
try (Session s = DatabaseSessionFactory.createSession()) { try (Session s = DatabaseSessionFactory.createSession()) {
var cb = s.getCriteriaBuilder();
var cq = cb.createQuery(me.kavin.piped.utils.obj.db.Playlist.class); var playlistCompletableFuture = Multithreading.supplyAsync(() -> DatabaseHelper.getPlaylistFromId(s, playlistId));
var root = cq.from(me.kavin.piped.utils.obj.db.Playlist.class); var playlistVideosCompletableFuture = Multithreading.supplyAsync(() -> DatabaseHelper.getPlaylistVideosFromPlaylistId(playlistId, true));
root.fetch("videos", JoinType.RIGHT)
.fetch("channel", JoinType.INNER); var pl = playlistCompletableFuture.get();
cq.select(root);
cq.where(cb.equal(root.get("playlist_id"), UUID.fromString(playlistId)));
var query = s.createQuery(cq);
var pl = query.uniqueResult();
if (pl == null) if (pl == null)
return mapper.writeValueAsBytes(mapper.createObjectNode() return mapper.writeValueAsBytes(mapper.createObjectNode()
@ -65,7 +57,7 @@ public class AuthPlaylistHandlers {
final List<ContentItem> relatedStreams = new ObjectArrayList<>(); final List<ContentItem> relatedStreams = new ObjectArrayList<>();
var videos = pl.getVideos(); var videos = playlistVideosCompletableFuture.get();
for (var video : videos) { for (var video : videos) {
var channel = video.getChannel(); var channel = video.getChannel();
@ -82,21 +74,16 @@ public class AuthPlaylistHandlers {
} }
public static byte[] playlistPipedRSSResponse(String playlistId) public static byte[] playlistPipedRSSResponse(String playlistId)
throws FeedException { throws Exception {
if (StringUtils.isBlank(playlistId)) if (StringUtils.isBlank(playlistId))
ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is required parameter")); ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is required parameter"));
try (Session s = DatabaseSessionFactory.createSession()) { try (Session s = DatabaseSessionFactory.createSession()) {
var cb = s.getCriteriaBuilder(); var playlistCompletableFuture = Multithreading.supplyAsync(() -> DatabaseHelper.getPlaylistFromId(s, playlistId));
var cq = cb.createQuery(me.kavin.piped.utils.obj.db.Playlist.class); var playlistVideosCompletableFuture = Multithreading.supplyAsync(() -> DatabaseHelper.getPlaylistVideosFromPlaylistId(playlistId, true));
var root = cq.from(me.kavin.piped.utils.obj.db.Playlist.class);
root.fetch("videos", JoinType.RIGHT) var pl = playlistCompletableFuture.get();
.fetch("channel", JoinType.INNER);
cq.select(root);
cq.where(cb.equal(root.get("playlist_id"), UUID.fromString(playlistId)));
var query = s.createQuery(cq);
var pl = query.uniqueResult();
final List<SyndEntry> entries = new ObjectArrayList<>(); final List<SyndEntry> entries = new ObjectArrayList<>();
@ -108,7 +95,9 @@ public class AuthPlaylistHandlers {
feed.setLink(Constants.FRONTEND_URL + "/playlist?list=" + pl.getPlaylistId()); feed.setLink(Constants.FRONTEND_URL + "/playlist?list=" + pl.getPlaylistId());
feed.setPublishedDate(new Date()); feed.setPublishedDate(new Date());
for (var video : pl.getVideos()) { var videos = playlistVideosCompletableFuture.get();
for (var video : videos) {
SyndEntry entry = new SyndEntryImpl(); SyndEntry entry = new SyndEntryImpl();
entry.setAuthor(video.getChannel().getUploader()); entry.setAuthor(video.getChannel().getUploader());
entry.setLink(Constants.FRONTEND_URL + "/video?id=" + video.getId()); entry.setLink(Constants.FRONTEND_URL + "/video?id=" + video.getId());
@ -223,7 +212,6 @@ public class AuthPlaylistHandlers {
var cb = s.getCriteriaBuilder(); var cb = s.getCriteriaBuilder();
var query = cb.createQuery(me.kavin.piped.utils.obj.db.Playlist.class); var query = cb.createQuery(me.kavin.piped.utils.obj.db.Playlist.class);
var root = query.from(me.kavin.piped.utils.obj.db.Playlist.class); var root = query.from(me.kavin.piped.utils.obj.db.Playlist.class);
root.fetch("videos", JoinType.RIGHT);
query.where(cb.equal(root.get("playlist_id"), UUID.fromString(playlistId))); query.where(cb.equal(root.get("playlist_id"), UUID.fromString(playlistId)));
var playlist = s.createQuery(query).uniqueResult(); var playlist = s.createQuery(query).uniqueResult();

View file

@ -123,6 +123,12 @@ public class DatabaseHelper {
return s.createQuery(cr).uniqueResult(); return s.createQuery(cr).uniqueResult();
} }
public static Playlist getPlaylistFromId(String id) {
try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) {
return getPlaylistFromId(s, id);
}
}
public static List<PlaylistVideo> getPlaylistVideosFromIds(SharedSessionContract s, Collection<String> id) { public static List<PlaylistVideo> getPlaylistVideosFromIds(SharedSessionContract s, Collection<String> id) {
CriteriaBuilder cb = s.getCriteriaBuilder(); CriteriaBuilder cb = s.getCriteriaBuilder();
CriteriaQuery<PlaylistVideo> cr = cb.createQuery(PlaylistVideo.class); CriteriaQuery<PlaylistVideo> cr = cb.createQuery(PlaylistVideo.class);
@ -132,6 +138,29 @@ public class DatabaseHelper {
return s.createQuery(cr).list(); return s.createQuery(cr).list();
} }
public static List<PlaylistVideo> getPlaylistVideosFromPlaylistId(SharedSessionContract s, String id, boolean fetchChannel) {
CriteriaBuilder cb = s.getCriteriaBuilder();
CriteriaQuery<PlaylistVideo> cr = cb.createQuery(PlaylistVideo.class);
var root = cr.from(PlaylistVideo.class);
root.fetch("channel", JoinType.RIGHT);
cr.select(root);
var sq = cr.subquery(String.class);
{
var sqRoot = sq.from(Playlist.class);
sq.select(sqRoot.get("videos").get("id"))
.where(cb.equal(sqRoot.get("playlist_id"), UUID.fromString(id)));
}
cr.where(cb.equal(root.get("id"), sq));
return s.createQuery(cr).list();
}
public static List<PlaylistVideo> getPlaylistVideosFromPlaylistId(String id, boolean fetchChannel) {
try (StatelessSession s = DatabaseSessionFactory.createStatelessSession()) {
return getPlaylistVideosFromPlaylistId(s, id, fetchChannel);
}
}
public static PubSub getPubSubFromId(SharedSessionContract s, String id) { public static PubSub getPubSubFromId(SharedSessionContract s, String id) {
CriteriaBuilder cb = s.getCriteriaBuilder(); CriteriaBuilder cb = s.getCriteriaBuilder();
CriteriaQuery<PubSub> cr = cb.createQuery(PubSub.class); CriteriaQuery<PubSub> cr = cb.createQuery(PubSub.class);