Merge pull request #450 from TeamPiped/playlist-fixes

Speed up playlists and fix adding videos on empty playlists.
This commit is contained in:
Kavin 2022-11-18 17:30:55 +00:00 committed by GitHub
commit 8aff82fde2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
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;
public class PlaylistHandlers {
public static byte[] playlistResponse(String playlistId) throws ExtractionException, IOException {
public static byte[] playlistResponse(String playlistId) throws Exception {
if (StringUtils.isBlank(playlistId))
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))
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.SyndFeed;
import com.rometools.rome.feed.synd.SyndFeedImpl;
import com.rometools.rome.io.FeedException;
import com.rometools.rome.io.SyndFeedOutput;
import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import jakarta.persistence.criteria.JoinType;
import me.kavin.piped.consts.Constants;
import me.kavin.piped.utils.DatabaseHelper;
import me.kavin.piped.utils.DatabaseSessionFactory;
import me.kavin.piped.utils.ExceptionHandler;
import me.kavin.piped.utils.URLUtils;
import me.kavin.piped.utils.*;
import me.kavin.piped.utils.obj.ContentItem;
import me.kavin.piped.utils.obj.Playlist;
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;
public class AuthPlaylistHandlers {
public static byte[] playlistPipedResponse(String playlistId) throws IOException {
public static byte[] playlistPipedResponse(String playlistId) throws Exception {
if (StringUtils.isBlank(playlistId))
ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is a required parameter"));
try (Session s = DatabaseSessionFactory.createSession()) {
var cb = s.getCriteriaBuilder();
var cq = cb.createQuery(me.kavin.piped.utils.obj.db.Playlist.class);
var root = cq.from(me.kavin.piped.utils.obj.db.Playlist.class);
root.fetch("videos", JoinType.RIGHT)
.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();
var playlistCompletableFuture = Multithreading.supplyAsync(() -> DatabaseHelper.getPlaylistFromId(s, playlistId));
var playlistVideosCompletableFuture = Multithreading.supplyAsync(() -> DatabaseHelper.getPlaylistVideosFromPlaylistId(playlistId, true));
var pl = playlistCompletableFuture.get();
if (pl == null)
return mapper.writeValueAsBytes(mapper.createObjectNode()
@ -65,7 +57,7 @@ public class AuthPlaylistHandlers {
final List<ContentItem> relatedStreams = new ObjectArrayList<>();
var videos = pl.getVideos();
var videos = playlistVideosCompletableFuture.get();
for (var video : videos) {
var channel = video.getChannel();
@ -82,21 +74,16 @@ public class AuthPlaylistHandlers {
}
public static byte[] playlistPipedRSSResponse(String playlistId)
throws FeedException {
throws Exception {
if (StringUtils.isBlank(playlistId))
ExceptionHandler.throwErrorResponse(new InvalidRequestResponse("playlistId is required parameter"));
try (Session s = DatabaseSessionFactory.createSession()) {
var cb = s.getCriteriaBuilder();
var cq = cb.createQuery(me.kavin.piped.utils.obj.db.Playlist.class);
var root = cq.from(me.kavin.piped.utils.obj.db.Playlist.class);
root.fetch("videos", JoinType.RIGHT)
.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();
var playlistCompletableFuture = Multithreading.supplyAsync(() -> DatabaseHelper.getPlaylistFromId(s, playlistId));
var playlistVideosCompletableFuture = Multithreading.supplyAsync(() -> DatabaseHelper.getPlaylistVideosFromPlaylistId(playlistId, true));
var pl = playlistCompletableFuture.get();
final List<SyndEntry> entries = new ObjectArrayList<>();
@ -108,7 +95,9 @@ public class AuthPlaylistHandlers {
feed.setLink(Constants.FRONTEND_URL + "/playlist?list=" + pl.getPlaylistId());
feed.setPublishedDate(new Date());
for (var video : pl.getVideos()) {
var videos = playlistVideosCompletableFuture.get();
for (var video : videos) {
SyndEntry entry = new SyndEntryImpl();
entry.setAuthor(video.getChannel().getUploader());
entry.setLink(Constants.FRONTEND_URL + "/video?id=" + video.getId());
@ -223,7 +212,6 @@ public class AuthPlaylistHandlers {
var cb = s.getCriteriaBuilder();
var query = cb.createQuery(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)));
var playlist = s.createQuery(query).uniqueResult();

View file

@ -123,6 +123,12 @@ public class DatabaseHelper {
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) {
CriteriaBuilder cb = s.getCriteriaBuilder();
CriteriaQuery<PlaylistVideo> cr = cb.createQuery(PlaylistVideo.class);
@ -132,6 +138,29 @@ public class DatabaseHelper {
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) {
CriteriaBuilder cb = s.getCriteriaBuilder();
CriteriaQuery<PubSub> cr = cb.createQuery(PubSub.class);